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