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