From dc0139d4af4d246f6dcafb04425e3f1198c347c3 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 5 May 2017 12:31:49 +0100 Subject: [PATCH] use CoreSelection for track selection --- gtk2_ardour/automation_time_axis.cc | 1 + gtk2_ardour/automation_time_axis.h | 4 +- gtk2_ardour/axis_view.cc | 10 +- gtk2_ardour/axis_view.h | 7 +- gtk2_ardour/editor.cc | 46 +- gtk2_ardour/editor.h | 15 +- gtk2_ardour/editor_canvas_events.cc | 4 +- gtk2_ardour/editor_drag.cc | 4 +- gtk2_ardour/editor_mixer.cc | 3 +- gtk2_ardour/editor_mouse.cc | 1 - gtk2_ardour/editor_routes.cc | 51 +-- gtk2_ardour/editor_routes.h | 2 +- gtk2_ardour/editor_selection.cc | 174 +++++--- gtk2_ardour/mixer_ui.cc | 28 +- gtk2_ardour/mixer_ui.h | 7 +- gtk2_ardour/monitor_section.cc | 1 + gtk2_ardour/monitor_section.h | 4 +- gtk2_ardour/monitor_selector.h | 2 - gtk2_ardour/port_group.cc | 2 +- gtk2_ardour/processor_box.cc | 20 +- gtk2_ardour/processor_box.h | 6 +- gtk2_ardour/processor_selection.h | 84 ++-- gtk2_ardour/public_editor.h | 16 +- gtk2_ardour/route_params_ui.cc | 8 +- gtk2_ardour/route_params_ui.h | 4 +- gtk2_ardour/route_processor_selection.cc | 94 ++-- gtk2_ardour/route_processor_selection.h | 22 +- gtk2_ardour/selection.cc | 526 +++++++++++++---------- gtk2_ardour/selection.h | 4 + gtk2_ardour/time_axis_view.cc | 10 - gtk2_ardour/track_selection.cc | 44 -- gtk2_ardour/track_selection.h | 2 - gtk2_ardour/wscript | 1 + 33 files changed, 677 insertions(+), 530 deletions(-) diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index 0b71e423cd..3028fdaefd 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -1062,3 +1062,4 @@ AutomationTimeAxisView::color () const { return gdk_color_from_rgb (_route->presentation_info().color()); } + diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h index 4be1a4c4d2..c298ac325b 100644 --- a/gtk2_ardour/automation_time_axis.h +++ b/gtk2_ardour/automation_time_axis.h @@ -105,8 +105,8 @@ class AutomationTimeAxisView : public TimeAxisView { std::string state_id() const; static bool parse_state_id (std::string const &, PBD::ID &, bool &, Evoral::Parameter &); - boost::shared_ptr control() { return _control; } - boost::shared_ptr controller() { return _controller; } + boost::shared_ptr control() const { return _control; } + boost::shared_ptr controller() const { return _controller; } Evoral::Parameter parameter () const { return _parameter; } diff --git a/gtk2_ardour/axis_view.cc b/gtk2_ardour/axis_view.cc index 3d25b35e17..632289bddf 100644 --- a/gtk2_ardour/axis_view.cc +++ b/gtk2_ardour/axis_view.cc @@ -27,17 +27,19 @@ #include "pbd/error.h" #include "pbd/convert.h" +#include "pbd/i18n.h" #include #include #include +#include "ardour/selection.h" + #include "public_editor.h" #include "ardour_ui.h" #include "gui_object.h" #include "axis_view.h" #include "utils.h" -#include "pbd/i18n.h" using namespace std; using namespace Gtk; @@ -131,10 +133,4 @@ AxisView::set_selected (bool yn) } Selectable::set_selected (yn); - - boost::shared_ptr s = stripable (); - - if (s) { - s->presentation_info().set_selected (yn); - } } diff --git a/gtk2_ardour/axis_view.h b/gtk2_ardour/axis_view.h index 81fe489252..77518116a3 100644 --- a/gtk2_ardour/axis_view.h +++ b/gtk2_ardour/axis_view.h @@ -35,6 +35,10 @@ #include "prompter.h" #include "selectable.h" +namespace PBD { + class Controllable; +} + namespace ARDOUR { class Session; class Stripable; @@ -49,14 +53,13 @@ namespace ARDOUR { class AxisView : public virtual PBD::ScopedConnectionList, public virtual ARDOUR::SessionHandlePtr, public virtual Selectable { public: - ARDOUR::Session* session() const { return _session; } - virtual std::string name() const = 0; virtual Gdk::Color color() const = 0; sigc::signal Hiding; virtual boost::shared_ptr stripable() const = 0; + virtual boost::shared_ptr control() const { return boost::shared_ptr(); } virtual std::string state_id() const = 0; /* for now, we always return properties in string form. diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 0d6eb33416..7fe49de57b 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -642,6 +642,8 @@ Editor::Editor () bottom_hbox.set_border_width (2); bottom_hbox.set_spacing (3); + PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&Editor::presentation_info_changed, this, _1), gui_context()); + _route_groups = new EditorRouteGroups (this); _routes = new EditorRoutes (this); _regions = new EditorRegions (this); @@ -1038,7 +1040,7 @@ Editor::control_unselect () void Editor::control_select (boost::shared_ptr s, Selection::Operation op) { - TimeAxisView* tav = axis_view_from_stripable (s); + TimeAxisView* tav = time_axis_view_from_stripable (s); if (tav) { switch (op) { @@ -1371,6 +1373,12 @@ Editor::set_session (Session *t) XMLNode* node = ARDOUR_UI::instance()->editor_settings(); set_state (*node, Stateful::loading_state_version); + /* catch up on selection state, etc. */ + + PropertyChange sc; + sc.add (Properties::selected); + presentation_info_changed (sc); + /* catch up with the playhead */ _session->request_locate (playhead_cursor->current_frame ()); @@ -1431,25 +1439,6 @@ Editor::set_session (Session *t) break; } - /* catch up on selection of stripables (other selection state is lost - * when a session is closed - */ - - StripableList sl; - TrackViewList tl; - _session->get_stripables (sl); - for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) { - if ((*s)->presentation_info().selected()) { - RouteTimeAxisView* rtav = get_route_view_by_route_id ((*s)->id()); - if (rtav) { - tl.push_back (rtav); - } - } - } - if (!tl.empty()) { - selection->set (tl); - } - /* register for undo history */ _session->register_with_memento_command_factory(id(), this); _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento); @@ -5246,8 +5235,8 @@ Editor::region_view_removed () _summary->set_background_dirty (); } -TimeAxisView* -Editor::axis_view_from_stripable (boost::shared_ptr s) const +AxisView* +Editor::axis_view_by_stripable (boost::shared_ptr s) const { for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) { if ((*j)->stripable() == s) { @@ -5258,6 +5247,17 @@ Editor::axis_view_from_stripable (boost::shared_ptr s) const return 0; } +AxisView* +Editor::axis_view_by_control (boost::shared_ptr c) const +{ + for (TrackViewList::const_iterator j = track_views.begin (); j != track_views.end(); ++j) { + if ((*j)->control() == c) { + return *j; + } + } + + return 0; +} TrackViewList Editor::axis_views_from_routes (boost::shared_ptr r) const @@ -5265,7 +5265,7 @@ Editor::axis_views_from_routes (boost::shared_ptr r) const TrackViewList t; for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { - TimeAxisView* tv = axis_view_from_stripable (*i); + TimeAxisView* tv = time_axis_view_from_stripable (*i); if (tv) { t.push_back (tv); } diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 868817bddd..60bf93494d 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -140,14 +140,13 @@ class TimeSelection; class RegionLayeringOrderEditor; class VerboseCursor; -class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARDOUR::SessionHandlePtr +class Editor : public PublicEditor, public PBD::ScopedConnectionList { public: Editor (); ~Editor (); void set_session (ARDOUR::Session *); - ARDOUR::Session* session() const { return _session; } Gtk::Window* use_own_window (bool and_fill_it); @@ -427,7 +426,7 @@ public: void start_resize_line_ops (); void end_resize_line_ops (); - TrackViewList const & get_track_views () { + TrackViewList const & get_track_views () const { return track_views; } @@ -1158,7 +1157,13 @@ private: /* track views */ TrackViewList track_views; std::pair trackview_by_y_position (double, bool trackview_relative_offset = true) const; - TimeAxisView* axis_view_from_stripable (boost::shared_ptr) const; + + AxisView* axis_view_by_stripable (boost::shared_ptr) const; + AxisView* axis_view_by_control (boost::shared_ptr) const; + + TimeAxisView* time_axis_view_from_stripable (boost::shared_ptr s) const { + return dynamic_cast (axis_view_by_stripable (s)); + } TrackViewList get_tracks_for_range_action () const; @@ -1857,7 +1862,7 @@ private: void time_selection_changed (); void update_time_selection_display (); - void track_selection_changed (); + void presentation_info_changed (PBD::PropertyChange const &); void region_selection_changed (); sigc::connection editor_regions_selection_changed_connection; void sensitize_all_region_actions (bool); diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index 132f6f870b..ad1cb8def8 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -1295,7 +1295,7 @@ Editor::drop_regions (const Glib::RefPtr& /*context*/, } list > audio_tracks; audio_tracks = session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order); - rtav = dynamic_cast (axis_view_from_stripable (audio_tracks.front())); + rtav = dynamic_cast (time_axis_view_from_stripable (audio_tracks.front())); } else if (boost::dynamic_pointer_cast (region)) { ChanCount one_midi_port (DataType::MIDI, 1); list > midi_tracks; @@ -1304,7 +1304,7 @@ Editor::drop_regions (const Glib::RefPtr& /*context*/, boost::shared_ptr(), (ARDOUR::Plugin::PresetRecord*) 0, (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order); - rtav = dynamic_cast (axis_view_from_stripable (midi_tracks.front())); + rtav = dynamic_cast (time_axis_view_from_stripable (midi_tracks.front())); } else { return; } diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 35e9d395c5..90ef18f5b7 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -1443,7 +1443,7 @@ RegionMoveDrag::create_destination_time_axis (boost::shared_ptr region, output_chan = _editor->session()->master_out()->n_inputs().n_audio(); } audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order); - tav =_editor->axis_view_from_stripable (audio_tracks.front()); + tav =_editor->time_axis_view_from_stripable (audio_tracks.front()); } else { ChanCount one_midi_port (DataType::MIDI, 1); list > midi_tracks; @@ -1452,7 +1452,7 @@ RegionMoveDrag::create_destination_time_axis (boost::shared_ptr region, boost::shared_ptr(), (ARDOUR::Plugin::PresetRecord*) 0, (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order); - tav = _editor->axis_view_from_stripable (midi_tracks.front()); + tav = _editor->time_axis_view_from_stripable (midi_tracks.front()); } if (tav) { diff --git a/gtk2_ardour/editor_mixer.cc b/gtk2_ardour/editor_mixer.cc index 840b2293cf..0063536735 100644 --- a/gtk2_ardour/editor_mixer.cc +++ b/gtk2_ardour/editor_mixer.cc @@ -146,7 +146,7 @@ Editor::show_editor_mixer (bool yn) if (current_mixer_strip && current_mixer_strip->get_parent() == 0) { global_hpacker.pack_start (*current_mixer_strip, Gtk::PACK_SHRINK ); - global_hpacker.reorder_child (*current_mixer_strip, 0); + global_hpacker.reorder_child (*current_mixer_strip, 0); current_mixer_strip->show (); } @@ -285,4 +285,3 @@ Editor::mixer_strip_width_changed () editor_mixer_strip_width = current_mixer_strip->get_width_enum (); } - diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 008f609474..2348ef7ba5 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -337,7 +337,6 @@ Editor::update_time_selection_display () break; case MouseObject: selection->clear_time (); - selection->clear_tracks (); selection->clear_midi_notes (); break; case MouseDraw: diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index 0d08c6ea87..059439befe 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -31,6 +31,7 @@ #include "ardour/audio_track.h" #include "ardour/midi_track.h" #include "ardour/route.h" +#include "ardour/selection.h" #include "ardour/session.h" #include "ardour/solo_isolate_control.h" #include "ardour/utils.h" @@ -319,7 +320,6 @@ EditorRoutes::EditorRoutes (Editor* e) _display.set_enable_search (false); Route::PluginSetup.connect_same_thread (*this, boost::bind (&EditorRoutes::plugin_setup, this, _1, _2, _3)); - PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::presentation_info_changed, this, _1), gui_context()); } bool @@ -727,7 +727,10 @@ EditorRoutes::time_axis_views_added (list tavs) } } - _display.set_model (Glib::RefPtr()); + { + PBD::Unwinder uw (_ignore_selection_change, true); + _display.set_model (Glib::RefPtr()); + } for (list::iterator x = tavs.begin(); x != tavs.end(); ++x) { @@ -754,7 +757,6 @@ EditorRoutes::time_axis_views_added (list tavs) row[_columns.is_track] = (boost::dynamic_pointer_cast (stripable) != 0); - if (midi_trk) { row[_columns.is_input_active] = midi_trk->input_active (); row[_columns.is_midi] = true; @@ -832,7 +834,11 @@ EditorRoutes::time_axis_views_added (list tavs) update_input_active_display (); update_active_display (); - _display.set_model (_model); + { + PBD::Unwinder uw (_ignore_selection_change, true); + cerr << "Should ignore model/selection change\n"; + _display.set_model (_model); + } /* now update route order keys from the treeview/track display order */ @@ -1082,19 +1088,6 @@ EditorRoutes::sync_presentation_info_from_treeview () } } -void -EditorRoutes::presentation_info_changed (PropertyChange const & what_changed) -{ - PropertyChange soh; - soh.add (Properties::selected); - soh.add (Properties::order); - soh.add (Properties::hidden); - - if (what_changed.contains (soh)) { - sync_treeview_from_presentation_info (what_changed); - } -} - void EditorRoutes::sync_treeview_from_presentation_info (PropertyChange const & what_changed) { @@ -1169,13 +1162,18 @@ EditorRoutes::sync_treeview_from_presentation_info (PropertyChange const & what_ if (what_changed.contains (Properties::selected)) { + /* by the time this is invoked, the GUI Selection model has + * already updated itself. + */ + TrackViewList tvl; PBD::Unwinder uw (_ignore_selection_change, true); - /* step one: set the treeview model selection state */ + /* set the treeview model selection state */ + for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) { boost::shared_ptr stripable = (*ri)[_columns.stripable]; - if (stripable && stripable->presentation_info().selected()) { + if (stripable && stripable->is_selected()) { TimeAxisView* tav = (*ri)[_columns.tv]; if (tav) { tvl.push_back (tav); @@ -1185,12 +1183,6 @@ EditorRoutes::sync_treeview_from_presentation_info (PropertyChange const & what_ _display.get_selection()->unselect (*ri); } } - - /* step two: set the Selection (for stripables/routes) */ - _editor->get_selection().set (tvl); - - /* step three, tell the editor */ - _editor->track_selection_changed (); } redisplay (); @@ -1645,7 +1637,7 @@ EditorRoutes::move_selected_tracks (bool up) /* build a list that includes time axis view information */ for (StripableList::const_iterator sli = sl.begin(); sli != sl.end(); ++sli) { - TimeAxisView* tv = _editor->axis_view_from_stripable (*sli); + TimeAxisView* tv = _editor->time_axis_view_from_stripable (*sli); view_stripables.push_back (ViewStripable (tv, *sli)); } @@ -1666,7 +1658,7 @@ EditorRoutes::move_selected_tracks (bool up) while (vsi != view_stripables.end()) { - if (vsi->stripable->presentation_info().selected()) { + if (vsi->stripable->is_selected()) { if (unselected_neighbour != view_stripables.end()) { @@ -1701,7 +1693,7 @@ EditorRoutes::move_selected_tracks (bool up) --vsi; - if (vsi->stripable->presentation_info().selected()) { + if (vsi->stripable->is_selected()) { if (unselected_neighbour != view_stripables.end()) { @@ -1852,6 +1844,7 @@ EditorRoutes::views () const void EditorRoutes::clear () { + PBD::Unwinder uw (_ignore_selection_change, true); _display.set_model (Glib::RefPtr (0)); _model->clear (); _display.set_model (_model); @@ -1902,7 +1895,7 @@ EditorRoutes::show_tracks_with_regions_at_playhead () set show; for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { - TimeAxisView* tav = _editor->axis_view_from_stripable (*i); + TimeAxisView* tav = _editor->time_axis_view_from_stripable (*i); if (tav) { show.insert (tav); } diff --git a/gtk2_ardour/editor_routes.h b/gtk2_ardour/editor_routes.h index a9d7115fdf..2ceb6682b2 100644 --- a/gtk2_ardour/editor_routes.h +++ b/gtk2_ardour/editor_routes.h @@ -65,6 +65,7 @@ public: void hide_all_tracks (bool); void clear (); void sync_presentation_info_from_treeview (); + void sync_treeview_from_presentation_info (PBD::PropertyChange const &); private: void initial_display (); @@ -78,7 +79,6 @@ private: void on_tv_solo_safe_toggled (std::string const &); void build_menu (); void presentation_info_changed (PBD::PropertyChange const &); - void sync_treeview_from_presentation_info (PBD::PropertyChange const &); void row_deleted (Gtk::TreeModel::Path const &); void visible_changed (std::string const &); void active_changed (std::string const &); diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc index 770bfe9c1f..c9e2ba49fd 100644 --- a/gtk2_ardour/editor_selection.cc +++ b/gtk2_ardour/editor_selection.cc @@ -27,12 +27,14 @@ #include "ardour/playlist.h" #include "ardour/profile.h" #include "ardour/route_group.h" +#include "ardour/selection.h" #include "ardour/session.h" #include "control_protocol/control_protocol.h" -#include "editor_drag.h" #include "editor.h" +#include "editor_drag.h" +#include "editor_routes.h" #include "actions.h" #include "audio_time_axis.h" #include "audio_region_view.h" @@ -267,9 +269,7 @@ Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no break; case Selection::Add: - if (!selection->selected (&view)) { - selection->add (&view); - } + selection->add (&view); break; case Selection::Set: @@ -999,84 +999,136 @@ struct SelectionOrderSorter { }; void -Editor::track_selection_changed () +Editor::presentation_info_changed (PropertyChange const & what_changed) { - SelectionOrderSorter cmp; - selection->tracks.sort (cmp); + /* We cannot ensure ordering of the handlers for + * PresentationInfo::Changed, so we have to do everything in order + * here, as a single handler. + */ - switch (selection->tracks.size()) { - case 0: - break; - default: - set_selected_mixer_strip (*(selection->tracks.back())); - if (!_track_selection_change_without_scroll) { - ensure_time_axis_view_is_visible (*(selection->tracks.back()), false); - } - break; + for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { + (*i)->set_selected (false); + (*i)->hide_selection (); } - RouteNotificationListPtr routes (new RouteNotificationList); - StripableNotificationListPtr stripables (new StripableNotificationList); + /* STEP 1: set the GUI selection state (in which TimeAxisViews for the + * currently selected stripable/controllable duples are found and added + */ - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + selection->core_selection_changed (what_changed); - bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()); + /* STEP 2: update TimeAxisView's knowledge of their selected state + */ - (*i)->set_selected (yn); - TimeAxisView::Children c = (*i)->get_child_list (); - for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) { - (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end()); - } + PropertyChange pc; + pc.add (Properties::selected); - if (yn) { - (*i)->reshow_selection (selection->time); - } else { - (*i)->hide_selection (); - } + if (what_changed.contains (Properties::selected)) { + StripableNotificationListPtr stripables (new StripableNotificationList); - if (yn) { - RouteTimeAxisView* rtav = dynamic_cast (*i); - if (rtav) { - routes->push_back (rtav->route()); - stripables->push_back (rtav->route()); + switch (selection->tracks.size()) { + case 0: + break; + default: + set_selected_mixer_strip (*(selection->tracks.back())); + if (!_track_selection_change_without_scroll) { + ensure_time_axis_view_is_visible (*(selection->tracks.back()), false); } + break; } - } - ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty()); + CoreSelection::StripableAutomationControls sc; + _session->selection().get_stripables (sc); - sensitize_the_right_region_actions (false); + for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) { - /* notify control protocols */ + AxisView* av = axis_view_by_stripable ((*i).stripable); - ControlProtocol::StripableSelectionChanged (stripables); + if (!av) { + continue; + } - if (sfbrowser && _session && !_session->deletion_in_progress()) { - uint32_t audio_track_cnt = 0; - uint32_t midi_track_cnt = 0; + TimeAxisView* tav = dynamic_cast (av); - for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) { - AudioTimeAxisView* atv = dynamic_cast(*x); + if (!tav) { + continue; /* impossible */ + } - if (atv) { - if (atv->is_audio_track()) { - audio_track_cnt++; - } + if (!(*i).controllable) { + + /* "parent" track selected */ + tav->set_selected (true); + tav->reshow_selection (selection->time); } else { - MidiTimeAxisView* mtv = dynamic_cast(*x); - if (mtv) { - if (mtv->is_midi_track()) { - midi_track_cnt++; + /* possibly a child */ + + TimeAxisView::Children c = tav->get_child_list (); + + for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) { + + boost::shared_ptr control = (*j)->control (); + + if (control != (*i).controllable) { + continue; } + + (*j)->set_selected (true); + (*j)->reshow_selection (selection->time); } } + + stripables->push_back ((*i).stripable); + } + + ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty()); + + sensitize_the_right_region_actions (false); + + /* STEP 4: notify control protocols */ + + ControlProtocol::StripableSelectionChanged (stripables); + + if (sfbrowser && _session && !_session->deletion_in_progress()) { + uint32_t audio_track_cnt = 0; + uint32_t midi_track_cnt = 0; + + for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) { + AudioTimeAxisView* atv = dynamic_cast(*x); + + if (atv) { + if (atv->is_audio_track()) { + audio_track_cnt++; + } + + } else { + MidiTimeAxisView* mtv = dynamic_cast(*x); + + if (mtv) { + if (mtv->is_midi_track()) { + midi_track_cnt++; + } + } + } + } + + sfbrowser->reset (audio_track_cnt, midi_track_cnt); } + } - sfbrowser->reset (audio_track_cnt, midi_track_cnt); + /* STEP 4: update EditorRoutes treeview */ + + PropertyChange soh; + + soh.add (Properties::selected); + soh.add (Properties::order); + soh.add (Properties::hidden); + + if (what_changed.contains (soh)) { + _routes->sync_treeview_from_presentation_info (what_changed); } } @@ -1570,28 +1622,32 @@ Editor::select_all_objects (Selection::Operation op) { list touched; - TrackViewList ts = track_views; - if (internal_editing() && select_all_internal_edit(op)) { return; // Selected notes } + TrackViewList ts; + + if (selection->tracks.empty()) { + ts = track_views; + } else { + ts = selection->tracks; + } + for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) { if ((*iter)->hidden()) { continue; } (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched); - selection->add (*iter); } - begin_reversible_selection_op (X_("select all")); switch (op) { case Selection::Add: selection->add (touched); break; case Selection::Toggle: - selection->add (touched); + selection->toggle (touched); break; case Selection::Set: selection->set (touched); diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 4c5d60d6c7..b70ceef37d 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -48,6 +48,7 @@ #include "ardour/midi_track.h" #include "ardour/plugin_manager.h" #include "ardour/route_group.h" +#include "ardour/selection.h" #include "ardour/session.h" #include "ardour/vca.h" #include "ardour/vca_manager.h" @@ -110,6 +111,7 @@ Mixer_UI::Mixer_UI () , _maximised (false) , _show_mixer_list (true) , myactions (X_("mixer")) + , _selection (*this, *this) { register_actions (); load_bindings (); @@ -683,6 +685,8 @@ Mixer_UI::remove_strip (MixerStrip* strip) void Mixer_UI::presentation_info_changed (PropertyChange const & what_changed) { + _selection.presentation_info_changed (what_changed); + PropertyChange soh; soh.add (Properties::selected); soh.add (Properties::order); @@ -850,7 +854,7 @@ Mixer_UI::sync_treeview_from_presentation_info (PropertyChange const & what_chan for (list::const_iterator i = strips.begin(); i != strips.end(); ++i) { boost::shared_ptr stripable = (*i)->stripable(); - if (stripable && stripable->presentation_info().selected()) { + if (stripable && stripable->is_selected()) { _selection.add (*i); } else { _selection.remove (*i); @@ -891,7 +895,7 @@ Mixer_UI::strip_by_stripable (boost::shared_ptr s) const } AxisView* -Mixer_UI::axis_by_stripable (boost::shared_ptr s) const +Mixer_UI::axis_view_by_stripable (boost::shared_ptr s) const { for (list::const_iterator i = strips.begin(); i != strips.end(); ++i) { if ((*i)->stripable() == s) { @@ -902,6 +906,18 @@ Mixer_UI::axis_by_stripable (boost::shared_ptr s) const return 0; } +AxisView* +Mixer_UI::axis_view_by_control (boost::shared_ptr c) const +{ + for (list::const_iterator i = strips.begin(); i != strips.end(); ++i) { + if ((*i)->control() == c) { + return (*i); + } + } + + return 0; +} + bool Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip) { @@ -1000,6 +1016,7 @@ Mixer_UI::set_session (Session* sess) } if (!_session) { + _selection.clear (); return; } @@ -1029,6 +1046,13 @@ Mixer_UI::set_session (Session* sess) if (_visible) { show_window(); } + + /* catch up on selection state, etc. */ + + PropertyChange sc; + sc.add (Properties::selected); + _selection.presentation_info_changed (sc); + start_updating (); } diff --git a/gtk2_ardour/mixer_ui.h b/gtk2_ardour/mixer_ui.h index 413c8be727..4e2bda711b 100644 --- a/gtk2_ardour/mixer_ui.h +++ b/gtk2_ardour/mixer_ui.h @@ -47,6 +47,7 @@ #include "gtkmm2ext/tabbable.h" #include "gtkmm2ext/treeutils.h" +#include "axis_provider.h" #include "enums.h" #include "route_processor_selection.h" @@ -76,7 +77,7 @@ protected: virtual bool row_drop_possible_vfunc (const Gtk::TreeModel::Path&, const Gtk::SelectionData&) const; }; -class Mixer_UI : public Gtkmm2ext::Tabbable, public PBD::ScopedConnectionList, public ARDOUR::SessionHandlePtr +class Mixer_UI : public Gtkmm2ext::Tabbable, public PBD::ScopedConnectionList, public ARDOUR::SessionHandlePtr, public AxisViewProvider { public: static Mixer_UI* instance(); @@ -188,7 +189,9 @@ class Mixer_UI : public Gtkmm2ext::Tabbable, public PBD::ScopedConnectionList, p MixerStrip* strip_by_route (boost::shared_ptr) const; MixerStrip* strip_by_stripable (boost::shared_ptr) const; - AxisView* axis_by_stripable (boost::shared_ptr) const; + + AxisView* axis_view_by_stripable (boost::shared_ptr) const; + AxisView* axis_view_by_control (boost::shared_ptr) const; void hide_all_strips (bool with_select); void unselect_all_strips(); diff --git a/gtk2_ardour/monitor_section.cc b/gtk2_ardour/monitor_section.cc index de1d9504c0..2cd46c051a 100644 --- a/gtk2_ardour/monitor_section.cc +++ b/gtk2_ardour/monitor_section.cc @@ -88,6 +88,7 @@ MonitorSection::MonitorSection (Session* s) , solo_mute_override_button (ArdourButton::led_default_elements) , toggle_processorbox_button (ArdourButton::default_elements) , _inhibit_solo_model_update (false) + , _rr_selection () , _ui_initialized (false) { diff --git a/gtk2_ardour/monitor_section.h b/gtk2_ardour/monitor_section.h index b58203f521..61f4480c2f 100644 --- a/gtk2_ardour/monitor_section.h +++ b/gtk2_ardour/monitor_section.h @@ -32,8 +32,8 @@ #include "monitor_selector.h" #include "plugin_selector.h" -#include "route_processor_selection.h" #include "processor_box.h" +#include "processor_selection.h" namespace Gtkmm2ext { class TearOff; @@ -192,7 +192,7 @@ class MonitorSection : public RouteUI, public Gtk::EventBox ProcessorBox* insert_box; PluginSelector* _plugin_selector; - RouteProcessorSelection _rr_selection; + ProcessorSelection _rr_selection; void help_count_processors (boost::weak_ptr p, uint32_t* cnt) const; uint32_t count_processors (); diff --git a/gtk2_ardour/monitor_selector.h b/gtk2_ardour/monitor_selector.h index 8c642b5dfe..8e572e958c 100644 --- a/gtk2_ardour/monitor_selector.h +++ b/gtk2_ardour/monitor_selector.h @@ -34,8 +34,6 @@ class MonitorSelector : public PortMatrix std::string disassociation_verb () const; std::string channel_noun () const; - ARDOUR::Session* session() const { return _session; } - uint32_t n_io_ports () const; boost::shared_ptr const io () { return _io; } void setup_ports (int); diff --git a/gtk2_ardour/port_group.cc b/gtk2_ardour/port_group.cc index 7439fe1b77..5db8585a83 100644 --- a/gtk2_ardour/port_group.cc +++ b/gtk2_ardour/port_group.cc @@ -378,7 +378,7 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp /* Now put the bundles that belong to these sorted RouteIOs into the PortGroup. */ for (list::iterator i = route_ios.begin(); i != route_ios.end(); ++i) { - TimeAxisView* tv = PublicEditor::instance().axis_view_from_stripable (i->route); + TimeAxisView* tv = PublicEditor::instance().time_axis_view_from_stripable (i->route); /* Work out which group to put these IOs' bundles in */ boost::shared_ptr g; diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index 37645f0185..34bcbf28b5 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -76,9 +76,9 @@ #include "plugin_ui.h" #include "port_insert_ui.h" #include "processor_box.h" +#include "processor_selection.h" #include "public_editor.h" #include "return_ui.h" -#include "route_processor_selection.h" #include "script_selector.h" #include "send_ui.h" #include "timers.h" @@ -1807,14 +1807,14 @@ static std::list drag_targets_noplugin() } ProcessorBox::ProcessorBox (ARDOUR::Session* sess, boost::function get_plugin_selector, - RouteProcessorSelection& rsel, MixerStrip* parent, bool owner_is_mixer) + ProcessorSelection& psel, MixerStrip* parent, bool owner_is_mixer) : _parent_strip (parent) , _owner_is_mixer (owner_is_mixer) , ab_direction (true) , _get_plugin_selector (get_plugin_selector) , _placement (-1) , _visible_prefader_processors (0) - , _rr_selection(rsel) + , _p_selection(psel) , processor_display (drop_targets()) , _redisplay_pending (false) { @@ -2200,7 +2200,7 @@ ProcessorBox::show_processor_menu (int arg) const bool sensitive = !processor_display.selection().empty() && ! stub_processor_selected (); - paste_action->set_sensitive (!_rr_selection.processors.empty()); + paste_action->set_sensitive (!_p_selection.processors.empty()); cut_action->set_sensitive (sensitive && can_cut ()); copy_action->set_sensitive (sensitive); delete_action->set_sensitive (sensitive || stub_processor_selected ()); @@ -3165,7 +3165,7 @@ ProcessorBox::cut_processors (const ProcSelection& to_be_removed) return; } - _rr_selection.set (node); + _p_selection.set (node); no_processor_redisplay = false; redisplay_processors (); @@ -3189,7 +3189,7 @@ ProcessorBox::copy_processors (const ProcSelection& to_be_copied) } } - _rr_selection.set (node); + _p_selection.set (node); } void @@ -3304,22 +3304,22 @@ ProcessorBox::rename_processor (boost::shared_ptr processor) void ProcessorBox::paste_processors () { - if (_rr_selection.processors.empty()) { + if (_p_selection.processors.empty()) { return; } - paste_processor_state (_rr_selection.processors.get_node().children(), boost::shared_ptr()); + paste_processor_state (_p_selection.processors.get_node().children(), boost::shared_ptr()); } void ProcessorBox::paste_processors (boost::shared_ptr before) { - if (_rr_selection.processors.empty()) { + if (_p_selection.processors.empty()) { return; } - paste_processor_state (_rr_selection.processors.get_node().children(), before); + paste_processor_state (_p_selection.processors.get_node().children(), before); } void diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 330a920e87..5e73afa45c 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -62,7 +62,7 @@ class MotionController; class PluginSelector; class PluginUIWindow; -class RouteProcessorSelection; +class ProcessorSelection; class MixerStrip; namespace ARDOUR { @@ -414,7 +414,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD }; ProcessorBox (ARDOUR::Session*, boost::function get_plugin_selector, - RouteProcessorSelection&, MixerStrip* parent, bool owner_is_mixer = false); + ProcessorSelection&, MixerStrip* parent, bool owner_is_mixer = false); ~ProcessorBox (); void set_route (boost::shared_ptr); @@ -490,7 +490,7 @@ class ProcessorBox : public Gtk::HBox, public PluginInterestedObject, public ARD int _placement; uint32_t _visible_prefader_processors; - RouteProcessorSelection& _rr_selection; + ProcessorSelection& _p_selection; static Gtkmm2ext::ActionMap myactions; static void load_bindings (); diff --git a/gtk2_ardour/processor_selection.h b/gtk2_ardour/processor_selection.h index d371493b41..fc3a133ef1 100644 --- a/gtk2_ardour/processor_selection.h +++ b/gtk2_ardour/processor_selection.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2007 Paul Davis + Copyright (C) 2004 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,40 +20,64 @@ #ifndef __ardour_gtk_processor_selection_h__ #define __ardour_gtk_processor_selection_h__ +#include + +#include "pbd/signals.h" #include "pbd/xml++.h" -class ProcessorSelection { +class XMLProcessorSelection { public: - ProcessorSelection() : node (0) {} - ~ProcessorSelection() { if (node) { delete node; } } - - void set (XMLNode* n) { - if (node) { - delete node; - } - node = n; - } - - void add (XMLNode* newchild) { - if (!node) { - node = new XMLNode ("add"); - } - node->add_child_nocopy (*newchild); - } - - void clear () { - if (node) { - delete node; - node = 0; - } - } - - bool empty () const { return node == 0 || node->children().empty(); } - - const XMLNode& get_node() const { return *node; } + XMLProcessorSelection() : node (0) {} + ~XMLProcessorSelection() { if (node) { delete node; } } + + void set (XMLNode* n) { + if (node) { + delete node; + } + node = n; + } + + void add (XMLNode* newchild) { + if (!node) { + node = new XMLNode ("add"); + } + node->add_child_nocopy (*newchild); + } + + void clear () { + if (node) { + delete node; + node = 0; + } + } + + bool empty () const { return node == 0 || node->children().empty(); } + + const XMLNode& get_node() const { return *node; } private: - XMLNode* node; + XMLNode* node; }; +class ProcessorSelection : public PBD::ScopedConnectionList, public sigc::trackable +{ + public: + ProcessorSelection () {} + + XMLProcessorSelection processors; + sigc::signal ProcessorsChanged; + + ProcessorSelection& operator= (const ProcessorSelection& other); + + void clear (); + bool empty(); + + void set (XMLNode* node); + void add (XMLNode* node); + + void clear_processors (); +}; + +bool operator==(const ProcessorSelection& a, const ProcessorSelection& b); + #endif /* __ardour_gtk_processor_selection_h__ */ diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index ea7980fb7b..f0ed177500 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -40,6 +40,8 @@ #include "pbd/statefuldestructible.h" +#include "ardour/session_handle.h" + #include "canvas/fwd.h" #include "gtkmm2ext/actions.h" @@ -47,6 +49,7 @@ #include "gtkmm2ext/tabbable.h" #include "gtkmm2ext/visibility_tracker.h" +#include "axis_provider.h" #include "editing.h" #include "selection.h" @@ -105,8 +108,8 @@ using ARDOUR::framecnt_t; * of PublicEditor need not be recompiled if private methods or member variables * change. */ -class PublicEditor : public Gtkmm2ext::Tabbable { -public: +class PublicEditor : public Gtkmm2ext::Tabbable, public ARDOUR::SessionHandlePtr, public AxisViewProvider { + public: PublicEditor (Gtk::Widget& content); virtual ~PublicEditor (); @@ -123,9 +126,6 @@ public: */ virtual void set_session (ARDOUR::Session* s) = 0; - /** @return The Session that we are editing, or 0 */ - virtual ARDOUR::Session* session () const = 0; - /** Set the snap type. * @param t Snap type (defined in editing_syms.h) */ @@ -353,6 +353,8 @@ public: virtual RouteTimeAxisView* get_route_view_by_route_id (const PBD::ID& id) const = 0; + virtual TimeAxisView* time_axis_view_from_stripable (boost::shared_ptr s) const = 0; + virtual void get_equivalent_regions (RegionView* rv, std::vector&, PBD::PropertyID) const = 0; virtual RegionView* regionview_from_region (boost::shared_ptr) const = 0; virtual RouteTimeAxisView* rtav_from_route (boost::shared_ptr) const = 0; @@ -422,15 +424,13 @@ public: virtual ArdourCanvas::GtkCanvasViewport* get_track_canvas() const = 0; - virtual TimeAxisView* axis_view_from_stripable (boost::shared_ptr) const = 0; - virtual void set_current_trimmable (boost::shared_ptr) = 0; virtual void set_current_movable (boost::shared_ptr) = 0; virtual void center_screen (framepos_t) = 0; virtual TrackViewList axis_views_from_routes (boost::shared_ptr) const = 0; - virtual TrackViewList const & get_track_views () = 0; + virtual TrackViewList const & get_track_views () const = 0; virtual DragManager* drags () const = 0; virtual void maybe_autoscroll (bool, bool, bool from_headers) = 0; diff --git a/gtk2_ardour/route_params_ui.cc b/gtk2_ardour/route_params_ui.cc index a8ddc02c1f..b377c838db 100644 --- a/gtk2_ardour/route_params_ui.cc +++ b/gtk2_ardour/route_params_ui.cc @@ -56,9 +56,9 @@ using namespace Gtk; using namespace Gtkmm2ext; RouteParams_UI::RouteParams_UI () - : ArdourWindow (_("Tracks and Busses")), - latency_apply_button (Stock::APPLY), - track_menu(0) + : ArdourWindow (_("Tracks and Busses")) + , latency_apply_button (Stock::APPLY) + , track_menu(0) { insert_box = 0; _input_iosel = 0; @@ -235,7 +235,7 @@ RouteParams_UI::setup_processor_boxes() cleanup_processor_boxes(); // construct new redirect boxes - insert_box = new ProcessorBox (_session, boost::bind (&RouteParams_UI::plugin_selector, this), _rr_selection, 0); + insert_box = new ProcessorBox (_session, boost::bind (&RouteParams_UI::plugin_selector, this), _p_selection, 0); insert_box->set_route (_route); boost::shared_ptr at = boost::dynamic_pointer_cast(_route); diff --git a/gtk2_ardour/route_params_ui.h b/gtk2_ardour/route_params_ui.h index 83ddf27110..2f04590c2b 100644 --- a/gtk2_ardour/route_params_ui.h +++ b/gtk2_ardour/route_params_ui.h @@ -40,7 +40,7 @@ #include "ardour_window.h" #include "processor_box.h" -#include "route_processor_selection.h" +#include "processor_selection.h" #include "latency_gui.h" namespace ARDOUR { @@ -113,7 +113,7 @@ class RouteParams_UI : public ArdourWindow, public PBD::ScopedConnectionList IOSelector * _output_iosel; PluginSelector *_plugin_selector; - RouteProcessorSelection _rr_selection; + ProcessorSelection _p_selection; boost::shared_ptr _route; PBD::ScopedConnection _route_processors_connection; diff --git a/gtk2_ardour/route_processor_selection.cc b/gtk2_ardour/route_processor_selection.cc index 5b0b48c4ba..0dc15e7f8a 100644 --- a/gtk2_ardour/route_processor_selection.cc +++ b/gtk2_ardour/route_processor_selection.cc @@ -19,21 +19,27 @@ #include #include + #include "pbd/error.h" +#include "pbd/i18n.h" +#include "ardour/selection.h" +#include "ardour/session.h" +#include "ardour/session_handle.h" + +#include "axis_provider.h" #include "gui_thread.h" #include "mixer_strip.h" +#include "mixer_ui.h" #include "route_processor_selection.h" #include "route_ui.h" -#include "pbd/i18n.h" - using namespace std; using namespace ARDOUR; using namespace PBD; - -RouteProcessorSelection::RouteProcessorSelection() +RouteProcessorSelection::RouteProcessorSelection (SessionHandlePtr& s, AxisViewProvider& ap) + : shp (s), avp (ap) { } @@ -41,7 +47,7 @@ RouteProcessorSelection& RouteProcessorSelection::operator= (const RouteProcessorSelection& other) { if (&other != this) { - processors = other.processors; + (*((ProcessorSelection*) this)) = (*((ProcessorSelection const *) &other)); axes = other.axes; } return *this; @@ -62,46 +68,57 @@ RouteProcessorSelection::clear () } void -RouteProcessorSelection::clear_processors () +RouteProcessorSelection::clear_routes () { - processors.clear (); - ProcessorsChanged (); + if (shp.session()) { + PresentationInfo::ChangeSuspender cs; + shp.session()->selection().clear_stripables (); + } } void -RouteProcessorSelection::clear_routes () +RouteProcessorSelection::presentation_info_changed (PropertyChange const & what_changed) { - PresentationInfo::ChangeSuspender cs; + Session* s = shp.session(); - for (AxisViewSelection::iterator i = axes.begin(); i != axes.end(); ++i) { - (*i)->set_selected (false); + if (!s) { + /* too early ... session handle provider doesn't know about the + session yet. + */ + return; } - axes.clear (); - drop_connections (); -} -void -RouteProcessorSelection::add (XMLNode* node) -{ - // XXX check for duplicate - processors.add (node); - ProcessorsChanged(); -} + PropertyChange pc; + pc.add (Properties::selected); -void -RouteProcessorSelection::set (XMLNode* node) -{ - clear_processors (); - processors.set (node); - ProcessorsChanged (); + CoreSelection::StripableAutomationControls sc; + s->selection().get_stripables (sc); + + for (AxisViewSelection::iterator a = axes.begin(); a != axes.end(); ++a) { + (*a)->set_selected (false); + } + + axes.clear (); + + for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) { + AxisView* av = avp.axis_view_by_stripable ((*i).stripable); + if (av) { + axes.insert (av); + av->set_selected (true); + } + } } void RouteProcessorSelection::add (AxisView* r) { + if (!shp.session()) { + return; + } + if (axes.insert (r).second) { - r->set_selected (true); + shp.session()->selection().add (r->stripable(), boost::shared_ptr()); MixerStrip* ms = dynamic_cast (r); @@ -114,23 +131,20 @@ RouteProcessorSelection::add (AxisView* r) void RouteProcessorSelection::remove (AxisView* r) { - ENSURE_GUI_THREAD (*this, &RouteProcessorSelection::remove, r); - PresentationInfo::ChangeSuspender cs; - - AxisViewSelection::iterator i; - if ((i = find (axes.begin(), axes.end(), r)) != axes.end()) { - AxisView* av = *i; - axes.erase (i); - av->set_selected (false); + if (!shp.session()) { + return; } + ENSURE_GUI_THREAD (*this, &RouteProcessorSelection::remove, r); + shp.session()->selection().remove (r->stripable(), boost::shared_ptr()); } void RouteProcessorSelection::set (AxisView* r) { - PresentationInfo::ChangeSuspender cs; - clear_routes (); - add (r); + if (!shp.session()) { + return; + } + shp.session()->selection().set (r->stripable(), boost::shared_ptr()); } bool diff --git a/gtk2_ardour/route_processor_selection.h b/gtk2_ardour/route_processor_selection.h index c708723e4d..b0e7c36fbd 100644 --- a/gtk2_ardour/route_processor_selection.h +++ b/gtk2_ardour/route_processor_selection.h @@ -26,34 +26,36 @@ #include "processor_selection.h" #include "route_ui_selection.h" -class RouteProcessorSelection : public PBD::ScopedConnectionList, public sigc::trackable +namespace ARDOUR { + class SessionHandlePtr; +} + +class AxisViewProvider; + +class RouteProcessorSelection : public ProcessorSelection { public: - ProcessorSelection processors; AxisViewSelection axes; - RouteProcessorSelection(); + RouteProcessorSelection (ARDOUR::SessionHandlePtr&, AxisViewProvider&); RouteProcessorSelection& operator= (const RouteProcessorSelection& other); - sigc::signal ProcessorsChanged; - void clear (); bool empty(); - void set (XMLNode* node); - void add (XMLNode* node); - void set (AxisView*); void add (AxisView*); void remove (AxisView*); + bool selected (AxisView*); - void clear_processors (); void clear_routes (); - bool selected (AxisView*); + void presentation_info_changed (PBD::PropertyChange const & what_changed); private: + ARDOUR::SessionHandlePtr& shp; + AxisViewProvider& avp; void removed (AxisView*); }; diff --git a/gtk2_ardour/selection.cc b/gtk2_ardour/selection.cc index 1114222248..23d89d0786 100644 --- a/gtk2_ardour/selection.cc +++ b/gtk2_ardour/selection.cc @@ -24,9 +24,10 @@ #include "pbd/stacktrace.h" #include "pbd/types_convert.h" +#include "ardour/evoral_types_convert.h" #include "ardour/playlist.h" #include "ardour/rc_configuration.h" -#include "ardour/evoral_types_convert.h" +#include "ardour/selection.h" #include "control_protocol/control_protocol.h" @@ -65,9 +66,6 @@ Selection::Selection (const PublicEditor* e) /* we have disambiguate which remove() for the compiler */ - void (Selection::*track_remove)(TimeAxisView*) = &Selection::remove; - TimeAxisView::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (track_remove, this, _1), gui_context()); - void (Selection::*marker_remove)(ArdourMarker*) = &Selection::remove; ArdourMarker::CatchDeletion.connect (*this, MISSING_INVALIDATOR, boost::bind (marker_remove, this, _1), gui_context()); @@ -130,20 +128,6 @@ Selection::clear_objects (bool with_signal) clear_midi_regions (with_signal); } -void -Selection::clear_tracks (bool with_signal) -{ - if (!tracks.empty()) { - PresentationInfo::ChangeSuspender cs; - - for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) { - (*x)->set_selected (false); - } - - tracks.clear (); - } -} - void Selection::clear_time (bool with_signal) { @@ -271,38 +255,6 @@ Selection::toggle (boost::shared_ptr pl) PlaylistsChanged (); } -void -Selection::toggle (const TrackViewList& track_list) -{ - PresentationInfo::ChangeSuspender cs; - - for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) { - if (dynamic_cast (*i)) { - continue; - } - toggle ((*i)); - } -} - -void -Selection::toggle (TimeAxisView* track) -{ - if (dynamic_cast (track)) { - return; - } - - TrackSelection::iterator i; - - if ((i = find (tracks.begin(), tracks.end(), track)) == tracks.end()) { - tracks.push_back (track); - track->set_selected (true); - } else { - tracks.erase (i); - track->set_selected (false); - } - -} - void Selection::toggle (const MidiNoteSelection& midi_note_list) { @@ -405,10 +357,10 @@ Selection::toggle (framepos_t start, framepos_t end) void Selection::add (boost::shared_ptr pl) { - clear_time(); //enforce object/range exclusivity - clear_tracks(); //enforce object/track exclusivity if (find (playlists.begin(), playlists.end(), pl) == playlists.end()) { + clear_time(); //enforce object/range exclusivity + clear_tracks(); //enforce object/track exclusivity pl->use (); playlists.push_back(pl); PlaylistsChanged (); @@ -418,9 +370,6 @@ Selection::add (boost::shared_ptr pl) void Selection::add (const list >& pllist) { - clear_time(); //enforce object/range exclusivity - clear_tracks(); //enforce object/track exclusivity - bool changed = false; for (list >::const_iterator i = pllist.begin(); i != pllist.end(); ++i) { @@ -432,51 +381,21 @@ Selection::add (const list >& pllist) } if (changed) { + clear_time(); //enforce object/range exclusivity + clear_tracks(); //enforce object/track exclusivity PlaylistsChanged (); } } -void -Selection::add (TrackViewList const & track_list) -{ - clear_objects(); //enforce object/range exclusivity - - PresentationInfo::ChangeSuspender cs; - - TrackViewList added = tracks.add (track_list); - - if (!added.empty()) { - for (TrackViewList::iterator x = added.begin(); x != added.end(); ++x) { - if (dynamic_cast (*x)) { - continue; - } - (*x)->set_selected (true); - } - } -} - -void -Selection::add (TimeAxisView* track) -{ - if (dynamic_cast (track)) { - return; - } - - TrackViewList tr; - tr.push_back (track); - add (tr); -} - void Selection::add (const MidiNoteSelection& midi_list) { - clear_time(); //enforce object/range exclusivity - clear_tracks(); //enforce object/track exclusivity - const MidiNoteSelection::const_iterator b = midi_list.begin(); const MidiNoteSelection::const_iterator e = midi_list.end(); if (!midi_list.empty()) { + clear_time(); //enforce object/range exclusivity + clear_tracks(); //enforce object/track exclusivity midi_notes.insert (midi_notes.end(), b, e); MidiNotesChanged (); } @@ -496,9 +415,6 @@ Selection::add (MidiCutBuffer* midi) void Selection::add (vector& v) { - clear_time(); //enforce object/range exclusivity - clear_tracks(); //enforce object/track exclusivity - /* XXX This method or the add (const RegionSelection&) needs to go */ @@ -511,6 +427,8 @@ Selection::add (vector& v) } if (changed) { + clear_time(); //enforce object/range exclusivity + clear_tracks(); //enforce object/track exclusivity RegionsChanged (); } } @@ -518,9 +436,6 @@ Selection::add (vector& v) void Selection::add (const RegionSelection& rs) { - clear_time(); //enforce object/range exclusivity - clear_tracks(); //enforce object/track exclusivity - /* XXX This method or the add (const vector&) needs to go */ @@ -533,6 +448,8 @@ Selection::add (const RegionSelection& rs) } if (changed) { + clear_time(); //enforce object/range exclusivity + clear_tracks(); //enforce object/track exclusivity RegionsChanged (); } } @@ -540,12 +457,11 @@ Selection::add (const RegionSelection& rs) void Selection::add (RegionView* r) { - clear_time(); //enforce object/range exclusivity - clear_tracks(); //enforce object/track exclusivity - if (find (regions.begin(), regions.end(), r) == regions.end()) { bool changed = regions.add (r); if (changed) { + clear_time(); //enforce object/range exclusivity + clear_tracks(); //enforce object/track exclusivity RegionsChanged (); } } @@ -624,16 +540,18 @@ Selection::replace (uint32_t sid, framepos_t start, framepos_t end) void Selection::add (boost::shared_ptr cl) { - clear_time(); //enforce object/range exclusivity - clear_tracks(); //enforce object/track exclusivity + boost::shared_ptr al = boost::dynamic_pointer_cast(cl); - boost::shared_ptr al - = boost::dynamic_pointer_cast(cl); if (!al) { warning << "Programming error: Selected list is not an ARDOUR::AutomationList" << endmsg; return; } + if (!cl->empty()) { + clear_time(); //enforce object/range exclusivity + clear_tracks(); //enforce object/track exclusivity + } + /* The original may change so we must store a copy (not a pointer) here. * e.g AutomationLine rewrites the list with gain mapping. * the downside is that we can't perfom duplicate checks. @@ -643,38 +561,6 @@ Selection::add (boost::shared_ptr cl) LinesChanged(); } -void -Selection::remove (TimeAxisView* track) -{ - list::iterator i; - if ((i = find (tracks.begin(), tracks.end(), track)) != tracks.end()) { - /* erase first, because set_selected() will remove the track - from the selection, invalidating the iterator. - - In fact, we don't really even need to do the erase, but this is - a hangover of axis view selection being in the GUI. - */ - tracks.erase (i); - track->set_selected (false); - } -} - -void -Selection::remove (const TrackViewList& track_list) -{ - PresentationInfo::ChangeSuspender cs; - - for (TrackViewList::const_iterator i = track_list.begin(); i != track_list.end(); ++i) { - - TrackViewList::iterator x = find (tracks.begin(), tracks.end(), *i); - - if (x != tracks.end()) { - tracks.erase (x); - (*i)->set_selected (false); - } - } -} - void Selection::remove (ControlPoint* p) { @@ -800,68 +686,13 @@ Selection::remove (boost::shared_ptr ac) } } -void -Selection::set (TimeAxisView* track) -{ - if (dynamic_cast (track)) { - return; - } - clear_objects (); //enforce object/range exclusivity - - PresentationInfo::ChangeSuspender cs; - - if (!tracks.empty()) { - - if (tracks.size() == 1 && tracks.front() == track) { - /* already single selection: nothing to do */ - return; - } - - for (TrackViewList::iterator x = tracks.begin(); x != tracks.end(); ++x) { - (*x)->set_selected (false); - } - - tracks.clear (); - } - - add (track); -} - -void -Selection::set (const TrackViewList& track_list) -{ - clear_objects(); //enforce object/range exclusivity - - - TrackViewList to_be_added; - TrackViewList to_be_removed; - - for (TrackViewList::const_iterator x = tracks.begin(); x != tracks.end(); ++x) { - if (find (track_list.begin(), track_list.end(), *x) == track_list.end()) { - to_be_removed.push_back (*x); - } - } - - for (TrackViewList::const_iterator x = track_list.begin(); x != track_list.end(); ++x) { - if (dynamic_cast (*x)) { - continue; - } - if (find (tracks.begin(), tracks.end(), *x) == tracks.end()) { - to_be_added.push_back (*x); - } - } - - PresentationInfo::ChangeSuspender cs; - remove (to_be_removed); - add (to_be_added); - -} - void Selection::set (const MidiNoteSelection& midi_list) { - clear_time (); //enforce region/object exclusivity - clear_tracks(); //enforce object/track exclusivity + if (!midi_list.empty()) { + clear_time (); //enforce region/object exclusivity + clear_tracks(); //enforce object/track exclusivity + } clear_objects (); add (midi_list); } @@ -869,8 +700,10 @@ Selection::set (const MidiNoteSelection& midi_list) void Selection::set (boost::shared_ptr playlist) { - clear_time (); //enforce region/object exclusivity - clear_tracks(); //enforce object/track exclusivity + if (playlist) { + clear_time (); //enforce region/object exclusivity + clear_tracks(); //enforce object/track exclusivity + } clear_objects (); add (playlist); } @@ -878,7 +711,9 @@ Selection::set (boost::shared_ptr playlist) void Selection::set (const list >& pllist) { - clear_time(); //enforce region/object exclusivity + if (!pllist.empty()) { + clear_time(); //enforce region/object exclusivity + } clear_objects (); add (pllist); } @@ -886,8 +721,10 @@ Selection::set (const list >& pllist) void Selection::set (const RegionSelection& rs) { - clear_time(); //enforce region/object exclusivity - clear_tracks(); //enforce object/track exclusivity + if (!rs.empty()) { + clear_time(); //enforce region/object exclusivity + clear_tracks(); //enforce object/track exclusivity + } clear_objects(); regions = rs; RegionsChanged(); /* EMIT SIGNAL */ @@ -896,8 +733,10 @@ Selection::set (const RegionSelection& rs) void Selection::set (MidiRegionView* mrv) { - clear_time(); //enforce region/object exclusivity - clear_tracks(); //enforce object/track exclusivity + if (mrv) { + clear_time(); //enforce region/object exclusivity + clear_tracks(); //enforce object/track exclusivity + } clear_objects (); add (mrv); } @@ -905,8 +744,10 @@ Selection::set (MidiRegionView* mrv) void Selection::set (RegionView* r, bool /*also_clear_tracks*/) { - clear_time(); //enforce region/object exclusivity - clear_tracks(); //enforce object/track exclusivity + if (r) { + clear_time(); //enforce region/object exclusivity + clear_tracks(); //enforce object/track exclusivity + } clear_objects (); add (r); } @@ -914,8 +755,11 @@ Selection::set (RegionView* r, bool /*also_clear_tracks*/) void Selection::set (vector& v) { - clear_time(); //enforce region/object exclusivity - clear_tracks(); //enforce object/track exclusivity + if (!v.empty()) { + clear_time(); //enforce region/object exclusivity + clear_tracks(); //enforce object/track exclusivity + } + clear_objects(); add (v); @@ -999,12 +843,6 @@ Selection::selected (ArdourMarker* m) const return find (markers.begin(), markers.end(), m) != markers.end(); } -bool -Selection::selected (TimeAxisView* tv) const -{ - return tv->selected (); -} - bool Selection::selected (RegionView* rv) const { @@ -1384,26 +1222,19 @@ Selection::set_state (XMLNode const & node, int) clear_midi_notes (); clear_points (); clear_time (); - clear_tracks (); clear_markers (); RegionSelection selected_regions; + /* NOTE: stripable/time-axis-view selection is saved/restored by + * ARDOUR::CoreSelection, not this Selection object + */ + PBD::ID id; XMLNodeList children = node.children (); - for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { - if ((*i)->name() == X_("RouteView")) { - - if (!(*i)->get_property (X_("id"), id)) { - assert(false); // handle this more gracefully? - } - RouteTimeAxisView* rtv = editor->get_route_view_by_route_id (id); - if (rtv) { - add (rtv); - } - - } else if ((*i)->name() == X_("Region")) { + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + if ((*i)->name() == X_("Region")) { if (!(*i)->get_property (X_("id"), id)) { assert(false); @@ -1594,3 +1425,252 @@ Selection::remove_regions (TimeAxisView* t) i = tmp; } } + +/* TIME AXIS VIEW ... proxy for Stripable/Controllable + * + * public methods just modify the CoreSelection; PresentationInfo::Changed will + * trigger Selection::core_selection_changed() and we will update our own data + * structures there. + */ + +void +Selection::toggle (const TrackViewList& track_list) +{ + TrackViewList t = add_grouped_tracks (track_list); + + CoreSelection& selection (editor->session()->selection()); + PresentationInfo::ChangeSuspender cs; + + for (TrackSelection::const_iterator i = t.begin(); i != t.end(); ++i) { + boost::shared_ptr s = (*i)->stripable (); + boost::shared_ptr c = (*i)->control (); + selection.toggle (s, c); + } +} + +void +Selection::toggle (TimeAxisView* track) +{ + if (dynamic_cast (track)) { + return; + } + + TrackViewList tr; + tr.push_back (track); + toggle (tr); +} + +void +Selection::add (TrackViewList const & track_list) +{ + TrackViewList t = add_grouped_tracks (track_list); + + CoreSelection& selection (editor->session()->selection()); + PresentationInfo::ChangeSuspender cs; + + for (TrackSelection::const_iterator i = t.begin(); i != t.end(); ++i) { + boost::shared_ptr s = (*i)->stripable (); + boost::shared_ptr c = (*i)->control (); + selection.add (s, c); + } +} + +void +Selection::add (TimeAxisView* track) +{ + if (dynamic_cast (track)) { + return; + } + + TrackViewList tr; + tr.push_back (track); + add (tr); +} + +void +Selection::remove (TimeAxisView* track) +{ + if (dynamic_cast (track)) { + return; + } + + TrackViewList tvl; + tvl.push_back (track); + remove (tvl); +} + +void +Selection::remove (const TrackViewList& t) +{ + CoreSelection& selection (editor->session()->selection()); + PresentationInfo::ChangeSuspender cs; + + for (TrackSelection::const_iterator i = t.begin(); i != t.end(); ++i) { + boost::shared_ptr s = (*i)->stripable (); + boost::shared_ptr c = (*i)->control (); + selection.remove (s, c); + } +} + +void +Selection::set (TimeAxisView* track) +{ + if (dynamic_cast (track)) { + return; + } + + TrackViewList tvl; + tvl.push_back (track); + set (tvl); +} + +void +Selection::set (const TrackViewList& track_list) +{ + TrackViewList t = add_grouped_tracks (track_list); + + CoreSelection& selection (editor->session()->selection()); + PresentationInfo::ChangeSuspender cs; + + selection.clear_stripables (); + + for (TrackSelection::const_iterator i = t.begin(); i != t.end(); ++i) { + boost::shared_ptr s = (*i)->stripable (); + boost::shared_ptr c = (*i)->control (); + selection.add (s, c); + } +} + +void +Selection::clear_tracks (bool) +{ + Session* s = editor->session(); + if (s) { + CoreSelection& selection (s->selection()); + selection.clear_stripables (); + } +} + +bool +Selection::selected (TimeAxisView* tv) const +{ + Session* session = editor->session(); + + if (!session) { + return false; + } + + CoreSelection& selection (session->selection()); + boost::shared_ptr s = tv->stripable (); + boost::shared_ptr c = tv->control (); + + if (c) { + return selection.selected (c); + } + + return selection.selected (s); +} + +TrackViewList +Selection::add_grouped_tracks (TrackViewList const & t) +{ + TrackViewList added; + + for (TrackSelection::const_iterator i = t.begin(); i != t.end(); ++i) { + if (dynamic_cast (*i)) { + continue; + } + + /* select anything in the same select-enabled route group */ + ARDOUR::RouteGroup* rg = (*i)->route_group (); + + if (rg && rg->is_active() && rg->is_select ()) { + + TrackViewList tr = editor->axis_views_from_routes (rg->route_list ()); + + for (TrackViewList::iterator j = tr.begin(); j != tr.end(); ++j) { + + /* Do not add the trackview passed in as an + * argument, because we want that to be on the + * end of the list. + */ + + if (*j != *i) { + if (!added.contains (*j)) { + added.push_back (*j); + } + } + } + } + } + + /* now add the the trackview's passed in as actual arguments */ + added.insert (added.end(), t.begin(), t.end()); + + return added; +} + +#if 0 +static void dump_tracks (Selection const & s) +{ + cerr << "--TRACKS [" << s.tracks.size() << ']' << ":\n"; + for (TrackViewList::const_iterator x = s.tracks.begin(); x != s.tracks.end(); ++x) { + cerr << (*x)->name() << ' ' << (*x)->stripable() << " C = " << (*x)->control() << endl; + } + cerr << "///\n"; +} +#endif + +void +Selection::core_selection_changed (PropertyChange const & what_changed) +{ + PropertyChange pc; + + pc.add (Properties::selected); + + if (!what_changed.contains (pc)) { + return; + } + + CoreSelection& selection (editor->session()->selection()); + + if (selection.selected()) { + clear_objects(); // enforce object/range exclusivity + } + + tracks.clear (); // clear stage for whatever tracks are now selected (maybe none) + + TrackViewList const & tvl (editor->get_track_views ()); + + for (TrackViewList::const_iterator x = tvl.begin(); x != tvl.end(); ++x) { + + boost::shared_ptr s = (*x)->stripable (); + boost::shared_ptr c = (*x)->control (); + + if (!s) { + continue; + } + + TimeAxisView* tav = editor->time_axis_view_from_stripable (s); + + if (!tav) { + continue; + } + + if ((c && selection.selected (c)) || selection.selected (s)) { + tracks.push_back (tav); + } + + TimeAxisView::Children kids = tav->get_child_list (); + + for (TimeAxisView::Children::iterator j = kids.begin(); j != kids.end(); ++j) { + s = (*j)->stripable (); + c = (*j)->control (); + + if ((c && selection.selected (c)) || selection.selected (s)) { + tracks.push_back ((*j).get()); + } + } + + } +} diff --git a/gtk2_ardour/selection.h b/gtk2_ardour/selection.h index 959690b95d..7e8ba19d7b 100644 --- a/gtk2_ardour/selection.h +++ b/gtk2_ardour/selection.h @@ -225,9 +225,13 @@ class Selection : public sigc::trackable, public PBD::ScopedConnectionList std::list > > pending_midi_note_selection; + void core_selection_changed (PBD::PropertyChange const & pc); + private: PublicEditor const * editor; uint32_t next_time_id; + + TrackViewList add_grouped_tracks (TrackViewList const & t); }; bool operator==(const Selection& a, const Selection& b); diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc index 9997349628..5bd26b193a 100644 --- a/gtk2_ardour/time_axis_view.cc +++ b/gtk2_ardour/time_axis_view.cc @@ -769,19 +769,9 @@ TimeAxisView::set_selected (bool yn) time_axis_vbox.set_name (controls_base_unselected_name); hide_selection (); - - /* children will be set for the yn=true case. but when deselecting - the editor only has a list of top-level trackviews, so we - have to do this here. - */ - - for (Children::iterator i = children.begin(); i != children.end(); ++i) { - (*i)->set_selected (false); - } } time_axis_frame.show(); - } void diff --git a/gtk2_ardour/track_selection.cc b/gtk2_ardour/track_selection.cc index ac3d8b68e5..ad761ef4cd 100644 --- a/gtk2_ardour/track_selection.cc +++ b/gtk2_ardour/track_selection.cc @@ -39,47 +39,3 @@ TrackSelection::~TrackSelection () { } -TrackViewList -TrackSelection::add (TrackViewList const & t) -{ - TrackViewList added; - - for (TrackSelection::const_iterator i = t.begin(); i != t.end(); ++i) { - if (dynamic_cast (*i)) { - continue; - } - - /* select anything in the same select-enabled route group */ - ARDOUR::RouteGroup* rg = (*i)->route_group (); - - if (rg && rg->is_active() && rg->is_select ()) { - - TrackViewList tr = _editor->axis_views_from_routes (rg->route_list ()); - - for (TrackViewList::iterator j = tr.begin(); j != tr.end(); ++j) { - - /* Do not add the trackview passed in as an - * argument, because we want that to be on the - * end of the list. - */ - - if (*j != *i) { - if (!contains (*j)) { - added.push_back (*j); - push_back (*j); - } - } - } - } - - /* now add the the trackview's passed in as actual arguments */ - - if (!contains (*i)) { - added.push_back (*i); - push_back (*i); - } - } - - - return added; -} diff --git a/gtk2_ardour/track_selection.h b/gtk2_ardour/track_selection.h index 2e9927ab42..8c7aac45c4 100644 --- a/gtk2_ardour/track_selection.h +++ b/gtk2_ardour/track_selection.h @@ -35,8 +35,6 @@ public: virtual ~TrackSelection (); - TrackViewList add (TrackViewList const &); - template void foreach_time_axis (Function f) { for (iterator i = begin(); i != end(); ++i) { diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 646ae17d6b..b925e2657b 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -202,6 +202,7 @@ gtk2_ardour_sources = [ 'port_matrix_labels.cc', 'port_matrix_row_labels.cc', 'processor_box.cc', + 'processor_selection.cc', 'patch_change_dialog.cc', 'progress_reporter.cc', 'prompter.cc', -- 2.30.2