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