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 (0, ::get_icon("mute-disabled"));
119 mute_col_renderer->set_pixbuf (1, ::get_icon("muted-by-others"));
120 mute_col_renderer->set_pixbuf (2, ::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 (0, ::get_icon("solo-disabled"));
135 solo_col_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
136 solo_col_renderer->set_pixbuf (3, ::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->set_sizing(TREE_VIEW_COLUMN_FIXED);
143 solo_state_column->set_alignment(ALIGN_CENTER);
144 solo_state_column->set_expand(false);
145 solo_state_column->set_fixed_width(column_width);
147 // Solo isolate toggle
148 CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
150 solo_iso_renderer->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
151 solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
152 solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
154 TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
156 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
157 solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
158 solo_isolate_state_column->set_alignment(ALIGN_CENTER);
159 solo_isolate_state_column->set_expand(false);
160 solo_isolate_state_column->set_fixed_width(column_width);
163 CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
165 solo_safe_renderer->set_pixbuf (0, ::get_icon("solo-safe-disabled"));
166 solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-safe-enabled"));
167 solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
169 TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
170 solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
171 solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
172 solo_safe_state_column->set_alignment(ALIGN_CENTER);
173 solo_safe_state_column->set_expand(false);
174 solo_safe_state_column->set_fixed_width(column_width);
176 _display.append_column (*input_active_column);
177 _display.append_column (*rec_state_column);
178 _display.append_column (*mute_state_column);
179 _display.append_column (*solo_state_column);
180 _display.append_column (*solo_isolate_state_column);
181 _display.append_column (*solo_safe_state_column);
183 _name_column = _display.append_column (_("Name"), _columns.text) - 1;
184 _visible_column = _display.append_column (_("V"), _columns.visible) - 1;
185 _active_column = _display.append_column (_("A"), _columns.active) - 1;
187 _display.set_headers_visible (true);
188 _display.set_name ("TrackListDisplay");
189 _display.get_selection()->set_mode (SELECTION_SINGLE);
190 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
191 _display.set_reorderable (true);
192 _display.set_rules_hint (true);
193 _display.set_size_request (100, -1);
194 _display.add_object_drag (_columns.route.index(), "routes");
196 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
199 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
201 TreeViewColumn* name_column = _display.get_column (_name_column);
203 assert (name_column);
205 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
206 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
207 name_column->set_expand(true);
208 name_column->set_min_width(50);
210 name_cell->property_editable() = true;
211 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
213 // Set the visible column cell renderer to radio toggle
214 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
216 visible_cell->property_activatable() = true;
217 visible_cell->property_radio() = false;
218 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
220 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
221 visible_col->set_expand(false);
222 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
223 visible_col->set_fixed_width(30);
224 visible_col->set_alignment(ALIGN_CENTER);
226 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
228 active_cell->property_activatable() = true;
229 active_cell->property_radio() = false;
230 active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
232 TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
233 active_col->set_expand (false);
234 active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
235 active_col->set_fixed_width (30);
236 active_col->set_alignment (ALIGN_CENTER);
238 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
239 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
241 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
242 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
244 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
245 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
247 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
248 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
250 _display.set_enable_search (false);
252 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
256 EditorRoutes::focus_in (GdkEventFocus*)
258 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
261 old_focus = win->get_focus ();
268 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
273 EditorRoutes::focus_out (GdkEventFocus*)
276 old_focus->grab_focus ();
284 EditorRoutes::enter_notify (GdkEventCrossing*)
290 /* arm counter so that ::selection_filter() will deny selecting anything for the
291 next two attempts to change selection status.
293 selection_countdown = 2;
294 _scroller.grab_focus ();
295 Keyboard::magic_widget_grab_focus ();
300 EditorRoutes::leave_notify (GdkEventCrossing*)
302 selection_countdown = 0;
305 old_focus->grab_focus ();
309 Keyboard::magic_widget_drop_focus ();
314 EditorRoutes::set_session (Session* s)
316 SessionHandlePtr::set_session (s);
321 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
322 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
327 EditorRoutes::on_input_active_changed (std::string const & path_string)
329 // Get the model row that has been toggled.
330 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
332 TimeAxisView* tv = row[_columns.tv];
333 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
336 boost::shared_ptr<MidiTrack> mt;
337 mt = rtv->midi_track();
339 mt->set_input_active (!mt->input_active());
345 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
347 // Get the model row that has been toggled.
348 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
350 TimeAxisView* tv = row[_columns.tv];
351 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
353 if (rtv && rtv->track()) {
354 boost::shared_ptr<RouteList> rl (new RouteList);
355 rl->push_back (rtv->route());
356 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
361 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
363 // Get the model row that has been toggled.
364 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
366 TimeAxisView *tv = row[_columns.tv];
367 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
370 boost::shared_ptr<RouteList> rl (new RouteList);
371 rl->push_back (rtv->route());
372 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
377 EditorRoutes::on_tv_solo_enable_toggled (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);
386 boost::shared_ptr<RouteList> rl (new RouteList);
387 rl->push_back (rtv->route());
388 if (Config->get_solo_control_is_listen_control()) {
389 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
391 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
397 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
399 // Get the model row that has been toggled.
400 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
402 TimeAxisView *tv = row[_columns.tv];
403 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
406 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
411 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
413 // Get the model row that has been toggled.
414 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
416 TimeAxisView *tv = row[_columns.tv];
417 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
420 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
425 EditorRoutes::build_menu ()
427 using namespace Menu_Helpers;
432 MenuList& items = _menu->items();
433 _menu->set_name ("ArdourContextMenu");
435 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
436 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
437 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
438 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
439 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
440 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
441 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
442 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
443 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
447 EditorRoutes::show_menu ()
453 _menu->popup (1, gtk_get_current_event_time());
457 EditorRoutes::redisplay ()
459 if (_no_redisplay || !_session) {
463 TreeModel::Children rows = _model->children();
464 TreeModel::Children::iterator i;
468 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
469 TimeAxisView *tv = (*i)[_columns.tv];
470 boost::shared_ptr<Route> route = (*i)[_columns.route];
473 // just a "title" row
477 if (!_redisplay_does_not_reset_order_keys) {
478 /* this reorder is caused by user action, so reassign sort order keys
481 route->set_order_key (N_ ("editor"), n);
484 bool visible = tv->marked_for_display ();
486 /* show or hide the TimeAxisView */
488 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
489 tv->clip_to_viewport ();
497 /* whenever we go idle, update the track view list to reflect the new order.
498 we can't do this here, because we could mess up something that is traversing
499 the track order and has caused a redisplay of the list.
501 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
503 _editor->reset_controls_layout_height (position);
504 _editor->reset_controls_layout_width ();
505 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
506 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
508 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
510 We're increasing the size of the canvas while the bottom is visible.
511 We scroll down to keep in step with the controls layout.
513 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
516 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
517 _session->sync_order_keys (N_ ("editor"));
522 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
524 if (!_session || _session->deletion_in_progress()) {
528 /* this could require an order reset & sync */
529 _session->set_remote_control_ids();
530 _ignore_reorder = true;
532 _ignore_reorder = false;
536 EditorRoutes::visible_changed (std::string const & path)
538 if (_session && _session->deletion_in_progress()) {
544 if ((iter = _model->get_iter (path))) {
545 TimeAxisView* tv = (*iter)[_columns.tv];
547 bool visible = (*iter)[_columns.visible];
549 if (tv->set_marked_for_display (!visible)) {
550 _redisplay_does_not_reset_order_keys = true;
551 _session->set_remote_control_ids();
552 update_visibility ();
554 _redisplay_does_not_reset_order_keys = false;
561 EditorRoutes::active_changed (std::string const & path)
563 if (_session && _session->deletion_in_progress ()) {
567 Gtk::TreeModel::Row row = *_model->get_iter (path);
568 boost::shared_ptr<Route> route = row[_columns.route];
569 bool const active = row[_columns.active];
570 route->set_active (!active, this);
574 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
578 _redisplay_does_not_sync_order_keys = true;
579 suspend_redisplay ();
581 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
583 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
585 row = *(_model->append ());
587 row[_columns.text] = (*x)->route()->name();
588 row[_columns.visible] = (*x)->marked_for_display();
589 row[_columns.active] = (*x)->route()->active ();
590 row[_columns.tv] = *x;
591 row[_columns.route] = (*x)->route ();
592 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
595 row[_columns.is_input_active] = midi_trk->input_active ();
596 row[_columns.is_midi] = true;
598 row[_columns.is_input_active] = false;
599 row[_columns.is_midi] = false;
602 row[_columns.mute_state] = (*x)->route()->muted();
603 row[_columns.solo_state] = RouteUI::solo_visual_state ((*x)->route());
604 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
605 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
606 row[_columns.name_editable] = true;
608 _ignore_reorder = true;
610 /* added a new fresh one at the end */
611 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
612 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
615 _ignore_reorder = false;
617 boost::weak_ptr<Route> wr ((*x)->route());
619 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
620 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
622 if ((*x)->is_track()) {
623 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
624 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
627 if ((*x)->is_midi_track()) {
628 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
629 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
630 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
633 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
634 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
635 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
636 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
637 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
638 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
641 update_rec_display ();
642 update_mute_display ();
643 update_solo_display (true);
644 update_solo_isolate_display ();
645 update_solo_safe_display ();
646 update_input_active_display ();
647 update_active_display ();
649 _redisplay_does_not_sync_order_keys = false;
653 EditorRoutes::handle_gui_changes (string const & what, void*)
655 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
657 if (what == "track_height") {
658 /* Optional :make tracks change height while it happens, instead
661 //update_canvas_now ();
665 if (what == "visible_tracks") {
671 EditorRoutes::route_removed (TimeAxisView *tv)
673 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
675 TreeModel::Children rows = _model->children();
676 TreeModel::Children::iterator ri;
678 /* the core model has changed, there is no need to sync
682 _redisplay_does_not_sync_order_keys = true;
684 for (ri = rows.begin(); ri != rows.end(); ++ri) {
685 if ((*ri)[_columns.tv] == tv) {
691 _redisplay_does_not_sync_order_keys = false;
695 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
697 if (!what_changed.contains (ARDOUR::Properties::name)) {
701 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
703 boost::shared_ptr<Route> route = r.lock ();
709 TreeModel::Children rows = _model->children();
710 TreeModel::Children::iterator i;
712 for (i = rows.begin(); i != rows.end(); ++i) {
713 boost::shared_ptr<Route> t = (*i)[_columns.route];
715 (*i)[_columns.text] = route->name();
722 EditorRoutes::update_active_display ()
724 TreeModel::Children rows = _model->children();
725 TreeModel::Children::iterator i;
727 for (i = rows.begin(); i != rows.end(); ++i) {
728 boost::shared_ptr<Route> route = (*i)[_columns.route];
729 (*i)[_columns.active] = route->active ();
734 EditorRoutes::update_visibility ()
736 TreeModel::Children rows = _model->children();
737 TreeModel::Children::iterator i;
739 suspend_redisplay ();
741 for (i = rows.begin(); i != rows.end(); ++i) {
742 TimeAxisView *tv = (*i)[_columns.tv];
743 (*i)[_columns.visible] = tv->marked_for_display ();
750 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
752 TreeModel::Children rows = _model->children();
753 TreeModel::Children::iterator i;
755 for (i = rows.begin(); i != rows.end(); ++i) {
756 if ((*i)[_columns.tv] == &tv) {
757 tv.set_marked_for_display (false);
758 (*i)[_columns.visible] = false;
766 EditorRoutes::show_track_in_display (TimeAxisView& tv)
768 TreeModel::Children rows = _model->children();
769 TreeModel::Children::iterator i;
772 for (i = rows.begin(); i != rows.end(); ++i) {
773 if ((*i)[_columns.tv] == &tv) {
774 tv.set_marked_for_display (true);
775 (*i)[_columns.visible] = true;
783 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
788 /** If src != "editor", take editor order keys from each route and use them to rearrange the
789 * route list so that the visual arrangement of routes matches the order keys from the routes.
792 EditorRoutes::sync_order_keys (string const & src)
794 map<int, int> new_order;
795 TreeModel::Children rows = _model->children();
796 TreeModel::Children::iterator ri;
798 if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
802 bool changed = false;
805 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
806 boost::shared_ptr<Route> route = (*ri)[_columns.route];
808 int const old_key = order;
809 int const new_key = route->order_key (N_ ("editor"));
811 new_order[new_key] = old_key;
813 if (new_key != old_key) {
819 _redisplay_does_not_reset_order_keys = true;
821 /* `compact' new_order into a vector */
823 for (map<int, int>::const_iterator i = new_order.begin(); i != new_order.end(); ++i) {
824 co.push_back (i->second);
827 assert (co.size() == _model->children().size ());
829 _model->reorder (co);
830 _redisplay_does_not_reset_order_keys = false;
836 EditorRoutes::hide_all_tracks (bool /*with_select*/)
838 TreeModel::Children rows = _model->children();
839 TreeModel::Children::iterator i;
841 suspend_redisplay ();
843 for (i = rows.begin(); i != rows.end(); ++i) {
845 TreeModel::Row row = (*i);
846 TimeAxisView *tv = row[_columns.tv];
852 row[_columns.visible] = false;
857 /* XXX this seems like a hack and half, but its not clear where to put this
861 //reset_scrolling_region ();
865 EditorRoutes::set_all_tracks_visibility (bool yn)
867 TreeModel::Children rows = _model->children();
868 TreeModel::Children::iterator i;
870 suspend_redisplay ();
872 for (i = rows.begin(); i != rows.end(); ++i) {
874 TreeModel::Row row = (*i);
875 TimeAxisView* tv = row[_columns.tv];
881 (*i)[_columns.visible] = yn;
888 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
890 TreeModel::Children rows = _model->children();
891 TreeModel::Children::iterator i;
893 suspend_redisplay ();
895 for (i = rows.begin(); i != rows.end(); ++i) {
897 TreeModel::Row row = (*i);
898 TimeAxisView* tv = row[_columns.tv];
900 AudioTimeAxisView* atv;
901 MidiTimeAxisView* mtv;
907 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
910 (*i)[_columns.visible] = yn;
914 if (atv->is_audio_track()) {
915 (*i)[_columns.visible] = yn;
920 if (!atv->is_audio_track()) {
921 (*i)[_columns.visible] = yn;
926 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
929 (*i)[_columns.visible] = yn;
933 if (mtv->is_midi_track()) {
934 (*i)[_columns.visible] = yn;
945 EditorRoutes::hide_all_routes ()
947 set_all_tracks_visibility (false);
951 EditorRoutes::show_all_routes ()
953 set_all_tracks_visibility (true);
957 EditorRoutes::show_all_audiotracks()
959 set_all_audio_midi_visibility (1, true);
962 EditorRoutes::hide_all_audiotracks ()
964 set_all_audio_midi_visibility (1, false);
968 EditorRoutes::show_all_audiobus ()
970 set_all_audio_midi_visibility (2, true);
973 EditorRoutes::hide_all_audiobus ()
975 set_all_audio_midi_visibility (2, false);
979 EditorRoutes::show_all_miditracks()
981 set_all_audio_midi_visibility (3, true);
984 EditorRoutes::hide_all_miditracks ()
986 set_all_audio_midi_visibility (3, false);
990 EditorRoutes::key_press (GdkEventKey* ev)
993 boost::shared_ptr<RouteList> rl (new RouteList);
996 switch (ev->keyval) {
998 case GDK_ISO_Left_Tab:
1000 /* If we appear to be editing something, leave that cleanly and appropriately.
1002 if (name_editable) {
1003 name_editable->editing_done ();
1007 col = _display.get_column (_name_column); // select&focus on name column
1009 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1010 treeview_select_previous (_display, _model, col);
1012 treeview_select_next (_display, _model, col);
1019 if (get_relevant_routes (rl)) {
1020 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1026 if (Config->get_solo_control_is_listen_control()) {
1027 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1029 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1035 if (get_relevant_routes (rl)) {
1036 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1048 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1051 RouteTimeAxisView* rtv;
1052 RefPtr<TreeSelection> selection = _display.get_selection();
1056 if (selection->count_selected_rows() != 0) {
1060 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1061 iter = selection->get_selected (tm);
1064 /* use mouse pointer */
1069 _display.get_pointer (x, y);
1070 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1072 if (_display.get_path_at_pos (bx, by, path)) {
1073 iter = _model->get_iter (path);
1078 tv = (*iter)[_columns.tv];
1080 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1082 rl->push_back (rtv->route());
1087 return !rl->empty();
1091 EditorRoutes::button_press (GdkEventButton* ev)
1093 if (Keyboard::is_context_menu_event (ev)) {
1098 //Scroll editor canvas to selected track
1099 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1101 TreeModel::Path path;
1102 TreeViewColumn *tvc;
1106 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
1108 // Get the model row.
1109 Gtk::TreeModel::Row row = *_model->get_iter (path);
1111 TimeAxisView *tv = row[_columns.tv];
1113 int y_pos = tv->y_position();
1115 //Clamp the y pos so that we do not extend beyond the canvas full height.
1116 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1117 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1120 //Only scroll to if the track is visible
1122 _editor->reset_y_origin (y_pos);
1130 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1132 if (selection_countdown) {
1133 if (--selection_countdown == 0) {
1136 /* no selection yet ... */
1143 struct EditorOrderRouteSorter {
1144 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1145 /* use of ">" forces the correct sort order */
1146 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1151 EditorRoutes::initial_display ()
1153 suspend_redisplay ();
1157 resume_redisplay ();
1161 boost::shared_ptr<RouteList> routes = _session->get_routes();
1162 RouteList r (*routes);
1163 EditorOrderRouteSorter sorter;
1166 _editor->handle_new_route (r);
1168 /* don't show master bus in a new session */
1170 if (ARDOUR_UI::instance()->session_is_new ()) {
1172 TreeModel::Children rows = _model->children();
1173 TreeModel::Children::iterator i;
1175 _no_redisplay = true;
1177 for (i = rows.begin(); i != rows.end(); ++i) {
1179 TimeAxisView *tv = (*i)[_columns.tv];
1180 RouteTimeAxisView *rtv;
1182 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1183 if (rtv->route()->is_master()) {
1184 _display.get_selection()->unselect (i);
1189 _no_redisplay = false;
1193 resume_redisplay ();
1197 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1199 _redisplay_does_not_sync_order_keys = true;
1200 _session->set_remote_control_ids();
1202 _redisplay_does_not_sync_order_keys = false;
1206 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1208 const SelectionData& data,
1209 guint info, guint time)
1211 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1212 _display.on_drag_data_received (context, x, y, data, info, time);
1216 context->drag_finish (true, false, time);
1220 EditorRoutes::move_selected_tracks (bool up)
1222 if (_editor->selection->tracks.empty()) {
1226 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1227 std::list<ViewRoute> view_routes;
1228 std::vector<int> neworder;
1229 TreeModel::Children rows = _model->children();
1230 TreeModel::Children::iterator ri;
1232 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1233 TimeAxisView* tv = (*ri)[_columns.tv];
1234 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1236 view_routes.push_back (ViewRoute (tv, route));
1239 list<ViewRoute>::iterator trailing;
1240 list<ViewRoute>::iterator leading;
1244 trailing = view_routes.begin();
1245 leading = view_routes.begin();
1249 while (leading != view_routes.end()) {
1250 if (_editor->selection->selected (leading->first)) {
1251 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1252 leading = view_routes.erase (leading);
1261 /* if we could use reverse_iterator in list::insert, this code
1262 would be a beautiful reflection of the code above. but we can't
1263 and so it looks like a bit of a mess.
1266 trailing = view_routes.end();
1267 leading = view_routes.end();
1269 --leading; if (leading == view_routes.begin()) { return; }
1275 if (_editor->selection->selected (leading->first)) {
1276 list<ViewRoute>::iterator tmp;
1278 /* need to insert *after* trailing, not *before* it,
1279 which is what insert (iter, val) normally does.
1285 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1287 /* can't use iter = cont.erase (iter); form here, because
1288 we need iter to move backwards.
1296 if (leading == view_routes.begin()) {
1297 /* the one we've just inserted somewhere else
1298 was the first in the list. erase this copy,
1299 and then break, because we're done.
1304 view_routes.erase (leading);
1313 if (leading == view_routes.begin()) {
1322 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1323 neworder.push_back (leading->second->order_key (N_ ("editor")));
1326 _model->reorder (neworder);
1328 _session->sync_order_keys (N_ ("editor"));
1332 EditorRoutes::update_input_active_display ()
1334 TreeModel::Children rows = _model->children();
1335 TreeModel::Children::iterator i;
1337 for (i = rows.begin(); i != rows.end(); ++i) {
1338 boost::shared_ptr<Route> route = (*i)[_columns.route];
1340 if (boost::dynamic_pointer_cast<Track> (route)) {
1341 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1344 (*i)[_columns.is_input_active] = mt->input_active();
1351 EditorRoutes::update_rec_display ()
1353 TreeModel::Children rows = _model->children();
1354 TreeModel::Children::iterator i;
1356 for (i = rows.begin(); i != rows.end(); ++i) {
1357 boost::shared_ptr<Route> route = (*i)[_columns.route];
1359 if (boost::dynamic_pointer_cast<Track> (route)) {
1360 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1362 if (route->record_enabled()) {
1363 if (_session->record_status() == Session::Recording) {
1364 (*i)[_columns.rec_state] = 1;
1366 (*i)[_columns.rec_state] = 2;
1368 } else if (mt && mt->step_editing()) {
1369 (*i)[_columns.rec_state] = 3;
1371 (*i)[_columns.rec_state] = 0;
1374 (*i)[_columns.name_editable] = !route->record_enabled ();
1380 EditorRoutes::update_mute_display ()
1382 TreeModel::Children rows = _model->children();
1383 TreeModel::Children::iterator i;
1385 for (i = rows.begin(); i != rows.end(); ++i) {
1386 boost::shared_ptr<Route> route = (*i)[_columns.route];
1387 (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
1392 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1394 TreeModel::Children rows = _model->children();
1395 TreeModel::Children::iterator i;
1397 for (i = rows.begin(); i != rows.end(); ++i) {
1398 boost::shared_ptr<Route> route = (*i)[_columns.route];
1399 (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
1404 EditorRoutes::update_solo_isolate_display ()
1406 TreeModel::Children rows = _model->children();
1407 TreeModel::Children::iterator i;
1409 for (i = rows.begin(); i != rows.end(); ++i) {
1410 boost::shared_ptr<Route> route = (*i)[_columns.route];
1411 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
1416 EditorRoutes::update_solo_safe_display ()
1418 TreeModel::Children rows = _model->children();
1419 TreeModel::Children::iterator i;
1421 for (i = rows.begin(); i != rows.end(); ++i) {
1422 boost::shared_ptr<Route> route = (*i)[_columns.route];
1423 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
1428 EditorRoutes::views () const
1430 list<TimeAxisView*> v;
1431 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1432 v.push_back ((*i)[_columns.tv]);
1439 EditorRoutes::clear ()
1441 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1443 _display.set_model (_model);
1447 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1451 /* give it a special name */
1453 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1456 e->set_name (X_("RouteNameEditorEntry"));
1461 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1465 TreeIter iter = _model->get_iter (path);
1471 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1473 if (route && route->name() != new_text) {
1474 route->set_name (new_text);
1479 EditorRoutes::solo_changed_so_update_mute ()
1481 update_mute_display ();
1485 EditorRoutes::show_tracks_with_regions_at_playhead ()
1487 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1489 set<TimeAxisView*> show;
1490 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1491 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1497 suspend_redisplay ();
1499 TreeModel::Children rows = _model->children ();
1500 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1501 TimeAxisView* tv = (*i)[_columns.tv];
1502 (*i)[_columns.visible] = (show.find (tv) != show.end());
1505 resume_redisplay ();