X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_routes.cc;h=1368979e5896ef313775fb2f61437db3011747e5;hb=8b3d50f7e6c4ab7575b169cccc54efc0e2a94fd0;hp=b351c8a51dd518d51d5e761de5c8cdf52c40b2b2;hpb=9510cb20fcb9376fa20fc7bbc35aa8c53c55137e;p=ardour.git diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index b351c8a51d..1368979e58 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -41,9 +41,11 @@ #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" @@ -56,12 +58,15 @@ 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) { _scroller.add (_display); _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); @@ -70,15 +75,17 @@ 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); @@ -89,7 +96,8 @@ EditorRoutes::EditorRoutes (Editor* e) 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 (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)); @@ -105,6 +113,7 @@ EditorRoutes::EditorRoutes (Editor* e) 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->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled)); TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer)); @@ -128,7 +137,7 @@ EditorRoutes::EditorRoutes (Editor* e) 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(15); + solo_isolate_state_column->set_fixed_width(22); // Solo safe toggle CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ()); @@ -150,12 +159,15 @@ EditorRoutes::EditorRoutes (Editor* e) _display.append_column (*solo_isolate_state_column); _display.append_column (*solo_safe_state_column); - _display.append_column (_("Name"), _columns.text); + 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); _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); @@ -164,6 +176,7 @@ EditorRoutes::EditorRoutes (Editor* e) CellRendererText* name_cell = dynamic_cast (_display.get_column_cell_renderer (5)); assert (name_cell); + name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started)); TreeViewColumn* name_column = _display.get_column (5); @@ -194,95 +207,167 @@ EditorRoutes::EditorRoutes (Editor* e) _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); + + _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::set_session (Session* s) { - EditorComponent::set_session (s); + SessionHandlePtr::set_session (s); initial_display (); 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_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)); - TimeAxisView *tv = row[_columns.tv]; - AudioTimeAxisView *atv = dynamic_cast (tv); + TimeAxisView* tv = row[_columns.tv]; + RouteTimeAxisView *rtv = dynamic_cast (tv); - if (atv != 0 && atv->is_audio_track()){ + 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); - - if (atv != 0) { + RouteTimeAxisView *rtv = dynamic_cast (tv); + + 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 (Glib::ustring const & path_string) +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]; - 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_isolated (!rtv->route()->solo_isolated(), this); } } void -EditorRoutes::on_tv_solo_safe_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_safe (!atv->route()->solo_safe(), this); + if (rtv) { + rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this); } } @@ -361,12 +446,15 @@ EditorRoutes::redisplay () 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); @@ -398,7 +486,7 @@ EditorRoutes::route_deleted (Gtk::TreeModel::Path const &) } void -EditorRoutes::visible_changed (Glib::ustring const & path) +EditorRoutes::visible_changed (std::string const & path) { if (_session && _session->deletion_in_progress()) { return; @@ -438,7 +526,7 @@ EditorRoutes::routes_added (list routes) row[_columns.route] = (*x)->route (); row[_columns.is_track] = (boost::dynamic_pointer_cast ((*x)->route()) != 0); row[_columns.mute_state] = (*x)->route()->muted(); - row[_columns.solo_state] = (*x)->route()->soloed(); + 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; @@ -462,8 +550,14 @@ EditorRoutes::routes_added (list routes) t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); } + 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()); + } + (*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()); } @@ -797,6 +891,107 @@ EditorRoutes::hide_all_miditracks () 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 (5); // 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 EditorRoutes::button_press (GdkEventButton* ev) { @@ -837,8 +1032,16 @@ EditorRoutes::button_press (GdkEventButton* ev) } 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; } @@ -1039,8 +1242,21 @@ 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 (); } } @@ -1054,7 +1270,7 @@ EditorRoutes::update_mute_display () 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); } } @@ -1066,7 +1282,7 @@ EditorRoutes::update_solo_display (bool /* selfsoloed */) 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); } } @@ -1114,9 +1330,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; }