make track list solo-isolate column wide enough to show "SI" as its name
[ardour.git] / gtk2_ardour / editor_routes.cc
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 <list>
21 #include <vector>
22 #include <algorithm>
23 #include <cstdlib>
24 #include <cmath>
25 #include <cassert>
26
27 #include "ardour/session.h"
28
29 #include "editor.h"
30 #include "keyboard.h"
31 #include "ardour_ui.h"
32 #include "audio_time_axis.h"
33 #include "midi_time_axis.h"
34 #include "mixer_strip.h"
35 #include "gui_thread.h"
36 #include "actions.h"
37 #include "utils.h"
38 #include "editor_group_tabs.h"
39 #include "editor_routes.h"
40
41 #include "pbd/unknown_type.h"
42
43 #include "ardour/route.h"
44 #include "ardour/midi_track.h"
45
46 #include "gtkmm2ext/cell_renderer_pixbuf_multi.h"
47 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
48 #include "gtkmm2ext/treeutils.h"
49
50 #include "i18n.h"
51
52 using namespace std;
53 using namespace ARDOUR;
54 using namespace PBD;
55 using namespace Gtk;
56 using namespace Gtkmm2ext;
57 using namespace Glib;
58 using Gtkmm2ext::Keyboard;
59
60 EditorRoutes::EditorRoutes (Editor* e)
61         : EditorComponent (e)
62         , _ignore_reorder (false)
63         , _no_redisplay (false)
64         , _redisplay_does_not_sync_order_keys (false)
65         , _redisplay_does_not_reset_order_keys (false)
66         ,_menu (0)
67         , old_focus (0)
68         , selection_countdown (0)
69         , name_editable (0)
70 {
71         _scroller.add (_display);
72         _scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
73
74         _model = ListStore::create (_columns);
75         _display.set_model (_model);
76
77         // Record enable toggle
78         CellRendererPixbufMulti* rec_col_renderer = manage (new CellRendererPixbufMulti());
79
80         rec_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
81         rec_col_renderer->set_pixbuf (1, ::get_icon("rec-in-progress"));
82         rec_col_renderer->set_pixbuf (2, ::get_icon("rec-enabled"));
83         rec_col_renderer->set_pixbuf (3, ::get_icon("step-editing"));
84         rec_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_rec_enable_changed));
85
86         TreeViewColumn* rec_state_column = manage (new TreeViewColumn("R", *rec_col_renderer));
87
88         rec_state_column->add_attribute(rec_col_renderer->property_state(), _columns.rec_state);
89         rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track);
90         rec_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
91         rec_state_column->set_alignment(ALIGN_CENTER);
92         rec_state_column->set_expand(false);
93         rec_state_column->set_fixed_width(15);
94
95         // Mute enable toggle
96         CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti());
97
98         mute_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
99         mute_col_renderer->set_pixbuf (1, ::get_icon("muted-by-others"));
100         mute_col_renderer->set_pixbuf (2, ::get_icon("mute-enabled"));
101         mute_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled));
102
103         TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer));
104
105         mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state);
106         mute_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
107         mute_state_column->set_alignment(ALIGN_CENTER);
108         mute_state_column->set_expand(false);
109         mute_state_column->set_fixed_width(15);
110
111         // Solo enable toggle
112         CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti());
113
114         solo_col_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
115         solo_col_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
116         solo_col_renderer->set_pixbuf (3, ::get_icon("soloed-by-others"));
117         solo_col_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled));
118
119         TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer));
120
121         solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state);
122         solo_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
123         solo_state_column->set_alignment(ALIGN_CENTER);
124         solo_state_column->set_expand(false);
125         solo_state_column->set_fixed_width(15);
126
127         // Solo isolate toggle
128         CellRendererPixbufMulti* solo_iso_renderer = manage (new CellRendererPixbufMulti());
129
130         solo_iso_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
131         solo_iso_renderer->set_pixbuf (1, ::get_icon("solo-isolated"));
132         solo_iso_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_isolate_toggled));
133
134         TreeViewColumn* solo_isolate_state_column = manage (new TreeViewColumn("SI", *solo_iso_renderer));
135
136         solo_isolate_state_column->add_attribute(solo_iso_renderer->property_state(), _columns.solo_isolate_state);
137         solo_isolate_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
138         solo_isolate_state_column->set_alignment(ALIGN_CENTER);
139         solo_isolate_state_column->set_expand(false);
140         solo_isolate_state_column->set_fixed_width(22);
141
142         // Solo safe toggle
143         CellRendererPixbufMulti* solo_safe_renderer = manage (new CellRendererPixbufMulti ());
144
145         solo_safe_renderer->set_pixbuf (0, ::get_icon("act-disabled"));
146         solo_safe_renderer->set_pixbuf (1, ::get_icon("solo-enabled"));
147         solo_safe_renderer->signal_changed().connect (sigc::mem_fun (*this, &EditorRoutes::on_tv_solo_safe_toggled));
148
149         TreeViewColumn* solo_safe_state_column = manage (new TreeViewColumn(_("SS"), *solo_safe_renderer));
150         solo_safe_state_column->add_attribute(solo_safe_renderer->property_state(), _columns.solo_safe_state);
151         solo_safe_state_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
152         solo_safe_state_column->set_alignment(ALIGN_CENTER);
153         solo_safe_state_column->set_expand(false);
154         solo_safe_state_column->set_fixed_width(22);
155
156         _display.append_column (*rec_state_column);
157         _display.append_column (*mute_state_column);
158         _display.append_column (*solo_state_column);
159         _display.append_column (*solo_isolate_state_column);
160         _display.append_column (*solo_safe_state_column);
161         
162         int colnum = _display.append_column (_("Name"), _columns.text);
163         TreeViewColumn* c = _display.get_column (colnum-1);
164         c->set_data ("i_am_the_tab_column", (void*) 0xfeedface);
165         _display.append_column (_("V"), _columns.visible);
166         
167         _display.set_headers_visible (true);
168         _display.set_name ("TrackListDisplay");
169         _display.get_selection()->set_mode (SELECTION_SINGLE);
170         _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRoutes::selection_filter));
171         _display.set_reorderable (true);
172         _display.set_rules_hint (true);
173         _display.set_size_request (100, -1);
174         _display.add_object_drag (_columns.route.index(), "routes");
175
176         CellRendererText* name_cell = dynamic_cast<CellRendererText*> (_display.get_column_cell_renderer (5));
177
178         assert (name_cell);
179         name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit_started));
180
181         TreeViewColumn* name_column = _display.get_column (5);
182
183         assert (name_column);
184
185         name_column->add_attribute (name_cell->property_editable(), _columns.name_editable);
186         name_column->set_sizing(TREE_VIEW_COLUMN_FIXED);
187         name_column->set_expand(true);
188         name_column->set_min_width(50);
189
190         name_cell->property_editable() = true;
191         name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRoutes::name_edit));
192
193         // Set the visible column cell renderer to radio toggle
194         CellRendererToggle* visible_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (6));
195
196         visible_cell->property_activatable() = true;
197         visible_cell->property_radio() = false;
198         visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRoutes::visible_changed));
199         
200         TreeViewColumn* visible_col = dynamic_cast<TreeViewColumn*> (_display.get_column (6));
201         visible_col->set_expand(false);
202         visible_col->set_sizing(TREE_VIEW_COLUMN_FIXED);
203         visible_col->set_fixed_width(30);
204         visible_col->set_alignment(ALIGN_CENTER);
205         
206         _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
207         _model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
208         
209         _display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
210         _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRoutes::key_press), false);
211
212         _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_in), false);
213         _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRoutes::focus_out));
214
215         _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::enter_notify), false);
216         _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRoutes::leave_notify), false);
217
218         _display.set_enable_search (false);
219
220         Route::SyncOrderKeys.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::sync_order_keys, this, _1), gui_context());
221 }
222
223 bool
224 EditorRoutes::focus_in (GdkEventFocus*)
225 {
226         Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
227
228         if (win) {
229                 old_focus = win->get_focus ();
230         } else {
231                 old_focus = 0;
232         }
233
234         name_editable = 0;
235
236         /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
237         return true;
238 }
239
240 bool
241 EditorRoutes::focus_out (GdkEventFocus*)
242 {
243         if (old_focus) {
244                 old_focus->grab_focus ();
245                 old_focus = 0;
246         }
247
248         return false;
249 }
250
251 bool
252 EditorRoutes::enter_notify (GdkEventCrossing*)
253 {
254         /* arm counter so that ::selection_filter() will deny selecting anything for the 
255            next two attempts to change selection status.
256         */
257         selection_countdown = 2;
258         _scroller.grab_focus ();
259         Keyboard::magic_widget_grab_focus ();
260         return false;
261 }
262
263 bool
264 EditorRoutes::leave_notify (GdkEventCrossing*)
265 {
266         selection_countdown = 0;
267
268         if (old_focus) {
269                 old_focus->grab_focus ();
270                 old_focus = 0;
271         }
272
273         Keyboard::magic_widget_drop_focus ();
274         return false;
275 }
276
277 void
278 EditorRoutes::set_session (Session* s)
279 {
280         SessionHandlePtr::set_session (s);
281
282         initial_display ();
283
284         if (_session) {
285                 _session->SoloChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::solo_changed_so_update_mute, this), gui_context());
286                 _session->RecordStateChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
287         }
288 }
289
290 void
291 EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string)
292 {
293         // Get the model row that has been toggled.
294         Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
295
296         TimeAxisView *tv = row[_columns.tv];
297         AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
298
299         if (atv != 0 && atv->is_audio_track()){
300                 boost::shared_ptr<RouteList> rl (new RouteList);
301                 rl->push_back (atv->route());
302                 _session->set_record_enabled (rl, !atv->track()->record_enabled(), Session::rt_cleanup);
303         }
304 }
305
306 void
307 EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string)
308 {
309         // Get the model row that has been toggled.
310         Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
311
312         TimeAxisView *tv = row[_columns.tv];
313         RouteTimeAxisView *rtv = dynamic_cast<RouteTimeAxisView*> (tv);
314         
315         if (rtv != 0) {
316                 boost::shared_ptr<RouteList> rl (new RouteList);
317                 rl->push_back (rtv->route());
318                 _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup);
319         }
320 }
321
322 void
323 EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string)
324 {
325         // Get the model row that has been toggled.
326         Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
327
328         TimeAxisView *tv = row[_columns.tv];
329         AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
330
331         if (atv != 0) {
332                 boost::shared_ptr<RouteList> rl (new RouteList);
333                 rl->push_back (atv->route());
334                 _session->set_solo (rl, !atv->route()->soloed(), Session::rt_cleanup);
335         }
336 }
337
338 void
339 EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string)
340 {
341         // Get the model row that has been toggled.
342         Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
343
344         TimeAxisView *tv = row[_columns.tv];
345         AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
346
347         if (atv != 0) {
348                 atv->route()->set_solo_isolated (!atv->route()->solo_isolated(), this);
349         }
350 }
351
352 void
353 EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string)
354 {
355         // Get the model row that has been toggled.
356         Gtk::TreeModel::Row row = *_model->get_iter (Gtk::TreeModel::Path (path_string));
357
358         TimeAxisView *tv = row[_columns.tv];
359         AudioTimeAxisView *atv = dynamic_cast<AudioTimeAxisView*> (tv);
360
361         if (atv != 0) {
362                 atv->route()->set_solo_safe (!atv->route()->solo_safe(), this);
363         }
364 }
365
366 void
367 EditorRoutes::build_menu ()
368 {
369         using namespace Menu_Helpers;
370         using namespace Gtk;
371
372         _menu = new Menu;
373
374         MenuList& items = _menu->items();
375         _menu->set_name ("ArdourContextMenu");
376
377         items.push_back (MenuElem (_("Show All"), sigc::mem_fun (*this, &EditorRoutes::show_all_routes)));
378         items.push_back (MenuElem (_("Hide All"), sigc::mem_fun (*this, &EditorRoutes::hide_all_routes)));
379         items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiotracks)));
380         items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiotracks)));
381         items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::show_all_audiobus)));
382         items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun (*this, &EditorRoutes::hide_all_audiobus)));
383         items.push_back (MenuElem (_("Show All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::show_all_miditracks)));
384         items.push_back (MenuElem (_("Hide All Midi Tracks"), sigc::mem_fun (*this, &EditorRoutes::hide_all_miditracks)));
385         items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), sigc::mem_fun (*this, &EditorRoutes::show_tracks_with_regions_at_playhead)));
386 }
387
388 void
389 EditorRoutes::show_menu ()
390 {
391         if (_menu == 0) {
392                 build_menu ();
393         }
394
395         _menu->popup (1, gtk_get_current_event_time());
396 }
397
398 void
399 EditorRoutes::redisplay ()
400 {
401         if (_no_redisplay || !_session) {
402                 return;
403         }
404
405         TreeModel::Children rows = _model->children();
406         TreeModel::Children::iterator i;
407         uint32_t position;
408         int n;
409
410         for (n = 0, position = 0, i = rows.begin(); i != rows.end(); ++i) {
411                 TimeAxisView *tv = (*i)[_columns.tv];
412                 boost::shared_ptr<Route> route = (*i)[_columns.route];
413
414                 if (tv == 0) {
415                         // just a "title" row
416                         continue;
417                 }
418
419                 if (!_redisplay_does_not_reset_order_keys) {
420                         /* this reorder is caused by user action, so reassign sort order keys
421                            to tracks.
422                         */
423                         route->set_order_key (N_ ("editor"), n);
424                 }
425
426                 bool visible = (*i)[_columns.visible];
427
428                 /* show or hide the TimeAxisView */
429                 if (visible) {
430                         tv->set_marked_for_display (true);
431                         position += tv->show_at (position, n, &_editor->edit_controls_vbox);
432                         tv->clip_to_viewport ();
433                 } else {
434                         tv->set_marked_for_display (false);
435                         tv->hide ();
436                 }
437
438                 n++;
439         }
440
441         /* whenever we go idle, update the track view list to reflect the new order.
442            we can't do this here, because we could mess up something that is traversing
443            the track order and has caused a redisplay of the list.
444         */
445         Glib::signal_idle().connect (sigc::mem_fun (*_editor, &Editor::sync_track_view_list_and_routes));
446
447         _editor->full_canvas_height = position + _editor->canvas_timebars_vsize;
448         _editor->vertical_adjustment.set_upper (_editor->full_canvas_height);
449
450         if ((_editor->vertical_adjustment.get_value() + _editor->_canvas_height) > _editor->vertical_adjustment.get_upper()) {
451                 /*
452                    We're increasing the size of the canvas while the bottom is visible.
453                    We scroll down to keep in step with the controls layout.
454                 */
455                 _editor->vertical_adjustment.set_value (_editor->full_canvas_height - _editor->_canvas_height);
456         }
457
458         if (!_redisplay_does_not_reset_order_keys && !_redisplay_does_not_sync_order_keys) {
459                 _session->sync_order_keys (N_ ("editor"));
460         }
461 }
462
463 void
464 EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
465 {
466         if (!_session || _session->deletion_in_progress()) {
467                 return;
468         }
469                 
470         /* this could require an order reset & sync */
471         _session->set_remote_control_ids();
472         _ignore_reorder = true;
473         redisplay ();
474         _ignore_reorder = false;
475 }
476
477 void
478 EditorRoutes::visible_changed (std::string const & path)
479 {
480         if (_session && _session->deletion_in_progress()) {
481                 return;
482         }
483
484         TreeIter iter;
485
486         if ((iter = _model->get_iter (path))) {
487                 TimeAxisView* tv = (*iter)[_columns.tv];
488                 if (tv) {
489                         bool visible = (*iter)[_columns.visible];
490                         (*iter)[_columns.visible] = !visible;
491                 }
492         }
493
494         _redisplay_does_not_reset_order_keys = true;
495         _session->set_remote_control_ids();
496         redisplay ();
497         _redisplay_does_not_reset_order_keys = false;
498 }
499
500 void
501 EditorRoutes::routes_added (list<RouteTimeAxisView*> routes)
502 {
503         TreeModel::Row row;
504
505         _redisplay_does_not_sync_order_keys = true;
506         suspend_redisplay ();
507
508         for (list<RouteTimeAxisView*>::iterator x = routes.begin(); x != routes.end(); ++x) {
509
510                 row = *(_model->append ());
511
512                 row[_columns.text] = (*x)->route()->name();
513                 row[_columns.visible] = (*x)->marked_for_display();
514                 row[_columns.tv] = *x;
515                 row[_columns.route] = (*x)->route ();
516                 row[_columns.is_track] = (boost::dynamic_pointer_cast<Track> ((*x)->route()) != 0);
517                 row[_columns.mute_state] = (*x)->route()->muted();
518                 row[_columns.solo_state] = (*x)->route()->soloed();
519                 row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated();
520                 row[_columns.solo_safe_state] = (*x)->route()->solo_safe();
521                 row[_columns.name_editable] = true;
522
523                 _ignore_reorder = true;
524
525                 /* added a new fresh one at the end */
526                 if ((*x)->route()->order_key (N_ ("editor")) == -1) {
527                         (*x)->route()->set_order_key (N_ ("editor"), _model->children().size()-1);
528                 }
529
530                 _ignore_reorder = false;
531
532                 boost::weak_ptr<Route> wr ((*x)->route());
533
534                 (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context());
535                 (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context());
536
537                 if ((*x)->is_track()) {
538                         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> ((*x)->route());
539                         t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
540                 }
541
542                 if ((*x)->is_midi_track()) {
543                         boost::shared_ptr<MidiTrack> t = boost::dynamic_pointer_cast<MidiTrack> ((*x)->route());
544                         t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context());
545                 }
546
547                 (*x)->route()->mute_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context());
548                 (*x)->route()->solo_changed.connect (*this, MISSING_INVALIDATOR, ui_bind (&EditorRoutes::update_solo_display, this, _1), gui_context());
549                 (*x)->route()->solo_isolated_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context());
550                 (*x)->route()->solo_safe_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context());
551         }
552
553         update_rec_display ();
554         update_mute_display ();
555         update_solo_display (true);
556         update_solo_isolate_display ();
557         update_solo_safe_display ();
558         resume_redisplay ();
559         _redisplay_does_not_sync_order_keys = false;
560 }
561
562 void
563 EditorRoutes::handle_gui_changes (string const & what, void*)
564 {
565         ENSURE_GUI_THREAD (*this, &EditorRoutes::handle_gui_changes, what, src)
566
567         if (what == "track_height") {
568                 /* Optional :make tracks change height while it happens, instead
569                    of on first-idle
570                 */
571                 //update_canvas_now ();
572                 redisplay ();
573         }
574
575         if (what == "visible_tracks") {
576                 redisplay ();
577         }
578 }
579
580 void
581 EditorRoutes::route_removed (TimeAxisView *tv)
582 {
583         ENSURE_GUI_THREAD (*this, &EditorRoutes::route_removed, tv)
584
585         TreeModel::Children rows = _model->children();
586         TreeModel::Children::iterator ri;
587
588         /* the core model has changed, there is no need to sync
589            view orders.
590         */
591
592         _redisplay_does_not_sync_order_keys = true;
593
594         for (ri = rows.begin(); ri != rows.end(); ++ri) {
595                 if ((*ri)[_columns.tv] == tv) {
596                         _model->erase (ri);
597                         break;
598                 }
599         }
600
601         _redisplay_does_not_sync_order_keys = false;
602 }
603
604 void
605 EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr<Route> r)
606 {
607         if (!what_changed.contains (ARDOUR::Properties::name)) {
608                 return;
609         }
610
611         ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r)
612
613         boost::shared_ptr<Route> route = r.lock ();
614
615         if (!route) {
616                 return;
617         }
618
619         TreeModel::Children rows = _model->children();
620         TreeModel::Children::iterator i;
621
622         for (i = rows.begin(); i != rows.end(); ++i) {
623                 boost::shared_ptr<Route> t = (*i)[_columns.route];
624                 if (t == route) {
625                         (*i)[_columns.text] = route->name();
626                         break;
627                 }
628         }
629 }
630
631 void
632 EditorRoutes::update_visibility ()
633 {
634         TreeModel::Children rows = _model->children();
635         TreeModel::Children::iterator i;
636
637         suspend_redisplay ();
638
639         for (i = rows.begin(); i != rows.end(); ++i) {
640                 TimeAxisView *tv = (*i)[_columns.tv];
641                 (*i)[_columns.visible] = tv->marked_for_display ();
642                 cerr << "marked " << tv->name() << " for display = " << tv->marked_for_display() << endl;
643         }
644
645         resume_redisplay ();
646 }
647
648 void
649 EditorRoutes::hide_track_in_display (TimeAxisView& tv)
650 {
651         TreeModel::Children rows = _model->children();
652         TreeModel::Children::iterator i;
653
654         for (i = rows.begin(); i != rows.end(); ++i) {
655                 if ((*i)[_columns.tv] == &tv) {
656                         (*i)[_columns.visible] = false;
657                         break;
658                 }
659         }
660
661         redisplay ();
662 }
663
664 void
665 EditorRoutes::show_track_in_display (TimeAxisView& tv)
666 {
667         TreeModel::Children rows = _model->children();
668         TreeModel::Children::iterator i;
669
670         for (i = rows.begin(); i != rows.end(); ++i) {
671                 if ((*i)[_columns.tv] == &tv) {
672                         (*i)[_columns.visible] = true;
673                         break;
674                 }
675         }
676
677         redisplay ();
678 }
679
680 void
681 EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
682 {
683         redisplay ();
684 }
685
686 /** If src != "editor", take editor order keys from each route and use them to rearrange the
687  *  route list so that the visual arrangement of routes matches the order keys from the routes.
688  */
689 void
690 EditorRoutes::sync_order_keys (string const & src)
691 {
692         vector<int> neworder;
693         TreeModel::Children rows = _model->children();
694         TreeModel::Children::iterator ri;
695
696         if (src == N_ ("editor") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
697                 return;
698         }
699
700         for (ri = rows.begin(); ri != rows.end(); ++ri) {
701                 neworder.push_back (0);
702         }
703
704         bool changed = false;
705         int order;
706
707         for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
708                 boost::shared_ptr<Route> route = (*ri)[_columns.route];
709
710                 int old_key = order;
711                 int new_key = route->order_key (N_ ("editor"));
712
713                 neworder[new_key] = old_key;
714
715                 if (new_key != old_key) {
716                         changed = true;
717                 }
718         }
719
720         if (changed) {
721                 _redisplay_does_not_reset_order_keys = true;
722                 _model->reorder (neworder);
723                 _redisplay_does_not_reset_order_keys = false;
724         }
725 }
726
727
728 void
729 EditorRoutes::hide_all_tracks (bool /*with_select*/)
730 {
731         TreeModel::Children rows = _model->children();
732         TreeModel::Children::iterator i;
733
734         suspend_redisplay ();
735
736         for (i = rows.begin(); i != rows.end(); ++i) {
737
738                 TreeModel::Row row = (*i);
739                 TimeAxisView *tv = row[_columns.tv];
740
741                 if (tv == 0) {
742                         continue;
743                 }
744
745                 row[_columns.visible] = false;
746         }
747
748         resume_redisplay ();
749
750         /* XXX this seems like a hack and half, but its not clear where to put this
751            otherwise.
752         */
753
754         //reset_scrolling_region ();
755 }
756
757 void
758 EditorRoutes::set_all_tracks_visibility (bool yn)
759 {
760         TreeModel::Children rows = _model->children();
761         TreeModel::Children::iterator i;
762
763         suspend_redisplay ();
764
765         for (i = rows.begin(); i != rows.end(); ++i) {
766
767                 TreeModel::Row row = (*i);
768                 TimeAxisView* tv = row[_columns.tv];
769
770                 if (tv == 0) {
771                         continue;
772                 }
773
774                 (*i)[_columns.visible] = yn;
775         }
776
777         resume_redisplay ();
778 }
779
780 void
781 EditorRoutes::set_all_audio_midi_visibility (int tracks, bool yn)
782 {
783         TreeModel::Children rows = _model->children();
784         TreeModel::Children::iterator i;
785
786         suspend_redisplay ();
787
788         for (i = rows.begin(); i != rows.end(); ++i) {
789           
790                 TreeModel::Row row = (*i);
791                 TimeAxisView* tv = row[_columns.tv];
792                 
793                 AudioTimeAxisView* atv;
794                 MidiTimeAxisView* mtv;
795                 
796                 if (tv == 0) {
797                         continue;
798                 }
799
800                 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
801                         switch (tracks) {
802                         case 0:
803                                 (*i)[_columns.visible] = yn;
804                                 break;
805
806                         case 1:
807                                 if (atv->is_audio_track()) {
808                                         (*i)[_columns.visible] = yn;
809                                 }
810                                 break;
811
812                         case 2:
813                                 if (!atv->is_audio_track()) {
814                                         (*i)[_columns.visible] = yn;
815                                 }
816                                 break;
817                         }
818                 }
819                 else if ((mtv = dynamic_cast<MidiTimeAxisView*>(tv)) != 0) {
820                         switch (tracks) {
821                         case 0:
822                                 (*i)[_columns.visible] = yn;
823                                 break;
824
825                         case 3:
826                                 if (mtv->is_midi_track()) {
827                                         (*i)[_columns.visible] = yn;
828                                 }
829                                 break;
830                         }
831                 }
832         }
833
834         resume_redisplay ();
835 }
836
837 void
838 EditorRoutes::hide_all_routes ()
839 {
840         set_all_tracks_visibility (false);
841 }
842
843 void
844 EditorRoutes::show_all_routes ()
845 {
846         set_all_tracks_visibility (true);
847 }
848
849 void
850 EditorRoutes::show_all_audiotracks()
851 {
852         set_all_audio_midi_visibility (1, true);
853 }
854 void
855 EditorRoutes::hide_all_audiotracks ()
856 {
857         set_all_audio_midi_visibility (1, false);
858 }
859
860 void
861 EditorRoutes::show_all_audiobus ()
862 {
863         set_all_audio_midi_visibility (2, true);
864 }
865 void
866 EditorRoutes::hide_all_audiobus ()
867 {
868         set_all_audio_midi_visibility (2, false);
869 }
870
871 void
872 EditorRoutes::show_all_miditracks()
873 {
874         set_all_audio_midi_visibility (3, true);
875 }
876 void
877 EditorRoutes::hide_all_miditracks ()
878 {
879         set_all_audio_midi_visibility (3, false);
880 }
881
882 bool
883 EditorRoutes::key_press (GdkEventKey* ev)
884 {
885         TreeViewColumn *col;
886         boost::shared_ptr<RouteList> rl (new RouteList);
887         TreePath path;
888
889         switch (ev->keyval) {
890         case GDK_Tab:
891         case GDK_ISO_Left_Tab:
892
893                 /* If we appear to be editing something, leave that cleanly and appropriately.
894                 */
895                 if (name_editable) {
896                         name_editable->editing_done ();
897                         name_editable = 0;
898                 } 
899
900                 col = _display.get_column (5); // select&focus on name column
901
902                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
903                         treeview_select_previous (_display, _model, col);
904                 } else {
905                         treeview_select_next (_display, _model, col);
906                 }
907
908                 return true;
909                 break;
910
911         case 'm':
912                 if (get_relevant_routes (rl)) {
913                         _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup);
914                 }
915                 return true;
916                 break;
917
918         case 's':
919                 if (get_relevant_routes (rl)) {
920                         _session->set_solo (rl, !rl->front()->soloed(), Session::rt_cleanup);
921                 }
922                 return true;
923                 break;
924
925         case 'r':
926                 if (get_relevant_routes (rl)) {
927                         _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup);
928                 }
929                 break;
930
931         default:
932                 break;
933         }
934
935         return false;
936 }
937
938 bool
939 EditorRoutes::get_relevant_routes (boost::shared_ptr<RouteList> rl)
940 {
941         TimeAxisView* tv;
942         RouteTimeAxisView* rtv;
943         RefPtr<TreeSelection> selection = _display.get_selection();
944         TreePath path;
945         TreeIter iter;
946
947         if (selection->count_selected_rows() != 0) {
948
949                 /* use selection */
950
951                 RefPtr<TreeModel> tm = RefPtr<TreeModel>::cast_dynamic (_model);
952                 iter = selection->get_selected (tm);
953
954         } else {
955                 /* use mouse pointer */
956
957                 int x, y;
958                 int bx, by;
959
960                 _display.get_pointer (x, y);
961                 _display.convert_widget_to_bin_window_coords (x, y, bx, by);
962
963                 if (_display.get_path_at_pos (bx, by, path)) {
964                         iter = _model->get_iter (path);
965                 }
966         }
967
968         if (iter) {
969                 tv = (*iter)[_columns.tv];
970                 if (tv) {
971                         rtv = dynamic_cast<RouteTimeAxisView*>(tv);
972                         if (rtv) {
973                                 rl->push_back (rtv->route());
974                         }
975                 }
976         }
977
978         return !rl->empty();
979 }
980
981 bool
982 EditorRoutes::button_press (GdkEventButton* ev)
983 {
984         if (Keyboard::is_context_menu_event (ev)) {
985                 show_menu ();
986                 return true;
987         }
988         
989         //Scroll editor canvas to selected track
990         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
991                 
992                 TreeModel::Path path;
993                 TreeViewColumn *tvc;
994                 int cell_x;
995                 int cell_y;
996                 
997                 _display.get_path_at_pos ((int) ev->x, (int) ev->y, path, tvc, cell_x, cell_y);
998
999                 // Get the model row.
1000                 Gtk::TreeModel::Row row = *_model->get_iter (path);
1001                 
1002                 TimeAxisView *tv = row[_columns.tv];
1003                 
1004                 int y_pos = tv->y_position();
1005                 
1006                 //Clamp the y pos so that we do not extend beyond the canvas full height.
1007                 if (_editor->full_canvas_height - y_pos < _editor->_canvas_height){
1008                     y_pos = _editor->full_canvas_height - _editor->_canvas_height;
1009                 }
1010                 
1011                 //Only scroll to if the track is visible
1012                 if(y_pos != -1){
1013                     _editor->reset_y_origin (y_pos);
1014                 }
1015         }
1016         
1017         return false;
1018 }
1019
1020 bool
1021 EditorRoutes::selection_filter (Glib::RefPtr<TreeModel> const &, TreeModel::Path const&, bool /*selected*/)
1022 {
1023         if (selection_countdown) {
1024                 if (--selection_countdown == 0) {
1025                         return true;
1026                 } else {
1027                         /* no selection yet ... */
1028                         return false;
1029                 }
1030         }
1031         return true;
1032 }
1033
1034 struct EditorOrderRouteSorter {
1035     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1036             /* use of ">" forces the correct sort order */
1037             return a->order_key (N_ ("editor")) < b->order_key (N_ ("editor"));
1038     }
1039 };
1040
1041 void
1042 EditorRoutes::initial_display ()
1043 {
1044         suspend_redisplay ();
1045         _model->clear ();
1046
1047         if (!_session) {
1048                 resume_redisplay ();
1049                 return;
1050         }
1051
1052         boost::shared_ptr<RouteList> routes = _session->get_routes();
1053         RouteList r (*routes);
1054         EditorOrderRouteSorter sorter;
1055
1056         r.sort (sorter);
1057         _editor->handle_new_route (r);
1058
1059         /* don't show master bus in a new session */
1060
1061         if (ARDOUR_UI::instance()->session_is_new ()) {
1062
1063                 TreeModel::Children rows = _model->children();
1064                 TreeModel::Children::iterator i;
1065
1066                 _no_redisplay = true;
1067
1068                 for (i = rows.begin(); i != rows.end(); ++i) {
1069
1070                         TimeAxisView *tv =  (*i)[_columns.tv];
1071                         RouteTimeAxisView *rtv;
1072
1073                         if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1074                                 if (rtv->route()->is_master()) {
1075                                         _display.get_selection()->unselect (i);
1076                                 }
1077                         }
1078                 }
1079
1080                 _no_redisplay = false;
1081                 redisplay ();
1082         }
1083
1084         resume_redisplay ();
1085 }
1086
1087 void
1088 EditorRoutes::track_list_reorder (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int* /*new_order*/)
1089 {
1090         _redisplay_does_not_sync_order_keys = true;
1091         _session->set_remote_control_ids();
1092         redisplay ();
1093         _redisplay_does_not_sync_order_keys = false;
1094 }
1095
1096 void
1097 EditorRoutes::display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1098                                              int x, int y,
1099                                              const SelectionData& data,
1100                                              guint info, guint time)
1101 {
1102         if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1103                 _display.on_drag_data_received (context, x, y, data, info, time);
1104                 return;
1105         }
1106
1107         context->drag_finish (true, false, time);
1108 }
1109
1110 void
1111 EditorRoutes::move_selected_tracks (bool up)
1112 {
1113         if (_editor->selection->tracks.empty()) {
1114                 return;
1115         }
1116
1117         typedef std::pair<TimeAxisView*,boost::shared_ptr<Route> > ViewRoute;
1118         std::list<ViewRoute> view_routes;
1119         std::vector<int> neworder;
1120         TreeModel::Children rows = _model->children();
1121         TreeModel::Children::iterator ri;
1122
1123         for (ri = rows.begin(); ri != rows.end(); ++ri) {
1124                 TimeAxisView* tv = (*ri)[_columns.tv];
1125                 boost::shared_ptr<Route> route = (*ri)[_columns.route];
1126
1127                 view_routes.push_back (ViewRoute (tv, route));
1128         }
1129
1130         list<ViewRoute>::iterator trailing;
1131         list<ViewRoute>::iterator leading;
1132
1133         if (up) {
1134
1135                 trailing = view_routes.begin();
1136                 leading = view_routes.begin();
1137
1138                 ++leading;
1139
1140                 while (leading != view_routes.end()) {
1141                         if (_editor->selection->selected (leading->first)) {
1142                                 view_routes.insert (trailing, ViewRoute (leading->first, leading->second));
1143                                 leading = view_routes.erase (leading);
1144                         } else {
1145                                 ++leading;
1146                                 ++trailing;
1147                         }
1148                 }
1149
1150         } else {
1151
1152                 /* if we could use reverse_iterator in list::insert, this code
1153                    would be a beautiful reflection of the code above. but we can't
1154                    and so it looks like a bit of a mess.
1155                 */
1156
1157                 trailing = view_routes.end();
1158                 leading = view_routes.end();
1159
1160                 --leading; if (leading == view_routes.begin()) { return; }
1161                 --leading;
1162                 --trailing;
1163
1164                 while (1) {
1165
1166                         if (_editor->selection->selected (leading->first)) {
1167                                 list<ViewRoute>::iterator tmp;
1168
1169                                 /* need to insert *after* trailing, not *before* it,
1170                                    which is what insert (iter, val) normally does.
1171                                 */
1172
1173                                 tmp = trailing;
1174                                 tmp++;
1175
1176                                 view_routes.insert (tmp, ViewRoute (leading->first, leading->second));
1177
1178                                 /* can't use iter = cont.erase (iter); form here, because
1179                                    we need iter to move backwards.
1180                                 */
1181
1182                                 tmp = leading;
1183                                 --tmp;
1184
1185                                 bool done = false;
1186
1187                                 if (leading == view_routes.begin()) {
1188                                         /* the one we've just inserted somewhere else
1189                                            was the first in the list. erase this copy,
1190                                            and then break, because we're done.
1191                                         */
1192                                         done = true;
1193                                 }
1194
1195                                 view_routes.erase (leading);
1196
1197                                 if (done) {
1198                                         break;
1199                                 }
1200
1201                                 leading = tmp;
1202
1203                         } else {
1204                                 if (leading == view_routes.begin()) {
1205                                         break;
1206                                 }
1207                                 --leading;
1208                                 --trailing;
1209                         }
1210                 };
1211         }
1212
1213         for (leading = view_routes.begin(); leading != view_routes.end(); ++leading) {
1214                 neworder.push_back (leading->second->order_key (N_ ("editor")));
1215         }
1216
1217         _model->reorder (neworder);
1218
1219        _session->sync_order_keys (N_ ("editor"));
1220 }
1221
1222 void
1223 EditorRoutes::update_rec_display ()
1224 {
1225         TreeModel::Children rows = _model->children();
1226         TreeModel::Children::iterator i;
1227
1228         for (i = rows.begin(); i != rows.end(); ++i) {
1229                 boost::shared_ptr<Route> route = (*i)[_columns.route];
1230
1231                 if (boost::dynamic_pointer_cast<Track> (route)) {
1232                         boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
1233
1234                         if (route->record_enabled()) {
1235                                 if (_session->record_status() == Session::Recording) {
1236                                         (*i)[_columns.rec_state] = 1;
1237                                 } else {
1238                                         (*i)[_columns.rec_state] = 2;
1239                                 }
1240                         } else if (mt && mt->step_editing()) {
1241                                 (*i)[_columns.rec_state] = 3;
1242                         } else {
1243                                 (*i)[_columns.rec_state] = 0;
1244                         }
1245                 
1246                         (*i)[_columns.name_editable] = !route->record_enabled ();
1247                 }
1248         }
1249 }
1250
1251 void
1252 EditorRoutes::update_mute_display ()
1253 {
1254         TreeModel::Children rows = _model->children();
1255         TreeModel::Children::iterator i;
1256
1257         for (i = rows.begin(); i != rows.end(); ++i) {
1258                 boost::shared_ptr<Route> route = (*i)[_columns.route];
1259                 (*i)[_columns.mute_state] = RouteUI::mute_visual_state (_session, route);
1260         }
1261 }
1262
1263 void
1264 EditorRoutes::update_solo_display (bool /* selfsoloed */)
1265 {
1266         TreeModel::Children rows = _model->children();
1267         TreeModel::Children::iterator i;
1268
1269         for (i = rows.begin(); i != rows.end(); ++i) {
1270                 boost::shared_ptr<Route> route = (*i)[_columns.route];
1271                 (*i)[_columns.solo_state] = RouteUI::solo_visual_state (route);
1272         }
1273 }
1274
1275 void
1276 EditorRoutes::update_solo_isolate_display ()
1277 {
1278         TreeModel::Children rows = _model->children();
1279         TreeModel::Children::iterator i;
1280
1281         for (i = rows.begin(); i != rows.end(); ++i) {
1282                 boost::shared_ptr<Route> route = (*i)[_columns.route];
1283                 (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_visual_state (route) > 0 ? 1 : 0;
1284         }
1285 }
1286
1287 void
1288 EditorRoutes::update_solo_safe_display ()
1289 {
1290         TreeModel::Children rows = _model->children();
1291         TreeModel::Children::iterator i;
1292
1293         for (i = rows.begin(); i != rows.end(); ++i) {
1294                 boost::shared_ptr<Route> route = (*i)[_columns.route];
1295                 (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_visual_state (route) > 0 ? 1 : 0;
1296         }
1297 }
1298
1299 list<TimeAxisView*>
1300 EditorRoutes::views () const
1301 {
1302         list<TimeAxisView*> v;
1303         for (TreeModel::Children::iterator i = _model->children().begin(); i != _model->children().end(); ++i) {
1304                 v.push_back ((*i)[_columns.tv]);
1305         }
1306
1307         return v;
1308 }
1309
1310 void
1311 EditorRoutes::clear ()
1312 {
1313         _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1314         _model->clear ();
1315         _display.set_model (_model);
1316 }
1317
1318 void
1319 EditorRoutes::name_edit_started (CellEditable* ce, const Glib::ustring&)
1320 {
1321         name_editable = ce;
1322
1323         /* give it a special name */
1324
1325         Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
1326
1327         if (e) {
1328                 e->set_name (X_("RouteNameEditorEntry"));
1329         }
1330 }
1331
1332 void
1333 EditorRoutes::name_edit (std::string const & path, std::string const & new_text)
1334 {
1335         name_editable = 0;
1336
1337         TreeIter iter = _model->get_iter (path);
1338
1339         if (!iter) {
1340                 return;
1341         }
1342
1343         boost::shared_ptr<Route> route = (*iter)[_columns.route];
1344
1345         if (route && route->name() != new_text) {
1346                 route->set_name (new_text);
1347         }
1348 }
1349
1350 void
1351 EditorRoutes::solo_changed_so_update_mute ()
1352 {
1353         update_mute_display ();
1354 }
1355
1356 void
1357 EditorRoutes::show_tracks_with_regions_at_playhead ()
1358 {
1359         boost::shared_ptr<RouteList> const r = _session->get_routes_with_regions_at (_session->transport_frame ());
1360
1361         set<TimeAxisView*> show;
1362         for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
1363                 TimeAxisView* tav = _editor->axis_view_from_route (*i);
1364                 if (tav) {
1365                         show.insert (tav);
1366                 }
1367         }
1368
1369         suspend_redisplay ();
1370         
1371         TreeModel::Children rows = _model->children ();
1372         for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
1373                 TimeAxisView* tv = (*i)[_columns.tv];
1374                 (*i)[_columns.visible] = (show.find (tv) != show.end());
1375         }
1376
1377         resume_redisplay ();
1378 }