make all TimeAxisView derived types return PresentationInfo for sorting
[ardour.git] / gtk2_ardour / route_time_axis.cc
1 /*
2     Copyright (C) 2006 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 #include <cstdlib>
20 #include <cmath>
21 #include <cassert>
22
23 #include <algorithm>
24 #include <string>
25 #include <vector>
26 #include <map>
27 #include <utility>
28
29 #include <sigc++/bind.h>
30
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
37
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
44
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/pannable.h"
49 #include "ardour/panner.h"
50 #include "ardour/processor.h"
51 #include "ardour/profile.h"
52 #include "ardour/route_group.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlists.h"
55
56 #include "evoral/Parameter.hpp"
57
58 #include "canvas/debug.h"
59
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "audio_streamview.h"
63 #include "debug.h"
64 #include "route_time_axis.h"
65 #include "automation_time_axis.h"
66 #include "enums.h"
67 #include "gui_thread.h"
68 #include "item_counts.h"
69 #include "keyboard.h"
70 #include "paste_context.h"
71 #include "playlist_selector.h"
72 #include "point_selection.h"
73 #include "prompter.h"
74 #include "public_editor.h"
75 #include "region_view.h"
76 #include "rgb_macros.h"
77 #include "selection.h"
78 #include "streamview.h"
79 #include "tooltips.h"
80 #include "ui_config.h"
81 #include "utils.h"
82 #include "route_group_menu.h"
83
84 #include "ardour/track.h"
85
86 #include "i18n.h"
87
88 using namespace ARDOUR;
89 using namespace ARDOUR_UI_UTILS;
90 using namespace PBD;
91 using namespace Gtkmm2ext;
92 using namespace Gtk;
93 using namespace Editing;
94 using namespace std;
95 using std::list;
96
97 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
98         : AxisView(sess)
99         , RouteUI(sess)
100         , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
101         , _view (0)
102         , parent_canvas (canvas)
103         , no_redraw (false)
104         , button_table (3, 3)
105         , route_group_button (S_("RTAV|G"))
106         , playlist_button (S_("RTAV|P"))
107         , automation_button (S_("RTAV|A"))
108         , automation_action_menu (0)
109         , plugins_submenu_item (0)
110         , route_group_menu (0)
111         , playlist_action_menu (0)
112         , mode_menu (0)
113         , color_mode_menu (0)
114         , gm (sess, true, 75, 14)
115         , _ignore_set_layer_display (false)
116         , gain_automation_item(NULL)
117         , trim_automation_item(NULL)
118         , mute_automation_item(NULL)
119         , pan_automation_item(NULL)
120 {
121         number_label.set_name("tracknumber label");
122         number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
123         number_label.set_alignment(.5, .5);
124         number_label.set_fallthrough_to_parent (true);
125
126         sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
127         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
128
129         parameter_changed ("editor-stereo-only-meters");
130 }
131
132 void
133 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
134 {
135         RouteUI::set_route (rt);
136
137         CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
138         CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
139         CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
140
141         int meter_width = 3;
142         if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
143                 meter_width = 6;
144         }
145         gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
146         gm.get_level_meter().set_no_show_all();
147         gm.get_level_meter().setup_meters(50, meter_width);
148         gm.update_gain_sensitive ();
149
150         string str = gui_property ("height");
151         if (!str.empty()) {
152                 set_height (atoi (str));
153         } else {
154                 set_height (preset_height (HeightNormal));
155         }
156
157         if (!_route->is_auditioner()) {
158                 if (gui_property ("visible").empty()) {
159                         set_gui_property ("visible", true);
160                 }
161         } else {
162                 set_gui_property ("visible", false);
163         }
164
165         timestretch_rect = 0;
166         no_redraw = false;
167
168         ignore_toggle = false;
169
170         route_group_button.set_name ("route button");
171         playlist_button.set_name ("route button");
172         automation_button.set_name ("route button");
173
174         route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
175         playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
176         automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
177
178         if (is_track()) {
179
180                 if (ARDOUR::Profile->get_mixbus()) {
181                         controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
182                 } else {
183                         controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
184                 }
185
186                 if (is_midi_track()) {
187                         set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
188                         gm.set_fader_name ("MidiTrackFader");
189                 } else {
190                         set_tooltip(*rec_enable_button, _("Record"));
191                         gm.set_fader_name ("AudioTrackFader");
192                 }
193
194                 /* set playlist button tip to the current playlist, and make it update when it changes */
195                 update_playlist_tip ();
196                 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
197
198         } else {
199                 gm.set_fader_name ("AudioBusFader");
200                 Gtk::Fixed *blank = manage(new Gtk::Fixed());
201                 controls_button_size_group->add_widget(*blank);
202                 if (ARDOUR::Profile->get_mixbus() ) {
203                         controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
204                 } else {
205                         controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
206                 }
207                 blank->show();
208         }
209
210         top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
211
212         if (!ARDOUR::Profile->get_mixbus()) {
213                 controls_meters_size_group->add_widget (gm.get_level_meter());
214         }
215
216         _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
217         _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
218         _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
219         _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
220
221         if (ARDOUR::Profile->get_mixbus()) {
222                 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
223         } else {
224                 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
225         }
226         // mute button is always present, it is used to
227         // force the 'blank' placeholders to the proper size
228         controls_button_size_group->add_widget(*mute_button);
229
230         if (!_route->is_master()) {
231                 if (ARDOUR::Profile->get_mixbus()) {
232                         controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233                 } else {
234                         controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
235                 }
236         } else {
237                 Gtk::Fixed *blank = manage(new Gtk::Fixed());
238                 controls_button_size_group->add_widget(*blank);
239                 if (ARDOUR::Profile->get_mixbus()) {
240                         controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241                 } else {
242                         controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
243                 }
244                 blank->show();
245         }
246
247         if (ARDOUR::Profile->get_mixbus()) {
248                 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249                 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
250         }
251         else if (!ARDOUR::Profile->get_trx()) {
252                 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
253                 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
254         }
255
256         set_tooltip(*solo_button,_("Solo"));
257         set_tooltip(*mute_button,_("Mute"));
258         set_tooltip(route_group_button, _("Route Group"));
259
260         mute_button->set_tweaks(ArdourButton::TrackHeader);
261         solo_button->set_tweaks(ArdourButton::TrackHeader);
262         rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
263         playlist_button.set_tweaks(ArdourButton::TrackHeader);
264         automation_button.set_tweaks(ArdourButton::TrackHeader);
265         route_group_button.set_tweaks(ArdourButton::TrackHeader);
266
267         if (is_midi_track()) {
268                 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
269         } else {
270                 set_tooltip(automation_button, _("Automation"));
271         }
272
273         update_track_number_visibility();
274         label_view ();
275
276         if (ARDOUR::Profile->get_mixbus()) {
277                 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
278         }
279         else if (!ARDOUR::Profile->get_trx()) {
280                 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
281         }
282
283         if (is_track() && track()->mode() == ARDOUR::Normal) {
284                 if (ARDOUR::Profile->get_mixbus()) {
285                         controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286                 }
287                 else if (!ARDOUR::Profile->get_trx()) {
288                         controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
289                 }
290         }
291
292         _y_position = -1;
293
294         _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
295         _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
296
297         if (is_track()) {
298
299                 str = gui_property ("layer-display");
300                 if (!str.empty()) {
301                         set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
302                 }
303
304                 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
305                 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
306
307                 /* pick up the correct freeze state */
308                 map_frozen ();
309
310         }
311
312         _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
313         UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
314
315         PropertyList* plist = new PropertyList();
316
317         plist->add (ARDOUR::Properties::mute, true);
318         plist->add (ARDOUR::Properties::solo, true);
319
320         route_group_menu = new RouteGroupMenu (_session, plist);
321
322         gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
323 }
324
325 RouteTimeAxisView::~RouteTimeAxisView ()
326 {
327         cleanup_gui_properties ();
328
329         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
330                 delete *i;
331         }
332
333         delete playlist_action_menu;
334         playlist_action_menu = 0;
335
336         delete _view;
337         _view = 0;
338
339         _automation_tracks.clear ();
340
341         delete route_group_menu;
342         CatchDeletion (this);
343 }
344
345 void
346 RouteTimeAxisView::post_construct ()
347 {
348         /* map current state of the route */
349
350         update_diskstream_display ();
351         setup_processor_menu_and_curves ();
352         reset_processor_automation_curves ();
353 }
354
355 /** Set up the processor menu for the current set of processors, and
356  *  display automation curves for any parameters which have data.
357  */
358 void
359 RouteTimeAxisView::setup_processor_menu_and_curves ()
360 {
361         _subplugin_menu_map.clear ();
362         subplugin_menu.items().clear ();
363         _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
364         _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
365 }
366
367 gint
368 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
369 {
370         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
371                 if (_route->route_group()) {
372                         _route->route_group()->remove (_route);
373                 }
374                 return false;
375         }
376
377         WeakRouteList r;
378         r.push_back (route ());
379
380         route_group_menu->build (r);
381         route_group_menu->menu()->popup (ev->button, ev->time);
382
383         return false;
384 }
385
386 void
387 RouteTimeAxisView::playlist_changed ()
388 {
389         label_view ();
390 }
391
392 void
393 RouteTimeAxisView::label_view ()
394 {
395         string x = _route->name ();
396         if (x != name_label.get_text ()) {
397                 name_label.set_text (x);
398         }
399         const int64_t track_number = _route->track_number ();
400         if (track_number == 0) {
401                 number_label.set_text ("");
402         } else {
403                 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
404         }
405 }
406
407 void
408 RouteTimeAxisView::update_track_number_visibility ()
409 {
410         DisplaySuspender ds;
411         bool show_label = _session->config.get_track_name_number();
412
413         if (_route && _route->is_master()) {
414                 show_label = false;
415         }
416
417         if (number_label.get_parent()) {
418                 controls_table.remove (number_label);
419         }
420         if (show_label) {
421                 if (ARDOUR::Profile->get_mixbus()) {
422                         controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
423                 } else {
424                         controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
425                 }
426                 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
427                 // except the width of the number label is subtracted from the name-hbox, so we
428                 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
429                 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
430                 if (tnw & 1) --tnw;
431                 number_label.set_size_request(tnw, -1);
432                 number_label.show ();
433         } else {
434                 number_label.hide ();
435         }
436 }
437
438 void
439 RouteTimeAxisView::parameter_changed (string const & p)
440 {
441         if (p == "track-name-number") {
442                 update_track_number_visibility();
443         } else if (p == "editor-stereo-only-meters") {
444                 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
445                         gm.get_level_meter().set_max_audio_meter_count (2);
446                 } else {
447                         gm.get_level_meter().set_max_audio_meter_count (0);
448                 }
449         }
450 }
451
452 void
453 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
454 {
455         if (what_changed.contains (ARDOUR::Properties::name)) {
456                 label_view ();
457         }
458 }
459
460 void
461 RouteTimeAxisView::take_name_changed (void *src)
462 {
463         if (src != this) {
464                 label_view ();
465         }
466 }
467
468 void
469 RouteTimeAxisView::playlist_click ()
470 {
471         build_playlist_menu ();
472         conditionally_add_to_selection ();
473         playlist_action_menu->popup (1, gtk_get_current_event_time());
474 }
475
476 void
477 RouteTimeAxisView::automation_click ()
478 {
479         conditionally_add_to_selection ();
480         build_automation_action_menu (false);
481         automation_action_menu->popup (1, gtk_get_current_event_time());
482 }
483
484 void
485 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
486 {
487         using namespace Menu_Helpers;
488
489         /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
490            otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
491         */
492
493         detach_menu (subplugin_menu);
494
495         _main_automation_menu_map.clear ();
496         delete automation_action_menu;
497         automation_action_menu = new Menu;
498
499         MenuList& items = automation_action_menu->items();
500
501         automation_action_menu->set_name ("ArdourContextMenu");
502
503         items.push_back (MenuElem (_("Show All Automation"),
504                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
505
506         items.push_back (MenuElem (_("Show Existing Automation"),
507                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
508
509         items.push_back (MenuElem (_("Hide All Automation"),
510                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
511
512         /* Attach the plugin submenu. It may have previously been used elsewhere,
513            so it was detached above
514         */
515
516         if (!subplugin_menu.items().empty()) {
517                 items.push_back (SeparatorElem ());
518                 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
519                 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
520         }
521
522         /* Add any route automation */
523
524         if (gain_track) {
525                 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
526                 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
527                 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
528                                                   (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
529
530                 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
531         }
532
533         if (trim_track) {
534                 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
535                 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
536                 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
537                                                   (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
538
539                 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
540         }
541
542         if (mute_track) {
543                 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
544                 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
545                 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
546                                                   (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
547
548                 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
549         }
550
551         if (!pan_tracks.empty()) {
552                 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
553                 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
554                 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
555                                                  (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
556
557                 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
558                 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
559                         _main_automation_menu_map[*p] = pan_automation_item;
560                 }
561         }
562 }
563
564 void
565 RouteTimeAxisView::build_display_menu ()
566 {
567         using namespace Menu_Helpers;
568
569         /* prepare it */
570
571         TimeAxisView::build_display_menu ();
572
573         /* now fill it with our stuff */
574
575         MenuList& items = display_menu->items();
576         display_menu->set_name ("ArdourContextMenu");
577
578         items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
579
580         items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
581
582         items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
583
584         items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
585
586         items.push_back (SeparatorElem());
587
588         if (_size_menu) {
589                 detach_menu (*_size_menu);
590         }
591         build_size_menu ();
592         items.push_back (MenuElem (_("Height"), *_size_menu));
593
594         items.push_back (SeparatorElem());
595
596         items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
597         items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
598         items.push_back (SeparatorElem());
599
600         // Hook for derived classes to add type specific stuff
601         append_extra_display_menu_items ();
602
603         if (is_track()) {
604
605                 Menu* layers_menu = manage (new Menu);
606                 MenuList &layers_items = layers_menu->items();
607                 layers_menu->set_name("ArdourContextMenu");
608
609                 RadioMenuItem::Group layers_group;
610
611                 /* Find out how many overlaid/stacked tracks we have in the selection */
612
613                 int overlaid = 0;
614                 int stacked = 0;
615                 TrackSelection const & s = _editor.get_selection().tracks;
616                 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
617                         StreamView* v = (*i)->view ();
618                         if (!v) {
619                                 continue;
620                         }
621
622                         switch (v->layer_display ()) {
623                         case Overlaid:
624                                 ++overlaid;
625                                 break;
626                         case Stacked:
627                         case Expanded:
628                                 ++stacked;
629                                 break;
630                         }
631                 }
632
633                 /* We're not connecting to signal_toggled() here; in the case where these two items are
634                    set to be in the `inconsistent' state, it seems that one or other will end up active
635                    as well as inconsistent (presumably due to the RadioMenuItem::Group).  Then when you
636                    select the active one, no toggled signal is emitted so nothing happens.
637                 */
638
639                 _ignore_set_layer_display = true;
640
641                 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
642                 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
643                 i->set_active (overlaid != 0 && stacked == 0);
644                 i->set_inconsistent (overlaid != 0 && stacked != 0);
645                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
646
647                 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
648                 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
649                 i->set_active (overlaid == 0 && stacked != 0);
650                 i->set_inconsistent (overlaid != 0 && stacked != 0);
651                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
652
653                 _ignore_set_layer_display = false;
654
655                 items.push_back (MenuElem (_("Layers"), *layers_menu));
656
657                 Menu* alignment_menu = manage (new Menu);
658                 MenuList& alignment_items = alignment_menu->items();
659                 alignment_menu->set_name ("ArdourContextMenu");
660
661                 RadioMenuItem::Group align_group;
662
663                 /* Same verbose hacks as for the layering options above */
664
665                 int existing = 0;
666                 int capture = 0;
667                 int automatic = 0;
668                 int styles = 0;
669                 boost::shared_ptr<Track> first_track;
670
671                 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
672                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
673                         if (!r || !r->is_track ()) {
674                                 continue;
675                         }
676
677                         if (!first_track) {
678                                 first_track = r->track();
679                         }
680
681                         switch (r->track()->alignment_choice()) {
682                         case Automatic:
683                                 ++automatic;
684                                 styles |= 0x1;
685                                 switch (r->track()->alignment_style()) {
686                                 case ExistingMaterial:
687                                         ++existing;
688                                         break;
689                                 case CaptureTime:
690                                         ++capture;
691                                         break;
692                                 }
693                                 break;
694                         case UseExistingMaterial:
695                                 ++existing;
696                                 styles |= 0x2;
697                                 break;
698                         case UseCaptureTime:
699                                 ++capture;
700                                 styles |= 0x4;
701                                 break;
702                         }
703                 }
704
705                 bool inconsistent;
706                 switch (styles) {
707                 case 1:
708                 case 2:
709                 case 4:
710                         inconsistent = false;
711                         break;
712                 default:
713                         inconsistent = true;
714                         break;
715                 }
716
717                 if (!inconsistent && first_track) {
718
719                         alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
720                         i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
721                         i->set_active (automatic != 0 && existing == 0 && capture == 0);
722                         i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
723
724                         switch (first_track->alignment_choice()) {
725                         case Automatic:
726                                 switch (first_track->alignment_style()) {
727                                 case ExistingMaterial:
728                                         alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
729                                         break;
730                                 case CaptureTime:
731                                         alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
732                                         break;
733                                 }
734                                 break;
735                         default:
736                                 break;
737                         }
738
739                         alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
740                         i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
741                         i->set_active (existing != 0 && capture == 0 && automatic == 0);
742                         i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
743
744                         alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
745                         i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
746                         i->set_active (existing == 0 && capture != 0 && automatic == 0);
747                         i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
748
749                         items.push_back (MenuElem (_("Alignment"), *alignment_menu));
750
751                 } else {
752                         /* show nothing */
753                 }
754
755                 Menu* mode_menu = manage (new Menu);
756                 MenuList& mode_items = mode_menu->items ();
757                 mode_menu->set_name ("ArdourContextMenu");
758
759                 RadioMenuItem::Group mode_group;
760
761                 int normal = 0;
762                 int tape = 0;
763                 int non_layered = 0;
764
765                 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
766                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
767                         if (!r || !r->is_track ()) {
768                                 continue;
769                         }
770
771                         switch (r->track()->mode()) {
772                         case Normal:
773                                 ++normal;
774                                 break;
775                         case Destructive:
776                                 ++tape;
777                                 break;
778                         case NonLayered:
779                                 ++non_layered;
780                                 break;
781                         }
782                 }
783
784                 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
785                 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
786                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
787                 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
788                 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
789
790                 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
791                 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
792                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
793                 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
794                 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
795
796                 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
797                 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
798                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
799                 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
800                 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
801
802                 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
803
804                 items.push_back (SeparatorElem());
805
806                 build_playlist_menu ();
807                 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
808                 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
809         }
810
811         route_group_menu->detach ();
812
813         WeakRouteList r;
814         for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
815                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
816                 if (rtv) {
817                         r.push_back (rtv->route ());
818                 }
819         }
820
821         if (r.empty ()) {
822                 r.push_back (route ());
823         }
824
825         route_group_menu->build (r);
826         items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
827
828         build_automation_action_menu (true);
829         items.push_back (MenuElem (_("Automation"), *automation_action_menu));
830
831         items.push_back (SeparatorElem());
832
833         int active = 0;
834         int inactive = 0;
835         TrackSelection const & s = _editor.get_selection().tracks;
836         for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
837                 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
838                 if (!r) {
839                         continue;
840                 }
841
842                 if (r->route()->active()) {
843                         ++active;
844                 } else {
845                         ++inactive;
846                 }
847         }
848
849         items.push_back (CheckMenuElem (_("Active")));
850         Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
851         bool click_sets_active = true;
852         if (active > 0 && inactive == 0) {
853                 i->set_active (true);
854                 click_sets_active = false;
855         } else if (active > 0 && inactive > 0) {
856                 i->set_inconsistent (true);
857         }
858         i->set_sensitive(! _session->transport_rolling());
859         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
860
861         items.push_back (SeparatorElem());
862         items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
863         if (_route && !_route->is_master()) {
864                 items.push_back (SeparatorElem());
865                 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
866         }
867         items.push_back (SeparatorElem());
868         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
869 }
870
871 void
872 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
873 {
874         if (apply_to_selection) {
875                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
876         } else {
877
878                 bool needs_bounce = false;
879
880                 if (!track()->can_use_mode (mode, needs_bounce)) {
881
882                         if (!needs_bounce) {
883                                 /* cannot be done */
884                                 return;
885                         } else {
886                                 cerr << "would bounce this one\n";
887                                 return;
888                         }
889                 }
890
891                 track()->set_mode (mode);
892         }
893 }
894
895 void
896 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
897 {
898         TimeAxisView::show_timestretch (start, end, layers, layer);
899
900         hide_timestretch ();
901
902 #if 0
903         if (ts.empty()) {
904                 return;
905         }
906
907
908         /* check that the time selection was made in our route, or our route group.
909            remember that route_group() == 0 implies the route is *not* in a edit group.
910         */
911
912         if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
913                 /* this doesn't apply to us */
914                 return;
915         }
916
917         /* ignore it if our edit group is not active */
918
919         if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
920                 return;
921         }
922 #endif
923
924         if (timestretch_rect == 0) {
925                 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
926                 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
927                 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
928         }
929
930         timestretch_rect->show ();
931         timestretch_rect->raise_to_top ();
932
933         double const x1 = start / _editor.get_current_zoom();
934         double const x2 = (end - 1) / _editor.get_current_zoom();
935
936         timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
937                                                    x2, current_height() * (layers - layer) / layers));
938 }
939
940 void
941 RouteTimeAxisView::hide_timestretch ()
942 {
943         TimeAxisView::hide_timestretch ();
944
945         if (timestretch_rect) {
946                 timestretch_rect->hide ();
947         }
948 }
949
950 void
951 RouteTimeAxisView::show_selection (TimeSelection& ts)
952 {
953
954 #if 0
955         /* ignore it if our edit group is not active or if the selection was started
956            in some other track or route group (remember that route_group() == 0 means
957            that the track is not in an route group).
958         */
959
960         if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
961             (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
962                 hide_selection ();
963                 return;
964         }
965 #endif
966
967         TimeAxisView::show_selection (ts);
968 }
969
970 void
971 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
972 {
973         int gmlen = h - 9;
974         bool height_changed = (height == 0) || (h != height);
975
976         int meter_width = 3;
977         if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
978                 meter_width = 6;
979         }
980         gm.get_level_meter().setup_meters (gmlen, meter_width);
981
982         TimeAxisView::set_height (h, m);
983
984         if (_view) {
985                 _view->set_height ((double) current_height());
986         }
987
988         if (height >= preset_height (HeightNormal)) {
989
990                 reset_meter();
991
992                 gm.get_gain_slider().show();
993                 mute_button->show();
994                 if (!_route || _route->is_monitor()) {
995                         solo_button->hide();
996                 } else {
997                         solo_button->show();
998                 }
999                 if (rec_enable_button)
1000                         rec_enable_button->show();
1001
1002                 route_group_button.show();
1003                 automation_button.show();
1004
1005                 if (is_track() && track()->mode() == ARDOUR::Normal) {
1006                         playlist_button.show();
1007                 }
1008
1009         } else {
1010
1011                 reset_meter();
1012
1013                 gm.get_gain_slider().hide();
1014                 mute_button->show();
1015                 if (!_route || _route->is_monitor()) {
1016                         solo_button->hide();
1017                 } else {
1018                         solo_button->show();
1019                 }
1020                 if (rec_enable_button)
1021                         rec_enable_button->show();
1022
1023                 route_group_button.hide ();
1024                 automation_button.hide ();
1025
1026                 if (is_track() && track()->mode() == ARDOUR::Normal) {
1027                         playlist_button.hide ();
1028                 }
1029
1030         }
1031
1032         if (height_changed && !no_redraw) {
1033                 /* only emit the signal if the height really changed */
1034                 request_redraw ();
1035         }
1036 }
1037
1038 void
1039 RouteTimeAxisView::route_color_changed ()
1040 {
1041         if (_view) {
1042                 _view->apply_color (color(), StreamView::RegionColor);
1043         }
1044
1045         number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1046 }
1047
1048 void
1049 RouteTimeAxisView::reset_samples_per_pixel ()
1050 {
1051         set_samples_per_pixel (_editor.get_current_zoom());
1052 }
1053
1054 void
1055 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1056 {
1057         double speed = 1.0;
1058
1059         if (track()) {
1060                 speed = track()->speed();
1061         }
1062
1063         if (_view) {
1064                 _view->set_samples_per_pixel (fpp * speed);
1065         }
1066
1067         TimeAxisView::set_samples_per_pixel (fpp * speed);
1068 }
1069
1070 void
1071 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1072 {
1073         if (!mitem->get_active()) {
1074                 /* this is one of the two calls made when these radio menu items change status. this one
1075                    is for the item that became inactive, and we want to ignore it.
1076                 */
1077                 return;
1078         }
1079
1080         if (apply_to_selection) {
1081                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1082         } else {
1083                 if (track ()) {
1084                         track()->set_align_choice (choice);
1085                 }
1086         }
1087 }
1088
1089 void
1090 RouteTimeAxisView::rename_current_playlist ()
1091 {
1092         ArdourPrompter prompter (true);
1093         string name;
1094
1095         boost::shared_ptr<Track> tr = track();
1096         if (!tr || tr->destructive()) {
1097                 return;
1098         }
1099
1100         boost::shared_ptr<Playlist> pl = tr->playlist();
1101         if (!pl) {
1102                 return;
1103         }
1104
1105         prompter.set_title (_("Rename Playlist"));
1106         prompter.set_prompt (_("New name for playlist:"));
1107         prompter.set_initial_text (pl->name());
1108         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1109         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1110
1111         switch (prompter.run ()) {
1112         case Gtk::RESPONSE_ACCEPT:
1113                 prompter.get_result (name);
1114                 if (name.length()) {
1115                         pl->set_name (name);
1116                 }
1117                 break;
1118
1119         default:
1120                 break;
1121         }
1122 }
1123
1124 std::string
1125 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1126 {
1127         std::string ret (basename);
1128
1129         std::string const group_string = "." + route_group()->name() + ".";
1130
1131         // iterate through all playlists
1132         int maxnumber = 0;
1133         for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1134                 std::string tmp = (*i)->name();
1135
1136                 std::string::size_type idx = tmp.find(group_string);
1137                 // find those which belong to this group
1138                 if (idx != string::npos) {
1139                         tmp = tmp.substr(idx + group_string.length());
1140
1141                         // and find the largest current number
1142                         int x = atoi(tmp);
1143                         if (x > maxnumber) {
1144                                 maxnumber = x;
1145                         }
1146                 }
1147         }
1148
1149         maxnumber++;
1150
1151         char buf[32];
1152         snprintf (buf, sizeof(buf), "%d", maxnumber);
1153
1154         ret = this->name() + "." + route_group()->name () + "." + buf;
1155
1156         return ret;
1157 }
1158
1159 void
1160 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1161 {
1162         string name;
1163
1164         boost::shared_ptr<Track> tr = track ();
1165         if (!tr || tr->destructive()) {
1166                 return;
1167         }
1168
1169         boost::shared_ptr<const Playlist> pl = tr->playlist();
1170         if (!pl) {
1171                 return;
1172         }
1173
1174         name = pl->name();
1175
1176         if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1177                 name = resolve_new_group_playlist_name(name, playlists_before_op);
1178         }
1179
1180         while (_session->playlists->by_name(name)) {
1181                 name = Playlist::bump_name (name, *_session);
1182         }
1183
1184         // TODO: The prompter "new" button should be de-activated if the user
1185         // specifies a playlist name which already exists in the session.
1186
1187         if (prompt) {
1188
1189                 ArdourPrompter prompter (true);
1190
1191                 prompter.set_title (_("New Copy Playlist"));
1192                 prompter.set_prompt (_("Name for new playlist:"));
1193                 prompter.set_initial_text (name);
1194                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1195                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1196                 prompter.show_all ();
1197
1198                 switch (prompter.run ()) {
1199                 case Gtk::RESPONSE_ACCEPT:
1200                         prompter.get_result (name);
1201                         break;
1202
1203                 default:
1204                         return;
1205                 }
1206         }
1207
1208         if (name.length()) {
1209                 tr->use_copy_playlist ();
1210                 tr->playlist()->set_name (name);
1211         }
1212 }
1213
1214 void
1215 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1216 {
1217         string name;
1218
1219         boost::shared_ptr<Track> tr = track ();
1220         if (!tr || tr->destructive()) {
1221                 return;
1222         }
1223
1224         boost::shared_ptr<const Playlist> pl = tr->playlist();
1225         if (!pl) {
1226                 return;
1227         }
1228
1229         name = pl->name();
1230
1231         if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1232                 name = resolve_new_group_playlist_name(name,playlists_before_op);
1233         }
1234
1235         while (_session->playlists->by_name(name)) {
1236                 name = Playlist::bump_name (name, *_session);
1237         }
1238
1239
1240         if (prompt) {
1241
1242                 ArdourPrompter prompter (true);
1243
1244                 prompter.set_title (_("New Playlist"));
1245                 prompter.set_prompt (_("Name for new playlist:"));
1246                 prompter.set_initial_text (name);
1247                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1248                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1249
1250                 switch (prompter.run ()) {
1251                 case Gtk::RESPONSE_ACCEPT:
1252                         prompter.get_result (name);
1253                         break;
1254
1255                 default:
1256                         return;
1257                 }
1258         }
1259
1260         if (name.length()) {
1261                 tr->use_new_playlist ();
1262                 tr->playlist()->set_name (name);
1263         }
1264 }
1265
1266 void
1267 RouteTimeAxisView::clear_playlist ()
1268 {
1269         boost::shared_ptr<Track> tr = track ();
1270         if (!tr || tr->destructive()) {
1271                 return;
1272         }
1273
1274         boost::shared_ptr<Playlist> pl = tr->playlist();
1275         if (!pl) {
1276                 return;
1277         }
1278
1279         _editor.clear_playlist (pl);
1280 }
1281
1282 void
1283 RouteTimeAxisView::speed_changed ()
1284 {
1285         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1286 }
1287
1288 void
1289 RouteTimeAxisView::update_diskstream_display ()
1290 {
1291         if (!track()) {
1292                 return;
1293         }
1294
1295         map_frozen ();
1296 }
1297
1298 void
1299 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1300 {
1301         if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1302
1303                 /* special case: select/deselect all tracks */
1304
1305                 _editor.begin_reversible_selection_op (X_("Selection Click"));
1306
1307                 if (_editor.get_selection().selected (this)) {
1308                         _editor.get_selection().clear_tracks ();
1309                 } else {
1310                         _editor.select_all_tracks ();
1311                 }
1312
1313                 _editor.commit_reversible_selection_op ();
1314
1315                 return;
1316         }
1317
1318         _editor.begin_reversible_selection_op (X_("Selection Click"));
1319
1320         switch (ArdourKeyboard::selection_type (ev->state)) {
1321         case Selection::Toggle:
1322                 _editor.get_selection().toggle (this);
1323                 break;
1324
1325         case Selection::Set:
1326                 _editor.get_selection().set (this);
1327                 break;
1328
1329         case Selection::Extend:
1330                 _editor.extend_selection_to_track (*this);
1331                 break;
1332
1333         case Selection::Add:
1334                 _editor.get_selection().add (this);
1335                 break;
1336         }
1337
1338         _editor.commit_reversible_selection_op ();
1339 }
1340
1341 void
1342 RouteTimeAxisView::set_selected_points (PointSelection& points)
1343 {
1344         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1345                 (*i)->set_selected_points (points);
1346         }
1347         AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1348         if (asv) {
1349                 asv->set_selected_points (points);
1350         }
1351 }
1352
1353 void
1354 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1355 {
1356         if (_view) {
1357                 _view->set_selected_regionviews (regions);
1358         }
1359 }
1360
1361 /** Add the selectable things that we have to a list.
1362  * @param results List to add things to.
1363  */
1364 void
1365 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1366 {
1367         double speed = 1.0;
1368
1369         if (track() != 0) {
1370                 speed = track()->speed();
1371         }
1372
1373         framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1374         framepos_t const end_adjusted   = session_frame_to_track_frame(end, speed);
1375
1376         if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1377                 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1378         }
1379
1380         /* pick up visible automation tracks */
1381
1382         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1383                 if (!(*i)->hidden()) {
1384                         (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1385                 }
1386         }
1387 }
1388
1389 void
1390 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1391 {
1392         if (_view) {
1393                 _view->get_inverted_selectables (sel, results);
1394         }
1395
1396         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1397                 if (!(*i)->hidden()) {
1398                         (*i)->get_inverted_selectables (sel, results);
1399                 }
1400         }
1401
1402         return;
1403 }
1404
1405 RouteGroup*
1406 RouteTimeAxisView::route_group () const
1407 {
1408         return _route->route_group();
1409 }
1410
1411 string
1412 RouteTimeAxisView::name() const
1413 {
1414         return _route->name();
1415 }
1416
1417 boost::shared_ptr<Playlist>
1418 RouteTimeAxisView::playlist () const
1419 {
1420         boost::shared_ptr<Track> tr;
1421
1422         if ((tr = track()) != 0) {
1423                 return tr->playlist();
1424         } else {
1425                 return boost::shared_ptr<Playlist> ();
1426         }
1427 }
1428
1429 bool
1430 RouteTimeAxisView::name_entry_changed (string const& str)
1431 {
1432         if (str == _route->name()) {
1433                 return true;
1434         }
1435
1436         string x = str;
1437         
1438         strip_whitespace_edges (x);
1439
1440         if (x.empty()) {
1441                 return false;
1442         }
1443
1444         if (_session->route_name_internal (x)) {
1445                 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1446                 return false;
1447         } else if (RouteUI::verify_new_route_name (x)) {
1448                 _route->set_name (x);
1449                 return true;
1450         }
1451
1452         return false;
1453 }
1454
1455 boost::shared_ptr<Region>
1456 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1457 {
1458         boost::shared_ptr<Playlist> pl = playlist ();
1459
1460         if (pl) {
1461                 return pl->find_next_region (pos, point, dir);
1462         }
1463
1464         return boost::shared_ptr<Region> ();
1465 }
1466
1467 framepos_t
1468 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1469 {
1470         boost::shared_ptr<Playlist> pl = playlist ();
1471
1472         if (pl) {
1473                 return pl->find_next_region_boundary (pos, dir);
1474         }
1475
1476         return -1;
1477 }
1478
1479 void
1480 RouteTimeAxisView::fade_range (TimeSelection& selection)
1481 {
1482         boost::shared_ptr<Playlist> what_we_got;
1483         boost::shared_ptr<Track> tr = track ();
1484         boost::shared_ptr<Playlist> playlist;
1485
1486         if (tr == 0) {
1487                 /* route is a bus, not a track */
1488                 return;
1489         }
1490
1491         playlist = tr->playlist();
1492
1493         TimeSelection time (selection);
1494         float const speed = tr->speed();
1495         if (speed != 1.0f) {
1496                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1497                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1498                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1499                 }
1500         }
1501
1502         playlist->clear_changes ();
1503         playlist->clear_owned_changes ();
1504
1505         playlist->fade_range (time);
1506
1507         vector<Command*> cmds;
1508         playlist->rdiff (cmds);
1509         _session->add_commands (cmds);
1510         _session->add_command (new StatefulDiffCommand (playlist));
1511
1512 }
1513
1514 void
1515 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1516 {
1517         boost::shared_ptr<Playlist> what_we_got;
1518         boost::shared_ptr<Track> tr = track ();
1519         boost::shared_ptr<Playlist> playlist;
1520
1521         if (tr == 0) {
1522                 /* route is a bus, not a track */
1523                 return;
1524         }
1525
1526         playlist = tr->playlist();
1527
1528         TimeSelection time (selection.time);
1529         float const speed = tr->speed();
1530         if (speed != 1.0f) {
1531                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1532                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1533                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1534                 }
1535         }
1536
1537         playlist->clear_changes ();
1538         playlist->clear_owned_changes ();
1539
1540         switch (op) {
1541         case Delete:
1542                 if (playlist->cut (time) != 0) {
1543                         if (Config->get_edit_mode() == Ripple)
1544                                 playlist->ripple(time.start(), -time.length(), NULL);
1545                                 // no need to exclude any regions from rippling here
1546
1547                         vector<Command*> cmds;
1548                         playlist->rdiff (cmds);
1549                         _session->add_commands (cmds);
1550
1551                         _session->add_command (new StatefulDiffCommand (playlist));
1552                 }
1553                 break;
1554
1555         case Cut:
1556                 if ((what_we_got = playlist->cut (time)) != 0) {
1557                         _editor.get_cut_buffer().add (what_we_got);
1558                         if (Config->get_edit_mode() == Ripple)
1559                                 playlist->ripple(time.start(), -time.length(), NULL);
1560                                 // no need to exclude any regions from rippling here
1561
1562                         vector<Command*> cmds;
1563                         playlist->rdiff (cmds);
1564                         _session->add_commands (cmds);
1565
1566                         _session->add_command (new StatefulDiffCommand (playlist));
1567                 }
1568                 break;
1569         case Copy:
1570                 if ((what_we_got = playlist->copy (time)) != 0) {
1571                         _editor.get_cut_buffer().add (what_we_got);
1572                 }
1573                 break;
1574
1575         case Clear:
1576                 if ((what_we_got = playlist->cut (time)) != 0) {
1577                         if (Config->get_edit_mode() == Ripple)
1578                                 playlist->ripple(time.start(), -time.length(), NULL);
1579                                 // no need to exclude any regions from rippling here
1580
1581                         vector<Command*> cmds;
1582                         playlist->rdiff (cmds);
1583                         _session->add_commands (cmds);
1584                         _session->add_command (new StatefulDiffCommand (playlist));
1585                         what_we_got->release ();
1586                 }
1587                 break;
1588         }
1589 }
1590
1591 bool
1592 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1593 {
1594         if (!is_track()) {
1595                 return false;
1596         }
1597
1598         boost::shared_ptr<Playlist>       pl   = playlist ();
1599         const ARDOUR::DataType            type = pl->data_type();
1600         PlaylistSelection::const_iterator p    = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1601
1602         if (p == selection.playlists.end()) {
1603                 return false;
1604         }
1605         ctx.counts.increase_n_playlists(type);
1606
1607         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1608
1609         if (track()->speed() != 1.0f) {
1610                 pos = session_frame_to_track_frame (pos, track()->speed());
1611                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1612         }
1613
1614         /* add multi-paste offset if applicable */
1615         std::pair<framepos_t, framepos_t> extent   = (*p)->get_extent();
1616         const framecnt_t                  duration = extent.second - extent.first;
1617         pos += _editor.get_paste_offset(pos, ctx.count, duration);
1618
1619         pl->clear_changes ();
1620         pl->clear_owned_changes ();
1621         if (Config->get_edit_mode() == Ripple) {
1622                 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1623                 framecnt_t amount = extent.second - extent.first;
1624                 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1625         }
1626         pl->paste (*p, pos, ctx.times);
1627
1628         vector<Command*> cmds;
1629         pl->rdiff (cmds);
1630         _session->add_commands (cmds);
1631
1632         _session->add_command (new StatefulDiffCommand (pl));
1633
1634         return true;
1635 }
1636
1637
1638 struct PlaylistSorter {
1639     bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1640             return a->sort_id() < b->sort_id();
1641     }
1642 };
1643
1644 void
1645 RouteTimeAxisView::build_playlist_menu ()
1646 {
1647         using namespace Menu_Helpers;
1648
1649         if (!is_track()) {
1650                 return;
1651         }
1652
1653         delete playlist_action_menu;
1654         playlist_action_menu = new Menu;
1655         playlist_action_menu->set_name ("ArdourContextMenu");
1656
1657         MenuList& playlist_items = playlist_action_menu->items();
1658         playlist_action_menu->set_name ("ArdourContextMenu");
1659         playlist_items.clear();
1660
1661         RadioMenuItem::Group playlist_group;
1662         boost::shared_ptr<Track> tr = track ();
1663
1664         vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1665
1666         /* sort the playlists */
1667         PlaylistSorter cmp;
1668         sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1669
1670         /* add the playlists to the menu */
1671         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1672                 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1673                 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1674                 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1675
1676                 if (tr->playlist()->id() == (*i)->id()) {
1677                         item->set_active();
1678
1679                 }
1680         }
1681
1682         playlist_items.push_back (SeparatorElem());
1683         playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1684         playlist_items.push_back (SeparatorElem());
1685
1686         if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1687                 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1688                 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1689
1690         } else {
1691                 // Use a label which tells the user what is happening
1692                 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1693                 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1694
1695         }
1696
1697         playlist_items.push_back (SeparatorElem());
1698         playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1699         playlist_items.push_back (SeparatorElem());
1700
1701         playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1702 }
1703
1704 void
1705 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1706 {
1707         assert (is_track());
1708
1709         // exit if we were triggered by deactivating the old playlist
1710         if (!item->get_active()) {
1711                 return;
1712         }
1713
1714         boost::shared_ptr<Playlist> pl (wpl.lock());
1715
1716         if (!pl) {
1717                 return;
1718         }
1719
1720         if (track()->playlist() == pl) {
1721                 // exit when use_playlist is called by the creation of the playlist menu
1722                 // or the playlist choice is unchanged
1723                 return;
1724         }
1725
1726         track()->use_playlist (pl);
1727
1728         RouteGroup* rg = route_group();
1729
1730         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1731                 std::string group_string = "." + rg->name() + ".";
1732
1733                 std::string take_name = pl->name();
1734                 std::string::size_type idx = take_name.find(group_string);
1735
1736                 if (idx == std::string::npos)
1737                         return;
1738
1739                 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1740
1741                 boost::shared_ptr<RouteList> rl (rg->route_list());
1742
1743                 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1744                         if ((*i) == this->route()) {
1745                                 continue;
1746                         }
1747
1748                         std::string playlist_name = (*i)->name()+group_string+take_name;
1749
1750                         boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1751                         if (!track) {
1752                                 continue;
1753                         }
1754
1755                         if (track->freeze_state() == Track::Frozen) {
1756                                 /* Don't change playlists of frozen tracks */
1757                                 continue;
1758                         }
1759
1760                         boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1761                         if (!ipl) {
1762                                 // No playlist for this track for this take yet, make it
1763                                 track->use_new_playlist();
1764                                 track->playlist()->set_name(playlist_name);
1765                         } else {
1766                                 track->use_playlist(ipl);
1767                         }
1768                 }
1769         }
1770 }
1771
1772 void
1773 RouteTimeAxisView::update_playlist_tip ()
1774 {
1775         RouteGroup* rg = route_group ();
1776         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1777                 string group_string = "." + rg->name() + ".";
1778
1779                 string take_name = track()->playlist()->name();
1780                 string::size_type idx = take_name.find(group_string);
1781
1782                 if (idx != string::npos) {
1783                         /* find the bit containing the take number / name */
1784                         take_name = take_name.substr (idx + group_string.length());
1785
1786                         /* set the playlist button tooltip to the take name */
1787                         set_tooltip (
1788                                 playlist_button,
1789                                 string_compose(_("Take: %1.%2"),
1790                                         Gtkmm2ext::markup_escape_text (rg->name()),
1791                                         Gtkmm2ext::markup_escape_text (take_name))
1792                                 );
1793
1794                         return;
1795                 }
1796         }
1797
1798         /* set the playlist button tooltip to the playlist name */
1799         set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1800 }
1801
1802
1803 void
1804 RouteTimeAxisView::show_playlist_selector ()
1805 {
1806         _editor.playlist_selector().show_for (this);
1807 }
1808
1809 void
1810 RouteTimeAxisView::map_frozen ()
1811 {
1812         if (!is_track()) {
1813                 return;
1814         }
1815
1816         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1817
1818         switch (track()->freeze_state()) {
1819         case Track::Frozen:
1820                 playlist_button.set_sensitive (false);
1821                 break;
1822         default:
1823                 playlist_button.set_sensitive (true);
1824                 break;
1825         }
1826         RouteUI::map_frozen ();
1827 }
1828
1829 void
1830 RouteTimeAxisView::color_handler ()
1831 {
1832         //case cTimeStretchOutline:
1833         if (timestretch_rect) {
1834                 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1835         }
1836         //case cTimeStretchFill:
1837         if (timestretch_rect) {
1838                 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1839         }
1840
1841         reset_meter();
1842 }
1843
1844 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1845  *  Will add track if necessary.
1846  */
1847 void
1848 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1849 {
1850         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1851         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1852
1853         if (!track) {
1854                 /* it doesn't exist yet, so we don't care about the button state: just add it */
1855                 create_automation_child (param, true);
1856         } else {
1857                 assert (menu);
1858                 bool yn = menu->get_active();
1859                 bool changed = false;
1860
1861                 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1862
1863                         /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1864                            will have done that for us.
1865                         */
1866
1867                         if (changed && !no_redraw) {
1868                                 request_redraw ();
1869                         }
1870                 }
1871         }
1872 }
1873
1874 void
1875 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1876 {
1877         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1878
1879         if (!track) {
1880                 return;
1881         }
1882
1883         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1884
1885         if (menu && !_hidden) {
1886                 ignore_toggle = true;
1887                 menu->set_active (false);
1888                 ignore_toggle = false;
1889         }
1890
1891         if (_route && !no_redraw) {
1892                 request_redraw ();
1893         }
1894 }
1895
1896 void
1897 RouteTimeAxisView::update_gain_track_visibility ()
1898 {
1899         bool const showit = gain_automation_item->get_active();
1900
1901         if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1902                 gain_track->set_marked_for_display (showit);
1903
1904                 /* now trigger a redisplay */
1905
1906                 if (!no_redraw) {
1907                          _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1908                 }
1909         }
1910 }
1911
1912 void
1913 RouteTimeAxisView::update_trim_track_visibility ()
1914 {
1915         bool const showit = trim_automation_item->get_active();
1916
1917         if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1918                 trim_track->set_marked_for_display (showit);
1919
1920                 /* now trigger a redisplay */
1921
1922                 if (!no_redraw) {
1923                          _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1924                 }
1925         }
1926 }
1927
1928 void
1929 RouteTimeAxisView::update_mute_track_visibility ()
1930 {
1931         bool const showit = mute_automation_item->get_active();
1932
1933         if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1934                 mute_track->set_marked_for_display (showit);
1935
1936                 /* now trigger a redisplay */
1937
1938                 if (!no_redraw) {
1939                          _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1940                 }
1941         }
1942 }
1943
1944 void
1945 RouteTimeAxisView::update_pan_track_visibility ()
1946 {
1947         bool const showit = pan_automation_item->get_active();
1948         bool changed = false;
1949
1950         for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1951                 if ((*i)->set_marked_for_display (showit)) {
1952                         changed = true;
1953                 }
1954         }
1955
1956         if (changed) {
1957                 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1958         }
1959 }
1960
1961 void
1962 RouteTimeAxisView::ensure_pan_views (bool show)
1963 {
1964         bool changed = false;
1965         for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1966                 changed = true;
1967                 (*i)->set_marked_for_display (false);
1968         }
1969         if (changed) {
1970                 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1971         }
1972         pan_tracks.clear();
1973
1974         if (!_route->panner()) {
1975                 return;
1976         }
1977
1978         set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1979         set<Evoral::Parameter>::iterator p;
1980
1981         for (p = params.begin(); p != params.end(); ++p) {
1982                 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1983
1984                 if (pan_control->parameter().type() == NullAutomation) {
1985                         error << "Pan control has NULL automation type!" << endmsg;
1986                         continue;
1987                 }
1988
1989                 if (automation_child (pan_control->parameter ()).get () == 0) {
1990
1991                         /* we don't already have an AutomationTimeAxisView for this parameter */
1992
1993                         std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1994
1995                         boost::shared_ptr<AutomationTimeAxisView> t (
1996                                         new AutomationTimeAxisView (_session,
1997                                                 _route,
1998                                                 _route->pannable(),
1999                                                 pan_control,
2000                                                 pan_control->parameter (),
2001                                                 _editor,
2002                                                 *this,
2003                                                 false,
2004                                                 parent_canvas,
2005                                                 name)
2006                                         );
2007
2008                         pan_tracks.push_back (t);
2009                         add_automation_child (*p, t, show);
2010                 } else {
2011                         pan_tracks.push_back (automation_child (pan_control->parameter ()));
2012                 }
2013         }
2014 }
2015
2016
2017 void
2018 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2019 {
2020         if (apply_to_selection) {
2021                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2022         } else {
2023                 no_redraw = true;
2024
2025                 /* Show our automation */
2026
2027                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2028                         i->second->set_marked_for_display (true);
2029
2030                         Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2031
2032                         if (menu) {
2033                                 menu->set_active(true);
2034                         }
2035                 }
2036
2037
2038                 /* Show processor automation */
2039
2040                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2041                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2042                                 if ((*ii)->view == 0) {
2043                                         add_processor_automation_curve ((*i)->processor, (*ii)->what);
2044                                 }
2045
2046                                 (*ii)->menu_item->set_active (true);
2047                         }
2048                 }
2049
2050                 no_redraw = false;
2051
2052                 /* Redraw */
2053
2054                 request_redraw ();
2055         }
2056 }
2057
2058 void
2059 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2060 {
2061         if (apply_to_selection) {
2062                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2063         } else {
2064                 no_redraw = true;
2065
2066                 /* Show our automation */
2067
2068                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2069                         if (i->second->has_automation()) {
2070                                 i->second->set_marked_for_display (true);
2071
2072                                 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2073                                 if (menu) {
2074                                         menu->set_active(true);
2075                                 }
2076                         }
2077                 }
2078
2079                 /* Show processor automation */
2080
2081                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2082                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2083                                 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2084                                         (*ii)->menu_item->set_active (true);
2085                                 }
2086                         }
2087                 }
2088
2089                 no_redraw = false;
2090
2091                 request_redraw ();
2092         }
2093 }
2094
2095 void
2096 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2097 {
2098         if (apply_to_selection) {
2099                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2100         } else {
2101                 no_redraw = true;
2102
2103                 /* Hide our automation */
2104
2105                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2106                         i->second->set_marked_for_display (false);
2107
2108                         Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2109
2110                         if (menu) {
2111                                 menu->set_active (false);
2112                         }
2113                 }
2114
2115                 /* Hide processor automation */
2116
2117                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2118                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2119                                 (*ii)->menu_item->set_active (false);
2120                         }
2121                 }
2122
2123                 no_redraw = false;
2124                 request_redraw ();
2125         }
2126 }
2127
2128
2129 void
2130 RouteTimeAxisView::region_view_added (RegionView* rv)
2131 {
2132         /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2133         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2134                 boost::shared_ptr<AutomationTimeAxisView> atv;
2135
2136                 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2137                         atv->add_ghost(rv);
2138                 }
2139         }
2140
2141         for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2142                 (*i)->add_ghost(rv);
2143         }
2144 }
2145
2146 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2147 {
2148         for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2149                 delete *i;
2150         }
2151 }
2152
2153
2154 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2155 {
2156         parent.remove_processor_automation_node (this);
2157 }
2158
2159 void
2160 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2161 {
2162         if (pan->view) {
2163                 remove_child (pan->view);
2164         }
2165 }
2166
2167 RouteTimeAxisView::ProcessorAutomationNode*
2168 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2169 {
2170         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2171
2172                 if ((*i)->processor == processor) {
2173
2174                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2175                                 if ((*ii)->what == what) {
2176                                         return *ii;
2177                                 }
2178                         }
2179                 }
2180         }
2181
2182         return 0;
2183 }
2184
2185 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2186 void
2187 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2188 {
2189         string name;
2190         ProcessorAutomationNode* pan;
2191
2192         if ((pan = find_processor_automation_node (processor, what)) == 0) {
2193                 /* session state may never have been saved with new plugin */
2194                 error << _("programming error: ")
2195                       << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2196                                          processor->name(), what.type(), (int) what.channel(), what.id() )
2197                       << endmsg;
2198                 abort(); /*NOTREACHED*/
2199                 return;
2200         }
2201
2202         if (pan->view) {
2203                 return;
2204         }
2205
2206         boost::shared_ptr<AutomationControl> control
2207                 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2208
2209         pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2210                 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2211                                             _editor, *this, false, parent_canvas,
2212                                             processor->describe_parameter (what), processor->name()));
2213
2214         pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2215
2216         add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2217
2218         if (_view) {
2219                 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2220         }
2221 }
2222
2223 void
2224 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2225 {
2226         if (!_hidden) {
2227                 pan->menu_item->set_active (false);
2228         }
2229
2230         if (!no_redraw) {
2231                 request_redraw ();
2232         }
2233 }
2234
2235 void
2236 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2237 {
2238         boost::shared_ptr<Processor> processor (p.lock ());
2239
2240         if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2241                 /* The Amp processor is a special case and is dealt with separately */
2242                 return;
2243         }
2244
2245         set<Evoral::Parameter> existing;
2246
2247         processor->what_has_data (existing);
2248
2249         for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2250
2251                 Evoral::Parameter param (*i);
2252                 boost::shared_ptr<AutomationLine> al;
2253
2254                 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2255                         al->queue_reset ();
2256                 } else {
2257                         add_processor_automation_curve (processor, param);
2258                 }
2259         }
2260 }
2261
2262 void
2263 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2264 {
2265         using namespace Menu_Helpers;
2266
2267         add_child (track);
2268
2269         track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2270
2271         _automation_tracks[param] = track;
2272
2273         /* existing state overrides "show" argument */
2274         string s = track->gui_property ("visible");
2275         if (!s.empty()) {
2276                 show = string_is_affirmative (s);
2277         }
2278
2279         /* this might or might not change the visibility status, so don't rely on it */
2280         track->set_marked_for_display (show);
2281
2282         if (show && !no_redraw) {
2283                 request_redraw ();
2284         }
2285
2286         if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2287                 /* MIDI-related parameters are always in the menu, there's no
2288                    reason to rebuild the menu just because we added a automation
2289                    lane for one of them. But if we add a non-MIDI automation
2290                    lane, then we need to invalidate the display menu.
2291                 */
2292                 delete display_menu;
2293                 display_menu = 0;
2294         }
2295 }
2296
2297 void
2298 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2299 {
2300         boost::shared_ptr<Processor> processor (p.lock ());
2301
2302         if (!processor || !processor->display_to_user ()) {
2303                 return;
2304         }
2305
2306         /* we use this override to veto the Amp processor from the plugin menu,
2307            as its automation lane can be accessed using the special "Fader" menu
2308            option
2309         */
2310
2311         if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2312                 return;
2313         }
2314
2315         using namespace Menu_Helpers;
2316         ProcessorAutomationInfo *rai;
2317         list<ProcessorAutomationInfo*>::iterator x;
2318
2319         const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2320
2321         if (automatable.empty()) {
2322                 return;
2323         }
2324
2325         for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2326                 if ((*x)->processor == processor) {
2327                         break;
2328                 }
2329         }
2330
2331         if (x == processor_automation.end()) {
2332                 rai = new ProcessorAutomationInfo (processor);
2333                 processor_automation.push_back (rai);
2334         } else {
2335                 rai = *x;
2336         }
2337
2338         /* any older menu was deleted at the top of processors_changed()
2339            when we cleared the subplugin menu.
2340         */
2341
2342         rai->menu = manage (new Menu);
2343         MenuList& items = rai->menu->items();
2344         rai->menu->set_name ("ArdourContextMenu");
2345
2346         items.clear ();
2347
2348         std::set<Evoral::Parameter> has_visible_automation;
2349         AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2350
2351         for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2352
2353                 ProcessorAutomationNode* pan;
2354                 Gtk::CheckMenuItem* mitem;
2355
2356                 string name = processor->describe_parameter (*i);
2357
2358                 if (name == X_("hidden")) {
2359                         continue;
2360                 }
2361
2362                 items.push_back (CheckMenuElem (name));
2363                 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2364
2365                 _subplugin_menu_map[*i] = mitem;
2366
2367                 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2368                         mitem->set_active(true);
2369                 }
2370
2371                 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2372
2373                         /* new item */
2374
2375                         pan = new ProcessorAutomationNode (*i, mitem, *this);
2376
2377                         rai->lines.push_back (pan);
2378
2379                 } else {
2380
2381                         pan->menu_item = mitem;
2382
2383                 }
2384
2385                 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2386         }
2387
2388         if (items.size() == 0) {
2389                 return;
2390         }
2391
2392         /* add the menu for this processor, because the subplugin
2393            menu is always cleared at the top of processors_changed().
2394            this is the result of some poor design in gtkmm and/or
2395            GTK+.
2396         */
2397
2398         subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2399         rai->valid = true;
2400 }
2401
2402 void
2403 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2404                                                RouteTimeAxisView::ProcessorAutomationNode* pan)
2405 {
2406         bool showit = pan->menu_item->get_active();
2407         bool redraw = false;
2408
2409         if (pan->view == 0 && showit) {
2410                 add_processor_automation_curve (rai->processor, pan->what);
2411                 redraw = true;
2412         }
2413
2414         if (pan->view && pan->view->set_marked_for_display (showit)) {
2415                 redraw = true;
2416         }
2417
2418         if (redraw && !no_redraw) {
2419                 request_redraw ();
2420         }
2421 }
2422
2423 void
2424 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2425 {
2426         if (c.type == RouteProcessorChange::MeterPointChange) {
2427                 /* nothing to do if only the meter point has changed */
2428                 return;
2429         }
2430
2431         using namespace Menu_Helpers;
2432
2433         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2434                 (*i)->valid = false;
2435         }
2436
2437         setup_processor_menu_and_curves ();
2438
2439         bool deleted_processor_automation = false;
2440
2441         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2442
2443                 list<ProcessorAutomationInfo*>::iterator tmp;
2444
2445                 tmp = i;
2446                 ++tmp;
2447
2448                 if (!(*i)->valid) {
2449
2450                         delete *i;
2451                         processor_automation.erase (i);
2452                         deleted_processor_automation = true;
2453
2454                 }
2455
2456                 i = tmp;
2457         }
2458
2459         if (deleted_processor_automation && !no_redraw) {
2460                 request_redraw ();
2461         }
2462 }
2463
2464 boost::shared_ptr<AutomationLine>
2465 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2466 {
2467         ProcessorAutomationNode* pan;
2468
2469         if ((pan = find_processor_automation_node (processor, what)) != 0) {
2470                 if (pan->view) {
2471                         pan->view->line();
2472                 }
2473         }
2474
2475         return boost::shared_ptr<AutomationLine>();
2476 }
2477
2478 void
2479 RouteTimeAxisView::reset_processor_automation_curves ()
2480 {
2481         for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2482                 (*i)->reset();
2483         }
2484 }
2485
2486 bool
2487 RouteTimeAxisView::can_edit_name () const
2488 {
2489         /* we do not allow track name changes if it is record enabled
2490          */
2491         boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2492         if (!trk) {
2493                 return true;
2494         }
2495         return !trk->rec_enable_control()->get_value();
2496 }
2497
2498 void
2499 RouteTimeAxisView::blink_rec_display (bool onoff)
2500 {
2501         RouteUI::blink_rec_display (onoff);
2502 }
2503
2504 void
2505 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2506 {
2507         if (_ignore_set_layer_display) {
2508                 return;
2509         }
2510
2511         if (apply_to_selection) {
2512                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2513         } else {
2514
2515                 if (_view) {
2516                         _view->set_layer_display (d);
2517                 }
2518
2519                 set_gui_property (X_("layer-display"), enum_2_string (d));
2520         }
2521 }
2522
2523 LayerDisplay
2524 RouteTimeAxisView::layer_display () const
2525 {
2526         if (_view) {
2527                 return _view->layer_display ();
2528         }
2529
2530         /* we don't know, since we don't have a _view, so just return something */
2531         return Overlaid;
2532 }
2533
2534
2535
2536 boost::shared_ptr<AutomationTimeAxisView>
2537 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2538 {
2539         AutomationTracks::iterator i = _automation_tracks.find(param);
2540         if (i != _automation_tracks.end()) {
2541                 return i->second;
2542         } else {
2543                 return boost::shared_ptr<AutomationTimeAxisView>();
2544         }
2545 }
2546
2547 void
2548 RouteTimeAxisView::fast_update ()
2549 {
2550         gm.get_level_meter().update_meters ();
2551 }
2552
2553 void
2554 RouteTimeAxisView::hide_meter ()
2555 {
2556         clear_meter ();
2557         gm.get_level_meter().hide_meters ();
2558 }
2559
2560 void
2561 RouteTimeAxisView::show_meter ()
2562 {
2563         reset_meter ();
2564 }
2565
2566 void
2567 RouteTimeAxisView::reset_meter ()
2568 {
2569         if (UIConfiguration::instance().get_show_track_meters()) {
2570                 int meter_width = 3;
2571                 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2572                         meter_width = 6;
2573                 }
2574                 gm.get_level_meter().setup_meters (height - 9, meter_width);
2575         } else {
2576                 hide_meter ();
2577         }
2578 }
2579
2580 void
2581 RouteTimeAxisView::clear_meter ()
2582 {
2583         gm.get_level_meter().clear_meters ();
2584 }
2585
2586 void
2587 RouteTimeAxisView::meter_changed ()
2588 {
2589         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2590         reset_meter();
2591         if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2592                 request_redraw ();
2593         }
2594         // reset peak when meter point changes
2595         gm.reset_peak_display();
2596 }
2597
2598 void
2599 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2600 {
2601         reset_meter ();
2602         if (_route && !no_redraw) {
2603                 request_redraw ();
2604         }
2605 }
2606
2607 void
2608 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2609 {
2610         using namespace Menu_Helpers;
2611
2612         if (!_underlay_streams.empty()) {
2613                 MenuList& parent_items = parent_menu->items();
2614                 Menu* gs_menu = manage (new Menu);
2615                 gs_menu->set_name ("ArdourContextMenu");
2616                 MenuList& gs_items = gs_menu->items();
2617
2618                 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2619
2620                 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2621                         gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2622                                                     sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2623                 }
2624         }
2625 }
2626
2627 bool
2628 RouteTimeAxisView::set_underlay_state()
2629 {
2630         if (!underlay_xml_node) {
2631                 return false;
2632         }
2633
2634         XMLNodeList nlist = underlay_xml_node->children();
2635         XMLNodeConstIterator niter;
2636         XMLNode *child_node;
2637
2638         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2639                 child_node = *niter;
2640
2641                 if (child_node->name() != "Underlay") {
2642                         continue;
2643                 }
2644
2645                 XMLProperty const * prop = child_node->property ("id");
2646                 if (prop) {
2647                         PBD::ID id (prop->value());
2648
2649                         RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2650
2651                         if (v) {
2652                                 add_underlay(v->view(), false);
2653                         }
2654                 }
2655         }
2656
2657         return false;
2658 }
2659
2660 void
2661 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2662 {
2663         if (!v) {
2664                 return;
2665         }
2666
2667         RouteTimeAxisView& other = v->trackview();
2668
2669         if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2670                 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2671                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2672                         abort(); /*NOTREACHED*/
2673                 }
2674
2675                 _underlay_streams.push_back(v);
2676                 other._underlay_mirrors.push_back(this);
2677
2678                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2679
2680 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2681                 if (update_xml) {
2682                         if (!underlay_xml_node) {
2683                                 underlay_xml_node = xml_node->add_child("Underlays");
2684                         }
2685
2686                         XMLNode* node = underlay_xml_node->add_child("Underlay");
2687                         XMLProperty const * prop = node->add_property("id");
2688                         prop->set_value(v->trackview().route()->id().to_s());
2689                 }
2690 #endif
2691         }
2692 }
2693
2694 void
2695 RouteTimeAxisView::remove_underlay (StreamView* v)
2696 {
2697         if (!v) {
2698                 return;
2699         }
2700
2701         UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2702         RouteTimeAxisView& other = v->trackview();
2703
2704         if (it != _underlay_streams.end()) {
2705                 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2706
2707                 if (gm == other._underlay_mirrors.end()) {
2708                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2709                         abort(); /*NOTREACHED*/
2710                 }
2711
2712                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2713
2714                 _underlay_streams.erase(it);
2715                 other._underlay_mirrors.erase(gm);
2716
2717                 if (underlay_xml_node) {
2718                         underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2719                 }
2720         }
2721 }
2722
2723 void
2724 RouteTimeAxisView::set_button_names ()
2725 {
2726         if (_route && _route->solo_safe_control()->solo_safe()) {
2727                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2728         } else {
2729                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2730         }
2731         if (Config->get_solo_control_is_listen_control()) {
2732                 switch (Config->get_listen_position()) {
2733                         case AfterFaderListen:
2734                                 solo_button->set_text (S_("AfterFader|A"));
2735                                 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2736                                 break;
2737                         case PreFaderListen:
2738                                 solo_button->set_text (S_("PreFader|P"));
2739                                 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2740                         break;
2741                 }
2742         } else {
2743                 solo_button->set_text (S_("Solo|S"));
2744                 set_tooltip (*solo_button, _("Solo"));
2745         }
2746         mute_button->set_text (S_("Mute|M"));
2747 }
2748
2749 Gtk::CheckMenuItem*
2750 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2751 {
2752         ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2753         if (i != _main_automation_menu_map.end()) {
2754                 return i->second;
2755         }
2756
2757         i = _subplugin_menu_map.find (param);
2758         if (i != _subplugin_menu_map.end()) {
2759                 return i->second;
2760         }
2761
2762         return 0;
2763 }
2764
2765 void
2766 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2767 {
2768         boost::shared_ptr<AutomationControl> c = _route->gain_control();
2769         if (!c) {
2770                 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2771                 return;
2772         }
2773
2774         gain_track.reset (new AutomationTimeAxisView (_session,
2775                                                       _route, _route->amp(), c, param,
2776                                                       _editor,
2777                                                       *this,
2778                                                       false,
2779                                                       parent_canvas,
2780                                                       _route->amp()->describe_parameter(param)));
2781
2782         if (_view) {
2783                 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2784         }
2785
2786         add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2787 }
2788
2789 void
2790 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2791 {
2792         boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2793         if (!c || ! _route->trim()->active()) {
2794                 return;
2795         }
2796
2797         trim_track.reset (new AutomationTimeAxisView (_session,
2798                                                       _route, _route->trim(), c, param,
2799                                                       _editor,
2800                                                       *this,
2801                                                       false,
2802                                                       parent_canvas,
2803                                                       _route->trim()->describe_parameter(param)));
2804
2805         if (_view) {
2806                 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2807         }
2808
2809         add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2810 }
2811
2812 void
2813 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2814 {
2815         boost::shared_ptr<AutomationControl> c = _route->mute_control();
2816         if (!c) {
2817                 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2818                 return;
2819         }
2820
2821         mute_track.reset (new AutomationTimeAxisView (_session,
2822                                                       _route, _route, c, param,
2823                                                       _editor,
2824                                                       *this,
2825                                                       false,
2826                                                       parent_canvas,
2827                                                       _route->describe_parameter(param)));
2828
2829         if (_view) {
2830                 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2831         }
2832
2833         add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2834 }
2835
2836 static
2837 void add_region_to_list (RegionView* rv, RegionList* l)
2838 {
2839         l->push_back (rv->region());
2840 }
2841
2842 RegionView*
2843 RouteTimeAxisView::combine_regions ()
2844 {
2845         /* as of may 2011, we do not offer uncombine for MIDI tracks
2846          */
2847
2848         if (!is_audio_track()) {
2849                 return 0;
2850         }
2851
2852         if (!_view) {
2853                 return 0;
2854         }
2855
2856         RegionList selected_regions;
2857         boost::shared_ptr<Playlist> playlist = track()->playlist();
2858
2859         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2860
2861         if (selected_regions.size() < 2) {
2862                 return 0;
2863         }
2864
2865         playlist->clear_changes ();
2866         boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2867
2868         _session->add_command (new StatefulDiffCommand (playlist));
2869         /* make the new region be selected */
2870
2871         return _view->find_view (compound_region);
2872 }
2873
2874 void
2875 RouteTimeAxisView::uncombine_regions ()
2876 {
2877         /* as of may 2011, we do not offer uncombine for MIDI tracks
2878          */
2879         if (!is_audio_track()) {
2880                 return;
2881         }
2882
2883         if (!_view) {
2884                 return;
2885         }
2886
2887         RegionList selected_regions;
2888         boost::shared_ptr<Playlist> playlist = track()->playlist();
2889
2890         /* have to grab selected regions first because the uncombine is going
2891          * to change that in the middle of the list traverse
2892          */
2893
2894         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2895
2896         playlist->clear_changes ();
2897
2898         for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2899                 playlist->uncombine (*i);
2900         }
2901
2902         _session->add_command (new StatefulDiffCommand (playlist));
2903 }
2904
2905 string
2906 RouteTimeAxisView::state_id() const
2907 {
2908         return string_compose ("rtav %1", _route->id().to_s());
2909 }
2910
2911
2912 void
2913 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2914 {
2915         TimeAxisView::remove_child (c);
2916
2917         boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2918         if (a) {
2919                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2920                         if (i->second == a) {
2921                                 _automation_tracks.erase (i);
2922                                 return;
2923                         }
2924                 }
2925         }
2926 }
2927
2928 PresentationInfo const &
2929 RouteTimeAxisView::presentation_info () const
2930 {
2931         return _route->presentation_info();
2932 }