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)
72 , _adding_routes (false)
75 , selection_countdown (0)
78 static const int column_width = 22;
80 _scroller.add (_display);
81 _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
83 _model = ListStore::create (_columns);
84 _display.set_model (_model);
86 // Record enable toggle
87 CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
89 rec_col_renderer->set_pixbuf (0, ::get_icon("record-normal-disabled"));
90 rec_col_renderer->set_pixbuf (1, ::get_icon("record-normal-in-progress"));
91 rec_col_renderer->set_pixbuf (2, ::get_icon("record-normal-enabled"));
92 rec_col_renderer->set_pixbuf (3, ::get_icon("record-step"));
93 rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
95 TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
97 rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
98 rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
100 rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
101 rec_state_column->set_alignment(ALIGN_CENTER);
102 rec_state_column->set_expand(false);
103 rec_state_column->set_fixed_width(column_width);
107 CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti());
108 input_active_col_renderer->set_pixbuf (0, ::get_icon("midi-input-inactive"));
109 input_active_col_renderer->set_pixbuf (1, ::get_icon("midi-input-active"));
110 input_active_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed));
112 TreeViewColumn* input_active_column = manage (new TreeViewColumn ("I", *input_active_col_renderer));
114 input_active_column->add_attribute(input_active_col_renderer->property_state(), _columns.is_input_active);
115 input_active_column->add_attribute (input_active_col_renderer->property_visible(), _columns.is_midi);
117 input_active_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
118 input_active_column->set_alignment(ALIGN_CENTER);
119 input_active_column->set_expand(false);
120 input_active_column->set_fixed_width(column_width);
122 // Mute enable toggle
123 CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
125 mute_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("mute-disabled"));
126 mute_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("muted-by-others"));
127 mute_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("mute-enabled"));
128 mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
130 TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
132 mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
133 mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
134 mute_state_column->set_alignment(ALIGN_CENTER);
135 mute_state_column->set_expand(false);
136 mute_state_column->set_fixed_width(15);
138 // Solo enable toggle
139 CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
141 solo_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("solo-disabled"));
142 solo_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("solo-enabled"));
143 solo_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("soloed-by-others"));
144 solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
146 TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
148 solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
149 solo_state_column->add_attribute(solo_col_renderer->property_visible(), _columns.solo_visible);
150 solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
151 solo_state_column->set_alignment(ALIGN_CENTER);
152 solo_state_column->set_expand(false);
153 solo_state_column->set_fixed_width(column_width);
155 // Solo isolate toggle
156 CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
158 solo_iso_renderer->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
159 solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
160 solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
162 TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
164 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
165 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_visible(), _columns.solo_visible);
166 solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
167 solo_isolate_state_column->set_alignment(ALIGN_CENTER);
168 solo_isolate_state_column->set_expand(false);
169 solo_isolate_state_column->set_fixed_width(column_width);
172 CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
174 solo_safe_renderer->set_pixbuf (0, ::get_icon("solo-safe-disabled"));
175 solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-safe-enabled"));
176 solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
178 TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
179 solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
180 solo_safe_state_column->add_attribute(solo_safe_renderer->property_visible(), _columns.solo_visible);
181 solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
182 solo_safe_state_column->set_alignment(ALIGN_CENTER);
183 solo_safe_state_column->set_expand(false);
184 solo_safe_state_column->set_fixed_width(column_width);
186 _name_column = _display.append_column ("", _columns.text) - 1;
187 _visible_column = _display.append_column ("", _columns.visible) - 1;
188 _active_column = _display.append_column ("", _columns.active) - 1;
190 _display.append_column (*input_active_column);
191 _display.append_column (*rec_state_column);
192 _display.append_column (*mute_state_column);
193 _display.append_column (*solo_state_column);
194 _display.append_column (*solo_isolate_state_column);
195 _display.append_column (*solo_safe_state_column);
202 { 0, _("Name"), _("Track/Bus Name") },
203 { 1, _("V"), _("Track/Bus visible ?") },
204 { 2, _("A"), _("Track/Bus active ?") },
205 { 3, _("I"), _("MIDI input enabled") },
206 { 4, _("R"), _("Record enabled") },
207 { 5, _("M"), _("Muted") },
208 { 6, _("S"), _("Soloed") },
209 { 7, _("SI"), _("Solo Isolated") },
210 { 8, _("SS"), _("Solo Safe (Locked)") },
214 for (int i = 0; ci[i].index >= 0; ++i) {
215 col = _display.get_column (ci[i].index);
216 l = manage (new Label (ci[i].label));
217 ARDOUR_UI::instance()->set_tip (*l, ci[i].tooltip);
218 col->set_widget (*l);
222 _display.set_headers_visible (true);
223 _display.get_selection()->set_mode (SELECTION_SINGLE);
224 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
225 _display.set_reorderable (true);
226 _display.set_name (X_("EditGroupList"));
227 _display.set_rules_hint (true);
228 _display.set_size_request (100, -1);
229 _display.add_object_drag (_columns.route.index(), "routes");
231 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
234 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
236 TreeViewColumn* name_column = _display.get_column (_name_column);
238 assert (name_column);
240 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
241 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
242 name_column->set_expand(true);
243 name_column->set_min_width(50);
245 name_cell->property_editable() = true;
246 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
248 // Set the visible column cell renderer to radio toggle
249 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
251 visible_cell->property_activatable() = true;
252 visible_cell->property_radio() = false;
253 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
255 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
256 visible_col->set_expand(false);
257 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
258 visible_col->set_fixed_width(30);
259 visible_col->set_alignment(ALIGN_CENTER);
261 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
263 active_cell->property_activatable() = true;
264 active_cell->property_radio() = false;
265 active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
267 TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
268 active_col->set_expand (false);
269 active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
270 active_col->set_fixed_width (30);
271 active_col->set_alignment (ALIGN_CENTER);
273 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
274 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
276 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
277 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
279 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
280 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
282 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
283 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
285 _display.set_enable_search (false);
287 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::sync_treeview_from_order_keys, this), gui_context());
291 EditorRoutes::focus_in (GdkEventFocus*)
293 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
296 old_focus = win->get_focus ();
303 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
308 EditorRoutes::focus_out (GdkEventFocus*)
311 old_focus->grab_focus ();
319 EditorRoutes::enter_notify (GdkEventCrossing*)
325 /* arm counter so that ::selection_filter() will deny selecting anything for the
326 next two attempts to change selection status.
328 selection_countdown = 2;
329 _scroller.grab_focus ();
330 Keyboard::magic_widget_grab_focus ();
335 EditorRoutes::leave_notify (GdkEventCrossing*)
337 selection_countdown = 0;
340 old_focus->grab_focus ();
344 Keyboard::magic_widget_drop_focus ();
349 EditorRoutes::set_session (Session* s)
351 SessionHandlePtr::set_session (s);
356 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
357 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
362 EditorRoutes::on_input_active_changed (std::string const & path_string)
364 // Get the model row that has been toggled.
365 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
367 TimeAxisView* tv = row[_columns.tv];
368 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
371 boost::shared_ptr<MidiTrack> mt;
372 mt = rtv->midi_track();
374 mt->set_input_active (!mt->input_active());
380 EditorRoutes::on_tv_rec_enable_changed (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);
388 if (rtv && rtv->track()) {
389 boost::shared_ptr<RouteList> rl (new RouteList);
390 rl->push_back (rtv->route());
391 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
396 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
398 // Get the model row that has been toggled.
399 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
401 TimeAxisView *tv = row[_columns.tv];
402 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
405 boost::shared_ptr<RouteList> rl (new RouteList);
406 rl->push_back (rtv->route());
407 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
412 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
414 // Get the model row that has been toggled.
415 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
417 TimeAxisView *tv = row[_columns.tv];
418 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
421 boost::shared_ptr<RouteList> rl (new RouteList);
422 rl->push_back (rtv->route());
423 if (Config->get_solo_control_is_listen_control()) {
424 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
426 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
432 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
434 // Get the model row that has been toggled.
435 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
437 TimeAxisView *tv = row[_columns.tv];
438 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
441 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
446 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
448 // Get the model row that has been toggled.
449 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
451 TimeAxisView *tv = row[_columns.tv];
452 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
455 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
460 EditorRoutes::build_menu ()
462 using namespace Menu_Helpers;
467 MenuList& items = _menu->items();
468 _menu->set_name ("ArdourContextMenu");
470 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
471 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
472 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
473 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
474 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
475 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
476 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
477 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
478 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
482 EditorRoutes::show_menu ()
488 _menu->popup (1, gtk_get_current_event_time());
492 EditorRoutes::redisplay ()
494 if (_no_redisplay || !_session || _session->deletion_in_progress()) {
498 TreeModel::Children rows = _model->children();
499 TreeModel::Children::iterator i;
502 /* n will be the count of tracks plus children (updated by TimeAxisView::show_at),
503 so we will use that to know where to put things.
507 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
508 TimeAxisView *tv = (*i)[_columns.tv];
509 boost::shared_ptr<Route> route = (*i)[_columns.route];
512 // just a "title" row
516 bool visible = tv->marked_for_display ();
518 /* show or hide the TimeAxisView */
520 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
529 /* whenever we go idle, update the track view list to reflect the new order.
530 we can't do this here, because we could mess up something that is traversing
531 the track order and has caused a redisplay of the list.
533 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
535 _editor->reset_controls_layout_height (position);
536 _editor->reset_controls_layout_width ();
537 _editor->_full_canvas_height = position;
539 if ((_editor->vertical_adjustment.get_value() + _editor->_visible_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->_visible_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)
602 PBD::Unwinder<bool> at (_adding_routes, true);
604 bool from_scratch = (_model->children().size() == 0);
605 Gtk::TreeModel::Children::iterator insert_iter = _model->children().end();
607 for (Gtk::TreeModel::Children::iterator it = _model->children().begin(); it != _model->children().end(); ++it) {
608 boost::shared_ptr<Route> r = (*it)[_columns.route];
610 if (r->order_key() == (routes.front()->route()->order_key() + routes.size())) {
617 _editor->selection->tracks.clear();
620 suspend_redisplay ();
622 _display.set_model (Glib::RefPtr<ListStore>());
624 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
626 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
628 TreeModel::Row row = *(_model->insert (insert_iter));
630 row[_columns.text] = (*x)->route()->name();
631 row[_columns.visible] = (*x)->marked_for_display();
632 row[_columns.active] = (*x)->route()->active ();
633 row[_columns.tv] = *x;
634 row[_columns.route] = (*x)->route ();
635 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
638 row[_columns.is_input_active] = midi_trk->input_active ();
639 row[_columns.is_midi] = true;
641 row[_columns.is_input_active] = false;
642 row[_columns.is_midi] = false;
645 row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
646 row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
647 row[_columns.solo_visible] = !(*x)->route()->is_master ();
648 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
649 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
650 row[_columns.name_editable] = true;
653 _editor->selection->add(*x);
656 boost::weak_ptr<Route> wr ((*x)->route());
658 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
659 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
661 if ((*x)->is_track()) {
662 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
663 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
666 if ((*x)->is_midi_track()) {
667 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
668 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
669 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
672 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
673 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
674 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
675 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
676 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
677 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
681 update_rec_display ();
682 update_mute_display ();
683 update_solo_display (true);
684 update_solo_isolate_display ();
685 update_solo_safe_display ();
686 update_input_active_display ();
687 update_active_display ();
690 _display.set_model (_model);
692 /* now update route order keys from the treeview/track display order */
694 sync_order_keys_from_treeview ();
698 EditorRoutes::handle_gui_changes (string const & what, void*)
700 if (_adding_routes) {
704 if (what == "track_height") {
705 /* Optional :make tracks change height while it happens, instead
711 if (what == "visible_tracks") {
717 EditorRoutes::route_removed (TimeAxisView *tv)
719 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
721 TreeModel::Children rows = _model->children();
722 TreeModel::Children::iterator ri;
724 for (ri = rows.begin(); ri != rows.end(); ++ri) {
725 if ((*ri)[_columns.tv] == tv) {
731 /* the deleted signal for the treeview/model will take
737 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
739 if (!what_changed.contains (ARDOUR::Properties::name)) {
743 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
745 boost::shared_ptr<Route> route = r.lock ();
751 TreeModel::Children rows = _model->children();
752 TreeModel::Children::iterator i;
754 for (i = rows.begin(); i != rows.end(); ++i) {
755 boost::shared_ptr<Route> t = (*i)[_columns.route];
757 (*i)[_columns.text] = route->name();
764 EditorRoutes::update_active_display ()
766 TreeModel::Children rows = _model->children();
767 TreeModel::Children::iterator i;
769 for (i = rows.begin(); i != rows.end(); ++i) {
770 boost::shared_ptr<Route> route = (*i)[_columns.route];
771 (*i)[_columns.active] = route->active ();
776 EditorRoutes::update_visibility ()
778 TreeModel::Children rows = _model->children();
779 TreeModel::Children::iterator i;
781 suspend_redisplay ();
783 for (i = rows.begin(); i != rows.end(); ++i) {
784 TimeAxisView *tv = (*i)[_columns.tv];
785 (*i)[_columns.visible] = tv->marked_for_display ();
788 /* force route order keys catch up with visibility changes
791 sync_order_keys_from_treeview ();
797 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
799 TreeModel::Children rows = _model->children();
800 TreeModel::Children::iterator i;
802 for (i = rows.begin(); i != rows.end(); ++i) {
803 if ((*i)[_columns.tv] == &tv) {
804 tv.set_marked_for_display (false);
805 (*i)[_columns.visible] = false;
813 EditorRoutes::show_track_in_display (TimeAxisView& tv)
815 TreeModel::Children rows = _model->children();
816 TreeModel::Children::iterator i;
819 for (i = rows.begin(); i != rows.end(); ++i) {
820 if ((*i)[_columns.tv] == &tv) {
821 tv.set_marked_for_display (true);
822 (*i)[_columns.visible] = true;
830 EditorRoutes::reset_remote_control_ids ()
832 if (Config->get_remote_model() == UserOrdered || !_session || _session->deletion_in_progress()) {
836 TreeModel::Children rows = _model->children();
843 DEBUG_TRACE (DEBUG::OrderKeys, "editor reset remote control ids\n");
845 TreeModel::Children::iterator ri;
846 bool rid_change = false;
848 uint32_t invisible_key = UINT32_MAX;
850 for (ri = rows.begin(); ri != rows.end(); ++ri) {
852 boost::shared_ptr<Route> route = (*ri)[_columns.route];
853 bool visible = (*ri)[_columns.visible];
856 if (!route->is_master() && !route->is_monitor()) {
858 uint32_t new_rid = (visible ? rid : invisible_key--);
860 if (new_rid != route->remote_control_id()) {
861 route->set_remote_control_id_explicit (new_rid);
873 /* tell the world that we changed the remote control IDs */
874 _session->notify_remote_id_change ();
880 EditorRoutes::sync_order_keys_from_treeview ()
882 if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
886 TreeModel::Children rows = _model->children();
893 DEBUG_TRACE (DEBUG::OrderKeys, "editor sync order keys from treeview\n");
895 TreeModel::Children::iterator ri;
896 bool changed = false;
897 bool rid_change = false;
900 uint32_t invisible_key = UINT32_MAX;
902 for (ri = rows.begin(); ri != rows.end(); ++ri) {
904 boost::shared_ptr<Route> route = (*ri)[_columns.route];
905 bool visible = (*ri)[_columns.visible];
907 uint32_t old_key = route->order_key ();
909 if (order != old_key) {
910 route->set_order_key (order);
915 if ((Config->get_remote_model() == MixerOrdered) && !route->is_master() && !route->is_monitor()) {
917 uint32_t new_rid = (visible ? rid : invisible_key--);
919 if (new_rid != route->remote_control_id()) {
920 route->set_remote_control_id_explicit (new_rid);
934 /* tell the world that we changed the editor sort keys */
935 _session->sync_order_keys ();
939 /* tell the world that we changed the remote control IDs */
940 _session->notify_remote_id_change ();
945 EditorRoutes::sync_treeview_from_order_keys ()
947 /* Some route order key(s) have been changed, make sure that
948 we update out tree/list model and GUI to reflect the change.
951 if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
955 DEBUG_TRACE (DEBUG::OrderKeys, "editor sync model from order keys.\n");
957 /* we could get here after either a change in the Mixer or Editor sort
958 * order, but either way, the mixer order keys reflect the intended
959 * order for the GUI, so reorder the treeview model to match it.
962 vector<int> neworder;
963 TreeModel::Children rows = _model->children();
964 uint32_t old_order = 0;
965 bool changed = false;
971 OrderKeySortedRoutes sorted_routes;
973 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
974 boost::shared_ptr<Route> route = (*ri)[_columns.route];
975 sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key ()));
978 SortByNewDisplayOrder cmp;
980 sort (sorted_routes.begin(), sorted_routes.end(), cmp);
981 neworder.assign (sorted_routes.size(), 0);
985 for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
987 neworder[n] = sr->old_display_order;
989 if (sr->old_display_order != n) {
993 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("EDITOR change order for %1 from %2 to %3\n",
994 sr->route->name(), sr->old_display_order, n));
998 Unwinder<bool> uw (_ignore_reorder, true);
999 _model->reorder (neworder);
1006 EditorRoutes::hide_all_tracks (bool /*with_select*/)
1008 TreeModel::Children rows = _model->children();
1009 TreeModel::Children::iterator i;
1011 suspend_redisplay ();
1013 for (i = rows.begin(); i != rows.end(); ++i) {
1015 TreeModel::Row row = (*i);
1016 TimeAxisView *tv = row[_columns.tv];
1022 row[_columns.visible] = false;
1025 resume_redisplay ();
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 tv->set_marked_for_display (yn);
1046 (*i)[_columns.visible] = yn;
1049 /* force route order keys catch up with visibility changes
1052 sync_order_keys_from_treeview ();
1054 resume_redisplay ();
1058 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
1060 TreeModel::Children rows = _model->children();
1061 TreeModel::Children::iterator i;
1063 suspend_redisplay ();
1065 for (i = rows.begin(); i != rows.end(); ++i) {
1067 TreeModel::Row row = (*i);
1068 TimeAxisView* tv = row[_columns.tv];
1070 AudioTimeAxisView* atv;
1071 MidiTimeAxisView* mtv;
1077 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1080 (*i)[_columns.visible] = yn;
1084 if (atv->is_audio_track()) {
1085 (*i)[_columns.visible] = yn;
1090 if (!atv->is_audio_track()) {
1091 (*i)[_columns.visible] = yn;
1096 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
1099 (*i)[_columns.visible] = yn;
1103 if (mtv->is_midi_track()) {
1104 (*i)[_columns.visible] = yn;
1111 /* force route order keys catch up with visibility changes
1114 sync_order_keys_from_treeview ();
1116 resume_redisplay ();
1120 EditorRoutes::hide_all_routes ()
1122 set_all_tracks_visibility (false);
1126 EditorRoutes::show_all_routes ()
1128 set_all_tracks_visibility (true);
1132 EditorRoutes::show_all_audiotracks()
1134 set_all_audio_midi_visibility (1, true);
1137 EditorRoutes::hide_all_audiotracks ()
1139 set_all_audio_midi_visibility (1, false);
1143 EditorRoutes::show_all_audiobus ()
1145 set_all_audio_midi_visibility (2, true);
1148 EditorRoutes::hide_all_audiobus ()
1150 set_all_audio_midi_visibility (2, false);
1154 EditorRoutes::show_all_miditracks()
1156 set_all_audio_midi_visibility (3, true);
1159 EditorRoutes::hide_all_miditracks ()
1161 set_all_audio_midi_visibility (3, false);
1165 EditorRoutes::key_press (GdkEventKey* ev)
1167 TreeViewColumn *col;
1168 boost::shared_ptr<RouteList> rl (new RouteList);
1171 switch (ev->keyval) {
1173 case GDK_ISO_Left_Tab:
1175 /* If we appear to be editing something, leave that cleanly and appropriately.
1177 if (name_editable) {
1178 name_editable->editing_done ();
1182 col = _display.get_column (_name_column); // select&focus on name column
1184 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1185 treeview_select_previous (_display, _model, col);
1187 treeview_select_next (_display, _model, col);
1194 if (get_relevant_routes (rl)) {
1195 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1201 if (get_relevant_routes (rl)) {
1202 if (Config->get_solo_control_is_listen_control()) {
1203 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1205 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1212 if (get_relevant_routes (rl)) {
1213 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1225 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1228 RouteTimeAxisView* rtv;
1229 RefPtr<TreeSelection> selection = _display.get_selection();
1233 if (selection->count_selected_rows() != 0) {
1237 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1238 iter = selection->get_selected (tm);
1241 /* use mouse pointer */
1246 _display.get_pointer (x, y);
1247 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1249 if (_display.get_path_at_pos (bx, by, path)) {
1250 iter = _model->get_iter (path);
1255 tv = (*iter)[_columns.tv];
1257 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1259 rl->push_back (rtv->route());
1264 return !rl->empty();
1268 EditorRoutes::button_press (GdkEventButton* ev)
1270 if (Keyboard::is_context_menu_event (ev)) {
1275 TreeModel::Path path;
1276 TreeViewColumn *tvc;
1280 if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1281 /* cancel selection */
1282 _display.get_selection()->unselect_all ();
1283 /* end any editing by grabbing focus */
1284 _display.grab_focus ();
1288 //Scroll editor canvas to selected track
1289 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1291 // Get the model row.
1292 Gtk::TreeModel::Row row = *_model->get_iter (path);
1294 TimeAxisView *tv = row[_columns.tv];
1296 int y_pos = tv->y_position();
1298 //Clamp the y pos so that we do not extend beyond the canvas full height.
1299 if (_editor->_full_canvas_height - y_pos < _editor->_visible_canvas_height){
1300 y_pos = _editor->_full_canvas_height - _editor->_visible_canvas_height;
1303 //Only scroll to if the track is visible
1305 _editor->reset_y_origin (y_pos);
1313 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1315 if (selection_countdown) {
1316 if (--selection_countdown == 0) {
1319 /* no selection yet ... */
1326 struct EditorOrderRouteSorter {
1327 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1328 if (a->is_master()) {
1329 /* master before everything else */
1331 } else if (b->is_master()) {
1332 /* everything else before master */
1335 return a->order_key () < b->order_key ();
1340 EditorRoutes::initial_display ()
1342 suspend_redisplay ();
1346 resume_redisplay ();
1350 boost::shared_ptr<RouteList> routes = _session->get_routes();
1352 if (ARDOUR_UI::instance()->session_is_new ()) {
1354 /* new session: stamp all routes with the right editor order
1358 _editor->add_routes (*(routes.get()));
1362 /* existing session: sort a copy of the route list by
1363 * editor-order and add its contents to the display.
1366 RouteList r (*routes);
1367 EditorOrderRouteSorter sorter;
1370 _editor->add_routes (r);
1374 resume_redisplay ();
1378 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1380 const SelectionData& data,
1381 guint info, guint time)
1383 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1384 _display.on_drag_data_received (context, x, y, data, info, time);
1388 context->drag_finish (true, false, time);
1392 EditorRoutes::move_selected_tracks (bool up)
1394 if (_editor->selection->tracks.empty()) {
1398 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1399 std::list<ViewRoute> view_routes;
1400 std::vector<int> neworder;
1401 TreeModel::Children rows = _model->children();
1402 TreeModel::Children::iterator ri;
1404 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1405 TimeAxisView* tv = (*ri)[_columns.tv];
1406 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1408 view_routes.push_back (ViewRoute (tv, route));
1411 list<ViewRoute>::iterator trailing;
1412 list<ViewRoute>::iterator leading;
1416 trailing = view_routes.begin();
1417 leading = view_routes.begin();
1421 while (leading != view_routes.end()) {
1422 if (_editor->selection->selected (leading->first)) {
1423 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1424 leading = view_routes.erase (leading);
1433 /* if we could use reverse_iterator in list::insert, this code
1434 would be a beautiful reflection of the code above. but we can't
1435 and so it looks like a bit of a mess.
1438 trailing = view_routes.end();
1439 leading = view_routes.end();
1441 --leading; if (leading == view_routes.begin()) { return; }
1447 if (_editor->selection->selected (leading->first)) {
1448 list<ViewRoute>::iterator tmp;
1450 /* need to insert *after* trailing, not *before* it,
1451 which is what insert (iter, val) normally does.
1457 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1459 /* can't use iter = cont.erase (iter); form here, because
1460 we need iter to move backwards.
1468 if (leading == view_routes.begin()) {
1469 /* the one we've just inserted somewhere else
1470 was the first in the list. erase this copy,
1471 and then break, because we're done.
1476 view_routes.erase (leading);
1485 if (leading == view_routes.begin()) {
1494 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1495 uint32_t order = leading->second->order_key ();
1496 neworder.push_back (order);
1500 DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
1501 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1502 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("\t%1\n", *i));
1504 DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
1506 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1507 if (*i >= (int) neworder.size()) {
1508 cerr << "Trying to move something to " << *i << " of " << neworder.size() << endl;
1510 assert (*i < (int) neworder.size ());
1514 _model->reorder (neworder);
1518 EditorRoutes::update_input_active_display ()
1520 TreeModel::Children rows = _model->children();
1521 TreeModel::Children::iterator i;
1523 for (i = rows.begin(); i != rows.end(); ++i) {
1524 boost::shared_ptr<Route> route = (*i)[_columns.route];
1526 if (boost::dynamic_pointer_cast<Track> (route)) {
1527 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1530 (*i)[_columns.is_input_active] = mt->input_active();
1537 EditorRoutes::update_rec_display ()
1539 TreeModel::Children rows = _model->children();
1540 TreeModel::Children::iterator i;
1542 for (i = rows.begin(); i != rows.end(); ++i) {
1543 boost::shared_ptr<Route> route = (*i)[_columns.route];
1545 if (boost::dynamic_pointer_cast<Track> (route)) {
1546 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1548 if (route->record_enabled()) {
1549 if (_session->record_status() == Session::Recording) {
1550 (*i)[_columns.rec_state] = 1;
1552 (*i)[_columns.rec_state] = 2;
1554 } else if (mt && mt->step_editing()) {
1555 (*i)[_columns.rec_state] = 3;
1557 (*i)[_columns.rec_state] = 0;
1560 (*i)[_columns.name_editable] = !route->record_enabled ();
1566 EditorRoutes::update_mute_display ()
1568 TreeModel::Children rows = _model->children();
1569 TreeModel::Children::iterator i;
1571 for (i = rows.begin(); i != rows.end(); ++i) {
1572 boost::shared_ptr<Route> route = (*i)[_columns.route];
1573 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1578 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1580 TreeModel::Children rows = _model->children();
1581 TreeModel::Children::iterator i;
1583 for (i = rows.begin(); i != rows.end(); ++i) {
1584 boost::shared_ptr<Route> route = (*i)[_columns.route];
1585 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1590 EditorRoutes::update_solo_isolate_display ()
1592 TreeModel::Children rows = _model->children();
1593 TreeModel::Children::iterator i;
1595 for (i = rows.begin(); i != rows.end(); ++i) {
1596 boost::shared_ptr<Route> route = (*i)[_columns.route];
1597 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1602 EditorRoutes::update_solo_safe_display ()
1604 TreeModel::Children rows = _model->children();
1605 TreeModel::Children::iterator i;
1607 for (i = rows.begin(); i != rows.end(); ++i) {
1608 boost::shared_ptr<Route> route = (*i)[_columns.route];
1609 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1614 EditorRoutes::views () const
1616 list<TimeAxisView*> v;
1617 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1618 v.push_back ((*i)[_columns.tv]);
1625 EditorRoutes::clear ()
1627 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1629 _display.set_model (_model);
1633 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1637 /* give it a special name */
1639 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1642 e->set_name (X_("RouteNameEditorEntry"));
1647 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1651 TreeIter iter = _model->get_iter (path);
1657 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1659 if (route && route->name() != new_text) {
1660 route->set_name (new_text);
1665 EditorRoutes::solo_changed_so_update_mute ()
1667 update_mute_display ();
1671 EditorRoutes::show_tracks_with_regions_at_playhead ()
1673 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1675 set<TimeAxisView*> show;
1676 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1677 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1683 suspend_redisplay ();
1685 TreeModel::Children rows = _model->children ();
1686 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1687 TimeAxisView* tv = (*i)[_columns.tv];
1688 (*i)[_columns.visible] = (show.find (tv) != show.end());
1691 resume_redisplay ();