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