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 (_("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 (_("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 (_("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 (ArdourCanvas::Item* /*item*/, 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 _session->begin_reversible_command (_("add automation event"));
582 XMLNode& before = list->get_state();
586 XMLNode& after = list->get_state();
587 _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList> (*list, &before, &after));
588 _session->set_dirty ();
592 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
594 list<boost::shared_ptr<AutomationLine> > lines;
596 lines.push_back (_line);
598 lines = _view->get_lines ();
601 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
602 cut_copy_clear_one (**i, selection, op);
607 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
609 boost::shared_ptr<Evoral::ControlList> what_we_got;
610 boost::shared_ptr<AutomationList> alist (line.the_list());
612 XMLNode &before = alist->get_state();
614 /* convert time selection to automation list model coordinates */
615 const Evoral::TimeConverter<double, ARDOUR::framepos_t>& tc = line.time_converter ();
616 double const start = tc.from (selection.time.front().start - tc.origin_b ());
617 double const end = tc.from (selection.time.front().end - tc.origin_b ());
621 if (alist->cut (start, end) != 0) {
622 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
628 if ((what_we_got = alist->cut (start, end)) != 0) {
629 _editor.get_cut_buffer().add (what_we_got);
630 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
634 if ((what_we_got = alist->copy (start, end)) != 0) {
635 _editor.get_cut_buffer().add (what_we_got);
640 if ((what_we_got = alist->cut (start, end)) != 0) {
641 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
647 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
648 double when = (*x)->when;
649 double val = (*x)->value;
650 line.model_to_view_coord (when, val);
658 AutomationTimeAxisView::reset_objects (PointSelection& selection)
660 list<boost::shared_ptr<AutomationLine> > lines;
662 lines.push_back (_line);
664 lines = _view->get_lines ();
667 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
668 reset_objects_one (**i, selection);
673 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
675 boost::shared_ptr<AutomationList> alist(line.the_list());
677 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
679 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
681 if ((*i).track != this) {
685 alist->reset_range ((*i).start, (*i).end);
690 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
692 list<boost::shared_ptr<AutomationLine> > lines;
694 lines.push_back (_line);
696 lines = _view->get_lines ();
699 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
700 cut_copy_clear_objects_one (**i, selection, op);
705 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
707 boost::shared_ptr<Evoral::ControlList> what_we_got;
708 boost::shared_ptr<AutomationList> alist(line.the_list());
710 XMLNode &before = alist->get_state();
712 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
714 if ((*i).track != this) {
720 if (alist->cut ((*i).start, (*i).end) != 0) {
721 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
725 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
726 _editor.get_cut_buffer().add (what_we_got);
727 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
731 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
732 _editor.get_cut_buffer().add (what_we_got);
737 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
738 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
747 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
748 double when = (*x)->when;
749 double val = (*x)->value;
750 line.model_to_view_coord (when, val);
757 /** Paste a selection.
758 * @param pos Position to paste to (session frames).
759 * @param times Number of times to paste.
760 * @param selection Selection to paste.
761 * @param nth Index of the AutomationList within the selection to paste from.
764 AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
766 boost::shared_ptr<AutomationLine> line;
771 line = _view->paste_line (pos);
778 return paste_one (*line, pos, times, selection, nth);
782 AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float times, Selection& selection, size_t nth)
784 AutomationSelection::iterator p;
785 boost::shared_ptr<AutomationList> alist(line.the_list());
787 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
789 if (p == selection.lines.end()) {
793 /* Make a copy of the list because we have to scale the
794 values from view coordinates to model coordinates, and we're
795 not supposed to modify the points in the selection.
798 AutomationList copy (**p);
800 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
801 double when = (*x)->when;
802 double val = (*x)->value;
803 line.view_to_model_coord (when, val);
808 double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ());
810 XMLNode &before = alist->get_state();
811 alist->paste (copy, model_pos, times);
812 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
818 AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
820 if (!_line && !_view) {
824 if (touched (top, bot)) {
826 /* remember: this is X Window - coordinate space starts in upper left and moves down.
827 _y_position is the "origin" or "top" of the track.
830 /* bottom of our track */
831 double const mybot = _y_position + height;
836 if (_y_position >= top && mybot <= bot) {
838 /* _y_position is below top, mybot is above bot, so we're fully
847 /* top and bot are within _y_position .. mybot */
849 topfrac = 1.0 - ((top - _y_position) / height);
850 botfrac = 1.0 - ((bot - _y_position) / height);
855 _line->get_selectables (start, end, botfrac, topfrac, results);
857 _view->get_selectables (start, end, botfrac, topfrac, results);
863 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
866 _line->get_inverted_selectables (sel, result);
871 AutomationTimeAxisView::set_selected_points (PointSelection& points)
874 _line->set_selected_points (points);
876 _view->set_selected_points (points);
881 AutomationTimeAxisView::clear_lines ()
884 _list_connections.drop_connections ();
888 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
893 assert(line->the_list() == _control->list());
895 _control->alist()->automation_state_changed.connect (
896 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
899 _control->alist()->InterpolationChanged.connect (
900 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
906 line->set_height (height);
908 /* pick up the current state */
909 automation_state_changed ();
915 AutomationTimeAxisView::entered()
918 _line->track_entered();
923 AutomationTimeAxisView::exited ()
926 _line->track_exited();
931 AutomationTimeAxisView::color_handler ()
939 AutomationTimeAxisView::set_state_2X (const XMLNode& node, int /*version*/)
941 if (node.name() == X_("gain") && _parameter == Evoral::Parameter (GainAutomation)) {
942 XMLProperty const * shown = node.property (X_("shown"));
944 bool yn = string_is_affirmative (shown->value ());
946 _canvas_display->show (); /* FIXME: necessary? show_at? */
948 set_gui_property ("visible", yn);
950 set_gui_property ("visible", false);
958 AutomationTimeAxisView::set_state (const XMLNode&, int /*version*/)
964 AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr<Automatable>& automatable, set<Evoral::Parameter>& visible)
966 /* this keeps "knowledge" of how we store visibility information
967 in XML private to this class.
970 assert (automatable);
972 Automatable::Controls& controls (automatable->controls());
974 for (Automatable::Controls::iterator i = controls.begin(); i != controls.end(); ++i) {
976 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
980 const XMLNode* gui_node = ac->extra_xml ("GUI");
983 const XMLProperty* prop = gui_node->property ("shown");
985 if (string_is_affirmative (prop->value())) {
986 visible.insert (i->first);
995 /** @return true if this view has any automation data to display */
997 AutomationTimeAxisView::has_automation () const
999 return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
1002 list<boost::shared_ptr<AutomationLine> >
1003 AutomationTimeAxisView::lines () const
1005 list<boost::shared_ptr<AutomationLine> > lines;
1008 lines.push_back (_line);
1010 lines = _view->get_lines ();
1017 AutomationTimeAxisView::state_id() const
1020 return string_compose ("automation %1", _control->id().to_s());
1022 assert (_parameter);
1023 return string_compose ("automation %1 %2/%3/%4",
1027 (int) _parameter.channel());
1031 /** Given a state id string, see if it is one generated by
1032 * this class. If so, parse it into its components.
1033 * @param state_id State ID string to parse.
1034 * @param route_id Filled in with the route's ID if the state ID string is parsed.
1035 * @param has_parameter Filled in with true if the state ID has a parameter, otherwise false.
1036 * @param parameter Filled in with the state ID's parameter, if it has one.
1037 * @return true if this is a state ID generated by this class, otherwise false.
1041 AutomationTimeAxisView::parse_state_id (
1042 string const & state_id,
1044 bool & has_parameter,
1045 Evoral::Parameter & parameter)
1053 if (a != X_("automation")) {
1057 route_id = PBD::ID (b);
1060 has_parameter = false;
1064 has_parameter = true;
1067 boost::split (p, c, boost::is_any_of ("/"));
1069 assert (p.size() == 3);
1071 parameter = Evoral::Parameter (
1072 boost::lexical_cast<int> (p[0]),
1073 boost::lexical_cast<int> (p[2]),
1074 boost::lexical_cast<int> (p[1])