From bc3e09edbfeb4859218bf51476e34635f45cf249 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 8 May 2019 22:53:25 +0000 Subject: [PATCH] GTK: use native file chooser dialog, if available. This requires GTK 3.24+ and (at least for GTK 3.24) the environment variable GTK_USE_PORTAL to be set to 1. --- CHANGELOG.md | 3 +- src/CMakeLists.txt | 8 +++ src/config.h.in | 3 + src/platform/guigtk.cpp | 132 ++++++++++++++++++++++++++++------------ 4 files changed, 106 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9b2b5b..17a0e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ Other new features: * The "=" key is bound to "Zoom In", like "+" key. * The numpad decimal separator key is bound to "." regardless of locale. * On Windows, full-screen mode is implemented. + * On Linux, native file chooser dialog can be used. Bugs fixed: * A point in 3d constrained to any line whose length is free no longer @@ -98,7 +99,7 @@ Bugs fixed: * Paste Transformed with a negative scale does not invert arcs. * The tangent arc now modifies the original entities instead of deleting them, such that their constraints are retained. - * When linking a sketch file, missing custom styles are now imported from + * When linking a sketch file, missing custom styles are now imported from the linked file. 2.x diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 874e88d..91a01c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,6 +13,14 @@ endif() set(HAVE_SPACEWARE ${SPACEWARE_FOUND}) +if(NOT WIN32 OR APPLE) + if(GTKMM_gtkmm-3.0_VERSION VERSION_LESS "3.24.0") + set(HAVE_GTK_FILECHOOSERNATIVE 0) + else() + set(HAVE_GTK_FILECHOOSERNATIVE 1) + endif() +endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) diff --git a/src/config.h.in b/src/config.h.in index 2f210d7..475b973 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -16,4 +16,7 @@ #cmakedefine HAVE_BACKTRACE #define BACKTRACE_HEADER @BACKTRACE_HEADER@ +/* If we use GTK, can we use the native file chooser? */ +#cmakedefine HAVE_GTK_FILECHOOSERNATIVE + #endif diff --git a/src/platform/guigtk.cpp b/src/platform/guigtk.cpp index 8f57afd..ac5f448 100644 --- a/src/platform/guigtk.cpp +++ b/src/platform/guigtk.cpp @@ -15,6 +15,10 @@ #include #include #include +#define HAVE_GTK_FILECHOOSERNATIVE +#if defined(HAVE_GTK_FILECHOOSERNATIVE) +# include +#endif #include #include #include @@ -1187,32 +1191,27 @@ MessageDialogRef CreateMessageDialog(WindowRef parentWindow) { // File dialogs //----------------------------------------------------------------------------- -class FileDialogImplGtk final : public FileDialog { +class FileDialogImplGtk : public FileDialog { public: - Gtk::FileChooserDialog gtkDialog; + Gtk::FileChooser *gtkChooser; std::vector extensions; - FileDialogImplGtk(Gtk::FileChooserDialog &&dialog) - : gtkDialog(std::move(dialog)) - { - gtkDialog.property_filter().signal_changed(). + void InitFileChooser(Gtk::FileChooser &chooser) { + gtkChooser = &chooser; + gtkChooser->property_filter().signal_changed(). connect(sigc::mem_fun(this, &FileDialogImplGtk::FilterChanged)); } - void SetTitle(std::string title) override { - gtkDialog.set_title(PrepareTitle(title)); - } - void SetCurrentName(std::string name) override { - gtkDialog.set_current_name(name); + gtkChooser->set_current_name(name); } Platform::Path GetFilename() override { - return Path::From(gtkDialog.get_filename()); + return Path::From(gtkChooser->get_filename()); } void SetFilename(Platform::Path path) override { - gtkDialog.set_filename(path.raw); + gtkChooser->set_filename(path.raw); } void AddFilter(std::string name, std::vector extensions) override { @@ -1233,13 +1232,13 @@ public: gtkFilter->set_name(name + " (" + desc + ")"); this->extensions.push_back(extensions.front()); - gtkDialog.add_filter(gtkFilter); + gtkChooser->add_filter(gtkFilter); } std::string GetExtension() { - auto filters = gtkDialog.list_filters(); + auto filters = gtkChooser->list_filters(); size_t filterIndex = - std::find(filters.begin(), filters.end(), gtkDialog.get_filter()) - + std::find(filters.begin(), filters.end(), gtkChooser->get_filter()) - filters.begin(); if(filterIndex < extensions.size()) { return extensions[filterIndex]; @@ -1249,14 +1248,14 @@ public: } void SetExtension(std::string extension) { - auto filters = gtkDialog.list_filters(); + auto filters = gtkChooser->list_filters(); size_t extensionIndex = std::find(extensions.begin(), extensions.end(), extension) - extensions.begin(); if(extensionIndex < filters.size()) { - gtkDialog.set_filter(filters[extensionIndex]); + gtkChooser->set_filter(filters[extensionIndex]); } else { - gtkDialog.set_filter(filters.front()); + gtkChooser->set_filter(filters.front()); } } @@ -1270,21 +1269,46 @@ public: void FreezeChoices(SettingsRef settings, const std::string &key) override { settings->FreezeString("Dialog_" + key + "_Folder", - gtkDialog.get_current_folder()); + gtkChooser->get_current_folder()); settings->FreezeString("Dialog_" + key + "_Filter", GetExtension()); } void ThawChoices(SettingsRef settings, const std::string &key) override { - gtkDialog.set_current_folder(settings->ThawString("Dialog_" + key + "_Folder")); + gtkChooser->set_current_folder(settings->ThawString("Dialog_" + key + "_Folder")); SetExtension(settings->ThawString("Dialog_" + key + "_Filter")); } - bool RunModal() override { - if(gtkDialog.get_action() == Gtk::FILE_CHOOSER_ACTION_SAVE && - Path::From(gtkDialog.get_current_name()).FileStem().empty()) { - gtkDialog.set_current_name(std::string(_("untitled")) + "." + GetExtension()); + void CheckForUntitledFile() { + if(gtkChooser->get_action() == Gtk::FILE_CHOOSER_ACTION_SAVE && + Path::From(gtkChooser->get_current_name()).FileStem().empty()) { + gtkChooser->set_current_name(std::string(_("untitled")) + "." + GetExtension()); } + } +}; +class FileDialogGtkImplGtk final : public FileDialogImplGtk { +public: + Gtk::FileChooserDialog gtkDialog; + + FileDialogGtkImplGtk(Gtk::Window >kParent, bool isSave) + : gtkDialog(gtkParent, + isSave ? C_("title", "Save File") + : C_("title", "Open File"), + isSave ? Gtk::FILE_CHOOSER_ACTION_SAVE + : Gtk::FILE_CHOOSER_ACTION_OPEN) { + gtkDialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL); + gtkDialog.add_button(isSave ? C_("button", "_Save") + : C_("button", "_Open"), Gtk::RESPONSE_OK); + gtkDialog.set_default_response(Gtk::RESPONSE_OK); + InitFileChooser(gtkDialog); + } + + void SetTitle(std::string title) override { + gtkDialog.set_title(PrepareTitle(title)); + } + + bool RunModal() override { + CheckForUntitledFile(); if(gtkDialog.run() == Gtk::RESPONSE_OK) { return true; } else { @@ -1293,26 +1317,56 @@ public: } }; +#if defined(HAVE_GTK_FILECHOOSERNATIVE) + +class FileDialogNativeImplGtk final : public FileDialogImplGtk { +public: + Glib::RefPtr gtkNative; + + FileDialogNativeImplGtk(Gtk::Window >kParent, bool isSave) { + gtkNative = Gtk::FileChooserNative::create( + isSave ? C_("title", "Save File") + : C_("title", "Open File"), + gtkParent, + isSave ? Gtk::FILE_CHOOSER_ACTION_SAVE + : Gtk::FILE_CHOOSER_ACTION_OPEN, + isSave ? C_("button", "_Save") + : C_("button", "_Open"), + C_("button", "_Cancel")); + // Seriously, GTK?! + InitFileChooser(*gtkNative.operator->()); + } + + void SetTitle(std::string title) override { + gtkNative->set_title(PrepareTitle(title)); + } + + bool RunModal() override { + CheckForUntitledFile(); + if(gtkNative->run() == Gtk::RESPONSE_ACCEPT) { + return true; + } else { + return false; + } + } +}; + +#endif + +#if defined(HAVE_GTK_FILECHOOSERNATIVE) +# define FILE_DIALOG_IMPL FileDialogNativeImplGtk +#else +# define FILE_DIALOG_IMPL FileDialogGtkImplGtk +#endif + FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) { Gtk::Window >kParent = std::static_pointer_cast(parentWindow)->gtkWindow; - Gtk::FileChooserDialog gtkDialog(gtkParent, C_("title", "Open File"), - Gtk::FILE_CHOOSER_ACTION_OPEN); - gtkDialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL); - gtkDialog.add_button(C_("button", "_Open"), Gtk::RESPONSE_OK); - gtkDialog.set_default_response(Gtk::RESPONSE_OK); - return std::make_shared(std::move(gtkDialog)); - + return std::make_shared(gtkParent, /*isSave=*/false); } FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) { Gtk::Window >kParent = std::static_pointer_cast(parentWindow)->gtkWindow; - Gtk::FileChooserDialog gtkDialog(gtkParent, C_("title", "Save File"), - Gtk::FILE_CHOOSER_ACTION_SAVE); - gtkDialog.set_do_overwrite_confirmation(true); - gtkDialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL); - gtkDialog.add_button(C_("button", "_Save"), Gtk::RESPONSE_OK); - gtkDialog.set_default_response(Gtk::RESPONSE_OK); - return std::make_shared(std::move(gtkDialog)); + return std::make_shared(gtkParent, /*isSave=*/true); } //-----------------------------------------------------------------------------