summaryrefslogtreecommitdiffstats
path: root/src/gradient-drag.h
blob: 86e1bde26d54a37c031853bf77447fa1e0a0a27e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef SEEN_GRADIENT_DRAG_H
#define SEEN_GRADIENT_DRAG_H

/*
 * On-canvas gradient dragging
 *
 * Authors:
 *   bulia byak <bulia@users.sf.net>
 *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
 *   Jon A. Cruz <jon@joncruz.org>
 *   Tavmjong Bah <tavmjong@free.fr>
 *
 * Copyright (C) 2012 Authors
 * Copyright (C) 2007 Johan Engelen
 * Copyright (C) 2005 Authors
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <cstddef>
#include <sigc++/sigc++.h>
#include <vector>
#include <set>
#include <gdk/gdk.h>
#include <glib.h>
#include <glibmm/ustring.h>

#include <2geom/point.h>

#include "object/sp-gradient.h" // TODO refactor enums to external .h file
#include "object/sp-mesh-array.h"

class SPKnot;

class SPDesktop;
class SPCSSAttr;
class SPLinearGradient;
class SPMeshGradient;
class SPItem;
class SPObject;
class SPRadialGradient;
class SPStop;

namespace Inkscape {
class Selection;
class CanvasItemCurve;
} // namespace Inkscape

/**
This class represents a single draggable point of a gradient. It remembers the item
which has the gradient, whether it's fill or stroke, the point type (from the
GrPointType enum), and the point number (needed if more than 2 stops are present).
*/
struct GrDraggable {
    GrDraggable(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke);
    virtual ~GrDraggable();

    SPItem *item;
    GrPointType point_type;
    gint point_i;  // the stop number of this point ( = 0 POINT_LG_BEGIN and POINT_RG_CENTER)
    Inkscape::PaintTarget fill_or_stroke;

    SPObject *getServer();

    bool mayMerge(GrDraggable *da2);

    inline int equals(GrDraggable *other) {
        return ((item == other->item) && (point_type == other->point_type) && (point_i == other->point_i) && (fill_or_stroke == other->fill_or_stroke));
    }
};

class GrDrag;

/**
This class holds together a visible on-canvas knot and a list of draggables that need to
be moved when the knot moves. Normally there's one draggable in the list, but there may
be more when draggers are snapped together.
*/
struct GrDragger {
    GrDragger(GrDrag *parent, Geom::Point p, GrDraggable *draggable);
    virtual ~GrDragger();

    GrDrag *parent;

    SPKnot *knot;

    // position of the knot, desktop coords
    Geom::Point point;
    // position of the knot before it began to drag; updated when released
    Geom::Point point_original;

    std::vector<GrDraggable *> draggables;

    void addDraggable(GrDraggable *draggable);

    void updateKnotShape();
    void updateTip();

    void select();
    void deselect();
    bool isSelected();

    /* Given one GrDraggable, these all update other draggables belonging to same GrDragger */
    void moveThisToDraggable(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke, bool write_repr);
    void moveOtherToDraggable(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke, bool write_repr);
    void updateMidstopDependencies(GrDraggable *draggable, bool write_repr);
    void updateDependencies(bool write_repr);

    /* Update handles/tensors when mesh corner moved */
    void moveMeshHandles( Geom::Point pc_old,  MeshNodeOperation op );

    /* Following are for highlighting mesh handles when corner node is selected. */
    GrDragger *getMgCorner();
    void highlightNode(SPMeshNode *node, bool highlight, Geom::Point corner_pos, int index);
    void highlightCorner(bool highlight);

    bool mayMerge(GrDragger *other);
    bool mayMerge(GrDraggable *da2);

    bool isA(GrPointType point_type);
    bool isA(SPItem *item, GrPointType point_type, Inkscape::PaintTarget fill_or_stroke);
    bool isA(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke);

    void fireDraggables(bool write_repr, bool scale_radial = false, bool merging_focus = false);

protected:
    void updateControlSizesOverload(SPKnot * knot);
    void updateControlSizes();

private:
    sigc::connection _moved_connection;
    sigc::connection _clicked_connection;
    sigc::connection _doubleclicked_connection;
    sigc::connection _mousedown_connection;
    sigc::connection _ungrabbed_connection;
};


/**
This is the root class of the gradient dragging machinery. It holds lists of GrDraggers
and of lines (simple canvas items). It also remembers one of the draggers as selected.
*/
class GrDrag {
public: // FIXME: make more of this private!

    GrDrag(SPDesktop *desktop);
    virtual ~GrDrag();

    bool isNonEmpty() {return !draggers.empty();}
    bool hasSelection() {return !selected.empty();}
    guint numSelected() {return selected.size();}
    guint numDraggers() {return draggers.size();}

    guint singleSelectedDraggerNumDraggables() {
        return (selected.empty()? 0 : (*(selected.begin()))->draggables.size() );
    }

    guint singleSelectedDraggerSingleDraggableType() {
        return (selected.empty() ? 0 : ((*(selected.begin()))->draggables[0]->point_type)); 
    }

    // especially the selection must be private, fix gradient-context to remove direct access to it
    std::set<GrDragger *> selected; // list of GrDragger*
    void setSelected(GrDragger *dragger, bool add_to_selection = false, bool override = true);
    void setDeselected(GrDragger *dragger);
    void deselectAll();
    void selectAll();
    void selectByCoords(std::vector<Geom::Point> coords);
    void selectByStop(SPStop *stop,  bool add_to_selection = true, bool override = true);
    void selectRect(Geom::Rect const &r);

    bool dropColor(SPItem *item, gchar const *c, Geom::Point p);

    SPStop *addStopNearPoint(SPItem *item, Geom::Point mouse_p, double tolerance);

    void deleteSelected(bool just_one = false);

    guint32 getColor();

    bool keep_selection;

    GrDragger *getDraggerFor(GrDraggable *d);
    GrDragger *getDraggerFor(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke);

    void grabKnot(GrDragger *dragger, gint x, gint y, guint32 etime);
    void grabKnot(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke, gint x, gint y, guint32 etime);

    bool local_change;

    SPDesktop *desktop;

    // lists of edges of selection bboxes, to snap draggers to
    std::vector<double> hor_levels;
    std::vector<double> vert_levels;

    std::vector<GrDragger *> draggers;
    std::vector<Inkscape::CanvasItemCurve *> item_curves;

    void updateDraggers();
    void refreshDraggers();
    void updateLines();
    void updateLevels();

    bool mouseOver();

    void selected_move_nowrite(double x, double y, bool scale_radial);
    void selected_move(double x, double y, bool write_repr = true, bool scale_radial = false);
    void selected_move_screen(double x, double y);

    bool key_press_handler(GdkEvent *event);

    GrDragger *select_next();
    GrDragger *select_prev();

    void selected_reverse_vector();

private:
    void deselect_all();

    void addLine( SPItem *item, Geom::Point p1, Geom::Point p2, Inkscape::PaintTarget fill_or_stroke);
    void addCurve(SPItem *item, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3,
                  int corner0, int corner1, int handle0, int handle1, Inkscape::PaintTarget fill_or_stroke);

    GrDragger *addDragger(GrDraggable *draggable);

    void addDraggersRadial(SPRadialGradient *rg, SPItem *item, Inkscape::PaintTarget fill_or_stroke);
    void addDraggersLinear(SPLinearGradient *lg, SPItem *item, Inkscape::PaintTarget fill_or_stroke);
    void addDraggersMesh(  SPMeshGradient   *mg, SPItem *item, Inkscape::PaintTarget fill_or_stroke);
    void refreshDraggersMesh(SPMeshGradient *mg, SPItem *item, Inkscape::PaintTarget fill_or_stroke);

    bool styleSet( const SPCSSAttr *css, bool switch_style);

    Glib::ustring makeStopSafeColor( gchar const *str, bool &isNull );

    Inkscape::Selection *selection;
    sigc::connection sel_changed_connection;
    sigc::connection sel_modified_connection;

    sigc::connection style_set_connection;
    sigc::connection style_query_connection;
};

#endif // SEEN_GRADIENT_DRAG_H
/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :