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