2 Copyright (C) 2000-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "ardour/session.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"
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"
53 using namespace ARDOUR
;
56 using namespace Gtkmm2ext
;
58 using Gtkmm2ext::Keyboard
;
60 EditorRoutes::EditorRoutes (Editor
* 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)
68 , selection_countdown (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);
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);
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
));
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());
241 EditorRoutes::focus_in (GdkEventFocus
*)
243 Window
* win
= dynamic_cast<Window
*> (_scroller
.get_toplevel ());
246 old_focus
= win
->get_focus ();
253 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
258 EditorRoutes::focus_out (GdkEventFocus
*)
261 old_focus
->grab_focus ();
269 EditorRoutes::enter_notify (GdkEventCrossing
*)
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 ();
285 EditorRoutes::leave_notify (GdkEventCrossing
*)
287 selection_countdown
= 0;
290 old_focus
->grab_focus ();
294 Keyboard::magic_widget_drop_focus ();
299 EditorRoutes::set_session (Session
* s
)
301 SessionHandlePtr::set_session (s
);
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());
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
);
321 boost::shared_ptr
<MidiTrack
> mt
;
322 mt
= rtv
->midi_track();
324 mt
->set_input_active (!mt
->input_active());
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
);
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
);
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
);
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
);
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
);
376 _session
->set_solo (rl
, !rtv
->route()->self_soloed(), Session::rt_cleanup
);
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
);
391 rtv
->route()->set_solo_isolated (!rtv
->route()->solo_isolated(), this);
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
);
405 rtv
->route()->set_solo_safe (!rtv
->route()->solo_safe(), this);
410 EditorRoutes::build_menu ()
412 using namespace Menu_Helpers
;
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
)));
432 EditorRoutes::show_menu ()
438 _menu
->popup (1, gtk_get_current_event_time());
442 EditorRoutes::redisplay ()
444 if (_no_redisplay
|| !_session
) {
448 TreeModel::Children rows
= _model
->children();
449 TreeModel::Children::iterator i
;
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
];
458 // just a "title" row
462 if (!_redisplay_does_not_reset_order_keys
) {
463 /* this reorder is caused by user action, so reassign sort order keys
466 route
->set_order_key (N_ ("editor"), n
);
469 bool visible
= (*i
)[_columns
.visible
];
471 /* show or hide the TimeAxisView */
473 tv
->set_marked_for_display (true);
474 position
+= tv
->show_at (position
, n
, &_editor
->edit_controls_vbox
);
475 tv
->clip_to_viewport ();
477 tv
->set_visibility (false);
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"));
509 EditorRoutes::route_deleted (Gtk::TreeModel::Path
const &)
511 if (!_session
|| _session
->deletion_in_progress()) {
515 /* this could require an order reset & sync */
516 _session
->set_remote_control_ids();
517 _ignore_reorder
= true;
519 _ignore_reorder
= false;
523 EditorRoutes::visible_changed (std::string
const & path
)
525 if (_session
&& _session
->deletion_in_progress()) {
531 if ((iter
= _model
->get_iter (path
))) {
532 TimeAxisView
* tv
= (*iter
)[_columns
.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();
542 _redisplay_does_not_reset_order_keys
= false;
546 EditorRoutes::routes_added (list
<RouteTimeAxisView
*> routes
)
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);
566 row
[_columns
.is_input_active
] = midi_trk
->input_active ();
567 row
[_columns
.is_midi
] = true;
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 ();
618 _redisplay_does_not_sync_order_keys
= false;
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
630 //update_canvas_now ();
634 if (what
== "visible_tracks") {
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
651 _redisplay_does_not_sync_order_keys
= true;
653 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
654 if ((*ri
)[_columns
.tv
] == tv
) {
660 _redisplay_does_not_sync_order_keys
= false;
664 EditorRoutes::route_property_changed (const PropertyChange
& what_changed
, boost::weak_ptr
<Route
> r
)
666 if (!what_changed
.contains (ARDOUR::Properties::name
)) {
670 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed
, r
)
672 boost::shared_ptr
<Route
> route
= r
.lock ();
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
];
684 (*i
)[_columns
.text
] = route
->name();
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
;
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;
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;
740 EditorRoutes::reordered (TreeModel::Path
const &, TreeModel::iterator
const &, int* /*what*/)
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.
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()) {
759 bool changed
= false;
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
) {
776 _redisplay_does_not_reset_order_keys
= true;
778 /* `compact' new_order into a vector */
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;
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
];
807 row
[_columns
.visible
] = false;
812 /* XXX this seems like a hack and half, but its not clear where to put this
816 //reset_scrolling_region ();
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
];
836 (*i
)[_columns
.visible
] = yn
;
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
;
862 if ((atv
= dynamic_cast<AudioTimeAxisView
*>(tv
)) != 0) {
865 (*i
)[_columns
.visible
] = yn
;
869 if (atv
->is_audio_track()) {
870 (*i
)[_columns
.visible
] = yn
;
875 if (!atv
->is_audio_track()) {
876 (*i
)[_columns
.visible
] = yn
;
881 else if ((mtv
= dynamic_cast<MidiTimeAxisView
*>(tv
)) != 0) {
884 (*i
)[_columns
.visible
] = yn
;
888 if (mtv
->is_midi_track()) {
889 (*i
)[_columns
.visible
] = yn
;
900 EditorRoutes::hide_all_routes ()
902 set_all_tracks_visibility (false);
906 EditorRoutes::show_all_routes ()
908 set_all_tracks_visibility (true);
912 EditorRoutes::show_all_audiotracks()
914 set_all_audio_midi_visibility (1, true);
917 EditorRoutes::hide_all_audiotracks ()
919 set_all_audio_midi_visibility (1, false);
923 EditorRoutes::show_all_audiobus ()
925 set_all_audio_midi_visibility (2, true);
928 EditorRoutes::hide_all_audiobus ()
930 set_all_audio_midi_visibility (2, false);
934 EditorRoutes::show_all_miditracks()
936 set_all_audio_midi_visibility (3, true);
939 EditorRoutes::hide_all_miditracks ()
941 set_all_audio_midi_visibility (3, false);
945 EditorRoutes::key_press (GdkEventKey
* ev
)
948 boost::shared_ptr
<RouteList
> rl (new RouteList
);
951 switch (ev
->keyval
) {
953 case GDK_ISO_Left_Tab
:
955 /* If we appear to be editing something, leave that cleanly and appropriately.
958 name_editable
->editing_done ();
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
);
967 treeview_select_next (_display
, _model
, col
);
974 if (get_relevant_routes (rl
)) {
975 _session
->set_mute (rl
, !rl
->front()->muted(), Session::rt_cleanup
);
981 if (Config
->get_solo_control_is_listen_control()) {
982 _session
->set_listen (rl
, !rl
->front()->listening_via_monitor(), Session::rt_cleanup
);
984 _session
->set_solo (rl
, !rl
->front()->self_soloed(), Session::rt_cleanup
);
990 if (get_relevant_routes (rl
)) {
991 _session
->set_record_enabled (rl
, !rl
->front()->record_enabled(), Session::rt_cleanup
);
1003 EditorRoutes::get_relevant_routes (boost::shared_ptr
<RouteList
> rl
)
1006 RouteTimeAxisView
* rtv
;
1007 RefPtr
<TreeSelection
> selection
= _display
.get_selection();
1011 if (selection
->count_selected_rows() != 0) {
1015 RefPtr
<TreeModel
> tm
= RefPtr
<TreeModel
>::cast_dynamic (_model
);
1016 iter
= selection
->get_selected (tm
);
1019 /* use mouse pointer */
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
);
1033 tv
= (*iter
)[_columns
.tv
];
1035 rtv
= dynamic_cast<RouteTimeAxisView
*>(tv
);
1037 rl
->push_back (rtv
->route());
1042 return !rl
->empty();
1046 EditorRoutes::button_press (GdkEventButton
* ev
)
1048 if (Keyboard::is_context_menu_event (ev
)) {
1053 //Scroll editor canvas to selected track
1054 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
1056 TreeModel::Path path
;
1057 TreeViewColumn
*tvc
;
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
1077 _editor
->reset_y_origin (y_pos
);
1085 EditorRoutes::selection_filter (Glib::RefPtr
<TreeModel
> const &, TreeModel::Path
const&, bool /*selected*/)
1087 if (selection_countdown
) {
1088 if (--selection_countdown
== 0) {
1091 /* no selection yet ... */
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"));
1106 EditorRoutes::initial_display ()
1108 suspend_redisplay ();
1112 resume_redisplay ();
1116 boost::shared_ptr
<RouteList
> routes
= _session
->get_routes();
1117 RouteList
r (*routes
);
1118 EditorOrderRouteSorter 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;
1148 resume_redisplay ();
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();
1157 _redisplay_does_not_sync_order_keys
= false;
1161 EditorRoutes::display_drag_data_received (const RefPtr
<Gdk::DragContext
>& context
,
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
);
1171 context
->drag_finish (true, false, time
);
1175 EditorRoutes::move_selected_tracks (bool up
)
1177 if (_editor
->selection
->tracks
.empty()) {
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
;
1199 trailing
= view_routes
.begin();
1200 leading
= view_routes
.begin();
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
);
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; }
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.
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.
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.
1259 view_routes
.erase (leading
);
1268 if (leading
== view_routes
.begin()) {
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"));
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
);
1299 (*i
)[_columns
.is_input_active
] = mt
->input_active();
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;
1321 (*i
)[_columns
.rec_state
] = 2;
1323 } else if (mt
&& mt
->step_editing()) {
1324 (*i
)[_columns
.rec_state
] = 3;
1326 (*i
)[_columns
.rec_state
] = 0;
1329 (*i
)[_columns
.name_editable
] = !route
->record_enabled ();
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
);
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
);
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;
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;
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
]);
1394 EditorRoutes::clear ()
1396 _display
.set_model (Glib::RefPtr
<Gtk::TreeStore
> (0));
1398 _display
.set_model (_model
);
1402 EditorRoutes::name_edit_started (CellEditable
* ce
, const Glib::ustring
&)
1406 /* give it a special name */
1408 Gtk::Entry
*e
= dynamic_cast<Gtk::Entry
*> (ce
);
1411 e
->set_name (X_("RouteNameEditorEntry"));
1416 EditorRoutes::name_edit (std::string
const & path
, std::string
const & new_text
)
1420 TreeIter iter
= _model
->get_iter (path
);
1426 boost::shared_ptr
<Route
> route
= (*iter
)[_columns
.route
];
1428 if (route
&& route
->name() != new_text
) {
1429 route
->set_name (new_text
);
1434 EditorRoutes::solo_changed_so_update_mute ()
1436 update_mute_display ();
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
);
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 ();