enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[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 "pbd/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 boost::shared_ptr<Playlist>
1414 RouteTimeAxisView::playlist () const
1415 {
1416         boost::shared_ptr<Track> tr;
1417
1418         if ((tr = track()) != 0) {
1419                 return tr->playlist();
1420         } else {
1421                 return boost::shared_ptr<Playlist> ();
1422         }
1423 }
1424
1425 bool
1426 RouteTimeAxisView::name_entry_changed (string const& str)
1427 {
1428         if (str == _route->name()) {
1429                 return true;
1430         }
1431
1432         string x = str;
1433         
1434         strip_whitespace_edges (x);
1435
1436         if (x.empty()) {
1437                 return false;
1438         }
1439
1440         if (_session->route_name_internal (x)) {
1441                 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1442                 return false;
1443         } else if (RouteUI::verify_new_route_name (x)) {
1444                 _route->set_name (x);
1445                 return true;
1446         }
1447
1448         return false;
1449 }
1450
1451 boost::shared_ptr<Region>
1452 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1453 {
1454         boost::shared_ptr<Playlist> pl = playlist ();
1455
1456         if (pl) {
1457                 return pl->find_next_region (pos, point, dir);
1458         }
1459
1460         return boost::shared_ptr<Region> ();
1461 }
1462
1463 framepos_t
1464 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1465 {
1466         boost::shared_ptr<Playlist> pl = playlist ();
1467
1468         if (pl) {
1469                 return pl->find_next_region_boundary (pos, dir);
1470         }
1471
1472         return -1;
1473 }
1474
1475 void
1476 RouteTimeAxisView::fade_range (TimeSelection& selection)
1477 {
1478         boost::shared_ptr<Playlist> what_we_got;
1479         boost::shared_ptr<Track> tr = track ();
1480         boost::shared_ptr<Playlist> playlist;
1481
1482         if (tr == 0) {
1483                 /* route is a bus, not a track */
1484                 return;
1485         }
1486
1487         playlist = tr->playlist();
1488
1489         TimeSelection time (selection);
1490         float const speed = tr->speed();
1491         if (speed != 1.0f) {
1492                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1493                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1494                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1495                 }
1496         }
1497
1498         playlist->clear_changes ();
1499         playlist->clear_owned_changes ();
1500
1501         playlist->fade_range (time);
1502
1503         vector<Command*> cmds;
1504         playlist->rdiff (cmds);
1505         _session->add_commands (cmds);
1506         _session->add_command (new StatefulDiffCommand (playlist));
1507
1508 }
1509
1510 void
1511 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1512 {
1513         boost::shared_ptr<Playlist> what_we_got;
1514         boost::shared_ptr<Track> tr = track ();
1515         boost::shared_ptr<Playlist> playlist;
1516
1517         if (tr == 0) {
1518                 /* route is a bus, not a track */
1519                 return;
1520         }
1521
1522         playlist = tr->playlist();
1523
1524         TimeSelection time (selection.time);
1525         float const speed = tr->speed();
1526         if (speed != 1.0f) {
1527                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1528                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1529                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1530                 }
1531         }
1532
1533         playlist->clear_changes ();
1534         playlist->clear_owned_changes ();
1535
1536         switch (op) {
1537         case Delete:
1538                 if (playlist->cut (time) != 0) {
1539                         if (Config->get_edit_mode() == Ripple)
1540                                 playlist->ripple(time.start(), -time.length(), NULL);
1541                                 // no need to exclude any regions from rippling here
1542
1543                         vector<Command*> cmds;
1544                         playlist->rdiff (cmds);
1545                         _session->add_commands (cmds);
1546
1547                         _session->add_command (new StatefulDiffCommand (playlist));
1548                 }
1549                 break;
1550
1551         case Cut:
1552                 if ((what_we_got = playlist->cut (time)) != 0) {
1553                         _editor.get_cut_buffer().add (what_we_got);
1554                         if (Config->get_edit_mode() == Ripple)
1555                                 playlist->ripple(time.start(), -time.length(), NULL);
1556                                 // no need to exclude any regions from rippling here
1557
1558                         vector<Command*> cmds;
1559                         playlist->rdiff (cmds);
1560                         _session->add_commands (cmds);
1561
1562                         _session->add_command (new StatefulDiffCommand (playlist));
1563                 }
1564                 break;
1565         case Copy:
1566                 if ((what_we_got = playlist->copy (time)) != 0) {
1567                         _editor.get_cut_buffer().add (what_we_got);
1568                 }
1569                 break;
1570
1571         case Clear:
1572                 if ((what_we_got = playlist->cut (time)) != 0) {
1573                         if (Config->get_edit_mode() == Ripple)
1574                                 playlist->ripple(time.start(), -time.length(), NULL);
1575                                 // no need to exclude any regions from rippling here
1576
1577                         vector<Command*> cmds;
1578                         playlist->rdiff (cmds);
1579                         _session->add_commands (cmds);
1580                         _session->add_command (new StatefulDiffCommand (playlist));
1581                         what_we_got->release ();
1582                 }
1583                 break;
1584         }
1585 }
1586
1587 bool
1588 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1589 {
1590         if (!is_track()) {
1591                 return false;
1592         }
1593
1594         boost::shared_ptr<Playlist>       pl   = playlist ();
1595         const ARDOUR::DataType            type = pl->data_type();
1596         PlaylistSelection::const_iterator p    = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1597
1598         if (p == selection.playlists.end()) {
1599                 return false;
1600         }
1601         ctx.counts.increase_n_playlists(type);
1602
1603         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1604
1605         if (track()->speed() != 1.0f) {
1606                 pos = session_frame_to_track_frame (pos, track()->speed());
1607                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1608         }
1609
1610         /* add multi-paste offset if applicable */
1611         std::pair<framepos_t, framepos_t> extent   = (*p)->get_extent();
1612         const framecnt_t                  duration = extent.second - extent.first;
1613         pos += _editor.get_paste_offset(pos, ctx.count, duration);
1614
1615         pl->clear_changes ();
1616         pl->clear_owned_changes ();
1617         if (Config->get_edit_mode() == Ripple) {
1618                 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1619                 framecnt_t amount = extent.second - extent.first;
1620                 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1621         }
1622         pl->paste (*p, pos, ctx.times, sub_num);
1623
1624         vector<Command*> cmds;
1625         pl->rdiff (cmds);
1626         _session->add_commands (cmds);
1627
1628         _session->add_command (new StatefulDiffCommand (pl));
1629
1630         return true;
1631 }
1632
1633
1634 struct PlaylistSorter {
1635     bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1636             return a->sort_id() < b->sort_id();
1637     }
1638 };
1639
1640 void
1641 RouteTimeAxisView::build_playlist_menu ()
1642 {
1643         using namespace Menu_Helpers;
1644
1645         if (!is_track()) {
1646                 return;
1647         }
1648
1649         delete playlist_action_menu;
1650         playlist_action_menu = new Menu;
1651         playlist_action_menu->set_name ("ArdourContextMenu");
1652
1653         MenuList& playlist_items = playlist_action_menu->items();
1654         playlist_action_menu->set_name ("ArdourContextMenu");
1655         playlist_items.clear();
1656
1657         RadioMenuItem::Group playlist_group;
1658         boost::shared_ptr<Track> tr = track ();
1659
1660         vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1661
1662         /* sort the playlists */
1663         PlaylistSorter cmp;
1664         sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1665
1666         /* add the playlists to the menu */
1667         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1668                 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1669                 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1670                 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1671
1672                 if (tr->playlist()->id() == (*i)->id()) {
1673                         item->set_active();
1674
1675                 }
1676         }
1677
1678         playlist_items.push_back (SeparatorElem());
1679         playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1680         playlist_items.push_back (SeparatorElem());
1681
1682         if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1683                 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1684                 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1685
1686         } else {
1687                 // Use a label which tells the user what is happening
1688                 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1689                 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1690
1691         }
1692
1693         playlist_items.push_back (SeparatorElem());
1694         playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1695         playlist_items.push_back (SeparatorElem());
1696
1697         playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1698 }
1699
1700 void
1701 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1702 {
1703         assert (is_track());
1704
1705         // exit if we were triggered by deactivating the old playlist
1706         if (!item->get_active()) {
1707                 return;
1708         }
1709
1710         boost::shared_ptr<Playlist> pl (wpl.lock());
1711
1712         if (!pl) {
1713                 return;
1714         }
1715
1716         if (track()->playlist() == pl) {
1717                 // exit when use_playlist is called by the creation of the playlist menu
1718                 // or the playlist choice is unchanged
1719                 return;
1720         }
1721
1722         track()->use_playlist (pl);
1723
1724         RouteGroup* rg = route_group();
1725
1726         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1727                 std::string group_string = "." + rg->name() + ".";
1728
1729                 std::string take_name = pl->name();
1730                 std::string::size_type idx = take_name.find(group_string);
1731
1732                 if (idx == std::string::npos)
1733                         return;
1734
1735                 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1736
1737                 boost::shared_ptr<RouteList> rl (rg->route_list());
1738
1739                 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1740                         if ((*i) == this->route()) {
1741                                 continue;
1742                         }
1743
1744                         std::string playlist_name = (*i)->name()+group_string+take_name;
1745
1746                         boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1747                         if (!track) {
1748                                 continue;
1749                         }
1750
1751                         if (track->freeze_state() == Track::Frozen) {
1752                                 /* Don't change playlists of frozen tracks */
1753                                 continue;
1754                         }
1755
1756                         boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1757                         if (!ipl) {
1758                                 // No playlist for this track for this take yet, make it
1759                                 track->use_new_playlist();
1760                                 track->playlist()->set_name(playlist_name);
1761                         } else {
1762                                 track->use_playlist(ipl);
1763                         }
1764                 }
1765         }
1766 }
1767
1768 void
1769 RouteTimeAxisView::update_playlist_tip ()
1770 {
1771         RouteGroup* rg = route_group ();
1772         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1773                 string group_string = "." + rg->name() + ".";
1774
1775                 string take_name = track()->playlist()->name();
1776                 string::size_type idx = take_name.find(group_string);
1777
1778                 if (idx != string::npos) {
1779                         /* find the bit containing the take number / name */
1780                         take_name = take_name.substr (idx + group_string.length());
1781
1782                         /* set the playlist button tooltip to the take name */
1783                         set_tooltip (
1784                                 playlist_button,
1785                                 string_compose(_("Take: %1.%2"),
1786                                         Gtkmm2ext::markup_escape_text (rg->name()),
1787                                         Gtkmm2ext::markup_escape_text (take_name))
1788                                 );
1789
1790                         return;
1791                 }
1792         }
1793
1794         /* set the playlist button tooltip to the playlist name */
1795         set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1796 }
1797
1798
1799 void
1800 RouteTimeAxisView::show_playlist_selector ()
1801 {
1802         _editor.playlist_selector().show_for (this);
1803 }
1804
1805 void
1806 RouteTimeAxisView::map_frozen ()
1807 {
1808         if (!is_track()) {
1809                 return;
1810         }
1811
1812         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1813
1814         switch (track()->freeze_state()) {
1815         case Track::Frozen:
1816                 playlist_button.set_sensitive (false);
1817                 break;
1818         default:
1819                 playlist_button.set_sensitive (true);
1820                 break;
1821         }
1822         RouteUI::map_frozen ();
1823 }
1824
1825 void
1826 RouteTimeAxisView::color_handler ()
1827 {
1828         //case cTimeStretchOutline:
1829         if (timestretch_rect) {
1830                 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1831         }
1832         //case cTimeStretchFill:
1833         if (timestretch_rect) {
1834                 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1835         }
1836
1837         reset_meter();
1838 }
1839
1840 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1841  *  Will add track if necessary.
1842  */
1843 void
1844 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1845 {
1846         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1847         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1848
1849         if (!track) {
1850                 /* it doesn't exist yet, so we don't care about the button state: just add it */
1851                 create_automation_child (param, true);
1852         } else {
1853                 assert (menu);
1854                 bool yn = menu->get_active();
1855                 bool changed = false;
1856
1857                 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1858
1859                         /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1860                            will have done that for us.
1861                         */
1862
1863                         if (changed && !no_redraw) {
1864                                 request_redraw ();
1865                         }
1866                 }
1867         }
1868 }
1869
1870 void
1871 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1872 {
1873         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1874
1875         if (!track) {
1876                 return;
1877         }
1878
1879         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1880
1881         if (menu && !_hidden) {
1882                 ignore_toggle = true;
1883                 menu->set_active (false);
1884                 ignore_toggle = false;
1885         }
1886
1887         if (_route && !no_redraw) {
1888                 request_redraw ();
1889         }
1890 }
1891
1892 void
1893 RouteTimeAxisView::update_gain_track_visibility ()
1894 {
1895         bool const showit = gain_automation_item->get_active();
1896
1897         if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1898                 gain_track->set_marked_for_display (showit);
1899
1900                 /* now trigger a redisplay */
1901
1902                 if (!no_redraw) {
1903                          _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1904                 }
1905         }
1906 }
1907
1908 void
1909 RouteTimeAxisView::update_trim_track_visibility ()
1910 {
1911         bool const showit = trim_automation_item->get_active();
1912
1913         if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1914                 trim_track->set_marked_for_display (showit);
1915
1916                 /* now trigger a redisplay */
1917
1918                 if (!no_redraw) {
1919                          _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1920                 }
1921         }
1922 }
1923
1924 void
1925 RouteTimeAxisView::update_mute_track_visibility ()
1926 {
1927         bool const showit = mute_automation_item->get_active();
1928
1929         if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1930                 mute_track->set_marked_for_display (showit);
1931
1932                 /* now trigger a redisplay */
1933
1934                 if (!no_redraw) {
1935                          _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1936                 }
1937         }
1938 }
1939
1940 void
1941 RouteTimeAxisView::update_pan_track_visibility ()
1942 {
1943         bool const showit = pan_automation_item->get_active();
1944         bool changed = false;
1945
1946         for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1947                 if ((*i)->set_marked_for_display (showit)) {
1948                         changed = true;
1949                 }
1950         }
1951
1952         if (changed) {
1953                 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1954         }
1955 }
1956
1957 void
1958 RouteTimeAxisView::ensure_pan_views (bool show)
1959 {
1960         bool changed = false;
1961         for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1962                 changed = true;
1963                 (*i)->set_marked_for_display (false);
1964         }
1965         if (changed) {
1966                 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1967         }
1968         pan_tracks.clear();
1969
1970         if (!_route->panner()) {
1971                 return;
1972         }
1973
1974         set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1975         set<Evoral::Parameter>::iterator p;
1976
1977         for (p = params.begin(); p != params.end(); ++p) {
1978                 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1979
1980                 if (pan_control->parameter().type() == NullAutomation) {
1981                         error << "Pan control has NULL automation type!" << endmsg;
1982                         continue;
1983                 }
1984
1985                 if (automation_child (pan_control->parameter ()).get () == 0) {
1986
1987                         /* we don't already have an AutomationTimeAxisView for this parameter */
1988
1989                         std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1990
1991                         boost::shared_ptr<AutomationTimeAxisView> t (
1992                                         new AutomationTimeAxisView (_session,
1993                                                 _route,
1994                                                 _route->pannable(),
1995                                                 pan_control,
1996                                                 pan_control->parameter (),
1997                                                 _editor,
1998                                                 *this,
1999                                                 false,
2000                                                 parent_canvas,
2001                                                 name)
2002                                         );
2003
2004                         pan_tracks.push_back (t);
2005                         add_automation_child (*p, t, show);
2006                 } else {
2007                         pan_tracks.push_back (automation_child (pan_control->parameter ()));
2008                 }
2009         }
2010 }
2011
2012
2013 void
2014 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2015 {
2016         if (apply_to_selection) {
2017                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2018         } else {
2019                 no_redraw = true;
2020
2021                 /* Show our automation */
2022
2023                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2024                         i->second->set_marked_for_display (true);
2025
2026                         Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2027
2028                         if (menu) {
2029                                 menu->set_active(true);
2030                         }
2031                 }
2032
2033
2034                 /* Show processor automation */
2035
2036                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2037                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2038                                 if ((*ii)->view == 0) {
2039                                         add_processor_automation_curve ((*i)->processor, (*ii)->what);
2040                                 }
2041
2042                                 (*ii)->menu_item->set_active (true);
2043                         }
2044                 }
2045
2046                 no_redraw = false;
2047
2048                 /* Redraw */
2049
2050                 request_redraw ();
2051         }
2052 }
2053
2054 void
2055 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2056 {
2057         if (apply_to_selection) {
2058                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2059         } else {
2060                 no_redraw = true;
2061
2062                 /* Show our automation */
2063
2064                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2065                         if (i->second->has_automation()) {
2066                                 i->second->set_marked_for_display (true);
2067
2068                                 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2069                                 if (menu) {
2070                                         menu->set_active(true);
2071                                 }
2072                         }
2073                 }
2074
2075                 /* Show processor automation */
2076
2077                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2078                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2079                                 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2080                                         (*ii)->menu_item->set_active (true);
2081                                 }
2082                         }
2083                 }
2084
2085                 no_redraw = false;
2086
2087                 request_redraw ();
2088         }
2089 }
2090
2091 void
2092 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2093 {
2094         if (apply_to_selection) {
2095                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2096         } else {
2097                 no_redraw = true;
2098
2099                 /* Hide our automation */
2100
2101                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2102                         i->second->set_marked_for_display (false);
2103
2104                         Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2105
2106                         if (menu) {
2107                                 menu->set_active (false);
2108                         }
2109                 }
2110
2111                 /* Hide processor automation */
2112
2113                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2114                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2115                                 (*ii)->menu_item->set_active (false);
2116                         }
2117                 }
2118
2119                 no_redraw = false;
2120                 request_redraw ();
2121         }
2122 }
2123
2124
2125 void
2126 RouteTimeAxisView::region_view_added (RegionView* rv)
2127 {
2128         /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2129         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2130                 boost::shared_ptr<AutomationTimeAxisView> atv;
2131
2132                 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2133                         atv->add_ghost(rv);
2134                 }
2135         }
2136
2137         for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2138                 (*i)->add_ghost(rv);
2139         }
2140 }
2141
2142 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2143 {
2144         for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2145                 delete *i;
2146         }
2147 }
2148
2149
2150 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2151 {
2152         parent.remove_processor_automation_node (this);
2153 }
2154
2155 void
2156 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2157 {
2158         if (pan->view) {
2159                 remove_child (pan->view);
2160         }
2161 }
2162
2163 RouteTimeAxisView::ProcessorAutomationNode*
2164 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2165 {
2166         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2167
2168                 if ((*i)->processor == processor) {
2169
2170                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2171                                 if ((*ii)->what == what) {
2172                                         return *ii;
2173                                 }
2174                         }
2175                 }
2176         }
2177
2178         return 0;
2179 }
2180
2181 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2182 void
2183 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2184 {
2185         string name;
2186         ProcessorAutomationNode* pan;
2187
2188         if ((pan = find_processor_automation_node (processor, what)) == 0) {
2189                 /* session state may never have been saved with new plugin */
2190                 error << _("programming error: ")
2191                       << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2192                                          processor->name(), what.type(), (int) what.channel(), what.id() )
2193                       << endmsg;
2194                 abort(); /*NOTREACHED*/
2195                 return;
2196         }
2197
2198         if (pan->view) {
2199                 return;
2200         }
2201
2202         boost::shared_ptr<AutomationControl> control
2203                 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2204
2205         pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2206                 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2207                                             _editor, *this, false, parent_canvas,
2208                                             processor->describe_parameter (what), processor->name()));
2209
2210         pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2211
2212         add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2213
2214         if (_view) {
2215                 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2216         }
2217 }
2218
2219 void
2220 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2221 {
2222         if (!_hidden) {
2223                 pan->menu_item->set_active (false);
2224         }
2225
2226         if (!no_redraw) {
2227                 request_redraw ();
2228         }
2229 }
2230
2231 void
2232 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2233 {
2234         boost::shared_ptr<Processor> processor (p.lock ());
2235
2236         if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2237                 /* The Amp processor is a special case and is dealt with separately */
2238                 return;
2239         }
2240
2241         set<Evoral::Parameter> existing;
2242
2243         processor->what_has_data (existing);
2244
2245         for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2246
2247                 Evoral::Parameter param (*i);
2248                 boost::shared_ptr<AutomationLine> al;
2249
2250                 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2251                         al->queue_reset ();
2252                 } else {
2253                         add_processor_automation_curve (processor, param);
2254                 }
2255         }
2256 }
2257
2258 void
2259 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2260 {
2261         using namespace Menu_Helpers;
2262
2263         add_child (track);
2264
2265         track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2266
2267         _automation_tracks[param] = track;
2268
2269         /* existing state overrides "show" argument */
2270         string s = track->gui_property ("visible");
2271         if (!s.empty()) {
2272                 show = string_is_affirmative (s);
2273         }
2274
2275         /* this might or might not change the visibility status, so don't rely on it */
2276         track->set_marked_for_display (show);
2277
2278         if (show && !no_redraw) {
2279                 request_redraw ();
2280         }
2281
2282         if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2283                 /* MIDI-related parameters are always in the menu, there's no
2284                    reason to rebuild the menu just because we added a automation
2285                    lane for one of them. But if we add a non-MIDI automation
2286                    lane, then we need to invalidate the display menu.
2287                 */
2288                 delete display_menu;
2289                 display_menu = 0;
2290         }
2291 }
2292
2293 void
2294 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2295 {
2296         boost::shared_ptr<Processor> processor (p.lock ());
2297
2298         if (!processor || !processor->display_to_user ()) {
2299                 return;
2300         }
2301
2302         /* we use this override to veto the Amp processor from the plugin menu,
2303            as its automation lane can be accessed using the special "Fader" menu
2304            option
2305         */
2306
2307         if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2308                 return;
2309         }
2310
2311         using namespace Menu_Helpers;
2312         ProcessorAutomationInfo *rai;
2313         list<ProcessorAutomationInfo*>::iterator x;
2314
2315         const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2316
2317         if (automatable.empty()) {
2318                 return;
2319         }
2320
2321         for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2322                 if ((*x)->processor == processor) {
2323                         break;
2324                 }
2325         }
2326
2327         if (x == processor_automation.end()) {
2328                 rai = new ProcessorAutomationInfo (processor);
2329                 processor_automation.push_back (rai);
2330         } else {
2331                 rai = *x;
2332         }
2333
2334         /* any older menu was deleted at the top of processors_changed()
2335            when we cleared the subplugin menu.
2336         */
2337
2338         rai->menu = manage (new Menu);
2339         MenuList& items = rai->menu->items();
2340         rai->menu->set_name ("ArdourContextMenu");
2341
2342         items.clear ();
2343
2344         std::set<Evoral::Parameter> has_visible_automation;
2345         AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2346
2347         for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2348
2349                 ProcessorAutomationNode* pan;
2350                 Gtk::CheckMenuItem* mitem;
2351
2352                 string name = processor->describe_parameter (*i);
2353
2354                 if (name == X_("hidden")) {
2355                         continue;
2356                 }
2357
2358                 items.push_back (CheckMenuElem (name));
2359                 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2360
2361                 _subplugin_menu_map[*i] = mitem;
2362
2363                 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2364                         mitem->set_active(true);
2365                 }
2366
2367                 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2368
2369                         /* new item */
2370
2371                         pan = new ProcessorAutomationNode (*i, mitem, *this);
2372
2373                         rai->lines.push_back (pan);
2374
2375                 } else {
2376
2377                         pan->menu_item = mitem;
2378
2379                 }
2380
2381                 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2382         }
2383
2384         if (items.size() == 0) {
2385                 return;
2386         }
2387
2388         /* add the menu for this processor, because the subplugin
2389            menu is always cleared at the top of processors_changed().
2390            this is the result of some poor design in gtkmm and/or
2391            GTK+.
2392         */
2393
2394         subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2395         rai->valid = true;
2396 }
2397
2398 void
2399 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2400                                                RouteTimeAxisView::ProcessorAutomationNode* pan)
2401 {
2402         bool showit = pan->menu_item->get_active();
2403         bool redraw = false;
2404
2405         if (pan->view == 0 && showit) {
2406                 add_processor_automation_curve (rai->processor, pan->what);
2407                 redraw = true;
2408         }
2409
2410         if (pan->view && pan->view->set_marked_for_display (showit)) {
2411                 redraw = true;
2412         }
2413
2414         if (redraw && !no_redraw) {
2415                 request_redraw ();
2416         }
2417 }
2418
2419 void
2420 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2421 {
2422         if (c.type == RouteProcessorChange::MeterPointChange) {
2423                 /* nothing to do if only the meter point has changed */
2424                 return;
2425         }
2426
2427         using namespace Menu_Helpers;
2428
2429         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2430                 (*i)->valid = false;
2431         }
2432
2433         setup_processor_menu_and_curves ();
2434
2435         bool deleted_processor_automation = false;
2436
2437         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2438
2439                 list<ProcessorAutomationInfo*>::iterator tmp;
2440
2441                 tmp = i;
2442                 ++tmp;
2443
2444                 if (!(*i)->valid) {
2445
2446                         delete *i;
2447                         processor_automation.erase (i);
2448                         deleted_processor_automation = true;
2449
2450                 }
2451
2452                 i = tmp;
2453         }
2454
2455         if (deleted_processor_automation && !no_redraw) {
2456                 request_redraw ();
2457         }
2458 }
2459
2460 boost::shared_ptr<AutomationLine>
2461 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2462 {
2463         ProcessorAutomationNode* pan;
2464
2465         if ((pan = find_processor_automation_node (processor, what)) != 0) {
2466                 if (pan->view) {
2467                         pan->view->line();
2468                 }
2469         }
2470
2471         return boost::shared_ptr<AutomationLine>();
2472 }
2473
2474 void
2475 RouteTimeAxisView::reset_processor_automation_curves ()
2476 {
2477         for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2478                 (*i)->reset();
2479         }
2480 }
2481
2482 bool
2483 RouteTimeAxisView::can_edit_name () const
2484 {
2485         /* we do not allow track name changes if it is record enabled
2486          */
2487         boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2488         if (!trk) {
2489                 return true;
2490         }
2491         return !trk->rec_enable_control()->get_value();
2492 }
2493
2494 void
2495 RouteTimeAxisView::blink_rec_display (bool onoff)
2496 {
2497         RouteUI::blink_rec_display (onoff);
2498 }
2499
2500 void
2501 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2502 {
2503         if (_ignore_set_layer_display) {
2504                 return;
2505         }
2506
2507         if (apply_to_selection) {
2508                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2509         } else {
2510
2511                 if (_view) {
2512                         _view->set_layer_display (d);
2513                 }
2514
2515                 set_gui_property (X_("layer-display"), enum_2_string (d));
2516         }
2517 }
2518
2519 LayerDisplay
2520 RouteTimeAxisView::layer_display () const
2521 {
2522         if (_view) {
2523                 return _view->layer_display ();
2524         }
2525
2526         /* we don't know, since we don't have a _view, so just return something */
2527         return Overlaid;
2528 }
2529
2530
2531
2532 boost::shared_ptr<AutomationTimeAxisView>
2533 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2534 {
2535         AutomationTracks::iterator i = _automation_tracks.find(param);
2536         if (i != _automation_tracks.end()) {
2537                 return i->second;
2538         } else {
2539                 return boost::shared_ptr<AutomationTimeAxisView>();
2540         }
2541 }
2542
2543 void
2544 RouteTimeAxisView::fast_update ()
2545 {
2546         gm.get_level_meter().update_meters ();
2547 }
2548
2549 void
2550 RouteTimeAxisView::hide_meter ()
2551 {
2552         clear_meter ();
2553         gm.get_level_meter().hide_meters ();
2554 }
2555
2556 void
2557 RouteTimeAxisView::show_meter ()
2558 {
2559         reset_meter ();
2560 }
2561
2562 void
2563 RouteTimeAxisView::reset_meter ()
2564 {
2565         if (UIConfiguration::instance().get_show_track_meters()) {
2566                 int meter_width = 3;
2567                 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2568                         meter_width = 6;
2569                 }
2570                 gm.get_level_meter().setup_meters (height - 9, meter_width);
2571         } else {
2572                 hide_meter ();
2573         }
2574 }
2575
2576 void
2577 RouteTimeAxisView::clear_meter ()
2578 {
2579         gm.get_level_meter().clear_meters ();
2580 }
2581
2582 void
2583 RouteTimeAxisView::meter_changed ()
2584 {
2585         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2586         reset_meter();
2587         if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2588                 request_redraw ();
2589         }
2590         // reset peak when meter point changes
2591         gm.reset_peak_display();
2592 }
2593
2594 void
2595 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2596 {
2597         reset_meter ();
2598         if (_route && !no_redraw) {
2599                 request_redraw ();
2600         }
2601 }
2602
2603 void
2604 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2605 {
2606         using namespace Menu_Helpers;
2607
2608         if (!_underlay_streams.empty()) {
2609                 MenuList& parent_items = parent_menu->items();
2610                 Menu* gs_menu = manage (new Menu);
2611                 gs_menu->set_name ("ArdourContextMenu");
2612                 MenuList& gs_items = gs_menu->items();
2613
2614                 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2615
2616                 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2617                         gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2618                                                     sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2619                 }
2620         }
2621 }
2622
2623 bool
2624 RouteTimeAxisView::set_underlay_state()
2625 {
2626         if (!underlay_xml_node) {
2627                 return false;
2628         }
2629
2630         XMLNodeList nlist = underlay_xml_node->children();
2631         XMLNodeConstIterator niter;
2632         XMLNode *child_node;
2633
2634         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2635                 child_node = *niter;
2636
2637                 if (child_node->name() != "Underlay") {
2638                         continue;
2639                 }
2640
2641                 XMLProperty const * prop = child_node->property ("id");
2642                 if (prop) {
2643                         PBD::ID id (prop->value());
2644
2645                         RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2646
2647                         if (v) {
2648                                 add_underlay(v->view(), false);
2649                         }
2650                 }
2651         }
2652
2653         return false;
2654 }
2655
2656 void
2657 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2658 {
2659         if (!v) {
2660                 return;
2661         }
2662
2663         RouteTimeAxisView& other = v->trackview();
2664
2665         if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2666                 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2667                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2668                         abort(); /*NOTREACHED*/
2669                 }
2670
2671                 _underlay_streams.push_back(v);
2672                 other._underlay_mirrors.push_back(this);
2673
2674                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2675
2676 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2677                 if (update_xml) {
2678                         if (!underlay_xml_node) {
2679                                 underlay_xml_node = xml_node->add_child("Underlays");
2680                         }
2681
2682                         XMLNode* node = underlay_xml_node->add_child("Underlay");
2683                         XMLProperty const * prop = node->add_property("id");
2684                         prop->set_value(v->trackview().route()->id().to_s());
2685                 }
2686 #endif
2687         }
2688 }
2689
2690 void
2691 RouteTimeAxisView::remove_underlay (StreamView* v)
2692 {
2693         if (!v) {
2694                 return;
2695         }
2696
2697         UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2698         RouteTimeAxisView& other = v->trackview();
2699
2700         if (it != _underlay_streams.end()) {
2701                 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2702
2703                 if (gm == other._underlay_mirrors.end()) {
2704                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2705                         abort(); /*NOTREACHED*/
2706                 }
2707
2708                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2709
2710                 _underlay_streams.erase(it);
2711                 other._underlay_mirrors.erase(gm);
2712
2713                 if (underlay_xml_node) {
2714                         underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2715                 }
2716         }
2717 }
2718
2719 void
2720 RouteTimeAxisView::set_button_names ()
2721 {
2722         if (_route && _route->solo_safe_control()->solo_safe()) {
2723                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2724         } else {
2725                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2726         }
2727         if (Config->get_solo_control_is_listen_control()) {
2728                 switch (Config->get_listen_position()) {
2729                         case AfterFaderListen:
2730                                 solo_button->set_text (S_("AfterFader|A"));
2731                                 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2732                                 break;
2733                         case PreFaderListen:
2734                                 solo_button->set_text (S_("PreFader|P"));
2735                                 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2736                         break;
2737                 }
2738         } else {
2739                 solo_button->set_text (S_("Solo|S"));
2740                 set_tooltip (*solo_button, _("Solo"));
2741         }
2742         mute_button->set_text (S_("Mute|M"));
2743 }
2744
2745 Gtk::CheckMenuItem*
2746 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2747 {
2748         ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2749         if (i != _main_automation_menu_map.end()) {
2750                 return i->second;
2751         }
2752
2753         i = _subplugin_menu_map.find (param);
2754         if (i != _subplugin_menu_map.end()) {
2755                 return i->second;
2756         }
2757
2758         return 0;
2759 }
2760
2761 void
2762 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2763 {
2764         boost::shared_ptr<AutomationControl> c = _route->gain_control();
2765         if (!c) {
2766                 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2767                 return;
2768         }
2769
2770         gain_track.reset (new AutomationTimeAxisView (_session,
2771                                                       _route, _route->amp(), c, param,
2772                                                       _editor,
2773                                                       *this,
2774                                                       false,
2775                                                       parent_canvas,
2776                                                       _route->amp()->describe_parameter(param)));
2777
2778         if (_view) {
2779                 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2780         }
2781
2782         add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2783 }
2784
2785 void
2786 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2787 {
2788         boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2789         if (!c || ! _route->trim()->active()) {
2790                 return;
2791         }
2792
2793         trim_track.reset (new AutomationTimeAxisView (_session,
2794                                                       _route, _route->trim(), c, param,
2795                                                       _editor,
2796                                                       *this,
2797                                                       false,
2798                                                       parent_canvas,
2799                                                       _route->trim()->describe_parameter(param)));
2800
2801         if (_view) {
2802                 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2803         }
2804
2805         add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2806 }
2807
2808 void
2809 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2810 {
2811         boost::shared_ptr<AutomationControl> c = _route->mute_control();
2812         if (!c) {
2813                 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2814                 return;
2815         }
2816
2817         mute_track.reset (new AutomationTimeAxisView (_session,
2818                                                       _route, _route, c, param,
2819                                                       _editor,
2820                                                       *this,
2821                                                       false,
2822                                                       parent_canvas,
2823                                                       _route->describe_parameter(param)));
2824
2825         if (_view) {
2826                 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2827         }
2828
2829         add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2830 }
2831
2832 static
2833 void add_region_to_list (RegionView* rv, RegionList* l)
2834 {
2835         l->push_back (rv->region());
2836 }
2837
2838 RegionView*
2839 RouteTimeAxisView::combine_regions ()
2840 {
2841         /* as of may 2011, we do not offer uncombine for MIDI tracks
2842          */
2843
2844         if (!is_audio_track()) {
2845                 return 0;
2846         }
2847
2848         if (!_view) {
2849                 return 0;
2850         }
2851
2852         RegionList selected_regions;
2853         boost::shared_ptr<Playlist> playlist = track()->playlist();
2854
2855         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2856
2857         if (selected_regions.size() < 2) {
2858                 return 0;
2859         }
2860
2861         playlist->clear_changes ();
2862         boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2863
2864         _session->add_command (new StatefulDiffCommand (playlist));
2865         /* make the new region be selected */
2866
2867         return _view->find_view (compound_region);
2868 }
2869
2870 void
2871 RouteTimeAxisView::uncombine_regions ()
2872 {
2873         /* as of may 2011, we do not offer uncombine for MIDI tracks
2874          */
2875         if (!is_audio_track()) {
2876                 return;
2877         }
2878
2879         if (!_view) {
2880                 return;
2881         }
2882
2883         RegionList selected_regions;
2884         boost::shared_ptr<Playlist> playlist = track()->playlist();
2885
2886         /* have to grab selected regions first because the uncombine is going
2887          * to change that in the middle of the list traverse
2888          */
2889
2890         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2891
2892         playlist->clear_changes ();
2893
2894         for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2895                 playlist->uncombine (*i);
2896         }
2897
2898         _session->add_command (new StatefulDiffCommand (playlist));
2899 }
2900
2901 string
2902 RouteTimeAxisView::state_id() const
2903 {
2904         return string_compose ("rtav %1", _route->id().to_s());
2905 }
2906
2907
2908 void
2909 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2910 {
2911         TimeAxisView::remove_child (c);
2912
2913         boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2914         if (a) {
2915                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2916                         if (i->second == a) {
2917                                 _automation_tracks.erase (i);
2918                                 return;
2919                         }
2920                 }
2921         }
2922 }
2923
2924 Gdk::Color
2925 RouteTimeAxisView::color () const
2926 {
2927         return route_color ();
2928 }
2929
2930 bool
2931 RouteTimeAxisView::marked_for_display () const
2932 {
2933         return !_route->presentation_info().hidden();
2934 }
2935
2936 bool
2937 RouteTimeAxisView::set_marked_for_display (bool yn)
2938 {
2939         return RouteUI::mark_hidden (!yn);
2940 }
2941