fixed width columns for LHS of editor route list
[ardour2.git] / gtk2_ardour / editor_routes.cc
blob65db793bb32bf5509e92c46d6b4410e8819abc7b
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("act-disabled"));
83 rec_col_renderer->set_pixbuf (1, ::get_icon("rec-in-progress"));
84 rec_col_renderer->set_pixbuf (2, ::get_icon("rec-enabled"));
85 rec_col_renderer->set_pixbuf (3, ::get_icon("step-editing"));
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("act-disabled"));
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 = (*i)[_columns.visible];
473 /* show or hide the TimeAxisView */
474 if (visible) {
475 tv->set_marked_for_display (true);
476 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
477 tv->clip_to_viewport ();
478 } else {
479 tv->set_visibility (false);
482 n++;
486 /* whenever we go idle, update the track view list to reflect the new order.
487 we can't do this here, because we could mess up something that is traversing
488 the track order and has caused a redisplay of the list.
490 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
492 _editor->reset_controls_layout_height (position);
493 _editor->reset_controls_layout_width ();
494 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
495 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
497 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
499 We're increasing the size of the canvas while the bottom is visible.
500 We scroll down to keep in step with the controls layout.
502 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
505 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
506 _session->sync_order_keys (N_ ("editor"));
510 void
511 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
513 if (!_session || _session->deletion_in_progress()) {
514 return;
517 /* this could require an order reset & sync */
518 _session->set_remote_control_ids();
519 _ignore_reorder = true;
520 redisplay ();
521 _ignore_reorder = false;
524 void
525 EditorRoutes::visible_changed (std::string const & path)
527 if (_session && _session->deletion_in_progress()) {
528 return;
531 TreeIter iter;
533 if ((iter = _model->get_iter (path))) {
534 TimeAxisView* tv = (*iter)[_columns.tv];
535 if (tv) {
536 bool visible = (*iter)[_columns.visible];
537 (*iter)[_columns.visible] = !visible;
541 _redisplay_does_not_reset_order_keys = true;
542 _session->set_remote_control_ids();
543 redisplay ();
544 _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 ();
703 cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl;
706 resume_redisplay ();
709 void
710 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
712 TreeModel::Children rows = _model->children();
713 TreeModel::Children::iterator i;
715 for (i = rows.begin(); i != rows.end(); ++i) {
716 if ((*i)[_columns.tv] == &tv) {
717 (*i)[_columns.visible] = false;
718 break;
722 redisplay ();
725 void
726 EditorRoutes::show_track_in_display (TimeAxisView& tv)
728 TreeModel::Children rows = _model->children();
729 TreeModel::Children::iterator i;
731 for (i = rows.begin(); i != rows.end(); ++i) {
732 if ((*i)[_columns.tv] == &tv) {
733 (*i)[_columns.visible] = true;
734 break;
738 redisplay ();
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 _model->reorder (co);
787 _redisplay_does_not_reset_order_keys = false;
792 void
793 EditorRoutes::hide_all_tracks (bool /*with_select*/)
795 TreeModel::Children rows = _model->children();
796 TreeModel::Children::iterator i;
798 suspend_redisplay ();
800 for (i = rows.begin(); i != rows.end(); ++i) {
802 TreeModel::Row row = (*i);
803 TimeAxisView *tv = row[_columns.tv];
805 if (tv == 0) {
806 continue;
809 row[_columns.visible] = false;
812 resume_redisplay ();
814 /* XXX this seems like a hack and half, but its not clear where to put this
815 otherwise.
818 //reset_scrolling_region ();
821 void
822 EditorRoutes::set_all_tracks_visibility (bool yn)
824 TreeModel::Children rows = _model->children();
825 TreeModel::Children::iterator i;
827 suspend_redisplay ();
829 for (i = rows.begin(); i != rows.end(); ++i) {
831 TreeModel::Row row = (*i);
832 TimeAxisView* tv = row[_columns.tv];
834 if (tv == 0) {
835 continue;
838 (*i)[_columns.visible] = yn;
841 resume_redisplay ();
844 void
845 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
847 TreeModel::Children rows = _model->children();
848 TreeModel::Children::iterator i;
850 suspend_redisplay ();
852 for (i = rows.begin(); i != rows.end(); ++i) {
854 TreeModel::Row row = (*i);
855 TimeAxisView* tv = row[_columns.tv];
857 AudioTimeAxisView* atv;
858 MidiTimeAxisView* mtv;
860 if (tv == 0) {
861 continue;
864 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
865 switch (tracks) {
866 case 0:
867 (*i)[_columns.visible] = yn;
868 break;
870 case 1:
871 if (atv->is_audio_track()) {
872 (*i)[_columns.visible] = yn;
874 break;
876 case 2:
877 if (!atv->is_audio_track()) {
878 (*i)[_columns.visible] = yn;
880 break;
883 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
884 switch (tracks) {
885 case 0:
886 (*i)[_columns.visible] = yn;
887 break;
889 case 3:
890 if (mtv->is_midi_track()) {
891 (*i)[_columns.visible] = yn;
893 break;
898 resume_redisplay ();
901 void
902 EditorRoutes::hide_all_routes ()
904 set_all_tracks_visibility (false);
907 void
908 EditorRoutes::show_all_routes ()
910 set_all_tracks_visibility (true);
913 void
914 EditorRoutes::show_all_audiotracks()
916 set_all_audio_midi_visibility (1, true);
918 void
919 EditorRoutes::hide_all_audiotracks ()
921 set_all_audio_midi_visibility (1, false);
924 void
925 EditorRoutes::show_all_audiobus ()
927 set_all_audio_midi_visibility (2, true);
929 void
930 EditorRoutes::hide_all_audiobus ()
932 set_all_audio_midi_visibility (2, false);
935 void
936 EditorRoutes::show_all_miditracks()
938 set_all_audio_midi_visibility (3, true);
940 void
941 EditorRoutes::hide_all_miditracks ()
943 set_all_audio_midi_visibility (3, false);
946 bool
947 EditorRoutes::key_press (GdkEventKey* ev)
949 TreeViewColumn *col;
950 boost::shared_ptr<RouteList> rl (new RouteList);
951 TreePath path;
953 switch (ev->keyval) {
954 case GDK_Tab:
955 case GDK_ISO_Left_Tab:
957 /* If we appear to be editing something, leave that cleanly and appropriately.
959 if (name_editable) {
960 name_editable->editing_done ();
961 name_editable = 0;
964 col = _display.get_column (_name_column); // select&focus on name column
966 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
967 treeview_select_previous (_display, _model, col);
968 } else {
969 treeview_select_next (_display, _model, col);
972 return true;
973 break;
975 case 'm':
976 if (get_relevant_routes (rl)) {
977 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
979 return true;
980 break;
982 case 's':
983 if (Config->get_solo_control_is_listen_control()) {
984 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
985 } else {
986 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
988 return true;
989 break;
991 case 'r':
992 if (get_relevant_routes (rl)) {
993 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
995 break;
997 default:
998 break;
1001 return false;
1004 bool
1005 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1007 TimeAxisView* tv;
1008 RouteTimeAxisView* rtv;
1009 RefPtr<TreeSelection> selection = _display.get_selection();
1010 TreePath path;
1011 TreeIter iter;
1013 if (selection->count_selected_rows() != 0) {
1015 /* use selection */
1017 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1018 iter = selection->get_selected (tm);
1020 } else {
1021 /* use mouse pointer */
1023 int x, y;
1024 int bx, by;
1026 _display.get_pointer (x, y);
1027 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1029 if (_display.get_path_at_pos (bx, by, path)) {
1030 iter = _model->get_iter (path);
1034 if (iter) {
1035 tv = (*iter)[_columns.tv];
1036 if (tv) {
1037 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1038 if (rtv) {
1039 rl->push_back (rtv->route());
1044 return !rl->empty();
1047 bool
1048 EditorRoutes::button_press (GdkEventButton* ev)
1050 if (Keyboard::is_context_menu_event (ev)) {
1051 show_menu ();
1052 return true;
1055 //Scroll editor canvas to selected track
1056 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1058 TreeModel::Path path;
1059 TreeViewColumn *tvc;
1060 int cell_x;
1061 int cell_y;
1063 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
1065 // Get the model row.
1066 Gtk::TreeModel::Row row = *_model->get_iter (path);
1068 TimeAxisView *tv = row[_columns.tv];
1070 int y_pos = tv->y_position();
1072 //Clamp the y pos so that we do not extend beyond the canvas full height.
1073 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1074 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1077 //Only scroll to if the track is visible
1078 if(y_pos != -1){
1079 _editor->reset_y_origin (y_pos);
1083 return false;
1086 bool
1087 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1089 if (selection_countdown) {
1090 if (--selection_countdown == 0) {
1091 return true;
1092 } else {
1093 /* no selection yet ... */
1094 return false;
1097 return true;
1100 struct EditorOrderRouteSorter {
1101 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1102 /* use of ">" forces the correct sort order */
1103 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1107 void
1108 EditorRoutes::initial_display ()
1110 suspend_redisplay ();
1111 _model->clear ();
1113 if (!_session) {
1114 resume_redisplay ();
1115 return;
1118 boost::shared_ptr<RouteList> routes = _session->get_routes();
1119 RouteList r (*routes);
1120 EditorOrderRouteSorter sorter;
1122 r.sort (sorter);
1123 _editor->handle_new_route (r);
1125 /* don't show master bus in a new session */
1127 if (ARDOUR_UI::instance()->session_is_new ()) {
1129 TreeModel::Children rows = _model->children();
1130 TreeModel::Children::iterator i;
1132 _no_redisplay = true;
1134 for (i = rows.begin(); i != rows.end(); ++i) {
1136 TimeAxisView *tv = (*i)[_columns.tv];
1137 RouteTimeAxisView *rtv;
1139 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1140 if (rtv->route()->is_master()) {
1141 _display.get_selection()->unselect (i);
1146 _no_redisplay = false;
1147 redisplay ();
1150 resume_redisplay ();
1153 void
1154 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1156 _redisplay_does_not_sync_order_keys = true;
1157 _session->set_remote_control_ids();
1158 redisplay ();
1159 _redisplay_does_not_sync_order_keys = false;
1162 void
1163 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1164 int x, int y,
1165 const SelectionData& data,
1166 guint info, guint time)
1168 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1169 _display.on_drag_data_received (context, x, y, data, info, time);
1170 return;
1173 context->drag_finish (true, false, time);
1176 void
1177 EditorRoutes::move_selected_tracks (bool up)
1179 if (_editor->selection->tracks.empty()) {
1180 return;
1183 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1184 std::list<ViewRoute> view_routes;
1185 std::vector<int> neworder;
1186 TreeModel::Children rows = _model->children();
1187 TreeModel::Children::iterator ri;
1189 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1190 TimeAxisView* tv = (*ri)[_columns.tv];
1191 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1193 view_routes.push_back (ViewRoute (tv, route));
1196 list<ViewRoute>::iterator trailing;
1197 list<ViewRoute>::iterator leading;
1199 if (up) {
1201 trailing = view_routes.begin();
1202 leading = view_routes.begin();
1204 ++leading;
1206 while (leading != view_routes.end()) {
1207 if (_editor->selection->selected (leading->first)) {
1208 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1209 leading = view_routes.erase (leading);
1210 } else {
1211 ++leading;
1212 ++trailing;
1216 } else {
1218 /* if we could use reverse_iterator in list::insert, this code
1219 would be a beautiful reflection of the code above. but we can't
1220 and so it looks like a bit of a mess.
1223 trailing = view_routes.end();
1224 leading = view_routes.end();
1226 --leading; if (leading == view_routes.begin()) { return; }
1227 --leading;
1228 --trailing;
1230 while (1) {
1232 if (_editor->selection->selected (leading->first)) {
1233 list<ViewRoute>::iterator tmp;
1235 /* need to insert *after* trailing, not *before* it,
1236 which is what insert (iter, val) normally does.
1239 tmp = trailing;
1240 tmp++;
1242 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1244 /* can't use iter = cont.erase (iter); form here, because
1245 we need iter to move backwards.
1248 tmp = leading;
1249 --tmp;
1251 bool done = false;
1253 if (leading == view_routes.begin()) {
1254 /* the one we've just inserted somewhere else
1255 was the first in the list. erase this copy,
1256 and then break, because we're done.
1258 done = true;
1261 view_routes.erase (leading);
1263 if (done) {
1264 break;
1267 leading = tmp;
1269 } else {
1270 if (leading == view_routes.begin()) {
1271 break;
1273 --leading;
1274 --trailing;
1279 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1280 neworder.push_back (leading->second->order_key (N_ ("editor")));
1283 _model->reorder (neworder);
1285 _session->sync_order_keys (N_ ("editor"));
1288 void
1289 EditorRoutes::update_input_active_display ()
1291 TreeModel::Children rows = _model->children();
1292 TreeModel::Children::iterator i;
1294 for (i = rows.begin(); i != rows.end(); ++i) {
1295 boost::shared_ptr<Route> route = (*i)[_columns.route];
1297 if (boost::dynamic_pointer_cast<Track> (route)) {
1298 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1300 if (mt) {
1301 (*i)[_columns.is_input_active] = mt->input_active();
1307 void
1308 EditorRoutes::update_rec_display ()
1310 TreeModel::Children rows = _model->children();
1311 TreeModel::Children::iterator i;
1313 for (i = rows.begin(); i != rows.end(); ++i) {
1314 boost::shared_ptr<Route> route = (*i)[_columns.route];
1316 if (boost::dynamic_pointer_cast<Track> (route)) {
1317 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1319 if (route->record_enabled()) {
1320 if (_session->record_status() == Session::Recording) {
1321 (*i)[_columns.rec_state] = 1;
1322 } else {
1323 (*i)[_columns.rec_state] = 2;
1325 } else if (mt && mt->step_editing()) {
1326 (*i)[_columns.rec_state] = 3;
1327 } else {
1328 (*i)[_columns.rec_state] = 0;
1331 (*i)[_columns.name_editable] = !route->record_enabled ();
1336 void
1337 EditorRoutes::update_mute_display ()
1339 TreeModel::Children rows = _model->children();
1340 TreeModel::Children::iterator i;
1342 for (i = rows.begin(); i != rows.end(); ++i) {
1343 boost::shared_ptr<Route> route = (*i)[_columns.route];
1344 (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
1348 void
1349 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1351 TreeModel::Children rows = _model->children();
1352 TreeModel::Children::iterator i;
1354 for (i = rows.begin(); i != rows.end(); ++i) {
1355 boost::shared_ptr<Route> route = (*i)[_columns.route];
1356 (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
1360 void
1361 EditorRoutes::update_solo_isolate_display ()
1363 TreeModel::Children rows = _model->children();
1364 TreeModel::Children::iterator i;
1366 for (i = rows.begin(); i != rows.end(); ++i) {
1367 boost::shared_ptr<Route> route = (*i)[_columns.route];
1368 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
1372 void
1373 EditorRoutes::update_solo_safe_display ()
1375 TreeModel::Children rows = _model->children();
1376 TreeModel::Children::iterator i;
1378 for (i = rows.begin(); i != rows.end(); ++i) {
1379 boost::shared_ptr<Route> route = (*i)[_columns.route];
1380 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
1384 list<TimeAxisView*>
1385 EditorRoutes::views () const
1387 list<TimeAxisView*> v;
1388 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1389 v.push_back ((*i)[_columns.tv]);
1392 return v;
1395 void
1396 EditorRoutes::clear ()
1398 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1399 _model->clear ();
1400 _display.set_model (_model);
1403 void
1404 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1406 name_editable = ce;
1408 /* give it a special name */
1410 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1412 if (e) {
1413 e->set_name (X_("RouteNameEditorEntry"));
1417 void
1418 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1420 name_editable = 0;
1422 TreeIter iter = _model->get_iter (path);
1424 if (!iter) {
1425 return;
1428 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1430 if (route && route->name() != new_text) {
1431 route->set_name (new_text);
1435 void
1436 EditorRoutes::solo_changed_so_update_mute ()
1438 update_mute_display ();
1441 void
1442 EditorRoutes::show_tracks_with_regions_at_playhead ()
1444 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1446 set<TimeAxisView*> show;
1447 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1448 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1449 if (tav) {
1450 show.insert (tav);
1454 suspend_redisplay ();
1456 TreeModel::Children rows = _model->children ();
1457 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1458 TimeAxisView* tv = (*i)[_columns.tv];
1459 (*i)[_columns.visible] = (show.find (tv) != show.end());
1462 resume_redisplay ();