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