Code

f9086950dc3011c06ae3494a8df2684cd75dd729
[inkscape.git] / src / ui / tool / transform-handle-set.cpp
1 /** @file
2  * Affine transform handles component
3  */
4 /* Authors:
5  *   Krzysztof Kosiński <tweenk.pl@gmail.com>
6  *
7  * Copyright (C) 2009 Authors
8  * Released under GNU GPL, read the file 'COPYING' for more information
9  */
11 #include <math.h>
12 #include <algorithm>
13 #include <glib.h>
14 #include <glib/gi18n.h>
15 #include <gdk/gdk.h>
16 #include <2geom/transforms.h>
17 #include "desktop.h"
18 #include "desktop-handles.h"
19 #include "display/sodipodi-ctrlrect.h"
20 #include "preferences.h"
21 #include "ui/tool/commit-events.h"
22 #include "ui/tool/control-point.h"
23 #include "ui/tool/event-utils.h"
24 #include "ui/tool/transform-handle-set.h"
26 // FIXME BRAIN DAMAGE WARNING: this is a global variable in select-context.cpp
27 // It should be moved to a header
28 extern GdkPixbuf *handles[];
29 GType sp_select_context_get_type();
31 namespace Inkscape {
32 namespace UI {
34 namespace {
35 Gtk::AnchorType corner_to_anchor(unsigned c) {
36     switch (c % 4) {
37     case 0: return Gtk::ANCHOR_NE;
38     case 1: return Gtk::ANCHOR_NW;
39     case 2: return Gtk::ANCHOR_SW;
40     default: return Gtk::ANCHOR_SE;
41     }
42 }
43 Gtk::AnchorType side_to_anchor(unsigned s) {
44     switch (s % 4) {
45     case 0: return Gtk::ANCHOR_N;
46     case 1: return Gtk::ANCHOR_W;
47     case 2: return Gtk::ANCHOR_S;
48     default: return Gtk::ANCHOR_E;
49     }
50 }
52 // TODO move those two functions into a common place
53 double snap_angle(double a) {
54     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
55     int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000);
56     double unit_angle = M_PI / snaps;
57     return CLAMP(unit_angle * round(a / unit_angle), -M_PI, M_PI);
58 }
59 double snap_increment_degrees() {
60     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
61     int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000);
62     return 180.0 / snaps;
63 }
65 ControlPoint::ColorSet thandle_cset = {
66     {0x000000ff, 0x000000ff},
67     {0x00ff6600, 0x000000ff},
68     {0x00ff6600, 0x000000ff}
69 };
71 ControlPoint::ColorSet center_cset = {
72     {0x00000000, 0x000000ff},
73     {0x00000000, 0xff0000b0},
74     {0x00000000, 0xff0000b0}    
75 };
76 } // anonymous namespace
78 /** Base class for node transform handles to simplify implementation */
79 class TransformHandle : public ControlPoint {
80 public:
81     TransformHandle(TransformHandleSet &th, Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb)
82         : ControlPoint(th._desktop, Geom::Point(), anchor, pb, &thandle_cset,
83             th._transform_handle_group)
84         , _th(th)
85     {
86         setVisible(false);
87         signal_grabbed.connect(
88             sigc::bind_return(
89                 sigc::hide(
90                     sigc::mem_fun(*this, &TransformHandle::_grabbedHandler)),
91                 false));
92         signal_dragged.connect(
93             sigc::hide<0>(
94                 sigc::mem_fun(*this, &TransformHandle::_draggedHandler)));
95         signal_ungrabbed.connect(
96             sigc::hide(
97                 sigc::mem_fun(*this, &TransformHandle::_ungrabbedHandler)));
98     }
99 protected:
100     virtual void startTransform() {}
101     virtual void endTransform() {}
102     virtual Geom::Matrix computeTransform(Geom::Point const &pos, GdkEventMotion *event) = 0;
103     virtual CommitEvent getCommitEvent() = 0;
105     Geom::Matrix _last_transform;
106     Geom::Point _origin;
107     TransformHandleSet &_th;
108 private:
109     void _grabbedHandler() {
110         _origin = position();
111         _last_transform.setIdentity();
112         startTransform();
114         _th._setActiveHandle(this);
115         _cset = &invisible_cset;
116         _setState(_state);
117     }
118     void _draggedHandler(Geom::Point &new_pos, GdkEventMotion *event)
119     {
120         Geom::Matrix t = computeTransform(new_pos, event);
121         // protect against degeneracies
122         if (t.isSingular()) return;
123         Geom::Matrix incr = _last_transform.inverse() * t;
124         if (incr.isSingular()) return;
125         _th.signal_transform.emit(incr);
126         _last_transform = t;
127     }
128     void _ungrabbedHandler() {
129         _th._clearActiveHandle();
130         _cset = &thandle_cset;
131         _setState(_state);
132         endTransform();
133         _th.signal_commit.emit(getCommitEvent());
134     }
135 };
137 class ScaleHandle : public TransformHandle {
138 public:
139     ScaleHandle(TransformHandleSet &th, Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb)
140         : TransformHandle(th, anchor, pb)
141     {}
142 protected:
143     virtual Glib::ustring _getTip(unsigned state) {
144         if (state_held_control(state)) {
145             if (state_held_shift(state)) {
146                 return C_("Transform handle tip",
147                     "<b>Shift+Ctrl:</b> scale uniformly about the rotation center");
148             }
149             return C_("Transform handle tip", "<b>Ctrl:</b> scale uniformly");
150         }
151         if (state_held_shift(state)) {
152             if (state_held_alt(state)) {
153                 return C_("Transform handle tip",
154                     "<b>Shift+Alt:</b> scale using an integer ratio about the rotation center");
155             }
156             return C_("Transform handle tip", "<b>Shift:</b> scale from the rotation center");
157         }
158         if (state_held_alt(state)) {
159             return C_("Transform handle tip", "<b>Alt:</b> scale using an integer ratio");
160         }
161         return C_("Transform handle tip", "<b>Scale handle:</b> drag to scale the selection");
162     }
164     virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
165         return format_tip(C_("Transform handle tip",
166             "Scale by %.2f%% x %.2f%%"), _last_scale_x * 100, _last_scale_y * 100);
167     }
169     virtual bool _hasDragTips() { return true; }
171     static double _last_scale_x, _last_scale_y;
172 };
173 double ScaleHandle::_last_scale_x = 1.0;
174 double ScaleHandle::_last_scale_y = 1.0;
176 /// Corner scaling handle for node transforms
177 class ScaleCornerHandle : public ScaleHandle {
178 public:
179     ScaleCornerHandle(TransformHandleSet &th, unsigned corner)
180         : ScaleHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner))
181         , _corner(corner)
182     {}
183 protected:
184     virtual void startTransform() {
185         _sc_center = _th.rotationCenter();
186         _sc_opposite = _th.bounds().corner(_corner + 2);
187         _last_scale_x = _last_scale_y = 1.0;
188     }
189     virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) {
190         Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite;
191         Geom::Point vold = _origin - scc, vnew = new_pos - scc;
192         // avoid exploding the selection
193         if (Geom::are_near(vold[Geom::X], 0) || Geom::are_near(vold[Geom::Y], 0))
194             return Geom::identity();
196         double scale[2] = { vnew[Geom::X] / vold[Geom::X], vnew[Geom::Y] / vold[Geom::Y] };
197         if (held_alt(*event)) {
198             for (unsigned i = 0; i < 2; ++i) {
199                 if (scale[i] >= 1.0) scale[i] = round(scale[i]);
200                 else scale[i] = 1.0 / round(1.0 / scale[i]);
201             }
202         } else if (held_control(*event)) {
203             scale[0] = scale[1] = std::min(scale[0], scale[1]);
204         }
205         _last_scale_x = scale[0];
206         _last_scale_y = scale[1];
207         Geom::Matrix t = Geom::Translate(-scc)
208             * Geom::Scale(scale[0], scale[1])
209             * Geom::Translate(scc);
210         return t;
211     }
212     virtual CommitEvent getCommitEvent() {
213         return _last_transform.isUniformScale()
214             ? COMMIT_MOUSE_SCALE_UNIFORM
215             : COMMIT_MOUSE_SCALE;
216     }
217 private:
218     static Glib::RefPtr<Gdk::Pixbuf> _corner_to_pixbuf(unsigned c) {
219         sp_select_context_get_type();
220         switch (c % 2) {
221         case 0: return Glib::wrap(handles[1], true);
222         default: return Glib::wrap(handles[0], true);
223         }
224     }
225     Geom::Point _sc_center;
226     Geom::Point _sc_opposite;
227     unsigned _corner;
228 };
230 /// Side scaling handle for node transforms
231 class ScaleSideHandle : public ScaleHandle {
232 public:
233     ScaleSideHandle(TransformHandleSet &th, unsigned side)
234         : ScaleHandle(th, side_to_anchor(side), _side_to_pixbuf(side))
235         , _side(side)
236     {}
237 protected:
238     virtual void startTransform() {
239         _sc_center = _th.rotationCenter();
240         Geom::Rect b = _th.bounds();
241         _sc_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3));
242         _last_scale_x = _last_scale_y = 1.0;
243     }
244     virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) {
245         Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite;
246         Geom::Point vs;
247         Geom::Dim2 d1 = static_cast<Geom::Dim2>((_side + 1) % 2);
248         Geom::Dim2 d2 = static_cast<Geom::Dim2>(_side % 2);
250         // avoid exploding the selection
251         if (Geom::are_near(scc[d1], _origin[d1]))
252             return Geom::identity();
254         vs[d1] = (new_pos - scc)[d1] / (_origin - scc)[d1];
255         if (held_alt(*event)) {
256             if (vs[d1] >= 1.0) vs[d1] = round(vs[d1]);
257             else vs[d1] = 1.0 / round(1.0 / vs[d1]);
258         }
259         vs[d2] = held_control(*event) ? vs[d1] : 1.0;
261         _last_scale_x = vs[Geom::X];
262         _last_scale_y = vs[Geom::Y];
263         Geom::Matrix t = Geom::Translate(-scc)
264             * Geom::Scale(vs)
265             * Geom::Translate(scc);
266         return t;
267     }
268     virtual CommitEvent getCommitEvent() {
269         return _last_transform.isUniformScale()
270             ? COMMIT_MOUSE_SCALE_UNIFORM
271             : COMMIT_MOUSE_SCALE;
272     }
273 private:
274     static Glib::RefPtr<Gdk::Pixbuf> _side_to_pixbuf(unsigned c) {
275         sp_select_context_get_type();
276         switch (c % 2) {
277         case 0: return Glib::wrap(handles[3], true);
278         default: return Glib::wrap(handles[2], true);
279         }
280     }
281     Geom::Point _sc_center;
282     Geom::Point _sc_opposite;
283     unsigned _side;
284 };
286 /// Rotation handle for node transforms
287 class RotateHandle : public TransformHandle {
288 public:
289     RotateHandle(TransformHandleSet &th, unsigned corner)
290         : TransformHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner))
291         , _corner(corner)
292     {}
293 protected:
295     virtual void startTransform() {
296         _rot_center = _th.rotationCenter();
297         _rot_opposite = _th.bounds().corner(_corner + 2);
298         _last_angle = 0;
299     }
301     virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event)
302     {
303         Geom::Point rotc = held_shift(*event) ? _rot_opposite : _rot_center;
304         double angle = Geom::angle_between(_origin - rotc, new_pos - rotc);
305         if (held_control(*event)) {
306             angle = snap_angle(angle);
307         }
308         _last_angle = angle;
309         Geom::Matrix t = Geom::Translate(-rotc)
310             * Geom::Rotate(angle)
311             * Geom::Translate(rotc);
312         return t;
313     }
315     virtual CommitEvent getCommitEvent() { return COMMIT_MOUSE_ROTATE; }
317     virtual Glib::ustring _getTip(unsigned state) {
318         if (state_held_shift(state)) {
319             if (state_held_control(state)) {
320                 return format_tip(C_("Transform handle tip",
321                     "<b>Shift+Ctrl:</b> rotate around the opposite corner and snap "
322                     "angle to %f° increments"), snap_increment_degrees());
323             }
324             return C_("Transform handle tip", "<b>Shift:</b> rotate around the opposite corner");
325         }
326         if (state_held_control(state)) {
327             return format_tip(C_("Transform handle tip",
328                 "<b>Ctrl:</b> snap angle to %f° increments"), snap_increment_degrees());
329         }
330         return C_("Transform handle tip", "<b>Rotation handle:</b> drag to rotate "
331             "the selection around the rotation center");
332     }
334     virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
335         return format_tip(C_("Transform handle tip", "Rotate by %.2f°"),
336             _last_angle * 360.0);
337     }
339     virtual bool _hasDragTips() { return true; }
341 private:
342     static Glib::RefPtr<Gdk::Pixbuf> _corner_to_pixbuf(unsigned c) {
343         sp_select_context_get_type();
344         switch (c % 4) {
345         case 0: return Glib::wrap(handles[10], true);
346         case 1: return Glib::wrap(handles[8], true);
347         case 2: return Glib::wrap(handles[6], true);
348         default: return Glib::wrap(handles[4], true);
349         }
350     }
351     Geom::Point _rot_center;
352     Geom::Point _rot_opposite;
353     unsigned _corner;
354     static double _last_angle;
355 };
356 double RotateHandle::_last_angle = 0;
358 class SkewHandle : public TransformHandle {
359 public:
360     SkewHandle(TransformHandleSet &th, unsigned side)
361         : TransformHandle(th, side_to_anchor(side), _side_to_pixbuf(side))
362         , _side(side)
363     {}
365 protected:
367     virtual void startTransform() {
368         _skew_center = _th.rotationCenter();
369         Geom::Rect b = _th.bounds();
370         _skew_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3));
371         _last_angle = 0;
372         _last_horizontal = _side % 2;
373     }
375     virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event)
376     {
377         Geom::Point scc = held_shift(*event) ? _skew_center : _skew_opposite;
378         // d1 and d2 are reversed with respect to ScaleSideHandle
379         Geom::Dim2 d1 = static_cast<Geom::Dim2>(_side % 2);
380         Geom::Dim2 d2 = static_cast<Geom::Dim2>((_side + 1) % 2);
381         Geom::Point proj, scale(1.0, 1.0);
383         // Skew handles allow scaling up to integer multiples of the original size
384         // in the second direction; prevent explosions
385         // TODO should the scaling part be only active with Alt?
386         if (!Geom::are_near(_origin[d2], scc[d2])) {
387             scale[d2] = (new_pos - scc)[d2] / (_origin - scc)[d2];
388         }
390         if (scale[d2] < 1.0) {
391             scale[d2] = copysign(1.0, scale[d2]);
392         } else {
393             scale[d2] = floor(scale[d2]);
394         }
396         // Calculate skew angle. The angle is calculated with regards to the point obtained
397         // by projecting the handle position on the relevant side of the bounding box.
398         // This avoids degeneracies when moving the skew angle over the rotation center
399         proj[d1] = new_pos[d1];
400         proj[d2] = scc[d2] + (_origin[d2] - scc[d2]) * scale[d2];
401         double angle = 0;
402         if (!Geom::are_near(proj[d2], scc[d2]))
403             angle = Geom::angle_between(_origin - scc, proj - scc);
404         if (held_control(*event)) angle = snap_angle(angle);
406         // skew matrix has the from [[1, k],[0, 1]] for horizontal skew
407         // and [[1,0],[k,1]] for vertical skew.
408         Geom::Matrix skew = Geom::identity();
409         // correct the sign of the tangent
410         skew[d2 + 1] = (d1 == Geom::X ? -1.0 : 1.0) * tan(angle);
412         _last_angle = angle;
413         Geom::Matrix t = Geom::Translate(-scc)
414             * Geom::Scale(scale) * skew
415             * Geom::Translate(scc);
416         return t;
417     }
419     virtual CommitEvent getCommitEvent() {
420         return _side % 2
421             ? COMMIT_MOUSE_SKEW_Y
422             : COMMIT_MOUSE_SKEW_X;
423     }
425     virtual Glib::ustring _getTip(unsigned state) {
426         if (state_held_shift(state)) {
427             if (state_held_control(state)) {
428                 return format_tip(C_("Transform handle tip",
429                     "<b>Shift+Ctrl:</b> skew about the rotation center with snapping "
430                     "to %f° increments"), snap_increment_degrees());
431             }
432             return C_("Transform handle tip", "<b>Shift:</b> skew about the rotation center");
433         }
434         if (state_held_control(state)) {
435             return format_tip(C_("Transform handle tip",
436                 "<b>Ctrl:</b> snap skew angle to %f° increments"), snap_increment_degrees());
437         }
438         return C_("Transform handle tip",
439             "<b>Skew handle:</b> drag to skew (shear) selection about "
440             "the opposite handle");
441     }
443     virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
444         if (_last_horizontal) {
445             return format_tip(C_("Transform handle tip", "Skew horizontally by %.2f°"),
446                 _last_angle * 360.0);
447         } else {
448             return format_tip(C_("Transform handle tip", "Skew vertically by %.2f°"),
449                 _last_angle * 360.0);
450         }
451     }
453     virtual bool _hasDragTips() { return true; }
455 private:
457     static Glib::RefPtr<Gdk::Pixbuf> _side_to_pixbuf(unsigned s) {
458         sp_select_context_get_type();
459         switch (s % 4) {
460         case 0: return Glib::wrap(handles[9], true);
461         case 1: return Glib::wrap(handles[7], true);
462         case 2: return Glib::wrap(handles[5], true);
463         default: return Glib::wrap(handles[11], true);
464         }
465     }
466     Geom::Point _skew_center;
467     Geom::Point _skew_opposite;
468     unsigned _side;
469     static bool _last_horizontal;
470     static double _last_angle;
471 };
472 bool SkewHandle::_last_horizontal = false;
473 double SkewHandle::_last_angle = 0;
475 class RotationCenter : public ControlPoint {
476 public:
477     RotationCenter(TransformHandleSet &th)
478         : ControlPoint(th._desktop, Geom::Point(), Gtk::ANCHOR_CENTER, _get_pixbuf(),
479             &center_cset, th._transform_handle_group)
480         , _th(th)
481     {
482         setVisible(false);
483     }
485 protected:
487     virtual Glib::ustring _getTip(unsigned /*state*/) {
488         return C_("Transform handle tip",
489             "<b>Rotation center:</b> drag to change the origin of transforms");
490     }
492 private:
494     static Glib::RefPtr<Gdk::Pixbuf> _get_pixbuf() {
495         sp_select_context_get_type();
496         return Glib::wrap(handles[12], true);
497     }
499     TransformHandleSet &_th;
500 };
502 TransformHandleSet::TransformHandleSet(SPDesktop *d, SPCanvasGroup *th_group)
503     : Manipulator(d)
504     , _active(0)
505     , _transform_handle_group(th_group)
506     , _mode(MODE_SCALE)
507     , _in_transform(false)
508     , _visible(true)
510     _trans_outline = static_cast<CtrlRect*>(sp_canvas_item_new(sp_desktop_controls(_desktop),
511         SP_TYPE_CTRLRECT, NULL));
512     sp_canvas_item_hide(_trans_outline);
513     _trans_outline->setDashed(true);
515     for (unsigned i = 0; i < 4; ++i) {
516         _scale_corners[i] = new ScaleCornerHandle(*this, i);
517         _scale_sides[i] = new ScaleSideHandle(*this, i);
518         _rot_corners[i] = new RotateHandle(*this, i);
519         _skew_sides[i] = new SkewHandle(*this, i);
520     }
521     _center = new RotationCenter(*this);
522     // when transforming, update rotation center position
523     signal_transform.connect(sigc::mem_fun(*_center, &RotationCenter::transform));
526 TransformHandleSet::~TransformHandleSet()
528     for (unsigned i = 0; i < 17; ++i) {
529         delete _handles[i];
530     }
533 /** Sets the mode of transform handles (scale or rotate). */
534 void TransformHandleSet::setMode(Mode m)
536     _mode = m;
537     _updateVisibility(_visible);
540 Geom::Rect TransformHandleSet::bounds()
542     return Geom::Rect(*_scale_corners[0], *_scale_corners[2]);
545 ControlPoint &TransformHandleSet::rotationCenter()
547     return *_center;
550 void TransformHandleSet::setVisible(bool v)
552     if (_visible != v) {
553         _visible = v;
554         _updateVisibility(_visible);
555     }
558 void TransformHandleSet::setBounds(Geom::Rect const &r, bool preserve_center)
560     if (_in_transform) {
561         _trans_outline->setRectangle(r);
562     } else {
563         for (unsigned i = 0; i < 4; ++i) {
564             _scale_corners[i]->move(r.corner(i));
565             _scale_sides[i]->move(Geom::middle_point(r.corner(i), r.corner(i+1)));
566             _rot_corners[i]->move(r.corner(i));
567             _skew_sides[i]->move(Geom::middle_point(r.corner(i), r.corner(i+1)));
568         }
569         if (!preserve_center) _center->move(r.midpoint());
570         if (_visible) _updateVisibility(true);
571     }
574 bool TransformHandleSet::event(GdkEvent*)
576     return false;
579 void TransformHandleSet::_emitTransform(Geom::Matrix const &t)
581     signal_transform.emit(t);
582     _center->transform(t);
585 void TransformHandleSet::_setActiveHandle(ControlPoint *th)
587     _active = th;
588     if (_in_transform)
589         throw std::logic_error("Transform initiated when another transform in progress");
590     _in_transform = true;
591     // hide all handles except the active one
592     _updateVisibility(false);
593     sp_canvas_item_show(_trans_outline);
596 void TransformHandleSet::_clearActiveHandle()
598     // This can only be called from handles, so they had to be visible before _setActiveHandle
599     sp_canvas_item_hide(_trans_outline);
600     _active = 0;
601     _in_transform = false;
602     _updateVisibility(_visible);
605 /** Update the visibility of transformation handles according to settings and the dimensions
606  * of the bounding box. It hides the handles that would have no effect or lead to
607  * discontinuities. Additionally, side handles for which there is no space are not shown. */
608 void TransformHandleSet::_updateVisibility(bool v)
610     if (v) {
611         Geom::Rect b = bounds();
612         Geom::Point handle_size(
613             gdk_pixbuf_get_width(handles[0]) / _desktop->current_zoom(),
614             gdk_pixbuf_get_height(handles[0]) / _desktop->current_zoom());
615         Geom::Point bp = b.dimensions();
617         // do not scale when the bounding rectangle has zero width or height
618         bool show_scale = (_mode == MODE_SCALE) && !Geom::are_near(b.minExtent(), 0);
619         // do not rotate if the bounding rectangle is degenerate
620         bool show_rotate = (_mode == MODE_ROTATE_SKEW) && !Geom::are_near(b.maxExtent(), 0);
621         bool show_scale_side[2], show_skew[2];
623         // show sides if:
624         // a) there is enough space between corner handles, or
625         // b) corner handles are not shown, but side handles make sense
626         // this affects horizontal and vertical scale handles; skew handles never
627         // make sense if rotate handles are not shown
628         for (unsigned i = 0; i < 2; ++i) {
629             Geom::Dim2 d = static_cast<Geom::Dim2>(i);
630             Geom::Dim2 otherd = static_cast<Geom::Dim2>((i+1)%2);
631             show_scale_side[i] = (_mode == MODE_SCALE);
632             show_scale_side[i] &= (show_scale ? bp[d] >= handle_size[d]
633                 : !Geom::are_near(bp[otherd], 0));
634             show_skew[i] = (show_rotate && bp[d] >= handle_size[d]
635                 && !Geom::are_near(bp[otherd], 0));
636         }
637         for (unsigned i = 0; i < 4; ++i) {
638             _scale_corners[i]->setVisible(show_scale);
639             _rot_corners[i]->setVisible(show_rotate);
640             _scale_sides[i]->setVisible(show_scale_side[i%2]);
641             _skew_sides[i]->setVisible(show_skew[i%2]);
642         }
643         // show rotation center if there is enough space (?)
644         _center->setVisible(show_rotate /*&& bp[Geom::X] > handle_size[Geom::X]
645             && bp[Geom::Y] > handle_size[Geom::Y]*/);
646     } else {
647         for (unsigned i = 0; i < 17; ++i) {
648             if (_handles[i] != _active)
649                 _handles[i]->setVisible(false);
650         }
651     }
652     
655 } // namespace UI
656 } // namespace Inkscape
658 /*
659   Local Variables:
660   mode:c++
661   c-file-style:"stroustrup"
662   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
663   indent-tabs-mode:nil
664   fill-column:99
665   End:
666 */
667 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :