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>
27 #include <pbd/stacktrace.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;
238 group = new ArdourCanvas::Group (parent);
239 group->property_x() = 0.0;
240 group->property_y() = 0.0;
242 line = new ArdourCanvas::Line (*group);
243 line->property_width_pixels() = (guint)1;
244 line->set_data ("line", this);
246 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
248 alist.StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
250 trackview.session().register_with_memento_command_factory(alist.id(), this);
254 AutomationLine::~AutomationLine ()
256 vector_delete (&control_points);
261 AutomationLine::event_handler (GdkEvent* event)
263 return PublicEditor::instance().canvas_line_event (event, line, this);
267 AutomationLine::queue_reset ()
269 if (!update_pending) {
270 update_pending = true;
271 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
276 AutomationLine::show ()
280 if (points_visible) {
281 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
290 AutomationLine::hide ()
293 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
300 AutomationLine::control_point_box_size ()
302 if (_height > TimeAxisView::hLarger) {
304 } else if (_height > (guint32) TimeAxisView::hNormal) {
311 AutomationLine::set_y_position_and_height (guint32 y, guint32 h)
313 bool changed = false;
315 if (y != _y_position) {
323 double const bsz = control_point_box_size();
325 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
326 (*i)->set_size (bsz);
337 AutomationLine::set_line_color (uint32_t color)
340 line->property_fill_color_rgba() = color;
344 AutomationLine::set_verbose_cursor_uses_gain_mapping (bool yn)
346 if (yn != _vc_uses_gain_mapping) {
347 _vc_uses_gain_mapping = yn;
353 AutomationLine::nth (uint32_t n)
355 if (n < control_points.size()) {
356 return control_points[n];
363 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
366 uint32_t last_movable = UINT_MAX;
367 double x_limit = DBL_MAX;
369 /* this just changes the current view. it does not alter
370 the model in any way at all.
373 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
374 and needs to be converted to a canvas unit distance.
379 y = _y_position + _height - (y * _height);
383 /* x-coord cannot move beyond adjacent points or the start/end, and is
384 already in frames. it needs to be converted to canvas units.
387 x = trackview.editor.frame_to_unit (x);
389 /* clamp x position using view coordinates */
391 ControlPoint *before;
395 before = nth (cp.view_index - 1);
396 x = max (x, before->get_x()+1.0);
403 if (cp.view_index < control_points.size() - 1) {
405 after = nth (cp.view_index + 1);
407 /*if it is a "spike" leave the x alone */
409 if (after->get_x() - before->get_x() < 2) {
413 x = min (x, after->get_x()-1.0);
423 /* find the first point that can't move */
425 for (uint32_t n = cp.view_index + 1; (after = nth (n)) != 0; ++n) {
426 if (!after->can_slide) {
427 x_limit = after->get_x() - 1.0;
428 last_movable = after->view_index;
433 delta = x - cp.get_x();
438 /* leave the x-coordinate alone */
440 x = trackview.editor.frame_to_unit ((*cp.model)->when);
446 cp.move_to (x, y, ControlPoint::Full);
447 reset_line_coords (cp);
451 uint32_t limit = min (control_points.size(), (size_t)last_movable);
453 /* move the current point to wherever the user told it to go, subject
457 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
458 reset_line_coords (cp);
460 /* now move all subsequent control points, to reflect the motion.
463 for (uint32_t i = cp.view_index + 1; i < limit; ++i) {
464 ControlPoint *p = nth (i);
468 new_x = min (p->get_x() + delta, x_limit);
469 p->move_to (new_x, p->get_y(), ControlPoint::Full);
470 reset_line_coords (*p);
477 AutomationLine::reset_line_coords (ControlPoint& cp)
479 if (cp.view_index < line_points.size()) {
480 line_points[cp.view_index].set_x (cp.get_x());
481 line_points[cp.view_index].set_y (cp.get_y());
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, false, 0);
500 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
502 /* part one: find out where the visual control point is.
503 initial results are in canvas units. ask the
504 line to convert them to something relevant.
507 mr.xval = (nframes_t) floor (cp.get_x());
508 mr.yval = 1.0 - ( (cp.get_y() - _y_position) / _height);
510 /* if xval has not changed, set it directly from the model to avoid rounding errors */
512 if (mr.xval == trackview.editor.frame_to_unit((*cp.model)->when)) {
513 mr.xval = (nframes_t) (*cp.model)->when;
515 mr.xval = trackview.editor.unit_to_frame (mr.xval);
518 /* virtual call: this will do the right thing
519 for whatever particular type of line we are.
522 view_to_model_y (mr.yval);
524 /* part 2: find out where the model point is now
527 mr.xpos = (nframes_t) (*cp.model)->when;
528 mr.ypos = (*cp.model)->value;
530 /* part 3: get the position of the visual control
531 points before and after us.
534 ControlPoint* before;
538 before = nth (cp.view_index - 1);
543 after = nth (cp.view_index + 1);
546 mr.xmin = (nframes_t) (*before->model)->when;
547 mr.ymin = (*before->model)->value;
548 mr.start = before->model;
557 mr.end = after->model;
567 AutomationLine::determine_visible_control_points (ALPoints& points)
569 uint32_t view_index, pi, n;
570 AutomationList::iterator model;
572 double last_control_point_x = 0.0;
573 double last_control_point_y = 0.0;
574 uint32_t this_rx = 0;
575 uint32_t prev_rx = 0;
576 uint32_t this_ry = 0;
577 uint32_t prev_ry = 0;
582 /* hide all existing points, and the line */
586 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
593 if (points.empty()) {
597 npoints = points.size();
599 /* compute derivative/slope for the entire line */
601 slope = new double[npoints];
603 for (n = 0; n < npoints - 1; ++n) {
604 double xdelta = points[n+1].x - points[n].x;
605 double ydelta = points[n+1].y - points[n].y;
606 slope[n] = ydelta/xdelta;
609 box_size = (uint32_t) control_point_box_size ();
611 /* read all points and decide which ones to show as control points */
615 for (model = alist.begin(), pi = 0; pi < npoints; ++model, ++pi) {
617 double tx = points[pi].x;
618 double ty = points[pi].y;
620 /* now ensure that the control_points vector reflects the current curve
621 state, but don't plot control points too close together. also, don't
622 plot a series of points all with the same value.
624 always plot the first and last points, of course.
627 if (invalid_point (points, pi)) {
628 /* for some reason, we are supposed to ignore this point,
629 but still keep track of the model index.
634 if (pi > 0 && pi < npoints - 1) {
635 if (slope[pi] == slope[pi-1]) {
637 /* no reason to display this point */
643 /* need to round here. the ultimate coordinates are integer
644 pixels, so tiny deltas in the coords will be eliminated
645 and we end up with "colinear" line segments. since the
646 line rendering code in libart doesn't like this very
647 much, we eliminate them here. don't do this for the first and last
651 this_rx = (uint32_t) rint (tx);
652 this_ry = (uint32_t) rint (ty);
654 if (view_index && pi != npoints && /* not the first, not the last */
655 (((this_rx == prev_rx) && (this_ry == prev_ry)) || /* same point */
656 (((this_rx - prev_rx) < (box_size + 2)) && /* not identical, but still too close horizontally */
657 (abs ((int)(this_ry - prev_ry)) < (int) (box_size + 2))))) { /* too close vertically */
661 /* ok, we should display this point */
663 if (view_index >= cpsize) {
665 /* make sure we have enough control points */
667 ControlPoint* ncp = new ControlPoint (*this);
669 ncp->set_size (box_size);
671 control_points.push_back (ncp);
675 ControlPoint::ShapeType shape;
677 if (!terminal_points_can_slide) {
679 control_points[view_index]->can_slide = false;
681 shape = ControlPoint::Start;
683 shape = ControlPoint::Full;
685 } else if (pi == npoints - 1) {
686 control_points[view_index]->can_slide = false;
687 shape = ControlPoint::End;
689 control_points[view_index]->can_slide = true;
690 shape = ControlPoint::Full;
693 control_points[view_index]->can_slide = true;
694 shape = ControlPoint::Full;
697 last_control_point_x = tx;
698 last_control_point_y = ty;
700 control_points[view_index]->reset (tx, ty, model, view_index, shape);
705 /* finally, control visibility */
707 if (_visible && points_visible) {
708 control_points[view_index]->show ();
709 control_points[view_index]->set_visible (true);
711 if (!points_visible) {
712 control_points[view_index]->set_visible (false);
719 /* discard extra CP's to avoid confusing ourselves */
721 while (control_points.size() > view_index) {
722 ControlPoint* cp = control_points.back();
723 control_points.pop_back ();
727 if (!terminal_points_can_slide) {
728 control_points.back()->can_slide = false;
733 if (view_index > 1) {
735 npoints = view_index;
737 /* reset the line coordinates */
739 while (line_points.size() < npoints) {
740 line_points.push_back (Art::Point (0,0));
743 while (line_points.size() > npoints) {
744 line_points.pop_back ();
747 for (view_index = 0; view_index < npoints; ++view_index) {
748 line_points[view_index].set_x (control_points[view_index]->get_x());
749 line_points[view_index].set_y (control_points[view_index]->get_y());
752 line->property_points() = line_points;
760 set_selected_points (trackview.editor.get_selection().points);
765 AutomationLine::get_verbose_cursor_string (float fraction)
769 if (_vc_uses_gain_mapping) {
770 if (fraction == 0.0) {
771 snprintf (buf, sizeof (buf), "-inf dB");
773 snprintf (buf, sizeof (buf), "%.1fdB", coefficient_to_dB (slider_position_to_gain (fraction)));
776 snprintf (buf, sizeof (buf), "%.2f", fraction);
783 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
785 return p[index].x == max_frames && p[index].y == DBL_MAX;
789 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
791 p[index].x = max_frames;
792 p[index].y = DBL_MAX;
796 AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction)
798 if (trackview.editor.current_session() == 0) { /* how? */
805 str = _("automation event move");
807 str = _("automation range drag");
810 trackview.editor.current_session()->begin_reversible_command (str);
811 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(alist, &get_state(), 0));
815 first_drag_fraction = fraction;
816 last_drag_fraction = fraction;
822 AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
825 drag_distance += (x - drag_x);
827 drag_distance -= (drag_x - x);
832 modify_view_point (cp, x, fraction, with_push);
834 if (line_points.size() > 1) {
835 line->property_points() = line_points;
839 did_push = with_push;
843 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
845 double const ydelta = fraction - last_drag_fraction;
847 did_push = with_push;
849 last_drag_fraction = fraction;
856 for (uint32_t i = i1 ; i <= i2; i++) {
858 modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y() + _y_position) /_height) + ydelta, with_push);
861 if (line_points.size() > 1) {
862 line->property_points() = line_points;
869 AutomationLine::end_drag (ControlPoint* cp)
878 sync_model_with_view_point (*cp, did_push, drag_distance);
880 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
885 update_pending = false;
887 trackview.editor.current_session()->add_command (new MementoCommand<AutomationList>(alist, 0, &alist.get_state()));
888 trackview.editor.current_session()->commit_reversible_command ();
889 trackview.editor.current_session()->set_dirty ();
894 AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
896 ModelRepresentation mr;
899 model_representation (cp, mr);
901 /* how much are we changing the central point by */
903 ydelta = mr.yval - mr.ypos;
906 apply the full change to the central point, and interpolate
907 on both axes to cover all model points represented
908 by the control point.
911 /* change all points before the primary point */
913 for (AutomationList::iterator i = mr.start; i != cp.model; ++i) {
915 double fract = ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
916 double y_delta = ydelta * fract;
917 double x_delta = distance * fract;
921 if (y_delta || x_delta) {
922 alist.modify (i, (*i)->when + x_delta, mr.ymin + y_delta);
926 /* change the primary point */
928 update_pending = true;
929 alist.modify (cp.model, mr.xval, mr.yval);
932 /* change later points */
934 AutomationList::iterator i = cp.model;
938 while (i != mr.end) {
940 double delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
942 /* all later points move by the same distance along the x-axis as the main point */
945 alist.modify (i, (*i)->when + distance, (*i)->value + delta);
953 /* move all points after the range represented by the view by the same distance
954 as the main point moved.
957 alist.slide (mr.end, drag_distance);
963 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
965 ControlPoint *bcp = 0;
966 ControlPoint *acp = 0;
969 /* xval is in frames */
971 unit_xval = trackview.editor.frame_to_unit (xval);
973 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
975 if ((*i)->get_x() <= unit_xval) {
977 if (!bcp || (*i)->get_x() > bcp->get_x()) {
979 before = bcp->view_index;
982 } else if ((*i)->get_x() > unit_xval) {
984 after = acp->view_index;
993 AutomationLine::is_last_point (ControlPoint& cp)
995 ModelRepresentation mr;
997 model_representation (cp, mr);
999 // If the list is not empty, and the point is the last point in the list
1001 if (!alist.empty() && mr.end == alist.end()) {
1009 AutomationLine::is_first_point (ControlPoint& cp)
1011 ModelRepresentation mr;
1013 model_representation (cp, mr);
1015 // If the list is not empty, and the point is the first point in the list
1017 if (!alist.empty() && mr.start == alist.begin()) {
1024 // This is copied into AudioRegionGainLine
1026 AutomationLine::remove_point (ControlPoint& cp)
1028 ModelRepresentation mr;
1030 model_representation (cp, mr);
1032 trackview.editor.current_session()->begin_reversible_command (_("remove control point"));
1033 XMLNode &before = alist.get_state();
1035 alist.erase (mr.start, mr.end);
1037 trackview.editor.current_session()->add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
1038 trackview.editor.current_session()->commit_reversible_command ();
1039 trackview.editor.current_session()->set_dirty ();
1043 AutomationLine::get_selectables (nframes_t& start, nframes_t& end,
1044 double botfrac, double topfrac, list<Selectable*>& results)
1051 bool collecting = false;
1053 /* Curse X11 and its inverted coordinate system! */
1055 bot = _y_position + (1.0 - topfrac) * _height;
1056 top = _y_position + (1.0 - botfrac) * _height;
1058 nstart = max_frames;
1061 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1063 nframes_t when = (nframes_t) (*(*i)->model)->when;
1065 if (when >= start && when <= end) {
1067 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1070 (*i)->set_visible(true);
1072 nstart = min (nstart, when);
1073 nend = max (nend, when);
1079 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1081 nstart = max_frames;
1089 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1095 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1101 AutomationLine::set_selected_points (PointSelection& points)
1106 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1107 (*i)->selected = false;
1110 if (points.empty()) {
1114 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1116 if (&(*r).track != &trackview) {
1120 /* Curse X11 and its inverted coordinate system! */
1122 bot = _y_position + (1.0 - (*r).high_fract) * _height;
1123 top = _y_position + (1.0 - (*r).low_fract) * _height;
1125 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1127 double rstart, rend;
1129 rstart = trackview.editor.frame_to_unit ((*r).start);
1130 rend = trackview.editor.frame_to_unit ((*r).end);
1132 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1134 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1136 (*i)->selected = true;
1144 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1145 (*i)->show_color (false, !points_visible);
1150 void AutomationLine::set_colors() {
1151 set_line_color( color_map[cAutomationLine] );
1152 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1153 (*i)->show_color (false, !points_visible);
1158 AutomationLine::show_selection ()
1160 TimeSelection& time (trackview.editor.get_selection().time);
1162 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1164 (*i)->selected = false;
1166 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1167 double rstart, rend;
1169 rstart = trackview.editor.frame_to_unit ((*r).start);
1170 rend = trackview.editor.frame_to_unit ((*r).end);
1172 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1173 (*i)->selected = true;
1178 (*i)->show_color (false, !points_visible);
1183 AutomationLine::hide_selection ()
1185 // show_selection ();
1189 AutomationLine::list_changed ()
1195 AutomationLine::reset_callback (const AutomationList& events)
1197 ALPoints tmp_points;
1198 uint32_t npoints = events.size();
1201 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1204 control_points.clear ();
1209 AutomationList::const_iterator ai;
1211 for (ai = events.const_begin(); ai != events.const_end(); ++ai) {
1213 double translated_y = (*ai)->value;
1214 model_to_view_y (translated_y);
1216 tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit ((*ai)->when),
1217 _y_position + _height - (translated_y * _height)));
1220 determine_visible_control_points (tmp_points);
1224 AutomationLine::reset ()
1226 update_pending = false;
1232 alist.apply_to_points (*this, &AutomationLine::reset_callback);
1236 AutomationLine::clear ()
1238 /* parent must create command */
1239 XMLNode &before = get_state();
1241 trackview.editor.current_session()->add_command (new MementoCommand<AutomationLine>(*this, &before, &get_state()));
1242 trackview.editor.current_session()->commit_reversible_command ();
1243 trackview.editor.current_session()->set_dirty ();
1247 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1252 AutomationLine::change_model_range (AutomationList::iterator start, AutomationList::iterator end, double xdelta, float ydelta)
1254 alist.move_range (start, end, xdelta, ydelta);
1258 AutomationLine::show_all_control_points ()
1260 points_visible = true;
1262 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1264 (*i)->set_visible (true);
1269 AutomationLine::hide_all_but_selected_control_points ()
1271 points_visible = false;
1273 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1274 if (!(*i)->selected) {
1275 (*i)->set_visible (false);
1281 AutomationLine::get_state (void)
1283 /* function as a proxy for the model */
1284 return alist.get_state();
1288 AutomationLine::set_state (const XMLNode &node)
1290 /* function as a proxy for the model */
1291 return alist.set_state (node);