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