1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3  * @file
4  * Calligraphy 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 "calligraphy-toolbar.h"
29 
30 #include <glibmm/i18n.h>
31 #include <gtkmm/comboboxtext.h>
32 #include <gtkmm/separatortoolitem.h>
33 
34 #include "desktop.h"
35 #include "document-undo.h"
36 #include "ui/dialog/calligraphic-profile-rename.h"
37 #include "ui/icon-names.h"
38 #include "ui/simple-pref-pusher.h"
39 #include "ui/uxmanager.h"
40 #include "ui/widget/canvas.h"
41 #include "ui/widget/combo-tool-item.h"
42 #include "ui/widget/spin-button-tool-item.h"
43 #include "ui/widget/unit-tracker.h"
44 
45 using Inkscape::DocumentUndo;
46 using Inkscape::UI::Widget::UnitTracker;
47 using Inkscape::Util::Quantity;
48 using Inkscape::Util::Unit;
49 using Inkscape::Util::unit_table;
50 
get_presets_list()51 std::vector<Glib::ustring> get_presets_list() {
52 
53     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
54 
55     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
56 
57     return presets;
58 }
59 
60 namespace Inkscape {
61 namespace UI {
62 namespace Toolbar {
63 
CalligraphyToolbar(SPDesktop * desktop)64 CalligraphyToolbar::CalligraphyToolbar(SPDesktop *desktop)
65     : Toolbar(desktop)
66     , _tracker(new UnitTracker(Inkscape::Util::UNIT_TYPE_LINEAR))
67     , _presets_blocked(false)
68 {
69     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
70     _tracker->prependUnit(unit_table.getUnit("px"));
71     _tracker->changeLabel("%", 0, true);
72     prefs->getBool("/tools/calligraphic/abs_width")
73         ? _tracker->setActiveUnitByLabel(prefs->getString("/tools/calligraphic/unit"))
74         : _tracker->setActiveUnitByLabel("%");
75 
76     /*calligraphic profile */
77     {
78         _profile_selector_combo = Gtk::manage(new Gtk::ComboBoxText());
79         _profile_selector_combo->set_tooltip_text(_("Choose a preset"));
80 
81         build_presets_list();
82 
83         auto profile_selector_ti = Gtk::manage(new Gtk::ToolItem());
84         profile_selector_ti->add(*_profile_selector_combo);
85         add(*profile_selector_ti);
86 
87         _profile_selector_combo->signal_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::change_profile));
88     }
89 
90     /*calligraphic profile editor */
91     {
92         auto profile_edit_item = Gtk::manage(new Gtk::ToolButton(_("Add/Edit Profile")));
93         profile_edit_item->set_tooltip_text(_("Add or edit calligraphic profile"));
94         profile_edit_item->set_icon_name(INKSCAPE_ICON("document-properties"));
95         profile_edit_item->signal_clicked().connect(sigc::mem_fun(*this, &CalligraphyToolbar::edit_profile));
96         add(*profile_edit_item);
97     }
98 
99     add(* Gtk::manage(new Gtk::SeparatorToolItem()));
100 
101     {
102         /* Width */
103         std::vector<Glib::ustring> labels = {_("(hairline)"), "", "", "", _("(default)"), "", "", "", "", _("(broad stroke)")};
104         std::vector<double>        values = {              1,  3,  5, 10,             15, 20, 30, 50, 75,                 100};
105         auto width_val = prefs->getDouble("/tools/calligraphic/width", 15.118);
106         Unit const *unit = unit_table.getUnit(prefs->getString("/tools/calligraphic/unit"));
107         _width_adj = Gtk::Adjustment::create(Quantity::convert(width_val, "px", unit), 0.001, 100, 1.0, 10.0);
108         auto width_item =
109             Gtk::manage(new UI::Widget::SpinButtonToolItem("calligraphy-width", _("Width:"), _width_adj, 0.001, 3));
110         width_item->set_tooltip_text(_("The width of the calligraphic pen (relative to the visible canvas area)"));
111         width_item->set_custom_numeric_menu_data(values, labels);
112         width_item->set_focus_widget(desktop->canvas);
113         _width_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::width_value_changed));
114         _widget_map["width"] = G_OBJECT(_width_adj->gobj());
115         // ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
116         add(*width_item);
117         _tracker->addAdjustment(_width_adj->gobj());
118         width_item->set_sensitive(true);
119     }
120 
121     /* Unit Menu */
122     {
123         auto unit_menu_ti = _tracker->create_tool_item(_("Units"), "");
124         add(*unit_menu_ti);
125         unit_menu_ti->signal_changed_after().connect(sigc::mem_fun(*this, &CalligraphyToolbar::unit_changed));
126     }
127 
128     /* Use Pressure button */
129     {
130         _usepressure = add_toggle_button(_("Pressure"),
131                                          _("Use the pressure of the input device to alter the width of the pen"));
132         _usepressure->set_icon_name(INKSCAPE_ICON("draw-use-pressure"));
133         _widget_map["usepressure"] = G_OBJECT(_usepressure->gobj());
134         _usepressure_pusher.reset(new SimplePrefPusher(_usepressure, "/tools/calligraphic/usepressure"));
135         _usepressure->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CalligraphyToolbar::on_pref_toggled),
136                                                           _usepressure,
137                                                           "/tools/calligraphic/usepressure"));
138     }
139 
140     /* Trace Background button */
141     {
142         _tracebackground = add_toggle_button(_("Trace Background"),
143                                             _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"));
144         _tracebackground->set_icon_name(INKSCAPE_ICON("draw-trace-background"));
145         _widget_map["tracebackground"] = G_OBJECT(_tracebackground->gobj());
146         _tracebackground_pusher.reset(new SimplePrefPusher(_tracebackground, "/tools/calligraphic/tracebackground"));
147         _tracebackground->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CalligraphyToolbar::on_pref_toggled),
148                                                               _tracebackground,
149                                                               "/tools/calligraphic/tracebackground"));
150     }
151 
152     {
153         /* Thinning */
154         std::vector<Glib::ustring> labels = {_("(speed blows up stroke)"),  "",  "", _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), "", "", _("(speed deflates stroke)")};
155         std::vector<double>        values = {                        -100, -40, -20,                    -10,                     0,                              10, 20, 40,                          100};
156         auto thinning_val = prefs->getDouble("/tools/calligraphic/thinning", 10);
157         _thinning_adj = Gtk::Adjustment::create(thinning_val, -100, 100, 1, 10.0);
158         auto thinning_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("calligraphy-thinning", _("Thinning:"), _thinning_adj, 1, 0));
159         thinning_item->set_tooltip_text(("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"));
160         thinning_item->set_custom_numeric_menu_data(values, labels);
161         thinning_item->set_focus_widget(desktop->canvas);
162         _thinning_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::velthin_value_changed));
163         _widget_map["thinning"] = G_OBJECT(_thinning_adj->gobj());
164         add(*thinning_item);
165         thinning_item->set_sensitive(true);
166     }
167 
168     {
169         /* Mass */
170         std::vector<Glib::ustring> labels = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), "", "", _("(maximum inertia)")};
171         std::vector<double>        values = {              0.0,                                2,                        10, 20, 50,                    100};
172         auto mass_val = prefs->getDouble("/tools/calligraphic/mass", 2.0);
173         _mass_adj = Gtk::Adjustment::create(mass_val, 0.0, 100, 1, 10.0);
174         auto mass_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("calligraphy-mass", _("Mass:"), _mass_adj, 1, 0));
175         mass_item->set_tooltip_text(_("Increase to make the pen drag behind, as if slowed by inertia"));
176         mass_item->set_custom_numeric_menu_data(values, labels);
177         mass_item->set_focus_widget(desktop->canvas);
178         _mass_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::mass_value_changed));
179         _widget_map["mass"] = G_OBJECT(_mass_adj->gobj());
180         // ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
181         add(*mass_item);
182         mass_item->set_sensitive(true);
183     }
184 
185     add(* Gtk::manage(new Gtk::SeparatorToolItem()));
186 
187     {
188         /* Angle */
189         std::vector<Glib::ustring> labels = {_("(left edge up)"),  "",  "", _("(horizontal)"), _("(default)"), "", _("(right edge up)")};
190         std::vector<double>        values = {                -90, -60, -30,                 0,             30, 60,                   90};
191         auto angle_val = prefs->getDouble("/tools/calligraphic/angle", 30);
192         _angle_adj = Gtk::Adjustment::create(angle_val, -90.0, 90.0, 1.0, 10.0);
193         _angle_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("calligraphy-angle", _("Angle:"), _angle_adj, 1, 0));
194         _angle_item->set_tooltip_text(_("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"));
195         _angle_item->set_custom_numeric_menu_data(values, labels);
196         _angle_item->set_focus_widget(desktop->canvas);
197         _angle_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::angle_value_changed));
198         _widget_map["angle"] = G_OBJECT(_angle_adj->gobj());
199         add(*_angle_item);
200         _angle_item->set_sensitive(true);
201     }
202 
203     /* Use Tilt button */
204     {
205         _usetilt = add_toggle_button(_("Tilt"),
206                                      _("Use the tilt of the input device to alter the angle of the pen's nib"));
207         _usetilt->set_icon_name(INKSCAPE_ICON("draw-use-tilt"));
208         _widget_map["usetilt"] = G_OBJECT(_usetilt->gobj());
209         _usetilt_pusher.reset(new SimplePrefPusher(_usetilt, "/tools/calligraphic/usetilt"));
210         _usetilt->signal_toggled().connect(sigc::mem_fun(*this, &CalligraphyToolbar::tilt_state_changed));
211         _angle_item->set_sensitive(!prefs->getBool("/tools/calligraphic/usetilt", true));
212         _usetilt->set_active(prefs->getBool("/tools/calligraphic/usetilt", true));
213     }
214 
215     {
216         /* Fixation */
217         std::vector<Glib::ustring> labels = {_("(perpendicular to stroke, \"brush\")"), "", "", "", _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
218         std::vector<double>        values = {                                        0, 20, 40, 60,                           90,                            100};
219         auto flatness_val = prefs->getDouble("/tools/calligraphic/flatness", 90);
220         _fixation_adj = Gtk::Adjustment::create(flatness_val, 0.0, 100, 1.0, 10.0);
221         auto flatness_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("calligraphy-fixation", _("Fixation:"), _fixation_adj, 1, 0));
222         flatness_item->set_tooltip_text(_("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"));
223         flatness_item->set_custom_numeric_menu_data(values, labels);
224         flatness_item->set_focus_widget(desktop->canvas);
225         _fixation_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::flatness_value_changed));
226         _widget_map["flatness"] = G_OBJECT(_fixation_adj->gobj());
227         add(*flatness_item);
228         flatness_item->set_sensitive(true);
229     }
230 
231     add(* Gtk::manage(new Gtk::SeparatorToolItem()));
232 
233     {
234         /* Cap Rounding */
235         std::vector<Glib::ustring> labels = {_("(blunt caps, default)"), _("(slightly bulging)"),  "",  "", _("(approximately round)"), _("(long protruding caps)")};
236         std::vector<double>        values = {                         0,                     0.3, 0.5, 1.0,                        1.4,                         5.0};
237         auto cap_rounding_val = prefs->getDouble("/tools/calligraphic/cap_rounding", 0.0);
238         _cap_rounding_adj = Gtk::Adjustment::create(cap_rounding_val, 0.0, 5.0, 0.01, 0.1);
239         auto cap_rounding_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("calligraphy-cap-rounding", _("Caps:"), _cap_rounding_adj, 0.01, 2));
240 
241         // TRANSLATORS: "cap" means "end" (both start and finish) here
242         cap_rounding_item->set_tooltip_text(_("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"));
243         cap_rounding_item->set_custom_numeric_menu_data(values, labels);
244         cap_rounding_item->set_focus_widget(desktop->canvas);
245         _cap_rounding_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::cap_rounding_value_changed));
246         _widget_map["cap_rounding"] = G_OBJECT(_cap_rounding_adj->gobj());
247         add(*cap_rounding_item);
248         cap_rounding_item->set_sensitive(true);
249     }
250 
251     add(* Gtk::manage(new Gtk::SeparatorToolItem()));
252 
253     {
254         /* Tremor */
255         std::vector<Glib::ustring> labels = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), "", "", _("(maximum tremor)")};
256         std::vector<double>        values = {                 0,                   10,                       20, 40, 60,                   100};
257         auto tremor_val = prefs->getDouble("/tools/calligraphic/tremor", 0.0);
258         _tremor_adj = Gtk::Adjustment::create(tremor_val, 0.0, 100, 1, 10.0);
259         auto tremor_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("calligraphy-tremor", _("Tremor:"), _tremor_adj, 1, 0));
260         tremor_item->set_tooltip_text(_("Increase to make strokes rugged and trembling"));
261         tremor_item->set_custom_numeric_menu_data(values, labels);
262         tremor_item->set_focus_widget(desktop->canvas);
263         _tremor_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::tremor_value_changed));
264         _widget_map["tremor"] = G_OBJECT(_tremor_adj->gobj());
265         // ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
266         add(*tremor_item);
267         tremor_item->set_sensitive(true);
268     }
269 
270     {
271         /* Wiggle */
272         std::vector<Glib::ustring> labels = {_("(no wiggle)"), _("(slight deviation)"), "", "", _("(wild waves and curls)")};
273         std::vector<double>        values = {               0,                      20, 40, 60,                         100};
274         auto wiggle_val = prefs->getDouble("/tools/calligraphic/wiggle", 0.0);
275         _wiggle_adj = Gtk::Adjustment::create(wiggle_val, 0.0, 100, 1, 10.0);
276         auto wiggle_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("calligraphy-wiggle", _("Wiggle:"), _wiggle_adj, 1, 0));
277         wiggle_item->set_tooltip_text(_("Increase to make the pen waver and wiggle"));
278         wiggle_item->set_custom_numeric_menu_data(values, labels);
279         wiggle_item->set_focus_widget(desktop->canvas);
280         _wiggle_adj->signal_value_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::wiggle_value_changed));
281         _widget_map["wiggle"] = G_OBJECT(_wiggle_adj->gobj());
282         // ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
283         add(*wiggle_item);
284         wiggle_item->set_sensitive(true);
285     }
286 
287     show_all();
288 }
289 
290 GtkWidget *
create(SPDesktop * desktop)291 CalligraphyToolbar::create(SPDesktop *desktop)
292 {
293     auto toolbar = new CalligraphyToolbar(desktop);
294     return GTK_WIDGET(toolbar->gobj());
295 }
296 
297 void
width_value_changed()298 CalligraphyToolbar::width_value_changed()
299 {
300     Unit const *unit = _tracker->getActiveUnit();
301     g_return_if_fail(unit != nullptr);
302     auto prefs = Inkscape::Preferences::get();
303     _tracker->getCurrentLabel() == "%" ? prefs->setBool("/tools/calligraphic/abs_width", false)
304                                          : prefs->setBool("/tools/calligraphic/abs_width", true);
305     prefs->setDouble("/tools/calligraphic/width", Quantity::convert(_width_adj->get_value(), unit, "px"));
306     update_presets_list();
307 }
308 
309 void
velthin_value_changed()310 CalligraphyToolbar::velthin_value_changed()
311 {
312     auto prefs = Inkscape::Preferences::get();
313     prefs->setDouble("/tools/calligraphic/thinning", _thinning_adj->get_value() );
314     update_presets_list();
315 }
316 
317 void
angle_value_changed()318 CalligraphyToolbar::angle_value_changed()
319 {
320     auto prefs = Inkscape::Preferences::get();
321     prefs->setDouble( "/tools/calligraphic/angle", _angle_adj->get_value() );
322     update_presets_list();
323 }
324 
325 void
flatness_value_changed()326 CalligraphyToolbar::flatness_value_changed()
327 {
328     auto prefs = Inkscape::Preferences::get();
329     prefs->setDouble( "/tools/calligraphic/flatness", _fixation_adj->get_value() );
330     update_presets_list();
331 }
332 
333 void
cap_rounding_value_changed()334 CalligraphyToolbar::cap_rounding_value_changed()
335 {
336     auto prefs = Inkscape::Preferences::get();
337     prefs->setDouble( "/tools/calligraphic/cap_rounding", _cap_rounding_adj->get_value() );
338     update_presets_list();
339 }
340 
341 void
tremor_value_changed()342 CalligraphyToolbar::tremor_value_changed()
343 {
344     auto prefs = Inkscape::Preferences::get();
345     prefs->setDouble( "/tools/calligraphic/tremor", _tremor_adj->get_value() );
346     update_presets_list();
347 }
348 
349 void
wiggle_value_changed()350 CalligraphyToolbar::wiggle_value_changed()
351 {
352     auto prefs = Inkscape::Preferences::get();
353     prefs->setDouble( "/tools/calligraphic/wiggle", _wiggle_adj->get_value() );
354     update_presets_list();
355 }
356 
357 void
mass_value_changed()358 CalligraphyToolbar::mass_value_changed()
359 {
360     auto prefs = Inkscape::Preferences::get();
361     prefs->setDouble( "/tools/calligraphic/mass", _mass_adj->get_value() );
362     update_presets_list();
363 }
364 
365 void
on_pref_toggled(Gtk::ToggleToolButton * item,const Glib::ustring & path)366 CalligraphyToolbar::on_pref_toggled(Gtk::ToggleToolButton *item,
367                                     const Glib::ustring&   path)
368 {
369     auto prefs = Inkscape::Preferences::get();
370     prefs->setBool(path, item->get_active());
371     update_presets_list();
372 }
373 
374 void
update_presets_list()375 CalligraphyToolbar::update_presets_list()
376 {
377     if (_presets_blocked) {
378         return;
379     }
380 
381     auto prefs = Inkscape::Preferences::get();
382     auto presets = get_presets_list();
383 
384     int index = 1;  // 0 is for no preset.
385     for (auto i = presets.begin(); i != presets.end(); ++i, ++index) {
386         bool match = true;
387 
388         auto preset = prefs->getAllEntries(*i);
389         for (auto & j : preset) {
390             Glib::ustring entry_name = j.getEntryName();
391             if (entry_name == "id" || entry_name == "name") {
392                 continue;
393             }
394 
395             void *widget = _widget_map[entry_name.data()];
396             if (widget) {
397                 if (GTK_IS_ADJUSTMENT(widget)) {
398                     double v = j.getDouble();
399                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
400                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
401                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
402                         match = false;
403                         break;
404                     }
405                 } else if (GTK_IS_TOGGLE_TOOL_BUTTON(widget)) {
406                     bool v = j.getBool();
407                     auto toggle = GTK_TOGGLE_TOOL_BUTTON(widget);
408                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
409                     if ( static_cast<bool>(gtk_toggle_tool_button_get_active(toggle)) != v ) {
410                         match = false;
411                         break;
412                     }
413                 }
414             }
415         }
416 
417         if (match) {
418             // newly added item is at the same index as the
419             // save command, so we need to change twice for it to take effect
420             _profile_selector_combo->set_active(0);
421             _profile_selector_combo->set_active(index);
422             return;
423         }
424     }
425 
426     // no match found
427     _profile_selector_combo->set_active(0);
428 }
429 
430 void
tilt_state_changed()431 CalligraphyToolbar::tilt_state_changed()
432 {
433     _angle_item->set_sensitive(!_usetilt->get_active());
434     on_pref_toggled(_usetilt, "/tools/calligraphic/usetilt");
435 }
436 
437 void
build_presets_list()438 CalligraphyToolbar::build_presets_list()
439 {
440     _presets_blocked = true;
441 
442     _profile_selector_combo->remove_all();
443     _profile_selector_combo->append(_("No preset"));
444 
445     // iterate over all presets to populate the list
446     auto prefs = Inkscape::Preferences::get();
447     auto presets = get_presets_list();
448 
449     for (auto & preset : presets) {
450         Glib::ustring preset_name = prefs->getString(preset + "/name");
451 
452         if (!preset_name.empty()) {
453             _profile_selector_combo->append(_(preset_name.data()));
454         }
455     }
456 
457     _presets_blocked = false;
458 
459     update_presets_list();
460 }
461 
462 void
change_profile()463 CalligraphyToolbar::change_profile()
464 {
465     auto mode = _profile_selector_combo->get_active_row_number();
466     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
467 
468     if (_presets_blocked) {
469         return;
470     }
471 
472     // mode is one-based so we subtract 1
473     std::vector<Glib::ustring> presets = get_presets_list();
474 
475     Glib::ustring preset_path = "";
476     if (mode - 1 < presets.size()) {
477         preset_path = presets.at(mode - 1);
478     }
479 
480     if (!preset_path.empty()) {
481         _presets_blocked = true; //temporarily block the selector so no one will updadte it while we're reading it
482 
483         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
484 
485         // Shouldn't this be std::map?
486         for (auto & i : preset) {
487             Glib::ustring entry_name = i.getEntryName();
488             if (entry_name == "id" || entry_name == "name") {
489                 continue;
490             }
491             void *widget = _widget_map[entry_name.data()];
492             if (widget) {
493                 if (GTK_IS_ADJUSTMENT(widget)) {
494                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
495                     gtk_adjustment_set_value(adj, i.getDouble());
496                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
497                 } else if (GTK_IS_TOGGLE_TOOL_BUTTON(widget)) {
498                     auto toggle = GTK_TOGGLE_TOOL_BUTTON(widget);
499                     gtk_toggle_tool_button_set_active(toggle, i.getBool());
500                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
501                 } else {
502                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
503                 }
504             } else {
505                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
506             }
507         }
508         _presets_blocked = false;
509     }
510 }
511 
512 void
edit_profile()513 CalligraphyToolbar::edit_profile()
514 {
515     save_profile(nullptr);
516 }
517 
unit_changed(int)518 void CalligraphyToolbar::unit_changed(int /* NotUsed */)
519 {
520     Unit const *unit = _tracker->getActiveUnit();
521     g_return_if_fail(unit != nullptr);
522     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
523     _tracker->getCurrentLabel() == "%" ? prefs->setBool("/tools/calligraphic/abs_width", false)
524                                         : prefs->setBool("/tools/calligraphic/abs_width", true);
525     prefs->setDouble("/tools/calligraphic/width",
526                      CLAMP(prefs->getDouble("/tools/calligraphic/width"), Quantity::convert(0.001, unit, "px"),
527                            Quantity::convert(100, unit, "px")));
528     prefs->setString("/tools/calligraphic/unit", unit->abbr);
529 }
530 
save_profile(GtkWidget *)531 void CalligraphyToolbar::save_profile(GtkWidget * /*widget*/)
532 {
533     using Inkscape::UI::Dialog::CalligraphicProfileRename;
534     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
535     if (! _desktop) {
536         return;
537     }
538 
539     if (_presets_blocked) {
540         return;
541     }
542 
543     Glib::ustring current_profile_name = _profile_selector_combo->get_active_text();
544 
545     if (current_profile_name == _("No preset")) {
546         current_profile_name = "";
547     }
548 
549     CalligraphicProfileRename::show(_desktop, current_profile_name);
550     if ( !CalligraphicProfileRename::applied()) {
551         // dialog cancelled
552         update_presets_list();
553         return;
554     }
555     Glib::ustring new_profile_name = CalligraphicProfileRename::getProfileName();
556 
557     if (new_profile_name.empty()) {
558         // empty name entered
559         update_presets_list ();
560         return;
561     }
562 
563     _presets_blocked = true;
564 
565     // If there's a preset with the given name, find it and set save_path appropriately
566     auto presets = get_presets_list();
567     int total_presets = presets.size();
568     int new_index = -1;
569     Glib::ustring save_path; // profile pref path without a trailing slash
570 
571     int temp_index = 0;
572     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
573         Glib::ustring name = prefs->getString(*i + "/name");
574         if (!name.empty() && (new_profile_name == name || current_profile_name == name)) {
575             new_index = temp_index;
576             save_path = *i;
577             break;
578         }
579     }
580 
581     if ( CalligraphicProfileRename::deleted() && new_index != -1) {
582         prefs->remove(save_path);
583         _presets_blocked = false;
584         build_presets_list();
585         return;
586     }
587 
588     if (new_index == -1) {
589         // no preset with this name, create
590         new_index = total_presets + 1;
591         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
592         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
593         g_free(profile_id);
594     }
595 
596     for (auto map_item : _widget_map) {
597         auto widget_name = map_item.first;
598         auto widget      = map_item.second;
599 
600         if (widget) {
601             if (GTK_IS_ADJUSTMENT(widget)) {
602                 GtkAdjustment* adj = GTK_ADJUSTMENT(widget);
603                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
604                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
605             } else if (GTK_IS_TOGGLE_TOOL_BUTTON(widget)) {
606                 auto toggle = GTK_TOGGLE_TOOL_BUTTON(widget);
607                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_tool_button_get_active(toggle));
608                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
609             } else {
610                 g_warning("Unknown widget type for preset: %s\n", widget_name.c_str());
611             }
612         } else {
613             g_warning("Bad key when writing preset: %s\n", widget_name.c_str());
614         }
615     }
616     prefs->setString(save_path + "/name", new_profile_name);
617 
618     _presets_blocked = true;
619     build_presets_list();
620 }
621 
622 }
623 }
624 }
625 
626 
627 /*
628   Local Variables:
629   mode:c++
630   c-file-style:"stroustrup"
631   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
632   indent-tabs-mode:nil
633   fill-column:99
634   End:
635 */
636 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
637