Inkscape
Vector Graphics Editor
preferences-widget.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/*
7 * Authors:
8 * Marco Scholten
9 * Bruno Dilly <bruno.dilly@gmail.com>
10 *
11 * Copyright (C) 2004, 2006, 2007 Authors
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include <iostream>
17#include <sstream>
18#include <string>
19#include <utility>
20#include <glibmm/i18n.h>
21#include <glibmm/convert.h>
22#include <glibmm/regex.h>
23#include <gtkmm/box.h>
24#include <gtkmm/scale.h>
25#include <sigc++/functors/mem_fun.h>
26
27#include "desktop.h"
28#include "inkscape.h"
29#include "message-stack.h"
30#include "preferences.h"
31#include "selcue.h"
32#include "selection-chemistry.h"
34#include "io/sys.h"
36#include "ui/icon-loader.h"
37#include "ui/pack.h"
38#include "ui/util.h"
40
41#ifdef _WIN32
42#include <windows.h>
43#endif
44
45namespace Inkscape::UI::Widget {
46
48{
49 set_margin(12);
50
51 set_orientation(Gtk::Orientation::VERTICAL);
52 set_column_spacing(12);
53 set_row_spacing(6);
54}
55
67void DialogPage::add_line(bool indent,
68 Glib::ustring const &label,
69 Gtk::Widget &widget,
70 Glib::ustring const &suffix,
71 const Glib::ustring &tip,
72 bool expand_widget,
73 Gtk::Widget *other_widget)
74{
75 if (!tip.empty())
76 widget.set_tooltip_text(tip);
77
78 auto const hb = Gtk::make_managed<Gtk::Box>();
79 hb->set_spacing(12);
80 hb->set_hexpand(true);
82 hb->set_valign(Gtk::Align::CENTER);
83
84 // Add a label in the first column if provided
85 if (!label.empty()) {
86 auto const label_widget = Gtk::make_managed<Gtk::Label>(label, Gtk::Align::START,
87 Gtk::Align::CENTER, true);
88 label_widget->set_mnemonic_widget(widget);
89 label_widget->set_markup(label_widget->get_text());
90
91 if (indent) {
92 label_widget->set_margin_start(12);
93 }
94
95 label_widget->set_valign(Gtk::Align::CENTER);
96 attach_next_to(*label_widget, Gtk::PositionType::BOTTOM);
97
98 attach_next_to(*hb, *label_widget, Gtk::PositionType::RIGHT, 1, 1);
99 } else {
100 if (indent) {
101 hb->set_margin_start(12);
102 }
103
104 attach_next_to(*hb, Gtk::PositionType::BOTTOM, 2, 1);
105 }
106
107 // Add a label on the right of the widget if desired
108 if (!suffix.empty()) {
109 auto const suffix_widget = Gtk::make_managed<Gtk::Label>(suffix, Gtk::Align::START, Gtk::Align::CENTER, true);
110 suffix_widget->set_markup(suffix_widget->get_text());
111 UI::pack_start(*hb, *suffix_widget,false,false);
112 }
113
114 // Pack an additional widget into a box with the widget if desired
115 if (other_widget)
116 UI::pack_start(*hb, *other_widget, expand_widget, expand_widget);
117}
118
119void DialogPage::add_group_header(Glib::ustring name, int columns)
120{
121 if (name.empty()) return;
122
123 auto const label_widget = Gtk::make_managed<Gtk::Label>(Glib::ustring("<b>").append(name).append("</b>"),
124 Gtk::Align::START, Gtk::Align::CENTER, true);
125
126 label_widget->set_use_markup(true);
127 label_widget->set_valign(Gtk::Align::CENTER);
128 attach_next_to(*label_widget, Gtk::PositionType::BOTTOM, columns, 1);
129}
130
131void DialogPage::add_group_note(Glib::ustring name)
132{
133 if (name.empty()) return;
134
135 auto const label_widget = Gtk::make_managed<Gtk::Label>(Glib::ustring("<i>").append(name).append("</i>"),
136 Gtk::Align::START , Gtk::Align::CENTER, true);
137 label_widget->set_use_markup(true);
138 label_widget->set_valign(Gtk::Align::CENTER);
139 label_widget->set_wrap(true);
140 label_widget->set_wrap_mode(Pango::WrapMode::WORD);
141 attach_next_to(*label_widget, Gtk::PositionType::BOTTOM, 2, 1);
142}
143
144void DialogPage::set_tip(Gtk::Widget& widget, Glib::ustring const &tip)
145{
146 widget.set_tooltip_text (tip);
147}
148
149void PrefCheckButton::init(Glib::ustring const &label, Glib::ustring const &prefs_path,
150 bool default_value)
151{
154 if (!label.empty())
155 this->set_label(label);
156 this->set_active( prefs->getBool(_prefs_path, default_value) );
157}
158
160{
161 if (this->get_visible()) //only take action if the user toggled it
162 {
164 prefs->setBool(_prefs_path, this->get_active());
165 }
166 this->changed_signal.emit(this->get_active());
167}
168
169void PrefRadioButton::init(Glib::ustring const &label, Glib::ustring const &prefs_path,
170 Glib::ustring const &string_value, bool default_value, PrefRadioButton* group_member)
171{
174 _string_value = string_value;
175 (void)default_value;
176 this->set_label(label);
177
178 if (group_member)
179 {
180 this->set_group(*group_member);
181 }
182
184 Glib::ustring val = prefs->getString(_prefs_path);
185 if ( !val.empty() )
186 this->set_active(val == _string_value);
187 else
188 this->set_active( false );
189}
190
191void PrefRadioButton::init(Glib::ustring const &label, Glib::ustring const &prefs_path,
192 int int_value, bool default_value, PrefRadioButton* group_member)
193{
196 _int_value = int_value;
197 this->set_label(label);
198
199 if (group_member)
200 {
201 this->set_group(*group_member);
202 }
203
205 if (default_value)
206 this->set_active( prefs->getInt(_prefs_path, int_value) == _int_value );
207 else
208 this->set_active( prefs->getInt(_prefs_path, int_value + 1) == _int_value );
209}
210
212{
214
215 if (this->get_visible() && this->get_active() ) //only take action if toggled by user (to active)
216 {
217 if ( _value_type == VAL_STRING )
219 else if ( _value_type == VAL_INT )
221 }
222 this->changed_signal.emit(this->get_active());
223}
224
225PrefRadioButtons::PrefRadioButtons(const std::vector<PrefItem>& buttons, const Glib::ustring& prefs_path) {
226 set_spacing(2);
227
228 PrefRadioButton* group = nullptr;
229 for (auto&& item : buttons) {
230 auto* btn = Gtk::make_managed<PrefRadioButton>();
231 btn->init(item.label, prefs_path, item.int_value, item.is_default, group);
232 btn->set_tooltip_text(item.tooltip);
233 append(*btn);
234 if (!group) group = btn;
235 }
236}
237
238void PrefSpinButton::init(Glib::ustring const &prefs_path,
239 double lower, double upper, double step_increment, double /*page_increment*/,
240 double default_value, bool is_int, bool is_percent)
241{
243 _is_int = is_int;
244 _is_percent = is_percent;
246 double value;
247 if (is_int) {
248 if (is_percent) {
249 value = 100 * prefs->getDoubleLimited(prefs_path, default_value, lower/100.0, upper/100.0);
250 } else {
251 value = (double) prefs->getIntLimited(prefs_path, (int) default_value, (int) lower, (int) upper);
252 }
253 } else {
254 value = prefs->getDoubleLimited(prefs_path, default_value, lower, upper);
255 }
256
257 this->set_range (lower, upper);
258 this->set_increments (step_increment, 0);
259 this->set_value (value);
260 this->set_width_chars(6);
261 if (is_int)
262 this->set_digits(0);
263 else if (step_increment < 0.1)
264 this->set_digits(4);
265 else
266 this->set_digits(2);
267
268 signal_value_changed().connect(sigc::mem_fun(*this, &PrefSpinButton::on_value_changed));
269}
270
272{
274 if (this->get_visible()) //only take action if user changed value
275 {
276 if (_is_int) {
277 if (_is_percent) {
278 prefs->setDouble(_prefs_path, this->get_value()/100.0);
279 } else {
280 prefs->setInt(_prefs_path, (int) this->get_value());
281 }
282 } else {
283 prefs->setDouble(_prefs_path, this->get_value());
284 }
285 }
286 this->changed_signal.emit(this->get_value());
287}
288
289void PrefSpinUnit::init(Glib::ustring const &prefs_path,
290 double lower, double upper, double step_increment,
291 double default_value, UnitType unit_type, Glib::ustring const &default_unit)
292{
294 _is_percent = (unit_type == UNIT_TYPE_DIMENSIONLESS);
295
296 resetUnitType(unit_type);
297 setUnit(default_unit);
298 setRange (lower, upper);
299 setIncrements (step_increment, 0);
300 if (step_increment < 0.1) {
301 setDigits(4);
302 } else {
303 setDigits(2);
304 }
305
307 double value = prefs->getDoubleLimited(prefs_path, default_value, lower, upper);
308 Glib::ustring unitstr = prefs->getUnit(prefs_path);
309 if (unitstr.length() == 0) {
310 unitstr = default_unit;
311 // write the assumed unit to preferences:
312 prefs->setDoubleUnit(_prefs_path, value, unitstr);
313 }
314 setValue(value, unitstr);
315
316 signal_value_changed().connect(sigc::mem_fun(*this, &PrefSpinUnit::on_my_value_changed));
317}
318
320{
322 if (getWidget()->get_visible()) //only take action if user changed value
323 {
324 prefs->setDoubleUnit(_prefs_path, getValue(getUnit()->abbr), getUnit()->abbr);
325 }
326}
327
328const double ZoomCorrRuler::textsize = 7;
329const double ZoomCorrRuler::textpadding = 5;
330
332 _unitconv(1.0),
333 _border(5)
334{
336
337 set_draw_func(sigc::mem_fun(*this, &ZoomCorrRuler::on_draw));
338}
339
340void ZoomCorrRuler::set_size(int x, int y)
341{
342 _min_width = x;
343 _height = y;
344 set_size_request(x + _border*2, y + _border*2);
345}
346
347// The following two functions are borrowed from 2geom's toy-framework-2; if they are useful in
348// other locations, we should perhaps make them (or adapted versions of them) publicly available
349static void
350draw_text(cairo_t *cr, Geom::Point loc, const char* txt, bool bottom = false,
351 double fontsize = ZoomCorrRuler::textsize, std::string fontdesc = "Sans") {
352 PangoLayout* layout = pango_cairo_create_layout (cr);
353 pango_layout_set_text(layout, txt, -1);
354
355 // set font and size
356 std::ostringstream sizestr;
357 sizestr << fontsize;
358 fontdesc = fontdesc + " " + sizestr.str();
359 PangoFontDescription *font_desc = pango_font_description_from_string(fontdesc.c_str());
360 pango_layout_set_font_description(layout, font_desc);
361 pango_font_description_free (font_desc);
362
363 PangoRectangle logical_extent;
364 pango_layout_get_pixel_extents(layout, nullptr, &logical_extent);
365 cairo_move_to(cr, loc[Geom::X], loc[Geom::Y] - (bottom ? logical_extent.height : 0));
366 pango_cairo_show_layout(cr, layout);
367}
368
369static void
371 std::ostringstream number;
372 number << num;
373 draw_text(cr, pos, number.str().c_str(), true);
374}
375
376/*
377 * \arg dist The distance between consecutive minor marks
378 * \arg major_interval Number of marks after which to draw a major mark
379 */
380void
381ZoomCorrRuler::draw_marks(Cairo::RefPtr<Cairo::Context> const &cr,
382 double const dist, int const major_interval)
383{
385 const double zoomcorr = prefs->getDouble("/options/zoomcorrection/value", 1.0);
386 double mark = 0;
387 int i = 0;
388 double step = dist * zoomcorr / _unitconv;
389 bool draw_minor = true;
390 if (step <= 0) {
391 return;
392 }
393 else if (step < 2) {
394 // marks too dense
395 draw_minor = false;
396 }
397 int last_pos = -1;
398 while (mark <= _drawing_width) {
399 cr->move_to(mark, _height);
400 if ((i % major_interval) == 0) {
401 // don't overcrowd the marks
402 if (static_cast<int>(mark) > last_pos) {
403 // major mark
404 cr->line_to(mark, 0);
406 draw_number(cr->cobj(), textpos, dist * i);
407
408 last_pos = static_cast<int>(mark) + 1;
409 }
410 } else if (draw_minor) {
411 // minor mark
413 }
414 mark += step;
415 ++i;
416 }
417}
418
419void
420ZoomCorrRuler::on_draw(Cairo::RefPtr<Cairo::Context> const &cr, int const width, int const height)
421{
422 auto const &w = width;
423 _drawing_width = w - _border * 2;
424
425 auto const fg = get_color();
426 cr->set_line_width(1);
427 cr->set_source_rgb(fg.get_red(), fg.get_green(), fg.get_blue());
428
429 cr->translate(_border, _border); // so that we have a small white border around the ruler
430 cr->move_to (0, _height);
431 cr->line_to (_drawing_width, _height);
432
434 Glib::ustring abbr = prefs->getString("/options/zoomcorrection/unit");
435 if (abbr == "cm") {
436 draw_marks(cr, 0.1, 10);
437 } else if (abbr == "in") {
438 draw_marks(cr, 0.25, 4);
439 } else if (abbr == "mm") {
440 draw_marks(cr, 10, 10);
441 } else if (abbr == "pc") {
442 draw_marks(cr, 1, 10);
443 } else if (abbr == "pt") {
444 draw_marks(cr, 10, 10);
445 } else if (abbr == "px") {
446 draw_marks(cr, 10, 10);
447 } else {
448 draw_marks(cr, 1, 1);
449 }
450 cr->stroke();
451}
452
453void
455{
456 if (this->get_visible() || freeze) //only take action if user changed value
457 {
458 freeze = true;
460 prefs->setDouble("/options/zoomcorrection/value", _slider->get_value() / 100.0);
461 _sb->set_value(_slider->get_value());
462 _ruler.queue_draw();
463 freeze = false;
464 }
465}
466
467void
469{
470 if (this->get_visible() || freeze) //only take action if user changed value
471 {
472 freeze = true;
474 prefs->setDouble("/options/zoomcorrection/value", _sb->get_value() / 100.0);
475 _slider->set_value(_sb->get_value());
476 _ruler.queue_draw();
477 freeze = false;
478 }
479}
480
481void
483 if (!_unit.get_sensitive()) {
484 // when the unit menu is initialized, the unit is set to the default but
485 // it needs to be reset later so we don't perform the change in this case
486 return;
487 }
489 prefs->setString("/options/zoomcorrection/unit", _unit.getUnitAbbr());
490 double conv = _unit.getConversion(_unit.getUnitAbbr(), "px");
492 if (_ruler.get_visible()) {
493 _ruler.queue_draw();
494 }
495}
496
498{
499 return _sb->mnemonic_activate ( group_cycling );
500}
501
502void
503ZoomCorrRulerSlider::init(int ruler_width, int ruler_height, double lower, double upper,
504 double step_increment, double page_increment, double default_value)
505{
507 double value = prefs->getDoubleLimited("/options/zoomcorrection/value", default_value, lower, upper) * 100.0;
508
509 freeze = false;
510
511 _ruler.set_size(ruler_width, ruler_height);
512
513 _slider = Gtk::make_managed<Gtk::Scale>(Gtk::Orientation::HORIZONTAL);
514
515 _slider->set_size_request(_ruler.width(), -1);
516 _slider->set_range (lower, upper);
517 _slider->set_increments (step_increment, page_increment);
518 _slider->set_value (value);
519 _slider->set_digits(2);
520
521 _slider->signal_value_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_slider_value_changed));
522 _sb = Gtk::make_managed<Inkscape::UI::Widget::SpinButton>();
523 _sb->signal_value_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_spinbutton_value_changed));
524 _unit.signal_changed().connect(sigc::mem_fun(*this, &ZoomCorrRulerSlider::on_unit_changed));
525
526 _sb->set_range (lower, upper);
527 _sb->set_increments (step_increment, 0);
528 _sb->set_value (value);
529 _sb->set_digits(2);
530 _sb->set_halign(Gtk::Align::CENTER);
531 _sb->set_valign(Gtk::Align::END);
532
533 _unit.set_sensitive(false);
535 _unit.set_sensitive(true);
536 _unit.setUnit(prefs->getString("/options/zoomcorrection/unit"));
537 _unit.set_halign(Gtk::Align::CENTER);
538 _unit.set_valign(Gtk::Align::END);
539
540 _slider->set_hexpand(true);
541 _ruler.set_hexpand(true);
542 auto const table = Gtk::make_managed<Gtk::Grid>();
543 table->attach(*_slider, 0, 0, 1, 1);
544 table->attach(*_sb, 1, 0, 1, 1);
545 table->attach(_ruler, 0, 1, 1, 1);
546 table->attach(_unit, 1, 1, 1, 1);
547
549}
550
551void
553{
554 if (this->get_visible() || freeze) //only take action if user changed value
555 {
556 freeze = true;
558 prefs->setDouble(_prefs_path, _slider->get_value());
559 if (_sb) _sb->set_value(_slider->get_value());
560 freeze = false;
561 }
562}
563
564void
566{
567 if (this->get_visible() || freeze) //only take action if user changed value
568 {
569 freeze = true;
571 if (_sb) {
572 prefs->setDouble(_prefs_path, _sb->get_value());
573 _slider->set_value(_sb->get_value());
574 }
575 freeze = false;
576 }
577}
578
579bool PrefSlider::on_mnemonic_activate ( bool group_cycling )
580{
581 return _sb ? _sb->mnemonic_activate ( group_cycling ) : false;
582}
583
584void
585PrefSlider::init(Glib::ustring const &prefs_path,
586 double lower, double upper, double step_increment, double page_increment, double default_value, int digits)
587{
589
591 double value = prefs->getDoubleLimited(prefs_path, default_value, lower, upper);
592
593 freeze = false;
594
595 _slider = Gtk::make_managed<Gtk::Scale>(Gtk::Orientation::HORIZONTAL);
596
597 _slider->set_range (lower, upper);
598 _slider->set_increments (step_increment, page_increment);
599 _slider->set_value (value);
600 _slider->set_digits(digits);
601 _slider->signal_value_changed().connect(sigc::mem_fun(*this, &PrefSlider::on_slider_value_changed));
602 if (_spin) {
603 _sb = Gtk::make_managed<Inkscape::UI::Widget::SpinButton>();
604 _sb->signal_value_changed().connect(sigc::mem_fun(*this, &PrefSlider::on_spinbutton_value_changed));
605 _sb->set_range (lower, upper);
606 _sb->set_increments (step_increment, 0);
607 _sb->set_value (value);
608 _sb->set_digits(digits);
609 _sb->set_halign(Gtk::Align::CENTER);
610 _sb->set_valign(Gtk::Align::END);
611 }
612
613 auto const table = Gtk::make_managed<Gtk::Grid>();
614 _slider->set_hexpand();
615 table->attach(*_slider, 0, 0, 1, 1);
616 if (_sb) table->attach(*_sb, 1, 0, 1, 1);
617
619}
620
621void PrefCombo::init(Glib::ustring const &prefs_path,
622 std::span<Glib::ustring const> labels,
623 std::span<int const> values,
624 int const default_value)
625{
626 int const labels_size = labels.size();
627 int const values_size = values.size();
628 if (values_size != labels_size) {
629 std::cerr << "PrefCombo::"
630 << "Different number of values/labels in " << prefs_path.raw() << std::endl;
631 return;
632 }
633
636 int row = 0;
637 int value = prefs->getInt(_prefs_path, default_value);
638
639 for (int i = 0; i < labels_size; ++i) {
640 this->append(labels[i]);
641 _values.push_back(values[i]);
642 if (value == values[i])
643 row = i;
644 }
645 this->set_active(row);
646}
647
648void PrefCombo::init(Glib::ustring const &prefs_path,
649 std::span<Glib::ustring const> labels,
650 std::span<Glib::ustring const> values,
651 Glib::ustring const &default_value)
652{
653 int const labels_size = labels.size();
654 int const values_size = values.size();
655 if (values_size != labels_size) {
656 std::cerr << "PrefCombo::"
657 << "Different number of values/labels in " << prefs_path.raw() << std::endl;
658 return;
659 }
660
663 int row = 0;
664 Glib::ustring value = prefs->getString(_prefs_path);
665 if (value.empty()) {
666 value = default_value;
667 }
668
669 for (int i = 0; i < labels_size; ++i) {
670 this->append(labels[i]);
671 _ustr_values.push_back(values[i]);
672 if (value == values[i])
673 row = i;
674 }
675 this->set_active(row);
676}
677
679{
680 if (this->get_visible()) //only take action if user changed value
681 {
683 if(!_values.empty())
684 {
685 prefs->setInt(_prefs_path, _values[this->get_active_row_number()]);
686 }
687 else
688 {
689 prefs->setString(_prefs_path, _ustr_values[this->get_active_row_number()]);
690 }
691 }
692}
693
694void PrefEntryButtonHBox::init(Glib::ustring const &prefs_path,
695 bool visibility, Glib::ustring const &default_string)
696{
698 _default_string = default_string;
700 relatedEntry = Gtk::make_managed<Gtk::Entry>();
701 relatedButton = Gtk::make_managed<Gtk::Button>(_("Reset"));
702 relatedEntry->set_invisible_char('*');
703 relatedEntry->set_visibility(visibility);
704 relatedEntry->set_text(prefs->getString(_prefs_path));
707 relatedButton->signal_clicked().connect(
709 relatedEntry->signal_changed().connect(
711}
712
714{
715 if (this->get_visible()) //only take action if user changed value
716 {
718 prefs->setString(_prefs_path, relatedEntry->get_text());
719 }
720}
721
723{
724 if (this->get_visible()) //only take action if user changed value
725 {
728 relatedEntry->set_text(_default_string);
729 }
730}
731
733{
734 return relatedEntry->mnemonic_activate ( group_cycling );
735}
736
738 bool visibility)
739{
742
743 relatedEntry = Gtk::make_managed<Gtk::Entry>();
744 relatedEntry->set_invisible_char('*');
745 relatedEntry->set_visibility(visibility);
746 relatedEntry->set_text(prefs->getString(_prefs_path));
747
748 relatedButton = Gtk::make_managed<Gtk::Button>();
749 auto const pixlabel = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 3);
750 auto const im = sp_get_icon_image("applications-graphics", Gtk::IconSize::NORMAL);
751 UI::pack_start(*pixlabel, *im);
752 auto const l = Gtk::make_managed<Gtk::Label>();
753 l->set_markup_with_mnemonic(_("_Browse..."));
754 UI::pack_start(*pixlabel, *l);
755 relatedButton->set_child(*pixlabel);
756
757 UI::pack_end(*this, *relatedButton, false, false, 4);
758 UI::pack_start(*this, *relatedEntry, true, true);
759
760 relatedButton->signal_clicked().connect(
762 relatedEntry->signal_changed().connect(
764}
765
767{
768 if (this->get_visible()) { // Only take action if user changed value.
770 prefs->setString(_prefs_path, Glib::filename_from_utf8(relatedEntry->get_text()));
771 }
772}
773
775
777{
778 if (this->get_visible()) { // Only take action if user changed value.
779
780 // Get the current directory for finding files.
781 static std::string open_path;
783
784 // Create a dialog.
785 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
789 *desktop->getToplevel(),
790 open_path,
792 _("Select a bitmap editor")).release();
793 }
794
795 // Show the dialog.
796 bool const success = selectPrefsFileInstance->show();
797
798 if (!success) {
799 return;
800 }
801
802 // User selected something, get file.
803 auto file = selectPrefsFileInstance->getFile();
804 if (!file) {
805 return;
806 }
807
808 auto path = file->get_path();
809 if (!path.empty()) {
810 open_path = path;;
811 }
812
813 if (!open_path.empty()) {
815 prefs->setString(_prefs_path, open_path);
816 }
817
818 relatedEntry->set_text(file->get_parse_name());
819 }
820}
821
823{
824 return relatedEntry->mnemonic_activate ( group_cycling );
825}
826
827void PrefOpenFolder::init(Glib::ustring const &entry_string, Glib::ustring const &tooltip)
828{
829 relatedEntry = Gtk::make_managed<Gtk::Entry>();
830 relatedButton = Gtk::make_managed<Gtk::Button>();
831 auto const pixlabel = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 3);
832 auto const im = sp_get_icon_image("document-open", Gtk::IconSize::NORMAL);
833 UI::pack_start(*pixlabel, *im);
834 auto const l = Gtk::make_managed<Gtk::Label>();
835 l->set_markup_with_mnemonic(_("Open"));
836 UI::pack_start(*pixlabel, *l);
837 relatedButton->set_child(*pixlabel);
838 relatedButton->set_tooltip_text(tooltip);
839 relatedEntry->set_text(entry_string);
840 relatedEntry->set_sensitive(false);
841 UI::pack_end(*this, *relatedButton, false, false, 4);
842 UI::pack_start(*this, *relatedEntry, true, true);
843 relatedButton->signal_clicked().connect(sigc::mem_fun(*this, &PrefOpenFolder::onRelatedButtonClickedCallback));
844}
845
847{
848 g_mkdir_with_parents(relatedEntry->get_text().c_str(), 0700);
849 // https://stackoverflow.com/questions/42442189/how-to-open-spawn-a-file-with-glib-gtkmm-in-windows
850#ifdef _WIN32
851 ShellExecute(NULL, "open", relatedEntry->get_text().c_str(), NULL, NULL, SW_SHOWDEFAULT);
852#elif defined(__APPLE__)
853 std::vector<std::string> argv = { "open", relatedEntry->get_text().raw() };
854 Glib::spawn_async("", argv, Glib::SpawnFlags::SEARCH_PATH);
855#else
856 char * const path = g_filename_to_uri(relatedEntry->get_text().c_str(), NULL, NULL);
857 std::vector<std::string> argv = { "xdg-open", path };
858 Glib::spawn_async("", argv, Glib::SpawnFlags::SEARCH_PATH);
859 g_free(path);
860#endif
861}
862
863void PrefEntry::init(Glib::ustring const &prefs_path, bool visibility)
864{
867 this->set_invisible_char('*');
868 this->set_visibility(visibility);
869 this->set_text(prefs->getString(_prefs_path));
870}
871
873{
874 if (this->get_visible()) //only take action if user changed value
875 {
877 prefs->setString(_prefs_path, this->get_text());
878 }
879}
880
882{
883 if (this->get_visible()) //only take action if user changed value
884 {
886 prefs->setString(_prefs_path, Glib::filename_to_utf8(this->get_text()));
887 }
888}
889
890void PrefMultiEntry::init(Glib::ustring const &prefs_path, int height)
891{
892 // TODO: Figure out if there's a way to specify height in lines instead of px
893 // and how to obtain a reasonable default width if 'expand_widget' is not used
894 set_size_request(100, height);
895 set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC);
896 set_has_frame(true);
897
898 set_child(_text);
899
902 Glib::ustring value = prefs->getString(_prefs_path);
903 value = Glib::Regex::create("\\|")->replace_literal(value, 0, "\n", (Glib::Regex::MatchFlags)0);
904 _text.get_buffer()->set_text(value);
905 _text.get_buffer()->signal_changed().connect(sigc::mem_fun(*this, &PrefMultiEntry::on_changed));
906}
907
909{
910 if (get_visible()) //only take action if user changed value
911 {
913 Glib::ustring value = _text.get_buffer()->get_text();
914 value = Glib::Regex::create("\\n")->replace_literal(value, 0, "|", (Glib::Regex::MatchFlags)0);
915 prefs->setString(_prefs_path, value);
916 }
917}
918
919void PrefColorPicker::init(Glib::ustring const &label, Glib::ustring const &prefs_path,
920 std::uint32_t const default_rgba)
921{
923 _title = label;
925 this->setRgba32( prefs->getInt(_prefs_path, (int)default_rgba) );
926}
927
928void PrefColorPicker::on_changed(std::uint32_t const rgba)
929{
930 if (this->get_visible()) //only take action if the user toggled it
931 {
933 prefs->setInt(_prefs_path, static_cast<int>(rgba));
934 }
935}
936
937void PrefUnit::init(Glib::ustring const &prefs_path)
938{
943}
944
946{
947 if (this->get_visible()) //only take action if user changed value
948 {
951 }
952}
953
954} // namespace Inkscape::UI::Widget
955
956/*
957 Local Variables:
958 mode:c++
959 c-file-style:"stroustrup"
960 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
961 indent-tabs-mode:nil
962 fill-column:99
963 End:
964*/
965// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
_PangoFontDescription PangoFontDescription
Definition: Layout-TNG.h:46
@ RIGHT
Definition: LivarotDefs.h:87
struct _PangoLayout PangoLayout
Two-dimensional point that doubles as a vector.
Definition: point.h:66
Preference storage class.
Definition: preferences.h:63
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
Definition: preferences.h:363
void setDoubleUnit(Glib::ustring const &pref_path, double value, Glib::ustring const &unit_abbr)
Set a floating point value with unit.
Glib::ustring getUnit(Glib::ustring const &pref_path)
Retrieve the unit string.
Definition: preferences.h:456
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
Definition: preferences.h:421
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
Definition: preferences.h:446
static Preferences * get()
Access the singleton Preferences object.
Definition: preferences.h:599
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
Definition: preferences.h:386
void setString(Glib::ustring const &pref_path, Glib::ustring const &value)
Set an UTF-8 string value.
void setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
int getIntLimited(Glib::ustring const &pref_path, int def=0, int min=INT_MIN, int max=INT_MAX)
Retrieve a limited integer.
Definition: preferences.h:401
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
double getDoubleLimited(Glib::ustring const &pref_path, double def=0.0, double min=DBL_MIN, double max=DBL_MAX, Glib::ustring const &unit="")
Retrieve a limited floating point value.
Definition: preferences.h:437
virtual bool show()=0
Show file selector.
This class provides an implementation-independent API for file "Open" dialogs.
Definition: filedialog.h:127
virtual Glib::RefPtr< Gio::File > getFile()=0
static std::unique_ptr< FileOpenDialog > create(Gtk::Window &parentWindow, std::string const &path, FileDialogType fileTypes, char const *title)
Factory.
Definition: filedialog.cpp:135
void setRgba32(std::uint32_t rgba)
void add_group_note(Glib::ustring 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)
Gtk::Widget const * getWidget() const
Definition: labelled.h:48
void init(Glib::ustring const &label, Glib::ustring const &prefs_path, bool default_value)
sigc::signal< void(bool)> changed_signal
void on_changed(std::uint32_t rgba) override
void init(Glib::ustring const &abel, Glib::ustring const &prefs_path, std::uint32_t default_rgba)
void init(Glib::ustring const &prefs_path, std::span< Glib::ustring const > labels, std::span< int const > values, int default_value)
std::vector< Glib::ustring > _ustr_values
string key values used optionally instead of numeric _values
bool on_mnemonic_activate(bool group_cycling) override
void init(Glib::ustring const &prefs_path, bool mask, Glib::ustring const &default_string)
bool on_mnemonic_activate(bool group_cycling) override
void init(Glib::ustring const &prefs_path, bool mask)
void init(Glib::ustring const &prefs_path, bool mask)
void init(Glib::ustring const &prefs_path, int height)
void init(Glib::ustring const &entry_string, Glib::ustring const &tooltip)
sigc::signal< void(bool)> changed_signal
void init(Glib::ustring const &label, Glib::ustring const &prefs_path, int int_value, bool default_value, PrefRadioButton *group_member)
PrefRadioButtons(const std::vector< PrefItem > &buttons, const Glib::ustring &prefs_path)
void init(Glib::ustring const &prefs_path, double lower, double upper, double step_increment, double page_increment, double default_value, int digits)
bool on_mnemonic_activate(bool group_cycling) override
Inkscape::UI::Widget::SpinButton * _sb
sigc::signal< void(double)> changed_signal
void init(Glib::ustring const &prefs_path, double lower, double upper, double step_increment, double page_increment, double default_value, bool is_int, bool is_percent)
void init(Glib::ustring const &prefs_path, double lower, double upper, double step_increment, double default_value, UnitType unit_type, Glib::ustring const &default_unit)
void init(Glib::ustring const &prefs_path)
void setValue(double number, Glib::ustring const &units)
Sets the number and unit system.
bool setUnit(Glib::ustring const &units)
Sets the unit for the ScalarUnit widget.
Definition: scalar-unit.cpp:74
void resetUnitType(UnitType unit_type)
Resets the unit type for the ScalarUnit widget.
Definition: scalar-unit.cpp:90
Unit const * getUnit() const
Gets the object for the currently selected unit.
Definition: scalar-unit.cpp:96
void setIncrements(double step, double page)
Sets the step and page increments for the spin button.
Definition: scalar.cpp:123
Glib::SignalProxy< void()> signal_value_changed()
Signal raised when the spin button's value changes.
Definition: scalar.cpp:159
void setDigits(unsigned digits)
Sets the precision to be displayed by the spin button.
Definition: scalar.cpp:94
void setRange(double min, double max)
Sets the minimum and maximum range allowed for the spin button.
Definition: scalar.cpp:128
double getValue() const
Get the value in the spin_button.
Definition: scalar.cpp:83
double getConversion(Glib::ustring const &new_unit_abbr, Glib::ustring const &old_unit_abbr="no_unit") const
Returns the conversion factor required to convert values of the currently selected unit into units of...
Definition: unit-menu.cpp:111
bool setUnit(Glib::ustring const &unit)
Sets the dropdown widget to the given unit abbreviation.
Definition: unit-menu.cpp:68
Glib::ustring getUnitAbbr() const
Returns the abbreviated unit name of the selected unit.
Definition: unit-menu.cpp:77
bool setUnitType(UnitType unit_type)
Adds the unit type to the widget.
Definition: unit-menu.cpp:30
bool on_mnemonic_activate(bool group_cycling) override
Inkscape::UI::Widget::SpinButton * _sb
void init(int ruler_width, int ruler_height, double lower, double upper, double step_increment, double page_increment, double default_value)
ZoomCorrRuler(int width=100, int height=20)
void draw_marks(Cairo::RefPtr< Cairo::Context > const &cr, double dist, int major_interval)
void on_draw(Cairo::RefPtr< Cairo::Context > const &cr, int width, int height)
To do: update description of desktop.
Definition: desktop.h:150
Gtk::Window const * getToplevel() const
Definition: desktop.cpp:1086
char const * label() const
Gets the author-visible label property for the object or a default if no label is defined.
Definition: sp-object.cpp:428
const double w
Definition: conic-4.cpp:19
Editable view implementation.
Virtual base definitions for native file dialogs.
static Glib::ustring const prefs_path
@ Y
Definition: coord.h:48
@ X
Definition: coord.h:48
macro for checking gtkmm version
Gtk::Image * sp_get_icon_image(Glib::ustring const &icon_name, int size)
Definition: icon-loader.cpp:27
Icon Loader.
SPItem * item
Definition: imagemagick.cpp:43
Raw stack of active status messages.
double dist(const Point &a, const Point &b)
Definition: geometry.cpp:310
void get_start_directory(std::string &start_path, Glib::ustring const &prefs_path, bool try_document_dir)
Set initial directory for dialog given a preference path.
Definition: filedialog.cpp:63
static const auto fontsize
Custom widgets.
Definition: desktop.h:127
static constexpr int height
static Inkscape::UI::Dialog::FileOpenDialog * selectPrefsFileInstance
static void draw_number(cairo_t *cr, Geom::Point pos, double num)
static void draw_text(cairo_t *cr, Geom::Point loc, const char *txt, bool bottom=false, double fontsize=ZoomCorrRuler::textsize, std::string fontdesc="Sans")
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 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
@ UNIT_TYPE_DIMENSIONLESS
Definition: units.h:33
@ UNIT_TYPE_LINEAR
Definition: units.h:34
static void append(std::vector< T > &target, std::vector< T > &&source)
Definition: shortcuts.cpp:515
@ HORIZONTAL
The x-dimension (0).
Definition: rectangle.h:43
@ VERTICAL
The y-dimension (1).
Definition: rectangle.h:47
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
struct _cairo cairo_t
Definition: path-cairo.h:16
void cairo_move_to(cairo_t *cr, Geom::Point p1)
Widgets for Inkscape Preferences dialog.
Singleton class to access the preferences file in a convenient way.
int num
Definition: scribble.cpp:47
double width
std::unique_ptr< Toolbar >(* create)(SPDesktop *desktop)
Definition: toolbars.cpp:63