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 "pbd/unknown_type.h"
28 #include "pbd/unwind.h"
30 #include "ardour/debug.h"
31 #include "ardour/route.h"
32 #include "ardour/midi_track.h"
33 #include "ardour/session.h"
35 #include "gtkmm2ext/cell_renderer_pixbuf_multi.h"
36 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
37 #include "gtkmm2ext/treeutils.h"
41 #include "ardour_ui.h"
42 #include "audio_time_axis.h"
43 #include "midi_time_axis.h"
44 #include "mixer_strip.h"
45 #include "gui_thread.h"
48 #include "route_sorter.h"
49 #include "editor_group_tabs.h"
50 #include "editor_routes.h"
55 using namespace ARDOUR;
58 using namespace Gtkmm2ext;
60 using Gtkmm2ext::Keyboard;
68 EditorRoutes::EditorRoutes (Editor* e)
70 , _ignore_reorder (false)
71 , _no_redisplay (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_name (X_("LHSList"));
226 _display.set_rules_hint (true);
227 _display.set_size_request (100, -1);
228 _display.add_object_drag (_columns.route.index(), "routes");
230 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
233 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
235 TreeViewColumn* name_column = _display.get_column (_name_column);
237 assert (name_column);
239 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
240 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
241 name_column->set_expand(true);
242 name_column->set_min_width(50);
244 name_cell->property_editable() = true;
245 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
247 // Set the visible column cell renderer to radio toggle
248 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
250 visible_cell->property_activatable() = true;
251 visible_cell->property_radio() = false;
252 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
254 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
255 visible_col->set_expand(false);
256 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
257 visible_col->set_fixed_width(30);
258 visible_col->set_alignment(ALIGN_CENTER);
260 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
262 active_cell->property_activatable() = true;
263 active_cell->property_radio() = false;
264 active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
266 TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
267 active_col->set_expand (false);
268 active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
269 active_col->set_fixed_width (30);
270 active_col->set_alignment (ALIGN_CENTER);
272 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
273 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
275 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
276 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
278 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
279 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
281 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
282 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
284 _display.set_enable_search (false);
286 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::sync_treeview_from_order_keys, this, _1), gui_context());
290 EditorRoutes::focus_in (GdkEventFocus*)
292 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
295 old_focus = win->get_focus ();
302 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
307 EditorRoutes::focus_out (GdkEventFocus*)
310 old_focus->grab_focus ();
318 EditorRoutes::enter_notify (GdkEventCrossing*)
324 /* arm counter so that ::selection_filter() will deny selecting anything for the
325 next two attempts to change selection status.
327 selection_countdown = 2;
328 _scroller.grab_focus ();
329 Keyboard::magic_widget_grab_focus ();
334 EditorRoutes::leave_notify (GdkEventCrossing*)
336 selection_countdown = 0;
339 old_focus->grab_focus ();
343 Keyboard::magic_widget_drop_focus ();
348 EditorRoutes::set_session (Session* s)
350 SessionHandlePtr::set_session (s);
355 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
356 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
361 EditorRoutes::on_input_active_changed (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<MidiTrack> mt;
371 mt = rtv->midi_track();
373 mt->set_input_active (!mt->input_active());
379 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
381 // Get the model row that has been toggled.
382 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
384 TimeAxisView* tv = row[_columns.tv];
385 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
387 if (rtv && rtv->track()) {
388 boost::shared_ptr<RouteList> rl (new RouteList);
389 rl->push_back (rtv->route());
390 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
395 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
397 // Get the model row that has been toggled.
398 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
400 TimeAxisView *tv = row[_columns.tv];
401 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
404 boost::shared_ptr<RouteList> rl (new RouteList);
405 rl->push_back (rtv->route());
406 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
411 EditorRoutes::on_tv_solo_enable_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 boost::shared_ptr<RouteList> rl (new RouteList);
421 rl->push_back (rtv->route());
422 if (Config->get_solo_control_is_listen_control()) {
423 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
425 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
431 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
433 // Get the model row that has been toggled.
434 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
436 TimeAxisView *tv = row[_columns.tv];
437 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
440 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
445 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
447 // Get the model row that has been toggled.
448 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
450 TimeAxisView *tv = row[_columns.tv];
451 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
454 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
459 EditorRoutes::build_menu ()
461 using namespace Menu_Helpers;
466 MenuList& items = _menu->items();
467 _menu->set_name ("ArdourContextMenu");
469 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
470 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
471 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
472 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
473 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
474 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
475 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
476 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
477 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
481 EditorRoutes::show_menu ()
487 _menu->popup (1, gtk_get_current_event_time());
491 EditorRoutes::redisplay ()
493 if (_no_redisplay || !_session || _session->deletion_in_progress()) {
497 TreeModel::Children rows = _model->children();
498 TreeModel::Children::iterator i;
501 /* n will be the count of tracks plus children (updated by TimeAxisView::show_at),
502 so we will use that to know where to put things.
506 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
507 TimeAxisView *tv = (*i)[_columns.tv];
508 boost::shared_ptr<Route> route = (*i)[_columns.route];
511 // just a "title" row
515 bool visible = tv->marked_for_display ();
517 /* show or hide the TimeAxisView */
519 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
520 tv->clip_to_viewport ();
528 /* whenever we go idle, update the track view list to reflect the new order.
529 we can't do this here, because we could mess up something that is traversing
530 the track order and has caused a redisplay of the list.
532 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
534 _editor->reset_controls_layout_height (position);
535 _editor->reset_controls_layout_width ();
536 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
537 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
539 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
541 We're increasing the size of the canvas while the bottom is visible.
542 We scroll down to keep in step with the controls layout.
544 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
549 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
551 /* this happens as the second step of a DnD within the treeview as well
552 as when a row/route is actually deleted.
554 DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview row deleted\n");
555 sync_order_keys_from_treeview ();
559 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
561 DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview reordered\n");
562 sync_order_keys_from_treeview ();
566 EditorRoutes::visible_changed (std::string const & path)
568 if (_session && _session->deletion_in_progress()) {
574 if ((iter = _model->get_iter (path))) {
575 TimeAxisView* tv = (*iter)[_columns.tv];
577 bool visible = (*iter)[_columns.visible];
579 if (tv->set_marked_for_display (!visible)) {
580 update_visibility ();
587 EditorRoutes::active_changed (std::string const & path)
589 if (_session && _session->deletion_in_progress ()) {
593 Gtk::TreeModel::Row row = *_model->get_iter (path);
594 boost::shared_ptr<Route> route = row[_columns.route];
595 bool const active = row[_columns.active];
596 route->set_active (!active, this);
600 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
604 suspend_redisplay ();
606 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
608 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
610 row = *(_model->append ());
612 row[_columns.text] = (*x)->route()->name();
613 row[_columns.visible] = (*x)->marked_for_display();
614 row[_columns.active] = (*x)->route()->active ();
615 row[_columns.tv] = *x;
616 row[_columns.route] = (*x)->route ();
617 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
620 row[_columns.is_input_active] = midi_trk->input_active ();
621 row[_columns.is_midi] = true;
623 row[_columns.is_input_active] = false;
624 row[_columns.is_midi] = false;
627 row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
628 row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
629 row[_columns.solo_visible] = !(*x)->route()->is_master ();
630 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
631 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
632 row[_columns.name_editable] = true;
634 boost::weak_ptr<Route> wr ((*x)->route());
636 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
637 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
639 if ((*x)->is_track()) {
640 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
641 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
644 if ((*x)->is_midi_track()) {
645 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
646 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
647 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
650 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
651 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
652 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
653 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
654 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
655 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
658 update_rec_display ();
659 update_mute_display ();
660 update_solo_display (true);
661 update_solo_isolate_display ();
662 update_solo_safe_display ();
663 update_input_active_display ();
664 update_active_display ();
667 /* now update route order keys from the treeview/track display order */
669 sync_order_keys_from_treeview ();
673 EditorRoutes::handle_gui_changes (string const & what, void*)
675 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
677 if (what == "track_height") {
678 /* Optional :make tracks change height while it happens, instead
681 //update_canvas_now ();
685 if (what == "visible_tracks") {
691 EditorRoutes::route_removed (TimeAxisView *tv)
693 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
695 TreeModel::Children rows = _model->children();
696 TreeModel::Children::iterator ri;
698 for (ri = rows.begin(); ri != rows.end(); ++ri) {
699 if ((*ri)[_columns.tv] == tv) {
705 /* the deleted signal for the treeview/model will take
711 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
713 if (!what_changed.contains (ARDOUR::Properties::name)) {
717 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
719 boost::shared_ptr<Route> route = r.lock ();
725 TreeModel::Children rows = _model->children();
726 TreeModel::Children::iterator i;
728 for (i = rows.begin(); i != rows.end(); ++i) {
729 boost::shared_ptr<Route> t = (*i)[_columns.route];
731 (*i)[_columns.text] = route->name();
738 EditorRoutes::update_active_display ()
740 TreeModel::Children rows = _model->children();
741 TreeModel::Children::iterator i;
743 for (i = rows.begin(); i != rows.end(); ++i) {
744 boost::shared_ptr<Route> route = (*i)[_columns.route];
745 (*i)[_columns.active] = route->active ();
750 EditorRoutes::update_visibility ()
752 TreeModel::Children rows = _model->children();
753 TreeModel::Children::iterator i;
755 suspend_redisplay ();
757 for (i = rows.begin(); i != rows.end(); ++i) {
758 TimeAxisView *tv = (*i)[_columns.tv];
759 (*i)[_columns.visible] = tv->marked_for_display ();
762 /* force route order keys catch up with visibility changes
765 sync_order_keys_from_treeview ();
771 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
773 TreeModel::Children rows = _model->children();
774 TreeModel::Children::iterator i;
776 for (i = rows.begin(); i != rows.end(); ++i) {
777 if ((*i)[_columns.tv] == &tv) {
778 tv.set_marked_for_display (false);
779 (*i)[_columns.visible] = false;
787 EditorRoutes::show_track_in_display (TimeAxisView& tv)
789 TreeModel::Children rows = _model->children();
790 TreeModel::Children::iterator i;
793 for (i = rows.begin(); i != rows.end(); ++i) {
794 if ((*i)[_columns.tv] == &tv) {
795 tv.set_marked_for_display (true);
796 (*i)[_columns.visible] = true;
804 EditorRoutes::reset_remote_control_ids ()
806 if (Config->get_remote_model() != EditorOrdered || !_session || _session->deletion_in_progress()) {
810 TreeModel::Children rows = _model->children();
817 DEBUG_TRACE (DEBUG::OrderKeys, "editor reset remote control ids\n");
819 TreeModel::Children::iterator ri;
820 bool rid_change = false;
822 uint32_t invisible_key = UINT32_MAX;
824 for (ri = rows.begin(); ri != rows.end(); ++ri) {
826 boost::shared_ptr<Route> route = (*ri)[_columns.route];
827 bool visible = (*ri)[_columns.visible];
830 if (!route->is_master() && !route->is_monitor()) {
832 uint32_t new_rid = (visible ? rid : invisible_key--);
834 if (new_rid != route->remote_control_id()) {
835 route->set_remote_control_id_from_order_key (EditorSort, new_rid);
847 /* tell the world that we changed the remote control IDs */
848 _session->notify_remote_id_change ();
854 EditorRoutes::sync_order_keys_from_treeview ()
856 if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
860 TreeModel::Children rows = _model->children();
867 DEBUG_TRACE (DEBUG::OrderKeys, "editor sync order keys from treeview\n");
869 TreeModel::Children::iterator ri;
870 bool changed = false;
871 bool rid_change = false;
874 uint32_t invisible_key = UINT32_MAX;
876 for (ri = rows.begin(); ri != rows.end(); ++ri) {
878 boost::shared_ptr<Route> route = (*ri)[_columns.route];
879 bool visible = (*ri)[_columns.visible];
881 uint32_t old_key = route->order_key (EditorSort);
883 if (order != old_key) {
884 route->set_order_key (EditorSort, order);
889 if ((Config->get_remote_model() == EditorOrdered) && !route->is_master() && !route->is_monitor()) {
891 uint32_t new_rid = (visible ? rid : invisible_key--);
893 if (new_rid != route->remote_control_id()) {
894 route->set_remote_control_id_from_order_key (EditorSort, new_rid);
908 /* tell the world that we changed the editor sort keys */
909 _session->sync_order_keys (EditorSort);
913 /* tell the world that we changed the remote control IDs */
914 _session->notify_remote_id_change ();
919 EditorRoutes::sync_treeview_from_order_keys (RouteSortOrderKey src)
921 /* Some route order key(s) for `src' has been changed, make sure that
922 we update out tree/list model and GUI to reflect the change.
925 if (!_session || _session->deletion_in_progress()) {
929 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("editor sync model from order keys, src = %1\n", enum_2_string (src)));
931 if (src == MixerSort) {
933 if (!Config->get_sync_all_route_ordering()) {
934 /* mixer sort keys changed - we don't care */
938 DEBUG_TRACE (DEBUG::OrderKeys, "reset editor order key to match mixer\n");
940 /* mixer sort keys were changed, update the editor sort
941 * keys since "sync mixer+editor order" is enabled.
944 boost::shared_ptr<RouteList> r = _session->get_routes ();
946 for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
947 (*i)->sync_order_keys (src);
951 /* we could get here after either a change in the Mixer or Editor sort
952 * order, but either way, the mixer order keys reflect the intended
953 * order for the GUI, so reorder the treeview model to match it.
956 vector<int> neworder;
957 TreeModel::Children rows = _model->children();
958 uint32_t old_order = 0;
959 bool changed = false;
965 OrderKeySortedRoutes sorted_routes;
967 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
968 boost::shared_ptr<Route> route = (*ri)[_columns.route];
969 sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key (EditorSort)));
972 SortByNewDisplayOrder cmp;
974 sort (sorted_routes.begin(), sorted_routes.end(), cmp);
975 neworder.assign (sorted_routes.size(), 0);
979 for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
981 neworder[n] = sr->old_display_order;
983 if (sr->old_display_order != n) {
987 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("EDITOR change order for %1 from %2 to %3\n",
988 sr->route->name(), sr->old_display_order, n));
992 Unwinder<bool> uw (_ignore_reorder, true);
993 _model->reorder (neworder);
1000 EditorRoutes::hide_all_tracks (bool /*with_select*/)
1002 TreeModel::Children rows = _model->children();
1003 TreeModel::Children::iterator i;
1005 suspend_redisplay ();
1007 for (i = rows.begin(); i != rows.end(); ++i) {
1009 TreeModel::Row row = (*i);
1010 TimeAxisView *tv = row[_columns.tv];
1016 row[_columns.visible] = false;
1019 resume_redisplay ();
1021 /* XXX this seems like a hack and half, but its not clear where to put this
1025 //reset_scrolling_region ();
1029 EditorRoutes::set_all_tracks_visibility (bool yn)
1031 TreeModel::Children rows = _model->children();
1032 TreeModel::Children::iterator i;
1034 suspend_redisplay ();
1036 for (i = rows.begin(); i != rows.end(); ++i) {
1038 TreeModel::Row row = (*i);
1039 TimeAxisView* tv = row[_columns.tv];
1045 (*i)[_columns.visible] = yn;
1048 /* force route order keys catch up with visibility changes
1051 sync_order_keys_from_treeview ();
1053 resume_redisplay ();
1057 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
1059 TreeModel::Children rows = _model->children();
1060 TreeModel::Children::iterator i;
1062 suspend_redisplay ();
1064 for (i = rows.begin(); i != rows.end(); ++i) {
1066 TreeModel::Row row = (*i);
1067 TimeAxisView* tv = row[_columns.tv];
1069 AudioTimeAxisView* atv;
1070 MidiTimeAxisView* mtv;
1076 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1079 (*i)[_columns.visible] = yn;
1083 if (atv->is_audio_track()) {
1084 (*i)[_columns.visible] = yn;
1089 if (!atv->is_audio_track()) {
1090 (*i)[_columns.visible] = yn;
1095 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
1098 (*i)[_columns.visible] = yn;
1102 if (mtv->is_midi_track()) {
1103 (*i)[_columns.visible] = yn;
1110 /* force route order keys catch up with visibility changes
1113 sync_order_keys_from_treeview ();
1115 resume_redisplay ();
1119 EditorRoutes::hide_all_routes ()
1121 set_all_tracks_visibility (false);
1125 EditorRoutes::show_all_routes ()
1127 set_all_tracks_visibility (true);
1131 EditorRoutes::show_all_audiotracks()
1133 set_all_audio_midi_visibility (1, true);
1136 EditorRoutes::hide_all_audiotracks ()
1138 set_all_audio_midi_visibility (1, false);
1142 EditorRoutes::show_all_audiobus ()
1144 set_all_audio_midi_visibility (2, true);
1147 EditorRoutes::hide_all_audiobus ()
1149 set_all_audio_midi_visibility (2, false);
1153 EditorRoutes::show_all_miditracks()
1155 set_all_audio_midi_visibility (3, true);
1158 EditorRoutes::hide_all_miditracks ()
1160 set_all_audio_midi_visibility (3, false);
1164 EditorRoutes::key_press (GdkEventKey* ev)
1166 TreeViewColumn *col;
1167 boost::shared_ptr<RouteList> rl (new RouteList);
1170 switch (ev->keyval) {
1172 case GDK_ISO_Left_Tab:
1174 /* If we appear to be editing something, leave that cleanly and appropriately.
1176 if (name_editable) {
1177 name_editable->editing_done ();
1181 col = _display.get_column (_name_column); // select&focus on name column
1183 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1184 treeview_select_previous (_display, _model, col);
1186 treeview_select_next (_display, _model, col);
1193 if (get_relevant_routes (rl)) {
1194 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1200 if (Config->get_solo_control_is_listen_control()) {
1201 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1203 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1209 if (get_relevant_routes (rl)) {
1210 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1222 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1225 RouteTimeAxisView* rtv;
1226 RefPtr<TreeSelection> selection = _display.get_selection();
1230 if (selection->count_selected_rows() != 0) {
1234 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1235 iter = selection->get_selected (tm);
1238 /* use mouse pointer */
1243 _display.get_pointer (x, y);
1244 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1246 if (_display.get_path_at_pos (bx, by, path)) {
1247 iter = _model->get_iter (path);
1252 tv = (*iter)[_columns.tv];
1254 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1256 rl->push_back (rtv->route());
1261 return !rl->empty();
1265 EditorRoutes::button_press (GdkEventButton* ev)
1267 if (Keyboard::is_context_menu_event (ev)) {
1272 TreeModel::Path path;
1273 TreeViewColumn *tvc;
1277 if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1278 /* cancel selection */
1279 _display.get_selection()->unselect_all ();
1280 /* end any editing by grabbing focus */
1281 _display.grab_focus ();
1285 //Scroll editor canvas to selected track
1286 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1288 // Get the model row.
1289 Gtk::TreeModel::Row row = *_model->get_iter (path);
1291 TimeAxisView *tv = row[_columns.tv];
1293 int y_pos = tv->y_position();
1295 //Clamp the y pos so that we do not extend beyond the canvas full height.
1296 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1297 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1300 //Only scroll to if the track is visible
1302 _editor->reset_y_origin (y_pos);
1310 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1312 if (selection_countdown) {
1313 if (--selection_countdown == 0) {
1316 /* no selection yet ... */
1323 struct EditorOrderRouteSorter {
1324 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1325 if (a->is_master()) {
1326 /* master before everything else */
1328 } else if (b->is_master()) {
1329 /* everything else before master */
1332 return a->order_key (EditorSort) < b->order_key (EditorSort);
1337 EditorRoutes::initial_display ()
1339 suspend_redisplay ();
1343 resume_redisplay ();
1347 boost::shared_ptr<RouteList> routes = _session->get_routes();
1349 if (ARDOUR_UI::instance()->session_is_new ()) {
1351 /* new session: stamp all routes with the right editor order
1355 _editor->add_routes (*(routes.get()));
1359 /* existing session: sort a copy of the route list by
1360 * editor-order and add its contents to the display.
1363 RouteList r (*routes);
1364 EditorOrderRouteSorter sorter;
1367 _editor->add_routes (r);
1371 resume_redisplay ();
1375 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1377 const SelectionData& data,
1378 guint info, guint time)
1380 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1381 _display.on_drag_data_received (context, x, y, data, info, time);
1385 context->drag_finish (true, false, time);
1389 EditorRoutes::move_selected_tracks (bool up)
1391 if (_editor->selection->tracks.empty()) {
1395 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1396 std::list<ViewRoute> view_routes;
1397 std::vector<int> neworder;
1398 TreeModel::Children rows = _model->children();
1399 TreeModel::Children::iterator ri;
1401 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1402 TimeAxisView* tv = (*ri)[_columns.tv];
1403 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1405 view_routes.push_back (ViewRoute (tv, route));
1408 list<ViewRoute>::iterator trailing;
1409 list<ViewRoute>::iterator leading;
1413 trailing = view_routes.begin();
1414 leading = view_routes.begin();
1418 while (leading != view_routes.end()) {
1419 if (_editor->selection->selected (leading->first)) {
1420 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1421 leading = view_routes.erase (leading);
1430 /* if we could use reverse_iterator in list::insert, this code
1431 would be a beautiful reflection of the code above. but we can't
1432 and so it looks like a bit of a mess.
1435 trailing = view_routes.end();
1436 leading = view_routes.end();
1438 --leading; if (leading == view_routes.begin()) { return; }
1444 if (_editor->selection->selected (leading->first)) {
1445 list<ViewRoute>::iterator tmp;
1447 /* need to insert *after* trailing, not *before* it,
1448 which is what insert (iter, val) normally does.
1454 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1456 /* can't use iter = cont.erase (iter); form here, because
1457 we need iter to move backwards.
1465 if (leading == view_routes.begin()) {
1466 /* the one we've just inserted somewhere else
1467 was the first in the list. erase this copy,
1468 and then break, because we're done.
1473 view_routes.erase (leading);
1482 if (leading == view_routes.begin()) {
1491 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1492 uint32_t order = leading->second->order_key (EditorSort);
1493 neworder.push_back (order);
1497 DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
1498 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1499 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("\t%1\n", *i));
1501 DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
1503 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1504 if (*i >= (int) neworder.size()) {
1505 cerr << "Trying to move something to " << *i << " of " << neworder.size() << endl;
1507 assert (*i < (int) neworder.size ());
1511 _model->reorder (neworder);
1515 EditorRoutes::update_input_active_display ()
1517 TreeModel::Children rows = _model->children();
1518 TreeModel::Children::iterator i;
1520 for (i = rows.begin(); i != rows.end(); ++i) {
1521 boost::shared_ptr<Route> route = (*i)[_columns.route];
1523 if (boost::dynamic_pointer_cast<Track> (route)) {
1524 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1527 (*i)[_columns.is_input_active] = mt->input_active();
1534 EditorRoutes::update_rec_display ()
1536 TreeModel::Children rows = _model->children();
1537 TreeModel::Children::iterator i;
1539 for (i = rows.begin(); i != rows.end(); ++i) {
1540 boost::shared_ptr<Route> route = (*i)[_columns.route];
1542 if (boost::dynamic_pointer_cast<Track> (route)) {
1543 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1545 if (route->record_enabled()) {
1546 if (_session->record_status() == Session::Recording) {
1547 (*i)[_columns.rec_state] = 1;
1549 (*i)[_columns.rec_state] = 2;
1551 } else if (mt && mt->step_editing()) {
1552 (*i)[_columns.rec_state] = 3;
1554 (*i)[_columns.rec_state] = 0;
1557 (*i)[_columns.name_editable] = !route->record_enabled ();
1563 EditorRoutes::update_mute_display ()
1565 TreeModel::Children rows = _model->children();
1566 TreeModel::Children::iterator i;
1568 for (i = rows.begin(); i != rows.end(); ++i) {
1569 boost::shared_ptr<Route> route = (*i)[_columns.route];
1570 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1575 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1577 TreeModel::Children rows = _model->children();
1578 TreeModel::Children::iterator i;
1580 for (i = rows.begin(); i != rows.end(); ++i) {
1581 boost::shared_ptr<Route> route = (*i)[_columns.route];
1582 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1587 EditorRoutes::update_solo_isolate_display ()
1589 TreeModel::Children rows = _model->children();
1590 TreeModel::Children::iterator i;
1592 for (i = rows.begin(); i != rows.end(); ++i) {
1593 boost::shared_ptr<Route> route = (*i)[_columns.route];
1594 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1599 EditorRoutes::update_solo_safe_display ()
1601 TreeModel::Children rows = _model->children();
1602 TreeModel::Children::iterator i;
1604 for (i = rows.begin(); i != rows.end(); ++i) {
1605 boost::shared_ptr<Route> route = (*i)[_columns.route];
1606 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1611 EditorRoutes::views () const
1613 list<TimeAxisView*> v;
1614 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1615 v.push_back ((*i)[_columns.tv]);
1622 EditorRoutes::clear ()
1624 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1626 _display.set_model (_model);
1630 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1634 /* give it a special name */
1636 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1639 e->set_name (X_("RouteNameEditorEntry"));
1644 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1648 TreeIter iter = _model->get_iter (path);
1654 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1656 if (route && route->name() != new_text) {
1657 route->set_name (new_text);
1662 EditorRoutes::solo_changed_so_update_mute ()
1664 update_mute_display ();
1668 EditorRoutes::show_tracks_with_regions_at_playhead ()
1670 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1672 set<TimeAxisView*> show;
1673 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1674 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1680 suspend_redisplay ();
1682 TreeModel::Children rows = _model->children ();
1683 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1684 TimeAxisView* tv = (*i)[_columns.tv];
1685 (*i)[_columns.visible] = (show.find (tv) != show.end());
1688 resume_redisplay ();