1 /*
2 * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
4 * Copyright (C) 2006 Hans Fugal <hans@fugal.net>
5 * Copyright (C) 2008-2011 David Robillard <d@drobilla.net>
6 * Copyright (C) 2008-2012 Carl Hetherington <carl@carlh.net>
7 * Copyright (C) 2013-2014 John Emmas <john@creativepost.co.uk>
8 * Copyright (C) 2013-2015 Colin Fletcher <colin.m.fletcher@googlemail.com>
9 * Copyright (C) 2014-2015 Ben Loftis <ben@harrisonconsoles.com>
10 * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
11 * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 */
27
28 #include <cstdlib>
29 #include <cmath>
30
31 #include <gtkmm2ext/gtk_ui.h>
32
33 #include "ardour/session.h"
34 #include "ardour/location.h"
35 #include "ardour/profile.h"
36 #include "pbd/memento_command.h"
37
38 #include "canvas/canvas.h"
39 #include "canvas/item.h"
40 #include "canvas/rectangle.h"
41
42 #include "widgets/prompter.h"
43
44 #include "editor.h"
45 #include "marker.h"
46 #include "selection.h"
47 #include "editing.h"
48 #include "gui_thread.h"
49 #include "actions.h"
50 #include "editor_drag.h"
51 #include "region_view.h"
52
53 #include "pbd/i18n.h"
54
55 using namespace std;
56 using namespace ARDOUR;
57 using namespace PBD;
58 using namespace Gtk;
59 using namespace Gtkmm2ext;
60
61 void
clear_marker_display()62 Editor::clear_marker_display ()
63 {
64 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
65 delete i->second;
66 }
67
68 location_markers.clear ();
69 _sorted_marker_lists.clear ();
70 }
71
72 void
add_new_location(Location * location)73 Editor::add_new_location (Location *location)
74 {
75 ENSURE_GUI_THREAD (*this, &Editor::add_new_location, location);
76
77 ArdourCanvas::Container* group = add_new_location_internal (location);
78
79 /* Do a full update of the markers in this group */
80 update_marker_labels (group);
81
82 if (location->is_auto_punch()) {
83 update_punch_range_view ();
84 }
85
86 if (location->is_auto_loop()) {
87 update_loop_range_view ();
88 }
89 }
90
91 /** Add a new location, without a time-consuming update of all marker labels;
92 * the caller must call update_marker_labels () after calling this.
93 * @return canvas group that the location's marker was added to.
94 */
95 ArdourCanvas::Container*
add_new_location_internal(Location * location)96 Editor::add_new_location_internal (Location* location)
97 {
98 LocationMarkers *lam = new LocationMarkers;
99 uint32_t color;
100
101 /* make a note here of which group this marker ends up in */
102 ArdourCanvas::Container* group = 0;
103
104 if (location->is_cd_marker()) {
105 color = location_cd_marker_color;
106 } else if (location->is_mark()) {
107 color = location_marker_color;
108 } else if (location->is_auto_loop()) {
109 color = location_loop_color;
110 } else if (location->is_auto_punch()) {
111 color = location_punch_color;
112 } else {
113 color = location_range_color;
114 }
115
116 if (location->is_mark()) {
117
118 if (location->is_cd_marker() && ruler_cd_marker_action->get_active()) {
119 lam->start = new ArdourMarker (*this, *cd_marker_group, color, location->name(), ArdourMarker::Mark, location->start());
120 group = cd_marker_group;
121 } else {
122 lam->start = new ArdourMarker (*this, *marker_group, color, location->name(), ArdourMarker::Mark, location->start());
123 group = marker_group;
124 }
125
126 lam->end = 0;
127
128 } else if (location->is_auto_loop()) {
129
130 // transport marker
131 lam->start = new ArdourMarker (*this, *transport_marker_group, color,
132 location->name(), ArdourMarker::LoopStart, location->start());
133 lam->end = new ArdourMarker (*this, *transport_marker_group, color,
134 location->name(), ArdourMarker::LoopEnd, location->end());
135 group = transport_marker_group;
136
137 } else if (location->is_auto_punch()) {
138
139 // transport marker
140 lam->start = new ArdourMarker (*this, *transport_marker_group, color,
141 location->name(), ArdourMarker::PunchIn, location->start());
142 lam->end = new ArdourMarker (*this, *transport_marker_group, color,
143 location->name(), ArdourMarker::PunchOut, location->end());
144 group = transport_marker_group;
145
146 } else if (location->is_session_range()) {
147
148 // session range
149 lam->start = new ArdourMarker (*this, *marker_group, color, _("start"), ArdourMarker::SessionStart, location->start());
150 lam->end = new ArdourMarker (*this, *marker_group, color, _("end"), ArdourMarker::SessionEnd, location->end());
151 group = marker_group;
152
153 } else {
154 // range marker
155 if (location->is_cd_marker() && ruler_cd_marker_action->get_active()) {
156 lam->start = new ArdourMarker (*this, *cd_marker_group, color,
157 location->name(), ArdourMarker::RangeStart, location->start());
158 lam->end = new ArdourMarker (*this, *cd_marker_group, color,
159 location->name(), ArdourMarker::RangeEnd, location->end());
160 group = cd_marker_group;
161 } else {
162 lam->start = new ArdourMarker (*this, *range_marker_group, color,
163 location->name(), ArdourMarker::RangeStart, location->start());
164 lam->end = new ArdourMarker (*this, *range_marker_group, color,
165 location->name(), ArdourMarker::RangeEnd, location->end());
166 group = range_marker_group;
167 }
168 }
169
170 if (location->is_hidden ()) {
171 lam->hide();
172 } else {
173 lam->show ();
174 }
175
176 location->name_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
177 location->position_lock_style_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context());
178 location->FlagsChanged.connect (*this, invalidator (*this), boost::bind (&Editor::location_flags_changed, this, location), gui_context());
179
180 pair<Location*,LocationMarkers*> newpair;
181
182 newpair.first = location;
183 newpair.second = lam;
184
185 location_markers.insert (newpair);
186
187 if (select_new_marker && location->is_mark()) {
188 selection->set (lam->start);
189 select_new_marker = false;
190 }
191
192 lam->set_show_lines (_show_marker_lines);
193
194 /* Add these markers to the appropriate sorted marker lists, which will render
195 them unsorted until a call to update_marker_labels() sorts them out.
196 */
197 _sorted_marker_lists[group].push_back (lam->start);
198 if (lam->end) {
199 _sorted_marker_lists[group].push_back (lam->end);
200 }
201
202 return group;
203 }
204
205 void
location_changed(Location * location)206 Editor::location_changed (Location *location)
207 {
208 ENSURE_GUI_THREAD (*this, &Editor::location_changed, location)
209
210 LocationMarkers *lam = find_location_markers (location);
211
212 if (lam == 0) {
213 /* a location that isn't "marked" with markers */
214 return;
215 }
216
217 if (location->position_lock_style() == MusicTime) {
218 lam->set_name ("\u266B" + location->name ()); // BEAMED EIGHTH NOTES
219 } else {
220 lam->set_name (location->name ());
221 }
222
223 lam->set_position (location->start(), location->end());
224
225 if (location->is_auto_loop()) {
226 update_loop_range_view ();
227 } else if (location->is_auto_punch()) {
228 update_punch_range_view ();
229 }
230
231 check_marker_label (lam->start);
232 if (lam->end) {
233 check_marker_label (lam->end);
234 }
235 }
236
237 /** Look at a marker and check whether its label, and those of the previous and next markers,
238 * need to have their labels updated (in case those labels need to be shortened or can be
239 * lengthened)
240 */
241 void
check_marker_label(ArdourMarker * m)242 Editor::check_marker_label (ArdourMarker* m)
243 {
244 /* Get a time-ordered list of markers from the last time anything changed */
245 std::list<ArdourMarker*>& sorted = _sorted_marker_lists[m->get_parent()];
246
247 list<ArdourMarker*>::iterator i = find (sorted.begin(), sorted.end(), m);
248
249 list<ArdourMarker*>::iterator prev = sorted.end ();
250 list<ArdourMarker*>::iterator next = i;
251 ++next;
252
253 /* Look to see if the previous marker is still behind `m' in time */
254 if (i != sorted.begin()) {
255
256 prev = i;
257 --prev;
258
259 if ((*prev)->position() > m->position()) {
260 /* This marker is no longer in the correct order with the previous one, so
261 * update all the markers in this group.
262 */
263 update_marker_labels (m->get_parent ());
264 return;
265 }
266 }
267
268 /* Look to see if the next marker is still ahead of `m' in time */
269 if (next != sorted.end() && (*next)->position() < m->position()) {
270 /* This marker is no longer in the correct order with the next one, so
271 * update all the markers in this group.
272 */
273 update_marker_labels (m->get_parent ());
274 return;
275 }
276
277 if (prev != sorted.end()) {
278
279 /* Update just the available space between the previous marker and this one */
280
281 double const p = sample_to_pixel (m->position() - (*prev)->position());
282
283 if (m->label_on_left()) {
284 (*prev)->set_right_label_limit (p / 2);
285 } else {
286 (*prev)->set_right_label_limit (p);
287 }
288
289 if ((*prev)->label_on_left ()) {
290 m->set_left_label_limit (p);
291 } else {
292 m->set_left_label_limit (p / 2);
293 }
294 }
295
296 if (next != sorted.end()) {
297
298 /* Update just the available space between this marker and the next */
299
300 double const p = sample_to_pixel ((*next)->position() - m->position());
301
302 if ((*next)->label_on_left()) {
303 m->set_right_label_limit (p / 2);
304 } else {
305 m->set_right_label_limit (p);
306 }
307
308 if (m->label_on_left()) {
309 (*next)->set_left_label_limit (p);
310 } else {
311 (*next)->set_left_label_limit (p / 2);
312 }
313 }
314 }
315
316 struct MarkerComparator {
operator ()MarkerComparator317 bool operator() (ArdourMarker const * a, ArdourMarker const * b) {
318 return a->position() < b->position();
319 }
320 };
321
322 /** Update all marker labels in all groups */
323 void
update_marker_labels()324 Editor::update_marker_labels ()
325 {
326 for (std::map<ArdourCanvas::Container *, std::list<ArdourMarker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
327 update_marker_labels (i->first);
328 }
329 }
330
331 /** Look at all markers in a group and update label widths */
332 void
update_marker_labels(ArdourCanvas::Container * group)333 Editor::update_marker_labels (ArdourCanvas::Container* group)
334 {
335 list<ArdourMarker*>& sorted = _sorted_marker_lists[group];
336
337 if (sorted.empty()) {
338 return;
339 }
340
341 /* We sort the list of markers and then set up the space available between each one */
342
343 sorted.sort (MarkerComparator ());
344
345 list<ArdourMarker*>::iterator i = sorted.begin ();
346
347 list<ArdourMarker*>::iterator prev = sorted.end ();
348 list<ArdourMarker*>::iterator next = i;
349
350 if (next != sorted.end()) {
351 ++next;
352 }
353
354 while (i != sorted.end()) {
355
356 if (prev != sorted.end()) {
357 double const p = sample_to_pixel ((*i)->position() - (*prev)->position());
358
359 if ((*prev)->label_on_left()) {
360 (*i)->set_left_label_limit (p);
361 } else {
362 (*i)->set_left_label_limit (p / 2);
363 }
364
365 }
366
367 if (next != sorted.end()) {
368 double const p = sample_to_pixel ((*next)->position() - (*i)->position());
369
370 if ((*next)->label_on_left()) {
371 (*i)->set_right_label_limit (p / 2);
372 } else {
373 (*i)->set_right_label_limit (p);
374 }
375
376 ++next;
377 }
378
379 prev = i;
380 ++i;
381 }
382 }
383
384 void
location_flags_changed(Location * location)385 Editor::location_flags_changed (Location *location)
386 {
387 ENSURE_GUI_THREAD (*this, &Editor::location_flags_changed, location, src)
388
389 LocationMarkers *lam = find_location_markers (location);
390
391 if (lam == 0) {
392 /* a location that isn't "marked" with markers */
393 return;
394 }
395
396 // move cd markers to/from cd marker bar as appropriate
397 ensure_cd_marker_updated (lam, location);
398
399 if (location->is_cd_marker()) {
400 lam->set_color_rgba (location_cd_marker_color);
401 } else if (location->is_mark()) {
402 lam->set_color_rgba (location_marker_color);
403 } else if (location->is_auto_punch()) {
404 lam->set_color_rgba (location_punch_color);
405 } else if (location->is_auto_loop()) {
406 lam->set_color_rgba (location_loop_color);
407 } else {
408 lam->set_color_rgba (location_range_color);
409 }
410
411 if (location->is_hidden()) {
412 lam->hide();
413 } else {
414 lam->show ();
415 }
416 }
417
update_cd_marker_display()418 void Editor::update_cd_marker_display ()
419 {
420 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
421 LocationMarkers * lam = i->second;
422 Location * location = i->first;
423
424 ensure_cd_marker_updated (lam, location);
425 }
426 }
427
ensure_cd_marker_updated(LocationMarkers * lam,Location * location)428 void Editor::ensure_cd_marker_updated (LocationMarkers * lam, Location * location)
429 {
430 if (location->is_cd_marker()
431 && (ruler_cd_marker_action->get_active() && lam->start->get_parent() != cd_marker_group))
432 {
433 //cerr << "reparenting non-cd marker so it can be relocated: " << location->name() << endl;
434 if (lam->start) {
435 lam->start->reparent (*cd_marker_group);
436 }
437 if (lam->end) {
438 lam->end->reparent (*cd_marker_group);
439 }
440 }
441 else if ( (!location->is_cd_marker() || !ruler_cd_marker_action->get_active())
442 && (lam->start->get_parent() == cd_marker_group))
443 {
444 //cerr << "reparenting non-cd marker so it can be relocated: " << location->name() << endl;
445 if (location->is_mark()) {
446 if (lam->start) {
447 lam->start->reparent (*marker_group);
448 }
449 if (lam->end) {
450 lam->end->reparent (*marker_group);
451 }
452 }
453 else {
454 if (lam->start) {
455 lam->start->reparent (*range_marker_group);
456 }
457 if (lam->end) {
458 lam->end->reparent (*range_marker_group);
459 }
460 }
461 }
462 }
463
~LocationMarkers()464 Editor::LocationMarkers::~LocationMarkers ()
465 {
466 delete start;
467 delete end;
468 }
469
470 Editor::LocationMarkers *
find_location_markers(Location * location) const471 Editor::find_location_markers (Location *location) const
472 {
473 LocationMarkerMap::const_iterator i;
474
475 for (i = location_markers.begin(); i != location_markers.end(); ++i) {
476 if ((*i).first == location) {
477 return (*i).second;
478 }
479 }
480
481 return 0;
482 }
483
484 Location *
find_location_from_marker(ArdourMarker * marker,bool & is_start) const485 Editor::find_location_from_marker (ArdourMarker *marker, bool& is_start) const
486 {
487 LocationMarkerMap::const_iterator i;
488
489 for (i = location_markers.begin(); i != location_markers.end(); ++i) {
490 LocationMarkers *lm = (*i).second;
491 if (lm->start == marker) {
492 is_start = true;
493 return (*i).first;
494 } else if (lm->end == marker) {
495 is_start = false;
496 return (*i).first;
497 }
498 }
499
500 return 0;
501 }
502
503 void
refresh_location_display_internal(const Locations::LocationList & locations)504 Editor::refresh_location_display_internal (const Locations::LocationList& locations)
505 {
506 /* invalidate all */
507
508 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
509 i->second->valid = false;
510 }
511
512 /* add new ones */
513
514 for (Locations::LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
515
516 LocationMarkerMap::iterator x;
517
518 if ((x = location_markers.find (*i)) != location_markers.end()) {
519 x->second->valid = true;
520 continue;
521 }
522
523 add_new_location_internal (*i);
524 }
525
526 /* remove dead ones */
527
528 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ) {
529
530 LocationMarkerMap::iterator tmp;
531
532 tmp = i;
533 ++tmp;
534
535 if (!i->second->valid) {
536
537 remove_sorted_marker (i->second->start);
538 if (i->second->end) {
539 remove_sorted_marker (i->second->end);
540 }
541
542 LocationMarkers* m = i->second;
543 location_markers.erase (i);
544 delete m;
545 }
546
547 i = tmp;
548 }
549
550 update_punch_range_view ();
551 update_loop_range_view ();
552 }
553
554 void
refresh_location_display()555 Editor::refresh_location_display ()
556 {
557 ENSURE_GUI_THREAD (*this, &Editor::refresh_location_display)
558
559 if (_session) {
560 _session->locations()->apply (*this, &Editor::refresh_location_display_internal);
561 }
562
563 update_marker_labels ();
564 }
565
566 void
hide()567 Editor::LocationMarkers::hide()
568 {
569 start->hide ();
570 if (end) {
571 end->hide ();
572 }
573 }
574
575 void
show()576 Editor::LocationMarkers::show()
577 {
578 start->show ();
579 if (end) {
580 end->show ();
581 }
582 }
583
584 void
set_name(const string & str)585 Editor::LocationMarkers::set_name (const string& str)
586 {
587 /* XXX: hack: don't change names of session start/end markers */
588
589 if (start->type() != ArdourMarker::SessionStart) {
590 start->set_name (str);
591 }
592
593 if (end && end->type() != ArdourMarker::SessionEnd) {
594 end->set_name (str);
595 }
596 }
597
598 void
set_position(samplepos_t startf,samplepos_t endf)599 Editor::LocationMarkers::set_position (samplepos_t startf,
600 samplepos_t endf)
601 {
602 start->set_position (startf);
603 if (end) {
604 end->set_position (endf);
605 }
606 }
607
608 void
set_color_rgba(uint32_t rgba)609 Editor::LocationMarkers::set_color_rgba (uint32_t rgba)
610 {
611 start->set_color_rgba (rgba);
612 if (end) {
613 end->set_color_rgba (rgba);
614 }
615 }
616
617 void
set_show_lines(bool s)618 Editor::LocationMarkers::set_show_lines (bool s)
619 {
620 start->set_show_line (s);
621 if (end) {
622 end->set_show_line (s);
623 }
624 }
625
626 void
set_selected(bool s)627 Editor::LocationMarkers::set_selected (bool s)
628 {
629 start->set_selected (s);
630 if (end) {
631 end->set_selected (s);
632 }
633 }
634
635 void
set_entered(bool s)636 Editor::LocationMarkers::set_entered (bool s)
637 {
638 start->set_entered (s);
639 if (end) {
640 end->set_entered (s);
641 }
642 }
643
644 void
setup_lines()645 Editor::LocationMarkers::setup_lines ()
646 {
647 start->setup_line ();
648 if (end) {
649 end->setup_line ();
650 }
651 }
652
653 void
mouse_add_new_marker(samplepos_t where,bool is_cd)654 Editor::mouse_add_new_marker (samplepos_t where, bool is_cd)
655 {
656 string markername;
657 int flags = (is_cd ? Location::IsCDMarker|Location::IsMark : Location::IsMark);
658
659 if (_session) {
660 _session->locations()->next_available_name(markername, _("mark"));
661 if (!choose_new_marker_name(markername)) {
662 return;
663 }
664 Location *location = new Location (*_session, where, where, markername, (Location::Flags) flags, get_grid_music_divisions (0));
665 begin_reversible_command (_("add marker"));
666
667 XMLNode &before = _session->locations()->get_state();
668 _session->locations()->add (location, true);
669 XMLNode &after = _session->locations()->get_state();
670 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
671
672 /* find the marker we just added */
673
674 LocationMarkers *lam = find_location_markers (location);
675 if (lam) {
676 /* make it the selected marker */
677 selection->set (lam->start);
678 }
679
680 commit_reversible_command ();
681 }
682 }
683
684 void
mouse_add_new_loop(samplepos_t where)685 Editor::mouse_add_new_loop (samplepos_t where)
686 {
687 if (!_session) {
688 return;
689 }
690
691 /* Make this marker 1/8th of the visible area of the session so that
692 it's reasonably easy to manipulate after creation.
693 */
694
695 samplepos_t const end = where + current_page_samples() / 8;
696
697 set_loop_range (where, end, _("set loop range"));
698 }
699
700 void
mouse_add_new_punch(samplepos_t where)701 Editor::mouse_add_new_punch (samplepos_t where)
702 {
703 if (!_session) {
704 return;
705 }
706
707 /* Make this marker 1/8th of the visible area of the session so that
708 it's reasonably easy to manipulate after creation.
709 */
710
711 samplepos_t const end = where + current_page_samples() / 8;
712
713 set_punch_range (where, end, _("set punch range"));
714 }
715
716 void
mouse_add_new_range(samplepos_t where)717 Editor::mouse_add_new_range (samplepos_t where)
718 {
719 if (!_session) {
720 return;
721 }
722
723 /* Make this marker 1/8th of the visible area of the session so that
724 it's reasonably easy to manipulate after creation.
725 */
726
727 samplepos_t const end = where + current_page_samples() / 8;
728
729 string name;
730 _session->locations()->next_available_name (name, _("range"));
731 Location* loc = new Location (*_session, where, end, name, Location::IsRangeMarker);
732
733 begin_reversible_command (_("new range marker"));
734 XMLNode& before = _session->locations()->get_state ();
735 _session->locations()->add (loc, true);
736 XMLNode& after = _session->locations()->get_state ();
737 _session->add_command (new MementoCommand<Locations> (*_session->locations(), &before, &after));
738 commit_reversible_command ();
739 }
740
741 void
remove_marker(ArdourCanvas::Item & item)742 Editor::remove_marker (ArdourCanvas::Item& item)
743 {
744 ArdourMarker* marker;
745
746 if (!_session) {
747 return;
748 }
749
750 if ((marker = static_cast<ArdourMarker*> (item.get_data ("marker"))) == 0) {
751 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
752 abort(); /*NOTREACHED*/
753 }
754
755 remove_marker (marker);
756 }
757
758 void
remove_marker(ArdourMarker * marker)759 Editor::remove_marker (ArdourMarker* marker)
760 {
761 if (!_session) {
762 return;
763 }
764
765 if (marker->type() == ArdourMarker::RegionCue) {
766 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_region_marker), marker));
767 } else {
768
769 bool is_start;
770
771 Location* loc = find_location_from_marker (marker, is_start);
772
773 if (loc) {
774 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_global_marker), loc));
775 }
776 }
777 }
778
779 gint
really_remove_global_marker(Location * loc)780 Editor::really_remove_global_marker (Location* loc)
781 {
782 begin_reversible_command (_("remove marker"));
783 XMLNode &before = _session->locations()->get_state();
784 _session->locations()->remove (loc);
785 XMLNode &after = _session->locations()->get_state();
786 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
787 commit_reversible_command ();
788 return FALSE;
789 }
790
791 gint
really_remove_region_marker(ArdourMarker * marker)792 Editor::really_remove_region_marker (ArdourMarker* marker)
793 {
794 begin_reversible_command (_("remove region marker"));
795 RegionView* rv = marker->region_view();
796
797 if (!rv) {
798 abort_reversible_command ();
799 return FALSE;
800 }
801
802 CueMarker cm = rv->find_model_cue_marker (marker);
803 if (cm.text().empty()) {
804 abort_reversible_command ();
805 return FALSE;
806 }
807
808 remove_region_marker (cm);
809
810 commit_reversible_command ();
811 return FALSE;
812 }
813
814 void
location_gone(Location * location)815 Editor::location_gone (Location *location)
816 {
817 ENSURE_GUI_THREAD (*this, &Editor::location_gone, location)
818
819 LocationMarkerMap::iterator i;
820
821 if (location == transport_loop_location()) {
822 update_loop_range_view ();
823 }
824
825 if (location == transport_punch_location()) {
826 update_punch_range_view ();
827 }
828
829 for (i = location_markers.begin(); i != location_markers.end(); ++i) {
830 if (i->first == location) {
831
832 remove_sorted_marker (i->second->start);
833 if (i->second->end) {
834 remove_sorted_marker (i->second->end);
835 }
836
837 LocationMarkers* m = i->second;
838 location_markers.erase (i);
839 delete m;
840
841 /* Markers that visually overlap with this (removed) marker
842 * need to be re-displayed.
843 * But finding such cases is similarly expensive as simply
844 * re-displaying all.. so:
845 */
846 refresh_location_display ();
847 break;
848 }
849 }
850 }
851
852 void
tempo_or_meter_marker_context_menu(GdkEventButton * ev,ArdourCanvas::Item * item)853 Editor::tempo_or_meter_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
854 {
855 marker_menu_item = item;
856
857 MeterMarker* mm;
858 TempoMarker* tm;
859 dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
860
861 bool can_remove = false;
862
863 if (mm) {
864 can_remove = !mm->meter().initial ();
865 build_meter_marker_menu (mm, can_remove);
866 meter_marker_menu->popup (1, ev->time);
867 } else if (tm) {
868 if (!tm->tempo().active()) {
869 return;
870 }
871 can_remove = !tm->tempo().initial() && !tm->tempo().locked_to_meter();
872 build_tempo_marker_menu (tm, can_remove);
873 tempo_marker_menu->popup (1, ev->time);
874 } else {
875 return;
876 }
877 }
878
879 void
marker_context_menu(GdkEventButton * ev,ArdourCanvas::Item * item)880 Editor::marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item* item)
881 {
882 ArdourMarker * marker;
883 if ((marker = reinterpret_cast<ArdourMarker *> (item->get_data("marker"))) == 0) {
884 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
885 abort(); /*NOTREACHED*/
886 }
887
888 if (marker->type() == ArdourMarker::RegionCue) {
889 /* no context menu for these puppies */
890 return;
891 }
892
893 bool is_start;
894 Location * loc = find_location_from_marker (marker, is_start);
895
896
897 if (loc == transport_loop_location() || loc == transport_punch_location() || loc->is_session_range ()) {
898
899 build_range_marker_menu (loc, loc == transport_loop_location() || loc == transport_punch_location(), loc->is_session_range());
900
901 marker_menu_item = item;
902 range_marker_menu->popup (1, ev->time);
903
904 } else if (loc->is_mark()) {
905
906 build_marker_menu (loc);
907
908 // GTK2FIX use action group sensitivity
909 #ifdef GTK2FIX
910 if (children.size() >= 3) {
911 MenuItem * loopitem = &children[2];
912 if (loopitem) {
913 if (loc->is_mark()) {
914 loopitem->set_sensitive(false);
915 }
916 else {
917 loopitem->set_sensitive(true);
918 }
919 }
920 }
921 #endif
922 marker_menu_item = item;
923 marker_menu->popup (1, ev->time);
924
925 } else if (loc->is_range_marker()) {
926 build_range_marker_menu (loc, false, false);
927 marker_menu_item = item;
928 range_marker_menu->popup (1, ev->time);
929 }
930 }
931
932 void
new_transport_marker_context_menu(GdkEventButton * ev,ArdourCanvas::Item *)933 Editor::new_transport_marker_context_menu (GdkEventButton* ev, ArdourCanvas::Item*)
934 {
935 if (new_transport_marker_menu == 0) {
936 build_new_transport_marker_menu ();
937 }
938
939 new_transport_marker_menu->popup (1, ev->time);
940
941 }
942
943 void
build_marker_menu(Location * loc)944 Editor::build_marker_menu (Location* loc)
945 {
946 using namespace Menu_Helpers;
947
948 delete marker_menu;
949 marker_menu = new Menu;
950
951 MenuList& items = marker_menu->items();
952 marker_menu->set_name ("ArdourContextMenu");
953
954 items.push_back (MenuElem (_("Locate to Here"), sigc::mem_fun(*this, &Editor::marker_menu_set_playhead)));
955 items.push_back (MenuElem (_("Play from Here"), sigc::mem_fun(*this, &Editor::marker_menu_play_from)));
956 items.push_back (MenuElem (_("Move Mark to Playhead"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
957
958 items.push_back (SeparatorElem());
959
960 items.push_back (MenuElem (_("Create Range to Next Marker"), sigc::mem_fun(*this, &Editor::marker_menu_range_to_next)));
961
962 items.push_back (MenuElem (_("Promote to Time Origin"), sigc::mem_fun(*this, &Editor::marker_menu_set_origin)));
963 items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &Editor::marker_menu_hide)));
964 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::marker_menu_rename)));
965
966 items.push_back (CheckMenuElem (_("Lock")));
967 Gtk::CheckMenuItem* lock_item = static_cast<Gtk::CheckMenuItem*> (&items.back());
968 if (loc->locked ()) {
969 lock_item->set_active ();
970 }
971 lock_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_marker_menu_lock));
972
973 items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
974 Gtk::CheckMenuItem* glue_item = static_cast<Gtk::CheckMenuItem*> (&items.back());
975 glue_item->set_active (loc->position_lock_style() == MusicTime);
976
977 glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_marker_menu_glue));
978
979 items.push_back (SeparatorElem());
980
981 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
982 }
983
984 void
build_range_marker_menu(Location * loc,bool loop_or_punch,bool session)985 Editor::build_range_marker_menu (Location* loc, bool loop_or_punch, bool session)
986 {
987 using namespace Menu_Helpers;
988
989 bool const loop_or_punch_or_session = loop_or_punch || session;
990
991 delete range_marker_menu;
992 range_marker_menu = new Menu;
993
994 MenuList& items = range_marker_menu->items();
995 range_marker_menu->set_name ("ArdourContextMenu");
996
997 items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::marker_menu_play_range)));
998 items.push_back (MenuElem (_("Locate to Marker"), sigc::mem_fun(*this, &Editor::marker_menu_set_playhead)));
999 items.push_back (MenuElem (_("Play from Marker"), sigc::mem_fun(*this, &Editor::marker_menu_play_from)));
1000 items.push_back (MenuElem (_("Loop Range"), sigc::mem_fun(*this, &Editor::marker_menu_loop_range)));
1001
1002 items.push_back (MenuElem (_("Set Marker from Playhead"), sigc::mem_fun(*this, &Editor::marker_menu_set_from_playhead)));
1003 items.push_back (MenuElem (_("Set Range from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::marker_menu_set_from_selection), false)));
1004
1005 items.push_back (MenuElem (_("Zoom to Range"), sigc::mem_fun (*this, &Editor::marker_menu_zoom_to_range)));
1006
1007 items.push_back (SeparatorElem());
1008 items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
1009
1010 Gtk::CheckMenuItem* glue_item = static_cast<Gtk::CheckMenuItem*> (&items.back());
1011 glue_item->set_active (loc->position_lock_style() == MusicTime);
1012 glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_marker_menu_glue));
1013
1014 items.push_back (SeparatorElem());
1015 items.push_back (MenuElem (_("Loudness Assistant..."), sigc::mem_fun(*this, &Editor::loudness_assistant_marker)));
1016 items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_range)));
1017 items.push_back (SeparatorElem());
1018
1019 items.push_back (MenuElem (_("Promote to Time Origin"), sigc::mem_fun(*this, &Editor::marker_menu_set_origin)));
1020 if (!loop_or_punch_or_session) {
1021 items.push_back (MenuElem (_("Hide Range"), sigc::mem_fun(*this, &Editor::marker_menu_hide)));
1022 items.push_back (MenuElem (_("Rename Range..."), sigc::mem_fun(*this, &Editor::marker_menu_rename)));
1023 }
1024
1025 if (!session) {
1026 items.push_back (MenuElem (_("Remove Range"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
1027 }
1028
1029 if (!loop_or_punch_or_session || !session) {
1030 items.push_back (SeparatorElem());
1031 }
1032
1033 items.push_back (MenuElem (_("Separate Regions in Range"), sigc::mem_fun(*this, &Editor::marker_menu_separate_regions_using_location)));
1034 items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::marker_menu_select_all_selectables_using_range)));
1035 items.push_back (MenuElem (_("Select Range"), sigc::mem_fun(*this, &Editor::marker_menu_select_using_range)));
1036 }
1037
1038 void
build_tempo_marker_menu(TempoMarker * loc,bool can_remove)1039 Editor::build_tempo_marker_menu (TempoMarker* loc, bool can_remove)
1040 {
1041 using namespace Menu_Helpers;
1042
1043 delete tempo_marker_menu;
1044 tempo_marker_menu = new Menu;
1045
1046 MenuList& items = tempo_marker_menu->items();
1047 tempo_marker_menu->set_name ("ArdourContextMenu");
1048
1049 if (!loc->tempo().initial()) {
1050 if (loc->tempo().clamped()) {
1051 items.push_back (MenuElem (_("Don't Continue"), sigc::mem_fun(*this, &Editor::toggle_tempo_clamped)));
1052 } else {
1053 items.push_back (MenuElem (_("Continue"), sigc::mem_fun(*this, &Editor::toggle_tempo_clamped)));
1054 }
1055 }
1056
1057 if (loc->tempo().type() == TempoSection::Ramp) {
1058 items.push_back (MenuElem (_("Set Constant"), sigc::mem_fun(*this, &Editor::toggle_tempo_type)));
1059 }
1060
1061 TempoSection* next_ts = _session->tempo_map().next_tempo_section (&loc->tempo());
1062 if (next_ts && next_ts->note_types_per_minute() != loc->tempo().end_note_types_per_minute()) {
1063 items.push_back (MenuElem (_("Ramp to Next"), sigc::mem_fun(*this, &Editor::ramp_to_next_tempo)));
1064 }
1065
1066 if (loc->tempo().position_lock_style() == AudioTime && can_remove) {
1067 items.push_back (SeparatorElem());
1068 items.push_back (MenuElem (_("Lock to Music"), sigc::mem_fun(*this, &Editor::toggle_marker_lock_style)));
1069 } else if (can_remove) {
1070 items.push_back (SeparatorElem());
1071 items.push_back (MenuElem (_("Lock to Audio"), sigc::mem_fun(*this, &Editor::toggle_marker_lock_style)));
1072 }
1073
1074 items.push_back (SeparatorElem());
1075
1076 items.push_back (MenuElem (_("Edit..."), sigc::mem_fun(*this, &Editor::marker_menu_edit)));
1077 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
1078 items.back().set_sensitive (can_remove);
1079 }
1080
1081 void
build_meter_marker_menu(MeterMarker * loc,bool can_remove)1082 Editor::build_meter_marker_menu (MeterMarker* loc, bool can_remove)
1083 {
1084 using namespace Menu_Helpers;
1085
1086 delete meter_marker_menu;
1087 meter_marker_menu = new Menu;
1088
1089 MenuList& items = meter_marker_menu->items();
1090 meter_marker_menu->set_name ("ArdourContextMenu");
1091
1092 if (loc->meter().position_lock_style() == AudioTime && can_remove) {
1093 items.push_back (MenuElem (_("Lock to Music"), sigc::mem_fun(*this, &Editor::toggle_marker_lock_style)));
1094 } else if (can_remove) {
1095 items.push_back (MenuElem (_("Lock to Audio"), sigc::mem_fun(*this, &Editor::toggle_marker_lock_style)));
1096 }
1097
1098 items.push_back (MenuElem (_("Edit..."), sigc::mem_fun(*this, &Editor::marker_menu_edit)));
1099 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::marker_menu_remove)));
1100
1101 items.back().set_sensitive (can_remove);
1102 }
1103
1104 void
build_new_transport_marker_menu()1105 Editor::build_new_transport_marker_menu ()
1106 {
1107 using namespace Menu_Helpers;
1108
1109 new_transport_marker_menu = new Menu;
1110
1111 MenuList& items = new_transport_marker_menu->items();
1112 new_transport_marker_menu->set_name ("ArdourContextMenu");
1113
1114 items.push_back (MenuElem (_("Set Loop Range"), sigc::mem_fun(*this, &Editor::new_transport_marker_menu_set_loop)));
1115 items.push_back (MenuElem (_("Set Punch Range"), sigc::mem_fun(*this, &Editor::new_transport_marker_menu_set_punch)));
1116
1117 new_transport_marker_menu->signal_unmap().connect ( sigc::mem_fun(*this, &Editor::new_transport_marker_menu_popdown));
1118 }
1119
1120 void
marker_menu_hide()1121 Editor::marker_menu_hide ()
1122 {
1123 ArdourMarker* marker;
1124
1125 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1126 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1127 abort(); /*NOTREACHED*/
1128 }
1129
1130 Location* l;
1131 bool is_start;
1132
1133 if ((l = find_location_from_marker (marker, is_start)) != 0) {
1134 l->set_hidden (true, this);
1135 }
1136 }
1137
1138 void
marker_menu_set_origin()1139 Editor::marker_menu_set_origin ()
1140 {
1141 ArdourMarker* marker;
1142
1143 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1144 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1145 abort(); /*NOTREACHED*/
1146 }
1147
1148 Location* l;
1149 bool is_start;
1150
1151 if ((l = find_location_from_marker (marker, is_start)) != 0) {
1152 _session->locations()->set_clock_origin (l, this);
1153 }
1154 }
1155
1156 void
marker_menu_select_using_range()1157 Editor::marker_menu_select_using_range ()
1158 {
1159 ArdourMarker* marker;
1160
1161 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1162 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1163 abort(); /*NOTREACHED*/
1164 }
1165
1166 Location* l;
1167 bool is_start;
1168
1169 if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
1170 set_selection_from_range (*l);
1171 }
1172 }
1173
1174 void
marker_menu_select_all_selectables_using_range()1175 Editor::marker_menu_select_all_selectables_using_range ()
1176 {
1177 ArdourMarker* marker;
1178
1179 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1180 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1181 abort(); /*NOTREACHED*/
1182 }
1183
1184 Location* l;
1185 bool is_start;
1186
1187 if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
1188 select_all_within (l->start(), l->end() - 1, 0, DBL_MAX, track_views, Selection::Set, false);
1189 }
1190
1191 }
1192
1193 void
marker_menu_separate_regions_using_location()1194 Editor::marker_menu_separate_regions_using_location ()
1195 {
1196 ArdourMarker* marker;
1197
1198 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1199 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1200 abort(); /*NOTREACHED*/
1201 }
1202
1203 Location* l;
1204 bool is_start;
1205
1206 if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
1207 separate_regions_using_location (*l);
1208 }
1209
1210 }
1211
1212 void
marker_menu_play_from()1213 Editor::marker_menu_play_from ()
1214 {
1215 ArdourMarker* marker;
1216
1217 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1218 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1219 abort(); /*NOTREACHED*/
1220 }
1221
1222 Location* l;
1223 bool is_start;
1224
1225 if ((l = find_location_from_marker (marker, is_start)) != 0) {
1226
1227 if (l->is_mark()) {
1228 _session->request_locate (l->start(), MustRoll);
1229 }
1230 else {
1231 //_session->request_bounded_roll (l->start(), l->end());
1232
1233 if (is_start) {
1234 _session->request_locate (l->start(), MustRoll);
1235 } else {
1236 _session->request_locate (l->end(), MustRoll);
1237 }
1238 }
1239 }
1240 }
1241
1242 void
marker_menu_set_playhead()1243 Editor::marker_menu_set_playhead ()
1244 {
1245 ArdourMarker* marker;
1246
1247 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1248 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1249 abort(); /*NOTREACHED*/
1250 }
1251
1252 Location* l;
1253 bool is_start;
1254
1255 if ((l = find_location_from_marker (marker, is_start)) != 0) {
1256
1257 if (l->is_mark()) {
1258 _session->request_locate (l->start(), MustStop);
1259 }
1260 else {
1261 if (is_start) {
1262 _session->request_locate (l->start(), MustStop);
1263 } else {
1264 _session->request_locate (l->end(), MustStop);
1265 }
1266 }
1267 }
1268 }
1269
1270 void
marker_menu_range_to_next()1271 Editor::marker_menu_range_to_next ()
1272 {
1273 ArdourMarker* marker;
1274 if (!_session) {
1275 return;
1276 }
1277
1278 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1279 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1280 abort(); /*NOTREACHED*/
1281 }
1282
1283 Location* l;
1284 bool is_start;
1285
1286 if ((l = find_location_from_marker (marker, is_start)) == 0) {
1287 return;
1288 }
1289
1290 samplepos_t start;
1291 samplepos_t end;
1292 _session->locations()->marks_either_side (marker->position(), start, end);
1293
1294 if (end != max_samplepos) {
1295 string range_name = l->name();
1296 range_name += "-range";
1297
1298 Location* newrange = new Location (*_session, marker->position(), end, range_name, Location::IsRangeMarker);
1299 _session->locations()->add (newrange);
1300 }
1301 }
1302
1303 void
marker_menu_set_from_playhead()1304 Editor::marker_menu_set_from_playhead ()
1305 {
1306 ArdourMarker* marker;
1307
1308 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1309 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1310 abort(); /*NOTREACHED*/
1311 }
1312
1313 Location* l;
1314 bool is_start;
1315 const int32_t divisions = get_grid_music_divisions (0);
1316
1317 if ((l = find_location_from_marker (marker, is_start)) != 0) {
1318
1319 if (l->is_mark()) {
1320 l->set_start (_session->audible_sample (), false, true, divisions);
1321 }
1322 else {
1323 if (is_start) {
1324 l->set_start (_session->audible_sample (), false, true, divisions);
1325 } else {
1326 l->set_end (_session->audible_sample (), false, true, divisions);
1327 }
1328 }
1329 }
1330 }
1331
1332 void
marker_menu_set_from_selection(bool)1333 Editor::marker_menu_set_from_selection (bool /*force_regions*/)
1334 {
1335 ArdourMarker* marker;
1336
1337 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1338 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1339 abort(); /*NOTREACHED*/
1340 }
1341
1342 Location* l;
1343 bool is_start;
1344
1345 if ((l = find_location_from_marker (marker, is_start)) != 0) {
1346
1347 if (l->is_mark()) {
1348
1349 // nothing for now
1350
1351 } else {
1352
1353 if (!selection->time.empty()) {
1354 l->set (selection->time.start(), selection->time.end_sample());
1355 } else if (!selection->regions.empty()) {
1356 l->set (selection->regions.start(), selection->regions.end_sample());
1357 }
1358 }
1359 }
1360 }
1361
1362
1363 void
marker_menu_play_range()1364 Editor::marker_menu_play_range ()
1365 {
1366 ArdourMarker* marker;
1367
1368 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1369 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1370 abort(); /*NOTREACHED*/
1371 }
1372
1373 Location* l;
1374 bool is_start;
1375
1376 if ((l = find_location_from_marker (marker, is_start)) != 0) {
1377
1378 if (l->is_mark()) {
1379 _session->request_locate (l->start(), MustRoll);
1380 }
1381 else {
1382 _session->request_bounded_roll (l->start(), l->end());
1383
1384 }
1385 }
1386 }
1387
1388 void
marker_menu_loop_range()1389 Editor::marker_menu_loop_range ()
1390 {
1391 ArdourMarker* marker;
1392
1393 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1394 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1395 abort(); /*NOTREACHED*/
1396 }
1397
1398 Location* l;
1399 bool is_start;
1400
1401 if ((l = find_location_from_marker (marker, is_start)) != 0) {
1402 if (l != transport_loop_location()) {
1403 cerr << "Set loop\n";
1404 set_loop_range (l->start(), l->end(), _("loop range from marker"));
1405 } else {
1406 cerr << " at TL\n";
1407 }
1408 _session->request_play_loop (true);
1409 }
1410 }
1411
1412 /** Temporal zoom to the range of the marker_menu_item (plus 5% either side) */
1413 void
marker_menu_zoom_to_range()1414 Editor::marker_menu_zoom_to_range ()
1415 {
1416 ArdourMarker* marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"));
1417 assert (marker);
1418
1419 bool is_start;
1420 Location* l = find_location_from_marker (marker, is_start);
1421 if (l == 0) {
1422 return;
1423 }
1424
1425 samplecnt_t const extra = l->length() * 0.05;
1426 samplepos_t a = l->start ();
1427 if (a >= extra) {
1428 a -= extra;
1429 }
1430
1431 samplepos_t b = l->end ();
1432 if (b < (max_samplepos - extra)) {
1433 b += extra;
1434 }
1435
1436 temporal_zoom_by_sample (a, b);
1437 }
1438
1439 void
dynamic_cast_marker_object(void * p,MeterMarker ** m,TempoMarker ** t) const1440 Editor::dynamic_cast_marker_object (void* p, MeterMarker** m, TempoMarker** t) const
1441 {
1442 ArdourMarker* marker = reinterpret_cast<ArdourMarker*> (p);
1443 if (!marker) {
1444 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1445 abort(); /*NOTREACHED*/
1446 }
1447
1448 *m = dynamic_cast<MeterMarker*> (marker);
1449 *t = dynamic_cast<TempoMarker*> (marker);
1450 }
1451
1452 void
marker_menu_edit()1453 Editor::marker_menu_edit ()
1454 {
1455 MeterMarker* mm;
1456 TempoMarker* tm;
1457 dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1458
1459 if (mm) {
1460 edit_meter_section (&mm->meter());
1461 } else if (tm) {
1462 edit_tempo_section (&tm->tempo());
1463 }
1464 }
1465
1466 void
marker_menu_remove()1467 Editor::marker_menu_remove ()
1468 {
1469 MeterMarker* mm;
1470 TempoMarker* tm;
1471 dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1472
1473 if (mm) {
1474 remove_meter_marker (marker_menu_item);
1475 } else if (tm) {
1476 remove_tempo_marker (marker_menu_item);
1477 } else {
1478 remove_marker (*marker_menu_item);
1479 }
1480 }
1481
1482 void
toggle_marker_lock_style()1483 Editor::toggle_marker_lock_style ()
1484 {
1485 MeterMarker* mm;
1486 TempoMarker* tm;
1487 dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1488
1489 if (mm) {
1490 begin_reversible_command (_("change meter lock style"));
1491 XMLNode &before = _session->tempo_map().get_state();
1492 MeterSection* msp = &mm->meter();
1493
1494 const Meter meter (msp->divisions_per_bar(), msp->note_divisor());
1495 const Timecode::BBT_Time bbt (msp->bbt());
1496 const PositionLockStyle pls = (msp->position_lock_style() == AudioTime) ? MusicTime : AudioTime;
1497
1498 _session->tempo_map().replace_meter (*msp, meter, bbt, msp->sample(), pls);
1499
1500 XMLNode &after = _session->tempo_map().get_state();
1501 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1502 commit_reversible_command ();
1503 } else if (tm) {
1504 TempoSection* tsp = &tm->tempo();
1505
1506 const double pulse = tsp->pulse();
1507 const samplepos_t sample = tsp->sample();
1508 const PositionLockStyle pls = (tsp->position_lock_style() == AudioTime) ? MusicTime : AudioTime;
1509 const Tempo tempo (tsp->note_types_per_minute(), tsp->note_type(), tsp->end_note_types_per_minute());
1510
1511 begin_reversible_command (_("change tempo lock style"));
1512 XMLNode &before = _session->tempo_map().get_state();
1513
1514 _session->tempo_map().replace_tempo (*tsp, tempo, pulse, sample, pls);
1515
1516 XMLNode &after = _session->tempo_map().get_state();
1517 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1518 commit_reversible_command ();
1519 }
1520 }
1521 /* actally just resets the ts to constant using initial tempo */
1522 void
toggle_tempo_type()1523 Editor::toggle_tempo_type ()
1524 {
1525 TempoMarker* tm;
1526 MeterMarker* mm;
1527 dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1528
1529 if (tm) {
1530 TempoSection* tsp = &tm->tempo();
1531
1532 const Tempo tempo (tsp->note_types_per_minute(), tsp->note_type());
1533 const double pulse = tsp->pulse();
1534 const samplepos_t sample = tsp->sample();
1535 const PositionLockStyle pls = tsp->position_lock_style();
1536
1537 begin_reversible_command (_("set tempo to constant"));
1538 XMLNode &before = _session->tempo_map().get_state();
1539
1540 _session->tempo_map().replace_tempo (*tsp, tempo, pulse, sample, pls);
1541
1542 XMLNode &after = _session->tempo_map().get_state();
1543 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1544 commit_reversible_command ();
1545 }
1546 }
1547 /* clamped locks the previous section end tempo to the start tempo */
1548 void
toggle_tempo_clamped()1549 Editor::toggle_tempo_clamped ()
1550 {
1551 TempoMarker* tm;
1552 MeterMarker* mm;
1553 dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1554
1555 if (tm) {
1556 begin_reversible_command (_("Clamp Tempo"));
1557 XMLNode &before = _session->tempo_map().get_state();
1558
1559 TempoSection* tsp = &tm->tempo();
1560 TempoSection* prev = _session->tempo_map().previous_tempo_section (tsp);
1561
1562 if (prev) {
1563 /* set to the end tempo of the previous section */
1564 Tempo new_tempo (prev->end_note_types_per_minute(), prev->note_type(), tsp->end_note_types_per_minute());
1565 _session->tempo_map().gui_change_tempo (tsp, new_tempo);
1566 }
1567
1568 tsp->set_clamped (!tsp->clamped());
1569
1570 XMLNode &after = _session->tempo_map().get_state();
1571 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1572 commit_reversible_command ();
1573 }
1574 }
1575
1576 void
ramp_to_next_tempo()1577 Editor::ramp_to_next_tempo ()
1578 {
1579 TempoMarker* tm;
1580 MeterMarker* mm;
1581 dynamic_cast_marker_object (marker_menu_item->get_data ("marker"), &mm, &tm);
1582
1583 if (tm) {
1584 TempoMap& tmap (_session->tempo_map());
1585 TempoSection* tsp = &tm->tempo();
1586 TempoSection* next_ts = tmap.next_tempo_section (&tm->tempo());
1587 if (next_ts) {
1588 const Tempo tempo (tsp->note_types_per_minute(), tsp->note_type(), next_ts->note_types_per_minute());
1589 const double pulse = tsp->pulse();
1590 const samplepos_t sample = tsp->sample();
1591 const PositionLockStyle pls = tsp->position_lock_style();
1592
1593 begin_reversible_command (_("ramp to next tempo"));
1594 XMLNode &before = _session->tempo_map().get_state();
1595
1596 tmap.replace_tempo (*tsp, tempo, pulse, sample, pls);
1597
1598 XMLNode &after = _session->tempo_map().get_state();
1599 _session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
1600 commit_reversible_command ();
1601 }
1602 }
1603 }
1604
1605 void
toggle_marker_menu_lock()1606 Editor::toggle_marker_menu_lock ()
1607 {
1608 ArdourMarker* marker;
1609
1610 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1611 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1612 abort(); /*NOTREACHED*/
1613 }
1614
1615 Location* loc;
1616 bool ignored;
1617
1618 loc = find_location_from_marker (marker, ignored);
1619
1620 if (!loc) {
1621 return;
1622 }
1623
1624 if (loc->locked()) {
1625 loc->unlock ();
1626 } else {
1627 loc->lock ();
1628 }
1629 }
1630
1631 void
marker_menu_rename()1632 Editor::marker_menu_rename ()
1633 {
1634 ArdourMarker* marker;
1635
1636 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1637 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1638 abort(); /*NOTREACHED*/
1639 }
1640
1641
1642 rename_marker (marker);
1643 }
1644
1645 void
rename_marker(ArdourMarker * marker)1646 Editor::rename_marker(ArdourMarker *marker)
1647 {
1648 Location* loc;
1649 bool is_start;
1650
1651 loc = find_location_from_marker (marker, is_start);
1652
1653 if (!loc) {
1654 return;
1655 }
1656
1657 if (loc == transport_loop_location() || loc == transport_punch_location() || loc->is_session_range()) {
1658 return;
1659 }
1660
1661 ArdourWidgets::Prompter dialog (true);
1662 string txt;
1663
1664 dialog.set_prompt (_("New Name:"));
1665
1666 if (loc->is_mark()) {
1667 dialog.set_title (_("Rename Mark"));
1668 } else {
1669 dialog.set_title (_("Rename Range"));
1670 }
1671
1672 dialog.set_name ("MarkRenameWindow");
1673 dialog.set_size_request (250, -1);
1674 dialog.set_position (Gtk::WIN_POS_MOUSE);
1675
1676 dialog.add_button (_("Rename"), RESPONSE_ACCEPT);
1677 dialog.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1678 dialog.set_initial_text (loc->name());
1679
1680 dialog.show ();
1681
1682 switch (dialog.run ()) {
1683 case RESPONSE_ACCEPT:
1684 break;
1685 default:
1686 return;
1687 }
1688
1689 begin_reversible_command ( _("rename marker") );
1690 XMLNode &before = _session->locations()->get_state();
1691
1692 dialog.get_result(txt);
1693 loc->set_name (txt);
1694 _session->set_dirty ();
1695
1696 XMLNode &after = _session->locations()->get_state();
1697 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1698 commit_reversible_command ();
1699 }
1700
1701 void
new_transport_marker_menu_popdown()1702 Editor::new_transport_marker_menu_popdown ()
1703 {
1704 // hide rects
1705 transport_bar_drag_rect->hide();
1706
1707 _drags->abort ();
1708 }
1709
1710 void
new_transport_marker_menu_set_loop()1711 Editor::new_transport_marker_menu_set_loop ()
1712 {
1713 set_loop_range (temp_location->start(), temp_location->end(), _("set loop range"));
1714 }
1715
1716 void
new_transport_marker_menu_set_punch()1717 Editor::new_transport_marker_menu_set_punch ()
1718 {
1719 set_punch_range (temp_location->start(), temp_location->end(), _("set punch range"));
1720 }
1721
1722 void
update_loop_range_view()1723 Editor::update_loop_range_view ()
1724 {
1725 if (_session == 0) {
1726 return;
1727 }
1728
1729 Location* tll;
1730
1731 if (_session->get_play_loop() && ((tll = transport_loop_location()) != 0)) {
1732
1733 double x1 = sample_to_pixel (tll->start());
1734 double x2 = sample_to_pixel (tll->end());
1735
1736 transport_loop_range_rect->set_x0 (x1);
1737 transport_loop_range_rect->set_x1 (x2);
1738
1739 transport_loop_range_rect->show();
1740
1741 } else {
1742 transport_loop_range_rect->hide();
1743 }
1744 }
1745
1746 void
update_punch_range_view()1747 Editor::update_punch_range_view ()
1748 {
1749 if (_session == 0) {
1750 return;
1751 }
1752
1753 Location* tpl;
1754
1755 if ((_session->config.get_punch_in() || _session->config.get_punch_out()) && ((tpl = transport_punch_location()) != 0)) {
1756
1757 double pixel_start;
1758 double pixel_end;
1759
1760 if (_session->config.get_punch_in()) {
1761 pixel_start = sample_to_pixel (tpl->start());
1762 } else {
1763 pixel_start = 0;
1764 }
1765 if (_session->config.get_punch_out()) {
1766 pixel_end = sample_to_pixel (tpl->end());
1767 } else {
1768 pixel_end = sample_to_pixel (max_samplepos);
1769 }
1770
1771 transport_punch_range_rect->set_x0 (pixel_start);
1772 transport_punch_range_rect->set_x1 (pixel_end);
1773 transport_punch_range_rect->show();
1774
1775 } else {
1776
1777 transport_punch_range_rect->hide();
1778 }
1779 }
1780
1781 void
marker_selection_changed()1782 Editor::marker_selection_changed ()
1783 {
1784 if (_session && _session->deletion_in_progress()) {
1785 return;
1786 }
1787
1788 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1789 i->second->set_selected (false);
1790 }
1791
1792 for (MarkerSelection::iterator x = selection->markers.begin(); x != selection->markers.end(); ++x) {
1793 (*x)->set_selected (true);
1794 }
1795 }
1796
1797 struct SortLocationsByPosition {
operator ()SortLocationsByPosition1798 bool operator() (Location* a, Location* b) {
1799 return a->start() < b->start();
1800 }
1801 };
1802
1803 void
goto_nth_marker(int n)1804 Editor::goto_nth_marker (int n)
1805 {
1806 if (!_session) {
1807 return;
1808 }
1809 const Locations::LocationList& l (_session->locations()->list());
1810 Locations::LocationList ordered;
1811 ordered = l;
1812
1813 SortLocationsByPosition cmp;
1814 ordered.sort (cmp);
1815
1816 for (Locations::LocationList::iterator i = ordered.begin(); n >= 0 && i != ordered.end(); ++i) {
1817 if ((*i)->is_mark() && !(*i)->is_hidden() && !(*i)->is_session_range()) {
1818 if (n == 0) {
1819 _session->request_locate ((*i)->start());
1820 break;
1821 }
1822 --n;
1823 }
1824 }
1825 }
1826
1827 void
toggle_marker_menu_glue()1828 Editor::toggle_marker_menu_glue ()
1829 {
1830 ArdourMarker* marker;
1831
1832 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
1833 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
1834 abort(); /*NOTREACHED*/
1835 }
1836
1837 Location* loc;
1838 bool ignored;
1839
1840 loc = find_location_from_marker (marker, ignored);
1841
1842 if (!loc) {
1843 return;
1844 }
1845
1846 begin_reversible_command (_("change marker lock style"));
1847 XMLNode &before = _session->locations()->get_state();
1848
1849 if (loc->position_lock_style() == MusicTime) {
1850 loc->set_position_lock_style (AudioTime);
1851 } else {
1852 loc->set_position_lock_style (MusicTime);
1853 }
1854
1855 XMLNode &after = _session->locations()->get_state();
1856 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1857 commit_reversible_command ();
1858 }
1859
1860 void
toggle_marker_lines()1861 Editor::toggle_marker_lines ()
1862 {
1863 _show_marker_lines = !_show_marker_lines;
1864
1865 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1866 i->second->set_show_lines (_show_marker_lines);
1867 }
1868 }
1869
1870 void
remove_sorted_marker(ArdourMarker * m)1871 Editor::remove_sorted_marker (ArdourMarker* m)
1872 {
1873 for (std::map<ArdourCanvas::Container *, std::list<ArdourMarker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
1874 i->second.remove (m);
1875 }
1876 }
1877
1878 ArdourMarker *
find_marker_from_location_id(PBD::ID const & id,bool is_start) const1879 Editor::find_marker_from_location_id (PBD::ID const & id, bool is_start) const
1880 {
1881 for (LocationMarkerMap::const_iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1882 if (i->first->id() == id) {
1883 return is_start ? i->second->start : i->second->end;
1884 }
1885 }
1886
1887 return 0;
1888 }
1889