a9cf202983efc0b39492c7fb80b4b02f11400b69
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 "style.h"
30 #include "desktop.h"
31 #include "desktop-handles.h"
32 #include "sp-namedview.h"
33 #include "live_effects/effect.h"
35 #include "sp-pattern.h"
36 #include "sp-path.h"
38 #include <glibmm/i18n.h>
40 #include "object-edit.h"
42 #include <libnr/nr-scale-ops.h>
44 #include "xml/repr.h"
46 #include "2geom/isnan.h"
48 #define sp_round(v,m) (((v) < 0.0) ? ((ceil((v) / (m) - 0.5)) * (m)) : ((floor((v) / (m) + 0.5)) * (m)))
50 static KnotHolder *sp_lpe_knot_holder(SPItem *item, SPDesktop *desktop)
51 {
52 KnotHolder *knot_holder = new KnotHolder(desktop, item, NULL);
54 Inkscape::LivePathEffect::Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item));
55 effect->addHandles(knot_holder, desktop, item);
57 return knot_holder;
58 }
60 KnotHolder *
61 sp_item_knot_holder(SPItem *item, SPDesktop *desktop)
62 {
63 KnotHolder *knotholder = NULL;
65 if (SP_IS_LPE_ITEM(item) &&
66 sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item)) &&
67 sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))->isVisible() &&
68 sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))->providesKnotholder()) {
69 knotholder = sp_lpe_knot_holder(item, desktop);
70 } else if (SP_IS_RECT(item)) {
71 knotholder = new RectKnotHolder(desktop, item, NULL);
72 } else if (SP_IS_BOX3D(item)) {
73 knotholder = new Box3DKnotHolder(desktop, item, NULL);
74 } else if (SP_IS_ARC(item)) {
75 knotholder = new ArcKnotHolder(desktop, item, NULL);
76 } else if (SP_IS_STAR(item)) {
77 knotholder = new StarKnotHolder(desktop, item, NULL);
78 } else if (SP_IS_SPIRAL(item)) {
79 knotholder = new SpiralKnotHolder(desktop, item, NULL);
80 } else if (SP_IS_OFFSET(item)) {
81 knotholder = new OffsetKnotHolder(desktop, item, NULL);
82 } else if (SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) {
83 knotholder = new FlowtextKnotHolder(desktop, SP_FLOWTEXT(item)->get_frame(NULL), NULL);
84 } else if ((SP_OBJECT(item)->style->fill.isPaintserver())
85 && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style))) {
86 knotholder = new KnotHolder(desktop, item, NULL);
87 knotholder->add_pattern_knotholder();
88 }
90 return knotholder;
91 }
93 /* SPRect */
95 /* handle for horizontal rounding radius */
96 class RectKnotHolderEntityRX : public KnotHolderEntity {
97 public:
98 virtual Geom::Point knot_get();
99 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
100 virtual void knot_click(guint state);
101 };
103 /* handle for vertical rounding radius */
104 class RectKnotHolderEntityRY : public KnotHolderEntity {
105 public:
106 virtual Geom::Point knot_get();
107 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
108 virtual void knot_click(guint state);
109 };
111 /* handle for width/height adjustment */
112 class RectKnotHolderEntityWH : public KnotHolderEntity {
113 public:
114 virtual Geom::Point knot_get();
115 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
117 protected:
118 void set_internal(Geom::Point const &p, Geom::Point const &origin, guint state);
119 };
121 /* handle for x/y adjustment */
122 class RectKnotHolderEntityXY : public KnotHolderEntity {
123 public:
124 virtual Geom::Point knot_get();
125 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
126 };
128 Geom::Point
129 RectKnotHolderEntityRX::knot_get()
130 {
131 SPRect *rect = SP_RECT(item);
133 return Geom::Point(rect->x.computed + rect->width.computed - rect->rx.computed, rect->y.computed);
134 }
136 void
137 RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
138 {
139 SPRect *rect = SP_RECT(item);
141 //In general we cannot just snap this radius to an arbitrary point, as we have only a single
142 //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap
143 //the radius then we should have a constrained snap. snap_knot_position() is unconstrained
145 if (state & GDK_CONTROL_MASK) {
146 gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0;
147 rect->rx.computed = rect->ry.computed = CLAMP(rect->x.computed + rect->width.computed - p[Geom::X], 0.0, temp);
148 rect->rx._set = rect->ry._set = true;
150 } else {
151 rect->rx.computed = CLAMP(rect->x.computed + rect->width.computed - p[Geom::X], 0.0, rect->width.computed / 2.0);
152 rect->rx._set = true;
153 }
155 update_knot();
157 ((SPObject*)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
158 }
160 void
161 RectKnotHolderEntityRX::knot_click(guint state)
162 {
163 SPRect *rect = SP_RECT(item);
165 if (state & GDK_SHIFT_MASK) {
166 /* remove rounding from rectangle */
167 SP_OBJECT_REPR(rect)->setAttribute("rx", NULL);
168 SP_OBJECT_REPR(rect)->setAttribute("ry", NULL);
169 } else if (state & GDK_CONTROL_MASK) {
170 /* Ctrl-click sets the vertical rounding to be the same as the horizontal */
171 SP_OBJECT_REPR(rect)->setAttribute("ry", SP_OBJECT_REPR(rect)->attribute("rx"));
172 }
174 update_knot();
175 }
177 Geom::Point
178 RectKnotHolderEntityRY::knot_get()
179 {
180 SPRect *rect = SP_RECT(item);
182 return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->ry.computed);
183 }
185 void
186 RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
187 {
188 SPRect *rect = SP_RECT(item);
190 //In general we cannot just snap this radius to an arbitrary point, as we have only a single
191 //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap
192 //the radius then we should have a constrained snap. snap_knot_position() is unconstrained
194 if (state & GDK_CONTROL_MASK) {
195 gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0;
196 rect->rx.computed = rect->ry.computed = CLAMP(p[Geom::Y] - rect->y.computed, 0.0, temp);
197 rect->ry._set = rect->rx._set = true;
198 } else {
199 if (!rect->rx._set || rect->rx.computed == 0) {
200 rect->ry.computed = CLAMP(p[Geom::Y] - rect->y.computed,
201 0.0,
202 MIN(rect->height.computed / 2.0, rect->width.computed / 2.0));
203 } else {
204 rect->ry.computed = CLAMP(p[Geom::Y] - rect->y.computed,
205 0.0,
206 rect->height.computed / 2.0);
207 }
209 rect->ry._set = true;
210 }
212 update_knot();
214 ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
215 }
217 void
218 RectKnotHolderEntityRY::knot_click(guint state)
219 {
220 SPRect *rect = SP_RECT(item);
222 if (state & GDK_SHIFT_MASK) {
223 /* remove rounding */
224 SP_OBJECT_REPR(rect)->setAttribute("rx", NULL);
225 SP_OBJECT_REPR(rect)->setAttribute("ry", NULL);
226 } else if (state & GDK_CONTROL_MASK) {
227 /* Ctrl-click sets the vertical rounding to be the same as the horizontal */
228 SP_OBJECT_REPR(rect)->setAttribute("rx", SP_OBJECT_REPR(rect)->attribute("ry"));
229 }
230 }
232 #define SGN(x) ((x)>0?1:((x)<0?-1:0))
234 static void sp_rect_clamp_radii(SPRect *rect)
235 {
236 // clamp rounding radii so that they do not exceed width/height
237 if (2 * rect->rx.computed > rect->width.computed) {
238 rect->rx.computed = 0.5 * rect->width.computed;
239 rect->rx._set = true;
240 }
241 if (2 * rect->ry.computed > rect->height.computed) {
242 rect->ry.computed = 0.5 * rect->height.computed;
243 rect->ry._set = true;
244 }
245 }
247 Geom::Point
248 RectKnotHolderEntityWH::knot_get()
249 {
250 SPRect *rect = SP_RECT(item);
252 return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed);
253 }
255 void
256 RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &origin, guint state)
257 {
258 SPRect *rect = SP_RECT(item);
259 Geom::Point const s = snap_knot_position(p);
261 if (state & GDK_CONTROL_MASK) {
262 // original width/height when drag started
263 gdouble const w_orig = (origin[Geom::X] - rect->x.computed);
264 gdouble const h_orig = (origin[Geom::Y] - rect->y.computed);
266 //original ratio
267 gdouble const ratio = (w_orig / h_orig);
269 // mouse displacement since drag started
270 gdouble const minx = s[Geom::X] - origin[Geom::X];
271 gdouble const miny = s[Geom::Y] - origin[Geom::Y];
273 if (fabs(minx) > fabs(miny)) {
275 // snap to horizontal or diagonal
276 rect->width.computed = MAX(w_orig + minx, 0);
277 if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) {
278 // closer to the diagonal and in same-sign quarters, change both using ratio
279 rect->height.computed = MAX(h_orig + minx / ratio, 0);
280 } else {
281 // closer to the horizontal, change only width, height is h_orig
282 rect->height.computed = MAX(h_orig, 0);
283 }
285 } else {
286 // snap to vertical or diagonal
287 rect->height.computed = MAX(h_orig + miny, 0);
288 if (miny != 0 && fabs(minx/miny) > 0.5 * ratio && (SGN(minx) == SGN(miny))) {
289 // closer to the diagonal and in same-sign quarters, change both using ratio
290 rect->width.computed = MAX(w_orig + miny * ratio, 0);
291 } else {
292 // closer to the vertical, change only height, width is w_orig
293 rect->width.computed = MAX(w_orig, 0);
294 }
295 }
297 rect->width._set = rect->height._set = true;
299 } else {
300 // move freely
301 rect->width.computed = MAX(s[Geom::X] - rect->x.computed, 0);
302 rect->height.computed = MAX(s[Geom::Y] - rect->y.computed, 0);
303 rect->width._set = rect->height._set = true;
304 }
306 sp_rect_clamp_radii(rect);
308 ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
309 }
311 void
312 RectKnotHolderEntityWH::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
313 {
314 set_internal(p, origin, state);
315 update_knot();
316 }
318 Geom::Point
319 RectKnotHolderEntityXY::knot_get()
320 {
321 SPRect *rect = SP_RECT(item);
323 return Geom::Point(rect->x.computed, rect->y.computed);
324 }
326 void
327 RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
328 {
329 SPRect *rect = SP_RECT(item);
331 // opposite corner (unmoved)
332 gdouble opposite_x = (rect->x.computed + rect->width.computed);
333 gdouble opposite_y = (rect->y.computed + rect->height.computed);
335 // original width/height when drag started
336 gdouble w_orig = opposite_x - origin[Geom::X];
337 gdouble h_orig = opposite_y - origin[Geom::Y];
339 Geom::Point const s = snap_knot_position(p);
341 // mouse displacement since drag started
342 gdouble minx = s[Geom::X] - origin[Geom::X];
343 gdouble miny = s[Geom::Y] - origin[Geom::Y];
345 if (state & GDK_CONTROL_MASK) {
346 //original ratio
347 gdouble ratio = (w_orig / h_orig);
349 if (fabs(minx) > fabs(miny)) {
351 // snap to horizontal or diagonal
352 rect->x.computed = MIN(s[Geom::X], opposite_x);
353 rect->width.computed = MAX(w_orig - minx, 0);
354 if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) {
355 // closer to the diagonal and in same-sign quarters, change both using ratio
356 rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y);
357 rect->height.computed = MAX(h_orig - minx / ratio, 0);
358 } else {
359 // closer to the horizontal, change only width, height is h_orig
360 rect->y.computed = MIN(origin[Geom::Y], opposite_y);
361 rect->height.computed = MAX(h_orig, 0);
362 }
364 } else {
366 // snap to vertical or diagonal
367 rect->y.computed = MIN(s[Geom::Y], opposite_y);
368 rect->height.computed = MAX(h_orig - miny, 0);
369 if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) {
370 // closer to the diagonal and in same-sign quarters, change both using ratio
371 rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x);
372 rect->width.computed = MAX(w_orig - miny * ratio, 0);
373 } else {
374 // closer to the vertical, change only height, width is w_orig
375 rect->x.computed = MIN(origin[Geom::X], opposite_x);
376 rect->width.computed = MAX(w_orig, 0);
377 }
379 }
381 rect->width._set = rect->height._set = rect->x._set = rect->y._set = true;
383 } else {
384 // move freely
385 rect->x.computed = MIN(s[Geom::X], opposite_x);
386 rect->width.computed = MAX(w_orig - minx, 0);
387 rect->y.computed = MIN(s[Geom::Y], opposite_y);
388 rect->height.computed = MAX(h_orig - miny, 0);
389 rect->width._set = rect->height._set = rect->x._set = rect->y._set = true;
390 }
392 sp_rect_clamp_radii(rect);
394 update_knot();
396 ((SPObject *)rect)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
397 }
399 RectKnotHolder::RectKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
400 KnotHolder(desktop, item, relhandler)
401 {
402 RectKnotHolderEntityRX *entity_rx = new RectKnotHolderEntityRX();
403 RectKnotHolderEntityRY *entity_ry = new RectKnotHolderEntityRY();
404 RectKnotHolderEntityWH *entity_wh = new RectKnotHolderEntityWH();
405 RectKnotHolderEntityXY *entity_xy = new RectKnotHolderEntityXY();
406 entity_rx->create(desktop, item, this,
407 _("Adjust the <b>horizontal rounding</b> radius; with <b>Ctrl</b> "
408 "to make the vertical radius the same"),
409 SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR);
410 entity_ry->create(desktop, item, this,
411 _("Adjust the <b>vertical rounding</b> radius; with <b>Ctrl</b> "
412 "to make the horizontal radius the same"),
413 SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR);
414 entity_wh->create(desktop, item, this,
415 _("Adjust the <b>width and height</b> of the rectangle; with <b>Ctrl</b> "
416 "to lock ratio or stretch in one dimension only"),
417 SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
418 entity_xy->create(desktop, item, this,
419 _("Adjust the <b>width and height</b> of the rectangle; with <b>Ctrl</b> "
420 "to lock ratio or stretch in one dimension only"),
421 SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
422 entity.push_back(entity_rx);
423 entity.push_back(entity_ry);
424 entity.push_back(entity_wh);
425 entity.push_back(entity_xy);
427 add_pattern_knotholder();
428 }
430 /* Box3D (= the new 3D box structure) */
432 class Box3DKnotHolderEntity : public KnotHolderEntity {
433 public:
434 virtual Geom::Point knot_get() = 0;
435 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) = 0;
437 Geom::Point knot_get_generic(SPItem *item, unsigned int knot_id);
438 void knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &p, guint state);
439 };
441 Geom::Point
442 Box3DKnotHolderEntity::knot_get_generic(SPItem *item, unsigned int knot_id)
443 {
444 return box3d_get_corner_screen(SP_BOX3D(item), knot_id);
445 }
447 void
448 Box3DKnotHolderEntity::knot_set_generic(SPItem *item, unsigned int knot_id, Geom::Point const &new_pos, guint state)
449 {
450 Geom::Point const s = snap_knot_position(new_pos);
452 g_assert(item != NULL);
453 SPBox3D *box = SP_BOX3D(item);
454 Geom::Matrix const i2d (sp_item_i2d_affine (item));
456 Box3D::Axis movement;
457 if ((knot_id < 4) != (state & GDK_SHIFT_MASK)) {
458 movement = Box3D::XY;
459 } else {
460 movement = Box3D::Z;
461 }
463 box3d_set_corner (box, knot_id, s * i2d, movement, (state & GDK_CONTROL_MASK));
464 box3d_set_z_orders(box);
465 box3d_position_set(box);
466 }
468 class Box3DKnotHolderEntity0 : public Box3DKnotHolderEntity {
469 public:
470 virtual Geom::Point knot_get();
471 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
472 };
474 class Box3DKnotHolderEntity1 : public Box3DKnotHolderEntity {
475 public:
476 virtual Geom::Point knot_get();
477 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
478 };
480 class Box3DKnotHolderEntity2 : public Box3DKnotHolderEntity {
481 public:
482 virtual Geom::Point knot_get();
483 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
484 };
486 class Box3DKnotHolderEntity3 : public Box3DKnotHolderEntity {
487 public:
488 virtual Geom::Point knot_get();
489 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
490 };
492 class Box3DKnotHolderEntity4 : public Box3DKnotHolderEntity {
493 public:
494 virtual Geom::Point knot_get();
495 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
496 };
498 class Box3DKnotHolderEntity5 : public Box3DKnotHolderEntity {
499 public:
500 virtual Geom::Point knot_get();
501 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
502 };
504 class Box3DKnotHolderEntity6 : public Box3DKnotHolderEntity {
505 public:
506 virtual Geom::Point knot_get();
507 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
508 };
510 class Box3DKnotHolderEntity7 : public Box3DKnotHolderEntity {
511 public:
512 virtual Geom::Point knot_get();
513 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
514 };
516 class Box3DKnotHolderEntityCenter : public KnotHolderEntity {
517 public:
518 virtual Geom::Point knot_get();
519 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
520 };
522 Geom::Point
523 Box3DKnotHolderEntity0::knot_get()
524 {
525 return knot_get_generic(item, 0);
526 }
528 Geom::Point
529 Box3DKnotHolderEntity1::knot_get()
530 {
531 return knot_get_generic(item, 1);
532 }
534 Geom::Point
535 Box3DKnotHolderEntity2::knot_get()
536 {
537 return knot_get_generic(item, 2);
538 }
540 Geom::Point
541 Box3DKnotHolderEntity3::knot_get()
542 {
543 return knot_get_generic(item, 3);
544 }
546 Geom::Point
547 Box3DKnotHolderEntity4::knot_get()
548 {
549 return knot_get_generic(item, 4);
550 }
552 Geom::Point
553 Box3DKnotHolderEntity5::knot_get()
554 {
555 return knot_get_generic(item, 5);
556 }
558 Geom::Point
559 Box3DKnotHolderEntity6::knot_get()
560 {
561 return knot_get_generic(item, 6);
562 }
564 Geom::Point
565 Box3DKnotHolderEntity7::knot_get()
566 {
567 return knot_get_generic(item, 7);
568 }
570 Geom::Point
571 Box3DKnotHolderEntityCenter::knot_get()
572 {
573 return box3d_get_center_screen(SP_BOX3D(item));
574 }
576 void
577 Box3DKnotHolderEntity0::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
578 {
579 knot_set_generic(item, 0, new_pos, state);
580 }
582 void
583 Box3DKnotHolderEntity1::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
584 {
585 knot_set_generic(item, 1, new_pos, state);
586 }
588 void
589 Box3DKnotHolderEntity2::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
590 {
591 knot_set_generic(item, 2, new_pos, state);
592 }
594 void
595 Box3DKnotHolderEntity3::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
596 {
597 knot_set_generic(item, 3, new_pos, state);
598 }
600 void
601 Box3DKnotHolderEntity4::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
602 {
603 knot_set_generic(item, 4, new_pos, state);
604 }
606 void
607 Box3DKnotHolderEntity5::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
608 {
609 knot_set_generic(item, 5, new_pos, state);
610 }
612 void
613 Box3DKnotHolderEntity6::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
614 {
615 knot_set_generic(item, 6, new_pos, state);
616 }
618 void
619 Box3DKnotHolderEntity7::knot_set(Geom::Point const &new_pos, Geom::Point const &/*origin*/, guint state)
620 {
621 knot_set_generic(item, 7, new_pos, state);
622 }
624 void
625 Box3DKnotHolderEntityCenter::knot_set(Geom::Point const &new_pos, Geom::Point const &origin, guint state)
626 {
627 Geom::Point const s = snap_knot_position(new_pos);
629 SPBox3D *box = SP_BOX3D(item);
630 Geom::Matrix const i2d (sp_item_i2d_affine (item));
632 box3d_set_center (SP_BOX3D(item), s * i2d, origin * i2d, !(state & GDK_SHIFT_MASK) ? Box3D::XY : Box3D::Z,
633 state & GDK_CONTROL_MASK);
635 box3d_set_z_orders(box);
636 box3d_position_set(box);
637 }
639 Box3DKnotHolder::Box3DKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
640 KnotHolder(desktop, item, relhandler)
641 {
642 Box3DKnotHolderEntity0 *entity_corner0 = new Box3DKnotHolderEntity0();
643 Box3DKnotHolderEntity1 *entity_corner1 = new Box3DKnotHolderEntity1();
644 Box3DKnotHolderEntity2 *entity_corner2 = new Box3DKnotHolderEntity2();
645 Box3DKnotHolderEntity3 *entity_corner3 = new Box3DKnotHolderEntity3();
646 Box3DKnotHolderEntity4 *entity_corner4 = new Box3DKnotHolderEntity4();
647 Box3DKnotHolderEntity5 *entity_corner5 = new Box3DKnotHolderEntity5();
648 Box3DKnotHolderEntity6 *entity_corner6 = new Box3DKnotHolderEntity6();
649 Box3DKnotHolderEntity7 *entity_corner7 = new Box3DKnotHolderEntity7();
650 Box3DKnotHolderEntityCenter *entity_center = new Box3DKnotHolderEntityCenter();
652 entity_corner0->create(desktop, item, this,
653 _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; "
654 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
655 entity_corner1->create(desktop, item, this,
656 _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; "
657 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
658 entity_corner2->create(desktop, item, this,
659 _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; "
660 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
661 entity_corner3->create(desktop, item, this,
662 _("Resize box in X/Y direction; with <b>Shift</b> along the Z axis; "
663 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
664 entity_corner4->create(desktop, item, this,
665 _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; "
666 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
667 entity_corner5->create(desktop, item, this,
668 _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; "
669 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
670 entity_corner6->create(desktop, item, this,
671 _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; "
672 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
673 entity_corner7->create(desktop, item, this,
674 _("Resize box along the Z axis; with <b>Shift</b> in X/Y direction; "
675 "with <b>Ctrl</b> to constrain to the directions of edges or diagonals"));
676 entity_center->create(desktop, item, this,
677 _("Move the box in perspective"),
678 SP_KNOT_SHAPE_CROSS);
680 entity.push_back(entity_corner0);
681 entity.push_back(entity_corner1);
682 entity.push_back(entity_corner2);
683 entity.push_back(entity_corner3);
684 entity.push_back(entity_corner4);
685 entity.push_back(entity_corner5);
686 entity.push_back(entity_corner6);
687 entity.push_back(entity_corner7);
688 entity.push_back(entity_center);
690 add_pattern_knotholder();
691 }
693 /* SPArc */
695 class ArcKnotHolderEntityStart : public KnotHolderEntity {
696 public:
697 virtual Geom::Point knot_get();
698 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
699 };
701 class ArcKnotHolderEntityEnd : public KnotHolderEntity {
702 public:
703 virtual Geom::Point knot_get();
704 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
705 virtual void knot_click(guint state);
706 };
708 class ArcKnotHolderEntityRX : public KnotHolderEntity {
709 public:
710 virtual Geom::Point knot_get();
711 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
712 virtual void knot_click(guint state);
713 };
715 class ArcKnotHolderEntityRY : public KnotHolderEntity {
716 public:
717 virtual Geom::Point knot_get();
718 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
719 virtual void knot_click(guint state);
720 };
722 /*
723 * return values:
724 * 1 : inside
725 * 0 : on the curves
726 * -1 : outside
727 */
728 static gint
729 sp_genericellipse_side(SPGenericEllipse *ellipse, Geom::Point const &p)
730 {
731 gdouble dx = (p[Geom::X] - ellipse->cx.computed) / ellipse->rx.computed;
732 gdouble dy = (p[Geom::Y] - ellipse->cy.computed) / ellipse->ry.computed;
734 gdouble s = dx * dx + dy * dy;
735 if (s < 1.0) return 1;
736 if (s > 1.0) return -1;
737 return 0;
738 }
740 void
741 ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
742 {
743 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
744 int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
746 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
747 SPArc *arc = SP_ARC(item);
749 ge->closed = (sp_genericellipse_side(ge, p) == -1) ? TRUE : FALSE;
751 Geom::Point delta = p - Geom::Point(ge->cx.computed, ge->cy.computed);
752 Geom::Scale sc(ge->rx.computed, ge->ry.computed);
753 ge->start = atan2(delta * sc.inverse());
754 if ( ( state & GDK_CONTROL_MASK )
755 && snaps )
756 {
757 ge->start = sp_round(ge->start, M_PI/snaps);
758 }
759 sp_genericellipse_normalize(ge);
760 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
761 }
763 Geom::Point
764 ArcKnotHolderEntityStart::knot_get()
765 {
766 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
767 SPArc *arc = SP_ARC(item);
769 return sp_arc_get_xy(arc, ge->start);
770 }
772 void
773 ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
774 {
775 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
776 int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
778 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
779 SPArc *arc = SP_ARC(item);
781 ge->closed = (sp_genericellipse_side(ge, p) == -1) ? TRUE : FALSE;
783 Geom::Point delta = p - Geom::Point(ge->cx.computed, ge->cy.computed);
784 Geom::Scale sc(ge->rx.computed, ge->ry.computed);
785 ge->end = atan2(delta * sc.inverse());
786 if ( ( state & GDK_CONTROL_MASK )
787 && snaps )
788 {
789 ge->end = sp_round(ge->end, M_PI/snaps);
790 }
791 sp_genericellipse_normalize(ge);
792 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
793 }
795 Geom::Point
796 ArcKnotHolderEntityEnd::knot_get()
797 {
798 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
799 SPArc *arc = SP_ARC(item);
801 return sp_arc_get_xy(arc, ge->end);
802 }
805 void
806 ArcKnotHolderEntityEnd::knot_click(guint state)
807 {
808 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
810 if (state & GDK_SHIFT_MASK) {
811 ge->end = ge->start = 0;
812 ((SPObject *)ge)->updateRepr();
813 }
814 }
817 void
818 ArcKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
819 {
820 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
822 Geom::Point const s = snap_knot_position(p);
824 ge->rx.computed = fabs( ge->cx.computed - s[Geom::X] );
826 if ( state & GDK_CONTROL_MASK ) {
827 ge->ry.computed = ge->rx.computed;
828 }
830 ((SPObject *)item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
831 }
833 Geom::Point
834 ArcKnotHolderEntityRX::knot_get()
835 {
836 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
838 return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(ge->rx.computed, 0));
839 }
841 void
842 ArcKnotHolderEntityRX::knot_click(guint state)
843 {
844 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
846 if (state & GDK_CONTROL_MASK) {
847 ge->ry.computed = ge->rx.computed;
848 ((SPObject *)ge)->updateRepr();
849 }
850 }
852 void
853 ArcKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
854 {
855 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
857 Geom::Point const s = snap_knot_position(p);
859 ge->ry.computed = fabs( ge->cy.computed - s[Geom::Y] );
861 if ( state & GDK_CONTROL_MASK ) {
862 ge->rx.computed = ge->ry.computed;
863 }
865 ((SPObject *)item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
866 }
868 Geom::Point
869 ArcKnotHolderEntityRY::knot_get()
870 {
871 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
873 return (Geom::Point(ge->cx.computed, ge->cy.computed) - Geom::Point(0, ge->ry.computed));
874 }
876 void
877 ArcKnotHolderEntityRY::knot_click(guint state)
878 {
879 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
881 if (state & GDK_CONTROL_MASK) {
882 ge->rx.computed = ge->ry.computed;
883 ((SPObject *)ge)->updateRepr();
884 }
885 }
887 ArcKnotHolder::ArcKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
888 KnotHolder(desktop, item, relhandler)
889 {
890 ArcKnotHolderEntityRX *entity_rx = new ArcKnotHolderEntityRX();
891 ArcKnotHolderEntityRY *entity_ry = new ArcKnotHolderEntityRY();
892 ArcKnotHolderEntityStart *entity_start = new ArcKnotHolderEntityStart();
893 ArcKnotHolderEntityEnd *entity_end = new ArcKnotHolderEntityEnd();
894 entity_rx->create(desktop, item, this,
895 _("Adjust ellipse <b>width</b>, with <b>Ctrl</b> to make circle"),
896 SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
897 entity_ry->create(desktop, item, this,
898 _("Adjust ellipse <b>height</b>, with <b>Ctrl</b> to make circle"),
899 SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR);
900 entity_start->create(desktop, item, this,
901 _("Position the <b>start point</b> of the arc or segment; with <b>Ctrl</b>"
902 "to snap angle; drag <b>inside</b> the ellipse for arc, <b>outside</b> for segment"),
903 SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR);
904 entity_end->create(desktop, item, this,
905 _("Position the <b>end point</b> of the arc or segment; with <b>Ctrl</b> to snap angle; "
906 "drag <b>inside</b> the ellipse for arc, <b>outside</b> for segment"),
907 SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR);
908 entity.push_back(entity_rx);
909 entity.push_back(entity_ry);
910 entity.push_back(entity_start);
911 entity.push_back(entity_end);
913 add_pattern_knotholder();
914 }
916 /* SPStar */
918 class StarKnotHolderEntity1 : public KnotHolderEntity {
919 public:
920 virtual Geom::Point knot_get();
921 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
922 virtual void knot_click(guint state);
923 };
925 class StarKnotHolderEntity2 : public KnotHolderEntity {
926 public:
927 virtual Geom::Point knot_get();
928 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
929 virtual void knot_click(guint state);
930 };
932 void
933 StarKnotHolderEntity1::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
934 {
935 SPStar *star = SP_STAR(item);
937 Geom::Point const s = snap_knot_position(p);
939 Geom::Point d = s - to_2geom(star->center);
941 double arg1 = atan2(d);
942 double darg1 = arg1 - star->arg[0];
944 if (state & GDK_MOD1_MASK) {
945 star->randomized = darg1/(star->arg[0] - star->arg[1]);
946 } else if (state & GDK_SHIFT_MASK) {
947 star->rounded = darg1/(star->arg[0] - star->arg[1]);
948 } else if (state & GDK_CONTROL_MASK) {
949 star->r[0] = L2(d);
950 } else {
951 star->r[0] = L2(d);
952 star->arg[0] = arg1;
953 star->arg[1] += darg1;
954 }
955 ((SPObject *)star)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
956 }
958 void
959 StarKnotHolderEntity2::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
960 {
961 SPStar *star = SP_STAR(item);
963 Geom::Point const s = snap_knot_position(p);
965 if (star->flatsided == false) {
966 Geom::Point d = s - to_2geom(star->center);
968 double arg1 = atan2(d);
969 double darg1 = arg1 - star->arg[1];
971 if (state & GDK_MOD1_MASK) {
972 star->randomized = darg1/(star->arg[0] - star->arg[1]);
973 } else if (state & GDK_SHIFT_MASK) {
974 star->rounded = fabs(darg1/(star->arg[0] - star->arg[1]));
975 } else if (state & GDK_CONTROL_MASK) {
976 star->r[1] = L2(d);
977 star->arg[1] = star->arg[0] + M_PI / star->sides;
978 }
979 else {
980 star->r[1] = L2(d);
981 star->arg[1] = atan2(d);
982 }
983 ((SPObject *)star)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
984 }
985 }
987 Geom::Point
988 StarKnotHolderEntity1::knot_get()
989 {
990 g_assert(item != NULL);
992 SPStar *star = SP_STAR(item);
994 return sp_star_get_xy(star, SP_STAR_POINT_KNOT1, 0);
996 }
998 Geom::Point
999 StarKnotHolderEntity2::knot_get()
1000 {
1001 g_assert(item != NULL);
1003 SPStar *star = SP_STAR(item);
1005 return sp_star_get_xy(star, SP_STAR_POINT_KNOT2, 0);
1006 }
1008 static void
1009 sp_star_knot_click(SPItem *item, guint state)
1010 {
1011 SPStar *star = SP_STAR(item);
1013 if (state & GDK_MOD1_MASK) {
1014 star->randomized = 0;
1015 ((SPObject *)star)->updateRepr();
1016 } else if (state & GDK_SHIFT_MASK) {
1017 star->rounded = 0;
1018 ((SPObject *)star)->updateRepr();
1019 } else if (state & GDK_CONTROL_MASK) {
1020 star->arg[1] = star->arg[0] + M_PI / star->sides;
1021 ((SPObject *)star)->updateRepr();
1022 }
1023 }
1025 void
1026 StarKnotHolderEntity1::knot_click(guint state)
1027 {
1028 return sp_star_knot_click(item, state);
1029 }
1031 void
1032 StarKnotHolderEntity2::knot_click(guint state)
1033 {
1034 return sp_star_knot_click(item, state);
1035 }
1037 StarKnotHolder::StarKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
1038 KnotHolder(desktop, item, relhandler)
1039 {
1040 SPStar *star = SP_STAR(item);
1042 StarKnotHolderEntity1 *entity1 = new StarKnotHolderEntity1();
1043 entity1->create(desktop, item, this,
1044 _("Adjust the <b>tip radius</b> of the star or polygon; "
1045 "with <b>Shift</b> to round; with <b>Alt</b> to randomize"));
1046 entity.push_back(entity1);
1048 if (star->flatsided == false) {
1049 StarKnotHolderEntity2 *entity2 = new StarKnotHolderEntity2();
1050 entity2->create(desktop, item, this,
1051 _("Adjust the <b>base radius</b> of the star; with <b>Ctrl</b> to keep star rays "
1052 "radial (no skew); with <b>Shift</b> to round; with <b>Alt</b> to randomize"));
1053 entity.push_back(entity2);
1054 }
1056 add_pattern_knotholder();
1057 }
1059 /* SPSpiral */
1061 class SpiralKnotHolderEntityInner : public KnotHolderEntity {
1062 public:
1063 virtual Geom::Point knot_get();
1064 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
1065 virtual void knot_click(guint state);
1066 };
1068 class SpiralKnotHolderEntityOuter : public KnotHolderEntity {
1069 public:
1070 virtual Geom::Point knot_get();
1071 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
1072 };
1075 /*
1076 * set attributes via inner (t=t0) knot point:
1077 * [default] increase/decrease inner point
1078 * [shift] increase/decrease inner and outer arg synchronizely
1079 * [control] constrain inner arg to round per PI/4
1080 */
1081 void
1082 SpiralKnotHolderEntityInner::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
1083 {
1084 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1085 int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
1087 SPSpiral *spiral = SP_SPIRAL(item);
1089 gdouble dx = p[Geom::X] - spiral->cx;
1090 gdouble dy = p[Geom::Y] - spiral->cy;
1092 if (state & GDK_MOD1_MASK) {
1093 // adjust divergence by vertical drag, relative to rad
1094 double new_exp = (spiral->rad + dy)/(spiral->rad);
1095 spiral->exp = new_exp > 0? new_exp : 0;
1096 } else {
1097 // roll/unroll from inside
1098 gdouble arg_t0;
1099 sp_spiral_get_polar(spiral, spiral->t0, NULL, &arg_t0);
1101 gdouble arg_tmp = atan2(dy, dx) - arg_t0;
1102 gdouble arg_t0_new = arg_tmp - floor((arg_tmp+M_PI)/(2.0*M_PI))*2.0*M_PI + arg_t0;
1103 spiral->t0 = (arg_t0_new - spiral->arg) / (2.0*M_PI*spiral->revo);
1105 /* round inner arg per PI/snaps, if CTRL is pressed */
1106 if ( ( state & GDK_CONTROL_MASK )
1107 && ( fabs(spiral->revo) > SP_EPSILON_2 )
1108 && ( snaps != 0 ) ) {
1109 gdouble arg = 2.0*M_PI*spiral->revo*spiral->t0 + spiral->arg;
1110 spiral->t0 = (sp_round(arg, M_PI/snaps) - spiral->arg)/(2.0*M_PI*spiral->revo);
1111 }
1113 spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999);
1114 }
1116 ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1117 }
1119 /*
1120 * set attributes via outer (t=1) knot point:
1121 * [default] increase/decrease revolution factor
1122 * [control] constrain inner arg to round per PI/4
1123 */
1124 void
1125 SpiralKnotHolderEntityOuter::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state)
1126 {
1127 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1128 int snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
1130 SPSpiral *spiral = SP_SPIRAL(item);
1132 gdouble dx = p[Geom::X] - spiral->cx;
1133 gdouble dy = p[Geom::Y] - spiral->cy;
1135 if (state & GDK_SHIFT_MASK) { // rotate without roll/unroll
1136 spiral->arg = atan2(dy, dx) - 2.0*M_PI*spiral->revo;
1137 if (!(state & GDK_MOD1_MASK)) {
1138 // if alt not pressed, change also rad; otherwise it is locked
1139 spiral->rad = MAX(hypot(dx, dy), 0.001);
1140 }
1141 if ( ( state & GDK_CONTROL_MASK )
1142 && snaps ) {
1143 spiral->arg = sp_round(spiral->arg, M_PI/snaps);
1144 }
1145 } else { // roll/unroll
1146 // arg of the spiral outer end
1147 double arg_1;
1148 sp_spiral_get_polar(spiral, 1, NULL, &arg_1);
1150 // its fractional part after the whole turns are subtracted
1151 double arg_r = arg_1 - sp_round(arg_1, 2.0*M_PI);
1153 // arg of the mouse point relative to spiral center
1154 double mouse_angle = atan2(dy, dx);
1155 if (mouse_angle < 0)
1156 mouse_angle += 2*M_PI;
1158 // snap if ctrl
1159 if ( ( state & GDK_CONTROL_MASK ) && snaps ) {
1160 mouse_angle = sp_round(mouse_angle, M_PI/snaps);
1161 }
1163 // by how much we want to rotate the outer point
1164 double diff = mouse_angle - arg_r;
1165 if (diff > M_PI)
1166 diff -= 2*M_PI;
1167 else if (diff < -M_PI)
1168 diff += 2*M_PI;
1170 // calculate the new rad;
1171 // the value of t corresponding to the angle arg_1 + diff:
1172 double t_temp = ((arg_1 + diff) - spiral->arg)/(2*M_PI*spiral->revo);
1173 // the rad at that t:
1174 double rad_new = 0;
1175 if (t_temp > spiral->t0)
1176 sp_spiral_get_polar(spiral, t_temp, &rad_new, NULL);
1178 // change the revo (converting diff from radians to the number of turns)
1179 spiral->revo += diff/(2*M_PI);
1180 if (spiral->revo < 1e-3)
1181 spiral->revo = 1e-3;
1183 // if alt not pressed and the values are sane, change the rad
1184 if (!(state & GDK_MOD1_MASK) && rad_new > 1e-3 && rad_new/spiral->rad < 2) {
1185 // adjust t0 too so that the inner point stays unmoved
1186 double r0;
1187 sp_spiral_get_polar(spiral, spiral->t0, &r0, NULL);
1188 spiral->rad = rad_new;
1189 spiral->t0 = pow(r0 / spiral->rad, 1.0/spiral->exp);
1190 }
1191 if (!IS_FINITE(spiral->t0)) spiral->t0 = 0.0;
1192 spiral->t0 = CLAMP(spiral->t0, 0.0, 0.999);
1193 }
1195 ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1196 }
1198 Geom::Point
1199 SpiralKnotHolderEntityInner::knot_get()
1200 {
1201 SPSpiral *spiral = SP_SPIRAL(item);
1203 return sp_spiral_get_xy(spiral, spiral->t0);
1204 }
1206 Geom::Point
1207 SpiralKnotHolderEntityOuter::knot_get()
1208 {
1209 SPSpiral *spiral = SP_SPIRAL(item);
1211 return sp_spiral_get_xy(spiral, 1.0);
1212 }
1214 void
1215 SpiralKnotHolderEntityInner::knot_click(guint state)
1216 {
1217 SPSpiral *spiral = SP_SPIRAL(item);
1219 if (state & GDK_MOD1_MASK) {
1220 spiral->exp = 1;
1221 ((SPObject *)spiral)->updateRepr();
1222 } else if (state & GDK_SHIFT_MASK) {
1223 spiral->t0 = 0;
1224 ((SPObject *)spiral)->updateRepr();
1225 }
1226 }
1228 SpiralKnotHolder::SpiralKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
1229 KnotHolder(desktop, item, relhandler)
1230 {
1231 SpiralKnotHolderEntityInner *entity_inner = new SpiralKnotHolderEntityInner();
1232 SpiralKnotHolderEntityOuter *entity_outer = new SpiralKnotHolderEntityOuter();
1233 entity_inner->create(desktop, item, this,
1234 _("Roll/unroll the spiral from <b>inside</b>; with <b>Ctrl</b> to snap angle; "
1235 "with <b>Alt</b> to converge/diverge"));
1236 entity_outer->create(desktop, item, this,
1237 _("Roll/unroll the spiral from <b>outside</b>; with <b>Ctrl</b> to snap angle; "
1238 "with <b>Shift</b> to scale/rotate"));
1239 entity.push_back(entity_inner);
1240 entity.push_back(entity_outer);
1242 add_pattern_knotholder();
1243 }
1245 /* SPOffset */
1247 class OffsetKnotHolderEntity : public KnotHolderEntity {
1248 public:
1249 virtual Geom::Point knot_get();
1250 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
1251 };
1253 void
1254 OffsetKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/)
1255 {
1256 SPOffset *offset = SP_OFFSET(item);
1258 offset->rad = sp_offset_distance_to_original(offset, p);
1259 offset->knot = p;
1260 offset->knotSet = true;
1262 ((SPObject *)offset)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1263 }
1266 Geom::Point
1267 OffsetKnotHolderEntity::knot_get()
1268 {
1269 SPOffset *offset = SP_OFFSET(item);
1271 Geom::Point np;
1272 sp_offset_top_point(offset,&np);
1273 return np;
1274 }
1276 OffsetKnotHolder::OffsetKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
1277 KnotHolder(desktop, item, relhandler)
1278 {
1279 OffsetKnotHolderEntity *entity_offset = new OffsetKnotHolderEntity();
1280 entity_offset->create(desktop, item, this,
1281 _("Adjust the <b>offset distance</b>"));
1282 entity.push_back(entity_offset);
1284 add_pattern_knotholder();
1285 }
1287 // TODO: this is derived from RectKnotHolderEntityWH because it used the same static function
1288 // set_internal as the latter before KnotHolderEntity was C++ified. Check whether this also makes
1289 // sense logically.
1290 class FlowtextKnotHolderEntity : public RectKnotHolderEntityWH {
1291 public:
1292 virtual Geom::Point knot_get();
1293 virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
1294 };
1296 Geom::Point
1297 FlowtextKnotHolderEntity::knot_get()
1298 {
1299 SPRect *rect = SP_RECT(item);
1301 return Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed);
1302 }
1304 void
1305 FlowtextKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state)
1306 {
1307 set_internal(p, origin, state);
1308 }
1310 FlowtextKnotHolder::FlowtextKnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFunc relhandler) :
1311 KnotHolder(desktop, item, relhandler)
1312 {
1313 g_assert(item != NULL);
1315 FlowtextKnotHolderEntity *entity_flowtext = new FlowtextKnotHolderEntity();
1316 entity_flowtext->create(desktop, item, this,
1317 _("Drag to resize the <b>flowed text frame</b>"));
1318 entity.push_back(entity_flowtext);
1319 }
1321 /*
1322 Local Variables:
1323 mode:c++
1324 c-file-style:"stroustrup"
1325 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1326 indent-tabs-mode:nil
1327 fill-column:99
1328 End:
1329 */
1330 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :