Compare commits
14 Commits
matthiasc/
...
wip/alexl/
Author | SHA1 | Date | |
---|---|---|---|
|
e97a26070c | ||
|
2631261597 | ||
|
32457cded3 | ||
|
e9b6566dcc | ||
|
20025b41a4 | ||
|
108ae05a76 | ||
|
a21156750d | ||
|
9b8148da17 | ||
|
10c3e4af91 | ||
|
a97997ce3a | ||
|
c7c64f0718 | ||
|
ccadf57ee3 | ||
|
89aa77b797 | ||
|
5be0351033 |
@@ -753,6 +753,7 @@ gtk_base_c_sources = \
|
||||
gtkfilechooserembed.c \
|
||||
gtkfilechooserentry.c \
|
||||
gtkfilechoosernative.c \
|
||||
gtkfilechoosernativeportal.c \
|
||||
gtkfilechooserutils.c \
|
||||
gtkfilechooserwidget.c \
|
||||
gtkfilefilter.c \
|
||||
|
@@ -51,7 +51,10 @@
|
||||
* for use with “File/Open” or “File/Save as” commands. By default, this
|
||||
* just uses a #GtkFileChooserDialog to implement the actual dialog.
|
||||
* However, on certain platforms, such as Windows, the native platform
|
||||
* file chooser is uses instead.
|
||||
* file chooser is uses instead. When the application is running in a
|
||||
* sandboxed environment without direct filesystem access (such as Flatpak),
|
||||
* #GtkFileChooserNative may call the proper APIs (portals) to let the user
|
||||
* choose a file and make it available to the application.
|
||||
*
|
||||
* While the API of #GtkFileChooserNative closely mirrors #GtkFileChooserDialog, the main
|
||||
* difference is that there is no access to any #GtkWindow or #GtkWidget for the dialog.
|
||||
@@ -170,11 +173,26 @@
|
||||
*
|
||||
* If any of these features are used the regular #GtkFileChooserDialog
|
||||
* will be used in place of the native one.
|
||||
*
|
||||
* ## Portal details ## {#gtkfilechooserdialognative-portal}
|
||||
*
|
||||
* When the org.freedesktop.portal.FileChooser portal is available on the
|
||||
* session bus, it is used to bring up an out-of-process file chooser. Depending
|
||||
* on the kind of session the application is running in, this may or may not
|
||||
* be a GTK+ file chooser. In this situation, the following things are not
|
||||
* supported and will be silently ignored:
|
||||
*
|
||||
* * Extra widgets added with gtk_file_chooser_set_extra_widget().
|
||||
*
|
||||
* * Use of custom previews by connecting to #GtkFileChooser::update-preview.
|
||||
*
|
||||
* * Any #GtkFileFilter added with a custom filter.
|
||||
*/
|
||||
|
||||
enum {
|
||||
MODE_FALLBACK,
|
||||
MODE_WIN32,
|
||||
MODE_PORTAL,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -493,6 +511,13 @@ hide_dialog (GtkFileChooserNative *self)
|
||||
gtk_widget_hide (self->dialog);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_file_chooser_native_show_fallback (GtkFileChooserNative *self)
|
||||
{
|
||||
self->mode = MODE_FALLBACK;
|
||||
show_dialog (self);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_file_chooser_native_set_current_folder (GtkFileChooser *chooser,
|
||||
GFile *file,
|
||||
@@ -558,6 +583,7 @@ gtk_file_chooser_native_get_files (GtkFileChooser *chooser)
|
||||
|
||||
switch (self->mode)
|
||||
{
|
||||
case MODE_PORTAL:
|
||||
case MODE_WIN32:
|
||||
return g_slist_copy_deep (self->custom_files, (GCopyFunc)g_object_ref, NULL);
|
||||
|
||||
@@ -579,6 +605,10 @@ gtk_file_chooser_native_show (GtkNativeDialog *native)
|
||||
self->mode = MODE_WIN32;
|
||||
#endif
|
||||
|
||||
if (self->mode == MODE_FALLBACK &&
|
||||
gtk_file_chooser_native_portal_show (self))
|
||||
self->mode = MODE_PORTAL;
|
||||
|
||||
if (self->mode == MODE_FALLBACK)
|
||||
show_dialog (self);
|
||||
}
|
||||
@@ -597,6 +627,8 @@ gtk_file_chooser_native_hide (GtkNativeDialog *native)
|
||||
#ifdef GDK_WINDOWING_WIN32
|
||||
gtk_file_chooser_native_win32_hide (self);
|
||||
#endif
|
||||
case MODE_PORTAL:
|
||||
gtk_file_chooser_native_portal_hide (self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
387
gtk/gtkfilechoosernativeportal.c
Normal file
387
gtk/gtkfilechoosernativeportal.c
Normal file
@@ -0,0 +1,387 @@
|
||||
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
|
||||
/* GTK - The GIMP Toolkit
|
||||
* gtkfilechoosernativeportal.c: Portal File selector dialog
|
||||
* Copyright (C) 2015, Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkfilechoosernativeprivate.h"
|
||||
#include "gtknativedialogprivate.h"
|
||||
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkfilechooserdialog.h"
|
||||
#include "gtkfilechooserprivate.h"
|
||||
#include "gtkfilechooserwidget.h"
|
||||
#include "gtkfilechooserwidgetprivate.h"
|
||||
#include "gtkfilechooserutils.h"
|
||||
#include "gtkfilechooserembed.h"
|
||||
#include "gtkfilesystem.h"
|
||||
#include "gtksizerequest.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtksettings.h"
|
||||
#include "gtktogglebutton.h"
|
||||
#include "gtkstylecontext.h"
|
||||
#include "gtkheaderbar.h"
|
||||
#include "gtklabel.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkinvisible.h"
|
||||
#include "gtkfilechooserentry.h"
|
||||
#include "gtkfilefilterprivate.h"
|
||||
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include "x11/gdkx.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
GtkFileChooserNative *self;
|
||||
|
||||
GtkWidget *grab_widget;
|
||||
|
||||
GDBusConnection *connection;
|
||||
char *portal_handle;
|
||||
guint portal_response_signal_id;
|
||||
gboolean modal;
|
||||
|
||||
gboolean hidden;
|
||||
|
||||
} FilechooserPortalData;
|
||||
|
||||
|
||||
static void
|
||||
filechooser_portal_data_free (FilechooserPortalData *data)
|
||||
{
|
||||
if (data->portal_response_signal_id != 0)
|
||||
g_dbus_connection_signal_unsubscribe (data->connection,
|
||||
data->portal_response_signal_id);
|
||||
|
||||
//TODO: This causes a crash in other code. Do we double unref somewhere?
|
||||
// g_object_unref (data->connection);
|
||||
|
||||
if (data->grab_widget)
|
||||
{
|
||||
gtk_grab_remove (data->grab_widget);
|
||||
gtk_widget_destroy (data->grab_widget);
|
||||
}
|
||||
|
||||
if (data->self)
|
||||
g_object_unref (data->self);
|
||||
|
||||
g_free (data->portal_handle);
|
||||
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
file_response (GDBusConnection *connection,
|
||||
const gchar *sender_name,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *signal_name,
|
||||
GVariant *parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkFileChooserNative *self = user_data;
|
||||
FilechooserPortalData *data = self->mode_data;
|
||||
guint32 portal_response;
|
||||
int gtk_response;
|
||||
char **uris;
|
||||
int i;
|
||||
GVariant *response_data;
|
||||
|
||||
g_variant_get (parameters, "(u^a&s@a{sv})", &portal_response, &uris, &response_data);
|
||||
|
||||
if (data->portal_handle == NULL ||
|
||||
strcmp (object_path, data->portal_handle) != 0)
|
||||
return;
|
||||
|
||||
g_slist_free_full (self->custom_files, g_object_unref);
|
||||
self->custom_files = NULL;
|
||||
for (i = 0; uris[i]; i++)
|
||||
self->custom_files = g_slist_prepend (self->custom_files, g_file_new_for_uri (uris[i]));
|
||||
|
||||
switch (portal_response)
|
||||
{
|
||||
case 0:
|
||||
gtk_response = GTK_RESPONSE_OK;
|
||||
break;
|
||||
case 1:
|
||||
gtk_response = GTK_RESPONSE_CANCEL;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
gtk_response = GTK_RESPONSE_DELETE_EVENT;
|
||||
break;
|
||||
}
|
||||
|
||||
filechooser_portal_data_free (data);
|
||||
self->mode_data = NULL;
|
||||
|
||||
_gtk_native_dialog_emit_response (GTK_NATIVE_DIALOG (self), gtk_response);
|
||||
}
|
||||
|
||||
static void
|
||||
send_close (FilechooserPortalData *data)
|
||||
{
|
||||
GDBusMessage *message;
|
||||
GError *error = NULL;
|
||||
|
||||
message = g_dbus_message_new_method_call ("org.freedesktop.portal.Desktop",
|
||||
data->portal_handle,
|
||||
"org.freedesktop.portal.Request",
|
||||
"Close");
|
||||
g_dbus_message_set_body (message,
|
||||
g_variant_new ("()"));
|
||||
|
||||
if (!g_dbus_connection_send_message (data->connection,
|
||||
message,
|
||||
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
||||
NULL, &error))
|
||||
{
|
||||
g_warning ("unable to send FileChooser Close message: %s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
open_file_msg_cb (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
FilechooserPortalData *data = user_data;
|
||||
GtkFileChooserNative *self = data->self;
|
||||
GDBusMessage *reply;
|
||||
GError *error = NULL;
|
||||
|
||||
reply = g_dbus_connection_send_message_with_reply_finish (data->connection, res, &error);
|
||||
|
||||
if (reply && g_dbus_message_to_gerror (reply, &error))
|
||||
g_clear_object (&reply);
|
||||
|
||||
if (reply == NULL)
|
||||
{
|
||||
if (!data->hidden)
|
||||
_gtk_native_dialog_emit_response (GTK_NATIVE_DIALOG (self), GTK_RESPONSE_DELETE_EVENT);
|
||||
g_warning ("Can't open portal file chooser: %s\n", error->message);
|
||||
filechooser_portal_data_free (data);
|
||||
|
||||
/* fall back manually */
|
||||
gtk_file_chooser_native_show_fallback (GTK_FILE_CHOOSER_NATIVE (self));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_get_child (g_dbus_message_get_body (reply), 0, "o",
|
||||
&data->portal_handle);
|
||||
|
||||
if (data->hidden)
|
||||
{
|
||||
/* The dialog was hidden before we got the handle, close it now */
|
||||
send_close (data);
|
||||
filechooser_portal_data_free (data);
|
||||
}
|
||||
|
||||
g_object_unref (reply);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
get_filters (GtkFileChooser *self)
|
||||
{
|
||||
GSList *list, *l;
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sa(us))"));
|
||||
list = gtk_file_chooser_list_filters (self);
|
||||
for (l = list; l; l = l->next)
|
||||
{
|
||||
GtkFileFilter *filter = l->data;
|
||||
g_variant_builder_add (&builder, "@(sa(us))", gtk_file_filter_to_gvariant (filter));
|
||||
}
|
||||
g_slist_free (list);
|
||||
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_file_chooser_native_portal_show (GtkFileChooserNative *self)
|
||||
{
|
||||
FilechooserPortalData *data;
|
||||
GtkWindow *transient_for;
|
||||
guint update_preview_signal;
|
||||
GDBusConnection *connection;
|
||||
char *parent_window_str;
|
||||
GDBusMessage *message;
|
||||
GVariantBuilder opt_builder;
|
||||
GtkFileChooserAction action;
|
||||
gboolean multiple;
|
||||
const char *method_name;
|
||||
const char *use_portal;
|
||||
gchar *path;
|
||||
|
||||
path = g_strdup_printf ("/run/user/%d/flatpak-info", getuid());
|
||||
if (g_file_test (path, G_FILE_TEST_EXISTS))
|
||||
use_portal = "1";
|
||||
else
|
||||
{
|
||||
use_portal = g_getenv ("GTK_USE_PORTAL");
|
||||
if (!use_portal)
|
||||
use_portal = "";
|
||||
}
|
||||
g_free (path);
|
||||
|
||||
if (g_str_equal (use_portal, "0"))
|
||||
return FALSE;
|
||||
|
||||
if (!g_str_equal (use_portal, "1"))
|
||||
{
|
||||
if (gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (self)) != NULL)
|
||||
return FALSE;
|
||||
|
||||
update_preview_signal = g_signal_lookup ("update-preview", GTK_TYPE_FILE_CHOOSER);
|
||||
if (g_signal_has_handler_pending (self, update_preview_signal, 0, TRUE))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
if (connection == NULL)
|
||||
return FALSE;
|
||||
|
||||
action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (self));
|
||||
multiple = gtk_file_chooser_get_select_multiple (GTK_FILE_CHOOSER (self));
|
||||
|
||||
if (action == GTK_FILE_CHOOSER_ACTION_OPEN && !multiple)
|
||||
method_name = "OpenFile";
|
||||
else if (action == GTK_FILE_CHOOSER_ACTION_OPEN && multiple)
|
||||
method_name = "OpenFiles";
|
||||
else if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
method_name = "SaveFile";
|
||||
else
|
||||
{
|
||||
g_warning ("GTK_FILE_CHOOSER_ACTION_%s is not supported by GtkFileChooserNativePortal", action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ? "SELECT_FOLDER" : "CREATE_FOLDER");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
data = g_new0 (FilechooserPortalData, 1);
|
||||
data->self = g_object_ref (self);
|
||||
data->connection = connection;
|
||||
|
||||
message = g_dbus_message_new_method_call ("org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.FileChooser",
|
||||
method_name);
|
||||
|
||||
parent_window_str = NULL;
|
||||
transient_for = gtk_native_dialog_get_transient_for (GTK_NATIVE_DIALOG (self));
|
||||
if (transient_for != NULL && gtk_widget_is_visible (GTK_WIDGET (transient_for)))
|
||||
{
|
||||
GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (transient_for));
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
if (GDK_IS_X11_WINDOW(window))
|
||||
parent_window_str = g_strdup_printf ("x11:%x", (guint32)gdk_x11_window_get_xid (window));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gtk_native_dialog_get_modal (GTK_NATIVE_DIALOG (self)))
|
||||
data->modal = TRUE;
|
||||
|
||||
if (data->modal && transient_for)
|
||||
{
|
||||
data->grab_widget = gtk_invisible_new_for_screen (gtk_widget_get_screen (GTK_WIDGET (transient_for)));
|
||||
gtk_grab_add (GTK_WIDGET (data->grab_widget));
|
||||
}
|
||||
|
||||
g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
|
||||
if (self->accept_label)
|
||||
g_variant_builder_add (&opt_builder, "{sv}", "accept_label",
|
||||
g_variant_new_string (self->accept_label));
|
||||
if (self->cancel_label)
|
||||
g_variant_builder_add (&opt_builder, "{sv}", "cancel_label",
|
||||
g_variant_new_string (self->cancel_label));
|
||||
g_variant_builder_add (&opt_builder, "{sv}", "modal",
|
||||
g_variant_new_boolean (data->modal));
|
||||
g_variant_builder_add (&opt_builder, "{sv}", "filters", get_filters (GTK_FILE_CHOOSER (self)));
|
||||
if (GTK_FILE_CHOOSER_NATIVE (self)->current_name)
|
||||
g_variant_builder_add (&opt_builder, "{sv}", "current_name",
|
||||
g_variant_new_string (GTK_FILE_CHOOSER_NATIVE (self)->current_name));
|
||||
if (GTK_FILE_CHOOSER_NATIVE (self)->current_folder)
|
||||
{
|
||||
gchar *path;
|
||||
|
||||
path = g_file_get_path (GTK_FILE_CHOOSER_NATIVE (self)->current_folder);
|
||||
g_variant_builder_add (&opt_builder, "{sv}", "current_folder",
|
||||
g_variant_new_bytestring (path));
|
||||
g_free (path);
|
||||
}
|
||||
if (GTK_FILE_CHOOSER_NATIVE (self)->current_file)
|
||||
{
|
||||
gchar *path;
|
||||
|
||||
path = g_file_get_path (GTK_FILE_CHOOSER_NATIVE (self)->current_file);
|
||||
g_variant_builder_add (&opt_builder, "{sv}", "current_file",
|
||||
g_variant_new_bytestring (path));
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
g_dbus_message_set_body (message,
|
||||
g_variant_new ("(ss@a{sv})",
|
||||
parent_window_str ? parent_window_str : "",
|
||||
gtk_native_dialog_get_title (GTK_NATIVE_DIALOG (self)),
|
||||
g_variant_builder_end (&opt_builder)));
|
||||
g_free (parent_window_str);
|
||||
|
||||
data->portal_response_signal_id =
|
||||
g_dbus_connection_signal_subscribe (data->connection,
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"org.freedesktop.portal.FileChooserRequest",
|
||||
"Response",
|
||||
NULL,
|
||||
NULL,
|
||||
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
|
||||
file_response,
|
||||
self, NULL);
|
||||
|
||||
g_dbus_connection_send_message_with_reply (data->connection,
|
||||
message,
|
||||
G_DBUS_SEND_MESSAGE_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
NULL,
|
||||
open_file_msg_cb,
|
||||
data);
|
||||
|
||||
self->mode_data = data;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_file_chooser_native_portal_hide (GtkFileChooserNative *self)
|
||||
{
|
||||
FilechooserPortalData *data = self->mode_data;
|
||||
|
||||
/* This is always set while dialog visible */
|
||||
g_assert (data != NULL);
|
||||
|
||||
data->hidden = TRUE;
|
||||
|
||||
if (data->portal_handle)
|
||||
{
|
||||
send_close (data);
|
||||
filechooser_portal_data_free (data);
|
||||
}
|
||||
|
||||
self->mode_data = NULL;
|
||||
}
|
@@ -48,6 +48,11 @@ struct _GtkFileChooserNative
|
||||
gboolean gtk_file_chooser_native_win32_show (GtkFileChooserNative *self);
|
||||
void gtk_file_chooser_native_win32_hide (GtkFileChooserNative *self);
|
||||
|
||||
gboolean gtk_file_chooser_native_portal_show (GtkFileChooserNative *self);
|
||||
void gtk_file_chooser_native_portal_hide (GtkFileChooserNative *self);
|
||||
|
||||
void gtk_file_chooser_native_show_fallback (GtkFileChooserNative *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FILE_CHOOSER_NATIVE_PRIVATE_H__ */
|
||||
|
@@ -718,3 +718,82 @@ gtk_file_filter_filter (GtkFileFilter *filter,
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GVariant *
|
||||
gtk_file_filter_to_gvariant (GtkFileFilter *filter)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
GSList *l;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(us)"));
|
||||
for (l = filter->rules; l; l = l->next)
|
||||
{
|
||||
FilterRule *rule = l->data;
|
||||
|
||||
switch (rule->type)
|
||||
{
|
||||
case FILTER_RULE_PATTERN:
|
||||
g_variant_builder_add (&builder, "(us)", 0, rule->u.mime_type);
|
||||
break;
|
||||
case FILTER_RULE_MIME_TYPE:
|
||||
g_variant_builder_add (&builder, "(us)", 1, rule->u.mime_type);
|
||||
break;
|
||||
case FILTER_RULE_PIXBUF_FORMATS:
|
||||
{
|
||||
GSList *f;
|
||||
|
||||
for (f = rule->u.pixbuf_formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
gchar **mime_types;
|
||||
int i;
|
||||
|
||||
mime_types = gdk_pixbuf_format_get_mime_types (fmt);
|
||||
for (i = 0; mime_types[i]; i++)
|
||||
g_variant_builder_add (&builder, "(us)", 1, mime_types[i]);
|
||||
g_strfreev (mime_types);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FILTER_RULE_CUSTOM:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return g_variant_new ("(s@a(us))", filter->name, g_variant_builder_end (&builder));
|
||||
}
|
||||
|
||||
GtkFileFilter *
|
||||
gtk_file_filter_from_gvariant (GVariant *variant)
|
||||
{
|
||||
GtkFileFilter *filter;
|
||||
GVariantIter *iter;
|
||||
const char *name;
|
||||
int type;
|
||||
char *tmp;
|
||||
|
||||
filter = gtk_file_filter_new ();
|
||||
|
||||
g_variant_get (variant, "(&sa(us))", &name, &iter);
|
||||
|
||||
gtk_file_filter_set_name (filter, name);
|
||||
|
||||
while (g_variant_iter_next (iter, "(u&s)", &type, &tmp))
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 0:
|
||||
gtk_file_filter_add_pattern (filter, tmp);
|
||||
break;
|
||||
case 1:
|
||||
gtk_file_filter_add_mime_type (filter, tmp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_variant_iter_free (iter);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@ G_BEGIN_DECLS
|
||||
|
||||
char ** _gtk_file_filter_get_as_patterns (GtkFileFilter *filter);
|
||||
|
||||
GVariant *gtk_file_filter_to_gvariant (GtkFileFilter *filter);
|
||||
GtkFileFilter *gtk_file_filter_from_gvariant (GVariant *variant);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_FILE_FILTER_PRIVATE_H__ */
|
||||
|
@@ -24,6 +24,10 @@
|
||||
|
||||
#include "gtkshow.h"
|
||||
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
#include "x11/gdkx.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* gtk_show_uri:
|
||||
* @screen: (allow-none): screen to show the uri on
|
||||
@@ -70,8 +74,54 @@ gtk_show_uri (GdkScreen *screen,
|
||||
gdk_app_launch_context_set_screen (context, screen);
|
||||
gdk_app_launch_context_set_timestamp (context, timestamp);
|
||||
|
||||
ret = g_app_info_launch_default_for_uri (uri, (GAppLaunchContext*)context, error);
|
||||
ret = g_app_info_launch_default_for_uri (uri, G_APP_LAUNCH_CONTEXT (context), error);
|
||||
g_object_unref (context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_show_uri_on_window (GtkWindow *parent,
|
||||
const char *uri,
|
||||
guint32 timestamp,
|
||||
GError **error)
|
||||
{
|
||||
GdkAppLaunchContext *context;
|
||||
gboolean ret;
|
||||
GdkDisplay *display;
|
||||
|
||||
g_return_val_if_fail (uri != NULL, FALSE);
|
||||
|
||||
if (parent)
|
||||
display = gtk_widget_get_display (GTK_WIDGET (parent));
|
||||
else
|
||||
display = gdk_display_get_default ();
|
||||
|
||||
context = gdk_display_get_app_launch_context (display);
|
||||
|
||||
if (parent)
|
||||
{
|
||||
GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (parent));
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
if (GDK_IS_X11_WINDOW(window))
|
||||
{
|
||||
char *parent_window_str;
|
||||
|
||||
parent_window_str = g_strdup_printf ("x11:%x", (guint32)gdk_x11_window_get_xid (window));
|
||||
g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context),
|
||||
"PARENT_WINDOW_ID",
|
||||
parent_window_str);
|
||||
g_free (parent_window_str);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gdk_app_launch_context_set_timestamp (context, timestamp);
|
||||
|
||||
ret = g_app_info_launch_default_for_uri (uri, G_APP_LAUNCH_CONTEXT (context), error);
|
||||
|
||||
g_object_unref (context);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwindow.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
@@ -33,6 +35,12 @@ gboolean gtk_show_uri (GdkScreen *screen,
|
||||
guint32 timestamp,
|
||||
GError **error);
|
||||
|
||||
GDK_AVAILABLE_IN_3_22
|
||||
gboolean gtk_show_uri_on_window (GtkWindow *parent,
|
||||
const char *uri,
|
||||
guint32 timestamp,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SHOW_H__ */
|
||||
|
Reference in New Issue
Block a user