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