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);
521 tv->clip_to_viewport ();
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 + _editor->canvas_timebars_vsize;
538 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
540 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
542 We're increasing the size of the canvas while the bottom is visible.
543 We scroll down to keep in step with the controls layout.
545 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
550 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
552 /* this happens as the second step of a DnD within the treeview as well
553 as when a row/route is actually deleted.
555 DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview row deleted\n");
556 sync_order_keys_from_treeview ();
560 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
562 DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview reordered\n");
563 sync_order_keys_from_treeview ();
567 EditorRoutes::visible_changed (std::string const & path)
569 if (_session && _session->deletion_in_progress()) {
575 if ((iter = _model->get_iter (path))) {
576 TimeAxisView* tv = (*iter)[_columns.tv];
578 bool visible = (*iter)[_columns.visible];
580 if (tv->set_marked_for_display (!visible)) {
581 update_visibility ();
588 EditorRoutes::active_changed (std::string const & path)
590 if (_session && _session->deletion_in_progress ()) {
594 Gtk::TreeModel::Row row = *_model->get_iter (path);
595 boost::shared_ptr<Route> route = row[_columns.route];
596 bool const active = row[_columns.active];
597 route->set_active (!active, this);
601 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
603 PBD::Unwinder<bool> at (_adding_routes, true);
605 bool from_scratch = (_model->children().size() == 0);
606 Gtk::TreeModel::Children::iterator insert_iter = _model->children().end();
608 for (Gtk::TreeModel::Children::iterator it = _model->children().begin(); it != _model->children().end(); ++it) {
609 boost::shared_ptr<Route> r = (*it)[_columns.route];
611 if (r->order_key() == (routes.front()->route()->order_key() + routes.size())) {
618 _editor->selection->tracks.clear();
621 suspend_redisplay ();
623 _display.set_model (Glib::RefPtr<ListStore>());
625 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
627 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
629 TreeModel::Row row = *(_model->insert (insert_iter));
631 row[_columns.text] = (*x)->route()->name();
632 row[_columns.visible] = (*x)->marked_for_display();
633 row[_columns.active] = (*x)->route()->active ();
634 row[_columns.tv] = *x;
635 row[_columns.route] = (*x)->route ();
636 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
639 row[_columns.is_input_active] = midi_trk->input_active ();
640 row[_columns.is_midi] = true;
642 row[_columns.is_input_active] = false;
643 row[_columns.is_midi] = false;
646 row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
647 row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
648 row[_columns.solo_visible] = !(*x)->route()->is_master ();
649 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
650 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
651 row[_columns.name_editable] = true;
654 _editor->selection->add(*x);
657 boost::weak_ptr<Route> wr ((*x)->route());
659 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
660 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
662 if ((*x)->is_track()) {
663 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
664 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
667 if ((*x)->is_midi_track()) {
668 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
669 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
670 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
673 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
674 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
675 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
676 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
677 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
678 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
682 update_rec_display ();
683 update_mute_display ();
684 update_solo_display (true);
685 update_solo_isolate_display ();
686 update_solo_safe_display ();
687 update_input_active_display ();
688 update_active_display ();
691 _display.set_model (_model);
693 /* now update route order keys from the treeview/track display order */
695 sync_order_keys_from_treeview ();
699 EditorRoutes::handle_gui_changes (string const & what, void*)
701 if (_adding_routes) {
705 if (what == "track_height") {
706 /* Optional :make tracks change height while it happens, instead
709 //update_canvas_now ();
713 if (what == "visible_tracks") {
719 EditorRoutes::route_removed (TimeAxisView *tv)
721 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
723 TreeModel::Children rows = _model->children();
724 TreeModel::Children::iterator ri;
726 for (ri = rows.begin(); ri != rows.end(); ++ri) {
727 if ((*ri)[_columns.tv] == tv) {
733 /* the deleted signal for the treeview/model will take
739 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
741 if (!what_changed.contains (ARDOUR::Properties::name)) {
745 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
747 boost::shared_ptr<Route> route = r.lock ();
753 TreeModel::Children rows = _model->children();
754 TreeModel::Children::iterator i;
756 for (i = rows.begin(); i != rows.end(); ++i) {
757 boost::shared_ptr<Route> t = (*i)[_columns.route];
759 (*i)[_columns.text] = route->name();
766 EditorRoutes::update_active_display ()
768 TreeModel::Children rows = _model->children();
769 TreeModel::Children::iterator i;
771 for (i = rows.begin(); i != rows.end(); ++i) {
772 boost::shared_ptr<Route> route = (*i)[_columns.route];
773 (*i)[_columns.active] = route->active ();
778 EditorRoutes::update_visibility ()
780 TreeModel::Children rows = _model->children();
781 TreeModel::Children::iterator i;
783 suspend_redisplay ();
785 for (i = rows.begin(); i != rows.end(); ++i) {
786 TimeAxisView *tv = (*i)[_columns.tv];
787 (*i)[_columns.visible] = tv->marked_for_display ();
790 /* force route order keys catch up with visibility changes
793 sync_order_keys_from_treeview ();
799 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
801 TreeModel::Children rows = _model->children();
802 TreeModel::Children::iterator i;
804 for (i = rows.begin(); i != rows.end(); ++i) {
805 if ((*i)[_columns.tv] == &tv) {
806 tv.set_marked_for_display (false);
807 (*i)[_columns.visible] = false;
815 EditorRoutes::show_track_in_display (TimeAxisView& tv)
817 TreeModel::Children rows = _model->children();
818 TreeModel::Children::iterator i;
821 for (i = rows.begin(); i != rows.end(); ++i) {
822 if ((*i)[_columns.tv] == &tv) {
823 tv.set_marked_for_display (true);
824 (*i)[_columns.visible] = true;
832 EditorRoutes::reset_remote_control_ids ()
834 if (Config->get_remote_model() == UserOrdered || !_session || _session->deletion_in_progress()) {
838 TreeModel::Children rows = _model->children();
845 DEBUG_TRACE (DEBUG::OrderKeys, "editor reset remote control ids\n");
847 TreeModel::Children::iterator ri;
848 bool rid_change = false;
850 uint32_t invisible_key = UINT32_MAX;
852 for (ri = rows.begin(); ri != rows.end(); ++ri) {
854 boost::shared_ptr<Route> route = (*ri)[_columns.route];
855 bool visible = (*ri)[_columns.visible];
858 if (!route->is_master() && !route->is_monitor()) {
860 uint32_t new_rid = (visible ? rid : invisible_key--);
862 if (new_rid != route->remote_control_id()) {
863 route->set_remote_control_id_explicit (new_rid);
875 /* tell the world that we changed the remote control IDs */
876 _session->notify_remote_id_change ();
882 EditorRoutes::sync_order_keys_from_treeview ()
884 if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
888 TreeModel::Children rows = _model->children();
895 DEBUG_TRACE (DEBUG::OrderKeys, "editor sync order keys from treeview\n");
897 TreeModel::Children::iterator ri;
898 bool changed = false;
899 bool rid_change = false;
902 uint32_t invisible_key = UINT32_MAX;
904 for (ri = rows.begin(); ri != rows.end(); ++ri) {
906 boost::shared_ptr<Route> route = (*ri)[_columns.route];
907 bool visible = (*ri)[_columns.visible];
909 uint32_t old_key = route->order_key ();
911 if (order != old_key) {
912 route->set_order_key (order);
917 if ((Config->get_remote_model() == MixerOrdered) && !route->is_master() && !route->is_monitor()) {
919 uint32_t new_rid = (visible ? rid : invisible_key--);
921 if (new_rid != route->remote_control_id()) {
922 route->set_remote_control_id_explicit (new_rid);
936 /* tell the world that we changed the editor sort keys */
937 _session->sync_order_keys ();
941 /* tell the world that we changed the remote control IDs */
942 _session->notify_remote_id_change ();
947 EditorRoutes::sync_treeview_from_order_keys ()
949 /* Some route order key(s) have been changed, make sure that
950 we update out tree/list model and GUI to reflect the change.
953 if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
957 DEBUG_TRACE (DEBUG::OrderKeys, "editor sync model from order keys.\n");
959 /* we could get here after either a change in the Mixer or Editor sort
960 * order, but either way, the mixer order keys reflect the intended
961 * order for the GUI, so reorder the treeview model to match it.
964 vector<int> neworder;
965 TreeModel::Children rows = _model->children();
966 uint32_t old_order = 0;
967 bool changed = false;
973 OrderKeySortedRoutes sorted_routes;
975 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
976 boost::shared_ptr<Route> route = (*ri)[_columns.route];
977 sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key ()));
980 SortByNewDisplayOrder cmp;
982 sort (sorted_routes.begin(), sorted_routes.end(), cmp);
983 neworder.assign (sorted_routes.size(), 0);
987 for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
989 neworder[n] = sr->old_display_order;
991 if (sr->old_display_order != n) {
995 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("EDITOR change order for %1 from %2 to %3\n",
996 sr->route->name(), sr->old_display_order, n));
1000 Unwinder<bool> uw (_ignore_reorder, true);
1001 _model->reorder (neworder);
1008 EditorRoutes::hide_all_tracks (bool /*with_select*/)
1010 TreeModel::Children rows = _model->children();
1011 TreeModel::Children::iterator i;
1013 suspend_redisplay ();
1015 for (i = rows.begin(); i != rows.end(); ++i) {
1017 TreeModel::Row row = (*i);
1018 TimeAxisView *tv = row[_columns.tv];
1024 row[_columns.visible] = false;
1027 resume_redisplay ();
1029 /* XXX this seems like a hack and half, but its not clear where to put this
1033 //reset_scrolling_region ();
1037 EditorRoutes::set_all_tracks_visibility (bool yn)
1039 TreeModel::Children rows = _model->children();
1040 TreeModel::Children::iterator i;
1042 suspend_redisplay ();
1044 for (i = rows.begin(); i != rows.end(); ++i) {
1046 TreeModel::Row row = (*i);
1047 TimeAxisView* tv = row[_columns.tv];
1053 tv->set_marked_for_display (yn);
1054 (*i)[_columns.visible] = yn;
1057 /* force route order keys catch up with visibility changes
1060 sync_order_keys_from_treeview ();
1062 resume_redisplay ();
1066 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
1068 TreeModel::Children rows = _model->children();
1069 TreeModel::Children::iterator i;
1071 suspend_redisplay ();
1073 for (i = rows.begin(); i != rows.end(); ++i) {
1075 TreeModel::Row row = (*i);
1076 TimeAxisView* tv = row[_columns.tv];
1078 AudioTimeAxisView* atv;
1079 MidiTimeAxisView* mtv;
1085 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1088 (*i)[_columns.visible] = yn;
1092 if (atv->is_audio_track()) {
1093 (*i)[_columns.visible] = yn;
1098 if (!atv->is_audio_track()) {
1099 (*i)[_columns.visible] = yn;
1104 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
1107 (*i)[_columns.visible] = yn;
1111 if (mtv->is_midi_track()) {
1112 (*i)[_columns.visible] = yn;
1119 /* force route order keys catch up with visibility changes
1122 sync_order_keys_from_treeview ();
1124 resume_redisplay ();
1128 EditorRoutes::hide_all_routes ()
1130 set_all_tracks_visibility (false);
1134 EditorRoutes::show_all_routes ()
1136 set_all_tracks_visibility (true);
1140 EditorRoutes::show_all_audiotracks()
1142 set_all_audio_midi_visibility (1, true);
1145 EditorRoutes::hide_all_audiotracks ()
1147 set_all_audio_midi_visibility (1, false);
1151 EditorRoutes::show_all_audiobus ()
1153 set_all_audio_midi_visibility (2, true);
1156 EditorRoutes::hide_all_audiobus ()
1158 set_all_audio_midi_visibility (2, false);
1162 EditorRoutes::show_all_miditracks()
1164 set_all_audio_midi_visibility (3, true);
1167 EditorRoutes::hide_all_miditracks ()
1169 set_all_audio_midi_visibility (3, false);
1173 EditorRoutes::key_press (GdkEventKey* ev)
1175 TreeViewColumn *col;
1176 boost::shared_ptr<RouteList> rl (new RouteList);
1179 switch (ev->keyval) {
1181 case GDK_ISO_Left_Tab:
1183 /* If we appear to be editing something, leave that cleanly and appropriately.
1185 if (name_editable) {
1186 name_editable->editing_done ();
1190 col = _display.get_column (_name_column); // select&focus on name column
1192 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1193 treeview_select_previous (_display, _model, col);
1195 treeview_select_next (_display, _model, col);
1202 if (get_relevant_routes (rl)) {
1203 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1209 if (get_relevant_routes (rl)) {
1210 if (Config->get_solo_control_is_listen_control()) {
1211 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1213 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1220 if (get_relevant_routes (rl)) {
1221 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1233 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1236 RouteTimeAxisView* rtv;
1237 RefPtr<TreeSelection> selection = _display.get_selection();
1241 if (selection->count_selected_rows() != 0) {
1245 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1246 iter = selection->get_selected (tm);
1249 /* use mouse pointer */
1254 _display.get_pointer (x, y);
1255 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1257 if (_display.get_path_at_pos (bx, by, path)) {
1258 iter = _model->get_iter (path);
1263 tv = (*iter)[_columns.tv];
1265 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1267 rl->push_back (rtv->route());
1272 return !rl->empty();
1276 EditorRoutes::button_press (GdkEventButton* ev)
1278 if (Keyboard::is_context_menu_event (ev)) {
1283 TreeModel::Path path;
1284 TreeViewColumn *tvc;
1288 if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1289 /* cancel selection */
1290 _display.get_selection()->unselect_all ();
1291 /* end any editing by grabbing focus */
1292 _display.grab_focus ();
1296 //Scroll editor canvas to selected track
1297 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1299 // Get the model row.
1300 Gtk::TreeModel::Row row = *_model->get_iter (path);
1302 TimeAxisView *tv = row[_columns.tv];
1304 int y_pos = tv->y_position();
1306 //Clamp the y pos so that we do not extend beyond the canvas full height.
1307 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1308 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1311 //Only scroll to if the track is visible
1313 _editor->reset_y_origin (y_pos);
1321 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1323 if (selection_countdown) {
1324 if (--selection_countdown == 0) {
1327 /* no selection yet ... */
1334 struct EditorOrderRouteSorter {
1335 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1336 if (a->is_master()) {
1337 /* master before everything else */
1339 } else if (b->is_master()) {
1340 /* everything else before master */
1343 return a->order_key () < b->order_key ();
1348 EditorRoutes::initial_display ()
1350 suspend_redisplay ();
1354 resume_redisplay ();
1358 boost::shared_ptr<RouteList> routes = _session->get_routes();
1360 if (ARDOUR_UI::instance()->session_is_new ()) {
1362 /* new session: stamp all routes with the right editor order
1366 _editor->add_routes (*(routes.get()));
1370 /* existing session: sort a copy of the route list by
1371 * editor-order and add its contents to the display.
1374 RouteList r (*routes);
1375 EditorOrderRouteSorter sorter;
1378 _editor->add_routes (r);
1382 resume_redisplay ();
1386 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1388 const SelectionData& data,
1389 guint info, guint time)
1391 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1392 _display.on_drag_data_received (context, x, y, data, info, time);
1396 context->drag_finish (true, false, time);
1400 EditorRoutes::move_selected_tracks (bool up)
1402 if (_editor->selection->tracks.empty()) {
1406 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1407 std::list<ViewRoute> view_routes;
1408 std::vector<int> neworder;
1409 TreeModel::Children rows = _model->children();
1410 TreeModel::Children::iterator ri;
1412 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1413 TimeAxisView* tv = (*ri)[_columns.tv];
1414 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1416 view_routes.push_back (ViewRoute (tv, route));
1419 list<ViewRoute>::iterator trailing;
1420 list<ViewRoute>::iterator leading;
1424 trailing = view_routes.begin();
1425 leading = view_routes.begin();
1429 while (leading != view_routes.end()) {
1430 if (_editor->selection->selected (leading->first)) {
1431 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1432 leading = view_routes.erase (leading);
1441 /* if we could use reverse_iterator in list::insert, this code
1442 would be a beautiful reflection of the code above. but we can't
1443 and so it looks like a bit of a mess.
1446 trailing = view_routes.end();
1447 leading = view_routes.end();
1449 --leading; if (leading == view_routes.begin()) { return; }
1455 if (_editor->selection->selected (leading->first)) {
1456 list<ViewRoute>::iterator tmp;
1458 /* need to insert *after* trailing, not *before* it,
1459 which is what insert (iter, val) normally does.
1465 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1467 /* can't use iter = cont.erase (iter); form here, because
1468 we need iter to move backwards.
1476 if (leading == view_routes.begin()) {
1477 /* the one we've just inserted somewhere else
1478 was the first in the list. erase this copy,
1479 and then break, because we're done.
1484 view_routes.erase (leading);
1493 if (leading == view_routes.begin()) {
1502 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1503 uint32_t order = leading->second->order_key ();
1504 neworder.push_back (order);
1508 DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
1509 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1510 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("\t%1\n", *i));
1512 DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
1514 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1515 if (*i >= (int) neworder.size()) {
1516 cerr << "Trying to move something to " << *i << " of " << neworder.size() << endl;
1518 assert (*i < (int) neworder.size ());
1522 _model->reorder (neworder);
1526 EditorRoutes::update_input_active_display ()
1528 TreeModel::Children rows = _model->children();
1529 TreeModel::Children::iterator i;
1531 for (i = rows.begin(); i != rows.end(); ++i) {
1532 boost::shared_ptr<Route> route = (*i)[_columns.route];
1534 if (boost::dynamic_pointer_cast<Track> (route)) {
1535 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1538 (*i)[_columns.is_input_active] = mt->input_active();
1545 EditorRoutes::update_rec_display ()
1547 TreeModel::Children rows = _model->children();
1548 TreeModel::Children::iterator i;
1550 for (i = rows.begin(); i != rows.end(); ++i) {
1551 boost::shared_ptr<Route> route = (*i)[_columns.route];
1553 if (boost::dynamic_pointer_cast<Track> (route)) {
1554 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1556 if (route->record_enabled()) {
1557 if (_session->record_status() == Session::Recording) {
1558 (*i)[_columns.rec_state] = 1;
1560 (*i)[_columns.rec_state] = 2;
1562 } else if (mt && mt->step_editing()) {
1563 (*i)[_columns.rec_state] = 3;
1565 (*i)[_columns.rec_state] = 0;
1568 (*i)[_columns.name_editable] = !route->record_enabled ();
1574 EditorRoutes::update_mute_display ()
1576 TreeModel::Children rows = _model->children();
1577 TreeModel::Children::iterator i;
1579 for (i = rows.begin(); i != rows.end(); ++i) {
1580 boost::shared_ptr<Route> route = (*i)[_columns.route];
1581 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1586 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1588 TreeModel::Children rows = _model->children();
1589 TreeModel::Children::iterator i;
1591 for (i = rows.begin(); i != rows.end(); ++i) {
1592 boost::shared_ptr<Route> route = (*i)[_columns.route];
1593 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1598 EditorRoutes::update_solo_isolate_display ()
1600 TreeModel::Children rows = _model->children();
1601 TreeModel::Children::iterator i;
1603 for (i = rows.begin(); i != rows.end(); ++i) {
1604 boost::shared_ptr<Route> route = (*i)[_columns.route];
1605 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1610 EditorRoutes::update_solo_safe_display ()
1612 TreeModel::Children rows = _model->children();
1613 TreeModel::Children::iterator i;
1615 for (i = rows.begin(); i != rows.end(); ++i) {
1616 boost::shared_ptr<Route> route = (*i)[_columns.route];
1617 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1622 EditorRoutes::views () const
1624 list<TimeAxisView*> v;
1625 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1626 v.push_back ((*i)[_columns.tv]);
1633 EditorRoutes::clear ()
1635 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1637 _display.set_model (_model);
1641 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1645 /* give it a special name */
1647 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1650 e->set_name (X_("RouteNameEditorEntry"));
1655 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1659 TreeIter iter = _model->get_iter (path);
1665 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1667 if (route && route->name() != new_text) {
1668 route->set_name (new_text);
1673 EditorRoutes::solo_changed_so_update_mute ()
1675 update_mute_display ();
1679 EditorRoutes::show_tracks_with_regions_at_playhead ()
1681 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1683 set<TimeAxisView*> show;
1684 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1685 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1691 suspend_redisplay ();
1693 TreeModel::Children rows = _model->children ();
1694 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1695 TimeAxisView* tv = (*i)[_columns.tv];
1696 (*i)[_columns.visible] = (show.find (tv) != show.end());
1699 resume_redisplay ();