Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
inkscape-preferences.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/* Authors:
7 * Carl Hetherington
8 * Marco Scholten
9 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
10 * Bruno Dilly <bruno.dilly@gmail.com>
11 *
12 * Copyright (C) 2004-2013 Authors
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
18
19#ifdef HAVE_CONFIG_H
20# include "config.h" // only include where actually required!
21#endif
22
23#include <algorithm>
24#include <fstream>
25#include <iomanip>
26#include <string>
27#include <gio/gio.h>
28#include <glibmm/error.h>
29#include <glibmm/i18n.h>
30#include <glibmm/markup.h>
31#include <glibmm/miscutils.h>
32#include <glibmm/convert.h>
33#include <glibmm/regex.h>
34#include <glibmm/ustring.h>
35#include <giomm/themedicon.h>
36#include <gtkmm/binlayout.h>
37#include <gdkmm/display.h>
38#include <gtkmm/accelerator.h>
39#include <gtkmm/box.h>
40#include <gtkmm/cssprovider.h>
41#include <gtkmm/dialog.h>
42#include <gtkmm/entry.h>
43#include <gtkmm/enums.h>
44#include <gtkmm/eventcontrollerkey.h>
45#include <gtkmm/fontchooserdialog.h>
46#include <gtkmm/icontheme.h>
47#include <gtkmm/image.h>
48#include <gtkmm/label.h>
49#include <gtkmm/messagedialog.h>
50#include <gtkmm/picture.h>
51#include <gtkmm/recentmanager.h>
52#include <gtkmm/revealer.h>
53#include <gtkmm/scale.h>
54#include <gtkmm/settings.h>
55#include <gtkmm/styleprovider.h>
56#include <gtkmm/togglebutton.h>
57#include <sigc++/adaptors/bind.h>
58#include <sigc++/functors/mem_fun.h>
59#include <cairomm/matrix.h>
60#include <cairomm/surface.h>
61#include <glibmm/refptr.h>
62#include <gtkmm/comboboxtext.h>
63#include <gtkmm/object.h>
67
68#if WITH_GSOURCEVIEW
69# include <gtksourceview/gtksource.h>
70#endif
71
72#include "auto-save.h"
73#include "document.h"
74#include "enums.h"
75#include "inkscape-window.h"
76#include "inkscape.h"
77#include "message-stack.h"
78#include "path-prefix.h"
79#include "preferences.h"
80#include "selcue.h"
81#include "selection-chemistry.h"
82#include "selection.h"
83#include "style.h"
84
85#include "colors/cms/system.h"
86#include "colors/cms/profile.h"
87#include "colors/manager.h"
88#include "colors/spaces/base.h"
90#include <sigc++/scoped_connection.h>
91#include "io/resource.h"
92#include "ui/builder-utils.h"
93#include "ui/dialog-run.h"
94#include "ui/interface.h"
95#include "ui/modifiers.h"
96#include "ui/pack.h"
97#include "ui/shortcuts.h"
98#include "ui/themes.h"
102#include "ui/util.h"
106#include "util/trim.h"
109
110namespace Inkscape::UI::Dialog {
111
121
122[[nodiscard]] static auto reset_icon()
123{
124 auto const image = Gtk::make_managed<Gtk::Image>();
125 image->set_from_icon_name("reset");
126 image->set_opacity(0.6);
127 image->set_tooltip_text(_("Requires restart to take effect"));
128 return image;
129}
130
139static bool fuzzy_search(Glib::ustring const &pattern, Glib::ustring const &string, float &score)
140{
141 auto const norm_patt = pattern.lowercase().normalize();
142 auto const norm_str = string .lowercase().normalize();
143 bool const found = norm_str.find(norm_patt) != norm_str.npos;
144 score = found ? static_cast<float>(pattern.size()) / string.size() : 0;
145 return score > 0.0 ? true : false;
146}
147
155static bool fuzzy_search(Glib::ustring const &pattern, Glib::ustring const &string)
156{
157 float score{};
158 return fuzzy_search(pattern, string, score);
159}
160
161[[nodiscard]] static auto get_children_or_mnemonic_labels(Gtk::Widget &widget)
162{
163 if (dynamic_cast<Gtk::DropDown *>(&widget)) {
164 std::vector<Gtk::Widget *> children;
165 return children;
166 }
167 auto children = UI::get_children(widget);
168 if (children.empty()) {
169 children = widget.list_mnemonic_labels();
170 }
171 return children;
172}
173
181static int get_num_matches(Glib::ustring const &key, Gtk::Widget *widget)
182{
183 g_assert(widget);
184
185 int matches = 0;
186
187 if (auto const label = dynamic_cast<Gtk::Label *>(widget)) {
188 if (fuzzy_search(key, label->get_text().lowercase())) {
189 ++matches;
190 }
191 }
192
193 for (auto const child : get_children_or_mnemonic_labels(*widget)) {
194 matches += get_num_matches(key, child);
195 }
196
197 return matches;
198}
199
200// Shortcuts model =============
201
202class ModelColumns final : public Gtk::TreeModel::ColumnRecord {
203public:
204 ModelColumns() {
205 add(name);
206 add(id);
207 add(shortcut);
208 add(description);
209 add(shortcutkey);
210 add(user_set);
211 }
212
213 Gtk::TreeModelColumn<Glib::ustring> name;
214 Gtk::TreeModelColumn<Glib::ustring> id;
215 Gtk::TreeModelColumn<Glib::ustring> shortcut;
216 Gtk::TreeModelColumn<Glib::ustring> description;
217 Gtk::TreeModelColumn<Gtk::AccelKey> shortcutkey;
218 Gtk::TreeModelColumn<unsigned int> user_set;
219};
220ModelColumns _kb_columns;
221static ModelColumns& onKBGetCols() {
222 return _kb_columns;
223}
224
231void InkscapePreferences::add_highlight(Gtk::Label *label, Glib::ustring const &key)
232{
233 Glib::ustring text = label->get_text();
234 Glib::ustring const n_text = text.lowercase().normalize();
235 Glib::ustring const n_key = key.lowercase().normalize();
236 label->add_css_class("highlight");
237 auto const pos = n_text.find(n_key);
238 auto const len = n_key.size();
239 text = Glib::Markup::escape_text(text.substr(0, pos)) + "<span weight=\"bold\" underline=\"single\">" +
240 Glib::Markup::escape_text(text.substr(pos, len)) + "</span>" +
241 Glib::Markup::escape_text(text.substr(pos + len));
242 label->set_markup(text);
243}
244
252{
253 if (label->get_use_markup()) {
254 Glib::ustring text = label->get_text();
255 label->set_text(text);
256 label->remove_css_class("highlight");
257 }
258}
259
261 : DialogBase("/dialogs/preferences", "Preferences"),
262 _minimum_width(0),
263 _minimum_height(0),
264 _natural_width(900),
265 _natural_height(700),
266 _current_page(nullptr),
267 _init(true)
268{
269 //get the width of a spinbutton
270 {
272 sb.set_width_chars(6);
273 append(sb);
274 int ignore{};
275 sb.measure(Gtk::Orientation::HORIZONTAL, -1, ignore, _sb_width, ignore, ignore);
276 remove(sb);
277 }
278
279 //Main HBox
280 auto const hbox_list_page = Gtk::make_managed<Gtk::Box>();
281 hbox_list_page->set_margin(12);
282 hbox_list_page->set_spacing(12);
283 append(*hbox_list_page);
284
285 //Pagelist
286 auto const list_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 3);
287 auto const scrolled_window = Gtk::make_managed<Gtk::ScrolledWindow>();
288 _search.set_valign(Gtk::Align::START);
289 UI::pack_start(*list_box, _search, false, true);
290 UI::pack_start(*list_box, *scrolled_window, false, true);
291 UI::pack_start(*hbox_list_page, *list_box, false, true);
292 _page_list.set_headers_visible(false);
293 scrolled_window->set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC);
294 scrolled_window->set_valign(Gtk::Align::FILL);
295 scrolled_window->set_propagate_natural_width();
296 scrolled_window->set_propagate_natural_height();
297 scrolled_window->set_child(_page_list);
298 scrolled_window->set_vexpand_set(true);
299 scrolled_window->set_vexpand(true);
300 scrolled_window->set_has_frame(true);
301 _page_list_model = Gtk::TreeStore::create(_page_list_columns);
302 _page_list_model_filter = Gtk::TreeModelFilter::create(_page_list_model);
303 _page_list_model_sort = Gtk::TreeModelSort::create(_page_list_model_filter);
304 _page_list_model_sort->set_sort_column(_page_list_columns._col_name, Gtk::SortType::ASCENDING);
305 _page_list.set_enable_search(false);
307 _page_list.append_column("name",_page_list_columns._col_name);
308 Glib::RefPtr<Gtk::TreeSelection> page_list_selection = _page_list.get_selection();
309 page_list_selection->signal_changed().connect(sigc::mem_fun(*this, &InkscapePreferences::on_pagelist_selection_changed));
310 page_list_selection->set_mode(Gtk::SelectionMode::BROWSE);
311
312 // Search
313 _page_list.set_search_column(-1); // this disables pop-up search!
314 _search.signal_search_changed().connect(sigc::mem_fun(*this, &InkscapePreferences::on_search_changed));
315 _search.set_tooltip_text("Search");
316 _page_list_model_sort->set_sort_func(
317 _page_list_columns._col_name, [this](Gtk::TreeModel::const_iterator const &a,
318 Gtk::TreeModel::const_iterator const &b) -> int
319 {
320 float score_a, score_b;
321 Glib::ustring key = _search.get_text().lowercase();
322 if (key == "") {
323 return -1;
324 }
325 Glib::ustring label_a = a->get_value(_page_list_columns._col_name).lowercase();
326 Glib::ustring label_b = b->get_value(_page_list_columns._col_name).lowercase();
327 auto *grid_a = a->get_value(_page_list_columns._col_page);
328 auto *grid_b = b->get_value(_page_list_columns._col_page);
329 int num_res_a = num_widgets_in_grid(key, grid_a);
330 int num_res_b = num_widgets_in_grid(key, grid_b);
331 fuzzy_search(key, label_a, score_a);
332 fuzzy_search(key, label_b, score_b);
333 if (score_a > score_b) {
334 return -1;
335 } else if (score_a < score_b) {
336 return 1;
337 } else if (num_res_a >= num_res_b) {
338 return -1;
339 } else if (num_res_a < num_res_b) {
340 return 1;
341 } else {
342 return a->get_value(_page_list_columns._col_id) > b->get_value(_page_list_columns._col_id) ? -1 : 1;
343 }
344 });
345
346 _search.signal_next_match().connect([this]{
347 if (_search_results.size() > 0) {
348 Gtk::TreeModel::iterator curr = _page_list.get_selection()->get_selected();
349 auto _page_list_selection = _page_list.get_selection();
350 auto next = get_next_result(curr);
351 if (next) {
352 _page_list.get_model()->get_iter(next);
353 _page_list.scroll_to_cell(next, *_page_list.get_column(0));
354 _page_list.set_cursor(next);
355 }
356 }
357 });
358
359 _search.signal_previous_match().connect([this]{
360 if (_search_results.size() > 0) {
361 Gtk::TreeModel::iterator curr = _page_list.get_selection()->get_selected();
362 auto _page_list_selection = _page_list.get_selection();
363 auto prev = get_prev_result(curr);
364 if (prev) {
365 _page_list.get_model()->get_iter(prev);
366 _page_list.scroll_to_cell(prev, *_page_list.get_column(0));
367 _page_list.set_cursor(prev);
368 }
369 }
370 });
371
372 auto const key = Gtk::EventControllerKey::create();
373 key->signal_key_pressed().connect([this](auto &&...args) { return on_navigate_key_pressed(args...); }, true);
374 _search.add_controller(key);
375
376 _page_list_model_filter->set_visible_func([this](Gtk::TreeModel::const_iterator const &row){
377 auto key_lower = _search.get_text().lowercase();
378 return recursive_filter(key_lower, row);
379 });
380
381 //Pages
382 auto const vbox_page = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
383 auto const title_frame = Gtk::make_managed<Gtk::Frame>();
384
385 auto const pageScroller = Gtk::make_managed<Gtk::ScrolledWindow>();
386 pageScroller->set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
387 pageScroller->set_propagate_natural_width();
388 pageScroller->set_propagate_natural_height();
389 pageScroller->set_child(*vbox_page);
390 UI::pack_start(*hbox_list_page, *pageScroller, true, true);
391
392 title_frame->set_child(_page_title);
393 UI::pack_start(*vbox_page, *title_frame, false, false);
394 UI::pack_start(*vbox_page, _page_frame, true, true);
395 _page_frame.add_css_class("flat");
396 title_frame->add_css_class("flat");
397
398 initPageTools();
399 initPageUI();
400 initPageBehavior();
401 initPageIO();
402
403 initPageSystem();
404 initPageBitmaps();
405 initPageRendering();
406 initPageSpellcheck();
407
408 signal_map().connect(sigc::mem_fun(*this, &InkscapePreferences::showPage));
409
410 //calculate the size request for this dialog
411 _page_list.expand_all();
412 _page_list_model->foreach_iter(sigc::mem_fun(*this, &InkscapePreferences::GetSizeRequest));
413 _page_list.collapse_all();
414
415 // Set Custom theme
417 _theme_oberver = prefs->createObserver("/theme/", [=]() {
418 prefs->setString("/options/boot/theme", "custom");
419 });
420}
421
422InkscapePreferences::~InkscapePreferences() = default;
423
431void InkscapePreferences::get_widgets_in_grid(Glib::ustring const &key, Gtk::Widget *widget)
432{
433 g_assert(widget);
434
435 if (auto const label = dynamic_cast<Gtk::Label *>(widget)) {
436 if (fuzzy_search(key, label->get_text())) {
437 _search_results.push_back(label);
438 }
439 }
440
441 for (auto const child : get_children_or_mnemonic_labels(*widget)) {
442 get_widgets_in_grid(key, child);
443 }
444}
445
454int InkscapePreferences::num_widgets_in_grid(Glib::ustring const &key, Gtk::Widget *widget)
455{
456 g_assert(widget);
457
458 int results = 0;
459
460 if (auto const label = dynamic_cast<Gtk::Label *>(widget)) {
461 if (fuzzy_search(key, label->get_text())) {
462 ++results;
463 }
464 }
465
466 for (auto const child : get_children_or_mnemonic_labels(*widget)) {
467 results += num_widgets_in_grid(key, child);
468 }
469
470 return results;
471}
472
477void InkscapePreferences::on_search_changed()
478{
479 _num_results = 0;
480 if (_search_results.size() > 0) {
481 for (auto *result : _search_results) {
482 remove_highlight(result);
483 }
484 _search_results.clear();
485 }
486 auto key = _search.get_text();
487 _page_list_model_filter->refilter();
488 // get first iter
489 Gtk::TreeModel::Children children = _page_list.get_model()->children();
490 Gtk::TreeModel::iterator iter = children.begin();
491
492 highlight_results(key, iter);
493 goto_first_result();
494 if (key == "") {
495 Gtk::TreeModel::Children children = _page_list.get_model()->children();
496 Gtk::TreeModel::iterator iter = children.begin();
497 _page_list.scroll_to_cell(Gtk::TreePath(iter), *_page_list.get_column(0));
498 _page_list.set_cursor(Gtk::TreePath(iter));
499 } else if (_num_results == 0 && key != "") {
500 _page_list.set_has_tooltip(false);
501 _show_all = true;
502 _page_list_model_filter->refilter();
503 _show_all = false;
504 show_not_found();
505 } else {
506 _page_list.expand_all();
507 }
508}
509
513void InkscapePreferences::goto_first_result()
514{
515 auto key = _search.get_text();
516 if (_num_results > 0) {
517 Gtk::TreeModel::iterator curr = _page_list.get_model()->children().begin();
518 if (fuzzy_search(key, curr->get_value(_page_list_columns._col_name)) ||
519 get_num_matches(key, curr->get_value(_page_list_columns._col_page)) > 0) {
520 _page_list.scroll_to_cell(Gtk::TreePath(curr), *_page_list.get_column(0));
521 _page_list.set_cursor(Gtk::TreePath(curr));
522 } else {
523 auto next = get_next_result(curr);
524 if (next) {
525 _page_list.scroll_to_cell(next, *_page_list.get_column(0));
526 _page_list.set_cursor(next);
527 }
528 }
529 }
530}
531
540Gtk::TreePath InkscapePreferences::get_next_result(Gtk::TreeModel::iterator &iter, bool check_children)
541{
542 auto key = _search.get_text();
543 Gtk::TreePath path = Gtk::TreePath(iter);
544 if (iter->children().begin() && check_children) { // check for search results in children
545 auto child = iter->children().begin();
546 _page_list.expand_row(path, false);
547 if (fuzzy_search(key, child->get_value(_page_list_columns._col_name)) ||
548 get_num_matches(key, child->get_value(_page_list_columns._col_page)) > 0) {
549 return Gtk::TreePath(child);
550 } else {
551 return get_next_result(child);
552 }
553 } else {
554 ++iter; // go to next row
555 if (iter) { // if row exists
556 if (fuzzy_search(key, iter->get_value(_page_list_columns._col_name)) ||
557 get_num_matches(key, iter->get_value(_page_list_columns._col_page))) {
558 path.next();
559 return path;
560 } else {
561 return get_next_result(iter);
562 }
563 } else if (path.up() && path) {
564 path.next();
565 iter = _page_list.get_model()->get_iter(path);
566 if (iter) {
567 if (fuzzy_search(key, iter->get_value(_page_list_columns._col_name)) ||
568 get_num_matches(key, iter->get_value(_page_list_columns._col_page))) {
569 return Gtk::TreePath(iter);
570 } else {
571 return get_next_result(iter);
572 }
573 } else {
574 path.up();
575 if (path) {
576 iter = _page_list.get_model()->get_iter(path);
577 return get_next_result(iter, false); // dont check for children
578 } else {
579 return Gtk::TreePath(_page_list.get_model()->children().begin());
580 }
581 }
582 }
583 }
584 assert(!iter);
585 return Gtk::TreePath();
586}
587
596Gtk::TreePath InkscapePreferences::get_prev_result(Gtk::TreeModel::iterator &iter, bool iterate)
597{
598 auto key = _search.get_text();
599 Gtk::TreePath path = Gtk::TreePath(iter);
600 if (iterate) {
601 --iter;
602 }
603 if (iter) {
604 if (iter->children().begin()) {
605 auto child = iter->children().end();
606 --child;
607 Gtk::TreePath path = Gtk::TreePath(iter);
608 _page_list.expand_row(path, false);
609 return get_prev_result(child, false);
610 } else if (fuzzy_search(key, iter->get_value(_page_list_columns._col_name)) ||
611 get_num_matches(key, iter->get_value(_page_list_columns._col_page))) {
612 return (Gtk::TreePath(iter));
613 } else {
614 return get_prev_result(iter);
615 }
616 } else if (path.up()) {
617 if (path) {
618 iter = _page_list.get_model()->get_iter(path);
619 if (fuzzy_search(key, iter->get_value(_page_list_columns._col_name)) ||
620 get_num_matches(key, iter->get_value(_page_list_columns._col_page))) {
621 return path;
622 } else {
623 return get_prev_result(iter);
624 }
625 } else {
626 auto lastIter = _page_list.get_model()->children().end();
627 --lastIter;
628 return get_prev_result(lastIter, false);
629 }
630 } else {
631 return Gtk::TreePath(iter);
632 }
633}
634
643bool InkscapePreferences::on_navigate_key_pressed(unsigned keyval, unsigned /*keycode*/,
644 Gdk::ModifierType state)
645{
646 if (keyval != GDK_KEY_F3 || _search_results.size() == 0) {
647 return false;
648 }
649
650 auto const modmask = Gtk::Accelerator::get_default_mod_mask();
651 if ((state & modmask) == Gdk::ModifierType::SHIFT_MASK) {
652 Gtk::TreeModel::iterator curr = _page_list.get_selection()->get_selected();
653 auto _page_list_selection = _page_list.get_selection();
654 auto prev = get_prev_result(curr);
655 if (prev) {
656 _page_list.scroll_to_cell(prev, *_page_list.get_column(0));
657 _page_list.set_cursor(prev);
658 }
659 } else {
660 Gtk::TreeModel::iterator curr = _page_list.get_selection()->get_selected();
661 auto _page_list_selection = _page_list.get_selection();
662 auto next = get_next_result(curr);
663 if (next) {
664 _page_list.scroll_to_cell(next, *_page_list.get_column(0));
665 _page_list.set_cursor(next);
666 }
667 }
668 return false;
669}
670
677void InkscapePreferences::highlight_results(Glib::ustring const &key, Gtk::TreeModel::iterator &iter)
678{
679 Gtk::TreeModel::Path path;
680 Glib::ustring Txt;
681 while (iter) {
682 auto const grid = iter->get_value(_page_list_columns._col_page);
683 get_widgets_in_grid(key, grid);
684 if (_search_results.size() > 0) {
685 for (auto *result : _search_results) {
686 // underline and highlight
687 add_highlight(static_cast<Gtk::Label *>(result), key);
688 }
689 }
690 if (iter->children()) {
691 auto children = iter->children();
692 auto child_iter = children.begin();
693 highlight_results(key, child_iter);
694 }
695 iter++;
696 }
697}
698
707bool InkscapePreferences::recursive_filter(Glib::ustring &key, Gtk::TreeModel::const_iterator const &iter)
708{
709 if(_show_all)
710 return true;
711 auto row_label = iter->get_value(_page_list_columns._col_name).lowercase();
712 if (key == "") {
713 return true;
714 }
715 if (fuzzy_search(key, row_label)) {
716 ++_num_results;
717 return true;
718 }
719 auto *grid = iter->get_value(_page_list_columns._col_page);
720 int matches = get_num_matches(key, grid);
721 _num_results += matches;
722 if (matches) {
723 return true;
724 }
725 auto child = iter->children().begin();
726 if (child) {
727 for (auto inner = child; inner; ++inner) {
728 if (recursive_filter(key, inner)) {
729 return true;
730 }
731 }
732 }
733 return false;
734}
735
736Gtk::TreeModel::iterator InkscapePreferences::AddPage(DialogPage& p, Glib::ustring title, int id)
737{
738 return AddPage(p, title, Gtk::TreeModel::iterator() , id);
739}
740
741Gtk::TreeModel::iterator InkscapePreferences::AddPage(DialogPage& p, Glib::ustring title, Gtk::TreeModel::iterator parent, int id)
742{
743 Gtk::TreeModel::iterator iter;
744 if (parent)
745 iter = _page_list_model->append((*parent).children());
746 else
747 iter = _page_list_model->append();
748 Gtk::TreeModel::Row row = *iter;
749 row[_page_list_columns._col_name] = title;
750 row[_page_list_columns._col_id] = id;
751 row[_page_list_columns._col_page] = &p;
752 return iter;
753}
754
755void InkscapePreferences::AddSelcueCheckbox(DialogPage &p, Glib::ustring const &prefs_path, bool def_value)
756{
757 auto const cb = Gtk::make_managed<PrefCheckButton>();
758 cb->init ( _("Show selection cue"), prefs_path + "/selcue", def_value);
759 p.add_line( false, "", *cb, "", _("Whether selected objects display a selection cue (the same as in selector)"));
760}
761
762void InkscapePreferences::AddGradientCheckbox(DialogPage &p, Glib::ustring const &prefs_path, bool def_value)
763{
764 auto const cb = Gtk::make_managed<PrefCheckButton>();
765 cb->init ( _("Enable gradient editing"), prefs_path + "/gradientdrag", def_value);
766 p.add_line( false, "", *cb, "", _("Whether selected objects display gradient editing controls"));
767}
768
769void InkscapePreferences::AddLayerChangeCheckbox(DialogPage &p, Glib::ustring const &prefs_path, bool def_value)
770{
771 auto const cb = Gtk::make_managed<PrefCheckButton>();
772 cb->init ( _("Change layer on selection"), prefs_path + "/changelayer", def_value);
773 p.add_line( false, "", *cb, "", _("Whether selecting objects in another layer changes the active layer"));
774}
775
776void InkscapePreferences::AddPageChangeCheckbox(DialogPage &p, Glib::ustring const &prefs_path, bool def_value)
777{
778 auto const cb = Gtk::make_managed<PrefCheckButton>();
779 cb->init ( _("Change page on selection"), prefs_path + "/changepage", def_value);
780 p.add_line( false, "", *cb, "", _("Whether selecting objects on another page changes the current page"));
781}
782
783void InkscapePreferences::AddConvertGuidesCheckbox(DialogPage &p, Glib::ustring const &prefs_path, bool def_value) {
784 auto const cb = Gtk::make_managed<PrefCheckButton>();
785 cb->init ( _("Conversion to guides uses edges instead of bounding box"), prefs_path + "/convertguides", def_value);
786 p.add_line( false, "", *cb, "", _("Converting an object to guides places these along the object's true edges (imitating the object's shape), not along the bounding box"));
787}
788
789void InkscapePreferences::AddDotSizeSpinbutton(DialogPage &p, Glib::ustring const &prefs_path, double def_value)
790{
791 auto const sb = Gtk::make_managed<PrefSpinButton>();
792 sb->init ( prefs_path + "/dot-size", 0.0, 1000.0, 0.1, 10.0, def_value, false, false);
793 p.add_line( false, _("Ctrl+click _dot size:"), *sb, _("times current stroke width"),
794 _("Size of dots created with Ctrl+click (relative to current stroke width)"),
795 false );
796}
797
798void InkscapePreferences::AddBaseSimplifySpinbutton(DialogPage &p, Glib::ustring const &prefs_path, double def_value)
799{
800 auto const sb = Gtk::make_managed<PrefSpinButton>();
801 sb->init ( prefs_path + "/base-simplify", 0.0, 100.0, 1.0, 10.0, def_value, false, false);
802 p.add_line( false, _("Base simplify:"), *sb, _("on dynamic LPE simplify"),
803 _("Base simplify of dynamic LPE based simplify"),
804 false );
805}
806
807
808static void StyleFromSelectionToTool(Glib::ustring const &prefs_path, StyleSwatch *swatch)
809{
810 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
811 if (desktop == nullptr)
812 return;
813
815
816 if (selection->isEmpty()) {
818 _("<b>No objects selected</b> to take the style from."));
819 return;
820 }
821 SPItem *item = selection->singleItem();
822 if (!item) {
823 /* TODO: If each item in the selection has the same style then don't consider it an error.
824 * Maybe we should try to handle multiple selections anyway, e.g. the intersection of the
825 * style attributes for the selected items. */
827 _("<b>More than one object selected.</b> Cannot take style from multiple objects."));
828 return;
829 }
830
832
833 if (!css) return;
834
835 // remove black-listed properties
837
838 // only store text style for the text tool
839 if (prefs_path != "/tools/text") {
841 }
842
843 // we cannot store properties with uris - they will be invalid in other documents
845
847 prefs->setStyle(prefs_path + "/style", css);
849
850 // update the swatch
851 if (swatch) {
852 SPCSSAttr *css = prefs->getInheritedStyle(prefs_path + "/style");
853 swatch->setStyle (css);
855 }
856}
857
858void InkscapePreferences::AddNewObjectsStyle(DialogPage &p, Glib::ustring const &prefs_path, const gchar *banner)
859{
860 if (banner)
861 p.add_group_header(banner);
862 else
863 p.add_group_header( _("Style of new objects"));
864 auto const current = Gtk::make_managed<PrefRadioButton>();
865 current->init ( _("Last used style"), prefs_path + "/usecurrent", 1, true, nullptr);
866 p.add_line( true, "", *current, "",
867 _("Apply the style you last set on an object"));
868
869 auto const own = Gtk::make_managed<PrefRadioButton>();
870 auto const hb = Gtk::make_managed<Gtk::Box>();
871 own->init ( _("This tool's own style:"), prefs_path + "/usecurrent", 0, false, current);
872 own->set_halign(Gtk::Align::START);
873 own->set_valign(Gtk::Align::START);
874 hb->append(*own);
875
876 p.set_tip( *own, _("Each tool may store its own style to apply to the newly created objects. Use the button below to set it."));
877 p.add_line( true, "", *hb, "", "");
878
879 // style swatch
880 auto const button = Gtk::make_managed<Gtk::Button>(_("Take from selection"), true);
881 StyleSwatch *swatch = nullptr;
883
884 if (prefs->getInt(prefs_path + "/usecurrent")) {
885 button->set_sensitive(false);
886 }
887
888 SPCSSAttr *css = prefs->getStyle(prefs_path + "/style");
889 swatch = Gtk::make_managed<StyleSwatch>(css, _("This tool's style of new objects"));
890 hb->append(*swatch);
892
893 button->signal_clicked().connect(sigc::bind(&StyleFromSelectionToTool, prefs_path, swatch));
894 own->changed_signal.connect( sigc::mem_fun(*button, &Gtk::Button::set_sensitive) );
895 p.add_line( true, "", *button, "",
896 _("Remember the style of the (first) selected object as this tool's style"));
897}
898#define get_tool_action(toolname) ("win.tool-switch('" + toolname + "')")
899Glib::ustring get_tool_action_name(Glib::ustring toolname)
900{
901 auto *iapp = InkscapeApplication::instance();
902 if (iapp)
903 return iapp->get_action_extra_data().get_label_for_action(get_tool_action(toolname));
904 return "";
905}
906void InkscapePreferences::initPageTools()
907{
908 Gtk::TreeModel::iterator iter_tools = this->AddPage(_page_tools, _("Tools"), PREFS_PAGE_TOOLS);
909 this->AddPage(_page_selector, get_tool_action_name("Select"), iter_tools, PREFS_PAGE_TOOLS_SELECTOR);
910 this->AddPage(_page_node, get_tool_action_name("Node"), iter_tools, PREFS_PAGE_TOOLS_NODE);
911
912 // shapes
913 Gtk::TreeModel::iterator iter_shapes = this->AddPage(_page_shapes, _("Shapes"), iter_tools, PREFS_PAGE_TOOLS_SHAPES);
914 this->AddPage(_page_rectangle, get_tool_action_name("Rect"), iter_shapes, PREFS_PAGE_TOOLS_SHAPES_RECT);
915 this->AddPage(_page_ellipse, get_tool_action_name("Arc"), iter_shapes, PREFS_PAGE_TOOLS_SHAPES_ELLIPSE);
916 this->AddPage(_page_star, get_tool_action_name("Star"), iter_shapes, PREFS_PAGE_TOOLS_SHAPES_STAR);
917 this->AddPage(_page_3dbox, get_tool_action_name("3DBox"), iter_shapes, PREFS_PAGE_TOOLS_SHAPES_3DBOX);
918 this->AddPage(_page_spiral, get_tool_action_name("Spiral"), iter_shapes, PREFS_PAGE_TOOLS_SHAPES_SPIRAL);
919
920 this->AddPage(_page_pen, get_tool_action_name("Pen"), iter_tools, PREFS_PAGE_TOOLS_PEN);
921 this->AddPage(_page_pencil, get_tool_action_name("Pencil"), iter_tools, PREFS_PAGE_TOOLS_PENCIL);
922 this->AddPage(_page_calligraphy, get_tool_action_name("Calligraphic"), iter_tools, PREFS_PAGE_TOOLS_CALLIGRAPHY);
923 this->AddPage(_page_text, get_tool_action_name("Text"), iter_tools, PREFS_PAGE_TOOLS_TEXT);
924
925 this->AddPage(_page_gradient, get_tool_action_name("Gradient"), iter_tools, PREFS_PAGE_TOOLS_GRADIENT);
926 this->AddPage(_page_dropper, get_tool_action_name("Dropper"), iter_tools, PREFS_PAGE_TOOLS_DROPPER);
927 this->AddPage(_page_paintbucket, get_tool_action_name("PaintBucket"), iter_tools, PREFS_PAGE_TOOLS_PAINTBUCKET);
928
929 this->AddPage(_page_tweak, get_tool_action_name("Tweak"), iter_tools, PREFS_PAGE_TOOLS_TWEAK);
930 this->AddPage(_page_spray, get_tool_action_name("Spray"), iter_tools, PREFS_PAGE_TOOLS_SPRAY);
931 this->AddPage(_page_eraser, get_tool_action_name("Eraser"), iter_tools, PREFS_PAGE_TOOLS_ERASER);
932 this->AddPage(_page_connector, get_tool_action_name("Connector"), iter_tools, PREFS_PAGE_TOOLS_CONNECTOR);
933#ifdef WITH_LPETOOL
934 this->AddPage(_page_lpetool, get_tool_action_name("LPETool"), iter_tools, PREFS_PAGE_TOOLS_LPETOOL);
935#endif // WITH_LPETOOL
936 this->AddPage(_page_measure, get_tool_action_name("Measure"), iter_tools, PREFS_PAGE_TOOLS_MEASURE);
937 this->AddPage(_page_zoom, get_tool_action_name("Zoom"), iter_tools, PREFS_PAGE_TOOLS_ZOOM);
938 _page_tools.add_group_header( _("Bounding box to use"));
939 _t_bbox_visual.init ( _("Visual bounding box"), "/tools/bounding_box", 0, false, nullptr); // 0 means visual
940 _page_tools.add_line( true, "", _t_bbox_visual, "",
941 _("This bounding box includes stroke width, markers, filter margins, etc."));
942 _t_bbox_geometric.init ( _("Geometric bounding box"), "/tools/bounding_box", 1, true, &_t_bbox_visual); // 1 means geometric
943 _page_tools.add_line( true, "", _t_bbox_geometric, "",
944 _("This bounding box includes only the bare path"));
945
946 _page_tools.add_group_header( _("Conversion to guides"));
947 _t_cvg_keep_objects.init ( _("Keep objects after conversion to guides"), "/tools/cvg_keep_objects", false);
948 _page_tools.add_line( true, "", _t_cvg_keep_objects, "",
949 _("When converting an object to guides, don't delete the object after the conversion"));
950 _t_cvg_convert_whole_groups.init ( _("Treat groups as a single object"), "/tools/cvg_convert_whole_groups", false);
951 _page_tools.add_line( true, "", _t_cvg_convert_whole_groups, "",
952 _("Treat groups as a single object during conversion to guides rather than converting each child separately"));
953
954 _pencil_average_all_sketches.init ( _("Average all sketches"), "/tools/freehand/pencil/average_all_sketches", false);
955 _calligrapy_keep_selected.init ( _("Select new path"), "/tools/calligraphic/keep_selected", true);
956 _connector_ignore_text.init( _("Don't attach connectors to text objects"), "/tools/connector/ignoretext", true);
957
958 //Selector
959
960
961 AddSelcueCheckbox(_page_selector, "/tools/select", false);
962 AddGradientCheckbox(_page_selector, "/tools/select", false);
963 AddLayerChangeCheckbox(_page_selector, "/tools/select", true);
964 AddPageChangeCheckbox(_page_selector, "/tools/select", true);
965
966 _page_selector.add_group_header( _("When transforming, show"));
967 _t_sel_trans_obj.init ( _("Objects"), "/tools/select/show", "content", true, nullptr);
968 _page_selector.add_line( true, "", _t_sel_trans_obj, "",
969 _("Show the actual objects when moving or transforming"));
970 _t_sel_trans_outl.init ( _("Box outline"), "/tools/select/show", "outline", false, &_t_sel_trans_obj);
971 _page_selector.add_line( true, "", _t_sel_trans_outl, "",
972 _("Show only a box outline of the objects when moving or transforming"));
973 _page_selector.add_group_header( _("Per-object selection cue"));
974 _t_sel_cue_none.init ( C_("Selection cue", "None"), "/options/selcue/value", Inkscape::SelCue::NONE, false, nullptr);
975 _page_selector.add_line( true, "", _t_sel_cue_none, "",
976 _("No per-object selection indication"));
977 _t_sel_cue_mark.init ( _("Mark"), "/options/selcue/value", Inkscape::SelCue::MARK, true, &_t_sel_cue_none);
978 _page_selector.add_line( true, "", _t_sel_cue_mark, "",
979 _("Each selected object has a diamond mark in the top left corner"));
980 _t_sel_cue_box.init ( _("Box"), "/options/selcue/value", Inkscape::SelCue::BBOX, false, &_t_sel_cue_none);
981 _page_selector.add_line( true, "", _t_sel_cue_box, "",
982 _("Each selected object displays its bounding box"));
983
984 //Node
985 AddSelcueCheckbox(_page_node, "/tools/nodes", true);
986 AddGradientCheckbox(_page_node, "/tools/nodes", true);
987 AddLayerChangeCheckbox(_page_node, "/tools/nodes", false);
988
989 _page_node.add_group_header( _("Path outline"));
990 _t_node_pathoutline_color.init(_("Path outline color"), "/tools/nodes/highlight_color", "#ff0000ff");
991 _page_node.add_line( false, "", _t_node_pathoutline_color, "", _("Selects the color used for showing the path outline"), false);
992 _t_node_show_outline.init(_("Always show outline"), "/tools/nodes/show_outline", false);
993 _page_node.add_line( true, "", _t_node_show_outline, "", _("Show outlines for all paths, not only invisible paths"));
994 _t_node_live_outline.init(_("Update outline when dragging nodes"), "/tools/nodes/live_outline", false);
995 _page_node.add_line( true, "", _t_node_live_outline, "", _("Update the outline when dragging or transforming nodes; if this is off, the outline will only update when completing a drag"));
996 _t_node_live_objects.init(_("Update paths when dragging nodes"), "/tools/nodes/live_objects", false);
997 _page_node.add_line( true, "", _t_node_live_objects, "", _("Update paths when dragging or transforming nodes; if this is off, paths will only be updated when completing a drag"));
998 _t_node_show_path_direction.init(_("Show path direction on outlines"), "/tools/nodes/show_path_direction", false);
999 _page_node.add_line( true, "", _t_node_show_path_direction, "", _("Visualize the direction of selected paths by drawing small arrows in the middle of each outline segment"));
1000 _t_node_pathflash_enabled.init ( _("Show temporary path outline"), "/tools/nodes/pathflash_enabled", false);
1001 _page_node.add_line( true, "", _t_node_pathflash_enabled, "", _("When hovering over a path, briefly flash its outline"));
1002 _t_node_pathflash_selected.init ( _("Show temporary outline for selected paths"), "/tools/nodes/pathflash_selected", false);
1003 _page_node.add_line( true, "", _t_node_pathflash_selected, "", _("Show temporary outline even when a path is selected for editing"));
1004 _t_node_pathflash_timeout.init("/tools/nodes/pathflash_timeout", 0, 10000.0, 100.0, 100.0, 1000.0, true, false);
1005 _page_node.add_line( false, _("_Flash time:"), _t_node_pathflash_timeout, "ms", _("Specifies how long the path outline will be visible after a mouse-over (in milliseconds); specify 0 to have the outline shown until mouse leaves the path"), false);
1006 _page_node.add_group_header(_("Editing preferences"));
1007 _t_node_single_node_transform_handles.init(_("Show transform handles for single nodes"), "/tools/nodes/single_node_transform_handles", false);
1008 _page_node.add_line( true, "", _t_node_single_node_transform_handles, "", _("Show transform handles even when only a single node is selected"));
1009 _t_node_delete_flat_corner.init("/tools/node/flat-cusp-angle", 0, 180, 1, 5, 135, false, false);
1010 _page_node.add_line(true, _("Cusp considered flat for deletion:"), _t_node_delete_flat_corner, "degrees or more", _("Preserve shape when deleting flat nodes.\nInsert segments for sharp ones."), false);
1011
1012 _page_node.add_group_header(_("Delete Modes"));
1013
1014 auto prep_del_combo = [](std::string const &name, UI::Widget::PrefCombo &widget, UI::NodeDeleteMode initial) {
1015 Glib::ustring const labels[] = {
1016 _("Preserve curves only"),
1017 _("Preserve cusps only"),
1018 _("Preserve both"),
1019 _("Straight lines"),
1020 _("Remove nodes and leave a gap"),
1021 _("Remove lines and leave a gap")
1022 };
1023 int const values[] = {
1024 // See ui/tool/path-manipulator.h
1031 };
1032 widget.init("/tools/node/delete-mode-" + name, labels, values, (int)initial);
1033 };
1034
1035 prep_del_combo("default", _t_node_delete_mode, UI::NodeDeleteMode::automatic);
1036 prep_del_combo("ctrl", _t_node_delete_mode1, UI::NodeDeleteMode::line_segment);
1037 prep_del_combo("alt", _t_node_delete_mode2, UI::NodeDeleteMode::gap_nodes);
1038 prep_del_combo("shift", _t_node_delete_mode3, UI::NodeDeleteMode::curve_fit);
1039 prep_del_combo("cut", _t_node_cut_mode, UI::NodeDeleteMode::gap_lines);
1040
1041 _page_node.add_line( false, _("_Delete Modes:"), _t_node_delete_mode, "", _("What happens when nodes are deleted."), false);
1042 _page_node.add_line( false, "+ Ctrl", _t_node_delete_mode1, "", _("What happens when nodes are deleted while ctrl is held."), false);
1043 _page_node.add_line( false, "+ Alt", _t_node_delete_mode2, "", _("What happens when nodes are deleted while alt is held."), false);
1044 _page_node.add_line( false, "+ Shift", _t_node_delete_mode3, "", _("What happens when nodes are deleted while shift is held."), false);
1045 _page_node.add_line( false, _("Cut Mode:"), _t_node_cut_mode, "", _("What happens when nodes are cut."), false);
1046
1047 //Tweak
1048 this->AddNewObjectsStyle(_page_tweak, "/tools/tweak", _("Object paint style"));
1049 AddSelcueCheckbox(_page_tweak, "/tools/tweak", true);
1050 AddGradientCheckbox(_page_tweak, "/tools/tweak", false);
1051
1052 //Zoom
1053 AddSelcueCheckbox(_page_zoom, "/tools/zoom", true);
1054 AddGradientCheckbox(_page_zoom, "/tools/zoom", false);
1055
1056 //Measure
1057 auto const cb = Gtk::make_managed<PrefCheckButton>();
1058 cb->init ( _("Ignore first and last points"), "/tools/measure/ignore_1st_and_last", true);
1059 _page_measure.add_line( false, "", *cb, "", _("The start and end of the measurement tool's control line will not be considered for calculating lengths. Only lengths between actual curve intersections will be displayed."));
1060
1061 //Shapes
1062 this->AddSelcueCheckbox(_page_shapes, "/tools/shapes", true);
1063 this->AddGradientCheckbox(_page_shapes, "/tools/shapes", true);
1064
1065 //Rectangle
1066 this->AddNewObjectsStyle(_page_rectangle, "/tools/shapes/rect");
1067 this->AddConvertGuidesCheckbox(_page_rectangle, "/tools/shapes/rect", true);
1068
1069 //3D box
1070 this->AddNewObjectsStyle(_page_3dbox, "/tools/shapes/3dbox");
1071 this->AddConvertGuidesCheckbox(_page_3dbox, "/tools/shapes/3dbox", true);
1072
1073 //Ellipse
1074 this->AddNewObjectsStyle(_page_ellipse, "/tools/shapes/arc");
1075
1076 //Star
1077 this->AddNewObjectsStyle(_page_star, "/tools/shapes/star");
1078
1079 //Spiral
1080 this->AddNewObjectsStyle(_page_spiral, "/tools/shapes/spiral");
1081
1082 //Pencil
1083 this->AddSelcueCheckbox(_page_pencil, "/tools/freehand/pencil", true);
1084 this->AddNewObjectsStyle(_page_pencil, "/tools/freehand/pencil");
1085 this->AddDotSizeSpinbutton(_page_pencil, "/tools/freehand/pencil", 3.0);
1086 this->AddBaseSimplifySpinbutton(_page_pencil, "/tools/freehand/pencil", 25.0);
1087 _page_pencil.add_group_header( _("Sketch mode"));
1088 _page_pencil.add_line( true, "", _pencil_average_all_sketches, "",
1089 _("If on, the sketch result will be the normal average of all sketches made, instead of averaging the old result with the new sketch"));
1090
1091 //Pen
1092 this->AddSelcueCheckbox(_page_pen, "/tools/freehand/pen", true);
1093 this->AddNewObjectsStyle(_page_pen, "/tools/freehand/pen");
1094 this->AddDotSizeSpinbutton(_page_pen, "/tools/freehand/pen", 3.0);
1095
1096 //Calligraphy
1097 this->AddSelcueCheckbox(_page_calligraphy, "/tools/calligraphic", false);
1098 this->AddNewObjectsStyle(_page_calligraphy, "/tools/calligraphic");
1099 _page_calligraphy.add_line( false, "", _calligrapy_keep_selected, "",
1100 _("If on, each newly created object will be selected (deselecting previous selection)"));
1101
1102 //Text
1103 this->AddSelcueCheckbox(_page_text, "/tools/text", true);
1104 this->AddGradientCheckbox(_page_text, "/tools/text", true);
1105 {
1106 auto cb = Gtk::make_managed<PrefCheckButton>();
1107 cb->init ( _("Show font samples in the drop-down list"), "/tools/text/show_sample_in_list", true);
1108 _page_text.add_line( false, "", *cb, "", _("Show font samples alongside font names in the drop-down list in Text bar"));
1109
1110 _font_dialog.init(_("Show font substitution warning dialog"), "/options/font/substitutedlg", false);
1111 _page_text.add_line( false, "", _font_dialog, "", _("Show font substitution warning dialog when requested fonts are not available on the system"));
1112 _font_sample.init("/tools/text/font_sample", true);
1113 _page_text.add_line( false, _("Font sample"), _font_sample, "", _("Change font preview sample text"), true);
1114
1115 cb = Gtk::make_managed<PrefCheckButton>();
1116 cb->init ( _("Use SVG2 auto-flowed text"), "/tools/text/use_svg2", true);
1117 _page_text.add_line( false, "", *cb, "", _("Use SVG2 auto-flowed text instead of SVG1.2 auto-flowed text. (Recommended)"));
1118
1119 _recently_used_fonts_size.init("/tools/text/recently_used_fonts_size", 0, 100, 1, 10, 10, true, false);
1120 _page_text.add_line( false, _("Fonts in 'Recently used' collection:"), _recently_used_fonts_size, "",
1121 _("Maximum number of fonts in the 'Recently used' font collection"), false);
1122 _recently_used_fonts_size.changed_signal.connect([](double new_size) {
1124 recently_used_fonts->change_max_list_size(new_size); });
1125 }
1126
1127 //_page_text.add_group_header( _("Text units"));
1128 //_font_output_px.init ( _("Always output text size in pixels (px)"), "/options/font/textOutputPx", true);
1129 //_page_text.add_line( true, "", _font_output_px, "", _("Always convert the text size units above into pixels (px) before saving to file"));
1130
1131 _page_text.add_group_header( _("Font directories"));
1132 _font_fontsdir_system.init( _("Use Inkscape's fonts directory"), "/options/font/use_fontsdir_system", true);
1133 _page_text.add_line( true, "", _font_fontsdir_system, "", _("Load additional fonts from \"fonts\" directory located in Inkscape's global \"share\" directory"));
1134 _font_fontsdir_user.init( _("Use user's fonts directory"), "/options/font/use_fontsdir_user", true);
1135 _page_text.add_line( true, "", _font_fontsdir_user, "", _("Load additional fonts from \"fonts\" directory located in Inkscape's user configuration directory"));
1136 _font_fontdirs_custom.init("/options/font/custom_fontdirs", 50);
1137 _page_text.add_line(true, _("Additional font directories"), _font_fontdirs_custom, "", _("Load additional fonts from custom locations (one path per line)"), true);
1138
1139
1140 this->AddNewObjectsStyle(_page_text, "/tools/text");
1141
1142 //Spray
1143 AddSelcueCheckbox(_page_spray, "/tools/spray", true);
1144 AddGradientCheckbox(_page_spray, "/tools/spray", false);
1145
1146 //Eraser
1147 this->AddNewObjectsStyle(_page_eraser, "/tools/eraser");
1148
1149 //Paint Bucket
1150 this->AddSelcueCheckbox(_page_paintbucket, "/tools/paintbucket", false);
1151 this->AddNewObjectsStyle(_page_paintbucket, "/tools/paintbucket");
1152
1153 //Gradient
1154 this->AddSelcueCheckbox(_page_gradient, "/tools/gradient", true);
1155 _misc_forkvectors.init( _("Prevent sharing of gradient definitions"), "/options/forkgradientvectors/value", true);
1156 _page_gradient.add_line( false, "", _misc_forkvectors, "",
1157 _("When on, shared gradient definitions are automatically forked on change; uncheck to allow sharing of gradient definitions so that editing one object may affect other objects using the same gradient"), true);
1158
1159 _misc_gradientangle.init("/dialogs/gradienteditor/angle", -359, 359, 1, 90, 0, false, false);
1160 _page_gradient.add_line( false, _("Linear gradient _angle:"), _misc_gradientangle, "",
1161 _("Default angle of new linear gradients in degrees (clockwise from horizontal)"), false);
1162
1163 _misc_gradient_collect.init(_("Auto-delete unused gradients"), "/option/gradient/auto_collect", true);
1164 _page_gradient.add_line(
1165 false, "", _misc_gradient_collect, "",
1166 _("When enabled, gradients that are not used will be deleted (auto-collected) automatically "
1167 "from the SVG file. When disabled, unused gradients will be preserved in "
1168 "the file for later use. (Note: This setting only affects new gradients.)"),
1169 true);
1170
1171 //Dropper
1172 this->AddSelcueCheckbox(_page_dropper, "/tools/dropper", true);
1173 this->AddGradientCheckbox(_page_dropper, "/tools/dropper", true);
1174
1175 //Connector
1176 this->AddSelcueCheckbox(_page_connector, "/tools/connector", true);
1177 _page_connector.add_line(false, "", _connector_ignore_text, "",
1178 _("If on, connector attachment points will not be shown for text objects"));
1179
1180#ifdef WITH_LPETOOL
1181 //LPETool
1182 //disabled, because the LPETool is not finished yet.
1183 this->AddNewObjectsStyle(_page_lpetool, "/tools/lpetool");
1184#endif // WITH_LPETOOL
1185}
1186
1187void InkscapePreferences::get_highlight_colors(guint32 &colorsetbase, guint32 &colorsetsuccess,
1188 guint32 &colorsetwarning, guint32 &colorseterror)
1189{
1190 using namespace Inkscape::IO::Resource;
1192 Glib::ustring themeiconname = prefs->getString("/theme/iconTheme", prefs->getString("/theme/defaultIconTheme", ""));
1193 Glib::ustring prefix = "";
1194 if (prefs->getBool("/theme/darkTheme", false)) {
1195 prefix = ".dark ";
1196 }
1197 auto const higlight = get_filename(ICONS, (themeiconname + "/highlights.css").c_str(), false, true);
1198 if (!higlight.empty()) {
1199 std::ifstream ifs(higlight);
1200 std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
1201 Glib::ustring result;
1202 size_t startpos = content.find(prefix + ".base");
1203 size_t endpos = content.find("}");
1204 if (startpos != std::string::npos) {
1205 result = content.substr(startpos, endpos - startpos);
1206 size_t startposin = result.find("fill:");
1207 size_t endposin = result.find(";");
1208 result = result.substr(startposin + 5, endposin - (startposin + 5));
1210 colorsetbase = to_guint32(Gdk::RGBA(result));
1211 }
1212 content.erase(0, endpos + 1);
1213 startpos = content.find(prefix + ".success");
1214 endpos = content.find("}");
1215 if (startpos != std::string::npos) {
1216 result = content.substr(startpos, endpos - startpos);
1217 size_t startposin = result.find("fill:");
1218 size_t endposin = result.find(";");
1219 result = result.substr(startposin + 5, endposin - (startposin + 5));
1221 colorsetsuccess = to_guint32(Gdk::RGBA(result));
1222 }
1223 content.erase(0, endpos + 1);
1224 startpos = content.find(prefix + ".warning");
1225 endpos = content.find("}");
1226 if (startpos != std::string::npos) {
1227 result = content.substr(startpos, endpos - startpos);
1228 size_t startposin = result.find("fill:");
1229 size_t endposin = result.find(";");
1230 result = result.substr(startposin + 5, endposin - (startposin + 5));
1232 colorsetwarning = to_guint32(Gdk::RGBA(result));
1233 }
1234 content.erase(0, endpos + 1);
1235 startpos = content.find(prefix + ".error");
1236 endpos = content.find("}");
1237 if (startpos != std::string::npos) {
1238 result = content.substr(startpos, endpos - startpos);
1239 size_t startposin = result.find("fill:");
1240 size_t endposin = result.find(";");
1241 result = result.substr(startposin + 5, endposin - (startposin + 5));
1243 colorseterror = to_guint32(Gdk::RGBA(result));
1244 }
1245 }
1246}
1247
1248void InkscapePreferences::resetIconsColors(bool themechange)
1249{
1251
1252 Glib::ustring themeiconname = prefs->getString("/theme/iconTheme", prefs->getString("/theme/defaultIconTheme", ""));
1253
1254 if (!prefs->getBool("/theme/symbolicIcons", false)) {
1255 _symbolic_base_colors.set_sensitive(false);
1256 _symbolic_highlight_colors.set_sensitive(false);
1257 _symbolic_base_color.set_sensitive(false);
1258 _symbolic_success_color.set_sensitive(false);
1259 _symbolic_warning_color.set_sensitive(false);
1260 _symbolic_error_color.set_sensitive(false);
1261 return;
1262 }
1263
1264 auto doChangeIconsColors = false;
1265
1266 if (prefs->getBool("/theme/symbolicDefaultBaseColors", true) ||
1267 !prefs->getEntry("/theme/" + themeiconname + "/symbolicBaseColor").isValidUInt()) {
1268 auto const display = Gdk::Display::get_default();
1269 if (INKSCAPE.themecontext->getColorizeProvider()) {
1270 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getColorizeProvider());
1271 }
1272 auto base_color = _symbolic_base_color.get_color();
1273 // This is a hack to fix a problematic style which isn't updated fast enough on
1274 // change from dark to bright themes
1275 if (themechange) {
1276 base_color = to_rgba(_symbolic_base_color.get_current_color().toRGBA());
1277 }
1278 // This colors are set on style.css of inkscape, we copy highlight to not use
1279 guint32 colorsetbase = to_guint32(base_color);
1280 guint32 colorsetsuccess = colorsetbase;
1281 guint32 colorsetwarning = colorsetbase;
1282 guint32 colorseterror = colorsetbase;
1283 get_highlight_colors(colorsetbase, colorsetsuccess, colorsetwarning, colorseterror);
1284 _symbolic_base_color.setColor(Colors::Color(colorsetbase));
1285 prefs->setUInt("/theme/" + themeiconname + "/symbolicBaseColor", colorsetbase);
1286 _symbolic_base_color.set_sensitive(false);
1287 doChangeIconsColors = true;
1288 } else {
1289 _symbolic_base_color.set_sensitive(true);
1290 }
1291
1292 if (prefs->getBool("/theme/symbolicDefaultHighColors", true)) {
1293 auto const display = Gdk::Display::get_default();
1294 if (INKSCAPE.themecontext->getColorizeProvider()) {
1295 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getColorizeProvider());
1296 }
1297 auto const success_color = _symbolic_success_color.get_color();
1298 auto const warning_color = _symbolic_warning_color.get_color();
1299 auto const error_color = _symbolic_error_color .get_color();
1300 //we copy base to not use
1301 guint32 colorsetbase = to_guint32(success_color);
1302 guint32 colorsetsuccess = to_guint32(success_color);
1303 guint32 colorsetwarning = to_guint32(warning_color);
1304 guint32 colorseterror = to_guint32(error_color);
1305 get_highlight_colors(colorsetbase, colorsetsuccess, colorsetwarning, colorseterror);
1306 _symbolic_success_color.setColor(Colors::Color(colorsetsuccess));
1307 _symbolic_warning_color.setColor(Colors::Color(colorsetwarning));
1308 _symbolic_error_color.setColor(Colors::Color(colorseterror));
1309 prefs->setUInt("/theme/" + themeiconname + "/symbolicSuccessColor", colorsetsuccess);
1310 prefs->setUInt("/theme/" + themeiconname + "/symbolicWarningColor", colorsetwarning);
1311 prefs->setUInt("/theme/" + themeiconname + "/symbolicErrorColor", colorseterror);
1312 _symbolic_success_color.set_sensitive(false);
1313 _symbolic_warning_color.set_sensitive(false);
1314 _symbolic_error_color.set_sensitive(false);
1315 doChangeIconsColors = true;
1316 } else {
1317 _symbolic_success_color.set_sensitive(true);
1318 _symbolic_warning_color.set_sensitive(true);
1319 _symbolic_error_color.set_sensitive(true);
1320 }
1321
1322 if (doChangeIconsColors) {
1323 changeIconsColors();
1324 }
1325}
1326
1327void InkscapePreferences::resetIconsColorsWrapper() { resetIconsColors(false); }
1328
1329void InkscapePreferences::changeIconsColors()
1330{
1332 Glib::ustring themeiconname = prefs->getString("/theme/iconTheme", prefs->getString("/theme/defaultIconTheme", ""));
1333 guint32 colorsetbase = prefs->getUInt("/theme/" + themeiconname + "/symbolicBaseColor", 0x2E3436ff);
1334 guint32 colorsetsuccess = prefs->getUInt("/theme/" + themeiconname + "/symbolicSuccessColor", 0x4AD589ff);
1335 guint32 colorsetwarning = prefs->getUInt("/theme/" + themeiconname + "/symbolicWarningColor", 0xF57900ff);
1336 guint32 colorseterror = prefs->getUInt("/theme/" + themeiconname + "/symbolicErrorColor", 0xCC0000ff);
1337 _symbolic_base_color.setColor(Colors::Color(colorsetbase));
1338 _symbolic_success_color.setColor(Colors::Color(colorsetsuccess));
1339 _symbolic_warning_color.setColor(Colors::Color(colorsetwarning));
1340 _symbolic_error_color.setColor(Colors::Color(colorseterror));
1341
1342 auto const &colorize_provider = INKSCAPE.themecontext->getColorizeProvider();
1343 if (!colorize_provider) return;
1344
1345 auto const display = Gdk::Display::get_default();
1346 Gtk::StyleProvider::remove_provider_for_display(display, colorize_provider);
1347
1348 Glib::ustring css_str = "";
1349 if (prefs->getBool("/theme/symbolicIcons", false)) {
1350 css_str = INKSCAPE.themecontext->get_symbolic_colors();
1351 }
1352
1353 {
1354 auto const on_error = [&](auto const &section, auto const &error)
1355 {
1356 g_critical("CSSProviderError::load_from_data(): failed to load '%s'\n(%s)",
1357 css_str.c_str(), error.what());
1358 };
1359 sigc::scoped_connection _ = colorize_provider->signal_parsing_error().connect(on_error);
1360 colorize_provider->load_from_data(css_str);
1361 }
1362
1363 Gtk::StyleProvider::add_provider_for_display(display, colorize_provider,
1364 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1365}
1366
1367void InkscapePreferences::toggleSymbolic()
1368{
1370 Gtk::Window *window = SP_ACTIVE_DESKTOP->getInkscapeWindow();
1371 if (prefs->getBool("/theme/symbolicIcons", false)) {
1372 if (window ) {
1373 window->add_css_class("symbolic");
1374 window->remove_css_class("regular");
1375 }
1376 _symbolic_base_colors.set_sensitive(true);
1377 _symbolic_highlight_colors.set_sensitive(true);
1378 Glib::ustring themeiconname = prefs->getString("/theme/iconTheme", prefs->getString("/theme/defaultIconTheme", ""));
1379 if (prefs->getBool("/theme/symbolicDefaultColors", true) ||
1380 !prefs->getEntry("/theme/" + themeiconname + "/symbolicBaseColor").isValidUInt()) {
1381 resetIconsColors();
1382 } else {
1383 changeIconsColors();
1384 }
1385 } else {
1386 if (window) {
1387 window->add_css_class("regular");
1388 window->remove_css_class("symbolic");
1389 }
1390 auto const display = Gdk::Display::get_default();
1391 if (INKSCAPE.themecontext->getColorizeProvider()) {
1392 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getColorizeProvider());
1393 }
1394 _symbolic_base_colors.set_sensitive(false);
1395 _symbolic_highlight_colors.set_sensitive(false);
1396 }
1397 INKSCAPE.themecontext->getChangeThemeSignal().emit();
1398 INKSCAPE.themecontext->add_gtk_css(true);
1399}
1400
1401void InkscapePreferences::comboThemeChange()
1402{
1403 //we reset theming on combo change
1404 _dark_theme.set_active(false);
1405 _symbolic_base_colors.set_active(true);
1406 if (_contrast_theme.getSpinButton()->get_value() != 10.0){
1407 _contrast_theme.getSpinButton()->set_value(10.0);
1408 } else {
1409 themeChange();
1410 }
1411}
1412void InkscapePreferences::contrastThemeChange()
1413{
1414 //we reset theming on combo change
1415 themeChange(true);
1416}
1417
1418void InkscapePreferences::themeChange(bool contrastslider)
1419{
1420 Gtk::Window *window = SP_ACTIVE_DESKTOP->getInkscapeWindow();
1421
1422 if (window) {
1423 auto const display = Gdk::Display::get_default();
1424
1425 if (INKSCAPE.themecontext->getContrastThemeProvider()) {
1426 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getContrastThemeProvider());
1427 }
1428
1429 if (INKSCAPE.themecontext->getThemeProvider() ) {
1430 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getThemeProvider() );
1431 }
1432
1434 Glib::ustring current_theme = prefs->getString("/theme/gtkTheme", prefs->getString("/theme/defaultGtkTheme", ""));
1435
1436 _dark_theme.get_parent()->set_visible(dark_themes[current_theme]);
1437
1438 auto settings = Gtk::Settings::get_default();
1439 settings->property_gtk_theme_name() = current_theme;
1440
1441 auto const dark = INKSCAPE.themecontext->isCurrentThemeDark(window);
1442 bool toggled = prefs->getBool("/theme/darkTheme", false) != dark;
1443 prefs->setBool("/theme/darkTheme", dark);
1444
1445 INKSCAPE.themecontext->getChangeThemeSignal().emit();
1446 INKSCAPE.themecontext->add_gtk_css(true, contrastslider);
1447 resetIconsColors(toggled);
1448 }
1449}
1450
1451void InkscapePreferences::preferDarkThemeChange()
1452{
1453 Gtk::Window *window = SP_ACTIVE_DESKTOP->getInkscapeWindow();
1454 if (window) {
1456 auto const dark = INKSCAPE.themecontext->isCurrentThemeDark(window);
1457 bool toggled = prefs->getBool("/theme/darkTheme", false) != dark;
1458 prefs->setBool("/theme/darkTheme", dark);
1459 INKSCAPE.themecontext->getChangeThemeSignal().emit();
1460 INKSCAPE.themecontext->add_gtk_css(true);
1461 // we avoid switched base colors
1462 if (!_symbolic_base_colors.get_active()) {
1463 prefs->setBool("/theme/symbolicDefaultBaseColors", true);
1464 resetIconsColors(false);
1465 _symbolic_base_colors.set_sensitive(true);
1466 prefs->setBool("/theme/symbolicDefaultBaseColors", false);
1467 } else {
1468 resetIconsColors(toggled);
1469 }
1470 }
1471}
1472
1473void InkscapePreferences::symbolicThemeCheck()
1474{
1475 using namespace Inkscape::IO::Resource;
1477 Glib::ustring themeiconname = prefs->getString("/theme/iconTheme", prefs->getString("/theme/defaultIconTheme", ""));
1478 bool symbolic = false;
1479 auto settings = Gtk::Settings::get_default();
1480 if (settings) {
1481 if (themeiconname != "") {
1482 settings->property_gtk_icon_theme_name() = themeiconname;
1483 }
1484 }
1485 // we always show symbolic in default theme (relays in hicolor theme)
1486 if (themeiconname != prefs->getString("/theme/defaultIconTheme", "")) {
1487 for (auto &&folder : get_foldernames(ICONS, { "application" })) {
1488 auto path = folder;
1489 const size_t last_slash_idx = folder.find_last_of("\\/");
1490 if (std::string::npos != last_slash_idx) {
1491 folder.erase(0, last_slash_idx + 1);
1492 }
1493
1494 auto const folder_utf8 = Glib::filename_to_utf8(folder);
1495 if (folder_utf8 == themeiconname) {
1496#ifdef _WIN32
1497 path += g_win32_locale_filename_from_utf8("/symbolic/actions");
1498#else
1499 path += "/symbolic/actions";
1500#endif
1501
1502 symbolic = !get_filenames(path, {".svg"}, {}).empty();
1503 }
1504 }
1505 } else {
1506 symbolic = true;
1507 }
1508 if (_symbolic_icons.get_parent()) {
1509 if (!symbolic) {
1510 _symbolic_icons.set_active(false);
1511 _symbolic_icons.get_parent()->set_visible(false);
1512 _symbolic_base_colors.get_parent()->set_visible(false);
1513 _symbolic_highlight_colors.get_parent()->set_visible(false);
1514 _symbolic_base_color.get_parent()->get_parent()->set_visible(false);
1515 _symbolic_success_color.get_parent()->get_parent()->set_visible(false);
1516 } else {
1517 _symbolic_icons.get_parent()->set_visible(true);
1518 _symbolic_base_colors.get_parent()->set_visible(true);
1519 _symbolic_highlight_colors.get_parent()->set_visible(true);
1520 _symbolic_base_color.get_parent()->get_parent()->set_visible(true);
1521 _symbolic_success_color.get_parent()->get_parent()->set_visible(true);
1522 }
1523 }
1524 if (symbolic) {
1525 if (prefs->getBool("/theme/symbolicDefaultHighColors", true) ||
1526 prefs->getBool("/theme/symbolicDefaultBaseColors", true) ||
1527 !prefs->getEntry("/theme/" + themeiconname + "/symbolicBaseColor").isValidUInt()) {
1528 resetIconsColors();
1529 } else {
1530 changeIconsColors();
1531 }
1532 auto colorsetbase = prefs->getColor("/theme/" + themeiconname + "/symbolicBaseColor", "#2E3436ff");
1533 auto colorsetsuccess = prefs->getColor("/theme/" + themeiconname + "/symbolicSuccessColor", "#4AD589ff");
1534 auto colorsetwarning = prefs->getColor("/theme/" + themeiconname + "/symbolicWarningColor", "#F57900ff");
1535 auto colorseterror = prefs->getColor("/theme/" + themeiconname + "/symbolicErrorColor", "#CC0000ff");
1536 _symbolic_base_color.init(_("Color for symbolic icons:"), "/theme/" + themeiconname + "/symbolicBaseColor",
1537 colorsetbase.toString());
1538 _symbolic_success_color.init(_("Color for symbolic success icons:"),
1539 "/theme/" + themeiconname + "/symbolicSuccessColor", colorsetsuccess.toString());
1540 _symbolic_warning_color.init(_("Color for symbolic warning icons:"),
1541 "/theme/" + themeiconname + "/symbolicWarningColor", colorsetwarning.toString());
1542 _symbolic_error_color.init(_("Color for symbolic error icons:"),
1543 "/theme/" + themeiconname + "/symbolicErrorColor", colorseterror.toString());
1544 }
1545}
1546
1547static Cairo::RefPtr<Cairo::Surface> draw_color_preview(unsigned int rgb, unsigned int frame_rgb, int device_scale) {
1549 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, size * device_scale, size * device_scale);
1550 cairo_surface_set_device_scale(surface->cobj(), device_scale, device_scale);
1551 auto ctx = Cairo::Context::create(surface);
1552 ctx->arc(size / 2, size / 2, size / 2, 0.0, 2 * M_PI);
1553 ctx->set_source_rgb((frame_rgb >> 16 & 0xff) / 255.0, (frame_rgb >> 8 & 0xff) / 255.0, (frame_rgb & 0xff) / 255.0);
1554 ctx->fill();
1555 size -= 2;
1556 ctx->set_matrix(Cairo::translation_matrix(1, 1));
1557 ctx->arc(size / 2, size / 2, size / 2, 0.0, 2 * M_PI);
1558 ctx->set_source_rgb((rgb >> 16 & 0xff) / 255.0, (rgb >> 8 & 0xff) / 255.0, (rgb & 0xff) / 255.0);
1559 ctx->fill();
1560 return surface;
1561}
1562
1563// create a preview of few selected handles
1564void InkscapePreferences::initPageUI()
1565{
1566 Gtk::TreeModel::iterator iter_ui = this->AddPage(_page_ui, _("Interface"), PREFS_PAGE_UI);
1567
1568 Glib::ustring languages[] = {_("System default"),
1569 _("Albanian (sq)"), _("Arabic (ar)"), _("Armenian (hy)"), _("Assamese (as)"), _("Azerbaijani (az)"),
1570 _("Basque (eu)"), _("Belarusian (be)"), _("Bulgarian (bg)"), _("Bengali (bn)"), _("Bengali/Bangladesh (bn_BD)"), _("Bodo (brx)"), _("Breton (br)"),
1571 _("Catalan (ca)"), _("Valencian Catalan (ca@valencia)"), _("Chinese/China (zh_CN)"), _("Chinese/Taiwan (zh_TW)"), _("Croatian (hr)"), _("Czech (cs)"),
1572 _("Danish (da)"), _("Dogri (doi)"), _("Dutch (nl)"), _("Dzongkha (dz)"),
1573 _("German (de)"), _("Greek (el)"),
1574 _("English (en)"), _("English/Australia (en_AU)"), _("English/Canada (en_CA)"), _("English/Great Britain (en_GB)"), _("Esperanto (eo)"), _("Estonian (et)"),
1575 _("Farsi (fa)"), _("Finnish (fi)"), _("French (fr)"),
1576 _("Galician (gl)"), _("Gujarati (gu)"),
1577 _("Hebrew (he)"), _("Hindi (hi)"), _("Hungarian (hu)"),
1578 _("Icelandic (is)"), _("Indonesian (id)"), _("Irish (ga)"), _("Italian (it)"),
1579 _("Japanese (ja)"),
1580 _("Kannada (kn)"), _("Kashmiri in Perso-Arabic script (ks@aran)"), _("Kashmiri in Devanagari script (ks@deva)"), _("Khmer (km)"), _("Kinyarwanda (rw)"), _("Konkani (kok)"), _("Konkani in Latin script (kok@latin)"), _("Korean (ko)"),
1581 _("Latvian (lv)"), _("Lithuanian (lt)"),
1582 _("Macedonian (mk)"), _("Maithili (mai)"), _("Malayalam (ml)"), _("Manipuri (mni)"), _("Manipuri in Bengali script (mni@beng)"), _("Marathi (mr)"), _("Mongolian (mn)"),
1583 _("Nepali (ne)"), _("Norwegian Bokmål (nb)"), _("Norwegian Nynorsk (nn)"),
1584 _("Odia (or)"),
1585 _("Panjabi (pa)"), _("Polish (pl)"), _("Portuguese (pt)"), _("Portuguese/Brazil (pt_BR)"),
1586 _("Romanian (ro)"), _("Russian (ru)"),
1587 _("Sanskrit (sa)"), _("Santali (sat)"), _("Santali in Devanagari script (sat@deva)"), _("Serbian (sr)"), _("Serbian in Latin script (sr@latin)"),
1588 _("Sindhi (sd)"), _("Sindhi in Devanagari script (sd@deva)"), _("Slovak (sk)"), _("Slovenian (sl)"), _("Spanish (es)"), _("Spanish/Mexico (es_MX)"), _("Swedish (sv)"),
1589 _("Tamil (ta)"), _("Telugu (te)"), _("Thai (th)"), _("Turkish (tr)"),
1590 _("Ukrainian (uk)"), _("Urdu (ur)"),
1591 _("Vietnamese (vi)")};
1592 Glib::ustring langValues[] = {"",
1593 "sq", "ar", "hy", "as", "az",
1594 "eu", "be", "bg", "bn", "bn_BD", "brx", "br",
1595 "ca", "ca@valencia", "zh_CN", "zh_TW", "hr", "cs",
1596 "da", "doi", "nl", "dz",
1597 "de", "el",
1598 "en", "en_AU", "en_CA", "en_GB", "eo", "et",
1599 "fa", "fi", "fr",
1600 "gl", "gu",
1601 "he", "hi", "hu",
1602 "is", "id", "ga", "it",
1603 "ja",
1604 "kn", "ks@aran", "ks@deva", "km", "rw", "kok", "kok@latin", "ko",
1605 "lv", "lt",
1606 "mk", "mai", "ml", "mni", "mni@beng", "mr", "mn",
1607 "ne", "nb", "nn",
1608 "or",
1609 "pa", "pl", "pt", "pt_BR",
1610 "ro", "ru",
1611 "sa", "sat", "sat@deva", "sr", "sr@latin",
1612 "sd", "sd@deva", "sk", "sl", "es", "es_MX", "sv",
1613 "ta", "te", "th", "tr",
1614 "uk", "ur",
1615 "vi" };
1616
1617 {
1618 // sorting languages according to translated name
1619 int i = 0;
1620 int j = 0;
1621 int n = sizeof( languages ) / sizeof( Glib::ustring );
1622 Glib::ustring key_language;
1623 Glib::ustring key_langValue;
1624 for ( j = 1 ; j < n ; j++ ) {
1625 key_language = languages[j];
1626 key_langValue = langValues[j];
1627 i = j-1;
1628 while ( i >= 0
1629 && ( ( languages[i] > key_language
1630 && langValues[i] != "" )
1631 || key_langValue == "" ) )
1632 {
1633 languages[i+1] = languages[i];
1634 langValues[i+1] = langValues[i];
1635 i--;
1636 }
1637 languages[i+1] = key_language;
1638 langValues[i+1] = key_langValue;
1639 }
1640 }
1641
1642 _ui_languages.init( "/ui/language", languages, langValues, languages[0]);
1643 _ui_languages.enable_search();
1644 _page_ui.add_line( false, _("Language:"), _ui_languages, "",
1645 _("Set the language for menus and number formats"), false, reset_icon());
1646
1648
1649 _misc_recent.init("/options/maxrecentdocuments/value", 0.0, 1000.0, 1.0, 1.0, 1.0, true, false);
1650
1651 auto const reset_recent = Gtk::make_managed<Gtk::Button>(_("Clear list"));
1652 reset_recent->signal_clicked().connect(sigc::mem_fun(*this, &InkscapePreferences::on_reset_open_recent_clicked));
1653
1654 _page_ui.add_line( false, _("Maximum documents\n in Open _Recent:"), _misc_recent, "",
1655 _("Set the maximum length of the Open Recent list in the File menu, or clear the list"), false, reset_recent);
1656
1657 _page_ui.add_group_header(_("_Zoom correction factor (in %)"), 2);
1658 _page_ui.add_group_note(_("Adjust the slider until the length of the ruler on your screen matches its real length. This information is used when zooming to 1:1, 1:2, etc., to display objects in their true sizes"));
1659 _ui_zoom_correction.init(300, 30, 0.01, 500.0, 1.0, 10.0, 1.0);
1660 _page_ui.add_line( true, "", _ui_zoom_correction, "", "", true);
1661
1662 _ui_realworldzoom.init( _("Show zoom percentage corrected by factor"), "/options/zoomcorrection/shown", true);
1663 _page_ui.add_line( false, "", _ui_realworldzoom, "", _("Zoom percentage can be either by the physical units or by pixels."));
1664
1665 _ui_rotationlock.init(_("Lock canvas rotation by default"), "/options/rotationlock", false);
1666 _page_ui.add_line(false, "", _ui_rotationlock, "",
1667 _("Prevent accidental canvas rotation by disabling on-canvas keyboard and mouse actions for rotation"), true);
1668
1669 _ui_rulersel.init( _("Show selection in ruler"), "/options/ruler/show_bbox", true);
1670 _page_ui.add_line( false, "", _ui_rulersel, "", _("Shows a blue line in the ruler where the selection is."));
1671
1672 _page_ui.add_group_header(_("User Interface"), 2);
1673 // _page_ui.add_group_header(_("Handle size"));
1674 _mouse_grabsize.init("/options/grabsize/value", 1, 15, 1, 2, 3, 0);
1675 _page_ui.add_line(true, _("Handle size"), _mouse_grabsize, "", _("Set the relative size of node handles"), true);
1676 {
1677 auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
1678 auto img = Gtk::make_managed<Gtk::Picture>();
1679 auto scale = get_scale_factor();
1681 img->set_layout_manager(Gtk::BinLayout::create());
1682 img->set_size_request(surface->get_width() / scale, surface->get_height() / scale);
1683 img->set_paintable(to_texture(surface));
1684 img->set_hexpand();
1685 img->set_halign(Gtk::Align::CENTER);
1686 box->append(*img);
1687 auto cb = Gtk::make_managed<Inkscape::UI::Widget::IconComboBox>(false);
1688 cb->set_valign(Gtk::Align::CENTER);
1689 auto& mgr = Handles::Manager::get();
1690 int i = 0;
1691 for (auto theme : mgr.get_handle_themes()) {
1692 unsigned int frame = theme.positive ? 0x000000 : 0xffffff; // black or white
1693 cb->add_row(draw_color_preview(theme.rgb_accent_color, frame, get_scale_factor()), theme.title, i++);
1694 }
1695 cb->refilter();
1696 cb->set_active_by_id(mgr.get_selected_theme());
1697
1698 // Update on auto-reload or theme change
1699 mgr.connectCssUpdated(sigc::track_object(
1700 [=, this]() { img->set_paintable(to_texture(draw_handles_preview(get_scale_factor()))); }, *this));
1701 cb->signal_changed().connect([=, this](int id) {
1703 });
1704
1705 box->append(*cb);
1706 _handle_size = Preferences::PreferencesObserver::create("/options/grabsize/value", [img, this](const Preferences::Entry&){
1707 img->set_paintable(to_texture(draw_handles_preview(get_scale_factor())));
1708 });
1709 _page_ui.add_line(true, _("Handle colors"), *box, "", "Select handle color scheme.");
1710 }
1711 _narrow_spinbutton.init(_("Use narrow number entry boxes"), "/theme/narrowSpinButton", false);
1712 _page_ui.add_line(false, "", _narrow_spinbutton, "", _("Make number editing boxes smaller by limiting padding"), false);
1713
1714 _page_ui.add_group_header(_("Status bar"), 2);
1715 auto const sb_style = Gtk::make_managed<UI::Widget::PrefCheckButton>();
1716 sb_style->init(_("Show current style"), "/statusbar/visibility/style", true);
1717 _page_ui.add_line(false, "", *sb_style, "", _("Control visibility of current fill, stroke and opacity in status bar."), true);
1718 auto const sb_layer = Gtk::make_managed<UI::Widget::PrefCheckButton>();
1719 sb_layer->init(_("Show layer selector"), "/statusbar/visibility/layer", true);
1720 _page_ui.add_line(false, "", *sb_layer, "", _("Control visibility of layer selection menu in status bar."), true);
1721 auto const sb_coords = Gtk::make_managed<UI::Widget::PrefCheckButton>();
1722 sb_coords->init(_("Show mouse coordinates"), "/statusbar/visibility/coordinates", true);
1723 _page_ui.add_line(false, "", *sb_coords, "", _("Control visibility of mouse coordinates X & Y in status bar."), true);
1724 auto const sb_rotate = Gtk::make_managed<UI::Widget::PrefCheckButton>();
1725 sb_rotate->init(_("Show canvas rotation"), "/statusbar/visibility/rotation", true);
1726 _page_ui.add_line(false, "", *sb_rotate, "", _("Control visibility of canvas rotation in status bar."), true);
1727
1728 _page_ui.add_group_header(_("Mouse cursors"), 2);
1729 _ui_cursorscaling.init(_("Enable scaling"), "/options/cursorscaling", true);
1730 _page_ui.add_line(false, "", _ui_cursorscaling, "", _("When off, cursor scaling is disabled. Cursor scaling may be broken when fractional scaling is enabled."), true);
1731 _ui_cursor_shadow.init(_("Show drop shadow"), "/options/cursor-drop-shadow", true);
1732 _page_ui.add_line(false, "", _ui_cursor_shadow, "", _("Control visibility of drop shadow for Inkscape cursors."), true);
1733
1734 // Theme
1735 _page_theme.add_group_header(_("Theme"));
1736 _dark_theme.init(_("Use dark theme"), "/theme/preferDarkTheme", false);
1737 Glib::ustring current_theme = prefs->getString("/theme/gtkTheme", prefs->getString("/theme/defaultGtkTheme", ""));
1738 Glib::ustring default_theme = prefs->getString("/theme/defaultGtkTheme");
1739 Glib::ustring theme = "";
1740 {
1741 dark_themes = INKSCAPE.themecontext->get_available_themes();
1742 std::vector<Glib::ustring> labels;
1743 std::vector<Glib::ustring> values;
1744 for (auto const &[theme, dark] : dark_themes) {
1745 if (theme == default_theme) {
1746 continue;
1747 }
1748 values.emplace_back(theme);
1749 labels.emplace_back(theme);
1750 }
1751 std::sort(labels.begin(), labels.end());
1752 std::sort(values.begin(), values.end());
1753 labels.erase(unique(labels.begin(), labels.end()), labels.end());
1754 values.erase(unique(values.begin(), values.end()), values.end());
1755 values.emplace_back("");
1756 Glib::ustring default_theme_label = _("Use system theme");
1757 default_theme_label += " (" + default_theme + ")";
1758 labels.emplace_back(default_theme_label);
1759
1760 _gtk_theme.init("/theme/gtkTheme", labels, values, "");
1761 _page_theme.add_line(false, _("Change GTK theme:"), _gtk_theme, "", "", false);
1762 _gtk_theme.signal_changed().connect(sigc::mem_fun(*this, &InkscapePreferences::comboThemeChange));
1763 }
1764
1765 _sys_user_themes_dir_copy.init(g_build_filename(g_get_user_data_dir(), "themes", nullptr), _("Open themes folder"));
1766 _page_theme.add_line(true, _("User themes:"), _sys_user_themes_dir_copy, "", _("Location of the user’s themes"), true, Gtk::make_managed<Gtk::Box>());
1767 _contrast_theme.init("/theme/contrast", 1, 10, 1, 2, 10, 1);
1768
1769 _page_theme.add_line(true, "", _dark_theme, "", _("Use dark theme"), true);
1770 {
1771 auto const font_scale = Gtk::make_managed<UI::Widget::PrefSlider>();
1772 font_scale->init(ThemeContext::get_font_scale_pref_path(), 50, 150, 5, 5, 100, 0); // 50% to 150%
1773 font_scale->getSlider()->set_format_value_func([=](double const val) {
1774 return Inkscape::ustring::format_classic(std::fixed, std::setprecision(0), val) + "%";
1775 });
1776 // Live updates commented out; too disruptive
1777 // font_scale->getSlider()->signal_value_changed().connect([=](){
1778 // INKSCAPE.themecontext->adjust_global_font_scale(font_scale->getSlider()->get_value() / 100.0);
1779 // });
1780 auto const space = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
1781 space->set_valign(Gtk::Align::CENTER);
1782 auto const reset = Gtk::make_managed<Gtk::Button>();
1783 reset->set_tooltip_text(_("Reset font size to 100%"));
1784 reset->set_image_from_icon_name("reset-settings-symbolic");
1785 reset->set_size_request(30, -1);
1786 auto const apply = Gtk::make_managed<Gtk::Button>(_("Apply"));
1787 apply->set_tooltip_text(_("Apply font size changes to the UI"));
1788 apply->set_valign(Gtk::Align::FILL);
1789 apply->set_margin_end(5);
1790 reset->set_valign(Gtk::Align::FILL);
1791 space->append(*apply);
1792 space->append(*reset);
1793 reset->signal_clicked().connect([=](){
1794 font_scale->getSlider()->set_value(100);
1795 INKSCAPE.themecontext->adjustGlobalFontScale(1.0);
1796 });
1797 apply->signal_clicked().connect([=](){
1798 INKSCAPE.themecontext->adjustGlobalFontScale(font_scale->getSlider()->get_value() / 100.0);
1799 });
1800 _page_theme.add_line(false, _("_Font scale:"), *font_scale, "", _("Adjust size of UI fonts"), true, space);
1801 }
1802 auto const space = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
1803 space->set_size_request(_sb_width / 3, -1);
1804 _page_theme.add_line(false, _("_Contrast:"), _contrast_theme, "",
1805 _("Make background brighter or darker to adjust contrast"), true, space);
1806 _contrast_theme.getSlider()->signal_value_changed().connect(sigc::mem_fun(*this, &InkscapePreferences::contrastThemeChange));
1807
1808 _dark_theme.get_parent()->set_visible(dark_themes[current_theme]);
1809
1810 _dark_theme.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::preferDarkThemeChange));
1811
1812 // Icons
1813 _page_theme.add_group_header(_("Icons"));
1814 {
1815 using namespace Inkscape::IO::Resource;
1816 std::vector<Glib::ustring> labels;
1817 std::vector<Glib::ustring> values;
1818 Glib::ustring default_icon_theme = prefs->getString("/theme/defaultIconTheme");
1819 for (auto &&folder : get_foldernames(ICONS, { "application" })) {
1820 // from https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path#8520871
1821 // Maybe we can link boost path utilities
1822 // Remove directory if present.
1823 // Do this before extension removal in case the directory has a period character.
1824 const size_t last_slash_idx = folder.find_last_of("\\/");
1825 if (std::string::npos != last_slash_idx) {
1826 folder.erase(0, last_slash_idx + 1);
1827 }
1828
1829 // we want use Adwaita instead fallback hicolor theme
1830 auto const folder_utf8 = Glib::filename_to_utf8(folder);
1831 if (folder_utf8 == default_icon_theme) {
1832 continue;
1833 }
1834
1835 labels.emplace_back( folder) ;
1836 values.emplace_back(std::move(folder));
1837 }
1838 std::sort(labels.begin(), labels.end());
1839 std::sort(values.begin(), values.end());
1840 labels.erase(unique(labels.begin(), labels.end()), labels.end());
1841 values.erase(unique(values.begin(), values.end()), values.end());
1842 values.emplace_back("");
1843 Glib::ustring default_icon_label = _("Use system icons");
1844 default_icon_label += " (" + default_icon_theme + ")";
1845 labels.emplace_back(default_icon_label);
1846
1847 _icon_theme.init("/theme/iconTheme", labels, values, "");
1848 _page_theme.add_line(false, _("Change icon theme:"), _icon_theme, "", "", false);
1849 _icon_theme.signal_changed().connect(sigc::mem_fun(*this, &InkscapePreferences::symbolicThemeCheck));
1850 _sys_user_icons_dir_copy.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::ICONS, ""),
1851 _("Open icons folder"));
1852 _page_theme.add_line(true, _("User icons: "), _sys_user_icons_dir_copy, "", _("Location of the user’s icons"), true, Gtk::make_managed<Gtk::Box>());
1853 }
1854 Glib::ustring themeiconname = prefs->getString("/theme/iconTheme", prefs->getString("/theme/defaultIconTheme", ""));
1855 _symbolic_icons.init(_("Use symbolic icons"), "/theme/symbolicIcons", false);
1856 _symbolic_icons.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::toggleSymbolic));
1857 _page_theme.add_line(true, "", _symbolic_icons, "", "", true);
1858 _symbolic_base_colors.init(_("Use default base color for icons"), "/theme/symbolicDefaultBaseColors", true);
1859 _symbolic_base_colors.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::resetIconsColorsWrapper));
1860 _page_theme.add_line(true, "", _symbolic_base_colors, "", "", true);
1861 _symbolic_highlight_colors.init(_("Use default highlight colors for icons"), "/theme/symbolicDefaultHighColors", true);
1862 _symbolic_highlight_colors.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::resetIconsColorsWrapper));
1863 _page_theme.add_line(true, "", _symbolic_highlight_colors, "", "", true);
1864 _symbolic_base_color.init(_("Color for symbolic icons:"), "/theme/" + themeiconname + "/symbolicBaseColor",
1865 "#2E3436ff");
1866 _symbolic_success_color.init(_("Color for symbolic success icons:"),
1867 "/theme/" + themeiconname + "/symbolicSuccessColor", "#4AD589ff");
1868 _symbolic_warning_color.init(_("Color for symbolic warning icons:"),
1869 "/theme/" + themeiconname + "/symbolicWarningColor", "#F57900ff");
1870 _symbolic_error_color.init(_("Color for symbolic error icons:"), "/theme/" + themeiconname + "/symbolicErrorColor",
1871 "#CC0000ff");
1872 _symbolic_base_color.add_css_class("system_base_color");
1873 _symbolic_success_color.add_css_class("system_success_color");
1874 _symbolic_warning_color.add_css_class("system_warning_color");
1875 _symbolic_error_color.add_css_class("system_error_color");
1876 _symbolic_base_color.add_css_class("symboliccolors");
1877 _symbolic_success_color.add_css_class("symboliccolors");
1878 _symbolic_warning_color.add_css_class("symboliccolors");
1879 _symbolic_error_color.add_css_class("symboliccolors");
1880 auto const changeIconsColor = sigc::hide(sigc::mem_fun(*this, &InkscapePreferences::changeIconsColors));
1881 _symbolic_base_color.connectChanged (changeIconsColor);
1882 _symbolic_warning_color.connectChanged(changeIconsColor);
1883 _symbolic_success_color.connectChanged(changeIconsColor);
1884 _symbolic_error_color.connectChanged (changeIconsColor);
1885 auto const icon_buttons = Gtk::make_managed<Gtk::Box>();
1886 UI::pack_start(*icon_buttons, _symbolic_base_color, true, true, 4);
1887 _page_theme.add_line(false, "", *icon_buttons, _("Icon color base"),
1888 _("Base color for icons"), false);
1889 auto const icon_buttons_hight = Gtk::make_managed<Gtk::Box>();
1890 UI::pack_start(*icon_buttons_hight, _symbolic_success_color, true, true, 4);
1891 UI::pack_start(*icon_buttons_hight, _symbolic_warning_color, true, true, 4);
1892 UI::pack_start(*icon_buttons_hight, _symbolic_error_color, true, true, 4);
1893 _page_theme.add_line(false, "", *icon_buttons_hight, _("Icon color highlights"),
1894 _("Highlight colors supported by some symbolic icon themes"),
1895 false);
1896 auto const icon_buttons_def = Gtk::make_managed<Gtk::Box>();
1897 resetIconsColors();
1898 changeIconsColors();
1899 _page_theme.add_line(false, "", *icon_buttons_def, "",
1900 _("Reset theme colors for some symbolic icon themes"),
1901 false);
1902 Glib::ustring menu_icons_labels[] = {_("Yes"), _("No"), _("Theme decides")};
1903 int menu_icons_values[] = {1, -1, 0};
1904 _menu_icons.init("/theme/menuIcons", menu_icons_labels, menu_icons_values, 0);
1905 _page_theme.add_line(false, _("Show icons in menus:"), _menu_icons, "",
1906 _("You can either enable or disable all icons in menus. By default, the setting for the 'use-icon' attribute in the 'menus.ui' file determines whether to display icons in menus."), false, reset_icon());
1907 _shift_icons.init(_("Shift icons in menus"), "/theme/shiftIcons", true);
1908 _page_theme.add_line(true, "", _shift_icons, "",
1909 _("This preference fixes icon positions in menus."), false, reset_icon());
1910
1911 _page_theme.add_group_header(_("XML Editor"));
1912#if WITH_GSOURCEVIEW
1913 {
1914 auto manager = gtk_source_style_scheme_manager_get_default();
1915 auto ids = gtk_source_style_scheme_manager_get_scheme_ids(manager);
1916
1917 auto const syntax = Gtk::make_managed<UI::Widget::PrefCombo>();
1918 std::vector<Glib::ustring> labels;
1919 std::vector<Glib::ustring> values;
1920 for (const char* style = *ids; style; style = *++ids) {
1921 if (auto scheme = gtk_source_style_scheme_manager_get_scheme(manager, style)) {
1922 auto name = gtk_source_style_scheme_get_name(scheme);
1923 labels.emplace_back(name);
1924 }
1925 else {
1926 labels.emplace_back(style);
1927 }
1928 values.emplace_back(style);
1929 }
1930 syntax->init("/theme/syntax-color-theme", labels, values, "");
1931 _page_theme.add_line(false, _("Color theme:"), *syntax, "", _("Syntax coloring for XML Editor"), false);
1932 }
1933#endif
1934 {
1935 auto const font_button = Gtk::make_managed<Gtk::Button>("...");
1936 font_button->set_halign(Gtk::Align::START);
1937 auto const font_box = Gtk::make_managed<Gtk::Entry>();
1938 font_box->set_editable(false);
1939 font_box->set_sensitive(false);
1940 auto theme = INKSCAPE.themecontext;
1941 font_box->set_text(theme->getMonospacedFont().to_string());
1942 font_button->signal_clicked().connect([=, this]{
1943 auto dlg = std::make_unique<Gtk::FontChooserDialog>();
1944 // show fixed-size fonts only
1945 dlg->set_filter_func([](const Glib::RefPtr<const Pango::FontFamily>& family, const Glib::RefPtr<const Pango::FontFace>& face) {
1946 return family && family->is_monospace();
1947 });
1948 dlg->set_font_desc(theme->getMonospacedFont());
1949 dlg->signal_response().connect([=, d = dlg.get()] (int response) {
1950 if (response == Gtk::ResponseType::OK) {
1951 auto desc = d->get_font_desc();
1952 theme->saveMonospacedFont(desc);
1953 theme->adjustGlobalFontScale(theme->getFontScale() / 100);
1954 font_box->set_text(desc.to_string());
1955 }
1956 });
1957 dialog_show_modal_and_selfdestruct(std::move(dlg), get_root());
1958 });
1959 _page_theme.add_line(false, _("Monospaced font:"), *font_box, "", _("Select fixed-width font"), true, font_button);
1960
1961 auto const mono_font = Gtk::make_managed<UI::Widget::PrefCheckButton>();
1962 mono_font->init( _("Use monospaced font"), "/dialogs/xml/mono-font", false);
1963 _page_theme.add_line(false, _("XML tree:"), *mono_font, "", _("Use fixed-width font in XML Editor"), false);
1964 }
1965
1966 //=======================================================================================================
1967
1968 this->AddPage(_page_theme, _("Theming"), iter_ui, PREFS_PAGE_UI_THEME);
1969 symbolicThemeCheck();
1970
1971 // Toolbars
1972 _page_toolbars.add_group_header(_("Toolbars"));
1973 try {
1974 auto builder = Inkscape::UI::create_builder("toolbar-tool-prefs.ui");
1975 auto &toolbox = get_widget<Gtk::Box>(builder, "tool-toolbar-prefs");
1976
1977 for_each_descendant(toolbox, [=](Gtk::Widget &widget) {
1978 if (auto const button = dynamic_cast<Gtk::ToggleButton *>(&widget)) {
1979 // do not execute any action:
1980 button->set_action_name("");
1981
1982 button->set_sensitive();
1983 auto action_name = sp_get_action_target(button);
1985 auto visible = Inkscape::Preferences::get()->getBool(path, true);
1986 button->set_active(visible);
1987 button->signal_clicked().connect([=](){
1988 bool new_state = !button->get_active();
1989 button->set_active(new_state);
1990 Inkscape::Preferences::get()->setBool(path, button->get_active());
1991 });
1992 auto *iapp = InkscapeApplication::instance();
1993 if (iapp) {
1994 auto tooltip =
1995 iapp->get_action_extra_data().get_tooltip_for_action(get_tool_action(action_name), true, true);
1996 button->set_tooltip_markup(tooltip);
1997 }
1998 }
2000 });
2001
2002 _page_toolbars.add_line(false, "", toolbox, "", _("Select visible tool buttons"), true);
2003
2004 struct tbar_info {const char* label; const char* prefs;} toolbars[] = {
2005 {_("Toolbox icon size:"), Inkscape::UI::Toolbar::tools_icon_size},
2006 {_("Control bar icon size:"), Inkscape::UI::Toolbar::ctrlbars_icon_size},
2007 };
2010 auto const format_value = [](int const val) -> Glib::ustring
2011 { return std::to_string(100 * val / min) += '%'; };
2012 for (auto&& tbox : toolbars) {
2013 auto const slider = Gtk::make_managed<UI::Widget::PrefSlider>(false);
2014 slider->init(tbox.prefs, min, max, 1, 4, min, 0);
2015 slider->getSlider()->set_format_value_func(format_value);
2016 slider->getSlider()->set_draw_value();
2017 slider->getSlider()->add_css_class("small-marks");
2018 for (int i = min; i <= max; i += 8) {
2019 auto const markup = (i % min == 0) ? format_value(i) : Glib::ustring{};
2020 slider->getSlider()->add_mark(i, Gtk::PositionType::BOTTOM, markup);
2021 }
2022 _page_toolbars.add_line(false, tbox.label, *slider, "", _("Adjust toolbar icon size"));
2023 }
2024
2025 std::vector<PrefItem> snap = {
2026 { _("Simple"), 1, _("Present simplified snapping options that manage all advanced settings"), true },
2027 { _("Advanced"), 0, _("Expose all snapping options for manual control") },
2028 { _("Permanent"), 2, _("All advanced snap options appear in a permanent bar") },
2029 };
2030 _page_toolbars.add_line(false, _("Snap controls bar:"), *Gtk::make_managed<PrefRadioButtons>(snap, "/toolbox/simplesnap"), "", "");
2031 } catch (Glib::Error const &error) {
2032 g_error("Couldn't load toolbar-tool-prefs user interface file: `%s`", error.what());
2033 }
2034
2035 this->AddPage(_page_toolbars, _("Toolbars"), iter_ui, PREFS_PAGE_UI_TOOLBARS);
2036
2037 // Windows
2038 _win_save_geom.init ( _("Save and restore window geometry for each document"), "/options/savewindowgeometry/value", PREFS_WINDOW_GEOMETRY_FILE, true, nullptr);
2039 _win_save_geom_prefs.init ( _("Remember and use last window's geometry"), "/options/savewindowgeometry/value", PREFS_WINDOW_GEOMETRY_LAST, false, &_win_save_geom);
2040 _win_save_geom_off.init ( _("Don't save window geometry"), "/options/savewindowgeometry/value", PREFS_WINDOW_GEOMETRY_NONE, false, &_win_save_geom);
2041
2042 _win_native.init ( _("Native open/save dialogs"), "/options/desktopintegration/value", 1, true, nullptr);
2043 _win_gtk.init ( _("GTK open/save dialogs"), "/options/desktopintegration/value", 0, false, &_win_native);
2044
2045 {
2046 Glib::ustring startModeLabels[] = {C_("Start mode", "Nothing"),
2047 C_("Start mode", "Splash screen only"),
2048 C_("Start mode", "Welcome screen")};
2049 int startModeValues[] = {0, 1, 2};
2050
2051 _win_start_mode.init( "/options/boot/mode", startModeLabels, startModeValues, 2);
2052 _page_windows.add_line( false, _("Show when starting:"), _win_start_mode, "",
2053 _("Set what shows when loading the program normally."), false);
2054 }
2055
2056 _win_hide_task.init ( _("Dialogs are hidden in taskbar"), "/options/dialogsskiptaskbar/value", true);
2057 _win_save_viewport.init ( _("Save and restore documents viewport"), "/options/savedocviewport/value", true);
2058 _win_zoom_resize.init ( _("Zoom when window is resized"), "/options/stickyzoom/value", false);
2059 _win_ontop_none.init ( C_("Dialog on top", "None"), "/options/transientpolicy/value", PREFS_DIALOGS_WINDOWS_NONE, false, nullptr);
2060 _win_ontop_normal.init ( _("Normal"), "/options/transientpolicy/value", PREFS_DIALOGS_WINDOWS_NORMAL, true, &_win_ontop_none);
2061 _win_ontop_agressive.init ( _("Aggressive"), "/options/transientpolicy/value", PREFS_DIALOGS_WINDOWS_AGGRESSIVE, false, &_win_ontop_none);
2062
2063 _win_dialogs_labels_auto.init( _("Automatic"), "/options/notebooklabels/value", PREFS_NOTEBOOK_LABELS_AUTO, true, nullptr);
2064 _win_dialogs_labels_active.init( _("Active"), "/options/notebooklabels/value", PREFS_NOTEBOOK_LABELS_ACTIVE, true, nullptr);
2065 _win_dialogs_labels_off.init( _("Off"), "/options/notebooklabels/value", PREFS_NOTEBOOK_LABELS_OFF, false, &_win_dialogs_labels_auto);
2066
2067 _win_dialogs_tab_close_btn.init(_("Show close button in tab"), "/options/notebooktabs/show-closebutton", false);
2068
2069 {
2070 Glib::ustring defaultSizeLabels[] = {C_("Window size", "Default"),
2071 C_("Window size", "Small"),
2072 C_("Window size", "Large"),
2073 C_("Window size", "Maximized")};
2074 int defaultSizeValues[] = {PREFS_WINDOW_SIZE_NATURAL,
2078
2079 _win_default_size.init( "/options/defaultwindowsize/value", defaultSizeLabels, defaultSizeValues, PREFS_WINDOW_SIZE_NATURAL);
2080 _page_windows.add_line( false, _("Default window size:"), _win_default_size, "",
2081 _("Set the default window size"), false);
2082 }
2083
2084 _page_windows.add_group_header( _("Saving window size and position"), 4);
2085 _page_windows.add_line( true, "", _win_save_geom_off, "",
2086 _("Let the window manager determine placement of all windows"));
2087 _page_windows.add_line( true, "", _win_save_geom_prefs, "",
2088 _("Remember and use the last window's geometry (saves geometry to user preferences)"));
2089 _page_windows.add_line( true, "", _win_save_geom, "",
2090 _("Save and restore window geometry for each document (saves geometry in the document)"));
2091
2092#ifdef _WIN32
2093 _page_windows.add_group_header( _("Desktop integration"));
2094 _page_windows.add_line( true, "", _win_native, "",
2095 _("Use Windows like open and save dialogs"));
2096 _page_windows.add_line( true, "", _win_gtk, "",
2097 _("Use GTK open and save dialogs "));
2098#endif
2099 _page_windows.add_group_header(_("Dialogs settings"), 4);
2100
2101 std::vector<PrefItem> dock = {
2102 { _("Docked"), PREFS_DIALOGS_BEHAVIOR_DOCKABLE, _("Allow dialog docking"), true },
2103 { _("Floating"), PREFS_DIALOGS_BEHAVIOR_FLOATING, _("Disable dialog docking") }
2104 };
2105 _page_windows.add_line(true, _("Dialog behavior"), *Gtk::make_managed<PrefRadioButtons>(dock, "/options/dialogtype/value"), "", "", false, reset_icon());
2106
2107#ifndef _WIN32 // non-Win32 special code to enable transient dialogs
2108 std::vector<PrefItem> on_top = {
2109 { C_("Dialog on top", "None"), PREFS_DIALOGS_WINDOWS_NONE, _("Dialogs are treated as regular windows") },
2110 { _("Normal"), PREFS_DIALOGS_WINDOWS_NORMAL, _("Dialogs stay on top of document windows"), true },
2111 { _("Aggressive"), PREFS_DIALOGS_WINDOWS_AGGRESSIVE, _("Same as Normal but may work better with some window managers") }
2112 };
2113 _page_windows.add_line(true, _("Dialog on top"), *Gtk::make_managed<PrefRadioButtons>(on_top, "/options/transientpolicy/value"), "", "");
2114#endif
2115
2116 std::vector<PrefItem> labels = {
2117 { _("Always"), PREFS_NOTEBOOK_LABELS_AUTO, _("Dialog names will be displayed when there is enough space"), true },
2118 { _("Active tab only"), PREFS_NOTEBOOK_LABELS_ACTIVE, _("Only show label on active tab") },
2119 { _("Off"), PREFS_NOTEBOOK_LABELS_OFF, _("Only show dialog icons") }
2120 };
2121 _page_windows.add_line(true, _("Tab labels"), *Gtk::make_managed<PrefRadioButtons>(labels, "/options/notebooklabels/value"), "", "", false);
2122 _page_windows.add_line(true, "Dialog tabs", _win_dialogs_tab_close_btn, "", _("Show close button in dialog tabs"));
2123
2124 auto const save_dlg = Gtk::make_managed<PrefCheckButton>();
2125 save_dlg->init(_("Save and restore dialogs' status"), "/options/savedialogposition/value", true);
2126 _page_windows.add_line(true, "", *save_dlg, "", _("Save and restore dialogs' status (the last open windows dialogs are saved when it closes)"));
2127
2128#ifndef _WIN32 // FIXME: Temporary Win32 special code to enable transient dialogs
2129 _page_windows.add_line( true, "", _win_hide_task, "",
2130 _("Whether dialog windows are to be hidden in the window manager taskbar"));
2131#endif
2132 _page_windows.add_group_header( _("Text and Font dialog"));
2133 std::vector<PrefItem> lister = {
2134 { _("List fonts and styles"), 0, _("List fonts and styles separately"), true },
2135 { _("Unified font browser (experimental)"), 1, _("Show all font styles in a single list") }
2136 };
2137 _page_windows.add_line(true, _("Font selector"), *Gtk::make_managed<PrefRadioButtons>(lister, "/options/font/browser"), "", "", false, reset_icon());
2138
2139 _page_windows.add_group_header( _("Miscellaneous"));
2140
2141 _page_windows.add_line( true, "", _win_zoom_resize, "",
2142 _("Zoom drawing when document window is resized, to keep the same area visible (this is the default which can be changed in any window using the button above the right scrollbar)"));
2143 _page_windows.add_line( true, "", _win_save_viewport, "",
2144 _("Save documents viewport (zoom and panning position). Useful to turn off when sharing version controlled files."));
2145
2146 this->AddPage(_page_windows, _("Windows"), iter_ui, PREFS_PAGE_UI_WINDOWS);
2147
2148 // default colors in RGBA
2149 static auto GRID_DEFAULT_MAJOR_COLOR = "#0099e54d";
2150 static auto GRID_DEFAULT_BLOCK_COLOR = "#0047cb4d";
2151
2152 // Color pickers
2153 _compact_colorselector.init(_("Use compact color selector mode switch"), "/colorselector/switcher", true);
2154 _page_color_pickers.add_line(false, "", _compact_colorselector, "", _("Use compact combo box for selecting color modes"), false);
2155
2156 _page_color_pickers.add_group_header(_("Visible color pickers"));
2157 {
2158 auto const container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
2159 auto prefs = Inkscape::Preferences::get();
2160 for (auto& space : Colors::Manager::get().spaces(Colors::Space::Traits::Picker)) {
2161 auto const btn = Gtk::make_managed<Gtk::ToggleButton>();
2162 auto const box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
2163 auto const label = Gtk::make_managed<Gtk::Label>(space->getName());
2164 label->set_valign(Gtk::Align::CENTER);
2165 UI::pack_start(*box, *label);
2166 UI::pack_start(*box, *Gtk::make_managed<Gtk::Image>(Gio::ThemedIcon::create(space->getIcon())));
2167 box->set_spacing(3);
2168 auto path = space->getPrefsPath() + "visible";
2169 btn->set_active(prefs->getBool(path));
2170 btn->set_child(*box);
2171 btn->set_has_frame(false);
2172
2173 btn->signal_toggled().connect([=, path = std::move(path)]() {
2174 prefs->setBool(path, btn->get_active());
2175
2176 auto const buttons = UI::get_children(*container);
2177 auto const is_active = [](Gtk::Widget const * const child)
2178 { return dynamic_cast<Gtk::ToggleButton const &>(*child).get_active(); };
2179 if (!buttons.empty() && std::none_of(buttons.begin(), buttons.end(), is_active)) {
2180 // all pickers hidden; not a good combination; select first one
2181 dynamic_cast<Gtk::ToggleButton &>(*buttons.front()).set_active(true);
2182 }
2183 });
2184
2185 UI::pack_start(*container, *btn);
2186 }
2187
2188 container->set_spacing(5);
2189 _page_color_pickers.add_line(true, "", *container, "", _("Select color pickers"), false);
2190 }
2191
2192 AddPage(_page_color_pickers, _("Color Selector"), iter_ui, PREFS_PAGE_UI_COLOR_PICKERS);
2193 // end of Color pickers
2194
2195 // Grids
2196 _page_grids.add_group_header( _("Line color when zooming out"));
2197
2198 _grids_no_emphasize_on_zoom.init( _("Minor grid line color"), "/options/grids/no_emphasize_when_zoomedout", 1, true, nullptr);
2199 _page_grids.add_line( true, "", _grids_no_emphasize_on_zoom, "", _("The gridlines will be shown in minor grid line color"), false);
2200 _grids_emphasize_on_zoom.init( _("Major grid line color"), "/options/grids/no_emphasize_when_zoomedout", 0, false, &_grids_no_emphasize_on_zoom);
2201 _page_grids.add_line( true, "", _grids_emphasize_on_zoom, "", _("The gridlines will be shown in major grid line color"), false);
2202
2203 _page_grids.add_group_header( _("Default grid settings"));
2204
2205 _page_grids.add_line( true, "", _grids_notebook, "", "", false);
2206 _grids_notebook.set_halign(Gtk::Align::START);
2207 _grids_notebook.set_hexpand(false);
2208 auto& grid_modular = *Gtk::make_managed<UI::Widget::DialogPage>();
2209 _grids_notebook.append_page(_grids_xy, _("Rectangular Grid"));
2210 _grids_notebook.append_page(_grids_axonom, _("Axonometric Grid"));
2211 _grids_notebook.append_page(grid_modular, _("Modular Grid"));
2212 {
2213 // Rectangular SPGrid properties
2214 _grids_xy_units.init("/options/grids/xy/units");
2215 _grids_xy.add_line( false, _("Grid units:"), _grids_xy_units, "", "", false);
2216 _grids_xy.add_line( false, _("Origin X:"), _grids_xy_origin_x, "", _("X coordinate of grid origin"), false);
2217 _grids_xy.add_line( false, _("Origin Y:"), _grids_xy_origin_y, "", _("Y coordinate of grid origin"), false);
2218 _grids_xy.add_line( false, _("Spacing X:"), _grids_xy_spacing_x, "", _("Distance between vertical grid lines"), false);
2219 _grids_xy.add_line( false, _("Spacing Y:"), _grids_xy_spacing_y, "", _("Distance between horizontal grid lines"), false);
2220
2221 _grids_xy_empcolor.init(_("Grid color:"), "/options/grids/xy/empcolor", GRID_DEFAULT_MAJOR_COLOR);
2222 _grids_xy.add_line( false, _("Grid color:"), _grids_xy_empcolor, "", _("Color used for grid lines"), false);
2223 _grids_xy_empspacing.init("/options/grids/xy/empspacing", 1.0, 1000.0, 1.0, 5.0, 5.0, true, false);
2224 _grids_xy.add_line( false, _("Major grid line every:"), _grids_xy_empspacing, "", "", false);
2225 _grids_xy_dotted.init( _("Show dots instead of lines"), "/options/grids/xy/dotted", false);
2226 _grids_xy.add_line( false, "", _grids_xy_dotted, "", _("If set, display dots at gridpoints instead of gridlines"), false);
2227
2228 // Axonometric SPGrid properties:
2229 _grids_axonom_units.init("/options/grids/axonom/units");
2230 _grids_axonom.add_line( false, _("Grid units:"), _grids_axonom_units, "", "", false);
2231 _grids_axonom.add_line( false, _("Origin X:"), _grids_axonom_origin_x, "", _("X coordinate of grid origin"), false);
2232 _grids_axonom.add_line( false, _("Origin Y:"), _grids_axonom_origin_y, "", _("Y coordinate of grid origin"), false);
2233 _grids_axonom.add_line( false, _("Spacing Y:"), _grids_axonom_spacing_y, "", _("Base length of z-axis"), false);
2234 _grids_axonom_angle_x.init("/options/grids/axonom/angle_x", -360.0, 360.0, 1.0, 10.0, 30.0, false, false);
2235 _grids_axonom_angle_z.init("/options/grids/axonom/angle_z", -360.0, 360.0, 1.0, 10.0, 30.0, false, false);
2236 _grids_axonom.add_line( false, _("Angle X:"), _grids_axonom_angle_x, "", _("Angle of x-axis"), false);
2237 _grids_axonom.add_line( false, _("Angle Z:"), _grids_axonom_angle_z, "", _("Angle of z-axis"), false);
2238 _grids_axonom_empcolor.init(_("Grid color:"), "/options/grids/axonom/empcolor", GRID_DEFAULT_MAJOR_COLOR);
2239 _grids_axonom.add_line( false, _("Grid color:"), _grids_axonom_empcolor, "", _("Color used for grid lines"), false);
2240 _grids_axonom_empspacing.init("/options/grids/axonom/empspacing", 1.0, 1000.0, 1.0, 5.0, 5.0, true, false);
2241 _grids_axonom.add_line( false, _("Major grid line every:"), _grids_axonom_empspacing, "", "", false);
2242 // Modular grid
2243 auto const units = Gtk::make_managed<UI::Widget::PrefUnit>();
2244 units->init("/options/grids/modular/units");
2245 auto const origin_x = Gtk::make_managed<UI::Widget::PrefSpinButton>();
2246 auto const origin_y = Gtk::make_managed<UI::Widget::PrefSpinButton>();
2247 auto const block_width = Gtk::make_managed<UI::Widget::PrefSpinButton>();
2248 auto const block_height = Gtk::make_managed<UI::Widget::PrefSpinButton>();
2249 auto const gap_x = Gtk::make_managed<UI::Widget::PrefSpinButton>();
2250 auto const gap_y = Gtk::make_managed<UI::Widget::PrefSpinButton>();
2251 auto const margin_x = Gtk::make_managed<UI::Widget::PrefSpinButton>();
2252 auto const margin_y = Gtk::make_managed<UI::Widget::PrefSpinButton>();
2253 auto const color_major = Gtk::make_managed<UI::Widget::PrefColorPicker>();
2254 color_major->init(_("Grid color:"), "/options/grids/modular/empcolor", GRID_DEFAULT_BLOCK_COLOR);
2255
2256 grid_modular.add_line(false, _("Grid units:"), *units, "", "", false);
2257 grid_modular.add_line(false, _("Origin X:"), *origin_x, "", _("X coordinate of grid origin"), false);
2258 grid_modular.add_line(false, _("Origin Y:"), *origin_y, "", _("Y coordinate of grid origin"), false);
2259 grid_modular.add_line( false, _("Block width:"), *block_width, "", _("Width of grid modules"), false);
2260 grid_modular.add_line( false, _("Block height:"), *block_height, "", _("Height of grid modules"), false);
2261 grid_modular.add_line(false, _("Gap X:"), *gap_x, "", _("Horizontal distance between blocks"), false);
2262 grid_modular.add_line(false, _("Gap Y:"), *gap_y, "", _("Vertical distance between blocks"), false);
2263 grid_modular.add_line(false, _("Margin X:"), *margin_x, "", _("Right and left margins"), false);
2264 grid_modular.add_line(false, _("Margin Y:"), *margin_y, "", _("Top and bottom margins"), false);
2265 grid_modular.add_line( false, _("Grid color:"), *color_major, "", _("Color used for grid blocks"), false);
2266
2267 for (auto [spin, path] : (std::tuple<PrefSpinButton*, const char*>[]) {
2268 {&_grids_xy_origin_x, "/options/grids/xy/origin_x"},
2269 {&_grids_xy_origin_y, "/options/grids/xy/origin_y"},
2270 {&_grids_xy_spacing_x, "/options/grids/xy/spacing_x"},
2271 {&_grids_xy_spacing_y, "/options/grids/xy/spacing_y"},
2272 {&_grids_axonom_origin_x, "/options/grids/axonom/origin_x"},
2273 {&_grids_axonom_origin_y, "/options/grids/axonom/origin_y"},
2274 {&_grids_axonom_spacing_y, "/options/grids/axonom/spacing_y"},
2275 {origin_x, "/options/grids/modular/origin_x"},
2276 {origin_y, "/options/grids/modular/origin_y"},
2277 {block_width, "/options/grids/modular/spacing_x"},
2278 {block_height, "/options/grids/modular/spacing_y"},
2279 {gap_x, "/options/grids/modular/gapx"},
2280 {gap_y, "/options/grids/modular/gapy"},
2281 {margin_x, "/options/grids/modular/marginx"},
2282 {margin_y, "/options/grids/modular/marginy"},
2283 }) {
2284 spin->init(path, -10'000.0, 10'000.0, 0.1, 1.0, 0.0, false, false);
2285 spin->set_digits(5);
2286 spin->set_width_chars(12);
2287 }
2288 }
2289
2290 this->AddPage(_page_grids, _("Grids"), iter_ui, PREFS_PAGE_UI_GRIDS);
2291
2292 // Command Palette
2293 _page_command_palette.add_group_header(_("Display Options"));
2294
2295 _cp_show_full_action_name.init(_("Show command line argument names"), "/options/commandpalette/showfullactionname/value", false);
2296 _page_command_palette.add_line(true, "", _cp_show_full_action_name, "", _("Show action argument names in the command palette suggestions, most useful for using them on the command line"));
2297
2298 _cp_show_untranslated_name.init(_("Show untranslated (English) names"), "/options/commandpalette/showuntranslatedname/value", true);
2299 _page_command_palette.add_line(true, "", _cp_show_untranslated_name, "", _("Also show the English names of the command"));
2300
2301 this->AddPage(_page_command_palette, _("Command Palette"), iter_ui, PREFS_PAGE_COMMAND_PALETTE);
2302 // /Command Palette
2303
2304
2305 initKeyboardShortcuts(iter_ui);
2306}
2307
2308static void profileComboChanged( Gtk::ComboBoxText* combo )
2309{
2311 int rowNum = combo->get_active_row_number();
2312 if ( rowNum < 1 ) {
2313 prefs->setString("/options/displayprofile/uri", "");
2314 } else {
2315 Glib::ustring active = combo->get_active_text();
2316
2317 auto &cms_system = Inkscape::Colors::CMS::System::get();
2318 if (auto profile = cms_system.getProfile(active)) {
2319 prefs->setString("/options/displayprofile/uri", profile->getPath());
2320 }
2321 }
2322}
2323
2324static void proofComboChanged( Gtk::ComboBoxText* combo )
2325{
2326 Glib::ustring active = combo->get_active_text();
2327 auto &cms_system = Inkscape::Colors::CMS::System::get();
2328 if (auto profile = cms_system.getProfile(active)) {
2330 prefs->setString("/options/softproof/uri", profile->getPath());
2331 }
2332}
2333
2334static void gamutColorChanged( Gtk::ColorButton* btn ) {
2335 auto rgba = btn->get_rgba();
2336 auto r = rgba.get_red_u();
2337 auto g = rgba.get_green_u();
2338 auto b = rgba.get_blue_u();
2339
2340 gchar* tmp = g_strdup_printf("#%02x%02x%02x", (r >> 8), (g >> 8), (b >> 8) );
2341
2343 prefs->setString("/options/softproof/gamutcolor", tmp);
2344 g_free(tmp);
2345}
2346
2347void InkscapePreferences::initPageIO()
2348{
2349 Gtk::TreeModel::iterator iter_io = this->AddPage(_page_io, _("Input/Output"), PREFS_PAGE_IO);
2350
2351 _save_use_current_dir.init( _("Use current directory for \"Save As ...\""), "/dialogs/save_as/use_current_dir", true);
2352 _page_io.add_line( false, "", _save_use_current_dir, "",
2353 _("When this option is on, the \"Save as...\" and \"Save a Copy...\" dialogs will always open in the directory where the currently open document is; when it's off, each will open in the directory where you last saved a file using it"), true);
2354
2355 _misc_default_metadata.init( _("Add default metadata to new documents"), "/metadata/addToNewFile", false);
2356 _page_io.add_line( false, "", _misc_default_metadata, "",
2357 _("Add default metadata to new documents. Default metadata can be set from Document Properties->Metadata."), true);
2358
2359 _export_all_extensions.init( _("Show all outputs in Export Dialog"), "/dialogs/export/show_all_extensions", false);
2360 _page_io.add_line( false, "", _export_all_extensions, "",
2361 _("Will list all possible output extensions in the Export Dialog selection."), true);
2362
2363 // Input devices options
2364 _mouse_sens.init ( "/options/cursortolerance/value", 0.0, 30.0, 1.0, 1.0, 8.0, true, false);
2365 _page_mouse.add_line( false, _("_Grab sensitivity:"), _mouse_sens, _("pixels"),
2366 _("How close on the screen you need to be to an object to be able to grab it with mouse (in screen pixels)"), false, reset_icon());
2367 _mouse_thres.init ( "/options/dragtolerance/value", 0.0, 100.0, 1.0, 1.0, 8.0, true, false);
2368 _page_mouse.add_line( false, _("_Click/drag threshold:"), _mouse_thres, _("pixels"),
2369 _("Maximum mouse drag (in screen pixels) which is considered a click, not a drag"), false);
2370
2371 _mouse_use_ext_input.init( _("Use pressure-sensitive tablet"), "/options/useextinput/value", true);
2372 _page_mouse.add_line(false, "",_mouse_use_ext_input, "",
2373 _("Use the capabilities of a tablet or other pressure-sensitive device. Disable this only if you have problems with the tablet (you can still use it as a mouse)"), false, reset_icon());
2374
2375 _mouse_switch_on_ext_input.init( _("Switch tool based on tablet device"), "/options/switchonextinput/value", false);
2376 _page_mouse.add_line(false, "",_mouse_switch_on_ext_input, "",
2377 _("Change tool as different devices are used on the tablet (pen, eraser, mouse)"), false, reset_icon());
2378 this->AddPage(_page_mouse, _("Input devices"), iter_io, PREFS_PAGE_IO_MOUSE);
2379
2380 // SVG output options
2381 _svgoutput_usenamedcolors.init( _("Use named colors"), "/options/svgoutput/usenamedcolors", false);
2382 _page_svgoutput.add_line( false, "", _svgoutput_usenamedcolors, "", _("If set, write the CSS name of the color when available (e.g. 'red' or 'magenta') instead of the numeric value"), false);
2383
2384 _page_svgoutput.add_group_header( _("XML formatting"));
2385
2386 _svgoutput_inlineattrs.init( _("Inline attributes"), "/options/svgoutput/inlineattrs", false);
2387 _page_svgoutput.add_line( true, "", _svgoutput_inlineattrs, "", _("Put attributes on the same line as the element tag"), false);
2388
2389 _svgoutput_indent.init("/options/svgoutput/indent", 0.0, 1000.0, 1.0, 2.0, 2.0, true, false);
2390 _page_svgoutput.add_line( true, _("_Indent, spaces:"), _svgoutput_indent, "", _("The number of spaces to use for indenting nested elements; set to 0 for no indentation"), false);
2391
2392 _page_svgoutput.add_group_header( _("Path data"));
2393
2394 Glib::ustring const pathstringFormatLabels[] = {_("Absolute"), _("Relative"), _("Optimized")};
2395 int const pathstringFormatValues[] = {0, 1, 2};
2396 _svgoutput_pathformat.init("/options/svgoutput/pathstring_format", pathstringFormatLabels, pathstringFormatValues, 2);
2397 _page_svgoutput.add_line( true, _("Path string format:"), _svgoutput_pathformat, "", _("Path data should be written: only with absolute coordinates, only with relative coordinates, or optimized for string length (mixed absolute and relative coordinates)"), false);
2398
2399 _svgoutput_forcerepeatcommands.init( _("Force repeat commands"), "/options/svgoutput/forcerepeatcommands", false);
2400 _page_svgoutput.add_line( true, "", _svgoutput_forcerepeatcommands, "", _("Force repeating of the same path command (for example, 'L 1,2 L 3,4' instead of 'L 1,2 3,4')"), false);
2401
2402 _page_svgoutput.add_group_header( _("Numbers"));
2403
2404 _svgoutput_numericprecision.init("/options/svgoutput/numericprecision", 1.0, 16.0, 1.0, 2.0, 8.0, true, false);
2405 _page_svgoutput.add_line( true, _("_Numeric precision:"), _svgoutput_numericprecision, "", _("Significant figures of the values written to the SVG file"), false);
2406
2407 _svgoutput_minimumexponent.init("/options/svgoutput/minimumexponent", -32.0, -1, 1.0, 2.0, -8.0, true, false);
2408 _page_svgoutput.add_line( true, _("Minimum _exponent:"), _svgoutput_minimumexponent, "", _("The smallest number written to SVG is 10 to the power of this exponent; anything smaller is written as zero"), false);
2409
2410 /* Code to add controls for attribute checking options */
2411
2412 /* Add incorrect style properties options */
2413 _page_svgoutput.add_group_header( _("Improper Attributes Actions"));
2414
2415 _svgoutput_attrwarn.init( _("Print warnings"), "/options/svgoutput/incorrect_attributes_warn", true);
2416 _page_svgoutput.add_line( true, "", _svgoutput_attrwarn, "", _("Print warning if invalid or non-useful attributes found. Database files located in inkscape_data_dir/attributes."), false);
2417 _svgoutput_attrremove.init( _("Remove attributes"), "/options/svgoutput/incorrect_attributes_remove", false);
2418 _page_svgoutput.add_line( true, "", _svgoutput_attrremove, "", _("Delete invalid or non-useful attributes from element tag"), false);
2419
2420 /* Add incorrect style properties options */
2421 _page_svgoutput.add_group_header( _("Inappropriate Style Properties Actions"));
2422
2423 _svgoutput_stylepropwarn.init( _("Print warnings"), "/options/svgoutput/incorrect_style_properties_warn", true);
2424 _page_svgoutput.add_line( true, "", _svgoutput_stylepropwarn, "", _("Print warning if inappropriate style properties found (i.e. 'font-family' set on a <rect>). Database files located in inkscape_data_dir/attributes."), false);
2425 _svgoutput_stylepropremove.init( _("Remove style properties"), "/options/svgoutput/incorrect_style_properties_remove", false);
2426 _page_svgoutput.add_line( true, "", _svgoutput_stylepropremove, "", _("Delete inappropriate style properties"), false);
2427
2428 /* Add default or inherited style properties options */
2429 _page_svgoutput.add_group_header( _("Non-useful Style Properties Actions"));
2430
2431 _svgoutput_styledefaultswarn.init( _("Print warnings"), "/options/svgoutput/style_defaults_warn", true);
2432 _page_svgoutput.add_line( true, "", _svgoutput_styledefaultswarn, "", _("Print warning if redundant style properties found (i.e. if a property has the default value and a different value is not inherited or if value is the same as would be inherited). Database files located in inkscape_data_dir/attributes."), false);
2433 _svgoutput_styledefaultsremove.init( _("Remove style properties"), "/options/svgoutput/style_defaults_remove", false);
2434 _page_svgoutput.add_line( true, "", _svgoutput_styledefaultsremove, "", _("Delete redundant style properties"), false);
2435
2436 _page_svgoutput.add_group_header( _("Check Attributes and Style Properties on"));
2437
2438 _svgoutput_check_reading.init( _("Reading"), "/options/svgoutput/check_on_reading", false);
2439 _page_svgoutput.add_line( true, "", _svgoutput_check_reading, "", _("Check attributes and style properties on reading in SVG files (including those internal to Inkscape which will slow down startup)"), false);
2440 _svgoutput_check_editing.init( _("Editing"), "/options/svgoutput/check_on_editing", false);
2441 _page_svgoutput.add_line( true, "", _svgoutput_check_editing, "", _("Check attributes and style properties while editing SVG files (may slow down Inkscape, mostly useful for debugging)"), false);
2442 _svgoutput_check_writing.init( _("Writing"), "/options/svgoutput/check_on_writing", true);
2443 _page_svgoutput.add_line( true, "", _svgoutput_check_writing, "", _("Check attributes and style properties on writing out SVG files"), false);
2444
2445 this->AddPage(_page_svgoutput, _("SVG output"), iter_io, PREFS_PAGE_IO_SVGOUTPUT);
2446
2447 // SVG Export Options ==========================================
2448
2449 // SVG 2 Fallbacks
2450 _page_svgexport.add_group_header( _("SVG 2"));
2451 _svgexport_insert_text_fallback.init( _("Insert SVG 1.1 fallback in text"), "/options/svgexport/text_insertfallback", true );
2452 _svgexport_insert_mesh_polyfill.init( _("Insert JavaScript code for mesh gradients"), "/options/svgexport/mesh_insertpolyfill", true );
2453 _svgexport_insert_hatch_polyfill.init( _("Insert JavaScript code for SVG2 hatches"), "/options/svgexport/hatch_insertpolyfill", true );
2454
2455 _page_svgexport.add_line( false, "", _svgexport_insert_text_fallback, "", _("Adds fallback options for non-SVG 2 renderers."), false);
2456 _page_svgexport.add_line( false, "", _svgexport_insert_mesh_polyfill, "", _("Adds a JavaScript polyfill for rendering meshes in web browsers."), false);
2457 _page_svgexport.add_line( false, "", _svgexport_insert_hatch_polyfill, "", _("Adds a JavaScript polyfill for rendering hatches in web browsers."), false);
2458
2459 // SVG Export Options (SVG 2 -> SVG 1)
2460 _page_svgexport.add_group_header( _("SVG 2 to SVG 1.1"));
2461
2462 _svgexport_remove_marker_auto_start_reverse.init( _("Use correct marker direction in SVG 1.1 renderers"), "/options/svgexport/marker_autostartreverse", false);
2463 _svgexport_remove_marker_context_paint.init( _("Use correct marker colors in SVG 1.1 renderers"), "/options/svgexport/marker_contextpaint", false);
2464
2465 _page_svgexport.add_line( false, "", _svgexport_remove_marker_auto_start_reverse, "", _("SVG 2 allows markers to automatically be reversed at the start of a path with 'auto_start_reverse'. This adds a rotated duplicate of the marker's definition."), false);
2466 _page_svgexport.add_line( false, "", _svgexport_remove_marker_context_paint, "", _("SVG 2 allows markers to automatically match the stroke color by using 'context_paint' or 'context_fill'. This adjusts the markers own colors."), false);
2467
2468 this->AddPage(_page_svgexport, _("SVG export"), iter_io, PREFS_PAGE_IO_SVGEXPORT);
2469
2470
2471 // CMS options
2473 /* TRANSLATORS: see http://www.newsandtech.com/issues/2004/03-04/pt/03-04_rendering.htm */
2474 Glib::ustring const intentLabels[] = {_("Perceptual"), _("Relative Colorimetric"),
2475 _("Saturation"), _("Absolute Colorimetric")};
2476 int const intentValues[] = {0, 1, 2, 3};
2477
2478 _page_cms.add_group_header( _("Display adjustment"));
2479
2480 Glib::ustring tmpStr;
2481 for (auto const &path : Inkscape::Colors::CMS::System::get().getDirectoryPaths()) {
2482 tmpStr += "\n";
2483 tmpStr += path.first;
2484 }
2485
2486 gchar* profileTip = g_strdup_printf(_("The ICC profile to use to calibrate display output.\nSearched directories:%s"), tmpStr.c_str());
2487 _page_cms.add_line( true, _("User monitor profile:"), _cms_display_profile, "",
2488 profileTip, false);
2489 g_free(profileTip);
2490 profileTip = nullptr;
2491
2492 _cms_from_user.init( _("Use profile from user"), "/options/displayprofile/use_user_profile", false);
2493 _page_cms.add_line( true, "", _cms_from_user, "",
2494 _("Use a user-specified ICC profile for monitor color correction. Warning: System wide color correction should be disabled."), false);
2495
2496 _cms_intent.init("/options/displayprofile/intent", intentLabels, intentValues, 0);
2497 _page_cms.add_line( true, _("Display rendering intent:"), _cms_intent, "",
2498 _("The rendering intent to use to calibrate display output"), false);
2499
2500 _page_cms.add_group_header( _("Proofing"));
2501
2502 _cms_softproof.init( _("Simulate output on screen"), "/options/softproof/enable", false);
2503 _page_cms.add_line( true, "", _cms_softproof, "",
2504 _("Simulates output of target device"), false);
2505
2506 _cms_gamutwarn.init( _("Mark out of gamut colors"), "/options/softproof/gamutwarn", false);
2507 _page_cms.add_line( true, "", _cms_gamutwarn, "",
2508 _("Highlights colors that are out of gamut for the target device"), false);
2509
2510 Glib::ustring colorStr = prefs->getString("/options/softproof/gamutcolor");
2511
2512 Gdk::RGBA tmpColor( colorStr.empty() ? "#00ff00" : colorStr);
2513 _cms_gamutcolor.set_rgba( tmpColor );
2514
2515 _page_cms.add_line( true, _("Out of gamut warning color:"), _cms_gamutcolor, "",
2516 _("Selects the color used for out of gamut warning"), false);
2517
2518 _page_cms.add_line( true, _("Device profile:"), _cms_proof_profile, "",
2519 _("The ICC profile to use to simulate device output"), false);
2520
2521 _cms_proof_intent.init("/options/softproof/intent", intentLabels, intentValues, 0);
2522 _page_cms.add_line( true, _("Device rendering intent:"), _cms_proof_intent, "",
2523 _("The rendering intent to use to calibrate device output"), false);
2524
2525 _cms_proof_blackpoint.init( _("Black point compensation"), "/options/softproof/bpc", false);
2526 _page_cms.add_line( true, "", _cms_proof_blackpoint, "",
2527 _("Enables black point compensation"), false);
2528
2529 {
2530 auto &cms_system = Inkscape::Colors::CMS::System::get();
2531 std::string current = prefs->getString( "/options/displayprofile/uri" );
2532 gint index = 0;
2533 _cms_display_profile.append(_("<none>"));
2534 index++;
2535 for (auto profile : cms_system.getDisplayProfiles()) {
2536 _cms_display_profile.append(profile->getName());
2537 if (profile->getPath() == current) {
2538 _cms_display_profile.set_active(index);
2539 }
2540 index++;
2541 }
2542 if ( current.empty() ) {
2543 _cms_display_profile.set_active(0);
2544 }
2545
2546 current = prefs->getString("/options/softproof/uri");
2547 index = 0;
2548 for (auto profile : cms_system.getOutputProfiles()) {
2549 _cms_proof_profile.append(profile->getName());
2550 if (profile->getPath() == current) {
2551 _cms_proof_profile.set_active(index);
2552 }
2553 index++;
2554 }
2555 }
2556
2557 _cms_gamutcolor.signal_color_set().connect(sigc::bind(&gamutColorChanged, &_cms_gamutcolor));
2558
2559 _cms_display_profile.signal_changed().connect(sigc::bind(&profileComboChanged, &_cms_display_profile));
2560 _cms_proof_profile.signal_changed().connect(sigc::bind(&proofComboChanged, &_cms_proof_profile));
2561
2562 this->AddPage(_page_cms, _("Color management"), iter_io, PREFS_PAGE_IO_CMS);
2563
2564 // Autosave options
2565 _save_autosave_enable.init( _("Enable autosave"), "/options/autosave/enable", true);
2566 _page_autosave.add_line(false, "", _save_autosave_enable, "", _("Automatically save the current document(s) at a given interval, thus minimizing loss in case of a crash"), false);
2567 if (prefs->getString("/options/autosave/path").empty()) {
2568 // Set the default fallback "tmp dir" if autosave path is not set.
2569 prefs->setString("/options/autosave/path", Glib::build_filename(Glib::get_user_cache_dir(), "inkscape"));
2570 }
2571
2572 _save_autosave_path_dir.init(prefs->getString("/options/autosave/path"), "/options/autosave/path",
2573 Glib::build_filename(Glib::get_user_cache_dir(), "inkscape"));
2574 _page_autosave.add_line(false, C_("Filesystem", "Autosave _directory:"), _save_autosave_path_dir, "",
2575 _("The directory where autosaves will be written. This should be an absolute path (starts "
2576 "with / on UNIX or a drive letter such as C: on Windows)."),
2577 true);
2578
2579 _save_autosave_interval.init("/options/autosave/interval", 1.0, 10800.0, 1.0, 10.0, 10.0, true, false);
2580 _page_autosave.add_line(false, _("_Interval (in minutes):"), _save_autosave_interval, "", _("Interval (in minutes) at which document will be autosaved"), false);
2581 _save_autosave_max.init("/options/autosave/max", 1.0, 10000.0, 1.0, 10.0, 10.0, true, false);
2582 _page_autosave.add_line(false, _("_Maximum number of autosaves:"), _save_autosave_max, "", _("Maximum number of autosaved files; use this to limit the storage space used"), false);
2583
2584 // When changing the interval or enabling/disabling the autosave function,
2585 // update our running configuration
2586 _save_autosave_enable.changed_signal.connect([](bool) { Inkscape::AutoSave::restart(); });
2587 _save_autosave_interval.changed_signal.connect([](double) { Inkscape::AutoSave::restart(); });
2588
2589 this->AddPage(_page_autosave, _("Autosave"), iter_io, PREFS_PAGE_IO_AUTOSAVE);
2590
2591 // No Result
2592 _page_notfound.add_group_header(_("No matches were found, try another search!"));
2593}
2594
2595void InkscapePreferences::initPageBehavior()
2596{
2597 Gtk::TreeModel::iterator iter_behavior = this->AddPage(_page_behavior, _("Behavior"), PREFS_PAGE_BEHAVIOR);
2598
2599 _misc_simpl.init("/options/simplifythreshold/value", 0.0001, 1.0, 0.0001, 0.0010, 0.0010, false, false);
2600 _page_behavior.add_line( false, _("_Simplification threshold:"), _misc_simpl, "",
2601 _("How strong is the Node tool's Simplify command by default. If you invoke this command several times in quick succession, it will act more and more aggressively; invoking it again after a pause restores the default threshold."), false);
2602
2603 _undo_limit.init("", "/options/undo/limit", true);
2604 _page_behavior.add_line(false, _("Limit Undo Size:"), _undo_limit, "",
2605 _("Enable the undo limit and remove old changes. Disabling this option will use more memory."));
2606 _undo_size.init("/options/undo/size", 1.0, 32000.0, 1.0, 1.0, 200.0, true, false);
2607 _page_behavior.add_line(false, _("Maximum _Undo Size:"), _undo_size, "",
2608 _("How large the undo log will be allowed to get before being trimmed to free memory."), false );
2609 _undo_limit.changed_signal.connect(sigc::mem_fun(_undo_size, &Gtk::Widget::set_sensitive));
2610 _undo_size.set_sensitive(_undo_limit.get_active());
2611
2612 _markers_color_stock.init ( _("Color stock markers the same color as object"), "/options/markers/colorStockMarkers", true);
2613 _markers_color_custom.init ( _("Color custom markers the same color as object"), "/options/markers/colorCustomMarkers", false);
2614 _markers_color_update.init ( _("Update marker color when object color changes"), "/options/markers/colorUpdateMarkers", true);
2615
2616 // Selecting options
2617 _sel_all.init ( _("Select in all layers"), "/options/kbselection/inlayer", PREFS_SELECTION_ALL, false, nullptr);
2618 _sel_current.init ( _("Select only within current layer"), "/options/kbselection/inlayer", PREFS_SELECTION_LAYER, true, &_sel_all);
2619 _sel_recursive.init ( _("Select in current layer and sublayers"), "/options/kbselection/inlayer", PREFS_SELECTION_LAYER_RECURSIVE, false, &_sel_all);
2620 _sel_hidden.init ( _("Ignore hidden objects and layers"), "/options/kbselection/onlyvisible", true);
2621 _sel_locked.init ( _("Ignore locked objects and layers"), "/options/kbselection/onlysensitive", true);
2622 _sel_inlayer_same.init ( _("Select same behaves like select all"), "/options/selection/samelikeall", false);
2623
2624 _sel_layer_deselects.init ( _("Deselect upon layer change"), "/options/selection/layerdeselect", true);
2625
2626 _sel_touch_topmost_only.init ( _("Select the topmost items only when in touch selection mode"), "/options/selection/touchsel_topmost_only", true);
2627 _sel_zero_opacity.init(_("Select transparent objects, strokes, and fills"), "/options/selection/zeroopacity", false);
2628
2629 _page_select.add_line( false, "", _sel_layer_deselects, "",
2630 _("Uncheck this to be able to keep the current objects selected when the current layer changes"));
2631 _page_select.add_line(
2632 false, "", _sel_zero_opacity, "",
2633 _("Check to make objects, strokes, and fills which are completely transparent selectable even if not in outline mode"));
2634
2635 _page_select.add_line( false, "", _sel_touch_topmost_only, "",
2636 _("In touch selection mode, if multiple items overlap at a point, select only the topmost item"));
2637
2638 _page_select.add_group_header( _("Ctrl+A, Tab, Shift+Tab"));
2639 _page_select.add_line( true, "", _sel_all, "",
2640 _("Make keyboard selection commands work on objects in all layers"));
2641 _page_select.add_line( true, "", _sel_current, "",
2642 _("Make keyboard selection commands work on objects in current layer only"));
2643 _page_select.add_line( true, "", _sel_recursive, "",
2644 _("Make keyboard selection commands work on objects in current layer and all its sublayers"));
2645 _page_select.add_line( true, "", _sel_hidden, "",
2646 _("Uncheck this to be able to select objects that are hidden (either by themselves or by being in a hidden layer)"));
2647 _page_select.add_line( true, "", _sel_locked, "",
2648 _("Uncheck this to be able to select objects that are locked (either by themselves or by being in a locked layer)"));
2649 _page_select.add_line( true, "", _sel_inlayer_same, "",
2650 _("Check this to make the 'select same' functions work like the select all functions, restricting to current layer only."));
2651
2652 _sel_cycle.init ( _("Wrap when cycling objects in z-order"), "/options/selection/cycleWrap", true);
2653
2654 _page_select.add_group_header( _("Alt+Scroll Wheel"));
2655 _page_select.add_line( true, "", _sel_cycle, "",
2656 _("Wrap around at start and end when cycling objects in z-order"));
2657
2658 auto const paste_above_selected = Gtk::make_managed<UI::Widget::PrefCheckButton>();
2659 paste_above_selected->init(_("Paste above selection instead of layer-top"), "/options/paste/aboveselected", true);
2660 _page_select.add_line(false, "", *paste_above_selected, "",
2661 _("If checked, pasted items and imported documents will be placed immediately above the "
2662 "current selection (z-order). Otherwise, insertion happens on top of all objects in the current layer."));
2663
2664 this->AddPage(_page_select, _("Selecting"), iter_behavior, PREFS_PAGE_BEHAVIOR_SELECTING);
2665
2666 // Transforms options
2667 _trans_scale_stroke.init ( _("Scale stroke width"), "/options/transform/stroke", true);
2668 _trans_scale_corner.init ( _("Scale rounded corners in rectangles"), "/options/transform/rectcorners", false);
2669 _trans_gradient.init ( _("Transform gradients"), "/options/transform/gradient", true);
2670 _trans_pattern.init ( _("Transform patterns"), "/options/transform/pattern", false);
2671 _trans_dash_scale.init(_("Scale dashes with stroke"), "/options/dash/scale", true);
2672 _trans_optimized.init ( _("Optimized"), "/options/preservetransform/value", 0, true, nullptr);
2673 _trans_preserved.init ( _("Preserved"), "/options/preservetransform/value", 1, false, &_trans_optimized);
2674
2675 _page_transforms.add_line( false, "", _trans_scale_stroke, "",
2676 _("When scaling objects, scale the stroke width by the same proportion"));
2677 _page_transforms.add_line( false, "", _trans_scale_corner, "",
2678 _("When scaling rectangles, scale the radii of rounded corners"));
2679 _page_transforms.add_line( false, "", _trans_gradient, "",
2680 _("Move gradients (in fill or stroke) along with the objects"));
2681 _page_transforms.add_line( false, "", _trans_pattern, "",
2682 _("Move patterns (in fill or stroke) along with the objects"));
2683 _page_transforms.add_line(false, "", _trans_dash_scale, "", _("When changing stroke width, scale dash array"));
2684 _page_transforms.add_group_header( _("Store transformation"));
2685 _page_transforms.add_line( true, "", _trans_optimized, "",
2686 _("If possible, apply transformation to objects without adding a transform= attribute"));
2687 _page_transforms.add_line( true, "", _trans_preserved, "",
2688 _("Always store transformation as a transform= attribute on objects"));
2689
2690 this->AddPage(_page_transforms, _("Transforms"), iter_behavior, PREFS_PAGE_BEHAVIOR_TRANSFORMS);
2691
2692 // Scrolling options
2693 _scroll_wheel.init ( "/options/wheelscroll/value", 0.0, 1000.0, 1.0, 1.0, 40.0, true, false);
2694 _page_scrolling.add_line( false, _("Mouse _wheel scrolls by:"), _scroll_wheel, _("pixels"),
2695 _("One mouse wheel notch scrolls by this distance in screen pixels (horizontally with Shift)"), false);
2696 _page_scrolling.add_group_header( _("Ctrl+arrows"));
2697 _scroll_arrow_px.init ( "/options/keyscroll/value", 0.0, 1000.0, 1.0, 1.0, 10.0, true, false);
2698 _page_scrolling.add_line( true, _("Sc_roll by:"), _scroll_arrow_px, _("pixels"),
2699 _("Pressing Ctrl+arrow key scrolls by this distance (in screen pixels)"), false);
2700 _scroll_arrow_acc.init ( "/options/scrollingacceleration/value", 0.0, 5.0, 0.01, 1.0, 0.35, false, false);
2701 _page_scrolling.add_line( true, _("_Acceleration:"), _scroll_arrow_acc, "",
2702 _("Pressing and holding Ctrl+arrow will gradually speed up scrolling (0 for no acceleration)"), false);
2703 _page_scrolling.add_group_header( _("Autoscrolling"));
2704 _scroll_auto_speed.init ( "/options/autoscrollspeed/value", 0.0, 5.0, 0.01, 1.0, 0.7, false, false);
2705 _page_scrolling.add_line( true, _("_Speed:"), _scroll_auto_speed, "",
2706 _("How fast the canvas autoscrolls when you drag beyond canvas edge (0 to turn autoscroll off)"), false);
2707 _scroll_auto_thres.init ( "/options/autoscrolldistance/value", -600.0, 600.0, 1.0, 1.0, -10.0, true, false);
2708 _page_scrolling.add_line( true, _("_Threshold:"), _scroll_auto_thres, _("pixels"),
2709 _("How far (in screen pixels) you need to be from the canvas edge to trigger autoscroll; positive is outside the canvas, negative is within the canvas"), false);
2710 _scroll_space.init ( _("Mouse move pans when Space is pressed"), "/options/spacebarpans/value", true);
2711 _page_scrolling.add_line( true, "", _scroll_space, "",
2712 _("When on, pressing and holding Space and dragging pans canvas"));
2713 this->AddPage(_page_scrolling, _("Scrolling"), iter_behavior, PREFS_PAGE_BEHAVIOR_SCROLLING);
2714
2715 // Snapping options
2716 _page_snapping.add_group_header( _("Snap indicator"));
2717
2718 _snap_indicator.init( _("Enable snap indicator"), "/options/snapindicator/value", true);
2719 _page_snapping.add_line( true, "", _snap_indicator, "",
2720 _("After snapping, a symbol is drawn at the point that has snapped"));
2721 _snap_indicator.changed_signal.connect( sigc::mem_fun(_snap_persistence, &Gtk::Widget::set_sensitive) );
2722
2723 _snap_indicator_distance.init( _("Show snap distance in case of alignment or distribution snap"), "/options/snapindicatordistance/value", false);
2724 _page_snapping.add_line( true, "", _snap_indicator_distance, "",
2725 _("Show snap distance in case of alignment or distribution snap"));
2726
2727 _snap_persistence.init("/options/snapindicatorpersistence/value", 0.1, 10, 0.1, 1, 2, 1);
2728 _page_snapping.add_line( true, _("Snap indicator persistence (in seconds):"), _snap_persistence, "",
2729 _("Controls how long the snap indicator message will be shown, before it disappears"), true);
2730
2731
2732 _page_snapping.add_group_header( _("What should snap"));
2733
2734 _snap_closest_only.init( _("Only snap the node closest to the pointer"), "/options/snapclosestonly/value", false);
2735 _page_snapping.add_line( true, "", _snap_closest_only, "",
2736 _("Only try to snap the node that is initially closest to the mouse pointer"));
2737
2738 _snap_mouse_pointer.init( _("Snap the mouse pointer when dragging a constrained knot"), "/options/snapmousepointer/value", false);
2739 _page_snapping.add_line( true, "", _snap_mouse_pointer, "",
2740 _("When dragging a knot along a constraint line, then snap the position of the mouse pointer instead of snapping the projection of the knot onto the constraint line"));
2741
2742 _snap_weight.init("/options/snapweight/value", 0, 1, 0.1, 0.2, 0.5, 1);
2743 _page_snapping.add_line( true, _("_Weight factor:"), _snap_weight, "",
2744 _("When multiple snap solutions are found, then Inkscape can either prefer the closest transformation (when set to 0), or prefer the node that was initially the closest to the pointer (when set to 1)"), true);
2745
2746 _page_snapping.add_group_header( _("Delayed snap"));
2747
2748 _snap_delay.init("/options/snapdelay/value", 0, 1, 0.1, 0.2, 0, 1);
2749 _page_snapping.add_line( true, _("Delay (in seconds):"), _snap_delay, "",
2750 _("Postpone snapping as long as the mouse is moving, and then wait an additional fraction of a second. This additional delay is specified here. When set to zero or to a very small number, snapping will be immediate."), true);
2751
2752 _page_snapping.add_group_header( _("Restrict Snap Targets"));
2753
2754 _snap_always_grid.init(_("Always snap to grids"), "/options/snap/grid/always", false);
2755 _page_snapping.add_line(true, "", _snap_always_grid, "", _("When a grid is visible, and snapping to grids is active, other snap targets will be ignored, unless explicitly allowed below."));
2756
2757 _snap_always_guide.init(_("Always snap to guides"), "/options/snap/guide/always", false);
2758 _page_snapping.add_line(true, "", _snap_always_guide, "", _("When there are any guidelines in the current viewport, and snapping to guides is active, other snap targets will be ignored, unless explicitly allowed below."));
2759
2760 _page_snapping.add_group_header( _("While Always Snapping to Grid/Guides"));
2761
2762 _snap_always_object.init(_("Allow snapping to objects"), "/options/snap/object/always", false);
2763 _page_snapping.add_line(true, "", _snap_always_object, "", _("Allow snapping to objects while 'Always snap to grids / guides' is active, if an object is closer."));
2764
2765 _snap_always_align.init(_("Allow alignment snapping"), "/options/snap/alignment/always", false);
2766 _page_snapping.add_line(true, "", _snap_always_align, "", _("Allow alignment snapping while 'Always snap to grids / guides' is active, if an alignment snap target is closer."));
2767
2768 _snap_always_dist.init(_("Allow distribution snapping"), "/options/snap/distribution/always", false);
2769 _page_snapping.add_line(true, "", _snap_always_dist, "", _("Allow distribution snapping while 'Always snap to grids / guides' is active, if a distribution snap target is closer."));
2770
2771 this->AddPage(_page_snapping, _("Snapping"), iter_behavior, PREFS_PAGE_BEHAVIOR_SNAPPING);
2772
2773 // Steps options
2774
2775 //nudgedistance is limited to 1000 in select-context.cpp: use the same limit here
2776 _steps_arrow.init ( "/options/nudgedistance/value", 0.0, 1000.0, 0.01, 2.0, UNIT_TYPE_LINEAR, "px");
2777 _page_steps.add_line( false, _("_Arrow keys move by:"), _steps_arrow, "",
2778 _("Pressing an arrow key moves selected object(s) or node(s) by this distance"), false);
2779
2780 //defaultscale is limited to 1000 in select-context.cpp: use the same limit here
2781 _steps_scale.init ( "/options/defaultscale/value", 0.0, 1000.0, 0.01, 2.0, UNIT_TYPE_LINEAR, "px");
2782 _page_steps.add_line( false, _("&gt; and &lt; _scale by:"), _steps_scale, "",
2783 _("Pressing > or < scales selection up or down by this increment"), false);
2784
2785 _steps_inset.init ( "/options/defaultoffsetwidth/value", 0.0, 3000.0, 0.01, 2.0, UNIT_TYPE_LINEAR, "px");
2786 _page_steps.add_line( false, _("_Inset/Outset by:"), _steps_inset, "",
2787 _("Inset and Outset commands displace the path by this distance"), false);
2788
2789 _steps_compass.init ( _("Compass-like display of angles"), "/options/compassangledisplay/value", true);
2790 _page_steps.add_line( false, "", _steps_compass, "",
2791 _("When on, angles are displayed with 0 at north, 0 to 360 range, positive clockwise; otherwise with 0 at east, -180 to 180 range, positive counterclockwise"));
2792
2793 {
2794 Glib::ustring const labels[] = {"90", "60", "45", "36", "30", "22.5", "18", "15", "12", "10",
2795 "7.5", "6", "5", "3", "2", "1", "0.5", C_("Rotation angle", "None")};
2796 int const values[] = {2, 3, 4, 5, 6, 8, 10, 12, 15, 18, 24, 30, 36, 60, 90, 180, 360, 0};
2797 _steps_rot_snap.init("/options/rotationsnapsperpi/value", labels, values, 12);
2798 }
2799 _steps_rot_snap.set_size_request(_sb_width);
2800 _page_steps.add_line( false, _("_Rotation snaps every:"), _steps_rot_snap, _("degrees"),
2801 _("Rotating with Ctrl pressed snaps every that much degrees; also, pressing [ or ] rotates by this amount"), false);
2802
2803 _steps_rot_relative.init ( _("Relative snapping of guideline angles"), "/options/relativeguiderotationsnap/value", false);
2804 _page_steps.add_line( false, "", _steps_rot_relative, "",
2805 _("When on, the snap angles when rotating a guideline will be relative to the original angle"));
2806
2807 _steps_zoom.init ( "/options/zoomincrement/value", 101.0, 500.0, 1.0, 1.0, M_SQRT2, true, true);
2808 _page_steps.add_line( false, _("_Zoom in/out by:"), _steps_zoom, _("%"),
2809 _("Zoom tool click, +/- keys, and middle click zoom in and out by this multiplier"), false);
2810
2811 _middle_mouse_zoom.init ( _("Zoom with middle mouse click"), "/options/middlemousezoom/value", true);
2812 _page_steps.add_line( true, "", _middle_mouse_zoom, "",
2813 _("When activated, clicking the middle mouse button (usually the mouse wheel) zooms."));
2814
2815 _page_steps.add_group_header( _("Canvas rotation"));
2816
2817 _steps_rotate.init ( "/options/rotateincrement/value", 1, 90, 1.0, 5.0, 15, false, false);
2818 _page_steps.add_line( false, _("_Rotate canvas by:"), _steps_rotate, _("degrees"),
2819 _("Rotate canvas clockwise and counter-clockwise by this amount."), false);
2820
2821 _move_rotated.init ( _("Arrow keys move object relative to screen"), "/options/moverotated/value", true);
2822 _page_steps.add_line( false, "", _move_rotated, "",
2823 _("When on, arrow keys move objects relative to screen. When the canvas is rotated, the selection will then still be moved horizontally and vertically relative to the screen, not to the rotated document."));
2824
2825 this->AddPage(_page_steps, _("Steps"), iter_behavior, PREFS_PAGE_BEHAVIOR_STEPS);
2826
2827 // Clones options
2828 _clone_option_parallel.init ( _("Move in parallel"), "/options/clonecompensation/value",
2829 SP_CLONE_COMPENSATION_PARALLEL, true, nullptr);
2830 _clone_option_stay.init ( _("Stay unmoved"), "/options/clonecompensation/value",
2831 SP_CLONE_COMPENSATION_UNMOVED, false, &_clone_option_parallel);
2832 _clone_option_transform.init ( _("Move according to transform"), "/options/clonecompensation/value",
2833 SP_CLONE_COMPENSATION_NONE, false, &_clone_option_parallel);
2834 _clone_option_unlink.init ( _("Are unlinked"), "/options/cloneorphans/value",
2835 SP_CLONE_ORPHANS_UNLINK, true, nullptr);
2836 _clone_option_delete.init ( _("Are deleted"), "/options/cloneorphans/value",
2837 SP_CLONE_ORPHANS_DELETE, false, &_clone_option_unlink);
2838 _clone_option_keep.init ( _("Become orphans"), "/options/cloneorphans/value",
2839 SP_CLONE_ORPHANS_KEEP, false, &_clone_option_unlink);
2840
2841 _page_clones.add_group_header( _("Moving original: clones and linked offsets"));
2842 _page_clones.add_line(true, "", _clone_option_parallel, "",
2843 _("Clones are translated by the same vector as their original"));
2844 _page_clones.add_line(true, "", _clone_option_stay, "",
2845 _("Clones preserve their positions when their original is moved"));
2846 _page_clones.add_line(true, "", _clone_option_transform, "",
2847 _("Each clone moves according to the value of its transform= attribute; for example, a rotated clone will move in a different direction than its original"));
2848 _page_clones.add_group_header( _("Deleting original: clones"));
2849 _page_clones.add_line(true, "", _clone_option_unlink, "",
2850 _("Orphaned clones are converted to regular objects"));
2851 _page_clones.add_line(true, "", _clone_option_delete, "",
2852 _("Orphaned clones are deleted along with their original"));
2853 _page_clones.add_line(true, "", _clone_option_keep, "",
2854 _("Orphaned clones are not modified"));
2855
2856 _page_clones.add_group_header( _("Duplicating original+clones/linked offset"));
2857
2858 _clone_relink_on_duplicate.init ( _("Relink duplicated clones"), "/options/relinkclonesonduplicate/value", false);
2859 _page_clones.add_line(true, "", _clone_relink_on_duplicate, "",
2860 _("When duplicating a selection containing both a clone and its original (possibly in groups), relink the duplicated clone to the duplicated original instead of the old original"));
2861
2862 _page_clones.add_group_header( _("Unlinking clones"));
2863 _clone_to_curves.init ( _("Path operations unlink clones"), "/options/pathoperationsunlink/value", true);
2864 _page_clones.add_line(true, "", _clone_to_curves, "",
2865 _("The following path operations will unlink clones: Stroke to path, Object to path, Boolean operations, Combine, Break apart"));
2866 _clone_ignore_to_curves.init ( _("'Object to Path' only unlinks (keeps LPEs, shapes)"), "/options/clonestocurvesjustunlink/value", true);
2867 _page_clones.add_line(true, "", _clone_ignore_to_curves, "",
2868 _("'Object to path' only unlinks clones when they are converted to paths, but preserves any LPEs and shapes within the clones."));
2869 //TRANSLATORS: Heading for the Inkscape Preferences "Clones" Page
2870 this->AddPage(_page_clones, _("Clones"), iter_behavior, PREFS_PAGE_BEHAVIOR_CLONES);
2871
2872 // Clip paths and masks options
2873 _mask_mask_on_top.init ( _("When applying, use the topmost selected object as clippath/mask"), "/options/maskobject/topmost", true);
2874 _page_mask.add_line(false, "", _mask_mask_on_top, "",
2875 _("Uncheck this to use the bottom selected object as the clipping path or mask"));
2876 _mask_mask_on_ungroup.init ( _("When ungrouping, clips/masks are preserved in children"), "/options/maskobject/maskonungroup", true);
2877 _page_mask.add_line(false, "", _mask_mask_on_ungroup, "",
2878 _("Uncheck this to remove clip/mask on ungroup"));
2879 _mask_mask_remove.init ( _("Remove clippath/mask object after applying"), "/options/maskobject/remove", true);
2880 _page_mask.add_line(false, "", _mask_mask_remove, "",
2881 _("After applying, remove the object used as the clipping path or mask from the drawing"));
2882
2883 _page_mask.add_group_header( _("Before applying"));
2884
2885 _mask_grouping_none.init( _("Do not group clipped/masked objects"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE, true, nullptr);
2886 _mask_grouping_separate.init( _("Put every clipped/masked object in its own group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_SEPARATE, false, &_mask_grouping_none);
2887 _mask_grouping_all.init( _("Put all clipped/masked objects into one group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_ALL, false, &_mask_grouping_none);
2888
2889 _page_mask.add_line(true, "", _mask_grouping_none, "",
2890 _("Apply clippath/mask to every object"));
2891
2892 _page_mask.add_line(true, "", _mask_grouping_separate, "",
2893 _("Apply clippath/mask to groups containing single object"));
2894
2895 _page_mask.add_line(true, "", _mask_grouping_all, "",
2896 _("Apply clippath/mask to group containing all objects"));
2897
2898 _page_mask.add_group_header( _("After releasing"));
2899
2900 _mask_ungrouping.init ( _("Ungroup automatically created groups"), "/options/maskobject/ungrouping", true);
2901 _page_mask.add_line(true, "", _mask_ungrouping, "",
2902 _("Ungroup groups created when setting clip/mask"));
2903
2904 this->AddPage(_page_mask, _("Clippaths and masks"), iter_behavior, PREFS_PAGE_BEHAVIOR_MASKS);
2905
2906 // Markers options
2907 _page_markers.add_group_header( _("Stroke Style Markers"));
2908 _page_markers.add_line( true, "", _markers_color_stock, "",
2909 _("Stroke color same as object, fill color either object fill color or marker fill color"));
2910 _page_markers.add_line( true, "", _markers_color_custom, "",
2911 _("Stroke color same as object, fill color either object fill color or marker fill color"));
2912 _page_markers.add_line( true, "", _markers_color_update, "",
2913 _("Update marker color when object color changes"));
2914
2915 this->AddPage(_page_markers, _("Markers"), iter_behavior, PREFS_PAGE_BEHAVIOR_MARKERS);
2916
2917 // Clipboard options
2918 _clipboard_style_computed.init(_("Copy computed style"), "/options/copycomputedstyle/value", 1, true, nullptr);
2919 _clipboard_style_verbatim.init(_("Copy class and style attributes verbatim"), "/options/copycomputedstyle/value", 0,
2920 false, &_clipboard_style_computed);
2921
2922 _page_clipboard.add_group_header(_("Copying objects to the clipboard"));
2923 _page_clipboard.add_line(true, "", _clipboard_style_computed, "",
2924 _("The object's 'style' attribute will be set to the computed style, "
2925 "preserving the object's appearance as in previous Inkscape versions"));
2926 _page_clipboard.add_line(
2927 true, "", _clipboard_style_verbatim, "",
2928 _("The object's 'style' and 'class' values will be copied verbatim, and will replace those of the target object when using 'Paste style'"));
2929
2930 this->AddPage(_page_clipboard, _("Clipboard"), iter_behavior, PREFS_PAGE_BEHAVIOR_CLIPBOARD);
2931
2932 // Document cleanup options
2933 _page_cleanup.add_group_header( _("Document cleanup"));
2934 _cleanup_swatches.init ( _("Remove unused swatches when doing a document cleanup"), "/options/cleanupswatches/value", false); // text label
2935 _page_cleanup.add_line( true, "", _cleanup_swatches, "",
2936 _("Remove unused swatches when doing a document cleanup")); // tooltip
2937 this->AddPage(_page_cleanup, _("Cleanup"), iter_behavior, PREFS_PAGE_BEHAVIOR_CLEANUP);
2938 _page_lpe.add_group_header( _("General"));
2939 _lpe_show_experimental.init ( _("Show experimental effects"), "/dialogs/livepatheffect/showexperimental", false); // text label
2940 _page_lpe.add_line( true, "", _lpe_show_experimental, "",
2941 _("Show experimental effects")); // tooltip
2942 _page_lpe.add_group_header( _("Tiling"));
2943 _lpe_copy_mirroricons.init ( _("Add advanced tiling options"), "/live_effects/copy/mirroricons", true); // text label
2944 _page_lpe.add_line( true, "", _lpe_copy_mirroricons, "",
2945 _("Enables using 16 advanced mirror options between the copies (so there can be copies that are mirrored differently between the rows and the columns) for Tiling LPE")); // tooltip
2946 this->AddPage(_page_lpe, _("Live Path Effects (LPE)"), iter_behavior, PREFS_PAGE_BEHAVIOR_LPE);
2947}
2948
2949void InkscapePreferences::initPageRendering()
2950{
2951 // render threads
2952 _filter_multi_threaded.init("/options/threading/numthreads", 0.0, 32.0, 1.0, 2.0, 0.0, true, false);
2953 _page_rendering.add_line(false, _("Number of _Threads:"), _filter_multi_threaded, "", _("Configure number of threads to use when rendering. The default value of zero means choose automatically."), false);
2954
2955 // rendering cache
2956 _rendering_cache_size.init("/options/renderingcache/size", 0.0, 4096.0, 1.0, 32.0, 64.0, true, false);
2957 _page_rendering.add_line( false, _("Rendering _cache size:"), _rendering_cache_size, C_("mebibyte (2^20 bytes) abbreviation","MiB"), _("Set the amount of memory per document which can be used to store rendered parts of the drawing for later reuse; set to zero to disable caching"), false);
2958
2959 // rendering x-ray radius
2960 _rendering_xray_radius.init("/options/rendering/xray-radius", 1.0, 1500.0, 1.0, 100.0, 100.0, true, false);
2961 _page_rendering.add_line( false, _("X-ray radius:"), _rendering_xray_radius, "", _("Radius of the circular area around the mouse cursor in X-ray mode"), false);
2962
2963 // rendering outline overlay opacity
2964 _rendering_outline_overlay_opacity.init("/options/rendering/outline-overlay-opacity", 0.0, 100.0, 1.0, 5.0, 50.0, true, false);
2965 _page_rendering.add_line( false, _("Outline overlay opacity:"), _rendering_outline_overlay_opacity, _("%"), _("Opacity of the overlay in outline overlay view mode"), false);
2966
2967 // update strategy
2968 {
2969 constexpr int values[] = { 1, 2, 3 };
2970 Glib::ustring const labels[] = { _("Responsive"), _("Full redraw"), _("Multiscale") };
2971 _canvas_update_strategy.init("/options/rendering/update_strategy", labels, values, 3);
2972 _page_rendering.add_line(false, _("Update strategy:"), _canvas_update_strategy, "", _("How to update continually changing content when it can't be redrawn fast enough"), false);
2973 }
2974
2975 // opengl
2976 _canvas_request_opengl.init(_("Enable OpenGL"), "/options/rendering/request_opengl", false);
2977 _page_rendering.add_line(false, "", _canvas_request_opengl, "", _("Request that the canvas should be painted with OpenGL rather than Cairo. If OpenGL is unsupported, it will fall back to Cairo."), false);
2978
2979 // blur quality
2980 _blur_quality_best.init ( _("Best quality (slowest)"), "/options/blurquality/value",
2981 BLUR_QUALITY_BEST, false, nullptr);
2982 _blur_quality_better.init ( _("Better quality (slower)"), "/options/blurquality/value",
2983 BLUR_QUALITY_BETTER, false, &_blur_quality_best);
2984 _blur_quality_normal.init ( _("Average quality"), "/options/blurquality/value",
2985 BLUR_QUALITY_NORMAL, true, &_blur_quality_best);
2986 _blur_quality_worse.init ( _("Lower quality (faster)"), "/options/blurquality/value",
2987 BLUR_QUALITY_WORSE, false, &_blur_quality_best);
2988 _blur_quality_worst.init ( _("Lowest quality (fastest)"), "/options/blurquality/value",
2989 BLUR_QUALITY_WORST, false, &_blur_quality_best);
2990
2991 _page_rendering.add_group_header( _("Gaussian blur quality for display"));
2992 _page_rendering.add_line( true, "", _blur_quality_best, "",
2993 _("Best quality, but display may be very slow at high zooms (bitmap export always uses best quality)"));
2994 _page_rendering.add_line( true, "", _blur_quality_better, "",
2995 _("Better quality, but slower display"));
2996 _page_rendering.add_line( true, "", _blur_quality_normal, "",
2997 _("Average quality, acceptable display speed"));
2998 _page_rendering.add_line( true, "", _blur_quality_worse, "",
2999 _("Lower quality (some artifacts), but display is faster"));
3000 _page_rendering.add_line( true, "", _blur_quality_worst, "",
3001 _("Lowest quality (considerable artifacts), but display is fastest"));
3002
3003 // filter quality
3004 _filter_quality_best.init ( _("Best quality (slowest)"), "/options/filterquality/value",
3006 _filter_quality_better.init ( _("Better quality (slower)"), "/options/filterquality/value",
3007 Inkscape::Filters::FILTER_QUALITY_BETTER, false, &_filter_quality_best);
3008 _filter_quality_normal.init ( _("Average quality"), "/options/filterquality/value",
3009 Inkscape::Filters::FILTER_QUALITY_NORMAL, true, &_filter_quality_best);
3010 _filter_quality_worse.init ( _("Lower quality (faster)"), "/options/filterquality/value",
3011 Inkscape::Filters::FILTER_QUALITY_WORSE, false, &_filter_quality_best);
3012 _filter_quality_worst.init ( _("Lowest quality (fastest)"), "/options/filterquality/value",
3013 Inkscape::Filters::FILTER_QUALITY_WORST, false, &_filter_quality_best);
3014
3015 _page_rendering.add_group_header( _("Filter effects quality for display"));
3016 _page_rendering.add_line( true, "", _filter_quality_best, "",
3017 _("Best quality, but display may be very slow at high zooms (bitmap export always uses best quality)"));
3018 _page_rendering.add_line( true, "", _filter_quality_better, "",
3019 _("Better quality, but slower display"));
3020 _page_rendering.add_line( true, "", _filter_quality_normal, "",
3021 _("Average quality, acceptable display speed"));
3022 _page_rendering.add_line( true, "", _filter_quality_worse, "",
3023 _("Lower quality (some artifacts), but display is faster"));
3024 _page_rendering.add_line( true, "", _filter_quality_worst, "",
3025 _("Lowest quality (considerable artifacts), but display is fastest"));
3026
3027#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0)
3028 _cairo_dithering.init(_("Use dithering"), "/options/dithering/value", true);
3029 _page_rendering.add_line(false, "", _cairo_dithering, "", _("Makes gradients smoother. This can significantly impact the size of generated PNG files."));
3030#endif
3031
3032 auto const grid = Gtk::make_managed<Gtk::Grid>();
3033 grid->set_margin(12);
3034 grid->set_orientation(Gtk::Orientation::VERTICAL);
3035 grid->set_column_spacing(12);
3036 grid->set_row_spacing(6);
3037
3038 auto const revealer = Gtk::make_managed<Gtk::Revealer>();
3039 revealer->set_child(*grid);
3040 revealer->set_reveal_child(Inkscape::Preferences::get()->getBool("/options/rendering/devmode"));
3041
3042 _canvas_developer_mode_enabled.init(_("Enable developer mode"), "/options/rendering/devmode", false);
3043 _canvas_developer_mode_enabled.signal_toggled().connect([revealer, this] { revealer->set_reveal_child(_canvas_developer_mode_enabled.get_active()); });
3044
3045 _page_rendering.add_group_header(_("Developer mode"));
3046 _page_rendering.add_line(true, "", _canvas_developer_mode_enabled, "", _("Enable additional debugging options"), false);
3047 _page_rendering.attach_next_to(*revealer, Gtk::PositionType::BOTTOM);
3048
3049 auto add_devmode_line = [&] (Glib::ustring const &label, Gtk::Widget &widget, Glib::ustring const &suffix, Glib::ustring const &tip) {
3050 widget.set_tooltip_text(tip);
3051
3052 auto const hb = Gtk::make_managed<Gtk::Box>();
3053 hb->set_spacing(12);
3054 hb->set_hexpand(true);
3055 UI::pack_start(*hb, widget, false, false);
3056 hb->set_valign(Gtk::Align::CENTER);
3057
3058 auto const label_widget = Gtk::make_managed<Gtk::Label>(label, Gtk::Align::START, Gtk::Align::CENTER, true);
3059 label_widget->set_mnemonic_widget(widget);
3060 label_widget->set_markup(label_widget->get_text());
3061 label_widget->set_margin_start(12);
3062
3063 label_widget->set_valign(Gtk::Align::CENTER);
3064 grid->attach_next_to(*label_widget, Gtk::PositionType::BOTTOM);
3065 grid->attach_next_to(*hb, *label_widget, Gtk::PositionType::RIGHT, 1, 1);
3066
3067 if (!suffix.empty()) {
3068 auto const suffix_widget = Gtk::make_managed<Gtk::Label>(suffix, Gtk::Align::START, Gtk::Align::CENTER, true);
3069 suffix_widget->set_markup(suffix_widget->get_text());
3070 UI::pack_start(*hb, *suffix_widget, false, false);
3071 }
3072 };
3073
3074 auto add_devmode_group_header = [&] (Glib::ustring const &name) {
3075 auto const label_widget = Gtk::make_managed<Gtk::Label>(Glib::ustring(/*"<span size='large'>*/"<b>") + name + Glib::ustring("</b>"/*</span>"*/) , Gtk::Align::START , Gtk::Align::CENTER, true);
3076 label_widget->set_use_markup(true);
3077 label_widget->set_valign(Gtk::Align::CENTER);
3078 grid->attach_next_to(*label_widget, Gtk::PositionType::BOTTOM);
3079 };
3080
3081 //TRANSLATORS: The following are options for fine-tuning rendering, meant to be used by developers,
3082 //find more explanations at https://gitlab.com/inkscape/inbox/-/issues/6544#note_886540227
3083 add_devmode_group_header(_("Low-level tuning options"));
3084 _canvas_tile_size.init("/options/rendering/tile_size", 1.0, 10000.0, 1.0, 0.0, 300.0, true, false);
3085 add_devmode_line(_("Tile size"), _canvas_tile_size, "", _("Halve rendering tile rectangles until their largest dimension is this small"));
3086 _canvas_render_time_limit.init("/options/rendering/render_time_limit", 1.0, 5000.0, 1.0, 0.0, 80.0, true, false);
3087 add_devmode_line(_("Render time limit"), _canvas_render_time_limit, C_("millisecond abbreviation", "ms"), _("The maximum time allowed for a rendering time slice"));
3088 {
3089 constexpr int values[] = { 1, 2, 3, 4 };
3090 Glib::ustring const labels[] = { _("Auto"), _("Persistent"), _("Asynchronous"), _("Synchronous") };
3091 _canvas_pixelstreamer_method.init("/options/rendering/pixelstreamer_method", labels, values, 1);
3092 add_devmode_line(_("Pixel streaming method"), _canvas_pixelstreamer_method, "", _("Change the method used for streaming pixel data to the GPU. The default is Auto, which picks the best method available at runtime. As for the other options, higher up is better."));
3093 }
3094 _canvas_padding.init("/options/rendering/padding", 0.0, 1000.0, 1.0, 0.0, 350.0, true, false);
3095 add_devmode_line(_("Buffer padding"), _canvas_padding, C_("pixel abbreviation", "px"), _("Use buffers bigger than the window by this amount"));
3096 _canvas_prerender.init("/options/rendering/prerender", 0.0, 1000.0, 1.0, 0.0, 100.0, true, false);
3097 add_devmode_line(_("Prerender margin"), _canvas_prerender, "", _("Pre-render a margin around the visible region."));
3098 _canvas_preempt.init("/options/rendering/preempt", 0.0, 1000.0, 1.0, 0.0, 250.0, true, false);
3099 add_devmode_line(_("Preempt size"), _canvas_preempt, "", _("Prevent thin tiles at the rendering edge by making them at least this size."));
3100 _canvas_coarsener_min_size.init("/options/rendering/coarsener_min_size", 0.0, 1000.0, 1.0, 0.0, 200.0, true, false);
3101 add_devmode_line(_("Min size for coarsener algorithm"), _canvas_coarsener_min_size, C_("pixel abbreviation", "px"), _("Coarsener algorithm only processes rectangles smaller/thinner than this."));
3102 _canvas_coarsener_glue_size.init("/options/rendering/coarsener_glue_size", 0.0, 1000.0, 1.0, 0.0, 80.0, true, false);
3103 add_devmode_line(_("Glue size for coarsener algorithm"), _canvas_coarsener_glue_size, C_("pixel abbreviation", "px"), _("Coarsener algorithm absorbs nearby rectangles within this distance."));
3104 _canvas_coarsener_min_fullness.init("/options/rendering/coarsener_min_fullness", 0.0, 1.0, 0.0, 0.0, 0.3, false, false);
3105 add_devmode_line(_("Min fullness for coarsener algorithm"), _canvas_coarsener_min_fullness, "", _("Refuse coarsening algorithm's attempt if the result would be more empty than this."));
3106
3107 add_devmode_group_header(_("Debugging, profiling and experiments"));
3108 _canvas_debug_framecheck.init("", "/options/rendering/debug_framecheck", false);
3109 add_devmode_line(_("Framecheck"), _canvas_debug_framecheck, "", _("Print profiling data of selected operations to a file"));
3110 _canvas_debug_logging.init("", "/options/rendering/debug_logging", false);
3111 add_devmode_line(_("Logging"), _canvas_debug_logging, "", _("Log certain events to the console"));
3112 _canvas_debug_delay_redraw.init("", "/options/rendering/debug_delay_redraw", false);
3113 add_devmode_line(_("Delay redraw"), _canvas_debug_delay_redraw, "", _("Introduce a fixed delay for each tile"));
3114 _canvas_debug_delay_redraw_time.init("/options/rendering/debug_delay_redraw_time", 0.0, 1000000.0, 1.0, 0.0, 50.0, true, false);
3115 add_devmode_line(_("Delay redraw time"), _canvas_debug_delay_redraw_time, C_("microsecond abbreviation", "μs"), _("The delay to introduce for each tile"));
3116 _canvas_debug_show_redraw.init("", "/options/rendering/debug_show_redraw", false);
3117 add_devmode_line(_("Show redraw"), _canvas_debug_show_redraw, "", _("Paint a translucent random colour over each newly drawn tile"));
3118 _canvas_debug_show_unclean.init("", "/options/rendering/debug_show_unclean", false);
3119 add_devmode_line(_("Show unclean region"), _canvas_debug_show_unclean, "", _("Show the region that needs to be redrawn in red (only in Cairo mode)"));
3120 _canvas_debug_show_snapshot.init("", "/options/rendering/debug_show_snapshot", false);
3121 add_devmode_line(_("Show snapshot region"), _canvas_debug_show_snapshot, "", _("Show the region that still contains a saved copy of previously rendered content in blue (only in Cairo mode)"));
3122 _canvas_debug_show_clean.init("", "/options/rendering/debug_show_clean", false);
3123 add_devmode_line(_("Show clean region's fragmentation"), _canvas_debug_show_clean, "", _("Show the outlines of the rectangles in the region where rendering is complete in green (only in Cairo mode)"));
3124 _canvas_debug_disable_redraw.init("", "/options/rendering/debug_disable_redraw", false);
3125 add_devmode_line(_("Disable redraw"), _canvas_debug_disable_redraw, "", _("Temporarily disable the idle redraw process completely"));
3126 _canvas_debug_sticky_decoupled.init("", "/options/rendering/debug_sticky_decoupled", false);
3127 add_devmode_line(_("Sticky decoupled mode"), _canvas_debug_sticky_decoupled, "", _("Stay in decoupled mode even after rendering is complete"));
3128 _canvas_debug_animate.init("", "/options/rendering/debug_animate", false);
3129 add_devmode_line(_("Animate"), _canvas_debug_animate, "", _("Continuously adjust viewing parameters in an animation loop."));
3130
3131 AddPage(_page_rendering, _("Rendering"), PREFS_PAGE_RENDERING);
3132}
3133
3134void InkscapePreferences::initPageBitmaps()
3135{
3136 /* Note: /options/bitmapoversample removed with Cairo renderer */
3137 _page_bitmaps.add_group_header( _("Edit"));
3138 _misc_bitmap_autoreload.init(_("Automatically reload images"), "/options/bitmapautoreload/value", true);
3139 _page_bitmaps.add_line( false, "", _misc_bitmap_autoreload, "",
3140 _("Automatically reload linked images when file is changed on disk"));
3141 _misc_bitmap_editor.init("/options/bitmapeditor/value", true);
3142 _page_bitmaps.add_line( false, _("_Bitmap editor:"), _misc_bitmap_editor, "", "", true);
3143 _misc_svg_editor.init("/options/svgeditor/value", true);
3144 _page_bitmaps.add_line( false, _("_SVG editor:"), _misc_svg_editor, "", "", true);
3145
3146 _page_bitmaps.add_group_header( _("Export"));
3147 _importexport_export_res.init("/dialogs/export/defaultxdpi/value", 0.0, 6000.0, 1.0, 1.0, Inkscape::Util::Quantity::convert(1, "in", "px"), true, false);
3148 _page_bitmaps.add_line( false, _("Default export _resolution:"), _importexport_export_res, _("dpi"),
3149 _("Default image resolution (in dots per inch) in the Export dialog"), false);
3150 _page_bitmaps.add_group_header( _("Create"));
3151 _bitmap_copy_res.init("/options/createbitmap/resolution", 1.0, 6000.0, 1.0, 1.0, Inkscape::Util::Quantity::convert(1, "in", "px"), true, false);
3152 _page_bitmaps.add_line( false, _("Resolution for Create Bitmap _Copy:"), _bitmap_copy_res, _("dpi"),
3153 _("Resolution used by the Create Bitmap Copy command"), false);
3154
3155 _page_bitmaps.add_group_header( _("Import"));
3156 _bitmap_ask.init(_("Ask about linking and scaling when importing bitmap images"), "/dialogs/import/ask", true);
3157 _page_bitmaps.add_line( true, "", _bitmap_ask, "",
3158 _("Pop-up linking and scaling dialog when importing bitmap image."));
3159 _svg_ask.init(_("Ask about linking and scaling when importing SVG images"), "/dialogs/import/ask_svg", true);
3160 _page_bitmaps.add_line( true, "", _svg_ask, "",
3161 _("Pop-up linking and scaling dialog when importing SVG image."));
3162
3163 _svgoutput_usesodipodiabsref.init(_("Store absolute file path for linked images"),
3164 "/options/svgoutput/usesodipodiabsref", false);
3165 _page_bitmaps.add_line(
3166 true, "", _svgoutput_usesodipodiabsref, "",
3167 _("By default, image links are stored as relative paths whenever possible. If this option is enabled, Inkscape "
3168 "will additionally add an absolute path ('sodipodi:absref' attribute) to the image. This is used as a "
3169 "fall-back for locating the linked image, for example if the SVG document has been moved on disk. Note that this "
3170 "will expose your directory structure in the file's source code, which can include personal information like your username."),
3171 false);
3172
3173 {
3174 Glib::ustring labels[] = {_("Embed"), _("Link")};
3175 Glib::ustring values[] = {"embed", "link"};
3176 _bitmap_link.init("/dialogs/import/link", labels, values, "link");
3177 _page_bitmaps.add_line( false, _("Bitmap import/open mode:"), _bitmap_link, "", "", false);
3178 }
3179
3180 {
3181 Glib::ustring labels[] = {_("Include"), _("Pages"), _("Embed"), _("Link"), _("New")};
3182 Glib::ustring values[] = {"include", "pages", "embed", "link", "new"};
3183 _svg_link.init("/dialogs/import/import_mode_svg", labels, values, "include");
3184 _page_bitmaps.add_line( false, _("SVG import mode:"), _svg_link, "", "", false);
3185 }
3186
3187 {
3188 Glib::ustring labels[] = {_("None (auto)"), _("Smooth (optimizeQuality)"), _("Blocky (optimizeSpeed)") };
3189 Glib::ustring values[] = {"auto", "optimizeQuality", "optimizeSpeed"};
3190 _bitmap_scale.init("/dialogs/import/scale", labels, values, "scale");
3191 _page_bitmaps.add_line( false, _("Image scale (image-rendering):"), _bitmap_scale, "", "", false);
3192 }
3193
3194 /* Note: /dialogs/import/quality removed use of in r12542 */
3195 _importexport_import_res.init("/dialogs/import/defaultxdpi/value", 0.0, 6000.0, 1.0, 1.0, Inkscape::Util::Quantity::convert(1, "in", "px"), true, false);
3196 _page_bitmaps.add_line( false, _("Default _import resolution:"), _importexport_import_res, _("dpi"),
3197 _("Default import resolution (in dots per inch) for bitmap and SVG import"), false);
3198 _importexport_import_res_override.init(_("Override file resolution"), "/dialogs/import/forcexdpi", false);
3199 _page_bitmaps.add_line( false, "", _importexport_import_res_override, "",
3200 _("Use default bitmap resolution in favor of information from file"));
3201
3202 _page_bitmaps.add_group_header( _("Render"));
3203 // rendering outlines for pixmap image tags
3204 _rendering_image_outline.init( _("Images in Outline Mode"), "/options/rendering/imageinoutlinemode", false);
3205 _page_bitmaps.add_line(false, "", _rendering_image_outline, "", _("When active will render images while in outline mode instead of a red box with an x. This is useful for manual tracing."));
3206
3207 this->AddPage(_page_bitmaps, _("Imported Images"), PREFS_PAGE_BITMAPS);
3208}
3209
3210[[nodiscard]] static auto getShortcutsFileLabelsAndValues()
3211{
3213 std::vector<Glib::ustring> labels(pairs.size()), values(pairs.size());
3214 std::transform(pairs.begin(), pairs.end(), labels.begin(), [](auto &&pair){ return std::move(pair.first ); });
3215 std::transform(pairs.begin(), pairs.end(), values.begin(), [](auto &&pair){ return std::move(pair.second); });
3216 return std::pair{std::move(labels), std::move(values)};
3217}
3218
3219void InkscapePreferences::initKeyboardShortcuts(Gtk::TreeModel::iterator iter_ui)
3220{
3221 // ------- Shortcut file --------
3222 {
3223 auto const &[labels, values] = getShortcutsFileLabelsAndValues();
3224 auto const &default_value = values.front();
3225 _kb_filelist.init("/options/kbshortcuts/shortcutfile", labels, values, default_value);
3226
3227 auto tooltip =
3228 Glib::ustring::compose(_("Select a file of predefined shortcuts and modifiers to use. Any customizations you "
3229 "create will be added separately to %1"),
3231 _page_keyshortcuts.add_line( false, _("Keyboard file:"), _kb_filelist, "", tooltip.c_str(), false);
3232 }
3233
3234 // ---------- Tree --------
3235 _kb_store = Gtk::TreeStore::create( _kb_columns );
3236 _kb_store->set_sort_column ( GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, Gtk::SortType::ASCENDING ); // only sort in onKBListKeyboardShortcuts()
3237
3238 _kb_filter = Gtk::TreeModelFilter::create(_kb_store);
3239 _kb_filter->set_visible_func (sigc::mem_fun(*this, &InkscapePreferences::onKBSearchFilter));
3240
3241 _kb_shortcut_renderer.property_editable() = true;
3242
3243 _kb_tree.set_model(_kb_filter);
3244 _kb_tree.append_column(_("Name"), _kb_columns.name);
3245 _kb_tree.append_column(_("Shortcut"), _kb_shortcut_renderer);
3246 _kb_tree.append_column(_("Description"), _kb_columns.description);
3247 _kb_tree.append_column(_("ID"), _kb_columns.id);
3248
3249 _kb_tree.set_expander_column(*_kb_tree.get_column(0));
3250
3251 // Name
3252 _kb_tree.get_column(0)->set_resizable(true);
3253 _kb_tree.get_column(0)->set_clickable(true);
3254 _kb_tree.get_column(0)->set_fixed_width (200);
3255
3256 // Shortcut
3257 _kb_tree.get_column(1)->set_resizable(true);
3258 _kb_tree.get_column(1)->set_clickable(true);
3259 _kb_tree.get_column(1)->set_fixed_width (150);
3260 //_kb_tree.get_column(1)->add_attribute(_kb_shortcut_renderer.property_text(), _kb_columns.shortcut);
3261 _kb_tree.get_column(1)->set_cell_data_func(_kb_shortcut_renderer, &InkscapePreferences::onKBShortcutRenderer);
3262
3263 // Description
3264 auto desc_renderer = dynamic_cast<Gtk::CellRendererText*>(_kb_tree.get_column_cell_renderer(2));
3265 desc_renderer->property_wrap_mode() = Pango::WrapMode::WORD;
3266 desc_renderer->property_wrap_width() = 600;
3267 _kb_tree.get_column(2)->set_resizable(true);
3268 _kb_tree.get_column(2)->set_clickable(true);
3269 _kb_tree.get_column(2)->set_expand(true);
3270
3271 // ID
3272 _kb_tree.get_column(3)->set_resizable(true);
3273 _kb_tree.get_column(3)->set_clickable(true);
3274
3275 _kb_shortcut_renderer.signal_accel_edited().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBTreeEdited) );
3276 _kb_shortcut_renderer.signal_accel_cleared().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBTreeCleared) );
3277
3278 _kb_notebook.append_page(_kb_page_shortcuts, _("Shortcuts"));
3279 auto const shortcut_scroller = Gtk::make_managed<Gtk::ScrolledWindow>();
3280 shortcut_scroller->set_child(_kb_tree);
3281 shortcut_scroller->set_hexpand();
3282 shortcut_scroller->set_vexpand();
3283 // -------- Search --------
3284 _kb_search.init("/options/kbshortcuts/value", true);
3285 // clear filter initially to show all shortcuts;
3286 // this entry will typically be stale and long forgotten and not what user is looking for
3287 _kb_search.set_text({});
3288 _kb_page_shortcuts.add_line( false, _("Search:"), _kb_search, "", "", true);
3289 _kb_page_shortcuts.attach(*shortcut_scroller, 0, 3, 2, 1);
3290
3291 _mod_store = Gtk::TreeStore::create( _mod_columns );
3292 _mod_tree.set_model(_mod_store);
3293 _mod_tree.append_column(_("Name"), _mod_columns.name);
3294 _mod_tree.append_column("hot", _mod_columns.and_modifiers);
3295 _mod_tree.append_column(_("ID"), _mod_columns.id);
3296 _mod_tree.set_tooltip_column(2);
3297
3298 // In order to get tooltips on header, we must create our own label.
3299 auto const and_keys_header = Gtk::make_managed<Gtk::Label>(_("Modifier"));
3300 and_keys_header->set_tooltip_text(_("All keys specified must be held down to activate this functionality."));
3301 and_keys_header->set_visible(true);
3302 auto and_keys_column = _mod_tree.get_column(1);
3303 and_keys_column->set_widget(*and_keys_header);
3304
3305 auto const edit_bar = Gtk::make_managed<Gtk::Box>();
3306 _kb_mod_ctrl.set_label("Ctrl");
3307 _kb_mod_shift.set_label("Shift");
3308 _kb_mod_alt.set_label("Alt");
3309 _kb_mod_meta.set_label("Meta");
3310 _kb_mod_enabled.set_label(_("Enabled"));
3311 edit_bar->append(_kb_mod_ctrl);
3312 edit_bar->append(_kb_mod_shift);
3313 edit_bar->append(_kb_mod_alt);
3314 edit_bar->append(_kb_mod_meta);
3315 edit_bar->append(_kb_mod_enabled);
3316 _kb_mod_ctrl.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::on_modifier_edited));
3317 _kb_mod_shift.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::on_modifier_edited));
3318 _kb_mod_alt.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::on_modifier_edited));
3319 _kb_mod_meta.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::on_modifier_edited));
3320 _kb_mod_enabled.signal_toggled().connect(sigc::mem_fun(*this, &InkscapePreferences::on_modifier_enabled));
3321 _kb_page_modifiers.add_line(false, _("Change:"), *edit_bar, "", "", true);
3322
3323 _mod_tree.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &InkscapePreferences::on_modifier_selection_changed));
3324 on_modifier_selection_changed();
3325
3326 _kb_notebook.append_page(_kb_page_modifiers, _("Tools Modifiers"));
3327 auto const mod_scroller = Gtk::make_managed<Gtk::ScrolledWindow>();
3328 mod_scroller->set_child(_mod_tree);
3329 mod_scroller->set_hexpand();
3330 mod_scroller->set_vexpand();
3331 _kb_page_modifiers.attach(*mod_scroller, 0, 1, 2, 1);
3332
3333 int row = 2;
3334 _page_keyshortcuts.attach(_kb_notebook, 0, row, 2, 1);
3335
3336 row++;
3337
3338 // ------ Reset/Import/Export -------
3339 auto const box_buttons = Gtk::make_managed<Gtk::Box>();
3340
3341 box_buttons->set_spacing(4);
3342 box_buttons->set_hexpand();
3343 _page_keyshortcuts.attach(*box_buttons, 0, row, 3, 1);
3344
3345 auto const kb_reset = Gtk::make_managed<Gtk::Button>(_("Reset"));
3346 kb_reset->set_use_underline();
3347 kb_reset->set_tooltip_text(_("Remove all your customized keyboard shortcuts, and revert to the shortcuts in the shortcut file listed above"));
3348 UI::pack_start(*box_buttons, *kb_reset, false, true, 6);
3349
3350 auto const kb_export = Gtk::make_managed<Gtk::Button>(_("Export ..."));
3351 kb_export->set_use_underline();
3352 kb_export->set_tooltip_text(_("Export custom keyboard shortcuts to a file"));
3353 UI::pack_end(*box_buttons, *kb_export, false, true, 6);
3354
3355 auto const kb_import = Gtk::make_managed<Gtk::Button>(_("Import ..."));
3356 kb_import->set_use_underline();
3357 kb_import->set_tooltip_text(_("Import custom keyboard shortcuts from a file"));
3358 UI::pack_end(*box_buttons, *kb_import, false, true, 6);
3359
3360 kb_reset->signal_clicked().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBReset) );
3361 kb_import->signal_clicked().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBImport) );
3362 kb_export->signal_clicked().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBExport) );
3363 _kb_filelist.signal_changed().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBList) );
3364 _page_keyshortcuts.signal_realize().connect( sigc::mem_fun(*this, &InkscapePreferences::onKBRealize) );
3365
3366 auto const key = Gtk::EventControllerKey::create();
3367 key->signal_key_released().connect([this](auto &&...args) { onKBSearchKeyReleased(); });
3368 _kb_search.add_controller(key);
3369
3370 _keyboard_sizegroup = Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL);
3371 _keyboard_sizegroup->add_widget(*kb_reset);
3372 _keyboard_sizegroup->add_widget(*kb_export);
3373 _keyboard_sizegroup->add_widget(*kb_import);
3374
3375 this->AddPage(_page_keyshortcuts, _("Keyboard Shortcuts"), iter_ui, PREFS_PAGE_UI_KEYBOARD_SHORTCUTS);
3376
3377 _kb_shortcuts_loaded = false;
3378 Gtk::TreeStore::iterator iter_group = _kb_store->append();
3379 (*iter_group)[_kb_columns.name] = _("Loading ...");
3380 (*iter_group)[_kb_columns.shortcut] = "";
3381 (*iter_group)[_kb_columns.id] = "";
3382 (*iter_group)[_kb_columns.description] = "";
3383 (*iter_group)[_kb_columns.shortcutkey] = Gtk::AccelKey();
3384 (*iter_group)[_kb_columns.user_set] = 0;
3385
3386 Gtk::TreeStore::iterator iter_mods = _mod_store->append();
3387 (*iter_mods)[_mod_columns.name] = _("Loading ...");
3388 (*iter_group)[_mod_columns.id] = "";
3389 (*iter_group)[_mod_columns.description] = _("Unable to load keyboard modifier list.");
3390 (*iter_group)[_mod_columns.and_modifiers] = "";
3391}
3392
3393void InkscapePreferences::onKBList()
3394{
3395 // New file path already stored in preferences.
3397 onKBListKeyboardShortcuts();
3398}
3399
3400void InkscapePreferences::onKBReset()
3401{
3403 onKBListKeyboardShortcuts();
3404}
3405
3406void InkscapePreferences::onKBImport()
3407{
3408 if (Inkscape::Shortcuts::getInstance().import_shortcuts()) {
3409 onKBListKeyboardShortcuts();
3410 }
3411}
3412
3413void InkscapePreferences::onKBExport()
3414{
3416}
3417
3418void InkscapePreferences::onKBSearchKeyReleased()
3419{
3420 _kb_filter->refilter();
3421 auto search = _kb_search.get_text();
3422 if (search.length() > 2) {
3423 _kb_tree.expand_all();
3424 } else {
3425 _kb_tree.collapse_all();
3426 }
3427}
3428
3429void InkscapePreferences::onKBTreeCleared(const Glib::ustring& path)
3430{
3431 // Triggered by "Back" key.
3432 Gtk::TreeModel::iterator iter = _kb_filter->get_iter(path);
3433 Glib::ustring id = (*iter)[_kb_columns.id];
3434 // Gtk::AccelKey current_shortcut_key = (*iter)[_kb_columns.shortcutkey];
3435
3436 // Remove current shortcut from user file (won't remove from other files).
3438
3439 onKBListKeyboardShortcuts();
3440}
3441
3442void InkscapePreferences::onKBTreeEdited (const Glib::ustring& path, guint accel_key, Gdk::ModifierType accel_mods, guint hardware_keycode)
3443{
3445 auto const state = static_cast<GdkModifierType>(accel_mods);
3446 auto const new_shortcut_key = shortcuts.get_from(nullptr, accel_key, hardware_keycode,
3447 state, true);
3448 if (new_shortcut_key.is_null()) return;
3449
3450 Gtk::TreeModel::iterator iter = _kb_filter->get_iter(path);
3451 Glib::ustring id = (*iter)[_kb_columns.id];
3452 Gtk::AccelKey const current_shortcut_key = (*iter)[_kb_columns.shortcutkey];
3453
3454 if (new_shortcut_key.get_key() == current_shortcut_key.get_key() &&
3455 new_shortcut_key.get_mod() == current_shortcut_key.get_mod())
3456 {
3457 return;
3458 }
3459
3460 auto iapp = InkscapeApplication::instance();
3461 InkActionExtraData& action_data = iapp->get_action_extra_data();
3462
3463 // Check if there is currently an actions assigned to this shortcut; if yes ask if the shortcut should be reassigned
3464 Glib::ustring action_name;
3465 Glib::ustring accel = Gtk::Accelerator::name(accel_key, accel_mods);
3466 auto const &actions = shortcuts.get_actions(accel);
3467
3468 for (auto possible_action : actions) {
3469 if (action_data.isSameContext(id, possible_action)) {
3470 // TODO: Reformat the data attached here so it's compatible with action_data
3471 action_name = possible_action;
3472 break;
3473 }
3474 }
3475
3476 if (!action_name.empty()) {
3477 auto action_label = action_data.get_label_for_action(action_name);
3478
3479 Glib::ustring message =
3480 Glib::ustring::compose(_("Keyboard shortcut \"%1\"\nis already assigned to \"%2\""),
3481 shortcuts.get_label(new_shortcut_key), action_label.empty() ? action_name : action_label);
3482 Gtk::MessageDialog dialog(message, false, Gtk::MessageType::QUESTION, Gtk::ButtonsType::YES_NO, true);
3483 dialog.set_title(_("Reassign shortcut?"));
3484 dialog.set_secondary_text(_("Are you sure you want to reassign this shortcut?"));
3485 dialog.set_transient_for(dynamic_cast<Gtk::Window &>(*get_root()));
3486 int response = Inkscape::UI::dialog_run(dialog);
3487 if (response != Gtk::ResponseType::YES) {
3488 return;
3489 }
3490 }
3491
3492 shortcuts.add_user_shortcut(id, new_shortcut_key);
3493
3494 onKBListKeyboardShortcuts();
3495}
3496
3497static bool is_leaf_visible(const Gtk::TreeModel::const_iterator& iter, const Glib::ustring& search) {
3498 Glib::ustring name = (*iter)[_kb_columns.name];
3499 Glib::ustring desc = (*iter)[_kb_columns.description];
3500 Glib::ustring shortcut = (*iter)[_kb_columns.shortcut];
3501 Glib::ustring id = (*iter)[_kb_columns.id];
3502
3503 if (name.lowercase().find(search) != name.npos
3504 || shortcut.lowercase().find(search) != name.npos
3505 || desc.lowercase().find(search) != name.npos
3506 || id.lowercase().find(search) != name.npos) {
3507 return true;
3508 }
3509
3510 for (auto const &child : iter->children()) {
3511 if (is_leaf_visible(child.get_iter(), search)) {
3512 return true;
3513 }
3514 }
3515
3516 return false;
3517}
3518
3519bool InkscapePreferences::onKBSearchFilter(const Gtk::TreeModel::const_iterator& iter)
3520{
3521 Glib::ustring search = _kb_search.get_text().lowercase();
3522 if (search.empty()) {
3523 return true;
3524 }
3525
3526 return is_leaf_visible(iter, search);
3527}
3528
3529void InkscapePreferences::onKBRealize()
3530{
3531 if (!_kb_shortcuts_loaded /*&& _current_page == &_page_keyshortcuts*/) {
3532 _kb_shortcuts_loaded = true;
3533 onKBListKeyboardShortcuts();
3534 }
3535}
3536
3537void InkscapePreferences::onKBShortcutRenderer(Gtk::CellRenderer *renderer,
3538 Gtk::TreeModel::const_iterator const &iter)
3539{
3540 Glib::ustring shortcut = (*iter)[onKBGetCols().shortcut];
3541 shortcut = Glib::Markup::escape_text(shortcut);
3542 unsigned int user_set = (*iter)[onKBGetCols().user_set];
3543 Gtk::CellRendererAccel *accel = dynamic_cast<Gtk::CellRendererAccel *>(renderer);
3544 if (user_set) {
3545 accel->property_markup() = Glib::ustring("<span font-weight='bold'> " + shortcut + " </span>").c_str();
3546 } else {
3547 accel->property_markup() = Glib::ustring("<span> " + shortcut + " </span>").c_str();
3548 }
3549}
3550
3551void InkscapePreferences::on_modifier_selection_changed()
3552{
3553 _kb_is_updated = true;
3554 Gtk::TreeStore::iterator iter = _mod_tree.get_selection()->get_selected();
3555 auto selected = static_cast<bool>(iter);
3556
3557 _kb_mod_ctrl.set_sensitive(selected);
3558 _kb_mod_shift.set_sensitive(selected);
3559 _kb_mod_alt.set_sensitive(selected);
3560 _kb_mod_meta.set_sensitive(selected);
3561 _kb_mod_enabled.set_sensitive(selected);
3562
3563 _kb_mod_ctrl.set_active(false);
3564 _kb_mod_shift.set_active(false);
3565 _kb_mod_alt.set_active(false);
3566 _kb_mod_meta.set_active(false);
3567 _kb_mod_enabled.set_active(false);
3568
3569 if (selected) {
3570 Glib::ustring modifier_id = (*iter)[_mod_columns.id];
3571 auto modifier = Modifiers::Modifier::get(modifier_id.c_str());
3573 if(modifier != nullptr) {
3574 mask = modifier->get_and_mask();
3575 } else {
3576 _kb_mod_enabled.set_sensitive(false);
3577 }
3578 if(mask != Inkscape::Modifiers::NEVER) {
3579 _kb_mod_enabled.set_active(true);
3580 _kb_mod_ctrl.set_active(mask & Inkscape::Modifiers::CTRL);
3581 _kb_mod_shift.set_active(mask & Inkscape::Modifiers::SHIFT);
3582 _kb_mod_alt.set_active(mask & Inkscape::Modifiers::ALT);
3583 _kb_mod_meta.set_active(mask & Inkscape::Modifiers::META);
3584 } else {
3585 _kb_mod_ctrl.set_sensitive(false);
3586 _kb_mod_shift.set_sensitive(false);
3587 _kb_mod_alt.set_sensitive(false);
3588 _kb_mod_meta.set_sensitive(false);
3589 }
3590 }
3591 _kb_is_updated = false;
3592}
3593
3594void InkscapePreferences::on_modifier_enabled()
3595{
3596 auto active = _kb_mod_enabled.get_active();
3597 _kb_mod_ctrl.set_sensitive(active);
3598 _kb_mod_shift.set_sensitive(active);
3599 _kb_mod_alt.set_sensitive(active);
3600 _kb_mod_meta.set_sensitive(active);
3601 on_modifier_edited();
3602}
3603
3604void InkscapePreferences::on_modifier_edited()
3605{
3606 Gtk::TreeStore::iterator iter = _mod_tree.get_selection()->get_selected();
3607 if (!iter || _kb_is_updated) return;
3608
3609 Glib::ustring modifier_id = (*iter)[_mod_columns.id];
3610 auto modifier = Modifiers::Modifier::get(modifier_id.c_str());
3611 if(!_kb_mod_enabled.get_active()) {
3613 } else {
3615 if(_kb_mod_ctrl.get_active()) mask |= Inkscape::Modifiers::CTRL;
3616 if(_kb_mod_shift.get_active()) mask |= Inkscape::Modifiers::SHIFT;
3617 if(_kb_mod_alt.get_active()) mask |= Inkscape::Modifiers::ALT;
3618 if(_kb_mod_meta.get_active()) mask |= Inkscape::Modifiers::META;
3619 modifier->set_user(mask, Inkscape::Modifiers::NOT_SET);
3620 }
3622 shortcuts.write_user();
3623 (*iter)[_mod_columns.and_modifiers] = modifier->get_label();
3624}
3625
3626void InkscapePreferences::onKBListKeyboardShortcuts()
3627{
3629
3630 // Save the current selection
3631 Gtk::TreeStore::iterator iter = _kb_tree.get_selection()->get_selected();
3632 Glib::ustring selected_id = "";
3633 if (iter) {
3634 selected_id = (*iter)[_kb_columns.id];
3635 }
3636
3637 _kb_store->clear();
3638 _mod_store->clear();
3639
3640 // Gio::Actions
3641
3642 auto iapp = InkscapeApplication::instance();
3643 auto gapp = iapp->gtk_app();
3644
3645 // std::vector<Glib::ustring> actions = shortcuts.list_all_actions(); // All actions (app, win, doc)
3646
3647 // Simpler and better to get action list from extra data (contains "detailed action names").
3648 InkActionExtraData& action_data = iapp->get_action_extra_data();
3649 std::vector<Glib::ustring> actions = action_data.get_actions();
3650
3651 // Sort actions by section
3652 auto action_sort =
3653 [&](Glib::ustring &a, Glib::ustring &b) {
3654 return action_data.get_section_for_action(a) < action_data.get_section_for_action(b);
3655 };
3656 std::sort (actions.begin(), actions.end(), action_sort);
3657
3658 Glib::ustring old_section;
3659 Gtk::TreeStore::iterator iter_group;
3660
3661 // Fill sections
3662 for (auto const &action : actions) {
3663 Glib::ustring section = action_data.get_section_for_action(action);
3664 if (section.empty()) section = C_("Action Section", "Misc");
3665 if (section != old_section) {
3666 iter_group = _kb_store->append();
3667 (*iter_group)[_kb_columns.name] = g_dpgettext2(NULL, "Action Section", section.c_str());
3668 (*iter_group)[_kb_columns.shortcut] = "";
3669 (*iter_group)[_kb_columns.description] = "";
3670 (*iter_group)[_kb_columns.shortcutkey] = Gtk::AccelKey();
3671 (*iter_group)[_kb_columns.id] = "";
3672 (*iter_group)[_kb_columns.user_set] = 0;
3673 old_section = section;
3674 }
3675
3676 // Find accelerators
3677 auto const &accels = shortcuts.get_triggers(action);
3678 Glib::ustring shortcut_label;
3679 for (auto const &accel : accels) {
3680 // Convert to more user friendly notation.
3681 // ::get_label shows key pad and numeric keys identically.
3682 // TODO: Results in labels like "Numpad Alt+5"
3683 if (accel.find("KP") != Glib::ustring::npos) {
3684 shortcut_label += _("Numpad");
3685 shortcut_label += " ";
3686 }
3687 unsigned int key = 0;
3688 Gdk::ModifierType mod = Gdk::ModifierType(0);
3689 Gtk::Accelerator::parse(accel, key, mod);
3690 shortcut_label += Gtk::Accelerator::get_label(key, mod) + ", ";
3691 }
3692
3693 if (shortcut_label.size() > 1) {
3694 shortcut_label.erase(shortcut_label.size()-2);
3695 }
3696
3697 // Find primary (i.e. first) shortcut.
3698 Gtk::AccelKey shortcut_key;
3699 if (accels.size() > 0) {
3700 unsigned int key = 0;
3701 Gdk::ModifierType mod = Gdk::ModifierType(0);
3702 Gtk::Accelerator::parse(accels[0], key, mod);
3703 shortcut_key = Gtk::AccelKey(key, mod);
3704 }
3705
3706 // Add the action to the group
3707 Gtk::TreeStore::iterator row = _kb_store->append(iter_group->children());
3708 (*row)[_kb_columns.name] = action_data.get_label_for_action(action);
3709 (*row)[_kb_columns.shortcut] = shortcut_label;
3710 (*row)[_kb_columns.description] = action_data.get_tooltip_for_action(action);
3711 (*row)[_kb_columns.shortcutkey] = shortcut_key;
3712 (*row)[_kb_columns.id] = action;
3713 (*row)[_kb_columns.user_set] = shortcuts.is_user_set(action);
3714
3715 if (selected_id == action) {
3716 Gtk::TreeStore::Path sel_path = _kb_filter->convert_child_path_to_path(_kb_store->get_path(row));
3717 _kb_tree.expand_to_path(sel_path);
3718 _kb_tree.get_selection()->select(sel_path);
3719 }
3720 }
3721
3722 std::string old_mod_group;
3723 Gtk::TreeStore::iterator iter_mod_group;
3724
3725 // Modifiers (mouse specific keys)
3726 for(auto modifier: Inkscape::Modifiers::Modifier::getList()) {
3727 auto cat_name = modifier->get_category();
3728 if (cat_name != old_mod_group) {
3729 iter_mod_group = _mod_store->append();
3730 (*iter_mod_group)[_mod_columns.name] = cat_name.empty() ? "" : _(cat_name.c_str());
3731 (*iter_mod_group)[_mod_columns.id] = "";
3732 (*iter_mod_group)[_mod_columns.description] = "";
3733 (*iter_mod_group)[_mod_columns.and_modifiers] = "";
3734 (*iter_mod_group)[_mod_columns.user_set] = 0;
3735 old_mod_group = cat_name;
3736 }
3737
3738 // Find accelerators
3739 Gtk::TreeStore::iterator iter_modifier = _mod_store->append(iter_mod_group->children());
3740 (*iter_modifier)[_mod_columns.name] = (modifier->get_name() && strlen(modifier->get_name())) ? _(modifier->get_name()) : "";
3741 (*iter_modifier)[_mod_columns.id] = modifier->get_id();
3742 (*iter_modifier)[_mod_columns.description] = (modifier->get_description() && strlen(modifier->get_description())) ? _(modifier->get_description()) : "";
3743 (*iter_modifier)[_mod_columns.and_modifiers] = modifier->get_label();
3744 (*iter_modifier)[_mod_columns.user_set] = modifier->is_set_user();
3745 }
3746
3747 // re-order once after updating (then disable ordering again to increase performance)
3748 _kb_store->set_sort_column (_kb_columns.id, Gtk::SortType::ASCENDING );
3749 _kb_store->set_sort_column ( GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, Gtk::SortType::ASCENDING );
3750
3751 if (selected_id.empty()) {
3752 _kb_tree.expand_to_path(_kb_store->get_path(_kb_store->get_iter("0:1")));
3753 }
3754
3755 // Update all GUI text that includes shortcuts.
3756 for (auto win : gapp->get_windows()) {
3757 shortcuts.update_gui_text_recursive(win);
3758 }
3759}
3760
3761void InkscapePreferences::initPageSpellcheck()
3762{
3763#if WITH_LIBSPELLING
3764 _spell_ignorenumbers.init(_("Ignore words with digits"), "/dialogs/spellcheck/ignorenumbers", true);
3765 _page_spellcheck.add_line(false, "", _spell_ignorenumbers, "", _("Ignore words containing digits, such as \"R2D2\""), true);
3766
3767 _spell_ignoreallcaps.init(_("Ignore words in ALL CAPITALS"), "/dialogs/spellcheck/ignoreallcaps", false);
3768 _page_spellcheck.add_line(false, "", _spell_ignoreallcaps, "", _("Ignore words in all capitals, such as \"IUPAC\""), true);
3769
3770 AddPage(_page_spellcheck, _("Spellcheck"), PREFS_PAGE_SPELLCHECK);
3771#endif
3772}
3773
3774template <typename string_type>
3775static void appendList(Glib::ustring& tmp, const std::vector<string_type> &listing)
3776{
3777 for (auto const & str : listing) {
3778 tmp += str;
3779 tmp += "\n";
3780 }
3781}
3782
3783
3784void InkscapePreferences::initPageSystem()
3785{
3787 _sys_shared_path.init("/options/resources/sharedpath", true);
3788 auto const box = Gtk::make_managed<Gtk::Box>();
3789 UI::pack_start(*box, _sys_shared_path);
3790 box->set_size_request(300, -1);
3791 _page_system.add_line( false, _("Shared default resources folder:"), *box, "",
3792 _("A folder structured like a user's Inkscape preferences directory. This makes it possible to share a set of resources, such as extensions, fonts, icon sets, keyboard shortcuts, patterns/hatches, palettes, symbols, templates, themes and user interface definition files, between multiple users who have access to that folder (on the same computer or in the network). Requires a restart of Inkscape to work when changed."), false, reset_icon());
3793 _page_system.add_group_header( _("System info"));
3794
3795 _sys_user_prefs.set_text(prefs->getPrefsFilename());
3796 _sys_user_prefs.set_editable(false);
3797
3798 auto const hbox = Gtk::make_managed<Gtk::Box>();
3799
3800 auto const reset_prefs = Gtk::make_managed<Gtk::Button>(_("Reset"));
3801 reset_prefs->set_tooltip_text(_("Reset the preferences to default"));
3802 reset_prefs->signal_clicked().connect(sigc::mem_fun(*this, &InkscapePreferences::on_reset_prefs_clicked));
3803 hbox->append(*reset_prefs);
3804
3805 auto const save_prefs = Gtk::make_managed<Gtk::Button>(_("Save"));
3806 save_prefs->set_tooltip_text(_("Save the preferences to disk"));
3807 save_prefs->set_action_name("app.save-preferences");
3808 hbox->append(*save_prefs);
3809 hbox->set_hexpand(false);
3810
3811 _page_system.add_line(true, _("User preferences:"), _sys_user_prefs, "",
3812 _("Location of the user’s preferences file"), true, hbox);
3813 auto profilefolder = Inkscape::IO::Resource::profile_path();
3814 _sys_user_config.init(profilefolder.c_str(), _("Open preferences folder"));
3815 _page_system.add_line(true, _("User config:"), _sys_user_config, "", _("Location of users configuration"), true);
3816
3818 _sys_user_extension_dir.init(extensions_folder,
3819 _("Open extensions folder"));
3820 _page_system.add_line(true, _("User extensions:"), _sys_user_extension_dir, "",
3821 _("Location of the user’s extensions"), true);
3822
3823 _sys_user_fonts_dir.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::FONTS, ""),
3824 _("Open fonts folder"));
3825 _page_system.add_line(true, _("User fonts:"), _sys_user_fonts_dir, "", _("Location of the user’s fonts"), true);
3826
3827 _sys_user_themes_dir.init(g_build_filename(g_get_user_data_dir(), "themes", nullptr), _("Open themes folder"));
3828 _page_system.add_line(true, _("User themes:"), _sys_user_themes_dir, "", _("Location of the user’s themes"), true);
3829
3830 _sys_user_icons_dir.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::ICONS, ""),
3831 _("Open icons folder"));
3832 _page_system.add_line(true, _("User icons:"), _sys_user_icons_dir, "", _("Location of the user’s icons"), true);
3833
3834 _sys_user_templates_dir.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::TEMPLATES, ""),
3835 _("Open templates folder"));
3836 _page_system.add_line(true, _("User templates:"), _sys_user_templates_dir, "",
3837 _("Location of the user’s templates"), true);
3838
3839 _sys_user_symbols_dir.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::SYMBOLS, ""),
3840 _("Open symbols folder"));
3841
3842 _page_system.add_line(true, _("User symbols:"), _sys_user_symbols_dir, "", _("Location of the user’s symbols"),
3843 true);
3844
3845 _sys_user_paint_servers_dir.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::PAINT, ""),
3846 _("Open paint servers folder"));
3847
3848 _page_system.add_line(true, _("User paint servers:"), _sys_user_paint_servers_dir, "",
3849 _("Location of the user’s paint servers"), true);
3850
3851 _sys_user_palettes_dir.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::PALETTES, ""),
3852 _("Open palettes folder"));
3853 _page_system.add_line(true, _("User palettes:"), _sys_user_palettes_dir, "", _("Location of the user’s palettes"),
3854 true);
3855
3856 _sys_user_keys_dir.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::KEYS, ""),
3857 _("Open keyboard shortcuts folder"));
3858 _page_system.add_line(true, _("User keys:"), _sys_user_keys_dir, "",
3859 _("Location of the user’s keyboard mapping files"), true);
3860
3861 _sys_user_ui_dir.init((char const *)IO::Resource::get_path(IO::Resource::USER, IO::Resource::UIS, ""),
3862 _("Open user interface folder"));
3863 _page_system.add_line(true, _("User UI:"), _sys_user_ui_dir, "",
3864 _("Location of the user’s user interface description files"), true);
3865
3866 _sys_user_cache.set_text(g_get_user_cache_dir());
3867 _sys_user_cache.set_editable(false);
3868 _page_system.add_line(true, _("User cache:"), _sys_user_cache, "", _("Location of user’s cache"), true);
3869
3870 Glib::ustring tmp_dir = prefs->getString("/options/autosave/path");
3871 if (tmp_dir.empty()) {
3872 tmp_dir = Glib::build_filename(Glib::get_user_cache_dir(), "inkscape");
3873 }
3874 _sys_tmp_files.set_text(tmp_dir);
3875 _sys_tmp_files.set_editable(false);
3876 _page_system.add_line(true, _("Temporary files:"), _sys_tmp_files, "", _("Location of the temporary files used for autosave"), true);
3877
3878 _sys_data.set_text(get_inkscape_datadir());
3879 _sys_data.set_editable(false);
3880 _page_system.add_line(true, _("Inkscape data:"), _sys_data, "", _("Location of Inkscape data"), true);
3881
3883 _sys_extension_dir.set_text(extensions_folder);
3884 _sys_extension_dir.set_editable(false);
3885 _page_system.add_line(true, _("Inkscape extensions:"), _sys_extension_dir, "", _("Location of the Inkscape extensions"), true);
3886
3887 Glib::ustring tmp;
3888 auto system_data_dirs = Glib::get_system_data_dirs();
3889 appendList(tmp, system_data_dirs);
3890 _sys_systemdata.get_buffer()->insert(_sys_systemdata.get_buffer()->end(), tmp);
3891 _sys_systemdata.set_editable(false);
3892 _sys_systemdata_scroll.set_child(_sys_systemdata);
3893 _sys_systemdata_scroll.set_size_request(100, 80);
3894 _sys_systemdata_scroll.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
3895 _sys_systemdata_scroll.set_has_frame(true);
3896 _page_system.add_line(true, _("System data:"), _sys_systemdata_scroll, "", _("Locations of system data"), true);
3897
3898 _sys_fontdirs_custom.init("/options/font/custom_fontdirs", 50);
3899 _page_system.add_line(true, _("Custom Font directories"), _sys_fontdirs_custom, "", _("Load additional fonts from custom locations (one path per line)"), true);
3900
3901 tmp = "";
3902
3903 auto const icon_theme = Gtk::IconTheme::get_for_display(Gdk::Display::get_default());
3904 auto paths = icon_theme->get_search_path();
3905 appendList( tmp, paths );
3906 _sys_icon.get_buffer()->insert(_sys_icon.get_buffer()->end(), tmp);
3907 _sys_icon.set_editable(false);
3908 _sys_icon_scroll.set_child(_sys_icon);
3909 _sys_icon_scroll.set_size_request(100, 80);
3910 _sys_icon_scroll.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
3911 _sys_icon_scroll.set_has_frame(true);
3912 _page_system.add_line(true, _("Icon theme:"), _sys_icon_scroll, "", _("Locations of icon themes"), true);
3913
3914 this->AddPage(_page_system, _("System"), PREFS_PAGE_SYSTEM);
3915}
3916
3917bool InkscapePreferences::GetSizeRequest(const Gtk::TreeModel::iterator& iter)
3918{
3919 Gtk::TreeModel::Row row = *iter;
3920 DialogPage* page = row[_page_list_columns._col_page];
3921
3922 _page_frame.set_child(*page);
3923 int minimum_width{}, natural_width{}, minimum_height{}, natural_height{}, ignore{};
3924 measure(Gtk::Orientation::HORIZONTAL, -1, minimum_width , natural_width , ignore, ignore);
3925 measure(Gtk::Orientation::VERTICAL , -1, minimum_height, natural_height, ignore, ignore);
3926 _minimum_width = std::max(_minimum_width, minimum_width );
3927 _minimum_height = std::max(_minimum_height, minimum_height);
3928 _natural_width = std::max(_natural_width, natural_width );
3929 _natural_height = std::max(_natural_height, natural_height);
3930 _page_frame.unset_child();
3931
3932 return false;
3933}
3934
3935// Check if iter points to page indicated in preferences.
3936bool InkscapePreferences::matchPage(Gtk::TreeModel::const_iterator const &iter)
3937{
3938 auto const &row = *iter;
3940 int desired_page = prefs->getInt("/dialogs/preferences/page", 0);
3941 _init = false;
3942 if (desired_page == row[_page_list_columns._col_id])
3943 {
3944 auto const path = _page_list.get_model()->get_path(iter);
3945 _page_list.expand_to_path(path);
3946 _page_list.get_selection()->select(iter);
3947 if (desired_page == PREFS_PAGE_UI_THEME)
3948 symbolicThemeCheck();
3949 return true;
3950 }
3951 return false;
3952}
3953
3954void InkscapePreferences::on_reset_open_recent_clicked()
3955{
3956 Glib::RefPtr<Gtk::RecentManager> manager = Gtk::RecentManager::get_default();
3957 std::vector< Glib::RefPtr< Gtk::RecentInfo > > recent_list = manager->get_items();
3958
3959 // Remove only elements that were added by Inkscape
3960 // TODO: This should likely preserve items that were also accessed by other apps.
3961 // However there does not seem to be straightforward way to delete only an application from an item.
3962 for (auto e : recent_list) {
3963 if (e->has_application(g_get_prgname())
3964 || e->has_application("org.inkscape.Inkscape")
3965 || e->has_application("inkscape")
3966 || e->has_application("inkscape.exe")
3967 ) {
3968 manager->remove_item(e->get_uri());
3969 }
3970 }
3971}
3972
3973void InkscapePreferences::on_reset_prefs_clicked()
3974{
3976}
3977
3978void InkscapePreferences::show_not_found()
3979{
3980 if (_current_page)
3981 _page_frame.unset_child();
3983 _current_page = &_page_notfound;
3984 _page_title.set_markup(_("<span size='large'><b>No Results</b></span>"));
3985 _page_frame.set_child(*_current_page);
3986 _current_page->set_visible(true);
3987 if (prefs->getInt("/dialogs/preferences/page", 0) == PREFS_PAGE_UI_THEME) {
3988 symbolicThemeCheck();
3989 }
3990}
3991
3992void InkscapePreferences::show_nothing_on_page()
3993{
3994 _page_frame.unset_child();
3995 _page_title.set_text("");
3996}
3997
3998void InkscapePreferences::on_pagelist_selection_changed()
3999{
4000 // show new selection
4001 Glib::RefPtr<Gtk::TreeSelection> selection = _page_list.get_selection();
4002 Gtk::TreeModel::iterator iter = selection->get_selected();
4003 if(iter)
4004 {
4005 if (_current_page)
4006 _page_frame.unset_child();
4007 Gtk::TreeModel::Row row = *iter;
4008 _current_page = row[_page_list_columns._col_page];
4010 if (!_init) {
4011 prefs->setInt("/dialogs/preferences/page", row[_page_list_columns._col_id]);
4012 }
4013 Glib::ustring col_name_escaped = Glib::Markup::escape_text( row[_page_list_columns._col_name] );
4014 _page_title.set_markup("<span size='large'><b>" + col_name_escaped + "</b></span>");
4015 _page_frame.set_child(*_current_page);
4016 _current_page->set_visible(true);
4017 if (prefs->getInt("/dialogs/preferences/page", 0) == PREFS_PAGE_UI_THEME) {
4018 symbolicThemeCheck();
4019 }
4020 }
4021}
4022
4023// Show page indicated in preferences file.
4024void InkscapePreferences::showPage()
4025{
4026 _search.set_text("");
4027 _page_list.get_model()->foreach_iter(sigc::mem_fun(*this, &InkscapePreferences::matchPage));
4028}
4029
4030} // namespace Inkscape::UI::Dialog
4031
4032/*
4033 Local Variables:
4034 mode:c++
4035 c-file-style:"stroustrup"
4036 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4037 indent-tabs-mode:nil
4038 fill-column:99
4039 End:
4040*/
4041// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
const char * action_name
Gtk builder utilities.
Cairo::RefPtr< Cairo::ImageSurface > surface
Definition canvas.cpp:137
uint64_t page
Definition canvas.cpp:171
Geom::IntRect visible
Definition canvas.cpp:154
Glib::ustring get_label_for_action(Glib::ustring const &action_name, bool translated=true) const
bool isSameContext(Glib::ustring const &action_one, Glib::ustring const &action_two) const
Return true if the action/shortcut context is the same.
Glib::ustring get_section_for_action(Glib::ustring const &action_name) const
std::vector< Glib::ustring > get_actions()
Glib::ustring get_tooltip_for_action(Glib::ustring const &action_name, bool translated=true, bool expanded=false) const
static InkscapeApplication * instance()
Singleton instance.
static void restart()
static System & get()
Definition system.h:35
DirPaths const & getDirectoryPaths()
Create list of all directories where ICC profiles are expected to be found.
Definition system.cpp:125
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
static Modifier * get(Type index)
A function to turn an enum index into a modifier object.
Definition modifiers.h:215
static std::vector< Modifier const * > getList()
List all the modifiers available.
bool isEmpty()
Returns true if no items are selected.
SPItem * singleItem()
Returns a single selected item.
Data type representing a typeless value of a preference.
bool isValidUInt() const
Check if the preference value can be interpreted as an unsigned integer.
static std::unique_ptr< PreferencesObserver > create(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
Preference storage class.
Definition preferences.h:61
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
void reset()
Deletes the preferences.xml file.
void setStyle(Glib::ustring const &pref_path, SPCSSAttr *style)
Set a CSS style.
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
unsigned int getUInt(Glib::ustring const &pref_path, unsigned int def=0)
Retrieve an unsigned integer.
static Preferences * get()
Access the singleton Preferences object.
Colors::Color getColor(Glib::ustring const &pref_path, std::string const &def="black")
Glib::ustring getPrefsFilename() const
Get the preferences file name in UTF-8.
void setUInt(Glib::ustring const &pref_path, unsigned int value)
Set an unsigned integer value.
Entry const getEntry(Glib::ustring const &pref_path)
Retrieve a preference entry without specifying its type.
SPCSSAttr * getStyle(Glib::ustring const &pref_path)
Retrieve a CSS style.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
std::unique_ptr< PreferencesObserver > createObserver(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
Create an observer watching preference 'path' and calling provided function when preference changes.
void setString(Glib::ustring const &pref_path, Glib::ustring const &value)
Set an UTF-8 string value.
SPCSSAttr * getInheritedStyle(Glib::ustring const &pref_path)
Retrieve an inherited CSS style.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
static RecentlyUsedFonts * get()
void change_max_list_size(const int &max_size)
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
bool clear_user_shortcuts()
Remove all user's shortcuts (simply overwrites existing file).
static Glib::ustring get_label(const Gtk::AccelKey &shortcut)
static Shortcuts & getInstance(bool init=true)
Definition shortcuts.h:66
static std::vector< std::pair< Glib::ustring, std::string > > get_file_names()
Get a list of filenames to populate menu in preferences dialog.
static Gtk::AccelKey get_from(GtkEventControllerKey const *controller, unsigned keyval, unsigned keycode, GdkModifierType state, bool fix=false)
Controller provides the group. It can be nullptr; if so, we use group 0.
void update_gui_text_recursive(Gtk::Widget *widget)
bool write_user()
Write user shortcuts to file.
std::vector< Glib::ustring > get_triggers(Glib::ustring const &action_name) const
Returns a vector of triggers for a given detailed_action_name.
bool add_user_shortcut(Glib::ustring const &detailed_action_name, Gtk::AccelKey const &trigger)
bool is_user_set(Glib::ustring const &detailed_action_name)
Return if user set shortcut for Gio::Action.
bool remove_user_shortcut(Glib::ustring const &detailed_action_name)
std::vector< Glib::ustring > get_actions(Glib::ustring const &trigger) const
Returns a vector of detailed_action_names for a given trigger.
DialogBase is the base class for the dialog system.
Definition dialog-base.h:42
Gtk::TreeModelColumn< UI::Widget::DialogPage * > _col_page
bool GetSizeRequest(const Gtk::TreeModel::iterator &iter)
Glib::RefPtr< Gtk::TreeModelFilter > _page_list_model_filter
void add_highlight(Gtk::Label *label, Glib::ustring const &key)
Add CSS-based highlight-class and pango highlight to a Gtk::Label.
Glib::RefPtr< Gtk::TreeStore > _page_list_model
Glib::RefPtr< Gtk::TreeModelSort > _page_list_model_sort
void on_search_changed()
Implementation of the search functionality executes each time search entry is changed.
int num_widgets_in_grid(Glib::ustring const &key, Gtk::Widget *widget)
Get number of child Labels that match a key in a widget grid and add the results to global _search_re...
void remove_highlight(Gtk::Label *label)
Remove CSS-based highlight-class and pango highlight from a Gtk::Label.
static Glib::ustring get_font_scale_pref_path()
Definition themes.h:68
static Glib::ustring get_tool_visible_button_path(const Glib::ustring &button_action_name)
void add_group_header(Glib::ustring name, int columns=1)
void add_line(bool indent, Glib::ustring const &label, Gtk::Widget &widget, Glib::ustring const &suffix, Glib::ustring const &tip, bool expand=true, Gtk::Widget *other_widget=nullptr)
Add a widget to the bottom row of the dialog page.
void set_tip(Gtk::Widget &widget, Glib::ustring const &tip)
SpinButton widget, that allows entry of simple math expressions (also units, when linked with UnitMen...
Definition spinbutton.h:52
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:588
To do: update description of desktop.
Definition desktop.h:149
Inkscape::MessageStack * messageStack() const
Definition desktop.h:160
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Base class for visual SVG elements.
Definition sp-item.h:109
Access operating system wide information about color management.
double inner(valarray< double > const &x, valarray< double > const &y)
std::shared_ptr< Css const > css
friend Manager
Css & result
Glib::RefPtr< Gtk::IconTheme > icon_theme
static char const *const current
Definition dir-util.cpp:71
static char const *const parent
Definition dir-util.cpp:70
unsigned int guint32
@ PREFS_WINDOW_GEOMETRY_NONE
Definition enums.h:119
@ PREFS_WINDOW_GEOMETRY_LAST
Definition enums.h:121
@ PREFS_WINDOW_GEOMETRY_FILE
Definition enums.h:120
@ SP_CLONE_ORPHANS_KEEP
Definition enums.h:97
@ SP_CLONE_ORPHANS_UNLINK
Definition enums.h:95
@ SP_CLONE_ORPHANS_DELETE
Definition enums.h:96
@ PREFS_MASKOBJECT_GROUPING_ALL
Definition enums.h:113
@ PREFS_MASKOBJECT_GROUPING_SEPARATE
Definition enums.h:112
@ PREFS_MASKOBJECT_GROUPING_NONE
Definition enums.h:111
@ PREFS_WINDOW_SIZE_SMALL
Definition enums.h:158
@ PREFS_WINDOW_SIZE_NATURAL
Definition enums.h:157
@ PREFS_WINDOW_SIZE_MAXIMIZED
Definition enums.h:160
@ PREFS_WINDOW_SIZE_LARGE
Definition enums.h:159
@ SP_CLONE_COMPENSATION_UNMOVED
Definition enums.h:90
@ SP_CLONE_COMPENSATION_PARALLEL
Definition enums.h:89
@ SP_CLONE_COMPENSATION_NONE
Definition enums.h:91
@ PREFS_SELECTION_LAYER
Definition enums.h:104
@ PREFS_SELECTION_LAYER_RECURSIVE
Definition enums.h:105
@ PREFS_SELECTION_ALL
Definition enums.h:103
@ PREFS_DIALOGS_WINDOWS_NORMAL
Definition enums.h:142
@ PREFS_DIALOGS_WINDOWS_AGGRESSIVE
Definition enums.h:143
@ PREFS_DIALOGS_WINDOWS_NONE
Definition enums.h:141
@ PREFS_DIALOGS_BEHAVIOR_DOCKABLE
Definition enums.h:135
@ PREFS_DIALOGS_BEHAVIOR_FLOATING
Definition enums.h:134
@ PREFS_NOTEBOOK_LABELS_AUTO
Definition enums.h:149
@ PREFS_NOTEBOOK_LABELS_ACTIVE
Definition enums.h:150
@ PREFS_NOTEBOOK_LABELS_OFF
Definition enums.h:151
static Glib::ustring const prefs_path
std::unique_ptr< Magick::Image > image
SPItem * item
Inkscape Preferences dialog.
@ PREFS_PAGE_TOOLS_SHAPES_RECT
@ PREFS_PAGE_TOOLS_SHAPES_3DBOX
@ PREFS_PAGE_UI_KEYBOARD_SHORTCUTS
@ PREFS_PAGE_TOOLS_SPRAY
@ PREFS_PAGE_BEHAVIOR_CLEANUP
@ PREFS_PAGE_BEHAVIOR_MARKERS
@ PREFS_PAGE_RENDERING
@ PREFS_PAGE_IO_CMS
@ PREFS_PAGE_UI_THEME
@ PREFS_PAGE_UI
@ PREFS_PAGE_TOOLS_TWEAK
@ PREFS_PAGE_TOOLS_TEXT
@ PREFS_PAGE_TOOLS_LPETOOL
@ PREFS_PAGE_BEHAVIOR_MASKS
@ PREFS_PAGE_BEHAVIOR_STEPS
@ PREFS_PAGE_BEHAVIOR_SCROLLING
@ PREFS_PAGE_BEHAVIOR
@ PREFS_PAGE_TOOLS_SELECTOR
@ PREFS_PAGE_IO_MOUSE
@ PREFS_PAGE_IO
@ PREFS_PAGE_TOOLS_SHAPES
@ PREFS_PAGE_UI_TOOLBARS
@ PREFS_PAGE_BEHAVIOR_TRANSFORMS
@ PREFS_PAGE_TOOLS_SHAPES_STAR
@ PREFS_PAGE_BEHAVIOR_CLONES
@ PREFS_PAGE_UI_COLOR_PICKERS
@ PREFS_PAGE_TOOLS_ZOOM
@ PREFS_PAGE_UI_GRIDS
@ PREFS_PAGE_BEHAVIOR_SNAPPING
@ PREFS_PAGE_BEHAVIOR_SELECTING
@ PREFS_PAGE_TOOLS_SHAPES_ELLIPSE
@ PREFS_PAGE_TOOLS_NODE
@ PREFS_PAGE_BEHAVIOR_CLIPBOARD
@ PREFS_PAGE_BITMAPS
@ PREFS_PAGE_TOOLS_PAINTBUCKET
@ PREFS_PAGE_TOOLS_MEASURE
@ PREFS_PAGE_TOOLS_SHAPES_SPIRAL
@ PREFS_PAGE_TOOLS_DROPPER
@ PREFS_PAGE_SPELLCHECK
@ PREFS_PAGE_TOOLS_CALLIGRAPHY
@ PREFS_PAGE_SYSTEM
@ PREFS_PAGE_TOOLS_ERASER
@ PREFS_PAGE_UI_WINDOWS
@ PREFS_PAGE_TOOLS
@ PREFS_PAGE_TOOLS_GRADIENT
@ PREFS_PAGE_COMMAND_PALETTE
@ PREFS_PAGE_IO_SVGEXPORT
@ PREFS_PAGE_TOOLS_PEN
@ PREFS_PAGE_TOOLS_PENCIL
@ PREFS_PAGE_BEHAVIOR_LPE
@ PREFS_PAGE_IO_SVGOUTPUT
@ PREFS_PAGE_TOOLS_CONNECTOR
@ PREFS_PAGE_IO_AUTOSAVE
Inkscape - An SVG editor.
Glib::ustring label
Raw stack of active status messages.
vector< vector< Point > > paths
Definition metro.cpp:36
MultiDegree< n > max(MultiDegree< n > const &p, MultiDegree< n > const &q)
Returns the maximal degree appearing in the two arguments for each variables.
Definition sbasisN.h:158
Piecewise< SBasis > min(SBasis const &f, SBasis const &g)
Return the more negative of the two functions pointwise.
Util::ptr_shared get_path(Domain domain, Type type, char const *filename, char const *extra)
Definition resource.cpp:137
std::string get_path_string(Domain domain, Type type, char const *filename, char const *extra)
Definition resource.cpp:148
std::string get_filename(Type type, char const *filename, bool localized, bool silent)
Definition resource.cpp:170
std::string profile_path()
Definition resource.cpp:415
Dialog code.
Definition desktop.h:117
static void gamutColorChanged(Gtk::ColorButton *btn)
static bool is_leaf_visible(const Gtk::TreeModel::const_iterator &iter, const Glib::ustring &search)
static ModelColumns & onKBGetCols()
static int get_num_matches(Glib::ustring const &key, Gtk::Widget *widget)
Get number of child Labels that match a key in a widget.
static void StyleFromSelectionToTool(Glib::ustring const &prefs_path, StyleSwatch *swatch)
static void appendList(Glib::ustring &tmp, const std::vector< string_type > &listing)
static void profileComboChanged(Gtk::ComboBoxText *combo)
static bool fuzzy_search(Glib::ustring const &pattern, Glib::ustring const &string, float &score)
Case-insensitive and unicode normalized search of pattern in string.
static Cairo::RefPtr< Cairo::Surface > draw_color_preview(unsigned int rgb, unsigned int frame_rgb, int device_scale)
Glib::ustring get_tool_action_name(Glib::ustring toolname)
static void proofComboChanged(Gtk::ComboBoxText *combo)
static auto getShortcutsFileLabelsAndValues()
static auto get_children_or_mnemonic_labels(Gtk::Widget &widget)
static constexpr const int min_pixel_size
static constexpr const char * tools_icon_size
static constexpr const char * ctrlbars_icon_size
static constexpr const int max_pixel_size
void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the end of box.
Definition pack.cpp:153
void dialog_show_modal_and_selfdestruct(std::unique_ptr< Gtk::Dialog > dialog, Gtk::Root *root)
Show a dialog modally, destroying it when the user dismisses it.
Gtk::Widget * for_each_descendant(Gtk::Widget &widget, Func &&func)
Like for_each_child() but also tests the initial widget & recurses through childrenʼs children.
Definition util.h:132
std::vector< Gtk::Widget * > get_children(Gtk::Widget &widget)
Get a vector of the widgetʼs children, from get_first_child() through each get_next_sibling().
Definition util.cpp:156
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
int dialog_run(Gtk::Dialog &dialog)
This is a GTK4 porting aid meant to replace the removal of the Gtk::Dialog synchronous API.
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
T * get(GValue *value)
Returns a borrowed pointer to the T held by a value if it holds one, else nullptr.
Definition value-utils.h:64
void trim(Glib::ustring &input, Glib::ustring const &also_remove="")
Modifies a string in place, removing leading and trailing whitespace characters.
Definition trim.h:34
Glib::ustring format_classic(T const &... args)
Cairo::RefPtr< Cairo::ImageSurface > draw_handles_preview(int device_scale)
static void append(std::vector< T > &target, std::vector< T > &&source)
@ ERROR_MESSAGE
Definition message.h:29
STL namespace.
@ BLUR_QUALITY_NORMAL
@ BLUR_QUALITY_BEST
@ BLUR_QUALITY_WORSE
@ BLUR_QUALITY_BETTER
@ BLUR_QUALITY_WORST
static cairo_user_data_key_t key
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Path manipulator - a component that edits a single path on-canvas.
char const * get_inkscape_datadir()
Determine the location of the Inkscape data directory (typically the share/ folder from where Inkscap...
TODO: insert short description here.
Widgets for Inkscape Preferences dialog.
Singleton class to access the preferences file in a convenient way.
Authors: see git history.
Ocnode * child[8]
Definition quantize.cpp:33
RGB rgb
Definition quantize.cpp:36
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
Inkscape::IO::Resource - simple resource API.
auto len
Definition safe-printf.h:21
void remove(std::vector< T > &vec, T const &val)
Definition sanitize.cpp:94
SPCSSAttr * take_style_from_item(SPObject *object)
static auto const GRID_DEFAULT_MAJOR_COLOR
Definition sp-grid.cpp:42
Glib::ustring sp_get_action_target(Gtk::Widget *widget)
Get string action target, if available.
Static style swatch (fill, stroke, opacity)
SPCSSAttr * sp_css_attr_unset_blacklist(SPCSSAttr *css)
Unset properties that should not be set for default tool style.
Definition style.cpp:1498
SPCSSAttr * sp_css_attr_unset_text(SPCSSAttr *css)
Unset any text-related properties.
Definition style.cpp:1444
SPCSSAttr * sp_css_attr_unset_uris(SPCSSAttr *css)
Unset any properties that contain URI values.
Definition style.cpp:1543
SPStyle - a style object for SPItem objects.
int index
SPDesktop * desktop
Gtk <themes> helper code.
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder
Inkscape::Util::trim - trim whitespace and other characters.
Glib::RefPtr< Gdk::Texture > to_texture(Cairo::RefPtr< Cairo::Surface > const &surface)
Convert an image surface in ARGB32 format to a texture.
Definition util.cpp:510
Gdk::RGBA to_rgba(guint32 const u32)
Definition util.cpp:325
guint32 to_guint32(Gdk::RGBA const &rgba)
Definition util.cpp:312