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