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