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)
603 PBD::Unwinder<bool> at (_adding_routes, true);
605 suspend_redisplay ();
607 _display.set_model (Glib::RefPtr<ListStore>());
609 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
611 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
613 row = *(_model->append ());
615 row[_columns.text] = (*x)->route()->name();
616 row[_columns.visible] = (*x)->marked_for_display();
617 row[_columns.active] = (*x)->route()->active ();
618 row[_columns.tv] = *x;
619 row[_columns.route] = (*x)->route ();
620 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
623 row[_columns.is_input_active] = midi_trk->input_active ();
624 row[_columns.is_midi] = true;
626 row[_columns.is_input_active] = false;
627 row[_columns.is_midi] = false;
630 row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
631 row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
632 row[_columns.solo_visible] = !(*x)->route()->is_master ();
633 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
634 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
635 row[_columns.name_editable] = true;
637 boost::weak_ptr<Route> wr ((*x)->route());
639 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
640 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
642 if ((*x)->is_track()) {
643 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
644 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
647 if ((*x)->is_midi_track()) {
648 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
649 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
650 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
653 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
654 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
655 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
656 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
657 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
658 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
662 update_rec_display ();
663 update_mute_display ();
664 update_solo_display (true);
665 update_solo_isolate_display ();
666 update_solo_safe_display ();
667 update_input_active_display ();
668 update_active_display ();
671 _display.set_model (_model);
673 /* now update route order keys from the treeview/track display order */
675 sync_order_keys_from_treeview ();
679 EditorRoutes::handle_gui_changes (string const & what, void*)
681 if (_adding_routes) {
685 if (what == "track_height") {
686 /* Optional :make tracks change height while it happens, instead
692 if (what == "visible_tracks") {
698 EditorRoutes::route_removed (TimeAxisView *tv)
700 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
702 TreeModel::Children rows = _model->children();
703 TreeModel::Children::iterator ri;
705 for (ri = rows.begin(); ri != rows.end(); ++ri) {
706 if ((*ri)[_columns.tv] == tv) {
712 /* the deleted signal for the treeview/model will take
718 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
720 if (!what_changed.contains (ARDOUR::Properties::name)) {
724 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
726 boost::shared_ptr<Route> route = r.lock ();
732 TreeModel::Children rows = _model->children();
733 TreeModel::Children::iterator i;
735 for (i = rows.begin(); i != rows.end(); ++i) {
736 boost::shared_ptr<Route> t = (*i)[_columns.route];
738 (*i)[_columns.text] = route->name();
745 EditorRoutes::update_active_display ()
747 TreeModel::Children rows = _model->children();
748 TreeModel::Children::iterator i;
750 for (i = rows.begin(); i != rows.end(); ++i) {
751 boost::shared_ptr<Route> route = (*i)[_columns.route];
752 (*i)[_columns.active] = route->active ();
757 EditorRoutes::update_visibility ()
759 TreeModel::Children rows = _model->children();
760 TreeModel::Children::iterator i;
762 suspend_redisplay ();
764 for (i = rows.begin(); i != rows.end(); ++i) {
765 TimeAxisView *tv = (*i)[_columns.tv];
766 (*i)[_columns.visible] = tv->marked_for_display ();
769 /* force route order keys catch up with visibility changes
772 sync_order_keys_from_treeview ();
778 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
780 TreeModel::Children rows = _model->children();
781 TreeModel::Children::iterator i;
783 for (i = rows.begin(); i != rows.end(); ++i) {
784 if ((*i)[_columns.tv] == &tv) {
785 tv.set_marked_for_display (false);
786 (*i)[_columns.visible] = false;
794 EditorRoutes::show_track_in_display (TimeAxisView& tv)
796 TreeModel::Children rows = _model->children();
797 TreeModel::Children::iterator i;
800 for (i = rows.begin(); i != rows.end(); ++i) {
801 if ((*i)[_columns.tv] == &tv) {
802 tv.set_marked_for_display (true);
803 (*i)[_columns.visible] = true;
811 EditorRoutes::reset_remote_control_ids ()
813 if (Config->get_remote_model() == UserOrdered || !_session || _session->deletion_in_progress()) {
817 TreeModel::Children rows = _model->children();
824 DEBUG_TRACE (DEBUG::OrderKeys, "editor reset remote control ids\n");
826 TreeModel::Children::iterator ri;
827 bool rid_change = false;
829 uint32_t invisible_key = UINT32_MAX;
831 for (ri = rows.begin(); ri != rows.end(); ++ri) {
833 boost::shared_ptr<Route> route = (*ri)[_columns.route];
834 bool visible = (*ri)[_columns.visible];
837 if (!route->is_master() && !route->is_monitor()) {
839 uint32_t new_rid = (visible ? rid : invisible_key--);
841 if (new_rid != route->remote_control_id()) {
842 route->set_remote_control_id_explicit (new_rid);
854 /* tell the world that we changed the remote control IDs */
855 _session->notify_remote_id_change ();
861 EditorRoutes::sync_order_keys_from_treeview ()
863 if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
867 TreeModel::Children rows = _model->children();
874 DEBUG_TRACE (DEBUG::OrderKeys, "editor sync order keys from treeview\n");
876 TreeModel::Children::iterator ri;
877 bool changed = false;
878 bool rid_change = false;
881 uint32_t invisible_key = UINT32_MAX;
883 for (ri = rows.begin(); ri != rows.end(); ++ri) {
885 boost::shared_ptr<Route> route = (*ri)[_columns.route];
886 bool visible = (*ri)[_columns.visible];
888 uint32_t old_key = route->order_key ();
890 if (order != old_key) {
891 route->set_order_key (order);
896 if ((Config->get_remote_model() == MixerOrdered) && !route->is_master() && !route->is_monitor()) {
898 uint32_t new_rid = (visible ? rid : invisible_key--);
900 if (new_rid != route->remote_control_id()) {
901 route->set_remote_control_id_explicit (new_rid);
915 /* tell the world that we changed the editor sort keys */
916 _session->sync_order_keys ();
920 /* tell the world that we changed the remote control IDs */
921 _session->notify_remote_id_change ();
926 EditorRoutes::sync_treeview_from_order_keys ()
928 /* Some route order key(s) have been changed, make sure that
929 we update out tree/list model and GUI to reflect the change.
932 if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
936 DEBUG_TRACE (DEBUG::OrderKeys, "editor sync model from order keys.\n");
938 /* we could get here after either a change in the Mixer or Editor sort
939 * order, but either way, the mixer order keys reflect the intended
940 * order for the GUI, so reorder the treeview model to match it.
943 vector<int> neworder;
944 TreeModel::Children rows = _model->children();
945 uint32_t old_order = 0;
946 bool changed = false;
952 OrderKeySortedRoutes sorted_routes;
954 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
955 boost::shared_ptr<Route> route = (*ri)[_columns.route];
956 sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key ()));
959 SortByNewDisplayOrder cmp;
961 sort (sorted_routes.begin(), sorted_routes.end(), cmp);
962 neworder.assign (sorted_routes.size(), 0);
966 for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
968 neworder[n] = sr->old_display_order;
970 if (sr->old_display_order != n) {
974 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("EDITOR change order for %1 from %2 to %3\n",
975 sr->route->name(), sr->old_display_order, n));
979 Unwinder<bool> uw (_ignore_reorder, true);
980 _model->reorder (neworder);
987 EditorRoutes::hide_all_tracks (bool /*with_select*/)
989 TreeModel::Children rows = _model->children();
990 TreeModel::Children::iterator i;
992 suspend_redisplay ();
994 for (i = rows.begin(); i != rows.end(); ++i) {
996 TreeModel::Row row = (*i);
997 TimeAxisView *tv = row[_columns.tv];
1003 row[_columns.visible] = false;
1006 resume_redisplay ();
1008 /* XXX this seems like a hack and half, but its not clear where to put this
1012 //reset_scrolling_region ();
1016 EditorRoutes::set_all_tracks_visibility (bool yn)
1018 TreeModel::Children rows = _model->children();
1019 TreeModel::Children::iterator i;
1021 suspend_redisplay ();
1023 for (i = rows.begin(); i != rows.end(); ++i) {
1025 TreeModel::Row row = (*i);
1026 TimeAxisView* tv = row[_columns.tv];
1032 tv->set_marked_for_display (yn);
1033 (*i)[_columns.visible] = yn;
1036 /* force route order keys catch up with visibility changes
1039 sync_order_keys_from_treeview ();
1041 resume_redisplay ();
1045 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
1047 TreeModel::Children rows = _model->children();
1048 TreeModel::Children::iterator i;
1050 suspend_redisplay ();
1052 for (i = rows.begin(); i != rows.end(); ++i) {
1054 TreeModel::Row row = (*i);
1055 TimeAxisView* tv = row[_columns.tv];
1057 AudioTimeAxisView* atv;
1058 MidiTimeAxisView* mtv;
1064 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1067 (*i)[_columns.visible] = yn;
1071 if (atv->is_audio_track()) {
1072 (*i)[_columns.visible] = yn;
1077 if (!atv->is_audio_track()) {
1078 (*i)[_columns.visible] = yn;
1083 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
1086 (*i)[_columns.visible] = yn;
1090 if (mtv->is_midi_track()) {
1091 (*i)[_columns.visible] = yn;
1098 /* force route order keys catch up with visibility changes
1101 sync_order_keys_from_treeview ();
1103 resume_redisplay ();
1107 EditorRoutes::hide_all_routes ()
1109 set_all_tracks_visibility (false);
1113 EditorRoutes::show_all_routes ()
1115 set_all_tracks_visibility (true);
1119 EditorRoutes::show_all_audiotracks()
1121 set_all_audio_midi_visibility (1, true);
1124 EditorRoutes::hide_all_audiotracks ()
1126 set_all_audio_midi_visibility (1, false);
1130 EditorRoutes::show_all_audiobus ()
1132 set_all_audio_midi_visibility (2, true);
1135 EditorRoutes::hide_all_audiobus ()
1137 set_all_audio_midi_visibility (2, false);
1141 EditorRoutes::show_all_miditracks()
1143 set_all_audio_midi_visibility (3, true);
1146 EditorRoutes::hide_all_miditracks ()
1148 set_all_audio_midi_visibility (3, false);
1152 EditorRoutes::key_press (GdkEventKey* ev)
1154 TreeViewColumn *col;
1155 boost::shared_ptr<RouteList> rl (new RouteList);
1158 switch (ev->keyval) {
1160 case GDK_ISO_Left_Tab:
1162 /* If we appear to be editing something, leave that cleanly and appropriately.
1164 if (name_editable) {
1165 name_editable->editing_done ();
1169 col = _display.get_column (_name_column); // select&focus on name column
1171 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1172 treeview_select_previous (_display, _model, col);
1174 treeview_select_next (_display, _model, col);
1181 if (get_relevant_routes (rl)) {
1182 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1188 if (get_relevant_routes (rl)) {
1189 if (Config->get_solo_control_is_listen_control()) {
1190 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1192 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1199 if (get_relevant_routes (rl)) {
1200 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1212 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1215 RouteTimeAxisView* rtv;
1216 RefPtr<TreeSelection> selection = _display.get_selection();
1220 if (selection->count_selected_rows() != 0) {
1224 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1225 iter = selection->get_selected (tm);
1228 /* use mouse pointer */
1233 _display.get_pointer (x, y);
1234 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1236 if (_display.get_path_at_pos (bx, by, path)) {
1237 iter = _model->get_iter (path);
1242 tv = (*iter)[_columns.tv];
1244 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1246 rl->push_back (rtv->route());
1251 return !rl->empty();
1255 EditorRoutes::button_press (GdkEventButton* ev)
1257 if (Keyboard::is_context_menu_event (ev)) {
1262 TreeModel::Path path;
1263 TreeViewColumn *tvc;
1267 if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1268 /* cancel selection */
1269 _display.get_selection()->unselect_all ();
1270 /* end any editing by grabbing focus */
1271 _display.grab_focus ();
1275 //Scroll editor canvas to selected track
1276 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1278 // Get the model row.
1279 Gtk::TreeModel::Row row = *_model->get_iter (path);
1281 TimeAxisView *tv = row[_columns.tv];
1283 int y_pos = tv->y_position();
1285 //Clamp the y pos so that we do not extend beyond the canvas full height.
1286 if (_editor->_full_canvas_height - y_pos < _editor->_visible_canvas_height){
1287 y_pos = _editor->_full_canvas_height - _editor->_visible_canvas_height;
1290 //Only scroll to if the track is visible
1292 _editor->reset_y_origin (y_pos);
1300 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1302 if (selection_countdown) {
1303 if (--selection_countdown == 0) {
1306 /* no selection yet ... */
1313 struct EditorOrderRouteSorter {
1314 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1315 if (a->is_master()) {
1316 /* master before everything else */
1318 } else if (b->is_master()) {
1319 /* everything else before master */
1322 return a->order_key () < b->order_key ();
1327 EditorRoutes::initial_display ()
1329 suspend_redisplay ();
1333 resume_redisplay ();
1337 boost::shared_ptr<RouteList> routes = _session->get_routes();
1339 if (ARDOUR_UI::instance()->session_is_new ()) {
1341 /* new session: stamp all routes with the right editor order
1345 _editor->add_routes (*(routes.get()));
1349 /* existing session: sort a copy of the route list by
1350 * editor-order and add its contents to the display.
1353 RouteList r (*routes);
1354 EditorOrderRouteSorter sorter;
1357 _editor->add_routes (r);
1361 resume_redisplay ();
1365 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1367 const SelectionData& data,
1368 guint info, guint time)
1370 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1371 _display.on_drag_data_received (context, x, y, data, info, time);
1375 context->drag_finish (true, false, time);
1379 EditorRoutes::move_selected_tracks (bool up)
1381 if (_editor->selection->tracks.empty()) {
1385 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1386 std::list<ViewRoute> view_routes;
1387 std::vector<int> neworder;
1388 TreeModel::Children rows = _model->children();
1389 TreeModel::Children::iterator ri;
1391 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1392 TimeAxisView* tv = (*ri)[_columns.tv];
1393 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1395 view_routes.push_back (ViewRoute (tv, route));
1398 list<ViewRoute>::iterator trailing;
1399 list<ViewRoute>::iterator leading;
1403 trailing = view_routes.begin();
1404 leading = view_routes.begin();
1408 while (leading != view_routes.end()) {
1409 if (_editor->selection->selected (leading->first)) {
1410 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1411 leading = view_routes.erase (leading);
1420 /* if we could use reverse_iterator in list::insert, this code
1421 would be a beautiful reflection of the code above. but we can't
1422 and so it looks like a bit of a mess.
1425 trailing = view_routes.end();
1426 leading = view_routes.end();
1428 --leading; if (leading == view_routes.begin()) { return; }
1434 if (_editor->selection->selected (leading->first)) {
1435 list<ViewRoute>::iterator tmp;
1437 /* need to insert *after* trailing, not *before* it,
1438 which is what insert (iter, val) normally does.
1444 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1446 /* can't use iter = cont.erase (iter); form here, because
1447 we need iter to move backwards.
1455 if (leading == view_routes.begin()) {
1456 /* the one we've just inserted somewhere else
1457 was the first in the list. erase this copy,
1458 and then break, because we're done.
1463 view_routes.erase (leading);
1472 if (leading == view_routes.begin()) {
1481 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1482 uint32_t order = leading->second->order_key ();
1483 neworder.push_back (order);
1487 DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
1488 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1489 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("\t%1\n", *i));
1491 DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
1493 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1494 if (*i >= (int) neworder.size()) {
1495 cerr << "Trying to move something to " << *i << " of " << neworder.size() << endl;
1497 assert (*i < (int) neworder.size ());
1501 _model->reorder (neworder);
1505 EditorRoutes::update_input_active_display ()
1507 TreeModel::Children rows = _model->children();
1508 TreeModel::Children::iterator i;
1510 for (i = rows.begin(); i != rows.end(); ++i) {
1511 boost::shared_ptr<Route> route = (*i)[_columns.route];
1513 if (boost::dynamic_pointer_cast<Track> (route)) {
1514 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1517 (*i)[_columns.is_input_active] = mt->input_active();
1524 EditorRoutes::update_rec_display ()
1526 TreeModel::Children rows = _model->children();
1527 TreeModel::Children::iterator i;
1529 for (i = rows.begin(); i != rows.end(); ++i) {
1530 boost::shared_ptr<Route> route = (*i)[_columns.route];
1532 if (boost::dynamic_pointer_cast<Track> (route)) {
1533 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1535 if (route->record_enabled()) {
1536 if (_session->record_status() == Session::Recording) {
1537 (*i)[_columns.rec_state] = 1;
1539 (*i)[_columns.rec_state] = 2;
1541 } else if (mt && mt->step_editing()) {
1542 (*i)[_columns.rec_state] = 3;
1544 (*i)[_columns.rec_state] = 0;
1547 (*i)[_columns.name_editable] = !route->record_enabled ();
1553 EditorRoutes::update_mute_display ()
1555 TreeModel::Children rows = _model->children();
1556 TreeModel::Children::iterator i;
1558 for (i = rows.begin(); i != rows.end(); ++i) {
1559 boost::shared_ptr<Route> route = (*i)[_columns.route];
1560 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1565 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1567 TreeModel::Children rows = _model->children();
1568 TreeModel::Children::iterator i;
1570 for (i = rows.begin(); i != rows.end(); ++i) {
1571 boost::shared_ptr<Route> route = (*i)[_columns.route];
1572 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1577 EditorRoutes::update_solo_isolate_display ()
1579 TreeModel::Children rows = _model->children();
1580 TreeModel::Children::iterator i;
1582 for (i = rows.begin(); i != rows.end(); ++i) {
1583 boost::shared_ptr<Route> route = (*i)[_columns.route];
1584 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1589 EditorRoutes::update_solo_safe_display ()
1591 TreeModel::Children rows = _model->children();
1592 TreeModel::Children::iterator i;
1594 for (i = rows.begin(); i != rows.end(); ++i) {
1595 boost::shared_ptr<Route> route = (*i)[_columns.route];
1596 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1601 EditorRoutes::views () const
1603 list<TimeAxisView*> v;
1604 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1605 v.push_back ((*i)[_columns.tv]);
1612 EditorRoutes::clear ()
1614 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1616 _display.set_model (_model);
1620 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1624 /* give it a special name */
1626 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1629 e->set_name (X_("RouteNameEditorEntry"));
1634 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1638 TreeIter iter = _model->get_iter (path);
1644 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1646 if (route && route->name() != new_text) {
1647 route->set_name (new_text);
1652 EditorRoutes::solo_changed_so_update_mute ()
1654 update_mute_display ();
1658 EditorRoutes::show_tracks_with_regions_at_playhead ()
1660 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1662 set<TimeAxisView*> show;
1663 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1664 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1670 suspend_redisplay ();
1672 TreeModel::Children rows = _model->children ();
1673 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1674 TimeAxisView* tv = (*i)[_columns.tv];
1675 (*i)[_columns.visible] = (show.find (tv) != show.end());
1678 resume_redisplay ();