64c551f0d1125dcba33476fe525c34841b474a21
[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 #include <gtkmm2ext/window_title.h>
26
27 #include "ardour/utils.h"
28 #include "ardour/configuration.h"
29 #include "ardour/session.h"
30 #include "pbd/memento_command.h"
31
32 #include "ardour_ui.h"
33 #include "prompter.h"
34 #include "location_ui.h"
35 #include "keyboard.h"
36 #include "utils.h"
37 #include "gui_thread.h"
38
39 #include "i18n.h"
40
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         WindowTitle title(Glib::get_application_name());
630         title += _("Locations");
631
632         set_title(title.get_string());
633         set_wmclass(X_("ardour_locations"), "Ardour");
634
635         set_name ("LocationWindow");
636
637         get_vbox()->pack_start (location_hpacker);
638
639         location_vpacker.set_spacing (5);
640
641         location_vpacker.pack_start (loop_edit_row, false, false);
642         location_vpacker.pack_start (punch_edit_row, false, false);
643
644         location_rows.set_name("LocationLocRows");
645         location_rows_scroller.add (location_rows);
646         location_rows_scroller.set_name ("LocationLocRowsScroller");
647         location_rows_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
648         location_rows_scroller.set_size_request (-1, 130);
649         
650         newest_location = 0;
651
652         loc_frame_box.set_spacing (5);
653         loc_frame_box.set_border_width (5);
654         loc_frame_box.set_name("LocationFrameBox");
655         
656         loc_frame_box.pack_start (location_rows_scroller, true, true);
657
658         add_location_button.set_name ("LocationAddLocationButton");
659         loc_frame_box.pack_start (add_location_button, false, false);
660
661         loc_frame.set_name ("LocationLocEditorFrame");
662         loc_frame.set_label (_("Location (CD index) markers"));
663         loc_frame.add (loc_frame_box);
664         loc_range_panes.pack1(loc_frame, true, false);
665
666         
667         range_rows.set_name("LocationRangeRows");
668         range_rows_scroller.add (range_rows);
669         range_rows_scroller.set_name ("LocationRangeRowsScroller");
670         range_rows_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
671         range_rows_scroller.set_size_request (-1, 130);
672         
673         range_frame_box.set_spacing (5);
674         range_frame_box.set_name("LocationFrameBox");
675         range_frame_box.set_border_width (5);
676         range_frame_box.pack_start (range_rows_scroller, true, true);
677
678         add_range_button.set_name ("LocationAddRangeButton");
679         range_frame_box.pack_start (add_range_button, false, false);
680
681         range_frame.set_name ("LocationRangeEditorFrame");
682         range_frame.set_label (_("Range (CD track) markers"));
683         range_frame.add (range_frame_box);
684         loc_range_panes.pack2(range_frame, true, false);
685         location_vpacker.pack_start (loc_range_panes, true, true);
686         
687         location_hpacker.pack_start (location_vpacker, true, true);
688
689         add_location_button.signal_clicked().connect (mem_fun(*this, &LocationUI::add_new_location));
690         add_range_button.signal_clicked().connect (mem_fun(*this, &LocationUI::add_new_range));
691         
692         //add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::BUTTON_RELEASE_MASK);
693
694         
695 }
696
697 LocationUI::~LocationUI()
698 {
699 }
700
701 void LocationUI::on_show()
702 {
703         ArdourDialog::on_show();
704         refresh_location_list();
705 }
706
707
708 gint LocationUI::do_location_remove (ARDOUR::Location *loc)
709 {
710         /* this is handled internally by Locations, but there's
711            no point saving state etc. when we know the marker
712            cannot be removed.
713         */
714
715         if (loc->is_end()) {
716                 return FALSE;
717         }
718
719         session->begin_reversible_command (_("remove marker"));
720         XMLNode &before = session->locations()->get_state();
721         session->locations()->remove (loc);
722         XMLNode &after = session->locations()->get_state();
723         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
724         session->commit_reversible_command ();
725
726         return FALSE;
727 }
728
729 void LocationUI::location_remove_requested (ARDOUR::Location *loc)
730 {
731         // must do this to prevent problems when destroying
732         // the effective sender of this event
733         
734   Glib::signal_idle().connect (bind (mem_fun(*this, &LocationUI::do_location_remove), loc));
735 }
736
737
738 void LocationUI::location_redraw_ranges ()
739 {
740
741         range_rows.hide();
742         range_rows.show();
743
744 }
745
746
747 void
748 LocationUI::location_added (Location* location)
749 {
750         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationUI::location_added), location));
751         
752         if (location->is_auto_punch()) {
753                 punch_edit_row.set_location(location);
754         }
755         else if (location->is_auto_loop()) {
756                 loop_edit_row.set_location(location);
757         }
758         else {
759                 refresh_location_list ();
760         }
761 }
762
763 void
764 LocationUI::location_removed (Location* location)
765 {
766         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationUI::location_removed), location));
767         
768         if (location->is_auto_punch()) {
769                 punch_edit_row.set_location(0);
770         }
771         else if (location->is_auto_loop()) {
772                 loop_edit_row.set_location(0);
773         }
774         else {
775                 refresh_location_list ();
776         }
777 }
778
779 struct LocationSortByStart {
780     bool operator() (Location *a, Location *b) {
781             return a->start() < b->start();
782     }
783 };
784
785 void
786 LocationUI::map_locations (Locations::LocationList& locations)
787 {
788         Locations::LocationList::iterator i;
789         Location* location;
790         gint n; 
791         int mark_n = 0;
792         Locations::LocationList temp = locations;
793         LocationSortByStart cmp;
794
795         temp.sort (cmp);
796         locations = temp;
797
798         Box_Helpers::BoxList & loc_children = location_rows.children();
799         Box_Helpers::BoxList & range_children = range_rows.children();
800         LocationEditRow * erow;
801         
802         for (n = 0, i = locations.begin(); i != locations.end(); ++n, ++i) {
803
804                 location = *i;
805
806                 if (location->is_mark()) {
807                         mark_n++;
808                         erow = manage (new LocationEditRow(session, location, mark_n));
809                         erow->remove_requested.connect (mem_fun(*this, &LocationUI::location_remove_requested));
810                         erow->redraw_ranges.connect (mem_fun(*this, &LocationUI::location_redraw_ranges));
811                         loc_children.push_back(Box_Helpers::Element(*erow, PACK_SHRINK, 1, PACK_START));
812                         if (location == newest_location) {
813                                 newest_location = 0;
814                                 erow->focus_name();
815                         }
816                 }
817                 else if (location->is_auto_punch()) {
818                         punch_edit_row.set_session (session);
819                         punch_edit_row.set_location (location);
820                         punch_edit_row.show_all();
821                 }
822                 else if (location->is_auto_loop()) {
823                         loop_edit_row.set_session (session);
824                         loop_edit_row.set_location (location);
825                         loop_edit_row.show_all();
826                 }
827                 else {
828                         erow = manage (new LocationEditRow(session, location));
829                         erow->remove_requested.connect (mem_fun(*this, &LocationUI::location_remove_requested));
830                         range_children.push_back(Box_Helpers::Element(*erow,  PACK_SHRINK, 1, PACK_START));
831                 }
832         }
833
834         range_rows.show_all();
835         location_rows.show_all();
836 }
837
838 void
839 LocationUI::add_new_location()
840 {
841         string markername;
842
843         if (session) {
844                 nframes_t where = session->audible_frame();
845                 session->locations()->next_available_name(markername,"mark");
846                 Location *location = new Location (where, where, markername, Location::IsMark);
847                 if (Config->get_name_new_markers()) {
848                         newest_location = location;                     
849                 }
850                 session->begin_reversible_command (_("add marker"));
851                 XMLNode &before = session->locations()->get_state();
852                 session->locations()->add (location, true);
853                 XMLNode &after = session->locations()->get_state();
854                 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
855                 session->commit_reversible_command ();
856         }
857         
858 }
859
860 void
861 LocationUI::add_new_range()
862 {
863         string rangename;
864
865         if (session) {
866                 nframes_t where = session->audible_frame();
867                 session->locations()->next_available_name(rangename,"unnamed");
868                 Location *location = new Location (where, where, rangename, Location::IsRangeMarker);
869                 session->begin_reversible_command (_("add range marker"));
870                 XMLNode &before = session->locations()->get_state();
871                 session->locations()->add (location, true);
872                 XMLNode &after = session->locations()->get_state();
873                 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
874                 session->commit_reversible_command ();
875         }
876 }
877
878
879 void
880 LocationUI::refresh_location_list_s (Change ignored)
881 {
882         ENSURE_GUI_THREAD(bind (mem_fun(*this, &LocationUI::refresh_location_list_s), ignored));
883         
884         refresh_location_list ();
885 }
886
887 void
888 LocationUI::refresh_location_list ()
889 {
890         ENSURE_GUI_THREAD(mem_fun(*this, &LocationUI::refresh_location_list));
891         using namespace Box_Helpers;
892
893         // this is just too expensive to do when window is not shown
894         if (!is_visible()) return;
895
896         BoxList & loc_children = location_rows.children();
897         BoxList & range_children = range_rows.children();
898
899         loc_children.clear();
900         range_children.clear();
901
902         if (session) {
903                 session->locations()->apply (*this, &LocationUI::map_locations);
904         }
905         
906 }
907
908 void
909 LocationUI::set_session(ARDOUR::Session* sess)
910 {
911         ArdourDialog::set_session (sess);
912
913         if (session) {
914                 session->locations()->changed.connect (mem_fun(*this, &LocationUI::refresh_location_list));
915                 session->locations()->StateChanged.connect (mem_fun(*this, &LocationUI::refresh_location_list_s));
916                 session->locations()->added.connect (mem_fun(*this, &LocationUI::location_added));
917                 session->locations()->removed.connect (mem_fun(*this, &LocationUI::location_removed));
918                 session->GoingAway.connect (mem_fun(*this, &LocationUI::session_gone));
919         }
920         refresh_location_list ();
921 }
922
923 void
924 LocationUI::session_gone()
925 {
926         ENSURE_GUI_THREAD(mem_fun(*this, &LocationUI::session_gone));
927         
928         hide_all();
929
930         using namespace Box_Helpers;
931         BoxList & loc_children = location_rows.children();
932         BoxList & range_children = range_rows.children();
933
934         loc_children.clear();
935         range_children.clear();
936
937         loop_edit_row.set_session (0);
938         loop_edit_row.set_location (0);
939
940         punch_edit_row.set_session (0);
941         punch_edit_row.set_location (0);
942
943         ArdourDialog::session_gone ();
944 }
945
946 bool
947 LocationUI::on_delete_event (GdkEventAny* ev)
948 {
949         hide ();
950         return true;
951 }