Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
arc-toolbar.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/* Authors:
7 * MenTaLguY <mental@rydia.net>
8 * Lauris Kaplinski <lauris@kaplinski.com>
9 * bulia byak <buliabyak@users.sf.net>
10 * Frank Felfe <innerspace@iname.com>
11 * John Cliff <simarilius@yahoo.com>
12 * David Turner <novalis@gnu.org>
13 * Josh Andler <scislac@scislac.com>
14 * Jon A. Cruz <jon@joncruz.org>
15 * Maximilian Albert <maximilian.albert@gmail.com>
16 * Tavmjong Bah <tavmjong@free.fr>
17 * Abhishek Sharma
18 * Kris De Gussem <Kris.DeGussem@gmail.com>
19 * Vaibhav Malik <vaibhavmalik2018@gmail.com>
20 *
21 * Copyright (C) 2004 David Turner
22 * Copyright (C) 2003 MenTaLguY
23 * Copyright (C) 1999-2011 authors
24 * Copyright (C) 2001-2002 Ximian, Inc.
25 *
26 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
27 */
28
29#include "arc-toolbar.h"
30
31#include <glibmm/i18n.h>
32#include <gtkmm/adjustment.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-ellipse.h"
39#include "object/sp-namedview.h"
40#include "selection.h"
41#include "ui/builder-utils.h"
42#include "ui/icon-names.h"
43#include "ui/tools/arc-tool.h"
44#include "ui/util.h"
49
53
54namespace Inkscape::UI::Toolbar {
55
57 : ArcToolbar{create_builder("toolbar-arc.ui")}
58{}
59
60ArcToolbar::ArcToolbar(Glib::RefPtr<Gtk::Builder> const &builder)
61 : Toolbar{get_widget<Gtk::Box>(builder, "arc-toolbar")}
62 , _tracker{std::make_unique<UnitTracker>(Util::UNIT_TYPE_LINEAR)}
63 , _mode_item{get_widget<Gtk::Label>(builder, "_mode_item")}
64 , _rx_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_rx_item")}
65 , _ry_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_ry_item")}
66 , _start_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_start_item")}
67 , _end_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_end_item")}
68 , _make_whole{get_widget<Gtk::Button>(builder, "_make_whole")}
69 , _type_buttons{
70 &get_widget<Gtk::ToggleButton>(builder, "slice_btn"),
71 &get_widget<Gtk::ToggleButton>(builder, "arc_btn"),
72 &get_widget<Gtk::ToggleButton>(builder, "chord_btn")
73 }
74{
75 auto unit_menu = _tracker->create_tool_item(_("Units"), "");
76 get_widget<Gtk::Box>(builder, "unit_menu_box").append(*unit_menu);
77
82
84 {1, ""},
85 {2, ""},
86 {3, ""},
87 {5, ""},
88 {10, ""},
89 {20, ""},
90 {50, ""},
91 {100, ""},
92 {200, ""},
93 {500, ""}
94 });
95
97 {1, ""},
98 {2, ""},
99 {3, ""},
100 {5, ""},
101 {10, ""},
102 {20, ""},
103 {50, ""},
104 {100, ""},
105 {200, ""},
106 {500, ""}
107 });
108
109 // Values auto-calculated.
112
113 int type = Preferences::get()->getInt("/tools/shapes/arc/arc_type", 0);
114 type = std::clamp<int>(type, 0, _type_buttons.size() - 1);
115 _type_buttons[type]->set_active();
116
117 for (int i = 0; i < _type_buttons.size(); i++) {
118 _type_buttons[i]->signal_toggled().connect([this, i] {
119 if (_type_buttons[i]->get_active()) {
120 _typeChanged(i);
121 }
122 });
123 }
124
125 _make_whole.signal_clicked().connect(sigc::mem_fun(*this, &ArcToolbar::_setDefaults));
126
128}
129
130ArcToolbar::~ArcToolbar() = default;
131
133{
134 auto const adj = btn.get_adjustment();
135 auto const val = Preferences::get()->getDouble("/tools/shapes/arc/" + name, 0);
136 adj->set_value(Quantity::convert(val, "px", _tracker->getActiveUnit()));
137 adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &ArcToolbar::_valueChanged), adj, name));
138
139 _tracker->addAdjustment(adj->gobj());
140 btn.addUnitTracker(_tracker.get());
141 btn.set_sensitive(false);
142 btn.setDefocusTarget(this);
143}
144
146{
147 auto const adj = btn.get_adjustment();
148 auto const val = Preferences::get()->getDouble("/tools/shapes/arc/" + name, 0);
149 adj->set_value(val);
150 adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &ArcToolbar::_startendValueChanged), adj, name, other_btn.get_adjustment()));
151}
152
154{
155 assert(!_repr);
156 _repr = repr;
157 _ellipse = ellipse;
159 _repr->addObserver(*this);
160}
161
163{
164 assert(_repr);
165 _repr->removeObserver(*this);
167 _repr = nullptr;
168 _ellipse = nullptr;
170}
171
173{
174 if (_desktop) {
175 _selection_changed_conn.disconnect();
176
177 if (_repr) {
178 _detachRepr();
179 }
180 }
181
183
184 if (_desktop) {
185 auto sel = _desktop->getSelection();
187 _selectionChanged(sel); // Synthesize an emission to trigger the update
188
189 _sensitivize();
190 }
191}
192
194{
195 _tracker->setActiveUnit(unit);
196}
197
198void ArcToolbar::_valueChanged(Glib::RefPtr<Gtk::Adjustment> const &adj, Glib::ustring const &value_name)
199{
200 // quit if run by the XML listener or a unit change
201 if (_blocker.pending() || _tracker->isUpdating()) {
202 return;
203 }
204
205 // in turn, prevent XML listener from responding
206 auto guard = _blocker.block();
207
208 // Per SVG spec "a [radius] value of zero disables rendering of the element".
209 // However our implementation does not allow a setting of zero in the UI (not even in the XML editor)
210 // and ugly things happen if it's forced here, so better leave the properties untouched.
211 if (!adj->get_value()) {
212 return;
213 }
214
215 auto const unit = _tracker->getActiveUnit();
216
217 Preferences::get()->setDouble("/tools/shapes/arc/" + value_name, Quantity::convert(adj->get_value(), unit, "px"));
218
219 bool modified = false;
220 for (auto item : _desktop->getSelection()->items()) {
221 if (auto ge = cast<SPGenericEllipse>(item)) {
222
223 if (value_name == "rx") {
224 ge->setVisibleRx(Quantity::convert(adj->get_value(), unit, "px"));
225 } else {
226 ge->setVisibleRy(Quantity::convert(adj->get_value(), unit, "px"));
227 }
228
229 ge->normalize();
230 ge->updateRepr();
231
232 modified = true;
233 }
234 }
235
236 if (modified) {
237 DocumentUndo::done(_desktop->getDocument(), _("Ellipse: Change radius"), INKSCAPE_ICON("draw-ellipse"));
238 }
239}
240
241void ArcToolbar::_startendValueChanged(Glib::RefPtr<Gtk::Adjustment> const &adj, Glib::ustring const &value_name, Glib::RefPtr<Gtk::Adjustment> const &other_adj)
242{
243 Preferences::get()->setDouble("/tools/shapes/arc/" + value_name, adj->get_value());
244
245 // quit if run by the XML listener
246 if (_blocker.pending()) {
247 return;
248 }
249
250 // in turn, prevent XML listener from responding
251 auto guard = _blocker.block();
252
253 bool modified = false;
254 for (auto item : _desktop->getSelection()->items()) {
255 if (auto ge = cast<SPGenericEllipse>(item)) {
256
257 auto const val = Geom::rad_from_deg(adj->get_value());
258 if (value_name == "start") {
259 ge->start = val;
260 } else {
261 ge->end = val;
262 }
263
264 ge->normalize();
265 ge->updateRepr();
266
267 modified = true;
268 }
269 }
270
271 _sensitivize();
272
273 if (modified) {
274 DocumentUndo::maybeDone(_desktop->getDocument(), value_name.c_str(), _("Arc: Change start/end"), INKSCAPE_ICON("draw-ellipse"));
275 }
276}
277
279{
280 Preferences::get()->setInt("/tools/shapes/arc/arc_type", type);
281
282 // quit if run by the XML listener
283 if (_blocker.pending()) {
284 return;
285 }
286
287 // in turn, prevent XML listener from responding
288 auto guard = _blocker.block();
289
290 char const *arc_type = "slice";
291 bool open = false;
292 switch (type) {
293 case 0:
294 arc_type = "slice";
295 open = false;
296 break;
297 case 1:
298 arc_type = "arc";
299 open = true;
300 break;
301 case 2:
302 arc_type = "chord";
303 open = true; // For backward compat, not truly open but chord most like arc.
304 break;
305 default:
306 std::cerr << __FUNCTION__ << ": bad arc type: " << type << std::endl;
307 break;
308 }
309
310 bool modified = false;
311 for (auto item : _desktop->getSelection()->items()) {
312 if (is<SPGenericEllipse>(item)) {
313 auto repr = item->getRepr();
314 repr->setAttribute("sodipodi:open", open ? "true" : nullptr);
315 repr->setAttribute("sodipodi:arc-type", arc_type);
316 item->updateRepr();
317 modified = true;
318 }
319 }
320
321 if (modified) {
322 DocumentUndo::done(_desktop->getDocument(), _("Arc: Change arc type"), INKSCAPE_ICON("draw-ellipse"));
323 }
324}
325
327{
328 _start_item.get_adjustment()->set_value(0.0);
329 _end_item.get_adjustment()->set_value(0.0);
330 onDefocus();
331}
332
334{
335 bool disabled = _start_item.get_adjustment()->get_value() == 0 &&
336 _end_item .get_adjustment()->get_value() == 0 &&
337 _single; // only for a single selected ellipse (for now)
338 for (auto btn : _type_buttons) {
339 btn->set_sensitive(!disabled);
340 }
341 _make_whole.set_sensitive(!disabled);
342}
343
345{
346 if (_repr) {
347 _detachRepr();
348 }
349
350 int n_selected = 0;
351 XML::Node *repr = nullptr;
352 SPGenericEllipse *ellipse = nullptr;
353
354 for (auto item : selection->items()){
355 if (auto ge = cast<SPGenericEllipse>(item)) {
356 n_selected++;
357 repr = ge->getRepr();
358 ellipse = ge;
359 }
360 }
361
362 _single = n_selected == 1;
363
364 if (_single) {
365 _attachRepr(repr, ellipse);
366 _queueUpdate();
367 }
368
369 _mode_item.set_markup(n_selected == 0 ? _("<b>New:</b>") : _("<b>Change:</b>"));
370 _rx_item.set_sensitive(n_selected > 0);
371 _ry_item.set_sensitive(n_selected > 0);
372
373 if (!_single) { // otherwise handled by _queueUpdate
374 _sensitivize();
375 }
376}
377
379{
380 assert(_repr);
381 assert(_ellipse);
382
383 // quit if run by the UI callbacks
384 if (_blocker.pending()) {
385 return;
386 }
387
388 _queueUpdate();
389}
390
392{
393 if (_tick_callback) {
394 return;
395 }
396
397 _tick_callback = add_tick_callback([this] (Glib::RefPtr<Gdk::FrameClock> const &) {
398 _update();
399 _tick_callback = 0;
400 return false;
401 });
402}
403
405{
406 if (!_tick_callback) {
407 return;
408 }
409
410 remove_tick_callback(_tick_callback);
411 _tick_callback = 0;
412}
413
415{
416 assert(_repr);
417 assert(_ellipse);
418
419 // prevent UI callbacks from responding
420 auto guard = _blocker.block();
421
422 _rx_item.get_adjustment()->set_value(Quantity::convert(_ellipse->getVisibleRx(), "px", _tracker->getActiveUnit()));
423 _ry_item.get_adjustment()->set_value(Quantity::convert(_ellipse->getVisibleRy(), "px", _tracker->getActiveUnit()));
424 _start_item.get_adjustment()->set_value(Geom::deg_from_rad(Geom::Angle{_ellipse->start}.radians0()));
425 _end_item.get_adjustment()->set_value(Geom::deg_from_rad(Geom::Angle{_ellipse->end}.radians0()));
426 _type_buttons[_ellipse->arc_type]->set_active();
427
428 _sensitivize();
429}
430
431} // namespace Inkscape::UI::Toolbar
432
433/*
434 Local Variables:
435 mode:c++
436 c-file-style:"stroustrup"
437 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
438 indent-tabs-mode:nil
439 fill-column:99
440 End:
441*/
442// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Gtk builder utilities.
Wrapper for angular values.
Definition angle.h:73
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
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
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.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
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.
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
void setActiveUnit(Util::Unit const *unit) override
UI::Widget::SpinButton & _ry_item
Definition arc-toolbar.h:76
UI::Widget::SpinButton & _rx_item
Definition arc-toolbar.h:75
UI::Widget::SpinButton & _start_item
Definition arc-toolbar.h:77
void notifyAttributeChanged(XML::Node &node, GQuark name, Util::ptr_shared old_value, Util::ptr_shared new_value) override
Attribute change callback.
std::vector< Gtk::ToggleButton * > _type_buttons
Definition arc-toolbar.h:82
UI::Widget::SpinButton & _end_item
Definition arc-toolbar.h:78
void _startendValueChanged(Glib::RefPtr< Gtk::Adjustment > const &adj, Glib::ustring const &value_name, Glib::RefPtr< Gtk::Adjustment > const &other_adj)
std::unique_ptr< UI::Widget::UnitTracker > _tracker
Definition arc-toolbar.h:73
void setDesktop(SPDesktop *desktop) override
void _valueChanged(Glib::RefPtr< Gtk::Adjustment > const &adj, Glib::ustring const &value_name)
void _selectionChanged(Selection *selection)
sigc::connection _selection_changed_conn
void _setupStartendButton(UI::Widget::SpinButton &btn, Glib::ustring const &name, UI::Widget::SpinButton &other_btn)
void _attachRepr(XML::Node *repr, SPGenericEllipse *ellipse)
void _setupDerivedSpinButton(UI::Widget::SpinButton &btn, Glib::ustring const &name)
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 addUnitTracker(UnitTracker *ut)
Definition spinbutton.h:68
void setDefocusTarget(decltype(_defocus_target) target)
Definition spinbutton.h:127
void set_custom_numeric_menu_data(NumericMenuData &&custom_menu_data)
Interface for refcounted XML nodes.
Definition node.h:80
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual void addObserver(NodeObserver &observer)=0
Add an object that will be notified of the changes to this node.
virtual void removeObserver(NodeObserver &observer)=0
Remove an object from the list of observers.
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
double getVisibleRy() const
double getVisibleRx() const
GenericEllipseArcType arc_type
Definition sp-ellipse.h:55
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.
guint32 GQuark
SPDesktop * desktop
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder
TODO: insert short description here.