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