Add API allowing plugin preset load to affect automation
[ardour.git] / gtk2_ardour / editor_routes.cc
index fe1e5af62b56405c2ad97a0aa4450441983464cc..b1618bde49b12892daad4d100434d04bfc7f3c4b 100644 (file)
@@ -77,6 +77,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)
@@ -252,7 +253,7 @@ 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);
@@ -318,7 +319,7 @@ 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());
+       PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::presentation_info_changed, this, _1), gui_context());
 }
 
 bool
@@ -925,7 +926,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 ();
 
                        }
@@ -1019,6 +1019,8 @@ EditorRoutes::sync_presentation_info_from_treeview ()
        OrderingKeys sorted;
        const size_t cmp_max = rows.size ();
 
+       PresentationInfo::ChangeSuspender cs;
+
        // 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++;
@@ -1059,7 +1061,7 @@ EditorRoutes::sync_presentation_info_from_treeview ()
                }
 
                if (order != stripable->presentation_info().order()) {
-                       stripable->set_presentation_order (order, false);
+                       stripable->set_presentation_order (order);
                        change = true;
                }
 
@@ -1083,21 +1085,28 @@ EditorRoutes::sync_presentation_info_from_treeview ()
                        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);
+                                       sr->stripable->set_presentation_order (n);
                                }
                        }
                }
        }
+}
+
+void
+EditorRoutes::presentation_info_changed (PropertyChange const & what_changed)
+{
+       PropertyChange soh;
+       soh.add (Properties::selected);
+       soh.add (Properties::order);
+       soh.add (Properties::hidden);
 
-       if (change) {
-               DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from editor GUI\n");
-               _session->notify_presentation_info_change ();
-               _session->set_dirty();
+       if (what_changed.contains (soh)) {
+               sync_treeview_from_presentation_info (what_changed);
        }
 }
 
 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 +1118,86 @@ EditorRoutes::sync_treeview_from_presentation_info ()
 
        DEBUG_TRACE (DEBUG::OrderKeys, "editor sync model from presentation info.\n");
 
-       vector<int> 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)) {
 
-       OrderingKeys sorted;
-       const size_t cmp_max = rows.size ();
+               vector<int> neworder;
+               uint32_t old_order = 0;
+               bool changed = false;
 
-       for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
-               boost::shared_ptr<Stripable> stripable = (*ri)[_columns.stripable];
-               /* use global order */
-               sorted.push_back (OrderKeys (old_order, stripable, cmp_max));
-       }
+               if (rows.empty()) {
+                       return;
+               }
+
+               OrderingKeys sorted;
+               const size_t cmp_max = rows.size ();
+
+               for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
+                       boost::shared_ptr<Stripable> stripable = (*ri)[_columns.stripable];
+                       /* use global order */
+                       sorted.push_back (OrderKeys (old_order, stripable, cmp_max));
+               }
 
-       SortByNewDisplayOrder cmp;
+               SortByNewDisplayOrder 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 (OrderingKeys::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<bool> 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<bool> uw2 (_ignore_selection_change, true);
+
+                       _display.unset_model();
+                       _model->reorder (neworder);
+                       _display.set_model (_model);
                }
        }
 
-       if (changed) {
-               Unwinder<bool> 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)
-                */
-               _display.unset_model();
-               _model->reorder (neworder);
-               _display.set_model (_model);
+       if (what_changed.contains (Properties::selected)) {
+
+               TrackViewList tvl;
+               PBD::Unwinder<bool> uw (_ignore_selection_change, true);
+
+               /* step one: set the treeview model selection state */
+               for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
+                       boost::shared_ptr<Stripable> stripable = (*ri)[_columns.stripable];
+                       if (stripable && stripable->presentation_info().selected()) {
+                               TimeAxisView* tav = (*ri)[_columns.tv];
+                               if (tav) {
+                                       tvl.push_back (tav);
+                               }
+                               _display.get_selection()->select (*ri);
+                       } else {
+                               _display.get_selection()->unselect (*ri);
+                       }
+               }
+
+               /* step two: set the Selection (for stripables/routes) */
+               _editor->get_selection().set (tvl);
        }
 
        redisplay ();
@@ -1461,6 +1503,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 +1515,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,7 +1536,7 @@ EditorRoutes::selection_changed ()
 }
 
 bool
-EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
+EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const& model, TreeModel::Path const& path, bool /*selected*/)
 {
        if (selection_countdown) {
                if (--selection_countdown == 0) {
@@ -1502,6 +1546,15 @@ EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path
                        return false;
                }
        }
+
+       TreeModel::iterator iter = model->get_iter (path);
+       if (iter) {
+               boost::shared_ptr<Stripable> stripable = (*iter)[_columns.stripable];
+               if (boost::dynamic_pointer_cast<VCA> (stripable)) {
+                       return false;
+               }
+       }
+
        return true;
 }
 
@@ -1529,13 +1582,15 @@ struct PresentationInfoVCASorter
 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,7 +1604,8 @@ EditorRoutes::initial_display ()
        }
 
        _editor->add_stripables (s);
-       sync_treeview_from_presentation_info ();
+
+       sync_treeview_from_presentation_info (Properties::order);
 }
 
 void
@@ -1569,133 +1625,116 @@ EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& contex
 struct ViewStripable {
        TimeAxisView* tav;
        boost::shared_ptr<Stripable> stripable;
-       uint32_t old_order;
 
-       ViewStripable (TimeAxisView* t, boost::shared_ptr<Stripable> s, uint32_t n)
-               : tav (t), stripable (s), old_order (n) {}
+       ViewStripable (TimeAxisView* t, boost::shared_ptr<Stripable> 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::PresentationOrderSorter());
+
        std::list<ViewStripable> view_stripables;
-       std::vector<int> 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> 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->axis_view_from_stripable (*sli);
+               view_stripables.push_back (ViewStripable (tv, *sli));
        }
 
-       list<ViewStripable>::iterator trailing;
-       list<ViewStripable>::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<ViewStripable>::iterator unselected_neighbour;
+       list<ViewStripable>::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->presentation_info().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()) {
 
-               trailing = view_stripables.end();
-               leading = 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();
 
-               --leading; if (leading == view_stripables.begin()) { return; }
-               --leading;
-               --trailing;
+                                               unselected_neighbour->stripable->set_presentation_order (my_order);
+                                               vsi->stripable->set_presentation_order (unselected_neighbour_order);
 
-               while (1) {
+                                               if (!scroll_to) {
+                                                       scroll_to = vsi->tav;
+                                               }
+                                       }
 
-                       if (_editor->selection->selected (leading->tav)) {
-                               list<ViewStripable>::iterator tmp;
+                               } else {
 
-                               /* need to insert *after* trailing, not *before* it,
-                                  which is what insert (iter, val) normally does.
-                               */
+                                       if (vsi->tav) {
+                                               unselected_neighbour = vsi;
+                                       }
+
+                               }
 
-                               tmp = trailing;
-                               tmp++;
+                               ++vsi;
+                       }
 
-                               view_stripables.insert (tmp, ViewStripable (*leading));
+               } else {
 
-                               /* can't use iter = cont.erase (iter); form here, because
-                                  we need iter to move backwards.
-                               */
+                       unselected_neighbour = view_stripables.end();
+                       vsi = unselected_neighbour;
 
-                               tmp = leading;
-                               --tmp;
+                       do {
 
-                               bool done = false;
+                               --vsi;
 
-                               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;
-                               }
+                               if (vsi->stripable->presentation_info().selected()) {
 
-                               view_stripables.erase (leading);
+                                       if (unselected_neighbour != view_stripables.end()) {
 
-                               if (done) {
-                                       break;
-                               }
+                                               PresentationInfo::order_t unselected_neighbour_order = unselected_neighbour->stripable->presentation_info().order();
+                                               PresentationInfo::order_t my_order = vsi->stripable->presentation_info().order();
 
-                               leading = tmp;
+                                               unselected_neighbour->stripable->set_presentation_order (my_order);
+                                               vsi->stripable->set_presentation_order (unselected_neighbour_order);
+
+                                               if (!scroll_to) {
+                                                       scroll_to = vsi->tav;
+                                               }
+                                       }
+
+                               } else {
+
+                                       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<int>::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
@@ -1894,5 +1933,6 @@ int
 EditorRoutes::plugin_setup (boost::shared_ptr<Route> r, boost::shared_ptr<PluginInsert> pi, ARDOUR::Route::PluginSetupOptions flags)
 {
        PluginSetupDialog psd (r, pi, flags);
-       return psd.run ();
+       int rv = psd.run ();
+       return rv + (psd.fan_out() ? 4 : 0);
 }