gtk style related fixes:
[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 <map>
27 #include <utility>
28
29 #include <sigc++/bind.h>
30
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
37
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
44
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
53
54 #include "evoral/Parameter.hpp"
55
56 #include "canvas/debug.h"
57
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
60 #include "debug.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.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 "streamview.h"
75 #include "utils.h"
76 #include "route_group_menu.h"
77
78 #include "ardour/track.h"
79
80 #include "i18n.h"
81
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
84 using namespace PBD;
85 using namespace Gtkmm2ext;
86 using namespace Gtk;
87 using namespace Editing;
88 using namespace std;
89 using std::list;
90
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
92         : AxisView(sess)
93         , RouteUI(sess)
94         , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
95         , _view (0)
96         , parent_canvas (canvas)
97         , no_redraw (false)
98         , button_table (3, 3)
99         , route_group_button (_("G"))
100         , playlist_button (_("P"))
101         , automation_button (_("A"))
102         , automation_action_menu (0)
103         , plugins_submenu_item (0)
104         , route_group_menu (0)
105         , playlist_action_menu (0)
106         , mode_menu (0)
107         , color_mode_menu (0)
108         , gm (sess, true, 75, 14)
109         , _ignore_set_layer_display (false)
110 {
111         number_label.set_name("tracknumber label");
112         number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
113         number_label.set_alignment(.5, .5);
114         number_label.set_fallthrough_to_parent (true);
115
116         sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
117 }
118
119 void
120 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
121 {
122         RouteUI::set_route (rt);
123
124         CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
125         CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
126         CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
127
128         int meter_width = 3;
129         if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
130                 meter_width = 6;
131         }
132         gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
133         gm.get_level_meter().set_no_show_all();
134         gm.get_level_meter().setup_meters(50, meter_width);
135         gm.update_gain_sensitive ();
136
137         string str = gui_property ("height");
138         if (!str.empty()) {
139                 set_height (atoi (str));
140         } else {
141                 set_height (preset_height (HeightNormal));
142         }
143
144         if (!_route->is_auditioner()) {
145                 if (gui_property ("visible").empty()) {
146                         set_gui_property ("visible", true);
147                 }
148         } else {
149                 set_gui_property ("visible", false);
150         }
151
152         mute_changed (0);
153         update_solo_display ();
154
155         timestretch_rect = 0;
156         no_redraw = false;
157
158         ignore_toggle = false;
159
160         route_group_button.set_name ("route button");
161         playlist_button.set_name ("route button");
162         automation_button.set_name ("route button");
163
164         route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
165         playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
166         automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
167
168         if (is_track()) {
169
170                 if (ARDOUR::Profile->get_mixbus()) {
171                         controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
172                 } else {
173                         controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
174                 }
175                 controls_button_size_group->add_widget(*rec_enable_button);
176
177                 if (is_midi_track()) {
178                         ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
179                         gm.set_fader_name ("MidiTrackFader");
180                 } else {
181                         ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
182                         gm.set_fader_name ("AudioTrackFader");
183                 }
184
185                 rec_enable_button->set_sensitive (_session->writable());
186                 
187                 /* set playlist button tip to the current playlist, and make it update when it changes */
188                 update_playlist_tip ();
189                 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
190
191         } else {
192                 gm.set_fader_name ("AudioBusFader");
193                 Gtk::Fixed *blank = manage(new Gtk::Fixed());
194                 controls_button_size_group->add_widget(*blank);
195                 if (ARDOUR::Profile->get_mixbus() ) {
196                         controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
197                 } else {
198                         controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
199                 }
200                 blank->show();
201         }
202
203         top_hbox.pack_end(gm.get_level_meter(), false, false, 4);
204
205         if (!ARDOUR::Profile->get_mixbus()) {
206                 controls_meters_size_group->add_widget (gm.get_level_meter());
207         }
208
209         _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
210         _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
211         _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
212         _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
213
214         if (ARDOUR::Profile->get_mixbus()) {
215                 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
216         } else {
217                 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
218         }
219         controls_button_size_group->add_widget(*mute_button);
220
221         if (!_route->is_master()) {
222                 if (ARDOUR::Profile->get_mixbus()) {
223                         controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
224                 } else {
225                         controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
226                 }
227                 controls_button_size_group->add_widget(*solo_button);
228         } else {
229                 Gtk::Fixed *blank = manage(new Gtk::Fixed());
230                 controls_button_size_group->add_widget(*blank);
231                 if (ARDOUR::Profile->get_mixbus()) {
232                         controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233                 } else {
234                         controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
235                 }
236                 blank->show();
237         }
238
239         if (ARDOUR::Profile->get_mixbus()) {
240                 controls_button_size_group->add_widget(route_group_button);
241                 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
242                 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
243         }
244         else if (!ARDOUR::Profile->get_trx()) {
245                 controls_button_size_group->add_widget(route_group_button);
246                 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
247                 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
248         }
249         
250         ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
251         ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
252         ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
253
254         mute_button->set_tweaks(ArdourButton::Square);
255         solo_button->set_tweaks(ArdourButton::Square);
256         rec_enable_button->set_tweaks(ArdourButton::Square);
257         playlist_button.set_tweaks(ArdourButton::Square);
258         automation_button.set_tweaks(ArdourButton::Square);
259         route_group_button.set_tweaks(ArdourButton::Square);
260
261         if (is_midi_track()) {
262                 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
263         } else {
264                 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
265         }
266
267         update_track_number_visibility();
268         label_view ();
269
270         if (ARDOUR::Profile->get_mixbus()) {
271                 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
272                 controls_button_size_group->add_widget(automation_button);
273         }
274         else if (!ARDOUR::Profile->get_trx()) {
275                 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
276                 controls_button_size_group->add_widget(automation_button);
277         }
278
279         if (is_track() && track()->mode() == ARDOUR::Normal) {
280                 if (ARDOUR::Profile->get_mixbus()) {
281                         controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
282                         controls_button_size_group->add_widget(playlist_button);
283                 }
284                 else if (!ARDOUR::Profile->get_trx()) {
285                         controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286                         controls_button_size_group->add_widget(playlist_button);
287                 }
288         }
289
290         _y_position = -1;
291
292         _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
293         _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
294
295         if (is_track()) {
296
297                 str = gui_property ("layer-display");
298                 if (!str.empty()) {
299                         set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
300                 }
301
302                 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
303                 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
304
305                 /* pick up the correct freeze state */
306                 map_frozen ();
307
308         } 
309
310         _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
311         ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
312
313         PropertyList* plist = new PropertyList();
314
315         plist->add (ARDOUR::Properties::mute, true);
316         plist->add (ARDOUR::Properties::solo, true);
317
318         route_group_menu = new RouteGroupMenu (_session, plist);
319
320         gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
321 }
322
323 RouteTimeAxisView::~RouteTimeAxisView ()
324 {
325         CatchDeletion (this);
326
327         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
328                 delete *i;
329         }
330
331         delete playlist_action_menu;
332         playlist_action_menu = 0;
333
334         delete _view;
335         _view = 0;
336
337         _automation_tracks.clear ();
338
339         delete route_group_menu;
340 }
341
342 void
343 RouteTimeAxisView::post_construct ()
344 {
345         /* map current state of the route */
346
347         update_diskstream_display ();
348         setup_processor_menu_and_curves ();
349         reset_processor_automation_curves ();
350 }
351
352 /** Set up the processor menu for the current set of processors, and
353  *  display automation curves for any parameters which have data.
354  */
355 void
356 RouteTimeAxisView::setup_processor_menu_and_curves ()
357 {
358         _subplugin_menu_map.clear ();
359         subplugin_menu.items().clear ();
360         _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
361         _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
362 }
363
364 gint
365 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
366 {
367         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
368                 if (_route->route_group()) {
369                         _route->route_group()->remove (_route);
370                 }
371                 return false;
372         }
373
374         WeakRouteList r;
375         r.push_back (route ());
376
377         route_group_menu->build (r);
378         route_group_menu->menu()->popup (ev->button, ev->time);
379
380         return false;
381 }
382
383 void
384 RouteTimeAxisView::playlist_changed ()
385 {
386         label_view ();
387 }
388
389 void
390 RouteTimeAxisView::label_view ()
391 {
392         string x = _route->name ();
393         if (x != name_label.get_text ()) {
394                 name_label.set_text (x);
395         }
396         const int64_t track_number = _route->track_number ();
397         if (track_number == 0) {
398                 number_label.set_text ("");
399         } else {
400                 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
401         }
402 }
403
404 void
405 RouteTimeAxisView::update_track_number_visibility ()
406 {
407         DisplaySuspender ds;
408         bool show_label = _session->config.get_track_name_number();
409
410         if (_route && _route->is_master()) {
411                 show_label = false;
412         }
413
414         if (number_label.get_parent()) {
415                 controls_table.remove (number_label);
416         }
417         if (show_label) {
418                 if (ARDOUR::Profile->get_mixbus()) {
419                         controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
420                 } else {
421                         controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
422                 }
423                 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
424                 // except the width of the number label is subtracted from the name-hbox, so we
425                 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
426                 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
427                 if (tnw & 1) --tnw;
428                 number_label.set_size_request(tnw, -1);
429                 number_label.show ();
430                 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
431         } else {
432                 number_label.hide ();
433                 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
434         }
435 }
436
437 void
438 RouteTimeAxisView::parameter_changed (string const & p)
439 {
440         if (p == "track-name-number") {
441                 update_track_number_visibility();
442         }
443 }
444
445 void
446 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
447 {
448         if (what_changed.contains (ARDOUR::Properties::name)) {
449                 label_view ();
450         }
451 }
452
453 void
454 RouteTimeAxisView::take_name_changed (void *src)
455 {
456         if (src != this) {
457                 label_view ();
458         }
459 }
460
461 void
462 RouteTimeAxisView::playlist_click ()
463 {
464         build_playlist_menu ();
465         conditionally_add_to_selection ();
466         playlist_action_menu->popup (1, gtk_get_current_event_time());
467 }
468
469 void
470 RouteTimeAxisView::automation_click ()
471 {
472         conditionally_add_to_selection ();
473         build_automation_action_menu (false);
474         automation_action_menu->popup (1, gtk_get_current_event_time());
475 }
476
477 void
478 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
479 {
480         using namespace Menu_Helpers;
481
482         /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
483            otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
484         */
485
486         detach_menu (subplugin_menu);
487
488         _main_automation_menu_map.clear ();
489         delete automation_action_menu;
490         automation_action_menu = new Menu;
491
492         MenuList& items = automation_action_menu->items();
493
494         automation_action_menu->set_name ("ArdourContextMenu");
495
496         items.push_back (MenuElem (_("Show All Automation"),
497                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
498
499         items.push_back (MenuElem (_("Show Existing Automation"),
500                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
501
502         items.push_back (MenuElem (_("Hide All Automation"),
503                                    sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
504
505         /* Attach the plugin submenu. It may have previously been used elsewhere,
506            so it was detached above 
507         */
508         
509         if (!subplugin_menu.items().empty()) {
510                 items.push_back (SeparatorElem ());
511                 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
512                 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
513         }
514 }
515
516 void
517 RouteTimeAxisView::build_display_menu ()
518 {
519         using namespace Menu_Helpers;
520
521         /* prepare it */
522
523         TimeAxisView::build_display_menu ();
524
525         /* now fill it with our stuff */
526
527         MenuList& items = display_menu->items();
528         display_menu->set_name ("ArdourContextMenu");
529
530         items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
531
532         items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
533
534         items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
535
536         items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
537
538         items.push_back (SeparatorElem());
539
540         if (_size_menu) {
541                 detach_menu (*_size_menu);
542         }
543         build_size_menu ();
544         items.push_back (MenuElem (_("Height"), *_size_menu));
545
546         items.push_back (SeparatorElem());
547
548         if (!Profile->get_sae()) {
549                 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
550                 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
551                 items.push_back (SeparatorElem());
552         }
553
554         // Hook for derived classes to add type specific stuff
555         append_extra_display_menu_items ();
556
557         if (is_track()) {
558
559                 Menu* layers_menu = manage (new Menu);
560                 MenuList &layers_items = layers_menu->items();
561                 layers_menu->set_name("ArdourContextMenu");
562
563                 RadioMenuItem::Group layers_group;
564
565                 /* Find out how many overlaid/stacked tracks we have in the selection */
566
567                 int overlaid = 0;
568                 int stacked = 0;
569                 TrackSelection const & s = _editor.get_selection().tracks;
570                 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
571                         StreamView* v = (*i)->view ();
572                         if (!v) {
573                                 continue;
574                         }
575
576                         switch (v->layer_display ()) {
577                         case Overlaid:
578                                 ++overlaid;
579                                 break;
580                         case Stacked:
581                         case Expanded:
582                                 ++stacked;
583                                 break;
584                         }
585                 }
586
587                 /* We're not connecting to signal_toggled() here; in the case where these two items are
588                    set to be in the `inconsistent' state, it seems that one or other will end up active
589                    as well as inconsistent (presumably due to the RadioMenuItem::Group).  Then when you
590                    select the active one, no toggled signal is emitted so nothing happens.
591                 */
592
593                 _ignore_set_layer_display = true;
594                 
595                 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
596                 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
597                 i->set_active (overlaid != 0 && stacked == 0);
598                 i->set_inconsistent (overlaid != 0 && stacked != 0);
599                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
600
601                 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
602                 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
603                 i->set_active (overlaid == 0 && stacked != 0);
604                 i->set_inconsistent (overlaid != 0 && stacked != 0);
605                 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
606                 
607                 _ignore_set_layer_display = false;
608
609                 items.push_back (MenuElem (_("Layers"), *layers_menu));
610
611                 if (!Profile->get_sae()) {
612
613                         Menu* alignment_menu = manage (new Menu);
614                         MenuList& alignment_items = alignment_menu->items();
615                         alignment_menu->set_name ("ArdourContextMenu");
616
617                         RadioMenuItem::Group align_group;
618
619                         /* Same verbose hacks as for the layering options above */
620
621                         int existing = 0;
622                         int capture = 0;
623                         int automatic = 0;
624                         int styles = 0;
625                         boost::shared_ptr<Track> first_track;
626
627                         TrackSelection const & s = _editor.get_selection().tracks;
628                         for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
629                                 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
630                                 if (!r || !r->is_track ()) {
631                                         continue;
632                                 }
633
634                                 if (!first_track) {
635                                         first_track = r->track();
636                                 }
637
638                                 switch (r->track()->alignment_choice()) {
639                                 case Automatic:
640                                         ++automatic;
641                                         styles |= 0x1;
642                                         switch (r->track()->alignment_style()) {
643                                         case ExistingMaterial:
644                                                 ++existing;
645                                                 break;
646                                         case CaptureTime:
647                                                 ++capture;
648                                                 break;
649                                         }
650                                         break;
651                                 case UseExistingMaterial:
652                                         ++existing;
653                                         styles |= 0x2;
654                                         break;
655                                 case UseCaptureTime:
656                                         ++capture;
657                                         styles |= 0x4;
658                                         break;
659                                 }
660                         }
661
662                         bool inconsistent;
663                         switch (styles) {
664                         case 1:
665                         case 2:
666                         case 4:
667                                 inconsistent = false;
668                                 break;
669                         default:
670                                 inconsistent = true;
671                                 break;
672                         }
673
674                         RadioMenuItem* i;
675
676                         if (!inconsistent && first_track) {
677
678                                 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
679                                 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
680                                 i->set_active (automatic != 0 && existing == 0 && capture == 0);
681                                 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
682
683                                 switch (first_track->alignment_choice()) {
684                                 case Automatic:
685                                         switch (first_track->alignment_style()) {
686                                         case ExistingMaterial:
687                                                 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
688                                                 break;
689                                         case CaptureTime:
690                                                 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
691                                                 break;
692                                         }
693                                         break;
694                                 default:
695                                         break;
696                                 }
697
698                                 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
699                                 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
700                                 i->set_active (existing != 0 && capture == 0 && automatic == 0);
701                                 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
702
703                                 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
704                                 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
705                                 i->set_active (existing == 0 && capture != 0 && automatic == 0);
706                                 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
707
708                                 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
709
710                         } else {
711                                 /* show nothing */
712                         }
713
714                         Menu* mode_menu = manage (new Menu);
715                         MenuList& mode_items = mode_menu->items ();
716                         mode_menu->set_name ("ArdourContextMenu");
717
718                         RadioMenuItem::Group mode_group;
719
720                         int normal = 0;
721                         int tape = 0;
722                         int non_layered = 0;
723
724                         for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
725                                 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
726                                 if (!r || !r->is_track ()) {
727                                         continue;
728                                 }
729
730                                 switch (r->track()->mode()) {
731                                 case Normal:
732                                         ++normal;
733                                         break;
734                                 case Destructive:
735                                         ++tape;
736                                         break;
737                                 case NonLayered:
738                                         ++non_layered;
739                                         break;
740                                 }
741                         }
742
743                         mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
744                         i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
745                         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
746                         i->set_active (normal != 0 && tape == 0 && non_layered == 0);
747                         i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
748
749                         mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
750                         i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
751                         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
752                         i->set_active (normal == 0 && tape != 0 && non_layered == 0);
753                         i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
754
755                         mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
756                         i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
757                         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
758                         i->set_active (normal == 0 && tape == 0 && non_layered != 0);
759                         i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
760
761                         items.push_back (MenuElem (_("Mode"), *mode_menu));
762                 }
763
764
765                 items.push_back (SeparatorElem());
766
767                 build_playlist_menu ();
768                 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
769                 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
770         }
771
772         route_group_menu->detach ();
773         
774         WeakRouteList r;
775         for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
776                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
777                 if (rtv) {
778                         r.push_back (rtv->route ());
779                 }
780         }
781         
782         if (r.empty ()) {
783                 r.push_back (route ());
784         }
785
786         route_group_menu->build (r);
787         items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
788         
789         build_automation_action_menu (true);
790         items.push_back (MenuElem (_("Automation"), *automation_action_menu));
791         
792         items.push_back (SeparatorElem());
793         
794         int active = 0;
795         int inactive = 0;
796         TrackSelection const & s = _editor.get_selection().tracks;
797         for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
798                 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
799                 if (!r) {
800                         continue;
801                 }
802
803                 if (r->route()->active()) {
804                         ++active;
805                 } else {
806                         ++inactive;
807                 }
808         }
809
810         items.push_back (CheckMenuElem (_("Active")));
811         Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
812         bool click_sets_active = true;
813         if (active > 0 && inactive == 0) {
814                 i->set_active (true);
815                 click_sets_active = false;
816         } else if (active > 0 && inactive > 0) {
817                 i->set_inconsistent (true);
818         }
819         i->set_sensitive(! _session->transport_rolling());
820         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
821
822         items.push_back (SeparatorElem());
823         items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
824         if (!Profile->get_sae()) {
825                 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
826         } else {
827                 items.push_front (SeparatorElem());
828                 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
829         }
830 }
831
832 void
833 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
834 {
835         if (apply_to_selection) {
836                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
837         } else {
838
839                 bool needs_bounce = false;
840
841                 if (!track()->can_use_mode (mode, needs_bounce)) {
842
843                         if (!needs_bounce) {
844                                 /* cannot be done */
845                                 return;
846                         } else {
847                                 cerr << "would bounce this one\n";
848                                 return;
849                         }
850                 }
851
852                 track()->set_mode (mode);
853         }
854 }
855
856 void
857 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
858 {
859         TimeAxisView::show_timestretch (start, end, layers, layer);
860
861         hide_timestretch ();
862
863 #if 0
864         if (ts.empty()) {
865                 return;
866         }
867
868
869         /* check that the time selection was made in our route, or our route group.
870            remember that route_group() == 0 implies the route is *not* in a edit group.
871         */
872
873         if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
874                 /* this doesn't apply to us */
875                 return;
876         }
877
878         /* ignore it if our edit group is not active */
879
880         if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
881                 return;
882         }
883 #endif
884
885         if (timestretch_rect == 0) {
886                 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
887                 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
888                 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
889         }
890
891         timestretch_rect->show ();
892         timestretch_rect->raise_to_top ();
893
894         double const x1 = start / _editor.get_current_zoom();
895         double const x2 = (end - 1) / _editor.get_current_zoom();
896
897         timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
898                                                    x2, current_height() * (layers - layer) / layers));
899 }
900
901 void
902 RouteTimeAxisView::hide_timestretch ()
903 {
904         TimeAxisView::hide_timestretch ();
905
906         if (timestretch_rect) {
907                 timestretch_rect->hide ();
908         }
909 }
910
911 void
912 RouteTimeAxisView::show_selection (TimeSelection& ts)
913 {
914
915 #if 0
916         /* ignore it if our edit group is not active or if the selection was started
917            in some other track or route group (remember that route_group() == 0 means
918            that the track is not in an route group).
919         */
920
921         if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
922             (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
923                 hide_selection ();
924                 return;
925         }
926 #endif
927
928         TimeAxisView::show_selection (ts);
929 }
930
931 void
932 RouteTimeAxisView::set_height (uint32_t h)
933 {
934         int gmlen = h - 9;
935         bool height_changed = (height == 0) || (h != height);
936
937         int meter_width = 3;
938         if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
939                 meter_width = 6;
940         }
941         gm.get_level_meter().setup_meters (gmlen, meter_width);
942
943         TimeAxisView::set_height (h);
944
945         if (_view) {
946                 _view->set_height ((double) current_height());
947         }
948
949         if (height >= preset_height (HeightNormal)) {
950
951                 reset_meter();
952
953                 gm.get_gain_slider().show();
954                 mute_button->show();
955                 if (!_route || _route->is_monitor()) {
956                         solo_button->hide();
957                 } else {
958                         solo_button->show();
959                 }
960                 if (rec_enable_button)
961                         rec_enable_button->show();
962
963                 route_group_button.show();
964                 automation_button.show();
965
966                 if (is_track() && track()->mode() == ARDOUR::Normal) {
967                         playlist_button.show();
968                 }
969
970         } else {
971
972                 reset_meter();
973
974                 gm.get_gain_slider().hide();
975                 mute_button->show();
976                 if (!_route || _route->is_monitor()) {
977                         solo_button->hide();
978                 } else {
979                         solo_button->show();
980                 }
981                 if (rec_enable_button)
982                         rec_enable_button->show();
983
984                 route_group_button.hide ();
985                 automation_button.hide ();
986
987                 if (is_track() && track()->mode() == ARDOUR::Normal) {
988                         playlist_button.hide ();
989                 }
990
991         }
992
993         if (height_changed && !no_redraw) {
994                 /* only emit the signal if the height really changed */
995                 request_redraw ();
996         }
997 }
998
999 void
1000 RouteTimeAxisView::route_color_changed ()
1001 {
1002         if (_view) {
1003                 _view->apply_color (color(), StreamView::RegionColor);
1004         }
1005
1006         number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1007 }
1008
1009 void
1010 RouteTimeAxisView::reset_samples_per_pixel ()
1011 {
1012         set_samples_per_pixel (_editor.get_current_zoom());
1013 }
1014
1015 void
1016 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1017 {
1018         double speed = 1.0;
1019
1020         if (track()) {
1021                 speed = track()->speed();
1022         }
1023
1024         if (_view) {
1025                 _view->set_samples_per_pixel (fpp * speed);
1026         }
1027
1028         TimeAxisView::set_samples_per_pixel (fpp * speed);
1029 }
1030
1031 void
1032 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1033 {
1034         if (!mitem->get_active()) {
1035                 /* this is one of the two calls made when these radio menu items change status. this one
1036                    is for the item that became inactive, and we want to ignore it.
1037                 */
1038                 return;
1039         }
1040
1041         if (apply_to_selection) {
1042                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1043         } else {
1044                 if (track ()) {
1045                         track()->set_align_choice (choice);
1046                 }
1047         }
1048 }
1049
1050 void
1051 RouteTimeAxisView::rename_current_playlist ()
1052 {
1053         ArdourPrompter prompter (true);
1054         string name;
1055
1056         boost::shared_ptr<Track> tr = track();
1057         if (!tr || tr->destructive()) {
1058                 return;
1059         }
1060
1061         boost::shared_ptr<Playlist> pl = tr->playlist();
1062         if (!pl) {
1063                 return;
1064         }
1065
1066         prompter.set_title (_("Rename Playlist"));
1067         prompter.set_prompt (_("New name for playlist:"));
1068         prompter.set_initial_text (pl->name());
1069         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1070         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1071
1072         switch (prompter.run ()) {
1073         case Gtk::RESPONSE_ACCEPT:
1074                 prompter.get_result (name);
1075                 if (name.length()) {
1076                         pl->set_name (name);
1077                 }
1078                 break;
1079
1080         default:
1081                 break;
1082         }
1083 }
1084
1085 std::string
1086 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1087 {
1088         std::string ret (basename);
1089
1090         std::string const group_string = "." + route_group()->name() + ".";
1091
1092         // iterate through all playlists
1093         int maxnumber = 0;
1094         for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1095                 std::string tmp = (*i)->name();
1096
1097                 std::string::size_type idx = tmp.find(group_string);
1098                 // find those which belong to this group
1099                 if (idx != string::npos) {
1100                         tmp = tmp.substr(idx + group_string.length());
1101
1102                         // and find the largest current number
1103                         int x = atoi(tmp);
1104                         if (x > maxnumber) {
1105                                 maxnumber = x;
1106                         }
1107                 }
1108         }
1109
1110         maxnumber++;
1111
1112         char buf[32];
1113         snprintf (buf, sizeof(buf), "%d", maxnumber);
1114
1115         ret = this->name() + "." + route_group()->name () + "." + buf;
1116
1117         return ret;
1118 }
1119
1120 void
1121 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1122 {
1123         string name;
1124
1125         boost::shared_ptr<Track> tr = track ();
1126         if (!tr || tr->destructive()) {
1127                 return;
1128         }
1129
1130         boost::shared_ptr<const Playlist> pl = tr->playlist();
1131         if (!pl) {
1132                 return;
1133         }
1134
1135         name = pl->name();
1136
1137         if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1138                 name = resolve_new_group_playlist_name(name, playlists_before_op);
1139         }
1140
1141         while (_session->playlists->by_name(name)) {
1142                 name = Playlist::bump_name (name, *_session);
1143         }
1144
1145         // TODO: The prompter "new" button should be de-activated if the user
1146         // specifies a playlist name which already exists in the session.
1147
1148         if (prompt) {
1149
1150                 ArdourPrompter prompter (true);
1151
1152                 prompter.set_title (_("New Copy Playlist"));
1153                 prompter.set_prompt (_("Name for new playlist:"));
1154                 prompter.set_initial_text (name);
1155                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1156                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1157                 prompter.show_all ();
1158
1159                 switch (prompter.run ()) {
1160                 case Gtk::RESPONSE_ACCEPT:
1161                         prompter.get_result (name);
1162                         break;
1163
1164                 default:
1165                         return;
1166                 }
1167         }
1168
1169         if (name.length()) {
1170                 tr->use_copy_playlist ();
1171                 tr->playlist()->set_name (name);
1172         }
1173 }
1174
1175 void
1176 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1177 {
1178         string name;
1179
1180         boost::shared_ptr<Track> tr = track ();
1181         if (!tr || tr->destructive()) {
1182                 return;
1183         }
1184
1185         boost::shared_ptr<const Playlist> pl = tr->playlist();
1186         if (!pl) {
1187                 return;
1188         }
1189
1190         name = pl->name();
1191
1192         if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1193                 name = resolve_new_group_playlist_name(name,playlists_before_op);
1194         }
1195
1196         while (_session->playlists->by_name(name)) {
1197                 name = Playlist::bump_name (name, *_session);
1198         }
1199
1200
1201         if (prompt) {
1202
1203                 ArdourPrompter prompter (true);
1204
1205                 prompter.set_title (_("New Playlist"));
1206                 prompter.set_prompt (_("Name for new playlist:"));
1207                 prompter.set_initial_text (name);
1208                 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1209                 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1210
1211                 switch (prompter.run ()) {
1212                 case Gtk::RESPONSE_ACCEPT:
1213                         prompter.get_result (name);
1214                         break;
1215
1216                 default:
1217                         return;
1218                 }
1219         }
1220
1221         if (name.length()) {
1222                 tr->use_new_playlist ();
1223                 tr->playlist()->set_name (name);
1224         }
1225 }
1226
1227 void
1228 RouteTimeAxisView::clear_playlist ()
1229 {
1230         boost::shared_ptr<Track> tr = track ();
1231         if (!tr || tr->destructive()) {
1232                 return;
1233         }
1234
1235         boost::shared_ptr<Playlist> pl = tr->playlist();
1236         if (!pl) {
1237                 return;
1238         }
1239
1240         _editor.clear_playlist (pl);
1241 }
1242
1243 void
1244 RouteTimeAxisView::speed_changed ()
1245 {
1246         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1247 }
1248
1249 void
1250 RouteTimeAxisView::update_diskstream_display ()
1251 {
1252         if (!track()) {
1253                 return;
1254         }
1255
1256         map_frozen ();
1257 }
1258
1259 void
1260 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1261 {
1262         if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1263
1264                 /* special case: select/deselect all tracks */
1265                 if (_editor.get_selection().selected (this)) {
1266                         _editor.get_selection().clear_tracks ();
1267                 } else {
1268                         _editor.select_all_tracks ();
1269                 }
1270
1271                 return;
1272         }
1273
1274         switch (ArdourKeyboard::selection_type (ev->state)) {
1275         case Selection::Toggle:
1276                 _editor.get_selection().toggle (this);
1277                 break;
1278
1279         case Selection::Set:
1280                 _editor.get_selection().set (this);
1281                 break;
1282
1283         case Selection::Extend:
1284                 _editor.extend_selection_to_track (*this);
1285                 break;
1286
1287         case Selection::Add:
1288                 _editor.get_selection().add (this);
1289                 break;
1290         }
1291 }
1292
1293 void
1294 RouteTimeAxisView::set_selected_points (PointSelection& points)
1295 {
1296         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1297                 (*i)->set_selected_points (points);
1298         }
1299 }
1300
1301 void
1302 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1303 {
1304         if (_view) {
1305                 _view->set_selected_regionviews (regions);
1306         }
1307 }
1308
1309 /** Add the selectable things that we have to a list.
1310  * @param results List to add things to.
1311  */
1312 void
1313 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1314 {
1315         double speed = 1.0;
1316
1317         if (track() != 0) {
1318                 speed = track()->speed();
1319         }
1320
1321         framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1322         framepos_t const end_adjusted   = session_frame_to_track_frame(end, speed);
1323
1324         if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1325                 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1326         }
1327
1328         /* pick up visible automation tracks */
1329
1330         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1331                 if (!(*i)->hidden()) {
1332                         (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1333                 }
1334         }
1335 }
1336
1337 void
1338 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1339 {
1340         if (_view) {
1341                 _view->get_inverted_selectables (sel, results);
1342         }
1343
1344         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1345                 if (!(*i)->hidden()) {
1346                         (*i)->get_inverted_selectables (sel, results);
1347                 }
1348         }
1349
1350         return;
1351 }
1352
1353 RouteGroup*
1354 RouteTimeAxisView::route_group () const
1355 {
1356         return _route->route_group();
1357 }
1358
1359 string
1360 RouteTimeAxisView::name() const
1361 {
1362         return _route->name();
1363 }
1364
1365 boost::shared_ptr<Playlist>
1366 RouteTimeAxisView::playlist () const
1367 {
1368         boost::shared_ptr<Track> tr;
1369
1370         if ((tr = track()) != 0) {
1371                 return tr->playlist();
1372         } else {
1373                 return boost::shared_ptr<Playlist> ();
1374         }
1375 }
1376
1377 void
1378 RouteTimeAxisView::name_entry_changed ()
1379 {
1380         TimeAxisView::name_entry_changed ();
1381
1382         string x = name_entry->get_text ();
1383
1384         if (x == _route->name()) {
1385                 return;
1386         }
1387
1388         strip_whitespace_edges (x);
1389
1390         if (x.length() == 0) {
1391                 name_entry->set_text (_route->name());
1392                 return;
1393         }
1394
1395         if (_session->route_name_internal (x)) {
1396                 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1397                                                                     PROGRAM_NAME));
1398                 name_entry->grab_focus ();
1399         } else if (RouteUI::verify_new_route_name (x)) {
1400                 _route->set_name (x);
1401         } else {
1402                 name_entry->grab_focus ();
1403         }
1404 }
1405
1406 boost::shared_ptr<Region>
1407 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1408 {
1409         boost::shared_ptr<Playlist> pl = playlist ();
1410
1411         if (pl) {
1412                 return pl->find_next_region (pos, point, dir);
1413         }
1414
1415         return boost::shared_ptr<Region> ();
1416 }
1417
1418 framepos_t
1419 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1420 {
1421         boost::shared_ptr<Playlist> pl = playlist ();
1422
1423         if (pl) {
1424                 return pl->find_next_region_boundary (pos, dir);
1425         }
1426
1427         return -1;
1428 }
1429
1430 void
1431 RouteTimeAxisView::fade_range (TimeSelection& selection)
1432 {
1433         boost::shared_ptr<Playlist> what_we_got;
1434         boost::shared_ptr<Track> tr = track ();
1435         boost::shared_ptr<Playlist> playlist;
1436
1437         if (tr == 0) {
1438                 /* route is a bus, not a track */
1439                 return;
1440         }
1441
1442         playlist = tr->playlist();
1443
1444         TimeSelection time (selection);
1445         float const speed = tr->speed();
1446         if (speed != 1.0f) {
1447                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1448                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1449                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1450                 }
1451         }
1452
1453         playlist->clear_changes ();
1454         playlist->clear_owned_changes ();
1455
1456         playlist->fade_range (time);
1457
1458         vector<Command*> cmds;
1459         playlist->rdiff (cmds);
1460         _session->add_commands (cmds);
1461         _session->add_command (new StatefulDiffCommand (playlist));
1462
1463 }
1464
1465 void
1466 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1467 {
1468         boost::shared_ptr<Playlist> what_we_got;
1469         boost::shared_ptr<Track> tr = track ();
1470         boost::shared_ptr<Playlist> playlist;
1471
1472         if (tr == 0) {
1473                 /* route is a bus, not a track */
1474                 return;
1475         }
1476
1477         playlist = tr->playlist();
1478
1479         TimeSelection time (selection.time);
1480         float const speed = tr->speed();
1481         if (speed != 1.0f) {
1482                 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1483                         (*i).start = session_frame_to_track_frame((*i).start, speed);
1484                         (*i).end   = session_frame_to_track_frame((*i).end,   speed);
1485                 }
1486         }
1487
1488         playlist->clear_changes ();
1489         playlist->clear_owned_changes ();
1490
1491         switch (op) {
1492         case Delete:
1493                 if (playlist->cut (time) != 0) {
1494                         if (Config->get_edit_mode() == Ripple)
1495                                 playlist->ripple(time.start(), -time.length(), NULL);
1496                                 // no need to exclude any regions from rippling here
1497
1498                         vector<Command*> cmds;
1499                         playlist->rdiff (cmds);
1500                         _session->add_commands (cmds);
1501                         
1502                         _session->add_command (new StatefulDiffCommand (playlist));
1503                 }
1504                 break;
1505                 
1506         case Cut:
1507                 if ((what_we_got = playlist->cut (time)) != 0) {
1508                         _editor.get_cut_buffer().add (what_we_got);
1509                         if (Config->get_edit_mode() == Ripple)
1510                                 playlist->ripple(time.start(), -time.length(), NULL);
1511                                 // no need to exclude any regions from rippling here
1512
1513                         vector<Command*> cmds;
1514                         playlist->rdiff (cmds);
1515                         _session->add_commands (cmds);
1516
1517                         _session->add_command (new StatefulDiffCommand (playlist));
1518                 }
1519                 break;
1520         case Copy:
1521                 if ((what_we_got = playlist->copy (time)) != 0) {
1522                         _editor.get_cut_buffer().add (what_we_got);
1523                 }
1524                 break;
1525
1526         case Clear:
1527                 if ((what_we_got = playlist->cut (time)) != 0) {
1528                         if (Config->get_edit_mode() == Ripple)
1529                                 playlist->ripple(time.start(), -time.length(), NULL);
1530                                 // no need to exclude any regions from rippling here
1531
1532                         vector<Command*> cmds;
1533                         playlist->rdiff (cmds);
1534                         _session->add_commands (cmds);
1535                         _session->add_command (new StatefulDiffCommand (playlist));
1536                         what_we_got->release ();
1537                 }
1538                 break;
1539         }
1540 }
1541
1542 bool
1543 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1544 {
1545         if (!is_track()) {
1546                 return false;
1547         }
1548
1549         boost::shared_ptr<Playlist> pl = playlist ();
1550         PlaylistSelection::iterator p;
1551
1552         for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1553
1554         if (p == selection.playlists.end()) {
1555                 return false;
1556         }
1557
1558         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1559
1560         if (track()->speed() != 1.0f) {
1561                 pos = session_frame_to_track_frame (pos, track()->speed());
1562                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1563         }
1564
1565         pl->clear_changes ();
1566         if (Config->get_edit_mode() == Ripple) {
1567                 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1568                 framecnt_t amount = extent.second - extent.first;
1569                 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1570         }
1571         pl->paste (*p, pos, times);
1572
1573         vector<Command*> cmds;
1574         pl->rdiff (cmds);
1575         _session->add_commands (cmds);
1576
1577         _session->add_command (new StatefulDiffCommand (pl));
1578
1579         return true;
1580 }
1581
1582
1583 struct PlaylistSorter {
1584     bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1585             return a->sort_id() < b->sort_id();
1586     }
1587 };
1588
1589 void
1590 RouteTimeAxisView::build_playlist_menu ()
1591 {
1592         using namespace Menu_Helpers;
1593
1594         if (!is_track()) {
1595                 return;
1596         }
1597
1598         delete playlist_action_menu;
1599         playlist_action_menu = new Menu;
1600         playlist_action_menu->set_name ("ArdourContextMenu");
1601
1602         MenuList& playlist_items = playlist_action_menu->items();
1603         playlist_action_menu->set_name ("ArdourContextMenu");
1604         playlist_items.clear();
1605
1606         RadioMenuItem::Group playlist_group;
1607         boost::shared_ptr<Track> tr = track ();
1608
1609         vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1610
1611         /* sort the playlists */
1612         PlaylistSorter cmp;
1613         sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1614
1615         /* add the playlists to the menu */
1616         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1617                 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1618                 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1619                 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1620
1621                 if (tr->playlist()->id() == (*i)->id()) {
1622                         item->set_active();
1623
1624                 }
1625         }
1626
1627         playlist_items.push_back (SeparatorElem());
1628         playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1629         playlist_items.push_back (SeparatorElem());
1630
1631         if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1632                 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1633                 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1634
1635         } else {
1636                 // Use a label which tells the user what is happening
1637                 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1638                 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1639
1640         }
1641
1642         playlist_items.push_back (SeparatorElem());
1643         playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1644         playlist_items.push_back (SeparatorElem());
1645
1646         playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1647 }
1648
1649 void
1650 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1651 {
1652         assert (is_track());
1653
1654         // exit if we were triggered by deactivating the old playlist
1655         if (!item->get_active()) {
1656                 return;
1657         }
1658
1659         boost::shared_ptr<Playlist> pl (wpl.lock());
1660
1661         if (!pl) {
1662                 return;
1663         }
1664
1665         if (track()->playlist() == pl) {
1666                 // exit when use_playlist is called by the creation of the playlist menu
1667                 // or the playlist choice is unchanged
1668                 return;
1669         }
1670
1671         track()->use_playlist (pl);
1672         
1673         RouteGroup* rg = route_group();
1674         
1675         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1676                 std::string group_string = "." + rg->name() + ".";
1677                 
1678                 std::string take_name = pl->name();
1679                 std::string::size_type idx = take_name.find(group_string);
1680                 
1681                 if (idx == std::string::npos)
1682                         return;
1683                 
1684                 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1685                 
1686                 boost::shared_ptr<RouteList> rl (rg->route_list());
1687                 
1688                 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1689                         if ((*i) == this->route()) {
1690                                 continue;
1691                         }
1692
1693                         std::string playlist_name = (*i)->name()+group_string+take_name;
1694                         
1695                         boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1696                         if (!track) {
1697                                 continue;
1698                         }
1699
1700                         if (track->freeze_state() == Track::Frozen) {
1701                                 /* Don't change playlists of frozen tracks */
1702                                 continue;
1703                         }
1704                         
1705                         boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1706                         if (!ipl) {
1707                                 // No playlist for this track for this take yet, make it
1708                                 track->use_new_playlist();
1709                                 track->playlist()->set_name(playlist_name);
1710                         } else {
1711                                 track->use_playlist(ipl);
1712                         }
1713                 }
1714         }
1715 }
1716
1717 void
1718 RouteTimeAxisView::update_playlist_tip ()
1719 {
1720         RouteGroup* rg = route_group ();
1721         if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1722                 string group_string = "." + rg->name() + ".";
1723                 
1724                 string take_name = track()->playlist()->name();
1725                 string::size_type idx = take_name.find(group_string);
1726                 
1727                 if (idx != string::npos) {
1728                         /* find the bit containing the take number / name */
1729                         take_name = take_name.substr (idx + group_string.length());
1730
1731                         /* set the playlist button tooltip to the take name */
1732                         ARDOUR_UI::instance()->set_tip (
1733                                 playlist_button,
1734                                 string_compose(_("Take: %1.%2"),
1735                                         Glib::Markup::escape_text(rg->name()),
1736                                         Glib::Markup::escape_text(take_name))
1737                                 );
1738                         
1739                         return;
1740                 }
1741         }
1742
1743         /* set the playlist button tooltip to the playlist name */
1744         ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1745 }
1746
1747
1748 void
1749 RouteTimeAxisView::show_playlist_selector ()
1750 {
1751         _editor.playlist_selector().show_for (this);
1752 }
1753
1754 void
1755 RouteTimeAxisView::map_frozen ()
1756 {
1757         if (!is_track()) {
1758                 return;
1759         }
1760
1761         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1762
1763         switch (track()->freeze_state()) {
1764         case Track::Frozen:
1765                 playlist_button.set_sensitive (false);
1766                 rec_enable_button->set_sensitive (false);
1767                 break;
1768         default:
1769                 playlist_button.set_sensitive (true);
1770                 rec_enable_button->set_sensitive (true);
1771                 break;
1772         }
1773 }
1774
1775 void
1776 RouteTimeAxisView::color_handler ()
1777 {
1778         //case cTimeStretchOutline:
1779         if (timestretch_rect) {
1780                 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1781         }
1782         //case cTimeStretchFill:
1783         if (timestretch_rect) {
1784                 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1785         }
1786
1787         reset_meter();
1788 }
1789
1790 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1791  *  Will add track if necessary.
1792  */
1793 void
1794 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1795 {
1796         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1797         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1798
1799         if (!track) {
1800                 /* it doesn't exist yet, so we don't care about the button state: just add it */
1801                 create_automation_child (param, true);
1802         } else {
1803                 assert (menu);
1804                 bool yn = menu->get_active();
1805                 bool changed = false;
1806
1807                 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1808
1809                         /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1810                            will have done that for us.
1811                         */
1812
1813                         if (changed && !no_redraw) {
1814                                 request_redraw ();
1815                         }
1816                 }
1817         }
1818 }
1819
1820 void
1821 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1822 {
1823         boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1824
1825         if (!track) {
1826                 return;
1827         }
1828
1829         Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1830
1831         if (menu && !_hidden) {
1832                 ignore_toggle = true;
1833                 menu->set_active (false);
1834                 ignore_toggle = false;
1835         }
1836
1837         if (_route && !no_redraw) {
1838                 request_redraw ();
1839         }
1840 }
1841
1842
1843 void
1844 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1845 {
1846         if (apply_to_selection) {
1847                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1848         } else {
1849                 no_redraw = true;
1850
1851                 /* Show our automation */
1852
1853                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1854                         i->second->set_marked_for_display (true);
1855
1856                         Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1857
1858                         if (menu) {
1859                                 menu->set_active(true);
1860                         }
1861                 }
1862
1863
1864                 /* Show processor automation */
1865
1866                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1867                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1868                                 if ((*ii)->view == 0) {
1869                                         add_processor_automation_curve ((*i)->processor, (*ii)->what);
1870                                 }
1871
1872                                 (*ii)->menu_item->set_active (true);
1873                         }
1874                 }
1875
1876                 no_redraw = false;
1877
1878                 /* Redraw */
1879
1880                 request_redraw ();
1881         }
1882 }
1883
1884 void
1885 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1886 {
1887         if (apply_to_selection) {
1888                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1889         } else {
1890                 no_redraw = true;
1891
1892                 /* Show our automation */
1893
1894                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1895                         if (i->second->has_automation()) {
1896                                 i->second->set_marked_for_display (true);
1897
1898                                 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1899                                 if (menu) {
1900                                         menu->set_active(true);
1901                                 }
1902                         }
1903                 }
1904
1905                 /* Show processor automation */
1906
1907                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1908                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1909                                 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1910                                         (*ii)->menu_item->set_active (true);
1911                                 }
1912                         }
1913                 }
1914
1915                 no_redraw = false;
1916
1917                 request_redraw ();
1918         }
1919 }
1920
1921 void
1922 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1923 {
1924         if (apply_to_selection) {
1925                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1926         } else {
1927                 no_redraw = true;
1928
1929                 /* Hide our automation */
1930
1931                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1932                         i->second->set_marked_for_display (false);
1933
1934                         Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1935
1936                         if (menu) {
1937                                 menu->set_active (false);
1938                         }
1939                 }
1940
1941                 /* Hide processor automation */
1942
1943                 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1944                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1945                                 (*ii)->menu_item->set_active (false);
1946                         }
1947                 }
1948
1949                 no_redraw = false;
1950                 request_redraw ();
1951         }
1952 }
1953
1954
1955 void
1956 RouteTimeAxisView::region_view_added (RegionView* rv)
1957 {
1958         /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1959         for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1960                 boost::shared_ptr<AutomationTimeAxisView> atv;
1961
1962                 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1963                         atv->add_ghost(rv);
1964                 }
1965         }
1966
1967         for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1968                 (*i)->add_ghost(rv);
1969         }
1970 }
1971
1972 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1973 {
1974         for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1975                 delete *i;
1976         }
1977 }
1978
1979
1980 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1981 {
1982         parent.remove_processor_automation_node (this);
1983 }
1984
1985 void
1986 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1987 {
1988         if (pan->view) {
1989                 remove_child (pan->view);
1990         }
1991 }
1992
1993 RouteTimeAxisView::ProcessorAutomationNode*
1994 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1995 {
1996         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1997
1998                 if ((*i)->processor == processor) {
1999
2000                         for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2001                                 if ((*ii)->what == what) {
2002                                         return *ii;
2003                                 }
2004                         }
2005                 }
2006         }
2007
2008         return 0;
2009 }
2010
2011 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2012 void
2013 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2014 {
2015         string name;
2016         ProcessorAutomationNode* pan;
2017
2018         if ((pan = find_processor_automation_node (processor, what)) == 0) {
2019                 /* session state may never have been saved with new plugin */
2020                 error << _("programming error: ")
2021                       << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2022                                          processor->name(), what.type(), (int) what.channel(), what.id() )
2023                       << endmsg;
2024                 /*NOTREACHED*/
2025                 return;
2026         }
2027
2028         if (pan->view) {
2029                 return;
2030         }
2031
2032         boost::shared_ptr<AutomationControl> control
2033                 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2034         
2035         pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2036                 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2037                                             _editor, *this, false, parent_canvas, 
2038                                             processor->describe_parameter (what), processor->name()));
2039
2040         pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2041
2042         add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2043
2044         if (_view) {
2045                 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2046         }
2047 }
2048
2049 void
2050 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2051 {
2052         if (!_hidden) {
2053                 pan->menu_item->set_active (false);
2054         }
2055
2056         if (!no_redraw) {
2057                 request_redraw ();
2058         }
2059 }
2060
2061 void
2062 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2063 {
2064         boost::shared_ptr<Processor> processor (p.lock ());
2065
2066         if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2067                 /* The Amp processor is a special case and is dealt with separately */
2068                 return;
2069         }
2070
2071         set<Evoral::Parameter> existing;
2072
2073         processor->what_has_data (existing);
2074
2075         for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2076                 
2077                 Evoral::Parameter param (*i);
2078                 boost::shared_ptr<AutomationLine> al;
2079
2080                 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2081                         al->queue_reset ();
2082                 } else {
2083                         add_processor_automation_curve (processor, param);
2084                 }
2085         }
2086 }
2087
2088 void
2089 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2090 {
2091         using namespace Menu_Helpers;
2092
2093         add_child (track);
2094
2095         track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2096
2097         _automation_tracks[param] = track;
2098
2099         /* existing state overrides "show" argument */
2100         string s = track->gui_property ("visible");
2101         if (!s.empty()) { 
2102                 show = string_is_affirmative (s);
2103         }
2104
2105         /* this might or might not change the visibility status, so don't rely on it */
2106         track->set_marked_for_display (show);
2107
2108         if (show && !no_redraw) {
2109                 request_redraw ();
2110         }
2111
2112         if (!EventTypeMap::instance().is_midi_parameter(param)) {
2113                 /* MIDI-related parameters are always in the menu, there's no
2114                    reason to rebuild the menu just because we added a automation
2115                    lane for one of them. But if we add a non-MIDI automation
2116                    lane, then we need to invalidate the display menu.
2117                 */
2118                 delete display_menu;
2119                 display_menu = 0;
2120         }
2121 }
2122
2123 void
2124 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2125 {
2126         boost::shared_ptr<Processor> processor (p.lock ());
2127
2128         if (!processor || !processor->display_to_user ()) {
2129                 return;
2130         }
2131
2132         /* we use this override to veto the Amp processor from the plugin menu,
2133            as its automation lane can be accessed using the special "Fader" menu
2134            option
2135         */
2136
2137         if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2138                 return;
2139         }
2140
2141         using namespace Menu_Helpers;
2142         ProcessorAutomationInfo *rai;
2143         list<ProcessorAutomationInfo*>::iterator x;
2144
2145         const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2146
2147         if (automatable.empty()) {
2148                 return;
2149         }
2150
2151         for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2152                 if ((*x)->processor == processor) {
2153                         break;
2154                 }
2155         }
2156
2157         if (x == processor_automation.end()) {
2158
2159                 rai = new ProcessorAutomationInfo (processor);
2160                 processor_automation.push_back (rai);
2161
2162         } else {
2163
2164                 rai = *x;
2165
2166         }
2167
2168         /* any older menu was deleted at the top of processors_changed()
2169            when we cleared the subplugin menu.
2170         */
2171
2172         rai->menu = manage (new Menu);
2173         MenuList& items = rai->menu->items();
2174         rai->menu->set_name ("ArdourContextMenu");
2175
2176         items.clear ();
2177
2178         std::set<Evoral::Parameter> has_visible_automation;
2179         AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2180
2181         for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2182
2183                 ProcessorAutomationNode* pan;
2184                 Gtk::CheckMenuItem* mitem;
2185
2186                 string name = processor->describe_parameter (*i);
2187
2188                 items.push_back (CheckMenuElem (name));
2189                 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2190                 
2191                 _subplugin_menu_map[*i] = mitem;
2192
2193                 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2194                         mitem->set_active(true);
2195                 }
2196
2197                 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2198
2199                         /* new item */
2200
2201                         pan = new ProcessorAutomationNode (*i, mitem, *this);
2202
2203                         rai->lines.push_back (pan);
2204
2205                 } else {
2206
2207                         pan->menu_item = mitem;
2208
2209                 }
2210
2211                 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2212         }
2213
2214         /* add the menu for this processor, because the subplugin
2215            menu is always cleared at the top of processors_changed().
2216            this is the result of some poor design in gtkmm and/or
2217            GTK+.
2218         */
2219
2220         subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2221         rai->valid = true;
2222 }
2223
2224 void
2225 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2226                                                RouteTimeAxisView::ProcessorAutomationNode* pan)
2227 {
2228         bool showit = pan->menu_item->get_active();
2229         bool redraw = false;
2230
2231         if (pan->view == 0 && showit) {
2232                 add_processor_automation_curve (rai->processor, pan->what);
2233                 redraw = true;
2234         }
2235
2236         if (pan->view && pan->view->set_marked_for_display (showit)) {
2237                 redraw = true;
2238         }
2239         
2240         if (redraw && !no_redraw) {
2241                 request_redraw ();
2242         }
2243 }
2244
2245 void
2246 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2247 {
2248         if (c.type == RouteProcessorChange::MeterPointChange) {
2249                 /* nothing to do if only the meter point has changed */
2250                 return;
2251         }
2252
2253         using namespace Menu_Helpers;
2254
2255         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2256                 (*i)->valid = false;
2257         }
2258
2259         setup_processor_menu_and_curves ();
2260
2261         bool deleted_processor_automation = false;
2262
2263         for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2264
2265                 list<ProcessorAutomationInfo*>::iterator tmp;
2266
2267                 tmp = i;
2268                 ++tmp;
2269
2270                 if (!(*i)->valid) {
2271
2272                         delete *i;
2273                         processor_automation.erase (i);
2274                         deleted_processor_automation = true;
2275
2276                 }
2277
2278                 i = tmp;
2279         }
2280
2281         if (deleted_processor_automation && !no_redraw) {
2282                 request_redraw ();
2283         }
2284 }
2285
2286 boost::shared_ptr<AutomationLine>
2287 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2288 {
2289         ProcessorAutomationNode* pan;
2290
2291         if ((pan = find_processor_automation_node (processor, what)) != 0) {
2292                 if (pan->view) {
2293                         pan->view->line();
2294                 }
2295         }
2296
2297         return boost::shared_ptr<AutomationLine>();
2298 }
2299
2300 void
2301 RouteTimeAxisView::reset_processor_automation_curves ()
2302 {
2303         for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2304                 (*i)->reset();
2305         }
2306 }
2307
2308 bool
2309 RouteTimeAxisView::can_edit_name () const
2310 {
2311         /* we do not allow track name changes if it is record enabled
2312          */
2313         return !_route->record_enabled();
2314 }
2315
2316 void
2317 RouteTimeAxisView::blink_rec_display (bool onoff)
2318 {
2319         RouteUI::blink_rec_display (onoff);
2320 }
2321
2322 void
2323 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2324 {
2325         if (_ignore_set_layer_display) {
2326                 return;
2327         }
2328         
2329         if (apply_to_selection) {
2330                 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2331         } else {
2332
2333                 if (_view) {
2334                         _view->set_layer_display (d);
2335                 }
2336
2337                 set_gui_property (X_("layer-display"), enum_2_string (d));
2338         }
2339 }
2340
2341 LayerDisplay
2342 RouteTimeAxisView::layer_display () const
2343 {
2344         if (_view) {
2345                 return _view->layer_display ();
2346         }
2347
2348         /* we don't know, since we don't have a _view, so just return something */
2349         return Overlaid;
2350 }
2351
2352
2353
2354 boost::shared_ptr<AutomationTimeAxisView>
2355 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2356 {
2357         AutomationTracks::iterator i = _automation_tracks.find(param);
2358         if (i != _automation_tracks.end()) {
2359                 return i->second;
2360         } else {
2361                 return boost::shared_ptr<AutomationTimeAxisView>();
2362         }
2363 }
2364
2365 void
2366 RouteTimeAxisView::fast_update ()
2367 {
2368         gm.get_level_meter().update_meters ();
2369 }
2370
2371 void
2372 RouteTimeAxisView::hide_meter ()
2373 {
2374         clear_meter ();
2375         gm.get_level_meter().hide_meters ();
2376 }
2377
2378 void
2379 RouteTimeAxisView::show_meter ()
2380 {
2381         reset_meter ();
2382 }
2383
2384 void
2385 RouteTimeAxisView::reset_meter ()
2386 {
2387         if (Config->get_show_track_meters()) {
2388                 int meter_width = 3;
2389                 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2390                         meter_width = 6;
2391                 }
2392                 gm.get_level_meter().setup_meters (height - 9, meter_width);
2393         } else {
2394                 hide_meter ();
2395         }
2396 }
2397
2398 void
2399 RouteTimeAxisView::clear_meter ()
2400 {
2401         gm.get_level_meter().clear_meters ();
2402 }
2403
2404 void
2405 RouteTimeAxisView::meter_changed ()
2406 {
2407         ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2408         reset_meter();
2409         if (_route && !no_redraw) {
2410                 request_redraw ();
2411         }
2412         // reset peak when meter point changes
2413         gm.reset_peak_display();
2414 }
2415
2416 void
2417 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2418 {
2419         reset_meter ();
2420         if (_route && !no_redraw) {
2421                 request_redraw ();
2422         }
2423 }
2424
2425 void
2426 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2427 {
2428         using namespace Menu_Helpers;
2429
2430         if (!_underlay_streams.empty()) {
2431                 MenuList& parent_items = parent_menu->items();
2432                 Menu* gs_menu = manage (new Menu);
2433                 gs_menu->set_name ("ArdourContextMenu");
2434                 MenuList& gs_items = gs_menu->items();
2435
2436                 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2437
2438                 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2439                         gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2440                                                     sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2441                 }
2442         }
2443 }
2444
2445 bool
2446 RouteTimeAxisView::set_underlay_state()
2447 {
2448         if (!underlay_xml_node) {
2449                 return false;
2450         }
2451
2452         XMLNodeList nlist = underlay_xml_node->children();
2453         XMLNodeConstIterator niter;
2454         XMLNode *child_node;
2455
2456         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2457                 child_node = *niter;
2458
2459                 if (child_node->name() != "Underlay") {
2460                         continue;
2461                 }
2462
2463                 XMLProperty* prop = child_node->property ("id");
2464                 if (prop) {
2465                         PBD::ID id (prop->value());
2466
2467                         RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2468
2469                         if (v) {
2470                                 add_underlay(v->view(), false);
2471                         }
2472                 }
2473         }
2474
2475         return false;
2476 }
2477
2478 void
2479 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2480 {
2481         if (!v) {
2482                 return;
2483         }
2484
2485         RouteTimeAxisView& other = v->trackview();
2486
2487         if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2488                 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2489                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2490                         /*NOTREACHED*/
2491                 }
2492
2493                 _underlay_streams.push_back(v);
2494                 other._underlay_mirrors.push_back(this);
2495
2496                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2497
2498 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2499                 if (update_xml) {
2500                         if (!underlay_xml_node) {
2501                                 underlay_xml_node = xml_node->add_child("Underlays");
2502                         }
2503
2504                         XMLNode* node = underlay_xml_node->add_child("Underlay");
2505                         XMLProperty* prop = node->add_property("id");
2506                         prop->set_value(v->trackview().route()->id().to_s());
2507                 }
2508 #endif
2509         }
2510 }
2511
2512 void
2513 RouteTimeAxisView::remove_underlay (StreamView* v)
2514 {
2515         if (!v) {
2516                 return;
2517         }
2518
2519         UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2520         RouteTimeAxisView& other = v->trackview();
2521
2522         if (it != _underlay_streams.end()) {
2523                 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2524
2525                 if (gm == other._underlay_mirrors.end()) {
2526                         fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2527                         /*NOTREACHED*/
2528                 }
2529
2530                 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2531
2532                 _underlay_streams.erase(it);
2533                 other._underlay_mirrors.erase(gm);
2534
2535                 if (underlay_xml_node) {
2536                         underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2537                 }
2538         }
2539 }
2540
2541 void
2542 RouteTimeAxisView::set_button_names ()
2543 {
2544         if (_route && _route->solo_safe()) {
2545                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2546         } else {
2547                 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2548         }
2549         if (Config->get_solo_control_is_listen_control()) {
2550                 switch (Config->get_listen_position()) {
2551                         case AfterFaderListen:
2552                                 solo_button->set_text (_("A"));
2553                                 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2554                                 break;
2555                         case PreFaderListen:
2556                                 solo_button->set_text (_("P"));
2557                                 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2558                         break;
2559                 }
2560         } else {
2561                 solo_button->set_text (_("S"));
2562                 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2563         }
2564         mute_button->set_text (_("M"));
2565 }
2566
2567 Gtk::CheckMenuItem*
2568 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2569 {
2570         ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2571         if (i != _main_automation_menu_map.end()) {
2572                 return i->second;
2573         }
2574
2575         i = _subplugin_menu_map.find (param);
2576         if (i != _subplugin_menu_map.end()) {
2577                 return i->second;
2578         }
2579
2580         return 0;
2581 }
2582
2583 void
2584 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2585 {
2586         boost::shared_ptr<AutomationControl> c = _route->gain_control();
2587         if (!c) {
2588                 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2589                 return;
2590         }
2591
2592         gain_track.reset (new AutomationTimeAxisView (_session,
2593                                                       _route, _route->amp(), c, param,
2594                                                       _editor,
2595                                                       *this,
2596                                                       false,
2597                                                       parent_canvas,
2598                                                       _route->amp()->describe_parameter(param)));
2599
2600         if (_view) {
2601                 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2602         }
2603
2604         add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2605 }
2606
2607 void
2608 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2609 {
2610         boost::shared_ptr<AutomationControl> c = _route->mute_control();
2611         if (!c) {
2612                 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2613                 return;
2614         }
2615
2616         mute_track.reset (new AutomationTimeAxisView (_session,
2617                                                       _route, _route, c, param,
2618                                                       _editor,
2619                                                       *this,
2620                                                       false,
2621                                                       parent_canvas,
2622                                                       _route->describe_parameter(param)));
2623
2624         if (_view) {
2625                 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2626         }
2627
2628         add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2629 }
2630
2631 static
2632 void add_region_to_list (RegionView* rv, RegionList* l)
2633 {
2634         l->push_back (rv->region());
2635 }
2636
2637 RegionView*
2638 RouteTimeAxisView::combine_regions ()
2639 {
2640         /* as of may 2011, we do not offer uncombine for MIDI tracks
2641          */
2642
2643         if (!is_audio_track()) {
2644                 return 0;
2645         }
2646
2647         if (!_view) {
2648                 return 0;
2649         }
2650
2651         RegionList selected_regions;
2652         boost::shared_ptr<Playlist> playlist = track()->playlist();
2653
2654         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2655
2656         if (selected_regions.size() < 2) {
2657                 return 0;
2658         }
2659
2660         playlist->clear_changes ();
2661         boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2662
2663         _session->add_command (new StatefulDiffCommand (playlist));
2664         /* make the new region be selected */
2665
2666         return _view->find_view (compound_region);
2667 }
2668
2669 void
2670 RouteTimeAxisView::uncombine_regions ()
2671 {
2672         /* as of may 2011, we do not offer uncombine for MIDI tracks
2673          */
2674         if (!is_audio_track()) {
2675                 return;
2676         }
2677
2678         if (!_view) {
2679                 return;
2680         }
2681
2682         RegionList selected_regions;
2683         boost::shared_ptr<Playlist> playlist = track()->playlist();
2684
2685         /* have to grab selected regions first because the uncombine is going
2686          * to change that in the middle of the list traverse
2687          */
2688
2689         _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2690
2691         playlist->clear_changes ();
2692
2693         for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2694                 playlist->uncombine (*i);
2695         }
2696
2697         _session->add_command (new StatefulDiffCommand (playlist));
2698 }
2699
2700 string
2701 RouteTimeAxisView::state_id() const
2702 {
2703         return string_compose ("rtav %1", _route->id().to_s());
2704 }
2705
2706
2707 void
2708 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2709 {
2710         TimeAxisView::remove_child (c);
2711         
2712         boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2713         if (a) {
2714                 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2715                         if (i->second == a) {
2716                                 _automation_tracks.erase (i);
2717                                 return;
2718                         }
2719                 }
2720         }
2721 }