From 47c48dfcb87a574f6b96f3abf3ed14b9b08b2371 Mon Sep 17 00:00:00 2001 From: Ascrod <32915892+Ascrod@users.noreply.github.com> Date: Sat, 10 Aug 2019 17:26:31 -0400 Subject: Issue #999 - Use xdg-desktop-portal for file selection dialogs on Linux. --- widget/gtk/nsFilePicker.cpp | 131 ++++++++++++++++++++++++++++++++------------ widget/gtk/nsFilePicker.h | 17 ++++-- 2 files changed, 109 insertions(+), 39 deletions(-) diff --git a/widget/gtk/nsFilePicker.cpp b/widget/gtk/nsFilePicker.cpp index 172cb4444..a2d2e1cff 100644 --- a/widget/gtk/nsFilePicker.cpp +++ b/widget/gtk/nsFilePicker.cpp @@ -197,7 +197,7 @@ ReadMultipleFiles(gpointer filename, gpointer array) } void -nsFilePicker::ReadValuesFromFileChooser(GtkWidget *file_chooser) +nsFilePicker::ReadValuesFromFileChooser(void *file_chooser) { mFiles.Clear(); @@ -389,19 +389,10 @@ nsFilePicker::Open(nsIFilePickerShownCallback *aCallback) if (!mOkButtonLabel.IsEmpty()) { accept_button = buttonLabel.get(); } else { - accept_button = (action == GTK_FILE_CHOOSER_ACTION_SAVE) ? - GTK_STOCK_SAVE : GTK_STOCK_OPEN; + accept_button = nullptr; } - GtkWidget *file_chooser = - gtk_file_chooser_dialog_new(title, parent_widget, action, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - accept_button, GTK_RESPONSE_ACCEPT, - nullptr); - gtk_dialog_set_alternative_button_order(GTK_DIALOG(file_chooser), - GTK_RESPONSE_ACCEPT, - GTK_RESPONSE_CANCEL, - -1); + void *file_chooser = GtkFileChooserNew(title.get(), parent_widget, action, accept_button); if (mAllowURLs) { gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(file_chooser), FALSE); } @@ -412,11 +403,7 @@ nsFilePicker::Open(nsIFilePickerShownCallback *aCallback) g_signal_connect(file_chooser, "update-preview", G_CALLBACK(UpdateFilePreviewWidget), img_preview); } - GtkWindow *window = GTK_WINDOW(file_chooser); - gtk_window_set_modal(window, TRUE); - if (parent_widget) { - gtk_window_set_destroy_with_parent(window, TRUE); - } + GtkFileChooserSetModal(file_chooser, parent_widget, TRUE); NS_ConvertUTF16toUTF8 defaultName(mDefault); switch (mMode) { @@ -454,18 +441,21 @@ nsFilePicker::Open(nsIFilePickerShownCallback *aCallback) // Otherwise, if our dialog gets destroyed, we'll lose the dialog's // delegate by the time this gets processed in the event loop. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1166741 - GtkDialog *dialog = GTK_DIALOG(file_chooser); - GtkContainer *area = GTK_CONTAINER(gtk_dialog_get_content_area(dialog)); - gtk_container_forall(area, [](GtkWidget *widget, - gpointer data) { - if (GTK_IS_FILE_CHOOSER_WIDGET(widget)) { - auto result = static_cast(data); - *result = GTK_FILE_CHOOSER_WIDGET(widget); - } - }, &mFileChooserDelegate); - - if (mFileChooserDelegate) - g_object_ref(mFileChooserDelegate); + if (GTK_IS_DIALOG(file_chooser)) { + GtkDialog *dialog = GTK_DIALOG(file_chooser); + GtkContainer *area = GTK_CONTAINER(gtk_dialog_get_content_area(dialog)); + gtk_container_forall(area, [](GtkWidget *widget, + gpointer data) { + if (GTK_IS_FILE_CHOOSER_WIDGET(widget)) { + auto result = static_cast(data); + *result = GTK_FILE_CHOOSER_WIDGET(widget); + } + }, &mFileChooserDelegate); + + if (mFileChooserDelegate) { + g_object_ref(mFileChooserDelegate); + } + } #endif gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_chooser), @@ -473,7 +463,9 @@ nsFilePicker::Open(nsIFilePickerShownCallback *aCallback) } } - gtk_dialog_set_default_response(GTK_DIALOG(file_chooser), GTK_RESPONSE_ACCEPT); + if (GTK_IS_DIALOG(file_chooser)) { + gtk_dialog_set_default_response(GTK_DIALOG(file_chooser), GTK_RESPONSE_ACCEPT); + } int32_t count = mFilters.Length(); for (int32_t i = 0; i < count; ++i) { @@ -517,14 +509,13 @@ nsFilePicker::Open(nsIFilePickerShownCallback *aCallback) mCallback = aCallback; NS_ADDREF_THIS(); g_signal_connect(file_chooser, "response", G_CALLBACK(OnResponse), this); - g_signal_connect(file_chooser, "destroy", G_CALLBACK(OnDestroy), this); - gtk_widget_show(file_chooser); + GtkFileChooserShow(file_chooser); return NS_OK; } /* static */ void -nsFilePicker::OnResponse(GtkWidget* file_chooser, gint response_id, +nsFilePicker::OnResponse(void* file_chooser, gint response_id, gpointer user_data) { static_cast(user_data)-> @@ -539,7 +530,7 @@ nsFilePicker::OnDestroy(GtkWidget* file_chooser, gpointer user_data) } void -nsFilePicker::Done(GtkWidget* file_chooser, gint response) +nsFilePicker::Done(void* file_chooser, gint response) { mRunning = false; @@ -583,7 +574,7 @@ nsFilePicker::Done(GtkWidget* file_chooser, gint response) // requests that any remaining references be released, but the reference // count will not be decremented again if GtkWindow's reference has already // been released. - gtk_widget_destroy(file_chooser); + GtkFileChooserDestroy(file_chooser); #if (MOZ_WIDGET_GTK == 3) if (mFileChooserDelegate) { @@ -608,3 +599,73 @@ nsFilePicker::Done(GtkWidget* file_chooser, gint response) } NS_RELEASE_THIS(); } + +// All below functions available as of GTK 3.20+ + +void * +nsFilePicker::GtkFileChooserNew( + const gchar *title, GtkWindow *parent, + GtkFileChooserAction action, + const gchar *accept_label) +{ + static auto sGtkFileChooserNativeNewPtr = (void * (*)( + const gchar *, GtkWindow *, + GtkFileChooserAction, + const gchar *, const gchar *)) + dlsym(RTLD_DEFAULT, "gtk_file_chooser_native_new"); + if (sGtkFileChooserNativeNewPtr != nullptr) { + return (*sGtkFileChooserNativeNewPtr)(title, parent, action, accept_label, nullptr); + } + if (accept_label == nullptr) { + accept_label = (action == GTK_FILE_CHOOSER_ACTION_SAVE) + ? GTK_STOCK_SAVE : GTK_STOCK_OPEN; + } + GtkWidget *file_chooser = gtk_file_chooser_dialog_new(title, parent, action, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + accept_label, GTK_RESPONSE_ACCEPT, nullptr); + gtk_dialog_set_alternative_button_order(GTK_DIALOG(file_chooser), + GTK_RESPONSE_ACCEPT, GTK_RESPONSE_CANCEL, -1); + return file_chooser; +} + +void +nsFilePicker::GtkFileChooserShow(void *file_chooser) +{ + static auto sGtkNativeDialogShowPtr = (void (*)(void *)) + dlsym(RTLD_DEFAULT, "gtk_native_dialog_show"); + if (sGtkNativeDialogShowPtr != nullptr) { + (*sGtkNativeDialogShowPtr)(file_chooser); + } else { + g_signal_connect(file_chooser, "destroy", G_CALLBACK(OnDestroy), this); + gtk_widget_show(GTK_WIDGET(file_chooser)); + } +} + +void +nsFilePicker::GtkFileChooserDestroy(void *file_chooser) +{ + static auto sGtkNativeDialogDestroyPtr = (void (*)(void *)) + dlsym(RTLD_DEFAULT, "gtk_native_dialog_destroy"); + if (sGtkNativeDialogDestroyPtr != nullptr) { + (*sGtkNativeDialogDestroyPtr)(file_chooser); + } else { + gtk_widget_destroy(GTK_WIDGET(file_chooser)); + } +} + +void +nsFilePicker::GtkFileChooserSetModal(void *file_chooser, + GtkWindow *parent_widget, gboolean modal) +{ + static auto sGtkNativeDialogSetModalPtr = (void (*)(void *, gboolean)) + dlsym(RTLD_DEFAULT, "gtk_native_dialog_set_modal"); + if (sGtkNativeDialogSetModalPtr != nullptr) { + (*sGtkNativeDialogSetModalPtr)(file_chooser, modal); + } else { + GtkWindow *window = GTK_WINDOW(file_chooser); + gtk_window_set_modal(window, modal); + if (parent_widget != nullptr) { + gtk_window_set_destroy_with_parent(window, modal); + } + } +} diff --git a/widget/gtk/nsFilePicker.h b/widget/gtk/nsFilePicker.h index 2b5042098..f462ca324 100644 --- a/widget/gtk/nsFilePicker.h +++ b/widget/gtk/nsFilePicker.h @@ -48,12 +48,12 @@ public: protected: virtual ~nsFilePicker(); - void ReadValuesFromFileChooser(GtkWidget *file_chooser); + void ReadValuesFromFileChooser(void *file_chooser); - static void OnResponse(GtkWidget* dialog, gint response_id, + static void OnResponse(void* dialog, gint response_id, gpointer user_data); - static void OnDestroy(GtkWidget* dialog, gpointer user_data); - void Done(GtkWidget* dialog, gint response_id); + static void OnDestroy(GtkWidget* file_chooser, gpointer user_data); + void Done(void* file_chooser, gint response_id); nsCOMPtr mParentWidget; nsCOMPtr mCallback; @@ -74,6 +74,15 @@ protected: private: static nsIFile *mPrevDisplayDirectory; + void *GtkFileChooserNew( + const gchar *title, GtkWindow *parent, + GtkFileChooserAction action, + const gchar *accept_label); + void GtkFileChooserShow(void *file_chooser); + void GtkFileChooserDestroy(void *file_chooser); + void GtkFileChooserSetModal(void *file_chooser, GtkWindow* parent_widget, + gboolean modal); + #if (MOZ_WIDGET_GTK == 3) GtkFileChooserWidget *mFileChooserDelegate; #endif -- cgit v1.2.3