Merge branch 'dnd-gestures-2' into 'master'

Add new dnd api

See merge request GNOME/gtk!1278
This commit is contained in:
Matthias Clasen 2020-01-11 06:09:58 +00:00
commit f5daecf353
83 changed files with 5727 additions and 5637 deletions

View File

@ -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)
{
GdkTexture *texture;
GdkDrop *drop = GDK_DROP (source);
GtkWidget *image = data;
const GValue *value;
GError *error = NULL;
texture = gtk_selection_data_get_texture (selection_data);
gtk_image_set_from_paintable (GTK_IMAGE (data), GDK_PAINTABLE (texture));
g_object_unref (texture);
value = gdk_drop_read_value_finish (drop, result, &error);
if (value)
{
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);
}
}
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 ();

View File

@ -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 void
get_image_data (GtkWidget *widget,
GdkDrag *drag,
GtkSelectionData *selection,
guint target_info,
gpointer data)
static GdkPaintable *
get_image_paintable (GtkImage *image)
{
GtkWidget *image;
const gchar *name;
gint size;
GdkPixbuf *pixbuf;
const gchar *icon_name;
GtkIconTheme *icon_theme;
GtkIconInfo *icon_info;
int size;
image = gtk_bin_get_child (GTK_BIN (widget));
name = gtk_image_get_icon_name (GTK_IMAGE (image));
size = gtk_image_get_pixel_size (GTK_IMAGE (image));
pixbuf = get_icon (image, name, size);
gtk_selection_data_set_pixbuf (selection, pixbuf);
g_object_unref (pixbuf);
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_scalable_image_data (GtkWidget *widget,
GdkDrag *drag,
GtkSelectionData *selection,
guint target_info,
gpointer data)
drag_begin (GtkDragSource *source,
GdkDrag *drag,
GtkWidget *widget)
{
gchar *uris[2];
GtkIconInfo *info;
GtkWidget *image;
GFile *file;
const gchar *name;
GdkPaintable *paintable;
image = gtk_bin_get_child (GTK_BIN (widget));
name = gtk_image_get_icon_name (GTK_IMAGE (image));
paintable = get_image_paintable (GTK_IMAGE (widget));
if (paintable)
{
int w, h;
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);
}
}
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_file (GValue *value,
gpointer data)
{
const char *name;
GtkIconInfo *info;
GFile *file;
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);

View File

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

View File

@ -5001,49 +5001,6 @@ GTK_TYPE_SELECTION_DATA
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>
@ -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>

View File

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

View File

@ -96,7 +96,6 @@ private_headers = [
'gtkcsswidgetnodeprivate.h',
'gtkcsswin32sizevalueprivate.h',
'gtkdialogprivate.h',
'gtkdndprivate.h',
'gtkentryprivate.h',
'gtkeventcontrollerlegacyprivate.h',
'gtkeventcontrollerprivate.h',

View File

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

View File

@ -24,6 +24,7 @@
#include "gdkcontentformats.h"
#include "filetransferportalprivate.h"
#include "gdktexture.h"
#include "gdkrgbaprivate.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
@ -848,6 +849,63 @@ file_uri_deserializer (GdkContentDeserializer *deserializer)
g_object_unref (output);
}
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)
{
@ -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);
}

View File

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

View File

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

View File

@ -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>
@ -862,6 +863,48 @@ file_text_serializer (GdkContentSerializer *serializer)
gdk_content_serializer_set_task_data (serializer, path, g_free);
}
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)
{
@ -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);
}

View File

@ -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.
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
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);
priv->in_drag = 0;
gdk_content_formats_unref (targets);
gtk_drag_set_icon_default (drag);
}
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 doesnt result from a drop.
@ -2953,87 +2962,33 @@ get_status_pending (GdkDrop *drop)
}
static void
gtk_calendar_drag_leave (GtkWidget *widget,
GdkDrop *drop)
gtk_calendar_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkCalendar *calendar)
{
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)
{
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 *

View File

@ -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);
gtk_drag_source_set_icon (source, paintable, 0, 0);
g_object_unref (paintable);
}
static void
gtk_color_button_drag_begin (GtkWidget *widget,
GdkDrag *drag,
gpointer data)
get_rgba_value (GValue *value,
gpointer data)
{
GtkColorButton *button = data;
GtkColorButtonPrivate *priv = gtk_color_button_get_instance_private (button);
set_color_icon (drag, &priv->rgba);
}
static void
gtk_color_button_drag_data_get (GtkWidget *widget,
GdkDrag *drag,
GtkSelectionData *selection_data,
GtkColorButton *button)
{
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");

View File

@ -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);
}
static void
swatch_drag_data_received (GtkWidget *widget,
GdkDrop *drop,
GtkSelectionData *selection_data)
{
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)
value = gdk_drop_read_value_finish (drop, result, NULL);
if (value)
{
g_warning ("Received invalid color data");
return;
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 gboolean
swatch_drag_drop (GtkDropTarget *dest,
GdkDrop *drop,
int x,
int y,
GtkColorSwatch *swatch)
{
if (gdk_drop_has_value (drop, GDK_TYPE_RGBA))
{
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

File diff suppressed because it is too large Load Diff

View File

@ -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__ */

View File

@ -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__ */

File diff suppressed because it is too large Load Diff

View File

@ -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 widgets 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 widgets 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);
GDK_AVAILABLE_IN_ALL
void gtk_drag_dest_set_target_list (GtkWidget *widget,
GdkContentFormats *target_list);
GDK_AVAILABLE_IN_ALL
void gtk_drag_dest_add_text_targets (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_drag_dest_add_image_targets (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_drag_dest_add_uri_targets (GtkWidget *widget);
GdkContentFormats *gtk_drop_target_get_formats (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
void gtk_drag_dest_set_track_motion (GtkWidget *widget,
gboolean track_motion);
void gtk_drop_target_set_actions (GtkDropTarget *dest,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
gboolean gtk_drag_dest_get_track_motion (GtkWidget *widget);
GdkDragAction gtk_drop_target_get_actions (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
GdkDrop *gtk_drop_target_get_drop (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
const char *gtk_drop_target_find_mimetype (GtkDropTarget *dest);
GDK_AVAILABLE_IN_ALL
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
void gtk_drop_target_deny_drop (GtkDropTarget *dest,
GdkDrop *drop);
G_END_DECLS

32
gtk/gtkdragdestprivate.h Normal file
View File

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

View File

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

51
gtk/gtkdragicon.h Normal file
View File

@ -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__ */

View File

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

View File

@ -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.
*/
typedef struct _GtkDragSourceSite GtkDragSourceSite;
struct _GtkDragSourceSite
struct _GtkDragSource
{
GdkModifierType start_button_mask;
GdkContentFormats *target_list; /* Targets for drag data */
GdkDragAction actions; /* Possible actions */
GtkGestureSingle parent_instance;
GtkImageDefinition *image_def;
GtkGesture *drag_gesture;
GdkContentProvider *content;
GdkDragAction actions;
GdkPaintable *paintable;
int hot_x;
int hot_y;
gdouble start_x;
gdouble start_y;
GdkDrag *drag;
};
static void
gtk_drag_source_gesture_begin (GtkGesture *gesture,
GdkEventSequence *sequence,
gpointer data)
struct _GtkDragSourceClass
{
GtkDragSourceSite *site = data;
guint button;
GtkGestureSingleClass parent_class;
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));
GdkContentProvider *(* prepare) (GtkDragSource *source,
double x,
double y);
};
g_assert (button >= 1);
enum {
PROP_CONTENT = 1,
PROP_ACTIONS,
NUM_PROPERTIES
};
if (!site->start_button_mask ||
!(site->start_button_mask & (GDK_BUTTON1_MASK << (button - 1))))
gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
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_gesture_update (GtkGesture *gesture,
GdkEventSequence *sequence,
gpointer data)
gtk_drag_source_finalize (GObject *object)
{
gdouble start_x, start_y, offset_x, offset_y;
GtkDragSourceSite *site = data;
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_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
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;
}
return GTK_EVENT_CONTROLLER_CLASS (gtk_drag_source_parent_class)->filter_event (controller, event);
}
static void
gtk_drag_source_begin (GtkGesture *gesture,
GdkEventSequence *sequence)
{
GtkDragSource *source = GTK_DRAG_SOURCE (gesture);
GdkEventSequence *current;
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_update (GtkGesture *gesture,
GdkEventSequence *sequence)
{
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)
{
GtkDragSourceSite *site = data;
GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (class);
GtkGestureClass *gesture_class = GTK_GESTURE_CLASS (class);
if (site->target_list)
gdk_content_formats_unref (site->target_list);
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;
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);
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);
}
/**
* 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 GdkContentProvider *
gtk_drag_source_prepare (GtkDragSource *source,
double x,
double y)
{
GtkDragSourceSite *site;
if (source->actions == 0)
return NULL;
g_return_if_fail (GTK_IS_WIDGET (widget));
if (source->content == NULL)
return NULL;
site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
return g_object_ref (source->content);
}
if (site)
static void
drag_end (GtkDragSource *source,
gboolean success)
{
gboolean delete_data;
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);
delete_data = success && gdk_drag_get_selected_action (source->drag) == GDK_ACTION_MOVE;
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)
{
if (site->target_list)
gdk_content_formats_unref (site->target_list);
}
else
{
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_DRAG_SOURCE (source), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (widget), 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 thats 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
*
* 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().
* Sets a content provider on a #GtkDragSource.
*
* 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_DRAG_SOURCE (source));
g_return_if_fail (GTK_IS_WIDGET (widget));
if (!g_set_object (&source->content, content))
return;
site = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
if (site == NULL)
g_object_notify_by_pspec (G_OBJECT (source), properties[PROP_CONTENT]);
}
/**
* gtk_drag_source_get_actions:
* @source: a #GtkDragSource
*
* Gets the actions that are currently set on the #GtkDragSource.
*
* Returns: the actions set on @source
*/
GdkDragAction
gtk_drag_source_get_actions (GtkDragSource *source)
{
g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), 0);
return source->actions;
}
/**
* gtk_drag_source_set_actions:
* @source: a #GtkDragSource
* @actions: the actions to offer
*
* Sets the actions on the #GtkDragSource.
*
* 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_set_actions (GtkDragSource *source,
GdkDragAction actions)
{
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_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.
*
* 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_set_icon (GtkDragSource *source,
GdkPaintable *paintable,
int hot_x,
int hot_y)
{
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_get_drag:
* @source: a #GtkDragSource
*
* Returns the underlying #GtkDrag object for an ongoing drag.
*
* Returns: (nullable): the #GdkDrag of the current drag operation, or %NULL
*/
GdkDrag *
gtk_drag_source_get_drag (GtkDragSource *source)
{
g_return_val_if_fail (GTK_IS_DRAG_SOURCE (source), NULL);
return source->drag;
}
/**
* gtk_drag_source_drag_cancel:
* @source: a #GtkDragSource
*
* Cancels a currently ongoing drag operation.
*/
void
gtk_drag_source_drag_cancel (GtkDragSource *source)
{
g_return_if_fail (GTK_IS_DRAG_SOURCE (source));
if (source->drag)
{
g_warning ("gtk_drag_source_set_target_list() requires the widget "
"to already be a drag source.");
return;
gboolean success = FALSE;
g_signal_emit (source, signals[DRAG_CANCEL], 0, source->drag, GDK_DRAG_CANCEL_ERROR, &success);
drag_end (source, FALSE);
}
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;
}
/**
* gtk_drag_source_add_text_targets: (method)
* @widget: a #GtkWidget thats is a drag source
*
* 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().
*/
void
gtk_drag_source_add_text_targets (GtkWidget *widget)
{
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);
}
/**
* gtk_drag_source_add_image_targets: (method)
* @widget: a #GtkWidget thats is a drag source
*
* 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().
*/
void
gtk_drag_source_add_image_targets (GtkWidget *widget)
{
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);
}
/**
* gtk_drag_source_add_uri_targets: (method)
* @widget: a #GtkWidget thats is a drag source
*
* 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().
*/
void
gtk_drag_source_add_uri_targets (GtkWidget *widget)
{
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);
}
/**
* gtk_drag_source_set_icon_name: (method)
* gtk_drag_check_threshold: (method)
* @widget: a #GtkWidget
* @icon_name: name of icon to use
*
* 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.
*/
void
gtk_drag_source_set_icon_name (GtkWidget *widget,
const gchar *icon_name)
{
GtkDragSourceSite *site;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (icon_name != 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);
}
/**
* gtk_drag_source_set_icon_gicon: (method)
* @widget: a #GtkWidget
* @icon: A #GIcon
* @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
*
* Sets the icon that will be used for drags from a particular source
* to @icon. See the docs for #GtkIconTheme for more details.
*/
void
gtk_drag_source_set_icon_gicon (GtkWidget *widget,
GIcon *icon)
{
GtkDragSourceSite *site;
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);
gtk_image_definition_unref (site->image_def);
site->image_def = gtk_image_definition_new_gicon (icon);
}
/**
* gtk_drag_source_set_icon_paintable: (method)
* @widget: a #GtkWidget
* @paintable: A #GdkPaintable
* 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);
}

View File

@ -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);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_set_target_list (GtkWidget *widget,
GdkContentFormats *target_list);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_add_text_targets (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_add_image_targets (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_add_uri_targets (GtkWidget *widget);
GtkDragSource *gtk_drag_source_new (void);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_set_icon_name (GtkWidget *widget,
const gchar *icon_name);
void gtk_drag_source_set_content (GtkDragSource *source,
GdkContentProvider *content);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_set_icon_gicon (GtkWidget *widget,
GIcon *icon);
GdkContentProvider *gtk_drag_source_get_content (GtkDragSource *source);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_set_icon_paintable (GtkWidget *widget,
GdkPaintable *paintable);
void gtk_drag_source_set_actions (GtkDragSource *source,
GdkDragAction actions);
GDK_AVAILABLE_IN_ALL
GdkDragAction gtk_drag_source_get_actions (GtkDragSource *source);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_set_icon (GtkDragSource *source,
GdkPaintable *paintable,
int hot_x,
int hot_y);
GDK_AVAILABLE_IN_ALL
void gtk_drag_source_drag_cancel (GtkDragSource *sourcei);
GDK_AVAILABLE_IN_ALL
GdkDrag * gtk_drag_source_get_drag (GtkDragSource *source);
GDK_AVAILABLE_IN_ALL
gboolean gtk_drag_check_threshold (GtkWidget *widget,
int start_x,
int start_y,
int current_x,
int current_y);
G_END_DECLS

View File

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

View File

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

View File

@ -693,31 +693,6 @@ typedef enum
GTK_TREE_VIEW_GRID_LINES_BOTH
} 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

View File

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

View File

@ -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))
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)
{
gchar **uris;
struct DndSelectFolderData *info;
GFile *file;
uris = gtk_selection_data_get_uris (data);
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);
}
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

View File

@ -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;
selection_data = gtk_drop_target_read_selection_finish (dest, result, NULL);
/* 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;
}
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");
const char *target = g_intern_static_string ("text/uri-list");
gtk_drop_target_read_selection (dest, target, NULL, file_list_drag_data_received_cb, impl);
return TRUE;
}
static void
file_list_drag_begin_cb (GtkWidget *widget,
GdkDrag *drag,
GtkFileChooserWidget *impl)
{
GtkFileChooserWidgetPrivate *priv = gtk_file_chooser_widget_get_instance_private (impl);
gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (priv->places_sidebar),
TRUE,
drag);
}
/* 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 files 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

View File

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

View File

@ -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,14 +6052,35 @@ 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);
g_object_unref (content);
g_signal_connect (drag, "dnd-finished", G_CALLBACK (gtk_icon_view_dnd_finished_cb), icon_view);
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;
icon = gtk_icon_view_create_drag_icon (icon_view, path);
gtk_drag_icon_set_from_paintable (drag, icon, x, y);
g_object_unref (icon);
icon_view->priv->drag = drag;
g_object_unref (drag);
out:
if (path)
@ -6092,71 +6090,28 @@ gtk_icon_view_maybe_begin_drag (GtkIconView *icon_view,
}
/* Source side drag signals */
static void
gtk_icon_view_drag_begin (GtkWidget *widget,
GdkDrag *drag)
static GBytes *
gtk_icon_view_drag_data_get (const char *mime_type,
gpointer data)
{
GtkIconView *icon_view;
GtkIconViewItem *item;
GdkPaintable *icon;
gint x, y;
GtkTreePath *path;
icon_view = GTK_ICON_VIEW (widget);
/* 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;
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);
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_set_icon_paintable (drag, icon, x, y);
g_object_unref (icon);
}
static void
gtk_icon_view_drag_end (GtkWidget *widget,
GdkDrag *drag)
{
/* do nothing */
}
static void
gtk_icon_view_drag_data_get (GtkWidget *widget,
GdkDrag *drag,
GtkSelectionData *selection_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);

View File

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

View File

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

View File

@ -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);
gtk_drag_icon_set_from_paintable (drag, get_selection_paintable (label), 0, 0);
g_object_unref (drag);
info->in_drag = FALSE;
gdk_content_formats_unref (target_list);
}
}
else
@ -5140,47 +5121,6 @@ gtk_label_get_selectable (GtkLabel *label)
return priv->select_info && priv->select_info->selectable;
}
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,

View File

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

View File

@ -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,17 +2754,10 @@ 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)
{

View File

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

View File

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

View File

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

View File

@ -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,
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_leave (GtkWidget *widget,
GdkDrop *drop);
static gboolean gtk_notebook_drag_drop (GtkWidget *widget,
int x,
int y);
static void gtk_notebook_drag_leave (GtkDropTarget *dest);
static gboolean gtk_notebook_drag_drop (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,
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 gboolean
gtk_notebook_drag_drop (GtkWidget *widget,
GdkDrop *drop,
gint x,
gint y)
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 (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, };
target = gtk_selection_data_get_target (data);
if (target == g_intern_static_string ("GTK_NOTEBOOK_TAB"))
sdata.target = g_intern_string (mime_type);
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);

View File

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

View File

@ -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);
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);
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);
sidebar->drag_row = NULL;
sidebar->row_placeholder = NULL;
sidebar->dragging_over = FALSE;

View File

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

View File

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

View File

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

544
gtk/gtksearchlistmodel.c Normal file
View File

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

50
gtk/gtksearchlistmodel.h Normal file
View File

@ -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__ */

View File

@ -143,130 +143,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]))

View File

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

View File

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

View File

@ -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().
@ -2803,6 +2799,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,
@ -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;
target_list = gtk_content_formats_add_text_targets (target_list);
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);
gtk_drag_begin (widget,
gdk_event_get_device ((GdkEvent*) event),
target_list, actions,
priv->drag_start_x + ranges[0],
priv->drag_start_y);
if (priv->editable)
actions = GDK_ACTION_COPY|GDK_ACTION_MOVE;
else
actions = GDK_ACTION_COPY;
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);
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;
GtkWidget *widget = GTK_WIDGET (self);
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);
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);
gtk_selection_data_set_text (selection_data, str, -1);
g_free (str);
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;
}
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

View File

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

View File

@ -453,6 +453,10 @@ gboolean gtk_text_buffer_delete_selection (GtkTextBuffer *buffer,
gboolean interactive,
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

View File

@ -27,6 +27,8 @@
G_BEGIN_DECLS
#include "gtktextbuffer.h"
/* This is a private uninstalled header shared between
* GtkTextView and GtkEntry
*/

View File

@ -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;
if (gdk_drag_get_selected_action (drag) == GDK_ACTION_MOVE)
gtk_text_buffer_delete_selection (self->priv->buffer, TRUE, self->priv->editable);
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 (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);
}
static void
gtk_text_view_drag_begin (GtkWidget *widget,
GdkDrag *drag)
{
/* do nothing */
}
static void
gtk_text_view_drag_end (GtkWidget *widget,
GdkDrag *drag)
{
GtkTextView *text_view;
text_view = GTK_TEXT_VIEW (widget);
text_view->priv->dnd_x = text_view->priv->dnd_y = -1;
}
static void
gtk_text_view_drag_data_get (GtkWidget *widget,
GdkDrag *drag,
GtkSelectionData *selection_data)
{
GtkTextView *text_view = GTK_TEXT_VIEW (widget);
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;
if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
{
gtk_selection_data_set (selection_data,
g_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
8, /* bytes */
(void*)&buffer,
sizeof (buffer));
}
if (text_view->priv->editable)
actions = GDK_ACTION_COPY | GDK_ACTION_MOVE;
else
actions = GDK_ACTION_COPY;
content = gtk_text_buffer_get_selection_content (buffer);
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);
g_signal_connect (drag, "dnd-finished", G_CALLBACK (dnd_finished_cb), text_view);
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
{
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);
}
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);
}
text_view->priv->drag = drag;
g_object_unref (drag);
}
static void
gtk_text_view_drag_data_delete (GtkWidget *widget,
GdkDrag *drag)
gtk_text_view_drag_leave (GtkDropTarget *dest,
GdkDrop *drop,
GtkTextView *text_view)
{
gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->priv->buffer,
TRUE, GTK_TEXT_VIEW (widget)->priv->editable);
}
static void
gtk_text_view_drag_leave (GtkWidget *widget,
GdkDrop *drop)
{
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);
action = gtk_text_view_get_action (text_view, drop);
gtk_text_buffer_begin_user_action (buffer);
if (!gtk_text_buffer_insert_interactive (buffer,
&drop_point, (gchar *) str, -1,
text_view->priv->editable))
gtk_widget_error_bell (GTK_WIDGET (text_view));
g_free (str);
gtk_text_buffer_get_iter_at_mark (buffer, &drop_point, priv->dnd_mark);
gtk_text_buffer_place_cursor (buffer, &drop_point);
gtk_text_buffer_end_user_action (buffer);
gdk_drop_finish (drop, action);
}
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 (priv->scroll_timeout != 0)
g_source_remove (priv->scroll_timeout);
priv->scroll_timeout = 0;
gtk_text_mark_set_visible (priv->dnd_mark, FALSE);
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;
action = gtk_text_view_get_action (widget, drop);
if (action == 0)
if (gtk_text_view_get_action (text_view, drop) == 0)
goto done;
gtk_text_buffer_begin_user_action (buffer);
if (gtk_selection_data_get_target (selection_data) == g_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
if (gdk_drop_has_value (drop, G_TYPE_STRING))
{
GtkTextBuffer *src_buffer = NULL;
GtkTextIter start, end;
gboolean copy_tags = TRUE;
if (gtk_selection_data_get_length (selection_data) != sizeof (src_buffer))
return;
memcpy (&src_buffer, gtk_selection_data_get_data (selection_data), sizeof (src_buffer));
if (src_buffer == NULL)
return;
g_return_if_fail (GTK_IS_TEXT_BUFFER (src_buffer));
if (gtk_text_buffer_get_tag_table (src_buffer) !=
gtk_text_buffer_get_tag_table (buffer))
{
copy_tags = FALSE;
}
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;
str = gtk_text_iter_get_visible_text (&start, &end);
gtk_text_buffer_insert_interactive (buffer,
&drop_point, str, -1,
priv->editable);
g_free (str);
}
}
gdk_drop_read_text_async (drop, NULL, got_text, text_view);
return TRUE;
}
else
insert_text_data (text_view, &drop_point, selection_data);
done:
gdk_drop_finish (drop, action);
if (action)
{
gtk_text_buffer_get_iter_at_mark (buffer,
&drop_point,
priv->dnd_mark);
gtk_text_buffer_place_cursor (buffer, &drop_point);
gtk_text_buffer_end_user_action (buffer);
}
done:
gdk_drop_finish (drop, 0);
return TRUE;
}
static void
gtk_text_view_set_hadjustment (GtkTextView *text_view,
GtkAdjustment *adjustment)

View File

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

View File

@ -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)
{
case GTK_TREE_VIEW_DROP_BEFORE:
gtk_style_context_add_class (context, "before");
break;
case GTK_TREE_VIEW_DROP_AFTER:
gtk_style_context_add_class (context, "after");
break;
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
gtk_style_context_add_class (context, "into");
break;
default:
break;
}
_gtk_tree_view_find_node (tree_view, drag_dest_path, &drag_tree, &drag_node);
if (drag_tree != NULL)
{
TreeViewDragInfo *di;
di = get_info (tree_view);
/* Draw indicator for the drop
*/
switch (tree_view->drag_dest_pos)
{
case GTK_TREE_VIEW_DROP_BEFORE:
gtk_css_node_set_classes (di->cssnode, (const char *[]){"before", NULL});
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_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);
set_source_row (drag, model, path);
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;
g_object_unref (drag);
di->source_item = gtk_tree_row_reference_new (model, path);
out:
if (path)
@ -7130,121 +7136,21 @@ gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view)
return retval;
}
static void
gtk_tree_view_drag_begin (GtkWidget *widget,
GdkDrag *drag)
{
GtkTreeView *tree_view;
GtkTreePath *path = NULL;
gint cell_x, cell_y;
GdkPaintable *row_pix;
TreeViewDragInfo *di;
tree_view = GTK_TREE_VIEW (widget);
/* if the user uses a custom DND source impl, we don't set the icon here */
di = get_info (tree_view);
if (di == NULL || !di->source_set)
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)
return;
row_pix = gtk_tree_view_create_row_drag_icon (tree_view, path);
gtk_drag_set_icon_paintable (drag, row_pix, tree_view->press_start_x + 1, 1);
g_object_unref (row_pix);
gtk_tree_path_free (path);
}
static void
gtk_tree_view_drag_end (GtkWidget *widget,
GdkDrag *drag)
gtk_tree_view_dnd_finished_cb (GdkDrag *drag,
GtkWidget *widget)
{
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
TreeViewDragInfo *di;
GtkTreeModel *model;
GtkTreePath *source_row;
tree_view->event_last_x = -10000;
tree_view->event_last_y = -10000;
}
/* Default signal implementations for the drag signals */
static void
gtk_tree_view_drag_data_get (GtkWidget *widget,
GdkDrag *drag,
GtkSelectionData *selection_data)
{
GtkTreeView *tree_view;
GtkTreeModel *model;
TreeViewDragInfo *di;
GtkTreePath *source_row;
tree_view = GTK_TREE_VIEW (widget);
model = gtk_tree_view_get_model (tree_view);
if (model == NULL)
if (gdk_drag_get_selected_action (drag) != GDK_ACTION_MOVE)
return;
di = get_info (GTK_TREE_VIEW (widget));
if (di == NULL)
return;
source_row = get_source_row (drag);
if (source_row == NULL)
return;
/* We can implement the GTK_TREE_MODEL_ROW target generically for
* any model; for DragSource models there are some other targets
* we also support.
*/
if (GTK_IS_TREE_DRAG_SOURCE (model) &&
gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
source_row,
selection_data))
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);
}
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);
@ -7253,10 +7159,10 @@ gtk_tree_view_drag_data_delete (GtkWidget *widget,
di = get_info (tree_view);
if (di == NULL)
if (di == NULL || di->source_item == NULL)
return;
source_row = get_source_row (drag);
source_row = gtk_tree_row_reference_get_path (di->source_item);
if (source_row == NULL)
return;
@ -7265,45 +7171,97 @@ gtk_tree_view_drag_data_delete (GtkWidget *widget,
gtk_tree_path_free (source_row);
set_source_row (drag, NULL, NULL);
g_clear_pointer (&di->source_item, gtk_tree_row_reference_free);
}
/* Default signal implementations for the drag signals */
static GBytes *
gtk_tree_view_drag_data_get (const char *mime_type,
gpointer data)
{
GtkTreeView *tree_view = data;
GtkTreeModel *model;
TreeViewDragInfo *di;
GtkTreePath *source_row;
GtkSelectionData sdata = { 0, };
sdata.target = g_intern_string (mime_type);
model = gtk_tree_view_get_model (tree_view);
if (model == NULL)
return NULL;
di = get_info (tree_view);
if (di == NULL || di->source_item == NULL)
return NULL;
source_row = gtk_tree_row_reference_get_path (di->source_item);
if (source_row == NULL)
return NULL;
/* We can implement the GTK_TREE_MODEL_ROW target generically for
* any model; for DragSource models there are some other targets
* we also support.
*/
if (GTK_IS_TREE_DRAG_SOURCE (model) &&
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 (mime_type == 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_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)

View File

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

View File

@ -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;
@ -1657,368 +1642,6 @@ gtk_widget_class_init (GtkWidgetClass *klass)
G_TYPE_FROM_CLASS (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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'],

View File

@ -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)
{
if (!popup_timer)
popup_timer = g_timeout_add (500, popup_cb, NULL);
return TRUE;
}
void
popsite_leave (GtkWidget *widget,
GdkDrop *drop)
popsite_enter (GtkDropTarget *dest)
{
g_print ("popsite enter\n");
if (!popup_timer)
popup_timer = g_timeout_add (500, popup_cb, NULL);
}
void
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 ();

View File

@ -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;
GdkDrop *drop = GDK_DROP (source);
GtkWidget *image = data;
const GValue *value;
GError *error = NULL;
if (gtk_selection_data_get_length (selection_data) == 0)
return;
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);
}
else if (gtk_selection_data_targets_include_text (selection_data))
{
text = (gchar *)gtk_selection_data_get_text (selection_data);
gtk_image_set_from_icon_name (GTK_IMAGE (data), text);
g_free (text);
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
{
g_assert_not_reached ();
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
{
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"));
gtk_popover_popdown (GTK_POPOVER (popover));
perform_drop (drop, image);
}
static void
do_cancel (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"));
gtk_popover_popdown (GTK_POPOVER (popover));
gdk_drop_finish (drop, 0);
g_object_set_data (G_OBJECT (image), "drop", NULL);
}
static void
ask_actions (GdkDrop *drop,
GtkWidget *image)
{
GtkWidget *popover, *box, *button;
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));
}
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"));
if (drop)
{
g_print ("denying drop, late\n");
gtk_drop_target_deny_drop (dest, drop);
}
return G_SOURCE_REMOVE;
}
static gboolean
image_drag_motion (GtkDropTarget *dest,
GdkDrop *drop,
gpointer data)
{
GtkWidget *image = data;
g_object_set_data_full (G_OBJECT (image), "drop", g_object_ref (drop), g_object_unref);
g_timeout_add (1000, delayed_deny, dest);
gdk_drop_status (drop, gtk_drop_target_get_actions (dest));
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_object_set_data_full (G_OBJECT (image), "drop", g_object_ref (drop), g_object_unref);
g_print ("drop, actions %d\n", action);
if (!gdk_drag_action_is_unique (action))
ask_actions (drop, image);
else
perform_drop (drop, image);
return TRUE;
}
static void
update_source_icon (GtkDragSource *source,
const char *icon_name,
int hotspot)
{
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);
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
drag_begin (GtkDragSource *source)
{
g_print ("drag begin\n");
}
static void
drag_end (GtkDragSource *source)
{
g_print ("drag end\n");
}
static gboolean
drag_cancel (GtkDragSource *source,
GdkDrag *drag,
GdkDragCancelReason reason)
{
g_print ("drag failed: %d\n", reason);
return FALSE;
}
GtkWidget *
make_image (const gchar *icon_name, int hotspot)
{
GtkWidget *image;
GtkDragSource *source;
GtkDropTarget *dest;
GdkContentFormats *formats;
GdkContentFormatsBuilder *builder;
GdkContentProvider *content;
image = gtk_image_new_from_icon_name (icon_name);
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, GDK_ACTION_COPY);
update_source_target_list (image);
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);
g_object_set_data (G_OBJECT (image), "hotspot", GINT_TO_POINTER (hotspot));
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 (image, "drag-begin", G_CALLBACK (image_drag_begin), image);
g_signal_connect (image, "drag-data-get", G_CALLBACK (image_drag_data_get), image);
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));
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);
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));
return image;
}
GtkWidget *
make_image2 (const gchar *icon_name, int hotspot)
{
GtkWidget *image;
image = gtk_image_new_from_icon_name (icon_name);
gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, GDK_ACTION_COPY);
update_source_target_list (image);
g_object_set_data (G_OBJECT (image), "hotspot", GINT_TO_POINTER (hotspot));
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);
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);
gdk_content_formats_unref (formats);
return image;
}
static void
spinner_drag_begin (GtkWidget *widget,
GdkDrag *drag,
gpointer data)
spinner_drag_begin (GtkDragSource *source,
GdkDrag *drag,
GtkWidget *widget)
{
GtkWidget *spinner;
GdkPaintable *paintable;
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);
}
static void
spinner_drag_end (GtkWidget *widget,
GdkDrag *drag,
gpointer data)
{
GtkWidget *spinner;
g_print ("GtkWidget::drag-end\n");
spinner = g_object_get_data (G_OBJECT (drag), "spinner");
gtk_widget_destroy (spinner);
}
static gboolean
spinner_drag_failed (GtkWidget *widget,
GdkDrag *drag,
GtkDragResult result,
gpointer data)
{
GTypeClass *class;
GEnumValue *value;
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);
return FALSE;
}
void
spinner_drag_data_get (GtkWidget *widget,
GdkDrag *drag,
GtkSelectionData *selection_data,
gpointer data)
{
g_print ("GtkWidget::drag-data-get\n");
gtk_selection_data_set_text (selection_data, "ACTIVE", -1);
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 ();

398
tests/testdnd3.c Normal file
View File

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

View File

@ -8,45 +8,6 @@ clear_pressed (GtkEntry *entry, gint icon, gpointer data)
gtk_editable_set_text (GTK_EDITABLE (entry), "");
}
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

View File

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

View File

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

View File

@ -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)
{
GtkWidget **child;
GdkDrop *drop = GDK_DROP (source);
GInputStream *stream;
const char *mime_type;
child = (void*) gtk_selection_data_get_data (data);
stream = gdk_drop_read_finish (drop, result, &mime_type, NULL);
g_idle_add (remove_in_idle, *child);
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)
{
gdk_drop_read_async (drop, (const char *[]) { "GTK_NOTEBOOK_TAB", NULL }, G_PRIORITY_DEFAULT, NULL, got_page, NULL);
gdk_drop_finish (drop, GDK_ACTION_MOVE);
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;
}

View File

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

View File

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