Fix formatting samplecnt_t (aka int64_t aka long long int)
[ardour.git] / gtk2_ardour / region_selection.cc
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  */
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  */
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&
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
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  */
90 bool RegionSelection::contains (RegionView* rv) const
91 {
92         return find (begin(), end(), rv) != end();
93 }
94
95 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
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
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
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
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 {
188         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
199 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 {
213         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
231 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
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
259 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
270 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
285 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> >
297 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
308 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
323 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 }