2 Copyright (C) 2002-2003 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.
25 #include <pbd/stl_delete.h>
26 #include <pbd/memento_command.h>
28 #include <ardour/automation_event.h>
29 #include <ardour/curve.h>
30 #include <ardour/dB.h>
32 #include "simplerect.h"
33 #include "automation_line.h"
34 #include "rgb_macros.h"
35 #include "ardour_ui.h"
36 #include "public_editor.h"
38 #include "selection.h"
39 #include "time_axis_view.h"
40 #include "point_selection.h"
41 #include "automation_selectable.h"
42 #include "automation_time_axis.h"
43 #include "public_editor.h"
45 #include <ardour/session.h>
51 using namespace ARDOUR;
53 using namespace Editing;
54 using namespace Gnome; // for Canvas
56 ControlPoint::ControlPoint (AutomationLine& al)
59 model = al.the_list().end();
68 item = new Canvas::SimpleRect (line.canvas_group());
69 item->property_draw() = true;
70 item->property_fill() = false;
71 item->property_fill_color_rgba() = color_map[cControlPointFill];
72 item->property_outline_color_rgba() = color_map[cControlPointOutline];
73 item->property_outline_pixels() = 1;
74 item->set_data ("control_point", this);
75 item->signal_event().connect (mem_fun (this, &ControlPoint::event_handler));
81 ControlPoint::ControlPoint (const ControlPoint& other, bool dummy_arg_to_force_special_copy_constructor)
89 view_index = other.view_index;
90 can_slide = other.can_slide;
93 _shape = other._shape;
97 item = new Canvas::SimpleRect (line.canvas_group());
98 item->property_fill() = false;
99 item->property_outline_color_rgba() = color_map[cEnteredControlPointOutline];
100 item->property_outline_pixels() = 1;
102 /* NOTE: no event handling in copied ControlPoints */
108 ControlPoint::~ControlPoint ()
114 ControlPoint::event_handler (GdkEvent* event)
116 return PublicEditor::instance().canvas_control_point_event (event, item, this);
120 ControlPoint::hide ()
132 ControlPoint::set_visible (bool yn)
134 item->property_draw() = (gboolean) yn;
138 ControlPoint::reset (double x, double y, AutomationList::iterator mi, uint32_t vi, ShapeType shape)
142 move_to (x, y, shape);
146 ControlPoint::show_color (bool entered, bool hide_too)
150 item->property_outline_color_rgba() = color_map[cEnteredControlPointSelected];
153 item->property_outline_color_rgba() = color_map[cEnteredControlPoint];
161 item->property_outline_color_rgba() = color_map[cControlPointSelected];
164 item->property_outline_color_rgba() = color_map[cControlPoint];
173 ControlPoint::set_size (double sz)
179 item->property_fill() = (gboolean) TRUE;
181 item->property_fill() = (gboolean) FALSE;
185 move_to (_x, _y, _shape);
189 ControlPoint::move_to (double x, double y, ShapeType shape)
193 double half_size = rint(_size/2.0);
210 item->property_x1() = x1;
211 item->property_x2() = x2;
212 item->property_y1() = y - half_size;
213 item->property_y2() = y + half_size;
222 AutomationLine::AutomationLine (const string & name, TimeAxisView& tv, ArdourCanvas::Group& parent, AutomationList& al)
226 _parent_group (parent)
228 points_visible = false;
229 update_pending = false;
230 _vc_uses_gain_mapping = false;
233 terminal_points_can_slide = true;
236 group = new ArdourCanvas::Group (parent);
237 group->property_x() = 0.0;
238 group->property_y() = 0.0;
240 line = new ArdourCanvas::Line (*group);
241 line->property_width_pixels() = (guint)1;
242 line->set_data ("line", this);
244 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
246 alist.StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
249 AutomationLine::~AutomationLine ()
251 vector_delete (&control_points);
256 AutomationLine::event_handler (GdkEvent* event)
258 return PublicEditor::instance().canvas_line_event (event, line, this);
262 AutomationLine::queue_reset ()
264 if (!update_pending) {
265 update_pending = true;
266 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
271 AutomationLine::set_point_size (double sz)
273 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
279 AutomationLine::show ()
283 if (points_visible) {
284 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
293 AutomationLine::hide ()
296 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
303 AutomationLine::set_height (guint32 h)
308 if (_height > (guint32) TimeAxisView::Larger) {
309 set_point_size (8.0);
310 } else if (_height > (guint32) TimeAxisView::Normal) {
311 set_point_size (6.0);
313 set_point_size (4.0);
321 AutomationLine::set_line_color (uint32_t color)
324 line->property_fill_color_rgba() = color;
328 AutomationLine::set_verbose_cursor_uses_gain_mapping (bool yn)
330 if (yn != _vc_uses_gain_mapping) {
331 _vc_uses_gain_mapping = yn;
337 AutomationLine::nth (uint32_t n)
339 if (n < control_points.size()) {
340 return control_points[n];
347 AutomationLine::modify_view (ControlPoint& cp, double x, double y, bool with_push)
349 modify_view_point (cp, x, y, with_push);
354 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
357 uint32_t last_movable = UINT_MAX;
358 double x_limit = DBL_MAX;
360 /* this just changes the current view. it does not alter
361 the model in any way at all.
364 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
365 and needs to be converted to a canvas unit distance.
370 y = _height - (y * _height);
374 /* x-coord cannot move beyond adjacent points or the start/end, and is
375 already in frames. it needs to be converted to canvas units.
378 x = trackview.editor.frame_to_unit (x);
380 /* clamp x position using view coordinates */
382 ControlPoint *before;
386 before = nth (cp.view_index - 1);
387 x = max (x, before->get_x()+1.0);
394 if (cp.view_index < control_points.size() - 1) {
396 after = nth (cp.view_index + 1);
398 /*if it is a "spike" leave the x alone */
400 if (after->get_x() - before->get_x() < 2) {
404 x = min (x, after->get_x()-1.0);
414 /* find the first point that can't move */
416 for (uint32_t n = cp.view_index + 1; (after = nth (n)) != 0; ++n) {
417 if (!after->can_slide) {
418 x_limit = after->get_x() - 1.0;
419 last_movable = after->view_index;
424 delta = x - cp.get_x();
429 /* leave the x-coordinate alone */
431 x = trackview.editor.frame_to_unit ((*cp.model)->when);
437 cp.move_to (x, y, ControlPoint::Full);
438 reset_line_coords (cp);
442 uint32_t limit = min (control_points.size(), (size_t)last_movable);
444 /* move the current point to wherever the user told it to go, subject
448 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
449 reset_line_coords (cp);
451 /* now move all subsequent control points, to reflect the motion.
454 for (uint32_t i = cp.view_index + 1; i < limit; ++i) {
455 ControlPoint *p = nth (i);
459 new_x = min (p->get_x() + delta, x_limit);
460 p->move_to (new_x, p->get_y(), ControlPoint::Full);
461 reset_line_coords (*p);
468 AutomationLine::reset_line_coords (ControlPoint& cp)
470 line_points[cp.view_index].set_x (cp.get_x());
471 line_points[cp.view_index].set_y (cp.get_y());
475 AutomationLine::update_line ()
477 line->property_points() = line_points;
481 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
486 update_pending = true;
488 for (uint32_t i = start; i <= end; ++i) {
490 sync_model_with_view_point(*p);
497 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
499 /* part one: find out where the visual control point is.
500 initial results are in canvas units. ask the
501 line to convert them to something relevant.
504 mr.xval = (jack_nframes_t) floor (cp.get_x());
505 mr.yval = 1.0 - (cp.get_y() / _height);
508 /* if xval has not changed, set it directly from the model to avoid rounding errors */
510 if (mr.xval == trackview.editor.frame_to_unit((*cp.model)->when)) {
511 mr.xval = (jack_nframes_t) (*cp.model)->when;
513 mr.xval = trackview.editor.unit_to_frame (mr.xval);
517 /* virtual call: this will do the right thing
518 for whatever particular type of line we are.
521 view_to_model_y (mr.yval);
523 /* part 2: find out where the model point is now
526 mr.xpos = (jack_nframes_t) (*cp.model)->when;
527 mr.ypos = (*cp.model)->value;
529 /* part 3: get the position of the visual control
530 points before and after us.
533 ControlPoint* before;
537 before = nth (cp.view_index - 1);
542 after = nth (cp.view_index + 1);
545 mr.xmin = (jack_nframes_t) (*before->model)->when;
546 mr.ymin = (*before->model)->value;
547 mr.start = before->model;
556 mr.end = after->model;
566 AutomationLine::sync_model_from (ControlPoint& cp)
571 sync_model_with_view_point (cp);
573 /* we might have moved all points after `cp' by some amount
574 if we pressed the with_push modifyer some of the time during the drag
575 so all subsequent points have to be resynced
578 lasti = control_points.size() - 1;
581 update_pending = true;
583 while (p != &cp && lasti) {
584 sync_model_with_view_point (*p);
591 AutomationLine::sync_model_with_view_point (ControlPoint& cp)
593 ModelRepresentation mr;
596 model_representation (cp, mr);
598 /* part 4: how much are we changing the central point by */
600 ydelta = mr.yval - mr.ypos;
602 /* IMPORTANT: changing the model when the x-coordinate changes
603 may invalidate the iterators that we are using. this means that we have
604 to change the points before+after the one corresponding to the visual CP
605 first (their x-coordinate doesn't change). then we change the
608 apply the full change to the central point, and interpolate
609 in each direction to cover all model points represented
610 by the control point.
613 /* part 5: change all points before the primary point */
615 for (AutomationList::iterator i = mr.start; i != cp.model; ++i) {
619 delta = ydelta * ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
621 /* x-coordinate (generally time) stays where it is,
622 y-coordinate moves by a certain amount.
625 update_pending = true;
626 change_model (i, (*i)->when, mr.yval + delta);
629 /* part 6: change later points */
631 AutomationList::iterator i = cp.model;
635 while (i != mr.end) {
639 delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
641 /* x-coordinate (generally time) stays where it is,
642 y-coordinate moves by a certain amount.
645 update_pending = true;
646 change_model (i, (*i)->when, (*i)->value + delta);
651 /* part 7: change the primary point */
653 update_pending = true;
654 change_model (cp.model, mr.xval, mr.yval);
658 AutomationLine::determine_visible_control_points (ALPoints& points)
660 uint32_t view_index, pi, n;
661 AutomationList::iterator model;
663 double last_control_point_x = 0.0;
664 double last_control_point_y = 0.0;
665 uint32_t this_rx = 0;
666 uint32_t prev_rx = 0;
667 uint32_t this_ry = 0;
668 uint32_t prev_ry = 0;
671 /* hide all existing points, and the line */
673 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
679 if (points.empty()) {
683 npoints = points.size();
685 /* compute derivative/slope for the entire line */
687 slope = new double[npoints];
689 for (n = 0; n < npoints - 1; ++n) {
690 double xdelta = points[n+1].x - points[n].x;
691 double ydelta = points[n+1].y - points[n].y;
692 slope[n] = ydelta/xdelta;
695 /* read all points and decide which ones to show as control points */
699 for (model = alist.begin(), pi = 0; pi < npoints; ++model, ++pi) {
701 double tx = points[pi].x;
702 double ty = points[pi].y;
704 /* now ensure that the control_points vector reflects the current curve
705 state, but don't plot control points too close together. also, don't
706 plot a series of points all with the same value.
708 always plot the first and last points, of course.
711 if (invalid_point (points, pi)) {
712 /* for some reason, we are supposed to ignore this point,
713 but still keep track of the model index.
718 if (pi > 0 && pi < npoints - 1) {
719 if (slope[pi] == slope[pi-1]) {
721 /* no reason to display this point */
727 /* need to round here. the ultimate coordinates are integer
728 pixels, so tiny deltas in the coords will be eliminated
729 and we end up with "colinear" line segments. since the
730 line rendering code in libart doesn't like this very
731 much, we eliminate them here. don't do this for the first and last
735 this_rx = (uint32_t) rint (tx);
736 this_ry = (unsigned long) rint (ty);
738 if (view_index && pi != npoints && (this_rx == prev_rx) && (this_ry == prev_ry)) {
743 /* ok, we should display this point */
745 if (view_index >= control_points.size()) {
746 /* make sure we have enough control points */
748 ControlPoint* ncp = new ControlPoint (*this);
750 if (_height > (guint32) TimeAxisView::Larger) {
752 } else if (_height > (guint32) TimeAxisView::Normal) {
758 control_points.push_back (ncp);
761 ControlPoint::ShapeType shape;
763 if (!terminal_points_can_slide) {
765 control_points[view_index]->can_slide = false;
767 shape = ControlPoint::Start;
769 shape = ControlPoint::Full;
771 } else if (pi == npoints - 1) {
772 control_points[view_index]->can_slide = false;
773 shape = ControlPoint::End;
775 control_points[view_index]->can_slide = true;
776 shape = ControlPoint::Full;
779 control_points[view_index]->can_slide = true;
780 shape = ControlPoint::Full;
783 last_control_point_x = tx;
784 last_control_point_y = ty;
786 control_points[view_index]->reset (tx, ty, model, view_index, shape);
791 /* finally, control visibility */
793 if (_visible && points_visible) {
794 control_points[view_index]->show ();
795 control_points[view_index]->set_visible (true);
797 if (!points_visible) {
798 control_points[view_index]->set_visible (false);
805 /* discard extra CP's to avoid confusing ourselves */
807 while (control_points.size() > view_index) {
808 ControlPoint* cp = control_points.back();
809 control_points.pop_back ();
813 if (!terminal_points_can_slide) {
814 control_points.back()->can_slide = false;
819 if (view_index > 1) {
821 npoints = view_index;
823 /* reset the line coordinates */
825 while (line_points.size() < npoints) {
826 line_points.push_back (Art::Point (0,0));
829 for (view_index = 0; view_index < npoints; ++view_index) {
830 line_points[view_index].set_x (control_points[view_index]->get_x());
831 line_points[view_index].set_y (control_points[view_index]->get_y());
834 line->property_points() = line_points;
841 set_selected_points (trackview.editor.get_selection().points);
845 AutomationLine::get_verbose_cursor_string (float fraction)
849 if (_vc_uses_gain_mapping) {
850 if (fraction == 0.0) {
851 snprintf (buf, sizeof (buf), "-inf dB");
853 snprintf (buf, sizeof (buf), "%.1fdB", coefficient_to_dB (slider_position_to_gain (fraction)));
856 snprintf (buf, sizeof (buf), "%.2f", fraction);
863 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
865 return p[index].x == max_frames && p[index].y == DBL_MAX;
869 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
871 p[index].x = max_frames;
872 p[index].y = DBL_MAX;
876 AutomationLine::start_drag (ControlPoint* cp, float fraction)
878 if (trackview.editor.current_session() == 0) { /* how? */
885 str = _("automation event move");
887 str = _("automation range drag");
890 trackview.editor.current_session()->begin_reversible_command (str);
891 trackview.editor.current_session()->add_command (new MementoUndoCommand<AutomationLine>(*this, get_state()));
893 first_drag_fraction = fraction;
894 last_drag_fraction = fraction;
899 AutomationLine::point_drag (ControlPoint& cp, jack_nframes_t x, float fraction, bool with_push)
901 modify_view (cp, x, fraction, with_push);
906 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
908 double ydelta = fraction - last_drag_fraction;
910 last_drag_fraction = fraction;
917 for (uint32_t i = i1 ; i <= i2; i++) {
919 modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
928 AutomationLine::end_drag (ControlPoint* cp)
933 sync_model_from (*cp);
935 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
938 update_pending = false;
940 trackview.editor.current_session()->add_command (new MementoRedoCommand<AutomationLine>(*this, get_state()));
941 trackview.editor.current_session()->commit_reversible_command ();
942 trackview.editor.current_session()->set_dirty ();
947 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
949 ControlPoint *bcp = 0;
950 ControlPoint *acp = 0;
953 /* xval is in frames */
955 unit_xval = trackview.editor.frame_to_unit (xval);
957 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
959 if ((*i)->get_x() <= unit_xval) {
961 if (!bcp || (*i)->get_x() > bcp->get_x()) {
963 before = bcp->view_index;
966 } else if ((*i)->get_x() > unit_xval) {
968 after = acp->view_index;
977 AutomationLine::is_last_point (ControlPoint& cp)
979 ModelRepresentation mr;
981 model_representation (cp, mr);
983 // If the list is not empty, and the point is the last point in the list
985 if (!alist.empty() && mr.end == alist.end()) {
993 AutomationLine::is_first_point (ControlPoint& cp)
995 ModelRepresentation mr;
997 model_representation (cp, mr);
999 // If the list is not empty, and the point is the first point in the list
1001 if (!alist.empty() && mr.start == alist.begin()) {
1008 // This is copied into AudioRegionGainLine
1010 AutomationLine::remove_point (ControlPoint& cp)
1012 ModelRepresentation mr;
1014 model_representation (cp, mr);
1016 trackview.editor.current_session()->begin_reversible_command (_("remove control point"));
1017 XMLNode &before = get_state();
1019 alist.erase (mr.start, mr.end);
1021 trackview.editor.current_session()->add_command(new MementoCommand<AutomationLine>(*this, before, get_state()));
1022 trackview.editor.current_session()->commit_reversible_command ();
1023 trackview.editor.current_session()->set_dirty ();
1027 AutomationLine::get_selectables (jack_nframes_t& start, jack_nframes_t& end,
1028 double botfrac, double topfrac, list<Selectable*>& results)
1033 jack_nframes_t nstart;
1034 jack_nframes_t nend;
1035 bool collecting = false;
1037 /* Curse X11 and its inverted coordinate system! */
1039 bot = (1.0 - topfrac) * _height;
1040 top = (1.0 - botfrac) * _height;
1042 nstart = max_frames;
1045 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1047 jack_nframes_t when = (jack_nframes_t) (*(*i)->model)->when;
1049 if (when >= start && when <= end) {
1051 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1054 (*i)->set_visible(true);
1056 nstart = min (nstart, when);
1057 nend = max (nend, when);
1063 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1065 nstart = max_frames;
1073 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1079 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1085 AutomationLine::set_selected_points (PointSelection& points)
1090 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1091 (*i)->selected = false;
1094 if (points.empty()) {
1098 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1100 if (&(*r).track != &trackview) {
1104 /* Curse X11 and its inverted coordinate system! */
1106 bot = (1.0 - (*r).high_fract) * _height;
1107 top = (1.0 - (*r).low_fract) * _height;
1109 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1111 double rstart, rend;
1113 rstart = trackview.editor.frame_to_unit ((*r).start);
1114 rend = trackview.editor.frame_to_unit ((*r).end);
1116 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1118 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1120 (*i)->selected = true;
1128 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1129 (*i)->show_color (false, !points_visible);
1135 AutomationLine::show_selection ()
1137 TimeSelection& time (trackview.editor.get_selection().time);
1139 // cerr << "show selection\n";
1141 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1143 (*i)->selected = false;
1145 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1146 double rstart, rend;
1148 rstart = trackview.editor.frame_to_unit ((*r).start);
1149 rend = trackview.editor.frame_to_unit ((*r).end);
1151 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1152 (*i)->selected = true;
1157 (*i)->show_color (false, !points_visible);
1162 AutomationLine::hide_selection ()
1164 // cerr << "hide selection\n";
1165 // show_selection ();
1169 // This is copied into AudioRegionGainLine
1171 AutomationLine::get_memento ()
1173 return alist.get_memento();
1177 AutomationLine::list_changed (Change ignored)
1183 AutomationLine::reset_callback (const AutomationList& events)
1185 ALPoints tmp_points;
1186 uint32_t npoints = events.size();
1189 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1192 control_points.clear ();
1197 AutomationList::const_iterator ai;
1199 for (ai = events.const_begin(); ai != events.const_end(); ++ai) {
1201 double translated_y;
1203 translated_y = (*ai)->value;
1204 model_to_view_y (translated_y);
1206 tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit ((*ai)->when),
1207 _height - (translated_y * _height)));
1210 determine_visible_control_points (tmp_points);
1214 AutomationLine::reset ()
1216 update_pending = false;
1222 alist.apply_to_points (*this, &AutomationLine::reset_callback);
1226 AutomationLine::clear ()
1228 /* parent must create command */
1229 XMLNode &before = get_state();
1231 trackview.editor.current_session()->add_command (new MementoCommand<AutomationLine>(*this, before, get_state()));
1232 trackview.editor.current_session()->commit_reversible_command ();
1233 trackview.editor.current_session()->set_dirty ();
1237 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1239 alist.modify (i, (jack_nframes_t) x, y);
1243 AutomationLine::change_model_range (AutomationList::iterator start, AutomationList::iterator end, double xdelta, float ydelta)
1245 alist.move_range (start, end, xdelta, ydelta);
1249 AutomationLine::show_all_control_points ()
1251 points_visible = true;
1253 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1255 (*i)->set_visible (true);
1260 AutomationLine::hide_all_but_selected_control_points ()
1262 points_visible = false;
1264 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1265 if (!(*i)->selected) {
1266 (*i)->set_visible (false);
1271 XMLNode &AutomationLine::get_state(void)
1274 return alist.get_state();
1277 int AutomationLine::set_state(const XMLNode &node)
1280 alist.set_state(node);