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