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