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