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 static const int column_width
= 22;
73 _scroller
.add (_display
);
74 _scroller
.set_policy (POLICY_NEVER
, POLICY_AUTOMATIC
);
76 _model
= ListStore::create (_columns
);
77 _display
.set_model (_model
);
79 // Record enable toggle
80 CellRendererPixbufMulti
* rec_col_renderer
= manage (new CellRendererPixbufMulti());
82 rec_col_renderer
->set_pixbuf (0, ::get_icon("record-normal-disabled"));
83 rec_col_renderer
->set_pixbuf (1, ::get_icon("record-normal-in-progress"));
84 rec_col_renderer
->set_pixbuf (2, ::get_icon("record-normal-enabled"));
85 rec_col_renderer
->set_pixbuf (3, ::get_icon("record-step"));
86 rec_col_renderer
->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed
));
88 TreeViewColumn
* rec_state_column
= manage (new TreeViewColumn("R", *rec_col_renderer
));
90 rec_state_column
->add_attribute(rec_col_renderer
->property_state(), _columns
.rec_state
);
91 rec_state_column
->add_attribute(rec_col_renderer
->property_visible(), _columns
.is_track
);
93 rec_state_column
->set_sizing(TREE_VIEW_COLUMN_FIXED
);
94 rec_state_column
->set_alignment(ALIGN_CENTER
);
95 rec_state_column
->set_expand(false);
96 rec_state_column
->set_fixed_width(column_width
);
100 CellRendererPixbufMulti
* input_active_col_renderer
= manage (new CellRendererPixbufMulti());
101 input_active_col_renderer
->set_pixbuf (0, ::get_icon("midi-input-inactive"));
102 input_active_col_renderer
->set_pixbuf (1, ::get_icon("midi-input-active"));
103 input_active_col_renderer
->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed
));
105 TreeViewColumn
* input_active_column
= manage (new TreeViewColumn ("I", *input_active_col_renderer
));
107 input_active_column
->add_attribute(input_active_col_renderer
->property_state(), _columns
.is_input_active
);
108 input_active_column
->add_attribute (input_active_col_renderer
->property_visible(), _columns
.is_midi
);
110 input_active_column
->set_sizing(TREE_VIEW_COLUMN_FIXED
);
111 input_active_column
->set_alignment(ALIGN_CENTER
);
112 input_active_column
->set_expand(false);
113 input_active_column
->set_fixed_width(column_width
);
115 // Mute enable toggle
116 CellRendererPixbufMulti
* mute_col_renderer
= manage (new CellRendererPixbufMulti());
118 mute_col_renderer
->set_pixbuf (0, ::get_icon("mute-disabled"));
119 mute_col_renderer
->set_pixbuf (1, ::get_icon("muted-by-others"));
120 mute_col_renderer
->set_pixbuf (2, ::get_icon("mute-enabled"));
121 mute_col_renderer
->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled
));
123 TreeViewColumn
* mute_state_column
= manage (new TreeViewColumn("M", *mute_col_renderer
));
125 mute_state_column
->add_attribute(mute_col_renderer
->property_state(), _columns
.mute_state
);
126 mute_state_column
->set_sizing(TREE_VIEW_COLUMN_FIXED
);
127 mute_state_column
->set_alignment(ALIGN_CENTER
);
128 mute_state_column
->set_expand(false);
129 mute_state_column
->set_fixed_width(15);
131 // Solo enable toggle
132 CellRendererPixbufMulti
* solo_col_renderer
= manage (new CellRendererPixbufMulti());
134 solo_col_renderer
->set_pixbuf (0, ::get_icon("solo-disabled"));
135 solo_col_renderer
->set_pixbuf (1, ::get_icon("solo-enabled"));
136 solo_col_renderer
->set_pixbuf (3, ::get_icon("soloed-by-others"));
137 solo_col_renderer
->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled
));
139 TreeViewColumn
* solo_state_column
= manage (new TreeViewColumn("S", *solo_col_renderer
));
141 solo_state_column
->add_attribute(solo_col_renderer
->property_state(), _columns
.solo_state
);
142 solo_state_column
->set_sizing(TREE_VIEW_COLUMN_FIXED
);
143 solo_state_column
->set_alignment(ALIGN_CENTER
);
144 solo_state_column
->set_expand(false);
145 solo_state_column
->set_fixed_width(column_width
);
147 // Solo isolate toggle
148 CellRendererPixbufMulti
* solo_iso_renderer
= manage (new CellRendererPixbufMulti());
150 solo_iso_renderer
->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
151 solo_iso_renderer
->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
152 solo_iso_renderer
->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled
));
154 TreeViewColumn
* solo_isolate_state_column
= manage (new TreeViewColumn("SI", *solo_iso_renderer
));
156 solo_isolate_state_column
->add_attribute(solo_iso_renderer
->property_state(), _columns
.solo_isolate_state
);
157 solo_isolate_state_column
->set_sizing(TREE_VIEW_COLUMN_FIXED
);
158 solo_isolate_state_column
->set_alignment(ALIGN_CENTER
);
159 solo_isolate_state_column
->set_expand(false);
160 solo_isolate_state_column
->set_fixed_width(column_width
);
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
));
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());
243 EditorRoutes::focus_in (GdkEventFocus
*)
245 Window
* win
= dynamic_cast<Window
*> (_scroller
.get_toplevel ());
248 old_focus
= win
->get_focus ();
255 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
260 EditorRoutes::focus_out (GdkEventFocus
*)
263 old_focus
->grab_focus ();
271 EditorRoutes::enter_notify (GdkEventCrossing
*)
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 ();
287 EditorRoutes::leave_notify (GdkEventCrossing
*)
289 selection_countdown
= 0;
292 old_focus
->grab_focus ();
296 Keyboard::magic_widget_drop_focus ();
301 EditorRoutes::set_session (Session
* s
)
303 SessionHandlePtr::set_session (s
);
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());
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
);
323 boost::shared_ptr
<MidiTrack
> mt
;
324 mt
= rtv
->midi_track();
326 mt
->set_input_active (!mt
->input_active());
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
);
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
);
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
);
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
);
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
);
378 _session
->set_solo (rl
, !rtv
->route()->self_soloed(), Session::rt_cleanup
);
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
);
393 rtv
->route()->set_solo_isolated (!rtv
->route()->solo_isolated(), this);
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
);
407 rtv
->route()->set_solo_safe (!rtv
->route()->solo_safe(), this);
412 EditorRoutes::build_menu ()
414 using namespace Menu_Helpers
;
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
)));
434 EditorRoutes::show_menu ()
440 _menu
->popup (1, gtk_get_current_event_time());
444 EditorRoutes::redisplay ()
446 if (_no_redisplay
|| !_session
) {
450 TreeModel::Children rows
= _model
->children();
451 TreeModel::Children::iterator i
;
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
];
460 // just a "title" row
464 if (!_redisplay_does_not_reset_order_keys
) {
465 /* this reorder is caused by user action, so reassign sort order keys
468 route
->set_order_key (N_ ("editor"), n
);
471 bool visible
= tv
->marked_for_display ();
473 /* show or hide the TimeAxisView */
475 position
+= tv
->show_at (position
, n
, &_editor
->edit_controls_vbox
);
476 tv
->clip_to_viewport ();
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
];
536 if (tv
->set_marked_for_display (!visible
)) {
537 _redisplay_does_not_reset_order_keys
= true;
538 _session
->set_remote_control_ids();
539 update_visibility ();
541 _redisplay_does_not_reset_order_keys
= false;
548 EditorRoutes::routes_added (list
<RouteTimeAxisView
*> routes
)
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);
568 row
[_columns
.is_input_active
] = midi_trk
->input_active ();
569 row
[_columns
.is_midi
] = true;
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 ();
620 _redisplay_does_not_sync_order_keys
= false;
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
632 //update_canvas_now ();
636 if (what
== "visible_tracks") {
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
653 _redisplay_does_not_sync_order_keys
= true;
655 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
656 if ((*ri
)[_columns
.tv
] == tv
) {
662 _redisplay_does_not_sync_order_keys
= false;
666 EditorRoutes::route_property_changed (const PropertyChange
& what_changed
, boost::weak_ptr
<Route
> r
)
668 if (!what_changed
.contains (ARDOUR::Properties::name
)) {
672 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed
, r
)
674 boost::shared_ptr
<Route
> route
= r
.lock ();
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
];
686 (*i
)[_columns
.text
] = route
->name();
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 ();
709 EditorRoutes::hide_track_in_display (TimeAxisView
& tv
)
711 TreeModel::Children rows
= _model
->children();
712 TreeModel::Children::iterator i
;
714 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
715 if ((*i
)[_columns
.tv
] == &tv
) {
716 tv
.set_marked_for_display (false);
717 (*i
)[_columns
.visible
] = false;
725 EditorRoutes::show_track_in_display (TimeAxisView
& tv
)
727 TreeModel::Children rows
= _model
->children();
728 TreeModel::Children::iterator i
;
731 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
732 if ((*i
)[_columns
.tv
] == &tv
) {
733 tv
.set_marked_for_display (true);
734 (*i
)[_columns
.visible
] = true;
742 EditorRoutes::reordered (TreeModel::Path
const &, TreeModel::iterator
const &, int* /*what*/)
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.
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()) {
761 bool changed
= false;
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
) {
778 _redisplay_does_not_reset_order_keys
= true;
780 /* `compact' new_order into a vector */
782 for (map
<int, int>::const_iterator i
= new_order
.begin(); i
!= new_order
.end(); ++i
) {
783 co
.push_back (i
->second
);
786 assert (co
.size() == _model
->children().size ());
788 _model
->reorder (co
);
789 _redisplay_does_not_reset_order_keys
= false;
795 EditorRoutes::hide_all_tracks (bool /*with_select*/)
797 TreeModel::Children rows
= _model
->children();
798 TreeModel::Children::iterator i
;
800 suspend_redisplay ();
802 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
804 TreeModel::Row row
= (*i
);
805 TimeAxisView
*tv
= row
[_columns
.tv
];
811 row
[_columns
.visible
] = false;
816 /* XXX this seems like a hack and half, but its not clear where to put this
820 //reset_scrolling_region ();
824 EditorRoutes::set_all_tracks_visibility (bool yn
)
826 TreeModel::Children rows
= _model
->children();
827 TreeModel::Children::iterator i
;
829 suspend_redisplay ();
831 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
833 TreeModel::Row row
= (*i
);
834 TimeAxisView
* tv
= row
[_columns
.tv
];
840 (*i
)[_columns
.visible
] = yn
;
847 EditorRoutes::set_all_audio_midi_visibility (int tracks
, bool yn
)
849 TreeModel::Children rows
= _model
->children();
850 TreeModel::Children::iterator i
;
852 suspend_redisplay ();
854 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
856 TreeModel::Row row
= (*i
);
857 TimeAxisView
* tv
= row
[_columns
.tv
];
859 AudioTimeAxisView
* atv
;
860 MidiTimeAxisView
* mtv
;
866 if ((atv
= dynamic_cast<AudioTimeAxisView
*>(tv
)) != 0) {
869 (*i
)[_columns
.visible
] = yn
;
873 if (atv
->is_audio_track()) {
874 (*i
)[_columns
.visible
] = yn
;
879 if (!atv
->is_audio_track()) {
880 (*i
)[_columns
.visible
] = yn
;
885 else if ((mtv
= dynamic_cast<MidiTimeAxisView
*>(tv
)) != 0) {
888 (*i
)[_columns
.visible
] = yn
;
892 if (mtv
->is_midi_track()) {
893 (*i
)[_columns
.visible
] = yn
;
904 EditorRoutes::hide_all_routes ()
906 set_all_tracks_visibility (false);
910 EditorRoutes::show_all_routes ()
912 set_all_tracks_visibility (true);
916 EditorRoutes::show_all_audiotracks()
918 set_all_audio_midi_visibility (1, true);
921 EditorRoutes::hide_all_audiotracks ()
923 set_all_audio_midi_visibility (1, false);
927 EditorRoutes::show_all_audiobus ()
929 set_all_audio_midi_visibility (2, true);
932 EditorRoutes::hide_all_audiobus ()
934 set_all_audio_midi_visibility (2, false);
938 EditorRoutes::show_all_miditracks()
940 set_all_audio_midi_visibility (3, true);
943 EditorRoutes::hide_all_miditracks ()
945 set_all_audio_midi_visibility (3, false);
949 EditorRoutes::key_press (GdkEventKey
* ev
)
952 boost::shared_ptr
<RouteList
> rl (new RouteList
);
955 switch (ev
->keyval
) {
957 case GDK_ISO_Left_Tab
:
959 /* If we appear to be editing something, leave that cleanly and appropriately.
962 name_editable
->editing_done ();
966 col
= _display
.get_column (_name_column
); // select&focus on name column
968 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::TertiaryModifier
)) {
969 treeview_select_previous (_display
, _model
, col
);
971 treeview_select_next (_display
, _model
, col
);
978 if (get_relevant_routes (rl
)) {
979 _session
->set_mute (rl
, !rl
->front()->muted(), Session::rt_cleanup
);
985 if (Config
->get_solo_control_is_listen_control()) {
986 _session
->set_listen (rl
, !rl
->front()->listening_via_monitor(), Session::rt_cleanup
);
988 _session
->set_solo (rl
, !rl
->front()->self_soloed(), Session::rt_cleanup
);
994 if (get_relevant_routes (rl
)) {
995 _session
->set_record_enabled (rl
, !rl
->front()->record_enabled(), Session::rt_cleanup
);
1007 EditorRoutes::get_relevant_routes (boost::shared_ptr
<RouteList
> rl
)
1010 RouteTimeAxisView
* rtv
;
1011 RefPtr
<TreeSelection
> selection
= _display
.get_selection();
1015 if (selection
->count_selected_rows() != 0) {
1019 RefPtr
<TreeModel
> tm
= RefPtr
<TreeModel
>::cast_dynamic (_model
);
1020 iter
= selection
->get_selected (tm
);
1023 /* use mouse pointer */
1028 _display
.get_pointer (x
, y
);
1029 _display
.convert_widget_to_bin_window_coords (x
, y
, bx
, by
);
1031 if (_display
.get_path_at_pos (bx
, by
, path
)) {
1032 iter
= _model
->get_iter (path
);
1037 tv
= (*iter
)[_columns
.tv
];
1039 rtv
= dynamic_cast<RouteTimeAxisView
*>(tv
);
1041 rl
->push_back (rtv
->route());
1046 return !rl
->empty();
1050 EditorRoutes::button_press (GdkEventButton
* ev
)
1052 if (Keyboard::is_context_menu_event (ev
)) {
1057 //Scroll editor canvas to selected track
1058 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
1060 TreeModel::Path path
;
1061 TreeViewColumn
*tvc
;
1065 _display
.get_path_at_pos ((int) ev
->x
, (int) ev
->y
, path
, tvc
, cell_x
, cell_y
);
1067 // Get the model row.
1068 Gtk::TreeModel::Row row
= *_model
->get_iter (path
);
1070 TimeAxisView
*tv
= row
[_columns
.tv
];
1072 int y_pos
= tv
->y_position();
1074 //Clamp the y pos so that we do not extend beyond the canvas full height.
1075 if (_editor
->full_canvas_height
- y_pos
< _editor
->_canvas_height
){
1076 y_pos
= _editor
->full_canvas_height
- _editor
->_canvas_height
;
1079 //Only scroll to if the track is visible
1081 _editor
->reset_y_origin (y_pos
);
1089 EditorRoutes::selection_filter (Glib::RefPtr
<TreeModel
> const &, TreeModel::Path
const&, bool /*selected*/)
1091 if (selection_countdown
) {
1092 if (--selection_countdown
== 0) {
1095 /* no selection yet ... */
1102 struct EditorOrderRouteSorter
{
1103 bool operator() (boost::shared_ptr
<Route
> a
, boost::shared_ptr
<Route
> b
) {
1104 /* use of ">" forces the correct sort order */
1105 return a
->order_key (N_ ("editor")) < b
->order_key (N_ ("editor"));
1110 EditorRoutes::initial_display ()
1112 suspend_redisplay ();
1116 resume_redisplay ();
1120 boost::shared_ptr
<RouteList
> routes
= _session
->get_routes();
1121 RouteList
r (*routes
);
1122 EditorOrderRouteSorter sorter
;
1125 _editor
->handle_new_route (r
);
1127 /* don't show master bus in a new session */
1129 if (ARDOUR_UI::instance()->session_is_new ()) {
1131 TreeModel::Children rows
= _model
->children();
1132 TreeModel::Children::iterator i
;
1134 _no_redisplay
= true;
1136 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
1138 TimeAxisView
*tv
= (*i
)[_columns
.tv
];
1139 RouteTimeAxisView
*rtv
;
1141 if ((rtv
= dynamic_cast<RouteTimeAxisView
*>(tv
)) != 0) {
1142 if (rtv
->route()->is_master()) {
1143 _display
.get_selection()->unselect (i
);
1148 _no_redisplay
= false;
1152 resume_redisplay ();
1156 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path
const &, Gtk::TreeModel::iterator
const &, int* /*new_order*/)
1158 _redisplay_does_not_sync_order_keys
= true;
1159 _session
->set_remote_control_ids();
1161 _redisplay_does_not_sync_order_keys
= false;
1165 EditorRoutes::display_drag_data_received (const RefPtr
<Gdk::DragContext
>& context
,
1167 const SelectionData
& data
,
1168 guint info
, guint time
)
1170 if (data
.get_target() == "GTK_TREE_MODEL_ROW") {
1171 _display
.on_drag_data_received (context
, x
, y
, data
, info
, time
);
1175 context
->drag_finish (true, false, time
);
1179 EditorRoutes::move_selected_tracks (bool up
)
1181 if (_editor
->selection
->tracks
.empty()) {
1185 typedef std::pair
<TimeAxisView
*,boost::shared_ptr
<Route
> > ViewRoute
;
1186 std::list
<ViewRoute
> view_routes
;
1187 std::vector
<int> neworder
;
1188 TreeModel::Children rows
= _model
->children();
1189 TreeModel::Children::iterator ri
;
1191 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
1192 TimeAxisView
* tv
= (*ri
)[_columns
.tv
];
1193 boost::shared_ptr
<Route
> route
= (*ri
)[_columns
.route
];
1195 view_routes
.push_back (ViewRoute (tv
, route
));
1198 list
<ViewRoute
>::iterator trailing
;
1199 list
<ViewRoute
>::iterator leading
;
1203 trailing
= view_routes
.begin();
1204 leading
= view_routes
.begin();
1208 while (leading
!= view_routes
.end()) {
1209 if (_editor
->selection
->selected (leading
->first
)) {
1210 view_routes
.insert (trailing
, ViewRoute (leading
->first
, leading
->second
));
1211 leading
= view_routes
.erase (leading
);
1220 /* if we could use reverse_iterator in list::insert, this code
1221 would be a beautiful reflection of the code above. but we can't
1222 and so it looks like a bit of a mess.
1225 trailing
= view_routes
.end();
1226 leading
= view_routes
.end();
1228 --leading
; if (leading
== view_routes
.begin()) { return; }
1234 if (_editor
->selection
->selected (leading
->first
)) {
1235 list
<ViewRoute
>::iterator tmp
;
1237 /* need to insert *after* trailing, not *before* it,
1238 which is what insert (iter, val) normally does.
1244 view_routes
.insert (tmp
, ViewRoute (leading
->first
, leading
->second
));
1246 /* can't use iter = cont.erase (iter); form here, because
1247 we need iter to move backwards.
1255 if (leading
== view_routes
.begin()) {
1256 /* the one we've just inserted somewhere else
1257 was the first in the list. erase this copy,
1258 and then break, because we're done.
1263 view_routes
.erase (leading
);
1272 if (leading
== view_routes
.begin()) {
1281 for (leading
= view_routes
.begin(); leading
!= view_routes
.end(); ++leading
) {
1282 neworder
.push_back (leading
->second
->order_key (N_ ("editor")));
1285 _model
->reorder (neworder
);
1287 _session
->sync_order_keys (N_ ("editor"));
1291 EditorRoutes::update_input_active_display ()
1293 TreeModel::Children rows
= _model
->children();
1294 TreeModel::Children::iterator i
;
1296 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
1297 boost::shared_ptr
<Route
> route
= (*i
)[_columns
.route
];
1299 if (boost::dynamic_pointer_cast
<Track
> (route
)) {
1300 boost::shared_ptr
<MidiTrack
> mt
= boost::dynamic_pointer_cast
<MidiTrack
> (route
);
1303 (*i
)[_columns
.is_input_active
] = mt
->input_active();
1310 EditorRoutes::update_rec_display ()
1312 TreeModel::Children rows
= _model
->children();
1313 TreeModel::Children::iterator i
;
1315 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
1316 boost::shared_ptr
<Route
> route
= (*i
)[_columns
.route
];
1318 if (boost::dynamic_pointer_cast
<Track
> (route
)) {
1319 boost::shared_ptr
<MidiTrack
> mt
= boost::dynamic_pointer_cast
<MidiTrack
> (route
);
1321 if (route
->record_enabled()) {
1322 if (_session
->record_status() == Session::Recording
) {
1323 (*i
)[_columns
.rec_state
] = 1;
1325 (*i
)[_columns
.rec_state
] = 2;
1327 } else if (mt
&& mt
->step_editing()) {
1328 (*i
)[_columns
.rec_state
] = 3;
1330 (*i
)[_columns
.rec_state
] = 0;
1333 (*i
)[_columns
.name_editable
] = !route
->record_enabled ();
1339 EditorRoutes::update_mute_display ()
1341 TreeModel::Children rows
= _model
->children();
1342 TreeModel::Children::iterator i
;
1344 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
1345 boost::shared_ptr
<Route
> route
= (*i
)[_columns
.route
];
1346 (*i
)[_columns
.mute_state
] = RouteUI::mute_visual_state (_session
, route
);
1351 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1353 TreeModel::Children rows
= _model
->children();
1354 TreeModel::Children::iterator i
;
1356 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
1357 boost::shared_ptr
<Route
> route
= (*i
)[_columns
.route
];
1358 (*i
)[_columns
.solo_state
] = RouteUI::solo_visual_state (route
);
1363 EditorRoutes::update_solo_isolate_display ()
1365 TreeModel::Children rows
= _model
->children();
1366 TreeModel::Children::iterator i
;
1368 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
1369 boost::shared_ptr
<Route
> route
= (*i
)[_columns
.route
];
1370 (*i
)[_columns
.solo_isolate_state
] = RouteUI::solo_isolate_visual_state (route
) > 0 ? 1 : 0;
1375 EditorRoutes::update_solo_safe_display ()
1377 TreeModel::Children rows
= _model
->children();
1378 TreeModel::Children::iterator i
;
1380 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
1381 boost::shared_ptr
<Route
> route
= (*i
)[_columns
.route
];
1382 (*i
)[_columns
.solo_safe_state
] = RouteUI::solo_safe_visual_state (route
) > 0 ? 1 : 0;
1387 EditorRoutes::views () const
1389 list
<TimeAxisView
*> v
;
1390 for (TreeModel::Children::iterator i
= _model
->children().begin(); i
!= _model
->children().end(); ++i
) {
1391 v
.push_back ((*i
)[_columns
.tv
]);
1398 EditorRoutes::clear ()
1400 _display
.set_model (Glib::RefPtr
<Gtk::TreeStore
> (0));
1402 _display
.set_model (_model
);
1406 EditorRoutes::name_edit_started (CellEditable
* ce
, const Glib::ustring
&)
1410 /* give it a special name */
1412 Gtk::Entry
*e
= dynamic_cast<Gtk::Entry
*> (ce
);
1415 e
->set_name (X_("RouteNameEditorEntry"));
1420 EditorRoutes::name_edit (std::string
const & path
, std::string
const & new_text
)
1424 TreeIter iter
= _model
->get_iter (path
);
1430 boost::shared_ptr
<Route
> route
= (*iter
)[_columns
.route
];
1432 if (route
&& route
->name() != new_text
) {
1433 route
->set_name (new_text
);
1438 EditorRoutes::solo_changed_so_update_mute ()
1440 update_mute_display ();
1444 EditorRoutes::show_tracks_with_regions_at_playhead ()
1446 boost::shared_ptr
<RouteList
> const r
= _session
->get_routes_with_regions_at (_session
->transport_frame ());
1448 set
<TimeAxisView
*> show
;
1449 for (RouteList::const_iterator i
= r
->begin(); i
!= r
->end(); ++i
) {
1450 TimeAxisView
* tav
= _editor
->axis_view_from_route (*i
);
1456 suspend_redisplay ();
1458 TreeModel::Children rows
= _model
->children ();
1459 for (TreeModel::Children::iterator i
= rows
.begin(); i
!= rows
.end(); ++i
) {
1460 TimeAxisView
* tv
= (*i
)[_columns
.tv
];
1461 (*i
)[_columns
.visible
] = (show
.find (tv
) != show
.end());
1464 resume_redisplay ();