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