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