diff options
-rw-r--r-- | modules/libpref/init/all.js | 6 | ||||
-rw-r--r-- | widget/gtk/nsFilePicker.cpp | 133 | ||||
-rw-r--r-- | widget/gtk/nsFilePicker.h | 18 |
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 |