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);
185 /* note that this handler connects *before* the default handler */
186 _controller->event_widget().signal_scroll_event().connect (mem_fun (*this, &AutomationTimeAxisView::controls_ebox_scroll), false);
189 controls_table.show_all ();
191 hide_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked));
192 auto_button.signal_clicked().connect (sigc::mem_fun(*this, &AutomationTimeAxisView::auto_clicked));
194 controls_base_selected_name = X_("AutomationTrackControlsBaseSelected");
195 controls_base_unselected_name = X_("AutomationTrackControlsBase");
196 controls_ebox.set_name (controls_base_unselected_name);
198 /* ask for notifications of any new RegionViews */
205 /* no regions, just a single line for the entire track (e.g. bus gain) */
209 boost::shared_ptr<AutomationLine> line (
211 ARDOUR::EventTypeMap::instance().to_symbol(_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));
228 _route->DropReferences.connect (
229 _route_connections, invalidator (*this), ui_bind (&AutomationTimeAxisView::route_going_away, this), gui_context ()
233 AutomationTimeAxisView::~AutomationTimeAxisView ()
239 AutomationTimeAxisView::route_going_away ()
245 AutomationTimeAxisView::auto_clicked ()
247 using namespace Menu_Helpers;
249 if (automation_menu == 0) {
250 automation_menu = manage (new Menu);
251 automation_menu->set_name ("ArdourContextMenu");
252 MenuList& items (automation_menu->items());
254 items.push_back (MenuElem (S_("Automation|Manual"), sigc::bind (sigc::mem_fun(*this,
255 &AutomationTimeAxisView::set_automation_state), (AutoState) ARDOUR::Off)));
256 items.push_back (MenuElem (_("Play"), sigc::bind (sigc::mem_fun(*this,
257 &AutomationTimeAxisView::set_automation_state), (AutoState) Play)));
258 items.push_back (MenuElem (_("Write"), sigc::bind (sigc::mem_fun(*this,
259 &AutomationTimeAxisView::set_automation_state), (AutoState) Write)));
260 items.push_back (MenuElem (_("Touch"), sigc::bind (sigc::mem_fun(*this,
261 &AutomationTimeAxisView::set_automation_state), (AutoState) Touch)));
264 automation_menu->popup (1, gtk_get_current_event_time());
268 AutomationTimeAxisView::set_automation_state (AutoState state)
270 if (ignore_state_request) {
275 _automatable->set_parameter_automation_state (_parameter, state);
279 _view->set_automation_state (state);
281 /* AutomationStreamViews don't signal when their automation state changes, so handle
282 our updates `manually'.
284 automation_state_changed ();
289 AutomationTimeAxisView::automation_state_changed ()
293 /* update button label */
296 state = _view->automation_state ();
299 state = _control->alist()->automation_state ();
304 switch (state & (ARDOUR::Off|Play|Touch|Write)) {
306 auto_button.set_label (S_("Automation|Manual"));
308 ignore_state_request = true;
309 auto_off_item->set_active (true);
310 auto_play_item->set_active (false);
311 auto_touch_item->set_active (false);
312 auto_write_item->set_active (false);
313 ignore_state_request = false;
317 auto_button.set_label (_("Play"));
318 if (auto_play_item) {
319 ignore_state_request = true;
320 auto_play_item->set_active (true);
321 auto_off_item->set_active (false);
322 auto_touch_item->set_active (false);
323 auto_write_item->set_active (false);
324 ignore_state_request = false;
328 auto_button.set_label (_("Write"));
329 if (auto_write_item) {
330 ignore_state_request = true;
331 auto_write_item->set_active (true);
332 auto_off_item->set_active (false);
333 auto_play_item->set_active (false);
334 auto_touch_item->set_active (false);
335 ignore_state_request = false;
339 auto_button.set_label (_("Touch"));
340 if (auto_touch_item) {
341 ignore_state_request = true;
342 auto_touch_item->set_active (true);
343 auto_off_item->set_active (false);
344 auto_play_item->set_active (false);
345 auto_write_item->set_active (false);
346 ignore_state_request = false;
350 auto_button.set_label (_("???"));
355 /** The interpolation style of our AutomationList has changed, so update */
357 AutomationTimeAxisView::interpolation_changed (AutomationList::InterpolationStyle s)
359 if (mode_line_item && mode_discrete_item) {
360 if (s == AutomationList::Discrete) {
361 mode_discrete_item->set_active(true);
362 mode_line_item->set_active(false);
364 mode_line_item->set_active(true);
365 mode_discrete_item->set_active(false);
370 /** A menu item has been selected to change our interpolation mode */
372 AutomationTimeAxisView::set_interpolation (AutomationList::InterpolationStyle style)
374 /* Tell our view's list, if we have one, otherwise tell our own.
375 * Everything else will be signalled back from that.
379 _view->set_interpolation (style);
382 _control->list()->set_interpolation (style);
387 AutomationTimeAxisView::clear_clicked ()
389 assert (_line || _view);
391 _session->begin_reversible_command (_("clear automation"));
399 _session->commit_reversible_command ();
400 _session->set_dirty ();
404 AutomationTimeAxisView::set_height (uint32_t h)
406 bool const changed = (height != (uint32_t) h) || first_call_to_set_height;
407 uint32_t const normal = preset_height (HeightNormal);
408 bool const changed_between_small_and_normal = ( (height < normal && h >= normal) || (height >= normal || h < normal) );
410 TimeAxisView::set_height (h);
412 _base_rect->property_y2() = h;
415 _line->set_height(h);
419 _view->set_height(h);
420 _view->update_contents_height();
423 if (changed_between_small_and_normal || first_call_to_set_height) {
425 first_call_to_set_height = false;
427 if (h >= preset_height (HeightNormal)) {
430 name_hbox.show_all ();
433 hide_button.show_all();
435 } else if (h >= preset_height (HeightSmall)) {
436 controls_table.hide_all ();
439 name_hbox.show_all ();
444 } else if (h >= preset_height (HeightNormal)) {
445 cerr << "track grown, but neither changed_between_small_and_normal nor first_call_to_set_height set!" << endl;
449 if (canvas_item_visible (_canvas_display) && _route) {
450 /* only emit the signal if the height really changed and we were visible */
451 _route->gui_changed ("visible_tracks", (void *) 0); /* EMIT_SIGNAL */
457 AutomationTimeAxisView::set_samples_per_unit (double spu)
459 TimeAxisView::set_samples_per_unit (spu);
466 _view->set_samples_per_unit (spu);
471 AutomationTimeAxisView::hide_clicked ()
473 hide_button.set_sensitive(false);
474 set_marked_for_display (false);
475 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(parent);
477 rtv->request_redraw ();
479 hide_button.set_sensitive(true);
483 AutomationTimeAxisView::build_display_menu ()
485 using namespace Menu_Helpers;
489 TimeAxisView::build_display_menu ();
491 /* now fill it with our stuff */
493 MenuList& items = display_menu->items();
495 items.push_back (MenuElem (_("Hide"), sigc::mem_fun(*this, &AutomationTimeAxisView::hide_clicked)));
496 items.push_back (SeparatorElem());
497 items.push_back (MenuElem (_("Clear"), sigc::mem_fun(*this, &AutomationTimeAxisView::clear_clicked)));
498 items.push_back (SeparatorElem());
502 Menu* auto_state_menu = manage (new Menu);
503 auto_state_menu->set_name ("ArdourContextMenu");
504 MenuList& as_items = auto_state_menu->items();
506 as_items.push_back (CheckMenuElem (S_("Automation|Manual"), sigc::bind (
507 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
508 (AutoState) ARDOUR::Off)));
509 auto_off_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
511 as_items.push_back (CheckMenuElem (_("Play"), sigc::bind (
512 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
514 auto_play_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
516 as_items.push_back (CheckMenuElem (_("Write"), sigc::bind (
517 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
518 (AutoState) Write)));
519 auto_write_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
521 as_items.push_back (CheckMenuElem (_("Touch"), sigc::bind (
522 sigc::mem_fun(*this, &AutomationTimeAxisView::set_automation_state),
523 (AutoState) Touch)));
524 auto_touch_item = dynamic_cast<CheckMenuItem*>(&as_items.back());
526 items.push_back (MenuElem (_("State"), *auto_state_menu));
530 /* current interpolation state */
531 AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
533 if (EventTypeMap::instance().is_midi_parameter(_parameter)) {
535 Menu* auto_mode_menu = manage (new Menu);
536 auto_mode_menu->set_name ("ArdourContextMenu");
537 MenuList& am_items = auto_mode_menu->items();
539 RadioMenuItem::Group group;
541 am_items.push_back (RadioMenuElem (group, _("Discrete"), sigc::bind (
542 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
543 AutomationList::Discrete)));
544 mode_discrete_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
545 mode_discrete_item->set_active (s == AutomationList::Discrete);
547 am_items.push_back (RadioMenuElem (group, _("Linear"), sigc::bind (
548 sigc::mem_fun(*this, &AutomationTimeAxisView::set_interpolation),
549 AutomationList::Linear)));
550 mode_line_item = dynamic_cast<CheckMenuItem*>(&am_items.back());
551 mode_line_item->set_active (s == AutomationList::Linear);
553 items.push_back (MenuElem (_("Mode"), *auto_mode_menu));
556 /* make sure the automation menu state is correct */
558 automation_state_changed ();
559 interpolation_changed (s);
563 AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when, double y)
571 _canvas_display->w2i (x, y);
573 /* compute vertical fractional position */
575 y = 1.0 - (y / height);
579 _line->view_to_model_coord (x, y);
581 boost::shared_ptr<AutomationList> list = _line->the_list ();
583 _editor.snap_to_with_modifier (when, event);
585 _session->begin_reversible_command (_("add automation event"));
586 XMLNode& before = list->get_state();
590 XMLNode& after = list->get_state();
591 _session->commit_reversible_command (new MementoCommand<ARDOUR::AutomationList> (*list, &before, &after));
592 _session->set_dirty ();
596 AutomationTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
598 list<boost::shared_ptr<AutomationLine> > lines;
600 lines.push_back (_line);
602 lines = _view->get_lines ();
605 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
606 cut_copy_clear_one (**i, selection, op);
611 AutomationTimeAxisView::cut_copy_clear_one (AutomationLine& line, Selection& selection, CutCopyOp op)
613 boost::shared_ptr<Evoral::ControlList> what_we_got;
614 boost::shared_ptr<AutomationList> alist (line.the_list());
616 XMLNode &before = alist->get_state();
618 /* convert time selection to automation list model coordinates */
619 const Evoral::TimeConverter<double, ARDOUR::framepos_t>& tc = line.time_converter ();
620 double const start = tc.from (selection.time.front().start - tc.origin_b ());
621 double const end = tc.from (selection.time.front().end - tc.origin_b ());
625 if (alist->cut (start, end) != 0) {
626 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
632 if ((what_we_got = alist->cut (start, end)) != 0) {
633 _editor.get_cut_buffer().add (what_we_got);
634 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
638 if ((what_we_got = alist->copy (start, end)) != 0) {
639 _editor.get_cut_buffer().add (what_we_got);
644 if ((what_we_got = alist->cut (start, end)) != 0) {
645 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
651 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
652 double when = (*x)->when;
653 double val = (*x)->value;
654 line.model_to_view_coord (when, val);
662 AutomationTimeAxisView::reset_objects (PointSelection& selection)
664 list<boost::shared_ptr<AutomationLine> > lines;
666 lines.push_back (_line);
668 lines = _view->get_lines ();
671 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
672 reset_objects_one (**i, selection);
677 AutomationTimeAxisView::reset_objects_one (AutomationLine& line, PointSelection& selection)
679 boost::shared_ptr<AutomationList> alist(line.the_list());
681 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &alist->get_state(), 0));
683 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
685 if ((*i).track != this) {
689 alist->reset_range ((*i).start, (*i).end);
694 AutomationTimeAxisView::cut_copy_clear_objects (PointSelection& selection, CutCopyOp op)
696 list<boost::shared_ptr<AutomationLine> > lines;
698 lines.push_back (_line);
700 lines = _view->get_lines ();
703 for (list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin(); i != lines.end(); ++i) {
704 cut_copy_clear_objects_one (**i, selection, op);
709 AutomationTimeAxisView::cut_copy_clear_objects_one (AutomationLine& line, PointSelection& selection, CutCopyOp op)
711 boost::shared_ptr<Evoral::ControlList> what_we_got;
712 boost::shared_ptr<AutomationList> alist(line.the_list());
714 XMLNode &before = alist->get_state();
716 for (PointSelection::iterator i = selection.begin(); i != selection.end(); ++i) {
718 if ((*i).track != this) {
724 if (alist->cut ((*i).start, (*i).end) != 0) {
725 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
729 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
730 _editor.get_cut_buffer().add (what_we_got);
731 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
735 if ((what_we_got = alist->copy ((*i).start, (*i).end)) != 0) {
736 _editor.get_cut_buffer().add (what_we_got);
741 if ((what_we_got = alist->cut ((*i).start, (*i).end)) != 0) {
742 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), new XMLNode (before), &alist->get_state()));
751 for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
752 double when = (*x)->when;
753 double val = (*x)->value;
754 line.model_to_view_coord (when, val);
761 /** Paste a selection.
762 * @param pos Position to paste to (session frames).
763 * @param times Number of times to paste.
764 * @param selection Selection to paste.
765 * @param nth Index of the AutomationList within the selection to paste from.
768 AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
770 boost::shared_ptr<AutomationLine> line;
775 line = _view->paste_line (pos);
782 return paste_one (*line, pos, times, selection, nth);
786 AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float times, Selection& selection, size_t nth)
788 AutomationSelection::iterator p;
789 boost::shared_ptr<AutomationList> alist(line.the_list());
791 for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {}
793 if (p == selection.lines.end()) {
797 /* Make a copy of the list because we have to scale the
798 values from view coordinates to model coordinates, and we're
799 not supposed to modify the points in the selection.
802 AutomationList copy (**p);
804 for (AutomationList::iterator x = copy.begin(); x != copy.end(); ++x) {
805 double when = (*x)->when;
806 double val = (*x)->value;
807 line.view_to_model_coord (when, val);
812 double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ());
814 XMLNode &before = alist->get_state();
815 alist->paste (copy, model_pos, times);
816 _session->add_command (new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
822 AutomationTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
824 if (!_line && !_view) {
828 if (touched (top, bot)) {
830 /* remember: this is X Window - coordinate space starts in upper left and moves down.
831 _y_position is the "origin" or "top" of the track.
834 /* bottom of our track */
835 double const mybot = _y_position + height;
840 if (_y_position >= top && mybot <= bot) {
842 /* _y_position is below top, mybot is above bot, so we're fully
851 /* top and bot are within _y_position .. mybot */
853 topfrac = 1.0 - ((top - _y_position) / height);
854 botfrac = 1.0 - ((bot - _y_position) / height);
859 _line->get_selectables (start, end, botfrac, topfrac, results);
861 _view->get_selectables (start, end, botfrac, topfrac, results);
867 AutomationTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& result)
870 _line->get_inverted_selectables (sel, result);
875 AutomationTimeAxisView::set_selected_points (PointSelection& points)
878 _line->set_selected_points (points);
880 _view->set_selected_points (points);
885 AutomationTimeAxisView::clear_lines ()
888 _list_connections.drop_connections ();
892 AutomationTimeAxisView::add_line (boost::shared_ptr<AutomationLine> line)
897 assert(line->the_list() == _control->list());
899 _control->alist()->automation_state_changed.connect (
900 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::automation_state_changed, this), gui_context()
903 _control->alist()->InterpolationChanged.connect (
904 _list_connections, invalidator (*this), boost::bind (&AutomationTimeAxisView::interpolation_changed, this, _1), gui_context()
910 line->set_height (height);
912 /* pick up the current state */
913 automation_state_changed ();
919 AutomationTimeAxisView::entered()
922 _line->track_entered();
927 AutomationTimeAxisView::exited ()
930 _line->track_exited();
935 AutomationTimeAxisView::color_handler ()
943 AutomationTimeAxisView::set_state_2X (const XMLNode& node, int /*version*/)
945 if (node.name() == X_("gain") && _parameter == Evoral::Parameter (GainAutomation)) {
946 XMLProperty const * shown = node.property (X_("shown"));
948 bool yn = string_is_affirmative (shown->value ());
950 _canvas_display->show (); /* FIXME: necessary? show_at? */
952 set_gui_property ("visible", yn);
954 set_gui_property ("visible", false);
962 AutomationTimeAxisView::set_state (const XMLNode&, int /*version*/)
968 AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr<Automatable>& automatable, set<Evoral::Parameter>& visible)
970 /* this keeps "knowledge" of how we store visibility information
971 in XML private to this class.
974 assert (automatable);
976 Automatable::Controls& controls (automatable->controls());
978 for (Automatable::Controls::iterator i = controls.begin(); i != controls.end(); ++i) {
980 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
984 const XMLNode* gui_node = ac->extra_xml ("GUI");
987 const XMLProperty* prop = gui_node->property ("shown");
989 if (string_is_affirmative (prop->value())) {
990 visible.insert (i->first);
999 /** @return true if this view has any automation data to display */
1001 AutomationTimeAxisView::has_automation () const
1003 return ( (_line && _line->npoints() > 0) || (_view && _view->has_automation()) );
1006 list<boost::shared_ptr<AutomationLine> >
1007 AutomationTimeAxisView::lines () const
1009 list<boost::shared_ptr<AutomationLine> > lines;
1012 lines.push_back (_line);
1014 lines = _view->get_lines ();
1021 AutomationTimeAxisView::state_id() const
1024 return string_compose ("automation %1", _control->id().to_s());
1026 assert (_parameter);
1027 return string_compose ("automation %1 %2/%3/%4",
1031 (int) _parameter.channel());
1035 /** Given a state id string, see if it is one generated by
1036 * this class. If so, parse it into its components.
1037 * @param state_id State ID string to parse.
1038 * @param route_id Filled in with the route's ID if the state ID string is parsed.
1039 * @param has_parameter Filled in with true if the state ID has a parameter, otherwise false.
1040 * @param parameter Filled in with the state ID's parameter, if it has one.
1041 * @return true if this is a state ID generated by this class, otherwise false.
1045 AutomationTimeAxisView::parse_state_id (
1046 string const & state_id,
1048 bool & has_parameter,
1049 Evoral::Parameter & parameter)
1057 if (a != X_("automation")) {
1061 route_id = PBD::ID (b);
1064 has_parameter = false;
1068 has_parameter = true;
1071 boost::split (p, c, boost::is_any_of ("/"));
1073 assert (p.size() == 3);
1075 parameter = Evoral::Parameter (
1076 boost::lexical_cast<int> (p[0]),
1077 boost::lexical_cast<int> (p[2]),
1078 boost::lexical_cast<int> (p[1])