Merged with trunk R776
[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
27 #include <sigc++/bind.h>
28
29 #include <pbd/error.h>
30 #include <pbd/stl_delete.h>
31 #include <pbd/whitespace.h>
32 #include <pbd/memento_command.h>
33
34 #include <gtkmm/menu.h>
35 #include <gtkmm/menuitem.h>
36 #include <gtkmm2ext/gtk_ui.h>
37 #include <gtkmm2ext/selector.h>
38 #include <gtkmm2ext/stop_signal.h>
39 #include <gtkmm2ext/bindable_button.h>
40 #include <gtkmm2ext/utils.h>
41
42 #include <ardour/playlist.h>
43 #include <ardour/diskstream.h>
44 #include <ardour/insert.h>
45 #include <ardour/ladspa_plugin.h>
46 #include <ardour/location.h>
47 #include <ardour/panner.h>
48 #include <ardour/playlist.h>
49 #include <ardour/session.h>
50 #include <ardour/session_playlist.h>
51 #include <ardour/utils.h>
52
53 #include "ardour_ui.h"
54 #include "route_time_axis.h"
55 #include "automation_time_axis.h"
56 #include "redirect_automation_time_axis.h"
57 #include "redirect_automation_line.h"
58 #include "canvas_impl.h"
59 #include "crossfade_view.h"
60 #include "enums.h"
61 #include "gui_thread.h"
62 #include "keyboard.h"
63 #include "playlist_selector.h"
64 #include "plugin_selector.h"
65 #include "plugin_ui.h"
66 #include "point_selection.h"
67 #include "prompter.h"
68 #include "public_editor.h"
69 #include "region_view.h"
70 #include "rgb_macros.h"
71 #include "selection.h"
72 #include "simplerect.h"
73 #include "streamview.h"
74 #include "utils.h"
75
76 #include <ardour/track.h>
77
78 #include "i18n.h"
79
80 using namespace ARDOUR;
81 using namespace PBD;
82 using namespace Gtk;
83 using namespace Editing;
84
85
86 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
87         : AxisView(sess),
88           RouteUI(rt, sess, _("m"), _("s"), _("r")), // mute, solo, and record
89           TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas),
90           parent_canvas (canvas),
91           button_table (3, 3),
92           edit_group_button (_("g")), // group
93           playlist_button (_("p")), 
94           size_button (_("h")), // height
95           automation_button (_("a")),
96           visual_button (_("v"))
97
98 {
99         _has_state = true;
100         playlist_menu = 0;
101         playlist_action_menu = 0;
102         automation_action_menu = 0;
103         _view = 0;
104         timestretch_rect = 0;
105         no_redraw = false;
106
107         ignore_toggle = false;
108
109         mute_button->set_active (false);
110         solo_button->set_active (false);
111         
112         mute_button->set_name ("TrackMuteButton");
113         solo_button->set_name ("SoloButton");
114         edit_group_button.set_name ("TrackGroupButton");
115         playlist_button.set_name ("TrackPlaylistButton");
116         automation_button.set_name ("TrackAutomationButton");
117         size_button.set_name ("TrackSizeButton");
118         visual_button.set_name ("TrackVisualButton");
119         hide_button.set_name ("TrackRemoveButton");
120
121         hide_button.add (*(manage (new Image (get_xpm("small_x.xpm")))));
122
123         solo_button->signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false);
124         mute_button->signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false);
125         playlist_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false);
126         automation_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false);
127         size_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false);
128         visual_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false);
129         hide_button.signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false);
130
131         solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false);
132         solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false);
133         mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
134         mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
135         edit_group_button.signal_button_release_event().connect (mem_fun(*this, &RouteTimeAxisView::edit_click), false);
136         playlist_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::playlist_click));
137         automation_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::automation_click));
138         size_button.signal_button_release_event().connect (mem_fun(*this, &RouteTimeAxisView::size_click), false);
139         visual_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::visual_click));
140         hide_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::hide_click));
141
142         if (is_track()) {
143                 rec_enable_button->set_active (false);
144                 rec_enable_button->set_name ("TrackRecordEnableButton");
145                 rec_enable_button->signal_button_press_event().connect (mem_fun (*this, &RouteTimeAxisView::select_me), false);
146                 rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press));
147                 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
148                 ARDOUR_UI::instance()->tooltips().set_tip(*rec_enable_button, _("Record"));
149         }
150
151         controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
152         controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::FILL|Gtk::EXPAND, 0, 0);
153
154         controls_table.attach (edit_group_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
155
156         ARDOUR_UI::instance()->tooltips().set_tip(*solo_button,_("Solo"));
157         ARDOUR_UI::instance()->tooltips().set_tip(*mute_button,_("Mute"));
158         ARDOUR_UI::instance()->tooltips().set_tip(edit_group_button,_("Edit Group"));
159         ARDOUR_UI::instance()->tooltips().set_tip(size_button,_("Display Height"));
160         ARDOUR_UI::instance()->tooltips().set_tip(playlist_button,_("Playlist"));
161         ARDOUR_UI::instance()->tooltips().set_tip(automation_button, _("Automation"));
162         ARDOUR_UI::instance()->tooltips().set_tip(visual_button, _("Visual options"));
163         ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("Hide this track"));
164         
165         label_view ();
166
167         controls_table.attach (hide_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
168         controls_table.attach (visual_button, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
169         controls_table.attach (size_button, 2, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
170         controls_table.attach (automation_button, 3, 4, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
171
172         if (is_track() && track()->mode() == ARDOUR::Normal) {
173                 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
174         }
175
176         /* remove focus from the buttons */
177         
178         automation_button.unset_flags (Gtk::CAN_FOCUS);
179         solo_button->unset_flags (Gtk::CAN_FOCUS);
180         mute_button->unset_flags (Gtk::CAN_FOCUS);
181         edit_group_button.unset_flags (Gtk::CAN_FOCUS);
182         size_button.unset_flags (Gtk::CAN_FOCUS);
183         playlist_button.unset_flags (Gtk::CAN_FOCUS);
184         hide_button.unset_flags (Gtk::CAN_FOCUS);
185         visual_button.unset_flags (Gtk::CAN_FOCUS);
186
187         /* map current state of the route */
188
189         update_diskstream_display ();
190         solo_changed(0);
191         mute_changed(0);
192         //redirects_changed (0);
193         //reset_redirect_automation_curves ();
194         y_position = -1;
195
196         _route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
197         _route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
198         _route->redirects_changed.connect (mem_fun(*this, &RouteTimeAxisView::redirects_changed));
199         _route->name_changed.connect (mem_fun(*this, &RouteTimeAxisView::route_name_changed));
200         _route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
201
202         if (is_track()) {
203
204                 track()->FreezeChange.connect (mem_fun(*this, &RouteTimeAxisView::map_frozen));
205                 track()->DiskstreamChanged.connect (mem_fun(*this, &RouteTimeAxisView::diskstream_changed));
206                 get_diskstream()->SpeedChanged.connect (mem_fun(*this, &RouteTimeAxisView::speed_changed));
207
208                 /* ask for notifications of any new RegionViews */
209                 // FIXME: _view is NULL, but it would be nice to attach this here :/
210                 //_view->RegionViewAdded.connect (mem_fun(*this, &RouteTimeAxisView::region_view_added));
211                 //_view->attach ();
212
213                 /* pick up the correct freeze state */
214                 map_frozen ();
215
216         }
217
218         editor.ZoomChanged.connect (mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
219         ColorChanged.connect (mem_fun (*this, &RouteTimeAxisView::color_handler));
220 }
221
222 RouteTimeAxisView::~RouteTimeAxisView ()
223 {
224         GoingAway (); /* EMIT_SIGNAL */
225
226         vector_delete (&redirect_automation_curves);
227
228         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
229                 delete *i;
230         }
231
232         if (playlist_menu) {
233                 delete playlist_menu;
234                 playlist_menu = 0;
235         }
236   
237         if (playlist_action_menu) {
238                 delete playlist_action_menu;
239                 playlist_action_menu = 0;
240         }
241
242         if (_view) {
243                 delete _view;
244                 _view = 0;
245         }
246 }
247
248 void
249 RouteTimeAxisView::set_playlist (Playlist *newplaylist)
250 {
251         Playlist *pl = playlist();
252         assert(pl);
253
254         modified_connection.disconnect ();
255         state_changed_connection.disconnect ();
256         
257         state_changed_connection = pl->StateChanged.connect (mem_fun(*this, &RouteTimeAxisView::playlist_state_changed));
258         modified_connection = pl->Modified.connect (mem_fun(*this, &RouteTimeAxisView::playlist_modified));
259 }
260
261 void
262 RouteTimeAxisView::playlist_modified ()
263 {
264 }
265
266 gint
267 RouteTimeAxisView::edit_click (GdkEventButton *ev)
268 {
269         if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
270                 _route->set_edit_group (0, this);
271                 return FALSE;
272         } 
273
274         using namespace Menu_Helpers;
275
276         MenuList& items = edit_group_menu.items ();
277         RadioMenuItem::Group group;
278
279         items.clear ();
280         items.push_back (RadioMenuElem (group, _("No group"), 
281                                         bind (mem_fun(*this, &RouteTimeAxisView::set_edit_group_from_menu), (RouteGroup *) 0)));
282         
283         if (_route->edit_group() == 0) {
284                 static_cast<RadioMenuItem*>(&items.back())->set_active ();
285         }
286         
287         _session.foreach_edit_group (bind (mem_fun (*this, &RouteTimeAxisView::add_edit_group_menu_item), &group));
288         edit_group_menu.popup (ev->button, ev->time);
289
290         return FALSE;
291 }
292
293 void
294 RouteTimeAxisView::add_edit_group_menu_item (RouteGroup *eg, RadioMenuItem::Group* group)
295 {
296         using namespace Menu_Helpers;
297
298         MenuList &items = edit_group_menu.items();
299
300         cerr << "adding edit group " << eg->name() << endl;
301
302         items.push_back (RadioMenuElem (*group, eg->name(), bind (mem_fun(*this, &RouteTimeAxisView::set_edit_group_from_menu), eg)));
303         if (_route->edit_group() == eg) {
304                 static_cast<RadioMenuItem*>(&items.back())->set_active ();
305         }
306 }
307
308 void
309 RouteTimeAxisView::set_edit_group_from_menu (RouteGroup *eg)
310
311 {
312         _route->set_edit_group (eg, this);
313 }
314
315 void
316 RouteTimeAxisView::playlist_state_changed (Change ignored)
317 {
318         // ENSURE_GUI_THREAD (bind (mem_fun(*this, &RouteTimeAxisView::playlist_state_changed), ignored));
319         // why are we here ?
320 }
321
322 void
323 RouteTimeAxisView::playlist_changed ()
324
325 {
326         label_view ();
327
328         if (is_track()) {
329                 set_playlist (dynamic_cast<Playlist*>(get_diskstream()->playlist()));
330         }
331 }
332
333 void
334 RouteTimeAxisView::label_view ()
335 {
336         string x = _route->name();
337
338         if (x != name_entry.get_text()) {
339                 name_entry.set_text (x);
340         }
341
342         ARDOUR_UI::instance()->tooltips().set_tip (name_entry, x);
343 }
344
345 void
346 RouteTimeAxisView::route_name_changed (void *src)
347 {
348         editor.route_name_changed (this);
349         label_view ();
350 }
351
352 void
353 RouteTimeAxisView::take_name_changed (void *src)
354
355 {
356         if (src != this) {
357                 label_view ();
358         }
359 }
360
361 void
362 RouteTimeAxisView::playlist_click ()
363 {
364         // always build a new action menu
365         
366         if (playlist_action_menu == 0) {
367                 playlist_action_menu = new Menu;
368                 playlist_action_menu->set_name ("ArdourContextMenu");
369         }
370         
371         build_playlist_menu(playlist_action_menu);
372
373         playlist_action_menu->popup (1, 0);
374 }
375
376 void
377 RouteTimeAxisView::automation_click ()
378 {
379         if (automation_action_menu == 0) {
380                 /* this seems odd, but the automation action
381                    menu is built as part of the display menu.
382                 */
383                 build_display_menu ();
384         }
385         automation_action_menu->popup (1, 0);
386 }
387
388 void
389 RouteTimeAxisView::build_automation_action_menu ()
390 {
391         using namespace Menu_Helpers;
392
393         automation_action_menu = manage (new Menu);
394         MenuList& automation_items = automation_action_menu->items();
395         automation_action_menu->set_name ("ArdourContextMenu");
396         
397         automation_items.push_back (MenuElem (_("Show all automation"),
398                                               mem_fun(*this, &RouteTimeAxisView::show_all_automation)));
399
400         automation_items.push_back (MenuElem (_("Show existing automation"),
401                                               mem_fun(*this, &RouteTimeAxisView::show_existing_automation)));
402
403         automation_items.push_back (MenuElem (_("Hide all automation"),
404                                               mem_fun(*this, &RouteTimeAxisView::hide_all_automation)));
405
406         automation_items.push_back (MenuElem (_("Plugins"), subplugin_menu));
407 }
408
409 void
410 RouteTimeAxisView::build_display_menu ()
411 {
412         using namespace Menu_Helpers;
413
414         /* get the size menu ready */
415
416         build_size_menu ();
417
418         /* prepare it */
419
420         TimeAxisView::build_display_menu ();
421
422         /* now fill it with our stuff */
423
424         MenuList& items = display_menu->items();
425         display_menu->set_name ("ArdourContextMenu");
426         
427         items.push_back (MenuElem (_("Height"), *size_menu));
428         items.push_back (MenuElem (_("Color"), mem_fun(*this, &RouteTimeAxisView::select_track_color)));
429
430         items.push_back (SeparatorElem());
431
432         build_remote_control_menu ();
433         items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu));
434
435         build_automation_action_menu ();
436         items.push_back (MenuElem (_("Automation"), *automation_action_menu));
437
438         // Hook for derived classes to add type specific stuff
439         items.push_back (SeparatorElem());
440         append_extra_display_menu_items ();
441         items.push_back (SeparatorElem());
442         
443         if (is_track()) {
444
445                 Menu* alignment_menu = manage (new Menu);
446                 MenuList& alignment_items = alignment_menu->items();
447                 alignment_menu->set_name ("ArdourContextMenu");
448
449                 RadioMenuItem::Group align_group;
450                 
451                 alignment_items.push_back (RadioMenuElem (align_group, _("Align with existing material"),
452                         bind (mem_fun(*this, &RouteTimeAxisView::set_align_style), ExistingMaterial)));
453                 align_existing_item = dynamic_cast<RadioMenuItem*>(&alignment_items.back());
454                 if (get_diskstream()->alignment_style() == ExistingMaterial)
455                         align_existing_item->set_active();
456                 
457                 alignment_items.push_back (RadioMenuElem (align_group, _("Align with capture time"),
458                         bind (mem_fun(*this, &RouteTimeAxisView::set_align_style), CaptureTime)));
459                 align_capture_item = dynamic_cast<RadioMenuItem*>(&alignment_items.back());
460                 if (get_diskstream()->alignment_style() == CaptureTime)
461                         align_capture_item->set_active();
462                 
463                 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
464
465                 get_diskstream()->AlignmentStyleChanged.connect (
466                         mem_fun(*this, &RouteTimeAxisView::align_style_changed));
467         }
468
469         items.push_back (SeparatorElem());
470         items.push_back (CheckMenuElem (_("Active"), mem_fun(*this, &RouteUI::toggle_route_active)));
471         route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
472         route_active_menu_item->set_active (_route->active());
473
474         items.push_back (SeparatorElem());
475         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route)));
476 }
477
478
479 void
480 RouteTimeAxisView::show_timestretch (jack_nframes_t start, jack_nframes_t end)
481 {
482         double x1;
483         double x2;
484         double y2;
485         
486         TimeAxisView::show_timestretch (start, end);
487
488         hide_timestretch ();
489
490 #if 0   
491         if (ts.empty()) {
492                 return;
493         }
494
495
496         /* check that the time selection was made in our route, or our edit group.
497            remember that edit_group() == 0 implies the route is *not* in a edit group.
498         */
499
500         if (!(ts.track == this || (ts.group != 0 && ts.group == _route->edit_group()))) {
501                 /* this doesn't apply to us */
502                 return;
503         }
504
505         /* ignore it if our edit group is not active */
506         
507         if ((ts.track != this) && _route->edit_group() && !_route->edit_group()->is_active()) {
508                 return;
509         }
510 #endif
511
512         if (timestretch_rect == 0) {
513                 timestretch_rect = new SimpleRect (*canvas_display);
514                 timestretch_rect->property_x1() =  0.0;
515                 timestretch_rect->property_y1() =  0.0;
516                 timestretch_rect->property_x2() =  0.0;
517                 timestretch_rect->property_y2() =  0.0;
518                 timestretch_rect->property_fill_color_rgba() =  color_map[cTimeStretchFill];
519                 timestretch_rect->property_outline_color_rgba() = color_map[cTimeStretchOutline];
520         }
521
522         timestretch_rect->show ();
523         timestretch_rect->raise_to_top ();
524
525         x1 = start / editor.get_current_zoom();
526         x2 = (end - 1) / editor.get_current_zoom();
527         y2 = height - 2;
528         
529         timestretch_rect->property_x1() = x1;
530         timestretch_rect->property_y1() = 1.0;
531         timestretch_rect->property_x2() = x2;
532         timestretch_rect->property_y2() = y2;
533 }
534
535 void
536 RouteTimeAxisView::hide_timestretch ()
537 {
538         TimeAxisView::hide_timestretch ();
539
540         if (timestretch_rect) {
541                 timestretch_rect->hide ();
542         }
543 }
544
545 void
546 RouteTimeAxisView::show_selection (TimeSelection& ts)
547 {
548
549 #if 0
550         /* ignore it if our edit group is not active or if the selection was started
551            in some other track or edit group (remember that edit_group() == 0 means
552            that the track is not in an edit group).
553         */
554
555         if (((ts.track != this && !is_child (ts.track)) && _route->edit_group() && !_route->edit_group()->is_active()) ||
556             (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->edit_group())))) {
557                 hide_selection ();
558                 return;
559         }
560 #endif
561
562         TimeAxisView::show_selection (ts);
563 }
564
565 void
566 RouteTimeAxisView::set_height (TrackHeight h)
567 {
568         bool height_changed = (height == 0) || (h != height_style);
569
570         TimeAxisView::set_height (h);
571
572         ensure_xml_node ();
573
574         _view->set_height ((double) height);
575
576         switch (height_style) {
577         case Largest:
578                 xml_node->add_property ("track_height", "largest");
579                 break;
580
581         case Large:
582                 xml_node->add_property ("track_height", "large");
583                 break;
584
585         case Larger:
586                 xml_node->add_property ("track_height", "larger");
587                 break;
588
589         case Normal:
590                 xml_node->add_property ("track_height", "normal");
591                 break;
592
593         case Smaller:
594                 xml_node->add_property ("track_height", "smaller");
595                 break;
596
597         case Small:
598                 xml_node->add_property ("track_height", "small");
599                 break;
600         }
601
602         switch (height_style) {
603         case Largest:
604         case Large:
605         case Larger:
606         case Normal:
607                 show_name_entry ();
608                 hide_name_label ();
609
610                 mute_button->show_all();
611                 solo_button->show_all();
612                 if (rec_enable_button)
613                         rec_enable_button->show_all();
614
615                 edit_group_button.show_all();
616                 hide_button.show_all();
617                 visual_button.show_all();
618                 size_button.show_all();
619                 automation_button.show_all();
620                 
621                 if (is_track() && track()->mode() == ARDOUR::Normal) {
622                         playlist_button.show_all();
623                 }
624                 break;
625
626         case Smaller:
627                 show_name_entry ();
628                 hide_name_label ();
629
630                 mute_button->show_all();
631                 solo_button->show_all();
632                 if (rec_enable_button)
633                         rec_enable_button->show_all();
634
635                 edit_group_button.hide ();
636                 hide_button.hide ();
637                 visual_button.hide ();
638                 size_button.hide ();
639                 automation_button.hide ();
640                 
641                 if (is_track() && track()->mode() == ARDOUR::Normal) {
642                         playlist_button.hide ();
643                 }
644                 break;
645
646         case Small:
647                 hide_name_entry ();
648                 show_name_label ();
649
650                 mute_button->hide();
651                 solo_button->hide();
652                 if (rec_enable_button)
653                         rec_enable_button->hide();
654
655                 edit_group_button.hide ();
656                 hide_button.hide ();
657                 visual_button.hide ();
658                 size_button.hide ();
659                 automation_button.hide ();
660                 playlist_button.hide ();
661                 name_label.set_text (_route->name());
662                 break;
663         }
664
665         if (height_changed) {
666                 /* only emit the signal if the height really changed */
667                  _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
668         }
669 }
670
671 void
672 RouteTimeAxisView::select_track_color ()
673 {
674         if (RouteUI::choose_color ()) {
675
676                 if (_view) {
677                         _view->apply_color (_color, StreamView::RegionColor);
678                 }
679         }
680 }
681
682 void
683 RouteTimeAxisView::reset_samples_per_unit ()
684 {
685         set_samples_per_unit (editor.get_current_zoom());
686 }
687
688 void
689 RouteTimeAxisView::set_samples_per_unit (double spu)
690 {
691         double speed = 1.0;
692
693         if (get_diskstream() != 0) {
694                 speed = get_diskstream()->speed();
695         }
696         
697         if (_view) {
698                 _view->set_samples_per_unit (spu * speed);
699         }
700
701         TimeAxisView::set_samples_per_unit (spu * speed);
702 }
703
704 void
705 RouteTimeAxisView::align_style_changed ()
706 {
707         switch (get_diskstream()->alignment_style()) {
708         case ExistingMaterial:
709                 if (!align_existing_item->get_active()) {
710                         align_existing_item->set_active();
711                 }
712                 break;
713         case CaptureTime:
714                 if (!align_capture_item->get_active()) {
715                         align_capture_item->set_active();
716                 }
717                 break;
718         }
719 }
720
721 void
722 RouteTimeAxisView::set_align_style (AlignStyle style)
723 {
724         get_diskstream()->set_align_style (style);
725 }
726
727 void
728 RouteTimeAxisView::rename_current_playlist ()
729 {
730         ArdourPrompter prompter (true);
731         string name;
732
733         Diskstream *const ds = get_diskstream();
734         if (!ds || ds->destructive())
735                 return;
736
737         Playlist *const pl = ds->playlist();
738         if (!pl)
739                 return;
740
741         prompter.set_prompt (_("Name for playlist"));
742         prompter.set_initial_text (pl->name());
743         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
744         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
745
746         switch (prompter.run ()) {
747         case Gtk::RESPONSE_ACCEPT:
748                 prompter.get_result (name);
749                 if (name.length()) {
750                         pl->set_name (name);
751                 }
752                 break;
753
754         default:
755                 break;
756         }
757 }
758
759 void
760 RouteTimeAxisView::use_copy_playlist (bool prompt)
761 {
762         string name;
763         
764         Diskstream *const ds = get_diskstream();
765         if (!ds || ds->destructive())
766                 return;
767
768         Playlist *const pl = ds->playlist();
769         if (!pl)
770                 return;
771
772         name = Playlist::bump_name (pl->name(), _session);
773
774         if (prompt) {
775
776                 ArdourPrompter prompter (true);
777                 
778                 prompter.set_prompt (_("Name for Playlist"));
779                 prompter.set_initial_text (name);
780                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
781                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
782                 prompter.show_all ();
783                 
784                 switch (prompter.run ()) {
785                 case Gtk::RESPONSE_ACCEPT:
786                         prompter.get_result (name);
787                         break;
788                         
789                 default:
790                         return;
791                 }
792         }
793
794         if (name.length()) {
795                 ds->use_copy_playlist ();
796                 pl->set_name (name);
797         }
798 }
799
800 void
801 RouteTimeAxisView::use_new_playlist (bool prompt)
802 {
803         string name;
804         
805         Diskstream *const ds = get_diskstream();
806         if (!ds || ds->destructive())
807                 return;
808
809         Playlist *const pl = ds->playlist();
810         if (!pl)
811                 return;
812
813         name = Playlist::bump_name (pl->name(), _session);
814
815         if (prompt) {
816                 
817                 ArdourPrompter prompter (true);
818                 
819                 prompter.set_prompt (_("Name for Playlist"));
820                 prompter.set_initial_text (name);
821                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
822                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
823                 
824                 switch (prompter.run ()) {
825                 case Gtk::RESPONSE_ACCEPT:
826                         prompter.get_result (name);
827                         break;
828                         
829                 default:
830                         return;
831                 }
832         }
833
834         if (name.length()) {
835                 ds->use_new_playlist ();
836                 pl->set_name (name);
837         }
838 }
839
840 void
841 RouteTimeAxisView::clear_playlist ()
842 {
843         Diskstream *const ds = get_diskstream();
844         if (!ds || ds->destructive())
845                 return;
846
847         Playlist *const pl = ds->playlist();
848         if (!pl)
849                 return;
850
851         editor.clear_playlist (*pl);
852 }
853
854 void
855 RouteTimeAxisView::speed_changed ()
856 {
857         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
858 }
859
860 void
861 RouteTimeAxisView::diskstream_changed ()
862 {
863         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &RouteTimeAxisView::update_diskstream_display));
864 }       
865
866 void
867 RouteTimeAxisView::update_diskstream_display ()
868 {
869         if (!get_diskstream()) // bus
870                 return;
871
872         set_playlist (get_diskstream()->playlist());
873         map_frozen ();
874 }       
875
876 void
877 RouteTimeAxisView::selection_click (GdkEventButton* ev)
878 {
879         PublicEditor::TrackViewList* tracks = editor.get_valid_views (this, _route->edit_group());
880
881         switch (Keyboard::selection_type (ev->state)) {
882         case Selection::Toggle:
883                 /* XXX this is not right */
884                 editor.get_selection().add (*tracks);
885                 break;
886                 
887         case Selection::Set:
888                 editor.get_selection().set (*tracks);
889                 break;
890
891         case Selection::Extend:
892                 /* not defined yet */
893                 break;
894         }
895
896         delete tracks;
897 }
898
899 void
900 RouteTimeAxisView::set_selected_points (PointSelection& points)
901 {
902         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
903                 (*i)->set_selected_points (points);
904         }
905 }
906
907 void
908 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
909 {
910         _view->set_selected_regionviews (regions);
911 }
912
913 void
914 RouteTimeAxisView::get_selectables (jack_nframes_t start, jack_nframes_t end, double top, double bot, list<Selectable*>& results)
915 {
916         double speed = 1.0;
917         
918         if (get_diskstream() != 0) {
919                 speed = get_diskstream()->speed();
920         }
921         
922         jack_nframes_t start_adjusted = session_frame_to_track_frame(start, speed);
923         jack_nframes_t end_adjusted   = session_frame_to_track_frame(end, speed);
924
925         if (_view && ((top < 0.0 && bot < 0.0)) || touched (top, bot)) {
926                 _view->get_selectables (start_adjusted, end_adjusted, results);
927         }
928
929         /* pick up visible automation tracks */
930         
931         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
932                 if (!(*i)->hidden()) {
933                         (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
934                 }
935         }
936 }
937
938 void
939 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
940 {
941         if (_view) {
942                 _view->get_inverted_selectables (sel, results);
943         }
944
945         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
946                 if (!(*i)->hidden()) {
947                         (*i)->get_inverted_selectables (sel, results);
948                 }
949         }
950
951         return;
952 }
953
954 RouteGroup*
955 RouteTimeAxisView::edit_group() const
956 {
957         return _route->edit_group();
958 }
959
960 string
961 RouteTimeAxisView::name() const
962 {
963         return _route->name();
964 }
965
966 Playlist *
967 RouteTimeAxisView::playlist () const 
968 {
969         Diskstream *ds;
970
971         if ((ds = get_diskstream()) != 0) {
972                 return ds->playlist(); 
973         } else {
974                 return 0; 
975         }
976 }
977
978 void
979 RouteTimeAxisView::name_entry_changed ()
980 {
981         string x;
982
983         x = name_entry.get_text ();
984         
985         if (x == _route->name()) {
986                 return;
987         }
988
989         if (x.length() == 0) {
990                 name_entry.set_text (_route->name());
991                 return;
992         }
993
994         strip_whitespace_edges(x);
995
996         if (_session.route_name_unique (x)) {
997                 _route->set_name (x, this);
998         } else {
999                 ARDOUR_UI::instance()->popup_error (_("a track already exists with that name"));
1000                 name_entry.set_text (_route->name());
1001         }
1002 }
1003
1004 void
1005 RouteTimeAxisView::visual_click ()
1006 {
1007         popup_display_menu (0);
1008 }
1009
1010 void
1011 RouteTimeAxisView::hide_click ()
1012 {
1013         editor.hide_track_in_display (*this);
1014 }
1015
1016 Region*
1017 RouteTimeAxisView::find_next_region (jack_nframes_t pos, RegionPoint point, int32_t dir)
1018 {
1019         Diskstream *stream;
1020         Playlist *playlist;
1021
1022         if ((stream = get_diskstream()) != 0 && (playlist = stream->playlist()) != 0) {
1023                 return playlist->find_next_region (pos, point, dir);
1024         }
1025
1026         return 0;
1027 }
1028
1029 bool
1030 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1031 {
1032         Playlist* what_we_got;
1033         Diskstream* ds = get_diskstream();
1034         Playlist* playlist;
1035         bool ret = false;
1036
1037         if (ds == 0) {
1038                 /* route is a bus, not a track */
1039                 return false;
1040         }
1041
1042         playlist = ds->playlist();
1043
1044
1045         TimeSelection time (selection.time);
1046         float speed = ds->speed();
1047         if (speed != 1.0f) {
1048                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1049                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1050                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1051                 }
1052         }
1053         
1054         XMLNode &before = playlist->get_state();
1055         switch (op) {
1056         case Cut:
1057                 if ((what_we_got = playlist->cut (time)) != 0) {
1058                         editor.get_cut_buffer().add (what_we_got);
1059                         _session.add_command( new MementoCommand<Playlist>(*playlist, before, playlist->get_state()));
1060                         ret = true;
1061                 }
1062                 break;
1063         case Copy:
1064                 if ((what_we_got = playlist->copy (time)) != 0) {
1065                         editor.get_cut_buffer().add (what_we_got);
1066                 }
1067                 break;
1068
1069         case Clear:
1070                 if ((what_we_got = playlist->cut (time)) != 0) {
1071                         _session.add_command( new MementoCommand<Playlist>(*playlist, before, playlist->get_state()));
1072                         what_we_got->unref ();
1073                         ret = true;
1074                 }
1075                 break;
1076         }
1077
1078         return ret;
1079 }
1080
1081 bool
1082 RouteTimeAxisView::paste (jack_nframes_t pos, float times, Selection& selection, size_t nth)
1083 {
1084         if (!is_track()) {
1085                 return false;
1086         }
1087
1088         Playlist* playlist = get_diskstream()->playlist();
1089         PlaylistSelection::iterator p;
1090         
1091         for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth);
1092
1093         if (p == selection.playlists.end()) {
1094                 return false;
1095         }
1096
1097         if (get_diskstream()->speed() != 1.0f)
1098                 pos = session_frame_to_track_frame(pos, get_diskstream()->speed() );
1099         
1100         XMLNode &before = playlist->get_state();
1101         playlist->paste (**p, pos, times);
1102         _session.add_command( new MementoCommand<Playlist>(*playlist, before, playlist->get_state()));
1103
1104         return true;
1105 }
1106
1107
1108 list<TimeAxisView*>
1109 RouteTimeAxisView::get_child_list()
1110 {
1111   
1112         list<TimeAxisView*>redirect_children;
1113         
1114         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
1115                 if (!(*i)->hidden()) {
1116                         redirect_children.push_back(*i);
1117                 }
1118         }
1119         return redirect_children;
1120 }
1121
1122
1123 void
1124 RouteTimeAxisView::build_playlist_menu (Gtk::Menu * menu)
1125 {
1126         using namespace Menu_Helpers;
1127
1128         if (!menu || !is_track()) {
1129                 return;
1130         }
1131
1132         MenuList& playlist_items = menu->items();
1133         menu->set_name ("ArdourContextMenu");
1134         playlist_items.clear();
1135
1136         if (playlist_menu) {
1137                 delete playlist_menu;
1138         }
1139         playlist_menu = new Menu;
1140         playlist_menu->set_name ("ArdourContextMenu");
1141
1142         playlist_items.push_back (MenuElem (string_compose (_("Current: %1"), get_diskstream()->playlist()->name())));
1143         playlist_items.push_back (SeparatorElem());
1144         
1145         playlist_items.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1146         playlist_items.push_back (SeparatorElem());
1147
1148         playlist_items.push_back (MenuElem (_("New"), mem_fun(editor, &PublicEditor::new_playlists)));
1149         playlist_items.push_back (MenuElem (_("New Copy"), mem_fun(editor, &PublicEditor::copy_playlists)));
1150         playlist_items.push_back (SeparatorElem());
1151         playlist_items.push_back (MenuElem (_("Clear Current"), mem_fun(editor, &PublicEditor::clear_playlists)));
1152         playlist_items.push_back (SeparatorElem());
1153         playlist_items.push_back (MenuElem(_("Select"), mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1154
1155 }
1156
1157 void
1158 RouteTimeAxisView::show_playlist_selector ()
1159 {
1160         editor.playlist_selector().show_for (this);
1161 }
1162
1163 void
1164 RouteTimeAxisView::map_frozen ()
1165 {
1166         if (!is_track()) {
1167                 return;
1168         }
1169
1170         ENSURE_GUI_THREAD (mem_fun(*this, &RouteTimeAxisView::map_frozen));
1171
1172         switch (track()->freeze_state()) {
1173         case Track::Frozen:
1174                 playlist_button.set_sensitive (false);
1175                 rec_enable_button->set_sensitive (false);
1176                 break;
1177         default:
1178                 playlist_button.set_sensitive (true);
1179                 rec_enable_button->set_sensitive (true);
1180                 break;
1181         }
1182 }
1183
1184 void
1185 RouteTimeAxisView::color_handler (ColorID id, uint32_t val)
1186 {
1187         switch (id) {
1188         case cTimeStretchOutline:
1189                 timestretch_rect->property_outline_color_rgba() = val;
1190                 break;
1191         case cTimeStretchFill:
1192                 timestretch_rect->property_fill_color_rgba() = val;
1193                 break;
1194         default:
1195                 break;
1196         }
1197 }
1198
1199 bool
1200 RouteTimeAxisView::select_me (GdkEventButton* ev)
1201 {
1202         editor.get_selection().add (this);
1203         return false;
1204 }
1205
1206 void
1207 RouteTimeAxisView::show_all_automation ()
1208 {
1209         no_redraw = true;
1210
1211         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1212                 for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1213                         if ((*ii)->view == 0) {
1214                                 add_redirect_automation_curve ((*i)->redirect, (*ii)->what);
1215                         } 
1216
1217                         (*ii)->menu_item->set_active (true);
1218                 }
1219         }
1220
1221         no_redraw = false;
1222
1223          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1224 }
1225
1226 void
1227 RouteTimeAxisView::show_existing_automation ()
1228 {
1229         no_redraw = true;
1230
1231         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1232                 for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1233                         if ((*ii)->view != 0) {
1234                                 (*ii)->menu_item->set_active (true);
1235                         }
1236                 }
1237         }
1238
1239         no_redraw = false;
1240
1241          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1242 }
1243
1244 void
1245 RouteTimeAxisView::hide_all_automation ()
1246 {
1247         no_redraw = true;
1248
1249         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1250                 for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1251                         (*ii)->menu_item->set_active (false);
1252                 }
1253         }
1254
1255         no_redraw = false;
1256          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1257 }
1258
1259
1260 void
1261 RouteTimeAxisView::region_view_added (RegionView* rv)
1262 {
1263         for (vector<TimeAxisView*>::iterator i = children.begin(); i != children.end(); ++i) {
1264                 AutomationTimeAxisView* atv;
1265
1266                 if ((atv = dynamic_cast<AutomationTimeAxisView*> (*i)) != 0) {
1267                         rv->add_ghost (*atv);
1268                 }
1269         }
1270 }
1271
1272 void
1273 RouteTimeAxisView::add_ghost_to_redirect (RegionView* rv, AutomationTimeAxisView* atv)
1274 {
1275         rv->add_ghost (*atv);
1276 }
1277
1278 RouteTimeAxisView::RedirectAutomationInfo::~RedirectAutomationInfo ()
1279 {
1280         for (vector<RedirectAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1281                 delete *i;
1282         }
1283 }
1284
1285
1286 RouteTimeAxisView::RedirectAutomationNode::~RedirectAutomationNode ()
1287 {
1288         parent.remove_ran (this);
1289
1290         if (view) {
1291                 delete view;
1292         }
1293 }
1294
1295 void
1296 RouteTimeAxisView::remove_ran (RedirectAutomationNode* ran)
1297 {
1298         if (ran->view) {
1299                 remove_child (ran->view);
1300         }
1301 }
1302
1303 RouteTimeAxisView::RedirectAutomationNode*
1304 RouteTimeAxisView::find_redirect_automation_node (boost::shared_ptr<Redirect> redirect, uint32_t what)
1305 {
1306         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1307
1308                 if ((*i)->redirect == redirect) {
1309
1310                         for (vector<RedirectAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1311                                 if ((*ii)->what == what) {
1312                                         return *ii;
1313                                 }
1314                         }
1315                 }
1316         }
1317
1318         return 0;
1319 }
1320
1321 // FIXME: duplicated in midi_time_axis.cc
1322 static string 
1323 legalize_for_xml_node (string str)
1324 {
1325         string::size_type pos;
1326         string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=:";
1327         string legal;
1328
1329         legal = str;
1330         pos = 0;
1331
1332         while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
1333                 legal.replace (pos, 1, "_");
1334                 pos += 1;
1335         }
1336
1337         return legal;
1338 }
1339
1340
1341 void
1342 RouteTimeAxisView::add_redirect_automation_curve (boost::shared_ptr<Redirect> redirect, uint32_t what)
1343 {
1344         RedirectAutomationLine* ral;
1345         string name;
1346         RedirectAutomationNode* ran;
1347
1348         if ((ran = find_redirect_automation_node (redirect, what)) == 0) {
1349                 fatal << _("programming error: ")
1350                       << string_compose (X_("redirect automation curve for %1:%2 not registered with audio track!"),
1351                                   redirect->name(), what)
1352                       << endmsg;
1353                 /*NOTREACHED*/
1354                 return;
1355         }
1356
1357         if (ran->view) {
1358                 return;
1359         }
1360
1361         name = redirect->describe_parameter (what);
1362
1363         /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1364
1365         char state_name[256];
1366         snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (redirect->name()).c_str(), what);
1367
1368         ran->view = new RedirectAutomationTimeAxisView (_session, _route, editor, *this, parent_canvas, name, what, *redirect, state_name);
1369
1370         ral = new RedirectAutomationLine (name, 
1371                                           *redirect, what, _session, *ran->view,
1372                                           *ran->view->canvas_display, redirect->automation_list (what));
1373         
1374         ral->set_line_color (color_map[cRedirectAutomationLine]);
1375         ral->queue_reset ();
1376
1377         ran->view->add_line (*ral);
1378
1379         ran->view->Hiding.connect (bind (mem_fun(*this, &RouteTimeAxisView::redirect_automation_track_hidden), ran, redirect));
1380
1381         if (!ran->view->marked_for_display()) {
1382                 ran->view->hide ();
1383         } else {
1384                 ran->menu_item->set_active (true);
1385         }
1386
1387         add_child (ran->view);
1388
1389         _view->foreach_regionview (bind (mem_fun(*this, &RouteTimeAxisView::add_ghost_to_redirect), ran->view));
1390
1391         redirect->mark_automation_visible (what, true);
1392 }
1393
1394 void
1395 RouteTimeAxisView::redirect_automation_track_hidden (RouteTimeAxisView::RedirectAutomationNode* ran, boost::shared_ptr<Redirect> r)
1396 {
1397         if (!_hidden) {
1398                 ran->menu_item->set_active (false);
1399         }
1400
1401         r->mark_automation_visible (ran->what, false);
1402
1403          _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1404 }
1405
1406 void
1407 RouteTimeAxisView::add_existing_redirect_automation_curves (boost::shared_ptr<Redirect> redirect)
1408 {
1409         set<uint32_t> s;
1410         RedirectAutomationLine *ral;
1411
1412         redirect->what_has_visible_automation (s);
1413
1414         for (set<uint32_t>::iterator i = s.begin(); i != s.end(); ++i) {
1415                 
1416                 if ((ral = find_redirect_automation_curve (redirect, *i)) != 0) {
1417                         ral->queue_reset ();
1418                 } else {
1419                         add_redirect_automation_curve (redirect, (*i));
1420                 }
1421         }
1422 }
1423
1424 void
1425 RouteTimeAxisView::add_redirect_to_subplugin_menu (boost::shared_ptr<Redirect> r)
1426 {
1427         using namespace Menu_Helpers;
1428         RedirectAutomationInfo *rai;
1429         list<RedirectAutomationInfo*>::iterator x;
1430         
1431         const std::set<uint32_t>& automatable = r->what_can_be_automated ();
1432         std::set<uint32_t> has_visible_automation;
1433
1434         r->what_has_visible_automation(has_visible_automation);
1435
1436         if (automatable.empty()) {
1437                 return;
1438         }
1439
1440         for (x = redirect_automation.begin(); x != redirect_automation.end(); ++x) {
1441                 if ((*x)->redirect == r) {
1442                         break;
1443                 }
1444         }
1445
1446         if (x == redirect_automation.end()) {
1447
1448                 rai = new RedirectAutomationInfo (r);
1449                 redirect_automation.push_back (rai);
1450
1451         } else {
1452
1453                 rai = *x;
1454
1455         }
1456
1457         /* any older menu was deleted at the top of redirects_changed()
1458            when we cleared the subplugin menu.
1459         */
1460
1461         rai->menu = manage (new Menu);
1462         MenuList& items = rai->menu->items();
1463         rai->menu->set_name ("ArdourContextMenu");
1464
1465         items.clear ();
1466
1467         for (std::set<uint32_t>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
1468
1469                 RedirectAutomationNode* ran;
1470                 CheckMenuItem* mitem;
1471                 
1472                 string name = r->describe_parameter (*i);
1473                 
1474                 items.push_back (CheckMenuElem (name));
1475                 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
1476
1477                 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
1478                         mitem->set_active(true);
1479                 }
1480
1481                 if ((ran = find_redirect_automation_node (r, *i)) == 0) {
1482
1483                         /* new item */
1484                         
1485                         ran = new RedirectAutomationNode (*i, mitem, *this);
1486                         
1487                         rai->lines.push_back (ran);
1488
1489                 } else {
1490
1491                         ran->menu_item = mitem;
1492
1493                 }
1494
1495                 mitem->signal_toggled().connect (bind (mem_fun(*this, &RouteTimeAxisView::redirect_menu_item_toggled), rai, ran));
1496         }
1497
1498         /* add the menu for this redirect, because the subplugin
1499            menu is always cleared at the top of redirects_changed().
1500            this is the result of some poor design in gtkmm and/or
1501            GTK+.
1502         */
1503
1504         subplugin_menu.items().push_back (MenuElem (r->name(), *rai->menu));
1505         rai->valid = true;
1506 }
1507
1508 void
1509 RouteTimeAxisView::redirect_menu_item_toggled (RouteTimeAxisView::RedirectAutomationInfo* rai,
1510                                                RouteTimeAxisView::RedirectAutomationNode* ran)
1511 {
1512         bool showit = ran->menu_item->get_active();
1513         bool redraw = false;
1514
1515         if (ran->view == 0 && showit) {
1516                 add_redirect_automation_curve (rai->redirect, ran->what);
1517                 redraw = true;
1518         }
1519
1520         if (showit != ran->view->marked_for_display()) {
1521
1522                 if (showit) {
1523                         ran->view->set_marked_for_display (true);
1524                         ran->view->canvas_display->show();
1525                 } else {
1526                         rai->redirect->mark_automation_visible (ran->what, true);
1527                         ran->view->set_marked_for_display (false);
1528                         ran->view->hide ();
1529                 }
1530
1531                 redraw = true;
1532
1533         }
1534
1535         if (redraw && !no_redraw) {
1536
1537                 /* now trigger a redisplay */
1538                 
1539                  _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1540
1541         }
1542 }
1543
1544 void
1545 RouteTimeAxisView::redirects_changed (void *src)
1546 {
1547         using namespace Menu_Helpers;
1548
1549         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ++i) {
1550                 (*i)->valid = false;
1551         }
1552
1553         subplugin_menu.items().clear ();
1554
1555         _route->foreach_redirect (this, &RouteTimeAxisView::add_redirect_to_subplugin_menu);
1556         _route->foreach_redirect (this, &RouteTimeAxisView::add_existing_redirect_automation_curves);
1557
1558         for (list<RedirectAutomationInfo*>::iterator i = redirect_automation.begin(); i != redirect_automation.end(); ) {
1559
1560                 list<RedirectAutomationInfo*>::iterator tmp;
1561
1562                 tmp = i;
1563                 ++tmp;
1564
1565                 if (!(*i)->valid) {
1566
1567                         delete *i;
1568                         redirect_automation.erase (i);
1569
1570                 } 
1571
1572                 i = tmp;
1573         }
1574
1575         /* change in visibility was possible */
1576
1577         _route->gui_changed ("track_height", this);
1578 }
1579
1580 RedirectAutomationLine *
1581 RouteTimeAxisView::find_redirect_automation_curve (boost::shared_ptr<Redirect> redirect, uint32_t what)
1582 {
1583         RedirectAutomationNode* ran;
1584
1585         if ((ran = find_redirect_automation_node (redirect, what)) != 0) {
1586                 if (ran->view) {
1587                         return dynamic_cast<RedirectAutomationLine*> (ran->view->lines.front());
1588                 } 
1589         }
1590
1591         return 0;
1592 }
1593
1594 void
1595 RouteTimeAxisView::reset_redirect_automation_curves ()
1596 {
1597         for (vector<RedirectAutomationLine*>::iterator i = redirect_automation_curves.begin(); i != redirect_automation_curves.end(); ++i) {
1598                 (*i)->reset();
1599         }
1600 }
1601