diff options
author | Matthias Clasen <mclasen@redhat.com> | 2023-02-26 21:25:26 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2023-02-26 21:25:26 +0000 |
commit | a1f0d6428711f89e6846943485014c93ee22129d (patch) | |
tree | 9720d87571fdab048be965913825ccccdf1a57e4 | |
parent | bae6a37ed9613136a5519637fdfa4c2fdb19a243 (diff) | |
parent | 8562476872ae4568b85cc616269750a5b803f33a (diff) | |
download | gtk+-a1f0d6428711f89e6846943485014c93ee22129d.tar.gz |
Merge branch 'file-transfer-portal-gtk3' into 'gtk-3-24'
selection: Support the file transfer portal
See merge request GNOME/gtk!5554
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | gtk/filetransferportal.c | 498 | ||||
-rw-r--r-- | gtk/filetransferportalprivate.h | 49 | ||||
-rw-r--r-- | gtk/gtkselection.c | 111 | ||||
-rw-r--r-- | gtk/meson.build | 4 | ||||
-rw-r--r-- | tests/meson.build | 3 | ||||
-rw-r--r-- | tests/testfileportal.c | 130 |
7 files changed, 795 insertions, 3 deletions
@@ -1,6 +1,9 @@ Overview of Changes in GTK+ 3.24.37, xx-xx-xxxx =============================================== +* Support the file transfer portal for copy-paste and DND + + Overview of Changes in GTK+ 3.24.36, 12-22-2022 =============================================== diff --git a/gtk/filetransferportal.c b/gtk/filetransferportal.c new file mode 100644 index 0000000000..30afaeed97 --- /dev/null +++ b/gtk/filetransferportal.c @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2018 Matthias Clasen + * + * 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 <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <gio/gio.h> + +#ifdef G_OS_UNIX + +#include <gio/gunixfdlist.h> + +#ifndef O_PATH +#define O_PATH 0 +#endif + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#else +#define HAVE_O_CLOEXEC 1 +#endif + +#include "filetransferportalprivate.h" + +static GDBusProxy *file_transfer_proxy = NULL; + +typedef struct { + GTask *task; + char **files; + int len; + int start; +} AddFileData; + +static void +free_add_file_data (gpointer data) +{ + AddFileData *afd = data; + + g_object_unref (afd->task); + g_free (afd->files); + g_free (afd); +} + +static void add_files (GDBusProxy *proxy, + AddFileData *afd); + +static void +add_files_done (GObject *object, + GAsyncResult *result, + gpointer data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (object); + AddFileData *afd = data; + GError *error = NULL; + GVariant *ret; + + ret = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, NULL, result, &error); + if (ret == NULL) + { + g_task_return_error (afd->task, error); + free_add_file_data (afd); + return; + } + + g_variant_unref (ret); + + if (afd->start >= afd->len) + { + g_task_return_boolean (afd->task, TRUE); + free_add_file_data (afd); + return; + } + + add_files (proxy, afd); +} + +/* We call AddFiles in chunks of 16 to avoid running into + * the per-message fd limit of the bus. + */ +static void +add_files (GDBusProxy *proxy, + AddFileData *afd) +{ + GUnixFDList *fd_list; + GVariantBuilder fds, options; + int i; + char *key; + + g_variant_builder_init (&fds, G_VARIANT_TYPE ("ah")); + fd_list = g_unix_fd_list_new (); + + for (i = 0; afd->files[afd->start + i]; i++) + { + int fd; + int fd_in; + GError *error = NULL; + + if (i == 16) + break; + + fd = open (afd->files[afd->start + i], O_PATH | O_CLOEXEC); + if (fd == -1) + { + g_task_return_new_error (afd->task, G_IO_ERROR, g_io_error_from_errno (errno), + "Failed to open %s", afd->files[afd->start + i]); + free_add_file_data (afd); + g_object_unref (fd_list); + return; + } + +#ifndef HAVE_O_CLOEXEC + fcntl (fd, F_SETFD, FD_CLOEXEC); +#endif + fd_in = g_unix_fd_list_append (fd_list, fd, &error); + close (fd); + + if (fd_in == -1) + { + g_task_return_error (afd->task, error); + free_add_file_data (afd); + g_object_unref (fd_list); + return; + } + + g_variant_builder_add (&fds, "h", fd_in); + } + + afd->start += 16; + + key = (char *)g_object_get_data (G_OBJECT (afd->task), "key"); + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_dbus_proxy_call_with_unix_fd_list (proxy, + "AddFiles", + g_variant_new ("(saha{sv})", key, &fds, &options), + 0, -1, + fd_list, + NULL, + add_files_done, afd); + + g_object_unref (fd_list); +} + +static void +start_session_done (GObject *object, + GAsyncResult *result, + gpointer data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (object); + AddFileData *afd = data; + GError *error = NULL; + GVariant *ret; + const char *key; + + ret = g_dbus_proxy_call_finish (proxy, result, &error); + if (ret == NULL) + { + g_task_return_error (afd->task, error); + free_add_file_data (afd); + return; + } + + g_variant_get (ret, "(&s)", &key); + + g_object_set_data_full (G_OBJECT (afd->task), "key", g_strdup (key), g_free); + + g_variant_unref (ret); + + add_files (proxy, afd); +} + +void +file_transfer_portal_register_files (const char **files, + gboolean writable, + GAsyncReadyCallback callback, + gpointer data) +{ + GTask *task; + AddFileData *afd; + GVariantBuilder options; + + task = g_task_new (NULL, NULL, callback, data); + + if (file_transfer_proxy == NULL) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "No portal found"); + g_object_unref (task); + return; + } + + afd = g_new (AddFileData, 1); + afd->task = task; + afd->files = g_strdupv ((char **)files); + afd->len = g_strv_length (afd->files); + afd->start = 0; + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&options, "{sv}", "writable", g_variant_new_boolean (writable)); + g_variant_builder_add (&options, "{sv}", "autostop", g_variant_new_boolean (TRUE)); + + g_dbus_proxy_call (file_transfer_proxy, "StartTransfer", + g_variant_new ("(a{sv})", &options), + 0, -1, NULL, start_session_done, afd); +} + +gboolean +file_transfer_portal_register_files_finish (GAsyncResult *result, + char **key, + GError **error) +{ + if (g_task_propagate_boolean (G_TASK (result), error)) + { + *key = g_strdup (g_object_get_data (G_OBJECT (result), "key")); + return TRUE; + } + + return FALSE; +} + +char * +file_transfer_portal_register_files_sync (const char **files, + gboolean writable, + GError **error) +{ + const char *value; + char *key; + GUnixFDList *fd_list; + GVariantBuilder fds, options; + int i; + GVariant *ret; + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + ret = g_dbus_proxy_call_sync (file_transfer_proxy, + "StartTransfer", + g_variant_new ("(a{sv})", &options), + 0, + -1, + NULL, + error); + if (ret == NULL) + return NULL; + + g_variant_get (ret, "(&s)", &value); + key = g_strdup (value); + g_variant_unref (ret); + + fd_list = NULL; + + for (i = 0; files[i]; i++) + { + int fd; + int fd_in; + + if (fd_list == NULL) + { + g_variant_builder_init (&fds, G_VARIANT_TYPE ("ah")); + fd_list = g_unix_fd_list_new (); + } + + fd = open (files[i], O_PATH | O_CLOEXEC); + if (fd == -1) + { + g_set_error (error, + G_IO_ERROR, g_io_error_from_errno (errno), + "Failed to open %s", files[i]); + g_variant_builder_clear (&fds); + g_object_unref (fd_list); + g_free (key); + return NULL; + } + +#ifndef HAVE_O_CLOEXEC + fcntl (fd, F_SETFD, FD_CLOEXEC); +#endif + fd_in = g_unix_fd_list_append (fd_list, fd, error); + close (fd); + + if (fd_in == -1) + { + g_variant_builder_clear (&fds); + g_object_unref (fd_list); + g_free (key); + return NULL; + } + + g_variant_builder_add (&fds, "h", fd_in); + + if ((i + 1) % 16 == 0 || files[i + 1] == NULL) + { + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + ret = g_dbus_proxy_call_with_unix_fd_list_sync (file_transfer_proxy, + "AddFiles", + g_variant_new ("(saha{sv})", + key, + &fds, + &options), + 0, + -1, + fd_list, + NULL, + NULL, + error); + g_clear_object (&fd_list); + + if (ret == NULL) + { + g_free (key); + return NULL; + } + + g_variant_unref (ret); + } + } + + return key; +} + +static void +retrieve_files_done (GObject *object, + GAsyncResult *result, + gpointer data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (object); + GTask *task = data; + GError *error = NULL; + GVariant *ret; + char **files; + + ret = g_dbus_proxy_call_finish (proxy, result, &error); + if (ret == NULL) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_variant_get (ret, "(^a&s)", &files); + + g_object_set_data_full (G_OBJECT (task), "files", g_strdupv (files), (GDestroyNotify)g_strfreev); + + g_variant_unref (ret); + + g_task_return_boolean (task, TRUE); +} + +void +file_transfer_portal_retrieve_files (const char *key, + GAsyncReadyCallback callback, + gpointer data) +{ + GTask *task; + GVariantBuilder options; + + task = g_task_new (NULL, NULL, callback, data); + + if (file_transfer_proxy == NULL) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "No portal found"); + g_object_unref (task); + return; + } + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_dbus_proxy_call (file_transfer_proxy, + "RetrieveFiles", + g_variant_new ("(sa{sv})", key, &options), + 0, -1, NULL, + retrieve_files_done, task); +} + +gboolean +file_transfer_portal_retrieve_files_finish (GAsyncResult *result, + char ***files, + GError **error) +{ + if (g_task_propagate_boolean (G_TASK (result), error)) + { + *files = g_strdupv (g_object_get_data (G_OBJECT (result), "files")); + return TRUE; + } + + return FALSE; +} + +char ** +file_transfer_portal_retrieve_files_sync (const char *key, + GError **error) +{ + GVariantBuilder options; + GVariant *ret; + char **files = NULL; + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + ret = g_dbus_proxy_call_sync (file_transfer_proxy, + "RetrieveFiles", + g_variant_new ("(sa{sv})", key, &options), + 0, -1, NULL, + error); + if (ret) + { + const char **value; + g_variant_get (ret, "(^a&s)", &value); + files = g_strdupv ((char **)value); + g_variant_unref (ret); + } + + return files; +} + +static void +connection_closed (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error) +{ + g_clear_object (&file_transfer_proxy); +} + +static void +finish_registration (void) +{ + /* Free the singleton when the connection closes, important for test */ + g_signal_connect (g_dbus_proxy_get_connection (G_DBUS_PROXY (file_transfer_proxy)), + "closed", G_CALLBACK (connection_closed), NULL); +} + +static gboolean +proxy_has_owner (GDBusProxy *proxy) +{ + char *owner; + + owner = g_dbus_proxy_get_name_owner (proxy); + if (owner) + { + g_free (owner); + return TRUE; + } + + return FALSE; +} + +void +file_transfer_portal_register (void) +{ + static gboolean called; + + if (!called) + { + called = TRUE; + + file_transfer_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES + | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS + | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, + "org.freedesktop.portal.Documents", + "/org/freedesktop/portal/documents", + "org.freedesktop.portal.FileTransfer", + NULL, + NULL); + + if (file_transfer_proxy && !proxy_has_owner (file_transfer_proxy)) + g_clear_object (&file_transfer_proxy); + + if (file_transfer_proxy) + finish_registration (); + } +} + +gboolean +file_transfer_portal_supported (void) +{ + file_transfer_portal_register (); + + return file_transfer_proxy != NULL; +} + +#endif /* G_OS_UNIX */ diff --git a/gtk/filetransferportalprivate.h b/gtk/filetransferportalprivate.h new file mode 100644 index 0000000000..d136b53345 --- /dev/null +++ b/gtk/filetransferportalprivate.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 Matthias Clasen + * + * 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/>. + */ + +#ifndef __FILE_TRANSFER_PROTOCOL_H__ +#define __FILE_TRANSFER_PROTOCOL_H__ + + +void file_transfer_portal_register (void); + +void file_transfer_portal_register_files (const char **files, + gboolean writable, + GAsyncReadyCallback callback, + gpointer data); +gboolean file_transfer_portal_register_files_finish (GAsyncResult *result, + char **key, + GError **error); + +void file_transfer_portal_retrieve_files (const char *key, + GAsyncReadyCallback callback, + gpointer data); +gboolean file_transfer_portal_retrieve_files_finish (GAsyncResult *result, + char ***files, + GError **error); + + +char * file_transfer_portal_register_files_sync (const char **files, + gboolean writable, + GError **error); + +char ** file_transfer_portal_retrieve_files_sync (const char *key, + GError **error); + +gboolean file_transfer_portal_supported (void); + +#endif diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c index 32d9de88bf..048b4ad496 100644 --- a/gtk/gtkselection.c +++ b/gtk/gtkselection.c @@ -116,6 +116,10 @@ #include "broadway/gdkbroadway.h" #endif +#ifndef G_OS_WIN32 +#include "filetransferportalprivate.h" +#endif + #undef DEBUG_SELECTION /* Maximum size of a sent chunk, in bytes. Also the default size of @@ -338,6 +342,7 @@ static GdkAtom text_plain_atom; static GdkAtom text_plain_utf8_atom; static GdkAtom text_plain_locale_atom; static GdkAtom text_uri_list_atom; +static GdkAtom portal_files_atom; static void init_atoms (void) @@ -358,6 +363,7 @@ init_atoms (void) g_free (tmp); text_uri_list_atom = gdk_atom_intern_static_string ("text/uri-list"); + portal_files_atom = gdk_atom_intern_static_string ("application/vnd.portal.files"); } } @@ -502,6 +508,10 @@ gtk_target_list_add_image_targets (GtkTargetList *list, * Appends the URI targets supported by #GtkSelectionData to * the target list. All targets are added with the same @info. * + * Since 3.24.37, this includes the application/vnd.portal.files + * target when possible, to allow sending files between sandboxed + * apps via the FileTransfer portal. + * * Since: 2.6 **/ void @@ -512,7 +522,12 @@ gtk_target_list_add_uri_targets (GtkTargetList *list, init_atoms (); - gtk_target_list_add (list, text_uri_list_atom, 0, info); + gtk_target_list_add (list, text_uri_list_atom, 0, info); + +#ifndef G_OS_WIN32 + if (file_transfer_portal_supported ()) + gtk_target_list_add (list, portal_files_atom, 0, info); +#endif } /** @@ -1835,6 +1850,9 @@ gtk_selection_data_get_pixbuf (const GtkSelectionData *selection_data) * Sets the contents of the selection from a list of URIs. * The string is converted to the form determined by * @selection_data->target. + * + * Since 3.24.37, this may involve using the FileTransfer + * portal to send files between sandboxed apps. * * Returns: %TRUE if the selection was successfully set, * otherwise %FALSE. @@ -1880,6 +1898,57 @@ gtk_selection_data_set_uris (GtkSelectionData *selection_data, return TRUE; } } +#ifndef G_OS_WIN32 + else if (selection_data->target == portal_files_atom && + file_transfer_portal_supported ()) + { + GPtrArray *a; + char **files; + char *key; + GError *error = NULL; + + a = g_ptr_array_new (); + + for (int i = 0; uris[i]; i++) + { + GFile *file; + char *path; + + file = g_file_new_for_uri (uris[i]); + path = g_file_get_path (file); + g_object_unref (file); + + if (path == NULL) + { + g_ptr_array_unref (a); + return FALSE; + } + + g_ptr_array_add (a, path); + } + + g_ptr_array_add (a, NULL); + files = (char **) g_ptr_array_free (a, FALSE); + + key = file_transfer_portal_register_files_sync ((const char **)files, TRUE, &error); + if (key == NULL) + { + g_strfreev (files); + g_warning ("%s", error->message); + g_error_free (error); + return FALSE; + } + + gtk_selection_data_set (selection_data, + portal_files_atom, + 8, (guchar *)key, strlen (key)); + + g_strfreev (files); + g_free (key); + + return TRUE; + } +#endif return FALSE; } @@ -1890,6 +1959,9 @@ gtk_selection_data_set_uris (GtkSelectionData *selection_data, * * Gets the contents of the selection data as array of URIs. * + * Since 3.24.37, this may involve using the FileTransfer + * portal to send files between sandboxed apps. + * * Returns: (array zero-terminated=1) (element-type utf8) (transfer full): if * the selection data contains a list of * URIs, a newly allocated %NULL-terminated string array @@ -1922,6 +1994,40 @@ gtk_selection_data_get_uris (const GtkSelectionData *selection_data) g_strfreev (list); } +#ifndef G_OS_WIN32 + else if (selection_data->length >= 0 && + selection_data->type == portal_files_atom && + file_transfer_portal_supported ()) + { + char *key; + GError *error = NULL; + char **files; + + key = g_strndup ((char *) selection_data->data, selection_data->length); + files = file_transfer_portal_retrieve_files_sync (key, &error); + if (error) + { + g_warning ("%s", error->message); + g_error_free (error); + } + g_free (key); + + if (files) + { + GPtrArray *uris = g_ptr_array_new (); + + for (int i = 0; files[i]; i++) + { + GFile *file = g_file_new_for_path (files[i]); + g_ptr_array_add (uris, g_file_get_uri (file)); + g_object_unref (file); + } + + g_ptr_array_add (uris, NULL); + result = (char **) g_ptr_array_free (uris, FALSE); + } + } +#endif return result; } @@ -2246,7 +2352,8 @@ gtk_targets_include_uri (GdkAtom *targets, for (i = 0; i < n_targets; i++) { - if (targets[i] == text_uri_list_atom) + if (targets[i] == text_uri_list_atom || + targets[i] == portal_files_atom) { result = TRUE; break; diff --git a/gtk/meson.build b/gtk/meson.build index 21540dd93b..4b7a453e11 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -660,6 +660,10 @@ if os_unix and tracker3_enabled endif if os_unix + gtk_unix_sources += 'filetransferportal.c' +endif + +if os_unix gtk_sources += gtk_unix_sources endif diff --git a/tests/meson.build b/tests/meson.build index 586fe2f45e..6ff249f8a1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -36,7 +36,7 @@ gtk_tests = [ ['testcombo'], ['testcombochange'], ['testdialog'], - ['testdnd'], + ['testdnd2'], ['testellipsise'], ['testemblems'], ['testentrycompletion'], @@ -45,6 +45,7 @@ gtk_tests = [ ['testexpander'], ['testfilechooserbutton'], ['testfilechooser'], + ['testfileportal'], ['testflowbox'], ['testfontchooser'], ['testfontoptions'], diff --git a/tests/testfileportal.c b/tests/testfileportal.c new file mode 100644 index 0000000000..0918af1d1b --- /dev/null +++ b/tests/testfileportal.c @@ -0,0 +1,130 @@ +/* simple.c + * Copyright (C) 1997 Red Hat, Inc + * Author: Elliot Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ +#include "config.h" +#include <gtk/gtk.h> + +static void +drag_begin_cb (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + char **uris; + char *cwd; + + cwd = g_get_current_dir (); + uris = g_new0 (char *, 2); + uris[0] = g_strconcat ("file://", cwd, "/README.md", NULL); + g_free (cwd); + + g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL); + gtk_drag_set_icon_default (context); + + g_object_set_data_full (G_OBJECT (widget), "uris", g_strdupv ((char **)uris), (GDestroyNotify) g_strfreev); +} + +static void +drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection, + unsigned int target_info, + unsigned int time, + gpointer data) +{ + char **uris = (char **)g_object_get_data (G_OBJECT (widget), "uris"); + + gtk_selection_data_set_uris (selection, uris); + + g_object_set_data (G_OBJECT (widget), "uris", NULL); +} + +static void +drag_data_received (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + GtkSelectionData *selection_data, + unsigned int info, + unsigned int time, + gpointer user_data) +{ + GtkLabel *label = user_data; + char **uris; + + uris = gtk_selection_data_get_uris (selection_data); + + if (uris) + { + gtk_label_set_label (label, uris[0]); + g_strfreev (uris); + } +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window, *label, *eventbox, *box; + GtkTargetEntry targets[] = { + { "application/vnd.portal.files", 0, 0 }, + }; + + gtk_init (&argc, &argv); + + window = g_object_connect (g_object_new (gtk_window_get_type (), + "type", GTK_WINDOW_TOPLEVEL, + "title", "hello world", + "resizable", FALSE, + "border_width", 10, + NULL), + "signal::destroy", gtk_main_quit, NULL, + NULL); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_show (box); + gtk_container_add (GTK_CONTAINER (window), box); + + eventbox = gtk_event_box_new (); + gtk_container_add (GTK_CONTAINER (box), eventbox); + gtk_widget_show (eventbox); + gtk_event_box_set_above_child (GTK_EVENT_BOX (eventbox), TRUE); + + label = gtk_label_new ("drag me"); + gtk_container_add (GTK_CONTAINER (eventbox), label); + + gtk_drag_source_set (eventbox, GDK_BUTTON1_MASK, targets, G_N_ELEMENTS (targets), GDK_ACTION_COPY); + g_signal_connect (eventbox, "drag-begin", G_CALLBACK (drag_begin_cb), NULL); + g_signal_connect (eventbox, "drag-data-get", G_CALLBACK (drag_data_get), NULL); + gtk_widget_show (label); + + eventbox = gtk_event_box_new (); + gtk_container_add (GTK_CONTAINER (box), eventbox); + gtk_widget_show (eventbox); + gtk_event_box_set_above_child (GTK_EVENT_BOX (eventbox), TRUE); + + label = gtk_label_new ("drop here"); + gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (eventbox), label); + gtk_drag_dest_set (eventbox, GTK_DEST_DEFAULT_ALL, targets, G_N_ELEMENTS (targets), GDK_ACTION_COPY); + + g_signal_connect (eventbox, "drag-data-received", G_CALLBACK (drag_data_received), label); + + gtk_widget_show (window); + + gtk_main (); + + return 0; +} |