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