#include <cmath>
#include <cassert>
+#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"
#include "ardour_ui.h"
#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 "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 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)
+ , _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);
// Record enable toggle
CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
- 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->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));
TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
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(15);
+ 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 (0, ::get_icon("act-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->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));
// 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 (1, ::get_icon("solo-enabled"));
- solo_col_renderer->set_pixbuf (3, ::get_icon("soloed-by-others"));
+ 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(15);
+ 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("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(22);
+ 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("act-disabled"));
- solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
+ 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(22);
+ 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 (*mute_state_column);
_display.append_column (*solo_state_column);
_display.append_column (*solo_isolate_state_column);
_display.append_column (*solo_safe_state_column);
-
- 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);
-
+
+
+ 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_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");
- CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (5));
+ CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_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 (5);
+ TreeViewColumn* name_column = _display.get_column (_name_column);
assert (name_column);
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<CellRendererToggle*> (_display.get_column_cell_renderer (6));
+ CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_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<TreeViewColumn*> (_display.get_column (6));
+
+ TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_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<CellRendererToggle*> (_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<TreeViewColumn*> (_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);
_display.set_enable_search (false);
- Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
+ Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::sync_treeview_from_order_keys, this, _1), gui_context());
}
bool
if (name_editable) {
return true;
}
-
- /* arm counter so that ::selection_filter() will deny selecting anything for the
+
+ /* arm counter so that ::selection_filter() will deny selecting anything for the
next two attempts to change selection status.
*/
selection_countdown = 2;
}
}
+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<RouteTimeAxisView*> (tv);
+
+ if (rtv) {
+ boost::shared_ptr<MidiTrack> 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)
{
TimeAxisView *tv = row[_columns.tv];
RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
-
+
if (rtv != 0) {
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (rtv->route());
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (rtv->route());
if (Config->get_solo_control_is_listen_control()) {
- _session->set_listen (rl, !rtv->route()->listening(), Session::rt_cleanup);
+ _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::redisplay ()
{
- if (_no_redisplay || !_session) {
+ if (_no_redisplay || !_session || _session->deletion_in_progress()) {
return;
}
TreeModel::Children rows = _model->children();
TreeModel::Children::iterator i;
uint32_t position;
+
+ /* 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) {
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++;
}
-
/* 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.
_editor->reset_controls_layout_height (position);
_editor->reset_controls_layout_width ();
- _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
- _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
+ _editor->_full_canvas_height = position;
+ _editor->vertical_adjustment.set_upper (_editor->_full_canvas_height);
- if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
+ if ((_editor->vertical_adjustment.get_value() + _editor->_visible_canvas_height) > _editor->vertical_adjustment.get_upper()) {
/*
We're increasing the size of the canvas while the bottom is visible.
We scroll down to keep in step with the controls layout.
*/
- _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
- }
-
- if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
- _session->sync_order_keys (N_ ("editor"));
+ _editor->vertical_adjustment.set_value (_editor->_full_canvas_height - _editor->_visible_canvas_height);
}
}
void
EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
{
- 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;
+ /* 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
TimeAxisView* tv = (*iter)[_columns.tv];
if (tv) {
bool visible = (*iter)[_columns.visible];
- (*iter)[_columns.visible] = !visible;
+
+ if (tv->set_marked_for_display (!visible)) {
+ update_visibility ();
+ }
}
}
+}
- _redisplay_does_not_reset_order_keys = true;
- _session->set_remote_control_ids();
- redisplay ();
- _redisplay_does_not_reset_order_keys = false;
+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> route = row[_columns.route];
+ bool const active = row[_columns.active];
+ route->set_active (!active, this);
}
void
EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
{
TreeModel::Row row;
+ PBD::Unwinder<bool> at (_adding_routes, true);
- _redisplay_does_not_sync_order_keys = true;
suspend_redisplay ();
+ _display.set_model (Glib::RefPtr<ListStore>());
+
for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
+ boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
+
row = *(_model->append ());
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<Track> ((*x)->route()) != 0);
- 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 */
- 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;
}
- _ignore_reorder = 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;
boost::weak_ptr<Route> wr ((*x)->route());
- (*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());
+ (*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<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
if ((*x)->is_midi_track()) {
boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
+ t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
}
(*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_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_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*)
{
- ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
+ if (_adding_routes) {
+ return;
+ }
if (what == "track_height") {
/* Optional :make tracks change height while it happens, instead
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);
}
}
- _redisplay_does_not_sync_order_keys = false;
+ /* the deleted signal for the treeview/model will take
+ care of any updates.
+ */
}
void
}
}
+void
+EditorRoutes::update_active_display ()
+{
+ TreeModel::Children rows = _model->children();
+ TreeModel::Children::iterator i;
+
+ for (i = rows.begin(); i != rows.end(); ++i) {
+ boost::shared_ptr<Route> route = (*i)[_columns.route];
+ (*i)[_columns.active] = route->active ();
+ }
+}
+
void
EditorRoutes::update_visibility ()
{
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;
}
+ /* force route order keys catch up with visibility changes
+ */
+
+ sync_order_keys_from_treeview ();
+
resume_redisplay ();
}
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;
}
}
-
- redisplay ();
}
void
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;
}
}
-
- redisplay ();
}
void
-EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
+EditorRoutes::reset_remote_control_ids ()
{
- redisplay ();
+ if (Config->get_remote_model() != EditorOrdered || !_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> 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_from_order_key (EditorSort, 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 ();
+ }
}
-/** 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)
+EditorRoutes::sync_order_keys_from_treeview ()
{
- vector<int> neworder;
+ 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> route = (*ri)[_columns.route];
+ bool visible = (*ri)[_columns.visible];
+
+ uint32_t old_key = route->order_key (EditorSort);
+
+ if (order != old_key) {
+ route->set_order_key (EditorSort, order);
+
+ changed = true;
+ }
+
+ if ((Config->get_remote_model() == EditorOrdered) && !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_from_order_key (EditorSort, new_rid);
+ rid_change = true;
+ }
+
+ if (visible) {
+ rid++;
+ }
+
+ }
+
+ ++order;
+ }
+
+ if (changed) {
+ /* tell the world that we changed the editor sort keys */
+ _session->sync_order_keys (EditorSort);
+ }
+
+ if (rid_change) {
+ /* tell the world that we changed the remote control IDs */
+ _session->notify_remote_id_change ();
+ }
+}
+
+void
+EditorRoutes::sync_treeview_from_order_keys (RouteSortOrderKey src)
+{
+ /* Some route order key(s) for `src' has been changed, make sure that
+ we update out tree/list model and GUI to reflect the change.
+ */
- if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
+ if (!_session || _session->deletion_in_progress()) {
return;
}
- for (ri = rows.begin(); ri != rows.end(); ++ri) {
- neworder.push_back (0);
+ DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("editor sync model from order keys, src = %1\n", enum_2_string (src)));
+
+ if (src == MixerSort) {
+
+ if (!Config->get_sync_all_route_ordering()) {
+ /* mixer sort keys changed - we don't care */
+ return;
+ }
+
+ DEBUG_TRACE (DEBUG::OrderKeys, "reset editor order key to match mixer\n");
+
+ /* mixer sort keys were changed, update the editor sort
+ * keys since "sync mixer+editor order" is enabled.
+ */
+
+ boost::shared_ptr<RouteList> r = _session->get_routes ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->sync_order_keys (src);
+ }
}
+ /* 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.
+ */
+
+ vector<int> neworder;
+ TreeModel::Children rows = _model->children();
+ uint32_t old_order = 0;
bool changed = false;
- int order;
- for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
+ if (rows.empty()) {
+ return;
+ }
+
+ OrderKeySortedRoutes sorted_routes;
+
+ for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
boost::shared_ptr<Route> route = (*ri)[_columns.route];
+ sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key (EditorSort)));
+ }
- int old_key = order;
- int new_key = route->order_key (N_ ("editor"));
+ SortByNewDisplayOrder cmp;
- neworder[new_key] = old_key;
+ sort (sorted_routes.begin(), sorted_routes.end(), cmp);
+ neworder.assign (sorted_routes.size(), 0);
- if (new_key != old_key) {
+ uint32_t n = 0;
+
+ for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
+
+ neworder[n] = sr->old_display_order;
+
+ 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<bool> uw (_ignore_reorder, true);
_model->reorder (neworder);
- _redisplay_does_not_reset_order_keys = false;
}
-}
+ redisplay ();
+}
void
EditorRoutes::hide_all_tracks (bool /*with_select*/)
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 ();
}
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;
}
}
}
+ /* force route order keys catch up with visibility changes
+ */
+
+ sync_order_keys_from_treeview ();
+
resume_redisplay ();
}
if (name_editable) {
name_editable->editing_done ();
name_editable = 0;
- }
+ }
- col = _display.get_column (5); // select&focus on name column
+ 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);
break;
case 's':
- if (Config->get_solo_control_is_listen_control()) {
- _session->set_listen (rl, !rl->front()->listening(), Session::rt_cleanup);
- } else {
- _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
+ 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;
show_menu ();
return true;
}
-
+
+ TreeModel::Path path;
+ 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;
+ }
+
//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;
+ if (_editor->_full_canvas_height - y_pos < _editor->_visible_canvas_height){
+ y_pos = _editor->_full_canvas_height - _editor->_visible_canvas_height;
}
-
+
//Only scroll to if the track is visible
if(y_pos != -1){
_editor->reset_y_origin (y_pos);
}
}
-
+
return false;
}
struct EditorOrderRouteSorter {
bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> 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 (EditorSort) < b->order_key (EditorSort);
}
};
}
boost::shared_ptr<RouteList> routes = _session->get_routes();
- RouteList r (*routes);
- EditorOrderRouteSorter sorter;
-
- r.sort (sorter);
- _editor->handle_new_route (r);
-
- /* don't show master bus in a new session */
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
+ */
- for (i = rows.begin(); i != rows.end(); ++i) {
-
- TimeAxisView *tv = (*i)[_columns.tv];
- RouteTimeAxisView *rtv;
+ _editor->add_routes (*(routes.get()));
+
+ } else {
- if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
- if (rtv->route()->is_master()) {
- _display.get_selection()->unselect (i);
- }
- }
- }
+ /* existing session: sort a copy of the route list by
+ * editor-order and add its contents to the display.
+ */
- _no_redisplay = false;
- redisplay ();
+ RouteList r (*routes);
+ EditorOrderRouteSorter sorter;
+
+ 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<Gdk::DragContext>& context,
int x, int y,
}
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 (EditorSort);
+ neworder.push_back (order);
+ }
+
+#ifndef NDEBUG
+ DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
+ for (vector<int>::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<int>::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);
+}
- _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> route = (*i)[_columns.route];
+
+ if (boost::dynamic_pointer_cast<Track> (route)) {
+ boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
+
+ if (mt) {
+ (*i)[_columns.is_input_active] = mt->input_active();
+ }
+ }
+ }
}
void
} else {
(*i)[_columns.rec_state] = 0;
}
-
+
(*i)[_columns.name_editable] = !route->record_enabled ();
}
}
for (i = rows.begin(); i != rows.end(); ++i) {
boost::shared_ptr<Route> route = (*i)[_columns.route];
- (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
+ (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
}
}
for (i = rows.begin(); i != rows.end(); ++i) {
boost::shared_ptr<Route> route = (*i)[_columns.route];
- (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
+ (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
}
}
for (i = rows.begin(); i != rows.end(); ++i) {
boost::shared_ptr<Route> route = (*i)[_columns.route];
- (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
+ (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
}
}
for (i = rows.begin(); i != rows.end(); ++i) {
boost::shared_ptr<Route> route = (*i)[_columns.route];
- (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
+ (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
}
}
}
suspend_redisplay ();
-
+
TreeModel::Children rows = _model->children ();
for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
TimeAxisView* tv = (*i)[_columns.tv];
resume_redisplay ();
}
+