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 _name_column = _display.append_column ("", _columns.text) - 1;
186 _visible_column = _display.append_column ("", _columns.visible) - 1;
187 _active_column = _display.append_column ("", _columns.active) - 1;
189 _display.append_column (*input_active_column);
190 _display.append_column (*rec_state_column);
191 _display.append_column (*mute_state_column);
192 _display.append_column (*solo_state_column);
193 _display.append_column (*solo_isolate_state_column);
194 _display.append_column (*solo_safe_state_column);
201 { 0, _("Name"), _("Track/Bus Name") },
202 { 1, _("V"), _("Track/Bus visible ?") },
203 { 2, _("A"), _("Track/Bus active ?") },
204 { 3, _("I"), _("MIDI input enabled") },
205 { 4, _("R"), _("Record enabled") },
206 { 5, _("M"), _("Muted") },
207 { 6, _("S"), _("Soloed") },
208 { 7, _("SI"), _("Solo Isolated") },
209 { 8, _("SS"), _("Solo Safe (Locked)") },
213 for (int i = 0; ci[i].index >= 0; ++i) {
214 col = _display.get_column (ci[i].index);
215 l = manage (new Label (ci[i].label));
216 ARDOUR_UI::instance()->set_tip (*l, ci[i].tooltip);
217 col->set_widget (*l);
221 _display.set_headers_visible (true);
222 _display.get_selection()->set_mode (SELECTION_SINGLE);
223 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
224 _display.set_reorderable (true);
225 _display.set_rules_hint (true);
226 _display.set_size_request (100, -1);
227 _display.add_object_drag (_columns.route.index(), "routes");
229 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
232 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
234 TreeViewColumn* name_column = _display.get_column (_name_column);
236 assert (name_column);
238 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
239 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
240 name_column->set_expand(true);
241 name_column->set_min_width(50);
243 name_cell->property_editable() = true;
244 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
246 // Set the visible column cell renderer to radio toggle
247 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
249 visible_cell->property_activatable() = true;
250 visible_cell->property_radio() = false;
251 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
253 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
254 visible_col->set_expand(false);
255 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
256 visible_col->set_fixed_width(30);
257 visible_col->set_alignment(ALIGN_CENTER);
259 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
261 active_cell->property_activatable() = true;
262 active_cell->property_radio() = false;
263 active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
265 TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
266 active_col->set_expand (false);
267 active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
268 active_col->set_fixed_width (30);
269 active_col->set_alignment (ALIGN_CENTER);
271 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
272 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
274 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
275 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
277 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
278 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
280 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
281 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
283 _display.set_enable_search (false);
285 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
289 EditorRoutes::focus_in (GdkEventFocus*)
291 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
294 old_focus = win->get_focus ();
301 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
306 EditorRoutes::focus_out (GdkEventFocus*)
309 old_focus->grab_focus ();
317 EditorRoutes::enter_notify (GdkEventCrossing*)
323 /* arm counter so that ::selection_filter() will deny selecting anything for the
324 next two attempts to change selection status.
326 selection_countdown = 2;
327 _scroller.grab_focus ();
328 Keyboard::magic_widget_grab_focus ();
333 EditorRoutes::leave_notify (GdkEventCrossing*)
335 selection_countdown = 0;
338 old_focus->grab_focus ();
342 Keyboard::magic_widget_drop_focus ();
347 EditorRoutes::set_session (Session* s)
349 SessionHandlePtr::set_session (s);
354 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
355 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
360 EditorRoutes::on_input_active_changed (std::string const & path_string)
362 // Get the model row that has been toggled.
363 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
365 TimeAxisView* tv = row[_columns.tv];
366 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
369 boost::shared_ptr<MidiTrack> mt;
370 mt = rtv->midi_track();
372 mt->set_input_active (!mt->input_active());
378 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
380 // Get the model row that has been toggled.
381 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
383 TimeAxisView* tv = row[_columns.tv];
384 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
386 if (rtv && rtv->track()) {
387 boost::shared_ptr<RouteList> rl (new RouteList);
388 rl->push_back (rtv->route());
389 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
394 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
396 // Get the model row that has been toggled.
397 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
399 TimeAxisView *tv = row[_columns.tv];
400 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
403 boost::shared_ptr<RouteList> rl (new RouteList);
404 rl->push_back (rtv->route());
405 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
410 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
412 // Get the model row that has been toggled.
413 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
415 TimeAxisView *tv = row[_columns.tv];
416 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
419 boost::shared_ptr<RouteList> rl (new RouteList);
420 rl->push_back (rtv->route());
421 if (Config->get_solo_control_is_listen_control()) {
422 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
424 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
430 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
432 // Get the model row that has been toggled.
433 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
435 TimeAxisView *tv = row[_columns.tv];
436 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
439 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
444 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
446 // Get the model row that has been toggled.
447 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
449 TimeAxisView *tv = row[_columns.tv];
450 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
453 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
458 EditorRoutes::build_menu ()
460 using namespace Menu_Helpers;
465 MenuList& items = _menu->items();
466 _menu->set_name ("ArdourContextMenu");
468 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
469 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
470 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
471 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
472 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
473 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
474 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
475 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
476 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
480 EditorRoutes::show_menu ()
486 _menu->popup (1, gtk_get_current_event_time());
490 EditorRoutes::redisplay ()
492 if (_no_redisplay || !_session || _session->deletion_in_progress()) {
496 TreeModel::Children rows = _model->children();
497 TreeModel::Children::iterator i;
500 /* n will be the count of tracks plus children (updated by TimeAxisView::show_at),
501 so we will use that to know where to put things.
505 /* Order keys must not take children into account, so use a separate counter
510 for (n = 0, order_key = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
511 TimeAxisView *tv = (*i)[_columns.tv];
512 boost::shared_ptr<Route> route = (*i)[_columns.route];
515 // just a "title" row
519 if (!_redisplay_does_not_reset_order_keys) {
520 /* this reorder is caused by user action, so reassign sort order keys
523 route->set_order_key (N_ ("editor"), order_key);
526 bool visible = tv->marked_for_display ();
528 /* show or hide the TimeAxisView */
530 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
531 tv->clip_to_viewport ();
540 /* whenever we go idle, update the track view list to reflect the new order.
541 we can't do this here, because we could mess up something that is traversing
542 the track order and has caused a redisplay of the list.
544 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
546 _editor->reset_controls_layout_height (position);
547 _editor->reset_controls_layout_width ();
548 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
549 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
551 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
553 We're increasing the size of the canvas while the bottom is visible.
554 We scroll down to keep in step with the controls layout.
556 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
559 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
560 _session->sync_order_keys (N_ ("editor"));
565 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
567 if (!_session || _session->deletion_in_progress()) {
571 /* this could require an order reset & sync */
572 _session->set_remote_control_ids();
573 _ignore_reorder = true;
575 _ignore_reorder = false;
579 EditorRoutes::visible_changed (std::string const & path)
581 if (_session && _session->deletion_in_progress()) {
587 if ((iter = _model->get_iter (path))) {
588 TimeAxisView* tv = (*iter)[_columns.tv];
590 bool visible = (*iter)[_columns.visible];
592 if (tv->set_marked_for_display (!visible)) {
593 _redisplay_does_not_reset_order_keys = true;
594 _session->set_remote_control_ids();
595 update_visibility ();
597 _redisplay_does_not_reset_order_keys = false;
604 EditorRoutes::active_changed (std::string const & path)
606 if (_session && _session->deletion_in_progress ()) {
610 Gtk::TreeModel::Row row = *_model->get_iter (path);
611 boost::shared_ptr<Route> route = row[_columns.route];
612 bool const active = row[_columns.active];
613 route->set_active (!active, this);
617 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
621 _redisplay_does_not_sync_order_keys = true;
622 suspend_redisplay ();
624 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
626 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
628 row = *(_model->append ());
630 row[_columns.text] = (*x)->route()->name();
631 row[_columns.visible] = (*x)->marked_for_display();
632 row[_columns.active] = (*x)->route()->active ();
633 row[_columns.tv] = *x;
634 row[_columns.route] = (*x)->route ();
635 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
638 row[_columns.is_input_active] = midi_trk->input_active ();
639 row[_columns.is_midi] = true;
641 row[_columns.is_input_active] = false;
642 row[_columns.is_midi] = false;
645 row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
646 row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
647 row[_columns.solo_visible] = !(*x)->route()->is_master ();
648 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
649 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
650 row[_columns.name_editable] = true;
652 _ignore_reorder = true;
654 /* added a new fresh one at the end */
655 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
656 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
659 _ignore_reorder = false;
661 boost::weak_ptr<Route> wr ((*x)->route());
663 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
664 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
666 if ((*x)->is_track()) {
667 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
668 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
671 if ((*x)->is_midi_track()) {
672 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
673 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
674 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
677 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
678 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
679 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
680 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
681 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
682 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
685 update_rec_display ();
686 update_mute_display ();
687 update_solo_display (true);
688 update_solo_isolate_display ();
689 update_solo_safe_display ();
690 update_input_active_display ();
691 update_active_display ();
693 _redisplay_does_not_sync_order_keys = false;
697 EditorRoutes::handle_gui_changes (string const & what, void*)
699 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
701 if (what == "track_height") {
702 /* Optional :make tracks change height while it happens, instead
705 //update_canvas_now ();
709 if (what == "visible_tracks") {
715 EditorRoutes::route_removed (TimeAxisView *tv)
717 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
719 TreeModel::Children rows = _model->children();
720 TreeModel::Children::iterator ri;
722 /* the core model has changed, there is no need to sync
726 _redisplay_does_not_sync_order_keys = true;
728 for (ri = rows.begin(); ri != rows.end(); ++ri) {
729 if ((*ri)[_columns.tv] == tv) {
735 _redisplay_does_not_sync_order_keys = false;
739 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
741 if (!what_changed.contains (ARDOUR::Properties::name)) {
745 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
747 boost::shared_ptr<Route> route = r.lock ();
753 TreeModel::Children rows = _model->children();
754 TreeModel::Children::iterator i;
756 for (i = rows.begin(); i != rows.end(); ++i) {
757 boost::shared_ptr<Route> t = (*i)[_columns.route];
759 (*i)[_columns.text] = route->name();
766 EditorRoutes::update_active_display ()
768 TreeModel::Children rows = _model->children();
769 TreeModel::Children::iterator i;
771 for (i = rows.begin(); i != rows.end(); ++i) {
772 boost::shared_ptr<Route> route = (*i)[_columns.route];
773 (*i)[_columns.active] = route->active ();
778 EditorRoutes::update_visibility ()
780 TreeModel::Children rows = _model->children();
781 TreeModel::Children::iterator i;
783 suspend_redisplay ();
785 for (i = rows.begin(); i != rows.end(); ++i) {
786 TimeAxisView *tv = (*i)[_columns.tv];
787 (*i)[_columns.visible] = tv->marked_for_display ();
794 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
796 TreeModel::Children rows = _model->children();
797 TreeModel::Children::iterator i;
799 for (i = rows.begin(); i != rows.end(); ++i) {
800 if ((*i)[_columns.tv] == &tv) {
801 tv.set_marked_for_display (false);
802 (*i)[_columns.visible] = false;
810 EditorRoutes::show_track_in_display (TimeAxisView& tv)
812 TreeModel::Children rows = _model->children();
813 TreeModel::Children::iterator i;
816 for (i = rows.begin(); i != rows.end(); ++i) {
817 if ((*i)[_columns.tv] == &tv) {
818 tv.set_marked_for_display (true);
819 (*i)[_columns.visible] = true;
827 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
832 /** If src != "editor", take editor order keys from each route and use them to rearrange the
833 * route list so that the visual arrangement of routes matches the order keys from the routes.
836 EditorRoutes::sync_order_keys (string const & src)
838 map<int, int> new_order;
839 TreeModel::Children rows = _model->children();
840 TreeModel::Children::iterator ri;
842 if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
846 bool changed = false;
849 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
850 boost::shared_ptr<Route> route = (*ri)[_columns.route];
852 int const old_key = order;
853 int const new_key = route->order_key (N_ ("editor"));
855 new_order[new_key] = old_key;
857 if (new_key != old_key) {
863 _redisplay_does_not_reset_order_keys = true;
865 /* `compact' new_order into a vector */
867 for (map<int, int>::const_iterator i = new_order.begin(); i != new_order.end(); ++i) {
868 co.push_back (i->second);
871 assert (co.size() == _model->children().size ());
873 _model->reorder (co);
874 _redisplay_does_not_reset_order_keys = false;
880 EditorRoutes::hide_all_tracks (bool /*with_select*/)
882 TreeModel::Children rows = _model->children();
883 TreeModel::Children::iterator i;
885 suspend_redisplay ();
887 for (i = rows.begin(); i != rows.end(); ++i) {
889 TreeModel::Row row = (*i);
890 TimeAxisView *tv = row[_columns.tv];
896 row[_columns.visible] = false;
901 /* XXX this seems like a hack and half, but its not clear where to put this
905 //reset_scrolling_region ();
909 EditorRoutes::set_all_tracks_visibility (bool yn)
911 TreeModel::Children rows = _model->children();
912 TreeModel::Children::iterator i;
914 suspend_redisplay ();
916 for (i = rows.begin(); i != rows.end(); ++i) {
918 TreeModel::Row row = (*i);
919 TimeAxisView* tv = row[_columns.tv];
925 (*i)[_columns.visible] = yn;
932 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
934 TreeModel::Children rows = _model->children();
935 TreeModel::Children::iterator i;
937 suspend_redisplay ();
939 for (i = rows.begin(); i != rows.end(); ++i) {
941 TreeModel::Row row = (*i);
942 TimeAxisView* tv = row[_columns.tv];
944 AudioTimeAxisView* atv;
945 MidiTimeAxisView* mtv;
951 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
954 (*i)[_columns.visible] = yn;
958 if (atv->is_audio_track()) {
959 (*i)[_columns.visible] = yn;
964 if (!atv->is_audio_track()) {
965 (*i)[_columns.visible] = yn;
970 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
973 (*i)[_columns.visible] = yn;
977 if (mtv->is_midi_track()) {
978 (*i)[_columns.visible] = yn;
989 EditorRoutes::hide_all_routes ()
991 set_all_tracks_visibility (false);
995 EditorRoutes::show_all_routes ()
997 set_all_tracks_visibility (true);
1001 EditorRoutes::show_all_audiotracks()
1003 set_all_audio_midi_visibility (1, true);
1006 EditorRoutes::hide_all_audiotracks ()
1008 set_all_audio_midi_visibility (1, false);
1012 EditorRoutes::show_all_audiobus ()
1014 set_all_audio_midi_visibility (2, true);
1017 EditorRoutes::hide_all_audiobus ()
1019 set_all_audio_midi_visibility (2, false);
1023 EditorRoutes::show_all_miditracks()
1025 set_all_audio_midi_visibility (3, true);
1028 EditorRoutes::hide_all_miditracks ()
1030 set_all_audio_midi_visibility (3, false);
1034 EditorRoutes::key_press (GdkEventKey* ev)
1036 TreeViewColumn *col;
1037 boost::shared_ptr<RouteList> rl (new RouteList);
1040 switch (ev->keyval) {
1042 case GDK_ISO_Left_Tab:
1044 /* If we appear to be editing something, leave that cleanly and appropriately.
1046 if (name_editable) {
1047 name_editable->editing_done ();
1051 col = _display.get_column (_name_column); // select&focus on name column
1053 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1054 treeview_select_previous (_display, _model, col);
1056 treeview_select_next (_display, _model, col);
1063 if (get_relevant_routes (rl)) {
1064 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1070 if (Config->get_solo_control_is_listen_control()) {
1071 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1073 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1079 if (get_relevant_routes (rl)) {
1080 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1092 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1095 RouteTimeAxisView* rtv;
1096 RefPtr<TreeSelection> selection = _display.get_selection();
1100 if (selection->count_selected_rows() != 0) {
1104 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1105 iter = selection->get_selected (tm);
1108 /* use mouse pointer */
1113 _display.get_pointer (x, y);
1114 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1116 if (_display.get_path_at_pos (bx, by, path)) {
1117 iter = _model->get_iter (path);
1122 tv = (*iter)[_columns.tv];
1124 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1126 rl->push_back (rtv->route());
1131 return !rl->empty();
1135 EditorRoutes::button_press (GdkEventButton* ev)
1137 if (Keyboard::is_context_menu_event (ev)) {
1142 TreeModel::Path path;
1143 TreeViewColumn *tvc;
1147 if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1148 /* cancel selection */
1149 _display.get_selection()->unselect_all ();
1150 /* end any editing by grabbing focus */
1151 _display.grab_focus ();
1155 //Scroll editor canvas to selected track
1156 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1158 // Get the model row.
1159 Gtk::TreeModel::Row row = *_model->get_iter (path);
1161 TimeAxisView *tv = row[_columns.tv];
1163 int y_pos = tv->y_position();
1165 //Clamp the y pos so that we do not extend beyond the canvas full height.
1166 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1167 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1170 //Only scroll to if the track is visible
1172 _editor->reset_y_origin (y_pos);
1180 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1182 if (selection_countdown) {
1183 if (--selection_countdown == 0) {
1186 /* no selection yet ... */
1193 struct EditorOrderRouteSorter {
1194 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1195 /* use of ">" forces the correct sort order */
1196 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1201 EditorRoutes::initial_display ()
1203 suspend_redisplay ();
1207 resume_redisplay ();
1211 boost::shared_ptr<RouteList> routes = _session->get_routes();
1212 RouteList r (*routes);
1213 EditorOrderRouteSorter sorter;
1216 _editor->handle_new_route (r);
1218 /* don't show master bus in a new session */
1220 if (ARDOUR_UI::instance()->session_is_new ()) {
1222 TreeModel::Children rows = _model->children();
1223 TreeModel::Children::iterator i;
1225 _no_redisplay = true;
1227 for (i = rows.begin(); i != rows.end(); ++i) {
1229 TimeAxisView *tv = (*i)[_columns.tv];
1230 RouteTimeAxisView *rtv;
1232 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1233 if (rtv->route()->is_master()) {
1234 _display.get_selection()->unselect (i);
1239 _no_redisplay = false;
1243 resume_redisplay ();
1247 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1249 _redisplay_does_not_sync_order_keys = true;
1250 _session->set_remote_control_ids();
1252 _redisplay_does_not_sync_order_keys = false;
1256 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1258 const SelectionData& data,
1259 guint info, guint time)
1261 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1262 _display.on_drag_data_received (context, x, y, data, info, time);
1266 context->drag_finish (true, false, time);
1270 EditorRoutes::move_selected_tracks (bool up)
1272 if (_editor->selection->tracks.empty()) {
1276 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1277 std::list<ViewRoute> view_routes;
1278 std::vector<int> neworder;
1279 TreeModel::Children rows = _model->children();
1280 TreeModel::Children::iterator ri;
1282 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1283 TimeAxisView* tv = (*ri)[_columns.tv];
1284 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1286 view_routes.push_back (ViewRoute (tv, route));
1289 list<ViewRoute>::iterator trailing;
1290 list<ViewRoute>::iterator leading;
1294 trailing = view_routes.begin();
1295 leading = view_routes.begin();
1299 while (leading != view_routes.end()) {
1300 if (_editor->selection->selected (leading->first)) {
1301 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1302 leading = view_routes.erase (leading);
1311 /* if we could use reverse_iterator in list::insert, this code
1312 would be a beautiful reflection of the code above. but we can't
1313 and so it looks like a bit of a mess.
1316 trailing = view_routes.end();
1317 leading = view_routes.end();
1319 --leading; if (leading == view_routes.begin()) { return; }
1325 if (_editor->selection->selected (leading->first)) {
1326 list<ViewRoute>::iterator tmp;
1328 /* need to insert *after* trailing, not *before* it,
1329 which is what insert (iter, val) normally does.
1335 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1337 /* can't use iter = cont.erase (iter); form here, because
1338 we need iter to move backwards.
1346 if (leading == view_routes.begin()) {
1347 /* the one we've just inserted somewhere else
1348 was the first in the list. erase this copy,
1349 and then break, because we're done.
1354 view_routes.erase (leading);
1363 if (leading == view_routes.begin()) {
1372 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1373 neworder.push_back (leading->second->order_key (N_ ("editor")));
1377 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1378 assert (*i < (int) neworder.size ());
1382 _model->reorder (neworder);
1384 _session->sync_order_keys (N_ ("editor"));
1388 EditorRoutes::update_input_active_display ()
1390 TreeModel::Children rows = _model->children();
1391 TreeModel::Children::iterator i;
1393 for (i = rows.begin(); i != rows.end(); ++i) {
1394 boost::shared_ptr<Route> route = (*i)[_columns.route];
1396 if (boost::dynamic_pointer_cast<Track> (route)) {
1397 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1400 (*i)[_columns.is_input_active] = mt->input_active();
1407 EditorRoutes::update_rec_display ()
1409 TreeModel::Children rows = _model->children();
1410 TreeModel::Children::iterator i;
1412 for (i = rows.begin(); i != rows.end(); ++i) {
1413 boost::shared_ptr<Route> route = (*i)[_columns.route];
1415 if (boost::dynamic_pointer_cast<Track> (route)) {
1416 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1418 if (route->record_enabled()) {
1419 if (_session->record_status() == Session::Recording) {
1420 (*i)[_columns.rec_state] = 1;
1422 (*i)[_columns.rec_state] = 2;
1424 } else if (mt && mt->step_editing()) {
1425 (*i)[_columns.rec_state] = 3;
1427 (*i)[_columns.rec_state] = 0;
1430 (*i)[_columns.name_editable] = !route->record_enabled ();
1436 EditorRoutes::update_mute_display ()
1438 TreeModel::Children rows = _model->children();
1439 TreeModel::Children::iterator i;
1441 for (i = rows.begin(); i != rows.end(); ++i) {
1442 boost::shared_ptr<Route> route = (*i)[_columns.route];
1443 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1448 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1450 TreeModel::Children rows = _model->children();
1451 TreeModel::Children::iterator i;
1453 for (i = rows.begin(); i != rows.end(); ++i) {
1454 boost::shared_ptr<Route> route = (*i)[_columns.route];
1455 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1460 EditorRoutes::update_solo_isolate_display ()
1462 TreeModel::Children rows = _model->children();
1463 TreeModel::Children::iterator i;
1465 for (i = rows.begin(); i != rows.end(); ++i) {
1466 boost::shared_ptr<Route> route = (*i)[_columns.route];
1467 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1472 EditorRoutes::update_solo_safe_display ()
1474 TreeModel::Children rows = _model->children();
1475 TreeModel::Children::iterator i;
1477 for (i = rows.begin(); i != rows.end(); ++i) {
1478 boost::shared_ptr<Route> route = (*i)[_columns.route];
1479 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1484 EditorRoutes::views () const
1486 list<TimeAxisView*> v;
1487 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1488 v.push_back ((*i)[_columns.tv]);
1495 EditorRoutes::clear ()
1497 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1499 _display.set_model (_model);
1503 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1507 /* give it a special name */
1509 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1512 e->set_name (X_("RouteNameEditorEntry"));
1517 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1521 TreeIter iter = _model->get_iter (path);
1527 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1529 if (route && route->name() != new_text) {
1530 route->set_name (new_text);
1535 EditorRoutes::solo_changed_so_update_mute ()
1537 update_mute_display ();
1541 EditorRoutes::show_tracks_with_regions_at_playhead ()
1543 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1545 set<TimeAxisView*> show;
1546 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1547 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1553 suspend_redisplay ();
1555 TreeModel::Children rows = _model->children ();
1556 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1557 TimeAxisView* tv = (*i)[_columns.tv];
1558 (*i)[_columns.visible] = (show.find (tv) != show.end());
1561 resume_redisplay ();