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