2 Copyright (C) 2000-2007 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <gtkmm2ext/barcontroller.h>
23 #include "pbd/memento_command.h"
24 #include "pbd/stacktrace.h"
26 #include "ardour/automation_control.h"
27 #include "ardour/event_type_map.h"
28 #include "ardour/route.h"
29 #include "ardour/session.h"
31 #include "ardour_ui.h"
32 #include "automation_time_axis.h"
33 #include "automation_streamview.h"
34 #include "global_signals.h"
35 #include "gui_thread.h"
36 #include "route_time_axis.h"
37 #include "automation_line.h"
38 #include "public_editor.h"
39 #include "simplerect.h"
40 #include "selection.h"
41 #include "rgb_macros.h"
42 #include "point_selection.h"
43 #include "canvas_impl.h"
49 using namespace ARDOUR;
52 using namespace Gtkmm2ext;
53 using namespace Editing;
55 Pango::FontDescription AutomationTimeAxisView::name_font;
56 bool AutomationTimeAxisView::have_name_font = false;
59 /** \a a the automatable object this time axis is to display data for.
60 * For route/track automation (e.g. gain) pass the route for both \r and \a.
61 * For route child (e.g. plugin) automation, pass the child for \a.
62 * For region automation (e.g. MIDI CC), pass null for \a.
64 AutomationTimeAxisView::AutomationTimeAxisView (
66 boost::shared_ptr<Route> r,
67 boost::shared_ptr<Automatable> a,
68 boost::shared_ptr<AutomationControl> c,
73 ArdourCanvas::Canvas& canvas,
75 const string & nomparent
78 , TimeAxisView (s, e, &parent, canvas)
84 , _view (show_regions ? new AutomationStreamView (*this) : 0)
86 , auto_button (X_("")) /* force addition of a label */
88 if (!have_name_font) {
89 name_font = get_font_for_style (X_("AutomationTrackName"));
90 have_name_font = true;
93 if (_automatable && _control) {
94 _controller = AutomationController::create (_automatable, _control->parameter(), _control);
102 mode_discrete_item = 0;
105 ignore_state_request = false;
106 first_call_to_set_height = true;
108 _base_rect = new SimpleRect(*_canvas_display);
109 _base_rect->property_x1() = 0.0;
110 _base_rect->property_y1() = 0.0;
111 /** gnomecanvas sometimes converts this value to int or adds 2 to it, so it must be
112 set correctly to avoid overflow.
114 _base_rect->property_x2() = INT_MAX - 2;
115 _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
117 /* outline ends and bottom */
118 _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
119 _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
121 _base_rect->set_data ("trackview", this);
123 _base_rect->signal_event().connect (sigc::bind (
124 sigc::mem_fun (_editor, &PublicEditor::canvas_automation_track_event),
128 _base_rect->lower_to_bottom();
131 hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
133 auto_button.set_name ("TrackVisualButton");
134 hide_button.set_name ("TrackRemoveButton");
136 auto_button.unset_flags (Gtk::CAN_FOCUS);
137 hide_button.unset_flags (Gtk::CAN_FOCUS);
139 controls_table.set_no_show_all();
141 ARDOUR_UI::instance()->set_tip(auto_button, _("automation state"));
142 ARDOUR_UI::instance()->set_tip(hide_button, _("hide track"));
144 string str = gui_property ("height");
146 set_height (atoi (str));
148 set_height (preset_height (HeightNormal));
151 /* rearrange the name display */
153 /* we never show these for automation tracks, so make
154 life easier and remove them.
159 /* keep the parameter name short */
161 string shortpname = _name;
163 shortpname = fit_to_pixels (_name, 60, name_font, ignore_width, true);
165 name_label.set_text (shortpname);
166 name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
167 name_label.set_name (X_("TrackParameterName"));
169 string tipname = nomparent;
170 if (!tipname.empty()) {
174 ARDOUR_UI::instance()->set_tip(controls_ebox, tipname);
176 /* add the buttons */
177 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
179 controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
182 /* add bar controller */
183 controls_table.attach (*_controller.get(), 0, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
186 controls_table.show_all ();
188 hide_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
189 auto_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
191 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
192 controls_base_unselected_name = X_("AutomationTrackControlsBase");
193 controls_ebox.set_name (controls_base_unselected_name);
195 /* ask for notifications of any new RegionViews */
202 /* no regions, just a single line for the entire track (e.g. bus gain) */
206 boost::shared_ptr<AutomationLine> line (
208 ARDOUR::EventTypeMap::instance().to_symbol(_parameter),
215 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
216 line->queue_reset ();
220 /* make sure labels etc. are correct */
222 automation_state_changed ();
223 ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
225 _route->DropReferences.connect (
226 _route_connections, invalidator (*this), ui_bind (&AutomationTimeAxisView::route_going_away, this), gui_context ()
230 AutomationTimeAxisView::~AutomationTimeAxisView ()
235 AutomationTimeAxisView::route_going_away ()
241 AutomationTimeAxisView::auto_clicked ()
243 using namespace Menu_Helpers;
245 if (automation_menu == 0) {
246 automation_menu = manage (new Menu);
247 automation_menu->set_name ("ArdourContextMenu");
248 MenuList& items (automation_menu->items());
250 items.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
251 &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
252 items.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
253 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
254 items.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
255 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
256 items.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
257 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
260 automation_menu->popup (1, gtk_get_current_event_time());
264 AutomationTimeAxisView::set_automation_state (AutoState state)
266 if (ignore_state_request) {
271 _automatable->set_parameter_automation_state (_parameter, state);
275 _view->set_automation_state (state);
277 /* AutomationStreamViews don't signal when their automation state changes, so handle
278 our updates `manually'.
280 automation_state_changed ();
285 AutomationTimeAxisView::automation_state_changed ()
289 /* update button label */
292 state = _view->automation_state ();
295 state = _control->alist()->automation_state ();
300 switch (state & (Off|Play|Touch|Write)) {
302 auto_button.set_label (_("Manual"));
304 ignore_state_request = true;
305 auto_off_item->set_active (true);
306 auto_play_item->set_active (false);
307 auto_touch_item->set_active (false);
308 auto_write_item->set_active (false);
309 ignore_state_request = false;
313 auto_button.set_label (_("Play"));
314 if (auto_play_item) {
315 ignore_state_request = true;
316 auto_play_item->set_active (true);
317 auto_off_item->set_active (false);
318 auto_touch_item->set_active (false);
319 auto_write_item->set_active (false);
320 ignore_state_request = false;
324 auto_button.set_label (_("Write"));
325 if (auto_write_item) {
326 ignore_state_request = true;
327 auto_write_item->set_active (true);
328 auto_off_item->set_active (false);
329 auto_play_item->set_active (false);
330 auto_touch_item->set_active (false);
331 ignore_state_request = false;
335 auto_button.set_label (_("Touch"));
336 if (auto_touch_item) {
337 ignore_state_request = true;
338 auto_touch_item->set_active (true);
339 auto_off_item->set_active (false);
340 auto_play_item->set_active (false);
341 auto_write_item->set_active (false);
342 ignore_state_request = false;
346 auto_button.set_label (_("???"));
351 /** The interpolation style of our AutomationList has changed, so update */
353 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
355 if (mode_line_item && mode_discrete_item) {
356 if (s == AutomationList::Discrete) {
357 mode_discrete_item->set_active(true);
358 mode_line_item->set_active(false);
360 mode_line_item->set_active(true);
361 mode_discrete_item->set_active(false);
366 /** A menu item has been selected to change our interpolation mode */
368 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
370 /* Tell our view's list, if we have one, otherwise tell our own.
371 * Everything else will be signalled back from that.
375 _view->set_interpolation (style);
378 _control->list()->set_interpolation (style);
383 AutomationTimeAxisView::clear_clicked ()
385 assert (_line || _view);
387 _session->begin_reversible_command (_("clear automation"));
395 _session->commit_reversible_command ();
396 _session->set_dirty ();
400 AutomationTimeAxisView::set_height (uint32_t h)
402 bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
403 uint32_t const normal = preset_height (HeightNormal);
404 bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
406 TimeAxisView::set_height (h);
408 _base_rect->property_y2() = h;
411 _line->set_height(h);
415 _view->set_height(h);
416 _view->update_contents_height();
419 if (changed_between_small_and_normal || first_call_to_set_height) {
421 first_call_to_set_height = false;
423 if (h >= preset_height (HeightNormal)) {
426 name_hbox.show_all ();
429 hide_button.show_all();
431 } else if (h >= preset_height (HeightSmall)) {
432 controls_table.hide_all ();
435 name_hbox.show_all ();
440 } else if (h >= preset_height (HeightNormal)) {
441 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
445 if (canvas_item_visible (_canvas_display) && _route) {
446 /* only emit the signal if the height really changed and we were visible */
447 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
453 AutomationTimeAxisView::set_samples_per_unit (double spu)
455 TimeAxisView::set_samples_per_unit (spu);
462 _view->set_samples_per_unit (spu);
467 AutomationTimeAxisView::hide_clicked ()
469 hide_button.set_sensitive(false);
470 set_marked_for_display (false);
471 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(parent);
473 rtv->request_redraw ();
475 hide_button.set_sensitive(true);
479 AutomationTimeAxisView::build_display_menu ()
481 using namespace Menu_Helpers;
485 TimeAxisView::build_display_menu ();
487 /* now fill it with our stuff */
489 MenuList& items = display_menu->items();
491 items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
492 items.push_back (SeparatorElem());
493 items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
494 items.push_back (SeparatorElem());
498 Menu* auto_state_menu = manage (new Menu);
499 auto_state_menu->set_name ("ArdourContextMenu");
500 MenuList& as_items = auto_state_menu->items();
502 as_items.push_back (CheckMenuElem (_("Manual"), sigc::bind (
503 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
505 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
507 as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
508 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
510 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
512 as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
513 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
514 (AutoState) Write)));
515 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
517 as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
518 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
519 (AutoState) Touch)));
520 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
522 items.push_back (MenuElem (_("State"), *auto_state_menu));
526 /* current interpolation state */
527 AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
529 if (EventTypeMap::instance().is_midi_parameter(_parameter)) {
531 Menu* auto_mode_menu = manage (new Menu);
532 auto_mode_menu->set_name ("ArdourContextMenu");
533 MenuList& am_items = auto_mode_menu->items();
535 RadioMenuItem::Group group;
537 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
538 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
539 AutomationList::Discrete)));
540 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
541 mode_discrete_item->set_active (s == AutomationList::Discrete);
543 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
544 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
545 AutomationList::Linear)));
546 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
547 mode_line_item->set_active (s == AutomationList::Linear);
549 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
552 /* make sure the automation menu state is correct */
554 automation_state_changed ();
555 interpolation_changed (s);
559 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, framepos_t when, double y)
567 _canvas_display->w2i (x, y);
569 /* compute vertical fractional position */
571 y = 1.0 - (y / height);
575 _line->view_to_model_coord (x, y);
577 boost::shared_ptr<AutomationList> list = _line->the_list ();
579 _session->begin_reversible_command (_("add automation event"));
580 XMLNode& before = list->get_state();
584 XMLNode& after = list->get_state();
585 _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList> (*list, &before, &after));
586 _session->set_dirty ();
590 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
592 list<boost::shared_ptr<AutomationLine> > lines;
594 lines.push_back (_line);
596 lines = _view->get_lines ();
599 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
600 cut_copy_clear_one (**i, selection, op);
605 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
607 boost::shared_ptr<Evoral::ControlList> what_we_got;
608 boost::shared_ptr<AutomationList> alist (line.the_list());
610 XMLNode &before = alist->get_state();
612 /* convert time selection to automation list model coordinates */
613 const Evoral::TimeConverter<double, ARDOUR::framepos_t>& tc = line.time_converter ();
614 double const start = tc.from (selection.time.front().start - tc.origin_b ());
615 double const end = tc.from (selection.time.front().end - tc.origin_b ());
619 if (alist->cut (start, end) != 0) {
620 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
626 if ((what_we_got = alist->cut (start, end)) != 0) {
627 _editor.get_cut_buffer().add (what_we_got);
628 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
632 if ((what_we_got = alist->copy (start, end)) != 0) {
633 _editor.get_cut_buffer().add (what_we_got);
638 if ((what_we_got = alist->cut (start, end)) != 0) {
639 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
645 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
646 double when = (*x)->when;
647 double val = (*x)->value;
648 line.model_to_view_coord (when, val);
656 AutomationTimeAxisView::reset_objects (PointSelection& selection)
658 list<boost::shared_ptr<AutomationLine> > lines;
660 lines.push_back (_line);
662 lines = _view->get_lines ();
665 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
666 reset_objects_one (**i, selection);
671 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
673 boost::shared_ptr<AutomationList> alist(line.the_list());
675 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
677 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
679 if ((*i).track != this) {
683 alist->reset_range ((*i).start, (*i).end);
688 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
690 list<boost::shared_ptr<AutomationLine> > lines;
692 lines.push_back (_line);
694 lines = _view->get_lines ();
697 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
698 cut_copy_clear_objects_one (**i, selection, op);
703 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
705 boost::shared_ptr<Evoral::ControlList> what_we_got;
706 boost::shared_ptr<AutomationList> alist(line.the_list());
708 XMLNode &before = alist->get_state();
710 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
712 if ((*i).track != this) {
718 if (alist->cut ((*i).start, (*i).end) != 0) {
719 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
723 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
724 _editor.get_cut_buffer().add (what_we_got);
725 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
729 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
730 _editor.get_cut_buffer().add (what_we_got);
735 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
736 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
745 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
746 double when = (*x)->when;
747 double val = (*x)->value;
748 line.model_to_view_coord (when, val);
755 /** Paste a selection.
756 * @param pos Position to paste to (session frames).
757 * @param times Number of times to paste.
758 * @param selection Selection to paste.
759 * @param nth Index of the AutomationList within the selection to paste from.
762 AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
764 boost::shared_ptr<AutomationLine> line;
769 line = _view->paste_line (pos);
776 return paste_one (*line, pos, times, selection, nth);
780 AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float times, Selection& selection, size_t nth)
782 AutomationSelection::iterator p;
783 boost::shared_ptr<AutomationList> alist(line.the_list());
785 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
787 if (p == selection.lines.end()) {
791 /* Make a copy of the list because we have to scale the
792 values from view coordinates to model coordinates, and we're
793 not supposed to modify the points in the selection.
796 AutomationList copy (**p);
798 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
799 double when = (*x)->when;
800 double val = (*x)->value;
801 line.view_to_model_coord (when, val);
806 double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ());
808 XMLNode &before = alist->get_state();
809 alist->paste (copy, model_pos, times);
810 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
816 AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
818 if (!_line && !_view) {
822 if (touched (top, bot)) {
824 /* remember: this is X Window - coordinate space starts in upper left and moves down.
825 _y_position is the "origin" or "top" of the track.
828 /* bottom of our track */
829 double const mybot = _y_position + height;
834 if (_y_position >= top && mybot <= bot) {
836 /* _y_position is below top, mybot is above bot, so we're fully
845 /* top and bot are within _y_position .. mybot */
847 topfrac = 1.0 - ((top - _y_position) / height);
848 botfrac = 1.0 - ((bot - _y_position) / height);
853 _line->get_selectables (start, end, botfrac, topfrac, results);
855 _view->get_selectables (start, end, botfrac, topfrac, results);
861 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
864 _line->get_inverted_selectables (sel, result);
869 AutomationTimeAxisView::set_selected_points (PointSelection& points)
872 _line->set_selected_points (points);
874 _view->set_selected_points (points);
879 AutomationTimeAxisView::clear_lines ()
882 _list_connections.drop_connections ();
886 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
891 assert(line->the_list() == _control->list());
893 _control->alist()->automation_state_changed.connect (
894 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
897 _control->alist()->InterpolationChanged.connect (
898 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
904 line->set_height (height);
906 /* pick up the current state */
907 automation_state_changed ();
913 AutomationTimeAxisView::entered()
916 _line->track_entered();
921 AutomationTimeAxisView::exited ()
924 _line->track_exited();
929 AutomationTimeAxisView::color_handler ()
937 AutomationTimeAxisView::set_state_2X (const XMLNode& node, int /*version*/)
939 if (node.name() == X_("gain") && _parameter == Evoral::Parameter (GainAutomation)) {
940 XMLProperty const * shown = node.property (X_("shown"));
942 bool yn = string_is_affirmative (shown->value ());
944 _canvas_display->show (); /* FIXME: necessary? show_at? */
946 set_gui_property ("visible", (yn ? "yes" : "no"));
948 set_gui_property ("visible", "no");
956 AutomationTimeAxisView::set_state (const XMLNode& node, int /*version*/)
962 AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr<Automatable>& automatable, set<Evoral::Parameter>& visible)
964 /* this keeps "knowledge" of how we store visibility information
965 in XML private to this class.
968 assert (automatable);
970 Automatable::Controls& controls (automatable->controls());
972 for (Automatable::Controls::iterator i = controls.begin(); i != controls.end(); ++i) {
974 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
978 const XMLNode* gui_node = ac->extra_xml ("GUI");
981 const XMLProperty* prop = gui_node->property ("shown");
983 if (string_is_affirmative (prop->value())) {
984 visible.insert (i->first);
993 /** @return true if this view has any automation data to display */
995 AutomationTimeAxisView::has_automation () const
997 return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
1000 list<boost::shared_ptr<AutomationLine> >
1001 AutomationTimeAxisView::lines () const
1003 list<boost::shared_ptr<AutomationLine> > lines;
1006 lines.push_back (_line);
1008 lines = _view->get_lines ();
1015 AutomationTimeAxisView::state_id() const
1018 return string_compose ("automation %1", _control->id().to_s());
1020 assert (_parameter);
1021 return string_compose ("automation %1 %2/%3/%4",
1025 _parameter.channel());