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