canvas::grid starts being able to do its job a little
[ardour.git] / gtk2_ardour / editor_routes.cc
index d0ff0dfbfed3e0ab8d45b1056b5ab0f5a63398eb..2c391907582062aa68a7de9cbf3667ee2d4c9f96 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 ();
 
                        }
@@ -1016,6 +1016,11 @@ EditorRoutes::sync_presentation_info_from_treeview ()
        bool master_is_first = false;
        uint32_t count = 0;
 
+       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++;
@@ -1056,23 +1061,52 @@ 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;
                }
 
+               sorted.push_back (OrderKeys (order, stripable, cmp_max));
+
                ++order;
                ++count;
        }
 
-       if (change) {
-               DEBUG_TRACE (DEBUG::OrderKeys, "... notify PI change from editor GUI\n");
-               _session->notify_presentation_info_change ();
-               _session->set_dirty();
+       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);
+                               }
+                       }
+               }
+       }
+}
+
+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 ()
+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.
@@ -1084,43 +1118,85 @@ 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 ();
 
-       SortByNewDisplayOrder cmp;
+               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));
+               }
 
-       sort (sorted.begin(), sorted.end(), cmp);
-       neworder.assign (sorted.size(), 0);
+               SortByNewDisplayOrder cmp;
 
-       uint32_t n = 0;
+               sort (sorted.begin(), sorted.end(), cmp);
+               neworder.assign (sorted.size(), 0);
 
-       for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
+               uint32_t n = 0;
 
-               neworder[n] = sr->old_display_order;
+               for (OrderingKeys::iterator sr = sorted.begin(); sr != sorted.end(); ++sr, ++n) {
 
-               if (sr->old_display_order != n) {
-                       changed = true;
+                       neworder[n] = sr->old_display_order;
+
+                       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 -> rync PI from TV -> notify -> sync TV from PI -> crash)
+                        */
+                       _display.unset_model();
+                       _model->reorder (neworder);
+                       _display.set_model (_model);
                }
        }
 
-       if (changed) {
-               Unwinder<bool> uw (_ignore_reorder, true);
-               _model->reorder (neworder);
+       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 ();
@@ -1426,6 +1502,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) {
@@ -1434,8 +1514,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))) {
@@ -1457,7 +1535,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) {
@@ -1467,6 +1545,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;
 }
 
@@ -1514,6 +1601,8 @@ EditorRoutes::initial_display ()
        }
 
        _editor->add_stripables (s);
+
+       sync_treeview_from_presentation_info (Properties::order);
 }
 
 void
@@ -1561,6 +1650,8 @@ EditorRoutes::move_selected_tracks (bool up)
        list<ViewStripable>::iterator trailing;
        list<ViewStripable>::iterator leading;
 
+       TimeAxisView* scroll_to = NULL;
+
        if (up) {
 
                trailing = view_stripables.begin();
@@ -1571,6 +1662,9 @@ EditorRoutes::move_selected_tracks (bool up)
                while (leading != view_stripables.end()) {
                        if (_editor->selection->selected (leading->tav)) {
                                view_stripables.insert (trailing, ViewStripable (*leading));
+                               if (!scroll_to) {
+                                       scroll_to = leading->tav;
+                               }
                                leading = view_stripables.erase (leading);
                        } else {
                                ++leading;
@@ -1595,6 +1689,9 @@ EditorRoutes::move_selected_tracks (bool up)
                while (1) {
 
                        if (_editor->selection->selected (leading->tav)) {
+                               if (!scroll_to) {
+                                       scroll_to = leading->tav;
+                               }
                                list<ViewStripable>::iterator tmp;
 
                                /* need to insert *after* trailing, not *before* it,
@@ -1641,7 +1738,12 @@ EditorRoutes::move_selected_tracks (bool up)
                };
        }
 
-       for (leading = view_stripables.begin(); leading != view_stripables.end(); ++leading) {
+       bool changed = false;
+       unsigned int i = 0;
+       for (leading = view_stripables.begin(); leading != view_stripables.end(); ++leading, ++i) {
+               if (leading->old_order != i) {
+                       changed = true;
+               }
                neworder.push_back (leading->old_order);
 #ifndef NDEBUG
                if (leading->old_order != neworder.size() - 1) {
@@ -1650,6 +1752,10 @@ EditorRoutes::move_selected_tracks (bool up)
 #endif
        }
 
+       if (!changed) {
+               return;
+       }
+
 #ifndef NDEBUG
        DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
        for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
@@ -1658,8 +1764,12 @@ EditorRoutes::move_selected_tracks (bool up)
        DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
 #endif
 
-
        _model->reorder (neworder);
+
+       if (scroll_to) {
+               _editor->ensure_time_axis_view_is_visible (*scroll_to, false);
+       }
+
 }
 
 void
@@ -1858,5 +1968,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);
 }