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