merge with master, including manual merge conflict resolution
[ardour.git] / gtk2_ardour / editor_routes.cc
index 8ea152ba54b66e5e6449d501791b99293410da39..bf629ea27dd54a533194abc7f257ec388757bc53 100644 (file)
 #include <cmath>
 #include <cassert>
 
+#include "pbd/unknown_type.h"
+#include "pbd/unwind.h"
+
+#include "ardour/debug.h"
+#include "ardour/route.h"
+#include "ardour/midi_track.h"
 #include "ardour/session.h"
 
+#include "gtkmm2ext/cell_renderer_pixbuf_multi.h"
+#include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
+#include "gtkmm2ext/treeutils.h"
+
 #include "editor.h"
 #include "keyboard.h"
 #include "ardour_ui.h"
 #include "gui_thread.h"
 #include "actions.h"
 #include "utils.h"
+#include "route_sorter.h"
 #include "editor_group_tabs.h"
 #include "editor_routes.h"
 
-#include "pbd/unknown_type.h"
-
-#include "ardour/route.h"
-#include "ardour/midi_track.h"
-
-#include "gtkmm2ext/cell_renderer_pixbuf_multi.h"
-#include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
-#include "gtkmm2ext/treeutils.h"
-
 #include "i18n.h"
 
 using namespace std;
@@ -57,17 +59,24 @@ using namespace Gtkmm2ext;
 using namespace Glib;
 using Gtkmm2ext::Keyboard;
 
+struct ColumnInfo {
+    int         index;
+    const char* label;
+    const char* tooltip;
+};
+
 EditorRoutes::EditorRoutes (Editor* e)
        : EditorComponent (e)
         , _ignore_reorder (false)
         , _no_redisplay (false)
-        , _redisplay_does_not_sync_order_keys (false)
-        , _redisplay_does_not_reset_order_keys (false)
-        ,_menu (0)
+        , _adding_routes (false)
+        , _menu (0)
         , old_focus (0)
         , selection_countdown (0)
         , name_editable (0)
 {
+       static const int column_width = 22;
+
        _scroller.add (_display);
        _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
 
@@ -77,27 +86,45 @@ EditorRoutes::EditorRoutes (Editor* e)
        // Record enable toggle
        CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
 
-       rec_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
-       rec_col_renderer->set_pixbuf (1, ::get_icon("rec-in-progress"));
-       rec_col_renderer->set_pixbuf (2, ::get_icon("rec-enabled"));
-       rec_col_renderer->set_pixbuf (3, ::get_icon("step-editing"));
+       rec_col_renderer->set_pixbuf (0, ::get_icon("record-normal-disabled"));
+       rec_col_renderer->set_pixbuf (1, ::get_icon("record-normal-in-progress"));
+       rec_col_renderer->set_pixbuf (2, ::get_icon("record-normal-enabled"));
+       rec_col_renderer->set_pixbuf (3, ::get_icon("record-step"));
        rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
 
        TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
 
        rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
        rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
+
        rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
        rec_state_column->set_alignment(ALIGN_CENTER);
        rec_state_column->set_expand(false);
-       rec_state_column->set_fixed_width(15);
+       rec_state_column->set_fixed_width(column_width);
+
+       // MIDI Input Active
+
+       CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti());
+       input_active_col_renderer->set_pixbuf (0, ::get_icon("midi-input-inactive"));
+       input_active_col_renderer->set_pixbuf (1, ::get_icon("midi-input-active"));
+       input_active_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed));
+
+       TreeViewColumn* input_active_column = manage (new TreeViewColumn ("I", *input_active_col_renderer));
+
+       input_active_column->add_attribute(input_active_col_renderer->property_state(), _columns.is_input_active);
+       input_active_column->add_attribute (input_active_col_renderer->property_visible(), _columns.is_midi);
+
+       input_active_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
+       input_active_column->set_alignment(ALIGN_CENTER);
+       input_active_column->set_expand(false);
+       input_active_column->set_fixed_width(column_width);
 
        // Mute enable toggle
        CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
 
-       mute_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
-       mute_col_renderer->set_pixbuf (1, ::get_icon("muted-by-others"));
-       mute_col_renderer->set_pixbuf (2, ::get_icon("mute-enabled"));
+       mute_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("mute-disabled"));
+       mute_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("muted-by-others"));
+       mute_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("mute-enabled"));
        mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
 
        TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
@@ -111,74 +138,102 @@ EditorRoutes::EditorRoutes (Editor* e)
        // Solo enable toggle
        CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
 
-       solo_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
-       solo_col_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
-       solo_col_renderer->set_pixbuf (3, ::get_icon("soloed-by-others"));
+       solo_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("solo-disabled"));
+       solo_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("solo-enabled"));
+       solo_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("soloed-by-others"));
        solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
 
        TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
 
        solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
+       solo_state_column->add_attribute(solo_col_renderer->property_visible(), _columns.solo_visible);
        solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
        solo_state_column->set_alignment(ALIGN_CENTER);
        solo_state_column->set_expand(false);
-       solo_state_column->set_fixed_width(15);
+       solo_state_column->set_fixed_width(column_width);
 
        // Solo isolate toggle
        CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
 
-       solo_iso_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
-       solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolated"));
+       solo_iso_renderer->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
+       solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
        solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
 
        TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
 
        solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
+       solo_isolate_state_column->add_attribute(solo_iso_renderer->property_visible(), _columns.solo_visible);
        solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
        solo_isolate_state_column->set_alignment(ALIGN_CENTER);
        solo_isolate_state_column->set_expand(false);
-       solo_isolate_state_column->set_fixed_width(22);
+       solo_isolate_state_column->set_fixed_width(column_width);
 
        // Solo safe toggle
        CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
 
-       solo_safe_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
-       solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
+       solo_safe_renderer->set_pixbuf (0, ::get_icon("solo-safe-disabled"));
+       solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-safe-enabled"));
        solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
 
        TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
        solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
+       solo_safe_state_column->add_attribute(solo_safe_renderer->property_visible(), _columns.solo_visible);
        solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
        solo_safe_state_column->set_alignment(ALIGN_CENTER);
        solo_safe_state_column->set_expand(false);
-       solo_safe_state_column->set_fixed_width(22);
+       solo_safe_state_column->set_fixed_width(column_width);
+
+        _name_column = _display.append_column ("", _columns.text) - 1;
+       _visible_column = _display.append_column ("", _columns.visible) - 1;
+       _active_column = _display.append_column ("", _columns.active) - 1;
 
+       _display.append_column (*input_active_column);
        _display.append_column (*rec_state_column);
        _display.append_column (*mute_state_column);
        _display.append_column (*solo_state_column);
        _display.append_column (*solo_isolate_state_column);
        _display.append_column (*solo_safe_state_column);
-       
-        int colnum = _display.append_column (_("Name"), _columns.text);
-       TreeViewColumn* c = _display.get_column (colnum-1);
-        c->set_data ("i_am_the_tab_column", (void*) 0xfeedface);
-       _display.append_column (_("V"), _columns.visible);
-       
+
+
+       TreeViewColumn* col;
+       Gtk::Label* l;
+
+       ColumnInfo ci[] = {
+               { 0, _("Name"), _("Track/Bus Name") },
+               { 1, _("V"), _("Track/Bus visible ?") },
+               { 2, _("A"), _("Track/Bus active ?") },
+               { 3, _("I"), _("MIDI input enabled") },
+               { 4, _("R"), _("Record enabled") },
+               { 5, _("M"), _("Muted") },
+               { 6, _("S"), _("Soloed") },
+               { 7, _("SI"), _("Solo Isolated") },
+               { 8, _("SS"), _("Solo Safe (Locked)") },
+               { -1, 0, 0 }
+       };
+
+       for (int i = 0; ci[i].index >= 0; ++i) {
+               col = _display.get_column (ci[i].index);
+               l = manage (new Label (ci[i].label));
+               ARDOUR_UI::instance()->set_tip (*l, ci[i].tooltip);
+               col->set_widget (*l);
+               l->show ();
+       }
+
        _display.set_headers_visible (true);
-       _display.set_name ("TrackListDisplay");
        _display.get_selection()->set_mode (SELECTION_SINGLE);
        _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
        _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.route.index(), "routes");
 
-       CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (5));
+       CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
 
        assert (name_cell);
         name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
 
-       TreeViewColumn* name_column = _display.get_column (5);
+       TreeViewColumn* name_column = _display.get_column (_name_column);
 
        assert (name_column);
 
@@ -191,21 +246,33 @@ EditorRoutes::EditorRoutes (Editor* e)
        name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
 
        // Set the visible column cell renderer to radio toggle
-       CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (6));
+       CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
 
        visible_cell->property_activatable() = true;
        visible_cell->property_radio() = false;
        visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
-       
-       TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (6));
+
+       TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
        visible_col->set_expand(false);
        visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
        visible_col->set_fixed_width(30);
        visible_col->set_alignment(ALIGN_CENTER);
+
+       CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
+
+       active_cell->property_activatable() = true;
+       active_cell->property_radio() = false;
+       active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
+
+       TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
+       active_col->set_expand (false);
+       active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
+       active_col->set_fixed_width (30);
+       active_col->set_alignment (ALIGN_CENTER);
        
        _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
        _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
-       
+
        _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
        _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
 
@@ -217,7 +284,7 @@ EditorRoutes::EditorRoutes (Editor* e)
 
         _display.set_enable_search (false);
 
-       Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
+       Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::sync_treeview_from_order_keys, this, _1), gui_context());
 }
 
 bool
@@ -254,8 +321,8 @@ EditorRoutes::enter_notify (GdkEventCrossing*)
        if (name_editable) {
                return true;
        }
-       
-        /* arm counter so that ::selection_filter() will deny selecting anything for the 
+
+        /* arm counter so that ::selection_filter() will deny selecting anything for the
            next two attempts to change selection status.
         */
         selection_countdown = 2;
@@ -291,6 +358,24 @@ EditorRoutes::set_session (Session* s)
        }
 }
 
+void
+EditorRoutes::on_input_active_changed (std::string const & path_string)
+{
+       // Get the model row that has been toggled.
+       Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
+
+       TimeAxisView* tv = row[_columns.tv];
+       RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+
+       if (rtv) {
+               boost::shared_ptr<MidiTrack> mt;
+               mt = rtv->midi_track();
+               if (mt) {
+                       mt->set_input_active (!mt->input_active());
+               }
+       }
+}
+
 void
 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
 {
@@ -315,7 +400,7 @@ EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
 
        TimeAxisView *tv = row[_columns.tv];
        RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
-        
+
        if (rtv != 0) {
                boost::shared_ptr<RouteList> rl (new RouteList);
                rl->push_back (rtv->route());
@@ -336,7 +421,7 @@ EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
                boost::shared_ptr<RouteList> rl (new RouteList);
                rl->push_back (rtv->route());
                if (Config->get_solo_control_is_listen_control()) {
-                       _session->set_listen (rl, !rtv->route()->listening(), Session::rt_cleanup);
+                       _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
                } else {
                        _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
                }
@@ -406,13 +491,17 @@ EditorRoutes::show_menu ()
 void
 EditorRoutes::redisplay ()
 {
-       if (_no_redisplay || !_session) {
+       if (_no_redisplay || !_session || _session->deletion_in_progress()) {
                return;
        }
 
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
        uint32_t position;
+
+       /* n will be the count of tracks plus children (updated by TimeAxisView::show_at),
+          so we will use that to know where to put things.
+       */
        int n;
 
        for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
@@ -424,29 +513,19 @@ EditorRoutes::redisplay ()
                        continue;
                }
 
-               if (!_redisplay_does_not_reset_order_keys) {
-                       /* this reorder is caused by user action, so reassign sort order keys
-                          to tracks.
-                       */
-                       route->set_order_key (N_ ("editor"), n);
-               }
-
-               bool visible = (*i)[_columns.visible];
+               bool visible = tv->marked_for_display ();
 
                /* show or hide the TimeAxisView */
                if (visible) {
-                       tv->set_marked_for_display (true);
                        position += tv->show_at (position, n, &_editor->edit_controls_vbox);
                        tv->clip_to_viewport ();
                } else {
-                       tv->set_marked_for_display (false);
                        tv->hide ();
                }
 
                n++;
        }
 
-
        /* whenever we go idle, update the track view list to reflect the new order.
           we can't do this here, because we could mess up something that is traversing
           the track order and has caused a redisplay of the list.
@@ -455,34 +534,33 @@ EditorRoutes::redisplay ()
 
         _editor->reset_controls_layout_height (position);
         _editor->reset_controls_layout_width ();
-       _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
-       _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
+       _editor->_full_canvas_height = position;
+       _editor->vertical_adjustment.set_upper (_editor->_full_canvas_height);
 
-       if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
+       if ((_editor->vertical_adjustment.get_value() + _editor->_visible_canvas_height) > _editor->vertical_adjustment.get_upper()) {
                /*
                   We're increasing the size of the canvas while the bottom is visible.
                   We scroll down to keep in step with the controls layout.
                */
-               _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
-       }
-
-       if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
-               _session->sync_order_keys (N_ ("editor"));
+               _editor->vertical_adjustment.set_value (_editor->_full_canvas_height - _editor->_visible_canvas_height);
        }
 }
 
 void
 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
 {
-       if (!_session || _session->deletion_in_progress()) {
-               return;
-       }
-               
-        /* this could require an order reset & sync */
-       _session->set_remote_control_ids();
-       _ignore_reorder = true;
-       redisplay ();
-       _ignore_reorder = false;
+       /* this happens as the second step of a DnD within the treeview as well
+          as when a row/route is actually deleted.
+       */
+       DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview row deleted\n");
+       sync_order_keys_from_treeview ();
+}
+
+void
+EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
+{
+       DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview reordered\n");
+       sync_order_keys_from_treeview ();
 }
 
 void
@@ -498,52 +576,69 @@ EditorRoutes::visible_changed (std::string const & path)
                TimeAxisView* tv = (*iter)[_columns.tv];
                if (tv) {
                        bool visible = (*iter)[_columns.visible];
-                       (*iter)[_columns.visible] = !visible;
+
+                       if (tv->set_marked_for_display (!visible)) {
+                               update_visibility ();
+                       }
                }
        }
+}
 
-       _redisplay_does_not_reset_order_keys = true;
-       _session->set_remote_control_ids();
-       redisplay ();
-       _redisplay_does_not_reset_order_keys = false;
+void
+EditorRoutes::active_changed (std::string const & path)
+{
+       if (_session && _session->deletion_in_progress ()) {
+               return;
+       }
+
+       Gtk::TreeModel::Row row = *_model->get_iter (path);
+       boost::shared_ptr<Route> route = row[_columns.route];
+       bool const active = row[_columns.active];
+       route->set_active (!active, this);
 }
 
 void
 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
 {
        TreeModel::Row row;
+       PBD::Unwinder<bool> at (_adding_routes, true);
 
-       _redisplay_does_not_sync_order_keys = true;
        suspend_redisplay ();
 
+       _display.set_model (Glib::RefPtr<ListStore>());
+
        for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
 
+               boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
+
                row = *(_model->append ());
 
                row[_columns.text] = (*x)->route()->name();
                row[_columns.visible] = (*x)->marked_for_display();
+               row[_columns.active] = (*x)->route()->active ();
                row[_columns.tv] = *x;
                row[_columns.route] = (*x)->route ();
                row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
-               row[_columns.mute_state] = (*x)->route()->muted();
-               row[_columns.solo_state] = RouteUI::solo_visual_state ((*x)->route());
-               row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
-               row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
-               row[_columns.name_editable] = true;
 
-               _ignore_reorder = true;
-
-               /* added a new fresh one at the end */
-               if ((*x)->route()->order_key (N_ ("editor")) == -1) {
-                       (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
+               if (midi_trk) {
+                       row[_columns.is_input_active] = midi_trk->input_active ();
+                       row[_columns.is_midi] = true;
+               } else {
+                       row[_columns.is_input_active] = false;
+                       row[_columns.is_midi] = false;
                }
 
-               _ignore_reorder = false;
+               row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
+               row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
+               row[_columns.solo_visible] = !(*x)->route()->is_master ();
+               row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
+               row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
+               row[_columns.name_editable] = true;
 
                boost::weak_ptr<Route> wr ((*x)->route());
 
-               (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
-               (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
+               (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
+               (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
 
                if ((*x)->is_track()) {
                        boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
@@ -553,13 +648,16 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
                if ((*x)->is_midi_track()) {
                        boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
                        t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
+                       t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
                }
 
                (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
-               (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
-               (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
+               (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
+               (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
                (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
                (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
+               (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
+
        }
 
        update_rec_display ();
@@ -567,14 +665,23 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
        update_solo_display (true);
        update_solo_isolate_display ();
        update_solo_safe_display ();
+       update_input_active_display ();
+       update_active_display ();
+
        resume_redisplay ();
-       _redisplay_does_not_sync_order_keys = false;
+       _display.set_model (_model);
+
+       /* now update route order keys from the treeview/track display order */
+
+       sync_order_keys_from_treeview ();
 }
 
 void
 EditorRoutes::handle_gui_changes (string const & what, void*)
 {
-       ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
+       if (_adding_routes) {
+               return;
+       }
 
        if (what == "track_height") {
                /* Optional :make tracks change height while it happens, instead
@@ -597,12 +704,6 @@ EditorRoutes::route_removed (TimeAxisView *tv)
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator ri;
 
-       /* the core model has changed, there is no need to sync
-          view orders.
-       */
-
-       _redisplay_does_not_sync_order_keys = true;
-
        for (ri = rows.begin(); ri != rows.end(); ++ri) {
                if ((*ri)[_columns.tv] == tv) {
                        _model->erase (ri);
@@ -610,7 +711,9 @@ EditorRoutes::route_removed (TimeAxisView *tv)
                }
        }
 
-       _redisplay_does_not_sync_order_keys = false;
+       /* the deleted signal for the treeview/model will take 
+          care of any updates.
+       */
 }
 
 void
@@ -640,6 +743,18 @@ EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost:
        }
 }
 
+void
+EditorRoutes::update_active_display ()
+{
+       TreeModel::Children rows = _model->children();
+       TreeModel::Children::iterator i;
+
+       for (i = rows.begin(); i != rows.end(); ++i) {
+               boost::shared_ptr<Route> route = (*i)[_columns.route];
+               (*i)[_columns.active] = route->active ();
+       }
+}
+
 void
 EditorRoutes::update_visibility ()
 {
@@ -651,9 +766,13 @@ EditorRoutes::update_visibility ()
        for (i = rows.begin(); i != rows.end(); ++i) {
                TimeAxisView *tv = (*i)[_columns.tv];
                (*i)[_columns.visible] = tv->marked_for_display ();
-               cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl;
        }
 
+       /* force route order keys catch up with visibility changes
+        */
+
+       sync_order_keys_from_treeview ();
+
        resume_redisplay ();
 }
 
@@ -665,12 +784,12 @@ EditorRoutes::hide_track_in_display (TimeAxisView& tv)
 
        for (i = rows.begin(); i != rows.end(); ++i) {
                if ((*i)[_columns.tv] == &tv) {
+                       tv.set_marked_for_display (false);
                        (*i)[_columns.visible] = false;
+                       redisplay ();
                        break;
                }
        }
-
-       redisplay ();
 }
 
 void
@@ -679,63 +798,212 @@ EditorRoutes::show_track_in_display (TimeAxisView& tv)
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
+
        for (i = rows.begin(); i != rows.end(); ++i) {
                if ((*i)[_columns.tv] == &tv) {
+                       tv.set_marked_for_display (true);
                        (*i)[_columns.visible] = true;
+                       redisplay ();
                        break;
                }
        }
-
-       redisplay ();
 }
 
 void
-EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
+EditorRoutes::reset_remote_control_ids ()
 {
-       redisplay ();
+       if (Config->get_remote_model() != EditorOrdered || !_session || _session->deletion_in_progress()) {
+               return;
+       }
+
+       TreeModel::Children rows = _model->children();
+       
+       if (rows.empty()) {
+               return;
+       }
+
+       
+       DEBUG_TRACE (DEBUG::OrderKeys, "editor reset remote control ids\n");
+
+       TreeModel::Children::iterator ri;
+       bool rid_change = false;
+       uint32_t rid = 1;
+       uint32_t invisible_key = UINT32_MAX;
+
+       for (ri = rows.begin(); ri != rows.end(); ++ri) {
+
+               boost::shared_ptr<Route> route = (*ri)[_columns.route];
+               bool visible = (*ri)[_columns.visible];
+
+
+               if (!route->is_master() && !route->is_monitor()) {
+
+                       uint32_t new_rid = (visible ? rid : invisible_key--);
+
+                       if (new_rid != route->remote_control_id()) {
+                               route->set_remote_control_id_from_order_key (EditorSort, new_rid);      
+                               rid_change = true;
+                       }
+                       
+                       if (visible) {
+                               rid++;
+                       }
+
+               }
+       }
+
+       if (rid_change) {
+               /* tell the world that we changed the remote control IDs */
+               _session->notify_remote_id_change ();
+       }
 }
 
-/** If src != "editor", take editor order keys from each route and use them to rearrange the
- *  route list so that the visual arrangement of routes matches the order keys from the routes.
- */
+
 void
-EditorRoutes::sync_order_keys (string const & src)
+EditorRoutes::sync_order_keys_from_treeview ()
 {
-       vector<int> neworder;
+       if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
+               return;
+       }
+
        TreeModel::Children rows = _model->children();
+       
+       if (rows.empty()) {
+               return;
+       }
+
+       
+       DEBUG_TRACE (DEBUG::OrderKeys, "editor sync order keys from treeview\n");
+
        TreeModel::Children::iterator ri;
+       bool changed = false;
+       bool rid_change = false;
+       uint32_t order = 0;
+       uint32_t rid = 1;
+       uint32_t invisible_key = UINT32_MAX;
+
+       for (ri = rows.begin(); ri != rows.end(); ++ri) {
+
+               boost::shared_ptr<Route> route = (*ri)[_columns.route];
+               bool visible = (*ri)[_columns.visible];
+
+               uint32_t old_key = route->order_key (EditorSort);
+
+               if (order != old_key) {
+                       route->set_order_key (EditorSort, order);
+
+                       changed = true;
+               }
+
+               if ((Config->get_remote_model() == EditorOrdered) && !route->is_master() && !route->is_monitor()) {
+
+                       uint32_t new_rid = (visible ? rid : invisible_key--);
+
+                       if (new_rid != route->remote_control_id()) {
+                               route->set_remote_control_id_from_order_key (EditorSort, new_rid);      
+                               rid_change = true;
+                       }
+                       
+                       if (visible) {
+                               rid++;
+                       }
+
+               }
+
+               ++order;
+       }
+       
+       if (changed) {
+               /* tell the world that we changed the editor sort keys */
+               _session->sync_order_keys (EditorSort);
+       }
+
+       if (rid_change) {
+               /* tell the world that we changed the remote control IDs */
+               _session->notify_remote_id_change ();
+       }
+}
+
+void
+EditorRoutes::sync_treeview_from_order_keys (RouteSortOrderKey src)
+{
+       /* Some route order key(s) for `src' has been changed, make sure that 
+          we update out tree/list model and GUI to reflect the change.
+       */
 
-       if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
+       if (!_session || _session->deletion_in_progress()) {
                return;
        }
 
-       for (ri = rows.begin(); ri != rows.end(); ++ri) {
-               neworder.push_back (0);
+       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("editor sync model from order keys, src = %1\n", enum_2_string (src)));
+
+       if (src == MixerSort) {
+
+               if (!Config->get_sync_all_route_ordering()) {
+                       /* mixer sort keys changed - we don't care */
+                       return;
+               }
+
+               DEBUG_TRACE (DEBUG::OrderKeys, "reset editor order key to match mixer\n");
+
+               /* mixer sort keys were changed, update the editor sort
+                * keys since "sync mixer+editor order" is enabled.
+                */
+
+               boost::shared_ptr<RouteList> r = _session->get_routes ();
+               
+               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                       (*i)->sync_order_keys (src);
+               }
        }
 
+       /* we could get here after either a change in the Mixer or Editor sort
+        * order, but either way, the mixer order keys reflect the intended
+        * order for the GUI, so reorder the treeview model to match it.
+        */
+
+       vector<int> neworder;
+       TreeModel::Children rows = _model->children();
+       uint32_t old_order = 0;
        bool changed = false;
-       int order;
 
-       for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
+       if (rows.empty()) {
+               return;
+       }
+
+       OrderKeySortedRoutes sorted_routes;
+
+       for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
                boost::shared_ptr<Route> route = (*ri)[_columns.route];
+               sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key (EditorSort)));
+       }
 
-               int old_key = order;
-               int new_key = route->order_key (N_ ("editor"));
+       SortByNewDisplayOrder cmp;
 
-               neworder[new_key] = old_key;
+       sort (sorted_routes.begin(), sorted_routes.end(), cmp);
+       neworder.assign (sorted_routes.size(), 0);
 
-               if (new_key != old_key) {
+       uint32_t n = 0;
+       
+       for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
+
+               neworder[n] = sr->old_display_order;
+
+               if (sr->old_display_order != n) {
                        changed = true;
                }
+
+               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("EDITOR change order for %1 from %2 to %3\n",
+                                                              sr->route->name(), sr->old_display_order, n));
        }
 
        if (changed) {
-               _redisplay_does_not_reset_order_keys = true;
+               Unwinder<bool> uw (_ignore_reorder, true);
                _model->reorder (neworder);
-               _redisplay_does_not_reset_order_keys = false;
        }
-}
 
+       redisplay ();
+}
 
 void
 EditorRoutes::hide_all_tracks (bool /*with_select*/)
@@ -783,9 +1051,15 @@ EditorRoutes::set_all_tracks_visibility (bool yn)
                        continue;
                }
 
+               tv->set_marked_for_display (yn);
                (*i)[_columns.visible] = yn;
        }
 
+       /* force route order keys catch up with visibility changes
+        */
+
+       sync_order_keys_from_treeview ();
+
        resume_redisplay ();
 }
 
@@ -798,13 +1072,13 @@ EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
        suspend_redisplay ();
 
        for (i = rows.begin(); i != rows.end(); ++i) {
-         
+
                TreeModel::Row row = (*i);
                TimeAxisView* tv = row[_columns.tv];
-               
+
                AudioTimeAxisView* atv;
                MidiTimeAxisView* mtv;
-               
+
                if (tv == 0) {
                        continue;
                }
@@ -843,6 +1117,11 @@ EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
                }
        }
 
+       /* force route order keys catch up with visibility changes
+        */
+
+       sync_order_keys_from_treeview ();
+
        resume_redisplay ();
 }
 
@@ -907,9 +1186,9 @@ EditorRoutes::key_press (GdkEventKey* ev)
                 if (name_editable) {
                         name_editable->editing_done ();
                         name_editable = 0;
-                } 
+                }
 
-                col = _display.get_column (5); // select&focus on name column
+                col = _display.get_column (_name_column); // select&focus on name column
 
                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
                         treeview_select_previous (_display, _model, col);
@@ -928,10 +1207,12 @@ EditorRoutes::key_press (GdkEventKey* ev)
                 break;
 
         case 's':
-               if (Config->get_solo_control_is_listen_control()) {
-                       _session->set_listen (rl, !rl->front()->listening(), Session::rt_cleanup);
-               } else {
-                       _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
+                if (get_relevant_routes (rl)) {
+                       if (Config->get_solo_control_is_listen_control()) {
+                               _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
+                       } else {
+                               _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
+                       }
                }
                 return true;
                 break;
@@ -999,35 +1280,41 @@ EditorRoutes::button_press (GdkEventButton* ev)
                show_menu ();
                return true;
        }
-       
+
+       TreeModel::Path path;
+       TreeViewColumn *tvc;
+       int cell_x;
+       int cell_y;
+
+       if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
+               /* cancel selection */
+               _display.get_selection()->unselect_all ();
+               /* end any editing by grabbing focus */
+               _display.grab_focus ();
+               return true;
+       }
+
        //Scroll editor canvas to selected track
        if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
-               
-               TreeModel::Path path;
-               TreeViewColumn *tvc;
-               int cell_x;
-               int cell_y;
-               
-               _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
 
                // Get the model row.
                Gtk::TreeModel::Row row = *_model->get_iter (path);
-               
+
                TimeAxisView *tv = row[_columns.tv];
-               
+
                int y_pos = tv->y_position();
-               
+
                //Clamp the y pos so that we do not extend beyond the canvas full height.
-               if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
-                   y_pos = _editor->full_canvas_height - _editor->_canvas_height;
+               if (_editor->_full_canvas_height - y_pos < _editor->_visible_canvas_height){
+                   y_pos = _editor->_full_canvas_height - _editor->_visible_canvas_height;
                }
-               
+
                //Only scroll to if the track is visible
                if(y_pos != -1){
                    _editor->reset_y_origin (y_pos);
                }
        }
-       
+
        return false;
 }
 
@@ -1047,8 +1334,14 @@ EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path
 
 struct EditorOrderRouteSorter {
     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
-           /* use of ">" forces the correct sort order */
-           return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
+           if (a->is_master()) {
+                   /* master before everything else */
+                   return true;
+           } else if (b->is_master()) {
+                   /* everything else before master */
+                   return false;
+           }
+           return a->order_key (EditorSort) < b->order_key (EditorSort);
     }
 };
 
@@ -1064,49 +1357,32 @@ EditorRoutes::initial_display ()
        }
 
        boost::shared_ptr<RouteList> routes = _session->get_routes();
-       RouteList r (*routes);
-       EditorOrderRouteSorter sorter;
-
-       r.sort (sorter);
-       _editor->handle_new_route (r);
-
-       /* don't show master bus in a new session */
 
        if (ARDOUR_UI::instance()->session_is_new ()) {
 
-               TreeModel::Children rows = _model->children();
-               TreeModel::Children::iterator i;
-
-               _no_redisplay = true;
+               /* new session: stamp all routes with the right editor order
+                * key
+                */
 
-               for (i = rows.begin(); i != rows.end(); ++i) {
-
-                       TimeAxisView *tv =  (*i)[_columns.tv];
-                       RouteTimeAxisView *rtv;
+               _editor->add_routes (*(routes.get()));
+               
+       } else {
 
-                       if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
-                               if (rtv->route()->is_master()) {
-                                       _display.get_selection()->unselect (i);
-                               }
-                       }
-               }
+               /* existing session: sort a copy of the route list by
+                * editor-order and add its contents to the display.
+                */
 
-               _no_redisplay = false;
-               redisplay ();
+               RouteList r (*routes);
+               EditorOrderRouteSorter sorter;
+               
+               r.sort (sorter);
+               _editor->add_routes (r);
+               
        }
 
        resume_redisplay ();
 }
 
-void
-EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
-{
-       _redisplay_does_not_sync_order_keys = true;
-       _session->set_remote_control_ids();
-       redisplay ();
-       _redisplay_does_not_sync_order_keys = false;
-}
-
 void
 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
                                             int x, int y,
@@ -1225,12 +1501,45 @@ EditorRoutes::move_selected_tracks (bool up)
        }
 
        for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
-               neworder.push_back (leading->second->order_key (N_ ("editor")));
+               uint32_t order = leading->second->order_key (EditorSort);
+               neworder.push_back (order);
+       }
+
+#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));
        }
+       DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
+
+       for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
+               if (*i >= (int) neworder.size()) {
+                       cerr << "Trying to move something to " << *i << " of " << neworder.size() << endl;
+               }
+               assert (*i < (int) neworder.size ());
+       }
+#endif 
 
        _model->reorder (neworder);
+}
 
-       _session->sync_order_keys (N_ ("editor"));
+void
+EditorRoutes::update_input_active_display ()
+{
+       TreeModel::Children rows = _model->children();
+       TreeModel::Children::iterator i;
+
+       for (i = rows.begin(); i != rows.end(); ++i) {
+               boost::shared_ptr<Route> route = (*i)[_columns.route];
+
+               if (boost::dynamic_pointer_cast<Track> (route)) {
+                       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
+                       
+                       if (mt) {
+                               (*i)[_columns.is_input_active] = mt->input_active();
+                       }
+               }
+       }
 }
 
 void
@@ -1256,7 +1565,7 @@ EditorRoutes::update_rec_display ()
                        } else {
                                (*i)[_columns.rec_state] = 0;
                        }
-               
+
                        (*i)[_columns.name_editable] = !route->record_enabled ();
                }
        }
@@ -1270,7 +1579,7 @@ EditorRoutes::update_mute_display ()
 
        for (i = rows.begin(); i != rows.end(); ++i) {
                boost::shared_ptr<Route> route = (*i)[_columns.route];
-               (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
+               (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
        }
 }
 
@@ -1282,7 +1591,7 @@ EditorRoutes::update_solo_display (bool /* selfsoloed */)
 
        for (i = rows.begin(); i != rows.end(); ++i) {
                boost::shared_ptr<Route> route = (*i)[_columns.route];
-               (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
+               (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
        }
 }
 
@@ -1294,7 +1603,7 @@ EditorRoutes::update_solo_isolate_display ()
 
        for (i = rows.begin(); i != rows.end(); ++i) {
                boost::shared_ptr<Route> route = (*i)[_columns.route];
-               (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
+               (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
        }
 }
 
@@ -1306,7 +1615,7 @@ EditorRoutes::update_solo_safe_display ()
 
        for (i = rows.begin(); i != rows.end(); ++i) {
                boost::shared_ptr<Route> route = (*i)[_columns.route];
-               (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
+               (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
        }
 }
 
@@ -1381,7 +1690,7 @@ EditorRoutes::show_tracks_with_regions_at_playhead ()
        }
 
        suspend_redisplay ();
-       
+
        TreeModel::Children rows = _model->children ();
        for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
                TimeAxisView* tv = (*i)[_columns.tv];
@@ -1390,3 +1699,4 @@ EditorRoutes::show_tracks_with_regions_at_playhead ()
 
        resume_redisplay ();
 }
+