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