1 #define __SP_OBJECT_EDIT_C__
3 /*
4 * Node editing extension to objects
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Mitsuru Oka
9 * Maximilian Albert <maximilian.albert@gmail.com>
10 *
11 * Licensed under GNU GPL
12 */
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
20 #include "sp-item.h"
21 #include "sp-rect.h"
22 #include "box3d.h"
23 #include "sp-ellipse.h"
24 #include "sp-star.h"
25 #include "sp-spiral.h"
26 #include "sp-offset.h"
27 #include "sp-flowtext.h"
28 #include "preferences.h"
29 #include "desktop-affine.h"
30 #include "style.h"
31 #include "desktop.h"
32 #include "desktop-handles.h"
33 #include "sp-namedview.h"
34 #include "live_effects/effect.h"
36 #include "sp-pattern.h"
37 #include "sp-path.h"
39 #include <glibmm/i18n.h>
41 #include "object-edit.h"
43 #include <libnr/nr-scale-ops.h>
45 #include "xml/repr.h"
47 #include "2geom/isnan.h"
49 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
51 static KnotHolder *sp_lpe_knot_holder(SPItem *item, SPDesktop *desktop)
52 {
53 KnotHolder *knot_holder = new KnotHolder(desktop, item, NULL);
55 Inkscape::LivePathEffect::Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item));
56 effect->addHandles(knot_holder, desktop, item);
58 return knot_holder;
59 }
61 KnotHolder *
62 sp_item_knot_holder(SPItem *item, SPDesktop *desktop)
63 {
64 KnotHolder *knotholder = NULL;
66 if (SP_IS_LPE_ITEM(item) &&
67 sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item)) &&
68 sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))->isVisible() &&
69 sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))->providesKnotholder()) {
70 knotholder = sp_lpe_knot_holder(item, desktop);
71 } else if (SP_IS_RECT(item)) {
72 knotholder = new RectKnotHolder(desktop, item, NULL);
73 } else if (SP_IS_BOX3D(item)) {
74 knotholder = new Box3DKnotHolder(desktop, item, NULL);
75 } else if (SP_IS_ARC(item)) {
76 knotholder = new ArcKnotHolder(desktop, item, NULL);
77 } else if (SP_IS_STAR(item)) {
78 knotholder = new StarKnotHolder(desktop, item, NULL);
79 } else if (SP_IS_SPIRAL(item)) {
80 knotholder = new SpiralKnotHolder(desktop, item, NULL);
81 } else if (SP_IS_OFFSET(item)) {
82 knotholder = new OffsetKnotHolder(desktop, item, NULL);
83 } else if (SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) {
84 knotholder = new FlowtextKnotHolder(desktop, SP_FLOWTEXT(item)->get_frame(NULL), NULL);
85 } else if ((SP_OBJECT(item)->style->fill.isPaintserver())
86 && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style))) {
87 knotholder = new KnotHolder(desktop, item, NULL);
88 knotholder->add_pattern_knotholder();
89 }
91 return knotholder;
92 }
94 /* SPRect */
96 /* handle for horizontal rounding radius */
97 class RectKnotHolderEntityRX : public KnotHolderEntity {
98 public:
99 virtual Geom::Point knot_get();
100 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
101 virtual void knot_click(guint state);
102 };
104 /* handle for vertical rounding radius */
105 class RectKnotHolderEntityRY : public KnotHolderEntity {
106 public:
107 virtual Geom::Point knot_get();
108 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
109 virtual void knot_click(guint state);
110 };
112 /* handle for width/height adjustment */
113 class RectKnotHolderEntityWH : public KnotHolderEntity {
114 public:
115 virtual Geom::Point knot_get();
116 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
118 protected:
119 void set_internal(Geom::Point const &p, Geom::Point const &origin, guint state);
120 };
122 /* handle for x/y adjustment */
123 class RectKnotHolderEntityXY : public KnotHolderEntity {
124 public:
125 virtual Geom::Point knot_get();
126 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
127 };
129 Geom::Point
130 RectKnotHolderEntityRX::knot_get()
131 {
132 SPRect *rect = SP_RECT(item);
134 return Geom::Point(rect->x.computed + rect->width.computed - rect->rx.computed, rect->y.computed);
135 }
137 void
138 RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
139 {
140 SPRect *rect = SP_RECT(item);
142 //In general we cannot just snap this radius to an arbitrary point, as we have only a single
143 //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap
144 //the radius then we should have a constrained snap. snap_knot_position() is unconstrained
146 if (state & GDK_CONTROL_MASK) {
147 gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0;
148 rect->rx.computed = rect->ry.computed = CLAMP(rect->x.computed + rect->width.computed - p[Geom::X], 0.0, temp);
149 rect->rx._set = rect->ry._set = true;
151 } else {
152 rect->rx.computed = CLAMP(rect->x.computed + rect->width.computed - p[Geom::X], 0.0, rect->width.computed / 2.0);
153 rect->rx._set = true;
154 }
156 update_knot();
158 ((SPObject*)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
159 }
161 void
162 RectKnotHolderEntityRX::knot_click(guint state)
163 {
164 SPRect *rect = SP_RECT(item);
166 if (state & GDK_SHIFT_MASK) {
167 /* remove rounding from rectangle */
168 SP_OBJECT_REPR(rect)->setAttribute("rx", NULL);
169 SP_OBJECT_REPR(rect)->setAttribute("ry", NULL);
170 } else if (state & GDK_CONTROL_MASK) {
171 /* Ctrl-click sets the vertical rounding to be the same as the horizontal */
172 SP_OBJECT_REPR(rect)->setAttribute("ry", SP_OBJECT_REPR(rect)->attribute("rx"));
173 }
175 update_knot();
176 }
178 Geom::Point
179 RectKnotHolderEntityRY::knot_get()
180 {
181 SPRect *rect = SP_RECT(item);
183 return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->ry.computed);
184 }
186 void
187 RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
188 {
189 SPRect *rect = SP_RECT(item);
191 //In general we cannot just snap this radius to an arbitrary point, as we have only a single
192 //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap
193 //the radius then we should have a constrained snap. snap_knot_position() is unconstrained
195 if (state & GDK_CONTROL_MASK) {
196 gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0;
197 rect->rx.computed = rect->ry.computed = CLAMP(p[Geom::Y] - rect->y.computed, 0.0, temp);
198 rect->ry._set = rect->rx._set = true;
199 } else {
200 if (!rect->rx._set || rect->rx.computed == 0) {
201 rect->ry.computed = CLAMP(p[Geom::Y] - rect->y.computed,
202 0.0,
203 MIN(rect->height.computed / 2.0, rect->width.computed / 2.0));
204 } else {
205 rect->ry.computed = CLAMP(p[Geom::Y] - rect->y.computed,
206 0.0,
207 rect->height.computed / 2.0);
208 }
210 rect->ry._set = true;
211 }
213 update_knot();
215 ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
216 }
218 void
219 RectKnotHolderEntityRY::knot_click(guint state)
220 {
221 SPRect *rect = SP_RECT(item);
223 if (state & GDK_SHIFT_MASK) {
224 /* remove rounding */
225 SP_OBJECT_REPR(rect)->setAttribute("rx", NULL);
226 SP_OBJECT_REPR(rect)->setAttribute("ry", NULL);
227 } else if (state & GDK_CONTROL_MASK) {
228 /* Ctrl-click sets the vertical rounding to be the same as the horizontal */
229 SP_OBJECT_REPR(rect)->setAttribute("rx", SP_OBJECT_REPR(rect)->attribute("ry"));
230 }
231 }
233 #define SGN(x) ((x)>0?1:((x)<0?-1:0))
235 static void sp_rect_clamp_radii(SPRect *rect)
236 {
237 // clamp rounding radii so that they do not exceed width/height
238 if (2 * rect->rx.computed > rect->width.computed) {
239 rect->rx.computed = 0.5 * rect->width.computed;
240 rect->rx._set = true;
241 }
242 if (2 * rect->ry.computed > rect->height.computed) {
243 rect->ry.computed = 0.5 * rect->height.computed;
244 rect->ry._set = true;
245 }
246 }
248 Geom::Point
249 RectKnotHolderEntityWH::knot_get()
250 {
251 SPRect *rect = SP_RECT(item);
253 return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed);
254 }
256 void
257 RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &origin, guint state)
258 {
259 SPRect *rect = SP_RECT(item);
260 Geom::Point const s = snap_knot_position(p);
262 if (state & GDK_CONTROL_MASK) {
263 // original width/height when drag started
264 gdouble const w_orig = (origin[Geom::X] - rect->x.computed);
265 gdouble const h_orig = (origin[Geom::Y] - rect->y.computed);
267 //original ratio
268 gdouble const ratio = (w_orig / h_orig);
270 // mouse displacement since drag started
271 gdouble const minx = s[Geom::X] - origin[Geom::X];
272 gdouble const miny = s[Geom::Y] - origin[Geom::Y];
274 if (fabs(minx) > fabs(miny)) {
276 // snap to horizontal or diagonal
277 rect->width.computed = MAX(w_orig + minx, 0);
278 if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) {
279 // closer to the diagonal and in same-sign quarters, change both using ratio
280 rect->height.computed = MAX(h_orig + minx / ratio, 0);
281 } else {
282 // closer to the horizontal, change only width, height is h_orig
283 rect->height.computed = MAX(h_orig, 0);
284 }
286 } else {
287 // snap to vertical or diagonal
288 rect->height.computed = MAX(h_orig + miny, 0);
289 if (miny != 0 && fabs(minx/miny) > 0.5 * ratio && (SGN(minx) == SGN(miny))) {
290 // closer to the diagonal and in same-sign quarters, change both using ratio
291 rect->width.computed = MAX(w_orig + miny * ratio, 0);
292 } else {
293 // closer to the vertical, change only height, width is w_orig
294 rect->width.computed = MAX(w_orig, 0);
295 }
296 }
298 rect->width._set = rect->height._set = true;
300 } else {
301 // move freely
302 rect->width.computed = MAX(s[Geom::X] - rect->x.computed, 0);
303 rect->height.computed = MAX(s[Geom::Y] - rect->y.computed, 0);
304 rect->width._set = rect->height._set = true;
305 }
307 sp_rect_clamp_radii(rect);
309 ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
310 }
312 void
313 RectKnotHolderEntityWH::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
314 {
315 set_internal(p, origin, state);
316 update_knot();
317 }
319 Geom::Point
320 RectKnotHolderEntityXY::knot_get()
321 {
322 SPRect *rect = SP_RECT(item);
324 return Geom::Point(rect->x.computed, rect->y.computed);
325 }
327 void
328 RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
329 {
330 SPRect *rect = SP_RECT(item);
332 // opposite corner (unmoved)
333 gdouble opposite_x = (rect->x.computed + rect->width.computed);
334 gdouble opposite_y = (rect->y.computed + rect->height.computed);
336 // original width/height when drag started
337 gdouble w_orig = opposite_x - origin[Geom::X];
338 gdouble h_orig = opposite_y - origin[Geom::Y];
340 Geom::Point const s = snap_knot_position(p);
342 // mouse displacement since drag started
343 gdouble minx = s[Geom::X] - origin[Geom::X];
344 gdouble miny = s[Geom::Y] - origin[Geom::Y];
346 if (state & GDK_CONTROL_MASK) {
347 //original ratio
348 gdouble ratio = (w_orig / h_orig);
350 if (fabs(minx) > fabs(miny)) {
352 // snap to horizontal or diagonal
353 rect->x.computed = MIN(s[Geom::X], opposite_x);
354 rect->width.computed = MAX(w_orig - minx, 0);
355 if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) {
356 // closer to the diagonal and in same-sign quarters, change both using ratio
357 rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y);
358 rect->height.computed = MAX(h_orig - minx / ratio, 0);
359 } else {
360 // closer to the horizontal, change only width, height is h_orig
361 rect->y.computed = MIN(origin[Geom::Y], opposite_y);
362 rect->height.computed = MAX(h_orig, 0);
363 }
365 } else {
367 // snap to vertical or diagonal
368 rect->y.computed = MIN(s[Geom::Y], opposite_y);
369 rect->height.computed = MAX(h_orig - miny, 0);
370 if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) {
371 // closer to the diagonal and in same-sign quarters, change both using ratio
372 rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x);
373 rect->width.computed = MAX(w_orig - miny * ratio, 0);
374 } else {
375 // closer to the vertical, change only height, width is w_orig
376 rect->x.computed = MIN(origin[Geom::X], opposite_x);
377 rect->width.computed = MAX(w_orig, 0);
378 }
380 }
382 rect->width._set = rect->height._set = rect->x._set = rect->y._set = true;
384 } else {
385 // move freely
386 rect->x.computed = MIN(s[Geom::X], opposite_x);
387 rect->width.computed = MAX(w_orig - minx, 0);
388 rect->y.computed = MIN(s[Geom::Y], opposite_y);
389 rect->height.computed = MAX(h_orig - miny, 0);
390 rect->width._set = rect->height._set = rect->x._set = rect->y._set = true;
391 }
393 sp_rect_clamp_radii(rect);
395 update_knot();
397 ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
398 }
400 RectKnotHolder::RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
401 KnotHolder(desktop, item, relhandler)
402 {
403 RectKnotHolderEntityRX *entity_rx = new RectKnotHolderEntityRX();
404 RectKnotHolderEntityRY *entity_ry = new RectKnotHolderEntityRY();
405 RectKnotHolderEntityWH *entity_wh = new RectKnotHolderEntityWH();
406 RectKnotHolderEntityXY *entity_xy = new RectKnotHolderEntityXY();
407 entity_rx->create(desktop, item, this,
408 _("Adjust the <b>horizontal rounding</b> radius; with <b>Ctrl</b> "
409 "to make the vertical radius the same"),
410 SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR);
411 entity_ry->create(desktop, item, this,
412 _("Adjust the <b>vertical rounding</b> radius; with <b>Ctrl</b> "
413 "to make the horizontal radius the same"),
414 SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR);
415 entity_wh->create(desktop, item, this,
416 _("Adjust the <b>width and height</b> of the rectangle; with <b>Ctrl</b>"
417 "to lock ratio or stretch in one dimension only"),
418 SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
419 entity_xy->create(desktop, item, this,
420 _("Adjust the <b>width and height</b> of the rectangle; with <b>Ctrl</b>"
421 "to lock ratio or stretch in one dimension only"),
422 SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
423 entity.push_back(entity_rx);
424 entity.push_back(entity_ry);
425 entity.push_back(entity_wh);
426 entity.push_back(entity_xy);
428 add_pattern_knotholder();
429 }
431 /* Box3D (= the new 3D box structure) */
433 class Box3DKnotHolderEntity : public KnotHolderEntity {
434 public:
435 virtual Geom::Point knot_get() = 0;
436 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) = 0;
438 Geom::Point knot_get_generic(SPItem *item, unsigned int knot_id);
439 void knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &p, guint state);
440 };
442 Geom::Point
443 Box3DKnotHolderEntity::knot_get_generic(SPItem *item, unsigned int knot_id)
444 {
445 return box3d_get_corner_screen(SP_BOX3D(item), knot_id);
446 }
448 void
449 Box3DKnotHolderEntity::knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &new_pos, guint state)
450 {
451 Geom::Point const s = snap_knot_position(new_pos);
453 g_assert(item != NULL);
454 SPBox3D *box = SP_BOX3D(item);
455 Geom::Matrix const i2d (sp_item_i2d_affine (item));
457 Box3D::Axis movement;
458 if ((knot_id < 4) != (state & GDK_SHIFT_MASK)) {
459 movement = Box3D::XY;
460 } else {
461 movement = Box3D::Z;
462 }
464 box3d_set_corner (box, knot_id, s * i2d, movement, (state & GDK_CONTROL_MASK));
465 box3d_set_z_orders(box);
466 box3d_position_set(box);
467 }
469 class Box3DKnotHolderEntity0 : public Box3DKnotHolderEntity {
470 public:
471 virtual Geom::Point knot_get();
472 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
473 };
475 class Box3DKnotHolderEntity1 : public Box3DKnotHolderEntity {
476 public:
477 virtual Geom::Point knot_get();
478 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
479 };
481 class Box3DKnotHolderEntity2 : public Box3DKnotHolderEntity {
482 public:
483 virtual Geom::Point knot_get();
484 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
485 };
487 class Box3DKnotHolderEntity3 : public Box3DKnotHolderEntity {
488 public:
489 virtual Geom::Point knot_get();
490 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
491 };
493 class Box3DKnotHolderEntity4 : public Box3DKnotHolderEntity {
494 public:
495 virtual Geom::Point knot_get();
496 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
497 };
499 class Box3DKnotHolderEntity5 : public Box3DKnotHolderEntity {
500 public:
501 virtual Geom::Point knot_get();
502 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
503 };
505 class Box3DKnotHolderEntity6 : public Box3DKnotHolderEntity {
506 public:
507 virtual Geom::Point knot_get();
508 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
509 };
511 class Box3DKnotHolderEntity7 : public Box3DKnotHolderEntity {
512 public:
513 virtual Geom::Point knot_get();
514 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
515 };
517 class Box3DKnotHolderEntityCenter : public KnotHolderEntity {
518 public:
519 virtual Geom::Point knot_get();
520 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
521 };
523 Geom::Point
524 Box3DKnotHolderEntity0::knot_get()
525 {
526 return knot_get_generic(item, 0);
527 }
529 Geom::Point
530 Box3DKnotHolderEntity1::knot_get()
531 {
532 return knot_get_generic(item, 1);
533 }
535 Geom::Point
536 Box3DKnotHolderEntity2::knot_get()
537 {
538 return knot_get_generic(item, 2);
539 }
541 Geom::Point
542 Box3DKnotHolderEntity3::knot_get()
543 {
544 return knot_get_generic(item, 3);
545 }
547 Geom::Point
548 Box3DKnotHolderEntity4::knot_get()
549 {
550 return knot_get_generic(item, 4);
551 }
553 Geom::Point
554 Box3DKnotHolderEntity5::knot_get()
555 {
556 return knot_get_generic(item, 5);
557 }
559 Geom::Point
560 Box3DKnotHolderEntity6::knot_get()
561 {
562 return knot_get_generic(item, 6);
563 }
565 Geom::Point
566 Box3DKnotHolderEntity7::knot_get()
567 {
568 return knot_get_generic(item, 7);
569 }
571 Geom::Point
572 Box3DKnotHolderEntityCenter::knot_get()
573 {
574 return box3d_get_center_screen(SP_BOX3D(item));
575 }
577 void
578 Box3DKnotHolderEntity0::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
579 {
580 knot_set_generic(item, 0, new_pos, state);
581 }
583 void
584 Box3DKnotHolderEntity1::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
585 {
586 knot_set_generic(item, 1, new_pos, state);
587 }
589 void
590 Box3DKnotHolderEntity2::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
591 {
592 knot_set_generic(item, 2, new_pos, state);
593 }
595 void
596 Box3DKnotHolderEntity3::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
597 {
598 knot_set_generic(item, 3, new_pos, state);
599 }
601 void
602 Box3DKnotHolderEntity4::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
603 {
604 knot_set_generic(item, 4, new_pos, state);
605 }
607 void
608 Box3DKnotHolderEntity5::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
609 {
610 knot_set_generic(item, 5, new_pos, state);
611 }
613 void
614 Box3DKnotHolderEntity6::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
615 {
616 knot_set_generic(item, 6, new_pos, state);
617 }
619 void
620 Box3DKnotHolderEntity7::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
621 {
622 knot_set_generic(item, 7, new_pos, state);
623 }
625 void
626 Box3DKnotHolderEntityCenter::knot_set(Geom::Point const &new_pos, Geom::Point const &origin, guint state)
627 {
628 Geom::Point const s = snap_knot_position(new_pos);
630 SPBox3D *box = SP_BOX3D(item);
631 Geom::Matrix const i2d (sp_item_i2d_affine (item));
633 box3d_set_center (SP_BOX3D(item), s * i2d, origin * i2d, !(state & GDK_SHIFT_MASK) ? Box3D::XY : Box3D::Z,
634 state & GDK_CONTROL_MASK);
636 box3d_set_z_orders(box);
637 box3d_position_set(box);
638 }
640 Box3DKnotHolder::Box3DKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
641 KnotHolder(desktop, item, relhandler)
642 {
643 Box3DKnotHolderEntity0 *entity_corner0 = new Box3DKnotHolderEntity0();
644 Box3DKnotHolderEntity1 *entity_corner1 = new Box3DKnotHolderEntity1();
645 Box3DKnotHolderEntity2 *entity_corner2 = new Box3DKnotHolderEntity2();
646 Box3DKnotHolderEntity3 *entity_corner3 = new Box3DKnotHolderEntity3();
647 Box3DKnotHolderEntity4 *entity_corner4 = new Box3DKnotHolderEntity4();
648 Box3DKnotHolderEntity5 *entity_corner5 = new Box3DKnotHolderEntity5();
649 Box3DKnotHolderEntity6 *entity_corner6 = new Box3DKnotHolderEntity6();
650 Box3DKnotHolderEntity7 *entity_corner7 = new Box3DKnotHolderEntity7();
651 Box3DKnotHolderEntityCenter *entity_center = new Box3DKnotHolderEntityCenter();
653 entity_corner0->create(desktop, item, this,
654 _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; "
655 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
656 entity_corner1->create(desktop, item, this,
657 _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; "
658 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
659 entity_corner2->create(desktop, item, this,
660 _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; "
661 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
662 entity_corner3->create(desktop, item, this,
663 _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; "
664 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
665 entity_corner4->create(desktop, item, this,
666 _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; "
667 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
668 entity_corner5->create(desktop, item, this,
669 _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; "
670 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
671 entity_corner6->create(desktop, item, this,
672 _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; "
673 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
674 entity_corner7->create(desktop, item, this,
675 _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; "
676 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
677 entity_center->create(desktop, item, this,
678 _("Move the box in perspective"),
679 SP_KNOT_SHAPE_CROSS);
681 entity.push_back(entity_corner0);
682 entity.push_back(entity_corner1);
683 entity.push_back(entity_corner2);
684 entity.push_back(entity_corner3);
685 entity.push_back(entity_corner4);
686 entity.push_back(entity_corner5);
687 entity.push_back(entity_corner6);
688 entity.push_back(entity_corner7);
689 entity.push_back(entity_center);
691 add_pattern_knotholder();
692 }
694 /* SPArc */
696 class ArcKnotHolderEntityStart : public KnotHolderEntity {
697 public:
698 virtual Geom::Point knot_get();
699 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
700 };
702 class ArcKnotHolderEntityEnd : public KnotHolderEntity {
703 public:
704 virtual Geom::Point knot_get();
705 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
706 virtual void knot_click(guint state);
707 };
709 class ArcKnotHolderEntityRX : public KnotHolderEntity {
710 public:
711 virtual Geom::Point knot_get();
712 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
713 virtual void knot_click(guint state);
714 };
716 class ArcKnotHolderEntityRY : public KnotHolderEntity {
717 public:
718 virtual Geom::Point knot_get();
719 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
720 virtual void knot_click(guint state);
721 };
723 /*
724 * return values:
725 * 1 : inside
726 * 0 : on the curves
727 * -1 : outside
728 */
729 static gint
730 sp_genericellipse_side(SPGenericEllipse *ellipse, Geom::Point const &p)
731 {
732 gdouble dx = (p[Geom::X] - ellipse->cx.computed) / ellipse->rx.computed;
733 gdouble dy = (p[Geom::Y] - ellipse->cy.computed) / ellipse->ry.computed;
735 gdouble s = dx * dx + dy * dy;
736 if (s < 1.0) return 1;
737 if (s > 1.0) return -1;
738 return 0;
739 }
741 void
742 ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
743 {
744 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
745 int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
747 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
748 SPArc *arc = SP_ARC(item);
750 ge->closed = (sp_genericellipse_side(ge, p) == -1) ? TRUE : FALSE;
752 Geom::Point delta = p - Geom::Point(ge->cx.computed, ge->cy.computed);
753 Geom::Scale sc(ge->rx.computed, ge->ry.computed);
754 ge->start = atan2(delta * sc.inverse());
755 if ( ( state & GDK_CONTROL_MASK )
756 && snaps )
757 {
758 ge->start = sp_round(ge->start, M_PI/snaps);
759 }
760 sp_genericellipse_normalize(ge);
761 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
762 }
764 Geom::Point
765 ArcKnotHolderEntityStart::knot_get()
766 {
767 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
768 SPArc *arc = SP_ARC(item);
770 return sp_arc_get_xy(arc, ge->start);
771 }
773 void
774 ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
775 {
776 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
777 int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
779 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
780 SPArc *arc = SP_ARC(item);
782 ge->closed = (sp_genericellipse_side(ge, p) == -1) ? TRUE : FALSE;
784 Geom::Point delta = p - Geom::Point(ge->cx.computed, ge->cy.computed);
785 Geom::Scale sc(ge->rx.computed, ge->ry.computed);
786 ge->end = atan2(delta * sc.inverse());
787 if ( ( state & GDK_CONTROL_MASK )
788 && snaps )
789 {
790 ge->end = sp_round(ge->end, M_PI/snaps);
791 }
792 sp_genericellipse_normalize(ge);
793 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
794 }
796 Geom::Point
797 ArcKnotHolderEntityEnd::knot_get()
798 {
799 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
800 SPArc *arc = SP_ARC(item);
802 return sp_arc_get_xy(arc, ge->end);
803 }
806 void
807 ArcKnotHolderEntityEnd::knot_click(guint state)
808 {
809 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
811 if (state & GDK_SHIFT_MASK) {
812 ge->end = ge->start = 0;
813 ((SPObject *)ge)->updateRepr();
814 }
815 }
818 void
819 ArcKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
820 {
821 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
823 Geom::Point const s = snap_knot_position(p);
825 ge->rx.computed = fabs( ge->cx.computed - s[Geom::X] );
827 if ( state & GDK_CONTROL_MASK ) {
828 ge->ry.computed = ge->rx.computed;
829 }
831 ((SPObject *)item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
832 }
834 Geom::Point
835 ArcKnotHolderEntityRX::knot_get()
836 {
837 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
839 return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(ge->rx.computed, 0));
840 }
842 void
843 ArcKnotHolderEntityRX::knot_click(guint state)
844 {
845 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
847 if (state & GDK_CONTROL_MASK) {
848 ge->ry.computed = ge->rx.computed;
849 ((SPObject *)ge)->updateRepr();
850 }
851 }
853 void
854 ArcKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
855 {
856 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
858 Geom::Point const s = snap_knot_position(p);
860 ge->ry.computed = fabs( ge->cy.computed - s[Geom::Y] );
862 if ( state & GDK_CONTROL_MASK ) {
863 ge->rx.computed = ge->ry.computed;
864 }
866 ((SPObject *)item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
867 }
869 Geom::Point
870 ArcKnotHolderEntityRY::knot_get()
871 {
872 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
874 return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(0, ge->ry.computed));
875 }
877 void
878 ArcKnotHolderEntityRY::knot_click(guint state)
879 {
880 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
882 if (state & GDK_CONTROL_MASK) {
883 ge->rx.computed = ge->ry.computed;
884 ((SPObject *)ge)->updateRepr();
885 }
886 }
888 ArcKnotHolder::ArcKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
889 KnotHolder(desktop, item, relhandler)
890 {
891 ArcKnotHolderEntityRX *entity_rx = new ArcKnotHolderEntityRX();
892 ArcKnotHolderEntityRY *entity_ry = new ArcKnotHolderEntityRY();
893 ArcKnotHolderEntityStart *entity_start = new ArcKnotHolderEntityStart();
894 ArcKnotHolderEntityEnd *entity_end = new ArcKnotHolderEntityEnd();
895 entity_rx->create(desktop, item, this,
896 _("Adjust ellipse <b>width</b>, with <b>Ctrl</b> to make circle"),
897 SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
898 entity_ry->create(desktop, item, this,
899 _("Adjust ellipse <b>height</b>, with <b>Ctrl</b> to make circle"),
900 SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
901 entity_start->create(desktop, item, this,
902 _("Position the <b>start point</b> of the arc or segment; with <b>Ctrl</b>"
903 "to snap angle; drag <b>inside</b> the ellipse for arc, <b>outside</b> for segment"),
904 SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR);
905 entity_end->create(desktop, item, this,
906 _("Position the <b>end point</b> of the arc or segment; with <b>Ctrl</b> to snap angle; "
907 "drag <b>inside</b> the ellipse for arc, <b>outside</b> for segment"),
908 SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR);
909 entity.push_back(entity_rx);
910 entity.push_back(entity_ry);
911 entity.push_back(entity_start);
912 entity.push_back(entity_end);
914 add_pattern_knotholder();
915 }
917 /* SPStar */
919 class StarKnotHolderEntity1 : public KnotHolderEntity {
920 public:
921 virtual Geom::Point knot_get();
922 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
923 virtual void knot_click(guint state);
924 };
926 class StarKnotHolderEntity2 : public KnotHolderEntity {
927 public:
928 virtual Geom::Point knot_get();
929 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
930 virtual void knot_click(guint state);
931 };
933 void
934 StarKnotHolderEntity1::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
935 {
936 SPStar *star = SP_STAR(item);
938 Geom::Point const s = snap_knot_position(p);
940 Geom::Point d = s - to_2geom(star->center);
942 double arg1 = atan2(d);
943 double darg1 = arg1 - star->arg[0];
945 if (state & GDK_MOD1_MASK) {
946 star->randomized = darg1/(star->arg[0] - star->arg[1]);
947 } else if (state & GDK_SHIFT_MASK) {
948 star->rounded = darg1/(star->arg[0] - star->arg[1]);
949 } else if (state & GDK_CONTROL_MASK) {
950 star->r[0] = L2(d);
951 } else {
952 star->r[0] = L2(d);
953 star->arg[0] = arg1;
954 star->arg[1] += darg1;
955 }
956 ((SPObject *)star)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
957 }
959 void
960 StarKnotHolderEntity2::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
961 {
962 SPStar *star = SP_STAR(item);
964 Geom::Point const s = snap_knot_position(p);
966 if (star->flatsided == false) {
967 Geom::Point d = s - to_2geom(star->center);
969 double arg1 = atan2(d);
970 double darg1 = arg1 - star->arg[1];
972 if (state & GDK_MOD1_MASK) {
973 star->randomized = darg1/(star->arg[0] - star->arg[1]);
974 } else if (state & GDK_SHIFT_MASK) {
975 star->rounded = fabs(darg1/(star->arg[0] - star->arg[1]));
976 } else if (state & GDK_CONTROL_MASK) {
977 star->r[1] = L2(d);
978 star->arg[1] = star->arg[0] + M_PI / star->sides;
979 }
980 else {
981 star->r[1] = L2(d);
982 star->arg[1] = atan2(d);
983 }
984 ((SPObject *)star)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
985 }
986 }
988 Geom::Point
989 StarKnotHolderEntity1::knot_get()
990 {
991 g_assert(item != NULL);
993 SPStar *star = SP_STAR(item);
995 return sp_star_get_xy(star, SP_STAR_POINT_KNOT1, 0);
997 }
999 Geom::Point
1000 StarKnotHolderEntity2::knot_get()
1001 {
1002 g_assert(item != NULL);
1004 SPStar *star = SP_STAR(item);
1006 return sp_star_get_xy(star, SP_STAR_POINT_KNOT2, 0);
1007 }
1009 static void
1010 sp_star_knot_click(SPItem *item, guint state)
1011 {
1012 SPStar *star = SP_STAR(item);
1014 if (state & GDK_MOD1_MASK) {
1015 star->randomized = 0;
1016 ((SPObject *)star)->updateRepr();
1017 } else if (state & GDK_SHIFT_MASK) {
1018 star->rounded = 0;
1019 ((SPObject *)star)->updateRepr();
1020 } else if (state & GDK_CONTROL_MASK) {
1021 star->arg[1] = star->arg[0] + M_PI / star->sides;
1022 ((SPObject *)star)->updateRepr();
1023 }
1024 }
1026 void
1027 StarKnotHolderEntity1::knot_click(guint state)
1028 {
1029 return sp_star_knot_click(item, state);
1030 }
1032 void
1033 StarKnotHolderEntity2::knot_click(guint state)
1034 {
1035 return sp_star_knot_click(item, state);
1036 }
1038 StarKnotHolder::StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
1039 KnotHolder(desktop, item, relhandler)
1040 {
1041 SPStar *star = SP_STAR(item);
1043 StarKnotHolderEntity1 *entity1 = new StarKnotHolderEntity1();
1044 entity1->create(desktop, item, this,
1045 _("Adjust the <b>tip radius</b> of the star or polygon; "
1046 "with <b>Shift</b> to round; with <b>Alt</b> to randomize"));
1047 entity.push_back(entity1);
1049 if (star->flatsided == false) {
1050 StarKnotHolderEntity2 *entity2 = new StarKnotHolderEntity2();
1051 entity2->create(desktop, item, this,
1052 _("Adjust the <b>base radius</b> of the star; with <b>Ctrl</b> to keep star rays "
1053 "radial (no skew); with <b>Shift</b> to round; with <b>Alt</b> to randomize"));
1054 entity.push_back(entity2);
1055 }
1057 add_pattern_knotholder();
1058 }
1060 /* SPSpiral */
1062 class SpiralKnotHolderEntityInner : public KnotHolderEntity {
1063 public:
1064 virtual Geom::Point knot_get();
1065 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
1066 virtual void knot_click(guint state);
1067 };
1069 class SpiralKnotHolderEntityOuter : public KnotHolderEntity {
1070 public:
1071 virtual Geom::Point knot_get();
1072 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
1073 };
1076 /*
1077 * set attributes via inner (t=t0) knot point:
1078 * [default] increase/decrease inner point
1079 * [shift] increase/decrease inner and outer arg synchronizely
1080 * [control] constrain inner arg to round per PI/4
1081 */
1082 void
1083 SpiralKnotHolderEntityInner::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
1084 {
1085 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1086 int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
1088 SPSpiral *spiral = SP_SPIRAL(item);
1090 gdouble dx = p[Geom::X] - spiral->cx;
1091 gdouble dy = p[Geom::Y] - spiral->cy;
1093 if (state & GDK_MOD1_MASK) {
1094 // adjust divergence by vertical drag, relative to rad
1095 double new_exp = (spiral->rad + dy)/(spiral->rad);
1096 spiral->exp = new_exp > 0? new_exp : 0;
1097 } else {
1098 // roll/unroll from inside
1099 gdouble arg_t0;
1100 sp_spiral_get_polar(spiral, spiral->t0, NULL, &arg_t0);
1102 gdouble arg_tmp = atan2(dy, dx) - arg_t0;
1103 gdouble arg_t0_new = arg_tmp - floor((arg_tmp+M_PI)/(2.0*M_PI))*2.0*M_PI + arg_t0;
1104 spiral->t0 = (arg_t0_new - spiral->arg) / (2.0*M_PI*spiral->revo);
1106 /* round inner arg per PI/snaps, if CTRL is pressed */
1107 if ( ( state & GDK_CONTROL_MASK )
1108 && ( fabs(spiral->revo) > SP_EPSILON_2 )
1109 && ( snaps != 0 ) ) {
1110 gdouble arg = 2.0*M_PI*spiral->revo*spiral->t0 + spiral->arg;
1111 spiral->t0 = (sp_round(arg, M_PI/snaps) - spiral->arg)/(2.0*M_PI*spiral->revo);
1112 }
1114 spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999);
1115 }
1117 ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1118 }
1120 /*
1121 * set attributes via outer (t=1) knot point:
1122 * [default] increase/decrease revolution factor
1123 * [control] constrain inner arg to round per PI/4
1124 */
1125 void
1126 SpiralKnotHolderEntityOuter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
1127 {
1128 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1129 int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
1131 SPSpiral *spiral = SP_SPIRAL(item);
1133 gdouble dx = p[Geom::X] - spiral->cx;
1134 gdouble dy = p[Geom::Y] - spiral->cy;
1136 if (state & GDK_SHIFT_MASK) { // rotate without roll/unroll
1137 spiral->arg = atan2(dy, dx) - 2.0*M_PI*spiral->revo;
1138 if (!(state & GDK_MOD1_MASK)) {
1139 // if alt not pressed, change also rad; otherwise it is locked
1140 spiral->rad = MAX(hypot(dx, dy), 0.001);
1141 }
1142 if ( ( state & GDK_CONTROL_MASK )
1143 && snaps ) {
1144 spiral->arg = sp_round(spiral->arg, M_PI/snaps);
1145 }
1146 } else { // roll/unroll
1147 // arg of the spiral outer end
1148 double arg_1;
1149 sp_spiral_get_polar(spiral, 1, NULL, &arg_1);
1151 // its fractional part after the whole turns are subtracted
1152 double arg_r = arg_1 - sp_round(arg_1, 2.0*M_PI);
1154 // arg of the mouse point relative to spiral center
1155 double mouse_angle = atan2(dy, dx);
1156 if (mouse_angle < 0)
1157 mouse_angle += 2*M_PI;
1159 // snap if ctrl
1160 if ( ( state & GDK_CONTROL_MASK ) && snaps ) {
1161 mouse_angle = sp_round(mouse_angle, M_PI/snaps);
1162 }
1164 // by how much we want to rotate the outer point
1165 double diff = mouse_angle - arg_r;
1166 if (diff > M_PI)
1167 diff -= 2*M_PI;
1168 else if (diff < -M_PI)
1169 diff += 2*M_PI;
1171 // calculate the new rad;
1172 // the value of t corresponding to the angle arg_1 + diff:
1173 double t_temp = ((arg_1 + diff) - spiral->arg)/(2*M_PI*spiral->revo);
1174 // the rad at that t:
1175 double rad_new = 0;
1176 if (t_temp > spiral->t0)
1177 sp_spiral_get_polar(spiral, t_temp, &rad_new, NULL);
1179 // change the revo (converting diff from radians to the number of turns)
1180 spiral->revo += diff/(2*M_PI);
1181 if (spiral->revo < 1e-3)
1182 spiral->revo = 1e-3;
1184 // if alt not pressed and the values are sane, change the rad
1185 if (!(state & GDK_MOD1_MASK) && rad_new > 1e-3 && rad_new/spiral->rad < 2) {
1186 // adjust t0 too so that the inner point stays unmoved
1187 double r0;
1188 sp_spiral_get_polar(spiral, spiral->t0, &r0, NULL);
1189 spiral->rad = rad_new;
1190 spiral->t0 = pow(r0 / spiral->rad, 1.0/spiral->exp);
1191 }
1192 if (!IS_FINITE(spiral->t0)) spiral->t0 = 0.0;
1193 spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999);
1194 }
1196 ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1197 }
1199 Geom::Point
1200 SpiralKnotHolderEntityInner::knot_get()
1201 {
1202 SPSpiral *spiral = SP_SPIRAL(item);
1204 return sp_spiral_get_xy(spiral, spiral->t0);
1205 }
1207 Geom::Point
1208 SpiralKnotHolderEntityOuter::knot_get()
1209 {
1210 SPSpiral *spiral = SP_SPIRAL(item);
1212 return sp_spiral_get_xy(spiral, 1.0);
1213 }
1215 void
1216 SpiralKnotHolderEntityInner::knot_click(guint state)
1217 {
1218 SPSpiral *spiral = SP_SPIRAL(item);
1220 if (state & GDK_MOD1_MASK) {
1221 spiral->exp = 1;
1222 ((SPObject *)spiral)->updateRepr();
1223 } else if (state & GDK_SHIFT_MASK) {
1224 spiral->t0 = 0;
1225 ((SPObject *)spiral)->updateRepr();
1226 }
1227 }
1229 SpiralKnotHolder::SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
1230 KnotHolder(desktop, item, relhandler)
1231 {
1232 SpiralKnotHolderEntityInner *entity_inner = new SpiralKnotHolderEntityInner();
1233 SpiralKnotHolderEntityOuter *entity_outer = new SpiralKnotHolderEntityOuter();
1234 entity_inner->create(desktop, item, this,
1235 _("Roll/unroll the spiral from <b>inside</b>; with <b>Ctrl</b> to snap angle; "
1236 "with <b>Alt</b> to converge/diverge"));
1237 entity_outer->create(desktop, item, this,
1238 _("Roll/unroll the spiral from <b>outside</b>; with <b>Ctrl</b> to snap angle; "
1239 "with <b>Shift</b> to scale/rotate"));
1240 entity.push_back(entity_inner);
1241 entity.push_back(entity_outer);
1243 add_pattern_knotholder();
1244 }
1246 /* SPOffset */
1248 class OffsetKnotHolderEntity : public KnotHolderEntity {
1249 public:
1250 virtual Geom::Point knot_get();
1251 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
1252 };
1254 void
1255 OffsetKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/)
1256 {
1257 SPOffset *offset = SP_OFFSET(item);
1259 offset->rad = sp_offset_distance_to_original(offset, p);
1260 offset->knot = p;
1261 offset->knotSet = true;
1263 ((SPObject *)offset)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1264 }
1267 Geom::Point
1268 OffsetKnotHolderEntity::knot_get()
1269 {
1270 SPOffset *offset = SP_OFFSET(item);
1272 Geom::Point np;
1273 sp_offset_top_point(offset,&np);
1274 return np;
1275 }
1277 OffsetKnotHolder::OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
1278 KnotHolder(desktop, item, relhandler)
1279 {
1280 OffsetKnotHolderEntity *entity_offset = new OffsetKnotHolderEntity();
1281 entity_offset->create(desktop, item, this,
1282 _("Adjust the <b>offset distance</b>"));
1283 entity.push_back(entity_offset);
1285 add_pattern_knotholder();
1286 }
1288 // TODO: this is derived from RectKnotHolderEntityWH because it used the same static function
1289 // set_internal as the latter before KnotHolderEntity was C++ified. Check whether this also makes
1290 // sense logically.
1291 class FlowtextKnotHolderEntity : public RectKnotHolderEntityWH {
1292 public:
1293 virtual Geom::Point knot_get();
1294 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
1295 };
1297 Geom::Point
1298 FlowtextKnotHolderEntity::knot_get()
1299 {
1300 SPRect *rect = SP_RECT(item);
1302 return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed);
1303 }
1305 void
1306 FlowtextKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
1307 {
1308 set_internal(p, origin, state);
1309 }
1311 FlowtextKnotHolder::FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
1312 KnotHolder(desktop, item, relhandler)
1313 {
1314 g_assert(item != NULL);
1316 FlowtextKnotHolderEntity *entity_flowtext = new FlowtextKnotHolderEntity();
1317 entity_flowtext->create(desktop, item, this,
1318 _("Drag to resize the <b>flowed text frame</b>"));
1319 entity.push_back(entity_flowtext);
1320 }
1322 /*
1323 Local Variables:
1324 mode:c++
1325 c-file-style:"stroustrup"
1326 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1327 indent-tabs-mode:nil
1328 fill-column:99
1329 End:
1330 */
1331 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :