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