2 Copyright (C) 2000-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "pbd/unknown_type.h"
28 #include "pbd/unwind.h"
30 #include "ardour/debug.h"
31 #include "ardour/route.h"
32 #include "ardour/midi_track.h"
33 #include "ardour/session.h"
35 #include "gtkmm2ext/cell_renderer_pixbuf_multi.h"
36 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
37 #include "gtkmm2ext/treeutils.h"
41 #include "ardour_ui.h"
42 #include "audio_time_axis.h"
43 #include "midi_time_axis.h"
44 #include "mixer_strip.h"
45 #include "gui_thread.h"
48 #include "route_sorter.h"
49 #include "editor_group_tabs.h"
50 #include "editor_routes.h"
55 using namespace ARDOUR;
58 using namespace Gtkmm2ext;
60 using Gtkmm2ext::Keyboard;
68 EditorRoutes::EditorRoutes (Editor* e)
70 , _ignore_reorder (false)
71 , _no_redisplay (false)
72 , _adding_routes (false)
75 , selection_countdown (0)
78 static const int column_width = 22;
80 _scroller.add (_display);
81 _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
83 _model = ListStore::create (_columns);
84 _display.set_model (_model);
86 // Record enable toggle
87 CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
89 rec_col_renderer->set_pixbuf (0, ::get_icon("record-normal-disabled"));
90 rec_col_renderer->set_pixbuf (1, ::get_icon("record-normal-in-progress"));
91 rec_col_renderer->set_pixbuf (2, ::get_icon("record-normal-enabled"));
92 rec_col_renderer->set_pixbuf (3, ::get_icon("record-step"));
93 rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
95 TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
97 rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
98 rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
100 rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
101 rec_state_column->set_alignment(ALIGN_CENTER);
102 rec_state_column->set_expand(false);
103 rec_state_column->set_fixed_width(column_width);
107 CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti());
108 input_active_col_renderer->set_pixbuf (0, ::get_icon("midi-input-inactive"));
109 input_active_col_renderer->set_pixbuf (1, ::get_icon("midi-input-active"));
110 input_active_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed));
112 TreeViewColumn* input_active_column = manage (new TreeViewColumn ("I", *input_active_col_renderer));
114 input_active_column->add_attribute(input_active_col_renderer->property_state(), _columns.is_input_active);
115 input_active_column->add_attribute (input_active_col_renderer->property_visible(), _columns.is_midi);
117 input_active_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
118 input_active_column->set_alignment(ALIGN_CENTER);
119 input_active_column->set_expand(false);
120 input_active_column->set_fixed_width(column_width);
122 // Mute enable toggle
123 CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
125 mute_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("mute-disabled"));
126 mute_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("muted-by-others"));
127 mute_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("mute-enabled"));
128 mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
130 TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
132 mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
133 mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
134 mute_state_column->set_alignment(ALIGN_CENTER);
135 mute_state_column->set_expand(false);
136 mute_state_column->set_fixed_width(15);
138 // Solo enable toggle
139 CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
141 solo_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("solo-disabled"));
142 solo_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("solo-enabled"));
143 solo_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("soloed-by-others"));
144 solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
146 TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
148 solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
149 solo_state_column->add_attribute(solo_col_renderer->property_visible(), _columns.solo_visible);
150 solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
151 solo_state_column->set_alignment(ALIGN_CENTER);
152 solo_state_column->set_expand(false);
153 solo_state_column->set_fixed_width(column_width);
155 // Solo isolate toggle
156 CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
158 solo_iso_renderer->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
159 solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
160 solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
162 TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
164 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
165 solo_isolate_state_column->add_attribute(solo_iso_renderer->property_visible(), _columns.solo_visible);
166 solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
167 solo_isolate_state_column->set_alignment(ALIGN_CENTER);
168 solo_isolate_state_column->set_expand(false);
169 solo_isolate_state_column->set_fixed_width(column_width);
172 CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
174 solo_safe_renderer->set_pixbuf (0, ::get_icon("solo-safe-disabled"));
175 solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-safe-enabled"));
176 solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
178 TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
179 solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
180 solo_safe_state_column->add_attribute(solo_safe_renderer->property_visible(), _columns.solo_visible);
181 solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
182 solo_safe_state_column->set_alignment(ALIGN_CENTER);
183 solo_safe_state_column->set_expand(false);
184 solo_safe_state_column->set_fixed_width(column_width);
186 _name_column = _display.append_column ("", _columns.text) - 1;
187 _visible_column = _display.append_column ("", _columns.visible) - 1;
188 _active_column = _display.append_column ("", _columns.active) - 1;
190 _display.append_column (*input_active_column);
191 _display.append_column (*rec_state_column);
192 _display.append_column (*mute_state_column);
193 _display.append_column (*solo_state_column);
194 _display.append_column (*solo_isolate_state_column);
195 _display.append_column (*solo_safe_state_column);
202 { 0, _("Name"), _("Track/Bus Name") },
203 { 1, _("V"), _("Track/Bus visible ?") },
204 { 2, _("A"), _("Track/Bus active ?") },
205 { 3, _("I"), _("MIDI input enabled") },
206 { 4, _("R"), _("Record enabled") },
207 { 5, _("M"), _("Muted") },
208 { 6, _("S"), _("Soloed") },
209 { 7, _("SI"), _("Solo Isolated") },
210 { 8, _("SS"), _("Solo Safe (Locked)") },
214 for (int i = 0; ci[i].index >= 0; ++i) {
215 col = _display.get_column (ci[i].index);
216 l = manage (new Label (ci[i].label));
217 ARDOUR_UI::instance()->set_tip (*l, ci[i].tooltip);
218 col->set_widget (*l);
222 _display.set_headers_visible (true);
223 _display.get_selection()->set_mode (SELECTION_SINGLE);
224 _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
225 _display.set_reorderable (true);
226 _display.set_name (X_("EditGroupList"));
227 _display.set_rules_hint (true);
228 _display.set_size_request (100, -1);
229 _display.add_object_drag (_columns.route.index(), "routes");
231 CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
234 name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
236 TreeViewColumn* name_column = _display.get_column (_name_column);
238 assert (name_column);
240 name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
241 name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
242 name_column->set_expand(true);
243 name_column->set_min_width(50);
245 name_cell->property_editable() = true;
246 name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
248 // Set the visible column cell renderer to radio toggle
249 CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
251 visible_cell->property_activatable() = true;
252 visible_cell->property_radio() = false;
253 visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
255 TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
256 visible_col->set_expand(false);
257 visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
258 visible_col->set_fixed_width(30);
259 visible_col->set_alignment(ALIGN_CENTER);
261 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
263 active_cell->property_activatable() = true;
264 active_cell->property_radio() = false;
265 active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
267 TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
268 active_col->set_expand (false);
269 active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
270 active_col->set_fixed_width (30);
271 active_col->set_alignment (ALIGN_CENTER);
273 _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
274 _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
276 _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
277 _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
279 _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
280 _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
282 _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
283 _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
285 _display.set_enable_search (false);
287 Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::sync_treeview_from_order_keys, this, _1), gui_context());
291 EditorRoutes::focus_in (GdkEventFocus*)
293 Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
296 old_focus = win->get_focus ();
303 /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
308 EditorRoutes::focus_out (GdkEventFocus*)
311 old_focus->grab_focus ();
319 EditorRoutes::enter_notify (GdkEventCrossing*)
325 /* arm counter so that ::selection_filter() will deny selecting anything for the
326 next two attempts to change selection status.
328 selection_countdown = 2;
329 _scroller.grab_focus ();
330 Keyboard::magic_widget_grab_focus ();
335 EditorRoutes::leave_notify (GdkEventCrossing*)
337 selection_countdown = 0;
340 old_focus->grab_focus ();
344 Keyboard::magic_widget_drop_focus ();
349 EditorRoutes::set_session (Session* s)
351 SessionHandlePtr::set_session (s);
356 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
357 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
362 EditorRoutes::on_input_active_changed (std::string const & path_string)
364 // Get the model row that has been toggled.
365 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
367 TimeAxisView* tv = row[_columns.tv];
368 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
371 boost::shared_ptr<MidiTrack> mt;
372 mt = rtv->midi_track();
374 mt->set_input_active (!mt->input_active());
380 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
382 // Get the model row that has been toggled.
383 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
385 TimeAxisView* tv = row[_columns.tv];
386 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
388 if (rtv && rtv->track()) {
389 boost::shared_ptr<RouteList> rl (new RouteList);
390 rl->push_back (rtv->route());
391 _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
396 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
398 // Get the model row that has been toggled.
399 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
401 TimeAxisView *tv = row[_columns.tv];
402 RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
405 boost::shared_ptr<RouteList> rl (new RouteList);
406 rl->push_back (rtv->route());
407 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
412 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
414 // Get the model row that has been toggled.
415 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
417 TimeAxisView *tv = row[_columns.tv];
418 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
421 boost::shared_ptr<RouteList> rl (new RouteList);
422 rl->push_back (rtv->route());
423 if (Config->get_solo_control_is_listen_control()) {
424 _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
426 _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
432 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
434 // Get the model row that has been toggled.
435 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
437 TimeAxisView *tv = row[_columns.tv];
438 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
441 rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
446 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
448 // Get the model row that has been toggled.
449 Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
451 TimeAxisView *tv = row[_columns.tv];
452 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
455 rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
460 EditorRoutes::build_menu ()
462 using namespace Menu_Helpers;
467 MenuList& items = _menu->items();
468 _menu->set_name ("ArdourContextMenu");
470 items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
471 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
472 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
473 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
474 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
475 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
476 items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
477 items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
478 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
482 EditorRoutes::show_menu ()
488 _menu->popup (1, gtk_get_current_event_time());
492 EditorRoutes::redisplay ()
494 if (_no_redisplay || !_session || _session->deletion_in_progress()) {
498 TreeModel::Children rows = _model->children();
499 TreeModel::Children::iterator i;
502 /* n will be the count of tracks plus children (updated by TimeAxisView::show_at),
503 so we will use that to know where to put things.
507 for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
508 TimeAxisView *tv = (*i)[_columns.tv];
509 boost::shared_ptr<Route> route = (*i)[_columns.route];
512 // just a "title" row
516 bool visible = tv->marked_for_display ();
518 /* show or hide the TimeAxisView */
520 position += tv->show_at (position, n, &_editor->edit_controls_vbox);
521 tv->clip_to_viewport ();
529 /* whenever we go idle, update the track view list to reflect the new order.
530 we can't do this here, because we could mess up something that is traversing
531 the track order and has caused a redisplay of the list.
533 Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
535 _editor->reset_controls_layout_height (position);
536 _editor->reset_controls_layout_width ();
537 _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
538 _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
540 if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
542 We're increasing the size of the canvas while the bottom is visible.
543 We scroll down to keep in step with the controls layout.
545 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
550 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
552 /* this happens as the second step of a DnD within the treeview as well
553 as when a row/route is actually deleted.
555 DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview row deleted\n");
556 sync_order_keys_from_treeview ();
560 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
562 DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview reordered\n");
563 sync_order_keys_from_treeview ();
567 EditorRoutes::visible_changed (std::string const & path)
569 if (_session && _session->deletion_in_progress()) {
575 if ((iter = _model->get_iter (path))) {
576 TimeAxisView* tv = (*iter)[_columns.tv];
578 bool visible = (*iter)[_columns.visible];
580 if (tv->set_marked_for_display (!visible)) {
581 update_visibility ();
588 EditorRoutes::active_changed (std::string const & path)
590 if (_session && _session->deletion_in_progress ()) {
594 Gtk::TreeModel::Row row = *_model->get_iter (path);
595 boost::shared_ptr<Route> route = row[_columns.route];
596 bool const active = row[_columns.active];
597 route->set_active (!active, this);
601 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
604 PBD::Unwinder<bool> at (_adding_routes, true);
606 suspend_redisplay ();
608 _display.set_model (Glib::RefPtr<ListStore>());
610 for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
612 boost::shared_ptr<MidiTrack> midi_trk = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
614 row = *(_model->append ());
616 row[_columns.text] = (*x)->route()->name();
617 row[_columns.visible] = (*x)->marked_for_display();
618 row[_columns.active] = (*x)->route()->active ();
619 row[_columns.tv] = *x;
620 row[_columns.route] = (*x)->route ();
621 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
624 row[_columns.is_input_active] = midi_trk->input_active ();
625 row[_columns.is_midi] = true;
627 row[_columns.is_input_active] = false;
628 row[_columns.is_midi] = false;
631 row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
632 row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
633 row[_columns.solo_visible] = !(*x)->route()->is_master ();
634 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
635 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
636 row[_columns.name_editable] = true;
638 boost::weak_ptr<Route> wr ((*x)->route());
640 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
641 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
643 if ((*x)->is_track()) {
644 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
645 t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
648 if ((*x)->is_midi_track()) {
649 boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
650 t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
651 t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context());
654 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
655 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
656 (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
657 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
658 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
659 (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
663 update_rec_display ();
664 update_mute_display ();
665 update_solo_display (true);
666 update_solo_isolate_display ();
667 update_solo_safe_display ();
668 update_input_active_display ();
669 update_active_display ();
672 _display.set_model (_model);
674 /* now update route order keys from the treeview/track display order */
676 sync_order_keys_from_treeview ();
680 EditorRoutes::handle_gui_changes (string const & what, void*)
682 if (_adding_routes) {
686 if (what == "track_height") {
687 /* Optional :make tracks change height while it happens, instead
690 //update_canvas_now ();
694 if (what == "visible_tracks") {
700 EditorRoutes::route_removed (TimeAxisView *tv)
702 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
704 TreeModel::Children rows = _model->children();
705 TreeModel::Children::iterator ri;
707 for (ri = rows.begin(); ri != rows.end(); ++ri) {
708 if ((*ri)[_columns.tv] == tv) {
714 /* the deleted signal for the treeview/model will take
720 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
722 if (!what_changed.contains (ARDOUR::Properties::name)) {
726 ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
728 boost::shared_ptr<Route> route = r.lock ();
734 TreeModel::Children rows = _model->children();
735 TreeModel::Children::iterator i;
737 for (i = rows.begin(); i != rows.end(); ++i) {
738 boost::shared_ptr<Route> t = (*i)[_columns.route];
740 (*i)[_columns.text] = route->name();
747 EditorRoutes::update_active_display ()
749 TreeModel::Children rows = _model->children();
750 TreeModel::Children::iterator i;
752 for (i = rows.begin(); i != rows.end(); ++i) {
753 boost::shared_ptr<Route> route = (*i)[_columns.route];
754 (*i)[_columns.active] = route->active ();
759 EditorRoutes::update_visibility ()
761 TreeModel::Children rows = _model->children();
762 TreeModel::Children::iterator i;
764 suspend_redisplay ();
766 for (i = rows.begin(); i != rows.end(); ++i) {
767 TimeAxisView *tv = (*i)[_columns.tv];
768 (*i)[_columns.visible] = tv->marked_for_display ();
771 /* force route order keys catch up with visibility changes
774 sync_order_keys_from_treeview ();
780 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
782 TreeModel::Children rows = _model->children();
783 TreeModel::Children::iterator i;
785 for (i = rows.begin(); i != rows.end(); ++i) {
786 if ((*i)[_columns.tv] == &tv) {
787 tv.set_marked_for_display (false);
788 (*i)[_columns.visible] = false;
796 EditorRoutes::show_track_in_display (TimeAxisView& tv)
798 TreeModel::Children rows = _model->children();
799 TreeModel::Children::iterator i;
802 for (i = rows.begin(); i != rows.end(); ++i) {
803 if ((*i)[_columns.tv] == &tv) {
804 tv.set_marked_for_display (true);
805 (*i)[_columns.visible] = true;
813 EditorRoutes::reset_remote_control_ids ()
815 if (Config->get_remote_model() != EditorOrdered || !_session || _session->deletion_in_progress()) {
819 TreeModel::Children rows = _model->children();
826 DEBUG_TRACE (DEBUG::OrderKeys, "editor reset remote control ids\n");
828 TreeModel::Children::iterator ri;
829 bool rid_change = false;
831 uint32_t invisible_key = UINT32_MAX;
833 for (ri = rows.begin(); ri != rows.end(); ++ri) {
835 boost::shared_ptr<Route> route = (*ri)[_columns.route];
836 bool visible = (*ri)[_columns.visible];
839 if (!route->is_master() && !route->is_monitor()) {
841 uint32_t new_rid = (visible ? rid : invisible_key--);
843 if (new_rid != route->remote_control_id()) {
844 route->set_remote_control_id_from_order_key (EditorSort, new_rid);
856 /* tell the world that we changed the remote control IDs */
857 _session->notify_remote_id_change ();
863 EditorRoutes::sync_order_keys_from_treeview ()
865 if (_ignore_reorder || !_session || _session->deletion_in_progress()) {
869 TreeModel::Children rows = _model->children();
876 DEBUG_TRACE (DEBUG::OrderKeys, "editor sync order keys from treeview\n");
878 TreeModel::Children::iterator ri;
879 bool changed = false;
880 bool rid_change = false;
883 uint32_t invisible_key = UINT32_MAX;
885 for (ri = rows.begin(); ri != rows.end(); ++ri) {
887 boost::shared_ptr<Route> route = (*ri)[_columns.route];
888 bool visible = (*ri)[_columns.visible];
890 uint32_t old_key = route->order_key (EditorSort);
892 if (order != old_key) {
893 route->set_order_key (EditorSort, order);
898 if ((Config->get_remote_model() == EditorOrdered) && !route->is_master() && !route->is_monitor()) {
900 uint32_t new_rid = (visible ? rid : invisible_key--);
902 if (new_rid != route->remote_control_id()) {
903 route->set_remote_control_id_from_order_key (EditorSort, new_rid);
917 /* tell the world that we changed the editor sort keys */
918 _session->sync_order_keys (EditorSort);
922 /* tell the world that we changed the remote control IDs */
923 _session->notify_remote_id_change ();
928 EditorRoutes::sync_treeview_from_order_keys (RouteSortOrderKey src)
930 /* Some route order key(s) for `src' has been changed, make sure that
931 we update out tree/list model and GUI to reflect the change.
934 if (!_session || _session->deletion_in_progress()) {
938 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("editor sync model from order keys, src = %1\n", enum_2_string (src)));
940 if (src == MixerSort) {
942 if (!Config->get_sync_all_route_ordering()) {
943 /* mixer sort keys changed - we don't care */
947 DEBUG_TRACE (DEBUG::OrderKeys, "reset editor order key to match mixer\n");
949 /* mixer sort keys were changed, update the editor sort
950 * keys since "sync mixer+editor order" is enabled.
953 boost::shared_ptr<RouteList> r = _session->get_routes ();
955 for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
956 (*i)->sync_order_keys (src);
960 /* we could get here after either a change in the Mixer or Editor sort
961 * order, but either way, the mixer order keys reflect the intended
962 * order for the GUI, so reorder the treeview model to match it.
965 vector<int> neworder;
966 TreeModel::Children rows = _model->children();
967 uint32_t old_order = 0;
968 bool changed = false;
974 OrderKeySortedRoutes sorted_routes;
976 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
977 boost::shared_ptr<Route> route = (*ri)[_columns.route];
978 sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key (EditorSort)));
981 SortByNewDisplayOrder cmp;
983 sort (sorted_routes.begin(), sorted_routes.end(), cmp);
984 neworder.assign (sorted_routes.size(), 0);
988 for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
990 neworder[n] = sr->old_display_order;
992 if (sr->old_display_order != n) {
996 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("EDITOR change order for %1 from %2 to %3\n",
997 sr->route->name(), sr->old_display_order, n));
1001 Unwinder<bool> uw (_ignore_reorder, true);
1002 _model->reorder (neworder);
1009 EditorRoutes::hide_all_tracks (bool /*with_select*/)
1011 TreeModel::Children rows = _model->children();
1012 TreeModel::Children::iterator i;
1014 suspend_redisplay ();
1016 for (i = rows.begin(); i != rows.end(); ++i) {
1018 TreeModel::Row row = (*i);
1019 TimeAxisView *tv = row[_columns.tv];
1025 row[_columns.visible] = false;
1028 resume_redisplay ();
1030 /* XXX this seems like a hack and half, but its not clear where to put this
1034 //reset_scrolling_region ();
1038 EditorRoutes::set_all_tracks_visibility (bool yn)
1040 TreeModel::Children rows = _model->children();
1041 TreeModel::Children::iterator i;
1043 suspend_redisplay ();
1045 for (i = rows.begin(); i != rows.end(); ++i) {
1047 TreeModel::Row row = (*i);
1048 TimeAxisView* tv = row[_columns.tv];
1054 (*i)[_columns.visible] = yn;
1057 /* force route order keys catch up with visibility changes
1060 sync_order_keys_from_treeview ();
1062 resume_redisplay ();
1066 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
1068 TreeModel::Children rows = _model->children();
1069 TreeModel::Children::iterator i;
1071 suspend_redisplay ();
1073 for (i = rows.begin(); i != rows.end(); ++i) {
1075 TreeModel::Row row = (*i);
1076 TimeAxisView* tv = row[_columns.tv];
1078 AudioTimeAxisView* atv;
1079 MidiTimeAxisView* mtv;
1085 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1088 (*i)[_columns.visible] = yn;
1092 if (atv->is_audio_track()) {
1093 (*i)[_columns.visible] = yn;
1098 if (!atv->is_audio_track()) {
1099 (*i)[_columns.visible] = yn;
1104 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
1107 (*i)[_columns.visible] = yn;
1111 if (mtv->is_midi_track()) {
1112 (*i)[_columns.visible] = yn;
1119 /* force route order keys catch up with visibility changes
1122 sync_order_keys_from_treeview ();
1124 resume_redisplay ();
1128 EditorRoutes::hide_all_routes ()
1130 set_all_tracks_visibility (false);
1134 EditorRoutes::show_all_routes ()
1136 set_all_tracks_visibility (true);
1140 EditorRoutes::show_all_audiotracks()
1142 set_all_audio_midi_visibility (1, true);
1145 EditorRoutes::hide_all_audiotracks ()
1147 set_all_audio_midi_visibility (1, false);
1151 EditorRoutes::show_all_audiobus ()
1153 set_all_audio_midi_visibility (2, true);
1156 EditorRoutes::hide_all_audiobus ()
1158 set_all_audio_midi_visibility (2, false);
1162 EditorRoutes::show_all_miditracks()
1164 set_all_audio_midi_visibility (3, true);
1167 EditorRoutes::hide_all_miditracks ()
1169 set_all_audio_midi_visibility (3, false);
1173 EditorRoutes::key_press (GdkEventKey* ev)
1175 TreeViewColumn *col;
1176 boost::shared_ptr<RouteList> rl (new RouteList);
1179 switch (ev->keyval) {
1181 case GDK_ISO_Left_Tab:
1183 /* If we appear to be editing something, leave that cleanly and appropriately.
1185 if (name_editable) {
1186 name_editable->editing_done ();
1190 col = _display.get_column (_name_column); // select&focus on name column
1192 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1193 treeview_select_previous (_display, _model, col);
1195 treeview_select_next (_display, _model, col);
1202 if (get_relevant_routes (rl)) {
1203 _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1209 if (Config->get_solo_control_is_listen_control()) {
1210 _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1212 _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1218 if (get_relevant_routes (rl)) {
1219 _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1231 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
1234 RouteTimeAxisView* rtv;
1235 RefPtr<TreeSelection> selection = _display.get_selection();
1239 if (selection->count_selected_rows() != 0) {
1243 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1244 iter = selection->get_selected (tm);
1247 /* use mouse pointer */
1252 _display.get_pointer (x, y);
1253 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1255 if (_display.get_path_at_pos (bx, by, path)) {
1256 iter = _model->get_iter (path);
1261 tv = (*iter)[_columns.tv];
1263 rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1265 rl->push_back (rtv->route());
1270 return !rl->empty();
1274 EditorRoutes::button_press (GdkEventButton* ev)
1276 if (Keyboard::is_context_menu_event (ev)) {
1281 TreeModel::Path path;
1282 TreeViewColumn *tvc;
1286 if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1287 /* cancel selection */
1288 _display.get_selection()->unselect_all ();
1289 /* end any editing by grabbing focus */
1290 _display.grab_focus ();
1294 //Scroll editor canvas to selected track
1295 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1297 // Get the model row.
1298 Gtk::TreeModel::Row row = *_model->get_iter (path);
1300 TimeAxisView *tv = row[_columns.tv];
1302 int y_pos = tv->y_position();
1304 //Clamp the y pos so that we do not extend beyond the canvas full height.
1305 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1306 y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1309 //Only scroll to if the track is visible
1311 _editor->reset_y_origin (y_pos);
1319 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1321 if (selection_countdown) {
1322 if (--selection_countdown == 0) {
1325 /* no selection yet ... */
1332 struct EditorOrderRouteSorter {
1333 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1334 if (a->is_master()) {
1335 /* master before everything else */
1337 } else if (b->is_master()) {
1338 /* everything else before master */
1341 return a->order_key (EditorSort) < b->order_key (EditorSort);
1346 EditorRoutes::initial_display ()
1348 suspend_redisplay ();
1352 resume_redisplay ();
1356 boost::shared_ptr<RouteList> routes = _session->get_routes();
1358 if (ARDOUR_UI::instance()->session_is_new ()) {
1360 /* new session: stamp all routes with the right editor order
1364 _editor->add_routes (*(routes.get()));
1368 /* existing session: sort a copy of the route list by
1369 * editor-order and add its contents to the display.
1372 RouteList r (*routes);
1373 EditorOrderRouteSorter sorter;
1376 _editor->add_routes (r);
1380 resume_redisplay ();
1384 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1386 const SelectionData& data,
1387 guint info, guint time)
1389 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1390 _display.on_drag_data_received (context, x, y, data, info, time);
1394 context->drag_finish (true, false, time);
1398 EditorRoutes::move_selected_tracks (bool up)
1400 if (_editor->selection->tracks.empty()) {
1404 typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1405 std::list<ViewRoute> view_routes;
1406 std::vector<int> neworder;
1407 TreeModel::Children rows = _model->children();
1408 TreeModel::Children::iterator ri;
1410 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1411 TimeAxisView* tv = (*ri)[_columns.tv];
1412 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1414 view_routes.push_back (ViewRoute (tv, route));
1417 list<ViewRoute>::iterator trailing;
1418 list<ViewRoute>::iterator leading;
1422 trailing = view_routes.begin();
1423 leading = view_routes.begin();
1427 while (leading != view_routes.end()) {
1428 if (_editor->selection->selected (leading->first)) {
1429 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1430 leading = view_routes.erase (leading);
1439 /* if we could use reverse_iterator in list::insert, this code
1440 would be a beautiful reflection of the code above. but we can't
1441 and so it looks like a bit of a mess.
1444 trailing = view_routes.end();
1445 leading = view_routes.end();
1447 --leading; if (leading == view_routes.begin()) { return; }
1453 if (_editor->selection->selected (leading->first)) {
1454 list<ViewRoute>::iterator tmp;
1456 /* need to insert *after* trailing, not *before* it,
1457 which is what insert (iter, val) normally does.
1463 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1465 /* can't use iter = cont.erase (iter); form here, because
1466 we need iter to move backwards.
1474 if (leading == view_routes.begin()) {
1475 /* the one we've just inserted somewhere else
1476 was the first in the list. erase this copy,
1477 and then break, because we're done.
1482 view_routes.erase (leading);
1491 if (leading == view_routes.begin()) {
1500 for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1501 uint32_t order = leading->second->order_key (EditorSort);
1502 neworder.push_back (order);
1506 DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
1507 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1508 DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("\t%1\n", *i));
1510 DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
1512 for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1513 if (*i >= (int) neworder.size()) {
1514 cerr << "Trying to move something to " << *i << " of " << neworder.size() << endl;
1516 assert (*i < (int) neworder.size ());
1520 _model->reorder (neworder);
1524 EditorRoutes::update_input_active_display ()
1526 TreeModel::Children rows = _model->children();
1527 TreeModel::Children::iterator i;
1529 for (i = rows.begin(); i != rows.end(); ++i) {
1530 boost::shared_ptr<Route> route = (*i)[_columns.route];
1532 if (boost::dynamic_pointer_cast<Track> (route)) {
1533 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1536 (*i)[_columns.is_input_active] = mt->input_active();
1543 EditorRoutes::update_rec_display ()
1545 TreeModel::Children rows = _model->children();
1546 TreeModel::Children::iterator i;
1548 for (i = rows.begin(); i != rows.end(); ++i) {
1549 boost::shared_ptr<Route> route = (*i)[_columns.route];
1551 if (boost::dynamic_pointer_cast<Track> (route)) {
1552 boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1554 if (route->record_enabled()) {
1555 if (_session->record_status() == Session::Recording) {
1556 (*i)[_columns.rec_state] = 1;
1558 (*i)[_columns.rec_state] = 2;
1560 } else if (mt && mt->step_editing()) {
1561 (*i)[_columns.rec_state] = 3;
1563 (*i)[_columns.rec_state] = 0;
1566 (*i)[_columns.name_editable] = !route->record_enabled ();
1572 EditorRoutes::update_mute_display ()
1574 TreeModel::Children rows = _model->children();
1575 TreeModel::Children::iterator i;
1577 for (i = rows.begin(); i != rows.end(); ++i) {
1578 boost::shared_ptr<Route> route = (*i)[_columns.route];
1579 (*i)[_columns.mute_state] = RouteUI::mute_active_state (_session, route);
1584 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1586 TreeModel::Children rows = _model->children();
1587 TreeModel::Children::iterator i;
1589 for (i = rows.begin(); i != rows.end(); ++i) {
1590 boost::shared_ptr<Route> route = (*i)[_columns.route];
1591 (*i)[_columns.solo_state] = RouteUI::solo_active_state (route);
1596 EditorRoutes::update_solo_isolate_display ()
1598 TreeModel::Children rows = _model->children();
1599 TreeModel::Children::iterator i;
1601 for (i = rows.begin(); i != rows.end(); ++i) {
1602 boost::shared_ptr<Route> route = (*i)[_columns.route];
1603 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0;
1608 EditorRoutes::update_solo_safe_display ()
1610 TreeModel::Children rows = _model->children();
1611 TreeModel::Children::iterator i;
1613 for (i = rows.begin(); i != rows.end(); ++i) {
1614 boost::shared_ptr<Route> route = (*i)[_columns.route];
1615 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0;
1620 EditorRoutes::views () const
1622 list<TimeAxisView*> v;
1623 for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1624 v.push_back ((*i)[_columns.tv]);
1631 EditorRoutes::clear ()
1633 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1635 _display.set_model (_model);
1639 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1643 /* give it a special name */
1645 Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1648 e->set_name (X_("RouteNameEditorEntry"));
1653 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1657 TreeIter iter = _model->get_iter (path);
1663 boost::shared_ptr<Route> route = (*iter)[_columns.route];
1665 if (route && route->name() != new_text) {
1666 route->set_name (new_text);
1671 EditorRoutes::solo_changed_so_update_mute ()
1673 update_mute_display ();
1677 EditorRoutes::show_tracks_with_regions_at_playhead ()
1679 boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1681 set<TimeAxisView*> show;
1682 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1683 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1689 suspend_redisplay ();
1691 TreeModel::Children rows = _model->children ();
1692 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1693 TimeAxisView* tv = (*i)[_columns.tv];
1694 (*i)[_columns.visible] = (show.find (tv) != show.end());
1697 resume_redisplay ();