Skip to content

Commit

Permalink
Add non-blocking gtk4-ready XojMsgBox::ReplaceFileQuestion()
Browse files Browse the repository at this point in the history
  • Loading branch information
bhennion committed Feb 24, 2024
1 parent e032aa4 commit 91dd41b
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 31 deletions.
24 changes: 10 additions & 14 deletions src/core/control/Control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1709,8 +1709,6 @@ auto Control::showSaveDialog() -> bool {
gtk_file_chooser_dialog_new(_("Save File"), getGtkWindow(), GTK_FILE_CHOOSER_ACTION_SAVE, _("_Cancel"),
GTK_RESPONSE_CANCEL, _("_Save"), GTK_RESPONSE_OK, nullptr);

gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), true);

GtkFileFilter* filterXoj = gtk_file_filter_new();
gtk_file_filter_set_name(filterXoj, _("Xournal++ files"));
gtk_file_filter_add_mime_type(filterXoj, "application/x-xopp");
Expand All @@ -1721,22 +1719,20 @@ auto Control::showSaveDialog() -> bool {
auto suggested_name = this->doc->createSaveFilename(Document::XOPP, this->settings->getDefaultSaveName());
this->doc->unlock();

gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), Util::toGFilename(suggested_folder).c_str());
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), Util::toGFile(suggested_folder), nullptr);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), Util::toGFilename(suggested_name).c_str());
gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
Util::toGFilename(this->settings->getLastOpenPath()).c_str(), nullptr);

gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), false); // handled below

gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(this->getWindow()->getWindow()));
gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog), Util::toGFile(this->settings->getLastOpenPath()),
nullptr);

while (true) {
if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) {
gtk_widget_destroy(dialog);
return false;
}

auto fileTmp = Util::fromGFilename(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
auto fileTmp = Util::fromGFile(
xoj::util::GObjectSPtr<GFile>(gtk_file_chooser_get_file(GTK_FILE_CHOOSER(dialog)), xoj::util::adopt)
.get());
Util::clearExtensions(fileTmp);
fileTmp += ".xopp";
// Since we add the extension after the OK button, we have to check manually on existing files
Expand All @@ -1745,13 +1741,13 @@ auto Control::showSaveDialog() -> bool {
}
}

auto filename = Util::fromGFilename(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
settings->setLastSavePath(filename.parent_path());
auto file = Util::fromGFile(
xoj::util::GObjectSPtr<GFile>(gtk_file_chooser_get_file(GTK_FILE_CHOOSER(dialog)), xoj::util::adopt).get());
settings->setLastSavePath(file.parent_path());
gtk_widget_destroy(dialog);

this->doc->lock();

this->doc->setFilepath(filename);
this->doc->setFilepath(file);
this->doc->unlock();

return true;
Expand Down
38 changes: 22 additions & 16 deletions src/core/control/jobs/BaseExportJob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,13 @@
#include "util/PathUtil.h" // for toGFilename, clearExtensions
#include "util/PopupWindowWrapper.h" // for PopupWindowWrapper
#include "util/XojMsgBox.h" // for XojMsgBox
#include "util/glib_casts.h" // for wrap_for_g_callback_v
#include "util/i18n.h" // for _, FS, _F

BaseExportJob::BaseExportJob(Control* control, const std::string& name): BlockingJob(control, name) {}

BaseExportJob::~BaseExportJob() = default;

static GtkWidget* initDialog(GtkWindow* win) {
return gtk_file_chooser_dialog_new(_("Export PDF"), win, GTK_FILE_CHOOSER_ACTION_SAVE, _("_Cancel"),
GTK_RESPONSE_CANCEL, _("_Export"), GTK_RESPONSE_OK, nullptr);
}

void BaseExportJob::addFileFilterToDialog(GtkFileChooser* dialog, const std::string& name, const std::string& mime) {
GtkFileFilter* filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, name.c_str());
Expand Down Expand Up @@ -54,7 +50,9 @@ auto BaseExportJob::checkOverwriteBackgroundPDF(fs::path const& file) const -> b
}

void BaseExportJob::showFileChooser(std::function<void()> onFileSelected, std::function<void()> onCancel) {
auto* dialog = initDialog(control->getGtkWindow());
auto* dialog =
gtk_file_chooser_dialog_new(_("Export"), control->getGtkWindow(), GTK_FILE_CHOOSER_ACTION_SAVE,
_("_Cancel"), GTK_RESPONSE_CANCEL, _("_Export"), GTK_RESPONSE_OK, nullptr);
addFilterToDialog(GTK_FILE_CHOOSER(dialog));

Settings* settings = control->getSettings();
Expand All @@ -76,8 +74,8 @@ void BaseExportJob::showFileChooser(std::function<void()> onFileSelected, std::f
onFileSelected(std::move(onFileSelected)),
onCancel(std::move(onCancel)) {
this->signalId = g_signal_connect(
dialog, "response", G_CALLBACK(+[](GtkDialog* dialog, int response, gpointer data) {
auto* self = static_cast<FileDlg*>(data);
dialog, "response", G_CALLBACK((+[](GtkDialog* dialog, int response, gpointer data) {
FileDlg* self = static_cast<FileDlg*>(data);
auto* job = self->job;
if (response == GTK_RESPONSE_OK) {
auto file = Util::fromGFile(
Expand All @@ -90,21 +88,29 @@ void BaseExportJob::showFileChooser(std::function<void()> onFileSelected, std::f
const char* filterName =
gtk_file_filter_get_name(gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog)));
;
if (job->testAndSetFilepath(std::move(file), filterName) &&
job->control->askToReplace(job->filepath)) {
job->control->getSettings()->setLastSavePath(job->filepath.parent_path());
self->onFileSelected();
// Closing the window causes another "response" signal, which we want to ignore
g_signal_handler_disconnect(dialog, self->signalId);
gtk_window_close(GTK_WINDOW(dialog)); // Deletes self, don't do anything after this
if (job->testAndSetFilepath(std::move(file), filterName)) {
auto doExport = [self, dialog]() {
// Closing the window causes another "response" signal, which we want to ignore
g_signal_handler_disconnect(dialog, self->signalId);
gtk_window_close(GTK_WINDOW(dialog));
self->job->control->getSettings()->setLastSavePath(
self->job->filepath.parent_path());
self->onFileSelected();
};
if (!fs::exists(job->filepath)) {
doExport();
} else {
XojMsgBox::replaceFileQuestion(GTK_WINDOW(dialog), job->filepath,
std::move(doExport), []() {});
}
} // else the dialog stays on until a suitable destination is found or cancel is hit.
} else {
self->onCancel();
// Closing the window causes another "response" signal, which we want to ignore
g_signal_handler_disconnect(dialog, self->signalId);
gtk_window_close(GTK_WINDOW(dialog)); // Deletes self, don't do anything after this
}
}),
})),
this);
}
~FileDlg() = default;
Expand Down
4 changes: 3 additions & 1 deletion src/core/control/jobs/CustomExportJob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ void CustomExportJob::showDialogAndRun() {
}

Util::execInUiThread([job]() {
// We wait for the first dialog to be closed before showing the second one
// We must wait for the FileChooser dialog to be closed before showing the next one, otherwise the
// FileChooser dialog stays on
// Todo(post-gtk4): try out without the execInUiThread
xoj::popup::PopupWindowWrapper<xoj::popup::ExportDialog> popup(
job->control->getGladeSearchPath(), job->format, job->control->getCurrentPageNo() + 1,
job->control->getDocument()->getPageCount(), [job](const xoj::popup::ExportDialog& dialog) {
Expand Down
27 changes: 27 additions & 0 deletions src/util/XojMsgBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <glib.h> // for g_free, g_markup_escape_text, g_error_free

#include "util/PopupWindowWrapper.h"
#include "util/Util.h"
#include "util/gtk4_helper.h"
#include "util/i18n.h" // for _, FS, _F
#include "util/raii/CStringWrapper.h"
Expand Down Expand Up @@ -177,6 +178,32 @@ auto XojMsgBox::replaceFileQuestion(GtkWindow* win, const std::string& msg) -> i
return res;
}

void XojMsgBox::replaceFileQuestion(GtkWindow* win, const fs::path& file, std::function<void()> overwrite,
std::function<void()> pickOtherPath) {
GtkWidget* dialog = gtk_message_dialog_new(
win, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
FS(FORMAT_STR("The file {1} already exists! Do you want to replace it?") % file.filename().u8string())
.c_str());
if (win != nullptr) {
gtk_window_set_transient_for(GTK_WINDOW(dialog), win);
}
gtk_dialog_add_button(GTK_DIALOG(dialog), _("Select another name"), GTK_RESPONSE_CANCEL);
gtk_dialog_add_button(GTK_DIALOG(dialog), _("Replace"), GTK_RESPONSE_OK);

xoj::popup::PopupWindowWrapper<XojMsgBox> popup(
GTK_DIALOG(dialog),
[overwrite = std::move(overwrite), pickOtherPath = std::move(pickOtherPath)](int response) {
if (response == GTK_RESPONSE_OK) {
// Wait for the message dialog to close before executing the response
Util::execInUiThread(std::move(overwrite));
} else {
// Wait for the message dialog to close before executing the response
Util::execInUiThread(std::move(pickOtherPath));
}
});
popup.show(win);
}

constexpr auto* XOJ_HELP = "https://xournalpp.github.io/community/help/";

void XojMsgBox::showHelp(GtkWindow* win) {
Expand Down
5 changes: 5 additions & 0 deletions src/util/gtk4_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <gtk/gtk.h>

#include "util/Assert.h"
#include "util/raii/CStringWrapper.h"

namespace {
void set_child(GtkContainer* c, GtkWidget* child) {
Expand Down Expand Up @@ -122,6 +123,10 @@ void gtk_im_context_set_client_widget(GtkIMContext* context, GtkWidget* widget)
}

/**** GtkFileChooserDialog ****/
gboolean gtk_file_chooser_add_shortcut_folder(GtkFileChooser* chooser, GFile* file, GError** error) {
auto uri = xoj::util::OwnedCString::assumeOwnership(g_file_get_uri(file));
return gtk_file_chooser_add_shortcut_folder(chooser, uri.get(), error);
}
gboolean gtk_file_chooser_set_current_folder(GtkFileChooser* chooser, GFile* file, GError** error) {
return gtk_file_chooser_set_current_folder_file(chooser, file, error);
}
5 changes: 5 additions & 0 deletions src/util/include/util/XojMsgBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include "util/raii/GtkWindowUPtr.h"

#include "filesystem.h"

class XojMsgBox final {
public:
XojMsgBox(
Expand Down Expand Up @@ -69,4 +71,7 @@ class XojMsgBox final {
bool error = false);
static int replaceFileQuestion(GtkWindow* win, const std::string& msg);
static void showHelp(GtkWindow* win);

static void replaceFileQuestion(GtkWindow* win, const fs::path& file, std::function<void()> overwrite,
std::function<void()> pickOtherPath);
};
1 change: 1 addition & 0 deletions src/util/include/util/gtk4_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ void gtk_label_set_wrap_mode(GtkLabel* label, PangoWrapMode wrap_mode);
void gtk_im_context_set_client_widget(GtkIMContext* context, GtkWidget* widget);

/**** GtkFileChooserDialog ****/
gboolean gtk_file_chooser_add_shortcut_folder(GtkFileChooser* chooser, GFile* file, GError** error);
gboolean gtk_file_chooser_set_current_folder(GtkFileChooser* chooser, GFile* file, GError** error);

0 comments on commit 91dd41b

Please sign in to comment.