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