13#include <glibmm/i18n.h>
14#ifdef GDK_WINDOWING_WAYLAND
15#include <gdk/wayland/gdkwayland.h>
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>
46class NameIdCols:
public Gtk::TreeModel::ColumnRecord {
50 this->add(this->col_name);
51 this->add(this->col_id);
53 Gtk::TreeModelColumn<Glib::ustring> col_name;
54 Gtk::TreeModelColumn<Glib::ustring> col_id;
57class RecentCols:
public Gtk::TreeModel::ColumnRecord {
61 this->add(this->col_name);
62 this->add(this->col_id);
63 this->add(this->col_dt);
64 this->add(this->col_crash);
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;
72class CanvasCols:
public Gtk::TreeModel::ColumnRecord {
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);
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;
95class ThemeCols:
public Gtk::TreeModel::ColumnRecord {
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);
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;
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"))
137 set_name(
"start-screen-window");
141 set_receives_default(
true);
142 set_default_widget(*
this);
147 get_content_area()->append(
messages);
152 set_default_size(700, 0);
153 set_resizable(
false);
158 get_widget<Gtk::Picture>(
build_splash,
"start-splash" ).set_filename(start_splash_file);
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);
168 property_resizable() =
false;
175 auto main_context = Glib::MainContext::get_default();
176 while (main_context->iteration(
false)) {
184#ifdef GDK_WINDOWING_WAYLAND
186 if (GDK_IS_WAYLAND_DISPLAY(this->get_display()->gobj())) {
188 auto main_context = Glib::MainContext::get_default();
189 while (main_context->iteration(
false)) {
194 set_default_size(700, 360);
205 auto tabs = &get_widget<Gtk::Notebook>(
build_welcome,
"tabs");
206 get_content_area()->append(*tabs);
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");
218 auto dark_toggle = &get_widget<Gtk::Switch> (
build_welcome,
"dark_toggle");
223 auto const key = Gtk::EventControllerKey::create();
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);
270 load_btn->set_sensitive(
true);
276 close_btn.signal_clicked().connect([
this] { response(GTK_RESPONSE_CLOSE); });
284 kinds->append_page(*
page, cat);
293 std::string
opt_shown =
"/options/boot/shown/ver";
297 tabs->set_current_page(0);
300 tabs->set_current_page(2);
318 auto &combo = get_widget<Gtk::ComboBox>(
build_welcome, widget_name.c_str());
319 Gtk::TreeModel::iterator iter = combo.get_active();
321 Gtk::TreeModel::Row row = *iter;
335 auto &combo = get_widget<Gtk::ComboBox>(
build_welcome, widget_name.c_str());
336 if (unique_id.empty()) {
338 }
else if (!combo.set_active_id(unique_id)) {
339 combo.set_active(-1);
351 auto &
page = *pages.at(page_num);
363 store->set_sort_column(cols.col_dt, Gtk::SortType::DESCENDING);
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()));
375 for (
auto const &recent_file : recent_files) {
377 auto file = Gio::File::create_for_uri(recent_file->get_uri());
378 std::string path = file->get_path();
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");
404 auto load_btn = &get_widget<Gtk::Button>(
build_welcome,
"load");
405 load_btn->set_visible(
name ==
"???");
418 response(GTK_RESPONSE_APPLY);
428 auto kinds = &get_widget<Gtk::Notebook>(
build_welcome,
"kinds");
441 auto iter =
recentfiles->get_selection()->get_selected();
443 Gtk::TreeModel::Row row = *iter;
445 Glib::ustring uri = row[cols.col_id];
446 Glib::RefPtr<Gio::File> file;
449 file = Gio::File::create_for_uri(uri);
452 std::string open_path;
455 std::string current_folder;
459 file =
choose_file_open(_(
"Open a different file"),
this, filters, current_folder);
466 prefs->setString(
"/dialogs/open/path", current_folder);
470 _document = app->document_open(file).first;
475 auto main_context = Glib::MainContext::get_default();
476 while (main_context->iteration(
false)) {}
478 response(GTK_RESPONSE_OK);
490 auto tabs = &get_widget<Gtk::Notebook>(
build_welcome,
"tabs");
491 int page = tabs->get_current_page();
493 response(GTK_RESPONSE_CANCEL);
495 tabs->set_current_page(
page + 1);
504#ifdef GDK_WINDOWING_QUARTZ
506 if (keyval ==
'q' &&
static_cast<GdkModifierType
>(state) == (GDK_MOD2_MASK | GDK_META_MASK)) {
515 response(GTK_RESPONSE_CANCEL);
528 if (response_id == GTK_RESPONSE_DELETE_EVENT || response_id == GTK_RESPONSE_CLOSE) {
532 if (response_id == GTK_RESPONSE_CANCEL) {
535 if (response_id != GTK_RESPONSE_OK && !
_document) {
552 auto old_enabled = prefs->getBool(
"/options/boot/enabled",
true);
553 return prefs->getInt(
"/options/boot/mode", old_enabled ? 1 : 0);
559 auto &button = get_widget<Gtk::CheckButton>(
build_welcome,
"show_toggle");
561 prefs->setInt(
"/options/boot/mode", button.get_active() ? 1 : 0);
574 auto const display = Gdk::Display::get_default();
576 if (INKSCAPE.themecontext->getContrastThemeProvider()) {
577 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getContrastThemeProvider());
580 auto settings = Gtk::Settings::get_default();
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",
""));
587 if (prefs->getBool(
"/theme/symbolicIcons",
false)) {
588 add_css_class(
"symbolic");
589 remove_css_class(
"regular");
591 add_css_class(
"regular");
592 remove_css_class(
"symbolic");
595 if (INKSCAPE.themecontext->getColorizeProvider()) {
596 Gtk::StyleProvider::remove_provider_for_display(display, INKSCAPE.themecontext->getColorizeProvider());
599 if (!prefs->getBool(
"/theme/symbolicDefaultHighColors",
false)) {
600 Gtk::CssProvider::create();
601 Glib::ustring css_str = INKSCAPE.themecontext->get_symbolic_colors();
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());
607 Gtk::StyleProvider::add_provider_for_display(display, INKSCAPE.themecontext->getColorizeProvider(),
608 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
613 INKSCAPE.themecontext->getChangeThemeSignal().emit();
629 Glib::ustring theme_id = row[cols.id];
630 if (theme_id ==
"custom")
return;
631 prefs->setString(
"/options/boot/theme", row[cols.id]);
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]);
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);
646 prefs->setBool(
"/theme/symbolicDefaultBaseColors",
true);
647 prefs->setBool(
"/theme/symbolicDefaultHighColors",
true);
649 Glib::ustring prefix =
"/theme/" + icons;
650 prefs->setBool(
"/theme/symbolicDefaultBaseColors",
false);
651 prefs->setBool(
"/theme/symbolicDefaultHighColors",
false);
653 prefs->setUInt(prefix +
"/symbolicBaseColor",
get_color_value(row[cols.base_dark]));
655 prefs->setUInt(prefix +
"/symbolicBaseColor",
get_color_value(row[cols.base]));
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]));
662 refresh_theme(prefs->getString(
"/theme/gtkTheme", prefs->getString(
"/theme/defaultGtkTheme",
"")));
664 g_warning(
"Couldn't find theme value.");
679 prefs->setString(
"/options/boot/canvas", row[cols.id]);
681 Gdk::RGBA gdk_color = Gdk::RGBA(row[cols.pagecolor]);
683 prefs->setDouble(
"/template/base/pageopacity", gdk_color.get_alpha());
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());
689 prefs->setBool(
"/template/base/pagecheckerboard", row[cols.checkered]);
690 prefs->setInt(
"/template/base/pageshadow", row[cols.shadow] ? 2 : 0);
692 Gdk::RGBA gdk_desk = Gdk::RGBA(row[cols.deskcolor]);
695 g_warning(
"Couldn't find canvas value.");
704 auto store = &
dynamic_cast<Gtk::ListStore &
>(*themes->get_model());
705 auto available = INKSCAPE.themecontext->get_available_themes();
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();
712 bool has_system_theme =
false;
713 if (theme_name !=
"Adwaita" || icons_name !=
"hicolor") {
714 has_system_theme =
true;
722 for(
auto row :
store->children()) {
723 Glib::ustring theme = row[cols.theme];
724 if (!row[cols.enabled]) {
726 row[cols.enabled] = available.find(theme) != available.end();
727 }
else if(row[cols.id] ==
"system" && !has_system_theme) {
729 row[cols.enabled] =
false;
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);
743 keys_warning.set_message_type(Gtk::MessageType::WARNING);
744 keys_warning.set_visible(
false);
752 auto &keys = get_widget<Gtk::ComboBox>(
build_welcome,
"keys");
754 auto store = &
dynamic_cast<Gtk::ListStore &
>(*keys.get_model());
758 Gtk::TreeModel::Row row = *(
store->append());
759 row[cols.col_name] =
item.first;
760 row[cols.col_id] =
item.second;
764 auto current = prefs->getString(
"/options/kbshortcuts/shortcutfile");
780 Glib::ustring set_to = row[cols.col_id];
781 prefs->setString(
"/options/kbshortcuts/shortcutfile", set_to);
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);
798 auto themes = INKSCAPE.themecontext->get_available_themes();
799 Glib::ustring current_theme = prefs->getString(
"/theme/gtkTheme", prefs->getString(
"/theme/defaultGtkTheme",
""));
801 auto &dark_toggle = get_widget<Gtk::Switch>(
build_welcome,
"dark_toggle");
802 dark_toggle.set_active(dark);
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
void refresh_dark_switch()
Set current state of Dark Switch based on current selected theme.
SPDocument * get_template_document()
Return the template document from the kinds widget.
Gtk::WindowHandle & banners
static int get_start_mode()
Get the preference for the startup mode.
Inkscape::UI::Widget::TemplateList templates
void notebook_next(Gtk::Widget *button)
When a button needs to go to the next notebook page.
Gtk::TreeView * recentfiles
void on_recent_changed()
Called when a new recent document is selected.
void refresh_theme(Glib::ustring theme_name)
Refresh theme in-place so user can see a semi-preview.
void banner_switch(unsigned page_num)
When a notbook is switched, reveal the right banner image (gtk signal).
void keyboard_changed()
Set the keys file based on the keys set in the enlist above.
sigc::scoped_connection _tabs_switch_page_conn
Glib::RefPtr< Gtk::Builder > build_welcome
Glib::RefPtr< Gtk::Builder > build_splash
void new_document()
Called when new button clicked or template is double clicked, or escape pressed.
void on_kind_changed(const Glib::ustring &name)
Called when the left side tabs are changed.
void filter_themes(Gtk::ComboBox *themes)
const std::string opt_shown
void enlist_recent_files()
void load_document()
Called when load button clicked.
void canvas_changed()
Called when the canvas dropdown changes.
Gtk::TreeModel::Row active_combo(std::string widget_name)
Return the active row of the named combo box.
bool on_key_pressed(unsigned keyval, unsigned keycode, Gdk::ModifierType state)
When a key is pressed in the main window.
sigc::scoped_connection _templates_switch_page_conn
void refresh_keys_warning()
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)
void theme_changed()
Set the theme, icon pack and other theme options from a set defined in the glade file.
Typed SVG document implementation.
static char const *const current
Consolidates version info for Inkscape, its various dependencies and the OS we're running on.
Mini static library that contains the version of Inkscape.
std::string get_filename(Type type, char const *filename, bool localized, bool silent)
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().
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 ¤t_folder, Glib::ustring const &accept)
Synchronously run a Gtk::FileDialog to open a single file for reading data.
static cairo_user_data_key_t key
Inkscape::IO::Resource - simple resource API.
A dialog for the start screen.
Gtk <themes> helper code.
unsigned int get_color_value(const Glib::ustring color)
Color is store as a string in the form #RRGGBBAA, '0' means "unset".
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,...