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