more attempted cleanup of AU preset declarations
[ardour2.git] / gtk2_ardour / editor_routes.cc
blob1368979e5896ef313775fb2f61437db3011747e5
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);
90 rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
91 rec_state_column->set_alignment(ALIGN_CENTER);
92 rec_state_column->set_expand(false);
93 rec_state_column->set_fixed_width(15);
95 // Mute enable toggle
96 CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
98 mute_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
99 mute_col_renderer->set_pixbuf (1, ::get_icon("muted-by-others"));
100 mute_col_renderer->set_pixbuf (2, ::get_icon("mute-enabled"));
101 mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
103 TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
105 mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
106 mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
107 mute_state_column->set_alignment(ALIGN_CENTER);
108 mute_state_column->set_expand(false);
109 mute_state_column->set_fixed_width(15);
111 // Solo enable toggle
112 CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
114 solo_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
115 solo_col_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
116 solo_col_renderer->set_pixbuf (3, ::get_icon("soloed-by-others"));
117 solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
119 TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
121 solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
122 solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
123 solo_state_column->set_alignment(ALIGN_CENTER);
124 solo_state_column->set_expand(false);
125 solo_state_column->set_fixed_width(15);
127 // Solo isolate toggle
128 CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
130 solo_iso_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
131 solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolated"));
132 solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
134 TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
136 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
137 solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
138 solo_isolate_state_column->set_alignment(ALIGN_CENTER);
139 solo_isolate_state_column->set_expand(false);
140 solo_isolate_state_column->set_fixed_width(22);
142 // Solo safe toggle
143 CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
145 solo_safe_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
146 solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
147 solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
149 TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
150 solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
151 solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
152 solo_safe_state_column->set_alignment(ALIGN_CENTER);
153 solo_safe_state_column->set_expand(false);
154 solo_safe_state_column->set_fixed_width(22);
156 _display.append_column (*rec_state_column);
157 _display.append_column (*mute_state_column);
158 _display.append_column (*solo_state_column);
159 _display.append_column (*solo_isolate_state_column);
160 _display.append_column (*solo_safe_state_column);
162 int colnum = _display.append_column (_("Name"), _columns.text);
163 TreeViewColumn* c = _display.get_column (colnum-1);
164 c->set_data ("i_am_the_tab_column", (void*) 0xfeedface);
165 _display.append_column (_("V"), _columns.visible);
167 _display.set_headers_visible (true);
168 _display.set_name ("TrackListDisplay");
169 _display.get_selection()->set_mode (SELECTION_SINGLE);
170 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
171 _display.set_reorderable (true);
172 _display.set_rules_hint (true);
173 _display.set_size_request (100, -1);
174 _display.add_object_drag (_columns.route.index(), "routes");
176 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (5));
178 assert (name_cell);
179 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
181 TreeViewColumn* name_column = _display.get_column (5);
183 assert (name_column);
185 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
186 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
187 name_column->set_expand(true);
188 name_column->set_min_width(50);
190 name_cell->property_editable() = true;
191 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
193 // Set the visible column cell renderer to radio toggle
194 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (6));
196 visible_cell->property_activatable() = true;
197 visible_cell->property_radio() = false;
198 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
200 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (6));
201 visible_col->set_expand(false);
202 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
203 visible_col->set_fixed_width(30);
204 visible_col->set_alignment(ALIGN_CENTER);
206 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
207 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
209 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
210 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
212 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
213 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
215 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
216 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
218 _display.set_enable_search (false);
220 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
223 bool
224 EditorRoutes::focus_in (GdkEventFocus*)
226 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
228 if (win) {
229 old_focus = win->get_focus ();
230 } else {
231 old_focus = 0;
234 name_editable = 0;
236 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
237 return true;
240 bool
241 EditorRoutes::focus_out (GdkEventFocus*)
243 if (old_focus) {
244 old_focus->grab_focus ();
245 old_focus = 0;
248 return false;
251 bool
252 EditorRoutes::enter_notify (GdkEventCrossing*)
254 if (name_editable) {
255 return true;
258 /* arm counter so that ::selection_filter() will deny selecting anything for the
259 next two attempts to change selection status.
261 selection_countdown = 2;
262 _scroller.grab_focus ();
263 Keyboard::magic_widget_grab_focus ();
264 return false;
267 bool
268 EditorRoutes::leave_notify (GdkEventCrossing*)
270 selection_countdown = 0;
272 if (old_focus) {
273 old_focus->grab_focus ();
274 old_focus = 0;
277 Keyboard::magic_widget_drop_focus ();
278 return false;
281 void
282 EditorRoutes::set_session (Session* s)
284 SessionHandlePtr::set_session (s);
286 initial_display ();
288 if (_session) {
289 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
290 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
294 void
295 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
297 // Get the model row that has been toggled.
298 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
300 TimeAxisView* tv = row[_columns.tv];
301 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
303 if (rtv && rtv->track()) {
304 boost::shared_ptr<RouteList> rl (new RouteList);
305 rl->push_back (rtv->route());
306 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
310 void
311 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
313 // Get the model row that has been toggled.
314 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
316 TimeAxisView *tv = row[_columns.tv];
317 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
319 if (rtv != 0) {
320 boost::shared_ptr<RouteList> rl (new RouteList);
321 rl->push_back (rtv->route());
322 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
326 void
327 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
329 // Get the model row that has been toggled.
330 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
332 TimeAxisView *tv = row[_columns.tv];
333 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
335 if (rtv != 0) {
336 boost::shared_ptr<RouteList> rl (new RouteList);
337 rl->push_back (rtv->route());
338 if (Config->get_solo_control_is_listen_control()) {
339 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
340 } else {
341 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
346 void
347 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
349 // Get the model row that has been toggled.
350 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
352 TimeAxisView *tv = row[_columns.tv];
353 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
355 if (rtv) {
356 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
360 void
361 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
363 // Get the model row that has been toggled.
364 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
366 TimeAxisView *tv = row[_columns.tv];
367 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
369 if (rtv) {
370 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
374 void
375 EditorRoutes::build_menu ()
377 using namespace Menu_Helpers;
378 using namespace Gtk;
380 _menu = new Menu;
382 MenuList& items = _menu->items();
383 _menu->set_name ("ArdourContextMenu");
385 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
386 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
387 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
388 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
389 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
390 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
391 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
392 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
393 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
396 void
397 EditorRoutes::show_menu ()
399 if (_menu == 0) {
400 build_menu ();
403 _menu->popup (1, gtk_get_current_event_time());
406 void
407 EditorRoutes::redisplay ()
409 if (_no_redisplay || !_session) {
410 return;
413 TreeModel::Children rows = _model->children();
414 TreeModel::Children::iterator i;
415 uint32_t position;
416 int n;
418 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
419 TimeAxisView *tv = (*i)[_columns.tv];
420 boost::shared_ptr<Route> route = (*i)[_columns.route];
422 if (tv == 0) {
423 // just a "title" row
424 continue;
427 if (!_redisplay_does_not_reset_order_keys) {
428 /* this reorder is caused by user action, so reassign sort order keys
429 to tracks.
431 route->set_order_key (N_ ("editor"), n);
434 bool visible = (*i)[_columns.visible];
436 /* show or hide the TimeAxisView */
437 if (visible) {
438 tv->set_marked_for_display (true);
439 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
440 tv->clip_to_viewport ();
441 } else {
442 tv->set_marked_for_display (false);
443 tv->hide ();
446 n++;
450 /* whenever we go idle, update the track view list to reflect the new order.
451 we can't do this here, because we could mess up something that is traversing
452 the track order and has caused a redisplay of the list.
454 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
456 _editor->reset_controls_layout_height (position);
457 _editor->reset_controls_layout_width ();
458 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
459 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
461 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
463 We're increasing the size of the canvas while the bottom is visible.
464 We scroll down to keep in step with the controls layout.
466 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
469 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
470 _session->sync_order_keys (N_ ("editor"));
474 void
475 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
477 if (!_session || _session->deletion_in_progress()) {
478 return;
481 /* this could require an order reset & sync */
482 _session->set_remote_control_ids();
483 _ignore_reorder = true;
484 redisplay ();
485 _ignore_reorder = false;
488 void
489 EditorRoutes::visible_changed (std::string const & path)
491 if (_session && _session->deletion_in_progress()) {
492 return;
495 TreeIter iter;
497 if ((iter = _model->get_iter (path))) {
498 TimeAxisView* tv = (*iter)[_columns.tv];
499 if (tv) {
500 bool visible = (*iter)[_columns.visible];
501 (*iter)[_columns.visible] = !visible;
505 _redisplay_does_not_reset_order_keys = true;
506 _session->set_remote_control_ids();
507 redisplay ();
508 _redisplay_does_not_reset_order_keys = false;
511 void
512 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
514 TreeModel::Row row;
516 _redisplay_does_not_sync_order_keys = true;
517 suspend_redisplay ();
519 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
521 row = *(_model->append ());
523 row[_columns.text] = (*x)->route()->name();
524 row[_columns.visible] = (*x)->marked_for_display();
525 row[_columns.tv] = *x;
526 row[_columns.route] = (*x)->route ();
527 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
528 row[_columns.mute_state] = (*x)->route()->muted();
529 row[_columns.solo_state] = RouteUI::solo_visual_state ((*x)->route());
530 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
531 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
532 row[_columns.name_editable] = true;
534 _ignore_reorder = true;
536 /* added a new fresh one at the end */
537 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
538 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
541 _ignore_reorder = false;
543 boost::weak_ptr<Route> wr ((*x)->route());
545 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
546 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
548 if ((*x)->is_track()) {
549 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
550 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
553 if ((*x)->is_midi_track()) {
554 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
555 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
558 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
559 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
560 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
561 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
562 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
565 update_rec_display ();
566 update_mute_display ();
567 update_solo_display (true);
568 update_solo_isolate_display ();
569 update_solo_safe_display ();
570 resume_redisplay ();
571 _redisplay_does_not_sync_order_keys = false;
574 void
575 EditorRoutes::handle_gui_changes (string const & what, void*)
577 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
579 if (what == "track_height") {
580 /* Optional :make tracks change height while it happens, instead
581 of on first-idle
583 //update_canvas_now ();
584 redisplay ();
587 if (what == "visible_tracks") {
588 redisplay ();
592 void
593 EditorRoutes::route_removed (TimeAxisView *tv)
595 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
597 TreeModel::Children rows = _model->children();
598 TreeModel::Children::iterator ri;
600 /* the core model has changed, there is no need to sync
601 view orders.
604 _redisplay_does_not_sync_order_keys = true;
606 for (ri = rows.begin(); ri != rows.end(); ++ri) {
607 if ((*ri)[_columns.tv] == tv) {
608 _model->erase (ri);
609 break;
613 _redisplay_does_not_sync_order_keys = false;
616 void
617 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
619 if (!what_changed.contains (ARDOUR::Properties::name)) {
620 return;
623 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
625 boost::shared_ptr<Route> route = r.lock ();
627 if (!route) {
628 return;
631 TreeModel::Children rows = _model->children();
632 TreeModel::Children::iterator i;
634 for (i = rows.begin(); i != rows.end(); ++i) {
635 boost::shared_ptr<Route> t = (*i)[_columns.route];
636 if (t == route) {
637 (*i)[_columns.text] = route->name();
638 break;
643 void
644 EditorRoutes::update_visibility ()
646 TreeModel::Children rows = _model->children();
647 TreeModel::Children::iterator i;
649 suspend_redisplay ();
651 for (i = rows.begin(); i != rows.end(); ++i) {
652 TimeAxisView *tv = (*i)[_columns.tv];
653 (*i)[_columns.visible] = tv->marked_for_display ();
654 cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl;
657 resume_redisplay ();
660 void
661 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
663 TreeModel::Children rows = _model->children();
664 TreeModel::Children::iterator i;
666 for (i = rows.begin(); i != rows.end(); ++i) {
667 if ((*i)[_columns.tv] == &tv) {
668 (*i)[_columns.visible] = false;
669 break;
673 redisplay ();
676 void
677 EditorRoutes::show_track_in_display (TimeAxisView& tv)
679 TreeModel::Children rows = _model->children();
680 TreeModel::Children::iterator i;
682 for (i = rows.begin(); i != rows.end(); ++i) {
683 if ((*i)[_columns.tv] == &tv) {
684 (*i)[_columns.visible] = true;
685 break;
689 redisplay ();
692 void
693 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
695 redisplay ();
698 /** If src != "editor", take editor order keys from each route and use them to rearrange the
699 * route list so that the visual arrangement of routes matches the order keys from the routes.
701 void
702 EditorRoutes::sync_order_keys (string const & src)
704 vector<int> neworder;
705 TreeModel::Children rows = _model->children();
706 TreeModel::Children::iterator ri;
708 if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
709 return;
712 for (ri = rows.begin(); ri != rows.end(); ++ri) {
713 neworder.push_back (0);
716 bool changed = false;
717 int order;
719 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
720 boost::shared_ptr<Route> route = (*ri)[_columns.route];
722 int old_key = order;
723 int new_key = route->order_key (N_ ("editor"));
725 neworder[new_key] = old_key;
727 if (new_key != old_key) {
728 changed = true;
732 if (changed) {
733 _redisplay_does_not_reset_order_keys = true;
734 _model->reorder (neworder);
735 _redisplay_does_not_reset_order_keys = false;
740 void
741 EditorRoutes::hide_all_tracks (bool /*with_select*/)
743 TreeModel::Children rows = _model->children();
744 TreeModel::Children::iterator i;
746 suspend_redisplay ();
748 for (i = rows.begin(); i != rows.end(); ++i) {
750 TreeModel::Row row = (*i);
751 TimeAxisView *tv = row[_columns.tv];
753 if (tv == 0) {
754 continue;
757 row[_columns.visible] = false;
760 resume_redisplay ();
762 /* XXX this seems like a hack and half, but its not clear where to put this
763 otherwise.
766 //reset_scrolling_region ();
769 void
770 EditorRoutes::set_all_tracks_visibility (bool yn)
772 TreeModel::Children rows = _model->children();
773 TreeModel::Children::iterator i;
775 suspend_redisplay ();
777 for (i = rows.begin(); i != rows.end(); ++i) {
779 TreeModel::Row row = (*i);
780 TimeAxisView* tv = row[_columns.tv];
782 if (tv == 0) {
783 continue;
786 (*i)[_columns.visible] = yn;
789 resume_redisplay ();
792 void
793 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
795 TreeModel::Children rows = _model->children();
796 TreeModel::Children::iterator i;
798 suspend_redisplay ();
800 for (i = rows.begin(); i != rows.end(); ++i) {
802 TreeModel::Row row = (*i);
803 TimeAxisView* tv = row[_columns.tv];
805 AudioTimeAxisView* atv;
806 MidiTimeAxisView* mtv;
808 if (tv == 0) {
809 continue;
812 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
813 switch (tracks) {
814 case 0:
815 (*i)[_columns.visible] = yn;
816 break;
818 case 1:
819 if (atv->is_audio_track()) {
820 (*i)[_columns.visible] = yn;
822 break;
824 case 2:
825 if (!atv->is_audio_track()) {
826 (*i)[_columns.visible] = yn;
828 break;
831 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
832 switch (tracks) {
833 case 0:
834 (*i)[_columns.visible] = yn;
835 break;
837 case 3:
838 if (mtv->is_midi_track()) {
839 (*i)[_columns.visible] = yn;
841 break;
846 resume_redisplay ();
849 void
850 EditorRoutes::hide_all_routes ()
852 set_all_tracks_visibility (false);
855 void
856 EditorRoutes::show_all_routes ()
858 set_all_tracks_visibility (true);
861 void
862 EditorRoutes::show_all_audiotracks()
864 set_all_audio_midi_visibility (1, true);
866 void
867 EditorRoutes::hide_all_audiotracks ()
869 set_all_audio_midi_visibility (1, false);
872 void
873 EditorRoutes::show_all_audiobus ()
875 set_all_audio_midi_visibility (2, true);
877 void
878 EditorRoutes::hide_all_audiobus ()
880 set_all_audio_midi_visibility (2, false);
883 void
884 EditorRoutes::show_all_miditracks()
886 set_all_audio_midi_visibility (3, true);
888 void
889 EditorRoutes::hide_all_miditracks ()
891 set_all_audio_midi_visibility (3, false);
894 bool
895 EditorRoutes::key_press (GdkEventKey* ev)
897 TreeViewColumn *col;
898 boost::shared_ptr<RouteList> rl (new RouteList);
899 TreePath path;
901 switch (ev->keyval) {
902 case GDK_Tab:
903 case GDK_ISO_Left_Tab:
905 /* If we appear to be editing something, leave that cleanly and appropriately.
907 if (name_editable) {
908 name_editable->editing_done ();
909 name_editable = 0;
912 col = _display.get_column (5); // select&focus on name column
914 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
915 treeview_select_previous (_display, _model, col);
916 } else {
917 treeview_select_next (_display, _model, col);
920 return true;
921 break;
923 case 'm':
924 if (get_relevant_routes (rl)) {
925 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
927 return true;
928 break;
930 case 's':
931 if (Config->get_solo_control_is_listen_control()) {
932 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
933 } else {
934 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
936 return true;
937 break;
939 case 'r':
940 if (get_relevant_routes (rl)) {
941 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
943 break;
945 default:
946 break;
949 return false;
952 bool
953 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
955 TimeAxisView* tv;
956 RouteTimeAxisView* rtv;
957 RefPtr<TreeSelection> selection = _display.get_selection();
958 TreePath path;
959 TreeIter iter;
961 if (selection->count_selected_rows() != 0) {
963 /* use selection */
965 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
966 iter = selection->get_selected (tm);
968 } else {
969 /* use mouse pointer */
971 int x, y;
972 int bx, by;
974 _display.get_pointer (x, y);
975 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
977 if (_display.get_path_at_pos (bx, by, path)) {
978 iter = _model->get_iter (path);
982 if (iter) {
983 tv = (*iter)[_columns.tv];
984 if (tv) {
985 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
986 if (rtv) {
987 rl->push_back (rtv->route());
992 return !rl->empty();
995 bool
996 EditorRoutes::button_press (GdkEventButton* ev)
998 if (Keyboard::is_context_menu_event (ev)) {
999 show_menu ();
1000 return true;
1003 //Scroll editor canvas to selected track
1004 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1006 TreeModel::Path path;
1007 TreeViewColumn *tvc;
1008 int cell_x;
1009 int cell_y;
1011 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
1013 // Get the model row.
1014 Gtk::TreeModel::Row row = *_model->get_iter (path);
1016 TimeAxisView *tv = row[_columns.tv];
1018 int y_pos = tv->y_position();
1020 //Clamp the y pos so that we do not extend beyond the canvas full height.
1021 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1022 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1025 //Only scroll to if the track is visible
1026 if(y_pos != -1){
1027 _editor->reset_y_origin (y_pos);
1031 return false;
1034 bool
1035 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1037 if (selection_countdown) {
1038 if (--selection_countdown == 0) {
1039 return true;
1040 } else {
1041 /* no selection yet ... */
1042 return false;
1045 return true;
1048 struct EditorOrderRouteSorter {
1049 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1050 /* use of ">" forces the correct sort order */
1051 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1055 void
1056 EditorRoutes::initial_display ()
1058 suspend_redisplay ();
1059 _model->clear ();
1061 if (!_session) {
1062 resume_redisplay ();
1063 return;
1066 boost::shared_ptr<RouteList> routes = _session->get_routes();
1067 RouteList r (*routes);
1068 EditorOrderRouteSorter sorter;
1070 r.sort (sorter);
1071 _editor->handle_new_route (r);
1073 /* don't show master bus in a new session */
1075 if (ARDOUR_UI::instance()->session_is_new ()) {
1077 TreeModel::Children rows = _model->children();
1078 TreeModel::Children::iterator i;
1080 _no_redisplay = true;
1082 for (i = rows.begin(); i != rows.end(); ++i) {
1084 TimeAxisView *tv = (*i)[_columns.tv];
1085 RouteTimeAxisView *rtv;
1087 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1088 if (rtv->route()->is_master()) {
1089 _display.get_selection()->unselect (i);
1094 _no_redisplay = false;
1095 redisplay ();
1098 resume_redisplay ();
1101 void
1102 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1104 _redisplay_does_not_sync_order_keys = true;
1105 _session->set_remote_control_ids();
1106 redisplay ();
1107 _redisplay_does_not_sync_order_keys = false;
1110 void
1111 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1112 int x, int y,
1113 const SelectionData& data,
1114 guint info, guint time)
1116 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1117 _display.on_drag_data_received (context, x, y, data, info, time);
1118 return;
1121 context->drag_finish (true, false, time);
1124 void
1125 EditorRoutes::move_selected_tracks (bool up)
1127 if (_editor->selection->tracks.empty()) {
1128 return;
1131 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1132 std::list<ViewRoute> view_routes;
1133 std::vector<int> neworder;
1134 TreeModel::Children rows = _model->children();
1135 TreeModel::Children::iterator ri;
1137 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1138 TimeAxisView* tv = (*ri)[_columns.tv];
1139 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1141 view_routes.push_back (ViewRoute (tv, route));
1144 list<ViewRoute>::iterator trailing;
1145 list<ViewRoute>::iterator leading;
1147 if (up) {
1149 trailing = view_routes.begin();
1150 leading = view_routes.begin();
1152 ++leading;
1154 while (leading != view_routes.end()) {
1155 if (_editor->selection->selected (leading->first)) {
1156 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1157 leading = view_routes.erase (leading);
1158 } else {
1159 ++leading;
1160 ++trailing;
1164 } else {
1166 /* if we could use reverse_iterator in list::insert, this code
1167 would be a beautiful reflection of the code above. but we can't
1168 and so it looks like a bit of a mess.
1171 trailing = view_routes.end();
1172 leading = view_routes.end();
1174 --leading; if (leading == view_routes.begin()) { return; }
1175 --leading;
1176 --trailing;
1178 while (1) {
1180 if (_editor->selection->selected (leading->first)) {
1181 list<ViewRoute>::iterator tmp;
1183 /* need to insert *after* trailing, not *before* it,
1184 which is what insert (iter, val) normally does.
1187 tmp = trailing;
1188 tmp++;
1190 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1192 /* can't use iter = cont.erase (iter); form here, because
1193 we need iter to move backwards.
1196 tmp = leading;
1197 --tmp;
1199 bool done = false;
1201 if (leading == view_routes.begin()) {
1202 /* the one we've just inserted somewhere else
1203 was the first in the list. erase this copy,
1204 and then break, because we're done.
1206 done = true;
1209 view_routes.erase (leading);
1211 if (done) {
1212 break;
1215 leading = tmp;
1217 } else {
1218 if (leading == view_routes.begin()) {
1219 break;
1221 --leading;
1222 --trailing;
1227 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1228 neworder.push_back (leading->second->order_key (N_ ("editor")));
1231 _model->reorder (neworder);
1233 _session->sync_order_keys (N_ ("editor"));
1236 void
1237 EditorRoutes::update_rec_display ()
1239 TreeModel::Children rows = _model->children();
1240 TreeModel::Children::iterator i;
1242 for (i = rows.begin(); i != rows.end(); ++i) {
1243 boost::shared_ptr<Route> route = (*i)[_columns.route];
1245 if (boost::dynamic_pointer_cast<Track> (route)) {
1246 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1248 if (route->record_enabled()) {
1249 if (_session->record_status() == Session::Recording) {
1250 (*i)[_columns.rec_state] = 1;
1251 } else {
1252 (*i)[_columns.rec_state] = 2;
1254 } else if (mt && mt->step_editing()) {
1255 (*i)[_columns.rec_state] = 3;
1256 } else {
1257 (*i)[_columns.rec_state] = 0;
1260 (*i)[_columns.name_editable] = !route->record_enabled ();
1265 void
1266 EditorRoutes::update_mute_display ()
1268 TreeModel::Children rows = _model->children();
1269 TreeModel::Children::iterator i;
1271 for (i = rows.begin(); i != rows.end(); ++i) {
1272 boost::shared_ptr<Route> route = (*i)[_columns.route];
1273 (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
1277 void
1278 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1280 TreeModel::Children rows = _model->children();
1281 TreeModel::Children::iterator i;
1283 for (i = rows.begin(); i != rows.end(); ++i) {
1284 boost::shared_ptr<Route> route = (*i)[_columns.route];
1285 (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
1289 void
1290 EditorRoutes::update_solo_isolate_display ()
1292 TreeModel::Children rows = _model->children();
1293 TreeModel::Children::iterator i;
1295 for (i = rows.begin(); i != rows.end(); ++i) {
1296 boost::shared_ptr<Route> route = (*i)[_columns.route];
1297 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
1301 void
1302 EditorRoutes::update_solo_safe_display ()
1304 TreeModel::Children rows = _model->children();
1305 TreeModel::Children::iterator i;
1307 for (i = rows.begin(); i != rows.end(); ++i) {
1308 boost::shared_ptr<Route> route = (*i)[_columns.route];
1309 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
1313 list<TimeAxisView*>
1314 EditorRoutes::views () const
1316 list<TimeAxisView*> v;
1317 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1318 v.push_back ((*i)[_columns.tv]);
1321 return v;
1324 void
1325 EditorRoutes::clear ()
1327 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1328 _model->clear ();
1329 _display.set_model (_model);
1332 void
1333 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1335 name_editable = ce;
1337 /* give it a special name */
1339 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1341 if (e) {
1342 e->set_name (X_("RouteNameEditorEntry"));
1346 void
1347 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1349 name_editable = 0;
1351 TreeIter iter = _model->get_iter (path);
1353 if (!iter) {
1354 return;
1357 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1359 if (route && route->name() != new_text) {
1360 route->set_name (new_text);
1364 void
1365 EditorRoutes::solo_changed_so_update_mute ()
1367 update_mute_display ();
1370 void
1371 EditorRoutes::show_tracks_with_regions_at_playhead ()
1373 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1375 set<TimeAxisView*> show;
1376 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1377 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1378 if (tav) {
1379 show.insert (tav);
1383 suspend_redisplay ();
1385 TreeModel::Children rows = _model->children ();
1386 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1387 TimeAxisView* tv = (*i)[_columns.tv];
1388 (*i)[_columns.visible] = (show.find (tv) != show.end());
1391 resume_redisplay ();