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