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>
27 #include <ardour/automation_event.h>
28 #include <ardour/curve.h>
29 #include <ardour/dB.h>
31 #include "simplerect.h"
32 #include "automation_line.h"
33 #include "rgb_macros.h"
34 #include "ardour_ui.h"
35 #include "public_editor.h"
37 #include "selection.h"
38 #include "time_axis_view.h"
39 #include "point_selection.h"
40 #include "automation_selectable.h"
41 #include "automation_time_axis.h"
42 #include "public_editor.h"
44 #include <ardour/session.h>
50 using namespace ARDOUR;
51 using namespace Editing;
52 using namespace Gnome; // for Canvas
54 ControlPoint::ControlPoint (AutomationLine& al)
57 model = al.the_list().end();
66 item = new Canvas::SimpleRect (line.canvas_group());
67 item->property_draw() = true;
68 item->property_fill() = false;
69 item->property_fill_color_rgba() = color_map[cControlPointFill];
70 item->property_outline_color_rgba() = color_map[cControlPointOutline];
71 item->property_outline_pixels() = 1;
72 item->set_data ("control_point", this);
73 item->signal_event().connect (mem_fun (this, &ControlPoint::event_handler));
79 ControlPoint::ControlPoint (const ControlPoint& other, bool dummy_arg_to_force_special_copy_constructor)
87 view_index = other.view_index;
88 can_slide = other.can_slide;
91 _shape = other._shape;
95 item = new Canvas::SimpleRect (line.canvas_group());
96 item->property_fill() = false;
97 item->property_outline_color_rgba() = color_map[cEnteredControlPointOutline];
98 item->property_outline_pixels() = 1;
100 /* NOTE: no event handling in copied ControlPoints */
106 ControlPoint::~ControlPoint ()
108 gtk_object_destroy (GTK_OBJECT(item));
112 ControlPoint::event_handler (GdkEvent* event)
114 return PublicEditor::instance().canvas_control_point_event (event, item, this);
118 ControlPoint::hide ()
130 ControlPoint::set_visible (bool yn)
132 item->set_property ("draw", (gboolean) yn);
136 ControlPoint::reset (double x, double y, AutomationList::iterator mi, uint32_t vi, ShapeType shape)
140 move_to (x, y, shape);
144 ControlPoint::show_color (bool entered, bool hide_too)
148 item->set_property ("outline_color_rgba", color_map[cEnteredControlPointSelected]);
151 item->set_property ("outline_color_rgba", color_map[cEnteredControlPoint]);
159 item->set_property ("outline_color_rgba", color_map[cControlPointSelected]);
162 item->set_property ("outline_color_rgba", color_map[cControlPoint]);
171 ControlPoint::set_size (double sz)
177 item->set_property ("fill", (gboolean) TRUE);
179 item->set_property ("fill", (gboolean) FALSE);
183 move_to (_x, _y, _shape);
187 ControlPoint::move_to (double x, double y, ShapeType shape)
191 double half_size = rint(_size/2.0);
208 item->set_property ("x1", x1);
209 item->set_property ("x2", x2);
210 item->set_property ("y1", y - half_size);
211 item->set_property ("y2", y + half_size);
220 AutomationLine::AutomationLine (string name, TimeAxisView& tv, ArdourCanvas::Group& parent, AutomationList& al)
224 _parent_group (parent)
226 points_visible = false;
227 update_pending = false;
228 _vc_uses_gain_mapping = false;
231 terminal_points_can_slide = true;
234 group = new ArdourCanvas::Group (parent);
235 group->set_property ("x", 0.0);
236 group->set_property ("y", 0.0);
238 line = new ArdourCanvas::Line (*group);
239 line->set_property ("width_pixels", (guint)1);
241 line->signal_event().connect (mem_fun (*this, &AutomationLine::event_handler));
243 alist.StateChanged.connect (mem_fun(*this, &AutomationLine::list_changed));
246 AutomationLine::~AutomationLine ()
248 vector_delete (&control_points);
250 gtk_object_destroy (GTK_OBJECT(group));
254 AutomationLine::event_handler (GdkEvent* event)
256 return PublicEditor::instance().canvas_line_event (event, line, this);
260 AutomationLine::queue_reset ()
262 if (!update_pending) {
263 update_pending = true;
264 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &AutomationLine::reset));
269 AutomationLine::set_point_size (double sz)
271 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
277 AutomationLine::show ()
281 if (points_visible) {
282 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
291 AutomationLine::hide ()
294 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
301 AutomationLine::set_height (guint32 h)
306 if (_height > (guint32) TimeAxisView::Larger) {
307 set_point_size (8.0);
308 } else if (_height > (guint32) TimeAxisView::Normal) {
309 set_point_size (6.0);
311 set_point_size (4.0);
319 AutomationLine::set_line_color (uint32_t color)
322 line->set_property ("fill_color_rgba", color);
326 AutomationLine::set_verbose_cursor_uses_gain_mapping (bool yn)
328 if (yn != _vc_uses_gain_mapping) {
329 _vc_uses_gain_mapping = yn;
335 AutomationLine::nth (uint32_t n)
337 if (n < control_points.size()) {
338 return control_points[n];
345 AutomationLine::modify_view (ControlPoint& cp, double x, double y, bool with_push)
347 modify_view_point (cp, x, y, with_push);
352 AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push)
355 uint32_t last_movable = UINT_MAX;
356 double x_limit = DBL_MAX;
358 /* this just changes the current view. it does not alter
359 the model in any way at all.
362 /* clamp y-coord appropriately. y is supposed to be a normalized fraction (0.0-1.0),
363 and needs to be converted to a canvas unit distance.
368 y = _height - (y * _height);
372 /* x-coord cannot move beyond adjacent points or the start/end, and is
373 already in frames. it needs to be converted to canvas units.
376 x = trackview.editor.frame_to_unit (x);
378 /* clamp x position using view coordinates */
380 ControlPoint *before;
384 before = nth (cp.view_index - 1);
385 x = max (x, before->get_x()+1.0);
392 if (cp.view_index < control_points.size() - 1) {
394 after = nth (cp.view_index + 1);
396 /*if it is a "spike" leave the x alone */
398 if (after->get_x() - before->get_x() < 2) {
402 x = min (x, after->get_x()-1.0);
412 /* find the first point that can't move */
414 for (uint32_t n = cp.view_index + 1; (after = nth (n)) != 0; ++n) {
415 if (!after->can_slide) {
416 x_limit = after->get_x() - 1.0;
417 last_movable = after->view_index;
422 delta = x - cp.get_x();
427 /* leave the x-coordinate alone */
429 x = trackview.editor.frame_to_unit ((*cp.model)->when);
435 cp.move_to (x, y, ControlPoint::Full);
436 reset_line_coords (cp);
440 uint32_t limit = min (control_points.size(), (size_t)last_movable);
442 /* move the current point to wherever the user told it to go, subject
446 cp.move_to (min (x, x_limit), y, ControlPoint::Full);
447 reset_line_coords (cp);
449 /* now move all subsequent control points, to reflect the motion.
452 for (uint32_t i = cp.view_index + 1; i < limit; ++i) {
453 ControlPoint *p = nth (i);
457 new_x = min (p->get_x() + delta, x_limit);
458 p->move_to (new_x, p->get_y(), ControlPoint::Full);
459 reset_line_coords (*p);
466 AutomationLine::reset_line_coords (ControlPoint& cp)
468 line_points[cp.view_index].set_x (cp.get_x());
469 line_points[cp.view_index].set_y (cp.get_y());
473 AutomationLine::update_line ()
475 line->property_points() = line_points;
479 AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end)
484 update_pending = true;
486 for (uint32_t i = start; i <= end; ++i) {
488 sync_model_with_view_point(*p);
495 AutomationLine::model_representation (ControlPoint& cp, ModelRepresentation& mr)
497 /* part one: find out where the visual control point is.
498 initial results are in canvas units. ask the
499 line to convert them to something relevant.
502 mr.xval = (jack_nframes_t) floor (cp.get_x());
503 mr.yval = 1.0 - (cp.get_y() / _height);
506 /* if xval has not changed, set it directly from the model to avoid rounding errors */
508 if (mr.xval == trackview.editor.frame_to_unit((*cp.model)->when)) {
509 mr.xval = (jack_nframes_t) (*cp.model)->when;
511 mr.xval = trackview.editor.unit_to_frame (mr.xval);
515 /* virtual call: this will do the right thing
516 for whatever particular type of line we are.
519 view_to_model_y (mr.yval);
521 /* part 2: find out where the model point is now
524 mr.xpos = (jack_nframes_t) (*cp.model)->when;
525 mr.ypos = (*cp.model)->value;
527 /* part 3: get the position of the visual control
528 points before and after us.
531 ControlPoint* before;
535 before = nth (cp.view_index - 1);
540 after = nth (cp.view_index + 1);
543 mr.xmin = (jack_nframes_t) (*before->model)->when;
544 mr.ymin = (*before->model)->value;
545 mr.start = before->model;
554 mr.end = after->model;
564 AutomationLine::sync_model_from (ControlPoint& cp)
569 sync_model_with_view_point (cp);
571 /* we might have moved all points after `cp' by some amount
572 if we pressed the with_push modifyer some of the time during the drag
573 so all subsequent points have to be resynced
576 lasti = control_points.size() - 1;
579 update_pending = true;
581 while (p != &cp && lasti) {
582 sync_model_with_view_point (*p);
589 AutomationLine::sync_model_with_view_point (ControlPoint& cp)
591 ModelRepresentation mr;
594 model_representation (cp, mr);
596 /* part 4: how much are we changing the central point by */
598 ydelta = mr.yval - mr.ypos;
600 /* IMPORTANT: changing the model when the x-coordinate changes
601 may invalidate the iterators that we are using. this means that we have
602 to change the points before+after the one corresponding to the visual CP
603 first (their x-coordinate doesn't change). then we change the
606 apply the full change to the central point, and interpolate
607 in each direction to cover all model points represented
608 by the control point.
611 /* part 5: change all points before the primary point */
613 for (AutomationList::iterator i = mr.start; i != cp.model; ++i) {
617 delta = ydelta * ((*i)->when - mr.xmin) / (mr.xpos - mr.xmin);
619 /* x-coordinate (generally time) stays where it is,
620 y-coordinate moves by a certain amount.
623 update_pending = true;
624 change_model (i, (*i)->when, mr.yval + delta);
627 /* part 6: change later points */
629 AutomationList::iterator i = cp.model;
633 while (i != mr.end) {
637 delta = ydelta * (mr.xmax - (*i)->when) / (mr.xmax - mr.xpos);
639 /* x-coordinate (generally time) stays where it is,
640 y-coordinate moves by a certain amount.
643 update_pending = true;
644 change_model (i, (*i)->when, (*i)->value + delta);
649 /* part 7: change the primary point */
651 update_pending = true;
652 change_model (cp.model, mr.xval, mr.yval);
656 AutomationLine::determine_visible_control_points (ALPoints& points)
658 uint32_t view_index, pi, n;
659 AutomationList::iterator model;
661 double last_control_point_x = 0.0;
662 double last_control_point_y = 0.0;
663 uint32_t this_rx = 0;
664 uint32_t prev_rx = 0;
665 uint32_t this_ry = 0;
666 uint32_t prev_ry = 0;
669 /* hide all existing points, and the line */
671 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
677 if (points.empty()) {
681 npoints = points.size();
683 /* compute derivative/slope for the entire line */
685 slope = new double[npoints];
687 for (n = 0; n < npoints - 1; ++n) {
688 double xdelta = points[n+1].x - points[n].x;
689 double ydelta = points[n+1].y - points[n].y;
690 slope[n] = ydelta/xdelta;
693 /* read all points and decide which ones to show as control points */
697 for (model = alist.begin(), pi = 0; pi < npoints; ++model, ++pi) {
699 double tx = points[pi].x;
700 double ty = points[pi].y;
702 /* now ensure that the control_points vector reflects the current curve
703 state, but don't plot control points too close together. also, don't
704 plot a series of points all with the same value.
706 always plot the first and last points, of course.
709 if (invalid_point (points, pi)) {
710 /* for some reason, we are supposed to ignore this point,
711 but still keep track of the model index.
716 if (pi > 0 && pi < npoints - 1) {
717 if (slope[pi] == slope[pi-1]) {
719 /* no reason to display this point */
725 /* need to round here. the ultimate coordinates are integer
726 pixels, so tiny deltas in the coords will be eliminated
727 and we end up with "colinear" line segments. since the
728 line rendering code in libart doesn't like this very
729 much, we eliminate them here. don't do this for the first and last
733 this_rx = (uint32_t) rint (tx);
734 this_ry = (unsigned long) rint (ty);
736 if (view_index && pi != npoints && (this_rx == prev_rx) && (this_ry == prev_ry)) {
741 /* ok, we should display this point */
743 if (view_index >= control_points.size()) {
744 /* make sure we have enough control points */
746 ControlPoint* ncp = new ControlPoint (*this);
748 if (_height > (guint32) TimeAxisView::Larger) {
750 } else if (_height > (guint32) TimeAxisView::Normal) {
756 control_points.push_back (ncp);
759 ControlPoint::ShapeType shape;
761 if (!terminal_points_can_slide) {
763 control_points[view_index]->can_slide = false;
765 shape = ControlPoint::Start;
767 shape = ControlPoint::Full;
769 } else if (pi == npoints - 1) {
770 control_points[view_index]->can_slide = false;
771 shape = ControlPoint::End;
773 control_points[view_index]->can_slide = true;
774 shape = ControlPoint::Full;
777 control_points[view_index]->can_slide = true;
778 shape = ControlPoint::Full;
781 last_control_point_x = tx;
782 last_control_point_y = ty;
784 control_points[view_index]->reset (tx, ty, model, view_index, shape);
789 /* finally, control visibility */
791 if (_visible && points_visible) {
792 control_points[view_index]->show ();
793 control_points[view_index]->set_visible (true);
795 if (!points_visible) {
796 control_points[view_index]->set_visible (false);
803 /* discard extra CP's to avoid confusing ourselves */
805 while (control_points.size() > view_index) {
806 ControlPoint* cp = control_points.back();
807 control_points.pop_back ();
811 if (!terminal_points_can_slide) {
812 control_points.back()->can_slide = false;
817 if (view_index > 1) {
819 npoints = view_index;
821 /* reset the line coordinates */
823 while (line_points.size() < npoints) {
824 line_points.push_back (Art::Point (0,0));
827 for (view_index = 0; view_index < npoints; ++view_index) {
828 line_points[view_index].set_x (control_points[view_index]->get_x());
829 line_points[view_index].set_y (control_points[view_index]->get_y());
832 line->property_points() = line_points;
839 set_selected_points (trackview.editor.get_selection().points);
843 AutomationLine::get_verbose_cursor_string (float fraction)
847 if (_vc_uses_gain_mapping) {
848 if (fraction == 0.0) {
849 snprintf (buf, sizeof (buf), "-inf dB");
851 snprintf (buf, sizeof (buf), "%.1fdB", coefficient_to_dB (slider_position_to_gain (fraction)));
854 snprintf (buf, sizeof (buf), "%.2f", fraction);
861 AutomationLine::invalid_point (ALPoints& p, uint32_t index)
863 return p[index].x == max_frames && p[index].y == DBL_MAX;
867 AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
869 p[index].x = max_frames;
870 p[index].y = DBL_MAX;
874 AutomationLine::start_drag (ControlPoint* cp, float fraction)
876 if (trackview.editor.current_session() == 0) { /* how? */
883 str = _("automation event move");
885 str = _("automation range drag");
888 trackview.editor.current_session()->begin_reversible_command (str);
889 trackview.editor.current_session()->add_undo (get_memento());
891 first_drag_fraction = fraction;
892 last_drag_fraction = fraction;
897 AutomationLine::point_drag (ControlPoint& cp, jack_nframes_t x, float fraction, bool with_push)
899 modify_view (cp, x, fraction, with_push);
904 AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
906 double ydelta = fraction - last_drag_fraction;
908 last_drag_fraction = fraction;
915 for (uint32_t i = i1 ; i <= i2; i++) {
917 modify_view_point (*cp, trackview.editor.unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
926 AutomationLine::end_drag (ControlPoint* cp)
931 sync_model_from (*cp);
933 sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
936 update_pending = false;
938 trackview.editor.current_session()->add_redo_no_execute (get_memento());
939 trackview.editor.current_session()->commit_reversible_command ();
940 trackview.editor.current_session()->set_dirty ();
945 AutomationLine::control_points_adjacent (double xval, uint32_t & before, uint32_t& after)
947 ControlPoint *bcp = 0;
948 ControlPoint *acp = 0;
951 /* xval is in frames */
953 unit_xval = trackview.editor.frame_to_unit (xval);
955 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
957 if ((*i)->get_x() <= unit_xval) {
959 if (!bcp || (*i)->get_x() > bcp->get_x()) {
961 before = bcp->view_index;
964 } else if ((*i)->get_x() > unit_xval) {
966 after = acp->view_index;
975 AutomationLine::is_last_point (ControlPoint& cp)
977 ModelRepresentation mr;
979 model_representation (cp, mr);
981 // If the list is not empty, and the point is the last point in the list
983 if (!alist.empty() && mr.end == alist.end()) {
991 AutomationLine::is_first_point (ControlPoint& cp)
993 ModelRepresentation mr;
995 model_representation (cp, mr);
997 // If the list is not empty, and the point is the first point in the list
999 if (!alist.empty() && mr.start == alist.begin()) {
1006 // This is copied into AudioRegionGainLine
1008 AutomationLine::remove_point (ControlPoint& cp)
1010 ModelRepresentation mr;
1012 model_representation (cp, mr);
1014 trackview.editor.current_session()->begin_reversible_command (_("remove control point"));
1015 trackview.editor.current_session()->add_undo (get_memento());
1017 alist.erase (mr.start, mr.end);
1019 trackview.editor.current_session()->add_redo_no_execute (get_memento());
1020 trackview.editor.current_session()->commit_reversible_command ();
1021 trackview.editor.current_session()->set_dirty ();
1025 AutomationLine::get_selectables (jack_nframes_t& start, jack_nframes_t& end,
1026 double botfrac, double topfrac, list<Selectable*>& results)
1031 jack_nframes_t nstart;
1032 jack_nframes_t nend;
1033 bool collecting = false;
1035 /* Curse X11 and its inverted coordinate system! */
1037 bot = (1.0 - topfrac) * _height;
1038 top = (1.0 - botfrac) * _height;
1040 nstart = max_frames;
1043 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1045 jack_nframes_t when = (jack_nframes_t) (*(*i)->model)->when;
1047 if (when >= start && when <= end) {
1049 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1052 (*i)->set_visible(true);
1054 nstart = min (nstart, when);
1055 nend = max (nend, when);
1061 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1063 nstart = max_frames;
1071 results.push_back (new AutomationSelectable (nstart, nend, botfrac, topfrac, trackview));
1077 AutomationLine::get_inverted_selectables (Selection&, list<Selectable*>& results)
1083 AutomationLine::set_selected_points (PointSelection& points)
1088 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1089 (*i)->selected = false;
1092 if (points.empty()) {
1096 for (PointSelection::iterator r = points.begin(); r != points.end(); ++r) {
1098 if (&(*r).track != &trackview) {
1102 /* Curse X11 and its inverted coordinate system! */
1104 bot = (1.0 - (*r).high_fract) * _height;
1105 top = (1.0 - (*r).low_fract) * _height;
1107 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1109 double rstart, rend;
1111 rstart = trackview.editor.frame_to_unit ((*r).start);
1112 rend = trackview.editor.frame_to_unit ((*r).end);
1114 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1116 if ((*i)->get_y() >= bot && (*i)->get_y() <= top) {
1118 (*i)->selected = true;
1126 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1127 (*i)->show_color (false, !points_visible);
1133 AutomationLine::show_selection ()
1135 TimeSelection& time (trackview.editor.get_selection().time);
1137 // cerr << "show selection\n";
1139 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1141 (*i)->selected = false;
1143 for (list<AudioRange>::iterator r = time.begin(); r != time.end(); ++r) {
1144 double rstart, rend;
1146 rstart = trackview.editor.frame_to_unit ((*r).start);
1147 rend = trackview.editor.frame_to_unit ((*r).end);
1149 if ((*i)->get_x() >= rstart && (*i)->get_x() <= rend) {
1150 (*i)->selected = true;
1155 (*i)->show_color (false, !points_visible);
1160 AutomationLine::hide_selection ()
1162 // cerr << "hide selection\n";
1163 // show_selection ();
1167 // This is copied into AudioRegionGainLine
1169 AutomationLine::get_memento ()
1171 return alist.get_memento();
1175 AutomationLine::list_changed (Change ignored)
1181 AutomationLine::reset_callback (const AutomationList& events)
1183 ALPoints tmp_points;
1184 uint32_t npoints = events.size();
1187 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1190 control_points.clear ();
1195 AutomationList::const_iterator ai;
1197 for (ai = events.const_begin(); ai != events.const_end(); ++ai) {
1199 double translated_y;
1201 translated_y = (*ai)->value;
1202 model_to_view_y (translated_y);
1204 tmp_points.push_back (ALPoint (trackview.editor.frame_to_unit ((*ai)->when),
1205 _height - (translated_y * _height)));
1208 determine_visible_control_points (tmp_points);
1212 AutomationLine::reset ()
1214 update_pending = false;
1220 alist.apply_to_points (*this, &AutomationLine::reset_callback);
1224 AutomationLine::clear ()
1226 /* parent must create command */
1227 trackview.editor.current_session()->add_undo (get_memento());
1229 trackview.editor.current_session()->add_redo_no_execute (get_memento());
1230 trackview.editor.current_session()->commit_reversible_command ();
1231 trackview.editor.current_session()->set_dirty ();
1235 AutomationLine::change_model (AutomationList::iterator i, double x, double y)
1237 alist.modify (i, (jack_nframes_t) x, y);
1241 AutomationLine::change_model_range (AutomationList::iterator start, AutomationList::iterator end, double xdelta, float ydelta)
1243 alist.move_range (start, end, xdelta, ydelta);
1247 AutomationLine::show_all_control_points ()
1249 points_visible = true;
1251 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1253 (*i)->set_visible (true);
1258 AutomationLine::hide_all_but_selected_control_points ()
1260 points_visible = false;
1262 for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
1263 if (!(*i)->selected) {
1264 (*i)->set_visible (false);