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