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