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