diff options
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)) { @@ -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; |