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