idle update editor TreeView
[ardour.git] / gtk2_ardour / editor_routes.cc
index a10e22f306cb31733684bedc7c91170bed922d28..7ad4e8a7cd214e2746fd20e44a63bdcc6daa7efc 100644 (file)
@@ -53,6 +53,7 @@
 
 using namespace std;
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
@@ -70,6 +71,7 @@ EditorRoutes::EditorRoutes (Editor* e)
         , _ignore_reorder (false)
         , _no_redisplay (false)
         , _adding_routes (false)
+        , _route_deletion_in_progress (false)
         , _menu (0)
         , old_focus (0)
         , selection_countdown (0)
@@ -222,6 +224,7 @@ EditorRoutes::EditorRoutes (Editor* e)
        _display.set_headers_visible (true);
        _display.get_selection()->set_mode (SELECTION_SINGLE);
        _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);
        _display.set_name (X_("EditGroupList"));
        _display.set_rules_hint (true);
@@ -270,7 +273,7 @@ EditorRoutes::EditorRoutes (Editor* e)
        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_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::row_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);
@@ -379,6 +382,7 @@ EditorRoutes::on_input_active_changed (std::string const & path_string)
 void
 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
 {
+       DisplaySuspender ds;
        // Get the model row that has been toggled.
        Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
 
@@ -491,9 +495,10 @@ EditorRoutes::show_menu ()
 void
 EditorRoutes::redisplay ()
 {
-       if (_no_redisplay || !_session || _session->deletion_in_progress()) {
+       if (_no_redisplay || !_session || _session->deletion_in_progress() || _redisplaying) {
                return;
        }
+       _redisplaying = true; // tv->show_at() below causes recursive redisplay via handle_gui_changes()
 
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
@@ -518,7 +523,6 @@ EditorRoutes::redisplay ()
                /* show or hide the TimeAxisView */
                if (visible) {
                        position += tv->show_at (position, n, &_editor->edit_controls_vbox);
-                       tv->clip_to_viewport ();
                } else {
                        tv->hide ();
                }
@@ -534,31 +538,44 @@ 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;
 
-       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);
+               _editor->vertical_adjustment.set_value (_editor->_full_canvas_height - _editor->_visible_canvas_height);
        }
+       _redisplaying = false;
 }
 
 void
-EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
+EditorRoutes::row_deleted (Gtk::TreeModel::Path const &)
 {
-       /* this happens as the second step of a DnD within the treeview as well
-          as when a row/route is actually deleted.
+       /* this happens as the second step of a DnD within the treeview, and
+          when a route is actually removed. we don't differentiate between
+          the two cases.
+          
+          note that the sync_orders_keys() step may not actually change any
+          RID's (e.g. the last track may be removed, so all other tracks keep
+          the same RID), which means that no redisplay would happen. so we 
+           have to force a redisplay.
        */
+
        DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview row deleted\n");
+
+       DisplaySuspender ds;
        sync_order_keys_from_treeview ();
 }
 
 void
 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
 {
+       /* reordering implies that RID's will change, so sync_order_keys() will
+          cause a redisplay.
+       */
+
        DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview reordered\n");
        sync_order_keys_from_treeview ();
 }
@@ -570,6 +587,7 @@ EditorRoutes::visible_changed (std::string const & path)
                return;
        }
 
+       DisplaySuspender ds;
        TreeIter iter;
 
        if ((iter = _model->get_iter (path))) {
@@ -600,10 +618,25 @@ EditorRoutes::active_changed (std::string const & path)
 void
 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
 {
-       TreeModel::Row row;
        PBD::Unwinder<bool> at (_adding_routes, true);
 
-       suspend_redisplay ();
+       bool from_scratch = (_model->children().size() == 0);
+       Gtk::TreeModel::Children::iterator insert_iter = _model->children().end();
+
+       for (Gtk::TreeModel::Children::iterator it = _model->children().begin(); it != _model->children().end(); ++it) {
+               boost::shared_ptr<Route> r = (*it)[_columns.route];
+
+               if (r->order_key() == (routes.front()->route()->order_key() + routes.size())) {
+                       insert_iter = it;
+                       break;
+               }
+       }
+
+       if(!from_scratch) {
+               _editor->selection->tracks.clear();
+       } 
+
+       DisplaySuspender ds;
 
        _display.set_model (Glib::RefPtr<ListStore>());
 
@@ -611,7 +644,7 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
 
                boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
 
-               row = *(_model->append ());
+               TreeModel::Row row = *(_model->insert (insert_iter));
 
                row[_columns.text] = (*x)->route()->name();
                row[_columns.visible] = (*x)->marked_for_display();
@@ -635,6 +668,10 @@ 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());
@@ -645,12 +682,12 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
                        t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
                }
 
-               if ((*x)->is_midi_track()) {
-                       boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
+               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());
-               }
-
+                       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, 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());
@@ -668,7 +705,6 @@ EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
        update_input_active_display ();
        update_active_display ();
 
-       resume_redisplay ();
        _display.set_model (_model);
 
        /* now update route order keys from the treeview/track display order */
@@ -687,7 +723,6 @@ EditorRoutes::handle_gui_changes (string const & what, void*)
                /* Optional :make tracks change height while it happens, instead
                   of on first-idle
                */
-               //update_canvas_now ();
                redisplay ();
        }
 
@@ -706,6 +741,7 @@ EditorRoutes::route_removed (TimeAxisView *tv)
 
        for (ri = rows.begin(); ri != rows.end(); ++ri) {
                if ((*ri)[_columns.tv] == tv) {
+                       PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
                        _model->erase (ri);
                        break;
                }
@@ -746,13 +782,10 @@ 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 ();
+       if (_queue_mute_rec_solo_etc == 0) {
+               Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
        }
+       _queue_mute_rec_solo_etc |= 16;
 }
 
 void
@@ -761,7 +794,7 @@ EditorRoutes::update_visibility ()
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       suspend_redisplay ();
+       DisplaySuspender ds ();
 
        for (i = rows.begin(); i != rows.end(); ++i) {
                TimeAxisView *tv = (*i)[_columns.tv];
@@ -772,8 +805,6 @@ EditorRoutes::update_visibility ()
         */
 
        sync_order_keys_from_treeview ();
-
-       resume_redisplay ();
 }
 
 void
@@ -991,7 +1022,7 @@ EditorRoutes::hide_all_tracks (bool /*with_select*/)
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        for (i = rows.begin(); i != rows.end(); ++i) {
 
@@ -1004,14 +1035,6 @@ EditorRoutes::hide_all_tracks (bool /*with_select*/)
 
                row[_columns.visible] = false;
        }
-
-       resume_redisplay ();
-
-       /* XXX this seems like a hack and half, but its not clear where to put this
-          otherwise.
-       */
-
-       //reset_scrolling_region ();
 }
 
 void
@@ -1020,7 +1043,7 @@ EditorRoutes::set_all_tracks_visibility (bool yn)
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        for (i = rows.begin(); i != rows.end(); ++i) {
 
@@ -1039,8 +1062,6 @@ EditorRoutes::set_all_tracks_visibility (bool yn)
         */
 
        sync_order_keys_from_treeview ();
-
-       resume_redisplay ();
 }
 
 void
@@ -1049,7 +1070,7 @@ EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
        TreeModel::Children rows = _model->children();
        TreeModel::Children::iterator i;
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        for (i = rows.begin(); i != rows.end(); ++i) {
 
@@ -1101,8 +1122,6 @@ EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
         */
 
        sync_order_keys_from_treeview ();
-
-       resume_redisplay ();
 }
 
 void
@@ -1277,27 +1296,46 @@ EditorRoutes::button_press (GdkEventButton* ev)
        //Scroll editor canvas to selected track
        if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
 
-               // 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;
-               }
-
-               //Only scroll to if the track is visible
-               if(y_pos != -1){
-                   _editor->reset_y_origin (y_pos);
+               if (tv) {
+                       _editor->ensure_time_axis_view_is_visible (*tv, true);
                }
        }
 
        return false;
 }
 
+void
+EditorRoutes::selection_changed ()
+{
+       if (_display.get_selection()->count_selected_rows() > 0) {
+
+               TreeIter iter;
+               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))) {
+
+                               TimeAxisView* tv = (*iter)[_columns.tv];
+                               selected.push_back (tv);
+                       }
+                       
+               }
+
+               _editor->get_selection().set (selected);
+               _editor->ensure_time_axis_view_is_visible (*(selected.front()), true);
+
+       } else {
+               _editor->get_selection().clear_tracks ();
+       }
+}
+
 bool
 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
 {
@@ -1328,11 +1366,10 @@ struct EditorOrderRouteSorter {
 void
 EditorRoutes::initial_display ()
 {
-       suspend_redisplay ();
+       DisplaySuspender ds;
        _model->clear ();
 
        if (!_session) {
-               resume_redisplay ();
                return;
        }
 
@@ -1359,8 +1396,6 @@ EditorRoutes::initial_display ()
                _editor->add_routes (r);
                
        }
-
-       resume_redisplay ();
 }
 
 void
@@ -1525,78 +1560,96 @@ EditorRoutes::update_input_active_display ()
 void
 EditorRoutes::update_rec_display ()
 {
+       if (_queue_mute_rec_solo_etc == 0) {
+               Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
+       }
+       _queue_mute_rec_solo_etc |= 32;
+}
+
+bool
+EditorRoutes::idle_update_mute_rec_solo_etc()
+{
+       const int what = _queue_mute_rec_solo_etc;
+       _queue_mute_rec_solo_etc = 0;
        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 (route->record_enabled()) {
-                               if (_session->record_status() == Session::Recording) {
-                                       (*i)[_columns.rec_state] = 1;
+               if (what & 1) {
+                       (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
+               }
+               if (what & 2) {
+                       (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
+               }
+               if (what & 4) {
+                       (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
+               }
+               if (what & 8) {
+                       (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
+               }
+               if (what & 16) {
+                       (*i)[_columns.active] = route->active ();
+               }
+               if (what & 32) { // rec
+
+                       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;
                                } else {
-                                       (*i)[_columns.rec_state] = 2;
+                                       (*i)[_columns.rec_state] = 0;
                                }
-                       } else if (mt && mt->step_editing()) {
-                               (*i)[_columns.rec_state] = 3;
-                       } else {
-                               (*i)[_columns.rec_state] = 0;
-                       }
 
-                       (*i)[_columns.name_editable] = !route->record_enabled ();
+                               (*i)[_columns.name_editable] = !route->record_enabled ();
+                       }
                }
        }
+       return false; // do not call again (until needed)
 }
 
+
 void
 EditorRoutes::update_mute_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.mute_state] = RouteUI::mute_active_state (_session, route);
+       if (_queue_mute_rec_solo_etc == 0) {
+               Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
        }
+       _queue_mute_rec_solo_etc |= 1;
 }
 
 void
 EditorRoutes::update_solo_display (bool /* selfsoloed */)
 {
-       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.solo_state] = RouteUI::solo_active_state (route);
+       if (_queue_mute_rec_solo_etc == 0) {
+               Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
        }
+       _queue_mute_rec_solo_etc |= 2;
 }
 
 void
 EditorRoutes::update_solo_isolate_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.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
+       if (_queue_mute_rec_solo_etc == 0) {
+               Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
        }
+       _queue_mute_rec_solo_etc |= 4;
 }
 
 void
 EditorRoutes::update_solo_safe_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.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
+       if (_queue_mute_rec_solo_etc == 0) {
+               Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
        }
+       _queue_mute_rec_solo_etc |= 4;
 }
 
 list<TimeAxisView*>
@@ -1669,14 +1722,11 @@ EditorRoutes::show_tracks_with_regions_at_playhead ()
                }
        }
 
-       suspend_redisplay ();
+       DisplaySuspender ds;
 
        TreeModel::Children rows = _model->children ();
        for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
                TimeAxisView* tv = (*i)[_columns.tv];
                (*i)[_columns.visible] = (show.find (tv) != show.end());
        }
-
-       resume_redisplay ();
 }
-