fix peak-thread, GUI-thread race condition
[ardour.git] / gtk2_ardour / editor_routes.cc
index 7fc069e17713d96bb194a08bffc0f3396fffbed4..b249c0a100364ac4ce631e1336d295286e0411dc 100644 (file)
@@ -72,12 +72,13 @@ EditorRoutes::EditorRoutes (Editor* e)
        , _no_redisplay (false)
        , _adding_routes (false)
        , _route_deletion_in_progress (false)
+       , _redisplay_on_resume (false)
        , _redisplay_active (0)
        , _queue_tv_update (0)
        , _menu (0)
        , old_focus (0)
        , selection_countdown (0)
-               , name_editable (0)
+       , name_editable (0)
 {
        static const int column_width = 22;
 
@@ -203,15 +204,15 @@ EditorRoutes::EditorRoutes (Editor* e)
        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)") },
+               { 0,  _("Name"),        _("Track/Bus Name") },
+               { 1, S_("Visible|V"),   _("Track/Bus visible ?") },
+               { 2, S_("Active|A"),    _("Track/Bus active ?") },
+               { 3, S_("MidiInput|I"), _("MIDI input enabled") },
+               { 4, S_("Rec|R"),       _("Record enabled") },
+               { 5, S_("Mute|M"),      _("Muted") },
+               { 6, S_("Solo|S"),      _("Soloed") },
+               { 7, S_("SoloIso|SI"),  _("Solo Isolated") },
+               { 8, S_("SoloLock|SS"), _("Solo Safe (Locked)") },
                { -1, 0, 0 }
        };
 
@@ -360,6 +361,13 @@ EditorRoutes::set_session (Session* s)
        if (_session) {
                _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
                _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
+
+               /* TODO: check if these needs to be tied in with DisplaySuspender
+                * Given that the UI is single-threaded and DisplaySuspender is only used
+                * in loops in the UI thread all should be fine.
+                */
+               _session->BatchUpdateStart.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::suspend_redisplay, this), gui_context());
+               _session->BatchUpdateEnd.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::resume_redisplay, this), gui_context());
        }
 }
 
@@ -516,7 +524,7 @@ EditorRoutes::redisplay_real ()
                }
 
                bool visible = tv->marked_for_display ();
-
+               
                /* show or hide the TimeAxisView */
                if (visible) {
                        position += tv->show_at (position, n, &_editor->edit_controls_vbox);
@@ -549,21 +557,25 @@ EditorRoutes::redisplay_real ()
 void
 EditorRoutes::redisplay ()
 {
-       if (_no_redisplay || !_session || _session->deletion_in_progress()) {
+       if (!_session || _session->deletion_in_progress()) {
+               return;
+       }
+
+       if (_no_redisplay) {
+               _redisplay_on_resume = true;
                return;
        }
 
        // model deprecated g_atomic_int_exchange_and_add(, 1)
        g_atomic_int_inc(&_redisplay_active);
        if (!g_atomic_int_compare_and_exchange (&_redisplay_active, 1, 1)) {
-               printf ("SKIP redisplay\n");
                return;
        }
 
        redisplay_real ();
 
        while (!g_atomic_int_compare_and_exchange (&_redisplay_active, 1, 0)) {
-               g_atomic_pointer_set(&_redisplay_active, 1);
+               g_atomic_int_set(&_redisplay_active, 1);
                redisplay_real ();
        }
 }
@@ -637,7 +649,6 @@ void
 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
 {
        PBD::Unwinder<bool> at (_adding_routes, true);
-
        bool from_scratch = (_model->children().size() == 0);
        Gtk::TreeModel::Children::iterator insert_iter = _model->children().end();
 
@@ -650,10 +661,6 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
                }
        }
 
-       if(!from_scratch) {
-               _editor->selection->tracks.clear();
-       }
-
        DisplaySuspender ds;
 
        _display.set_model (Glib::RefPtr<ListStore>());
@@ -686,10 +693,6 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
                row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
                row[_columns.name_editable] = true;
 
-               if (!from_scratch) {
-                       _editor->selection->add(*x);
-               }
-
                boost::weak_ptr<Route> wr ((*x)->route());
 
                (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
@@ -726,8 +729,9 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
        _display.set_model (_model);
 
        /* now update route order keys from the treeview/track display order */
-
-       sync_order_keys_from_treeview ();
+       if (!from_scratch) {
+               sync_order_keys_from_treeview ();
+       }
 }
 
 void
@@ -811,7 +815,7 @@ EditorRoutes::update_visibility ()
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       DisplaySuspender ds ();
+       DisplaySuspender ds;
 
        for (i = rows.begin(); i != rows.end(); ++i) {
                TimeAxisView *tv = (*i)[_columns.tv];
@@ -880,10 +884,19 @@ EditorRoutes::reset_remote_control_ids ()
 
        for (ri = rows.begin(); ri != rows.end(); ++ri) {
 
+               /* skip two special values */
+               
+               if (rid == Route::MasterBusRemoteControlID) {
+                       rid++;
+               }
+               
+               if (rid == Route::MonitorBusRemoteControlID) {
+                       rid++;
+               }
+
                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--);
@@ -1326,6 +1339,8 @@ EditorRoutes::button_press (GdkEventButton* ev)
 void
 EditorRoutes::selection_changed ()
 {
+       _editor->begin_reversible_selection_op (X_("Select Track from Route List"));
+
        if (_display.get_selection()->count_selected_rows() > 0) {
 
                TreeIter iter;
@@ -1350,6 +1365,8 @@ EditorRoutes::selection_changed ()
        } else {
                _editor->get_selection().clear_tracks ();
        }
+
+       _editor->commit_reversible_selection_op ();
 }
 
 bool
@@ -1390,29 +1407,10 @@ EditorRoutes::initial_display ()
                return;
        }
 
-       boost::shared_ptr<RouteList> routes = _session->get_routes();
-
-       if (ARDOUR_UI::instance()->session_is_new ()) {
-
-               /* new session: stamp all routes with the right editor order
-                * key
-                */
-
-               _editor->add_routes (*(routes.get()));
-
-       } else {
-
-               /* existing session: sort a copy of the route list by
-                * editor-order and add its contents to the display.
-                */
-
-               RouteList r (*routes);
-               EditorOrderRouteSorter sorter;
-
-               r.sort (sorter);
-               _editor->add_routes (r);
-
-       }
+       RouteList r (*_session->get_routes());
+               
+       r.sort (EditorOrderRouteSorter ());
+       _editor->add_routes (r);
 }
 
 void
@@ -1596,26 +1594,25 @@ EditorRoutes::idle_update_mute_rec_solo_etc()
                (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
                (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
                (*i)[_columns.active] = route->active ();
-               {
-                       if (boost::dynamic_pointer_cast<Track> (route)) {
-                               boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
-
-                               if (route->record_enabled()) {
-                                       if (_session->record_status() == Session::Recording) {
-                                               (*i)[_columns.rec_state] = 1;
-                                       } else {
-                                               (*i)[_columns.rec_state] = 2;
-                                       }
-                               } else if (mt && mt->step_editing()) {
-                                       (*i)[_columns.rec_state] = 3;
+               if (boost::dynamic_pointer_cast<Track> (route)) {
+                       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
+                       
+                       if (route->record_enabled()) {
+                               if (_session->record_status() == Session::Recording) {
+                                       (*i)[_columns.rec_state] = 1;
                                } else {
-                                       (*i)[_columns.rec_state] = 0;
+                                       (*i)[_columns.rec_state] = 2;
                                }
-
-                               (*i)[_columns.name_editable] = !route->record_enabled ();
+                       } else if (mt && mt->step_editing()) {
+                               (*i)[_columns.rec_state] = 3;
+                       } else {
+                               (*i)[_columns.rec_state] = 0;
                        }
+                       
+                       (*i)[_columns.name_editable] = !route->record_enabled ();
                }
        }
+
        return false; // do not call again (until needed)
 }