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