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