Skip to content

Commit

Permalink
GTK: use native file chooser dialog, if available.
Browse files Browse the repository at this point in the history
This requires GTK 3.24+ and (at least for GTK 3.24) the environment
variable GTK_USE_PORTAL to be set to 1.
  • Loading branch information
whitequark committed May 8, 2019
1 parent e7b75f1 commit 0645008
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 40 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -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
Expand All @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -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)

Expand Down
3 changes: 3 additions & 0 deletions src/config.h.in
Expand Up @@ -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
132 changes: 93 additions & 39 deletions src/platform/guigtk.cpp
Expand Up @@ -15,6 +15,10 @@
#include <gtkmm/cssprovider.h>
#include <gtkmm/entry.h>
#include <gtkmm/filechooserdialog.h>
#define HAVE_GTK_FILECHOOSERNATIVE
#if defined(HAVE_GTK_FILECHOOSERNATIVE)
# include <gtkmm/filechoosernative.h>
#endif
#include <gtkmm/fixed.h>
#include <gtkmm/glarea.h>
#include <gtkmm/main.h>
Expand Down Expand Up @@ -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<std::string> 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<std::string> extensions) override {
Expand All @@ -1233,13 +1232,13 @@ class FileDialogImplGtk final : public FileDialog {
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];
Expand All @@ -1249,14 +1248,14 @@ class FileDialogImplGtk final : public FileDialog {
}

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());
}
}

Expand All @@ -1270,21 +1269,46 @@ class FileDialogImplGtk final : public FileDialog {

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 &gtkParent, 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 {
Expand All @@ -1293,26 +1317,56 @@ class FileDialogImplGtk final : public FileDialog {
}
};

#if defined(HAVE_GTK_FILECHOOSERNATIVE)

class FileDialogNativeImplGtk final : public FileDialogImplGtk {
public:
Glib::RefPtr<Gtk::FileChooserNative> gtkNative;

FileDialogNativeImplGtk(Gtk::Window &gtkParent, 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_OK) {
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 &gtkParent = std::static_pointer_cast<WindowImplGtk>(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<FileDialogImplGtk>(std::move(gtkDialog));

return std::make_shared<FILE_DIALOG_IMPL>(gtkParent, /*isSave=*/false);
}

FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
Gtk::Window &gtkParent = std::static_pointer_cast<WindowImplGtk>(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<FileDialogImplGtk>(std::move(gtkDialog));
return std::make_shared<FILE_DIALOG_IMPL>(gtkParent, /*isSave=*/true);
}

//-----------------------------------------------------------------------------
Expand Down

0 comments on commit 0645008

Please sign in to comment.