1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3 * @file
4 * Rect aux toolbar
5 */
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 *
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 "rect-toolbar.h"
29
30 #include <glibmm/i18n.h>
31
32 #include <gtkmm/separatortoolitem.h>
33 #include <gtkmm/toolbutton.h>
34
35 #include "desktop.h"
36 #include "document-undo.h"
37 #include "selection.h"
38 #include "verbs.h"
39
40 #include "object/sp-namedview.h"
41 #include "object/sp-rect.h"
42
43 #include "ui/icon-names.h"
44 #include "ui/tools/rect-tool.h"
45 #include "ui/uxmanager.h"
46 #include "ui/widget/canvas.h"
47 #include "ui/widget/combo-tool-item.h"
48 #include "ui/widget/label-tool-item.h"
49 #include "ui/widget/spinbutton.h"
50 #include "ui/widget/spin-button-tool-item.h"
51 #include "ui/widget/unit-tracker.h"
52
53 #include "widgets/widget-sizes.h"
54
55 #include "xml/node-event-vector.h"
56
57 using Inkscape::UI::Widget::UnitTracker;
58 using Inkscape::UI::UXManager;
59 using Inkscape::DocumentUndo;
60 using Inkscape::Util::Unit;
61 using Inkscape::Util::Quantity;
62 using Inkscape::Util::unit_table;
63
64 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
65 nullptr, /* child_added */
66 nullptr, /* child_removed */
67 Inkscape::UI::Toolbar::RectToolbar::event_attr_changed,
68 nullptr, /* content_changed */
69 nullptr /* order_changed */
70 };
71
72 namespace Inkscape {
73 namespace UI {
74 namespace Toolbar {
75
RectToolbar(SPDesktop * desktop)76 RectToolbar::RectToolbar(SPDesktop *desktop)
77 : Toolbar(desktop),
78 _tracker(new UnitTracker(Inkscape::Util::UNIT_TYPE_LINEAR)),
79 _freeze(false),
80 _single(true),
81 _repr(nullptr),
82 _mode_item(Gtk::manage(new UI::Widget::LabelToolItem(_("<b>New:</b>"))))
83 {
84 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
85
86 // rx/ry units menu: create
87 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
88 // fixme: add % meaning per cent of the width/height
89 _tracker->setActiveUnit(desktop->getNamedView()->display_units);
90 _mode_item->set_use_markup(true);
91
92 /* W */
93 {
94 auto width_val = prefs->getDouble("/tools/shapes/rect/width", 0);
95 _width_adj = Gtk::Adjustment::create(width_val, 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP);
96 _width_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("rect-width", _("W:"), _width_adj));
97 _width_item->get_spin_button()->addUnitTracker(_tracker);
98 _width_item->set_focus_widget(_desktop->canvas);
99 _width_item->set_all_tooltip_text(_("Width of rectangle"));
100
101 _width_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &RectToolbar::value_changed),
102 _width_adj,
103 "width",
104 &SPRect::setVisibleWidth));
105 _tracker->addAdjustment(_width_adj->gobj());
106 _width_item->set_sensitive(false);
107
108 std::vector<double> values = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
109 _width_item->set_custom_numeric_menu_data(values);
110 }
111
112 /* H */
113 {
114 auto height_val = prefs->getDouble("/tools/shapes/rect/height", 0);
115
116 _height_adj = Gtk::Adjustment::create(height_val, 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP);
117 _height_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &RectToolbar::value_changed),
118 _height_adj,
119 "height",
120 &SPRect::setVisibleHeight));
121 _tracker->addAdjustment(_height_adj->gobj());
122
123 std::vector<double> values = { 1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
124 _height_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("rect-height", _("H:"), _height_adj));
125 _height_item->get_spin_button()->addUnitTracker(_tracker);
126 _height_item->set_custom_numeric_menu_data(values);
127 _height_item->set_all_tooltip_text(_("Height of rectangle"));
128 _height_item->set_focus_widget(_desktop->canvas);
129 _height_item->set_sensitive(false);
130 }
131
132 /* rx */
133 {
134 std::vector<Glib::ustring> labels = {_("not rounded"), "", "", "", "", "", "", "", ""};
135 std::vector<double> values = { 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
136 auto rx_val = prefs->getDouble("/tools/shapes/rect/rx", 0);
137 _rx_adj = Gtk::Adjustment::create(rx_val, 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP);
138 _rx_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &RectToolbar::value_changed),
139 _rx_adj,
140 "rx",
141 &SPRect::setVisibleRx));
142 _tracker->addAdjustment(_rx_adj->gobj());
143 _rx_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("rect-rx", _("Rx:"), _rx_adj));
144 _rx_item->get_spin_button()->addUnitTracker(_tracker);
145 _rx_item->set_all_tooltip_text(_("Horizontal radius of rounded corners"));
146 _rx_item->set_focus_widget(_desktop->canvas);
147 _rx_item->set_custom_numeric_menu_data(values, labels);
148 }
149
150 /* ry */
151 {
152 std::vector<Glib::ustring> labels = {_("not rounded"), "", "", "", "", "", "", "", ""};
153 std::vector<double> values = { 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
154 auto ry_val = prefs->getDouble("/tools/shapes/rect/ry", 0);
155 _ry_adj = Gtk::Adjustment::create(ry_val, 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP);
156 _ry_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &RectToolbar::value_changed),
157 _ry_adj,
158 "ry",
159 &SPRect::setVisibleRy));
160 _tracker->addAdjustment(_ry_adj->gobj());
161 _ry_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("rect-ry", _("Ry:"), _ry_adj));
162 _ry_item->get_spin_button()->addUnitTracker(_tracker);
163 _ry_item->set_all_tooltip_text(_("Vertical radius of rounded corners"));
164 _ry_item->set_focus_widget(_desktop->canvas);
165 _ry_item->set_custom_numeric_menu_data(values, labels);
166 }
167
168 // add the units menu
169 auto unit_menu_ti = _tracker->create_tool_item(_("Units"), (""));
170
171 /* Reset */
172 {
173 _not_rounded = Gtk::manage(new Gtk::ToolButton(_("Not rounded")));
174 _not_rounded->set_tooltip_text(_("Make corners sharp"));
175 _not_rounded->set_icon_name(INKSCAPE_ICON("rectangle-make-corners-sharp"));
176 _not_rounded->signal_clicked().connect(sigc::mem_fun(*this, &RectToolbar::defaults));
177 _not_rounded->set_sensitive(true);
178 }
179
180 add(*_mode_item);
181 add(*_width_item);
182 add(*_height_item);
183 add(*_rx_item);
184 add(*_ry_item);
185 add(*unit_menu_ti);
186 add(* Gtk::manage(new Gtk::SeparatorToolItem()));
187 add(*_not_rounded);
188 show_all();
189
190 sensitivize();
191
192 _desktop->connectEventContextChanged(sigc::mem_fun(*this, &RectToolbar::watch_ec));
193 }
194
~RectToolbar()195 RectToolbar::~RectToolbar()
196 {
197 if (_repr) { // remove old listener
198 _repr->removeListenerByData(this);
199 Inkscape::GC::release(_repr);
200 _repr = nullptr;
201 }
202 }
203
204 GtkWidget *
create(SPDesktop * desktop)205 RectToolbar::create(SPDesktop *desktop)
206 {
207 auto toolbar = new RectToolbar(desktop);
208 return GTK_WIDGET(toolbar->gobj());
209 }
210
211 void
value_changed(Glib::RefPtr<Gtk::Adjustment> & adj,gchar const * value_name,void (SPRect::* setter)(gdouble))212 RectToolbar::value_changed(Glib::RefPtr<Gtk::Adjustment>& adj,
213 gchar const *value_name,
214 void (SPRect::*setter)(gdouble))
215 {
216 Unit const *unit = _tracker->getActiveUnit();
217 g_return_if_fail(unit != nullptr);
218
219 if (DocumentUndo::getUndoSensitive(_desktop->getDocument())) {
220 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
221 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name,
222 Quantity::convert(adj->get_value(), unit, "px"));
223 }
224
225 // quit if run by the attr_changed listener
226 if (_freeze || _tracker->isUpdating()) {
227 return;
228 }
229
230 // in turn, prevent listener from responding
231 _freeze = true;
232
233 bool modmade = false;
234 Inkscape::Selection *selection = _desktop->getSelection();
235 auto itemlist= selection->items();
236 for(auto i=itemlist.begin();i!=itemlist.end();++i){
237 if (SP_IS_RECT(*i)) {
238 if (adj->get_value() != 0) {
239 (SP_RECT(*i)->*setter)(Quantity::convert(adj->get_value(), unit, "px"));
240 } else {
241 (*i)->removeAttribute(value_name);
242 }
243 modmade = true;
244 }
245 }
246
247 sensitivize();
248
249 if (modmade) {
250 DocumentUndo::done(_desktop->getDocument(), SP_VERB_CONTEXT_RECT,
251 _("Change rectangle"));
252 }
253
254 _freeze = false;
255 }
256
257 void
sensitivize()258 RectToolbar::sensitivize()
259 {
260 if (_rx_adj->get_value() == 0 && _ry_adj->get_value() == 0 && _single) { // only for a single selected rect (for now)
261 _not_rounded->set_sensitive(false);
262 } else {
263 _not_rounded->set_sensitive(true);
264 }
265 }
266
267 void
defaults()268 RectToolbar::defaults()
269 {
270 _rx_adj->set_value(0.0);
271 _ry_adj->set_value(0.0);
272
273 sensitivize();
274 }
275
276 void
watch_ec(SPDesktop * desktop,Inkscape::UI::Tools::ToolBase * ec)277 RectToolbar::watch_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolBase* ec)
278 {
279 static sigc::connection changed;
280
281 // use of dynamic_cast<> seems wrong here -- we just need to check the current tool
282
283 if (dynamic_cast<Inkscape::UI::Tools::RectTool *>(ec)) {
284 Inkscape::Selection *sel = desktop->getSelection();
285
286 changed = sel->connectChanged(sigc::mem_fun(*this, &RectToolbar::selection_changed));
287
288 // Synthesize an emission to trigger the update
289 selection_changed(sel);
290 } else {
291 if (changed) {
292 changed.disconnect();
293
294 if (_repr) { // remove old listener
295 _repr->removeListenerByData(this);
296 Inkscape::GC::release(_repr);
297 _repr = nullptr;
298 }
299 }
300 }
301 }
302
303 /**
304 * \param selection should not be NULL.
305 */
306 void
selection_changed(Inkscape::Selection * selection)307 RectToolbar::selection_changed(Inkscape::Selection *selection)
308 {
309 int n_selected = 0;
310 Inkscape::XML::Node *repr = nullptr;
311 SPItem *item = nullptr;
312
313 if (_repr) { // remove old listener
314 _item = nullptr;
315 _repr->removeListenerByData(this);
316 Inkscape::GC::release(_repr);
317 _repr = nullptr;
318 }
319
320 auto itemlist= selection->items();
321 for(auto i=itemlist.begin();i!=itemlist.end();++i){
322 if (SP_IS_RECT(*i)) {
323 n_selected++;
324 item = *i;
325 repr = item->getRepr();
326 }
327 }
328
329 _single = false;
330
331 if (n_selected == 0) {
332 _mode_item->set_markup(_("<b>New:</b>"));
333 _width_item->set_sensitive(false);
334 _height_item->set_sensitive(false);
335 } else if (n_selected == 1) {
336 _mode_item->set_markup(_("<b>Change:</b>"));
337 _single = true;
338 _width_item->set_sensitive(true);
339 _height_item->set_sensitive(true);
340
341 if (repr) {
342 _repr = repr;
343 _item = item;
344 Inkscape::GC::anchor(_repr);
345 _repr->addListener(&rect_tb_repr_events, this);
346 _repr->synthesizeEvents(&rect_tb_repr_events, this);
347 }
348 } else {
349 // FIXME: implement averaging of all parameters for multiple selected
350 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
351 _mode_item->set_markup(_("<b>Change:</b>"));
352 sensitivize();
353 }
354 }
355
event_attr_changed(Inkscape::XML::Node *,gchar const *,gchar const *,gchar const *,bool,gpointer data)356 void RectToolbar::event_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*name*/,
357 gchar const * /*old_value*/, gchar const * /*new_value*/,
358 bool /*is_interactive*/, gpointer data)
359 {
360 auto toolbar = reinterpret_cast<RectToolbar*>(data);
361
362 // quit if run by the _changed callbacks
363 if (toolbar->_freeze) {
364 return;
365 }
366
367 // in turn, prevent callbacks from responding
368 toolbar->_freeze = true;
369
370 Unit const *unit = toolbar->_tracker->getActiveUnit();
371 g_return_if_fail(unit != nullptr);
372
373 if (toolbar->_item && SP_IS_RECT(toolbar->_item)) {
374 {
375 gdouble rx = SP_RECT(toolbar->_item)->getVisibleRx();
376 toolbar->_rx_adj->set_value(Quantity::convert(rx, "px", unit));
377 }
378
379 {
380 gdouble ry = SP_RECT(toolbar->_item)->getVisibleRy();
381 toolbar->_ry_adj->set_value(Quantity::convert(ry, "px", unit));
382 }
383
384 {
385 gdouble width = SP_RECT(toolbar->_item)->getVisibleWidth();
386 toolbar->_width_adj->set_value(Quantity::convert(width, "px", unit));
387 }
388
389 {
390 gdouble height = SP_RECT(toolbar->_item)->getVisibleHeight();
391 toolbar->_height_adj->set_value(Quantity::convert(height, "px", unit));
392 }
393 }
394
395 toolbar->sensitivize();
396 toolbar->_freeze = false;
397 }
398
399 }
400 }
401 }
402
403
404 /*
405 Local Variables:
406 mode:c++
407 c-file-style:"stroustrup"
408 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
409 indent-tabs-mode:nil
410 fill-column:99
411 End:
412 */
413 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :
414