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 (ActiveState(0), ::get_icon("mute-disabled"));
125 mute_col_renderer->set_pixbuf (Mid, ::get_icon("muted-by-others"));
126 mute_col_renderer->set_pixbuf (Active, ::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 (ActiveState(0), ::get_icon("solo-disabled"));
141 solo_col_renderer->set_pixbuf (Active, ::get_icon("solo-enabled"));
142 solo_col_renderer->set_pixbuf (Mid, ::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() ? Active : ActiveState (0);
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 //Scroll editor canvas to selected track
1142 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1144 TreeModel::Path path;
1145 TreeViewColumn *tvc;
1149 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
1151 // Get the model row.
1152 Gtk::TreeModel::Row row = *_model->get_iter (path);
1154 TimeAxisView *tv = row[_columns.tv];
1156 int y_pos = tv->y_position();
1158 //Clamp the y pos so that we do not extend beyond the canvas full height.
1159 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1160 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1163 //Only scroll to if the track is visible
1165 _editor->reset_y_origin (y_pos);
1173 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1175 if (selection_countdown) {
1176 if (--selection_countdown == 0) {
1179 /* no selection yet ... */
1186 struct EditorOrderRouteSorter {
1187 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1188 /* use of ">" forces the correct sort order */
1189 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1194 EditorRoutes::initial_display ()
1196 suspend_redisplay ();
1200 resume_redisplay ();
1204 boost::shared_ptr<RouteList> routes = _session->get_routes();
1205 RouteList r (*routes);
1206 EditorOrderRouteSorter sorter;
1209 _editor->handle_new_route (r);
1211 /* don't show master bus in a new session */
1213 if (ARDOUR_UI::instance()->session_is_new ()) {
1215 TreeModel::Children rows = _model->children();
1216 TreeModel::Children::iterator i;
1218 _no_redisplay = true;
1220 for (i = rows.begin(); i != rows.end(); ++i) {
1222 TimeAxisView *tv = (*i)[_columns.tv];
1223 RouteTimeAxisView *rtv;
1225 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1226 if (rtv->route()->is_master()) {
1227 _display.get_selection()->unselect (i);
1232 _no_redisplay = false;
1236 resume_redisplay ();
1240 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1242 _redisplay_does_not_sync_order_keys = true;
1243 _session->set_remote_control_ids();
1245 _redisplay_does_not_sync_order_keys = false;
1249 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1251 const SelectionData& data,
1252 guint info, guint time)
1254 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1255 _display.on_drag_data_received (context, x, y, data, info, time);
1259 context->drag_finish (true, false, time);
1263 EditorRoutes::move_selected_tracks (bool up)
1265 if (_editor->selection->tracks.empty()) {
1269 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1270 std::list<ViewRoute> view_routes;
1271 std::vector<int> neworder;
1272 TreeModel::Children rows = _model->children();
1273 TreeModel::Children::iterator ri;
1275 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1276 TimeAxisView* tv = (*ri)[_columns.tv];
1277 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1279 view_routes.push_back (ViewRoute (tv, route));
1282 list<ViewRoute>::iterator trailing;
1283 list<ViewRoute>::iterator leading;
1287 trailing = view_routes.begin();
1288 leading = view_routes.begin();
1292 while (leading != view_routes.end()) {
1293 if (_editor->selection->selected (leading->first)) {
1294 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1295 leading = view_routes.erase (leading);
1304 /* if we could use reverse_iterator in list::insert, this code
1305 would be a beautiful reflection of the code above. but we can't
1306 and so it looks like a bit of a mess.
1309 trailing = view_routes.end();
1310 leading = view_routes.end();
1312 --leading; if (leading == view_routes.begin()) { return; }
1318 if (_editor->selection->selected (leading->first)) {
1319 list<ViewRoute>::iterator tmp;
1321 /* need to insert *after* trailing, not *before* it,
1322 which is what insert (iter, val) normally does.
1328 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1330 /* can't use iter = cont.erase (iter); form here, because
1331 we need iter to move backwards.
1339 if (leading == view_routes.begin()) {
1340 /* the one we've just inserted somewhere else
1341 was the first in the list. erase this copy,
1342 and then break, because we're done.
1347 view_routes.erase (leading);
1356 if (leading == view_routes.begin()) {
1365 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1366 neworder.push_back (leading->second->order_key (N_ ("editor")));
1370 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1371 assert (*i < (int) neworder.size ());
1375 _model->reorder (neworder);
1377 _session->sync_order_keys (N_ ("editor"));
1381 EditorRoutes::update_input_active_display ()
1383 TreeModel::Children rows = _model->children();
1384 TreeModel::Children::iterator i;
1386 for (i = rows.begin(); i != rows.end(); ++i) {
1387 boost::shared_ptr<Route> route = (*i)[_columns.route];
1389 if (boost::dynamic_pointer_cast<Track> (route)) {
1390 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1393 (*i)[_columns.is_input_active] = mt->input_active();
1400 EditorRoutes::update_rec_display ()
1402 TreeModel::Children rows = _model->children();
1403 TreeModel::Children::iterator i;
1405 for (i = rows.begin(); i != rows.end(); ++i) {
1406 boost::shared_ptr<Route> route = (*i)[_columns.route];
1408 if (boost::dynamic_pointer_cast<Track> (route)) {
1409 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1411 if (route->record_enabled()) {
1412 if (_session->record_status() == Session::Recording) {
1413 (*i)[_columns.rec_state] = 1;
1415 (*i)[_columns.rec_state] = 2;
1417 } else if (mt && mt->step_editing()) {
1418 (*i)[_columns.rec_state] = 3;
1420 (*i)[_columns.rec_state] = 0;
1423 (*i)[_columns.name_editable] = !route->record_enabled ();
1429 EditorRoutes::update_mute_display ()
1431 TreeModel::Children rows = _model->children();
1432 TreeModel::Children::iterator i;
1434 for (i = rows.begin(); i != rows.end(); ++i) {
1435 boost::shared_ptr<Route> route = (*i)[_columns.route];
1436 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1441 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1443 TreeModel::Children rows = _model->children();
1444 TreeModel::Children::iterator i;
1446 for (i = rows.begin(); i != rows.end(); ++i) {
1447 boost::shared_ptr<Route> route = (*i)[_columns.route];
1448 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1453 EditorRoutes::update_solo_isolate_display ()
1455 TreeModel::Children rows = _model->children();
1456 TreeModel::Children::iterator i;
1458 for (i = rows.begin(); i != rows.end(); ++i) {
1459 boost::shared_ptr<Route> route = (*i)[_columns.route];
1460 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1465 EditorRoutes::update_solo_safe_display ()
1467 TreeModel::Children rows = _model->children();
1468 TreeModel::Children::iterator i;
1470 for (i = rows.begin(); i != rows.end(); ++i) {
1471 boost::shared_ptr<Route> route = (*i)[_columns.route];
1472 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1477 EditorRoutes::views () const
1479 list<TimeAxisView*> v;
1480 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1481 v.push_back ((*i)[_columns.tv]);
1488 EditorRoutes::clear ()
1490 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1492 _display.set_model (_model);
1496 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1500 /* give it a special name */
1502 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1505 e->set_name (X_("RouteNameEditorEntry"));
1510 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1514 TreeIter iter = _model->get_iter (path);
1520 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1522 if (route && route->name() != new_text) {
1523 route->set_name (new_text);
1528 EditorRoutes::solo_changed_so_update_mute ()
1530 update_mute_display ();
1534 EditorRoutes::show_tracks_with_regions_at_playhead ()
1536 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1538 set<TimeAxisView*> show;
1539 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1540 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1546 suspend_redisplay ();
1548 TreeModel::Children rows = _model->children ();
1549 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1550 TimeAxisView* tv = (*i)[_columns.tv];
1551 (*i)[_columns.visible] = (show.find (tv) != show.end());
1554 resume_redisplay ();