Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
startup.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Copyright (C) Martin Owens 2019 <doctormo@gmail.com>
7 *
8 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9 */
10
11#include "startup.h"
12
13#include <glibmm/i18n.h>
14#ifdef GDK_WINDOWING_WAYLAND
15#include <gdk/wayland/gdkwayland.h>
16#endif
17#include <gtkmm/checkbutton.h>
18#include <gtkmm/combobox.h>
19#include <gtkmm/cssprovider.h>
20#include <gtkmm/eventcontrollerkey.h>
21#include <gtkmm/filefilter.h>
22#include <gtkmm/infobar.h>
23#include <gtkmm/liststore.h>
24#include <gtkmm/settings.h>
25#include <gtkmm/switch.h>
26#include <gtkmm/windowhandle.h>
27
29#include "inkscape.h"
30#include "inkscape-version.h"
32#include "io/recent-files.h"
33#include "io/resource.h"
34#include "ui/builder-utils.h"
37#include "ui/shortcuts.h"
38#include "ui/themes.h"
39#include "ui/util.h"
40
41using namespace Inkscape::IO;
42using namespace Inkscape::UI::View;
43
44namespace Inkscape::UI::Dialog {
45
46class NameIdCols: public Gtk::TreeModel::ColumnRecord {
47 public:
48 // These types must match those for the model in the .glade file
49 NameIdCols() {
50 this->add(this->col_name);
51 this->add(this->col_id);
52 }
53 Gtk::TreeModelColumn<Glib::ustring> col_name;
54 Gtk::TreeModelColumn<Glib::ustring> col_id;
55};
56
57class RecentCols: public Gtk::TreeModel::ColumnRecord {
58 public:
59 // These types must match those for the model in the .glade file
60 RecentCols() {
61 this->add(this->col_name);
62 this->add(this->col_id);
63 this->add(this->col_dt);
64 this->add(this->col_crash);
65 }
66 Gtk::TreeModelColumn<Glib::ustring> col_name;
67 Gtk::TreeModelColumn<Glib::ustring> col_id;
68 Gtk::TreeModelColumn<gint64> col_dt;
69 Gtk::TreeModelColumn<bool> col_crash;
70};
71
72class CanvasCols: public Gtk::TreeModel::ColumnRecord {
73 public:
74 // These types must match those for the model in the .glade file
75 CanvasCols() {
76 this->add(this->id);
77 this->add(this->name);
78 this->add(this->icon_filename);
79 this->add(this->pagecolor);
80 this->add(this->checkered);
81 this->add(this->bordercolor);
82 this->add(this->shadow);
83 this->add(this->deskcolor);
84 }
85 Gtk::TreeModelColumn<Glib::ustring> id;
86 Gtk::TreeModelColumn<Glib::ustring> name;
87 Gtk::TreeModelColumn<Glib::ustring> icon_filename;
88 Gtk::TreeModelColumn<Glib::ustring> pagecolor;
89 Gtk::TreeModelColumn<bool> checkered;
90 Gtk::TreeModelColumn<Glib::ustring> bordercolor;
91 Gtk::TreeModelColumn<bool> shadow;
92 Gtk::TreeModelColumn<Glib::ustring> deskcolor;
93};
94
95class ThemeCols: public Gtk::TreeModel::ColumnRecord {
96 public:
97 // These types must match those for the model in the .glade file
98 ThemeCols() {
99 this->add(this->id);
100 this->add(this->name);
101 this->add(this->theme);
102 this->add(this->icons);
103 this->add(this->base);
104 this->add(this->base_dark);
105 this->add(this->success);
106 this->add(this->warn);
107 this->add(this->error);
108 this->add(this->symbolic);
109 this->add(this->smallicons);
110 this->add(this->enabled);
111 }
112 Gtk::TreeModelColumn<Glib::ustring> id;
113 Gtk::TreeModelColumn<Glib::ustring> name;
114 Gtk::TreeModelColumn<Glib::ustring> theme;
115 Gtk::TreeModelColumn<Glib::ustring> icons;
116 Gtk::TreeModelColumn<Glib::ustring> base;
117 Gtk::TreeModelColumn<Glib::ustring> base_dark;
118 Gtk::TreeModelColumn<Glib::ustring> success;
119 Gtk::TreeModelColumn<Glib::ustring> warn;
120 Gtk::TreeModelColumn<Glib::ustring> error;
121 Gtk::TreeModelColumn<bool> symbolic;
122 Gtk::TreeModelColumn<bool> smallicons;
123 Gtk::TreeModelColumn<bool> enabled;
124};
125
127
129 : Gtk::Dialog()
130 , opt_shown(std::string("/options/boot/shown/ver") + Inkscape::version_string_without_revision)
131 , build_splash(create_builder("inkscape-splash.glade"))
132 // Global widgets
133 , banners (get_widget<Gtk::WindowHandle> (build_splash, "banner"))
134 , close_btn (get_widget<Gtk::Button> (build_splash, "close_window"))
135 , messages (get_widget<Gtk::Label> (build_splash, "messages"))
136{
137 set_name("start-screen-window");
138 set_title(Inkscape::inkscape_version());
139 set_focusable(true);
140 grab_focus();
141 set_receives_default(true);
142 set_default_widget(*this);
143 set_modal(true);
144
145 // Move banner to dialog window
146 set_titlebar(banners);
147 get_content_area()->append(messages);
148}
149
151{
152 set_default_size(700, 0);
153 set_resizable(false);
154
155 // Show the main banner when already welcomed for the first time
156 if (Inkscape::Preferences::get()->getBool(opt_shown, false)) {
157 auto const start_splash_file = Resource::get_filename(Resource::SCREENS, "start-splash.png");
158 get_widget<Gtk::Picture>(build_splash, "start-splash" ).set_filename(start_splash_file);
159 banner_switch(2);
160 } else {
161 auto const welcome_text_file = Resource::get_filename(Resource::SCREENS, "start-welcome-text.svg", true);
162 auto const start_welcome_file = Resource::get_filename(Resource::SCREENS, "start-welcome.png");
163 get_widget<Gtk::Picture>(build_splash, "welcome_text" ).set_filename(welcome_text_file);
164 get_widget<Gtk::Picture>(build_splash, "start-welcome" ).set_filename(start_welcome_file);
165 }
166
167 close_btn.hide();
168 property_resizable() = false;
169 show();
170 banners.show();
171 set_visible(true);
172 present(); // This makes the widget actually appear
173 _timer.start();
174
175 auto main_context = Glib::MainContext::get_default();
176 while (main_context->iteration(false)) {
177 }
178}
179
181{
182 _welcome = true;
183
184#ifdef GDK_WINDOWING_WAYLAND
185 // Hide the window for a short time so it can be repositioned
186 if (GDK_IS_WAYLAND_DISPLAY(this->get_display()->gobj())) {
187 hide();
188 auto main_context = Glib::MainContext::get_default();
189 while (main_context->iteration(false)) {
190 }
191 }
192#endif
193
194 set_default_size(700, 360);
195 grab_focus();
196 messages.hide();
197
198 build_welcome = create_builder("inkscape-welcome.glade");
199
200 // Populate with template extensions
202
203 recentfiles = &get_widget<Gtk::TreeView>(build_welcome, "recent_treeview");
204
205 auto tabs = &get_widget<Gtk::Notebook>(build_welcome, "tabs");
206 get_content_area()->append(*tabs);
207
208 // Get references to various widget used locally. (In order of appearance.)
209 auto canvas = &get_widget<Gtk::ComboBox> (build_welcome, "canvas");
210 auto themes = &get_widget<Gtk::ComboBox> (build_welcome, "themes");
211 auto keys = &get_widget<Gtk::ComboBox> (build_welcome, "keys");
212 auto kinds = &get_widget<Gtk::Notebook> (build_welcome, "kinds");
213 auto save = &get_widget<Gtk::Button> (build_welcome, "save");
214 auto thanks = &get_widget<Gtk::Button> (build_welcome, "thanks");
215 auto load_btn = &get_widget<Gtk::Button> (build_welcome, "load");
216 auto new_btn = &get_widget<Gtk::Button> (build_welcome, "new");
217 auto show_toggle = &get_widget<Gtk::CheckButton> (build_welcome, "show_toggle");
218 auto dark_toggle = &get_widget<Gtk::Switch> (build_welcome, "dark_toggle");
219
220 // Add signals and setup things.
221 auto prefs = Inkscape::Preferences::get();
222
223 auto const key = Gtk::EventControllerKey::create();
224 key->signal_key_pressed().connect(sigc::mem_fun(*this, &StartScreen::on_key_pressed), true);
225 add_controller(key);
226
227 _tabs_switch_page_conn = tabs->signal_switch_page().connect([this](Gtk::Widget *tab, unsigned page_num) {
228 banner_switch(page_num);
229 });
230
231 // Setup the lists of items
233 enlist_keys();
234 filter_themes(themes);
235 set_active_combo("themes", prefs->getString("/options/boot/theme"));
236 set_active_combo("canvas", prefs->getString("/options/boot/canvas"));
237
238 // initialise dark depending on prefs and background
240
241 // Load pictures. Gtk::Picture doesn't appear to be able to load image files from builder files.
242 auto const welcome_text_file = Resource::get_filename(Resource::SCREENS, "start-welcome-text.svg", true);
243 auto const start_welcome_file = Resource::get_filename(Resource::SCREENS, "start-welcome.png");
244 auto const start_support_file = Resource::get_filename(Resource::SCREENS, "start-support.png");
245 auto const start_splash_file = Resource::get_filename(Resource::SCREENS, "start-splash.png");
246 auto const start_support_time = Resource::get_filename(Resource::SCREENS, "start-support-time.png");
247 auto const start_support_money = Resource::get_filename(Resource::SCREENS, "start-support-money.png");
248
249 get_widget<Gtk::Picture>(build_splash, "welcome_text" ).set_filename(welcome_text_file);
250 get_widget<Gtk::Picture>(build_splash, "start-welcome" ).set_filename(start_welcome_file);
251 get_widget<Gtk::Picture>(build_splash, "start-support" ).set_filename(start_support_file);
252 get_widget<Gtk::Picture>(build_splash, "start-splash" ).set_filename(start_splash_file);
253 get_widget<Gtk::Picture>(build_welcome, "start-support-time" ).set_filename(start_support_time);
254 get_widget<Gtk::Picture>(build_welcome, "start-support-money").set_filename(start_support_money);
255
256 // Welcome! tab
257 canvas->signal_changed().connect(sigc::mem_fun(*this, &StartScreen::canvas_changed));
258 keys->signal_changed().connect(sigc::mem_fun(*this, &StartScreen::keyboard_changed));
259 themes->signal_changed().connect(sigc::mem_fun(*this, &StartScreen::theme_changed));
260 dark_toggle->property_active().signal_changed().connect(sigc::mem_fun(*this, &StartScreen::theme_changed));
261 save->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &StartScreen::notebook_next), save));
262
263 // "Supported by You" tab
264 thanks->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &StartScreen::notebook_next), thanks));
265
266 // "Time to Draw" tab
267 recentfiles->signal_row_activated().connect(sigc::hide(sigc::hide((sigc::mem_fun(*this, &StartScreen::load_document)))));
268 recentfiles->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &StartScreen::on_recent_changed));
270 load_btn->set_sensitive(true);
271
272 show_toggle->signal_toggled().connect(sigc::mem_fun(*this, &StartScreen::show_toggle));
273 load_btn->signal_clicked().connect(sigc::mem_fun(*this, &StartScreen::load_document));
274 templates.connectItemSelected([this](int){ new_document(); });
275 new_btn->signal_clicked().connect(sigc::mem_fun(*this, &StartScreen::new_document));
276 close_btn.signal_clicked().connect([this] { response(GTK_RESPONSE_CLOSE); });
277 close_btn.show();
278
279 // move pages from stack to our notebook
280 for (auto cat : templates.get_categories()) {
281 if (auto page = templates.get_child_by_name(cat)) {
282 page->reference();
283 templates.remove(*page);
284 kinds->append_page(*page, cat);
285 page->unreference();
286 }
287 }
288 kinds->signal_switch_page().connect([this](Gtk::Widget* page, auto) {
290 });
291
292 // Show the first tab ONLY on the first run for this version
293 std::string opt_shown = "/options/boot/shown/ver";
295 if (!prefs->getBool(opt_shown, false)) {
297 tabs->set_current_page(0);
298 prefs->setBool(opt_shown, true);
299 } else {
300 tabs->set_current_page(2);
301 }
302 // Refresh keyboard warning message
304
305 show();
306}
307
315Gtk::TreeModel::Row
316StartScreen::active_combo(std::string widget_name)
317{
318 auto &combo = get_widget<Gtk::ComboBox>(build_welcome, widget_name.c_str());
319 Gtk::TreeModel::iterator iter = combo.get_active();
320 if (!iter) throw 2;
321 Gtk::TreeModel::Row row = *iter;
322 if (!row) throw 3;
323 return row;
324}
325
332void
333StartScreen::set_active_combo(std::string widget_name, std::string unique_id)
334{
335 auto &combo = get_widget<Gtk::ComboBox>(build_welcome, widget_name.c_str());
336 if (unique_id.empty()) {
337 combo.set_active(0); // Select the first
338 } else if (!combo.set_active_id(unique_id)) {
339 combo.set_active(-1); // Select nothing
340 }
341}
342
346void
348{
349 auto &stack = get_widget<Gtk::Stack>(build_splash, "banner-stack");
350 auto const pages = UI::get_children(stack);
351 auto &page = *pages.at(page_num);
352 stack.set_visible_child(page);
353}
354
355void
357{
358 RecentCols cols;
359
360 auto store = &dynamic_cast<Gtk::ListStore &>(*recentfiles->get_model());
361 store->clear();
362 // Now sort the result by visited time
363 store->set_sort_column(cols.col_dt, Gtk::SortType::DESCENDING);
364
365 // Open [other]
366 Gtk::TreeModel::Row first_row = *(store->append());
367 first_row[cols.col_name] = _("Browse for other files...");
368 first_row[cols.col_id] = "";
369 first_row[cols.col_dt] = std::numeric_limits<gint64>::max();
370 recentfiles->get_selection()->select(store->get_path(first_row.get_iter()));
371
372 auto recent_files = Inkscape::getInkscapeRecentFiles();
373 auto shortened_path_map = Inkscape::getShortenedPathMap(recent_files);
374
375 for (auto const &recent_file : recent_files) {
376 // This uri is a GVFS uri, so parse it with that or it will fail.
377 auto file = Gio::File::create_for_uri(recent_file->get_uri());
378 std::string path = file->get_path();
379 // Note: Do not check if the file exists, to avoid long delays. See https://gitlab.com/inkscape/inkscape/-/issues/2348 .
380 if (!path.empty() && recent_file->get_mime_type() == "image/svg+xml") {
381 Gtk::TreeModel::Row row = *(store->append());
382 row[cols.col_name] = shortened_path_map[recent_file->get_uri_display()];
383 row[cols.col_id] = recent_file->get_uri();
384 row[cols.col_dt] = recent_file->get_modified().to_unix();
385 row[cols.col_crash] = recent_file->has_group("Crash");
386 }
387 }
388}
389
393void
395{
396 // TODO: In the future this is where previews and other information can be loaded.
397}
398
402void StartScreen::on_kind_changed(const Glib::ustring& name)
403{
404 auto load_btn = &get_widget<Gtk::Button>(build_welcome, "load");
405 load_btn->set_visible(name == "???");
406}
407
411void
413{
414 // Generate a new document from the selected template.
416 if (_document) {
417 // Quit welcome screen if options not 'canceled'
418 response(GTK_RESPONSE_APPLY);
419 }
420}
421
427{
428 auto kinds = &get_widget<Gtk::Notebook>(build_welcome, "kinds");
429 return templates.new_document(kinds->get_nth_page(kinds->get_current_page()));
430}
431
435void
437{
438 RecentCols cols;
440
441 auto iter = recentfiles->get_selection()->get_selected();
442 if (iter) {
443 Gtk::TreeModel::Row row = *iter;
444 if (row) {
445 Glib::ustring uri = row[cols.col_id];
446 Glib::RefPtr<Gio::File> file;
447
448 if (!uri.empty()) {
449 file = Gio::File::create_for_uri(uri);
450 } else {
451 // Browse for file instead
452 std::string open_path;
453 get_start_directory(open_path, "/dialogs/open/path");
454
455 std::string current_folder;
456 get_start_directory(current_folder, "/dialogs/open/path");
457
458 auto filters = create_open_filters();
459 file = choose_file_open(_("Open a different file"), this, filters, current_folder);
460
461 if (!file) {
462 return; // Cancel
463 }
464
465 auto prefs = Inkscape::Preferences::get();
466 prefs->setString("/dialogs/open/path", current_folder);
467 }
468
469 // Now we have file, open document.
470 _document = app->document_open(file).first;
471
472 if (_document) {
473 // We're done, hand back to app but first flush conflicting signals
474 // like signal_row_activated which blocks if the dialog is destroyed
475 auto main_context = Glib::MainContext::get_default();
476 while (main_context->iteration(false)) {}
477
478 response(GTK_RESPONSE_OK);
479 }
480 }
481 }
482}
483
487void
488StartScreen::notebook_next(Gtk::Widget *button)
489{
490 auto tabs = &get_widget<Gtk::Notebook>(build_welcome, "tabs");
491 int page = tabs->get_current_page();
492 if (page == 2) {
493 response(GTK_RESPONSE_CANCEL); // Only occurs from keypress.
494 } else {
495 tabs->set_current_page(page + 1);
496 }
497}
498
502bool StartScreen::on_key_pressed(unsigned keyval, unsigned /*keycode*/, Gdk::ModifierType state)
503{
504#ifdef GDK_WINDOWING_QUARTZ
505 // On macOS only, if user press Cmd+Q => exit
506 if (keyval == 'q' && static_cast<GdkModifierType>(state) == (GDK_MOD2_MASK | GDK_META_MASK)) {
507 close();
508 return false;
509 }
510#endif
511
512 switch (keyval) {
513 case GDK_KEY_Escape:
514 // Prevent loading any selected items
515 response(GTK_RESPONSE_CANCEL);
516 return true;
517 case GDK_KEY_Return:
518 notebook_next(nullptr);
519 return true;
520 }
521
522 return false;
523}
524
525void
527{
528 if (response_id == GTK_RESPONSE_DELETE_EVENT || response_id == GTK_RESPONSE_CLOSE) {
529 // Don't open a window for force closing.
530 return;
531 }
532 if (response_id == GTK_RESPONSE_CANCEL) {
534 }
535 if (response_id != GTK_RESPONSE_OK && !_document) {
536 // Last ditch attempt to generate a new document while exiting.
538 }
539}
540
541
550{
551 auto prefs = Inkscape::Preferences::get();
552 auto old_enabled = prefs->getBool("/options/boot/enabled", true);
553 return prefs->getInt("/options/boot/mode", old_enabled ? 1 : 0);
554}
555
556void
558{
559 auto &button = get_widget<Gtk::CheckButton>(build_welcome, "show_toggle");
560 auto prefs = Inkscape::Preferences::get();
561 prefs->setInt("/options/boot/mode", button.get_active() ? 1 : 0);
562}
563
571void
572StartScreen::refresh_theme(Glib::ustring theme_name)
573{
574 auto const display = Gdk::Display::get_default();
575
576 if (INKSCAPE.themecontext->getContrastThemeProvider()) {
577 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getContrastThemeProvider());
578 }
579
580 auto settings = Gtk::Settings::get_default();
581 auto prefs = Inkscape::Preferences::get();
582
583 settings->property_gtk_theme_name() = theme_name;
584 settings->property_gtk_application_prefer_dark_theme() = prefs->getBool("/theme/preferDarkTheme", true);
585 settings->property_gtk_icon_theme_name() = prefs->getString("/theme/iconTheme", prefs->getString("/theme/defaultIconTheme", ""));
586
587 if (prefs->getBool("/theme/symbolicIcons", false)) {
588 add_css_class("symbolic");
589 remove_css_class("regular");
590 } else {
591 add_css_class("regular");
592 remove_css_class("symbolic");
593 }
594
595 if (INKSCAPE.themecontext->getColorizeProvider()) {
596 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getColorizeProvider());
597 }
598
599 if (!prefs->getBool("/theme/symbolicDefaultHighColors", false)) {
600 Gtk::CssProvider::create();
601 Glib::ustring css_str = INKSCAPE.themecontext->get_symbolic_colors();
602 try {
603 INKSCAPE.themecontext->getColorizeProvider()->load_from_string(css_str);
604 } catch (Gtk::CssParserError const &ex) {
605 g_critical("CSSProviderError::load_from_data(): failed to load '%s'\n(%s)", css_str.c_str(), ex.what());
606 }
607 Gtk::StyleProvider::add_provider_for_display(display, INKSCAPE.themecontext->getColorizeProvider(),
608 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
609 }
610 // set dark switch and disable if there is no prefer option for dark
612
613 INKSCAPE.themecontext->getChangeThemeSignal().emit();
614}
615
621void
623{
624 auto prefs = Inkscape::Preferences::get();
625
626 ThemeCols cols;
627 try {
628 auto row = active_combo("themes");
629 Glib::ustring theme_id = row[cols.id];
630 if (theme_id == "custom") return;
631 prefs->setString("/options/boot/theme", row[cols.id]);
632
633 // Update theme from combo.
634 Glib::ustring icons = row[cols.icons];
635 prefs->setBool("/toolbox/tools/small", row[cols.smallicons]);
636 prefs->setString("/theme/gtkTheme", row[cols.theme]);
637 prefs->setString("/theme/iconTheme", icons);
638 prefs->setBool("/theme/symbolicIcons", row[cols.symbolic]);
639
640 auto &dark_toggle = get_widget<Gtk::Switch>(build_welcome, "dark_toggle");
641 bool is_dark = dark_toggle.get_active();
642 prefs->setBool("/theme/preferDarkTheme", is_dark);
643 prefs->setBool("/theme/darkTheme", is_dark);
644 // Symbolic icon colours
645 if (get_color_value(row[cols.base]) == 0) {
646 prefs->setBool("/theme/symbolicDefaultBaseColors", true);
647 prefs->setBool("/theme/symbolicDefaultHighColors", true);
648 } else {
649 Glib::ustring prefix = "/theme/" + icons;
650 prefs->setBool("/theme/symbolicDefaultBaseColors", false);
651 prefs->setBool("/theme/symbolicDefaultHighColors", false);
652 if (is_dark) {
653 prefs->setUInt(prefix + "/symbolicBaseColor", get_color_value(row[cols.base_dark]));
654 } else {
655 prefs->setUInt(prefix + "/symbolicBaseColor", get_color_value(row[cols.base]));
656 }
657 prefs->setUInt(prefix + "/symbolicSuccessColor", get_color_value(row[cols.success]));
658 prefs->setUInt(prefix + "/symbolicWarningColor", get_color_value(row[cols.warn]));
659 prefs->setUInt(prefix + "/symbolicErrorColor", get_color_value(row[cols.error]));
660 }
661
662 refresh_theme(prefs->getString("/theme/gtkTheme", prefs->getString("/theme/defaultGtkTheme", "")));
663 } catch(int e) {
664 g_warning("Couldn't find theme value.");
665 }
666}
667
671void
673{
674 CanvasCols cols;
675 try {
676 auto row = active_combo("canvas");
677
678 auto prefs = Inkscape::Preferences::get();
679 prefs->setString("/options/boot/canvas", row[cols.id]);
680
681 Gdk::RGBA gdk_color = Gdk::RGBA(row[cols.pagecolor]);
682 prefs->setString("/template/base/pagecolor", gdk_to_css_color(gdk_color));
683 prefs->setDouble("/template/base/pageopacity", gdk_color.get_alpha());
684
685 Gdk::RGBA gdk_border = Gdk::RGBA(row[cols.bordercolor]);
686 prefs->setString("/template/base/bordercolor", gdk_to_css_color(gdk_border));
687 prefs->setDouble("/template/base/borderopacity", gdk_border.get_alpha());
688
689 prefs->setBool("/template/base/pagecheckerboard", row[cols.checkered]);
690 prefs->setInt("/template/base/pageshadow", row[cols.shadow] ? 2 : 0);
691
692 Gdk::RGBA gdk_desk = Gdk::RGBA(row[cols.deskcolor]);
693 prefs->setString("/template/base/deskcolor", gdk_to_css_color(gdk_desk));
694 } catch(int e) {
695 g_warning("Couldn't find canvas value.");
696 }
697}
698
699void
700StartScreen::filter_themes(Gtk::ComboBox *themes)
701{
702 ThemeCols cols;
703 // We need to disable themes which aren't available.
704 auto store = &dynamic_cast<Gtk::ListStore &>(*themes->get_model());
705 auto available = INKSCAPE.themecontext->get_available_themes();
706
707 // Detect use of custom theme here, detect defaults used in many systems.
708 auto settings = Gtk::Settings::get_default();
709 Glib::ustring theme_name = settings->property_gtk_theme_name();
710 Glib::ustring icons_name = settings->property_gtk_icon_theme_name();
711
712 bool has_system_theme = false;
713 if (theme_name != "Adwaita" || icons_name != "hicolor") {
714 has_system_theme = true;
715 /* Enable if/when we want custom to be the default.
716 if (prefs->getString("/options/boot/theme").empty()) {
717 prefs->setString("/options/boot/theme", "system")
718 theme_changed();
719 }*/
720 }
721
722 for(auto row : store->children()) {
723 Glib::ustring theme = row[cols.theme];
724 if (!row[cols.enabled]) {
725 // Available themes; We only "enable" them, we don't disable them.
726 row[cols.enabled] = available.find(theme) != available.end();
727 } else if(row[cols.id] == "system" && !has_system_theme) {
728 // Disable system theme option if not available.
729 row[cols.enabled] = false;
730 }
731 }
732}
733
734void
736{
737 auto prefs = Inkscape::Preferences::get();
738 auto current_file = prefs->getString("/options/kbshortcuts/shortcutfile", "inkscape.xml");
739 auto &keys_warning = get_widget<Gtk::InfoBar>(build_welcome, "keys_warning");
740 if (current_file != "inkscape.xml" && current_file != "default.xml") {
741 keys_warning.set_visible(true);
742 } else {
743 keys_warning.set_message_type(Gtk::MessageType::WARNING);
744 keys_warning.set_visible(false);
745 }
746}
747
748void
750{
751 NameIdCols cols;
752 auto &keys = get_widget<Gtk::ComboBox>(build_welcome, "keys");
753
754 auto store = &dynamic_cast<Gtk::ListStore &>(*keys.get_model());
755 store->clear();
756
757 for (auto const &item : Inkscape::Shortcuts::get_file_names()) {
758 Gtk::TreeModel::Row row = *(store->append());
759 row[cols.col_name] = item.first;
760 row[cols.col_id] = item.second;
761 }
762
763 auto prefs = Inkscape::Preferences::get();
764 auto current = prefs->getString("/options/kbshortcuts/shortcutfile");
765 if (current.empty()) {
766 current = "inkscape.xml";
767 }
768 keys.set_active_id(current);
769}
770
774void
776{
777 NameIdCols cols;
778 auto row = active_combo("keys");
779 auto prefs = Inkscape::Preferences::get();
780 Glib::ustring set_to = row[cols.col_id];
781 prefs->setString("/options/kbshortcuts/shortcutfile", set_to);
783}
784
790{
791 auto prefs = Inkscape::Preferences::get();
792
793 auto const window = dynamic_cast<Gtk::Window *>(get_root());
794 bool dark = INKSCAPE.themecontext->isCurrentThemeDark(window);
795 prefs->setBool("/theme/preferDarkTheme", dark);
796 prefs->setBool("/theme/darkTheme", dark);
797
798 auto themes = INKSCAPE.themecontext->get_available_themes();
799 Glib::ustring current_theme = prefs->getString("/theme/gtkTheme", prefs->getString("/theme/defaultGtkTheme", ""));
800
801 auto &dark_toggle = get_widget<Gtk::Switch>(build_welcome, "dark_toggle");
802 dark_toggle.set_active(dark);
803}
804
805} // namespace Inkscape::UI::Dialog
806
807/*
808 Local Variables:
809 mode:c++
810 c-file-style:"stroustrup"
811 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
812 indent-tabs-mode:nil
813 fill-column:99
814 End:
815*/
816// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Gtk builder utilities.
uint64_t page
Definition canvas.cpp:171
Fragment store
Definition canvas.cpp:155
static InkscapeApplication * instance()
Singleton instance.
static Preferences * get()
Access the singleton Preferences object.
static std::vector< std::pair< Glib::ustring, std::string > > get_file_names()
Get a list of filenames to populate menu in preferences dialog.
void on_response(int response_id) override
Definition startup.cpp:526
void refresh_dark_switch()
Set current state of Dark Switch based on current selected theme.
Definition startup.cpp:789
SPDocument * get_template_document()
Return the template document from the kinds widget.
Definition startup.cpp:426
Gtk::WindowHandle & banners
Definition startup.h:78
static int get_start_mode()
Get the preference for the startup mode.
Definition startup.cpp:549
Inkscape::UI::Widget::TemplateList templates
Definition startup.h:81
void notebook_next(Gtk::Widget *button)
When a button needs to go to the next notebook page.
Definition startup.cpp:488
void on_recent_changed()
Called when a new recent document is selected.
Definition startup.cpp:394
void refresh_theme(Glib::ustring theme_name)
Refresh theme in-place so user can see a semi-preview.
Definition startup.cpp:572
void banner_switch(unsigned page_num)
When a notbook is switched, reveal the right banner image (gtk signal).
Definition startup.cpp:347
void keyboard_changed()
Set the keys file based on the keys set in the enlist above.
Definition startup.cpp:775
sigc::scoped_connection _tabs_switch_page_conn
Definition startup.h:89
Glib::RefPtr< Gtk::Builder > build_welcome
Definition startup.h:83
Glib::RefPtr< Gtk::Builder > build_splash
Definition startup.h:77
void new_document()
Called when new button clicked or template is double clicked, or escape pressed.
Definition startup.cpp:412
void on_kind_changed(const Glib::ustring &name)
Called when the left side tabs are changed.
Definition startup.cpp:402
void filter_themes(Gtk::ComboBox *themes)
Definition startup.cpp:700
const std::string opt_shown
Definition startup.h:74
void load_document()
Called when load button clicked.
Definition startup.cpp:436
void canvas_changed()
Called when the canvas dropdown changes.
Definition startup.cpp:672
Gtk::TreeModel::Row active_combo(std::string widget_name)
Return the active row of the named combo box.
Definition startup.cpp:316
bool on_key_pressed(unsigned keyval, unsigned keycode, Gdk::ModifierType state)
When a key is pressed in the main window.
Definition startup.cpp:502
sigc::scoped_connection _templates_switch_page_conn
Definition startup.h:90
void set_active_combo(std::string widget_name, std::string unique_id)
Set the active item in the combo based on the unique_id (column set in glade)
Definition startup.cpp:333
void theme_changed()
Set the theme, icon pack and other theme options from a set defined in the glade file.
Definition startup.cpp:622
void init(Extension::TemplateShow mode, AddPage add_page, bool allow_unselect=false)
Initialise this template list with categories and icons.
void reset_selection(Gtk::Widget *current_page=nullptr)
Reset the selection, forcing the use of the default template.
const std::vector< Glib::ustring > & get_categories() const
sigc::signal< void(const Glib::ustring &)> signal_switch_page()
SPDocument * new_document(Gtk::Widget *current_page=nullptr)
Create a new document based on the selected item and return.
sigc::connection connectItemSelected(const sigc::slot< void(int)> &slot)
Typed SVG document implementation.
Definition document.h:101
static char const *const current
Definition dir-util.cpp:71
SPItem * item
Consolidates version info for Inkscape, its various dependencies and the OS we're running on.
Mini static library that contains the version of Inkscape.
Definition desktop.h:50
std::string get_filename(Type type, char const *filename, bool localized, bool silent)
Definition resource.cpp:170
Low-level IO code.
Dialog code.
Definition desktop.h:117
void get_start_directory(std::string &start_path, Glib::ustring const &prefs_path, bool try_document_dir)
Find the start directory for a file dialog.
Glib::RefPtr< Gio::ListStore< Gtk::FileFilter > > create_open_filters()
Create a Gtk::FileFilter for all image file types.
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:141
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
Helper class to stream background task notifications as a series of messages.
std::vector< Glib::RefPtr< Gtk::RecentInfo > > getInkscapeRecentFiles(unsigned max_files)
Generate a vector of recently used Inkscape files.
std::map< Glib::ustring, std::string > getShortenedPathMap(std::vector< Glib::RefPtr< Gtk::RecentInfo > > const &recent_files)
Generate the shortened labeles for a list of recently used files.
char const * version_string_without_revision
version string excluding revision and date
std::string inkscape_version()
Return Inkscape version string.
Glib::RefPtr< Gio::File > choose_file_open(Glib::ustring const &title, Gtk::Window *parent, Glib::RefPtr< Gio::ListStore< Gtk::FileFilter > > const &filters_model, std::string &current_folder, Glib::ustring const &accept)
Synchronously run a Gtk::FileDialog to open a single file for reading data.
STL namespace.
static cairo_user_data_key_t key
Inkscape::IO::Resource - simple resource API.
A dialog for the start screen.
Gtk <themes> helper code.
Glib::ustring name
Definition toolbars.cpp:55
Gtk::Stack & stack
unsigned int get_color_value(const Glib::ustring color)
Color is store as a string in the form #RRGGBBAA, '0' means "unset".
Definition util.cpp:260
Glib::ustring gdk_to_css_color(const Gdk::RGBA &color)
These GUI related color conversions allow us to convert from SVG xml attributes to Gdk colors,...
Definition util.cpp:325