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