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