Merged with trunk R1612.
[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 #include <gtkmm2ext/stop_signal.h>
25
26 #include <ardour/utils.h>
27 #include <ardour/configuration.h>
28 #include <ardour/session.h>
29 #include <pbd/memento_command.h>
30
31 #include "ardour_ui.h"
32 #include "prompter.h"
33 #include "location_ui.h"
34 #include "keyboard.h"
35 #include "utils.h"
36 #include "gui_thread.h"
37
38 #include "i18n.h"
39
40 using namespace ARDOUR;
41 using namespace PBD;
42 using namespace Gtk;
43 using namespace Gtkmm2ext;
44
45 LocationEditRow::LocationEditRow(Session * sess, Location * loc, int32_t num)
46         : location(0), session(0),
47           item_table (1, 7, false),
48           start_set_button (_("Set")),
49           start_go_button (_("Go")),
50           start_clock (X_("locationstart"), true, X_("LocationEditRowClock"), true),
51           end_set_button (_("Set")),
52           end_go_button (_("Go")),
53           end_clock (X_("locationend"), true, X_("LocationEditRowClock"), true),
54           length_clock (X_("locationlength"), true, X_("LocationEditRowClock"), true, true),
55           cd_check_button (_("CD")),
56           hide_check_button (_("Hidden")),
57           remove_button (_("Remove")),
58           scms_check_button (_("SCMS")),
59           preemph_check_button (_("Pre-Emphasis"))
60
61 {
62         
63         i_am_the_modifier = 0;
64
65         number_label.set_name ("LocationEditNumberLabel");
66         name_label.set_name ("LocationEditNameLabel");
67         name_entry.set_name ("LocationEditNameEntry");
68         start_set_button.set_name ("LocationEditSetButton");
69         start_go_button.set_name ("LocationEditGoButton");
70         end_set_button.set_name ("LocationEditSetButton");
71         end_go_button.set_name ("LocationEditGoButton");
72         cd_check_button.set_name ("LocationEditCdButton");
73         hide_check_button.set_name ("LocationEditHideButton");
74         remove_button.set_name ("LocationEditRemoveButton");
75         isrc_label.set_name ("LocationEditNumberLabel");
76         isrc_entry.set_name ("LocationEditNameEntry");
77         scms_check_button.set_name ("LocationEditCdButton");
78         preemph_check_button.set_name ("LocationEditCdButton");
79         performer_label.set_name ("LocationEditNumberLabel");
80         performer_entry.set_name ("LocationEditNameEntry");
81         composer_label.set_name ("LocationEditNumberLabel");
82         composer_entry.set_name ("LocationEditNameEntry");
83
84
85         isrc_label.set_text ("ISRC: ");
86         isrc_label.set_size_request (30, -1);
87         performer_label.set_text ("Performer: ");
88         performer_label.set_size_request (60, -1);
89         composer_label.set_text ("Composer: ");
90         composer_label.set_size_request (60, -1);
91
92         isrc_entry.set_size_request (112, -1);
93         isrc_entry.set_max_length(12);
94         isrc_entry.set_editable (true);
95
96         performer_entry.set_size_request (100, -1);
97         performer_entry.set_editable (true);
98
99         composer_entry.set_size_request (100, -1);
100         composer_entry.set_editable (true);
101
102         cd_track_details_hbox.pack_start (isrc_label, false, false);
103         cd_track_details_hbox.pack_start (isrc_entry, false, false);
104         cd_track_details_hbox.pack_start (scms_check_button, false, false);
105         cd_track_details_hbox.pack_start (preemph_check_button, false, false);
106         cd_track_details_hbox.pack_start (performer_label, false, false);
107         cd_track_details_hbox.pack_start (performer_entry, true, true);
108         cd_track_details_hbox.pack_start (composer_label, false, false);
109         cd_track_details_hbox.pack_start (composer_entry, true, true);
110
111         isrc_entry.signal_changed().connect (mem_fun(*this, &LocationEditRow::isrc_entry_changed)); 
112         performer_entry.signal_changed().connect (mem_fun(*this, &LocationEditRow::performer_entry_changed));
113         composer_entry.signal_changed().connect (mem_fun(*this, &LocationEditRow::composer_entry_changed));
114         scms_check_button.signal_toggled().connect(mem_fun(*this, &LocationEditRow::scms_toggled));
115         preemph_check_button.signal_toggled().connect(mem_fun(*this, &LocationEditRow::preemph_toggled));
116
117
118         set_session (sess);
119
120
121         item_table.attach (number_label, 0, 1, 0, 1, FILL, FILL, 3, 0);
122         
123         start_hbox.pack_start (start_go_button, false, false);
124         start_hbox.pack_start (start_clock, false, false);
125         start_hbox.pack_start (start_set_button, false, false);
126
127         item_table.attach (start_hbox, 2, 3, 0, 1, FILL, FILL, 4, 0);
128
129         
130         start_set_button.signal_clicked().connect(bind (mem_fun (*this, &LocationEditRow::set_button_pressed), LocStart));
131         start_go_button.signal_clicked().connect(bind (mem_fun (*this, &LocationEditRow::go_button_pressed), LocStart));
132         start_clock.ValueChanged.connect (bind (mem_fun (*this, &LocationEditRow::clock_changed), LocStart));
133
134         
135         end_hbox.pack_start (end_go_button, false, false);
136         end_hbox.pack_start (end_clock, false, false);
137         end_hbox.pack_start (end_set_button, false, false);
138         
139         //item_table.attach (end_hbox, 2, 3, 0, 1, 0, 0, 4, 0);
140         
141         end_set_button.signal_clicked().connect(bind (mem_fun (*this, &LocationEditRow::set_button_pressed), LocEnd));
142         end_go_button.signal_clicked().connect(bind (mem_fun (*this, &LocationEditRow::go_button_pressed), LocEnd));
143         end_clock.ValueChanged.connect (bind (mem_fun (*this, &LocationEditRow::clock_changed), LocEnd));
144         
145 //      item_table.attach (length_clock, 3, 4, 0, 1, 0, 0, 4, 0);
146         length_clock.ValueChanged.connect (bind ( mem_fun(*this, &LocationEditRow::clock_changed), LocLength));
147
148 //      item_table.attach (cd_check_button, 4, 5, 0, 1, 0, Gtk::FILL, 4, 0);
149 //      item_table.attach (hide_check_button, 5, 6, 0, 1, 0, Gtk::FILL, 4, 0);
150 //      item_table.attach (remove_button, 7, 8, 0, 1, 0, Gtk::FILL, 4, 0);
151         
152         cd_check_button.signal_toggled().connect(mem_fun(*this, &LocationEditRow::cd_toggled));
153         hide_check_button.signal_toggled().connect(mem_fun(*this, &LocationEditRow::hide_toggled));
154         
155         remove_button.signal_clicked().connect(mem_fun(*this, &LocationEditRow::remove_button_pressed));
156
157         pack_start(item_table, true, true);
158
159         set_location (loc);
160         set_number (num);
161 }
162
163 LocationEditRow::~LocationEditRow()
164 {
165         if (location) {
166                 start_changed_connection.disconnect();
167                 end_changed_connection.disconnect();
168                 name_changed_connection.disconnect();
169                 changed_connection.disconnect();
170                 flags_changed_connection.disconnect();
171         }
172 }
173
174 void
175 LocationEditRow::set_session (Session *sess)
176 {
177         session = sess;
178
179         if (!session) return;
180
181         start_clock.set_session (session);
182         end_clock.set_session (session);
183         length_clock.set_session (session);     
184         
185 }
186
187 void
188 LocationEditRow::set_number (int num)
189 {
190         number = num;
191
192         if (number >= 0 ) {
193                 number_label.set_text (string_compose ("%1", number));
194         }
195 }
196
197 void
198 LocationEditRow::set_location (Location *loc)
199 {
200         if (location) {
201                 start_changed_connection.disconnect();
202                 end_changed_connection.disconnect();
203                 name_changed_connection.disconnect();
204                 changed_connection.disconnect();
205                 flags_changed_connection.disconnect();
206         }
207
208         location = loc;
209
210         if (!location) return;
211
212         if (!hide_check_button.get_parent()) {
213                 item_table.attach (hide_check_button, 6, 7, 0, 1, FILL, Gtk::FILL, 4, 0);
214         }
215         hide_check_button.set_active (location->is_hidden());
216         
217         if (location->is_auto_loop() || location-> is_auto_punch()) {
218                 // use label instead of entry
219
220                 name_label.set_text (location->name());
221                 name_label.set_size_request (80, -1);
222
223                 if (!name_label.get_parent()) {
224                         item_table.attach (name_label, 1, 2, 0, 1, FILL, Gtk::FILL, 4, 0);
225                 }
226                 
227                 name_label.show();
228
229         } else {
230
231                 name_entry.set_text (location->name());
232                 name_entry.set_size_request (100, -1);
233                 name_entry.set_editable (true);
234                 name_entry.signal_changed().connect (mem_fun(*this, &LocationEditRow::name_entry_changed));  
235
236                 if (!name_entry.get_parent()) {
237                         item_table.attach (name_entry, 1, 2, 0, 1, FILL | EXPAND, FILL, 4, 0);
238                 }
239                 name_entry.show();
240
241                 if (!cd_check_button.get_parent()) {
242                         item_table.attach (cd_check_button, 5, 6, 0, 1, FILL, Gtk::FILL, 4, 0);
243                 }
244                 if (!remove_button.get_parent()) {
245                         item_table.attach (remove_button, 7, 8, 0, 1, FILL, Gtk::FILL, 4, 0);
246                 }
247
248                 /* XXX i can't find a way to hide the button without messing up 
249                    the row spacing, so make it insensitive (paul).
250                 */
251
252                 if (location->is_end() || location->is_start()) {
253                         remove_button.set_sensitive (false);
254                 }
255
256                 cd_check_button.set_active (location->is_cd_marker());
257                 cd_check_button.show();
258                 hide_check_button.show();
259         }
260
261         start_clock.set (location->start(), true);
262         
263
264         if (!location->is_mark()) {
265                 if (!end_hbox.get_parent()) {
266                         item_table.attach (end_hbox, 3, 4, 0, 1, FILL, FILL, 4, 0);
267                 }
268                 if (!length_clock.get_parent()) {
269                         item_table.attach (length_clock, 4, 5, 0, 1, FILL, FILL, 4, 0);
270                 }
271
272                 end_clock.set (location->end(), true);
273                 length_clock.set (location->length(), true);
274
275                 end_set_button.show();
276                 end_go_button.show();
277                 end_clock.show();
278                 length_clock.show();
279         }
280         else {
281                 end_set_button.hide();
282                 end_go_button.hide();
283                 end_clock.hide();
284                 length_clock.hide();
285         }
286
287         start_changed_connection = location->start_changed.connect (mem_fun(*this, &LocationEditRow::start_changed));
288         end_changed_connection = location->end_changed.connect (mem_fun(*this, &LocationEditRow::end_changed));
289         name_changed_connection = location->name_changed.connect (mem_fun(*this, &LocationEditRow::name_changed));
290         changed_connection = location->changed.connect (mem_fun(*this, &LocationEditRow::location_changed));
291         flags_changed_connection = location->FlagsChanged.connect (mem_fun(*this, &LocationEditRow::flags_changed));
292         
293 }
294
295 void
296 LocationEditRow::name_entry_changed ()
297 {
298         ENSURE_GUI_THREAD(mem_fun(*this, &LocationEditRow::name_entry_changed));
299         if (i_am_the_modifier || !location) return;
300
301         location->set_name (name_entry.get_text());
302 }
303
304
305 void
306 LocationEditRow::isrc_entry_changed ()
307 {
308         ENSURE_GUI_THREAD(mem_fun(*this, &LocationEditRow::isrc_entry_changed));
309         
310         if (i_am_the_modifier || !location) return;
311
312         if (isrc_entry.get_text() != "" ) {
313
314           location->cd_info["isrc"] = isrc_entry.get_text();
315           
316         } else {
317           location->cd_info.erase("isrc");
318         }
319 }
320
321 void
322 LocationEditRow::performer_entry_changed ()
323 {
324         ENSURE_GUI_THREAD(mem_fun(*this, &LocationEditRow::performer_entry_changed));
325         
326         if (i_am_the_modifier || !location) return;
327
328         if (performer_entry.get_text() != "") {
329           location->cd_info["performer"] = performer_entry.get_text();
330         } else {
331           location->cd_info.erase("performer");
332         }
333 }
334
335 void
336 LocationEditRow::composer_entry_changed ()
337 {
338         ENSURE_GUI_THREAD(mem_fun(*this, &LocationEditRow::composer_entry_changed));
339         
340         if (i_am_the_modifier || !location) return;
341
342         if (composer_entry.get_text() != "") {
343         location->cd_info["composer"] = composer_entry.get_text();
344         } else {
345           location->cd_info.erase("composer");
346         }
347 }
348
349
350 void
351 LocationEditRow::set_button_pressed (LocationPart part)
352 {
353         if (!location) return;
354         
355         switch (part) {
356         case LocStart:
357                 location->set_start (session->transport_frame ());
358                 break;
359         case LocEnd:
360                 location->set_end (session->transport_frame ());
361                 break;
362         default:
363                 break;
364         }
365 }
366
367 void
368 LocationEditRow::go_button_pressed (LocationPart part)
369 {
370         if (!location) return;
371
372         switch (part) {
373         case LocStart:
374                 ARDOUR_UI::instance()->do_transport_locate (location->start());
375                 break;
376         case LocEnd:
377                 ARDOUR_UI::instance()->do_transport_locate (location->end());
378                 break;
379         default:
380                 break;
381         }
382 }
383
384 void
385 LocationEditRow::clock_changed (LocationPart part)
386 {
387         if (i_am_the_modifier || !location) return;
388         
389         switch (part) {
390         case LocStart:
391                 location->set_start (start_clock.current_time());
392                 break;
393         case LocEnd:
394                 location->set_end (end_clock.current_time());
395                 break;
396         case LocLength:
397                 location->set_end (location->start() + length_clock.current_duration());
398         default:
399                 break;
400         }
401
402 }
403
404 void
405 LocationEditRow::cd_toggled ()
406 {
407
408         if (i_am_the_modifier || !location) return;
409         location->set_cd (cd_check_button.get_active(), this);
410
411         if (location->is_cd_marker() && !(location->is_mark())) {
412
413           if (location->cd_info.find("isrc") != location->cd_info.end()) {
414             isrc_entry.set_text(location->cd_info["isrc"]);
415           }
416           if (location->cd_info.find("performer") != location->cd_info.end()) {
417             performer_entry.set_text(location->cd_info["performer"]);
418           }
419           if (location->cd_info.find("composer") != location->cd_info.end()) {
420             composer_entry.set_text(location->cd_info["composer"]);
421           }
422           if (location->cd_info.find("scms") != location->cd_info.end()) {
423             scms_check_button.set_active(true);
424           }
425           if (location->cd_info.find("preemph") != location->cd_info.end()) {
426             preemph_check_button.set_active(true);
427           }
428           
429           if(!cd_track_details_hbox.get_parent()) {
430             item_table.attach (cd_track_details_hbox, 1, 8, 1, 2, FILL | EXPAND, FILL, 4, 0);
431           }
432           // item_table.resize(2, 7);
433           cd_track_details_hbox.show_all();
434
435         } else if (cd_track_details_hbox.get_parent()){
436
437             item_table.remove (cd_track_details_hbox);  
438             //    item_table.resize(1, 7);
439             redraw_ranges(); /*         EMIT_SIGNAL */
440         }
441
442 }
443
444
445 void
446 LocationEditRow::hide_toggled ()
447 {
448         if (i_am_the_modifier || !location) return;
449
450         location->set_hidden (hide_check_button.get_active(), this);
451 }
452
453 void
454 LocationEditRow::remove_button_pressed ()
455 {
456         if (!location) return;
457
458         remove_requested(location); /*  EMIT_SIGNAL */
459 }
460
461
462
463 void
464 LocationEditRow::scms_toggled ()
465 {
466         if (i_am_the_modifier || !location) return;
467
468         if (scms_check_button.get_active()) {
469           location->cd_info["scms"] = "on";
470         } else {
471           location->cd_info.erase("scms");
472         }
473         
474 }
475
476 void
477 LocationEditRow::preemph_toggled ()
478 {
479         if (i_am_the_modifier || !location) return;
480
481         if (preemph_check_button.get_active()) {
482           location->cd_info["preemph"] = "on";
483         } else {
484           location->cd_info.erase("preemph");
485         }
486 }
487
488 void
489 LocationEditRow::end_changed (ARDOUR::Location *loc)
490 {
491         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationEditRow::end_changed), loc));
492
493         if (!location) return;
494                 
495         // update end and length
496         i_am_the_modifier++;
497
498         end_clock.set (location->end());
499         length_clock.set (location->length());
500         
501         i_am_the_modifier--;
502 }
503
504 void
505 LocationEditRow::start_changed (ARDOUR::Location *loc)
506 {
507         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationEditRow::start_changed), loc));
508         
509         if (!location) return;
510         
511         // update end and length
512         i_am_the_modifier++;
513
514         start_clock.set (location->start());
515         
516         i_am_the_modifier--;
517 }
518
519 void
520 LocationEditRow::name_changed (ARDOUR::Location *loc)
521 {
522         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationEditRow::name_changed), loc));
523         
524         if (!location) return;
525
526         // update end and length
527         i_am_the_modifier++;
528
529         name_entry.set_text(location->name());
530         name_label.set_text(location->name());
531
532         i_am_the_modifier--;
533
534 }
535
536 void
537 LocationEditRow::location_changed (ARDOUR::Location *loc)
538 {       
539         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationEditRow::location_changed), loc));
540         
541         if (!location) return;
542
543         i_am_the_modifier++;
544
545         start_clock.set (location->start());
546         end_clock.set (location->end());
547         length_clock.set (location->length());
548
549         i_am_the_modifier--;
550
551 }
552
553 void
554 LocationEditRow::flags_changed (ARDOUR::Location *loc, void *src)
555 {
556         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationEditRow::flags_changed), loc, src));
557         
558         if (!location) return;
559
560         i_am_the_modifier++;
561
562         cd_check_button.set_active (location->is_cd_marker());
563         hide_check_button.set_active (location->is_hidden());
564
565         i_am_the_modifier--;
566 }
567
568 LocationUI::LocationUI ()
569         : ArdourDialog ("location dialog"),
570           add_location_button (_("Add New Location")),
571           add_range_button (_("Add New Range"))
572 {
573         i_am_the_modifier = 0;
574
575         set_title(_("ardour: locations"));
576         set_wmclass(X_("ardour_locations"), "Ardour");
577
578         set_name ("LocationWindow");
579
580         get_vbox()->pack_start (location_hpacker);
581
582         location_vpacker.set_border_width (10);
583         location_vpacker.set_spacing (5);
584
585         location_vpacker.pack_start (loop_edit_row, false, false);
586         location_vpacker.pack_start (punch_edit_row, false, false);
587
588         location_rows.set_name("LocationLocRows");
589         location_rows_scroller.add (location_rows);
590         location_rows_scroller.set_name ("LocationLocRowsScroller");
591         location_rows_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
592         location_rows_scroller.set_size_request (-1, 130);
593
594         loc_frame_box.set_spacing (5);
595         loc_frame_box.set_border_width (5);
596         loc_frame_box.set_name("LocationFrameBox");
597         
598         loc_frame_box.pack_start (location_rows_scroller, true, true);
599
600         add_location_button.set_name ("LocationAddLocationButton");
601         loc_frame_box.pack_start (add_location_button, false, false);
602
603         loc_frame.set_name ("LocationLocEditorFrame");
604         loc_frame.set_label (_("Location (CD Index) Markers"));
605         loc_frame.add (loc_frame_box);
606         loc_range_panes.pack1(loc_frame, true, false);
607
608         
609         range_rows.set_name("LocationRangeRows");
610         range_rows_scroller.add (range_rows);
611         range_rows_scroller.set_name ("LocationRangeRowsScroller");
612         range_rows_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
613         range_rows_scroller.set_size_request (-1, 130);
614         
615         range_frame_box.set_spacing (5);
616         range_frame_box.set_name("LocationFrameBox");
617         range_frame_box.set_border_width (5);
618         range_frame_box.pack_start (range_rows_scroller, true, true);
619
620         add_range_button.set_name ("LocationAddRangeButton");
621         range_frame_box.pack_start (add_range_button, false, false);
622
623         range_frame.set_name ("LocationRangeEditorFrame");
624         range_frame.set_label (_("Range (CD Track) Markers"));
625         range_frame.add (range_frame_box);
626         loc_range_panes.pack2(range_frame, true, false);
627         location_vpacker.pack_start (loc_range_panes, true, true);
628         
629         location_hpacker.pack_start (location_vpacker, true, true);
630
631         add_location_button.signal_clicked().connect (mem_fun(*this, &LocationUI::add_new_location));
632         add_range_button.signal_clicked().connect (mem_fun(*this, &LocationUI::add_new_range));
633         
634         //add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_RELEASE_MASK);
635
636         
637 }
638
639 LocationUI::~LocationUI()
640 {
641 }
642
643
644
645 gint LocationUI::do_location_remove (ARDOUR::Location *loc)
646 {
647         /* this is handled internally by Locations, but there's
648            no point saving state etc. when we know the marker
649            cannot be removed.
650         */
651
652         if (loc->is_end()) {
653                 return FALSE;
654         }
655
656         session->begin_reversible_command (_("remove marker"));
657         XMLNode &before = session->locations()->get_state();
658         session->locations()->remove (loc);
659         XMLNode &after = session->locations()->get_state();
660         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
661         session->commit_reversible_command ();
662
663         return FALSE;
664 }
665
666 void LocationUI::location_remove_requested (ARDOUR::Location *loc)
667 {
668         // must do this to prevent problems when destroying
669         // the effective sender of this event
670         
671   Glib::signal_idle().connect (bind (mem_fun(*this, &LocationUI::do_location_remove), loc));
672 }
673
674
675 void LocationUI::location_redraw_ranges ()
676 {
677
678         range_rows.hide();
679         range_rows.show();
680
681 }
682
683
684 void
685 LocationUI::location_added (Location* location)
686 {
687         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationUI::location_added), location));
688         
689         if (location->is_auto_punch()) {
690                 punch_edit_row.set_location(location);
691         }
692         else if (location->is_auto_loop()) {
693                 loop_edit_row.set_location(location);
694         }
695         else {
696                 refresh_location_list ();
697         }
698 }
699
700 void
701 LocationUI::location_removed (Location* location)
702 {
703         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationUI::location_removed), location));
704         
705         if (location->is_auto_punch()) {
706                 punch_edit_row.set_location(0);
707         }
708         else if (location->is_auto_loop()) {
709                 loop_edit_row.set_location(0);
710         }
711         else {
712                 refresh_location_list ();
713         }
714 }
715
716 struct LocationSortByStart {
717     bool operator() (Location *a, Location *b) {
718             return a->start() < b->start();
719     }
720 };
721
722 void
723 LocationUI::map_locations (Locations::LocationList& locations)
724 {
725         Locations::LocationList::iterator i;
726         Location* location;
727         gint n; 
728         int mark_n = 0;
729         Locations::LocationList temp = locations;
730         LocationSortByStart cmp;
731
732         temp.sort (cmp);
733         locations = temp;
734
735         Box_Helpers::BoxList & loc_children = location_rows.children();
736         Box_Helpers::BoxList & range_children = range_rows.children();
737         LocationEditRow * erow;
738         
739         for (n = 0, i = locations.begin(); i != locations.end(); ++n, ++i) {
740
741                 location = *i;
742
743                 if (location->is_mark()) {
744                         mark_n++;
745                         erow = manage (new LocationEditRow(session, location, mark_n));
746                         erow->remove_requested.connect (mem_fun(*this, &LocationUI::location_remove_requested));
747                         erow->redraw_ranges.connect (mem_fun(*this, &LocationUI::location_redraw_ranges));
748                         loc_children.push_back(Box_Helpers::Element(*erow, PACK_SHRINK, 1, PACK_START));
749                 }
750                 else if (location->is_auto_punch()) {
751                         punch_edit_row.set_session (session);
752                         punch_edit_row.set_location (location);
753                 }
754                 else if (location->is_auto_loop()) {
755                         loop_edit_row.set_session (session);
756                         loop_edit_row.set_location (location);
757                 }
758                 else {
759                         erow = manage (new LocationEditRow(session, location));
760                         erow->remove_requested.connect (mem_fun(*this, &LocationUI::location_remove_requested));
761                         range_children.push_back(Box_Helpers::Element(*erow,  PACK_SHRINK, 1, PACK_START));
762                 }
763         }
764
765         range_rows.show_all();
766         location_rows.show_all();
767 }
768
769 void
770 LocationUI::add_new_location()
771 {
772         string markername;
773
774         if (session) {
775                 nframes_t where = session->audible_frame();
776                 session->locations()->next_available_name(markername,"mark");
777                 Location *location = new Location (where, where, markername, Location::IsMark);
778                 session->begin_reversible_command (_("add marker"));
779                 XMLNode &before = session->locations()->get_state();
780                 session->locations()->add (location, true);
781                 XMLNode &after = session->locations()->get_state();
782                 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
783                 session->commit_reversible_command ();
784         }
785         
786 }
787
788 void
789 LocationUI::add_new_range()
790 {
791         string rangename;
792
793         if (session) {
794                 nframes_t where = session->audible_frame();
795                 session->locations()->next_available_name(rangename,"unnamed");
796                 Location *location = new Location (where, where, rangename, Location::IsRangeMarker);
797                 session->begin_reversible_command (_("add range marker"));
798                 XMLNode &before = session->locations()->get_state();
799                 session->locations()->add (location, true);
800                 XMLNode &after = session->locations()->get_state();
801                 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
802                 session->commit_reversible_command ();
803         }
804 }
805
806
807 void
808 LocationUI::refresh_location_list_s (Change ignored)
809 {
810         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationUI::refresh_location_list_s), ignored));
811         
812         refresh_location_list ();
813 }
814
815 void
816 LocationUI::refresh_location_list ()
817 {
818         ENSURE_GUI_THREAD(mem_fun(*this, &LocationUI::refresh_location_list));
819         using namespace Box_Helpers;
820
821         BoxList & loc_children = location_rows.children();
822         BoxList & range_children = range_rows.children();
823
824         loc_children.clear();
825         range_children.clear();
826
827         if (session) {
828                 session->locations()->apply (*this, &LocationUI::map_locations);
829         }
830         
831 }
832
833 void
834 LocationUI::set_session(ARDOUR::Session* sess)
835 {
836         ArdourDialog::set_session (sess);
837
838         if (session) {
839                 session->locations()->changed.connect (mem_fun(*this, &LocationUI::refresh_location_list));
840                 session->locations()->StateChanged.connect (mem_fun(*this, &LocationUI::refresh_location_list_s));
841                 session->locations()->added.connect (mem_fun(*this, &LocationUI::location_added));
842                 session->locations()->removed.connect (mem_fun(*this, &LocationUI::location_removed));
843                 session->GoingAway.connect (mem_fun(*this, &LocationUI::session_gone));
844         }
845         refresh_location_list ();
846 }
847
848 void
849 LocationUI::session_gone()
850 {
851         ENSURE_GUI_THREAD(mem_fun(*this, &LocationUI::session_gone));
852         
853         hide_all();
854
855         using namespace Box_Helpers;
856         BoxList & loc_children = location_rows.children();
857         BoxList & range_children = range_rows.children();
858
859         loc_children.clear();
860         range_children.clear();
861
862         loop_edit_row.set_session (0);
863         loop_edit_row.set_location (0);
864
865         punch_edit_row.set_session (0);
866         punch_edit_row.set_location (0);
867
868         ArdourDialog::session_gone ();
869 }
870
871 bool
872 LocationUI::on_delete_event (GdkEventAny* ev)
873 {
874         hide ();
875         return true;
876 }