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;
60 EditorRoutes::EditorRoutes (Editor* e)
62 , _ignore_reorder (false)
63 , _no_redisplay (false)
64 , _redisplay_does_not_sync_order_keys (false)
65 , _redisplay_does_not_reset_order_keys (false)
68 , selection_countdown (0)
71 static const int column_width = 22;
73 _scroller.add (_display);
74 _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
76 _model = ListStore::create (_columns);
77 _display.set_model (_model);
79 // Record enable toggle
80 CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
82 rec_col_renderer->set_pixbuf (0, ::get_icon("record-normal-disabled"));
83 rec_col_renderer->set_pixbuf (1, ::get_icon("record-normal-in-progress"));
84 rec_col_renderer->set_pixbuf (2, ::get_icon("record-normal-enabled"));
85 rec_col_renderer->set_pixbuf (3, ::get_icon("record-step"));
86 rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
88 TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
90 rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
91 rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
93 rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
94 rec_state_column->set_alignment(ALIGN_CENTER);
95 rec_state_column->set_expand(false);
96 rec_state_column->set_fixed_width(column_width);
100 CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti());
101 input_active_col_renderer->set_pixbuf (0, ::get_icon("midi-input-inactive"));
102 input_active_col_renderer->set_pixbuf (1, ::get_icon("midi-input-active"));
103 input_active_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed));
105 TreeViewColumn* input_active_column = manage (new TreeViewColumn ("I", *input_active_col_renderer));
107 input_active_column->add_attribute(input_active_col_renderer->property_state(), _columns.is_input_active);
108 input_active_column->add_attribute (input_active_col_renderer->property_visible(), _columns.is_midi);
110 input_active_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
111 input_active_column->set_alignment(ALIGN_CENTER);
112 input_active_column->set_expand(false);
113 input_active_column->set_fixed_width(column_width);
115 // Mute enable toggle
116 CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
118 mute_col_renderer->set_pixbuf (ActiveState(0), ::get_icon("mute-disabled"));
119 mute_col_renderer->set_pixbuf (Mid, ::get_icon("muted-by-others"));
120 mute_col_renderer->set_pixbuf (Active, ::get_icon("mute-enabled"));
121 mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
123 TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
125 mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
126 mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
127 mute_state_column->set_alignment(ALIGN_CENTER);
128 mute_state_column->set_expand(false);
129 mute_state_column->set_fixed_width(15);
131 // Solo enable toggle
132 CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
134 solo_col_renderer->set_pixbuf (ActiveState(0), ::get_icon("solo-disabled"));
135 solo_col_renderer->set_pixbuf (Active, ::get_icon("solo-enabled"));
136 solo_col_renderer->set_pixbuf (Mid, ::get_icon("soloed-by-others"));
137 solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
139 TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
141 solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
142 solo_state_column->add_attribute(solo_col_renderer->property_visible(), _columns.solo_visible);
143 solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
144 solo_state_column->set_alignment(ALIGN_CENTER);
145 solo_state_column->set_expand(false);
146 solo_state_column->set_fixed_width(column_width);
148 // Solo isolate toggle
149 CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
151 solo_iso_renderer->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
152 solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
153 solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
155 TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
157 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
158 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_visible(), _columns.solo_visible);
159 solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
160 solo_isolate_state_column->set_alignment(ALIGN_CENTER);
161 solo_isolate_state_column->set_expand(false);
162 solo_isolate_state_column->set_fixed_width(column_width);
165 CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
167 solo_safe_renderer->set_pixbuf (0, ::get_icon("solo-safe-disabled"));
168 solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-safe-enabled"));
169 solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
171 TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
172 solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
173 solo_safe_state_column->add_attribute(solo_safe_renderer->property_visible(), _columns.solo_visible);
174 solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
175 solo_safe_state_column->set_alignment(ALIGN_CENTER);
176 solo_safe_state_column->set_expand(false);
177 solo_safe_state_column->set_fixed_width(column_width);
179 _display.append_column (*input_active_column);
180 _display.append_column (*rec_state_column);
181 _display.append_column (*mute_state_column);
182 _display.append_column (*solo_state_column);
183 _display.append_column (*solo_isolate_state_column);
184 _display.append_column (*solo_safe_state_column);
186 _name_column = _display.append_column (_("Name"), _columns.text) - 1;
187 _visible_column = _display.append_column (_("V"), _columns.visible) - 1;
188 _active_column = _display.append_column (_("A"), _columns.active) - 1;
190 _display.set_headers_visible (true);
191 _display.set_name ("TrackListDisplay");
192 _display.get_selection()->set_mode (SELECTION_SINGLE);
193 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
194 _display.set_reorderable (true);
195 _display.set_rules_hint (true);
196 _display.set_size_request (100, -1);
197 _display.add_object_drag (_columns.route.index(), "routes");
199 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
202 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
204 TreeViewColumn* name_column = _display.get_column (_name_column);
206 assert (name_column);
208 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
209 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
210 name_column->set_expand(true);
211 name_column->set_min_width(50);
213 name_cell->property_editable() = true;
214 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
216 // Set the visible column cell renderer to radio toggle
217 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
219 visible_cell->property_activatable() = true;
220 visible_cell->property_radio() = false;
221 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
223 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
224 visible_col->set_expand(false);
225 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
226 visible_col->set_fixed_width(30);
227 visible_col->set_alignment(ALIGN_CENTER);
229 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
231 active_cell->property_activatable() = true;
232 active_cell->property_radio() = false;
233 active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
235 TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
236 active_col->set_expand (false);
237 active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
238 active_col->set_fixed_width (30);
239 active_col->set_alignment (ALIGN_CENTER);
241 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
242 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
244 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
245 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
247 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
248 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
250 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
251 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
253 _display.set_enable_search (false);
255 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
259 EditorRoutes::focus_in (GdkEventFocus*)
261 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
264 old_focus = win->get_focus ();
271 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
276 EditorRoutes::focus_out (GdkEventFocus*)
279 old_focus->grab_focus ();
287 EditorRoutes::enter_notify (GdkEventCrossing*)
293 /* arm counter so that ::selection_filter() will deny selecting anything for the
294 next two attempts to change selection status.
296 selection_countdown = 2;
297 _scroller.grab_focus ();
298 Keyboard::magic_widget_grab_focus ();
303 EditorRoutes::leave_notify (GdkEventCrossing*)
305 selection_countdown = 0;
308 old_focus->grab_focus ();
312 Keyboard::magic_widget_drop_focus ();
317 EditorRoutes::set_session (Session* s)
319 SessionHandlePtr::set_session (s);
324 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
325 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
330 EditorRoutes::on_input_active_changed (std::string const & path_string)
332 // Get the model row that has been toggled.
333 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
335 TimeAxisView* tv = row[_columns.tv];
336 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
339 boost::shared_ptr<MidiTrack> mt;
340 mt = rtv->midi_track();
342 mt->set_input_active (!mt->input_active());
348 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
350 // Get the model row that has been toggled.
351 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
353 TimeAxisView* tv = row[_columns.tv];
354 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
356 if (rtv && rtv->track()) {
357 boost::shared_ptr<RouteList> rl (new RouteList);
358 rl->push_back (rtv->route());
359 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
364 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
366 // Get the model row that has been toggled.
367 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
369 TimeAxisView *tv = row[_columns.tv];
370 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
373 boost::shared_ptr<RouteList> rl (new RouteList);
374 rl->push_back (rtv->route());
375 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
380 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
382 // Get the model row that has been toggled.
383 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
385 TimeAxisView *tv = row[_columns.tv];
386 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
389 boost::shared_ptr<RouteList> rl (new RouteList);
390 rl->push_back (rtv->route());
391 if (Config->get_solo_control_is_listen_control()) {
392 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
394 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
400 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
402 // Get the model row that has been toggled.
403 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
405 TimeAxisView *tv = row[_columns.tv];
406 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
409 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
414 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
416 // Get the model row that has been toggled.
417 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
419 TimeAxisView *tv = row[_columns.tv];
420 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
423 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
428 EditorRoutes::build_menu ()
430 using namespace Menu_Helpers;
435 MenuList& items = _menu->items();
436 _menu->set_name ("ArdourContextMenu");
438 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
439 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
440 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
441 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
442 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
443 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
444 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
445 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
446 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
450 EditorRoutes::show_menu ()
456 _menu->popup (1, gtk_get_current_event_time());
460 EditorRoutes::redisplay ()
462 if (_no_redisplay || !_session) {
466 TreeModel::Children rows = _model->children();
467 TreeModel::Children::iterator i;
470 /* n will be the count of tracks plus children (updated by TimeAxisView::show_at),
471 so we will use that to know where to put things.
475 /* Order keys must not take children into account, so use a separate counter
480 for (n = 0, order_key = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
481 TimeAxisView *tv = (*i)[_columns.tv];
482 boost::shared_ptr<Route> route = (*i)[_columns.route];
485 // just a "title" row
489 if (!_redisplay_does_not_reset_order_keys) {
490 /* this reorder is caused by user action, so reassign sort order keys
493 route->set_order_key (N_ ("editor"), order_key);
496 bool visible = tv->marked_for_display ();
498 /* show or hide the TimeAxisView */
500 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
501 tv->clip_to_viewport ();
510 /* whenever we go idle, update the track view list to reflect the new order.
511 we can't do this here, because we could mess up something that is traversing
512 the track order and has caused a redisplay of the list.
514 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
516 _editor->reset_controls_layout_height (position);
517 _editor->reset_controls_layout_width ();
518 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
519 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
521 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
523 We're increasing the size of the canvas while the bottom is visible.
524 We scroll down to keep in step with the controls layout.
526 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
529 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
530 _session->sync_order_keys (N_ ("editor"));
535 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
537 if (!_session || _session->deletion_in_progress()) {
541 /* this could require an order reset & sync */
542 _session->set_remote_control_ids();
543 _ignore_reorder = true;
545 _ignore_reorder = false;
549 EditorRoutes::visible_changed (std::string const & path)
551 if (_session && _session->deletion_in_progress()) {
557 if ((iter = _model->get_iter (path))) {
558 TimeAxisView* tv = (*iter)[_columns.tv];
560 bool visible = (*iter)[_columns.visible];
562 if (tv->set_marked_for_display (!visible)) {
563 _redisplay_does_not_reset_order_keys = true;
564 _session->set_remote_control_ids();
565 update_visibility ();
567 _redisplay_does_not_reset_order_keys = false;
574 EditorRoutes::active_changed (std::string const & path)
576 if (_session && _session->deletion_in_progress ()) {
580 Gtk::TreeModel::Row row = *_model->get_iter (path);
581 boost::shared_ptr<Route> route = row[_columns.route];
582 bool const active = row[_columns.active];
583 route->set_active (!active, this);
587 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
591 _redisplay_does_not_sync_order_keys = true;
592 suspend_redisplay ();
594 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
596 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
598 row = *(_model->append ());
600 row[_columns.text] = (*x)->route()->name();
601 row[_columns.visible] = (*x)->marked_for_display();
602 row[_columns.active] = (*x)->route()->active ();
603 row[_columns.tv] = *x;
604 row[_columns.route] = (*x)->route ();
605 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
608 row[_columns.is_input_active] = midi_trk->input_active ();
609 row[_columns.is_midi] = true;
611 row[_columns.is_input_active] = false;
612 row[_columns.is_midi] = false;
615 row[_columns.mute_state] = (*x)->route()->muted() ? Active : ActiveState (0);
616 row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
617 row[_columns.solo_visible] = !(*x)->route()->is_master ();
618 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
619 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
620 row[_columns.name_editable] = true;
622 _ignore_reorder = true;
624 /* added a new fresh one at the end */
625 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
626 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
629 _ignore_reorder = false;
631 boost::weak_ptr<Route> wr ((*x)->route());
633 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
634 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
636 if ((*x)->is_track()) {
637 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
638 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
641 if ((*x)->is_midi_track()) {
642 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
643 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
644 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
647 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
648 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
649 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
650 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
651 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
652 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
655 update_rec_display ();
656 update_mute_display ();
657 update_solo_display (true);
658 update_solo_isolate_display ();
659 update_solo_safe_display ();
660 update_input_active_display ();
661 update_active_display ();
663 _redisplay_does_not_sync_order_keys = false;
667 EditorRoutes::handle_gui_changes (string const & what, void*)
669 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
671 if (what == "track_height") {
672 /* Optional :make tracks change height while it happens, instead
675 //update_canvas_now ();
679 if (what == "visible_tracks") {
685 EditorRoutes::route_removed (TimeAxisView *tv)
687 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
689 TreeModel::Children rows = _model->children();
690 TreeModel::Children::iterator ri;
692 /* the core model has changed, there is no need to sync
696 _redisplay_does_not_sync_order_keys = true;
698 for (ri = rows.begin(); ri != rows.end(); ++ri) {
699 if ((*ri)[_columns.tv] == tv) {
705 _redisplay_does_not_sync_order_keys = false;
709 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
711 if (!what_changed.contains (ARDOUR::Properties::name)) {
715 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
717 boost::shared_ptr<Route> route = r.lock ();
723 TreeModel::Children rows = _model->children();
724 TreeModel::Children::iterator i;
726 for (i = rows.begin(); i != rows.end(); ++i) {
727 boost::shared_ptr<Route> t = (*i)[_columns.route];
729 (*i)[_columns.text] = route->name();
736 EditorRoutes::update_active_display ()
738 TreeModel::Children rows = _model->children();
739 TreeModel::Children::iterator i;
741 for (i = rows.begin(); i != rows.end(); ++i) {
742 boost::shared_ptr<Route> route = (*i)[_columns.route];
743 (*i)[_columns.active] = route->active ();
748 EditorRoutes::update_visibility ()
750 TreeModel::Children rows = _model->children();
751 TreeModel::Children::iterator i;
753 suspend_redisplay ();
755 for (i = rows.begin(); i != rows.end(); ++i) {
756 TimeAxisView *tv = (*i)[_columns.tv];
757 (*i)[_columns.visible] = tv->marked_for_display ();
764 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
766 TreeModel::Children rows = _model->children();
767 TreeModel::Children::iterator i;
769 for (i = rows.begin(); i != rows.end(); ++i) {
770 if ((*i)[_columns.tv] == &tv) {
771 tv.set_marked_for_display (false);
772 (*i)[_columns.visible] = false;
780 EditorRoutes::show_track_in_display (TimeAxisView& tv)
782 TreeModel::Children rows = _model->children();
783 TreeModel::Children::iterator i;
786 for (i = rows.begin(); i != rows.end(); ++i) {
787 if ((*i)[_columns.tv] == &tv) {
788 tv.set_marked_for_display (true);
789 (*i)[_columns.visible] = true;
797 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
802 /** If src != "editor", take editor order keys from each route and use them to rearrange the
803 * route list so that the visual arrangement of routes matches the order keys from the routes.
806 EditorRoutes::sync_order_keys (string const & src)
808 map<int, int> new_order;
809 TreeModel::Children rows = _model->children();
810 TreeModel::Children::iterator ri;
812 if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
816 bool changed = false;
819 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
820 boost::shared_ptr<Route> route = (*ri)[_columns.route];
822 int const old_key = order;
823 int const new_key = route->order_key (N_ ("editor"));
825 new_order[new_key] = old_key;
827 if (new_key != old_key) {
833 _redisplay_does_not_reset_order_keys = true;
835 /* `compact' new_order into a vector */
837 for (map<int, int>::const_iterator i = new_order.begin(); i != new_order.end(); ++i) {
838 co.push_back (i->second);
841 assert (co.size() == _model->children().size ());
843 _model->reorder (co);
844 _redisplay_does_not_reset_order_keys = false;
850 EditorRoutes::hide_all_tracks (bool /*with_select*/)
852 TreeModel::Children rows = _model->children();
853 TreeModel::Children::iterator i;
855 suspend_redisplay ();
857 for (i = rows.begin(); i != rows.end(); ++i) {
859 TreeModel::Row row = (*i);
860 TimeAxisView *tv = row[_columns.tv];
866 row[_columns.visible] = false;
871 /* XXX this seems like a hack and half, but its not clear where to put this
875 //reset_scrolling_region ();
879 EditorRoutes::set_all_tracks_visibility (bool yn)
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 (*i)[_columns.visible] = yn;
902 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
904 TreeModel::Children rows = _model->children();
905 TreeModel::Children::iterator i;
907 suspend_redisplay ();
909 for (i = rows.begin(); i != rows.end(); ++i) {
911 TreeModel::Row row = (*i);
912 TimeAxisView* tv = row[_columns.tv];
914 AudioTimeAxisView* atv;
915 MidiTimeAxisView* mtv;
921 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
924 (*i)[_columns.visible] = yn;
928 if (atv->is_audio_track()) {
929 (*i)[_columns.visible] = yn;
934 if (!atv->is_audio_track()) {
935 (*i)[_columns.visible] = yn;
940 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
943 (*i)[_columns.visible] = yn;
947 if (mtv->is_midi_track()) {
948 (*i)[_columns.visible] = yn;
959 EditorRoutes::hide_all_routes ()
961 set_all_tracks_visibility (false);
965 EditorRoutes::show_all_routes ()
967 set_all_tracks_visibility (true);
971 EditorRoutes::show_all_audiotracks()
973 set_all_audio_midi_visibility (1, true);
976 EditorRoutes::hide_all_audiotracks ()
978 set_all_audio_midi_visibility (1, false);
982 EditorRoutes::show_all_audiobus ()
984 set_all_audio_midi_visibility (2, true);
987 EditorRoutes::hide_all_audiobus ()
989 set_all_audio_midi_visibility (2, false);
993 EditorRoutes::show_all_miditracks()
995 set_all_audio_midi_visibility (3, true);
998 EditorRoutes::hide_all_miditracks ()
1000 set_all_audio_midi_visibility (3, false);
1004 EditorRoutes::key_press (GdkEventKey* ev)
1006 TreeViewColumn *col;
1007 boost::shared_ptr<RouteList> rl (new RouteList);
1010 switch (ev->keyval) {
1012 case GDK_ISO_Left_Tab:
1014 /* If we appear to be editing something, leave that cleanly and appropriately.
1016 if (name_editable) {
1017 name_editable->editing_done ();
1021 col = _display.get_column (_name_column); // select&focus on name column
1023 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1024 treeview_select_previous (_display, _model, col);
1026 treeview_select_next (_display, _model, col);
1033 if (get_relevant_routes (rl)) {
1034 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1040 if (Config->get_solo_control_is_listen_control()) {
1041 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1043 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1049 if (get_relevant_routes (rl)) {
1050 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1062 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1065 RouteTimeAxisView* rtv;
1066 RefPtr<TreeSelection> selection = _display.get_selection();
1070 if (selection->count_selected_rows() != 0) {
1074 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1075 iter = selection->get_selected (tm);
1078 /* use mouse pointer */
1083 _display.get_pointer (x, y);
1084 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1086 if (_display.get_path_at_pos (bx, by, path)) {
1087 iter = _model->get_iter (path);
1092 tv = (*iter)[_columns.tv];
1094 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1096 rl->push_back (rtv->route());
1101 return !rl->empty();
1105 EditorRoutes::button_press (GdkEventButton* ev)
1107 if (Keyboard::is_context_menu_event (ev)) {
1112 //Scroll editor canvas to selected track
1113 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1115 TreeModel::Path path;
1116 TreeViewColumn *tvc;
1120 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
1122 // Get the model row.
1123 Gtk::TreeModel::Row row = *_model->get_iter (path);
1125 TimeAxisView *tv = row[_columns.tv];
1127 int y_pos = tv->y_position();
1129 //Clamp the y pos so that we do not extend beyond the canvas full height.
1130 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1131 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1134 //Only scroll to if the track is visible
1136 _editor->reset_y_origin (y_pos);
1144 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1146 if (selection_countdown) {
1147 if (--selection_countdown == 0) {
1150 /* no selection yet ... */
1157 struct EditorOrderRouteSorter {
1158 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1159 /* use of ">" forces the correct sort order */
1160 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1165 EditorRoutes::initial_display ()
1167 suspend_redisplay ();
1171 resume_redisplay ();
1175 boost::shared_ptr<RouteList> routes = _session->get_routes();
1176 RouteList r (*routes);
1177 EditorOrderRouteSorter sorter;
1180 _editor->handle_new_route (r);
1182 /* don't show master bus in a new session */
1184 if (ARDOUR_UI::instance()->session_is_new ()) {
1186 TreeModel::Children rows = _model->children();
1187 TreeModel::Children::iterator i;
1189 _no_redisplay = true;
1191 for (i = rows.begin(); i != rows.end(); ++i) {
1193 TimeAxisView *tv = (*i)[_columns.tv];
1194 RouteTimeAxisView *rtv;
1196 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1197 if (rtv->route()->is_master()) {
1198 _display.get_selection()->unselect (i);
1203 _no_redisplay = false;
1207 resume_redisplay ();
1211 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1213 _redisplay_does_not_sync_order_keys = true;
1214 _session->set_remote_control_ids();
1216 _redisplay_does_not_sync_order_keys = false;
1220 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1222 const SelectionData& data,
1223 guint info, guint time)
1225 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1226 _display.on_drag_data_received (context, x, y, data, info, time);
1230 context->drag_finish (true, false, time);
1234 EditorRoutes::move_selected_tracks (bool up)
1236 if (_editor->selection->tracks.empty()) {
1240 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1241 std::list<ViewRoute> view_routes;
1242 std::vector<int> neworder;
1243 TreeModel::Children rows = _model->children();
1244 TreeModel::Children::iterator ri;
1246 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1247 TimeAxisView* tv = (*ri)[_columns.tv];
1248 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1250 view_routes.push_back (ViewRoute (tv, route));
1253 list<ViewRoute>::iterator trailing;
1254 list<ViewRoute>::iterator leading;
1258 trailing = view_routes.begin();
1259 leading = view_routes.begin();
1263 while (leading != view_routes.end()) {
1264 if (_editor->selection->selected (leading->first)) {
1265 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1266 leading = view_routes.erase (leading);
1275 /* if we could use reverse_iterator in list::insert, this code
1276 would be a beautiful reflection of the code above. but we can't
1277 and so it looks like a bit of a mess.
1280 trailing = view_routes.end();
1281 leading = view_routes.end();
1283 --leading; if (leading == view_routes.begin()) { return; }
1289 if (_editor->selection->selected (leading->first)) {
1290 list<ViewRoute>::iterator tmp;
1292 /* need to insert *after* trailing, not *before* it,
1293 which is what insert (iter, val) normally does.
1299 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1301 /* can't use iter = cont.erase (iter); form here, because
1302 we need iter to move backwards.
1310 if (leading == view_routes.begin()) {
1311 /* the one we've just inserted somewhere else
1312 was the first in the list. erase this copy,
1313 and then break, because we're done.
1318 view_routes.erase (leading);
1327 if (leading == view_routes.begin()) {
1336 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1337 neworder.push_back (leading->second->order_key (N_ ("editor")));
1341 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1342 assert (*i < (int) neworder.size ());
1346 _model->reorder (neworder);
1348 _session->sync_order_keys (N_ ("editor"));
1352 EditorRoutes::update_input_active_display ()
1354 TreeModel::Children rows = _model->children();
1355 TreeModel::Children::iterator i;
1357 for (i = rows.begin(); i != rows.end(); ++i) {
1358 boost::shared_ptr<Route> route = (*i)[_columns.route];
1360 if (boost::dynamic_pointer_cast<Track> (route)) {
1361 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1364 (*i)[_columns.is_input_active] = mt->input_active();
1371 EditorRoutes::update_rec_display ()
1373 TreeModel::Children rows = _model->children();
1374 TreeModel::Children::iterator i;
1376 for (i = rows.begin(); i != rows.end(); ++i) {
1377 boost::shared_ptr<Route> route = (*i)[_columns.route];
1379 if (boost::dynamic_pointer_cast<Track> (route)) {
1380 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1382 if (route->record_enabled()) {
1383 if (_session->record_status() == Session::Recording) {
1384 (*i)[_columns.rec_state] = 1;
1386 (*i)[_columns.rec_state] = 2;
1388 } else if (mt && mt->step_editing()) {
1389 (*i)[_columns.rec_state] = 3;
1391 (*i)[_columns.rec_state] = 0;
1394 (*i)[_columns.name_editable] = !route->record_enabled ();
1400 EditorRoutes::update_mute_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];
1407 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1412 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1414 TreeModel::Children rows = _model->children();
1415 TreeModel::Children::iterator i;
1417 for (i = rows.begin(); i != rows.end(); ++i) {
1418 boost::shared_ptr<Route> route = (*i)[_columns.route];
1419 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1424 EditorRoutes::update_solo_isolate_display ()
1426 TreeModel::Children rows = _model->children();
1427 TreeModel::Children::iterator i;
1429 for (i = rows.begin(); i != rows.end(); ++i) {
1430 boost::shared_ptr<Route> route = (*i)[_columns.route];
1431 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1436 EditorRoutes::update_solo_safe_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.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1448 EditorRoutes::views () const
1450 list<TimeAxisView*> v;
1451 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1452 v.push_back ((*i)[_columns.tv]);
1459 EditorRoutes::clear ()
1461 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1463 _display.set_model (_model);
1467 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1471 /* give it a special name */
1473 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1476 e->set_name (X_("RouteNameEditorEntry"));
1481 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1485 TreeIter iter = _model->get_iter (path);
1491 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1493 if (route && route->name() != new_text) {
1494 route->set_name (new_text);
1499 EditorRoutes::solo_changed_so_update_mute ()
1501 update_mute_display ();
1505 EditorRoutes::show_tracks_with_regions_at_playhead ()
1507 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1509 set<TimeAxisView*> show;
1510 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1511 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1517 suspend_redisplay ();
1519 TreeModel::Children rows = _model->children ();
1520 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1521 TimeAxisView* tv = (*i)[_columns.tv];
1522 (*i)[_columns.visible] = (show.find (tv) != show.end());
1525 resume_redisplay ();