Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
paint-selector.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors:
6 * see git history
7 * Lauris Kaplinski
8 * bulia byak <buliabyak@users.sf.net>
9 * John Cliff <simarilius@yahoo.com>
10 * Jon A. Cruz <jon@joncruz.org>
11 * Abhishek Sharma
12 *
13 * Copyright (C) 2018 Authors
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#define noSP_PS_VERBOSE
18
19#include "paint-selector.h"
20
21#include <cstring>
22#include <exception>
23#include <iostream>
24#include <string>
25#include <vector>
26
27#include <2geom/affine.h>
28#include <2geom/point.h>
29#include <2geom/transforms.h>
30#include <glibmm/fileutils.h>
31#include <glibmm/i18n.h>
32#include <gtkmm/combobox.h>
33#include <gtkmm/label.h>
34#include <gtkmm/togglebutton.h>
35
36#include "desktop-style.h"
37#include "document.h"
38#include "style.h"
39#include "inkscape.h"
40
41#include "helper/stock-items.h"
42#include "io/resource.h"
43#include "io/sys.h"
44#include "object/sp-hatch.h"
47#include "object/sp-pattern.h"
49#include "object/sp-stop.h"
50#include "path-prefix.h"
53#include "ui/icon-loader.h"
54#include "ui/icon-names.h"
55#include "ui/pack.h"
62#include "xml/repr.h"
63
64#ifdef SP_PS_VERBOSE
65static gchar const *modeStrings[] = {
66 "MODE_EMPTY",
67 "MODE_MULTIPLE",
68 "MODE_NONE",
69 "MODE_SOLID_COLOR",
70 "MODE_GRADIENT_LINEAR",
71 "MODE_GRADIENT_RADIAL",
72#ifdef WITH_MESH
73 "MODE_GRADIENT_MESH",
74#endif
75 "MODE_PATTERN",
76 "MODE_SWATCH",
77 "MODE_UNSET",
78 ".",
79 ".",
80};
81#endif
82
83namespace {
84GtkWidget *ink_combo_box_new_with_model(GtkTreeModel *model)
85{
86 auto const combobox = Gtk::make_managed<Gtk::ComboBox>();
87 gtk_combo_box_set_model(combobox->gobj(), model);
88 return combobox->Gtk::Widget::gobj();
89}
90} // namespace
91
92namespace Inkscape {
93namespace UI {
94namespace Widget {
95
96class FillRuleRadioButton : public Gtk::ToggleButton {
97 private:
99
100 public:
101 FillRuleRadioButton() = default;
102 FillRuleRadioButton(Gtk::ToggleButton &group) { set_group(group); }
103
104 inline void set_fillrule(PaintSelector::FillRule fillrule) { _fillrule = fillrule; }
105 inline PaintSelector::FillRule get_fillrule() const { return _fillrule; }
106};
107
108class StyleToggleButton : public Gtk::ToggleButton {
109 private:
110 PaintSelector::Mode _style;
111
112 public:
113 inline void set_style(PaintSelector::Mode style) { _style = style; }
114 inline PaintSelector::Mode get_style() const { return _style; }
115};
116
124
132
133#define XPAD 4
134#define YPAD 1
135
136PaintSelector::PaintSelector(FillOrStroke kind, std::shared_ptr<Colors::ColorSet> colors)
137 : _selected_colors(std::move(colors))
138{
139 set_orientation(Gtk::Orientation::VERTICAL);
140
141 _mode = static_cast<PaintSelector::Mode>(-1); // huh? do you mean 0xff? -- I think this means "not in the enum"
142
143 /* Paint style button box */
144 _style = Gtk::make_managed<Gtk::Box>();
145 _style->set_name("PaintSelector");
146 _style->set_visible(true);
147 UI::pack_start(*this, *_style, false, false);
148
149 /* Buttons */
150 _none = style_button_add(INKSCAPE_ICON("paint-none"), PaintSelector::MODE_NONE, _("No paint"));
151 _solid = style_button_add(INKSCAPE_ICON("paint-solid"), PaintSelector::MODE_SOLID_COLOR, _("Flat color"));
152 _gradient = style_button_add(INKSCAPE_ICON("paint-gradient-linear"), PaintSelector::MODE_GRADIENT_LINEAR,
153 _("Linear gradient"));
154 _radial = style_button_add(INKSCAPE_ICON("paint-gradient-radial"), PaintSelector::MODE_GRADIENT_RADIAL,
155 _("Radial gradient"));
156#ifdef WITH_MESH
157 _mesh =
158 style_button_add(INKSCAPE_ICON("paint-gradient-mesh"), PaintSelector::MODE_GRADIENT_MESH, _("Mesh gradient"));
159#endif
160 _pattern = style_button_add(INKSCAPE_ICON("paint-pattern"), PaintSelector::MODE_PATTERN, _("Pattern"));
161 _swatch = style_button_add(INKSCAPE_ICON("paint-swatch"), PaintSelector::MODE_SWATCH, _("Swatch"));
162 _unset = style_button_add(INKSCAPE_ICON("paint-unknown"), PaintSelector::MODE_UNSET,
163 _("Unset paint (make it undefined so it can be inherited)"));
164
165 /* Fillrule */
166 {
167 _fillrulebox = Gtk::make_managed<Gtk::Box>();
168 UI::pack_end(*_style, *_fillrulebox, true, false);
169
170 _evenodd = Gtk::make_managed<FillRuleRadioButton>();
171 _evenodd->set_has_frame(false);
172 // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/painting.html#FillRuleProperty
173 _evenodd->set_tooltip_text(
174 _("Any path self-intersections or subpaths create holes in the fill (fill-rule: evenodd)"));
176 _evenodd->set_image_from_icon_name("fill-rule-even-odd", Gtk::IconSize::NORMAL); // Previously GTK_ICON_SIZE_MENU
177 UI::pack_start(*_fillrulebox, *_evenodd, false, false);
178 _evenodd->signal_toggled().connect(
179 sigc::bind(sigc::mem_fun(*this, &PaintSelector::fillrule_toggled), _evenodd));
180
181 _nonzero = Gtk::make_managed<FillRuleRadioButton>();
182 _nonzero->set_group(*_evenodd);
183 _nonzero->set_has_frame(false);
184 // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/painting.html#FillRuleProperty
185 _nonzero->set_tooltip_text(_("Fill is solid unless a subpath is counterdirectional (fill-rule: nonzero)"));
187 _nonzero->set_image_from_icon_name("fill-rule-nonzero", Gtk::IconSize::NORMAL); // Previously GTK_ICON_SIZE_MENU
188 UI::pack_start(*_fillrulebox, *_nonzero, false, false);
189 _nonzero->signal_toggled().connect(
190 sigc::bind(sigc::mem_fun(*this, &PaintSelector::fillrule_toggled), _nonzero));
191 }
192
193 /* Frame */
194 _label = Gtk::make_managed<Gtk::Label>("");
195 auto const lbbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
196 _label->set_visible(true);
197 UI::pack_start(*lbbox, *_label, false, false, 4);
198 UI::pack_start(*this, *lbbox, false, false, 4);
199
200 _frame = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
201 _frame->set_visible(true);
202 UI::pack_start(*this, *_frame, true, true);
203
204 _selected_colors->signal_grabbed.connect(sigc::mem_fun(*this, &PaintSelector::onSelectedColorGrabbed));
205 _selected_colors->signal_released.connect(sigc::mem_fun(*this, &PaintSelector::onSelectedColorReleased));
206 _selected_colors->signal_changed.connect(sigc::mem_fun(*this, &PaintSelector::onSelectedColorChanged));
207
208 // from _new function
210
211 _fillrulebox->set_visible(kind == FILL);
212}
213
214StyleToggleButton *PaintSelector::style_button_add(gchar const *pixmap, PaintSelector::Mode mode, gchar const *tip)
215{
216 auto const b = Gtk::make_managed<StyleToggleButton>();
217 b->set_tooltip_text(tip);
218 b->set_visible(true);
219 b->set_has_frame(false);
220 b->set_style(mode);
221 if (_none) {
222 b->set_group(*_none);
223 }
224
225 b->set_image_from_icon_name(pixmap, Gtk::IconSize::NORMAL); // Previously GTK_ICON_SIZE_BUTTON
226
227 UI::pack_start(*_style, *b, false, false);
228 b->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &PaintSelector::style_button_toggled), b));
229
230 return b;
231}
232
233void PaintSelector::style_button_toggled(StyleToggleButton *tb)
234{
235 if (!_update && tb->get_active()) {
236 // button toggled: explicit user action where fill/stroke style change is initiated/requested
237 set_mode_ex(tb->get_style(), true);
238 }
239}
240
241void PaintSelector::fillrule_toggled(FillRuleRadioButton *tb)
242{
243 if (!_update && tb->get_active()) {
244 auto fr = tb->get_fillrule();
246 }
247}
248
252
253void PaintSelector::set_mode_ex(Mode mode, bool switch_style) {
254 if (_mode != mode) {
255 _update = true;
256 _label->set_visible(true);
257#ifdef SP_PS_VERBOSE
258 g_print("Mode change %d -> %d %s -> %s\n", _mode, mode, modeStrings[_mode], modeStrings[mode]);
259#endif
260 switch (mode) {
261 case MODE_EMPTY:
263 break;
264 case MODE_MULTIPLE:
266 break;
267 case MODE_NONE:
269 break;
270 case MODE_SOLID_COLOR:
272 break;
276 break;
277#ifdef WITH_MESH
280 break;
281#endif
282 case MODE_PATTERN:
284 break;
285 case MODE_HATCH:
287 break;
288 case MODE_SWATCH:
290 break;
291 case MODE_UNSET:
293 break;
294 default:
295 g_warning("file %s: line %d: Unknown paint mode %d", __FILE__, __LINE__, mode);
296 break;
297 }
298 _mode = mode;
299 _signal_mode_changed.emit(_mode, switch_style);
300 _update = false;
301 }
302}
303
305{
306 if (_fillrulebox) {
307 // TODO this flips widgets but does not use a member to store state. Revisit
308 _evenodd->set_active(fillrule == FILLRULE_EVENODD);
309 _nonzero->set_active(fillrule == FILLRULE_NONZERO);
310 }
311}
312
314{
315#ifdef SP_PS_VERBOSE
316 g_print("PaintSelector set SWATCH\n");
317#endif
319
320 if (_selector_swatch) {
321 _selector_swatch->setVector((vector) ? vector->document : nullptr, vector);
322 }
323}
324
326{
327#ifdef SP_PS_VERBOSE
328 g_print("PaintSelector set GRADIENT LINEAR\n");
329#endif
331
332 auto gsel = getGradientFromData();
333
334 gsel->setMode(GradientSelector::MODE_LINEAR);
335 gsel->setGradient(gradient);
336 gsel->setVector((vector) ? vector->document : nullptr, vector);
337 gsel->selectStop(selected);
338}
339
341{
342#ifdef SP_PS_VERBOSE
343 g_print("PaintSelector set GRADIENT RADIAL\n");
344#endif
346
347 auto gsel = getGradientFromData();
348
349 gsel->setMode(GradientSelector::MODE_RADIAL);
350 gsel->setGradient(gradient);
351 gsel->setVector((vector) ? vector->document : nullptr, vector);
352 gsel->selectStop(selected);
353}
354
355#ifdef WITH_MESH
357{
358#ifdef SP_PS_VERBOSE
359 g_print("PaintSelector set GRADIENT MESH\n");
360#endif
362
363 // GradientSelector *gsel = getGradientFromData(this);
364
365 // gsel->setMode(GradientSelector::MODE_GRADIENT_MESH);
366 // gsel->setVector((mesh) ? mesh->document : 0, mesh);
367}
368#endif
369
371{
372 g_return_if_fail(isPaintModeGradient(_mode));
373
374 auto gsel = getGradientFromData();
375 gsel->setUnits(units);
376 gsel->setSpread(spread);
377}
378
380{
381 g_return_if_fail(isPaintModeGradient(_mode));
382
383 auto gsel = getGradientFromData();
384 units = gsel->getUnits();
385 spread = gsel->getSpread();
386}
387
388
390{
391 SPGradient *vect = nullptr;
392
394 auto gsel = getGradientFromData();
395 vect = gsel->getVector();
396 }
397
398 return vect;
399}
400
401
403{
406 getGradientProperties(units, spread);
407 gr->setUnits(units);
408 gr->setSpread(spread);
409 gr->updateRepr();
410}
411
413{
415 _selector_solid_color->set_visible(false);
416 }
417 if (_selector_gradient) {
418 _selector_gradient->set_visible(false);
419 }
420 if (_selector_mesh) {
421 _selector_mesh->set_visible(false);
422 }
423 if (_selector_pattern) {
424 _selector_pattern->set_visible(false);
425 }
426 if (_selector_swatch) {
427 _selector_swatch->set_visible(false);
428 }
429}
430
432{
433 set_style_buttons(nullptr);
434 _style->set_sensitive(false);
435 clear_frame();
436 _label->set_markup(_("<b>No objects</b>"));
437}
438
440{
441 set_style_buttons(nullptr);
442 _style->set_sensitive(true);
443 clear_frame();
444 _label->set_markup(_("<b>Multiple styles</b>"));
445}
446
448{
450 _style->set_sensitive(true);
451 clear_frame();
452 _label->set_markup(_("<b>Paint is undefined</b>"));
453}
454
456{
458 _style->set_sensitive(true);
459 clear_frame();
460 _label->set_markup(_("<b>No paint</b>"));
461}
462
463/* Color paint */
464
467
469{
470 if (_updating_color)
471 return;
472
473 if (_mode == MODE_SOLID_COLOR) {
474 if (_selected_colors->isGrabbed()) {
475 _signal_dragged.emit();
476 } else {
477 _signal_changed.emit();
478 }
479 } else {
480 g_warning("PaintSelector::onSelectedColorChanged(): selected color changed while not in color selection mode");
481 }
482}
483
485{
487
489 auto gsel = getGradientFromData();
490 if (gsel) {
491 SPGradient *gradient = gsel->getVector();
492
493 // Gradient can be null if object paint is changed externally (ie. with a color picker tool)
494 if (gradient) {
495 _selected_colors->block();
496 _selected_colors->clear();
497 _selected_colors->set(gradient->getFirstStop()->getId(), gradient->getFirstStop()->getColor());
498 _selected_colors->unblock();
499 }
500 }
501 }
502
504 _style->set_sensitive(true);
505
507 /* Already have color selector */
508 // Do nothing
509 } else {
510 clear_frame();
511
512 /* Create new color selector */
513 /* Create vbox */
515 _selector_solid_color = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 4);
516
517 /* Color selector */
518 auto const color_selector = Gtk::make_managed<ColorNotebook>(_selected_colors);
519 color_selector->set_visible(true);
520 UI::pack_start(*_selector_solid_color, *color_selector, true, true);
521 /* Pack everything to frame */
523 color_selector->set_label(_("<b>Flat color</b>"));
524 }
525
526 _selector_solid_color->set_visible(true);
527 }
528
529 _label->set_markup(""); //_("<b>Flat color</b>"));
530 _label->set_visible(false);
531
532#ifdef SP_PS_VERBOSE
533 g_print("Color req\n");
534#endif
535}
536
537/* Gradient */
538
540
542
544
546
548{
553 }
554 _style->set_sensitive(true);
555
557 // do nothing - the selector should already be a GradientSelector
558 } else {
559 clear_frame();
560 if (!_selector_gradient) {
561 /* Create new gradient selector */
562 try {
563 _selector_gradient = Gtk::make_managed<GradientEditor>("/gradient-edit");
564 _selector_gradient->set_visible(true);
565 _selector_gradient->signal_grabbed().connect(sigc::mem_fun(*this, &PaintSelector::gradient_grabbed));
566 _selector_gradient->signal_dragged().connect(sigc::mem_fun(*this, &PaintSelector::gradient_dragged));
568 _selector_gradient->signal_changed().connect(sigc::mem_fun(*this, &PaintSelector::gradient_changed));
569 _selector_gradient->signal_stop_selected().connect([this](SPStop* stop) { _signal_stop_selected.emit(stop); });
570 /* Pack everything to frame */
571 _frame->append(*_selector_gradient);
572 }
573 catch (std::exception& ex) {
574 g_error("Creation of GradientEditor widget failed: %s.", ex.what());
575 throw;
576 }
577 } else {
578 // Necessary when creating new gradients via the Fill and Stroke dialog
579 _selector_gradient->setVector(nullptr, nullptr);
580 }
581 _selector_gradient->set_visible(true);
582 }
583
584 /* Actually we have to set option menu history here */
587 // sp_gradient_selector_set_mode(SP_GRADIENT_SELECTOR(gsel), SP_GRADIENT_SELECTOR_MODE_LINEAR);
588 // _label->set_markup(_("<b>Linear gradient</b>"));
589 _label->set_visible(false);
592 // _label->set_markup(_("<b>Radial gradient</b>"));
593 _label->set_visible(false);
594 }
595
596#ifdef SP_PS_VERBOSE
597 g_print("Gradient req\n");
598#endif
599}
600
601// ************************* MESH ************************
602#ifdef WITH_MESH
603void PaintSelector::mesh_destroy(GtkWidget *widget, PaintSelector * /*psel*/)
604{
605 // drop our reference to the mesh menu widget
606 g_object_unref(G_OBJECT(widget));
607}
608
609void PaintSelector::mesh_change(GtkWidget * /*widget*/, PaintSelector *psel) { psel->_signal_changed.emit(); }
610
611
615static std::vector<SPMeshGradient *> ink_mesh_list_get(SPDocument *source)
616{
617 std::vector<SPMeshGradient *> pl;
618 if (source == nullptr)
619 return pl;
620
621
622 std::vector<SPObject *> meshes = source->getResourceList("gradient");
623 for (auto meshe : meshes) {
624 if (is<SPMeshGradient>(meshe) && cast<SPGradient>(meshe) == cast<SPGradient>(meshe)->getArray()) { // only if this is a
625 // root mesh
626 pl.push_back(cast<SPMeshGradient>(meshe));
627 }
628 }
629 return pl;
630}
631
635static void sp_mesh_menu_build(GtkWidget *combo, std::vector<SPMeshGradient *> &mesh_list, SPDocument * /*source*/)
636{
637 GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo)));
638 GtkTreeIter iter;
639
640 for (auto i : mesh_list) {
641
642 Inkscape::XML::Node *repr = i->getRepr();
643
644 gchar const *meshid = repr->attribute("id");
645 gchar const *label = meshid;
646
647 // Only relevant if we supply a set of canned meshes.
648 gboolean stockid = false;
649 if (repr->attribute("inkscape:stockid")) {
650 label = _(repr->attribute("inkscape:stockid"));
651 stockid = true;
652 }
653
654 gtk_list_store_append(store, &iter);
655 gtk_list_store_set(store, &iter, COMBO_COL_LABEL, label, COMBO_COL_STOCK, stockid, COMBO_COL_MESH, meshid,
656 COMBO_COL_SEP, FALSE, -1);
657 }
658}
659
664static void sp_mesh_list_from_doc(GtkWidget *combo, SPDocument * /*current_doc*/, SPDocument *source,
665 SPDocument * /*mesh_doc*/)
666{
667 std::vector<SPMeshGradient *> pl = ink_mesh_list_get(source);
668 sp_mesh_menu_build(combo, pl, source);
669}
670
671
672static void ink_mesh_menu_populate_menu(GtkWidget *combo, SPDocument *doc)
673{
674 static SPDocument *meshes_doc = nullptr;
675
676 // If we ever add a list of canned mesh gradients, uncomment following:
677
678 // find and load meshes.svg
679 // if (meshes_doc == NULL) {
680 // char *meshes_source = g_build_filename(INKSCAPE_MESHESDIR, "meshes.svg", NULL);
681 // if (Inkscape::IO::file_test(meshes_source, G_FILE_TEST_IS_REGULAR)) {
682 // meshes_doc = SPDocument::createNewDoc(meshes_source, FALSE);
683 // }
684 // g_free(meshes_source);
685 // }
686
687 // suck in from current doc
688 sp_mesh_list_from_doc(combo, nullptr, doc, meshes_doc);
689
690 // add separator
691 // {
692 // GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo)));
693 // GtkTreeIter iter;
694 // gtk_list_store_append (store, &iter);
695 // gtk_list_store_set(store, &iter,
696 // COMBO_COL_LABEL, "", COMBO_COL_STOCK, false, COMBO_COL_MESH, "", COMBO_COL_SEP, true, -1);
697 // }
698
699 // suck in from meshes.svg
700 // if (meshes_doc) {
701 // doc->ensureUpToDate();
702 // sp_mesh_list_from_doc ( combo, doc, meshes_doc, NULL );
703 // }
704}
705
706
707static GtkWidget *ink_mesh_menu(GtkWidget *combo)
708{
709 SPDocument *doc = SP_ACTIVE_DOCUMENT;
710
711 GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo)));
712 GtkTreeIter iter;
713
714 if (!doc) {
715
716 gtk_list_store_append(store, &iter);
717 gtk_list_store_set(store, &iter, COMBO_COL_LABEL, _("No document selected"), COMBO_COL_STOCK, false,
718 COMBO_COL_MESH, "", COMBO_COL_SEP, false, -1);
719 gtk_widget_set_sensitive(combo, FALSE);
720
721 } else {
722
723 ink_mesh_menu_populate_menu(combo, doc);
724 gtk_widget_set_sensitive(combo, TRUE);
725 }
726
727 // Select the first item that is not a separator
728 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
729 gboolean sep = false;
730 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, COMBO_COL_SEP, &sep, -1);
731 if (sep) {
732 gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
733 }
734 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter);
735 }
736
737 return combo;
738}
739
740
741/*update mesh list*/
743{
744 if (_update) {
745 return;
746 }
747
748 g_assert(_meshmenu != nullptr);
749
750 /* Clear existing menu if any */
751 GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(_meshmenu));
752 gtk_list_store_clear(GTK_LIST_STORE(store));
753
755
756 /* Set history */
757
758 if (mesh && !_meshmenu_update) {
759 _meshmenu_update = true;
760 gchar const *meshname = mesh->getRepr()->attribute("id");
761
762 // Find this mesh and set it active in the combo_box
763 GtkTreeIter iter;
764 gchar *meshid = nullptr;
765 bool valid = gtk_tree_model_get_iter_first(store, &iter);
766 if (!valid) {
767 return;
768 }
769 gtk_tree_model_get(store, &iter, COMBO_COL_MESH, &meshid, -1);
770 while (valid && strcmp(meshid, meshname) != 0) {
771 valid = gtk_tree_model_iter_next(store, &iter);
772 g_free(meshid);
773 meshid = nullptr;
774 gtk_tree_model_get(store, &iter, COMBO_COL_MESH, &meshid, -1);
775 }
776
777 if (valid) {
778 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(_meshmenu), &iter);
779 }
780
781 _meshmenu_update = false;
782 g_free(meshid);
783 }
784}
785
786#ifdef WITH_MESH
788{
791 }
792 _style->set_sensitive(true);
793
795 /* Already have mesh menu */
796 // Do nothing - the Selector is already a Gtk::Box with the required contents
797 } else {
798 clear_frame();
799
800 if (!_selector_mesh) {
801 /* Create vbox */
802 _selector_mesh = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 4);
803
804 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 1);
805
811 gtk_list_store_new(COMBO_N_COLS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN);
812 GtkWidget *combo = ink_combo_box_new_with_model(GTK_TREE_MODEL(store));
813 gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combo), PaintSelector::isSeparator, nullptr, nullptr);
814
815 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
816 gtk_cell_renderer_set_padding(renderer, 2, 0);
817 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
818 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "text", COMBO_COL_LABEL, nullptr);
819
820 ink_mesh_menu(combo);
821 g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(PaintSelector::mesh_change), this);
822 g_signal_connect(G_OBJECT(combo), "destroy", G_CALLBACK(PaintSelector::mesh_destroy), this);
823 _meshmenu = combo;
824 g_object_ref(G_OBJECT(combo));
825
826 gtk_box_append(hb->gobj(), combo);
827 UI::pack_start(*_selector_mesh, *hb, false, false, AUX_BETWEEN_BUTTON_GROUPS);
828
829 g_object_unref(G_OBJECT(store));
830
831 auto const hb2 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
832
833 auto const l = Gtk::make_managed<Gtk::Label>();
834 l->set_markup(_("Use the <b>Mesh tool</b> to modify the mesh."));
835 l->set_wrap(true);
836 l->set_size_request(180, -1);
837 UI::pack_start(*hb2, *l, true, true, AUX_BETWEEN_BUTTON_GROUPS);
838 UI::pack_start(*_selector_mesh, *hb2, false, false, AUX_BETWEEN_BUTTON_GROUPS);
839
840 _frame->append(*_selector_mesh);
841 }
842
843 _selector_mesh->set_visible(true);
844 _label->set_markup(_("<b>Mesh fill</b>"));
845 }
846#ifdef SP_PS_VERBOSE
847 g_print("Mesh req\n");
848#endif
849}
850#endif // WITH_MESH
851
853{
854 g_return_val_if_fail((_mode == MODE_GRADIENT_MESH), NULL);
855
856 /* no mesh menu if we were just selected */
857 if (_meshmenu == nullptr) {
858 return nullptr;
859 }
860 GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(_meshmenu));
861
862 /* Get the selected mesh */
863 GtkTreeIter iter;
864 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(_meshmenu), &iter) ||
865 !gtk_list_store_iter_is_valid(GTK_LIST_STORE(store), &iter)) {
866 return nullptr;
867 }
868
869 gchar *meshid = nullptr;
870 gboolean stockid = FALSE;
871 // gchar *label = nullptr;
872 gtk_tree_model_get(store, &iter, COMBO_COL_STOCK, &stockid, COMBO_COL_MESH, &meshid, -1);
873 // gtk_tree_model_get (store, &iter, COMBO_COL_LABEL, &label, COMBO_COL_STOCK, &stockid, COMBO_COL_MESH, &meshid,
874 // -1); std::cout << " .. meshid: " << (meshid?meshid:"null") << " label: " << (label?label:"null") << std::endl;
875 // g_free(label);
876 if (meshid == nullptr) {
877 return nullptr;
878 }
879
880 SPMeshGradient *mesh = nullptr;
881 if (strcmp(meshid, "none")) {
882
883 gchar *mesh_name;
884 if (stockid) {
885 mesh_name = g_strconcat("urn:inkscape:mesh:", meshid, nullptr);
886 } else {
887 mesh_name = g_strdup(meshid);
888 }
889
890 SPObject *mesh_obj = get_stock_item(mesh_name);
891 if (mesh_obj && is<SPMeshGradient>(mesh_obj)) {
892 mesh = cast<SPMeshGradient>(mesh_obj);
893 }
894 g_free(mesh_name);
895 } else {
896 std::cerr << "PaintSelector::getMeshGradient: Unexpected meshid value." << std::endl;
897 }
898
899 g_free(meshid);
900
901 return mesh;
902}
903
904#endif
905// ************************ End Mesh ************************
906
907void PaintSelector::set_style_buttons(Gtk::ToggleButton *active)
908{
909 _none->set_active(active == _none);
910 _solid->set_active(active == _solid);
911 _gradient->set_active(active == _gradient);
912 _radial->set_active(active == _radial);
913#ifdef WITH_MESH
914 _mesh->set_active(active == _mesh);
915#endif
916 _pattern->set_active(active == _pattern);
917 _swatch->set_active(active == _swatch);
918 _unset->set_active(active == _unset);
919}
920
921void PaintSelector::pattern_destroy(GtkWidget *widget, PaintSelector * /*psel*/)
922{
923 // drop our reference to the pattern menu widget
924 g_object_unref(G_OBJECT(widget));
925}
926
927void PaintSelector::pattern_change(GtkWidget * /*widget*/, PaintSelector *psel) { psel->_signal_changed.emit(); }
928
929
930/*update pattern list*/
932{
933 if (_update) return;
934 if (!_selector_pattern) return;
935
937}
938
940{
943 }
944
945 _style->set_sensitive(true);
946
948 /* Already have pattern menu */
949 } else {
950 clear_frame();
951
952 if (!_selector_pattern) {
953 _selector_pattern = Gtk::make_managed<PatternEditor>("/pattern-edit", PatternManager::get());
954 _selector_pattern->signal_changed().connect([this](){ _signal_changed.emit(); });
955 _selector_pattern->signal_color_changed().connect([this](Colors::Color const &){ _signal_changed.emit(); });
956 _selector_pattern->signal_edit().connect([this](){ _signal_edit_pattern.emit(); });
957 _frame->append(*_selector_pattern);
958 }
959
960 SPDocument* document = SP_ACTIVE_DOCUMENT;
962 _selector_pattern->set_visible(true);
963 _label->set_visible(false);
964 }
965#ifdef SP_PS_VERBOSE
966 g_print("Pattern req\n");
967#endif
968}
969
971{
974 }
975
976 _style->set_sensitive(true);
977
979 /* Already have hatch menu, for the moment unset */
980 } else {
981 clear_frame();
982
983 _label->set_markup(_("<b>Hatch fill</b>"));
984 }
985#ifdef SP_PS_VERBOSE
986 g_print("Hatch req\n");
987#endif
988}
989
990gboolean PaintSelector::isSeparator(GtkTreeModel *model, GtkTreeIter *iter, gpointer /*data*/)
991{
992 gboolean sep = FALSE;
993 gtk_tree_model_get(model, iter, COMBO_COL_SEP, &sep, -1);
994 return sep;
995}
996
997std::optional<Colors::Color> PaintSelector::get_pattern_color() {
998 if (!_selector_pattern) return Colors::Color(0x000000ff);
999
1001}
1002
1009
1016
1018 Geom::Scale gap(0, 0);
1019 if (!_selector_pattern) return gap;
1020
1022}
1023
1025 if (!_selector_pattern) return Glib::ustring();
1026
1027 return _selector_pattern->get_label();
1028}
1029
1035
1037 g_return_val_if_fail(_mode == MODE_PATTERN, nullptr);
1038
1039 if (!_selector_pattern) return nullptr;
1040
1041 auto sel = _selector_pattern->get_selected();
1042 auto stock_doc = sel.second;
1043
1044 if (sel.first.empty()) return nullptr;
1045
1046 auto patid = sel.first;
1047 SPObject* pat_obj = nullptr;
1048 if (patid != "none") {
1049 if (stock_doc) {
1050 patid = "urn:inkscape:pattern:" + patid;
1051 }
1052 pat_obj = get_stock_item(patid.c_str(), stock_doc != nullptr, stock_doc);
1053 } else {
1054 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1055 pat_obj = doc->getObjectById(patid);
1056 }
1057
1058 return cast<SPPattern>(pat_obj);
1059}
1060
1062{
1065 }
1066
1067 _style->set_sensitive(true);
1068
1070 // Do nothing. The selector is already a SwatchSelector
1071 } else {
1072 clear_frame();
1073
1074 if (!_selector_swatch) {
1075 // Create new gradient selector
1076 _selector_swatch = Gtk::make_managed<SwatchSelector>();
1077
1079 gsel->signal_grabbed().connect(sigc::mem_fun(*this, &PaintSelector::gradient_grabbed));
1080 gsel->signal_dragged().connect(sigc::mem_fun(*this, &PaintSelector::gradient_dragged));
1081 gsel->signal_released().connect(sigc::mem_fun(*this, &PaintSelector::gradient_released));
1082 gsel->signal_changed().connect(sigc::mem_fun(*this, &PaintSelector::gradient_changed));
1083
1084 // Pack everything to frame
1085 _frame->append(*_selector_swatch);
1086 } else {
1087 // Necessary when creating new swatches via the Fill and Stroke dialog
1088 _selector_swatch->setVector(nullptr, nullptr);
1089 }
1090 _selector_swatch->set_visible(true);
1091 _label->set_markup(_("<b>Swatch fill</b>"));
1092 }
1093
1094#ifdef SP_PS_VERBOSE
1095 g_print("Swatch req\n");
1096#endif
1097}
1098
1100{
1102 SPIPaint const &target = *style.getFillOrStroke(kind == FILL);
1103
1104 if (!target.set) {
1105 mode = MODE_UNSET;
1106 } else if (target.isPaintserver()) {
1107 SPPaintServer const *server = kind == FILL ? style.getFillPaintServer() : style.getStrokePaintServer();
1108
1109#ifdef SP_PS_VERBOSE
1110 g_message("PaintSelector::getModeForStyle(%p, %d)", &style, kind);
1111 g_message("==== server:%p %s grad:%s swatch:%s", server, server->getId(),
1112 (is<SPGradient>(server) ? "Y" : "n"),
1113 (is<SPGradient>(server) && cast<SPGradient>(server)->getVector()->isSwatch() ? "Y" : "n"));
1114#endif // SP_PS_VERBOSE
1115
1116
1117 if (server && is<SPGradient>(server) && cast<SPGradient>(server)->getVector()->isSwatch()) {
1118 mode = MODE_SWATCH;
1119 } else if (is<SPLinearGradient>(server)) {
1121 } else if (is<SPRadialGradient>(server)) {
1123#ifdef WITH_MESH
1124 } else if (is<SPMeshGradient>(server)) {
1126#endif
1127 } else if (is<SPPattern>(server)) {
1129 } else if (is<SPHatch>(server)) {
1130 mode = MODE_HATCH;
1131 } else {
1132 g_warning("file %s: line %d: Unknown paintserver", __FILE__, __LINE__);
1133 mode = MODE_NONE;
1134 }
1135 } else if (target.isColor()) {
1136 // TODO this is no longer a valid assertion:
1137 mode = MODE_SOLID_COLOR; // so far only rgb can be read from svg
1138 } else if (target.isNone()) {
1139 mode = MODE_NONE;
1140 } else {
1141 g_warning("file %s: line %d: Unknown paint type", __FILE__, __LINE__);
1142 mode = MODE_NONE;
1143 }
1144
1145 return mode;
1146}
1147
1148} // namespace Widget
1149} // namespace UI
1150} // namespace Inkscape
1151/*
1152 Local Variables:
1153 mode:c++
1154 c-file-style:"stroustrup"
1155 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1156 indent-tabs-mode:nil
1157 fill-column:99
1158 End:
1159*/
1160// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Cartesian point / 2D vector and related operations.
3x3 affine transformation matrix.
Fragment store
Definition canvas.cpp:155
3x3 matrix representing an affine transformation.
Definition affine.h:70
Two-dimensional point that doubles as a vector.
Definition point.h:66
Scaling from the origin.
Definition transforms.h:150
sigc::signal< void(SPStop *)> & signal_stop_selected()
void setMode(SelectorMode mode) final
decltype(_signal_grabbed) signal_grabbed() const
void setVector(SPDocument *doc, SPGradient *vector) final
decltype(_signal_changed) signal_changed() const
decltype(_signal_released) signal_released() const
decltype(_signal_dragged) signal_dragged() const
decltype(_signal_grabbed) signal_grabbed() const
Generic paint selector widget.
void setGradientRadial(SPGradient *vector, SPRadialGradient *gradient, SPStop *selected)
sigc::signal< void()> _signal_released
void updateMeshList(SPMeshGradient *pat)
std::shared_ptr< Colors::ColorSet > _selected_colors
void style_button_toggled(StyleToggleButton *tb)
sigc::signal< void(SPStop *)> _signal_stop_selected
void set_mode_hatch(PaintSelector::Mode mode)
void set_mode_pattern(PaintSelector::Mode mode)
void setGradientLinear(SPGradient *vector, SPLinearGradient *gradient, SPStop *selected)
sigc::signal< void()> _signal_grabbed
GradientSelectorInterface * getGradientFromData() const
void set_mode_ex(Mode mode, bool switch_style)
void set_mode_swatch(PaintSelector::Mode mode)
sigc::signal< void()> _signal_dragged
void setGradientProperties(SPGradientUnits units, SPGradientSpread spread)
void set_style_buttons(Gtk::ToggleButton *active)
static gboolean isSeparator(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
void fillrule_toggled(FillRuleRadioButton *tb)
sigc::signal< void()> _signal_changed
void set_mode_gradient(PaintSelector::Mode mode)
std::optional< Colors::Color > get_pattern_color()
void set_mode_mesh(PaintSelector::Mode mode)
void setGradientMesh(SPMeshGradient *array)
void getGradientProperties(SPGradientUnits &units, SPGradientSpread &spread) const
sigc::signal< void(Mode, bool)> _signal_mode_changed
PaintSelector(FillOrStroke kind, std::shared_ptr< Colors::ColorSet > colors)
StyleToggleButton * style_button_add(gchar const *px, PaintSelector::Mode mode, gchar const *tip)
sigc::signal< void(FillRule)> _signal_fillrule_changed
sigc::signal< void()> _signal_edit_pattern
static void pattern_change(GtkWidget *widget, PaintSelector *psel)
static void mesh_change(GtkWidget *widget, PaintSelector *psel)
void pushAttrsToGradient(SPGradient *gr) const
static void mesh_destroy(GtkWidget *widget, PaintSelector *psel)
static Mode getModeForStyle(SPStyle const &style, FillOrStroke kind)
static void pattern_destroy(GtkWidget *widget, PaintSelector *psel)
std::optional< Colors::Color > get_selected_color()
decltype(_signal_edit) signal_edit() const
decltype(_signal_color_changed) signal_color_changed() const
std::pair< std::string, SPDocument * > get_selected()
decltype(_signal_changed) signal_changed() const
void set_document(SPDocument *document)
void set_selected(SPPattern *pattern)
void setVector(SPDocument *doc, SPGradient *vector)
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.
Typed SVG document implementation.
Definition document.h:101
SPObject * getObjectById(std::string const &id) const
std::vector< SPObject * > const getResourceList(char const *key)
Gradient.
Definition sp-gradient.h:86
SPStop * getFirstStop()
SPGradient * getVector(bool force_private=false)
Returns private vector of given gradient (the gradient at the end of the href chain which has stops),...
void setSpread(SPGradientSpread spread)
Set spread property of gradient and emit modified.
void setUnits(SPGradientUnits units)
Set units property of gradient and emit modified.
void set(SPAttr key, char const *value) override
Set gradient attribute to value.
Paint type internal to SPStyle.
bool isPaintserver() const
bool isColor() const
bool isNone() const
Linear gradient.
Mesh gradient.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
SPDocument * document
Definition sp-object.h:188
char const * getId() const
Returns the objects current ID string.
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
Radial gradient.
Gradient stop.
Definition sp-stop.h:31
Inkscape::Colors::Color getColor() const
Definition sp-stop.cpp:130
An SVG style object.
Definition style.h:45
SPPaintServer * getFillPaintServer()
Definition style.h:339
SPPaintServer * getStrokePaintServer()
Definition style.h:343
SPIPaint * getFillOrStroke(bool fill_)
Get either the fill or the stroke property.
Definition style.h:355
A notebook with RGB, CMYK, CMS, HSL, and Wheel pages.
TODO: insert short description here.
@ FILL
Gradient vector and position widget.
Icon Loader.
Macro for icon names used in Inkscape.
double offset
Glib::ustring label
static GtkWidget * ink_mesh_menu(GtkWidget *combo)
static void sp_mesh_menu_build(GtkWidget *combo, std::vector< SPMeshGradient * > &mesh_list, SPDocument *)
Adds menu items for mesh list.
static std::vector< SPMeshGradient * > ink_mesh_list_get(SPDocument *source)
Returns a list of meshes in the defs of the given source document as a vector.
static bool isPaintModeGradient(PaintSelector::Mode mode)
static void sp_mesh_list_from_doc(GtkWidget *combo, SPDocument *, SPDocument *source, SPDocument *)
Pick up all meshes from source, except those that are in current_doc (if non-NULL),...
static void ink_mesh_menu_populate_menu(GtkWidget *combo, SPDocument *doc)
void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the end of box.
Definition pack.cpp:153
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
Helper class to stream background task notifications as a series of messages.
STL namespace.
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
static gchar const * modeStrings[]
PaintSelector: Generic paint selector widget.
int mode
TODO: insert short description here.
C facade to Inkscape::XML::Node.
Inkscape::IO::Resource - simple resource API.
SPGradientSpread
@ SP_GRADIENT_SPREAD_PAD
SPGradientUnits
@ SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX
SVG <hatch> implementation.
TODO: insert short description here.
TODO: insert short description here.
SVG <pattern> implementation.
TODO: insert short description here.
TODO: insert short description here.
SPObject * get_stock_item(gchar const *urn, bool stock, SPDocument *stock_doc)
TODO: insert short description here.
SPStyle - a style object for SPItem objects.
TODO: insert short description here.
Affine transformation classes.
struct _GtkListStore GtkListStore
TODO: insert short description here.