Vkeybd: add a mod-wheel
[ardour.git] / gtk2_ardour / editor_markers.cc
1 /*
2  * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2006 Hans Fugal <hans@fugal.net>
5  * Copyright (C) 2008-2011 David Robillard <d@drobilla.net>
6  * Copyright (C) 2008-2012 Carl Hetherington <carl@carlh.net>
7  * Copyright (C) 2013-2014 John Emmas <john@creativepost.co.uk>
8  * Copyright (C) 2013-2015 Colin Fletcher <colin.m.fletcher@googlemail.com>
9  * Copyright (C) 2014-2015 Ben Loftis <ben@harrisonconsoles.com>
10  * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
11  * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #include <cstdlib>
29 #include <cmath>
30
31 #include <gtkmm2ext/gtk_ui.h>
32
33 #include "ardour/session.h"
34 #include "ardour/location.h"
35 #include "ardour/profile.h"
36 #include "pbd/memento_command.h"
37
38 #include "canvas/canvas.h"
39 #include "canvas/item.h"
40 #include "canvas/rectangle.h"
41
42 #include "widgets/prompter.h"
43
44 #include "editor.h"
45 #include "marker.h"
46 #include "selection.h"
47 #include "editing.h"
48 #include "gui_thread.h"
49 #include "actions.h"
50 #include "editor_drag.h"
51
52 #include "pbd/i18n.h"
53
54 using namespace std;
55 using namespace ARDOUR;
56 using namespace PBD;
57 using namespace Gtk;
58 using namespace Gtkmm2ext;
59
60 void
61 Editor::clear_marker_display ()
62 {
63         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
64                 delete i->second;
65         }
66
67         location_markers.clear ();
68         _sorted_marker_lists.clear ();
69 }
70
71 void
72 Editor::add_new_location (Location *location)
73 {
74         ENSURE_GUI_THREAD (*this, &Editor::add_new_location, location);
75
76         ArdourCanvas::Container* group = add_new_location_internal (location);
77
78         /* Do a full update of the markers in this group */
79         update_marker_labels (group);
80
81         if (location->is_auto_punch()) {
82                 update_punch_range_view ();
83         }
84
85         if (location->is_auto_loop()) {
86                 update_loop_range_view ();
87         }
88 }
89
90 /** Add a new location, without a time-consuming update of all marker labels;
91  *  the caller must call update_marker_labels () after calling this.
92  *  @return canvas group that the location's marker was added to.
93  */
94 ArdourCanvas::Container*
95 Editor::add_new_location_internal (Location* location)
96 {
97         LocationMarkers *lam = new LocationMarkers;
98         uint32_t color;
99
100         /* make a note here of which group this marker ends up in */
101         ArdourCanvas::Container* group = 0;
102
103         if (location->is_cd_marker()) {
104                 color = location_cd_marker_color;
105         } else if (location->is_mark()) {
106                 color = location_marker_color;
107         } else if (location->is_auto_loop()) {
108                 color = location_loop_color;
109         } else if (location->is_auto_punch()) {
110                 color = location_punch_color;
111         } else {
112                 color = location_range_color;
113         }
114
115         if (location->is_mark()) {
116
117                 if (location->is_cd_marker() && ruler_cd_marker_action->get_active()) {
118                         lam->start = new ArdourMarker (*this, *cd_marker_group, color, location->name(), ArdourMarker::Mark, location->start());
119                         group = cd_marker_group;
120                 } else {
121                         lam->start = new ArdourMarker (*this, *marker_group, color, location->name(), ArdourMarker::Mark, location->start());
122                         group = marker_group;
123                 }
124
125                 lam->end = 0;
126
127         } else if (location->is_auto_loop()) {
128
129                 // transport marker
130                 lam->start = new ArdourMarker (*this, *transport_marker_group, color,
131                                          location->name(), ArdourMarker::LoopStart, location->start());
132                 lam->end   = new ArdourMarker (*this, *transport_marker_group, color,
133                                          location->name(), ArdourMarker::LoopEnd, location->end());
134                 group = transport_marker_group;
135
136         } else if (location->is_auto_punch()) {
137
138                 // transport marker
139                 lam->start = new ArdourMarker (*this, *transport_marker_group, color,
140                                          location->name(), ArdourMarker::PunchIn, location->start());
141                 lam->end   = new ArdourMarker (*this, *transport_marker_group, color,
142                                          location->name(), ArdourMarker::PunchOut, location->end());
143                 group = transport_marker_group;
144
145         } else if (location->is_session_range()) {
146
147                 // session range
148                 lam->start = new ArdourMarker (*this, *marker_group, color, _("start"), ArdourMarker::SessionStart, location->start());
149                 lam->end = new ArdourMarker (*this, *marker_group, color, _("end"), ArdourMarker::SessionEnd, location->end());
150                 group = marker_group;
151
152         } else {
153                 // range marker
154                 if (location->is_cd_marker() && ruler_cd_marker_action->get_active()) {
155                         lam->start = new ArdourMarker (*this, *cd_marker_group, color,
156                                                  location->name(), ArdourMarker::RangeStart, location->start());
157                         lam->end   = new ArdourMarker (*this, *cd_marker_group, color,
158                                                  location->name(), ArdourMarker::RangeEnd, location->end());
159                         group = cd_marker_group;
160                 } else {
161                         lam->start = new ArdourMarker (*this, *range_marker_group, color,
162                                                  location->name(), ArdourMarker::RangeStart, location->start());
163                         lam->end   = new ArdourMarker (*this, *range_marker_group, color,
164                                                  location->name(), ArdourMarker::RangeEnd, location->end());
165                         group = range_marker_group;
166                 }
167         }
168
169         if (location->is_hidden ()) {
170                 lam->hide();
171         } else {
172                 lam->show ();
173         }
174
175         location->name_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
176         location->position_lock_style_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
177         location->FlagsChanged.connect (*this, invalidator (*this), boost::bind (&Editor::location_flags_changed, this, location), gui_context());
178
179         pair<Location*,LocationMarkers*> newpair;
180
181         newpair.first = location;
182         newpair.second = lam;
183
184         location_markers.insert (newpair);
185
186         if (select_new_marker && location->is_mark()) {
187                 selection->set (lam->start);
188                 select_new_marker = false;
189         }
190
191         lam->canvas_height_set (_visible_canvas_height);
192         lam->set_show_lines (_show_marker_lines);
193
194         /* Add these markers to the appropriate sorted marker lists, which will render
195            them unsorted until a call to update_marker_labels() sorts them out.
196         */
197         _sorted_marker_lists[group].push_back (lam->start);
198         if (lam->end) {
199                 _sorted_marker_lists[group].push_back (lam->end);
200         }
201
202         return group;
203 }
204
205 void
206 Editor::location_changed (Location *location)
207 {
208         ENSURE_GUI_THREAD (*this, &Editor::location_changed, location)
209
210         LocationMarkers *lam = find_location_markers (location);
211
212         if (lam == 0) {
213                 /* a location that isn't "marked" with markers */
214                 return;
215         }
216
217         if (location->position_lock_style() == MusicTime) {
218                 lam->set_name ("\u266B" + location->name ()); // BEAMED EIGHTH NOTES
219         } else {
220                 lam->set_name (location->name ());
221         }
222
223         lam->set_position (location->start(), location->end());
224
225         if (location->is_auto_loop()) {
226                 update_loop_range_view ();
227         } else if (location->is_auto_punch()) {
228                 update_punch_range_view ();
229         }
230
231         check_marker_label (lam->start);
232         if (lam->end) {
233                 check_marker_label (lam->end);
234         }
235 }
236
237 /** Look at a marker and check whether its label, and those of the previous and next markers,
238  *  need to have their labels updated (in case those labels need to be shortened or can be
239  *  lengthened)
240  */
241 void
242 Editor::check_marker_label (ArdourMarker* m)
243 {
244         /* Get a time-ordered list of markers from the last time anything changed */
245         std::list<ArdourMarker*>& sorted = _sorted_marker_lists[m->get_parent()];
246
247         list<ArdourMarker*>::iterator i = find (sorted.begin(), sorted.end(), m);
248
249         list<ArdourMarker*>::iterator prev = sorted.end ();
250         list<ArdourMarker*>::iterator next = i;
251         ++next;
252
253         /* Look to see if the previous marker is still behind `m' in time */
254         if (i != sorted.begin()) {
255
256                 prev = i;
257                 --prev;
258
259                 if ((*prev)->position() > m->position()) {
260                         /* This marker is no longer in the correct order with the previous one, so
261                          * update all the markers in this group.
262                          */
263                         update_marker_labels (m->get_parent ());
264                         return;
265                 }
266         }
267
268         /* Look to see if the next marker is still ahead of `m' in time */
269         if (next != sorted.end() && (*next)->position() < m->position()) {
270                 /* This marker is no longer in the correct order with the next one, so
271                  * update all the markers in this group.
272                  */
273                 update_marker_labels (m->get_parent ());
274                 return;
275         }
276
277         if (prev != sorted.end()) {
278
279                 /* Update just the available space between the previous marker and this one */
280
281                 double const p = sample_to_pixel (m->position() - (*prev)->position());
282
283                 if (m->label_on_left()) {
284                         (*prev)->set_right_label_limit (p / 2);
285                 } else {
286                         (*prev)->set_right_label_limit (p);
287                 }
288
289                 if ((*prev)->label_on_left ()) {
290                         m->set_left_label_limit (p);
291                 } else {
292                         m->set_left_label_limit (p / 2);
293                 }
294         }
295
296         if (next != sorted.end()) {
297
298                 /* Update just the available space between this marker and the next */
299
300                 double const p = sample_to_pixel ((*next)->position() - m->position());
301
302                 if ((*next)->label_on_left()) {
303                         m->set_right_label_limit (p / 2);
304                 } else {
305                         m->set_right_label_limit (p);
306                 }
307
308                 if (m->label_on_left()) {
309                         (*next)->set_left_label_limit (p);
310                 } else {
311                         (*next)->set_left_label_limit (p / 2);
312                 }
313         }
314 }
315
316 struct MarkerComparator {
317         bool operator() (ArdourMarker const * a, ArdourMarker const * b) {
318                 return a->position() < b->position();
319         }
320 };
321
322 /** Update all marker labels in all groups */
323 void
324 Editor::update_marker_labels ()
325 {
326         for (std::map<ArdourCanvas::Container *, std::list<ArdourMarker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
327                 update_marker_labels (i->first);
328         }
329 }
330
331 /** Look at all markers in a group and update label widths */
332 void
333 Editor::update_marker_labels (ArdourCanvas::Container* group)
334 {
335         list<ArdourMarker*>& sorted = _sorted_marker_lists[group];
336
337         if (sorted.empty()) {
338                 return;
339         }
340
341         /* We sort the list of markers and then set up the space available between each one */
342
343         sorted.sort (MarkerComparator ());
344
345         list<ArdourMarker*>::iterator i = sorted.begin ();
346
347         list<ArdourMarker*>::iterator prev = sorted.end ();
348         list<ArdourMarker*>::iterator next = i;
349
350         if (next != sorted.end()) {
351                 ++next;
352         }
353
354         while (i != sorted.end()) {
355
356                 if (prev != sorted.end()) {
357                         double const p = sample_to_pixel ((*i)->position() - (*prev)->position());
358
359                         if ((*prev)->label_on_left()) {
360                                 (*i)->set_left_label_limit (p);
361                         } else {
362                                 (*i)->set_left_label_limit (p / 2);
363                         }
364
365                 }
366
367                 if (next != sorted.end()) {
368                         double const p = sample_to_pixel ((*next)->position() - (*i)->position());
369
370                         if ((*next)->label_on_left()) {
371                                 (*i)->set_right_label_limit (p / 2);
372                         } else {
373                                 (*i)->set_right_label_limit (p);
374                         }
375
376                         ++next;
377                 }
378
379                 prev = i;
380                 ++i;
381         }
382 }
383
384 void
385 Editor::location_flags_changed (Location *location)
386 {
387         ENSURE_GUI_THREAD (*this, &Editor::location_flags_changed, location, src)
388
389         LocationMarkers *lam = find_location_markers (location);
390
391         if (lam == 0) {
392                 /* a location that isn't "marked" with markers */
393                 return;
394         }
395
396         // move cd markers to/from cd marker bar as appropriate
397         ensure_cd_marker_updated (lam, location);
398
399         if (location->is_cd_marker()) {
400                 lam->set_color_rgba (location_cd_marker_color);
401         } else if (location->is_mark()) {
402                 lam->set_color_rgba (location_marker_color);
403         } else if (location->is_auto_punch()) {
404                 lam->set_color_rgba (location_punch_color);
405         } else if (location->is_auto_loop()) {
406                 lam->set_color_rgba (location_loop_color);
407         } else {
408                 lam->set_color_rgba (location_range_color);
409         }
410
411         if (location->is_hidden()) {
412                 lam->hide();
413         } else {
414                 lam->show ();
415         }
416 }
417
418 void Editor::update_cd_marker_display ()
419 {
420         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
421                 LocationMarkers * lam = i->second;
422                 Location * location = i->first;
423
424                 ensure_cd_marker_updated (lam, location);
425         }
426 }
427
428 void Editor::ensure_cd_marker_updated (LocationMarkers * lam, Location * location)
429 {
430         if (location->is_cd_marker()
431             && (ruler_cd_marker_action->get_active() &&  lam->start->get_parent() != cd_marker_group))
432         {
433                 //cerr << "reparenting non-cd marker so it can be relocated: " << location->name() << endl;
434                 if (lam->start) {
435                         lam->start->reparent (*cd_marker_group);
436                 }
437                 if (lam->end) {
438                         lam->end->reparent (*cd_marker_group);
439                 }
440         }
441         else if ( (!location->is_cd_marker() || !ruler_cd_marker_action->get_active())
442                   && (lam->start->get_parent() == cd_marker_group))
443         {
444                 //cerr << "reparenting non-cd marker so it can be relocated: " << location->name() << endl;
445                 if (location->is_mark()) {
446                         if (lam->start) {
447                                 lam->start->reparent (*marker_group);
448                         }
449                         if (lam->end) {
450                                 lam->end->reparent (*marker_group);
451                         }
452                 }
453                 else {
454                         if (lam->start) {
455                                 lam->start->reparent (*range_marker_group);
456                         }
457                         if (lam->end) {
458                                 lam->end->reparent (*range_marker_group);
459                         }
460                 }
461         }
462 }
463
464 Editor::LocationMarkers::~LocationMarkers ()
465 {
466         delete start;
467         delete end;
468 }
469
470 Editor::LocationMarkers *
471 Editor::find_location_markers (Location *location) const
472 {
473         LocationMarkerMap::const_iterator i;
474
475         for (i = location_markers.begin(); i != location_markers.end(); ++i) {
476                 if ((*i).first == location) {
477                         return (*i).second;
478                 }
479         }
480
481         return 0;
482 }
483
484 Location *
485 Editor::find_location_from_marker (ArdourMarker *marker, bool& is_start) const
486 {
487         LocationMarkerMap::const_iterator i;
488
489         for (i = location_markers.begin(); i != location_markers.end(); ++i) {
490                 LocationMarkers *lm = (*i).second;
491                 if (lm->start == marker) {
492                         is_start = true;
493                         return (*i).first;
494                 } else if (lm->end == marker) {
495                         is_start = false;
496                         return (*i).first;
497                 }
498         }
499
500         return 0;
501 }
502
503 void
504 Editor::refresh_location_display_internal (const Locations::LocationList& locations)
505 {
506         /* invalidate all */
507
508         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
509                 i->second->valid = false;
510         }
511
512         /* add new ones */
513
514         for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
515
516                 LocationMarkerMap::iterator x;
517
518                 if ((x = location_markers.find (*i)) != location_markers.end()) {
519                         x->second->valid = true;
520                         continue;
521                 }
522
523                 add_new_location_internal (*i);
524         }
525
526         /* remove dead ones */
527
528         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ) {
529
530                 LocationMarkerMap::iterator tmp;
531
532                 tmp = i;
533                 ++tmp;
534
535                 if (!i->second->valid) {
536
537                         remove_sorted_marker (i->second->start);
538                         if (i->second->end) {
539                                 remove_sorted_marker (i->second->end);
540                         }
541
542                         LocationMarkers* m = i->second;
543                         location_markers.erase (i);
544                         delete m;
545                 }
546
547                 i = tmp;
548         }
549
550         update_punch_range_view ();
551         update_loop_range_view ();
552 }
553
554 void
555 Editor::refresh_location_display ()
556 {
557         ENSURE_GUI_THREAD (*this, &Editor::refresh_location_display)
558
559         if (_session) {
560                 _session->locations()->apply (*this, &Editor::refresh_location_display_internal);
561         }
562
563         update_marker_labels ();
564 }
565
566 void
567 Editor::LocationMarkers::hide()
568 {
569         start->hide ();
570         if (end) {
571                 end->hide ();
572         }
573 }
574
575 void
576 Editor::LocationMarkers::show()
577 {
578         start->show ();
579         if (end) {
580                 end->show ();
581         }
582 }
583
584 void
585 Editor::LocationMarkers::canvas_height_set (double h)
586 {
587         start->canvas_height_set (h);
588         if (end) {
589                 end->canvas_height_set (h);
590         }
591 }
592
593 void
594 Editor::LocationMarkers::set_name (const string& str)
595 {
596         /* XXX: hack: don't change names of session start/end markers */
597
598         if (start->type() != ArdourMarker::SessionStart) {
599                 start->set_name (str);
600         }
601
602         if (end && end->type() != ArdourMarker::SessionEnd) {
603                 end->set_name (str);
604         }
605 }
606
607 void
608 Editor::LocationMarkers::set_position (samplepos_t startf,
609                                        samplepos_t endf)
610 {
611         start->set_position (startf);
612         if (end) {
613                 end->set_position (endf);
614         }
615 }
616
617 void
618 Editor::LocationMarkers::set_color_rgba (uint32_t rgba)
619 {
620         start->set_color_rgba (rgba);
621         if (end) {
622                 end->set_color_rgba (rgba);
623         }
624 }
625
626 void
627 Editor::LocationMarkers::set_show_lines (bool s)
628 {
629         start->set_show_line (s);
630         if (end) {
631                 end->set_show_line (s);
632         }
633 }
634
635 void
636 Editor::LocationMarkers::set_selected (bool s)
637 {
638         start->set_selected (s);
639         if (end) {
640                 end->set_selected (s);
641         }
642 }
643
644 void
645 Editor::LocationMarkers::setup_lines ()
646 {
647         start->setup_line ();
648         if (end) {
649                 end->setup_line ();
650         }
651 }
652
653 void
654 Editor::mouse_add_new_marker (samplepos_t where, bool is_cd)
655 {
656         string markername;
657         int flags = (is_cd ? Location::IsCDMarker|Location::IsMark : Location::IsMark);
658
659         if (_session) {
660                 _session->locations()->next_available_name(markername, _("mark"));
661                 if (!choose_new_marker_name(markername)) {
662                         return;
663                 }
664                 Location *location = new Location (*_session, where, where, markername, (Location::Flags) flags, get_grid_music_divisions (0));
665                 begin_reversible_command (_("add marker"));
666
667                 XMLNode &before = _session->locations()->get_state();
668                 _session->locations()->add (location, true);
669                 XMLNode &after = _session->locations()->get_state();
670                 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
671
672                 /* find the marker we just added */
673
674                 LocationMarkers *lam = find_location_markers (location);
675                 if (lam) {
676                         /* make it the selected marker */
677                         selection->set (lam->start);
678                 }
679
680                 commit_reversible_command ();
681         }
682 }
683
684 void
685 Editor::mouse_add_new_loop (samplepos_t where)
686 {
687         if (!_session) {
688                 return;
689         }
690
691         /* Make this marker 1/8th of the visible area of the session so that
692            it's reasonably easy to manipulate after creation.
693         */
694
695         samplepos_t const end = where + current_page_samples() / 8;
696
697         set_loop_range (where, end,  _("set loop range"));
698 }
699
700 void
701 Editor::mouse_add_new_punch (samplepos_t where)
702 {
703         if (!_session) {
704                 return;
705         }
706
707         /* Make this marker 1/8th of the visible area of the session so that
708            it's reasonably easy to manipulate after creation.
709         */
710
711         samplepos_t const end = where + current_page_samples() / 8;
712
713         set_punch_range (where, end,  _("set punch range"));
714 }
715
716 void
717 Editor::mouse_add_new_range (samplepos_t where)
718 {
719         if (!_session) {
720                 return;
721         }
722
723         /* Make this marker 1/8th of the visible area of the session so that
724            it's reasonably easy to manipulate after creation.
725         */
726
727         samplepos_t const end = where + current_page_samples() / 8;
728
729         string name;
730         _session->locations()->next_available_name (name, _("range"));
731         Location* loc = new Location (*_session, where, end, name, Location::IsRangeMarker);
732
733         begin_reversible_command (_("new range marker"));
734         XMLNode& before = _session->locations()->get_state ();
735         _session->locations()->add (loc, true);
736         XMLNode& after = _session->locations()->get_state ();
737         _session->add_command (new MementoCommand<Locations> (*_session->locations(), &before, &after));
738         commit_reversible_command ();
739 }
740
741 void
742 Editor::remove_marker (ArdourCanvas::Item& item, GdkEvent*)
743 {
744         ArdourMarker* marker;
745         bool is_start;
746
747         if ((marker = static_cast<ArdourMarker*> (item.get_data ("marker"))) == 0) {
748                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
749                 abort(); /*NOTREACHED*/
750         }
751
752         if (entered_marker == marker) {
753                 entered_marker = NULL;
754         }
755
756         Location* loc = find_location_from_marker (marker, is_start);
757
758         if (_session && loc) {
759                 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
760         }
761 }
762
763 gint
764 Editor::really_remove_marker (Location* loc)
765 {
766         begin_reversible_command (_("remove marker"));
767         XMLNode &before = _session->locations()->get_state();
768         _session->locations()->remove (loc);
769         XMLNode &after = _session->locations()->get_state();
770         _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
771         commit_reversible_command ();
772         return FALSE;
773 }
774
775 void
776 Editor::location_gone (Location *location)
777 {
778         ENSURE_GUI_THREAD (*this, &Editor::location_gone, location)
779
780         LocationMarkerMap::iterator i;
781
782         if (location == transport_loop_location()) {
783                 update_loop_range_view ();
784         }
785
786         if (location == transport_punch_location()) {
787                 update_punch_range_view ();
788         }
789
790         for (i = location_markers.begin(); i != location_markers.end(); ++i) {
791                 if (i->first == location) {
792
793                         remove_sorted_marker (i->second->start);
794                         if (i->second->end) {
795                                 remove_sorted_marker (i->second->end);
796                         }
797
798                         LocationMarkers* m = i->second;
799                         location_markers.erase (i);
800                         delete m;
801
802                         /* Markers that visually overlap with this (removed) marker
803                          * need to be re-displayed.
804                          * But finding such cases is similarly expensive as simply
805                          * re-displaying all..  so:
806                          */
807                         refresh_location_display ();
808                         break;
809                 }
810         }
811 }
812
813 void
814 Editor::tempo_or_meter_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
815 {
816         marker_menu_item = item;
817
818         MeterMarker* mm;
819         TempoMarker* tm;
820         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
821
822         bool can_remove = false;
823
824         if (mm) {
825                 can_remove = !mm->meter().initial ();
826                 build_meter_marker_menu (mm, can_remove);
827                 meter_marker_menu->popup (1, ev->time);
828         } else if (tm) {
829                 if (!tm->tempo().active()) {
830                         return;
831                 }
832                 can_remove = !tm->tempo().initial() && !tm->tempo().locked_to_meter();
833                 build_tempo_marker_menu (tm, can_remove);
834                 tempo_marker_menu->popup (1, ev->time);
835         } else {
836                 return;
837         }
838 }
839
840 void
841 Editor::marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
842 {
843         ArdourMarker * marker;
844         if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data("marker"))) == 0) {
845                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
846                 abort(); /*NOTREACHED*/
847         }
848
849         bool is_start;
850         Location * loc = find_location_from_marker (marker, is_start);
851
852         if (loc == transport_loop_location() || loc == transport_punch_location() || loc->is_session_range ()) {
853
854                 build_range_marker_menu (loc, loc == transport_loop_location() || loc == transport_punch_location(), loc->is_session_range());
855
856                 marker_menu_item = item;
857                 range_marker_menu->popup (1, ev->time);
858
859         } else if (loc->is_mark()) {
860
861                         build_marker_menu (loc);
862
863                 // GTK2FIX use action group sensitivity
864 #ifdef GTK2FIX
865                         if (children.size() >= 3) {
866                                 MenuItem * loopitem = &children[2];
867                                 if (loopitem) {
868                                         if (loc->is_mark()) {
869                                                 loopitem->set_sensitive(false);
870                                         }
871                                         else {
872                                                 loopitem->set_sensitive(true);
873                                         }
874                                 }
875                         }
876 #endif
877                         marker_menu_item = item;
878                         marker_menu->popup (1, ev->time);
879
880         } else if (loc->is_range_marker()) {
881                 build_range_marker_menu (loc, false, false);
882                 marker_menu_item = item;
883                 range_marker_menu->popup (1, ev->time);
884         }
885 }
886
887 void
888 Editor::new_transport_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item*)
889 {
890         if (new_transport_marker_menu == 0) {
891                 build_new_transport_marker_menu ();
892         }
893
894         new_transport_marker_menu->popup (1, ev->time);
895
896 }
897
898 void
899 Editor::build_marker_menu (Location* loc)
900 {
901         using namespace Menu_Helpers;
902
903         delete marker_menu;
904         marker_menu = new Menu;
905
906         MenuList& items = marker_menu->items();
907         marker_menu->set_name ("ArdourContextMenu");
908
909         items.push_back (MenuElem (_("Locate to Here"), sigc::mem_fun(*this, &Editor::marker_menu_set_playhead)));
910         items.push_back (MenuElem (_("Play from Here"), sigc::mem_fun(*this, &Editor::marker_menu_play_from)));
911         items.push_back (MenuElem (_("Move Mark to Playhead"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
912
913         items.push_back (SeparatorElem());
914
915         items.push_back (MenuElem (_("Create Range to Next Marker"), sigc::mem_fun(*this, &Editor::marker_menu_range_to_next)));
916
917         items.push_back (MenuElem (_("Promote to Time Origin"), sigc::mem_fun(*this, &Editor::marker_menu_set_origin)));
918         items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &Editor::marker_menu_hide)));
919         items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::marker_menu_rename)));
920
921         items.push_back (CheckMenuElem (_("Lock")));
922         Gtk::CheckMenuItem* lock_item = static_cast<Gtk::CheckMenuItem*> (&items.back());
923         if (loc->locked ()) {
924                 lock_item->set_active ();
925         }
926         lock_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_marker_menu_lock));
927
928         items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
929         Gtk::CheckMenuItem* glue_item = static_cast<Gtk::CheckMenuItem*> (&items.back());
930         glue_item->set_active (loc->position_lock_style() == MusicTime);
931
932         glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_marker_menu_glue));
933
934         items.push_back (SeparatorElem());
935
936         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
937 }
938
939 void
940 Editor::build_range_marker_menu (Location* loc, bool loop_or_punch, bool session)
941 {
942         using namespace Menu_Helpers;
943
944         bool const loop_or_punch_or_session = loop_or_punch || session;
945
946         delete range_marker_menu;
947         range_marker_menu = new Menu;
948
949         MenuList& items = range_marker_menu->items();
950         range_marker_menu->set_name ("ArdourContextMenu");
951
952         items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::marker_menu_play_range)));
953         items.push_back (MenuElem (_("Locate to Marker"), sigc::mem_fun(*this, &Editor::marker_menu_set_playhead)));
954         items.push_back (MenuElem (_("Play from Marker"), sigc::mem_fun(*this, &Editor::marker_menu_play_from)));
955         items.push_back (MenuElem (_("Loop Range"), sigc::mem_fun(*this, &Editor::marker_menu_loop_range)));
956
957         items.push_back (MenuElem (_("Set Marker from Playhead"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
958         items.push_back (MenuElem (_("Set Range from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::marker_menu_set_from_selection), false)));
959
960         items.push_back (MenuElem (_("Zoom to Range"), sigc::mem_fun (*this, &Editor::marker_menu_zoom_to_range)));
961
962         items.push_back (SeparatorElem());
963         items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
964
965         Gtk::CheckMenuItem* glue_item = static_cast<Gtk::CheckMenuItem*> (&items.back());
966         glue_item->set_active (loc->position_lock_style() == MusicTime);
967         glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_marker_menu_glue));
968
969         items.push_back (SeparatorElem());
970         items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_range)));
971         items.push_back (SeparatorElem());
972
973         items.push_back (MenuElem (_("Promote to Time Origin"), sigc::mem_fun(*this, &Editor::marker_menu_set_origin)));
974         if (!loop_or_punch_or_session) {
975                 items.push_back (MenuElem (_("Hide Range"), sigc::mem_fun(*this, &Editor::marker_menu_hide)));
976                 items.push_back (MenuElem (_("Rename Range..."), sigc::mem_fun(*this, &Editor::marker_menu_rename)));
977         }
978
979         if (!session) {
980                 items.push_back (MenuElem (_("Remove Range"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
981         }
982
983         if (!loop_or_punch_or_session || !session) {
984                 items.push_back (SeparatorElem());
985         }
986
987         items.push_back (MenuElem (_("Separate Regions in Range"), sigc::mem_fun(*this, &Editor::marker_menu_separate_regions_using_location)));
988         items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::marker_menu_select_all_selectables_using_range)));
989         items.push_back (MenuElem (_("Select Range"), sigc::mem_fun(*this, &Editor::marker_menu_select_using_range)));
990 }
991
992 void
993 Editor::build_tempo_marker_menu (TempoMarker* loc, bool can_remove)
994 {
995         using namespace Menu_Helpers;
996
997         delete tempo_marker_menu;
998         tempo_marker_menu = new Menu;
999
1000         MenuList& items = tempo_marker_menu->items();
1001         tempo_marker_menu->set_name ("ArdourContextMenu");
1002
1003         if (!loc->tempo().initial()) {
1004                 if (loc->tempo().clamped()) {
1005                         items.push_back (MenuElem (_("Don't Continue"), sigc::mem_fun(*this, &Editor::toggle_tempo_clamped)));
1006                 } else {
1007                         items.push_back (MenuElem (_("Continue"), sigc::mem_fun(*this, &Editor::toggle_tempo_clamped)));
1008                 }
1009         }
1010
1011         if (loc->tempo().type() == TempoSection::Ramp) {
1012                 items.push_back (MenuElem (_("Set Constant"), sigc::mem_fun(*this, &Editor::toggle_tempo_type)));
1013         }
1014
1015         TempoSection* next_ts = _session->tempo_map().next_tempo_section (&loc->tempo());
1016         if (next_ts && next_ts->note_types_per_minute() != loc->tempo().end_note_types_per_minute()) {
1017                 items.push_back (MenuElem (_("Ramp to Next"), sigc::mem_fun(*this, &Editor::ramp_to_next_tempo)));
1018         }
1019
1020         if (loc->tempo().position_lock_style() == AudioTime && can_remove) {
1021                 items.push_back (SeparatorElem());
1022                 items.push_back (MenuElem (_("Lock to Music"), sigc::mem_fun(*this, &Editor::toggle_marker_lock_style)));
1023         } else if (can_remove) {
1024                 items.push_back (SeparatorElem());
1025                 items.push_back (MenuElem (_("Lock to Audio"), sigc::mem_fun(*this, &Editor::toggle_marker_lock_style)));
1026         }
1027
1028         items.push_back (SeparatorElem());
1029
1030         items.push_back (MenuElem (_("Edit..."), sigc::mem_fun(*this, &Editor::marker_menu_edit)));
1031         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
1032         items.back().set_sensitive (can_remove);
1033 }
1034
1035 void
1036 Editor::build_meter_marker_menu (MeterMarker* loc, bool can_remove)
1037 {
1038         using namespace Menu_Helpers;
1039
1040         delete meter_marker_menu;
1041         meter_marker_menu = new Menu;
1042
1043         MenuList& items = meter_marker_menu->items();
1044         meter_marker_menu->set_name ("ArdourContextMenu");
1045
1046         if (loc->meter().position_lock_style() == AudioTime && can_remove) {
1047                 items.push_back (MenuElem (_("Lock to Music"), sigc::mem_fun(*this, &Editor::toggle_marker_lock_style)));
1048         } else if (can_remove) {
1049                 items.push_back (MenuElem (_("Lock to Audio"), sigc::mem_fun(*this, &Editor::toggle_marker_lock_style)));
1050         }
1051
1052         items.push_back (MenuElem (_("Edit..."), sigc::mem_fun(*this, &Editor::marker_menu_edit)));
1053         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
1054
1055         items.back().set_sensitive (can_remove);
1056 }
1057
1058 void
1059 Editor::build_new_transport_marker_menu ()
1060 {
1061         using namespace Menu_Helpers;
1062
1063         new_transport_marker_menu = new Menu;
1064
1065         MenuList& items = new_transport_marker_menu->items();
1066         new_transport_marker_menu->set_name ("ArdourContextMenu");
1067
1068         items.push_back (MenuElem (_("Set Loop Range"), sigc::mem_fun(*this, &Editor::new_transport_marker_menu_set_loop)));
1069         items.push_back (MenuElem (_("Set Punch Range"), sigc::mem_fun(*this, &Editor::new_transport_marker_menu_set_punch)));
1070
1071         new_transport_marker_menu->signal_unmap().connect ( sigc::mem_fun(*this, &Editor::new_transport_marker_menu_popdown));
1072 }
1073
1074 void
1075 Editor::marker_menu_hide ()
1076 {
1077         ArdourMarker* marker;
1078
1079         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1080                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1081                 abort(); /*NOTREACHED*/
1082         }
1083
1084         Location* l;
1085         bool is_start;
1086
1087         if ((l = find_location_from_marker (marker, is_start)) != 0) {
1088                 l->set_hidden (true, this);
1089         }
1090 }
1091
1092 void
1093 Editor::marker_menu_set_origin ()
1094 {
1095         ArdourMarker* marker;
1096
1097         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1098                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1099                 abort(); /*NOTREACHED*/
1100         }
1101
1102         Location* l;
1103         bool is_start;
1104
1105         if ((l = find_location_from_marker (marker, is_start)) != 0) {
1106                 _session->locations()->set_clock_origin (l, this);
1107         }
1108 }
1109
1110 void
1111 Editor::marker_menu_select_using_range ()
1112 {
1113         ArdourMarker* marker;
1114
1115         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1116                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1117                 abort(); /*NOTREACHED*/
1118         }
1119
1120         Location* l;
1121         bool is_start;
1122
1123         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
1124                 set_selection_from_range (*l);
1125         }
1126 }
1127
1128 void
1129 Editor::marker_menu_select_all_selectables_using_range ()
1130 {
1131         ArdourMarker* marker;
1132
1133         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1134                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1135                 abort(); /*NOTREACHED*/
1136         }
1137
1138         Location* l;
1139         bool is_start;
1140
1141         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
1142                 select_all_within (l->start(), l->end() - 1, 0,  DBL_MAX, track_views, Selection::Set, false);
1143         }
1144
1145 }
1146
1147 void
1148 Editor::marker_menu_separate_regions_using_location ()
1149 {
1150         ArdourMarker* marker;
1151
1152         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1153                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1154                 abort(); /*NOTREACHED*/
1155         }
1156
1157         Location* l;
1158         bool is_start;
1159
1160         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
1161                 separate_regions_using_location (*l);
1162         }
1163
1164 }
1165
1166 void
1167 Editor::marker_menu_play_from ()
1168 {
1169         ArdourMarker* marker;
1170
1171         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1172                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1173                 abort(); /*NOTREACHED*/
1174         }
1175
1176         Location* l;
1177         bool is_start;
1178
1179         if ((l = find_location_from_marker (marker, is_start)) != 0) {
1180
1181                 if (l->is_mark()) {
1182                         _session->request_locate (l->start(), true);
1183                 }
1184                 else {
1185                         //_session->request_bounded_roll (l->start(), l->end());
1186
1187                         if (is_start) {
1188                                 _session->request_locate (l->start(), true);
1189                         } else {
1190                                 _session->request_locate (l->end(), true);
1191                         }
1192                 }
1193         }
1194 }
1195
1196 void
1197 Editor::marker_menu_set_playhead ()
1198 {
1199         ArdourMarker* marker;
1200
1201         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1202                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1203                 abort(); /*NOTREACHED*/
1204         }
1205
1206         Location* l;
1207         bool is_start;
1208
1209         if ((l = find_location_from_marker (marker, is_start)) != 0) {
1210
1211                 if (l->is_mark()) {
1212                         _session->request_locate (l->start(), false);
1213                 }
1214                 else {
1215                         if (is_start) {
1216                                 _session->request_locate (l->start(), false);
1217                         } else {
1218                                 _session->request_locate (l->end(), false);
1219                         }
1220                 }
1221         }
1222 }
1223
1224 void
1225 Editor::marker_menu_range_to_next ()
1226 {
1227         ArdourMarker* marker;
1228         if (!_session) {
1229                 return;
1230         }
1231
1232         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1233                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1234                 abort(); /*NOTREACHED*/
1235         }
1236
1237         Location* l;
1238         bool is_start;
1239
1240         if ((l = find_location_from_marker (marker, is_start)) == 0) {
1241                 return;
1242         }
1243
1244         samplepos_t start;
1245         samplepos_t end;
1246         _session->locations()->marks_either_side (marker->position(), start, end);
1247
1248         if (end != max_samplepos) {
1249                 string range_name = l->name();
1250                 range_name += "-range";
1251
1252                 Location* newrange = new Location (*_session, marker->position(), end, range_name, Location::IsRangeMarker);
1253                 _session->locations()->add (newrange);
1254         }
1255 }
1256
1257 void
1258 Editor::marker_menu_set_from_playhead ()
1259 {
1260         ArdourMarker* marker;
1261
1262         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1263                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1264                 abort(); /*NOTREACHED*/
1265         }
1266
1267         Location* l;
1268         bool is_start;
1269         const int32_t divisions = get_grid_music_divisions (0);
1270
1271         if ((l = find_location_from_marker (marker, is_start)) != 0) {
1272
1273                 if (l->is_mark()) {
1274                         l->set_start (_session->audible_sample (), false, true, divisions);
1275                 }
1276                 else {
1277                         if (is_start) {
1278                                 l->set_start (_session->audible_sample (), false, true, divisions);
1279                         } else {
1280                                 l->set_end (_session->audible_sample (), false, true, divisions);
1281                         }
1282                 }
1283         }
1284 }
1285
1286 void
1287 Editor::marker_menu_set_from_selection (bool /*force_regions*/)
1288 {
1289         ArdourMarker* marker;
1290
1291         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1292                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1293                 abort(); /*NOTREACHED*/
1294         }
1295
1296         Location* l;
1297         bool is_start;
1298
1299         if ((l = find_location_from_marker (marker, is_start)) != 0) {
1300
1301                 if (l->is_mark()) {
1302
1303                         // nothing for now
1304
1305                 } else {
1306
1307                         if (!selection->time.empty()) {
1308                                 l->set (selection->time.start(), selection->time.end_sample());
1309                         } else if (!selection->regions.empty()) {
1310                                 l->set (selection->regions.start(), selection->regions.end_sample());
1311                         }
1312                 }
1313         }
1314 }
1315
1316
1317 void
1318 Editor::marker_menu_play_range ()
1319 {
1320         ArdourMarker* marker;
1321
1322         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1323                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1324                 abort(); /*NOTREACHED*/
1325         }
1326
1327         Location* l;
1328         bool is_start;
1329
1330         if ((l = find_location_from_marker (marker, is_start)) != 0) {
1331
1332                 if (l->is_mark()) {
1333                         _session->request_locate (l->start(), true);
1334                 }
1335                 else {
1336                         _session->request_bounded_roll (l->start(), l->end());
1337
1338                 }
1339         }
1340 }
1341
1342 void
1343 Editor::marker_menu_loop_range ()
1344 {
1345         ArdourMarker* marker;
1346
1347         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1348                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1349                 abort(); /*NOTREACHED*/
1350         }
1351
1352         Location* l;
1353         bool is_start;
1354
1355         if ((l = find_location_from_marker (marker, is_start)) != 0) {
1356                 if (l != transport_loop_location()) {
1357                         set_loop_range (l->start(), l->end(), _("loop range from marker"));
1358                 }
1359                 _session->request_locate (l->start(), true);
1360                 _session->request_play_loop (true);
1361         }
1362 }
1363
1364 /** Temporal zoom to the range of the marker_menu_item (plus 5% either side) */
1365 void
1366 Editor::marker_menu_zoom_to_range ()
1367 {
1368         ArdourMarker* marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"));
1369         assert (marker);
1370
1371         bool is_start;
1372         Location* l = find_location_from_marker (marker, is_start);
1373         if (l == 0) {
1374                 return;
1375         }
1376
1377         samplecnt_t const extra = l->length() * 0.05;
1378         samplepos_t a = l->start ();
1379         if (a >= extra) {
1380                 a -= extra;
1381         }
1382
1383         samplepos_t b = l->end ();
1384         if (b < (max_samplepos - extra)) {
1385                 b += extra;
1386         }
1387
1388         temporal_zoom_by_sample (a, b);
1389 }
1390
1391 void
1392 Editor::dynamic_cast_marker_object (void* p, MeterMarker** m, TempoMarker** t) const
1393 {
1394         ArdourMarker* marker = reinterpret_cast<ArdourMarker*> (p);
1395         if (!marker) {
1396                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1397                 abort(); /*NOTREACHED*/
1398         }
1399
1400         *m = dynamic_cast<MeterMarker*> (marker);
1401         *t = dynamic_cast<TempoMarker*> (marker);
1402 }
1403
1404 void
1405 Editor::marker_menu_edit ()
1406 {
1407         MeterMarker* mm;
1408         TempoMarker* tm;
1409         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1410
1411         if (mm) {
1412                 edit_meter_section (&mm->meter());
1413         } else if (tm) {
1414                 edit_tempo_section (&tm->tempo());
1415         }
1416 }
1417
1418 void
1419 Editor::marker_menu_remove ()
1420 {
1421         MeterMarker* mm;
1422         TempoMarker* tm;
1423         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1424
1425         if (mm) {
1426                 remove_meter_marker (marker_menu_item);
1427         } else if (tm) {
1428                 remove_tempo_marker (marker_menu_item);
1429         } else {
1430                 remove_marker (*marker_menu_item, (GdkEvent*) 0);
1431         }
1432 }
1433
1434 void
1435 Editor::toggle_marker_lock_style ()
1436 {
1437         MeterMarker* mm;
1438         TempoMarker* tm;
1439         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1440
1441         if (mm) {
1442                 begin_reversible_command (_("change meter lock style"));
1443                 XMLNode &before = _session->tempo_map().get_state();
1444                 MeterSection* msp = &mm->meter();
1445
1446                 const Meter meter (msp->divisions_per_bar(), msp->note_divisor());
1447                 const Timecode::BBT_Time bbt (msp->bbt());
1448                 const PositionLockStyle pls = (msp->position_lock_style() == AudioTime) ? MusicTime : AudioTime;
1449
1450                 _session->tempo_map().replace_meter (*msp, meter, bbt, msp->sample(), pls);
1451
1452                 XMLNode &after = _session->tempo_map().get_state();
1453                 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1454                 commit_reversible_command ();
1455         } else if (tm) {
1456                 TempoSection* tsp = &tm->tempo();
1457
1458                 const double pulse = tsp->pulse();
1459                 const samplepos_t sample = tsp->sample();
1460                 const PositionLockStyle pls = (tsp->position_lock_style() == AudioTime) ? MusicTime : AudioTime;
1461                 const Tempo tempo (tsp->note_types_per_minute(), tsp->note_type(), tsp->end_note_types_per_minute());
1462
1463                 begin_reversible_command (_("change tempo lock style"));
1464                 XMLNode &before = _session->tempo_map().get_state();
1465
1466                 _session->tempo_map().replace_tempo (*tsp, tempo, pulse, sample, pls);
1467
1468                 XMLNode &after = _session->tempo_map().get_state();
1469                 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1470                 commit_reversible_command ();
1471         }
1472 }
1473 /* actally just resets the ts to constant using initial tempo */
1474 void
1475 Editor::toggle_tempo_type ()
1476 {
1477         TempoMarker* tm;
1478         MeterMarker* mm;
1479         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1480
1481         if (tm) {
1482                 TempoSection* tsp = &tm->tempo();
1483
1484                 const Tempo tempo (tsp->note_types_per_minute(), tsp->note_type());
1485                 const double pulse = tsp->pulse();
1486                 const samplepos_t sample = tsp->sample();
1487                 const PositionLockStyle pls = tsp->position_lock_style();
1488
1489                 begin_reversible_command (_("set tempo to constant"));
1490                 XMLNode &before = _session->tempo_map().get_state();
1491
1492                 _session->tempo_map().replace_tempo (*tsp, tempo, pulse, sample, pls);
1493
1494                 XMLNode &after = _session->tempo_map().get_state();
1495                 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1496                 commit_reversible_command ();
1497         }
1498 }
1499 /* clamped locks the previous section end tempo to the start tempo */
1500 void
1501 Editor::toggle_tempo_clamped ()
1502 {
1503         TempoMarker* tm;
1504         MeterMarker* mm;
1505         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1506
1507         if (tm) {
1508                 begin_reversible_command (_("Clamp Tempo"));
1509                 XMLNode &before = _session->tempo_map().get_state();
1510
1511                 TempoSection* tsp = &tm->tempo();
1512                 TempoSection* prev = _session->tempo_map().previous_tempo_section (tsp);
1513
1514                 if (prev) {
1515                         /* set to the end tempo of the previous section */
1516                         Tempo new_tempo (prev->end_note_types_per_minute(), prev->note_type(), tsp->end_note_types_per_minute());
1517                         _session->tempo_map().gui_change_tempo (tsp, new_tempo);
1518                 }
1519
1520                 tsp->set_clamped (!tsp->clamped());
1521
1522                 XMLNode &after = _session->tempo_map().get_state();
1523                 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1524                 commit_reversible_command ();
1525         }
1526 }
1527
1528 void
1529 Editor::ramp_to_next_tempo ()
1530 {
1531         TempoMarker* tm;
1532         MeterMarker* mm;
1533         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1534
1535         if (tm) {
1536                 TempoMap& tmap (_session->tempo_map());
1537                 TempoSection* tsp = &tm->tempo();
1538                 TempoSection* next_ts = tmap.next_tempo_section (&tm->tempo());
1539                 if (next_ts) {
1540                         const Tempo tempo (tsp->note_types_per_minute(), tsp->note_type(), next_ts->note_types_per_minute());
1541                         const double pulse = tsp->pulse();
1542                         const samplepos_t sample = tsp->sample();
1543                         const PositionLockStyle pls = tsp->position_lock_style();
1544
1545                         begin_reversible_command (_("ramp to next tempo"));
1546                         XMLNode &before = _session->tempo_map().get_state();
1547
1548                         tmap.replace_tempo (*tsp, tempo, pulse, sample, pls);
1549
1550                         XMLNode &after = _session->tempo_map().get_state();
1551                         _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1552                         commit_reversible_command ();
1553                 }
1554         }
1555 }
1556
1557 void
1558 Editor::toggle_marker_menu_lock ()
1559 {
1560         ArdourMarker* marker;
1561
1562         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1563                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1564                 abort(); /*NOTREACHED*/
1565         }
1566
1567         Location* loc;
1568         bool ignored;
1569
1570         loc = find_location_from_marker (marker, ignored);
1571
1572         if (!loc) {
1573                 return;
1574         }
1575
1576         if (loc->locked()) {
1577                 loc->unlock ();
1578         } else {
1579                 loc->lock ();
1580         }
1581 }
1582
1583 void
1584 Editor::marker_menu_rename ()
1585 {
1586         ArdourMarker* marker;
1587
1588         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1589                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1590                 abort(); /*NOTREACHED*/
1591         }
1592
1593
1594         rename_marker (marker);
1595 }
1596
1597 void
1598 Editor::rename_marker(ArdourMarker *marker)
1599 {
1600         Location* loc;
1601         bool is_start;
1602
1603         loc = find_location_from_marker (marker, is_start);
1604
1605         if (!loc) {
1606                 return;
1607         }
1608
1609         if (loc == transport_loop_location() || loc == transport_punch_location() || loc->is_session_range()) {
1610                 return;
1611         }
1612
1613         ArdourWidgets::Prompter dialog (true);
1614         string txt;
1615
1616         dialog.set_prompt (_("New Name:"));
1617
1618         if (loc->is_mark()) {
1619                 dialog.set_title (_("Rename Mark"));
1620         } else {
1621                 dialog.set_title (_("Rename Range"));
1622         }
1623
1624         dialog.set_name ("MarkRenameWindow");
1625         dialog.set_size_request (250, -1);
1626         dialog.set_position (Gtk::WIN_POS_MOUSE);
1627
1628         dialog.add_button (_("Rename"), RESPONSE_ACCEPT);
1629         dialog.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1630         dialog.set_initial_text (loc->name());
1631
1632         dialog.show ();
1633
1634         switch (dialog.run ()) {
1635         case RESPONSE_ACCEPT:
1636                 break;
1637         default:
1638                 return;
1639         }
1640
1641         begin_reversible_command ( _("rename marker") );
1642         XMLNode &before = _session->locations()->get_state();
1643
1644         dialog.get_result(txt);
1645         loc->set_name (txt);
1646         _session->set_dirty ();
1647
1648         XMLNode &after = _session->locations()->get_state();
1649         _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1650         commit_reversible_command ();
1651 }
1652
1653 void
1654 Editor::new_transport_marker_menu_popdown ()
1655 {
1656         // hide rects
1657         transport_bar_drag_rect->hide();
1658
1659         _drags->abort ();
1660 }
1661
1662 void
1663 Editor::new_transport_marker_menu_set_loop ()
1664 {
1665         set_loop_range (temp_location->start(), temp_location->end(), _("set loop range"));
1666 }
1667
1668 void
1669 Editor::new_transport_marker_menu_set_punch ()
1670 {
1671         set_punch_range (temp_location->start(), temp_location->end(), _("set punch range"));
1672 }
1673
1674 void
1675 Editor::update_loop_range_view ()
1676 {
1677         if (_session == 0) {
1678                 return;
1679         }
1680
1681         Location* tll;
1682
1683         if (_session->get_play_loop() && ((tll = transport_loop_location()) != 0)) {
1684
1685                 double x1 = sample_to_pixel (tll->start());
1686                 double x2 = sample_to_pixel (tll->end());
1687
1688                 transport_loop_range_rect->set_x0 (x1);
1689                 transport_loop_range_rect->set_x1 (x2);
1690
1691                 transport_loop_range_rect->show();
1692
1693         } else {
1694                 transport_loop_range_rect->hide();
1695         }
1696 }
1697
1698 void
1699 Editor::update_punch_range_view ()
1700 {
1701         if (_session == 0) {
1702                 return;
1703         }
1704
1705         Location* tpl;
1706
1707         if ((_session->config.get_punch_in() || _session->config.get_punch_out()) && ((tpl = transport_punch_location()) != 0)) {
1708
1709                 double pixel_start;
1710                 double pixel_end;
1711
1712                 if (_session->config.get_punch_in()) {
1713                         pixel_start = sample_to_pixel (tpl->start());
1714                 } else {
1715                         pixel_start = 0;
1716                 }
1717                 if (_session->config.get_punch_out()) {
1718                         pixel_end = sample_to_pixel (tpl->end());
1719                 } else {
1720                         pixel_end = sample_to_pixel (max_samplepos);
1721                 }
1722
1723                 transport_punch_range_rect->set_x0 (pixel_start);
1724                 transport_punch_range_rect->set_x1 (pixel_end);
1725                 transport_punch_range_rect->show();
1726
1727         } else {
1728
1729                 transport_punch_range_rect->hide();
1730         }
1731 }
1732
1733 void
1734 Editor::marker_selection_changed ()
1735 {
1736         if (_session && _session->deletion_in_progress()) {
1737                 return;
1738         }
1739
1740         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1741                 i->second->set_selected (false);
1742         }
1743
1744         for (MarkerSelection::iterator x = selection->markers.begin(); x != selection->markers.end(); ++x) {
1745                 (*x)->set_selected (true);
1746         }
1747 }
1748
1749 struct SortLocationsByPosition {
1750         bool operator() (Location* a, Location* b) {
1751                 return a->start() < b->start();
1752         }
1753 };
1754
1755 void
1756 Editor::goto_nth_marker (int n)
1757 {
1758         if (!_session) {
1759                 return;
1760         }
1761         const Locations::LocationList& l (_session->locations()->list());
1762         Locations::LocationList ordered;
1763         ordered = l;
1764
1765         SortLocationsByPosition cmp;
1766         ordered.sort (cmp);
1767
1768         for (Locations::LocationList::iterator i = ordered.begin(); n >= 0 && i != ordered.end(); ++i) {
1769                 if ((*i)->is_mark() && !(*i)->is_hidden() && !(*i)->is_session_range()) {
1770                         if (n == 0) {
1771                                 _session->request_locate ((*i)->start(), _session->transport_rolling());
1772                                 break;
1773                         }
1774                         --n;
1775                 }
1776         }
1777 }
1778
1779 void
1780 Editor::toggle_marker_menu_glue ()
1781 {
1782         ArdourMarker* marker;
1783
1784         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1785                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1786                 abort(); /*NOTREACHED*/
1787         }
1788
1789         Location* loc;
1790         bool ignored;
1791
1792         loc = find_location_from_marker (marker, ignored);
1793
1794         if (!loc) {
1795                 return;
1796         }
1797
1798         begin_reversible_command (_("change marker lock style"));
1799         XMLNode &before = _session->locations()->get_state();
1800
1801         if (loc->position_lock_style() == MusicTime) {
1802                 loc->set_position_lock_style (AudioTime);
1803         } else {
1804                 loc->set_position_lock_style (MusicTime);
1805         }
1806
1807         XMLNode &after = _session->locations()->get_state();
1808         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1809         commit_reversible_command ();
1810 }
1811
1812 void
1813 Editor::toggle_marker_lines ()
1814 {
1815         _show_marker_lines = !_show_marker_lines;
1816
1817         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1818                 i->second->set_show_lines (_show_marker_lines);
1819         }
1820 }
1821
1822 void
1823 Editor::remove_sorted_marker (ArdourMarker* m)
1824 {
1825         for (std::map<ArdourCanvas::Container *, std::list<ArdourMarker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
1826                 i->second.remove (m);
1827         }
1828 }
1829
1830 ArdourMarker *
1831 Editor::find_marker_from_location_id (PBD::ID const & id, bool is_start) const
1832 {
1833         for (LocationMarkerMap::const_iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1834                 if (i->first->id() == id) {
1835                         return is_start ? i->second->start : i->second->end;
1836                 }
1837         }
1838
1839         return 0;
1840 }