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>
23 #include <boost/algorithm/string.hpp>
24 #include <boost/lexical_cast.hpp>
26 #include "pbd/memento_command.h"
27 #include "pbd/stacktrace.h"
29 #include "ardour/automation_control.h"
30 #include "ardour/event_type_map.h"
31 #include "ardour/route.h"
32 #include "ardour/session.h"
34 #include "ardour_ui.h"
35 #include "automation_time_axis.h"
36 #include "automation_streamview.h"
37 #include "global_signals.h"
38 #include "gui_thread.h"
39 #include "route_time_axis.h"
40 #include "automation_line.h"
41 #include "public_editor.h"
42 #include "simplerect.h"
43 #include "selection.h"
44 #include "rgb_macros.h"
45 #include "point_selection.h"
46 #include "canvas_impl.h"
52 using namespace ARDOUR;
55 using namespace Gtkmm2ext;
56 using namespace Editing;
58 Pango::FontDescription AutomationTimeAxisView::name_font;
59 bool AutomationTimeAxisView::have_name_font = false;
62 /** \a a the automatable object this time axis is to display data for.
63 * For route/track automation (e.g. gain) pass the route for both \r and \a.
64 * For route child (e.g. plugin) automation, pass the child for \a.
65 * For region automation (e.g. MIDI CC), pass null for \a.
67 AutomationTimeAxisView::AutomationTimeAxisView (
69 boost::shared_ptr<Route> r,
70 boost::shared_ptr<Automatable> a,
71 boost::shared_ptr<AutomationControl> c,
76 ArdourCanvas::Canvas& canvas,
78 const string & nomparent
81 , TimeAxisView (s, e, &parent, canvas)
87 , _view (show_regions ? new AutomationStreamView (*this) : 0)
89 , auto_button (X_("")) /* force addition of a label */
90 , _show_regions (show_regions)
92 if (!have_name_font) {
93 name_font = get_font_for_style (X_("AutomationTrackName"));
94 have_name_font = true;
97 if (_automatable && _control) {
98 _controller = AutomationController::create (_automatable, _control->parameter(), _control);
106 mode_discrete_item = 0;
109 ignore_state_request = false;
110 first_call_to_set_height = true;
112 _base_rect = new SimpleRect(*_canvas_display);
113 _base_rect->property_x1() = 0.0;
114 _base_rect->property_y1() = 0.0;
115 /** gnomecanvas sometimes converts this value to int or adds 2 to it, so it must be
116 set correctly to avoid overflow.
118 _base_rect->property_x2() = INT_MAX - 2;
119 _base_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackOutline.get();
121 /* outline ends and bottom */
122 _base_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);
123 _base_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AutomationTrackFill.get();
125 _base_rect->set_data ("trackview", this);
127 _base_rect->signal_event().connect (sigc::bind (
128 sigc::mem_fun (_editor, &PublicEditor::canvas_automation_track_event),
132 _base_rect->lower_to_bottom();
135 hide_button.add (*(manage (new Gtk::Image (::get_icon("hide")))));
137 auto_button.set_name ("TrackVisualButton");
138 hide_button.set_name ("TrackRemoveButton");
140 auto_button.unset_flags (Gtk::CAN_FOCUS);
141 hide_button.unset_flags (Gtk::CAN_FOCUS);
143 controls_table.set_no_show_all();
145 ARDOUR_UI::instance()->set_tip(auto_button, _("automation state"));
146 ARDOUR_UI::instance()->set_tip(hide_button, _("hide track"));
148 string str = gui_property ("height");
150 set_height (atoi (str));
152 set_height (preset_height (HeightNormal));
155 /* rearrange the name display */
157 controls_table.remove (name_hbox);
158 controls_table.attach (name_hbox, 1, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
160 /* we never show these for automation tracks, so make
161 life easier and remove them.
166 name_label.set_text (_name);
167 name_label.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
168 name_label.set_name (X_("TrackParameterName"));
169 name_label.set_ellipsize (Pango::ELLIPSIZE_END);
171 string tipname = nomparent;
172 if (!tipname.empty()) {
176 ARDOUR_UI::instance()->set_tip(controls_ebox, tipname);
178 /* add the buttons */
179 controls_table.attach (hide_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
180 controls_table.attach (auto_button, 6, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
183 /* add bar controller */
184 controls_table.attach (*_controller.get(), 0, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
187 controls_table.show_all ();
189 hide_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
190 auto_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
192 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
193 controls_base_unselected_name = X_("AutomationTrackControlsBase");
194 controls_ebox.set_name (controls_base_unselected_name);
196 /* ask for notifications of any new RegionViews */
203 /* no regions, just a single line for the entire track (e.g. bus gain) */
207 boost::shared_ptr<AutomationLine> line (
209 ARDOUR::EventTypeMap::instance().to_symbol(_parameter),
216 line->set_line_color (ARDOUR_UI::config()->canvasvar_ProcessorAutomationLine.get());
217 line->queue_reset ();
221 /* make sure labels etc. are correct */
223 automation_state_changed ();
224 ColorsChanged.connect (sigc::mem_fun (*this, &AutomationTimeAxisView::color_handler));
226 _route->DropReferences.connect (
227 _route_connections, invalidator (*this), ui_bind (&AutomationTimeAxisView::route_going_away, this), gui_context ()
231 AutomationTimeAxisView::~AutomationTimeAxisView ()
237 AutomationTimeAxisView::route_going_away ()
243 AutomationTimeAxisView::auto_clicked ()
245 using namespace Menu_Helpers;
247 if (automation_menu == 0) {
248 automation_menu = manage (new Menu);
249 automation_menu->set_name ("ArdourContextMenu");
250 MenuList& items (automation_menu->items());
252 items.push_back (MenuElem (S_("Automation|Manual"), sigc::bind (sigc::mem_fun(*this,
253 &AutomationTimeAxisView::set_automation_state), (AutoState) Off)));
254 items.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
255 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
256 items.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
257 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
258 items.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
259 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
262 automation_menu->popup (1, gtk_get_current_event_time());
266 AutomationTimeAxisView::set_automation_state (AutoState state)
268 if (ignore_state_request) {
273 _automatable->set_parameter_automation_state (_parameter, state);
277 _view->set_automation_state (state);
279 /* AutomationStreamViews don't signal when their automation state changes, so handle
280 our updates `manually'.
282 automation_state_changed ();
287 AutomationTimeAxisView::automation_state_changed ()
291 /* update button label */
294 state = _view->automation_state ();
297 state = _control->alist()->automation_state ();
302 switch (state & (Off|Play|Touch|Write)) {
304 auto_button.set_label (S_("Automation|Manual"));
306 ignore_state_request = true;
307 auto_off_item->set_active (true);
308 auto_play_item->set_active (false);
309 auto_touch_item->set_active (false);
310 auto_write_item->set_active (false);
311 ignore_state_request = false;
315 auto_button.set_label (_("Play"));
316 if (auto_play_item) {
317 ignore_state_request = true;
318 auto_play_item->set_active (true);
319 auto_off_item->set_active (false);
320 auto_touch_item->set_active (false);
321 auto_write_item->set_active (false);
322 ignore_state_request = false;
326 auto_button.set_label (_("Write"));
327 if (auto_write_item) {
328 ignore_state_request = true;
329 auto_write_item->set_active (true);
330 auto_off_item->set_active (false);
331 auto_play_item->set_active (false);
332 auto_touch_item->set_active (false);
333 ignore_state_request = false;
337 auto_button.set_label (_("Touch"));
338 if (auto_touch_item) {
339 ignore_state_request = true;
340 auto_touch_item->set_active (true);
341 auto_off_item->set_active (false);
342 auto_play_item->set_active (false);
343 auto_write_item->set_active (false);
344 ignore_state_request = false;
348 auto_button.set_label (_("???"));
353 /** The interpolation style of our AutomationList has changed, so update */
355 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
357 if (mode_line_item && mode_discrete_item) {
358 if (s == AutomationList::Discrete) {
359 mode_discrete_item->set_active(true);
360 mode_line_item->set_active(false);
362 mode_line_item->set_active(true);
363 mode_discrete_item->set_active(false);
368 /** A menu item has been selected to change our interpolation mode */
370 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
372 /* Tell our view's list, if we have one, otherwise tell our own.
373 * Everything else will be signalled back from that.
377 _view->set_interpolation (style);
380 _control->list()->set_interpolation (style);
385 AutomationTimeAxisView::clear_clicked ()
387 assert (_line || _view);
389 _session->begin_reversible_command (_("clear automation"));
397 _session->commit_reversible_command ();
398 _session->set_dirty ();
402 AutomationTimeAxisView::set_height (uint32_t h)
404 bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
405 uint32_t const normal = preset_height (HeightNormal);
406 bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
408 TimeAxisView::set_height (h);
410 _base_rect->property_y2() = h;
413 _line->set_height(h);
417 _view->set_height(h);
418 _view->update_contents_height();
421 if (changed_between_small_and_normal || first_call_to_set_height) {
423 first_call_to_set_height = false;
425 if (h >= preset_height (HeightNormal)) {
428 name_hbox.show_all ();
431 hide_button.show_all();
433 } else if (h >= preset_height (HeightSmall)) {
434 controls_table.hide_all ();
437 name_hbox.show_all ();
442 } else if (h >= preset_height (HeightNormal)) {
443 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
447 if (canvas_item_visible (_canvas_display) && _route) {
448 /* only emit the signal if the height really changed and we were visible */
449 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
455 AutomationTimeAxisView::set_samples_per_unit (double spu)
457 TimeAxisView::set_samples_per_unit (spu);
464 _view->set_samples_per_unit (spu);
469 AutomationTimeAxisView::hide_clicked ()
471 hide_button.set_sensitive(false);
472 set_marked_for_display (false);
473 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(parent);
475 rtv->request_redraw ();
477 hide_button.set_sensitive(true);
481 AutomationTimeAxisView::build_display_menu ()
483 using namespace Menu_Helpers;
487 TimeAxisView::build_display_menu ();
489 /* now fill it with our stuff */
491 MenuList& items = display_menu->items();
493 items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
494 items.push_back (SeparatorElem());
495 items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
496 items.push_back (SeparatorElem());
500 Menu* auto_state_menu = manage (new Menu);
501 auto_state_menu->set_name ("ArdourContextMenu");
502 MenuList& as_items = auto_state_menu->items();
504 as_items.push_back (CheckMenuElem (S_("Automation|Manual"), sigc::bind (
505 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
507 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
509 as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
510 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
512 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
514 as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
515 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
516 (AutoState) Write)));
517 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
519 as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
520 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
521 (AutoState) Touch)));
522 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
524 items.push_back (MenuElem (_("State"), *auto_state_menu));
528 /* current interpolation state */
529 AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
531 if (EventTypeMap::instance().is_midi_parameter(_parameter)) {
533 Menu* auto_mode_menu = manage (new Menu);
534 auto_mode_menu->set_name ("ArdourContextMenu");
535 MenuList& am_items = auto_mode_menu->items();
537 RadioMenuItem::Group group;
539 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
540 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
541 AutomationList::Discrete)));
542 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
543 mode_discrete_item->set_active (s == AutomationList::Discrete);
545 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
546 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
547 AutomationList::Linear)));
548 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
549 mode_line_item->set_active (s == AutomationList::Linear);
551 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
554 /* make sure the automation menu state is correct */
556 automation_state_changed ();
557 interpolation_changed (s);
561 AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when, double y)
569 _canvas_display->w2i (x, y);
571 /* compute vertical fractional position */
573 y = 1.0 - (y / height);
577 _line->view_to_model_coord (x, y);
579 boost::shared_ptr<AutomationList> list = _line->the_list ();
581 _editor.snap_to_with_modifier (when, event);
583 _session->begin_reversible_command (_("add automation event"));
584 XMLNode& before = list->get_state();
588 XMLNode& after = list->get_state();
589 _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList> (*list, &before, &after));
590 _session->set_dirty ();
594 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
596 list<boost::shared_ptr<AutomationLine> > lines;
598 lines.push_back (_line);
600 lines = _view->get_lines ();
603 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
604 cut_copy_clear_one (**i, selection, op);
609 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
611 boost::shared_ptr<Evoral::ControlList> what_we_got;
612 boost::shared_ptr<AutomationList> alist (line.the_list());
614 XMLNode &before = alist->get_state();
616 /* convert time selection to automation list model coordinates */
617 const Evoral::TimeConverter<double, ARDOUR::framepos_t>& tc = line.time_converter ();
618 double const start = tc.from (selection.time.front().start - tc.origin_b ());
619 double const end = tc.from (selection.time.front().end - tc.origin_b ());
623 if (alist->cut (start, end) != 0) {
624 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
630 if ((what_we_got = alist->cut (start, end)) != 0) {
631 _editor.get_cut_buffer().add (what_we_got);
632 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
636 if ((what_we_got = alist->copy (start, end)) != 0) {
637 _editor.get_cut_buffer().add (what_we_got);
642 if ((what_we_got = alist->cut (start, end)) != 0) {
643 _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);
660 AutomationTimeAxisView::reset_objects (PointSelection& selection)
662 list<boost::shared_ptr<AutomationLine> > lines;
664 lines.push_back (_line);
666 lines = _view->get_lines ();
669 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
670 reset_objects_one (**i, selection);
675 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
677 boost::shared_ptr<AutomationList> alist(line.the_list());
679 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
681 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
683 if ((*i).track != this) {
687 alist->reset_range ((*i).start, (*i).end);
692 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
694 list<boost::shared_ptr<AutomationLine> > lines;
696 lines.push_back (_line);
698 lines = _view->get_lines ();
701 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
702 cut_copy_clear_objects_one (**i, selection, op);
707 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
709 boost::shared_ptr<Evoral::ControlList> what_we_got;
710 boost::shared_ptr<AutomationList> alist(line.the_list());
712 XMLNode &before = alist->get_state();
714 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
716 if ((*i).track != this) {
722 if (alist->cut ((*i).start, (*i).end) != 0) {
723 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
727 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
728 _editor.get_cut_buffer().add (what_we_got);
729 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
733 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
734 _editor.get_cut_buffer().add (what_we_got);
739 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
740 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
749 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
750 double when = (*x)->when;
751 double val = (*x)->value;
752 line.model_to_view_coord (when, val);
759 /** Paste a selection.
760 * @param pos Position to paste to (session frames).
761 * @param times Number of times to paste.
762 * @param selection Selection to paste.
763 * @param nth Index of the AutomationList within the selection to paste from.
766 AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
768 boost::shared_ptr<AutomationLine> line;
773 line = _view->paste_line (pos);
780 return paste_one (*line, pos, times, selection, nth);
784 AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float times, Selection& selection, size_t nth)
786 AutomationSelection::iterator p;
787 boost::shared_ptr<AutomationList> alist(line.the_list());
789 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
791 if (p == selection.lines.end()) {
795 /* Make a copy of the list because we have to scale the
796 values from view coordinates to model coordinates, and we're
797 not supposed to modify the points in the selection.
800 AutomationList copy (**p);
802 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
803 double when = (*x)->when;
804 double val = (*x)->value;
805 line.view_to_model_coord (when, val);
810 double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ());
812 XMLNode &before = alist->get_state();
813 alist->paste (copy, model_pos, times);
814 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
820 AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
822 if (!_line && !_view) {
826 if (touched (top, bot)) {
828 /* remember: this is X Window - coordinate space starts in upper left and moves down.
829 _y_position is the "origin" or "top" of the track.
832 /* bottom of our track */
833 double const mybot = _y_position + height;
838 if (_y_position >= top && mybot <= bot) {
840 /* _y_position is below top, mybot is above bot, so we're fully
849 /* top and bot are within _y_position .. mybot */
851 topfrac = 1.0 - ((top - _y_position) / height);
852 botfrac = 1.0 - ((bot - _y_position) / height);
857 _line->get_selectables (start, end, botfrac, topfrac, results);
859 _view->get_selectables (start, end, botfrac, topfrac, results);
865 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
868 _line->get_inverted_selectables (sel, result);
873 AutomationTimeAxisView::set_selected_points (PointSelection& points)
876 _line->set_selected_points (points);
878 _view->set_selected_points (points);
883 AutomationTimeAxisView::clear_lines ()
886 _list_connections.drop_connections ();
890 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
895 assert(line->the_list() == _control->list());
897 _control->alist()->automation_state_changed.connect (
898 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
901 _control->alist()->InterpolationChanged.connect (
902 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
908 line->set_height (height);
910 /* pick up the current state */
911 automation_state_changed ();
917 AutomationTimeAxisView::entered()
920 _line->track_entered();
925 AutomationTimeAxisView::exited ()
928 _line->track_exited();
933 AutomationTimeAxisView::color_handler ()
941 AutomationTimeAxisView::set_state_2X (const XMLNode& node, int /*version*/)
943 if (node.name() == X_("gain") && _parameter == Evoral::Parameter (GainAutomation)) {
944 XMLProperty const * shown = node.property (X_("shown"));
946 bool yn = string_is_affirmative (shown->value ());
948 _canvas_display->show (); /* FIXME: necessary? show_at? */
950 set_gui_property ("visible", yn);
952 set_gui_property ("visible", false);
960 AutomationTimeAxisView::set_state (const XMLNode&, int /*version*/)
966 AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr<Automatable>& automatable, set<Evoral::Parameter>& visible)
968 /* this keeps "knowledge" of how we store visibility information
969 in XML private to this class.
972 assert (automatable);
974 Automatable::Controls& controls (automatable->controls());
976 for (Automatable::Controls::iterator i = controls.begin(); i != controls.end(); ++i) {
978 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
982 const XMLNode* gui_node = ac->extra_xml ("GUI");
985 const XMLProperty* prop = gui_node->property ("shown");
987 if (string_is_affirmative (prop->value())) {
988 visible.insert (i->first);
997 /** @return true if this view has any automation data to display */
999 AutomationTimeAxisView::has_automation () const
1001 return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
1004 list<boost::shared_ptr<AutomationLine> >
1005 AutomationTimeAxisView::lines () const
1007 list<boost::shared_ptr<AutomationLine> > lines;
1010 lines.push_back (_line);
1012 lines = _view->get_lines ();
1019 AutomationTimeAxisView::state_id() const
1022 return string_compose ("automation %1", _control->id().to_s());
1024 assert (_parameter);
1025 return string_compose ("automation %1 %2/%3/%4",
1029 (int) _parameter.channel());
1033 /** Given a state id string, see if it is one generated by
1034 * this class. If so, parse it into its components.
1035 * @param state_id State ID string to parse.
1036 * @param route_id Filled in with the route's ID if the state ID string is parsed.
1037 * @param has_parameter Filled in with true if the state ID has a parameter, otherwise false.
1038 * @param parameter Filled in with the state ID's parameter, if it has one.
1039 * @return true if this is a state ID generated by this class, otherwise false.
1043 AutomationTimeAxisView::parse_state_id (
1044 string const & state_id,
1046 bool & has_parameter,
1047 Evoral::Parameter & parameter)
1055 if (a != X_("automation")) {
1059 route_id = PBD::ID (b);
1062 has_parameter = false;
1066 has_parameter = true;
1069 boost::split (p, c, boost::is_any_of ("/"));
1071 assert (p.size() == 3);
1073 parameter = Evoral::Parameter (
1074 boost::lexical_cast<int> (p[0]),
1075 boost::lexical_cast<int> (p[2]),
1076 boost::lexical_cast<int> (p[1])