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