Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
mesh-toolbar.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * bulia byak <bulia@dr.com>
8 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
9 * Abhishek Sharma
10 * Tavmjong Bah <tavjong@free.fr>
11 * Vaibhav Malik <vaibhavmalik2018@gmail.com>
12 *
13 * Copyright (C) 2012 Tavmjong Bah
14 * Copyright (C) 2007 Johan Engelen
15 * Copyright (C) 2005 authors
16 *
17 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
18 */
19
20#include "mesh-toolbar.h"
21
22#include <glibmm/i18n.h>
23#include <gtkmm/comboboxtext.h>
24#include <gtkmm/liststore.h>
25#include <gtkmm/messagedialog.h>
26#include <gtkmm/togglebutton.h>
27
28#include "desktop.h"
29#include "document-undo.h"
30#include "document.h"
31#include "gradient-drag.h"
32#include "inkscape.h"
33#include "object/sp-defs.h"
35#include "selection.h"
36#include "style.h"
37#include "ui/builder-utils.h"
38#include "ui/dialog-run.h"
39#include "ui/icon-names.h"
41#include "ui/tools/mesh-tool.h"
42#include "ui/util.h"
45
48
49namespace Inkscape::UI::Toolbar {
50namespace {
51
52// Get a list of selected meshes taking into account fill/stroke toggles
53std::vector<SPMeshGradient *> ms_get_dt_selected_gradients(Selection *selection)
54{
55 std::vector<SPMeshGradient *> ms_selected;
56
57 auto prefs = Preferences::get();
58 bool edit_fill = prefs->getBool("/tools/mesh/edit_fill", true);
59 bool edit_stroke = prefs->getBool("/tools/mesh/edit_stroke", true);
60
61 for (auto item : selection->items()) {
62 auto style = item->style;
63 if (!style) {
64 continue;
65 }
66
67 if (edit_fill && style->fill.isPaintserver()) {
68 auto server = item->style->getFillPaintServer();
69 if (auto mesh = cast<SPMeshGradient>(server)) {
70 ms_selected.push_back(mesh);
71 }
72 }
73
74 if (edit_stroke && style->stroke.isPaintserver()) {
75 auto server = item->style->getStrokePaintServer();
76 if (auto mesh = cast<SPMeshGradient>(server)) {
77 ms_selected.push_back(mesh);
78 }
79 }
80 }
81
82 return ms_selected;
83}
84
85/*
86 * Get the current selection status from the desktop
87 */
88void ms_read_selection(Selection *selection,
89 SPMeshGradient *&ms_selected,
90 bool &ms_selected_multi,
91 SPMeshType &ms_type,
92 bool &ms_type_multi)
93{
94 ms_selected = nullptr;
95 ms_selected_multi = false;
96 ms_type = SP_MESH_TYPE_COONS;
97 ms_type_multi = false;
98
99 bool first = true;
100
101 // Read desktop selection, taking into account fill/stroke toggles
102 for (auto const &mesh : ms_get_dt_selected_gradients(selection)) {
103 if (first) {
104 ms_selected = mesh;
105 ms_type = mesh->type;
106 first = false;
107 } else {
108 if (ms_selected != mesh) {
109 ms_selected_multi = true;
110 }
111 if (ms_type != mesh->type) {
112 ms_type_multi = true;
113 }
114 }
115 }
116}
117
118} // namespace
119
121 : MeshToolbar{create_builder("toolbar-mesh.ui")}
122{}
123
124MeshToolbar::MeshToolbar(Glib::RefPtr<Gtk::Builder> const &builder)
125 : Toolbar{get_widget<Gtk::Box>(builder, "mesh-toolbar")}
126 , _row_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_row_item")}
127 , _col_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_col_item")}
128 , _edit_fill_btn{&get_widget<Gtk::ToggleButton>(builder, "_edit_fill_btn")}
129 , _edit_stroke_btn{&get_widget<Gtk::ToggleButton>(builder, "_edit_stroke_btn")}
130 , _show_handles_btn{&get_widget<Gtk::ToggleButton>(builder, "show_handles_btn")}
131{
132 auto prefs = Preferences::get();
133
134 // Configure the types combo box.
136 auto store = Gtk::ListStore::create(columns);
137 Gtk::TreeModel::Row row;
138
139 row = *store->append();
140 row[columns.col_label] = C_("Type", "Coons");
141 row[columns.col_sensitive] = true;
142
143 row = *store->append();
144 row[columns.col_label] = _("Bicubic");
145 row[columns.col_sensitive] = true;
146
148 _("Smoothing"),
149 // TRANSLATORS: Type of Smoothing. See https://en.wikipedia.org/wiki/Coons_patch
150 _("Coons: no smoothing. Bicubic: smoothing across patch boundaries."), "Not Used", store));
153
154 _select_type_item->signal_changed().connect(sigc::mem_fun(*this, &MeshToolbar::type_changed));
155 get_widget<Gtk::Box>(builder, "select_type_box").append(*_select_type_item);
156
157 // Setup the spin buttons.
160
162 {1, ""},
163 {2, ""},
164 {3, ""},
165 {4, ""},
166 {5, ""},
167 {6, ""},
168 {7, ""},
169 {8, ""},
170 {9, ""},
171 {10, ""},
172 });
173
175 {1, ""},
176 {2, ""},
177 {3, ""},
178 {4, ""},
179 {5, ""},
180 {6, ""},
181 {7, ""},
182 {8, ""},
183 {9, ""},
184 {10, ""},
185 });
186
187 // Configure mode buttons
188 int mode = prefs->getInt("/tools/mesh/mesh_geometry", SP_MESH_GEOMETRY_NORMAL);
189
190 int btn_index = 0;
191 for_each_child(get_widget<Gtk::Box>(builder, "new_type_buttons_box"), [&](Gtk::Widget &item){
192 auto &btn = dynamic_cast<Gtk::ToggleButton &>(item);
193 btn.set_active(btn_index == mode);
194 btn.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &MeshToolbar::new_geometry_changed), btn_index++));
196 });
197
198 mode = prefs->getInt("/tools/mesh/newfillorstroke");
199
200 btn_index = 0;
201 for_each_child(get_widget<Gtk::Box>(builder, "new_fillstroke_buttons_box"), [&](Gtk::Widget &item){
202 auto &btn = dynamic_cast<Gtk::ToggleButton &>(item);
203 btn.set_active(btn_index == mode);
204 btn.signal_clicked().connect(
205 sigc::bind(sigc::mem_fun(*this, &MeshToolbar::new_fillstroke_changed), btn_index++));
207 });
208
209 // Edit fill mesh.
210 _edit_fill_pusher.reset(new UI::SimplePrefPusher(_edit_fill_btn, "/tools/mesh/edit_fill"));
211
212 // Edit stroke mesh.
213 _edit_stroke_pusher.reset(new UI::SimplePrefPusher(_edit_stroke_btn, "/tools/mesh/edit_stroke"));
214
215 // Show/hide side and tensor handles.
216 _show_handles_pusher.reset(new UI::SimplePrefPusher(_show_handles_btn, "/tools/mesh/show_handles"));
217
219
220 // Signals.
221 _edit_fill_btn->signal_toggled().connect(sigc::mem_fun(*this, &MeshToolbar::toggle_fill_stroke));
222 _edit_stroke_btn->signal_toggled().connect(sigc::mem_fun(*this, &MeshToolbar::toggle_fill_stroke));
223 _show_handles_btn->signal_toggled().connect(sigc::mem_fun(*this, &MeshToolbar::toggle_handles));
224 get_widget<Gtk::Button>(builder, "toggle_sides_btn")
225 .signal_clicked()
226 .connect(sigc::mem_fun(*this, &MeshToolbar::toggle_sides));
227 get_widget<Gtk::Button>(builder, "make_elliptical_btn")
228 .signal_clicked()
229 .connect(sigc::mem_fun(*this, &MeshToolbar::make_elliptical));
230 get_widget<Gtk::Button>(builder, "pick_colors_btn")
231 .signal_clicked()
232 .connect(sigc::mem_fun(*this, &MeshToolbar::pick_colors));
233 get_widget<Gtk::Button>(builder, "scale_mesh_btn")
234 .signal_clicked()
235 .connect(sigc::mem_fun(*this, &MeshToolbar::fit_mesh));
236
237 get_widget<Gtk::Button>(builder, "warning_btn").signal_clicked().connect([this] { warning_popup(); });
238}
239
240MeshToolbar::~MeshToolbar() = default;
241
243{
244 if (_desktop) {
245 c_selection_changed.disconnect();
246 c_selection_modified.disconnect();
247 c_subselection_changed.disconnect();
248 c_defs_release.disconnect();
249 c_defs_modified.disconnect();
250 }
251
253
254 if (_desktop) {
255 // connect to selection modified and changed signals
256 auto sel = desktop->getSelection();
257 auto document = desktop->getDocument();
258
259 c_selection_changed = sel->connectChanged([this] (auto) { selection_changed(); });
260 c_selection_modified = sel->connectModified([this] (auto, auto) { selection_changed(); });
261
262 c_defs_release = document->getDefs()->connectRelease([this] (auto) { selection_changed(); });
263 c_defs_modified = document->getDefs()->connectModified([this] (auto, auto) { selection_changed(); });
265 }
266}
267
269 double default_value, ValueChangedMemFun value_changed_mem_fun)
270{
271 auto const path = "/tools/mesh/" + name;
272 auto const val = Preferences::get()->getDouble(path, default_value);
273
274 auto adj = btn.get_adjustment();
275 adj->set_value(val);
276 adj->signal_value_changed().connect(sigc::mem_fun(*this, value_changed_mem_fun));
277
278 btn.setDefocusTarget(this);
279}
280
282{
283 Preferences::get()->setInt("/tools/mesh/mesh_geometry", mode);
284}
285
287{
288 Preferences::get()->setInt("/tools/mesh/newfillorstroke", mode);
289}
290
292{
293 if (_blocker.pending()) {
294 return;
295 }
296
297 auto guard = _blocker.block();
298
299 int rows = _row_item.get_adjustment()->get_value();
300
301 Preferences::get()->setInt("/tools/mesh/mesh_rows", rows);
302}
303
305{
306 if (_blocker.pending()) {
307 return;
308 }
309
310 auto guard = _blocker.block();
311
312 int cols = _col_item.get_adjustment()->get_value();
313
314 Preferences::get()->setInt("/tools/mesh/mesh_cols", cols);
315}
316
318{
319 auto prefs = Preferences::get();
320 prefs->setBool("/tools/mesh/edit_fill", _edit_fill_btn->get_active());
321 prefs->setBool("/tools/mesh/edit_stroke", _edit_stroke_btn->get_active());
322
323 if (auto mt = get_mesh_tool()) {
324 auto drag = mt->get_drag();
325 drag->updateDraggers();
326 drag->updateLines();
327 drag->updateLevels();
328 selection_changed(); // Need to update Type widget
329 }
330}
331
333{
334 auto prefs = Preferences::get();
335 prefs->setBool("/tools/mesh/show_handles", _show_handles_btn->get_active());
336
337 if (auto mt = get_mesh_tool()) {
338 mt->get_drag()->refreshDraggers();
339 }
340}
341
342/*
343 * Core function, setup all the widgets whenever something changes on the desktop
344 */
346{
347 if (_blocker.pending()) {
348 return;
349 }
350
351 if (!_desktop) {
352 return;
353 }
354
355 auto selection = _desktop->getSelection();
356 if (selection) {
357 SPMeshGradient *ms_selected = nullptr;
359 bool ms_selected_multi = false;
360 bool ms_type_multi = false;
361 ms_read_selection(selection, ms_selected, ms_selected_multi, ms_type, ms_type_multi);
362
363 if (_select_type_item) {
364 _select_type_item->set_sensitive(!ms_type_multi);
365 auto guard = _blocker.block();
367 }
368 }
369}
370
372{
373 char *msg = _("Mesh gradients are part of SVG 2:\n"
374 "* Syntax may change.\n"
375 "* Web browser implementation is not guaranteed.\n"
376 "\n"
377 "For web: convert to bitmap (Edit->Make bitmap copy).\n"
378 "For print: export to PDF.");
379 auto dialog = std::make_unique<Gtk::MessageDialog>(msg, false, Gtk::MessageType::WARNING, Gtk::ButtonsType::OK, true);
380 dialog_show_modal_and_selfdestruct(std::move(dialog), get_root());
381}
382
387{
388 if (_blocker.pending()) {
389 return;
390 }
391
392 auto selection = _desktop->getSelection();
393 auto meshes = ms_get_dt_selected_gradients(selection);
394
395 auto type = static_cast<SPMeshType>(mode);
396 for (auto &mesh : meshes) {
397 mesh->type = type;
398 mesh->type_set = true;
399 mesh->updateRepr();
400 }
401
402 if (!meshes.empty()) {
403 DocumentUndo::done(_desktop->getDocument(), _("Set mesh type"), INKSCAPE_ICON("mesh-gradient"));
404 }
405}
406
408{
409 if (auto mt = get_mesh_tool()) {
410 mt->corner_operation(MG_CORNER_SIDE_TOGGLE);
411 }
412}
413
415{
416 if (auto mt = get_mesh_tool()) {
417 mt->corner_operation(MG_CORNER_SIDE_ARC);
418 }
419}
420
422{
423 if (auto mt = get_mesh_tool()) {
424 mt->corner_operation(MG_CORNER_COLOR_PICK);
425 }
426}
427
429{
430 if (auto mt = get_mesh_tool()) {
431 mt->fit_mesh_in_bbox();
432 }
433}
434
436{
437 if (!_desktop) {
438 return nullptr;
439 }
440 return dynamic_cast<Tools::MeshTool *>(_desktop->getTool());
441}
442
443} // namespace Inkscape::UI::Toolbar
444
445/*
446 Local Variables:
447 mode:c++
448 c-file-style:"stroustrup"
449 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
450 indent-tabs-mode:nil
451 fill-column:99
452 End:
453*/
454// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Gtk builder utilities.
Fragment store
Definition canvas.cpp:155
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
static Preferences * get()
Access the singleton Preferences object.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
A simple mediator class that sets the state of a Gtk::ToggleButton when a preference is changed.
std::unique_ptr< UI::SimplePrefPusher > _edit_stroke_pusher
void type_changed(int mode)
Sets mesh type: Coons, Bicubic.
UI::Widget::SpinButton & _col_item
void(MeshToolbar::*)() ValueChangedMemFun
UI::Widget::SpinButton & _row_item
sigc::connection c_subselection_changed
std::unique_ptr< UI::SimplePrefPusher > _edit_fill_pusher
UI::Widget::ComboToolItem * _select_type_item
std::unique_ptr< UI::SimplePrefPusher > _show_handles_pusher
Gtk::ToggleButton * _edit_stroke_btn
void setup_derived_spin_button(UI::Widget::SpinButton &btn, Glib::ustring const &name, double default_value, ValueChangedMemFun value_changed_mem_fun)
Tools::MeshTool * get_mesh_tool() const
void setDesktop(SPDesktop *desktop) override
Gtk::ToggleButton * _show_handles_btn
Gtk::ToggleButton * _edit_fill_btn
Base class for all tool toolbars.
Definition toolbar.h:72
virtual void setDesktop(SPDesktop *desktop)
Definition toolbar.h:76
Gtk::TreeModelColumn< bool > col_sensitive
Gtk::TreeModelColumn< Glib::ustring > col_label
void use_group_label(bool use_group_label)
static ComboToolItem * create(const Glib::ustring &label, const Glib::ustring &tooltip, const Glib::ustring &stock_id, Glib::RefPtr< Gtk::ListStore > store, bool has_entry=false)
sigc::signal< void(int)> signal_changed()
SpinButton widget, that allows entry of simple math expressions (also units, when linked with UnitMen...
Definition spinbutton.h:52
void setDefocusTarget(decltype(_defocus_target) target)
Definition spinbutton.h:127
void set_custom_numeric_menu_data(NumericMenuData &&custom_menu_data)
scoped_block block()
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
sigc::connection connectModified(ModifiedSignal::slot_type slot)
Mesh gradient.
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
SPPaintServer * getFillPaintServer()
Definition style.h:339
SPPaintServer * getStrokePaintServer()
Definition style.h:343
A combobox that can be displayed in a toolbar.
Glib::ustring msg
Editable view implementation.
TODO: insert short description here.
Macro for icon names used in Inkscape.
SPItem * item
Definition desktop.h:50
void dialog_show_modal_and_selfdestruct(std::unique_ptr< Gtk::Dialog > dialog, Gtk::Root *root)
Show a dialog modally, destroying it when the user dismisses it.
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
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:103
W & get_derived_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id, Args &&... args)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
int mode
GList * items
@ SP_MESH_GEOMETRY_NORMAL
SPMeshType
A group of classes and functions for manipulating mesh gradients.
@ SP_MESH_TYPE_COONS
@ MG_CORNER_COLOR_PICK
@ MG_CORNER_SIDE_TOGGLE
@ MG_CORNER_SIDE_ARC
TODO: insert short description here.
SPStyle - a style object for SPItem objects.
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder