~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/gradient-drag.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __GRADIENT_DRAG_C__
 
2
 
 
3
/*
 
4
 * On-canvas gradient dragging
 
5
 *
 
6
 * Authors:
 
7
 *   bulia byak <buliabyak@users.sf.net>
 
8
 *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
 
9
 *
 
10
 * Copyright (C) 2007 Johan Engelen
 
11
 * Copyright (C) 2005 Authors
 
12
 *
 
13
 * Released under GNU GPL, read the file 'COPYING' for more information
 
14
 */
 
15
 
 
16
#ifdef HAVE_CONFIG_H
 
17
#include "config.h"
 
18
#endif
 
19
 
 
20
#include <glibmm/i18n.h>
 
21
#include <cstring>
 
22
#include <string>
 
23
 
 
24
#include "desktop-handles.h"
 
25
#include "selection.h"
 
26
#include "desktop.h"
 
27
#include "desktop-style.h"
 
28
#include "document.h"
 
29
#include "display/sp-ctrlline.h"
 
30
#include "display/sp-canvas-util.h"
 
31
#include "xml/repr.h"
 
32
#include "svg/css-ostringstream.h"
 
33
#include "svg/svg.h"
 
34
#include "libnr/nr-point-fns.h"
 
35
#include "preferences.h"
 
36
#include "sp-item.h"
 
37
#include "style.h"
 
38
#include "knot.h"
 
39
#include "sp-linear-gradient.h"
 
40
#include "sp-radial-gradient.h"
 
41
#include "gradient-chemistry.h"
 
42
#include "gradient-drag.h"
 
43
#include "sp-stop.h"
 
44
#include "snap.h"
 
45
#include "sp-namedview.h"
 
46
#include "selection-chemistry.h"
 
47
 
 
48
#define GR_KNOT_COLOR_NORMAL 0xffffff00
 
49
#define GR_KNOT_COLOR_MOUSEOVER 0xff000000
 
50
#define GR_KNOT_COLOR_SELECTED 0x0000ff00
 
51
 
 
52
#define GR_LINE_COLOR_FILL 0x0000ff7f
 
53
#define GR_LINE_COLOR_STROKE 0x9999007f
 
54
 
 
55
// screen pixels between knots when they snap:
 
56
#define SNAP_DIST 5
 
57
 
 
58
// absolute distance between gradient points for them to become a single dragger when the drag is created:
 
59
#define MERGE_DIST 0.1
 
60
 
 
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
 
72
};
 
73
 
 
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>")
 
84
};
 
85
 
 
86
static void
 
87
gr_drag_sel_changed(Inkscape::Selection */*selection*/, gpointer data)
 
88
{
 
89
    GrDrag *drag = (GrDrag *) data;
 
90
    drag->updateDraggers ();
 
91
    drag->updateLines ();
 
92
    drag->updateLevels ();
 
93
}
 
94
 
 
95
static void
 
96
gr_drag_sel_modified (Inkscape::Selection */*selection*/, guint /*flags*/, gpointer data)
 
97
{
 
98
    GrDrag *drag = (GrDrag *) data;
 
99
    if (drag->local_change) {
 
100
        drag->local_change = false;
 
101
    } else {
 
102
        drag->updateDraggers ();
 
103
    }
 
104
    drag->updateLines ();
 
105
    drag->updateLevels ();
 
106
}
 
107
 
 
108
/**
 
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
 
111
any.
 
112
*/
 
113
int
 
114
gr_drag_style_query (SPStyle *style, int property, gpointer data)
 
115
{
 
116
    GrDrag *drag = (GrDrag *) data;
 
117
 
 
118
    if (property != QUERY_STYLE_PROPERTY_FILL && property != QUERY_STYLE_PROPERTY_STROKE && property != QUERY_STYLE_PROPERTY_MASTEROPACITY) {
 
119
        return QUERY_STYLE_NOTHING;
 
120
    }
 
121
 
 
122
    if (!drag->selected) {
 
123
        return QUERY_STYLE_NOTHING;
 
124
    } else {
 
125
        int ret = QUERY_STYLE_NOTHING;
 
126
 
 
127
        float cf[4];
 
128
        cf[0] = cf[1] = cf[2] = cf[3] = 0;
 
129
 
 
130
        int count = 0;
 
131
 
 
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;
 
136
 
 
137
                if (ret == QUERY_STYLE_NOTHING) {
 
138
                    ret = QUERY_STYLE_SINGLE;
 
139
                } else if (ret == QUERY_STYLE_SINGLE) {
 
140
                    ret = QUERY_STYLE_MULTIPLE_AVERAGED;
 
141
                }
 
142
 
 
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);
 
148
 
 
149
                count ++;
 
150
            }
 
151
        }
 
152
 
 
153
        if (count) {
 
154
            cf[0] /= count;
 
155
            cf[1] /= count;
 
156
            cf[2] /= count;
 
157
            cf[3] /= count;
 
158
 
 
159
            // set both fill and stroke with our stop-color and stop-opacity
 
160
            style->fill.clear();
 
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;
 
166
 
 
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;
 
171
 
 
172
            style->opacity.value = SP_SCALE24_FROM_FLOAT (cf[3]);
 
173
            style->opacity.set = TRUE;
 
174
        }
 
175
 
 
176
        return ret;
 
177
    }
 
178
}
 
179
 
 
180
bool
 
181
gr_drag_style_set (const SPCSSAttr *css, gpointer data)
 
182
{
 
183
    GrDrag *drag = (GrDrag *) data;
 
184
 
 
185
    if (!drag->selected)
 
186
        return false;
 
187
 
 
188
    SPCSSAttr *stop = sp_repr_css_attr_new ();
 
189
 
 
190
    // See if the css contains interesting properties, and if so, translate them into the format
 
191
    // acceptable for gradient stops
 
192
 
 
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"));
 
196
 
 
197
    if (css->attribute("lighting-color"))
 
198
        sp_repr_css_set_property (stop, "stop-color", css->attribute("lighting-color"));
 
199
 
 
200
    if (css->attribute("color"))
 
201
        sp_repr_css_set_property (stop, "stop-color", css->attribute("color"));
 
202
 
 
203
    if (css->attribute("stroke") && strcmp(css->attribute("stroke"), "none"))
 
204
        sp_repr_css_set_property (stop, "stop-color", css->attribute("stroke"));
 
205
 
 
206
    if (css->attribute("fill") && strcmp(css->attribute("fill"), "none"))
 
207
        sp_repr_css_set_property (stop, "stop-color", css->attribute("fill"));
 
208
 
 
209
    if (css->attribute("stop-color"))
 
210
        sp_repr_css_set_property (stop, "stop-color", css->attribute("stop-color"));
 
211
 
 
212
 
 
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);
 
221
 
 
222
        Inkscape::CSSOStringStream os;
 
223
        os << accumulated;
 
224
        sp_repr_css_set_property (stop, "stop-opacity", os.str().c_str());
 
225
 
 
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
 
229
    }
 
230
 
 
231
    if (!stop->attributeList()) { // nothing for us here, pass it on
 
232
        sp_repr_css_attr_unref(stop);
 
233
        return false;
 
234
    }
 
235
 
 
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;
 
240
 
 
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);
 
243
        }
 
244
    }
 
245
 
 
246
    //sp_repr_css_print(stop);
 
247
    sp_repr_css_attr_unref(stop);
 
248
    return true;
 
249
}
 
250
 
 
251
guint32 GrDrag::getColor()
 
252
{
 
253
    if (!selected) return 0;
 
254
 
 
255
    float cf[4];
 
256
    cf[0] = cf[1] = cf[2] = cf[3] = 0;
 
257
 
 
258
    int count = 0;
 
259
 
 
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;
 
264
 
 
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);
 
270
 
 
271
            count ++;
 
272
        }
 
273
    }
 
274
 
 
275
    if (count) {
 
276
        cf[0] /= count;
 
277
        cf[1] /= count;
 
278
        cf[2] /= count;
 
279
        cf[3] /= count;
 
280
    }
 
281
 
 
282
    return SP_RGBA32_F_COMPOSE(cf[0], cf[1], cf[2], cf[3]);
 
283
}
 
284
 
 
285
SPStop *
 
286
GrDrag::addStopNearPoint (SPItem *item, Geom::Point mouse_p, double tolerance)
 
287
{
 
288
    gfloat offset; // type of SPStop.offset = gfloat
 
289
    SPGradient *gradient;
 
290
    bool fill_or_stroke = true;
 
291
    bool r1_knot = false;
 
292
 
 
293
    bool addknot = false;
 
294
    do {
 
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);
 
299
 
 
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 ) {
 
303
                // add the knot
 
304
                offset = get_offset_between_points(nearest, begin, end);
 
305
                addknot = true;
 
306
                break; // break out of the while loop: add only one knot
 
307
            }
 
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);
 
315
                addknot = true;
 
316
                r1_knot = true;
 
317
                break; // break out of the while loop: add only one knot
 
318
            }
 
319
 
 
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);
 
325
                addknot = true;
 
326
                r1_knot = false;
 
327
                break; // break out of the while loop: add only one knot
 
328
            }
 
329
        }
 
330
        fill_or_stroke = !fill_or_stroke;
 
331
    } while (!fill_or_stroke && !addknot) ;
 
332
 
 
333
    if (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);
 
337
        guint i = 1;
 
338
        while ( (next_stop) && (next_stop->offset < offset) ) {
 
339
            prev_stop = next_stop;
 
340
            next_stop = sp_next_stop(next_stop);
 
341
            i++;
 
342
        }
 
343
        if (!next_stop) {
 
344
            // logical error: the endstop should have offset 1 and should always be more than this offset here
 
345
            return NULL;
 
346
        }
 
347
 
 
348
 
 
349
        SPStop *newstop = sp_vector_add_stop (vector, prev_stop, next_stop, offset);
 
350
        sp_gradient_ensure_vector (gradient);
 
351
        updateDraggers();
 
352
 
 
353
        return newstop;
 
354
    }
 
355
 
 
356
    return NULL;
 
357
}
 
358
 
 
359
 
 
360
bool
 
361
GrDrag::dropColor(SPItem */*item*/, gchar const *c, Geom::Point p)
 
362
{
 
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;
 
366
 
 
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;
 
373
               local_change = true;
 
374
               sp_item_gradient_stop_set_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop);
 
375
           }
 
376
           sp_repr_css_attr_unref(stop);
 
377
           return true;
 
378
        }
 
379
    }
 
380
 
 
381
    // now see if we're over line and create a new stop
 
382
    bool over_line = false;
 
383
    SPCtrlLine *line = NULL;
 
384
    if (lines) {
 
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());
 
391
                if (stop) {
 
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");
 
396
                    return true;
 
397
                }
 
398
            }
 
399
        }
 
400
    }
 
401
 
 
402
    return false;
 
403
}
 
404
 
 
405
 
 
406
GrDrag::GrDrag(SPDesktop *desktop) {
 
407
 
 
408
    this->desktop = desktop;
 
409
 
 
410
    this->selection = sp_desktop_selection(desktop);
 
411
 
 
412
    this->draggers = NULL;
 
413
    this->lines = NULL;
 
414
    this->selected = NULL;
 
415
 
 
416
    this->hor_levels.clear();
 
417
    this->vert_levels.clear();
 
418
 
 
419
    this->local_change = false;
 
420
 
 
421
    this->sel_changed_connection = this->selection->connectChanged(
 
422
        sigc::bind (
 
423
            sigc::ptr_fun(&gr_drag_sel_changed),
 
424
            (gpointer)this )
 
425
 
 
426
        );
 
427
    this->sel_modified_connection = this->selection->connectModified(
 
428
        sigc::bind(
 
429
            sigc::ptr_fun(&gr_drag_sel_modified),
 
430
            (gpointer)this )
 
431
        );
 
432
 
 
433
    this->style_set_connection = this->desktop->connectSetStyle(
 
434
        sigc::bind(
 
435
            sigc::ptr_fun(&gr_drag_style_set),
 
436
            (gpointer)this )
 
437
        );
 
438
 
 
439
    this->style_query_connection = this->desktop->connectQueryStyle(
 
440
        sigc::bind(
 
441
            sigc::ptr_fun(&gr_drag_style_query),
 
442
            (gpointer)this )
 
443
        );
 
444
 
 
445
    this->updateDraggers ();
 
446
    this->updateLines ();
 
447
    this->updateLevels ();
 
448
 
 
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));
 
451
    }
 
452
}
 
453
 
 
454
GrDrag::~GrDrag()
 
455
{
 
456
    this->sel_changed_connection.disconnect();
 
457
    this->sel_modified_connection.disconnect();
 
458
    this->style_set_connection.disconnect();
 
459
    this->style_query_connection.disconnect();
 
460
 
 
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;
 
467
    } else {
 
468
        desktop->gr_item = NULL;
 
469
        desktop->gr_point_type = 0;
 
470
        desktop->gr_point_i = 0;
 
471
        desktop->gr_fill_or_stroke = true;
 
472
    }
 
473
 
 
474
    deselect_all();
 
475
    for (GList *l = this->draggers; l != NULL; l = l->next) {
 
476
        delete ((GrDragger *) l->data);
 
477
    }
 
478
    g_list_free (this->draggers);
 
479
    this->draggers = NULL;
 
480
    this->selected = NULL;
 
481
 
 
482
    for (GSList *l = this->lines; l != NULL; l = l->next) {
 
483
        gtk_object_destroy( GTK_OBJECT (l->data));
 
484
    }
 
485
    g_slist_free (this->lines);
 
486
    this->lines = NULL;
 
487
}
 
488
 
 
489
GrDraggable::GrDraggable (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke)
 
490
{
 
491
    this->item = item;
 
492
    this->point_type = point_type;
 
493
    this->point_i = point_i;
 
494
    this->fill_or_stroke = fill_or_stroke;
 
495
 
 
496
    g_object_ref (G_OBJECT (this->item));
 
497
}
 
498
 
 
499
GrDraggable::~GrDraggable ()
 
500
{
 
501
    g_object_unref (G_OBJECT (this->item));
 
502
}
 
503
 
 
504
 
 
505
SPObject *
 
506
GrDraggable::getServer ()
 
507
{
 
508
    if (!item)
 
509
        return NULL;
 
510
 
 
511
    SPObject *server = NULL;
 
512
    if (fill_or_stroke)
 
513
        server = SP_OBJECT_STYLE_FILL_SERVER (item);
 
514
    else
 
515
        server = SP_OBJECT_STYLE_STROKE_SERVER (item);
 
516
 
 
517
    return server;
 
518
}
 
519
 
 
520
static
 
521
boost::optional<Geom::Point>
 
522
get_snap_vector (Geom::Point p, Geom::Point o, double snap, double initial)
 
523
{
 
524
    double r = L2 (p - o);
 
525
    if (r < 1e-3) {
 
526
        return boost::optional<Geom::Point>();
 
527
    }
 
528
 
 
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);
 
534
}
 
535
 
 
536
static void
 
537
gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gpointer data)
 
538
{
 
539
    GrDragger *dragger = (GrDragger *) data;
 
540
    GrDrag *drag = dragger->parent;
 
541
 
 
542
    Geom::Point p = ppointer;
 
543
 
 
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();
 
547
 
 
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);
 
558
            }
 
559
            dr_new->updateKnotShape();
 
560
            g_slist_free (dragger->draggables->next);
 
561
            dragger->draggables->next = NULL;
 
562
            dragger->updateKnotShape();
 
563
            dragger->updateTip();
 
564
        }
 
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) {
 
570
 
 
571
                // Merge draggers:
 
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);
 
577
                }
 
578
 
 
579
                // unlink and delete this dragger
 
580
                dragger->parent->draggers = g_list_remove (dragger->parent->draggers, dragger);
 
581
                delete dragger;
 
582
 
 
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 ();
 
588
                d_new->updateTip ();
 
589
                d_new->updateDependencies(true);
 
590
                sp_document_done (sp_desktop_document (d_new->parent->desktop), SP_VERB_CONTEXT_GRADIENT,
 
591
                                  _("Merge gradient handles"));
 
592
                return;
 
593
            }
 
594
        }
 
595
    }
 
596
 
 
597
    if (!((state & GDK_SHIFT_MASK) || ((state & GDK_CONTROL_MASK) && (state & GDK_MOD1_MASK)))) {
 
598
        // Try snapping to the grid or guides
 
599
        m.setup(desktop);
 
600
        Inkscape::SnappedPoint s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p), Inkscape::SNAPSOURCE_HANDLE);
 
601
        if (s.getSnapped()) {
 
602
            p = s.getPoint();
 
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);
 
613
                    was_snapped = true;
 
614
                    sp_knot_moveto (knot, p);
 
615
                }
 
616
            }
 
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);
 
622
                    was_snapped = true;
 
623
                    sp_knot_moveto (knot, p);
 
624
                }
 
625
            }
 
626
            if (was_snapped) {
 
627
                desktop->snapindicator->set_new_snaptarget(s);
 
628
            }
 
629
        }
 
630
    }
 
631
 
 
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. */
 
636
 
 
637
        // This list will store snap vectors from all draggables of dragger
 
638
        GSList *snap_vectors = NULL;
 
639
 
 
640
        for (GSList const* i = dragger->draggables; i != NULL; i = i->next) {
 
641
            GrDraggable *draggable = (GrDraggable *) i->data;
 
642
 
 
643
            Geom::Point dr_snap(Geom::infinity(), Geom::infinity());
 
644
 
 
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)
 
649
                        continue;
 
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));
 
657
                            dr_snap = center;
 
658
                        } else {
 
659
                            // moving linear around the other end
 
660
                            dr_snap = d_new->point;
 
661
                        }
 
662
                    }
 
663
                }
 
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)
 
668
                        continue;
 
669
                    if (d_new->isA (draggable->item,
 
670
                                    POINT_RG_CENTER,
 
671
                                    draggable->fill_or_stroke)) {
 
672
                        // found the center of the radial gradient;
 
673
                        dr_snap = d_new->point;
 
674
                    }
 
675
                }
 
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;
 
679
            }
 
680
 
 
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));
 
686
                } else {
 
687
                    // with Ctrl, snap to M_PI/snaps
 
688
                    snap_vector = get_snap_vector (p, dr_snap, M_PI/snaps, 0);
 
689
                }
 
690
            }
 
691
            if (snap_vector) {
 
692
                snap_vectors = g_slist_prepend (snap_vectors, &(*snap_vector));
 
693
            }
 
694
        }
 
695
 
 
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))
 
701
                move = *snap_vector;
 
702
        }
 
703
        if (move[Geom::X] < 9999) {
 
704
            p += move;
 
705
            sp_knot_moveto (knot, p);
 
706
        }
 
707
 
 
708
        g_slist_free(snap_vectors);
 
709
    }
 
710
 
 
711
    drag->keep_selection = (bool) g_list_find(drag->selected, dragger);
 
712
    bool scale_radial = (state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK);
 
713
 
 
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);
 
717
    } else {
 
718
        dragger->point = p;
 
719
        dragger->fireDraggables (false, scale_radial);
 
720
        dragger->updateDependencies(false);
 
721
    }
 
722
 
 
723
}
 
724
 
 
725
 
 
726
static void
 
727
gr_midpoint_limits(GrDragger *dragger, SPObject *server, Geom::Point *begin, Geom::Point *end, Geom::Point *low_lim, Geom::Point *high_lim, GSList **moving)
 
728
{
 
729
 
 
730
    GrDrag *drag = dragger->parent;
 
731
    // a midpoint dragger can (logically) only contain one GrDraggable
 
732
    GrDraggable *draggable = (GrDraggable *) dragger->draggables->data;
 
733
 
 
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);
 
737
 
 
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()) {
 
743
        GrDragger* d_add;
 
744
        while ( true )
 
745
        {
 
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;
 
751
            } else {
 
752
                break;
 
753
            }
 
754
        }
 
755
 
 
756
        while ( true )
 
757
        {
 
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;
 
763
            } else {
 
764
                break;
 
765
            }
 
766
        }
 
767
    }
 
768
 
 
769
    if ( SP_IS_LINEARGRADIENT(server) ) {
 
770
        guint num = SP_LINEARGRADIENT(server)->vector.stops.size();
 
771
        GrDragger *d_temp;
 
772
        if (lowest_i == 1) {
 
773
            d_temp = drag->getDraggerFor (draggable->item, POINT_LG_BEGIN, 0, draggable->fill_or_stroke);
 
774
        } else {
 
775
            d_temp = drag->getDraggerFor (draggable->item, POINT_LG_MID, lowest_i - 1, draggable->fill_or_stroke);
 
776
        }
 
777
        if (d_temp)
 
778
            *begin = d_temp->point;
 
779
 
 
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);
 
783
        }
 
784
        if (d_temp)
 
785
            *end = d_temp->point;
 
786
    } else if ( SP_IS_RADIALGRADIENT(server) ) {
 
787
        guint num = SP_RADIALGRADIENT(server)->vector.stops.size();
 
788
        GrDragger *d_temp;
 
789
        if (lowest_i == 1) {
 
790
            d_temp = drag->getDraggerFor (draggable->item, POINT_RG_CENTER, 0, draggable->fill_or_stroke);
 
791
        } else {
 
792
            d_temp = drag->getDraggerFor (draggable->item, draggable->point_type, lowest_i - 1, draggable->fill_or_stroke);
 
793
        }
 
794
        if (d_temp)
 
795
            *begin = d_temp->point;
 
796
 
 
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);
 
800
        }
 
801
        if (d_temp)
 
802
            *end = d_temp->point;
 
803
    }
 
804
 
 
805
    *low_lim  = dragger->point - (lowest_dragger->point - *begin);
 
806
    *high_lim = dragger->point - (highest_dragger->point - *end);
 
807
}
 
808
 
 
809
 
 
810
 
 
811
/**
 
812
Called when a midpoint knot is dragged.
 
813
*/
 
814
static void
 
815
gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &ppointer, guint state, gpointer data)
 
816
{
 
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;
 
821
 
 
822
    // FIXME: take from prefs
 
823
    double snap_fraction = 0.1;
 
824
 
 
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);
 
828
 
 
829
    SPObject *server = draggable->getServer();
 
830
 
 
831
    GSList *moving = NULL;
 
832
    gr_midpoint_limits(dragger, server, &begin, &end, &low_lim, &high_lim, &moving);
 
833
 
 
834
    if (state & GDK_CONTROL_MASK) {
 
835
        p = snap_vector_midpoint (p, low_lim, high_lim, snap_fraction);
 
836
    } else {
 
837
        p = snap_vector_midpoint (p, low_lim, high_lim, 0);
 
838
    }
 
839
    Geom::Point displacement = p - dragger->point;
 
840
 
 
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
 
847
            double alpha = 1.0;
 
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;
 
854
            }
 
855
        }
 
856
        drg->point += this_move;
 
857
        sp_knot_moveto (drgknot, drg->point);
 
858
        drg->fireDraggables (false);
 
859
        drg->updateDependencies(false);
 
860
    }
 
861
 
 
862
    g_slist_free(moving);
 
863
 
 
864
    drag->keep_selection = dragger->isSelected();
 
865
}
 
866
 
 
867
 
 
868
 
 
869
static void
 
870
gr_knot_grabbed_handler (SPKnot */*knot*/, unsigned int /*state*/, gpointer data)
 
871
{
 
872
    GrDragger *dragger = (GrDragger *) data;
 
873
 
 
874
    sp_canvas_force_full_redraw_after_interruptions(dragger->parent->desktop->canvas, 5);
 
875
}
 
876
 
 
877
/**
 
878
Called when the mouse releases a dragger knot; changes gradient writing to repr, updates other draggers if needed
 
879
*/
 
880
static void
 
881
gr_knot_ungrabbed_handler (SPKnot *knot, unsigned int state, gpointer data)
 
882
{
 
883
    GrDragger *dragger = (GrDragger *) data;
 
884
 
 
885
    sp_canvas_end_forced_full_redraws(dragger->parent->desktop->canvas);
 
886
 
 
887
    dragger->point_original = dragger->point = knot->pos;
 
888
 
 
889
    if ((state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK)) {
 
890
        dragger->fireDraggables (true, true);
 
891
    } else {
 
892
        dragger->fireDraggables (true);
 
893
    }
 
894
 
 
895
    for (GList *i = dragger->parent->selected; i != NULL; i = i->next) {
 
896
        GrDragger *d = (GrDragger *) i->data;
 
897
        if (d == dragger)
 
898
            continue;
 
899
        d->fireDraggables (true);
 
900
    }
 
901
 
 
902
    // make this dragger selected
 
903
    if (!dragger->parent->keep_selection) {
 
904
        dragger->parent->setSelected (dragger);
 
905
    }
 
906
    dragger->parent->keep_selection = false;
 
907
 
 
908
    dragger->updateDependencies(true);
 
909
 
 
910
    // we did an undoable action
 
911
    sp_document_done (sp_desktop_document (dragger->parent->desktop), SP_VERB_CONTEXT_GRADIENT,
 
912
                      _("Move gradient handle"));
 
913
}
 
914
 
 
915
/**
 
916
Called when a dragger knot is clicked; selects the dragger or deletes it depending on the
 
917
state of the keyboard keys
 
918
*/
 
919
static void
 
920
gr_knot_clicked_handler(SPKnot */*knot*/, guint state, gpointer data)
 
921
{
 
922
    GrDragger *dragger = (GrDragger *) data;
 
923
    GrDraggable *draggable = (GrDraggable *) dragger->draggables->data;
 
924
    if (!draggable) return;
 
925
 
 
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
 
931
                SPStop *stop = NULL;
 
932
                switch (draggable->point_type) {  // if we delete first or last stop, move the next/previous to the edge
 
933
                case POINT_LG_BEGIN:
 
934
                case POINT_RG_CENTER:
 
935
                    stop = sp_first_stop(gradient);
 
936
                        {
 
937
                            SPStop *next = sp_next_stop (stop);
 
938
                                if (next) {
 
939
                                        next->offset = 0;
 
940
                                        sp_repr_set_css_double (SP_OBJECT_REPR (next), "offset", 0);
 
941
                                }
 
942
                        }
 
943
                    break;
 
944
                case POINT_LG_END:
 
945
                case POINT_RG_R1:
 
946
                case POINT_RG_R2:
 
947
                    stop = sp_last_stop(gradient);
 
948
                    {
 
949
                            SPStop *prev = sp_prev_stop (stop, gradient);
 
950
                            if (prev) {
 
951
                                    prev->offset = 1;
 
952
                                    sp_repr_set_css_double (SP_OBJECT_REPR (prev), "offset", 1);
 
953
                            }
 
954
                        }
 
955
                    break;
 
956
                case POINT_LG_MID:
 
957
                case POINT_RG_MID1:
 
958
                case POINT_RG_MID2:
 
959
                    stop = sp_get_stop_i(gradient, draggable->point_i);
 
960
                    break;
 
961
                }
 
962
 
 
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"));
 
966
        }
 
967
    } else {
 
968
    // select the dragger
 
969
        dragger->point_original = dragger->point;
 
970
 
 
971
        if ( state & GDK_SHIFT_MASK ) {
 
972
            dragger->parent->setSelected (dragger, true, false);
 
973
        } else {
 
974
            dragger->parent->setSelected (dragger);
 
975
        }
 
976
    }
 
977
}
 
978
 
 
979
/**
 
980
Called when a dragger knot is doubleclicked; opens gradient editor with the stop from the first draggable
 
981
*/
 
982
static void
 
983
gr_knot_doubleclicked_handler (SPKnot */*knot*/, guint /*state*/, gpointer data)
 
984
{
 
985
    GrDragger *dragger = (GrDragger *) data;
 
986
 
 
987
    dragger->point_original = dragger->point;
 
988
 
 
989
    if (dragger->draggables == NULL)
 
990
        return;
 
991
 
 
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);
 
994
}
 
995
 
 
996
/**
 
997
Act upon all draggables of the dragger, setting them to the dragger's point
 
998
*/
 
999
void
 
1000
GrDragger::fireDraggables (bool write_repr, bool scale_radial, bool merging_focus)
 
1001
{
 
1002
    for (GSList const* i = this->draggables; i != NULL; i = i->next) {
 
1003
        GrDraggable *draggable = (GrDraggable *) i->data;
 
1004
 
 
1005
        // set local_change flag so that selection_changed callback does not regenerate draggers
 
1006
        this->parent->local_change = true;
 
1007
 
 
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)))
 
1012
        {
 
1013
            sp_item_gradient_set_coords (draggable->item, draggable->point_type, draggable->point_i, this->point, draggable->fill_or_stroke, write_repr, scale_radial);
 
1014
        }
 
1015
    }
 
1016
}
 
1017
 
 
1018
/**
 
1019
Checks if the dragger has a draggable with this point_type
 
1020
 */
 
1021
bool
 
1022
GrDragger::isA (gint point_type)
 
1023
{
 
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) {
 
1027
            return true;
 
1028
        }
 
1029
    }
 
1030
    return false;
 
1031
}
 
1032
 
 
1033
/**
 
1034
Checks if the dragger has a draggable with this item, point_type + point_i (number), fill_or_stroke
 
1035
 */
 
1036
bool
 
1037
GrDragger::isA (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke)
 
1038
{
 
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) ) {
 
1042
            return true;
 
1043
        }
 
1044
    }
 
1045
    return false;
 
1046
}
 
1047
 
 
1048
/**
 
1049
Checks if the dragger has a draggable with this item, point_type, fill_or_stroke
 
1050
 */
 
1051
bool
 
1052
GrDragger::isA (SPItem *item, gint point_type, bool fill_or_stroke)
 
1053
{
 
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) ) {
 
1057
            return true;
 
1058
        }
 
1059
    }
 
1060
    return false;
 
1061
}
 
1062
 
 
1063
bool
 
1064
GrDraggable::mayMerge (GrDraggable *da2)
 
1065
{
 
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
 
1071
            return false;
 
1072
        }
 
1073
    }
 
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) )
 
1078
        return false;
 
1079
 
 
1080
    return true;
 
1081
}
 
1082
 
 
1083
bool
 
1084
GrDragger::mayMerge (GrDragger *other)
 
1085
{
 
1086
    if (this == other)
 
1087
        return false;
 
1088
 
 
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))
 
1094
                return false;
 
1095
        }
 
1096
    }
 
1097
    return true;
 
1098
}
 
1099
 
 
1100
bool
 
1101
GrDragger::mayMerge (GrDraggable *da2)
 
1102
{
 
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))
 
1106
            return false;
 
1107
    }
 
1108
    return true;
 
1109
}
 
1110
 
 
1111
/**
 
1112
Updates the statusbar tip of the dragger knot, based on its draggables
 
1113
 */
 
1114
void
 
1115
GrDragger::updateTip ()
 
1116
{
 
1117
        if (this->knot && this->knot->tip) {
 
1118
                g_free (this->knot->tip);
 
1119
                this->knot->tip = NULL;
 
1120
        }
 
1121
 
 
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) {
 
1126
            case POINT_LG_MID:
 
1127
            case POINT_RG_MID1:
 
1128
            case POINT_RG_MID2:
 
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]),
 
1131
                                                   draggable->point_i,
 
1132
                                                   item_desc,
 
1133
                                                   draggable->fill_or_stroke == false ? _(" (stroke)") : "");
 
1134
                break;
 
1135
 
 
1136
            default:
 
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]),
 
1139
                                                   item_desc,
 
1140
                                                   draggable->fill_or_stroke == false ? _(" (stroke)") : "");
 
1141
                break;
 
1142
        }
 
1143
        g_free(item_desc);
 
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"));
 
1146
    } else {
 
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",
 
1150
                                                    length),
 
1151
                                           length);
 
1152
    }
 
1153
}
 
1154
 
 
1155
/**
 
1156
Adds a draggable to the dragger
 
1157
 */
 
1158
void
 
1159
GrDragger::updateKnotShape ()
 
1160
{
 
1161
    if (!draggables)
 
1162
        return;
 
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);
 
1165
}
 
1166
 
 
1167
/**
 
1168
Adds a draggable to the dragger
 
1169
 */
 
1170
void
 
1171
GrDragger::addDraggable (GrDraggable *draggable)
 
1172
{
 
1173
    this->draggables = g_slist_prepend (this->draggables, draggable);
 
1174
 
 
1175
    this->updateTip();
 
1176
}
 
1177
 
 
1178
 
 
1179
/**
 
1180
Moves this dragger to the point of the given draggable, acting upon all other draggables
 
1181
 */
 
1182
void
 
1183
GrDragger::moveThisToDraggable (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, bool write_repr)
 
1184
{
 
1185
    this->point = sp_item_gradient_get_coords (item, point_type, point_i, fill_or_stroke);
 
1186
    this->point_original = this->point;
 
1187
 
 
1188
    sp_knot_moveto (this->knot, this->point);
 
1189
 
 
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) ) {
 
1196
            continue;
 
1197
        }
 
1198
        sp_item_gradient_set_coords (da->item, da->point_type, da->point_i, this->point, da->fill_or_stroke, write_repr, false);
 
1199
    }
 
1200
    // FIXME: here we should also call this->updateDependencies(write_repr); to propagate updating, but how to prevent loops?
 
1201
}
 
1202
 
 
1203
 
 
1204
/**
 
1205
Moves all midstop draggables that depend on this one
 
1206
 */
 
1207
void
 
1208
GrDragger::updateMidstopDependencies (GrDraggable *draggable, bool write_repr) {
 
1209
    SPObject *server = draggable->getServer();
 
1210
    if (!server)
 
1211
        return;
 
1212
    guint num = SP_GRADIENT(server)->vector.stops.size();
 
1213
    if (num <= 2) return;
 
1214
 
 
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);
 
1218
        }
 
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);
 
1223
        }
 
1224
    }
 
1225
}
 
1226
 
 
1227
 
 
1228
/**
 
1229
Moves all draggables that depend on this one
 
1230
 */
 
1231
void
 
1232
GrDragger::updateDependencies (bool write_repr)
 
1233
{
 
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:
 
1238
                {
 
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);
 
1241
 
 
1242
                    this->updateMidstopDependencies (draggable, write_repr);
 
1243
                }
 
1244
                break;
 
1245
            case POINT_LG_END:
 
1246
                {
 
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);
 
1249
 
 
1250
                    this->updateMidstopDependencies (draggable, write_repr);
 
1251
                }
 
1252
                break;
 
1253
            case POINT_LG_MID:
 
1254
                // no other nodes depend on mid points.
 
1255
                break;
 
1256
            case POINT_RG_R2:
 
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);
 
1260
                break;
 
1261
            case POINT_RG_R1:
 
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);
 
1265
                break;
 
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);
 
1271
                break;
 
1272
            case POINT_RG_FOCUS:
 
1273
                // nothing can depend on that
 
1274
                break;
 
1275
            case POINT_RG_MID1:
 
1276
                this->moveOtherToDraggable (draggable->item, POINT_RG_MID2, draggable->point_i, draggable->fill_or_stroke, write_repr);
 
1277
                break;
 
1278
            case POINT_RG_MID2:
 
1279
                this->moveOtherToDraggable (draggable->item, POINT_RG_MID1, draggable->point_i, draggable->fill_or_stroke, write_repr);
 
1280
                break;
 
1281
            default:
 
1282
                break;
 
1283
        }
 
1284
    }
 
1285
}
 
1286
 
 
1287
 
 
1288
 
 
1289
GrDragger::GrDragger (GrDrag *parent, Geom::Point p, GrDraggable *draggable)
 
1290
  : point(p),
 
1291
    point_original(p)
 
1292
{
 
1293
    this->draggables = NULL;
 
1294
 
 
1295
    this->parent = parent;
 
1296
 
 
1297
    // create the knot
 
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);
 
1303
 
 
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);
 
1307
 
 
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) ) )
 
1314
    {
 
1315
        this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_midpoint_handler), this);
 
1316
    } else {
 
1317
        this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_handler), this);
 
1318
    }
 
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);
 
1323
 
 
1324
    // add the initial draggable
 
1325
    if (draggable)
 
1326
        this->addDraggable (draggable);
 
1327
    updateKnotShape();
 
1328
}
 
1329
 
 
1330
GrDragger::~GrDragger ()
 
1331
{
 
1332
    // unselect if it was selected
 
1333
    this->parent->setDeselected(this);
 
1334
 
 
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);
 
1341
 
 
1342
    /* unref should call destroy */
 
1343
    g_object_unref (G_OBJECT (this->knot));
 
1344
 
 
1345
    // delete all draggables
 
1346
    for (GSList const* i = this->draggables; i != NULL; i = i->next) {
 
1347
        delete ((GrDraggable *) i->data);
 
1348
    }
 
1349
    g_slist_free (this->draggables);
 
1350
    this->draggables = NULL;
 
1351
}
 
1352
 
 
1353
/**
 
1354
Select the dragger which has the given draggable.
 
1355
*/
 
1356
GrDragger *
 
1357
GrDrag::getDraggerFor (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke)
 
1358
{
 
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)) {
 
1367
                return (dragger);
 
1368
            }
 
1369
        }
 
1370
    }
 
1371
    return NULL;
 
1372
}
 
1373
 
 
1374
 
 
1375
void
 
1376
GrDragger::moveOtherToDraggable (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, bool write_repr)
 
1377
{
 
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);
 
1381
    }
 
1382
}
 
1383
 
 
1384
 
 
1385
/**
 
1386
  Draw this dragger as selected
 
1387
*/
 
1388
void
 
1389
GrDragger::select()
 
1390
{
 
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);
 
1393
}
 
1394
 
 
1395
/**
 
1396
  Draw this dragger as normal (deselected)
 
1397
*/
 
1398
void
 
1399
GrDragger::deselect()
 
1400
{
 
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);
 
1403
}
 
1404
 
 
1405
bool
 
1406
GrDragger::isSelected()
 
1407
{
 
1408
    return g_list_find (parent->selected, this);
 
1409
}
 
1410
 
 
1411
/**
 
1412
\brief Deselect all stops/draggers (private)
 
1413
*/
 
1414
void
 
1415
GrDrag::deselect_all()
 
1416
{
 
1417
    while (selected) {
 
1418
        ( (GrDragger*) selected->data)->deselect();
 
1419
        selected = g_list_remove(selected, selected->data);
 
1420
    }
 
1421
}
 
1422
 
 
1423
/**
 
1424
\brief Deselect all stops/draggers (public; emits signal)
 
1425
*/
 
1426
void
 
1427
GrDrag::deselectAll()
 
1428
{
 
1429
    deselect_all();
 
1430
    this->desktop->emitToolSubselectionChanged(NULL);
 
1431
}
 
1432
 
 
1433
/**
 
1434
\brief Select all stops/draggers
 
1435
*/
 
1436
void
 
1437
GrDrag::selectAll()
 
1438
{
 
1439
    for (GList *l = this->draggers; l != NULL; l = l->next) {
 
1440
        GrDragger *d = ((GrDragger *) l->data);
 
1441
        setSelected (d, true, true);
 
1442
    }
 
1443
}
 
1444
 
 
1445
/**
 
1446
\brief Select all stops/draggers that match the coords
 
1447
*/
 
1448
void
 
1449
GrDrag::selectByCoords(std::vector<Geom::Point> coords)
 
1450
{
 
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);
 
1456
            }
 
1457
        }
 
1458
    }
 
1459
}
 
1460
 
 
1461
 
 
1462
/**
 
1463
\brief Select all stops/draggers that fall within the rect
 
1464
*/
 
1465
void
 
1466
GrDrag::selectRect(Geom::Rect const &r)
 
1467
{
 
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);
 
1472
        }
 
1473
    }
 
1474
}
 
1475
 
 
1476
/**
 
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
 
1481
*/
 
1482
void
 
1483
GrDrag::setSelected (GrDragger *dragger, bool add_to_selection, bool override)
 
1484
{
 
1485
    GrDragger *seldragger = NULL;
 
1486
 
 
1487
    if (add_to_selection) {
 
1488
        if (!dragger) return;
 
1489
        if (override) {
 
1490
            if (!g_list_find(selected, dragger)) {
 
1491
                selected = g_list_prepend(selected, dragger);
 
1492
            }
 
1493
            dragger->select();
 
1494
            seldragger = dragger;
 
1495
        } else { // toggle
 
1496
            if (g_list_find(selected, dragger)) {
 
1497
                selected = g_list_remove(selected, dragger);
 
1498
                dragger->deselect();
 
1499
                if (selected) {
 
1500
                    seldragger = (GrDragger*) selected->data; // select the dragger that is first in the list
 
1501
                }
 
1502
            } else {
 
1503
                selected = g_list_prepend(selected, dragger);
 
1504
                dragger->select();
 
1505
                seldragger = dragger;
 
1506
            }
 
1507
        }
 
1508
    } else {
 
1509
        deselect_all();
 
1510
        if (dragger) {
 
1511
            selected = g_list_prepend(selected, dragger);
 
1512
            dragger->select();
 
1513
            seldragger = dragger;
 
1514
        }
 
1515
    }
 
1516
    if (seldragger) {
 
1517
        this->desktop->emitToolSubselectionChanged((gpointer) seldragger);
 
1518
    }
 
1519
}
 
1520
 
 
1521
/**
 
1522
\brief Deselect a dragger
 
1523
\param dragger       The dragger to deselect
 
1524
*/
 
1525
void
 
1526
GrDrag::setDeselected (GrDragger *dragger)
 
1527
{
 
1528
    if (g_list_find(selected, dragger)) {
 
1529
        selected = g_list_remove(selected, dragger);
 
1530
        dragger->deselect();
 
1531
    }
 
1532
    this->desktop->emitToolSubselectionChanged((gpointer) (selected ? selected->data : NULL ));
 
1533
}
 
1534
 
 
1535
 
 
1536
 
 
1537
/**
 
1538
Create a line from p1 to p2 and add it to the lines list
 
1539
 */
 
1540
void
 
1541
GrDrag::addLine (SPItem *item, Geom::Point p1, Geom::Point p2, guint32 rgba)
 
1542
{
 
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);
 
1552
}
 
1553
 
 
1554
/**
 
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
 
1557
 */
 
1558
void
 
1559
GrDrag::addDragger (GrDraggable *draggable)
 
1560
{
 
1561
    Geom::Point p = sp_item_gradient_get_coords (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
 
1562
 
 
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();
 
1569
            return;
 
1570
        }
 
1571
    }
 
1572
 
 
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);
 
1576
}
 
1577
 
 
1578
/**
 
1579
Add draggers for the radial gradient rg on item
 
1580
*/
 
1581
void
 
1582
GrDrag::addDraggersRadial (SPRadialGradient *rg, SPItem *item, bool fill_or_stroke)
 
1583
{
 
1584
    addDragger (new GrDraggable (item, POINT_RG_CENTER, 0, fill_or_stroke));
 
1585
    guint num = rg->vector.stops.size();
 
1586
    if (num > 2) {
 
1587
        for ( guint i = 1; i < num - 1; i++ ) {
 
1588
            addDragger (new GrDraggable (item, POINT_RG_MID1, i, fill_or_stroke));
 
1589
        }
 
1590
    }
 
1591
    addDragger (new GrDraggable (item, POINT_RG_R1, num-1, fill_or_stroke));
 
1592
    if (num > 2) {
 
1593
        for ( guint i = 1; i < num - 1; i++ ) {
 
1594
            addDragger (new GrDraggable (item, POINT_RG_MID2, i, fill_or_stroke));
 
1595
        }
 
1596
    }
 
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));
 
1599
}
 
1600
 
 
1601
/**
 
1602
Add draggers for the linear gradient lg on item
 
1603
*/
 
1604
void
 
1605
GrDrag::addDraggersLinear (SPLinearGradient *lg, SPItem *item, bool fill_or_stroke)
 
1606
{
 
1607
    addDragger (new GrDraggable (item, POINT_LG_BEGIN, 0, fill_or_stroke));
 
1608
    guint num = lg->vector.stops.size();
 
1609
    if (num > 2) {
 
1610
        for ( guint i = 1; i < num - 1; i++ ) {
 
1611
            addDragger (new GrDraggable (item, POINT_LG_MID, i, fill_or_stroke));
 
1612
        }
 
1613
    }
 
1614
    addDragger (new GrDraggable (item, POINT_LG_END, num-1, fill_or_stroke));
 
1615
}
 
1616
 
 
1617
/**
 
1618
Artificially grab the knot of this dragger; used by the gradient context
 
1619
*/
 
1620
void
 
1621
GrDrag::grabKnot (GrDragger *dragger, gint x, gint y, guint32 etime)
 
1622
{
 
1623
    if (dragger) {
 
1624
        sp_knot_start_dragging (dragger->knot, dragger->point, x, y, etime);
 
1625
    }
 
1626
}
 
1627
 
 
1628
/**
 
1629
Artificially grab the knot of the dragger with this draggable; used by the gradient context
 
1630
*/
 
1631
void
 
1632
GrDrag::grabKnot (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, gint x, gint y, guint32 etime)
 
1633
{
 
1634
    GrDragger *dragger = getDraggerFor (item, point_type, point_i, fill_or_stroke);
 
1635
    if (dragger) {
 
1636
        sp_knot_start_dragging (dragger->knot, dragger->point, x, y, etime);
 
1637
    }
 
1638
}
 
1639
 
 
1640
/**
 
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
 
1643
*/
 
1644
void
 
1645
GrDrag::updateDraggers ()
 
1646
{
 
1647
    while (selected) {
 
1648
        selected = g_list_remove(selected, selected->data);
 
1649
    }
 
1650
    // delete old draggers
 
1651
    for (GList const* i = this->draggers; i != NULL; i = i->next) {
 
1652
        delete ((GrDragger *) i->data);
 
1653
    }
 
1654
    g_list_free (this->draggers);
 
1655
    this->draggers = NULL;
 
1656
 
 
1657
    g_return_if_fail (this->selection != NULL);
 
1658
 
 
1659
    for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
 
1660
 
 
1661
        SPItem *item = SP_ITEM(i->data);
 
1662
        SPStyle *style = SP_OBJECT_STYLE (item);
 
1663
 
 
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);
 
1670
            }
 
1671
        }
 
1672
 
 
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);
 
1679
            }
 
1680
        }
 
1681
    }
 
1682
}
 
1683
 
 
1684
/**
 
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
 
1687
*/
 
1688
void
 
1689
GrDrag::updateLines ()
 
1690
{
 
1691
    // delete old lines
 
1692
    for (GSList const *i = this->lines; i != NULL; i = i->next) {
 
1693
        gtk_object_destroy( GTK_OBJECT (i->data));
 
1694
    }
 
1695
    g_slist_free (this->lines);
 
1696
    this->lines = NULL;
 
1697
 
 
1698
    g_return_if_fail (this->selection != NULL);
 
1699
 
 
1700
    for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) {
 
1701
 
 
1702
        SPItem *item = SP_ITEM(i->data);
 
1703
 
 
1704
        SPStyle *style = SP_OBJECT_STYLE (item);
 
1705
 
 
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);
 
1714
            }
 
1715
        }
 
1716
 
 
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);
 
1725
            }
 
1726
        }
 
1727
    }
 
1728
}
 
1729
 
 
1730
/**
 
1731
Regenerates the levels list from the current selection
 
1732
*/
 
1733
void
 
1734
GrDrag::updateLevels ()
 
1735
{
 
1736
    hor_levels.clear();
 
1737
    vert_levels.clear();
 
1738
 
 
1739
    g_return_if_fail (this->selection != NULL);
 
1740
 
 
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);
 
1744
        if (rect) {
 
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]));
 
1752
        }
 
1753
    }
 
1754
}
 
1755
 
 
1756
void
 
1757
GrDrag::selected_reverse_vector ()
 
1758
{
 
1759
    if (selected == NULL)
 
1760
        return;
 
1761
 
 
1762
    for (GSList const* i = ( (GrDragger*) selected->data )->draggables; i != NULL; i = i->next) {
 
1763
        GrDraggable *draggable = (GrDraggable *) i->data;
 
1764
 
 
1765
        sp_item_gradient_reverse_vector (draggable->item, draggable->fill_or_stroke);
 
1766
    }
 
1767
}
 
1768
 
 
1769
void
 
1770
GrDrag::selected_move_nowrite (double x, double y, bool scale_radial)
 
1771
{
 
1772
    selected_move (x, y, false, scale_radial);
 
1773
}
 
1774
 
 
1775
void
 
1776
GrDrag::selected_move (double x, double y, bool write_repr, bool scale_radial)
 
1777
{
 
1778
    if (selected == NULL)
 
1779
        return;
 
1780
 
 
1781
    bool did = false;
 
1782
 
 
1783
    for (GList *i = selected; i != NULL; i = i->next) {
 
1784
        GrDragger *d = (GrDragger *) i->data;
 
1785
 
 
1786
        if (!d->isA(POINT_LG_MID) && !d->isA(POINT_RG_MID1) && !d->isA(POINT_RG_MID2)) {
 
1787
            // if this is an endpoint,
 
1788
 
 
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,
 
1798
                                    POINT_RG_CENTER,
 
1799
                                    0,
 
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;
 
1803
                    }
 
1804
                }
 
1805
                if (skip_radius_with_center)
 
1806
                    continue;
 
1807
            }
 
1808
 
 
1809
            did = true;
 
1810
            d->point += Geom::Point (x, y);
 
1811
            d->point_original = d->point;
 
1812
            sp_knot_moveto (d->knot, d->point);
 
1813
 
 
1814
            d->fireDraggables (write_repr, scale_radial);
 
1815
 
 
1816
            d->updateDependencies(write_repr);
 
1817
        }
 
1818
    }
 
1819
 
 
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)"));
 
1824
        return;
 
1825
    }
 
1826
 
 
1827
    if (!did) { // none of the end draggers are selected, so let's try to move the mids
 
1828
 
 
1829
        GrDragger *dragger = (GrDragger *) selected->data;
 
1830
        // a midpoint dragger can (logically) only contain one GrDraggable
 
1831
        GrDraggable *draggable = (GrDraggable *) dragger->draggables->data;
 
1832
 
 
1833
        Geom::Point begin(0,0), end(0,0);
 
1834
        Geom::Point low_lim(0,0), high_lim(0,0);
 
1835
 
 
1836
        SPObject *server = draggable->getServer();
 
1837
        GSList *moving = NULL;
 
1838
        gr_midpoint_limits(dragger, server, &begin, &end, &low_lim, &high_lim, &moving);
 
1839
 
 
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;
 
1843
 
 
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);
 
1851
            did = true;
 
1852
        }
 
1853
 
 
1854
        g_slist_free(moving);
 
1855
 
 
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)"));
 
1860
        }
 
1861
    }
 
1862
}
 
1863
 
 
1864
void
 
1865
GrDrag::selected_move_screen (double x, double y)
 
1866
{
 
1867
    gdouble zoom = desktop->current_zoom();
 
1868
    gdouble zx = x / zoom;
 
1869
    gdouble zy = y / zoom;
 
1870
 
 
1871
    selected_move (zx, zy);
 
1872
}
 
1873
 
 
1874
/**
 
1875
Select the knot next to the last selected one and deselect all other selected.
 
1876
*/
 
1877
GrDragger *
 
1878
GrDrag::select_next ()
 
1879
{
 
1880
    GrDragger *d = NULL;
 
1881
    if (selected == NULL || g_list_find(draggers, selected->data)->next == NULL) {
 
1882
        if (draggers)
 
1883
            d = (GrDragger *) draggers->data;
 
1884
    } else {
 
1885
        d = (GrDragger *) g_list_find(draggers, selected->data)->next->data;
 
1886
    }
 
1887
    if (d)
 
1888
        setSelected (d);
 
1889
    return d;
 
1890
}
 
1891
 
 
1892
/**
 
1893
Select the knot previous from the last selected one and deselect all other selected.
 
1894
*/
 
1895
GrDragger *
 
1896
GrDrag::select_prev ()
 
1897
{
 
1898
    GrDragger *d = NULL;
 
1899
    if (selected == NULL || g_list_find(draggers, selected->data)->prev == NULL) {
 
1900
        if (draggers)
 
1901
            d = (GrDragger *) g_list_last (draggers)->data;
 
1902
    } else {
 
1903
        d = (GrDragger *) g_list_find(draggers, selected->data)->prev->data;
 
1904
    }
 
1905
    if (d)
 
1906
        setSelected (d);
 
1907
    return d;
 
1908
}
 
1909
 
 
1910
 
 
1911
// FIXME: i.m.o. an ugly function that I just made to work, but... aargh! (Johan)
 
1912
void
 
1913
GrDrag::deleteSelected (bool just_one)
 
1914
{
 
1915
    if (!selected) return;
 
1916
 
 
1917
    SPDocument *document = false;
 
1918
 
 
1919
    struct StructStopInfo {
 
1920
        SPStop * spstop;
 
1921
        GrDraggable * draggable;
 
1922
        SPGradient * gradient;
 
1923
        SPGradient * vector;
 
1924
    };
 
1925
 
 
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
 
1928
    while (selected) {
 
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);
 
1934
 
 
1935
            switch (draggable->point_type) {
 
1936
                case POINT_LG_MID:
 
1937
                case POINT_RG_MID1:
 
1938
                case POINT_RG_MID2:
 
1939
                    {
 
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 ) {
 
1945
                                present = true;
 
1946
                                break; // no need to search further.
 
1947
                            }
 
1948
                        }
 
1949
                        if (!present)
 
1950
                            midstoplist = g_slist_append(midstoplist, stop);
 
1951
                    }
 
1952
                    break;
 
1953
                case POINT_LG_BEGIN:
 
1954
                case POINT_LG_END:
 
1955
                case POINT_RG_CENTER:
 
1956
                case POINT_RG_R1:
 
1957
                case POINT_RG_R2:
 
1958
                    {
 
1959
                        SPStop *stop = NULL;
 
1960
                        if ( (draggable->point_type == POINT_LG_BEGIN) || (draggable->point_type == POINT_RG_CENTER) ) {
 
1961
                            stop = sp_first_stop(vector);
 
1962
                        } else {
 
1963
                            stop = sp_last_stop(vector);
 
1964
                        }
 
1965
                        if (stop) {
 
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 ) {
 
1975
                                    present = true;
 
1976
                                    break; // no need to search further.
 
1977
                                }
 
1978
                            }
 
1979
                            if (!present)
 
1980
                                endstoplist = g_slist_append(endstoplist, stopinfo);
 
1981
                        }
 
1982
                    }
 
1983
                    break;
 
1984
                default:
 
1985
                    break;
 
1986
            }
 
1987
        }
 
1988
        selected = g_list_remove(selected, dragger);
 
1989
        if ( just_one ) break; // iterate once if just_one is set.
 
1990
    }
 
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);
 
1997
    }
 
1998
    while (endstoplist) {
 
1999
        StructStopInfo *stopinfo  = (StructStopInfo*) endstoplist->data;
 
2000
        document = SP_OBJECT_DOCUMENT (stopinfo->spstop);
 
2001
 
 
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...
 
2005
        int len = 0;
 
2006
        for ( SPObject *child = sp_object_first_child(stopinfo->vector) ;
 
2007
              child != NULL ;
 
2008
              child = SP_OBJECT_NEXT(child) )
 
2009
        {
 
2010
            if ( SP_IS_STOP(child) )  len ++;
 
2011
        }
 
2012
        if (len > 2)
 
2013
        {
 
2014
            switch (stopinfo->draggable->point_type) {
 
2015
                case POINT_LG_BEGIN:
 
2016
                    {
 
2017
                        SP_OBJECT_REPR(stopinfo->vector)->removeChild(SP_OBJECT_REPR(stopinfo->spstop));
 
2018
 
 
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];
 
2027
 
 
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);
 
2031
                        stop->offset = 0;
 
2032
                        sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", 0);
 
2033
 
 
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);
 
2041
                        }
 
2042
                    }
 
2043
                    break;
 
2044
                case POINT_LG_END:
 
2045
                    {
 
2046
                        SP_OBJECT_REPR(stopinfo->vector)->removeChild(SP_OBJECT_REPR(stopinfo->spstop));
 
2047
 
 
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];
 
2056
 
 
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);
 
2062
 
 
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);
 
2070
                        }
 
2071
                    }
 
2072
                    break;
 
2073
                case POINT_RG_CENTER:
 
2074
                    {
 
2075
                        SPStop *newfirst = sp_next_stop (stopinfo->spstop);
 
2076
                        if (newfirst) {
 
2077
                            newfirst->offset = 0;
 
2078
                            sp_repr_set_css_double (SP_OBJECT_REPR (newfirst), "offset", 0);
 
2079
                        }
 
2080
                        SP_OBJECT_REPR(stopinfo->vector)->removeChild(SP_OBJECT_REPR(stopinfo->spstop));
 
2081
                    }
 
2082
                    break;
 
2083
                case POINT_RG_R1:
 
2084
                case POINT_RG_R2:
 
2085
                        SP_OBJECT_REPR(stopinfo->vector)->removeChild(SP_OBJECT_REPR(stopinfo->spstop));
 
2086
 
 
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;
 
2093
 
 
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);
 
2098
 
 
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);
 
2106
                        }
 
2107
                        break;
 
2108
            }
 
2109
        }
 
2110
        else
 
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 ();
 
2113
 
 
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();
 
2118
            }
 
2119
 
 
2120
            if (unselectedrepr == NULL) {
 
2121
                if (stopinfo->draggable->fill_or_stroke) {
 
2122
                    sp_repr_css_unset_property (css, "fill");
 
2123
                } else {
 
2124
                    sp_repr_css_unset_property (css, "stroke");
 
2125
                }
 
2126
            } else {
 
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"));
 
2131
                } else {
 
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"));
 
2134
                }
 
2135
                sp_repr_css_attr_unref (stopcss);
 
2136
            }
 
2137
 
 
2138
            sp_repr_css_change (SP_OBJECT_REPR (stopinfo->draggable->item), css, "style");
 
2139
            sp_repr_css_attr_unref (css);
 
2140
        }
 
2141
 
 
2142
        endstoplist = g_slist_remove(endstoplist, stopinfo);
 
2143
        delete stopinfo;
 
2144
    }
 
2145
 
 
2146
    if (document) {
 
2147
        sp_document_done ( document, SP_VERB_CONTEXT_GRADIENT, _("Delete gradient stop(s)") );
 
2148
    }
 
2149
}
 
2150
 
 
2151
 
 
2152
/*
 
2153
  Local Variables:
 
2154
  mode:c++
 
2155
  c-file-style:"stroustrup"
 
2156
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
2157
  indent-tabs-mode:nil
 
2158
  fill-column:99
 
2159
  End:
 
2160
*/
 
2161
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :