more fun and games with selection logic
[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                 regions.add (r);
213         } else {
214                 regions.erase (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                         regions.add ((*x));
228                 } else {
229                         regions.erase (i);
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                 RegionsChanged ();
326         }
327 }
328
329 void
330 Selection::add (vector<RegionView*>& v)
331 {
332         bool changed = false;
333
334         for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
335                 if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
336                         regions.add ((*i));
337                         changed = true;
338                 }
339         }
340
341         if (changed) {
342                 RegionsChanged ();
343         }
344 }
345
346 long
347 Selection::add (nframes_t start, nframes_t end)
348 {
349         AudioRangeComparator cmp;
350
351         /* XXX this implementation is incorrect */
352
353         time.push_back (AudioRange (start, end, next_time_id++));
354         time.consolidate ();
355         time.sort (cmp);
356         
357         TimeChanged ();
358
359         return next_time_id - 1;
360 }
361
362 void
363 Selection::replace (uint32_t sid, nframes_t start, nframes_t end)
364 {
365         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
366                 if ((*i).id == sid) {
367                         time.erase (i);
368                         time.push_back (AudioRange(start,end, sid));
369
370                         /* don't consolidate here */
371
372
373                         AudioRangeComparator cmp;
374                         time.sort (cmp);
375
376                         TimeChanged ();
377                         break;
378                 }
379         }
380 }
381
382 void
383 Selection::add (AutomationList* ac)
384 {
385         if (find (lines.begin(), lines.end(), ac) == lines.end()) {
386                 lines.push_back (ac);
387                 LinesChanged();
388         }
389 }
390
391 void
392 Selection::remove (boost::shared_ptr<Redirect> r)
393 {
394         RedirectSelection::iterator i;
395         if ((i = find (redirects.begin(), redirects.end(), r)) != redirects.end()) {
396                 redirects.erase (i);
397                 RedirectsChanged ();
398         }
399 }
400
401 void
402 Selection::remove (TimeAxisView* track)
403 {
404         list<TimeAxisView*>::iterator i;
405         if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
406                 tracks.erase (i);
407                 TracksChanged();
408         }
409 }
410
411 void
412 Selection::remove (const list<TimeAxisView*>& track_list)
413 {
414         bool changed = false;
415
416         for (list<TimeAxisView*>::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
417
418                 list<TimeAxisView*>::iterator x;
419
420                 if ((x = find (tracks.begin(), tracks.end(), (*i))) != tracks.end()) {
421                         tracks.erase (x);
422                         changed = true;
423                 }
424         }
425
426         if (changed) {
427                 TracksChanged();
428         }
429 }
430
431 void
432 Selection::remove (boost::shared_ptr<Playlist> track)
433 {
434         list<boost::shared_ptr<Playlist> >::iterator i;
435         if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
436                 playlists.erase (i);
437                 PlaylistsChanged();
438         }
439 }
440
441 void
442 Selection::remove (const list<boost::shared_ptr<Playlist> >& pllist)
443 {
444         bool changed = false;
445
446         for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
447
448                 list<boost::shared_ptr<Playlist> >::iterator x;
449
450                 if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
451                         playlists.erase (x);
452                         changed = true;
453                 }
454         }
455
456         if (changed) {
457                 PlaylistsChanged();
458         }
459 }
460
461 void
462 Selection::remove (RegionView* r)
463 {
464         if (regions.remove (r)) {
465                 RegionsChanged ();
466         }
467
468         if (!regions.involves (r->get_trackview())) {
469                 remove (&r->get_trackview());
470         }
471 }
472
473
474 void
475 Selection::remove (uint32_t selection_id)
476 {
477         if (time.empty()) {
478                 return;
479         }
480
481         for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
482                 if ((*i).id == selection_id) {
483                         time.erase (i);
484                                                 
485                         TimeChanged ();
486                         break;
487                 }
488         }
489 }
490
491 void
492 Selection::remove (nframes_t start, nframes_t end)
493 {
494 }
495
496 void
497 Selection::remove (AutomationList *ac)
498 {
499         list<AutomationList*>::iterator i;
500         if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
501                 lines.erase (i);
502                 LinesChanged();
503         }
504 }
505
506 void
507 Selection::set (boost::shared_ptr<Redirect> r)
508 {
509         clear_redirects ();
510         add (r);
511 }
512
513 void
514 Selection::set (TimeAxisView* track)
515 {
516         clear_tracks ();
517         add (track);
518 }
519
520 void
521 Selection::set (const list<TimeAxisView*>& track_list)
522 {
523         clear_tracks ();
524         add (track_list);
525 }
526
527 void
528 Selection::set (boost::shared_ptr<Playlist> playlist)
529 {
530         clear_playlists ();
531         add (playlist);
532 }
533
534 void
535 Selection::set (const list<boost::shared_ptr<Playlist> >& pllist)
536 {
537         clear_playlists ();
538         add (pllist);
539 }
540
541 void
542 Selection::set (RegionView* r)
543 {
544         clear_regions ();
545         add (r);
546 }
547
548 void
549 Selection::set (vector<RegionView*>& v)
550 {
551         clear_regions ();
552         // make sure to deselect any automation selections
553         clear_points();
554         add (v);
555 }
556
557 long
558 Selection::set (TimeAxisView* track, nframes_t start, nframes_t end)
559 {
560         if ((start == 0 && end == 0) || end < start) {
561                 return 0;
562         }
563
564         if (time.empty()) {
565                 time.push_back (AudioRange (start, end, next_time_id++));
566         } else {
567                 /* reuse the first entry, and remove all the rest */
568
569                 while (time.size() > 1) {
570                         time.pop_front();
571                 }
572                 time.front().start = start;
573                 time.front().end = end;
574         }
575
576         if (track) {
577                 time.track = track;
578                 time.group = track->edit_group();
579         } else {
580                 time.track = 0;
581                 time.group = 0;
582         }
583
584         time.consolidate ();
585
586         TimeChanged ();
587
588         return time.front().id;
589 }
590
591 void
592 Selection::set (AutomationList *ac)
593 {
594         lines.clear();
595         add (ac);
596 }
597
598 bool
599 Selection::selected (TimeAxisView* tv)
600 {
601         return find (tracks.begin(), tracks.end(), tv) != tracks.end();
602 }
603
604 bool
605 Selection::selected (RegionView* rv)
606 {
607         return find (regions.begin(), regions.end(), rv) != regions.end();
608 }
609
610 bool
611 Selection::empty ()
612 {
613         return regions.empty () &&
614                 tracks.empty () &&
615                 points.empty () && 
616                 playlists.empty () && 
617                 lines.empty () &&
618                 time.empty () &&
619                 playlists.empty () &&
620                 redirects.empty ()
621                 ;
622 }
623
624 void
625 Selection::set (list<Selectable*>& selectables)
626 {
627         clear_regions();
628         clear_points ();
629         add (selectables);
630 }
631
632
633 void
634 Selection::add (list<Selectable*>& selectables)
635 {
636         RegionView* rv;
637         AutomationSelectable* as;
638         vector<RegionView*> rvs;
639         vector<AutomationSelectable*> autos;
640
641         for (std::list<Selectable*>::iterator i = selectables.begin(); i != selectables.end(); ++i) {
642                 if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
643                         rvs.push_back (rv);
644                 } else if ((as = dynamic_cast<AutomationSelectable*> (*i)) != 0) {
645                         autos.push_back (as);
646                 } else {
647                         fatal << _("programming error: ")
648                               << X_("unknown selectable type passed to Selection::set()")
649                               << endmsg;
650                         /*NOTREACHED*/
651                 }
652         }
653
654         if (!rvs.empty()) {
655                 add (rvs);
656         } 
657
658         if (!autos.empty()) {
659                 add (autos);
660         } 
661 }
662
663 void
664 Selection::clear_points ()
665 {
666         if (!points.empty()) {
667                 points.clear ();
668                 PointsChanged ();
669         }
670 }
671
672 void
673 Selection::add (vector<AutomationSelectable*>& autos)
674 {
675         for (vector<AutomationSelectable*>::iterator i = autos.begin(); i != autos.end(); ++i) {
676                 points.push_back (**i);
677                 delete *i;
678         }
679
680         PointsChanged ();
681 }