fix rounding errors and bbox glitches that led to lines missing redraws, plus a few...
[ardour2.git] / gtk2_ardour / editor_routes.cc
blob2953e33dba474be9a33711230e2eeae1f7c42cfb
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 /* arm counter so that ::selection_filter() will deny selecting anything for the
255 next two attempts to change selection status.
257 selection_countdown = 2;
258 _scroller.grab_focus ();
259 Keyboard::magic_widget_grab_focus ();
260 return false;
263 bool
264 EditorRoutes::leave_notify (GdkEventCrossing*)
266 selection_countdown = 0;
268 if (old_focus) {
269 old_focus->grab_focus ();
270 old_focus = 0;
273 Keyboard::magic_widget_drop_focus ();
274 return false;
277 void
278 EditorRoutes::set_session (Session* s)
280 SessionHandlePtr::set_session (s);
282 initial_display ();
284 if (_session) {
285 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
286 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
290 void
291 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
293 // Get the model row that has been toggled.
294 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
296 TimeAxisView *tv = row[_columns.tv];
297 AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
299 if (atv != 0 && atv->is_audio_track()){
300 boost::shared_ptr<RouteList> rl (new RouteList);
301 rl->push_back (atv->route());
302 _session->set_record_enabled (rl, !atv->track()->record_enabled(), Session::rt_cleanup);
306 void
307 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
309 // Get the model row that has been toggled.
310 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
312 TimeAxisView *tv = row[_columns.tv];
313 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
315 if (rtv != 0) {
316 boost::shared_ptr<RouteList> rl (new RouteList);
317 rl->push_back (rtv->route());
318 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
322 void
323 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
325 // Get the model row that has been toggled.
326 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
328 TimeAxisView *tv = row[_columns.tv];
329 AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
331 if (atv != 0) {
332 boost::shared_ptr<RouteList> rl (new RouteList);
333 rl->push_back (atv->route());
334 _session->set_solo (rl, !atv->route()->soloed(), Session::rt_cleanup);
338 void
339 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
341 // Get the model row that has been toggled.
342 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
344 TimeAxisView *tv = row[_columns.tv];
345 AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
347 if (atv != 0) {
348 atv->route()->set_solo_isolated (!atv->route()->solo_isolated(), this);
352 void
353 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
355 // Get the model row that has been toggled.
356 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
358 TimeAxisView *tv = row[_columns.tv];
359 AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
361 if (atv != 0) {
362 atv->route()->set_solo_safe (!atv->route()->solo_safe(), this);
366 void
367 EditorRoutes::build_menu ()
369 using namespace Menu_Helpers;
370 using namespace Gtk;
372 _menu = new Menu;
374 MenuList& items = _menu->items();
375 _menu->set_name ("ArdourContextMenu");
377 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
378 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
379 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
380 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
381 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
382 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
383 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
384 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
385 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
388 void
389 EditorRoutes::show_menu ()
391 if (_menu == 0) {
392 build_menu ();
395 _menu->popup (1, gtk_get_current_event_time());
398 void
399 EditorRoutes::redisplay ()
401 if (_no_redisplay || !_session) {
402 return;
405 TreeModel::Children rows = _model->children();
406 TreeModel::Children::iterator i;
407 uint32_t position;
408 int n;
410 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
411 TimeAxisView *tv = (*i)[_columns.tv];
412 boost::shared_ptr<Route> route = (*i)[_columns.route];
414 if (tv == 0) {
415 // just a "title" row
416 continue;
419 if (!_redisplay_does_not_reset_order_keys) {
420 /* this reorder is caused by user action, so reassign sort order keys
421 to tracks.
423 route->set_order_key (N_ ("editor"), n);
426 bool visible = (*i)[_columns.visible];
428 /* show or hide the TimeAxisView */
429 if (visible) {
430 tv->set_marked_for_display (true);
431 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
432 tv->clip_to_viewport ();
433 } else {
434 tv->set_marked_for_display (false);
435 tv->hide ();
438 n++;
441 /* whenever we go idle, update the track view list to reflect the new order.
442 we can't do this here, because we could mess up something that is traversing
443 the track order and has caused a redisplay of the list.
445 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
447 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
448 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
450 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
452 We're increasing the size of the canvas while the bottom is visible.
453 We scroll down to keep in step with the controls layout.
455 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
458 if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
459 _session->sync_order_keys (N_ ("editor"));
463 void
464 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
466 if (!_session || _session->deletion_in_progress()) {
467 return;
470 /* this could require an order reset & sync */
471 _session->set_remote_control_ids();
472 _ignore_reorder = true;
473 redisplay ();
474 _ignore_reorder = false;
477 void
478 EditorRoutes::visible_changed (std::string const & path)
480 if (_session && _session->deletion_in_progress()) {
481 return;
484 TreeIter iter;
486 if ((iter = _model->get_iter (path))) {
487 TimeAxisView* tv = (*iter)[_columns.tv];
488 if (tv) {
489 bool visible = (*iter)[_columns.visible];
490 (*iter)[_columns.visible] = !visible;
494 _redisplay_does_not_reset_order_keys = true;
495 _session->set_remote_control_ids();
496 redisplay ();
497 _redisplay_does_not_reset_order_keys = false;
500 void
501 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
503 TreeModel::Row row;
505 _redisplay_does_not_sync_order_keys = true;
506 suspend_redisplay ();
508 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
510 row = *(_model->append ());
512 row[_columns.text] = (*x)->route()->name();
513 row[_columns.visible] = (*x)->marked_for_display();
514 row[_columns.tv] = *x;
515 row[_columns.route] = (*x)->route ();
516 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
517 row[_columns.mute_state] = (*x)->route()->muted();
518 row[_columns.solo_state] = (*x)->route()->soloed();
519 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
520 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
521 row[_columns.name_editable] = true;
523 _ignore_reorder = true;
525 /* added a new fresh one at the end */
526 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
527 (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
530 _ignore_reorder = false;
532 boost::weak_ptr<Route> wr ((*x)->route());
534 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
535 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
537 if ((*x)->is_track()) {
538 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
539 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
542 if ((*x)->is_midi_track()) {
543 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
544 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
547 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
548 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
549 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
550 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
553 update_rec_display ();
554 update_mute_display ();
555 update_solo_display (true);
556 update_solo_isolate_display ();
557 update_solo_safe_display ();
558 resume_redisplay ();
559 _redisplay_does_not_sync_order_keys = false;
562 void
563 EditorRoutes::handle_gui_changes (string const & what, void*)
565 ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
567 if (what == "track_height") {
568 /* Optional :make tracks change height while it happens, instead
569 of on first-idle
571 //update_canvas_now ();
572 redisplay ();
575 if (what == "visible_tracks") {
576 redisplay ();
580 void
581 EditorRoutes::route_removed (TimeAxisView *tv)
583 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
585 TreeModel::Children rows = _model->children();
586 TreeModel::Children::iterator ri;
588 /* the core model has changed, there is no need to sync
589 view orders.
592 _redisplay_does_not_sync_order_keys = true;
594 for (ri = rows.begin(); ri != rows.end(); ++ri) {
595 if ((*ri)[_columns.tv] == tv) {
596 _model->erase (ri);
597 break;
601 _redisplay_does_not_sync_order_keys = false;
604 void
605 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
607 if (!what_changed.contains (ARDOUR::Properties::name)) {
608 return;
611 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
613 boost::shared_ptr<Route> route = r.lock ();
615 if (!route) {
616 return;
619 TreeModel::Children rows = _model->children();
620 TreeModel::Children::iterator i;
622 for (i = rows.begin(); i != rows.end(); ++i) {
623 boost::shared_ptr<Route> t = (*i)[_columns.route];
624 if (t == route) {
625 (*i)[_columns.text] = route->name();
626 break;
631 void
632 EditorRoutes::update_visibility ()
634 TreeModel::Children rows = _model->children();
635 TreeModel::Children::iterator i;
637 suspend_redisplay ();
639 for (i = rows.begin(); i != rows.end(); ++i) {
640 TimeAxisView *tv = (*i)[_columns.tv];
641 (*i)[_columns.visible] = tv->marked_for_display ();
642 cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl;
645 resume_redisplay ();
648 void
649 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
651 TreeModel::Children rows = _model->children();
652 TreeModel::Children::iterator i;
654 for (i = rows.begin(); i != rows.end(); ++i) {
655 if ((*i)[_columns.tv] == &tv) {
656 (*i)[_columns.visible] = false;
657 break;
661 redisplay ();
664 void
665 EditorRoutes::show_track_in_display (TimeAxisView& tv)
667 TreeModel::Children rows = _model->children();
668 TreeModel::Children::iterator i;
670 for (i = rows.begin(); i != rows.end(); ++i) {
671 if ((*i)[_columns.tv] == &tv) {
672 (*i)[_columns.visible] = true;
673 break;
677 redisplay ();
680 void
681 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
683 redisplay ();
686 /** If src != "editor", take editor order keys from each route and use them to rearrange the
687 * route list so that the visual arrangement of routes matches the order keys from the routes.
689 void
690 EditorRoutes::sync_order_keys (string const & src)
692 vector<int> neworder;
693 TreeModel::Children rows = _model->children();
694 TreeModel::Children::iterator ri;
696 if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
697 return;
700 for (ri = rows.begin(); ri != rows.end(); ++ri) {
701 neworder.push_back (0);
704 bool changed = false;
705 int order;
707 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
708 boost::shared_ptr<Route> route = (*ri)[_columns.route];
710 int old_key = order;
711 int new_key = route->order_key (N_ ("editor"));
713 neworder[new_key] = old_key;
715 if (new_key != old_key) {
716 changed = true;
720 if (changed) {
721 _redisplay_does_not_reset_order_keys = true;
722 _model->reorder (neworder);
723 _redisplay_does_not_reset_order_keys = false;
728 void
729 EditorRoutes::hide_all_tracks (bool /*with_select*/)
731 TreeModel::Children rows = _model->children();
732 TreeModel::Children::iterator i;
734 suspend_redisplay ();
736 for (i = rows.begin(); i != rows.end(); ++i) {
738 TreeModel::Row row = (*i);
739 TimeAxisView *tv = row[_columns.tv];
741 if (tv == 0) {
742 continue;
745 row[_columns.visible] = false;
748 resume_redisplay ();
750 /* XXX this seems like a hack and half, but its not clear where to put this
751 otherwise.
754 //reset_scrolling_region ();
757 void
758 EditorRoutes::set_all_tracks_visibility (bool yn)
760 TreeModel::Children rows = _model->children();
761 TreeModel::Children::iterator i;
763 suspend_redisplay ();
765 for (i = rows.begin(); i != rows.end(); ++i) {
767 TreeModel::Row row = (*i);
768 TimeAxisView* tv = row[_columns.tv];
770 if (tv == 0) {
771 continue;
774 (*i)[_columns.visible] = yn;
777 resume_redisplay ();
780 void
781 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
783 TreeModel::Children rows = _model->children();
784 TreeModel::Children::iterator i;
786 suspend_redisplay ();
788 for (i = rows.begin(); i != rows.end(); ++i) {
790 TreeModel::Row row = (*i);
791 TimeAxisView* tv = row[_columns.tv];
793 AudioTimeAxisView* atv;
794 MidiTimeAxisView* mtv;
796 if (tv == 0) {
797 continue;
800 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
801 switch (tracks) {
802 case 0:
803 (*i)[_columns.visible] = yn;
804 break;
806 case 1:
807 if (atv->is_audio_track()) {
808 (*i)[_columns.visible] = yn;
810 break;
812 case 2:
813 if (!atv->is_audio_track()) {
814 (*i)[_columns.visible] = yn;
816 break;
819 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
820 switch (tracks) {
821 case 0:
822 (*i)[_columns.visible] = yn;
823 break;
825 case 3:
826 if (mtv->is_midi_track()) {
827 (*i)[_columns.visible] = yn;
829 break;
834 resume_redisplay ();
837 void
838 EditorRoutes::hide_all_routes ()
840 set_all_tracks_visibility (false);
843 void
844 EditorRoutes::show_all_routes ()
846 set_all_tracks_visibility (true);
849 void
850 EditorRoutes::show_all_audiotracks()
852 set_all_audio_midi_visibility (1, true);
854 void
855 EditorRoutes::hide_all_audiotracks ()
857 set_all_audio_midi_visibility (1, false);
860 void
861 EditorRoutes::show_all_audiobus ()
863 set_all_audio_midi_visibility (2, true);
865 void
866 EditorRoutes::hide_all_audiobus ()
868 set_all_audio_midi_visibility (2, false);
871 void
872 EditorRoutes::show_all_miditracks()
874 set_all_audio_midi_visibility (3, true);
876 void
877 EditorRoutes::hide_all_miditracks ()
879 set_all_audio_midi_visibility (3, false);
882 bool
883 EditorRoutes::key_press (GdkEventKey* ev)
885 TreeViewColumn *col;
886 boost::shared_ptr<RouteList> rl (new RouteList);
887 TreePath path;
889 switch (ev->keyval) {
890 case GDK_Tab:
891 case GDK_ISO_Left_Tab:
893 /* If we appear to be editing something, leave that cleanly and appropriately.
895 if (name_editable) {
896 name_editable->editing_done ();
897 name_editable = 0;
900 col = _display.get_column (5); // select&focus on name column
902 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
903 treeview_select_previous (_display, _model, col);
904 } else {
905 treeview_select_next (_display, _model, col);
908 return true;
909 break;
911 case 'm':
912 if (get_relevant_routes (rl)) {
913 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
915 return true;
916 break;
918 case 's':
919 if (get_relevant_routes (rl)) {
920 _session->set_solo (rl, !rl->front()->soloed(), Session::rt_cleanup);
922 return true;
923 break;
925 case 'r':
926 if (get_relevant_routes (rl)) {
927 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
929 break;
931 default:
932 break;
935 return false;
938 bool
939 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
941 TimeAxisView* tv;
942 RouteTimeAxisView* rtv;
943 RefPtr<TreeSelection> selection = _display.get_selection();
944 TreePath path;
945 TreeIter iter;
947 if (selection->count_selected_rows() != 0) {
949 /* use selection */
951 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
952 iter = selection->get_selected (tm);
954 } else {
955 /* use mouse pointer */
957 int x, y;
958 int bx, by;
960 _display.get_pointer (x, y);
961 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
963 if (_display.get_path_at_pos (bx, by, path)) {
964 iter = _model->get_iter (path);
968 if (iter) {
969 tv = (*iter)[_columns.tv];
970 if (tv) {
971 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
972 if (rtv) {
973 rl->push_back (rtv->route());
978 return !rl->empty();
981 bool
982 EditorRoutes::button_press (GdkEventButton* ev)
984 if (Keyboard::is_context_menu_event (ev)) {
985 show_menu ();
986 return true;
989 //Scroll editor canvas to selected track
990 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
992 TreeModel::Path path;
993 TreeViewColumn *tvc;
994 int cell_x;
995 int cell_y;
997 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
999 // Get the model row.
1000 Gtk::TreeModel::Row row = *_model->get_iter (path);
1002 TimeAxisView *tv = row[_columns.tv];
1004 int y_pos = tv->y_position();
1006 //Clamp the y pos so that we do not extend beyond the canvas full height.
1007 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1008 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1011 //Only scroll to if the track is visible
1012 if(y_pos != -1){
1013 _editor->reset_y_origin (y_pos);
1017 return false;
1020 bool
1021 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1023 if (selection_countdown) {
1024 if (--selection_countdown == 0) {
1025 return true;
1026 } else {
1027 /* no selection yet ... */
1028 return false;
1031 return true;
1034 struct EditorOrderRouteSorter {
1035 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1036 /* use of ">" forces the correct sort order */
1037 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1041 void
1042 EditorRoutes::initial_display ()
1044 suspend_redisplay ();
1045 _model->clear ();
1047 if (!_session) {
1048 resume_redisplay ();
1049 return;
1052 boost::shared_ptr<RouteList> routes = _session->get_routes();
1053 RouteList r (*routes);
1054 EditorOrderRouteSorter sorter;
1056 r.sort (sorter);
1057 _editor->handle_new_route (r);
1059 /* don't show master bus in a new session */
1061 if (ARDOUR_UI::instance()->session_is_new ()) {
1063 TreeModel::Children rows = _model->children();
1064 TreeModel::Children::iterator i;
1066 _no_redisplay = true;
1068 for (i = rows.begin(); i != rows.end(); ++i) {
1070 TimeAxisView *tv = (*i)[_columns.tv];
1071 RouteTimeAxisView *rtv;
1073 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1074 if (rtv->route()->is_master()) {
1075 _display.get_selection()->unselect (i);
1080 _no_redisplay = false;
1081 redisplay ();
1084 resume_redisplay ();
1087 void
1088 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1090 _redisplay_does_not_sync_order_keys = true;
1091 _session->set_remote_control_ids();
1092 redisplay ();
1093 _redisplay_does_not_sync_order_keys = false;
1096 void
1097 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1098 int x, int y,
1099 const SelectionData& data,
1100 guint info, guint time)
1102 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1103 _display.on_drag_data_received (context, x, y, data, info, time);
1104 return;
1107 context->drag_finish (true, false, time);
1110 void
1111 EditorRoutes::move_selected_tracks (bool up)
1113 if (_editor->selection->tracks.empty()) {
1114 return;
1117 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1118 std::list<ViewRoute> view_routes;
1119 std::vector<int> neworder;
1120 TreeModel::Children rows = _model->children();
1121 TreeModel::Children::iterator ri;
1123 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1124 TimeAxisView* tv = (*ri)[_columns.tv];
1125 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1127 view_routes.push_back (ViewRoute (tv, route));
1130 list<ViewRoute>::iterator trailing;
1131 list<ViewRoute>::iterator leading;
1133 if (up) {
1135 trailing = view_routes.begin();
1136 leading = view_routes.begin();
1138 ++leading;
1140 while (leading != view_routes.end()) {
1141 if (_editor->selection->selected (leading->first)) {
1142 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1143 leading = view_routes.erase (leading);
1144 } else {
1145 ++leading;
1146 ++trailing;
1150 } else {
1152 /* if we could use reverse_iterator in list::insert, this code
1153 would be a beautiful reflection of the code above. but we can't
1154 and so it looks like a bit of a mess.
1157 trailing = view_routes.end();
1158 leading = view_routes.end();
1160 --leading; if (leading == view_routes.begin()) { return; }
1161 --leading;
1162 --trailing;
1164 while (1) {
1166 if (_editor->selection->selected (leading->first)) {
1167 list<ViewRoute>::iterator tmp;
1169 /* need to insert *after* trailing, not *before* it,
1170 which is what insert (iter, val) normally does.
1173 tmp = trailing;
1174 tmp++;
1176 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1178 /* can't use iter = cont.erase (iter); form here, because
1179 we need iter to move backwards.
1182 tmp = leading;
1183 --tmp;
1185 bool done = false;
1187 if (leading == view_routes.begin()) {
1188 /* the one we've just inserted somewhere else
1189 was the first in the list. erase this copy,
1190 and then break, because we're done.
1192 done = true;
1195 view_routes.erase (leading);
1197 if (done) {
1198 break;
1201 leading = tmp;
1203 } else {
1204 if (leading == view_routes.begin()) {
1205 break;
1207 --leading;
1208 --trailing;
1213 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1214 neworder.push_back (leading->second->order_key (N_ ("editor")));
1217 _model->reorder (neworder);
1219 _session->sync_order_keys (N_ ("editor"));
1222 void
1223 EditorRoutes::update_rec_display ()
1225 TreeModel::Children rows = _model->children();
1226 TreeModel::Children::iterator i;
1228 for (i = rows.begin(); i != rows.end(); ++i) {
1229 boost::shared_ptr<Route> route = (*i)[_columns.route];
1231 if (boost::dynamic_pointer_cast<Track> (route)) {
1232 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1234 if (route->record_enabled()) {
1235 if (_session->record_status() == Session::Recording) {
1236 (*i)[_columns.rec_state] = 1;
1237 } else {
1238 (*i)[_columns.rec_state] = 2;
1240 } else if (mt && mt->step_editing()) {
1241 (*i)[_columns.rec_state] = 3;
1242 } else {
1243 (*i)[_columns.rec_state] = 0;
1246 (*i)[_columns.name_editable] = !route->record_enabled ();
1251 void
1252 EditorRoutes::update_mute_display ()
1254 TreeModel::Children rows = _model->children();
1255 TreeModel::Children::iterator i;
1257 for (i = rows.begin(); i != rows.end(); ++i) {
1258 boost::shared_ptr<Route> route = (*i)[_columns.route];
1259 (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
1263 void
1264 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1266 TreeModel::Children rows = _model->children();
1267 TreeModel::Children::iterator i;
1269 for (i = rows.begin(); i != rows.end(); ++i) {
1270 boost::shared_ptr<Route> route = (*i)[_columns.route];
1271 (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
1275 void
1276 EditorRoutes::update_solo_isolate_display ()
1278 TreeModel::Children rows = _model->children();
1279 TreeModel::Children::iterator i;
1281 for (i = rows.begin(); i != rows.end(); ++i) {
1282 boost::shared_ptr<Route> route = (*i)[_columns.route];
1283 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
1287 void
1288 EditorRoutes::update_solo_safe_display ()
1290 TreeModel::Children rows = _model->children();
1291 TreeModel::Children::iterator i;
1293 for (i = rows.begin(); i != rows.end(); ++i) {
1294 boost::shared_ptr<Route> route = (*i)[_columns.route];
1295 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
1299 list<TimeAxisView*>
1300 EditorRoutes::views () const
1302 list<TimeAxisView*> v;
1303 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1304 v.push_back ((*i)[_columns.tv]);
1307 return v;
1310 void
1311 EditorRoutes::clear ()
1313 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1314 _model->clear ();
1315 _display.set_model (_model);
1318 void
1319 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1321 name_editable = ce;
1323 /* give it a special name */
1325 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1327 if (e) {
1328 e->set_name (X_("RouteNameEditorEntry"));
1332 void
1333 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1335 name_editable = 0;
1337 TreeIter iter = _model->get_iter (path);
1339 if (!iter) {
1340 return;
1343 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1345 if (route && route->name() != new_text) {
1346 route->set_name (new_text);
1350 void
1351 EditorRoutes::solo_changed_so_update_mute ()
1353 update_mute_display ();
1356 void
1357 EditorRoutes::show_tracks_with_regions_at_playhead ()
1359 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1361 set<TimeAxisView*> show;
1362 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1363 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1364 if (tav) {
1365 show.insert (tav);
1369 suspend_redisplay ();
1371 TreeModel::Children rows = _model->children ();
1372 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1373 TimeAxisView* tv = (*i)[_columns.tv];
1374 (*i)[_columns.visible] = (show.find (tv) != show.end());
1377 resume_redisplay ();