switch to basic round icon for MIDI input enable in editor track list; use StatefulBu...
[ardour2.git] / gtk2_ardour / editor_routes.cc
blob126d44c178da280f13b6a0d823c8489c5e0f5f25
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 _scroller.add (_display);
72 _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
74 _model = ListStore::create (_columns);
75 _display.set_model (_model);
77 // Record enable toggle
78 CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
80 rec_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
81 rec_col_renderer->set_pixbuf (1, ::get_icon("rec-in-progress"));
82 rec_col_renderer->set_pixbuf (2, ::get_icon("rec-enabled"));
83 rec_col_renderer->set_pixbuf (3, ::get_icon("step-editing"));
84 rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
86 TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
88 rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
89 rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
91 rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
92 rec_state_column->set_alignment(ALIGN_CENTER);
93 rec_state_column->set_expand(false);
94 rec_state_column->set_fixed_width(15);
96 // MIDI Input Active
98 CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti());
99 input_active_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
100 input_active_col_renderer->set_pixbuf (1, ::get_icon("midi-input-active"));
101 input_active_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed));
103 TreeViewColumn* input_active_column = manage (new TreeViewColumn ("I", *input_active_col_renderer));
105 input_active_column->add_attribute(input_active_col_renderer->property_state(), _columns.is_input_active);
106 input_active_column->add_attribute (input_active_col_renderer->property_visible(), _columns.is_midi);
108 input_active_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
109 input_active_column->set_alignment(ALIGN_CENTER);
110 input_active_column->set_expand(false);
111 input_active_column->set_fixed_width(20);
113 // Mute enable toggle
114 CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
116 mute_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
117 mute_col_renderer->set_pixbuf (1, ::get_icon("muted-by-others"));
118 mute_col_renderer->set_pixbuf (2, ::get_icon("mute-enabled"));
119 mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
121 TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
123 mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
124 mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
125 mute_state_column->set_alignment(ALIGN_CENTER);
126 mute_state_column->set_expand(false);
127 mute_state_column->set_fixed_width(15);
129 // Solo enable toggle
130 CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
132 solo_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
133 solo_col_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
134 solo_col_renderer->set_pixbuf (3, ::get_icon("soloed-by-others"));
135 solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
137 TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
139 solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
140 solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
141 solo_state_column->set_alignment(ALIGN_CENTER);
142 solo_state_column->set_expand(false);
143 solo_state_column->set_fixed_width(15);
145 // Solo isolate toggle
146 CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
148 solo_iso_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
149 solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolated"));
150 solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
152 TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
154 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
155 solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
156 solo_isolate_state_column->set_alignment(ALIGN_CENTER);
157 solo_isolate_state_column->set_expand(false);
158 solo_isolate_state_column->set_fixed_width(22);
160 // Solo safe toggle
161 CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
163 solo_safe_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
164 solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
165 solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
167 TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
168 solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
169 solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
170 solo_safe_state_column->set_alignment(ALIGN_CENTER);
171 solo_safe_state_column->set_expand(false);
172 solo_safe_state_column->set_fixed_width(22);
174 _display.append_column (*input_active_column);
175 _display.append_column (*rec_state_column);
176 _display.append_column (*mute_state_column);
177 _display.append_column (*solo_state_column);
178 _display.append_column (*solo_isolate_state_column);
179 _display.append_column (*solo_safe_state_column);
181 _name_column = _display.append_column (_("Name"), _columns.text) - 1;
182 _visible_column = _display.append_column (_("V"), _columns.visible) - 1;
184 _display.set_headers_visible (true);
185 _display.set_name ("TrackListDisplay");
186 _display.get_selection()->set_mode (SELECTION_SINGLE);
187 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
188 _display.set_reorderable (true);
189 _display.set_rules_hint (true);
190 _display.set_size_request (100, -1);
191 _display.add_object_drag (_columns.route.index(), "routes");
193 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
195 assert (name_cell);
196 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
198 TreeViewColumn* name_column = _display.get_column (_name_column);
200 assert (name_column);
202 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
203 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
204 name_column->set_expand(true);
205 name_column->set_min_width(50);
207 name_cell->property_editable() = true;
208 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
210 // Set the visible column cell renderer to radio toggle
211 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
213 visible_cell->property_activatable() = true;
214 visible_cell->property_radio() = false;
215 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
217 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
218 visible_col->set_expand(false);
219 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
220 visible_col->set_fixed_width(30);
221 visible_col->set_alignment(ALIGN_CENTER);
223 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
224 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
226 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
227 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
229 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
230 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
232 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
233 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
235 _display.set_enable_search (false);
237 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
240 bool
241 EditorRoutes::focus_in (GdkEventFocus*)
243 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
245 if (win) {
246 old_focus = win->get_focus ();
247 } else {
248 old_focus = 0;
251 name_editable = 0;
253 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
254 return true;
257 bool
258 EditorRoutes::focus_out (GdkEventFocus*)
260 if (old_focus) {
261 old_focus->grab_focus ();
262 old_focus = 0;
265 return false;
268 bool
269 EditorRoutes::enter_notify (GdkEventCrossing*)
271 if (name_editable) {
272 return true;
275 /* arm counter so that ::selection_filter() will deny selecting anything for the
276 next two attempts to change selection status.
278 selection_countdown = 2;
279 _scroller.grab_focus ();
280 Keyboard::magic_widget_grab_focus ();
281 return false;
284 bool
285 EditorRoutes::leave_notify (GdkEventCrossing*)
287 selection_countdown = 0;
289 if (old_focus) {
290 old_focus->grab_focus ();
291 old_focus = 0;
294 Keyboard::magic_widget_drop_focus ();
295 return false;
298 void
299 EditorRoutes::set_session (Session* s)
301 SessionHandlePtr::set_session (s);
303 initial_display ();
305 if (_session) {
306 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
307 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
311 void
312 EditorRoutes::on_input_active_changed (std::string const & path_string)
314 // Get the model row that has been toggled.
315 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
317 TimeAxisView* tv = row[_columns.tv];
318 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
320 if (rtv) {
321 boost::shared_ptr<MidiTrack> mt;
322 mt = rtv->midi_track();
323 if (mt) {
324 mt->set_input_active (!mt->input_active());
329 void
330 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
332 // Get the model row that has been toggled.
333 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
335 TimeAxisView* tv = row[_columns.tv];
336 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
338 if (rtv && rtv->track()) {
339 boost::shared_ptr<RouteList> rl (new RouteList);
340 rl->push_back (rtv->route());
341 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
345 void
346 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
348 // Get the model row that has been toggled.
349 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
351 TimeAxisView *tv = row[_columns.tv];
352 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
354 if (rtv != 0) {
355 boost::shared_ptr<RouteList> rl (new RouteList);
356 rl->push_back (rtv->route());
357 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
361 void
362 EditorRoutes::on_tv_solo_enable_toggled (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);
370 if (rtv != 0) {
371 boost::shared_ptr<RouteList> rl (new RouteList);
372 rl->push_back (rtv->route());
373 if (Config->get_solo_control_is_listen_control()) {
374 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
375 } else {
376 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
381 void
382 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
384 // Get the model row that has been toggled.
385 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
387 TimeAxisView *tv = row[_columns.tv];
388 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
390 if (rtv) {
391 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
395 void
396 EditorRoutes::on_tv_solo_safe_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);
404 if (rtv) {
405 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
409 void
410 EditorRoutes::build_menu ()
412 using namespace Menu_Helpers;
413 using namespace Gtk;
415 _menu = new Menu;
417 MenuList& items = _menu->items();
418 _menu->set_name ("ArdourContextMenu");
420 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
421 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
422 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
423 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
424 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
425 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
426 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
427 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
428 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
431 void
432 EditorRoutes::show_menu ()
434 if (_menu == 0) {
435 build_menu ();
438 _menu->popup (1, gtk_get_current_event_time());
441 void
442 EditorRoutes::redisplay ()
444 if (_no_redisplay || !_session) {
445 return;
448 TreeModel::Children rows = _model->children();
449 TreeModel::Children::iterator i;
450 uint32_t position;
451 int n;
453 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
454 TimeAxisView *tv = (*i)[_columns.tv];
455 boost::shared_ptr<Route> route = (*i)[_columns.route];
457 if (tv == 0) {
458 // just a "title" row
459 continue;
462 if (!_redisplay_does_not_reset_order_keys) {
463 /* this reorder is caused by user action, so reassign sort order keys
464 to tracks.
466 route->set_order_key (N_ ("editor"), n);
469 bool visible = (*i)[_columns.visible];
471 /* show or hide the TimeAxisView */
472 if (visible) {
473 tv->set_marked_for_display (true);
474 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
475 tv->clip_to_viewport ();
476 } else {
477 tv->set_visibility (false);
480 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];
535 (*iter)[_columns.visible] = !visible;
539 _redisplay_does_not_reset_order_keys = true;
540 _session->set_remote_control_ids();
541 redisplay ();
542 _redisplay_does_not_reset_order_keys = false;
545 void
546 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
548 TreeModel::Row row;
550 _redisplay_does_not_sync_order_keys = true;
551 suspend_redisplay ();
553 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
555 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
557 row = *(_model->append ());
559 row[_columns.text] = (*x)->route()->name();
560 row[_columns.visible] = (*x)->marked_for_display();
561 row[_columns.tv] = *x;
562 row[_columns.route] = (*x)->route ();
563 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
565 if (midi_trk) {
566 row[_columns.is_input_active] = midi_trk->input_active ();
567 row[_columns.is_midi] = true;
568 } else {
569 row[_columns.is_input_active] = false;
570 row[_columns.is_midi] = false;
573 row[_columns.mute_state] = (*x)->route()->muted();
574 row[_columns.solo_state] = RouteUI::solo_visual_state ((*x)->route());
575 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
576 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
577 row[_columns.name_editable] = true;
579 _ignore_reorder = true;
581 /* added a new fresh one at the end */
582 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
583 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
586 _ignore_reorder = false;
588 boost::weak_ptr<Route> wr ((*x)->route());
590 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
591 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
593 if ((*x)->is_track()) {
594 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
595 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
598 if ((*x)->is_midi_track()) {
599 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
600 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
601 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
604 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
605 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
606 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
607 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
608 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
611 update_rec_display ();
612 update_mute_display ();
613 update_solo_display (true);
614 update_solo_isolate_display ();
615 update_solo_safe_display ();
616 update_input_active_display ();
617 resume_redisplay ();
618 _redisplay_does_not_sync_order_keys = false;
621 void
622 EditorRoutes::handle_gui_changes (string const & what, void*)
624 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
626 if (what == "track_height") {
627 /* Optional :make tracks change height while it happens, instead
628 of on first-idle
630 //update_canvas_now ();
631 redisplay ();
634 if (what == "visible_tracks") {
635 redisplay ();
639 void
640 EditorRoutes::route_removed (TimeAxisView *tv)
642 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
644 TreeModel::Children rows = _model->children();
645 TreeModel::Children::iterator ri;
647 /* the core model has changed, there is no need to sync
648 view orders.
651 _redisplay_does_not_sync_order_keys = true;
653 for (ri = rows.begin(); ri != rows.end(); ++ri) {
654 if ((*ri)[_columns.tv] == tv) {
655 _model->erase (ri);
656 break;
660 _redisplay_does_not_sync_order_keys = false;
663 void
664 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
666 if (!what_changed.contains (ARDOUR::Properties::name)) {
667 return;
670 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
672 boost::shared_ptr<Route> route = r.lock ();
674 if (!route) {
675 return;
678 TreeModel::Children rows = _model->children();
679 TreeModel::Children::iterator i;
681 for (i = rows.begin(); i != rows.end(); ++i) {
682 boost::shared_ptr<Route> t = (*i)[_columns.route];
683 if (t == route) {
684 (*i)[_columns.text] = route->name();
685 break;
690 void
691 EditorRoutes::update_visibility ()
693 TreeModel::Children rows = _model->children();
694 TreeModel::Children::iterator i;
696 suspend_redisplay ();
698 for (i = rows.begin(); i != rows.end(); ++i) {
699 TimeAxisView *tv = (*i)[_columns.tv];
700 (*i)[_columns.visible] = tv->marked_for_display ();
701 cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl;
704 resume_redisplay ();
707 void
708 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
710 TreeModel::Children rows = _model->children();
711 TreeModel::Children::iterator i;
713 for (i = rows.begin(); i != rows.end(); ++i) {
714 if ((*i)[_columns.tv] == &tv) {
715 (*i)[_columns.visible] = false;
716 break;
720 redisplay ();
723 void
724 EditorRoutes::show_track_in_display (TimeAxisView& tv)
726 TreeModel::Children rows = _model->children();
727 TreeModel::Children::iterator i;
729 for (i = rows.begin(); i != rows.end(); ++i) {
730 if ((*i)[_columns.tv] == &tv) {
731 (*i)[_columns.visible] = true;
732 break;
736 redisplay ();
739 void
740 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
742 redisplay ();
745 /** If src != "editor", take editor order keys from each route and use them to rearrange the
746 * route list so that the visual arrangement of routes matches the order keys from the routes.
748 void
749 EditorRoutes::sync_order_keys (string const & src)
751 map<int, int> new_order;
752 TreeModel::Children rows = _model->children();
753 TreeModel::Children::iterator ri;
755 if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
756 return;
759 bool changed = false;
760 int order;
762 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
763 boost::shared_ptr<Route> route = (*ri)[_columns.route];
765 int const old_key = order;
766 int const new_key = route->order_key (N_ ("editor"));
768 new_order[new_key] = old_key;
770 if (new_key != old_key) {
771 changed = true;
775 if (changed) {
776 _redisplay_does_not_reset_order_keys = true;
778 /* `compact' new_order into a vector */
779 vector<int> co;
780 for (map<int, int>::const_iterator i = new_order.begin(); i != new_order.end(); ++i) {
781 co.push_back (i->second);
784 _model->reorder (co);
785 _redisplay_does_not_reset_order_keys = false;
790 void
791 EditorRoutes::hide_all_tracks (bool /*with_select*/)
793 TreeModel::Children rows = _model->children();
794 TreeModel::Children::iterator i;
796 suspend_redisplay ();
798 for (i = rows.begin(); i != rows.end(); ++i) {
800 TreeModel::Row row = (*i);
801 TimeAxisView *tv = row[_columns.tv];
803 if (tv == 0) {
804 continue;
807 row[_columns.visible] = false;
810 resume_redisplay ();
812 /* XXX this seems like a hack and half, but its not clear where to put this
813 otherwise.
816 //reset_scrolling_region ();
819 void
820 EditorRoutes::set_all_tracks_visibility (bool yn)
822 TreeModel::Children rows = _model->children();
823 TreeModel::Children::iterator i;
825 suspend_redisplay ();
827 for (i = rows.begin(); i != rows.end(); ++i) {
829 TreeModel::Row row = (*i);
830 TimeAxisView* tv = row[_columns.tv];
832 if (tv == 0) {
833 continue;
836 (*i)[_columns.visible] = yn;
839 resume_redisplay ();
842 void
843 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
845 TreeModel::Children rows = _model->children();
846 TreeModel::Children::iterator i;
848 suspend_redisplay ();
850 for (i = rows.begin(); i != rows.end(); ++i) {
852 TreeModel::Row row = (*i);
853 TimeAxisView* tv = row[_columns.tv];
855 AudioTimeAxisView* atv;
856 MidiTimeAxisView* mtv;
858 if (tv == 0) {
859 continue;
862 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
863 switch (tracks) {
864 case 0:
865 (*i)[_columns.visible] = yn;
866 break;
868 case 1:
869 if (atv->is_audio_track()) {
870 (*i)[_columns.visible] = yn;
872 break;
874 case 2:
875 if (!atv->is_audio_track()) {
876 (*i)[_columns.visible] = yn;
878 break;
881 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
882 switch (tracks) {
883 case 0:
884 (*i)[_columns.visible] = yn;
885 break;
887 case 3:
888 if (mtv->is_midi_track()) {
889 (*i)[_columns.visible] = yn;
891 break;
896 resume_redisplay ();
899 void
900 EditorRoutes::hide_all_routes ()
902 set_all_tracks_visibility (false);
905 void
906 EditorRoutes::show_all_routes ()
908 set_all_tracks_visibility (true);
911 void
912 EditorRoutes::show_all_audiotracks()
914 set_all_audio_midi_visibility (1, true);
916 void
917 EditorRoutes::hide_all_audiotracks ()
919 set_all_audio_midi_visibility (1, false);
922 void
923 EditorRoutes::show_all_audiobus ()
925 set_all_audio_midi_visibility (2, true);
927 void
928 EditorRoutes::hide_all_audiobus ()
930 set_all_audio_midi_visibility (2, false);
933 void
934 EditorRoutes::show_all_miditracks()
936 set_all_audio_midi_visibility (3, true);
938 void
939 EditorRoutes::hide_all_miditracks ()
941 set_all_audio_midi_visibility (3, false);
944 bool
945 EditorRoutes::key_press (GdkEventKey* ev)
947 TreeViewColumn *col;
948 boost::shared_ptr<RouteList> rl (new RouteList);
949 TreePath path;
951 switch (ev->keyval) {
952 case GDK_Tab:
953 case GDK_ISO_Left_Tab:
955 /* If we appear to be editing something, leave that cleanly and appropriately.
957 if (name_editable) {
958 name_editable->editing_done ();
959 name_editable = 0;
962 col = _display.get_column (_name_column); // select&focus on name column
964 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
965 treeview_select_previous (_display, _model, col);
966 } else {
967 treeview_select_next (_display, _model, col);
970 return true;
971 break;
973 case 'm':
974 if (get_relevant_routes (rl)) {
975 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
977 return true;
978 break;
980 case 's':
981 if (Config->get_solo_control_is_listen_control()) {
982 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
983 } else {
984 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
986 return true;
987 break;
989 case 'r':
990 if (get_relevant_routes (rl)) {
991 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
993 break;
995 default:
996 break;
999 return false;
1002 bool
1003 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1005 TimeAxisView* tv;
1006 RouteTimeAxisView* rtv;
1007 RefPtr<TreeSelection> selection = _display.get_selection();
1008 TreePath path;
1009 TreeIter iter;
1011 if (selection->count_selected_rows() != 0) {
1013 /* use selection */
1015 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1016 iter = selection->get_selected (tm);
1018 } else {
1019 /* use mouse pointer */
1021 int x, y;
1022 int bx, by;
1024 _display.get_pointer (x, y);
1025 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1027 if (_display.get_path_at_pos (bx, by, path)) {
1028 iter = _model->get_iter (path);
1032 if (iter) {
1033 tv = (*iter)[_columns.tv];
1034 if (tv) {
1035 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1036 if (rtv) {
1037 rl->push_back (rtv->route());
1042 return !rl->empty();
1045 bool
1046 EditorRoutes::button_press (GdkEventButton* ev)
1048 if (Keyboard::is_context_menu_event (ev)) {
1049 show_menu ();
1050 return true;
1053 //Scroll editor canvas to selected track
1054 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1056 TreeModel::Path path;
1057 TreeViewColumn *tvc;
1058 int cell_x;
1059 int cell_y;
1061 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
1063 // Get the model row.
1064 Gtk::TreeModel::Row row = *_model->get_iter (path);
1066 TimeAxisView *tv = row[_columns.tv];
1068 int y_pos = tv->y_position();
1070 //Clamp the y pos so that we do not extend beyond the canvas full height.
1071 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1072 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1075 //Only scroll to if the track is visible
1076 if(y_pos != -1){
1077 _editor->reset_y_origin (y_pos);
1081 return false;
1084 bool
1085 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1087 if (selection_countdown) {
1088 if (--selection_countdown == 0) {
1089 return true;
1090 } else {
1091 /* no selection yet ... */
1092 return false;
1095 return true;
1098 struct EditorOrderRouteSorter {
1099 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1100 /* use of ">" forces the correct sort order */
1101 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1105 void
1106 EditorRoutes::initial_display ()
1108 suspend_redisplay ();
1109 _model->clear ();
1111 if (!_session) {
1112 resume_redisplay ();
1113 return;
1116 boost::shared_ptr<RouteList> routes = _session->get_routes();
1117 RouteList r (*routes);
1118 EditorOrderRouteSorter sorter;
1120 r.sort (sorter);
1121 _editor->handle_new_route (r);
1123 /* don't show master bus in a new session */
1125 if (ARDOUR_UI::instance()->session_is_new ()) {
1127 TreeModel::Children rows = _model->children();
1128 TreeModel::Children::iterator i;
1130 _no_redisplay = true;
1132 for (i = rows.begin(); i != rows.end(); ++i) {
1134 TimeAxisView *tv = (*i)[_columns.tv];
1135 RouteTimeAxisView *rtv;
1137 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1138 if (rtv->route()->is_master()) {
1139 _display.get_selection()->unselect (i);
1144 _no_redisplay = false;
1145 redisplay ();
1148 resume_redisplay ();
1151 void
1152 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1154 _redisplay_does_not_sync_order_keys = true;
1155 _session->set_remote_control_ids();
1156 redisplay ();
1157 _redisplay_does_not_sync_order_keys = false;
1160 void
1161 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1162 int x, int y,
1163 const SelectionData& data,
1164 guint info, guint time)
1166 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1167 _display.on_drag_data_received (context, x, y, data, info, time);
1168 return;
1171 context->drag_finish (true, false, time);
1174 void
1175 EditorRoutes::move_selected_tracks (bool up)
1177 if (_editor->selection->tracks.empty()) {
1178 return;
1181 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1182 std::list<ViewRoute> view_routes;
1183 std::vector<int> neworder;
1184 TreeModel::Children rows = _model->children();
1185 TreeModel::Children::iterator ri;
1187 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1188 TimeAxisView* tv = (*ri)[_columns.tv];
1189 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1191 view_routes.push_back (ViewRoute (tv, route));
1194 list<ViewRoute>::iterator trailing;
1195 list<ViewRoute>::iterator leading;
1197 if (up) {
1199 trailing = view_routes.begin();
1200 leading = view_routes.begin();
1202 ++leading;
1204 while (leading != view_routes.end()) {
1205 if (_editor->selection->selected (leading->first)) {
1206 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1207 leading = view_routes.erase (leading);
1208 } else {
1209 ++leading;
1210 ++trailing;
1214 } else {
1216 /* if we could use reverse_iterator in list::insert, this code
1217 would be a beautiful reflection of the code above. but we can't
1218 and so it looks like a bit of a mess.
1221 trailing = view_routes.end();
1222 leading = view_routes.end();
1224 --leading; if (leading == view_routes.begin()) { return; }
1225 --leading;
1226 --trailing;
1228 while (1) {
1230 if (_editor->selection->selected (leading->first)) {
1231 list<ViewRoute>::iterator tmp;
1233 /* need to insert *after* trailing, not *before* it,
1234 which is what insert (iter, val) normally does.
1237 tmp = trailing;
1238 tmp++;
1240 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1242 /* can't use iter = cont.erase (iter); form here, because
1243 we need iter to move backwards.
1246 tmp = leading;
1247 --tmp;
1249 bool done = false;
1251 if (leading == view_routes.begin()) {
1252 /* the one we've just inserted somewhere else
1253 was the first in the list. erase this copy,
1254 and then break, because we're done.
1256 done = true;
1259 view_routes.erase (leading);
1261 if (done) {
1262 break;
1265 leading = tmp;
1267 } else {
1268 if (leading == view_routes.begin()) {
1269 break;
1271 --leading;
1272 --trailing;
1277 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1278 neworder.push_back (leading->second->order_key (N_ ("editor")));
1281 _model->reorder (neworder);
1283 _session->sync_order_keys (N_ ("editor"));
1286 void
1287 EditorRoutes::update_input_active_display ()
1289 TreeModel::Children rows = _model->children();
1290 TreeModel::Children::iterator i;
1292 for (i = rows.begin(); i != rows.end(); ++i) {
1293 boost::shared_ptr<Route> route = (*i)[_columns.route];
1295 if (boost::dynamic_pointer_cast<Track> (route)) {
1296 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1298 if (mt) {
1299 (*i)[_columns.is_input_active] = mt->input_active();
1305 void
1306 EditorRoutes::update_rec_display ()
1308 TreeModel::Children rows = _model->children();
1309 TreeModel::Children::iterator i;
1311 for (i = rows.begin(); i != rows.end(); ++i) {
1312 boost::shared_ptr<Route> route = (*i)[_columns.route];
1314 if (boost::dynamic_pointer_cast<Track> (route)) {
1315 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1317 if (route->record_enabled()) {
1318 if (_session->record_status() == Session::Recording) {
1319 (*i)[_columns.rec_state] = 1;
1320 } else {
1321 (*i)[_columns.rec_state] = 2;
1323 } else if (mt && mt->step_editing()) {
1324 (*i)[_columns.rec_state] = 3;
1325 } else {
1326 (*i)[_columns.rec_state] = 0;
1329 (*i)[_columns.name_editable] = !route->record_enabled ();
1334 void
1335 EditorRoutes::update_mute_display ()
1337 TreeModel::Children rows = _model->children();
1338 TreeModel::Children::iterator i;
1340 for (i = rows.begin(); i != rows.end(); ++i) {
1341 boost::shared_ptr<Route> route = (*i)[_columns.route];
1342 (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
1346 void
1347 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1349 TreeModel::Children rows = _model->children();
1350 TreeModel::Children::iterator i;
1352 for (i = rows.begin(); i != rows.end(); ++i) {
1353 boost::shared_ptr<Route> route = (*i)[_columns.route];
1354 (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
1358 void
1359 EditorRoutes::update_solo_isolate_display ()
1361 TreeModel::Children rows = _model->children();
1362 TreeModel::Children::iterator i;
1364 for (i = rows.begin(); i != rows.end(); ++i) {
1365 boost::shared_ptr<Route> route = (*i)[_columns.route];
1366 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
1370 void
1371 EditorRoutes::update_solo_safe_display ()
1373 TreeModel::Children rows = _model->children();
1374 TreeModel::Children::iterator i;
1376 for (i = rows.begin(); i != rows.end(); ++i) {
1377 boost::shared_ptr<Route> route = (*i)[_columns.route];
1378 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
1382 list<TimeAxisView*>
1383 EditorRoutes::views () const
1385 list<TimeAxisView*> v;
1386 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1387 v.push_back ((*i)[_columns.tv]);
1390 return v;
1393 void
1394 EditorRoutes::clear ()
1396 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1397 _model->clear ();
1398 _display.set_model (_model);
1401 void
1402 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1404 name_editable = ce;
1406 /* give it a special name */
1408 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1410 if (e) {
1411 e->set_name (X_("RouteNameEditorEntry"));
1415 void
1416 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1418 name_editable = 0;
1420 TreeIter iter = _model->get_iter (path);
1422 if (!iter) {
1423 return;
1426 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1428 if (route && route->name() != new_text) {
1429 route->set_name (new_text);
1433 void
1434 EditorRoutes::solo_changed_so_update_mute ()
1436 update_mute_display ();
1439 void
1440 EditorRoutes::show_tracks_with_regions_at_playhead ()
1442 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1444 set<TimeAxisView*> show;
1445 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1446 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1447 if (tav) {
1448 show.insert (tav);
1452 suspend_redisplay ();
1454 TreeModel::Children rows = _model->children ();
1455 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1456 TimeAxisView* tv = (*i)[_columns.tv];
1457 (*i)[_columns.visible] = (show.find (tv) != show.end());
1460 resume_redisplay ();