summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--demos/gtk-demo/clipboard.c123
-rw-r--r--demos/icon-browser/iconbrowserwin.c143
-rw-r--r--docs/reference/gtk/gtk4-docs.xml8
-rw-r--r--docs/reference/gtk/gtk4-sections.txt109
-rw-r--r--docs/reference/gtk/gtk4.types.in3
-rw-r--r--docs/reference/gtk/meson.build1
-rw-r--r--docs/reference/gtk/migrating-3to4.xml31
-rw-r--r--gdk/gdkcontentdeserializer.c64
-rw-r--r--gdk/gdkcontentproviderimpl.c230
-rw-r--r--gdk/gdkcontentproviderimpl.h15
-rw-r--r--gdk/gdkcontentserializer.c49
-rw-r--r--gdk/gdkdrag.c24
-rw-r--r--gdk/gdkdrop.c27
-rw-r--r--gdk/gdkdrop.h3
-rw-r--r--gdk/gdkevents.c7
-rw-r--r--gdk/gdksurface.c8
-rw-r--r--gdk/x11/gdkdrop-x11.c15
-rw-r--r--gtk/gtk.h2
-rw-r--r--gtk/gtkcalendar.c241
-rw-r--r--gtk/gtkcolorbutton.c149
-rw-r--r--gtk/gtkcolorswatch.c163
-rw-r--r--gtk/gtkdnd.c1425
-rw-r--r--gtk/gtkdnd.h102
-rw-r--r--gtk/gtkdndprivate.h59
-rw-r--r--gtk/gtkdragdest.c1180
-rw-r--r--gtk/gtkdragdest.h83
-rw-r--r--gtk/gtkdragdestprivate.h32
-rw-r--r--gtk/gtkdragicon.c74
-rw-r--r--gtk/gtkdragicon.h51
-rw-r--r--gtk/gtkdragiconprivate.h6
-rw-r--r--gtk/gtkdragsource.c816
-rw-r--r--gtk/gtkdragsource.h51
-rw-r--r--gtk/gtkentry.c45
-rw-r--r--gtk/gtkentry.h2
-rw-r--r--gtk/gtkenums.h25
-rw-r--r--gtk/gtkexpander.c37
-rw-r--r--gtk/gtkfilechooserbutton.c144
-rw-r--r--gtk/gtkfilechooserwidget.c92
-rw-r--r--gtk/gtkgesturelongpress.c2
-rw-r--r--gtk/gtkiconview.c358
-rw-r--r--gtk/gtkiconview.h4
-rw-r--r--gtk/gtkiconviewprivate.h8
-rw-r--r--gtk/gtklabel.c104
-rw-r--r--gtk/gtklinkbutton.c114
-rw-r--r--gtk/gtklistbox.c16
-rw-r--r--gtk/gtkmain.c69
-rw-r--r--gtk/gtkmain.h2
-rw-r--r--gtk/gtkmarshalers.list1
-rw-r--r--gtk/gtknotebook.c367
-rw-r--r--gtk/gtkpathbar.c48
-rw-r--r--gtk/gtkplacessidebar.c265
-rw-r--r--gtk/gtkpopover.c3
-rw-r--r--gtk/gtkprivate.h2
-rw-r--r--gtk/gtkscrolledwindow.c1
-rw-r--r--gtk/gtksearchlistmodel.c544
-rw-r--r--gtk/gtksearchlistmodel.h50
-rw-r--r--gtk/gtkselection.c131
-rw-r--r--gtk/gtkselection.h7
-rw-r--r--gtk/gtkstackswitcher.c57
-rw-r--r--gtk/gtktext.c331
-rw-r--r--gtk/gtktextbuffer.c8
-rw-r--r--gtk/gtktextbuffer.h4
-rw-r--r--gtk/gtktextutil.h2
-rw-r--r--gtk/gtktextview.c408
-rw-r--r--gtk/gtktreednd.h2
-rw-r--r--gtk/gtktreeview.c438
-rw-r--r--gtk/gtktreeview.h5
-rw-r--r--gtk/gtkwidget.c377
-rw-r--r--gtk/gtkwidget.h47
-rw-r--r--gtk/gtkwindow.c7
-rw-r--r--gtk/meson.build2
-rw-r--r--gtk/theme/Adwaita/_common.scss26
-rw-r--r--gtk/ui/gtkfilechooserwidget.ui5
-rw-r--r--tests/meson.build2
-rw-r--r--tests/testdnd.c260
-rw-r--r--tests/testdnd2.c467
-rw-r--r--tests/testdnd3.c398
-rw-r--r--tests/testentryicons.c57
-rw-r--r--tests/testimage.c178
-rw-r--r--tests/testlist3.c70
-rw-r--r--tests/testnotebookdnd.c84
-rw-r--r--tests/testtoolbar.c44
-rw-r--r--tests/testtreednd.c30
83 files changed, 5567 insertions, 5477 deletions
diff --git a/demos/gtk-demo/clipboard.c b/demos/gtk-demo/clipboard.c
index d4052e2e93..c8e5323bab 100644
--- a/demos/gtk-demo/clipboard.c
+++ b/demos/gtk-demo/clipboard.c
@@ -119,48 +119,76 @@ get_image_paintable (GtkImage *image)
}
static void
-drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
+drag_begin (GtkDragSource *source,
+ GdkDrag *drag,
+ GtkWidget *widget)
{
GdkPaintable *paintable;
paintable = get_image_paintable (GTK_IMAGE (widget));
if (paintable)
{
- gtk_drag_set_icon_paintable (drag, paintable, -2, -2);
+ gtk_drag_source_set_icon (source, paintable, -2, -2);
g_object_unref (paintable);
}
}
-void
-drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- guint info,
- gpointer data)
+static void
+get_texture (GValue *value,
+ gpointer data)
{
- GdkPaintable *paintable;
+ GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (data));
- paintable = get_image_paintable (GTK_IMAGE (widget));
if (GDK_IS_TEXTURE (paintable))
- gtk_selection_data_set_texture (selection_data, GDK_TEXTURE (paintable));
+ g_value_set_object (value, paintable);
+}
+
+static GdkContentProvider *
+prepare_drag (GtkDragSource *source,
+ double x,
+ double y,
+ GtkWidget *image)
+{
+ return gdk_content_provider_new_with_callback (GDK_TYPE_TEXTURE, get_texture, image);
}
static void
-drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data,
- gpointer data)
+got_texture (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
- if (gtk_selection_data_get_length (selection_data) > 0)
+ GdkDrop *drop = GDK_DROP (source);
+ GtkWidget *image = data;
+ const GValue *value;
+ GError *error = NULL;
+
+ value = gdk_drop_read_value_finish (drop, result, &error);
+ if (value)
{
- GdkTexture *texture;
+ GdkTexture *texture = g_value_get_object (value);
+ gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture));
+ }
+ else
+ {
+ g_print ("Failed to get data: %s\n", error->message);
+ g_error_free (error);
+ }
+}
- texture = gtk_selection_data_get_texture (selection_data);
- gtk_image_set_from_paintable (GTK_IMAGE (data), GDK_PAINTABLE (texture));
- g_object_unref (texture);
+static gboolean
+drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkWidget *widget)
+{
+ if (gdk_drop_has_value (drop, GDK_TYPE_TEXTURE))
+ {
+ gdk_drop_read_value_async (drop, GDK_TYPE_TEXTURE, G_PRIORITY_DEFAULT, NULL, got_texture, widget);
+ return TRUE;
}
+
+ return FALSE;
}
static void
@@ -171,12 +199,8 @@ copy_image (GSimpleAction *action,
GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (data));
GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (data));
- g_print ("copy image\n");
if (GDK_IS_TEXTURE (paintable))
- {
-g_print ("set clipboard\n");
- gdk_clipboard_set_texture (clipboard, GDK_TEXTURE (paintable));
- }
+ gdk_clipboard_set_texture (clipboard, GDK_TEXTURE (paintable));
if (paintable)
g_object_unref (paintable);
@@ -247,6 +271,9 @@ do_clipboard (GtkWidget *do_widget)
{ "paste", paste_image, NULL, NULL, NULL },
};
GActionGroup *actions;
+ GtkDragSource *source;
+ GtkDropTarget *dest;
+ GdkContentFormats *formats;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_display (GTK_WINDOW (window),
@@ -305,22 +332,21 @@ do_clipboard (GtkWidget *do_widget)
/* Create the first image */
image = gtk_image_new_from_icon_name ("dialog-warning");
+ gtk_image_set_pixel_size (GTK_IMAGE (image), 48);
gtk_container_add (GTK_CONTAINER (hbox), image);
/* make image a drag source */
- gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, GDK_ACTION_COPY);
- gtk_drag_source_add_image_targets (image);
- g_signal_connect (image, "drag-begin",
- G_CALLBACK (drag_begin), image);
- g_signal_connect (image, "drag-data-get",
- G_CALLBACK (drag_data_get), image);
+ source = gtk_drag_source_new ();
+ g_signal_connect (source, "prepare", G_CALLBACK (prepare_drag), NULL);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
/* accept drops on image */
- gtk_drag_dest_set (image, GTK_DEST_DEFAULT_ALL,
- NULL, GDK_ACTION_COPY);
- gtk_drag_dest_add_image_targets (image);
- g_signal_connect (image, "drag-data-received",
- G_CALLBACK (drag_data_received), image);
+ formats = gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE);
+ dest = gtk_drop_target_new (formats, GDK_ACTION_COPY);
+ gdk_content_formats_unref (formats);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), image);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
/* context menu on image */
gesture = gtk_gesture_click_new ();
@@ -337,22 +363,21 @@ do_clipboard (GtkWidget *do_widget)
/* Create the second image */
image = gtk_image_new_from_icon_name ("process-stop");
+ gtk_image_set_pixel_size (GTK_IMAGE (image), 48);
gtk_container_add (GTK_CONTAINER (hbox), image);
/* make image a drag source */
- gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, GDK_ACTION_COPY);
- gtk_drag_source_add_image_targets (image);
- g_signal_connect (image, "drag-begin",
- G_CALLBACK (drag_begin), image);
- g_signal_connect (image, "drag-data-get",
- G_CALLBACK (drag_data_get), image);
+ source = gtk_drag_source_new ();
+ g_signal_connect (source, "prepare", G_CALLBACK (prepare_drag), NULL);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
/* accept drops on image */
- gtk_drag_dest_set (image, GTK_DEST_DEFAULT_ALL,
- NULL, GDK_ACTION_COPY);
- gtk_drag_dest_add_image_targets (image);
- g_signal_connect (image, "drag-data-received",
- G_CALLBACK (drag_data_received), image);
+ formats = gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE);
+ dest = gtk_drop_target_new (formats, GDK_ACTION_COPY);
+ gdk_content_formats_unref (formats);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), image);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
/* context menu on image */
gesture = gtk_gesture_click_new ();
diff --git a/demos/icon-browser/iconbrowserwin.c b/demos/icon-browser/iconbrowserwin.c
index 593c8cf917..d31a635b4b 100644
--- a/demos/icon-browser/iconbrowserwin.c
+++ b/demos/icon-browser/iconbrowserwin.c
@@ -5,9 +5,6 @@
#include <gtk/gtk.h>
/* Drag 'n Drop */
-static const char *target_table[] = {
- "text/uri-list"
-};
typedef struct
{
@@ -76,30 +73,11 @@ search_text_changed (GtkEntry *entry, IconBrowserWindow *win)
gtk_tree_model_filter_refilter (win->filter_model);
}
-static GdkPixbuf *
-get_icon (GtkWidget *image, const gchar *name, gint size)
-{
- GtkIconInfo *info;
- GtkStyleContext *context;
- GdkTexture *texture;
- GdkPixbuf *pixbuf;
-
- context = gtk_widget_get_style_context (image);
- info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (), name, size, 0);
- texture = GDK_TEXTURE (gtk_icon_info_load_symbolic_for_context (info, context, NULL, NULL));
- pixbuf = gdk_pixbuf_get_from_texture (texture);
- g_object_unref (texture);
- g_object_unref (info);
-
- return pixbuf;
-}
-
static void
set_image (GtkWidget *image, const gchar *name, gint size)
{
gtk_image_set_from_icon_name (GTK_IMAGE (image), name);
gtk_image_set_pixel_size (GTK_IMAGE (image), size);
- gtk_drag_source_set_icon_name (image, name);
}
static void
@@ -365,78 +343,107 @@ search_mode_toggled (GObject *searchbar, GParamSpec *pspec, IconBrowserWindow *w
gtk_list_box_unselect_all (GTK_LIST_BOX (win->context_list));
}
+static GdkPaintable *
+get_image_paintable (GtkImage *image)
+{
+ const gchar *icon_name;
+ GtkIconTheme *icon_theme;
+ GtkIconInfo *icon_info;
+ int size;
+
+ switch (gtk_image_get_storage_type (image))
+ {
+ case GTK_IMAGE_PAINTABLE:
+ return g_object_ref (gtk_image_get_paintable (image));
+ case GTK_IMAGE_ICON_NAME:
+ icon_name = gtk_image_get_icon_name (image);
+ size = gtk_image_get_pixel_size (image);
+ icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (image)));
+ icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size,
+ GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_GENERIC_FALLBACK);
+ if (icon_info == NULL)
+ return NULL;
+ return gtk_icon_info_load_icon (icon_info, NULL);
+ default:
+ g_warning ("Image storage type %d not handled",
+ gtk_image_get_storage_type (image));
+ return NULL;
+ }
+}
+
static void
-get_image_data (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection,
- guint target_info,
- gpointer data)
+drag_begin (GtkDragSource *source,
+ GdkDrag *drag,
+ GtkWidget *widget)
{
- GtkWidget *image;
- const gchar *name;
- gint size;
- GdkPixbuf *pixbuf;
+ GdkPaintable *paintable;
- image = gtk_bin_get_child (GTK_BIN (widget));
+ paintable = get_image_paintable (GTK_IMAGE (widget));
+ if (paintable)
+ {
+ int w, h;
- name = gtk_image_get_icon_name (GTK_IMAGE (image));
- size = gtk_image_get_pixel_size (GTK_IMAGE (image));
+ w = gdk_paintable_get_intrinsic_width (paintable);
+ h = gdk_paintable_get_intrinsic_height (paintable);
+ gtk_drag_source_set_icon (source, paintable, w, h);
+ g_object_unref (paintable);
+ }
+}
- pixbuf = get_icon (image, name, size);
- gtk_selection_data_set_pixbuf (selection, pixbuf);
- g_object_unref (pixbuf);
+static void
+get_texture (GValue *value,
+ gpointer data)
+{
+ GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (data));
+
+ if (GDK_IS_TEXTURE (paintable))
+ g_value_set_object (value, paintable);
}
static void
-get_scalable_image_data (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection,
- guint target_info,
- gpointer data)
+get_file (GValue *value,
+ gpointer data)
{
- gchar *uris[2];
+ const char *name;
GtkIconInfo *info;
- GtkWidget *image;
GFile *file;
- const gchar *name;
- image = gtk_bin_get_child (GTK_BIN (widget));
- name = gtk_image_get_icon_name (GTK_IMAGE (image));
+ name = gtk_image_get_icon_name (GTK_IMAGE (data));
info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (), name, -1, 0);
file = g_file_new_for_path (gtk_icon_info_get_filename (info));
- uris[0] = g_file_get_uri (file);
- uris[1] = NULL;
-
- gtk_selection_data_set_uris (selection, uris);
-
- g_free (uris[0]);
- g_object_unref (info);
+ g_value_set_object (value, file);
g_object_unref (file);
+ g_object_unref (info);
}
static void
setup_image_dnd (GtkWidget *image)
{
- gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, GDK_ACTION_COPY);
- gtk_drag_source_add_image_targets (image);
- g_signal_connect (image, "drag-data-get", G_CALLBACK (get_image_data), NULL);
+ GdkContentProvider *content;
+ GtkDragSource *source;
+
+ source = gtk_drag_source_new ();
+ content = gdk_content_provider_new_with_callback (GDK_TYPE_TEXTURE, get_texture, image);
+ gtk_drag_source_set_content (source, content);
+ g_object_unref (content);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
}
static void
setup_scalable_image_dnd (GtkWidget *image)
{
- GtkWidget *parent;
- GdkContentFormats *targets;
+ GdkContentProvider *content;
+ GtkDragSource *source;
- parent = gtk_widget_get_parent (image);
- targets = gdk_content_formats_new (target_table, G_N_ELEMENTS (target_table));
- gtk_drag_source_set (parent, GDK_BUTTON1_MASK,
- targets,
- GDK_ACTION_COPY);
- gdk_content_formats_unref (targets);
+ source = gtk_drag_source_new ();
+ content = gdk_content_provider_new_with_callback (G_TYPE_FILE, get_file, image);
+ gtk_drag_source_set_content (source, content);
+ g_object_unref (content);
- g_signal_connect (parent, "drag-data-get", G_CALLBACK (get_scalable_image_data), NULL);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
}
static void
@@ -446,8 +453,7 @@ icon_browser_window_init (IconBrowserWindow *win)
gtk_widget_init_template (GTK_WIDGET (win));
- list = gdk_content_formats_new (NULL, 0);
- list = gtk_content_formats_add_text_targets (list);
+ list = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (win->list),
GDK_BUTTON1_MASK,
list,
@@ -459,7 +465,6 @@ icon_browser_window_init (IconBrowserWindow *win)
setup_image_dnd (win->image3);
setup_image_dnd (win->image4);
setup_image_dnd (win->image5);
- setup_image_dnd (win->image6);
setup_scalable_image_dnd (win->image6);
win->contexts = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, context_free);
diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml
index 577f229ca9..4b2625a7b5 100644
--- a/docs/reference/gtk/gtk4-docs.xml
+++ b/docs/reference/gtk/gtk4-docs.xml
@@ -344,6 +344,13 @@
<xi:include href="xml/gtkpadcontroller.xml" />
</chapter>
+ <chapter>
+ <title>Data exchange, clipboards and Drag-and-Drop</title>
+ <xi:include href="xml/gtkdragsource.xml"/>
+ <xi:include href="xml/gtkdragicon.xml"/>
+ <xi:include href="xml/gtkdroptarget.xml"/>
+ </chapter>
+
</part>
<part id="gtkbase">
@@ -352,7 +359,6 @@
<xi:include href="xml/gtkfeatures.xml" />
<xi:include href="xml/gtkaccelgroup.xml" />
<xi:include href="xml/gtkaccelmap.xml" />
- <xi:include href="xml/gtkdnd.xml" />
<xi:include href="xml/gtksettings.xml" />
<xi:include href="xml/gtkbindings.xml" />
<xi:include href="xml/gtkenums.xml" />
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index f185463143..de0ec777dd 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -5002,49 +5002,6 @@ gtk_selection_data_get_type
</SECTION>
<SECTION>
-<FILE>gtkdnd</FILE>
-<TITLE>Drag and Drop</TITLE>
-GtkDestDefaults
-GtkDragResult
-
-<SUBSECTION Destination Side>
-gtk_drag_dest_set
-gtk_drag_dest_unset
-gtk_drag_dest_find_target
-gtk_drag_dest_get_target_list
-gtk_drag_dest_set_target_list
-gtk_drag_dest_add_text_targets
-gtk_drag_dest_add_image_targets
-gtk_drag_dest_add_uri_targets
-gtk_drag_dest_set_track_motion
-gtk_drag_dest_get_track_motion
-gtk_drag_get_data
-gtk_drag_get_source_widget
-gtk_drag_highlight
-gtk_drag_unhighlight
-
-<SUBSECTION Source Side>
-gtk_drag_begin
-gtk_drag_cancel
-gtk_drag_set_icon_widget
-gtk_drag_set_icon_paintable
-gtk_drag_set_icon_name
-gtk_drag_set_icon_gicon
-gtk_drag_set_icon_default
-gtk_drag_check_threshold
-gtk_drag_source_set
-gtk_drag_source_set_icon_name
-gtk_drag_source_set_icon_gicon
-gtk_drag_source_set_icon_paintable
-gtk_drag_source_unset
-gtk_drag_source_set_target_list
-gtk_drag_source_get_target_list
-gtk_drag_source_add_text_targets
-gtk_drag_source_add_image_targets
-gtk_drag_source_add_uri_targets
-</SECTION>
-
-<SECTION>
<FILE>gtkbindings</FILE>
<TITLE>Bindings</TITLE>
GtkBindingSet
@@ -7191,3 +7148,69 @@ gtk_constraint_guide_get_max_size
GTK_TYPE_CONSTRAINT_GUIDE
gtk_constraint_guide_get_tyoe
</SECTION>
+
+<SECTION>
+<FILE>gtkdragsource</FILE>
+GtkDragSource
+gtk_drag_source_new
+gtk_drag_source_set_content
+gtk_drag_source_get_content
+gtk_drag_source_set_actions
+gtk_drag_source_get_actions
+gtk_drag_source_set_icon
+gtk_drag_source_drag_cancel
+gtk_drag_source_get_drag
+gtk_drag_check_threshold
+<SUBSECTION Standard>
+GTK_TYPE_DRAG_SOURCE
+GTK_DRAG_SOURCE
+GTK_DRAG_SOURCE_CLASS
+GTK_IS_DRAG_SOURCE
+GTK_IS_DRAG_SOURCE_CLASS
+GTK_DRAG_SOURCE_GET_CLASS
+<SUBSECTION Private>
+gtk_drag_source_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtkdroptarget</FILE>
+GtkDropTarget
+gtk_drop_target_new
+gtk_drop_target_set_formats
+gtk_drop_target_get_formats
+gtk_drop_target_set_actions
+gtk_drop_target_get_actions
+gtk_drop_target_get_drop
+gtk_drop_target_find_mimetype
+gtk_drop_target_read_selection
+gtk_drop_target_read_selection_finish
+gtk_drag_highlight
+gtk_drag_unhighlight
+
+<SUBSECTION Standard>
+GTK_TYPE_DROP_TARGET
+GTK_DROP_TARGET
+GTK_DROP_TARGET_CLASS
+GTK_IS_DROP_TARGET
+GTK_IS_DROP_TARGET_CLASS
+GTK_DROP_TARGET_GET_CLASS
+<SUBSECTION Private>
+gtk_drop_target_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtkdragicon</FILE>
+GtkDragIcon
+gtk_drag_icon_new_for_drag
+gtk_drag_icon_set_from_paintable
+
+<SUBSECTION Standard>
+GTK_TYPE_DRAG_ICON
+GTK_DRAG_ICON
+GTK_DRAG_ICON_CLASS
+GTK_IS_DRAG_ICON
+GTK_IS_DRAG_ICON_CLASS
+GTK_DRAG_ICON_GET_CLASS
+<SUBSECTION Private>
+gtk_drag_icon_get_type
+</SECTION>
diff --git a/docs/reference/gtk/gtk4.types.in b/docs/reference/gtk/gtk4.types.in
index f758c3eee7..60d8712f97 100644
--- a/docs/reference/gtk/gtk4.types.in
+++ b/docs/reference/gtk/gtk4.types.in
@@ -57,7 +57,10 @@ gtk_constraint_target_get_type
gtk_container_get_type
gtk_css_provider_get_type
gtk_dialog_get_type
+gtk_drag_icon_get_type
+gtk_drag_source_get_type
gtk_drawing_area_get_type
+gtk_drop_target_get_type
gtk_editable_get_type
gtk_entry_buffer_get_type
gtk_entry_completion_get_type
diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build
index 1cb3f26991..0f01d83117 100644
--- a/docs/reference/gtk/meson.build
+++ b/docs/reference/gtk/meson.build
@@ -96,7 +96,6 @@ private_headers = [
'gtkcsswidgetnodeprivate.h',
'gtkcsswin32sizevalueprivate.h',
'gtkdialogprivate.h',
- 'gtkdndprivate.h',
'gtkentryprivate.h',
'gtkeventcontrollerlegacyprivate.h',
'gtkeventcontrollerprivate.h',
diff --git a/docs/reference/gtk/migrating-3to4.xml b/docs/reference/gtk/migrating-3to4.xml
index d64261eee7..5d1e776514 100644
--- a/docs/reference/gtk/migrating-3to4.xml
+++ b/docs/reference/gtk/migrating-3to4.xml
@@ -903,6 +903,37 @@
gtk_tooltip_set_custom().
</para>
</section>
+
+ <section>
+ <title>Switch to the new DND api</title>
+ <para>
+ The source-side DND apis in GTK 4 have been changed to use an event controller, #GtkDragSource.
+ </para>
+ <para>
+ Instead of calling gtk_drag_source_set() and connecting to #GtkWidget signals, you create
+ a #GtkDragSource object, attach it to the widget with gtk_widget_add_controller(), and connect
+ to #GtkDragSource signals. Instead of calling gtk_drag_begin() on a widget to start a drag
+ manually, call gdk_drag_begin().
+ </para>
+ <para>
+ The ::drag-data-get signal has been replaced by the #GtkDragSource::prepare signal, which
+ returns a #GdkContentProvider for the drag operation.
+ </para>
+ <para>
+ The destination-side DND apis in GTK 4 have also been changed to use and event controller,
+ #GTkDropTarget.
+ </para>
+ <para>
+ Instead of calling gtk_drag_dest_set() and connecting to #GtkWidget signals, you create
+ a #GtkDropTarget object, attach it to the widget with gtk_widget_add_controller(), and
+ connect to #GtkDropTarget signals.
+ </para>
+ <para>
+ The ::drag-motion signal has been renamed to #GtkDragSource::accept, and instead of
+ ::drag-data-received, you need to use async read methods on the #GdkDrop object, such
+ as gdk_drop_read_value_async() or gdk_drop_read_text_async().
+ </para>
+ </section>
</section>
</chapter>
diff --git a/gdk/gdkcontentdeserializer.c b/gdk/gdkcontentdeserializer.c
index 79b7df86a2..0015520b3c 100644
--- a/gdk/gdkcontentdeserializer.c
+++ b/gdk/gdkcontentdeserializer.c
@@ -24,6 +24,7 @@
#include "gdkcontentformats.h"
#include "filetransferportalprivate.h"
#include "gdktexture.h"
+#include "gdkrgbaprivate.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
@@ -849,6 +850,63 @@ file_uri_deserializer (GdkContentDeserializer *deserializer)
}
static void
+color_deserializer_finish (GObject *source,
+ GAsyncResult *result,
+ gpointer deserializer)
+{
+ GOutputStream *stream = G_OUTPUT_STREAM (source);
+ GError *error = NULL;
+ gssize written;
+
+ written = g_output_stream_splice_finish (stream, result, &error);
+ if (written < 0)
+ {
+ gdk_content_deserializer_return_error (deserializer, error);
+ return;
+ }
+ else if (written == 0)
+ {
+ GdkRGBA black = GDK_RGBA ("000");
+
+ /* Never return NULL, we only return that on error */
+ g_value_set_boxed (gdk_content_deserializer_get_value (deserializer), &black);
+ }
+ else
+ {
+ guint16 *data;
+ GdkRGBA rgba;
+
+ data = (guint16 *)g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (stream));
+ rgba.red = data[0] / 65535.0;
+ rgba.green = data[1] / 65535.0;
+ rgba.blue = data[2] / 65535.0;
+ rgba.alpha = data[3] / 65535.0;
+
+ g_value_set_boxed (gdk_content_deserializer_get_value (deserializer), &rgba);
+ }
+ gdk_content_deserializer_return_success (deserializer);
+}
+
+static void
+color_deserializer (GdkContentDeserializer *deserializer)
+{
+ GOutputStream *output;
+ guint16 *data;
+
+ data = g_new0 (guint16, 4);
+ output = g_memory_output_stream_new (data, 4 * sizeof (guint16), NULL, g_free);
+
+ g_output_stream_splice_async (output,
+ gdk_content_deserializer_get_input_stream (deserializer),
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ gdk_content_deserializer_get_priority (deserializer),
+ gdk_content_deserializer_get_cancellable (deserializer),
+ color_deserializer_finish,
+ deserializer);
+ g_object_unref (output);
+}
+
+static void
init (void)
{
static gboolean initialized = FALSE;
@@ -956,5 +1014,11 @@ init (void)
string_deserializer,
(gpointer) "ASCII",
NULL);
+
+ gdk_content_register_deserializer ("application/x-color",
+ GDK_TYPE_RGBA,
+ color_deserializer,
+ NULL,
+ NULL);
}
diff --git a/gdk/gdkcontentproviderimpl.c b/gdk/gdkcontentproviderimpl.c
index e7b70c28ab..ebea1356a3 100644
--- a/gdk/gdkcontentproviderimpl.c
+++ b/gdk/gdkcontentproviderimpl.c
@@ -21,6 +21,7 @@
#include "gdkcontentprovider.h"
#include "gdkcontentformats.h"
+#include "gdkcontentserializer.h"
#include "gdkintl.h"
#include "gdkcontentproviderimpl.h"
@@ -280,3 +281,232 @@ gdk_content_provider_new_for_bytes (const char *mime_type,
return GDK_CONTENT_PROVIDER (content);
}
+
+#define GDK_TYPE_CONTENT_PROVIDER_CALLBACK (gdk_content_provider_callback_get_type ())
+#define GDK_CONTENT_PROVIDER_CALLBACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_CALLBACK, GdkContentProviderCallback))
+
+typedef struct _GdkContentProviderCallback GdkContentProviderCallback;
+typedef struct _GdkContentProviderCallbackClass GdkContentProviderCallbackClass;
+
+struct _GdkContentProviderCallback
+{
+ GdkContentProvider parent;
+
+ GType type;
+ GdkContentProviderGetValueFunc func;
+ gpointer data;
+};
+
+struct _GdkContentProviderCallbackClass
+{
+ GdkContentProviderClass parent_class;
+};
+
+GType gdk_content_provider_callback_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (GdkContentProviderCallback, gdk_content_provider_callback, GDK_TYPE_CONTENT_PROVIDER)
+
+static GdkContentFormats *
+gdk_content_provider_callback_ref_formats (GdkContentProvider *provider)
+{
+ GdkContentProviderCallback *callback = GDK_CONTENT_PROVIDER_CALLBACK (provider);
+
+ return gdk_content_formats_new_for_gtype (callback->type);
+}
+
+static gboolean
+gdk_content_provider_callback_get_value (GdkContentProvider *provider,
+ GValue *value,
+ GError **error)
+{
+ GdkContentProviderCallback *callback = GDK_CONTENT_PROVIDER_CALLBACK (provider);
+
+ if (G_VALUE_HOLDS (value, callback->type) && callback->func != NULL)
+ {
+ callback->func (value, callback->data);
+ return TRUE;
+ }
+
+ return GDK_CONTENT_PROVIDER_CLASS (gdk_content_provider_callback_parent_class)->get_value (provider, value, error);
+}
+
+static void
+gdk_content_provider_callback_class_init (GdkContentProviderCallbackClass *class)
+{
+ GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
+
+ provider_class->ref_formats = gdk_content_provider_callback_ref_formats;
+ provider_class->get_value = gdk_content_provider_callback_get_value;
+}
+
+static void
+gdk_content_provider_callback_init (GdkContentProviderCallback *content)
+{
+}
+
+/**
+ * gdk_content_provider_new_for_callback:
+ * @type: the type that the callback provides
+ * @func: callback to populate a #GValue
+ * @data: data that gets passed to @func
+ *
+ * Create a content provider that provides data that is provided via a callback.
+ *
+ * Returns: a new #GdkContentProvider
+ **/
+GdkContentProvider *
+gdk_content_provider_new_with_callback (GType type,
+ GdkContentProviderGetValueFunc func,
+ gpointer data)
+{
+ GdkContentProviderCallback *content;
+
+ content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_CALLBACK, NULL);
+ content->type = type;
+ content->func = func;
+ content->data = data;
+
+ return GDK_CONTENT_PROVIDER (content);
+}
+
+#define GDK_TYPE_CONTENT_PROVIDER_CALLBACK2 (gdk_content_provider_callback2_get_type ())
+#define GDK_CONTENT_PROVIDER_CALLBACK2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CONTENT_PROVIDER_CALLBACK2, GdkContentProviderCallback2))
+
+typedef struct _GdkContentProviderCallback2 GdkContentProviderCallback2;
+typedef struct _GdkContentProviderCallback2Class GdkContentProviderCallback2Class;
+
+struct _GdkContentProviderCallback2
+{
+ GdkContentProvider parent;
+
+ GdkContentFormats *formats;
+ GdkContentProviderGetBytesFunc func;
+ gpointer data;
+};
+
+struct _GdkContentProviderCallback2Class
+{
+ GdkContentProviderClass parent_class;
+};
+
+GType gdk_content_provider_callback2_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (GdkContentProviderCallback2, gdk_content_provider_callback2, GDK_TYPE_CONTENT_PROVIDER)
+
+static GdkContentFormats *
+gdk_content_provider_callback2_ref_formats (GdkContentProvider *provider)
+{
+ GdkContentProviderCallback2 *callback = GDK_CONTENT_PROVIDER_CALLBACK2 (provider);
+
+ return gdk_content_formats_ref (callback->formats);
+}
+
+static void
+gdk_content_provider_callback2_write_mime_type_done (GObject *stream,
+ GAsyncResult *result,
+ gpointer task)
+{
+ GError *error = NULL;
+
+ if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, NULL, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+gdk_content_provider_callback2_write_mime_type_async (GdkContentProvider *provider,
+ const char *mime_type,
+ GOutputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GdkContentProviderCallback2 *content = GDK_CONTENT_PROVIDER_CALLBACK2 (provider);
+ GTask *task;
+ GBytes *bytes;
+
+ task = g_task_new (content, cancellable, callback, user_data);
+ g_task_set_priority (task, io_priority);
+ g_task_set_source_tag (task, gdk_content_provider_callback2_write_mime_type_async);
+
+ if (!gdk_content_formats_contain_mime_type (content->formats, mime_type))
+ {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Cannot provide contents as “%s”"), mime_type);
+ g_object_unref (task);
+ return;
+ }
+
+ bytes = content->func (mime_type, content->data);
+ if (!bytes)
+ {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Failed to get contents as “%s”"), mime_type);
+ g_object_unref (task);
+ return;
+ }
+
+ g_object_set_data_full (G_OBJECT (task), "bytes", bytes, (GDestroyNotify)g_bytes_unref);
+
+ g_output_stream_write_all_async (stream,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ io_priority,
+ cancellable,
+ gdk_content_provider_callback2_write_mime_type_done,
+ task);
+}
+
+static gboolean
+gdk_content_provider_callback2_write_mime_type_finish (GdkContentProvider *provider,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, provider), FALSE);
+ g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_content_provider_callback2_write_mime_type_async, FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+gdk_content_provider_callback2_class_init (GdkContentProviderCallback2Class *class)
+{
+ GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
+
+ provider_class->ref_formats = gdk_content_provider_callback2_ref_formats;
+ provider_class->write_mime_type_async = gdk_content_provider_callback2_write_mime_type_async;
+ provider_class->write_mime_type_finish = gdk_content_provider_callback2_write_mime_type_finish;
+}
+
+static void
+gdk_content_provider_callback2_init (GdkContentProviderCallback2 *content)
+{
+}
+
+/**
+ * gdk_content_provider_new_with_formats:
+ * @formats: formats to advertise
+ * @func: callback to populate a #GValue
+ * @data: data that gets passed to @func
+ *
+ * Create a content provider that provides data that is provided via a callback.
+ *
+ * Returns: a new #GdkContentProvider
+ **/
+GdkContentProvider *
+gdk_content_provider_new_with_formats (GdkContentFormats *formats,
+ GdkContentProviderGetBytesFunc func,
+ gpointer data)
+{
+ GdkContentProviderCallback2 *content;
+ content = g_object_new (GDK_TYPE_CONTENT_PROVIDER_CALLBACK2, NULL);
+ content->formats = gdk_content_formats_union_serialize_mime_types (gdk_content_formats_ref (formats));
+ content->func = func;
+ content->data = data;
+
+ return GDK_CONTENT_PROVIDER (content);
+}
diff --git a/gdk/gdkcontentproviderimpl.h b/gdk/gdkcontentproviderimpl.h
index 1c41360243..19ac6969e1 100644
--- a/gdk/gdkcontentproviderimpl.h
+++ b/gdk/gdkcontentproviderimpl.h
@@ -35,6 +35,21 @@ GDK_AVAILABLE_IN_ALL
GdkContentProvider * gdk_content_provider_new_for_bytes (const char *mime_type,
GBytes *bytes);
+typedef void (*GdkContentProviderGetValueFunc) (GValue *value,
+ gpointer data);
+
+GDK_AVAILABLE_IN_ALL
+GdkContentProvider * gdk_content_provider_new_with_callback (GType type,
+ GdkContentProviderGetValueFunc func,
+ gpointer data);
+
+typedef GBytes * (*GdkContentProviderGetBytesFunc) (const char *mime_type,
+ gpointer data);
+
+GDK_AVAILABLE_IN_ALL
+GdkContentProvider * gdk_content_provider_new_with_formats (GdkContentFormats *formats,
+ GdkContentProviderGetBytesFunc func,
+ gpointer data);
G_END_DECLS
diff --git a/gdk/gdkcontentserializer.c b/gdk/gdkcontentserializer.c
index b78818a75f..6caea1edef 100644
--- a/gdk/gdkcontentserializer.c
+++ b/gdk/gdkcontentserializer.c
@@ -25,6 +25,7 @@
#include "gdkpixbuf.h"
#include "filetransferportalprivate.h"
#include "gdktextureprivate.h"
+#include "gdkrgba.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <string.h>
@@ -863,6 +864,48 @@ file_text_serializer (GdkContentSerializer *serializer)
}
static void
+color_serializer_finish (GObject *source,
+ GAsyncResult *result,
+ gpointer serializer)
+{
+ GOutputStream *stream = G_OUTPUT_STREAM (source);
+ GError *error = NULL;
+
+ if (!g_output_stream_write_all_finish (stream, result, NULL, &error))
+ gdk_content_serializer_return_error (serializer, error);
+ else
+ gdk_content_serializer_return_success (serializer);
+}
+
+static void
+color_serializer (GdkContentSerializer *serializer)
+{
+ const GValue *value;
+ GdkRGBA *rgba;
+ guint16 *data;
+
+ value = gdk_content_serializer_get_value (serializer);
+ rgba = g_value_get_boxed (value);
+ data = g_new0 (guint16, 4);
+ if (rgba)
+ {
+ data[0] = (guint16) (rgba->red * 65535);
+ data[1] = (guint16) (rgba->green * 65535);
+ data[2] = (guint16) (rgba->blue * 65535);
+ data[3] = (guint16) (rgba->alpha * 65535);
+ }
+
+ g_output_stream_write_all_async (gdk_content_serializer_get_output_stream (serializer),
+ data,
+ 4 * sizeof (guint16),
+ gdk_content_serializer_get_priority (serializer),
+ gdk_content_serializer_get_cancellable (serializer),
+ color_serializer_finish,
+ serializer);
+ gdk_content_serializer_set_task_data (serializer, data, g_free);
+}
+
+static void
init (void)
{
static gboolean initialized = FALSE;
@@ -984,5 +1027,11 @@ init (void)
string_serializer,
(gpointer) "ASCII",
NULL);
+
+ gdk_content_register_serializer (GDK_TYPE_RGBA,
+ "application/x-color",
+ color_serializer,
+ NULL,
+ NULL);
}
diff --git a/gdk/gdkdrag.c b/gdk/gdkdrag.c
index acf7a7bede..7dfc79e6e0 100644
--- a/gdk/gdkdrag.c
+++ b/gdk/gdkdrag.c
@@ -90,20 +90,30 @@ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkDrag, gdk_drag, G_TYPE_OBJECT)
/**
* SECTION:dnd
- * @title: Drag And Drop
- * @short_description: Functions for controlling drag and drop handling
+ * @Title: Drag And Drop
+ * @Short_description: Functions for controlling drag and drop handling
*
- * These functions provide a low level interface for drag and drop.
+ * These functions provide a low-level interface for drag and drop.
*
* The GdkDrag object represents the source side of an ongoing DND operation.
* It is created when a drag is started, and stays alive for duration of
- * the DND operation.
+ * the DND operation. After a drag has been started with gdk_drag_begin(),
+ * the caller gets informed about the status of the ongoing drag operation
+ * with signals on the #GtkDrag object.
*
* The GdkDrop object represents the target side of an ongoing DND operation.
+ * Possible drop sites get informed about the status of the ongoing drag operation
+ * with events of type %GDK_DRAG_ENTER, %GDK_DRAG_LEAVE, %GDK_DRAG_MOTION and
+ * %GDK_DROP_START. The #GdkDrop object can be obtained from these #GdkEvents
+ * using gdk_event_get_drop().
+ *
+ * The actual data transfer is initiated from the target side via an async
+ * read, using one of the GdkDrop functions for this purpose: gdk_drop_read_async(),
+ * gdk_drop_read_value_async() or gdk_drop_read_text_async().
*
- * GTK+ provides a higher level abstraction based on top of these functions,
- * and so they are not normally needed in GTK+ applications. See the
- * [Drag and Drop][gtk4-Drag-and-Drop] section of the GTK+ documentation
+ * GTK provides a higher level abstraction based on top of these functions,
+ * and so they are not normally needed in GTK applications. See the
+ * [Drag and Drop][gtk4-Drag-and-Drop] section of the GTK documentation
* for more information.
*/
diff --git a/gdk/gdkdrop.c b/gdk/gdkdrop.c
index eb04ca26ad..d82e90df2c 100644
--- a/gdk/gdkdrop.c
+++ b/gdk/gdkdrop.c
@@ -1001,3 +1001,30 @@ gdk_drop_emit_drop_event (GdkDrop *self,
gdk_drop_do_emit_event (event, dont_queue);
}
+/**
+ * gdk_drop_has_value:
+ * @drop: a #GdkDrop
+ * @type: the type to check
+ *
+ * Returns whether calling gdk_drop_read_value_async() for @type
+ * can succeed.
+ *
+ * Returns: %TRUE if the data can be deserialized to the given type
+ */
+gboolean
+gdk_drop_has_value (GdkDrop *drop,
+ GType type)
+{
+ GdkContentFormats *formats;
+ gboolean ret;
+
+ formats = gdk_content_formats_ref (gdk_drop_get_formats (drop));
+ formats = gdk_content_formats_union_deserialize_gtypes (formats);
+
+ ret = gdk_content_formats_contain_gtype (formats, type);
+
+ gdk_content_formats_unref (formats);
+
+ return ret;
+}
+
diff --git a/gdk/gdkdrop.h b/gdk/gdkdrop.h
index db5acd13e9..6e29864293 100644
--- a/gdk/gdkdrop.h
+++ b/gdk/gdkdrop.h
@@ -92,6 +92,9 @@ char * gdk_drop_read_text_finish (GdkDrop
GAsyncResult *result,
GError **error);
+GDK_AVAILABLE_IN_ALL
+gboolean gdk_drop_has_value (GdkDrop *self,
+ GType type);
G_END_DECLS
diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
index 27c7501bd3..aec67fcec6 100644
--- a/gdk/gdkevents.c
+++ b/gdk/gdkevents.c
@@ -1013,6 +1013,13 @@ gdk_event_set_coords (GdkEvent *event,
event->touchpad_pinch.x = x;
event->touchpad_pinch.y = y;
break;
+ case GDK_DRAG_ENTER:
+ case GDK_DRAG_LEAVE:
+ case GDK_DRAG_MOTION:
+ case GDK_DROP_START:
+ event->dnd.x = x;
+ event->dnd.y = y;
+ break;
default:
break;
}
diff --git a/gdk/gdksurface.c b/gdk/gdksurface.c
index 7158c253f5..8424576238 100644
--- a/gdk/gdksurface.c
+++ b/gdk/gdksurface.c
@@ -3583,7 +3583,13 @@ gdk_surface_register_dnd (GdkSurface *surface)
*
* Starts a drag and creates a new drag context for it.
*
- * This function is called by the drag source.
+ * This function is called by the drag source. After this call, you
+ * probably want to set up the drag icon using the surface returned
+ * by gdk_drag_get_drag_surface().
+ *
+ * Note: if @actions include %GDK_ACTION_MOVE, you need to listen for
+ * the #GdkDrag::dnd-finished signal and delete the data at the source
+ * if gdk_drag_get_selected_action() returns %GDK_ACTION_MOVE.
*
* Returns: (transfer full) (nullable): a newly created #GdkDrag or
* %NULL on error.
diff --git a/gdk/x11/gdkdrop-x11.c b/gdk/x11/gdkdrop-x11.c
index b0d9457b3b..bbf377f898 100644
--- a/gdk/x11/gdkdrop-x11.c
+++ b/gdk/x11/gdkdrop-x11.c
@@ -747,7 +747,7 @@ gdk_x11_drop_status (GdkDrop *drop,
GdkDragAction actions)
{
GdkX11Drop *drop_x11 = GDK_X11_DROP (drop);
- GdkDragAction possible_actions;
+ GdkDragAction possible_actions, suggested_action;
XEvent xev;
GdkDisplay *display;
@@ -755,6 +755,17 @@ gdk_x11_drop_status (GdkDrop *drop,
possible_actions = actions & gdk_drop_get_actions (drop);
+ if (drop_x11->suggested_action != 0)
+ suggested_action = drop_x11->suggested_action;
+ else if (possible_actions & GDK_ACTION_COPY)
+ suggested_action = GDK_ACTION_COPY;
+ else if (possible_actions & GDK_ACTION_MOVE)
+ suggested_action = GDK_ACTION_MOVE;
+ else if (possible_actions & GDK_ACTION_ASK)
+ suggested_action = GDK_ACTION_ASK;
+ else
+ suggested_action = 0;
+
xev.xclient.type = ClientMessage;
xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus");
xev.xclient.format = 32;
@@ -764,7 +775,7 @@ gdk_x11_drop_status (GdkDrop *drop,
xev.xclient.data.l[1] = (possible_actions != 0) ? (2 | 1) : 0;
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 0;
- xev.xclient.data.l[4] = xdnd_action_to_atom (display, possible_actions);
+ xev.xclient.data.l[4] = xdnd_action_to_atom (display, suggested_action);
if (gdk_drop_get_drag (drop))
{
diff --git a/gtk/gtk.h b/gtk/gtk.h
index a64a2a826a..f6f2030833 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -90,8 +90,8 @@
#include <gtk/gtkcustomlayout.h>
#include <gtk/gtkdebug.h>
#include <gtk/gtkdialog.h>
-#include <gtk/gtkdnd.h>
#include <gtk/gtkdragdest.h>
+#include <gtk/gtkdragicon.h>
#include <gtk/gtkdragsource.h>
#include <gtk/gtkdrawingarea.h>
#include <gtk/gtkeditable.h>
diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c
index c1e96c0028..e3862a3bfc 100644
--- a/gtk/gtkcalendar.c
+++ b/gtk/gtkcalendar.c
@@ -72,7 +72,6 @@
#endif
#include "gtkcalendar.h"
-#include "gtkdnd.h"
#include "gtkdragdest.h"
#include "gtkintl.h"
#include "gtkmain.h"
@@ -87,6 +86,10 @@
#include "gtkgesturedrag.h"
#include "gtkeventcontrollerscroll.h"
#include "gtkeventcontrollerkey.h"
+#include "gtkdragsource.h"
+#include "gtknative.h"
+#include "gtkicontheme.h"
+#include "gtkdragicon.h"
#define TIMEOUT_INITIAL 500
#define TIMEOUT_REPEAT 50
@@ -248,7 +251,6 @@ struct _GtkCalendarPrivate
guint need_timer : 1;
guint in_drag : 1;
- guint drag_highlight : 1;
guint32 timer;
gint click_child;
@@ -329,22 +331,17 @@ static gboolean gtk_calendar_query_tooltip (GtkWidget *widget,
gboolean keyboard_mode,
GtkTooltip *tooltip);
-static void gtk_calendar_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data);
-static void gtk_calendar_drag_data_received (GtkWidget *widget,
+static gboolean gtk_calendar_drag_accept (GtkDropTarget *dest,
GdkDrop *drop,
- GtkSelectionData *selection_data);
-static gboolean gtk_calendar_drag_motion (GtkWidget *widget,
+ GtkCalendar *calendar);
+static void gtk_calendar_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static void gtk_calendar_drag_leave (GtkWidget *widget,
- GdkDrop *drop);
-static gboolean gtk_calendar_drag_drop (GtkWidget *widget,
+ GtkCalendar *calendar);
+static gboolean gtk_calendar_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
+ int x,
+ int y,
+ GtkCalendar *calendar);
static void calendar_start_spinning (GtkCalendar *calendar,
@@ -392,12 +389,6 @@ gtk_calendar_class_init (GtkCalendarClass *class)
widget_class->grab_notify = gtk_calendar_grab_notify;
widget_class->query_tooltip = gtk_calendar_query_tooltip;
- widget_class->drag_data_get = gtk_calendar_drag_data_get;
- widget_class->drag_motion = gtk_calendar_drag_motion;
- widget_class->drag_leave = gtk_calendar_drag_leave;
- widget_class->drag_drop = gtk_calendar_drag_drop;
- widget_class->drag_data_received = gtk_calendar_drag_data_received;
-
/**
* GtkCalendar:year:
*
@@ -675,6 +666,8 @@ gtk_calendar_init (GtkCalendar *calendar)
#else
gchar *week_start;
#endif
+ GdkContentFormats *formats;
+ GtkDropTarget *dest;
gtk_widget_set_can_focus (widget, TRUE);
@@ -792,11 +785,17 @@ gtk_calendar_init (GtkCalendar *calendar)
priv->click_child = -1;
priv->in_drag = 0;
- priv->drag_highlight = 0;
- gtk_drag_dest_set (widget, 0, NULL, GDK_ACTION_COPY);
- gtk_drag_dest_add_text_targets (widget);
+ formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
+ dest = gtk_drop_target_new (formats, GDK_ACTION_COPY);
+ gdk_content_formats_unref (formats);
+
+ g_signal_connect (dest, "accept", G_CALLBACK (gtk_calendar_drag_accept), calendar);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_calendar_drag_leave), calendar);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_calendar_drag_drop), calendar);
+ gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (dest));
+
priv->year_before = 0;
/* Translate to calendar:YM if you want years to be displayed
@@ -2661,6 +2660,27 @@ gtk_calendar_drag_begin (GtkGestureDrag *gesture,
priv->in_drag = TRUE;
}
+static GdkContentProvider *
+get_calendar_content (GtkCalendar *calendar)
+{
+ GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar);
+ GDate *date;
+ gchar str[128];
+ GValue value = G_VALUE_INIT;
+ GdkContentProvider *content;
+
+ date = g_date_new_dmy (priv->selected_day, priv->month + 1, priv->year);
+ g_date_strftime (str, 127, "%x", date);
+ g_free (date);
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, str);
+ content = gdk_content_provider_new_for_value (&value);
+ g_value_unset (&value);
+
+ return content;
+}
+
static void
gtk_calendar_drag_update (GtkGestureDrag *gesture,
double x,
@@ -2671,8 +2691,12 @@ gtk_calendar_drag_update (GtkGestureDrag *gesture,
GtkCalendar *calendar = GTK_CALENDAR (widget);
GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar);
gdouble start_x, start_y;
+ GdkContentProvider *content;
+ GdkDevice *device;
GdkDrag *drag;
- GdkContentFormats *targets;
+ GtkIconTheme *theme;
+ GdkPaintable *paintable;
+ GdkSurface *surface;
if (!priv->in_drag)
return;
@@ -2682,19 +2706,22 @@ gtk_calendar_drag_update (GtkGestureDrag *gesture,
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
- gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
+ surface = gtk_native_get_surface (gtk_widget_get_native (widget));
+ device = gtk_gesture_get_device (GTK_GESTURE (gesture));
- targets = gdk_content_formats_new (NULL, 0);
- targets = gtk_content_formats_add_text_targets (targets);
- drag = gtk_drag_begin (widget,
- gtk_gesture_get_device (GTK_GESTURE (gesture)),
- targets, GDK_ACTION_COPY,
- start_x, start_y);
+ content = get_calendar_content (calendar);
- priv->in_drag = 0;
- gdk_content_formats_unref (targets);
+ drag = gdk_drag_begin (surface, device, content, GDK_ACTION_COPY, start_x, start_y);
+
+ theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (widget));
+ paintable = gtk_icon_theme_load_icon (theme, "text-x-generic", 32, 0, NULL);
+ gtk_drag_icon_set_from_paintable (drag, paintable, 0, 0);
+ g_clear_object (&paintable);
+
+ g_object_unref (content);
+ g_object_unref (drag);
- gtk_drag_set_icon_default (drag);
+ priv->in_drag = 0;
}
static gboolean
@@ -2914,24 +2941,6 @@ gtk_calendar_grab_notify (GtkWidget *widget,
* Drag and Drop *
****************************************/
-static void
-gtk_calendar_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data)
-{
- GtkCalendar *calendar = GTK_CALENDAR (widget);
- GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar);
- GDate *date;
- gchar str[128];
- gsize len;
-
- date = g_date_new_dmy (priv->selected_day, priv->month + 1, priv->year);
- len = g_date_strftime (str, 127, "%x", date);
- gtk_selection_data_set_text (selection_data, str, len);
-
- g_free (date);
-}
-
/* Get/set whether drag_motion requested the drag data and
* drag_data_received should thus not actually insert the data,
* since the data doesn’t result from a drop.
@@ -2953,87 +2962,33 @@ get_status_pending (GdkDrop *drop)
}
static void
-gtk_calendar_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
-{
- GtkCalendar *calendar = GTK_CALENDAR (widget);
- GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar);
-
- priv->drag_highlight = 0;
- gtk_drag_unhighlight (widget);
-}
-
-static gboolean
-gtk_calendar_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
-{
- GtkCalendar *calendar = GTK_CALENDAR (widget);
- GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar);
- GdkAtom target;
-
- if (!priv->drag_highlight)
- {
- priv->drag_highlight = 1;
- gtk_drag_highlight (widget);
- }
-
- target = gtk_drag_dest_find_target (widget, drop, NULL);
- if (target == NULL || gdk_drop_get_actions (drop) == 0)
- gdk_drop_status (drop, 0);
- else if (get_status_pending (drop) == 0)
- {
- set_status_pending (drop, gdk_drop_get_actions (drop));
- gtk_drag_get_data (widget, drop, target);
- }
-
- return TRUE;
-}
-
-static gboolean
-gtk_calendar_drag_drop (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+gtk_calendar_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkCalendar *calendar)
{
- GdkAtom target;
-
- target = gtk_drag_dest_find_target (widget, drop, NULL);
- if (target != NULL)
- {
- gtk_drag_get_data (widget, drop, target);
- return TRUE;
- }
-
- return FALSE;
}
static void
-gtk_calendar_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data)
+got_text (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
- GtkCalendar *calendar = GTK_CALENDAR (widget);
+ GtkDropTarget *dest = GTK_DROP_TARGET (data);
+ GtkCalendar *calendar = GTK_CALENDAR (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest)));
GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar);
+ GdkDrop *drop = GDK_DROP (source);
guint day, month, year;
gchar *str;
GDate *date;
GdkDragAction suggested_action;
suggested_action = get_status_pending (drop);
+ set_status_pending (drop, 0);
+
+ str = gdk_drop_read_text_finish (drop, result, NULL);
if (suggested_action)
{
- set_status_pending (drop, 0);
-
- /* We are getting this data due to a request in drag_motion,
- * rather than due to a request in drag_drop, so we are just
- * supposed to call drag_status, not actually paste in the
- * data.
- */
- str = (gchar*) gtk_selection_data_get_text (selection_data);
-
if (str)
{
date = g_date_new ();
@@ -3045,14 +3000,13 @@ gtk_calendar_drag_data_received (GtkWidget *widget,
}
else
suggested_action = 0;
-
gdk_drop_status (drop, suggested_action);
-
+ if (suggested_action == 0)
+ gtk_drop_target_deny_drop (dest, drop);
return;
}
date = g_date_new ();
- str = (gchar*) gtk_selection_data_get_text (selection_data);
if (str)
{
g_date_set_parse (date, str);
@@ -3064,6 +3018,7 @@ gtk_calendar_drag_data_received (GtkWidget *widget,
g_warning ("Received invalid date data");
g_date_free (date);
gdk_drop_finish (drop, 0);
+ gtk_drop_target_deny_drop (dest, drop);
return;
}
@@ -3074,7 +3029,6 @@ gtk_calendar_drag_data_received (GtkWidget *widget,
gdk_drop_finish (drop, suggested_action);
-
g_object_freeze_notify (G_OBJECT (calendar));
if (!(priv->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
&& (priv->display_flags & GTK_CALENDAR_SHOW_HEADING))
@@ -3083,6 +3037,47 @@ gtk_calendar_drag_data_received (GtkWidget *widget,
g_object_thaw_notify (G_OBJECT (calendar));
}
+static gboolean
+gtk_calendar_drag_accept (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkCalendar *calendar)
+{
+ GdkAtom target;
+
+ target = gtk_drop_target_find_mimetype (dest);
+ if (!target || gdk_drop_get_actions (drop) == 0)
+ {
+ gdk_drop_status (drop, 0);
+ return FALSE;
+ }
+ else if (get_status_pending (drop) == 0)
+ {
+ set_status_pending (drop, gdk_drop_get_actions (drop));
+ gdk_drop_read_text_async (drop, NULL, got_text, dest);
+ }
+ return TRUE;
+}
+
+static gboolean
+gtk_calendar_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkCalendar *calendar)
+{
+ GdkAtom target;
+
+ target = gtk_drop_target_find_mimetype (dest);
+ if (target != NULL)
+ {
+ set_status_pending (drop, 0);
+ gdk_drop_read_text_async (drop, NULL, got_text, dest);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/****************************************
* Public API *
diff --git a/gtk/gtkcolorbutton.c b/gtk/gtkcolorbutton.c
index e5471ce452..9369e350c0 100644
--- a/gtk/gtkcolorbutton.c
+++ b/gtk/gtkcolorbutton.c
@@ -36,7 +36,6 @@
#include "gtkcolorchooserprivate.h"
#include "gtkcolorchooserdialog.h"
#include "gtkcolorswatchprivate.h"
-#include "gtkdnd.h"
#include "gtkdragdest.h"
#include "gtkdragsource.h"
#include "gtkintl.h"
@@ -45,6 +44,9 @@
#include "gtkprivate.h"
#include "gtksnapshot.h"
#include "gtkstylecontext.h"
+#include "gtkdragsource.h"
+#include "gtkdragdest.h"
+#include "gtkeventcontroller.h"
/**
@@ -121,21 +123,6 @@ static void gtk_color_button_get_property (GObject *object,
static void gtk_color_button_clicked (GtkButton *button,
gpointer user_data);
-/* source side drag signals */
-static void gtk_color_button_drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data);
-static void gtk_color_button_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- GtkColorButton *button);
-
-/* target side drag signals */
-static void gtk_color_button_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data,
- GtkColorButton *button);
-
static guint color_button_signals[LAST_SIGNAL] = { 0 };
@@ -245,91 +232,64 @@ gtk_color_button_class_init (GtkColorButtonClass *klass)
}
static void
-gtk_color_button_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data,
- GtkColorButton *button)
+got_color (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
- GtkColorButtonPrivate *priv = gtk_color_button_get_instance_private (button);
- gint length;
- guint16 *dropped;
+ GdkDrop *drop = GDK_DROP (source);
+ const GValue *value;
- length = gtk_selection_data_get_length (selection_data);
-
- if (length < 0)
- return;
-
- /* We accept drops with the wrong format, since the KDE color
- * chooser incorrectly drops application/x-color with format 8.
- */
- if (length != 8)
+ value = gdk_drop_read_value_finish (drop, result, NULL);
+ if (value)
{
- g_warning ("%s: Received invalid color data", G_STRFUNC);
- return;
+ GdkRGBA *color = g_value_get_boxed (value);
+ gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (data), color);
+ gdk_drop_finish (drop, GDK_ACTION_COPY);
}
+ else
+ gdk_drop_finish (drop, 0);
+}
+static gboolean
+gtk_color_button_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkColorButton *button)
+{
+ if (gdk_drop_has_value (drop, GDK_TYPE_RGBA))
+ {
+ gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, button);
+ return TRUE;
+ }
- dropped = (guint16 *) gtk_selection_data_get_data (selection_data);
-
- priv->rgba.red = dropped[0] / 65535.;
- priv->rgba.green = dropped[1] / 65535.;
- priv->rgba.blue = dropped[2] / 65535.;
- priv->rgba.alpha = dropped[3] / 65535.;
-
- gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (priv->swatch), &priv->rgba);
-
- g_signal_emit (button, color_button_signals[COLOR_SET], 0);
-
- g_object_freeze_notify (G_OBJECT (button));
- g_object_notify (G_OBJECT (button), "rgba");
- g_object_thaw_notify (G_OBJECT (button));
+ return FALSE;
}
static void
-set_color_icon (GdkDrag *drag,
- const GdkRGBA *rgba)
+gtk_color_button_drag_begin (GtkDragSource *source,
+ GdkDrag *drag,
+ GtkColorButton *button)
{
+ GtkColorButtonPrivate *priv = gtk_color_button_get_instance_private (button);
GtkSnapshot *snapshot;
GdkPaintable *paintable;
snapshot = gtk_snapshot_new ();
- gtk_snapshot_append_color (snapshot,
- rgba,
- &GRAPHENE_RECT_INIT(0, 0, 48, 32));
+ gtk_snapshot_append_color (snapshot, &priv->rgba, &GRAPHENE_RECT_INIT(0, 0, 48, 32));
paintable = gtk_snapshot_free_to_paintable (snapshot, NULL);
- gtk_drag_set_icon_paintable (drag, paintable, 0, 0);
- g_object_unref (paintable);
-}
+ gtk_drag_source_set_icon (source, paintable, 0, 0);
-static void
-gtk_color_button_drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
-{
- GtkColorButton *button = data;
- GtkColorButtonPrivate *priv = gtk_color_button_get_instance_private (button);
-
- set_color_icon (drag, &priv->rgba);
+ g_object_unref (paintable);
}
static void
-gtk_color_button_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- GtkColorButton *button)
+get_rgba_value (GValue *value,
+ gpointer data)
{
- GtkColorButtonPrivate *priv = gtk_color_button_get_instance_private (button);
- guint16 dropped[4];
-
- dropped[0] = (guint16) (priv->rgba.red * 65535);
- dropped[1] = (guint16) (priv->rgba.green * 65535);
- dropped[2] = (guint16) (priv->rgba.blue * 65535);
- dropped[3] = (guint16) (priv->rgba.alpha * 65535);
-
- gtk_selection_data_set (selection_data,
- gtk_selection_data_get_target (selection_data),
- 16, (guchar *)dropped, 8);
+ GtkColorButtonPrivate *priv = gtk_color_button_get_instance_private (GTK_COLOR_BUTTON (data));
+ g_value_set_boxed (value, &priv->rgba);
}
static void
@@ -340,6 +300,9 @@ gtk_color_button_init (GtkColorButton *button)
PangoRectangle rect;
GtkStyleContext *context;
GdkContentFormats *targets;
+ GdkContentProvider *content;
+ GtkDragSource *source;
+ GtkDropTarget *dest;
priv->button = gtk_button_new ();
g_signal_connect (priv->button, "clicked", G_CALLBACK (gtk_color_button_clicked), button);
@@ -364,23 +327,17 @@ gtk_color_button_init (GtkColorButton *button)
priv->use_alpha = FALSE;
targets = gdk_content_formats_new (drop_types, G_N_ELEMENTS (drop_types));
- gtk_drag_dest_set (priv->button,
- GTK_DEST_DEFAULT_MOTION |
- GTK_DEST_DEFAULT_HIGHLIGHT |
- GTK_DEST_DEFAULT_DROP,
- targets,
- GDK_ACTION_COPY);
- gtk_drag_source_set (priv->button,
- GDK_BUTTON1_MASK|GDK_BUTTON3_MASK,
- targets,
- GDK_ACTION_COPY);
+ dest = gtk_drop_target_new (targets, GDK_ACTION_COPY);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_color_button_drag_drop), button);
+ gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (dest));
gdk_content_formats_unref (targets);
- g_signal_connect (priv->button, "drag-begin",
- G_CALLBACK (gtk_color_button_drag_begin), button);
- g_signal_connect (priv->button, "drag-data-received",
- G_CALLBACK (gtk_color_button_drag_data_received), button);
- g_signal_connect (priv->button, "drag-data-get",
- G_CALLBACK (gtk_color_button_drag_data_get), button);
+
+ source = gtk_drag_source_new ();
+ content = gdk_content_provider_new_with_callback (GDK_TYPE_RGBA, get_rgba_value, button);
+ gtk_drag_source_set_content (source, content);
+ g_object_unref (content);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (gtk_color_button_drag_begin), button);
+ gtk_widget_add_controller (priv->button, GTK_EVENT_CONTROLLER (source));
context = gtk_widget_get_style_context (GTK_WIDGET (priv->button));
gtk_style_context_add_class (context, "color");
diff --git a/gtk/gtkcolorswatch.c b/gtk/gtkcolorswatch.c
index b1eb56160e..af7dcbd6b5 100644
--- a/gtk/gtkcolorswatch.c
+++ b/gtk/gtkcolorswatch.c
@@ -22,7 +22,6 @@
#include "gtkbox.h"
#include "gtkcolorchooserprivate.h"
#include "gtkcssnodeprivate.h"
-#include "gtkdnd.h"
#include "gtkdragdest.h"
#include "gtkdragsource.h"
#include "gtkgesturelongpress.h"
@@ -40,6 +39,7 @@
#include "gtkwidgetprivate.h"
#include "gtkeventcontrollerkey.h"
#include "gtknative.h"
+#include "gtkdragsource.h"
#include "a11y/gtkcolorswatchaccessibleprivate.h"
@@ -66,6 +66,7 @@ typedef struct
GtkWidget *overlay_widget;
GtkWidget *popover;
+ GtkDropTarget *dest;
} GtkColorSwatchPrivate;
enum
@@ -73,7 +74,8 @@ enum
PROP_ZERO,
PROP_RGBA,
PROP_SELECTABLE,
- PROP_HAS_MENU
+ PROP_HAS_MENU,
+ PROP_CAN_DROP
};
G_DEFINE_TYPE_WITH_PRIVATE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_WIDGET)
@@ -113,87 +115,57 @@ swatch_snapshot (GtkWidget *widget,
gtk_widget_snapshot_child (widget, priv->overlay_widget, snapshot);
}
-
static void
-drag_set_color_icon (GdkDrag *drag,
- const GdkRGBA *color)
+gtk_color_swatch_drag_begin (GtkDragSource *source,
+ GdkDrag *drag,
+ GtkColorSwatch *swatch)
{
+ GtkColorSwatchPrivate *priv = gtk_color_swatch_get_instance_private (swatch);
GtkSnapshot *snapshot;
GdkPaintable *paintable;
snapshot = gtk_snapshot_new ();
- gtk_snapshot_append_color (snapshot,
- color,
- &GRAPHENE_RECT_INIT(0, 0, 48, 32));
+ gtk_snapshot_append_color (snapshot, &priv->color, &GRAPHENE_RECT_INIT(0, 0, 48, 32));
paintable = gtk_snapshot_free_to_paintable (snapshot, NULL);
- gtk_drag_set_icon_paintable (drag, paintable, 4, 4);
+ gtk_drag_source_set_icon (source, paintable, 0, 0);
+
g_object_unref (paintable);
}
static void
-swatch_drag_begin (GtkWidget *widget,
- GdkDrag *drag)
+got_color (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
- GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
- GdkRGBA color;
+ GdkDrop *drop = GDK_DROP (source);
+ const GValue *value;
- gtk_color_swatch_get_rgba (swatch, &color);
- drag_set_color_icon (drag, &color);
-}
-
-static void
-swatch_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data)
-{
- GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
- guint16 vals[4];
- GdkRGBA color;
-
- gtk_color_swatch_get_rgba (swatch, &color);
-
- vals[0] = color.red * 0xffff;
- vals[1] = color.green * 0xffff;
- vals[2] = color.blue * 0xffff;
- vals[3] = color.alpha * 0xffff;
-
- gtk_selection_data_set (selection_data,
- g_intern_static_string ("application/x-color"),
- 16, (guchar *)vals, 8);
+ value = gdk_drop_read_value_finish (drop, result, NULL);
+ if (value)
+ {
+ GdkRGBA *color = g_value_get_boxed (value);
+ gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (data), color);
+ gdk_drop_finish (drop, GDK_ACTION_COPY);
+ }
+ else
+ gdk_drop_finish (drop, 0);
}
-static void
-swatch_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data)
+static gboolean
+swatch_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkColorSwatch *swatch)
{
- gint length;
- guint16 *vals;
- GdkRGBA color;
-
- length = gtk_selection_data_get_length (selection_data);
-
- if (length < 0)
- return;
-
- /* We accept drops with the wrong format, since the KDE color
- * chooser incorrectly drops application/x-color with format 8.
- */
- if (length != 8)
+ if (gdk_drop_has_value (drop, GDK_TYPE_RGBA))
{
- g_warning ("Received invalid color data");
- return;
+ gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, swatch);
+ return TRUE;
}
- vals = (guint16 *) gtk_selection_data_get_data (selection_data);
-
- color.red = (gdouble)vals[0] / 0xffff;
- color.green = (gdouble)vals[1] / 0xffff;
- color.blue = (gdouble)vals[2] / 0xffff;
- color.alpha = (gdouble)vals[3] / 0xffff;
-
- gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (widget), &color);
+ return FALSE;
}
static void
@@ -455,6 +427,9 @@ swatch_get_property (GObject *object,
case PROP_HAS_MENU:
g_value_set_boolean (value, priv->has_menu);
break;
+ case PROP_CAN_DROP:
+ g_value_set_boolean (value, priv->dest != NULL);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -481,6 +456,9 @@ swatch_set_property (GObject *object,
case PROP_HAS_MENU:
priv->has_menu = g_value_get_boolean (value);
break;
+ case PROP_CAN_DROP:
+ gtk_color_swatch_set_can_drop (swatch, g_value_get_boolean (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -495,7 +473,7 @@ swatch_finalize (GObject *object)
g_free (priv->icon);
gtk_widget_unparent (priv->overlay_widget);
-
+
G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
}
@@ -523,9 +501,6 @@ gtk_color_swatch_class_init (GtkColorSwatchClass *class)
widget_class->measure = gtk_color_swatch_measure;
widget_class->snapshot = swatch_snapshot;
- widget_class->drag_begin = swatch_drag_begin;
- widget_class->drag_data_get = swatch_drag_data_get;
- widget_class->drag_data_received = swatch_drag_data_received;
widget_class->popup_menu = swatch_popup_menu;
widget_class->size_allocate = swatch_size_allocate;
widget_class->state_flags_changed = swatch_state_flags_changed;
@@ -539,6 +514,9 @@ gtk_color_swatch_class_init (GtkColorSwatchClass *class)
g_object_class_install_property (object_class, PROP_HAS_MENU,
g_param_spec_boolean ("has-menu", P_("Has Menu"), P_("Whether the swatch should offer customization"),
TRUE, GTK_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_CAN_DROP,
+ g_param_spec_boolean ("can-drop", P_("Can Drop"), P_("Whether the swatch should accept drops"),
+ FALSE, GTK_PARAM_READWRITE));
gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COLOR_SWATCH_ACCESSIBLE);
gtk_widget_class_set_css_name (widget_class, I_("colorswatch"));
@@ -600,6 +578,14 @@ static const char *dnd_targets[] = {
"application/x-color"
};
+static void
+get_rgba_value (GValue *value,
+ gpointer data)
+{
+ GtkColorSwatchPrivate *priv = gtk_color_swatch_get_instance_private (GTK_COLOR_SWATCH (data));
+ g_value_set_boxed (value, &priv->color);
+}
+
void
gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
const GdkRGBA *color)
@@ -611,12 +597,16 @@ gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
if (!priv->has_color)
{
- GdkContentFormats *targets = gdk_content_formats_new (dnd_targets, G_N_ELEMENTS (dnd_targets));
- gtk_drag_source_set (GTK_WIDGET (swatch),
- GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
- targets,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
- gdk_content_formats_unref (targets);
+ GdkContentProvider *content;
+ GtkDragSource *source;
+
+ source = gtk_drag_source_new ();
+ content = gdk_content_provider_new_with_callback (GDK_TYPE_RGBA, get_rgba_value, swatch);
+ gtk_drag_source_set_content (source, content);
+ g_object_unref (content);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (gtk_color_swatch_drag_begin), swatch);
+
+ gtk_widget_add_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (source));
}
priv->has_color = TRUE;
@@ -676,21 +666,28 @@ void
gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
gboolean can_drop)
{
- if (can_drop)
+ GtkColorSwatchPrivate *priv = gtk_color_swatch_get_instance_private (swatch);
+
+ if (can_drop == (priv->dest != NULL))
+ return;
+
+ if (can_drop && !priv->dest)
{
- GdkContentFormats *targets = gdk_content_formats_new (dnd_targets, G_N_ELEMENTS (dnd_targets));
- gtk_drag_dest_set (GTK_WIDGET (swatch),
- GTK_DEST_DEFAULT_HIGHLIGHT |
- GTK_DEST_DEFAULT_MOTION |
- GTK_DEST_DEFAULT_DROP,
- targets,
- GDK_ACTION_COPY);
+ GdkContentFormats *targets;
+
+ targets = gdk_content_formats_new (dnd_targets, G_N_ELEMENTS (dnd_targets));
+ priv->dest = gtk_drop_target_new (targets, GDK_ACTION_COPY);
+ g_signal_connect (priv->dest, "drag-drop", G_CALLBACK (swatch_drag_drop), swatch);
+ gtk_widget_add_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (priv->dest));
gdk_content_formats_unref (targets);
}
- else
+ if (!can_drop && priv->dest)
{
- gtk_drag_dest_unset (GTK_WIDGET (swatch));
+ gtk_widget_remove_controller (GTK_WIDGET (swatch), GTK_EVENT_CONTROLLER (priv->dest));
+ priv->dest = NULL;
}
+
+ g_object_notify (G_OBJECT (swatch), "can-drop");
}
void
diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c
deleted file mode 100644
index 17dbb49876..0000000000
--- a/gtk/gtkdnd.c
+++ /dev/null
@@ -1,1425 +0,0 @@
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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/>.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GTK+ Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-#include "config.h"
-
-#include "gtkdndprivate.h"
-
-#include "gtkdragdest.h"
-#include "gtkimageprivate.h"
-#include "gtkintl.h"
-#include "gtkmain.h"
-#include "gtkpicture.h"
-#include "gtkselectionprivate.h"
-#include "gtksettingsprivate.h"
-#include "gtkstylecontext.h"
-#include "gtktooltipprivate.h"
-#include "gtkwidgetprivate.h"
-#include "gtkwindowgroup.h"
-#include "gtkwindowprivate.h"
-#include "gtknative.h"
-#include "gtkdragiconprivate.h"
-
-#include "gdk/gdkcontentformatsprivate.h"
-#include "gdk/gdktextureprivate.h"
-
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-
-
-/**
- * SECTION:gtkdnd
- * @Short_description: Functions for controlling drag and drop handling
- * @Title: Drag and Drop
- *
- * GTK+ has a rich set of functions for doing inter-process communication
- * via the drag-and-drop metaphor.
- *
- * As well as the functions listed here, applications may need to use some
- * facilities provided for [Selections][gtk3-Selections]. Also, the Drag and
- * Drop API makes use of signals in the #GtkWidget class.
- */
-
-
-typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
-typedef struct _GtkDragDestInfo GtkDragDestInfo;
-
-struct _GtkDragSourceInfo
-{
- GtkWidget *widget;
- GdkContentFormats *target_list; /* Targets for drag data */
- GdkDrag *drag; /* drag context */
- GtkWidget *icon_window; /* Window for drag */
- GtkWidget *icon_widget; /* Widget for drag */
-
- guint drop_timeout; /* Timeout for aborting drop */
- guint destroy_icon : 1; /* If true, destroy icon_widget */
-};
-
-struct _GtkDragDestInfo
-{
- GtkWidget *widget; /* Widget in which drag is in */
- GdkDrop *drop; /* drop */
-};
-
-#define DROP_ABORT_TIME 300000
-
-typedef gboolean (* GtkDragDestCallback) (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y,
- guint32 time);
-
-/* Forward declarations */
-static gboolean gtk_drop_find_widget (GtkWidget *widget,
- GdkDrop *drop,
- GtkDragDestInfo *info,
- gint x,
- gint y,
- guint32 time,
- GtkDragDestCallback callback);
-static void gtk_drag_dest_leave (GtkWidget *widget,
- GdkDrop *drop,
- guint time);
-static gboolean gtk_drag_dest_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y,
- guint time);
-static gboolean gtk_drag_dest_drop (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y,
- guint time);
-static void gtk_drag_dest_set_widget (GtkDragDestInfo *info,
- GtkWidget *widget);
-
-static GtkDragDestInfo * gtk_drag_get_dest_info (GdkDrop *drop,
- gboolean create);
-static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDrag *drag,
- gboolean create);
-static void gtk_drag_clear_source_info (GdkDrag *drag);
-
-static void gtk_drag_drop (GtkDragSourceInfo *info);
-static void gtk_drag_drop_finished (GtkDragSourceInfo *info,
- GtkDragResult result);
-static void gtk_drag_cancel_internal (GtkDragSourceInfo *info,
- GtkDragResult result);
-
-static void gtk_drag_remove_icon (GtkDragSourceInfo *info);
-static void gtk_drag_source_info_destroy (GtkDragSourceInfo *info);
-
-static void gtk_drag_drop_performed_cb (GdkDrag *drag,
- GtkDragSourceInfo *info);
-static void gtk_drag_cancel_cb (GdkDrag *drag,
- GdkDragCancelReason reason,
- GtkDragSourceInfo *info);
-static void gtk_drag_dnd_finished_cb (GdkDrag *drag,
- GtkDragSourceInfo *info);
-
-static gboolean gtk_drag_abort_timeout (gpointer data);
-
-static void set_icon_helper (GdkDrag *drag,
- GtkImageDefinition *def,
- gint hot_x,
- gint hot_y);
-
-
-/********************
- * Destination side *
- ********************/
-
-typedef struct {
- GdkDrop *drop;
- GtkWidget *widget;
- const char *mime_type;
-} GtkDragGetData;
-
-static void
-gtk_drag_get_data_finish (GtkDragGetData *data,
- guchar *bytes,
- gsize size)
-{
- GtkDragDestSite *site;
- GtkSelectionData sdata;
-
- site = g_object_get_data (G_OBJECT (data->widget), "gtk-drag-dest");
-
- sdata.target = data->mime_type;
- sdata.type = data->mime_type;
- sdata.format = 8;
- sdata.length = size;
- sdata.data = bytes ? bytes : (guchar *)g_strdup ("");
- sdata.display = gtk_widget_get_display (data->widget);
-
- if (site && site->target_list)
- {
- if (gdk_content_formats_contain_mime_type (site->target_list, data->mime_type))
- {
- if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
- size >= 0)
- g_signal_emit_by_name (data->widget,
- "drag-data-received",
- data->drop,
- &sdata);
- }
- }
- else
- {
- g_signal_emit_by_name (data->widget,
- "drag-data-received",
- data->drop,
- &sdata);
- }
-
- if (site && site->flags & GTK_DEST_DEFAULT_DROP)
- {
- GdkDragAction action = site->actions & gdk_drop_get_actions (data->drop);
-
- if (size == 0)
- action = 0;
-
- if (!gdk_drag_action_is_unique (action))
- {
- if (action & GDK_ACTION_COPY)
- action = GDK_ACTION_COPY;
- else if (action & GDK_ACTION_MOVE)
- action = GDK_ACTION_MOVE;
- }
-
- gdk_drop_finish (data->drop, action);
- }
-
- g_object_unref (data->widget);
- g_object_unref (data->drop);
- g_slice_free (GtkDragGetData, data);
-}
-
-static void
-gtk_drag_get_data_got_data (GObject *source,
- GAsyncResult *result,
- gpointer data)
-{
- gssize written;
-
- written = g_output_stream_splice_finish (G_OUTPUT_STREAM (source), result, NULL);
- if (written < 0)
- {
- gtk_drag_get_data_finish (data, NULL, 0);
- }
- else
- {
- gtk_drag_get_data_finish (data,
- g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (source)),
- g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (source)));
- }
-}
-
-static void
-gtk_drag_get_data_got_stream (GObject *source,
- GAsyncResult *result,
- gpointer user_data)
-{
- GtkDragGetData *data = user_data;
- GInputStream *input_stream;
- GOutputStream *output_stream;
-
- input_stream = gdk_drop_read_finish (GDK_DROP (source), result, &data->mime_type, NULL);
- if (input_stream == NULL)
- {
- gtk_drag_get_data_finish (data, NULL, 0);
- return;
- }
-
- output_stream = g_memory_output_stream_new_resizable ();
- g_output_stream_splice_async (output_stream,
- input_stream,
- G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- G_PRIORITY_DEFAULT,
- NULL,
- gtk_drag_get_data_got_data,
- data);
- g_object_unref (output_stream);
- g_object_unref (input_stream);
-
-}
-
-/**
- * gtk_drag_get_data: (method)
- * @widget: the widget that will receive the
- * #GtkWidget::drag-data-received signal
- * @drop: the #GdkDrop
- * @target: the target (form of the data) to retrieve
- *
- * Gets the data associated with a drag. When the data
- * is received or the retrieval fails, GTK+ will emit a
- * #GtkWidget::drag-data-received signal. Failure of the retrieval
- * is indicated by the length field of the @selection_data
- * signal parameter being negative. However, when gtk_drag_get_data()
- * is called implicitely because the %GTK_DEST_DEFAULT_DROP was set,
- * then the widget will not receive notification of failed
- * drops.
- */
-void
-gtk_drag_get_data (GtkWidget *widget,
- GdkDrop *drop,
- GdkAtom target)
-{
- GtkDragGetData *data;
-
- g_return_if_fail (GTK_IS_WIDGET (widget));
- g_return_if_fail (GDK_IS_DROP (drop));
-
- data = g_slice_new0 (GtkDragGetData);
- data->widget = g_object_ref (widget);
- data->drop = g_object_ref (drop);
- data->mime_type = target;
-
- gdk_drop_read_async (drop,
- (const gchar *[2]) { target, NULL },
- G_PRIORITY_DEFAULT,
- NULL,
- gtk_drag_get_data_got_stream,
- data);
-}
-
-/**
- * gtk_drag_get_source_widget:
- * @drag: a drag context
- *
- * Determines the source widget for a drag.
- *
- * Returns: (nullable) (transfer none): if the drag is occurring
- * within a single application, a pointer to the source widget.
- * Otherwise, %NULL.
- */
-GtkWidget *
-gtk_drag_get_source_widget (GdkDrag *drag)
-{
- GtkDragSourceInfo *info;
-
- g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
-
- info = gtk_drag_get_source_info (drag, FALSE);
- if (info == NULL)
- return NULL;
-
- return info->widget;
-}
-
-/**
- * gtk_drag_highlight: (method)
- * @widget: a widget to highlight
- *
- * Highlights a widget as a currently hovered drop target.
- * To end the highlight, call gtk_drag_unhighlight().
- * GTK+ calls this automatically if %GTK_DEST_DEFAULT_HIGHLIGHT is set.
- */
-void
-gtk_drag_highlight (GtkWidget *widget)
-{
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
- gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
-}
-
-/**
- * gtk_drag_unhighlight: (method)
- * @widget: a widget to remove the highlight from
- *
- * Removes a highlight set by gtk_drag_highlight() from
- * a widget.
- */
-void
-gtk_drag_unhighlight (GtkWidget *widget)
-{
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
- gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
-}
-
-/*
- * _gtk_drag_dest_handle_event:
- * @toplevel: Toplevel widget that received the event
- * @event: the event to handle
- *
- * Called from widget event handling code on Drag events
- * for destinations.
- */
-void
-_gtk_drag_dest_handle_event (GtkWidget *toplevel,
- GdkEvent *event)
-{
- GtkDragDestInfo *info;
- GdkDrop *drop;
- guint32 time;
- GdkEventType event_type;
-
- g_return_if_fail (toplevel != NULL);
- g_return_if_fail (event != NULL);
-
- event_type = gdk_event_get_event_type (event);
- drop = gdk_event_get_drop (event);
- time = gdk_event_get_time (event);
-
- info = gtk_drag_get_dest_info (drop, TRUE);
-
- /* Find the widget for the event */
- switch ((guint) event_type)
- {
- case GDK_DRAG_ENTER:
- break;
-
- case GDK_DRAG_LEAVE:
- if (info->widget)
- {
- gtk_drag_dest_leave (info->widget, drop, time);
- gtk_drag_dest_set_widget (info, NULL);
- }
- break;
-
- case GDK_DRAG_MOTION:
- case GDK_DROP_START:
- {
- double x, y;
- gboolean found;
-
- if (event_type == GDK_DROP_START)
- {
- /* We send a leave here so that the widget unhighlights
- * properly.
- */
- if (info->widget)
- {
- gtk_drag_dest_leave (info->widget, drop, time);
- gtk_drag_dest_set_widget (info, NULL);
- }
- }
-
- gdk_event_get_coords (event, &x, &y);
-
- found = gtk_drop_find_widget (toplevel,
- drop,
- info,
- x,
- y,
- time,
- (event_type == GDK_DRAG_MOTION) ?
- gtk_drag_dest_motion :
- gtk_drag_dest_drop);
-
- if (info->widget && !found)
- {
- gtk_drag_dest_leave (info->widget, drop, time);
- gtk_drag_dest_set_widget (info, NULL);
- }
-
- /* Send a reply.
- */
- if (!found)
- gdk_drop_status (drop, 0);
- }
- break;
-
- default:
- g_assert_not_reached ();
- }
-}
-
-static gboolean
-gtk_drop_find_widget (GtkWidget *event_widget,
- GdkDrop *drop,
- GtkDragDestInfo *info,
- gint x,
- gint y,
- guint32 time,
- GtkDragDestCallback callback)
-{
- GtkWidget *widget;
-
- if (!gtk_widget_get_mapped (event_widget) ||
- !gtk_widget_get_sensitive (event_widget))
- return FALSE;
-
- widget = gtk_widget_pick (event_widget, x, y, GTK_PICK_DEFAULT);
-
- if (!widget)
- return FALSE;
-
- gtk_widget_translate_coordinates (event_widget, widget, x, y, &x, &y);
-
- while (widget)
- {
- GtkWidget *parent;
- GList *hierarchy = NULL;
- gboolean found = FALSE;
-
- if (!gtk_widget_get_mapped (widget))
- return FALSE;
-
- if (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_INSENSITIVE)
- {
- widget = gtk_widget_get_parent (widget);
- continue;
- }
-
- /* need to reference the entire hierarchy temporarily in case the
- * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
- */
- for (parent = widget;
- parent;
- parent = gtk_widget_get_parent (parent))
- {
- hierarchy = g_list_prepend (hierarchy, g_object_ref (parent));
- }
-
- /* If the current widget is registered as a drop site, check to
- * emit "drag-motion" to check if we are actually in a drop
- * site.
- */
- if (g_object_get_data (G_OBJECT (widget), "gtk-drag-dest"))
- {
- found = callback (widget, drop, x, y, time);
-
- /* If so, send a "drag-leave" to the last widget */
- if (found && info->widget != widget)
- {
- if (info->widget)
- gtk_drag_dest_leave (info->widget, drop, time);
-
- gtk_drag_dest_set_widget (info, widget);
- }
- }
-
- if (!found)
- {
- /* Get the parent before unreffing the hierarchy because
- * invoking the callback might have destroyed the widget
- */
- parent = gtk_widget_get_parent (widget);
-
- /* The parent might be going away when unreffing the
- * hierarchy, so also protect againt that
- */
- if (parent)
- g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
- }
-
- g_list_free_full (hierarchy, g_object_unref);
-
- if (found)
- return TRUE;
-
- if (parent)
- g_object_remove_weak_pointer (G_OBJECT (parent), (gpointer *) &parent);
- else
- return FALSE;
-
- if (!gtk_widget_translate_coordinates (widget, parent, x, y, &x, &y))
- return FALSE;
-
- widget = parent;
- }
-
- return FALSE;
-}
-
-static void
-gtk_drag_dest_set_widget (GtkDragDestInfo *info,
- GtkWidget *widget)
-{
- if (info->widget)
- g_object_remove_weak_pointer (G_OBJECT (info->widget), (gpointer *) &info->widget);
-
- info->widget = widget;
-
- if (info->widget)
- g_object_add_weak_pointer (G_OBJECT (info->widget), (gpointer *) &info->widget);
-}
-
-static void
-gtk_drag_dest_info_destroy (gpointer data)
-{
- GtkDragDestInfo *info = (GtkDragDestInfo *)data;
-
- gtk_drag_dest_set_widget (info, NULL);
-
- g_slice_free (GtkDragDestInfo, data);
-}
-
-static GtkDragDestInfo *
-gtk_drag_get_dest_info (GdkDrop *drop,
- gboolean create)
-{
- GtkDragDestInfo *info;
- static GQuark info_quark = 0;
- if (!info_quark)
- info_quark = g_quark_from_static_string ("gtk-dest-info");
-
- info = g_object_get_qdata (G_OBJECT (drop), info_quark);
- if (!info && create)
- {
- info = g_slice_new0 (GtkDragDestInfo);
- info->drop = drop;
- g_object_set_qdata_full (G_OBJECT (drop), info_quark,
- info, gtk_drag_dest_info_destroy);
- }
-
- return info;
-}
-
-static GQuark dest_info_quark = 0;
-
-static GtkDragSourceInfo *
-gtk_drag_get_source_info (GdkDrag *drag,
- gboolean create)
-{
- GtkDragSourceInfo *info;
- if (!dest_info_quark)
- dest_info_quark = g_quark_from_static_string ("gtk-source-info");
-
- info = g_object_get_qdata (G_OBJECT (drag), dest_info_quark);
- if (!info && create)
- {
- info = g_new0 (GtkDragSourceInfo, 1);
- info->drag = drag;
- g_object_set_qdata (G_OBJECT (drag), dest_info_quark, info);
- }
-
- return info;
-}
-
-static void
-gtk_drag_clear_source_info (GdkDrag *drag)
-{
- g_object_set_qdata (G_OBJECT (drag), dest_info_quark, NULL);
-}
-
-/*
- * Default drag handlers
- */
-static void
-gtk_drag_dest_leave (GtkWidget *widget,
- GdkDrop *drop,
- guint time)
-{
- GtkDragDestSite *site;
-
- site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
- g_return_if_fail (site != NULL);
-
- if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
- gtk_drag_unhighlight (widget);
-
- if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
- site->track_motion)
- g_signal_emit_by_name (widget, "drag-leave", drop, time);
-
- site->have_drag = FALSE;
-}
-
-static gboolean
-gtk_drag_dest_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y,
- guint time)
-{
- GtkDragDestSite *site;
- gboolean retval;
-
- site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
- g_return_val_if_fail (site != NULL, FALSE);
-
- if (site->track_motion || site->flags & GTK_DEST_DEFAULT_MOTION)
- {
- GdkDragAction actions;
-
- actions = gdk_drop_get_actions (drop);
-
- if ((actions & site->actions) == 0)
- actions = 0;
-
- if (actions && gtk_drag_dest_find_target (widget, drop, NULL))
- {
- if (!site->have_drag)
- {
- site->have_drag = TRUE;
- if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
- gtk_drag_highlight (widget);
- }
-
- gdk_drop_status (drop, site->actions);
- }
- else
- {
- gdk_drop_status (drop, 0);
- if (!site->track_motion)
- return TRUE;
- }
- }
-
- g_signal_emit_by_name (widget, "drag-motion",
- drop, x, y, &retval);
-
- return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
-}
-
-static gboolean
-gtk_drag_dest_drop (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y,
- guint time)
-{
- GtkDragDestSite *site;
- GtkDragDestInfo *info;
- gboolean retval;
-
- site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
- g_return_val_if_fail (site != NULL, FALSE);
-
- info = gtk_drag_get_dest_info (drop, FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
-
- if (site->flags & GTK_DEST_DEFAULT_DROP)
- {
- GdkAtom target = gtk_drag_dest_find_target (widget, drop, NULL);
-
- if (target == NULL)
- {
- gdk_drop_finish (drop, 0);
- return TRUE;
- }
- else
- gtk_drag_get_data (widget, drop, target);
- }
-
- g_signal_emit_by_name (widget, "drag-drop",
- drop, x, y, &retval);
-
- return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
-}
-
-/***************
- * Source side *
- ***************/
-
-#define GTK_TYPE_DRAG_CONTENT (gtk_drag_content_get_type ())
-#define GTK_DRAG_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_DRAG_CONTENT, GtkDragContent))
-#define GTK_IS_DRAG_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_DRAG_CONTENT))
-#define GTK_DRAG_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_DRAG_CONTENT, GtkDragContentClass))
-#define GTK_IS_DRAG_CONTENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DRAG_CONTENT))
-#define GTK_DRAG_CONTENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_DRAG_CONTENT, GtkDragContentClass))
-
-typedef struct _GtkDragContent GtkDragContent;
-typedef struct _GtkDragContentClass GtkDragContentClass;
-
-struct _GtkDragContent
-{
- GdkContentProvider parent;
-
- GtkWidget *widget;
- GdkDrag *drag;
- GdkContentFormats *formats;
- guint32 time;
-};
-
-struct _GtkDragContentClass
-{
- GdkContentProviderClass parent_class;
-};
-
-GType gtk_drag_content_get_type (void) G_GNUC_CONST;
-
-G_DEFINE_TYPE (GtkDragContent, gtk_drag_content, GDK_TYPE_CONTENT_PROVIDER)
-
-static GdkContentFormats *
-gtk_drag_content_ref_formats (GdkContentProvider *provider)
-{
- GtkDragContent *content = GTK_DRAG_CONTENT (provider);
-
- return gdk_content_formats_ref (content->formats);
-}
-
-static void
-gtk_drag_content_write_mime_type_done (GObject *stream,
- GAsyncResult *result,
- gpointer task)
-{
- GError *error = NULL;
-
- if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream),
- result,
- NULL,
- &error))
- {
- g_task_return_error (task, error);
- }
- else
- {
- g_task_return_boolean (task, TRUE);
- }
-
- g_object_unref (task);
-}
-
-static void
-gtk_drag_content_write_mime_type_async (GdkContentProvider *provider,
- const char *mime_type,
- GOutputStream *stream,
- int io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GtkDragContent *content = GTK_DRAG_CONTENT (provider);
- GtkSelectionData sdata = { 0, };
- GTask *task;
-
- task = g_task_new (content, cancellable, callback, user_data);
- g_task_set_priority (task, io_priority);
- g_task_set_source_tag (task, gtk_drag_content_write_mime_type_async);
-
- sdata.target = g_intern_string (mime_type);
- sdata.length = -1;
- sdata.display = gtk_widget_get_display (content->widget);
-
- g_signal_emit_by_name (content->widget, "drag-data-get",
- content->drag,
- &sdata);
-
- if (sdata.length == -1)
- {
- g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Cannot provide contents as “%s”"), mime_type);
- g_object_unref (task);
- return;
- }
- g_task_set_task_data (task, sdata.data, g_free);
-
- g_output_stream_write_all_async (stream,
- sdata.data,
- sdata.length,
- io_priority,
- cancellable,
- gtk_drag_content_write_mime_type_done,
- task);
-}
-
-static gboolean
-gtk_drag_content_write_mime_type_finish (GdkContentProvider *provider,
- GAsyncResult *result,
- GError **error)
-{
- g_return_val_if_fail (g_task_is_valid (result, provider), FALSE);
- g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_drag_content_write_mime_type_async, FALSE);
-
- return g_task_propagate_boolean (G_TASK (result), error);
-}
-
-static void
-gtk_drag_content_finalize (GObject *object)
-{
- GtkDragContent *content = GTK_DRAG_CONTENT (object);
-
- g_clear_object (&content->widget);
- g_clear_pointer (&content->formats, gdk_content_formats_unref);
-
- G_OBJECT_CLASS (gtk_drag_content_parent_class)->finalize (object);
-}
-
-static void
-gtk_drag_content_class_init (GtkDragContentClass *class)
-{
- GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
- GObjectClass *object_class = G_OBJECT_CLASS (class);
-
- object_class->finalize = gtk_drag_content_finalize;
-
- provider_class->ref_formats = gtk_drag_content_ref_formats;
- provider_class->write_mime_type_async = gtk_drag_content_write_mime_type_async;
- provider_class->write_mime_type_finish = gtk_drag_content_write_mime_type_finish;
-}
-
-static void
-gtk_drag_content_init (GtkDragContent *content)
-{
-}
-
-/* Like gtk_drag_begin(), but also takes a GtkImageDefinition
- * so that we can set the icon from the source site information
- */
-GdkDrag *
-gtk_drag_begin_internal (GtkWidget *widget,
- GdkDevice *device,
- GtkImageDefinition *icon,
- GdkContentFormats *target_list,
- GdkDragAction actions,
- int x,
- int y)
-{
- GtkDragSourceInfo *info;
- GtkNative *native;
- GdkDrag *drag;
- double px, py;
- int dx, dy;
- GtkDragContent *content;
-
- if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
- device = gdk_device_get_associated_device (device);
-
- native = gtk_widget_get_native (widget);
- gtk_widget_translate_coordinates (widget, GTK_WIDGET (native), x, y, &x, &y);
- gdk_surface_get_device_position (gtk_native_get_surface (native),
- device,
- &px, &py,
- NULL);
- dx = round (px) - x;
- dy = round (py) - y;
-
- content = g_object_new (GTK_TYPE_DRAG_CONTENT, NULL);
- content->widget = g_object_ref (widget);
- content->formats = gdk_content_formats_ref (target_list);
-
- drag = gdk_drag_begin (gtk_native_get_surface (native), device, GDK_CONTENT_PROVIDER (content), actions, dx, dy);
- if (drag == NULL)
- {
- g_object_unref (content);
- return NULL;
- }
-
- content->drag = drag;
- g_object_unref (content);
-
- info = gtk_drag_get_source_info (drag, TRUE);
-
- g_object_set_data (G_OBJECT (widget), I_("gtk-info"), info);
-
- info->widget = g_object_ref (widget);
-
- info->target_list = target_list;
- gdk_content_formats_ref (target_list);
-
- info->icon_window = NULL;
- info->icon_widget = NULL;
- info->destroy_icon = FALSE;
-
- gtk_widget_reset_controllers (widget);
-
- g_signal_emit_by_name (widget, "drag-begin", info->drag);
-
- /* Ensure that we have an icon before we start the drag; the
- * application may have set one in ::drag_begin, or it may
- * not have set one.
- */
- if (!info->icon_widget)
- {
- if (icon)
- {
- set_icon_helper (info->drag, icon, 0, 0);
- }
- else
- {
- icon = gtk_image_definition_new_icon_name ("text-x-generic");
- set_icon_helper (info->drag, icon, 0, 0);
- gtk_image_definition_unref (icon);
- }
- }
-
- g_signal_connect (drag, "drop-performed",
- G_CALLBACK (gtk_drag_drop_performed_cb), info);
- g_signal_connect (drag, "dnd-finished",
- G_CALLBACK (gtk_drag_dnd_finished_cb), info);
- g_signal_connect (drag, "cancel",
- G_CALLBACK (gtk_drag_cancel_cb), info);
-
- return info->drag;
-}
-
-/**
- * gtk_drag_begin: (method)
- * @widget: the source widget
- * @device: (nullable): the device that starts the drag or %NULL to use the default pointer
- * @targets: The targets (data formats) in which the source can provide the data
- * @actions: A bitmask of the allowed drag actions for this drag
- * @x: The initial x coordinate to start dragging from, in the coordinate space of @widget.
- * @y: The initial y coordinate to start dragging from, in the coordinate space of @widget.
- *
- * Initiates a drag on the source side. The function only needs to be used
- * when the application is starting drags itself, and is not needed when
- * gtk_drag_source_set() is used.
- *
- * Returns: (transfer none): the context for this drag
- */
-GdkDrag *
-gtk_drag_begin (GtkWidget *widget,
- GdkDevice *device,
- GdkContentFormats *targets,
- GdkDragAction actions,
- gint x,
- gint y)
-{
- g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
- g_return_val_if_fail (device == NULL || GDK_IS_DEVICE (device), NULL);
- g_return_val_if_fail (gtk_widget_get_realized (widget), NULL);
- g_return_val_if_fail (targets != NULL, NULL);
-
- if (device == NULL)
- {
- GdkSeat *seat;
-
- seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
- device = gdk_seat_get_pointer (seat);
- }
-
- return gtk_drag_begin_internal (widget,
- device,
- NULL,
- targets,
- actions,
- x, y);
-}
-
-static void
-icon_widget_destroyed (GtkWidget *widget,
- GtkDragSourceInfo *info)
-{
- g_clear_object (&info->icon_widget);
-}
-
-static void
-gtk_drag_set_icon_widget_internal (GdkDrag *drag,
- GtkWidget *widget,
- gint hot_x,
- gint hot_y,
- gboolean destroy_on_release)
-{
- GtkDragSourceInfo *info;
-
- g_return_if_fail (!GTK_IS_WINDOW (widget));
-
- info = gtk_drag_get_source_info (drag, FALSE);
- if (info == NULL)
- {
- if (destroy_on_release)
- gtk_widget_destroy (widget);
- return;
- }
-
- gtk_drag_remove_icon (info);
-
- if (widget)
- g_object_ref (widget);
-
- info->icon_widget = widget;
- info->destroy_icon = destroy_on_release;
-
- if (!widget)
- return;
-
- g_signal_connect (widget, "destroy", G_CALLBACK (icon_widget_destroyed), info);
-
- gdk_drag_set_hotspot (drag, hot_x, hot_y);
-
- if (!info->icon_window)
- {
- info->icon_window = gtk_drag_icon_new ();
- g_object_ref_sink (info->icon_window);
- gtk_widget_set_size_request (info->icon_window, 24, 24);
- gtk_drag_icon_set_surface (GTK_DRAG_ICON (info->icon_window),
- gdk_drag_get_drag_surface (drag));
- gtk_widget_show (info->icon_window);
- }
-
- gtk_drag_icon_set_widget (GTK_DRAG_ICON (info->icon_window), widget);
-}
-
-/**
- * gtk_drag_set_icon_widget:
- * @drag: the context for a drag
- * @widget: a widget to use as an icon
- * @hot_x: the X offset within @widget of the hotspot
- * @hot_y: the Y offset within @widget of the hotspot
- *
- * Changes the icon for drag operation to a given widget.
- * GTK+ will not destroy the widget, so if you don’t want
- * it to persist, you should connect to the “drag-end”
- * signal and destroy it yourself.
- */
-void
-gtk_drag_set_icon_widget (GdkDrag *drag,
- GtkWidget *widget,
- gint hot_x,
- gint hot_y)
-{
- g_return_if_fail (GDK_IS_DRAG (drag));
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
- gtk_drag_set_icon_widget_internal (drag, widget, hot_x, hot_y, FALSE);
-}
-
-static void
-set_icon_helper (GdkDrag *drag,
- GtkImageDefinition *def,
- gint hot_x,
- gint hot_y)
-{
- GtkWidget *widget;
-
- widget = gtk_image_new ();
- gtk_style_context_add_class (gtk_widget_get_style_context (widget), "drag-icon");
- gtk_image_set_from_definition (GTK_IMAGE (widget), def);
- gtk_drag_set_icon_widget_internal (drag, widget, hot_x, hot_y, TRUE);
-}
-
-void
-gtk_drag_set_icon_definition (GdkDrag *drag,
- GtkImageDefinition *def,
- gint hot_x,
- gint hot_y)
-{
- g_return_if_fail (GDK_IS_DRAG (drag));
- g_return_if_fail (def != NULL);
-
- set_icon_helper (drag, def, hot_x, hot_y);
-}
-
-/**
- * gtk_drag_set_icon_paintable:
- * @drag: the context for a drag
- * @paintable: the #GdkPaintable to use as icon
- * @hot_x: the X offset of the hotspot within the icon
- * @hot_y: the Y offset of the hotspot within the icon
- *
- * Sets @paintable as the icon for a given drag. GTK+ retains
- * references for the arguments, and will release them when
- * they are no longer needed.
- *
- * To position the @paintable relative to the mouse, its top
- * left will be positioned @hot_x, @hot_y pixels from the
- * mouse cursor.
- */
-void
-gtk_drag_set_icon_paintable (GdkDrag *drag,
- GdkPaintable *paintable,
- int hot_x,
- int hot_y)
-{
- GtkWidget *widget;
-
- g_return_if_fail (GDK_IS_DRAG (drag));
- g_return_if_fail (GDK_IS_PAINTABLE (paintable));
-
- widget = gtk_picture_new_for_paintable (paintable);
- gtk_picture_set_can_shrink (GTK_PICTURE (widget), FALSE);
-
- gtk_drag_set_icon_widget_internal (drag, widget, hot_x, hot_y, TRUE);
-}
-
-/**
- * gtk_drag_set_icon_name:
- * @drag: the context for a drag
- * @icon_name: name of icon to use
- * @hot_x: the X offset of the hotspot within the icon
- * @hot_y: the Y offset of the hotspot within the icon
- *
- * Sets the icon for a given drag from a named themed icon. See
- * the docs for #GtkIconTheme for more details. Note that the
- * size of the icon depends on the icon theme (the icon is
- * loaded at the symbolic size #GTK_ICON_SIZE_DND), thus
- * @hot_x and @hot_y have to be used with care.
- */
-void
-gtk_drag_set_icon_name (GdkDrag *drag,
- const gchar *icon_name,
- gint hot_x,
- gint hot_y)
-{
- GtkImageDefinition *def;
-
- g_return_if_fail (GDK_IS_DRAG (drag));
- g_return_if_fail (icon_name != NULL && icon_name[0] != '\0');
-
- def = gtk_image_definition_new_icon_name (icon_name);
- set_icon_helper (drag, def, hot_x, hot_y);
-
- gtk_image_definition_unref (def);
-}
-
-/**
- * gtk_drag_set_icon_gicon:
- * @drag: the context for a drag
- * @icon: a #GIcon
- * @hot_x: the X offset of the hotspot within the icon
- * @hot_y: the Y offset of the hotspot within the icon
- *
- * Sets the icon for a given drag from the given @icon.
- * See the documentation for gtk_drag_set_icon_name()
- * for more details about using icons in drag and drop.
- */
-void
-gtk_drag_set_icon_gicon (GdkDrag *drag,
- GIcon *icon,
- gint hot_x,
- gint hot_y)
-{
- GtkImageDefinition *def;
-
- g_return_if_fail (GDK_IS_DRAG (drag));
- g_return_if_fail (icon != NULL);
-
- def = gtk_image_definition_new_gicon (icon);
- set_icon_helper (drag, def, hot_x, hot_y);
-
- gtk_image_definition_unref (def);
-}
-
-/**
- * gtk_drag_set_icon_default:
- * @drag: the context for a drag
- *
- * Sets the icon for a particular drag to the default
- * icon.
- */
-void
-gtk_drag_set_icon_default (GdkDrag *drag)
-{
- g_return_if_fail (GDK_IS_DRAG (drag));
-
- gtk_drag_set_icon_name (drag, "text-x-generic", -2, -2);
-}
-
-/* Clean up from the drag, and display snapback, if necessary. */
-static void
-gtk_drag_drop_finished (GtkDragSourceInfo *info,
- GtkDragResult result)
-{
- gboolean success;
-
- success = (result == GTK_DRAG_RESULT_SUCCESS);
-
- if (!success)
- g_signal_emit_by_name (info->widget, "drag-failed",
- info->drag, result, &success);
-
- gdk_drag_drop_done (info->drag, success);
- gtk_drag_source_info_destroy (info);
-}
-
-static void
-gtk_drag_drop (GtkDragSourceInfo *info)
-{
- if (info->icon_window)
- gtk_widget_hide (info->icon_window);
-
- info->drop_timeout = g_timeout_add (DROP_ABORT_TIME, gtk_drag_abort_timeout, info);
- g_source_set_name_by_id (info->drop_timeout, "[gtk] gtk_drag_abort_timeout");
-}
-
-/*
- * Source side callbacks.
- */
-static void
-gtk_drag_remove_icon (GtkDragSourceInfo *info)
-{
- if (info->icon_widget)
- {
- GtkWidget *widget;
-
- widget = info->icon_widget;
- info->icon_widget = NULL;
-
- g_signal_handlers_disconnect_by_func (widget, icon_widget_destroyed, info);
-
- gtk_widget_hide (widget);
- gtk_widget_set_opacity (widget, 1.0);
-
- if (info->destroy_icon)
- gtk_widget_destroy (widget);
- else
- gtk_drag_icon_set_widget (GTK_DRAG_ICON (info->icon_window), NULL);
-
- g_object_unref (widget);
- }
-}
-
-static void
-gtk_drag_source_info_free (GtkDragSourceInfo *info)
-{
- gtk_drag_remove_icon (info);
- g_object_unref (info->icon_window);
- g_free (info);
-}
-
-static void
-gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
-{
- g_signal_handlers_disconnect_by_func (info->drag, gtk_drag_drop_performed_cb, info);
- g_signal_handlers_disconnect_by_func (info->drag, gtk_drag_dnd_finished_cb, info);
- g_signal_handlers_disconnect_by_func (info->drag, gtk_drag_cancel_cb, info);
-
- g_signal_emit_by_name (info->widget, "drag-end", info->drag);
-
- g_object_set_data (G_OBJECT (info->widget), I_("gtk-info"), NULL);
-
- g_clear_object (&info->widget);
-
- gdk_content_formats_unref (info->target_list);
-
- if (info->drop_timeout)
- g_source_remove (info->drop_timeout);
-
- /* keep the icon_window alive until the (possible) drag cancel animation is done */
- g_object_set_data_full (G_OBJECT (info->drag), "former-gtk-source-info", info, (GDestroyNotify)gtk_drag_source_info_free);
-
- gtk_drag_clear_source_info (info->drag);
- g_object_unref (info->drag);
-}
-
-/* Called on cancellation of a drag, either by the user
- * or programmatically.
- */
-static void
-gtk_drag_cancel_internal (GtkDragSourceInfo *info,
- GtkDragResult result)
-{
- gtk_drag_drop_finished (info, result);
-}
-
-static void
-gtk_drag_drop_performed_cb (GdkDrag *drag,
- GtkDragSourceInfo *info)
-{
- gtk_drag_drop (info);
-}
-
-static void
-gtk_drag_cancel_cb (GdkDrag *drag,
- GdkDragCancelReason reason,
- GtkDragSourceInfo *info)
-{
- GtkDragResult result;
-
- switch (reason)
- {
- case GDK_DRAG_CANCEL_NO_TARGET:
- result = GTK_DRAG_RESULT_NO_TARGET;
- break;
- case GDK_DRAG_CANCEL_USER_CANCELLED:
- result = GTK_DRAG_RESULT_USER_CANCELLED;
- break;
- case GDK_DRAG_CANCEL_ERROR:
- default:
- result = GTK_DRAG_RESULT_ERROR;
- break;
- }
- gtk_drag_cancel_internal (info, result);
-}
-
-static void
-gtk_drag_dnd_finished_cb (GdkDrag *drag,
- GtkDragSourceInfo *info)
-{
- if (gdk_drag_get_selected_action (drag) == GDK_ACTION_MOVE)
- {
- g_signal_emit_by_name (info->widget,
- "drag-data-delete",
- drag);
- }
-
- gtk_drag_source_info_destroy (info);
-}
-
-static gboolean
-gtk_drag_abort_timeout (gpointer data)
-{
- GtkDragSourceInfo *info = data;
-
- info->drop_timeout = 0;
- gtk_drag_drop_finished (info, GTK_DRAG_RESULT_TIMEOUT_EXPIRED);
-
- return G_SOURCE_REMOVE;
-}
-
-/**
- * gtk_drag_check_threshold: (method)
- * @widget: a #GtkWidget
- * @start_x: X coordinate of start of drag
- * @start_y: Y coordinate of start of drag
- * @current_x: current X coordinate
- * @current_y: current Y coordinate
- *
- * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
- * at (@current_x, @current_y) has passed the GTK+ drag threshold, and thus
- * should trigger the beginning of a drag-and-drop operation.
- *
- * Returns: %TRUE if the drag threshold has been passed.
- */
-gboolean
-gtk_drag_check_threshold (GtkWidget *widget,
- gint start_x,
- gint start_y,
- gint current_x,
- gint current_y)
-{
- gint drag_threshold;
-
- g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
-
- drag_threshold = gtk_settings_get_dnd_drag_threshold (gtk_widget_get_settings (widget));
-
- return (ABS (current_x - start_x) > drag_threshold ||
- ABS (current_y - start_y) > drag_threshold);
-}
-
-/**
- * gtk_drag_cancel:
- * @drag: a drag context, as e.g. returned by gtk_drag_begin()
- *
- * Cancels an ongoing drag operation on the source side.
- *
- * If you want to be able to cancel a drag operation in this way,
- * you need to keep a pointer to the drag context, either from an
- * explicit call to gtk_drag_begin(), or by connecting to
- * #GtkWidget::drag-begin.
- *
- * If @context does not refer to an ongoing drag operation, this
- * function does nothing.
- *
- * If a drag is cancelled in this way, the @result argument of
- * #GtkWidget::drag-failed is set to @GTK_DRAG_RESULT_ERROR.
- */
-void
-gtk_drag_cancel (GdkDrag *drag)
-{
- GtkDragSourceInfo *info;
-
- g_return_if_fail (GDK_IS_DRAG (drag));
-
- info = gtk_drag_get_source_info (drag, FALSE);
- if (info != NULL)
- gtk_drag_cancel_internal (info, GTK_DRAG_RESULT_ERROR);
-}
diff --git a/gtk/gtkdnd.h b/gtk/gtkdnd.h
deleted file mode 100644
index 2c81f9d689..0000000000
--- a/gtk/gtkdnd.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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/>.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GTK+ Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-#ifndef __GTK_DND_H__
-#define __GTK_DND_H__
-
-
-#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
-#error "Only <gtk/gtk.h> can be included directly."
-#endif
-
-#include <gtk/gtkwidget.h>
-#include <gtk/gtkselection.h>
-
-
-G_BEGIN_DECLS
-
-/* Destination side */
-
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_get_data (GtkWidget *widget,
- GdkDrop *drop,
- GdkAtom target);
-
-GDK_AVAILABLE_IN_ALL
-GtkWidget *gtk_drag_get_source_widget (GdkDrag *drag);
-
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_highlight (GtkWidget *widget);
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_unhighlight (GtkWidget *widget);
-
-/* Source side */
-
-GDK_AVAILABLE_IN_ALL
-GdkDrag *gtk_drag_begin (GtkWidget *widget,
- GdkDevice *device,
- GdkContentFormats *targets,
- GdkDragAction actions,
- gint x,
- gint y);
-
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_cancel (GdkDrag *drag);
-
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_set_icon_widget (GdkDrag *drag,
- GtkWidget *widget,
- gint hot_x,
- gint hot_y);
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_set_icon_paintable (GdkDrag *drag,
- GdkPaintable *paintable,
- int hot_x,
- int hot_y);
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_set_icon_name (GdkDrag *drag,
- const gchar *icon_name,
- gint hot_x,
- gint hot_y);
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_set_icon_gicon (GdkDrag *drag,
- GIcon *icon,
- gint hot_x,
- gint hot_y);
-
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_set_icon_default (GdkDrag *drag);
-
-GDK_AVAILABLE_IN_ALL
-gboolean gtk_drag_check_threshold (GtkWidget *widget,
- gint start_x,
- gint start_y,
- gint current_x,
- gint current_y);
-
-
-G_END_DECLS
-
-#endif /* __GTK_DND_H__ */
diff --git a/gtk/gtkdndprivate.h b/gtk/gtkdndprivate.h
deleted file mode 100644
index 45448a17e2..0000000000
--- a/gtk/gtkdndprivate.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
-/* GTK - The GIMP Toolkit
- * 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/>.
- */
-
-#ifndef __GTK_DND_PRIVATE_H__
-#define __GTK_DND_PRIVATE_H__
-
-#include "gtkdnd.h"
-
-#include "gtkdragdest.h"
-#include "gtkimagedefinitionprivate.h"
-#include "gtkselection.h"
-#include "gtkwidget.h"
-
-typedef struct _GtkDragDestSite GtkDragDestSite;
-struct _GtkDragDestSite
-{
- GtkDestDefaults flags;
- GdkContentFormats *target_list;
- GdkDragAction actions;
- guint do_proxy : 1;
- guint proxy_coords : 1;
- guint have_drag : 1;
- guint track_motion : 1;
-};
-
-G_BEGIN_DECLS
-
-GdkDrag * gtk_drag_begin_internal (GtkWidget *widget,
- GdkDevice *device,
- GtkImageDefinition *icon,
- GdkContentFormats *target_list,
- GdkDragAction actions,
- int x,
- int y);
-void gtk_drag_set_icon_definition (GdkDrag *drag,
- GtkImageDefinition *def,
- gint hot_x,
- gint hot_y);
-void _gtk_drag_dest_handle_event (GtkWidget *toplevel,
- GdkEvent *event);
-
-G_END_DECLS
-
-#endif /* __GTK_DND_PRIVATE_H__ */
diff --git a/gtk/gtkdragdest.c b/gtk/gtkdragdest.c
index da2205e09d..a7f32bc1c0 100644
--- a/gtk/gtkdragdest.c
+++ b/gtk/gtkdragdest.c
@@ -25,392 +25,1036 @@
#include "config.h"
#include "gtkdragdest.h"
+#include "gtkdragdestprivate.h"
-#include "gtkdnd.h"
-#include "gtkdndprivate.h"
#include "gtkintl.h"
#include "gtknative.h"
+#include "gtktypebuiltins.h"
+#include "gtkeventcontrollerprivate.h"
+#include "gtkmarshalers.h"
+#include "gtkselectionprivate.h"
-static void
-gtk_drag_dest_realized (GtkWidget *widget)
+/**
+ * SECTION:gtkdroptarget
+ * @Short_description: Event controller to receive DND drops
+ * @Title: GtkDropTarget
+ *
+ * GtkDropTarget is an auxiliary object that is used to receive
+ * Drag-and-Drop operations.
+ *
+ * To use a GtkDropTarget to receive drops on a widget, you create
+ * a GtkDropTarget object, configure which data formats and actions
+ * you support, connect to its signals, and then attach
+ * it to the widget with gtk_widget_add_controller().
+ *
+ * During a drag operation, the first signal that a GtkDropTarget
+ * emits is #GtkDropTarget::accept, which is meant to determine
+ * whether the target is a possible drop site for the ongoing drag.
+ * The default handler for the ::accept signal accepts the drag
+ * if it finds a compatible data format an an action that is supported
+ * on both sides.
+ *
+ * If it is, and the widget becomes the current target, you will
+ * receive a #GtkDropTarget::drag-enter signal, followed by
+ * #GtkDropTarget::drag-motion signals as the pointer moves, and
+ * finally either a #GtkDropTarget::drag-leave signal when the pointer
+ * move off the widget, or a #GtkDropTarget::drag-drop signal when
+ * a drop happens.
+ *
+ * The ::drag-enter and ::drag-motion handler can call gdk_drop_status()
+ * to update the status of the ongoing operation. The ::drag-drop handler
+ * should initiate the data transfer and finish the operation by calling
+ * gdk_drop_finish().
+ *
+ * Between the ::drag-enter and ::drag-leave signals the widget is the
+ * current drop target, and will receive the %GTK_STATE_FLAG_DROP_ACTIVE
+ * state, which can be used to style the widget as a drop targett.
+ */
+
+struct _GtkDropTarget
{
- GtkNative *native = gtk_widget_get_native (widget);
+ GtkEventController parent_object;
- gdk_surface_register_dnd (gtk_native_get_surface (native));
+ GdkContentFormats *formats;
+ GdkDragAction actions;
+
+ GtkWidget *widget;
+ GdkDrop *drop;
+ gboolean contains;
+ gboolean contains_pending;
+};
+
+struct _GtkDropTargetClass
+{
+ GtkEventControllerClass parent_class;
+
+ gboolean (*accept ) (GtkDropTarget *dest,
+ GdkDrop *drop);
+};
+
+enum {
+ PROP_FORMATS = 1,
+ PROP_ACTIONS,
+ PROP_CONTAINS,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+enum {
+ ACCEPT,
+ DRAG_ENTER,
+ DRAG_MOTION,
+ DRAG_LEAVE,
+ DRAG_DROP,
+ NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS];
+
+static gboolean gtk_drop_target_accept (GtkDropTarget *dest,
+ GdkDrop *drop);
+
+static gboolean gtk_drop_target_handle_event (GtkEventController *controller,
+ const GdkEvent *event);
+static gboolean gtk_drop_target_filter_event (GtkEventController *controller,
+ const GdkEvent *event);
+static void gtk_drop_target_set_widget (GtkEventController *controller,
+ GtkWidget *widget);
+static void gtk_drop_target_unset_widget (GtkEventController *controller);
+
+static gboolean gtk_drop_target_get_contains (GtkDropTarget *dest);
+static void gtk_drop_target_set_contains (GtkDropTarget *dest,
+ gboolean contains);
+
+typedef enum {
+ GTK_DROP_STATUS_NONE,
+ GTK_DROP_STATUS_ACCEPTED,
+ GTK_DROP_STATUS_DENIED
+} GtkDropStatus;
+
+static GtkDropStatus gtk_drop_target_get_drop_status (GtkDropTarget *dest,
+ GdkDrop *drop);
+static void gtk_drop_target_set_drop_status (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkDropStatus status);
+
+G_DEFINE_TYPE (GtkDropTarget, gtk_drop_target, GTK_TYPE_EVENT_CONTROLLER);
+
+static void
+gtk_drop_target_init (GtkDropTarget *dest)
+{
}
static void
-gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
- GParamSpec *pspec,
- gpointer data)
+gtk_drop_target_finalize (GObject *object)
{
- GtkNative *native = gtk_widget_get_native (widget);
+ GtkDropTarget *dest = GTK_DROP_TARGET (object);
- if (native && gtk_widget_get_realized (GTK_WIDGET (native)))
- gdk_surface_register_dnd (gtk_native_get_surface (native));
+ g_clear_pointer (&dest->formats, gdk_content_formats_unref);
+
+ G_OBJECT_CLASS (gtk_drop_target_parent_class)->finalize (object);
}
static void
-gtk_drag_dest_site_destroy (gpointer data)
+gtk_drop_target_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- GtkDragDestSite *site = data;
+ GtkDropTarget *dest = GTK_DROP_TARGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_FORMATS:
+ gtk_drop_target_set_formats (dest, g_value_get_boxed (value));
+ break;
- if (site->target_list)
- gdk_content_formats_unref (site->target_list);
+ case PROP_ACTIONS:
+ gtk_drop_target_set_actions (dest, g_value_get_flags (value));
+ break;
- g_slice_free (GtkDragDestSite, site);
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
}
static void
-gtk_drag_dest_set_internal (GtkWidget *widget,
- GtkDragDestSite *site)
+gtk_drop_target_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- GtkDragDestSite *old_site;
+ GtkDropTarget *dest = GTK_DROP_TARGET (object);
- old_site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
- if (old_site)
+ switch (prop_id)
{
- g_signal_handlers_disconnect_by_func (widget,
- gtk_drag_dest_realized,
- old_site);
- g_signal_handlers_disconnect_by_func (widget,
- gtk_drag_dest_hierarchy_changed,
- old_site);
-
- site->track_motion = old_site->track_motion;
+ case PROP_FORMATS:
+ g_value_set_boxed (value, gtk_drop_target_get_formats (dest));
+ break;
+
+ case PROP_ACTIONS:
+ g_value_set_flags (value, gtk_drop_target_get_actions (dest));
+ break;
+
+ case PROP_CONTAINS:
+ g_value_set_boolean (value, gtk_drop_target_get_contains (dest));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
+}
- if (gtk_widget_get_realized (widget))
- gtk_drag_dest_realized (widget);
+static void
+gtk_drop_target_class_init (GtkDropTargetClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
+
+ object_class->finalize = gtk_drop_target_finalize;
+ object_class->set_property = gtk_drop_target_set_property;
+ object_class->get_property = gtk_drop_target_get_property;
+
+ controller_class->handle_event = gtk_drop_target_handle_event;
+ controller_class->filter_event = gtk_drop_target_filter_event;
+ controller_class->set_widget = gtk_drop_target_set_widget;
+ controller_class->unset_widget = gtk_drop_target_unset_widget;
+
+ class->accept = gtk_drop_target_accept;
- g_signal_connect (widget, "realize",
- G_CALLBACK (gtk_drag_dest_realized), site);
- g_signal_connect (widget, "notify::root",
- G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
+ /**
+ * GtkDropTarget:formats:
+ *
+ * The #GdkContentFormats that determines the supported data formats
+ */
+ properties[PROP_FORMATS] =
+ g_param_spec_boxed ("formats", P_("Formats"), P_("Formats"),
+ GDK_TYPE_CONTENT_FORMATS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
- g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
- site, gtk_drag_dest_site_destroy);
+ /**
+ * GtkDropTarget:actions:
+ *
+ * The #GdkDragActions that this drop target supports
+ */
+ properties[PROP_ACTIONS] =
+ g_param_spec_flags ("actions", P_("Actions"), P_("Actions"),
+ GDK_TYPE_DRAG_ACTION, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkDropTarget:contains:
+ *
+ * Whether the drop target is currently the targed of an ongoing drag operation,
+ * and highlighted.
+ */
+ properties[PROP_CONTAINS] =
+ g_param_spec_boolean ("contains", P_("Contains an ongoing drag"), P_("Contains the current drag"),
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+ /**
+ * GtkDropTarget::drag-enter:
+ * @dest: the #GtkDropTarget
+ * @drop: the #GdkDrop
+ *
+ * The ::drag-enter signal is emitted on the drop site when the cursor
+ * enters the widget. It can be used to set up custom highlighting.
+ */
+ signals[DRAG_ENTER] =
+ g_signal_new (I_("drag-enter"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ GDK_TYPE_DROP);
+
+ /**
+ * GtkDropTarget::drag-leave:
+ * @dest: the #GtkDropTarget
+ * @drop: the #GdkDrop
+ *
+ * The ::drag-leave signal is emitted on the drop site when the cursor
+ * leaves the widget. Its main purpose it to undo things done in
+ * #GtkDropTarget::drag-enter.
+ */
+ signals[DRAG_LEAVE] =
+ g_signal_new (I_("drag-leave"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ GDK_TYPE_DROP);
+
+ /**
+ * GtkDropTarget::drag-motion:
+ * @dest: the #GtkDropTarget
+ * @drop: the #GdkDrop
+ * @x: the x coordinate of the current cursor position
+ * @y: the y coordinate of the current cursor position
+ *
+ * The ::drag motion signal is emitted while the pointer is moving
+ * over the drop target.
+ */
+ signals[DRAG_MOTION] =
+ g_signal_new (I_("drag-motion"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 3,
+ GDK_TYPE_DROP, G_TYPE_INT, G_TYPE_INT);
+
+ /**
+ * GtkWidget::accept:
+ * @dest: the #GtkDropTarget
+ * @drop: the #GdkDrop
+ *
+ * The ::accept signal is emitted on the drop site when the user
+ * moves the cursor over the widget during a drag. The signal handler
+ * must determine whether the cursor position is in a drop zone or not.
+ * If it is not in a drop zone, it returns %FALSE and no further processing
+ * is necessary. Otherwise, the handler returns %TRUE. In this case, the
+ * handler is responsible for providing the necessary information for
+ * displaying feedback to the user, by calling gdk_drag_status().
+ *
+ * The default handler for this signal decides whether to accept the drop
+ * based on the type of the data.
+ *
+ * If the decision whether the drop will be accepted or rejected can't be
+ * made based solely the data format, handler may inspect the dragged data
+ * by calling one of the #GdkDrop read functions and return %TRUE to
+ * tentatively accept the drop. When the data arrives and is found to not be
+ * acceptable, a call to gtk_drop_target_deny_drop() should be made to reject
+ * the drop.
+ *
+ * Returns: whether the cursor position is in a drop zone
+ */
+ signals[ACCEPT] =
+ g_signal_new (I_("accept"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkDropTargetClass, accept),
+ g_signal_accumulator_first_wins, NULL,
+ NULL,
+ G_TYPE_BOOLEAN, 1,
+ GDK_TYPE_DROP);
+
+ /**
+ * GtkDropTarget::drag-drop:
+ * @dest: the #GtkDropTarget
+ * @drop: the #GdkDrop
+ * @x: the x coordinate of the current cursor position
+ * @y: the y coordinate of the current cursor position
+ *
+ * The ::drag-drop signal is emitted on the drop site when the user drops
+ * the data onto the widget. The signal handler must determine whether
+ * the cursor position is in a drop zone or not. If it is not in a drop
+ * zone, it returns %FALSE and no further processing is necessary.
+ *
+ * Otherwise, the handler returns %TRUE. In this case, the handler must
+ * ensure that gdk_drop_finish() is called to let the source know that
+ * the drop is done. The call to gtk_drag_finish() can be done either
+ * directly or after receiving the data.
+ *
+ * To receive the data, use one of the read functions provides by #GtkDrop
+ * and #GtkDragDest: gdk_drop_read_async(), gdk_drop_read_value_async(),
+ * gdk_drop_read_text_async(), gtk_drop_target_read_selection().
+ *
+ * You can use gtk_drop_target_get_drop() to obtain the #GtkDrop object
+ * for the ongoing operation in your signal handler. If you call one of the
+ * read functions in your handler, GTK will ensure that the #GtkDrop object
+ * stays alive until the read is completed. If you delay obtaining the data
+ * (e.g. to handle %GDK_ACTION_ASK by showing a #GtkPopover), you need to
+ * hold a reference on the #GtkDrop.
+ *
+ * Returns: whether the cursor position is in a drop zone
+ */
+ signals[DRAG_DROP] =
+ g_signal_new (I_("drag-drop"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_BOOLEAN, 3,
+ GDK_TYPE_DROP, G_TYPE_INT, G_TYPE_INT);
}
/**
- * gtk_drag_dest_set: (method)
- * @widget: a #GtkWidget
- * @flags: which types of default drag behavior to use
- * @targets: (allow-none): the drop types that this @widget will
- * accept, or %NULL. Later you can access the list with
- * gtk_drag_dest_get_target_list() and gtk_drag_dest_find_target().
- * @actions: a bitmask of possible actions for a drop onto this @widget.
- *
- * Sets a widget as a potential drop destination, and adds default behaviors.
- *
- * The default behaviors listed in @flags have an effect similar
- * to installing default handlers for the widget’s drag-and-drop signals
- * (#GtkWidget::drag-motion, #GtkWidget::drag-drop, ...). They all exist
- * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
- * sufficient to connect to the widget’s #GtkWidget::drag-data-received
- * signal to get primitive, but consistent drag-and-drop support.
+ * gtk_drop_target_new:
+ * @formats: (nullable): the supported data formats
+ * @actions: the supported actions
*
- * Things become more complicated when you try to preview the dragged data,
- * as described in the documentation for #GtkWidget::drag-motion. The default
- * behaviors described by @flags make some assumptions, that can conflict
- * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
- * invokations of gdk_drag_status() in the context of #GtkWidget::drag-motion,
- * and invokations of gdk_drag_finish() in #GtkWidget::drag-data-received.
- * Especially the later is dramatic, when your own #GtkWidget::drag-motion
- * handler calls gtk_drag_get_data() to inspect the dragged data.
+ * Creates a new #GtkDropTarget object.
*
- * There’s no way to set a default action here, you can use the
- * #GtkWidget::drag-motion callback for that. Here’s an example which selects
- * the action to use depending on whether the control key is pressed or not:
- * |[<!-- language="C" -->
- * static void
- * drag_motion (GtkWidget *widget,
- * GdkDrag *drag,
- * gint x,
- * gint y,
- * guint time)
- * {
-* GdkModifierType mask;
+ * Returns: the new #GtkDropTarget
+ */
+GtkDropTarget *
+gtk_drop_target_new (GdkContentFormats *formats,
+ GdkDragAction actions)
+{
+ return g_object_new (GTK_TYPE_DROP_TARGET,
+ "formats", formats,
+ "actions", actions,
+ NULL);
+}
+
+/**
+ * gtk_drop_target_set_formats:
+ * @dest: a #GtkDropTarget
+ * @formats: (nullable): the supported data formats
*
- * gdk_surface_get_pointer (gtk_native_get_surface (gtk_widget_get_native (widget)),
- * NULL, NULL, &mask);
- * if (mask & GDK_CONTROL_MASK)
- * gdk_drag_status (context, GDK_ACTION_COPY, time);
- * else
- * gdk_drag_status (context, GDK_ACTION_MOVE, time);
- * }
- * ]|
+ * Sets the data formats that this drop target will accept.
*/
void
-gtk_drag_dest_set (GtkWidget *widget,
- GtkDestDefaults flags,
- GdkContentFormats *targets,
- GdkDragAction actions)
+gtk_drop_target_set_formats (GtkDropTarget *dest,
+ GdkContentFormats *formats)
{
- GtkDragDestSite *site;
+ g_return_if_fail (GTK_IS_DROP_TARGET (dest));
+
+ if (dest->formats == formats)
+ return;
- g_return_if_fail (GTK_IS_WIDGET (widget));
+ if (dest->formats)
+ gdk_content_formats_unref (dest->formats);
- site = g_slice_new0 (GtkDragDestSite);
+ dest->formats = formats;
- site->flags = flags;
- site->have_drag = FALSE;
- if (targets)
- site->target_list = gdk_content_formats_ref (targets);
- else
- site->target_list = NULL;
- site->actions = actions;
- site->track_motion = FALSE;
+ if (dest->formats)
+ gdk_content_formats_ref (dest->formats);
- gtk_drag_dest_set_internal (widget, site);
+ g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_FORMATS]);
}
/**
- * gtk_drag_dest_unset: (method)
- * @widget: a #GtkWidget
+ * gtk_drop_target_get_formats:
+ * @dest: a #GtkDropTarget
*
- * Clears information about a drop destination set with
- * gtk_drag_dest_set(). The widget will no longer receive
- * notification of drags.
+ * Gets the data formats that this drop target accepts.
+ *
+ * Returns: the supported data formats
*/
-void
-gtk_drag_dest_unset (GtkWidget *widget)
+GdkContentFormats *
+gtk_drop_target_get_formats (GtkDropTarget *dest)
{
- GtkDragDestSite *old_site;
+ g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL);
+
+ return dest->formats;
+}
- g_return_if_fail (GTK_IS_WIDGET (widget));
+/**
+ * gtk_drop_target_set_actions:
+ * @dest: a #GtkDropTarget
+ * @actions: the supported actions
+ *
+ * Sets the actions that this drop target supports.
+ */
+void
+gtk_drop_target_set_actions (GtkDropTarget *dest,
+ GdkDragAction actions)
+{
+ g_return_if_fail (GTK_IS_DROP_TARGET (dest));
+
+ if (dest->actions == actions)
+ return;
- old_site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
- if (old_site)
- {
- g_signal_handlers_disconnect_by_func (widget,
- gtk_drag_dest_realized,
- old_site);
- g_signal_handlers_disconnect_by_func (widget,
- gtk_drag_dest_hierarchy_changed,
- old_site);
- }
+ dest->actions = actions;
- g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
+ g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_ACTIONS]);
}
/**
- * gtk_drag_dest_get_target_list: (method)
- * @widget: a #GtkWidget
+ * gtk_drop_target_get_actions:
+ * @dst: a #GtkDropTarget
*
- * Returns the list of targets this widget can accept from
- * drag-and-drop.
+ * Gets the actions that this drop target supports.
*
- * Returns: (nullable) (transfer none): the #GdkContentFormats, or %NULL if none
+ * Returns: the actions that this drop target supports
*/
-GdkContentFormats *
-gtk_drag_dest_get_target_list (GtkWidget *widget)
+GdkDragAction
+gtk_drop_target_get_actions (GtkDropTarget *dest)
+{
+ g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), 0);
+
+ return dest->actions;
+}
+
+static void
+gtk_drag_dest_realized (GtkWidget *widget)
{
- GtkDragDestSite *site;
+ GtkNative *native = gtk_widget_get_native (widget);
- g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+ gdk_surface_register_dnd (gtk_native_get_surface (native));
+}
- site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
+static void
+gtk_drag_dest_hierarchy_changed (GtkWidget *widget,
+ GParamSpec *pspec,
+ gpointer data)
+{
+ GtkNative *native = gtk_widget_get_native (widget);
- return site ? site->target_list : NULL;
+ if (native && gtk_widget_get_realized (GTK_WIDGET (native)))
+ gdk_surface_register_dnd (gtk_native_get_surface (native));
}
/**
- * gtk_drag_dest_set_target_list: (method)
- * @widget: a #GtkWidget that’s a drag destination
- * @target_list: (allow-none): list of droppable targets, or %NULL for none
+ * gtk_drop_target_get_drop:
+ * @dest: a #GtkDropTarget
+ *
+ * Returns the underlying #GtkDrop object for an ongoing drag.
*
- * Sets the target types that this widget can accept from drag-and-drop.
- * The widget must first be made into a drag destination with
- * gtk_drag_dest_set().
+ * Returns: (nullable): the #GtkDrop of the current drag operation, or %NULL
*/
-void
-gtk_drag_dest_set_target_list (GtkWidget *widget,
- GdkContentFormats *target_list)
+GdkDrop *
+gtk_drop_target_get_drop (GtkDropTarget *dest)
{
- GtkDragDestSite *site;
+ g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL);
- g_return_if_fail (GTK_IS_WIDGET (widget));
+ return dest->drop;
+}
- site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
+static const char *
+gtk_drop_target_match (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ GdkContentFormats *formats;
+ const char *match;
- if (!site)
- {
- g_warning ("Can't set a target list on a widget until you've called gtk_drag_dest_set() "
- "to make the widget into a drag destination");
- return;
- }
+ formats = gdk_content_formats_ref (dest->formats);
+ formats = gdk_content_formats_union_deserialize_mime_types (formats);
- if (target_list)
- gdk_content_formats_ref (target_list);
+ match = gdk_content_formats_match_mime_type (formats, gdk_drop_get_formats (drop));
- if (site->target_list)
- gdk_content_formats_unref (site->target_list);
+ gdk_content_formats_unref (formats);
- site->target_list = target_list;
+ return match;
}
/**
- * gtk_drag_dest_add_text_targets: (method)
- * @widget: a #GtkWidget that’s a drag destination
+ * gtk_drop_target_find_mimetype:
+ * @dest: a #GtkDropTarget
*
- * Add the text targets supported by #GtkSelectionData to
- * the target list of the drag destination. The targets
- * are added with @info = 0. If you need another value,
- * use gtk_target_list_add_text_targets() and
- * gtk_drag_dest_set_target_list().
+ * Returns a mimetype that is supported both by @dest and the ongoing
+ * drag. For more detailed control, you can use gdk_drop_get_formats()
+ * to obtain the content formats that are supported by the source.
+ *
+ * Returns: (nullable): a matching mimetype for the ongoing drag, or %NULL
*/
-void
-gtk_drag_dest_add_text_targets (GtkWidget *widget)
+const char *
+gtk_drop_target_find_mimetype (GtkDropTarget *dest)
{
- GdkContentFormats *target_list;
+ if (!dest->drop)
+ return NULL;
- target_list = gtk_drag_dest_get_target_list (widget);
- if (target_list)
- gdk_content_formats_ref (target_list);
- else
- target_list = gdk_content_formats_new (NULL, 0);
- target_list = gtk_content_formats_add_text_targets (target_list);
- gtk_drag_dest_set_target_list (widget, target_list);
- gdk_content_formats_unref (target_list);
+ return gtk_drop_target_match (dest, dest->drop);
}
-/**
- * gtk_drag_dest_add_image_targets: (method)
- * @widget: a #GtkWidget that’s a drag destination
- *
- * Add the image targets supported by #GtkSelectionData to
- * the target list of the drag destination. The targets
- * are added with @info = 0. If you need another value,
- * use gtk_target_list_add_image_targets() and
- * gtk_drag_dest_set_target_list().
- */
-void
-gtk_drag_dest_add_image_targets (GtkWidget *widget)
+static gboolean
+gtk_drop_target_accept (GtkDropTarget *dest,
+ GdkDrop *drop)
{
- GdkContentFormats *target_list;
+ GdkDragAction dest_actions;
+ GdkDragAction actions;
+ GdkAtom target;
+
+ dest_actions = gtk_drop_target_get_actions (dest);
- target_list = gtk_drag_dest_get_target_list (widget);
- if (target_list)
- gdk_content_formats_ref (target_list);
- else
- target_list = gdk_content_formats_new (NULL, 0);
- target_list = gtk_content_formats_add_image_targets (target_list, FALSE);
- gtk_drag_dest_set_target_list (widget, target_list);
- gdk_content_formats_unref (target_list);
+ actions = dest_actions & gdk_drop_get_actions (drop);
+ target = gtk_drop_target_match (dest, drop);
+
+ return actions && target;
}
-/**
- * gtk_drag_dest_add_uri_targets: (method)
- * @widget: a #GtkWidget that’s a drag destination
- *
- * Add the URI targets supported by #GtkSelectionData to
- * the target list of the drag destination. The targets
- * are added with @info = 0. If you need another value,
- * use gtk_target_list_add_uri_targets() and
- * gtk_drag_dest_set_target_list().
+static void
+set_drop (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ if (dest->drop == drop)
+ return;
+
+ if (dest->drop)
+ g_object_remove_weak_pointer (G_OBJECT (dest->drop), (gpointer *)&dest->drop);
+
+ dest->drop = drop;
+
+ if (dest->drop)
+ g_object_add_weak_pointer (G_OBJECT (dest->drop), (gpointer *)&dest->drop);
+}
+
+static void
+gtk_drop_target_emit_drag_enter (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ set_drop (dest, drop);
+ g_signal_emit (dest, signals[DRAG_ENTER], 0, drop);
+}
+
+static void
+gtk_drop_target_emit_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ set_drop (dest, drop);
+ g_signal_emit (dest, signals[DRAG_LEAVE], 0, drop);
+ set_drop (dest, NULL);
+}
+
+static gboolean
+gtk_drop_target_emit_accept (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ gboolean result = FALSE;
+
+ set_drop (dest, drop);
+ g_signal_emit (dest, signals[ACCEPT], 0, drop, &result);
+
+ return result;
+}
+
+static void
+gtk_drop_target_emit_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y)
+{
+ set_drop (dest, drop);
+ g_signal_emit (dest, signals[DRAG_MOTION], 0, drop, x, y);
+}
+
+static gboolean
+gtk_drop_target_emit_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y)
+{
+ gboolean result = FALSE;
+
+ set_drop (dest, drop);
+ g_signal_emit (dest, signals[DRAG_DROP], 0, drop, x, y, &result);
+
+ return result;
+}
+
+static void
+gtk_drop_target_set_contains (GtkDropTarget *dest,
+ gboolean contains)
+{
+ if (dest->contains == contains)
+ return;
+
+ dest->contains = contains;
+
+ g_object_notify_by_pspec (G_OBJECT (dest), properties[PROP_CONTAINS]);
+}
+
+static gboolean
+gtk_drop_target_get_contains (GtkDropTarget *dest)
+{
+ return dest->contains;
+}
+
+static gboolean
+gtk_drop_target_filter_event (GtkEventController *controller,
+ const GdkEvent *event)
+{
+ switch ((int)gdk_event_get_event_type (event))
+ {
+ case GDK_DRAG_ENTER:
+ case GDK_DRAG_LEAVE:
+ case GDK_DRAG_MOTION:
+ case GDK_DROP_START:
+ return GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->filter_event (controller, event);
+
+ default:;
+ }
+
+ return TRUE;
+}
+
+static void
+clear_current_dest (gpointer data, GObject *former_object)
+{
+ g_object_set_data (G_OBJECT (data), "current-dest", NULL);
+}
+
+static void
+unset_current_dest (gpointer data)
+{
+ GtkDropTarget *dest = data;
+ GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+
+ gtk_drop_target_set_contains (dest, FALSE);
+ if (widget)
+ gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
+}
+
+static GtkDropTarget *
+gtk_drop_get_current_dest (GdkDrop *drop)
+{
+ return GTK_DROP_TARGET (g_object_get_data (G_OBJECT (drop), "current-dest"));
+}
+
+static void
+gtk_drop_set_current_dest (GdkDrop *drop,
+ GtkDropTarget *dest)
+{
+ GtkDropTarget *old_dest;
+ GtkWidget *widget;
+
+ old_dest = g_object_get_data (G_OBJECT (drop), "current-dest");
+
+ if (old_dest == dest)
+ return;
+
+ if (old_dest)
+ {
+ gtk_drop_target_set_contains (old_dest, FALSE);
+
+ widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (old_dest));
+ if (widget)
+ gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE);
+
+ gtk_drop_target_emit_drag_leave (old_dest, drop);
+
+ g_object_weak_unref (G_OBJECT (old_dest), clear_current_dest, drop);
+ }
+
+ g_object_set_data_full (G_OBJECT (drop), "current-dest", dest, unset_current_dest);
+
+ if (dest)
+ {
+ g_object_weak_ref (G_OBJECT (dest), clear_current_dest, drop);
+
+ gtk_drop_target_emit_drag_enter (dest, drop);
+
+ widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ if (widget)
+ gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
+
+ gtk_drop_target_set_contains (dest, TRUE);
+ }
+}
+
+static gboolean
+gtk_drop_target_handle_event (GtkEventController *controller,
+ const GdkEvent *event)
+{
+ GtkDropTarget *dest = GTK_DROP_TARGET (controller);
+ GdkDrop *drop;
+ double x, y;
+ GtkDropStatus status;
+ gboolean found = FALSE;
+
+ drop = gdk_event_get_drop (event);
+
+ status = gtk_drop_target_get_drop_status (dest, drop);
+ if (status == GTK_DROP_STATUS_DENIED)
+ return FALSE;
+
+ gdk_event_get_coords (event, &x, &y);
+
+ switch ((int)gdk_event_get_event_type (event))
+ {
+ case GDK_DRAG_MOTION:
+ if (status != GTK_DROP_STATUS_ACCEPTED)
+ {
+ found = gtk_drop_target_emit_accept (dest, drop);
+ if (found)
+ gtk_drop_target_set_drop_status (dest, drop, GTK_DROP_STATUS_ACCEPTED);
+ }
+ else
+ found = TRUE;
+
+ if (found)
+ {
+ gdk_drop_status (drop, gtk_drop_target_get_actions (dest));
+ gtk_drop_set_current_dest (drop, dest);
+ gtk_drop_target_emit_drag_motion (dest, drop, x, y);
+ }
+ break;
+
+ case GDK_DROP_START:
+ found = gtk_drop_target_emit_drag_drop (dest, drop, x, y);
+ break;
+
+ default:
+ break;
+ }
+
+ return found;
+}
+
+/*
+ * This function is called if none of the event
+ * controllers has handled a drag event.
*/
void
-gtk_drag_dest_add_uri_targets (GtkWidget *widget)
+gtk_drag_dest_handle_event (GtkWidget *toplevel,
+ GdkEvent *event)
+{
+ GdkDrop *drop;
+ GdkEventType event_type;
+
+ g_return_if_fail (toplevel != NULL);
+ g_return_if_fail (event != NULL);
+
+ event_type = gdk_event_get_event_type (event);
+ drop = gdk_event_get_drop (event);
+
+ switch ((guint) event_type)
+ {
+ case GDK_DRAG_ENTER:
+ break;
+
+ case GDK_DRAG_LEAVE:
+ gtk_drop_set_current_dest (drop, NULL);
+ break;
+
+ case GDK_DRAG_MOTION:
+ case GDK_DROP_START:
+ gtk_drop_set_current_dest (drop, NULL);
+ gdk_drop_status (drop, 0);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+gtk_drop_target_set_widget (GtkEventController *controller,
+ GtkWidget *widget)
+{
+ GtkDropTarget *dest = GTK_DROP_TARGET (controller);
+
+ GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->set_widget (controller, widget);
+
+ if (gtk_widget_get_realized (widget))
+ gtk_drag_dest_realized (widget);
+
+ g_signal_connect (widget, "realize", G_CALLBACK (gtk_drag_dest_realized), dest);
+ g_signal_connect (widget, "notify::root", G_CALLBACK (gtk_drag_dest_hierarchy_changed), dest);
+}
+
+static void
+gtk_drop_target_unset_widget (GtkEventController *controller)
{
- GdkContentFormats *target_list;
+ GtkWidget *widget;
+
+ widget = gtk_event_controller_get_widget (controller);
- target_list = gtk_drag_dest_get_target_list (widget);
- if (target_list)
- gdk_content_formats_ref (target_list);
- else
- target_list = gdk_content_formats_new (NULL, 0);
- target_list = gtk_content_formats_add_uri_targets (target_list);
- gtk_drag_dest_set_target_list (widget, target_list);
- gdk_content_formats_unref (target_list);
+ g_signal_handlers_disconnect_by_func (widget, gtk_drag_dest_realized, controller);
+ g_signal_handlers_disconnect_by_func (widget, gtk_drag_dest_hierarchy_changed, controller);
+
+ GTK_EVENT_CONTROLLER_CLASS (gtk_drop_target_parent_class)->unset_widget (controller);
+}
+
+static void
+gtk_drag_get_data_got_data (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GTask *task = data;
+ gssize written;
+ GError *error = NULL;
+ guchar *bytes;
+ gsize size;
+ GtkSelectionData *sdata;
+ GtkDropTarget *dest;
+ GdkDrop *drop;
+ GdkDisplay *display;
+
+ written = g_output_stream_splice_finish (G_OUTPUT_STREAM (source), result, &error);
+ if (written < 0)
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ bytes = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (source));
+ size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (source));
+
+ dest = GTK_DROP_TARGET (g_task_get_source_object (task));
+ drop = GDK_DROP (g_object_get_data (G_OBJECT (task), "drop"));
+ display = GDK_DISPLAY (g_object_get_data (G_OBJECT (task), "display"));
+
+ sdata = g_slice_new0 (GtkSelectionData);
+ sdata->target = g_task_get_task_data (task);
+ sdata->type = g_task_get_task_data (task);
+ sdata->format = 8;
+ sdata->length = size;
+ sdata->data = bytes ? bytes : (guchar *)g_strdup ("");
+ sdata->display = display;
+
+ set_drop (dest, drop);
+ g_task_return_pointer (task, sdata, NULL);
+ set_drop (dest, NULL);
+
+ g_object_unref (task);
+}
+
+static void
+gtk_drag_get_data_got_stream (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GTask *task = data;
+ GInputStream *input_stream;
+ GOutputStream *output_stream;
+ GError *error = NULL;
+ const char *mime_type;
+
+ input_stream = gdk_drop_read_finish (GDK_DROP (source), result, &mime_type, &error);
+ if (input_stream == NULL)
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_set_task_data (task, (gpointer)g_intern_string (mime_type), NULL);
+
+ output_stream = g_memory_output_stream_new_resizable ();
+ g_output_stream_splice_async (output_stream,
+ input_stream,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ gtk_drag_get_data_got_data,
+ task);
+ g_object_unref (output_stream);
+ g_object_unref (input_stream);
}
/**
- * gtk_drag_dest_set_track_motion: (method)
- * @widget: a #GtkWidget that’s a drag destination
- * @track_motion: whether to accept all targets
+ * gtk_drop_target_read_selection:
+ * @dest: a #GtkDropTarget
+ * @target: the data format to read
+ * @cancellable: (nullable): a cancellable
+ * @callback: callback to call on completion
+ * @user_data: data to pass to @callback
*
- * Tells the widget to emit #GtkWidget::drag-motion and
- * #GtkWidget::drag-leave events regardless of the targets and the
- * %GTK_DEST_DEFAULT_MOTION flag.
+ * Asynchronously reads the dropped data from an ongoing
+ * drag on a #GtkDropTarget, and returns the data in a
+ * #GtkSelectionData object.
*
- * This may be used when a widget wants to do generic
- * actions regardless of the targets that the source offers.
+ * This function is meant for cases where a #GtkSelectionData
+ * object is needed, such as when using the #GtkTreeModel DND
+ * support. In most other cases, the #GdkDrop async read
+ * APIs that return in input stream or #GValue are more
+ * convenient and should be preferred.
*/
void
-gtk_drag_dest_set_track_motion (GtkWidget *widget,
- gboolean track_motion)
+gtk_drop_target_read_selection (GtkDropTarget *dest,
+ GdkAtom target,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GtkDragDestSite *site;
+ GTask *task;
+ GtkWidget *widget;
- g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GTK_IS_DROP_TARGET (dest));
- site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
+ task = g_task_new (dest, NULL, callback, user_data);
+ g_object_set_data_full (G_OBJECT (task), "drop", g_object_ref (dest->drop), g_object_unref);
- g_return_if_fail (site != NULL);
+ widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ if (widget)
+ g_object_set_data (G_OBJECT (task), "display", gtk_widget_get_display (widget));
- site->track_motion = track_motion != FALSE;
+ gdk_drop_read_async (dest->drop,
+ (const char *[2]) { target, NULL },
+ G_PRIORITY_DEFAULT,
+ NULL,
+ gtk_drag_get_data_got_stream,
+ task);
}
/**
- * gtk_drag_dest_get_track_motion: (method)
- * @widget: a #GtkWidget that’s a drag destination
+ * gtk_drop_target_read_selection_finish:
+ * @dest: a #GtkDropTarget
+ * @result: a #GAsyncResult
+ * @error: (allow-none): location to store error information on failure, or %NULL
*
- * Returns whether the widget has been configured to always
- * emit #GtkWidget::drag-motion signals.
+ * Finishes an async drop read operation, see gtk_drop_target_read_selection().
*
- * Returns: %TRUE if the widget always emits
- * #GtkWidget::drag-motion events
+ * Returns: (nullable) (transfer full): the #GtkSelectionData, or %NULL
*/
-gboolean
-gtk_drag_dest_get_track_motion (GtkWidget *widget)
+GtkSelectionData *
+gtk_drop_target_read_selection_finish (GtkDropTarget *dest,
+ GAsyncResult *result,
+ GError **error)
{
- GtkDragDestSite *site;
+ g_return_val_if_fail (GTK_IS_DROP_TARGET (dest), NULL);
- g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
- site = g_object_get_data (G_OBJECT (widget), I_("gtk-drag-dest"));
+static GtkDropStatus
+gtk_drop_target_get_drop_status (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ GHashTable *denied;
- if (site)
- return site->track_motion;
+ denied = (GHashTable *)g_object_get_data (G_OBJECT (drop), "denied-drags");
+ if (denied)
+ return GPOINTER_TO_INT (g_hash_table_lookup (denied, dest));
- return FALSE;
+ return GTK_DROP_STATUS_NONE;
+}
+
+static void
+gtk_drop_target_set_drop_status (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkDropStatus status)
+{
+ GHashTable *drags;
+
+ drags = (GHashTable *)g_object_get_data (G_OBJECT (drop), "denied-drags");
+ if (!drags)
+ {
+ drags = g_hash_table_new (NULL, NULL);
+ g_object_set_data_full (G_OBJECT (drop), "denied-drags", drags, (GDestroyNotify)g_hash_table_unref);
+ }
+
+ g_hash_table_insert (drags, dest, GINT_TO_POINTER (status));
+
+ if (dest == gtk_drop_get_current_dest (drop))
+ {
+ gdk_drop_status (drop, 0);
+ gtk_drop_set_current_dest (drop, NULL);
+ }
}
/**
- * gtk_drag_dest_find_target: (method)
- * @widget: drag destination widget
- * @drop: #GdkDrop
- * @target_list: (allow-none): list of droppable targets, or %NULL to use
- * gtk_drag_dest_get_target_list (@widget).
+ * gtk_drop_target_deny_drop:
+ * @dest: a #GtkDropTarget
+ * @drop: the #GdkDrop of an ongoing drag operation
*
- * Looks for a match between the supported targets of @drop and the
- * @dest_target_list, returning the first matching target, otherwise
- * returning %NULL. @dest_target_list should usually be the return
- * value from gtk_drag_dest_get_target_list(), but some widgets may
- * have different valid targets for different parts of the widget; in
- * that case, they will have to implement a drag_motion handler that
- * passes the correct target list to this function.
+ * Sets the @drop as not accepted on this drag site.
*
- * Returns: (transfer none) (nullable): first target that the source offers
- * and the dest can accept, or %NULL
+ * This function should be used when delaying the decision
+ * on whether to accept a drag or not until after reading
+ * the data.
*/
-const char *
-gtk_drag_dest_find_target (GtkWidget *widget,
- GdkDrop *drop,
- GdkContentFormats *target_list)
+void
+gtk_drop_target_deny_drop (GtkDropTarget *dest,
+ GdkDrop *drop)
{
- g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
- g_return_val_if_fail (GDK_IS_DROP (drop), NULL);
+ g_return_if_fail (GTK_IS_DROP_TARGET (dest));
+ g_return_if_fail (GDK_IS_DROP (drop));
- if (target_list == NULL)
- target_list = gtk_drag_dest_get_target_list (widget);
-
- if (target_list == NULL)
- return NULL;
-
- return gdk_content_formats_match_mime_type (target_list,
- gdk_drop_get_formats (drop));
+ gtk_drop_target_set_drop_status (dest, drop, GTK_DROP_STATUS_DENIED);
}
-
diff --git a/gtk/gtkdragdest.h b/gtk/gtkdragdest.h
index f241f514a7..ac18face04 100644
--- a/gtk/gtkdragdest.h
+++ b/gtk/gtkdragdest.h
@@ -37,65 +37,58 @@
G_BEGIN_DECLS
-/**
- * GtkDestDefaults:
- * @GTK_DEST_DEFAULT_MOTION: If set for a widget, GTK+, during a drag over this
- * widget will check if the drag matches this widget’s list of possible formats
- * and actions.
- * GTK+ will then call gdk_drag_status() as appropriate.
- * @GTK_DEST_DEFAULT_HIGHLIGHT: If set for a widget, GTK+ will draw a highlight on
- * this widget as long as a drag is over this widget and the widget drag format
- * and action are acceptable.
- * @GTK_DEST_DEFAULT_DROP: If set for a widget, when a drop occurs, GTK+ will
- * will check if the drag matches this widget’s list of possible formats and
- * actions. If so, GTK+ will call gtk_drag_get_data() on behalf of the widget.
- * Whether or not the drop is successful, GTK+ will call gdk_drag_finish(). If
- * the action was a move, then if the drag was successful, then %TRUE will be
- * passed for the @delete parameter to gdk_drag_finish().
- * @GTK_DEST_DEFAULT_ALL: If set, specifies that all default actions should
- * be taken.
- *
- * The #GtkDestDefaults enumeration specifies the various
- * types of action that will be taken on behalf
- * of the user for a drag destination site.
- */
-typedef enum {
- GTK_DEST_DEFAULT_MOTION = 1 << 0,
- GTK_DEST_DEFAULT_HIGHLIGHT = 1 << 1,
- GTK_DEST_DEFAULT_DROP = 1 << 2,
- GTK_DEST_DEFAULT_ALL = 0x07
-} GtkDestDefaults;
+typedef struct _GtkDropTarget GtkDropTarget;
+
+
+#define GTK_TYPE_DROP_TARGET (gtk_drop_target_get_type ())
+#define GTK_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DROP_TARGET, GtkDropTarget))
+#define GTK_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DROP_TARGET, GtkDropTargetClass))
+#define GTK_IS_DROP_TARGET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DROP_TARGET))
+#define GTK_IS_DROP_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DROP_TARGET))
+#define GTK_DROP_TARGET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DROP_TARGET, GtkDropTargetClass))
+
+typedef struct _GtkDropTargetClass GtkDropTargetClass;
GDK_AVAILABLE_IN_ALL
-void gtk_drag_dest_set (GtkWidget *widget,
- GtkDestDefaults flags,
- GdkContentFormats *targets,
- GdkDragAction actions);
+GType gtk_drop_target_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
-void gtk_drag_dest_unset (GtkWidget *widget);
+GtkDropTarget *gtk_drop_target_new (GdkContentFormats *formats,
+ GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
-const char * gtk_drag_dest_find_target (GtkWidget *widget,
- GdkDrop *drop,
- GdkContentFormats *target_list);
+void gtk_drop_target_set_formats (GtkDropTarget *dest,
+ GdkContentFormats *formats);
GDK_AVAILABLE_IN_ALL
-GdkContentFormats* gtk_drag_dest_get_target_list (GtkWidget *widget);
+GdkContentFormats *gtk_drop_target_get_formats (GtkDropTarget *dest);
+
GDK_AVAILABLE_IN_ALL
-void gtk_drag_dest_set_target_list (GtkWidget *widget,
- GdkContentFormats *target_list);
+void gtk_drop_target_set_actions (GtkDropTarget *dest,
+ GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
-void gtk_drag_dest_add_text_targets (GtkWidget *widget);
+GdkDragAction gtk_drop_target_get_actions (GtkDropTarget *dest);
+
GDK_AVAILABLE_IN_ALL
-void gtk_drag_dest_add_image_targets (GtkWidget *widget);
+GdkDrop *gtk_drop_target_get_drop (GtkDropTarget *dest);
+
GDK_AVAILABLE_IN_ALL
-void gtk_drag_dest_add_uri_targets (GtkWidget *widget);
+const char *gtk_drop_target_find_mimetype (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
-void gtk_drag_dest_set_track_motion (GtkWidget *widget,
- gboolean track_motion);
+void gtk_drop_target_read_selection (GtkDropTarget *dest,
+ GdkAtom target,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data);
+GDK_AVAILABLE_IN_ALL
+GtkSelectionData *gtk_drop_target_read_selection_finish
+ (GtkDropTarget *dest,
+ GAsyncResult *result,
+ GError **error);
+
GDK_AVAILABLE_IN_ALL
-gboolean gtk_drag_dest_get_track_motion (GtkWidget *widget);
+void gtk_drop_target_deny_drop (GtkDropTarget *dest,
+ GdkDrop *drop);
G_END_DECLS
diff --git a/gtk/gtkdragdestprivate.h b/gtk/gtkdragdestprivate.h
new file mode 100644
index 0000000000..0113f98709
--- /dev/null
+++ b/gtk/gtkdragdestprivate.h
@@ -0,0 +1,32 @@
+
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2020 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 __GTK_DRAG_DEST_PRIVATE_H__
+#define __GTK_DRAG_DEST_PRIVATE_H__
+
+#include "gtkdragdest.h"
+
+G_BEGIN_DECLS
+
+
+void gtk_drag_dest_handle_event (GtkWidget *toplevel,
+ GdkEvent *event);
+
+G_END_DECLS
+
+#endif
diff --git a/gtk/gtkdragicon.c b/gtk/gtkdragicon.c
index 8b6edbbf97..ae36e1e1dc 100644
--- a/gtk/gtkdragicon.c
+++ b/gtk/gtkdragicon.c
@@ -23,8 +23,25 @@
#include "gtkintl.h"
#include "gtkwidgetprivate.h"
#include "gtkcssnodeprivate.h"
+#include "gtknativeprivate.h"
+#include "gtkpicture.h"
+/**
+ * SECTION:gtkdragicon
+ * @Short_description: A toplevel to use as drag icon
+ * @Title: GtkDragIcon
+ *
+ * GtkDragIcon is a #GtkNative implementation with the sole purpose
+ * to serve as a drag icon during DND operations. A drag icon moves
+ * with the pointer during a drag operation and is destroyed when
+ * the drag ends.
+ *
+ * To set up a drag icon and associate it with an ongoing drag operation,
+ * use gtk_drag_icon_set_from_paintable(). It is also possible to create
+ * a GtkDragIcon with gtk_drag_icon_new_for_drag(() and populate it
+ * with widgets yourself.
+ */
struct _GtkDragIcon
{
GtkWidget parent_instance;
@@ -374,6 +391,63 @@ gtk_drag_icon_new (void)
return g_object_new (GTK_TYPE_DRAG_ICON, NULL);
}
+/**
+ * gtk_drag_icon_new_for_drag:
+ * @drag: a #GtkDrag
+ *
+ * Creates a #GtkDragIcon and associates it with the drag operation.
+ *
+ * Returns: the new #GtkDragIcon
+ */
+GtkWidget *
+gtk_drag_icon_new_for_drag (GdkDrag *drag)
+{
+ GtkWidget *icon;
+
+ g_return_val_if_fail (GDK_IS_DRAG (drag), NULL);
+
+ icon = g_object_new (GTK_TYPE_DRAG_ICON, NULL);
+
+ gtk_drag_icon_set_surface (GTK_DRAG_ICON (icon), gdk_drag_get_drag_surface (drag));
+
+ return icon;
+}
+
+/**
+ * gtk_drag_icon_set_from_paintable:
+ * @drag: a #GdkDrag
+ * @paintable: a #GdkPaintable to display
+ * @hot_x: X coordinate of the hotspot
+ * @hot_y: Y coordinate of the hotspot
+ *
+ * Creates a #GtkDragIcon that shows @paintable, and associates
+ * it with the drag operation. The hotspot position on the paintable
+ * is aligned with the hotspot of the cursor.
+ */
+void
+gtk_drag_icon_set_from_paintable (GdkDrag *drag,
+ GdkPaintable *paintable,
+ int hot_x,
+ int hot_y)
+{
+ GtkWidget *icon;
+ GtkWidget *picture;
+
+ gdk_drag_set_hotspot (drag, hot_x, hot_y);
+
+ icon = gtk_drag_icon_new_for_drag (drag);
+
+ picture = gtk_picture_new_for_paintable (paintable);
+ gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
+ gtk_container_add (GTK_CONTAINER (icon), picture);
+
+ g_object_set_data_full (G_OBJECT (drag),
+ "icon",
+ g_object_ref_sink (icon),
+ (GDestroyNotify)gtk_widget_destroy);
+ gtk_widget_show (icon);
+}
+
void
gtk_drag_icon_set_surface (GtkDragIcon *icon,
GdkSurface *surface)
diff --git a/gtk/gtkdragicon.h b/gtk/gtkdragicon.h
new file mode 100644
index 0000000000..b8d7ffb38a
--- /dev/null
+++ b/gtk/gtkdragicon.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright © 2020 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.1 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/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#ifndef __GTK_DRAG_ICON_H__
+#define __GTK_DRAG_ICON_H__
+
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkcontainer.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_DRAG_ICON (gtk_drag_icon_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkDragIcon, gtk_drag_icon, GTK, DRAG_ICON, GtkContainer)
+
+GDK_AVAILABLE_IN_ALL
+GtkWidget * gtk_drag_icon_new_for_drag (GdkDrag *drag);
+GDK_AVAILABLE_IN_ALL
+void gtk_drag_icon_set_from_paintable (GdkDrag *drag,
+ GdkPaintable *paintable,
+ int hot_x,
+ int hot_y);
+
+G_END_DECLS
+
+
+#endif /* __GTK_DRAG_ICON_H__ */
diff --git a/gtk/gtkdragiconprivate.h b/gtk/gtkdragiconprivate.h
index 507ac908ea..c0fd36a672 100644
--- a/gtk/gtkdragiconprivate.h
+++ b/gtk/gtkdragiconprivate.h
@@ -26,14 +26,10 @@
#define __GTK_DRAG_ICON_PRIVATE_H__
#include <gio/gio.h>
-#include <gtk/gtk.h>
+#include <gtk/gtkdragicon.h>
G_BEGIN_DECLS
-#define GTK_TYPE_DRAG_ICON (gtk_drag_icon_get_type ())
-
-G_DECLARE_FINAL_TYPE (GtkDragIcon, gtk_drag_icon, GTK, DRAG_ICON, GtkContainer)
-
GtkWidget * gtk_drag_icon_new (void);
void gtk_drag_icon_set_surface (GtkDragIcon *icon,
diff --git a/gtk/gtkdragsource.c b/gtk/gtkdragsource.c
index 8cc461464e..ffa82273d6 100644
--- a/gtk/gtkdragsource.c
+++ b/gtk/gtkdragsource.c
@@ -26,365 +26,677 @@
#include "gtkdragsource.h"
-#include "gtkdnd.h"
-#include "gtkdndprivate.h"
#include "gtkgesturedrag.h"
+#include "gtkgesturesingleprivate.h"
#include "gtkimagedefinitionprivate.h"
+#include "gtknative.h"
+#include "gtkwidgetprivate.h"
#include "gtkintl.h"
+#include "gtkstylecontext.h"
+#include "gtkimageprivate.h"
+#include "gtkdragiconprivate.h"
+#include "gtkprivate.h"
+#include "gtkmarshalers.h"
+#include "gtkicontheme.h"
+#include "gtkpicture.h"
+#include "gtksettingsprivate.h"
+#include "gtkgesturesingle.h"
+/**
+ * SECTION:gtkdragsource
+ * @Short_description: Event controller to initiate DND operations
+ * @Title: GtkDragSource
+ *
+ * GtkDragSource is an auxiliary object that is used to initiate
+ * Drag-And-Drop operations. It can be set up with the necessary
+ * ingredients for a DND operation ahead of time. This includes
+ * the source for the data that is being transferred, in the form
+ * of a #GdkContentProvider, the desired action, and the icon to
+ * use during the drag operation. After setting it up, the drag
+ * source must be added to a widget as an event controller, using
+ * gtk_widget_add_controller().
+ *
+ * Setting up the content provider and icon ahead of time only
+ * makes sense when the data does not change. More commonly, you
+ * will want to set them up just in time. To do so, #GtkDragSource
+ * has #GtkDragSource::prepare and #GtkDragSource::drag-begin signals.
+ * The ::prepare signal is emitted before a drag is started, and
+ * can be used to set the content provider and actions that the
+ * drag should be started with. The ::drag-begin signal is emitted
+ * after the #GdkDrag object has been created, and can be used
+ * to set up the drag icon.
+ *
+ * During the DND operation, GtkDragSource emits signals that
+ * can be used to obtain updates about the status of the operation,
+ * but it is not normally necessary to connect to any signals,
+ * except for one case: when the supported actions include
+ * %GDK_DRAG_MOVE, you need to listen for the
+ * #GtkDragSource::drag-end signal and delete the
+ * data after it has been transferred.
+ */
+
+struct _GtkDragSource
+{
+ GtkGestureSingle parent_instance;
+
+ GdkContentProvider *content;
+ GdkDragAction actions;
+
+ GdkPaintable *paintable;
+ int hot_x;
+ int hot_y;
-typedef struct _GtkDragSourceSite GtkDragSourceSite;
+ gdouble start_x;
+ gdouble start_y;
-struct _GtkDragSourceSite
+ GdkDrag *drag;
+};
+
+struct _GtkDragSourceClass
{
- GdkModifierType start_button_mask;
- GdkContentFormats *target_list; /* Targets for drag data */
- GdkDragAction actions; /* Possible actions */
+ GtkGestureSingleClass parent_class;
- GtkImageDefinition *image_def;
- GtkGesture *drag_gesture;
+ GdkContentProvider *(* prepare) (GtkDragSource *source,
+ double x,
+ double y);
};
+
+enum {
+ PROP_CONTENT = 1,
+ PROP_ACTIONS,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+enum {
+ PREPARE,
+ DRAG_BEGIN,
+ DRAG_END,
+ DRAG_CANCEL,
+ NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS];
+
+static void gtk_drag_source_dnd_finished_cb (GdkDrag *drag,
+ GtkDragSource *source);
+static void gtk_drag_source_cancel_cb (GdkDrag *drag,
+ GdkDragCancelReason reason,
+ GtkDragSource *source);
+
+static GdkContentProvider *gtk_drag_source_prepare (GtkDragSource *source,
+ double x,
+ double y);
+
+static void gtk_drag_source_drag_begin (GtkDragSource *source);
+
+G_DEFINE_TYPE (GtkDragSource, gtk_drag_source, GTK_TYPE_GESTURE_SINGLE);
+
+static void
+gtk_drag_source_init (GtkDragSource *source)
+{
+ source->actions = GDK_ACTION_COPY;
+}
+
+static void
+gtk_drag_source_finalize (GObject *object)
+{
+ GtkDragSource *source = GTK_DRAG_SOURCE (object);
+
+ g_clear_object (&source->content);
+ g_clear_object (&source->paintable);
+
+ G_OBJECT_CLASS (gtk_drag_source_parent_class)->finalize (object);
+}
+
+static void
+gtk_drag_source_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkDragSource *source = GTK_DRAG_SOURCE (object);
+ switch (prop_id)
+ {
+ case PROP_CONTENT:
+ gtk_drag_source_set_content (source, g_value_get_object (value));
+ break;
+
+ case PROP_ACTIONS:
+ gtk_drag_source_set_actions (source, g_value_get_flags (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
static void
-gtk_drag_source_gesture_begin (GtkGesture *gesture,
- GdkEventSequence *sequence,
- gpointer data)
+gtk_drag_source_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- GtkDragSourceSite *site = data;
- guint button;
+ GtkDragSource *source = GTK_DRAG_SOURCE (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONTENT:
+ g_value_set_object (value, gtk_drag_source_get_content (source));
+ break;
+
+ case PROP_ACTIONS:
+ g_value_set_flags (value, gtk_drag_source_get_actions (source));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static gboolean
+gtk_drag_source_filter_event (GtkEventController *controller,
+ const GdkEvent *event)
+{
+ /* Let touchpad swipe events go through, only if they match n-points */
+ if (gdk_event_get_event_type (event) == GDK_TOUCHPAD_SWIPE)
+ {
+ guint n_points;
+ guint n_fingers;
+
+ g_object_get (G_OBJECT (controller), "n-points", &n_points, NULL);
+ gdk_event_get_touchpad_gesture_n_fingers (event, &n_fingers);
+
+ if (n_fingers == n_points)
+ return FALSE;
+ else
+ return TRUE;
+ }
- if (gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)))
- button = 1;
- else
- button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+ return GTK_EVENT_CONTROLLER_CLASS (gtk_drag_source_parent_class)->filter_event (controller, event);
+}
- g_assert (button >= 1);
+static void
+gtk_drag_source_begin (GtkGesture *gesture,
+ GdkEventSequence *sequence)
+{
+ GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
+ GdkEventSequence *current;
- if (!site->start_button_mask ||
- !(site->start_button_mask & (GDK_BUTTON1_MASK << (button - 1))))
- gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
+ current = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
+
+ gtk_gesture_get_point (gesture, current, &source->start_x, &source->start_y);
}
static void
-gtk_drag_source_gesture_update (GtkGesture *gesture,
- GdkEventSequence *sequence,
- gpointer data)
+gtk_drag_source_update (GtkGesture *gesture,
+ GdkEventSequence *sequence)
{
- gdouble start_x, start_y, offset_x, offset_y;
- GtkDragSourceSite *site = data;
+ GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
GtkWidget *widget;
+ double x, y;
+
+ if (!gtk_gesture_is_recognized (gesture))
+ return;
+
+ gtk_gesture_get_point (gesture, sequence, &x, &y);
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
- if (gtk_gesture_is_recognized (site->drag_gesture))
+ if (gtk_drag_check_threshold (widget, source->start_x, source->start_y, x, y))
{
- gtk_gesture_drag_get_start_point (GTK_GESTURE_DRAG (site->drag_gesture),
- &start_x, &start_y);
- gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (site->drag_gesture),
- &offset_x, &offset_y);
-
- if (gtk_drag_check_threshold (widget, start_x, start_y,
- start_x + offset_x, start_y + offset_y))
- {
- GdkDevice *device = gtk_gesture_get_device (site->drag_gesture);
-
- gtk_event_controller_reset (GTK_EVENT_CONTROLLER (site->drag_gesture));
-
- gtk_drag_begin_internal (widget,
- device,
- site->image_def, site->target_list,
- site->actions,
- start_x, start_y);
- }
+ gtk_drag_source_drag_begin (source);
}
}
static void
-gtk_drag_source_site_destroy (gpointer data)
+gtk_drag_source_class_init (GtkDragSourceClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
+ GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (class);
+
+ object_class->finalize = gtk_drag_source_finalize;
+ object_class->set_property = gtk_drag_source_set_property;
+ object_class->get_property = gtk_drag_source_get_property;
+
+ controller_class->filter_event = gtk_drag_source_filter_event;
+
+ gesture_class->begin = gtk_drag_source_begin;
+ gesture_class->update = gtk_drag_source_update;
+ gesture_class->end = NULL;
+
+ class->prepare = gtk_drag_source_prepare;
+
+ /**
+ * GtkDragSource:content:
+ *
+ * The data that is offered by drag operations from this source,
+ * in the form of a #GdkContentProvider.
+ */
+ properties[PROP_CONTENT] =
+ g_param_spec_object ("content",
+ P_("Content"),
+ P_("The content provider for the dragged data"),
+ GDK_TYPE_CONTENT_PROVIDER,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GtkDragSource:actions:
+ *
+ * The actions that are supported by drag operations from the source.
+ *
+ * Note that you must handle the #GtkDragSource::drag-end signal
+ * if the actions include %GDK_ACTION_MOVE.
+ */
+ properties[PROP_ACTIONS] =
+ g_param_spec_flags ("actions",
+ P_("Actions"),
+ P_("Supported actions"),
+ GDK_TYPE_DRAG_ACTION, GDK_ACTION_COPY,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+
+ /**
+ * GtkDragSource::prepare:
+ * @source: the #GtkDragSource
+ * @x: the X coordinate of the drag starting point
+ * @y: the Y coordinate fo the drag starting point
+ *
+ * The ::prepare signal is emitted when a drag is about to be initiated.
+ * It returns the * #GdkContentProvider to use for the drag that is about
+ * to start. The default handler for this signal returns the value of
+ * the #GtkDragSource::content property, so if you set up that property
+ * ahead of time, you don't need to connect to this signal.
+ *
+ * Returns: (transfer full) (nullable): a #GdkContentProvider, or %NULL
+ */
+ signals[PREPARE] =
+ g_signal_new (I_("prepare"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkDragSourceClass, prepare),
+ g_signal_accumulator_first_wins, NULL,
+ NULL,
+ GDK_TYPE_CONTENT_PROVIDER, 2,
+ G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+ /**
+ * GtkDragSource::drag-begin:
+ * @source: the #GtkDragSource
+ * @drag: the #GtkDrag object
+ *
+ * The ::drag-begin signal is emitted on the drag source when a drag
+ * is started. It can be used to e.g. set a custom drag icon with
+ * gtk_drag_source_set_icon().
+ */
+ signals[DRAG_BEGIN] =
+ g_signal_new (I_("drag-begin"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ GDK_TYPE_DRAG);
+
+ /**
+ * GtkDragSource::drag-end:
+ * @source: the #GtkDragSource
+ * @drag: the #GtkDrag object
+ * @delete_data: %TRUE if the drag was performing %GDK_ACTION_MOVE,
+ * and the data should be deleted
+ *
+ * The ::drag-end signal is emitted on the drag source when a drag is
+ * finished. A typical reason to connect to this signal is to undo
+ * things done in #GtkDragSource::prepare or #GtkDragSource::drag-begin.
+ */
+ signals[DRAG_END] =
+ g_signal_new (I_("drag-end"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ GDK_TYPE_DRAG,
+ G_TYPE_BOOLEAN);
+
+ /**
+ * GtkDragSource::drag-cancel:
+ * @source: the #GtkDragSource
+ * @drag: the #GtkDrag object
+ * @reason: information on why the drag failed
+ *
+ * The ::drag-cancel signal is emitted on the drag source when a drag has
+ * failed. The signal handler may handle a failed drag operation based on
+ * the type of error. It should return %TRUE if the failure has been handled
+ * and the default "drag operation failed" animation should not be shown.
+ *
+ * Returns: %TRUE if the failed drag operation has been already handled
+ */
+ signals[DRAG_CANCEL] =
+ g_signal_new (I_("drag-cancel"),
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ _gtk_boolean_handled_accumulator, NULL,
+ _gtk_marshal_BOOLEAN__OBJECT_ENUM,
+ G_TYPE_BOOLEAN, 2,
+ GDK_TYPE_DRAG,
+ GDK_TYPE_DRAG_CANCEL_REASON);
+}
+
+static GdkContentProvider *
+gtk_drag_source_prepare (GtkDragSource *source,
+ double x,
+ double y)
{
- GtkDragSourceSite *site = data;
+ if (source->actions == 0)
+ return NULL;
- if (site->target_list)
- gdk_content_formats_unref (site->target_list);
+ if (source->content == NULL)
+ return NULL;
- gtk_image_definition_unref (site->image_def);
- /* This gets called only during widget finalization.
- * And widget finalization takes care of gestures. */
- g_slice_free (GtkDragSourceSite, site);
+ return g_object_ref (source->content);
}
-/**
- * gtk_drag_source_set: (method)
- * @widget: a #GtkWidget
- * @start_button_mask: the bitmask of buttons that can start the drag
- * @targets: (allow-none): the targets that the drag will support,
- * may be %NULL
- * @actions: the bitmask of possible actions for a drag from this widget
- *
- * Sets up a widget so that GTK+ will start a drag operation when the user
- * clicks and drags on the widget. The widget must have a window.
- */
-void
-gtk_drag_source_set (GtkWidget *widget,
- GdkModifierType start_button_mask,
- GdkContentFormats *targets,
- GdkDragAction actions)
+static void
+drag_end (GtkDragSource *source,
+ gboolean success)
{
- GtkDragSourceSite *site;
+ gboolean delete_data;
- g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_dnd_finished_cb, source);
+ g_signal_handlers_disconnect_by_func (source->drag, gtk_drag_source_cancel_cb, source);
- site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
+ delete_data = success && gdk_drag_get_selected_action (source->drag) == GDK_ACTION_MOVE;
- if (site)
- {
- if (site->target_list)
- gdk_content_formats_unref (site->target_list);
- }
- else
+ g_signal_emit (source, signals[DRAG_END], 0, source->drag, delete_data);
+
+ gdk_drag_drop_done (source->drag, success);
+ g_clear_object (&source->drag);
+}
+
+static void
+gtk_drag_source_dnd_finished_cb (GdkDrag *drag,
+ GtkDragSource *source)
+{
+ drag_end (source, TRUE);
+}
+
+static void
+gtk_drag_source_cancel_cb (GdkDrag *drag,
+ GdkDragCancelReason reason,
+ GtkDragSource *source)
+{
+ gboolean success = FALSE;
+
+ g_signal_emit (source, signals[DRAG_CANCEL], 0, source->drag, reason, &success);
+ drag_end (source, FALSE);
+}
+
+static void
+gtk_drag_source_drag_begin (GtkDragSource *source)
+{
+ GtkWidget *widget;
+ GdkDevice *device;
+ int x, y;
+ GtkNative *native;
+ GdkSurface *surface;
+ double px, py;
+ int dx, dy;
+ GdkContentProvider *content = NULL;
+
+ widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
+ device = gtk_gesture_get_device (GTK_GESTURE (source));
+
+ if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+ device = gdk_device_get_associated_device (device);
+
+ native = gtk_widget_get_native (widget);
+ surface = gtk_native_get_surface (native);
+
+ gtk_widget_translate_coordinates (widget, GTK_WIDGET (native), source->start_x, source->start_y, &x, &y);
+ gdk_surface_get_device_position (surface, device, &px, &py, NULL);
+
+ dx = round (px) - x;
+ dy = round (py) - y;
+
+ g_signal_emit (source, signals[PREPARE], 0, source->start_x, source->start_y, &content);
+ if (!content)
+ return;
+
+ source->drag = gdk_drag_begin (surface, device, content, source->actions, dx, dy);
+
+ g_object_unref (content);
+
+ if (source->drag == NULL)
+ return;
+
+ gtk_widget_reset_controllers (widget);
+
+ g_signal_emit (source, signals[DRAG_BEGIN], 0, source->drag);
+
+ if (!source->paintable)
{
- site = g_slice_new0 (GtkDragSourceSite);
- site->image_def = gtk_image_definition_new_empty ();
- site->drag_gesture = gtk_gesture_drag_new ();
- gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (site->drag_gesture),
- GTK_PHASE_CAPTURE);
- gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (site->drag_gesture), 0);
- g_signal_connect (site->drag_gesture, "begin",
- G_CALLBACK (gtk_drag_source_gesture_begin),
- site);
- g_signal_connect (site->drag_gesture, "update",
- G_CALLBACK (gtk_drag_source_gesture_update),
- site);
- gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (site->drag_gesture));
-
- g_object_set_data_full (G_OBJECT (widget),
- I_("gtk-site-data"),
- site, gtk_drag_source_site_destroy);
+ GtkIconTheme *theme;
+
+ theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (widget));
+ source->paintable = gtk_icon_theme_load_icon (theme, "text-x-generic", 32, 0, NULL);
+ source->hot_x = 0;
+ source->hot_y = 0;
}
- site->start_button_mask = start_button_mask;
+ gtk_drag_icon_set_from_paintable (source->drag, source->paintable, source->hot_x, source->hot_y);
- if (targets)
- site->target_list = gdk_content_formats_ref (targets);
- else
- site->target_list = NULL;
-
- site->actions = actions;
+ g_signal_connect (source->drag, "dnd-finished",
+ G_CALLBACK (gtk_drag_source_dnd_finished_cb), source);
+ g_signal_connect (source->drag, "cancel",
+ G_CALLBACK (gtk_drag_source_cancel_cb), source);
}
/**
- * gtk_drag_source_unset: (method)
- * @widget: a #GtkWidget
+ * gtk_drag_source_new:
*
- * Undoes the effects of gtk_drag_source_set().
+ * Creates a new #GtkDragSource object.
+ *
+ * Returns: the new #GtkDragSource
*/
-void
-gtk_drag_source_unset (GtkWidget *widget)
+GtkDragSource *
+gtk_drag_source_new (void)
{
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
- g_object_set_data (G_OBJECT (widget), I_("gtk-site-data"), NULL);
+ return g_object_new (GTK_TYPE_DRAG_SOURCE, NULL);
}
/**
- * gtk_drag_source_get_target_list: (method)
- * @widget: a #GtkWidget
+ * gtk_drag_source_get_content:
+ * @source: a #GtkDragSource
*
- * Gets the list of targets this widget can provide for
- * drag-and-drop.
+ * Gets the current content provider of a #GtkDragSource.
*
- * Returns: (nullable) (transfer none): the #GdkContentFormats, or %NULL if none
+ * Returns: the #GtkContentProvider of @source
*/
-GdkContentFormats *
-gtk_drag_source_get_target_list (GtkWidget *widget)
+GdkContentProvider *
+gtk_drag_source_get_content (GtkDragSource *source)
{
- GtkDragSourceSite *site;
-
- g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+ g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
- site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
-
- return site ? site->target_list : NULL;
+ return source->content;
}
/**
- * gtk_drag_source_set_target_list: (method)
- * @widget: a #GtkWidget that’s a drag source
- * @target_list: (allow-none): list of draggable targets, or %NULL for none
+ * gtk_drag_source_set_content:
+ * @source: a #GtkDragSource
+ * @content: (nullable): a #GtkContentProvider, or %NULL
+ *
+ * Sets a content provider on a #GtkDragSource.
*
- * Changes the target types that this widget offers for drag-and-drop.
- * The widget must first be made into a drag source with
- * gtk_drag_source_set().
+ * When the data is requested in the cause of a
+ * DND operation, it will be obtained from the
+ * content provider.
+ *
+ * This function can be called before a drag is started,
+ * or in a handler for the #GtkDragSource::prepare signal.
+ *
+ * You may consider setting the content provider back to
+ * %NULL in a #GTkDragSource::drag-end signal handler.
*/
void
-gtk_drag_source_set_target_list (GtkWidget *widget,
- GdkContentFormats *target_list)
+gtk_drag_source_set_content (GtkDragSource *source,
+ GdkContentProvider *content)
{
- GtkDragSourceSite *site;
-
- g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
- site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
- if (site == NULL)
- {
- g_warning ("gtk_drag_source_set_target_list() requires the widget "
- "to already be a drag source.");
- return;
- }
+ if (!g_set_object (&source->content, content))
+ return;
- if (target_list)
- gdk_content_formats_ref (target_list);
-
- if (site->target_list)
- gdk_content_formats_unref (site->target_list);
-
- site->target_list = target_list;
+ g_object_notify_by_pspec (G_OBJECT (source), properties[PROP_CONTENT]);
}
/**
- * gtk_drag_source_add_text_targets: (method)
- * @widget: a #GtkWidget that’s is a drag source
+ * gtk_drag_source_get_actions:
+ * @source: a #GtkDragSource
*
- * Add the text targets supported by #GtkSelectionData to
- * the target list of the drag source. The targets
- * are added with @info = 0. If you need another value,
- * use gtk_content_formats_add_text_targets() and
- * gtk_drag_source_set_target_list().
+ * Gets the actions that are currently set on the #GtkDragSource.
+ *
+ * Returns: the actions set on @source
*/
-void
-gtk_drag_source_add_text_targets (GtkWidget *widget)
+GdkDragAction
+gtk_drag_source_get_actions (GtkDragSource *source)
{
- GdkContentFormats *target_list;
-
- target_list = gtk_drag_source_get_target_list (widget);
- if (target_list)
- gdk_content_formats_ref (target_list);
- else
- target_list = gdk_content_formats_new (NULL, 0);
- target_list = gtk_content_formats_add_text_targets (target_list);
- gtk_drag_source_set_target_list (widget, target_list);
- gdk_content_formats_unref (target_list);
+ g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), 0);
+
+ return source->actions;
}
/**
- * gtk_drag_source_add_image_targets: (method)
- * @widget: a #GtkWidget that’s is a drag source
+ * gtk_drag_source_set_actions:
+ * @source: a #GtkDragSource
+ * @actions: the actions to offer
+ *
+ * Sets the actions on the #GtkDragSource.
*
- * Add the writable image targets supported by #GtkSelectionData to
- * the target list of the drag source. The targets
- * are added with @info = 0. If you need another value,
- * use gtk_target_list_add_image_targets() and
- * gtk_drag_source_set_target_list().
+ * During a DND operation, the actions are offered
+ * to potential drop targets. If @actions include
+ * %GDK_ACTION_MOVE, you need to listen to the
+ * #GtkDraGSource::drag-end signal and handle
+ * @delete_data being %TRUE.
+ *
+ * This function can be called before a drag is started,
+ * or in a handler for the #GtkDragSource::prepare signal.
*/
void
-gtk_drag_source_add_image_targets (GtkWidget *widget)
+gtk_drag_source_set_actions (GtkDragSource *source,
+ GdkDragAction actions)
{
- GdkContentFormats *target_list;
-
- target_list = gtk_drag_source_get_target_list (widget);
- if (target_list)
- gdk_content_formats_ref (target_list);
- else
- target_list = gdk_content_formats_new (NULL, 0);
- target_list = gtk_content_formats_add_image_targets (target_list, TRUE);
- gtk_drag_source_set_target_list (widget, target_list);
- gdk_content_formats_unref (target_list);
+ g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+
+ if (source->actions == actions)
+ return;
+
+ source->actions = actions;
+
+ g_object_notify_by_pspec (G_OBJECT (source), properties[PROP_ACTIONS]);
}
/**
- * gtk_drag_source_add_uri_targets: (method)
- * @widget: a #GtkWidget that’s is a drag source
+ * gtk_drag_source_set_icon:
+ * @source: a #GtkDragSource
+ * @paintable: (nullable): the #GtkPaintable to use as icon, or %NULL
+ * @hot_x: the hotspot X coordinate on the icon
+ * @hot_y: the hotspot Y coordinate on the icon
+ *
+ * Sets a paintable to use as icon during DND operations.
+ *
+ * The hotspot coordinates determine the point on the icon
+ * that gets aligned with the hotspot of the cursor.
*
- * Add the URI targets supported by #GtkSelectionData to
- * the target list of the drag source. The targets
- * are added with @info = 0. If you need another value,
- * use gtk_content_formats_add_uri_targets() and
- * gtk_drag_source_set_target_list().
+ * If @paintable is %NULL, a default icon is used.
+ *
+ * This function can be called before a drag is started, or in
+ * a #GtkDragSource::prepare or #GtkDragSource::drag-begin signal handler.
*/
void
-gtk_drag_source_add_uri_targets (GtkWidget *widget)
+gtk_drag_source_set_icon (GtkDragSource *source,
+ GdkPaintable *paintable,
+ int hot_x,
+ int hot_y)
{
- GdkContentFormats *target_list;
-
- target_list = gtk_drag_source_get_target_list (widget);
- if (target_list)
- gdk_content_formats_ref (target_list);
- else
- target_list = gdk_content_formats_new (NULL, 0);
- target_list = gtk_content_formats_add_uri_targets (target_list);
- gtk_drag_source_set_target_list (widget, target_list);
- gdk_content_formats_unref (target_list);
+ g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
+
+ g_set_object (&source->paintable, paintable);
+
+ source->hot_x = hot_x;
+ source->hot_y = hot_y;
}
/**
- * gtk_drag_source_set_icon_name: (method)
- * @widget: a #GtkWidget
- * @icon_name: name of icon to use
+ * gtk_drag_source_get_drag:
+ * @source: a #GtkDragSource
+ *
+ * Returns the underlying #GtkDrag object for an ongoing drag.
*
- * Sets the icon that will be used for drags from a particular source
- * to a themed icon. See the docs for #GtkIconTheme for more details.
+ * Returns: (nullable): the #GdkDrag of the current drag operation, or %NULL
*/
-void
-gtk_drag_source_set_icon_name (GtkWidget *widget,
- const gchar *icon_name)
+GdkDrag *
+gtk_drag_source_get_drag (GtkDragSource *source)
{
- GtkDragSourceSite *site;
-
- g_return_if_fail (GTK_IS_WIDGET (widget));
- g_return_if_fail (icon_name != NULL);
+ g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
- site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
- g_return_if_fail (site != NULL);
-
- gtk_image_definition_unref (site->image_def);
- site->image_def = gtk_image_definition_new_icon_name (icon_name);
+ return source->drag;
}
/**
- * gtk_drag_source_set_icon_gicon: (method)
- * @widget: a #GtkWidget
- * @icon: A #GIcon
- *
- * Sets the icon that will be used for drags from a particular source
- * to @icon. See the docs for #GtkIconTheme for more details.
+ * gtk_drag_source_drag_cancel:
+ * @source: a #GtkDragSource
+ *
+ * Cancels a currently ongoing drag operation.
*/
void
-gtk_drag_source_set_icon_gicon (GtkWidget *widget,
- GIcon *icon)
+gtk_drag_source_drag_cancel (GtkDragSource *source)
{
- GtkDragSourceSite *site;
+ g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
- g_return_if_fail (GTK_IS_WIDGET (widget));
- g_return_if_fail (icon != NULL);
-
- site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
- g_return_if_fail (site != NULL);
+ if (source->drag)
+ {
+ gboolean success = FALSE;
- gtk_image_definition_unref (site->image_def);
- site->image_def = gtk_image_definition_new_gicon (icon);
+ g_signal_emit (source, signals[DRAG_CANCEL], 0, source->drag, GDK_DRAG_CANCEL_ERROR, &success);
+ drag_end (source, FALSE);
+ }
}
/**
- * gtk_drag_source_set_icon_paintable: (method)
+ * gtk_drag_check_threshold: (method)
* @widget: a #GtkWidget
- * @paintable: A #GdkPaintable
+ * @start_x: X coordinate of start of drag
+ * @start_y: Y coordinate of start of drag
+ * @current_x: current X coordinate
+ * @current_y: current Y coordinate
+ *
+ * Checks to see if a mouse drag starting at (@start_x, @start_y) and ending
+ * at (@current_x, @current_y) has passed the GTK drag threshold, and thus
+ * should trigger the beginning of a drag-and-drop operation.
*
- * Sets the icon that will be used for drags from a particular source
- * to @paintable.
+ * Returns: %TRUE if the drag threshold has been passed.
*/
-void
-gtk_drag_source_set_icon_paintable (GtkWidget *widget,
- GdkPaintable *paintable)
+gboolean
+gtk_drag_check_threshold (GtkWidget *widget,
+ int start_x,
+ int start_y,
+ int current_x,
+ int current_y)
{
- GtkDragSourceSite *site;
+ gint drag_threshold;
- g_return_if_fail (GTK_IS_WIDGET (widget));
- g_return_if_fail (GDK_IS_PAINTABLE (paintable));
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
- site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
- g_return_if_fail (site != NULL);
+ drag_threshold = gtk_settings_get_dnd_drag_threshold (gtk_widget_get_settings (widget));
- gtk_image_definition_unref (site->image_def);
- site->image_def = gtk_image_definition_new_paintable (paintable);
+ return (ABS (current_x - start_x) > drag_threshold ||
+ ABS (current_y - start_y) > drag_threshold);
}
-
diff --git a/gtk/gtkdragsource.h b/gtk/gtkdragsource.h
index b77f46db74..48090be672 100644
--- a/gtk/gtkdragsource.h
+++ b/gtk/gtkdragsource.h
@@ -37,36 +37,51 @@
G_BEGIN_DECLS
-GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_set (GtkWidget *widget,
- GdkModifierType start_button_mask,
- GdkContentFormats *targets,
- GdkDragAction actions);
+#define GTK_TYPE_DRAG_SOURCE (gtk_drag_source_get_type ())
+#define GTK_DRAG_SOURCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_DRAG_SOURCE, GtkDragSource))
+#define GTK_DRAG_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_DRAG_SOURCE, GtkDragSourceClass))
+#define GTK_IS_DRAG_SOURCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_DRAG_SOURCE))
+#define GTK_IS_DRAG_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_DRAG_SOURCE))
+#define GTK_DRAG_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_DRAG_SOURCE, GtkDragSourceClass))
+
+typedef struct _GtkDragSource GtkDragSource;
+typedef struct _GtkDragSourceClass GtkDragSourceClass;
GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_unset (GtkWidget *widget);
+GType gtk_drag_source_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
-GdkContentFormats * gtk_drag_source_get_target_list (GtkWidget *widget);
+GtkDragSource *gtk_drag_source_new (void);
+
GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_set_target_list (GtkWidget *widget,
- GdkContentFormats *target_list);
+void gtk_drag_source_set_content (GtkDragSource *source,
+ GdkContentProvider *content);
GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_add_text_targets (GtkWidget *widget);
+GdkContentProvider *gtk_drag_source_get_content (GtkDragSource *source);
+
GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_add_image_targets (GtkWidget *widget);
+void gtk_drag_source_set_actions (GtkDragSource *source,
+ GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_add_uri_targets (GtkWidget *widget);
+GdkDragAction gtk_drag_source_get_actions (GtkDragSource *source);
GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_set_icon_name (GtkWidget *widget,
- const gchar *icon_name);
+void gtk_drag_source_set_icon (GtkDragSource *source,
+ GdkPaintable *paintable,
+ int hot_x,
+ int hot_y);
GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_set_icon_gicon (GtkWidget *widget,
- GIcon *icon);
+void gtk_drag_source_drag_cancel (GtkDragSource *sourcei);
+
+GDK_AVAILABLE_IN_ALL
+GdkDrag * gtk_drag_source_get_drag (GtkDragSource *source);
+
GDK_AVAILABLE_IN_ALL
-void gtk_drag_source_set_icon_paintable (GtkWidget *widget,
- GdkPaintable *paintable);
+gboolean gtk_drag_check_threshold (GtkWidget *widget,
+ int start_x,
+ int start_y,
+ int current_x,
+ int current_y);
G_END_DECLS
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index e4e41bf535..b5d367a9c1 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -38,8 +38,6 @@
#include "gtkcelllayout.h"
#include "gtkcssnodeprivate.h"
#include "gtkdebug.h"
-#include "gtkdnd.h"
-#include "gtkdndprivate.h"
#include "gtkeditable.h"
#include "gtkemojichooser.h"
#include "gtkemojicompletion.h"
@@ -70,6 +68,9 @@
#include "gtkwindow.h"
#include "gtknative.h"
#include "gtkgestureclick.h"
+#include "gtkdragsource.h"
+#include "gtkdragicon.h"
+#include "gtkwidgetpaintable.h"
#include "a11y/gtkentryaccessible.h"
@@ -172,7 +173,7 @@ struct _EntryIconInfo
guint in_drag : 1;
GdkDragAction actions;
- GdkContentFormats *target_list;
+ GdkContentProvider *content;
};
enum {
@@ -1320,8 +1321,7 @@ gtk_entry_finalize (GObject *object)
if (icon_info == NULL)
continue;
- if (icon_info->target_list != NULL)
- gdk_content_formats_unref (icon_info->target_list);
+ g_clear_object (&icon_info->content);
gtk_widget_unparent (icon_info->widget);
@@ -1462,17 +1462,25 @@ icon_drag_update_cb (GtkGestureDrag *gesture,
pos = get_icon_position_from_controller (entry, GTK_EVENT_CONTROLLER (gesture));
icon_info = priv->icons[pos];
- if (icon_info->target_list != NULL &&
- gtk_drag_check_threshold (icon_info->widget,
- start_x, start_y,
- x, y))
+ if (icon_info->content != NULL &&
+ gtk_drag_check_threshold (icon_info->widget, start_x, start_y, x, y))
{
+ GdkPaintable *paintable;
+ GdkSurface *surface;
+ GdkDevice *device;
+ GdkDrag *drag;
+
icon_info->in_drag = TRUE;
- gtk_drag_begin (GTK_WIDGET (entry),
- gtk_gesture_get_device (GTK_GESTURE (gesture)),
- icon_info->target_list,
- icon_info->actions,
- start_x, start_y);
+
+ surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (entry)));
+ device = gtk_gesture_get_device (GTK_GESTURE (gesture));
+
+ drag = gdk_drag_begin (surface, device, icon_info->content, icon_info->actions, start_x, start_y);
+ paintable = gtk_widget_paintable_new (icon_info->widget);
+ gtk_drag_icon_set_from_paintable (drag, paintable, -2, -2);
+ g_object_unref (paintable);
+
+ g_object_unref (drag);
}
}
@@ -2742,7 +2750,7 @@ gtk_entry_get_icon_at_pos (GtkEntry *entry,
void
gtk_entry_set_icon_drag_source (GtkEntry *entry,
GtkEntryIconPosition icon_pos,
- GdkContentFormats *formats,
+ GdkContentProvider *provider,
GdkDragAction actions)
{
GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
@@ -2754,12 +2762,7 @@ gtk_entry_set_icon_drag_source (GtkEntry *entry,
if ((icon_info = priv->icons[icon_pos]) == NULL)
icon_info = construct_icon_info (GTK_WIDGET (entry), icon_pos);
- if (icon_info->target_list)
- gdk_content_formats_unref (icon_info->target_list);
- icon_info->target_list = formats;
- if (icon_info->target_list)
- gdk_content_formats_ref (icon_info->target_list);
-
+ g_set_object (&icon_info->content, provider);
icon_info->actions = actions;
}
diff --git a/gtk/gtkentry.h b/gtk/gtkentry.h
index fca03bc2b8..e7e4853a54 100644
--- a/gtk/gtkentry.h
+++ b/gtk/gtkentry.h
@@ -266,7 +266,7 @@ gchar * gtk_entry_get_icon_tooltip_markup (GtkEntry *
GDK_AVAILABLE_IN_ALL
void gtk_entry_set_icon_drag_source (GtkEntry *entry,
GtkEntryIconPosition icon_pos,
- GdkContentFormats *formats,
+ GdkContentProvider *content,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
gint gtk_entry_get_current_icon_drag_source (GtkEntry *entry);
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index c23bf05b17..edf1c2d373 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -694,31 +694,6 @@ typedef enum
} GtkTreeViewGridLines;
/**
- * GtkDragResult:
- * @GTK_DRAG_RESULT_SUCCESS: The drag operation was successful.
- * @GTK_DRAG_RESULT_NO_TARGET: No suitable drag target.
- * @GTK_DRAG_RESULT_USER_CANCELLED: The user cancelled the drag operation.
- * @GTK_DRAG_RESULT_TIMEOUT_EXPIRED: The drag operation timed out.
- * @GTK_DRAG_RESULT_GRAB_BROKEN: The pointer or keyboard grab used
- * for the drag operation was broken.
- * @GTK_DRAG_RESULT_ERROR: The drag operation failed due to some
- * unspecified error.
- *
- * Gives an indication why a drag operation failed.
- * The value can by obtained by connecting to the
- * #GtkWidget::drag-failed signal.
- */
-typedef enum
-{
- GTK_DRAG_RESULT_SUCCESS,
- GTK_DRAG_RESULT_NO_TARGET,
- GTK_DRAG_RESULT_USER_CANCELLED,
- GTK_DRAG_RESULT_TIMEOUT_EXPIRED,
- GTK_DRAG_RESULT_GRAB_BROKEN,
- GTK_DRAG_RESULT_ERROR
-} GtkDragResult;
-
-/**
* GtkSizeGroupMode:
* @GTK_SIZE_GROUP_NONE: group has no effect
* @GTK_SIZE_GROUP_HORIZONTAL: group affects horizontal requisition
diff --git a/gtk/gtkexpander.c b/gtk/gtkexpander.c
index 7b484f10ba..f1a38acc48 100644
--- a/gtk/gtkexpander.c
+++ b/gtk/gtkexpander.c
@@ -115,7 +115,6 @@
#include "gtkbox.h"
#include "gtkbuildable.h"
#include "gtkcontainerprivate.h"
-#include "gtkdnd.h"
#include "gtkdragdest.h"
#include "gtkiconprivate.h"
#include "gtkgestureclick.h"
@@ -193,12 +192,12 @@ static void gtk_expander_size_allocate (GtkWidget *widget,
int baseline);
static gboolean gtk_expander_focus (GtkWidget *widget,
GtkDirectionType direction);
-static gboolean gtk_expander_drag_motion (GtkWidget *widget,
+static gboolean gtk_expander_drag_accept (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static void gtk_expander_drag_leave (GtkWidget *widget,
- GdkDrop *drop);
+ GtkExpander *expander);
+static void gtk_expander_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkExpander *expander);
static void gtk_expander_add (GtkContainer *container,
GtkWidget *widget);
@@ -268,8 +267,6 @@ gtk_expander_class_init (GtkExpanderClass *klass)
widget_class->destroy = gtk_expander_destroy;
widget_class->size_allocate = gtk_expander_size_allocate;
widget_class->focus = gtk_expander_focus;
- widget_class->drag_motion = gtk_expander_drag_motion;
- widget_class->drag_leave = gtk_expander_drag_leave;
widget_class->measure = gtk_expander_measure;
container_class->add = gtk_expander_add;
@@ -350,6 +347,8 @@ gtk_expander_init (GtkExpander *expander)
{
GtkExpanderPrivate *priv = gtk_expander_get_instance_private (expander);
GtkGesture *gesture;
+ GtkDropTarget *dest;
+ GdkContentFormats *formats;
gtk_widget_set_can_focus (GTK_WIDGET (expander), TRUE);
@@ -375,8 +374,12 @@ gtk_expander_init (GtkExpander *expander)
GTK_STYLE_CLASS_HORIZONTAL);
gtk_container_add (GTK_CONTAINER (priv->title_widget), priv->arrow_widget);
- gtk_drag_dest_set (GTK_WIDGET (expander), 0, NULL, 0);
- gtk_drag_dest_set_track_motion (GTK_WIDGET (expander), TRUE);
+ formats = gdk_content_formats_new (NULL, 0);
+ dest = gtk_drop_target_new (formats, 0);
+ gdk_content_formats_unref (formats);
+ g_signal_connect (dest, "accept", G_CALLBACK (gtk_expander_drag_accept), expander);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_expander_drag_leave), expander);
+ gtk_widget_add_controller (GTK_WIDGET (expander), GTK_EVENT_CONTROLLER (dest));
gesture = gtk_gesture_click_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
@@ -544,12 +547,10 @@ expand_timeout (gpointer data)
}
static gboolean
-gtk_expander_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+gtk_expander_drag_accept (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkExpander *expander)
{
- GtkExpander *expander = GTK_EXPANDER (widget);
GtkExpanderPrivate *priv = gtk_expander_get_instance_private (expander);
if (!priv->expanded && !priv->expand_timer)
@@ -562,10 +563,10 @@ gtk_expander_drag_motion (GtkWidget *widget,
}
static void
-gtk_expander_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
+gtk_expander_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkExpander *expander)
{
- GtkExpander *expander = GTK_EXPANDER (widget);
GtkExpanderPrivate *priv = gtk_expander_get_instance_private (expander);
if (priv->expand_timer)
diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c
index 64ea59563e..00bacca83f 100644
--- a/gtk/gtkfilechooserbutton.c
+++ b/gtk/gtkfilechooserbutton.c
@@ -37,7 +37,6 @@
#include "gtkcellrendererpixbuf.h"
#include "gtkcombobox.h"
#include "gtkcssiconthemevalueprivate.h"
-#include "gtkdnd.h"
#include "gtkdragdest.h"
#include "gtkicontheme.h"
#include "gtkimage.h"
@@ -61,6 +60,7 @@
#include "gtksettings.h"
#include "gtkstylecontextprivate.h"
#include "gtkbitmaskprivate.h"
+#include "gtkeventcontroller.h"
/**
* SECTION:gtkfilechooserbutton
@@ -267,9 +267,11 @@ static void gtk_file_chooser_button_finalize (GObject *ob
/* GtkWidget Functions */
static void gtk_file_chooser_button_destroy (GtkWidget *widget);
-static void gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *data);
+static gboolean gtk_file_chooser_button_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkWidget *widget);
static void gtk_file_chooser_button_show (GtkWidget *widget);
static void gtk_file_chooser_button_hide (GtkWidget *widget);
static void gtk_file_chooser_button_root (GtkWidget *widget);
@@ -366,7 +368,6 @@ gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
gobject_class->finalize = gtk_file_chooser_button_finalize;
widget_class->destroy = gtk_file_chooser_button_destroy;
- widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
widget_class->show = gtk_file_chooser_button_show;
widget_class->hide = gtk_file_chooser_button_hide;
widget_class->map = gtk_file_chooser_button_map;
@@ -443,7 +444,9 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button)
GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
GtkWidget *box;
GtkWidget *icon;
+ GdkContentFormatsBuilder *builder;
GdkContentFormats *target_list;
+ GtkDropTarget *dest;
priv->button = gtk_button_new ();
g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb), button);
@@ -495,13 +498,13 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button)
NULL, NULL);
/* DnD */
- target_list = gdk_content_formats_new (NULL, 0);
- target_list = gtk_content_formats_add_uri_targets (target_list);
- target_list = gtk_content_formats_add_text_targets (target_list);
- gtk_drag_dest_set (GTK_WIDGET (button),
- (GTK_DEST_DEFAULT_ALL),
- target_list,
- GDK_ACTION_COPY);
+ builder = gdk_content_formats_builder_new ();
+ gdk_content_formats_builder_add_gtype (builder, G_TYPE_STRING);
+ gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_FILE_LIST);
+ target_list = gdk_content_formats_builder_free_to_formats (builder);
+ dest = gtk_drop_target_new (target_list, GDK_ACTION_COPY);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_file_chooser_button_drag_drop), button);
+ gtk_widget_add_controller (GTK_WIDGET (button), GTK_EVENT_CONTROLLER (dest));
gdk_content_formats_unref (target_list);
}
@@ -1146,62 +1149,91 @@ dnd_select_folder_get_info_cb (GCancellable *cancellable,
}
static void
-gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *data)
+dnd_select_file (GtkFileChooserButton *button,
+ GFile *file)
{
- GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
GtkFileChooserButtonPrivate *priv = gtk_file_chooser_button_get_instance_private (button);
- GFile *file;
- gchar *text;
+ struct DndSelectFolderData *info;
- if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
- GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received (widget,
- drop,
- data);
+ info = g_new0 (struct DndSelectFolderData, 1);
+ info->button = g_object_ref (button);
+ info->i = 0;
+ info->uris = g_new0 (char *, 2);
+ info->selected = FALSE;
+ info->file_system = priv->fs;
+ g_object_get (priv->chooser, "action", &info->action, NULL);
- if (widget == NULL || gtk_selection_data_get_length (data) < 0)
- return;
+ info->file = g_object_ref (file);
- if (gtk_selection_data_targets_include_uri (data))
- {
- gchar **uris;
- struct DndSelectFolderData *info;
+ if (priv->dnd_select_folder_cancellable)
+ g_cancellable_cancel (priv->dnd_select_folder_cancellable);
- uris = gtk_selection_data_get_uris (data);
+ priv->dnd_select_folder_cancellable =
+ _gtk_file_system_get_info (priv->fs, info->file,
+ "standard::type",
+ dnd_select_folder_get_info_cb, info);
+}
- if (uris != NULL)
- {
- info = g_new0 (struct DndSelectFolderData, 1);
- info->button = g_object_ref (button);
- info->i = 0;
- info->uris = uris;
- info->selected = FALSE;
- info->file_system = priv->fs;
- g_object_get (priv->chooser, "action", &info->action, NULL);
-
- info->file = g_file_new_for_uri (info->uris[info->i]);
-
- if (priv->dnd_select_folder_cancellable)
- g_cancellable_cancel (priv->dnd_select_folder_cancellable);
-
- priv->dnd_select_folder_cancellable =
- _gtk_file_system_get_info (priv->fs, info->file,
- "standard::type",
- dnd_select_folder_get_info_cb, info);
- }
+static void
+got_file (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (data);
+ GdkDrop *drop = GDK_DROP (source);
+ const GValue *value;
+
+ value = gdk_drop_read_value_finish (drop, result, NULL);
+ if (value)
+ {
+ GFile *file;
+
+ file = g_value_get_object (value);
+ dnd_select_file (button, file);
}
- else if (gtk_selection_data_targets_include_text (data))
+}
+
+static void
+got_text (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (data);
+ GdkDrop *drop = GDK_DROP (source);
+ char *str;
+
+ str = gdk_drop_read_text_finish (drop, result, NULL);
+ if (str)
{
- text = (char*) gtk_selection_data_get_text (data);
- file = g_file_new_for_uri (text);
- gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->chooser), file, NULL);
+ GFile *file;
+
+ file = g_file_new_for_uri (str);
+ dnd_select_file (button, file);
g_object_unref (file);
- g_free (text);
- g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
}
- gdk_drop_finish (drop, GDK_ACTION_COPY);
+}
+
+static gboolean
+gtk_file_chooser_button_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkWidget *button)
+{
+ if (gdk_drop_has_value (drop, G_TYPE_FILE))
+ {
+ gdk_drop_read_value_async (drop, G_TYPE_FILE, G_PRIORITY_DEFAULT, NULL, got_file, button);
+ return TRUE;
+ }
+ else
+ {
+ gdk_drop_read_text_async (drop, NULL, got_text, button);
+ return TRUE;
+ }
+
+ return FALSE;
+
}
static void
diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c
index 9e29889813..4aecc911c7 100644
--- a/gtk/gtkfilechooserwidget.c
+++ b/gtk/gtkfilechooserwidget.c
@@ -1982,25 +1982,26 @@ out:
}
static void
-file_list_drag_data_received_cb (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data,
- gpointer user_data)
+file_list_drag_data_received_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (user_data);
GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl);
+ GtkDropTarget *dest = GTK_DROP_TARGET (source);
+ GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ GdkDrop *drop = gtk_drop_target_get_drop (dest);
+ GdkDrag *drag = gdk_drop_get_drag (drop);
gchar **uris;
char *uri;
GFile *file;
+ GtkSelectionData *selection_data;
- /* Allow only drags from other widgets; see bug #533891. */
- if (gdk_drop_get_drag (drop) &&
- gtk_drag_get_source_widget (gdk_drop_get_drag (drop)) == widget)
- {
- g_signal_stop_emission_by_name (widget, "drag-data-received");
- return;
- }
+ selection_data = gtk_drop_target_read_selection_finish (dest, result, NULL);
+ /* Allow only drags from other widgets; see bug #533891. */
+ if (drag && gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag)) == widget)
+ return;
/* Parse the text/uri-list string, navigate to the first one */
uris = gtk_selection_data_get_uris (selection_data);
@@ -2025,60 +2026,34 @@ file_list_drag_data_received_cb (GtkWidget *widget,
file_list_drag_data_received_get_info_cb,
data);
}
-
- g_signal_stop_emission_by_name (widget, "drag-data-received");
}
/* Don't do anything with the drag_drop signal */
static gboolean
-file_list_drag_drop_cb (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y,
+file_list_drag_drop_cb (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
GtkFileChooserWidget *impl)
{
- g_signal_stop_emission_by_name (widget, "drag-drop");
- return TRUE;
-}
+ const char *target = g_intern_static_string ("text/uri-list");
-static void
-file_list_drag_begin_cb (GtkWidget *widget,
- GdkDrag *drag,
- GtkFileChooserWidget *impl)
-{
- GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl);
+ gtk_drop_target_read_selection (dest, target, NULL, file_list_drag_data_received_cb, impl);
- gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (priv->places_sidebar),
- TRUE,
- drag);
+ return TRUE;
}
/* Disable the normal tree drag motion handler, it makes it look like you're
dropping the dragged item onto a tree item */
static gboolean
-file_list_drag_motion_cb (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y,
+file_list_drag_accept_cb (GtkDropTarget *dest,
+ GdkDrop *drop,
GtkFileChooserWidget *impl)
{
- g_signal_stop_emission_by_name (widget, "drag-motion");
+ g_signal_stop_emission_by_name (dest, "accept");
return TRUE;
}
-static void
-file_list_drag_end_cb (GtkWidget *widget,
- GdkDrag *drag,
- gpointer user_data)
-{
- GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (user_data);
- GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl);
-
- gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (priv->places_sidebar),
- FALSE,
- drag);
-}
-
/* Sensitizes the "Copy file’s location" and other context menu items if there is actually
* a selection active.
*/
@@ -8468,14 +8443,9 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, box);
/* And a *lot* of callbacks to bind ... */
- gtk_widget_class_bind_template_callback (widget_class, file_list_drag_drop_cb);
- gtk_widget_class_bind_template_callback (widget_class, file_list_drag_data_received_cb);
gtk_widget_class_bind_template_callback (widget_class, list_popup_menu_cb);
gtk_widget_class_bind_template_callback (widget_class, file_list_query_tooltip_cb);
gtk_widget_class_bind_template_callback (widget_class, list_row_activated);
- gtk_widget_class_bind_template_callback (widget_class, file_list_drag_begin_cb);
- gtk_widget_class_bind_template_callback (widget_class, file_list_drag_motion_cb);
- gtk_widget_class_bind_template_callback (widget_class, file_list_drag_end_cb);
gtk_widget_class_bind_template_callback (widget_class, list_selection_changed);
gtk_widget_class_bind_template_callback (widget_class, list_cursor_changed);
gtk_widget_class_bind_template_callback (widget_class, browse_files_tree_view_keynav_failed_cb);
@@ -8510,23 +8480,25 @@ post_process_ui (GtkFileChooserWidget *impl)
GtkCellRenderer *cell;
GList *cells;
GFile *file;
+ GtkDropTarget *dest;
+ GdkContentFormats *formats;
/* Setup file list treeview */
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
gtk_tree_selection_set_select_function (selection,
list_select_func,
impl, NULL);
+ formats = gdk_content_formats_new_for_gtype (GDK_TYPE_FILE_LIST);
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (priv->browse_files_tree_view),
GDK_BUTTON1_MASK,
- NULL,
+ formats,
GDK_ACTION_COPY | GDK_ACTION_MOVE);
- gtk_drag_source_add_uri_targets (priv->browse_files_tree_view);
-
- gtk_drag_dest_set (priv->browse_files_tree_view,
- GTK_DEST_DEFAULT_ALL,
- NULL,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
- gtk_drag_dest_add_uri_targets (priv->browse_files_tree_view);
+
+ dest = gtk_drop_target_new (formats, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (dest, "accept", G_CALLBACK (file_list_drag_accept_cb), impl);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (file_list_drag_drop_cb), impl);
+ gtk_widget_add_controller (priv->browse_files_tree_view, GTK_EVENT_CONTROLLER (dest));
+ gdk_content_formats_unref (formats);
/* File browser treemodel columns are shared between GtkFileChooser implementations,
* so we don't set cell renderer attributes in GtkBuilder, but rather keep that
diff --git a/gtk/gtkgesturelongpress.c b/gtk/gtkgesturelongpress.c
index fc28c32f57..9639ea151c 100644
--- a/gtk/gtkgesturelongpress.c
+++ b/gtk/gtkgesturelongpress.c
@@ -37,7 +37,7 @@
#include "gtkgesturelongpressprivate.h"
#include "gtkgestureprivate.h"
#include "gtkmarshalers.h"
-#include "gtkdnd.h"
+#include "gtkdragsource.h"
#include "gtkprivate.h"
#include "gtkintl.h"
#include "gtkmarshalers.h"
diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c
index 8c5962d1fa..c662156488 100644
--- a/gtk/gtkiconview.c
+++ b/gtk/gtkiconview.c
@@ -30,7 +30,6 @@
#include "gtkcellrenderertext.h"
#include "gtkcombobox.h"
#include "gtkcssnodeprivate.h"
-#include "gtkdnd.h"
#include "gtkdragdest.h"
#include "gtkdragsource.h"
#include "gtkentry.h"
@@ -48,6 +47,11 @@
#include "gtkwidgetprivate.h"
#include "gtkwindow.h"
#include "gtkeventcontrollerkey.h"
+#include "gtkdragsource.h"
+#include "gtkdragdest.h"
+#include "gtkdragicon.h"
+#include "gtkselectionprivate.h"
+#include "gtknative.h"
#include "a11y/gtkiconviewaccessibleprivate.h"
@@ -279,30 +283,28 @@ static void update_text_cell (GtkIco
static void update_pixbuf_cell (GtkIconView *icon_view);
/* Source side drag signals */
-static void gtk_icon_view_drag_begin (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_icon_view_drag_end (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_icon_view_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data);
-static void gtk_icon_view_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag);
+static void gtk_icon_view_dnd_finished_cb (GdkDrag *drag,
+ GtkWidget *widget);
+static GBytes * gtk_icon_view_drag_data_get (const char *mime_type,
+ gpointer data);
/* Target side drag signals */
-static void gtk_icon_view_drag_leave (GtkWidget *widget,
- GdkDrop *drop);
-static gboolean gtk_icon_view_drag_motion (GtkWidget *widget,
+static void gtk_icon_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static gboolean gtk_icon_view_drag_drop (GtkWidget *widget,
+ GtkIconView *icon_view);
+static void gtk_icon_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static void gtk_icon_view_drag_data_received (GtkWidget *widget,
+ int x,
+ int y,
+ GtkIconView *icon_view);
+static gboolean gtk_icon_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
- GtkSelectionData *selection_data);
+ int x,
+ int y,
+ GtkIconView *icon_view);
+static void gtk_icon_view_drag_data_received (GObject *source,
+ GAsyncResult *result,
+ gpointer data);
static gboolean gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view,
double x,
double y,
@@ -360,14 +362,6 @@ gtk_icon_view_class_init (GtkIconViewClass *klass)
widget_class->measure = gtk_icon_view_measure;
widget_class->size_allocate = gtk_icon_view_size_allocate;
widget_class->snapshot = gtk_icon_view_snapshot;
- widget_class->drag_begin = gtk_icon_view_drag_begin;
- widget_class->drag_end = gtk_icon_view_drag_end;
- widget_class->drag_data_get = gtk_icon_view_drag_data_get;
- widget_class->drag_data_delete = gtk_icon_view_drag_data_delete;
- widget_class->drag_leave = gtk_icon_view_drag_leave;
- widget_class->drag_motion = gtk_icon_view_drag_motion;
- widget_class->drag_drop = gtk_icon_view_drag_drop;
- widget_class->drag_data_received = gtk_icon_view_drag_data_received;
container_class->remove = gtk_icon_view_remove;
container_class->forall = gtk_icon_view_forall;
@@ -1033,6 +1027,8 @@ gtk_icon_view_dispose (GObject *object)
g_clear_object (&priv->key_controller);
+ g_clear_pointer (&priv->source_formats, gdk_content_formats_unref);
+
G_OBJECT_CLASS (gtk_icon_view_parent_class)->dispose (object);
}
@@ -1761,9 +1757,14 @@ gtk_icon_view_snapshot (GtkWidget *widget,
break;
}
- gtk_snapshot_render_focus (snapshot, context,
+ gtk_style_context_save_to_node (context, icon_view->priv->dndnode);
+ gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE);
+
+ gtk_snapshot_render_frame (snapshot, context,
rect.x, rect.y,
rect.width, rect.height);
+
+ gtk_style_context_restore (context);
}
if (icon_view->priv->doing_rubberband)
@@ -5712,35 +5713,6 @@ unset_reorderable (GtkIconView *icon_view)
}
}
-static void
-set_source_row (GdkDrag *drag,
- GtkTreeModel *model,
- GtkTreePath *source_row)
-{
- if (source_row)
- g_object_set_data_full (G_OBJECT (drag),
- I_("gtk-icon-view-source-row"),
- gtk_tree_row_reference_new (model, source_row),
- (GDestroyNotify) gtk_tree_row_reference_free);
- else
- g_object_set_data_full (G_OBJECT (drag),
- I_("gtk-icon-view-source-row"),
- NULL, NULL);
-}
-
-static GtkTreePath*
-get_source_row (GdkDrag *drag)
-{
- GtkTreeRowReference *ref;
-
- ref = g_object_get_data (G_OBJECT (drag), "gtk-icon-view-source-row");
-
- if (ref)
- return gtk_tree_row_reference_get_path (ref);
- else
- return NULL;
-}
-
typedef struct
{
GtkTreeRowReference *dest_row;
@@ -5886,7 +5858,7 @@ drag_scroll_timeout (gpointer data)
static gboolean
set_destination (GtkIconView *icon_view,
- GdkDrop *drop,
+ GtkDropTarget *dest,
gint x,
gint y,
GdkDragAction *suggested_action,
@@ -5919,8 +5891,7 @@ set_destination (GtkIconView *icon_view,
return FALSE; /* no longer a drop site */
}
- *target = gtk_drag_dest_find_target (widget, drop,
- gtk_drag_dest_get_target_list (widget));
+ *target = gtk_drop_target_find_mimetype (dest);
if (*target == NULL)
return FALSE;
@@ -6030,11 +6001,14 @@ gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view,
double y,
GdkDevice *device)
{
- GtkWidget *widget = GTK_WIDGET (icon_view);
- GdkDrag *drag;
GtkTreePath *path = NULL;
GtkTreeModel *model;
gboolean retval = FALSE;
+ GdkContentProvider *content;
+ GdkPaintable *icon;
+ GtkIconViewItem *item;
+ GdkSurface *surface;
+ GdkDrag *drag;
if (!icon_view->priv->source_set)
goto out;
@@ -6055,16 +6029,19 @@ gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view,
icon_view->priv->pressed_button = -1;
- path = gtk_icon_view_get_path_at_pos (icon_view,
- icon_view->priv->press_start_x,
- icon_view->priv->press_start_y);
+ item = _gtk_icon_view_get_item_at_coords (icon_view,
+ icon_view->priv->press_start_x,
+ icon_view->priv->press_start_y,
+ TRUE,
+ NULL);
- if (path == NULL)
+ if (item == NULL)
goto out;
+ path = gtk_tree_path_new_from_indices (item->index, -1);
+
if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
- !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
- path))
+ !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model), path))
goto out;
/* FIXME Check whether we're a start button, if not return FALSE and
@@ -6075,88 +6052,66 @@ gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view,
retval = TRUE;
- drag = gtk_drag_begin (widget,
+ surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (icon_view)));
+
+ content = gdk_content_provider_new_with_formats (icon_view->priv->source_formats,
+ gtk_icon_view_drag_data_get,
+ icon_view);
+
+ drag = gdk_drag_begin (surface,
device,
- gtk_drag_source_get_target_list (widget),
- icon_view->priv->source_actions,
+ content,
+ icon_view->priv->source_actions,
icon_view->priv->press_start_x,
icon_view->priv->press_start_y);
- set_source_row (drag, model, path);
-
- out:
- if (path)
- gtk_tree_path_free (path);
-
- return retval;
-}
-
-/* Source side drag signals */
-static void
-gtk_icon_view_drag_begin (GtkWidget *widget,
- GdkDrag *drag)
-{
- GtkIconView *icon_view;
- GtkIconViewItem *item;
- GdkPaintable *icon;
- gint x, y;
- GtkTreePath *path;
-
- icon_view = GTK_ICON_VIEW (widget);
+ g_object_unref (content);
- /* if the user uses a custom DnD impl, we don't set the icon here */
- if (!icon_view->priv->dest_set && !icon_view->priv->source_set)
- return;
+ g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_icon_view_dnd_finished_cb), icon_view);
- item = _gtk_icon_view_get_item_at_coords (icon_view,
- icon_view->priv->press_start_x,
- icon_view->priv->press_start_y,
- TRUE,
- NULL);
-
- g_return_if_fail (item != NULL);
+ icon_view->priv->source_item = gtk_tree_row_reference_new (model, path);
x = icon_view->priv->press_start_x - item->cell_area.x + icon_view->priv->item_padding;
y = icon_view->priv->press_start_y - item->cell_area.y + icon_view->priv->item_padding;
- path = gtk_tree_path_new_from_indices (item->index, -1);
icon = gtk_icon_view_create_drag_icon (icon_view, path);
- gtk_tree_path_free (path);
+ gtk_drag_icon_set_from_paintable (drag, icon, x, y);
+ g_object_unref (icon);
- gtk_drag_set_icon_paintable (drag, icon, x, y);
+ icon_view->priv->drag = drag;
- g_object_unref (icon);
-}
+ g_object_unref (drag);
-static void
-gtk_icon_view_drag_end (GtkWidget *widget,
- GdkDrag *drag)
-{
- /* do nothing */
+ out:
+ if (path)
+ gtk_tree_path_free (path);
+
+ return retval;
}
-static void
-gtk_icon_view_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data)
+/* Source side drag signals */
+static GBytes *
+gtk_icon_view_drag_data_get (const char *mime_type,
+ gpointer data)
{
- GtkIconView *icon_view;
+ GtkIconView *icon_view = data;
GtkTreeModel *model;
GtkTreePath *source_row;
+ GtkSelectionData sdata = { 0, };
+
+ sdata.target = g_intern_string (mime_type);
- icon_view = GTK_ICON_VIEW (widget);
model = gtk_icon_view_get_model (icon_view);
if (model == NULL)
- return;
+ return NULL;
if (!icon_view->priv->source_set)
- return;
-
- source_row = get_source_row (drag);
+ return NULL;
+ source_row = gtk_tree_row_reference_get_path (icon_view->priv->source_item);
if (source_row == NULL)
- return;
+ return NULL;
/* We can implement the GTK_TREE_MODEL_ROW target generically for
* any model; for DragSource models there are some other formats
@@ -6164,29 +6119,31 @@ gtk_icon_view_drag_data_get (GtkWidget *widget,
*/
if (GTK_IS_TREE_DRAG_SOURCE (model) &&
- gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
- source_row,
- selection_data))
+ gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), source_row, &sdata))
goto done;
/* If drag_data_get does nothing, try providing row data. */
- if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("GTK_TREE_MODEL_ROW"))
- gtk_tree_set_row_drag_data (selection_data,
- model,
- source_row);
+ if (gtk_selection_data_get_target (&sdata) == g_intern_static_string ("GTK_TREE_MODEL_ROW"))
+ gtk_tree_set_row_drag_data (&sdata, model, source_row);
done:
gtk_tree_path_free (source_row);
+
+ return g_bytes_new_take ((gpointer)gtk_selection_data_get_data (&sdata),
+ gtk_selection_data_get_length (&sdata));
}
static void
-gtk_icon_view_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag)
+gtk_icon_view_dnd_finished_cb (GdkDrag *drag,
+ GtkWidget *widget)
{
GtkTreeModel *model;
GtkIconView *icon_view;
GtkTreePath *source_row;
+ if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE)
+ return;
+
icon_view = GTK_ICON_VIEW (widget);
model = gtk_icon_view_get_model (icon_view);
@@ -6196,28 +6153,24 @@ gtk_icon_view_drag_data_delete (GtkWidget *widget,
if (!icon_view->priv->source_set)
return;
- source_row = get_source_row (drag);
-
+ source_row = gtk_tree_row_reference_get_path (icon_view->priv->source_item);
if (source_row == NULL)
return;
- gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
- source_row);
+ gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row);
gtk_tree_path_free (source_row);
- set_source_row (drag, NULL, NULL);
+ g_clear_pointer (&icon_view->priv->source_item, gtk_tree_row_reference_free);
+ icon_view->priv->drag = NULL;
}
/* Target side drag signals */
static void
-gtk_icon_view_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
+gtk_icon_view_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkIconView *icon_view)
{
- GtkIconView *icon_view;
-
- icon_view = GTK_ICON_VIEW (widget);
-
/* unset any highlight row */
gtk_icon_view_set_drag_dest_item (icon_view,
NULL,
@@ -6226,26 +6179,24 @@ gtk_icon_view_drag_leave (GtkWidget *widget,
remove_scroll_timeout (icon_view);
}
-static gboolean
-gtk_icon_view_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+static void
+gtk_icon_view_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkIconView *icon_view)
{
GtkTreePath *path = NULL;
GtkIconViewDropPosition pos;
- GtkIconView *icon_view;
GdkDragAction suggested_action = 0;
GdkAtom target;
gboolean empty;
- icon_view = GTK_ICON_VIEW (widget);
-
- if (!set_destination (icon_view, drop, x, y, &suggested_action, &target))
- return FALSE;
-
- icon_view->priv->event_last_x = x;
- icon_view->priv->event_last_y = y;
+ if (!set_destination (icon_view, dest, x, y, &suggested_action, &target))
+ {
+ gdk_drop_status (drop, 0);
+ return;
+ }
gtk_icon_view_get_drag_dest_item (icon_view, &path, &pos);
@@ -6271,7 +6222,7 @@ gtk_icon_view_drag_motion (GtkWidget *widget,
* determining whether to accept the drop
*/
set_status_pending (drop, suggested_action);
- gtk_drag_get_data (widget, drop, target);
+ gtk_drop_target_read_selection (dest, target, NULL, gtk_icon_view_drag_data_received, icon_view);
}
else
{
@@ -6282,27 +6233,24 @@ gtk_icon_view_drag_motion (GtkWidget *widget,
if (path)
gtk_tree_path_free (path);
-
- return TRUE;
}
static gboolean
-gtk_icon_view_drag_drop (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+gtk_icon_view_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkIconView *icon_view)
{
- GtkIconView *icon_view;
GtkTreePath *path;
GdkDragAction suggested_action = 0;
GdkAtom target = NULL;
GtkTreeModel *model;
gboolean drop_append_mode;
- icon_view = GTK_ICON_VIEW (widget);
model = gtk_icon_view_get_model (icon_view);
- remove_scroll_timeout (GTK_ICON_VIEW (widget));
+ remove_scroll_timeout (icon_view);
if (!icon_view->priv->dest_set)
return FALSE;
@@ -6310,7 +6258,7 @@ gtk_icon_view_drag_drop (GtkWidget *widget,
if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag-drop"))
return FALSE;
- if (!set_destination (icon_view, drop, x, y, &suggested_action, &target))
+ if (!set_destination (icon_view, dest, x, y, &suggested_action, &target))
return FALSE;
path = get_logical_destination (icon_view, &drop_append_mode);
@@ -6333,7 +6281,7 @@ gtk_icon_view_drag_drop (GtkWidget *widget,
if (target != NULL)
{
- gtk_drag_get_data (widget, drop, target);
+ gtk_drop_target_read_selection (dest, target, NULL, gtk_icon_view_drag_data_received, icon_view);
return TRUE;
}
else
@@ -6341,16 +6289,16 @@ gtk_icon_view_drag_drop (GtkWidget *widget,
}
static GdkDragAction
-gtk_icon_view_get_action (GtkWidget *treeview,
+gtk_icon_view_get_action (GtkWidget *widget,
GdkDrop *drop)
{
+ GtkIconView *iconview = GTK_ICON_VIEW (widget);
GdkDrag *drag = gdk_drop_get_drag (drop);
- GtkWidget *source_widget = gtk_drag_get_source_widget (drag);
GdkDragAction actions;
actions = gdk_drop_get_actions (drop);
- if (source_widget == treeview &&
+ if (drag == iconview->priv->drag &&
actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
@@ -6367,25 +6315,31 @@ gtk_icon_view_get_action (GtkWidget *treeview,
}
static void
-gtk_icon_view_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data)
+gtk_icon_view_drag_data_received (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
+ GtkDropTarget *dest = GTK_DROP_TARGET (source);
+ GtkIconView *icon_view = data;
+ GdkDrop *drop = gtk_drop_target_get_drop (dest);
GtkTreePath *path;
GtkTreeModel *model;
- GtkIconView *icon_view;
GtkTreePath *dest_row;
GdkDragAction suggested_action;
gboolean drop_append_mode;
-
- icon_view = GTK_ICON_VIEW (widget);
+ GtkSelectionData *selection_data;
+
+ selection_data = gtk_drop_target_read_selection_finish (dest, result, NULL);
+ if (!selection_data)
+ return;
+
model = gtk_icon_view_get_model (icon_view);
if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag-data-received"))
- return;
+ goto out;
if (!icon_view->priv->dest_set)
- return;
+ goto out;
suggested_action = get_status_pending (drop);
@@ -6419,18 +6373,18 @@ gtk_icon_view_drag_data_received (GtkWidget *widget,
gtk_icon_view_set_drag_dest_item (icon_view,
NULL,
GTK_ICON_VIEW_DROP_LEFT);
- return;
+ goto out;
}
dest_row = get_dest_row (drop);
if (dest_row == NULL)
- return;
+ goto out;
if (gtk_selection_data_get_length (selection_data) >= 0)
{
- suggested_action = gtk_icon_view_get_action (widget, drop);
+ suggested_action = gtk_icon_view_get_action (GTK_WIDGET (icon_view), drop);
if (suggested_action &&
!gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
@@ -6445,9 +6399,13 @@ gtk_icon_view_drag_data_received (GtkWidget *widget,
/* drop dest_row */
set_dest_row (drop, NULL, NULL, FALSE, FALSE);
+
+out:
+ gtk_selection_data_free (selection_data);
}
/* Drag-and-Drop support */
+
/**
* gtk_icon_view_enable_model_drag_source:
* @icon_view: a #GtkIconView
@@ -6467,9 +6425,7 @@ gtk_icon_view_enable_model_drag_source (GtkIconView *icon_view,
{
g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
- gtk_drag_source_set (GTK_WIDGET (icon_view), 0, formats, actions);
-
- icon_view->priv->start_button_mask = start_button_mask;
+ icon_view->priv->source_formats = gdk_content_formats_ref (formats);
icon_view->priv->source_actions = actions;
icon_view->priv->source_set = TRUE;
@@ -6486,21 +6442,37 @@ gtk_icon_view_enable_model_drag_source (GtkIconView *icon_view,
*
* Turns @icon_view into a drop destination for automatic DND. Calling this
* method sets #GtkIconView:reorderable to %FALSE.
+ *
+ * Returns: (transfer none): the drop target that was attached
**/
-void
+GtkDropTarget *
gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view,
GdkContentFormats *formats,
GdkDragAction actions)
{
- g_return_if_fail (GTK_IS_ICON_VIEW (icon_view));
+ g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), NULL);
+ GtkCssNode *widget_node;
- gtk_drag_dest_set (GTK_WIDGET (icon_view), 0, formats, actions);
+ icon_view->priv->dest = gtk_drop_target_new (formats, actions);
+ g_signal_connect (icon_view->priv->dest, "drag-leave", G_CALLBACK (gtk_icon_view_drag_leave), icon_view);
+ g_signal_connect (icon_view->priv->dest, "drag-motion", G_CALLBACK (gtk_icon_view_drag_motion), icon_view);
+ g_signal_connect (icon_view->priv->dest, "drag-drop", G_CALLBACK (gtk_icon_view_drag_drop), icon_view);
+ gtk_widget_add_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest));
icon_view->priv->dest_actions = actions;
icon_view->priv->dest_set = TRUE;
unset_reorderable (icon_view);
+
+ widget_node = gtk_widget_get_css_node (GTK_WIDGET (icon_view));
+ icon_view->priv->dndnode = gtk_css_node_new ();
+ gtk_css_node_set_name (icon_view->priv->dndnode, I_("dndtarget"));
+ gtk_css_node_set_parent (icon_view->priv->dndnode, widget_node);
+ gtk_css_node_set_state (icon_view->priv->dndnode, gtk_css_node_get_state (widget_node));
+ g_object_unref (icon_view->priv->dndnode);
+
+ return icon_view->priv->dest;
}
/**
@@ -6517,7 +6489,7 @@ gtk_icon_view_unset_model_drag_source (GtkIconView *icon_view)
if (icon_view->priv->source_set)
{
- gtk_drag_source_unset (GTK_WIDGET (icon_view));
+ g_clear_pointer (&icon_view->priv->source_formats, gdk_content_formats_unref);
icon_view->priv->source_set = FALSE;
}
@@ -6538,8 +6510,12 @@ gtk_icon_view_unset_model_drag_dest (GtkIconView *icon_view)
if (icon_view->priv->dest_set)
{
- gtk_drag_dest_unset (GTK_WIDGET (icon_view));
+ gtk_widget_remove_controller (GTK_WIDGET (icon_view), GTK_EVENT_CONTROLLER (icon_view->priv->dest));
+ icon_view->priv->dest = NULL;
icon_view->priv->dest_set = FALSE;
+
+ gtk_css_node_set_parent (icon_view->priv->dndnode, NULL);
+ icon_view->priv->dndnode = NULL;
}
unset_reorderable (icon_view);
diff --git a/gtk/gtkiconview.h b/gtk/gtkiconview.h
index 56fccfc3d4..5334f72e97 100644
--- a/gtk/gtkiconview.h
+++ b/gtk/gtkiconview.h
@@ -28,6 +28,8 @@
#include <gtk/gtkcellarea.h>
#include <gtk/gtkselection.h>
#include <gtk/gtktooltip.h>
+#include <gtk/gtkdragsource.h>
+#include <gtk/gtkdragdest.h>
G_BEGIN_DECLS
@@ -218,7 +220,7 @@ void gtk_icon_view_enable_model_drag_source (GtkIconView
GdkContentFormats *formats,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
-void gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view,
+GtkDropTarget * gtk_icon_view_enable_model_drag_dest (GtkIconView *icon_view,
GdkContentFormats *formats,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
diff --git a/gtk/gtkiconviewprivate.h b/gtk/gtkiconviewprivate.h
index e507cb55bc..97ceed2986 100644
--- a/gtk/gtkiconviewprivate.h
+++ b/gtk/gtkiconviewprivate.h
@@ -19,6 +19,7 @@
#include "gtk/gtkcssnodeprivate.h"
#include "gtk/gtkgestureclick.h"
#include "gtk/gtkeventcontrollermotion.h"
+#include "gtk/gtkdragsource.h"
#ifndef __GTK_ICON_VIEW_PRIVATE_H__
#define __GTK_ICON_VIEW_PRIVATE_H__
@@ -132,9 +133,16 @@ struct _GtkIconViewPrivate
gint press_start_x;
gint press_start_y;
+ GdkContentFormats *source_formats;
+ GtkDropTarget *dest;
+ GtkCssNode *dndnode;
+
+ GdkDrag *drag;
+
GdkDragAction source_actions;
GdkDragAction dest_actions;
+ GtkTreeRowReference *source_item;
GtkTreeRowReference *dest_item;
GtkIconViewDropPosition dest_pos;
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index b8d8d1cf6f..dbaab2545d 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -32,7 +32,6 @@
#include "gtkcssnodeprivate.h"
#include "gtkcssshadowsvalueprivate.h"
#include "gtkcssstylepropertyprivate.h"
-#include "gtkdnd.h"
#include "gtkeventcontrollermotion.h"
#include "gtkgesturedrag.h"
#include "gtkgestureclick.h"
@@ -54,6 +53,8 @@
#include "gtkwindow.h"
#include "gtkpopovermenu.h"
#include "gtknative.h"
+#include "gtkdragsource.h"
+#include "gtkdragicon.h"
#include "a11y/gtklabelaccessibleprivate.h"
@@ -501,9 +502,6 @@ static gboolean gtk_label_mnemonic_activate (GtkWidget *widget,
static void gtk_label_setup_mnemonic (GtkLabel *label,
GtkWidget *toplevel,
guint last_key);
-static void gtk_label_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data);
static void gtk_label_buildable_interface_init (GtkBuildableIface *iface);
static gboolean gtk_label_buildable_custom_tag_start (GtkBuildable *buildable,
@@ -652,7 +650,6 @@ gtk_label_class_init (GtkLabelClass *class)
widget_class->unroot = gtk_label_unroot;
widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
widget_class->popup_menu = gtk_label_popup_menu;
- widget_class->drag_data_get = gtk_label_drag_data_get;
widget_class->grab_focus = gtk_label_grab_focus;
widget_class->focus = gtk_label_focus;
widget_class->get_request_mode = gtk_label_get_request_mode;
@@ -4597,16 +4594,10 @@ connect_mnemonics_visible_notify (GtkLabel *label)
}
}
-static void
-drag_begin_cb (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
+static GdkPaintable *
+get_selection_paintable (GtkLabel *label)
{
- GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
- GdkPaintable *paintable = NULL;
-
- g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
if ((priv->select_info->selection_anchor !=
priv->select_info->selection_end) &&
@@ -4628,20 +4619,10 @@ drag_begin_cb (GtkWidget *widget,
if (start > len)
start = len;
- paintable = gtk_text_util_create_drag_icon (widget,
- priv->text + start,
- end - start);
+ return gtk_text_util_create_drag_icon (GTK_WIDGET (label), priv->text + start, end - start);
}
- if (paintable)
- {
- gtk_drag_set_icon_paintable (drag, paintable, 0, 0);
- g_object_unref (paintable);
- }
- else
- {
- gtk_drag_set_icon_default (drag);
- }
+ return NULL;
}
static void
@@ -4734,27 +4715,27 @@ gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
if (info->in_drag)
{
- if (gtk_drag_check_threshold (widget,
- info->drag_start_x,
- info->drag_start_y,
- x, y))
+ if (gtk_drag_check_threshold (widget, info->drag_start_x, info->drag_start_y, x, y))
{
- GdkContentFormats *target_list = gdk_content_formats_new (NULL, 0);
+ GdkDrag *drag;
+ GdkSurface *surface;
+ GdkDevice *device;
- target_list = gtk_content_formats_add_text_targets (target_list);
+ surface = gtk_native_get_surface (gtk_widget_get_native (widget));
+ device = gtk_gesture_get_device (GTK_GESTURE (gesture));
- g_signal_connect (widget, "drag-begin",
- G_CALLBACK (drag_begin_cb), NULL);
- gtk_drag_begin (widget,
- gtk_gesture_get_device (GTK_GESTURE (gesture)),
- target_list,
- GDK_ACTION_COPY,
- info->drag_start_x,
- info->drag_start_y);
+ drag = gdk_drag_begin (surface,
+ device,
+ info->provider,
+ GDK_ACTION_COPY,
+ info->drag_start_x,
+ info->drag_start_y);
- info->in_drag = FALSE;
+ gtk_drag_icon_set_from_paintable (drag, get_selection_paintable (label), 0, 0);
- gdk_content_formats_unref (target_list);
+ g_object_unref (drag);
+
+ info->in_drag = FALSE;
}
}
else
@@ -5141,47 +5122,6 @@ gtk_label_get_selectable (GtkLabel *label)
}
static void
-gtk_label_set_selection_text (GtkLabel *label,
- GtkSelectionData *selection_data)
-{
- GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
-
- if (priv->select_info &&
- (priv->select_info->selection_anchor !=
- priv->select_info->selection_end) &&
- priv->text)
- {
- gint start, end;
- gint len;
-
- start = MIN (priv->select_info->selection_anchor,
- priv->select_info->selection_end);
- end = MAX (priv->select_info->selection_anchor,
- priv->select_info->selection_end);
-
- len = strlen (priv->text);
-
- if (end > len)
- end = len;
-
- if (start > len)
- start = len;
-
- gtk_selection_data_set_text (selection_data,
- priv->text + start,
- end - start);
- }
-}
-
-static void
-gtk_label_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data)
-{
- gtk_label_set_selection_text (GTK_LABEL (widget), selection_data);
-}
-
-static void
gtk_label_select_region_index (GtkLabel *label,
gint anchor_index,
gint end_index)
diff --git a/gtk/gtklinkbutton.c b/gtk/gtklinkbutton.c
index c8cc0aad2d..2621591a5e 100644
--- a/gtk/gtklinkbutton.c
+++ b/gtk/gtklinkbutton.c
@@ -124,10 +124,6 @@ static void gtk_link_button_set_property (GObject *object,
GParamSpec *pspec);
static void gtk_link_button_clicked (GtkButton *button);
static gboolean gtk_link_button_popup_menu (GtkWidget *widget);
-static void gtk_link_button_drag_data_get_cb (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection,
- gpointer user_data);
static gboolean gtk_link_button_query_tooltip_cb (GtkWidget *widget,
gint x,
gint y,
@@ -251,31 +247,97 @@ gtk_link_button_get_menu_model (void)
return G_MENU_MODEL (menu);
}
+#define GTK_TYPE_LINK_CONTENT (gtk_link_content_get_type ())
+#define GTK_LINK_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_LINK_CONTENT, GtkLinkContent))
+
+typedef struct _GtkLinkContent GtkLinkContent;
+typedef struct _GtkLinkContentClass GtkLinkContentClass;
+
+struct _GtkLinkContent
+{
+ GdkContentProvider parent;
+ GtkLinkButton *link;
+};
+
+struct _GtkLinkContentClass
+{
+ GdkContentProviderClass parent_class;
+};
+
+GType gtk_link_content_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (GtkLinkContent, gtk_link_content, GDK_TYPE_CONTENT_PROVIDER)
+
+static GdkContentFormats *
+gtk_link_content_ref_formats (GdkContentProvider *provider)
+{
+ GtkLinkContent *content = GTK_LINK_CONTENT (provider);
+
+ if (content->link)
+ return gdk_content_formats_union (gdk_content_formats_new_for_gtype (G_TYPE_STRING),
+ gdk_content_formats_new (link_drop_types, G_N_ELEMENTS (link_drop_types)));
+ else
+ return gdk_content_formats_new (NULL, 0);
+}
+
+static gboolean
+gtk_link_content_get_value (GdkContentProvider *provider,
+ GValue *value,
+ GError **error)
+{
+ GtkLinkContent *content = GTK_LINK_CONTENT (provider);
+
+ if (G_VALUE_HOLDS (value, G_TYPE_STRING) &&
+ content->link != NULL)
+ {
+ GtkLinkButtonPrivate *priv = gtk_link_button_get_instance_private (content->link);
+ char *uri;
+
+ uri = g_strdup_printf ("%s\r\n", priv->uri);
+ g_value_set_string (value, uri);
+ g_free (uri);
+
+ return TRUE;
+ }
+
+ return GDK_CONTENT_PROVIDER_CLASS (gtk_link_content_parent_class)->get_value (provider, value, error);
+}
+
+static void
+gtk_link_content_class_init (GtkLinkContentClass *class)
+{
+ GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class);
+
+ provider_class->ref_formats = gtk_link_content_ref_formats;
+ provider_class->get_value = gtk_link_content_get_value;
+}
+
+static void
+gtk_link_content_init (GtkLinkContent *content)
+{
+}
+
static void
gtk_link_button_init (GtkLinkButton *link_button)
{
GtkStyleContext *context;
- GdkContentFormats *targets;
GtkGesture *gesture;
+ GdkContentProvider *content;
+ GtkDragSource *source;
gtk_button_set_relief (GTK_BUTTON (link_button), GTK_RELIEF_NONE);
gtk_widget_set_state_flags (GTK_WIDGET (link_button), GTK_STATE_FLAG_LINK, FALSE);
gtk_widget_set_has_tooltip (GTK_WIDGET (link_button), TRUE);
- g_signal_connect (link_button, "drag-data-get",
- G_CALLBACK (gtk_link_button_drag_data_get_cb), NULL);
-
g_signal_connect (link_button, "query-tooltip",
G_CALLBACK (gtk_link_button_query_tooltip_cb), NULL);
- /* enable drag source */
- targets = gdk_content_formats_new (link_drop_types, G_N_ELEMENTS (link_drop_types));
- gtk_drag_source_set (GTK_WIDGET (link_button),
- GDK_BUTTON1_MASK,
- targets,
- GDK_ACTION_COPY);
- gdk_content_formats_unref (targets);
- gtk_drag_source_set_icon_name (GTK_WIDGET (link_button), "text-x-generic");
+ source = gtk_drag_source_new ();
+ content = g_object_new (GTK_TYPE_LINK_CONTENT, NULL);
+ GTK_LINK_CONTENT (content)->link = link_button;
+ gtk_drag_source_set_content (source, content);
+ g_object_unref (content);
+ gtk_widget_add_controller (GTK_WIDGET (link_button), GTK_EVENT_CONTROLLER (source));
gesture = gtk_gesture_click_new ();
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
@@ -449,26 +511,6 @@ gtk_link_button_popup_menu (GtkWidget *widget)
return TRUE;
}
-static void
-gtk_link_button_drag_data_get_cb (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection,
- gpointer user_data)
-{
- GtkLinkButton *link_button = GTK_LINK_BUTTON (widget);
- GtkLinkButtonPrivate *priv = gtk_link_button_get_instance_private (link_button);
- gchar *uri;
-
- uri = g_strdup_printf ("%s\r\n", priv->uri);
- gtk_selection_data_set (selection,
- gtk_selection_data_get_target (selection),
- 8,
- (guchar *) uri,
- strlen (uri));
-
- g_free (uri);
-}
-
/**
* gtk_link_button_new:
* @uri: a valid URI
diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c
index 0a85405d5b..c4d9a99545 100644
--- a/gtk/gtklistbox.c
+++ b/gtk/gtklistbox.c
@@ -25,7 +25,7 @@
#include "gtkbuildable.h"
#include "gtkcontainerprivate.h"
#include "gtkcssnodeprivate.h"
-#include "gtkdnd.h"
+#include "gtkdragdest.h"
#include "gtkgestureclick.h"
#include "gtkintl.h"
#include "gtkmain.h"
@@ -264,8 +264,6 @@ static void gtk_list_box_size_allocate (GtkWidget
int width,
int height,
int baseline);
-static void gtk_list_box_drag_leave (GtkWidget *widget,
- GdkDrop *drop);
static void gtk_list_box_activate_cursor_row (GtkListBox *box);
static void gtk_list_box_toggle_cursor_row (GtkListBox *box);
static void gtk_list_box_move_cursor (GtkListBox *box,
@@ -452,7 +450,6 @@ gtk_list_box_class_init (GtkListBoxClass *klass)
widget_class->get_request_mode = gtk_list_box_get_request_mode;
widget_class->measure = gtk_list_box_measure;
widget_class->size_allocate = gtk_list_box_size_allocate;
- widget_class->drag_leave = gtk_list_box_drag_leave;
container_class->add = gtk_list_box_add;
container_class->remove = gtk_list_box_remove;
container_class->forall = gtk_list_box_forall;
@@ -2728,7 +2725,7 @@ gtk_list_box_drag_unhighlight_row (GtkListBox *box)
if (priv->drag_highlighted_row == NULL)
return;
- gtk_drag_unhighlight (GTK_WIDGET (priv->drag_highlighted_row));
+ gtk_widget_unset_state_flags (GTK_WIDGET (priv->drag_highlighted_row), GTK_STATE_FLAG_DROP_ACTIVE);
g_clear_object (&priv->drag_highlighted_row);
}
@@ -2757,18 +2754,11 @@ gtk_list_box_drag_highlight_row (GtkListBox *box,
return;
gtk_list_box_drag_unhighlight_row (box);
- gtk_drag_highlight (GTK_WIDGET (row));
+ gtk_widget_set_state_flags (GTK_WIDGET (row), GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
priv->drag_highlighted_row = g_object_ref (row);
}
static void
-gtk_list_box_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
-{
- gtk_list_box_drag_unhighlight_row (GTK_LIST_BOX (widget));
-}
-
-static void
gtk_list_box_activate_cursor_row (GtkListBox *box)
{
gtk_list_box_select_and_activate (box, BOX_PRIV (box)->cursor_row);
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 37ee557c48..887ce9bd14 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -116,7 +116,7 @@
#include "gtkaccelmapprivate.h"
#include "gtkbox.h"
#include "gtkdebug.h"
-#include "gtkdndprivate.h"
+#include "gtkdragdestprivate.h"
#include "gtkmain.h"
#include "gtkmediafileprivate.h"
#include "gtkmodulesprivate.h"
@@ -1686,6 +1686,20 @@ is_focus_event (GdkEvent *event)
}
}
+static gboolean
+is_dnd_event (GdkEvent *event)
+{
+ switch ((guint) event->any.type)
+ {
+ case GDK_DRAG_ENTER:
+ case GDK_DRAG_LEAVE:
+ case GDK_DRAG_MOTION:
+ case GDK_DROP_START:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
static inline void
set_widget_active_state (GtkWidget *target,
@@ -1828,6 +1842,25 @@ handle_key_event (GdkEvent *event)
return focus_widget ? focus_widget : event_widget;
}
+static GtkWidget *
+handle_dnd_event (GdkEvent *event)
+{
+ GtkWidget *event_widget;
+ GtkWidget *target;
+ gdouble x, y;
+ GtkWidget *native;
+
+ event_widget = gtk_get_event_widget (event);
+
+ if (!gdk_event_get_coords (event, &x, &y))
+ return event_widget;
+
+ native = GTK_WIDGET (gtk_widget_get_native (event_widget));
+ target = gtk_widget_pick (native, x, y, GTK_PICK_DEFAULT);
+
+ return target;
+}
+
/**
* gtk_main_do_event:
* @event: An event to process (normally passed by GDK)
@@ -1924,6 +1957,8 @@ gtk_main_do_event (GdkEvent *event)
goto cleanup;
}
}
+ else if (is_dnd_event (event))
+ target_widget = handle_dnd_event (event);
if (!target_widget)
goto cleanup;
@@ -2038,12 +2073,17 @@ gtk_main_do_event (GdkEvent *event)
/* Crossing event propagation happens during picking */
break;
- case GDK_DRAG_ENTER:
- case GDK_DRAG_LEAVE:
case GDK_DRAG_MOTION:
case GDK_DROP_START:
- _gtk_drag_dest_handle_event (target_widget, event);
+ if (gtk_propagate_event (target_widget, event))
+ break;
+ G_GNUC_FALLTHROUGH;
+
+ case GDK_DRAG_ENTER:
+ case GDK_DRAG_LEAVE:
+ gtk_drag_dest_handle_event (target_widget, event);
break;
+
case GDK_EVENT_LAST:
default:
g_assert_not_reached ();
@@ -2633,14 +2673,19 @@ propagate_event_down (GtkWidget *widget,
return handled_event;
}
-void
+gboolean
gtk_propagate_event_internal (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost)
{
/* Propagate the event down and up */
- if (!propagate_event_down (widget, event, topmost))
- propagate_event_up (widget, event, topmost);
+ if (propagate_event_down (widget, event, topmost))
+ return TRUE;
+
+ if (propagate_event_up (widget, event, topmost))
+ return TRUE;
+
+ return FALSE;
}
/**
@@ -2667,8 +2712,10 @@ gtk_propagate_event_internal (GtkWidget *widget,
* certainly better ways to achieve your goals. For example, use
* gtk_widget_queue_draw() instead
* of making up expose events.
+ *
+ * Returns: %TRUE if the event was handled
*/
-void
+gboolean
gtk_propagate_event (GtkWidget *widget,
GdkEvent *event)
{
@@ -2676,8 +2723,8 @@ gtk_propagate_event (GtkWidget *widget,
GtkWidget *event_widget, *topmost = NULL;
GdkDevice *device;
- g_return_if_fail (GTK_IS_WIDGET (widget));
- g_return_if_fail (event != NULL);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
event_widget = gtk_get_event_widget (event);
window_group = gtk_main_get_window_group (event_widget);
@@ -2689,5 +2736,5 @@ gtk_propagate_event (GtkWidget *widget,
if (!topmost)
topmost = gtk_window_group_get_current_grab (window_group);
- gtk_propagate_event_internal (widget, event, topmost);
+ return gtk_propagate_event_internal (widget, event, topmost);
}
diff --git a/gtk/gtkmain.h b/gtk/gtkmain.h
index 81c1e574ad..0009c4d90e 100644
--- a/gtk/gtkmain.h
+++ b/gtk/gtkmain.h
@@ -160,7 +160,7 @@ GtkWidget *gtk_get_event_target_with_type (GdkEvent *event,
GType type);
GDK_AVAILABLE_IN_ALL
-void gtk_propagate_event (GtkWidget *widget,
+gboolean gtk_propagate_event (GtkWidget *widget,
GdkEvent *event);
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index 16c5c1caf2..ee0d355f85 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -58,6 +58,7 @@ OBJECT:VOID
STRING:DOUBLE
STRING:STRING
VOID:BOOLEAN,BOOLEAN,BOOLEAN
+VOID:BOXED
VOID:BOXED,BOXED
VOID:BOXED,BOXED,POINTER
VOID:BOXED,ENUM
diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
index c2dd98e900..c8fa3c94fd 100644
--- a/gtk/gtknotebook.c
+++ b/gtk/gtknotebook.c
@@ -33,8 +33,8 @@
#include "gtkbuildable.h"
#include "gtkbutton.h"
#include "gtkcssstylepropertyprivate.h"
-#include "gtkdnd.h"
#include "gtkdragdest.h"
+#include "gtkdragicon.h"
#include "gtkeventcontrollermotion.h"
#include "gtkgestureclick.h"
#include "gtkgizmoprivate.h"
@@ -52,6 +52,10 @@
#include "gtktypebuiltins.h"
#include "gtkwidgetpath.h"
#include "gtkwidgetprivate.h"
+#include "gtkdragsource.h"
+#include "gtkwidgetpaintable.h"
+#include "gtkselectionprivate.h"
+#include "gtknative.h"
#include "a11y/gtknotebookaccessible.h"
@@ -234,7 +238,6 @@ struct _GtkNotebookPrivate
GtkNotebookPage *detached_tab;
GdkContentFormats *source_targets;
GtkWidget *action_widget[N_ACTION_WIDGETS];
- GtkWidget *dnd_child;
GtkWidget *menu;
GtkWidget *menu_box;
@@ -695,29 +698,22 @@ static gboolean gtk_notebook_focus (GtkWidget *widget,
GtkDirectionType direction);
/*** Drag and drop Methods ***/
-static void gtk_notebook_drag_begin (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_notebook_drag_end (GtkWidget *widget,
- GdkDrag *drag);
-static gboolean gtk_notebook_drag_failed (GtkWidget *widget,
- GdkDrag *drag,
- GtkDragResult result);
-static gboolean gtk_notebook_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y);
-static void gtk_notebook_drag_leave (GtkWidget *widget,
- GdkDrop *drop);
-static gboolean gtk_notebook_drag_drop (GtkWidget *widget,
+static void gtk_notebook_dnd_finished_cb (GdkDrag *drag,
+ GtkWidget *widget);
+static void gtk_notebook_drag_cancel_cb (GdkDrag *drag,
+ GdkDragCancelReason reason,
+ GtkWidget *widget);
+static void gtk_notebook_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static void gtk_notebook_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *data);
-static void gtk_notebook_drag_data_received (GtkWidget *widget,
+ int x,
+ int y);
+static void gtk_notebook_drag_leave (GtkDropTarget *dest);
+static gboolean gtk_notebook_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
- GtkSelectionData *data);
+ int x,
+ int y);
+static GBytes * gtk_notebook_drag_data_get (const char *mime_type,
+ gpointer data);
/*** GtkContainer Methods ***/
static void gtk_notebook_add (GtkContainer *container,
@@ -964,14 +960,6 @@ gtk_notebook_class_init (GtkNotebookClass *class)
widget_class->grab_notify = gtk_notebook_grab_notify;
widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
widget_class->focus = gtk_notebook_focus;
- widget_class->drag_begin = gtk_notebook_drag_begin;
- widget_class->drag_end = gtk_notebook_drag_end;
- widget_class->drag_motion = gtk_notebook_drag_motion;
- widget_class->drag_leave = gtk_notebook_drag_leave;
- widget_class->drag_drop = gtk_notebook_drag_drop;
- widget_class->drag_data_get = gtk_notebook_drag_data_get;
- widget_class->drag_data_received = gtk_notebook_drag_data_received;
- widget_class->drag_failed = gtk_notebook_drag_failed;
widget_class->compute_expand = gtk_notebook_compute_expand;
container_class->add = gtk_notebook_add;
@@ -1306,6 +1294,7 @@ gtk_notebook_init (GtkNotebook *notebook)
GtkEventController *controller;
GtkGesture *gesture;
GtkLayoutManager *layout;
+ GtkDropTarget *dest;
gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
@@ -1337,14 +1326,6 @@ gtk_notebook_init (GtkNotebook *notebook)
priv->detached_tab = NULL;
priv->has_scrolled = FALSE;
- targets = gdk_content_formats_new (dst_notebook_targets, G_N_ELEMENTS (dst_notebook_targets));
- gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
- targets,
- GDK_ACTION_MOVE);
- gdk_content_formats_unref (targets);
-
- gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
-
priv->header_widget = g_object_new (GTK_TYPE_BOX,
"css-name", "header",
NULL);
@@ -1366,6 +1347,14 @@ gtk_notebook_init (GtkNotebook *notebook)
gtk_widget_set_vexpand (priv->stack_widget, TRUE);
gtk_widget_set_parent (priv->stack_widget, GTK_WIDGET (notebook));
+ targets = gdk_content_formats_new (dst_notebook_targets, G_N_ELEMENTS (dst_notebook_targets));
+ dest = gtk_drop_target_new (targets, GDK_ACTION_MOVE);
+ g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_notebook_drag_motion), NULL);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_notebook_drag_leave), NULL);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_notebook_drag_drop), NULL);
+ gtk_widget_add_controller (GTK_WIDGET (priv->tabs_widget), GTK_EVENT_CONTROLLER (dest));
+ gdk_content_formats_unref (targets);
+
gesture = gtk_gesture_click_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_CAPTURE);
@@ -1835,7 +1824,6 @@ gtk_notebook_get_property (GObject *object,
* gtk_notebook_drag_motion
* gtk_notebook_drag_drop
* gtk_notebook_drag_data_get
- * gtk_notebook_drag_data_received
*/
static void
remove_switch_tab_timer (GtkNotebook *notebook)
@@ -2881,12 +2869,43 @@ gtk_notebook_motion (GtkEventController *controller,
if (page->detachable &&
check_threshold (notebook, priv->mouse_x, priv->mouse_y))
{
+ GdkSurface *surface;
+ GdkDevice *device;
+ GdkContentProvider *content;
+ GdkDrag *drag;
+ GdkPaintable *paintable;
+
priv->detached_tab = priv->cur_page;
- gtk_drag_begin (widget,
- gtk_get_current_event_device (),
- priv->source_targets, GDK_ACTION_MOVE,
- priv->drag_begin_x, priv->drag_begin_y);
+ surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (notebook)));
+ device = gtk_get_current_event_device ();
+
+ content = gdk_content_provider_new_with_formats (priv->source_targets,
+ gtk_notebook_drag_data_get,
+ widget);
+ drag = gdk_drag_begin (surface, device, content, GDK_ACTION_MOVE, priv->drag_begin_x, priv->drag_begin_y);
+ g_object_unref (content);
+
+ g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_notebook_dnd_finished_cb), notebook);
+ g_signal_connect (drag, "cancel", G_CALLBACK (gtk_notebook_drag_cancel_cb), notebook);
+
+ paintable = gtk_widget_paintable_new (priv->detached_tab->tab_widget);
+ gtk_drag_icon_set_from_paintable (drag, paintable, -2, -2);
+ g_object_unref (paintable);
+
+ if (priv->dnd_timer)
+ {
+ g_source_remove (priv->dnd_timer);
+ priv->dnd_timer = 0;
+ }
+
+ priv->operation = DRAG_OPERATION_DETACH;
+ tab_drag_end (notebook, priv->cur_page);
+
+ g_object_set_data (G_OBJECT (drag), "gtk-notebook-drag-origin", notebook);
+
+ g_object_unref (drag);
+
return;
}
@@ -3088,46 +3107,8 @@ update_arrow_nodes (GtkNotebook *notebook)
}
static void
-gtk_notebook_drag_begin (GtkWidget *widget,
- GdkDrag *drag)
-{
- GtkNotebook *notebook = GTK_NOTEBOOK (widget);
- GtkNotebookPrivate *priv = notebook->priv;
- graphene_rect_t bounds;
- GtkWidget *tab_label;
-
- if (priv->dnd_timer)
- {
- g_source_remove (priv->dnd_timer);
- priv->dnd_timer = 0;
- }
-
- g_assert (priv->cur_page != NULL);
-
- priv->operation = DRAG_OPERATION_DETACH;
-
- tab_label = priv->detached_tab->tab_label;
-
- tab_drag_end (notebook, priv->cur_page);
- g_object_ref (tab_label);
- gtk_widget_unparent (tab_label);
-
- priv->dnd_child = tab_label;
- if (gtk_widget_compute_bounds (priv->dnd_child, priv->dnd_child, &bounds))
- gtk_widget_set_size_request (priv->dnd_child,
- ceilf (bounds.size.width),
- ceilf (bounds.size.height));
-
- gtk_style_context_add_class (gtk_widget_get_style_context (priv->dnd_child), "background");
-
- gtk_drag_set_icon_widget (drag, tab_label, -2, -2);
- g_object_set_data (G_OBJECT (priv->dnd_child), "drag-context", drag);
- g_object_unref (tab_label);
-}
-
-static void
-gtk_notebook_drag_end (GtkWidget *widget,
- GdkDrag *drag)
+gtk_notebook_dnd_finished_cb (GdkDrag *drag,
+ GtkWidget *widget)
{
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
GtkNotebookPrivate *priv = notebook->priv;
@@ -3148,17 +3129,9 @@ gtk_notebook_drag_end (GtkWidget *widget,
}
else if (priv->detached_tab)
{
- gtk_widget_set_size_request (priv->dnd_child, -1, -1);
- g_object_ref (priv->dnd_child);
- gtk_widget_unparent (priv->dnd_child);
- gtk_widget_set_parent (priv->dnd_child, priv->detached_tab->tab_widget);
- g_object_unref (priv->dnd_child);
gtk_notebook_switch_page (notebook, priv->detached_tab);
}
- gtk_style_context_remove_class (gtk_widget_get_style_context (priv->dnd_child), "background");
- priv->dnd_child = NULL;
-
priv->operation = DRAG_OPERATION_NONE;
}
@@ -3169,17 +3142,17 @@ gtk_notebook_create_window (GtkNotebook *notebook,
return NULL;
}
-static gboolean
-gtk_notebook_drag_failed (GtkWidget *widget,
- GdkDrag *drag,
- GtkDragResult result)
+static void
+gtk_notebook_drag_cancel_cb (GdkDrag *drag,
+ GdkDragCancelReason reason,
+ GtkWidget *widget)
{
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
GtkNotebookPrivate *priv = notebook->priv;
priv->rootwindow_drop = FALSE;
- if (result == GTK_DRAG_RESULT_NO_TARGET)
+ if (reason == GDK_DRAG_CANCEL_NO_TARGET)
{
GtkNotebook *dest_notebook = NULL;
@@ -3188,11 +3161,7 @@ gtk_notebook_drag_failed (GtkWidget *widget,
if (dest_notebook)
do_detach_tab (notebook, dest_notebook, priv->detached_tab->child);
-
- return TRUE;
}
-
- return FALSE;
}
static gboolean
@@ -3219,19 +3188,20 @@ gtk_notebook_switch_tab_timeout (gpointer data)
return FALSE;
}
-static gboolean
-gtk_notebook_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+static void
+gtk_notebook_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y)
{
+ GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK);
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
GtkNotebookPrivate *priv = notebook->priv;
graphene_rect_t position;
GtkNotebookArrow arrow;
GdkAtom target, tab_target;
GList *tab;
- gboolean retval = FALSE;
arrow = gtk_notebook_get_arrow (notebook, x, y);
if (arrow != ARROW_NONE)
@@ -3240,41 +3210,46 @@ gtk_notebook_drag_motion (GtkWidget *widget,
gtk_notebook_set_scroll_timer (notebook);
gdk_drop_status (drop, 0);
- retval = TRUE;
goto out;
}
stop_scrolling (notebook);
- target = gtk_drag_dest_find_target (widget, drop, NULL);
+ target = gtk_drop_target_find_mimetype (dest);
tab_target = g_intern_static_string ("GTK_NOTEBOOK_TAB");
if (target == tab_target)
{
GQuark group, source_group;
- GtkNotebook *source;
GtkWidget *source_child;
+ GdkDrag *drag = gdk_drop_get_drag (drop);
- retval = TRUE;
-
- source = GTK_NOTEBOOK (gtk_drag_get_source_widget (gdk_drop_get_drag (drop)));
- g_assert (source->priv->cur_page != NULL);
- source_child = source->priv->cur_page->child;
-
- group = notebook->priv->group;
- source_group = source->priv->group;
-
- if (group != 0 && group == source_group &&
- !(widget == source_child ||
- gtk_widget_is_ancestor (widget, source_child)))
+ if (!drag)
{
- gdk_drop_status (drop, GDK_ACTION_MOVE);
- goto out;
+ gdk_drop_status (drop, 0);
}
else
{
- /* it's a tab, but doesn't share
- * ID with this notebook */
- gdk_drop_status (drop, 0);
+ GtkNotebook *source = GTK_NOTEBOOK (g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin"));
+
+ g_assert (source->priv->cur_page != NULL);
+ source_child = source->priv->cur_page->child;
+
+ group = notebook->priv->group;
+ source_group = source->priv->group;
+
+ if (group != 0 && group == source_group &&
+ !(widget == source_child ||
+ gtk_widget_is_ancestor (widget, source_child)))
+ {
+ gdk_drop_status (drop, GDK_ACTION_MOVE);
+ goto out;
+ }
+ else
+ {
+ /* it's a tab, but doesn't share
+ * ID with this notebook */
+ gdk_drop_status (drop, 0);
+ }
}
}
@@ -3285,8 +3260,6 @@ gtk_notebook_drag_motion (GtkWidget *widget,
priv->mouse_x = x;
priv->mouse_y = y;
- retval = TRUE;
-
if (tab != priv->switch_tab)
remove_switch_tab_timer (notebook);
@@ -3303,40 +3276,86 @@ gtk_notebook_drag_motion (GtkWidget *widget,
remove_switch_tab_timer (notebook);
}
- out:
- return retval;
+ out:;
}
static void
-gtk_notebook_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
+gtk_notebook_drag_leave (GtkDropTarget *dest)
{
+ GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK);
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
remove_switch_tab_timer (notebook);
stop_scrolling (notebook);
}
+static void
+got_page (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GtkNotebook *notebook = GTK_NOTEBOOK (data);
+ GdkDrop *drop = GDK_DROP (source);
+ GdkDrag *drag = gdk_drop_get_drag (drop);
+ GtkWidget *source_widget;
+ GInputStream *stream;
+ const char *mime_type;
+
+ source_widget = GTK_WIDGET (drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL);
+
+ stream = gdk_drop_read_finish (drop, result, &mime_type, NULL);
+
+ if (stream)
+ {
+ GBytes *bytes;
+ GtkWidget **child;
+
+ bytes = g_input_stream_read_bytes (stream, sizeof (gpointer), NULL, NULL);
+ child = (gpointer)g_bytes_get_data (bytes, NULL);
+
+ do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child);
+ gdk_drop_finish (drop, GDK_ACTION_MOVE);
+
+ g_bytes_unref (bytes);
+ g_object_unref (stream);
+ }
+ else
+ gdk_drop_finish (drop, 0);
+}
+
static gboolean
-gtk_notebook_drag_drop (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+gtk_notebook_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y)
{
+ GtkWidget *tabs = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ GtkWidget *widget = gtk_widget_get_ancestor (tabs, GTK_TYPE_NOTEBOOK);
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
+ GdkDrag *drag = gdk_drop_get_drag (drop);
+ GtkWidget *source_widget;
GdkAtom target, tab_target;
- target = gtk_drag_dest_find_target (widget, drop, NULL);
+ source_widget = GTK_WIDGET (drag ? g_object_get_data (G_OBJECT (drag), "gtk-notebook-drag-origin") : NULL);
+
+ target = gtk_drop_target_find_mimetype (dest);
tab_target = g_intern_static_string ("GTK_NOTEBOOK_TAB");
- if (target == tab_target)
+ if (GTK_IS_NOTEBOOK (source_widget) &&
+ target == tab_target &&
+ (gdk_drop_get_actions (drop) & GDK_ACTION_MOVE))
{
notebook->priv->mouse_x = x;
notebook->priv->mouse_y = y;
- gtk_drag_get_data (widget, drop, target);
+
+ gdk_drop_read_async (drop, (const char *[]) { "GTK_NOTEBOOK_TAB", NULL }, G_PRIORITY_DEFAULT, NULL, got_page, notebook);
+
return TRUE;
}
+ gdk_drop_finish (drop, 0);
+
return FALSE;
}
@@ -3419,57 +3438,32 @@ do_detach_tab (GtkNotebook *from,
gtk_notebook_set_current_page (to, page_num);
}
-static void
-gtk_notebook_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *data)
+static GBytes *
+gtk_notebook_drag_data_get (const char *mime_type,
+ gpointer data)
{
- GtkNotebook *notebook = GTK_NOTEBOOK (widget);
+ GtkNotebook *notebook = GTK_NOTEBOOK (data);
GtkNotebookPrivate *priv = notebook->priv;
- GdkAtom target;
+ GtkSelectionData sdata = { 0, };
+
+ sdata.target = g_intern_string (mime_type);
- target = gtk_selection_data_get_target (data);
- if (target == g_intern_static_string ("GTK_NOTEBOOK_TAB"))
+ if (sdata.target == g_intern_static_string ("GTK_NOTEBOOK_TAB"))
{
- gtk_selection_data_set (data,
- target,
+ gtk_selection_data_set (&sdata,
+ sdata.target,
8,
(void*) &priv->detached_tab->child,
sizeof (gpointer));
priv->rootwindow_drop = FALSE;
}
- else if (target == g_intern_static_string ("application/x-rootwindow-drop"))
+ else if (sdata.target == g_intern_static_string ("application/x-rootwindow-drop"))
{
- gtk_selection_data_set (data, target, 8, NULL, 0);
+ gtk_selection_data_set (&sdata, sdata.target, 8, NULL, 0);
priv->rootwindow_drop = TRUE;
}
-}
-
-static void
-gtk_notebook_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *data)
-{
- GtkNotebook *notebook;
- GdkDrag *drag;
- GtkWidget *source_widget;
- GtkWidget **child;
-
- notebook = GTK_NOTEBOOK (widget);
- drag = gdk_drop_get_drag (drop);
- source_widget = gtk_drag_get_source_widget (drag);
-
- if (source_widget &&
- (gdk_drop_get_actions (drop) & GDK_ACTION_MOVE) &&
- gtk_selection_data_get_target (data) == g_intern_static_string ("GTK_NOTEBOOK_TAB"))
- {
- child = (void*) gtk_selection_data_get_data (data);
- do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child);
- gdk_drop_finish (drop, GDK_ACTION_MOVE);
- }
- else
- gdk_drop_finish (drop, 0);
+ return g_bytes_new_take (sdata.data, sdata.length);
}
/* Private GtkContainer Methods :
@@ -4249,17 +4243,8 @@ gtk_notebook_real_remove (GtkNotebook *notebook,
}
if (priv->detached_tab == list->data)
- {
- priv->detached_tab = NULL;
+ priv->detached_tab = NULL;
- if (priv->operation == DRAG_OPERATION_DETACH && !priv->remove_in_detach)
- {
- GdkDrag *drag;
-
- drag = (GdkDrag *)g_object_get_data (G_OBJECT (priv->dnd_child), "drag-context");
- gtk_drag_cancel (drag);
- }
- }
if (priv->switch_tab == list)
priv->switch_tab = NULL;
@@ -7099,15 +7084,17 @@ gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
* |[<!-- language="C" -->
* static void
* on_drag_data_received (GtkWidget *widget,
- * GdkDrag *drag,
+ * GdkDrop *drop,
* GtkSelectionData *data,
* guint time,
* gpointer user_data)
* {
+ * GtkDrag *drag;
* GtkWidget *notebook;
* GtkWidget **child;
*
- * notebook = gtk_drag_get_source_widget (drag);
+ * drag = gtk_drop_get_drag (drop);
+ * notebook = g_object_get_data (drag, "gtk-notebook-drag-origin");
* child = (void*) gtk_selection_data_get_data (data);
*
* // process_widget (*child);
diff --git a/gtk/gtkpathbar.c b/gtk/gtkpathbar.c
index 5bc9906888..37a7b05487 100644
--- a/gtk/gtkpathbar.c
+++ b/gtk/gtkpathbar.c
@@ -24,7 +24,6 @@
#include "gtkbox.h"
#include "gtkcssnodeprivate.h"
-#include "gtkdnd.h"
#include "gtkdragsource.h"
#include "gtkicontheme.h"
#include "gtkimage.h"
@@ -37,6 +36,7 @@
#include "gtkwidgetpath.h"
#include "gtkwidgetprivate.h"
#include "gtkeventcontrollerscroll.h"
+#include "gtkdragsource.h"
typedef struct
{
@@ -1240,15 +1240,8 @@ set_button_image (GtkPathBar *path_bar,
static void
button_data_free (ButtonData *button_data)
{
- if (button_data->file)
- g_object_unref (button_data->file);
- button_data->file = NULL;
-
+ g_clear_object (&button_data->file);
g_free (button_data->dir_name);
- button_data->dir_name = NULL;
-
- button_data->button = NULL;
-
g_free (button_data);
}
@@ -1312,25 +1305,6 @@ find_button_type (GtkPathBar *path_bar,
return NORMAL_BUTTON;
}
-static void
-button_drag_data_get_cb (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- gpointer data)
-{
- ButtonData *button_data;
- char *uris[2];
-
- button_data = data;
-
- uris[0] = g_file_get_uri (button_data->file);
- uris[1] = NULL;
-
- gtk_selection_data_set_uris (selection_data, uris);
-
- g_free (uris[0]);
-}
-
static ButtonData *
make_directory_button (GtkPathBar *path_bar,
const char *dir_name,
@@ -1341,6 +1315,9 @@ make_directory_button (GtkPathBar *path_bar,
AtkObject *atk_obj;
GtkWidget *child = NULL;
ButtonData *button_data;
+ GValue value = G_VALUE_INIT;
+ GdkContentProvider *content;
+ GtkDragSource *source;
file_is_hidden = !! file_is_hidden;
/* Is it a special button? */
@@ -1387,13 +1364,14 @@ make_directory_button (GtkPathBar *path_bar,
g_object_weak_ref (G_OBJECT (button_data->button),
(GWeakNotify) button_data_free, button_data);
- gtk_drag_source_set (button_data->button,
- GDK_BUTTON1_MASK,
- NULL,
- GDK_ACTION_COPY);
- gtk_drag_source_add_uri_targets (button_data->button);
- g_signal_connect (button_data->button, "drag-data-get",
- G_CALLBACK (button_drag_data_get_cb), button_data);
+ g_value_init (&value, G_TYPE_FILE);
+ g_value_set_object (&value, button_data->file);
+ source = gtk_drag_source_new ();
+ content = gdk_content_provider_new_for_value (&value);
+ gtk_drag_source_set_content (source, content);
+ g_object_unref (content);
+ gtk_widget_add_controller (button_data->button, GTK_EVENT_CONTROLLER (source));
+ g_value_unset (&value);
return button_data;
}
diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c
index 6af770161b..82c6ddad83 100644
--- a/gtk/gtkplacessidebar.c
+++ b/gtk/gtkplacessidebar.c
@@ -52,7 +52,6 @@
#include "gtklistbox.h"
#include "gtkselection.h"
#include "gtkdragdest.h"
-#include "gtkdnd.h"
#include "gtkseparator.h"
#include "gtkentry.h"
#include "gtkgesturelongpress.h"
@@ -63,6 +62,10 @@
#include "gtkgestureclick.h"
#include "gtkgesturedrag.h"
#include "gtknative.h"
+#include "gtkdragsource.h"
+#include "gtkdragicon.h"
+#include "gtkwidgetpaintable.h"
+#include "gtkselectionprivate.h"
/*< private >
* SECTION:gtkplacessidebar
@@ -309,16 +312,6 @@ enum {
DND_TEXT_URI_LIST
};
-/* Target types for dragging from the shortcuts list */
-static const char *dnd_source_targets[] = {
- "DND_GTK_SIDEBAR_ROW"
-};
-
-/* Target types for dropping into the shortcuts list */
-static const char *dnd_drop_targets [] = {
- "DND_GTK_SIDEBAR_ROW"
-};
-
G_DEFINE_TYPE (GtkPlacesSidebar, gtk_places_sidebar, GTK_TYPE_WIDGET);
static void
@@ -1641,21 +1634,25 @@ update_possible_drop_targets (GtkPlacesSidebar *sidebar,
g_list_free (rows);
}
+static void drag_data_received_callback (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data);
+
static gboolean
get_drag_data (GtkWidget *list_box,
- GdkDrop *drop,
+ GtkDropTarget *dest,
GtkListBoxRow *row)
{
GdkAtom target;
- target = gtk_drag_dest_find_target (list_box, drop, NULL);
+ target = gtk_drop_target_find_mimetype (dest);
if (target == NULL)
return FALSE;
if (row)
- g_object_set_data_full (G_OBJECT (drop), "places-sidebar-row", g_object_ref (row), g_object_unref);
- gtk_drag_get_data (list_box, drop, target);
+ g_object_set_data_full (G_OBJECT (dest), "places-sidebar-row", g_object_ref (row), g_object_unref);
+ gtk_drop_target_read_selection (dest, target, NULL, drag_data_received_callback, list_box);
return TRUE;
}
@@ -1678,7 +1675,8 @@ start_drop_feedback (GtkPlacesSidebar *sidebar,
GtkSidebarRow *row,
GdkDrag *drag)
{
- if (sidebar->drag_data_info != DND_GTK_SIDEBAR_ROW)
+ if (sidebar->drag_data_received &&
+ sidebar->drag_data_info != DND_GTK_SIDEBAR_ROW)
{
gtk_sidebar_row_reveal (GTK_SIDEBAR_ROW (sidebar->new_bookmark_row));
/* If the state is permanent, don't change it. The application controls it. */
@@ -1719,48 +1717,22 @@ stop_drop_feedback (GtkPlacesSidebar *sidebar)
sidebar->drag_data_info = DND_UNKNOWN;
}
-static void
-drag_begin_callback (GtkWidget *widget,
- GdkDrag *drag,
- gpointer user_data)
-{
- GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
- GtkAllocation allocation;
- GtkWidget *drag_widget;
-
- gtk_widget_get_allocation (sidebar->drag_row, &allocation);
- gtk_widget_hide (sidebar->drag_row);
-
- drag_widget = GTK_WIDGET (gtk_sidebar_row_clone (GTK_SIDEBAR_ROW (sidebar->drag_row)));
- sidebar->drag_row_height = allocation.height;
- gtk_widget_set_size_request (drag_widget, allocation.width, allocation.height);
-
- gtk_widget_set_opacity (drag_widget, 0.8);
-
- gtk_drag_set_icon_widget (drag,
- drag_widget,
- sidebar->drag_row_x,
- sidebar->drag_row_y);
-}
-
static GtkWidget *
create_placeholder_row (GtkPlacesSidebar *sidebar)
{
- return g_object_new (GTK_TYPE_SIDEBAR_ROW,
- "placeholder", TRUE,
- NULL);
+ return g_object_new (GTK_TYPE_SIDEBAR_ROW, "placeholder", TRUE, NULL);
}
-static gboolean
-drag_motion_callback (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y,
- gpointer user_data)
+static void
+drag_motion_callback (GtkDropTarget *dest,
+ GdkDrop *drop,
+ gint x,
+ gint y,
+ gpointer user_data)
{
+ GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
gint action;
GtkListBoxRow *row;
- GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
GtkPlacesSidebarPlaceType place_type;
gchar *drop_target_uri = NULL;
gint row_index;
@@ -1776,7 +1748,7 @@ drag_motion_callback (GtkWidget *widget,
/* Nothing to do if no drag data */
if (!sidebar->drag_data_received &&
- !get_drag_data (sidebar->list_box, drop, row))
+ !get_drag_data (sidebar->list_box, dest, row))
goto out;
/* Nothing to do if the target is not valid drop destination */
@@ -1791,7 +1763,6 @@ drag_motion_callback (GtkWidget *widget,
if (sidebar->row_placeholder == NULL)
{
sidebar->row_placeholder = create_placeholder_row (sidebar);
- gtk_widget_show (sidebar->row_placeholder);
g_object_ref_sink (sidebar->row_placeholder);
}
else if (GTK_WIDGET (row) == sidebar->row_placeholder)
@@ -1822,7 +1793,7 @@ drag_motion_callback (GtkWidget *widget,
* of the row, we need to increase the order-index.
*/
row_placeholder_index = row_index;
- gtk_widget_translate_coordinates (widget, GTK_WIDGET (row),
+ gtk_widget_translate_coordinates (GTK_WIDGET (sidebar), GTK_WIDGET (row),
x, y,
&dest_x, &dest_y);
@@ -1879,12 +1850,7 @@ drag_motion_callback (GtkWidget *widget,
out:
start_drop_feedback (sidebar, GTK_SIDEBAR_ROW (row), drag);
-
- g_signal_stop_emission_by_name (sidebar->list_box, "drag-motion");
-
gdk_drop_status (drop, action);
-
- return TRUE;
}
/* Takes an array of URIs and turns it into a list of GFile */
@@ -1950,42 +1916,41 @@ drop_files_as_bookmarks (GtkPlacesSidebar *sidebar,
}
}
-static void
-drag_data_get_callback (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *data,
- gpointer user_data)
+static GBytes *
+drag_data_get_callback (const char *mimetype,
+ gpointer user_data)
{
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
- GdkAtom target = gtk_selection_data_get_target (data);
- if (target == g_intern_static_string ("DND_GTK_SIDEBAR_ROW"))
- {
- gtk_selection_data_set (data,
- target,
- 8,
- (void*)&sidebar->drag_row,
- sizeof (gpointer));
- }
+ if (mimetype == g_intern_static_string ("DND_GTK_SIDEBAR_ROW"))
+ return g_bytes_new ((gpointer)&sidebar->drag_row, sizeof (gpointer));
+
+ return NULL;
}
static void
-drag_data_received_callback (GtkWidget *list_box,
- GdkDrop *drop,
- GtkSelectionData *selection_data,
- gpointer user_data)
-{
+drag_data_received_callback (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GtkDropTarget *dest = GTK_DROP_TARGET (source);
+ GtkWidget *list_box = user_data;
+ GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (gtk_widget_get_ancestor (list_box, GTK_TYPE_PLACES_SIDEBAR));
+ GdkDrop *drop = gtk_drop_target_get_drop (dest);
+ GdkDrag *drag = gdk_drop_get_drag (drop);
gint target_order_index;
GtkPlacesSidebarPlaceType target_place_type;
GtkPlacesSidebarSectionType target_section_type;
gchar *target_uri;
- GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
GtkListBoxRow *target_row;
GdkDragAction real_action;
+ GtkSelectionData *selection_data;
+
+ selection_data = gtk_drop_target_read_selection_finish (dest, result, NULL);
if (!sidebar->drag_data_received)
{
- if (gtk_selection_data_targets_include_uri (selection_data))
+ if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("text/uri-list"))
{
gchar **uris;
@@ -2001,19 +1966,22 @@ drag_data_received_callback (GtkWidget *list_box,
{
sidebar->drag_list = NULL;
if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("DND_GTK_SIDEBAR_ROW"))
- sidebar->drag_data_info = DND_GTK_SIDEBAR_ROW;
+ {
+ sidebar->drag_data_info = DND_GTK_SIDEBAR_ROW;
+ }
}
sidebar->drag_data_received = TRUE;
}
- g_signal_stop_emission_by_name (list_box, "drag-data-received");
-
if (!sidebar->drop_occurred)
- return;
+ goto out_free;
- target_row = g_object_get_data (G_OBJECT (drop), "places-sidebar-row");
+ target_row = g_object_get_data (G_OBJECT (dest), "places-sidebar-row");
if (target_row == NULL)
- return;
+ goto out_free;
+
+ if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (target_row), drag))
+ goto out_free;
g_object_get (target_row,
"place-type", &target_place_type,
@@ -2021,12 +1989,8 @@ drag_data_received_callback (GtkWidget *list_box,
"order-index", &target_order_index,
"uri", &target_uri,
NULL);
-
real_action = 0;
- if (!check_valid_drop_target (sidebar, GTK_SIDEBAR_ROW (target_row), gdk_drop_get_drag (drop)))
- goto out;
-
if (sidebar->drag_data_info == DND_GTK_SIDEBAR_ROW)
{
GtkWidget **source_row;
@@ -2081,18 +2045,20 @@ drag_data_received_callback (GtkWidget *list_box,
out:
sidebar->drop_occurred = FALSE;
- g_object_set_data (G_OBJECT (drop), "places-sidebar-row", NULL);
+ g_object_set_data (G_OBJECT (dest), "places-sidebar-row", NULL);
gdk_drop_finish (drop, real_action);
stop_drop_feedback (sidebar);
g_free (target_uri);
+
+out_free:
+ gtk_selection_data_free (selection_data);
}
static void
-drag_end_callback (GtkWidget *widget,
- GdkDrag *drag,
- gpointer user_data)
+dnd_finished_cb (GdkDrag *drag,
+ GtkPlacesSidebar *sidebar)
{
- stop_drop_feedback (GTK_PLACES_SIDEBAR (user_data));
+ stop_drop_feedback (sidebar);
}
/* This functions is called every time the drag source leaves
@@ -2105,8 +2071,8 @@ drag_end_callback (GtkWidget *widget,
* but that's not true, because this function is called also before drag_drop,
* which needs the data from the drag so we cannot free the drag data here.
* So now one could think we could just do nothing here, and wait for
- * drag-end or drag-failed signals and just stop_drop_feedback there. But that
- * is also not true, since when the drag comes from a diferent widget than the
+ * drag-end or drag-cancel signals and just stop_drop_feedback there. But that
+ * is also not true, since when the drag comes from a different widget than the
* sidebar, when the drag stops the last drag signal we receive is drag-leave.
* So here what we will do is restore the state of the sidebar as if no drag
* is being done (and if the application didnt request for permanent hints with
@@ -2114,9 +2080,9 @@ drag_end_callback (GtkWidget *widget,
* we build new drag data in drag_data_received.
*/
static void
-drag_leave_callback (GtkWidget *widget,
- GdkDrop *drop,
- gpointer user_data)
+drag_leave_callback (GtkDropTarget *dest,
+ GdkDrop *drop,
+ gpointer user_data)
{
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
@@ -2133,20 +2099,20 @@ drag_leave_callback (GtkWidget *widget,
}
static gboolean
-drag_drop_callback (GtkWidget *list_box,
- GdkDrop *drop,
- gint x,
- gint y,
- gpointer user_data)
+drag_drop_callback (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ gpointer user_data)
{
- gboolean retval = FALSE;
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
+ gboolean retval = FALSE;
GtkListBoxRow *row;
row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y);
sidebar->drop_occurred = TRUE;
- retval = get_drag_data (sidebar->list_box, drop, row);
- g_signal_stop_emission_by_name (sidebar->list_box, "drag-drop");
+
+ retval = get_drag_data (sidebar->list_box, dest, row);
return retval;
}
@@ -3778,6 +3744,13 @@ on_row_dragged (GtkGestureDrag *gesture,
{
gdouble start_x, start_y;
gint drag_x, drag_y;
+ GdkContentProvider *content;
+ GdkSurface *surface;
+ GdkDevice *device;
+ GtkAllocation allocation;
+ GtkWidget *drag_widget;
+ GdkPaintable *paintable;
+ GdkDrag *drag;
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
gtk_widget_translate_coordinates (GTK_WIDGET (row),
@@ -3787,10 +3760,35 @@ on_row_dragged (GtkGestureDrag *gesture,
sidebar->dragging_over = TRUE;
- gtk_drag_begin (GTK_WIDGET (sidebar),
- gtk_gesture_get_device (GTK_GESTURE (gesture)),
- sidebar->source_targets, GDK_ACTION_MOVE,
- drag_x, drag_y);
+ content = gdk_content_provider_new_with_formats (sidebar->source_targets,
+ drag_data_get_callback,
+ sidebar);
+
+ surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (sidebar)));
+ device = gtk_gesture_get_device (GTK_GESTURE (gesture));
+
+ drag = gdk_drag_begin (surface, device, content, GDK_ACTION_MOVE, drag_x, drag_y);
+
+ g_object_unref (content);
+
+ g_signal_connect (drag, "dnd-finished", G_CALLBACK (dnd_finished_cb), sidebar);
+
+ gtk_widget_get_allocation (sidebar->drag_row, &allocation);
+ gtk_widget_hide (sidebar->drag_row);
+
+ drag_widget = GTK_WIDGET (gtk_sidebar_row_clone (GTK_SIDEBAR_ROW (sidebar->drag_row)));
+ sidebar->drag_row_height = allocation.height;
+ gtk_widget_set_size_request (drag_widget, allocation.width, allocation.height);
+
+ gtk_widget_set_opacity (drag_widget, 0.8);
+
+ paintable = gtk_widget_paintable_new (drag_widget);
+ gtk_drag_icon_set_from_paintable (drag, paintable, sidebar->drag_row_x, sidebar->drag_row_y);
+ g_object_unref (paintable);
+
+ g_object_set_data_full (G_OBJECT (drag), "row-widget", drag_widget, (GDestroyNotify)gtk_widget_destroy);
+
+ g_object_unref (drag);
}
g_object_unref (sidebar);
@@ -4022,11 +4020,13 @@ shell_shows_desktop_changed (GtkSettings *settings,
static void
gtk_places_sidebar_init (GtkPlacesSidebar *sidebar)
{
- GdkContentFormats *target_list;
+ GdkContentFormats *formats;
+ GtkDropTarget *dest;
gboolean show_desktop;
GtkStyleContext *context;
GtkEventController *controller;
GtkGesture *gesture;
+ GdkContentFormatsBuilder *builder;
sidebar->cancellable = g_cancellable_new ();
@@ -4079,31 +4079,22 @@ gtk_places_sidebar_init (GtkPlacesSidebar *sidebar)
gtk_widget_add_controller (GTK_WIDGET (sidebar), GTK_EVENT_CONTROLLER (gesture));
/* DND support */
- gtk_drag_dest_set (sidebar->list_box,
- 0,
- NULL,
- GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
- target_list = gdk_content_formats_new (dnd_drop_targets, G_N_ELEMENTS (dnd_drop_targets));
- target_list = gtk_content_formats_add_uri_targets (target_list);
- gtk_drag_dest_set_target_list (sidebar->list_box, target_list);
- gdk_content_formats_unref (target_list);
- sidebar->source_targets = gdk_content_formats_new (dnd_source_targets, G_N_ELEMENTS (dnd_source_targets));
- sidebar->source_targets = gtk_content_formats_add_text_targets (sidebar->source_targets);
-
- g_signal_connect (sidebar->list_box, "drag-begin",
- G_CALLBACK (drag_begin_callback), sidebar);
- g_signal_connect (sidebar->list_box, "drag-motion",
- G_CALLBACK (drag_motion_callback), sidebar);
- g_signal_connect (sidebar->list_box, "drag-data-get",
- G_CALLBACK (drag_data_get_callback), sidebar);
- g_signal_connect (sidebar->list_box, "drag-data-received",
- G_CALLBACK (drag_data_received_callback), sidebar);
- g_signal_connect (sidebar->list_box, "drag-drop",
- G_CALLBACK (drag_drop_callback), sidebar);
- g_signal_connect (sidebar->list_box, "drag-end",
- G_CALLBACK (drag_end_callback), sidebar);
- g_signal_connect (sidebar->list_box, "drag-leave",
- G_CALLBACK (drag_leave_callback), sidebar);
+ builder = gdk_content_formats_builder_new ();
+ gdk_content_formats_builder_add_mime_type (builder, "DND_GTK_SIDEBAR_ROW");
+ gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_FILE_LIST);
+ formats = gdk_content_formats_builder_free_to_formats (builder);
+ dest = gtk_drop_target_new (formats, GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
+ gdk_content_formats_unref (formats);
+ g_signal_connect (dest, "drag-motion", G_CALLBACK (drag_motion_callback), sidebar);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop_callback), sidebar);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (drag_leave_callback), sidebar);
+ gtk_widget_add_controller (sidebar->list_box, GTK_EVENT_CONTROLLER (dest));
+
+ builder = gdk_content_formats_builder_new ();
+ gdk_content_formats_builder_add_mime_type (builder, "DND_GTK_SIDEBAR_ROW");
+ gdk_content_formats_builder_add_gtype (builder, G_TYPE_STRING);
+ sidebar->source_targets = gdk_content_formats_builder_free_to_formats (builder);
+
sidebar->drag_row = NULL;
sidebar->row_placeholder = NULL;
sidebar->dragging_over = FALSE;
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
index bcda0fdd50..a963b35110 100644
--- a/gtk/gtkpopover.c
+++ b/gtk/gtkpopover.c
@@ -1482,7 +1482,8 @@ gtk_popover_set_relative_to (GtkPopover *popover,
if (priv->relative_to)
{
- g_signal_connect (priv->relative_to, "size-allocate", G_CALLBACK (size_changed), popover);
+ g_signal_connect_object (priv->relative_to, "size-allocate",
+ G_CALLBACK (size_changed), popover, 0);
gtk_css_node_set_parent (gtk_widget_get_css_node (GTK_WIDGET (popover)),
gtk_widget_get_css_node (relative_to));
gtk_widget_set_parent (GTK_WIDGET (popover), relative_to);
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index 2732f064c8..38dfe92a31 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -90,7 +90,7 @@ gboolean _gtk_translate_keyboard_accel_state (GdkKeymap *keymap,
gint *level,
GdkModifierType *consumed_modifiers);
-void gtk_propagate_event_internal (GtkWidget *widget,
+gboolean gtk_propagate_event_internal (GtkWidget *widget,
GdkEvent *event,
GtkWidget *topmost);
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index 093e1d2d10..dbcda6083c 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -29,7 +29,6 @@
#include "gtkadjustment.h"
#include "gtkadjustmentprivate.h"
#include "gtkbindings.h"
-#include "gtkdnd.h"
#include "gtkeventcontrollermotion.h"
#include "gtkeventcontrollerscroll.h"
#include "gtkgesturedrag.h"
diff --git a/gtk/gtksearchlistmodel.c b/gtk/gtksearchlistmodel.c
new file mode 100644
index 0000000000..7a50c28833
--- /dev/null
+++ b/gtk/gtksearchlistmodel.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright © 2019 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.1 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/>.
+ *
+ * Authors: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "config.h"
+
+#include "gtksearchlistmodel.h"
+
+#include "gtkintl.h"
+#include "gtkselectionmodel.h"
+
+/**
+ * SECTION:gtksearchlistmodel
+ * @Short_description: A selection model that allows incremental searching
+ * @Title: GtkSearchListModel
+ * @see_also: #GtkSelectionModel
+ *
+ * GtkSearchListModel is an implementation of the #GtkSelectionModel interface
+ * that allows selecting a single element. The selected element can be determined
+ * interactively via a filter.
+ */
+struct _GtkSearchListModel
+{
+ GObject parent_instance;
+
+ GListModel *model;
+ guint selected;
+ gpointer selected_item;
+
+ GtkFilter *filter;
+};
+
+struct _GtkSearchListModelClass
+{
+ GObjectClass parent_class;
+};
+
+enum {
+ PROP_0,
+ PROP_MODEL,
+ PROP_FILTER,
+ PROP_SELECTED,
+ PROP_SELECTED_ITEM,
+
+ N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static GType
+gtk_search_list_model_get_item_type (GListModel *list)
+{
+ GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (list);
+
+ return g_list_model_get_item_type (self->model);
+}
+
+static guint
+gtk_search_list_model_get_n_items (GListModel *list)
+{
+ GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (list);
+
+ return g_list_model_get_n_items (self->model);
+}
+
+static gpointer
+gtk_search_list_model_get_item (GListModel *list,
+ guint position)
+{
+ GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (list);
+
+ return g_list_model_get_item (self->model, position);
+}
+
+static void
+gtk_search_list_model_list_model_init (GListModelInterface *iface)
+{
+ iface->get_item_type = gtk_search_list_model_get_item_type;
+ iface->get_n_items = gtk_search_list_model_get_n_items;
+ iface->get_item = gtk_search_list_model_get_item;
+}
+
+static gboolean
+gtk_search_list_model_is_selected (GtkSelectionModel *model,
+ guint position)
+{
+ GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (model);
+
+ return self->selected == position;
+}
+
+static void
+gtk_search_list_model_query_range (GtkSelectionModel *model,
+ guint position,
+ guint *start_range,
+ guint *n_range,
+ gboolean *selected)
+{
+ GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (model);
+ guint n_items;
+
+ n_items = g_list_model_get_n_items (self->model);
+
+ if (position >= n_items)
+ {
+ *start_range = position;
+ *n_range = 0;
+ *selected = FALSE;
+ }
+ else if (self->selected == GTK_INVALID_LIST_POSITION)
+ {
+ *start_range = 0;
+ *n_range = n_items;
+ *selected = FALSE;
+ }
+ else if (position < self->selected)
+ {
+ *start_range = 0;
+ *n_range = self->selected;
+ *selected = FALSE;
+ }
+ else if (position > self->selected)
+ {
+ *start_range = self->selected + 1;
+ *n_range = n_items - *start_range;
+ *selected = FALSE;
+ }
+ else
+ {
+ *start_range = self->selected;
+ *n_range = 1;
+ *selected = TRUE;
+ }
+}
+
+static void
+gtk_search_list_model_selection_model_init (GtkSelectionModelInterface *iface)
+{
+ iface->is_selected = gtk_search_list_model_is_selected;
+ iface->query_range = gtk_search_list_model_query_range;
+}
+
+G_DEFINE_TYPE_EXTENDED (GtkSearchListModel, gtk_search_list_model, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
+ gtk_search_list_model_list_model_init)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
+ gtk_search_list_model_selection_model_init))
+
+static void
+gtk_search_list_model_items_changed_cb (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkSearchListModel *self)
+{
+ g_object_freeze_notify (G_OBJECT (self));
+
+ if (self->selected_item == NULL)
+ {
+ /* nothing to do */
+ }
+ else if (self->selected < position)
+ {
+ /* unchanged */
+ }
+ else if (self->selected >= position + removed)
+ {
+ self->selected += added - removed;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]);
+ }
+ else
+ {
+ guint i;
+
+ for (i = 0; i < added; i++)
+ {
+ gpointer item = g_list_model_get_item (model, position + i);
+ if (item == self->selected_item)
+ {
+ /* the item moved */
+ //TODO refilter
+ if (self->selected != position + i)
+ {
+ self->selected = position + i;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]);
+ }
+ break;
+ }
+ }
+ if (i == added)
+ {
+ /* the item really was deleted */
+ g_clear_object (&self->selected_item);
+ self->selected = GTK_INVALID_LIST_POSITION;
+ //TODO refilter
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]);
+ }
+ }
+
+ g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
+
+ g_object_thaw_notify (G_OBJECT (self));
+}
+
+static void
+set_selected (GtkSearchListModel *self,
+ guint position)
+{
+ gpointer new_selected = NULL;
+ guint old_position;
+
+ if (self->selected == position)
+ return;
+
+ new_selected = g_list_model_get_item (self->model, position);
+
+ if (new_selected == NULL)
+ position = GTK_INVALID_LIST_POSITION;
+
+ if (self->selected == position)
+ return;
+
+ old_position = self->selected;
+ self->selected = position;
+ g_clear_object (&self->selected_item);
+ self->selected_item = new_selected;
+
+ if (old_position == GTK_INVALID_LIST_POSITION)
+ gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), position, 1);
+ else if (position == GTK_INVALID_LIST_POSITION)
+ gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), old_position, 1);
+ else if (position < old_position)
+ gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), position, old_position - position + 1);
+ else
+ gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), old_position, position - old_position + 1);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]);
+}
+
+static gboolean
+match_item (GtkSearchListModel *self,
+ guint position)
+{
+ gpointer item;
+ gboolean result;
+
+ item = g_list_model_get_item (self->model, position);
+ result = gtk_filter_match (self->filter, item);
+ g_object_unref (item);
+
+ return result;
+}
+
+static guint
+find_next_match (GtkSearchListModel *self,
+ guint position,
+ gboolean forward)
+{
+ guint i;
+
+ if (position == GTK_INVALID_LIST_POSITION)
+ position = 0;
+
+ g_print ("search %s from %u\n", forward ? "forward" : "backward", position);
+ if (forward)
+ for (i = position; i < g_list_model_get_n_items (self->model); i++)
+ {
+ if (match_item (self, i))
+ return i;
+ }
+ else
+ for (i = position; ; i--)
+ {
+ if (match_item (self, i))
+ return i;
+ if (i == 0)
+ break;
+ }
+
+ return GTK_INVALID_LIST_POSITION;
+}
+
+static void
+gtk_search_list_model_filter_changed_cb (GtkFilter *filter,
+ GtkFilterChange change,
+ GtkSearchListModel *self)
+{
+ guint position;
+
+g_print ("filter changed: change %d, strictness %d\n", change, gtk_filter_get_strictness (self->filter));
+
+ if (gtk_filter_get_strictness (self->filter) == GTK_FILTER_MATCH_NONE)
+ position = GTK_INVALID_LIST_POSITION;
+ else
+ switch (change)
+ {
+ case GTK_FILTER_CHANGE_DIFFERENT:
+ case GTK_FILTER_CHANGE_LESS_STRICT:
+ position = find_next_match (self, 0, TRUE);
+ break;
+ case GTK_FILTER_CHANGE_MORE_STRICT:
+ position = find_next_match (self, self->selected, TRUE);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_print ("select %u\n", position);
+ set_selected (self, position);
+}
+
+static void
+gtk_search_list_model_clear_model (GtkSearchListModel *self)
+{
+ if (self->model == NULL)
+ return;
+
+ g_signal_handlers_disconnect_by_func (self->model,
+ gtk_search_list_model_items_changed_cb,
+ self);
+ g_clear_object (&self->model);
+}
+
+static void
+gtk_search_list_model_clear_filter (GtkSearchListModel *self)
+{
+ if (self->filter == NULL)
+ return;
+
+ g_signal_handlers_disconnect_by_func (self->filter,
+ gtk_search_list_model_filter_changed_cb,
+ self);
+
+ g_clear_object (&self->filter);
+}
+
+static void
+gtk_search_list_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+
+{
+ GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ self->model = g_value_dup_object (value);
+ g_signal_connect (self->model, "items-changed",
+ G_CALLBACK (gtk_search_list_model_items_changed_cb), self);
+ break;
+
+ case PROP_FILTER:
+ self->filter = g_value_dup_object (value);
+ g_signal_connect (self->filter, "changed",
+ G_CALLBACK (gtk_search_list_model_filter_changed_cb), self);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_search_list_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ g_value_set_object (value, self->model);
+ break;
+
+ case PROP_FILTER:
+ g_value_set_object (value, self->filter);
+ break;
+
+ case PROP_SELECTED:
+ g_value_set_uint (value, self->selected);
+ break;
+
+ case PROP_SELECTED_ITEM:
+ g_value_set_object (value, self->selected_item);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_search_list_model_dispose (GObject *object)
+{
+ GtkSearchListModel *self = GTK_SEARCH_LIST_MODEL (object);
+
+ gtk_search_list_model_clear_model (self);
+ gtk_search_list_model_clear_filter (self);
+
+ self->selected = GTK_INVALID_LIST_POSITION;
+ g_clear_object (&self->selected_item);
+
+ G_OBJECT_CLASS (gtk_search_list_model_parent_class)->dispose (object);
+}
+
+static void
+gtk_search_list_model_class_init (GtkSearchListModelClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = gtk_search_list_model_get_property;
+ gobject_class->set_property = gtk_search_list_model_set_property;
+ gobject_class->dispose = gtk_search_list_model_dispose;
+
+ /**
+ * GtkSearchListModel:selected:
+ *
+ * Position of the selected item
+ */
+ properties[PROP_SELECTED] =
+ g_param_spec_uint ("selected",
+ P_("Selected"),
+ P_("Position of the selected item"),
+ 0, G_MAXUINT, GTK_INVALID_LIST_POSITION,
+ G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GtkSearchListModel:selected-item:
+ *
+ * The selected item
+ */
+ properties[PROP_SELECTED_ITEM] =
+ g_param_spec_object ("selected-item",
+ P_("Selected Item"),
+ P_("The selected item"),
+ G_TYPE_OBJECT,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GtkSearchListModel:model:
+ *
+ * The model being managed
+ */
+ properties[PROP_MODEL] =
+ g_param_spec_object ("model",
+ P_("The model"),
+ P_("The model being managed"),
+ G_TYPE_LIST_MODEL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GtkSearchListModel:filter:
+ *
+ * The filter determining the selected item
+ */
+ properties[PROP_FILTER] =
+ g_param_spec_object ("filter",
+ P_("The filter"),
+ P_("The filter being used"),
+ GTK_TYPE_FILTER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, properties);
+}
+
+static void
+gtk_search_list_model_init (GtkSearchListModel *self)
+{
+ self->selected = GTK_INVALID_LIST_POSITION;
+}
+
+/**
+ * gtk_search_list_model_new:
+ * @model: (transfer none): the #GListModel to manage
+ * @filter: (transfer none): the #GtkFilter to use
+ *
+ * Creates a new selection to handle @model.
+ *
+ * Returns: (transfer full) (type GtkSearchListModel): a new #GtkSearchListModel
+ **/
+GtkSearchListModel *
+gtk_search_list_model_new (GListModel *model,
+ GtkFilter *filter)
+{
+ g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
+
+ return g_object_new (GTK_TYPE_SEARCH_LIST_MODEL,
+ "model", model,
+ "filter", filter,
+ NULL);
+}
+
+gboolean
+gtk_search_list_model_next_match (GtkSearchListModel *self)
+{
+ guint position;
+
+ position = find_next_match (self, self->selected, TRUE);
+ if (position == GTK_INVALID_LIST_POSITION)
+ return FALSE;
+
+ set_selected (self, position);
+
+ return TRUE;
+}
+
+gboolean
+gtk_search_list_model_previous_match (GtkSearchListModel *self)
+{
+ guint position;
+
+ position = find_next_match (self, self->selected, FALSE);
+ if (position == GTK_INVALID_LIST_POSITION)
+ return FALSE;
+
+ set_selected (self, position);
+
+ return TRUE;
+}
diff --git a/gtk/gtksearchlistmodel.h b/gtk/gtksearchlistmodel.h
new file mode 100644
index 0000000000..324449bbcd
--- /dev/null
+++ b/gtk/gtksearchlistmodel.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2019 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.1 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/>.
+ *
+ * Authors: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#ifndef __GTK_SEARCH_LIST_MODEL_H__
+#define __GTK_SEARCH_LIST_MODEL_H__
+
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+#include <gtk/gtkfilter.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SEARCH_LIST_MODEL (gtk_search_list_model_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkSearchListModel, gtk_search_list_model, GTK, SEARCH_LIST_MODEL, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkSearchListModel * gtk_search_list_model_new (GListModel *model,
+ GtkFilter *filter);
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_search_list_model_next_match (GtkSearchListModel *self);
+
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_search_list_model_previous_match (GtkSearchListModel *self);
+
+G_END_DECLS
+
+#endif /* __GTK_SEARCH_LIST_MODEL_H__ */
diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c
index dfa8511966..e5b520eba2 100644
--- a/gtk/gtkselection.c
+++ b/gtk/gtkselection.c
@@ -144,130 +144,6 @@ init_atoms (void)
}
/**
- * gtk_content_formats_add_text_targets:
- * @list: a #GdkContentFormats
- *
- * Appends the text targets supported by #GtkSelectionData to
- * the target list. All targets are added with the same @info.
- **/
-GdkContentFormats *
-gtk_content_formats_add_text_targets (GdkContentFormats *list)
-{
- GdkContentFormatsBuilder *builder;
-
- g_return_val_if_fail (list != NULL, NULL);
-
- init_atoms ();
-
- builder = gdk_content_formats_builder_new ();
- gdk_content_formats_builder_add_formats (builder, list);
- gdk_content_formats_unref (list);
-
- /* Keep in sync with gtk_selection_data_targets_include_text()
- */
- gdk_content_formats_builder_add_mime_type (builder, utf8_atom);
- gdk_content_formats_builder_add_mime_type (builder, ctext_atom);
- gdk_content_formats_builder_add_mime_type (builder, text_atom);
- gdk_content_formats_builder_add_mime_type (builder, g_intern_static_string ("STRING"));
- gdk_content_formats_builder_add_mime_type (builder, text_plain_utf8_atom);
- if (!g_get_charset (NULL))
- gdk_content_formats_builder_add_mime_type (builder, text_plain_locale_atom);
- gdk_content_formats_builder_add_mime_type (builder, text_plain_atom);
-
- return gdk_content_formats_builder_free_to_formats (builder);
-}
-
-/**
- * gtk_content_formats_add_image_targets:
- * @list: a #GdkContentFormats
- * @writable: whether to add only targets for which GTK+ knows
- * how to convert a pixbuf into the format
- *
- * Appends the image targets supported by #GtkSelectionData to
- * the target list. All targets are added with the same @info.
- **/
-GdkContentFormats *
-gtk_content_formats_add_image_targets (GdkContentFormats *list,
- gboolean writable)
-{
- GdkContentFormatsBuilder *builder;
- GSList *formats, *f;
- gchar **mimes, **m;
-
- g_return_val_if_fail (list != NULL, NULL);
-
- builder = gdk_content_formats_builder_new ();
- gdk_content_formats_builder_add_formats (builder, list);
- gdk_content_formats_unref (list);
-
- formats = gdk_pixbuf_get_formats ();
-
- /* Make sure png comes first */
- for (f = formats; f; f = f->next)
- {
- GdkPixbufFormat *fmt = f->data;
- gchar *name;
-
- name = gdk_pixbuf_format_get_name (fmt);
- if (strcmp (name, "png") == 0)
- {
- formats = g_slist_delete_link (formats, f);
- formats = g_slist_prepend (formats, fmt);
-
- g_free (name);
-
- break;
- }
-
- g_free (name);
- }
-
- for (f = formats; f; f = f->next)
- {
- GdkPixbufFormat *fmt = f->data;
-
- if (writable && !gdk_pixbuf_format_is_writable (fmt))
- continue;
-
- mimes = gdk_pixbuf_format_get_mime_types (fmt);
- for (m = mimes; *m; m++)
- {
- gdk_content_formats_builder_add_mime_type (builder, *m);
- }
- g_strfreev (mimes);
- }
-
- g_slist_free (formats);
-
- return gdk_content_formats_builder_free_to_formats (builder);
-}
-
-/**
- * gtk_content_formats_add_uri_targets:
- * @list: a #GdkContentFormats
- *
- * Appends the URI targets supported by #GtkSelectionData to
- * the target list. All targets are added with the same @info.
- **/
-GdkContentFormats *
-gtk_content_formats_add_uri_targets (GdkContentFormats *list)
-{
- GdkContentFormatsBuilder *builder;
-
- g_return_val_if_fail (list != NULL, NULL);
-
- init_atoms ();
-
- builder = gdk_content_formats_builder_new ();
- gdk_content_formats_builder_add_formats (builder, list);
- gdk_content_formats_unref (list);
-
- gdk_content_formats_builder_add_mime_type (builder, text_uri_list_atom);
-
- return gdk_content_formats_builder_free_to_formats (builder);
-}
-
-/**
* gtk_selection_data_get_target:
* @selection_data: a pointer to a #GtkSelectionData-struct.
*
@@ -1158,8 +1034,11 @@ gtk_targets_include_image (GdkAtom *targets,
g_return_val_if_fail (targets != NULL || n_targets == 0, FALSE);
- list = gdk_content_formats_new (NULL, 0);
- list = gtk_content_formats_add_image_targets (list, writable);
+ list = gdk_content_formats_new_for_gtype (GDK_TYPE_TEXTURE);
+ if (writable)
+ list = gdk_content_formats_union_serialize_mime_types (list);
+ else
+ list = gdk_content_formats_union_deserialize_mime_types (list);
for (i = 0; i < n_targets && !result; i++)
{
if (gdk_content_formats_contain_mime_type (list, targets[i]))
diff --git a/gtk/gtkselection.h b/gtk/gtkselection.h
index 2fc9026c6d..983a0b398d 100644
--- a/gtk/gtkselection.h
+++ b/gtk/gtkselection.h
@@ -36,13 +36,6 @@ G_BEGIN_DECLS
#define GTK_TYPE_SELECTION_DATA (gtk_selection_data_get_type ())
-GDK_AVAILABLE_IN_ALL
-GdkContentFormats * gtk_content_formats_add_text_targets (GdkContentFormats *list) G_GNUC_WARN_UNUSED_RESULT;
-GDK_AVAILABLE_IN_ALL
-GdkContentFormats * gtk_content_formats_add_image_targets (GdkContentFormats *list,
- gboolean writable) G_GNUC_WARN_UNUSED_RESULT;
-GDK_AVAILABLE_IN_ALL
-GdkContentFormats * gtk_content_formats_add_uri_targets (GdkContentFormats *list) G_GNUC_WARN_UNUSED_RESULT;
GDK_AVAILABLE_IN_ALL
GdkAtom gtk_selection_data_get_target (const GtkSelectionData *selection_data);
diff --git a/gtk/gtkstackswitcher.c b/gtk/gtkstackswitcher.c
index 07de3af617..c9d30fecf1 100644
--- a/gtk/gtkstackswitcher.c
+++ b/gtk/gtkstackswitcher.c
@@ -22,7 +22,6 @@
#include "gtkstackswitcher.h"
#include "gtkboxlayout.h"
-#include "gtkdnd.h"
#include "gtkdragdest.h"
#include "gtkimage.h"
#include "gtkintl.h"
@@ -93,6 +92,19 @@ enum {
PROP_STACK
};
+static void gtk_stack_switcher_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkStackSwitcher *self);
+static gboolean gtk_stack_switcher_drag_accept (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkStackSwitcher *self);
+static void gtk_stack_switcher_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkStackSwitcher *self);
+
+
G_DEFINE_TYPE_WITH_PRIVATE (GtkStackSwitcher, gtk_stack_switcher, GTK_TYPE_WIDGET)
static void
@@ -100,14 +112,21 @@ gtk_stack_switcher_init (GtkStackSwitcher *switcher)
{
GtkStackSwitcherPrivate *priv = gtk_stack_switcher_get_instance_private (switcher);
GtkStyleContext *context;
+ GdkContentFormats *formats;
+ GtkDropTarget *dest;
priv->buttons = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
context = gtk_widget_get_style_context (GTK_WIDGET (switcher));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED);
- gtk_drag_dest_set (GTK_WIDGET (switcher), 0, NULL, 0);
- gtk_drag_dest_set_track_motion (GTK_WIDGET (switcher), TRUE);
+ formats = gdk_content_formats_new (NULL, 0);
+ dest = gtk_drop_target_new (formats, 0);
+ gdk_content_formats_unref (formats);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_stack_switcher_drag_leave), switcher);
+ g_signal_connect (dest, "accept", G_CALLBACK (gtk_stack_switcher_drag_accept), switcher);
+ g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_stack_switcher_drag_motion), switcher);
+ gtk_widget_add_controller (GTK_WIDGET (switcher), GTK_EVENT_CONTROLLER (dest));
}
static void
@@ -249,17 +268,24 @@ gtk_stack_switcher_switch_timeout (gpointer data)
}
static gboolean
-gtk_stack_switcher_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+gtk_stack_switcher_drag_accept (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkStackSwitcher *self)
+{
+ return TRUE;
+}
+
+static void
+gtk_stack_switcher_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkStackSwitcher *self)
{
- GtkStackSwitcher *self = GTK_STACK_SWITCHER (widget);
GtkStackSwitcherPrivate *priv = gtk_stack_switcher_get_instance_private (self);
GtkWidget *button;
GHashTableIter iter;
gpointer value;
- gboolean retval = FALSE;
button = NULL;
g_hash_table_iter_init (&iter, priv->buttons);
@@ -270,7 +296,6 @@ gtk_stack_switcher_drag_motion (GtkWidget *widget,
if (gtk_widget_contains (GTK_WIDGET (value), cx, cy))
{
button = GTK_WIDGET (value);
- retval = TRUE;
break;
}
}
@@ -287,16 +312,13 @@ gtk_stack_switcher_drag_motion (GtkWidget *widget,
self);
g_source_set_name_by_id (priv->switch_timer, "[gtk] gtk_stack_switcher_switch_timeout");
}
-
- return retval;
}
static void
-gtk_stack_switcher_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
+gtk_stack_switcher_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkStackSwitcher *self)
{
- GtkStackSwitcher *self = GTK_STACK_SWITCHER (widget);
-
remove_switch_timer (self);
}
@@ -562,9 +584,6 @@ gtk_stack_switcher_class_init (GtkStackSwitcherClass *class)
object_class->dispose = gtk_stack_switcher_dispose;
object_class->finalize = gtk_stack_switcher_finalize;
- widget_class->drag_motion = gtk_stack_switcher_drag_motion;
- widget_class->drag_leave = gtk_stack_switcher_drag_leave;
-
g_object_class_install_property (object_class,
PROP_STACK,
g_param_spec_object ("stack",
diff --git a/gtk/gtktext.c b/gtk/gtktext.c
index b9a763352e..45b9b3c82c 100644
--- a/gtk/gtktext.c
+++ b/gtk/gtktext.c
@@ -30,8 +30,6 @@
#include "gtkbutton.h"
#include "gtkcssnodeprivate.h"
#include "gtkdebug.h"
-#include "gtkdnd.h"
-#include "gtkdndprivate.h"
#include "gtkeditable.h"
#include "gtkemojichooser.h"
#include "gtkemojicompletion.h"
@@ -67,6 +65,9 @@
#include "gtkwindow.h"
#include "gtknative.h"
#include "gtkactionmuxerprivate.h"
+#include "gtkdragsource.h"
+#include "gtkdragdest.h"
+#include "gtkdragicon.h"
#include "a11y/gtktextaccessible.h"
@@ -179,6 +180,8 @@ struct _GtkTextPrivate
GtkTextHistory *history;
+ GdkDrag *drag;
+
float xalign;
int ascent; /* font ascent in pango units */
@@ -331,28 +334,22 @@ static void gtk_text_state_flags_changed (GtkWidget *widget,
GtkStateFlags previous_state);
static void gtk_text_root (GtkWidget *widget);
-static gboolean gtk_text_drag_drop (GtkWidget *widget,
+static gboolean gtk_text_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
- int y);
-static gboolean gtk_text_drag_motion (GtkWidget *widget,
+ int y,
+ GtkText *text);
+static gboolean gtk_text_drag_accept (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkText *self);
+static void gtk_text_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
int x,
- int y);
-static void gtk_text_drag_leave (GtkWidget *widget,
- GdkDrop *drop);
-static void gtk_text_drag_data_received (GtkWidget *widget,
+ int y,
+ GtkText *text);
+static void gtk_text_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
- GtkSelectionData *selection_data);
-static void gtk_text_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data);
-static void gtk_text_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_text_drag_begin (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_text_drag_end (GtkWidget *widget,
- GdkDrag *drag);
+ GtkText *text);
/* GtkEditable method implementations
@@ -729,19 +726,11 @@ gtk_text_class_init (GtkTextClass *class)
widget_class->snapshot = gtk_text_snapshot;
widget_class->grab_focus = gtk_text_grab_focus;
widget_class->style_updated = gtk_text_style_updated;
- widget_class->drag_begin = gtk_text_drag_begin;
- widget_class->drag_end = gtk_text_drag_end;
widget_class->direction_changed = gtk_text_direction_changed;
widget_class->state_flags_changed = gtk_text_state_flags_changed;
widget_class->root = gtk_text_root;
widget_class->mnemonic_activate = gtk_text_mnemonic_activate;
widget_class->popup_menu = gtk_text_popup_menu;
- widget_class->drag_drop = gtk_text_drag_drop;
- widget_class->drag_motion = gtk_text_drag_motion;
- widget_class->drag_leave = gtk_text_drag_leave;
- widget_class->drag_data_received = gtk_text_drag_data_received;
- widget_class->drag_data_get = gtk_text_drag_data_get;
- widget_class->drag_data_delete = gtk_text_drag_data_delete;
class->move_cursor = gtk_text_move_cursor;
class->insert_at_cursor = gtk_text_insert_at_cursor;
@@ -1722,6 +1711,8 @@ gtk_text_init (GtkText *self)
GtkGesture *gesture;
GtkEventController *controller;
int i;
+ GtkDropTarget *dest;
+ GdkContentFormats *formats;
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
@@ -1742,9 +1733,14 @@ gtk_text_init (GtkText *self)
priv->selection_content = g_object_new (GTK_TYPE_TEXT_CONTENT, NULL);
GTK_TEXT_CONTENT (priv->selection_content)->self = self;
- gtk_drag_dest_set (GTK_WIDGET (self), 0, NULL,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
- gtk_drag_dest_add_text_targets (GTK_WIDGET (self));
+ formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
+ dest = gtk_drop_target_new (formats, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (dest, "accept", G_CALLBACK (gtk_text_drag_accept), self);
+ g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_drag_motion), self);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_drag_leave), self);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_drag_drop), self);
+ gdk_content_formats_unref (formats);
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (dest));
/* This object is completely private. No external entity can gain a reference
* to it; so we create it here and destroy it in finalize().
@@ -2804,6 +2800,18 @@ gtk_text_motion_controller_motion (GtkEventControllerMotion *controller,
}
static void
+dnd_finished_cb (GdkDrag *drag,
+ GtkText *self)
+{
+ GtkTextPrivate *priv = gtk_text_get_instance_private (self);
+
+ if (gdk_drag_get_selected_action (drag) == GDK_ACTION_MOVE)
+ gtk_text_delete_selection (self);
+
+ priv->drag = NULL;
+}
+
+static void
gtk_text_drag_gesture_update (GtkGestureDrag *gesture,
double offset_x,
double offset_y,
@@ -2839,23 +2847,48 @@ gtk_text_drag_gesture_update (GtkGestureDrag *gesture,
{
int *ranges;
int n_ranges;
- GdkContentFormats *target_list = gdk_content_formats_new (NULL, 0);
- guint actions = priv->editable ? GDK_ACTION_COPY | GDK_ACTION_MOVE : GDK_ACTION_COPY;
+ char *text;
+ GdkDragAction actions;
+ GdkDrag *drag;
+ GdkPaintable *paintable;
+ GdkContentProvider *content;
+ GValue value = G_VALUE_INIT;
+
+ text = _gtk_text_get_selected_text (self);
+ gtk_text_get_pixel_ranges (self, &ranges, &n_ranges);
- target_list = gtk_content_formats_add_text_targets (target_list);
+ if (priv->editable)
+ actions = GDK_ACTION_COPY|GDK_ACTION_MOVE;
+ else
+ actions = GDK_ACTION_COPY;
- gtk_text_get_pixel_ranges (self, &ranges, &n_ranges);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, text);
+ content = gdk_content_provider_new_for_value (&value);
+ g_value_unset (&value);
+
+ drag = gdk_drag_begin (gdk_event_get_surface ((GdkEvent*) event),
+ gdk_event_get_device ((GdkEvent*) event),
+ content,
+ actions,
+ priv->drag_start_x,
+ priv->drag_start_y);
+ g_object_unref (content);
+
+ g_signal_connect (drag, "dnd-finished", G_CALLBACK (dnd_finished_cb), self);
- gtk_drag_begin (widget,
- gdk_event_get_device ((GdkEvent*) event),
- target_list, actions,
- priv->drag_start_x + ranges[0],
- priv->drag_start_y);
+ paintable = gtk_text_util_create_drag_icon (widget, text, -1);
+ gtk_drag_icon_set_from_paintable (drag, paintable, ranges[0], 0);
+ g_clear_object (&paintable);
+
+ priv->drag = drag;
+
+ g_object_unref (drag);
+
g_free (ranges);
+ g_free (text);
priv->in_drag = FALSE;
-
- gdk_content_formats_unref (target_list);
}
}
else
@@ -6097,137 +6130,28 @@ gtk_text_selection_bubble_popup_set (GtkText *self)
}
static void
-gtk_text_drag_begin (GtkWidget *widget,
- GdkDrag *drag)
+gtk_text_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkText *self)
{
- GtkText *self = GTK_TEXT (widget);
- GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- char *text;
-
- text = _gtk_text_get_selected_text (self);
-
- if (self)
- {
- int *ranges, n_ranges;
- GdkPaintable *paintable;
-
- paintable = gtk_text_util_create_drag_icon (widget, text, -1);
- gtk_text_get_pixel_ranges (self, &ranges, &n_ranges);
-
- gtk_drag_set_icon_paintable (drag,
- paintable,
- priv->drag_start_x - ranges[0],
- priv->drag_start_y);
-
- g_free (ranges);
- g_object_unref (paintable);
- g_free (text);
- }
-}
-
-static void
-gtk_text_drag_end (GtkWidget *widget,
- GdkDrag *drag)
-{
-}
-
-static void
-gtk_text_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
-{
- GtkText *self = GTK_TEXT (widget);
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
+ GtkWidget *widget = GTK_WIDGET (self);
- gtk_drag_unhighlight (widget);
priv->dnd_position = -1;
gtk_widget_queue_draw (widget);
}
-static gboolean
-gtk_text_drag_drop (GtkWidget *widget,
- GdkDrop *drop,
- int x,
- int y)
-{
- GtkText *self = GTK_TEXT (widget);
- GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- GdkAtom target = NULL;
-
- if (priv->editable)
- target = gtk_drag_dest_find_target (widget, drop, NULL);
-
- if (target != NULL)
- {
- priv->drop_position = gtk_text_find_position (self, x + priv->scroll_offset);
- gtk_drag_get_data (widget, drop, target);
- }
- else
- gdk_drop_finish (drop, 0);
-
- return TRUE;
-}
-
-static gboolean
-gtk_text_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- int x,
- int y)
-{
- GtkText *self = GTK_TEXT (widget);
- GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- GdkDragAction suggested_action;
- int new_position, old_position;
-
- old_position = priv->dnd_position;
- new_position = gtk_text_find_position (self, x + priv->scroll_offset);
-
- if (priv->editable &&
- gtk_drag_dest_find_target (widget, drop, NULL) != NULL)
- {
- suggested_action = GDK_ACTION_COPY | GDK_ACTION_MOVE;
-
- if (priv->selection_bound == priv->current_pos ||
- new_position < priv->selection_bound ||
- new_position > priv->current_pos)
- {
- priv->dnd_position = new_position;
- }
- else
- {
- priv->dnd_position = -1;
- }
- }
- else
- {
- /* Entry not editable, or no text */
- suggested_action = 0;
- priv->dnd_position = -1;
- }
-
- gdk_drop_status (drop, suggested_action);
- if (suggested_action == 0)
- gtk_drag_unhighlight (widget);
- else
- gtk_drag_highlight (widget);
-
- if (priv->dnd_position != old_position)
- gtk_widget_queue_draw (widget);
-
- return TRUE;
-}
-
static GdkDragAction
gtk_text_get_action (GtkText *self,
GdkDrop *drop)
{
- GtkWidget *widget = GTK_WIDGET (self);
+ GtkTextPrivate *priv = gtk_text_get_instance_private (self);
GdkDrag *drag = gdk_drop_get_drag (drop);
- GtkWidget *source_widget = gtk_drag_get_source_widget (drag);
GdkDragAction actions;
actions = gdk_drop_get_actions (drop);
- if (source_widget == widget &&
+ if (drag == priv->drag &&
actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
@@ -6241,19 +6165,20 @@ gtk_text_get_action (GtkText *self,
}
static void
-gtk_text_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data)
+got_text (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
- GtkText *self = GTK_TEXT (widget);
+ GdkDrop *drop = GDK_DROP (source);
+ GtkText *self = GTK_TEXT (data);
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- GdkDragAction action;
char *str;
+ GdkDragAction action;
- str = (char *) gtk_selection_data_get_text (selection_data);
+ str = gdk_drop_read_text_finish (drop, result, NULL);
action = gtk_text_get_action (self, drop);
- if (action && str && priv->editable)
+ if (action && str)
{
int length = -1;
int pos;
@@ -6288,34 +6213,84 @@ gtk_text_drag_data_received (GtkWidget *widget,
g_free (str);
}
-static void
-gtk_text_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data)
+static gboolean
+gtk_text_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkText *self)
{
- GtkText *self = GTK_TEXT (widget);
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
- if (priv->selection_bound != priv->current_pos)
+ if (priv->editable && gdk_drop_has_value (drop, G_TYPE_STRING))
{
- char *str = gtk_text_get_display_text (self, priv->selection_bound, priv->current_pos);
+ priv->drop_position = gtk_text_find_position (self, x + priv->scroll_offset);
+ gdk_drop_read_text_async (drop, NULL, got_text, self);
+ }
+ else
+ gdk_drop_finish (drop, 0);
+
+ return TRUE;
+}
- gtk_selection_data_set_text (selection_data, str, -1);
-
- g_free (str);
+static gboolean
+gtk_text_drag_accept (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkText *self)
+{
+ GtkTextPrivate *priv = gtk_text_get_instance_private (self);
+ GdkDragAction suggested_action;
+
+ if (priv->editable &&
+ gtk_drop_target_find_mimetype (dest) != NULL)
+ {
+ suggested_action = GDK_ACTION_COPY | GDK_ACTION_MOVE;
+ }
+ else
+ {
+ /* Entry not editable, or no text */
+ suggested_action = 0;
}
+
+ gdk_drop_status (drop, suggested_action);
+ return suggested_action != 0;
}
static void
-gtk_text_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag)
+gtk_text_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkText *self)
{
- GtkText *self = GTK_TEXT (widget);
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
+ int new_position, old_position;
+
+ old_position = priv->dnd_position;
+ new_position = gtk_text_find_position (self, x + priv->scroll_offset);
if (priv->editable &&
- priv->selection_bound != priv->current_pos)
- gtk_text_delete_selection (self);
+ gtk_drop_target_find_mimetype (dest) != NULL)
+ {
+ if (priv->selection_bound == priv->current_pos ||
+ new_position < priv->selection_bound ||
+ new_position > priv->current_pos)
+ {
+ priv->dnd_position = new_position;
+ }
+ else
+ {
+ priv->dnd_position = -1;
+ }
+ }
+ else
+ {
+ /* Entry not editable, or no text */
+ priv->dnd_position = -1;
+ }
+
+ if (priv->dnd_position != old_position)
+ gtk_widget_queue_draw (GTK_WIDGET (self));
}
/* We display the cursor when
diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c
index f764c96331..5ec18f7c5e 100644
--- a/gtk/gtktextbuffer.c
+++ b/gtk/gtktextbuffer.c
@@ -27,7 +27,6 @@
#include <string.h>
#include <stdarg.h>
-#include "gtkdnd.h"
#include "gtkmarshalers.h"
#include "gtktextbuffer.h"
#include "gtktexthistoryprivate.h"
@@ -4078,6 +4077,13 @@ cut_or_copy (GtkTextBuffer *buffer,
}
}
+GdkContentProvider *
+gtk_text_buffer_get_selection_content (GtkTextBuffer *buffer)
+{
+ return gtk_text_buffer_content_new (buffer);
+}
+
+
/**
* gtk_text_buffer_cut_clipboard:
* @buffer: a #GtkTextBuffer
diff --git a/gtk/gtktextbuffer.h b/gtk/gtktextbuffer.h
index 9517077fe6..969db7f3d8 100644
--- a/gtk/gtktextbuffer.h
+++ b/gtk/gtktextbuffer.h
@@ -454,6 +454,10 @@ gboolean gtk_text_buffer_delete_selection (GtkTextBuffer *buffer,
gboolean default_editable);
GDK_AVAILABLE_IN_ALL
+GdkContentProvider *
+ gtk_text_buffer_get_selection_content (GtkTextBuffer *buffer);
+
+GDK_AVAILABLE_IN_ALL
gboolean gtk_text_buffer_get_can_undo (GtkTextBuffer *buffer);
GDK_AVAILABLE_IN_ALL
gboolean gtk_text_buffer_get_can_redo (GtkTextBuffer *buffer);
diff --git a/gtk/gtktextutil.h b/gtk/gtktextutil.h
index 4ad534ce7b..472faf854f 100644
--- a/gtk/gtktextutil.h
+++ b/gtk/gtktextutil.h
@@ -27,6 +27,8 @@
G_BEGIN_DECLS
+#include "gtktextbuffer.h"
+
/* This is a private uninstalled header shared between
* GtkTextView and GtkEntry
*/
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index ab55d08057..ed5f6ad90d 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -31,7 +31,6 @@
#include "gtkadjustmentprivate.h"
#include "gtkbindings.h"
-#include "gtkdnd.h"
#include "gtkdebug.h"
#include "gtkintl.h"
#include "gtkmain.h"
@@ -233,6 +232,8 @@ struct _GtkTextViewPrivate
GtkCssNode *selection_node;
+ GdkDrag *drag;
+
/* Default style settings */
gint pixels_above_lines;
gint pixels_below_lines;
@@ -418,31 +419,20 @@ static gboolean get_middle_click_paste (GtkTextView *text_view);
static GtkTextBuffer* gtk_text_view_create_buffer (GtkTextView *text_view);
-/* Source side drag signals */
-static void gtk_text_view_drag_begin (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_text_view_drag_end (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_text_view_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data);
-static void gtk_text_view_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag);
-
/* Target side drag signals */
-static void gtk_text_view_drag_leave (GtkWidget *widget,
- GdkDrop *drop);
-static gboolean gtk_text_view_drag_motion (GtkWidget *widget,
+static void gtk_text_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static gboolean gtk_text_view_drag_drop (GtkWidget *widget,
+ GtkTextView *text_view);
+static gboolean gtk_text_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static void gtk_text_view_drag_data_received (GtkWidget *widget,
+ int x,
+ int y,
+ GtkTextView *text_view);
+static gboolean gtk_text_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
- GtkSelectionData *selection_data);
+ int x,
+ int y,
+ GtkTextView *text_view);
static gboolean gtk_text_view_popup_menu (GtkWidget *widget);
static void gtk_text_view_move_cursor (GtkTextView *text_view,
@@ -712,15 +702,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
widget_class->size_allocate = gtk_text_view_size_allocate;
widget_class->snapshot = gtk_text_view_snapshot;
widget_class->focus = gtk_text_view_focus;
- widget_class->drag_begin = gtk_text_view_drag_begin;
- widget_class->drag_end = gtk_text_view_drag_end;
- widget_class->drag_data_get = gtk_text_view_drag_data_get;
- widget_class->drag_data_delete = gtk_text_view_drag_data_delete;
-
- widget_class->drag_leave = gtk_text_view_drag_leave;
- widget_class->drag_motion = gtk_text_view_drag_motion;
- widget_class->drag_drop = gtk_text_view_drag_drop;
- widget_class->drag_data_received = gtk_text_view_drag_data_received;
widget_class->popup_menu = gtk_text_view_popup_menu;
@@ -1623,7 +1604,8 @@ static void
gtk_text_view_init (GtkTextView *text_view)
{
GtkWidget *widget = GTK_WIDGET (text_view);
- GdkContentFormats *target_list;
+ GdkContentFormats *formats;
+ GtkDropTarget *dest;
GtkTextViewPrivate *priv;
GtkStyleContext *context;
GtkEventController *controller;
@@ -1652,12 +1634,13 @@ gtk_text_view_init (GtkTextView *text_view)
priv->scroll_after_paste = FALSE;
- gtk_drag_dest_set (widget, 0, NULL,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
-
- target_list = gdk_content_formats_new_for_gtype (GTK_TYPE_TEXT_BUFFER);
- gtk_drag_dest_set_target_list (widget, target_list);
- gdk_content_formats_unref (target_list);
+ formats = gdk_content_formats_new_for_gtype (GTK_TYPE_TEXT_BUFFER);
+ dest = gtk_drop_target_new (formats, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ gdk_content_formats_unref (formats);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (gtk_text_view_drag_leave), text_view);
+ g_signal_connect (dest, "drag-motion", G_CALLBACK (gtk_text_view_drag_motion), text_view);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (gtk_text_view_drag_drop), text_view);
+ gtk_widget_add_controller (GTK_WIDGET (text_view), GTK_EVENT_CONTROLLER (dest));
priv->virtual_cursor_x = -1;
priv->virtual_cursor_y = -1;
@@ -6698,8 +6681,7 @@ gtk_text_view_copy_clipboard (GtkTextView *text_view)
{
GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
- gtk_text_buffer_copy_clipboard (get_buffer (text_view),
- clipboard);
+ gtk_text_buffer_copy_clipboard (get_buffer (text_view), clipboard);
/* on copy do not scroll, we are already onscreen */
}
@@ -6933,9 +6915,7 @@ drag_scan_timeout (gpointer data)
priv->dnd_x + priv->xoffset,
priv->dnd_y + priv->yoffset);
- gtk_text_buffer_move_mark (get_buffer (text_view),
- priv->dnd_mark,
- &newplace);
+ gtk_text_buffer_move_mark (get_buffer (text_view), priv->dnd_mark, &newplace);
pointer_xoffset = (gdouble) priv->dnd_x / text_window_get_width (priv->text_window);
pointer_yoffset = (gdouble) priv->dnd_y / text_window_get_height (priv->text_window);
@@ -7691,30 +7671,13 @@ gtk_text_view_im_context_filter_keypress (GtkTextView *text_view,
*/
static void
-drag_begin_cb (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
+dnd_finished_cb (GdkDrag *drag,
+ GtkTextView *self)
{
- GtkTextView *text_view = GTK_TEXT_VIEW (widget);
- GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
- GtkTextIter start;
- GtkTextIter end;
- GdkPaintable *paintable = NULL;
-
- g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
-
- if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
- paintable = gtk_text_util_create_rich_drag_icon (widget, buffer, &start, &end);
+ if (gdk_drag_get_selected_action (drag) == GDK_ACTION_MOVE)
+ gtk_text_buffer_delete_selection (self->priv->buffer, TRUE, self->priv->editable);
- if (paintable)
- {
- gtk_drag_set_icon_paintable (drag, paintable, 0, 0);
- g_object_unref (paintable);
- }
- else
- {
- gtk_drag_set_icon_default (drag);
- }
+ self->priv->drag = NULL;
}
static void
@@ -7724,89 +7687,48 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view,
gint x,
gint y)
{
- GdkContentFormats *formats;
-
- formats = gdk_content_formats_new_for_gtype (GTK_TYPE_TEXT_BUFFER);
-
- g_signal_connect (text_view, "drag-begin",
- G_CALLBACK (drag_begin_cb), NULL);
- gtk_drag_begin (GTK_WIDGET (text_view),
- gdk_event_get_device (event),
- formats,
- GDK_ACTION_COPY | GDK_ACTION_MOVE,
- x, y);
-}
+ GtkWidget *widget = GTK_WIDGET (text_view);
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
+ GdkContentProvider *content;
+ GtkTextIter start, end;
+ GdkDragAction actions;
+ GdkSurface *surface;
+ GdkDevice *device;
+ GdkDrag *drag;
-static void
-gtk_text_view_drag_begin (GtkWidget *widget,
- GdkDrag *drag)
-{
- /* do nothing */
-}
+ if (text_view->priv->editable)
+ actions = GDK_ACTION_COPY | GDK_ACTION_MOVE;
+ else
+ actions = GDK_ACTION_COPY;
-static void
-gtk_text_view_drag_end (GtkWidget *widget,
- GdkDrag *drag)
-{
- GtkTextView *text_view;
+ content = gtk_text_buffer_get_selection_content (buffer);
- text_view = GTK_TEXT_VIEW (widget);
- text_view->priv->dnd_x = text_view->priv->dnd_y = -1;
-}
+ surface = gdk_event_get_surface (event);
+ device = gdk_event_get_device (event);
+ drag = gdk_drag_begin (surface, device, content, actions, x, y);
+ g_object_unref (content);
-static void
-gtk_text_view_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data)
-{
- GtkTextView *text_view = GTK_TEXT_VIEW (widget);
- GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
+ g_signal_connect (drag, "dnd-finished", G_CALLBACK (dnd_finished_cb), text_view);
- if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
+ if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
{
- gtk_selection_data_set (selection_data,
- g_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
- 8, /* bytes */
- (void*)&buffer,
- sizeof (buffer));
+ GdkPaintable *paintable;
+ paintable = gtk_text_util_create_rich_drag_icon (widget, buffer, &start, &end);
+ gtk_drag_icon_set_from_paintable (drag, paintable, 0, 0);
+ g_object_unref (paintable);
}
- else
- {
- GtkTextIter start;
- GtkTextIter end;
- gchar *str = NULL;
- if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
- {
- /* Extract the selected text */
- str = gtk_text_iter_get_visible_text (&start, &end);
- }
-
- if (str)
- {
- gtk_selection_data_set_text (selection_data, str, -1);
- g_free (str);
- }
- }
-}
+ text_view->priv->drag = drag;
-static void
-gtk_text_view_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag)
-{
- gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->priv->buffer,
- TRUE, GTK_TEXT_VIEW (widget)->priv->editable);
+ g_object_unref (drag);
}
static void
-gtk_text_view_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
+gtk_text_view_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkTextView *text_view)
{
- GtkTextView *text_view;
- GtkTextViewPrivate *priv;
-
- text_view = GTK_TEXT_VIEW (widget);
- priv = text_view->priv;
+ GtkTextViewPrivate *priv = text_view->priv;
gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
@@ -7816,28 +7738,23 @@ gtk_text_view_drag_leave (GtkWidget *widget,
g_source_remove (priv->scroll_timeout);
priv->scroll_timeout = 0;
-
- gtk_drag_unhighlight (widget);
}
static gboolean
-gtk_text_view_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+gtk_text_view_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkTextView *text_view)
{
+ GtkTextViewPrivate *priv = text_view->priv;
GtkTextIter newplace;
- GtkTextView *text_view;
- GtkTextViewPrivate *priv;
GtkTextIter start;
GtkTextIter end;
GdkRectangle target_rect;
gint bx, by;
GdkAtom target;
gboolean can_accept = FALSE;
-
- text_view = GTK_TEXT_VIEW (widget);
- priv = text_view->priv;
target_rect = priv->text_window->allocation;
@@ -7856,8 +7773,7 @@ gtk_text_view_drag_motion (GtkWidget *widget,
&newplace,
bx, by);
- target = gtk_drag_dest_find_target (widget, drop,
- gtk_drag_dest_get_target_list (widget));
+ target = gtk_drop_target_find_mimetype (dest);
if (target == NULL)
{
@@ -7898,83 +7814,22 @@ gtk_text_view_drag_motion (GtkWidget *widget,
g_source_set_name_by_id (text_view->priv->scroll_timeout, "[gtk] drag_scan_timeout");
}
- gtk_drag_highlight (widget);
-
/* TRUE return means don't propagate the drag motion to parent
* widgets that may also be drop sites.
*/
return TRUE;
}
-static gboolean
-gtk_text_view_drag_drop (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
-{
- GtkTextView *text_view;
- GtkTextViewPrivate *priv;
- GtkTextIter drop_point;
- GdkAtom target = NULL;
-
- text_view = GTK_TEXT_VIEW (widget);
- priv = text_view->priv;
-
- if (priv->scroll_timeout != 0)
- g_source_remove (priv->scroll_timeout);
-
- priv->scroll_timeout = 0;
-
- gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
-
- gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
- &drop_point,
- priv->dnd_mark);
-
- if (gtk_text_iter_can_insert (&drop_point, priv->editable))
- target = gtk_drag_dest_find_target (widget, drop, NULL);
-
- if (target != NULL)
- gtk_drag_get_data (widget, drop, target);
- else
- gdk_drop_finish (drop, 0);
-
- return TRUE;
-}
-
-static void
-insert_text_data (GtkTextView *text_view,
- GtkTextIter *drop_point,
- GtkSelectionData *selection_data)
-{
- guchar *str;
-
- str = gtk_selection_data_get_text (selection_data);
-
- if (str)
- {
- if (!gtk_text_buffer_insert_interactive (get_buffer (text_view),
- drop_point, (gchar *) str, -1,
- text_view->priv->editable))
- {
- gtk_widget_error_bell (GTK_WIDGET (text_view));
- }
-
- g_free (str);
- }
-}
-
static GdkDragAction
-gtk_text_view_get_action (GtkWidget *textview,
- GdkDrop *drop)
+gtk_text_view_get_action (GtkTextView *textview,
+ GdkDrop *drop)
{
GdkDrag *drag = gdk_drop_get_drag (drop);
- GtkWidget *source_widget = gtk_drag_get_source_widget (drag);
GdkDragAction actions;
actions = gdk_drop_get_actions (drop);
- if (source_widget == textview &&
+ if (drag == textview->priv->drag &&
actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
@@ -7988,98 +7843,85 @@ gtk_text_view_get_action (GtkWidget *textview,
}
static void
-gtk_text_view_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data)
+got_text (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
+ GdkDrop *drop = GDK_DROP (source);
+ GtkTextView *text_view = GTK_TEXT_VIEW (data);
+ GtkTextViewPrivate *priv = text_view->priv;
+ GtkTextBuffer *buffer;
+ char *str;
GtkTextIter drop_point;
- GtkTextView *text_view;
- GtkTextViewPrivate *priv;
- GtkTextBuffer *buffer = NULL;
- GdkDragAction action = 0;
+ GdkDragAction action;
- text_view = GTK_TEXT_VIEW (widget);
- priv = text_view->priv;
-
- if (!priv->dnd_mark)
- goto done;
+ str = gdk_drop_read_text_finish (drop, result, NULL);
+ if (!str)
+ {
+ gdk_drop_finish (drop, 0);
+ return;
+ }
buffer = get_buffer (text_view);
+ gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
- gtk_text_buffer_get_iter_at_mark (buffer,
- &drop_point,
- priv->dnd_mark);
-
- if (!gtk_text_iter_can_insert (&drop_point, priv->editable))
- goto done;
-
- action = gtk_text_view_get_action (widget, drop);
- if (action == 0)
- goto done;
+ action = gtk_text_view_get_action (text_view, drop);
gtk_text_buffer_begin_user_action (buffer);
- if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
- {
- GtkTextBuffer *src_buffer = NULL;
- GtkTextIter start, end;
- gboolean copy_tags = TRUE;
+ if (!gtk_text_buffer_insert_interactive (buffer,
+ &drop_point, (gchar *) str, -1,
+ text_view->priv->editable))
+ gtk_widget_error_bell (GTK_WIDGET (text_view));
- if (gtk_selection_data_get_length (selection_data) != sizeof (src_buffer))
- return;
+ g_free (str);
- memcpy (&src_buffer, gtk_selection_data_get_data (selection_data), sizeof (src_buffer));
+ gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
+ gtk_text_buffer_place_cursor (buffer, &drop_point);
- if (src_buffer == NULL)
- return;
+ gtk_text_buffer_end_user_action (buffer);
- g_return_if_fail (GTK_IS_TEXT_BUFFER (src_buffer));
+ gdk_drop_finish (drop, action);
+}
- if (gtk_text_buffer_get_tag_table (src_buffer) !=
- gtk_text_buffer_get_tag_table (buffer))
- {
- copy_tags = FALSE;
- }
+static gboolean
+gtk_text_view_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkTextView *text_view)
+{
+ GtkTextViewPrivate *priv = text_view->priv;
+ GtkTextIter drop_point;
+ GtkTextBuffer *buffer = NULL;
- if (gtk_text_buffer_get_selection_bounds (src_buffer,
- &start,
- &end))
- {
- if (copy_tags)
- gtk_text_buffer_insert_range_interactive (buffer,
- &drop_point,
- &start,
- &end,
- priv->editable);
- else
- {
- gchar *str;
+ if (priv->scroll_timeout != 0)
+ g_source_remove (priv->scroll_timeout);
- str = gtk_text_iter_get_visible_text (&start, &end);
- gtk_text_buffer_insert_interactive (buffer,
- &drop_point, str, -1,
- priv->editable);
- g_free (str);
- }
- }
- }
- else
- insert_text_data (text_view, &drop_point, selection_data);
+ priv->scroll_timeout = 0;
- done:
- gdk_drop_finish (drop, action);
+ gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
- if (action)
- {
- gtk_text_buffer_get_iter_at_mark (buffer,
- &drop_point,
- priv->dnd_mark);
- gtk_text_buffer_place_cursor (buffer, &drop_point);
+ buffer = get_buffer (text_view);
+ gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
+
+ if (!gtk_text_iter_can_insert (&drop_point, priv->editable))
+ goto done;
+
+ if (gtk_text_view_get_action (text_view, drop) == 0)
+ goto done;
- gtk_text_buffer_end_user_action (buffer);
+ if (gdk_drop_has_value (drop, G_TYPE_STRING))
+ {
+ gdk_drop_read_text_async (drop, NULL, got_text, text_view);
+ return TRUE;
}
-}
+done:
+ gdk_drop_finish (drop, 0);
+ return TRUE;
+}
+
static void
gtk_text_view_set_hadjustment (GtkTextView *text_view,
GtkAdjustment *adjustment)
diff --git a/gtk/gtktreednd.h b/gtk/gtktreednd.h
index ca03048105..46d907e988 100644
--- a/gtk/gtktreednd.h
+++ b/gtk/gtktreednd.h
@@ -22,8 +22,8 @@
#error "Only <gtk/gtk.h> can be included directly."
#endif
+#include <gtk/gtkselection.h>
#include <gtk/gtktreemodel.h>
-#include <gtk/gtkdnd.h>
G_BEGIN_DECLS
diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c
index 7e349c9b26..e700bbbd91 100644
--- a/gtk/gtktreeview.c
+++ b/gtk/gtktreeview.c
@@ -33,6 +33,7 @@
#include "gtkcssstylepropertyprivate.h"
#include "gtkdragdest.h"
#include "gtkdragsource.h"
+#include "gtkdragicon.h"
#include "gtkentryprivate.h"
#include "gtksearchentryprivate.h"
#include "gtkeventcontrollerkey.h"
@@ -64,6 +65,7 @@
#include "gtkwindowgroup.h"
#include "gtknative.h"
#include "gtkpopover.h"
+#include "gtkselectionprivate.h"
#include "a11y/gtktreeviewaccessibleprivate.h"
@@ -149,13 +151,17 @@
* ┊ ┊
* │ ╰── <column header>
* │
- * ╰── [rubberband]
+ * ├── [rubberband]
+ * ╰── [dndtarget]
* ]|
*
* GtkTreeView has a main CSS node with name treeview and style class .view.
* It has a subnode with name header, which is the parent for all the column
* header widgets' CSS nodes.
+ *
* For rubberband selection, a subnode with name rubberband is used.
+ *
+ * For the drop target location during DND, a subnode with name dndtarget is used.
*/
enum
@@ -300,8 +306,14 @@ struct _GtkTreeViewChild
typedef struct _TreeViewDragInfo TreeViewDragInfo;
struct _TreeViewDragInfo
{
- GdkModifierType start_button_mask;
+ GdkContentFormats *source_formats;
GdkDragAction source_actions;
+ GdkDrag *drag;
+ GtkTreeRowReference *source_item;
+
+ GtkCssNode *cssnode;
+ GtkDropTarget *dest;
+ GdkModifierType start_button_mask;
guint source_set : 1;
guint dest_set : 1;
@@ -674,30 +686,28 @@ static void gtk_tree_view_forall (GtkContainer *container,
gpointer callback_data);
/* Source side drag signals */
-static void gtk_tree_view_drag_begin (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_tree_view_drag_end (GtkWidget *widget,
- GdkDrag *drag);
-static void gtk_tree_view_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data);
-static void gtk_tree_view_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag);
+static void gtk_tree_view_dnd_finished_cb (GdkDrag *drag,
+ GtkWidget *widget);
+static GBytes *gtk_tree_view_drag_data_get (const char *mimetype,
+ gpointer data);
/* Target side drag signals */
-static void gtk_tree_view_drag_leave (GtkWidget *widget,
- GdkDrop *drop);
-static gboolean gtk_tree_view_drag_motion (GtkWidget *widget,
+static void gtk_tree_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static gboolean gtk_tree_view_drag_drop (GtkWidget *widget,
+ GtkTreeView *tree_view);
+static void gtk_tree_view_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
- gint x,
- gint y);
-static void gtk_tree_view_drag_data_received (GtkWidget *widget,
+ int x,
+ int y,
+ GtkTreeView *tree_view);
+static gboolean gtk_tree_view_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
- GtkSelectionData *selection_data);
+ int x,
+ int y,
+ GtkTreeView *tree_view);
+static void gtk_tree_view_drag_data_received (GObject *source,
+ GAsyncResult *result,
+ gpointer data);
/* tree_model signals */
static gboolean gtk_tree_view_real_move_cursor (GtkTreeView *tree_view,
@@ -832,6 +842,7 @@ static inline gint gtk_tree_view_get_row_y_offset (GtkTreeView
GtkTreeRBNode *node);
static inline gint gtk_tree_view_get_row_height (GtkTreeView *tree_view,
GtkTreeRBNode *node);
+static TreeViewDragInfo* get_info (GtkTreeView *tree_view);
/* interactive search */
static void gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view);
@@ -1016,14 +1027,6 @@ gtk_tree_view_class_init (GtkTreeViewClass *class)
widget_class->measure = gtk_tree_view_measure;
widget_class->size_allocate = gtk_tree_view_size_allocate;
widget_class->snapshot = gtk_tree_view_snapshot;
- widget_class->drag_begin = gtk_tree_view_drag_begin;
- widget_class->drag_end = gtk_tree_view_drag_end;
- widget_class->drag_data_get = gtk_tree_view_drag_data_get;
- widget_class->drag_data_delete = gtk_tree_view_drag_data_delete;
- widget_class->drag_leave = gtk_tree_view_drag_leave;
- widget_class->drag_motion = gtk_tree_view_drag_motion;
- widget_class->drag_drop = gtk_tree_view_drag_drop;
- widget_class->drag_data_received = gtk_tree_view_drag_data_received;
widget_class->focus = gtk_tree_view_focus;
widget_class->grab_focus = gtk_tree_view_grab_focus;
widget_class->style_updated = gtk_tree_view_style_updated;
@@ -4863,41 +4866,47 @@ gtk_tree_view_bin_snapshot (GtkWidget *widget,
if (node == drag_highlight)
{
- /* Draw indicator for the drop
- */
GtkTreeRBTree *drag_tree = NULL;
GtkTreeRBNode *drag_node = NULL;
- gtk_style_context_save (context);
- gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE);
-
- switch (tree_view->drag_dest_pos)
+ _gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_tree, &drag_node);
+ if (drag_tree != NULL)
{
- case GTK_TREE_VIEW_DROP_BEFORE:
- gtk_style_context_add_class (context, "before");
- break;
+ TreeViewDragInfo *di;
- case GTK_TREE_VIEW_DROP_AFTER:
- gtk_style_context_add_class (context, "after");
- break;
+ di = get_info (tree_view);
+ /* Draw indicator for the drop
+ */
- case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
- case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
- gtk_style_context_add_class (context, "into");
- break;
+ switch (tree_view->drag_dest_pos)
+ {
+ case GTK_TREE_VIEW_DROP_BEFORE:
+ gtk_css_node_set_classes (di->cssnode, (const char *[]){"before", NULL});
+ break;
- default:
- break;
- }
+ case GTK_TREE_VIEW_DROP_AFTER:
+ gtk_css_node_set_classes (di->cssnode, (const char *[]){"after", NULL});
+ break;
+
+ case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
+ case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
+ gtk_css_node_set_classes (di->cssnode, (const char *[]){"into", NULL});
+ break;
+
+ default:
+ break;
+ }
+
+ gtk_style_context_save_to_node (context, di->cssnode);
+ gtk_style_context_set_state (context, gtk_style_context_get_state (context) | GTK_STATE_FLAG_DROP_ACTIVE);
- _gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_tree, &drag_node);
- if (drag_tree != NULL)
gtk_snapshot_render_frame (snapshot, context,
0, gtk_tree_view_get_row_y_offset (tree_view, drag_tree, drag_node),
bin_window_width,
gtk_tree_view_get_row_height (tree_view, drag_node));
- gtk_style_context_restore (context);
+ gtk_style_context_restore (context);
+ }
}
/* draw the big row-spanning focus rectangle, if needed */
@@ -6621,29 +6630,6 @@ _gtk_tree_view_column_autosize (GtkTreeView *tree_view,
/* Drag-and-drop */
-static void
-set_source_row (GdkDrag *drag,
- GtkTreeModel *model,
- GtkTreePath *source_row)
-{
- g_object_set_data_full (G_OBJECT (drag),
- I_("gtk-tree-view-source-row"),
- source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
- (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
-}
-
-static GtkTreePath*
-get_source_row (GdkDrag *drag)
-{
- GtkTreeRowReference *ref =
- g_object_get_data (G_OBJECT (drag), "gtk-tree-view-source-row");
-
- if (ref)
- return gtk_tree_row_reference_get_path (ref);
- else
- return NULL;
-}
-
typedef struct
{
GtkTreeRowReference *dest_row;
@@ -6749,6 +6735,10 @@ get_info (GtkTreeView *tree_view)
static void
destroy_info (TreeViewDragInfo *di)
{
+ g_clear_pointer (&di->source_formats, gdk_content_formats_unref);
+ g_clear_pointer (&di->source_item, gtk_tree_row_reference_free);
+ g_clear_object (&di->dest);
+
g_slice_free (TreeViewDragInfo, di);
}
@@ -6775,6 +6765,11 @@ ensure_info (GtkTreeView *tree_view)
static void
remove_info (GtkTreeView *tree_view)
{
+ TreeViewDragInfo *di;
+
+ di = get_info (tree_view);
+ if (di && di->dest)
+ gtk_widget_remove_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest));
g_object_set_data (G_OBJECT (tree_view), I_("gtk-tree-view-drag-info"), NULL);
}
@@ -6881,7 +6876,7 @@ scroll_row_timeout (gpointer data)
/* Returns TRUE if event should not be propagated to parent widgets */
static gboolean
set_destination_row (GtkTreeView *tree_view,
- GdkDrop *drop,
+ GtkDropTarget *dest,
/* coordinates relative to the widget */
gint x,
gint y,
@@ -6919,12 +6914,9 @@ set_destination_row (GtkTreeView *tree_view,
return FALSE; /* no longer a drop site */
}
- *target = gtk_drag_dest_find_target (widget, drop,
- gtk_drag_dest_get_target_list (widget));
+ *target = gtk_drop_target_find_mimetype (dest);
if (*target == NULL)
- {
- return FALSE;
- }
+ return FALSE;
if (!gtk_tree_view_get_dest_row_at_pos (tree_view,
x, y,
@@ -7058,13 +7050,17 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view)
{
GtkWidget *widget = GTK_WIDGET (tree_view);
gdouble start_x, start_y, offset_x, offset_y;
- GdkDrag *drag;
TreeViewDragInfo *di;
GtkTreePath *path = NULL;
gint button;
GtkTreeModel *model;
gboolean retval = FALSE;
gint bin_x, bin_y;
+ GdkSurface *surface;
+ GdkDevice *device;
+ GdkContentProvider *content;
+ GdkDrag *drag;
+ GdkPaintable *icon;
di = get_info (tree_view);
@@ -7115,13 +7111,23 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view)
gtk_gesture_set_state (GTK_GESTURE (tree_view->drag_gesture),
GTK_EVENT_SEQUENCE_CLAIMED);
- drag = gtk_drag_begin (widget,
- gtk_gesture_get_device (GTK_GESTURE (tree_view->drag_gesture)),
- gtk_drag_source_get_target_list (widget),
- di->source_actions,
- start_x, start_y);
+ surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (tree_view)));
+ device = gtk_gesture_get_device (GTK_GESTURE (tree_view->drag_gesture)),
+ content = gdk_content_provider_new_with_formats (di->source_formats, gtk_tree_view_drag_data_get, tree_view);
+
+ drag = gdk_drag_begin (surface, device, content, di->source_actions, start_x, start_y);
+
+ g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_tree_view_dnd_finished_cb), tree_view);
+
+ icon = gtk_tree_view_create_row_drag_icon (tree_view, path);
+ gtk_drag_icon_set_from_paintable (drag, icon, tree_view->press_start_x + 1, 1);
+ g_object_unref (icon);
+
+ di->drag = drag;
- set_source_row (drag, model, path);
+ g_object_unref (drag);
+
+ di->source_item = gtk_tree_row_reference_new (model, path);
out:
if (path)
@@ -7130,87 +7136,71 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view)
return retval;
}
-
static void
-gtk_tree_view_drag_begin (GtkWidget *widget,
- GdkDrag *drag)
+gtk_tree_view_dnd_finished_cb (GdkDrag *drag,
+ GtkWidget *widget)
{
- GtkTreeView *tree_view;
- GtkTreePath *path = NULL;
- gint cell_x, cell_y;
- GdkPaintable *row_pix;
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
TreeViewDragInfo *di;
+ GtkTreeModel *model;
+ GtkTreePath *source_row;
- tree_view = GTK_TREE_VIEW (widget);
+ tree_view->event_last_x = -10000;
+ tree_view->event_last_y = -10000;
- /* if the user uses a custom DND source impl, we don't set the icon here */
- di = get_info (tree_view);
+ if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE)
+ return;
- if (di == NULL || !di->source_set)
+ tree_view = GTK_TREE_VIEW (widget);
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
return;
- gtk_tree_view_get_path_at_pos (tree_view,
- tree_view->press_start_x,
- tree_view->press_start_y,
- &path,
- NULL,
- &cell_x,
- &cell_y);
-
- /* If path is NULL, there's nothing we can drag. For now, we silently
- * bail out. Actually, dragging should not be possible from an empty
- * tree view, but there's no way we can cancel that from here.
- * Automatically unsetting the tree view as drag source for empty models
- * is something that would likely break other people's code ...
- */
- if (!path)
+ di = get_info (tree_view);
+
+ if (di == NULL || di->source_item == NULL)
return;
- row_pix = gtk_tree_view_create_row_drag_icon (tree_view, path);
+ source_row = gtk_tree_row_reference_get_path (di->source_item);
- gtk_drag_set_icon_paintable (drag, row_pix, tree_view->press_start_x + 1, 1);
+ if (source_row == NULL)
+ return;
- g_object_unref (row_pix);
- gtk_tree_path_free (path);
-}
+ gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row);
-static void
-gtk_tree_view_drag_end (GtkWidget *widget,
- GdkDrag *drag)
-{
- GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ gtk_tree_path_free (source_row);
- tree_view->event_last_x = -10000;
- tree_view->event_last_y = -10000;
+ g_clear_pointer (&di->source_item, gtk_tree_row_reference_free);
}
/* Default signal implementations for the drag signals */
-static void
-gtk_tree_view_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data)
+static GBytes *
+gtk_tree_view_drag_data_get (const char *mime_type,
+ gpointer data)
{
- GtkTreeView *tree_view;
+ GtkTreeView *tree_view = data;
GtkTreeModel *model;
TreeViewDragInfo *di;
GtkTreePath *source_row;
+ GtkSelectionData sdata = { 0, };
- tree_view = GTK_TREE_VIEW (widget);
+ sdata.target = g_intern_string (mime_type);
model = gtk_tree_view_get_model (tree_view);
if (model == NULL)
- return;
+ return NULL;
- di = get_info (GTK_TREE_VIEW (widget));
+ di = get_info (tree_view);
- if (di == NULL)
- return;
+ if (di == NULL || di->source_item == NULL)
+ return NULL;
- source_row = get_source_row (drag);
+ source_row = gtk_tree_row_reference_get_path (di->source_item);
if (source_row == NULL)
- return;
+ return NULL;
/* We can implement the GTK_TREE_MODEL_ROW target generically for
* any model; for DragSource models there are some other targets
@@ -7220,90 +7210,58 @@ gtk_tree_view_drag_data_get (GtkWidget *widget,
if (GTK_IS_TREE_DRAG_SOURCE (model) &&
gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
source_row,
- selection_data))
+ &sdata))
goto done;
/* If drag_data_get does nothing, try providing row data. */
- if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("GTK_TREE_MODEL_ROW"))
+ if (mime_type == g_intern_static_string ("GTK_TREE_MODEL_ROW"))
{
- gtk_tree_set_row_drag_data (selection_data,
- model,
- source_row);
+ gtk_tree_set_row_drag_data (&sdata, model, source_row);
}
done:
gtk_tree_path_free (source_row);
-}
-
-
-static void
-gtk_tree_view_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag)
-{
- TreeViewDragInfo *di;
- GtkTreeModel *model;
- GtkTreeView *tree_view;
- GtkTreePath *source_row;
-
- tree_view = GTK_TREE_VIEW (widget);
- model = gtk_tree_view_get_model (tree_view);
-
- if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
- return;
-
- di = get_info (tree_view);
-
- if (di == NULL)
- return;
-
- source_row = get_source_row (drag);
-
- if (source_row == NULL)
- return;
-
- gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), source_row);
-
- gtk_tree_path_free (source_row);
- set_source_row (drag, NULL, NULL);
+ return g_bytes_new_take ((gpointer)gtk_selection_data_get_data (&sdata),
+ gtk_selection_data_get_length (&sdata));
}
static void
-gtk_tree_view_drag_leave (GtkWidget *widget,
- GdkDrop *drop)
+gtk_tree_view_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkTreeView *tree_view)
{
- GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
-
/* unset any highlight row */
- gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
+ gtk_tree_view_set_drag_dest_row (tree_view,
NULL,
GTK_TREE_VIEW_DROP_BEFORE);
- remove_scroll_timeout (GTK_TREE_VIEW (widget));
- remove_open_timeout (GTK_TREE_VIEW (widget));
+ remove_scroll_timeout (tree_view);
+ remove_open_timeout (tree_view);
tree_view->event_last_x = -10000;
tree_view->event_last_y = -10000;
}
-static gboolean
-gtk_tree_view_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+static void
+gtk_tree_view_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkTreeView *tree_view)
{
gboolean empty;
GtkTreePath *path = NULL;
GtkTreeViewDropPosition pos;
- GtkTreeView *tree_view;
GdkDragAction suggested_action = 0;
GdkAtom target;
- tree_view = GTK_TREE_VIEW (widget);
-
- if (!set_destination_row (tree_view, drop, x, y, &suggested_action, &target))
- return FALSE;
+ if (!set_destination_row (tree_view, dest, x, y, &suggested_action, &target))
+ {
+ gdk_drop_status (drop, 0);
+ return;
+ }
tree_view->event_last_x = x;
tree_view->event_last_y = y;
@@ -7339,7 +7297,7 @@ gtk_tree_view_drag_motion (GtkWidget *widget,
* determining whether to accept the drop
*/
set_status_pending (drop, suggested_action);
- gtk_drag_get_data (widget, drop, target);
+ gtk_drop_target_read_selection (dest, target, NULL, gtk_tree_view_drag_data_received, tree_view);
}
else
{
@@ -7350,18 +7308,16 @@ gtk_tree_view_drag_motion (GtkWidget *widget,
if (path)
gtk_tree_path_free (path);
-
- return TRUE;
}
static gboolean
-gtk_tree_view_drag_drop (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+gtk_tree_view_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkTreeView *tree_view)
{
- GtkTreeView *tree_view;
GtkTreePath *path;
GdkDragAction suggested_action = 0;
GdkAtom target = NULL;
@@ -7370,12 +7326,10 @@ gtk_tree_view_drag_drop (GtkWidget *widget,
gboolean path_down_mode;
gboolean drop_append_mode;
- tree_view = GTK_TREE_VIEW (widget);
-
model = gtk_tree_view_get_model (tree_view);
- remove_scroll_timeout (GTK_TREE_VIEW (widget));
- remove_open_timeout (GTK_TREE_VIEW (widget));
+ remove_scroll_timeout (tree_view);
+ remove_open_timeout (tree_view);
di = get_info (tree_view);
@@ -7385,7 +7339,7 @@ gtk_tree_view_drag_drop (GtkWidget *widget,
if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
return FALSE;
- if (!set_destination_row (tree_view, drop, x, y, &suggested_action, &target))
+ if (!set_destination_row (tree_view, dest, x, y, &suggested_action, &target))
return FALSE;
path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
@@ -7405,13 +7359,13 @@ gtk_tree_view_drag_drop (GtkWidget *widget,
gtk_tree_path_free (path);
/* Unset this thing */
- gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
+ gtk_tree_view_set_drag_dest_row (tree_view,
NULL,
GTK_TREE_VIEW_DROP_BEFORE);
if (target != NULL)
{
- gtk_drag_get_data (widget, drop, target);
+ gtk_drop_target_read_selection (dest, target, NULL, gtk_tree_view_drag_data_received, tree_view);
return TRUE;
}
else
@@ -7419,16 +7373,19 @@ gtk_tree_view_drag_drop (GtkWidget *widget,
}
static GdkDragAction
-gtk_tree_view_get_action (GtkWidget *treeview,
+gtk_tree_view_get_action (GtkWidget *widget,
GdkDrop *drop)
{
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ TreeViewDragInfo *di;
GdkDrag *drag = gdk_drop_get_drag (drop);
- GtkWidget *source_widget = gtk_drag_get_source_widget (drag);
GdkDragAction actions;
+ di = get_info (tree_view);
+
actions = gdk_drop_get_actions (drop);
- if (source_widget == treeview &&
+ if (di && di->drag == drag &&
actions & GDK_ACTION_MOVE)
return GDK_ACTION_MOVE;
@@ -7442,20 +7399,23 @@ gtk_tree_view_get_action (GtkWidget *treeview,
}
static void
-gtk_tree_view_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data)
+gtk_tree_view_drag_data_received (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
+ GtkDropTarget *dest = GTK_DROP_TARGET (source);
+ GtkTreeView *tree_view = GTK_TREE_VIEW (data);
+ GdkDrop *drop = gtk_drop_target_get_drop (dest);
GtkTreePath *path;
TreeViewDragInfo *di;
GtkTreeModel *model;
- GtkTreeView *tree_view;
GtkTreePath *dest_row;
GdkDragAction suggested_action;
gboolean path_down_mode;
gboolean drop_append_mode;
+ GtkSelectionData *selection_data;
- tree_view = GTK_TREE_VIEW (widget);
+ selection_data = gtk_drop_target_read_selection_finish (dest, result, NULL);
model = gtk_tree_view_get_model (tree_view);
@@ -7512,7 +7472,7 @@ gtk_tree_view_drag_data_received (GtkWidget *widget,
/* If you can't drop, remove user drop indicator until the next motion */
if (suggested_action == 0)
- gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget),
+ gtk_tree_view_set_drag_dest_row (tree_view,
NULL,
GTK_TREE_VIEW_DROP_BEFORE);
@@ -7537,7 +7497,7 @@ gtk_tree_view_drag_data_received (GtkWidget *widget,
if (gtk_selection_data_get_length (selection_data) >= 0)
{
- suggested_action = gtk_tree_view_get_action (widget, drop);
+ suggested_action = gtk_tree_view_get_action (GTK_WIDGET (tree_view), drop);
if (suggested_action &&
!gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
@@ -12915,15 +12875,13 @@ gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view,
g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
- gtk_drag_source_set (GTK_WIDGET (tree_view),
- 0,
- formats,
- actions);
-
di = ensure_info (tree_view);
- di->start_button_mask = start_button_mask;
+ di->source_formats = gdk_content_formats_ref (formats);
di->source_actions = actions;
+ di->drag = NULL;
+
+ di->start_button_mask = start_button_mask;
di->source_set = TRUE;
unset_reorderable (tree_view);
@@ -12938,25 +12896,39 @@ gtk_tree_view_enable_model_drag_source (GtkTreeView *tree_view,
*
* Turns @tree_view into a drop destination for automatic DND. Calling
* this method sets #GtkTreeView:reorderable to %FALSE.
+ *
+ * Returns: (transfer none): the drop target that has been attached
**/
-void
+GtkDropTarget *
gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view,
GdkContentFormats *formats,
GdkDragAction actions)
{
TreeViewDragInfo *di;
+ GtkCssNode *widget_node;
- g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
-
- gtk_drag_dest_set (GTK_WIDGET (tree_view),
- 0,
- formats,
- actions);
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
di = ensure_info (tree_view);
di->dest_set = TRUE;
+ di->dest = gtk_drop_target_new (formats, actions);
+ g_signal_connect (di->dest, "drag-leave", G_CALLBACK (gtk_tree_view_drag_leave), tree_view);
+ g_signal_connect (di->dest, "drag-motion", G_CALLBACK (gtk_tree_view_drag_motion), tree_view);
+ g_signal_connect (di->dest, "drag-drop", G_CALLBACK (gtk_tree_view_drag_drop), tree_view);
+ gtk_widget_add_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest));
+ g_object_ref (di->dest);
+
+ widget_node = gtk_widget_get_css_node (GTK_WIDGET (tree_view));
+ di->cssnode = gtk_css_node_new ();
+ gtk_css_node_set_name (di->cssnode, I_("dndtarget"));
+ gtk_css_node_set_parent (di->cssnode, widget_node);
+ gtk_css_node_set_state (di->cssnode, gtk_css_node_get_state (widget_node));
+ g_object_unref (di->cssnode);
+
unset_reorderable (tree_view);
+
+ return di->dest;
}
/**
@@ -12980,7 +12952,7 @@ gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view)
{
if (di->source_set)
{
- gtk_drag_source_unset (GTK_WIDGET (tree_view));
+ g_clear_pointer (&di->source_formats, gdk_content_formats_unref);
di->source_set = FALSE;
}
@@ -13012,8 +12984,12 @@ gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view)
{
if (di->dest_set)
{
- gtk_drag_dest_unset (GTK_WIDGET (tree_view));
+ gtk_widget_remove_controller (GTK_WIDGET (tree_view), GTK_EVENT_CONTROLLER (di->dest));
+ di->dest = NULL;
di->dest_set = FALSE;
+
+ gtk_css_node_set_parent (di->cssnode, NULL);
+ di->cssnode = NULL;
}
if (!di->dest_set && !di->source_set)
diff --git a/gtk/gtktreeview.h b/gtk/gtktreeview.h
index 9125c14063..d9d400034a 100644
--- a/gtk/gtktreeview.h
+++ b/gtk/gtktreeview.h
@@ -25,8 +25,9 @@
#include <gtk/gtkcontainer.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreeviewcolumn.h>
-#include <gtk/gtkdnd.h>
#include <gtk/gtkentry.h>
+#include <gtk/gtkdragsource.h>
+#include <gtk/gtkdragdest.h>
G_BEGIN_DECLS
@@ -319,7 +320,7 @@ void gtk_tree_view_enable_model_drag_source (GtkTreeView
GdkContentFormats *formats,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
-void gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view,
+GtkDropTarget * gtk_tree_view_enable_model_drag_dest (GtkTreeView *tree_view,
GdkContentFormats *formats,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 433dfe2f06..76de31262a 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -517,14 +517,6 @@ enum {
MNEMONIC_ACTIVATE,
MOVE_FOCUS,
KEYNAV_FAILED,
- DRAG_BEGIN,
- DRAG_END,
- DRAG_DATA_DELETE,
- DRAG_LEAVE,
- DRAG_MOTION,
- DRAG_DROP,
- DRAG_DATA_GET,
- DRAG_DATA_RECEIVED,
POPUP_MENU,
ACCEL_CLOSURES_CHANGED,
DISPLAY_CHANGED,
@@ -919,13 +911,6 @@ gtk_widget_class_init (GtkWidgetClass *klass)
klass->focus = gtk_widget_real_focus;
klass->move_focus = gtk_widget_real_move_focus;
klass->keynav_failed = gtk_widget_real_keynav_failed;
- klass->drag_begin = NULL;
- klass->drag_end = NULL;
- klass->drag_data_delete = NULL;
- klass->drag_leave = NULL;
- klass->drag_motion = NULL;
- klass->drag_drop = NULL;
- klass->drag_data_received = NULL;
klass->can_activate_accel = gtk_widget_real_can_activate_accel;
klass->query_tooltip = gtk_widget_real_query_tooltip;
klass->style_updated = gtk_widget_real_style_updated;
@@ -1658,368 +1643,6 @@ gtk_widget_class_init (GtkWidgetClass *klass)
_gtk_marshal_BOOLEAN__ENUMv);
/**
- * GtkWidget::drag-leave:
- * @widget: the object which received the signal.
- * @context: the drag context
- * @time: the timestamp of the motion event
- *
- * The ::drag-leave signal is emitted on the drop site when the cursor
- * leaves the widget. A typical reason to connect to this signal is to
- * undo things done in #GtkWidget::drag-motion, e.g. undo highlighting
- * with gtk_drag_unhighlight().
- *
- *
- * Likewise, the #GtkWidget::drag-leave signal is also emitted before the
- * ::drag-drop signal, for instance to allow cleaning up of a preview item
- * created in the #GtkWidget::drag-motion signal handler.
- */
- widget_signals[DRAG_LEAVE] =
- g_signal_new (I_("drag-leave"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_leave),
- NULL, NULL,
- NULL,
- G_TYPE_NONE, 1,
- GDK_TYPE_DROP);
-
- /**
- * GtkWidget::drag-begin:
- * @widget: the object which received the signal
- * @context: the drag context
- *
- * The ::drag-begin signal is emitted on the drag source when a drag is
- * started. A typical reason to connect to this signal is to set up a
- * custom drag icon with e.g. gtk_drag_source_set_icon_paintable().
- *
- * Note that some widgets set up a drag icon in the default handler of
- * this signal, so you may have to use g_signal_connect_after() to
- * override what the default handler did.
- */
- widget_signals[DRAG_BEGIN] =
- g_signal_new (I_("drag-begin"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_begin),
- NULL, NULL,
- NULL,
- G_TYPE_NONE, 1,
- GDK_TYPE_DRAG);
-
- /**
- * GtkWidget::drag-end:
- * @widget: the object which received the signal
- * @context: the drag context
- *
- * The ::drag-end signal is emitted on the drag source when a drag is
- * finished. A typical reason to connect to this signal is to undo
- * things done in #GtkWidget::drag-begin.
- */
- widget_signals[DRAG_END] =
- g_signal_new (I_("drag-end"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_end),
- NULL, NULL,
- NULL,
- G_TYPE_NONE, 1,
- GDK_TYPE_DRAG);
-
- /**
- * GtkWidget::drag-data-delete:
- * @widget: the object which received the signal
- * @context: the drag context
- *
- * The ::drag-data-delete signal is emitted on the drag source when a drag
- * with the action %GDK_ACTION_MOVE is successfully completed. The signal
- * handler is responsible for deleting the data that has been dropped. What
- * "delete" means depends on the context of the drag operation.
- */
- widget_signals[DRAG_DATA_DELETE] =
- g_signal_new (I_("drag-data-delete"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_data_delete),
- NULL, NULL,
- NULL,
- G_TYPE_NONE, 1,
- GDK_TYPE_DRAG);
-
- /**
- * GtkWidget::drag-failed:
- * @widget: the object which received the signal
- * @context: the drag context
- * @result: the result of the drag operation
- *
- * The ::drag-failed signal is emitted on the drag source when a drag has
- * failed. The signal handler may hook custom code to handle a failed DnD
- * operation based on the type of error, it returns %TRUE is the failure has
- * been already handled (not showing the default "drag operation failed"
- * animation), otherwise it returns %FALSE.
- *
- * Returns: %TRUE if the failed drag operation has been already handled.
- */
- widget_signals[DRAG_FAILED] =
- g_signal_new (I_("drag-failed"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_failed),
- _gtk_boolean_handled_accumulator, NULL,
- _gtk_marshal_BOOLEAN__OBJECT_ENUM,
- G_TYPE_BOOLEAN, 2,
- GDK_TYPE_DRAG,
- GTK_TYPE_DRAG_RESULT);
- g_signal_set_va_marshaller (widget_signals[DRAG_FAILED],
- G_TYPE_FROM_CLASS (klass),
- _gtk_marshal_BOOLEAN__OBJECT_ENUMv);
-
- /**
- * GtkWidget::drag-motion:
- * @widget: the object which received the signal
- * @drop: the #GdkDrop
- * @x: the x coordinate of the current cursor position
- * @y: the y coordinate of the current cursor position
- *
- * The ::drag-motion signal is emitted on the drop site when the user
- * moves the cursor over the widget during a drag. The signal handler
- * must determine whether the cursor position is in a drop zone or not.
- * If it is not in a drop zone, it returns %FALSE and no further processing
- * is necessary. Otherwise, the handler returns %TRUE. In this case, the
- * handler is responsible for providing the necessary information for
- * displaying feedback to the user, by calling gdk_drag_status().
- *
- * If the decision whether the drop will be accepted or rejected can't be
- * made based solely on the cursor position and the type of the data, the
- * handler may inspect the dragged data by calling gtk_drag_get_data() and
- * defer the gdk_drag_status() call to the #GtkWidget::drag-data-received
- * handler. Note that you must pass #GTK_DEST_DEFAULT_DROP,
- * #GTK_DEST_DEFAULT_MOTION or #GTK_DEST_DEFAULT_ALL to gtk_drag_dest_set()
- * when using the drag-motion signal that way.
- *
- * Also note that there is no drag-enter signal. The drag receiver has to
- * keep track of whether he has received any drag-motion signals since the
- * last #GtkWidget::drag-leave and if not, treat the drag-motion signal as
- * an "enter" signal. Upon an "enter", the handler will typically highlight
- * the drop site with gtk_drag_highlight().
- * |[<!-- language="C" -->
- * static void
- * drag_motion (GtkWidget *widget,
- * GdkDrop *drop,
- * gint x,
- * gint y,
- * {
- * GdkAtom target;
- *
- * PrivateData *private_data = GET_PRIVATE_DATA (widget);
- *
- * if (!private_data->drag_highlight)
- * {
- * private_data->drag_highlight = 1;
- * gtk_drag_highlight (widget);
- * }
- *
- * target = gtk_drag_dest_find_target (widget, drop, NULL);
- * if (target == NULL)
- * gdk_drop_status (drop, 0);
- * else
- * {
- * private_data->pending_status
- * = gdk_drop_get_actions (drop);
- * gtk_drag_get_data (widget, drop, target);
- * }
- *
- * return TRUE;
- * }
- *
- * static void
- * drag_data_received (GtkWidget *widget,
- * GdkDrop *drop,
- * GtkSelectionData *selection_data)
- * {
- * PrivateData *private_data = GET_PRIVATE_DATA (widget);
- *
- * if (private_data->suggested_action)
- * {
- * private_data->suggested_action = 0;
- *
- * // We are getting this data due to a request in drag_motion,
- * // rather than due to a request in drag_drop, so we are just
- * // supposed to call gdk_drag_status(), not actually paste in
- * // the data.
- *
- * str = gtk_selection_data_get_text (selection_data);
- * if (!data_is_acceptable (str))
- * gdk_drop_status (drop, 0);
- * else
- * gdk_drag_status (drop, GDK_ACTION_ALL);
- * }
- * else
- * {
- * // accept the drop
- * }
- * }
- * ]|
- *
- * Returns: whether the cursor position is in a drop zone
- */
- widget_signals[DRAG_MOTION] =
- g_signal_new (I_("drag-motion"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_motion),
- _gtk_boolean_handled_accumulator, NULL,
- _gtk_marshal_BOOLEAN__OBJECT_INT_INT,
- G_TYPE_BOOLEAN, 3,
- GDK_TYPE_DROP,
- G_TYPE_INT,
- G_TYPE_INT);
- g_signal_set_va_marshaller (widget_signals[DRAG_MOTION],
- G_TYPE_FROM_CLASS (klass),
- _gtk_marshal_BOOLEAN__OBJECT_INT_INTv);
-
- /**
- * GtkWidget::drag-drop:
- * @widget: the object which received the signal
- * @drop: the #GdkDrop
- * @x: the x coordinate of the current cursor position
- * @y: the y coordinate of the current cursor position
- *
- * The ::drag-drop signal is emitted on the drop site when the user drops
- * the data onto the widget. The signal handler must determine whether
- * the cursor position is in a drop zone or not. If it is not in a drop
- * zone, it returns %FALSE and no further processing is necessary.
- * Otherwise, the handler returns %TRUE. In this case, the handler must
- * ensure that gdk_drag_finish() is called to let the source know that
- * the drop is done. The call to gdk_drag_finish() can be done either
- * directly or in a #GtkWidget::drag-data-received handler which gets
- * triggered by calling gtk_drag_get_data() to receive the data for one
- * or more of the supported targets.
- *
- * Returns: whether the cursor position is in a drop zone
- */
- widget_signals[DRAG_DROP] =
- g_signal_new (I_("drag-drop"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_drop),
- _gtk_boolean_handled_accumulator, NULL,
- _gtk_marshal_BOOLEAN__OBJECT_INT_INT,
- G_TYPE_BOOLEAN, 3,
- GDK_TYPE_DROP,
- G_TYPE_INT,
- G_TYPE_INT);
- g_signal_set_va_marshaller (widget_signals[DRAG_DROP],
- G_TYPE_FROM_CLASS (klass),
- _gtk_marshal_BOOLEAN__OBJECT_INT_INTv);
-
- /**
- * GtkWidget::drag-data-get:
- * @widget: the object which received the signal
- * @context: the drag context
- * @data: the #GtkSelectionData to be filled with the dragged data
- * @info: the info that has been registered with the target in the
- * #GtkTargetList
- *
- * The ::drag-data-get signal is emitted on the drag source when the drop
- * site requests the data which is dragged. It is the responsibility of
- * the signal handler to fill @data with the data in the format which
- * is indicated by @info. See gtk_selection_data_set() and
- * gtk_selection_data_set_text().
- */
- widget_signals[DRAG_DATA_GET] =
- g_signal_new (I_("drag-data-get"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_data_get),
- NULL, NULL,
- _gtk_marshal_VOID__OBJECT_BOXED,
- G_TYPE_NONE, 2,
- GDK_TYPE_DRAG,
- GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE);
- g_signal_set_va_marshaller (widget_signals[DRAG_DATA_GET],
- G_TYPE_FROM_CLASS (klass),
- _gtk_marshal_VOID__OBJECT_BOXEDv);
-
- /**
- * GtkWidget::drag-data-received:
- * @widget: the object which received the signal
- * @drop: the #GdkDrop
- * @x: where the drop happened
- * @y: where the drop happened
- * @data: the received data
- *
- * The ::drag-data-received signal is emitted on the drop site when the
- * dragged data has been received. If the data was received in order to
- * determine whether the drop will be accepted, the handler is expected
- * to call gdk_drag_status() and not finish the drag.
- * If the data was received in response to a #GtkWidget::drag-drop signal
- * (and this is the last target to be received), the handler for this
- * signal is expected to process the received data and then call
- * gdk_drag_finish(), setting the @success parameter depending on
- * whether the data was processed successfully.
- *
- * Applications must create some means to determine why the signal was emitted
- * and therefore whether to call gdk_drag_status() or gdk_drag_finish().
- *
- * The handler may inspect the selected action with
- * gdk_drag_context_get_selected_action() before calling
- * gdk_drag_finish(), e.g. to implement %GDK_ACTION_ASK as
- * shown in the following example:
- * |[<!-- language="C" -->
- * void
- * drag_data_received (GtkWidget *widget,
- * GdkDrop *drop,
- * GtkSelectionData *data)
- * {
- * if ((data->length >= 0) && (data->format == 8))
- * {
- * GdkDragAction action;
- *
- * // handle data here
- *
- * action = gdk_drop_get_actions (drop);
- * if (!gdk_drag_action_is_unique (action))
- * {
- * GtkWidget *dialog;
- * gint response;
- *
- * dialog = gtk_message_dialog_new (NULL,
- * GTK_DIALOG_MODAL |
- * GTK_DIALOG_DESTROY_WITH_PARENT,
- * GTK_MESSAGE_INFO,
- * GTK_BUTTONS_YES_NO,
- * "Move the data ?\n");
- * response = gtk_dialog_run (GTK_DIALOG (dialog));
- * gtk_widget_destroy (dialog);
- *
- * if (response == GTK_RESPONSE_YES)
- * action = GDK_ACTION_MOVE;
- * else
- * action = GDK_ACTION_COPY;
- * }
- *
- * gdk_drop_finish (context, action);
- * }
- * else
- * gdk_drop_finish (context, 0);
- * }
- * ]|
- */
- widget_signals[DRAG_DATA_RECEIVED] =
- g_signal_new (I_("drag-data-received"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, drag_data_received),
- NULL, NULL,
- _gtk_marshal_VOID__OBJECT_BOXED,
- G_TYPE_NONE, 2,
- GDK_TYPE_DROP,
- GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE);
- g_signal_set_va_marshaller (widget_signals[DRAG_DATA_RECEIVED],
- G_TYPE_FROM_CLASS (klass),
- _gtk_marshal_VOID__OBJECT_BOXEDv);
-
- /**
* GtkWidget::query-tooltip:
* @widget: the object which received the signal
* @x: the x coordinate of the cursor position where the request has
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 2eb5c2797c..fc1221fc81 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -187,24 +187,6 @@ struct _GtkWidget
* @focus:
* @move_focus: Signal emitted when a change of focus is requested
* @keynav_failed: Signal emitted if keyboard navigation fails.
- * @drag_begin: Signal emitted on the drag source when a drag is
- * started.
- * @drag_end: Signal emitted on the drag source when a drag is
- * finished.
- * @drag_data_get: Signal emitted on the drag source when the drop
- * site requests the data which is dragged.
- * @drag_data_delete: Signal emitted on the drag source when a drag
- * with the action %GDK_ACTION_MOVE is successfully completed.
- * @drag_leave: Signal emitted on the drop site when the cursor leaves
- * the widget.
- * @drag_motion: signal emitted on the drop site when the user moves
- * the cursor over the widget during a drag.
- * @drag_drop: Signal emitted on the drop site when the user drops the
- * data onto the widget.
- * @drag_data_received: Signal emitted on the drop site when the
- * dragged data has been received.
- * @drag_failed: Signal emitted on the drag source when a drag has
- * failed.
* @popup_menu: Signal emitted whenever a widget should pop up a
* context menu.
* @get_accessible: Returns the accessible object that describes the
@@ -276,35 +258,6 @@ struct _GtkWidgetClass
gboolean (* keynav_failed) (GtkWidget *widget,
GtkDirectionType direction);
- /* Source side drag signals */
- void (* drag_begin) (GtkWidget *widget,
- GdkDrag *drag);
- void (* drag_end) (GtkWidget *widget,
- GdkDrag *drag);
- void (* drag_data_get) (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data);
- void (* drag_data_delete) (GtkWidget *widget,
- GdkDrag *drag);
-
- /* Target side drag signals */
- void (* drag_leave) (GtkWidget *widget,
- GdkDrop *drop);
- gboolean (* drag_motion) (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y);
- gboolean (* drag_drop) (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y);
- void (* drag_data_received) (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data);
- gboolean (* drag_failed) (GtkWidget *widget,
- GdkDrag *drag,
- GtkDragResult result);
-
/* Signals used only for keybindings */
gboolean (* popup_menu) (GtkWidget *widget);
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 3901f2bb80..c0a5067a52 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -1784,6 +1784,7 @@ gtk_window_init (GtkWindow *window)
GtkEventController *motion_controller;
#ifdef GDK_WINDOWING_X11
GdkContentFormats *targets;
+ GtkDropTarget *dest;
#endif
widget = GTK_WIDGET (window);
@@ -1838,11 +1839,9 @@ gtk_window_init (GtkWindow *window)
#ifdef GDK_WINDOWING_X11
targets = gdk_content_formats_new (dnd_dest_targets, G_N_ELEMENTS (dnd_dest_targets));
- gtk_drag_dest_set (GTK_WIDGET (window),
- GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
- targets,
- GDK_ACTION_MOVE);
+ dest = gtk_drop_target_new (targets, GDK_ACTION_MOVE);
gdk_content_formats_unref (targets);
+ gtk_widget_add_controller (GTK_WIDGET (window), GTK_EVENT_CONTROLLER (dest));
#endif
seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
diff --git a/gtk/meson.build b/gtk/meson.build
index 859bbd3a5e..c2c3773fab 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -213,7 +213,6 @@ gtk_public_sources = files([
'gtkcontainer.c',
'gtkcssprovider.c',
'gtkdialog.c',
- 'gtkdnd.c',
'gtkdragdest.c',
'gtkdragsource.c',
'gtkdrawingarea.c',
@@ -472,7 +471,6 @@ gtk_public_headers = files([
'gtkcustomlayout.h',
'gtkdebug.h',
'gtkdialog.h',
- 'gtkdnd.h',
'gtkdragdest.h',
'gtkdragsource.h',
'gtkdrawingarea.h',
diff --git a/gtk/theme/Adwaita/_common.scss b/gtk/theme/Adwaita/_common.scss
index dc56297ee6..8d1d5142ec 100644
--- a/gtk/theme/Adwaita/_common.scss
+++ b/gtk/theme/Adwaita/_common.scss
@@ -142,7 +142,19 @@ textview {
textview border { background-color: mix($bg_color, $base_color, 50%); }
-iconview { @extend .view; }
+iconview {
+ @extend .view;
+
+ &:drop(active) {
+ box-shadow: none;
+ }
+
+ dndtarget:drop(active) {
+ border-style: solid;
+ border-width: 1px;
+ border-color: $selected_borders_color;
+ }
+}
.rubberband,
rubberband {
@@ -1906,6 +1918,9 @@ treeview.view {
border-top: $backdrop_bg_color;
}
&:drop(active) {
+ box-shadow: none;
+ }
+ dndtarget:drop(active) {
border-style: solid none;
border-width: 1px;
border-color: $selected_borders_color;
@@ -3893,6 +3908,15 @@ expander-widget title:hover > expander {
color: lighten($fg_color,30%); //only lightens the icon
}
+placessidebar,
+stackswitcher,
+expander-widget {
+ &:not(decoration):not(window):drop(active):focus,
+ &:not(decoration):not(window):drop(active) {
+ box-shadow: none;
+ }
+}
+
/************
* Calendar *
diff --git a/gtk/ui/gtkfilechooserwidget.ui b/gtk/ui/gtkfilechooserwidget.ui
index 263bd6af2a..a1ee4f8573 100644
--- a/gtk/ui/gtkfilechooserwidget.ui
+++ b/gtk/ui/gtkfilechooserwidget.ui
@@ -156,11 +156,6 @@
<signal name="key-pressed" handler="treeview_key_press_cb" swapped="no"/>
</object>
</child>
- <signal name="drag-data-received" handler="file_list_drag_data_received_cb" swapped="no"/>
- <signal name="drag-drop" handler="file_list_drag_drop_cb" swapped="no"/>
- <signal name="drag-begin" handler="file_list_drag_begin_cb" swapped="no"/>
- <signal name="drag-motion" handler="file_list_drag_motion_cb" swapped="no"/>
- <signal name="drag-end" handler="file_list_drag_end_cb" swapped="no"/>
<signal name="popup-menu" handler="list_popup_menu_cb" swapped="no"/>
<signal name="query-tooltip" handler="file_list_query_tooltip_cb" swapped="no"/>
<signal name="row-activated" handler="list_row_activated" swapped="no"/>
diff --git a/tests/meson.build b/tests/meson.build
index e6041c3627..37d6f0d87f 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -31,6 +31,7 @@ gtk_tests = [
['testdialog'],
['testdnd'],
['testdnd2'],
+ ['testdnd3'],
['testellipsise'],
['testemblems'],
['testentrycompletion'],
@@ -53,7 +54,6 @@ gtk_tests = [
['testiconview-keynav'],
['testicontheme'],
['testinfobar'],
- ['testimage'],
['testkineticscrolling'],
['testlist'],
['testlist2'],
diff --git a/tests/testdnd.c b/tests/testdnd.c
index dfc9bceac1..54df81423e 100644
--- a/tests/testdnd.c
+++ b/tests/testdnd.c
@@ -298,9 +298,9 @@ static const char *target_table[] = {
static guint n_targets = sizeof(target_table) / sizeof(target_table[0]);
void
-target_drag_leave (GtkWidget *widget,
- GdkDrop *drop,
- guint time)
+target_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkWidget *widget)
{
g_print("leave\n");
have_drag = FALSE;
@@ -308,13 +308,12 @@ target_drag_leave (GtkWidget *widget,
}
gboolean
-target_drag_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+target_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkWidget *widget)
{
- GtkWidget *source_widget;
- GdkDrag *drag;
char *s;
if (!have_drag)
@@ -323,12 +322,6 @@ target_drag_motion (GtkWidget *widget,
gtk_image_set_from_pixbuf (GTK_IMAGE (widget), trashcan_open);
}
- drag = gdk_drop_get_drag (drop);
- source_widget = drag ? gtk_drag_get_source_widget (drag) : NULL;
- g_print ("motion, source %s\n", source_widget ?
- G_OBJECT_TYPE_NAME (source_widget) :
- "NULL");
-
s = gdk_content_formats_to_string (gdk_drop_get_formats (drop));
g_print ("%s\n", s);
@@ -337,11 +330,29 @@ target_drag_motion (GtkWidget *widget,
return TRUE;
}
+static void
+got_text_in_target (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ char *str;
+
+ str = gdk_drop_read_text_finish (GDK_DROP (object), result, NULL);
+ if (str)
+ {
+ g_print ("Received \"%s\" in target\n", str);
+ g_free (str);
+ }
+
+ gdk_drop_finish (GDK_DROP (object), GDK_ACTION_MOVE);
+}
+
gboolean
-target_drag_drop (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+target_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkWidget *widget)
{
GdkContentFormats *formats;
const char *format;
@@ -355,10 +366,12 @@ target_drag_drop (GtkWidget *widget,
format = gdk_content_formats_match_mime_type (formats, formats);
if (format)
{
- gtk_drag_get_data (widget, drop, format);
+ gdk_drop_read_text_async (drop, NULL, got_text_in_target, widget);
return TRUE;
}
+ gdk_drop_status (drop, 0);
+
return FALSE;
}
@@ -381,55 +394,33 @@ action_make_unique (GdkDragAction action)
return 0;
}
-void
-target_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data)
+static void
+got_text (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
{
- if (gtk_selection_data_get_length (selection_data) >= 0 &&
- gtk_selection_data_get_format (selection_data) == 8)
+ char *str;
+
+ str = gdk_drop_read_text_finish (GDK_DROP (object), result, NULL);
+ if (str)
{
- GdkDragAction action = gdk_drop_get_actions (drop);
- g_print ("Received \"%s\" in trashcan\n", (gchar *) gtk_selection_data_get_data (selection_data));
- action = action_make_unique (action);
- gdk_drop_finish (drop, action);
- return;
+ g_print ("Received \"%s\" in label\n", str);
+ g_free (str);
}
-
- gdk_drop_finish (drop, 0);
}
-
+
void
-label_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data)
+label_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ GtkWidget *widget)
{
- if (gtk_selection_data_get_length (selection_data) >= 0 &&
- gtk_selection_data_get_format (selection_data) == 8)
- {
- GdkDragAction action = action_make_unique (gdk_drop_get_actions (drop));
- g_print ("Received \"%s\" in label\n", (gchar *) gtk_selection_data_get_data (selection_data));
- gdk_drop_finish (drop, action);
- return;
- }
-
- gdk_drop_finish (drop, 0);
+ gdk_drop_read_text_async (drop, NULL, got_text, widget);
+ GdkDragAction action = action_make_unique (gdk_drop_get_actions (drop));
+ gdk_drop_finish (drop, action);
}
-void
-source_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- gpointer data)
-{
- if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("application/x-rootwindow-drop"))
- g_print ("I was dropped on the rootwin\n");
- else
- gtk_selection_data_set (selection_data,
- gtk_selection_data_get_target (selection_data),
- 8, (guchar *) "I'm Data!", 9);
-}
-
/* The following is a rather elaborate example demonstrating/testing
* changing of the window hierarchy during a drag - in this case,
* via a "spring-loaded" popup window.
@@ -453,11 +444,17 @@ popdown_cb (gpointer data)
}
gboolean
-popup_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+popup_motion (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ gdk_drop_status (drop, GDK_ACTION_COPY);
+ return TRUE;
+}
+
+void
+popup_enter (GtkDropTarget *dest)
{
+g_print ("popup enter\n");
if (!in_popup)
{
in_popup = TRUE;
@@ -468,14 +465,12 @@ popup_motion (GtkWidget *widget,
popdown_timer = 0;
}
}
-
- return TRUE;
}
void
-popup_leave (GtkWidget *widget,
- GdkDrop *drop)
+popup_leave (GtkDropTarget *dest)
{
+g_print ("popup leave\n");
if (in_popup)
{
in_popup = FALSE;
@@ -487,6 +482,15 @@ popup_leave (GtkWidget *widget,
}
}
+static gboolean
+popup_drop (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ gdk_drop_finish (drop, GDK_ACTION_COPY);
+ popdown_cb (NULL);
+ return TRUE;
+}
+
gboolean
popup_cb (gpointer data)
{
@@ -502,26 +506,26 @@ popup_cb (gpointer data)
popup_window = gtk_window_new (GTK_WINDOW_POPUP);
grid = gtk_grid_new ();
- targets = gdk_content_formats_new (target_table, n_targets - 1); /* no rootwin */
+ targets = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
for (i=0; i<3; i++)
for (j=0; j<3; j++)
{
char buffer[128];
+ GtkDropTarget *dest;
+
g_snprintf(buffer, sizeof(buffer), "%d,%d", i, j);
button = gtk_button_new_with_label (buffer);
gtk_widget_set_hexpand (button, TRUE);
gtk_widget_set_vexpand (button, TRUE);
gtk_grid_attach (GTK_GRID (grid), button, i, j, 1, 1);
- gtk_drag_dest_set (button,
- GTK_DEST_DEFAULT_ALL,
- targets,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
- g_signal_connect (button, "drag-motion",
- G_CALLBACK (popup_motion), NULL);
- g_signal_connect (button, "drag-leave",
- G_CALLBACK (popup_leave), NULL);
+ dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (dest, "accept", G_CALLBACK (popup_motion), NULL);
+ g_signal_connect (dest, "drag-enter", G_CALLBACK (popup_enter), NULL);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (popup_leave), NULL);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (popup_drop), NULL);
+ gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (dest));
}
gtk_container_add (GTK_CONTAINER (popup_window), grid);
gdk_content_formats_unref (targets);
@@ -531,30 +535,33 @@ popup_cb (gpointer data)
popped_up = TRUE;
}
- popdown_timer = g_timeout_add (500, popdown_cb, NULL);
- g_print ("added popdown\n");
-
popup_timer = FALSE;
return FALSE;
}
gboolean
-popsite_motion (GtkWidget *widget,
- GdkDrop *drop,
- gint x,
- gint y)
+popsite_motion (GtkDropTarget *dest,
+ int x,
+ int y,
+ GtkWidget *widget)
+{
+ return TRUE;
+}
+
+void
+popsite_enter (GtkDropTarget *dest)
{
+g_print ("popsite enter\n");
if (!popup_timer)
popup_timer = g_timeout_add (500, popup_cb, NULL);
- return TRUE;
}
void
-popsite_leave (GtkWidget *widget,
- GdkDrop *drop)
+popsite_leave (GtkDropTarget *dest)
{
+g_print ("popsite leave\n");
if (popup_timer)
{
g_source_remove (popup_timer);
@@ -562,10 +569,9 @@ popsite_leave (GtkWidget *widget,
}
}
-void
-source_drag_data_delete (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
+void
+source_drag_data_delete (GtkWidget *widget,
+ gpointer data)
{
g_print ("Delete the data!\n");
}
@@ -587,7 +593,11 @@ main (int argc, char **argv)
GtkWidget *button;
GdkPixbuf *drag_icon;
GdkTexture *texture;
+ GdkContentProvider *content;
+ GValue value = G_VALUE_INIT;
+ GtkDragSource *source;
GdkContentFormats *targets;
+ GtkDropTarget *dest;
test_init ();
@@ -610,13 +620,9 @@ main (int argc, char **argv)
label = gtk_label_new ("Drop Here\n");
targets = gdk_content_formats_new (target_table, n_targets - 1); /* no rootwin */
- gtk_drag_dest_set (label,
- GTK_DEST_DEFAULT_ALL,
- targets,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
-
- g_signal_connect (label, "drag_data_received",
- G_CALLBACK( label_drag_data_received), NULL);
+ dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (label_drag_drop), NULL);
+ gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_set_vexpand (label, TRUE);
@@ -624,49 +630,46 @@ main (int argc, char **argv)
label = gtk_label_new ("Popup\n");
- gtk_drag_dest_set (label,
- GTK_DEST_DEFAULT_ALL,
- targets,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ dest = gtk_drop_target_new (targets, GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect (dest, "accept", G_CALLBACK (popsite_motion), NULL);
+ g_signal_connect (dest, "drag-enter", G_CALLBACK (popsite_enter), NULL);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (popsite_leave), NULL);
+ gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_set_vexpand (label, TRUE);
gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1);
- g_signal_connect (label, "drag-motion",
- G_CALLBACK (popsite_motion), NULL);
- g_signal_connect (label, "drag-leave",
- G_CALLBACK (popsite_leave), NULL);
gdk_content_formats_unref (targets);
pixmap = gtk_image_new_from_pixbuf (trashcan_closed);
- gtk_drag_dest_set (pixmap, 0, NULL, 0);
+ targets = gdk_content_formats_new (NULL, 0);
+ dest = gtk_drop_target_new (targets, 0);
+ g_signal_connect (dest, "drag-leave", G_CALLBACK (target_drag_leave), pixmap);
+ g_signal_connect (dest, "accept", G_CALLBACK (target_drag_motion), pixmap);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (target_drag_drop), pixmap);
+ gtk_widget_add_controller (pixmap, GTK_EVENT_CONTROLLER (dest));
+ gdk_content_formats_unref (targets);
+
gtk_widget_set_hexpand (pixmap, TRUE);
gtk_widget_set_vexpand (pixmap, TRUE);
gtk_grid_attach (GTK_GRID (grid), pixmap, 1, 0, 1, 1);
- g_signal_connect (pixmap, "drag-leave",
- G_CALLBACK (target_drag_leave), NULL);
-
- g_signal_connect (pixmap, "drag-motion",
- G_CALLBACK (target_drag_motion), NULL);
-
- g_signal_connect (pixmap, "drag-drop",
- G_CALLBACK (target_drag_drop), NULL);
-
- g_signal_connect (pixmap, "drag-data-received",
- G_CALLBACK (target_drag_data_received), NULL);
/* Drag site */
- button = gtk_button_new_with_label ("Drag Here\n");
+ button = gtk_label_new ("Drag Here\n");
- targets = gdk_content_formats_new (target_table, n_targets);
- gtk_drag_source_set (button, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
- targets,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
- gtk_drag_source_set_icon_paintable (button, GDK_PAINTABLE (texture));
- gdk_content_formats_unref (targets);
+ source = gtk_drag_source_new ();
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, "I'm data!");
+ content = gdk_content_provider_new_for_value (&value);
+ g_value_unset (&value);
+ gtk_drag_source_set_content (source, content);
+ g_object_unref (content);
+ gtk_drag_source_set_actions (source, GDK_ACTION_COPY|GDK_ACTION_MOVE);
+ gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (source));
+ gtk_drag_source_set_icon (source, GDK_PAINTABLE (texture), 0, 0);
g_object_unref (texture);
@@ -674,11 +677,6 @@ main (int argc, char **argv)
gtk_widget_set_vexpand (button, TRUE);
gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 1, 1);
- g_signal_connect (button, "drag-data-get",
- G_CALLBACK (source_drag_data_get), NULL);
- g_signal_connect (button, "drag-data-delete",
- G_CALLBACK (source_drag_data_delete), NULL);
-
gtk_widget_show (window);
gtk_main ();
diff --git a/tests/testdnd2.c b/tests/testdnd2.c
index 5505055d0e..ecf5a95551 100644
--- a/tests/testdnd2.c
+++ b/tests/testdnd2.c
@@ -1,3 +1,4 @@
+#include <unistd.h>
#include <gtk/gtk.h>
static GdkPaintable *
@@ -37,122 +38,6 @@ enum {
BOTTOM_RIGHT
};
-static void
-image_drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
-{
- GdkPaintable *paintable;
- gint hotspot;
- gint hot_x, hot_y;
- gint size;
-
- paintable = get_image_paintable (GTK_IMAGE (data), &size);
- hotspot = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (data), "hotspot"));
- switch (hotspot)
- {
- default:
- case TOP_LEFT:
- hot_x = 0;
- hot_y = 0;
- break;
- case CENTER:
- hot_x = size / 2;
- hot_y = size / 2;
- break;
- case BOTTOM_RIGHT:
- hot_x = size;
- hot_y = size;
- break;
- }
- gtk_drag_set_icon_paintable (drag, paintable, hot_x, hot_y);
- g_object_unref (paintable);
-}
-
-static void
-drag_widget_destroyed (GtkWidget *image, gpointer data)
-{
- GtkWidget *widget = data;
-
- g_print ("drag widget destroyed\n");
- g_object_unref (image);
- g_object_set_data (G_OBJECT (widget), "drag widget", NULL);
-}
-
-static void
-window_drag_end (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
-{
- GtkWidget *window = data;
-
- gtk_widget_destroy (window);
- g_signal_handlers_disconnect_by_func (widget, window_drag_end, data);
-}
-
-static void
-window_drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
-{
- GdkPaintable *paintable;
- GtkWidget *image;
- int hotspot;
- int size;
-
- hotspot = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (data), "hotspot"));
-
- image = g_object_get_data (G_OBJECT (widget), "drag widget");
- if (image == NULL)
- {
- g_print ("creating new drag widget\n");
- paintable = get_image_paintable (GTK_IMAGE (data), &size);
- image = gtk_image_new_from_paintable (paintable);
- g_object_unref (paintable);
- g_object_ref (image);
- g_object_set_data (G_OBJECT (widget), "drag widget", image);
- g_signal_connect (image, "destroy", G_CALLBACK (drag_widget_destroyed), widget);
- }
- else
- g_print ("reusing drag widget\n");
-
- gtk_drag_set_icon_widget (drag, image, 0, 0);
-
- if (hotspot == CENTER)
- g_signal_connect (widget, "drag-end", G_CALLBACK (window_drag_end), image);
-}
-
-static void
-update_source_target_list (GtkWidget *image)
-{
- GdkContentFormats *target_list;
-
- target_list = gdk_content_formats_new (NULL, 0);
-
- target_list = gtk_content_formats_add_image_targets (target_list, FALSE);
- if (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)
- target_list = gtk_content_formats_add_text_targets (target_list);
-
- gtk_drag_source_set_target_list (image, target_list);
-
- gdk_content_formats_unref (target_list);
-}
-
-static void
-update_dest_target_list (GtkWidget *image)
-{
- GdkContentFormats *target_list;
-
- target_list = gdk_content_formats_new (NULL, 0);
-
- target_list = gtk_content_formats_add_image_targets (target_list, FALSE);
- target_list = gtk_content_formats_add_text_targets (target_list);
-
- gtk_drag_dest_set_target_list (image, target_list);
-
- gdk_content_formats_unref (target_list);
-}
-
void
image_drag_data_get (GtkWidget *widget,
GdkDrag *drag,
@@ -186,154 +71,320 @@ image_drag_data_get (GtkWidget *widget,
}
static void
-image_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data,
- gpointer data)
+got_texture (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
- gchar *text;
-
- if (gtk_selection_data_get_length (selection_data) == 0)
- return;
+ GdkDrop *drop = GDK_DROP (source);
+ GtkWidget *image = data;
+ const GValue *value;
+ GError *error = NULL;
- if (gtk_selection_data_targets_include_image (selection_data, FALSE))
+ value = gdk_drop_read_value_finish (drop, result, &error);
+ if (value)
{
- GdkTexture *texture;
-
- texture = gtk_selection_data_get_texture (selection_data);
- gtk_image_set_from_paintable (GTK_IMAGE (data), GDK_PAINTABLE (texture));
-
- g_object_unref (texture);
+ GdkTexture *texture = g_value_get_object (value);
+ gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture));
+ gdk_drop_finish (drop, GDK_ACTION_COPY);
}
- else if (gtk_selection_data_targets_include_text (selection_data))
+ else
{
- text = (gchar *)gtk_selection_data_get_text (selection_data);
- gtk_image_set_from_icon_name (GTK_IMAGE (data), text);
- g_free (text);
+ g_error_free (error);
+ gdk_drop_finish (drop, 0);
}
+
+ g_object_set_data (G_OBJECT (image), "drop", NULL);
+}
+
+static void
+perform_drop (GdkDrop *drop,
+ GtkWidget *image)
+{
+ if (gdk_drop_has_value (drop, GDK_TYPE_TEXTURE))
+ gdk_drop_read_value_async (drop, GDK_TYPE_TEXTURE, G_PRIORITY_DEFAULT, NULL, got_texture, image);
else
{
- g_assert_not_reached ();
+ gdk_drop_finish (drop, 0);
+ g_object_set_data (G_OBJECT (image), "drop", NULL);
}
}
+static void
+do_copy (GtkWidget *button)
+{
+ GtkWidget *popover = gtk_widget_get_ancestor (button, GTK_TYPE_POPOVER);
+ GtkWidget *image = gtk_popover_get_relative_to (GTK_POPOVER (popover));
+ GdkDrop *drop = GDK_DROP (g_object_get_data (G_OBJECT (image), "drop"));
-GtkWidget *
-make_image (const gchar *icon_name, int hotspot)
+ gtk_popover_popdown (GTK_POPOVER (popover));
+ perform_drop (drop, image);
+}
+
+static void
+do_cancel (GtkWidget *button)
{
- GtkWidget *image;
+ GtkWidget *popover = gtk_widget_get_ancestor (button, GTK_TYPE_POPOVER);
+ GtkWidget *image = gtk_popover_get_relative_to (GTK_POPOVER (popover));
+ GdkDrop *drop = GDK_DROP (g_object_get_data (G_OBJECT (image), "drop"));
- image = gtk_image_new_from_icon_name (icon_name);
- gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
+ gtk_popover_popdown (GTK_POPOVER (popover));
+ gdk_drop_finish (drop, 0);
- gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, GDK_ACTION_COPY);
- update_source_target_list (image);
+ g_object_set_data (G_OBJECT (image), "drop", NULL);
+}
- g_object_set_data (G_OBJECT (image), "hotspot", GINT_TO_POINTER (hotspot));
+static void
+ask_actions (GdkDrop *drop,
+ GtkWidget *image)
+{
+ GtkWidget *popover, *box, *button;
- g_signal_connect (image, "drag-begin", G_CALLBACK (image_drag_begin), image);
- g_signal_connect (image, "drag-data-get", G_CALLBACK (image_drag_data_get), image);
+ popover = g_object_get_data (G_OBJECT (image), "popover");
+ if (!popover)
+ {
+ popover = gtk_popover_new (image);
+ g_object_set_data (G_OBJECT (image), "popover", popover);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (popover), box);
+ button = gtk_button_new_with_label ("Copy");
+ g_signal_connect (button, "clicked", G_CALLBACK (do_copy), NULL);
+ gtk_container_add (GTK_CONTAINER (box), button);
+ button = gtk_button_new_with_label ("Move");
+ g_signal_connect (button, "clicked", G_CALLBACK (do_copy), NULL);
+ gtk_container_add (GTK_CONTAINER (box), button);
+ button = gtk_button_new_with_label ("Cancel");
+ g_signal_connect (button, "clicked", G_CALLBACK (do_cancel), NULL);
+ gtk_container_add (GTK_CONTAINER (box), button);
+ }
+ gtk_popover_popup (GTK_POPOVER (popover));
+}
- gtk_drag_dest_set (image, GTK_DEST_DEFAULT_ALL, NULL, GDK_ACTION_COPY);
- g_signal_connect (image, "drag-data-received", G_CALLBACK (image_drag_data_received), image);
- update_dest_target_list (image);
+static gboolean
+delayed_deny (gpointer data)
+{
+ GtkDropTarget *dest = data;
+ GtkWidget *image = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ GdkDrop *drop = GDK_DROP (g_object_get_data (G_OBJECT (image), "drop"));
- return image;
+ if (drop)
+ {
+ g_print ("denying drop, late\n");
+ gtk_drop_target_deny_drop (dest, drop);
+ }
+
+ return G_SOURCE_REMOVE;
}
-GtkWidget *
-make_image2 (const gchar *icon_name, int hotspot)
+static gboolean
+image_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
+ gpointer data)
{
- GtkWidget *image;
+ GtkWidget *image = data;
+ g_object_set_data_full (G_OBJECT (image), "drop", g_object_ref (drop), g_object_unref);
- image = gtk_image_new_from_icon_name (icon_name);
- gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
+ g_timeout_add (1000, delayed_deny, dest);
- gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, GDK_ACTION_COPY);
- update_source_target_list (image);
+ gdk_drop_status (drop, gtk_drop_target_get_actions (dest));
- g_object_set_data (G_OBJECT (image), "hotspot", GINT_TO_POINTER (hotspot));
+ return TRUE;
+}
+
+static gboolean
+image_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ gpointer data)
+{
+ GtkWidget *image = data;
+ GdkDragAction action = gdk_drop_get_actions (drop);
- g_signal_connect (image, "drag-begin", G_CALLBACK (window_drag_begin), image);
- g_signal_connect (image, "drag-data-get", G_CALLBACK (image_drag_data_get), image);
+ g_object_set_data_full (G_OBJECT (image), "drop", g_object_ref (drop), g_object_unref);
- gtk_drag_dest_set (image, GTK_DEST_DEFAULT_ALL, NULL, GDK_ACTION_COPY);
- g_signal_connect (image, "drag-data-received", G_CALLBACK (image_drag_data_received), image);
- update_dest_target_list (image);
+ g_print ("drop, actions %d\n", action);
+ if (!gdk_drag_action_is_unique (action))
+ ask_actions (drop, image);
+ else
+ perform_drop (drop, image);
- return image;
+ return TRUE;
}
static void
-spinner_drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
+update_source_icon (GtkDragSource *source,
+ const char *icon_name,
+ int hotspot)
{
- GtkWidget *spinner;
+ GdkPaintable *paintable;
+ int hot_x, hot_y;
+ int size = 48;
+
+ paintable = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ icon_name, size, 0, NULL);
+ switch (hotspot)
+ {
+ default:
+ case TOP_LEFT:
+ hot_x = 0;
+ hot_y = 0;
+ break;
+ case CENTER:
+ hot_x = size / 2;
+ hot_y = size / 2;
+ break;
+ case BOTTOM_RIGHT:
+ hot_x = size;
+ hot_y = size;
+ break;
+ }
+ gtk_drag_source_set_icon (source, paintable, hot_x, hot_y);
+ g_object_unref (paintable);
+}
+
+static GBytes *
+get_data (const char *mimetype,
+ gpointer data)
+{
+ GtkWidget *image = data;
+ GdkContentFormats *formats;
+ gboolean want_text;
+
+ formats = gdk_content_formats_new_for_gtype (G_TYPE_STRING);
+ formats = gdk_content_formats_union_serialize_mime_types (formats);
+ want_text = gdk_content_formats_contain_mime_type (formats, mimetype);
+ gdk_content_formats_unref (formats);
- g_print ("GtkWidget::drag-begin\n");
- spinner = g_object_new (GTK_TYPE_SPINNER,
- "visible", TRUE,
- "active", TRUE,
- NULL);
- gtk_drag_set_icon_widget (drag, spinner, 0, 0);
- g_object_set_data (G_OBJECT (drag), "spinner", spinner);
+ if (want_text)
+ {
+ const char *text = gtk_image_get_icon_name (GTK_IMAGE (image));
+
+ return g_bytes_new (text, strlen (text) + 1);
+ }
+ else if (strcmp (mimetype, "image/png") == 0)
+ {
+ int size;
+ GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (image), &size);
+ if (GDK_IS_TEXTURE (paintable))
+ {
+ char *name = g_strdup ("drag-data-XXXXXX");
+ int fd;
+ char *data;
+ gsize size;
+
+ // FIXME: this is horrible
+
+ fd = g_mkstemp (name);
+ close (fd);
+
+ gdk_texture_save_to_png (GDK_TEXTURE (paintable), name);
+
+ g_file_get_contents (name, &data, &size, NULL);
+ g_free (name);
+
+ return g_bytes_new_take (data, size);
+ }
+
+ g_clear_object (&paintable);
+ }
+ return NULL;
}
static void
-spinner_drag_end (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
+drag_begin (GtkDragSource *source)
{
- GtkWidget *spinner;
+ g_print ("drag begin\n");
+}
- g_print ("GtkWidget::drag-end\n");
- spinner = g_object_get_data (G_OBJECT (drag), "spinner");
- gtk_widget_destroy (spinner);
+static void
+drag_end (GtkDragSource *source)
+{
+ g_print ("drag end\n");
}
static gboolean
-spinner_drag_failed (GtkWidget *widget,
- GdkDrag *drag,
- GtkDragResult result,
- gpointer data)
+drag_cancel (GtkDragSource *source,
+ GdkDrag *drag,
+ GdkDragCancelReason reason)
{
- GTypeClass *class;
- GEnumValue *value;
+ g_print ("drag failed: %d\n", reason);
+ return FALSE;
+}
- class = g_type_class_ref (GTK_TYPE_DRAG_RESULT);
- value = g_enum_get_value (G_ENUM_CLASS (class), result);
- g_print ("GtkWidget::drag-failed %s\n", value->value_nick);
- g_type_class_unref (class);
+GtkWidget *
+make_image (const gchar *icon_name, int hotspot)
+{
+ GtkWidget *image;
+ GtkDragSource *source;
+ GtkDropTarget *dest;
+ GdkContentFormats *formats;
+ GdkContentFormatsBuilder *builder;
+ GdkContentProvider *content;
- return FALSE;
+ image = gtk_image_new_from_icon_name (icon_name);
+ gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
+
+ builder = gdk_content_formats_builder_new ();
+ gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_TEXTURE);
+ gdk_content_formats_builder_add_gtype (builder, G_TYPE_STRING);
+ formats = gdk_content_formats_builder_free_to_formats (builder);
+
+ content = gdk_content_provider_new_with_formats (formats, get_data, image);
+ source = gtk_drag_source_new ();
+ gtk_drag_source_set_content (source, content);
+ gtk_drag_source_set_actions (source, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK);
+ g_object_unref (content);
+ update_source_icon (source, icon_name, hotspot);
+
+ g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), NULL);
+ g_signal_connect (source, "drag-end", G_CALLBACK (drag_end), NULL);
+ g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
+
+ dest = gtk_drop_target_new (formats, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK);
+ g_signal_connect (dest, "accept", G_CALLBACK (image_drag_motion), image);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (image_drag_drop), image);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
+
+ gdk_content_formats_unref (formats);
+
+ return image;
}
-void
-spinner_drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- gpointer data)
+static void
+spinner_drag_begin (GtkDragSource *source,
+ GdkDrag *drag,
+ GtkWidget *widget)
{
- g_print ("GtkWidget::drag-data-get\n");
- gtk_selection_data_set_text (selection_data, "ACTIVE", -1);
+ GdkPaintable *paintable;
+
+ paintable = gtk_widget_paintable_new (widget);
+ gtk_drag_source_set_icon (source, paintable, 0, 0);
+ g_object_unref (paintable);
}
static GtkWidget *
make_spinner (void)
{
GtkWidget *spinner;
+ GtkDragSource *source;
+ GdkContentProvider *content;
+ GValue value = G_VALUE_INIT;
spinner = gtk_spinner_new ();
gtk_spinner_start (GTK_SPINNER (spinner));
- gtk_drag_source_set (spinner, GDK_BUTTON1_MASK, NULL, GDK_ACTION_COPY);
- gtk_drag_source_add_text_targets (spinner);
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, "ACTIVE");
+ content = gdk_content_provider_new_for_value (&value);
+ source = gtk_drag_source_new ();
+ gtk_drag_source_set_content (source, content);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (spinner_drag_begin), spinner);
+ gtk_widget_add_controller (spinner, GTK_EVENT_CONTROLLER (source));
- g_signal_connect (spinner, "drag-begin", G_CALLBACK (spinner_drag_begin), spinner);
- g_signal_connect (spinner, "drag-end", G_CALLBACK (spinner_drag_end), spinner);
- g_signal_connect (spinner, "drag-failed", G_CALLBACK (spinner_drag_failed), spinner);
- g_signal_connect (spinner, "drag-data-get", G_CALLBACK (spinner_drag_data_get), spinner);
+ g_object_unref (content);
+ g_value_unset (&value);
return spinner;
}
@@ -367,9 +418,9 @@ main (int argc, char *Argv[])
gtk_grid_attach (GTK_GRID (grid), make_spinner (), 0, 2, 1, 1);
gtk_grid_attach (GTK_GRID (grid), make_image ("weather-clear", CENTER), 1, 2, 1, 1);
- gtk_grid_attach (GTK_GRID (grid), make_image2 ("dialog-question", TOP_LEFT), 0, 3, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), make_image ("dialog-question", TOP_LEFT), 0, 3, 1, 1);
- gtk_grid_attach (GTK_GRID (grid), make_image2 ("dialog-information", CENTER), 1, 3, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), make_image ("dialog-information", CENTER), 1, 3, 1, 1);
gtk_widget_show (window);
gtk_main ();
diff --git a/tests/testdnd3.c b/tests/testdnd3.c
new file mode 100644
index 0000000000..f9315569fc
--- /dev/null
+++ b/tests/testdnd3.c
@@ -0,0 +1,398 @@
+#include <gtk/gtk.h>
+
+static GdkContentProvider *
+prepare (GtkDragSource *source, double x, double y)
+{
+ GtkWidget *canvas;
+ GtkWidget *item;
+ GdkContentProvider *provider;
+ GBytes *bytes;
+
+ canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
+ item = gtk_widget_pick (canvas, x, y, GTK_PICK_DEFAULT);
+
+ if (!GTK_IS_LABEL (item))
+ return NULL;
+
+ bytes = g_bytes_new (&item, sizeof (gpointer));
+ provider = gdk_content_provider_new_for_bytes ("CANVAS_ITEM", bytes);
+ g_bytes_unref (bytes);
+
+ g_object_set_data (G_OBJECT (canvas), "dragged-item", item);
+
+ return provider;
+}
+
+static void
+drag_begin (GtkDragSource *source, GdkDrag *drag)
+{
+ GtkWidget *canvas;
+ GtkWidget *item;
+
+ canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
+ item = g_object_get_data (G_OBJECT (canvas), "dragged-item");
+
+ gtk_widget_set_opacity (item, 0.5);
+}
+
+static void
+drag_end (GtkDragSource *source, GdkDrag *drag)
+{
+ GtkWidget *canvas;
+ GtkWidget *item;
+
+ canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source));
+ item = g_object_get_data (G_OBJECT (canvas), "dragged-item");
+ g_object_set_data (G_OBJECT (canvas), "dragged-item", NULL);
+
+ gtk_widget_set_opacity (item, 1.0);
+}
+
+typedef struct {
+ GtkWidget *canvas;
+ double x;
+ double y;
+} DropData;
+
+typedef struct {
+ double x, y;
+ double angle;
+ double delta;
+} TransformData;
+
+static void
+apply_transform (GtkWidget *item)
+{
+ GtkWidget *canvas = gtk_widget_get_parent (item);
+ TransformData *data;
+ GskTransform *transform;
+
+ data = g_object_get_data (G_OBJECT (item), "transform-data");
+ transform = gsk_transform_rotate (gsk_transform_translate (NULL, &(graphene_point_t){data->x, data->y}),
+ data->angle + data->delta);
+ gtk_fixed_set_child_transform (GTK_FIXED (canvas), item, transform);
+ gsk_transform_unref (transform);
+}
+
+static void
+got_data (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GdkDrop *drop = GDK_DROP (source);
+ DropData *data = user_data;
+ GInputStream *stream;
+ GBytes *bytes;
+ GtkWidget *item;
+ const char *mime_type;
+ GError *error = NULL;
+ TransformData *transform_data;
+ GtkWidget *canvas;
+ GtkWidget *last_child;
+
+ stream = gdk_drop_read_finish (drop, result, &mime_type, &error);
+ bytes = g_input_stream_read_bytes (stream, sizeof (gpointer), NULL, NULL);
+ item = (gpointer) *(gpointer *)g_bytes_get_data (bytes, NULL);
+
+ transform_data = g_object_get_data (G_OBJECT (item), "transform-data");
+
+ transform_data->x = data->x;
+ transform_data->y = data->y;
+
+ canvas = gtk_widget_get_parent (item);
+ last_child = gtk_widget_get_last_child (canvas);
+ if (item != last_child)
+ gtk_widget_insert_after (item, canvas, last_child);
+
+ apply_transform (item);
+
+ gdk_drop_finish (drop, GDK_ACTION_MOVE);
+
+ g_bytes_unref (bytes);
+ g_object_unref (stream);
+ g_free (data);
+}
+
+static gboolean
+drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y)
+{
+ DropData *data;
+
+ data = g_new (DropData, 1);
+ data->canvas = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ data->x = x;
+ data->y = y;
+
+ gdk_drop_read_async (drop, (const char *[]){"CANVAS_ITEM", NULL}, G_PRIORITY_DEFAULT, NULL, got_data, data);
+
+ return TRUE;
+}
+
+static GtkWidget *
+canvas_new (void)
+{
+ GtkWidget *canvas;
+ GtkDragSource *source;
+ GtkDropTarget *dest;
+ GdkContentFormats *formats;
+
+ canvas = gtk_fixed_new ();
+ gtk_widget_set_hexpand (canvas, TRUE);
+ gtk_widget_set_vexpand (canvas, TRUE);
+ gtk_style_context_add_class (gtk_widget_get_style_context (canvas), "frame");
+
+ source = gtk_drag_source_new ();
+ gtk_drag_source_set_actions (source, GDK_ACTION_MOVE);
+ g_signal_connect (source, "prepare", G_CALLBACK (prepare), NULL);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), NULL);
+ g_signal_connect (source, "drag-end", G_CALLBACK (drag_end), NULL);
+ gtk_widget_add_controller (canvas, GTK_EVENT_CONTROLLER (source));
+
+ formats = gdk_content_formats_new ((const char *[]){"CANVAS_ITEM", NULL}, 1);
+ dest = gtk_drop_target_new (formats, GDK_ACTION_MOVE);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), NULL);
+ gtk_widget_add_controller (canvas, GTK_EVENT_CONTROLLER (dest));
+ gdk_content_formats_unref (formats);
+
+ return canvas;
+}
+
+static void
+set_color (GtkWidget *item,
+ GdkRGBA *color)
+{
+ char *css;
+ char *str;
+ GtkStyleContext *context;
+ GtkCssProvider *provider;
+
+ str = gdk_rgba_to_string (color);
+ css = g_strdup_printf ("* { background: %s; padding: 10px; }", str);
+
+ context = gtk_widget_get_style_context (item);
+ provider = g_object_get_data (G_OBJECT (context), "style-provider");
+ if (provider)
+ gtk_style_context_remove_provider (context, GTK_STYLE_PROVIDER (provider));
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_data (provider, css, -1);
+ gtk_style_context_add_provider (gtk_widget_get_style_context (item), GTK_STYLE_PROVIDER (provider), 800);
+ g_object_set_data_full (G_OBJECT (context), "style-provider", provider, g_object_unref);
+
+ g_free (str);
+ g_free (css);
+}
+
+static void
+got_color (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GdkDrop *drop = GDK_DROP (source);
+ GtkDropTarget *dest = data;
+ GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
+ const GValue *value;
+ GdkRGBA *color;
+
+ value = gdk_drop_read_value_finish (drop, result, NULL);
+ color = g_value_get_boxed (value);
+ set_color (item, color);
+
+ gdk_drop_finish (drop, GDK_ACTION_COPY);
+}
+
+static gboolean
+item_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y)
+{
+ if (gtk_drop_target_find_mimetype (dest))
+ {
+ gdk_drop_read_value_async (drop, GDK_TYPE_RGBA, G_PRIORITY_DEFAULT, NULL, got_color, dest);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+item_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop)
+{
+ if (gtk_drop_target_find_mimetype (dest) != NULL)
+ {
+ gdk_drop_status (drop, GDK_ACTION_COPY);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+angle_changed (GtkGestureRotate *gesture,
+ double angle,
+ double delta)
+{
+ GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+ TransformData *data = g_object_get_data (G_OBJECT (item), "transform-data");
+
+ data->delta = angle / M_PI * 180.0;
+
+ apply_transform (item);
+}
+
+static void
+rotate_done (GtkGesture *gesture)
+{
+ GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+ TransformData *data = g_object_get_data (G_OBJECT (item), "transform-data");
+
+ data->angle = data->angle + data->delta;
+ data->delta = 0;
+}
+
+static void
+click_done (GtkGesture *gesture)
+{
+ GtkWidget *item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+ GtkWidget *canvas = gtk_widget_get_parent (item);
+ GtkWidget *last_child;
+
+ last_child = gtk_widget_get_last_child (canvas);
+ if (item != last_child)
+ gtk_widget_insert_after (item, canvas, last_child);
+}
+
+static GtkWidget *
+canvas_item_new (int i,
+ double x,
+ double y,
+ double angle)
+{
+ GtkWidget *widget;
+ char *label;
+ char *id;
+ TransformData *transform_data;
+ GdkRGBA rgba;
+ GtkDropTarget *dest;
+ GdkContentFormats *formats;
+ GtkGesture *gesture;
+
+ label = g_strdup_printf ("Item %d", i);
+ id = g_strdup_printf ("item%d", i);
+
+ gdk_rgba_parse (&rgba, "yellow");
+
+ widget = gtk_label_new (label);
+ gtk_style_context_add_class (gtk_widget_get_style_context (widget), "frame");
+ gtk_widget_set_name (widget, id);
+
+ set_color (widget, &rgba);
+ transform_data = g_new0 (TransformData, 1);
+ transform_data->x = x;
+ transform_data->y = y;
+ transform_data->angle = angle;
+ g_object_set_data_full (G_OBJECT (widget), "transform-data", transform_data, g_free);
+
+ g_free (label);
+ g_free (id);
+
+ formats = gdk_content_formats_new_for_gtype (GDK_TYPE_RGBA);
+ dest = gtk_drop_target_new (formats, GDK_ACTION_COPY);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (item_drag_drop), NULL);
+ g_signal_connect (dest, "accept", G_CALLBACK (item_drag_motion), NULL);
+ gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (dest));
+ gdk_content_formats_unref (formats);
+
+ gesture = gtk_gesture_rotate_new ();
+ g_signal_connect (gesture, "angle-changed", G_CALLBACK (angle_changed), NULL);
+ g_signal_connect (gesture, "end", G_CALLBACK (rotate_done), NULL);
+ gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
+
+ gesture = gtk_gesture_click_new ();
+ g_signal_connect (gesture, "released", G_CALLBACK (click_done), NULL);
+ gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
+
+ return widget;
+}
+
+int main (int argc, char *argv[])
+{
+ GtkWidget *window;
+ GtkWidget *sw;
+ GtkWidget *canvas;
+ GtkWidget *widget;
+ GtkWidget *box, *box2, *box3;
+ const char *colors[] = {
+ "red", "green", "blue", "magenta", "orange", "gray", "black", "yellow",
+ "white", "gray", "brown", "pink", "cyan", "bisque", "gold", "maroon",
+ "navy", "orchid", "olive", "peru", "salmon", "silver", "wheat",
+ NULL
+ };
+ int i;
+ int x, y;
+
+ gtk_init ();
+
+ widget = gtk_color_button_new ();
+ gtk_widget_destroy (widget);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (window), box);
+
+ box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_container_add (GTK_CONTAINER (box), box2);
+
+ canvas = canvas_new ();
+ gtk_container_add (GTK_CONTAINER (box2), canvas);
+
+ x = y = 40;
+ for (i = 0; i < 4; i++)
+ {
+ GtkWidget *item;
+
+ item = canvas_item_new (i, x, y, 0);
+ gtk_container_add (GTK_CONTAINER (canvas), item);
+ apply_transform (item);
+
+ x += 150;
+ y += 100;
+ }
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_NEVER);
+ gtk_container_add (GTK_CONTAINER (box), sw);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_style_context_add_class (gtk_widget_get_style_context (box3), "linked");
+ gtk_container_add (GTK_CONTAINER (sw), box3);
+
+ for (i = 0; colors[i]; i++)
+ {
+ GdkRGBA rgba;
+ GtkWidget *swatch;
+
+ gdk_rgba_parse (&rgba, colors[i]);
+
+ swatch = g_object_new (g_type_from_name ("GtkColorSwatch"),
+ "rgba", &rgba,
+ "selectable", FALSE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box3), swatch);
+ }
+
+ gtk_widget_show (window);
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/tests/testentryicons.c b/tests/testentryicons.c
index a3c92f7857..e117579d2e 100644
--- a/tests/testentryicons.c
+++ b/tests/testentryicons.c
@@ -9,45 +9,6 @@ clear_pressed (GtkEntry *entry, gint icon, gpointer data)
}
static void
-drag_begin_cb (GtkWidget *widget,
- GdkDrag *drag,
- gpointer user_data)
-{
- gint pos;
-
- pos = gtk_entry_get_current_icon_drag_source (GTK_ENTRY (widget));
- if (pos != -1)
- gtk_drag_set_icon_name (drag, "dialog-information", 2, 2);
-}
-
-static void
-drag_data_get_cb (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *data,
- gpointer user_data)
-{
- gint pos;
-
- pos = gtk_entry_get_current_icon_drag_source (GTK_ENTRY (widget));
-
- if (pos == GTK_ENTRY_ICON_PRIMARY)
- {
- gint start, end;
-
- if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
- {
- gchar *str;
-
- str = gtk_editable_get_chars (GTK_EDITABLE (widget), start, end);
- gtk_selection_data_set_text (data, str, -1);
- g_free (str);
- }
- else
- gtk_selection_data_set_text (data, "XXX", -1);
- }
-}
-
-static void
set_blank (GtkWidget *button,
GtkEntry *entry)
{
@@ -127,7 +88,8 @@ main (int argc, char **argv)
GtkWidget *button3;
GtkWidget *button4;
GIcon *icon;
- GdkContentFormats *tlist;
+ GdkContentProvider *content;
+ GValue value = G_VALUE_INIT;
gtk_init ();
@@ -190,16 +152,15 @@ main (int argc, char **argv)
gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry),
GTK_ENTRY_ICON_PRIMARY,
"Save a file");
- tlist = gdk_content_formats_new (NULL, 0);
- tlist = gtk_content_formats_add_text_targets (tlist);
+
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, "Amazing");
+ content = gdk_content_provider_new_for_value (&value);
+ g_value_unset (&value);
gtk_entry_set_icon_drag_source (GTK_ENTRY (entry),
GTK_ENTRY_ICON_PRIMARY,
- tlist, GDK_ACTION_COPY);
- g_signal_connect_after (entry, "drag-begin",
- G_CALLBACK (drag_begin_cb), NULL);
- g_signal_connect (entry, "drag-data-get",
- G_CALLBACK (drag_data_get_cb), NULL);
- gdk_content_formats_unref (tlist);
+ content, GDK_ACTION_COPY);
+ g_object_unref (content);
/*
* Search - Uses a helper function
diff --git a/tests/testimage.c b/tests/testimage.c
deleted file mode 100644
index 455d5c03fa..0000000000
--- a/tests/testimage.c
+++ /dev/null
@@ -1,178 +0,0 @@
-/* testimage.c
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * 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 <gtk/gtk.h>
-#include <gio/gio.h>
-
-static void
-drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
-{
- GtkWidget *image = GTK_WIDGET (data);
- GdkPaintable *paintable;
-
- paintable = gtk_image_get_paintable (GTK_IMAGE (image));
- gtk_drag_set_icon_paintable (drag, paintable, -2, -2);
-}
-
-void
-drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- gpointer data)
-{
- GtkWidget *image = GTK_WIDGET (data);
- GdkPaintable *paintable;
-
- paintable = gtk_image_get_paintable (GTK_IMAGE (image));
- if (GDK_IS_TEXTURE (paintable))
- gtk_selection_data_set_texture (selection_data, GDK_TEXTURE (paintable));
-}
-
-static void
-drag_data_received (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- guint info,
- guint32 time,
- gpointer data)
-{
- GtkWidget *image = GTK_WIDGET (data);
- GdkTexture *texture;
-
- if (gtk_selection_data_get_length (selection_data) < 0)
- return;
-
- texture = gtk_selection_data_get_texture (selection_data);
- gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture));
-
- g_object_unref (texture);
-}
-
-static gboolean
-idle_func (gpointer data)
-{
- g_print ("keep me busy\n");
-
- return G_SOURCE_CONTINUE;
-}
-
-int
-main (int argc, char **argv)
-{
- GtkWidget *window, *grid;
- GtkWidget *label, *image;
- GtkIconTheme *theme;
- GdkPaintable *paintable;
- gchar *icon_name = "help-browser";
- gchar *anim_filename = NULL;
- GtkIconInfo *icon_info;
- GIcon *icon;
- GFile *file;
-
- gtk_init ();
-
- if (argc > 1)
- icon_name = argv[1];
-
- if (argc > 2)
- anim_filename = argv[2];
-
- window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
- grid = gtk_grid_new ();
- gtk_grid_set_row_spacing (GTK_GRID (grid), 10);
- gtk_grid_set_column_spacing (GTK_GRID (grid), 10);
- gtk_container_add (GTK_CONTAINER (window), grid);
-
- label = gtk_label_new ("symbolic size");
- gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
- label = gtk_label_new ("fixed size");
- gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
-
- label = gtk_label_new ("GTK_IMAGE_PIXBUF");
- gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
-
- theme = gtk_icon_theme_get_default ();
- icon_info = gtk_icon_theme_lookup_icon_for_scale (theme, icon_name, 48, gtk_widget_get_scale_factor (window), GTK_ICON_LOOKUP_GENERIC_FALLBACK);
- paintable = gtk_icon_info_load_icon (icon_info, NULL);
- g_object_unref (icon_info);
- image = gtk_image_new_from_paintable (paintable);
- g_object_unref (paintable);
- gtk_grid_attach (GTK_GRID (grid), image, 2, 1, 1, 1);
-
- gtk_drag_source_set (image, GDK_BUTTON1_MASK,
- NULL,
- GDK_ACTION_COPY);
- gtk_drag_source_add_image_targets (image);
- g_signal_connect (image, "drag_begin", G_CALLBACK (drag_begin), image);
- g_signal_connect (image, "drag_data_get", G_CALLBACK (drag_data_get), image);
-
- gtk_drag_dest_set (image,
- GTK_DEST_DEFAULT_MOTION |
- GTK_DEST_DEFAULT_HIGHLIGHT |
- GTK_DEST_DEFAULT_DROP,
- NULL, GDK_ACTION_COPY);
- gtk_drag_dest_add_image_targets (image);
- g_signal_connect (image, "drag_data_received",
- G_CALLBACK (drag_data_received), image);
-
- label = gtk_label_new ("GTK_IMAGE_ICON_NAME");
- gtk_grid_attach (GTK_GRID (grid), label, 0, 4, 1, 1);
- image = gtk_image_new_from_icon_name (icon_name);
- gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
- gtk_grid_attach (GTK_GRID (grid), image, 1, 4, 1, 1);
- image = gtk_image_new_from_icon_name (icon_name);
- gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
- gtk_image_set_pixel_size (GTK_IMAGE (image), 30);
- gtk_grid_attach (GTK_GRID (grid), image, 2, 4, 1, 1);
-
- label = gtk_label_new ("GTK_IMAGE_GICON");
- gtk_grid_attach (GTK_GRID (grid), label, 0, 5, 1, 1);
- icon = g_themed_icon_new_with_default_fallbacks ("folder-remote");
- image = gtk_image_new_from_gicon (icon);
- gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
- g_object_unref (icon);
- gtk_grid_attach (GTK_GRID (grid), image, 1, 5, 1, 1);
- file = g_file_new_for_path ("apple-red.png");
- icon = g_file_icon_new (file);
- image = gtk_image_new_from_gicon (icon);
- gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
- g_object_unref (icon);
- gtk_image_set_pixel_size (GTK_IMAGE (image), 30);
- gtk_grid_attach (GTK_GRID (grid), image, 2, 5, 1, 1);
-
- if (anim_filename)
- {
- label = gtk_label_new ("GTK_IMAGE_ANIMATION (from file)");
- gtk_grid_attach (GTK_GRID (grid), label, 0, 6, 1, 1);
- image = gtk_image_new_from_file (anim_filename);
- gtk_image_set_pixel_size (GTK_IMAGE (image), 30);
- gtk_grid_attach (GTK_GRID (grid), image, 2, 6, 1, 1);
-
- /* produce high load */
- g_idle_add_full (G_PRIORITY_DEFAULT,
- idle_func, NULL, NULL);
- }
-
- gtk_widget_show (window);
-
- gtk_main ();
-
- return 0;
-}
diff --git a/tests/testlist3.c b/tests/testlist3.c
index 2f6aba8450..59120cc6bb 100644
--- a/tests/testlist3.c
+++ b/tests/testlist3.c
@@ -5,9 +5,9 @@ static const char *entries[] = {
};
static void
-drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
+drag_begin (GtkDragSource *source,
+ GdkDrag *drag,
+ GtkWidget *widget)
{
GtkWidget *row;
GtkAllocation alloc;
@@ -19,43 +19,31 @@ drag_begin (GtkWidget *widget,
paintable = gtk_widget_paintable_new (row);
gtk_widget_translate_coordinates (widget, row, 0, 0, &x, &y);
- gtk_drag_set_icon_paintable (drag, paintable, -x, -y);
+ gtk_drag_source_set_icon (source, paintable, -x, -y);
g_object_unref (paintable);
}
-
-void
-drag_data_get (GtkWidget *widget,
- GdkDrag *drag,
- GtkSelectionData *selection_data,
- gpointer data)
-{
- gtk_selection_data_set (selection_data,
- g_intern_static_string ("GTK_LIST_BOX_ROW"),
- 32,
- (const guchar *)&widget,
- sizeof (gpointer));
-}
-
-
static void
-drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selection_data,
- gpointer data)
+got_row (GObject *src,
+ GAsyncResult *result,
+ gpointer data)
{
- GtkWidget *target;
+ GtkDropTarget *dest = GTK_DROP_TARGET (src);
+ GtkWidget *target = data;
GtkWidget *row;
GtkWidget *source;
int pos;
+ GtkSelectionData *selection_data;
- target = widget;
+ selection_data = gtk_drop_target_read_selection_finish (dest, result, NULL);
pos = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (target));
row = (gpointer)* (gpointer*)gtk_selection_data_get_data (selection_data);
source = gtk_widget_get_ancestor (row, GTK_TYPE_LIST_BOX_ROW);
+ gtk_selection_data_free (selection_data);
+
if (source == target)
return;
@@ -65,11 +53,25 @@ drag_data_received (GtkWidget *widget,
g_object_unref (source);
}
+static void
+drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ gpointer data)
+{
+ gtk_drop_target_read_selection (dest, "GTK_LIST_BOX_ROW", NULL, got_row, data);
+}
+
static GtkWidget *
create_row (const gchar *text)
{
GtkWidget *row, *box, *label, *image;
+ GBytes *bytes;
+ GdkContentProvider *content;
GdkContentFormats *targets;
+ GtkDragSource *source;
+ GtkDropTarget *dest;
row = gtk_list_box_row_new ();
image = gtk_image_new_from_icon_name ("open-menu-symbolic");
@@ -81,14 +83,18 @@ create_row (const gchar *text)
gtk_container_add (GTK_CONTAINER (box), label);
gtk_container_add (GTK_CONTAINER (box), image);
- targets = gdk_content_formats_new (entries, 1);
-
- gtk_drag_source_set (image, GDK_BUTTON1_MASK, targets, GDK_ACTION_MOVE);
- g_signal_connect (image, "drag-begin", G_CALLBACK (drag_begin), NULL);
- g_signal_connect (image, "drag-data-get", G_CALLBACK (drag_data_get), NULL);
+ bytes = g_bytes_new (&row, sizeof (gpointer));
+ content = gdk_content_provider_new_for_bytes ("GTK_LIST_BOX_ROW", bytes);
+ source = gtk_drag_source_new ();
+ gtk_drag_source_set_content (source, content);
+ gtk_drag_source_set_actions (source, GDK_ACTION_MOVE);
+ g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
+ gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
- gtk_drag_dest_set (row, GTK_DEST_DEFAULT_ALL, targets, GDK_ACTION_MOVE);
- g_signal_connect (row, "drag-data-received", G_CALLBACK (drag_data_received), NULL);
+ targets = gdk_content_formats_new (entries, 1);
+ dest = gtk_drop_target_new (targets, GDK_ACTION_MOVE);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), row);
+ gtk_widget_add_controller (GTK_WIDGET (row), GTK_EVENT_CONTROLLER (dest));
gdk_content_formats_unref (targets);
diff --git a/tests/testnotebookdnd.c b/tests/testnotebookdnd.c
index 543b8186f3..fe99dfc21d 100644
--- a/tests/testnotebookdnd.c
+++ b/tests/testnotebookdnd.c
@@ -90,26 +90,11 @@ on_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint page_num, gpoi
g_print ("page %d reordered\n", page_num);
}
-static void
-on_notebook_drag_begin (GtkWidget *widget,
- GdkDrag *drag,
- gpointer data)
-{
- guint page_num;
-
- page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (widget));
-
- if (page_num > 2)
- gtk_drag_set_icon_name (drag,
- (page_num % 2) ? "help-browser" : "process-stop",
- 0, 0);
-}
-
static gboolean
remove_in_idle (gpointer data)
{
GtkWidget *child = data;
- GtkWidget *parent = gtk_widget_get_parent (child);
+ GtkWidget *parent = gtk_widget_get_ancestor (child, GTK_TYPE_NOTEBOOK);
GtkWidget *tab_label;
tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (parent), child);
@@ -120,16 +105,45 @@ remove_in_idle (gpointer data)
}
static void
-on_button_drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *data,
- gpointer user_data)
+got_page (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GdkDrop *drop = GDK_DROP (source);
+ GInputStream *stream;
+ const char *mime_type;
+
+ stream = gdk_drop_read_finish (drop, result, &mime_type, NULL);
+
+ if (stream)
+ {
+ GBytes *bytes;
+ GtkWidget **child;
+
+ bytes = g_input_stream_read_bytes (stream, sizeof (gpointer), NULL, NULL);
+ child = (gpointer)g_bytes_get_data (bytes, NULL);
+
+ g_idle_add (remove_in_idle, *child);
+
+ gdk_drop_finish (drop, GDK_ACTION_MOVE);
+
+ g_bytes_unref (bytes);
+ g_object_unref (stream);
+ }
+ else
+ gdk_drop_finish (drop, 0);
+}
+
+static gboolean
+on_button_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ gpointer user_data)
{
- GtkWidget **child;
+ gdk_drop_read_async (drop, (const char *[]) { "GTK_NOTEBOOK_TAB", NULL }, G_PRIORITY_DEFAULT, NULL, got_page, NULL);
- child = (void*) gtk_selection_data_get_data (data);
+ gdk_drop_finish (drop, GDK_ACTION_MOVE);
- g_idle_add (remove_in_idle, *child);
+ return TRUE;
}
static void
@@ -186,8 +200,6 @@ create_notebook (gchar **labels,
g_signal_connect (GTK_NOTEBOOK (notebook), "page-reordered",
G_CALLBACK (on_page_reordered), NULL);
- g_signal_connect_after (G_OBJECT (notebook), "drag-begin",
- G_CALLBACK (on_notebook_drag_begin), NULL);
return notebook;
}
@@ -216,12 +228,18 @@ create_notebook_non_dragable_content (gchar **labels,
while (*labels)
{
GtkWidget *button;
- button = gtk_button_new_with_label (*labels);
+ button = gtk_button_new_with_label ("example content");
/* Use GtkListBox since it bubbles up motion notify event, which can
* experience more issues than GtkBox. */
page = gtk_list_box_new ();
gtk_container_add (GTK_CONTAINER (page), button);
+ button = gtk_button_new_with_label ("row 2");
+ gtk_container_add (GTK_CONTAINER (page), button);
+
+ button = gtk_button_new_with_label ("third row");
+ gtk_container_add (GTK_CONTAINER (page), button);
+
title = gtk_label_new (*labels);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, title);
@@ -233,8 +251,6 @@ create_notebook_non_dragable_content (gchar **labels,
g_signal_connect (GTK_NOTEBOOK (notebook), "page-reordered",
G_CALLBACK (on_page_reordered), NULL);
- g_signal_connect_after (G_OBJECT (notebook), "drag-begin",
- G_CALLBACK (on_notebook_drag_begin), NULL);
return notebook;
}
@@ -271,8 +287,6 @@ create_notebook_with_notebooks (gchar **labels,
g_signal_connect (GTK_NOTEBOOK (notebook), "page-reordered",
G_CALLBACK (on_page_reordered), NULL);
- g_signal_connect_after (G_OBJECT (notebook), "drag-begin",
- G_CALLBACK (on_notebook_drag_begin), NULL);
return notebook;
}
@@ -281,18 +295,16 @@ create_trash_button (void)
{
GdkContentFormats *targets;
GtkWidget *button;
+ GtkDropTarget *dest;
button = gtk_button_new_with_mnemonic ("_Delete");
targets = gdk_content_formats_new (button_targets, G_N_ELEMENTS (button_targets));
- gtk_drag_dest_set (button,
- GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
- targets,
- GDK_ACTION_MOVE);
+ dest = gtk_drop_target_new (targets, GDK_ACTION_MOVE);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (on_button_drag_drop), NULL);
+ gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (dest));
gdk_content_formats_unref (targets);
- g_signal_connect_after (G_OBJECT (button), "drag-data-received",
- G_CALLBACK (on_button_drag_data_received), NULL);
return button;
}
diff --git a/tests/testtoolbar.c b/tests/testtoolbar.c
index a26f8a842b..458ca2e21c 100644
--- a/tests/testtoolbar.c
+++ b/tests/testtoolbar.c
@@ -293,11 +293,12 @@ bold_toggled (GtkToggleToolButton *button)
}
static gboolean
-toolbar_drag_drop (GtkWidget *widget,
- GdkDrop *drop,
+toolbar_drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
gint x, gint y,
GtkWidget *label)
{
+ GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
gchar buf[32];
g_snprintf(buf, sizeof(buf), "%d",
@@ -323,12 +324,11 @@ rtl_toggled (GtkCheckButton *check)
static GtkToolItem *drag_item = NULL;
static gboolean
-toolbar_drag_motion (GtkToolbar *toolbar,
- GdkDrop *drop,
+toolbar_drag_motion (GtkDropTarget *dest,
+ GdkDrop *drop,
gint x,
gint y,
- guint time,
- gpointer null)
+ GtkToolbar *toolbar)
{
gint index;
@@ -348,9 +348,9 @@ toolbar_drag_motion (GtkToolbar *toolbar,
}
static void
-toolbar_drag_leave (GtkToolbar *toolbar,
- GdkDrop *drop,
- gpointer null)
+toolbar_drag_leave (GtkDropTarget *dest,
+ GdkDrop *drop,
+ GtkToolbar *toolbar)
{
if (drag_item)
{
@@ -389,6 +389,9 @@ main (gint argc, gchar **argv)
GtkWidget *hbox, *hbox1, *hbox2, *checkbox, *option_menu, *menu;
gint i;
GdkContentFormats *targets;
+ GdkContentProvider *content;
+ GtkDragSource *source;
+ GtkDropTarget *dest;
static const gchar *toolbar_styles[] = { "icons", "text", "both (vertical)",
"both (horizontal)" };
GtkToolItem *item;
@@ -616,19 +619,18 @@ main (gint argc, gchar **argv)
gtk_container_add (GTK_CONTAINER (hbox), checkbox);
targets = gdk_content_formats_new (target_table, G_N_ELEMENTS (target_table));
- gtk_drag_source_set (button, GDK_BUTTON1_MASK,
- targets,
- GDK_ACTION_MOVE);
- gtk_drag_dest_set (toolbar, GTK_DEST_DEFAULT_DROP,
- targets,
- GDK_ACTION_MOVE);
+ content = gdk_content_provider_new_for_bytes (target_table[0], g_bytes_new ("", 1));
+ source = gtk_drag_source_new ();
+ gtk_drag_source_set_content (source, content);
+ gtk_drag_source_set_actions (source, GDK_ACTION_MOVE);
+ g_object_unref (content);
+ gtk_widget_add_controller (button, GTK_EVENT_CONTROLLER (source));
+ dest = gtk_drop_target_new (targets, GDK_ACTION_MOVE);
+ g_signal_connect (dest, "drag_motion", G_CALLBACK (toolbar_drag_motion), toolbar);
+ g_signal_connect (dest, "drag_leave", G_CALLBACK (toolbar_drag_leave), toolbar);
+ g_signal_connect (dest, "drag_drop", G_CALLBACK (toolbar_drag_drop), label);
+ gtk_widget_add_controller (toolbar, GTK_EVENT_CONTROLLER (dest));
gdk_content_formats_unref (targets);
- g_signal_connect (toolbar, "drag_motion",
- G_CALLBACK (toolbar_drag_motion), NULL);
- g_signal_connect (toolbar, "drag_leave",
- G_CALLBACK (toolbar_drag_leave), NULL);
- g_signal_connect (toolbar, "drag_drop",
- G_CALLBACK (toolbar_drag_drop), label);
gtk_widget_show (window);
diff --git a/tests/testtreednd.c b/tests/testtreednd.c
index 5cf423c9bf..306e87ad45 100644
--- a/tests/testtreednd.c
+++ b/tests/testtreednd.c
@@ -89,16 +89,32 @@ get_dragsource (void)
}
static void
-drag_data_received (GtkWidget *widget,
- GdkDrop *drop,
- GtkSelectionData *selda,
- gpointer dada)
+got_text (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
+ GtkDropTarget *dest = GTK_DROP_TARGET (source);
+ GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (dest));
gchar *text;
+ GtkSelectionData *selda;
+ selda = gtk_drop_target_read_selection_finish (dest, result, NULL);
+
text = (gchar*) gtk_selection_data_get_text (selda);
gtk_label_set_label (GTK_LABEL (widget), text);
g_free (text);
+
+ gtk_selection_data_free (selda);
+}
+
+static void
+drag_drop (GtkDropTarget *dest,
+ GdkDrop *drop,
+ int x,
+ int y,
+ gpointer dada)
+{
+ gtk_drop_target_read_selection (dest, "text/plain", NULL, got_text, dada);
}
static GtkWidget *
@@ -106,11 +122,13 @@ get_droptarget (void)
{
GtkWidget *label;
GdkContentFormats *targets;
+ GtkDropTarget *dest;
label = gtk_label_new ("Drop here");
targets = gdk_content_formats_new (entries, G_N_ELEMENTS (entries));
- gtk_drag_dest_set (label, GTK_DEST_DEFAULT_ALL, targets, GDK_ACTION_COPY);
- g_signal_connect (label, "drag-data-received", G_CALLBACK (drag_data_received), NULL);
+ dest = gtk_drop_target_new (targets, GDK_ACTION_COPY);
+ g_signal_connect (dest, "drag-drop", G_CALLBACK (drag_drop), NULL);
+ gtk_widget_add_controller (label, GTK_EVENT_CONTROLLER (dest));
gdk_content_formats_unref (targets);
return label;