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