Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
stroke-style.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/* Authors:
3 * Lauris Kaplinski <lauris@kaplinski.com>
4 * Bryce Harrington <brycehar@bryceharrington.org>
5 * bulia byak <buliabyak@users.sf.net>
6 * Maximilian Albert <maximilian.albert@gmail.com>
7 * Josh Andler <scislac@users.sf.net>
8 * Jon A. Cruz <jon@joncruz.org>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 2001-2005 authors
12 * Copyright (C) 2001 Ximian, Inc.
13 * Copyright (C) 2004 John Cliff
14 * Copyright (C) 2008 Maximilian Albert (gtkmm-ification)
15 *
16 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
17 */
18
19#include "stroke-style.h"
20
21#include <iostream>
22#include <iomanip>
23
24#include <glibmm/i18n.h>
25#include <gtkmm/adjustment.h>
26#include <gtkmm/entry.h>
27#include <gtkmm/grid.h>
28#include <gtkmm/label.h>
29#include <gtkmm/togglebutton.h>
30
31#include "document-undo.h"
32#include "fill-or-stroke.h"
33#include "inkscape.h"
34#include "selection.h"
35
37#include "object/sp-marker.h"
38#include "object/sp-namedview.h"
39#include "object/sp-rect.h"
40#include "object/sp-stop.h"
41#include "object/sp-text.h"
43#include "ui/icon-loader.h"
44#include "ui/icon-names.h"
45#include "ui/pack.h"
46#include "ui/util.h"
49#include "ui/widget/unit-menu.h"
51#include "ui/dialog-events.h"
53#include "widgets/style-utils.h"
54
56
63SPObject* getMarkerObj(gchar const *n, SPDocument *doc)
64{
65 gchar const *p = n;
66 while (*p != '\0' && *p != '#') {
67 p++;
68 }
69
70 if (*p == '\0' || p[1] == '\0') {
71 return nullptr;
72 }
73
74 p++;
75 int c = 0;
76 while (p[c] != '\0' && p[c] != ')') {
77 c++;
78 }
79
80 if (p[c] == '\0') {
81 return nullptr;
82 }
83
84 gchar* b = g_strdup(p);
85 b[c] = '\0';
86
87 // FIXME: get the document from the object and let the caller pass it in
88 SPObject *marker = doc->getObjectById(b);
89
90 g_free(b);
91 return marker;
92}
93
94namespace Inkscape::UI::Widget {
95
100static Gtk::Label *spw_label(Gtk::Grid *table, const gchar *label_text, int col, int row,
101 Gtk::Widget* target)
102{
103 auto const label_widget = Gtk::make_managed<Gtk::Label>();
104 g_assert(label_widget != nullptr);
105 if (target != nullptr) {
106 label_widget->set_text_with_mnemonic(label_text);
107 label_widget->set_mnemonic_widget(*target);
108 } else {
109 label_widget->set_text(label_text);
110 }
111 label_widget->set_visible(true);
112
113 label_widget->set_halign(Gtk::Align::START);
114 label_widget->set_valign(Gtk::Align::CENTER);
115 label_widget->set_margin_start(4);
116 label_widget->set_margin_end(4);
117
118 table->attach(*label_widget, col, row, 1, 1);
119
120 return label_widget;
121}
122
127static Gtk::Box *spw_hbox(Gtk::Grid *table, int width, int col, int row)
128{
129 /* Create a new hbox with a 4-pixel spacing between children */
130 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
131 g_assert(hb != nullptr);
132 hb->set_visible(true);
133 hb->set_hexpand();
134 hb->set_halign(Gtk::Align::FILL);
135 hb->set_valign(Gtk::Align::CENTER);
136 table->attach(*hb, col, row, width, 1);
137 return hb;
138}
139
149 char const *icon,
150 StrokeStyleButtonType button_type,
151 gchar const *stroke_style)
152 :
153 button_type(button_type),
154 stroke_style(stroke_style)
155{
156 if (!grp) {
157 grp = this;
158 } else {
159 set_group(*grp);
160 }
161 set_visible(true);
162
163 auto px = Gtk::manage(sp_get_icon_image(icon, Gtk::IconSize::NORMAL));
164 g_assert(px != nullptr);
165 px->set_visible(true);
166 set_child(*px);
167}
168
169std::vector<double> parse_pattern(const Glib::ustring& input) {
170 std::vector<double> output;
171 if (input.empty()) return output;
172
173 std::istringstream stream(input.c_str());
174 while (stream) {
175 double val;
176 stream >> val;
177 if (stream) {
178 output.push_back(val);
179 }
180 }
181
182 return output;
183}
184
186 Gtk::Box(),
188 widthSpin(),
189 unitSelector(),
190 joinMiter(),
191 joinRound(),
192 joinBevel(),
193 capButt(),
194 capRound(),
195 capSquare(),
196 dashSelector(),
197 update(false),
198 desktop(nullptr),
202 _old_unit(nullptr)
203{
204 set_name("StrokeSelector");
205 table = Gtk::make_managed<Gtk::Grid>();
206 table->set_margin(4);
207 table->set_row_spacing(4);
208 table->set_hexpand(false);
209 table->set_halign(Gtk::Align::CENTER);
210 table->set_visible(true);
211 append(*table);
212
213 Gtk::Box *hb;
214 gint i = 0;
215
216 //spw_label(t, C_("Stroke width", "_Width:"), 0, i);
217
218 hb = spw_hbox(table, 3, 1, i);
219
220// TODO: when this is gtkmmified, use a ScalarUnit instead of the separate
221// spinbutton and unit selector for stroke width. In sp_stroke_style_line_update, use
222// setHundredPercent to remember the averaged width corresponding to 100%. Then the
223// stroke_width_set_unit will be removed (because ScalarUnit takes care of conversions itself)
224 widthAdj = Gtk::Adjustment::create(1.0, 0.0, 1000.0, 0.1, 10.0, 0.0);
225 widthSpin = Gtk::make_managed<SpinButton>(widthAdj, 0.1, 3);
226 widthSpin->set_tooltip_text(_("Stroke width"));
227 widthSpin->set_visible(true);
228 spw_label(table, C_("Stroke width", "_Width"), 0, i, widthSpin);
229
231
232 UI::pack_start(*hb, *widthSpin, false, false);
233 unitSelector = Gtk::make_managed<UnitMenu>();
235 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
236
237 unitSelector->addUnit(*Util::UnitTable::get().getUnit("%"));
238 _hairline_item = unitSelector->append(_("Hairline"));
240 if (desktop) {
243 }
245 unitSelector->signal_changed().connect(sigc::mem_fun(*this, &StrokeStyle::unitChangedCB));
246 unitSelector->set_visible(true);
247
248 UI::pack_start(*hb, *unitSelector, FALSE, FALSE);
249 widthAdj->signal_value_changed().connect(sigc::mem_fun(*this, &StrokeStyle::setStrokeWidth));
250
251 i++;
252
253 /* Dash */
254 spw_label(table, _("Dashes"), 0, i, nullptr); //no mnemonic for now
255 //decide what to do:
256 // implement a set_mnemonic_source function in the
257 // DashSelector class, so that we do not have to
258 // expose any of the underlying widgets?
259 dashSelector = Gtk::make_managed<DashSelector>();
260 dashSelector->changed_signal.connect([this](){
262 return;
263 }
268 _editing_dash_pattern = false;
269 });
270 table->attach(*dashSelector, 1, i, 3, 1);
271
272 i++;
273
274 _pattern_entry = Gtk::make_managed<Gtk::Entry>();
275 _pattern_entry->signal_changed().connect([this](){
277 return;
278 }
280 update = true;
281 auto pattern = parse_pattern(_pattern_entry->get_text());
283 update = false;
285 _editing_dash_pattern = false;
286 });
287 table->attach(*_pattern_entry, 1, i, 4, 1);
288
289 _pattern_label = spw_label(table, _("_Pattern"), 0, i, _pattern_entry);
290 _pattern_label->set_tooltip_text(_("Repeating \"dash gap ...\" pattern"));
291
292 i++;
293
294 /* Drop down marker selectors*/
295 // TRANSLATORS: Path markers are an SVG feature that allows you to attach arbitrary shapes
296 // (arrowheads, bullets, faces, whatever) to the start, end, or middle nodes of a path.
297
298 spw_label(table, _("Markers"), 0, i, nullptr);
299
300 hb = spw_hbox(table, 1, 1, i);
301 i++;
302
303 startMarkerCombo = Gtk::make_managed<MarkerComboBox>("marker-start", SP_MARKER_LOC_START);
304 startMarkerCombo->set_tooltip_text(_("Start Markers are drawn on the first node of a path or shape"));
307 startMarkerCombo->set_visible(true);
308
309 UI::pack_start(*hb, *startMarkerCombo, true, true);
310
311 midMarkerCombo = Gtk::make_managed<MarkerComboBox>("marker-mid", SP_MARKER_LOC_MID);
312 midMarkerCombo->set_tooltip_text(_("Mid Markers are drawn on every node of a path or shape except the first and last nodes"));
315 midMarkerCombo->set_visible(true);
316
317 UI::pack_start(*hb, *midMarkerCombo, true, true);
318
319 endMarkerCombo = Gtk::make_managed<MarkerComboBox>("marker-end", SP_MARKER_LOC_END);
320 endMarkerCombo->set_tooltip_text(_("End Markers are drawn on the last node of a path or shape"));
323 endMarkerCombo->set_visible(true);
324
325 UI::pack_start(*hb, *endMarkerCombo, true, true);
326 i++;
327
328 /* Join type */
329 // TRANSLATORS: The line join style specifies the shape to be used at the
330 // corners of paths. It can be "miter", "round" or "bevel".
331 spw_label(table, _("Join"), 0, i, nullptr);
332
333 hb = spw_hbox(table, 3, 1, i);
334
335 Gtk::ToggleButton *joinGrp = nullptr;
336
337 joinBevel = makeRadioButton(joinGrp, INKSCAPE_ICON("stroke-join-bevel"),
338 hb, STROKE_STYLE_BUTTON_JOIN, "bevel");
339
340 // TRANSLATORS: Bevel join: joining lines with a blunted (flattened) corner.
341 // For an example, draw a triangle with a large stroke width and modify the
342 // "Join" option (in the Fill and Stroke dialog).
343 joinBevel->set_tooltip_text(_("Bevel join"));
344
345 joinRound = makeRadioButton(joinGrp, INKSCAPE_ICON("stroke-join-round"),
346 hb, STROKE_STYLE_BUTTON_JOIN, "round");
347
348 // TRANSLATORS: Round join: joining lines with a rounded corner.
349 // For an example, draw a triangle with a large stroke width and modify the
350 // "Join" option (in the Fill and Stroke dialog).
351 joinRound->set_tooltip_text(_("Round join"));
352
353 joinMiter = makeRadioButton(joinGrp, INKSCAPE_ICON("stroke-join-miter"),
354 hb, STROKE_STYLE_BUTTON_JOIN, "miter");
355
356 // TRANSLATORS: Miter join: joining lines with a sharp (pointed) corner.
357 // For an example, draw a triangle with a large stroke width and modify the
358 // "Join" option (in the Fill and Stroke dialog).
359 joinMiter->set_tooltip_text(_("Miter join"));
360
361 /* Miterlimit */
362 // TRANSLATORS: Miter limit: only for "miter join", this limits the length
363 // of the sharp "spike" when the lines connect at too sharp an angle.
364 // When two line segments meet at a sharp angle, a miter join results in a
365 // spike that extends well beyond the connection point. The purpose of the
366 // miter limit is to cut off such spikes (i.e. convert them into bevels)
367 // when they become too long.
368 //spw_label(t, _("Miter _limit:"), 0, i);
369 miterLimitAdj = Gtk::Adjustment::create(4.0, 0.0, 100000.0, 0.1, 10.0, 0.0);
370 miterLimitSpin = Gtk::make_managed<SpinButton>(miterLimitAdj, 0.1, 2);
371 miterLimitSpin->set_tooltip_text(_("Maximum length of the miter (in units of stroke width)"));
372 miterLimitSpin->set_width_chars(6);
373 miterLimitSpin->set_visible(true);
375
376 UI::pack_start(*hb, *miterLimitSpin, false, false);
377 miterLimitAdj->signal_value_changed().connect(sigc::mem_fun(*this, &StrokeStyle::setStrokeMiter));
378
379 i++;
380
381 /* Cap type */
382 // TRANSLATORS: cap type specifies the shape for the ends of lines
383 //spw_label(t, _("_Cap:"), 0, i);
384 spw_label(table, _("Cap"), 0, i, nullptr);
385
386 hb = spw_hbox(table, 3, 1, i);
387
388 Gtk::ToggleButton *capGrp = nullptr;
389
390 capButt = makeRadioButton(capGrp, INKSCAPE_ICON("stroke-cap-butt"),
391 hb, STROKE_STYLE_BUTTON_CAP, "butt");
392
393 // TRANSLATORS: Butt cap: the line shape does not extend beyond the end point
394 // of the line; the ends of the line are square
395 capButt->set_tooltip_text(_("Butt cap"));
396
397 capRound = makeRadioButton(capGrp, INKSCAPE_ICON("stroke-cap-round"),
398 hb, STROKE_STYLE_BUTTON_CAP, "round");
399
400 // TRANSLATORS: Round cap: the line shape extends beyond the end point of the
401 // line; the ends of the line are rounded
402 capRound->set_tooltip_text(_("Round cap"));
403
404 capSquare = makeRadioButton(capGrp, INKSCAPE_ICON("stroke-cap-square"),
405 hb, STROKE_STYLE_BUTTON_CAP, "square");
406
407 // TRANSLATORS: Square cap: the line shape extends beyond the end point of the
408 // line; the ends of the line are square
409 capSquare->set_tooltip_text(_("Square cap"));
410
411 i++;
412
413 /* Paint order */
414 // TRANSLATORS: Paint order determines the order the 'fill', 'stroke', and 'markers are painted.
415 spw_label(table, _("Order"), 0, i, nullptr);
416
417 hb = spw_hbox(table, 4, 1, i);
418
419 Gtk::ToggleButton *paintOrderGrp = nullptr;
420
421 paintOrderFSM = makeRadioButton(paintOrderGrp, INKSCAPE_ICON("paint-order-fsm"),
422 hb, STROKE_STYLE_BUTTON_ORDER, "normal");
423 paintOrderFSM->set_tooltip_text(_("1.Fill, 2.Stroke, 3.Markers"));
424
425 paintOrderSFM = makeRadioButton(paintOrderGrp, INKSCAPE_ICON("paint-order-sfm"),
426 hb, STROKE_STYLE_BUTTON_ORDER, "stroke fill markers");
427 paintOrderSFM->set_tooltip_text(_("1.Stroke, 2.Fill, 3.Markers"));
428
429 paintOrderFMS = makeRadioButton(paintOrderGrp, INKSCAPE_ICON("paint-order-fms"),
430 hb, STROKE_STYLE_BUTTON_ORDER, "fill markers stroke");
431 paintOrderFMS->set_tooltip_text(_("1.Fill, 2.Markers, 3.Stroke"));
432
433 i++;
434
435 hb = spw_hbox(table, 4, 1, i);
436
437 paintOrderMFS = makeRadioButton(paintOrderGrp, INKSCAPE_ICON("paint-order-mfs"),
438 hb, STROKE_STYLE_BUTTON_ORDER, "markers fill stroke");
439 paintOrderMFS->set_tooltip_text(_("1.Markers, 2.Fill, 3.Stroke"));
440
441 paintOrderSMF = makeRadioButton(paintOrderGrp, INKSCAPE_ICON("paint-order-smf"),
442 hb, STROKE_STYLE_BUTTON_ORDER, "stroke markers fill");
443 paintOrderSMF->set_tooltip_text(_("1.Stroke, 2.Markers, 3.Fill"));
444
445 paintOrderMSF = makeRadioButton(paintOrderGrp, INKSCAPE_ICON("paint-order-msf"),
446 hb, STROKE_STYLE_BUTTON_ORDER, "markers stroke fill");
447 paintOrderMSF->set_tooltip_text(_("1.Markers, 2.Stroke, 3.Fill"));
448
449 i++;
450}
451
455
457{
458 if (this->desktop != desktop) {
459
460 if (this->desktop) {
462 }
463 this->desktop = desktop;
464
465 if (!desktop) {
466 _handleDocumentReplaced(nullptr, nullptr);
467 return;
468 }
469
472
474
475 updateLine();
476 }
477}
478
480{
482 combo->setDocument(document);
483 }
484}
485
486
500StrokeStyle::makeRadioButton(Gtk::ToggleButton *&grp,
501 char const *icon,
502 Gtk::Box *hb,
503 StrokeStyleButtonType button_type,
504 gchar const *stroke_style)
505{
506 g_assert(icon != nullptr);
507 g_assert(hb != nullptr);
508
509 auto const tb = Gtk::make_managed<StrokeStyleButton>(grp, icon, button_type, stroke_style);
510 UI::pack_start(*hb, *tb, false, false);
511 tb->signal_toggled().connect(sigc::bind(
512 sigc::ptr_fun(&StrokeStyle::buttonToggledCB), tb, this));
513 return tb;
514}
515
517{
518 SPDesktop *desktop = this->desktop;
519
520 if (desktop) {
521 set_active_tool(desktop, "Marker");
523
524 if(mt) {
525 mt->editMarkerMode = _editMarkerMode;
527 }
528 }
529}
530
531
536
543{
545 return;
546 }
547
548 SPDocument *document = desktop->getDocument();
549 if (!document) {
550 return;
551 }
552
553 // Get marker ID; could be empty (to remove marker)
554 std::string marker = marker_combo->get_active_marker_uri();
555
556 update = true;
557
559 gchar const *combo_id = marker_combo->get_id();
560 sp_repr_css_set_property(css, combo_id, marker.c_str());
561
562 for (auto item : desktop->getSelection()->items()) {
563 if (!is<SPShape>(item)) {
564 continue;
565 }
566 if (Inkscape::XML::Node* selrepr = item->getRepr()) {
567 sp_repr_css_change_recursive(selrepr, css, "style");
568 }
569
570 item->requestModified(SP_OBJECT_MODIFIED_FLAG);
571 item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
572 // perform update to make sure any previously referenced markers are released,
573 // so they can be collected by DocumentUndo::done collect orphans
574 document->ensureUpToDate();
575
576 DocumentUndo::done(document, _("Set markers"), INKSCAPE_ICON("dialog-fill-and-stroke"));
577 }
578
579 // edit marker mode - update
580 if (auto mt = dynamic_cast<Inkscape::UI::Tools::MarkerTool*>(desktop->getTool())) {
581 mt->editMarkerMode = which;
582 mt->selection_changed(desktop->getSelection());
583 }
584
586 css = nullptr;
587
588 update = false;
589};
590
596{
597 Inkscape::Util::Unit const *new_unit = unitSelector->getUnit();
598
599 if (_old_unit == new_unit)
600 return;
601
602 // If the unit selector is set to hairline, don't do the normal conversion.
603 if (isHairlineSelected()) {
604 // Force update in setStrokeWidth
605 _old_unit = new_unit;
606 _last_width = -1;
608 return;
609 }
610
612 // Prevent update in setStrokeWidth
613 _last_width = 100.0;
614 widthSpin->set_value(100);
615 } else {
616 // Remove the non-scaling-stroke effect and the hairline extensions
617 if (!update) {
619 sp_repr_css_unset_property(css, "vector-effect");
620 sp_repr_css_unset_property(css, "-inkscape-stroke");
623 css = nullptr;
624 DocumentUndo::done(desktop->getDocument(), _("Remove hairline stroke"),
625 INKSCAPE_ICON("dialog-fill-and-stroke"));
626 }
628 // Prevent update of unit (inf-loop) in updateLine
629 _old_unit = new_unit;
630 // Going from % to any other unit means our widthSpin is completely invalid.
631 updateLine();
632 } else {
633 // Scale the value and record the old_unit
634 widthSpin->set_value(Inkscape::Util::Quantity::convert(widthSpin->get_value(), _old_unit, new_unit));
635 }
636 }
637 _old_unit = new_unit;
638}
639
644void
646{
647 // We care deeply about only updating when the style is updated
648 // if we update on other flags, we slow inkscape down when dragging
649 if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG)) {
650 updateLine();
651 }
652}
653
658void
663
669std::vector<double>
671{
672 auto prefs = Inkscape::Preferences::get();
673
674 std::vector<double> ret;
675 size_t len = style->stroke_dasharray.values.size();
676
677 double scaledash = 1.0;
678 if (prefs->getBool("/options/dash/scale", true) && style->stroke_width.computed) {
679 scaledash = style->stroke_width.computed;
680 }
681
682 offset = style->stroke_dashoffset.value / scaledash;
683 for (unsigned i = 0; i < len; i++) {
684 ret.push_back(style->stroke_dasharray.values[i].value / scaledash);
685 }
686 return ret;
687}
688
692void
700
701void StrokeStyle::update_dash_entry(const std::vector<double> &dash_pattern)
702{
703 if (_editing_dash_pattern || contains_focus(*_pattern_entry)) { /* The GtkText object has focus. The focus test is required
704 as the StokeStyle widget is updated after all changed
705 are made (unnecessarily via selectionModifiedCB()).
706 Without this test, the cursor is placed at the beginning
707 of the GtkEntry after each character is typed. */
708 return;
709 }
710
711 std::ostringstream ost;
712 for (auto d : dash_pattern) {
713 ost << d << ' ';
714 }
715 _pattern_entry->set_text(ost.str().c_str());
716
717 if (!dash_pattern.empty()) {
718 _pattern_label->set_visible(true);
719 _pattern_entry->set_visible(true);
720 }
721 else {
722 _pattern_label->set_visible(false);
723 _pattern_entry->set_visible(false);
724 }
725}
726
730void
731StrokeStyle::setJoinType (unsigned const jointype)
732{
733 Gtk::ToggleButton *tb = nullptr;
734 switch (jointype) {
736 tb = joinMiter;
737 break;
739 tb = joinRound;
740 break;
742 tb = joinBevel;
743 break;
744 default:
745 // Should not happen
746 std::cerr << "StrokeStyle::setJoinType(): Invalid value: " << jointype << std::endl;
747 tb = joinMiter;
748 break;
749 }
750 setJoinButtons(tb);
751}
752
756void
757StrokeStyle::setCapType (unsigned const captype)
758{
759 Gtk::ToggleButton *tb = nullptr;
760 switch (captype) {
762 tb = capButt;
763 break;
765 tb = capRound;
766 break;
768 tb = capSquare;
769 break;
770 default:
771 // Should not happen
772 std::cerr << "StrokeStyle::setCapType(): Invalid value: " << captype << std::endl;
773 tb = capButt;
774 break;
775 }
776 setCapButtons(tb);
777}
778
782void
783StrokeStyle::setPaintOrder (gchar const *paint_order)
784{
785 Gtk::ToggleButton *tb = paintOrderFSM;
786
787 SPIPaintOrder temp;
788 temp.read( paint_order );
789
790 if (temp.layer[0] != SP_CSS_PAINT_ORDER_NORMAL) {
791
792 if (temp.layer[0] == SP_CSS_PAINT_ORDER_FILL) {
793 if (temp.layer[1] == SP_CSS_PAINT_ORDER_STROKE) {
794 tb = paintOrderFSM;
795 } else {
796 tb = paintOrderFMS;
797 }
798 } else if (temp.layer[0] == SP_CSS_PAINT_ORDER_STROKE) {
799 if (temp.layer[1] == SP_CSS_PAINT_ORDER_FILL) {
800 tb = paintOrderSFM;
801 } else {
802 tb = paintOrderSMF;
803 }
804 } else {
805 if (temp.layer[1] == SP_CSS_PAINT_ORDER_STROKE) {
806 tb = paintOrderMSF;
807 } else {
808 tb = paintOrderMFS;
809 }
810 }
811
812 }
814}
815
820void
822{
823 if (update) {
824 return;
825 }
826
827 auto *widg = get_parent()->get_parent()->get_parent()->get_parent();
828 auto dialogbase = dynamic_cast<Inkscape::UI::Dialog::DialogBase*>(widg);
829 if (dialogbase && !dialogbase->getShowing()) {
830 return;
831 }
832
833 update = true;
834
835 Inkscape::Selection *sel = desktop ? desktop->getSelection() : nullptr;
836
837 if (!sel || sel->isEmpty()) {
838 // Nothing selected, grey-out all controls in the stroke-style dialog
839 table->set_sensitive(false);
840
841 update = false;
842
843 return;
844 }
845
846 FillOrStroke kind = STROKE;
847
848 // create temporary style
849 SPStyle query(SP_ACTIVE_DOCUMENT);
850 // query into it
856
857 SPIPaint &targPaint = *query.getFillOrStroke(kind == FILL);
858
859 {
860 table->set_sensitive(true);
861 widthSpin->set_sensitive(true);
862
863 if (result_sw == QUERY_STYLE_MULTIPLE_AVERAGED) {
864 unitSelector->setUnit("%");
865 } else if (query.stroke_extensions.hairline) {
866 unitSelector->set_selected(_hairline_item);
867 } else {
868 // same width, or only one object; no sense to keep percent, switch to absolute
869 Inkscape::Util::Unit const *tempunit = unitSelector->getUnit();
870 if (tempunit->type != Inkscape::Util::UNIT_TYPE_LINEAR) {
872 }
873 }
874
876
877 if (query.stroke_extensions.hairline) {
878 widthSpin->set_sensitive(false);
879 widthAdj->set_value(1);
880 } else if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) {
881 double avgwidth = Inkscape::Util::Quantity::convert(query.stroke_width.computed, "px", unit);
882 widthAdj->set_value(avgwidth);
883 } else {
884 widthAdj->set_value(100);
885 }
886
887 // if none of the selected objects has a stroke, than quite some controls should be disabled
888 // These options should also be disabled for hairlines, since they don't make sense for
889 // 0-width lines.
890 // The markers might still be shown though, so marker and stroke-width widgets stay enabled
891 bool is_enabled = (result_sw != QUERY_STYLE_NOTHING) && !targPaint.isNoneSet()
892 && !query.stroke_extensions.hairline;
893 joinMiter->set_sensitive(is_enabled);
894 joinRound->set_sensitive(is_enabled);
895 joinBevel->set_sensitive(is_enabled);
896
897 miterLimitSpin->set_sensitive(is_enabled);
898
899 capButt->set_sensitive(is_enabled);
900 capRound->set_sensitive(is_enabled);
901 capSquare->set_sensitive(is_enabled);
902
903 dashSelector->set_sensitive(is_enabled);
904 _pattern_entry->set_sensitive(is_enabled);
905 }
906
907 if (result_ml != QUERY_STYLE_NOTHING)
908 miterLimitAdj->set_value(query.stroke_miterlimit.value); // TODO: reflect averagedness?
909
911 if (! is_query_style_updateable(result_join)) {
912 setJoinType(query.stroke_linejoin.value);
913 } else {
914 setJoinButtons(nullptr);
915 }
916
917 if (! is_query_style_updateable(result_cap)) {
918 setCapType (query.stroke_linecap.value);
919 } else {
920 setCapButtons(nullptr);
921 }
922
923 if (! is_query_style_updateable(result_order)) {
924 setPaintOrder (query.paint_order.value);
925 } else {
926 setPaintOrder (nullptr);
927 }
928
929 std::vector<SPItem*> const objects(sel->items().begin(), sel->items().end());
930 if (objects.size()) {
931 SPObject *const object = objects[0];
932 SPStyle *const style = object->style;
933 /* Markers */
934 updateAllMarkers(objects, true); // FIXME: make this desktop query too
935
936 /* Dash */
937 setDashSelectorFromStyle(dashSelector, style); // FIXME: make this desktop query too
938 }
939 table->set_sensitive(true);
940
941 update = false;
942}
943
947void
949 int ndash, const double *dash, double offset,
950 double scale)
951{
952 if (ndash > 0) {
954 for (int i = 0; i < ndash; i++) {
955 osarray << dash[i] * scale;
956 if (i < (ndash - 1)) {
957 osarray << ",";
958 }
959 }
960 sp_repr_css_set_property(css, "stroke-dasharray", osarray.str().c_str());
961
963 osoffset << offset * scale;
964 sp_repr_css_set_property(css, "stroke-dashoffset", osoffset.str().c_str());
965 } else {
966 sp_repr_css_set_property(css, "stroke-dasharray", "none");
967 sp_repr_css_set_property(css, "stroke-dashoffset", nullptr);
968 }
969}
970
971static inline double calcScaleLineWidth(const double width_typed, SPItem *const item, Inkscape::Util::Unit const *const unit)
972{
973 if (unit->abbr == "%") {
974 auto scale = item->i2doc_affine().descrim();;
975 const gdouble old_w = item->style->stroke_width.computed;
976 return (old_w * width_typed / 100) * scale;
977 } else if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) {
978 return Inkscape::Util::Quantity::convert(width_typed, unit, "px");
979 }
980 return width_typed;
981}
982
987{
988 double width_typed = widthAdj->get_value();
989
990 // Don't change the selection if an update is happening,
991 // but also store the value for later comparison.
992 if (update || fabs(_last_width - width_typed) < 1E-6) {
993 _last_width = width_typed;
994 return;
995 }
996 update = true;
997
998 auto prefs = Inkscape::Preferences::get();
999 auto unit = unitSelector->getUnit();
1000
1002 if (isHairlineSelected()) {
1003 /* For renderers that don't understand -inkscape-stroke:hairline, fall back to 1px non-scaling */
1004 width_typed = 1;
1005 sp_repr_css_set_property(css, "vector-effect", "non-scaling-stroke");
1006 sp_repr_css_set_property(css, "-inkscape-stroke", "hairline");
1007 } else {
1008 sp_repr_css_unset_property(css, "vector-effect");
1009 sp_repr_css_unset_property(css, "-inkscape-stroke");
1010 }
1011
1012 for (auto item : desktop->getSelection()->items()) {
1013 const double width = calcScaleLineWidth(width_typed, item, unit);
1014 sp_repr_css_set_property_double(css, "stroke-width", width);
1015
1016 if (prefs->getBool("/options/dash/scale", true)) {
1017 // This will read the old stroke-width to un-scale the pattern.
1018 double offset = 0;
1019 auto dash = getDashFromStyle(item->style, offset);
1020 setScaledDash(css, dash.size(), dash.data(), offset, width);
1021 }
1023 }
1025
1027 DocumentUndo::done(desktop->getDocument(), _("Set stroke width"),
1028 INKSCAPE_ICON("dialog-fill-and-stroke"));
1029
1030 if (unit->abbr == "%") {
1031 // reset to 100 percent
1032 _last_width = 100.0;
1033 widthAdj->set_value(100.0);
1034 } else {
1035 _last_width = width_typed;
1036 }
1037 update = false;
1038}
1039
1044{
1045 if (update) {
1046 return;
1047 }
1048 update = true;
1049
1050 auto document = desktop->getDocument();
1051 auto prefs = Inkscape::Preferences::get();
1052
1053 const auto& dash = dashSelector->get_dash_pattern();
1054 double offset = dashSelector->get_offset();
1055
1057 for (auto item : desktop->getSelection()->items()) {
1058 double scale = item->i2doc_affine().descrim();
1059 if(prefs->getBool("/options/dash/scale", true)) {
1060 scale = item->style->stroke_width.computed * scale;
1061 }
1062
1063 setScaledDash(css, dash.size(), dash.data(), offset, scale);
1065 }
1067
1069 DocumentUndo::done(document, _("Set stroke dash"),
1070 INKSCAPE_ICON("dialog-fill-and-stroke"));
1071 update = false;
1072}
1073
1078{
1079 if (update) return;
1080 update = true;
1081
1083 auto const value = miterLimitAdj->get_value();
1084 sp_repr_css_set_property_double(css, "stroke-miterlimit", value);
1085
1086 for (auto item : desktop->getSelection()->items()) {
1088 }
1091 DocumentUndo::done(desktop->getDocument(), _("Set stroke miter"),
1092 INKSCAPE_ICON("dialog-fill-and-stroke"));
1093 update = false;
1094}
1095
1100bool
1102{
1103 return unitSelector->get_selected() == _hairline_item;
1104}
1105
1106
1115{
1116 if (spw->update) {
1117 return;
1118 }
1119
1120 if (tb->get_active()) {
1122 spw->miterLimitSpin->set_sensitive(!strcmp(tb->get_stroke_style(), "miter"));
1123 }
1124
1125 /* TODO: Create some standardized method */
1127
1128 switch (tb->get_button_type()) {
1130 sp_repr_css_set_property(css, "stroke-linejoin", tb->get_stroke_style());
1132 spw->setJoinButtons(tb);
1133 break;
1135 sp_repr_css_set_property(css, "stroke-linecap", tb->get_stroke_style());
1137 spw->setCapButtons(tb);
1138 break;
1140 sp_repr_css_set_property(css, "paint-order", tb->get_stroke_style());
1142 //spw->setPaintButtons(tb);
1143 }
1144
1146 css = nullptr;
1147
1148 DocumentUndo::done(spw->desktop->getDocument(), _("Set stroke style"), INKSCAPE_ICON("dialog-fill-and-stroke"));
1149 }
1150}
1151
1155void
1156StrokeStyle::setJoinButtons(Gtk::ToggleButton *active)
1157{
1158 joinMiter->set_active(active == joinMiter);
1159 miterLimitSpin->set_sensitive(active == joinMiter && !isHairlineSelected());
1160 joinRound->set_active(active == joinRound);
1161 joinBevel->set_active(active == joinBevel);
1162}
1163
1167void
1168StrokeStyle::setCapButtons(Gtk::ToggleButton *active)
1169{
1170 capButt->set_active(active == capButt);
1171 capRound->set_active(active == capRound);
1172 capSquare->set_active(active == capSquare);
1173}
1174
1175
1179void
1180StrokeStyle::setPaintOrderButtons(Gtk::ToggleButton *active)
1181{
1182 paintOrderFSM->set_active(active == paintOrderFSM);
1183 paintOrderSFM->set_active(active == paintOrderSFM);
1184 paintOrderFMS->set_active(active == paintOrderFMS);
1185 paintOrderMFS->set_active(active == paintOrderMFS);
1186 paintOrderSMF->set_active(active == paintOrderSMF);
1187 paintOrderMSF->set_active(active == paintOrderMSF);
1188}
1189
1190
1195static void buildGroupedItemList(SPObject *element, std::vector<SPObject*> &simple_list)
1196{
1197 if (is<SPGroup>(element)) {
1198 for (SPObject *i = element->firstChild(); i; i = i->getNext()) {
1199 buildGroupedItemList(i, simple_list);
1200 }
1201 } else {
1202 simple_list.push_back(element);
1203 }
1204}
1205
1206
1211void
1212StrokeStyle::updateAllMarkers(std::vector<SPItem*> const &objects, bool skip_undo)
1213{
1214 struct { MarkerComboBox *key; int loc; } const keyloc[] = {
1218 };
1219
1220 bool all_texts = true;
1221
1222 auto simplified_list = std::vector<SPObject *>();
1223 for (SPItem *item : objects) {
1224 buildGroupedItemList(item, simplified_list);
1225 }
1226
1227 for (SPObject *object : simplified_list) {
1228 if (!is<SPText>(object)) {
1229 all_texts = false;
1230 break;
1231 }
1232 }
1233
1234 // We show markers of the last object in the list only
1235 // FIXME: use the first in the list that has the marker of each type, if any
1236
1237 for (auto const &markertype : keyloc) {
1238 // For all three marker types,
1239
1240 // find the corresponding combobox item
1241 MarkerComboBox *combo = markertype.key;
1242
1243 // Quit if we're in update state
1244 if (combo->in_update()) {
1245 return;
1246 }
1247
1248 // Per SVG spec, text objects cannot have markers; disable combobox if only texts are selected
1249 // They should also be disabled for hairlines, since scaling against a 0-width line doesn't
1250 // make sense.
1251 combo->set_sensitive(!all_texts && !isHairlineSelected());
1252
1253 SPObject *marker = nullptr;
1254
1255 if (!all_texts && !isHairlineSelected()) {
1256 for (SPObject *object : simplified_list) {
1257 char const *value = object->style->marker_ptrs[markertype.loc]->value();
1258
1259 // If the object has this type of markers,
1260 if (value == nullptr)
1261 continue;
1262
1263 // Extract the name of the marker that the object uses
1264 marker = getMarkerObj(value, object->document);
1265 }
1266 }
1267
1268 // Scroll the combobox to that marker
1269 combo->set_current(marker);
1270 }
1271}
1272
1273} // namespace Inkscape::UI::Widget
1274
1275/*
1276 Local Variables:
1277 mode:c++
1278 c-file-style:"stroustrup"
1279 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1280 indent-tabs-mode:nil
1281 fill-column:99
1282 End:
1283*/
1284// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
void set_active_tool(InkscapeWindow *win, Glib::ustring const &tool)
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
A thin wrapper around std::ostringstream, but writing floating point numbers in the format required b...
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
bool isEmpty()
Returns true if no items are selected.
static Preferences * get()
Access the singleton Preferences object.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
DialogBase is the base class for the dialog system.
Definition dialog-base.h:42
void selection_changed(Inkscape::Selection *selection)
void set_dash_pattern(const std::vector< double > &dash, double offset)
const std::vector< double > & get_dash_pattern()
sigc::signal< void()> changed_signal
unsigned int append(const Glib::ustring &item)
ComboBox-like class for selecting stroke markers.
std::string get_active_marker_uri()
Return a uri string representing the current selected marker used for setting the marker style in the...
sigc::connection connect_changed(sigc::slot< void()> slot)
sigc::connection connect_edit(sigc::slot< void()> slot)
void set_current(SPObject *marker)
Sets the current marker in the marker combobox.
void setUnitMenu(UnitMenu *unit_menu)
Definition spinbutton.h:67
A custom radio check-button for setting the stroke style.
StrokeStyleButtonType get_button_type()
Get the type (line/cap) of the stroke-style button.
gchar const * get_stroke_style()
Get the stroke style attribute associated with the button.
StrokeStyleButton(Gtk::ToggleButton *&grp, char const *icon, StrokeStyleButtonType button_type, gchar const *stroke_style)
Construct a stroke-style radio button with a given icon.
void update_dash_entry(const std::vector< double > &dash_pattern)
Inkscape::Util::Unit const * _old_unit
StrokeStyleButton * paintOrderFMS
StrokeStyleButton * paintOrderMFS
void setCapType(unsigned const captype)
Sets the cap type for a line, and updates the stroke style widget's buttons.
StrokeStyleButton * paintOrderSMF
void enterEditMarkerMode(SPMarkerLoc editMarkerMode)
void markerSelectCB(MarkerComboBox *marker_combo, SPMarkerLoc const which)
Handles when user selects one of the markers from the marker combobox.
void setStrokeWidth()
Set the stroke width and adjust the dash pattern if needed.
void setJoinType(unsigned const jointype)
Sets the join type for a line, and updates the stroke style widget's buttons.
static void buttonToggledCB(StrokeStyleButton *tb, StrokeStyle *spw)
This routine handles toggle events for buttons in the stroke style dialog.
StrokeStyleButton * paintOrderMSF
void setStrokeDash()
Apply the stroke dash pattern to objects, scale to the existing width if needed.
void setStrokeMiter()
Set the Miter Limit value only.
StrokeStyleButton * makeRadioButton(Gtk::ToggleButton *&grp, char const *icon, Gtk::Box *hb, StrokeStyleButtonType button_type, gchar const *stroke_style)
Helper function for creating stroke-style radio buttons.
void setPaintOrderButtons(Gtk::ToggleButton *active)
Updates the paint order style toggle buttons.
void selectionModifiedCB(guint flags)
Callback for when stroke style widget is modified.
void setDesktop(SPDesktop *desktop)
void updateAllMarkers(std::vector< SPItem * > const &objects, bool skip_undo=false)
Updates the marker combobox to highlight the appropriate marker and scroll to that marker.
void setPaintOrder(gchar const *paint_order)
Sets the cap type for a line, and updates the stroke style widget's buttons.
void setJoinButtons(Gtk::ToggleButton *active)
Updates the join style toggle buttons.
StrokeStyleButton * paintOrderFSM
void unitChangedCB()
Callback for when UnitMenu widget is modified.
sigc::connection _document_replaced_connection
void updateLine()
Callback for when stroke style widget is updated, including markers, cap type, join type,...
void setScaledDash(SPCSSAttr *css, int ndash, const double *dash, double offset, double scale)
Sets a line's dash properties in a CSS style object.
std::vector< double > getDashFromStyle(SPStyle *style, double &offset)
Get a dash array and offset from the style.
bool isHairlineSelected() const
Returns whether the currently selected stroke width is "hairline".
void selectionChangedCB()
Callback for when stroke style widget is changed.
Glib::RefPtr< Gtk::Adjustment > widthAdj
StrokeStyleButtonType
List of valid types for the stroke-style radio check-button widget.
@ STROKE_STYLE_BUTTON_ORDER
A button to set the paint-order style.
@ STROKE_STYLE_BUTTON_JOIN
A button to set the line-join style.
@ STROKE_STYLE_BUTTON_CAP
A button to set the line-cap style.
void setCapButtons(Gtk::ToggleButton *active)
Updates the cap style toggle buttons.
void setDashSelectorFromStyle(DashSelector *dsel, SPStyle *style)
Sets selector widgets' dash style from an SPStyle object.
StrokeStyleButton * paintOrderSFM
Glib::RefPtr< Gtk::Adjustment > miterLimitAdj
void _handleDocumentReplaced(SPDesktop *, SPDocument *)
Unit const * getUnit() const
Returns the Unit object corresponding to the current selection in the dropdown widget.
Definition unit-menu.cpp:64
Glib::SignalProxyProperty signal_changed()
void addUnit(Unit const &u)
Adds a unit, possibly user-defined, to the menu.
Definition unit-menu.cpp:58
bool setUnit(Glib::ustring const &unit)
Sets the dropdown widget to the given unit abbreviation.
Definition unit-menu.cpp:75
bool setUnitType(UnitType unit_type, bool svg_length=false)
Adds the unit type to the widget.
Definition unit-menu.cpp:33
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:525
static UnitTable & get()
Definition units.cpp:410
UnitType type
Definition units.h:72
Glib::ustring abbr
Definition units.h:76
Interface for refcounted XML nodes.
Definition node.h:80
To do: update description of desktop.
Definition desktop.h:149
sigc::connection connectDocumentReplaced(F &&slot)
Definition desktop.h:257
SPDocument * getDocument() const
Definition desktop.h:189
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
Typed SVG document implementation.
Definition document.h:103
SPObject * getObjectById(std::string const &id) const
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
Paint order type internal to SPStyle.
void read(gchar const *str) override
SPPaintOrderLayer layer[PAINT_ORDER_LAYERS]
Paint type internal to SPStyle.
bool isNoneSet() const
char const * value() const
Get value if set, or inherited value, or default value (may be NULL)
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1816
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
void requestModified(unsigned int flags)
Requests that a modification notification signal be emitted later (e.g.
SPObject * firstChild()
Definition sp-object.h:315
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
An SVG style object.
Definition style.h:45
T< SPAttr::STROKE_DASHARRAY, SPIDashArray > stroke_dasharray
stroke-dasharray
Definition style.h:257
T< SPAttr::PAINT_ORDER, SPIPaintOrder > paint_order
Definition style.h:222
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition style.h:249
T< SPAttr::STROKE_LINEJOIN, SPIEnum< SPStrokeJoinType > > stroke_linejoin
stroke-linejoin
Definition style.h:253
SPIPaint * getFillOrStroke(bool fill_)
Get either the fill or the stroke property.
Definition style.h:355
T< SPAttr::STROKE_MITERLIMIT, SPIFloat > stroke_miterlimit
stroke-miterlimit
Definition style.h:255
T< SPAttr::STROKE_LINECAP, SPIEnum< SPStrokeCapType > > stroke_linecap
stroke-linecap
Definition style.h:251
SPIString * marker_ptrs[SP_MARKER_LOC_QTY]
Definition style.h:270
T< SPAttr::STROKE_DASHOFFSET, SPILength > stroke_dashoffset
stroke-dashoffset
Definition style.h:259
T< SPAttr::STROKE_EXTENSIONS, SPIStrokeExtensions > stroke_extensions
-inkscape-stroke
Definition style.h:263
TODO: insert short description here.
std::shared_ptr< Css const > css
double c[8][4]
std::vector< double > dash_pattern
A widget for selecting dash patterns and setting the dash offset.
void sp_desktop_apply_css_recursive(SPObject *o, SPCSSAttr *css, bool skip_lines)
Apply style on object and children, recursively.
void sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write_current, bool switch_style)
Apply style on selection on desktop.
int sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property)
Query the subselection (if any) or selection on the given desktop for the given property,...
@ QUERY_STYLE_PROPERTY_STROKEWIDTH
@ QUERY_STYLE_PROPERTY_STROKEMITERLIMIT
@ QUERY_STYLE_PROPERTY_PAINTORDER
@ QUERY_STYLE_PROPERTY_STROKEJOIN
@ QUERY_STYLE_PROPERTY_STROKECAP
@ QUERY_STYLE_NOTHING
@ QUERY_STYLE_MULTIPLE_AVERAGED
A base class for all dialogs.
void sp_dialog_defocus_on_enter(Gtk::Entry *e)
Event handler for dialog windows.
TODO: insert short description here.
Definition of the FillOrStroke enum.
@ FILL
@ STROKE
Gtk::Image * sp_get_icon_image(Glib::ustring const &icon_name, int size)
Icon Loader.
Macro for icon names used in Inkscape.
SPItem * item
Combobox for selecting dash patterns - implementation.
Marker edit mode - onCanvas marker editing of marker orientation, position, scale.
double offset
Definition desktop.h:50
Custom widgets.
Definition desktop.h:126
static Gtk::Box * spw_hbox(Gtk::Grid *table, int width, int col, int row)
Creates a horizontal layout manager with 4-pixel spacing between children and space for 'width' colum...
std::vector< double > parse_pattern(const Glib::ustring &input)
static Gtk::Label * spw_label(Gtk::Grid *table, const gchar *label_text, int col, int row, Gtk::Widget *target)
Creates a label widget with the given text, at the given col, row position in the table.
static double calcScaleLineWidth(const double width_typed, SPItem *const item, Inkscape::Util::Unit const *const unit)
static void buildGroupedItemList(SPObject *element, std::vector< SPObject * > &simple_list)
Recursively builds a simple list from an arbitrarily complex selection of items and grouped items.
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
bool contains_focus(Gtk::Widget &widget)
Returns if widget or one of its descendants has focus.
Definition util.cpp:218
@ UNIT_TYPE_DIMENSIONLESS
Definition units.h:33
@ UNIT_TYPE_LINEAR
Definition units.h:34
static void append(std::vector< T > &target, std::vector< T > &&source)
bool is_query_style_updateable(const int style)
Definition style-utils.h:19
static cairo_user_data_key_t key
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
Definition repr-css.cpp:67
void sp_repr_css_set_property_double(SPCSSAttr *css, gchar const *name, double value)
Set a style property to a new float value (e.g.
Definition repr-css.cpp:224
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
void sp_repr_css_change_recursive(Node *repr, SPCSSAttr *css, gchar const *attr)
Definition repr-css.cpp:371
void sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
Set a style property to "inkscape:unset".
Definition repr-css.cpp:202
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Definition repr-css.cpp:191
auto len
Definition safe-printf.h:21
SPMarkerLoc
These enums are to allow us to have 4-element arrays that represent a set of marker locations (all,...
@ SP_MARKER_LOC_START
@ SP_MARKER_LOC_END
@ SP_MARKER_LOC_MID
TODO: insert short description here.
SPObject * getMarkerObj(gchar const *n, SPDocument *doc)
Extract the actual name of the link e.g.
Widgets used in the stroke style dialog.
@ SP_STROKE_LINEJOIN_MITER
Definition style-enums.h:34
@ SP_STROKE_LINEJOIN_BEVEL
Definition style-enums.h:36
@ SP_STROKE_LINEJOIN_ROUND
Definition style-enums.h:35
@ SP_STROKE_LINECAP_SQUARE
Definition style-enums.h:43
@ SP_STROKE_LINECAP_ROUND
Definition style-enums.h:42
@ SP_STROKE_LINECAP_BUTT
Definition style-enums.h:41
@ SP_CSS_PAINT_ORDER_STROKE
@ SP_CSS_PAINT_ORDER_FILL
@ SP_CSS_PAINT_ORDER_NORMAL
Common utility functions for manipulating style.
SPDesktop * desktop
double width