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