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