Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
calligraphy-toolbar.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/* Authors:
6 * MenTaLguY <mental@rydia.net>
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Frank Felfe <innerspace@iname.com>
10 * John Cliff <simarilius@yahoo.com>
11 * David Turner <novalis@gnu.org>
12 * Josh Andler <scislac@scislac.com>
13 * Jon A. Cruz <jon@joncruz.org>
14 * Maximilian Albert <maximilian.albert@gmail.com>
15 * Tavmjong Bah <tavmjong@free.fr>
16 * Abhishek Sharma
17 * Kris De Gussem <Kris.DeGussem@gmail.com>
18 * Vaibhav Malik <vaibhavmalik2018@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/adjustment.h>
32#include <gtkmm/comboboxtext.h>
33#include <gtkmm/togglebutton.h>
34
35#include "desktop.h"
36#include "ui/builder-utils.h"
39#include "ui/util.h"
43
48
49namespace Inkscape::UI::Toolbar {
50
51static std::vector<Glib::ustring> get_presets_list()
52{
53 return Preferences::get()->getAllDirs("/tools/calligraphic/preset");
54}
55
59
60CalligraphyToolbar::CalligraphyToolbar(Glib::RefPtr<Gtk::Builder> const &builder)
61 : Toolbar{get_widget<Gtk::Box>(builder, "calligraphy-toolbar")}
62 , _tracker{std::make_unique<UnitTracker>(Util::UNIT_TYPE_LINEAR)}
63 , _profile_selector_combo{get_widget<Gtk::ComboBoxText>(builder, "_profile_selector_combo")}
64 , _width_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_width_item")}
65 , _thinning_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_thinning_item")}
66 , _mass_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_mass_item")}
67 , _angle_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_angle_item")}
68 , _usetilt_btn{&get_widget<Gtk::ToggleButton>(builder, "_usetilt_btn")}
69 , _flatness_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_flatness_item")}
70 , _cap_rounding_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_cap_rounding_item")}
71 , _tremor_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_tremor_item")}
72 , _wiggle_item{get_derived_widget<UI::Widget::SpinButton>(builder, "_wiggle_item")}
73{
74 auto prefs = Preferences::get();
75
76 _tracker->prependUnit(UnitTable::get().getUnit("px"));
77 _tracker->changeLabel("%", 0, true);
78 if (prefs->getBool("/tools/calligraphic/abs_width")) {
79 _tracker->setActiveUnitByLabel(prefs->getString("/tools/calligraphic/unit"));
80 }
81
82 auto &usepressure_btn = get_widget<Gtk::ToggleButton>(builder, "usepressure_btn");
83 auto &tracebackground_btn = get_widget<Gtk::ToggleButton>(builder, "tracebackground_btn");
84
85 // Setup the spin buttons.
94
96 { 1, _("(hairline)")},
97 { 3, ""},
98 { 5, ""},
99 {10, ""},
100 {15, _("(default)")},
101 {20, ""},
102 {30, ""},
103 {50, ""},
104 {75, ""},
105 {100, _("(broad stroke)")}
106 });
107
109 {-100, _("(speed blows up stroke)")},
110 { -40, ""},
111 { -20, ""},
112 { -10, _("(slight widening)")},
113 { 0, _("(constant width)")},
114 { 10, _("(slight thinning, default)")},
115 { 20, ""},
116 { 40, ""},
117 { 100, _("(speed deflates stroke)")}
118 });
119
121 { 0, _("(no inertia)")},
122 { 2, _("(slight smoothing, default)")},
123 { 10, _("(noticeable lagging)")},
124 { 20, ""},
125 { 50, ""},
126 {100, _("(maximum inertia)")}
127 });
128
130 {-90, _("(left edge up)")},
131 {-60, ""},
132 {-30, ""},
133 { 0, _("(horizontal)")},
134 { 30, _("(default)")},
135 { 60, ""},
136 { 90, _("(right edge up)")}
137 });
138
139 // Fixation
141 { 0, _("(perpendicular to stroke, \"brush\")")},
142 { 20, ""},
143 { 40, ""},
144 { 60, ""},
145 { 90, _("(almost fixed, default)")},
146 {100, _("(fixed by Angle, \"pen\")")}
147 });
148
150 { 0, _("(blunt caps, default)")},
151 {0.3, _("(slightly bulging)")},
152 {0.5, ""},
153 {1.0, ""},
154 {1.4, _("(approximately round)")},
155 {5.0, _("(long protruding caps)")}
156 });
157
159 { 0, _("(smooth line)")},
160 { 10, _("(slight tremor)")},
161 { 20, _("(noticeable tremor)")},
162 { 40, ""},
163 { 60, ""},
164 {100, _("(maximum tremor)")}
165 });
166
168 { 0, _("(no wiggle)")},
169 { 20, _("(slight deviation)")},
170 { 40, ""},
171 { 60, ""},
172 {100, _("(wild waves and curls)")}
173 });
174
175 // Configure the calligraphic profile combo box text.
177 _profile_selector_combo.signal_changed().connect(sigc::mem_fun(*this, &CalligraphyToolbar::change_profile));
178
179 // Unit menu.
180 auto unit_menu = _tracker->create_tool_item(_("Units"), "");
181 get_widget<Gtk::Box>(builder, "unit_menu_box").append(*unit_menu);
182 unit_menu->signal_changed_after().connect(sigc::mem_fun(*this, &CalligraphyToolbar::unit_changed));
183
184 // Use pressure button.
185 _widget_map["usepressure"] = &usepressure_btn;
186 _usepressure_pusher = std::make_unique<SimplePrefPusher>(&usepressure_btn, "/tools/calligraphic/usepressure");
187 usepressure_btn.signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CalligraphyToolbar::on_pref_toggled),
188 &usepressure_btn, "/tools/calligraphic/usepressure"));
189
190 // Tracebackground button.
191 _widget_map["tracebackground"] = &tracebackground_btn;
192 _tracebackground_pusher = std::make_unique<SimplePrefPusher>(&tracebackground_btn, "/tools/calligraphic/tracebackground");
193 tracebackground_btn.signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CalligraphyToolbar::on_pref_toggled),
194 &tracebackground_btn,
195 "/tools/calligraphic/tracebackground"));
196
197 // Use tilt button.
198 _widget_map["usetilt"] = _usetilt_btn;
199 _usetilt_pusher = std::make_unique<SimplePrefPusher>(_usetilt_btn, "/tools/calligraphic/usetilt");
200 _usetilt_btn->signal_toggled().connect(sigc::mem_fun(*this, &CalligraphyToolbar::tilt_state_changed));
201 _angle_item.set_sensitive(!prefs->getBool("/tools/calligraphic/usetilt", true));
202 _usetilt_btn->set_active(prefs->getBool("/tools/calligraphic/usetilt", true));
203
204 // Signals.
205 get_widget<Gtk::Button>(builder, "profile_edit_btn")
206 .signal_clicked()
207 .connect(sigc::mem_fun(*this, &CalligraphyToolbar::edit_profile));
208
210}
211
213
215 double default_value, ValueChangedMemFun value_changed_mem_fun)
216{
217 auto const prefs = Preferences::get();
218 auto const path = "/tools/calligraphic/" + name;
219 auto const val = prefs->getDouble(path, default_value);
220 auto adj = btn.get_adjustment();
221
222 if (name == "width") {
223 auto const unit = UnitTable::get().getUnit(prefs->getString("/tools/calligraphic/unit"));
224 adj = Gtk::Adjustment::create(Quantity::convert(val, "px", unit), 0.001, 100, 1.0, 10.0);
225 btn.set_adjustment(adj);
226 } else {
227 adj->set_value(val);
228 }
229
230 adj->signal_value_changed().connect(sigc::mem_fun(*this, value_changed_mem_fun));
231
232 _widget_map[name] = adj.get();
233 _tracker->addAdjustment(adj->gobj());
234 btn.setDefocusTarget(this);
235}
236
238{
239 auto const unit = _tracker->getActiveUnit();
240 auto prefs = Preferences::get();
241 prefs->setBool("/tools/calligraphic/abs_width", _tracker->getCurrentLabel() != "%");
242 prefs->setDouble("/tools/calligraphic/width",
243 Quantity::convert(_width_item.get_adjustment()->get_value(), unit, "px"));
245}
246
248{
249 Preferences::get()->setDouble("/tools/calligraphic/thinning", _thinning_item.get_adjustment()->get_value());
251}
252
254{
255 Preferences::get()->setDouble("/tools/calligraphic/angle", _angle_item.get_adjustment()->get_value());
257}
258
260{
261 Preferences::get()->setDouble("/tools/calligraphic/flatness", _flatness_item.get_adjustment()->get_value());
263}
264
266{
267 Preferences::get()->setDouble("/tools/calligraphic/cap_rounding", _cap_rounding_item.get_adjustment()->get_value());
269}
270
272{
273 Preferences::get()->setDouble("/tools/calligraphic/tremor", _tremor_item.get_adjustment()->get_value());
275}
276
278{
279 Preferences::get()->setDouble("/tools/calligraphic/wiggle", _wiggle_item.get_adjustment()->get_value());
281}
282
284{
285 Preferences::get()->setDouble("/tools/calligraphic/mass", _mass_item.get_adjustment()->get_value());
287}
288
289void CalligraphyToolbar::on_pref_toggled(Gtk::ToggleButton *item, Glib::ustring const &path)
290{
291 Preferences::get()->setBool(path, item->get_active());
293}
294
296{
297 if (_presets_blocked) {
298 return;
299 }
300
301 auto presets = get_presets_list();
302
303 int index = 1; // 0 is for no preset.
304 for (auto i = presets.begin(); i != presets.end(); ++i, ++index) {
305 bool match = true;
306
307 auto preset = Preferences::get()->getAllEntries(*i);
308 for (auto &j : preset) {
309 auto const entry_name = j.getEntryName();
310 if (entry_name == "id" || entry_name == "name") {
311 continue;
312 }
313
314 if (auto widget = _widget_map[entry_name.data()]) {
315 if (auto adj = dynamic_cast<Gtk::Adjustment *>(widget)) {
316 double v = j.getDouble();
317 if (std::abs(adj->get_value() - v) > 1e-6) {
318 match = false;
319 break;
320 }
321 } else if (auto toggle = dynamic_cast<Gtk::ToggleButton *>(widget)) {
322 bool v = j.getBool();
323 if (toggle->get_active() != v) {
324 match = false;
325 break;
326 }
327 }
328 }
329 }
330
331 if (match) {
332 // newly added item is at the same index as the
333 // save command, so we need to change twice for it to take effect
334 _profile_selector_combo.set_active(0);
335 _profile_selector_combo.set_active(index);
336 return;
337 }
338 }
339
340 // no match found
341 _profile_selector_combo.set_active(0);
342}
343
345{
346 _angle_item.set_sensitive(!_usetilt_btn->get_active());
347 on_pref_toggled(_usetilt_btn, "/tools/calligraphic/usetilt");
348}
349
351{
352 _presets_blocked = true;
353
354 _profile_selector_combo.remove_all();
355 _profile_selector_combo.append(_("No preset"));
356
357 // iterate over all presets to populate the list
358 for (auto const &preset : get_presets_list()) {
359 auto const preset_name = Preferences::get()->getString(preset + "/name");
360 if (!preset_name.empty()) {
361 _profile_selector_combo.append(_(preset_name.data()));
362 }
363 }
364
365 _presets_blocked = false;
366
368}
369
371{
372 auto mode = _profile_selector_combo.get_active_row_number();
373
374 if (_presets_blocked) {
375 return;
376 }
377
378 // mode is one-based so we subtract 1
379 auto const presets = get_presets_list();
380
381 Glib::ustring preset_path = "";
382 if (mode - 1 < presets.size()) {
383 preset_path = presets.at(mode - 1);
384 }
385
386 if (!preset_path.empty()) {
387 _presets_blocked = true; //temporarily block the selector so no one will updadte it while we're reading it
388
389 auto preset = Preferences::get()->getAllEntries(preset_path);
390
391 // Shouldn't this be std::map?
392 for (auto &i : preset) {
393 auto const entry_name = i.getEntryName();
394 if (entry_name == "id" || entry_name == "name") {
395 continue;
396 }
397 if (auto widget = _widget_map[entry_name.data()]) {
398 if (auto adj = dynamic_cast<Gtk::Adjustment *>(widget)) {
399 adj->set_value(i.getDouble());
400 } else if (auto toggle = dynamic_cast<Gtk::ToggleButton *>(widget)) {
401 toggle->set_active(i.getBool());
402 } else {
403 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
404 }
405 } else {
406 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
407 }
408 }
409
410 _presets_blocked = false;
411 }
412}
413
415{
416 save_profile(nullptr);
417}
418
420{
421 auto const unit = _tracker->getActiveUnit();
422 auto prefs = Preferences::get();
423 prefs->setBool("/tools/calligraphic/abs_width", _tracker->getCurrentLabel() != "%");
424 prefs->setDouble("/tools/calligraphic/width",
425 std::clamp(prefs->getDouble("/tools/calligraphic/width"), Quantity::convert(0.001, unit, "px"),
426 Quantity::convert(100, unit, "px")));
427 prefs->setString("/tools/calligraphic/unit", unit->abbr);
428}
429
431{
433 auto prefs = Preferences::get();
434 if (! _desktop) {
435 return;
436 }
437
438 if (_presets_blocked) {
439 return;
440 }
441
442 Glib::ustring current_profile_name = _profile_selector_combo.get_active_text();
443
444 if (current_profile_name == _("No preset")) {
445 current_profile_name = "";
446 }
447
448 CalligraphicProfileRename::show(_desktop, current_profile_name);
449 if (!CalligraphicProfileRename::applied()) {
450 // dialog cancelled
452 return;
453 }
454 Glib::ustring new_profile_name = CalligraphicProfileRename::getProfileName();
455
456 if (new_profile_name.empty()) {
457 // empty name entered
459 return;
460 }
461
462 _presets_blocked = true;
463
464 // If there's a preset with the given name, find it and set save_path appropriately
465 auto presets = get_presets_list();
466 int total_presets = presets.size();
467 int new_index = -1;
468 Glib::ustring save_path; // profile pref path without a trailing slash
469
470 for (int i = 0; i < presets.size(); i++) {
471 auto &p = presets[i];
472 auto const name = prefs->getString(p + "/name");
473 if (!name.empty() && (new_profile_name == name || current_profile_name == name)) {
474 new_index = i;
475 save_path = p;
476 break;
477 }
478 }
479
480 if (CalligraphicProfileRename::deleted() && new_index != -1) {
481 prefs->remove(save_path);
482 _presets_blocked = false;
484 return;
485 }
486
487 if (new_index == -1) {
488 // no preset with this name, create
489 new_index = total_presets + 1;
490 auto const profile_id = g_strdup_printf("/dcc%d", new_index);
491 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
492 g_free(profile_id);
493 }
494
495 for (auto const &[widget_name, widget] : _widget_map) {
496 if (widget) {
497 if (auto adj = dynamic_cast<Gtk::Adjustment *>(widget)) {
498 prefs->setDouble(save_path + "/" + widget_name, adj->get_value());
499 } else if (auto toggle = dynamic_cast<Gtk::ToggleButton *>(widget)) {
500 prefs->setBool(save_path + "/" + widget_name, toggle->get_active());
501 } else {
502 g_warning("Unknown widget type for preset: %s\n", widget_name.c_str());
503 }
504 } else {
505 g_warning("Bad key when writing preset: %s\n", widget_name.c_str());
506 }
507 }
508 prefs->setString(save_path + "/name", new_profile_name);
509
510 _presets_blocked = true;
512}
513
514} // namespace Inkscape::UI::Toolbar
515
516/*
517 Local Variables:
518 mode:c++
519 c-file-style:"stroustrup"
520 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
521 indent-tabs-mode:nil
522 fill-column:99
523 End:
524*/
525// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Gtk builder utilities.
Dialog for naming calligraphic profiles.
std::vector< Entry > getAllEntries(Glib::ustring const &path)
Get all entries from the specified directory.
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
std::vector< Glib::ustring > getAllDirs(Glib::ustring const &path)
Get all subdirectories of the specified directory.
void setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
std::unique_ptr< UI::Widget::UnitTracker > _tracker
std::unique_ptr< SimplePrefPusher > _tracebackground_pusher
std::unique_ptr< SimplePrefPusher > _usetilt_pusher
std::unique_ptr< SimplePrefPusher > _usepressure_pusher
void on_pref_toggled(Gtk::ToggleButton *item, Glib::ustring const &path)
std::map< std::string, Glib::Object * > _widget_map
void(CalligraphyToolbar::*)() ValueChangedMemFun
void setup_derived_spin_button(UI::Widget::SpinButton &btn, Glib::ustring const &name, double default_value, ValueChangedMemFun value_changed_mem_fun)
Base class for all tool toolbars.
Definition toolbar.h:72
SpinButton widget, that allows entry of simple math expressions (also units, when linked with UnitMen...
Definition spinbutton.h:52
void setDefocusTarget(decltype(_defocus_target) target)
Definition spinbutton.h:127
void set_custom_numeric_menu_data(NumericMenuData &&custom_menu_data)
A combobox that can be displayed in a toolbar.
Editable view implementation.
SPItem * item
Definition desktop.h:50
static std::vector< Glib::ustring > get_presets_list()
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.
int mode
int index
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder