use the same color for the stereo panner center line as the mono panner
[ardour.git] / gtk2_ardour / location_ui.cc
1 /*
2     Copyright (C) 2000 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cmath>
21 #include <cstdlib>
22
23 #include <gtkmm2ext/utils.h>
24
25 #include "ardour/utils.h"
26 #include "ardour/configuration.h"
27 #include "ardour/session.h"
28 #include "pbd/memento_command.h"
29
30 #include "ardour_ui.h"
31 #include "clock_group.h"
32 #include "gui_thread.h"
33 #include "keyboard.h"
34 #include "location_ui.h"
35 #include "prompter.h"
36 #include "utils.h"
37
38 #include "i18n.h"
39
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace PBD;
43 using namespace Gtk;
44 using namespace Gtkmm2ext;
45
46 LocationEditRow::LocationEditRow(Session * sess, Location * loc, int32_t num)
47         : SessionHandlePtr (0) /* explicitly set below */
48         , location(0) 
49         , item_table (1, 6, false)
50         , start_clock (X_("locationstart"), true, X_("LocationEditRowClock"), true, false)
51         , end_clock (X_("locationend"), true, X_("LocationEditRowClock"), true, false)
52         , length_clock (X_("locationlength"), true, X_("LocationEditRowClock"), true, false, true)
53         , cd_check_button (_("CD"))
54         , hide_check_button (_("Hide"))
55         , lock_check_button (_("Lock"))
56         , glue_check_button (_("Glue"))
57         , scms_check_button (_("SCMS"))
58         , preemph_check_button (_("Pre-Emphasis"))
59         , _clock_group (0)
60  {
61          i_am_the_modifier = 0;
62
63          start_go_button.set_image (*manage (new Image (Stock::JUMP_TO, Gtk::ICON_SIZE_SMALL_TOOLBAR)));
64          end_go_button.set_image (*manage (new Image (Stock::JUMP_TO, Gtk::ICON_SIZE_SMALL_TOOLBAR)));
65          remove_button.set_image (*manage (new Image (Stock::REMOVE, Gtk::ICON_SIZE_SMALL_TOOLBAR)));
66
67          number_label.set_name ("LocationEditNumberLabel");
68          name_label.set_name ("LocationEditNameLabel");
69          name_entry.set_name ("LocationEditNameEntry");
70          start_go_button.set_name ("LocationEditGoButton");
71          end_go_button.set_name ("LocationEditGoButton");
72          cd_check_button.set_name ("LocationEditCdButton");
73          hide_check_button.set_name ("LocationEditHideButton");
74          lock_check_button.set_name ("LocationEditLockButton");
75          glue_check_button.set_name ("LocationEditGlueButton");
76          remove_button.set_name ("LocationEditRemoveButton");
77          isrc_label.set_name ("LocationEditNumberLabel");
78          isrc_entry.set_name ("LocationEditNameEntry");
79          scms_check_button.set_name ("LocationEditCdButton");
80          preemph_check_button.set_name ("LocationEditCdButton");
81          performer_label.set_name ("LocationEditNumberLabel");
82          performer_entry.set_name ("LocationEditNameEntry");
83          composer_label.set_name ("LocationEditNumberLabel");
84          composer_entry.set_name ("LocationEditNameEntry");
85
86          isrc_label.set_text ("ISRC: ");
87          isrc_label.set_size_request (30, -1);
88          performer_label.set_text ("Performer: ");
89          performer_label.set_size_request (60, -1);
90          composer_label.set_text ("Composer: ");
91          composer_label.set_size_request (60, -1);
92
93          isrc_entry.set_size_request (112, -1);
94          isrc_entry.set_max_length(12);
95          isrc_entry.set_editable (true);
96
97          performer_entry.set_size_request (100, -1);
98          performer_entry.set_editable (true);
99
100          composer_entry.set_size_request (100, -1);
101          composer_entry.set_editable (true);
102
103          name_label.set_alignment (0, 0.5);
104
105          cd_track_details_hbox.pack_start (isrc_label, false, false);
106          cd_track_details_hbox.pack_start (isrc_entry, false, false);
107          cd_track_details_hbox.pack_start (scms_check_button, false, false);
108          cd_track_details_hbox.pack_start (preemph_check_button, false, false);
109          cd_track_details_hbox.pack_start (performer_label, false, false);
110          cd_track_details_hbox.pack_start (performer_entry, true, true);
111          cd_track_details_hbox.pack_start (composer_label, false, false);
112          cd_track_details_hbox.pack_start (composer_entry, true, true);
113
114          isrc_entry.signal_changed().connect (sigc::mem_fun(*this, &LocationEditRow::isrc_entry_changed));
115          performer_entry.signal_changed().connect (sigc::mem_fun(*this, &LocationEditRow::performer_entry_changed));
116          composer_entry.signal_changed().connect (sigc::mem_fun(*this, &LocationEditRow::composer_entry_changed));
117          scms_check_button.signal_toggled().connect(sigc::mem_fun(*this, &LocationEditRow::scms_toggled));
118          preemph_check_button.signal_toggled().connect(sigc::mem_fun(*this, &LocationEditRow::preemph_toggled));
119
120          set_session (sess);
121
122          start_hbox.pack_start (start_go_button, false, false);
123          start_hbox.pack_start (start_clock, false, false);
124
125          /* this is always in this location, no matter what the location is */
126
127          item_table.attach (start_hbox, 1, 2, 0, 1, FILL, FILL, 4, 0);
128
129          start_go_button.signal_clicked().connect(sigc::bind (sigc::mem_fun (*this, &LocationEditRow::go_button_pressed), LocStart));
130          start_clock.ValueChanged.connect (sigc::bind (sigc::mem_fun (*this, &LocationEditRow::clock_changed), LocStart));
131          start_clock.ChangeAborted.connect (sigc::bind (sigc::mem_fun (*this, &LocationEditRow::change_aborted), LocStart));
132
133          end_hbox.pack_start (end_go_button, false, false);
134          end_hbox.pack_start (end_clock, false, false);
135
136          end_go_button.signal_clicked().connect(sigc::bind (sigc::mem_fun (*this, &LocationEditRow::go_button_pressed), LocEnd));
137          end_clock.ValueChanged.connect (sigc::bind (sigc::mem_fun (*this, &LocationEditRow::clock_changed), LocEnd));
138          end_clock.ChangeAborted.connect (sigc::bind (sigc::mem_fun (*this, &LocationEditRow::change_aborted), LocEnd));
139
140          length_clock.ValueChanged.connect (sigc::bind ( sigc::mem_fun(*this, &LocationEditRow::clock_changed), LocLength));
141          length_clock.ChangeAborted.connect (sigc::bind (sigc::mem_fun (*this, &LocationEditRow::change_aborted), LocLength));
142
143          cd_check_button.signal_toggled().connect(sigc::mem_fun(*this, &LocationEditRow::cd_toggled));
144          hide_check_button.signal_toggled().connect(sigc::mem_fun(*this, &LocationEditRow::hide_toggled));
145          lock_check_button.signal_toggled().connect(sigc::mem_fun(*this, &LocationEditRow::lock_toggled));
146          glue_check_button.signal_toggled().connect(sigc::mem_fun(*this, &LocationEditRow::glue_toggled));
147
148          remove_button.signal_clicked().connect(sigc::mem_fun(*this, &LocationEditRow::remove_button_pressed));
149
150          pack_start(item_table, true, true);
151
152          set_location (loc);
153          set_number (num);
154  }
155
156  LocationEditRow::~LocationEditRow()
157  {
158          if (location) {
159                  connections.drop_connections ();
160          }
161
162          if (_clock_group) {
163                  _clock_group->remove (start_clock);
164                  _clock_group->remove (end_clock);
165                  _clock_group->remove (length_clock);
166          }
167  }
168
169  void
170  LocationEditRow::set_clock_group (ClockGroup& cg)
171  {
172          if (_clock_group) {
173                  _clock_group->remove (start_clock);
174                  _clock_group->remove (end_clock);
175                  _clock_group->remove (length_clock);
176          }
177
178          _clock_group = &cg;
179
180          _clock_group->add (start_clock);
181          _clock_group->add (end_clock);
182          _clock_group->add (length_clock);
183 }
184
185 void
186 LocationEditRow::set_session (Session *sess)
187 {
188         SessionHandlePtr::set_session (sess);
189
190         if (!_session) { 
191                 return;
192         }
193
194         start_clock.set_session (_session);
195         end_clock.set_session (_session);
196         length_clock.set_session (_session);
197
198 }
199
200 void
201 LocationEditRow::set_number (int num)
202 {
203         number = num;
204
205         if (number >= 0 ) {
206                 number_label.set_text (string_compose ("%1", number));
207         }
208 }
209
210 void
211 LocationEditRow::set_location (Location *loc)
212 {
213         if (location) {
214                 connections.drop_connections ();
215         }
216
217         location = loc;
218
219         if (!location) {
220                 return;
221         }
222
223         ++i_am_the_modifier;
224
225         if (!hide_check_button.get_parent()) {
226                 item_table.attach (hide_check_button, 5, 6, 0, 1, FILL, Gtk::FILL, 4, 0);
227                 item_table.attach (lock_check_button, 6, 7, 0, 1, FILL, Gtk::FILL, 4, 0);
228                 item_table.attach (glue_check_button, 7, 8, 0, 1, FILL, Gtk::FILL, 4, 0);
229         }
230         hide_check_button.set_active (location->is_hidden());
231         lock_check_button.set_active (location->locked());
232         glue_check_button.set_active (location->position_lock_style() == MusicTime);
233
234         if (location->is_auto_loop() || location-> is_auto_punch()) {
235                 // use label instead of entry
236
237                 name_label.set_text (location->name());
238                 name_label.set_size_request (80, -1);
239
240                 if (!name_label.get_parent()) {
241                         item_table.attach (name_label, 0, 1, 0, 1, FILL, FILL, 4, 0);
242                 }
243
244                 name_label.show();
245
246         } else {
247
248                 name_entry.set_text (location->name());
249                 name_entry.set_size_request (100, -1);
250                 name_entry.set_editable (true);
251                 name_entry.signal_changed().connect (sigc::mem_fun(*this, &LocationEditRow::name_entry_changed));
252
253                 if (!name_entry.get_parent()) {
254                         item_table.attach (name_entry, 0, 1, 0, 1, FILL | EXPAND, FILL, 4, 0);
255                 }
256                 name_entry.show();
257
258                 if (!cd_check_button.get_parent()) {
259                         item_table.attach (cd_check_button, 4, 5, 0, 1, FILL, FILL, 4, 0);
260                 }
261                 if (!remove_button.get_parent()) {
262                         item_table.attach (remove_button, 8, 9, 0, 1, FILL, FILL, 4, 0);
263                 }
264
265                 if (location->is_session_range()) {
266                         remove_button.set_sensitive (false);
267                 }
268
269                 cd_check_button.set_active (location->is_cd_marker());
270                 cd_check_button.show();
271
272                 if (location->start() == _session->current_start_frame()) {
273                         cd_check_button.set_sensitive (false);
274                 } else {
275                         cd_check_button.set_sensitive (true);
276                 }
277
278                 hide_check_button.show();
279                 lock_check_button.show();
280                 glue_check_button.show();
281         }
282
283         start_clock.set (location->start(), true);
284
285
286         if (!location->is_mark()) {
287                 if (!end_hbox.get_parent()) {
288                         item_table.attach (end_hbox, 2, 3, 0, 1, FILL, FILL, 4, 0);
289                 }
290                 if (!length_clock.get_parent()) {
291                         item_table.attach (length_clock, 3, 4, 0, 1, FILL, FILL, 4, 0);
292                 }
293
294                 end_clock.set (location->end(), true);
295                 length_clock.set (location->length(), true);
296
297                 end_go_button.show();
298                 end_clock.show();
299                 length_clock.show();
300
301                 ARDOUR_UI::instance()->set_tip (end_go_button, _("Jump to the end of this range"));
302                 ARDOUR_UI::instance()->set_tip (start_go_button, _("Jump to the start of this range"));
303                 ARDOUR_UI::instance()->set_tip (remove_button, _("Forget this range"));
304                 ARDOUR_UI::instance()->set_tip (start_clock, _("Start time"));
305                 ARDOUR_UI::instance()->set_tip (end_clock, _("End time"));
306                 ARDOUR_UI::instance()->set_tip (length_clock, _("Length"));
307
308         } else {
309
310                 ARDOUR_UI::instance()->set_tip (start_go_button, _("Jump to this marker"));
311                 ARDOUR_UI::instance()->set_tip (remove_button, _("Forget this marker"));
312                 ARDOUR_UI::instance()->set_tip (start_clock, _("Position"));
313
314                 end_go_button.hide();
315                 end_clock.hide();
316                 length_clock.hide();
317         }
318
319         set_clock_sensitivity ();
320
321         --i_am_the_modifier;
322
323         location->start_changed.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::start_changed, this, _1), gui_context());
324         location->end_changed.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::end_changed, this, _1), gui_context());
325         location->name_changed.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::name_changed, this, _1), gui_context());
326         location->changed.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::location_changed, this, _1), gui_context());
327         location->FlagsChanged.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::flags_changed, this, _1, _2), gui_context());
328         location->LockChanged.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::lock_changed, this, _1), gui_context());
329         location->PositionLockStyleChanged.connect (connections, invalidator (*this), ui_bind (&LocationEditRow::position_lock_style_changed, this, _1), gui_context());
330 }
331
332 void
333 LocationEditRow::name_entry_changed ()
334 {
335         ENSURE_GUI_THREAD (*this, &LocationEditRow::name_entry_changed)
336         
337         if (i_am_the_modifier || !location) {
338                 return;
339         }
340
341         location->set_name (name_entry.get_text());
342 }
343
344
345 void
346 LocationEditRow::isrc_entry_changed ()
347 {
348         ENSURE_GUI_THREAD (*this, &LocationEditRow::isrc_entry_changed)
349
350         if (i_am_the_modifier || !location) return;
351
352         if (isrc_entry.get_text() != "" ) {
353
354           location->cd_info["isrc"] = isrc_entry.get_text();
355
356         } else {
357           location->cd_info.erase("isrc");
358         }
359 }
360
361 void
362 LocationEditRow::performer_entry_changed ()
363 {
364         ENSURE_GUI_THREAD (*this, &LocationEditRow::performer_entry_changed)
365
366         if (i_am_the_modifier || !location) return;
367
368         if (performer_entry.get_text() != "") {
369           location->cd_info["performer"] = performer_entry.get_text();
370         } else {
371           location->cd_info.erase("performer");
372         }
373 }
374
375 void
376 LocationEditRow::composer_entry_changed ()
377 {
378         ENSURE_GUI_THREAD (*this, &LocationEditRow::composer_entry_changed)
379
380         if (i_am_the_modifier || !location) return;
381
382         if (composer_entry.get_text() != "") {
383         location->cd_info["composer"] = composer_entry.get_text();
384         } else {
385           location->cd_info.erase("composer");
386         }
387 }
388
389
390 void
391 LocationEditRow::go_button_pressed (LocationPart part)
392 {
393         if (!location) {
394                 return;
395         }
396
397         switch (part) {
398         case LocStart:
399                 ARDOUR_UI::instance()->do_transport_locate (location->start(), _session->transport_rolling ());
400                 break;
401         case LocEnd:
402                 ARDOUR_UI::instance()->do_transport_locate (location->end(), _session->transport_rolling ());
403                 break;
404         default:
405                 break;
406         }
407 }
408
409 void
410 LocationEditRow::clock_changed (LocationPart part)
411 {
412         if (i_am_the_modifier || !location) {
413                 return;
414         }
415
416         switch (part) {
417         case LocStart:
418                 location->set_start (start_clock.current_time());
419                 break;
420         case LocEnd:
421                 location->set_end (end_clock.current_time());
422                 break;
423         case LocLength:
424                 location->set_end (location->start() + length_clock.current_duration());
425         default:
426                 break;
427         }
428 }
429
430 void
431 LocationEditRow::change_aborted (LocationPart /*part*/)
432 {
433         if (i_am_the_modifier || !location) return;
434
435         set_location(location);
436 }
437
438 void
439 LocationEditRow::cd_toggled ()
440 {
441         if (i_am_the_modifier || !location) {
442                 return;
443         }
444
445         //if (cd_check_button.get_active() == location->is_cd_marker()) {
446         //      return;
447         //}
448
449         if (cd_check_button.get_active()) {
450                 if (location->start() <= _session->current_start_frame()) {
451                         error << _("You cannot put a CD marker at the start of the session") << endmsg;
452                         cd_check_button.set_active (false);
453                         return;
454                 }
455         }
456
457         location->set_cd (cd_check_button.get_active(), this);
458
459         if (location->is_cd_marker() && !(location->is_mark())) {
460
461                 if (location->cd_info.find("isrc") != location->cd_info.end()) {
462                         isrc_entry.set_text(location->cd_info["isrc"]);
463                 }
464                 if (location->cd_info.find("performer") != location->cd_info.end()) {
465                         performer_entry.set_text(location->cd_info["performer"]);
466                 }
467                 if (location->cd_info.find("composer") != location->cd_info.end()) {
468                         composer_entry.set_text(location->cd_info["composer"]);
469                 }
470                 if (location->cd_info.find("scms") != location->cd_info.end()) {
471                         scms_check_button.set_active(true);
472                 }
473                 if (location->cd_info.find("preemph") != location->cd_info.end()) {
474                         preemph_check_button.set_active(true);
475                 }
476
477                 if (!cd_track_details_hbox.get_parent()) {
478                         item_table.attach (cd_track_details_hbox, 0, 7, 1, 2, FILL | EXPAND, FILL, 4, 0);
479                 }
480                 // item_table.resize(2, 7);
481                 cd_track_details_hbox.show_all();
482
483         } else if (cd_track_details_hbox.get_parent()){
484
485                 item_table.remove (cd_track_details_hbox);
486                 //        item_table.resize(1, 7);
487                 redraw_ranges(); /*     EMIT_SIGNAL */
488         }
489 }
490
491 void
492 LocationEditRow::hide_toggled ()
493 {
494         if (i_am_the_modifier || !location) {
495                 return;
496         }
497
498         location->set_hidden (hide_check_button.get_active(), this);
499 }
500
501 void
502 LocationEditRow::lock_toggled ()
503 {
504         if (i_am_the_modifier || !location) {
505                 return;
506         }
507
508         if (location->locked()) {
509                 location->unlock ();
510         } else {
511                 location->lock ();
512         }
513 }
514
515 void
516 LocationEditRow::glue_toggled ()
517 {
518         if (i_am_the_modifier || !location) {
519                 return;
520         }
521
522         if (location->position_lock_style() == AudioTime) {
523                 location->set_position_lock_style (MusicTime);
524         } else {
525                 location->set_position_lock_style (AudioTime);
526         }
527 }
528
529 void
530 LocationEditRow::remove_button_pressed ()
531 {
532         if (!location) {
533                 return;
534         }
535
536         remove_requested (location); /* EMIT_SIGNAL */
537 }
538
539
540
541 void
542 LocationEditRow::scms_toggled ()
543 {
544         if (i_am_the_modifier || !location) return;
545
546         if (scms_check_button.get_active()) {
547           location->cd_info["scms"] = "on";
548         } else {
549           location->cd_info.erase("scms");
550         }
551
552 }
553
554 void
555 LocationEditRow::preemph_toggled ()
556 {
557         if (i_am_the_modifier || !location) return;
558
559         if (preemph_check_button.get_active()) {
560           location->cd_info["preemph"] = "on";
561         } else {
562           location->cd_info.erase("preemph");
563         }
564 }
565
566 void
567 LocationEditRow::end_changed (ARDOUR::Location *loc)
568 {
569         ENSURE_GUI_THREAD (*this, &LocationEditRow::end_changed, loc)
570
571         if (!location) return;
572
573         // update end and length
574         i_am_the_modifier++;
575
576         end_clock.set (location->end());
577         length_clock.set (location->length());
578
579         i_am_the_modifier--;
580 }
581
582 void
583 LocationEditRow::start_changed (ARDOUR::Location*)
584 {
585         if (!location) return;
586
587         // update end and length
588         i_am_the_modifier++;
589
590         start_clock.set (location->start());
591
592         if (location->start() == _session->current_start_frame()) {
593                 cd_check_button.set_sensitive (false);
594         } else {
595                 cd_check_button.set_sensitive (true);
596         }
597
598         i_am_the_modifier--;
599 }
600
601 void
602 LocationEditRow::name_changed (ARDOUR::Location *)
603 {
604         if (!location) return;
605
606         // update end and length
607         i_am_the_modifier++;
608
609         name_entry.set_text(location->name());
610         name_label.set_text(location->name());
611
612         i_am_the_modifier--;
613
614 }
615
616 void
617 LocationEditRow::location_changed (ARDOUR::Location*)
618 {
619
620         if (!location) return;
621
622         i_am_the_modifier++;
623
624         start_clock.set (location->start());
625         end_clock.set (location->end());
626         length_clock.set (location->length());
627
628         set_clock_sensitivity ();
629
630         i_am_the_modifier--;
631
632 }
633
634 void
635 LocationEditRow::flags_changed (ARDOUR::Location*, void *)
636 {
637         if (!location) {
638                 return;
639         }
640
641         i_am_the_modifier++;
642
643         cd_check_button.set_active (location->is_cd_marker());
644         hide_check_button.set_active (location->is_hidden());
645         glue_check_button.set_active (location->position_lock_style() == MusicTime);
646
647         i_am_the_modifier--;
648 }
649
650 void
651 LocationEditRow::lock_changed (ARDOUR::Location*)
652 {
653         if (!location) {
654                 return;
655         }
656
657         i_am_the_modifier++;
658
659         lock_check_button.set_active (location->locked());
660
661         set_clock_sensitivity ();
662
663         i_am_the_modifier--;
664 }
665
666 void
667 LocationEditRow::position_lock_style_changed (ARDOUR::Location*)
668 {
669         if (!location) {
670                 return;
671         }
672
673         i_am_the_modifier++;
674
675         glue_check_button.set_active (location->position_lock_style() == MusicTime);
676
677         i_am_the_modifier--;
678 }
679
680 void
681 LocationEditRow::focus_name() {
682         name_entry.grab_focus();
683 }
684
685 void
686 LocationEditRow::set_clock_sensitivity ()
687 {
688         start_clock.set_sensitive (!location->locked());
689         end_clock.set_sensitive (!location->locked());
690         length_clock.set_sensitive (!location->locked());
691 }
692
693 /*------------------------------------------------------------------------*/
694
695 LocationUI::LocationUI ()
696         : add_location_button (_("New Marker"))
697         , add_range_button (_("New Range"))
698 {
699         i_am_the_modifier = 0;
700
701         _clock_group = new ClockGroup;
702         _clock_group->set_clock_mode (AudioClock::Frames);
703
704         VBox* vbox = manage (new VBox);
705
706         Table* table = manage (new Table (2, 2));
707         table->set_spacings (4);
708         table->set_col_spacing (0, 32);
709         int table_row = 0;
710
711         Label* l = manage (new Label (_("<b>Loop/Punch Ranges</b>")));
712         l->set_alignment (0, 0.5);
713         l->set_use_markup (true);
714         table->attach (*l, 0, 2, table_row, table_row + 1);
715         ++table_row;
716
717         loop_edit_row.set_clock_group (*_clock_group);
718         punch_edit_row.set_clock_group (*_clock_group);
719
720         loop_punch_box.pack_start (loop_edit_row, false, false);
721         loop_punch_box.pack_start (punch_edit_row, false, false);
722         
723         table->attach (loop_punch_box, 1, 2, table_row, table_row + 1);
724         ++table_row;
725
726         vbox->pack_start (*table, false, false);
727         
728         table = manage (new Table (3, 2));
729         table->set_spacings (4);
730         table->set_col_spacing (0, 32);
731         table_row = 0;
732
733         table->attach (*manage (new Label ("")), 0, 2, table_row, table_row + 1, Gtk::SHRINK, Gtk::SHRINK);
734         ++table_row;
735         
736         l = manage (new Label (_("<b>Markers (Including CD Index)</b>")));
737         l->set_alignment (0, 0.5);
738         l->set_use_markup (true);
739         table->attach (*l, 0, 2, table_row, table_row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
740         ++table_row;
741
742         location_rows.set_name("LocationLocRows");
743         location_rows_scroller.add (location_rows);
744         location_rows_scroller.set_name ("LocationLocRowsScroller");
745         location_rows_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
746         location_rows_scroller.set_size_request (-1, 130);
747
748         newest_location = 0;
749
750         loc_frame_box.set_spacing (5);
751         loc_frame_box.set_border_width (5);
752         loc_frame_box.set_name("LocationFrameBox");
753
754         loc_frame_box.pack_start (location_rows_scroller, true, true);
755
756         add_location_button.set_name ("LocationAddLocationButton");
757
758         table->attach (loc_frame_box, 1, 2, table_row, table_row + 1);
759         ++table_row;
760
761         loc_range_panes.pack1 (*table, true, false);
762
763         table = manage (new Table (3, 2));
764         table->set_spacings (4);
765         table->set_col_spacing (0, 32);
766         table_row = 0;
767
768         table->attach (*manage (new Label ("")), 0, 2, table_row, table_row + 1, Gtk::SHRINK, Gtk::SHRINK);
769         ++table_row;
770         
771         l = manage (new Label (_("<b>Ranges (Including CD Track Ranges)</b>")));
772         l->set_alignment (0, 0.5);
773         l->set_use_markup (true);
774         table->attach (*l, 0, 2, table_row, table_row + 1, Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
775         ++table_row;
776
777         range_rows.set_name("LocationRangeRows");
778         range_rows_scroller.add (range_rows);
779         range_rows_scroller.set_name ("LocationRangeRowsScroller");
780         range_rows_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
781         range_rows_scroller.set_size_request (-1, 130);
782
783         range_frame_box.set_spacing (5);
784         range_frame_box.set_name("LocationFrameBox");
785         range_frame_box.set_border_width (5);
786         range_frame_box.pack_start (range_rows_scroller, true, true);
787
788         add_range_button.set_name ("LocationAddRangeButton");
789
790         table->attach (range_frame_box, 1, 2, table_row, table_row + 1);
791         ++table_row;
792
793         loc_range_panes.pack2 (*table, true, false);
794
795         HBox* add_button_box = manage (new HBox);
796         add_button_box->pack_start (add_location_button, true, true);
797         add_button_box->pack_start (add_range_button, true, true);
798
799         vbox->pack_start (loc_range_panes, true, true);
800         vbox->pack_start (*add_button_box, false, false);
801
802         pack_start (*vbox);
803
804         add_location_button.signal_clicked().connect (sigc::mem_fun(*this, &LocationUI::add_new_location));
805         add_range_button.signal_clicked().connect (sigc::mem_fun(*this, &LocationUI::add_new_range));
806         
807         show_all ();
808 }
809
810 LocationUI::~LocationUI()
811 {
812         delete _clock_group;
813 }
814
815 gint 
816 LocationUI::do_location_remove (ARDOUR::Location *loc)
817 {
818         /* this is handled internally by Locations, but there's
819            no point saving state etc. when we know the marker
820            cannot be removed.
821         */
822
823         if (loc->is_session_range()) {
824                 return FALSE;
825         }
826
827         _session->begin_reversible_command (_("remove marker"));
828         XMLNode &before = _session->locations()->get_state();
829         _session->locations()->remove (loc);
830         XMLNode &after = _session->locations()->get_state();
831         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
832         _session->commit_reversible_command ();
833
834         return FALSE;
835 }
836
837 void 
838 LocationUI::location_remove_requested (ARDOUR::Location *loc)
839 {
840         // must do this to prevent problems when destroying
841         // the effective sender of this event
842
843         Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &LocationUI::do_location_remove), loc));
844 }
845
846
847 void 
848 LocationUI::location_redraw_ranges ()
849 {
850         range_rows.hide();
851         range_rows.show();
852 }
853
854 struct LocationSortByStart {
855     bool operator() (Location *a, Location *b) {
856             return a->start() < b->start();
857     }
858 };
859
860 void
861 LocationUI::location_added (Location* location)
862 {
863         if (location->is_auto_punch()) {
864                 punch_edit_row.set_location(location);
865         } else if (location->is_auto_loop()) {
866                 loop_edit_row.set_location(location);
867         } else if (location->is_range_marker() || location->is_mark()) {
868                 Locations::LocationList loc = _session->locations()->list ();
869                 loc.sort (LocationSortByStart ());
870
871                 LocationEditRow* erow = manage (new LocationEditRow (_session, location));
872
873                 erow->set_clock_group (*_clock_group);
874                 erow->remove_requested.connect (sigc::mem_fun (*this, &LocationUI::location_remove_requested));
875
876                 Box_Helpers::BoxList & children = location->is_range_marker() ? range_rows.children () : location_rows.children ();
877
878                 /* Step through the location list and the GUI list to find the place to insert */
879                 Locations::LocationList::iterator i = loc.begin ();
880                 Box_Helpers::BoxList::iterator j = children.begin ();
881                 while (i != loc.end()) {
882
883                         if (location->flags() != (*i)->flags()) {
884                                 /* Skip locations in the session list that aren't of the right type */
885                                 ++i;
886                                 continue;
887                         }
888
889                         if (*i == location) {
890                                 children.insert (j, Box_Helpers::Element (*erow, PACK_SHRINK, 1, PACK_START));
891                                 break;
892                         }
893
894                         ++i;
895                         
896                         if (j != children.end()) {
897                                 ++j;
898                         }
899                 }
900
901                 range_rows.show_all ();
902                 location_rows.show_all ();
903         }
904 }
905
906 void
907 LocationUI::location_removed (Location* location)
908 {
909         ENSURE_GUI_THREAD (*this, &LocationUI::location_removed, location)
910
911         if (location->is_auto_punch()) {
912                 punch_edit_row.set_location(0);
913         } else if (location->is_auto_loop()) {
914                 loop_edit_row.set_location(0);
915         } else if (location->is_range_marker() || location->is_mark()) {
916                 Box_Helpers::BoxList& children = location->is_range_marker() ? range_rows.children () : location_rows.children ();
917                 for (Box_Helpers::BoxList::iterator i = children.begin(); i != children.end(); ++i) {
918                         LocationEditRow* r = dynamic_cast<LocationEditRow*> (i->get_widget());
919                         if (r && r->get_location() == location) {
920                                 children.erase (i);
921                                 break;
922                         }
923                 }
924         }
925 }
926
927 void
928 LocationUI::map_locations (Locations::LocationList& locations)
929 {
930         Locations::LocationList::iterator i;
931         gint n;
932         int mark_n = 0;
933         Locations::LocationList temp = locations;
934         LocationSortByStart cmp;
935
936         temp.sort (cmp);
937         locations = temp;
938
939         for (n = 0, i = locations.begin(); i != locations.end(); ++n, ++i) {
940
941                 Location* location = *i;
942
943                 if (location->is_mark()) {
944                         LocationEditRow* erow = manage (new LocationEditRow (_session, location, mark_n));
945
946                         erow->set_clock_group (*_clock_group);
947                         erow->remove_requested.connect (sigc::mem_fun(*this, &LocationUI::location_remove_requested));
948                         erow->redraw_ranges.connect (sigc::mem_fun(*this, &LocationUI::location_redraw_ranges));
949
950                         Box_Helpers::BoxList & loc_children = location_rows.children();
951                         loc_children.push_back(Box_Helpers::Element(*erow, PACK_SHRINK, 1, PACK_START));
952                         if (location == newest_location) {
953                                 newest_location = 0;
954                                 erow->focus_name();
955                         }
956                 } else if (location->is_auto_punch()) {
957                         punch_edit_row.set_session (_session);
958                         punch_edit_row.set_location (location);
959                         punch_edit_row.show_all();
960                 } else if (location->is_auto_loop()) {
961                         loop_edit_row.set_session (_session);
962                         loop_edit_row.set_location (location);
963                         loop_edit_row.show_all();
964                 } else {
965                         LocationEditRow* erow = manage (new LocationEditRow(_session, location));
966
967                         erow->set_clock_group (*_clock_group);
968                         erow->remove_requested.connect (sigc::mem_fun(*this, &LocationUI::location_remove_requested));
969
970                         Box_Helpers::BoxList & range_children = range_rows.children();
971                         range_children.push_back(Box_Helpers::Element(*erow,  PACK_SHRINK, 1, PACK_START));
972                 }
973         }
974
975         range_rows.show_all();
976         location_rows.show_all();
977 }
978
979 void
980 LocationUI::add_new_location()
981 {
982         string markername;
983
984         if (_session) {
985                 framepos_t where = _session->audible_frame();
986                 _session->locations()->next_available_name(markername,"mark");
987                 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
988                 if (Config->get_name_new_markers()) {
989                         newest_location = location;
990                 }
991                 _session->begin_reversible_command (_("add marker"));
992                 XMLNode &before = _session->locations()->get_state();
993                 _session->locations()->add (location, true);
994                 XMLNode &after = _session->locations()->get_state();
995                 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
996                 _session->commit_reversible_command ();
997         }
998
999 }
1000
1001 void
1002 LocationUI::add_new_range()
1003 {
1004         string rangename;
1005
1006         if (_session) {
1007                 framepos_t where = _session->audible_frame();
1008                 _session->locations()->next_available_name(rangename,"unnamed");
1009                 Location *location = new Location (*_session, where, where, rangename, Location::IsRangeMarker);
1010                 _session->begin_reversible_command (_("add range marker"));
1011                 XMLNode &before = _session->locations()->get_state();
1012                 _session->locations()->add (location, true);
1013                 XMLNode &after = _session->locations()->get_state();
1014                 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1015                 _session->commit_reversible_command ();
1016         }
1017 }
1018
1019 void
1020 LocationUI::refresh_location_list ()
1021 {
1022         ENSURE_GUI_THREAD (*this, &LocationUI::refresh_location_list)
1023         using namespace Box_Helpers;
1024
1025         // this is just too expensive to do when window is not shown
1026         if (!is_visible()) return;
1027
1028         BoxList & loc_children = location_rows.children();
1029         BoxList & range_children = range_rows.children();
1030
1031         loc_children.clear();
1032         range_children.clear();
1033
1034         if (_session) {
1035                 _session->locations()->apply (*this, &LocationUI::map_locations);
1036         }
1037
1038 }
1039
1040 void
1041 LocationUI::set_session(ARDOUR::Session* s)
1042 {
1043         SessionHandlePtr::set_session (s);
1044
1045         if (_session) {
1046                 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&LocationUI::locations_changed, this, _1), gui_context());
1047                 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), boost::bind (&LocationUI::refresh_location_list, this), gui_context());
1048                 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&LocationUI::location_added, this, _1), gui_context());
1049                 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&LocationUI::location_removed, this, _1), gui_context());
1050         }
1051
1052         loop_edit_row.set_session (s);
1053         punch_edit_row.set_session (s);
1054
1055         refresh_location_list ();
1056 }
1057
1058 void
1059 LocationUI::locations_changed (Locations::Change c)
1060 {
1061         /* removal is signalled by both a removed and a changed signal emission from Locations,
1062            so we don't need to refresh the list on a removal
1063         */
1064         if (c != Locations::REMOVAL) {
1065                 refresh_location_list ();
1066         }
1067 }
1068
1069 void
1070 LocationUI::session_going_away()
1071 {
1072         ENSURE_GUI_THREAD (*this, &LocationUI::session_going_away);
1073
1074         using namespace Box_Helpers;
1075         BoxList & loc_children = location_rows.children();
1076         BoxList & range_children = range_rows.children();
1077
1078         loc_children.clear();
1079         range_children.clear();
1080
1081         loop_edit_row.set_session (0);
1082         loop_edit_row.set_location (0);
1083
1084         punch_edit_row.set_session (0);
1085         punch_edit_row.set_location (0);
1086
1087         SessionHandlePtr::session_going_away ();
1088 }
1089
1090
1091 /*------------------------*/
1092
1093 LocationUIWindow::LocationUIWindow ()
1094         : ArdourDialog (_("Locations"))
1095 {
1096         set_wmclass(X_("ardour_locations"), PROGRAM_NAME);
1097         set_name ("LocationWindow");
1098
1099         get_vbox()->pack_start (_ui);
1100 }
1101
1102 LocationUIWindow::~LocationUIWindow()
1103 {
1104 }
1105
1106 void 
1107 LocationUIWindow::on_show()
1108 {
1109         _ui.refresh_location_list();
1110         ArdourDialog::on_show();
1111 }
1112
1113 bool
1114 LocationUIWindow::on_delete_event (GdkEventAny*)
1115 {
1116         hide ();
1117         return true;
1118 }
1119
1120 void
1121 LocationUIWindow::set_session (Session *s)
1122 {
1123         ArdourDialog::set_session (s);
1124         _ui.set_session (s);
1125 }
1126
1127 void
1128 LocationUIWindow::session_going_away ()
1129 {
1130         ArdourDialog::session_going_away ();
1131         hide_all();
1132 }