Inkscape
Vector Graphics Editor
marker-combo-box.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/* Author:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Maximilian Albert <maximilian.albert@gmail.com>
10 *
11 * Copyright (C) 2002 Lauris Kaplinski
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include "marker-combo-box.h"
17
18#include <chrono>
19#include <optional>
20#include <sstream>
21#include <utility>
22
23#include <glibmm/fileutils.h>
24#include <glibmm/i18n.h>
25#include <glibmm/main.h>
26#include <gtkmm/button.h>
27#include <gtkmm/checkbutton.h>
28#include <gtkmm/flowbox.h>
29#include <gtkmm/grid.h>
30#include <gtkmm/image.h>
31#include <gtkmm/label.h>
32#include <gtkmm/menubutton.h>
33#include <gtkmm/picture.h>
34#include <gtkmm/spinbutton.h>
35#include <gtkmm/togglebutton.h>
36#include <gtkmm/window.h>
37
38#include "helper/stock-items.h"
39#include "io/resource.h"
40#include "object/sp-defs.h"
41#include "object/sp-marker.h"
42#include "object/sp-root.h"
43#include "ui/builder-utils.h"
44#include "ui/svg-renderer.h"
45#include "ui/util.h"
46#include "ui/widget/bin.h"
48
49#define noTIMING_INFO 1;
50
53
54// size of marker image in a list
55static constexpr int ITEM_WIDTH = 40;
56static constexpr int ITEM_HEIGHT = 32;
57
58namespace Inkscape::UI::Widget {
59
60// separator for FlowBox widget
61static cairo_surface_t* create_separator(double alpha, int width, int height, int device_scale) {
62 width *= device_scale;
63 height *= device_scale;
64 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
65 cairo_t* ctx = cairo_create(surface);
66 cairo_set_source_rgba(ctx, 0.5, 0.5, 0.5, alpha);
67 cairo_move_to(ctx, 0.5, height / 2 + 0.5);
68 cairo_line_to(ctx, width + 0.5, height / 2 + 0.5);
69 cairo_set_line_width(ctx, 1.0 * device_scale);
70 cairo_stroke(ctx);
71 cairo_surface_flush(surface);
72 cairo_surface_set_device_scale(surface, device_scale, device_scale);
73 return surface;
74}
75
76// empty image; "no marker"
77static Cairo::RefPtr<Cairo::Surface> g_image_none;
78// error extracting/rendering marker; "bad marker"
79static Cairo::RefPtr<Cairo::Surface> g_bad_marker;
80
81Glib::ustring get_attrib(SPMarker* marker, const char* attrib) {
82 auto value = marker->getAttribute(attrib);
83 return value ? value : "";
84}
85
86double get_attrib_num(SPMarker* marker, const char* attrib) {
87 auto val = get_attrib(marker, attrib);
88 return strtod(val.c_str(), nullptr);
89}
90
91MarkerComboBox::MarkerComboBox(Glib::ustring id, int l) :
92 Glib::ObjectBase{"MarkerComboBox"},
94 Gtk::Box{},
95 _combo_id(std::move(id)),
96 _loc(l),
97 _builder(create_builder("marker-popup.glade")),
98 _marker_list(get_widget<Gtk::FlowBox>(_builder, "flowbox")),
99 _preview_bin(get_derived_widget<UI::Widget::Bin>(_builder, "preview-bin")),
100 _preview(get_widget<Gtk::Picture>(_builder, "preview")),
101 _marker_name(get_widget<Gtk::Label>(_builder, "marker-id")),
102 _link_scale(get_widget<Gtk::Button>(_builder, "link-scale")),
103 _scale_x(get_widget<Gtk::SpinButton>(_builder, "scale-x")),
104 _scale_y(get_widget<Gtk::SpinButton>(_builder, "scale-y")),
105 _scale_with_stroke(get_widget<Gtk::CheckButton>(_builder, "scale-with-stroke")),
106 _menu_btn(get_widget<Gtk::MenuButton>(_builder, "menu-btn")),
107 _angle_btn(get_widget<Gtk::SpinButton>(_builder, "angle")),
108 _offset_x(get_widget<Gtk::SpinButton>(_builder, "offset-x")),
109 _offset_y(get_widget<Gtk::SpinButton>(_builder, "offset-y")),
110 _input_grid(get_widget<Gtk::Grid>(_builder, "input-grid")),
111 _orient_auto_rev(get_widget<Gtk::ToggleButton>(_builder, "orient-auto-rev")),
112 _orient_auto(get_widget<Gtk::ToggleButton>(_builder, "orient-auto")),
113 _orient_angle(get_widget<Gtk::ToggleButton>(_builder, "orient-angle")),
114 _orient_flip_horz(get_widget<Gtk::Button>(_builder, "btn-horz-flip")),
115 _current_img(get_widget<Gtk::Image>(_builder, "current-img")),
116 _edit_marker(get_widget<Gtk::Button>(_builder, "edit-marker"))
117{
118 _background_color = 0x808080ff;
119 _foreground_color = 0x808080ff;
120
121 if (!g_image_none) {
122 auto device_scale = get_scale_factor();
123 g_image_none = Cairo::RefPtr<Cairo::Surface>(new Cairo::Surface(create_separator(1, ITEM_WIDTH, ITEM_HEIGHT, device_scale)));
124 }
125
126 if (!g_bad_marker) {
128 Inkscape::svg_renderer renderer(path.c_str());
129 g_bad_marker = renderer.render_surface(1.0);
130 }
131
132 prepend(_menu_btn);
133
134 _preview_bin.connectAfterResize([this] (int, int, int) {
135 // refresh after preview widget has been finally resized/expanded
137 });
138
140 _marker_list.bind_list_store(_marker_store, [=](const Glib::RefPtr<MarkerItem>& item){
141 auto const image = Gtk::make_managed<Gtk::Image>(to_texture(item->pix));
142 image->set_visible(true);
143 auto const box = Gtk::make_managed<Gtk::FlowBoxChild>();
144 box->set_child(*image);
145 if (item->separator) {
146 image->set_sensitive(false);
147 image->set_focusable(false);
148 image->set_size_request(-1, 10);
149 box->set_sensitive(false);
150 box->set_focusable(false);
151 box->add_css_class("marker-separator");
152 }
153 else {
154 box->add_css_class("marker-item-box");
155 }
157 box->set_size_request(item->width, item->height);
158 // removing ability to focus from all items to prevent crash when user edits "Offset Y" and presses tab key
159 // to move to the next widget; not ideal, as it limits navigation, but lesser evil
160 box->set_focusable(false);
161 return box;
162 });
163
165
166 set_sensitive(true);
167
168 _marker_list.signal_selected_children_changed().connect([=](){
169 auto item = get_active();
170 if (!item && !_marker_list.get_selected_children().empty()) {
171 _marker_list.unselect_all();
172 }
173 });
174
175 _marker_list.signal_child_activated().connect([=](Gtk::FlowBoxChild* box){
176 if (box->get_sensitive()) _signal_changed.emit();
177 });
178
179 auto set_orient = [=](bool enable_angle, const char* value) {
180 if (_update.pending()) return;
181 _angle_btn.set_sensitive(enable_angle);
183 };
184 _orient_auto_rev.signal_toggled().connect([=](){ set_orient(false, "auto-start-reverse"); });
185 _orient_auto.signal_toggled().connect([=]() { set_orient(false, "auto"); });
186 _orient_angle.signal_toggled().connect([=]() { set_orient(true, _angle_btn.get_text().c_str()); });
187 _orient_flip_horz.signal_clicked().connect([=]() { sp_marker_flip_horizontally(get_current()); });
188
189 _angle_btn.signal_value_changed().connect([=]() {
190 if (_update.pending() || !_angle_btn.is_sensitive()) return;
191 sp_marker_set_orient(get_current(), _angle_btn.get_text().c_str());
192 });
193
194 auto set_scale = [=](bool changeWidth) {
195 if (_update.pending()) return;
196
197 if (auto marker = get_current()) {
198 auto sx = _scale_x.get_value();
199 auto sy = _scale_y.get_value();
200 auto width = get_attrib_num(marker, "markerWidth");
201 auto height = get_attrib_num(marker, "markerHeight");
202 if (_scale_linked && width > 0.0 && height > 0.0) {
203 auto scoped(_update.block());
204 if (changeWidth) {
205 // scale height proportionally
206 sy = height * (sx / width);
207 _scale_y.set_value(sy);
208 }
209 else {
210 // scale width proportionally
211 sx = width * (sy / height);
212 _scale_x.set_value(sx);
213 }
214 }
215 sp_marker_set_size(marker, sx, sy);
216 }
217 };
218
219 // delay setting scale to idle time; if invoked by focus change due to new marker selection
220 // it leads to marker list rebuild and apparent flowbox content corruption
221 auto idle_set_scale = [=](bool changeWidth) {
222 if (_update.pending()) return;
223
224 if (auto orig_marker = get_current()) {
225 _idle = Glib::signal_idle().connect([=](){
226 if (auto marker = get_current()) {
227 if (marker == orig_marker) {
228 set_scale(changeWidth);
229 }
230 }
231 return false; // don't call again
232 });
233 }
234 };
235
236 _link_scale.signal_clicked().connect([=](){
237 if (_update.pending()) return;
241 });
242
243 _scale_x.signal_value_changed().connect([=]() { idle_set_scale(true); });
244 _scale_y.signal_value_changed().connect([=]() { idle_set_scale(false); });
245
246 _scale_with_stroke.signal_toggled().connect([=](){
247 if (_update.pending()) return;
249 });
250
251 auto set_offset = [=](){
252 if (_update.pending()) return;
253 sp_marker_set_offset(get_current(), _offset_x.get_value(), _offset_y.get_value());
254 };
255 _offset_x.signal_value_changed().connect([=]() { set_offset(); });
256 _offset_y.signal_value_changed().connect([=]() { set_offset(); });
257
258 // request to edit marker on canvas; close popup to get it out of the way and call marker edit tool
259 _edit_marker.signal_clicked().connect([this]{ _menu_btn.get_popover()->popdown(); _signal_edit(); });
260
261 // before showing popover refresh marker attributes
262 _menu_btn.get_popover()->signal_show().connect([=](){ update_ui(get_current(), false); }, false);
263
266 set_visible(true);
267}
268
270 _input_grid.set_sensitive(marker != nullptr);
271
272 if (marker) {
273 _scale_x.set_value(get_attrib_num(marker, "markerWidth"));
274 _scale_y.set_value(get_attrib_num(marker, "markerHeight"));
275 auto units = get_attrib(marker, "markerUnits");
276 _scale_with_stroke.set_active(units == "strokeWidth" || units == "");
277 auto aspect = get_attrib(marker, "preserveAspectRatio");
278 _scale_linked = aspect != "none";
280 // marker->setAttribute("markerUnits", scale_with_stroke ? "strokeWidth" : "userSpaceOnUse");
281 _offset_x.set_value(get_attrib_num(marker, "refX"));
282 _offset_y.set_value(get_attrib_num(marker, "refY"));
283 auto orient = get_attrib(marker, "orient");
284
285 // try parsing as number
286 _angle_btn.set_value(strtod(orient.c_str(), nullptr));
287 if (orient == "auto-start-reverse") {
288 _orient_auto_rev.set_active();
289 _angle_btn.set_sensitive(false);
290 }
291 else if (orient == "auto") {
292 _orient_auto.set_active();
293 _angle_btn.set_sensitive(false);
294 }
295 else {
296 _orient_angle.set_active();
297 _angle_btn.set_sensitive(true);
298 }
299 }
300}
301
303 _link_scale.set_child(get_widget<Gtk::Image>(_builder, _scale_linked ? "image-linked" : "image-unlinked"));
304}
305
306// update marker image inside the menu button
307void MarkerComboBox::update_menu_btn(Glib::RefPtr<MarkerItem> marker) {
308 _current_img.set(to_texture(marker ? marker->pix : g_image_none));
309}
310
311// update marker preview image in the popover panel
312void MarkerComboBox::update_preview(Glib::RefPtr<MarkerItem> item) {
313 Cairo::RefPtr<Cairo::Surface> surface;
314 Glib::ustring label;
315
316 if (!item) {
317 // TRANSLATORS: None - no marker selected for a path
318 label = _("None");
319 }
320
321 if (item && item->source && !item->id.empty()) {
322 Inkscape::Drawing drawing;
323 unsigned const visionkey = SPItem::display_key_new(1);
324 drawing.setRoot(_sandbox->getRoot()->invoke_show(drawing, visionkey, SP_ITEM_SHOW_DISPLAY));
325 // generate preview
326 auto alloc = _preview.get_allocation();
327 auto size = Geom::IntPoint(alloc.get_width() - 10, alloc.get_height() - 10);
328 if (size.x() > 0 && size.y() > 0) {
329 surface = create_marker_image(size, item->id.c_str(), item->source, drawing, visionkey, true, true, 2.60);
330 }
331 else {
332 // too early, preview hasn't been expanded/resized yet
333 _preview_no_alloc = true;
334 }
335 _sandbox->getRoot()->invoke_hide(visionkey);
336 label = _(item->label.c_str());
337 }
338
339 _preview.set_paintable(to_texture(surface));
340 std::ostringstream ost;
341 ost << "<small>" << label.raw() << "</small>";
342 _marker_name.set_markup(ost.str().c_str());
343}
344
346 return
347 id == item.id &&
348 label == item.label &&
349 separator == item.separator &&
350 stock == item.stock &&
351 history == item.history &&
352 source == item.source &&
353 width == item.width &&
354 height == item.height;
355}
356
357// find marker object by ID in a document
358SPMarker* find_marker(SPDocument* document, const Glib::ustring& marker_id) {
359 if (!document) return nullptr;
360
361 SPDefs* defs = document->getDefs();
362 if (!defs) return nullptr;
363
364 for (auto& child : defs->children) {
365 if (is<SPMarker>(&child)) {
366 auto marker = cast<SPMarker>(&child);
367 auto id = marker->getId();
368 if (id && marker_id == id) {
369 // found it
370 return marker;
371 }
372 }
373 }
374
375 // not found
376 return nullptr;
377}
378
380 // find current marker
382}
383
384void MarkerComboBox::set_active(Glib::RefPtr<MarkerItem> item) {
385 bool selected = false;
386 if (item) {
387 UI::for_each_child(_marker_list, [=, &selected] (Gtk::Widget &widget) {
388 if (auto box = dynamic_cast<Gtk::FlowBoxChild*>(&widget)) {
389 if (auto marker = _widgets_to_markers[box->get_child()]) {
390 if (*marker == *item) {
391 _marker_list.select_child(*box);
392 selected = true;
393 }
394 }
395 }
397 });
398 }
399
400 if (!selected) {
401 _marker_list.unselect_all();
402 }
403}
404
405Glib::RefPtr<MarkerComboBox::MarkerItem> MarkerComboBox::find_marker_item(SPMarker* marker) {
406 std::string id;
407 if (marker != nullptr) {
408 if (auto markname = marker->getRepr()->attribute("id")) {
409 id = markname;
410 }
411 }
412
413 Glib::RefPtr<MarkerItem> marker_item;
414 if (!id.empty()) {
415 for (auto&& item : _history_items) {
416 if (item->id == id) {
417 marker_item = item;
418 break;
419 }
420 }
421 }
422
423 return marker_item;
424}
425
426Glib::RefPtr<MarkerComboBox::MarkerItem> MarkerComboBox::get_active() {
427 auto empty = Glib::RefPtr<MarkerItem>();
428 auto sel = _marker_list.get_selected_children();
429 if (sel.size() == 1) {
430 auto item = _widgets_to_markers[sel.front()->get_child()];
431 if (item && item->separator) {
432 return empty;
433 }
434 return item;
435 }
436 else {
437 return empty;
438 }
439}
440
442{
443 if (_document != document) {
444
445 if (_document) {
447 }
448
449 _document = document;
450
451 if (_document) {
454 });
455 }
456
458
460 }
461}
462
471 if (_update.pending()) return;
472
473 auto scoped(_update.block());
474
475 /*
476 * Seems to be no way to get notified of changes just to markers,
477 * so listen to changes in all defs and check if the number of markers has changed here
478 * to avoid unnecessary refreshes when things like gradients change
479 */
480 // TODO: detect changes to markers; ignore changes to everything else;
481 // simple count check doesn't cut it, so just do it unconditionally for now
483
484 auto marker = find_marker_item(get_current());
485 update_menu_btn(marker);
486 update_preview(marker);
487}
488
489Glib::RefPtr<MarkerComboBox::MarkerItem> MarkerComboBox::add_separator(bool filler) {
490 auto item = MarkerItem::create();
491 item->history = false;
492 item->separator = true;
493 item->id = "None";
494 item->label = filler ? "filler" : "Separator";
495 item->stock = false;
496 if (!filler) {
497 auto device_scale = get_scale_factor();
498 static Cairo::RefPtr<Cairo::Surface> separator(new Cairo::Surface(create_separator(0.7, ITEM_WIDTH, 10, device_scale)));
499 item->pix = separator;
500 }
501 item->height = 10;
502 item->width = -1;
503 return item;
504}
505
509void
511{
512 if (_update.pending()) return;
513
514 static SPDocument *markers_doc = nullptr;
515
516 // find and load markers.svg
517 if (markers_doc == nullptr) {
518 using namespace Inkscape::IO::Resource;
519 auto markers_source = get_path_string(SYSTEM, MARKERS, "markers.svg");
520 if (Glib::file_test(markers_source, Glib::FileTest::IS_REGULAR)) {
521 markers_doc = SPDocument::createNewDoc(markers_source.c_str(), false);
522 }
523 }
524
525 // load markers from markers.svg
526 if (markers_doc) {
527 marker_list_from_doc(markers_doc, false);
528 }
529
531}
532
537{
538 auto sp_marker = cast<SPMarker>(marker);
539
540 bool reselect = sp_marker != get_current();
541
542 update_ui(sp_marker, reselect);
543}
544
545void MarkerComboBox::update_ui(SPMarker* marker, bool select) {
546 auto scoped(_update.block());
547
548 auto id = marker ? marker->getId() : nullptr;
549 _current_marker_id = id ? id : "";
550
551 auto marker_item = find_marker_item(marker);
552
553 if (select) {
554 set_active(marker_item);
555 }
556
558 update_menu_btn(marker_item);
559 update_preview(marker_item);
560}
561
566{
567 /* Get Marker */
568 auto item = get_active();
569 if (!item) {
570 return std::string();
571 }
572
573 std::string marker;
574
575 if (item->id != "none") {
576 bool stockid = item->stock;
577
578 std::string markurn = stockid ? "urn:inkscape:marker:" + item->id : item->id;
579 auto mark = cast<SPMarker>(get_stock_item(markurn.c_str(), stockid));
580
581 if (mark) {
582 Inkscape::XML::Node* repr = mark->getRepr();
583 auto id = repr->attribute("id");
584 if (id) {
585 std::ostringstream ost;
586 ost << "url(#" << id << ")";
587 marker = ost.str();
588 }
589 if (stockid) {
590 mark->getRepr()->setAttribute("inkscape:collect", "always");
591 }
592 // adjust marker's attributes (or add missing ones) to stay in sync with marker tool
594 }
595 } else {
596 marker = item->id;
597 }
598
599 return marker;
600}
601
608 std::vector<SPMarker*> markers = get_marker_list(source);
609 remove_markers(history);
610 add_markers(markers, source, history);
611 update_store();
612}
613
615 _marker_store->freeze_notify();
616
617 auto selected = get_active();
618
619 _marker_store->remove_all();
620 _widgets_to_markers.clear();
621
622 // recent and user-defined markers come first
623 for (auto&& item : _history_items) {
624 _marker_store->append(item);
625 }
626
627 // separator
628 if (!_history_items.empty()) {
629 // add empty boxes to fill up the row to 'max' elements and then
630 // extra ones to create entire new empty row (a separator of sorts)
631 auto max = _marker_list.get_max_children_per_line();
632 auto fillup = max - _history_items.size() % max;
633
634 for (int i = 0; i < fillup; ++i) {
635 _marker_store->append(add_separator(true));
636 }
637 for (int i = 0; i < max; ++i) {
638 _marker_store->append(add_separator(false));
639 }
640 }
641
642 // stock markers
643 for (auto&& item : _stock_items) {
644 _marker_store->append(item);
645 }
646
647 _marker_store->thaw_notify();
648
649 // reselect current
650 set_active(selected);
651}
657std::vector<SPMarker*> MarkerComboBox::get_marker_list(SPDocument* source)
658{
659 std::vector<SPMarker *> ml;
660 if (source == nullptr) return ml;
661
662 SPDefs *defs = source->getDefs();
663 if (!defs) {
664 return ml;
665 }
666
667 for (auto& child: defs->children) {
668 if (is<SPMarker>(&child)) {
669 auto marker = cast<SPMarker>(&child);
670 ml.push_back(marker);
671 }
672 }
673 return ml;
674}
675
679void MarkerComboBox::remove_markers (gboolean history)
680{
681 if (history) {
682 _history_items.clear();
683 }
684 else {
685 _stock_items.clear();
686 }
687}
688
692void MarkerComboBox::add_markers (std::vector<SPMarker *> const& marker_list, SPDocument *source, gboolean history)
693{
694 // Do this here, outside of loop, to speed up preview generation:
695 Inkscape::Drawing drawing;
696 unsigned const visionkey = SPItem::display_key_new(1);
697 drawing.setRoot(_sandbox->getRoot()->invoke_show(drawing, visionkey, SP_ITEM_SHOW_DISPLAY));
698
699 if (history) {
700 // add "None"
701 auto item = MarkerItem::create();
702 item->pix = g_image_none;
703 item->history = true;
704 item->separator = false;
705 item->id = "None";
706 item->label = "None";
707 item->stock = false;
708 item->width = ITEM_WIDTH;
709 item->height = ITEM_HEIGHT;
710 _history_items.push_back(item);
711 }
712
713#if TIMING_INFO
715#endif
716
717 for (auto i:marker_list) {
718
719 Inkscape::XML::Node *repr = i->getRepr();
720 gchar const *markid = repr->attribute("inkscape:stockid") ? repr->attribute("inkscape:stockid") : repr->attribute("id");
721
722 // generate preview
723 auto pixbuf = create_marker_image(Geom::IntPoint(ITEM_WIDTH, ITEM_HEIGHT), repr->attribute("id"), source, drawing, visionkey, false, true, 1.50);
724
725 auto item = MarkerItem::create();
726 item->source = source;
727 item->pix = pixbuf;
728 if (auto id = repr->attribute("id")) {
729 item->id = id;
730 }
731 item->label = markid ? markid : "";
732 item->stock = !history;
733 item->history = history;
734 item->width = ITEM_WIDTH;
735 item->height = ITEM_HEIGHT;
736
737 if (history) {
738 _history_items.emplace_back(std::move(item));
739 }
740 else {
741 _stock_items.emplace_back(std::move(item));
742 }
743 }
744
745 _sandbox->getRoot()->invoke_hide(visionkey);
746
747#if TIMING_INFO
748auto current_time = std::chrono::high_resolution_clock::now();
749auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - old_time);
750g_warning("%s render time for %d markers: %d ms", combo_id, (int)marker_list.size(), static_cast<int>(elapsed.count()));
751#endif
752}
753
759Cairo::RefPtr<Cairo::Surface>
761 SPDocument *source, Inkscape::Drawing &drawing, unsigned /*visionkey*/, bool checkerboard, bool no_clip, double scale)
762{
763 std::optional<guint32> checkerboard_color;
764 if (checkerboard) {
765 checkerboard_color = _background_color;
766 }
767
768 int device_scale = get_scale_factor();
769 auto const fg = get_color();
770 return Inkscape::create_marker_image(_combo_id, _sandbox.get(), fg, pixel_size, mname, source,
771 drawing, checkerboard_color, no_clip, scale, device_scale);
772}
773
774// capture background color when styles change
775void MarkerComboBox::css_changed(GtkCssStyleChange *) {
776 auto background = _background_color;
777 if (auto wnd = dynamic_cast<Gtk::Window*>(this->get_root())) {
778 auto const color = get_color_with_class(*wnd, "theme_bg_color");
779 background =
780 gint32(0xff * color.get_red()) << 24 |
781 gint32(0xff * color.get_green()) << 16 |
782 gint32(0xff * color.get_blue()) << 8 |
783 0xff;
784 }
785
786 auto const color = get_color();
787 auto foreground =
788 gint32(0xff * color.get_red()) << 24 |
789 gint32(0xff * color.get_green()) << 16 |
790 gint32(0xff * color.get_blue()) << 8 |
791 0xff;
792 if (foreground != _foreground_color || background != _background_color) {
793 _foreground_color = foreground;
794 _background_color = background;
795 // theme changed?
796 init_combo();
797 }
798}
799
800sigc::connection MarkerComboBox::connect_changed(sigc::slot<void ()> slot)
801{
802 return _signal_changed.connect(std::move(slot));
803}
804
805sigc::connection MarkerComboBox::connect_edit(sigc::slot<void ()> slot)
806{
807 return _signal_edit.connect(std::move(slot));
808}
809
810} // namespace Inkscape::UI::Widget
811
812/*
813 Local Variables:
814 mode:c++
815 c-file-style:"stroustrup"
816 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
817 indent-tabs-mode:nil
818 fill-column:99
819 End:
820*/
821// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition: aa.cpp:228
Bin: widget that can hold one child, useful as a base class of custom widgets.
Gtk builder utilities.
Cairo::RefPtr< Cairo::ImageSurface > surface
Definition: canvas.cpp:137
Two-dimensional point with integer coordinates.
Definition: int-point.h:57
void setRoot(DrawingItem *root)
Definition: drawing.cpp:63
The Bin is a widget that can hold a single child.
Definition: bin.h:31
sigc::connection connectAfterResize(F &&slot)
Register a handler to run immediately after a resize operation.
Definition: bin.h:59
bool operator==(const MarkerItem &item) const
static Glib::RefPtr< MarkerItem > create()
Glib::RefPtr< MarkerItem > add_separator(bool filler)
void init_combo()
Init the combobox widget to display markers from markers.svg.
void update_widgets_from_marker(SPMarker *marker)
void css_changed(GtkCssStyleChange *change) override
Called after gtk_widget_css_changed(): when a CSS widget node is validated & style changed.
void marker_list_from_doc(SPDocument *source, bool history)
Pick up all markers from source and add items to the list/store.
std::map< Gtk::Widget *, Glib::RefPtr< MarkerItem > > _widgets_to_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)
void remove_markers(gboolean history)
Remove history or non-history markers from the combo.
Glib::RefPtr< Gtk::Builder > _builder
void add_markers(std::vector< SPMarker * > const &marker_list, SPDocument *source, gboolean history)
Adds markers in marker_list to the combo.
void set_active(Glib::RefPtr< MarkerItem > item)
sigc::connection connect_edit(sigc::slot< void()> slot)
Glib::RefPtr< MarkerItem > get_active()
Glib::RefPtr< Gio::ListStore< MarkerItem > > _marker_store
std::unique_ptr< SPDocument > _sandbox
MarkerComboBox(Glib::ustring id, int loc)
void set_current(SPObject *marker)
Sets the current marker in the marker combobox.
void update_menu_btn(Glib::RefPtr< MarkerItem > marker_item)
void update_ui(SPMarker *marker, bool select)
std::vector< Glib::RefPtr< MarkerItem > > _stock_items
void update_preview(Glib::RefPtr< MarkerItem > marker_item)
std::vector< Glib::RefPtr< MarkerItem > > _history_items
void refresh_after_markers_modified()
This function is invoked after document "defs" section changes.
std::vector< SPMarker * > get_marker_list(SPDocument *source)
Returns a vector of markers in the defs of the given source document as a vector.
Cairo::RefPtr< Cairo::Surface > create_marker_image(Geom::IntPoint pixel_size, gchar const *mname, SPDocument *source, Inkscape::Drawing &drawing, unsigned, bool checkerboard, bool no_clip, double scale)
Creates a copy of the marker named mname, determines its visible and renderable area in the bounding ...
Glib::RefPtr< MarkerItem > find_marker_item(SPMarker *marker)
SpinButton widget, that allows entry of simple math expressions (also units, when linked with UnitMen...
Definition: spinbutton.h:49
A class you can inherit to access GTK4ʼs Widget.css_changed & .focus vfuncs, missing in gtkmm4.
Interface for refcounted XML nodes.
Definition: node.h:80
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Cairo::RefPtr< Cairo::ImageSurface > render_surface(double scale)
scoped_block block()
bool pending() const
Definition: sp-defs.h:19
Typed SVG document implementation.
Definition: document.h:106
SPDefs * getDefs()
Return the main defs object for the document.
Definition: document.cpp:259
static SPDocument * createNewDoc(char const *filename, bool keepalive, bool make_new=false, SPDocument *parent=nullptr)
Fetches document from filename, or creates new, if NULL; public document appears in document list.
Definition: document.cpp:678
static unsigned int display_key_new(unsigned numkeys)
Allocates unique integer keys.
Definition: sp-item.cpp:1169
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition: sp-object.h:146
char const * label() const
Gets the author-visible label property for the object or a default if no label is defined.
Definition: sp-object.cpp:428
char * id
Definition: sp-object.h:178
char const * getId() const
Returns the objects current ID string.
Definition: sp-object.cpp:206
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
Definition: sp-object.cpp:232
char const * getAttribute(char const *name) const
Definition: sp-object.cpp:1579
sigc::connection connectModified(sigc::slot< void(SPObject *, unsigned int)> slot)
Connects to the modification notification signal.
Definition: sp-object.h:691
ChildrenList children
Definition: sp-object.h:893
bool checkerboard(gint x, gint y, guint size)
struct _cairo_surface cairo_surface_t
std::unique_ptr< Magick::Image > image
Definition: imagemagick.cpp:40
SPItem * item
Definition: imagemagick.cpp:43
static constexpr int ITEM_HEIGHT
static constexpr int ITEM_WIDTH
Combobox for selecting dash patterns - implementation.
MultiDegree< n > max(MultiDegree< n > const &p, MultiDegree< n > const &q)
Returns the maximal degree appearing in the two arguments for each variables.
Definition: sbasisN.h:158
Definition: desktop.h:51
std::string get_path_string(Domain domain, Type type, char const *filename, char const *extra)
Definition: resource.cpp:145
std::string get_filename(Type type, char const *filename, bool localized, bool silent)
Definition: resource.cpp:167
bool file_test(char const *utf8name, GFileTest test)
Definition: sys.cpp:109
Button
helper to stop accidents on int vs gtkmm3's weak=typed enums, & looks nicer!
Definition: controller.h:70
static void set_sensitive(Gtk::SearchEntry2 &entry, bool const sensitive)
Custom widgets.
Definition: desktop.h:127
static cairo_surface_t * create_separator(double alpha, int width, int height, int device_scale)
static Cairo::RefPtr< Cairo::Surface > g_bad_marker
static constexpr int height
Glib::ustring get_attrib(SPMarker *marker, const char *attrib)
SPMarker * find_marker(SPDocument *document, const Glib::ustring &marker_id)
static Cairo::RefPtr< Cairo::Surface > g_image_none
static constexpr int ITEM_WIDTH
double get_attrib_num(SPMarker *marker, const char *attrib)
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Definition: builder-utils.h:33
Gtk::Widget * for_each_child(Gtk::Widget &widget, Func &&func, bool const plus_self=false, bool const recurse=false, int const level=0)
Call Func with a reference to each child of parent, until it returns _break.
Definition: util.h:95
W & get_derived_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id, Args &&... args)
Definition: builder-utils.h:42
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
std::unique_ptr< SPDocument > ink_markers_preview_doc(const Glib::ustring &group_id)
Returns a new document containing default start, mid, and end markers.
Cairo::RefPtr< Cairo::Surface > create_marker_image(const Glib::ustring &group_id, SPDocument *_sandbox, Gdk::RGBA marker_color, Geom::IntPoint pixel_size, const char *mname, SPDocument *source, Inkscape::Drawing &drawing, std::optional< guint32 > checkerboard, bool no_clip, double scale, int device_scale)
Creates a copy of the marker named mname, determines its visible and renderable area in the bounding ...
time_t now()
parse current time from SOURCE_DATE_EPOCH environment variable
STL namespace.
signed int gint32
void cairo_line_to(cairo_t *cr, Geom::Point p1)
struct _cairo cairo_t
Definition: path-cairo.h:16
void cairo_move_to(cairo_t *cr, Geom::Point p1)
int size
Ocnode * child[8]
Definition: quantize.cpp:33
Inkscape::IO::Resource - simple resource API.
void sp_marker_set_orient(SPMarker *marker, const char *value)
Definition: sp-marker.cpp:578
void sp_marker_scale_with_stroke(SPMarker *marker, bool scale_with_stroke)
Definition: sp-marker.cpp:599
void sp_validate_marker(SPMarker *sp_marker, SPDocument *doc)
Definition: sp-marker.cpp:330
void sp_marker_set_offset(SPMarker *marker, double dx, double dy)
Definition: sp-marker.cpp:609
void sp_marker_set_uniform_scale(SPMarker *marker, bool uniform)
Definition: sp-marker.cpp:620
void sp_marker_set_size(SPMarker *marker, double sx, double sy)
Definition: sp-marker.cpp:588
void sp_marker_flip_horizontally(SPMarker *marker)
Definition: sp-marker.cpp:630
SPRoot: SVG <svg> implementation.
SPObject * get_stock_item(gchar const *urn, bool stock, SPDocument *stock_doc)
TODO: insert short description here.
double width
std::unique_ptr< Toolbar >(* create)(SPDesktop *desktop)
Definition: toolbars.cpp:63
void cairo_set_source_rgba(cairo_t *cr, colour c)
Glib::RefPtr< Gdk::Texture > to_texture(Cairo::RefPtr< Cairo::Surface > const &surface)
Convert an image surface in ARGB32 format to a texture.
Definition: util.cpp:458
Gdk::RGBA get_color_with_class(Gtk::Widget &widget, Glib::ustring const &css_class)
Definition: util.cpp:272