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