Fix session-open after selecting new, template, then back
[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/processor.h"
50 #include "ardour/profile.h"
51 #include "ardour/route_group.h"
52 #include "ardour/session.h"
53 #include "ardour/session_playlists.h"
54 #include "ardour/track.h"
55
56 #include "canvas/debug.h"
57
58 #include "gtkmm2ext/gtk_ui.h"
59 #include "gtkmm2ext/utils.h"
60
61 #include "widgets/ardour_button.h"
62 #include "widgets/prompter.h"
63 #include "widgets/tooltips.h"
64
65 #include "ardour_ui.h"
66 #include "audio_streamview.h"
67 #include "debug.h"
68 #include "enums_convert.h"
69 #include "route_time_axis.h"
70 #include "automation_time_axis.h"
71 #include "enums.h"
72 #include "gui_thread.h"
73 #include "item_counts.h"
74 #include "keyboard.h"
75 #include "paste_context.h"
76 #include "playlist_selector.h"
77 #include "point_selection.h"
78 #include "public_editor.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "streamview.h"
83 #include "ui_config.h"
84 #include "utils.h"
85 #include "route_group_menu.h"
86
87 #include "pbd/i18n.h"
88
89 using namespace ARDOUR;
90 using namespace ArdourWidgets;
91 using namespace PBD;
92 using namespace Gtkmm2ext;
93 using namespace Gtk;
94 using namespace Editing;
95 using namespace std;
96 using std::list;
97
98 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
99         : RouteUI(sess)
100         , StripableTimeAxisView(ed, sess, canvas)
101         , _view (0)
102         , button_table (3, 3)
103         , route_group_button (S_("RTAV|G"))
104         , playlist_button (S_("RTAV|P"))
105         , automation_button (S_("RTAV|A"))
106         , automation_action_menu (0)
107         , plugins_submenu_item (0)
108         , route_group_menu (0)
109         , playlist_action_menu (0)
110         , mode_menu (0)
111         , color_mode_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         playlist_action_menu = 0;
341
342         delete _view;
343         _view = 0;
344
345         _automation_tracks.clear ();
346
347         delete route_group_menu;
348         CatchDeletion (this);
349 }
350
351 string
352 RouteTimeAxisView::name() const
353 {
354         if (_route) {
355                 return _route->name();
356         }
357         return string();
358 }
359
360 void
361 RouteTimeAxisView::post_construct ()
362 {
363         /* map current state of the route */
364
365         update_diskstream_display ();
366         setup_processor_menu_and_curves ();
367         reset_processor_automation_curves ();
368 }
369
370 /** Set up the processor menu for the current set of processors, and
371  *  display automation curves for any parameters which have data.
372  */
373 void
374 RouteTimeAxisView::setup_processor_menu_and_curves ()
375 {
376         _subplugin_menu_map.clear ();
377         subplugin_menu.items().clear ();
378         _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
379         _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
380 }
381
382 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                 }
790
791                 items.push_back (SeparatorElem());
792
793                 build_playlist_menu ();
794                 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
795                 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
796         }
797
798         route_group_menu->detach ();
799
800         WeakRouteList r;
801         for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
802                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
803                 if (rtv) {
804                         r.push_back (rtv->route ());
805                 }
806         }
807
808         if (r.empty ()) {
809                 r.push_back (route ());
810         }
811
812         if (!_route->is_master()) {
813                 route_group_menu->build (r);
814                 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
815         }
816
817         build_automation_action_menu (true);
818         items.push_back (MenuElem (_("Automation"), *automation_action_menu));
819
820         items.push_back (SeparatorElem());
821
822         int active = 0;
823         int inactive = 0;
824         TrackSelection const & s = _editor.get_selection().tracks;
825         for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
826                 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
827                 if (!r) {
828                         continue;
829                 }
830
831                 if (r->route()->active()) {
832                         ++active;
833                 } else {
834                         ++inactive;
835                 }
836         }
837
838         items.push_back (CheckMenuElem (_("Active")));
839         Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
840         bool click_sets_active = true;
841         if (active > 0 && inactive == 0) {
842                 i->set_active (true);
843                 click_sets_active = false;
844         } else if (active > 0 && inactive > 0) {
845                 i->set_inconsistent (true);
846         }
847         i->set_sensitive(! _session->transport_rolling());
848         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
849
850         items.push_back (SeparatorElem());
851         items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
852         if (_route && !_route->is_master()) {
853                 items.push_back (SeparatorElem());
854                 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
855         }
856         items.push_back (SeparatorElem());
857         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
858 }
859
860 void
861 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
862 {
863         TimeAxisView::show_timestretch (start, end, layers, layer);
864
865         hide_timestretch ();
866
867 #if 0
868         if (ts.empty()) {
869                 return;
870         }
871
872
873         /* check that the time selection was made in our route, or our route group.
874            remember that route_group() == 0 implies the route is *not* in a edit group.
875         */
876
877         if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
878                 /* this doesn't apply to us */
879                 return;
880         }
881
882         /* ignore it if our edit group is not active */
883
884         if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
885                 return;
886         }
887 #endif
888
889         if (timestretch_rect == 0) {
890                 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
891                 timestretch_rect->set_fill_color (Gtkmm2ext::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
892                 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
893         }
894
895         timestretch_rect->show ();
896         timestretch_rect->raise_to_top ();
897
898         double const x1 = start / _editor.get_current_zoom();
899         double const x2 = (end - 1) / _editor.get_current_zoom();
900
901         timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
902                                                    x2, current_height() * (layers - layer) / layers));
903 }
904
905 void
906 RouteTimeAxisView::hide_timestretch ()
907 {
908         TimeAxisView::hide_timestretch ();
909
910         if (timestretch_rect) {
911                 timestretch_rect->hide ();
912         }
913 }
914
915 void
916 RouteTimeAxisView::show_selection (TimeSelection& ts)
917 {
918
919 #if 0
920         /* ignore it if our edit group is not active or if the selection was started
921            in some other track or route group (remember that route_group() == 0 means
922            that the track is not in an route group).
923         */
924
925         if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
926             (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
927                 hide_selection ();
928                 return;
929         }
930 #endif
931
932         TimeAxisView::show_selection (ts);
933 }
934
935 void
936 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
937 {
938         int gmlen = h - 9;
939         bool height_changed = (height == 0) || (h != height);
940
941         int meter_width = 3;
942         if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
943                 meter_width = 6;
944         }
945         gm.get_level_meter().setup_meters (gmlen, meter_width);
946
947         TimeAxisView::set_height (h, m);
948
949         if (_view) {
950                 _view->set_height ((double) current_height());
951         }
952
953         if (height >= preset_height (HeightNormal)) {
954
955                 reset_meter();
956
957                 gm.get_gain_slider().show();
958                 mute_button->show();
959                 if (!_route || _route->is_monitor()) {
960                         solo_button->hide();
961                 } else {
962                         solo_button->show();
963                 }
964                 if (rec_enable_button)
965                         rec_enable_button->show();
966
967                 route_group_button.show();
968                 automation_button.show();
969
970                 if (is_track() && track()->mode() == ARDOUR::Normal) {
971                         playlist_button.show();
972                 }
973
974         } else {
975
976                 reset_meter();
977
978                 gm.get_gain_slider().hide();
979                 mute_button->show();
980                 if (!_route || _route->is_monitor()) {
981                         solo_button->hide();
982                 } else {
983                         solo_button->show();
984                 }
985                 if (rec_enable_button)
986                         rec_enable_button->show();
987
988                 route_group_button.hide ();
989                 automation_button.hide ();
990
991                 if (is_track() && track()->mode() == ARDOUR::Normal) {
992                         playlist_button.hide ();
993                 }
994
995         }
996
997         if (height_changed && !no_redraw) {
998                 /* only emit the signal if the height really changed */
999                 request_redraw ();
1000         }
1001 }
1002
1003 void
1004 RouteTimeAxisView::route_color_changed ()
1005 {
1006         using namespace ARDOUR_UI_UTILS;
1007         if (_view) {
1008                 _view->apply_color (color(), StreamView::RegionColor);
1009         }
1010         number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1011 }
1012
1013 void
1014 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1015 {
1016         double speed = 1.0;
1017
1018         if (track()) {
1019                 speed = track()->speed();
1020         }
1021
1022         if (_view) {
1023                 _view->set_samples_per_pixel (fpp * speed);
1024         }
1025
1026         StripableTimeAxisView::set_samples_per_pixel (fpp * speed);
1027 }
1028
1029 void
1030 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1031 {
1032         if (!mitem->get_active()) {
1033                 /* this is one of the two calls made when these radio menu items change status. this one
1034                          is for the item that became inactive, and we want to ignore it.
1035                          */
1036                 return;
1037         }
1038
1039         if (apply_to_selection) {
1040                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1041         } else {
1042                 if (track ()) {
1043                         track()->set_align_choice (choice);
1044                 }
1045         }
1046 }
1047
1048 void
1049 RouteTimeAxisView::rename_current_playlist ()
1050 {
1051         Prompter prompter (true);
1052         string name;
1053
1054         boost::shared_ptr<Track> tr = track();
1055         if (!tr || tr->destructive()) {
1056                 return;
1057         }
1058
1059         boost::shared_ptr<Playlist> pl = tr->playlist();
1060         if (!pl) {
1061                 return;
1062         }
1063
1064         prompter.set_title (_("Rename Playlist"));
1065         prompter.set_prompt (_("New name for playlist:"));
1066         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1067         prompter.set_initial_text (pl->name());
1068         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1069
1070         while (true) {
1071                 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1072                         break;
1073                 }
1074                 prompter.get_result (name);
1075                 if (name.length()) {
1076                         if (_session->playlists->by_name (name)) {
1077                                 MessageDialog msg (_("Given playlist name is not unique."));
1078                                 msg.run ();
1079                                 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1080                         } else {
1081                                 pl->set_name (name);
1082                                 break;
1083                         }
1084                 }
1085         }
1086 }
1087
1088 std::string
1089 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1090 {
1091         std::string ret (basename);
1092
1093         std::string const group_string = "." + route_group()->name() + ".";
1094
1095         // iterate through all playlists
1096         int maxnumber = 0;
1097         for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1098                 std::string tmp = (*i)->name();
1099
1100                 std::string::size_type idx = tmp.find(group_string);
1101                 // find those which belong to this group
1102                 if (idx != string::npos) {
1103                         tmp = tmp.substr(idx + group_string.length());
1104
1105                         // and find the largest current number
1106                         int x = atoi(tmp);
1107                         if (x > maxnumber) {
1108                                 maxnumber = x;
1109                         }
1110                 }
1111         }
1112
1113         maxnumber++;
1114
1115         char buf[32];
1116         snprintf (buf, sizeof(buf), "%d", maxnumber);
1117
1118         ret = this->name() + "." + route_group()->name () + "." + buf;
1119
1120         return ret;
1121 }
1122
1123 void
1124 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1125 {
1126         string name;
1127
1128         boost::shared_ptr<Track> tr = track ();
1129         if (!tr || tr->destructive()) {
1130                 return;
1131         }
1132
1133         boost::shared_ptr<const Playlist> pl = tr->playlist();
1134         if (!pl) {
1135                 return;
1136         }
1137
1138         name = pl->name();
1139
1140         if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1141                 name = resolve_new_group_playlist_name(name,playlists_before_op);
1142         }
1143
1144         while (_session->playlists->by_name(name)) {
1145                 name = Playlist::bump_name (name, *_session);
1146         }
1147
1148         if (prompt) {
1149                 // TODO: The prompter "new" button should be de-activated if the user
1150                 // specifies a playlist name which already exists in the session.
1151
1152                 Prompter prompter (true);
1153
1154                 if (copy) {
1155                         prompter.set_title (_("New Copy Playlist"));
1156                         prompter.set_prompt (_("Name for playlist copy:"));
1157                 } else {
1158                         prompter.set_title (_("New Playlist"));
1159                         prompter.set_prompt (_("Name for new playlist:"));
1160                 }
1161                 prompter.set_initial_text (name);
1162                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1163                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1164                 prompter.show_all ();
1165
1166                 while (true) {
1167                         if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1168                                 return;
1169                         }
1170                         prompter.get_result (name);
1171                         if (name.length()) {
1172                                 if (_session->playlists->by_name (name)) {
1173                                         MessageDialog msg (_("Given playlist name is not unique."));
1174                                         msg.run ();
1175                                         prompter.set_initial_text (Playlist::bump_name (name, *_session));
1176                                 } else {
1177                                         break;
1178                                 }
1179                         }
1180                 }
1181         }
1182
1183         if (name.length()) {
1184                 if (copy) {
1185                         tr->use_copy_playlist ();
1186                 } else {
1187                         tr->use_new_playlist ();
1188                 }
1189                 tr->playlist()->set_name (name);
1190         }
1191 }
1192
1193 void
1194 RouteTimeAxisView::clear_playlist ()
1195 {
1196         boost::shared_ptr<Track> tr = track ();
1197         if (!tr || tr->destructive()) {
1198                 return;
1199         }
1200
1201         boost::shared_ptr<Playlist> pl = tr->playlist();
1202         if (!pl) {
1203                 return;
1204         }
1205
1206         _editor.clear_playlist (pl);
1207 }
1208
1209 void
1210 RouteTimeAxisView::speed_changed ()
1211 {
1212         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1213 }
1214
1215 void
1216 RouteTimeAxisView::update_diskstream_display ()
1217 {
1218         if (!track()) {
1219                 return;
1220         }
1221
1222         map_frozen ();
1223 }
1224
1225 void
1226 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1227 {
1228         if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1229
1230                 /* special case: select/deselect all tracks */
1231
1232                 _editor.begin_reversible_selection_op (X_("Selection Click"));
1233
1234                 if (_editor.get_selection().selected (this)) {
1235                         _editor.get_selection().clear_tracks ();
1236                 } else {
1237                         _editor.select_all_tracks ();
1238                 }
1239
1240                 _editor.commit_reversible_selection_op ();
1241
1242                 return;
1243         }
1244
1245         _editor.begin_reversible_selection_op (X_("Selection Click"));
1246
1247         switch (ArdourKeyboard::selection_type (ev->state)) {
1248         case Selection::Toggle:
1249                 _editor.get_selection().toggle (this);
1250                 break;
1251
1252         case Selection::Set:
1253                 _editor.get_selection().set (this);
1254                 break;
1255
1256         case Selection::Extend:
1257                 _editor.extend_selection_to_track (*this);
1258                 break;
1259
1260         case Selection::Add:
1261                 _editor.get_selection().add (this);
1262                 break;
1263         }
1264
1265         _editor.commit_reversible_selection_op ();
1266
1267         _editor.set_selected_mixer_strip (*this);
1268 }
1269
1270 void
1271 RouteTimeAxisView::set_selected_points (PointSelection& points)
1272 {
1273         StripableTimeAxisView::set_selected_points (points);
1274         AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1275         if (asv) {
1276                 asv->set_selected_points (points);
1277         }
1278 }
1279
1280 void
1281 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1282 {
1283         if (_view) {
1284                 _view->set_selected_regionviews (regions);
1285         }
1286 }
1287
1288 /** Add the selectable things that we have to a list.
1289  * @param results List to add things to.
1290  */
1291 void
1292 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1293 {
1294         double speed = 1.0;
1295
1296         if (track() != 0) {
1297                 speed = track()->speed();
1298         }
1299
1300         framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1301         framepos_t const end_adjusted   = session_frame_to_track_frame(end, speed);
1302
1303         if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1304                 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1305         }
1306
1307         /* pick up visible automation tracks */
1308         StripableTimeAxisView::get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1309 }
1310
1311 void
1312 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1313 {
1314         if (_view) {
1315                 _view->get_inverted_selectables (sel, results);
1316         }
1317         StripableTimeAxisView::get_inverted_selectables (sel, results);
1318 }
1319
1320 RouteGroup*
1321 RouteTimeAxisView::route_group () const
1322 {
1323         return _route->route_group();
1324 }
1325
1326 boost::shared_ptr<Playlist>
1327 RouteTimeAxisView::playlist () const
1328 {
1329         boost::shared_ptr<Track> tr;
1330
1331         if ((tr = track()) != 0) {
1332                 return tr->playlist();
1333         } else {
1334                 return boost::shared_ptr<Playlist> ();
1335         }
1336 }
1337
1338 bool
1339 RouteTimeAxisView::name_entry_changed (string const& str)
1340 {
1341         if (str == _route->name()) {
1342                 return true;
1343         }
1344
1345         string x = str;
1346
1347         strip_whitespace_edges (x);
1348
1349         if (x.empty()) {
1350                 return false;
1351         }
1352
1353         if (_session->route_name_internal (x)) {
1354                 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1355                 return false;
1356         } else if (RouteUI::verify_new_route_name (x)) {
1357                 _route->set_name (x);
1358                 return true;
1359         }
1360
1361         return false;
1362 }
1363
1364 boost::shared_ptr<Region>
1365 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1366 {
1367         boost::shared_ptr<Playlist> pl = playlist ();
1368
1369         if (pl) {
1370                 return pl->find_next_region (pos, point, dir);
1371         }
1372
1373         return boost::shared_ptr<Region> ();
1374 }
1375
1376 framepos_t
1377 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1378 {
1379         boost::shared_ptr<Playlist> pl = playlist ();
1380
1381         if (pl) {
1382                 return pl->find_next_region_boundary (pos, dir);
1383         }
1384
1385         return -1;
1386 }
1387
1388 void
1389 RouteTimeAxisView::fade_range (TimeSelection& selection)
1390 {
1391         boost::shared_ptr<Playlist> what_we_got;
1392         boost::shared_ptr<Track> tr = track ();
1393         boost::shared_ptr<Playlist> playlist;
1394
1395         if (tr == 0) {
1396                 /* route is a bus, not a track */
1397                 return;
1398         }
1399
1400         playlist = tr->playlist();
1401
1402         TimeSelection time (selection);
1403         float const speed = tr->speed();
1404         if (speed != 1.0f) {
1405                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1406                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1407                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1408                 }
1409         }
1410
1411         playlist->clear_changes ();
1412         playlist->clear_owned_changes ();
1413
1414         playlist->fade_range (time);
1415
1416         vector<Command*> cmds;
1417         playlist->rdiff (cmds);
1418         _session->add_commands (cmds);
1419         _session->add_command (new StatefulDiffCommand (playlist));
1420
1421 }
1422
1423 void
1424 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1425 {
1426         boost::shared_ptr<Playlist> what_we_got;
1427         boost::shared_ptr<Track> tr = track ();
1428         boost::shared_ptr<Playlist> playlist;
1429
1430         if (tr == 0) {
1431                 /* route is a bus, not a track */
1432                 return;
1433         }
1434
1435         playlist = tr->playlist();
1436
1437         TimeSelection time (selection.time);
1438         float const speed = tr->speed();
1439         if (speed != 1.0f) {
1440                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1441                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1442                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1443                 }
1444         }
1445
1446         playlist->clear_changes ();
1447         playlist->clear_owned_changes ();
1448
1449         switch (op) {
1450         case Delete:
1451                 if (playlist->cut (time) != 0) {
1452                         if (Config->get_edit_mode() == Ripple) {
1453                                 playlist->ripple(time.start(), -time.length(), NULL);
1454                         }
1455                         // no need to exclude any regions from rippling here
1456
1457                         vector<Command*> cmds;
1458                         playlist->rdiff (cmds);
1459                         _session->add_commands (cmds);
1460
1461                         _session->add_command (new StatefulDiffCommand (playlist));
1462                 }
1463                 break;
1464
1465         case Cut:
1466                 if ((what_we_got = playlist->cut (time)) != 0) {
1467                         _editor.get_cut_buffer().add (what_we_got);
1468                         if (Config->get_edit_mode() == Ripple) {
1469                                 playlist->ripple(time.start(), -time.length(), NULL);
1470                         }
1471                         // no need to exclude any regions from rippling here
1472
1473                         vector<Command*> cmds;
1474                         playlist->rdiff (cmds);
1475                         _session->add_commands (cmds);
1476
1477                         _session->add_command (new StatefulDiffCommand (playlist));
1478                 }
1479                 break;
1480         case Copy:
1481                 if ((what_we_got = playlist->copy (time)) != 0) {
1482                         _editor.get_cut_buffer().add (what_we_got);
1483                 }
1484                 break;
1485
1486         case Clear:
1487                 if ((what_we_got = playlist->cut (time)) != 0) {
1488                         if (Config->get_edit_mode() == Ripple) {
1489                                 playlist->ripple(time.start(), -time.length(), NULL);
1490                         }
1491                         // no need to exclude any regions from rippling here
1492
1493                         vector<Command*> cmds;
1494                         playlist->rdiff (cmds);
1495                         _session->add_commands (cmds);
1496                         _session->add_command (new StatefulDiffCommand (playlist));
1497                         what_we_got->release ();
1498                 }
1499                 break;
1500         }
1501 }
1502
1503 bool
1504 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1505 {
1506         if (!is_track()) {
1507                 return false;
1508         }
1509
1510         boost::shared_ptr<Playlist>       pl   = playlist ();
1511         const ARDOUR::DataType            type = pl->data_type();
1512         PlaylistSelection::const_iterator p    = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1513
1514         if (p == selection.playlists.end()) {
1515                 return false;
1516         }
1517         ctx.counts.increase_n_playlists(type);
1518
1519         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1520
1521         if (track()->speed() != 1.0f) {
1522                 pos = session_frame_to_track_frame (pos, track()->speed());
1523                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1524         }
1525
1526         /* add multi-paste offset if applicable */
1527         std::pair<framepos_t, framepos_t> extent   = (*p)->get_extent();
1528         const framecnt_t                  duration = extent.second - extent.first;
1529         pos += _editor.get_paste_offset(pos, ctx.count, duration);
1530
1531         pl->clear_changes ();
1532         pl->clear_owned_changes ();
1533         if (Config->get_edit_mode() == Ripple) {
1534                 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1535                 framecnt_t amount = extent.second - extent.first;
1536                 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1537         }
1538         pl->paste (*p, pos, ctx.times, sub_num);
1539
1540         vector<Command*> cmds;
1541         pl->rdiff (cmds);
1542         _session->add_commands (cmds);
1543
1544         _session->add_command (new StatefulDiffCommand (pl));
1545
1546         return true;
1547 }
1548
1549
1550 struct PlaylistSorter {
1551         bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1552                 return a->sort_id() < b->sort_id();
1553         }
1554 };
1555
1556 void
1557 RouteTimeAxisView::build_playlist_menu ()
1558 {
1559         using namespace Menu_Helpers;
1560
1561         if (!is_track()) {
1562                 return;
1563         }
1564
1565         delete playlist_action_menu;
1566         playlist_action_menu = new Menu;
1567         playlist_action_menu->set_name ("ArdourContextMenu");
1568
1569         MenuList& playlist_items = playlist_action_menu->items();
1570         playlist_action_menu->set_name ("ArdourContextMenu");
1571         playlist_items.clear();
1572
1573         RadioMenuItem::Group playlist_group;
1574         boost::shared_ptr<Track> tr = track ();
1575
1576         vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1577
1578         /* sort the playlists */
1579         PlaylistSorter cmp;
1580         sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1581
1582         /* add the playlists to the menu */
1583         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1584                 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1585                 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1586                 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1587
1588                 if (tr->playlist()->id() == (*i)->id()) {
1589                         item->set_active();
1590                 }
1591         }
1592
1593         playlist_items.push_back (SeparatorElem());
1594         playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1595         playlist_items.push_back (SeparatorElem());
1596
1597         if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1598                 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1599                 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1600
1601         } else {
1602                 // Use a label which tells the user what is happening
1603                 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1604                 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1605
1606         }
1607
1608         playlist_items.push_back (SeparatorElem());
1609         playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1610         playlist_items.push_back (SeparatorElem());
1611
1612         playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1613 }
1614
1615 void
1616 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1617 {
1618         assert (is_track());
1619
1620         // exit if we were triggered by deactivating the old playlist
1621         if (!item->get_active()) {
1622                 return;
1623         }
1624
1625         boost::shared_ptr<Playlist> pl (wpl.lock());
1626
1627         if (!pl) {
1628                 return;
1629         }
1630
1631         if (track()->playlist() == pl) {
1632                 // exit when use_playlist is called by the creation of the playlist menu
1633                 // or the playlist choice is unchanged
1634                 return;
1635         }
1636
1637         track()->use_playlist (pl);
1638
1639         RouteGroup* rg = route_group();
1640
1641         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1642                 std::string group_string = "." + rg->name() + ".";
1643
1644                 std::string take_name = pl->name();
1645                 std::string::size_type idx = take_name.find(group_string);
1646
1647                 if (idx == std::string::npos)
1648                         return;
1649
1650                 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1651
1652                 boost::shared_ptr<RouteList> rl (rg->route_list());
1653
1654                 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1655                         if ((*i) == this->route()) {
1656                                 continue;
1657                         }
1658
1659                         std::string playlist_name = (*i)->name()+group_string+take_name;
1660
1661                         boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1662                         if (!track) {
1663                                 continue;
1664                         }
1665
1666                         if (track->freeze_state() == Track::Frozen) {
1667                                 /* Don't change playlists of frozen tracks */
1668                                 continue;
1669                         }
1670
1671                         boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1672                         if (!ipl) {
1673                                 // No playlist for this track for this take yet, make it
1674                                 track->use_new_playlist();
1675                                 track->playlist()->set_name(playlist_name);
1676                         } else {
1677                                 track->use_playlist(ipl);
1678                         }
1679                 }
1680         }
1681 }
1682
1683 void
1684 RouteTimeAxisView::update_playlist_tip ()
1685 {
1686         RouteGroup* rg = route_group ();
1687         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1688                 string group_string = "." + rg->name() + ".";
1689
1690                 string take_name = track()->playlist()->name();
1691                 string::size_type idx = take_name.find(group_string);
1692
1693                 if (idx != string::npos) {
1694                         /* find the bit containing the take number / name */
1695                         take_name = take_name.substr (idx + group_string.length());
1696
1697                         /* set the playlist button tooltip to the take name */
1698                         set_tooltip (
1699                                 playlist_button,
1700                                 string_compose(_("Take: %1.%2"),
1701                                         Gtkmm2ext::markup_escape_text (rg->name()),
1702                                         Gtkmm2ext::markup_escape_text (take_name))
1703                                 );
1704
1705                         return;
1706                 }
1707         }
1708
1709         /* set the playlist button tooltip to the playlist name */
1710         set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1711 }
1712
1713
1714 void
1715 RouteTimeAxisView::show_playlist_selector ()
1716 {
1717         _editor.playlist_selector().show_for (this);
1718 }
1719
1720 void
1721 RouteTimeAxisView::map_frozen ()
1722 {
1723         if (!is_track()) {
1724                 return;
1725         }
1726
1727         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1728
1729         switch (track()->freeze_state()) {
1730         case Track::Frozen:
1731                 playlist_button.set_sensitive (false);
1732                 break;
1733         default:
1734                 playlist_button.set_sensitive (true);
1735                 break;
1736         }
1737         RouteUI::map_frozen ();
1738 }
1739
1740 void
1741 RouteTimeAxisView::color_handler ()
1742 {
1743         //case cTimeStretchOutline:
1744         if (timestretch_rect) {
1745                 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1746         }
1747         //case cTimeStretchFill:
1748         if (timestretch_rect) {
1749                 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1750         }
1751
1752         reset_meter();
1753 }
1754
1755 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1756  *  Will add track if necessary.
1757  */
1758 void
1759 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1760 {
1761         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1762         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1763
1764         if (!track) {
1765                 /* it doesn't exist yet, so we don't care about the button state: just add it */
1766                 create_automation_child (param, true);
1767         } else {
1768                 assert (menu);
1769                 bool yn = menu->get_active();
1770                 bool changed = false;
1771
1772                 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1773
1774                         /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1775                            will have done that for us.
1776                         */
1777
1778                         if (changed && !no_redraw) {
1779                                 request_redraw ();
1780                         }
1781                 }
1782         }
1783 }
1784
1785 void
1786 RouteTimeAxisView::update_pan_track_visibility ()
1787 {
1788         bool const showit = pan_automation_item->get_active();
1789         bool changed = false;
1790
1791         for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1792                 if ((*i)->set_marked_for_display (showit)) {
1793                         changed = true;
1794                 }
1795         }
1796
1797         if (changed) {
1798                 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1799         }
1800 }
1801
1802 void
1803 RouteTimeAxisView::ensure_pan_views (bool show)
1804 {
1805         bool changed = false;
1806         for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1807                 changed = true;
1808                 (*i)->set_marked_for_display (false);
1809         }
1810         if (changed) {
1811                 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1812         }
1813         pan_tracks.clear();
1814
1815         if (!_route->panner()) {
1816                 return;
1817         }
1818
1819         set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1820         set<Evoral::Parameter>::iterator p;
1821
1822         for (p = params.begin(); p != params.end(); ++p) {
1823                 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1824
1825                 if (pan_control->parameter().type() == NullAutomation) {
1826                         error << "Pan control has NULL automation type!" << endmsg;
1827                         continue;
1828                 }
1829
1830                 if (automation_child (pan_control->parameter ()).get () == 0) {
1831
1832                         /* we don't already have an AutomationTimeAxisView for this parameter */
1833
1834                         std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1835
1836                         boost::shared_ptr<AutomationTimeAxisView> t (
1837                                         new AutomationTimeAxisView (_session,
1838                                                 _route,
1839                                                 _route->pannable(),
1840                                                 pan_control,
1841                                                 pan_control->parameter (),
1842                                                 _editor,
1843                                                 *this,
1844                                                 false,
1845                                                 parent_canvas,
1846                                                 name)
1847                                         );
1848
1849                         pan_tracks.push_back (t);
1850                         add_automation_child (*p, t, show);
1851                 } else {
1852                         pan_tracks.push_back (automation_child (pan_control->parameter ()));
1853                 }
1854         }
1855 }
1856
1857
1858 void
1859 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1860 {
1861         if (apply_to_selection) {
1862                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1863         } else {
1864                 no_redraw = true;
1865
1866                 StripableTimeAxisView::show_all_automation ();
1867
1868                 /* Show processor automation */
1869
1870                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1871                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1872                                 if ((*ii)->view == 0) {
1873                                         add_processor_automation_curve ((*i)->processor, (*ii)->what);
1874                                 }
1875
1876                                 (*ii)->menu_item->set_active (true);
1877                         }
1878                 }
1879
1880                 no_redraw = false;
1881
1882                 /* Redraw */
1883
1884                 request_redraw ();
1885         }
1886 }
1887
1888 void
1889 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1890 {
1891         if (apply_to_selection) {
1892                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1893         } else {
1894                 no_redraw = true;
1895
1896                 StripableTimeAxisView::show_existing_automation ();
1897
1898                 /* Show processor automation */
1899                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1900                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1901                                 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
1902                                         (*ii)->menu_item->set_active (true);
1903                                 }
1904                         }
1905                 }
1906
1907                 no_redraw = false;
1908                 request_redraw ();
1909         }
1910 }
1911
1912 void
1913 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1914 {
1915         if (apply_to_selection) {
1916                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1917         } else {
1918                 no_redraw = true;
1919                 StripableTimeAxisView::hide_all_automation ();
1920
1921                 /* Hide processor automation */
1922                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1923                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1924                                 (*ii)->menu_item->set_active (false);
1925                         }
1926                 }
1927
1928                 no_redraw = false;
1929                 request_redraw ();
1930         }
1931 }
1932
1933 void
1934 RouteTimeAxisView::region_view_added (RegionView* rv)
1935 {
1936         /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1937         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1938                 boost::shared_ptr<AutomationTimeAxisView> atv;
1939
1940                 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1941                         atv->add_ghost(rv);
1942                 }
1943         }
1944
1945         for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1946                 (*i)->add_ghost(rv);
1947         }
1948 }
1949
1950 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1951 {
1952         for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1953                 delete *i;
1954         }
1955 }
1956
1957
1958 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1959 {
1960         parent.remove_processor_automation_node (this);
1961 }
1962
1963 void
1964 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1965 {
1966         if (pan->view) {
1967                 remove_child (pan->view);
1968         }
1969 }
1970
1971 RouteTimeAxisView::ProcessorAutomationNode*
1972 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1973 {
1974         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1975
1976                 if ((*i)->processor == processor) {
1977
1978                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1979                                 if ((*ii)->what == what) {
1980                                         return *ii;
1981                                 }
1982                         }
1983                 }
1984         }
1985
1986         return 0;
1987 }
1988
1989 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1990 void
1991 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1992 {
1993         string name;
1994         ProcessorAutomationNode* pan;
1995
1996         if ((pan = find_processor_automation_node (processor, what)) == 0) {
1997                 /* session state may never have been saved with new plugin */
1998                 error << _("programming error: ")
1999                       << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2000                                          processor->name(), what.type(), (int) what.channel(), what.id() )
2001                       << endmsg;
2002                 abort(); /*NOTREACHED*/
2003                 return;
2004         }
2005
2006         if (pan->view) {
2007                 return;
2008         }
2009
2010         boost::shared_ptr<AutomationControl> control
2011                 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2012
2013         pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2014                 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2015                                             _editor, *this, false, parent_canvas,
2016                                             processor->describe_parameter (what), processor->name()));
2017
2018         pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2019
2020         add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2021
2022         if (_view) {
2023                 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2024         }
2025 }
2026
2027 void
2028 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2029 {
2030         if (!_hidden) {
2031                 pan->menu_item->set_active (false);
2032         }
2033
2034         if (!no_redraw) {
2035                 request_redraw ();
2036         }
2037 }
2038
2039 void
2040 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2041 {
2042         boost::shared_ptr<Processor> processor (p.lock ());
2043
2044         if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2045                 /* The Amp processor is a special case and is dealt with separately */
2046                 return;
2047         }
2048
2049         set<Evoral::Parameter> existing;
2050
2051         processor->what_has_data (existing);
2052
2053         for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2054
2055                 Evoral::Parameter param (*i);
2056                 boost::shared_ptr<AutomationLine> al;
2057
2058                 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2059                         al->queue_reset ();
2060                 } else {
2061                         add_processor_automation_curve (processor, param);
2062                 }
2063         }
2064 }
2065
2066 void
2067 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2068 {
2069         boost::shared_ptr<Processor> processor (p.lock ());
2070
2071         if (!processor || !processor->display_to_user ()) {
2072                 return;
2073         }
2074
2075         /* we use this override to veto the Amp processor from the plugin menu,
2076            as its automation lane can be accessed using the special "Fader" menu
2077            option
2078         */
2079
2080         if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2081                 return;
2082         }
2083
2084         using namespace Menu_Helpers;
2085         ProcessorAutomationInfo *rai;
2086         list<ProcessorAutomationInfo*>::iterator x;
2087
2088         const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2089
2090         if (automatable.empty()) {
2091                 return;
2092         }
2093
2094         for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2095                 if ((*x)->processor == processor) {
2096                         break;
2097                 }
2098         }
2099
2100         if (x == processor_automation.end()) {
2101                 rai = new ProcessorAutomationInfo (processor);
2102                 processor_automation.push_back (rai);
2103         } else {
2104                 rai = *x;
2105         }
2106
2107         /* any older menu was deleted at the top of processors_changed()
2108            when we cleared the subplugin menu.
2109         */
2110
2111         rai->menu = manage (new Menu);
2112         MenuList& items = rai->menu->items();
2113         rai->menu->set_name ("ArdourContextMenu");
2114
2115         items.clear ();
2116
2117         std::set<Evoral::Parameter> has_visible_automation;
2118         AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2119
2120         for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2121
2122                 ProcessorAutomationNode* pan;
2123                 Gtk::CheckMenuItem* mitem;
2124
2125                 string name = processor->describe_parameter (*i);
2126
2127                 if (name == X_("hidden")) {
2128                         continue;
2129                 }
2130
2131                 items.push_back (CheckMenuElem (name));
2132                 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2133
2134                 _subplugin_menu_map[*i] = mitem;
2135
2136                 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2137                         mitem->set_active(true);
2138                 }
2139
2140                 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2141
2142                         /* new item */
2143
2144                         pan = new ProcessorAutomationNode (*i, mitem, *this);
2145
2146                         rai->lines.push_back (pan);
2147
2148                 } else {
2149
2150                         pan->menu_item = mitem;
2151
2152                 }
2153
2154                 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2155         }
2156
2157         if (items.size() == 0) {
2158                 return;
2159         }
2160
2161         /* add the menu for this processor, because the subplugin
2162            menu is always cleared at the top of processors_changed().
2163            this is the result of some poor design in gtkmm and/or
2164            GTK+.
2165         */
2166
2167         subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2168         rai->valid = true;
2169 }
2170
2171 void
2172 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2173                                                RouteTimeAxisView::ProcessorAutomationNode* pan)
2174 {
2175         bool showit = pan->menu_item->get_active();
2176         bool redraw = false;
2177
2178         if (pan->view == 0 && showit) {
2179                 add_processor_automation_curve (rai->processor, pan->what);
2180                 redraw = true;
2181         }
2182
2183         if (pan->view && pan->view->set_marked_for_display (showit)) {
2184                 redraw = true;
2185         }
2186
2187         if (redraw && !no_redraw) {
2188                 request_redraw ();
2189         }
2190 }
2191
2192 void
2193 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2194 {
2195         if (c.type == RouteProcessorChange::MeterPointChange) {
2196                 /* nothing to do if only the meter point has changed */
2197                 return;
2198         }
2199
2200         using namespace Menu_Helpers;
2201
2202         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2203                 (*i)->valid = false;
2204         }
2205
2206         setup_processor_menu_and_curves ();
2207
2208         bool deleted_processor_automation = false;
2209
2210         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2211
2212                 list<ProcessorAutomationInfo*>::iterator tmp;
2213
2214                 tmp = i;
2215                 ++tmp;
2216
2217                 if (!(*i)->valid) {
2218
2219                         delete *i;
2220                         processor_automation.erase (i);
2221                         deleted_processor_automation = true;
2222
2223                 }
2224
2225                 i = tmp;
2226         }
2227
2228         if (deleted_processor_automation && !no_redraw) {
2229                 request_redraw ();
2230         }
2231 }
2232
2233 boost::shared_ptr<AutomationLine>
2234 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2235 {
2236         ProcessorAutomationNode* pan;
2237
2238         if ((pan = find_processor_automation_node (processor, what)) != 0) {
2239                 if (pan->view) {
2240                         pan->view->line();
2241                 }
2242         }
2243
2244         return boost::shared_ptr<AutomationLine>();
2245 }
2246
2247 void
2248 RouteTimeAxisView::reset_processor_automation_curves ()
2249 {
2250         for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2251                 (*i)->reset();
2252         }
2253 }
2254
2255 bool
2256 RouteTimeAxisView::can_edit_name () const
2257 {
2258         /* we do not allow track name changes if it is record enabled
2259          */
2260         boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2261         if (!trk) {
2262                 return true;
2263         }
2264         return !trk->rec_enable_control()->get_value();
2265 }
2266
2267 void
2268 RouteTimeAxisView::blink_rec_display (bool onoff)
2269 {
2270         RouteUI::blink_rec_display (onoff);
2271 }
2272
2273 void
2274 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2275 {
2276         if (_ignore_set_layer_display) {
2277                 return;
2278         }
2279
2280         if (apply_to_selection) {
2281                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2282         } else {
2283
2284                 if (_view) {
2285                         _view->set_layer_display (d);
2286                 }
2287
2288                 set_gui_property (X_("layer-display"), d);
2289         }
2290 }
2291
2292 LayerDisplay
2293 RouteTimeAxisView::layer_display () const
2294 {
2295         if (_view) {
2296                 return _view->layer_display ();
2297         }
2298
2299         /* we don't know, since we don't have a _view, so just return something */
2300         return Overlaid;
2301 }
2302
2303 void
2304 RouteTimeAxisView::fast_update ()
2305 {
2306         gm.get_level_meter().update_meters ();
2307 }
2308
2309 void
2310 RouteTimeAxisView::hide_meter ()
2311 {
2312         clear_meter ();
2313         gm.get_level_meter().hide_meters ();
2314 }
2315
2316 void
2317 RouteTimeAxisView::show_meter ()
2318 {
2319         reset_meter ();
2320 }
2321
2322 void
2323 RouteTimeAxisView::reset_meter ()
2324 {
2325         if (UIConfiguration::instance().get_show_track_meters()) {
2326                 int meter_width = 3;
2327                 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2328                         meter_width = 6;
2329                 }
2330                 gm.get_level_meter().setup_meters (height - 9, meter_width);
2331         } else {
2332                 hide_meter ();
2333         }
2334 }
2335
2336 void
2337 RouteTimeAxisView::clear_meter ()
2338 {
2339         gm.get_level_meter().clear_meters ();
2340 }
2341
2342 void
2343 RouteTimeAxisView::meter_changed ()
2344 {
2345         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2346         reset_meter();
2347         if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2348                 request_redraw ();
2349         }
2350         // reset peak when meter point changes
2351         gm.reset_peak_display();
2352 }
2353
2354 void
2355 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2356 {
2357         reset_meter ();
2358         if (_route && !no_redraw) {
2359                 request_redraw ();
2360         }
2361 }
2362
2363 void
2364 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2365 {
2366         using namespace Menu_Helpers;
2367
2368         if (!_underlay_streams.empty()) {
2369                 MenuList& parent_items = parent_menu->items();
2370                 Menu* gs_menu = manage (new Menu);
2371                 gs_menu->set_name ("ArdourContextMenu");
2372                 MenuList& gs_items = gs_menu->items();
2373
2374                 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2375
2376                 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2377                         gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2378                                                     sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2379                 }
2380         }
2381 }
2382
2383 bool
2384 RouteTimeAxisView::set_underlay_state()
2385 {
2386         if (!underlay_xml_node) {
2387                 return false;
2388         }
2389
2390         XMLNodeList nlist = underlay_xml_node->children();
2391         XMLNodeConstIterator niter;
2392         XMLNode *child_node;
2393
2394         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2395                 child_node = *niter;
2396
2397                 if (child_node->name() != "Underlay") {
2398                         continue;
2399                 }
2400
2401                 XMLProperty const * prop = child_node->property ("id");
2402                 if (prop) {
2403                         PBD::ID id (prop->value());
2404
2405                         StripableTimeAxisView* v = _editor.get_stripable_time_axis_by_id (id);
2406
2407                         if (v) {
2408                                 add_underlay(v->view(), false);
2409                         }
2410                 }
2411         }
2412
2413         return false;
2414 }
2415
2416 void
2417 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2418 {
2419         if (!v) {
2420                 return;
2421         }
2422
2423         RouteTimeAxisView& other = v->trackview();
2424
2425         if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2426                 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2427                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2428                         abort(); /*NOTREACHED*/
2429                 }
2430
2431                 _underlay_streams.push_back(v);
2432                 other._underlay_mirrors.push_back(this);
2433
2434                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2435
2436 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2437                 if (update_xml) {
2438                         if (!underlay_xml_node) {
2439                                 underlay_xml_node = xml_node->add_child("Underlays");
2440                         }
2441
2442                         XMLNode* node = underlay_xml_node->add_child("Underlay");
2443                         XMLProperty const * prop = node->add_property("id");
2444                         prop->set_value(v->trackview().route()->id().to_s());
2445                 }
2446 #endif
2447         }
2448 }
2449
2450 void
2451 RouteTimeAxisView::remove_underlay (StreamView* v)
2452 {
2453         if (!v) {
2454                 return;
2455         }
2456
2457         UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2458         RouteTimeAxisView& other = v->trackview();
2459
2460         if (it != _underlay_streams.end()) {
2461                 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2462
2463                 if (gm == other._underlay_mirrors.end()) {
2464                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2465                         abort(); /*NOTREACHED*/
2466                 }
2467
2468                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2469
2470                 _underlay_streams.erase(it);
2471                 other._underlay_mirrors.erase(gm);
2472
2473                 if (underlay_xml_node) {
2474                         underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2475                 }
2476         }
2477 }
2478
2479 void
2480 RouteTimeAxisView::set_button_names ()
2481 {
2482         if (_route && _route->solo_safe_control()->solo_safe()) {
2483                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2484         } else {
2485                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2486         }
2487         if (Config->get_solo_control_is_listen_control()) {
2488                 switch (Config->get_listen_position()) {
2489                         case AfterFaderListen:
2490                                 solo_button->set_text (S_("AfterFader|A"));
2491                                 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2492                                 break;
2493                         case PreFaderListen:
2494                                 solo_button->set_text (S_("PreFader|P"));
2495                                 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2496                         break;
2497                 }
2498         } else {
2499                 solo_button->set_text (S_("Solo|S"));
2500                 set_tooltip (*solo_button, _("Solo"));
2501         }
2502         mute_button->set_text (S_("Mute|M"));
2503 }
2504
2505 Gtk::CheckMenuItem*
2506 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2507 {
2508         Gtk::CheckMenuItem* rv = StripableTimeAxisView::automation_child_menu_item (param);
2509         if (rv) {
2510                 return rv;
2511         }
2512
2513         ParameterMenuMap::iterator i = _subplugin_menu_map.find (param);
2514         if (i != _subplugin_menu_map.end()) {
2515                 return i->second;
2516         }
2517
2518         return 0;
2519 }
2520
2521 void
2522 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2523 {
2524         boost::shared_ptr<AutomationControl> c = _route->gain_control();
2525         if (!c) {
2526                 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2527                 return;
2528         }
2529
2530         gain_track.reset (new AutomationTimeAxisView (_session,
2531                                                       _route, _route->amp(), c, param,
2532                                                       _editor,
2533                                                       *this,
2534                                                       false,
2535                                                       parent_canvas,
2536                                                       _route->amp()->describe_parameter(param)));
2537
2538         if (_view) {
2539                 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2540         }
2541
2542         add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2543 }
2544
2545 void
2546 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2547 {
2548         boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2549         if (!c || ! _route->trim()->active()) {
2550                 return;
2551         }
2552
2553         trim_track.reset (new AutomationTimeAxisView (_session,
2554                                                       _route, _route->trim(), c, param,
2555                                                       _editor,
2556                                                       *this,
2557                                                       false,
2558                                                       parent_canvas,
2559                                                       _route->trim()->describe_parameter(param)));
2560
2561         if (_view) {
2562                 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2563         }
2564
2565         add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2566 }
2567
2568 void
2569 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2570 {
2571         boost::shared_ptr<AutomationControl> c = _route->mute_control();
2572         if (!c) {
2573                 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2574                 return;
2575         }
2576
2577         mute_track.reset (new AutomationTimeAxisView (_session,
2578                                                       _route, _route, c, param,
2579                                                       _editor,
2580                                                       *this,
2581                                                       false,
2582                                                       parent_canvas,
2583                                                       _route->describe_parameter(param)));
2584
2585         if (_view) {
2586                 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2587         }
2588
2589         add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2590 }
2591
2592 static
2593 void add_region_to_list (RegionView* rv, RegionList* l)
2594 {
2595         l->push_back (rv->region());
2596 }
2597
2598 RegionView*
2599 RouteTimeAxisView::combine_regions ()
2600 {
2601         /* as of may 2011, we do not offer uncombine for MIDI tracks
2602          */
2603
2604         if (!is_audio_track()) {
2605                 return 0;
2606         }
2607
2608         if (!_view) {
2609                 return 0;
2610         }
2611
2612         RegionList selected_regions;
2613         boost::shared_ptr<Playlist> playlist = track()->playlist();
2614
2615         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2616
2617         if (selected_regions.size() < 2) {
2618                 return 0;
2619         }
2620
2621         playlist->clear_changes ();
2622         boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2623
2624         _session->add_command (new StatefulDiffCommand (playlist));
2625         /* make the new region be selected */
2626
2627         return _view->find_view (compound_region);
2628 }
2629
2630 void
2631 RouteTimeAxisView::uncombine_regions ()
2632 {
2633         /* as of may 2011, we do not offer uncombine for MIDI tracks
2634          */
2635         if (!is_audio_track()) {
2636                 return;
2637         }
2638
2639         if (!_view) {
2640                 return;
2641         }
2642
2643         RegionList selected_regions;
2644         boost::shared_ptr<Playlist> playlist = track()->playlist();
2645
2646         /* have to grab selected regions first because the uncombine is going
2647          * to change that in the middle of the list traverse
2648          */
2649
2650         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2651
2652         playlist->clear_changes ();
2653
2654         for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2655                 playlist->uncombine (*i);
2656         }
2657
2658         _session->add_command (new StatefulDiffCommand (playlist));
2659 }
2660
2661 string
2662 RouteTimeAxisView::state_id() const
2663 {
2664         return string_compose ("rtav %1", _route->id().to_s());
2665 }
2666
2667
2668 void
2669 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2670 {
2671         TimeAxisView::remove_child (c);
2672
2673         boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2674         if (a) {
2675                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2676                         if (i->second == a) {
2677                                 _automation_tracks.erase (i);
2678                                 return;
2679                         }
2680                 }
2681         }
2682 }
2683
2684 Gdk::Color
2685 RouteTimeAxisView::color () const
2686 {
2687         return route_color ();
2688 }
2689
2690 bool
2691 RouteTimeAxisView::marked_for_display () const
2692 {
2693         return !_route->presentation_info().hidden();
2694 }
2695
2696 bool
2697 RouteTimeAxisView::set_marked_for_display (bool yn)
2698 {
2699         return RouteUI::mark_hidden (!yn);
2700 }