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