1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3 * @file
4 * Spiral 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 "spiral-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-spiral.h"
41
42 #include "ui/icon-names.h"
43 #include "ui/uxmanager.h"
44 #include "ui/widget/canvas.h"
45 #include "ui/widget/label-tool-item.h"
46 #include "ui/widget/spin-button-tool-item.h"
47
48 #include "xml/node-event-vector.h"
49
50 using Inkscape::UI::UXManager;
51 using Inkscape::DocumentUndo;
52
53 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
54 nullptr, /* child_added */
55 nullptr, /* child_removed */
56 Inkscape::UI::Toolbar::SpiralToolbar::event_attr_changed,
57 nullptr, /* content_changed */
58 nullptr /* order_changed */
59 };
60
61 namespace Inkscape {
62 namespace UI {
63 namespace Toolbar {
SpiralToolbar(SPDesktop * desktop)64 SpiralToolbar::SpiralToolbar(SPDesktop *desktop) :
65 Toolbar(desktop),
66 _freeze(false),
67 _repr(nullptr)
68 {
69 auto prefs = Inkscape::Preferences::get();
70
71 {
72 _mode_item = Gtk::manage(new UI::Widget::LabelToolItem(_("<b>New:</b>")));
73 _mode_item->set_use_markup(true);
74 add(*_mode_item);
75 }
76
77 /* Revolution */
78 {
79 std::vector<Glib::ustring> labels = {_("just a curve"), "", _("one full revolution"), "", "", "", "", "", "", ""};
80 std::vector<double> values = { 0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
81 auto revolution_val = prefs->getDouble("/tools/shapes/spiral/revolution", 3.0);
82 _revolution_adj = Gtk::Adjustment::create(revolution_val, 0.01, 1024.0, 0.1, 1.0);
83 _revolution_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("spiral-revolutions", _("Turns:"), _revolution_adj, 1, 2));
84 _revolution_item->set_tooltip_text(_("Number of revolutions"));
85 _revolution_item->set_custom_numeric_menu_data(values, labels);
86 _revolution_item->set_focus_widget(desktop->getCanvas());
87 _revolution_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &SpiralToolbar::value_changed),
88 _revolution_adj, "revolution"));
89 add(*_revolution_item);
90 }
91
92 /* Expansion */
93 {
94 std::vector<Glib::ustring> labels = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), ""};
95 std::vector<double> values = { 0, 0.1, 0.5, 1, 1.5, 5, 20};
96 auto expansion_val = prefs->getDouble("/tools/shapes/spiral/expansion", 1.0);
97 _expansion_adj = Gtk::Adjustment::create(expansion_val, 0.0, 1000.0, 0.01, 1.0);
98
99 _expansion_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("spiral-expansion", _("Divergence:"), _expansion_adj));
100 _expansion_item->set_tooltip_text(_("How much denser/sparser are outer revolutions; 1 = uniform"));
101 _expansion_item->set_custom_numeric_menu_data(values, labels);
102 _expansion_item->set_focus_widget(desktop->getCanvas());
103 _expansion_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &SpiralToolbar::value_changed),
104 _expansion_adj, "expansion"));
105 add(*_expansion_item);
106 }
107
108 /* T0 */
109 {
110 std::vector<Glib::ustring> labels = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
111 std::vector<double> values = { 0, 0.5, 0.9};
112 auto t0_val = prefs->getDouble("/tools/shapes/spiral/t0", 0.0);
113 _t0_adj = Gtk::Adjustment::create(t0_val, 0.0, 0.999, 0.01, 1.0);
114 _t0_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("spiral-t0", _("Inner radius:"), _t0_adj));
115 _t0_item->set_tooltip_text(_("Radius of the innermost revolution (relative to the spiral size)"));
116 _t0_item->set_custom_numeric_menu_data(values, labels);
117 _t0_item->set_focus_widget(desktop->getCanvas());
118 _t0_adj->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &SpiralToolbar::value_changed),
119 _t0_adj, "t0"));
120 add(*_t0_item);
121 }
122
123 add(*Gtk::manage(new Gtk::SeparatorToolItem()));
124
125 /* Reset */
126 {
127 _reset_item = Gtk::manage(new Gtk::ToolButton(_("Defaults")));
128 _reset_item->set_icon_name(INKSCAPE_ICON("edit-clear"));
129 _reset_item->set_tooltip_text(_("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"));
130 _reset_item->signal_clicked().connect(sigc::mem_fun(*this, &SpiralToolbar::defaults));
131 add(*_reset_item);
132 }
133
134 _connection.reset(new sigc::connection(
135 desktop->getSelection()->connectChanged(sigc::mem_fun(*this, &SpiralToolbar::selection_changed))));
136
137 show_all();
138 }
139
~SpiralToolbar()140 SpiralToolbar::~SpiralToolbar()
141 {
142 if(_repr) {
143 _repr->removeListenerByData(this);
144 GC::release(_repr);
145 _repr = nullptr;
146 }
147
148 if(_connection) {
149 _connection->disconnect();
150 }
151 }
152
153 GtkWidget *
create(SPDesktop * desktop)154 SpiralToolbar::create(SPDesktop *desktop)
155 {
156 auto toolbar = new SpiralToolbar(desktop);
157 return GTK_WIDGET(toolbar->gobj());
158 }
159
160 void
value_changed(Glib::RefPtr<Gtk::Adjustment> & adj,Glib::ustring const & value_name)161 SpiralToolbar::value_changed(Glib::RefPtr<Gtk::Adjustment> &adj,
162 Glib::ustring const &value_name)
163 {
164 if (DocumentUndo::getUndoSensitive(_desktop->getDocument())) {
165 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
166 prefs->setDouble("/tools/shapes/spiral/" + value_name,
167 adj->get_value());
168 }
169
170 // quit if run by the attr_changed listener
171 if (_freeze) {
172 return;
173 }
174
175 // in turn, prevent listener from responding
176 _freeze = true;
177
178 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
179
180 bool modmade = false;
181 auto itemlist= _desktop->getSelection()->items();
182 for(auto i=itemlist.begin();i!=itemlist.end(); ++i){
183 SPItem *item = *i;
184 if (SP_IS_SPIRAL(item)) {
185 Inkscape::XML::Node *repr = item->getRepr();
186 sp_repr_set_svg_double( repr, namespaced_name,
187 adj->get_value() );
188 item->updateRepr();
189 modmade = true;
190 }
191 }
192
193 g_free(namespaced_name);
194
195 if (modmade) {
196 DocumentUndo::done(_desktop->getDocument(), SP_VERB_CONTEXT_SPIRAL,
197 _("Change spiral"));
198 }
199
200 _freeze = false;
201 }
202
203 void
defaults()204 SpiralToolbar::defaults()
205 {
206 // fixme: make settable
207 gdouble rev = 3;
208 gdouble exp = 1.0;
209 gdouble t0 = 0.0;
210
211 _revolution_adj->set_value(rev);
212 _expansion_adj->set_value(exp);
213 _t0_adj->set_value(t0);
214
215 if(_desktop->getCanvas()) _desktop->getCanvas()->grab_focus();
216 }
217
218 void
selection_changed(Inkscape::Selection * selection)219 SpiralToolbar::selection_changed(Inkscape::Selection *selection)
220 {
221 int n_selected = 0;
222 Inkscape::XML::Node *repr = nullptr;
223
224 if ( _repr ) {
225 _repr->removeListenerByData(this);
226 GC::release(_repr);
227 _repr = nullptr;
228 }
229
230 auto itemlist= selection->items();
231 for(auto i=itemlist.begin();i!=itemlist.end(); ++i){
232 SPItem *item = *i;
233 if (SP_IS_SPIRAL(item)) {
234 n_selected++;
235 repr = item->getRepr();
236 }
237 }
238
239 if (n_selected == 0) {
240 _mode_item->set_markup(_("<b>New:</b>"));
241 } else if (n_selected == 1) {
242 _mode_item->set_markup(_("<b>Change:</b>"));
243
244 if (repr) {
245 _repr = repr;
246 Inkscape::GC::anchor(_repr);
247 _repr->addListener(&spiral_tb_repr_events, this);
248 _repr->synthesizeEvents(&spiral_tb_repr_events, this);
249 }
250 } else {
251 // FIXME: implement averaging of all parameters for multiple selected
252 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
253 _mode_item->set_markup(_("<b>Change:</b>"));
254 }
255 }
256
257 void
event_attr_changed(Inkscape::XML::Node * repr,gchar const *,gchar const *,gchar const *,bool,gpointer data)258 SpiralToolbar::event_attr_changed(Inkscape::XML::Node *repr,
259 gchar const * /*name*/,
260 gchar const * /*old_value*/,
261 gchar const * /*new_value*/,
262 bool /*is_interactive*/,
263 gpointer data)
264 {
265 auto toolbar = reinterpret_cast<SpiralToolbar *>(data);
266
267 // quit if run by the _changed callbacks
268 if (toolbar->_freeze) {
269 return;
270 }
271
272 // in turn, prevent callbacks from responding
273 toolbar->_freeze = true;
274
275 double revolution = 3.0;
276 sp_repr_get_double(repr, "sodipodi:revolution", &revolution);
277 toolbar->_revolution_adj->set_value(revolution);
278
279 double expansion = 1.0;
280 sp_repr_get_double(repr, "sodipodi:expansion", &expansion);
281 toolbar->_expansion_adj->set_value(expansion);
282
283 double t0 = 0.0;
284 sp_repr_get_double(repr, "sodipodi:t0", &t0);
285 toolbar->_t0_adj->set_value(t0);
286
287 toolbar->_freeze = false;
288 }
289
290 }
291 }
292 }
293
294 /*
295 Local Variables:
296 mode:c++
297 c-file-style:"stroustrup"
298 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
299 indent-tabs-mode:nil
300 fill-column:99
301 End:
302 */
303 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
304