X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_routes.cc;h=65db793bb32bf5509e92c46d6b4410e8819abc7b;hb=c03ea1fdb307fadff2b3c3f837426f1039bf5b10;hp=9f091867ebc963558948c01f4e0ff7d7531b5fc1;hpb=64dc5427e4f5339a16a018692dd94f476c53cae9;p=ardour.git diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index 9f091867eb..65db793bb3 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -24,7 +24,6 @@ #include #include -#include "ardour/diskstream.h" #include "ardour/session.h" #include "editor.h" @@ -42,14 +41,15 @@ #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; -using namespace sigc; using namespace ARDOUR; using namespace PBD; using namespace Gtk; @@ -58,13 +58,18 @@ using namespace Glib; using Gtkmm2ext::Keyboard; 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) + : EditorComponent (e) + , _ignore_reorder (false) + , _no_redisplay (false) + , _redisplay_does_not_sync_order_keys (false) + , _redisplay_does_not_reset_order_keys (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); @@ -72,159 +77,334 @@ EditorRoutes::EditorRoutes (Editor* e) _display.set_model (_model); // Record enable toggle - CellRendererPixbufToggle* rec_col_renderer = manage (new CellRendererPixbufToggle()); + CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti()); - rec_col_renderer->set_active_pixbuf (::get_icon("rec-enabled")); - rec_col_renderer->set_inactive_pixbuf (::get_icon("act-disabled")); - rec_col_renderer->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_toggled)); + 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->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_active(), _columns.rec_enabled); + 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(column_width); + + // MIDI Input Active + + CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti()); + input_active_col_renderer->set_pixbuf (0, ::get_icon("act-disabled")); + 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("mute-enabled")); + mute_col_renderer->set_pixbuf (0, ::get_icon("mute-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->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled)); TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer)); mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state); + mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED); + mute_state_column->set_alignment(ALIGN_CENTER); + mute_state_column->set_expand(false); + mute_state_column->set_fixed_width(15); // 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 (0, ::get_icon("solo-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->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->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(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("I", *solo_iso_renderer)); + 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->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(column_width); + + // Solo safe toggle + CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ()); + + 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->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(column_width); + + _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 (_("Show"), _columns.visible); - _display.append_column (_("Name"), _columns.text); + _display.append_column (*solo_safe_state_column); + + _name_column = _display.append_column (_("Name"), _columns.text) - 1; + _visible_column = _display.append_column (_("V"), _columns.visible) - 1; _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_rules_hint (true); _display.set_size_request (100, -1); _display.add_object_drag (_columns.route.index(), "routes"); - CellRendererText* name_cell = dynamic_cast (_display.get_column_cell_renderer (5)); + CellRendererText* name_cell = dynamic_cast (_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 (_name_column); - TreeViewColumn* name_column = _display.get_column (5); assert (name_column); name_column->add_attribute (name_cell->property_editable(), _columns.name_editable); + name_column->set_sizing(TREE_VIEW_COLUMN_FIXED); + name_column->set_expand(true); + name_column->set_min_width(50); + name_cell->property_editable() = true; name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit)); - CellRendererToggle* visible_cell = dynamic_cast (_display.get_column_cell_renderer (4)); + // Set the visible column cell renderer to radio toggle + CellRendererToggle* visible_cell = dynamic_cast (_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 (_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); + _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); - Route::SyncOrderKeys.connect (sigc::mem_fun (*this, &EditorRoutes::sync_order_keys)); + _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false); + _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out)); + + _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false); + _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false); + + _display.set_enable_search (false); + + Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context()); +} + +bool +EditorRoutes::focus_in (GdkEventFocus*) +{ + Window* win = dynamic_cast (_scroller.get_toplevel ()); + + if (win) { + old_focus = win->get_focus (); + } else { + old_focus = 0; + } + + name_editable = 0; + + /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */ + return true; +} + +bool +EditorRoutes::focus_out (GdkEventFocus*) +{ + if (old_focus) { + old_focus->grab_focus (); + old_focus = 0; + } + + return false; +} + +bool +EditorRoutes::enter_notify (GdkEventCrossing*) +{ + if (name_editable) { + return true; + } + + /* arm counter so that ::selection_filter() will deny selecting anything for the + next two attempts to change selection status. + */ + selection_countdown = 2; + _scroller.grab_focus (); + Keyboard::magic_widget_grab_focus (); + return false; +} + +bool +EditorRoutes::leave_notify (GdkEventCrossing*) +{ + selection_countdown = 0; + + if (old_focus) { + old_focus->grab_focus (); + old_focus = 0; + } + + Keyboard::magic_widget_drop_focus (); + return false; } void -EditorRoutes::connect_to_session (Session* s) +EditorRoutes::set_session (Session* s) { - EditorComponent::connect_to_session (s); + SessionHandlePtr::set_session (s); initial_display (); - _session->SoloChanged.connect (sigc::mem_fun (*this, &EditorRoutes::solo_changed_so_update_mute)); + 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()); + } } void -EditorRoutes::on_tv_rec_enable_toggled (Glib::ustring const & path_string) +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)); - row[_columns.name_editable] = !row[_columns.rec_enabled]; - - TimeAxisView *tv = row[_columns.tv]; - AudioTimeAxisView *atv = dynamic_cast (tv); + TimeAxisView* tv = row[_columns.tv]; + RouteTimeAxisView *rtv = dynamic_cast (tv); + + if (rtv) { + boost::shared_ptr 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) +{ + // Get the model row that has been toggled. + Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string)); - if (atv != 0 && atv->is_audio_track()){ + TimeAxisView* tv = row[_columns.tv]; + RouteTimeAxisView *rtv = dynamic_cast (tv); + + if (rtv && rtv->track()) { boost::shared_ptr rl (new RouteList); - rl->push_back (atv->route()); - _session->set_record_enable (rl, !atv->track()->record_enabled(), Session::rt_cleanup); + rl->push_back (rtv->route()); + _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup); } } void -EditorRoutes::on_tv_mute_enable_toggled (Glib::ustring const & path_string) +EditorRoutes::on_tv_mute_enable_toggled (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]; - AudioTimeAxisView *atv = dynamic_cast (tv); + RouteTimeAxisView *rtv = dynamic_cast (tv); - if (atv != 0) { + if (rtv != 0) { boost::shared_ptr rl (new RouteList); - rl->push_back (atv->route()); - _session->set_mute (rl, !atv->route()->muted(), Session::rt_cleanup); + rl->push_back (rtv->route()); + _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup); } } void -EditorRoutes::on_tv_solo_enable_toggled (Glib::ustring const & path_string) +EditorRoutes::on_tv_solo_enable_toggled (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]; - AudioTimeAxisView *atv = dynamic_cast (tv); + RouteTimeAxisView* rtv = dynamic_cast (tv); - if (atv != 0) { + if (rtv != 0) { boost::shared_ptr rl (new RouteList); - rl->push_back (atv->route()); - _session->set_solo (rl, !atv->route()->soloed(), Session::rt_cleanup); + rl->push_back (rtv->route()); + if (Config->get_solo_control_is_listen_control()) { + _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup); + } else { + _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup); + } + } +} + +void +EditorRoutes::on_tv_solo_isolate_toggled (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 (tv); + + if (rtv) { + rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this); } } void -EditorRoutes::on_tv_solo_isolate_toggled (Glib::ustring const & path_string) +EditorRoutes::on_tv_solo_safe_toggled (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]; - AudioTimeAxisView *atv = dynamic_cast (tv); + RouteTimeAxisView* rtv = dynamic_cast (tv); - if (atv != 0) { - atv->route()->set_solo_isolated (!atv->route()->solo_isolated(), this); + if (rtv) { + rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this); } } @@ -245,6 +425,8 @@ EditorRoutes::build_menu () items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks))); items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus))); items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus))); + items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks))); + items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks))); items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead))); } @@ -261,15 +443,15 @@ EditorRoutes::show_menu () void EditorRoutes::redisplay () { + if (_no_redisplay || !_session) { + return; + } + TreeModel::Children rows = _model->children(); TreeModel::Children::iterator i; uint32_t position; int n; - if (_no_redisplay) { - return; - } - for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) { TimeAxisView *tv = (*i)[_columns.tv]; boost::shared_ptr route = (*i)[_columns.route]; @@ -294,19 +476,21 @@ EditorRoutes::redisplay () position += tv->show_at (position, n, &_editor->edit_controls_vbox); tv->clip_to_viewport (); } else { - tv->set_marked_for_display (false); - tv->hide (); + tv->set_visibility (false); } 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. */ Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes)); + _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); @@ -326,17 +510,24 @@ EditorRoutes::redisplay () void EditorRoutes::route_deleted (Gtk::TreeModel::Path const &) { - /* this could require an order reset & sync */ + 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; } - void -EditorRoutes::visible_changed (Glib::ustring const & path) +EditorRoutes::visible_changed (std::string const & path) { + if (_session && _session->deletion_in_progress()) { + return; + } + TreeIter iter; if ((iter = _model->get_iter (path))) { @@ -363,6 +554,8 @@ EditorRoutes::routes_added (list routes) for (list::iterator x = routes.begin(); x != routes.end(); ++x) { + boost::shared_ptr midi_trk = boost::dynamic_pointer_cast ((*x)->route()); + row = *(_model->append ()); row[_columns.text] = (*x)->route()->name(); @@ -371,6 +564,20 @@ EditorRoutes::routes_added (list routes) row[_columns.route] = (*x)->route (); row[_columns.is_track] = (boost::dynamic_pointer_cast ((*x)->route()) != 0); + 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; + } + + 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 */ @@ -381,27 +588,40 @@ EditorRoutes::routes_added (list routes) _ignore_reorder = false; boost::weak_ptr wr ((*x)->route()); - (*x)->route()->gui_changed.connect (sigc::mem_fun (*this, &EditorRoutes::handle_gui_changes)); - (*x)->route()->NameChanged.connect (sigc::bind (sigc::mem_fun (*this, &EditorRoutes::route_name_changed), wr)); - (*x)->GoingAway.connect (sigc::bind (sigc::mem_fun (*this, &EditorRoutes::route_removed), *x)); + + (*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()); if ((*x)->is_track()) { boost::shared_ptr t = boost::dynamic_pointer_cast ((*x)->route()); - t->diskstream()->RecordEnableChanged.connect (sigc::mem_fun (*this, &EditorRoutes::update_rec_display)); + t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); } - (*x)->route()->mute_changed.connect (sigc::mem_fun (*this, &EditorRoutes::update_mute_display)); - (*x)->route()->solo_changed.connect (sigc::mem_fun (*this, &EditorRoutes::update_solo_display)); - (*x)->route()->solo_isolated_changed.connect (sigc::mem_fun (*this, &EditorRoutes::update_solo_isolate_display)); + if ((*x)->is_midi_track()) { + boost::shared_ptr t = boost::dynamic_pointer_cast ((*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_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()); } update_rec_display (); + update_mute_display (); + update_solo_display (true); + update_solo_isolate_display (); + update_solo_safe_display (); + update_input_active_display (); resume_redisplay (); _redisplay_does_not_sync_order_keys = false; } void -EditorRoutes::handle_gui_changes (string const & what, void *src) +EditorRoutes::handle_gui_changes (string const & what, void*) { ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src) @@ -443,11 +663,16 @@ EditorRoutes::route_removed (TimeAxisView *tv) } void -EditorRoutes::route_name_changed (boost::weak_ptr r) +EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr r) { + if (!what_changed.contains (ARDOUR::Properties::name)) { + return; + } + ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r) boost::shared_ptr route = r.lock (); + if (!route) { return; } @@ -493,6 +718,8 @@ EditorRoutes::hide_track_in_display (TimeAxisView& tv) break; } } + + redisplay (); } void @@ -507,6 +734,8 @@ EditorRoutes::show_track_in_display (TimeAxisView& tv) break; } } + + redisplay (); } void @@ -521,7 +750,7 @@ EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, i void EditorRoutes::sync_order_keys (string const & src) { - vector neworder; + map new_order; TreeModel::Children rows = _model->children(); TreeModel::Children::iterator ri; @@ -529,20 +758,16 @@ EditorRoutes::sync_order_keys (string const & src) return; } - for (ri = rows.begin(); ri != rows.end(); ++ri) { - neworder.push_back (0); - } - bool changed = false; int order; for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) { boost::shared_ptr route = (*ri)[_columns.route]; - int old_key = order; - int new_key = route->order_key (N_ ("editor")); + int const old_key = order; + int const new_key = route->order_key (N_ ("editor")); - neworder[new_key] = old_key; + new_order[new_key] = old_key; if (new_key != old_key) { changed = true; @@ -551,7 +776,14 @@ EditorRoutes::sync_order_keys (string const & src) if (changed) { _redisplay_does_not_reset_order_keys = true; - _model->reorder (neworder); + + /* `compact' new_order into a vector */ + vector co; + for (map::const_iterator i = new_order.begin(); i != new_order.end(); ++i) { + co.push_back (i->second); + } + + _model->reorder (co); _redisplay_does_not_reset_order_keys = false; } } @@ -610,7 +842,7 @@ EditorRoutes::set_all_tracks_visibility (bool yn) } void -EditorRoutes::set_all_audio_visibility (int tracks, bool yn) +EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn) { TreeModel::Children rows = _model->children(); TreeModel::Children::iterator i; @@ -618,9 +850,12 @@ EditorRoutes::set_all_audio_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; @@ -645,6 +880,19 @@ EditorRoutes::set_all_audio_visibility (int tracks, bool yn) break; } } + else if ((mtv = dynamic_cast(tv)) != 0) { + switch (tracks) { + case 0: + (*i)[_columns.visible] = yn; + break; + + case 3: + if (mtv->is_midi_track()) { + (*i)[_columns.visible] = yn; + } + break; + } + } } resume_redisplay (); @@ -662,26 +910,138 @@ EditorRoutes::show_all_routes () set_all_tracks_visibility (true); } +void +EditorRoutes::show_all_audiotracks() +{ + set_all_audio_midi_visibility (1, true); +} +void +EditorRoutes::hide_all_audiotracks () +{ + set_all_audio_midi_visibility (1, false); +} + void EditorRoutes::show_all_audiobus () { - set_all_audio_visibility (2, true); + set_all_audio_midi_visibility (2, true); } void EditorRoutes::hide_all_audiobus () { - set_all_audio_visibility (2, false); + set_all_audio_midi_visibility (2, false); } void -EditorRoutes::show_all_audiotracks() +EditorRoutes::show_all_miditracks() { - set_all_audio_visibility (1, true); + set_all_audio_midi_visibility (3, true); } void -EditorRoutes::hide_all_audiotracks () +EditorRoutes::hide_all_miditracks () { - set_all_audio_visibility (1, false); + set_all_audio_midi_visibility (3, false); +} + +bool +EditorRoutes::key_press (GdkEventKey* ev) +{ + TreeViewColumn *col; + boost::shared_ptr rl (new RouteList); + TreePath path; + + switch (ev->keyval) { + case GDK_Tab: + case GDK_ISO_Left_Tab: + + /* If we appear to be editing something, leave that cleanly and appropriately. + */ + if (name_editable) { + name_editable->editing_done (); + name_editable = 0; + } + + 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); + } else { + treeview_select_next (_display, _model, col); + } + + return true; + break; + + case 'm': + if (get_relevant_routes (rl)) { + _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup); + } + return true; + break; + + case 's': + 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; + + case 'r': + if (get_relevant_routes (rl)) { + _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup); + } + break; + + default: + break; + } + + return false; +} + +bool +EditorRoutes::get_relevant_routes (boost::shared_ptr rl) +{ + TimeAxisView* tv; + RouteTimeAxisView* rtv; + RefPtr selection = _display.get_selection(); + TreePath path; + TreeIter iter; + + if (selection->count_selected_rows() != 0) { + + /* use selection */ + + RefPtr tm = RefPtr::cast_dynamic (_model); + iter = selection->get_selected (tm); + + } else { + /* use mouse pointer */ + + int x, y; + int bx, by; + + _display.get_pointer (x, y); + _display.convert_widget_to_bin_window_coords (x, y, bx, by); + + if (_display.get_path_at_pos (bx, by, path)) { + iter = _model->get_iter (path); + } + } + + if (iter) { + tv = (*iter)[_columns.tv]; + if (tv) { + rtv = dynamic_cast(tv); + if (rtv) { + rl->push_back (rtv->route()); + } + } + } + + return !rl->empty(); } bool @@ -692,12 +1052,48 @@ EditorRoutes::button_press (GdkEventButton* ev) 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; + } + + //Only scroll to if the track is visible + if(y_pos != -1){ + _editor->reset_y_origin (y_pos); + } + } + return false; } bool -EditorRoutes::selection_filter (Glib::RefPtr const &, TreeModel::Path const &, bool) +EditorRoutes::selection_filter (Glib::RefPtr const &, TreeModel::Path const&, bool /*selected*/) { + if (selection_countdown) { + if (--selection_countdown == 0) { + return true; + } else { + /* no selection yet ... */ + return false; + } + } return true; } @@ -711,15 +1107,19 @@ struct EditorOrderRouteSorter { void EditorRoutes::initial_display () { + suspend_redisplay (); + _model->clear (); + + if (!_session) { + resume_redisplay (); + return; + } + boost::shared_ptr routes = _session->get_routes(); RouteList r (*routes); EditorOrderRouteSorter sorter; r.sort (sorter); - - suspend_redisplay (); - - _model->clear (); _editor->handle_new_route (r); /* don't show master bus in a new session */ @@ -732,6 +1132,7 @@ EditorRoutes::initial_display () _no_redisplay = true; for (i = rows.begin(); i != rows.end(); ++i) { + TimeAxisView *tv = (*i)[_columns.tv]; RouteTimeAxisView *rtv; @@ -881,7 +1282,26 @@ EditorRoutes::move_selected_tracks (bool up) _model->reorder (neworder); - _session->sync_order_keys (N_ ("editor")); + _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 = (*i)[_columns.route]; + + if (boost::dynamic_pointer_cast (route)) { + boost::shared_ptr mt = boost::dynamic_pointer_cast (route); + + if (mt) { + (*i)[_columns.is_input_active] = mt->input_active(); + } + } + } } void @@ -893,39 +1313,52 @@ EditorRoutes::update_rec_display () for (i = rows.begin(); i != rows.end(); ++i) { boost::shared_ptr route = (*i)[_columns.route]; - if (boost::dynamic_pointer_cast(route)) { - (*i)[_columns.rec_enabled] = route->record_enabled (); + if (boost::dynamic_pointer_cast (route)) { + boost::shared_ptr mt = boost::dynamic_pointer_cast (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] = 0; + } + (*i)[_columns.name_editable] = !route->record_enabled (); } } } void -EditorRoutes::update_mute_display (void* /*src*/) +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 = (*i)[_columns.route]; - (*i)[_columns.mute_state] = RouteUI::mute_visual_state (*_session, route) > 0 ? 1 : 0; + (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route); } } void -EditorRoutes::update_solo_display (void* /*src*/) +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 = (*i)[_columns.route]; - (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route) > 0 ? 1 : 0; + (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route); } } void -EditorRoutes::update_solo_isolate_display (void* /*src*/) +EditorRoutes::update_solo_isolate_display () { TreeModel::Children rows = _model->children(); TreeModel::Children::iterator i; @@ -936,6 +1369,18 @@ EditorRoutes::update_solo_isolate_display (void* /*src*/) } } +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 = (*i)[_columns.route]; + (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0; + } +} + list EditorRoutes::views () const { @@ -956,9 +1401,26 @@ EditorRoutes::clear () } void -EditorRoutes::name_edit (Glib::ustring const & path, Glib::ustring const & new_text) +EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&) { + name_editable = ce; + + /* give it a special name */ + + Gtk::Entry *e = dynamic_cast (ce); + + if (e) { + e->set_name (X_("RouteNameEditorEntry")); + } +} + +void +EditorRoutes::name_edit (std::string const & path, std::string const & new_text) +{ + name_editable = 0; + TreeIter iter = _model->get_iter (path); + if (!iter) { return; } @@ -973,9 +1435,7 @@ EditorRoutes::name_edit (Glib::ustring const & path, Glib::ustring const & new_t void EditorRoutes::solo_changed_so_update_mute () { - ENSURE_GUI_THREAD (*this, &EditorRoutes::solo_changed_so_update_mute) - - update_mute_display (this); + update_mute_display (); } void @@ -992,7 +1452,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]; @@ -1000,5 +1460,4 @@ EditorRoutes::show_tracks_with_regions_at_playhead () } resume_redisplay (); - }