Rudimentary ghost note when adding MIDI notes to a region, as per #3180.
[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         } else {
508                 return;
509         }
510         
511         delete tempo_or_meter_marker_menu;
512         build_tempo_or_meter_marker_menu (can_remove);
513         tempo_or_meter_marker_menu->popup (1, ev->time);
514 }
515
516 void
517 Editor::marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
518 {
519         Marker * marker;
520         if ((marker = reinterpret_cast<Marker *> (item->get_data("marker"))) == 0) {
521                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
522                 /*NOTREACHED*/
523         }
524
525         bool is_start;
526         Location * loc = find_location_from_marker (marker, is_start);
527         if (loc == transport_loop_location() || loc == transport_punch_location()) {
528                 if (transport_marker_menu == 0) {
529                         build_range_marker_menu (true);
530                 }
531                 marker_menu_item = item;
532                 transport_marker_menu->popup (1, ev->time);
533         } else {
534
535                 if (loc->is_mark()) {
536                         Menu *markerMenu;
537                         if (loc->is_session_range ()) {
538                                 if (session_range_marker_menu == 0) {
539                                         build_marker_menu (true);
540                                 }
541                                 markerMenu = session_range_marker_menu;
542                         } else {
543                                 if (marker_menu == 0)
544                                         build_marker_menu (false);
545                                 markerMenu = marker_menu;
546                         }
547
548
549                 // GTK2FIX use action group sensitivity
550 #ifdef GTK2FIX
551                 if (children.size() >= 3) {
552                         MenuItem * loopitem = &children[2];
553                         if (loopitem) {
554                                 if (loc->is_mark()) {
555                                         loopitem->set_sensitive(false);
556                                 }
557                                 else {
558                                         loopitem->set_sensitive(true);
559                                 }
560                         }
561                 }
562 #endif
563                 marker_menu_item = item;
564                 markerMenu->popup (1, ev->time);
565                 }
566
567                 if (loc->is_range_marker()) {
568                        if (range_marker_menu == 0){
569                               build_range_marker_menu (false);
570                        }
571                        marker_menu_item = item;
572                        range_marker_menu->popup (1, ev->time);
573                 }
574         }
575 }
576
577 void
578 Editor::new_transport_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item*)
579 {
580         if (new_transport_marker_menu == 0) {
581                 build_new_transport_marker_menu ();
582         }
583
584         new_transport_marker_menu->popup (1, ev->time);
585
586 }
587
588 void
589 Editor::transport_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item*)
590 {
591         if (transport_marker_menu == 0) {
592                 build_range_marker_menu (true);
593         }
594
595         transport_marker_menu->popup (1, ev->time);
596 }
597
598 void
599 Editor::build_marker_menu (bool session_range)
600 {
601         using namespace Menu_Helpers;
602
603         Menu *markerMenu = new Menu;
604         if (session_range) {
605                 session_range_marker_menu = markerMenu;
606         } else {
607                 marker_menu = markerMenu;
608         }
609         MenuList& items = markerMenu->items();
610         markerMenu->set_name ("ArdourContextMenu");
611
612         items.push_back (MenuElem (_("Locate to here"), sigc::mem_fun(*this, &Editor::marker_menu_set_playhead)));
613         items.push_back (MenuElem (_("Play from here"), sigc::mem_fun(*this, &Editor::marker_menu_play_from)));
614         items.push_back (MenuElem (_("Move Mark to Playhead"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
615
616         items.push_back (SeparatorElem());
617
618         items.push_back (MenuElem (_("Create range to next marker"), sigc::mem_fun(*this, &Editor::marker_menu_range_to_next)));
619
620         items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &Editor::marker_menu_hide)));
621         if (session_range) {
622                 return;
623         }
624         items.push_back (MenuElem (_("Rename"), sigc::mem_fun(*this, &Editor::marker_menu_rename)));
625         items.push_back (MenuElem (_("Lock"), sigc::bind (sigc::mem_fun(*this, &Editor::marker_menu_lock), true)));
626         items.push_back (MenuElem (_("Unlock"), sigc::bind (sigc::mem_fun(*this, &Editor::marker_menu_lock), false)));
627
628         items.push_back (SeparatorElem());
629
630         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
631 }
632
633 void
634 Editor::build_range_marker_menu (bool loop_or_punch)
635 {
636         using namespace Menu_Helpers;
637
638         Menu *markerMenu = new Menu;
639         if (loop_or_punch) {
640                 transport_marker_menu = markerMenu;
641         } else {
642                 range_marker_menu = markerMenu;
643         }
644         MenuList& items = markerMenu->items();
645         markerMenu->set_name ("ArdourContextMenu");
646
647         items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::marker_menu_play_range)));
648         items.push_back (MenuElem (_("Locate to Range Mark"), sigc::mem_fun(*this, &Editor::marker_menu_set_playhead)));
649         items.push_back (MenuElem (_("Play from Range Mark"), sigc::mem_fun(*this, &Editor::marker_menu_play_from)));
650         if (! loop_or_punch) {
651                 items.push_back (MenuElem (_("Loop Range"), sigc::mem_fun(*this, &Editor::marker_menu_loop_range)));
652         }
653         items.push_back (MenuElem (_("Set Range Mark from Playhead"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
654         if (!Profile->get_sae()) {
655                 items.push_back (MenuElem (_("Set Range from Range Selection"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_selection)));
656         }
657
658         items.push_back (SeparatorElem());
659         items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_range)));
660         items.push_back (SeparatorElem());
661
662         if (!loop_or_punch) {
663                 items.push_back (MenuElem (_("Hide Range"), sigc::mem_fun(*this, &Editor::marker_menu_hide)));
664                 items.push_back (MenuElem (_("Rename Range"), sigc::mem_fun(*this, &Editor::marker_menu_rename)));
665                 items.push_back (MenuElem (_("Remove Range"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
666                 items.push_back (SeparatorElem());
667         }
668
669         items.push_back (MenuElem (_("Separate Regions in Range"), sigc::mem_fun(*this, &Editor::marker_menu_separate_regions_using_location)));
670         items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::marker_menu_select_all_selectables_using_range)));
671         if (!Profile->get_sae()) {
672                 items.push_back (MenuElem (_("Select Range"), sigc::mem_fun(*this, &Editor::marker_menu_select_using_range)));
673         }
674 }
675
676 void
677 Editor::build_tempo_or_meter_marker_menu (bool can_remove)
678 {
679         using namespace Menu_Helpers;
680
681         tempo_or_meter_marker_menu = new Menu;
682         MenuList& items = tempo_or_meter_marker_menu->items();
683         tempo_or_meter_marker_menu->set_name ("ArdourContextMenu");
684
685         items.push_back (MenuElem (_("Edit"), sigc::mem_fun(*this, &Editor::marker_menu_edit)));
686         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
687
688         items.back().set_sensitive (can_remove);
689 }
690
691 void
692 Editor::build_new_transport_marker_menu ()
693 {
694         using namespace Menu_Helpers;
695
696         new_transport_marker_menu = new Menu;
697         MenuList& items = new_transport_marker_menu->items();
698         new_transport_marker_menu->set_name ("ArdourContextMenu");
699
700         items.push_back (MenuElem (_("Set Loop Range"), sigc::mem_fun(*this, &Editor::new_transport_marker_menu_set_loop)));
701         items.push_back (MenuElem (_("Set Punch Range"), sigc::mem_fun(*this, &Editor::new_transport_marker_menu_set_punch)));
702
703         new_transport_marker_menu->signal_unmap().connect ( sigc::mem_fun(*this, &Editor::new_transport_marker_menu_popdown));
704 }
705
706 void
707 Editor::marker_menu_hide ()
708 {
709         Marker* marker;
710
711         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
712                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
713                 /*NOTREACHED*/
714         }
715
716         Location* l;
717         bool is_start;
718
719         if ((l = find_location_from_marker (marker, is_start)) != 0) {
720                 l->set_hidden (true, this);
721         }
722 }
723
724 void
725 Editor::marker_menu_select_using_range ()
726 {
727         Marker* marker;
728
729         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
730                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
731                 /*NOTREACHED*/
732         }
733
734         Location* l;
735         bool is_start;
736
737         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
738                 set_selection_from_range (*l);
739         }
740 }
741
742 void
743 Editor::marker_menu_select_all_selectables_using_range ()
744 {
745         Marker* marker;
746
747         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
748                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
749                 /*NOTREACHED*/
750         }
751
752         Location* l;
753         bool is_start;
754
755         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
756                 select_all_within (l->start(), l->end() - 1, 0,  DBL_MAX, track_views, Selection::Set);
757         }
758
759 }
760
761 void
762 Editor::marker_menu_separate_regions_using_location ()
763 {
764         Marker* marker;
765
766         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
767                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
768                 /*NOTREACHED*/
769         }
770
771         Location* l;
772         bool is_start;
773
774         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
775                 separate_regions_using_location (*l);
776         }
777
778 }
779
780 void
781 Editor::marker_menu_play_from ()
782 {
783         Marker* marker;
784
785         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
786                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
787                 /*NOTREACHED*/
788         }
789
790         Location* l;
791         bool is_start;
792
793         if ((l = find_location_from_marker (marker, is_start)) != 0) {
794
795                 if (l->is_mark()) {
796                         _session->request_locate (l->start(), true);
797                 }
798                 else {
799                         //_session->request_bounded_roll (l->start(), l->end());
800
801                         if (is_start) {
802                                 _session->request_locate (l->start(), true);
803                         } else {
804                                 _session->request_locate (l->end(), true);
805                         }
806                 }
807         }
808 }
809
810 void
811 Editor::marker_menu_set_playhead ()
812 {
813         Marker* marker;
814
815         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
816                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
817                 /*NOTREACHED*/
818         }
819
820         Location* l;
821         bool is_start;
822
823         if ((l = find_location_from_marker (marker, is_start)) != 0) {
824
825                 if (l->is_mark()) {
826                         _session->request_locate (l->start(), false);
827                 }
828                 else {
829                         if (is_start) {
830                                 _session->request_locate (l->start(), false);
831                         } else {
832                                 _session->request_locate (l->end(), false);
833                         }
834                 }
835         }
836 }
837
838 void
839 Editor::marker_menu_range_to_next ()
840 {
841         Marker* marker;
842         if (!_session) {
843                 return;
844         }
845
846         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
847                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
848                 /*NOTREACHED*/
849         }
850
851         Location* l;
852         bool is_start;
853
854         if ((l = find_location_from_marker (marker, is_start)) == 0) {
855                 return;
856         }
857
858         nframes64_t start;
859         nframes64_t end;
860         _session->locations()->marks_either_side (marker->position(), start, end);
861
862         if (end != max_frames) {
863                 string range_name = l->name();
864                 range_name += "-range";
865
866                 Location* newrange = new Location (marker->position(), end, range_name, Location::IsRangeMarker);
867                 _session->locations()->add (newrange);
868         }
869 }
870
871 void
872 Editor::marker_menu_set_from_playhead ()
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                         l->set_start (_session->audible_frame ());
888                 }
889                 else {
890                         if (is_start) {
891                                 l->set_start (_session->audible_frame ());
892                         } else {
893                                 l->set_end (_session->audible_frame ());
894                         }
895                 }
896         }
897 }
898
899 void
900 Editor::marker_menu_set_from_selection ()
901 {
902         Marker* marker;
903
904         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
905                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
906                 /*NOTREACHED*/
907         }
908
909         Location* l;
910         bool is_start;
911
912         if ((l = find_location_from_marker (marker, is_start)) != 0) {
913
914                 if (l->is_mark()) {
915                         // nothing for now
916                 }
917                 else {
918
919                         /* if range selection use first to last */
920
921                         if (mouse_mode == Editing::MouseRange) {
922                                 if (!selection->time.empty()) {
923                                         l->set_start (selection->time.start());
924                                         l->set_end (selection->time.end_frame());
925                                 }
926                         }
927                         else {
928                                 if (!selection->regions.empty()) {
929                                         l->set_start (selection->regions.start());
930                                         l->set_end (selection->regions.end_frame());
931                                 }
932                         }
933                 }
934         }
935 }
936
937
938 void
939 Editor::marker_menu_play_range ()
940 {
941         Marker* marker;
942
943         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
944                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
945                 /*NOTREACHED*/
946         }
947
948         Location* l;
949         bool is_start;
950
951         if ((l = find_location_from_marker (marker, is_start)) != 0) {
952
953                 if (l->is_mark()) {
954                         _session->request_locate (l->start(), true);
955                 }
956                 else {
957                         _session->request_bounded_roll (l->start(), l->end());
958
959                 }
960         }
961 }
962
963 void
964 Editor::marker_menu_loop_range ()
965 {
966         Marker* marker;
967
968         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
969                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
970                 /*NOTREACHED*/
971         }
972
973         Location* l;
974         bool is_start;
975
976         if ((l = find_location_from_marker (marker, is_start)) != 0) {
977                 Location* l2;
978                 if ((l2 = transport_loop_location()) != 0) {
979                         l2->set (l->start(), l->end());
980
981                         // enable looping, reposition and start rolling
982                         _session->request_play_loop(true);
983                         _session->request_locate (l2->start(), true);
984                 }
985         }
986 }
987
988 void
989 Editor::dynamic_cast_marker_object (void* p, MeterMarker** m, TempoMarker** t) const
990 {
991         Marker* marker = reinterpret_cast<Marker*> (p);
992         if (!marker) {
993                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
994                 /*NOTREACHED*/
995         }
996
997         *m = dynamic_cast<MeterMarker*> (marker);
998         *t = dynamic_cast<TempoMarker*> (marker);
999 }
1000
1001 void
1002 Editor::marker_menu_edit ()
1003 {
1004         MeterMarker* mm;
1005         TempoMarker* tm;
1006         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1007
1008         if (mm) {
1009                 edit_meter_section (&mm->meter());
1010         } else if (tm) {
1011                 edit_tempo_section (&tm->tempo());
1012         }
1013 }
1014
1015 void
1016 Editor::marker_menu_remove ()
1017 {
1018         MeterMarker* mm;
1019         TempoMarker* tm;
1020         dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1021
1022         if (mm) {
1023                 remove_meter_marker (marker_menu_item);
1024         } else if (tm) {
1025                 remove_tempo_marker (marker_menu_item);
1026         } else {
1027                 remove_marker (*marker_menu_item, (GdkEvent*) 0);
1028         }
1029 }
1030
1031 void
1032 Editor::marker_menu_lock (bool yn)
1033 {
1034
1035         Marker* marker;
1036
1037         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
1038                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1039                 /*NOTREACHED*/
1040         }
1041
1042         Location* loc;
1043         bool ignored;
1044
1045         loc = find_location_from_marker (marker, ignored);
1046
1047         if (!loc) return;
1048
1049         if (yn) {
1050                 loc->lock();
1051         } else {
1052                 loc->unlock ();
1053         }
1054 }
1055
1056 void
1057 Editor::marker_menu_rename ()
1058 {
1059         Marker* marker;
1060
1061         if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
1062                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1063                 /*NOTREACHED*/
1064         }
1065
1066         Location* loc;
1067         bool is_start;
1068
1069         loc = find_location_from_marker (marker, is_start);
1070
1071         if (!loc) return;
1072
1073         ArdourPrompter dialog (true);
1074         string txt;
1075
1076         dialog.set_prompt (_("New Name:"));
1077
1078         if (loc->is_mark()) {
1079                 dialog.set_title (_("Rename Mark"));
1080         } else {
1081                 dialog.set_title (_("Rename Range"));
1082         }
1083
1084         dialog.set_name ("MarkRenameWindow");
1085         dialog.set_size_request (250, -1);
1086         dialog.set_position (Gtk::WIN_POS_MOUSE);
1087
1088         dialog.add_button (_("Rename"), RESPONSE_ACCEPT);
1089         dialog.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1090         dialog.set_initial_text (loc->name());
1091
1092         dialog.show ();
1093
1094         switch (dialog.run ()) {
1095         case RESPONSE_ACCEPT:
1096                 break;
1097         default:
1098                 return;
1099         }
1100
1101         begin_reversible_command ( _("rename marker") );
1102         XMLNode &before = _session->locations()->get_state();
1103
1104         dialog.get_result(txt);
1105         loc->set_name (txt);
1106
1107         XMLNode &after = _session->locations()->get_state();
1108         _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1109         commit_reversible_command ();
1110 }
1111
1112 void
1113 Editor::new_transport_marker_menu_popdown ()
1114 {
1115         // hide rects
1116         transport_bar_drag_rect->hide();
1117
1118         _drags->abort ();
1119 }
1120
1121 void
1122 Editor::new_transport_marker_menu_set_loop ()
1123 {
1124         set_loop_range (temp_location->start(), temp_location->end(), _("set loop range"));
1125 }
1126
1127 void
1128 Editor::new_transport_marker_menu_set_punch ()
1129 {
1130         set_punch_range (temp_location->start(), temp_location->end(), _("set punch range"));
1131 }
1132
1133 void
1134 Editor::update_loop_range_view (bool visibility)
1135 {
1136         if (_session == 0) {
1137                 return;
1138         }
1139
1140         Location* tll;
1141
1142         if (_session->get_play_loop() && ((tll = transport_loop_location()) != 0)) {
1143
1144                 double x1 = frame_to_pixel (tll->start());
1145                 double x2 = frame_to_pixel (tll->end());
1146
1147                 transport_loop_range_rect->property_x1() = x1;
1148                 transport_loop_range_rect->property_x2() = x2;
1149
1150                 if (visibility) {
1151                         transport_loop_range_rect->show();
1152                 }
1153
1154         } else if (visibility) {
1155                 transport_loop_range_rect->hide();
1156         }
1157 }
1158
1159 void
1160 Editor::update_punch_range_view (bool visibility)
1161 {
1162         if (_session == 0) {
1163                 return;
1164         }
1165
1166         Location* tpl;
1167
1168         if ((_session->config.get_punch_in() || _session->config.get_punch_out()) && ((tpl = transport_punch_location()) != 0)) {
1169                 guint track_canvas_width,track_canvas_height;
1170                 track_canvas->get_size(track_canvas_width,track_canvas_height);
1171                 if (_session->config.get_punch_in()) {
1172                         transport_punch_range_rect->property_x1() = frame_to_pixel (tpl->start());
1173                         transport_punch_range_rect->property_x2() = (_session->config.get_punch_out() ? frame_to_pixel (tpl->end()) : frame_to_pixel (JACK_MAX_FRAMES));
1174                 } else {
1175                         transport_punch_range_rect->property_x1() = 0;
1176                         transport_punch_range_rect->property_x2() = (_session->config.get_punch_out() ? frame_to_pixel (tpl->end()) : track_canvas_width);
1177                 }
1178
1179                 if (visibility) {
1180                         transport_punch_range_rect->show();
1181                 }
1182         } else if (visibility) {
1183                 transport_punch_range_rect->hide();
1184         }
1185 }
1186
1187 void
1188 Editor::marker_selection_changed ()
1189 {
1190         if (_session && _session->deletion_in_progress()) {
1191                 return;
1192         }
1193
1194         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1195                 LocationMarkers* lam = i->second;
1196
1197                 if (lam->start) {
1198                         lam->start->hide_line();
1199                 }
1200
1201                 if (lam->end) {
1202                         lam->end->hide_line();
1203                 }
1204         }
1205
1206         for (MarkerSelection::iterator x = selection->markers.begin(); x != selection->markers.end(); ++x) {
1207                 (*x)->add_line (cursor_group, 0, _canvas_height);
1208                 (*x)->show_line ();
1209         }
1210 }
1211
1212 struct SortLocationsByPosition {
1213     bool operator() (Location* a, Location* b) {
1214             return a->start() < b->start();
1215     }
1216 };
1217
1218 void
1219 Editor::goto_nth_marker (int n)
1220 {
1221         if (!_session) {
1222                 return;
1223         }
1224         const Locations::LocationList& l (_session->locations()->list());
1225         Locations::LocationList ordered;
1226         ordered = l;
1227
1228         SortLocationsByPosition cmp;
1229         ordered.sort (cmp);
1230
1231         for (Locations::LocationList::iterator i = ordered.begin(); n >= 0 && i != ordered.end(); ++i) {
1232                 if ((*i)->is_mark() && !(*i)->is_hidden() && !(*i)->is_session_range()) {
1233                         if (n == 0) {
1234                                 _session->request_locate ((*i)->start(), _session->transport_rolling());
1235                                 break;
1236                         }
1237                         --n;
1238                 }
1239         }
1240 }