summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--modules/libpref/init/all.js6
-rw-r--r--widget/gtk/nsFilePicker.cpp133
-rw-r--r--widget/gtk/nsFilePicker.h18
3 files changed, 118 insertions, 39 deletions
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index cd4284a9e..e69a985ce 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4012,6 +4012,12 @@ pref("autocomplete.ungrab_during_mode_switch", true);
// toggling to use the XUL filepicker
pref("ui.allow_platform_file_picker", true);
+// Allow for using the native GTK file picker. If the application is not run
+// with GTK_USE_PORTAL=1 this pref has no effect.
+#ifdef MOZ_WIDGET_GTK
+pref("widget.allow-gtk-native-file-chooser", false);
+#endif
+
pref("helpers.global_mime_types_file", "/etc/mime.types");
pref("helpers.global_mailcap_file", "/etc/mailcap");
pref("helpers.private_mime_types_file", "~/.mime.types");
diff --git a/widget/gtk/nsFilePicker.cpp b/widget/gtk/nsFilePicker.cpp
index 172cb4444..05d8bb0f0 100644
--- a/widget/gtk/nsFilePicker.cpp
+++ b/widget/gtk/nsFilePicker.cpp
@@ -23,6 +23,7 @@
#include "nsNetUtil.h"
#include "nsReadableUtils.h"
#include "mozcontainer.h"
+#include "mozilla/Preferences.h"
#include "nsFilePicker.h"
@@ -175,6 +176,7 @@ nsFilePicker::nsFilePicker()
, mFileChooserDelegate(nullptr)
#endif
{
+ mUseNativeFileChooser = Preferences::GetBool("widget.allow-gtk-native-file-chooser", false);
}
nsFilePicker::~nsFilePicker()
@@ -197,7 +199,7 @@ ReadMultipleFiles(gpointer filename, gpointer array)
}
void
-nsFilePicker::ReadValuesFromFileChooser(GtkWidget *file_chooser)
+nsFilePicker::ReadValuesFromFileChooser(void *file_chooser)
{
mFiles.Clear();
@@ -389,19 +391,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 +405,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 +443,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<GtkFileChooserWidget**>(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<GtkFileChooserWidget**>(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 +465,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 +511,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<nsFilePicker*>(user_data)->
@@ -539,7 +532,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 +576,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 +601,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 (mUseNativeFileChooser && 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 (mUseNativeFileChooser && 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 (mUseNativeFileChooser && 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 (mUseNativeFileChooser && 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..e0a1d541d 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<nsIWidget> mParentWidget;
nsCOMPtr<nsIFilePickerShownCallback> mCallback;
@@ -74,9 +74,19 @@ 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
+ bool mUseNativeFileChooser;
};
#endif