unused code, showing basics of how to define a custom gtkmm-2.4 treemodel for stripables.
[ardour.git] / gtk2_ardour / editor_routes.cc
index 878d8d336f81c8972a7bf60c920497997f8c64e2..2c391907582062aa68a7de9cbf3667ee2d4c9f96 100644 (file)
@@ -57,7 +57,7 @@
 #include "vca_time_axis.h"
 #include "utils.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -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
@@ -581,7 +582,6 @@ EditorRoutes::redisplay_real ()
 
        for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
                TimeAxisView *tv = (*i)[_columns.tv];
-               boost::shared_ptr<Stripable> route = (*i)[_columns.stripable];
 
                if (tv == 0) {
                        // just a "title" row
@@ -792,10 +792,16 @@ EditorRoutes::time_axis_views_added (list<TimeAxisView*> tavs)
 
                boost::weak_ptr<Stripable> ws (stripable);
 
-               if (rtav) {
-                       rtav->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
-               }
+               /* for now, we need both of these. PropertyChanged covers on
+                * pre-defined, "global" things of interest to a
+                * UI. gui_changed covers arbitrary, un-enumerated, un-typed
+                * changes that may only be of interest to a particular
+                * UI (e.g. track-height is not of any relevant to OSC)
+                */
+
+               stripable->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
                stripable->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, ws), gui_context());
+               stripable->presentation_info().PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, ws), gui_context());
 
                if (boost::dynamic_pointer_cast<Track> (stripable)) {
                        boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (stripable);
@@ -839,9 +845,12 @@ EditorRoutes::time_axis_views_added (list<TimeAxisView*> tavs)
        _display.set_model (_model);
 
        /* now update route order keys from the treeview/track display order */
+
        if (!from_scratch) {
                sync_presentation_info_from_treeview ();
        }
+
+       redisplay ();
 }
 
 void
@@ -887,11 +896,13 @@ EditorRoutes::route_removed (TimeAxisView *tv)
 void
 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Stripable> s)
 {
-       if (!what_changed.contains (ARDOUR::Properties::name)) {
+       if (!what_changed.contains (ARDOUR::Properties::hidden) && !what_changed.contains (ARDOUR::Properties::name)) {
                return;
        }
 
-       ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
+       if (_adding_routes) {
+               return;
+       }
 
        boost::shared_ptr<Stripable> stripable = s.lock ();
 
@@ -903,9 +914,22 @@ EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost:
        TreeModel::Children::iterator i;
 
        for (i = rows.begin(); i != rows.end(); ++i) {
+
                boost::shared_ptr<Stripable> ss = (*i)[_columns.stripable];
+
                if (ss == stripable) {
-                       (*i)[_columns.text] = stripable->name();
+
+                       if (what_changed.contains (ARDOUR::Properties::name)) {
+                               (*i)[_columns.text] = stripable->name();
+                               break;
+                       }
+
+                       if (what_changed.contains (ARDOUR::Properties::hidden)) {
+                               (*i)[_columns.visible] = !stripable->presentation_info().hidden();
+                               redisplay ();
+
+                       }
+
                        break;
                }
        }
@@ -971,15 +995,6 @@ EditorRoutes::show_track_in_display (TimeAxisView& tv)
        }
 }
 
-void
-EditorRoutes::reset_remote_control_ids ()
-{
-       if (Config->get_remote_model() == UserOrdered || !_session || _session->deletion_in_progress()) {
-               return;
-       }
-
-       sync_presentation_info_from_treeview ();
-}
 void
 EditorRoutes::sync_presentation_info_from_treeview ()
 {
@@ -998,6 +1013,19 @@ EditorRoutes::sync_presentation_info_from_treeview ()
        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 ();
+
+       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++;
+               master_is_first = true;
+       }
 
        for (ri = rows.begin(); ri != rows.end(); ++ri) {
 
@@ -1014,22 +1042,71 @@ EditorRoutes::sync_presentation_info_from_treeview ()
 
                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_explicit (order);
+                       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);
+                               }
+                       }
+               }
        }
+}
+
+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 ();
+       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.
@@ -1041,42 +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;
+               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->presentation_info().order()));
-       }
+               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 ();
@@ -1382,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) {
@@ -1390,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))) {
@@ -1413,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) {
@@ -1423,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;
 }
 
@@ -1464,15 +1595,14 @@ EditorRoutes::initial_display ()
                s.push_back (*ri);
        }
 
-       _editor->add_routes (r);
-
        VCAList v (_session->vca_manager().vcas());
        for (VCAList::iterator vi = v.begin(); vi != v.end(); ++vi) {
                s.push_back (*vi);
        }
-       s.sort (PresentationInfoEditorSorter ());
 
-       _editor->add_vcas (v);
+       _editor->add_stripables (s);
+
+       sync_treeview_from_presentation_info (Properties::order);
 }
 
 void
@@ -1520,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();
@@ -1530,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;
@@ -1554,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,
@@ -1600,15 +1738,24 @@ 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) {
                        DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("move %1 to %2\n", leading->old_order, neworder.size() - 1));
                }
 #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) {
@@ -1617,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
@@ -1790,7 +1941,7 @@ EditorRoutes::show_tracks_with_regions_at_playhead ()
 
        set<TimeAxisView*> show;
        for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
-               TimeAxisView* tav = _editor->axis_view_from_route (*i);
+               TimeAxisView* tav = _editor->axis_view_from_stripable (*i);
                if (tav) {
                        show.insert (tav);
                }
@@ -1817,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);
 }