sort of no-op ... remove debug output ... just basically put it in git so that i...
[ardour.git] / libs / ardour / selection.cc
1 /*
2   Copyright (C) 2017 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 <vector>
21
22 #include "pbd/compose.h"
23 #include "pbd/signals.h"
24
25 #include "ardour/automation_control.h"
26 #include "ardour/debug.h"
27 #include "ardour/selection.h"
28 #include "ardour/session.h"
29 #include "ardour/stripable.h"
30
31 using namespace ARDOUR;
32 using namespace PBD;
33
34 void
35 CoreSelection::send_selection_change ()
36 {
37         PropertyChange pc;
38         pc.add (Properties::selected);
39         PresentationInfo::send_static_change (pc);
40 }
41
42 CoreSelection::CoreSelection (Session& s)
43         : session (s)
44         , selection_order (0)
45 {
46 }
47
48 CoreSelection::~CoreSelection ()
49 {
50 }
51
52 void
53 CoreSelection::toggle (boost::shared_ptr<Stripable> s, boost::shared_ptr<AutomationControl> c)
54 {
55         DEBUG_TRACE (DEBUG::Selection, string_compose ("toggle: s %1 selected %2 c %3 selected %4\n",
56                                                        s, selected (s), c, selected (c)));
57         if ((c && selected (c)) || selected (s)) {
58                 remove (s, c);
59         } else {
60                 add (s, c);
61         }
62 }
63
64 void
65 CoreSelection::add (boost::shared_ptr<Stripable> s, boost::shared_ptr<AutomationControl> c)
66 {
67         bool send = false;
68
69         {
70                 Glib::Threads::RWLock::WriterLock lm (_lock);
71
72                 SelectedStripable ss (s, c, g_atomic_int_add (&selection_order, 1));
73
74                 if (_stripables.insert (ss).second) {
75                         DEBUG_TRACE (DEBUG::Selection, string_compose ("added %1/%2 to s/c selection\n", s->name(), c));
76                         send = true;
77                 } else {
78                         DEBUG_TRACE (DEBUG::Selection, string_compose ("%1/%2 already in s/c selection\n", s->name(), c));
79                 }
80         }
81
82         if (send) {
83                 send_selection_change ();
84                 /* send per-object signal to notify interested parties
85                    the selection status has changed
86                 */
87                 if (s) {
88                         PropertyChange pc (Properties::selected);
89                         s->presentation_info().PropertyChanged (pc);
90                 }
91         }
92 }
93
94 void
95 CoreSelection::remove (boost::shared_ptr<Stripable> s, boost::shared_ptr<AutomationControl> c)
96 {
97         bool send = false;
98         {
99                 Glib::Threads::RWLock::WriterLock lm (_lock);
100
101                 SelectedStripable ss (s, c, 0);
102
103                 SelectedStripables::iterator i = _stripables.find (ss);
104
105                 if (i != _stripables.end()) {
106                         _stripables.erase (i);
107                         DEBUG_TRACE (DEBUG::Selection, string_compose ("removed %1/%2 from s/c selection\n", s, c));
108                         send = true;
109                 }
110         }
111
112         if (send) {
113                 send_selection_change ();
114                 /* send per-object signal to notify interested parties
115                    the selection status has changed
116                 */
117                 if (s) {
118                         PropertyChange pc (Properties::selected);
119                         s->presentation_info().PropertyChanged (pc);
120                 }
121         }
122 }
123
124 void
125 CoreSelection::set (boost::shared_ptr<Stripable> s, boost::shared_ptr<AutomationControl> c)
126 {
127         {
128                 Glib::Threads::RWLock::WriterLock lm (_lock);
129
130                 SelectedStripable ss (s, c, g_atomic_int_add (&selection_order, 1));
131
132                 if (_stripables.size() == 1 && _stripables.find (ss) != _stripables.end()) {
133                         return;
134                 }
135
136                 _stripables.clear ();
137                 _stripables.insert (ss);
138                 DEBUG_TRACE (DEBUG::Selection, string_compose ("set s/c selection to %1/%2\n", s->name(), c));
139         }
140
141         send_selection_change ();
142
143         /* send per-object signal to notify interested parties
144            the selection status has changed
145         */
146         if (s) {
147                 PropertyChange pc (Properties::selected);
148                 s->presentation_info().PropertyChanged (pc);
149         }
150 }
151
152 void
153 CoreSelection::clear_stripables ()
154 {
155         bool send = false;
156         std::vector<boost::shared_ptr<Stripable> > s;
157
158         DEBUG_TRACE (DEBUG::Selection, "clearing s/c selection\n");
159         {
160                 Glib::Threads::RWLock::WriterLock lm (_lock);
161
162                 if (!_stripables.empty()) {
163
164                         s.reserve (_stripables.size());
165
166                         for (SelectedStripables::const_iterator x = _stripables.begin(); x != _stripables.end(); ++x) {
167                                 boost::shared_ptr<Stripable> sp = session.stripable_by_id ((*x).stripable);
168                                 if (sp) {
169                                         s.push_back (sp);
170                                 }
171                         }
172
173                         _stripables.clear ();
174
175                         send = true;
176                         DEBUG_TRACE (DEBUG::Selection, "cleared s/c selection\n");
177                 }
178         }
179
180         if (send) {
181                 send_selection_change ();
182
183                 PropertyChange pc (Properties::selected);
184
185                 for (std::vector<boost::shared_ptr<Stripable> >::iterator ss = s.begin(); ss != s.end(); ++ss) {
186                         (*ss)->presentation_info().PropertyChanged (pc);
187                 }
188
189         }
190 }
191
192 bool
193 CoreSelection::selected (boost::shared_ptr<const Stripable> s) const
194 {
195         if (!s) {
196                 return false;
197         }
198
199         Glib::Threads::RWLock::ReaderLock lm (_lock);
200
201         for (SelectedStripables::const_iterator x = _stripables.begin(); x != _stripables.end(); ++x) {
202
203                 if (!((*x).controllable == 0)) {
204                         /* selected automation control */
205                         continue;
206                 }
207
208                 /* stripable itself selected, not just a control belonging to
209                  * it
210                  */
211
212                 if ((*x).stripable == s->id()) {
213                         return true;
214                 }
215         }
216
217         return false;
218 }
219
220 bool
221 CoreSelection::selected (boost::shared_ptr<const AutomationControl> c) const
222 {
223         if (!c) {
224                 return false;
225         }
226
227         Glib::Threads::RWLock::ReaderLock lm (_lock);
228
229         for (SelectedStripables::const_iterator x = _stripables.begin(); x != _stripables.end(); ++x) {
230                 if ((*x).controllable == c->id()) {
231                         return true;
232                 }
233         }
234
235         return false;
236 }
237
238 CoreSelection::SelectedStripable::SelectedStripable (boost::shared_ptr<Stripable> s, boost::shared_ptr<AutomationControl> c, int o)
239         : stripable (s ? s->id() : PBD::ID (0))
240         , controllable (c ? c->id() : PBD::ID (0))
241         , order (o)
242 {
243 }
244
245 struct StripableControllerSort {
246         bool operator() (CoreSelection::StripableAutomationControl const &a, CoreSelection::StripableAutomationControl const & b) const {
247                 return a.order < b.order;
248         }
249 };
250
251 void
252 CoreSelection::get_stripables (StripableAutomationControls& sc) const
253 {
254         Glib::Threads::RWLock::ReaderLock lm (_lock);
255
256         for (SelectedStripables::const_iterator x = _stripables.begin(); x != _stripables.end(); ++x) {
257
258                 boost::shared_ptr<Stripable> s = session.stripable_by_id ((*x).stripable);
259                 boost::shared_ptr<AutomationControl> c;
260
261                 if (!s) {
262                         /* some global automation control, not owned by a Stripable */
263                         c = session.automation_control_by_id ((*x).controllable);
264                 } else {
265                         /* automation control owned by a Stripable or one of its children */
266                         c = s->automation_control_recurse ((*x).controllable);
267                 }
268
269                 if (s || c) {
270                         sc.push_back (StripableAutomationControl (s, c, (*x).order));
271                 }
272         }
273
274         StripableControllerSort cmp;
275         sort (sc.begin(), sc.end(), cmp);
276 }
277
278 void
279 CoreSelection::remove_control_by_id (PBD::ID const & id)
280 {
281         Glib::Threads::RWLock::WriterLock lm (_lock);
282
283         for (SelectedStripables::iterator x = _stripables.begin(); x != _stripables.end(); ++x) {
284                 if ((*x).controllable == id) {
285                         _stripables.erase (x);
286                         return;
287                 }
288         }
289 }
290
291 void
292 CoreSelection::remove_stripable_by_id (PBD::ID const & id)
293 {
294         Glib::Threads::RWLock::WriterLock lm (_lock);
295
296         for (SelectedStripables::iterator x = _stripables.begin(); x != _stripables.end(); ) {
297                 if ((*x).stripable == id) {
298                         _stripables.erase (x++);
299                         /* keep going because there may be more than 1 pair of
300                            stripable/automation-control in the selection.
301                         */
302                 } else {
303                         ++x;
304                 }
305         }
306 }
307
308 XMLNode&
309 CoreSelection::get_state (void)
310 {
311         XMLNode* node = new XMLNode (X_("Selection"));
312
313         Glib::Threads::RWLock::WriterLock lm (_lock);
314
315         for (SelectedStripables::const_iterator x = _stripables.begin(); x != _stripables.end(); ++x) {
316                 XMLNode* child = new XMLNode (X_("StripableAutomationControl"));
317                 child->set_property (X_("stripable"), (*x).stripable.to_s());
318                 child->set_property (X_("control"), (*x).controllable.to_s());
319                 child->set_property (X_("order"), (*x).order);
320
321                 node->add_child_nocopy (*child);
322         }
323
324         return *node;
325 }
326 int
327 CoreSelection::set_state (const XMLNode& node, int /* version */)
328 {
329         XMLNodeList children (node.children());
330         Glib::Threads::RWLock::WriterLock lm (_lock);
331
332         _stripables.clear ();
333
334         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
335                 if ((*i)->name() != X_("StripableAutomationControl")) {
336                         continue;
337                 }
338
339                 std::string s;
340
341                 if (!(*i)->get_property (X_("stripable"), s)) {
342                         continue;
343                 }
344
345                 std::string c;
346
347                 if (!(*i)->get_property (X_("control"), c)) {
348                         continue;
349                 }
350
351                 int order;
352
353                 if (!(*i)->get_property (X_("order"), order)) {
354                         continue;
355                 }
356
357                 SelectedStripable ss (PBD::ID (s), PBD::ID (c), order);
358                 _stripables.insert (ss);
359         }
360
361         return 0;
362 }
363
364 uint32_t
365 CoreSelection::selected () const
366 {
367         Glib::Threads::RWLock::ReaderLock lm (_lock);
368         return _stripables.size();
369 }