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