1 /*
2  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
3  * Copyright (C) 2007-2015 David Robillard <d@drobilla.net>
4  * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
6  * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
7  * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
8  * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 
25 #include <algorithm>
26 #include <cstdlib>
27 
28 #include "pbd/unwind.h"
29 
30 #include "ardour/control_protocol_manager.h"
31 #include "ardour/midi_region.h"
32 #include "ardour/playlist.h"
33 #include "ardour/profile.h"
34 #include "ardour/route_group.h"
35 #include "ardour/selection.h"
36 #include "ardour/session.h"
37 #include "ardour/vca.h"
38 
39 #include "editor.h"
40 #include "editor_drag.h"
41 #include "editor_routes.h"
42 #include "editor_sources.h"
43 #include "actions.h"
44 #include "audio_time_axis.h"
45 #include "audio_region_view.h"
46 #include "audio_streamview.h"
47 #include "automation_line.h"
48 #include "control_point.h"
49 #include "editor_regions.h"
50 #include "editor_cursors.h"
51 #include "keyboard.h"
52 #include "midi_region_view.h"
53 #include "sfdb_ui.h"
54 
55 #include "pbd/i18n.h"
56 
57 using namespace std;
58 using namespace ARDOUR;
59 using namespace PBD;
60 using namespace Gtk;
61 using namespace Glib;
62 using namespace Gtkmm2ext;
63 using namespace Editing;
64 
65 struct TrackViewByPositionSorter
66 {
operator ()TrackViewByPositionSorter67 	bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
68 		return a->y_position() < b->y_position();
69 	}
70 };
71 
72 bool
extend_selection_to_track(TimeAxisView & view)73 Editor::extend_selection_to_track (TimeAxisView& view)
74 {
75 	if (selection->selected (&view)) {
76 		/* already selected, do nothing */
77 		return false;
78 	}
79 
80 	if (selection->tracks.empty()) {
81 
82 		if (!selection->selected (&view)) {
83 			selection->set (&view);
84 			return true;
85 		} else {
86 			return false;
87 		}
88 	}
89 
90 	/* something is already selected, so figure out which range of things to add */
91 
92 	TrackViewList to_be_added;
93 	TrackViewList sorted = track_views;
94 	TrackViewByPositionSorter cmp;
95 	bool passed_clicked = false;
96 	bool forwards = true;
97 
98 	sorted.sort (cmp);
99 
100 	/* figure out if we should go forward or backwards */
101 
102 	for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
103 
104 		if ((*i) == &view) {
105 			passed_clicked = true;
106 		}
107 
108 		if (selection->selected (*i)) {
109 			if (passed_clicked) {
110 				forwards = true;
111 			} else {
112 				forwards = false;
113 			}
114 			break;
115 		}
116 	}
117 
118 	passed_clicked = false;
119 
120 	if (forwards) {
121 
122 		for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
123 
124 			if ((*i) == &view) {
125 				passed_clicked = true;
126 				continue;
127 			}
128 
129 			if (passed_clicked) {
130 				if ((*i)->hidden()) {
131 					continue;
132 				}
133 				if (selection->selected (*i)) {
134 					break;
135 				} else if (!(*i)->hidden()) {
136 					to_be_added.push_back (*i);
137 				}
138 			}
139 		}
140 
141 	} else {
142 
143 		for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
144 
145 			if ((*r) == &view) {
146 				passed_clicked = true;
147 				continue;
148 			}
149 
150 			if (passed_clicked) {
151 
152 				if ((*r)->hidden()) {
153 					continue;
154 				}
155 
156 				if (selection->selected (*r)) {
157 					break;
158 				} else if (!(*r)->hidden()) {
159 					to_be_added.push_back (*r);
160 				}
161 			}
162 		}
163 	}
164 
165 	if (!selection->selected (&view)) {
166 		to_be_added.push_back (&view);
167 	}
168 
169 	if (!to_be_added.empty()) {
170 		selection->add (to_be_added);
171 		return true;
172 	}
173 
174 	return false;
175 }
176 
177 void
select_all_tracks()178 Editor::select_all_tracks ()
179 {
180 	TrackViewList tracks;
181 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
182 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i);
183 		if ( rtv && rtv->route()->is_track() ) {
184 			tracks.push_back (*i);
185 		}
186 	}
187 	PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
188 	selection->set (tracks);
189 }
190 
191 void
select_all_visible_lanes()192 Editor::select_all_visible_lanes ()
193 {
194 	TrackViewList visible_views;
195 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
196 		if ((*i)->marked_for_display()) {
197 			visible_views.push_back (*i);
198 		}
199 	}
200 	PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
201 	selection->set (visible_views);
202 }
203 
204 /** Select clicked_axisview, unless there are no currently selected
205  *  tracks, in which case nothing will happen unless `force' is true.
206  */
207 void
set_selected_track_as_side_effect(Selection::Operation op)208 Editor::set_selected_track_as_side_effect (Selection::Operation op)
209 {
210 	if (!clicked_axisview) {
211 		return;
212 	}
213 
214 	PBD::Unwinder<bool> uw (_editor_track_selection_change_without_scroll, true);
215 
216 	RouteGroup* group = NULL;
217 	if (clicked_routeview) {
218 		group = clicked_routeview->route()->route_group();
219 	}
220 
221 	switch (op) {
222 	case Selection::Toggle:
223 		if (selection->selected (clicked_axisview)) {
224 			if (group && group->is_active() && group->enabled_property(ARDOUR::Properties::group_select.property_id)) {
225 				for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
226 					if ((*i)->route_group() == group) {
227 						selection->remove(*i);
228 					}
229 				}
230 			} else {
231 				selection->remove (clicked_axisview);
232 			}
233 		} else {
234 			if (group && group->is_active() && group->enabled_property(ARDOUR::Properties::group_select.property_id)) {
235 				for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
236 					if ((*i)->route_group() == group) {
237 						selection->add(*i);
238 					}
239 				}
240 			} else {
241 				selection->add (clicked_axisview);
242 			}
243 		}
244 		break;
245 
246 	case Selection::Add:
247 		if (group && group->is_active() && group->enabled_property(ARDOUR::Properties::group_select.property_id)) {
248 			for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
249 				if ((*i)->route_group() == group) {
250 					selection->add(*i);
251 				}
252 			}
253 		} else {
254 			selection->add (clicked_axisview);
255 		}
256 		break;
257 
258 	case Selection::Set:
259 		selection->clear();
260 		if (group && group->is_active() && group->enabled_property(ARDOUR::Properties::group_select.property_id)) {
261 			for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
262 				if ((*i)->route_group() == group) {
263 					selection->add(*i);
264 				}
265 			}
266 		} else {
267 			selection->set (clicked_axisview);
268 		}
269 		break;
270 
271 	case Selection::Extend:
272 		selection->clear();
273 		break;
274 	}
275 }
276 
277 void
set_selected_track(TimeAxisView & view,Selection::Operation op,bool no_remove)278 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
279 {
280 	begin_reversible_selection_op (X_("Set Selected Track"));
281 
282 	switch (op) {
283 	case Selection::Toggle:
284 		if (selection->selected (&view)) {
285 			if (!no_remove) {
286 				selection->remove (&view);
287 			}
288 		} else {
289 			selection->add (&view);
290 		}
291 		break;
292 
293 	case Selection::Add:
294 		selection->add (&view);
295 		break;
296 
297 	case Selection::Set:
298 		selection->set (&view);
299 		break;
300 
301 	case Selection::Extend:
302 		extend_selection_to_track (view);
303 		break;
304 	}
305 
306 	commit_reversible_selection_op ();
307 }
308 
309 void
set_selected_track_from_click(bool press,Selection::Operation op,bool no_remove)310 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
311 {
312 	if (!clicked_routeview) {
313 		return;
314 	}
315 
316 	if (!press) {
317 		return;
318 	}
319 
320 	set_selected_track (*clicked_routeview, op, no_remove);
321 }
322 
323 bool
set_selected_control_point_from_click(bool press,Selection::Operation op)324 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
325 {
326 	if (!clicked_control_point) {
327 		return false;
328 	}
329 
330 	bool ret = false;
331 
332 	switch (op) {
333 	case Selection::Set:
334 		if (!selection->selected (clicked_control_point)) {
335 			selection->set (clicked_control_point);
336 			ret = true;
337 		} else {
338 			/* clicked on an already selected point */
339 			if (press) {
340 				break;
341 			} else {
342 				if (selection->points.size() > 1) {
343 					selection->set (clicked_control_point);
344 					ret = true;
345 				}
346 			}
347 		}
348 		break;
349 
350 	case Selection::Add:
351 		if (press) {
352 			selection->add (clicked_control_point);
353 			ret = true;
354 		}
355 		break;
356 	case Selection::Toggle:
357 
358 		/* This is a bit of a hack; if we Primary-Click-Drag a control
359 		   point (for push drag) we want the point we clicked on to be
360 		   selected, otherwise we end up confusingly dragging an
361 		   unselected point.  So here we ensure that the point is selected
362 		   after the press, and if we subsequently get a release (meaning no
363 		   drag occurred) we set things up so that the toggle has happened.
364 		*/
365 		if (press && !selection->selected (clicked_control_point)) {
366 			/* This is the button press, and the control point is not selected; make it so,
367 			   in case this press leads to a drag.  Also note that having done this, we don't
368 			   need to toggle again on release.
369 			*/
370 			selection->toggle (clicked_control_point);
371 			_control_point_toggled_on_press = true;
372 			ret = true;
373 		} else if (!press && !_control_point_toggled_on_press) {
374 			/* This is the release, and the point wasn't toggled on the press, so do it now */
375 			selection->toggle (clicked_control_point);
376 			ret = true;
377 		} else {
378 			/* Reset our flag */
379 			_control_point_toggled_on_press = false;
380 		}
381 		break;
382 	case Selection::Extend:
383 		/* XXX */
384 		break;
385 	}
386 
387 	return ret;
388 }
389 
390 void
get_onscreen_tracks(TrackViewList & tvl)391 Editor::get_onscreen_tracks (TrackViewList& tvl)
392 {
393 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
394 		if ((*i)->y_position() < _visible_canvas_height) {
395 			tvl.push_back (*i);
396 		}
397 	}
398 }
399 
400 /** Call a slot for a given `basis' track and also for any track that is in the same
401  *  active route group with a particular set of properties.
402  *
403  *  @param sl Slot to call.
404  *  @param basis Basis track.
405  *  @param prop Properties that active edit groups must share to be included in the map.
406  */
407 
408 void
mapover_grouped_routes(sigc::slot<void,RouteUI &> sl,RouteUI * basis,PBD::PropertyID prop) const409 Editor::mapover_grouped_routes (sigc::slot<void, RouteUI&> sl, RouteUI* basis, PBD::PropertyID prop) const
410 {
411 	set<RouteUI*> routes;
412 
413 	routes.insert(basis);
414 
415 	RouteGroup* group = basis->route()->route_group();
416 
417 	if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
418 
419 		/* the basis is a member of an active route group, with the appropriate
420 		 * properties; find other members */
421 
422 		for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
423 			RouteUI* v = dynamic_cast<RouteUI*> (*i);
424 			if ( v && (v->route() != basis->route()) && v->route()->route_group() == group) {
425 				routes.insert (v);
426 			}
427 		}
428 	}
429 
430 	/* call the slots */
431 	for (set<RouteUI*>::iterator i = routes.begin(); i != routes.end(); ++i) {
432 		sl (**i);
433 	}
434 }
435 
436 void
mapover_armed_routes(sigc::slot<void,RouteUI &> sl) const437 Editor::mapover_armed_routes (sigc::slot<void, RouteUI&> sl) const
438 {
439 	set<RouteUI*> routes;
440 	for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
441 		RouteUI* v = dynamic_cast<RouteUI*> (*i);
442 		if (v && v->route()->is_track()) {
443 			if ( v->track()->rec_enable_control()->get_value()) {
444 				routes.insert (v);
445 			}
446 		}
447 	}
448 	for (set<RouteUI*>::iterator i = routes.begin(); i != routes.end(); ++i) {
449 		sl (**i);
450 	}
451 }
452 
453 void
mapover_selected_routes(sigc::slot<void,RouteUI &> sl) const454 Editor::mapover_selected_routes (sigc::slot<void, RouteUI&> sl) const
455 {
456 	set<RouteUI*> routes;
457 	for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
458 		RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
459 		if (r) {
460 			routes.insert (r);
461 		}
462 	}
463 	for (set<RouteUI*>::iterator i = routes.begin(); i != routes.end(); ++i) {
464 		sl (**i);
465 	}
466 }
467 
468 void
mapover_all_routes(sigc::slot<void,RouteUI &> sl) const469 Editor::mapover_all_routes (sigc::slot<void, RouteUI&> sl) const
470 {
471 	set<RouteUI*> routes;
472 	for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
473 		RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
474 		if (r) {
475 			routes.insert (r);
476 		}
477 	}
478 	for (set<RouteUI*>::iterator i = routes.begin(); i != routes.end(); ++i) {
479 		sl (**i);
480 	}
481 }
482 
483 /** Call a slot for a given `basis' track and also for any track that is in the same
484  *  active route group with a particular set of properties.
485  *
486  *  @param sl Slot to call.
487  *  @param basis Basis track.
488  *  @param prop Properties that active edit groups must share to be included in the map.
489  */
490 
491 void
mapover_tracks_with_unique_playlists(sigc::slot<void,RouteTimeAxisView &,uint32_t> sl,TimeAxisView * basis,PBD::PropertyID prop) const492 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
493 {
494 	RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
495 	set<boost::shared_ptr<Playlist> > playlists;
496 
497 	if (route_basis == 0) {
498 		return;
499 	}
500 
501 	set<RouteTimeAxisView*> tracks;
502 	tracks.insert (route_basis);
503 
504 	RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
505 
506 	if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
507 
508 		/* the basis is a member of an active route group, with the appropriate
509 		   properties; find other members */
510 
511 		for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
512 			RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
513 
514 			if (v && v->route()->route_group() == group) {
515 
516 				boost::shared_ptr<Track> t = v->track();
517 				if (t) {
518 					if (playlists.insert (t->playlist()).second) {
519 						/* haven't seen this playlist yet */
520 						tracks.insert (v);
521 					}
522 				} else {
523 					/* not actually a "Track", but a timeaxis view that
524 					   we should mapover anyway.
525 					*/
526 					tracks.insert (v);
527 				}
528 			}
529 		}
530 	}
531 
532 	/* call the slots */
533 	uint32_t const sz = tracks.size ();
534 
535 	for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
536 		sl (**i, sz);
537 	}
538 }
539 
540 void
mapped_get_equivalent_regions(RouteTimeAxisView & tv,uint32_t,RegionView * basis,vector<RegionView * > * all_equivs) const541 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
542 {
543 	boost::shared_ptr<Playlist> pl;
544 	vector<boost::shared_ptr<Region> > results;
545 	RegionView* marv;
546 	boost::shared_ptr<Track> tr;
547 
548 	if ((tr = tv.track()) == 0) {
549 		/* bus */
550 		return;
551 	}
552 
553 	if (&tv == &basis->get_time_axis_view()) {
554 		/* looking in same track as the original */
555 		return;
556 	}
557 
558 	if ((pl = tr->playlist()) != 0) {
559 		pl->get_equivalent_regions (basis->region(), results);
560 	}
561 
562 	for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
563 		if ((marv = tv.view()->find_view (*ir)) != 0) {
564 			all_equivs->push_back (marv);
565 		}
566 	}
567 }
568 
569 void
get_equivalent_regions(RegionView * basis,vector<RegionView * > & equivalent_regions,PBD::PropertyID property) const570 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
571 {
572 	mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
573 
574 	/* add clicked regionview since we skipped all other regions in the same track as the one it was in */
575 
576 	equivalent_regions.push_back (basis);
577 }
578 
579 RegionSelection
get_equivalent_regions(RegionSelection & basis,PBD::PropertyID prop) const580 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
581 {
582 	RegionSelection equivalent;
583 
584 	for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
585 
586 		vector<RegionView*> eq;
587 
588 		mapover_tracks_with_unique_playlists (
589 			sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
590 			&(*i)->get_time_axis_view(), prop);
591 
592 		for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
593 			equivalent.add (*j);
594 		}
595 
596 		equivalent.add (*i);
597 	}
598 
599 	return equivalent;
600 }
601 
602 bool
set_selected_regionview_from_click(bool press,Selection::Operation op)603 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
604 {
605 	vector<RegionView*> all_equivalent_regions;
606 	bool commit = false;
607 
608 	if (!clicked_regionview || !clicked_routeview) {
609 		return false;
610 	}
611 
612 	if (press) {
613 		button_release_can_deselect = false;
614 	}
615 
616 	if (op == Selection::Toggle || op == Selection::Set) {
617 
618 		switch (op) {
619 		case Selection::Toggle:
620 			if (selection->selected (clicked_regionview)) {
621 				if (press) {
622 
623 					/* whatever was clicked was selected already; do nothing here but allow
624 					   the button release to deselect it
625 					*/
626 
627 					button_release_can_deselect = true;
628 
629 				} else {
630 					if (button_release_can_deselect) {
631 
632 						/* just remove this one region, but only on a permitted button release */
633 
634 						selection->remove (clicked_regionview);
635 						commit = true;
636 
637 						/* no more deselect action on button release till a new press
638 						   finds an already selected object.
639 						*/
640 
641 						button_release_can_deselect = false;
642 					}
643 				}
644 
645 			} else {
646 
647 				if (press) {
648 
649 					if (selection->selected (clicked_routeview)) {
650 						get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
651 					} else {
652 						all_equivalent_regions.push_back (clicked_regionview);
653 					}
654 
655 					/* add all the equivalent regions, but only on button press */
656 
657 					if (!all_equivalent_regions.empty()) {
658 						commit = true;
659 					}
660 
661 					selection->add (all_equivalent_regions);
662 				}
663 			}
664 			break;
665 
666 		case Selection::Set:
667 			if (!selection->selected (clicked_regionview)) {
668 				get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
669 				selection->set (all_equivalent_regions);
670 				commit = true;
671 			} else {
672 				/* clicked on an already selected region */
673 				if (press)
674 					goto out;
675 				else {
676 					if (selection->regions.size() > 1) {
677 						/* collapse region selection down to just this one region (and its equivalents) */
678 						get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
679 						selection->set(all_equivalent_regions);
680 						commit = true;
681 					}
682 				}
683 			}
684 			break;
685 
686 		default:
687 			/* silly compiler */
688 			break;
689 		}
690 
691 	} else if (op == Selection::Extend) {
692 
693 		list<Selectable*> results;
694 		samplepos_t last_sample;
695 		samplepos_t first_sample;
696 		bool same_track = false;
697 
698 		/* 1. find the last selected regionview in the track that was clicked in */
699 
700 		last_sample = 0;
701 		first_sample = max_samplepos;
702 
703 		for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
704 			if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
705 
706 				if ((*x)->region()->last_sample() > last_sample) {
707 					last_sample = (*x)->region()->last_sample();
708 				}
709 
710 				if ((*x)->region()->first_sample() < first_sample) {
711 					first_sample = (*x)->region()->first_sample();
712 				}
713 
714 				same_track = true;
715 			}
716 		}
717 
718 		if (same_track) {
719 
720 			/* 2. figure out the boundaries for our search for new objects */
721 
722 			switch (clicked_regionview->region()->coverage (first_sample, last_sample)) {
723 			case Evoral::OverlapNone:
724 				if (last_sample < clicked_regionview->region()->first_sample()) {
725 					first_sample = last_sample;
726 					last_sample = clicked_regionview->region()->last_sample();
727 				} else {
728 					last_sample = first_sample;
729 					first_sample = clicked_regionview->region()->first_sample();
730 				}
731 				break;
732 
733 			case Evoral::OverlapExternal:
734 				if (last_sample < clicked_regionview->region()->first_sample()) {
735 					first_sample = last_sample;
736 					last_sample = clicked_regionview->region()->last_sample();
737 				} else {
738 					last_sample = first_sample;
739 					first_sample = clicked_regionview->region()->first_sample();
740 				}
741 				break;
742 
743 			case Evoral::OverlapInternal:
744 				if (last_sample < clicked_regionview->region()->first_sample()) {
745 					first_sample = last_sample;
746 					last_sample = clicked_regionview->region()->last_sample();
747 				} else {
748 					last_sample = first_sample;
749 					first_sample = clicked_regionview->region()->first_sample();
750 				}
751 				break;
752 
753 			case Evoral::OverlapStart:
754 			case Evoral::OverlapEnd:
755 				/* nothing to do except add clicked region to selection, since it
756 				   overlaps with the existing selection in this track.
757 				*/
758 				break;
759 			}
760 
761 		} else {
762 
763 			/* click in a track that has no regions selected, so extend vertically
764 			   to pick out all regions that are defined by the existing selection
765 			   plus this one.
766 			*/
767 
768 
769 			first_sample = clicked_regionview->region()->position();
770 			last_sample = clicked_regionview->region()->last_sample();
771 
772 			for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
773 				if ((*i)->region()->position() < first_sample) {
774 					first_sample = (*i)->region()->position();
775 				}
776 				if ((*i)->region()->last_sample() + 1 > last_sample) {
777 					last_sample = (*i)->region()->last_sample();
778 				}
779 			}
780 		}
781 
782 		/* 2. find all the tracks we should select in */
783 
784 		set<RouteTimeAxisView*> relevant_tracks;
785 
786 		for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
787 			RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
788 			if (r) {
789 				relevant_tracks.insert (r);
790 			}
791 		}
792 
793 		set<RouteTimeAxisView*> already_in_selection;
794 
795 		if (relevant_tracks.empty()) {
796 
797 			/* no tracks selected .. thus .. if the
798 			   regionview we're in isn't selected
799 			   (i.e. we're about to extend to it), then
800 			   find all tracks between the this one and
801 			   any selected ones.
802 			*/
803 
804 			if (!selection->selected (clicked_regionview)) {
805 
806 				RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
807 
808 				if (rtv) {
809 
810 					/* add this track to the ones we will search */
811 
812 					relevant_tracks.insert (rtv);
813 
814 					/* find the track closest to this one that
815 					   already a selected region.
816 					*/
817 
818 					RouteTimeAxisView* closest = 0;
819 					int distance = INT_MAX;
820 					int key = rtv->route()->presentation_info().order ();
821 
822 					for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
823 
824 						RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
825 
826 						if (artv && artv != rtv) {
827 
828 							pair<set<RouteTimeAxisView*>::iterator,bool> result;
829 
830 							result = already_in_selection.insert (artv);
831 
832 							if (result.second) {
833 								/* newly added to already_in_selection */
834 
835 								int d = artv->route()->presentation_info().order ();
836 
837 								d -= key;
838 
839 								if (abs (d) < distance) {
840 									distance = abs (d);
841 									closest = artv;
842 								}
843 							}
844 						}
845 					}
846 
847 					if (closest) {
848 
849 						/* now add all tracks between that one and this one */
850 
851 						int okey = closest->route()->presentation_info().order ();
852 
853 						if (okey > key) {
854 							swap (okey, key);
855 						}
856 
857 						for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
858 							RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
859 							if (artv && artv != rtv) {
860 
861 								int k = artv->route()->presentation_info().order ();
862 
863 								if (k >= okey && k <= key) {
864 
865 									/* in range but don't add it if
866 									   it already has tracks selected.
867 									   this avoids odd selection
868 									   behaviour that feels wrong.
869 									*/
870 
871 									if (find (already_in_selection.begin(),
872 									          already_in_selection.end(),
873 									          artv) == already_in_selection.end()) {
874 
875 										relevant_tracks.insert (artv);
876 									}
877 								}
878 							}
879 						}
880 					}
881 				}
882 			}
883 		}
884 
885 		/* 3. find all selectable objects (regionviews in this case) between that one and the end of the
886 		   one that was clicked.
887 		*/
888 
889 		for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
890 			(*t)->get_selectables (first_sample, last_sample, -1.0, -1.0, results);
891 		}
892 
893 		/* 4. convert to a vector of regions */
894 
895 		vector<RegionView*> regions;
896 
897 		for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
898 			RegionView* arv;
899 
900 			if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
901 				regions.push_back (arv);
902 			}
903 		}
904 
905 		if (!regions.empty()) {
906 			selection->add (regions);
907 			commit = true;
908 		} else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
909 			/* ensure that at least the clicked regionview is selected. */
910 			selection->set (clicked_regionview);
911 			commit = true;
912 		}
913 
914 	}
915 
916 out:
917 	return commit;
918 }
919 
920 void
set_selected_midi_region_view(MidiRegionView & mrv)921 Editor::set_selected_midi_region_view (MidiRegionView& mrv)
922 {
923 	/* clear note selection in all currently selected MidiRegionViews */
924 
925 	if (get_selection().regions.contains (&mrv) && get_selection().regions.size() == 1) {
926 		/* Nothing to do */
927 		return;
928 	}
929 
930 	midi_action (&MidiRegionView::clear_note_selection);
931 	get_selection().set (&mrv);
932 }
933 
934 void
set_selection(std::list<Selectable * > s,Selection::Operation op)935 Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
936 {
937 	if (s.empty()) {
938 		return;
939 	}
940 	begin_reversible_selection_op (X_("set selection"));
941 	switch (op) {
942 		case Selection::Toggle:
943 			selection->toggle (s);
944 			break;
945 		case Selection::Set:
946 			selection->set (s);
947 			break;
948 		case Selection::Extend:
949 			selection->add (s);
950 			break;
951 		case Selection::Add:
952 			selection->add (s);
953 			break;
954 	}
955 
956 	commit_reversible_selection_op () ;
957 }
958 
959 void
set_selected_regionview_from_region_list(boost::shared_ptr<Region> region,Selection::Operation op)960 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
961 {
962 	vector<RegionView*> regionviews;
963 
964 	get_regionview_corresponding_to (region, regionviews);
965 
966 	if (regionviews.empty()) {
967 		return;
968 	}
969 
970 	begin_reversible_selection_op (X_("set selected regions"));
971 
972 	switch (op) {
973 	case Selection::Toggle:
974 		/* XXX this is not correct */
975 		selection->toggle (regionviews);
976 		break;
977 	case Selection::Set:
978 		selection->set (regionviews);
979 		break;
980 	case Selection::Extend:
981 		selection->add (regionviews);
982 		break;
983 	case Selection::Add:
984 		selection->add (regionviews);
985 		break;
986 	}
987 
988 	commit_reversible_selection_op () ;
989 }
990 
991 bool
set_selected_regionview_from_map_event(GdkEventAny *,StreamView * sv,boost::weak_ptr<Region> weak_r)992 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
993 {
994 	RegionView* rv;
995 	boost::shared_ptr<Region> r (weak_r.lock());
996 
997 	if (!r) {
998 		return true;
999 	}
1000 
1001 	if ((rv = sv->find_view (r)) == 0) {
1002 		return true;
1003 	}
1004 
1005 	/* don't reset the selection if its something other than
1006 	   a single other region.
1007 	*/
1008 
1009 	if (selection->regions.size() > 1) {
1010 		return true;
1011 	}
1012 
1013 	begin_reversible_selection_op (X_("set selected regions"));
1014 
1015 	selection->set (rv);
1016 
1017 	commit_reversible_selection_op () ;
1018 
1019 	return true;
1020 }
1021 
1022 void
presentation_info_changed(PropertyChange const & what_changed)1023 Editor::presentation_info_changed (PropertyChange const & what_changed)
1024 {
1025 	uint32_t n_tracks = 0;
1026 	uint32_t n_busses = 0;
1027 	uint32_t n_vcas = 0;
1028 	uint32_t n_routes = 0;
1029 	uint32_t n_stripables = 0;
1030 
1031 	/* We cannot ensure ordering of the handlers for
1032 	 * PresentationInfo::Changed, so we have to do everything in order
1033 	 * here, as a single handler.
1034 	 */
1035 
1036 	if (what_changed.contains (Properties::selected)) {
1037 		for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1038 			(*i)->set_selected (false);
1039 			(*i)->hide_selection ();
1040 		}
1041 	}
1042 
1043 	/* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1044 	 * currently selected stripable/controllable duples are found and added
1045 	 */
1046 
1047 	selection->core_selection_changed (what_changed);
1048 
1049 	/* STEP 2: update TimeAxisView's knowledge of their selected state
1050 	 */
1051 
1052 	if (what_changed.contains (Properties::selected)) {
1053 
1054 		StripableNotificationListPtr stripables (new StripableNotificationList);
1055 
1056 		switch (selection->tracks.size()) {
1057 		case 0:
1058 			break;
1059 		default:
1060 			set_selected_mixer_strip (*(selection->tracks.back()));
1061 			if (!_track_selection_change_without_scroll && !_editor_track_selection_change_without_scroll) {
1062 				ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1063 			}
1064 			break;
1065 		}
1066 
1067 		CoreSelection::StripableAutomationControls sc;
1068 		_session->selection().get_stripables (sc);
1069 
1070 		for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1071 
1072 			AxisView* av = axis_view_by_stripable ((*i).stripable);
1073 
1074 			if (!av) {
1075 				continue;
1076 			}
1077 
1078 			n_stripables++;
1079 
1080 			if (boost::dynamic_pointer_cast<Track> ((*i).stripable)) {
1081 				n_tracks++;
1082 				n_routes++;
1083 			} else if (boost::dynamic_pointer_cast<Route> ((*i).stripable)) {
1084 				n_busses++;
1085 				n_routes++;
1086 			} else if (boost::dynamic_pointer_cast<VCA> ((*i).stripable)) {
1087 				n_vcas++;
1088 			}
1089 
1090 			TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1091 
1092 			if (!tav) {
1093 				assert (0);
1094 				continue; /* impossible */
1095 			}
1096 
1097 			if (!(*i).controllable) {
1098 
1099 				/* "parent" track selected */
1100 				tav->set_selected (true);
1101 				tav->reshow_selection (selection->time);
1102 
1103 			} else {
1104 
1105 				/* possibly a child */
1106 
1107 				TimeAxisView::Children c = tav->get_child_list ();
1108 
1109 				for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1110 
1111 					boost::shared_ptr<AutomationControl> control = (*j)->control ();
1112 
1113 					if (control != (*i).controllable) {
1114 						continue;
1115 					}
1116 
1117 					(*j)->set_selected (true);
1118 					(*j)->reshow_selection (selection->time);
1119 				}
1120 			}
1121 
1122 			stripables->push_back ((*i).stripable);
1123 		}
1124 
1125 		ActionManager::set_sensitive (ActionManager::stripable_selection_sensitive_actions, (n_stripables > 0));
1126 		ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, (n_tracks > 0));
1127 		ActionManager::set_sensitive (ActionManager::bus_selection_sensitive_actions, (n_busses > 0));
1128 		ActionManager::set_sensitive (ActionManager::route_selection_sensitive_actions, (n_routes > 0));
1129 		ActionManager::set_sensitive (ActionManager::vca_selection_sensitive_actions, (n_vcas > 0));
1130 
1131 		sensitize_the_right_region_actions (false);
1132 
1133 		/* STEP 4: notify control protocols */
1134 
1135 		ControlProtocolManager::instance().stripable_selection_changed (stripables);
1136 
1137 		if (sfbrowser && _session && !_session->deletion_in_progress()) {
1138 			uint32_t audio_track_cnt = 0;
1139 			uint32_t midi_track_cnt = 0;
1140 
1141 			for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1142 				AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1143 
1144 				if (atv) {
1145 					if (atv->is_audio_track()) {
1146 						audio_track_cnt++;
1147 					}
1148 
1149 				} else {
1150 					MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1151 
1152 					if (mtv) {
1153 						if (mtv->is_midi_track()) {
1154 							midi_track_cnt++;
1155 						}
1156 					}
1157 				}
1158 			}
1159 
1160 			sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1161 		}
1162 	}
1163 
1164 	/* STEP 4: update EditorRoutes treeview */
1165 
1166 	PropertyChange soh;
1167 
1168 	soh.add (Properties::selected);
1169 	soh.add (Properties::order);
1170 	soh.add (Properties::hidden);
1171 
1172 	if (what_changed.contains (soh)) {
1173 		_routes->sync_treeview_from_presentation_info (what_changed);
1174 	}
1175 }
1176 
1177 void
track_selection_changed()1178 Editor::track_selection_changed ()
1179 {
1180 	/* reset paste count, so the plaste location doesn't get incremented
1181 	 * if we want to paste in the same place, but different track. */
1182 	paste_count = 0;
1183 
1184 	if ( _session->solo_selection_active() )
1185 		play_solo_selection(false);
1186 }
1187 
1188 void
time_selection_changed()1189 Editor::time_selection_changed ()
1190 {
1191 	/* XXX this is superficially inefficient. Hide the selection in all
1192 	 * tracks, then show it in all selected tracks.
1193 	 *
1194 	 * However, if you investigate what this actually does, it isn't
1195 	 * anywhere nearly as bad as it may appear. Remember: nothing is
1196 	 * redrawn or even recomputed during these two loops - that only
1197 	 * happens when we next render ...
1198 	 */
1199 
1200 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1201 		(*i)->hide_selection ();
1202 	}
1203 
1204 	for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1205 		(*i)->show_selection (selection->time);
1206 	}
1207 
1208 	if (selection->time.empty()) {
1209 		ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1210 	} else {
1211 		ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1212 	}
1213 
1214 	/* propagate into backend, but only when there is no drag or we are at
1215 	 * the end of a drag, otherwise this is too expensive (could case a
1216 	 * locate per mouse motion event.
1217 	 */
1218 
1219 	if (_session && !_drags->active()) {
1220 		if (selection->time.length() != 0) {
1221 			_session->set_range_selection (selection->time.start(), selection->time.end_sample());
1222 		} else {
1223 			_session->clear_range_selection ();
1224 		}
1225 	}
1226 }
1227 
1228 /** Set all region actions to have a given sensitivity */
1229 void
sensitize_all_region_actions(bool s)1230 Editor::sensitize_all_region_actions (bool s)
1231 {
1232 	Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1233 
1234 	for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1235 		(*i)->set_sensitive (s);
1236 	}
1237 
1238 	_all_region_actions_sensitized = s;
1239 }
1240 
1241 /** Sensitize region-based actions.
1242  *
1243  *  This method is called from whenever we leave the canvas, either by moving
1244  *  the pointer out of it, or by popping up a context menu. See
1245  *  Editor::{entered,left}_track_canvas() for details there.
1246  */
1247 void
sensitize_the_right_region_actions(bool because_canvas_crossing)1248 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1249 {
1250 	bool have_selection = false;
1251 	bool have_entered = false;
1252 	bool have_edit_point = false;
1253 	bool have_selected_source = false;
1254 	RegionSelection rs;
1255 
1256 	// std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1257 	// << std::endl;
1258 
1259 	if (!selection->regions.empty()) {
1260 		have_selection = true;
1261 		rs = selection->regions;
1262 	}
1263 
1264 	if (entered_regionview) {
1265 		have_entered = true;
1266 		rs.add (entered_regionview);
1267 	}
1268 
1269 	if ( _sources->get_single_selection() ) {
1270 		have_selected_source = true;
1271 	}
1272 
1273 	if (rs.empty() && !selection->tracks.empty()) {
1274 
1275 		/* no selected regions, but some selected tracks.
1276 		 */
1277 
1278 		if (_edit_point == EditAtMouse) {
1279 			if (!within_track_canvas) {
1280 				/* pointer is not in canvas, so edit point is meaningless */
1281 				have_edit_point = false;
1282 			} else {
1283 				/* inside canvas. we don't know where the edit
1284 				   point will be when an action is invoked, but
1285 				   assume it could intersect with a region.
1286 				*/
1287 				have_edit_point = true;
1288 			}
1289 		} else {
1290 			RegionSelection at_edit_point;
1291 			samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1292 			get_regions_at (at_edit_point, where, selection->tracks);
1293 			if (!at_edit_point.empty()) {
1294 				have_edit_point = true;
1295 			}
1296 			if (rs.empty()) {
1297 				rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1298 			}
1299 		}
1300 	}
1301 
1302 	//std::cerr << "\tfinal have selection: " << have_selection
1303 	// << " have entered " << have_entered
1304 	// << " have edit point " << have_edit_point
1305 	// << " EP = " << enum_2_string (_edit_point)
1306 	// << std::endl;
1307 
1308 	typedef std::map<std::string,RegionAction> RegionActionMap;
1309 
1310 	_ignore_region_action = true;
1311 
1312 	for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1313 		RegionActionTarget tgt = x->second.target;
1314 		bool sensitive = false;
1315 
1316 		if ((tgt & SelectedRegions) && have_selection) {
1317 			sensitive = true;
1318 		} else if ((tgt & EnteredRegions) && have_entered) {
1319 			sensitive = true;
1320 		} else if ((tgt & EditPointRegions) && have_edit_point) {
1321 			sensitive = true;
1322 		} else if ((tgt & ListSelection) && have_selected_source ) {
1323 			sensitive = true;
1324 		}
1325 
1326 		x->second.action->set_sensitive (sensitive);
1327 	}
1328 
1329 	/* Look through the regions that are selected and make notes about what we have got */
1330 
1331 	bool have_audio = false;
1332 	bool have_multichannel_audio = false;
1333 	bool have_midi = false;
1334 	bool have_locked = false;
1335 	bool have_unlocked = false;
1336 	bool have_video_locked = false;
1337 	bool have_video_unlocked = false;
1338 	bool have_position_lock_style_audio = false;
1339 	bool have_position_lock_style_music = false;
1340 	bool have_muted = false;
1341 	bool have_unmuted = false;
1342 	bool have_opaque = false;
1343 	bool have_non_opaque = false;
1344 	bool have_not_at_natural_position = false;
1345 	bool have_envelope_active = false;
1346 	bool have_envelope_inactive = false;
1347 	bool have_non_unity_scale_amplitude = false;
1348 	bool have_compound_regions = false;
1349 	bool have_inactive_fade_in = false;
1350 	bool have_inactive_fade_out = false;
1351 	bool have_active_fade_in = false;
1352 	bool have_active_fade_out = false;
1353 	bool have_transients = false;
1354 
1355 	for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1356 
1357 		boost::shared_ptr<Region> r = (*i)->region ();
1358 		boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1359 
1360 		if (ar) {
1361 			have_audio = true;
1362 			if (ar->n_channels() > 1) {
1363 				have_multichannel_audio = true;
1364 			}
1365 		}
1366 
1367 		if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1368 			have_midi = true;
1369 		}
1370 
1371 		if (r->is_compound()) {
1372 			have_compound_regions = true;
1373 		}
1374 
1375 		if (r->locked()) {
1376 			have_locked = true;
1377 		} else {
1378 			have_unlocked = true;
1379 		}
1380 
1381 		if (r->video_locked()) {
1382 			have_video_locked = true;
1383 		} else {
1384 			have_video_unlocked = true;
1385 		}
1386 
1387 		if (r->position_lock_style() == MusicTime) {
1388 			have_position_lock_style_music = true;
1389 		} else {
1390 			have_position_lock_style_audio = true;
1391 		}
1392 
1393 		if (r->muted()) {
1394 			have_muted = true;
1395 		} else {
1396 			have_unmuted = true;
1397 		}
1398 
1399 		if (r->opaque()) {
1400 			have_opaque = true;
1401 		} else {
1402 			have_non_opaque = true;
1403 		}
1404 
1405 		if (!r->at_natural_position()) {
1406 			have_not_at_natural_position = true;
1407 		}
1408 
1409 		if (r->has_transients ()){
1410 			have_transients = true;
1411 		}
1412 
1413 		if (ar) {
1414 			if (ar->envelope_active()) {
1415 				have_envelope_active = true;
1416 			} else {
1417 				have_envelope_inactive = true;
1418 			}
1419 
1420 			if (ar->scale_amplitude() != 1) {
1421 				have_non_unity_scale_amplitude = true;
1422 			}
1423 
1424 			if (ar->fade_in_active ()) {
1425 				have_active_fade_in = true;
1426 			} else {
1427 				have_inactive_fade_in = true;
1428 			}
1429 
1430 			if (ar->fade_out_active ()) {
1431 				have_active_fade_out = true;
1432 			} else {
1433 				have_inactive_fade_out = true;
1434 			}
1435 		}
1436 	}
1437 
1438 	_region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1439 
1440 	if (rs.size() > 1) {
1441 		_region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1442 		_region_actions->get_action("show-region-properties")->set_sensitive (false);
1443 		_region_actions->get_action("rename-region")->set_sensitive (false);
1444 		if (have_audio) {
1445 			/* XXX need to check whether there is than 1 per
1446 			   playlist, because otherwise this makes no sense.
1447 			*/
1448 			_region_actions->get_action("combine-regions")->set_sensitive (true);
1449 		} else {
1450 			_region_actions->get_action("combine-regions")->set_sensitive (false);
1451 		}
1452 	} else if (rs.size() == 1) {
1453 		_region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1454 		_region_actions->get_action("close-region-gaps")->set_sensitive (false);
1455 		_region_actions->get_action("combine-regions")->set_sensitive (false);
1456 	}
1457 
1458 	if (!have_multichannel_audio) {
1459 		_region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1460 	}
1461 
1462 	if (!have_midi) {
1463 		editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1464 		_region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1465 		_region_actions->get_action("quantize-region")->set_sensitive (false);
1466 		_region_actions->get_action("legatize-region")->set_sensitive (false);
1467 		_region_actions->get_action("remove-overlap")->set_sensitive (false);
1468 		_region_actions->get_action("transform-region")->set_sensitive (false);
1469 		_region_actions->get_action("fork-region")->set_sensitive (false);
1470 		_region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1471 		_region_actions->get_action("insert-patch-change")->set_sensitive (false);
1472 		_region_actions->get_action("transpose-region")->set_sensitive (false);
1473 	} else {
1474 		editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1475 		/* others were already marked sensitive */
1476 	}
1477 
1478 	/* ok, moving along... */
1479 
1480 	if (have_compound_regions) {
1481 		_region_actions->get_action("uncombine-regions")->set_sensitive (true);
1482 	} else {
1483 		_region_actions->get_action("uncombine-regions")->set_sensitive (false);
1484 	}
1485 
1486 	if (have_audio) {
1487 
1488 		if (have_envelope_active && !have_envelope_inactive) {
1489 			Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1490 		} else if (have_envelope_active && have_envelope_inactive) {
1491 			// Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1492 		}
1493 
1494 	} else {
1495 
1496 		_region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1497 		_region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1498 		_region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1499 		_region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1500 		_region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1501 		_region_actions->get_action("strip-region-silence")->set_sensitive (false);
1502 		_region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1503 
1504 	}
1505 
1506 	if (!have_non_unity_scale_amplitude || !have_audio) {
1507 		_region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1508 	}
1509 
1510 	Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1511 	a->set_active (have_locked && !have_unlocked);
1512 	if (have_locked && have_unlocked) {
1513 		// a->set_inconsistent ();
1514 	}
1515 
1516 	a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1517 	a->set_active (have_video_locked && !have_video_unlocked);
1518 	if (have_video_locked && have_video_unlocked) {
1519 		// a->set_inconsistent ();
1520 	}
1521 
1522 	a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1523 	a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1524 
1525 	vector<Widget*> proxies = a->get_proxies();
1526 	for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1527 		Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1528 		if (cmi) {
1529 			cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1530 		}
1531 	}
1532 
1533 	a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1534 	a->set_active (have_muted && !have_unmuted);
1535 	if (have_muted && have_unmuted) {
1536 		// a->set_inconsistent ();
1537 	}
1538 
1539 	a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1540 	a->set_active (have_opaque && !have_non_opaque);
1541 	if (have_opaque && have_non_opaque) {
1542 		// a->set_inconsistent ();
1543 	}
1544 
1545 	if (!have_not_at_natural_position) {
1546 		_region_actions->get_action("naturalize-region")->set_sensitive (false);
1547 	}
1548 
1549 	/* Todo: insert-region-from-source-list */
1550 	/* XXX: should also check that there is a track of the appropriate type for the selected region */
1551 #if 0
1552 	if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1553 		_region_actions->get_action("insert-region-from-source-list")->set_sensitive (false);
1554 	} else {
1555 		_region_actions->get_action("insert-region-from-source-list")->set_sensitive (true);
1556 	}
1557 #endif
1558 
1559 	a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1560 	a->set_active (have_active_fade_in && !have_inactive_fade_in);
1561 	if (have_active_fade_in && have_inactive_fade_in) {
1562 		// a->set_inconsistent ();
1563 	}
1564 
1565 	a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1566 	a->set_active (have_active_fade_out && !have_inactive_fade_out);
1567 
1568 	if (have_active_fade_out && have_inactive_fade_out) {
1569 		// a->set_inconsistent ();
1570 	}
1571 
1572 	bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1573 	bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1574 
1575 	a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1576 	a->set_active (have_active_fade && !have_inactive_fade);
1577 
1578 	if (have_active_fade && have_inactive_fade) {
1579 		// a->set_inconsistent ();
1580 	}
1581 
1582 	_ignore_region_action = false;
1583 
1584 	_all_region_actions_sensitized = false;
1585 }
1586 
1587 void
region_selection_changed()1588 Editor::region_selection_changed ()
1589 {
1590 	_regions->block_change_connection (true);
1591 	editor_regions_selection_changed_connection.block(true);
1592 
1593 	if (_region_selection_change_updates_region_list) {
1594 		_regions->unselect_all ();
1595 	}
1596 
1597 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1598 		(*i)->set_selected_regionviews (selection->regions);
1599 	}
1600 
1601 	if (_region_selection_change_updates_region_list) {
1602 		_regions->set_selected (selection->regions);
1603 	}
1604 
1605 	_regions->block_change_connection (false);
1606 	editor_regions_selection_changed_connection.block(false);
1607 
1608 	sensitize_the_right_region_actions (false);
1609 
1610 	/* propagate into backend */
1611 	assert (_session);
1612 
1613 	if (!selection->regions.empty()) {
1614 		_session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
1615 	} else {
1616 		_session->clear_object_selection ();
1617 	}
1618 
1619 	if (_session->solo_selection_active()) {
1620 		play_solo_selection(false);
1621 	}
1622 
1623 	/* set nudge button color */
1624 	if (! get_regions_from_selection_and_entered().empty()) {
1625 		/* nudge regions */
1626 		nudge_forward_button.set_name ("nudge button");
1627 		nudge_backward_button.set_name ("nudge button");
1628 	} else {
1629 		/* nudge marker or playhead */
1630 		nudge_forward_button.set_name ("transport button");
1631 		nudge_backward_button.set_name ("transport button");
1632 	}
1633 
1634 	//there are a few global Editor->Select actions which select regions even if you aren't in Object mode.
1635 	//if regions are selected, we must always force the mouse mode to Object...
1636 	//... otherwise the user is confusingly left with selected regions that can't be manipulated.
1637 	if (!selection->regions.empty() && !internal_editing()) {
1638 
1639 		/* if in MouseAudition and there's just 1 region selected
1640 		 * (i.e. we just clicked on it), leave things as they are
1641 		 */
1642 
1643 		if (selection->regions.size() > 1 || mouse_mode != Editing::MouseAudition) {
1644 			set_mouse_mode (MouseObject, false);
1645 		}
1646 	}
1647 }
1648 
1649 void
point_selection_changed()1650 Editor::point_selection_changed ()
1651 {
1652 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1653 		(*i)->set_selected_points (selection->points);
1654 	}
1655 }
1656 
1657 void
select_all_in_track(Selection::Operation op)1658 Editor::select_all_in_track (Selection::Operation op)
1659 {
1660 	list<Selectable *> touched;
1661 
1662 	if (!clicked_routeview) {
1663 		return;
1664 	}
1665 
1666 	begin_reversible_selection_op (X_("Select All in Track"));
1667 
1668 	clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1669 
1670 	switch (op) {
1671 	case Selection::Toggle:
1672 		selection->add (touched);
1673 		break;
1674 	case Selection::Set:
1675 		selection->set (touched);
1676 		break;
1677 	case Selection::Extend:
1678 		/* meaningless, because we're selecting everything */
1679 		break;
1680 	case Selection::Add:
1681 		selection->add (touched);
1682 		break;
1683 	}
1684 
1685 	commit_reversible_selection_op ();
1686 }
1687 
1688 bool
select_all_internal_edit(Selection::Operation)1689 Editor::select_all_internal_edit (Selection::Operation)
1690 {
1691 	bool selected = false;
1692 
1693 	RegionSelection copy (selection->regions);
1694 
1695 	for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1696 		MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1697 		if (mrv) {
1698 			mrv->select_all_notes ();
1699 			selected = true;
1700 		}
1701 	}
1702 
1703 	MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1704 	if (mrv) {
1705 		mrv->select_all_notes ();
1706 		selected = true;
1707 	}
1708 
1709 	return selected;
1710 }
1711 
1712 void
select_all_objects(Selection::Operation op)1713 Editor::select_all_objects (Selection::Operation op)
1714 {
1715 	list<Selectable *> touched;
1716 
1717 	if (internal_editing() && select_all_internal_edit(op)) {
1718 		return;  // Selected notes
1719 	}
1720 
1721 	TrackViewList ts;
1722 
1723 	if (selection->tracks.empty()) {
1724 		ts = track_views;
1725 	} else {
1726 		ts = selection->tracks;
1727 	}
1728 
1729 	for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1730 		if ((*iter)->hidden()) {
1731 			continue;
1732 		}
1733 		(*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1734 	}
1735 
1736 	begin_reversible_selection_op (X_("select all"));
1737 	switch (op) {
1738 	case Selection::Add:
1739 		selection->add (touched);
1740 		break;
1741 	case Selection::Toggle:
1742 		selection->toggle (touched);
1743 		break;
1744 	case Selection::Set:
1745 		selection->set (touched);
1746 		break;
1747 	case Selection::Extend:
1748 		/* meaningless, because we're selecting everything */
1749 		break;
1750 	}
1751 	commit_reversible_selection_op ();
1752 }
1753 
1754 void
invert_selection_in_track()1755 Editor::invert_selection_in_track ()
1756 {
1757 	list<Selectable *> touched;
1758 
1759 	if (!clicked_routeview) {
1760 		return;
1761 	}
1762 
1763 	begin_reversible_selection_op (X_("Invert Selection in Track"));
1764 	clicked_routeview->get_inverted_selectables (*selection, touched);
1765 	selection->set (touched);
1766 	commit_reversible_selection_op ();
1767 }
1768 
1769 void
invert_selection()1770 Editor::invert_selection ()
1771 {
1772 
1773 	if (internal_editing()) {
1774 		MidiRegionSelection ms = selection->midi_regions();
1775 		for (MidiRegionSelection::iterator i = ms.begin(); i != ms.end(); ++i) {
1776 			MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1777 			if (mrv) {
1778 				mrv->invert_selection ();
1779 			}
1780 		}
1781 		return;
1782 	}
1783 
1784 	if (!selection->tracks.empty()) {
1785 
1786 		TrackViewList inverted;
1787 
1788 		for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1789 			if (!(*iter)->selected()) {
1790 				inverted.push_back (*iter);
1791 			}
1792 		}
1793 
1794 		begin_reversible_selection_op (X_("Invert Track Selection"));
1795 		selection->set (inverted);
1796 		commit_reversible_selection_op ();
1797 
1798 	} else {
1799 
1800 		list<Selectable *> touched;
1801 
1802 		for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1803 			if ((*iter)->hidden()) {
1804 				continue;
1805 			}
1806 			(*iter)->get_inverted_selectables (*selection, touched);
1807 		}
1808 
1809 		begin_reversible_selection_op (X_("Invert ObjectSelection"));
1810 		selection->set (touched);
1811 		commit_reversible_selection_op ();
1812 	}
1813 }
1814 
1815 /** @param start Start time in session samples.
1816  *  @param end End time in session samples.
1817  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1818  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1819  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1820  *  within the region are already selected.
1821  */
1822 void
select_all_within(samplepos_t start,samplepos_t end,double top,double bot,const TrackViewList & tracklist,Selection::Operation op,bool preserve_if_selected)1823 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1824 {
1825 	list<Selectable*> found;
1826 
1827 	for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1828 
1829 		if ((*iter)->hidden()) {
1830 			continue;
1831 		}
1832 
1833 		(*iter)->get_selectables (start, end, top, bot, found);
1834 	}
1835 
1836 	if (found.empty()) {
1837 		selection->clear_objects();
1838 		selection->clear_time ();
1839 		return;
1840 	}
1841 
1842 	if (preserve_if_selected && op != Selection::Toggle) {
1843 		list<Selectable*>::iterator i = found.begin();
1844 		while (i != found.end() && (*i)->selected()) {
1845 			++i;
1846 		}
1847 
1848 		if (i == found.end()) {
1849 			return;
1850 		}
1851 	}
1852 
1853 	begin_reversible_selection_op (X_("select all within"));
1854 	switch (op) {
1855 	case Selection::Add:
1856 		selection->add (found);
1857 		break;
1858 	case Selection::Toggle:
1859 		selection->toggle (found);
1860 		break;
1861 	case Selection::Set:
1862 		selection->set (found);
1863 		break;
1864 	case Selection::Extend:
1865 		/* not defined yet */
1866 		break;
1867 	}
1868 
1869 	commit_reversible_selection_op ();
1870 }
1871 
1872 void
set_selection_from_region()1873 Editor::set_selection_from_region ()
1874 {
1875 	if (selection->regions.empty()) {
1876 		return;
1877 	}
1878 
1879 	/* find all the tracks that have selected regions */
1880 
1881 	set<TimeAxisView*> tracks;
1882 
1883 	for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1884 		tracks.insert (&(*r)->get_time_axis_view());
1885 	}
1886 
1887 	TrackViewList tvl;
1888 	tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1889 
1890 	/* select range (this will clear the region selection) */
1891 
1892 	selection->set (selection->regions.start(), selection->regions.end_sample());
1893 
1894 	/* and select the tracks */
1895 
1896 	selection->set (tvl);
1897 
1898 	if (!get_smart_mode () || !(mouse_mode == Editing::MouseObject) ) {
1899 		set_mouse_mode (Editing::MouseRange, false);
1900 	}
1901 }
1902 
1903 void
set_selection_from_punch()1904 Editor::set_selection_from_punch()
1905 {
1906 	Location* location;
1907 
1908 	if ((location = _session->locations()->auto_punch_location()) == 0)  {
1909 		return;
1910 	}
1911 
1912 	set_selection_from_range (*location);
1913 }
1914 
1915 void
set_selection_from_loop()1916 Editor::set_selection_from_loop()
1917 {
1918 	Location* location;
1919 
1920 	if ((location = _session->locations()->auto_loop_location()) == 0)  {
1921 		return;
1922 	}
1923 	set_selection_from_range (*location);
1924 }
1925 
1926 void
set_selection_from_range(Location & loc)1927 Editor::set_selection_from_range (Location& loc)
1928 {
1929 	begin_reversible_selection_op (X_("set selection from range"));
1930 
1931 	selection->set (loc.start(), loc.end());
1932 
1933 	// if no tracks are selected, enable all tracks
1934 	// (_something_ has to be selected for any range selection, otherwise the user won't see anything)
1935 	if (selection->tracks.empty()) {
1936 		select_all_visible_lanes();
1937 	}
1938 
1939 	commit_reversible_selection_op ();
1940 
1941 	if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1942 		set_mouse_mode (MouseRange, false);
1943 	}
1944 }
1945 
1946 void
select_all_selectables_using_time_selection()1947 Editor::select_all_selectables_using_time_selection ()
1948 {
1949 	list<Selectable *> touched;
1950 
1951 	if (selection->time.empty()) {
1952 		return;
1953 	}
1954 
1955 	samplepos_t start = selection->time[clicked_selection].start;
1956 	samplepos_t end = selection->time[clicked_selection].end;
1957 
1958 	if (end - start < 1)  {
1959 		return;
1960 	}
1961 
1962 	TrackViewList* ts;
1963 
1964 	if (selection->tracks.empty()) {
1965 		ts = &track_views;
1966 	} else {
1967 		ts = &selection->tracks;
1968 	}
1969 
1970 	for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1971 		if ((*iter)->hidden()) {
1972 			continue;
1973 		}
1974 		(*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1975 	}
1976 
1977 	begin_reversible_selection_op (X_("select all from range"));
1978 	selection->set (touched);
1979 	commit_reversible_selection_op ();
1980 }
1981 
1982 
1983 void
select_all_selectables_using_punch()1984 Editor::select_all_selectables_using_punch()
1985 {
1986 	Location* location = _session->locations()->auto_punch_location();
1987 	list<Selectable *> touched;
1988 
1989 	if (location == 0 || (location->end() - location->start() <= 1))  {
1990 		return;
1991 	}
1992 
1993 
1994 	TrackViewList* ts;
1995 
1996 	if (selection->tracks.empty()) {
1997 		ts = &track_views;
1998 	} else {
1999 		ts = &selection->tracks;
2000 	}
2001 
2002 	for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2003 		if ((*iter)->hidden()) {
2004 			continue;
2005 		}
2006 		(*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
2007 	}
2008 	begin_reversible_selection_op (X_("select all from punch"));
2009 	selection->set (touched);
2010 	commit_reversible_selection_op ();
2011 
2012 }
2013 
2014 void
select_all_selectables_using_loop()2015 Editor::select_all_selectables_using_loop()
2016 {
2017 	Location* location = _session->locations()->auto_loop_location();
2018 	list<Selectable *> touched;
2019 
2020 	if (location == 0 || (location->end() - location->start() <= 1))  {
2021 		return;
2022 	}
2023 
2024 
2025 	TrackViewList* ts;
2026 
2027 	if (selection->tracks.empty()) {
2028 		ts = &track_views;
2029 	} else {
2030 		ts = &selection->tracks;
2031 	}
2032 
2033 	for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2034 		if ((*iter)->hidden()) {
2035 			continue;
2036 		}
2037 		(*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
2038 	}
2039 	begin_reversible_selection_op (X_("select all from loop"));
2040 	selection->set (touched);
2041 	commit_reversible_selection_op ();
2042 
2043 }
2044 
2045 void
select_all_selectables_using_cursor(EditorCursor * cursor,bool after)2046 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
2047 {
2048 	samplepos_t start;
2049 	samplepos_t end;
2050 	list<Selectable *> touched;
2051 
2052 	if (after) {
2053 		start = cursor->current_sample();
2054 		end = _session->current_end_sample();
2055 	} else {
2056 		if (cursor->current_sample() > 0) {
2057 			start = 0;
2058 			end = cursor->current_sample() - 1;
2059 		} else {
2060 			return;
2061 		}
2062 	}
2063 
2064 	if (internal_editing()) {
2065 		for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2066 			MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2067 			if (mrv) {
2068 				mrv->select_range (start, end);
2069 			}
2070 		}
2071 		return;
2072 	}
2073 
2074 	if (after) {
2075 		begin_reversible_selection_op (X_("select all after cursor"));
2076 	} else {
2077 		begin_reversible_selection_op (X_("select all before cursor"));
2078 	}
2079 
2080 	TrackViewList* ts;
2081 
2082 	if (selection->tracks.empty()) {
2083 		ts = &track_views;
2084 	} else {
2085 		ts = &selection->tracks;
2086 	}
2087 
2088 	for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2089 		if ((*iter)->hidden()) {
2090 			continue;
2091 		}
2092 		(*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2093 	}
2094 	selection->set (touched);
2095 	commit_reversible_selection_op ();
2096 }
2097 
2098 void
select_all_selectables_using_edit(bool after,bool from_context_menu)2099 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2100 {
2101 	samplepos_t start;
2102 	samplepos_t end;
2103 	list<Selectable *> touched;
2104 
2105 	if (after) {
2106 		start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2107 		end = _session->current_end_sample();
2108 	} else {
2109 		if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2110 			start = 0;
2111 			end -= 1;
2112 		} else {
2113 			return;
2114 		}
2115 	}
2116 
2117 	if (internal_editing()) {
2118 		for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2119 			MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2120 			mrv->select_range (start, end);
2121 		}
2122 		return;
2123 	}
2124 
2125 	if (after) {
2126 		begin_reversible_selection_op (X_("select all after edit"));
2127 	} else {
2128 		begin_reversible_selection_op (X_("select all before edit"));
2129 	}
2130 
2131 	TrackViewList* ts;
2132 
2133 	if (selection->tracks.empty()) {
2134 		ts = &track_views;
2135 	} else {
2136 		ts = &selection->tracks;
2137 	}
2138 
2139 	for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2140 		if ((*iter)->hidden()) {
2141 			continue;
2142 		}
2143 		(*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2144 	}
2145 	selection->set (touched);
2146 	commit_reversible_selection_op ();
2147 }
2148 
2149 void
select_all_selectables_between(bool within)2150 Editor::select_all_selectables_between (bool within)
2151 {
2152 	samplepos_t start;
2153 	samplepos_t end;
2154 	list<Selectable *> touched;
2155 
2156 	if (!get_edit_op_range (start, end)) {
2157 		return;
2158 	}
2159 
2160 	if (internal_editing()) {
2161 		for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2162 			MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2163 			mrv->select_range (start, end);
2164 		}
2165 		return;
2166 	}
2167 
2168 	TrackViewList* ts;
2169 
2170 	if (selection->tracks.empty()) {
2171 		ts = &track_views;
2172 	} else {
2173 		ts = &selection->tracks;
2174 	}
2175 
2176 	for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2177 		if ((*iter)->hidden()) {
2178 			continue;
2179 		}
2180 		(*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2181 	}
2182 
2183 	begin_reversible_selection_op (X_("Select all Selectables Between"));
2184 	selection->set (touched);
2185 	commit_reversible_selection_op ();
2186 }
2187 
2188 void
select_range_between()2189 Editor::select_range_between ()
2190 {
2191 	samplepos_t start;
2192 	samplepos_t end;
2193 
2194 	if (!selection->time.empty()) {
2195 		selection->clear_time ();
2196 	}
2197 
2198 	if (!get_edit_op_range (start, end)) {
2199 		return;
2200 	}
2201 
2202 	if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2203 		set_mouse_mode (MouseRange, false);
2204 	}
2205 
2206 	begin_reversible_selection_op (X_("Select Range Between"));
2207 	selection->set (start, end);
2208 	commit_reversible_selection_op ();
2209 }
2210 
2211 bool
get_edit_op_range(samplepos_t & start,samplepos_t & end) const2212 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2213 {
2214 	/* if an explicit range exists, use it */
2215 
2216 	if ((mouse_mode == MouseRange || get_smart_mode()) &&  !selection->time.empty()) {
2217 		/* we know that these are ordered */
2218 		start = selection->time.start();
2219 		end = selection->time.end_sample();
2220 		return true;
2221 	} else {
2222 		start = 0;
2223 		end = 0;
2224 		return false;
2225 	}
2226 }
2227 
2228 void
deselect_all()2229 Editor::deselect_all ()
2230 {
2231 	begin_reversible_selection_op (X_("Deselect All"));
2232 	selection->clear ();
2233 	commit_reversible_selection_op ();
2234 }
2235 
2236 long
select_range(samplepos_t s,samplepos_t e)2237 Editor::select_range (samplepos_t s, samplepos_t e)
2238 {
2239 	begin_reversible_selection_op (X_("Select Range"));
2240 	selection->add (clicked_axisview);
2241 	selection->time.clear ();
2242 	long ret = selection->set (s, e);
2243 	commit_reversible_selection_op ();
2244 	return ret;
2245 }
2246 
2247 void
catch_up_on_midi_selection()2248 Editor::catch_up_on_midi_selection ()
2249 {
2250 	RegionSelection regions;
2251 
2252 	for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2253 		if ((*iter)->hidden()) {
2254 			continue;
2255 		}
2256 
2257 		MidiTimeAxisView* matv = dynamic_cast<MidiTimeAxisView*> (*iter);
2258 		if (!matv) {
2259 			continue;
2260 		}
2261 
2262 		matv->get_regions_with_selected_data (regions);
2263 	}
2264 
2265 	if (!regions.empty()) {
2266 		selection->set (regions);
2267 	}
2268 }
2269