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