Zoom session when the mouse pointer is moved up and down during a playhead drag.
[ardour2.git] / gtk2_ardour / editor_routes.cc
blob8368bff66af27243795b90750131b5837052c922
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 map<int, int> new_order;
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 bool changed = false;
713 int order;
715 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
716 boost::shared_ptr<Route> route = (*ri)[_columns.route];
718 int const old_key = order;
719 int const new_key = route->order_key (N_ ("editor"));
721 new_order[new_key] = old_key;
723 if (new_key != old_key) {
724 changed = true;
728 if (changed) {
729 _redisplay_does_not_reset_order_keys = true;
731 /* `compact' new_order into a vector */
732 vector<int> co;
733 for (map<int, int>::const_iterator i = new_order.begin(); i != new_order.end(); ++i) {
734 co.push_back (i->second);
737 _model->reorder (co);
738 _redisplay_does_not_reset_order_keys = false;
743 void
744 EditorRoutes::hide_all_tracks (bool /*with_select*/)
746 TreeModel::Children rows = _model->children();
747 TreeModel::Children::iterator i;
749 suspend_redisplay ();
751 for (i = rows.begin(); i != rows.end(); ++i) {
753 TreeModel::Row row = (*i);
754 TimeAxisView *tv = row[_columns.tv];
756 if (tv == 0) {
757 continue;
760 row[_columns.visible] = false;
763 resume_redisplay ();
765 /* XXX this seems like a hack and half, but its not clear where to put this
766 otherwise.
769 //reset_scrolling_region ();
772 void
773 EditorRoutes::set_all_tracks_visibility (bool yn)
775 TreeModel::Children rows = _model->children();
776 TreeModel::Children::iterator i;
778 suspend_redisplay ();
780 for (i = rows.begin(); i != rows.end(); ++i) {
782 TreeModel::Row row = (*i);
783 TimeAxisView* tv = row[_columns.tv];
785 if (tv == 0) {
786 continue;
789 (*i)[_columns.visible] = yn;
792 resume_redisplay ();
795 void
796 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
798 TreeModel::Children rows = _model->children();
799 TreeModel::Children::iterator i;
801 suspend_redisplay ();
803 for (i = rows.begin(); i != rows.end(); ++i) {
805 TreeModel::Row row = (*i);
806 TimeAxisView* tv = row[_columns.tv];
808 AudioTimeAxisView* atv;
809 MidiTimeAxisView* mtv;
811 if (tv == 0) {
812 continue;
815 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
816 switch (tracks) {
817 case 0:
818 (*i)[_columns.visible] = yn;
819 break;
821 case 1:
822 if (atv->is_audio_track()) {
823 (*i)[_columns.visible] = yn;
825 break;
827 case 2:
828 if (!atv->is_audio_track()) {
829 (*i)[_columns.visible] = yn;
831 break;
834 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
835 switch (tracks) {
836 case 0:
837 (*i)[_columns.visible] = yn;
838 break;
840 case 3:
841 if (mtv->is_midi_track()) {
842 (*i)[_columns.visible] = yn;
844 break;
849 resume_redisplay ();
852 void
853 EditorRoutes::hide_all_routes ()
855 set_all_tracks_visibility (false);
858 void
859 EditorRoutes::show_all_routes ()
861 set_all_tracks_visibility (true);
864 void
865 EditorRoutes::show_all_audiotracks()
867 set_all_audio_midi_visibility (1, true);
869 void
870 EditorRoutes::hide_all_audiotracks ()
872 set_all_audio_midi_visibility (1, false);
875 void
876 EditorRoutes::show_all_audiobus ()
878 set_all_audio_midi_visibility (2, true);
880 void
881 EditorRoutes::hide_all_audiobus ()
883 set_all_audio_midi_visibility (2, false);
886 void
887 EditorRoutes::show_all_miditracks()
889 set_all_audio_midi_visibility (3, true);
891 void
892 EditorRoutes::hide_all_miditracks ()
894 set_all_audio_midi_visibility (3, false);
897 bool
898 EditorRoutes::key_press (GdkEventKey* ev)
900 TreeViewColumn *col;
901 boost::shared_ptr<RouteList> rl (new RouteList);
902 TreePath path;
904 switch (ev->keyval) {
905 case GDK_Tab:
906 case GDK_ISO_Left_Tab:
908 /* If we appear to be editing something, leave that cleanly and appropriately.
910 if (name_editable) {
911 name_editable->editing_done ();
912 name_editable = 0;
915 col = _display.get_column (5); // select&focus on name column
917 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
918 treeview_select_previous (_display, _model, col);
919 } else {
920 treeview_select_next (_display, _model, col);
923 return true;
924 break;
926 case 'm':
927 if (get_relevant_routes (rl)) {
928 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
930 return true;
931 break;
933 case 's':
934 if (Config->get_solo_control_is_listen_control()) {
935 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
936 } else {
937 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
939 return true;
940 break;
942 case 'r':
943 if (get_relevant_routes (rl)) {
944 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
946 break;
948 default:
949 break;
952 return false;
955 bool
956 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
958 TimeAxisView* tv;
959 RouteTimeAxisView* rtv;
960 RefPtr<TreeSelection> selection = _display.get_selection();
961 TreePath path;
962 TreeIter iter;
964 if (selection->count_selected_rows() != 0) {
966 /* use selection */
968 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
969 iter = selection->get_selected (tm);
971 } else {
972 /* use mouse pointer */
974 int x, y;
975 int bx, by;
977 _display.get_pointer (x, y);
978 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
980 if (_display.get_path_at_pos (bx, by, path)) {
981 iter = _model->get_iter (path);
985 if (iter) {
986 tv = (*iter)[_columns.tv];
987 if (tv) {
988 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
989 if (rtv) {
990 rl->push_back (rtv->route());
995 return !rl->empty();
998 bool
999 EditorRoutes::button_press (GdkEventButton* ev)
1001 if (Keyboard::is_context_menu_event (ev)) {
1002 show_menu ();
1003 return true;
1006 //Scroll editor canvas to selected track
1007 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1009 TreeModel::Path path;
1010 TreeViewColumn *tvc;
1011 int cell_x;
1012 int cell_y;
1014 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
1016 // Get the model row.
1017 Gtk::TreeModel::Row row = *_model->get_iter (path);
1019 TimeAxisView *tv = row[_columns.tv];
1021 int y_pos = tv->y_position();
1023 //Clamp the y pos so that we do not extend beyond the canvas full height.
1024 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1025 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1028 //Only scroll to if the track is visible
1029 if(y_pos != -1){
1030 _editor->reset_y_origin (y_pos);
1034 return false;
1037 bool
1038 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1040 if (selection_countdown) {
1041 if (--selection_countdown == 0) {
1042 return true;
1043 } else {
1044 /* no selection yet ... */
1045 return false;
1048 return true;
1051 struct EditorOrderRouteSorter {
1052 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1053 /* use of ">" forces the correct sort order */
1054 return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1058 void
1059 EditorRoutes::initial_display ()
1061 suspend_redisplay ();
1062 _model->clear ();
1064 if (!_session) {
1065 resume_redisplay ();
1066 return;
1069 boost::shared_ptr<RouteList> routes = _session->get_routes();
1070 RouteList r (*routes);
1071 EditorOrderRouteSorter sorter;
1073 r.sort (sorter);
1074 _editor->handle_new_route (r);
1076 /* don't show master bus in a new session */
1078 if (ARDOUR_UI::instance()->session_is_new ()) {
1080 TreeModel::Children rows = _model->children();
1081 TreeModel::Children::iterator i;
1083 _no_redisplay = true;
1085 for (i = rows.begin(); i != rows.end(); ++i) {
1087 TimeAxisView *tv = (*i)[_columns.tv];
1088 RouteTimeAxisView *rtv;
1090 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1091 if (rtv->route()->is_master()) {
1092 _display.get_selection()->unselect (i);
1097 _no_redisplay = false;
1098 redisplay ();
1101 resume_redisplay ();
1104 void
1105 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1107 _redisplay_does_not_sync_order_keys = true;
1108 _session->set_remote_control_ids();
1109 redisplay ();
1110 _redisplay_does_not_sync_order_keys = false;
1113 void
1114 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1115 int x, int y,
1116 const SelectionData& data,
1117 guint info, guint time)
1119 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1120 _display.on_drag_data_received (context, x, y, data, info, time);
1121 return;
1124 context->drag_finish (true, false, time);
1127 void
1128 EditorRoutes::move_selected_tracks (bool up)
1130 if (_editor->selection->tracks.empty()) {
1131 return;
1134 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1135 std::list<ViewRoute> view_routes;
1136 std::vector<int> neworder;
1137 TreeModel::Children rows = _model->children();
1138 TreeModel::Children::iterator ri;
1140 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1141 TimeAxisView* tv = (*ri)[_columns.tv];
1142 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1144 view_routes.push_back (ViewRoute (tv, route));
1147 list<ViewRoute>::iterator trailing;
1148 list<ViewRoute>::iterator leading;
1150 if (up) {
1152 trailing = view_routes.begin();
1153 leading = view_routes.begin();
1155 ++leading;
1157 while (leading != view_routes.end()) {
1158 if (_editor->selection->selected (leading->first)) {
1159 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1160 leading = view_routes.erase (leading);
1161 } else {
1162 ++leading;
1163 ++trailing;
1167 } else {
1169 /* if we could use reverse_iterator in list::insert, this code
1170 would be a beautiful reflection of the code above. but we can't
1171 and so it looks like a bit of a mess.
1174 trailing = view_routes.end();
1175 leading = view_routes.end();
1177 --leading; if (leading == view_routes.begin()) { return; }
1178 --leading;
1179 --trailing;
1181 while (1) {
1183 if (_editor->selection->selected (leading->first)) {
1184 list<ViewRoute>::iterator tmp;
1186 /* need to insert *after* trailing, not *before* it,
1187 which is what insert (iter, val) normally does.
1190 tmp = trailing;
1191 tmp++;
1193 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1195 /* can't use iter = cont.erase (iter); form here, because
1196 we need iter to move backwards.
1199 tmp = leading;
1200 --tmp;
1202 bool done = false;
1204 if (leading == view_routes.begin()) {
1205 /* the one we've just inserted somewhere else
1206 was the first in the list. erase this copy,
1207 and then break, because we're done.
1209 done = true;
1212 view_routes.erase (leading);
1214 if (done) {
1215 break;
1218 leading = tmp;
1220 } else {
1221 if (leading == view_routes.begin()) {
1222 break;
1224 --leading;
1225 --trailing;
1230 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1231 neworder.push_back (leading->second->order_key (N_ ("editor")));
1234 _model->reorder (neworder);
1236 _session->sync_order_keys (N_ ("editor"));
1239 void
1240 EditorRoutes::update_rec_display ()
1242 TreeModel::Children rows = _model->children();
1243 TreeModel::Children::iterator i;
1245 for (i = rows.begin(); i != rows.end(); ++i) {
1246 boost::shared_ptr<Route> route = (*i)[_columns.route];
1248 if (boost::dynamic_pointer_cast<Track> (route)) {
1249 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1251 if (route->record_enabled()) {
1252 if (_session->record_status() == Session::Recording) {
1253 (*i)[_columns.rec_state] = 1;
1254 } else {
1255 (*i)[_columns.rec_state] = 2;
1257 } else if (mt && mt->step_editing()) {
1258 (*i)[_columns.rec_state] = 3;
1259 } else {
1260 (*i)[_columns.rec_state] = 0;
1263 (*i)[_columns.name_editable] = !route->record_enabled ();
1268 void
1269 EditorRoutes::update_mute_display ()
1271 TreeModel::Children rows = _model->children();
1272 TreeModel::Children::iterator i;
1274 for (i = rows.begin(); i != rows.end(); ++i) {
1275 boost::shared_ptr<Route> route = (*i)[_columns.route];
1276 (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
1280 void
1281 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1283 TreeModel::Children rows = _model->children();
1284 TreeModel::Children::iterator i;
1286 for (i = rows.begin(); i != rows.end(); ++i) {
1287 boost::shared_ptr<Route> route = (*i)[_columns.route];
1288 (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
1292 void
1293 EditorRoutes::update_solo_isolate_display ()
1295 TreeModel::Children rows = _model->children();
1296 TreeModel::Children::iterator i;
1298 for (i = rows.begin(); i != rows.end(); ++i) {
1299 boost::shared_ptr<Route> route = (*i)[_columns.route];
1300 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
1304 void
1305 EditorRoutes::update_solo_safe_display ()
1307 TreeModel::Children rows = _model->children();
1308 TreeModel::Children::iterator i;
1310 for (i = rows.begin(); i != rows.end(); ++i) {
1311 boost::shared_ptr<Route> route = (*i)[_columns.route];
1312 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
1316 list<TimeAxisView*>
1317 EditorRoutes::views () const
1319 list<TimeAxisView*> v;
1320 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1321 v.push_back ((*i)[_columns.tv]);
1324 return v;
1327 void
1328 EditorRoutes::clear ()
1330 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1331 _model->clear ();
1332 _display.set_model (_model);
1335 void
1336 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1338 name_editable = ce;
1340 /* give it a special name */
1342 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1344 if (e) {
1345 e->set_name (X_("RouteNameEditorEntry"));
1349 void
1350 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1352 name_editable = 0;
1354 TreeIter iter = _model->get_iter (path);
1356 if (!iter) {
1357 return;
1360 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1362 if (route && route->name() != new_text) {
1363 route->set_name (new_text);
1367 void
1368 EditorRoutes::solo_changed_so_update_mute ()
1370 update_mute_display ();
1373 void
1374 EditorRoutes::show_tracks_with_regions_at_playhead ()
1376 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1378 set<TimeAxisView*> show;
1379 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1380 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1381 if (tav) {
1382 show.insert (tav);
1386 suspend_redisplay ();
1388 TreeModel::Children rows = _model->children ();
1389 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1390 TimeAxisView* tv = (*i)[_columns.tv];
1391 (*i)[_columns.visible] = (show.find (tv) != show.end());
1394 resume_redisplay ();