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