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