ardour
selection.cc
Go to the documentation of this file.
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 
23 #include "pbd/error.h"
24 #include "pbd/stacktrace.h"
25 
26 #include "ardour/playlist.h"
28 
29 #include "gui_thread.h"
30 #include "midi_cut_buffer.h"
31 #include "region_view.h"
32 #include "selection.h"
33 #include "selection_templates.h"
34 #include "time_axis_view.h"
35 #include "automation_time_axis.h"
36 #include "public_editor.h"
37 #include "control_point.h"
38 
39 #include "i18n.h"
40 
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace PBD;
44 
47  return a.start < b.start;
48  }
49 };
50 
52  : tracks (e)
53  , editor (e)
54  , next_time_id (0)
55  , _no_tracks_changed (false)
56 {
57  clear ();
58 
59  /* we have disambiguate which remove() for the compiler */
60 
61  void (Selection::*track_remove)(TimeAxisView*) = &Selection::remove;
62  TimeAxisView::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (track_remove, this, _1), gui_context());
63 
64  void (Selection::*marker_remove)(Marker*) = &Selection::remove;
65  Marker::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (marker_remove, this, _1), gui_context());
66 
67  void (Selection::*point_remove)(ControlPoint*) = &Selection::remove;
68  ControlPoint::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (point_remove, this, _1), gui_context());
69 }
70 
71 #if 0
72 Selection&
73 Selection::operator= (const Selection& other)
74 {
75  if (&other != this) {
76  regions = other.regions;
77  tracks = other.tracks;
78  time = other.time;
79  lines = other.lines;
80  midi_regions = other.midi_regions;
81  midi_notes = other.midi_notes;
82  }
83  return *this;
84 }
85 #endif
86 
87 bool
88 operator== (const Selection& a, const Selection& b)
89 {
90  return a.regions == b.regions &&
91  a.tracks == b.tracks &&
92  a.time == b.time &&
93  a.lines == b.lines &&
94  a.playlists == b.playlists &&
95  a.midi_notes == b.midi_notes &&
97 }
98 
100 void
102 {
103  clear_tracks ();
104  clear_regions ();
105  clear_points ();
106  clear_lines();
107  clear_time ();
108  clear_playlists ();
109  clear_midi_notes ();
111  clear_markers ();
113 }
114 
115 void
117 {
118  clear_regions ();
119  clear_points ();
120  clear_lines();
121  clear_playlists ();
122  clear_midi_notes ();
124 }
125 
126 void
128 {
129  if (!tracks.empty()) {
130  for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) {
131  (*x)->set_selected (false);
132  }
133  tracks.clear ();
134  if (!_no_tracks_changed) {
135  TracksChanged();
136  }
137  }
138 }
139 
140 void
142 {
143  time.clear();
144 
145  TimeChanged ();
146 }
147 
148 void
150 {
151  cerr << "region selection layer dump" << endl;
152  for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
153  cerr << "layer: " << (int)(*i)->region()->layer() << endl;
154  }
155 }
156 
157 
158 void
160 {
161  if (!regions.empty()) {
162  regions.clear_all ();
163  RegionsChanged();
164  }
165 }
166 
167 void
169 {
170  if (!midi_notes.empty()) {
171  for (MidiNoteSelection::iterator x = midi_notes.begin(); x != midi_notes.end(); ++x) {
172  delete *x;
173  }
174  midi_notes.clear ();
175  MidiNotesChanged ();
176  }
177 
178  /* The note selection is actually stored in MidiRegionView, emit signal to
179  tell them to clear their selection. */
180  ClearMidiNoteSelection(); /* EMIT SIGNAL */
181 }
182 
183 void
185 {
186  if (!midi_regions.empty()) {
187  midi_regions.clear ();
189  }
190 }
191 
192 void
194 {
195  /* Selections own their playlists */
196 
197  for (PlaylistSelection::iterator i = playlists.begin(); i != playlists.end(); ++i) {
198  /* selections own their own regions, which are copies of the "originals". make them go away */
199  (*i)->drop_regions ();
200  (*i)->release ();
201  }
202 
203  if (!playlists.empty()) {
204  playlists.clear ();
206  }
207 }
208 
209 void
211 {
212  if (!lines.empty()) {
213  lines.clear ();
214  LinesChanged();
215  }
216 }
217 
218 void
220 {
221  if (!markers.empty()) {
222  markers.clear ();
223  MarkersChanged();
224  }
225 }
226 
227 void
229 {
230  clear_time(); //enforce object/range exclusivity
231  clear_tracks(); //enforce object/track exclusivity
232 
233  PlaylistSelection::iterator i;
234 
235  if ((i = find (playlists.begin(), playlists.end(), pl)) == playlists.end()) {
236  pl->use ();
237  playlists.push_back(pl);
238  } else {
239  playlists.erase (i);
240  }
241 
242  PlaylistsChanged ();
243 }
244 
245 void
246 Selection::toggle (const TrackViewList& track_list)
247 {
248  for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
249  toggle ((*i));
250  }
251 }
252 
253 void
255 {
256  TrackSelection::iterator i;
257 
258  if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) {
259  track->set_selected (true);
260  tracks.push_back (track);
261  } else {
262  track->set_selected (false);
263  tracks.erase (i);
264  }
265 
266  if (!_no_tracks_changed) {
267  TracksChanged();
268  }
269 }
270 
271 void
272 Selection::toggle (const MidiNoteSelection& midi_note_list)
273 {
274  clear_time(); //enforce object/range exclusivity
275  clear_tracks(); //enforce object/track exclusivity
276 
277  for (MidiNoteSelection::const_iterator i = midi_note_list.begin(); i != midi_note_list.end(); ++i) {
278  toggle ((*i));
279  }
280 }
281 
282 void
284 {
285  MidiNoteSelection::iterator i;
286 
287  if ((i = find (midi_notes.begin(), midi_notes.end(), midi)) == midi_notes.end()) {
288  midi_notes.push_back (midi);
289  } else {
290  /* remember that we own the MCB */
291  delete *i;
292  midi_notes.erase (i);
293  }
294 
296 }
297 
298 
299 void
301 {
302  clear_time(); //enforce object/range exclusivity
303  clear_tracks(); //enforce object/track exclusivity
304 
305  RegionSelection::iterator i;
306 
307  if ((i = find (regions.begin(), regions.end(), r)) == regions.end()) {
308  add (r);
309  } else {
310  remove (*i);
311  }
312 
313  RegionsChanged ();
314 }
315 
316 void
318 {
319  clear_time(); //enforce object/range exclusivity
320  clear_tracks(); //enforce object/track exclusivity
321 
322  MidiRegionSelection::iterator i;
323 
324  if ((i = find (midi_regions.begin(), midi_regions.end(), mrv)) == midi_regions.end()) {
325  add (mrv);
326  } else {
327  midi_regions.erase (i);
328  }
329 
331 }
332 
333 void
334 Selection::toggle (vector<RegionView*>& r)
335 {
336  clear_time(); //enforce object/range exclusivity
337  clear_tracks(); //enforce object/track exclusivity
338 
339  RegionSelection::iterator i;
340 
341  for (vector<RegionView*>::iterator x = r.begin(); x != r.end(); ++x) {
342  if ((i = find (regions.begin(), regions.end(), (*x))) == regions.end()) {
343  add ((*x));
344  } else {
345  remove (*x);
346  }
347  }
348 
349  RegionsChanged ();
350 }
351 
352 long
354 {
355  clear_objects(); //enforce object/range exclusivity
356 
358 
359  /* XXX this implementation is incorrect */
360 
361  time.push_back (AudioRange (start, end, ++next_time_id));
362  time.consolidate ();
363  time.sort (cmp);
364 
365  TimeChanged ();
366 
367  return next_time_id;
368 }
369 
370 void
372 {
373  clear_time(); //enforce object/range exclusivity
374  clear_tracks(); //enforce object/track exclusivity
375 
376  if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) {
377  pl->use ();
378  playlists.push_back(pl);
379  PlaylistsChanged ();
380  }
381 }
382 
383 void
385 {
386  clear_time(); //enforce object/range exclusivity
387  clear_tracks(); //enforce object/track exclusivity
388 
389  bool changed = false;
390 
391  for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
392  if (find (playlists.begin(), playlists.end(), (*i)) == playlists.end()) {
393  (*i)->use ();
394  playlists.push_back (*i);
395  changed = true;
396  }
397  }
398 
399  if (changed) {
400  PlaylistsChanged ();
401  }
402 }
403 
404 void
405 Selection::add (const TrackViewList& track_list)
406 {
407  clear_objects(); //enforce object/range exclusivity
408 
409  TrackViewList added = tracks.add (track_list);
410 
411  if (!added.empty()) {
412  for (TrackViewList::iterator x = added.begin(); x != added.end(); ++x) {
413  (*x)->set_selected (true);
414  }
415  if (!_no_tracks_changed) {
416  TracksChanged ();
417  }
418  }
419 }
420 
421 void
423 {
424  clear_objects(); //enforce object/range exclusivity
425 
426  TrackViewList tr;
427  track->set_selected (true);
428  tr.push_back (track);
429  add (tr);
430 }
431 
432 void
434 {
435  clear_time(); //enforce object/range exclusivity
436  clear_tracks(); //enforce object/track exclusivity
437 
438  const MidiNoteSelection::const_iterator b = midi_list.begin();
439  const MidiNoteSelection::const_iterator e = midi_list.end();
440 
441  if (!midi_list.empty()) {
442  midi_notes.insert (midi_notes.end(), b, e);
443  MidiNotesChanged ();
444  }
445 }
446 
447 void
449 {
450  /* we take ownership of the MCB */
451 
452  if (find (midi_notes.begin(), midi_notes.end(), midi) == midi_notes.end()) {
453  midi_notes.push_back (midi);
454  MidiNotesChanged ();
455  }
456 }
457 
458 void
459 Selection::add (vector<RegionView*>& v)
460 {
461  clear_time(); //enforce object/range exclusivity
462  clear_tracks(); //enforce object/track exclusivity
463 
464  /* XXX This method or the add (const RegionSelection&) needs to go
465  */
466 
467  bool changed = false;
468 
469  for (vector<RegionView*>::iterator i = v.begin(); i != v.end(); ++i) {
470  if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
471  changed = regions.add ((*i));
472  }
473  }
474 
475  if (changed) {
476  RegionsChanged ();
477  }
478 }
479 
480 void
482 {
483  clear_time(); //enforce object/range exclusivity
484  clear_tracks(); //enforce object/track exclusivity
485 
486  /* XXX This method or the add (const vector<RegionView*>&) needs to go
487  */
488 
489  bool changed = false;
490 
491  for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
492  if (find (regions.begin(), regions.end(), (*i)) == regions.end()) {
493  changed = regions.add ((*i));
494  }
495  }
496 
497  if (changed) {
498  RegionsChanged ();
499  }
500 }
501 
502 void
504 {
505  clear_time(); //enforce object/range exclusivity
506  clear_tracks(); //enforce object/track exclusivity
507 
508  if (find (regions.begin(), regions.end(), r) == regions.end()) {
509  bool changed = regions.add (r);
510  if (changed) {
511  RegionsChanged ();
512  }
513  }
514 }
515 
516 void
518 {
519  clear_time(); //enforce object/range exclusivity
520  clear_tracks(); //enforce object/track exclusivity
521 
522  if (find (midi_regions.begin(), midi_regions.end(), mrv) == midi_regions.end()) {
523  midi_regions.push_back (mrv);
524  /* XXX should we do this? */
526  }
527 }
528 
529 long
531 {
532  clear_objects(); //enforce object/range exclusivity
533 
535 
536  /* XXX this implementation is incorrect */
537 
538  time.push_back (AudioRange (start, end, ++next_time_id));
539  time.consolidate ();
540  time.sort (cmp);
541 
542  TimeChanged ();
543 
544  return next_time_id;
545 }
546 
547 void
549 {
550  if (distance == 0) {
551  return;
552  }
553 
554  for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
555  (*i).start += distance;
556  (*i).end += distance;
557  }
558 
559  TimeChanged ();
560 }
561 
562 void
564 {
565  clear_objects(); //enforce object/range exclusivity
566 
567  for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
568  if ((*i).id == sid) {
569  time.erase (i);
570  time.push_back (AudioRange(start,end, sid));
571 
572  /* don't consolidate here */
573 
574 
576  time.sort (cmp);
577 
578  TimeChanged ();
579  break;
580  }
581  }
582 }
583 
584 void
586 {
587  clear_time(); //enforce object/range exclusivity
588  clear_tracks(); //enforce object/track exclusivity
589 
592  if (!al) {
593  warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg;
594  return;
595  }
596  if (find (lines.begin(), lines.end(), al) == lines.end()) {
597  lines.push_back (al);
598  LinesChanged();
599  }
600 }
601 
602 void
604 {
605  list<TimeAxisView*>::iterator i;
606  if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) {
607  track->set_selected (false);
608  tracks.erase (i);
609  if (!_no_tracks_changed) {
610  TracksChanged();
611  }
612  }
613 }
614 
615 void
616 Selection::remove (const TrackViewList& track_list)
617 {
618  bool changed = false;
619 
620  for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) {
621 
622  TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i);
623  if (x != tracks.end()) {
624  (*i)->set_selected (false);
625  tracks.erase (x);
626  changed = true;
627  }
628  }
629 
630  if (changed) {
631  if (!_no_tracks_changed) {
632  TracksChanged();
633  }
634  }
635 }
636 
637 void
639 {
640  PointSelection::iterator i = find (points.begin(), points.end(), p);
641  if (i != points.end ()) {
642  points.erase (i);
643  }
644 }
645 
646 void
648 {
649  bool changed = false;
650 
651  for (MidiNoteSelection::const_iterator i = midi_list.begin(); i != midi_list.end(); ++i) {
652 
653  MidiNoteSelection::iterator x;
654 
655  if ((x = find (midi_notes.begin(), midi_notes.end(), (*i))) != midi_notes.end()) {
656  midi_notes.erase (x);
657  changed = true;
658  }
659  }
660 
661  if (changed) {
663  }
664 }
665 
666 void
668 {
669  MidiNoteSelection::iterator x;
670 
671  if ((x = find (midi_notes.begin(), midi_notes.end(), midi)) != midi_notes.end()) {
672  /* remember that we own the MCB */
673  delete *x;
674  midi_notes.erase (x);
675  MidiNotesChanged ();
676  }
677 }
678 
679 void
681 {
682  list<boost::shared_ptr<Playlist> >::iterator i;
683  if ((i = find (playlists.begin(), playlists.end(), track)) != playlists.end()) {
684  playlists.erase (i);
686  }
687 }
688 
689 void
691 {
692  bool changed = false;
693 
694  for (list<boost::shared_ptr<Playlist> >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) {
695 
696  list<boost::shared_ptr<Playlist> >::iterator x;
697 
698  if ((x = find (playlists.begin(), playlists.end(), (*i))) != playlists.end()) {
699  playlists.erase (x);
700  changed = true;
701  }
702  }
703 
704  if (changed) {
706  }
707 }
708 
709 void
711 {
712  if (regions.remove (r)) {
713  RegionsChanged ();
714  }
715 }
716 
717 void
719 {
720  MidiRegionSelection::iterator x;
721 
722  if ((x = find (midi_regions.begin(), midi_regions.end(), mrv)) != midi_regions.end()) {
723  midi_regions.erase (x);
725  }
726 }
727 
728 
729 void
730 Selection::remove (uint32_t selection_id)
731 {
732  if (time.empty()) {
733  return;
734  }
735 
736  for (list<AudioRange>::iterator i = time.begin(); i != time.end(); ++i) {
737  if ((*i).id == selection_id) {
738  time.erase (i);
739 
740  TimeChanged ();
741  break;
742  }
743  }
744 }
745 
746 void
748 {
749 }
750 
751 void
753 {
754  AutomationSelection::iterator i;
755  if ((i = find (lines.begin(), lines.end(), ac)) != lines.end()) {
756  lines.erase (i);
757  LinesChanged();
758  }
759 }
760 
761 void
763 {
764  clear_objects(); //enforce object/range exclusivity
765  clear_tracks ();
766  add (track);
767 }
768 
769 void
770 Selection::set (const TrackViewList& track_list)
771 {
772  clear_objects(); //enforce object/range exclusivity
773  clear_tracks ();
774  add (track_list);
775 }
776 
777 void
779 {
780  clear_time (); //enforce region/object exclusivity
781  clear_tracks(); //enforce object/track exclusivity
782  clear_objects ();
783  add (midi_list);
784 }
785 
786 void
788 {
789  clear_time (); //enforce region/object exclusivity
790  clear_tracks(); //enforce object/track exclusivity
791  clear_objects ();
792  add (playlist);
793 }
794 
795 void
797 {
798  clear_time(); //enforce region/object exclusivity
799  clear_objects ();
800  add (pllist);
801 }
802 
803 void
805 {
806  clear_time(); //enforce region/object exclusivity
807  clear_tracks(); //enforce object/track exclusivity
808  clear_objects();
809  regions = rs;
810  RegionsChanged(); /* EMIT SIGNAL */
811 }
812 
813 void
815 {
816  clear_time(); //enforce region/object exclusivity
817  clear_tracks(); //enforce object/track exclusivity
818  clear_objects ();
819  add (mrv);
820 }
821 
822 void
823 Selection::set (RegionView* r, bool /*also_clear_tracks*/)
824 {
825  clear_time(); //enforce region/object exclusivity
826  clear_tracks(); //enforce object/track exclusivity
827  clear_objects ();
828  add (r);
829 }
830 
831 void
832 Selection::set (vector<RegionView*>& v)
833 {
834  clear_time(); //enforce region/object exclusivity
835  clear_tracks(); //enforce object/track exclusivity
836  clear_objects();
837 
838  add (v);
839 }
840 
844 long
846 {
847  clear_objects(); //enforce region/object exclusivity
848  clear_time();
849 
850  if ((start == 0 && end == 0) || end < start) {
851  return 0;
852  }
853 
854  if (time.empty()) {
855  time.push_back (AudioRange (start, end, ++next_time_id));
856  } else {
857  /* reuse the first entry, and remove all the rest */
858 
859  while (time.size() > 1) {
860  time.pop_front();
861  }
862  time.front().start = start;
863  time.front().end = end;
864  }
865 
866  time.consolidate ();
867 
868  TimeChanged ();
869 
870  return time.front().id;
871 }
872 
881 void
883 {
884  clear_objects(); //enforce region/object exclusivity
885 
886  if ((start == 0 && end == 0) || (end < start)) {
887  return;
888  }
889 
890  if (time.empty ()) {
891  time.push_back (AudioRange (start, end, ++next_time_id));
892  } else {
893  time.sort (AudioRangeComparator ());
894  time.front().start = start;
895  time.back().end = end;
896  }
897 
898  time.consolidate ();
899 
900  TimeChanged ();
901 }
902 
903 void
905 {
906  clear_time(); //enforce region/object exclusivity
907  clear_tracks(); //enforce object/track exclusivity
908  clear_objects();
909 
910  add (ac);
911 }
912 
913 bool
915 {
916  return find (markers.begin(), markers.end(), m) != markers.end();
917 }
918 
919 bool
921 {
922  return tv->get_selected ();
923 }
924 
925 bool
927 {
928  return find (regions.begin(), regions.end(), rv) != regions.end();
929 }
930 
931 bool
933 {
934  return find (points.begin(), points.end(), cp) != points.end();
935 }
936 
937 bool
938 Selection::empty (bool internal_selection)
939 {
940  bool object_level_empty = regions.empty () &&
941  tracks.empty () &&
942  points.empty () &&
943  playlists.empty () &&
944  lines.empty () &&
945  time.empty () &&
946  playlists.empty () &&
947  markers.empty() &&
948  midi_regions.empty()
949  ;
950 
951  if (!internal_selection) {
952  return object_level_empty;
953  }
954 
955  /* this is intended to really only apply when using a Selection
956  as a cut buffer.
957  */
958 
959  return object_level_empty && midi_notes.empty() && points.empty();
960 }
961 
962 void
964 {
965  clear_time(); //enforce region/object exclusivity
966  clear_tracks(); //enforce object/track exclusivity
967 
968  cp->set_selected (!cp->get_selected ());
969  PointSelection::iterator i = find (points.begin(), points.end(), cp);
970  if (i == points.end()) {
971  points.push_back (cp);
972  } else {
973  points.erase (i);
974  }
975 
976  PointsChanged (); /* EMIT SIGNAL */
977 }
978 
979 void
980 Selection::toggle (vector<ControlPoint*> const & cps)
981 {
982  clear_time(); //enforce region/object exclusivity
983  clear_tracks(); //enforce object/track exclusivity
984 
985  for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
986  toggle (*i);
987  }
988 }
989 
990 void
991 Selection::toggle (list<Selectable*> const & selectables)
992 {
993  clear_time(); //enforce region/object exclusivity
994  clear_tracks(); //enforce object/track exclusivity
995 
996  RegionView* rv;
997  ControlPoint* cp;
998  vector<RegionView*> rvs;
999  vector<ControlPoint*> cps;
1000 
1001  for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1002  if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1003  rvs.push_back (rv);
1004  } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1005  cps.push_back (cp);
1006  } else {
1007  fatal << _("programming error: ")
1008  << X_("unknown selectable type passed to Selection::toggle()")
1009  << endmsg;
1010  abort(); /*NOTREACHED*/
1011  }
1012  }
1013 
1014  if (!rvs.empty()) {
1015  toggle (rvs);
1016  }
1017 
1018  if (!cps.empty()) {
1019  toggle (cps);
1020  }
1021 }
1022 
1023 void
1024 Selection::set (list<Selectable*> const & selectables)
1025 {
1026  clear_time (); //enforce region/object exclusivity
1027  clear_tracks(); //enforce object/track exclusivity
1028  clear_objects ();
1029 
1030  add (selectables);
1031 }
1032 
1033 void
1035 {
1036  clear_time (); //enforce region/object exclusivity
1037  clear_tracks(); //enforce object/track exclusivity
1038 
1039  for (PointSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
1040  points.push_back (*i);
1041  }
1042 }
1043 
1044 void
1045 Selection::add (list<Selectable*> const & selectables)
1046 {
1047  clear_time (); //enforce region/object exclusivity
1048  clear_tracks(); //enforce object/track exclusivity
1049 
1050  RegionView* rv;
1051  ControlPoint* cp;
1052  vector<RegionView*> rvs;
1053  vector<ControlPoint*> cps;
1054 
1055  for (std::list<Selectable*>::const_iterator i = selectables.begin(); i != selectables.end(); ++i) {
1056  if ((rv = dynamic_cast<RegionView*> (*i)) != 0) {
1057  rvs.push_back (rv);
1058  } else if ((cp = dynamic_cast<ControlPoint*> (*i)) != 0) {
1059  cps.push_back (cp);
1060  } else {
1061  fatal << _("programming error: ")
1062  << X_("unknown selectable type passed to Selection::add()")
1063  << endmsg;
1064  abort(); /*NOTREACHED*/
1065  }
1066  }
1067 
1068  if (!rvs.empty()) {
1069  add (rvs);
1070  }
1071 
1072  if (!cps.empty()) {
1073  add (cps);
1074  }
1075 }
1076 
1077 void
1079 {
1080  if (!points.empty()) {
1081  points.clear ();
1082  PointsChanged ();
1083  }
1084 }
1085 
1086 void
1088 {
1089  clear_time (); //enforce region/object exclusivity
1090  clear_tracks(); //enforce object/track exclusivity
1091 
1092  cp->set_selected (true);
1093  points.push_back (cp);
1094  PointsChanged (); /* EMIT SIGNAL */
1095 }
1096 
1097 void
1098 Selection::add (vector<ControlPoint*> const & cps)
1099 {
1100  clear_time (); //enforce region/object exclusivity
1101  clear_tracks(); //enforce object/track exclusivity
1102 
1103  for (vector<ControlPoint*>::const_iterator i = cps.begin(); i != cps.end(); ++i) {
1104  (*i)->set_selected (true);
1105  points.push_back (*i);
1106  }
1107  PointsChanged (); /* EMIT SIGNAL */
1108 }
1109 
1110 void
1112 {
1113  clear_time (); //enforce region/object exclusivity
1114  clear_tracks(); //enforce object/track exclusivity
1115 
1116  if (cp->get_selected()) {
1117  return;
1118  }
1119 
1120  for (uint32_t i = 0; i < cp->line().npoints(); ++i) {
1121  cp->line().nth (i)->set_selected (false);
1122  }
1123 
1124  clear_objects ();
1125  add (cp);
1126 }
1127 
1128 void
1130 {
1131  clear_time (); //enforce region/object exclusivity
1132  clear_tracks(); //enforce object/track exclusivity
1133  markers.clear ();
1134 
1135  add (m);
1136 }
1137 
1138 void
1140 {
1141  MarkerSelection::iterator i;
1142 
1143  if ((i = find (markers.begin(), markers.end(), m)) == markers.end()) {
1144  add (m);
1145  } else {
1146  remove (m);
1147  }
1148 }
1149 
1150 void
1152 {
1153  MarkerSelection::iterator i;
1154 
1155  if ((i = find (markers.begin(), markers.end(), m)) != markers.end()) {
1156  markers.erase (i);
1157  MarkersChanged();
1158  }
1159 }
1160 
1161 void
1163 {
1164  clear_time (); //enforce region/object exclusivity
1165  clear_tracks(); //enforce object/track exclusivity
1166 
1167  if (find (markers.begin(), markers.end(), m) == markers.end()) {
1168  markers.push_back (m);
1169  MarkersChanged();
1170  }
1171 }
1172 
1173 void
1174 Selection::add (const list<Marker*>& m)
1175 {
1176  clear_time (); //enforce region/object exclusivity
1177  clear_tracks(); //enforce object/track exclusivity
1178 
1179  markers.insert (markers.end(), m.begin(), m.end());
1180  markers.sort ();
1181  markers.unique ();
1182 
1183  MarkersChanged ();
1184 }
1185 
1186 void
1188 {
1189  s = max_framepos;
1190  e = 0;
1191 
1192  for (MarkerSelection::iterator i = begin(); i != end(); ++i) {
1193 
1194  if ((*i)->position() < s) {
1195  s = (*i)->position();
1196  }
1197 
1198  if ((*i)->position() > e) {
1199  e = (*i)->position();
1200  }
1201  }
1202 
1203  s = std::min (s, e);
1204  e = std::max (s, e);
1205 }
1206 
1207 XMLNode&
1209 {
1210  /* XXX: not complete; just sufficient to get track selection state
1211  so that re-opening plugin windows for editor mixer strips works
1212  */
1213 
1214  char buf[32];
1215  XMLNode* node = new XMLNode (X_("Selection"));
1216 
1217  for (TrackSelection::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
1218  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1219  AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (*i);
1220  if (rtv) {
1221  XMLNode* t = node->add_child (X_("RouteView"));
1222  t->add_property (X_("id"), atoi (rtv->route()->id().to_s().c_str()));
1223  } else if (atv) {
1224  XMLNode* t = node->add_child (X_("AutomationView"));
1225  t->add_property (X_("id"), atoi (atv->parent_route()->id().to_s().c_str()));
1226  t->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (atv->parameter ()));
1227  }
1228  }
1229 
1230  for (RegionSelection::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1231  XMLNode* r = node->add_child (X_("Region"));
1232  r->add_property (X_("id"), atoi ((*i)->region ()->id ().to_s ().c_str()));
1233  }
1234 
1235  /* midi region views have thir own internal selection. */
1236  XMLNode* n = NULL;
1237  list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > rid_notes;
1239  if (!rid_notes.empty()) {
1240  n = node->add_child (X_("MIDINote"));
1241  }
1242  list<pair<PBD::ID, std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rn_it;
1243  for (rn_it = rid_notes.begin(); rn_it != rid_notes.end(); ++rn_it) {
1244  assert(n); // hint for clang static analysis
1245  n->add_property (X_("region_id"), atoi((*rn_it).first.to_s().c_str()));
1246 
1247  for (std::set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > >::iterator i = (*rn_it).second.begin(); i != (*rn_it).second.end(); ++i) {
1248  XMLNode* nc = n->add_child(X_("note"));
1249  snprintf(buf, sizeof(buf), "%d", (*i)->channel());
1250  nc->add_property(X_("channel"), string(buf));
1251 
1252  snprintf(buf, sizeof(buf), "%f", (*i)->time().to_double());
1253  nc->add_property(X_("time"), string(buf));
1254 
1255  snprintf(buf, sizeof(buf), "%d", (*i)->note());
1256  nc->add_property(X_("note"), string(buf));
1257 
1258  snprintf(buf, sizeof(buf), "%f", (*i)->length().to_double());
1259  nc->add_property(X_("length"), string(buf));
1260 
1261  snprintf(buf, sizeof(buf), "%d", (*i)->velocity());
1262  nc->add_property(X_("velocity"), string(buf));
1263 
1264  snprintf(buf, sizeof(buf), "%d", (*i)->off_velocity());
1265  nc->add_property(X_("off-velocity"), string(buf));
1266  }
1267  }
1268 
1269  for (PointSelection::const_iterator i = points.begin(); i != points.end(); ++i) {
1270  AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (&(*i)->line().trackview);
1271  if (atv) {
1272 
1273  XMLNode* r = node->add_child (X_("ControlPoint"));
1274  r->add_property (X_("type"), "track");
1275  r->add_property (X_("route-id"), atoi (atv->parent_route()->id ().to_s ().c_str()));
1276  r->add_property (X_("automation-list-id"), atoi ((*i)->line().the_list()->id ().to_s ().c_str()));
1277  r->add_property (X_("parameter"), EventTypeMap::instance().to_symbol ((*i)->line().the_list()->parameter ()));
1278 
1279  snprintf(buf, sizeof(buf), "%d", (*i)->view_index());
1280  r->add_property (X_("view-index"), string(buf));
1281 
1282  }
1283  }
1284 
1285  for (TimeSelection::const_iterator i = time.begin(); i != time.end(); ++i) {
1286  XMLNode* t = node->add_child (X_("AudioRange"));
1287  snprintf(buf, sizeof(buf), "%" PRId64, (*i).start);
1288  t->add_property (X_("start"), string(buf));
1289  snprintf(buf, sizeof(buf), "%" PRId64, (*i).end);
1290  t->add_property (X_("end"), string(buf));
1291  }
1292 
1293  for (MarkerSelection::const_iterator i = markers.begin(); i != markers.end(); ++i) {
1294  XMLNode* t = node->add_child (X_("Marker"));
1295 
1296  bool is_start;
1297  Location* loc = editor->find_location_from_marker (*i, is_start);
1298 
1299  t->add_property (X_("id"), atoi (loc->id().to_s().c_str()));
1300  t->add_property (X_("start"), is_start ? X_("yes") : X_("no"));
1301  }
1302 
1303  return *node;
1304 }
1305 
1306 int
1307 Selection::set_state (XMLNode const & node, int)
1308 {
1309  if (node.name() != X_("Selection")) {
1310  return -1;
1311  }
1312 
1313  clear_regions ();
1314  clear_midi_notes ();
1315  clear_points ();
1316  clear_time ();
1317  clear_tracks ();
1318  clear_markers ();
1319 
1320  XMLNodeList children = node.children ();
1321  for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
1322  if ((*i)->name() == X_("RouteView")) {
1323 
1324  XMLProperty* prop_id = (*i)->property (X_("id"));
1325  assert (prop_id);
1326  PBD::ID id (prop_id->value ());
1328  if (rtv) {
1329  add (rtv);
1330  }
1331 
1332  } else if ((*i)->name() == X_("Region")) {
1333  XMLProperty* prop_id = (*i)->property (X_("id"));
1334  assert (prop_id);
1335  PBD::ID id (prop_id->value ());
1336 
1337  RegionSelection rs;
1338  editor->get_regionviews_by_id (id, rs);
1339 
1340  if (!rs.empty ()) {
1341  add (rs);
1342  } else {
1343  /*
1344  regionviews haven't been constructed - stash the region IDs
1345  so we can identify them in Editor::region_view_added ()
1346  */
1347  regions.pending.push_back (id);
1348  }
1349 
1350  } else if ((*i)->name() == X_("MIDINote")) {
1351  XMLProperty* prop_region_id = (*i)->property (X_("region-id"));
1352 
1353  assert (prop_region_id);
1354 
1355  PBD::ID const id (prop_region_id->value ());
1356  RegionSelection rs;
1357 
1358  editor->get_regionviews_by_id (id, rs); // there could be more than one
1359 
1360  std::list<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > notes;
1361  XMLNodeList children = (*i)->children ();
1362 
1363  for (XMLNodeList::const_iterator ci = children.begin(); ci != children.end(); ++ci) {
1364  XMLProperty* prop_channel = (*ci)->property (X_("channel"));
1365  XMLProperty* prop_time = (*ci)->property (X_("time"));
1366  XMLProperty* prop_note = (*ci)->property (X_("note"));
1367  XMLProperty* prop_length = (*ci)->property (X_("length"));
1368  XMLProperty* prop_velocity = (*ci)->property (X_("velocity"));
1369  XMLProperty* prop_off_velocity = (*ci)->property (X_("off-velocity"));
1370 
1371  assert (prop_channel);
1372  assert (prop_time);
1373  assert (prop_note);
1374  assert (prop_length);
1375  assert (prop_velocity);
1376  assert (prop_off_velocity);
1377 
1378  uint8_t channel = atoi(prop_channel->value());
1379  Evoral::Beats time (atof(prop_time->value()));
1380  Evoral::Beats length (atof(prop_length->value()));
1381  uint8_t note = atoi(prop_note->value());
1382  uint8_t velocity = atoi(prop_velocity->value());
1383  uint8_t off_velocity = atoi(prop_off_velocity->value());
1385  (new Evoral::Note<Evoral::Beats> (channel, time, length, note, velocity));
1386  the_note->set_off_velocity (off_velocity);
1387 
1388  notes.push_back (the_note);
1389  }
1390 
1391  for (RegionSelection::iterator rsi = rs.begin(); rsi != rs.end(); ++rsi) {
1392  MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*rsi);
1393  if (mrv) {
1394  mrv->select_notes(notes);
1395  }
1396  }
1397 
1398  if (rs.empty()) {
1399  /* regionviews containing these notes don't yet exist on the canvas.*/
1400  pending_midi_note_selection.push_back (make_pair (id, notes));
1401  }
1402 
1403  } else if ((*i)->name() == X_("ControlPoint")) {
1404  XMLProperty* prop_type = (*i)->property (X_("type"));
1405 
1406  assert(prop_type);
1407 
1408  if (prop_type->value () == "track") {
1409 
1410  XMLProperty* prop_route_id = (*i)->property (X_("route-id"));
1411  XMLProperty* prop_alist_id = (*i)->property (X_("automation-list-id"));
1412  XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
1413  XMLProperty* prop_view_index = (*i)->property (X_("view-index"));
1414 
1415  assert (prop_type);
1416  assert (prop_route_id);
1417  assert (prop_alist_id);
1418  assert (prop_parameter);
1419  assert (prop_view_index);
1420 
1421  PBD::ID route_id (prop_route_id->value ());
1423  vector <ControlPoint *> cps;
1424 
1425  if (rtv) {
1426  boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1427  if (atv) {
1428  list<boost::shared_ptr<AutomationLine> > lines = atv->lines();
1429  for (list<boost::shared_ptr<AutomationLine> > ::iterator li = lines.begin(); li != lines.end(); ++li) {
1430  if ((*li)->the_list()->id() == prop_alist_id->value()) {
1431  ControlPoint* cp = (*li)->nth(atol(prop_view_index->value().c_str()));
1432  if (cp) {
1433  cps.push_back (cp);
1434  cp->show();
1435  }
1436  }
1437  }
1438  }
1439  }
1440  if (!cps.empty()) {
1441  add (cps);
1442  }
1443  }
1444 
1445  } else if ((*i)->name() == X_("AudioRange")) {
1446  XMLProperty* prop_start = (*i)->property (X_("start"));
1447  XMLProperty* prop_end = (*i)->property (X_("end"));
1448 
1449  assert (prop_start);
1450  assert (prop_end);
1451 
1452  framepos_t s (atol (prop_start->value ().c_str()));
1453  framepos_t e (atol (prop_end->value ().c_str()));
1454 
1456 
1457  } else if ((*i)->name() == X_("AutomationView")) {
1458 
1459  XMLProperty* prop_id = (*i)->property (X_("id"));
1460  XMLProperty* prop_parameter = (*i)->property (X_("parameter"));
1461 
1462  assert (prop_id);
1463  assert (prop_parameter);
1464 
1465  PBD::ID id (prop_id->value ());
1467 
1468  if (rtv) {
1469  boost::shared_ptr<AutomationTimeAxisView> atv = rtv->automation_child (EventTypeMap::instance().from_symbol (prop_parameter->value ()));
1470 
1471  /* the automation could be for an entity that was never saved
1472  in the session file. Don't freak out if we can't find
1473  it.
1474  */
1475 
1476  if (atv) {
1477  add (atv.get());
1478  }
1479  }
1480 
1481  } else if ((*i)->name() == X_("Marker")) {
1482 
1483  XMLProperty* prop_id = (*i)->property (X_("id"));
1484  XMLProperty* prop_start = (*i)->property (X_("start"));
1485  assert (prop_id);
1486  assert (prop_start);
1487 
1488  PBD::ID id (prop_id->value ());
1490  if (m) {
1491  add (m);
1492  }
1493 
1494  }
1495 
1496  }
1497 
1498  return 0;
1499 }
1500 
1501 void
1503 {
1504  RegionSelection::iterator i = regions.begin();
1505  while (i != regions.end ()) {
1506  RegionSelection::iterator tmp = i;
1507  ++tmp;
1508 
1509  if (&(*i)->get_time_axis_view() == t) {
1510  remove (*i);
1511  }
1512 
1513  i = tmp;
1514  }
1515 }
1516 
1517 void
1519 {
1520  _no_tracks_changed = yn;
1521 }
static PBD::Signal1< void, TimeAxisView * > CatchDeletion
bool add(RegionView *)
PBD::Signal0< void > ClearMidiNoteSelection
Definition: selection.h:224
LIBPBD_API Transmitter fatal
ControlPoint * nth(uint32_t)
void move_time(framecnt_t)
Definition: selection.cc:548
std::list< PBD::ID > pending
int atoi(const string &s)
Definition: convert.cc:140
void clear_regions()
Definition: selection.cc:159
void remove_regions(TimeAxisView *)
Definition: selection.cc:1502
AutomationSelection lines
Definition: selection.h:84
const std::string & value() const
Definition: xml++.h:159
bool remove(RegionView *)
void remove(TimeAxisView *)
Definition: selection.cc:603
boost::shared_ptr< ARDOUR::Route > parent_route()
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
virtual void get_regionviews_by_id(PBD::ID const id, RegionSelection &regions) const =0
AutomationLine & line() const
Definition: control_point.h:81
const std::string & name() const
Definition: xml++.h:104
void clear()
Definition: selection.cc:101
Lists of selected things.
Definition: selection.h:66
sigc::signal< void > PlaylistsChanged
Definition: selection.h:101
Representation of the interface of the Editor class.
Definition: marker.h:41
MarkerSelection markers
Definition: selection.h:87
void set(std::list< Selectable * > const &)
Definition: selection.cc:1024
sigc::signal< void > MarkersChanged
Definition: selection.h:103
Definition: Beats.hpp:239
std::list< boost::shared_ptr< AutomationLine > > lines() const
void block_tracks_changed(bool)
Definition: selection.cc:1518
LIBPBD_API Transmitter warning
uint32_t npoints() const
const XMLNodeList & children(const std::string &str=std::string()) const
Definition: xml++.cc:329
static PBD::Signal1< void, Marker * > CatchDeletion
Definition: marker.h:64
std::list< std::pair< PBD::ID const, std::list< boost::shared_ptr< Evoral::Note< Evoral::Beats > > > > > pending_midi_note_selection
Definition: selection.h:226
sigc::signal< void > TimeChanged
Definition: selection.h:99
void range(ARDOUR::framepos_t &start, ARDOUR::framepos_t &end)
Definition: selection.cc:1187
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
XMLNode * add_child(const char *)
Definition: xml++.cc:351
void set_preserving_all_ranges(framepos_t, framepos_t)
Definition: selection.cc:882
sigc::signal< void > MidiRegionsChanged
Definition: selection.h:105
void clear_playlists()
Definition: selection.cc:193
bool empty(bool internal_selection=false)
Definition: selection.cc:938
Definition: id.h:32
bool operator==(const Selection &a, const Selection &b)
Definition: selection.cc:88
static PBD::Signal1< void, ControlPoint * > CatchDeletion
Definition: control_point.h:83
std::list< XMLNode * > XMLNodeList
Definition: xml++.h:44
Selection(PublicEditor const *e)
Definition: selection.cc:51
void clear_time()
Definition: selection.cc:141
#define _(Text)
Definition: i18n.h:11
void add(std::list< Selectable * > const &)
Definition: selection.cc:1045
int32_t atol(const string &s)
Definition: convert.cc:146
void toggle(std::list< Selectable * > const &)
Definition: selection.cc:991
#define X_(Text)
Definition: i18n.h:13
int64_t framecnt_t
Definition: types.h:76
void replace(uint32_t time_index, framepos_t start, framepos_t end)
Definition: selection.cc:563
Evoral::Parameter parameter() const
bool string_is_affirmative(const std::string &str)
Definition: convert.cc:282
Definition: amp.h:29
TrackViewList add(TrackViewList const &)
const PBD::ID & id() const
Definition: stateful.h:68
std::string to_s() const
Definition: id.cc:78
#define gui_context()
Definition: gui_thread.h:36
MidiNoteSelection midi_notes
Definition: selection.h:91
void clear_points()
Definition: selection.cc:1078
MidiRegionSelection midi_regions
Definition: selection.h:88
RegionSelection regions
Definition: selection.h:82
int64_t framepos_t
Definition: types.h:66
virtual ARDOUR::Location * find_location_from_marker(Marker *, bool &) const =0
bool _no_tracks_changed
Definition: selection.h:231
bool get_selected() const
Definition: selectable.h:40
PlaylistSelection playlists
Definition: selection.h:85
T * get() const
Definition: shared_ptr.hpp:268
virtual RouteTimeAxisView * get_route_view_by_route_id(const PBD::ID &id) const =0
virtual Marker * find_marker_from_location_id(PBD::ID const &, bool) const =0
bool selected(TimeAxisView *)
Definition: selection.cc:920
XMLProperty * add_property(const char *name, const std::string &value)
framepos_t start
Definition: types.h:289
void clear_objects()
Definition: selection.cc:116
PointSelection points
Definition: selection.h:86
ARDOUR::framepos_t start()
virtual void get_per_region_note_selection(std::list< std::pair< PBD::ID, std::set< boost::shared_ptr< Evoral::Note< Evoral::Beats > > > > > &) const =0
Definition: xml++.h:95
sigc::signal< void > PointsChanged
Definition: selection.h:102
void clear_tracks()
Definition: selection.cc:127
TimeSelection time
Definition: selection.h:83
TrackSelection tracks
Definition: selection.h:81
XMLNode & get_state() const
Definition: selection.cc:1208
Definition: debug.h:30
sigc::signal< void > LinesChanged
Definition: selection.h:100
sigc::signal< void > MidiNotesChanged
Definition: selection.h:104
void set_selected(bool)
void clear_lines()
Definition: selection.cc:210
static const framepos_t max_framepos
Definition: types.h:78
sigc::signal< void > TracksChanged
Definition: selection.h:98
void clear_midi_notes()
Definition: selection.cc:168
bool operator()(AudioRange a, AudioRange b)
Definition: selection.cc:46
void select_notes(std::list< boost::shared_ptr< NoteType > >)
uint32_t next_time_id
Definition: selection.h:230
sigc::signal< void > RegionsChanged
Definition: selection.h:97
virtual void set_selected(bool yn)
Definition: selectable.h:34
PublicEditor const * editor
Definition: selection.h:229
#define MISSING_INVALIDATOR
Definition: event_loop.h:86
void clear_midi_regions()
Definition: selection.cc:184
boost::shared_ptr< ARDOUR::Route > route() const
Definition: route_ui.h:76
void dump_region_layers()
Definition: selection.cc:149
double atof(const string &s)
Definition: convert.cc:158
void clear_markers()
Definition: selection.cc:219
LIBARDOUR_API PBD::PropertyDescriptor< framecnt_t > length
Definition: region.cc:64
int set_state(XMLNode const &, int)
Definition: selection.cc:1307