X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=gtk2_ardour%2Feditor_routes.cc;h=e0b562b4942f48682221437cbdc0dce0b1e99aad;hb=ae6b0b9f1ab02ab949eaf741b98e69bdab487e92;hp=e7e8d767951836915f183e7b028738d74ff32b8e;hpb=677bb36f5c012ac6d429a1d1fce0a726616160d4;p=ardour.git diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index e7e8d76795..e0b562b494 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2009 Paul Davis + Copyright (C) 2000-2009 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,7 +24,17 @@ #include #include -#include "ardour/diskstream.h" +#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" @@ -35,98 +45,414 @@ #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 "gtkmm2ext/cell_renderer_pixbuf_toggle.h" - #include "i18n.h" using namespace std; -using namespace sigc; using namespace ARDOUR; using namespace PBD; using namespace Gtk; 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) + : EditorComponent (e) + , _ignore_reorder (false) + , _no_redisplay (false) + , _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); _model = ListStore::create (_columns); _display.set_model (_model); - CellRendererPixbufToggle* rec_col_renderer = manage (new CellRendererPixbufToggle()); + // Record enable toggle + CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti()); - rec_col_renderer->set_active_pixbuf (::get_icon("record_normal_red")); - rec_col_renderer->set_inactive_pixbuf (::get_icon("record_disabled_grey")); + 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)); - rec_col_renderer->signal_toggled().connect (mem_fun (*this, &EditorRoutes::on_tv_rec_enable_toggled)); + TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer)); - Gtk::TreeViewColumn* rec_state_column = manage (new TreeViewColumn("Rec", *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("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 (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)); + + 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 (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(column_width); + + // Solo isolate toggle + CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti()); + + 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(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->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(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 (_("Show"), _columns.visible); - _display.append_column (_("Name"), _columns.text); - - _display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0)); - _display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1)); - _display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2)); - + _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); + + + 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_NONE); + _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"); - CellRendererToggle* visible_cell = dynamic_cast(_display.get_column_cell_renderer (1)); - + 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); + + 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)); + + // 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); + + CellRendererToggle* active_cell = dynamic_cast (_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 (_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); + + _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, boost::bind (&EditorRoutes::sync_treeview_from_order_keys, this), 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; + } - _model->signal_row_deleted().connect (mem_fun (*this, &EditorRoutes::route_deleted)); - _model->signal_row_changed().connect (mem_fun (*this, &EditorRoutes::changed)); - _model->signal_rows_reordered().connect (mem_fun (*this, &EditorRoutes::reordered)); - _display.signal_button_press_event().connect (mem_fun (*this, &EditorRoutes::button_press), false); + name_editable = 0; - Route::SyncOrderKeys.connect (mem_fun (*this, &EditorRoutes::sync_order_keys)); + /* 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 (); + + 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) +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 (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)); + + TimeAxisView* tv = row[_columns.tv]; + RouteTimeAxisView *rtv = dynamic_cast (tv); + + if (rtv && rtv->track()) { + boost::shared_ptr rl (new RouteList); + rl->push_back (rtv->route()); + _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup); + } +} + +void +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]; + RouteTimeAxisView *rtv = dynamic_cast (tv); + + if (rtv != 0) { + boost::shared_ptr rl (new RouteList); + rl->push_back (rtv->route()); + _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup); + } +} + +void +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]; + RouteTimeAxisView* rtv = dynamic_cast (tv); + + if (rtv != 0) { + boost::shared_ptr rl (new RouteList); + 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_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->is_audio_track()){ - atv->reversibly_apply_track_boolean ("rec-enable change", &Track::set_record_enable, !atv->track()->record_enabled(), this); + if (rtv) { + rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this); } } @@ -137,17 +463,19 @@ EditorRoutes::build_menu () using namespace Gtk; _menu = new Menu; - + MenuList& items = _menu->items(); _menu->set_name ("ArdourContextMenu"); - items.push_back (MenuElem (_("Show All"), mem_fun (*this, &EditorRoutes::show_all_routes))); - items.push_back (MenuElem (_("Hide All"), mem_fun (*this, &EditorRoutes::hide_all_routes))); - items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun (*this, &EditorRoutes::show_all_audiotracks))); - items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun (*this, &EditorRoutes::hide_all_audiotracks))); - items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun (*this, &EditorRoutes::show_all_audiobus))); - items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun (*this, &EditorRoutes::hide_all_audiobus))); - + items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes))); + items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes))); + items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks))); + 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))); } void @@ -163,14 +491,18 @@ EditorRoutes::show_menu () void EditorRoutes::redisplay () { + if (_no_redisplay || !_session || _session->deletion_in_progress()) { + return; + } + TreeModel::Children rows = _model->children(); TreeModel::Children::iterator i; uint32_t position; - int n; - if (_no_redisplay) { - return; - } + /* 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) { TimeAxisView *tv = (*i)[_columns.tv]; @@ -181,27 +513,16 @@ 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++; } @@ -209,95 +530,180 @@ EditorRoutes::redisplay () 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)); - Glib::signal_idle().connect (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); if ((_editor->vertical_adjustment.get_value() + _editor->_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")); - } } void EditorRoutes::route_deleted (Gtk::TreeModel::Path const &) { - /* 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 -EditorRoutes::changed (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &) +EditorRoutes::visible_changed (std::string const & path) { - /* never reset order keys because of a property change */ - _redisplay_does_not_reset_order_keys = true; - _session->set_remote_control_ids(); - redisplay (); - _redisplay_does_not_reset_order_keys = false; + if (_session && _session->deletion_in_progress()) { + return; + } + + TreeIter iter; + + if ((iter = _model->get_iter (path))) { + TimeAxisView* tv = (*iter)[_columns.tv]; + if (tv) { + bool visible = (*iter)[_columns.visible]; + + if (tv->set_marked_for_display (!visible)) { + update_visibility (); + } + } + } +} + +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 = row[_columns.route]; + bool const active = row[_columns.active]; + route->set_active (!active, this); } void EditorRoutes::routes_added (list routes) { - TreeModel::Row row; + PBD::Unwinder at (_adding_routes, true); + + 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 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(); + } - _redisplay_does_not_sync_order_keys = true; suspend_redisplay (); + _display.set_model (Glib::RefPtr()); + for (list::iterator x = routes.begin(); x != routes.end(); ++x) { - row = *(_model->append ()); + boost::shared_ptr midi_trk = boost::dynamic_pointer_cast ((*x)->route()); + + TreeModel::Row row = *(_model->insert (insert_iter)); 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 ((*x)->route()) != 0); - _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; + } + + 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; + + if (!from_scratch) { + _editor->selection->add(*x); } - - _ignore_reorder = false; boost::weak_ptr wr ((*x)->route()); - (*x)->route()->gui_changed.connect (mem_fun (*this, &EditorRoutes::handle_gui_changes)); - (*x)->route()->NameChanged.connect (bind (mem_fun (*this, &EditorRoutes::route_name_changed), wr)); - (*x)->GoingAway.connect (bind (mem_fun (*this, &EditorRoutes::route_removed), *x)); + + (*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 t = boost::dynamic_pointer_cast ((*x)->route()); - t->diskstream()->RecordEnableChanged.connect (mem_fun (*this, &EditorRoutes::update_rec_display)); + 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()); + 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()); + (*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 (); + update_mute_display (); + 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 *src) +EditorRoutes::handle_gui_changes (string const & what, void*) { - ENSURE_GUI_THREAD (bind (mem_fun(*this, &EditorRoutes::handle_gui_changes), what, src)); + if (_adding_routes) { + return; + } if (what == "track_height") { - /* Optional :make tracks change height while it happens, instead + /* Optional :make tracks change height while it happens, instead of on first-idle */ //update_canvas_now (); @@ -312,17 +718,11 @@ EditorRoutes::handle_gui_changes (string const & what, void *src) void EditorRoutes::route_removed (TimeAxisView *tv) { - ENSURE_GUI_THREAD (bind (mem_fun(*this, &EditorRoutes::route_removed), tv)); + ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, 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); @@ -330,29 +730,48 @@ 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 -EditorRoutes::route_name_changed (boost::weak_ptr r) +EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr r) { - ENSURE_GUI_THREAD (bind (mem_fun (*this, &EditorRoutes::route_name_changed), 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; } - + TreeModel::Children rows = _model->children(); TreeModel::Children::iterator i; - + for (i = rows.begin(); i != rows.end(); ++i) { boost::shared_ptr t = (*i)[_columns.route]; if (t == route) { (*i)[_columns.text] = route->name(); break; } - } + } +} + +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 = (*i)[_columns.route]; + (*i)[_columns.active] = route->active (); + } } void @@ -361,92 +780,229 @@ EditorRoutes::update_visibility () TreeModel::Children rows = _model->children(); TreeModel::Children::iterator i; - suspend_redisplay (); + suspend_redisplay (); + + for (i = rows.begin(); i != rows.end(); ++i) { + TimeAxisView *tv = (*i)[_columns.tv]; + (*i)[_columns.visible] = tv->marked_for_display (); + } + + /* force route order keys catch up with visibility changes + */ + + sync_order_keys_from_treeview (); + + resume_redisplay (); +} + +void +EditorRoutes::hide_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 (false); + (*i)[_columns.visible] = false; + redisplay (); + break; + } + } +} + +void +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; + } + } +} + +void +EditorRoutes::reset_remote_control_ids () +{ + if (Config->get_remote_model() == UserOrdered || !_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 = (*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_explicit (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 (); + } +} + + +void +EditorRoutes::sync_order_keys_from_treeview () +{ + 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 = (*ri)[_columns.route]; + bool visible = (*ri)[_columns.visible]; + + uint32_t old_key = route->order_key (); + + if (order != old_key) { + route->set_order_key (order); + + changed = true; + } + + if ((Config->get_remote_model() == MixerOrdered) && !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_explicit (new_rid); + rid_change = true; + } + + if (visible) { + rid++; + } + + } - 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; + ++order; + } + + if (changed) { + /* tell the world that we changed the editor sort keys */ + _session->sync_order_keys (); } - resume_redisplay (); + if (rid_change) { + /* tell the world that we changed the remote control IDs */ + _session->notify_remote_id_change (); + } } void -EditorRoutes::hide_track_in_display (TimeAxisView& tv) +EditorRoutes::sync_treeview_from_order_keys () { - TreeModel::Children rows = _model->children(); - TreeModel::Children::iterator i; + /* Some route order key(s) have been changed, make sure that + we update out tree/list model and GUI to reflect the change. + */ - for (i = rows.begin(); i != rows.end(); ++i) { - if ((*i)[_columns.tv] == &tv) { - (*i)[_columns.visible] = false; - break; - } + if (_ignore_reorder || !_session || _session->deletion_in_progress()) { + return; } -} -void -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) { - (*i)[_columns.visible] = true; - break; - } - } -} + DEBUG_TRACE (DEBUG::OrderKeys, "editor sync model from order keys.\n"); -void -EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/) -{ - redisplay (); -} + /* 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. + */ -/** 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) -{ vector neworder; TreeModel::Children rows = _model->children(); - TreeModel::Children::iterator ri; + uint32_t old_order = 0; + bool changed = false; - if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & Session::Loading) || rows.empty()) { + if (rows.empty()) { return; } - for (ri = rows.begin(); ri != rows.end(); ++ri) { - neworder.push_back (0); + OrderKeySortedRoutes sorted_routes; + + for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) { + boost::shared_ptr route = (*ri)[_columns.route]; + sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key ())); } - bool changed = false; - int order; + SortByNewDisplayOrder cmp; - for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) { - boost::shared_ptr route = (*ri)[_columns.route]; + sort (sorted_routes.begin(), sorted_routes.end(), cmp); + neworder.assign (sorted_routes.size(), 0); - int old_key = order; - int new_key = route->order_key (N_ ("editor")); + uint32_t n = 0; + + for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) { - neworder[new_key] = old_key; + neworder[n] = sr->old_display_order; - if (new_key != old_key) { + 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 uw (_ignore_reorder, true); _model->reorder (neworder); - _redisplay_does_not_reset_order_keys = false; } -} + redisplay (); +} void EditorRoutes::hide_all_tracks (bool /*with_select*/) @@ -457,14 +1013,14 @@ EditorRoutes::hide_all_tracks (bool /*with_select*/) suspend_redisplay (); for (i = rows.begin(); i != rows.end(); ++i) { - + TreeModel::Row row = (*i); TimeAxisView *tv = row[_columns.tv]; if (tv == 0) { continue; } - + row[_columns.visible] = false; } @@ -493,15 +1049,21 @@ EditorRoutes::set_all_tracks_visibility (bool yn) if (tv == 0) { 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 (); } 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; @@ -509,9 +1071,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; @@ -528,7 +1093,7 @@ EditorRoutes::set_all_audio_visibility (int tracks, bool yn) (*i)[_columns.visible] = yn; } break; - + case 2: if (!atv->is_audio_track()) { (*i)[_columns.visible] = yn; @@ -536,8 +1101,26 @@ 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; + } + } } + /* force route order keys catch up with visibility changes + */ + + sync_order_keys_from_treeview (); + resume_redisplay (); } @@ -553,26 +1136,140 @@ 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_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 (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; + + 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) { - set_all_audio_visibility (1, false); + 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 @@ -583,108 +1280,111 @@ EditorRoutes::button_press (GdkEventButton* ev) return true; } - TreeIter iter; TreeModel::Path path; - TreeViewColumn* column; - int cellx; - int celly; - - if (!_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) { - return false; + 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; } - switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) { + //Scroll editor canvas to selected track + if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { - case 0: - /* allow normal processing to occur */ - return false; - case 1: - if ((iter = _model->get_iter (path))) { - TimeAxisView* tv = (*iter)[_columns.tv]; - if (tv) { - bool visible = (*iter)[_columns.visible]; - (*iter)[_columns.visible] = !visible; - } - } - return true; + // Get the model row. + Gtk::TreeModel::Row row = *_model->get_iter (path); - case 2: - /* allow normal processing to occur */ - return false; + 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; + } - default: - break; + //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; } struct EditorOrderRouteSorter { bool operator() (boost::shared_ptr a, boost::shared_ptr 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 () < b->order_key (); } }; void EditorRoutes::initial_display () { - 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 */ + if (!_session) { + resume_redisplay (); + return; + } + + boost::shared_ptr routes = _session->get_routes(); 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 + */ + + _editor->add_routes (*(routes.get())); - for (i = rows.begin(); i != rows.end(); ++i) { - TimeAxisView *tv = (*i)[_columns.tv]; - RouteTimeAxisView *rtv; - - if ((rtv = dynamic_cast(tv)) != 0) { - if (rtv->route()->is_master()) { - _display.get_selection()->unselect (i); - } - } - } + } 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; - _no_redisplay = false; - redisplay (); - } + 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& context, - int x, int y, + int x, int y, const SelectionData& data, guint info, guint time) { @@ -692,7 +1392,7 @@ EditorRoutes::display_drag_data_received (const RefPtr& contex _display.on_drag_data_received (context, x, y, data, info, time); return; } - + context->drag_finish (true, false, time); } @@ -718,14 +1418,14 @@ EditorRoutes::move_selected_tracks (bool up) list::iterator trailing; list::iterator leading; - + if (up) { - + trailing = view_routes.begin(); leading = view_routes.begin(); - + ++leading; - + while (leading != view_routes.end()) { if (_editor->selection->selected (leading->first)) { view_routes.insert (trailing, ViewRoute (leading->first, leading->second)); @@ -763,7 +1463,7 @@ EditorRoutes::move_selected_tracks (bool up) tmp++; view_routes.insert (tmp, ViewRoute (leading->first, leading->second)); - + /* can't use iter = cont.erase (iter); form here, because we need iter to move backwards. */ @@ -782,7 +1482,7 @@ EditorRoutes::move_selected_tracks (bool up) } view_routes.erase (leading); - + if (done) { break; } @@ -800,12 +1500,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 (); + neworder.push_back (order); + } + +#ifndef NDEBUG + DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n"); + for (vector::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::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); +} + +void +EditorRoutes::update_input_active_display () +{ + TreeModel::Children rows = _model->children(); + TreeModel::Children::iterator i; - _session->sync_order_keys (N_ ("editor")); + 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 @@ -813,18 +1546,75 @@ EditorRoutes::update_rec_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)) { + if (boost::dynamic_pointer_cast (route)) { + boost::shared_ptr mt = boost::dynamic_pointer_cast (route); - if (route->record_enabled()){ - (*i)[_columns.rec_enabled] = true; + 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_enabled] = false; + (*i)[_columns.rec_state] = 0; } - } + + (*i)[_columns.name_editable] = !route->record_enabled (); + } + } +} + +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 = (*i)[_columns.route]; + (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route); + } +} + +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 = (*i)[_columns.route]; + (*i)[_columns.solo_state] = RouteUI::solo_active_state (route); + } +} + +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 = (*i)[_columns.route]; + (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0; + } +} + +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_active_state (route) ? 1 : 0; } } @@ -846,3 +1636,66 @@ EditorRoutes::clear () _model->clear (); _display.set_model (_model); } + +void +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; + } + + boost::shared_ptr route = (*iter)[_columns.route]; + + if (route && route->name() != new_text) { + route->set_name (new_text); + } +} + +void +EditorRoutes::solo_changed_so_update_mute () +{ + update_mute_display (); +} + +void +EditorRoutes::show_tracks_with_regions_at_playhead () +{ + boost::shared_ptr const r = _session->get_routes_with_regions_at (_session->transport_frame ()); + + set show; + for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) { + TimeAxisView* tav = _editor->axis_view_from_route (*i); + if (tav) { + show.insert (tav); + } + } + + suspend_redisplay (); + + 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 (); +} +