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