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.
26 #include <pbd/stl_delete.h>
27 #include <pbd/memento_command.h>
29 #include <ardour/automation_event.h>
30 #include <ardour/curve.h>
31 #include <ardour/dB.h>
33 #include "simplerect.h"
34 #include "automation_line.h"
35 #include "rgb_macros.h"
36 #include "ardour_ui.h"
37 #include "public_editor.h"
39 #include "selection.h"
40 #include "time_axis_view.h"
41 #include "point_selection.h"
42 #include "automation_selectable.h"
43 #include "automation_time_axis.h"
44 #include "public_editor.h"
46 #include <ardour/session.h>
52 using namespace ARDOUR;
54 using namespace Editing;
55 using namespace Gnome; // for Canvas
57 ControlPoint::ControlPoint (AutomationLine& al)
60 model = al.the_list().end();
69 item = new Canvas::SimpleRect (line.canvas_group());
70 item->property_draw() = true;
71 item->property_fill() = false;
72 item->property_fill_color_rgba() = color_map[cControlPointFill];
73 item->property_outline_color_rgba() = color_map[cControlPointOutline];
74 item->property_outline_pixels() = 1;
75 item->set_data ("control_point", this);
76 item->signal_event().connect (mem_fun (this, &ControlPoint::event_handler));
82 ControlPoint::ControlPoint (const ControlPoint& other, bool dummy_arg_to_force_special_copy_constructor)
90 view_index = other.view_index;
91 can_slide = other.can_slide;
94 _shape = other._shape;
98 item = new Canvas::SimpleRect (line.canvas_group());
99 item->property_fill() = false;
100 item->property_outline_color_rgba() = color_map[cEnteredControlPointOutline];
101 item->property_outline_pixels() = 1;
103 /* NOTE: no event handling in copied ControlPoints */
109 ControlPoint::~ControlPoint ()
115 ControlPoint::event_handler (GdkEvent* event)
117 return PublicEditor::instance().canvas_control_point_event (event, item, this);
121 ControlPoint::hide ()
133 ControlPoint::set_visible (bool yn)
135 item->property_draw() = (gboolean) yn;
139 ControlPoint::reset (double x, double y, AutomationList::iterator mi, uint32_t vi, ShapeType shape)
143 move_to (x, y, shape);
147 ControlPoint::show_color (bool entered, bool hide_too)
151 item->property_outline_color_rgba() = color_map[cEnteredControlPointSelected];
154 item->property_outline_color_rgba() = color_map[cEnteredControlPoint];
162 item->property_outline_color_rgba() = color_map[cControlPointSelected];
165 item->property_outline_color_rgba() = color_map[cControlPoint];
174 ControlPoint::set_size (double sz)
180 item->property_fill() = (gboolean) TRUE;
182 item->property_fill() = (gboolean) FALSE;
186 move_to (_x, _y, _shape);
190 ControlPoint::move_to (double x, double y, ShapeType shape)
194 double half_size = rint(_size/2.0);
211 item->property_x1() = x1;
212 item->property_x2() = x2;
213 item->property_y1() = y - half_size;
214 item->property_y2() = y + half_size;
223 AutomationLine::AutomationLine (const string & name, TimeAxisView& tv, ArdourCanvas::Group& parent, AutomationList& al)
227 _parent_group (parent)
229 points_visible = false;
230 update_pending = false;
231 _vc_uses_gain_mapping = false;
234 terminal_points_can_slide = true;
237 group = new ArdourCanvas::Group (parent);
238 group->property_x() = 0.0;
239 group->property_y() = 0.0;
241 line = new ArdourCanvas::Line (*group);
242 line->property_width_pixels() = (guint)1;
243 line->set_data ("line", this);
245 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
247 alist.StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
249 trackview.session().register_with_memento_command_factory(alist.id(), this);
253 AutomationLine::~AutomationLine ()
255 vector_delete (&control_points);
260 AutomationLine::event_handler (GdkEvent* event)
262 return PublicEditor::instance().canvas_line_event (event, line, this);
266 AutomationLine::queue_reset ()
268 if (!update_pending) {
269 update_pending = true;
270 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
275 AutomationLine::show ()
279 if (points_visible) {
280 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
289 AutomationLine::hide ()
292 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
299 AutomationLine::control_point_box_size ()
301 if (_height > TimeAxisView::hLarger) {
303 } else if (_height > (guint32) TimeAxisView::hNormal) {
310 AutomationLine::set_height (guint32 h)
315 double bsz = control_point_box_size();
317 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
318 (*i)->set_size (bsz);
326 AutomationLine::set_line_color (uint32_t color)
329 line->property_fill_color_rgba() = color;
333 AutomationLine::set_verbose_cursor_uses_gain_mapping (bool yn)
335 if (yn != _vc_uses_gain_mapping) {
336 _vc_uses_gain_mapping = yn;
342 AutomationLine::nth (uint32_t n)
344 if (n < control_points.size()) {
345 return control_points[n];
352 AutomationLine::modify_view (ControlPoint& cp, double x, double y, bool with_push)
354 modify_view_point (cp, x, y, with_push);
359 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
362 uint32_t last_movable = UINT_MAX;
363 double x_limit = DBL_MAX;
365 /* this just changes the current view. it does not alter
366 the model in any way at all.
369 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
370 and needs to be converted to a canvas unit distance.
375 y = _height - (y * _height);
379 /* x-coord cannot move beyond adjacent points or the start/end, and is
380 already in frames. it needs to be converted to canvas units.
383 x = trackview.editor.frame_to_unit (x);
385 /* clamp x position using view coordinates */
387 ControlPoint *before;
391 before = nth (cp.view_index - 1);
392 x = max (x, before->get_x()+1.0);
399 if (cp.view_index < control_points.size() - 1) {
401 after = nth (cp.view_index + 1);
403 /*if it is a "spike" leave the x alone */
405 if (after->get_x() - before->get_x() < 2) {
409 x = min (x, after->get_x()-1.0);
419 /* find the first point that can't move */
421 for (uint32_t n = cp.view_index + 1; (after = nth (n)) != 0; ++n) {
422 if (!after->can_slide) {
423 x_limit = after->get_x() - 1.0;
424 last_movable = after->view_index;
429 delta = x - cp.get_x();
434 /* leave the x-coordinate alone */
436 x = trackview.editor.frame_to_unit ((*cp.model)->when);
442 cp.move_to (x, y, ControlPoint::Full);
443 reset_line_coords (cp);
447 uint32_t limit = min (control_points.size(), (size_t)last_movable);
449 /* move the current point to wherever the user told it to go, subject
453 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
454 reset_line_coords (cp);
456 /* now move all subsequent control points, to reflect the motion.
459 for (uint32_t i = cp.view_index + 1; i < limit; ++i) {
460 ControlPoint *p = nth (i);
464 new_x = min (p->get_x() + delta, x_limit);
465 p->move_to (new_x, p->get_y(), ControlPoint::Full);
466 reset_line_coords (*p);
473 AutomationLine::reset_line_coords (ControlPoint& cp)
475 line_points[cp.view_index].set_x (cp.get_x());
476 line_points[cp.view_index].set_y (cp.get_y());
480 AutomationLine::update_line ()
482 line->property_points() = line_points;
486 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
491 update_pending = true;
493 for (uint32_t i = start; i <= end; ++i) {
495 sync_model_with_view_point(*p);
502 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
504 /* part one: find out where the visual control point is.
505 initial results are in canvas units. ask the
506 line to convert them to something relevant.
509 mr.xval = (nframes_t) floor (cp.get_x());
510 mr.yval = 1.0 - (cp.get_y() / _height);
513 /* if xval has not changed, set it directly from the model to avoid rounding errors */
515 if (mr.xval == trackview.editor.frame_to_unit((*cp.model)->when)) {
516 mr.xval = (nframes_t) (*cp.model)->when;
518 mr.xval = trackview.editor.unit_to_frame (mr.xval);
522 /* virtual call: this will do the right thing
523 for whatever particular type of line we are.
526 view_to_model_y (mr.yval);
528 /* part 2: find out where the model point is now
531 mr.xpos = (nframes_t) (*cp.model)->when;
532 mr.ypos = (*cp.model)->value;
534 /* part 3: get the position of the visual control
535 points before and after us.
538 ControlPoint* before;
542 before = nth (cp.view_index - 1);
547 after = nth (cp.view_index + 1);
550 mr.xmin = (nframes_t) (*before->model)->when;
551 mr.ymin = (*before->model)->value;
552 mr.start = before->model;
561 mr.end = after->model;
571 AutomationLine::sync_model_from (ControlPoint& cp)
576 sync_model_with_view_point (cp);
578 /* we might have moved all points after `cp' by some amount
579 if we pressed the with_push modifyer some of the time during the drag
580 so all subsequent points have to be resynced
583 lasti = control_points.size() - 1;
586 update_pending = true;
588 while (p != &cp && lasti) {
589 sync_model_with_view_point (*p);
596 AutomationLine::sync_model_with_view_point (ControlPoint& cp)
598 ModelRepresentation mr;
601 model_representation (cp, mr);
603 /* part 4: how much are we changing the central point by */
605 ydelta = mr.yval - mr.ypos;
607 /* IMPORTANT: changing the model when the x-coordinate changes
608 may invalidate the iterators that we are using. this means that we have
609 to change the points before+after the one corresponding to the visual CP
610 first (their x-coordinate doesn't change). then we change the
613 apply the full change to the central point, and interpolate
614 in each direction to cover all model points represented
615 by the control point.
618 /* part 5: change all points before the primary point */
620 for (AutomationList::iterator i = mr.start; i != cp.model; ++i) {
624 delta = ydelta * ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
626 /* x-coordinate (generally time) stays where it is,
627 y-coordinate moves by a certain amount.
630 update_pending = true;
631 change_model (i, (*i)->when, mr.yval + delta);
634 /* part 6: change later points */
636 AutomationList::iterator i = cp.model;
640 while (i != mr.end) {
644 delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
646 /* x-coordinate (generally time) stays where it is,
647 y-coordinate moves by a certain amount.
650 update_pending = true;
651 change_model (i, (*i)->when, (*i)->value + delta);
656 /* part 7: change the primary point */
658 update_pending = true;
659 change_model (cp.model, mr.xval, mr.yval);
663 AutomationLine::determine_visible_control_points (ALPoints& points)
665 uint32_t view_index, pi, n;
666 AutomationList::iterator model;
668 double last_control_point_x = 0.0;
669 double last_control_point_y = 0.0;
670 uint32_t this_rx = 0;
671 uint32_t prev_rx = 0;
672 uint32_t this_ry = 0;
673 uint32_t prev_ry = 0;
678 /* hide all existing points, and the line */
682 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
689 if (points.empty()) {
693 npoints = points.size();
695 /* compute derivative/slope for the entire line */
697 slope = new double[npoints];
699 for (n = 0; n < npoints - 1; ++n) {
700 double xdelta = points[n+1].x - points[n].x;
701 double ydelta = points[n+1].y - points[n].y;
702 slope[n] = ydelta/xdelta;
705 box_size = (uint32_t) control_point_box_size ();
707 /* read all points and decide which ones to show as control points */
711 for (model = alist.begin(), pi = 0; pi < npoints; ++model, ++pi) {
713 double tx = points[pi].x;
714 double ty = points[pi].y;
716 /* now ensure that the control_points vector reflects the current curve
717 state, but don't plot control points too close together. also, don't
718 plot a series of points all with the same value.
720 always plot the first and last points, of course.
723 if (invalid_point (points, pi)) {
724 /* for some reason, we are supposed to ignore this point,
725 but still keep track of the model index.
730 if (pi > 0 && pi < npoints - 1) {
731 if (slope[pi] == slope[pi-1]) {
733 /* no reason to display this point */
739 /* need to round here. the ultimate coordinates are integer
740 pixels, so tiny deltas in the coords will be eliminated
741 and we end up with "colinear" line segments. since the
742 line rendering code in libart doesn't like this very
743 much, we eliminate them here. don't do this for the first and last
747 this_rx = (uint32_t) rint (tx);
748 this_ry = (uint32_t) rint (ty);
750 if (view_index && pi != npoints && /* not the first, not the last */
751 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
752 (this_rx == prev_rx) || /* identical x coordinate */
753 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
754 ((abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2)))))) { /* too close vertically */
758 /* ok, we should display this point */
760 if (view_index >= cpsize) {
762 /* make sure we have enough control points */
764 ControlPoint* ncp = new ControlPoint (*this);
766 ncp->set_size (box_size);
768 control_points.push_back (ncp);
772 ControlPoint::ShapeType shape;
774 if (!terminal_points_can_slide) {
776 control_points[view_index]->can_slide = false;
778 shape = ControlPoint::Start;
780 shape = ControlPoint::Full;
782 } else if (pi == npoints - 1) {
783 control_points[view_index]->can_slide = false;
784 shape = ControlPoint::End;
786 control_points[view_index]->can_slide = true;
787 shape = ControlPoint::Full;
790 control_points[view_index]->can_slide = true;
791 shape = ControlPoint::Full;
794 last_control_point_x = tx;
795 last_control_point_y = ty;
797 control_points[view_index]->reset (tx, ty, model, view_index, shape);
802 /* finally, control visibility */
804 if (_visible && points_visible) {
805 control_points[view_index]->show ();
806 control_points[view_index]->set_visible (true);
808 if (!points_visible) {
809 control_points[view_index]->set_visible (false);
816 /* discard extra CP's to avoid confusing ourselves */
818 while (control_points.size() > view_index) {
819 ControlPoint* cp = control_points.back();
820 control_points.pop_back ();
824 if (!terminal_points_can_slide) {
825 control_points.back()->can_slide = false;
830 if (view_index > 1) {
832 npoints = view_index;
834 /* reset the line coordinates */
836 while (line_points.size() < npoints) {
837 line_points.push_back (Art::Point (0,0));
840 while (line_points.size() > npoints) {
841 line_points.pop_back ();
844 for (view_index = 0; view_index < npoints; ++view_index) {
845 line_points[view_index].set_x (control_points[view_index]->get_x());
846 line_points[view_index].set_y (control_points[view_index]->get_y());
849 line->property_points() = line_points;
857 set_selected_points (trackview.editor.get_selection().points);
862 AutomationLine::get_verbose_cursor_string (float fraction)
866 if (_vc_uses_gain_mapping) {
867 if (fraction == 0.0) {
868 snprintf (buf, sizeof (buf), "-inf dB");
870 snprintf (buf, sizeof (buf), "%.1fdB", coefficient_to_dB (slider_position_to_gain (fraction)));
873 snprintf (buf, sizeof (buf), "%.2f", fraction);
880 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
882 return p[index].x == max_frames && p[index].y == DBL_MAX;
886 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
888 p[index].x = max_frames;
889 p[index].y = DBL_MAX;
893 AutomationLine::start_drag (ControlPoint* cp, float fraction)
895 if (trackview.editor.current_session() == 0) { /* how? */
902 str = _("automation event move");
904 str = _("automation range drag");
907 trackview.editor.current_session()->begin_reversible_command (str);
908 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(alist, &get_state(), 0));
910 first_drag_fraction = fraction;
911 last_drag_fraction = fraction;
916 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
918 modify_view (cp, x, fraction, with_push);
923 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
925 double ydelta = fraction - last_drag_fraction;
927 last_drag_fraction = fraction;
934 for (uint32_t i = i1 ; i <= i2; i++) {
936 modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
945 AutomationLine::end_drag (ControlPoint* cp)
950 sync_model_from (*cp);
952 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
955 update_pending = false;
957 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(alist, 0, &alist.get_state()));
958 trackview.editor.current_session()->commit_reversible_command ();
959 trackview.editor.current_session()->set_dirty ();
964 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
966 ControlPoint *bcp = 0;
967 ControlPoint *acp = 0;
970 /* xval is in frames */
972 unit_xval = trackview.editor.frame_to_unit (xval);
974 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
976 if ((*i)->get_x() <= unit_xval) {
978 if (!bcp || (*i)->get_x() > bcp->get_x()) {
980 before = bcp->view_index;
983 } else if ((*i)->get_x() > unit_xval) {
985 after = acp->view_index;
994 AutomationLine::is_last_point (ControlPoint& cp)
996 ModelRepresentation mr;
998 model_representation (cp, mr);
1000 // If the list is not empty, and the point is the last point in the list
1002 if (!alist.empty() && mr.end == alist.end()) {
1010 AutomationLine::is_first_point (ControlPoint& cp)
1012 ModelRepresentation mr;
1014 model_representation (cp, mr);
1016 // If the list is not empty, and the point is the first point in the list
1018 if (!alist.empty() && mr.start == alist.begin()) {
1025 // This is copied into AudioRegionGainLine
1027 AutomationLine::remove_point (ControlPoint& cp)
1029 ModelRepresentation mr;
1031 model_representation (cp, mr);
1033 trackview.editor.current_session()->begin_reversible_command (_("remove control point"));
1034 XMLNode &before = alist.get_state();
1036 alist.erase (mr.start, mr.end);
1038 trackview.editor.current_session()->add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
1039 trackview.editor.current_session()->commit_reversible_command ();
1040 trackview.editor.current_session()->set_dirty ();
1044 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
1045 double botfrac, double topfrac, list<Selectable*>& results)
1052 bool collecting = false;
1054 /* Curse X11 and its inverted coordinate system! */
1056 bot = (1.0 - topfrac) * _height;
1057 top = (1.0 - botfrac) * _height;
1059 nstart = max_frames;
1062 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1064 nframes_t when = (nframes_t) (*(*i)->model)->when;
1066 if (when >= start && when <= end) {
1068 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1071 (*i)->set_visible(true);
1073 nstart = min (nstart, when);
1074 nend = max (nend, when);
1080 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1082 nstart = max_frames;
1090 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1096 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1102 AutomationLine::set_selected_points (PointSelection& points)
1107 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1108 (*i)->selected = false;
1111 if (points.empty()) {
1115 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1117 if (&(*r).track != &trackview) {
1121 /* Curse X11 and its inverted coordinate system! */
1123 bot = (1.0 - (*r).high_fract) * _height;
1124 top = (1.0 - (*r).low_fract) * _height;
1126 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1128 double rstart, rend;
1130 rstart = trackview.editor.frame_to_unit ((*r).start);
1131 rend = trackview.editor.frame_to_unit ((*r).end);
1133 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1135 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1137 (*i)->selected = true;
1145 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1146 (*i)->show_color (false, !points_visible);
1152 AutomationLine::show_selection ()
1154 TimeSelection& time (trackview.editor.get_selection().time);
1156 // cerr << "show selection\n";
1158 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1160 (*i)->selected = false;
1162 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1163 double rstart, rend;
1165 rstart = trackview.editor.frame_to_unit ((*r).start);
1166 rend = trackview.editor.frame_to_unit ((*r).end);
1168 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1169 (*i)->selected = true;
1174 (*i)->show_color (false, !points_visible);
1179 AutomationLine::hide_selection ()
1181 // cerr << "hide selection\n";
1182 // show_selection ();
1186 AutomationLine::list_changed ()
1192 AutomationLine::reset_callback (const AutomationList& events)
1194 ALPoints tmp_points;
1195 uint32_t npoints = events.size();
1198 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1201 control_points.clear ();
1206 AutomationList::const_iterator ai;
1208 for (ai = events.const_begin(); ai != events.const_end(); ++ai) {
1210 double translated_y = (*ai)->value;
1211 model_to_view_y (translated_y);
1213 tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit ((*ai)->when),
1214 _height - (translated_y * _height)));
1217 determine_visible_control_points (tmp_points);
1221 AutomationLine::reset ()
1223 update_pending = false;
1229 alist.apply_to_points (*this, &AutomationLine::reset_callback);
1233 AutomationLine::clear ()
1235 /* parent must create command */
1236 XMLNode &before = get_state();
1238 trackview.editor.current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1239 trackview.editor.current_session()->commit_reversible_command ();
1240 trackview.editor.current_session()->set_dirty ();
1244 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1246 alist.modify (i, (nframes_t) x, y);
1250 AutomationLine::change_model_range (AutomationList::iterator start, AutomationList::iterator end, double xdelta, float ydelta)
1252 alist.move_range (start, end, xdelta, ydelta);
1256 AutomationLine::show_all_control_points ()
1258 points_visible = true;
1260 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1262 (*i)->set_visible (true);
1267 AutomationLine::hide_all_but_selected_control_points ()
1269 points_visible = false;
1271 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1272 if (!(*i)->selected) {
1273 (*i)->set_visible (false);
1279 AutomationLine::get_state (void)
1281 /* function as a proxy for the model */
1282 return alist.get_state();
1286 AutomationLine::set_state (const XMLNode &node)
1288 /* function as a proxy for the model */
1289 return alist.set_state (node);