Tidy up a couple of bits in the session option editor.
[ardour.git] / gtk2_ardour / editor_markers.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 <sigc++/retype.h>
21 #include <cstdlib>
22 #include <cmath>
23
24 #include <libgnomecanvas/libgnomecanvas.h>
25 #include <gtkmm2ext/gtk_ui.h>
26
27 #include "ardour/session.h"
28 #include "ardour/location.h"
29 #include "ardour/profile.h"
30 #include "pbd/memento_command.h"
31
32 #include "editor.h"
33 #include "marker.h"
34 #include "selection.h"
35 #include "editing.h"
36 #include "gui_thread.h"
37 #include "simplerect.h"
38 #include "actions.h"
39 #include "prompter.h"
40 #include "editor_drag.h"
41
42 #include "i18n.h"
43
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47 using namespace Gtk;
48 using namespace Gtkmm2ext;
49
50 void
51 Editor::clear_marker_display ()
52 {
53         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
54                 delete i->second;
55         }
56
57         location_markers.clear ();
58 }
59
60 void
61 Editor::add_new_location (Location *location)
62 {
63         ENSURE_GUI_THREAD (*this, &Editor::add_new_location, location)
64
65         LocationMarkers *lam = new LocationMarkers;
66         uint32_t color;
67
68         if (location->is_cd_marker()) {
69                 color = location_cd_marker_color;
70         } else if (location->is_mark()) {
71                 color = location_marker_color;
72         } else if (location->is_auto_loop()) {
73                 color = location_loop_color;
74         } else if (location->is_auto_punch()) {
75                 color = location_punch_color;
76         } else {
77                 color = location_range_color;
78         }
79
80         if (location->is_mark()) {
81
82                 if (location->is_cd_marker() && ruler_cd_marker_action->get_active()) {
83                         lam->start = new Marker (*this, *cd_marker_group, color, location->name(), Marker::Mark, location->start());
84                 }
85                 else {
86                         lam->start = new Marker (*this, *marker_group, color, location->name(), Marker::Mark, location->start());
87                 }
88                 lam->end   = 0;
89
90         } else if (location->is_auto_loop()) {
91                 // transport marker
92                 lam->start = new Marker (*this, *transport_marker_group, color,
93                                          location->name(), Marker::LoopStart, location->start());
94                 lam->end   = new Marker (*this, *transport_marker_group, color,
95                                          location->name(), Marker::LoopEnd, location->end());
96
97         } else if (location->is_auto_punch()) {
98                 // transport marker
99                 lam->start = new Marker (*this, *transport_marker_group, color,
100                                          location->name(), Marker::PunchIn, location->start());
101                 lam->end   = new Marker (*this, *transport_marker_group, color,
102                                          location->name(), Marker::PunchOut, location->end());
103
104         } else if (location->is_session_range()) {
105                 // session range
106                 lam->start = new Marker (*this, *marker_group, color, _("start"), Marker::Start, location->start());
107                 lam->end = new Marker (*this, *marker_group, color, _("end"), Marker::End, location->end());
108                 
109         } else {
110                 // range marker
111                 if (location->is_cd_marker() && ruler_cd_marker_action->get_active()) {
112                         lam->start = new Marker (*this, *cd_marker_group, color,
113                                                  location->name(), Marker::Start, location->start());
114                         lam->end   = new Marker (*this, *cd_marker_group, color,
115                                                  location->name(), Marker::End, location->end());
116                 }
117                 else {
118                         lam->start = new Marker (*this, *range_marker_group, color,
119                                                  location->name(), Marker::Start, location->start());
120                         lam->end   = new Marker (*this, *range_marker_group, color,
121                                                  location->name(), Marker::End, location->end());
122                 }
123         }
124
125         if (location->is_hidden ()) {
126                 lam->hide();
127         } else {
128                 lam->show ();
129         }
130
131         location->start_changed.connect (*this, invalidator (*this), ui_bind (&Editor::location_changed, this, _1), gui_context());
132         location->end_changed.connect (*this, invalidator (*this), ui_bind (&Editor::location_changed, this, _1), gui_context());
133         location->changed.connect (*this, invalidator (*this), ui_bind (&Editor::location_changed, this, _1), gui_context());
134         location->name_changed.connect (*this, invalidator (*this), ui_bind (&Editor::location_changed, this, _1), gui_context());
135         location->FlagsChanged.connect (*this, invalidator (*this), ui_bind (&Editor::location_flags_changed, this, _1, _2), gui_context());
136
137         pair<Location*,LocationMarkers*> newpair;
138
139         newpair.first = location;
140         newpair.second = lam;
141
142         location_markers.insert (newpair);
143
144         if (select_new_marker && location->is_mark()) {
145                 selection->set (lam->start);
146                 select_new_marker = false;
147         }
148 }
149
150 void
151 Editor::location_changed (Location *location)
152 {
153         ENSURE_GUI_THREAD (*this, &Editor::location_changed, location)
154
155         LocationMarkers *lam = find_location_markers (location);
156
157         if (lam == 0) {
158                 /* a location that isn't "marked" with markers */
159                 return;
160         }
161
162         lam->set_name (location->name ());
163         lam->set_position (location->start(), location->end());
164
165         if (location->is_auto_loop()) {
166                 update_loop_range_view ();
167         } else if (location->is_auto_punch()) {
168                 update_punch_range_view ();
169         }
170 }
171
172 void
173 Editor::location_flags_changed (Location *location, void*)
174 {
175         ENSURE_GUI_THREAD (*this, &Editor::location_flags_changed, location, src)
176
177         LocationMarkers *lam = find_location_markers (location);
178
179         if (lam == 0) {
180                 /* a location that isn't "marked" with markers */
181                 return;
182         }
183
184         // move cd markers to/from cd marker bar as appropriate
185         ensure_cd_marker_updated (lam, location);
186
187         if (location->is_cd_marker()) {
188                 lam->set_color_rgba (location_cd_marker_color);
189         } else if (location->is_mark()) {
190                 lam->set_color_rgba (location_marker_color);
191         } else if (location->is_auto_punch()) {
192                 lam->set_color_rgba (location_punch_color);
193         } else if (location->is_auto_loop()) {
194                 lam->set_color_rgba (location_loop_color);
195         } else {
196                 lam->set_color_rgba (location_range_color);
197         }
198
199         if (location->is_hidden()) {
200                 lam->hide();
201         } else {
202                 lam->show ();
203         }
204 }
205
206 void Editor::update_cd_marker_display ()
207 {
208         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
209                 LocationMarkers * lam = i->second;
210                 Location * location = i->first;
211
212                 ensure_cd_marker_updated (lam, location);
213         }
214 }
215
216 void Editor::ensure_cd_marker_updated (LocationMarkers * lam, Location * location)
217 {
218         if (location->is_cd_marker()
219             && (ruler_cd_marker_action->get_active() &&  lam->start->get_parent() != cd_marker_group))
220         {
221                 //cerr << "reparenting non-cd marker so it can be relocated: " << location->name() << endl;
222                 if (lam->start) {
223                         lam->start->reparent (*cd_marker_group);
224                 }
225                 if (lam->end) {
226                         lam->end->reparent (*cd_marker_group);
227                 }
228         }
229         else if ( (!location->is_cd_marker() || !ruler_cd_marker_action->get_active())
230                   && (lam->start->get_parent() == cd_marker_group))
231         {
232                 //cerr << "reparenting non-cd marker so it can be relocated: " << location->name() << endl;
233                 if (location->is_mark()) {
234                         if (lam->start) {
235                                 lam->start->reparent (*marker_group);
236                         }
237                         if (lam->end) {
238                                 lam->end->reparent (*marker_group);
239                         }
240                 }
241                 else {
242                         if (lam->start) {
243                                 lam->start->reparent (*range_marker_group);
244                         }
245                         if (lam->end) {
246                                 lam->end->reparent (*range_marker_group);
247                         }
248                 }
249         }
250 }
251
252 Editor::LocationMarkers::~LocationMarkers ()
253 {
254         delete start;
255         delete end;
256 }
257
258 Editor::LocationMarkers *
259 Editor::find_location_markers (Location *location) const
260 {
261         LocationMarkerMap::const_iterator i;
262
263         for (i = location_markers.begin(); i != location_markers.end(); ++i) {
264                 if ((*i).first == location) {
265                         return (*i).second;
266                 }
267         }
268
269         return 0;
270 }
271
272 Location *
273 Editor::find_location_from_marker (Marker *marker, bool& is_start) const
274 {
275         LocationMarkerMap::const_iterator i;
276
277         for (i = location_markers.begin(); i != location_markers.end(); ++i) {
278                 LocationMarkers *lm = (*i).second;
279                 if (lm->start == marker) {
280                         is_start = true;
281                         return (*i).first;
282                 } else if (lm->end == marker) {
283                         is_start = false;
284                         return (*i).first;
285                 }
286         }
287
288         return 0;
289 }
290
291 void
292 Editor::refresh_location_display_internal (Locations::LocationList& locations)
293 {
294         /* invalidate all */
295
296         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
297                 i->second->valid = false;
298         }
299
300         /* add new ones */
301
302         for (Locations::LocationList::iterator i = locations.begin(); i != locations.end(); ++i) {
303
304                 LocationMarkerMap::iterator x;
305
306                 if ((x = location_markers.find (*i)) != location_markers.end()) {
307                         x->second->valid = true;
308                         continue;
309                 }
310
311                 add_new_location (*i);
312         }
313
314         /* remove dead ones */
315
316         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ) {
317
318                 LocationMarkerMap::iterator tmp;
319
320                 tmp = i;
321                 ++tmp;
322
323                 if (!i->second->valid) {
324                         delete i->second;
325                         location_markers.erase (i);
326                 }
327
328                 i = tmp;
329         }
330
331         update_punch_range_view (false);
332         update_loop_range_view (false);
333 }
334
335 void
336 Editor::refresh_location_display ()
337 {
338         ENSURE_GUI_THREAD (*this, &Editor::refresh_location_display)
339
340         if (_session) {
341                 _session->locations()->apply (*this, &Editor::refresh_location_display_internal);
342         }
343 }
344
345 void
346 Editor::refresh_location_display_s (const PropertyChange&)
347 {
348         ENSURE_GUI_THREAD (*this, &Editor::refresh_location_display_s, ignored)
349
350         if (_session) {
351                 _session->locations()->apply (*this, &Editor::refresh_location_display_internal);
352         }
353 }
354
355 void
356 Editor::LocationMarkers::hide()
357 {
358         start->hide ();
359         if (end) { end->hide(); }
360 }
361
362 void
363 Editor::LocationMarkers::show()
364 {
365         start->show ();
366         if (end) { end->show(); }
367 }
368
369 void
370 Editor::LocationMarkers::set_name (const string& str)
371 {
372         /* XXX: hack: don't change names of session start/end markers */
373         
374         if (start->type() != Marker::Start) {
375                 start->set_name (str);
376         }
377         
378         if (end && end->type() != Marker::End) {
379                 end->set_name (str);
380         }
381 }
382
383 void
384 Editor::LocationMarkers::set_position (framepos_t startf,
385                                        framepos_t endf)
386 {
387         start->set_position (startf);
388         if (end) { end->set_position (endf); }
389 }
390
391 void
392 Editor::LocationMarkers::set_color_rgba (uint32_t rgba)
393 {
394         start->set_color_rgba (rgba);
395         if (end) { end->set_color_rgba (rgba); }
396 }
397
398 void
399 Editor::mouse_add_new_marker (framepos_t where, bool is_cd, bool is_xrun)
400 {
401         string markername, markerprefix;
402         int flags = (is_cd ? Location::IsCDMarker|Location::IsMark : Location::IsMark);
403
404         if (is_xrun) {
405                 markerprefix = "xrun";
406                 flags = Location::IsMark;
407         } else {
408                 markerprefix = "mark";
409         }
410
411         if (_session) {
412                 _session->locations()->next_available_name(markername, markerprefix);
413                 if (!is_xrun && !choose_new_marker_name(markername)) {
414                         return;
415                 }
416                 Location *location = new Location (*_session, where, where, markername, (Location::Flags) flags);
417                 _session->begin_reversible_command (_("add marker"));
418                 XMLNode &before = _session->locations()->get_state();
419                 _session->locations()->add (location, true);
420                 XMLNode &after = _session->locations()->get_state();
421                 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
422                 _session->commit_reversible_command ();
423
424                 /* find the marker we just added */
425
426                 LocationMarkers *lam = find_location_markers (location);
427                 if (lam) {
428                         /* make it the selected marker */
429                         selection->set (lam->start);
430                 }
431         }
432 }
433
434 void
435 Editor::remove_marker (ArdourCanvas::Item& item, GdkEvent*)
436 {
437         Marker* marker;
438         bool is_start;
439
440         if ((marker = static_cast<Marker*> (item.get_data ("marker"))) == 0) {
441                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
442                 /*NOTREACHED*/
443         }
444
445         if (entered_marker == marker) {
446                 entered_marker = NULL;
447         }
448
449         Location* loc = find_location_from_marker (marker, is_start);
450
451         if (_session && loc) {
452                 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
453         }
454 }
455
456 gint
457 Editor::really_remove_marker (Location* loc)
458 {
459         _session->begin_reversible_command (_("remove marker"));
460         XMLNode &before = _session->locations()->get_state();
461         _session->locations()->remove (loc);
462         XMLNode &after = _session->locations()->get_state();
463         _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
464         _session->commit_reversible_command ();
465         return FALSE;
466 }
467
468 void
469 Editor::location_gone (Location *location)
470 {
471         ENSURE_GUI_THREAD (*this, &Editor::location_gone, location)
472
473         LocationMarkerMap::iterator i;
474
475         if (location == transport_loop_location()) {
476                 update_loop_range_view (true);
477         }
478
479         if (location == transport_punch_location()) {
480                 update_punch_range_view (true);
481         }
482
483         for (i = location_markers.begin(); i != location_markers.end(); ++i) {
484                 if ((*i).first == location) {
485                         delete (*i).second;
486                         location_markers.erase (i);
487                         break;
488                 }
489         }
490 }
491
492 void
493 Editor::tempo_or_meter_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
494 {
495         marker_menu_item = item;
496         
497         MeterMarker* mm;
498         TempoMarker* tm;
499         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
500
501         bool can_remove = false;
502
503         if (mm) {
504                 can_remove = mm->meter().movable ();
505         } else if (tm) {
506                 can_remove = tm->tempo().movable ();
507         } else {
508                 return;
509         }
510         
511         delete tempo_or_meter_marker_menu;
512         build_tempo_or_meter_marker_menu (can_remove);
513         tempo_or_meter_marker_menu->popup (1, ev->time);
514 }
515
516 void
517 Editor::marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
518 {
519         Marker * marker;
520         if ((marker = reinterpret_cast<Marker *> (item->get_data("marker"))) == 0) {
521                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
522                 /*NOTREACHED*/
523         }
524
525         bool is_start;
526         Location * loc = find_location_from_marker (marker, is_start);
527         if (loc == transport_loop_location() || loc == transport_punch_location()) {
528                 if (transport_marker_menu == 0) {
529                         build_range_marker_menu (true);
530                 }
531                 marker_menu_item = item;
532                 transport_marker_menu->popup (1, ev->time);
533         } else {
534
535                 if (loc->is_mark()) {
536                         Menu *markerMenu;
537                         if (loc->is_session_range ()) {
538                                 delete session_range_marker_menu;
539                                 build_marker_menu (true, loc);
540                                 markerMenu = session_range_marker_menu;
541                         } else {
542                                 delete marker_menu;
543                                 build_marker_menu (false, loc);
544                                 markerMenu = marker_menu;
545                         }
546
547
548                 // GTK2FIX use action group sensitivity
549 #ifdef GTK2FIX
550                 if (children.size() >= 3) {
551                         MenuItem * loopitem = &children[2];
552                         if (loopitem) {
553                                 if (loc->is_mark()) {
554                                         loopitem->set_sensitive(false);
555                                 }
556                                 else {
557                                         loopitem->set_sensitive(true);
558                                 }
559                         }
560                 }
561 #endif
562                 marker_menu_item = item;
563                 markerMenu->popup (1, ev->time);
564                 }
565
566                 if (loc->is_range_marker()) {
567                        if (range_marker_menu == 0){
568                               build_range_marker_menu (false);
569                        }
570                        marker_menu_item = item;
571                        range_marker_menu->popup (1, ev->time);
572                 }
573         }
574 }
575
576 void
577 Editor::new_transport_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item*)
578 {
579         if (new_transport_marker_menu == 0) {
580                 build_new_transport_marker_menu ();
581         }
582
583         new_transport_marker_menu->popup (1, ev->time);
584
585 }
586
587 void
588 Editor::transport_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item*)
589 {
590         if (transport_marker_menu == 0) {
591                 build_range_marker_menu (true);
592         }
593
594         transport_marker_menu->popup (1, ev->time);
595 }
596
597 void
598 Editor::build_marker_menu (bool session_range, Location* loc)
599 {
600         using namespace Menu_Helpers;
601
602         Menu *markerMenu = new Menu;
603         if (session_range) {
604                 session_range_marker_menu = markerMenu;
605         } else {
606                 marker_menu = markerMenu;
607         }
608         MenuList& items = markerMenu->items();
609         markerMenu->set_name ("ArdourContextMenu");
610
611         items.push_back (MenuElem (_("Locate to Here"), sigc::mem_fun(*this, &Editor::marker_menu_set_playhead)));
612         items.push_back (MenuElem (_("Play from Here"), sigc::mem_fun(*this, &Editor::marker_menu_play_from)));
613         items.push_back (MenuElem (_("Move Mark to Playhead"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
614
615         items.push_back (SeparatorElem());
616
617         items.push_back (MenuElem (_("Create Range to Next Marker"), sigc::mem_fun(*this, &Editor::marker_menu_range_to_next)));
618
619         items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &Editor::marker_menu_hide)));
620         if (session_range) {
621                 return;
622         }
623         items.push_back (MenuElem (_("Rename"), sigc::mem_fun(*this, &Editor::marker_menu_rename)));
624
625         items.push_back (CheckMenuElem (_("Lock")));
626         CheckMenuItem* lock_item = static_cast<CheckMenuItem*> (&items.back());
627         if (loc->locked ()) {
628                 lock_item->set_active ();
629         }
630         lock_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_marker_menu_lock));
631
632         items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
633         CheckMenuItem* glue_item = static_cast<CheckMenuItem*> (&items.back());
634         if (loc->position_lock_style() == MusicTime) {
635                 glue_item->set_active ();
636         }
637         glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_marker_menu_glue));
638         
639         items.push_back (SeparatorElem());
640
641         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
642 }
643
644 void
645 Editor::build_range_marker_menu (bool loop_or_punch)
646 {
647         using namespace Menu_Helpers;
648
649         Menu *markerMenu = new Menu;
650         if (loop_or_punch) {
651                 transport_marker_menu = markerMenu;
652         } else {
653                 range_marker_menu = markerMenu;
654         }
655         MenuList& items = markerMenu->items();
656         markerMenu->set_name ("ArdourContextMenu");
657
658         items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::marker_menu_play_range)));
659         items.push_back (MenuElem (_("Locate to Range Mark"), sigc::mem_fun(*this, &Editor::marker_menu_set_playhead)));
660         items.push_back (MenuElem (_("Play from Range Mark"), sigc::mem_fun(*this, &Editor::marker_menu_play_from)));
661         if (! loop_or_punch) {
662                 items.push_back (MenuElem (_("Loop Range"), sigc::mem_fun(*this, &Editor::marker_menu_loop_range)));
663         }
664         items.push_back (MenuElem (_("Set Range Mark from Playhead"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
665         if (!Profile->get_sae()) {
666                 items.push_back (MenuElem (_("Set Range from Range Selection"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_selection)));
667         }
668
669         items.push_back (SeparatorElem());
670         items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_range)));
671         items.push_back (SeparatorElem());
672
673         if (!loop_or_punch) {
674                 items.push_back (MenuElem (_("Hide Range"), sigc::mem_fun(*this, &Editor::marker_menu_hide)));
675                 items.push_back (MenuElem (_("Rename Range"), sigc::mem_fun(*this, &Editor::marker_menu_rename)));
676                 items.push_back (MenuElem (_("Remove Range"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
677                 items.push_back (SeparatorElem());
678         }
679
680         items.push_back (MenuElem (_("Separate Regions in Range"), sigc::mem_fun(*this, &Editor::marker_menu_separate_regions_using_location)));
681         items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::marker_menu_select_all_selectables_using_range)));
682         if (!Profile->get_sae()) {
683                 items.push_back (MenuElem (_("Select Range"), sigc::mem_fun(*this, &Editor::marker_menu_select_using_range)));
684         }
685 }
686
687 void
688 Editor::build_tempo_or_meter_marker_menu (bool can_remove)
689 {
690         using namespace Menu_Helpers;
691
692         tempo_or_meter_marker_menu = new Menu;
693         MenuList& items = tempo_or_meter_marker_menu->items();
694         tempo_or_meter_marker_menu->set_name ("ArdourContextMenu");
695
696         items.push_back (MenuElem (_("Edit"), sigc::mem_fun(*this, &Editor::marker_menu_edit)));
697         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
698
699         items.back().set_sensitive (can_remove);
700 }
701
702 void
703 Editor::build_new_transport_marker_menu ()
704 {
705         using namespace Menu_Helpers;
706
707         new_transport_marker_menu = new Menu;
708         MenuList& items = new_transport_marker_menu->items();
709         new_transport_marker_menu->set_name ("ArdourContextMenu");
710
711         items.push_back (MenuElem (_("Set Loop Range"), sigc::mem_fun(*this, &Editor::new_transport_marker_menu_set_loop)));
712         items.push_back (MenuElem (_("Set Punch Range"), sigc::mem_fun(*this, &Editor::new_transport_marker_menu_set_punch)));
713
714         new_transport_marker_menu->signal_unmap().connect ( sigc::mem_fun(*this, &Editor::new_transport_marker_menu_popdown));
715 }
716
717 void
718 Editor::marker_menu_hide ()
719 {
720         Marker* marker;
721
722         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
723                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
724                 /*NOTREACHED*/
725         }
726
727         Location* l;
728         bool is_start;
729
730         if ((l = find_location_from_marker (marker, is_start)) != 0) {
731                 l->set_hidden (true, this);
732         }
733 }
734
735 void
736 Editor::marker_menu_select_using_range ()
737 {
738         Marker* marker;
739
740         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
741                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
742                 /*NOTREACHED*/
743         }
744
745         Location* l;
746         bool is_start;
747
748         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
749                 set_selection_from_range (*l);
750         }
751 }
752
753 void
754 Editor::marker_menu_select_all_selectables_using_range ()
755 {
756         Marker* marker;
757
758         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
759                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
760                 /*NOTREACHED*/
761         }
762
763         Location* l;
764         bool is_start;
765
766         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
767                 select_all_within (l->start(), l->end() - 1, 0,  DBL_MAX, track_views, Selection::Set, false);
768         }
769
770 }
771
772 void
773 Editor::marker_menu_separate_regions_using_location ()
774 {
775         Marker* marker;
776
777         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
778                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
779                 /*NOTREACHED*/
780         }
781
782         Location* l;
783         bool is_start;
784
785         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
786                 separate_regions_using_location (*l);
787         }
788
789 }
790
791 void
792 Editor::marker_menu_play_from ()
793 {
794         Marker* marker;
795
796         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
797                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
798                 /*NOTREACHED*/
799         }
800
801         Location* l;
802         bool is_start;
803
804         if ((l = find_location_from_marker (marker, is_start)) != 0) {
805
806                 if (l->is_mark()) {
807                         _session->request_locate (l->start(), true);
808                 }
809                 else {
810                         //_session->request_bounded_roll (l->start(), l->end());
811
812                         if (is_start) {
813                                 _session->request_locate (l->start(), true);
814                         } else {
815                                 _session->request_locate (l->end(), true);
816                         }
817                 }
818         }
819 }
820
821 void
822 Editor::marker_menu_set_playhead ()
823 {
824         Marker* marker;
825
826         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
827                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
828                 /*NOTREACHED*/
829         }
830
831         Location* l;
832         bool is_start;
833
834         if ((l = find_location_from_marker (marker, is_start)) != 0) {
835
836                 if (l->is_mark()) {
837                         _session->request_locate (l->start(), false);
838                 }
839                 else {
840                         if (is_start) {
841                                 _session->request_locate (l->start(), false);
842                         } else {
843                                 _session->request_locate (l->end(), false);
844                         }
845                 }
846         }
847 }
848
849 void
850 Editor::marker_menu_range_to_next ()
851 {
852         Marker* marker;
853         if (!_session) {
854                 return;
855         }
856
857         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
858                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
859                 /*NOTREACHED*/
860         }
861
862         Location* l;
863         bool is_start;
864
865         if ((l = find_location_from_marker (marker, is_start)) == 0) {
866                 return;
867         }
868
869         framepos_t start;
870         framepos_t end;
871         _session->locations()->marks_either_side (marker->position(), start, end);
872
873         if (end != max_framepos) {
874                 string range_name = l->name();
875                 range_name += "-range";
876
877                 Location* newrange = new Location (*_session, marker->position(), end, range_name, Location::IsRangeMarker);
878                 _session->locations()->add (newrange);
879         }
880 }
881
882 void
883 Editor::marker_menu_set_from_playhead ()
884 {
885         Marker* marker;
886
887         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
888                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
889                 /*NOTREACHED*/
890         }
891
892         Location* l;
893         bool is_start;
894
895         if ((l = find_location_from_marker (marker, is_start)) != 0) {
896
897                 if (l->is_mark()) {
898                         l->set_start (_session->audible_frame ());
899                 }
900                 else {
901                         if (is_start) {
902                                 l->set_start (_session->audible_frame ());
903                         } else {
904                                 l->set_end (_session->audible_frame ());
905                         }
906                 }
907         }
908 }
909
910 void
911 Editor::marker_menu_set_from_selection ()
912 {
913         Marker* marker;
914
915         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
916                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
917                 /*NOTREACHED*/
918         }
919
920         Location* l;
921         bool is_start;
922
923         if ((l = find_location_from_marker (marker, is_start)) != 0) {
924
925                 if (l->is_mark()) {
926                         // nothing for now
927                 }
928                 else {
929
930                         /* if range selection use first to last */
931
932                         if (mouse_mode == Editing::MouseRange) {
933                                 if (!selection->time.empty()) {
934                                         l->set_start (selection->time.start());
935                                         l->set_end (selection->time.end_frame());
936                                 }
937                         }
938                         else {
939                                 if (!selection->regions.empty()) {
940                                         l->set_start (selection->regions.start());
941                                         l->set_end (selection->regions.end_frame());
942                                 }
943                         }
944                 }
945         }
946 }
947
948
949 void
950 Editor::marker_menu_play_range ()
951 {
952         Marker* marker;
953
954         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
955                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
956                 /*NOTREACHED*/
957         }
958
959         Location* l;
960         bool is_start;
961
962         if ((l = find_location_from_marker (marker, is_start)) != 0) {
963
964                 if (l->is_mark()) {
965                         _session->request_locate (l->start(), true);
966                 }
967                 else {
968                         _session->request_bounded_roll (l->start(), l->end());
969
970                 }
971         }
972 }
973
974 void
975 Editor::marker_menu_loop_range ()
976 {
977         Marker* marker;
978
979         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
980                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
981                 /*NOTREACHED*/
982         }
983
984         Location* l;
985         bool is_start;
986
987         if ((l = find_location_from_marker (marker, is_start)) != 0) {
988                 Location* l2;
989                 if ((l2 = transport_loop_location()) != 0) {
990                         l2->set (l->start(), l->end());
991
992                         // enable looping, reposition and start rolling
993                         _session->request_play_loop(true);
994                         _session->request_locate (l2->start(), true);
995                 }
996         }
997 }
998
999 void
1000 Editor::dynamic_cast_marker_object (void* p, MeterMarker** m, TempoMarker** t) const
1001 {
1002         Marker* marker = reinterpret_cast<Marker*> (p);
1003         if (!marker) {
1004                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1005                 /*NOTREACHED*/
1006         }
1007
1008         *m = dynamic_cast<MeterMarker*> (marker);
1009         *t = dynamic_cast<TempoMarker*> (marker);
1010 }
1011
1012 void
1013 Editor::marker_menu_edit ()
1014 {
1015         MeterMarker* mm;
1016         TempoMarker* tm;
1017         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1018
1019         if (mm) {
1020                 edit_meter_section (&mm->meter());
1021         } else if (tm) {
1022                 edit_tempo_section (&tm->tempo());
1023         }
1024 }
1025
1026 void
1027 Editor::marker_menu_remove ()
1028 {
1029         MeterMarker* mm;
1030         TempoMarker* tm;
1031         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1032
1033         if (mm) {
1034                 remove_meter_marker (marker_menu_item);
1035         } else if (tm) {
1036                 remove_tempo_marker (marker_menu_item);
1037         } else {
1038                 remove_marker (*marker_menu_item, (GdkEvent*) 0);
1039         }
1040 }
1041
1042 void
1043 Editor::toggle_marker_menu_lock ()
1044 {
1045         Marker* marker;
1046
1047         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
1048                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1049                 /*NOTREACHED*/
1050         }
1051
1052         Location* loc;
1053         bool ignored;
1054
1055         loc = find_location_from_marker (marker, ignored);
1056
1057         if (!loc) {
1058                 return;
1059         }
1060
1061         if (loc->locked()) {
1062                 loc->unlock ();
1063         } else {
1064                 loc->lock ();
1065         }
1066 }
1067
1068 void
1069 Editor::marker_menu_rename ()
1070 {
1071         Marker* marker;
1072
1073         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
1074                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1075                 /*NOTREACHED*/
1076         }
1077
1078         Location* loc;
1079         bool is_start;
1080
1081         loc = find_location_from_marker (marker, is_start);
1082
1083         if (!loc) return;
1084
1085         ArdourPrompter dialog (true);
1086         string txt;
1087
1088         dialog.set_prompt (_("New Name:"));
1089
1090         if (loc->is_mark()) {
1091                 dialog.set_title (_("Rename Mark"));
1092         } else {
1093                 dialog.set_title (_("Rename Range"));
1094         }
1095
1096         dialog.set_name ("MarkRenameWindow");
1097         dialog.set_size_request (250, -1);
1098         dialog.set_position (Gtk::WIN_POS_MOUSE);
1099
1100         dialog.add_button (_("Rename"), RESPONSE_ACCEPT);
1101         dialog.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1102         dialog.set_initial_text (loc->name());
1103
1104         dialog.show ();
1105
1106         switch (dialog.run ()) {
1107         case RESPONSE_ACCEPT:
1108                 break;
1109         default:
1110                 return;
1111         }
1112
1113         begin_reversible_command ( _("rename marker") );
1114         XMLNode &before = _session->locations()->get_state();
1115
1116         dialog.get_result(txt);
1117         loc->set_name (txt);
1118
1119         XMLNode &after = _session->locations()->get_state();
1120         _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1121         commit_reversible_command ();
1122 }
1123
1124 void
1125 Editor::new_transport_marker_menu_popdown ()
1126 {
1127         // hide rects
1128         transport_bar_drag_rect->hide();
1129
1130         _drags->abort ();
1131 }
1132
1133 void
1134 Editor::new_transport_marker_menu_set_loop ()
1135 {
1136         set_loop_range (temp_location->start(), temp_location->end(), _("set loop range"));
1137 }
1138
1139 void
1140 Editor::new_transport_marker_menu_set_punch ()
1141 {
1142         set_punch_range (temp_location->start(), temp_location->end(), _("set punch range"));
1143 }
1144
1145 void
1146 Editor::update_loop_range_view (bool visibility)
1147 {
1148         if (_session == 0) {
1149                 return;
1150         }
1151
1152         Location* tll;
1153
1154         if (_session->get_play_loop() && ((tll = transport_loop_location()) != 0)) {
1155
1156                 double x1 = frame_to_pixel (tll->start());
1157                 double x2 = frame_to_pixel (tll->end());
1158
1159                 transport_loop_range_rect->property_x1() = x1;
1160                 transport_loop_range_rect->property_x2() = x2;
1161
1162                 if (visibility) {
1163                         transport_loop_range_rect->show();
1164                 }
1165
1166         } else if (visibility) {
1167                 transport_loop_range_rect->hide();
1168         }
1169 }
1170
1171 void
1172 Editor::update_punch_range_view (bool visibility)
1173 {
1174         if (_session == 0) {
1175                 return;
1176         }
1177
1178         Location* tpl;
1179
1180         if ((_session->config.get_punch_in() || _session->config.get_punch_out()) && ((tpl = transport_punch_location()) != 0)) {
1181                 guint track_canvas_width,track_canvas_height;
1182                 track_canvas->get_size(track_canvas_width,track_canvas_height);
1183                 if (_session->config.get_punch_in()) {
1184                         transport_punch_range_rect->property_x1() = frame_to_pixel (tpl->start());
1185                         transport_punch_range_rect->property_x2() = (_session->config.get_punch_out() ? frame_to_pixel (tpl->end()) : frame_to_pixel (JACK_MAX_FRAMES));
1186                 } else {
1187                         transport_punch_range_rect->property_x1() = 0;
1188                         transport_punch_range_rect->property_x2() = (_session->config.get_punch_out() ? frame_to_pixel (tpl->end()) : track_canvas_width);
1189                 }
1190
1191                 if (visibility) {
1192                         transport_punch_range_rect->show();
1193                 }
1194         } else if (visibility) {
1195                 transport_punch_range_rect->hide();
1196         }
1197 }
1198
1199 void
1200 Editor::marker_selection_changed ()
1201 {
1202         if (_session && _session->deletion_in_progress()) {
1203                 return;
1204         }
1205
1206         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1207                 LocationMarkers* lam = i->second;
1208
1209                 if (lam->start) {
1210                         lam->start->hide_line();
1211                 }
1212
1213                 if (lam->end) {
1214                         lam->end->hide_line();
1215                 }
1216         }
1217
1218         for (MarkerSelection::iterator x = selection->markers.begin(); x != selection->markers.end(); ++x) {
1219                 (*x)->add_line (cursor_group, 0, _canvas_height);
1220                 (*x)->show_line ();
1221         }
1222 }
1223
1224 struct SortLocationsByPosition {
1225     bool operator() (Location* a, Location* b) {
1226             return a->start() < b->start();
1227     }
1228 };
1229
1230 void
1231 Editor::goto_nth_marker (int n)
1232 {
1233         if (!_session) {
1234                 return;
1235         }
1236         const Locations::LocationList& l (_session->locations()->list());
1237         Locations::LocationList ordered;
1238         ordered = l;
1239
1240         SortLocationsByPosition cmp;
1241         ordered.sort (cmp);
1242
1243         for (Locations::LocationList::iterator i = ordered.begin(); n >= 0 && i != ordered.end(); ++i) {
1244                 if ((*i)->is_mark() && !(*i)->is_hidden() && !(*i)->is_session_range()) {
1245                         if (n == 0) {
1246                                 _session->request_locate ((*i)->start(), _session->transport_rolling());
1247                                 break;
1248                         }
1249                         --n;
1250                 }
1251         }
1252 }
1253
1254 void
1255 Editor::toggle_marker_menu_glue ()
1256 {
1257         Marker* marker;
1258
1259         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
1260                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1261                 /*NOTREACHED*/
1262         }
1263
1264         Location* loc;
1265         bool ignored;
1266
1267         loc = find_location_from_marker (marker, ignored);
1268
1269         if (!loc) {
1270                 return;
1271         }
1272
1273         if (loc->position_lock_style() == MusicTime) {
1274                 loc->set_position_lock_style (AudioTime);
1275         } else {
1276                 loc->set_position_lock_style (MusicTime);
1277         }
1278
1279 }