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