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>
22 #include "pbd/memento_command.h"
23 #include "ardour/automation_control.h"
24 #include "ardour/event_type_map.h"
25 #include "ardour/route.h"
26 #include "ardour/session.h"
28 #include "ardour_ui.h"
29 #include "automation_time_axis.h"
30 #include "automation_streamview.h"
31 #include "gui_thread.h"
32 #include "route_time_axis.h"
33 #include "automation_line.h"
34 #include "public_editor.h"
35 #include "simplerect.h"
36 #include "selection.h"
37 #include "rgb_macros.h"
38 #include "automation_selectable.h"
39 #include "point_selection.h"
40 #include "canvas_impl.h"
46 using namespace ARDOUR;
49 using namespace Gtkmm2ext;
50 using namespace Editing;
52 Pango::FontDescription* AutomationTimeAxisView::name_font = 0;
53 bool AutomationTimeAxisView::have_name_font = false;
54 const string AutomationTimeAxisView::state_node_name = "AutomationChild";
57 /** \a a the automatable object this time axis is to display data for.
58 * For route/track automation (e.g. gain) pass the route for both \r and \a.
59 * For route child (e.g. plugin) automation, pass the child for \a.
60 * For region automation (e.g. MIDI CC), pass null for \a.
62 AutomationTimeAxisView::AutomationTimeAxisView (Session* s, boost::shared_ptr<Route> r,
63 boost::shared_ptr<Automatable> a, boost::shared_ptr<AutomationControl> c,
64 PublicEditor& e, TimeAxisView& parent, bool show_regions,
65 ArdourCanvas::Canvas& canvas, const string & nom, const string & nomparent)
67 TimeAxisView (s, e, &parent, canvas),
71 _controller(AutomationController::create(a, c->parameter(), c)),
73 _view (show_regions ? new AutomationStreamView(*this) : NULL),
75 auto_button (X_("")) /* force addition of a label */
77 if (!have_name_font) {
78 name_font = get_font_for_style (X_("AutomationTrackName"));
79 have_name_font = true;
87 mode_discrete_item = 0;
90 ignore_state_request = false;
91 first_call_to_set_height = true;
93 _base_rect = new SimpleRect(*_canvas_display);
94 _base_rect->property_x1() = 0.0;
95 _base_rect->property_y1() = 0.0;
96 _base_rect->property_x2() = LONG_MAX - 2;
97 _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
99 /* outline ends and bottom */
100 _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
101 _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
103 _base_rect->set_data ("trackview", this);
105 _base_rect->signal_event().connect (sigc::bind (
106 sigc::mem_fun (_editor, &PublicEditor::canvas_automation_track_event),
110 _base_rect->lower_to_bottom();
113 hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
115 auto_button.set_name ("TrackVisualButton");
116 hide_button.set_name ("TrackRemoveButton");
118 auto_button.unset_flags (Gtk::CAN_FOCUS);
119 hide_button.unset_flags (Gtk::CAN_FOCUS);
121 controls_table.set_no_show_all();
123 ARDOUR_UI::instance()->set_tip(auto_button, _("automation state"));
124 ARDOUR_UI::instance()->set_tip(hide_button, _("hide track"));
126 /* rearrange the name display */
128 /* we never show these for automation tracks, so make
129 life easier and remove them.
134 /* move the name label over a bit */
136 string shortpname = _name;
137 bool shortened = false;
140 shortpname = fit_to_pixels (_name, 60, *name_font, ignore_width, true);
142 if (shortpname != _name ){
146 name_label.set_text (shortpname);
147 name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
149 if (nomparent.length()) {
151 /* limit the plug name string */
153 string pname = fit_to_pixels (nomparent, 60, *name_font, ignore_width, true);
154 if (pname != nomparent) {
158 plugname = new Label (pname);
159 plugname->set_name (X_("TrackPlugName"));
161 name_label.set_name (X_("TrackParameterName"));
162 controls_table.remove (name_hbox);
163 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
164 plugname_packed = true;
165 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
168 plugname_packed = false;
172 string tipname = nomparent;
173 if (!tipname.empty()) {
177 ARDOUR_UI::instance()->set_tip(controls_ebox, tipname);
180 /* add the buttons */
181 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
183 controls_table.attach (auto_button, 5, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
185 /* add bar controller */
186 controls_table.attach (*_controller.get(), 0, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
188 controls_table.show_all ();
190 hide_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
191 auto_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
193 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
194 controls_base_unselected_name = X_("AutomationTrackControlsBase");
195 controls_ebox.set_name (controls_base_unselected_name);
197 XMLNode* xml_node = get_parent_with_state()->get_automation_child_xml_node (
198 _control->parameter());
201 set_state (*xml_node, Stateful::loading_state_version);
204 /* ask for notifications of any new RegionViews */
210 /* no regions, just a single line for the entire track (e.g. bus gain) */
212 boost::shared_ptr<AutomationLine> line(new AutomationLine (
213 ARDOUR::EventTypeMap::instance().to_symbol(_control->parameter()),
218 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
219 line->queue_reset ();
223 /* make sure labels etc. are correct */
225 automation_state_changed ();
226 ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
229 AutomationTimeAxisView::~AutomationTimeAxisView ()
234 AutomationTimeAxisView::auto_clicked ()
236 using namespace Menu_Helpers;
238 if (automation_menu == 0) {
239 automation_menu = manage (new Menu);
240 automation_menu->set_name ("ArdourContextMenu");
241 MenuList& items (automation_menu->items());
243 items.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
244 &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
245 items.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
246 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
247 items.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
248 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
249 items.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
250 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
253 automation_menu->popup (1, gtk_get_current_event_time());
257 AutomationTimeAxisView::set_automation_state (AutoState state)
259 if (!ignore_state_request) {
261 _automatable->set_parameter_automation_state (_control->parameter(), state);
264 if (_route == _automatable) { // This is a time axis for route (not region) automation
265 _route->set_parameter_automation_state (_control->parameter(), state);
268 if (_control->list())
269 _control->alist()->set_automation_state(state);
274 _view->set_automation_state (state);
276 /* AutomationStreamViews don't signal when their automation state changes, so handle
277 our updates `manually'.
279 automation_state_changed ();
284 AutomationTimeAxisView::automation_state_changed ()
288 /* update button label */
291 state = _control->alist()->automation_state ();
293 state = _view->automation_state ();
298 switch (state & (Off|Play|Touch|Write)) {
300 auto_button.set_label (_("Manual"));
302 ignore_state_request = true;
303 auto_off_item->set_active (true);
304 auto_play_item->set_active (false);
305 auto_touch_item->set_active (false);
306 auto_write_item->set_active (false);
307 ignore_state_request = false;
311 auto_button.set_label (_("Play"));
312 if (auto_play_item) {
313 ignore_state_request = true;
314 auto_play_item->set_active (true);
315 auto_off_item->set_active (false);
316 auto_touch_item->set_active (false);
317 auto_write_item->set_active (false);
318 ignore_state_request = false;
322 auto_button.set_label (_("Write"));
323 if (auto_write_item) {
324 ignore_state_request = true;
325 auto_write_item->set_active (true);
326 auto_off_item->set_active (false);
327 auto_play_item->set_active (false);
328 auto_touch_item->set_active (false);
329 ignore_state_request = false;
333 auto_button.set_label (_("Touch"));
334 if (auto_touch_item) {
335 ignore_state_request = true;
336 auto_touch_item->set_active (true);
337 auto_off_item->set_active (false);
338 auto_play_item->set_active (false);
339 auto_write_item->set_active (false);
340 ignore_state_request = false;
344 auto_button.set_label (_("???"));
349 /** The interpolation style of our AutomationList has changed, so update */
351 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
353 if (mode_line_item && mode_discrete_item) {
354 if (s == AutomationList::Discrete) {
355 mode_discrete_item->set_active(true);
356 mode_line_item->set_active(false);
358 mode_line_item->set_active(true);
359 mode_discrete_item->set_active(false);
364 /** A menu item has been selected to change our interpolation mode */
366 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
368 /* Tell our view's list, if we have one, otherwise tell our own.
369 * Everything else will be signalled back from that.
373 _view->set_interpolation (style);
375 _control->list()->set_interpolation (style);
380 AutomationTimeAxisView::clear_clicked ()
382 assert (_line || _view);
384 _session->begin_reversible_command (_("clear automation"));
392 _session->commit_reversible_command ();
393 _session->set_dirty ();
397 AutomationTimeAxisView::set_height (uint32_t h)
399 bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
400 uint32_t const normal = preset_height (HeightNormal);
401 bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
403 TimeAxisView* state_parent = get_parent_with_state ();
404 assert(state_parent);
405 XMLNode* xml_node = state_parent->get_automation_child_xml_node (_control->parameter());
407 TimeAxisView::set_height (h);
408 _base_rect->property_y2() = h;
411 _line->set_height(h);
414 _view->set_height(h);
415 _view->update_contents_height();
419 snprintf (buf, sizeof (buf), "%u", height);
421 xml_node->add_property ("height", buf);
424 if (changed_between_small_and_normal || first_call_to_set_height) {
426 first_call_to_set_height = false;
428 if (h >= preset_height (HeightNormal)) {
429 controls_table.remove (name_hbox);
432 if (plugname_packed) {
433 controls_table.remove (*plugname);
434 plugname_packed = false;
436 controls_table.attach (*plugname, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
437 plugname_packed = true;
438 controls_table.attach (name_hbox, 1, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
440 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
444 name_hbox.show_all ();
447 hide_button.show_all();
449 } else if (h >= preset_height (HeightSmall)) {
450 controls_table.remove (name_hbox);
452 if (plugname_packed) {
453 controls_table.remove (*plugname);
454 plugname_packed = false;
457 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
458 controls_table.hide_all ();
461 name_hbox.show_all ();
466 } else if (h >= preset_height (HeightNormal)) {
467 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
471 if (canvas_item_visible (_canvas_display)) {
472 /* only emit the signal if the height really changed and we were visible */
473 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
479 AutomationTimeAxisView::set_samples_per_unit (double spu)
481 TimeAxisView::set_samples_per_unit (spu);
487 _view->set_samples_per_unit (spu);
491 AutomationTimeAxisView::hide_clicked ()
493 // LAME fix for refreshing the hide button
494 hide_button.set_sensitive(false);
496 set_marked_for_display (false);
499 hide_button.set_sensitive(true);
503 AutomationTimeAxisView::build_display_menu ()
505 using namespace Menu_Helpers;
509 TimeAxisView::build_display_menu ();
511 /* now fill it with our stuff */
513 MenuList& items = display_menu->items();
515 items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
516 items.push_back (SeparatorElem());
517 items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
518 items.push_back (SeparatorElem());
522 Menu* auto_state_menu = manage (new Menu);
523 auto_state_menu->set_name ("ArdourContextMenu");
524 MenuList& as_items = auto_state_menu->items();
526 as_items.push_back (CheckMenuElem (_("Manual"), sigc::bind (
527 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
529 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
531 as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
532 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
534 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
536 as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
537 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
538 (AutoState) Write)));
539 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
541 as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
542 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
543 (AutoState) Touch)));
544 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
546 items.push_back (MenuElem (_("State"), *auto_state_menu));
550 /* current interpolation state */
551 AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
553 if (EventTypeMap::instance().is_midi_parameter(_control->parameter())) {
555 Menu* auto_mode_menu = manage (new Menu);
556 auto_mode_menu->set_name ("ArdourContextMenu");
557 MenuList& am_items = auto_mode_menu->items();
559 RadioMenuItem::Group group;
561 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
562 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
563 AutomationList::Discrete)));
564 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
565 mode_discrete_item->set_active (s == AutomationList::Discrete);
567 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
568 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
569 AutomationList::Linear)));
570 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
571 mode_line_item->set_active (s == AutomationList::Linear);
573 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
576 /* make sure the automation menu state is correct */
578 automation_state_changed ();
579 interpolation_changed (s);
583 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, nframes_t when, double y)
590 _canvas_display->w2i (x, y);
592 /* compute vertical fractional position */
594 y = 1.0 - (y / height);
598 _line->view_to_model_coord (x, y);
600 _session->begin_reversible_command (_("add automation event"));
601 XMLNode& before = _control->alist()->get_state();
603 _control->alist()->add (when, y);
605 XMLNode& after = _control->alist()->get_state();
606 _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList>(*_control->alist(), &before, &after));
608 _session->set_dirty ();
612 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
614 return (_line ? cut_copy_clear_one (*_line, selection, op) : false);
618 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
620 boost::shared_ptr<Evoral::ControlList> what_we_got;
621 boost::shared_ptr<AutomationList> alist (line.the_list());
624 XMLNode &before = alist->get_state();
628 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
629 _editor.get_cut_buffer().add (what_we_got);
630 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
635 if ((what_we_got = alist->copy (selection.time.front().start, selection.time.front().end)) != 0) {
636 _editor.get_cut_buffer().add (what_we_got);
641 if ((what_we_got = alist->cut (selection.time.front().start, selection.time.front().end)) != 0) {
642 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
649 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
650 double when = (*x)->when;
651 double val = (*x)->value;
652 line.model_to_view_coord (when, val);
662 AutomationTimeAxisView::reset_objects (PointSelection& selection)
664 reset_objects_one (*_line, selection);
668 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
670 boost::shared_ptr<AutomationList> alist(line.the_list());
672 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
674 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
676 if ((*i).track != this) {
680 alist->reset_range ((*i).start, (*i).end);
685 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
687 return cut_copy_clear_objects_one (*_line, selection, op);
691 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
693 boost::shared_ptr<Evoral::ControlList> what_we_got;
694 boost::shared_ptr<AutomationList> alist(line.the_list());
697 XMLNode &before = alist->get_state();
699 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
701 if ((*i).track != this) {
707 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
708 _editor.get_cut_buffer().add (what_we_got);
709 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
714 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
715 _editor.get_cut_buffer().add (what_we_got);
720 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
721 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
731 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
732 double when = (*x)->when;
733 double val = (*x)->value;
734 line.model_to_view_coord (when, val);
744 AutomationTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth)
746 return paste_one (*_line, pos, times, selection, nth);
750 AutomationTimeAxisView::paste_one (AutomationLine& line, nframes_t pos, float times, Selection& selection, size_t nth)
752 AutomationSelection::iterator p;
753 boost::shared_ptr<AutomationList> alist(line.the_list());
755 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
757 if (p == selection.lines.end()) {
761 /* Make a copy of the list because we have to scale the
762 values from view coordinates to model coordinates, and we're
763 not supposed to modify the points in the selection.
766 AutomationList copy (**p);
768 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
769 double when = (*x)->when;
770 double val = (*x)->value;
771 line.view_to_model_coord (when, val);
776 XMLNode &before = alist->get_state();
777 alist->paste (copy, pos, times);
778 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
784 AutomationTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list<Selectable*>& results)
786 if (_line && touched (top, bot)) {
790 /* remember: this is X Window - coordinate space starts in upper left and moves down.
791 _y_position is the "origin" or "top" of the track.
794 double mybot = _y_position + height;
796 if (_y_position >= top && mybot <= bot) {
798 /* _y_position is below top, mybot is above bot, so we're fully
807 /* top and bot are within _y_position .. mybot */
809 topfrac = 1.0 - ((top - _y_position) / height);
810 botfrac = 1.0 - ((bot - _y_position) / height);
814 _line->get_selectables (start, end, botfrac, topfrac, results);
819 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
822 _line->get_inverted_selectables (sel, result);
826 AutomationTimeAxisView::set_selected_points (PointSelection& points)
829 _line->set_selected_points (points);
834 AutomationTimeAxisView::clear_lines ()
837 _list_connections.drop_connections ();
841 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
845 assert(line->the_list() == _control->list());
847 _control->alist()->automation_state_changed.connect (
848 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
851 _control->alist()->InterpolationChanged.connect (
852 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
856 //_controller = AutomationController::create(_session, line->the_list(), _control);
858 line->set_height (height);
860 /* pick up the current state */
861 automation_state_changed ();
867 AutomationTimeAxisView::entered()
870 _line->track_entered();
874 AutomationTimeAxisView::exited ()
877 _line->track_exited();
881 AutomationTimeAxisView::color_handler ()
889 AutomationTimeAxisView::set_state (const XMLNode& node, int version)
891 TimeAxisView::set_state (node, version);
893 XMLProperty const * type = node.property ("automation-id");
894 if (type && type->value () == ARDOUR::EventTypeMap::instance().to_symbol (_control->parameter())) {
895 XMLProperty const * shown = node.property ("shown");
896 if (shown && shown->value () == "yes") {
897 set_marked_for_display (true);
898 _canvas_display->show (); /* FIXME: necessary? show_at? */
902 if (!_marked_for_display) {
910 AutomationTimeAxisView::get_state_node ()
912 TimeAxisView* state_parent = get_parent_with_state ();
915 return state_parent->get_automation_child_xml_node (_control->parameter());
922 AutomationTimeAxisView::update_extra_xml_shown (bool editor_shown)
924 XMLNode* xml_node = get_state_node();
926 xml_node->add_property ("shown", editor_shown ? "yes" : "no");
931 AutomationTimeAxisView::show_at (double y, int& nth, Gtk::VBox *parent)
933 update_extra_xml_shown (true);
935 return TimeAxisView::show_at (y, nth, parent);
939 AutomationTimeAxisView::hide ()
941 update_extra_xml_shown (false);
943 TimeAxisView::hide ();
947 AutomationTimeAxisView::set_visibility (bool yn)
949 bool changed = TimeAxisView::set_visibility (yn);
952 get_state_node()->add_property ("shown", yn ? X_("yes") : X_("no"));
958 /** @return true if this view has any automation data to display */
960 AutomationTimeAxisView::has_automation () const
962 return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );