restore/extend/simplify ControlProtocol API to allow tracking of selection
authorPaul Davis <paul@linuxaudiosystems.com>
Wed, 6 Jul 2016 17:36:55 +0000 (13:36 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Wed, 6 Jul 2016 17:39:10 +0000 (13:39 -0400)
libs/surfaces/control_protocol/control_protocol.cc
libs/surfaces/control_protocol/control_protocol/control_protocol.h
libs/surfaces/generic_midi/midifunction.cc
libs/surfaces/mackie/mackie_control_protocol.cc
libs/surfaces/mackie/mackie_control_protocol.h
libs/surfaces/osc/osc.cc

index f1b2f9f112b97f9cf3a434df2e5d520066a3cf6c..4d578409c33c8af57f0b880725e8f849d1a4a898 100644 (file)
@@ -49,8 +49,20 @@ PBD::Signal0<void> ControlProtocol::VerticalZoomOutSelected;
 PBD::Signal0<void>          ControlProtocol::StepTracksDown;
 PBD::Signal0<void>          ControlProtocol::StepTracksUp;
 
+PBD::Signal1<void,boost::shared_ptr<ARDOUR::Stripable> > ControlProtocol::AddStripableToSelection;
+PBD::Signal1<void,boost::shared_ptr<ARDOUR::Stripable> > ControlProtocol::SetStripableSelection;
+PBD::Signal1<void,boost::shared_ptr<ARDOUR::Stripable> > ControlProtocol::ToggleStripableSelection;
+PBD::Signal1<void,boost::shared_ptr<ARDOUR::Stripable> > ControlProtocol::RemoveStripableFromSelection;
+PBD::Signal0<void>          ControlProtocol::ClearStripableSelection;
+
 PBD::Signal1<void,StripableNotificationListPtr> ControlProtocol::StripableSelectionChanged;
 
+Glib::Threads::Mutex ControlProtocol::first_selected_mutex;
+boost::weak_ptr<Stripable> ControlProtocol::_first_selected_stripable;
+StripableNotificationList ControlProtocol::_last_selected;
+bool ControlProtocol::selection_connected = false;
+PBD::ScopedConnection ControlProtocol::selection_connection;
+
 const std::string ControlProtocol::state_node_name ("Protocol");
 
 ControlProtocol::ControlProtocol (Session& s, string str)
@@ -58,6 +70,12 @@ ControlProtocol::ControlProtocol (Session& s, string str)
        , _name (str)
        , _active (false)
 {
+       if (!selection_connected) {
+               /* this is all static, connect it only once (and early), for all ControlProtocols */
+
+               StripableSelectionChanged.connect_same_thread (selection_connection, boost::bind (&ControlProtocol::stripable_selection_changed, _1));
+               selection_connected = true;
+       }
 }
 
 ControlProtocol::~ControlProtocol ()
@@ -324,3 +342,35 @@ ControlProtocol::set_state (XMLNode const & node, int /* version */)
 
        return 0;
 }
+
+boost::shared_ptr<Stripable>
+ControlProtocol::first_selected_stripable ()
+{
+       Glib::Threads::Mutex::Lock lm (first_selected_mutex);
+       return _first_selected_stripable.lock();
+}
+
+void
+ControlProtocol::set_first_selected_stripable (boost::shared_ptr<Stripable> s)
+{
+       Glib::Threads::Mutex::Lock lm (first_selected_mutex);
+       _first_selected_stripable = s;
+}
+
+void
+ControlProtocol::stripable_selection_changed (StripableNotificationListPtr sp)
+{
+       _last_selected = *sp;
+
+       {
+               Glib::Threads::Mutex::Lock lm (first_selected_mutex);
+
+               if (!_last_selected.empty()) {
+                       _first_selected_stripable = _last_selected.front().lock();
+               } else {
+                       _first_selected_stripable = boost::weak_ptr<Stripable>();
+               }
+       }
+
+       cerr << "CP: selection now " << _last_selected.size() << endl;
+}
index 496ba990837f85490b35c1f2b4271f0a9c5d3e22..2485ca84ac892af336fbd0206d422d7f9071700e 100644 (file)
@@ -39,6 +39,7 @@ namespace ARDOUR {
 class Route;
 class Session;
 class Bundle;
+class Stripable;
 
 class LIBCONTROLCP_API ControlProtocol : public PBD::Stateful, public PBD::ScopedConnectionList, public BasicUI
 {
@@ -78,6 +79,12 @@ class LIBCONTROLCP_API ControlProtocol : public PBD::Stateful, public PBD::Scope
        static PBD::Signal0<void> StepTracksDown;
        static PBD::Signal0<void> StepTracksUp;
 
+       static PBD::Signal1<void,boost::shared_ptr<ARDOUR::Stripable> > AddStripableToSelection;
+       static PBD::Signal1<void,boost::shared_ptr<ARDOUR::Stripable> > SetStripableSelection;
+       static PBD::Signal1<void,boost::shared_ptr<ARDOUR::Stripable> > ToggleStripableSelection;
+       static PBD::Signal1<void,boost::shared_ptr<ARDOUR::Stripable> > RemoveStripableFromSelection;
+       static PBD::Signal0<void>          ClearStripableSelection;
+
        /* signals that one UI (e.g. the GUI) can emit to get all other UI's to
           respond. Typically this will always be GUI->"others" - the GUI pays
           no attention to these signals.
@@ -85,6 +92,9 @@ class LIBCONTROLCP_API ControlProtocol : public PBD::Stateful, public PBD::Scope
 
        static PBD::Signal1<void,StripableNotificationListPtr> StripableSelectionChanged;
 
+       static boost::shared_ptr<ARDOUR::Stripable> first_selected_stripable ();
+       static void set_first_selected_stripable (boost::shared_ptr<ARDOUR::Stripable>);
+
        /* the model here is as follows:
 
           we imagine most control surfaces being able to control
@@ -132,6 +142,7 @@ class LIBCONTROLCP_API ControlProtocol : public PBD::Stateful, public PBD::Scope
         int set_state (XMLNode const &, int version);
 
         static const std::string state_node_name;
+        static StripableNotificationList const & last_selected() { return _last_selected; }
 
   protected:
        std::vector<boost::shared_ptr<ARDOUR::Route> > route_table;
@@ -143,6 +154,14 @@ class LIBCONTROLCP_API ControlProtocol : public PBD::Stateful, public PBD::Scope
   private:
        LIBCONTROLCP_LOCAL ControlProtocol (const ControlProtocol&); /* noncopyable */
         bool _active;
+
+
+        static Glib::Threads::Mutex first_selected_mutex;
+        static boost::weak_ptr<ARDOUR::Stripable> _first_selected_stripable;
+        static StripableNotificationList _last_selected;
+        static void stripable_selection_changed (StripableNotificationListPtr);
+        static bool selection_connected;
+        static PBD::ScopedConnection selection_connection;
 };
 
 extern "C" {
index 4d2b19fdfd9d563194fe6ddd52d010af110d2eaf..3a60f5396102089b52f2bdb5fa58119575cebde3 100644 (file)
@@ -167,7 +167,8 @@ MIDIFunction::execute ()
                if (!_argument.empty()) {
                        uint32_t rid;
                        sscanf (_argument.c_str(), "%d", &rid);
-                       _ui->toggle_selection (rid, ARDOUR::PresentationInfo::Flag (ARDOUR::PresentationInfo::Route|ARDOUR::PresentationInfo::VCA));
+                       // XX fix me ... need to get stripable, not RID
+                       //_ui->toggle_selection (rid, ARDOUR::PresentationInfo::Flag (ARDOUR::PresentationInfo::Route|ARDOUR::PresentationInfo::VCA));
                        DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Function: SetRouteSelection = %1\n", rid));
                }
                break;
index 222b1177a849ce8c730404183a9e66046e79ab7b..c1786756a4c4b2319a1b790d535ec00e70fe7481 100644 (file)
@@ -289,12 +289,12 @@ MackieControlProtocol::get_sorted_stripables()
 
                switch (_view_mode) {
                case Mixer:
-                       if (!is_hidden (s)) {
+                       if (!s->presentation_info().hidden()) {
                                sorted.push_back (s);
                        }
                        break;
                case AudioTracks:
-                       if (is_audio_track(s) && !is_hidden(s)) {
+                       if (is_audio_track(s) && !s->presentation_info().hidden()) {
                                sorted.push_back (s);
                        }
                        break;
@@ -306,13 +306,13 @@ MackieControlProtocol::get_sorted_stripables()
                                }
 #endif
                        } else {
-                               if (!is_track(s) && !is_hidden(s)) {
+                               if (!is_track(s) && !s->presentation_info().hidden()) {
                                        sorted.push_back (s);
                                }
                        }
                        break;
                case MidiTracks:
-                       if (is_midi_track(s) && !is_hidden(s)) {
+                       if (is_midi_track(s) && !s->presentation_info().hidden()) {
                                sorted.push_back (s);
                        }
                        break;
@@ -320,22 +320,22 @@ MackieControlProtocol::get_sorted_stripables()
                        break;
                case Auxes: // in ardour, for now aux and buss are same. for mixbus, "Busses" are mixbuses, "Auxes" are ardour buses
 #ifdef MIXBUS
-                       if (!s->mixbus() && !is_track() && !is_hidden(s))
+                       if (!s->mixbus() && !is_track() && !s->presentation_info().hidden())
 #else
-                       if (!is_track(s) && !is_hidden(s))
+                       if (!is_track(s) && !s->presentation_info().hidden())
 #endif
                        {
                                sorted.push_back (s);
                        }
                        break;
                case Hidden: // Show all the tracks we have hidden
-                       if (is_hidden(s)) {
+                       if (s->presentation_info().hidden()) {
                                // maybe separate groups
                                sorted.push_back (s);
                        }
                        break;
                case Selected: // For example: a group (this is USER)
-                       if (selected(s) && !is_hidden(s)) {
+                       if (s->presentation_info().selected() && !s->presentation_info().hidden()) {
                                sorted.push_back (s);
                        }
                        break;
@@ -2039,23 +2039,19 @@ MackieControlProtocol::select_range ()
                return;
        }
 
-       if (stripables.size() == 1 && _last_selected_stripables.size() == 1 && stripables.front()->presentation_info().selected()) {
+       if (stripables.size() == 1 && ControlProtocol::last_selected().size() == 1 && stripables.front()->presentation_info().selected()) {
                /* cancel selection for one and only selected stripable */
-               session->toggle_stripable_selection (stripables.front());
+               ToggleStripableSelection (stripables.front());
        } else {
-
                for (StripableList::iterator s = stripables.begin(); s != stripables.end(); ++s) {
 
-                       if (main_modifier_state() == MODIFIER_CONTROL) {
-                               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("toggle selection of %1 (%2)\n", (*s)->name(), (*s)->presentation_info().order()));
-                               session->toggle_stripable_selection (*s);
+                       if (main_modifier_state() == MODIFIER_SHIFT) {
+                               ToggleStripableSelection (*s);
                        } else {
                                if (s == stripables.begin()) {
-                                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("set selection of %1 (%2)\n", (*s)->name(), (*s)->presentation_info().order()));
-                                       session->set_stripable_selection (*s);
-                       } else {
-                                       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add to selection %1 (%2)\n", (*s)->name(), (*s)->presentation_info().order()));
-                                       session->add_stripable_selection (*s);
+                                       SetStripableSelection (*s);
+                               } else {
+                                       AddStripableToSelection (*s);
                                }
                        }
                }
@@ -2351,27 +2347,6 @@ MackieControlProtocol::is_midi_track (boost::shared_ptr<Stripable> r) const
        return boost::dynamic_pointer_cast<MidiTrack>(r) != 0;
 }
 
-bool
-MackieControlProtocol::selected (boost::shared_ptr<Stripable> r) const
-{
-       for (Selection::const_iterator i = _last_selected_stripables.begin(); i != _last_selected_stripables.end(); ++i) {
-               boost::shared_ptr<ARDOUR::Stripable> rt = (*i).lock();
-               if (rt == r) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-bool
-MackieControlProtocol::is_hidden (boost::shared_ptr<Stripable> r) const
-{
-       if (!r) {
-               return false;
-       }
-       return (r->presentation_info().flags() & PresentationInfo::Hidden);
-}
-
 bool
 MackieControlProtocol::is_mapped (boost::shared_ptr<Stripable> r) const
 {
@@ -2391,14 +2366,6 @@ MackieControlProtocol::update_selected (boost::shared_ptr<Stripable> s, bool bec
 {
        if (became_selected) {
 
-               if (selected (s)) {
-                       /* already selected .. mmmm */
-                       cerr << "stripable " << s->name() << " already marked as selected\n";
-                       return;
-               }
-
-               _last_selected_stripables.push_back (boost::weak_ptr<Stripable> (s));
-
                check_fader_automation_state ();
 
                /* It is possible that first_selected_route() may return null if we
@@ -2412,33 +2379,19 @@ MackieControlProtocol::update_selected (boost::shared_ptr<Stripable> s, bool bec
                        set_subview_mode (None, boost::shared_ptr<Stripable>());
                }
 
-       } else {
-
-               for (Selection::iterator i = _last_selected_stripables.begin(); i != _last_selected_stripables.end(); ++i) {
-                       boost::shared_ptr<ARDOUR::Stripable> ss = (*i).lock();
-
-                       if (ss == s) {
-                               _last_selected_stripables.erase (i);
-                               break;
-                       }
-               }
        }
 }
 
 boost::shared_ptr<Stripable>
 MackieControlProtocol::first_selected_stripable () const
 {
-       if (_last_selected_stripables.empty()) {
-               return boost::shared_ptr<Stripable>();
-       }
-
-       boost::shared_ptr<Stripable> r = (*(_last_selected_stripables.begin())).lock();
+       boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
 
-       if (r) {
+       if (s) {
                /* check it is on one of our surfaces */
 
-               if (is_mapped (r)) {
-                       return r;
+               if (is_mapped (s)) {
+                       return s;
                }
 
                /* stripable is not mapped. thus, the currently selected stripable is
@@ -2446,10 +2399,10 @@ MackieControlProtocol::first_selected_stripable () const
                 * no currently selected stripable.
                 */
 
-               r.reset ();
+               s.reset ();
        }
 
-       return r; /* may be null */
+       return s; /* may be null */
 }
 
 boost::shared_ptr<Stripable>
index 76da4fe5e8d43e352f3dce8de468f8a8a9539c9d..56ead71b50d26554274aebeb106f45c683c841ca 100644 (file)
@@ -138,8 +138,6 @@ class MackieControlProtocol
        bool is_track (boost::shared_ptr<ARDOUR::Stripable>) const;
        bool is_audio_track (boost::shared_ptr<ARDOUR::Stripable>) const;
        bool is_midi_track (boost::shared_ptr<ARDOUR::Stripable>) const;
-       bool selected (boost::shared_ptr<ARDOUR::Stripable>) const;
-       bool is_hidden (boost::shared_ptr<ARDOUR::Stripable>) const;
        bool is_mapped (boost::shared_ptr<ARDOUR::Stripable>) const;
        boost::shared_ptr<ARDOUR::Stripable> first_selected_stripable () const;
        void update_selected (boost::shared_ptr<ARDOUR::Stripable>, bool selected);
@@ -335,8 +333,6 @@ class MackieControlProtocol
        bool                      needs_ipmidi_restart;
        bool                     _metering_active;
        bool                     _initialized;
-       typedef std::vector<boost::weak_ptr<ARDOUR::Stripable> > Selection;
-       Selection                _last_selected_stripables;
        XMLNode*                 configuration_state;
        int                      state_version;
        int                      _last_bank[9];
index f2fd8af8674b7f8804174ef4d72d3cccf866c04e..b199b64f84fe26c843bf1d022a2e538b946a9849 100644 (file)
@@ -2600,9 +2600,8 @@ OSC::route_plugin_parameter_print (int ssid, int piid, int par, lo_message msg)
 void
 OSC::gui_selection_changed ()
 {
-       boost::shared_ptr<Stripable> strip;
+       boost::shared_ptr<Stripable> strip = ControlProtocol::first_selected_stripable();
 
-       strip = boost::dynamic_pointer_cast<Stripable>(session->get_editor_mixer().lock());
        if (strip) {
                _select = strip;
                for (uint32_t it = 0; it < _surface.size(); ++it) {