Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
star-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Star drawing context
4 *
5 * Authors:
6 * Mitsuru Oka <oka326@parkcity.ne.jp>
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 1999-2002 Lauris Kaplinski
13 * Copyright (C) 2001-2002 Mitsuru Oka
14 *
15 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 */
17
18#include <cstring>
19#include <string>
20
21#include <gdk/gdkkeysyms.h>
22#include <glibmm/i18n.h>
23
24#include "star-tool.h"
25
26#include "context-fns.h"
27#include "desktop-style.h"
28#include "desktop.h"
29#include "document-undo.h"
30#include "document.h"
31#include "message-context.h"
32#include "selection.h"
33
34#include "object/sp-namedview.h"
35#include "object/sp-star.h"
36
37#include "ui/icon-names.h"
38#include "ui/shape-editor.h"
40
41#include "util/units.h"
42
44
45namespace Inkscape {
46namespace UI {
47namespace Tools {
48
50 : ToolBase(desktop, "/tools/shapes/star", "star.svg")
51{
52 sp_event_context_read(this, "isflatsided");
53 sp_event_context_read(this, "magnitude");
54 sp_event_context_read(this, "proportion");
55 sp_event_context_read(this, "rounded");
56 sp_event_context_read(this, "randomized");
57
58 this->shape_editor = new ShapeEditor(desktop);
59
61 if (item) {
62 this->shape_editor->set_item(item);
63 }
64
66
67 this->sel_changed_connection.disconnect();
68
69 this->sel_changed_connection = selection->connectChanged(sigc::mem_fun(*this, &StarTool::selection_changed));
70
72 if (prefs->getBool("/tools/shapes/selcue")) {
73 this->enableSelectionCue();
74 }
75
76 if (prefs->getBool("/tools/shapes/gradientdrag")) {
77 this->enableGrDrag();
78 }
79}
80
83
84 this->finishItem();
85 this->sel_changed_connection.disconnect();
86
87 this->enableGrDrag(false);
88
89 delete this->shape_editor;
90 this->shape_editor = nullptr;
91
92 /* fixme: This is necessary because we do not grab */
93 if (this->star) {
94 this->finishItem();
95 }
96}
97
105 g_assert (selection != nullptr);
106
107 this->shape_editor->unset_item();
108 this->shape_editor->set_item(selection->singleItem());
109}
110
111
113 Glib::ustring path = val.getEntryName();
114
115 if (path == "magnitude") {
116 this->magnitude = CLAMP(val.getInt(5), this->isflatsided ? 3 : 2, 1024);
117 } else if (path == "proportion") {
118 this->proportion = CLAMP(val.getDouble(0.5), 0.01, 2.0);
119 } else if (path == "isflatsided") {
120 this->isflatsided = val.getBool();
121 } else if (path == "rounded") {
122 this->rounded = val.getDouble();
123 } else if (path == "randomized") {
124 this->randomized = val.getDouble();
125 }
126}
127
129{
130 auto selection = _desktop->getSelection();
131 auto prefs = Inkscape::Preferences::get();
132
133 tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
134
135 bool ret = false;
136
137 inspect_event(event,
138 [&] (ButtonPressEvent const &event) {
139 if (event.num_press == 1 && event.button == 1) {
140 dragging = true;
141
142 setup_for_drag_start(event);
143 center = _desktop->w2d(event.pos);
144
145 // Snap center.
146 auto &m = _desktop->getNamedView()->snap_manager;
147 m.setup(_desktop);
148 m.freeSnapReturnByRef(center, SNAPSOURCE_NODE_HANDLE);
149
150 grabCanvasEvents();
151
152 ret = true;
153 m.unSetup();
154 }
155 },
156 [&] (MotionEvent const &event) {
157 if (dragging && (event.modifiers & GDK_BUTTON1_MASK)) {
158 if (!checkDragMoved(event.pos)) {
159 return;
160 }
161
162 auto const motion_dt = _desktop->w2d(event.pos);
163 drag(motion_dt, event.modifiers);
164
165 gobble_motion_events(GDK_BUTTON1_MASK);
166
167 ret = true;
168 } else if (!sp_event_context_knot_mouseover()) {
169 auto &m = _desktop->getNamedView()->snap_manager;
170 m.setup(_desktop);
171
172 auto const motion_dt = _desktop->w2d(event.pos);
173 m.preSnap(SnapCandidatePoint(motion_dt, SNAPSOURCE_NODE_HANDLE));
174 m.unSetup();
175 }
176 },
177 [&] (ButtonReleaseEvent const &event) {
178 xyp = {};
179 if (dragging && event.button == 1) {
180 dragging = false;
182
183 if (star) {
184 // We've been dragging, finish the star.
185 finishItem();
186 } else if (item_to_select) {
187 // No dragging, select clicked item if any.
188 if (event.modifiers & GDK_SHIFT_MASK) {
189 selection->toggle(item_to_select);
190 } else if (!selection->includes(item_to_select)) {
191 selection->set(item_to_select);
192 }
193 } else {
194 // Click in an empty space.
195 selection->clear();
196 }
197
198 item_to_select = nullptr;
199 ret = true;
200 }
202 },
203 [&] (KeyPressEvent const &event) {
204 switch (get_latin_keyval(event)) {
205 case GDK_KEY_Alt_L:
206 case GDK_KEY_Alt_R:
207 case GDK_KEY_Control_L:
208 case GDK_KEY_Control_R:
209 case GDK_KEY_Shift_L:
210 case GDK_KEY_Shift_R:
211 case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt (at least on my machine)
212 case GDK_KEY_Meta_R:
214 _("<b>Ctrl</b>: snap angle; keep rays radial"),
215 nullptr,
216 nullptr);
217 break;
218
219 case GDK_KEY_x:
220 case GDK_KEY_X:
221 if (mod_alt_only(event)) {
222 _desktop->setToolboxFocusTo("altx-star");
223 ret = true;
224 }
225 break;
226
227 case GDK_KEY_Escape:
228 if (dragging) {
229 dragging = false;
231 // If drawing, cancel, otherwise pass it up for deselecting.
232 cancel();
233 ret = true;
234 }
235 break;
236
237 case GDK_KEY_space:
238 if (dragging) {
240
241 dragging = false;
242
244
245 if (!within_tolerance) {
246 // We've been dragging, finish the star.
247 finishItem();
248 }
249 // Do not return true, so that space would work switching to selector.
250 }
251 break;
252
253 case GDK_KEY_Delete:
254 case GDK_KEY_KP_Delete:
255 case GDK_KEY_BackSpace:
256 ret = deleteSelectedDrag(mod_ctrl_only(event));
257 break;
258
259 default:
260 break;
261 }
262 },
263 [&] (KeyReleaseEvent const &event) {
264 switch (event.keyval) {
265 case GDK_KEY_Alt_L:
266 case GDK_KEY_Alt_R:
267 case GDK_KEY_Control_L:
268 case GDK_KEY_Control_R:
269 case GDK_KEY_Shift_L:
270 case GDK_KEY_Shift_R:
271 case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt
272 case GDK_KEY_Meta_R:
274 break;
275
276 default:
277 break;
278 }
279 },
280 [&] (CanvasEvent const &event) {}
281 );
282
283 return ret || ToolBase::root_handler(event);
284}
285
286void StarTool::drag(Geom::Point p, unsigned state)
287{
289 int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
290
291 if (!this->star) {
293 return;
294 }
295
297
298 // Create object
300 Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
301 repr->setAttribute("sodipodi:type", "star");
302
303 // Set style
304 sp_desktop_apply_style_tool(_desktop, repr, "/tools/shapes/star", false);
305
306 this->star = cast<SPStar>(currentLayer()->appendChildRepr(repr));
308 this->star->updateRepr();
309
310 // Adjust stroke width to cope with parent's transform
312 }
313
314 /* Snap corner point with no constraints */
316
317 m.setup(_desktop, true, star.get());
318 Geom::Point pt2g = p;
320 m.unSetup();
321
322 Geom::Point const p0 = _desktop->dt2doc(this->center) * _tr;
323 Geom::Point const p1 = _desktop->dt2doc(pt2g) * _tr;
324
325 double const sides = (gdouble) this->magnitude;
326 Geom::Point const d = p1 - p0;
327 Geom::Coord const r1 = Geom::L2(d);
328 double arg1 = atan2(d);
329
330 if (state & GDK_CONTROL_MASK) {
331 /* Snap angle */
332 double snaps_radian = M_PI/snaps;
333 arg1 = std::round(arg1/snaps_radian) * snaps_radian;
334 }
335
336 sp_star_position_set(star.get(), this->magnitude, p0, r1, r1 * this->proportion,
337 arg1, arg1 + M_PI / sides, this->isflatsided, this->rounded, this->randomized);
338
339 /* status text */
341 Glib::ustring rads = q.string(_desktop->getNamedView()->display_units);
343 ( this->isflatsided?
344 _("<b>Polygon</b>: radius %s, angle %.2f&#176;; with <b>Ctrl</b> to snap angle") :
345 _("<b>Star</b>: radius %s, angle %.2f&#176;; with <b>Ctrl</b> to snap angle") ),
346 rads.c_str(), arg1 * 180 / M_PI);
347}
348
350 this->message_context->clear();
351
352 if (star) {
353 if (this->star->r[1] == 0) {
354 // Don't allow the creating of zero sized arc, for example
355 // when the start and and point snap to the snap grid point
356 this->cancel();
357 return;
358 }
359
360 // Set the *transform* center, so select tool rotations work from the star's
361 // center instead of the bbox center for odd spoked stars.
362 this->star->setCenter(this->center);
363 this->star->set_shape();
364 this->star->updateRepr(SP_OBJECT_WRITE_EXT);
365
366 // update while creating inside a LPE group
367 sp_lpe_item_update_patheffect(this->star.get(), true, true);
369 DocumentUndo::done(_desktop->getDocument(), _("Create star"), INKSCAPE_ICON("draw-polygon-star"));
370
371 this->star = nullptr;
372 }
373}
374
378
379 if (star) {
381 }
382
383 this->within_tolerance = false;
384 this->xyp = {};
385 this->item_to_select = nullptr;
386
388}
389
390}
391}
392}
393
394/*
395 Local Variables:
396 mode:c++
397 c-file-style:"stroustrup"
398 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
399 indent-tabs-mode:nil
400 fill-column:99
401 End:
402*/
403// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Two-dimensional point that doubles as a vector.
Definition point.h:66
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static void cancel(SPDocument *document)
void clear()
removes our current message from the stack
void clear()
Unselects all selected objects.
SPItem * singleItem()
Returns a single selected item.
Data type representing a typeless value of a preference.
double getDouble(double def=0.0, Glib::ustring const &unit="") const
Interpret the preference as a floating point value.
Glib::ustring getEntryName() const
Get the last component of the preference's path.
bool getBool(bool def=false) const
Interpret the preference as a Boolean value.
int getInt(int def=0) const
Interpret the preference as an integer.
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
T * get() const
Definition weakptr.h:26
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void set(XML::Node *repr)
Set the selection to an XML node's SPObject.
Definition selection.h:118
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
Class to store data for points which are snap candidates, either as a source or as a target.
void set_item(SPItem *item)
void unset_item(bool keep_knotholder=false)
void drag(Geom::Point p, unsigned state)
SPWeakPtr< SPStar > star
Definition star-tool.h:42
void set(Preferences::Entry const &val) override
Called by our pref_observer if a preference has been changed.
StarTool(SPDesktop *desktop)
Definition star-tool.cpp:49
bool root_handler(CanvasEvent const &event) override
sigc::connection sel_changed_connection
Definition star-tool.h:64
void selection_changed(Selection *selection)
Callback that processes the "changed" signal on the selection; destroys old and creates new knotholde...
Base class for Event processors.
Definition tool-base.h:107
bool sp_event_context_knot_mouseover() const
Returns true if we're hovering above a knot (needed because we don't want to pre-snap in that case).
void ungrabCanvasEvents()
Ungrab events from the Canvas Catchall.
SPGroup * currentLayer() const
bool within_tolerance
are we still within tolerance of origin
Definition tool-base.h:148
bool dragging
are we dragging?
Definition tool-base.h:146
std::unique_ptr< MessageContext > message_context
Definition tool-base.h:193
SPItem * item_to_select
the item where mouse_press occurred, to be selected if this is a click not drag
Definition tool-base.h:152
Geom::IntPoint xyp
where drag started
Definition tool-base.h:145
virtual bool root_handler(CanvasEvent const &event)
bool checkDragMoved(Geom::Point const &pos)
Analyse the current position and return true once it has moved farther than tolerance from the drag o...
void enableGrDrag(bool enable=true)
void enableSelectionCue(bool enable=true)
Enables/disables the ToolBase's SelCue.
void discard_delayed_snap_event()
If a delayed snap event has been scheduled, this function will cancel it.
MessageContext * defaultMessageContext() const
Definition tool-base.h:123
bool deleteSelectedDrag(bool just_one)
Delete a selected GrDrag point.
Glib::ustring string(Unit const *u) const
Return a printable string of the value in the specified unit.
Definition units.cpp:515
Interface for refcounted XML nodes.
Definition node.h:80
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
void setToolboxFocusTo(char const *label)
Definition desktop.cpp:1128
Geom::Affine const & dt2doc() const
Definition desktop.cpp:1343
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::Selection * getSelection() const
Definition desktop.h:188
SPDocument * doc() const
Definition desktop.h:159
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Definition desktop.h:416
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
Base class for visual SVG elements.
Definition sp-item.h:109
void adjust_stroke_width_recursive(double ex)
Recursively scale stroke width in item and its children by expansion.
Definition sp-item.cpp:1522
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1823
void setCenter(Geom::Point const &object_centre)
Sets the transform_center_x and transform_center_y properties to retain the rotation center.
Definition sp-item.cpp:338
SnapManager snap_manager
Inkscape::Util::Unit const * display_units
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
void set_shape() override
Definition sp-star.cpp:357
double r[2]
Definition sp-star.h:34
Class to coordinate snapping operations.
Definition snap.h:80
void setup(SPDesktop const *desktop, bool snapindicator=true, SPObject const *item_to_ignore=nullptr, std::vector< Inkscape::SnapCandidatePoint > *unselected_nodes=nullptr)
Convenience shortcut when there is only one item to ignore.
Definition snap.cpp:663
void freeSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, Geom::OptRect const &bbox_to_snap=Geom::OptRect()) const
Try to snap a point to grids, guides or objects.
Definition snap.cpp:135
void unSetup()
Definition snap.h:147
void sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool_path, bool with_text)
Apply the desktop's current style or the tool style to repr.
Editable view implementation.
TODO: insert short description here.
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
Macro for icon names used in Inkscape.
SPItem * item
Interface for locally managing a current status message.
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
static R & release(R &r)
Decrements the reference count of a anchored object.
void sp_event_show_modifier_tip(MessageContext *message_context, KeyEvent const &event, char const *ctrl_tip, char const *shift_tip, char const *alt_tip)
Show tool context specific modifier tip.
void gobble_motion_events(unsigned mask)
Definition tool-base.h:244
unsigned get_latin_keyval(GtkEventControllerKey const *const controller, unsigned const keyval, unsigned const keycode, GdkModifierType const state, unsigned *consumed_modifiers)
Return the keyval corresponding to the event controller key in Latin group.
void sp_event_context_read(ToolBase *tool, char const *key)
Calls virtual set() function of ToolBase.
Helper class to stream background task notifications as a series of messages.
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
@ SNAPSOURCE_NODE_HANDLE
Definition snap-enums.h:43
bool mod_ctrl_only(unsigned modifiers)
bool mod_alt_only(unsigned modifiers)
@ IMMEDIATE_MESSAGE
Definition message.h:27
bool have_viable_layer(SPDesktop *desktop, MessageContext *message)
Check to see if the current layer is both unhidden and unlocked.
Inkscape::ShapeEditor This is a container class which contains a knotholder for shapes.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
void sp_star_position_set(SPStar *star, gint sides, Geom::Point center, gdouble r1, gdouble r2, gdouble arg1, gdouble arg2, bool isflat, double rounded, double randomized)
Definition sp-star.cpp:432
A mouse button (left/right/middle) is pressed.
A mouse button (left/right/middle) is released.
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
A key has been pressed.
A key has been released.
Movement of the mouse pointer.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
SPDesktop * desktop