ef93a3767c32777722c67854f5fd6d19c66e7a14
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 }
88 protected:
89 virtual void startTransform() {}
90 virtual void endTransform() {}
91 virtual Geom::Matrix computeTransform(Geom::Point const &pos, GdkEventMotion *event) = 0;
92 virtual CommitEvent getCommitEvent() = 0;
94 Geom::Matrix _last_transform;
95 Geom::Point _origin;
96 TransformHandleSet &_th;
97 private:
98 virtual bool grabbed(GdkEventMotion *) {
99 _origin = position();
100 _last_transform.setIdentity();
101 startTransform();
103 _th._setActiveHandle(this);
104 _cset = &invisible_cset;
105 _setState(_state);
106 return false;
107 }
108 virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event)
109 {
110 Geom::Matrix t = computeTransform(new_pos, event);
111 // protect against degeneracies
112 if (t.isSingular()) return;
113 Geom::Matrix incr = _last_transform.inverse() * t;
114 if (incr.isSingular()) return;
115 _th.signal_transform.emit(incr);
116 _last_transform = t;
117 }
118 virtual void ungrabbed(GdkEventButton *) {
119 _th._clearActiveHandle();
120 _cset = &thandle_cset;
121 _setState(_state);
122 endTransform();
123 _th.signal_commit.emit(getCommitEvent());
124 }
125 };
127 class ScaleHandle : public TransformHandle {
128 public:
129 ScaleHandle(TransformHandleSet &th, Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb)
130 : TransformHandle(th, anchor, pb)
131 {}
132 protected:
133 virtual Glib::ustring _getTip(unsigned state) {
134 if (state_held_control(state)) {
135 if (state_held_shift(state)) {
136 return C_("Transform handle tip",
137 "<b>Shift+Ctrl</b>: scale uniformly about the rotation center");
138 }
139 return C_("Transform handle tip", "<b>Ctrl:</b> scale uniformly");
140 }
141 if (state_held_shift(state)) {
142 if (state_held_alt(state)) {
143 return C_("Transform handle tip",
144 "<b>Shift+Alt</b>: scale using an integer ratio about the rotation center");
145 }
146 return C_("Transform handle tip", "<b>Shift</b>: scale from the rotation center");
147 }
148 if (state_held_alt(state)) {
149 return C_("Transform handle tip", "<b>Alt</b>: scale using an integer ratio");
150 }
151 return C_("Transform handle tip", "<b>Scale handle</b>: drag to scale the selection");
152 }
154 virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
155 return format_tip(C_("Transform handle tip",
156 "Scale by %.2f%% x %.2f%%"), _last_scale_x * 100, _last_scale_y * 100);
157 }
159 virtual bool _hasDragTips() { return true; }
161 static double _last_scale_x, _last_scale_y;
162 };
163 double ScaleHandle::_last_scale_x = 1.0;
164 double ScaleHandle::_last_scale_y = 1.0;
166 /// Corner scaling handle for node transforms
167 class ScaleCornerHandle : public ScaleHandle {
168 public:
169 ScaleCornerHandle(TransformHandleSet &th, unsigned corner)
170 : ScaleHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner))
171 , _corner(corner)
172 {}
173 protected:
174 virtual void startTransform() {
175 _sc_center = _th.rotationCenter();
176 _sc_opposite = _th.bounds().corner(_corner + 2);
177 _last_scale_x = _last_scale_y = 1.0;
178 }
179 virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) {
180 Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite;
181 Geom::Point vold = _origin - scc, vnew = new_pos - scc;
182 // avoid exploding the selection
183 if (Geom::are_near(vold[Geom::X], 0) || Geom::are_near(vold[Geom::Y], 0))
184 return Geom::identity();
186 double scale[2] = { vnew[Geom::X] / vold[Geom::X], vnew[Geom::Y] / vold[Geom::Y] };
187 if (held_alt(*event)) {
188 for (unsigned i = 0; i < 2; ++i) {
189 if (scale[i] >= 1.0) scale[i] = round(scale[i]);
190 else scale[i] = 1.0 / round(1.0 / scale[i]);
191 }
192 } else if (held_control(*event)) {
193 scale[0] = scale[1] = std::min(scale[0], scale[1]);
194 }
195 _last_scale_x = scale[0];
196 _last_scale_y = scale[1];
197 Geom::Matrix t = Geom::Translate(-scc)
198 * Geom::Scale(scale[0], scale[1])
199 * Geom::Translate(scc);
200 return t;
201 }
202 virtual CommitEvent getCommitEvent() {
203 return _last_transform.isUniformScale()
204 ? COMMIT_MOUSE_SCALE_UNIFORM
205 : COMMIT_MOUSE_SCALE;
206 }
207 private:
208 static Glib::RefPtr<Gdk::Pixbuf> _corner_to_pixbuf(unsigned c) {
209 sp_select_context_get_type();
210 switch (c % 2) {
211 case 0: return Glib::wrap(handles[1], true);
212 default: return Glib::wrap(handles[0], true);
213 }
214 }
215 Geom::Point _sc_center;
216 Geom::Point _sc_opposite;
217 unsigned _corner;
218 };
220 /// Side scaling handle for node transforms
221 class ScaleSideHandle : public ScaleHandle {
222 public:
223 ScaleSideHandle(TransformHandleSet &th, unsigned side)
224 : ScaleHandle(th, side_to_anchor(side), _side_to_pixbuf(side))
225 , _side(side)
226 {}
227 protected:
228 virtual void startTransform() {
229 _sc_center = _th.rotationCenter();
230 Geom::Rect b = _th.bounds();
231 _sc_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3));
232 _last_scale_x = _last_scale_y = 1.0;
233 }
234 virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) {
235 Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite;
236 Geom::Point vs;
237 Geom::Dim2 d1 = static_cast<Geom::Dim2>((_side + 1) % 2);
238 Geom::Dim2 d2 = static_cast<Geom::Dim2>(_side % 2);
240 // avoid exploding the selection
241 if (Geom::are_near(scc[d1], _origin[d1]))
242 return Geom::identity();
244 vs[d1] = (new_pos - scc)[d1] / (_origin - scc)[d1];
245 if (held_alt(*event)) {
246 if (vs[d1] >= 1.0) vs[d1] = round(vs[d1]);
247 else vs[d1] = 1.0 / round(1.0 / vs[d1]);
248 }
249 vs[d2] = held_control(*event) ? vs[d1] : 1.0;
251 _last_scale_x = vs[Geom::X];
252 _last_scale_y = vs[Geom::Y];
253 Geom::Matrix t = Geom::Translate(-scc)
254 * Geom::Scale(vs)
255 * Geom::Translate(scc);
256 return t;
257 }
258 virtual CommitEvent getCommitEvent() {
259 return _last_transform.isUniformScale()
260 ? COMMIT_MOUSE_SCALE_UNIFORM
261 : COMMIT_MOUSE_SCALE;
262 }
263 private:
264 static Glib::RefPtr<Gdk::Pixbuf> _side_to_pixbuf(unsigned c) {
265 sp_select_context_get_type();
266 switch (c % 2) {
267 case 0: return Glib::wrap(handles[3], true);
268 default: return Glib::wrap(handles[2], true);
269 }
270 }
271 Geom::Point _sc_center;
272 Geom::Point _sc_opposite;
273 unsigned _side;
274 };
276 /// Rotation handle for node transforms
277 class RotateHandle : public TransformHandle {
278 public:
279 RotateHandle(TransformHandleSet &th, unsigned corner)
280 : TransformHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner))
281 , _corner(corner)
282 {}
283 protected:
285 virtual void startTransform() {
286 _rot_center = _th.rotationCenter();
287 _rot_opposite = _th.bounds().corner(_corner + 2);
288 _last_angle = 0;
289 }
291 virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event)
292 {
293 Geom::Point rotc = held_shift(*event) ? _rot_opposite : _rot_center;
294 double angle = Geom::angle_between(_origin - rotc, new_pos - rotc);
295 if (held_control(*event)) {
296 angle = snap_angle(angle);
297 }
298 _last_angle = angle;
299 Geom::Matrix t = Geom::Translate(-rotc)
300 * Geom::Rotate(angle)
301 * Geom::Translate(rotc);
302 return t;
303 }
305 virtual CommitEvent getCommitEvent() { return COMMIT_MOUSE_ROTATE; }
307 virtual Glib::ustring _getTip(unsigned state) {
308 if (state_held_shift(state)) {
309 if (state_held_control(state)) {
310 return format_tip(C_("Transform handle tip",
311 "<b>Shift+Ctrl</b>: rotate around the opposite corner and snap "
312 "angle to %f° increments"), snap_increment_degrees());
313 }
314 return C_("Transform handle tip", "<b>Shift</b>: rotate around the opposite corner");
315 }
316 if (state_held_control(state)) {
317 return format_tip(C_("Transform handle tip",
318 "<b>Ctrl</b>: snap angle to %f° increments"), snap_increment_degrees());
319 }
320 return C_("Transform handle tip", "<b>Rotation handle</b>: drag to rotate "
321 "the selection around the rotation center");
322 }
324 virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
325 return format_tip(C_("Transform handle tip", "Rotate by %.2f°"),
326 _last_angle * 360.0);
327 }
329 virtual bool _hasDragTips() { return true; }
331 private:
332 static Glib::RefPtr<Gdk::Pixbuf> _corner_to_pixbuf(unsigned c) {
333 sp_select_context_get_type();
334 switch (c % 4) {
335 case 0: return Glib::wrap(handles[10], true);
336 case 1: return Glib::wrap(handles[8], true);
337 case 2: return Glib::wrap(handles[6], true);
338 default: return Glib::wrap(handles[4], true);
339 }
340 }
341 Geom::Point _rot_center;
342 Geom::Point _rot_opposite;
343 unsigned _corner;
344 static double _last_angle;
345 };
346 double RotateHandle::_last_angle = 0;
348 class SkewHandle : public TransformHandle {
349 public:
350 SkewHandle(TransformHandleSet &th, unsigned side)
351 : TransformHandle(th, side_to_anchor(side), _side_to_pixbuf(side))
352 , _side(side)
353 {}
355 protected:
357 virtual void startTransform() {
358 _skew_center = _th.rotationCenter();
359 Geom::Rect b = _th.bounds();
360 _skew_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3));
361 _last_angle = 0;
362 _last_horizontal = _side % 2;
363 }
365 virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event)
366 {
367 Geom::Point scc = held_shift(*event) ? _skew_center : _skew_opposite;
368 // d1 and d2 are reversed with respect to ScaleSideHandle
369 Geom::Dim2 d1 = static_cast<Geom::Dim2>(_side % 2);
370 Geom::Dim2 d2 = static_cast<Geom::Dim2>((_side + 1) % 2);
371 Geom::Point proj, scale(1.0, 1.0);
373 // Skew handles allow scaling up to integer multiples of the original size
374 // in the second direction; prevent explosions
375 // TODO should the scaling part be only active with Alt?
376 if (!Geom::are_near(_origin[d2], scc[d2])) {
377 scale[d2] = (new_pos - scc)[d2] / (_origin - scc)[d2];
378 }
380 if (scale[d2] < 1.0) {
381 scale[d2] = copysign(1.0, scale[d2]);
382 } else {
383 scale[d2] = floor(scale[d2]);
384 }
386 // Calculate skew angle. The angle is calculated with regards to the point obtained
387 // by projecting the handle position on the relevant side of the bounding box.
388 // This avoids degeneracies when moving the skew angle over the rotation center
389 proj[d1] = new_pos[d1];
390 proj[d2] = scc[d2] + (_origin[d2] - scc[d2]) * scale[d2];
391 double angle = 0;
392 if (!Geom::are_near(proj[d2], scc[d2]))
393 angle = Geom::angle_between(_origin - scc, proj - scc);
394 if (held_control(*event)) angle = snap_angle(angle);
396 // skew matrix has the from [[1, k],[0, 1]] for horizontal skew
397 // and [[1,0],[k,1]] for vertical skew.
398 Geom::Matrix skew = Geom::identity();
399 // correct the sign of the tangent
400 skew[d2 + 1] = (d1 == Geom::X ? -1.0 : 1.0) * tan(angle);
402 _last_angle = angle;
403 Geom::Matrix t = Geom::Translate(-scc)
404 * Geom::Scale(scale) * skew
405 * Geom::Translate(scc);
406 return t;
407 }
409 virtual CommitEvent getCommitEvent() {
410 return _side % 2
411 ? COMMIT_MOUSE_SKEW_Y
412 : COMMIT_MOUSE_SKEW_X;
413 }
415 virtual Glib::ustring _getTip(unsigned state) {
416 if (state_held_shift(state)) {
417 if (state_held_control(state)) {
418 return format_tip(C_("Transform handle tip",
419 "<b>Shift+Ctrl</b>: skew about the rotation center with snapping "
420 "to %f° increments"), snap_increment_degrees());
421 }
422 return C_("Transform handle tip", "<b>Shift</b>: skew about the rotation center");
423 }
424 if (state_held_control(state)) {
425 return format_tip(C_("Transform handle tip",
426 "<b>Ctrl</b>: snap skew angle to %f° increments"), snap_increment_degrees());
427 }
428 return C_("Transform handle tip",
429 "<b>Skew handle</b>: drag to skew (shear) selection about "
430 "the opposite handle");
431 }
433 virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) {
434 if (_last_horizontal) {
435 return format_tip(C_("Transform handle tip", "Skew horizontally by %.2f°"),
436 _last_angle * 360.0);
437 } else {
438 return format_tip(C_("Transform handle tip", "Skew vertically by %.2f°"),
439 _last_angle * 360.0);
440 }
441 }
443 virtual bool _hasDragTips() { return true; }
445 private:
447 static Glib::RefPtr<Gdk::Pixbuf> _side_to_pixbuf(unsigned s) {
448 sp_select_context_get_type();
449 switch (s % 4) {
450 case 0: return Glib::wrap(handles[9], true);
451 case 1: return Glib::wrap(handles[7], true);
452 case 2: return Glib::wrap(handles[5], true);
453 default: return Glib::wrap(handles[11], true);
454 }
455 }
456 Geom::Point _skew_center;
457 Geom::Point _skew_opposite;
458 unsigned _side;
459 static bool _last_horizontal;
460 static double _last_angle;
461 };
462 bool SkewHandle::_last_horizontal = false;
463 double SkewHandle::_last_angle = 0;
465 class RotationCenter : public ControlPoint {
466 public:
467 RotationCenter(TransformHandleSet &th)
468 : ControlPoint(th._desktop, Geom::Point(), Gtk::ANCHOR_CENTER, _get_pixbuf(),
469 ¢er_cset, th._transform_handle_group)
470 , _th(th)
471 {
472 setVisible(false);
473 }
475 protected:
477 virtual Glib::ustring _getTip(unsigned /*state*/) {
478 return C_("Transform handle tip",
479 "<b>Rotation center</b>: drag to change the origin of transforms");
480 }
482 private:
484 static Glib::RefPtr<Gdk::Pixbuf> _get_pixbuf() {
485 sp_select_context_get_type();
486 return Glib::wrap(handles[12], true);
487 }
489 TransformHandleSet &_th;
490 };
492 TransformHandleSet::TransformHandleSet(SPDesktop *d, SPCanvasGroup *th_group)
493 : Manipulator(d)
494 , _active(0)
495 , _transform_handle_group(th_group)
496 , _mode(MODE_SCALE)
497 , _in_transform(false)
498 , _visible(true)
499 {
500 _trans_outline = static_cast<CtrlRect*>(sp_canvas_item_new(sp_desktop_controls(_desktop),
501 SP_TYPE_CTRLRECT, NULL));
502 sp_canvas_item_hide(_trans_outline);
503 _trans_outline->setDashed(true);
505 for (unsigned i = 0; i < 4; ++i) {
506 _scale_corners[i] = new ScaleCornerHandle(*this, i);
507 _scale_sides[i] = new ScaleSideHandle(*this, i);
508 _rot_corners[i] = new RotateHandle(*this, i);
509 _skew_sides[i] = new SkewHandle(*this, i);
510 }
511 _center = new RotationCenter(*this);
512 // when transforming, update rotation center position
513 signal_transform.connect(sigc::mem_fun(*_center, &RotationCenter::transform));
514 }
516 TransformHandleSet::~TransformHandleSet()
517 {
518 for (unsigned i = 0; i < 17; ++i) {
519 delete _handles[i];
520 }
521 }
523 /** Sets the mode of transform handles (scale or rotate). */
524 void TransformHandleSet::setMode(Mode m)
525 {
526 _mode = m;
527 _updateVisibility(_visible);
528 }
530 Geom::Rect TransformHandleSet::bounds()
531 {
532 return Geom::Rect(*_scale_corners[0], *_scale_corners[2]);
533 }
535 ControlPoint &TransformHandleSet::rotationCenter()
536 {
537 return *_center;
538 }
540 void TransformHandleSet::setVisible(bool v)
541 {
542 if (_visible != v) {
543 _visible = v;
544 _updateVisibility(_visible);
545 }
546 }
548 void TransformHandleSet::setBounds(Geom::Rect const &r, bool preserve_center)
549 {
550 if (_in_transform) {
551 _trans_outline->setRectangle(r);
552 } else {
553 for (unsigned i = 0; i < 4; ++i) {
554 _scale_corners[i]->move(r.corner(i));
555 _scale_sides[i]->move(Geom::middle_point(r.corner(i), r.corner(i+1)));
556 _rot_corners[i]->move(r.corner(i));
557 _skew_sides[i]->move(Geom::middle_point(r.corner(i), r.corner(i+1)));
558 }
559 if (!preserve_center) _center->move(r.midpoint());
560 if (_visible) _updateVisibility(true);
561 }
562 }
564 bool TransformHandleSet::event(GdkEvent*)
565 {
566 return false;
567 }
569 void TransformHandleSet::_emitTransform(Geom::Matrix const &t)
570 {
571 signal_transform.emit(t);
572 _center->transform(t);
573 }
575 void TransformHandleSet::_setActiveHandle(ControlPoint *th)
576 {
577 _active = th;
578 if (_in_transform)
579 throw std::logic_error("Transform initiated when another transform in progress");
580 _in_transform = true;
581 // hide all handles except the active one
582 _updateVisibility(false);
583 sp_canvas_item_show(_trans_outline);
584 }
586 void TransformHandleSet::_clearActiveHandle()
587 {
588 // This can only be called from handles, so they had to be visible before _setActiveHandle
589 sp_canvas_item_hide(_trans_outline);
590 _active = 0;
591 _in_transform = false;
592 _updateVisibility(_visible);
593 }
595 /** Update the visibility of transformation handles according to settings and the dimensions
596 * of the bounding box. It hides the handles that would have no effect or lead to
597 * discontinuities. Additionally, side handles for which there is no space are not shown. */
598 void TransformHandleSet::_updateVisibility(bool v)
599 {
600 if (v) {
601 Geom::Rect b = bounds();
602 Geom::Point handle_size(
603 gdk_pixbuf_get_width(handles[0]) / _desktop->current_zoom(),
604 gdk_pixbuf_get_height(handles[0]) / _desktop->current_zoom());
605 Geom::Point bp = b.dimensions();
607 // do not scale when the bounding rectangle has zero width or height
608 bool show_scale = (_mode == MODE_SCALE) && !Geom::are_near(b.minExtent(), 0);
609 // do not rotate if the bounding rectangle is degenerate
610 bool show_rotate = (_mode == MODE_ROTATE_SKEW) && !Geom::are_near(b.maxExtent(), 0);
611 bool show_scale_side[2], show_skew[2];
613 // show sides if:
614 // a) there is enough space between corner handles, or
615 // b) corner handles are not shown, but side handles make sense
616 // this affects horizontal and vertical scale handles; skew handles never
617 // make sense if rotate handles are not shown
618 for (unsigned i = 0; i < 2; ++i) {
619 Geom::Dim2 d = static_cast<Geom::Dim2>(i);
620 Geom::Dim2 otherd = static_cast<Geom::Dim2>((i+1)%2);
621 show_scale_side[i] = (_mode == MODE_SCALE);
622 show_scale_side[i] &= (show_scale ? bp[d] >= handle_size[d]
623 : !Geom::are_near(bp[otherd], 0));
624 show_skew[i] = (show_rotate && bp[d] >= handle_size[d]
625 && !Geom::are_near(bp[otherd], 0));
626 }
627 for (unsigned i = 0; i < 4; ++i) {
628 _scale_corners[i]->setVisible(show_scale);
629 _rot_corners[i]->setVisible(show_rotate);
630 _scale_sides[i]->setVisible(show_scale_side[i%2]);
631 _skew_sides[i]->setVisible(show_skew[i%2]);
632 }
633 // show rotation center if there is enough space (?)
634 _center->setVisible(show_rotate /*&& bp[Geom::X] > handle_size[Geom::X]
635 && bp[Geom::Y] > handle_size[Geom::Y]*/);
636 } else {
637 for (unsigned i = 0; i < 17; ++i) {
638 if (_handles[i] != _active)
639 _handles[i]->setVisible(false);
640 }
641 }
643 }
645 } // namespace UI
646 } // namespace Inkscape
648 /*
649 Local Variables:
650 mode:c++
651 c-file-style:"stroustrup"
652 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
653 indent-tabs-mode:nil
654 fill-column:99
655 End:
656 */
657 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :