git.s-ol.nu inkscape / 87477c5
add livecoding tool to toolbar s-ol 3 years ago
10 changed file(s) with 1602 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
55 <toolitem action='ToolTweak' />
66 <toolitem action='ToolZoom' />
77 <toolitem action='ToolMeasure' />
8 <toolitem action='ToolLivecode' />
89
910 <!-- Shapes -->
1011 <toolitem action='ToolRect' />
8585 tools/tool-base.cpp
8686 tools/tweak-tool.cpp
8787 tools/zoom-tool.cpp
88 tools/livecode-tool.cpp
8889
8990 dialog/aboutbox.cpp
9091 dialog/align-and-distribute.cpp
3131 #include "ui/tools/text-tool.h"
3232 #include "ui/tools/tweak-tool.h"
3333 #include "ui/tools/zoom-tool.h"
34 #include "ui/tools/livecode-tool.h"
3435
3536 using namespace Inkscape::UI::Tools;
3637
8283 tool = new TweakTool;
8384 else if (id == "/tools/zoom")
8485 tool = new ZoomTool;
86 else if (id == "/tools/livecode")
87 tool = new LivecodeTool;
8588 else
8689 fprintf(stderr, "WARNING: unknown tool: %s", id.c_str());
8790
0 // SPDX-License-Identifier: GPL-2.0-or-later
1 /*
2 * Our nice measuring tool
3 *
4 * Authors:
5 * Felipe Correa da Silva Sanches <juca@members.fsf.org>
6 * Jon A. Cruz <jon@joncruz.org>
7 * Jabiertxo Arraiza <jabier.arraiza@marker.es>
8 *
9 * Copyright (C) 2011 Authors
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14 #include <gtkmm.h>
15 #include <glibmm/i18n.h>
16
17 #include <boost/none_t.hpp>
18
19 #include <2geom/line.h>
20 #include <2geom/path-intersection.h>
21
22 #include "desktop-style.h"
23 #include "desktop.h"
24 #include "document-undo.h"
25 #include "inkscape.h"
26 #include "path-chemistry.h"
27 #include "rubberband.h"
28 #include "text-editing.h"
29 #include "verbs.h"
30
31 #include "display/curve.h"
32 #include "display/sodipodi-ctrl.h"
33 #include "display/sp-canvas-util.h"
34 #include "display/sp-canvas.h"
35 #include "display/sp-ctrlcurve.h"
36 #include "display/sp-ctrlline.h"
37
38 #include "object/sp-defs.h"
39 #include "object/sp-flowtext.h"
40 #include "object/sp-namedview.h"
41 #include "object/sp-root.h"
42 #include "object/sp-shape.h"
43 #include "object/sp-text.h"
44
45 #include "ui/pixmaps/cursor-measure.xpm"
46
47 #include "svg/stringstream.h"
48 #include "svg/svg-color.h"
49 #include "svg/svg.h"
50
51 #include "ui/dialog/knot-properties.h"
52 #include "ui/tools/freehand-base.h"
53 #include "ui/tools/livecode-tool.h"
54
55 #include "util/units.h"
56
57 using Inkscape::ControlManager;
58 using Inkscape::CTLINE_SECONDARY;
59 using Inkscape::Util::unit_table;
60 using Inkscape::DocumentUndo;
61
62 #define MT_KNOT_COLOR_NORMAL 0xffffff00
63 #define MT_KNOT_COLOR_MOUSEOVER 0xff000000
64
65
66 namespace Inkscape {
67 namespace UI {
68 namespace Tools {
69
70 const std::string& LivecodeTool::getPrefsPath()
71 {
72 return LivecodeTool::prefsPath;
73 }
74
75 const std::string LivecodeTool::prefsPath = "/tools/livecode";
76
77 namespace {
78
79 /**
80 * Simple class to use for removing label overlap.
81 */
82 class LabelPlacement {
83 public:
84
85 double lengthVal;
86 double offset;
87 Geom::Point start;
88 Geom::Point end;
89 };
90
91 bool SortLabelPlacement(LabelPlacement const &first, LabelPlacement const &second)
92 {
93 if (first.end[Geom::Y] == second.end[Geom::Y]) {
94 return first.end[Geom::X] < second.end[Geom::X];
95 } else {
96 return first.end[Geom::Y] < second.end[Geom::Y];
97 }
98 }
99
100 //precision is for give the number of decimal positions
101 //of the label to calculate label width
102 void repositionOverlappingLabels(std::vector<LabelPlacement> &placements, SPDesktop *desktop, Geom::Point const &normal, double fontsize, int precision)
103 {
104 std::sort(placements.begin(), placements.end(), SortLabelPlacement);
105
106 double border = 3;
107 Geom::Rect box;
108 {
109 Geom::Point tmp(fontsize * (6 + precision) + (border * 2), fontsize + (border * 2));
110 tmp = desktop->w2d(tmp);
111 box = Geom::Rect(-tmp[Geom::X] / 2, -tmp[Geom::Y] / 2, tmp[Geom::X] / 2, tmp[Geom::Y] / 2);
112 }
113
114 // Using index since vector may be re-ordered as we go.
115 // Starting at one, since the first item can't overlap itself
116 for (size_t i = 1; i < placements.size(); i++) {
117 LabelPlacement &place = placements[i];
118
119 bool changed = false;
120 do {
121 Geom::Rect current(box + place.end);
122
123 changed = false;
124 bool overlaps = false;
125 for (size_t j = i; (j > 0) && !overlaps; --j) {
126 LabelPlacement &otherPlace = placements[j - 1];
127 Geom::Rect target(box + otherPlace.end);
128 if (current.intersects(target)) {
129 overlaps = true;
130 }
131 }
132 if (overlaps) {
133 place.offset += (fontsize + border);
134 place.end = place.start - desktop->w2d(normal * place.offset);
135 changed = true;
136 }
137 } while (changed);
138
139 std::sort(placements.begin(), placements.begin() + i + 1, SortLabelPlacement);
140 }
141 }
142
143 /**
144 * Calculates where to place the anchor for the display text and arc.
145 *
146 * @param desktop the desktop that is being used.
147 * @param angle the angle to be displaying.
148 * @param baseAngle the angle of the initial baseline.
149 * @param startPoint the point that is the vertex of the selected angle.
150 * @param endPoint the point that is the end the user is manipulating for measurement.
151 * @param fontsize the size to display the text label at.
152 */
153 Geom::Point calcAngleDisplayAnchor(SPDesktop *desktop, double angle, double baseAngle,
154 Geom::Point const &startPoint, Geom::Point const &endPoint,
155 double fontsize)
156 {
157 // Time for the trick work of figuring out where things should go, and how.
158 double lengthVal = (endPoint - startPoint).length();
159 double effective = baseAngle + (angle / 2);
160 Geom::Point where(lengthVal, 0);
161 where *= Geom::Affine(Geom::Rotate(effective)) * Geom::Affine(Geom::Translate(startPoint));
162
163 // When the angle is tight, the label would end up under the cursor and/or lines. Bump it
164 double scaledFontsize = std::abs(fontsize * desktop->w2d(Geom::Point(0, 1.0))[Geom::Y]);
165 if (std::abs((where - endPoint).length()) < scaledFontsize) {
166 where[Geom::Y] += scaledFontsize * 2;
167 }
168
169 // We now have the ideal position, but need to see if it will fit/work.
170
171 Geom::Rect visibleArea = desktop->get_display_area();
172 // Bring it in to "title safe" for the anchor point
173 Geom::Point textBox = desktop->w2d(Geom::Point(fontsize * 3, fontsize / 2));
174 textBox[Geom::Y] = std::abs(textBox[Geom::Y]);
175
176 visibleArea = Geom::Rect(visibleArea.min()[Geom::X] + textBox[Geom::X],
177 visibleArea.min()[Geom::Y] + textBox[Geom::Y],
178 visibleArea.max()[Geom::X] - textBox[Geom::X],
179 visibleArea.max()[Geom::Y] - textBox[Geom::Y]);
180
181 where[Geom::X] = std::min(where[Geom::X], visibleArea.max()[Geom::X]);
182 where[Geom::X] = std::max(where[Geom::X], visibleArea.min()[Geom::X]);
183 where[Geom::Y] = std::min(where[Geom::Y], visibleArea.max()[Geom::Y]);
184 where[Geom::Y] = std::max(where[Geom::Y], visibleArea.min()[Geom::Y]);
185
186 return where;
187 }
188
189 /**
190 * Create a measure item in current document.
191 *
192 * @param pathv the path to create.
193 * @param markers if the path results get markers.
194 * @param color of the stroke.
195 * @param measure_repr container element.
196 */
197 void setMeasureItem(Geom::PathVector pathv, bool is_curve, bool markers, guint32 color, Inkscape::XML::Node *measure_repr)
198 {
199 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
200 if(!desktop) {
201 return;
202 }
203 SPDocument *doc = desktop->getDocument();
204 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
205 Inkscape::XML::Node *repr;
206 repr = xml_doc->createElement("svg:path");
207 gchar *str = sp_svg_write_path(pathv);
208 SPCSSAttr *css = sp_repr_css_attr_new();
209 Geom::Coord strokewidth = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse().expansionX();
210 std::stringstream stroke_width;
211 stroke_width.imbue(std::locale::classic());
212 if(measure_repr) {
213 stroke_width << strokewidth / desktop->current_zoom();
214 } else {
215 stroke_width << strokewidth;
216 }
217 sp_repr_css_set_property (css, "stroke-width", stroke_width.str().c_str());
218 sp_repr_css_set_property (css, "fill", "none");
219 if(color) {
220 gchar color_line[64];
221 sp_svg_write_color (color_line, sizeof(color_line), color);
222 sp_repr_css_set_property (css, "stroke", color_line);
223 } else {
224 sp_repr_css_set_property (css, "stroke", "#ff0000");
225 }
226 char const * stroke_linecap = is_curve ? "butt" : "square";
227 sp_repr_css_set_property (css, "stroke-linecap", stroke_linecap);
228 sp_repr_css_set_property (css, "stroke-linejoin", "miter");
229 sp_repr_css_set_property (css, "stroke-miterlimit", "4");
230 sp_repr_css_set_property (css, "stroke-dasharray", "none");
231 if(measure_repr) {
232 sp_repr_css_set_property (css, "stroke-opacity", "0.5");
233 } else {
234 sp_repr_css_set_property (css, "stroke-opacity", "1");
235 }
236 if(markers) {
237 sp_repr_css_set_property (css, "marker-start", "url(#Arrow2Sstart)");
238 sp_repr_css_set_property (css, "marker-end", "url(#Arrow2Send)");
239 }
240 Glib::ustring css_str;
241 sp_repr_css_write_string(css,css_str);
242 repr->setAttribute("style", css_str.c_str());
243 sp_repr_css_attr_unref (css);
244 g_assert( str != nullptr );
245 repr->setAttribute("d", str);
246 g_free(str);
247 if(measure_repr) {
248 measure_repr->addChild(repr, nullptr);
249 Inkscape::GC::release(repr);
250 } else {
251 SPItem *item = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr));
252 Inkscape::GC::release(repr);
253 item->updateRepr();
254 desktop->getSelection()->clear();
255 desktop->getSelection()->add(item);
256 }
257 }
258
259 /**
260 * Given an angle, the arc center and edge point, draw an arc segment centered around that edge point.
261 *
262 * @param desktop the desktop that is being used.
263 * @param center the center point for the arc.
264 * @param end the point that ends at the edge of the arc segment.
265 * @param anchor the anchor point for displaying the text label.
266 * @param angle the angle of the arc segment to draw.
267 * @param measure_rpr the container of the curve if converted to items.
268 */
269 void createAngleDisplayCurve(SPDesktop *desktop, Geom::Point const &center, Geom::Point const &end, Geom::Point const &anchor, double angle, bool to_phantom, std::vector<SPCanvasItem *> &measure_phantom_items , std::vector<SPCanvasItem *> &measure_tmp_items , Inkscape::XML::Node *measure_repr = nullptr)
270 {
271 // Given that we have a point on the arc's edge and the angle of the arc, we need to get the two endpoints.
272
273 double textLen = std::abs((anchor - center).length());
274 double sideLen = std::abs((end - center).length());
275 if (sideLen > 0.0) {
276 double factor = std::min(1.0, textLen / sideLen);
277
278 // arc start
279 Geom::Point p1 = end * (Geom::Affine(Geom::Translate(-center))
280 * Geom::Affine(Geom::Scale(factor))
281 * Geom::Affine(Geom::Translate(center)));
282
283 // arc end
284 Geom::Point p4 = p1 * (Geom::Affine(Geom::Translate(-center))
285 * Geom::Affine(Geom::Rotate(-angle))
286 * Geom::Affine(Geom::Translate(center)));
287
288 // from Riskus
289 double xc = center[Geom::X];
290 double yc = center[Geom::Y];
291 double ax = p1[Geom::X] - xc;
292 double ay = p1[Geom::Y] - yc;
293 double bx = p4[Geom::X] - xc;
294 double by = p4[Geom::Y] - yc;
295 double q1 = (ax * ax) + (ay * ay);
296 double q2 = q1 + (ax * bx) + (ay * by);
297
298 double k2 = (4.0 / 3.0) * (std::sqrt(2 * q1 * q2) - q2) / ((ax * by) - (ay * bx));
299
300 Geom::Point p2(xc + ax - (k2 * ay),
301 yc + ay + (k2 * ax));
302 Geom::Point p3(xc + bx + (k2 * by),
303 yc + by - (k2 * bx));
304 SPCtrlCurve *curve = ControlManager::getManager().createControlCurve(desktop->getTempGroup(), p1, p2, p3, p4, CTLINE_SECONDARY);
305 if(to_phantom){
306 curve->rgba = 0x8888887f;
307 measure_phantom_items.push_back(SP_CANVAS_ITEM(curve));
308 } else {
309 measure_tmp_items.push_back(SP_CANVAS_ITEM(curve));
310 }
311 sp_canvas_item_move_to_z(SP_CANVAS_ITEM(curve), 0);
312 sp_canvas_item_show(SP_CANVAS_ITEM(curve));
313 if(measure_repr) {
314 Geom::PathVector pathv;
315 Geom::Path path;
316 path.start(desktop->doc2dt(p1));
317 path.appendNew<Geom::CubicBezier>(desktop->doc2dt(p2),desktop->doc2dt(p3),desktop->doc2dt(p4));
318 pathv.push_back(path);
319 pathv *= SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse();
320 if(!pathv.empty()) {
321 setMeasureItem(pathv, true, false, 0xff00007f, measure_repr);
322 }
323 }
324 }
325 }
326
327 boost::optional<Geom::Point> explicit_base_tmp = boost::none;
328
329 } // namespace
330
331 LivecodeTool::LivecodeTool()
332 : ToolBase(cursor_measure_xpm)
333 , grabbed(nullptr)
334 {
335 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
336 start_p = readMeasurePoint(true);
337 end_p = readMeasurePoint(false);
338 dimension_offset = 35;
339 last_pos = Geom::Point(0,0);
340 // create the knots
341 this->knot_start = new SPKnot(desktop, _("Measure start, <b>Shift+Click</b> for position dialog"));
342 this->knot_start->setMode(SP_KNOT_MODE_XOR);
343 this->knot_start->setFill(MT_KNOT_COLOR_NORMAL, MT_KNOT_COLOR_MOUSEOVER, MT_KNOT_COLOR_MOUSEOVER, MT_KNOT_COLOR_MOUSEOVER);
344 this->knot_start->setStroke(0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f);
345 this->knot_start->setShape(SP_KNOT_SHAPE_CIRCLE);
346 this->knot_start->updateCtrl();
347 this->knot_end = new SPKnot(desktop, _("Measure end, <b>Shift+Click</b> for position dialog"));
348 this->knot_end->setMode(SP_KNOT_MODE_XOR);
349 this->knot_end->setFill(MT_KNOT_COLOR_NORMAL, MT_KNOT_COLOR_MOUSEOVER, MT_KNOT_COLOR_MOUSEOVER, MT_KNOT_COLOR_MOUSEOVER);
350 this->knot_end->setStroke(0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f);
351 this->knot_end->setShape(SP_KNOT_SHAPE_CIRCLE);
352 this->knot_end->updateCtrl();
353 Geom::Rect display_area = desktop->get_display_area();
354 if(display_area.interiorContains(start_p) && display_area.interiorContains(end_p) && end_p != Geom::Point()) {
355 this->knot_start->moveto(start_p);
356 this->knot_start->show();
357 this->knot_end->moveto(end_p);
358 this->knot_end->show();
359 showCanvasItems();
360 } else {
361 start_p = Geom::Point(0,0);
362 end_p = Geom::Point(0,0);
363 writeMeasurePoint(start_p, true);
364 writeMeasurePoint(end_p, false);
365 }
366 this->_knot_start_moved_connection = this->knot_start->moved_signal.connect(sigc::mem_fun(*this, &LivecodeTool::knotStartMovedHandler));
367 this->_knot_start_click_connection = this->knot_start->click_signal.connect(sigc::mem_fun(*this, &LivecodeTool::knotClickHandler));
368 this->_knot_start_ungrabbed_connection = this->knot_start->ungrabbed_signal.connect(sigc::mem_fun(*this, &LivecodeTool::knotUngrabbedHandler));
369 this->_knot_end_moved_connection = this->knot_end->moved_signal.connect(sigc::mem_fun(*this, &LivecodeTool::knotEndMovedHandler));
370 this->_knot_end_click_connection = this->knot_end->click_signal.connect(sigc::mem_fun(*this, &LivecodeTool::knotClickHandler));
371 this->_knot_end_ungrabbed_connection = this->knot_end->ungrabbed_signal.connect(sigc::mem_fun(*this, &LivecodeTool::knotUngrabbedHandler));
372
373 }
374
375 LivecodeTool::~LivecodeTool()
376 {
377 this->_knot_start_moved_connection.disconnect();
378 this->_knot_start_ungrabbed_connection.disconnect();
379 this->_knot_end_moved_connection.disconnect();
380 this->_knot_end_ungrabbed_connection.disconnect();
381
382 /* unref should call destroy */
383 knot_unref(this->knot_start);
384 knot_unref(this->knot_end);
385 for (auto & measure_tmp_item : measure_tmp_items) {
386 sp_canvas_item_destroy(measure_tmp_item);
387 }
388 measure_tmp_items.clear();
389 for (auto & idx : measure_item) {
390 sp_canvas_item_destroy(idx);
391 }
392 measure_item.clear();
393 for (auto & measure_phantom_item : measure_phantom_items) {
394 sp_canvas_item_destroy(measure_phantom_item);
395 }
396 measure_phantom_items.clear();
397 }
398
399 Geom::Point LivecodeTool::readMeasurePoint(bool is_start) {
400 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
401 Glib::ustring measure_point = is_start ? "/tools/measure/measure-start" : "/tools/measure/measure-end";
402 return prefs->getPoint(measure_point, Geom::Point(Geom::infinity(),Geom::infinity()));
403 }
404
405 void LivecodeTool::writeMeasurePoint(Geom::Point point, bool is_start) {
406 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
407 Glib::ustring measure_point = is_start ? "/tools/measure/measure-start" : "/tools/measure/measure-end";
408 prefs->setPoint(measure_point, point);
409 }
410
411 //This function is used to reverse the Measure, I do it in two steps because when
412 //we move the knot the start_ or the end_p are overwritten so I need the original values.
413 void LivecodeTool::reverseKnots()
414 {
415 Geom::Point start = start_p;
416 Geom::Point end = end_p;
417 this->knot_start->moveto(end);
418 this->knot_start->show();
419 this->knot_end->moveto(start);
420 this->knot_end->show();
421 start_p = end;
422 end_p = start;
423 this->showCanvasItems();
424 }
425
426 void LivecodeTool::knotClickHandler(SPKnot *knot, guint state)
427 {
428 if (state & GDK_SHIFT_MASK) {
429 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
430 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
431 Glib::ustring const unit_name = prefs->getString("/tools/measure/unit");
432 explicit_base = explicit_base_tmp;
433 Inkscape::UI::Dialogs::KnotPropertiesDialog::showDialog(desktop, knot, unit_name);
434 }
435 }
436
437 void LivecodeTool::knotStartMovedHandler(SPKnot */*knot*/, Geom::Point const &ppointer, guint state)
438 {
439 Geom::Point point = this->knot_start->position();
440 if (state & GDK_CONTROL_MASK) {
441 spdc_endpoint_snap_rotation(this, point, end_p, state);
442 } else if (!(state & GDK_SHIFT_MASK)) {
443 SnapManager &snap_manager = desktop->namedview->snap_manager;
444 snap_manager.setup(desktop);
445 Inkscape::SnapCandidatePoint scp(point, Inkscape::SNAPSOURCE_OTHER_HANDLE);
446 scp.addOrigin(this->knot_end->position());
447 Inkscape::SnappedPoint sp = snap_manager.freeSnap(scp);
448 point = sp.getPoint();
449 snap_manager.unSetup();
450 }
451 if(start_p != point) {
452 start_p = point;
453 this->knot_start->moveto(start_p);
454 }
455 showCanvasItems();
456 }
457
458 void LivecodeTool::knotEndMovedHandler(SPKnot */*knot*/, Geom::Point const &ppointer, guint state)
459 {
460 Geom::Point point = this->knot_end->position();
461 if (state & GDK_CONTROL_MASK) {
462 spdc_endpoint_snap_rotation(this, point, start_p, state);
463 } else if (!(state & GDK_SHIFT_MASK)) {
464 SnapManager &snap_manager = desktop->namedview->snap_manager;
465 snap_manager.setup(desktop);
466 Inkscape::SnapCandidatePoint scp(point, Inkscape::SNAPSOURCE_OTHER_HANDLE);
467 scp.addOrigin(this->knot_start->position());
468 Inkscape::SnappedPoint sp = snap_manager.freeSnap(scp);
469 point = sp.getPoint();
470 snap_manager.unSetup();
471 }
472 if(end_p != point) {
473 end_p = point;
474 this->knot_end->moveto(end_p);
475 }
476 showCanvasItems();
477 }
478
479 void LivecodeTool::knotUngrabbedHandler(SPKnot */*knot*/, unsigned int state)
480 {
481 this->knot_start->moveto(start_p);
482 this->knot_end->moveto(end_p);
483 showCanvasItems();
484 }
485
486
487 //todo: we need this function?
488 void LivecodeTool::finish()
489 {
490 this->enableGrDrag(false);
491
492 if (this->grabbed) {
493 sp_canvas_item_ungrab(this->grabbed);
494 this->grabbed = nullptr;
495 }
496
497 ToolBase::finish();
498 }
499
500 static void calculate_intersections(SPDesktop * /*desktop*/, SPItem* item, Geom::PathVector const &lineseg, SPCurve *curve, std::vector<double> &intersections)
501 {
502 curve->transform(item->i2doc_affine());
503 // Find all intersections of the control-line with this shape
504 Geom::CrossingSet cs = Geom::crossings(lineseg, curve->get_pathvector());
505 Geom::delete_duplicates(cs[0]);
506
507 // Reconstruct and store the points of intersection
508 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
509 bool show_hidden = prefs->getBool("/tools/measure/show_hidden", true);
510 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
511 for (Geom::Crossings::const_iterator m = cs[0].begin(); m != cs[0].end(); ++m) {
512 if (!show_hidden) {
513 double eps = 0.0001;
514 if (((*m).ta > eps &&
515 item == desktop->getItemAtPoint(desktop->d2w(desktop->dt2doc(lineseg[0].pointAt((*m).ta - eps))), true, nullptr)) ||
516 ((*m).ta + eps < 1 &&
517 item == desktop->getItemAtPoint(desktop->d2w(desktop->dt2doc(lineseg[0].pointAt((*m).ta + eps))), true, nullptr))) {
518 intersections.push_back((*m).ta);
519 }
520 } else {
521 intersections.push_back((*m).ta);
522 }
523 }
524 }
525
526 bool LivecodeTool::root_handler(GdkEvent* event)
527 {
528 gint ret = FALSE;
529
530 switch (event->type) {
531 case GDK_BUTTON_PRESS: {
532 this->knot_start->hide();
533 this->knot_end->hide();
534 Geom::Point const button_w(event->button.x, event->button.y);
535 explicit_base = boost::none;
536 explicit_base_tmp = boost::none;
537 last_end = boost::none;
538
539 if (event->button.button == 1 && !this->space_panning) {
540 // save drag origin
541 start_p = desktop->w2d(Geom::Point(event->button.x, event->button.y));
542 within_tolerance = true;
543
544 ret = TRUE;
545 }
546
547 SnapManager &snap_manager = desktop->namedview->snap_manager;
548 snap_manager.setup(desktop);
549 snap_manager.freeSnapReturnByRef(start_p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
550 snap_manager.unSetup();
551
552 sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
553 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK,
554 nullptr, event->button.time);
555 this->grabbed = SP_CANVAS_ITEM(desktop->acetate);
556 break;
557 }
558 case GDK_KEY_PRESS: {
559 if ((event->key.keyval == GDK_KEY_Control_L) || (event->key.keyval == GDK_KEY_Control_R)) {
560 explicit_base_tmp = explicit_base;
561 explicit_base = end_p;
562 showInfoBox(last_pos, true);
563 }
564 break;
565 }
566 case GDK_KEY_RELEASE: {
567 if ((event->key.keyval == GDK_KEY_Control_L) || (event->key.keyval == GDK_KEY_Control_R)) {
568 showInfoBox(last_pos, false);
569 }
570 break;
571 }
572 case GDK_MOTION_NOTIFY: {
573 if (!(event->motion.state & GDK_BUTTON1_MASK)) {
574 if(!(event->motion.state & GDK_SHIFT_MASK)) {
575 Geom::Point const motion_w(event->motion.x, event->motion.y);
576 Geom::Point const motion_dt(desktop->w2d(motion_w));
577
578 SnapManager &snap_manager = desktop->namedview->snap_manager;
579 snap_manager.setup(desktop);
580
581 Inkscape::SnapCandidatePoint scp(motion_dt, Inkscape::SNAPSOURCE_OTHER_HANDLE);
582 scp.addOrigin(start_p);
583
584 snap_manager.preSnap(scp);
585 snap_manager.unSetup();
586 }
587 last_pos = Geom::Point(event->motion.x, event->motion.y);
588 if (event->motion.state & GDK_CONTROL_MASK) {
589 showInfoBox(last_pos, true);
590 } else {
591 showInfoBox(last_pos, false);
592 }
593 } else {
594 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
595 //Inkscape::Util::Unit const * unit = desktop->getNamedView()->getDisplayUnit();
596 for (auto & idx : measure_item) {
597 sp_canvas_item_destroy(idx);
598 }
599 measure_item.clear();
600 ret = TRUE;
601 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
602 tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
603 Geom::Point const motion_w(event->motion.x, event->motion.y);
604 if ( within_tolerance) {
605 if ( Geom::LInfty( motion_w - start_p ) < tolerance) {
606 return FALSE; // Do not drag if we're within tolerance from origin.
607 }
608 }
609 // Once the user has moved farther than tolerance from the original location
610 // (indicating they intend to move the object, not click), then always process the
611 // motion notify coordinates as given (no snapping back to origin)
612 within_tolerance = false;
613 if(event->motion.time == 0 || !last_end || Geom::LInfty( motion_w - *last_end ) > (tolerance/4.0)) {
614 Geom::Point const motion_dt(desktop->w2d(motion_w));
615 end_p = motion_dt;
616
617 if (event->motion.state & GDK_CONTROL_MASK) {
618 spdc_endpoint_snap_rotation(this, end_p, start_p, event->motion.state);
619 } else if (!(event->motion.state & GDK_SHIFT_MASK)) {
620 SnapManager &snap_manager = desktop->namedview->snap_manager;
621 snap_manager.setup(desktop);
622 Inkscape::SnapCandidatePoint scp(end_p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
623 scp.addOrigin(start_p);
624 Inkscape::SnappedPoint sp = snap_manager.freeSnap(scp);
625 end_p = sp.getPoint();
626 snap_manager.unSetup();
627 }
628 showCanvasItems();
629 last_end = motion_w ;
630 }
631 gobble_motion_events(GDK_BUTTON1_MASK);
632 }
633 break;
634 }
635 case GDK_BUTTON_RELEASE: {
636 this->knot_start->moveto(start_p);
637 this->knot_start->show();
638 if(last_end) {
639 end_p = desktop->w2d(*last_end);
640 if (event->button.state & GDK_CONTROL_MASK) {
641 spdc_endpoint_snap_rotation(this, end_p, start_p, event->motion.state);
642 } else if (!(event->button.state & GDK_SHIFT_MASK)) {
643 SnapManager &snap_manager = desktop->namedview->snap_manager;
644 snap_manager.setup(desktop);
645 Inkscape::SnapCandidatePoint scp(end_p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
646 scp.addOrigin(start_p);
647 Inkscape::SnappedPoint sp = snap_manager.freeSnap(scp);
648 end_p = sp.getPoint();
649 snap_manager.unSetup();
650 }
651 }
652 this->knot_end->moveto(end_p);
653 this->knot_end->show();
654 showCanvasItems();
655
656 if (this->grabbed) {
657 sp_canvas_item_ungrab(this->grabbed);
658 this->grabbed = nullptr;
659 }
660 break;
661 }
662 default:
663 break;
664 }
665 if (!ret) {
666 ret = ToolBase::root_handler(event);
667 }
668
669 return ret;
670 }
671
672 void LivecodeTool::setMarkers()
673 {
674 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
675 SPDocument *doc = desktop->getDocument();
676 SPObject *arrowStart = doc->getObjectById("Arrow2Sstart");
677 SPObject *arrowEnd = doc->getObjectById("Arrow2Send");
678 if (!arrowStart) {
679 setMarker(true);
680 }
681 if(!arrowEnd) {
682 setMarker(false);
683 }
684 }
685 void LivecodeTool::setMarker(bool isStart)
686 {
687 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
688 SPDocument *doc = desktop->getDocument();
689 SPDefs *defs = doc->getDefs();
690 Inkscape::XML::Node *rmarker;
691 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
692 rmarker = xml_doc->createElement("svg:marker");
693 rmarker->setAttribute("id", isStart ? "Arrow2Sstart" : "Arrow2Send");
694 rmarker->setAttribute("inkscape:isstock", "true");
695 rmarker->setAttribute("inkscape:stockid", isStart ? "Arrow2Sstart" : "Arrow2Send");
696 rmarker->setAttribute("orient", "auto");
697 rmarker->setAttribute("refX", "0.0");
698 rmarker->setAttribute("refY", "0.0");
699 rmarker->setAttribute("style", "overflow:visible;");
700 SPItem *marker = SP_ITEM(defs->appendChildRepr(rmarker));
701 Inkscape::GC::release(rmarker);
702 marker->updateRepr();
703 Inkscape::XML::Node *rpath;
704 rpath = xml_doc->createElement("svg:path");
705 rpath->setAttribute("d", "M 8.72,4.03 L -2.21,0.02 L 8.72,-4.00 C 6.97,-1.63 6.98,1.62 8.72,4.03 z");
706 rpath->setAttribute("id", isStart ? "Arrow2SstartPath" : "Arrow2SendPath");
707 SPCSSAttr *css = sp_repr_css_attr_new();
708 sp_repr_css_set_property (css, "stroke", "none");
709 sp_repr_css_set_property (css, "fill", "#000000");
710 sp_repr_css_set_property (css, "fill-opacity", "1");
711 Glib::ustring css_str;
712 sp_repr_css_write_string(css,css_str);
713 rpath->setAttribute("style", css_str.c_str());
714 sp_repr_css_attr_unref (css);
715 rpath->setAttribute("transform", isStart ? "scale(0.3) translate(-2.3,0)" : "scale(0.3) rotate(180) translate(-2.3,0)");
716 SPItem *path = SP_ITEM(marker->appendChildRepr(rpath));
717 Inkscape::GC::release(rpath);
718 path->updateRepr();
719 }
720
721 void LivecodeTool::toGuides()
722 {
723 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
724 if(!desktop || !start_p.isFinite() || !end_p.isFinite() || start_p == end_p) {
725 return;
726 }
727 SPDocument *doc = desktop->getDocument();
728 Geom::Point start = desktop->doc2dt(start_p) * desktop->doc2dt();
729 Geom::Point end = desktop->doc2dt(end_p) * desktop->doc2dt();
730 Geom::Ray ray(start,end);
731 SPNamedView *namedview = desktop->namedview;
732 if(!namedview) {
733 return;
734 }
735 setGuide(start,ray.angle(), _("Measure"));
736 if(explicit_base) {
737 explicit_base = *explicit_base * SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse();
738 ray.setPoints(start, *explicit_base);
739 if(ray.angle() != 0) {
740 setGuide(start,ray.angle(), _("Base"));
741 }
742 }
743 setGuide(start,0,"");
744 setGuide(start,Geom::rad_from_deg(90),_("Start"));
745 setGuide(end,0,_("End"));
746 setGuide(end,Geom::rad_from_deg(90),"");
747 showCanvasItems(true);
748 doc->ensureUpToDate();
749 DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MEASURE,_("Add guides from measure tool"));
750 }
751
752 void LivecodeTool::toPhantom()
753 {
754 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
755 if(!desktop || !start_p.isFinite() || !end_p.isFinite() || start_p == end_p) {
756 return;
757 }
758 SPDocument *doc = desktop->getDocument();
759 for (auto & measure_phantom_item : measure_phantom_items) {
760 sp_canvas_item_destroy(measure_phantom_item);
761 }
762 measure_phantom_items.clear();
763 for (auto & measure_tmp_item : measure_tmp_items) {
764 sp_canvas_item_destroy(measure_tmp_item);
765 }
766 measure_tmp_items.clear();
767 showCanvasItems(false, false, true);
768 doc->ensureUpToDate();
769 DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MEASURE,_("Keep last measure on the canvas, for reference"));
770 }
771
772 void LivecodeTool::toItem()
773 {
774 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
775 if(!desktop || !start_p.isFinite() || !end_p.isFinite() || start_p == end_p) {
776 return;
777 }
778 SPDocument *doc = desktop->getDocument();
779 Geom::Ray ray(start_p,end_p);
780 guint32 line_color_primary = 0x0000ff7f;
781 Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
782 Inkscape::XML::Node *rgroup = xml_doc->createElement("svg:g");
783 showCanvasItems(false, true, false, rgroup);
784 setLine(start_p,end_p, false, line_color_primary, rgroup);
785 SPItem *measure_item = SP_ITEM(desktop->currentLayer()->appendChildRepr(rgroup));
786 Inkscape::GC::release(rgroup);
787 measure_item->updateRepr();
788 doc->ensureUpToDate();
789 DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MEASURE,_("Convert measure to items"));
790 reset();
791 }
792
793 void LivecodeTool::toMarkDimension()
794 {
795 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
796 if(!desktop || !start_p.isFinite() || !end_p.isFinite() || start_p == end_p) {
797 return;
798 }
799 SPDocument *doc = desktop->getDocument();
800 setMarkers();
801 Geom::Ray ray(start_p,end_p);
802 Geom::Point start = start_p + Geom::Point::polar(ray.angle(), 5);
803 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
804 dimension_offset = prefs->getDouble("/tools/measure/offset", 5.0);
805 start = start + Geom::Point::polar(ray.angle() + Geom::rad_from_deg(90), -dimension_offset);
806 Geom::Point end = end_p + Geom::Point::polar(ray.angle(), -5);
807 end = end+ Geom::Point::polar(ray.angle() + Geom::rad_from_deg(90), -dimension_offset);
808 guint32 color = 0x000000ff;
809 setLine(start, end, true, color);
810 Glib::ustring unit_name = prefs->getString("/tools/measure/unit");
811 if (!unit_name.compare("")) {
812 unit_name = DEFAULT_UNIT_NAME;
813 }
814 double fontsize = prefs->getDouble("/tools/measure/fontsize", 10.0);
815 int precision = prefs->getInt("/tools/measure/precision", 2);
816 std::stringstream precision_str;
817 precision_str.imbue(std::locale::classic());
818 precision_str << "%." << precision << "f %s";
819 Geom::Point middle = Geom::middle_point(start, end);
820 double totallengthval = (end_p - start_p).length();
821 totallengthval = Inkscape::Util::Quantity::convert(totallengthval, "px", unit_name);
822 double scale = prefs->getDouble("/tools/measure/scale", 100.0) / 100.0;
823 gchar *totallength_str = g_strdup_printf(precision_str.str().c_str(), totallengthval * scale, unit_name.c_str());
824 double textangle = Geom::rad_from_deg(180) - ray.angle();
825 if (desktop->is_yaxisdown()) {
826 textangle = ray.angle() - Geom::rad_from_deg(180);
827 }
828 setLabelText(totallength_str, middle, fontsize, textangle, color);
829 g_free(totallength_str);
830 doc->ensureUpToDate();
831 DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MEASURE,_("Add global measure line"));
832 }
833
834 void LivecodeTool::setGuide(Geom::Point origin,double angle, const char *label)
835 {
836 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
837 SPDocument *doc = desktop->getDocument();
838 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
839 SPRoot const *root = doc->getRoot();
840 Geom::Affine affine(Geom::identity());
841 if(root) {
842 affine *= root->c2p.inverse();
843 }
844 SPNamedView *namedview = desktop->namedview;
845 if(!namedview) {
846 return;
847 }
848
849 // <sodipodi:guide> stores inverted y-axis coordinates
850 if (desktop->is_yaxisdown()) {
851 origin[Geom::Y] = doc->getHeight().value("px") - origin[Geom::Y];
852 angle *= -1.0;
853 }
854
855 origin *= affine;
856 //measure angle
857 Inkscape::XML::Node *guide;
858 guide = xml_doc->createElement("sodipodi:guide");
859 std::stringstream position;
860 position.imbue(std::locale::classic());
861 position << origin[Geom::X] << "," << origin[Geom::Y];
862 guide->setAttribute("position", position.str().c_str() );
863 guide->setAttribute("inkscape:color", "rgb(167,0,255)");
864 guide->setAttribute("inkscape:label", label);
865 Geom::Point unit_vector = Geom::rot90(origin.polar(angle));
866 std::stringstream angle_str;
867 angle_str.imbue(std::locale::classic());
868 angle_str << unit_vector[Geom::X] << "," << unit_vector[Geom::Y];
869 guide->setAttribute("orientation", angle_str.str().c_str());
870 namedview->appendChild(guide);
871 Inkscape::GC::release(guide);
872 }
873
874 void LivecodeTool::setLine(Geom::Point start_point,Geom::Point end_point, bool markers, guint32 color, Inkscape::XML::Node *measure_repr)
875 {
876 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
877 if(!desktop || !start_p.isFinite() || !end_p.isFinite()) {
878 return;
879 }
880 Geom::PathVector pathv;
881 Geom::Path path;
882 path.start(desktop->doc2dt(start_point));
883 path.appendNew<Geom::LineSegment>(desktop->doc2dt(end_point));
884 pathv.push_back(path);
885 pathv *= SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse();
886 if(!pathv.empty()) {
887 setMeasureItem(pathv, false, markers, color, measure_repr);
888 }
889 }
890
891 void LivecodeTool::setPoint(Geom::Point origin, Inkscape::XML::Node *measure_repr)
892 {
893 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
894 if(!desktop || !origin.isFinite()) {
895 return;
896 }
897 char const * svgd;
898 svgd = "m 0.707,0.707 6.586,6.586 m 0,-6.586 -6.586,6.586";
899 Geom::PathVector pathv = sp_svg_read_pathv(svgd);
900 Geom::Scale scale = Geom::Scale(desktop->current_zoom()).inverse();
901 pathv *= Geom::Translate(Geom::Point(-3.5,-3.5));
902 pathv *= scale;
903 pathv *= Geom::Translate(Geom::Point() - (scale.vector() * 0.5));
904 pathv *= Geom::Translate(desktop->doc2dt(origin));
905 pathv *= SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse();
906 if (!pathv.empty()) {
907 guint32 line_color_secondary = 0xff0000ff;
908 setMeasureItem(pathv, false, false, line_color_secondary, measure_repr);
909 }
910 }
911
912 void LivecodeTool::setLabelText(const char *value, Geom::Point pos, double fontsize, Geom::Coord angle, guint32 background, Inkscape::XML::Node *measure_repr, CanvasTextAnchorPositionEnum text_anchor)
913 {
914 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
915 Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
916 /* Create <text> */
917 pos = desktop->doc2dt(pos);
918 Inkscape::XML::Node *rtext = xml_doc->createElement("svg:text");
919 rtext->setAttribute("xml:space", "preserve");
920
921
922 /* Set style */
923 sp_desktop_apply_style_tool(desktop, rtext, "/tools/text", true);
924 if(measure_repr) {
925 sp_repr_set_svg_double(rtext, "x", 2);
926 sp_repr_set_svg_double(rtext, "y", 2);
927 } else {
928 sp_repr_set_svg_double(rtext, "x", 0);
929 sp_repr_set_svg_double(rtext, "y", 0);
930 }
931
932 /* Create <tspan> */
933 Inkscape::XML::Node *rtspan = xml_doc->createElement("svg:tspan");
934 rtspan->setAttribute("sodipodi:role", "line");
935 SPCSSAttr *css = sp_repr_css_attr_new();
936 std::stringstream font_size;
937 font_size.imbue(std::locale::classic());
938 if(measure_repr) {
939 font_size << fontsize;
940 } else {
941 font_size << fontsize << "pt";
942 }
943 sp_repr_css_set_property (css, "font-size", font_size.str().c_str());
944 sp_repr_css_set_property (css, "font-style", "normal");
945 sp_repr_css_set_property (css, "font-weight", "normal");
946 sp_repr_css_set_property (css, "line-height", "125%");
947 sp_repr_css_set_property (css, "letter-spacing", "0");
948 sp_repr_css_set_property (css, "word-spacing", "0");
949 sp_repr_css_set_property (css, "text-align", "center");
950 sp_repr_css_set_property (css, "text-anchor", "middle");
951 if(measure_repr) {
952 sp_repr_css_set_property (css, "fill", "#FFFFFF");
953 } else {
954 sp_repr_css_set_property (css, "fill", "#000000");
955 }
956 sp_repr_css_set_property (css, "fill-opacity", "1");
957 sp_repr_css_set_property (css, "stroke", "none");
958 Glib::ustring css_str;
959 sp_repr_css_write_string(css,css_str);
960 rtspan->setAttribute("style", css_str.c_str());
961 sp_repr_css_attr_unref (css);
962 rtext->addChild(rtspan, nullptr);
963 Inkscape::GC::release(rtspan);
964 /* Create TEXT */
965 Inkscape::XML::Node *rstring = xml_doc->createTextNode(value);
966 rtspan->addChild(rstring, nullptr);
967 Inkscape::GC::release(rstring);
968 SPItem *text_item = SP_ITEM(desktop->currentLayer()->appendChildRepr(rtext));
969 Inkscape::GC::release(rtext);
970 text_item->updateRepr();
971 Geom::OptRect bbox = text_item->geometricBounds();
972 if (!measure_repr && bbox) {
973 Geom::Point center = bbox->midpoint();
974 text_item->transform *= Geom::Translate(center).inverse();
975 pos += Geom::Point::polar(angle+ Geom::rad_from_deg(90), -bbox->height());
976 }
977 if(measure_repr) {
978 /* Create <group> */
979 Inkscape::XML::Node *rgroup = xml_doc->createElement("svg:g");
980 /* Create <rect> */
981 Inkscape::XML::Node *rrect = xml_doc->createElement("svg:rect");
982 SPCSSAttr *css = sp_repr_css_attr_new ();
983 gchar color_line[64];
984 sp_svg_write_color (color_line, sizeof(color_line), background);
985 sp_repr_css_set_property (css, "fill", color_line);
986 sp_repr_css_set_property (css, "fill-opacity", "0.5");
987 sp_repr_css_set_property (css, "stroke-width", "0");
988 Glib::ustring css_str;
989 sp_repr_css_write_string(css,css_str);
990 rrect->setAttribute("style", css_str.c_str());
991 sp_repr_css_attr_unref (css);
992 sp_repr_set_svg_double(rgroup, "x", 0);
993 sp_repr_set_svg_double(rgroup, "y", 0);
994 sp_repr_set_svg_double(rrect, "x", -bbox->width()/2.0);
995 sp_repr_set_svg_double(rrect, "y", -bbox->height());
996 sp_repr_set_svg_double(rrect, "width", bbox->width() + 6);
997 sp_repr_set_svg_double(rrect, "height", bbox->height() + 6);
998 Inkscape::XML::Node *rtextitem = text_item->getRepr();
999 text_item->deleteObject();
1000 rgroup->addChild(rtextitem, nullptr);
1001 Inkscape::GC::release(rtextitem);
1002 rgroup->addChild(rrect, nullptr);
1003 Inkscape::GC::release(rrect);
1004 SPItem *text_item_box = SP_ITEM(desktop->currentLayer()->appendChildRepr(rgroup));
1005 Geom::Scale scale = Geom::Scale(desktop->current_zoom()).inverse();
1006 if(bbox && text_anchor == TEXT_ANCHOR_CENTER) {
1007 text_item_box->transform *= Geom::Translate(bbox->midpoint() - Geom::Point(1.0,1.0)).inverse();
1008 }
1009 text_item_box->transform *= scale;
1010 text_item_box->transform *= Geom::Translate(Geom::Point() - (scale.vector() * 0.5));
1011 text_item_box->transform *= Geom::Translate(pos);
1012 text_item_box->transform *= SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse();
1013 text_item_box->updateRepr();
1014 text_item_box->doWriteTransform(text_item_box->transform, nullptr, true);
1015 Inkscape::XML::Node *rlabel = text_item_box->getRepr();
1016 text_item_box->deleteObject();
1017 measure_repr->addChild(rlabel, nullptr);
1018 Inkscape::GC::release(rlabel);
1019 } else {
1020 text_item->transform *= Geom::Rotate(angle);
1021 text_item->transform *= Geom::Translate(pos);
1022 text_item->transform *= SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse();
1023 text_item->doWriteTransform(text_item->transform, nullptr, true);
1024 }
1025 }
1026
1027 void LivecodeTool::reset()
1028 {
1029 this->knot_start->hide();
1030 this->knot_end->hide();
1031 for (auto & measure_tmp_item : measure_tmp_items) {
1032 sp_canvas_item_destroy(measure_tmp_item);
1033 }
1034 measure_tmp_items.clear();
1035 }
1036
1037 void LivecodeTool::setMeasureCanvasText(bool is_angle, double precision, double amount, double fontsize, Glib::ustring unit_name, Geom::Point position, guint32 background, CanvasTextAnchorPositionEnum text_anchor, bool to_item, bool to_phantom, Inkscape::XML::Node *measure_repr)
1038 {
1039 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1040 std::stringstream precision_str;
1041 precision_str.imbue(std::locale::classic());
1042 if(is_angle){
1043 precision_str << "%." << precision << "f °";
1044 } else {
1045 precision_str << "%." << precision << "f %s";
1046 }
1047 gchar *measure_str = g_strdup_printf(precision_str.str().c_str(), amount, unit_name.c_str());
1048 SPCanvasText *canvas_tooltip = sp_canvastext_new(desktop->getTempGroup(),
1049 desktop,
1050 position,
1051 measure_str);
1052 sp_canvastext_set_fontsize(canvas_tooltip, fontsize);
1053 canvas_tooltip->rgba = 0xffffffff;
1054 canvas_tooltip->rgba_background = background;
1055 canvas_tooltip->outline = false;
1056 canvas_tooltip->background = true;
1057 canvas_tooltip->anchor_position = text_anchor;
1058 if(to_phantom){
1059 canvas_tooltip->rgba_background = 0x4444447f;
1060 measure_phantom_items.push_back(SP_CANVAS_ITEM(canvas_tooltip));
1061 sp_canvas_item_show(SP_CANVAS_ITEM(canvas_tooltip));
1062 } else {
1063 measure_tmp_items.push_back(SP_CANVAS_ITEM(canvas_tooltip));
1064 sp_canvas_item_show(SP_CANVAS_ITEM(canvas_tooltip));
1065 }
1066
1067 if(to_item) {
1068 setLabelText(measure_str, position, fontsize, 0, background, measure_repr);
1069 }
1070 g_free(measure_str);
1071 }
1072
1073 void LivecodeTool::setMeasureCanvasItem(Geom::Point position, bool to_item, bool to_phantom, Inkscape::XML::Node *measure_repr){
1074 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1075 guint32 color = 0xff0000ff;
1076 if(to_phantom){
1077 color = 0x888888ff;
1078 }
1079 SPCanvasItem * canvasitem = sp_canvas_item_new(desktop->getTempGroup(),
1080 SP_TYPE_CTRL,
1081 "anchor", SP_ANCHOR_CENTER,
1082 "size", 9,
1083 "stroked", TRUE,
1084 "stroke_color", color,
1085 "mode", SP_KNOT_MODE_XOR,
1086 "shape", SP_KNOT_SHAPE_CROSS,
1087 NULL );
1088
1089 SP_CTRL(canvasitem)->moveto(position);
1090 if(to_phantom){
1091 measure_phantom_items.push_back(canvasitem);
1092 } else {
1093 measure_tmp_items.push_back(canvasitem);
1094 }
1095 sp_canvas_item_show(canvasitem);
1096 sp_canvas_item_move_to_z(canvasitem, 0);
1097
1098 if(to_item) {
1099 setPoint(position, measure_repr);
1100 }
1101 }
1102
1103 void LivecodeTool::setMeasureCanvasControlLine(Geom::Point start, Geom::Point end, bool to_item, bool to_phantom, Inkscape::CtrlLineType ctrl_line_type, Inkscape::XML::Node *measure_repr){
1104 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1105 gint32 color = ctrl_line_type == CTLINE_PRIMARY ? 0x0000ff7f : 0xff00007f;
1106 if(to_phantom){
1107 color = ctrl_line_type == CTLINE_PRIMARY ? 0x4444447f : 0x8888887f;
1108 }
1109 SPCtrlLine *control_line = ControlManager::getManager().createControlLine(desktop->getTempGroup(),
1110 start,
1111 end,
1112 ctrl_line_type);
1113 control_line->rgba = color;
1114 if(to_phantom){
1115 measure_phantom_items.push_back(SP_CANVAS_ITEM(control_line));
1116 } else {
1117 measure_tmp_items.push_back(SP_CANVAS_ITEM(control_line));
1118 }
1119 sp_canvas_item_move_to_z(SP_CANVAS_ITEM(control_line), 0);
1120 sp_canvas_item_show(SP_CANVAS_ITEM(control_line));
1121 if(to_item) {
1122 setLine(start,
1123 end,
1124 false,
1125 color,
1126 measure_repr);
1127 }
1128 }
1129
1130 void LivecodeTool::showItemInfoText(Geom::Point pos, gchar *measure_str, double fontsize)
1131 {
1132 SPCanvasText *canvas_tooltip = sp_canvastext_new(desktop->getTempGroup(),
1133 desktop,
1134 pos,
1135 measure_str);
1136 sp_canvastext_set_fontsize(canvas_tooltip, fontsize);
1137 canvas_tooltip->rgba = 0xffffffff;
1138 canvas_tooltip->outline = false;
1139 canvas_tooltip->background = true;
1140 canvas_tooltip->anchor_position = TEXT_ANCHOR_LEFT;
1141 canvas_tooltip->rgba_background = 0x00000099;
1142 measure_item.push_back(SP_CANVAS_ITEM(canvas_tooltip));
1143 sp_canvas_item_show(SP_CANVAS_ITEM(canvas_tooltip));
1144 }
1145
1146 void LivecodeTool::showInfoBox(Geom::Point cursor, bool into_groups)
1147 {
1148 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1149 Inkscape::Util::Unit const * unit = desktop->getNamedView()->getDisplayUnit();
1150 for (auto & idx : measure_item) {
1151 sp_canvas_item_destroy(idx);
1152 }
1153 measure_item.clear();
1154
1155 SPItem *newover = desktop->getItemAtPoint(cursor, into_groups);
1156 if (newover) {
1157 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1158 double fontsize = prefs->getDouble("/tools/measure/fontsize", 10.0);
1159 double scale = prefs->getDouble("/tools/measure/scale", 100.0) / 100.0;
1160 int precision = prefs->getInt("/tools/measure/precision", 2);
1161 Glib::ustring unit_name = prefs->getString("/tools/measure/unit");
1162 bool only_selected = prefs->getBool("/tools/measure/only_selected", false);
1163 if (!unit_name.compare("")) {
1164 unit_name = DEFAULT_UNIT_NAME;
1165 }
1166 Geom::Scale zoom = Geom::Scale(Inkscape::Util::Quantity::convert(desktop->current_zoom(), "px", unit->abbr)).inverse();
1167 if(newover != over){
1168 over = newover;
1169 Preferences *prefs = Preferences::get();
1170 int prefs_bbox = prefs->getBool("/tools/bounding_box", false);
1171 SPItem::BBoxType bbox_type = !prefs_bbox ? SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX;
1172 Geom::OptRect bbox = over->bounds(bbox_type);
1173 if (bbox) {
1174
1175 item_width = Inkscape::Util::Quantity::convert((*bbox).width() * scale, unit->abbr, unit_name);
1176 item_height = Inkscape::Util::Quantity::convert((*bbox).height() * scale, unit->abbr, unit_name);
1177 item_x = Inkscape::Util::Quantity::convert((*bbox).left(), unit->abbr, unit_name);
1178 Geom::Point y_point(0,Inkscape::Util::Quantity::convert((*bbox).bottom() * scale, unit->abbr, "px"));
1179 y_point *= desktop->doc2dt();
1180 item_y = Inkscape::Util::Quantity::convert(y_point[Geom::Y] * scale, "px", unit_name);
1181 if (SP_IS_SHAPE(over)) {
1182 Geom::PathVector shape = SP_SHAPE(over)->getCurve()->get_pathvector();
1183 item_length = Geom::length(paths_to_pw(shape));
1184 item_length = Inkscape::Util::Quantity::convert(item_length * scale, unit->abbr, unit_name);
1185 }
1186 }
1187 }
1188 gchar *measure_str = nullptr;
1189 std::stringstream precision_str;
1190 precision_str.imbue(std::locale::classic());
1191 double origin = Inkscape::Util::Quantity::convert(14, "px", unit->abbr);
1192 Geom::Point rel_position = Geom::Point(origin, origin);
1193 Geom::Point pos = desktop->w2d(cursor);
1194 double gap = Inkscape::Util::Quantity::convert(7 + fontsize, "px", unit->abbr);
1195 if (only_selected) {
1196 if (desktop->getSelection()->includes(over)) {
1197 showItemInfoText(pos + (rel_position * zoom),_("Selected"),fontsize);
1198 } else {
1199 showItemInfoText(pos + (rel_position * zoom),_("Not selected"),fontsize);
1200 }
1201 rel_position = Geom::Point(rel_position[Geom::X], rel_position[Geom::Y] + gap);
1202 }
1203 if (SP_IS_SHAPE(over)) {
1204 precision_str << _("Length") << ": %." << precision << "f %s";
1205 measure_str = g_strdup_printf(precision_str.str().c_str(), item_length, unit_name.c_str());
1206 precision_str.str("");
1207 showItemInfoText(pos + (rel_position * zoom),measure_str,fontsize);
1208 rel_position = Geom::Point(rel_position[Geom::X], rel_position[Geom::Y] + gap);
1209 } else if (SP_IS_GROUP(over)) {
1210 measure_str = _("Press 'CTRL' to measure into group");
1211 showItemInfoText(pos + (rel_position * zoom),measure_str,fontsize);
1212 rel_position = Geom::Point(rel_position[Geom::X], rel_position[Geom::Y] + gap);
1213 }
1214 precision_str << "Y: %." << precision << "f %s";
1215 measure_str = g_strdup_printf(precision_str.str().c_str(), item_y, unit_name.c_str());
1216 precision_str.str("");
1217 showItemInfoText(pos + (rel_position * zoom),measure_str,fontsize);
1218 rel_position = Geom::Point(rel_position[Geom::X], rel_position[Geom::Y] + gap);
1219
1220 precision_str << "X: %." << precision << "f %s";
1221 measure_str = g_strdup_printf(precision_str.str().c_str(), item_x, unit_name.c_str());
1222 precision_str.str("");
1223 showItemInfoText(pos + (rel_position * zoom),measure_str,fontsize);
1224 rel_position = Geom::Point(rel_position[Geom::X], rel_position[Geom::Y] + gap);
1225
1226 precision_str << _("Height") << ": %." << precision << "f %s";
1227 measure_str = g_strdup_printf(precision_str.str().c_str(), item_height, unit_name.c_str());
1228 precision_str.str("");
1229 showItemInfoText(pos + (rel_position * zoom),measure_str,fontsize);
1230 rel_position = Geom::Point(rel_position[Geom::X], rel_position[Geom::Y] + gap);
1231
1232 precision_str << _("Width") << ": %." << precision << "f %s";
1233 measure_str = g_strdup_printf(precision_str.str().c_str(), item_width, unit_name.c_str());
1234 precision_str.str("");
1235 showItemInfoText(pos + (rel_position * zoom),measure_str,fontsize);
1236 g_free(measure_str);
1237 }
1238 }
1239
1240 void LivecodeTool::showCanvasItems(bool to_guides, bool to_item, bool to_phantom, Inkscape::XML::Node *measure_repr)
1241 {
1242 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1243 if(!desktop || !start_p.isFinite() || !end_p.isFinite() || start_p == end_p) {
1244 return;
1245 }
1246 writeMeasurePoint(start_p, true);
1247 writeMeasurePoint(end_p, false);
1248 //clear previous canvas items, we'll draw new ones
1249 for (auto & measure_tmp_item : measure_tmp_items) {
1250 sp_canvas_item_destroy(measure_tmp_item);
1251 }
1252 measure_tmp_items.clear();
1253 //TODO:Calculate the measure area for current length and origin
1254 // and use canvas->requestRedraw. In the calculation need a gap for outside text
1255 // maybe this remove the trash lines on measure use
1256 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1257 bool show_in_between = prefs->getBool("/tools/measure/show_in_between", true);
1258 bool all_layers = prefs->getBool("/tools/measure/all_layers", true);
1259 dimension_offset = 70;
1260 Geom::PathVector lineseg;
1261 Geom::Path p;
1262 Geom::Point start_p_doc = start_p * desktop->dt2doc();
1263 Geom::Point end_p_doc = end_p * desktop->dt2doc();
1264 p.start(start_p_doc);
1265 p.appendNew<Geom::LineSegment>(end_p_doc);
1266 lineseg.push_back(p);
1267
1268 double angle = atan2(end_p - start_p);
1269 double baseAngle = 0;
1270
1271 if (explicit_base) {
1272 baseAngle = atan2(explicit_base.get() - start_p);
1273 angle -= baseAngle;
1274 }
1275
1276 std::vector<SPItem*> items;
1277 SPDocument *doc = desktop->getDocument();
1278 Geom::Rect rect(start_p_doc, end_p_doc);
1279 items = doc->getItemsPartiallyInBox(desktop->dkey, rect, false, true, false, true);
1280 Inkscape::LayerModel *layer_model = nullptr;
1281 SPObject *current_layer = nullptr;
1282 if(desktop){
1283 layer_model = desktop->layers;
1284 current_layer = desktop->currentLayer();
1285 }
1286 std::vector<double> intersection_times;
1287 bool only_selected = prefs->getBool("/tools/measure/only_selected", false);
1288 for (std::vector<SPItem*>::const_iterator i=items.begin(); i!=items.end(); ++i) {
1289 SPItem *item = *i;
1290 if (!desktop->getSelection()->includes(*i) && only_selected) {
1291 continue;
1292 }
1293 if(all_layers || (layer_model && layer_model->layerForObject(item) == current_layer)){
1294 if (SP_IS_SHAPE(item)) {
1295 calculate_intersections(desktop, item, lineseg, SP_SHAPE(item)->getCurve(), intersection_times);
1296 } else {
1297 if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
1298 Inkscape::Text::Layout::iterator iter = te_get_layout(item)->begin();
1299 do {
1300 Inkscape::Text::Layout::iterator iter_next = iter;
1301 iter_next.nextGlyph(); // iter_next is one glyph ahead from iter
1302 if (iter == iter_next) {
1303 break;
1304 }
1305
1306 // get path from iter to iter_next:
1307 SPCurve *curve = te_get_layout(item)->convertToCurves(iter, iter_next);
1308 iter = iter_next; // shift to next glyph
1309 if (!curve) {
1310 continue; // error converting this glyph
1311 }
1312 if (curve->is_empty()) { // whitespace glyph?
1313 curve->unref();
1314 continue;
1315 }
1316
1317 calculate_intersections(desktop, item, lineseg, curve, intersection_times);
1318 if (iter == te_get_layout(item)->end()) {
1319 break;
1320 }
1321 } while (true);
1322 }
1323 }
1324 }
1325 }
1326 Glib::ustring unit_name = prefs->getString("/tools/measure/unit");
1327 if (!unit_name.compare("")) {
1328 unit_name = DEFAULT_UNIT_NAME;
1329 }
1330 double scale = prefs->getDouble("/tools/measure/scale", 100.0) / 100.0;
1331 double fontsize = prefs->getDouble("/tools/measure/fontsize", 10.0);
1332 // Normal will be used for lines and text
1333 Geom::Point windowNormal = Geom::unit_vector(Geom::rot90(desktop->d2w(end_p - start_p)));
1334 Geom::Point normal = desktop->w2d(windowNormal);
1335
1336 std::vector<Geom::Point> intersections;
1337 std::sort(intersection_times.begin(), intersection_times.end());
1338 for (double & intersection_time : intersection_times) {
1339 intersections.push_back(lineseg[0].pointAt(intersection_time));
1340 }
1341
1342 if(!show_in_between && intersection_times.size() > 1) {
1343 Geom::Point start = lineseg[0].pointAt(intersection_times[0]);
1344 Geom::Point end = lineseg[0].pointAt(intersection_times[intersection_times.size()-1]);
1345 intersections.clear();
1346 intersections.push_back(start);
1347 intersections.push_back(end);
1348 }
1349 if (!prefs->getBool("/tools/measure/ignore_1st_and_last", true)) {
1350 intersections.insert(intersections.begin(),lineseg[0].pointAt(0));
1351 intersections.push_back(lineseg[0].pointAt(1));
1352 }
1353 std::vector<LabelPlacement> placements;
1354 for (size_t idx = 1; idx < intersections.size(); ++idx) {
1355 LabelPlacement placement;
1356 placement.lengthVal = (intersections[idx] - intersections[idx - 1]).length();
1357 placement.lengthVal = Inkscape::Util::Quantity::convert(placement.lengthVal, "px", unit_name);
1358 placement.offset = dimension_offset / 2;
1359 placement.start = desktop->doc2dt( (intersections[idx - 1] + intersections[idx]) / 2 );
1360 placement.end = placement.start - (normal * placement.offset);
1361
1362 placements.push_back(placement);
1363 }
1364 int precision = prefs->getInt("/tools/measure/precision", 2);
1365 // Adjust positions
1366 repositionOverlappingLabels(placements, desktop, windowNormal, fontsize, precision);
1367 for (auto & place : placements) {
1368 setMeasureCanvasText(false, precision, place.lengthVal * scale, fontsize, unit_name, place.end, 0x0000007f, TEXT_ANCHOR_CENTER, to_item, to_phantom, measure_repr);
1369 }
1370 Geom::Point angleDisplayPt = calcAngleDisplayAnchor(desktop, angle, baseAngle,
1371 start_p, end_p,
1372 fontsize);
1373 {
1374 setMeasureCanvasText(true, precision, Geom::deg_from_rad(angle), fontsize, unit_name, angleDisplayPt, 0x337f337f, TEXT_ANCHOR_CENTER, to_item, to_phantom, measure_repr);
1375 }
1376
1377 {
1378 double totallengthval = (end_p - start_p).length();
1379 totallengthval = Inkscape::Util::Quantity::convert(totallengthval, "px", unit_name);
1380 Geom::Point origin = end_p + desktop->w2d(Geom::Point(3*fontsize, -fontsize));
1381 setMeasureCanvasText(false, precision, totallengthval * scale, fontsize, unit_name, origin, 0x3333337f, TEXT_ANCHOR_LEFT, to_item, to_phantom, measure_repr);
1382 }
1383
1384 if (intersections.size() > 2) {
1385 double totallengthval = (intersections[intersections.size()-1] - intersections[0]).length();
1386 totallengthval = Inkscape::Util::Quantity::convert(totallengthval, "px", unit_name);
1387 Geom::Point origin = desktop->doc2dt((intersections[0] + intersections[intersections.size()-1])/2) + normal * dimension_offset;
1388 setMeasureCanvasText(false, precision, totallengthval * scale, fontsize, unit_name, origin, 0x33337f7f, TEXT_ANCHOR_CENTER, to_item, to_phantom, measure_repr);
1389 }
1390
1391 // Initial point
1392 {
1393 setMeasureCanvasItem(start_p, false, to_phantom, measure_repr);
1394 }
1395
1396 // Now that text has been added, we can add lines and controls so that they go underneath
1397 for (size_t idx = 0; idx < intersections.size(); ++idx) {
1398 setMeasureCanvasItem(desktop->doc2dt(intersections[idx]), to_item, to_phantom, measure_repr);
1399 if(to_guides) {
1400 gchar *cross_number;
1401 if (!prefs->getBool("/tools/measure/ignore_1st_and_last", true)) {
1402 cross_number= g_strdup_printf(_("Crossing %lu"), static_cast<unsigned long>(idx));
1403 } else {
1404 cross_number= g_strdup_printf(_("Crossing %lu"), static_cast<unsigned long>(idx + 1));
1405 }
1406 if (!prefs->getBool("/tools/measure/ignore_1st_and_last", true) && idx == 0) {
1407 setGuide(desktop->doc2dt(intersections[idx]), angle + Geom::rad_from_deg(90), "");
1408 } else {
1409 setGuide(desktop->doc2dt(intersections[idx]), angle + Geom::rad_from_deg(90), cross_number);
1410 }
1411 g_free(cross_number);
1412 }
1413 }
1414 // Since adding goes to the bottom, do all lines last.
1415
1416 // draw main control line
1417 {
1418 setMeasureCanvasControlLine(start_p, end_p, false, to_phantom, CTLINE_PRIMARY, measure_repr);
1419 double length = std::abs((end_p - start_p).length());
1420 Geom::Point anchorEnd = start_p;
1421 anchorEnd[Geom::X] += length;
1422 if (explicit_base) {
1423 anchorEnd *= (Geom::Affine(Geom::Translate(-start_p))
1424 * Geom::Affine(Geom::Rotate(baseAngle))
1425 * Geom::Affine(Geom::Translate(start_p)));
1426 }
1427 setMeasureCanvasControlLine(start_p, anchorEnd, to_item, to_phantom, CTLINE_SECONDARY, measure_repr);
1428 createAngleDisplayCurve(desktop, start_p, end_p, angleDisplayPt, angle, to_phantom, measure_phantom_items, measure_tmp_items, measure_repr);
1429 }
1430
1431 if (intersections.size() > 2) {
1432 setMeasureCanvasControlLine(desktop->doc2dt(intersections[0]) + normal * dimension_offset, desktop->doc2dt(intersections[intersections.size() - 1]) + normal * dimension_offset, to_item, to_phantom, CTLINE_PRIMARY , measure_repr);
1433
1434 setMeasureCanvasControlLine(desktop->doc2dt(intersections[0]), desktop->doc2dt(intersections[0]) + normal * dimension_offset, to_item, to_phantom, CTLINE_PRIMARY , measure_repr);
1435
1436 setMeasureCanvasControlLine(desktop->doc2dt(intersections[intersections.size() - 1]), desktop->doc2dt(intersections[intersections.size() - 1]) + normal * dimension_offset, to_item, to_phantom, CTLINE_PRIMARY , measure_repr);
1437 }
1438
1439 // call-out lines
1440 for (auto & place : placements) {
1441 setMeasureCanvasControlLine(place.start, place.end, to_item, to_phantom, CTLINE_SECONDARY, measure_repr);
1442 }
1443
1444 {
1445 for (size_t idx = 1; idx < intersections.size(); ++idx) {
1446 Geom::Point measure_text_pos = (intersections[idx - 1] + intersections[idx]) / 2;
1447 setMeasureCanvasControlLine(desktop->doc2dt(measure_text_pos), desktop->doc2dt(measure_text_pos) - (normal * dimension_offset / 2), to_item, to_phantom, CTLINE_SECONDARY, measure_repr);
1448 }
1449 }
1450 }
1451
1452 }
1453 }
1454 }
1455
1456 /*
1457 Local Variables:
1458 mode:c++
1459 c-file-style:"stroustrup"
1460 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1461 indent-tabs-mode:nil
1462 fill-column:99
1463 End:
1464 */
1465 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
0 // SPDX-License-Identifier: GPL-2.0-or-later
1 #ifndef SEEN_SP_LIVECODE_CONTEXT_H
2 #define SEEN_SP_LIVECODE_CONTEXT_H
3
4 /*
5 * Our fine measuring tool
6 *
7 * Authors:
8 * Felipe Correa da Silva Sanches <juca@members.fsf.org>
9 * Jabiertxo Arraiza <jabier.arraiza@marker.es>
10 * Copyright (C) 2011 Authors
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15 #include <cstddef>
16 #include <sigc++/sigc++.h>
17 #include "ui/tools/tool-base.h"
18 #include <2geom/point.h>
19 #include "display/canvas-text.h"
20 #include "display/canvas-temporary-item.h"
21 #include "ui/control-manager.h"
22 #include <boost/optional.hpp>
23
24 #define SP_LIVECODE_CONTEXT(obj) (dynamic_cast<Inkscape::UI::Tools::LivecodeTool*>((Inkscape::UI::Tools::ToolBase*)obj))
25 #define SP_IS_LIVECODE_CONTEXT(obj) (dynamic_cast<const Inkscape::UI::Tools::LivecodeTool*>((const Inkscape::UI::Tools::ToolBase*)obj) != NULL)
26
27 class SPKnot;
28
29 namespace Inkscape {
30 namespace UI {
31 namespace Tools {
32
33 class LivecodeTool : public ToolBase {
34 public:
35 LivecodeTool();
36 ~LivecodeTool() override;
37
38 static const std::string prefsPath;
39
40 void finish() override;
41 bool root_handler(GdkEvent* event) override;
42 virtual void showCanvasItems(bool to_guides = false, bool to_item = false, bool to_phantom = false, Inkscape::XML::Node *measure_repr = nullptr);
43 virtual void reverseKnots();
44 virtual void toGuides();
45 virtual void toPhantom();
46 virtual void toMarkDimension();
47 virtual void toItem();
48 virtual void reset();
49 virtual void setMarkers();
50 virtual void setMarker(bool isStart);
51 const std::string& getPrefsPath() override;
52 Geom::Point readMeasurePoint(bool is_start);
53 void showInfoBox(Geom::Point cursor, bool into_groups);
54 void showItemInfoText(Geom::Point pos, gchar *measure_str, double fontsize);
55 void writeMeasurePoint(Geom::Point point, bool is_start);
56 void setGuide(Geom::Point origin, double angle, const char *label);
57 void setPoint(Geom::Point origin, Inkscape::XML::Node *measure_repr);
58 void setLine(Geom::Point start_point,Geom::Point end_point, bool markers, guint32 color, Inkscape::XML::Node *measure_repr = nullptr);
59 void setMeasureCanvasText(bool is_angle, double precision, double amount, double fontsize, Glib::ustring unit_name, Geom::Point position, guint32 background, CanvasTextAnchorPositionEnum text_anchor, bool to_item, bool to_phantom, Inkscape::XML::Node *measure_repr);
60 void setMeasureCanvasItem(Geom::Point position, bool to_item, bool to_phantom, Inkscape::XML::Node *measure_repr);
61 void setMeasureCanvasControlLine(Geom::Point start, Geom::Point end, bool to_item, bool to_phantom, Inkscape::CtrlLineType ctrl_line_type, Inkscape::XML::Node *measure_repr);
62 void setLabelText(const char *value, Geom::Point pos, double fontsize, Geom::Coord angle, guint32 background , Inkscape::XML::Node *measure_repr = nullptr, CanvasTextAnchorPositionEnum text_anchor = TEXT_ANCHOR_CENTER );
63 void knotStartMovedHandler(SPKnot */*knot*/, Geom::Point const &ppointer, guint state);
64 void knotEndMovedHandler(SPKnot */*knot*/, Geom::Point const &ppointer, guint state);
65 void knotClickHandler(SPKnot *knot, guint state);
66 void knotUngrabbedHandler(SPKnot */*knot*/, unsigned int /*state*/);
67 private:
68 SPCanvasItem* grabbed;
69 boost::optional<Geom::Point> explicit_base;
70 boost::optional<Geom::Point> last_end;
71 SPKnot *knot_start;
72 SPKnot *knot_end;
73 gint dimension_offset;
74 Geom::Point start_p;
75 Geom::Point end_p;
76 Geom::Point last_pos;
77 std::vector<SPCanvasItem *> measure_tmp_items;
78 std::vector<SPCanvasItem *> measure_phantom_items;
79 std::vector<SPCanvasItem *> measure_item;
80 double item_width;
81 double item_height;
82 double item_x;
83 double item_y;
84 double item_length;
85 SPItem *over;
86 sigc::connection _knot_start_moved_connection;
87 sigc::connection _knot_start_ungrabbed_connection;
88 sigc::connection _knot_start_click_connection;
89 sigc::connection _knot_end_moved_connection;
90 sigc::connection _knot_end_click_connection;
91 sigc::connection _knot_end_ungrabbed_connection;
92 };
93
94 }
95 }
96 }
97
98 #endif // SEEN_SP_LIVECODE_CONTEXT_H
99
100 /*
101 Local Variables:
102 mode:c++
103 c-file-style:"stroustrup"
104 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
105 indent-tabs-mode:nil
106 fill-column:99
107 End:
108 */
109 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
7979 "/tools/paintbucket",
8080 "/tools/eraser",
8181 "/tools/lpetool",
82 "/tools/livecode",
8283 nullptr
8384 };
8485
107108 N_("<b>Click</b> to paint a bounded area, <b>Shift+click</b> to union the new fill with the current selection, <b>Ctrl+click</b> to change the clicked object's fill and stroke to the current setting."),
108109 N_("<b>Drag</b> to erase."),
109110 N_("Choose a subtool from the toolbar"),
111 N_("This doesn't do much rn."),
110112 };
111113
112114 static int
4242 TOOLS_CONNECTOR,
4343 TOOLS_PAINTBUCKET,
4444 TOOLS_ERASER,
45 TOOLS_LPETOOL
45 TOOLS_LPETOOL,
46 TOOLS_LIVECODE
4647 };
4748
4849 int tools_isactive(SPDesktop *dt, unsigned num);
17111711 case SP_VERB_CONTEXT_LPETOOL:
17121712 tools_switch(dt, TOOLS_LPETOOL);
17131713 break;
1714 case SP_VERB_CONTEXT_LIVECODE:
1715 tools_switch(dt, TOOLS_LIVECODE);
1716 break;
17141717
17151718 case SP_VERB_CONTEXT_SELECT_PREFS:
17161719 prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_SELECTOR);
18001803 g_print ("TODO: Create preferences page for LPETool\n");
18011804 prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_LPETOOL);
18021805 dt->_dlg_mgr->showDialog("InkscapePreferences");
1806 break;
1807 case SP_VERB_CONTEXT_LIVECODE_PREFS:
1808 // @TODO:LIVECODE
1809 // prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_MEASURE);
1810 // dt->_dlg_mgr->showDialog("InkscapePreferences");
18031811 break;
18041812 case SP_VERB_ALIGN_HORIZONTAL_RIGHT_TO_ANCHOR:
18051813 case SP_VERB_ALIGN_HORIZONTAL_LEFT:
28852893 INKSCAPE_ICON("draw-eraser")),
28862894 new ContextVerb(SP_VERB_CONTEXT_LPETOOL, "ToolLPETool", NC_("ContextVerb", "LPE Tool"),
28872895 N_("Do geometric constructions"), "draw-geometry"),
2896 new ContextVerb(SP_VERB_CONTEXT_LIVECODE, "ToolLivecode", NC_("ContextVerb", "Livecode Tool"),
2897 N_("Livecoding tool"), INKSCAPE_ICON("xml-text-new")),
28882898 // Tool prefs
28892899 new ContextVerb(SP_VERB_CONTEXT_SELECT_PREFS, "SelectPrefs", N_("Selector Preferences"),
28902900 N_("Open Preferences for the Selector tool"), nullptr),
29302940 N_("Open Preferences for the Eraser tool"), nullptr),
29312941 new ContextVerb(SP_VERB_CONTEXT_LPETOOL_PREFS, "LPEToolPrefs", N_("LPE Tool Preferences"),
29322942 N_("Open Preferences for the LPETool tool"), nullptr),
2943 new ContextVerb(SP_VERB_CONTEXT_LIVECODE_PREFS, "LivecodeToolPrefs", N_("Livecode Tool Preferences"),
2944 N_("Open Preferences for the Livecode tool"), nullptr),
29332945
29342946 // Zoom
29352947 new ZoomVerb(SP_VERB_ZOOM_IN, "ZoomIn", N_("Zoom In"), N_("Zoom in"), INKSCAPE_ICON("zoom-in")),
216216 SP_VERB_CONTEXT_LPE, /* not really a tool but used for editing LPE parameters on-canvas for example */
217217 SP_VERB_CONTEXT_ERASER,
218218 SP_VERB_CONTEXT_LPETOOL, /* note that this is very different from SP_VERB_CONTEXT_LPE above! */
219 SP_VERB_CONTEXT_LIVECODE,
219220 /* Tool preferences */
220221 SP_VERB_CONTEXT_SELECT_PREFS,
221222 SP_VERB_CONTEXT_NODE_PREFS,
239240 SP_VERB_CONTEXT_PAINTBUCKET_PREFS,
240241 SP_VERB_CONTEXT_ERASER_PREFS,
241242 SP_VERB_CONTEXT_LPETOOL_PREFS,
243 SP_VERB_CONTEXT_LIVECODE_PREFS,
242244
243245 /* Zooming */
244246 SP_VERB_ZOOM_IN,
164164 { "/tools/connector","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
165165 { "/tools/gradient", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
166166 { "/tools/mesh", "mesh_tool", SP_VERB_CONTEXT_MESH, SP_VERB_CONTEXT_MESH_PREFS },
167 { "/tools/livecode", "livecode_tool", SP_VERB_CONTEXT_LIVECODE, SP_VERB_CONTEXT_LIVECODE_PREFS },
167168 { "/tools/dropper", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
168169 { nullptr, nullptr, 0, 0 }
169170 };
210211 SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
211212 { "/tools/lpetool", "lpetool_toolbox", Inkscape::UI::Toolbar::LPEToolbar::create, "LPEToolToolbar",
212213 SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
214 { "/tools/livecode", "livecode_toolbox", Inkscape::UI::Toolbar::EraserToolbar::create, "LivecodeToolbar", // @TODO:livecode
215 SP_VERB_INVALID, nullptr, nullptr},
213216 // If you change TextToolbar here, change it also in desktop-widget.cpp
214217 { "/tools/text", "text_toolbox", Inkscape::UI::Toolbar::TextToolbar::create, "TextToolbar",
215218 SP_VERB_INVALID, nullptr, nullptr},