X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_routes.cc;h=45b9797f78d9b51e0d59793fc1f3095958462e0f;hb=3d15499cdacacbafa32c8fcfb1389e6f0716ce9c;hp=fe1e5af62b56405c2ad97a0aa4450441983464cc;hpb=48da03e301c8145f9568df0692001652ea31b796;p=ardour.git diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index fe1e5af62b..45b9797f78 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" @@ -77,6 +78,7 @@ struct ColumnInfo { EditorRoutes::EditorRoutes (Editor* e) : EditorComponent (e) , _ignore_reorder (false) + , _ignore_selection_change (false) , _no_redisplay (false) , _adding_routes (false) , _route_deletion_in_progress (false) @@ -85,7 +87,6 @@ EditorRoutes::EditorRoutes (Editor* e) , _queue_tv_update (0) , _menu (0) , old_focus (0) - , selection_countdown (0) , name_editable (0) { static const int column_width = 22; @@ -252,14 +253,13 @@ EditorRoutes::EditorRoutes (Editor* e) } _display.set_headers_visible (true); - _display.get_selection()->set_mode (SELECTION_SINGLE); + _display.get_selection()->set_mode (SELECTION_MULTIPLE); _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter)); _display.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::selection_changed)); _display.set_reorderable (true); _display.set_name (X_("EditGroupList")); _display.set_rules_hint (true); _display.set_size_request (100, -1); - _display.add_object_drag (_columns.stripable.index(), "routes"); CellRendererText* name_cell = dynamic_cast (_display.get_column_cell_renderer (_name_column)); @@ -318,7 +318,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::sync_treeview_from_presentation_info, this), gui_context()); } bool @@ -359,7 +358,6 @@ EditorRoutes::enter_notify (GdkEventCrossing*) /* arm counter so that ::selection_filter() will deny selecting anything for the * next two attempts to change selection status. */ - selection_countdown = 2; _scroller.grab_focus (); Keyboard::magic_widget_grab_focus (); return false; @@ -368,8 +366,6 @@ EditorRoutes::enter_notify (GdkEventCrossing*) bool EditorRoutes::leave_notify (GdkEventCrossing*) { - selection_countdown = 0; - if (old_focus) { old_focus->grab_focus (); old_focus = 0; @@ -557,16 +553,6 @@ EditorRoutes::build_menu () items.push_back (MenuElem (_("Only Show Tracks with Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead))); } -void -EditorRoutes::show_menu () -{ - if (_menu == 0) { - build_menu (); - } - - _menu->popup (1, gtk_get_current_event_time()); -} - void EditorRoutes::redisplay_real () { @@ -736,7 +722,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) { @@ -763,7 +752,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; @@ -841,7 +829,10 @@ 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); + _display.set_model (_model); + } /* now update route order keys from the treeview/track display order */ @@ -879,6 +870,8 @@ EditorRoutes::route_removed (TimeAxisView *tv) TreeModel::Children rows = _model->children(); TreeModel::Children::iterator ri; + PBD::Unwinder uw (_ignore_selection_change, true); + for (ri = rows.begin(); ri != rows.end(); ++ri) { if ((*ri)[_columns.tv] == tv) { PBD::Unwinder uw (_route_deletion_in_progress, true); @@ -886,10 +879,6 @@ EditorRoutes::route_removed (TimeAxisView *tv) break; } } - - /* the deleted signal for the treeview/model will take - care of any updates. - */ } void @@ -925,7 +914,6 @@ EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost: if (what_changed.contains (ARDOUR::Properties::hidden)) { (*i)[_columns.visible] = !stripable->presentation_info().hidden(); - cerr << stripable->name() << " visibility changed, redisplay\n"; redisplay (); } @@ -956,8 +944,7 @@ EditorRoutes::update_visibility () (*i)[_columns.visible] = tv->marked_for_display (); } - /* force route order keys catch up with visibility changes - */ + /* force route order keys catch up with visibility changes */ sync_presentation_info_from_treeview (); } @@ -1010,94 +997,41 @@ EditorRoutes::sync_presentation_info_from_treeview () DEBUG_TRACE (DEBUG::OrderKeys, "editor sync presentation info from treeview\n"); - TreeModel::Children::iterator ri; bool change = false; PresentationInfo::order_t order = 0; - bool master_is_first = false; - uint32_t count = 0; - - OrderingKeys sorted; - const size_t cmp_max = rows.size (); - - // special case master if it's got PI order 0 lets keep it there - if (_session->master_out() && (_session->master_out()->presentation_info().order() == 0)) { - order++; - master_is_first = true; - } - for (ri = rows.begin(); ri != rows.end(); ++ri) { + PresentationInfo::ChangeSuspender cs; + for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) { boost::shared_ptr stripable = (*ri)[_columns.stripable]; bool visible = (*ri)[_columns.visible]; - /* Monitor and Auditioner do not get their presentation - * info reset here. - */ - +#ifndef NDEBUG // these should not exist in the treeview + assert (stripable); if (stripable->is_monitor() || stripable->is_auditioner()) { + assert (0); continue; } +#endif stripable->presentation_info().set_hidden (!visible); - /* special case master if it's got PI order 0 lets keep it there - * but still allow master to move if first non-master route has - * presentation order 1 - */ - if ((count == 0) && master_is_first && (stripable->presentation_info().order() == 1)) { - master_is_first = false; // someone has moved master - order = 0; - } - - if (stripable->is_master() && master_is_first) { - if (count) { - continue; - } else { - count++; - continue; - } - } - if (order != stripable->presentation_info().order()) { - stripable->set_presentation_order (order, false); + stripable->set_presentation_order (order); change = true; } - - sorted.push_back (OrderKeys (order, stripable, cmp_max)); - ++order; - ++count; } - if (!change) { - // VCA (and Mixbus) special cases according to SortByNewDisplayOrder - uint32_t n = 0; - SortByNewDisplayOrder cmp; - sort (sorted.begin(), sorted.end(), cmp); - for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) { - if (sr->old_display_order != n) { - change = true; - } - } - if (change) { - n = 0; - for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) { - if (sr->stripable->presentation_info().order() != n) { - sr->stripable->set_presentation_order (n, false); - } - } - } - } + change |= _session->ensure_stripable_sort_order (); if (change) { - DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from editor GUI\n"); - _session->notify_presentation_info_change (); _session->set_dirty(); } } void -EditorRoutes::sync_treeview_from_presentation_info () +EditorRoutes::sync_treeview_from_presentation_info (PropertyChange const & what_changed) { /* Some route order key(s) have been changed, make sure that we update out tree/list model and GUI to reflect the change. @@ -1109,53 +1043,78 @@ EditorRoutes::sync_treeview_from_presentation_info () DEBUG_TRACE (DEBUG::OrderKeys, "editor sync model from presentation info.\n"); - vector neworder; + PropertyChange hidden_or_order; + hidden_or_order.add (Properties::hidden); + hidden_or_order.add (Properties::order); + TreeModel::Children rows = _model->children(); - uint32_t old_order = 0; + bool changed = false; - if (rows.empty()) { - return; - } + if (what_changed.contains (hidden_or_order)) { + vector neworder; + uint32_t old_order = 0; - OrderingKeys sorted; - const size_t cmp_max = rows.size (); + if (rows.empty()) { + return; + } - for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) { - boost::shared_ptr stripable = (*ri)[_columns.stripable]; - /* use global order */ - sorted.push_back (OrderKeys (old_order, stripable, cmp_max)); - } + TreeOrderKeys sorted; + for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) { + boost::shared_ptr stripable = (*ri)[_columns.stripable]; + /* use global order */ + sorted.push_back (TreeOrderKey (old_order, stripable)); + } - SortByNewDisplayOrder cmp; + TreeOrderKeySorter cmp; - sort (sorted.begin(), sorted.end(), cmp); - neworder.assign (sorted.size(), 0); + sort (sorted.begin(), sorted.end(), cmp); + neworder.assign (sorted.size(), 0); + + uint32_t n = 0; - uint32_t n = 0; + for (TreeOrderKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) { - for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) { + neworder[n] = sr->old_display_order; - neworder[n] = sr->old_display_order; + if (sr->old_display_order != n) { + changed = true; + } + } - if (sr->old_display_order != n) { - changed = true; + if (changed) { + Unwinder uw (_ignore_reorder, true); + /* prevent traverse_cells: assertion 'row_path != NULL' + * in case of DnD re-order: row-removed + row-inserted. + * + * The rows (stripables) are not actually removed from the model, + * but only from the display in the DnDTreeView. + * ->reorder() will fail to find the row_path. + * (re-order drag -> remove row -> sync PI from TV -> notify -> sync TV from PI -> crash) + */ + Unwinder uw2 (_ignore_selection_change, true); + + _display.unset_model(); + _model->reorder (neworder); + _display.set_model (_model); } } - if (changed) { - Unwinder uw (_ignore_reorder, true); - /* prevent traverse_cells: assertion 'row_path != NULL' - * in case of DnD re-order: row-removed + row-inserted. - * - * The rows (stripables) are not actually removed from the model, - * but only from the display in the DnDTreeView. - * ->reorder() will fail to find the row_path. - * (re-order drag -> remove row -> rync PI from TV -> notify -> sync TV from PI -> crash) + if (changed || what_changed.contains (Properties::selected)) { + /* by the time this is invoked, the GUI Selection model has + * already updated itself. */ - _display.unset_model(); - _model->reorder (neworder); - _display.set_model (_model); + PBD::Unwinder uw (_ignore_selection_change, true); + + /* 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->is_selected()) { + _display.get_selection()->select (*ri); + } else { + _display.get_selection()->unselect (*ri); + } + } } redisplay (); @@ -1427,7 +1386,10 @@ bool EditorRoutes::button_press (GdkEventButton* ev) { if (Keyboard::is_context_menu_event (ev)) { - show_menu (); + if (_menu == 0) { + build_menu (); + } + _menu->popup (ev->button, ev->time); return true; } @@ -1461,6 +1423,10 @@ EditorRoutes::button_press (GdkEventButton* ev) void EditorRoutes::selection_changed () { + if (_ignore_selection_change) { + return; + } + _editor->begin_reversible_selection_op (X_("Select Track from Route List")); if (_display.get_selection()->count_selected_rows() > 0) { @@ -1469,8 +1435,6 @@ EditorRoutes::selection_changed () TreeView::Selection::ListHandle_Path rows = _display.get_selection()->get_selected_rows (); TrackViewList selected; - _editor->get_selection().clear_regions (); - for (TreeView::Selection::ListHandle_Path::iterator i = rows.begin(); i != rows.end(); ++i) { if ((iter = _model->get_iter (*i))) { @@ -1492,50 +1456,31 @@ EditorRoutes::selection_changed () } bool -EditorRoutes::selection_filter (Glib::RefPtr const &, TreeModel::Path const&, bool /*selected*/) +EditorRoutes::selection_filter (Glib::RefPtr const& model, TreeModel::Path const& path, bool /*selected*/) { - if (selection_countdown) { - if (--selection_countdown == 0) { - return true; - } else { - /* no selection yet ... */ + TreeModel::iterator iter = model->get_iter (path); + if (iter) { + boost::shared_ptr stripable = (*iter)[_columns.stripable]; + if (boost::dynamic_pointer_cast (stripable)) { return false; } } + return true; } -struct PresentationInfoRouteSorter -{ - bool operator() (boost::shared_ptr a, boost::shared_ptr b) { - if (a->is_master()) { - /* master before everything else */ - return true; - } else if (b->is_master()) { - /* everything else before master */ - return false; - } - return a->presentation_info().order () < b->presentation_info().order (); - } -}; - -struct PresentationInfoVCASorter -{ - bool operator() (boost::shared_ptr a, boost::shared_ptr b) { - return a->presentation_info().order () < b->presentation_info().order (); - } -}; - void EditorRoutes::initial_display () { - DisplaySuspender ds; - _model->clear (); if (!_session) { + _model->clear (); return; } + DisplaySuspender ds; + _model->clear (); + StripableList s; RouteList r (*_session->get_routes()); @@ -1549,153 +1494,123 @@ EditorRoutes::initial_display () } _editor->add_stripables (s); - sync_treeview_from_presentation_info (); -} - -void -EditorRoutes::display_drag_data_received (const RefPtr& context, - int x, int y, - const SelectionData& data, - guint info, guint time) -{ - if (data.get_target() == "GTK_TREE_MODEL_ROW") { - _display.on_drag_data_received (context, x, y, data, info, time); - return; - } - context->drag_finish (true, false, time); + sync_treeview_from_presentation_info (Properties::order); } struct ViewStripable { TimeAxisView* tav; boost::shared_ptr stripable; - uint32_t old_order; - ViewStripable (TimeAxisView* t, boost::shared_ptr s, uint32_t n) - : tav (t), stripable (s), old_order (n) {} + ViewStripable (TimeAxisView* t, boost::shared_ptr s) + : tav (t), stripable (s) {} }; void EditorRoutes::move_selected_tracks (bool up) { - if (_editor->selection->tracks.empty()) { + TimeAxisView* scroll_to = 0; + StripableList sl; + _session->get_stripables (sl); + + if (sl.size() < 2) { + /* nope */ return; } + sl.sort (Stripable::Sorter()); + std::list view_stripables; - std::vector neworder; - TreeModel::Children rows = _model->children(); - TreeModel::Children::iterator ri; - TreeModel::Children::size_type n; - for (n = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++n) { - TimeAxisView* tv = (*ri)[_columns.tv]; - boost::shared_ptr stripable = (*ri)[_columns.stripable]; - view_stripables.push_back (ViewStripable (tv, stripable, n)); + /* build a list that includes time axis view information */ + + for (StripableList::const_iterator sli = sl.begin(); sli != sl.end(); ++sli) { + TimeAxisView* tv = _editor->time_axis_view_from_stripable (*sli); + view_stripables.push_back (ViewStripable (tv, *sli)); } - list::iterator trailing; - list::iterator leading; + /* for each selected stripable, move it above or below the adjacent + * stripable that has a time-axis view representation here. If there's + * no such representation, then + */ - if (up) { + list::iterator unselected_neighbour; + list::iterator vsi; - trailing = view_stripables.begin(); - leading = view_stripables.begin(); + { + PresentationInfo::ChangeSuspender cs; - ++leading; + if (up) { + unselected_neighbour = view_stripables.end (); + vsi = view_stripables.begin(); - while (leading != view_stripables.end()) { - if (_editor->selection->selected (leading->tav)) { - view_stripables.insert (trailing, ViewStripable (*leading)); - leading = view_stripables.erase (leading); - } else { - ++leading; - ++trailing; - } - } + while (vsi != view_stripables.end()) { - } else { + if (vsi->stripable->is_selected()) { - /* if we could use reverse_iterator in list::insert, this code - would be a beautiful reflection of the code above. but we can't - and so it looks like a bit of a mess. - */ + if (unselected_neighbour != view_stripables.end()) { + + PresentationInfo::order_t unselected_neighbour_order = unselected_neighbour->stripable->presentation_info().order(); + PresentationInfo::order_t my_order = vsi->stripable->presentation_info().order(); + + unselected_neighbour->stripable->set_presentation_order (my_order); + vsi->stripable->set_presentation_order (unselected_neighbour_order); + + if (!scroll_to) { + scroll_to = vsi->tav; + } + } + + } else { - trailing = view_stripables.end(); - leading = view_stripables.end(); + if (vsi->tav) { + unselected_neighbour = vsi; + } - --leading; if (leading == view_stripables.begin()) { return; } - --leading; - --trailing; + } - while (1) { + ++vsi; + } - if (_editor->selection->selected (leading->tav)) { - list::iterator tmp; + } else { - /* need to insert *after* trailing, not *before* it, - which is what insert (iter, val) normally does. - */ + unselected_neighbour = view_stripables.end(); + vsi = unselected_neighbour; - tmp = trailing; - tmp++; + do { - view_stripables.insert (tmp, ViewStripable (*leading)); + --vsi; - /* can't use iter = cont.erase (iter); form here, because - we need iter to move backwards. - */ + if (vsi->stripable->is_selected()) { - tmp = leading; - --tmp; + if (unselected_neighbour != view_stripables.end()) { - bool done = false; + PresentationInfo::order_t unselected_neighbour_order = unselected_neighbour->stripable->presentation_info().order(); + PresentationInfo::order_t my_order = vsi->stripable->presentation_info().order(); - if (leading == view_stripables.begin()) { - /* the one we've just inserted somewhere else - was the first in the list. erase this copy, - and then break, because we're done. - */ - done = true; - } + unselected_neighbour->stripable->set_presentation_order (my_order); + vsi->stripable->set_presentation_order (unselected_neighbour_order); - view_stripables.erase (leading); + if (!scroll_to) { + scroll_to = vsi->tav; + } + } - if (done) { - break; - } + } else { - leading = tmp; + if (vsi->tav) { + unselected_neighbour = vsi; + } - } else { - if (leading == view_stripables.begin()) { - break; } - --leading; - --trailing; - } - }; - } - for (leading = view_stripables.begin(); leading != view_stripables.end(); ++leading) { - neworder.push_back (leading->old_order); -#ifndef NDEBUG - if (leading->old_order != neworder.size() - 1) { - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("move %1 to %2\n", leading->old_order, neworder.size() - 1)); + } while (vsi != view_stripables.begin()); } -#endif } -#ifndef NDEBUG - DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n"); - for (vector::iterator i = neworder.begin(); i != neworder.end(); ++i) { - DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("\t%1\n", *i)); + if (scroll_to) { + _editor->ensure_time_axis_view_is_visible (*scroll_to, false); } - DEBUG_TRACE (DEBUG::OrderKeys, "-------\n"); -#endif - - - _model->reorder (neworder); } void @@ -1817,6 +1732,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); @@ -1867,7 +1783,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); } @@ -1894,5 +1810,6 @@ int EditorRoutes::plugin_setup (boost::shared_ptr r, boost::shared_ptr pi, ARDOUR::Route::PluginSetupOptions flags) { PluginSetupDialog psd (r, pi, flags); - return psd.run (); + int rv = psd.run (); + return rv + (psd.fan_out() ? 4 : 0); }