Maintain order keys even when routes are hidden, to prevent
[ardour2.git] / gtk2_ardour / editor_routes.cc
blob714c5faad92a2da2363ef3507259fb47fc81006a
1 /*
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.
20 #include <list>
21 #include <vector>
22 #include <algorithm>
23 #include <cstdlib>
24 #include <cmath>
25 #include <cassert>
27 #include "ardour/session.h"
29 #include "editor.h"
30 #include "keyboard.h"
31 #include "ardour_ui.h"
32 #include "audio_time_axis.h"
33 #include "midi_time_axis.h"
34 #include "mixer_strip.h"
35 #include "gui_thread.h"
36 #include "actions.h"
37 #include "utils.h"
38 #include "editor_group_tabs.h"
39 #include "editor_routes.h"
41 #include "pbd/unknown_type.h"
43 #include "ardour/route.h"
44 #include "ardour/midi_track.h"
46 #include "gtkmm2ext/cell_renderer_pixbuf_multi.h"
47 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
48 #include "gtkmm2ext/treeutils.h"
50 #include "i18n.h"
52 using namespace std;
53 using namespace ARDOUR;
54 using namespace PBD;
55 using namespace Gtk;
56 using namespace Gtkmm2ext;
57 using namespace Glib;
58 using Gtkmm2ext::Keyboard;
60 EditorRoutes::EditorRoutes (Editor* e)
61 : EditorComponent (e)
62 , _ignore_reorder (false)
63 , _no_redisplay (false)
64 , _redisplay_does_not_sync_order_keys (false)
65 , _redisplay_does_not_reset_order_keys (false)
66 ,_menu (0)
67 , old_focus (0)
68 , selection_countdown (0)
69 , name_editable (0)
71 static const int column_width = 22;
73 _scroller.add (_display);
74 _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
76 _model = ListStore::create (_columns);
77 _display.set_model (_model);
79 // Record enable toggle
80 CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
82 rec_col_renderer->set_pixbuf (0, ::get_icon("record-normal-disabled"));
83 rec_col_renderer->set_pixbuf (1, ::get_icon("record-normal-in-progress"));
84 rec_col_renderer->set_pixbuf (2, ::get_icon("record-normal-enabled"));
85 rec_col_renderer->set_pixbuf (3, ::get_icon("record-step"));
86 rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
88 TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
90 rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
91 rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
93 rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
94 rec_state_column->set_alignment(ALIGN_CENTER);
95 rec_state_column->set_expand(false);
96 rec_state_column->set_fixed_width(column_width);
98 // MIDI Input Active
100 CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti());
101 input_active_col_renderer->set_pixbuf (0, ::get_icon("midi-input-inactive"));
102 input_active_col_renderer->set_pixbuf (1, ::get_icon("midi-input-active"));
103 input_active_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed));
105 TreeViewColumn* input_active_column = manage (new TreeViewColumn ("I", *input_active_col_renderer));
107 input_active_column->add_attribute(input_active_col_renderer->property_state(), _columns.is_input_active);
108 input_active_column->add_attribute (input_active_col_renderer->property_visible(), _columns.is_midi);
110 input_active_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
111 input_active_column->set_alignment(ALIGN_CENTER);
112 input_active_column->set_expand(false);
113 input_active_column->set_fixed_width(column_width);
115 // Mute enable toggle
116 CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
118 mute_col_renderer->set_pixbuf (0, ::get_icon("mute-disabled"));
119 mute_col_renderer->set_pixbuf (1, ::get_icon("muted-by-others"));
120 mute_col_renderer->set_pixbuf (2, ::get_icon("mute-enabled"));
121 mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
123 TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
125 mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
126 mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
127 mute_state_column->set_alignment(ALIGN_CENTER);
128 mute_state_column->set_expand(false);
129 mute_state_column->set_fixed_width(15);
131 // Solo enable toggle
132 CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
134 solo_col_renderer->set_pixbuf (0, ::get_icon("solo-disabled"));
135 solo_col_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
136 solo_col_renderer->set_pixbuf (3, ::get_icon("soloed-by-others"));
137 solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
139 TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
141 solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
142 solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
143 solo_state_column->set_alignment(ALIGN_CENTER);
144 solo_state_column->set_expand(false);
145 solo_state_column->set_fixed_width(column_width);
147 // Solo isolate toggle
148 CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
150 solo_iso_renderer->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
151 solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
152 solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
154 TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
156 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
157 solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
158 solo_isolate_state_column->set_alignment(ALIGN_CENTER);
159 solo_isolate_state_column->set_expand(false);
160 solo_isolate_state_column->set_fixed_width(column_width);
162 // Solo safe toggle
163 CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
165 solo_safe_renderer->set_pixbuf (0, ::get_icon("solo-safe-disabled"));
166 solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-safe-enabled"));
167 solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
169 TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
170 solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
171 solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
172 solo_safe_state_column->set_alignment(ALIGN_CENTER);
173 solo_safe_state_column->set_expand(false);
174 solo_safe_state_column->set_fixed_width(column_width);
176 _display.append_column (*input_active_column);
177 _display.append_column (*rec_state_column);
178 _display.append_column (*mute_state_column);
179 _display.append_column (*solo_state_column);
180 _display.append_column (*solo_isolate_state_column);
181 _display.append_column (*solo_safe_state_column);
183 _name_column = _display.append_column (_("Name"), _columns.text) - 1;
184 _visible_column = _display.append_column (_("V"), _columns.visible) - 1;
186 _display.set_headers_visible (true);
187 _display.set_name ("TrackListDisplay");
188 _display.get_selection()->set_mode (SELECTION_SINGLE);
189 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
190 _display.set_reorderable (true);
191 _display.set_rules_hint (true);
192 _display.set_size_request (100, -1);
193 _display.add_object_drag (_columns.route.index(), "routes");
195 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
197 assert (name_cell);
198 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
200 TreeViewColumn* name_column = _display.get_column (_name_column);
202 assert (name_column);
204 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
205 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
206 name_column->set_expand(true);
207 name_column->set_min_width(50);
209 name_cell->property_editable() = true;
210 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
212 // Set the visible column cell renderer to radio toggle
213 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
215 visible_cell->property_activatable() = true;
216 visible_cell->property_radio() = false;
217 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
219 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
220 visible_col->set_expand(false);
221 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
222 visible_col->set_fixed_width(30);
223 visible_col->set_alignment(ALIGN_CENTER);
225 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
226 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
228 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
229 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
231 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
232 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
234 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
235 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
237 _display.set_enable_search (false);
239 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
242 bool
243 EditorRoutes::focus_in (GdkEventFocus*)
245 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
247 if (win) {
248 old_focus = win->get_focus ();
249 } else {
250 old_focus = 0;
253 name_editable = 0;
255 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
256 return true;
259 bool
260 EditorRoutes::focus_out (GdkEventFocus*)
262 if (old_focus) {
263 old_focus->grab_focus ();
264 old_focus = 0;
267 return false;
270 bool
271 EditorRoutes::enter_notify (GdkEventCrossing*)
273 if (name_editable) {
274 return true;
277 /* arm counter so that ::selection_filter() will deny selecting anything for the
278 next two attempts to change selection status.
280 selection_countdown = 2;
281 _scroller.grab_focus ();
282 Keyboard::magic_widget_grab_focus ();
283 return false;
286 bool
287 EditorRoutes::leave_notify (GdkEventCrossing*)
289 selection_countdown = 0;
291 if (old_focus) {
292 old_focus->grab_focus ();
293 old_focus = 0;
296 Keyboard::magic_widget_drop_focus ();
297 return false;
300 void
301 EditorRoutes::set_session (Session* s)
303 SessionHandlePtr::set_session (s);
305 initial_display ();
307 if (_session) {
308 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
309 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
313 void
314 EditorRoutes::on_input_active_changed (std::string const & path_string)
316 // Get the model row that has been toggled.
317 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
319 TimeAxisView* tv = row[_columns.tv];
320 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
322 if (rtv) {
323 boost::shared_ptr<MidiTrack> mt;
324 mt = rtv->midi_track();
325 if (mt) {
326 mt->set_input_active (!mt->input_active());
331 void
332 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
334 // Get the model row that has been toggled.
335 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
337 TimeAxisView* tv = row[_columns.tv];
338 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
340 if (rtv && rtv->track()) {
341 boost::shared_ptr<RouteList> rl (new RouteList);
342 rl->push_back (rtv->route());
343 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
347 void
348 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
350 // Get the model row that has been toggled.
351 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
353 TimeAxisView *tv = row[_columns.tv];
354 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
356 if (rtv != 0) {
357 boost::shared_ptr<RouteList> rl (new RouteList);
358 rl->push_back (rtv->route());
359 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
363 void
364 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
366 // Get the model row that has been toggled.
367 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
369 TimeAxisView *tv = row[_columns.tv];
370 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
372 if (rtv != 0) {
373 boost::shared_ptr<RouteList> rl (new RouteList);
374 rl->push_back (rtv->route());
375 if (Config->get_solo_control_is_listen_control()) {
376 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
377 } else {
378 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
383 void
384 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
386 // Get the model row that has been toggled.
387 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
389 TimeAxisView *tv = row[_columns.tv];
390 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
392 if (rtv) {
393 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
397 void
398 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
400 // Get the model row that has been toggled.
401 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
403 TimeAxisView *tv = row[_columns.tv];
404 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
406 if (rtv) {
407 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
411 void
412 EditorRoutes::build_menu ()
414 using namespace Menu_Helpers;
415 using namespace Gtk;
417 _menu = new Menu;
419 MenuList& items = _menu->items();
420 _menu->set_name ("ArdourContextMenu");
422 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
423 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
424 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
425 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
426 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
427 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
428 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
429 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
430 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
433 void
434 EditorRoutes::show_menu ()
436 if (_menu == 0) {
437 build_menu ();
440 _menu->popup (1, gtk_get_current_event_time());
443 void
444 EditorRoutes::redisplay ()
446 if (_no_redisplay || !_session) {
447 return;
450 TreeModel::Children rows = _model->children();
451 TreeModel::Children::iterator i;
452 uint32_t position;
453 int n;
455 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
456 TimeAxisView *tv = (*i)[_columns.tv];
457 boost::shared_ptr<Route> route = (*i)[_columns.route];
459 if (tv == 0) {
460 // just a "title" row
461 continue;
464 if (!_redisplay_does_not_reset_order_keys) {
465 /* this reorder is caused by user action, so reassign sort order keys
466 to tracks.
468 route->set_order_key (N_ ("editor"), n);
471 bool visible = tv->marked_for_display ();
473 /* show or hide the TimeAxisView */
474 if (visible) {
475 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
476 tv->clip_to_viewport ();
477 } else {
478 tv->hide ();
481 n++;
484 /* whenever we go idle, update the track view list to reflect the new order.
485 we can't do this here, because we could mess up something that is traversing
486 the track order and has caused a redisplay of the list.
488 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
490 _editor->reset_controls_layout_height (position);
491 _editor->reset_controls_layout_width ();
492 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
493 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
495 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
497 We're increasing the size of the canvas while the bottom is visible.
498 We scroll down to keep in step with the controls layout.
500 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
503 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
504 _session->sync_order_keys (N_ ("editor"));
508 void
509 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
511 if (!_session || _session->deletion_in_progress()) {
512 return;
515 /* this could require an order reset & sync */
516 _session->set_remote_control_ids();
517 _ignore_reorder = true;
518 redisplay ();
519 _ignore_reorder = false;
522 void
523 EditorRoutes::visible_changed (std::string const & path)
525 if (_session && _session->deletion_in_progress()) {
526 return;
529 TreeIter iter;
531 if ((iter = _model->get_iter (path))) {
532 TimeAxisView* tv = (*iter)[_columns.tv];
533 if (tv) {
534 bool visible = (*iter)[_columns.visible];
536 if (tv->set_marked_for_display (!visible)) {
537 _redisplay_does_not_reset_order_keys = true;
538 _session->set_remote_control_ids();
539 update_visibility ();
540 redisplay ();
541 _redisplay_does_not_reset_order_keys = false;
547 void
548 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
550 TreeModel::Row row;
552 _redisplay_does_not_sync_order_keys = true;
553 suspend_redisplay ();
555 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
557 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
559 row = *(_model->append ());
561 row[_columns.text] = (*x)->route()->name();
562 row[_columns.visible] = (*x)->marked_for_display();
563 row[_columns.tv] = *x;
564 row[_columns.route] = (*x)->route ();
565 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
567 if (midi_trk) {
568 row[_columns.is_input_active] = midi_trk->input_active ();
569 row[_columns.is_midi] = true;
570 } else {
571 row[_columns.is_input_active] = false;
572 row[_columns.is_midi] = false;
575 row[_columns.mute_state] = (*x)->route()->muted();
576 row[_columns.solo_state] = RouteUI::solo_visual_state ((*x)->route());
577 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
578 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
579 row[_columns.name_editable] = true;
581 _ignore_reorder = true;
583 /* added a new fresh one at the end */
584 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
585 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
588 _ignore_reorder = false;
590 boost::weak_ptr<Route> wr ((*x)->route());
592 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
593 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
595 if ((*x)->is_track()) {
596 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
597 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
600 if ((*x)->is_midi_track()) {
601 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
602 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
603 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
606 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
607 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
608 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
609 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
610 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
613 update_rec_display ();
614 update_mute_display ();
615 update_solo_display (true);
616 update_solo_isolate_display ();
617 update_solo_safe_display ();
618 update_input_active_display ();
619 resume_redisplay ();
620 _redisplay_does_not_sync_order_keys = false;
623 void
624 EditorRoutes::handle_gui_changes (string const & what, void*)
626 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
628 if (what == "track_height") {
629 /* Optional :make tracks change height while it happens, instead
630 of on first-idle
632 //update_canvas_now ();
633 redisplay ();
636 if (what == "visible_tracks") {
637 redisplay ();
641 void
642 EditorRoutes::route_removed (TimeAxisView *tv)
644 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
646 TreeModel::Children rows = _model->children();
647 TreeModel::Children::iterator ri;
649 /* the core model has changed, there is no need to sync
650 view orders.
653 _redisplay_does_not_sync_order_keys = true;
655 for (ri = rows.begin(); ri != rows.end(); ++ri) {
656 if ((*ri)[_columns.tv] == tv) {
657 _model->erase (ri);
658 break;
662 _redisplay_does_not_sync_order_keys = false;
665 void
666 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
668 if (!what_changed.contains (ARDOUR::Properties::name)) {
669 return;
672 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
674 boost::shared_ptr<Route> route = r.lock ();
676 if (!route) {
677 return;
680 TreeModel::Children rows = _model->children();
681 TreeModel::Children::iterator i;
683 for (i = rows.begin(); i != rows.end(); ++i) {
684 boost::shared_ptr<Route> t = (*i)[_columns.route];
685 if (t == route) {
686 (*i)[_columns.text] = route->name();
687 break;
692 void
693 EditorRoutes::update_visibility ()
695 TreeModel::Children rows = _model->children();
696 TreeModel::Children::iterator i;
698 suspend_redisplay ();
700 for (i = rows.begin(); i != rows.end(); ++i) {
701 TimeAxisView *tv = (*i)[_columns.tv];
702 (*i)[_columns.visible] = tv->marked_for_display ();
705 resume_redisplay ();
708 void
709 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
711 TreeModel::Children rows = _model->children();
712 TreeModel::Children::iterator i;
714 for (i = rows.begin(); i != rows.end(); ++i) {
715 if ((*i)[_columns.tv] == &tv) {
716 tv.set_marked_for_display (false);
717 (*i)[_columns.visible] = false;
718 redisplay ();
719 break;
724 void
725 EditorRoutes::show_track_in_display (TimeAxisView& tv)
727 TreeModel::Children rows = _model->children();
728 TreeModel::Children::iterator i;
731 for (i = rows.begin(); i != rows.end(); ++i) {
732 if ((*i)[_columns.tv] == &tv) {
733 tv.set_marked_for_display (true);
734 (*i)[_columns.visible] = true;
735 redisplay ();
736 break;
741 void
742 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
744 redisplay ();
747 /** If src != "editor", take editor order keys from each route and use them to rearrange the
748 * route list so that the visual arrangement of routes matches the order keys from the routes.
750 void
751 EditorRoutes::sync_order_keys (string const & src)
753 map<int, int> new_order;
754 TreeModel::Children rows = _model->children();
755 TreeModel::Children::iterator ri;
757 if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
758 return;
761 bool changed = false;
762 int order;
764 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
765 boost::shared_ptr<Route> route = (*ri)[_columns.route];
767 int const old_key = order;
768 int const new_key = route->order_key (N_ ("editor"));
770 new_order[new_key] = old_key;
772 if (new_key != old_key) {
773 changed = true;
777 if (changed) {
778 _redisplay_does_not_reset_order_keys = true;
780 /* `compact' new_order into a vector */
781 vector<int> co;
782 for (map<int, int>::const_iterator i = new_order.begin(); i != new_order.end(); ++i) {
783 co.push_back (i->second);
786 assert (co.size() == _model->children().size ());
788 _model->reorder (co);
789 _redisplay_does_not_reset_order_keys = false;
794 void
795 EditorRoutes::hide_all_tracks (bool /*with_select*/)
797 TreeModel::Children rows = _model->children();
798 TreeModel::Children::iterator i;
800 suspend_redisplay ();
802 for (i = rows.begin(); i != rows.end(); ++i) {
804 TreeModel::Row row = (*i);
805 TimeAxisView *tv = row[_columns.tv];
807 if (tv == 0) {
808 continue;
811 row[_columns.visible] = false;
814 resume_redisplay ();
816 /* XXX this seems like a hack and half, but its not clear where to put this
817 otherwise.
820 //reset_scrolling_region ();
823 void
824 EditorRoutes::set_all_tracks_visibility (bool yn)
826 TreeModel::Children rows = _model->children();
827 TreeModel::Children::iterator i;
829 suspend_redisplay ();
831 for (i = rows.begin(); i != rows.end(); ++i) {
833 TreeModel::Row row = (*i);
834 TimeAxisView* tv = row[_columns.tv];
836 if (tv == 0) {
837 continue;
840 (*i)[_columns.visible] = yn;
843 resume_redisplay ();
846 void
847 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
849 TreeModel::Children rows = _model->children();
850 TreeModel::Children::iterator i;
852 suspend_redisplay ();
854 for (i = rows.begin(); i != rows.end(); ++i) {
856 TreeModel::Row row = (*i);
857 TimeAxisView* tv = row[_columns.tv];
859 AudioTimeAxisView* atv;
860 MidiTimeAxisView* mtv;
862 if (tv == 0) {
863 continue;
866 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
867 switch (tracks) {
868 case 0:
869 (*i)[_columns.visible] = yn;
870 break;
872 case 1:
873 if (atv->is_audio_track()) {
874 (*i)[_columns.visible] = yn;
876 break;
878 case 2:
879 if (!atv->is_audio_track()) {
880 (*i)[_columns.visible] = yn;
882 break;
885 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
886 switch (tracks) {
887 case 0:
888 (*i)[_columns.visible] = yn;
889 break;
891 case 3:
892 if (mtv->is_midi_track()) {
893 (*i)[_columns.visible] = yn;
895 break;
900 resume_redisplay ();
903 void
904 EditorRoutes::hide_all_routes ()
906 set_all_tracks_visibility (false);
909 void
910 EditorRoutes::show_all_routes ()
912 set_all_tracks_visibility (true);
915 void
916 EditorRoutes::show_all_audiotracks()
918 set_all_audio_midi_visibility (1, true);
920 void
921 EditorRoutes::hide_all_audiotracks ()
923 set_all_audio_midi_visibility (1, false);
926 void
927 EditorRoutes::show_all_audiobus ()
929 set_all_audio_midi_visibility (2, true);
931 void
932 EditorRoutes::hide_all_audiobus ()
934 set_all_audio_midi_visibility (2, false);
937 void
938 EditorRoutes::show_all_miditracks()
940 set_all_audio_midi_visibility (3, true);
942 void
943 EditorRoutes::hide_all_miditracks ()
945 set_all_audio_midi_visibility (3, false);
948 bool
949 EditorRoutes::key_press (GdkEventKey* ev)
951 TreeViewColumn *col;
952 boost::shared_ptr<RouteList> rl (new RouteList);
953 TreePath path;
955 switch (ev->keyval) {
956 case GDK_Tab:
957 case GDK_ISO_Left_Tab:
959 /* If we appear to be editing something, leave that cleanly and appropriately.
961 if (name_editable) {
962 name_editable->editing_done ();
963 name_editable = 0;
966 col = _display.get_column (_name_column); // select&focus on name column
968 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
969 treeview_select_previous (_display, _model, col);
970 } else {
971 treeview_select_next (_display, _model, col);
974 return true;
975 break;
977 case 'm':
978 if (get_relevant_routes (rl)) {
979 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
981 return true;
982 break;
984 case 's':
985 if (Config->get_solo_control_is_listen_control()) {
986 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
987 } else {
988 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
990 return true;
991 break;
993 case 'r':
994 if (get_relevant_routes (rl)) {
995 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
997 break;
999 default:
1000 break;
1003 return false;
1006 bool
1007 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1009 TimeAxisView* tv;
1010 RouteTimeAxisView* rtv;
1011 RefPtr<TreeSelection> selection = _display.get_selection();
1012 TreePath path;
1013 TreeIter iter;
1015 if (selection->count_selected_rows() != 0) {
1017 /* use selection */
1019 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1020 iter = selection->get_selected (tm);
1022 } else {
1023 /* use mouse pointer */
1025 int x, y;
1026 int bx, by;
1028 _display.get_pointer (x, y);
1029 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1031 if (_display.get_path_at_pos (bx, by, path)) {
1032 iter = _model->get_iter (path);
1036 if (iter) {
1037 tv = (*iter)[_columns.tv];
1038 if (tv) {
1039 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1040 if (rtv) {
1041 rl->push_back (rtv->route());
1046 return !rl->empty();
1049 bool
1050 EditorRoutes::button_press (GdkEventButton* ev)
1052 if (Keyboard::is_context_menu_event (ev)) {
1053 show_menu ();
1054 return true;
1057 //Scroll editor canvas to selected track
1058 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1060 TreeModel::Path path;
1061 TreeViewColumn *tvc;
1062 int cell_x;
1063 int cell_y;
1065 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
1067 // Get the model row.
1068 Gtk::TreeModel::Row row = *_model->get_iter (path);
1070 TimeAxisView *tv = row[_columns.tv];
1072 int y_pos = tv->y_position();
1074 //Clamp the y pos so that we do not extend beyond the canvas full height.
1075 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1076 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1079 //Only scroll to if the track is visible
1080 if(y_pos != -1){
1081 _editor->reset_y_origin (y_pos);
1085 return false;
1088 bool
1089 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1091 if (selection_countdown) {
1092 if (--selection_countdown == 0) {
1093 return true;
1094 } else {
1095 /* no selection yet ... */
1096 return false;
1099 return true;
1102 struct EditorOrderRouteSorter {
1103 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1104 /* use of ">" forces the correct sort order */
1105 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1109 void
1110 EditorRoutes::initial_display ()
1112 suspend_redisplay ();
1113 _model->clear ();
1115 if (!_session) {
1116 resume_redisplay ();
1117 return;
1120 boost::shared_ptr<RouteList> routes = _session->get_routes();
1121 RouteList r (*routes);
1122 EditorOrderRouteSorter sorter;
1124 r.sort (sorter);
1125 _editor->handle_new_route (r);
1127 /* don't show master bus in a new session */
1129 if (ARDOUR_UI::instance()->session_is_new ()) {
1131 TreeModel::Children rows = _model->children();
1132 TreeModel::Children::iterator i;
1134 _no_redisplay = true;
1136 for (i = rows.begin(); i != rows.end(); ++i) {
1138 TimeAxisView *tv = (*i)[_columns.tv];
1139 RouteTimeAxisView *rtv;
1141 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1142 if (rtv->route()->is_master()) {
1143 _display.get_selection()->unselect (i);
1148 _no_redisplay = false;
1149 redisplay ();
1152 resume_redisplay ();
1155 void
1156 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1158 _redisplay_does_not_sync_order_keys = true;
1159 _session->set_remote_control_ids();
1160 redisplay ();
1161 _redisplay_does_not_sync_order_keys = false;
1164 void
1165 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1166 int x, int y,
1167 const SelectionData& data,
1168 guint info, guint time)
1170 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1171 _display.on_drag_data_received (context, x, y, data, info, time);
1172 return;
1175 context->drag_finish (true, false, time);
1178 void
1179 EditorRoutes::move_selected_tracks (bool up)
1181 if (_editor->selection->tracks.empty()) {
1182 return;
1185 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1186 std::list<ViewRoute> view_routes;
1187 std::vector<int> neworder;
1188 TreeModel::Children rows = _model->children();
1189 TreeModel::Children::iterator ri;
1191 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1192 TimeAxisView* tv = (*ri)[_columns.tv];
1193 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1195 view_routes.push_back (ViewRoute (tv, route));
1198 list<ViewRoute>::iterator trailing;
1199 list<ViewRoute>::iterator leading;
1201 if (up) {
1203 trailing = view_routes.begin();
1204 leading = view_routes.begin();
1206 ++leading;
1208 while (leading != view_routes.end()) {
1209 if (_editor->selection->selected (leading->first)) {
1210 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1211 leading = view_routes.erase (leading);
1212 } else {
1213 ++leading;
1214 ++trailing;
1218 } else {
1220 /* if we could use reverse_iterator in list::insert, this code
1221 would be a beautiful reflection of the code above. but we can't
1222 and so it looks like a bit of a mess.
1225 trailing = view_routes.end();
1226 leading = view_routes.end();
1228 --leading; if (leading == view_routes.begin()) { return; }
1229 --leading;
1230 --trailing;
1232 while (1) {
1234 if (_editor->selection->selected (leading->first)) {
1235 list<ViewRoute>::iterator tmp;
1237 /* need to insert *after* trailing, not *before* it,
1238 which is what insert (iter, val) normally does.
1241 tmp = trailing;
1242 tmp++;
1244 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1246 /* can't use iter = cont.erase (iter); form here, because
1247 we need iter to move backwards.
1250 tmp = leading;
1251 --tmp;
1253 bool done = false;
1255 if (leading == view_routes.begin()) {
1256 /* the one we've just inserted somewhere else
1257 was the first in the list. erase this copy,
1258 and then break, because we're done.
1260 done = true;
1263 view_routes.erase (leading);
1265 if (done) {
1266 break;
1269 leading = tmp;
1271 } else {
1272 if (leading == view_routes.begin()) {
1273 break;
1275 --leading;
1276 --trailing;
1281 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1282 neworder.push_back (leading->second->order_key (N_ ("editor")));
1285 _model->reorder (neworder);
1287 _session->sync_order_keys (N_ ("editor"));
1290 void
1291 EditorRoutes::update_input_active_display ()
1293 TreeModel::Children rows = _model->children();
1294 TreeModel::Children::iterator i;
1296 for (i = rows.begin(); i != rows.end(); ++i) {
1297 boost::shared_ptr<Route> route = (*i)[_columns.route];
1299 if (boost::dynamic_pointer_cast<Track> (route)) {
1300 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1302 if (mt) {
1303 (*i)[_columns.is_input_active] = mt->input_active();
1309 void
1310 EditorRoutes::update_rec_display ()
1312 TreeModel::Children rows = _model->children();
1313 TreeModel::Children::iterator i;
1315 for (i = rows.begin(); i != rows.end(); ++i) {
1316 boost::shared_ptr<Route> route = (*i)[_columns.route];
1318 if (boost::dynamic_pointer_cast<Track> (route)) {
1319 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1321 if (route->record_enabled()) {
1322 if (_session->record_status() == Session::Recording) {
1323 (*i)[_columns.rec_state] = 1;
1324 } else {
1325 (*i)[_columns.rec_state] = 2;
1327 } else if (mt && mt->step_editing()) {
1328 (*i)[_columns.rec_state] = 3;
1329 } else {
1330 (*i)[_columns.rec_state] = 0;
1333 (*i)[_columns.name_editable] = !route->record_enabled ();
1338 void
1339 EditorRoutes::update_mute_display ()
1341 TreeModel::Children rows = _model->children();
1342 TreeModel::Children::iterator i;
1344 for (i = rows.begin(); i != rows.end(); ++i) {
1345 boost::shared_ptr<Route> route = (*i)[_columns.route];
1346 (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
1350 void
1351 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1353 TreeModel::Children rows = _model->children();
1354 TreeModel::Children::iterator i;
1356 for (i = rows.begin(); i != rows.end(); ++i) {
1357 boost::shared_ptr<Route> route = (*i)[_columns.route];
1358 (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
1362 void
1363 EditorRoutes::update_solo_isolate_display ()
1365 TreeModel::Children rows = _model->children();
1366 TreeModel::Children::iterator i;
1368 for (i = rows.begin(); i != rows.end(); ++i) {
1369 boost::shared_ptr<Route> route = (*i)[_columns.route];
1370 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
1374 void
1375 EditorRoutes::update_solo_safe_display ()
1377 TreeModel::Children rows = _model->children();
1378 TreeModel::Children::iterator i;
1380 for (i = rows.begin(); i != rows.end(); ++i) {
1381 boost::shared_ptr<Route> route = (*i)[_columns.route];
1382 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
1386 list<TimeAxisView*>
1387 EditorRoutes::views () const
1389 list<TimeAxisView*> v;
1390 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1391 v.push_back ((*i)[_columns.tv]);
1394 return v;
1397 void
1398 EditorRoutes::clear ()
1400 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1401 _model->clear ();
1402 _display.set_model (_model);
1405 void
1406 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1408 name_editable = ce;
1410 /* give it a special name */
1412 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1414 if (e) {
1415 e->set_name (X_("RouteNameEditorEntry"));
1419 void
1420 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1422 name_editable = 0;
1424 TreeIter iter = _model->get_iter (path);
1426 if (!iter) {
1427 return;
1430 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1432 if (route && route->name() != new_text) {
1433 route->set_name (new_text);
1437 void
1438 EditorRoutes::solo_changed_so_update_mute ()
1440 update_mute_display ();
1443 void
1444 EditorRoutes::show_tracks_with_regions_at_playhead ()
1446 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1448 set<TimeAxisView*> show;
1449 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1450 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1451 if (tav) {
1452 show.insert (tav);
1456 suspend_redisplay ();
1458 TreeModel::Children rows = _model->children ();
1459 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1460 TimeAxisView* tv = (*i)[_columns.tv];
1461 (*i)[_columns.visible] = (show.find (tv) != show.end());
1464 resume_redisplay ();