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