Merge with 2.0-ongoing R2885.
[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_shown[ruler_time_cd_marker]) {
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_shown[ruler_time_cd_marker]) {
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_shown[ruler_time_cd_marker] &&  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_shown[ruler_time_cd_marker]) 
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 }
333
334 void
335 Editor::refresh_location_display ()
336 {
337         ENSURE_GUI_THREAD(mem_fun(*this, &Editor::refresh_location_display));
338         
339         if (session) {
340                 session->locations()->apply (*this, &Editor::refresh_location_display_internal);
341         }
342 }
343
344 void
345 Editor::refresh_location_display_s (Change ignored)
346 {
347         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::refresh_location_display_s), ignored));
348
349         if (session) {
350                 session->locations()->apply (*this, &Editor::refresh_location_display_internal);
351         }
352 }
353
354 void
355 Editor::LocationMarkers::hide() 
356 {
357         start->hide ();
358         if (end) { end->hide(); }
359 }
360
361 void
362 Editor::LocationMarkers::show() 
363 {
364         start->show ();
365         if (end) { end->show(); }
366 }
367
368 void
369 Editor::LocationMarkers::set_name (const string& str) 
370 {
371         start->set_name (str);
372         if (end) { end->set_name (str); }
373 }
374
375 void
376 Editor::LocationMarkers::set_position (nframes_t startf, 
377                                        nframes_t endf) 
378 {
379         start->set_position (startf);
380         if (end) { end->set_position (endf); }
381 }
382
383 void
384 Editor::LocationMarkers::set_color_rgba (uint32_t rgba) 
385 {
386         start->set_color_rgba (rgba);
387         if (end) { end->set_color_rgba (rgba); }
388 }
389
390 void
391 Editor::mouse_add_new_marker (nframes_t where, bool is_cd)
392 {
393         string markername;
394         int flags = (is_cd ? Location::IsCDMarker|Location::IsMark : Location::IsMark);
395         
396         if (session) {
397                 session->locations()->next_available_name(markername,"mark");
398                 Location *location = new Location (where, where, markername, (Location::Flags) flags);
399                 session->begin_reversible_command (_("add marker"));
400                 XMLNode &before = session->locations()->get_state();
401                 session->locations()->add (location, true);
402                 XMLNode &after = session->locations()->get_state();
403                 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
404                 session->commit_reversible_command ();
405
406                 /* find the marker we just added */
407
408                 LocationMarkers *lam = find_location_markers (location);
409                 if (lam) {
410                         /* make it the selected marker */
411                         selection->set (lam->start);
412                 }
413         }
414 }
415
416 void
417 Editor::remove_marker (ArdourCanvas::Item& item, GdkEvent* event)
418 {
419         Marker* marker;
420         bool is_start;
421
422         if ((marker = static_cast<Marker*> (item.get_data ("marker"))) == 0) {
423                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
424                 /*NOTREACHED*/
425         }
426
427         if (entered_marker == marker) {
428                 entered_marker = NULL;
429         }
430
431         Location* loc = find_location_from_marker (marker, is_start);
432
433         if (session && loc) {
434                 Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
435         }
436 }
437
438 gint
439 Editor::really_remove_marker (Location* loc)
440 {
441         session->begin_reversible_command (_("remove marker"));
442         XMLNode &before = session->locations()->get_state();
443         session->locations()->remove (loc);
444         XMLNode &after = session->locations()->get_state();
445         session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
446         session->commit_reversible_command ();
447         return FALSE;
448 }
449
450 void
451 Editor::location_gone (Location *location)
452 {
453         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::location_gone), location));
454         
455         LocationMarkerMap::iterator i;
456
457         if (location == transport_loop_location()) {
458                 update_loop_range_view (true);
459         }
460
461         if (location == transport_punch_location()) {
462                 update_punch_range_view (true);
463         }
464         
465         for (i = location_markers.begin(); i != location_markers.end(); ++i) {
466                 if ((*i).first == location) {
467                         delete (*i).second;
468                         location_markers.erase (i);
469                         break;
470                 }
471         }
472 }
473
474 void
475 Editor::tm_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
476 {
477         if (tm_marker_menu == 0) {
478                 build_tm_marker_menu ();
479         }
480
481         marker_menu_item = item;
482         tm_marker_menu->popup (1, ev->time);
483
484 }
485
486 void
487 Editor::marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
488 {
489         Marker * marker;
490         if ((marker = reinterpret_cast<Marker *> (item->get_data("marker"))) == 0) {
491                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
492                 /*NOTREACHED*/
493         }
494         
495         bool is_start;
496         Location * loc = find_location_from_marker (marker, is_start);
497         if (loc == transport_loop_location() || loc == transport_punch_location()) {
498                 if (transport_marker_menu == 0) {
499                         build_range_marker_menu (true);
500                 }
501                 marker_menu_item = item;
502                 transport_marker_menu->popup (1, ev->time);
503         } else {
504
505                 if (loc->is_mark()) {
506                         bool start_or_end = loc->is_start() || loc->is_end();
507                         Menu *markerMenu;
508                         if (start_or_end) {
509                                 if (start_end_marker_menu == 0)
510                                         build_marker_menu (true);
511                                 markerMenu = start_end_marker_menu;
512                         } else {
513                                 if (marker_menu == 0)
514                                         build_marker_menu (false);
515                                 markerMenu = marker_menu;
516                         }
517
518
519                 // GTK2FIX use action group sensitivity
520 #ifdef GTK2FIX
521                 if (children.size() >= 3) {
522                         MenuItem * loopitem = &children[2];
523                         if (loopitem) {
524                                 if (loc->is_mark()) {
525                                         loopitem->set_sensitive(false);
526                                 }
527                                 else {
528                                         loopitem->set_sensitive(true);
529                                 }
530                         }
531                 }
532 #endif          
533                 marker_menu_item = item;
534                 markerMenu->popup (1, ev->time);
535                 }
536
537                 if (loc->is_range_marker()) {
538                        if (range_marker_menu == 0){
539                               build_range_marker_menu (false);
540                        }
541                        marker_menu_item = item;
542                        range_marker_menu->popup (1, ev->time);
543                 }
544         }
545 }
546
547 void
548 Editor::new_transport_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
549 {
550         if (new_transport_marker_menu == 0) {
551                 build_new_transport_marker_menu ();
552         }
553
554         new_transport_marker_menu->popup (1, ev->time);
555
556 }
557
558 void
559 Editor::transport_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
560 {
561         if (transport_marker_menu == 0) {
562                 build_range_marker_menu (true);
563         }
564
565         transport_marker_menu->popup (1, ev->time);
566 }
567
568 void
569 Editor::build_marker_menu (bool start_or_end)
570 {
571         using namespace Menu_Helpers;
572
573         Menu *markerMenu = new Menu;
574         if (start_or_end) {
575                 start_end_marker_menu = markerMenu;
576         } else {
577                 marker_menu = markerMenu;
578         }
579         MenuList& items = markerMenu->items();
580         markerMenu->set_name ("ArdourContextMenu");
581
582         items.push_back (MenuElem (_("Locate to here"), mem_fun(*this, &Editor::marker_menu_set_playhead)));
583         items.push_back (MenuElem (_("Play from here"), mem_fun(*this, &Editor::marker_menu_play_from)));
584         items.push_back (MenuElem (_("Move Mark to Playhead"), mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
585
586         items.push_back (SeparatorElem());
587
588         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &Editor::marker_menu_hide)));
589         if (start_or_end) return;
590         items.push_back (MenuElem (_("Rename"), mem_fun(*this, &Editor::marker_menu_rename)));
591         items.push_back (MenuElem (_("Lock"), bind (mem_fun(*this, &Editor::marker_menu_lock), true)));
592         items.push_back (MenuElem (_("Unlock"), bind (mem_fun(*this, &Editor::marker_menu_lock), false)));
593
594         items.push_back (SeparatorElem());
595
596         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::marker_menu_remove)));
597 }
598
599 void
600 Editor::build_range_marker_menu (bool loop_or_punch)
601 {
602         using namespace Menu_Helpers;
603
604         Menu *markerMenu = new Menu;
605         if (loop_or_punch) {
606                 transport_marker_menu = markerMenu;
607         } else {
608                 range_marker_menu = markerMenu;
609         }
610         MenuList& items = markerMenu->items();
611         markerMenu->set_name ("ArdourContextMenu");
612
613         items.push_back (MenuElem (_("Play Range"), mem_fun(*this, &Editor::marker_menu_play_range)));
614         items.push_back (MenuElem (_("Locate to Range Mark"), mem_fun(*this, &Editor::marker_menu_set_playhead)));
615         items.push_back (MenuElem (_("Play from Range Mark"), mem_fun(*this, &Editor::marker_menu_play_from)));
616         if (! loop_or_punch) {
617                 items.push_back (MenuElem (_("Loop Range"), mem_fun(*this, &Editor::marker_menu_loop_range)));
618         }
619         items.push_back (MenuElem (_("Set Range Mark from Playhead"), mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
620         if (!Profile->get_sae()) {
621                 items.push_back (MenuElem (_("Set Range from Range Selection"), mem_fun(*this, &Editor::marker_menu_set_from_selection)));
622         }
623
624         items.push_back (SeparatorElem());
625
626         if (! loop_or_punch) {
627                 items.push_back (MenuElem (_("Hide Range"), mem_fun(*this, &Editor::marker_menu_hide)));
628                 items.push_back (MenuElem (_("Rename Range"), mem_fun(*this, &Editor::marker_menu_rename)));
629                 items.push_back (MenuElem (_("Remove Range"), mem_fun(*this, &Editor::marker_menu_remove)));
630         }
631
632         items.push_back (SeparatorElem());
633
634         items.push_back (MenuElem (_("Separate Regions in Range"), mem_fun(*this, &Editor::marker_menu_separate_regions_using_location)));
635         items.push_back (MenuElem (_("Select All in Range"), mem_fun(*this, &Editor::marker_menu_select_all_selectables_using_range)));
636         if (!Profile->get_sae()) {
637                 items.push_back (MenuElem (_("Select Range"), mem_fun(*this, &Editor::marker_menu_select_using_range)));
638         }
639 }
640
641 void
642 Editor::build_tm_marker_menu ()
643 {
644         using namespace Menu_Helpers;
645
646         tm_marker_menu = new Menu;
647         MenuList& items = tm_marker_menu->items();
648         tm_marker_menu->set_name ("ArdourContextMenu");
649
650         items.push_back (MenuElem (_("Edit"), mem_fun(*this, &Editor::marker_menu_edit)));
651         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::marker_menu_remove)));
652 }
653
654 void
655 Editor::build_new_transport_marker_menu ()
656 {
657         using namespace Menu_Helpers;
658
659         new_transport_marker_menu = new Menu;
660         MenuList& items = new_transport_marker_menu->items();
661         new_transport_marker_menu->set_name ("ArdourContextMenu");
662
663         items.push_back (MenuElem (_("Set Loop Range"), mem_fun(*this, &Editor::new_transport_marker_menu_set_loop)));
664         items.push_back (MenuElem (_("Set Punch Range"), mem_fun(*this, &Editor::new_transport_marker_menu_set_punch)));
665
666         new_transport_marker_menu->signal_unmap_event().connect ( mem_fun(*this, &Editor::new_transport_marker_menu_popdown)); 
667 }
668
669 void
670 Editor::marker_menu_hide ()
671 {
672         Marker* marker;
673
674         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
675                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
676                 /*NOTREACHED*/
677         }
678
679         Location* l;
680         bool is_start;
681         
682         if ((l = find_location_from_marker (marker, is_start)) != 0) {
683                 l->set_hidden (true, this);
684         }
685 }
686
687 void
688 Editor::marker_menu_select_using_range ()
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) && (l->end() > l->start())) {
701                 set_selection_from_range (*l);
702         }
703 }
704
705 void
706 Editor::marker_menu_select_all_selectables_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                 select_all_within (l->start(), l->end() - 1, 0,  DBL_MAX, track_views, Selection::Set);
720         }
721           
722 }
723
724 void
725 Editor::marker_menu_separate_regions_using_location ()
726 {
727         Marker* marker;
728
729         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
730                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
731                 /*NOTREACHED*/
732         }
733
734         Location* l;
735         bool is_start;
736
737         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
738                 separate_regions_using_location (*l);
739         }
740           
741 }
742
743 void
744 Editor::marker_menu_play_from ()
745 {
746         Marker* marker;
747
748         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
749                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
750                 /*NOTREACHED*/
751         }
752
753         Location* l;
754         bool is_start;
755         
756         if ((l = find_location_from_marker (marker, is_start)) != 0) {
757
758                 if (l->is_mark()) {
759                         session->request_locate (l->start(), true);
760                 }
761                 else {
762                         //session->request_bounded_roll (l->start(), l->end());
763                         
764                         if (is_start) {
765                                 session->request_locate (l->start(), true);
766                         } else {
767                                 session->request_locate (l->end(), true);
768                         }
769                 }
770         }
771 }
772
773 void
774 Editor::marker_menu_set_playhead ()
775 {
776         Marker* marker;
777
778         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
779                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
780                 /*NOTREACHED*/
781         }
782
783         Location* l;
784         bool is_start;
785         
786         if ((l = find_location_from_marker (marker, is_start)) != 0) {
787
788                 if (l->is_mark()) {
789                         session->request_locate (l->start(), false);
790                 }
791                 else {
792                         if (is_start) {
793                                 session->request_locate (l->start(), false);
794                         } else {
795                                 session->request_locate (l->end(), false);
796                         }
797                 }
798         }
799 }
800
801 void
802 Editor::marker_menu_set_from_playhead ()
803 {
804         Marker* marker;
805
806         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
807                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
808                 /*NOTREACHED*/
809         }
810
811         Location* l;
812         bool is_start;
813         
814         if ((l = find_location_from_marker (marker, is_start)) != 0) {
815
816                 if (l->is_mark()) {
817                         l->set_start (session->audible_frame ());
818                 }
819                 else {
820                         if (is_start) {
821                                 l->set_start (session->audible_frame ());
822                         } else {
823                                 l->set_end (session->audible_frame ());
824                         }
825                 }
826         }
827 }
828
829 void
830 Editor::marker_menu_set_from_selection ()
831 {
832         Marker* marker;
833
834         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
835                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
836                 /*NOTREACHED*/
837         }
838
839         Location* l;
840         bool is_start;
841         
842         if ((l = find_location_from_marker (marker, is_start)) != 0) {
843
844                 if (l->is_mark()) {
845                         // nothing for now
846                 }
847                 else {
848
849                         /* if range selection use first to last */
850
851                         if (mouse_mode == Editing::MouseRange) {
852                                 if (!selection->time.empty()) {
853                                         l->set_start (selection->time.start());
854                                         l->set_end (selection->time.end_frame());
855                                 }
856                         }
857                         else {
858                                 if (!selection->regions.empty()) {
859                                         l->set_start (selection->regions.start());
860                                         l->set_end (selection->regions.end_frame());
861                                 }
862                         }
863                 }
864         }
865 }
866
867
868 void
869 Editor::marker_menu_play_range ()
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                         session->request_locate (l->start(), true);
885                 }
886                 else {
887                         session->request_bounded_roll (l->start(), l->end());
888                         
889                 }
890         }
891 }
892
893 void
894 Editor::marker_menu_loop_range ()
895 {
896         Marker* marker;
897
898         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
899                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
900                 /*NOTREACHED*/
901         }
902
903         Location* l;
904         bool is_start;
905         
906         if ((l = find_location_from_marker (marker, is_start)) != 0) {
907                 Location* l2;
908                 if ((l2 = transport_loop_location()) != 0) {
909                         l2->set (l->start(), l->end());
910                         
911                         // enable looping, reposition and start rolling
912                         session->request_play_loop(true);
913                         session->request_locate (l2->start(), true);
914                 }
915         }
916 }
917
918 void
919 Editor::marker_menu_edit ()
920 {
921         MeterMarker* mm;
922         TempoMarker* tm;
923         Marker* marker;
924
925         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
926                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
927                 /*NOTREACHED*/
928         }
929
930         if ((mm = dynamic_cast<MeterMarker*> (marker)) != 0) {
931                 edit_meter_section (&mm->meter());
932         } else if ((tm = dynamic_cast<TempoMarker*> (marker)) != 0) {
933                 edit_tempo_section (&tm->tempo());
934         } else {
935                 fatal << X_("programming erorr: unhandled marker type in Editor::marker_menu_edit")
936                       << endmsg;
937                 /*NOTREACHED*/
938         }
939 }
940
941 void
942 Editor::marker_menu_remove ()
943 {
944         MeterMarker* mm;
945         TempoMarker* tm;
946         Marker* marker;
947
948         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
949                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
950                 /*NOTREACHED*/
951         }
952
953         if ((mm = dynamic_cast<MeterMarker*> (marker)) != 0) {
954                 remove_meter_marker (marker_menu_item);
955         } else if ((tm = dynamic_cast<TempoMarker*> (marker)) != 0) {
956                 remove_tempo_marker (marker_menu_item);
957         } else {
958                 remove_marker (*marker_menu_item, (GdkEvent*) 0);
959         }
960 }
961
962 void
963 Editor::marker_menu_lock (bool yn)
964 {
965
966         Marker* marker;
967
968         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
969                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
970                 /*NOTREACHED*/
971         }
972
973         Location* loc;
974         bool ignored;
975
976         loc = find_location_from_marker (marker, ignored);
977
978         if (!loc) return;
979
980         if (yn) {
981                 loc->lock();
982         } else {
983                 loc->unlock ();
984         }
985 }
986
987 void
988 Editor::marker_menu_rename ()
989 {
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         Location* loc;
998         bool is_start;
999
1000         loc = find_location_from_marker (marker, is_start);
1001
1002         if (!loc) return;
1003         
1004         ArdourPrompter dialog (true);
1005         string txt;
1006
1007         dialog.set_prompt (_("New Name:"));
1008
1009         WindowTitle title(Glib::get_application_name());
1010         if (loc->is_mark()) {
1011                 title += _("Rename Mark");
1012         } else {
1013                 title += _("Rename Range");
1014         }
1015
1016         dialog.set_title(title.get_string());
1017
1018         dialog.set_name ("MarkRenameWindow");
1019         dialog.set_size_request (250, -1);
1020         dialog.set_position (Gtk::WIN_POS_MOUSE);
1021
1022         dialog.add_button (_("Rename"), RESPONSE_ACCEPT);
1023         dialog.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1024         dialog.set_initial_text (loc->name());
1025
1026         dialog.show ();
1027
1028         switch (dialog.run ()) {
1029         case RESPONSE_ACCEPT:
1030                 break;
1031         default:
1032                 return;
1033         }
1034
1035         begin_reversible_command ( _("rename marker") );
1036         XMLNode &before = session->locations()->get_state();
1037
1038         dialog.get_result(txt);
1039         loc->set_name (txt);
1040         
1041         XMLNode &after = session->locations()->get_state();
1042         session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
1043         commit_reversible_command ();
1044 }
1045
1046 gint
1047 Editor::new_transport_marker_menu_popdown (GdkEventAny *ev)
1048 {
1049         // hide rects
1050         transport_bar_drag_rect->hide();
1051         range_marker_drag_rect->hide();
1052
1053         return FALSE;
1054 }
1055
1056 void
1057 Editor::new_transport_marker_menu_set_loop ()
1058 {
1059         set_loop_range (temp_location->start(), temp_location->end(), _("set loop range"));
1060 }
1061
1062 void
1063 Editor::new_transport_marker_menu_set_punch ()
1064 {
1065         set_punch_range (temp_location->start(), temp_location->end(), _("set punch range"));
1066 }
1067
1068 void
1069 Editor::update_loop_range_view (bool visibility)
1070 {
1071         if (session == 0) {
1072                 return;
1073         }
1074
1075         Location* tll;
1076
1077         if (session->get_play_loop() && ((tll = transport_loop_location()) != 0)) {
1078
1079                 double x1 = frame_to_pixel (tll->start());
1080                 double x2 = frame_to_pixel (tll->end());
1081                 
1082                 transport_loop_range_rect->property_x1() = x1;
1083                 transport_loop_range_rect->property_x2() = x2;
1084                 
1085                 if (visibility) {
1086                         transport_loop_range_rect->show();
1087                 }
1088
1089         } else if (visibility) {
1090                 transport_loop_range_rect->hide();
1091         }
1092 }
1093
1094 void
1095 Editor::update_punch_range_view (bool visibility)
1096 {
1097         if (session == 0) {
1098                 return;
1099         }
1100
1101         Location* tpl;
1102
1103         if ((Config->get_punch_in() || Config->get_punch_out()) && ((tpl = transport_punch_location()) != 0)) {
1104
1105                 double x1 = frame_to_pixel (tpl->start());
1106                 double x2 = frame_to_pixel (tpl->end());
1107                 
1108                 guint track_canvas_width,track_canvas_height;
1109                 track_canvas.get_size(track_canvas_width,track_canvas_height);
1110                 
1111                 transport_punch_range_rect->property_x1() = x1;
1112                 transport_punch_range_rect->property_x2() = x2;
1113                 
1114                 transport_punch_range_rect->property_x1() = (Config->get_punch_in() ? x1 : 0);
1115                 transport_punch_range_rect->property_x2() = (Config->get_punch_out() ? x2 : track_canvas_width);
1116                 
1117                 if (visibility) {
1118                         transport_punch_range_rect->show();
1119                 }
1120         }
1121         else if (visibility) {
1122                 transport_punch_range_rect->hide();
1123         }
1124
1125 //      if (session->get_punch_in()) {
1126 //              double x = frame_to_pixel (transport_punch_location->start());
1127 //              gnome_canvas_item_set (transport_punchin_line, "x1", x, "x2", x, NULL);
1128                 
1129 //              if (visibility) {
1130 //                      gnome_canvas_item_show (transport_punchin_line);
1131 //              }
1132 //      }
1133 //      else if (visibility) {
1134 //              gnome_canvas_item_hide (transport_punchin_line);
1135 //      }
1136         
1137 //      if (session->get_punch_out()) {
1138 //              double x = frame_to_pixel (transport_punch_location->end());
1139                 
1140 //              gnome_canvas_item_set (transport_punchout_line, "x1", x, "x2", x, NULL);
1141                 
1142 //              if (visibility) {
1143 //                      gnome_canvas_item_show (transport_punchout_line);
1144 //              }
1145 //      }
1146 //      else if (visibility) {
1147 //              gnome_canvas_item_hide (transport_punchout_line);
1148 //      }
1149 }
1150
1151 void
1152 Editor::marker_selection_changed ()
1153 {
1154         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1155                 LocationMarkers* lam = i->second;
1156
1157                 if (lam->start) {
1158                         lam->start->hide_line();
1159                 } 
1160
1161                 if (lam->end) {
1162                         lam->end->hide_line();
1163                 }
1164         }
1165
1166         edit_point_clock_connection_a.disconnect();
1167         edit_point_clock_connection_b.disconnect();
1168
1169         if (selection->markers.empty()) {
1170                 edit_point_clock.set (0);
1171                 return;
1172         }
1173
1174         for (MarkerSelection::iterator x = selection->markers.begin(); x != selection->markers.end(); ++x) {
1175                 (*x)->add_line (cursor_group, vertical_adjustment.get_value(), canvas_height);
1176                 (*x)->show_line ();
1177         }
1178
1179         edit_point_clock.set (selection->markers.front()->position());
1180
1181         bool ignored;
1182         Location* loc = find_location_from_marker (selection->markers.front(), ignored);
1183
1184         if (loc) {
1185                 edit_point_clock_connection_a = loc->changed.connect (mem_fun (*this, &Editor::selected_marker_moved));
1186                 edit_point_clock_connection_b = loc->start_changed.connect (mem_fun (*this, &Editor::selected_marker_moved));
1187         }
1188 }
1189
1190 void
1191 Editor::selected_marker_moved (Location* loc)
1192 {
1193         edit_point_clock.set (loc->start());
1194 }
1195
1196 struct SortLocationsByPosition { 
1197     bool operator() (Location* a, Location* b) {
1198             return a->start() < b->start();
1199     }
1200 };
1201
1202 void
1203 Editor::goto_nth_marker (int n)
1204 {
1205         if (!session) {
1206                 return;
1207         }
1208         const Locations::LocationList& l (session->locations()->list());
1209         Locations::LocationList ordered;
1210         ordered = l;
1211
1212         SortLocationsByPosition cmp;
1213         ordered.sort (cmp);
1214         
1215         for (Locations::LocationList::iterator i = ordered.begin(); n >= 0 && i != ordered.end(); ++i) {
1216                 if ((*i)->is_mark() && !(*i)->is_hidden() && !(*i)->is_start()) {
1217                         if (n == 0) {
1218                                 session->request_locate ((*i)->start(), session->transport_rolling());
1219                                 break;
1220                         }
1221                         --n;
1222                 }
1223         }
1224 }