Use xdg-desktop-portal for file selection dialogs on Linux.

This commit is contained in:
Fedor 2019-09-05 20:09:32 +03:00
parent 9998b2436a
commit aee84ebc64
3 changed files with 117 additions and 38 deletions

View File

@ -4019,6 +4019,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");

View File

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

View File

@ -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