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 <gtkmm2ext/utils.h>
24 #include "pbd/memento_command.h"
25 #include "pbd/stacktrace.h"
27 #include "ardour/automation_control.h"
28 #include "ardour/event_type_map.h"
29 #include "ardour/route.h"
30 #include "ardour/session.h"
32 #include "ardour_ui.h"
33 #include "automation_time_axis.h"
34 #include "automation_streamview.h"
35 #include "global_signals.h"
36 #include "gui_thread.h"
37 #include "route_time_axis.h"
38 #include "automation_line.h"
39 #include "public_editor.h"
40 #include "simplerect.h"
41 #include "selection.h"
42 #include "rgb_macros.h"
43 #include "point_selection.h"
44 #include "canvas_impl.h"
50 using namespace ARDOUR;
53 using namespace Gtkmm2ext;
54 using namespace Editing;
56 Pango::FontDescription AutomationTimeAxisView::name_font;
57 bool AutomationTimeAxisView::have_name_font = false;
60 /** \a a the automatable object this time axis is to display data for.
61 * For route/track automation (e.g. gain) pass the route for both \r and \a.
62 * For route child (e.g. plugin) automation, pass the child for \a.
63 * For region automation (e.g. MIDI CC), pass null for \a.
65 AutomationTimeAxisView::AutomationTimeAxisView (
67 boost::shared_ptr<Route> r,
68 boost::shared_ptr<Automatable> a,
69 boost::shared_ptr<AutomationControl> c,
74 ArdourCanvas::Canvas& canvas,
76 const string & nomparent
79 , TimeAxisView (s, e, &parent, canvas)
85 , _view (show_regions ? new AutomationStreamView (*this) : 0)
87 , auto_button (X_("")) /* force addition of a label */
89 if (!have_name_font) {
90 name_font = get_font_for_style (X_("AutomationTrackName"));
91 have_name_font = true;
94 if (_automatable && _control) {
95 _controller = AutomationController::create (_automatable, _control->parameter(), _control);
103 mode_discrete_item = 0;
106 ignore_state_request = false;
107 first_call_to_set_height = true;
109 _base_rect = new SimpleRect(*_canvas_display);
110 _base_rect->property_x1() = 0.0;
111 _base_rect->property_y1() = 0.0;
112 /** gnomecanvas sometimes converts this value to int or adds 2 to it, so it must be
113 set correctly to avoid overflow.
115 _base_rect->property_x2() = INT_MAX - 2;
116 _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
118 /* outline ends and bottom */
119 _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
120 _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
122 _base_rect->set_data ("trackview", this);
124 _base_rect->signal_event().connect (sigc::bind (
125 sigc::mem_fun (_editor, &PublicEditor::canvas_automation_track_event),
129 _base_rect->lower_to_bottom();
132 hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
134 auto_button.set_name ("TrackVisualButton");
135 hide_button.set_name ("TrackRemoveButton");
137 auto_button.unset_flags (Gtk::CAN_FOCUS);
138 hide_button.unset_flags (Gtk::CAN_FOCUS);
140 controls_table.set_no_show_all();
142 ARDOUR_UI::instance()->set_tip(auto_button, _("automation state"));
143 ARDOUR_UI::instance()->set_tip(hide_button, _("hide track"));
145 string str = gui_property ("height");
147 set_height (atoi (str));
149 set_height (preset_height (HeightNormal));
152 /* rearrange the name display */
154 controls_table.remove (name_hbox);
155 controls_table.attach (name_hbox, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
157 /* we never show these for automation tracks, so make
158 life easier and remove them.
163 name_label.set_text (_name);
164 name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
165 name_label.set_name (X_("TrackParameterName"));
166 name_label.set_ellipsize (Pango::ELLIPSIZE_END);
168 string tipname = nomparent;
169 if (!tipname.empty()) {
173 ARDOUR_UI::instance()->set_tip(controls_ebox, tipname);
175 /* add the buttons */
176 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
177 controls_table.attach (auto_button, 6, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
180 /* add bar controller */
181 controls_table.attach (*_controller.get(), 0, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
184 controls_table.show_all ();
186 hide_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
187 auto_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
189 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
190 controls_base_unselected_name = X_("AutomationTrackControlsBase");
191 controls_ebox.set_name (controls_base_unselected_name);
193 /* ask for notifications of any new RegionViews */
200 /* no regions, just a single line for the entire track (e.g. bus gain) */
204 boost::shared_ptr<AutomationLine> line (
206 ARDOUR::EventTypeMap::instance().to_symbol(_parameter),
213 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
214 line->queue_reset ();
218 /* make sure labels etc. are correct */
220 automation_state_changed ();
221 ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
223 _route->DropReferences.connect (
224 _route_connections, invalidator (*this), ui_bind (&AutomationTimeAxisView::route_going_away, this), gui_context ()
228 AutomationTimeAxisView::~AutomationTimeAxisView ()
233 AutomationTimeAxisView::route_going_away ()
239 AutomationTimeAxisView::auto_clicked ()
241 using namespace Menu_Helpers;
243 if (automation_menu == 0) {
244 automation_menu = manage (new Menu);
245 automation_menu->set_name ("ArdourContextMenu");
246 MenuList& items (automation_menu->items());
248 items.push_back (MenuElem (_("Manual"), sigc::bind (sigc::mem_fun(*this,
249 &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
250 items.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
251 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
252 items.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
253 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
254 items.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
255 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
258 automation_menu->popup (1, gtk_get_current_event_time());
262 AutomationTimeAxisView::set_automation_state (AutoState state)
264 if (ignore_state_request) {
269 _automatable->set_parameter_automation_state (_parameter, state);
273 _view->set_automation_state (state);
275 /* AutomationStreamViews don't signal when their automation state changes, so handle
276 our updates `manually'.
278 automation_state_changed ();
283 AutomationTimeAxisView::automation_state_changed ()
287 /* update button label */
290 state = _view->automation_state ();
293 state = _control->alist()->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);
376 _control->list()->set_interpolation (style);
381 AutomationTimeAxisView::clear_clicked ()
383 assert (_line || _view);
385 _session->begin_reversible_command (_("clear automation"));
393 _session->commit_reversible_command ();
394 _session->set_dirty ();
398 AutomationTimeAxisView::set_height (uint32_t h)
400 bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
401 uint32_t const normal = preset_height (HeightNormal);
402 bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
404 TimeAxisView::set_height (h);
406 _base_rect->property_y2() = h;
409 _line->set_height(h);
413 _view->set_height(h);
414 _view->update_contents_height();
417 if (changed_between_small_and_normal || first_call_to_set_height) {
419 first_call_to_set_height = false;
421 if (h >= preset_height (HeightNormal)) {
424 name_hbox.show_all ();
427 hide_button.show_all();
429 } else if (h >= preset_height (HeightSmall)) {
430 controls_table.hide_all ();
433 name_hbox.show_all ();
438 } else if (h >= preset_height (HeightNormal)) {
439 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
443 if (canvas_item_visible (_canvas_display) && _route) {
444 /* only emit the signal if the height really changed and we were visible */
445 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
451 AutomationTimeAxisView::set_samples_per_unit (double spu)
453 TimeAxisView::set_samples_per_unit (spu);
460 _view->set_samples_per_unit (spu);
465 AutomationTimeAxisView::hide_clicked ()
467 hide_button.set_sensitive(false);
468 set_marked_for_display (false);
469 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(parent);
471 rtv->request_redraw ();
473 hide_button.set_sensitive(true);
477 AutomationTimeAxisView::build_display_menu ()
479 using namespace Menu_Helpers;
483 TimeAxisView::build_display_menu ();
485 /* now fill it with our stuff */
487 MenuList& items = display_menu->items();
489 items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
490 items.push_back (SeparatorElem());
491 items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
492 items.push_back (SeparatorElem());
496 Menu* auto_state_menu = manage (new Menu);
497 auto_state_menu->set_name ("ArdourContextMenu");
498 MenuList& as_items = auto_state_menu->items();
500 as_items.push_back (CheckMenuElem (_("Manual"), sigc::bind (
501 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
503 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
505 as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
506 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
508 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
510 as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
511 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
512 (AutoState) Write)));
513 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
515 as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
516 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
517 (AutoState) Touch)));
518 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
520 items.push_back (MenuElem (_("State"), *auto_state_menu));
524 /* current interpolation state */
525 AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
527 if (EventTypeMap::instance().is_midi_parameter(_parameter)) {
529 Menu* auto_mode_menu = manage (new Menu);
530 auto_mode_menu->set_name ("ArdourContextMenu");
531 MenuList& am_items = auto_mode_menu->items();
533 RadioMenuItem::Group group;
535 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
536 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
537 AutomationList::Discrete)));
538 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
539 mode_discrete_item->set_active (s == AutomationList::Discrete);
541 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
542 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
543 AutomationList::Linear)));
544 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
545 mode_line_item->set_active (s == AutomationList::Linear);
547 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
550 /* make sure the automation menu state is correct */
552 automation_state_changed ();
553 interpolation_changed (s);
557 AutomationTimeAxisView::add_automation_event (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/, framepos_t when, double y)
565 _canvas_display->w2i (x, y);
567 /* compute vertical fractional position */
569 y = 1.0 - (y / height);
573 _line->view_to_model_coord (x, y);
575 boost::shared_ptr<AutomationList> list = _line->the_list ();
577 _session->begin_reversible_command (_("add automation event"));
578 XMLNode& before = list->get_state();
582 XMLNode& after = list->get_state();
583 _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList> (*list, &before, &after));
584 _session->set_dirty ();
588 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
590 list<boost::shared_ptr<AutomationLine> > lines;
592 lines.push_back (_line);
594 lines = _view->get_lines ();
597 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
598 cut_copy_clear_one (**i, selection, op);
603 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
605 boost::shared_ptr<Evoral::ControlList> what_we_got;
606 boost::shared_ptr<AutomationList> alist (line.the_list());
608 XMLNode &before = alist->get_state();
610 /* convert time selection to automation list model coordinates */
611 const Evoral::TimeConverter<double, ARDOUR::framepos_t>& tc = line.time_converter ();
612 double const start = tc.from (selection.time.front().start - tc.origin_b ());
613 double const end = tc.from (selection.time.front().end - tc.origin_b ());
617 if (alist->cut (start, end) != 0) {
618 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
624 if ((what_we_got = alist->cut (start, end)) != 0) {
625 _editor.get_cut_buffer().add (what_we_got);
626 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
630 if ((what_we_got = alist->copy (start, end)) != 0) {
631 _editor.get_cut_buffer().add (what_we_got);
636 if ((what_we_got = alist->cut (start, end)) != 0) {
637 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
643 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
644 double when = (*x)->when;
645 double val = (*x)->value;
646 line.model_to_view_coord (when, val);
654 AutomationTimeAxisView::reset_objects (PointSelection& selection)
656 list<boost::shared_ptr<AutomationLine> > lines;
658 lines.push_back (_line);
660 lines = _view->get_lines ();
663 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
664 reset_objects_one (**i, selection);
669 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
671 boost::shared_ptr<AutomationList> alist(line.the_list());
673 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
675 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
677 if ((*i).track != this) {
681 alist->reset_range ((*i).start, (*i).end);
686 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
688 list<boost::shared_ptr<AutomationLine> > lines;
690 lines.push_back (_line);
692 lines = _view->get_lines ();
695 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
696 cut_copy_clear_objects_one (**i, selection, op);
701 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
703 boost::shared_ptr<Evoral::ControlList> what_we_got;
704 boost::shared_ptr<AutomationList> alist(line.the_list());
706 XMLNode &before = alist->get_state();
708 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
710 if ((*i).track != this) {
716 if (alist->cut ((*i).start, (*i).end) != 0) {
717 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
721 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
722 _editor.get_cut_buffer().add (what_we_got);
723 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
727 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
728 _editor.get_cut_buffer().add (what_we_got);
733 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
734 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
743 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
744 double when = (*x)->when;
745 double val = (*x)->value;
746 line.model_to_view_coord (when, val);
753 /** Paste a selection.
754 * @param pos Position to paste to (session frames).
755 * @param times Number of times to paste.
756 * @param selection Selection to paste.
757 * @param nth Index of the AutomationList within the selection to paste from.
760 AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
762 boost::shared_ptr<AutomationLine> line;
767 line = _view->paste_line (pos);
774 return paste_one (*line, pos, times, selection, nth);
778 AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float times, Selection& selection, size_t nth)
780 AutomationSelection::iterator p;
781 boost::shared_ptr<AutomationList> alist(line.the_list());
783 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
785 if (p == selection.lines.end()) {
789 /* Make a copy of the list because we have to scale the
790 values from view coordinates to model coordinates, and we're
791 not supposed to modify the points in the selection.
794 AutomationList copy (**p);
796 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
797 double when = (*x)->when;
798 double val = (*x)->value;
799 line.view_to_model_coord (when, val);
804 double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ());
806 XMLNode &before = alist->get_state();
807 alist->paste (copy, model_pos, times);
808 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
814 AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
816 if (!_line && !_view) {
820 if (touched (top, bot)) {
822 /* remember: this is X Window - coordinate space starts in upper left and moves down.
823 _y_position is the "origin" or "top" of the track.
826 /* bottom of our track */
827 double const mybot = _y_position + height;
832 if (_y_position >= top && mybot <= bot) {
834 /* _y_position is below top, mybot is above bot, so we're fully
843 /* top and bot are within _y_position .. mybot */
845 topfrac = 1.0 - ((top - _y_position) / height);
846 botfrac = 1.0 - ((bot - _y_position) / height);
851 _line->get_selectables (start, end, botfrac, topfrac, results);
853 _view->get_selectables (start, end, botfrac, topfrac, results);
859 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
862 _line->get_inverted_selectables (sel, result);
867 AutomationTimeAxisView::set_selected_points (PointSelection& points)
870 _line->set_selected_points (points);
872 _view->set_selected_points (points);
877 AutomationTimeAxisView::clear_lines ()
880 _list_connections.drop_connections ();
884 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
889 assert(line->the_list() == _control->list());
891 _control->alist()->automation_state_changed.connect (
892 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
895 _control->alist()->InterpolationChanged.connect (
896 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
902 line->set_height (height);
904 /* pick up the current state */
905 automation_state_changed ();
911 AutomationTimeAxisView::entered()
914 _line->track_entered();
919 AutomationTimeAxisView::exited ()
922 _line->track_exited();
927 AutomationTimeAxisView::color_handler ()
935 AutomationTimeAxisView::set_state_2X (const XMLNode& node, int /*version*/)
937 if (node.name() == X_("gain") && _parameter == Evoral::Parameter (GainAutomation)) {
938 XMLProperty const * shown = node.property (X_("shown"));
940 bool yn = string_is_affirmative (shown->value ());
942 _canvas_display->show (); /* FIXME: necessary? show_at? */
944 set_gui_property ("visible", (yn ? "yes" : "no"));
946 set_gui_property ("visible", "no");
954 AutomationTimeAxisView::set_state (const XMLNode& node, int /*version*/)
960 AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr<Automatable>& automatable, set<Evoral::Parameter>& visible)
962 /* this keeps "knowledge" of how we store visibility information
963 in XML private to this class.
966 assert (automatable);
968 Automatable::Controls& controls (automatable->controls());
970 for (Automatable::Controls::iterator i = controls.begin(); i != controls.end(); ++i) {
972 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
976 const XMLNode* gui_node = ac->extra_xml ("GUI");
979 const XMLProperty* prop = gui_node->property ("shown");
981 if (string_is_affirmative (prop->value())) {
982 visible.insert (i->first);
991 /** @return true if this view has any automation data to display */
993 AutomationTimeAxisView::has_automation () const
995 return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
998 list<boost::shared_ptr<AutomationLine> >
999 AutomationTimeAxisView::lines () const
1001 list<boost::shared_ptr<AutomationLine> > lines;
1004 lines.push_back (_line);
1006 lines = _view->get_lines ();
1013 AutomationTimeAxisView::state_id() const
1016 return string_compose ("automation %1", _control->id().to_s());
1018 assert (_parameter);
1019 return string_compose ("automation %1 %2/%3/%4",
1023 _parameter.channel());