ardour
editor_routes.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000-2009 Paul Davis
3 
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.
8 
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.
13 
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.
17 
18 */
19 
20 #include <cstdlib>
21 #include <cassert>
22 #include <cmath>
23 #include <list>
24 #include <vector>
25 #include <algorithm>
26 
27 #include "pbd/unknown_type.h"
28 #include "pbd/unwind.h"
29 
30 #include "ardour/debug.h"
31 #include "ardour/midi_track.h"
32 #include "ardour/route.h"
33 #include "ardour/session.h"
34 
37 #include "gtkmm2ext/treeutils.h"
38 
39 #include "actions.h"
40 #include "ardour_ui.h"
41 #include "audio_time_axis.h"
42 #include "editor.h"
43 #include "editor_group_tabs.h"
44 #include "editor_routes.h"
45 #include "gui_thread.h"
46 #include "keyboard.h"
47 #include "midi_time_axis.h"
48 #include "mixer_strip.h"
49 #include "route_sorter.h"
50 #include "utils.h"
51 
52 #include "i18n.h"
53 
54 using namespace std;
55 using namespace ARDOUR;
56 using namespace ARDOUR_UI_UTILS;
57 using namespace PBD;
58 using namespace Gtk;
59 using namespace Gtkmm2ext;
60 using namespace Glib;
62 
63 struct ColumnInfo {
64  int index;
65  const char* label;
66  const char* tooltip;
67 };
68 
70  : EditorComponent (e)
71  , _ignore_reorder (false)
72  , _no_redisplay (false)
73  , _adding_routes (false)
74  , _route_deletion_in_progress (false)
75  , _redisplay_on_resume (false)
76  , _redisplay_active (0)
77  , _queue_tv_update (0)
78  , _menu (0)
79  , old_focus (0)
80  , selection_countdown (0)
81  , name_editable (0)
82 {
83  static const int column_width = 22;
84 
85  _scroller.add (_display);
86  _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
87 
88  _model = ListStore::create (_columns);
89  _display.set_model (_model);
90 
91  // Record enable toggle
92  CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
93 
94  rec_col_renderer->set_pixbuf (0, ::get_icon("record-normal-disabled"));
95  rec_col_renderer->set_pixbuf (1, ::get_icon("record-normal-in-progress"));
96  rec_col_renderer->set_pixbuf (2, ::get_icon("record-normal-enabled"));
97  rec_col_renderer->set_pixbuf (3, ::get_icon("record-step"));
98  rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
99 
100  TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
101 
102  rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
103  rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
104 
105  rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
106  rec_state_column->set_alignment(ALIGN_CENTER);
107  rec_state_column->set_expand(false);
108  rec_state_column->set_fixed_width(column_width);
109 
110  // MIDI Input Active
111 
112  CellRendererPixbufMulti* input_active_col_renderer = manage (new CellRendererPixbufMulti());
113  input_active_col_renderer->set_pixbuf (0, ::get_icon("midi-input-inactive"));
114  input_active_col_renderer->set_pixbuf (1, ::get_icon("midi-input-active"));
115  input_active_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_input_active_changed));
116 
117  TreeViewColumn* input_active_column = manage (new TreeViewColumn ("I", *input_active_col_renderer));
118 
119  input_active_column->add_attribute(input_active_col_renderer->property_state(), _columns.is_input_active);
120  input_active_column->add_attribute (input_active_col_renderer->property_visible(), _columns.is_midi);
121 
122  input_active_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
123  input_active_column->set_alignment(ALIGN_CENTER);
124  input_active_column->set_expand(false);
125  input_active_column->set_fixed_width(column_width);
126 
127  // Mute enable toggle
128  CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
129 
130  mute_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("mute-disabled"));
131  mute_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("muted-by-others"));
132  mute_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("mute-enabled"));
133  mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
134 
135  TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
136 
137  mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
138  mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
139  mute_state_column->set_alignment(ALIGN_CENTER);
140  mute_state_column->set_expand(false);
141  mute_state_column->set_fixed_width(15);
142 
143  // Solo enable toggle
144  CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
145 
146  solo_col_renderer->set_pixbuf (Gtkmm2ext::Off, ::get_icon("solo-disabled"));
147  solo_col_renderer->set_pixbuf (Gtkmm2ext::ExplicitActive, ::get_icon("solo-enabled"));
148  solo_col_renderer->set_pixbuf (Gtkmm2ext::ImplicitActive, ::get_icon("soloed-by-others"));
149  solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
150 
151  TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
152 
153  solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
154  solo_state_column->add_attribute(solo_col_renderer->property_visible(), _columns.solo_visible);
155  solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
156  solo_state_column->set_alignment(ALIGN_CENTER);
157  solo_state_column->set_expand(false);
158  solo_state_column->set_fixed_width(column_width);
159 
160  // Solo isolate toggle
161  CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
162 
163  solo_iso_renderer->set_pixbuf (0, ::get_icon("solo-isolate-disabled"));
164  solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolate-enabled"));
165  solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
166 
167  TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
168 
169  solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
170  solo_isolate_state_column->add_attribute(solo_iso_renderer->property_visible(), _columns.solo_visible);
171  solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
172  solo_isolate_state_column->set_alignment(ALIGN_CENTER);
173  solo_isolate_state_column->set_expand(false);
174  solo_isolate_state_column->set_fixed_width(column_width);
175 
176  // Solo safe toggle
177  CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
178 
179  solo_safe_renderer->set_pixbuf (0, ::get_icon("solo-safe-disabled"));
180  solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-safe-enabled"));
181  solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
182 
183  TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
184  solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
185  solo_safe_state_column->add_attribute(solo_safe_renderer->property_visible(), _columns.solo_visible);
186  solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
187  solo_safe_state_column->set_alignment(ALIGN_CENTER);
188  solo_safe_state_column->set_expand(false);
189  solo_safe_state_column->set_fixed_width(column_width);
190 
191  _name_column = _display.append_column ("", _columns.text) - 1;
192  _visible_column = _display.append_column ("", _columns.visible) - 1;
193  _active_column = _display.append_column ("", _columns.active) - 1;
194 
195  _display.append_column (*input_active_column);
196  _display.append_column (*rec_state_column);
197  _display.append_column (*mute_state_column);
198  _display.append_column (*solo_state_column);
199  _display.append_column (*solo_isolate_state_column);
200  _display.append_column (*solo_safe_state_column);
201 
202 
203  TreeViewColumn* col;
204  Gtk::Label* l;
205 
206  ColumnInfo ci[] = {
207  { 0, _("Name"), _("Track/Bus Name") },
208  { 1, S_("Visible|V"), _("Track/Bus visible ?") },
209  { 2, S_("Active|A"), _("Track/Bus active ?") },
210  { 3, S_("MidiInput|I"), _("MIDI input enabled") },
211  { 4, S_("Rec|R"), _("Record enabled") },
212  { 5, S_("Mute|M"), _("Muted") },
213  { 6, S_("Solo|S"), _("Soloed") },
214  { 7, S_("SoloIso|SI"), _("Solo Isolated") },
215  { 8, S_("SoloLock|SS"), _("Solo Safe (Locked)") },
216  { -1, 0, 0 }
217  };
218 
219  for (int i = 0; ci[i].index >= 0; ++i) {
220  col = _display.get_column (ci[i].index);
221  l = manage (new Label (ci[i].label));
222  ARDOUR_UI::instance()->set_tip (*l, ci[i].tooltip);
223  col->set_widget (*l);
224  l->show ();
225  }
226 
227  _display.set_headers_visible (true);
228  _display.get_selection()->set_mode (SELECTION_SINGLE);
229  _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
230  _display.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::selection_changed));
231  _display.set_reorderable (true);
232  _display.set_name (X_("EditGroupList"));
233  _display.set_rules_hint (true);
234  _display.set_size_request (100, -1);
235  _display.add_object_drag (_columns.route.index(), "routes");
236 
237  CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (_name_column));
238 
239  assert (name_cell);
240  name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
241 
242  TreeViewColumn* name_column = _display.get_column (_name_column);
243 
244  assert (name_column);
245 
246  name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
247  name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
248  name_column->set_expand(true);
249  name_column->set_min_width(50);
250 
251  name_cell->property_editable() = true;
252  name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
253 
254  // Set the visible column cell renderer to radio toggle
255  CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_visible_column));
256 
257  visible_cell->property_activatable() = true;
258  visible_cell->property_radio() = false;
259  visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
260 
261  TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_visible_column));
262  visible_col->set_expand(false);
263  visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
264  visible_col->set_fixed_width(30);
265  visible_col->set_alignment(ALIGN_CENTER);
266 
267  CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (_active_column));
268 
269  active_cell->property_activatable() = true;
270  active_cell->property_radio() = false;
271  active_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::active_changed));
272 
273  TreeViewColumn* active_col = dynamic_cast<TreeViewColumn*> (_display.get_column (_active_column));
274  active_col->set_expand (false);
275  active_col->set_sizing (TREE_VIEW_COLUMN_FIXED);
276  active_col->set_fixed_width (30);
277  active_col->set_alignment (ALIGN_CENTER);
278 
279  _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::row_deleted));
280  _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
281 
282  _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
283  _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
284 
285  _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
286  _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
287 
288  _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
289  _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
290 
291  _display.set_enable_search (false);
292 
293  Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::sync_treeview_from_order_keys, this), gui_context());
294 }
295 
296 bool
297 EditorRoutes::focus_in (GdkEventFocus*)
298 {
299  Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
300 
301  if (win) {
302  old_focus = win->get_focus ();
303  } else {
304  old_focus = 0;
305  }
306 
307  name_editable = 0;
308 
309  /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
310  return true;
311 }
312 
313 bool
314 EditorRoutes::focus_out (GdkEventFocus*)
315 {
316  if (old_focus) {
317  old_focus->grab_focus ();
318  old_focus = 0;
319  }
320 
321  return false;
322 }
323 
324 bool
325 EditorRoutes::enter_notify (GdkEventCrossing*)
326 {
327  if (name_editable) {
328  return true;
329  }
330 
331  /* arm counter so that ::selection_filter() will deny selecting anything for the
332  * next two attempts to change selection status.
333  */
335  _scroller.grab_focus ();
336  Keyboard::magic_widget_grab_focus ();
337  return false;
338 }
339 
340 bool
341 EditorRoutes::leave_notify (GdkEventCrossing*)
342 {
344 
345  if (old_focus) {
346  old_focus->grab_focus ();
347  old_focus = 0;
348  }
349 
350  Keyboard::magic_widget_drop_focus ();
351  return false;
352 }
353 
354 void
356 {
357  SessionHandlePtr::set_session (s);
358 
359  initial_display ();
360 
361  if (_session) {
364 
365  /* TODO: check if these needs to be tied in with DisplaySuspender
366  * Given that the UI is single-threaded and DisplaySuspender is only used
367  * in loops in the UI thread all should be fine.
368  */
370  _session->BatchUpdateEnd.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::resume_redisplay, this), gui_context());
371  }
372 }
373 
374 void
375 EditorRoutes::on_input_active_changed (std::string const & path_string)
376 {
377  // Get the model row that has been toggled.
378  Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
379 
380  TimeAxisView* tv = row[_columns.tv];
381  RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
382 
383  if (rtv) {
385  mt = rtv->midi_track();
386  if (mt) {
387  mt->set_input_active (!mt->input_active());
388  }
389  }
390 }
391 
392 void
393 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
394 {
395  DisplaySuspender ds;
396  // Get the model row that has been toggled.
397  Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
398 
399  TimeAxisView* tv = row[_columns.tv];
400  RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
401 
402  if (rtv && rtv->track()) {
404  rl->push_back (rtv->route());
405  _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup);
406  }
407 }
408 
409 void
410 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
411 {
412  // Get the model row that has been toggled.
413  Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
414 
415  TimeAxisView *tv = row[_columns.tv];
416  RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
417 
418  if (rtv != 0) {
420  rl->push_back (rtv->route());
421  _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
422  }
423 }
424 
425 void
426 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
427 {
428  // Get the model row that has been toggled.
429  Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
430 
431  TimeAxisView *tv = row[_columns.tv];
432  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
433 
434  if (rtv != 0) {
436  rl->push_back (rtv->route());
437  if (Config->get_solo_control_is_listen_control()) {
438  _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup);
439  } else {
440  _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup);
441  }
442  }
443 }
444 
445 void
446 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
447 {
448  // Get the model row that has been toggled.
449  Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
450 
451  TimeAxisView *tv = row[_columns.tv];
452  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
453 
454  if (rtv) {
455  rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), this);
456  }
457 }
458 
459 void
460 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
461 {
462  // Get the model row that has been toggled.
463  Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
464 
465  TimeAxisView *tv = row[_columns.tv];
466  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
467 
468  if (rtv) {
469  rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), this);
470  }
471 }
472 
473 void
475 {
476  using namespace Menu_Helpers;
477  using namespace Gtk;
478 
479  _menu = new Menu;
480 
481  MenuList& items = _menu->items();
482  _menu->set_name ("ArdourContextMenu");
483 
484  items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
485  items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
486  items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
487  items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
488  items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
489  items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
490  items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
491  items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
492  items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
493 }
494 
495 void
497 {
498  if (_menu == 0) {
499  build_menu ();
500  }
501 
502  _menu->popup (1, gtk_get_current_event_time());
503 }
504 
505 void
507 {
508  TreeModel::Children rows = _model->children();
509  TreeModel::Children::iterator i;
510  uint32_t position;
511 
512  /* n will be the count of tracks plus children (updated by TimeAxisView::show_at),
513  * so we will use that to know where to put things.
514  */
515  int n;
516 
517  for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
518  TimeAxisView *tv = (*i)[_columns.tv];
520 
521  if (tv == 0) {
522  // just a "title" row
523  continue;
524  }
525 
526  bool visible = tv->marked_for_display ();
527 
528  /* show or hide the TimeAxisView */
529  if (visible) {
530  position += tv->show_at (position, n, &_editor->edit_controls_vbox);
531  } else {
532  tv->hide ();
533  }
534 
535  n++;
536  }
537 
538  /* whenever we go idle, update the track view list to reflect the new order.
539  * we can't do this here, because we could mess up something that is traversing
540  * the track order and has caused a redisplay of the list.
541  */
542  Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
543 
547 
549  /*
550  * We're increasing the size of the canvas while the bottom is visible.
551  * We scroll down to keep in step with the controls layout.
552  */
554  }
555 }
556 
557 void
559 {
561  return;
562  }
563 
564  if (_no_redisplay) {
565  _redisplay_on_resume = true;
566  return;
567  }
568 
569  // model deprecated g_atomic_int_exchange_and_add(, 1)
570  g_atomic_int_inc(&_redisplay_active);
571  if (!g_atomic_int_compare_and_exchange (&_redisplay_active, 1, 1)) {
572  return;
573  }
574 
575  redisplay_real ();
576 
577  while (!g_atomic_int_compare_and_exchange (&_redisplay_active, 1, 0)) {
578  g_atomic_int_set(&_redisplay_active, 1);
579  redisplay_real ();
580  }
581 }
582 
583 void
584 EditorRoutes::row_deleted (Gtk::TreeModel::Path const &)
585 {
586  /* this happens as the second step of a DnD within the treeview, and
587  * when a route is actually removed. we don't differentiate between
588  * the two cases.
589  *
590  * note that the sync_orders_keys() step may not actually change any
591  * RID's (e.g. the last track may be removed, so all other tracks keep
592  * the same RID), which means that no redisplay would happen. so we
593  * have to force a redisplay.
594  */
595 
596  DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview row deleted\n");
597 
598  DisplaySuspender ds;
600 }
601 
602 void
603 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
604 {
605  /* reordering implies that RID's will change, so sync_order_keys() will
606  cause a redisplay.
607  */
608 
609  DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview reordered\n");
611 }
612 
613 void
614 EditorRoutes::visible_changed (std::string const & path)
615 {
617  return;
618  }
619 
620  DisplaySuspender ds;
621  TreeIter iter;
622 
623  if ((iter = _model->get_iter (path))) {
624  TimeAxisView* tv = (*iter)[_columns.tv];
625  if (tv) {
626  bool visible = (*iter)[_columns.visible];
627 
628  if (tv->set_marked_for_display (!visible)) {
630  }
631  }
632  }
633 }
634 
635 void
636 EditorRoutes::active_changed (std::string const & path)
637 {
639  return;
640  }
641 
642  Gtk::TreeModel::Row row = *_model->get_iter (path);
644  bool const active = row[_columns.active];
645  route->set_active (!active, this);
646 }
647 
648 void
649 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
650 {
652  bool from_scratch = (_model->children().size() == 0);
653  Gtk::TreeModel::Children::iterator insert_iter = _model->children().end();
654 
655  for (Gtk::TreeModel::Children::iterator it = _model->children().begin(); it != _model->children().end(); ++it) {
657 
658  if (r->order_key() == (routes.front()->route()->order_key() + routes.size())) {
659  insert_iter = it;
660  break;
661  }
662  }
663 
664  DisplaySuspender ds;
665 
666  _display.set_model (Glib::RefPtr<ListStore>());
667 
668  for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
669 
671 
672  TreeModel::Row row = *(_model->insert (insert_iter));
673 
674  row[_columns.text] = (*x)->route()->name();
675  row[_columns.visible] = (*x)->marked_for_display();
676  row[_columns.active] = (*x)->route()->active ();
677  row[_columns.tv] = *x;
678  row[_columns.route] = (*x)->route ();
679  row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
680 
681  if (midi_trk) {
682  row[_columns.is_input_active] = midi_trk->input_active ();
683  row[_columns.is_midi] = true;
684  } else {
685  row[_columns.is_input_active] = false;
686  row[_columns.is_midi] = false;
687  }
688 
689  row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off;
690  row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route());
691  row[_columns.solo_visible] = !(*x)->route()->is_master ();
692  row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
693  row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
694  row[_columns.name_editable] = true;
695 
696  boost::weak_ptr<Route> wr ((*x)->route());
697 
698  (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
699  (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
700 
701  if ((*x)->is_track()) {
703  t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
704  }
705 
706  if ((*x)->is_midi_track()) {
708  t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
710  }
711 
712  (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
713  (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
714  (*x)->route()->listen_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
715  (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
716  (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
717  (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ());
718 
719  }
720 
723  update_solo_display (true);
728 
729  _display.set_model (_model);
730 
731  /* now update route order keys from the treeview/track display order */
732  if (!from_scratch) {
734  }
735 }
736 
737 void
738 EditorRoutes::handle_gui_changes (string const & what, void*)
739 {
740  if (_adding_routes) {
741  return;
742  }
743 
744  if (what == "track_height") {
745  /* Optional :make tracks change height while it happens, instead
746  of on first-idle
747  */
748  redisplay ();
749  }
750 
751  if (what == "visible_tracks") {
752  redisplay ();
753  }
754 }
755 
756 void
758 {
760 
761  TreeModel::Children rows = _model->children();
762  TreeModel::Children::iterator ri;
763 
764  for (ri = rows.begin(); ri != rows.end(); ++ri) {
765  if ((*ri)[_columns.tv] == tv) {
767  _model->erase (ri);
768  break;
769  }
770  }
771 
772  /* the deleted signal for the treeview/model will take
773  care of any updates.
774  */
775 }
776 
777 void
779 {
780  if (!what_changed.contains (ARDOUR::Properties::name)) {
781  return;
782  }
783 
784  ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
785 
786  boost::shared_ptr<Route> route = r.lock ();
787 
788  if (!route) {
789  return;
790  }
791 
792  TreeModel::Children rows = _model->children();
793  TreeModel::Children::iterator i;
794 
795  for (i = rows.begin(); i != rows.end(); ++i) {
797  if (t == route) {
798  (*i)[_columns.text] = route->name();
799  break;
800  }
801  }
802 }
803 
804 void
806 {
807  if (g_atomic_int_compare_and_exchange (&_queue_tv_update, 0, 1)) {
808  Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
809  }
810 }
811 
812 void
814 {
815  TreeModel::Children rows = _model->children();
816  TreeModel::Children::iterator i;
817 
818  DisplaySuspender ds;
819 
820  for (i = rows.begin(); i != rows.end(); ++i) {
821  TimeAxisView *tv = (*i)[_columns.tv];
822  (*i)[_columns.visible] = tv->marked_for_display ();
823  }
824 
825  /* force route order keys catch up with visibility changes
826  */
827 
829 }
830 
831 void
833 {
834  TreeModel::Children rows = _model->children();
835  TreeModel::Children::iterator i;
836 
837  for (i = rows.begin(); i != rows.end(); ++i) {
838  if ((*i)[_columns.tv] == &tv) {
839  tv.set_marked_for_display (false);
840  (*i)[_columns.visible] = false;
841  redisplay ();
842  break;
843  }
844  }
845 }
846 
847 void
849 {
850  TreeModel::Children rows = _model->children();
851  TreeModel::Children::iterator i;
852 
853 
854  for (i = rows.begin(); i != rows.end(); ++i) {
855  if ((*i)[_columns.tv] == &tv) {
856  tv.set_marked_for_display (true);
857  (*i)[_columns.visible] = true;
858  redisplay ();
859  break;
860  }
861  }
862 }
863 
864 void
866 {
867  if (Config->get_remote_model() == UserOrdered || !_session || _session->deletion_in_progress()) {
868  return;
869  }
870 
871  TreeModel::Children rows = _model->children();
872 
873  if (rows.empty()) {
874  return;
875  }
876 
877 
878  DEBUG_TRACE (DEBUG::OrderKeys, "editor reset remote control ids\n");
879 
880  TreeModel::Children::iterator ri;
881  bool rid_change = false;
882  uint32_t rid = 1;
883  uint32_t invisible_key = UINT32_MAX;
884 
885  for (ri = rows.begin(); ri != rows.end(); ++ri) {
886 
887  /* skip two special values */
888 
889  if (rid == Route::MasterBusRemoteControlID) {
890  rid++;
891  }
892 
893  if (rid == Route::MonitorBusRemoteControlID) {
894  rid++;
895  }
896 
897  boost::shared_ptr<Route> route = (*ri)[_columns.route];
898  bool visible = (*ri)[_columns.visible];
899 
900  if (!route->is_master() && !route->is_monitor()) {
901 
902  uint32_t new_rid = (visible ? rid : invisible_key--);
903 
904  if (new_rid != route->remote_control_id()) {
905  route->set_remote_control_id_explicit (new_rid);
906  rid_change = true;
907  }
908 
909  if (visible) {
910  rid++;
911  }
912 
913  }
914  }
915 
916  if (rid_change) {
917  /* tell the world that we changed the remote control IDs */
919  }
920 }
921 
922 
923 void
925 {
927  return;
928  }
929 
930  TreeModel::Children rows = _model->children();
931 
932  if (rows.empty()) {
933  return;
934  }
935 
936 
937  DEBUG_TRACE (DEBUG::OrderKeys, "editor sync order keys from treeview\n");
938 
939  TreeModel::Children::iterator ri;
940  bool changed = false;
941  bool rid_change = false;
942  uint32_t order = 0;
943  uint32_t rid = 1;
944  uint32_t invisible_key = UINT32_MAX;
945 
946  for (ri = rows.begin(); ri != rows.end(); ++ri) {
947 
948  boost::shared_ptr<Route> route = (*ri)[_columns.route];
949  bool visible = (*ri)[_columns.visible];
950 
951  uint32_t old_key = route->order_key ();
952 
953  if (order != old_key) {
954  route->set_order_key (order);
955 
956  changed = true;
957  }
958 
959  if ((Config->get_remote_model() == MixerOrdered) && !route->is_master() && !route->is_monitor()) {
960 
961  uint32_t new_rid = (visible ? rid : invisible_key--);
962 
963  if (new_rid != route->remote_control_id()) {
964  route->set_remote_control_id_explicit (new_rid);
965  rid_change = true;
966  }
967 
968  if (visible) {
969  rid++;
970  }
971 
972  }
973 
974  ++order;
975  }
976 
977  if (changed) {
978  /* tell the world that we changed the editor sort keys */
980  }
981 
982  if (rid_change) {
983  /* tell the world that we changed the remote control IDs */
985  }
986 }
987 
988 void
990 {
991  /* Some route order key(s) have been changed, make sure that
992  we update out tree/list model and GUI to reflect the change.
993  */
994 
996  return;
997  }
998 
999  DEBUG_TRACE (DEBUG::OrderKeys, "editor sync model from order keys.\n");
1000 
1001  /* we could get here after either a change in the Mixer or Editor sort
1002  * order, but either way, the mixer order keys reflect the intended
1003  * order for the GUI, so reorder the treeview model to match it.
1004  */
1005 
1006  vector<int> neworder;
1007  TreeModel::Children rows = _model->children();
1008  uint32_t old_order = 0;
1009  bool changed = false;
1010 
1011  if (rows.empty()) {
1012  return;
1013  }
1014 
1015  OrderKeySortedRoutes sorted_routes;
1016 
1017  for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri, ++old_order) {
1018  boost::shared_ptr<Route> route = (*ri)[_columns.route];
1019  sorted_routes.push_back (RoutePlusOrderKey (route, old_order, route->order_key ()));
1020  }
1021 
1023 
1024  sort (sorted_routes.begin(), sorted_routes.end(), cmp);
1025  neworder.assign (sorted_routes.size(), 0);
1026 
1027  uint32_t n = 0;
1028 
1029  for (OrderKeySortedRoutes::iterator sr = sorted_routes.begin(); sr != sorted_routes.end(); ++sr, ++n) {
1030 
1031  neworder[n] = sr->old_display_order;
1032 
1033  if (sr->old_display_order != n) {
1034  changed = true;
1035  }
1036 
1037  DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("EDITOR change order for %1 from %2 to %3\n",
1038  sr->route->name(), sr->old_display_order, n));
1039  }
1040 
1041  if (changed) {
1042  Unwinder<bool> uw (_ignore_reorder, true);
1043  _model->reorder (neworder);
1044  }
1045 
1046  redisplay ();
1047 }
1048 
1049 void
1050 EditorRoutes::hide_all_tracks (bool /*with_select*/)
1051 {
1052  TreeModel::Children rows = _model->children();
1053  TreeModel::Children::iterator i;
1054 
1055  DisplaySuspender ds;
1056 
1057  for (i = rows.begin(); i != rows.end(); ++i) {
1058 
1059  TreeModel::Row row = (*i);
1060  TimeAxisView *tv = row[_columns.tv];
1061 
1062  if (tv == 0) {
1063  continue;
1064  }
1065 
1066  row[_columns.visible] = false;
1067  }
1068 }
1069 
1070 void
1072 {
1073  TreeModel::Children rows = _model->children();
1074  TreeModel::Children::iterator i;
1075 
1076  DisplaySuspender ds;
1077 
1078  for (i = rows.begin(); i != rows.end(); ++i) {
1079 
1080  TreeModel::Row row = (*i);
1081  TimeAxisView* tv = row[_columns.tv];
1082 
1083  if (tv == 0) {
1084  continue;
1085  }
1086 
1087  tv->set_marked_for_display (yn);
1088  (*i)[_columns.visible] = yn;
1089  }
1090 
1091  /* force route order keys catch up with visibility changes
1092  */
1093 
1095 }
1096 
1097 void
1099 {
1100  TreeModel::Children rows = _model->children();
1101  TreeModel::Children::iterator i;
1102 
1103  DisplaySuspender ds;
1104 
1105  for (i = rows.begin(); i != rows.end(); ++i) {
1106 
1107  TreeModel::Row row = (*i);
1108  TimeAxisView* tv = row[_columns.tv];
1109 
1110  AudioTimeAxisView* atv;
1111  MidiTimeAxisView* mtv;
1112 
1113  if (tv == 0) {
1114  continue;
1115  }
1116 
1117  if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1118  switch (tracks) {
1119  case 0:
1120  (*i)[_columns.visible] = yn;
1121  break;
1122 
1123  case 1:
1124  if (atv->is_audio_track()) {
1125  (*i)[_columns.visible] = yn;
1126  }
1127  break;
1128 
1129  case 2:
1130  if (!atv->is_audio_track()) {
1131  (*i)[_columns.visible] = yn;
1132  }
1133  break;
1134  }
1135  }
1136  else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
1137  switch (tracks) {
1138  case 0:
1139  (*i)[_columns.visible] = yn;
1140  break;
1141 
1142  case 3:
1143  if (mtv->is_midi_track()) {
1144  (*i)[_columns.visible] = yn;
1145  }
1146  break;
1147  }
1148  }
1149  }
1150 
1151  /* force route order keys catch up with visibility changes
1152  */
1153 
1155 }
1156 
1157 void
1159 {
1160  set_all_tracks_visibility (false);
1161 }
1162 
1163 void
1165 {
1167 }
1168 
1169 void
1171 {
1173 }
1174 void
1176 {
1177  set_all_audio_midi_visibility (1, false);
1178 }
1179 
1180 void
1182 {
1184 }
1185 void
1187 {
1188  set_all_audio_midi_visibility (2, false);
1189 }
1190 
1191 void
1193 {
1195 }
1196 void
1198 {
1199  set_all_audio_midi_visibility (3, false);
1200 }
1201 
1202 bool
1203 EditorRoutes::key_press (GdkEventKey* ev)
1204 {
1205  TreeViewColumn *col;
1207  TreePath path;
1208 
1209  switch (ev->keyval) {
1210  case GDK_Tab:
1211  case GDK_ISO_Left_Tab:
1212 
1213  /* If we appear to be editing something, leave that cleanly and appropriately. */
1214  if (name_editable) {
1215  name_editable->editing_done ();
1216  name_editable = 0;
1217  }
1218 
1219  col = _display.get_column (_name_column); // select&focus on name column
1220 
1221  if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
1223  } else {
1225  }
1226 
1227  return true;
1228  break;
1229 
1230  case 'm':
1231  if (get_relevant_routes (rl)) {
1232  _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
1233  }
1234  return true;
1235  break;
1236 
1237  case 's':
1238  if (get_relevant_routes (rl)) {
1239  if (Config->get_solo_control_is_listen_control()) {
1240  _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup);
1241  } else {
1242  _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup);
1243  }
1244  }
1245  return true;
1246  break;
1247 
1248  case 'r':
1249  if (get_relevant_routes (rl)) {
1250  _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
1251  }
1252  break;
1253 
1254  default:
1255  break;
1256  }
1257 
1258  return false;
1259 }
1260 
1261 bool
1263 {
1264  TimeAxisView* tv;
1265  RouteTimeAxisView* rtv;
1266  RefPtr<TreeSelection> selection = _display.get_selection();
1267  TreePath path;
1268  TreeIter iter;
1269 
1270  if (selection->count_selected_rows() != 0) {
1271 
1272  /* use selection */
1273 
1274  RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
1275  iter = selection->get_selected (tm);
1276 
1277  } else {
1278  /* use mouse pointer */
1279 
1280  int x, y;
1281  int bx, by;
1282 
1283  _display.get_pointer (x, y);
1284  _display.convert_widget_to_bin_window_coords (x, y, bx, by);
1285 
1286  if (_display.get_path_at_pos (bx, by, path)) {
1287  iter = _model->get_iter (path);
1288  }
1289  }
1290 
1291  if (iter) {
1292  tv = (*iter)[_columns.tv];
1293  if (tv) {
1294  rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1295  if (rtv) {
1296  rl->push_back (rtv->route());
1297  }
1298  }
1299  }
1300 
1301  return !rl->empty();
1302 }
1303 
1304 bool
1305 EditorRoutes::button_press (GdkEventButton* ev)
1306 {
1307  if (Keyboard::is_context_menu_event (ev)) {
1308  show_menu ();
1309  return true;
1310  }
1311 
1312  TreeModel::Path path;
1313  TreeViewColumn *tvc;
1314  int cell_x;
1315  int cell_y;
1316 
1317  if (!_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y)) {
1318  /* cancel selection */
1319  _display.get_selection()->unselect_all ();
1320  /* end any editing by grabbing focus */
1321  _display.grab_focus ();
1322  return true;
1323  }
1324 
1325  //Scroll editor canvas to selected track
1326  if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1327 
1328  Gtk::TreeModel::Row row = *_model->get_iter (path);
1329  TimeAxisView *tv = row[_columns.tv];
1330 
1331  if (tv) {
1333  }
1334  }
1335 
1336  return false;
1337 }
1338 
1339 void
1341 {
1342  _editor->begin_reversible_selection_op (X_("Select Track from Route List"));
1343 
1344  if (_display.get_selection()->count_selected_rows() > 0) {
1345 
1346  TreeIter iter;
1347  TreeView::Selection::ListHandle_Path rows = _display.get_selection()->get_selected_rows ();
1348  TrackViewList selected;
1349 
1351 
1352  for (TreeView::Selection::ListHandle_Path::iterator i = rows.begin(); i != rows.end(); ++i) {
1353 
1354  if ((iter = _model->get_iter (*i))) {
1355 
1356  TimeAxisView* tv = (*iter)[_columns.tv];
1357  selected.push_back (tv);
1358  }
1359 
1360  }
1361 
1362  _editor->get_selection().set (selected);
1363  _editor->ensure_time_axis_view_is_visible (*(selected.front()), true);
1364 
1365  } else {
1367  }
1368 
1370 }
1371 
1372 bool
1373 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1374 {
1375  if (selection_countdown) {
1376  if (--selection_countdown == 0) {
1377  return true;
1378  } else {
1379  /* no selection yet ... */
1380  return false;
1381  }
1382  }
1383  return true;
1384 }
1385 
1387 {
1389  if (a->is_master()) {
1390  /* master before everything else */
1391  return true;
1392  } else if (b->is_master()) {
1393  /* everything else before master */
1394  return false;
1395  }
1396  return a->order_key () < b->order_key ();
1397  }
1398 };
1399 
1400 void
1402 {
1403  DisplaySuspender ds;
1404  _model->clear ();
1405 
1406  if (!_session) {
1407  return;
1408  }
1409 
1410  RouteList r (*_session->get_routes());
1411 
1412  r.sort (EditorOrderRouteSorter ());
1413  _editor->add_routes (r);
1414 }
1415 
1416 void
1417 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1418  int x, int y,
1419  const SelectionData& data,
1420  guint info, guint time)
1421 {
1422  if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1423  _display.on_drag_data_received (context, x, y, data, info, time);
1424  return;
1425  }
1426 
1427  context->drag_finish (true, false, time);
1428 }
1429 
1430 void
1432 {
1433  if (_editor->selection->tracks.empty()) {
1434  return;
1435  }
1436 
1437  typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1438  std::list<ViewRoute> view_routes;
1439  std::vector<int> neworder;
1440  TreeModel::Children rows = _model->children();
1441  TreeModel::Children::iterator ri;
1442 
1443  for (ri = rows.begin(); ri != rows.end(); ++ri) {
1444  TimeAxisView* tv = (*ri)[_columns.tv];
1445  boost::shared_ptr<Route> route = (*ri)[_columns.route];
1446 
1447  view_routes.push_back (ViewRoute (tv, route));
1448  }
1449 
1450  list<ViewRoute>::iterator trailing;
1451  list<ViewRoute>::iterator leading;
1452 
1453  if (up) {
1454 
1455  trailing = view_routes.begin();
1456  leading = view_routes.begin();
1457 
1458  ++leading;
1459 
1460  while (leading != view_routes.end()) {
1461  if (_editor->selection->selected (leading->first)) {
1462  view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1463  leading = view_routes.erase (leading);
1464  } else {
1465  ++leading;
1466  ++trailing;
1467  }
1468  }
1469 
1470  } else {
1471 
1472  /* if we could use reverse_iterator in list::insert, this code
1473  would be a beautiful reflection of the code above. but we can't
1474  and so it looks like a bit of a mess.
1475  */
1476 
1477  trailing = view_routes.end();
1478  leading = view_routes.end();
1479 
1480  --leading; if (leading == view_routes.begin()) { return; }
1481  --leading;
1482  --trailing;
1483 
1484  while (1) {
1485 
1486  if (_editor->selection->selected (leading->first)) {
1487  list<ViewRoute>::iterator tmp;
1488 
1489  /* need to insert *after* trailing, not *before* it,
1490  which is what insert (iter, val) normally does.
1491  */
1492 
1493  tmp = trailing;
1494  tmp++;
1495 
1496  view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1497 
1498  /* can't use iter = cont.erase (iter); form here, because
1499  we need iter to move backwards.
1500  */
1501 
1502  tmp = leading;
1503  --tmp;
1504 
1505  bool done = false;
1506 
1507  if (leading == view_routes.begin()) {
1508  /* the one we've just inserted somewhere else
1509  was the first in the list. erase this copy,
1510  and then break, because we're done.
1511  */
1512  done = true;
1513  }
1514 
1515  view_routes.erase (leading);
1516 
1517  if (done) {
1518  break;
1519  }
1520 
1521  leading = tmp;
1522 
1523  } else {
1524  if (leading == view_routes.begin()) {
1525  break;
1526  }
1527  --leading;
1528  --trailing;
1529  }
1530  };
1531  }
1532 
1533  for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1534  uint32_t order = leading->second->order_key ();
1535  neworder.push_back (order);
1536  }
1537 
1538 #ifndef NDEBUG
1539  DEBUG_TRACE (DEBUG::OrderKeys, "New order after moving tracks:\n");
1540  for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1541  DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("\t%1\n", *i));
1542  }
1543  DEBUG_TRACE (DEBUG::OrderKeys, "-------\n");
1544 
1545  for (vector<int>::iterator i = neworder.begin(); i != neworder.end(); ++i) {
1546  if (*i >= (int) neworder.size()) {
1547  cerr << "Trying to move something to " << *i << " of " << neworder.size() << endl;
1548  }
1549  assert (*i < (int) neworder.size ());
1550  }
1551 #endif
1552 
1553  _model->reorder (neworder);
1554 }
1555 
1556 void
1558 {
1559  TreeModel::Children rows = _model->children();
1560  TreeModel::Children::iterator i;
1561 
1562  for (i = rows.begin(); i != rows.end(); ++i) {
1563  boost::shared_ptr<Route> route = (*i)[_columns.route];
1564 
1565  if (boost::dynamic_pointer_cast<Track> (route)) {
1567 
1568  if (mt) {
1569  (*i)[_columns.is_input_active] = mt->input_active();
1570  }
1571  }
1572  }
1573 }
1574 
1575 void
1577 {
1578  if (g_atomic_int_compare_and_exchange (&_queue_tv_update, 0, 1)) {
1579  Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
1580  }
1581 }
1582 
1583 bool
1585 {
1586  g_atomic_int_set (&_queue_tv_update, 0);
1587  TreeModel::Children rows = _model->children();
1588  TreeModel::Children::iterator i;
1589 
1590  for (i = rows.begin(); i != rows.end(); ++i) {
1591  boost::shared_ptr<Route> route = (*i)[_columns.route];
1596  (*i)[_columns.active] = route->active ();
1597  if (boost::dynamic_pointer_cast<Track> (route)) {
1599 
1600  if (route->record_enabled()) {
1601  if (_session->record_status() == Session::Recording) {
1602  (*i)[_columns.rec_state] = 1;
1603  } else {
1604  (*i)[_columns.rec_state] = 2;
1605  }
1606  } else if (mt && mt->step_editing()) {
1607  (*i)[_columns.rec_state] = 3;
1608  } else {
1609  (*i)[_columns.rec_state] = 0;
1610  }
1611 
1612  (*i)[_columns.name_editable] = !route->record_enabled ();
1613  }
1614  }
1615 
1616  return false; // do not call again (until needed)
1617 }
1618 
1619 
1620 void
1622 {
1623  if (g_atomic_int_compare_and_exchange (&_queue_tv_update, 0, 1)) {
1624  Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
1625  }
1626 }
1627 
1628 void
1629 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1630 {
1631  if (g_atomic_int_compare_and_exchange (&_queue_tv_update, 0, 1)) {
1632  Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
1633  }
1634 }
1635 
1636 void
1638 {
1639  if (g_atomic_int_compare_and_exchange (&_queue_tv_update, 0, 1)) {
1640  Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
1641  }
1642 }
1643 
1644 void
1646 {
1647  if (g_atomic_int_compare_and_exchange (&_queue_tv_update, 0, 1)) {
1648  Glib::signal_idle().connect (sigc::mem_fun (*this, &EditorRoutes::idle_update_mute_rec_solo_etc));
1649  }
1650 }
1651 
1652 list<TimeAxisView*>
1654 {
1655  list<TimeAxisView*> v;
1656  for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1657  v.push_back ((*i)[_columns.tv]);
1658  }
1659 
1660  return v;
1661 }
1662 
1663 void
1665 {
1666  _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1667  _model->clear ();
1668  _display.set_model (_model);
1669 }
1670 
1671 void
1672 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1673 {
1674  name_editable = ce;
1675 
1676  /* give it a special name */
1677 
1678  Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1679 
1680  if (e) {
1681  e->set_name (X_("RouteNameEditorEntry"));
1682  }
1683 }
1684 
1685 void
1686 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1687 {
1688  name_editable = 0;
1689 
1690  TreeIter iter = _model->get_iter (path);
1691 
1692  if (!iter) {
1693  return;
1694  }
1695 
1696  boost::shared_ptr<Route> route = (*iter)[_columns.route];
1697 
1698  if (route && route->name() != new_text) {
1699  route->set_name (new_text);
1700  }
1701 }
1702 
1703 void
1705 {
1707 }
1708 
1709 void
1711 {
1713 
1714  set<TimeAxisView*> show;
1715  for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1717  if (tav) {
1718  show.insert (tav);
1719  }
1720  }
1721 
1722  DisplaySuspender ds;
1723 
1724  TreeModel::Children rows = _model->children ();
1725  for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1726  TimeAxisView* tv = (*i)[_columns.tv];
1727  (*i)[_columns.visible] = (show.find (tv) != show.end());
1728  }
1729 }
void set_order_key(uint32_t)
Definition: route.cc:336
void show_all_audiotracks()
virtual guint32 show_at(double y, int &nth, Gtk::VBox *parent)
volatile gint _queue_tv_update
void clear_regions()
Definition: selection.cc:159
void sync_order_keys_from_treeview()
void commit_reversible_selection_op()
Definition: editor.cc:3376
void row_deleted(Gtk::TreeModel::Path const &)
void set_listen(boost::shared_ptr< RouteList >, bool, SessionEvent::RTeventCallback after=rt_cleanup, bool group_override=false)
boost::shared_ptr< ARDOUR::MidiTrack > midi_track() const
Definition: route_ui.cc:1762
void selection_changed()
void update_active_display()
Selection * selection
Definition: editor.h:1801
bool _ignore_reorder
void update_rec_display()
void on_tv_solo_safe_toggled(std::string const &)
void set_solo(boost::shared_ptr< RouteList >, bool, SessionEvent::RTeventCallback after=rt_cleanup, bool group_override=false)
bool is_midi_track() const
Definition: route_ui.cc:1756
bool selection_filter(Glib::RefPtr< Gtk::TreeModel > const &, Gtk::TreeModel::Path const &, bool)
bool key_press(GdkEventKey *ev)
void sync_order_keys()
Definition: session.cc:5510
bool is_audio_track() const
Definition: route_ui.cc:1744
void display_drag_data_received(Glib::RefPtr< Gdk::DragContext > const &, gint, gint, Gtk::SelectionData const &, guint, guint)
void update_solo_isolate_display()
uint32_t selection_countdown
void on_tv_solo_enable_toggled(std::string const &)
Definition: ardour_ui.h:130
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
static ARDOUR_UI * instance()
Definition: ardour_ui.h:187
LIBARDOUR_API PBD::PropertyDescriptor< std::string > name
bool button_press(GdkEventButton *)
void handle_gui_changes(std::string const &, void *)
void hide_all_miditracks()
void update_visibility()
PBD::Signal0< void > SoloChanged
Definition: session.h:708
bool solo_safe() const
Definition: route.cc:804
Gtk::TreeModelColumn< bool > active
bool idle_update_mute_rec_solo_etc()
Gtk::TreeModelColumn< bool > is_track
void set_record_enabled(boost::shared_ptr< RouteList >, bool, SessionEvent::RTeventCallback after=rt_cleanup, bool group_override=false)
Gtk::Adjustment vertical_adjustment
Definition: editor.h:1052
void set(std::list< Selectable * > const &)
Definition: selection.cc:1024
Gtk::TreeModelColumn< bool > name_editable
LIBARDOUR_API uint64_t OrderKeys
Definition: debug.cc:61
void move_selected_tracks(bool)
Definition: Beats.hpp:239
void on_tv_mute_enable_toggled(std::string const &)
void set_solo_isolated(bool yn, void *src)
Definition: route.cc:939
bool set_name(const std::string &str)
Definition: route.cc:3870
void set_pixbuf(uint32_t state, Glib::RefPtr< Gdk::Pixbuf > pixbuf)
void set_solo_safe(bool yn, void *src)
Definition: route.cc:795
bool enter_notify(GdkEventCrossing *)
void active_changed(std::string const &)
void show_all_routes()
RecordState record_status() const
Definition: session.h:276
void set_session(ARDOUR::Session *)
void hide_track_in_display(TimeAxisView &)
void hide_all_audiotracks()
#define ENSURE_GUI_THREAD(obj, method,...)
Definition: gui_thread.h:34
void sync_treeview_from_order_keys()
virtual bool set_marked_for_display(bool)
Definition: axis_view.cc:87
Gtk::TreeModelColumn< uint32_t > solo_state
PBD::Signal1< void, bool > StepEditStatusChange
Definition: midi_track.h:107
Gtk::TreeModelColumn< uint32_t > mute_state
double _full_canvas_height
full height of the canvas
Definition: editor.h:1075
void hide_all_routes()
#define _(Text)
Definition: i18n.h:11
void redisplay_real()
void update_mute_display()
void set_all_tracks_visibility(bool)
void name_edit_started(Gtk::CellEditable *, const Glib::ustring &)
LIBGTKMM2EXT_API uint64_t Keyboard
Definition: debug.cc:23
virtual bool record_enabled() const
Definition: route.h:131
void show_all_audiobus()
#define X_(Text)
Definition: i18n.h:13
static Gtkmm2ext::ActiveState solo_isolate_active_state(boost::shared_ptr< ARDOUR::Route >)
Definition: route_ui.cc:1118
uint32_t order_key() const
Definition: route.cc:306
PBD::Signal0< void > InputActiveChanged
Definition: midi_track.h:135
ExplicitActive
Definition: widget_state.h:13
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
Gtk::TreeModelColumn< TimeAxisView * > tv
Gtkmm2ext::DnDTreeView< boost::shared_ptr< ARDOUR::Route > > _display
bool listening_via_monitor() const
Definition: route.cc:785
void initial_display()
void visible_changed(std::string const &)
void set_input_active(bool)
Definition: midi_track.cc:826
void on_tv_solo_isolate_toggled(std::string const &)
void name_edit(std::string const &, std::string const &)
void begin_reversible_selection_op(std::string cmd_name)
Definition: editor.cc:3366
void routes_added(std::list< RouteTimeAxisView * > routes)
ModelColumns _columns
void on_drag_data_received(const Glib::RefPtr< Gdk::DragContext > &context, int x, int y, const Gtk::SelectionData &selection_data, guint info, guint time)
Definition: dndtreeview.h:116
void show_tracks_with_regions_at_playhead()
ImplicitActive
Definition: widget_state.h:13
framepos_t transport_frame() const
Definition: session.h:551
Definition: amp.h:29
bool focus_in(GdkEventFocus *)
boost::shared_ptr< ARDOUR::Track > track() const
Definition: route_ui.cc:1738
Gtk::TreeModelColumn< bool > is_midi
void update_solo_display(bool)
static Gtkmm2ext::ActiveState mute_active_state(ARDOUR::Session *, boost::shared_ptr< ARDOUR::Route >)
Definition: route_ui.cc:1193
#define gui_context()
Definition: gui_thread.h:36
void show_all_miditracks()
void show_track_in_display(TimeAxisView &)
bool deletion_in_progress() const
Definition: session.h:179
void add_object_drag(int column, std::string type_name)
Definition: dndtreeview.cc:58
bool self_soloed() const
Definition: route.h:164
void route_removed(TimeAxisView *)
boost::shared_ptr< RouteList > get_routes() const
Definition: session.h:229
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
static Gtkmm2ext::ActiveState solo_active_state(boost::shared_ptr< ARDOUR::Route >)
Definition: route_ui.cc:1090
bool record_enabled() const
Definition: track.cc:215
Gtk::ScrolledWindow _scroller
void suspend_redisplay()
Definition: editor_routes.h:42
void solo_changed_so_update_mute()
static Gtkmm2ext::ActiveState solo_safe_active_state(boost::shared_ptr< ARDOUR::Route >)
Definition: route_ui.cc:1132
void set_all_audio_midi_visibility(int, bool)
void notify_remote_id_change()
Definition: session.cc:5494
volatile gint _redisplay_active
LIBPBD_API Transmitter info
LIBARDOUR_API PBD::PropertyDescriptor< bool > active
Definition: route_group.cc:43
PBD::Signal0< void > RecordStateChanged
Definition: session.h:297
void reset_controls_layout_width()
Gtk::TreeModelColumn< std::string > text
void set_tip(Gtk::Widget &w, const gchar *tip)
Gtk::TreeModelColumn< boost::shared_ptr< ARDOUR::Route > > route
void update_input_active_display()
bool active() const
Definition: route.h:95
LIBGTKMM2EXT_API void treeview_select_next(Gtk::TreeView &view, Glib::RefPtr< Gtk::TreeModel > model, Gtk::TreeViewColumn *col)
bool muted() const
Definition: route.cc:1031
double _visible_canvas_height
height of the visible area of the track canvas
Definition: editor.h:1074
bool selected(TimeAxisView *)
Definition: selection.cc:920
void add_routes(ARDOUR::RouteList &)
Definition: editor.cc:5249
std::vector< RoutePlusOrderKey > OrderKeySortedRoutes
Definition: route_sorter.h:42
Gtk::VBox edit_controls_vbox
Definition: editor.h:1084
virtual void hide()
Gtk::TreeModelColumn< bool > is_input_active
void set_active(bool yn, void *)
Definition: route.cc:4002
Definition: editor.h:134
bool is_master() const
Definition: route.h:111
void on_input_active_changed(std::string const &)
bool get_relevant_routes(boost::shared_ptr< ARDOUR::RouteList > rl)
bool focus_out(GdkEventFocus *)
void ensure_time_axis_view_is_visible(TimeAxisView const &tav, bool at_top)
PBD::Signal0< void > BatchUpdateStart
Definition: session.h:186
std::string name() const
void clear_tracks()
Definition: selection.cc:127
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > position
Definition: region.cc:65
TrackSelection tracks
Definition: selection.h:81
void route_property_changed(const PBD::PropertyChange &, boost::weak_ptr< ARDOUR::Route >)
bool leave_notify(GdkEventCrossing *)
Gtk::CellEditable * name_editable
bool input_active() const
Definition: midi_track.cc:820
Definition: debug.h:30
Gtk::TreeModelColumn< uint32_t > solo_safe_state
PBD::Signal0< void > RecordEnableChanged
Definition: track.h:167
std::list< TimeAxisView * > views() const
Selection & get_selection() const
Definition: editor.h:244
void reset_remote_control_ids()
void hide_all_audiobus()
#define S_(Text)
Definition: i18n.h:18
Gtk::TreeModelColumn< uint32_t > rec_state
void update_solo_safe_display()
bool contains(PropertyDescriptor< T > p) const
RouteTimeAxisView * axis_view_from_route(boost::shared_ptr< ARDOUR::Route >) const
Definition: editor.cc:5201
Gtk::TreeModelColumn< uint32_t > solo_isolate_state
void reset_controls_layout_height(int32_t height)
Gtk::Menu * _menu
Glib::PropertyProxy< uint32_t > property_state()
void on_tv_rec_enable_changed(std::string const &)
uint32_t remote_control_id() const
Definition: route.cc:286
Off
Definition: widget_state.h:13
bool _route_deletion_in_progress
void set_remote_control_id_explicit(uint32_t order_key)
Definition: route.cc:312
Glib::RefPtr< Gtk::ListStore > _model
bool marked_for_display() const
Definition: axis_view.cc:80
bool _redisplay_on_resume
Glib::RefPtr< Gdk::Pixbuf > get_icon(const char *cname)
Definition: utils.cc:674
#define MISSING_INVALIDATOR
Definition: event_loop.h:86
std::list< boost::shared_ptr< Route > > RouteList
Definition: types.h:532
boost::shared_ptr< RouteList > get_routes_with_regions_at(framepos_t const) const
Definition: session.cc:4997
Gtk::Widget * old_focus
PBD::Signal0< void > BatchUpdateEnd
Definition: session.h:187
void hide_all_tracks(bool)
bool solo_isolated() const
Definition: route.cc:993
ARDOUR::Session * _session
boost::shared_ptr< ARDOUR::Route > route() const
Definition: route_ui.h:76
bool sync_track_view_list_and_routes()
Definition: editor.cc:5397
LIBGTKMM2EXT_API void treeview_select_previous(Gtk::TreeView &view, Glib::RefPtr< Gtk::TreeModel > model, Gtk::TreeViewColumn *col)
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
void set_mute(boost::shared_ptr< RouteList >, bool, SessionEvent::RTeventCallback after=rt_cleanup, bool group_override=false)
bool step_editing() const
Definition: midi_track.h:103
Gtk::TreeModelColumn< bool > solo_visible
Gtk::TreeModelColumn< bool > visible
bool is_monitor() const
Definition: route.h:112
EditorRoutes(Editor *)
void resume_redisplay()
Definition: editor_routes.h:49
void reordered(Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int *)