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