Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-guide.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Inkscape guideline implementation
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * Peter Moulder <pmoulder@mail.csse.monash.edu.au>
8 * Johan Engelen
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 2000-2002 authors
13 * Copyright (C) 2004 Monash University
14 * Copyright (C) 2007 Johan Engelen
15 *
16 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
17 */
18
19#include "sp-guide.h"
20
21#include <cstring>
22#include <vector>
23
24#include <glibmm/i18n.h>
25
26#include "attributes.h"
27#include "desktop-events.h"
28#include "document-undo.h"
29#include "page-manager.h"
30#include "sp-namedview.h"
31#include "sp-root.h"
32
33#include "colors/color.h"
34#include "colors/manager.h"
36#include "object/sp-page.h"
37#include "svg/svg.h"
39#include "util/units.h"
40#include "xml/repr.h"
41
43
44
46 : SPObject()
47 , label(nullptr)
48 , locked(false)
49 , normal_to_line(Geom::Point(0.,1.))
50 , point_on_line(Geom::Point(0.,0.))
51 , color(0x0086e599)
52 , hicolor(0xff00007f)
53{}
54
56{
57 this->color = color;
58 for (auto &view : views) {
59 view->set_stroke(color);
60 }
61}
62
64{
66
72
73 /* Register */
74 document->addResource("guide", this);
75}
76
78{
79 views.clear();
80
81 if (this->document) {
82 // Unregister ourselves
83 this->document->removeResource("guide", this);
84 }
85
87}
88
89void SPGuide::set(SPAttr key, const gchar *value) {
90 switch (key) {
92 if (auto c = Inkscape::Colors::Color::parse(value)) {
93 if (!c->hasOpacity())
94 c->addOpacity(0.5);
95 setColor(c->toRGBA());
96 }
97 break;
99 // this->label already freed in sp_guideline_set_label (src/display/guideline.cpp)
100 // see bug #1498444, bug #1469514
101 if (value) {
102 this->label = g_strdup(value);
103 } else {
104 this->label = nullptr;
105 }
106
107 this->set_label(this->label, false);
108 break;
110 if (value) {
111 this->set_locked(Inkscape::Util::read_bool(value, false), false);
112 }
113 break;
115 {
116 if (value && !strcmp(value, "horizontal")) {
117 /* Visual representation of a horizontal line, constrain vertically (y coordinate). */
118 this->normal_to_line = Geom::Point(0., 1.);
119 } else if (value && !strcmp(value, "vertical")) {
120 this->normal_to_line = Geom::Point(1., 0.);
121 } else if (value) {
122 gchar ** strarray = g_strsplit(value, ",", 2);
123 double newx, newy;
124 unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
125 success += sp_svg_number_read_d(strarray[1], &newy);
126 g_strfreev (strarray);
127 if (success == 2 && (fabs(newx) > 1e-6 || fabs(newy) > 1e-6)) {
128 Geom::Point direction(newx, newy);
129
130 // <sodipodi:guide> stores inverted y-axis coordinates
131 if (document->is_yaxisdown()) {
132 direction[Geom::X] *= -1.0;
133 }
134
135 direction.normalize();
136 this->normal_to_line = direction;
137 } else {
138 // default to vertical line for bad arguments
139 this->normal_to_line = Geom::Point(1., 0.);
140 }
141 } else {
142 // default to vertical line for bad arguments
143 this->normal_to_line = Geom::Point(1., 0.);
144 }
145 this->set_normal(this->normal_to_line, false);
146 }
147 break;
148 case SPAttr::POSITION:
149 {
150 if (value) {
151 gchar ** strarray = g_strsplit(value, ",", 2);
152 double newx, newy;
153 unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
154 success += sp_svg_number_read_d(strarray[1], &newy);
155 g_strfreev (strarray);
156 if (success == 2) {
157 // If root viewBox set, interpret guides in terms of viewBox (90/96)
159 if(root->viewBox_set && root->width && root->height) {
160 if(Geom::are_near((root->width.computed * root->viewBox.height()) / (root->viewBox.width() * root->height.computed), 1.0, Geom::EPSILON)) {
161 // for uniform scaling, try to reduce numerical error
162 double vbunit2px = (root->width.computed / root->viewBox.width() + root->height.computed / root->viewBox.height())/2.0;
163 newx = newx * vbunit2px;
164 newy = newy * vbunit2px;
165 } else {
166 newx = newx * root->width.computed / root->viewBox.width();
167 newy = newy * root->height.computed / root->viewBox.height();
168 }
169 }
170 this->point_on_line = Geom::Point(newx, newy);
171 } else if (success == 1) {
172 // before 0.46 style guideline definition.
173 const gchar *attr = this->getRepr()->attribute("orientation");
174 if (attr && !strcmp(attr, "horizontal")) {
175 this->point_on_line = Geom::Point(0, newx);
176 } else {
177 this->point_on_line = Geom::Point(newx, 0);
178 }
179 }
180
181 // <sodipodi:guide> stores inverted y-axis coordinates
182 if (document->is_yaxisdown()) {
184 }
185 } else {
186 // default to (0,0) for bad arguments
187 this->point_on_line = Geom::Point(0,0);
188 }
189 // update position in non-committing way
190 // fixme: perhaps we need to add an update method instead, and request_update here
191 this->moveto(this->point_on_line, false);
192 }
193 break;
194 default:
195 SPObject::set(key, value);
196 break;
197 }
198}
199
201 if (!document) return;
202
203 // refresh orientation and position taking current Y-axis orientation into account
206}
207
208/* Only used internally and in sp-line.cpp */
210{
211 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
212
213 Inkscape::XML::Node *repr = xml_doc->createElement("sodipodi:guide");
214
215 Geom::Point n = Geom::rot90(pt2 - pt1);
216
217 // If root viewBox set, interpret guides in terms of viewBox (90/96)
218 double newx = pt1.x();
219 double newy = pt1.y();
220
221 SPRoot *root = doc->getRoot();
222
223 // <sodipodi:guide> stores inverted y-axis coordinates
224 if (doc->is_yaxisdown()) {
225 newy = doc->getHeight().value("px") - newy;
226 n[Geom::X] *= -1.0;
227 }
228
229 if( root->viewBox_set ) {
230 // check to see if scaling is uniform
231 if(Geom::are_near((root->viewBox.width() * root->height.computed) / (root->width.computed * root->viewBox.height()), 1.0, Geom::EPSILON)) {
232 double px2vbunit = (root->viewBox.width()/root->width.computed + root->viewBox.height()/root->height.computed)/2.0;
233 newx = newx * px2vbunit;
234 newy = newy * px2vbunit;
235 } else {
236 newx = newx * root->viewBox.width() / root->width.computed;
237 newy = newy * root->viewBox.height() / root->height.computed;
238 }
239 }
240
241 repr->setAttributePoint("position", Geom::Point( newx, newy ));
242 repr->setAttributePoint("orientation", n);
243
244 SPNamedView *namedview = doc->getNamedView();
245 if (namedview) {
246 if (namedview->lockguides) {
247 repr->setAttribute("inkscape:locked", "true");
248 }
249 namedview->appendChild(repr);
250 }
252
253 auto guide = cast<SPGuide>(doc->getObjectByRepr(repr));
254 return guide;
255}
256
267
268void sp_guide_pt_pairs_to_guides(SPDocument *doc, std::list<std::pair<Geom::Point, Geom::Point> > &pts)
269{
270 for (auto & pt : pts) {
271 SPGuide::createSPGuide(doc, pt.first, pt.second);
272 }
273}
274
276{
277 std::list<std::pair<Geom::Point, Geom::Point> > pts;
278
280
281 pts.emplace_back(bounds.corner(0), bounds.corner(1));
282 pts.emplace_back(bounds.corner(1), bounds.corner(2));
283 pts.emplace_back(bounds.corner(2), bounds.corner(3));
284 pts.emplace_back(bounds.corner(3), bounds.corner(0));
285
287 DocumentUndo::done(doc, _("Create Guides Around the Current Page"), "");
288}
289
291{
292 std::vector<SPObject *> current = doc->getResourceList("guide");
293 while (!current.empty()){
294 auto guide = cast<SPGuide>(*(current.begin()));
295 guide->remove(true);
296 current = doc->getResourceList("guide");
297 }
298
299 DocumentUndo::done(doc, _("Delete All Guides"),"");
300}
301
302// Actually, create a new guide.
304{
305 Glib::ustring ulabel = (label?label:"");
307 item->set_stroke(color);
308 item->set_locked(locked);
309
310 item->connect_event(sigc::bind(sigc::ptr_fun(&sp_dt_guide_event), item, this));
311
312 // Ensure event forwarding by the guide handle ("the dot") to the corresponding line
313 auto dot = item->dot();
314 auto dot_handler = [=, this] (Inkscape::CanvasEvent const &ev) { return sp_dt_guide_event(ev, item, this); };
315 dot->connect_event(dot_handler);
316
317 views.emplace_back(item);
318}
319
321{
322 for (auto &view : views) {
323 view->set_visible(true);
324 }
325}
326
327// Actually deleted guide from a particular canvas.
329{
330 g_assert(canvas != nullptr);
331 for (auto it = views.begin(); it != views.end(); ++it) {
332 if (canvas == (*it)->get_canvas()) { // A guide can be displayed on more than one desktop with the same document.
333 views.erase(it);
334 return;
335 }
336 }
337
338 assert(false);
339}
340
342{
343 for (auto &view : views) {
344 view->set_visible(false);
345 }
346}
347
349{
350 g_assert(canvas != nullptr);
351
352 for (auto &view : views) {
353 if (canvas == view->get_canvas()) {
354 view->set_pickable(sensitive);
355 return;
356 }
357 }
358
359 assert(false);
360}
361
367void SPGuide::moveto(Geom::Point const point_on_line, bool const commit)
368{
369 if(this->locked) {
370 return;
371 }
372
373 for (auto &view : views) {
374 view->set_origin(point_on_line);
375 }
376
377 /* Calling Inkscape::XML::Node::setAttributePoint must precede calling sp_item_notify_moveto in the commit
378 case, so that the guide's new position is available for sp_item_rm_unsatisfied_cns. */
379 if (commit) {
380 // If root viewBox set, interpret guides in terms of viewBox (90/96)
381 double newx = point_on_line.x();
382 double newy = point_on_line.y();
383
384 // <sodipodi:guide> stores inverted y-axis coordinates
385 if (document->is_yaxisdown()) {
386 newy = document->getHeight().value("px") - newy;
387 }
388
390 if( root->viewBox_set ) {
391 // check to see if scaling is uniform
392 if(Geom::are_near((root->viewBox.width() * root->height.computed) / (root->width.computed * root->viewBox.height()), 1.0, Geom::EPSILON)) {
393 double px2vbunit = (root->viewBox.width()/root->width.computed + root->viewBox.height()/root->height.computed)/2.0;
394 newx = newx * px2vbunit;
395 newy = newy * px2vbunit;
396 } else {
397 newx = newx * root->viewBox.width() / root->width.computed;
398 newy = newy * root->viewBox.height() / root->height.computed;
399 }
400 }
401
402 //XML Tree being used here directly while it shouldn't be.
403 getRepr()->setAttributePoint("position", Geom::Point(newx, newy) );
404 }
405}
406
412void SPGuide::set_normal(Geom::Point const normal_to_line, bool const commit)
413{
414 if(this->locked) {
415 return;
416 }
417 for (auto &view : views) {
418 view->set_normal(normal_to_line);
419 }
420
421 /* Calling sp_repr_set_svg_point must precede calling sp_item_notify_moveto in the commit
422 case, so that the guide's new position is available for sp_item_rm_unsatisfied_cns. */
423 if (commit) {
424 //XML Tree being used directly while it shouldn't be
425 auto normal = normal_to_line;
426
427 // <sodipodi:guide> stores inverted y-axis coordinates
428 if (document->is_yaxisdown()) {
429 normal[Geom::X] *= -1.0;
430 }
431
432 getRepr()->setAttributePoint("orientation", normal);
433 }
434}
435
436void SPGuide::set_color(const unsigned r, const unsigned g, const unsigned b, bool const commit)
437{
438 this->color = (r << 24) | (g << 16) | (b << 8) | 0x7f;
439
440 if (! views.empty()) {
441 views[0]->set_stroke(color);
442 }
443
444 if (commit) {
445 std::ostringstream os;
446 os << "rgb(" << r << "," << g << "," << b << ")";
447 //XML Tree being used directly while it shouldn't be
448 setAttribute("inkscape:color", os.str());
449 }
450}
451
452void SPGuide::set_locked(const bool locked, bool const commit)
453{
454 this->locked = locked;
455 if ( !views.empty() ) {
456 views[0]->set_locked(locked);
457 }
458
459 if (commit) {
460 setAttribute("inkscape:locked", locked ? "true" : "false");
461 }
462}
463
464void SPGuide::set_label(const char* label, bool const commit)
465{
466 if (!views.empty()) {
467 views[0]->set_label(label ? label : "");
468 }
469
470 if (commit) {
471 //XML Tree being used directly while it shouldn't be
472 setAttribute("inkscape:label", label);
473 }
474}
475
482char* SPGuide::description(bool const verbose) const
483{
484 using Geom::X;
485 using Geom::Y;
486
487 char *descr = nullptr;
488 if ( !this->document ) {
489 // Guide has probably been deleted and no longer has an attached namedview.
490 descr = g_strdup(_("Deleted"));
491 } else {
492 SPNamedView *namedview = this->document->getNamedView();
493
496 Glib::ustring position_string_x = x_q.string(namedview->display_units);
497 Glib::ustring position_string_y = y_q.string(namedview->display_units);
498
499 gchar *shortcuts =
500 g_strdup_printf("; %s", _("<b>Shift+drag</b> to rotate, <b>Ctrl+drag</b> to move origin, <b>Del</b> to "
501 "delete; <b>double-click</b> to edit this guide's properties"));
502
503 if ( are_near(this->normal_to_line, Geom::Point(1., 0.)) ||
504 are_near(this->normal_to_line, -Geom::Point(1., 0.)) ) {
505 descr = g_strdup_printf(_("vertical, at %s"), position_string_x.c_str());
506 } else if ( are_near(this->normal_to_line, Geom::Point(0., 1.)) ||
507 are_near(this->normal_to_line, -Geom::Point(0., 1.)) ) {
508 descr = g_strdup_printf(_("horizontal, at %s"), position_string_y.c_str());
509 } else {
510 double const radians = this->angle();
511 double const degrees = Geom::deg_from_rad(radians);
512 int const degrees_int = (int) round(degrees);
513 descr = g_strdup_printf(_("at %d degrees, through (%s,%s)"),
514 degrees_int, position_string_x.c_str(), position_string_y.c_str());
515 }
516
517 if (verbose) {
518 gchar *oldDescr = descr;
519 descr = g_strconcat(oldDescr, shortcuts, nullptr);
520 g_free(oldDescr);
521 }
522
523 g_free(shortcuts);
524 }
525
526 return descr;
527}
528
529bool SPGuide::remove(bool force)
530{
531 if (this->locked && !force)
532 return false;
533
534 //XML Tree being used directly while it shouldn't be.
535 sp_repr_unparent(this->getRepr());
536
537 return true;
538}
539
540/*
541 Local Variables:
542 mode:c++
543 c-file-style:"stroustrup"
544 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
545 indent-tabs-mode:nil
546 fill-column:99
547 End:
548*/
549// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
pair< double, double > Point
Definition parser.cpp:7
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ ORIENTATION
@ INKSCAPE_LABEL
@ INKSCAPE_COLOR
@ INKSCAPE_LOCKED
Geom::IntRect bounds
Definition canvas.cpp:182
C width() const
Get the horizontal extent of the rectangle.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Two-dimensional point that doubles as a vector.
Definition point.h:66
void normalize()
Normalize the vector representing the point.
Definition point.cpp:96
constexpr Coord y() const noexcept
Definition point.h:106
constexpr Coord x() const noexcept
Definition point.h:104
Axis aligned, non-empty rectangle.
Definition rect.h:92
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
Geom::Rect getSelectedPageRect() const
Returns the selected page rect, OR the viewbox rect.
A widget for Inkscape's canvas.
Definition canvas.h:62
Glib::ustring string(Unit const *u) const
Return a printable string of the value in the specified unit.
Definition units.cpp:578
double value(Unit const *u) const
Return the quantity's value in the specified unit.
Definition units.cpp:565
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
bool setAttributePoint(Util::const_char_ptr key, Geom::Point const &val)
Definition node.cpp:137
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
SVGLength width
Typed SVG document implementation.
Definition document.h:101
bool removeResource(char const *key, SPObject *object)
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
bool addResource(char const *key, SPObject *object)
std::vector< SPObject * > const getResourceList(char const *key)
Inkscape::PageManager & getPageManager()
Definition document.h:162
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:211
bool is_yaxisdown() const
True if the desktop Y-axis points down, false if it points up.
Definition document.h:274
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
SPNamedView * getNamedView()
Get the namedview for this document, creates it if it's not found.
Definition document.cpp:235
Inkscape::Util::Quantity getHeight() const
Definition document.cpp:887
void set_locked(const bool locked, bool const commit)
Definition sp-guide.cpp:452
void fix_orientation()
Definition sp-guide.cpp:200
char * description(bool const verbose=true) const
Returns a human-readable description of the guideline for use in dialog boxes and status bar.
Definition sp-guide.cpp:482
double angle() const
Definition sp-guide.h:84
void sensitize(Inkscape::UI::Widget::Canvas *canvas, bool sensitive)
Definition sp-guide.cpp:348
void build(SPDocument *doc, Inkscape::XML::Node *repr) override
Definition sp-guide.cpp:63
void set_color(const unsigned r, const unsigned g, const unsigned b, bool const commit)
Definition sp-guide.cpp:436
void setColor(guint32 c)
Definition sp-guide.cpp:55
void set(SPAttr key, const char *value) override
Definition sp-guide.cpp:89
bool locked
Definition sp-guide.h:93
void moveto(Geom::Point const point_on_line, bool const commit)
Definition sp-guide.cpp:367
Geom::Point point_on_line
Definition sp-guide.h:95
guint32 color
Definition sp-guide.h:97
Geom::Point normal_to_line
Definition sp-guide.h:94
std::vector< CanvasItemPtr< Inkscape::CanvasItemGuideLine > > views
Definition sp-guide.h:92
void set_normal(Geom::Point const normal_to_line, bool const commit)
Definition sp-guide.cpp:412
static SPGuide * createSPGuide(SPDocument *doc, Geom::Point const &pt1, Geom::Point const &pt2)
Definition sp-guide.cpp:209
SPGuide * duplicate()
Definition sp-guide.cpp:257
void hideSPGuide()
Definition sp-guide.cpp:341
char * label
Definition sp-guide.h:91
bool remove(bool force=false)
Definition sp-guide.cpp:529
void showSPGuide()
Definition sp-guide.cpp:320
void set_label(const char *label, bool const commit)
Definition sp-guide.cpp:464
void release() override
Definition sp-guide.cpp:77
SVGBool lockguides
Inkscape::Util::Unit const * display_units
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Inkscape::XML::Node * repr
Definition sp-object.h:193
void appendChild(Inkscape::XML::Node *child)
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
SPDocument * document
Definition sp-object.h:188
virtual void set(SPAttr key, const char *value)
virtual void release()
void readAttr(char const *key)
Read value of key attribute from XML node into object.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
virtual void build(SPDocument *doc, Inkscape::XML::Node *repr)
<svg> element
Definition sp-root.h:31
Geom::Rect viewBox
Definition viewbox.h:35
float computed
Definition svg-length.h:50
RootCluster root
Utility functions to convert ascii representations to numbers.
double c[8][4]
bool sp_dt_guide_event(Inkscape::CanvasEvent const &event, Inkscape::CanvasItemGuideLine *guide_item, SPGuide *guide)
static char const *const current
Definition dir-util.cpp:71
TODO: insert short description here.
constexpr Coord EPSILON
Default "acceptably small" value.
Definition coord.h:84
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
Glib::ustring label
Various utility functions.
Definition affine.h:22
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
static R & release(R &r)
Decrements the reference count of a anchored object.
bool read_bool(gchar const *value, bool default_value)
Definition converters.h:61
static cairo_user_data_key_t key
C facade to Inkscape::XML::Node.
void sp_repr_unparent(Inkscape::XML::Node *repr)
Remove repr from children of its parent node.
Definition repr.h:107
void sp_guide_create_guides_around_page(SPDocument *doc)
Definition sp-guide.cpp:275
void sp_guide_pt_pairs_to_guides(SPDocument *doc, std::list< std::pair< Geom::Point, Geom::Point > > &pts)
Definition sp-guide.cpp:268
void sp_guide_delete_all_guides(SPDocument *doc)
Definition sp-guide.cpp:290
SPGuide – a guideline.
unsigned int guint32
Definition sp-guide.h:23
SPPage – a page object.
SPRoot: SVG <svg> implementation.
Abstract base class for events.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
unsigned int sp_svg_number_read_d(gchar const *str, double *val)
void dot(Cairo::RefPtr< Cairo::Context > &cr, double x, double y)