1 /*
2  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
3  * Copyright (C) 2006-2014 David Robillard <d@drobilla.net>
4  * Copyright (C) 2006-2017 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2014-2016 Nick Mainsbridge <mainsbridge@gmail.com>
7  * Copyright (C) 2016-2017 Robin Gareus <robin@gareus.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #include <algorithm>
25 
26 #include "ardour/region.h"
27 
28 #include "gui_thread.h"
29 #include "midi_region_view.h"
30 #include "region_view.h"
31 #include "region_selection.h"
32 #include "time_axis_view.h"
33 
34 using namespace std;
35 using namespace ARDOUR;
36 using namespace PBD;
37 
38 /** Construct an empty RegionSelection.
39  */
RegionSelection()40 RegionSelection::RegionSelection ()
41 {
42 	RegionView::RegionViewGoingAway.connect (death_connection, MISSING_INVALIDATOR, boost::bind (&RegionSelection::remove_it, this, _1), gui_context());
43 }
44 
45 /** Copy constructor.
46  *  @param other RegionSelection to copy.
47  */
RegionSelection(const RegionSelection & other)48 RegionSelection::RegionSelection (const RegionSelection& other)
49 	: std::list<RegionView*>()
50 {
51 	RegionView::RegionViewGoingAway.connect (death_connection, MISSING_INVALIDATOR, boost::bind (&RegionSelection::remove_it, this, _1), gui_context());
52 
53 	for (RegionSelection::const_iterator i = other.begin(); i != other.end(); ++i) {
54 		add (*i);
55 	}
56 }
57 
58 /** operator= to set a RegionSelection to be the same as another.
59  *  @param other Other RegionSelection.
60  */
61 RegionSelection&
operator =(const RegionSelection & other)62 RegionSelection::operator= (const RegionSelection& other)
63 {
64 	if (this != &other) {
65 
66 		clear_all();
67 
68 		for (RegionSelection::const_iterator i = other.begin(); i != other.end(); ++i) {
69 			add (*i);
70 		}
71 	}
72 
73 	return *this;
74 }
75 
76 /** Empty this RegionSelection.
77  */
78 void
clear_all()79 RegionSelection::clear_all()
80 {
81 	clear();
82 	pending.clear ();
83 	_bylayer.clear();
84 }
85 
86 /**
87  *  @param rv RegionView.
88  *  @return true if this selection contains rv.
89  */
contains(RegionView * rv) const90 bool RegionSelection::contains (RegionView* rv) const
91 {
92 	return find (begin(), end(), rv) != end();
93 }
94 
contains(boost::shared_ptr<ARDOUR::Region> region) const95 bool RegionSelection::contains (boost::shared_ptr<ARDOUR::Region> region) const
96 {
97 	for (const_iterator r = begin (); r != end (); ++r) {
98 		if ((*r)->region () == region) {
99 			return true;
100 		}
101 	}
102 	return false;
103 }
104 
105 /** Add a region to the selection.
106  *  @param rv Region to add.
107  *  @return false if we already had the region or if it cannot be added,
108  *          otherwise true.
109  */
110 bool
add(RegionView * rv)111 RegionSelection::add (RegionView* rv)
112 {
113 	if (!rv->region()->playlist()) {
114 		/* not attached to a playlist - selection not allowed.
115 		   This happens if the user tries to select a region
116 		   during a capture pass.
117 		*/
118 		return false;
119 	}
120 
121 	if (contains (rv)) {
122 		/* we already have it */
123 		return false;
124 	}
125 
126 	push_back (rv);
127 
128 	/* add to layer sorted list */
129 
130 	add_to_layer (rv);
131 
132 	return true;
133 }
134 
135 /** Remove a region from the selection.
136  *  @param rv Region to remove.
137  */
138 void
remove_it(RegionView * rv)139 RegionSelection::remove_it (RegionView *rv)
140 {
141 	remove (rv);
142 }
143 
144 /** Remove a region from the selection.
145  *  @param rv Region to remove.
146  *  @return true if the region was in the selection, false if not.
147  */
148 bool
remove(RegionView * rv)149 RegionSelection::remove (RegionView* rv)
150 {
151 	RegionSelection::iterator r;
152 
153 	if ((r = find (begin(), end(), rv)) != end()) {
154 
155 		// remove from layer sorted list
156 		_bylayer.remove (rv);
157 		pending.remove (rv->region()->id());
158 		erase (r);
159 		return true;
160 	}
161 
162 	return false;
163 }
164 
165 /** Add a region to the list sorted by layer.
166  *  @param rv Region to add.
167  */
168 void
add_to_layer(RegionView * rv)169 RegionSelection::add_to_layer (RegionView * rv)
170 {
171 	// insert it into layer sorted position
172 
173 	list<RegionView*>::iterator i;
174 
175 	for (i = _bylayer.begin(); i != _bylayer.end(); ++i)
176 	{
177 		if (rv->region()->layer() < (*i)->region()->layer()) {
178 			_bylayer.insert(i, rv);
179 			return;
180 		}
181 	}
182 
183 	// insert at end if we get here
184 	_bylayer.insert(i, rv);
185 }
186 
187 struct RegionSortByTime {
operator ()RegionSortByTime188 	bool operator() (const RegionView* a, const RegionView* b) const {
189 		return a->region()->position() < b->region()->position();
190 	}
191 };
192 
193 
194 /**
195  *  @param foo List which will be filled with the selection's regions
196  *  sorted by position.
197  */
198 void
by_position(list<RegionView * > & foo) const199 RegionSelection::by_position (list<RegionView*>& foo) const
200 {
201 	list<RegionView*>::const_iterator i;
202 	RegionSortByTime sorter;
203 
204 	for (i = _bylayer.begin(); i != _bylayer.end(); ++i) {
205 		foo.push_back (*i);
206 	}
207 
208 	foo.sort (sorter);
209 	return;
210 }
211 
212 struct RegionSortByTrack {
operator ()RegionSortByTrack213 	bool operator() (const RegionView* a, const RegionView* b) const {
214 
215 		/* really, track and position */
216 
217 		if (a->get_time_axis_view().order() == b->get_time_axis_view().order()) {
218 			return a->region()->position() < b->region()->position();
219 		} else {
220 			return a->get_time_axis_view().order() < b->get_time_axis_view().order();
221 		}
222 	}
223 };
224 
225 
226 /**
227  *  @param List which will be filled with the selection's regions
228  *  sorted by track and position.
229  */
230 void
by_track(list<RegionView * > & foo) const231 RegionSelection::by_track (list<RegionView*>& foo) const
232 {
233 	list<RegionView*>::const_iterator i;
234 	RegionSortByTrack sorter;
235 
236 	for (i = _bylayer.begin(); i != _bylayer.end(); ++i) {
237 		foo.push_back (*i);
238 	}
239 
240 	foo.sort (sorter);
241 	return;
242 }
243 
244 /**
245  *  @param Sort the selection by position and track.
246  */
247 void
sort_by_position_and_track()248 RegionSelection::sort_by_position_and_track ()
249 {
250 	RegionSortByTrack sorter;
251 	sort (sorter);
252 }
253 
254 /**
255  *  @param tv Track.
256  *  @return true if any of the selection's regions are on tv.
257  */
258 bool
involves(const TimeAxisView & tv) const259 RegionSelection::involves (const TimeAxisView& tv) const
260 {
261 	for (RegionSelection::const_iterator i = begin(); i != end(); ++i) {
262 		if (&(*i)->get_time_axis_view() == &tv) {
263 			return true;
264 		}
265 	}
266 	return false;
267 }
268 
269 samplepos_t
start() const270 RegionSelection::start () const
271 {
272 	samplepos_t s = max_samplepos;
273 	for (RegionSelection::const_iterator i = begin(); i != end(); ++i) {
274 		s = min (s, (*i)->region()->position ());
275 	}
276 
277 	if (s == max_samplepos) {
278 		return 0;
279 	}
280 
281 	return s;
282 }
283 
284 samplepos_t
end_sample() const285 RegionSelection::end_sample () const
286 {
287 	samplepos_t e = 0;
288 	for (RegionSelection::const_iterator i = begin(); i != end(); ++i) {
289 		e = max (e, (*i)->region()->last_sample ());
290 	}
291 
292 	return e;
293 }
294 
295 /** @return the playlists that the regions in the selection are on */
296 set<boost::shared_ptr<Playlist> >
playlists() const297 RegionSelection::playlists () const
298 {
299 	set<boost::shared_ptr<Playlist> > pl;
300 	for (RegionSelection::const_iterator i = begin(); i != end(); ++i) {
301 		pl.insert ((*i)->region()->playlist ());
302 	}
303 
304 	return pl;
305 }
306 
307 size_t
n_midi_regions() const308 RegionSelection::n_midi_regions () const
309 {
310 	size_t count = 0;
311 
312 	for (const_iterator r = begin(); r != end(); ++r) {
313 		MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
314 		if (mrv) {
315 			++count;
316 		}
317 	}
318 
319 	return count;
320 }
321 
322 ARDOUR::RegionList
regionlist() const323 RegionSelection::regionlist () const
324 {
325 	ARDOUR::RegionList rl;
326 	for (const_iterator r = begin (); r != end (); ++r) {
327 		rl.push_back ((*r)->region ());
328 	}
329 	return rl;
330 }
331