fix crash caused when deleting a region without a playlist PLUS make it impossible...
[ardour.git] / gtk2_ardour / region_selection.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include <algorithm>
20
21 #include "ardour/region.h"
22
23 #include "gui_thread.h"
24 #include "region_view.h"
25 #include "region_selection.h"
26 #include "time_axis_view.h"
27
28 using namespace std;
29 using namespace ARDOUR;
30 using namespace PBD;
31
32 /** Construct an empty RegionSelection.
33  */
34 RegionSelection::RegionSelection ()
35 {
36         RegionView::RegionViewGoingAway.connect (death_connection, MISSING_INVALIDATOR, ui_bind (&RegionSelection::remove_it, this, _1), gui_context());
37
38         _current_start = 0;
39         _current_end = 0;
40 }
41
42 /** Copy constructor.
43  *  @param other RegionSelection to copy.
44  */
45 RegionSelection::RegionSelection (const RegionSelection& other)
46         : std::list<RegionView*>()
47 {
48         RegionView::RegionViewGoingAway.connect (death_connection, MISSING_INVALIDATOR, ui_bind (&RegionSelection::remove_it, this, _1), gui_context());
49
50         _current_start = other._current_start;
51         _current_end = other._current_end;
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                 _current_start = other._current_start;
69                 _current_end = other._current_end;
70
71                 for (RegionSelection::const_iterator i = other.begin(); i != other.end(); ++i) {
72                         add (*i);
73                 }
74         }
75
76         return *this;
77 }
78
79 /** Empty this RegionSelection.
80  */
81 void
82 RegionSelection::clear_all()
83 {
84         clear();
85         _bylayer.clear();
86         _current_start = 0;
87         _current_end = 0;
88 }
89
90 /**
91  *  @param rv RegionView.
92  *  @return true if this selection contains rv.
93  */
94 bool RegionSelection::contains (RegionView* rv) const
95 {
96         return find (begin(), end(), rv) != end();
97 }
98
99 /** Add a region to the selection.
100  *  @param rv Region to add.
101  *  @return false if we already had the region or if it cannot be added, 
102  *          otherwise true.
103  */
104 bool
105 RegionSelection::add (RegionView* rv)
106 {
107         if (!rv->region()->playlist()) {
108                 /* not attached to a playlist - selection not allowed.
109                    This happens if the user tries to select a region
110                    during a capture pass.
111                 */
112                 return false;
113         }
114
115         if (contains (rv)) {
116                 /* we already have it */
117                 return false;
118         }
119
120         if (rv->region()->first_frame() < _current_start || empty()) {
121                 _current_start = rv->region()->first_frame();
122         }
123
124         if (rv->region()->last_frame() > _current_end || empty()) {
125                 _current_end = rv->region()->last_frame();
126         }
127
128         push_back (rv);
129
130         /* add to layer sorted list */
131
132         add_to_layer (rv);
133
134         return true;
135 }
136
137 /** Remove a region from the selection.
138  *  @param rv Region to remove.
139  */
140 void
141 RegionSelection::remove_it (RegionView *rv)
142 {
143         remove (rv);
144 }
145
146 /** Remove a region from the selection.
147  *  @param rv Region to remove.
148  *  @return true if the region was in the selection, false if not.
149  */
150 bool
151 RegionSelection::remove (RegionView* rv)
152 {
153         RegionSelection::iterator r;
154
155         if ((r = find (begin(), end(), rv)) != end()) {
156
157                 // remove from layer sorted list
158                 _bylayer.remove (rv);
159
160                 if (size() == 1) {
161
162                         /* this is the last one, so when we delete it
163                            we will be empty.
164                         */
165
166                         _current_start = 0;
167                         _current_end = 0;
168
169                 } else {
170
171                         boost::shared_ptr<Region> region ((*r)->region());
172
173                         if (region->first_frame() == _current_start) {
174
175                                 /* reset current start */
176
177                                 nframes_t ref = max_frames;
178
179                                 for (RegionSelection::iterator i = begin (); i != end(); ++i) {
180                                         if (region->first_frame() < ref) {
181                                                 ref = region->first_frame();
182                                         }
183                                 }
184
185                                 _current_start = ref;
186
187                         }
188
189                         if (region->last_frame() == _current_end) {
190
191                                 /* reset current end */
192
193                                 nframes_t ref = 0;
194
195                                 for (RegionSelection::iterator i = begin (); i != end(); ++i) {
196                                         if (region->first_frame() > ref) {
197                                                 ref = region->first_frame();
198                                         }
199                                 }
200
201                                 _current_end = ref;
202                         }
203                 }
204
205                 erase (r);
206
207                 return true;
208         }
209
210         return false;
211 }
212
213 /** Add a region to the list sorted by layer.
214  *  @param rv Region to add.
215  */
216 void
217 RegionSelection::add_to_layer (RegionView * rv)
218 {
219         // insert it into layer sorted position
220
221         list<RegionView*>::iterator i;
222
223         for (i = _bylayer.begin(); i != _bylayer.end(); ++i)
224         {
225                 if (rv->region()->layer() < (*i)->region()->layer()) {
226                         _bylayer.insert(i, rv);
227                         return;
228                 }
229         }
230
231         // insert at end if we get here
232         _bylayer.insert(i, rv);
233 }
234
235 struct RegionSortByTime {
236     bool operator() (const RegionView* a, const RegionView* b) const {
237             return a->region()->position() < b->region()->position();
238     }
239 };
240
241
242 /**
243  *  @param foo List which will be filled with the selection's regions
244  *  sorted by position.
245  */
246 void
247 RegionSelection::by_position (list<RegionView*>& foo) const
248 {
249         list<RegionView*>::const_iterator i;
250         RegionSortByTime sorter;
251
252         for (i = _bylayer.begin(); i != _bylayer.end(); ++i) {
253                 foo.push_back (*i);
254         }
255
256         foo.sort (sorter);
257         return;
258 }
259
260 struct RegionSortByTrack {
261     bool operator() (const RegionView* a, const RegionView* b) const {
262
263             /* really, track and position */
264
265             if (a->get_time_axis_view().order() == b->get_time_axis_view().order()) {
266                     return a->region()->position() < b->region()->position();
267             } else {
268                     return a->get_time_axis_view().order() < b->get_time_axis_view().order();
269             }
270     }
271 };
272
273
274 /**
275  *  @param List which will be filled with the selection's regions
276  *  sorted by track and position.
277  */
278 void
279 RegionSelection::by_track (list<RegionView*>& foo) const
280 {
281         list<RegionView*>::const_iterator i;
282         RegionSortByTrack sorter;
283
284         for (i = _bylayer.begin(); i != _bylayer.end(); ++i) {
285                 foo.push_back (*i);
286         }
287
288         foo.sort (sorter);
289         return;
290 }
291
292 /**
293  *  @param Sort the selection by position and track.
294  */
295 void
296 RegionSelection::sort_by_position_and_track ()
297 {
298         RegionSortByTrack sorter;
299         sort (sorter);
300 }
301
302 /**
303  *  @param tv Track.
304  *  @return true if any of the selection's regions are on tv.
305  */
306 bool
307 RegionSelection::involves (const TimeAxisView& tv) const
308 {
309         for (RegionSelection::const_iterator i = begin(); i != end(); ++i) {
310                 if (&(*i)->get_time_axis_view() == &tv) {
311                         return true;
312                 }
313         }
314         return false;
315 }
316