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