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 ();
1010 EditorRoutes::set_all_tracks_visibility (bool yn)
1012 TreeModel::Children rows = _model->children();
1013 TreeModel::Children::iterator i;
1015 suspend_redisplay ();
1017 for (i = rows.begin(); i != rows.end(); ++i) {
1019 TreeModel::Row row = (*i);
1020 TimeAxisView* tv = row[_columns.tv];
1026 tv->set_marked_for_display (yn);
1027 (*i)[_columns.visible] = yn;
1030 /* force route order keys catch up with visibility changes
1033 sync_order_keys_from_treeview ();
1035 resume_redisplay ();
1039 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
1041 TreeModel::Children rows = _model->children();
1042 TreeModel::Children::iterator i;
1044 suspend_redisplay ();
1046 for (i = rows.begin(); i != rows.end(); ++i) {
1048 TreeModel::Row row = (*i);
1049 TimeAxisView* tv = row[_columns.tv];
1051 AudioTimeAxisView* atv;
1052 MidiTimeAxisView* mtv;
1058 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1061 (*i)[_columns.visible] = yn;
1065 if (atv->is_audio_track()) {
1066 (*i)[_columns.visible] = yn;
1071 if (!atv->is_audio_track()) {
1072 (*i)[_columns.visible] = yn;
1077 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
1080 (*i)[_columns.visible] = yn;
1084 if (mtv->is_midi_track()) {
1085 (*i)[_columns.visible] = yn;
1092 /* force route order keys catch up with visibility changes
1095 sync_order_keys_from_treeview ();
1097 resume_redisplay ();
1101 EditorRoutes::hide_all_routes ()
1103 set_all_tracks_visibility (false);
1107 EditorRoutes::show_all_routes ()
1109 set_all_tracks_visibility (true);
1113 EditorRoutes::show_all_audiotracks()
1115 set_all_audio_midi_visibility (1, true);
1118 EditorRoutes::hide_all_audiotracks ()
1120 set_all_audio_midi_visibility (1, false);
1124 EditorRoutes::show_all_audiobus ()
1126 set_all_audio_midi_visibility (2, true);
1129 EditorRoutes::hide_all_audiobus ()
1131 set_all_audio_midi_visibility (2, false);
1135 EditorRoutes::show_all_miditracks()
1137 set_all_audio_midi_visibility (3, true);
1140 EditorRoutes::hide_all_miditracks ()
1142 set_all_audio_midi_visibility (3, false);
1146 EditorRoutes::key_press (GdkEventKey* ev)
1148 TreeViewColumn *col;
1149 boost::shared_ptr<RouteList> rl (new RouteList);
1152 switch (ev->keyval) {
1154 case GDK_ISO_Left_Tab:
1156 /* If we appear to be editing something, leave that cleanly and appropriately.
1158 if (name_editable) {
1159 name_editable->editing_done ();
1163 col = _display.get_column (_name_column); // select&focus on name column
1165 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1166 treeview_select_previous (_display, _model, col);
1168 treeview_select_next (_display, _model, col);
1175 if (get_relevant_routes (rl)) {
1176 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1182 if (get_relevant_routes (rl)) {
1183 if (Config->get_solo_control_is_listen_control()) {
1184 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1186 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1193 if (get_relevant_routes (rl)) {
1194 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1206 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1209 RouteTimeAxisView* rtv;
1210 RefPtr<TreeSelection> selection = _display.get_selection();
1214 if (selection->count_selected_rows() != 0) {
1218 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1219 iter = selection->get_selected (tm);
1222 /* use mouse pointer */
1227 _display.get_pointer (x, y);
1228 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1230 if (_display.get_path_at_pos (bx, by, path)) {
1231 iter = _model->get_iter (path);
1236 tv = (*iter)[_columns.tv];
1238 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1240 rl->push_back (rtv->route());
1245 return !rl->empty();
1249 EditorRoutes::button_press (GdkEventButton* ev)
1251 if (Keyboard::is_context_menu_event (ev)) {
1256 TreeModel::Path path;
1257 TreeViewColumn *tvc;
1261 if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1262 /* cancel selection */
1263 _display.get_selection()->unselect_all ();
1264 /* end any editing by grabbing focus */
1265 _display.grab_focus ();
1269 //Scroll editor canvas to selected track
1270 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1272 // Get the model row.
1273 Gtk::TreeModel::Row row = *_model->get_iter (path);
1275 TimeAxisView *tv = row[_columns.tv];
1277 int y_pos = tv->y_position();
1279 //Clamp the y pos so that we do not extend beyond the canvas full height.
1280 if (_editor->_full_canvas_height - y_pos < _editor->_visible_canvas_height){
1281 y_pos = _editor->_full_canvas_height - _editor->_visible_canvas_height;
1284 //Only scroll to if the track is visible
1286 _editor->reset_y_origin (y_pos);
1294 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1296 if (selection_countdown) {
1297 if (--selection_countdown == 0) {
1300 /* no selection yet ... */
1307 struct EditorOrderRouteSorter {
1308 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1309 if (a->is_master()) {
1310 /* master before everything else */
1312 } else if (b->is_master()) {
1313 /* everything else before master */
1316 return a->order_key () < b->order_key ();
1321 EditorRoutes::initial_display ()
1323 suspend_redisplay ();
1327 resume_redisplay ();
1331 boost::shared_ptr<RouteList> routes = _session->get_routes();
1333 if (ARDOUR_UI::instance()->session_is_new ()) {
1335 /* new session: stamp all routes with the right editor order
1339 _editor->add_routes (*(routes.get()));
1343 /* existing session: sort a copy of the route list by
1344 * editor-order and add its contents to the display.
1347 RouteList r (*routes);
1348 EditorOrderRouteSorter sorter;
1351 _editor->add_routes (r);
1355 resume_redisplay ();
1359 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1361 const SelectionData& data,
1362 guint info, guint time)
1364 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1365 _display.on_drag_data_received (context, x, y, data, info, time);
1369 context->drag_finish (true, false, time);
1373 EditorRoutes::move_selected_tracks (bool up)
1375 if (_editor->selection->tracks.empty()) {
1379 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1380 std::list<ViewRoute> view_routes;
1381 std::vector<int> neworder;
1382 TreeModel::Children rows = _model->children();
1383 TreeModel::Children::iterator ri;
1385 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1386 TimeAxisView* tv = (*ri)[_columns.tv];
1387 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1389 view_routes.push_back (ViewRoute (tv, route));
1392 list<ViewRoute>::iterator trailing;
1393 list<ViewRoute>::iterator leading;
1397 trailing = view_routes.begin();
1398 leading = view_routes.begin();
1402 while (leading != view_routes.end()) {
1403 if (_editor->selection->selected (leading->first)) {
1404 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1405 leading = view_routes.erase (leading);
1414 /* if we could use reverse_iterator in list::insert, this code
1415 would be a beautiful reflection of the code above. but we can't
1416 and so it looks like a bit of a mess.
1419 trailing = view_routes.end();
1420 leading = view_routes.end();
1422 --leading; if (leading == view_routes.begin()) { return; }
1428 if (_editor->selection->selected (leading->first)) {
1429 list<ViewRoute>::iterator tmp;
1431 /* need to insert *after* trailing, not *before* it,
1432 which is what insert (iter, val) normally does.
1438 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1440 /* can't use iter = cont.erase (iter); form here, because
1441 we need iter to move backwards.
1449 if (leading == view_routes.begin()) {
1450 /* the one we've just inserted somewhere else
1451 was the first in the list. erase this copy,
1452 and then break, because we're done.
1457 view_routes.erase (leading);
1466 if (leading == view_routes.begin()) {
1475 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1476 uint32_t order = leading->second->order_key ();
1477 neworder.push_back (order);
1481 DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
1482 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1483 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("\t%1\n", *i));
1485 DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
1487 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1488 if (*i >= (int) neworder.size()) {
1489 cerr << "Trying to move something to " << *i << " of " << neworder.size() << endl;
1491 assert (*i < (int) neworder.size ());
1495 _model->reorder (neworder);
1499 EditorRoutes::update_input_active_display ()
1501 TreeModel::Children rows = _model->children();
1502 TreeModel::Children::iterator i;
1504 for (i = rows.begin(); i != rows.end(); ++i) {
1505 boost::shared_ptr<Route> route = (*i)[_columns.route];
1507 if (boost::dynamic_pointer_cast<Track> (route)) {
1508 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1511 (*i)[_columns.is_input_active] = mt->input_active();
1518 EditorRoutes::update_rec_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);
1529 if (route->record_enabled()) {
1530 if (_session->record_status() == Session::Recording) {
1531 (*i)[_columns.rec_state] = 1;
1533 (*i)[_columns.rec_state] = 2;
1535 } else if (mt && mt->step_editing()) {
1536 (*i)[_columns.rec_state] = 3;
1538 (*i)[_columns.rec_state] = 0;
1541 (*i)[_columns.name_editable] = !route->record_enabled ();
1547 EditorRoutes::update_mute_display ()
1549 TreeModel::Children rows = _model->children();
1550 TreeModel::Children::iterator i;
1552 for (i = rows.begin(); i != rows.end(); ++i) {
1553 boost::shared_ptr<Route> route = (*i)[_columns.route];
1554 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1559 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1561 TreeModel::Children rows = _model->children();
1562 TreeModel::Children::iterator i;
1564 for (i = rows.begin(); i != rows.end(); ++i) {
1565 boost::shared_ptr<Route> route = (*i)[_columns.route];
1566 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1571 EditorRoutes::update_solo_isolate_display ()
1573 TreeModel::Children rows = _model->children();
1574 TreeModel::Children::iterator i;
1576 for (i = rows.begin(); i != rows.end(); ++i) {
1577 boost::shared_ptr<Route> route = (*i)[_columns.route];
1578 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1583 EditorRoutes::update_solo_safe_display ()
1585 TreeModel::Children rows = _model->children();
1586 TreeModel::Children::iterator i;
1588 for (i = rows.begin(); i != rows.end(); ++i) {
1589 boost::shared_ptr<Route> route = (*i)[_columns.route];
1590 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1595 EditorRoutes::views () const
1597 list<TimeAxisView*> v;
1598 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1599 v.push_back ((*i)[_columns.tv]);
1606 EditorRoutes::clear ()
1608 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1610 _display.set_model (_model);
1614 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1618 /* give it a special name */
1620 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1623 e->set_name (X_("RouteNameEditorEntry"));
1628 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1632 TreeIter iter = _model->get_iter (path);
1638 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1640 if (route && route->name() != new_text) {
1641 route->set_name (new_text);
1646 EditorRoutes::solo_changed_so_update_mute ()
1648 update_mute_display ();
1652 EditorRoutes::show_tracks_with_regions_at_playhead ()
1654 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1656 set<TimeAxisView*> show;
1657 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1658 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1664 suspend_redisplay ();
1666 TreeModel::Children rows = _model->children ();
1667 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1668 TimeAxisView* tv = (*i)[_columns.tv];
1669 (*i)[_columns.visible] = (show.find (tv) != show.end());
1672 resume_redisplay ();