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