fix nasty logic error that leads to crashing bugs when trying to operate on regions...
[ardour.git] / gtk2_ardour / selection.cc
1 /*
2     Copyright (C) 2002 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
20 #include <algorithm>
21 #include <sigc++/bind.h>
22 #include <pbd/error.h>
23 #include <pbd/stacktrace.h>
24
25 #include <ardour/playlist.h>
26
27 #include "region_view.h"
28 #include "selection.h"
29 #include "selection_templates.h"
30 #include "time_axis_view.h"
31 #include "automation_time_axis.h"
32
33 #include "i18n.h"
34
35 using namespace ARDOUR;
36 using namespace PBD;
37 using namespace sigc;
38
39 struct AudioRangeComparator {
40     bool operator()(AudioRange a, AudioRange b) {
41             return a.start < b.start;
42     }
43 };
44
45 Selection&
46 Selection::operator= (const Selection& other)
47 {
48         if (&other != this) {
49                 regions = other.regions;
50                 tracks = other.tracks;
51                 time = other.time;
52                 lines = other.lines;
53         }
54         return *this;
55 }
56
57 bool
58 operator== (const Selection& a, const Selection& b)
59 {
60         return a.regions == b.regions &&
61                 a.tracks == b.tracks &&
62                 a.time.track == b.time.track &&
63                 a.time.group == b.time.group && 
64                 a.time == b.time &&
65                 a.lines == b.lines &&
66                 a.playlists == b.playlists &&
67                 a.redirects == b.redirects;
68 }
69
70 void
71 Selection::clear ()
72 {
73         clear_tracks ();
74         clear_regions ();
75         clear_points ();
76         clear_lines();
77         clear_time ();
78         clear_playlists ();
79         clear_redirects ();
80 }
81
82 void
83 Selection::dump_region_layers()
84 {
85         cerr << "region selection layer dump" << endl;
86         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
87                 cerr << "layer: " << (int)(*i)->region()->layer() << endl;
88         }
89 }
90
91
92 void
93 Selection::clear_redirects ()
94 {
95         if (!redirects.empty()) {
96                 redirects.clear ();
97                 RedirectsChanged ();
98         }
99 }
100
101 void
102 Selection::clear_regions ()
103 {
104         if (!regions.empty()) {
105                 regions.clear_all ();
106                 RegionsChanged();
107         }
108 }
109
110 void
111 Selection::clear_tracks ()
112 {
113         if (!tracks.empty()) {
114                 tracks.clear ();
115                 TracksChanged();
116         }
117 }
118
119 void
120 Selection::clear_time ()
121 {
122         time.track = 0;
123         time.group = 0;
124         time.clear();
125
126         TimeChanged ();
127 }
128
129 void
130 Selection::clear_playlists ()
131 {
132         /* Selections own their playlists */
133
134         for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
135                 /* selections own their own regions, which are copies of the "originals". make them go away */
136                 (*i)->drop_regions ();
137                 (*i)->release ();
138         }
139
140         if (!playlists.empty()) {
141                 playlists.clear ();
142                 PlaylistsChanged();
143         }
144 }
145
146 void
147 Selection::clear_lines ()
148 {
149         if (!lines.empty()) {
150                 lines.clear ();
151                 LinesChanged();
152         }
153 }
154
155 void
156 Selection::toggle (boost::shared_ptr<Redirect> r)
157 {
158         RedirectSelection::iterator i;
159
160         if ((i = find (redirects.begin(), redirects.end(), r)) == redirects.end()) {
161                 redirects.push_back (r);
162         } else {
163                 redirects.erase (i);
164         }
165         RedirectsChanged();
166
167 }
168
169 void
170 Selection::toggle (boost::shared_ptr<Playlist> pl)
171 {
172         PlaylistSelection::iterator i;
173
174         if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
175                 pl->use ();
176                 playlists.push_back(pl);
177         } else {
178                 playlists.erase (i);
179         }
180
181         PlaylistsChanged ();
182 }
183
184 void
185 Selection::toggle (const list<TimeAxisView*>& track_list)
186 {
187         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
188                 toggle ( (*i) );
189         }
190 }
191
192 void
193 Selection::toggle (TimeAxisView* track)
194 {
195         TrackSelection::iterator i;
196         
197         if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
198                 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
199                 track->GoingAway.connect (sigc::bind (mem_fun (*this, pmf), track));
200                 tracks.push_back (track);
201         } else {
202                 tracks.erase (i);
203         }
204
205         TracksChanged();
206 }
207
208 void
209 Selection::toggle (RegionView* r)
210 {
211         RegionSelection::iterator i;
212
213         if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
214                 add (r);
215         } else {
216                 remove (*i);
217         }
218
219         RegionsChanged ();
220 }
221
222 void
223 Selection::toggle (vector<RegionView*>& r)
224 {
225         RegionSelection::iterator i;
226
227         for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
228                 if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
229                         add ((*x));
230                 } else {
231                         remove (*x);
232                 }
233         }
234
235         RegionsChanged ();
236 }
237
238 long
239 Selection::toggle (nframes_t start, nframes_t end)
240 {
241         AudioRangeComparator cmp;
242
243         /* XXX this implementation is incorrect */
244
245         time.push_back (AudioRange (start, end, next_time_id++));
246         time.consolidate ();
247         time.sort (cmp);
248         
249         TimeChanged ();
250
251         return next_time_id - 1;
252 }
253
254
255 void
256 Selection::add (boost::shared_ptr<Redirect> r)
257 {
258         if (find (redirects.begin(), redirects.end(), r) == redirects.end()) {
259                 redirects.push_back (r);
260                 RedirectsChanged();
261         }
262 }
263
264 void
265 Selection::add (boost::shared_ptr<Playlist> pl)
266 {
267         if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
268                 pl->use ();
269                 playlists.push_back(pl);
270                 PlaylistsChanged ();
271         }
272 }
273
274 void
275 Selection::add (const list<boost::shared_ptr<Playlist> >& pllist)
276 {
277         bool changed = false;
278
279         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
280                 if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
281                         (*i)->use ();
282                         playlists.push_back (*i);
283                         changed = true;
284                 }
285         }
286         
287         if (changed) {
288                 PlaylistsChanged ();
289         }
290 }
291
292 void
293 Selection::add (const list<TimeAxisView*>& track_list)
294 {
295         bool changed = false;
296
297         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
298                 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
299                         void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
300                         (*i)->GoingAway.connect (sigc::bind (mem_fun (*this, pmf), (*i)));
301                         tracks.push_back (*i);
302                         changed = true;
303                 }
304         }
305         
306         if (changed) {
307                 TracksChanged ();
308         }
309 }
310
311 void
312 Selection::add (TimeAxisView* track)
313 {
314         if (find (tracks.begin(), tracks.end(), track) == tracks.end()) {
315                 void (Selection::*pmf)(TimeAxisView*) = &Selection::remove;
316                 track->GoingAway.connect (sigc::bind (mem_fun (*this, pmf), track));
317                 tracks.push_back (track);
318                 TracksChanged();
319         }
320 }
321
322 void
323 Selection::add (RegionView* r)
324 {
325         if (find (regions.begin(), regions.end(), r) == regions.end()) {
326                 regions.add (r);
327                 add (&r->get_trackview());
328                 RegionsChanged ();
329         }
330 }
331
332 void
333 Selection::add (vector<RegionView*>& v)
334 {
335         bool changed = false;
336
337         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
338                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
339                         changed = regions.add ((*i));
340                         if (changed) {
341                                 add (&(*i)->get_trackview());
342                         }
343                 }
344         }
345
346         if (changed) {
347                 RegionsChanged ();
348         }
349 }
350
351 long
352 Selection::add (nframes_t start, nframes_t end)
353 {
354         AudioRangeComparator cmp;
355
356         /* XXX this implementation is incorrect */
357
358         time.push_back (AudioRange (start, end, next_time_id++));
359         time.consolidate ();
360         time.sort (cmp);
361         
362         TimeChanged ();
363
364         return next_time_id - 1;
365 }
366
367 void
368 Selection::replace (uint32_t sid, nframes_t start, nframes_t end)
369 {
370         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
371                 if ((*i).id == sid) {
372                         time.erase (i);
373                         time.push_back (AudioRange(start,end, sid));
374
375                         /* don't consolidate here */
376
377
378                         AudioRangeComparator cmp;
379                         time.sort (cmp);
380
381                         TimeChanged ();
382                         break;
383                 }
384         }
385 }
386
387 void
388 Selection::add (AutomationList* ac)
389 {
390         if (find (lines.begin(), lines.end(), ac) == lines.end()) {
391                 lines.push_back (ac);
392                 LinesChanged();
393         }
394 }
395
396 void
397 Selection::remove (boost::shared_ptr<Redirect> r)
398 {
399         RedirectSelection::iterator i;
400         if ((i = find (redirects.begin(), redirects.end(), r)) != redirects.end()) {
401                 redirects.erase (i);
402                 RedirectsChanged ();
403         }
404 }
405
406 void
407 Selection::remove (TimeAxisView* track)
408 {
409         list<TimeAxisView*>::iterator i;
410         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
411                 tracks.erase (i);
412                 TracksChanged();
413         }
414 }
415
416 void
417 Selection::remove (const list<TimeAxisView*>& track_list)
418 {
419         bool changed = false;
420
421         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
422
423                 list<TimeAxisView*>::iterator x;
424
425                 if ((x = find (tracks.begin(), tracks.end(), (*i))) != tracks.end()) {
426                         tracks.erase (x);
427                         changed = true;
428                 }
429         }
430
431         if (changed) {
432                 TracksChanged();
433         }
434 }
435
436 void
437 Selection::remove (boost::shared_ptr<Playlist> track)
438 {
439         list<boost::shared_ptr<Playlist> >::iterator i;
440         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
441                 playlists.erase (i);
442                 PlaylistsChanged();
443         }
444 }
445
446 void
447 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
448 {
449         bool changed = false;
450
451         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
452
453                 list<boost::shared_ptr<Playlist> >::iterator x;
454
455                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
456                         playlists.erase (x);
457                         changed = true;
458                 }
459         }
460
461         if (changed) {
462                 PlaylistsChanged();
463         }
464 }
465
466 void
467 Selection::remove (RegionView* r)
468 {
469         if (regions.remove (r)) {
470                 RegionsChanged ();
471         }
472
473         if (!regions.involves (r->get_trackview())) {
474                 remove (&r->get_trackview());
475         }
476 }
477
478
479 void
480 Selection::remove (uint32_t selection_id)
481 {
482         if (time.empty()) {
483                 return;
484         }
485
486         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
487                 if ((*i).id == selection_id) {
488                         time.erase (i);
489                                                 
490                         TimeChanged ();
491                         break;
492                 }
493         }
494 }
495
496 void
497 Selection::remove (nframes_t start, nframes_t end)
498 {
499 }
500
501 void
502 Selection::remove (AutomationList *ac)
503 {
504         list<AutomationList*>::iterator i;
505         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
506                 lines.erase (i);
507                 LinesChanged();
508         }
509 }
510
511 void
512 Selection::set (boost::shared_ptr<Redirect> r)
513 {
514         clear_redirects ();
515         add (r);
516 }
517
518 void
519 Selection::set (TimeAxisView* track)
520 {
521         clear_tracks ();
522         add (track);
523 }
524
525 void
526 Selection::set (const list<TimeAxisView*>& track_list)
527 {
528         clear_tracks ();
529         add (track_list);
530 }
531
532 void
533 Selection::set (boost::shared_ptr<Playlist> playlist)
534 {
535         clear_playlists ();
536         add (playlist);
537 }
538
539 void
540 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
541 {
542         clear_playlists ();
543         add (pllist);
544 }
545
546 void
547 Selection::set (RegionView* r)
548 {
549         clear_regions ();
550         clear_tracks ();
551         add (r);
552 }
553
554 void
555 Selection::set (vector<RegionView*>& v)
556 {
557         clear_tracks ();
558         clear_regions ();
559         // make sure to deselect any automation selections
560         clear_points();
561         add (v);
562 }
563
564 long
565 Selection::set (TimeAxisView* track, nframes_t start, nframes_t end)
566 {
567         if ((start == 0 && end == 0) || end < start) {
568                 return 0;
569         }
570
571         if (time.empty()) {
572                 time.push_back (AudioRange (start, end, next_time_id++));
573         } else {
574                 /* reuse the first entry, and remove all the rest */
575
576                 while (time.size() > 1) {
577                         time.pop_front();
578                 }
579                 time.front().start = start;
580                 time.front().end = end;
581         }
582
583         if (track) {
584                 time.track = track;
585                 time.group = track->edit_group();
586         } else {
587                 time.track = 0;
588                 time.group = 0;
589         }
590
591         time.consolidate ();
592
593         TimeChanged ();
594
595         return time.front().id;
596 }
597
598 void
599 Selection::set (AutomationList *ac)
600 {
601         lines.clear();
602         add (ac);
603 }
604
605 bool
606 Selection::selected (TimeAxisView* tv)
607 {
608         return find (tracks.begin(), tracks.end(), tv) != tracks.end();
609 }
610
611 bool
612 Selection::selected (RegionView* rv)
613 {
614         return find (regions.begin(), regions.end(), rv) != regions.end();
615 }
616
617 bool
618 Selection::empty ()
619 {
620         return regions.empty () &&
621                 tracks.empty () &&
622                 points.empty () && 
623                 playlists.empty () && 
624                 lines.empty () &&
625                 time.empty () &&
626                 playlists.empty () &&
627                 redirects.empty ()
628                 ;
629 }
630
631 void
632 Selection::toggle (const vector<AutomationSelectable*>& autos)
633 {
634         for (vector<AutomationSelectable*>::const_iterator x = autos.begin(); x != autos.end(); ++x) {
635                 if ((*x)->get_selected()) {
636                         points.remove (**x);
637                 } else {
638                         points.push_back (**x);
639                 }
640
641                 delete *x;
642         }
643
644         PointsChanged (); /* EMIT SIGNAL */
645 }
646
647 void
648 Selection::toggle (list<Selectable*>& selectables)
649 {
650         RegionView* rv;
651         AutomationSelectable* as;
652         vector<RegionView*> rvs;
653         vector<AutomationSelectable*> autos;
654
655         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
656                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
657                         rvs.push_back (rv);
658                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
659                         autos.push_back (as);
660                 } else {
661                         fatal << _("programming error: ")
662                               << X_("unknown selectable type passed to Selection::toggle()")
663                               << endmsg;
664                         /*NOTREACHED*/
665                 }
666         }
667
668         if (!rvs.empty()) {
669                 toggle (rvs);
670         } 
671
672         if (!autos.empty()) {
673                 toggle (autos);
674         } 
675 }
676
677 void
678 Selection::set (list<Selectable*>& selectables)
679 {
680         clear_regions();
681         clear_points ();
682         add (selectables);
683 }
684
685
686 void
687 Selection::add (list<Selectable*>& selectables)
688 {
689         RegionView* rv;
690         AutomationSelectable* as;
691         vector<RegionView*> rvs;
692         vector<AutomationSelectable*> autos;
693
694         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
695                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
696                         rvs.push_back (rv);
697                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
698                         autos.push_back (as);
699                 } else {
700                         fatal << _("programming error: ")
701                               << X_("unknown selectable type passed to Selection::add()")
702                               << endmsg;
703                         /*NOTREACHED*/
704                 }
705         }
706
707         if (!rvs.empty()) {
708                 add (rvs);
709         } 
710
711         if (!autos.empty()) {
712                 add (autos);
713         } 
714 }
715
716 void
717 Selection::clear_points ()
718 {
719         if (!points.empty()) {
720                 points.clear ();
721                 PointsChanged ();
722         }
723 }
724
725 void
726 Selection::add (vector<AutomationSelectable*>& autos)
727 {
728         for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
729                 points.push_back (**i);
730         }
731
732         PointsChanged ();
733 }