75% (?) of the way towards making mixer strips control bus sends. lots more to do
[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/audioplaylist.h"
44 #include "ardour/diskstream.h"
45 #include "ardour/event_type_map.h"
46 #include "ardour/ladspa_plugin.h"
47 #include "ardour/location.h"
48 #include "ardour/panner.h"
49 #include "ardour/playlist.h"
50 #include "ardour/playlist.h"
51 #include "ardour/processor.h"
52 #include "ardour/profile.h"
53 #include "ardour/route_group.h"
54 #include "ardour/session.h"
55 #include "ardour/session_playlist.h"
56 #include "ardour/utils.h"
57 #include "evoral/Parameter.hpp"
58
59 #include "ardour_ui.h"
60 #include "route_time_axis.h"
61 #include "automation_time_axis.h"
62 #include "canvas_impl.h"
63 #include "crossfade_view.h"
64 #include "enums.h"
65 #include "gui_thread.h"
66 #include "keyboard.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
69 #include "prompter.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "simplerect.h"
75 #include "streamview.h"
76 #include "utils.h"
77
78 #include "ardour/track.h"
79
80 #include "i18n.h"
81
82 using namespace ARDOUR;
83 using namespace PBD;
84 using namespace Gtkmm2ext;
85 using namespace Gtk;
86 using namespace Editing;
87 using namespace sigc;
88 using namespace std;
89
90 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
91
92 void
93 RouteTimeAxisView::setup_slider_pix ()
94 {
95         if ((slider = ::get_icon ("fader_belt_h")) == 0) {
96                 throw failed_constructor ();
97         }
98 }
99
100 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session& sess, boost::shared_ptr<Route> rt, Canvas& canvas)
101         : AxisView(sess),
102           RouteUI(rt, sess, _("m"), _("s"), _("r")), // mute, solo, and record
103           TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas),
104           parent_canvas (canvas),
105           button_table (3, 3),
106           edit_group_button (_("g")), // group
107           playlist_button (_("p")), 
108           size_button (_("h")), // height
109           automation_button (_("a")),
110           visual_button (_("v")),
111           gm (sess, slider, true)
112 {
113         gm.set_io (rt);
114         gm.get_level_meter().set_no_show_all();
115         gm.get_level_meter().setup_meters(50);
116
117         _has_state = true;
118         playlist_menu = 0;
119         playlist_action_menu = 0;
120         automation_action_menu = 0;
121         _view = 0;
122
123         if (!_route->is_hidden()) {
124                 _marked_for_display = true;
125         }
126
127         timestretch_rect = 0;
128         no_redraw = false;
129         destructive_track_mode_item = 0;
130         normal_track_mode_item = 0;
131         non_layered_track_mode_item = 0;
132
133         ignore_toggle = false;
134
135         edit_group_button.set_name ("TrackGroupButton");
136         playlist_button.set_name ("TrackPlaylistButton");
137         automation_button.set_name ("TrackAutomationButton");
138         size_button.set_name ("TrackSizeButton");
139         visual_button.set_name ("TrackVisualButton");
140         hide_button.set_name ("TrackRemoveButton");
141
142         edit_group_button.unset_flags (Gtk::CAN_FOCUS);
143         playlist_button.unset_flags (Gtk::CAN_FOCUS);
144         automation_button.unset_flags (Gtk::CAN_FOCUS);
145         size_button.unset_flags (Gtk::CAN_FOCUS);
146         visual_button.unset_flags (Gtk::CAN_FOCUS);
147         hide_button.unset_flags (Gtk::CAN_FOCUS);
148
149         hide_button.add (*(manage (new Image (::get_icon("hide")))));
150         hide_button.show_all ();
151
152         edit_group_button.signal_button_release_event().connect (mem_fun(*this, &RouteTimeAxisView::edit_click), false);
153         playlist_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::playlist_click));
154         automation_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::automation_click));
155         size_button.signal_button_release_event().connect (mem_fun(*this, &RouteTimeAxisView::size_click), false);
156         visual_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::visual_click));
157         hide_button.signal_clicked().connect (mem_fun(*this, &RouteTimeAxisView::hide_click));
158
159         solo_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press), false);
160         solo_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release), false);
161         mute_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press), false);
162         mute_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release), false);
163
164         if (is_track()) {
165
166                 /* use icon */
167
168                 rec_enable_button->remove ();
169
170                 switch (track()->mode()) {
171                 case ARDOUR::Normal:
172                 case ARDOUR::NonLayered:
173                         rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
174                         break;
175                 case ARDOUR::Destructive:
176                         rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
177                         break;
178                 }
179                 rec_enable_button->show_all ();
180
181                 rec_enable_button->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press), false);
182                 rec_enable_button->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release));
183                 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
184                 ARDOUR_UI::instance()->tooltips().set_tip(*rec_enable_button, _("Record"));
185
186         }
187
188         controls_hbox.pack_start(gm.get_level_meter(), false, false);
189         _route->meter_change.connect (mem_fun(*this, &RouteTimeAxisView::meter_changed));
190         _route->input_changed.connect (mem_fun(*this, &RouteTimeAxisView::io_changed));
191         _route->output_changed.connect (mem_fun(*this, &RouteTimeAxisView::io_changed));
192
193         controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
194         controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
195
196         controls_table.attach (edit_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
197         controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
198
199         ARDOUR_UI::instance()->tooltips().set_tip(*solo_button,_("Solo"));
200         ARDOUR_UI::instance()->tooltips().set_tip(*mute_button,_("Mute"));
201         ARDOUR_UI::instance()->tooltips().set_tip(edit_group_button,_("Edit Group"));
202         ARDOUR_UI::instance()->tooltips().set_tip(size_button,_("Display Height"));
203         ARDOUR_UI::instance()->tooltips().set_tip(playlist_button,_("Playlist"));
204         ARDOUR_UI::instance()->tooltips().set_tip(automation_button, _("Automation"));
205         ARDOUR_UI::instance()->tooltips().set_tip(visual_button, _("Visual options"));
206         ARDOUR_UI::instance()->tooltips().set_tip(hide_button, _("Hide this track"));
207         
208         label_view ();
209
210         if (0) {
211
212                 /* old school - when we used to put an extra row of buttons in place */
213
214                 controls_table.attach (hide_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
215                 controls_table.attach (visual_button, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
216                 controls_table.attach (size_button, 2, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
217                 controls_table.attach (automation_button, 3, 4, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
218
219         } else {
220
221                 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
222         }
223
224         if (is_track() && track()->mode() == ARDOUR::Normal) {
225                 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
226         }
227
228         _y_position = -1;
229
230         _route->mute_changed.connect (mem_fun(*this, &RouteUI::mute_changed));
231         _route->solo_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
232         _route->processors_changed.connect (mem_fun(*this, &RouteTimeAxisView::processors_changed));
233         _route->NameChanged.connect (mem_fun(*this, &RouteTimeAxisView::route_name_changed));
234         _route->solo_safe_changed.connect (mem_fun(*this, &RouteUI::solo_changed));
235
236
237         if (is_track()) {
238
239                 track()->TrackModeChanged.connect (mem_fun(*this, &RouteTimeAxisView::track_mode_changed));
240                 track()->FreezeChange.connect (mem_fun(*this, &RouteTimeAxisView::map_frozen));
241                 track()->DiskstreamChanged.connect (mem_fun(*this, &RouteTimeAxisView::diskstream_changed));
242                 get_diskstream()->SpeedChanged.connect (mem_fun(*this, &RouteTimeAxisView::speed_changed));
243
244                 /* pick up the correct freeze state */
245                 map_frozen ();
246
247         }
248
249         _editor.ZoomChanged.connect (mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
250         ColorsChanged.connect (mem_fun (*this, &RouteTimeAxisView::color_handler));
251
252         gm.get_gain_slider().signal_scroll_event().connect(mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
253         gm.get_gain_slider().set_name ("TrackGainFader");
254 }
255
256 RouteTimeAxisView::~RouteTimeAxisView ()
257 {
258         GoingAway (); /* EMIT_SIGNAL */
259
260         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
261                 delete *i;
262         }
263
264         delete playlist_menu;
265         playlist_menu = 0;
266   
267         delete playlist_action_menu;
268         playlist_action_menu = 0;
269
270         delete _view;
271         _view = 0;
272
273         for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
274                 delete i->second;
275         }
276         
277         _automation_tracks.clear ();
278 }
279
280 void
281 RouteTimeAxisView::post_construct ()
282 {
283         /* map current state of the route */
284
285         update_diskstream_display ();
286
287         subplugin_menu.items().clear ();
288         _route->foreach_processor (mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
289         _route->foreach_processor (mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
290         reset_processor_automation_curves ();
291 }
292
293 void
294 RouteTimeAxisView::set_playlist (boost::shared_ptr<Playlist> newplaylist)
295 {
296         boost::shared_ptr<Playlist> pl = playlist();
297         assert(pl);
298
299         modified_connection.disconnect ();
300         modified_connection = pl->Modified.connect (mem_fun(*this, &RouteTimeAxisView::playlist_modified));
301 }
302
303 void
304 RouteTimeAxisView::playlist_modified ()
305 {
306 }
307
308 gint
309 RouteTimeAxisView::edit_click (GdkEventButton *ev)
310 {
311         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
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         label_view ();
358
359         if (is_track()) {
360                 set_playlist (get_diskstream()->playlist());
361         }
362 }
363
364 void
365 RouteTimeAxisView::label_view ()
366 {
367         string x = _route->name();
368
369         if (x != name_entry.get_text()) {
370                 name_entry.set_text (x);
371         }
372
373         ARDOUR_UI::instance()->tooltips().set_tip (name_entry, x);
374 }
375
376 void
377 RouteTimeAxisView::route_name_changed ()
378 {
379         _editor.route_name_changed (this);
380         label_view ();
381 }
382
383 void
384 RouteTimeAxisView::take_name_changed (void *src)
385
386 {
387         if (src != this) {
388                 label_view ();
389         }
390 }
391
392 void
393 RouteTimeAxisView::playlist_click ()
394 {
395         // always build a new action menu
396   
397         delete playlist_action_menu;
398
399         playlist_action_menu = new Menu;
400         playlist_action_menu->set_name ("ArdourContextMenu");
401         
402         build_playlist_menu (playlist_action_menu);
403
404         conditionally_add_to_selection ();
405         playlist_action_menu->popup (1, gtk_get_current_event_time());
406 }
407
408 void
409 RouteTimeAxisView::automation_click ()
410 {
411         conditionally_add_to_selection ();
412         build_automation_action_menu ();
413         automation_action_menu->popup (1, gtk_get_current_event_time());
414 }
415
416 int
417 RouteTimeAxisView::set_state (const XMLNode& node)
418 {
419         TimeAxisView::set_state (node);
420
421         XMLNodeList kids = node.children();
422         XMLNodeConstIterator iter;
423         const XMLProperty* prop;
424         
425         for (iter = kids.begin(); iter != kids.end(); ++iter) {
426                 if ((*iter)->name() == AutomationTimeAxisView::state_node_name) {
427                         if ((prop = (*iter)->property ("automation-id")) != 0) {
428
429                                 Evoral::Parameter param = ARDOUR::EventTypeMap::instance().new_parameter(prop->value());
430                                 bool show = ((prop = (*iter)->property ("shown")) != 0) && prop->value() == "yes";
431                                 create_automation_child(param, show);
432                         } else {
433                                 warning << "Automation child has no ID" << endmsg;
434                         }
435                 }
436         }
437
438         return 0;
439 }
440
441 void
442 RouteTimeAxisView::build_automation_action_menu ()
443 {
444         using namespace Menu_Helpers;
445
446         automation_action_menu = manage (new Menu);
447         MenuList& automation_items = automation_action_menu->items();
448         automation_action_menu->set_name ("ArdourContextMenu");
449         
450         automation_items.push_back (MenuElem (_("Show all automation"),
451                                               mem_fun(*this, &RouteTimeAxisView::show_all_automation)));
452
453         automation_items.push_back (MenuElem (_("Show existing automation"),
454                                               mem_fun(*this, &RouteTimeAxisView::show_existing_automation)));
455
456         automation_items.push_back (MenuElem (_("Hide all automation"),
457                                               mem_fun(*this, &RouteTimeAxisView::hide_all_automation)));
458
459         if (subplugin_menu.get_attach_widget())
460                 subplugin_menu.detach();
461
462         automation_items.push_back (MenuElem (_("Plugins"), subplugin_menu));
463         
464         map<Evoral::Parameter, RouteAutomationNode*>::iterator i;
465         for (i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
466
467                 automation_items.push_back (SeparatorElem());
468
469                 delete i->second->menu_item;
470
471                 automation_items.push_back(CheckMenuElem (_route->describe_parameter(i->second->param), 
472                                 bind (mem_fun(*this, &RouteTimeAxisView::toggle_automation_track), i->second->param)));
473
474                 i->second->menu_item = static_cast<Gtk::CheckMenuItem*>(&automation_items.back());
475
476                 i->second->menu_item->set_active(show_automation(i->second->param));
477                 //i->second->menu_item->set_active(false);
478         }
479 }
480
481 void
482 RouteTimeAxisView::build_display_menu ()
483 {
484         using namespace Menu_Helpers;
485
486         /* get the size menu ready */
487
488         build_size_menu ();
489
490         /* prepare it */
491
492         TimeAxisView::build_display_menu ();
493
494         /* now fill it with our stuff */
495
496         MenuList& items = display_menu->items();
497         display_menu->set_name ("ArdourContextMenu");
498         
499         items.push_back (MenuElem (_("Height"), *size_menu));
500         items.push_back (MenuElem (_("Color"), mem_fun(*this, &RouteTimeAxisView::select_track_color)));
501
502         items.push_back (SeparatorElem());
503
504         if (!Profile->get_sae()) {
505                 build_remote_control_menu ();
506                 items.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu));
507                 /* rebuild this every time */
508                 build_automation_action_menu ();
509                 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
510                 items.push_back (SeparatorElem());
511         }
512
513         // Hook for derived classes to add type specific stuff
514         append_extra_display_menu_items ();
515         items.push_back (SeparatorElem());
516         
517         if (is_track()) {
518
519                 Menu *layers_menu = manage(new Menu);
520                 MenuList &layers_items = layers_menu->items();
521                 layers_menu->set_name("ArdourContextMenu");
522
523                 RadioMenuItem::Group layers_group;
524
525                 layers_items.push_back(RadioMenuElem (layers_group, _("Overlaid"),
526                                 bind (mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid)));
527                 layers_items.push_back(RadioMenuElem (layers_group, _("Stacked"),
528                                 bind (mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked)));
529
530                 items.push_back (MenuElem (_("Layers"), *layers_menu));
531
532                 Menu* alignment_menu = manage (new Menu);
533                 MenuList& alignment_items = alignment_menu->items();
534                 alignment_menu->set_name ("ArdourContextMenu");
535
536                 RadioMenuItem::Group align_group;
537
538                 alignment_items.push_back (RadioMenuElem (align_group, _("Align with existing material"),
539                                         bind (mem_fun(*this, &RouteTimeAxisView::set_align_style), ExistingMaterial)));
540                 align_existing_item = dynamic_cast<RadioMenuItem*>(&alignment_items.back());
541                 if (get_diskstream()->alignment_style() == ExistingMaterial)
542                         align_existing_item->set_active();
543
544                 alignment_items.push_back (RadioMenuElem (align_group, _("Align with capture time"),
545                                         bind (mem_fun(*this, &RouteTimeAxisView::set_align_style), CaptureTime)));
546                 align_capture_item = dynamic_cast<RadioMenuItem*>(&alignment_items.back());
547                 if (get_diskstream()->alignment_style() == CaptureTime)
548                         align_capture_item->set_active();
549
550                 if (!Profile->get_sae()) {
551                         items.push_back (MenuElem (_("Alignment"), *alignment_menu));
552                         get_diskstream()->AlignmentStyleChanged.connect (
553                                         mem_fun(*this, &RouteTimeAxisView::align_style_changed));
554                         
555                         RadioMenuItem::Group mode_group;
556                         items.push_back (RadioMenuElem (mode_group, _("Normal mode"), bind (
557                                         mem_fun (*this, &RouteTimeAxisView::set_track_mode),
558                                         ARDOUR::Normal)));
559                         normal_track_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
560
561                         items.push_back (RadioMenuElem (mode_group, _("Tape mode"), bind (
562                                         mem_fun (*this, &RouteTimeAxisView::set_track_mode),
563                                         ARDOUR::Destructive)));
564                         destructive_track_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
565
566                         items.push_back (RadioMenuElem (mode_group, _("No layering mode"),
567                                                         bind (mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered)));
568                         non_layered_track_mode_item = dynamic_cast<RadioMenuItem*>(&items.back());
569  
570
571                         
572                         switch (track()->mode()) {
573                         case ARDOUR::Destructive:
574                                 destructive_track_mode_item->set_active ();
575                                 break;
576                         case ARDOUR::Normal:
577                                 normal_track_mode_item->set_active ();
578                                 break;
579                         case ARDOUR::NonLayered:
580                                 non_layered_track_mode_item->set_active ();
581                                 break;
582                         }
583                 }
584
585                 get_diskstream()->AlignmentStyleChanged.connect (
586                                 mem_fun(*this, &RouteTimeAxisView::align_style_changed));
587
588                 mode_menu = build_mode_menu();
589                 if (mode_menu)
590                         items.push_back (MenuElem (_("Mode"), *mode_menu));
591                         
592                 color_mode_menu = build_color_mode_menu();
593                 if (color_mode_menu)
594                         items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
595                         
596                 items.push_back (SeparatorElem());
597         }
598
599         items.push_back (CheckMenuElem (_("Active"), mem_fun(*this, &RouteUI::toggle_route_active)));
600         route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back());
601         route_active_menu_item->set_active (_route->active());
602
603         items.push_back (SeparatorElem());
604         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &RouteTimeAxisView::hide_click)));
605         if (!Profile->get_sae()) {
606                 items.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route)));
607         } else {
608                 items.push_front (SeparatorElem());
609                 items.push_front (MenuElem (_("Delete"), mem_fun(*this, &RouteUI::remove_this_route)));
610         }
611 }
612
613 static bool __reset_item (RadioMenuItem* item, RadioMenuItem* item_2)
614 {
615         item->set_active ();
616         item_2->set_active ();
617         return false;
618 }
619
620 void
621 RouteTimeAxisView::set_track_mode (TrackMode mode)
622 {
623         RadioMenuItem* item;
624         RadioMenuItem* other_item;
625         RadioMenuItem* other_item_2;
626
627         switch (mode) {
628         case ARDOUR::Normal:
629                 item = normal_track_mode_item;
630                 other_item = non_layered_track_mode_item;
631                 other_item_2 = destructive_track_mode_item;
632                 break;
633         case ARDOUR::NonLayered:
634                 item = non_layered_track_mode_item;
635                 other_item = normal_track_mode_item;
636                 other_item_2 = destructive_track_mode_item;
637                 break;
638         case ARDOUR::Destructive:
639                 item = destructive_track_mode_item;
640                 other_item = normal_track_mode_item;
641                 other_item_2 = non_layered_track_mode_item;
642                 break;
643         default:
644                 fatal << string_compose (_("programming error: %1 %2"), "illegal track mode in RouteTimeAxisView::set_track_mode", mode) << endmsg;
645                 /*NOTREACHED*/
646                 return;
647         }
648         
649         if (item && other_item && other_item_2 && item->get_active() && track()->mode() != mode) {
650                 _set_track_mode (track().get(), mode, other_item, other_item_2);
651         }
652 }
653
654 void
655 RouteTimeAxisView::_set_track_mode (Track* track, TrackMode mode, RadioMenuItem* reset_item, RadioMenuItem* reset_item_2)
656 {
657         bool needs_bounce;
658
659         if (!track->can_use_mode (mode, needs_bounce)) {
660
661                 if (!needs_bounce) {
662                         /* cannot be done */
663                         Glib::signal_idle().connect (bind (sigc::ptr_fun (__reset_item), reset_item, reset_item_2));
664                         return;
665                 } else {
666                         cerr << "would bounce this one\n";
667                         return;
668                 }
669         }
670
671         track->set_mode (mode);
672
673         rec_enable_button->remove ();
674
675         switch (mode) {
676         case ARDOUR::NonLayered:
677         case ARDOUR::Normal:
678                 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
679                 break;
680         case ARDOUR::Destructive:
681                 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
682                 break;
683         }
684
685         rec_enable_button->show_all ();
686 }
687
688 void
689 RouteTimeAxisView::track_mode_changed ()
690 {
691         RadioMenuItem* item;
692         
693         switch (track()->mode()) {
694         case ARDOUR::Normal:
695                 item = normal_track_mode_item;
696                 break;
697         case ARDOUR::NonLayered:
698                 item = non_layered_track_mode_item;
699                 break;
700         case ARDOUR::Destructive:
701                 item = destructive_track_mode_item;
702                 break;
703         default:
704                 fatal << string_compose (_("programming error: %1 %2"), "illegal track mode in RouteTimeAxisView::set_track_mode", track()->mode()) << endmsg;
705                 /*NOTREACHED*/
706                 return;
707         }
708
709         item->set_active ();
710 }
711
712 void
713 RouteTimeAxisView::show_timestretch (nframes_t start, nframes_t end)
714 {
715         double x1;
716         double x2;
717         double y2;
718         
719         TimeAxisView::show_timestretch (start, end);
720
721         hide_timestretch ();
722
723 #if 0   
724         if (ts.empty()) {
725                 return;
726         }
727
728
729         /* check that the time selection was made in our route, or our edit group.
730            remember that edit_group() == 0 implies the route is *not* in a edit group.
731         */
732
733         if (!(ts.track == this || (ts.group != 0 && ts.group == _route->edit_group()))) {
734                 /* this doesn't apply to us */
735                 return;
736         }
737
738         /* ignore it if our edit group is not active */
739         
740         if ((ts.track != this) && _route->edit_group() && !_route->edit_group()->is_active()) {
741                 return;
742         }
743 #endif
744
745         if (timestretch_rect == 0) {
746                 timestretch_rect = new SimpleRect (*canvas_display ());
747                 timestretch_rect->property_x1() =  0.0;
748                 timestretch_rect->property_y1() =  0.0;
749                 timestretch_rect->property_x2() =  0.0;
750                 timestretch_rect->property_y2() =  0.0;
751                 timestretch_rect->property_fill_color_rgba() =  ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
752                 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
753         }
754
755         timestretch_rect->show ();
756         timestretch_rect->raise_to_top ();
757
758         x1 = start / _editor.get_current_zoom();
759         x2 = (end - 1) / _editor.get_current_zoom();
760         y2 = current_height() - 2;
761         
762         timestretch_rect->property_x1() = x1;
763         timestretch_rect->property_y1() = 1.0;
764         timestretch_rect->property_x2() = x2;
765         timestretch_rect->property_y2() = y2;
766 }
767
768 void
769 RouteTimeAxisView::hide_timestretch ()
770 {
771         TimeAxisView::hide_timestretch ();
772
773         if (timestretch_rect) {
774                 timestretch_rect->hide ();
775         }
776 }
777
778 void
779 RouteTimeAxisView::show_selection (TimeSelection& ts)
780 {
781
782 #if 0
783         /* ignore it if our edit group is not active or if the selection was started
784            in some other track or edit group (remember that edit_group() == 0 means
785            that the track is not in an edit group).
786         */
787
788         if (((ts.track != this && !is_child (ts.track)) && _route->edit_group() && !_route->edit_group()->is_active()) ||
789             (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->edit_group())))) {
790                 hide_selection ();
791                 return;
792         }
793 #endif
794
795         TimeAxisView::show_selection (ts);
796 }
797
798 void
799 RouteTimeAxisView::set_height (uint32_t h)
800 {
801         int gmlen = h - 5;
802         bool height_changed = (height == 0) || (h != height);
803         gm.get_level_meter().setup_meters (gmlen);
804
805         TimeAxisView::set_height (h);
806
807         ensure_xml_node ();
808
809         if (_view) {
810                 _view->set_height ((double) current_height());
811         }
812
813         char buf[32];
814         snprintf (buf, sizeof (buf), "%u", height);
815         xml_node->add_property ("height", buf);
816
817         if (height >= hNormal) {
818                 reset_meter();
819                 show_name_entry ();
820                 hide_name_label ();
821
822                 gm.get_gain_slider().show();
823                 mute_button->show();
824                 solo_button->show();
825                 if (rec_enable_button)
826                         rec_enable_button->show();
827
828                 edit_group_button.show();
829                 hide_button.show();
830                 visual_button.show();
831                 size_button.show();
832                 automation_button.show();
833                 
834                 if (is_track() && track()->mode() == ARDOUR::Normal) {
835                         playlist_button.show();
836                 }
837
838         } else if (height >= hSmaller) {
839
840                 reset_meter();
841                 show_name_entry ();
842                 hide_name_label ();
843
844                 gm.get_gain_slider().hide();
845                 mute_button->show();
846                 solo_button->show();
847                 if (rec_enable_button)
848                         rec_enable_button->show();
849
850                 edit_group_button.hide ();
851                 hide_button.hide ();
852                 visual_button.hide ();
853                 size_button.hide ();
854                 automation_button.hide ();
855                 
856                 if (is_track() && track()->mode() == ARDOUR::Normal) {
857                         playlist_button.hide ();
858                 }
859
860         } else {
861
862
863                 /* don't allow name_entry to be hidden while
864                    it has focus, otherwise the GUI becomes unusable.
865                 */
866
867                 if (name_entry.has_focus()) {
868                         if (name_entry.get_text() != _route->name()) {
869                                 name_entry_changed ();
870                         }
871                         controls_ebox.grab_focus ();
872                 }
873
874                 hide_name_entry ();
875                 show_name_label ();
876                 
877                 gm.get_gain_slider().hide();
878                 mute_button->hide();
879                 solo_button->hide();
880                 if (rec_enable_button)
881                         rec_enable_button->hide();
882
883                 edit_group_button.hide ();
884                 hide_button.hide ();
885                 visual_button.hide ();
886                 size_button.hide ();
887                 automation_button.hide ();
888                 playlist_button.hide ();
889                 name_label.set_text (_route->name());
890         }
891
892         if (height_changed) {
893                 /* only emit the signal if the height really changed */
894                  _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
895         }
896 }
897
898 void
899 RouteTimeAxisView::select_track_color ()
900 {
901         if (RouteUI::choose_color ()) {
902
903                 if (_view) {
904                         _view->apply_color (_color, StreamView::RegionColor);
905                 }
906         }
907 }
908
909 void
910 RouteTimeAxisView::reset_samples_per_unit ()
911 {
912         set_samples_per_unit (_editor.get_current_zoom());
913 }
914
915 void
916 RouteTimeAxisView::set_samples_per_unit (double spu)
917 {
918         double speed = 1.0;
919
920         if (get_diskstream() != 0) {
921                 speed = get_diskstream()->speed();
922         }
923         
924         if (_view) {
925                 _view->set_samples_per_unit (spu * speed);
926         }
927
928         TimeAxisView::set_samples_per_unit (spu * speed);
929 }
930
931 void
932 RouteTimeAxisView::align_style_changed ()
933 {
934         switch (get_diskstream()->alignment_style()) {
935         case ExistingMaterial:
936                 if (!align_existing_item->get_active()) {
937                         align_existing_item->set_active();
938                 }
939                 break;
940         case CaptureTime:
941                 if (!align_capture_item->get_active()) {
942                         align_capture_item->set_active();
943                 }
944                 break;
945         }
946 }
947
948 void
949 RouteTimeAxisView::set_align_style (AlignStyle style)
950 {
951         RadioMenuItem* item;
952
953         switch (style) {
954         case ExistingMaterial:
955                 item = align_existing_item;
956                 break;
957         case CaptureTime:
958                 item = align_capture_item;
959                 break;
960         default:
961                 fatal << string_compose (_("programming error: %1 %2"), "illegal align style in RouteTimeAxisView::set_align_style", style) << endmsg;
962                 /*NOTREACHED*/
963                 return;
964         }
965
966         if (item->get_active()) {
967                 get_diskstream()->set_align_style (style);
968         }
969 }
970
971 void
972 RouteTimeAxisView::rename_current_playlist ()
973 {
974         ArdourPrompter prompter (true);
975         string name;
976
977         boost::shared_ptr<Diskstream> ds = get_diskstream();
978         if (!ds || ds->destructive())
979                 return;
980
981         boost::shared_ptr<Playlist> pl = ds->playlist();
982         if (!pl)
983                 return;
984
985         prompter.set_prompt (_("Name for playlist"));
986         prompter.set_initial_text (pl->name());
987         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
988         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
989
990         switch (prompter.run ()) {
991         case Gtk::RESPONSE_ACCEPT:
992                 prompter.get_result (name);
993                 if (name.length()) {
994                         pl->set_name (name);
995                 }
996                 break;
997
998         default:
999                 break;
1000         }
1001 }
1002
1003 std::string 
1004 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1005 {
1006         std::string ret(basename);
1007
1008         std::string group_string = "."+edit_group()->name()+".";
1009
1010         // iterate through all playlists
1011         int maxnumber = 0;
1012         for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1013                 std::string tmp = (*i)->name();
1014
1015                 std::string::size_type idx = tmp.find(group_string);                    
1016                 // find those which belong to this group
1017                 if (idx != string::npos) {
1018                         tmp = tmp.substr(idx + group_string.length());
1019
1020                         // and find the largest current number
1021                         int x = atoi(tmp.c_str());
1022                         if (x > maxnumber) {
1023                                 maxnumber = x;
1024                         }
1025                 }
1026         }
1027
1028         maxnumber++;
1029
1030         char buf[32];
1031         snprintf (buf, sizeof(buf), "%d", maxnumber);
1032                
1033         ret = this->name()+"."+edit_group()->name()+"."+buf;
1034
1035         return ret;
1036 }
1037
1038 void
1039 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1040 {
1041         string name;
1042         
1043         boost::shared_ptr<Diskstream> ds = get_diskstream();
1044         if (!ds || ds->destructive())
1045                 return;
1046
1047         boost::shared_ptr<const Playlist> pl = ds->playlist();
1048         if (!pl)
1049                 return;
1050
1051         name = pl->name();
1052         
1053         if (edit_group() && edit_group()->is_active()) {
1054                 name = resolve_new_group_playlist_name(name, playlists_before_op);
1055         }
1056
1057         while (_session.playlist_by_name(name)) {
1058                 name = Playlist::bump_name (name, _session);
1059         }
1060
1061         // TODO: The prompter "new" button should be de-activated if the user
1062         // specifies a playlist name which already exists in the session.
1063
1064         if (prompt) {
1065
1066                 ArdourPrompter prompter (true);
1067                 
1068                 prompter.set_prompt (_("Name for Playlist"));
1069                 prompter.set_initial_text (name);
1070                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1071                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1072                 prompter.show_all ();
1073                 
1074                 switch (prompter.run ()) {
1075                 case Gtk::RESPONSE_ACCEPT:
1076                         prompter.get_result (name);
1077                         break;
1078                         
1079                 default:
1080                         return;
1081                 }
1082         }
1083
1084         if (name.length()) {
1085                 ds->use_copy_playlist ();
1086                 ds->playlist()->set_name (name);
1087         }
1088 }
1089
1090 void
1091 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1092 {
1093         string name;
1094         
1095         boost::shared_ptr<Diskstream> ds = get_diskstream();
1096         if (!ds || ds->destructive())
1097                 return;
1098
1099         boost::shared_ptr<const Playlist> pl = ds->playlist();
1100         if (!pl)
1101                 return;
1102
1103         name = pl->name();
1104         
1105         if (edit_group() && edit_group()->is_active()) {
1106                 name = resolve_new_group_playlist_name(name,playlists_before_op);
1107         }
1108
1109         while (_session.playlist_by_name(name)) {
1110                 name = Playlist::bump_name (name, _session);
1111         }
1112
1113
1114         if (prompt) {
1115                 
1116                 ArdourPrompter prompter (true);
1117                 
1118                 prompter.set_prompt (_("Name for Playlist"));
1119                 prompter.set_initial_text (name);
1120                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1121                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1122
1123                 switch (prompter.run ()) {
1124                 case Gtk::RESPONSE_ACCEPT:
1125                         prompter.get_result (name);
1126                         break;
1127                         
1128                 default:
1129                         return;
1130                 }
1131         }
1132
1133         if (name.length()) {
1134                 ds->use_new_playlist ();
1135                 ds->playlist()->set_name (name);
1136         }
1137 }
1138
1139 void
1140 RouteTimeAxisView::clear_playlist ()
1141 {
1142         boost::shared_ptr<Diskstream> ds = get_diskstream();
1143         if (!ds || ds->destructive())
1144                 return;
1145
1146         boost::shared_ptr<Playlist> pl = ds->playlist();
1147         if (!pl)
1148                 return;
1149
1150         _editor.clear_playlist (pl);
1151 }
1152
1153 void
1154 RouteTimeAxisView::speed_changed ()
1155 {
1156         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
1157 }
1158
1159 void
1160 RouteTimeAxisView::diskstream_changed ()
1161 {
1162         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &RouteTimeAxisView::update_diskstream_display));
1163 }       
1164
1165 void
1166 RouteTimeAxisView::update_diskstream_display ()
1167 {
1168         if (!get_diskstream()) // bus
1169                 return;
1170
1171         set_playlist (get_diskstream()->playlist());
1172         map_frozen ();
1173 }       
1174
1175 void
1176 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1177 {
1178         if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1179
1180                 /* special case: select/deselect all tracks */
1181                 if (_editor.get_selection().selected (this)) {
1182                         _editor.get_selection().clear_tracks ();
1183                 } else {
1184                         _editor.select_all_tracks ();
1185                 }
1186
1187                 return;
1188         } 
1189
1190         PublicEditor::TrackViewList* tracks = _editor.get_valid_views (this, _route->edit_group());
1191
1192         switch (Keyboard::selection_type (ev->state)) {
1193         case Selection::Toggle:
1194                 _editor.get_selection().toggle (*tracks);
1195                 break;
1196                 
1197         case Selection::Set:
1198                 _editor.get_selection().set (*tracks);
1199                 break;
1200
1201         case Selection::Extend:
1202                 if (tracks->size() > 1) {
1203                         /* add each one, do not "extend" */
1204                         _editor.get_selection().add (*tracks);
1205                 } else {
1206                         /* extend to the single track */
1207                         _editor.extend_selection_to_track (*tracks->front());
1208                 }
1209                 break;
1210
1211         case Selection::Add:
1212                 _editor.get_selection().add (*tracks);
1213                 break;
1214         }
1215
1216         delete tracks;
1217 }
1218
1219 void
1220 RouteTimeAxisView::set_selected_points (PointSelection& points)
1221 {
1222         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1223                 (*i)->set_selected_points (points);
1224         }
1225 }
1226
1227 void
1228 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1229 {
1230         if (_view) {
1231                 _view->set_selected_regionviews (regions);
1232         }
1233 }
1234
1235 /** Add the selectable things that we have to a list.
1236  * @param results List to add things to.
1237  */
1238 void
1239 RouteTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
1240 {
1241         double speed = 1.0;
1242         
1243         if (get_diskstream() != 0) {
1244                 speed = get_diskstream()->speed();
1245         }
1246         
1247         nframes_t start_adjusted = session_frame_to_track_frame(start, speed);
1248         nframes_t end_adjusted   = session_frame_to_track_frame(end, speed);
1249
1250         if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1251                 _view->get_selectables (start_adjusted, end_adjusted, results);
1252         }
1253
1254         /* pick up visible automation tracks */
1255         
1256         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1257                 if (!(*i)->hidden()) {
1258                         (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1259                 }
1260         }
1261 }
1262
1263 void
1264 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1265 {
1266         if (_view) {
1267                 _view->get_inverted_selectables (sel, results);
1268         }
1269
1270         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1271                 if (!(*i)->hidden()) {
1272                         (*i)->get_inverted_selectables (sel, results);
1273                 }
1274         }
1275
1276         return;
1277 }
1278
1279 bool
1280 RouteTimeAxisView::show_automation(Evoral::Parameter param)
1281 {
1282         return (_show_automation.find(param) != _show_automation.end());
1283 }
1284
1285 /** Retuns 0 if track for \a param doesn't exist.
1286  */
1287 RouteTimeAxisView::RouteAutomationNode*
1288 RouteTimeAxisView::automation_track (Evoral::Parameter param)
1289 {
1290         map<Evoral::Parameter, RouteAutomationNode*>::iterator i = _automation_tracks.find (param);
1291
1292         if (i != _automation_tracks.end()) {
1293                 return i->second;
1294         } else {
1295                 return 0;
1296         }
1297 }
1298
1299 /** Shorthand for GainAutomation, etc.
1300  */     
1301 RouteTimeAxisView::RouteAutomationNode*
1302 RouteTimeAxisView::automation_track (AutomationType type)
1303 {
1304         return automation_track (Evoral::Parameter(type));
1305 }
1306
1307 RouteGroup*
1308 RouteTimeAxisView::edit_group() const
1309 {
1310         return _route->edit_group();
1311 }
1312
1313 string
1314 RouteTimeAxisView::name() const
1315 {
1316         return _route->name();
1317 }
1318
1319 boost::shared_ptr<Playlist>
1320 RouteTimeAxisView::playlist () const 
1321 {
1322         boost::shared_ptr<Diskstream> ds;
1323
1324         if ((ds = get_diskstream()) != 0) {
1325                 return ds->playlist(); 
1326         } else {
1327                 return boost::shared_ptr<Playlist> ();
1328         }
1329 }
1330
1331 void
1332 RouteTimeAxisView::name_entry_changed ()
1333 {
1334         string x;
1335
1336         x = name_entry.get_text ();
1337         
1338         if (x == _route->name()) {
1339                 return;
1340         }
1341
1342         strip_whitespace_edges(x);
1343
1344         if (x.length() == 0) {
1345                 name_entry.set_text (_route->name());
1346                 return;
1347         }
1348
1349         if (!_session.route_name_unique (x)) {
1350                 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1351                 name_entry.set_text (_route->name());
1352         } else if (_session.route_name_internal (x)) {
1353                 ARDOUR_UI::instance()->popup_error (_("You cannot create a track with that name as it is reserved for Ardour"));
1354                 name_entry.set_text (_route->name());
1355         } else {
1356                 _route->set_name (x);
1357         }
1358 }
1359
1360 void
1361 RouteTimeAxisView::visual_click ()
1362 {
1363         popup_display_menu (0);
1364 }
1365
1366 void
1367 RouteTimeAxisView::hide_click ()
1368 {
1369         // LAME fix for hide_button refresh fix
1370         hide_button.set_sensitive(false);
1371         
1372         _editor.hide_track_in_display (*this);
1373         
1374         hide_button.set_sensitive(true);
1375 }
1376
1377 boost::shared_ptr<Region>
1378 RouteTimeAxisView::find_next_region (nframes_t pos, RegionPoint point, int32_t dir)
1379 {
1380         boost::shared_ptr<Diskstream> stream;
1381         boost::shared_ptr<Playlist> playlist;
1382
1383         if ((stream = get_diskstream()) != 0 && (playlist = stream->playlist()) != 0) {
1384                 return playlist->find_next_region (pos, point, dir);
1385         }
1386
1387         return boost::shared_ptr<Region> ();
1388 }
1389
1390 nframes64_t 
1391 RouteTimeAxisView::find_next_region_boundary (nframes64_t pos, int32_t dir)
1392 {
1393         boost::shared_ptr<Diskstream> stream;
1394         boost::shared_ptr<Playlist> playlist;
1395
1396         if ((stream = get_diskstream()) != 0 && (playlist = stream->playlist()) != 0) {
1397                 return playlist->find_next_region_boundary (pos, dir);
1398         }
1399
1400         return -1;
1401 }
1402
1403 bool
1404 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1405 {
1406         boost::shared_ptr<Playlist> what_we_got;
1407         boost::shared_ptr<Diskstream> ds = get_diskstream();
1408         boost::shared_ptr<Playlist> playlist;
1409         bool ret = false;
1410
1411         if (ds == 0) {
1412                 /* route is a bus, not a track */
1413                 return false;
1414         }
1415
1416         playlist = ds->playlist();
1417
1418         TimeSelection time (selection.time);
1419         float speed = ds->speed();
1420         if (speed != 1.0f) {
1421                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1422                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1423                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1424                 }
1425         }
1426         
1427         XMLNode &before = playlist->get_state();
1428         switch (op) {
1429         case Cut:
1430                 if ((what_we_got = playlist->cut (time)) != 0) {
1431                         _editor.get_cut_buffer().add (what_we_got);
1432                         _session.add_command( new MementoCommand<Playlist>(*playlist.get(), &before, &playlist->get_state()));
1433                         ret = true;
1434                 }
1435                 break;
1436         case Copy:
1437                 if ((what_we_got = playlist->copy (time)) != 0) {
1438                         _editor.get_cut_buffer().add (what_we_got);
1439                 }
1440                 break;
1441
1442         case Clear:
1443                 if ((what_we_got = playlist->cut (time)) != 0) {
1444                         _session.add_command( new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1445                         what_we_got->release ();
1446                         ret = true;
1447                 }
1448                 break;
1449         }
1450
1451         return ret;
1452 }
1453
1454 bool
1455 RouteTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
1456 {
1457         if (!is_track()) {
1458                 return false;
1459         }
1460
1461         boost::shared_ptr<Playlist> playlist = get_diskstream()->playlist();
1462         PlaylistSelection::iterator p;
1463         
1464         for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1465
1466         if (p == selection.playlists.end()) {
1467                 return false;
1468         }
1469
1470         if (get_diskstream()->speed() != 1.0f)
1471                 pos = session_frame_to_track_frame(pos, get_diskstream()->speed() );
1472         
1473         XMLNode &before = playlist->get_state();
1474         playlist->paste (*p, pos, times);
1475         _session.add_command( new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1476
1477         return true;
1478 }
1479
1480
1481 TimeAxisView::Children
1482 RouteTimeAxisView::get_child_list()
1483 {
1484         TimeAxisView::Children redirect_children;
1485         
1486         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1487                 if (!(*i)->hidden()) {
1488                         redirect_children.push_back(*i);
1489                 }
1490         }
1491         return redirect_children;
1492 }
1493
1494
1495 void
1496 RouteTimeAxisView::build_playlist_menu (Gtk::Menu * menu)
1497 {
1498         using namespace Menu_Helpers;
1499
1500         if (!menu || !is_track()) {
1501                 return;
1502         }
1503
1504         MenuList& playlist_items = menu->items();
1505         menu->set_name ("ArdourContextMenu");
1506         playlist_items.clear();
1507
1508         delete playlist_menu;
1509
1510         playlist_menu = new Menu;
1511         playlist_menu->set_name ("ArdourContextMenu");
1512
1513         vector<boost::shared_ptr<Playlist> > playlists;
1514         boost::shared_ptr<Diskstream> ds = get_diskstream();
1515         RadioMenuItem::Group playlist_group;
1516
1517         _session.get_playlists (playlists);
1518         
1519         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1520
1521                 if ((*i)->get_orig_diskstream_id() == ds->id()) {
1522                         playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name(), bind (mem_fun (*this, &RouteTimeAxisView::use_playlist),
1523                                                                                                      boost::weak_ptr<Playlist> (*i))));
1524
1525                         if (ds->playlist()->id() == (*i)->id()) {
1526                                 static_cast<RadioMenuItem*>(&playlist_items.back())->set_active();
1527                         }
1528                 } else if (ds->playlist()->id() == (*i)->id()) {
1529                         playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name(), bind (mem_fun (*this, &RouteTimeAxisView::use_playlist), 
1530                                                                                                      boost::weak_ptr<Playlist>(*i))));
1531                         static_cast<RadioMenuItem*>(&playlist_items.back())->set_active();
1532                         
1533                 }
1534         }
1535
1536         playlist_items.push_back (SeparatorElem());
1537         playlist_items.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1538         playlist_items.push_back (SeparatorElem());
1539
1540         if (!edit_group() || !edit_group()->is_active()) {
1541                 playlist_items.push_back (MenuElem (_("New"), bind(mem_fun(_editor, &PublicEditor::new_playlists), this)));
1542                 playlist_items.push_back (MenuElem (_("New Copy"), bind(mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1543
1544         } else {
1545                 // Use a label which tells the user what is happening
1546                 playlist_items.push_back (MenuElem (_("New Take"), bind(mem_fun(_editor, &PublicEditor::new_playlists), this)));
1547                 playlist_items.push_back (MenuElem (_("Copy Take"), bind(mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1548                 
1549         }
1550
1551         playlist_items.push_back (SeparatorElem());
1552         playlist_items.push_back (MenuElem (_("Clear Current"), bind(mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1553         playlist_items.push_back (SeparatorElem());
1554
1555         playlist_items.push_back (MenuElem(_("Select from all ..."), mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1556 }
1557
1558 void
1559 RouteTimeAxisView::use_playlist (boost::weak_ptr<Playlist> wpl)
1560 {
1561         assert (is_track());
1562
1563         boost::shared_ptr<Playlist> pl (wpl.lock());
1564
1565         if (!pl) {
1566                 return;
1567         }
1568
1569         boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
1570         
1571         if (apl) {
1572                 if (get_diskstream()->playlist() == apl) {
1573                         // radio button cotnrols mean this function is called for both the 
1574                         // old and new playlist
1575                         return;
1576                 }
1577                 get_diskstream()->use_playlist (apl);
1578
1579
1580                 if (edit_group() && edit_group()->is_active()) {
1581                         //PBD::stacktrace(cerr, 20);
1582                         std::string group_string = "."+edit_group()->name()+".";
1583
1584                         std::string take_name = apl->name();
1585                         std::string::size_type idx = take_name.find(group_string);
1586
1587                         if (idx == std::string::npos)
1588                                 return;
1589
1590                         take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1591                         
1592                         for (list<Route*>::const_iterator i = edit_group()->route_list().begin(); i != edit_group()->route_list().end(); ++i) {
1593                                 if ( (*i) == this->route().get()) {
1594                                         continue;
1595                                 }
1596                                 
1597                                 std::string playlist_name = (*i)->name()+group_string+take_name;
1598
1599                                 Track *track = dynamic_cast<Track *>(*i);
1600                                 if (!track) {
1601                                         std::cerr << "route " << (*i)->name() << " is not a Track" << std::endl;
1602                                         continue;
1603                                 }
1604
1605                                 boost::shared_ptr<Playlist> ipl = session().playlist_by_name(playlist_name);
1606                                 if (!ipl) {
1607                                         // No playlist for this track for this take yet, make it
1608                                         track->diskstream()->use_new_playlist();
1609                                         track->diskstream()->playlist()->set_name(playlist_name);
1610                                 } else {
1611                                         track->diskstream()->use_playlist(ipl);
1612                                 }
1613                                 
1614                                 //(*i)->get_dis
1615                         }
1616                 }
1617         }
1618 }
1619
1620 void
1621 RouteTimeAxisView::show_playlist_selector ()
1622 {
1623         _editor.playlist_selector().show_for (this);
1624 }
1625
1626 void
1627 RouteTimeAxisView::map_frozen ()
1628 {
1629         if (!is_track()) {
1630                 return;
1631         }
1632
1633         ENSURE_GUI_THREAD (mem_fun(*this, &RouteTimeAxisView::map_frozen));
1634
1635         switch (track()->freeze_state()) {
1636         case Track::Frozen:
1637                 playlist_button.set_sensitive (false);
1638                 rec_enable_button->set_sensitive (false);
1639                 break;
1640         default:
1641                 playlist_button.set_sensitive (true);
1642                 rec_enable_button->set_sensitive (true);
1643                 break;
1644         }
1645 }
1646
1647 void
1648 RouteTimeAxisView::color_handler ()
1649 {
1650         //case cTimeStretchOutline:
1651         if (timestretch_rect) {
1652                 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1653         }
1654         //case cTimeStretchFill:
1655         if (timestretch_rect) {
1656                 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1657         }
1658
1659         reset_meter();
1660 }
1661
1662 void
1663 RouteTimeAxisView::toggle_automation_track (Evoral::Parameter param)
1664 {
1665         RouteAutomationNode* node = automation_track(param);
1666
1667         if (!node)
1668                 return;
1669
1670         bool showit = node->menu_item->get_active();
1671
1672         if (showit != node->track->marked_for_display()) {
1673                 if (showit) {
1674                         node->track->set_marked_for_display (true);
1675                         node->track->canvas_display()->show();
1676                         node->track->get_state_node()->add_property ("shown", X_("yes"));
1677                 } else {
1678                         node->track->set_marked_for_display (false);
1679                         node->track->hide ();
1680                         node->track->get_state_node()->add_property ("shown", X_("no"));
1681                 }
1682
1683                 /* now trigger a redisplay */
1684                 
1685                 if (!no_redraw) {
1686                          _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1687                 }
1688         }
1689 }
1690
1691 void
1692 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1693 {
1694         RouteAutomationNode* ran = automation_track(param);
1695         if (!ran) {
1696                 return;
1697         }
1698         
1699         // if Evoral::Parameter::operator< doesn't obey strict weak ordering, we may crash here....
1700         _show_automation.erase(param);
1701         ran->track->get_state_node()->add_property (X_("shown"), X_("no"));
1702
1703         if (ran->menu_item && !_hidden) {
1704                 ran->menu_item->set_active (false);
1705         }
1706
1707          _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
1708 }
1709
1710
1711 void
1712 RouteTimeAxisView::show_all_automation ()
1713 {
1714         no_redraw = true;
1715         
1716         /* Show our automation */
1717
1718         map<Evoral::Parameter, RouteAutomationNode*>::iterator i;
1719         for (i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1720                 i->second->track->set_marked_for_display (true);
1721                 i->second->track->canvas_display()->show();
1722                 i->second->track->get_state_node()->add_property ("shown", X_("yes"));
1723                 i->second->menu_item->set_active(true);
1724         }
1725
1726
1727         /* Show processor automation */
1728
1729         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1730                 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1731                         if ((*ii)->view == 0) {
1732                                 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1733                         } 
1734
1735                         (*ii)->menu_item->set_active (true);
1736                 }
1737         }
1738
1739         no_redraw = false;
1740
1741         /* Redraw */
1742
1743          _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
1744 }
1745
1746 void
1747 RouteTimeAxisView::show_existing_automation ()
1748 {
1749         no_redraw = true;
1750         
1751         /* Show our automation */
1752
1753         map<Evoral::Parameter, RouteAutomationNode*>::iterator i;
1754         for (i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1755                 if (i->second->track->line() && i->second->track->line()->npoints() > 0) {
1756                         i->second->track->set_marked_for_display (true);
1757                         i->second->track->canvas_display()->show();
1758                         i->second->track->get_state_node()->add_property ("shown", X_("yes"));
1759                         i->second->menu_item->set_active(true);
1760                 }
1761         }
1762
1763
1764         /* Show processor automation */
1765
1766         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1767                 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1768                         if ((*ii)->view != 0 && (*i)->processor->data().control((*ii)->what)->list()->size() > 0) {
1769                                 (*ii)->menu_item->set_active (true);
1770                         }
1771                 }
1772         }
1773
1774         no_redraw = false;
1775         
1776         _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
1777 }
1778
1779 void
1780 RouteTimeAxisView::hide_all_automation ()
1781 {
1782         no_redraw = true;
1783
1784         /* Hide our automation */
1785
1786         for (map<Evoral::Parameter, RouteAutomationNode*>::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1787                 i->second->track->set_marked_for_display (false);
1788                 i->second->track->hide ();
1789                 i->second->track->get_state_node()->add_property ("shown", X_("no"));
1790                 i->second->menu_item->set_active (false);
1791         }
1792
1793         /* Hide processor automation */
1794
1795         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1796                 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1797                         (*ii)->menu_item->set_active (false);
1798                 }
1799         }
1800
1801         _show_automation.clear();
1802
1803         no_redraw = false;
1804          _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
1805 }
1806
1807
1808 void
1809 RouteTimeAxisView::region_view_added (RegionView* rv)
1810 {
1811         /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1812         if(is_audio_track()) {
1813                 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1814                         boost::shared_ptr<AutomationTimeAxisView> atv;
1815                         
1816                         if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1817                                 atv->add_ghost(rv);
1818                         }
1819                 }
1820         }
1821
1822         for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1823                 (*i)->add_ghost(rv);
1824         }
1825 }
1826
1827 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1828 {
1829         for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1830                 delete *i;
1831         }
1832 }
1833
1834
1835 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1836 {
1837         parent.remove_processor_automation_node (this);
1838 }
1839
1840 void
1841 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1842 {
1843         if (pan->view) {
1844                 remove_child (pan->view);
1845         }
1846 }
1847
1848 RouteTimeAxisView::ProcessorAutomationNode*
1849 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1850 {
1851         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1852
1853                 if ((*i)->processor == processor) {
1854
1855                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1856                                 if ((*ii)->what == what) {
1857                                         return *ii;
1858                                 }
1859                         }
1860                 }
1861         }
1862
1863         return 0;
1864 }
1865
1866 static string 
1867 legalize_for_xml_node (string str)
1868 {
1869         string::size_type pos;
1870         string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:";
1871         string legal;
1872
1873         legal = str;
1874         pos = 0;
1875
1876         while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
1877                 legal.replace (pos, 1, "_");
1878                 pos += 1;
1879         }
1880
1881         return legal;
1882 }
1883
1884
1885 void
1886 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1887 {
1888         string name;
1889         ProcessorAutomationNode* pan;
1890
1891         if ((pan = find_processor_automation_node (processor, what)) == 0) {
1892                 fatal << _("programming error: ")
1893                       << string_compose (X_("processor automation curve for %1:%2 not registered with track!"),
1894                                   processor->name(), what)
1895                       << endmsg;
1896                 /*NOTREACHED*/
1897                 return;
1898         }
1899
1900         if (pan->view) {
1901                 return;
1902         }
1903
1904         name = processor->describe_parameter (what);
1905
1906         /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1907
1908         /* FIXME: ew */
1909
1910         char state_name[256];
1911         snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id());
1912
1913         boost::shared_ptr<AutomationControl> control
1914                         = boost::dynamic_pointer_cast<AutomationControl>(processor->data().control(what, true));
1915
1916         pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1917                         new AutomationTimeAxisView (_session, _route, processor, control,
1918                                 _editor, *this, false, parent_canvas, name, state_name));
1919
1920         pan->view->Hiding.connect (bind (mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1921
1922         if (!pan->view->marked_for_display()) {
1923                 pan->view->hide ();
1924         } else {
1925                 pan->menu_item->set_active (true);
1926         }
1927
1928         add_child (pan->view);
1929
1930         if (_view) {
1931                 _view->foreach_regionview (mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1932         }
1933
1934         processor->mark_automation_visible (what, true);
1935 }
1936
1937 void
1938 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1939 {
1940         if (!_hidden) {
1941                 pan->menu_item->set_active (false);
1942         }
1943
1944         i->mark_automation_visible (pan->what, false);
1945
1946          _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
1947 }
1948
1949 void
1950 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1951 {
1952         boost::shared_ptr<Processor> processor (p.lock ());
1953         if (!processor) {
1954                 return;
1955         }
1956         
1957         set<Evoral::Parameter> s;
1958         boost::shared_ptr<AutomationLine> al;
1959
1960         processor->what_has_visible_data (s);
1961
1962         for (set<Evoral::Parameter>::iterator i = s.begin(); i != s.end(); ++i) {
1963                 
1964                 if ((al = find_processor_automation_curve (processor, *i)) != 0) {
1965                         al->queue_reset ();
1966                 } else {
1967                         add_processor_automation_curve (processor, (*i));
1968                 }
1969         }
1970 }
1971
1972 void
1973 RouteTimeAxisView::add_automation_child(Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1974 {
1975         using namespace Menu_Helpers;
1976
1977         XMLProperty* prop;
1978         XMLNode* node;
1979
1980         add_child (track);
1981
1982         track->Hiding.connect (bind (mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1983
1984         bool hideit = (!show);
1985
1986         if ((node = track->get_state_node()) != 0) {
1987                 if  ((prop = node->property ("shown")) != 0) {
1988                         if (prop->value() == "yes") {
1989                                 hideit = false;
1990                         }
1991                 } 
1992         }
1993
1994         _automation_tracks.insert(std::make_pair(param, new RouteAutomationNode(param, NULL, track)));
1995
1996         if (hideit) {
1997                 track->hide ();
1998         } else {
1999                 _show_automation.insert (param);
2000
2001
2002                 if (!no_redraw) {
2003                         _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
2004                 }
2005         }
2006
2007         build_display_menu();
2008 }
2009
2010
2011 void
2012 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2013 {
2014         boost::shared_ptr<Processor> processor (p.lock ());
2015         if (!processor) {
2016                 return;
2017         }
2018         
2019         using namespace Menu_Helpers;
2020         ProcessorAutomationInfo *rai;
2021         list<ProcessorAutomationInfo*>::iterator x;
2022         
2023         const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2024         std::set<Evoral::Parameter> has_visible_automation;
2025
2026         processor->what_has_visible_data(has_visible_automation);
2027
2028         if (automatable.empty()) {
2029                 return;
2030         }
2031
2032         for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2033                 if ((*x)->processor == processor) {
2034                         break;
2035                 }
2036         }
2037
2038         if (x == processor_automation.end()) {
2039
2040                 rai = new ProcessorAutomationInfo (processor);
2041                 processor_automation.push_back (rai);
2042
2043         } else {
2044
2045                 rai = *x;
2046
2047         }
2048
2049         /* any older menu was deleted at the top of processors_changed()
2050            when we cleared the subplugin menu.
2051         */
2052
2053         rai->menu = manage (new Menu);
2054         MenuList& items = rai->menu->items();
2055         rai->menu->set_name ("ArdourContextMenu");
2056
2057         items.clear ();
2058
2059         for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2060
2061                 ProcessorAutomationNode* pan;
2062                 CheckMenuItem* mitem;
2063                 
2064                 string name = processor->describe_parameter (*i);
2065                 
2066                 items.push_back (CheckMenuElem (name));
2067                 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2068
2069                 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2070                         mitem->set_active(true);
2071                 }
2072
2073                 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2074
2075                         /* new item */
2076                         
2077                         pan = new ProcessorAutomationNode (*i, mitem, *this);
2078                         
2079                         rai->lines.push_back (pan);
2080
2081                 } else {
2082
2083                         pan->menu_item = mitem;
2084
2085                 }
2086
2087                 mitem->signal_toggled().connect (bind (mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2088         }
2089
2090         /* add the menu for this processor, because the subplugin
2091            menu is always cleared at the top of processors_changed().
2092            this is the result of some poor design in gtkmm and/or
2093            GTK+.
2094         */
2095
2096         subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2097         rai->valid = true;
2098 }
2099
2100 void
2101 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2102                                                RouteTimeAxisView::ProcessorAutomationNode* pan)
2103 {
2104         bool showit = pan->menu_item->get_active();
2105         bool redraw = false;
2106
2107         if (pan->view == 0 && showit) {
2108                 add_processor_automation_curve (rai->processor, pan->what);
2109                 redraw = true;
2110         }
2111
2112         if (pan->view && showit != pan->view->marked_for_display()) {
2113
2114                 if (showit) {
2115                         pan->view->set_marked_for_display (true);
2116                         pan->view->canvas_display()->show();
2117                         pan->view->canvas_background()->show();
2118                 } else {
2119                         rai->processor->mark_automation_visible (pan->what, true);
2120                         pan->view->set_marked_for_display (false);
2121                         pan->view->hide ();
2122                 }
2123
2124                 redraw = true;
2125
2126         }
2127
2128         if (redraw && !no_redraw) {
2129
2130                 /* now trigger a redisplay */
2131                 
2132                  _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
2133
2134         }
2135 }
2136
2137 void
2138 RouteTimeAxisView::processors_changed ()
2139 {
2140         using namespace Menu_Helpers;
2141         
2142         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2143                 (*i)->valid = false;
2144         }
2145
2146         subplugin_menu.items().clear ();
2147
2148         _route->foreach_processor (mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
2149         _route->foreach_processor (mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
2150
2151         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2152
2153                 list<ProcessorAutomationInfo*>::iterator tmp;
2154
2155                 tmp = i;
2156                 ++tmp;
2157
2158                 if (!(*i)->valid) {
2159
2160                         delete *i;
2161                         processor_automation.erase (i);
2162
2163                 } 
2164
2165                 i = tmp;
2166         }
2167
2168         /* change in visibility was possible */
2169
2170         _route->gui_changed ("visible_tracks", this);
2171 }
2172
2173 boost::shared_ptr<AutomationLine>
2174 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2175 {
2176         ProcessorAutomationNode* pan;
2177
2178         if ((pan = find_processor_automation_node (processor, what)) != 0) {
2179                 if (pan->view) {
2180                         pan->view->line();
2181                 } 
2182         }
2183
2184         return boost::shared_ptr<AutomationLine>();
2185 }
2186
2187 void
2188 RouteTimeAxisView::reset_processor_automation_curves ()
2189 {
2190         for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2191                 (*i)->reset();
2192         }
2193 }
2194
2195 void
2196 RouteTimeAxisView::update_rec_display ()
2197 {
2198         RouteUI::update_rec_display ();
2199         name_entry.set_sensitive (!_route->record_enabled());
2200 }
2201                 
2202 void
2203 RouteTimeAxisView::set_layer_display (LayerDisplay d)
2204 {
2205         if (_view) {
2206                 _view->set_layer_display (d);
2207         }
2208 }
2209
2210 LayerDisplay
2211 RouteTimeAxisView::layer_display () const
2212 {
2213         if (_view) {
2214                 return _view->layer_display ();
2215         }
2216
2217         /* we don't know, since we don't have a _view, so just return something */
2218         return Overlaid;
2219 }
2220
2221         
2222
2223 boost::shared_ptr<AutomationTimeAxisView>
2224 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2225 {
2226         AutomationTracks::iterator i = _automation_tracks.find(param);
2227         if (i != _automation_tracks.end())
2228                 return i->second->track;
2229         else
2230                 return boost::shared_ptr<AutomationTimeAxisView>();
2231 }
2232
2233 void
2234 RouteTimeAxisView::fast_update ()
2235 {
2236         gm.get_level_meter().update_meters ();
2237 }
2238
2239 void
2240 RouteTimeAxisView::hide_meter ()
2241 {
2242         clear_meter ();
2243         gm.get_level_meter().hide_meters ();
2244 }
2245
2246 void
2247 RouteTimeAxisView::show_meter ()
2248 {
2249         reset_meter ();
2250 }
2251
2252 void
2253 RouteTimeAxisView::reset_meter ()
2254 {
2255         if (Config->get_show_track_meters()) {
2256                 gm.get_level_meter().setup_meters (height-5);
2257         } else {
2258                 hide_meter ();
2259         }
2260 }
2261
2262 void
2263 RouteTimeAxisView::clear_meter ()
2264 {
2265         gm.get_level_meter().clear_meters ();
2266 }
2267
2268 void
2269 RouteTimeAxisView::meter_changed (void *src)
2270 {
2271         ENSURE_GUI_THREAD (bind (mem_fun(*this, &RouteTimeAxisView::meter_changed), src));
2272         reset_meter();
2273 }
2274
2275 void
2276 RouteTimeAxisView::io_changed (IOChange change, void *src)
2277 {
2278         reset_meter ();
2279 }
2280
2281 void
2282 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu) {
2283         using namespace Menu_Helpers;
2284
2285         if(!_underlay_streams.empty()) {
2286                 MenuList& parent_items = parent_menu->items();
2287                 Menu* gs_menu = manage (new Menu);
2288                 gs_menu->set_name ("ArdourContextMenu");
2289                 MenuList& gs_items = gs_menu->items();
2290                 
2291                 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2292                 
2293                 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2294                         gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2295                                                     bind(mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2296                 }
2297         }
2298 }
2299
2300 bool
2301 RouteTimeAxisView::set_underlay_state() 
2302 {
2303         if(!underlay_xml_node) {
2304                 return false;
2305         }
2306
2307         XMLNodeList nlist = underlay_xml_node->children();
2308         XMLNodeConstIterator niter;
2309         XMLNode *child_node;
2310         
2311         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2312                 child_node = *niter;
2313
2314                 if(child_node->name() != "Underlay") {
2315                         continue;
2316                 }
2317
2318                 XMLProperty* prop = child_node->property ("id");
2319                 if (prop) {
2320                         PBD::ID id (prop->value());
2321
2322                         RouteTimeAxisView* v = _editor.get_route_view_by_id (id);
2323
2324                         if (v) {
2325                                 add_underlay(v->view(), false);
2326                         }
2327                 }
2328         }
2329
2330         return false;
2331 }
2332
2333 void
2334 RouteTimeAxisView::add_underlay(StreamView* v, bool update_xml) 
2335 {
2336         if(!v) {
2337                 return;
2338         }
2339
2340         RouteTimeAxisView& other = v->trackview();
2341
2342         if(find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2343                 if(find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2344                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2345                         /*NOTREACHED*/
2346                 }
2347
2348                 _underlay_streams.push_back(v);
2349                 other._underlay_mirrors.push_back(this);
2350
2351                 v->foreach_regionview(mem_fun(*this, &RouteTimeAxisView::add_ghost));
2352
2353                 if(update_xml) {
2354                         if(!underlay_xml_node) {
2355                                 ensure_xml_node();
2356                                 underlay_xml_node = xml_node->add_child("Underlays");
2357                         }
2358
2359                         XMLNode* node = underlay_xml_node->add_child("Underlay");
2360                         XMLProperty* prop = node->add_property("id");
2361                         prop->set_value(v->trackview().route()->id().to_s());
2362                 }
2363         }
2364 }
2365
2366 void
2367 RouteTimeAxisView::remove_underlay(StreamView* v) 
2368 {
2369         if(!v) {
2370                 return;
2371         }
2372
2373         UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2374         RouteTimeAxisView& other = v->trackview();
2375
2376         if(it != _underlay_streams.end()) {
2377                 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2378
2379                 if(gm == other._underlay_mirrors.end()) {
2380                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2381                         /*NOTREACHED*/
2382                 }
2383
2384                 v->foreach_regionview(mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2385
2386                 _underlay_streams.erase(it);
2387                 other._underlay_mirrors.erase(gm);
2388
2389                 if(underlay_xml_node) {
2390                         underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2391                 }
2392         }
2393 }
2394