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