1
#define __GRADIENT_DRAG_C__
4
* On-canvas gradient dragging
7
* bulia byak <buliabyak@users.sf.net>
8
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
10
* Copyright (C) 2007 Johan Engelen
11
* Copyright (C) 2005 Authors
13
* Released under GNU GPL, read the file 'COPYING' for more information
20
#include <glibmm/i18n.h>
24
#include "desktop-handles.h"
25
#include "selection.h"
27
#include "desktop-style.h"
29
#include "display/sp-ctrlline.h"
30
#include "display/sp-canvas-util.h"
32
#include "svg/css-ostringstream.h"
34
#include "libnr/nr-point-fns.h"
35
#include "preferences.h"
39
#include "sp-linear-gradient.h"
40
#include "sp-radial-gradient.h"
41
#include "gradient-chemistry.h"
42
#include "gradient-drag.h"
45
#include "sp-namedview.h"
46
#include "selection-chemistry.h"
48
#define GR_KNOT_COLOR_NORMAL 0xffffff00
49
#define GR_KNOT_COLOR_MOUSEOVER 0xff000000
50
#define GR_KNOT_COLOR_SELECTED 0x0000ff00
52
#define GR_LINE_COLOR_FILL 0x0000ff7f
53
#define GR_LINE_COLOR_STROKE 0x9999007f
55
// screen pixels between knots when they snap:
58
// absolute distance between gradient points for them to become a single dragger when the drag is created:
59
#define MERGE_DIST 0.1
61
// knot shapes corresponding to GrPointType enum
62
SPKnotShapeType gr_knot_shapes [] = {
63
SP_KNOT_SHAPE_SQUARE, //POINT_LG_BEGIN
64
SP_KNOT_SHAPE_CIRCLE, //POINT_LG_END
65
SP_KNOT_SHAPE_DIAMOND, //POINT_LG_MID
66
SP_KNOT_SHAPE_SQUARE, // POINT_RG_CENTER
67
SP_KNOT_SHAPE_CIRCLE, // POINT_RG_R1
68
SP_KNOT_SHAPE_CIRCLE, // POINT_RG_R2
69
SP_KNOT_SHAPE_CROSS, // POINT_RG_FOCUS
70
SP_KNOT_SHAPE_DIAMOND, //POINT_RG_MID1
71
SP_KNOT_SHAPE_DIAMOND //POINT_RG_MID2
74
const gchar *gr_knot_descr [] = {
75
N_("Linear gradient <b>start</b>"), //POINT_LG_BEGIN
76
N_("Linear gradient <b>end</b>"),
77
N_("Linear gradient <b>mid stop</b>"),
78
N_("Radial gradient <b>center</b>"),
79
N_("Radial gradient <b>radius</b>"),
80
N_("Radial gradient <b>radius</b>"),
81
N_("Radial gradient <b>focus</b>"), // POINT_RG_FOCUS
82
N_("Radial gradient <b>mid stop</b>"),
83
N_("Radial gradient <b>mid stop</b>")
87
gr_drag_sel_changed(Inkscape::Selection */*selection*/, gpointer data)
89
GrDrag *drag = (GrDrag *) data;
90
drag->updateDraggers ();
92
drag->updateLevels ();
96
gr_drag_sel_modified (Inkscape::Selection */*selection*/, guint /*flags*/, gpointer data)
98
GrDrag *drag = (GrDrag *) data;
99
if (drag->local_change) {
100
drag->local_change = false;
102
drag->updateDraggers ();
104
drag->updateLines ();
105
drag->updateLevels ();
109
When a _query_style_signal is received, check that \a property requests fill/stroke/opacity (otherwise
110
skip), and fill the \a style with the averaged color of all draggables of the selected dragger, if
114
gr_drag_style_query (SPStyle *style, int property, gpointer data)
116
GrDrag *drag = (GrDrag *) data;
118
if (property != QUERY_STYLE_PROPERTY_FILL && property != QUERY_STYLE_PROPERTY_STROKE && property != QUERY_STYLE_PROPERTY_MASTEROPACITY) {
119
return QUERY_STYLE_NOTHING;
122
if (!drag->selected) {
123
return QUERY_STYLE_NOTHING;
125
int ret = QUERY_STYLE_NOTHING;
128
cf[0] = cf[1] = cf[2] = cf[3] = 0;
132
for (GList *i = drag->selected; i != NULL; i = i->next) { // for all selected draggers
133
GrDragger *d = (GrDragger *) i->data;
134
for (GSList const* j = d->draggables; j != NULL; j = j->next) { // for all draggables of dragger
135
GrDraggable *draggable = (GrDraggable *) j->data;
137
if (ret == QUERY_STYLE_NOTHING) {
138
ret = QUERY_STYLE_SINGLE;
139
} else if (ret == QUERY_STYLE_SINGLE) {
140
ret = QUERY_STYLE_MULTIPLE_AVERAGED;
143
guint32 c = sp_item_gradient_stop_query_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
144
cf[0] += SP_RGBA32_R_F (c);
145
cf[1] += SP_RGBA32_G_F (c);
146
cf[2] += SP_RGBA32_B_F (c);
147
cf[3] += SP_RGBA32_A_F (c);
159
// set both fill and stroke with our stop-color and stop-opacity
161
style->fill.setColor( cf[0], cf[1], cf[2] );
162
style->fill.set = TRUE;
163
style->stroke.clear();
164
style->stroke.setColor( cf[0], cf[1], cf[2] );
165
style->stroke.set = TRUE;
167
style->fill_opacity.value = SP_SCALE24_FROM_FLOAT (1.0);
168
style->fill_opacity.set = TRUE;
169
style->stroke_opacity.value = SP_SCALE24_FROM_FLOAT (1.0);
170
style->stroke_opacity.set = TRUE;
172
style->opacity.value = SP_SCALE24_FROM_FLOAT (cf[3]);
173
style->opacity.set = TRUE;
181
gr_drag_style_set (const SPCSSAttr *css, gpointer data)
183
GrDrag *drag = (GrDrag *) data;
188
SPCSSAttr *stop = sp_repr_css_attr_new ();
190
// See if the css contains interesting properties, and if so, translate them into the format
191
// acceptable for gradient stops
193
// any of color properties, in order of increasing priority:
194
if (css->attribute("flood-color"))
195
sp_repr_css_set_property (stop, "stop-color", css->attribute("flood-color"));
197
if (css->attribute("lighting-color"))
198
sp_repr_css_set_property (stop, "stop-color", css->attribute("lighting-color"));
200
if (css->attribute("color"))
201
sp_repr_css_set_property (stop, "stop-color", css->attribute("color"));
203
if (css->attribute("stroke") && strcmp(css->attribute("stroke"), "none"))
204
sp_repr_css_set_property (stop, "stop-color", css->attribute("stroke"));
206
if (css->attribute("fill") && strcmp(css->attribute("fill"), "none"))
207
sp_repr_css_set_property (stop, "stop-color", css->attribute("fill"));
209
if (css->attribute("stop-color"))
210
sp_repr_css_set_property (stop, "stop-color", css->attribute("stop-color"));
213
if (css->attribute("stop-opacity")) { // direct setting of stop-opacity has priority
214
sp_repr_css_set_property (stop, "stop-opacity", css->attribute("stop-opacity"));
215
} else { // multiply all opacity properties:
216
gdouble accumulated = 1.0;
217
accumulated *= sp_svg_read_percentage(css->attribute("flood-opacity"), 1.0);
218
accumulated *= sp_svg_read_percentage(css->attribute("opacity"), 1.0);
219
accumulated *= sp_svg_read_percentage(css->attribute("stroke-opacity"), 1.0);
220
accumulated *= sp_svg_read_percentage(css->attribute("fill-opacity"), 1.0);
222
Inkscape::CSSOStringStream os;
224
sp_repr_css_set_property (stop, "stop-opacity", os.str().c_str());
226
if ((css->attribute("fill") && !css->attribute("stroke") && !strcmp(css->attribute("fill"), "none")) ||
227
(css->attribute("stroke") && !css->attribute("fill") && !strcmp(css->attribute("stroke"), "none")))
228
sp_repr_css_set_property (stop, "stop-opacity", "0"); // if a single fill/stroke property is set to none, don't change color, set opacity to 0
231
if (!stop->attributeList()) { // nothing for us here, pass it on
232
sp_repr_css_attr_unref(stop);
236
for (GList const* sel = drag->selected; sel != NULL; sel = sel->next) { // for all selected draggers
237
GrDragger* dragger = (GrDragger*) sel->data;
238
for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger
239
GrDraggable *draggable = (GrDraggable *) i->data;
241
drag->local_change = true;
242
sp_item_gradient_stop_set_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop);
246
//sp_repr_css_print(stop);
247
sp_repr_css_attr_unref(stop);
251
guint32 GrDrag::getColor()
253
if (!selected) return 0;
256
cf[0] = cf[1] = cf[2] = cf[3] = 0;
260
for (GList *i = selected; i != NULL; i = i->next) { // for all selected draggers
261
GrDragger *d = (GrDragger *) i->data;
262
for (GSList const* j = d->draggables; j != NULL; j = j->next) { // for all draggables of dragger
263
GrDraggable *draggable = (GrDraggable *) j->data;
265
guint32 c = sp_item_gradient_stop_query_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
266
cf[0] += SP_RGBA32_R_F (c);
267
cf[1] += SP_RGBA32_G_F (c);
268
cf[2] += SP_RGBA32_B_F (c);
269
cf[3] += SP_RGBA32_A_F (c);
282
return SP_RGBA32_F_COMPOSE(cf[0], cf[1], cf[2], cf[3]);
286
GrDrag::addStopNearPoint (SPItem *item, Geom::Point mouse_p, double tolerance)
288
gfloat offset; // type of SPStop.offset = gfloat
289
SPGradient *gradient;
290
bool fill_or_stroke = true;
291
bool r1_knot = false;
293
bool addknot = false;
295
gradient = sp_item_gradient (item, fill_or_stroke);
296
if (SP_IS_LINEARGRADIENT(gradient)) {
297
Geom::Point begin = sp_item_gradient_get_coords(item, POINT_LG_BEGIN, 0, fill_or_stroke);
298
Geom::Point end = sp_item_gradient_get_coords(item, POINT_LG_END, 0, fill_or_stroke);
300
Geom::Point nearest = snap_vector_midpoint (mouse_p, begin, end, 0);
301
double dist_screen = Geom::L2 (mouse_p - nearest);
302
if ( dist_screen < tolerance ) {
304
offset = get_offset_between_points(nearest, begin, end);
306
break; // break out of the while loop: add only one knot
308
} else if (SP_IS_RADIALGRADIENT(gradient)) {
309
Geom::Point begin = sp_item_gradient_get_coords(item, POINT_RG_CENTER, 0, fill_or_stroke);
310
Geom::Point end = sp_item_gradient_get_coords(item, POINT_RG_R1, 0, fill_or_stroke);
311
Geom::Point nearest = snap_vector_midpoint (mouse_p, begin, end, 0);
312
double dist_screen = Geom::L2 (mouse_p - nearest);
313
if ( dist_screen < tolerance ) {
314
offset = get_offset_between_points(nearest, begin, end);
317
break; // break out of the while loop: add only one knot
320
end = sp_item_gradient_get_coords(item, POINT_RG_R2, 0, fill_or_stroke);
321
nearest = snap_vector_midpoint (mouse_p, begin, end, 0);
322
dist_screen = Geom::L2 (mouse_p - nearest);
323
if ( dist_screen < tolerance ) {
324
offset = get_offset_between_points(nearest, begin, end);
327
break; // break out of the while loop: add only one knot
330
fill_or_stroke = !fill_or_stroke;
331
} while (!fill_or_stroke && !addknot) ;
334
SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false);
335
SPStop* prev_stop = sp_first_stop(vector);
336
SPStop* next_stop = sp_next_stop(prev_stop);
338
while ( (next_stop) && (next_stop->offset < offset) ) {
339
prev_stop = next_stop;
340
next_stop = sp_next_stop(next_stop);
344
// logical error: the endstop should have offset 1 and should always be more than this offset here
349
SPStop *newstop = sp_vector_add_stop (vector, prev_stop, next_stop, offset);
350
sp_gradient_ensure_vector (gradient);
361
GrDrag::dropColor(SPItem */*item*/, gchar const *c, Geom::Point p)
363
// first, see if we can drop onto one of the existing draggers
364
for (GList *i = draggers; i != NULL; i = i->next) { // for all draggables of dragger
365
GrDragger *d = (GrDragger *) i->data;
367
if (Geom::L2(p - d->point)*desktop->current_zoom() < 5) {
368
SPCSSAttr *stop = sp_repr_css_attr_new ();
369
sp_repr_css_set_property (stop, "stop-color", c);
370
sp_repr_css_set_property (stop, "stop-opacity", "1");
371
for (GSList *j = d->draggables; j != NULL; j = j->next) { // for all draggables of dragger
372
GrDraggable *draggable = (GrDraggable *) j->data;
374
sp_item_gradient_stop_set_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop);
376
sp_repr_css_attr_unref(stop);
381
// now see if we're over line and create a new stop
382
bool over_line = false;
383
SPCtrlLine *line = NULL;
385
for (GSList *l = lines; (l != NULL) && (!over_line); l = l->next) {
386
line = (SPCtrlLine*) l->data;
387
Geom::Point nearest = snap_vector_midpoint (p, line->s, line->e, 0);
388
double dist_screen = Geom::L2 (p - nearest) * desktop->current_zoom();
389
if (line->item && dist_screen < 5) {
390
SPStop *stop = addStopNearPoint (line->item, p, 5/desktop->current_zoom());
392
SPCSSAttr *css = sp_repr_css_attr_new ();
393
sp_repr_css_set_property (css, "stop-color", c);
394
sp_repr_css_set_property (css, "stop-opacity", "1");
395
sp_repr_css_change (SP_OBJECT_REPR (stop), css, "style");
406
GrDrag::GrDrag(SPDesktop *desktop) {
408
this->desktop = desktop;
410
this->selection = sp_desktop_selection(desktop);
412
this->draggers = NULL;
414
this->selected = NULL;
416
this->hor_levels.clear();
417
this->vert_levels.clear();
419
this->local_change = false;
421
this->sel_changed_connection = this->selection->connectChanged(
423
sigc::ptr_fun(&gr_drag_sel_changed),
427
this->sel_modified_connection = this->selection->connectModified(
429
sigc::ptr_fun(&gr_drag_sel_modified),
433
this->style_set_connection = this->desktop->connectSetStyle(
435
sigc::ptr_fun(&gr_drag_style_set),
439
this->style_query_connection = this->desktop->connectQueryStyle(
441
sigc::ptr_fun(&gr_drag_style_query),
445
this->updateDraggers ();
446
this->updateLines ();
447
this->updateLevels ();
449
if (desktop->gr_item) {
450
this->setSelected (getDraggerFor (desktop->gr_item, desktop->gr_point_type, desktop->gr_point_i, desktop->gr_fill_or_stroke));
456
this->sel_changed_connection.disconnect();
457
this->sel_modified_connection.disconnect();
458
this->style_set_connection.disconnect();
459
this->style_query_connection.disconnect();
461
if (this->selected) {
462
GrDraggable *draggable = (GrDraggable *) ((GrDragger*)this->selected->data)->draggables->data;
463
desktop->gr_item = draggable->item;
464
desktop->gr_point_type = draggable->point_type;
465
desktop->gr_point_i = draggable->point_i;
466
desktop->gr_fill_or_stroke = draggable->fill_or_stroke;
468
desktop->gr_item = NULL;
469
desktop->gr_point_type = 0;
470
desktop->gr_point_i = 0;
471
desktop->gr_fill_or_stroke = true;
475
for (GList *l = this->draggers; l != NULL; l = l->next) {
476
delete ((GrDragger *) l->data);
478
g_list_free (this->draggers);
479
this->draggers = NULL;
480
this->selected = NULL;
482
for (GSList *l = this->lines; l != NULL; l = l->next) {
483
gtk_object_destroy( GTK_OBJECT (l->data));
485
g_slist_free (this->lines);
489
GrDraggable::GrDraggable (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke)
492
this->point_type = point_type;
493
this->point_i = point_i;
494
this->fill_or_stroke = fill_or_stroke;
496
g_object_ref (G_OBJECT (this->item));
499
GrDraggable::~GrDraggable ()
501
g_object_unref (G_OBJECT (this->item));
506
GrDraggable::getServer ()
511
SPObject *server = NULL;
513
server = SP_OBJECT_STYLE_FILL_SERVER (item);
515
server = SP_OBJECT_STYLE_STROKE_SERVER (item);
521
boost::optional<Geom::Point>
522
get_snap_vector (Geom::Point p, Geom::Point o, double snap, double initial)
524
double r = L2 (p - o);
526
return boost::optional<Geom::Point>();
529
double angle = atan2 (p - o);
530
// snap angle to snaps increments, starting from initial:
531
double a_snapped = initial + floor((angle - initial)/snap + 0.5) * snap;
532
// calculate the new position and subtract p to get the vector:
533
return (o + r * Geom::Point(cos(a_snapped), sin(a_snapped)) - p);
537
gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gpointer data)
539
GrDragger *dragger = (GrDragger *) data;
540
GrDrag *drag = dragger->parent;
542
Geom::Point p = ppointer;
544
SPDesktop *desktop = dragger->parent->desktop;
545
SnapManager &m = desktop->namedview->snap_manager;
546
double snap_dist = m.snapprefs.getObjectTolerance() / dragger->parent->desktop->current_zoom();
548
if (state & GDK_SHIFT_MASK) {
549
// with Shift; unsnap if we carry more than one draggable
550
if (dragger->draggables && dragger->draggables->next) {
551
// create a new dragger
552
GrDragger *dr_new = new GrDragger (dragger->parent, dragger->point, NULL);
553
dragger->parent->draggers = g_list_prepend (dragger->parent->draggers, dr_new);
554
// relink to it all but the first draggable in the list
555
for (GSList const* i = dragger->draggables->next; i != NULL; i = i->next) {
556
GrDraggable *draggable = (GrDraggable *) i->data;
557
dr_new->addDraggable (draggable);
559
dr_new->updateKnotShape();
560
g_slist_free (dragger->draggables->next);
561
dragger->draggables->next = NULL;
562
dragger->updateKnotShape();
563
dragger->updateTip();
565
} else if (!(state & GDK_CONTROL_MASK)) {
566
// without Shift or Ctrl; see if we need to snap to another dragger
567
for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) {
568
GrDragger *d_new = (GrDragger *) di->data;
569
if (dragger->mayMerge(d_new) && Geom::L2 (d_new->point - p) < snap_dist) {
572
for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger
573
GrDraggable *draggable = (GrDraggable *) i->data;
574
// copy draggable to d_new:
575
GrDraggable *da_new = new GrDraggable (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
576
d_new->addDraggable (da_new);
579
// unlink and delete this dragger
580
dragger->parent->draggers = g_list_remove (dragger->parent->draggers, dragger);
583
// update the new merged dragger
584
d_new->fireDraggables(true, false, true);
585
d_new->parent->updateLines();
586
d_new->parent->setSelected (d_new);
587
d_new->updateKnotShape ();
589
d_new->updateDependencies(true);
590
sp_document_done (sp_desktop_document (d_new->parent->desktop), SP_VERB_CONTEXT_GRADIENT,
591
_("Merge gradient handles"));
597
if (!((state & GDK_SHIFT_MASK) || ((state & GDK_CONTROL_MASK) && (state & GDK_MOD1_MASK)))) {
598
// Try snapping to the grid or guides
600
Inkscape::SnappedPoint s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p), Inkscape::SNAPSOURCE_HANDLE);
601
if (s.getSnapped()) {
603
sp_knot_moveto (knot, p);
604
} else if (m.snapprefs.getSnapEnabledGlobally() && m.snapprefs.getSnapModeNode() && !(m.snapprefs.getSnapPostponedGlobally())) {
605
bool was_snapped = false;
606
double dist = NR_HUGE;
607
// No snapping so far, let's see if we need to snap to any of the levels
608
for (guint i = 0; i < dragger->parent->hor_levels.size(); i++) {
609
dist = fabs(p[Geom::Y] - dragger->parent->hor_levels[i]);
610
if (dist < snap_dist) {
611
p[Geom::Y] = dragger->parent->hor_levels[i];
612
s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false);
614
sp_knot_moveto (knot, p);
617
for (guint i = 0; i < dragger->parent->vert_levels.size(); i++) {
618
dist = fabs(p[Geom::X] - dragger->parent->vert_levels[i]);
619
if (dist < snap_dist) {
620
p[Geom::X] = dragger->parent->vert_levels[i];
621
s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false);
623
sp_knot_moveto (knot, p);
627
desktop->snapindicator->set_new_snaptarget(s);
632
if (state & GDK_CONTROL_MASK) {
633
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
634
unsigned snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12));
635
/* 0 means no snapping. */
637
// This list will store snap vectors from all draggables of dragger
638
GSList *snap_vectors = NULL;
640
for (GSList const* i = dragger->draggables; i != NULL; i = i->next) {
641
GrDraggable *draggable = (GrDraggable *) i->data;
643
Geom::Point dr_snap(Geom::infinity(), Geom::infinity());
645
if (draggable->point_type == POINT_LG_BEGIN || draggable->point_type == POINT_LG_END) {
646
for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) {
647
GrDragger *d_new = (GrDragger *) di->data;
648
if (d_new == dragger)
650
if (d_new->isA (draggable->item,
651
draggable->point_type == POINT_LG_BEGIN? POINT_LG_END : POINT_LG_BEGIN,
652
draggable->fill_or_stroke)) {
653
// found the other end of the linear gradient;
654
if (state & GDK_SHIFT_MASK) {
655
// moving linear around center
656
Geom::Point center = Geom::Point (0.5*(d_new->point + dragger->point));
659
// moving linear around the other end
660
dr_snap = d_new->point;
664
} else if (draggable->point_type == POINT_RG_R1 || draggable->point_type == POINT_RG_R2 || draggable->point_type == POINT_RG_FOCUS) {
665
for (GList *di = dragger->parent->draggers; di != NULL; di = di->next) {
666
GrDragger *d_new = (GrDragger *) di->data;
667
if (d_new == dragger)
669
if (d_new->isA (draggable->item,
671
draggable->fill_or_stroke)) {
672
// found the center of the radial gradient;
673
dr_snap = d_new->point;
676
} else if (draggable->point_type == POINT_RG_CENTER) {
677
// radial center snaps to hor/vert relative to its original position
678
dr_snap = dragger->point_original;
681
boost::optional<Geom::Point> snap_vector;
682
if (dr_snap.isFinite()) {
683
if (state & GDK_MOD1_MASK) {
684
// with Alt, snap to the original angle and its perpendiculars
685
snap_vector = get_snap_vector (p, dr_snap, M_PI/2, Geom::atan2 (dragger->point_original - dr_snap));
687
// with Ctrl, snap to M_PI/snaps
688
snap_vector = get_snap_vector (p, dr_snap, M_PI/snaps, 0);
692
snap_vectors = g_slist_prepend (snap_vectors, &(*snap_vector));
696
// Move by the smallest of snap vectors:
697
Geom::Point move(9999, 9999);
698
for (GSList const *i = snap_vectors; i != NULL; i = i->next) {
699
Geom::Point *snap_vector = (Geom::Point *) i->data;
700
if (Geom::L2(*snap_vector) < Geom::L2(move))
703
if (move[Geom::X] < 9999) {
705
sp_knot_moveto (knot, p);
708
g_slist_free(snap_vectors);
711
drag->keep_selection = (bool) g_list_find(drag->selected, dragger);
712
bool scale_radial = (state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK);
714
if (drag->keep_selection) {
715
Geom::Point diff = p - dragger->point;
716
drag->selected_move_nowrite (diff[Geom::X], diff[Geom::Y], scale_radial);
719
dragger->fireDraggables (false, scale_radial);
720
dragger->updateDependencies(false);
727
gr_midpoint_limits(GrDragger *dragger, SPObject *server, Geom::Point *begin, Geom::Point *end, Geom::Point *low_lim, Geom::Point *high_lim, GSList **moving)
730
GrDrag *drag = dragger->parent;
731
// a midpoint dragger can (logically) only contain one GrDraggable
732
GrDraggable *draggable = (GrDraggable *) dragger->draggables->data;
734
// get begin and end points between which dragging is allowed:
735
// the draglimits are between knot(lowest_i - 1) and knot(highest_i + 1)
736
*moving = g_slist_append(*moving, dragger);
738
guint lowest_i = draggable->point_i;
739
guint highest_i = draggable->point_i;
740
GrDragger *lowest_dragger = dragger;
741
GrDragger *highest_dragger = dragger;
742
if (dragger->isSelected()) {
746
d_add = drag->getDraggerFor(draggable->item, draggable->point_type, lowest_i - 1, draggable->fill_or_stroke);
747
if ( d_add && g_list_find(drag->selected, d_add) ) {
748
lowest_i = lowest_i - 1;
749
*moving = g_slist_prepend(*moving, d_add);
750
lowest_dragger = d_add;
758
d_add = drag->getDraggerFor(draggable->item, draggable->point_type, highest_i + 1, draggable->fill_or_stroke);
759
if ( d_add && g_list_find(drag->selected, d_add) ) {
760
highest_i = highest_i + 1;
761
*moving = g_slist_append(*moving, d_add);
762
highest_dragger = d_add;
769
if ( SP_IS_LINEARGRADIENT(server) ) {
770
guint num = SP_LINEARGRADIENT(server)->vector.stops.size();
773
d_temp = drag->getDraggerFor (draggable->item, POINT_LG_BEGIN, 0, draggable->fill_or_stroke);
775
d_temp = drag->getDraggerFor (draggable->item, POINT_LG_MID, lowest_i - 1, draggable->fill_or_stroke);
778
*begin = d_temp->point;
780
d_temp = drag->getDraggerFor (draggable->item, POINT_LG_MID, highest_i + 1, draggable->fill_or_stroke);
781
if (d_temp == NULL) {
782
d_temp = drag->getDraggerFor (draggable->item, POINT_LG_END, num-1, draggable->fill_or_stroke);
785
*end = d_temp->point;
786
} else if ( SP_IS_RADIALGRADIENT(server) ) {
787
guint num = SP_RADIALGRADIENT(server)->vector.stops.size();
790
d_temp = drag->getDraggerFor (draggable->item, POINT_RG_CENTER, 0, draggable->fill_or_stroke);
792
d_temp = drag->getDraggerFor (draggable->item, draggable->point_type, lowest_i - 1, draggable->fill_or_stroke);
795
*begin = d_temp->point;
797
d_temp = drag->getDraggerFor (draggable->item, draggable->point_type, highest_i + 1, draggable->fill_or_stroke);
798
if (d_temp == NULL) {
799
d_temp = drag->getDraggerFor (draggable->item, (draggable->point_type==POINT_RG_MID1) ? POINT_RG_R1 : POINT_RG_R2, num-1, draggable->fill_or_stroke);
802
*end = d_temp->point;
805
*low_lim = dragger->point - (lowest_dragger->point - *begin);
806
*high_lim = dragger->point - (highest_dragger->point - *end);
812
Called when a midpoint knot is dragged.
815
gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &ppointer, guint state, gpointer data)
817
GrDragger *dragger = (GrDragger *) data;
818
GrDrag *drag = dragger->parent;
819
// a midpoint dragger can (logically) only contain one GrDraggable
820
GrDraggable *draggable = (GrDraggable *) dragger->draggables->data;
822
// FIXME: take from prefs
823
double snap_fraction = 0.1;
825
Geom::Point p = ppointer;
826
Geom::Point begin(0,0), end(0,0);
827
Geom::Point low_lim(0,0), high_lim(0,0);
829
SPObject *server = draggable->getServer();
831
GSList *moving = NULL;
832
gr_midpoint_limits(dragger, server, &begin, &end, &low_lim, &high_lim, &moving);
834
if (state & GDK_CONTROL_MASK) {
835
p = snap_vector_midpoint (p, low_lim, high_lim, snap_fraction);
837
p = snap_vector_midpoint (p, low_lim, high_lim, 0);
839
Geom::Point displacement = p - dragger->point;
841
for (GSList const* i = moving; i != NULL; i = i->next) {
842
GrDragger *drg = (GrDragger*) i->data;
843
SPKnot *drgknot = drg->knot;
844
Geom::Point this_move = displacement;
845
if (state & GDK_MOD1_MASK) {
846
// FIXME: unify all these profiles (here, in nodepath, in tweak) in one place
848
if (Geom::L2(drg->point - dragger->point) + Geom::L2(drg->point - begin) - 1e-3 > Geom::L2(dragger->point - begin)) { // drg is on the end side from dragger
849
double x = Geom::L2(drg->point - dragger->point)/Geom::L2(end - dragger->point);
850
this_move = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5) * this_move;
851
} else { // drg is on the begin side from dragger
852
double x = Geom::L2(drg->point - dragger->point)/Geom::L2(begin - dragger->point);
853
this_move = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5) * this_move;
856
drg->point += this_move;
857
sp_knot_moveto (drgknot, drg->point);
858
drg->fireDraggables (false);
859
drg->updateDependencies(false);
862
g_slist_free(moving);
864
drag->keep_selection = dragger->isSelected();
870
gr_knot_grabbed_handler (SPKnot */*knot*/, unsigned int /*state*/, gpointer data)
872
GrDragger *dragger = (GrDragger *) data;
874
sp_canvas_force_full_redraw_after_interruptions(dragger->parent->desktop->canvas, 5);
878
Called when the mouse releases a dragger knot; changes gradient writing to repr, updates other draggers if needed
881
gr_knot_ungrabbed_handler (SPKnot *knot, unsigned int state, gpointer data)
883
GrDragger *dragger = (GrDragger *) data;
885
sp_canvas_end_forced_full_redraws(dragger->parent->desktop->canvas);
887
dragger->point_original = dragger->point = knot->pos;
889
if ((state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK)) {
890
dragger->fireDraggables (true, true);
892
dragger->fireDraggables (true);
895
for (GList *i = dragger->parent->selected; i != NULL; i = i->next) {
896
GrDragger *d = (GrDragger *) i->data;
899
d->fireDraggables (true);
902
// make this dragger selected
903
if (!dragger->parent->keep_selection) {
904
dragger->parent->setSelected (dragger);
906
dragger->parent->keep_selection = false;
908
dragger->updateDependencies(true);
910
// we did an undoable action
911
sp_document_done (sp_desktop_document (dragger->parent->desktop), SP_VERB_CONTEXT_GRADIENT,
912
_("Move gradient handle"));
916
Called when a dragger knot is clicked; selects the dragger or deletes it depending on the
917
state of the keyboard keys
920
gr_knot_clicked_handler(SPKnot */*knot*/, guint state, gpointer data)
922
GrDragger *dragger = (GrDragger *) data;
923
GrDraggable *draggable = (GrDraggable *) dragger->draggables->data;
924
if (!draggable) return;
926
if ( (state & GDK_CONTROL_MASK) && (state & GDK_MOD1_MASK ) ) {
927
// delete this knot from vector
928
SPGradient *gradient = sp_item_gradient (draggable->item, draggable->fill_or_stroke);
929
gradient = sp_gradient_get_vector (gradient, false);
930
if (gradient->vector.stops.size() > 2) { // 2 is the minimum
932
switch (draggable->point_type) { // if we delete first or last stop, move the next/previous to the edge
934
case POINT_RG_CENTER:
935
stop = sp_first_stop(gradient);
937
SPStop *next = sp_next_stop (stop);
940
sp_repr_set_css_double (SP_OBJECT_REPR (next), "offset", 0);
947
stop = sp_last_stop(gradient);
949
SPStop *prev = sp_prev_stop (stop, gradient);
952
sp_repr_set_css_double (SP_OBJECT_REPR (prev), "offset", 1);
959
stop = sp_get_stop_i(gradient, draggable->point_i);
963
SP_OBJECT_REPR(gradient)->removeChild(SP_OBJECT_REPR(stop));
964
sp_document_done (SP_OBJECT_DOCUMENT (gradient), SP_VERB_CONTEXT_GRADIENT,
965
_("Delete gradient stop"));
968
// select the dragger
969
dragger->point_original = dragger->point;
971
if ( state & GDK_SHIFT_MASK ) {
972
dragger->parent->setSelected (dragger, true, false);
974
dragger->parent->setSelected (dragger);
980
Called when a dragger knot is doubleclicked; opens gradient editor with the stop from the first draggable
983
gr_knot_doubleclicked_handler (SPKnot */*knot*/, guint /*state*/, gpointer data)
985
GrDragger *dragger = (GrDragger *) data;
987
dragger->point_original = dragger->point;
989
if (dragger->draggables == NULL)
992
GrDraggable *draggable = (GrDraggable *) dragger->draggables->data;
993
sp_item_gradient_edit_stop (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
997
Act upon all draggables of the dragger, setting them to the dragger's point
1000
GrDragger::fireDraggables (bool write_repr, bool scale_radial, bool merging_focus)
1002
for (GSList const* i = this->draggables; i != NULL; i = i->next) {
1003
GrDraggable *draggable = (GrDraggable *) i->data;
1005
// set local_change flag so that selection_changed callback does not regenerate draggers
1006
this->parent->local_change = true;
1008
// change gradient, optionally writing to repr; prevent focus from moving if it's snapped
1009
// to the center, unless it's the first update upon merge when we must snap it to the point
1010
if (merging_focus ||
1011
!(draggable->point_type == POINT_RG_FOCUS && this->isA(draggable->item, POINT_RG_CENTER, draggable->point_i, draggable->fill_or_stroke)))
1013
sp_item_gradient_set_coords (draggable->item, draggable->point_type, draggable->point_i, this->point, draggable->fill_or_stroke, write_repr, scale_radial);
1019
Checks if the dragger has a draggable with this point_type
1022
GrDragger::isA (gint point_type)
1024
for (GSList const* i = this->draggables; i != NULL; i = i->next) {
1025
GrDraggable *draggable = (GrDraggable *) i->data;
1026
if (draggable->point_type == point_type) {
1034
Checks if the dragger has a draggable with this item, point_type + point_i (number), fill_or_stroke
1037
GrDragger::isA (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke)
1039
for (GSList const* i = this->draggables; i != NULL; i = i->next) {
1040
GrDraggable *draggable = (GrDraggable *) i->data;
1041
if ( (draggable->point_type == point_type) && (draggable->point_i == point_i) && (draggable->item == item) && (draggable->fill_or_stroke == fill_or_stroke) ) {
1049
Checks if the dragger has a draggable with this item, point_type, fill_or_stroke
1052
GrDragger::isA (SPItem *item, gint point_type, bool fill_or_stroke)
1054
for (GSList const* i = this->draggables; i != NULL; i = i->next) {
1055
GrDraggable *draggable = (GrDraggable *) i->data;
1056
if ( (draggable->point_type == point_type) && (draggable->item == item) && (draggable->fill_or_stroke == fill_or_stroke) ) {
1064
GrDraggable::mayMerge (GrDraggable *da2)
1066
if ((this->item == da2->item) && (this->fill_or_stroke == da2->fill_or_stroke)) {
1067
// we must not merge the points of the same gradient!
1068
if (!((this->point_type == POINT_RG_FOCUS && da2->point_type == POINT_RG_CENTER) ||
1069
(this->point_type == POINT_RG_CENTER && da2->point_type == POINT_RG_FOCUS))) {
1070
// except that we can snap center and focus together
1074
// disable merging of midpoints.
1075
if ( (this->point_type == POINT_LG_MID) || (da2->point_type == POINT_LG_MID)
1076
|| (this->point_type == POINT_RG_MID1) || (da2->point_type == POINT_RG_MID1)
1077
|| (this->point_type == POINT_RG_MID2) || (da2->point_type == POINT_RG_MID2) )
1084
GrDragger::mayMerge (GrDragger *other)
1089
for (GSList const* i = this->draggables; i != NULL; i = i->next) { // for all draggables of this
1090
GrDraggable *da1 = (GrDraggable *) i->data;
1091
for (GSList const* j = other->draggables; j != NULL; j = j->next) { // for all draggables of other
1092
GrDraggable *da2 = (GrDraggable *) j->data;
1093
if (!da1->mayMerge(da2))
1101
GrDragger::mayMerge (GrDraggable *da2)
1103
for (GSList const* i = this->draggables; i != NULL; i = i->next) { // for all draggables of this
1104
GrDraggable *da1 = (GrDraggable *) i->data;
1105
if (!da1->mayMerge(da2))
1112
Updates the statusbar tip of the dragger knot, based on its draggables
1115
GrDragger::updateTip ()
1117
if (this->knot && this->knot->tip) {
1118
g_free (this->knot->tip);
1119
this->knot->tip = NULL;
1122
if (g_slist_length (this->draggables) == 1) {
1123
GrDraggable *draggable = (GrDraggable *) this->draggables->data;
1124
char *item_desc = sp_item_description(draggable->item);
1125
switch (draggable->point_type) {
1129
this->knot->tip = g_strdup_printf (_("%s %d for: %s%s; drag with <b>Ctrl</b> to snap offset; click with <b>Ctrl+Alt</b> to delete stop"),
1130
_(gr_knot_descr[draggable->point_type]),
1133
draggable->fill_or_stroke == false ? _(" (stroke)") : "");
1137
this->knot->tip = g_strdup_printf (_("%s for: %s%s; drag with <b>Ctrl</b> to snap angle, with <b>Ctrl+Alt</b> to preserve angle, with <b>Ctrl+Shift</b> to scale around center"),
1138
_(gr_knot_descr[draggable->point_type]),
1140
draggable->fill_or_stroke == false ? _(" (stroke)") : "");
1144
} else if (g_slist_length (draggables) == 2 && isA (POINT_RG_CENTER) && isA (POINT_RG_FOCUS)) {
1145
this->knot->tip = g_strdup_printf (_("Radial gradient <b>center</b> and <b>focus</b>; drag with <b>Shift</b> to separate focus"));
1147
int length = g_slist_length (this->draggables);
1148
this->knot->tip = g_strdup_printf (ngettext("Gradient point shared by <b>%d</b> gradient; drag with <b>Shift</b> to separate",
1149
"Gradient point shared by <b>%d</b> gradients; drag with <b>Shift</b> to separate",
1156
Adds a draggable to the dragger
1159
GrDragger::updateKnotShape ()
1163
GrDraggable *last = (GrDraggable *) g_slist_last(draggables)->data;
1164
g_object_set (G_OBJECT (this->knot->item), "shape", gr_knot_shapes[last->point_type], NULL);
1168
Adds a draggable to the dragger
1171
GrDragger::addDraggable (GrDraggable *draggable)
1173
this->draggables = g_slist_prepend (this->draggables, draggable);
1180
Moves this dragger to the point of the given draggable, acting upon all other draggables
1183
GrDragger::moveThisToDraggable (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, bool write_repr)
1185
this->point = sp_item_gradient_get_coords (item, point_type, point_i, fill_or_stroke);
1186
this->point_original = this->point;
1188
sp_knot_moveto (this->knot, this->point);
1190
for (GSList const* i = this->draggables; i != NULL; i = i->next) {
1191
GrDraggable *da = (GrDraggable *) i->data;
1192
if ( (da->item == item) &&
1193
(point_type == -1 || da->point_type == point_type) &&
1194
(point_i == -1 || da->point_i == point_i) &&
1195
(da->fill_or_stroke == fill_or_stroke) ) {
1198
sp_item_gradient_set_coords (da->item, da->point_type, da->point_i, this->point, da->fill_or_stroke, write_repr, false);
1200
// FIXME: here we should also call this->updateDependencies(write_repr); to propagate updating, but how to prevent loops?
1205
Moves all midstop draggables that depend on this one
1208
GrDragger::updateMidstopDependencies (GrDraggable *draggable, bool write_repr) {
1209
SPObject *server = draggable->getServer();
1212
guint num = SP_GRADIENT(server)->vector.stops.size();
1213
if (num <= 2) return;
1215
if ( SP_IS_LINEARGRADIENT(server) ) {
1216
for ( guint i = 1; i < num - 1; i++ ) {
1217
this->moveOtherToDraggable (draggable->item, POINT_LG_MID, i, draggable->fill_or_stroke, write_repr);
1219
} else if ( SP_IS_RADIALGRADIENT(server) ) {
1220
for ( guint i = 1; i < num - 1; i++ ) {
1221
this->moveOtherToDraggable (draggable->item, POINT_RG_MID1, i, draggable->fill_or_stroke, write_repr);
1222
this->moveOtherToDraggable (draggable->item, POINT_RG_MID2, i, draggable->fill_or_stroke, write_repr);
1229
Moves all draggables that depend on this one
1232
GrDragger::updateDependencies (bool write_repr)
1234
for (GSList const* i = this->draggables; i != NULL; i = i->next) {
1235
GrDraggable *draggable = (GrDraggable *) i->data;
1236
switch (draggable->point_type) {
1237
case POINT_LG_BEGIN:
1239
// the end point is dependent only when dragging with ctrl+shift
1240
this->moveOtherToDraggable (draggable->item, POINT_LG_END, -1, draggable->fill_or_stroke, write_repr);
1242
this->updateMidstopDependencies (draggable, write_repr);
1247
// the begin point is dependent only when dragging with ctrl+shift
1248
this->moveOtherToDraggable (draggable->item, POINT_LG_BEGIN, 0, draggable->fill_or_stroke, write_repr);
1250
this->updateMidstopDependencies (draggable, write_repr);
1254
// no other nodes depend on mid points.
1257
this->moveOtherToDraggable (draggable->item, POINT_RG_R1, -1, draggable->fill_or_stroke, write_repr);
1258
this->moveOtherToDraggable (draggable->item, POINT_RG_FOCUS, -1, draggable->fill_or_stroke, write_repr);
1259
this->updateMidstopDependencies (draggable, write_repr);
1262
this->moveOtherToDraggable (draggable->item, POINT_RG_R2, -1, draggable->fill_or_stroke, write_repr);
1263
this->moveOtherToDraggable (draggable->item, POINT_RG_FOCUS, -1, draggable->fill_or_stroke, write_repr);
1264
this->updateMidstopDependencies (draggable, write_repr);
1266
case POINT_RG_CENTER:
1267
this->moveOtherToDraggable (draggable->item, POINT_RG_R1, -1, draggable->fill_or_stroke, write_repr);
1268
this->moveOtherToDraggable (draggable->item, POINT_RG_R2, -1, draggable->fill_or_stroke, write_repr);
1269
this->moveOtherToDraggable (draggable->item, POINT_RG_FOCUS, -1, draggable->fill_or_stroke, write_repr);
1270
this->updateMidstopDependencies (draggable, write_repr);
1272
case POINT_RG_FOCUS:
1273
// nothing can depend on that
1276
this->moveOtherToDraggable (draggable->item, POINT_RG_MID2, draggable->point_i, draggable->fill_or_stroke, write_repr);
1279
this->moveOtherToDraggable (draggable->item, POINT_RG_MID1, draggable->point_i, draggable->fill_or_stroke, write_repr);
1289
GrDragger::GrDragger (GrDrag *parent, Geom::Point p, GrDraggable *draggable)
1293
this->draggables = NULL;
1295
this->parent = parent;
1298
this->knot = sp_knot_new (parent->desktop, NULL);
1299
this->knot->setMode(SP_KNOT_MODE_XOR);
1300
this->knot->setFill(GR_KNOT_COLOR_NORMAL, GR_KNOT_COLOR_MOUSEOVER, GR_KNOT_COLOR_MOUSEOVER);
1301
this->knot->setStroke(0x0000007f, 0x0000007f, 0x0000007f);
1302
sp_knot_update_ctrl(this->knot);
1304
// move knot to the given point
1305
sp_knot_set_position (this->knot, p, SP_KNOT_STATE_NORMAL);
1306
sp_knot_show (this->knot);
1308
// connect knot's signals
1309
if ( (draggable) // it can be NULL if a node in unsnapped (eg. focus point unsnapped from center)
1310
// luckily, midstops never snap to other nodes so are never unsnapped...
1311
&& ( (draggable->point_type == POINT_LG_MID)
1312
|| (draggable->point_type == POINT_RG_MID1)
1313
|| (draggable->point_type == POINT_RG_MID2) ) )
1315
this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_midpoint_handler), this);
1317
this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_handler), this);
1319
g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (gr_knot_clicked_handler), this);
1320
g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (gr_knot_doubleclicked_handler), this);
1321
g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (gr_knot_grabbed_handler), this);
1322
g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (gr_knot_ungrabbed_handler), this);
1324
// add the initial draggable
1326
this->addDraggable (draggable);
1330
GrDragger::~GrDragger ()
1332
// unselect if it was selected
1333
this->parent->setDeselected(this);
1335
// disconnect signals
1336
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_moved_handler), this);
1337
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_clicked_handler), this);
1338
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_doubleclicked_handler), this);
1339
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_grabbed_handler), this);
1340
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_ungrabbed_handler), this);
1342
/* unref should call destroy */
1343
g_object_unref (G_OBJECT (this->knot));
1345
// delete all draggables
1346
for (GSList const* i = this->draggables; i != NULL; i = i->next) {
1347
delete ((GrDraggable *) i->data);
1349
g_slist_free (this->draggables);
1350
this->draggables = NULL;
1354
Select the dragger which has the given draggable.
1357
GrDrag::getDraggerFor (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke)
1359
for (GList const* i = this->draggers; i != NULL; i = i->next) {
1360
GrDragger *dragger = (GrDragger *) i->data;
1361
for (GSList const* j = dragger->draggables; j != NULL; j = j->next) {
1362
GrDraggable *da2 = (GrDraggable *) j->data;
1363
if ( (da2->item == item) &&
1364
(point_type == -1 || da2->point_type == point_type) && // -1 means this does not matter
1365
(point_i == -1 || da2->point_i == point_i) && // -1 means this does not matter
1366
(da2->fill_or_stroke == fill_or_stroke)) {
1376
GrDragger::moveOtherToDraggable (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, bool write_repr)
1378
GrDragger *d = this->parent->getDraggerFor (item, point_type, point_i, fill_or_stroke);
1379
if (d && d != this) {
1380
d->moveThisToDraggable (item, point_type, point_i, fill_or_stroke, write_repr);
1386
Draw this dragger as selected
1391
this->knot->fill [SP_KNOT_STATE_NORMAL] = GR_KNOT_COLOR_SELECTED;
1392
g_object_set (G_OBJECT (this->knot->item), "fill_color", GR_KNOT_COLOR_SELECTED, NULL);
1396
Draw this dragger as normal (deselected)
1399
GrDragger::deselect()
1401
this->knot->fill [SP_KNOT_STATE_NORMAL] = GR_KNOT_COLOR_NORMAL;
1402
g_object_set (G_OBJECT (this->knot->item), "fill_color", GR_KNOT_COLOR_NORMAL, NULL);
1406
GrDragger::isSelected()
1408
return g_list_find (parent->selected, this);
1412
\brief Deselect all stops/draggers (private)
1415
GrDrag::deselect_all()
1418
( (GrDragger*) selected->data)->deselect();
1419
selected = g_list_remove(selected, selected->data);
1424
\brief Deselect all stops/draggers (public; emits signal)
1427
GrDrag::deselectAll()
1430
this->desktop->emitToolSubselectionChanged(NULL);
1434
\brief Select all stops/draggers
1439
for (GList *l = this->draggers; l != NULL; l = l->next) {
1440
GrDragger *d = ((GrDragger *) l->data);
1441
setSelected (d, true, true);
1446
\brief Select all stops/draggers that match the coords
1449
GrDrag::selectByCoords(std::vector<Geom::Point> coords)
1451
for (GList *l = this->draggers; l != NULL; l = l->next) {
1452
GrDragger *d = ((GrDragger *) l->data);
1453
for (guint k = 0; k < coords.size(); k++) {
1454
if (Geom::L2 (d->point - coords[k]) < 1e-4) {
1455
setSelected (d, true, true);
1463
\brief Select all stops/draggers that fall within the rect
1466
GrDrag::selectRect(Geom::Rect const &r)
1468
for (GList *l = this->draggers; l != NULL; l = l->next) {
1469
GrDragger *d = ((GrDragger *) l->data);
1470
if (r.contains(d->point)) {
1471
setSelected (d, true, true);
1477
\brief Select a dragger
1478
\param dragger The dragger to select
1479
\param add_to_selection If true, add to selection, otherwise deselect others
1480
\param override If true, always select this node, otherwise toggle selected status
1483
GrDrag::setSelected (GrDragger *dragger, bool add_to_selection, bool override)
1485
GrDragger *seldragger = NULL;
1487
if (add_to_selection) {
1488
if (!dragger) return;
1490
if (!g_list_find(selected, dragger)) {
1491
selected = g_list_prepend(selected, dragger);
1494
seldragger = dragger;
1496
if (g_list_find(selected, dragger)) {
1497
selected = g_list_remove(selected, dragger);
1498
dragger->deselect();
1500
seldragger = (GrDragger*) selected->data; // select the dragger that is first in the list
1503
selected = g_list_prepend(selected, dragger);
1505
seldragger = dragger;
1511
selected = g_list_prepend(selected, dragger);
1513
seldragger = dragger;
1517
this->desktop->emitToolSubselectionChanged((gpointer) seldragger);
1522
\brief Deselect a dragger
1523
\param dragger The dragger to deselect
1526
GrDrag::setDeselected (GrDragger *dragger)
1528
if (g_list_find(selected, dragger)) {
1529
selected = g_list_remove(selected, dragger);
1530
dragger->deselect();
1532
this->desktop->emitToolSubselectionChanged((gpointer) (selected ? selected->data : NULL ));
1538
Create a line from p1 to p2 and add it to the lines list
1541
GrDrag::addLine (SPItem *item, Geom::Point p1, Geom::Point p2, guint32 rgba)
1543
SPCanvasItem *line = sp_canvas_item_new(sp_desktop_controls(this->desktop),
1544
SP_TYPE_CTRLLINE, NULL);
1545
sp_canvas_item_move_to_z(line, 0);
1546
SP_CTRLLINE(line)->item = item;
1547
sp_ctrlline_set_coords(SP_CTRLLINE(line), p1, p2);
1548
if (rgba != GR_LINE_COLOR_FILL) // fill is the default, so don't set color for it to speed up redraw
1549
sp_ctrlline_set_rgba32 (SP_CTRLLINE(line), rgba);
1550
sp_canvas_item_show (line);
1551
this->lines = g_slist_append (this->lines, line);
1555
If there already exists a dragger within MERGE_DIST of p, add the draggable to it; otherwise create
1556
new dragger and add it to draggers list
1559
GrDrag::addDragger (GrDraggable *draggable)
1561
Geom::Point p = sp_item_gradient_get_coords (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
1563
for (GList *i = this->draggers; i != NULL; i = i->next) {
1564
GrDragger *dragger = (GrDragger *) i->data;
1565
if (dragger->mayMerge (draggable) && Geom::L2 (dragger->point - p) < MERGE_DIST) {
1566
// distance is small, merge this draggable into dragger, no need to create new dragger
1567
dragger->addDraggable (draggable);
1568
dragger->updateKnotShape();
1573
GrDragger *new_dragger = new GrDragger(this, p, draggable);
1574
// fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
1575
this->draggers = g_list_append (this->draggers, new_dragger);
1579
Add draggers for the radial gradient rg on item
1582
GrDrag::addDraggersRadial (SPRadialGradient *rg, SPItem *item, bool fill_or_stroke)
1584
addDragger (new GrDraggable (item, POINT_RG_CENTER, 0, fill_or_stroke));
1585
guint num = rg->vector.stops.size();
1587
for ( guint i = 1; i < num - 1; i++ ) {
1588
addDragger (new GrDraggable (item, POINT_RG_MID1, i, fill_or_stroke));
1591
addDragger (new GrDraggable (item, POINT_RG_R1, num-1, fill_or_stroke));
1593
for ( guint i = 1; i < num - 1; i++ ) {
1594
addDragger (new GrDraggable (item, POINT_RG_MID2, i, fill_or_stroke));
1597
addDragger (new GrDraggable (item, POINT_RG_R2, num-1, fill_or_stroke));
1598
addDragger (new GrDraggable (item, POINT_RG_FOCUS, 0, fill_or_stroke));
1602
Add draggers for the linear gradient lg on item
1605
GrDrag::addDraggersLinear (SPLinearGradient *lg, SPItem *item, bool fill_or_stroke)
1607
addDragger (new GrDraggable (item, POINT_LG_BEGIN, 0, fill_or_stroke));
1608
guint num = lg->vector.stops.size();
1610
for ( guint i = 1; i < num - 1; i++ ) {
1611
addDragger (new GrDraggable (item, POINT_LG_MID, i, fill_or_stroke));
1614
addDragger (new GrDraggable (item, POINT_LG_END, num-1, fill_or_stroke));
1618
Artificially grab the knot of this dragger; used by the gradient context
1621
GrDrag::grabKnot (GrDragger *dragger, gint x, gint y, guint32 etime)
1624
sp_knot_start_dragging (dragger->knot, dragger->point, x, y, etime);
1629
Artificially grab the knot of the dragger with this draggable; used by the gradient context
1632
GrDrag::grabKnot (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, gint x, gint y, guint32 etime)
1634
GrDragger *dragger = getDraggerFor (item, point_type, point_i, fill_or_stroke);
1636
sp_knot_start_dragging (dragger->knot, dragger->point, x, y, etime);
1641
Regenerates the draggers list from the current selection; is called when selection is changed or
1642
modified, also when a radial dragger needs to update positions of other draggers in the gradient
1645
GrDrag::updateDraggers ()
1648
selected = g_list_remove(selected, selected->data);
1650
// delete old draggers
1651
for (GList const* i = this->draggers; i != NULL; i = i->next) {
1652
delete ((GrDragger *) i->data);
1654
g_list_free (this->draggers);
1655
this->draggers = NULL;
1657
g_return_if_fail (this->selection != NULL);
1659
for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
1661
SPItem *item = SP_ITEM(i->data);
1662
SPStyle *style = SP_OBJECT_STYLE (item);
1664
if (style && (style->fill.isPaintserver())) {
1665
SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item);
1666
if (SP_IS_LINEARGRADIENT (server)) {
1667
addDraggersLinear (SP_LINEARGRADIENT (server), item, true);
1668
} else if (SP_IS_RADIALGRADIENT (server)) {
1669
addDraggersRadial (SP_RADIALGRADIENT (server), item, true);
1673
if (style && (style->stroke.isPaintserver())) {
1674
SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item);
1675
if (SP_IS_LINEARGRADIENT (server)) {
1676
addDraggersLinear (SP_LINEARGRADIENT (server), item, false);
1677
} else if (SP_IS_RADIALGRADIENT (server)) {
1678
addDraggersRadial (SP_RADIALGRADIENT (server), item, false);
1685
Regenerates the lines list from the current selection; is called on each move of a dragger, so that
1686
lines are always in sync with the actual gradient
1689
GrDrag::updateLines ()
1692
for (GSList const *i = this->lines; i != NULL; i = i->next) {
1693
gtk_object_destroy( GTK_OBJECT (i->data));
1695
g_slist_free (this->lines);
1698
g_return_if_fail (this->selection != NULL);
1700
for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
1702
SPItem *item = SP_ITEM(i->data);
1704
SPStyle *style = SP_OBJECT_STYLE (item);
1706
if (style && (style->fill.isPaintserver())) {
1707
SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item);
1708
if (SP_IS_LINEARGRADIENT (server)) {
1709
this->addLine (item, sp_item_gradient_get_coords (item, POINT_LG_BEGIN, 0, true), sp_item_gradient_get_coords (item, POINT_LG_END, 0, true), GR_LINE_COLOR_FILL);
1710
} else if (SP_IS_RADIALGRADIENT (server)) {
1711
Geom::Point center = sp_item_gradient_get_coords (item, POINT_RG_CENTER, 0, true);
1712
this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R1, 0, true), GR_LINE_COLOR_FILL);
1713
this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R2, 0, true), GR_LINE_COLOR_FILL);
1717
if (style && (style->stroke.isPaintserver())) {
1718
SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item);
1719
if (SP_IS_LINEARGRADIENT (server)) {
1720
this->addLine (item, sp_item_gradient_get_coords (item, POINT_LG_BEGIN, 0, false), sp_item_gradient_get_coords (item, POINT_LG_END, 0, false), GR_LINE_COLOR_STROKE);
1721
} else if (SP_IS_RADIALGRADIENT (server)) {
1722
Geom::Point center = sp_item_gradient_get_coords (item, POINT_RG_CENTER, 0, false);
1723
this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R1, 0, false), GR_LINE_COLOR_STROKE);
1724
this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R2, 0, false), GR_LINE_COLOR_STROKE);
1731
Regenerates the levels list from the current selection
1734
GrDrag::updateLevels ()
1737
vert_levels.clear();
1739
g_return_if_fail (this->selection != NULL);
1741
for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
1742
SPItem *item = SP_ITEM(i->data);
1743
Geom::OptRect rect = sp_item_bbox_desktop (item);
1745
// Remember the edges of the bbox and the center axis
1746
hor_levels.push_back(rect->min()[Geom::Y]);
1747
hor_levels.push_back(rect->max()[Geom::Y]);
1748
hor_levels.push_back(0.5 * (rect->min()[Geom::Y] + rect->max()[Geom::Y]));
1749
vert_levels.push_back(rect->min()[Geom::X]);
1750
vert_levels.push_back(rect->max()[Geom::X]);
1751
vert_levels.push_back(0.5 * (rect->min()[Geom::X] + rect->max()[Geom::X]));
1757
GrDrag::selected_reverse_vector ()
1759
if (selected == NULL)
1762
for (GSList const* i = ( (GrDragger*) selected->data )->draggables; i != NULL; i = i->next) {
1763
GrDraggable *draggable = (GrDraggable *) i->data;
1765
sp_item_gradient_reverse_vector (draggable->item, draggable->fill_or_stroke);
1770
GrDrag::selected_move_nowrite (double x, double y, bool scale_radial)
1772
selected_move (x, y, false, scale_radial);
1776
GrDrag::selected_move (double x, double y, bool write_repr, bool scale_radial)
1778
if (selected == NULL)
1783
for (GList *i = selected; i != NULL; i = i->next) {
1784
GrDragger *d = (GrDragger *) i->data;
1786
if (!d->isA(POINT_LG_MID) && !d->isA(POINT_RG_MID1) && !d->isA(POINT_RG_MID2)) {
1787
// if this is an endpoint,
1789
// Moving an rg center moves its focus and radii as well.
1790
// therefore, if this is a focus or radius and if selection
1791
// contains the center as well, do not move this one
1792
if (d->isA(POINT_RG_R1) || d->isA(POINT_RG_R2) ||
1793
(d->isA(POINT_RG_FOCUS) && !d->isA(POINT_RG_CENTER))) {
1794
bool skip_radius_with_center = false;
1795
for (GList *di = selected; di != NULL; di = di->next) {
1796
GrDragger *d_new = (GrDragger *) di->data;
1797
if (d_new->isA (((GrDraggable *) d->draggables->data)->item,
1800
((GrDraggable *) d->draggables->data)->fill_or_stroke)) {
1801
// FIXME: here we take into account only the first draggable!
1802
skip_radius_with_center = true;
1805
if (skip_radius_with_center)
1810
d->point += Geom::Point (x, y);
1811
d->point_original = d->point;
1812
sp_knot_moveto (d->knot, d->point);
1814
d->fireDraggables (write_repr, scale_radial);
1816
d->updateDependencies(write_repr);
1820
if (write_repr && did) {
1821
// we did an undoable action
1822
sp_document_maybe_done (sp_desktop_document (desktop), "grmoveh", SP_VERB_CONTEXT_GRADIENT,
1823
_("Move gradient handle(s)"));
1827
if (!did) { // none of the end draggers are selected, so let's try to move the mids
1829
GrDragger *dragger = (GrDragger *) selected->data;
1830
// a midpoint dragger can (logically) only contain one GrDraggable
1831
GrDraggable *draggable = (GrDraggable *) dragger->draggables->data;
1833
Geom::Point begin(0,0), end(0,0);
1834
Geom::Point low_lim(0,0), high_lim(0,0);
1836
SPObject *server = draggable->getServer();
1837
GSList *moving = NULL;
1838
gr_midpoint_limits(dragger, server, &begin, &end, &low_lim, &high_lim, &moving);
1840
Geom::Point p(x, y);
1841
p = snap_vector_midpoint (dragger->point + p, low_lim, high_lim, 0);
1842
Geom::Point displacement = p - dragger->point;
1844
for (GSList const* i = moving; i != NULL; i = i->next) {
1845
GrDragger *drg = (GrDragger*) i->data;
1846
SPKnot *drgknot = drg->knot;
1847
drg->point += displacement;
1848
sp_knot_moveto (drgknot, drg->point);
1849
drg->fireDraggables (true);
1850
drg->updateDependencies(true);
1854
g_slist_free(moving);
1856
if (write_repr && did) {
1857
// we did an undoable action
1858
sp_document_maybe_done (sp_desktop_document (desktop), "grmovem", SP_VERB_CONTEXT_GRADIENT,
1859
_("Move gradient mid stop(s)"));
1865
GrDrag::selected_move_screen (double x, double y)
1867
gdouble zoom = desktop->current_zoom();
1868
gdouble zx = x / zoom;
1869
gdouble zy = y / zoom;
1871
selected_move (zx, zy);
1875
Select the knot next to the last selected one and deselect all other selected.
1878
GrDrag::select_next ()
1880
GrDragger *d = NULL;
1881
if (selected == NULL || g_list_find(draggers, selected->data)->next == NULL) {
1883
d = (GrDragger *) draggers->data;
1885
d = (GrDragger *) g_list_find(draggers, selected->data)->next->data;
1893
Select the knot previous from the last selected one and deselect all other selected.
1896
GrDrag::select_prev ()
1898
GrDragger *d = NULL;
1899
if (selected == NULL || g_list_find(draggers, selected->data)->prev == NULL) {
1901
d = (GrDragger *) g_list_last (draggers)->data;
1903
d = (GrDragger *) g_list_find(draggers, selected->data)->prev->data;
1911
// FIXME: i.m.o. an ugly function that I just made to work, but... aargh! (Johan)
1913
GrDrag::deleteSelected (bool just_one)
1915
if (!selected) return;
1917
SPDocument *document = false;
1919
struct StructStopInfo {
1921
GrDraggable * draggable;
1922
SPGradient * gradient;
1923
SPGradient * vector;
1926
GSList *midstoplist = NULL; // list of stops that must be deleted (will be deleted first)
1927
GSList *endstoplist = NULL; // list of stops that must be deleted
1929
GrDragger *dragger = (GrDragger*) selected->data;
1930
for (GSList * drgble = dragger->draggables; drgble != NULL; drgble = drgble->next) {
1931
GrDraggable *draggable = (GrDraggable*) drgble->data;
1932
SPGradient *gradient = sp_item_gradient (draggable->item, draggable->fill_or_stroke);
1933
SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false);
1935
switch (draggable->point_type) {
1940
SPStop *stop = sp_get_stop_i(vector, draggable->point_i);
1941
// check if already present in list. (e.g. when both RG_MID1 and RG_MID2 were selected)
1942
bool present = false;
1943
for (GSList const * l = midstoplist; l != NULL; l = l->next) {
1944
if ( (SPStop*)l->data == stop ) {
1946
break; // no need to search further.
1950
midstoplist = g_slist_append(midstoplist, stop);
1953
case POINT_LG_BEGIN:
1955
case POINT_RG_CENTER:
1959
SPStop *stop = NULL;
1960
if ( (draggable->point_type == POINT_LG_BEGIN) || (draggable->point_type == POINT_RG_CENTER) ) {
1961
stop = sp_first_stop(vector);
1963
stop = sp_last_stop(vector);
1966
StructStopInfo *stopinfo = new StructStopInfo;
1967
stopinfo->spstop = stop;
1968
stopinfo->draggable = draggable;
1969
stopinfo->gradient = gradient;
1970
stopinfo->vector = vector;
1971
// check if already present in list. (e.g. when both R1 and R2 were selected)
1972
bool present = false;
1973
for (GSList const * l = endstoplist; l != NULL; l = l->next) {
1974
if ( ((StructStopInfo*)l->data)->spstop == stopinfo->spstop ) {
1976
break; // no need to search further.
1980
endstoplist = g_slist_append(endstoplist, stopinfo);
1988
selected = g_list_remove(selected, dragger);
1989
if ( just_one ) break; // iterate once if just_one is set.
1991
while (midstoplist) {
1992
SPStop *stop = (SPStop*) midstoplist->data;
1993
document = SP_OBJECT_DOCUMENT (stop);
1994
Inkscape::XML::Node * parent = SP_OBJECT_REPR(stop)->parent();
1995
parent->removeChild(SP_OBJECT_REPR(stop));
1996
midstoplist = g_slist_remove(midstoplist, stop);
1998
while (endstoplist) {
1999
StructStopInfo *stopinfo = (StructStopInfo*) endstoplist->data;
2000
document = SP_OBJECT_DOCUMENT (stopinfo->spstop);
2002
// 2 is the minimum, cannot delete more than that without deleting the whole vector
2003
// cannot use vector->vector.stops.size() because the vector might be invalidated by deletion of a midstop
2004
// manually count the children, don't know if there already exists a function for this...
2006
for ( SPObject *child = sp_object_first_child(stopinfo->vector) ;
2008
child = SP_OBJECT_NEXT(child) )
2010
if ( SP_IS_STOP(child) ) len ++;
2014
switch (stopinfo->draggable->point_type) {
2015
case POINT_LG_BEGIN:
2017
SP_OBJECT_REPR(stopinfo->vector)->removeChild(SP_OBJECT_REPR(stopinfo->spstop));
2019
SPLinearGradient *lg = SP_LINEARGRADIENT(stopinfo->gradient);
2020
Geom::Point oldbegin = Geom::Point (lg->x1.computed, lg->y1.computed);
2021
Geom::Point end = Geom::Point (lg->x2.computed, lg->y2.computed);
2022
SPStop *stop = sp_first_stop(stopinfo->vector);
2023
gdouble offset = stop->offset;
2024
Geom::Point newbegin = oldbegin + offset * (end - oldbegin);
2025
lg->x1.computed = newbegin[Geom::X];
2026
lg->y1.computed = newbegin[Geom::Y];
2028
Inkscape::XML::Node *repr = SP_OBJECT_REPR(stopinfo->gradient);
2029
sp_repr_set_svg_double(repr, "x1", lg->x1.computed);
2030
sp_repr_set_svg_double(repr, "y1", lg->y1.computed);
2032
sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", 0);
2034
// iterate through midstops to set new offset values such that they won't move on canvas.
2035
SPStop *laststop = sp_last_stop(stopinfo->vector);
2036
stop = sp_next_stop(stop);
2037
while ( stop != laststop ) {
2038
stop->offset = (stop->offset - offset)/(1 - offset);
2039
sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", stop->offset);
2040
stop = sp_next_stop(stop);
2046
SP_OBJECT_REPR(stopinfo->vector)->removeChild(SP_OBJECT_REPR(stopinfo->spstop));
2048
SPLinearGradient *lg = SP_LINEARGRADIENT(stopinfo->gradient);
2049
Geom::Point begin = Geom::Point (lg->x1.computed, lg->y1.computed);
2050
Geom::Point oldend = Geom::Point (lg->x2.computed, lg->y2.computed);
2051
SPStop *laststop = sp_last_stop(stopinfo->vector);
2052
gdouble offset = laststop->offset;
2053
Geom::Point newend = begin + offset * (oldend - begin);
2054
lg->x2.computed = newend[Geom::X];
2055
lg->y2.computed = newend[Geom::Y];
2057
Inkscape::XML::Node *repr = SP_OBJECT_REPR(stopinfo->gradient);
2058
sp_repr_set_svg_double(repr, "x2", lg->x2.computed);
2059
sp_repr_set_svg_double(repr, "y2", lg->y2.computed);
2060
laststop->offset = 1;
2061
sp_repr_set_css_double (SP_OBJECT_REPR (laststop), "offset", 1);
2063
// iterate through midstops to set new offset values such that they won't move on canvas.
2064
SPStop *stop = sp_first_stop(stopinfo->vector);
2065
stop = sp_next_stop(stop);
2066
while ( stop != laststop ) {
2067
stop->offset = stop->offset / offset;
2068
sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", stop->offset);
2069
stop = sp_next_stop(stop);
2073
case POINT_RG_CENTER:
2075
SPStop *newfirst = sp_next_stop (stopinfo->spstop);
2077
newfirst->offset = 0;
2078
sp_repr_set_css_double (SP_OBJECT_REPR (newfirst), "offset", 0);
2080
SP_OBJECT_REPR(stopinfo->vector)->removeChild(SP_OBJECT_REPR(stopinfo->spstop));
2085
SP_OBJECT_REPR(stopinfo->vector)->removeChild(SP_OBJECT_REPR(stopinfo->spstop));
2087
SPRadialGradient *rg = SP_RADIALGRADIENT(stopinfo->gradient);
2088
double oldradius = rg->r.computed;
2089
SPStop *laststop = sp_last_stop(stopinfo->vector);
2090
gdouble offset = laststop->offset;
2091
double newradius = offset * oldradius;
2092
rg->r.computed = newradius;
2094
Inkscape::XML::Node *repr = SP_OBJECT_REPR(rg);
2095
sp_repr_set_svg_double(repr, "r", rg->r.computed);
2096
laststop->offset = 1;
2097
sp_repr_set_css_double (SP_OBJECT_REPR (laststop), "offset", 1);
2099
// iterate through midstops to set new offset values such that they won't move on canvas.
2100
SPStop *stop = sp_first_stop(stopinfo->vector);
2101
stop = sp_next_stop(stop);
2102
while ( stop != laststop ) {
2103
stop->offset = stop->offset / offset;
2104
sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", stop->offset);
2105
stop = sp_next_stop(stop);
2111
{ // delete the gradient from the object. set fill to unset FIXME: set to fill of unselected node?
2112
SPCSSAttr *css = sp_repr_css_attr_new ();
2114
// stopinfo->spstop is the selected stop
2115
Inkscape::XML::Node *unselectedrepr = SP_OBJECT_REPR(stopinfo->vector)->firstChild();
2116
if (unselectedrepr == SP_OBJECT_REPR(stopinfo->spstop) ) {
2117
unselectedrepr = unselectedrepr->next();
2120
if (unselectedrepr == NULL) {
2121
if (stopinfo->draggable->fill_or_stroke) {
2122
sp_repr_css_unset_property (css, "fill");
2124
sp_repr_css_unset_property (css, "stroke");
2127
SPCSSAttr *stopcss = sp_repr_css_attr(unselectedrepr, "style");
2128
if (stopinfo->draggable->fill_or_stroke) {
2129
sp_repr_css_set_property(css, "fill", sp_repr_css_property(stopcss, "stop-color", "inkscape:unset"));
2130
sp_repr_css_set_property(css, "fill-opacity", sp_repr_css_property(stopcss, "stop-opacity", "1"));
2132
sp_repr_css_set_property(css, "stroke", sp_repr_css_property(stopcss, "stop-color", "inkscape:unset"));
2133
sp_repr_css_set_property(css, "stroke-opacity", sp_repr_css_property(stopcss, "stop-opacity", "1"));
2135
sp_repr_css_attr_unref (stopcss);
2138
sp_repr_css_change (SP_OBJECT_REPR (stopinfo->draggable->item), css, "style");
2139
sp_repr_css_attr_unref (css);
2142
endstoplist = g_slist_remove(endstoplist, stopinfo);
2147
sp_document_done ( document, SP_VERB_CONTEXT_GRADIENT, _("Delete gradient stop(s)") );
2155
c-file-style:"stroustrup"
2156
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2157
indent-tabs-mode:nil
2161
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :