Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
star-toolbar.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/* Authors:
6 * MenTaLguY <mental@rydia.net>
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Frank Felfe <innerspace@iname.com>
10 * John Cliff <simarilius@yahoo.com>
11 * David Turner <novalis@gnu.org>
12 * Josh Andler <scislac@scislac.com>
13 * Jon A. Cruz <jon@joncruz.org>
14 * Maximilian Albert <maximilian.albert@gmail.com>
15 * Tavmjong Bah <tavmjong@free.fr>
16 * Abhishek Sharma
17 * Kris De Gussem <Kris.DeGussem@gmail.com>
18 * Vaibhav Malik <vaibhavmalik2018@gmail.com>
19 *
20 * Copyright (C) 2004 David Turner
21 * Copyright (C) 2003 MenTaLguY
22 * Copyright (C) 1999-2011 authors
23 * Copyright (C) 2001-2002 Ximian, Inc.
24 *
25 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
26 */
27
28#include "star-toolbar.h"
29
30#include <glibmm/i18n.h>
31#include <gtkmm/adjustment.h>
32#include <gtkmm/box.h>
33#include <gtkmm/label.h>
34#include <gtkmm/togglebutton.h>
35
36#include "desktop.h"
37#include "document-undo.h"
38#include "object/sp-star.h"
39#include "selection.h"
40#include "ui/builder-utils.h"
41#include "ui/icon-names.h"
42#include "ui/tools/star-tool.h"
43#include "ui/util.h"
47
49
50namespace Inkscape::UI::Toolbar {
51
53 : StarToolbar{create_builder("toolbar-star.ui")}
54{}
55
56StarToolbar::StarToolbar(Glib::RefPtr<Gtk::Builder> const &builder)
57 : Toolbar{get_widget<Gtk::Box>(builder, "star-toolbar")}
58 , _mode_item{get_widget<Gtk::Label>(builder, "_mode_item")}
59 , _magnitude_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_magnitude_item")}
60 , _spoke_box{get_widget<Gtk::Box>(builder, "_spoke_box")}
61 , _spoke_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_spoke_item")}
62 , _roundedness_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_roundedness_item")}
63 , _randomization_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_randomization_item")}
64 , _tracker{std::make_unique<UI::Widget::UnitTracker>(Util::UNIT_TYPE_LINEAR)}
65 , _length_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_length_item")}
66{
67 bool is_flat_sided = Preferences::get()->getBool("/tools/shapes/star/isflatsided", false);
68
70 {2, ""},
71 {3, _("triangle/tri-star")},
72 {4, _("square/quad-star")},
73 {5, _("pentagon/five-pointed star")},
74 {6, _("hexagon/six-pointed star")},
75 {7, ""},
76 {8, ""},
77 {10, ""},
78 {12, ""},
79 {20, ""}
80 });
81
83 {0.010, _("thin-ray star")},
84 {0.200, ""},
85 {0.382, _("pentagram")},
86 {0.577, _("hexagram")},
87 {0.692, _("heptagram")},
88 {0.765, _("octagram")},
89 {1.000, _("regular polygon")}
90 });
91
93 {-1.0 , _("stretched")},
94 {-0.2 , _("twisted")},
95 {-0.03, _("slightly pinched")},
96 { 0.0 , _("NOT rounded")},
97 { 0.05, _("slightly rounded")},
98 { 0.1 , _("visibly rounded")},
99 { 0.2 , _("well rounded")},
100 { 0.3 , _("amply rounded")},
101 { 0.5 , ""},
102 { 1.0 , _("stretched")},
103 {10.0 , _("blown up")}
104 });
105
107 { 0.00, _("NOT randomized")},
108 { 0.01, _("slightly irregular")},
109 { 0.10, _("visibly randomized")},
110 { 0.50, _("strongly randomized")},
111 {10.00, _("blown up")}
112 });
113
119
120 // Flatsided checkbox
121 _flat_item_buttons.push_back(&get_widget<Gtk::ToggleButton>(builder, "flat_polygon_button"));
122 _flat_item_buttons.push_back(&get_widget<Gtk::ToggleButton>(builder, "flat_star_button"));
123 _flat_item_buttons[is_flat_sided ? 0 : 1]->set_active();
124
125 int btn_index = 0;
126 for (auto btn : _flat_item_buttons) {
127 btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &StarToolbar::side_mode_changed), btn_index++));
128 }
129
130 get_widget<Gtk::Button>(builder, "reset_btn")
131 .signal_clicked()
132 .connect(sigc::mem_fun(*this, &StarToolbar::_setDefaults));
133
134 _spoke_box.set_visible(!is_flat_sided);
135
136 auto unit_menu = _tracker->create_tool_item(_("Units"), (""));
137 get_widget<Gtk::Box>(builder, "unit_menu_box").append(*unit_menu);
138 _tracker->addAdjustment(_length_item.get_adjustment()->gobj());
139
141}
142
143StarToolbar::~StarToolbar() = default;
144
146{
147 assert(!_repr);
148 _repr = repr;
150 _repr->addObserver(*this);
151}
152
154{
155 assert(_repr);
156 _repr->removeObserver(*this);
158 _repr = nullptr;
159}
160
162 double default_value, ValueChangedMemFun value_changed_mem_fun)
163{
164 auto const path = "/tools/shapes/star/" + name;
165 auto const val = Preferences::get()->getDouble(path, default_value);
166
167 auto adj = btn.get_adjustment();
168 adj->set_value(val);
169 adj->signal_value_changed().connect(sigc::mem_fun(*this, value_changed_mem_fun));
170
171 btn.setDefocusTarget(this);
172}
173
175{
176 if (_desktop) {
177 _selection_changed_conn.disconnect();
178 _selection_modified_conn.disconnect();
179
180 if (_repr) {
181 _detachRepr();
182 }
183 }
184
186
187 if (_desktop) {
188 auto sel = _desktop->getSelection();
190 _selection_modified_conn = sel->connectChanged(sigc::mem_fun(*this, &StarToolbar::_selectionModified));
191 _selectionChanged(sel); // Synthesize an emission to trigger the update
192 }
193}
194
196{
197 _tracker->setActiveUnit(unit);
198}
199
201{
202 bool const flat = mode == 0;
203
205 Preferences::get()->setBool("/tools/shapes/star/isflatsided", flat);
206 }
207
208 // quit if run by the attr_changed listener
209 if (_blocker.pending()) {
210 return;
211 }
212
213 // in turn, prevent listener from responding
214 auto guard = _blocker.block();
215
216 auto adj = _magnitude_item.get_adjustment();
217 _spoke_box.set_visible(!flat);
218
219 for (auto item : _desktop->getSelection()->items()) {
220 if (is<SPStar>(item)) {
221 auto repr = item->getRepr();
222 if (flat) {
223 int sides = adj->get_value();
224 if (sides < 3) {
225 repr->setAttributeInt("sodipodi:sides", 3);
226 }
227 }
228 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false");
229
230 item->updateRepr();
231 }
232 }
233
234 adj->set_lower(flat ? 3 : 2);
235 if (flat && adj->get_value() < 3) {
236 adj->set_value(3);
237 }
238
239 if (!_batchundo) {
240 DocumentUndo::done(_desktop->getDocument(), flat ? _("Make polygon") : _("Make star"), INKSCAPE_ICON("draw-polygon-star"));
241 }
242}
243
245{
246 auto adj = _magnitude_item.get_adjustment();
247
249 // do not remember prefs if this call is initiated by an undo change, because undoing object
250 // creation sets bogus values to its attributes before it is deleted
251 Preferences::get()->setInt("/tools/shapes/star/magnitude", adj->get_value());
252 }
253
254 // quit if run by the attr_changed listener
255 if (_blocker.pending()) {
256 return;
257 }
258
259 // in turn, prevent listener from responding
260 auto guard = _blocker.block();
261
262 for (auto item : _desktop->getSelection()->items()) {
263 if (is<SPStar>(item)) {
264 auto repr = item->getRepr();
265 repr->setAttributeInt("sodipodi:sides", adj->get_value());
266 double arg1 = repr->getAttributeDouble("sodipodi:arg1", 0.5);
267 repr->setAttributeSvgDouble("sodipodi:arg2", arg1 + M_PI / adj->get_value());
268 item->updateRepr();
269 }
270 }
271
272 if (!_batchundo) {
273 DocumentUndo::maybeDone(_desktop->getDocument(), "star:numcorners", _("Star: Change number of corners"), INKSCAPE_ICON("draw-polygon-star"));
274 }
275}
276
278{
279 auto adj = _spoke_item.get_adjustment();
280
282 if (!std::isnan(adj->get_value())) {
283 Preferences::get()->setDouble("/tools/shapes/star/proportion", adj->get_value());
284 }
285 }
286
287 // quit if run by the attr_changed listener
288 if (_blocker.pending()) {
289 return;
290 }
291
292 // in turn, prevent listener from responding
293 auto guard = _blocker.block();
294
295 for (auto item : _desktop->getSelection()->items()) {
296 if (is<SPStar>(item)) {
297 auto repr = item->getRepr();
298
299 double r1 = repr->getAttributeDouble("sodipodi:r1", 1.0);
300 double r2 = repr->getAttributeDouble("sodipodi:r2", 1.0);
301
302 if (r2 < r1) {
303 repr->setAttributeSvgDouble("sodipodi:r2", r1 * adj->get_value());
304 } else {
305 repr->setAttributeSvgDouble("sodipodi:r1", r2 * adj->get_value());
306 }
307
308 item->updateRepr();
309 }
310 }
311
312 if (!_batchundo) {
313 DocumentUndo::maybeDone(_desktop->getDocument(), "star:spokeratio", _("Star: Change spoke ratio"), INKSCAPE_ICON("draw-polygon-star"));
314 }
315}
316
318{
319 auto adj = _roundedness_item.get_adjustment();
320
322 Preferences::get()->setDouble("/tools/shapes/star/rounded", adj->get_value());
323 }
324
325 // quit if run by the attr_changed listener
326 if (_blocker.pending()) {
327 return;
328 }
329
330 // in turn, prevent listener from responding
331 auto guard = _blocker.block();
332
333 for (auto item : _desktop->getSelection()->items()) {
334 if (is<SPStar>(item)) {
335 auto repr = item->getRepr();
336 repr->setAttributeSvgDouble("inkscape:rounded", adj->get_value());
337 item->updateRepr();
338 }
339 }
340
341 if (!_batchundo) {
342 DocumentUndo::maybeDone(_desktop->getDocument(), "star:rounding", _("Star: Change rounding"), INKSCAPE_ICON("draw-polygon-star"));
343 }
344}
345
347{
348 auto adj = _randomization_item.get_adjustment();
349
351 Preferences::get()->setDouble("/tools/shapes/star/randomized", adj->get_value());
352 }
353
354 // quit if run by the attr_changed listener
355 if (_blocker.pending()) {
356 return;
357 }
358
359 // in turn, prevent listener from responding
360 auto guard = _blocker.block();
361
362 for (auto item : _desktop->getSelection()->items()) {
363 if (is<SPStar>(item)) {
364 auto repr = item->getRepr();
365 repr->setAttributeSvgDouble("inkscape:randomized", adj->get_value());
366 item->updateRepr();
367 }
368 }
369
370 if (!_batchundo) {
371 DocumentUndo::maybeDone(_desktop->getDocument(), "star:randomisation", _("Star: Change randomization"), INKSCAPE_ICON("draw-polygon-star"));
372 }
373}
374
376{
377 if (!_blocker.pending() || _tracker->isUpdating()) {
378 // in turn, prevent listener from responding
379 auto guard = _blocker.block();
380
381 auto adj = _length_item.get_adjustment();
382 Preferences::get()->setDouble("/tools/shapes/star/length", adj->get_value());
383
384 auto value = Util::Quantity::convert(adj->get_value(), _tracker->getActiveUnit(), "px");
385
386 for (auto item : _desktop->getSelection()->items()) {
387 if (auto star = cast<SPStar>(item)) {
388 star -> setSideLength(value);
389 }
390 }
391 }
392}
393
395{
396 _batchundo = true;
397
398 // fixme: make settable in prefs!
399 int mag = 5;
400 double prop = 0.5;
401 bool flat = false;
402 double randomized = 0;
403 double rounded = 0;
404
405 _flat_item_buttons[flat ? 0 : 1]->set_active();
406
407 _spoke_box.set_visible(!flat);
408
409 if (_magnitude_item.get_adjustment()->get_value() == mag) {
410 // Ensure handler runs even if value not changed, to reset inner handle.
412 } else {
413 _magnitude_item.get_adjustment()->set_value(mag);
414 }
415 _spoke_item.get_adjustment()->set_value(prop);
416 _roundedness_item.get_adjustment()->set_value(rounded);
417 _randomization_item.get_adjustment()->set_value(randomized);
418
419 DocumentUndo::done(_desktop->getDocument(), _("Star: Reset to defaults"), INKSCAPE_ICON("draw-polygon-star"));
420 _batchundo = false;
421}
422
424{
425 if (_repr) {
426 _detachRepr();
427 }
428
429 int n_selected = 0;
430 XML::Node *repr = nullptr;
431 double lengths = 0;
432
433 for (auto item : selection->items()) {
434 if (auto star = cast<SPStar>(item)) {
435 n_selected++;
436 repr = item->getRepr();
437 lengths += star->getSideLength();
438 }
439 }
440
441 _mode_item.set_markup(n_selected == 0 ? _("<b>New:</b>") : _("<b>Change:</b>"));
442 _length_item.set_sensitive(n_selected > 0);
443
444 if (n_selected == 1) {
445 _attachRepr(repr);
446 _repr->synthesizeEvents(*this); // Fixme: BAD
447 }
448 _selectionModified(selection);
449}
450
452{
453 if (!_blocker.pending()|| _tracker->isUpdating()) {
454 auto guard = _blocker.block();
455 auto length_adj = _length_item.get_adjustment();
456
457 int n_selected = 0;
458 double lengths = 0;
459 for (auto item : selection->items()) {
460 if (auto star = cast<SPStar>(item)) {
461 n_selected++;
462 lengths += star->getSideLength();
463 }
464 }
465 if (n_selected > 0) {
466 auto value = Util::Quantity::convert(lengths / n_selected, "px", _tracker->getActiveUnit());
467 length_adj->set_value(value);
468 }
469 }
470}
471
473{
474 assert(_repr);
475
476 // quit if run by the _changed callbacks
477 if (_blocker.pending()) {
478 return;
479 }
480
481 auto const name = g_quark_to_string(name_);
482
483 // in turn, prevent callbacks from responding
484 auto guard = _blocker.block();
485
486 bool isFlatSided = Preferences::get()->getBool("/tools/shapes/star/isflatsided", false);
487 auto mag_adj = _magnitude_item.get_adjustment();
488 auto spoke_adj = _spoke_item.get_adjustment();
489 auto length_adj = _length_item.get_adjustment();
490
491 if (!strcmp(name, "inkscape:randomized")) {
492 double randomized = _repr->getAttributeDouble("inkscape:randomized", 0.0);
493 _randomization_item.get_adjustment()->set_value(randomized);
494 } else if (!strcmp(name, "inkscape:rounded")) {
495 double rounded = _repr->getAttributeDouble("inkscape:rounded", 0.0);
496 _roundedness_item.get_adjustment()->set_value(rounded);
497 } else if (!strcmp(name, "inkscape:flatsided")) {
498 char const *flatsides = _repr->attribute("inkscape:flatsided");
499 if (flatsides && !strcmp(flatsides,"false")) {
500 _flat_item_buttons[1]->set_active();
501 _spoke_box.set_visible(true);
502 mag_adj->set_lower(2);
503 } else {
504 _flat_item_buttons[0]->set_active();
505 _spoke_box.set_visible(false);
506 mag_adj->set_lower(3);
507 }
508 } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2") && !isFlatSided) {
509 double r1 = _repr->getAttributeDouble("sodipodi:r1", 1.0);
510 double r2 = _repr->getAttributeDouble("sodipodi:r2", 1.0);
511
512 if (r2 < r1) {
513 spoke_adj->set_value(r2 / r1);
514 } else {
515 spoke_adj->set_value(r1 / r2);
516 }
517 } else if (!strcmp(name, "sodipodi:sides")) {
518 int sides = _repr->getAttributeInt("sodipodi:sides", 0);
519 mag_adj->set_value(sides);
520 }
521
522 double lengths = 0;
523 int n_selected = 0;
524 for (auto item : _desktop->getSelection()->items()) {
525 if (auto star = cast<SPStar>(item)) {
526 n_selected++;
527 lengths += star->getSideLength();
528 }
529 }
530
531 if (n_selected > 0) {
532 auto value = Util::Quantity::convert(lengths / n_selected, "px", _tracker->getActiveUnit());
533 length_adj->set_value(value);
534 }
535}
536
537} // namespace Inkscape::UI::Toolbar
538
539/*
540 Local Variables:
541 mode:c++
542 c-file-style:"stroustrup"
543 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
544 indent-tabs-mode:nil
545 fill-column:99
546 End:
547*/
548// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :
Gtk builder utilities.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static bool getUndoSensitive(SPDocument const *document)
static void maybeDone(SPDocument *document, const gchar *keyconst, 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 getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
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 setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
std::vector< Gtk::ToggleButton * > _flat_item_buttons
void(StarToolbar::*)() ValueChangedMemFun
UI::Widget::SpinButton & _length_item
void setActiveUnit(Util::Unit const *unit) override
UI::Widget::SpinButton & _randomization_item
void _selectionModified(Selection *selection)
sigc::connection _selection_modified_conn
void notifyAttributeChanged(XML::Node &node, GQuark name, Util::ptr_shared old_value, Util::ptr_shared new_value) override
Attribute change callback.
UI::Widget::SpinButton & _roundedness_item
std::unique_ptr< UI::Widget::UnitTracker > _tracker
void setup_derived_spin_button(UI::Widget::SpinButton &btn, Glib::ustring const &name, double default_value, ValueChangedMemFun value_changed_mem_fun)
UI::Widget::SpinButton & _spoke_item
sigc::connection _selection_changed_conn
UI::Widget::SpinButton & _magnitude_item
void setDesktop(SPDesktop *desktop) override
void _selectionChanged(Selection *selection)
Base class for all tool toolbars.
Definition toolbar.h:72
virtual void setDesktop(SPDesktop *desktop)
Definition toolbar.h:76
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)
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:588
Interface for refcounted XML nodes.
Definition node.h:80
virtual void synthesizeEvents(NodeObserver &observer)=0
Generate a sequence of events corresponding to the state of this node.
double getAttributeDouble(Util::const_char_ptr key, double default_value=0.0) const
Definition node.cpp:76
bool setAttributeInt(Util::const_char_ptr key, int val)
Definition node.cpp:92
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual void addObserver(NodeObserver &observer)=0
Add an object that will be notified of the changes to this node.
int getAttributeInt(Util::const_char_ptr key, int default_value=0) const
Definition node.cpp:67
virtual void removeObserver(NodeObserver &observer)=0
Remove an object from the list of observers.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
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::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.
A combobox that can be displayed in a toolbar.
Editable view implementation.
TODO: insert short description here.
Macro for icon names used in Inkscape.
SPItem * item
Definition desktop.h:50
static R & anchor(R &r)
Increments the reference count of a anchored object.
Definition gc-anchored.h:92
static R & release(R &r)
Decrements the reference count of a anchored object.
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
W & get_derived_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id, Args &&... args)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
Miscellaneous supporting code.
Definition document.h:93
STL namespace.
int mode
guint32 GQuark
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder