2 Copyright (C) 2000-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "ardour/session.h"
31 #include "ardour_ui.h"
32 #include "audio_time_axis.h"
33 #include "midi_time_axis.h"
34 #include "mixer_strip.h"
35 #include "gui_thread.h"
38 #include "editor_group_tabs.h"
39 #include "editor_routes.h"
41 #include "pbd/unknown_type.h"
43 #include "ardour/route.h"
44 #include "ardour/midi_track.h"
46 #include "gtkmm2ext/cell_renderer_pixbuf_multi.h"
47 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
48 #include "gtkmm2ext/treeutils.h"
53 using namespace ARDOUR;
56 using namespace Gtkmm2ext;
58 using Gtkmm2ext::Keyboard;
66 EditorRoutes::EditorRoutes (Editor* e)
68 , _ignore_reorder (false)
69 , _no_redisplay (false)
70 , _redisplay_does_not_sync_order_keys (false)
71 , _redisplay_does_not_reset_order_keys (false)
74 , selection_countdown (0)
77 static const int column_width = 22;
79 _scroller.add (_display);
80 _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
82 _model = ListStore::create (_columns);
83 _display.set_model (_model);
85 // Record enable toggle
86 CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
88 rec_col_renderer->set_pixbuf (0, ::get_icon("record-normal-disabled"));
89 rec_col_renderer->set_pixbuf (1, ::get_icon("record-normal-in-progress"));
90 rec_col_renderer->set_pixbuf (2, ::get_icon("record-normal-enabled"));
91 rec_col_renderer->set_pixbuf (3, ::get_icon("record-step"));
92 rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
94 TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
96 rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
97 rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
99 rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
100 rec_state_column->set_alignment(ALIGN_CENTER);
101 rec_state_column->set_expand(false);
102 rec_state_column->set_fixed_width(column_width);
106 CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti());
107 input_active_col_renderer->set_pixbuf (0, ::get_icon("midi-input-inactive"));
108 input_active_col_renderer->set_pixbuf (1, ::get_icon("midi-input-active"));
109 input_active_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed));
111 TreeViewColumn* input_active_column = manage (new TreeViewColumn ("I", *input_active_col_renderer));
113 input_active_column->add_attribute(input_active_col_renderer->property_state(), _columns.is_input_active);
114 input_active_column->add_attribute (input_active_col_renderer->property_visible(), _columns.is_midi);
116 input_active_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
117 input_active_column->set_alignment(ALIGN_CENTER);
118 input_active_column->set_expand(false);
119 input_active_column->set_fixed_width(column_width);
121 // Mute enable toggle
122 CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
124 mute_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("mute-disabled"));
125 mute_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("muted-by-others"));
126 mute_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("mute-enabled"));
127 mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
129 TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
131 mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
132 mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
133 mute_state_column->set_alignment(ALIGN_CENTER);
134 mute_state_column->set_expand(false);
135 mute_state_column->set_fixed_width(15);
137 // Solo enable toggle
138 CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
140 solo_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("solo-disabled"));
141 solo_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("solo-enabled"));
142 solo_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("soloed-by-others"));
143 solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
145 TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
147 solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
148 solo_state_column->add_attribute(solo_col_renderer->property_visible(), _columns.solo_visible);
149 solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
150 solo_state_column->set_alignment(ALIGN_CENTER);
151 solo_state_column->set_expand(false);
152 solo_state_column->set_fixed_width(column_width);
154 // Solo isolate toggle
155 CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
157 solo_iso_renderer->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
158 solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
159 solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
161 TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
163 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
164 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_visible(), _columns.solo_visible);
165 solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
166 solo_isolate_state_column->set_alignment(ALIGN_CENTER);
167 solo_isolate_state_column->set_expand(false);
168 solo_isolate_state_column->set_fixed_width(column_width);
171 CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
173 solo_safe_renderer->set_pixbuf (0, ::get_icon("solo-safe-disabled"));
174 solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-safe-enabled"));
175 solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
177 TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
178 solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
179 solo_safe_state_column->add_attribute(solo_safe_renderer->property_visible(), _columns.solo_visible);
180 solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
181 solo_safe_state_column->set_alignment(ALIGN_CENTER);
182 solo_safe_state_column->set_expand(false);
183 solo_safe_state_column->set_fixed_width(column_width);
185 _display.append_column (*input_active_column);
186 _display.append_column (*rec_state_column);
187 _display.append_column (*mute_state_column);
188 _display.append_column (*solo_state_column);
189 _display.append_column (*solo_isolate_state_column);
190 _display.append_column (*solo_safe_state_column);
192 _name_column = _display.append_column ("", _columns.text) - 1;
193 _visible_column = _display.append_column ("", _columns.visible) - 1;
194 _active_column = _display.append_column ("", _columns.active) - 1;
200 { 0, _("I"), _("MIDI input enabled") },
201 { 1, _("R"), _("Record enabled") },
202 { 2, _("M"), _("Muted") },
203 { 3, _("S"), _("Soloed") },
204 { 4, _("SI"), _("Solo Isolated") },
205 { 5, _("SS"), _("Solo Safe (Locked)") },
206 { 6, _("Name"), _("Track/Bus Name") },
207 { 7, _("V"), _("Track/Bus visible ?") },
208 { 8, _("A"), _("Track/Bus active ?") },
212 for (int i = 0; ci[i].index >= 0; ++i) {
213 col = _display.get_column (ci[i].index);
214 l = manage (new Label (ci[i].label));
215 ARDOUR_UI::instance()->set_tip (*l, ci[i].tooltip);
216 col->set_widget (*l);
220 _display.set_headers_visible (true);
221 _display.get_selection()->set_mode (SELECTION_SINGLE);
222 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
223 _display.set_reorderable (true);
224 _display.set_rules_hint (true);
225 _display.set_size_request (100, -1);
226 _display.add_object_drag (_columns.route.index(), "routes");
228 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
231 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
233 TreeViewColumn* name_column = _display.get_column (_name_column);
235 assert (name_column);
237 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
238 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
239 name_column->set_expand(true);
240 name_column->set_min_width(50);
242 name_cell->property_editable() = true;
243 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
245 // Set the visible column cell renderer to radio toggle
246 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
248 visible_cell->property_activatable() = true;
249 visible_cell->property_radio() = false;
250 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
252 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
253 visible_col->set_expand(false);
254 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
255 visible_col->set_fixed_width(30);
256 visible_col->set_alignment(ALIGN_CENTER);
258 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
260 active_cell->property_activatable() = true;
261 active_cell->property_radio() = false;
262 active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
264 TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
265 active_col->set_expand (false);
266 active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
267 active_col->set_fixed_width (30);
268 active_col->set_alignment (ALIGN_CENTER);
270 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
271 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
273 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
274 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
276 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
277 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
279 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
280 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
282 _display.set_enable_search (false);
284 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
288 EditorRoutes::focus_in (GdkEventFocus*)
290 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
293 old_focus = win->get_focus ();
300 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
305 EditorRoutes::focus_out (GdkEventFocus*)
308 old_focus->grab_focus ();
316 EditorRoutes::enter_notify (GdkEventCrossing*)
322 /* arm counter so that ::selection_filter() will deny selecting anything for the
323 next two attempts to change selection status.
325 selection_countdown = 2;
326 _scroller.grab_focus ();
327 Keyboard::magic_widget_grab_focus ();
332 EditorRoutes::leave_notify (GdkEventCrossing*)
334 selection_countdown = 0;
337 old_focus->grab_focus ();
341 Keyboard::magic_widget_drop_focus ();
346 EditorRoutes::set_session (Session* s)
348 SessionHandlePtr::set_session (s);
353 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
354 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
359 EditorRoutes::on_input_active_changed (std::string const & path_string)
361 // Get the model row that has been toggled.
362 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
364 TimeAxisView* tv = row[_columns.tv];
365 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
368 boost::shared_ptr<MidiTrack> mt;
369 mt = rtv->midi_track();
371 mt->set_input_active (!mt->input_active());
377 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
379 // Get the model row that has been toggled.
380 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
382 TimeAxisView* tv = row[_columns.tv];
383 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
385 if (rtv && rtv->track()) {
386 boost::shared_ptr<RouteList> rl (new RouteList);
387 rl->push_back (rtv->route());
388 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
393 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
395 // Get the model row that has been toggled.
396 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
398 TimeAxisView *tv = row[_columns.tv];
399 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
402 boost::shared_ptr<RouteList> rl (new RouteList);
403 rl->push_back (rtv->route());
404 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
409 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
411 // Get the model row that has been toggled.
412 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
414 TimeAxisView *tv = row[_columns.tv];
415 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
418 boost::shared_ptr<RouteList> rl (new RouteList);
419 rl->push_back (rtv->route());
420 if (Config->get_solo_control_is_listen_control()) {
421 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
423 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
429 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
431 // Get the model row that has been toggled.
432 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
434 TimeAxisView *tv = row[_columns.tv];
435 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
438 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
443 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
445 // Get the model row that has been toggled.
446 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
448 TimeAxisView *tv = row[_columns.tv];
449 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
452 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
457 EditorRoutes::build_menu ()
459 using namespace Menu_Helpers;
464 MenuList& items = _menu->items();
465 _menu->set_name ("ArdourContextMenu");
467 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
468 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
469 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
470 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
471 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
472 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
473 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
474 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
475 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
479 EditorRoutes::show_menu ()
485 _menu->popup (1, gtk_get_current_event_time());
489 EditorRoutes::redisplay ()
491 if (_no_redisplay || !_session) {
495 TreeModel::Children rows = _model->children();
496 TreeModel::Children::iterator i;
499 /* n will be the count of tracks plus children (updated by TimeAxisView::show_at),
500 so we will use that to know where to put things.
504 /* Order keys must not take children into account, so use a separate counter
509 for (n = 0, order_key = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
510 TimeAxisView *tv = (*i)[_columns.tv];
511 boost::shared_ptr<Route> route = (*i)[_columns.route];
514 // just a "title" row
518 if (!_redisplay_does_not_reset_order_keys) {
519 /* this reorder is caused by user action, so reassign sort order keys
522 route->set_order_key (N_ ("editor"), order_key);
525 bool visible = tv->marked_for_display ();
527 /* show or hide the TimeAxisView */
529 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
530 tv->clip_to_viewport ();
539 /* whenever we go idle, update the track view list to reflect the new order.
540 we can't do this here, because we could mess up something that is traversing
541 the track order and has caused a redisplay of the list.
543 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
545 _editor->reset_controls_layout_height (position);
546 _editor->reset_controls_layout_width ();
547 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
548 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
550 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
552 We're increasing the size of the canvas while the bottom is visible.
553 We scroll down to keep in step with the controls layout.
555 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
558 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
559 _session->sync_order_keys (N_ ("editor"));
564 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
566 if (!_session || _session->deletion_in_progress()) {
570 /* this could require an order reset & sync */
571 _session->set_remote_control_ids();
572 _ignore_reorder = true;
574 _ignore_reorder = false;
578 EditorRoutes::visible_changed (std::string const & path)
580 if (_session && _session->deletion_in_progress()) {
586 if ((iter = _model->get_iter (path))) {
587 TimeAxisView* tv = (*iter)[_columns.tv];
589 bool visible = (*iter)[_columns.visible];
591 if (tv->set_marked_for_display (!visible)) {
592 _redisplay_does_not_reset_order_keys = true;
593 _session->set_remote_control_ids();
594 update_visibility ();
596 _redisplay_does_not_reset_order_keys = false;
603 EditorRoutes::active_changed (std::string const & path)
605 if (_session && _session->deletion_in_progress ()) {
609 Gtk::TreeModel::Row row = *_model->get_iter (path);
610 boost::shared_ptr<Route> route = row[_columns.route];
611 bool const active = row[_columns.active];
612 route->set_active (!active, this);
616 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
620 _redisplay_does_not_sync_order_keys = true;
621 suspend_redisplay ();
623 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
625 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
627 row = *(_model->append ());
629 row[_columns.text] = (*x)->route()->name();
630 row[_columns.visible] = (*x)->marked_for_display();
631 row[_columns.active] = (*x)->route()->active ();
632 row[_columns.tv] = *x;
633 row[_columns.route] = (*x)->route ();
634 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
637 row[_columns.is_input_active] = midi_trk->input_active ();
638 row[_columns.is_midi] = true;
640 row[_columns.is_input_active] = false;
641 row[_columns.is_midi] = false;
644 row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
645 row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
646 row[_columns.solo_visible] = !(*x)->route()->is_master ();
647 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
648 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
649 row[_columns.name_editable] = true;
651 _ignore_reorder = true;
653 /* added a new fresh one at the end */
654 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
655 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
658 _ignore_reorder = false;
660 boost::weak_ptr<Route> wr ((*x)->route());
662 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
663 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
665 if ((*x)->is_track()) {
666 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
667 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
670 if ((*x)->is_midi_track()) {
671 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
672 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
673 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
676 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
677 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
678 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
679 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
680 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
681 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
684 update_rec_display ();
685 update_mute_display ();
686 update_solo_display (true);
687 update_solo_isolate_display ();
688 update_solo_safe_display ();
689 update_input_active_display ();
690 update_active_display ();
692 _redisplay_does_not_sync_order_keys = false;
696 EditorRoutes::handle_gui_changes (string const & what, void*)
698 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
700 if (what == "track_height") {
701 /* Optional :make tracks change height while it happens, instead
704 //update_canvas_now ();
708 if (what == "visible_tracks") {
714 EditorRoutes::route_removed (TimeAxisView *tv)
716 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
718 TreeModel::Children rows = _model->children();
719 TreeModel::Children::iterator ri;
721 /* the core model has changed, there is no need to sync
725 _redisplay_does_not_sync_order_keys = true;
727 for (ri = rows.begin(); ri != rows.end(); ++ri) {
728 if ((*ri)[_columns.tv] == tv) {
734 _redisplay_does_not_sync_order_keys = false;
738 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
740 if (!what_changed.contains (ARDOUR::Properties::name)) {
744 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
746 boost::shared_ptr<Route> route = r.lock ();
752 TreeModel::Children rows = _model->children();
753 TreeModel::Children::iterator i;
755 for (i = rows.begin(); i != rows.end(); ++i) {
756 boost::shared_ptr<Route> t = (*i)[_columns.route];
758 (*i)[_columns.text] = route->name();
765 EditorRoutes::update_active_display ()
767 TreeModel::Children rows = _model->children();
768 TreeModel::Children::iterator i;
770 for (i = rows.begin(); i != rows.end(); ++i) {
771 boost::shared_ptr<Route> route = (*i)[_columns.route];
772 (*i)[_columns.active] = route->active ();
777 EditorRoutes::update_visibility ()
779 TreeModel::Children rows = _model->children();
780 TreeModel::Children::iterator i;
782 suspend_redisplay ();
784 for (i = rows.begin(); i != rows.end(); ++i) {
785 TimeAxisView *tv = (*i)[_columns.tv];
786 (*i)[_columns.visible] = tv->marked_for_display ();
793 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
795 TreeModel::Children rows = _model->children();
796 TreeModel::Children::iterator i;
798 for (i = rows.begin(); i != rows.end(); ++i) {
799 if ((*i)[_columns.tv] == &tv) {
800 tv.set_marked_for_display (false);
801 (*i)[_columns.visible] = false;
809 EditorRoutes::show_track_in_display (TimeAxisView& tv)
811 TreeModel::Children rows = _model->children();
812 TreeModel::Children::iterator i;
815 for (i = rows.begin(); i != rows.end(); ++i) {
816 if ((*i)[_columns.tv] == &tv) {
817 tv.set_marked_for_display (true);
818 (*i)[_columns.visible] = true;
826 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
831 /** If src != "editor", take editor order keys from each route and use them to rearrange the
832 * route list so that the visual arrangement of routes matches the order keys from the routes.
835 EditorRoutes::sync_order_keys (string const & src)
837 map<int, int> new_order;
838 TreeModel::Children rows = _model->children();
839 TreeModel::Children::iterator ri;
841 if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
845 bool changed = false;
848 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
849 boost::shared_ptr<Route> route = (*ri)[_columns.route];
851 int const old_key = order;
852 int const new_key = route->order_key (N_ ("editor"));
854 new_order[new_key] = old_key;
856 if (new_key != old_key) {
862 _redisplay_does_not_reset_order_keys = true;
864 /* `compact' new_order into a vector */
866 for (map<int, int>::const_iterator i = new_order.begin(); i != new_order.end(); ++i) {
867 co.push_back (i->second);
870 assert (co.size() == _model->children().size ());
872 _model->reorder (co);
873 _redisplay_does_not_reset_order_keys = false;
879 EditorRoutes::hide_all_tracks (bool /*with_select*/)
881 TreeModel::Children rows = _model->children();
882 TreeModel::Children::iterator i;
884 suspend_redisplay ();
886 for (i = rows.begin(); i != rows.end(); ++i) {
888 TreeModel::Row row = (*i);
889 TimeAxisView *tv = row[_columns.tv];
895 row[_columns.visible] = false;
900 /* XXX this seems like a hack and half, but its not clear where to put this
904 //reset_scrolling_region ();
908 EditorRoutes::set_all_tracks_visibility (bool yn)
910 TreeModel::Children rows = _model->children();
911 TreeModel::Children::iterator i;
913 suspend_redisplay ();
915 for (i = rows.begin(); i != rows.end(); ++i) {
917 TreeModel::Row row = (*i);
918 TimeAxisView* tv = row[_columns.tv];
924 (*i)[_columns.visible] = yn;
931 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
933 TreeModel::Children rows = _model->children();
934 TreeModel::Children::iterator i;
936 suspend_redisplay ();
938 for (i = rows.begin(); i != rows.end(); ++i) {
940 TreeModel::Row row = (*i);
941 TimeAxisView* tv = row[_columns.tv];
943 AudioTimeAxisView* atv;
944 MidiTimeAxisView* mtv;
950 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
953 (*i)[_columns.visible] = yn;
957 if (atv->is_audio_track()) {
958 (*i)[_columns.visible] = yn;
963 if (!atv->is_audio_track()) {
964 (*i)[_columns.visible] = yn;
969 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
972 (*i)[_columns.visible] = yn;
976 if (mtv->is_midi_track()) {
977 (*i)[_columns.visible] = yn;
988 EditorRoutes::hide_all_routes ()
990 set_all_tracks_visibility (false);
994 EditorRoutes::show_all_routes ()
996 set_all_tracks_visibility (true);
1000 EditorRoutes::show_all_audiotracks()
1002 set_all_audio_midi_visibility (1, true);
1005 EditorRoutes::hide_all_audiotracks ()
1007 set_all_audio_midi_visibility (1, false);
1011 EditorRoutes::show_all_audiobus ()
1013 set_all_audio_midi_visibility (2, true);
1016 EditorRoutes::hide_all_audiobus ()
1018 set_all_audio_midi_visibility (2, false);
1022 EditorRoutes::show_all_miditracks()
1024 set_all_audio_midi_visibility (3, true);
1027 EditorRoutes::hide_all_miditracks ()
1029 set_all_audio_midi_visibility (3, false);
1033 EditorRoutes::key_press (GdkEventKey* ev)
1035 TreeViewColumn *col;
1036 boost::shared_ptr<RouteList> rl (new RouteList);
1039 switch (ev->keyval) {
1041 case GDK_ISO_Left_Tab:
1043 /* If we appear to be editing something, leave that cleanly and appropriately.
1045 if (name_editable) {
1046 name_editable->editing_done ();
1050 col = _display.get_column (_name_column); // select&focus on name column
1052 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1053 treeview_select_previous (_display, _model, col);
1055 treeview_select_next (_display, _model, col);
1062 if (get_relevant_routes (rl)) {
1063 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1069 if (Config->get_solo_control_is_listen_control()) {
1070 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1072 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1078 if (get_relevant_routes (rl)) {
1079 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1091 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1094 RouteTimeAxisView* rtv;
1095 RefPtr<TreeSelection> selection = _display.get_selection();
1099 if (selection->count_selected_rows() != 0) {
1103 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1104 iter = selection->get_selected (tm);
1107 /* use mouse pointer */
1112 _display.get_pointer (x, y);
1113 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1115 if (_display.get_path_at_pos (bx, by, path)) {
1116 iter = _model->get_iter (path);
1121 tv = (*iter)[_columns.tv];
1123 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1125 rl->push_back (rtv->route());
1130 return !rl->empty();
1134 EditorRoutes::button_press (GdkEventButton* ev)
1136 if (Keyboard::is_context_menu_event (ev)) {
1141 TreeModel::Path path;
1142 TreeViewColumn *tvc;
1146 if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1147 /* cancel selection */
1148 _display.get_selection()->unselect_all ();
1149 /* end any editing by grabbing focus */
1150 _display.grab_focus ();
1154 //Scroll editor canvas to selected track
1155 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1157 // Get the model row.
1158 Gtk::TreeModel::Row row = *_model->get_iter (path);
1160 TimeAxisView *tv = row[_columns.tv];
1162 int y_pos = tv->y_position();
1164 //Clamp the y pos so that we do not extend beyond the canvas full height.
1165 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1166 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1169 //Only scroll to if the track is visible
1171 _editor->reset_y_origin (y_pos);
1179 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1181 if (selection_countdown) {
1182 if (--selection_countdown == 0) {
1185 /* no selection yet ... */
1192 struct EditorOrderRouteSorter {
1193 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1194 /* use of ">" forces the correct sort order */
1195 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1200 EditorRoutes::initial_display ()
1202 suspend_redisplay ();
1206 resume_redisplay ();
1210 boost::shared_ptr<RouteList> routes = _session->get_routes();
1211 RouteList r (*routes);
1212 EditorOrderRouteSorter sorter;
1215 _editor->handle_new_route (r);
1217 /* don't show master bus in a new session */
1219 if (ARDOUR_UI::instance()->session_is_new ()) {
1221 TreeModel::Children rows = _model->children();
1222 TreeModel::Children::iterator i;
1224 _no_redisplay = true;
1226 for (i = rows.begin(); i != rows.end(); ++i) {
1228 TimeAxisView *tv = (*i)[_columns.tv];
1229 RouteTimeAxisView *rtv;
1231 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1232 if (rtv->route()->is_master()) {
1233 _display.get_selection()->unselect (i);
1238 _no_redisplay = false;
1242 resume_redisplay ();
1246 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1248 _redisplay_does_not_sync_order_keys = true;
1249 _session->set_remote_control_ids();
1251 _redisplay_does_not_sync_order_keys = false;
1255 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1257 const SelectionData& data,
1258 guint info, guint time)
1260 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1261 _display.on_drag_data_received (context, x, y, data, info, time);
1265 context->drag_finish (true, false, time);
1269 EditorRoutes::move_selected_tracks (bool up)
1271 if (_editor->selection->tracks.empty()) {
1275 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1276 std::list<ViewRoute> view_routes;
1277 std::vector<int> neworder;
1278 TreeModel::Children rows = _model->children();
1279 TreeModel::Children::iterator ri;
1281 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1282 TimeAxisView* tv = (*ri)[_columns.tv];
1283 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1285 view_routes.push_back (ViewRoute (tv, route));
1288 list<ViewRoute>::iterator trailing;
1289 list<ViewRoute>::iterator leading;
1293 trailing = view_routes.begin();
1294 leading = view_routes.begin();
1298 while (leading != view_routes.end()) {
1299 if (_editor->selection->selected (leading->first)) {
1300 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1301 leading = view_routes.erase (leading);
1310 /* if we could use reverse_iterator in list::insert, this code
1311 would be a beautiful reflection of the code above. but we can't
1312 and so it looks like a bit of a mess.
1315 trailing = view_routes.end();
1316 leading = view_routes.end();
1318 --leading; if (leading == view_routes.begin()) { return; }
1324 if (_editor->selection->selected (leading->first)) {
1325 list<ViewRoute>::iterator tmp;
1327 /* need to insert *after* trailing, not *before* it,
1328 which is what insert (iter, val) normally does.
1334 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1336 /* can't use iter = cont.erase (iter); form here, because
1337 we need iter to move backwards.
1345 if (leading == view_routes.begin()) {
1346 /* the one we've just inserted somewhere else
1347 was the first in the list. erase this copy,
1348 and then break, because we're done.
1353 view_routes.erase (leading);
1362 if (leading == view_routes.begin()) {
1371 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1372 neworder.push_back (leading->second->order_key (N_ ("editor")));
1376 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1377 assert (*i < (int) neworder.size ());
1381 _model->reorder (neworder);
1383 _session->sync_order_keys (N_ ("editor"));
1387 EditorRoutes::update_input_active_display ()
1389 TreeModel::Children rows = _model->children();
1390 TreeModel::Children::iterator i;
1392 for (i = rows.begin(); i != rows.end(); ++i) {
1393 boost::shared_ptr<Route> route = (*i)[_columns.route];
1395 if (boost::dynamic_pointer_cast<Track> (route)) {
1396 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1399 (*i)[_columns.is_input_active] = mt->input_active();
1406 EditorRoutes::update_rec_display ()
1408 TreeModel::Children rows = _model->children();
1409 TreeModel::Children::iterator i;
1411 for (i = rows.begin(); i != rows.end(); ++i) {
1412 boost::shared_ptr<Route> route = (*i)[_columns.route];
1414 if (boost::dynamic_pointer_cast<Track> (route)) {
1415 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1417 if (route->record_enabled()) {
1418 if (_session->record_status() == Session::Recording) {
1419 (*i)[_columns.rec_state] = 1;
1421 (*i)[_columns.rec_state] = 2;
1423 } else if (mt && mt->step_editing()) {
1424 (*i)[_columns.rec_state] = 3;
1426 (*i)[_columns.rec_state] = 0;
1429 (*i)[_columns.name_editable] = !route->record_enabled ();
1435 EditorRoutes::update_mute_display ()
1437 TreeModel::Children rows = _model->children();
1438 TreeModel::Children::iterator i;
1440 for (i = rows.begin(); i != rows.end(); ++i) {
1441 boost::shared_ptr<Route> route = (*i)[_columns.route];
1442 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1447 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1449 TreeModel::Children rows = _model->children();
1450 TreeModel::Children::iterator i;
1452 for (i = rows.begin(); i != rows.end(); ++i) {
1453 boost::shared_ptr<Route> route = (*i)[_columns.route];
1454 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1459 EditorRoutes::update_solo_isolate_display ()
1461 TreeModel::Children rows = _model->children();
1462 TreeModel::Children::iterator i;
1464 for (i = rows.begin(); i != rows.end(); ++i) {
1465 boost::shared_ptr<Route> route = (*i)[_columns.route];
1466 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1471 EditorRoutes::update_solo_safe_display ()
1473 TreeModel::Children rows = _model->children();
1474 TreeModel::Children::iterator i;
1476 for (i = rows.begin(); i != rows.end(); ++i) {
1477 boost::shared_ptr<Route> route = (*i)[_columns.route];
1478 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1483 EditorRoutes::views () const
1485 list<TimeAxisView*> v;
1486 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1487 v.push_back ((*i)[_columns.tv]);
1494 EditorRoutes::clear ()
1496 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1498 _display.set_model (_model);
1502 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1506 /* give it a special name */
1508 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1511 e->set_name (X_("RouteNameEditorEntry"));
1516 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1520 TreeIter iter = _model->get_iter (path);
1526 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1528 if (route && route->name() != new_text) {
1529 route->set_name (new_text);
1534 EditorRoutes::solo_changed_so_update_mute ()
1536 update_mute_display ();
1540 EditorRoutes::show_tracks_with_regions_at_playhead ()
1542 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1544 set<TimeAxisView*> show;
1545 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1546 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1552 suspend_redisplay ();
1554 TreeModel::Children rows = _model->children ();
1555 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1556 TimeAxisView* tv = (*i)[_columns.tv];
1557 (*i)[_columns.visible] = (show.find (tv) != show.end());
1560 resume_redisplay ();