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