2015-12-11 15:44:46 +00:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
2017-10-23 03:27:01 +00:00
|
|
|
static cairo_surface_t *
|
|
|
|
get_image_surface (GtkImage *image,
|
|
|
|
int *out_size)
|
2015-12-11 15:44:46 +00:00
|
|
|
{
|
|
|
|
const gchar *icon_name;
|
|
|
|
GtkIconSize size;
|
|
|
|
GtkIconTheme *icon_theme;
|
|
|
|
int width;
|
2017-10-23 03:27:01 +00:00
|
|
|
cairo_surface_t *surface;
|
2015-12-11 15:44:46 +00:00
|
|
|
|
|
|
|
switch (gtk_image_get_storage_type (image))
|
|
|
|
{
|
2017-10-23 03:27:01 +00:00
|
|
|
case GTK_IMAGE_SURFACE:
|
|
|
|
surface = gtk_image_get_surface (image);
|
|
|
|
*out_size = cairo_image_surface_get_width (surface);
|
|
|
|
return cairo_surface_reference (surface);
|
2015-12-11 15:44:46 +00:00
|
|
|
case GTK_IMAGE_ICON_NAME:
|
|
|
|
gtk_image_get_icon_name (image, &icon_name, &size);
|
2017-10-31 01:59:06 +00:00
|
|
|
icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (image)));
|
2015-12-11 15:44:46 +00:00
|
|
|
gtk_icon_size_lookup (size, &width, NULL);
|
2017-10-23 03:27:01 +00:00
|
|
|
*out_size = width;
|
|
|
|
return gtk_icon_theme_load_surface (icon_theme, icon_name, width, 1, NULL, GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL);
|
2015-12-11 15:44:46 +00:00
|
|
|
default:
|
|
|
|
g_warning ("Image storage type %d not handled",
|
|
|
|
gtk_image_get_storage_type (image));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum {
|
|
|
|
TARGET_IMAGE,
|
|
|
|
TARGET_TEXT
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
TOP_LEFT,
|
|
|
|
CENTER,
|
|
|
|
BOTTOM_RIGHT
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
image_drag_begin (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gpointer data)
|
|
|
|
{
|
2017-10-23 03:27:01 +00:00
|
|
|
cairo_surface_t *surface;
|
2015-12-11 15:44:46 +00:00
|
|
|
gint hotspot;
|
|
|
|
gint hot_x, hot_y;
|
2017-10-23 03:27:01 +00:00
|
|
|
gint size;
|
2015-12-11 15:44:46 +00:00
|
|
|
|
2017-10-23 03:27:01 +00:00
|
|
|
surface = get_image_surface (GTK_IMAGE (data), &size);
|
2015-12-11 15:44:46 +00:00
|
|
|
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:
|
2017-10-23 03:27:01 +00:00
|
|
|
hot_x = size / 2;
|
|
|
|
hot_y = size / 2;
|
2015-12-11 15:44:46 +00:00
|
|
|
break;
|
|
|
|
case BOTTOM_RIGHT:
|
2017-10-23 03:27:01 +00:00
|
|
|
hot_x = size;
|
|
|
|
hot_y = size;
|
2015-12-11 15:44:46 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-10-23 03:27:01 +00:00
|
|
|
cairo_surface_set_device_offset (surface, hot_x, hot_y);
|
|
|
|
gtk_drag_set_icon_surface (context, surface);
|
|
|
|
cairo_surface_destroy (surface);
|
2015-12-11 15:44:46 +00:00
|
|
|
}
|
|
|
|
|
2016-04-27 00:34:24 +00:00
|
|
|
static void
|
2017-04-25 16:47:42 +00:00
|
|
|
drag_widget_destroyed (GtkWidget *image, gpointer data)
|
2016-04-27 00:34:24 +00:00
|
|
|
{
|
|
|
|
GtkWidget *widget = data;
|
|
|
|
|
|
|
|
g_print ("drag widget destroyed\n");
|
2017-04-25 16:47:42 +00:00
|
|
|
g_object_unref (image);
|
|
|
|
g_object_set_data (G_OBJECT (widget), "drag widget", NULL);
|
2016-04-27 00:34:24 +00:00
|
|
|
}
|
|
|
|
|
2016-04-21 18:21:14 +00:00
|
|
|
static void
|
2017-08-01 22:49:42 +00:00
|
|
|
window_drag_end (GtkWidget *widget, GdkDragContext *context, gpointer data)
|
2016-04-21 18:21:14 +00:00
|
|
|
{
|
|
|
|
GtkWidget *window = data;
|
|
|
|
|
|
|
|
gtk_widget_destroy (window);
|
2017-08-01 22:49:42 +00:00
|
|
|
g_signal_handlers_disconnect_by_func (widget, window_drag_end, data);
|
2016-04-21 18:21:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
window_drag_begin (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gpointer data)
|
|
|
|
{
|
2017-10-23 03:27:01 +00:00
|
|
|
cairo_surface_t *surface;
|
2016-04-21 18:21:14 +00:00
|
|
|
GtkWidget *image;
|
2016-04-26 04:19:02 +00:00
|
|
|
int hotspot;
|
2017-10-23 03:27:01 +00:00
|
|
|
int size;
|
2016-04-26 04:19:02 +00:00
|
|
|
|
|
|
|
hotspot = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (data), "hotspot"));
|
2016-04-21 18:21:14 +00:00
|
|
|
|
2017-04-25 16:47:42 +00:00
|
|
|
image = g_object_get_data (G_OBJECT (widget), "drag widget");
|
|
|
|
if (image == NULL)
|
2016-04-26 04:19:02 +00:00
|
|
|
{
|
2016-04-27 00:34:24 +00:00
|
|
|
g_print ("creating new drag widget\n");
|
2017-10-23 03:27:01 +00:00
|
|
|
surface = get_image_surface (GTK_IMAGE (data), &size);
|
|
|
|
image = gtk_image_new_from_surface (surface);
|
|
|
|
cairo_surface_destroy (surface);
|
2017-04-25 16:47:42 +00:00
|
|
|
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);
|
2016-04-26 04:19:02 +00:00
|
|
|
}
|
2016-04-27 00:34:24 +00:00
|
|
|
else
|
|
|
|
g_print ("reusing drag widget\n");
|
2016-04-21 18:21:14 +00:00
|
|
|
|
2017-04-25 16:47:42 +00:00
|
|
|
gtk_drag_set_icon_widget (context, image, 0, 0);
|
2016-04-21 18:21:14 +00:00
|
|
|
|
2016-04-26 04:19:02 +00:00
|
|
|
if (hotspot == CENTER)
|
2017-04-25 16:47:42 +00:00
|
|
|
g_signal_connect (widget, "drag-end", G_CALLBACK (window_drag_end), image);
|
2016-04-21 18:21:14 +00:00
|
|
|
}
|
|
|
|
|
2015-12-11 15:44:46 +00:00
|
|
|
static void
|
2017-08-01 22:49:42 +00:00
|
|
|
update_source_target_list (GtkWidget *image)
|
2015-12-11 15:44:46 +00:00
|
|
|
{
|
|
|
|
GtkTargetList *target_list;
|
|
|
|
|
|
|
|
target_list = gtk_target_list_new (NULL, 0);
|
|
|
|
|
|
|
|
gtk_target_list_add_image_targets (target_list, TARGET_IMAGE, FALSE);
|
|
|
|
if (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)
|
|
|
|
gtk_target_list_add_text_targets (target_list, TARGET_TEXT);
|
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
gtk_drag_source_set_target_list (image, target_list);
|
2015-12-11 15:44:46 +00:00
|
|
|
|
|
|
|
gtk_target_list_unref (target_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-08-01 22:49:42 +00:00
|
|
|
update_dest_target_list (GtkWidget *image)
|
2015-12-11 15:44:46 +00:00
|
|
|
{
|
|
|
|
GtkTargetList *target_list;
|
|
|
|
|
|
|
|
target_list = gtk_target_list_new (NULL, 0);
|
|
|
|
|
|
|
|
gtk_target_list_add_image_targets (target_list, TARGET_IMAGE, FALSE);
|
|
|
|
gtk_target_list_add_text_targets (target_list, TARGET_TEXT);
|
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
gtk_drag_dest_set_target_list (image, target_list);
|
2015-12-11 15:44:46 +00:00
|
|
|
|
|
|
|
gtk_target_list_unref (target_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
image_drag_data_get (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
GtkSelectionData *selection_data,
|
|
|
|
guint info,
|
|
|
|
guint time,
|
|
|
|
gpointer data)
|
|
|
|
{
|
2017-10-23 03:27:01 +00:00
|
|
|
cairo_surface_t *surface;
|
2015-12-11 15:44:46 +00:00
|
|
|
const gchar *name;
|
2017-10-23 03:27:01 +00:00
|
|
|
int size;
|
2015-12-11 15:44:46 +00:00
|
|
|
|
|
|
|
switch (info)
|
|
|
|
{
|
|
|
|
case TARGET_IMAGE:
|
2017-10-23 03:27:01 +00:00
|
|
|
surface = get_image_surface (GTK_IMAGE (data), &size);
|
|
|
|
gtk_selection_data_set_surface (selection_data, surface);
|
2015-12-11 15:44:46 +00:00
|
|
|
break;
|
|
|
|
case TARGET_TEXT:
|
2016-04-21 18:21:14 +00:00
|
|
|
if (gtk_image_get_storage_type (GTK_IMAGE (data)) == GTK_IMAGE_ICON_NAME)
|
|
|
|
gtk_image_get_icon_name (GTK_IMAGE (data), &name, NULL);
|
|
|
|
else
|
|
|
|
name = "Boo!";
|
2015-12-11 15:44:46 +00:00
|
|
|
gtk_selection_data_set_text (selection_data, name, -1);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
image_drag_data_received (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
GtkSelectionData *selection_data,
|
|
|
|
guint info,
|
|
|
|
guint32 time,
|
|
|
|
gpointer data)
|
|
|
|
{
|
2017-10-23 03:27:01 +00:00
|
|
|
cairo_surface_t *surface;
|
2015-12-11 15:44:46 +00:00
|
|
|
gchar *text;
|
|
|
|
|
|
|
|
if (gtk_selection_data_get_length (selection_data) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (info)
|
|
|
|
{
|
|
|
|
case TARGET_IMAGE:
|
2017-10-23 03:27:01 +00:00
|
|
|
surface = gtk_selection_data_get_surface (selection_data);
|
|
|
|
gtk_image_set_from_surface (GTK_IMAGE (data), surface);
|
|
|
|
cairo_surface_destroy (surface);
|
2015-12-11 15:44:46 +00:00
|
|
|
break;
|
|
|
|
case TARGET_TEXT:
|
|
|
|
text = (gchar *)gtk_selection_data_get_text (selection_data);
|
|
|
|
gtk_image_set_from_icon_name (GTK_IMAGE (data), text, GTK_ICON_SIZE_DIALOG);
|
|
|
|
g_free (text);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GtkWidget *
|
|
|
|
make_image (const gchar *icon_name, int hotspot)
|
|
|
|
{
|
2017-08-01 22:49:42 +00:00
|
|
|
GtkWidget *image;
|
2015-12-11 15:44:46 +00:00
|
|
|
|
|
|
|
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_DIALOG);
|
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
|
|
|
|
update_source_target_list (image);
|
2015-12-11 15:44:46 +00:00
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (image), "hotspot", GINT_TO_POINTER (hotspot));
|
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
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);
|
2015-12-11 15:44:46 +00:00
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
gtk_drag_dest_set (image, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
|
|
|
|
g_signal_connect (image, "drag-data-received", G_CALLBACK (image_drag_data_received), image);
|
|
|
|
update_dest_target_list (image);
|
2015-12-11 15:44:46 +00:00
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
return image;
|
2015-12-11 15:44:46 +00:00
|
|
|
}
|
|
|
|
|
2016-04-21 18:21:14 +00:00
|
|
|
GtkWidget *
|
|
|
|
make_image2 (const gchar *icon_name, int hotspot)
|
|
|
|
{
|
2017-08-01 22:49:42 +00:00
|
|
|
GtkWidget *image;
|
2016-04-21 18:21:14 +00:00
|
|
|
|
|
|
|
image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_DIALOG);
|
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
gtk_drag_source_set (image, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
|
|
|
|
update_source_target_list (image);
|
2016-04-21 18:21:14 +00:00
|
|
|
|
|
|
|
g_object_set_data (G_OBJECT (image), "hotspot", GINT_TO_POINTER (hotspot));
|
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
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);
|
2016-04-21 18:21:14 +00:00
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
gtk_drag_dest_set (image, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
|
|
|
|
g_signal_connect (image, "drag-data-received", G_CALLBACK (image_drag_data_received), image);
|
|
|
|
update_dest_target_list (image);
|
2016-04-21 18:21:14 +00:00
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
return image;
|
2016-04-21 18:21:14 +00:00
|
|
|
}
|
|
|
|
|
2015-12-11 15:44:46 +00:00
|
|
|
static void
|
|
|
|
spinner_drag_begin (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
GtkWidget *spinner;
|
|
|
|
|
2015-12-13 21:01:05 +00:00
|
|
|
g_print ("GtkWidget::drag-begin\n");
|
2015-12-11 15:44:46 +00:00
|
|
|
spinner = g_object_new (GTK_TYPE_SPINNER,
|
|
|
|
"visible", TRUE,
|
|
|
|
"active", TRUE,
|
|
|
|
NULL);
|
|
|
|
gtk_drag_set_icon_widget (context, spinner, 0, 0);
|
2015-12-11 15:50:25 +00:00
|
|
|
g_object_set_data (G_OBJECT (context), "spinner", spinner);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
spinner_drag_end (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
GtkWidget *spinner;
|
|
|
|
|
2015-12-13 21:01:05 +00:00
|
|
|
g_print ("GtkWidget::drag-end\n");
|
2015-12-11 15:50:25 +00:00
|
|
|
spinner = g_object_get_data (G_OBJECT (context), "spinner");
|
|
|
|
gtk_widget_destroy (spinner);
|
2015-12-11 15:44:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-13 21:01:05 +00:00
|
|
|
static gboolean
|
|
|
|
spinner_drag_failed (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-12-11 15:44:46 +00:00
|
|
|
void
|
|
|
|
spinner_drag_data_get (GtkWidget *widget,
|
|
|
|
GdkDragContext *context,
|
|
|
|
GtkSelectionData *selection_data,
|
|
|
|
guint info,
|
|
|
|
guint time,
|
|
|
|
gpointer data)
|
|
|
|
{
|
2015-12-13 21:01:05 +00:00
|
|
|
g_print ("GtkWidget::drag-data-get\n");
|
2015-12-11 15:44:46 +00:00
|
|
|
gtk_selection_data_set_text (selection_data, "ACTIVE", -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GtkWidget *
|
|
|
|
make_spinner (void)
|
|
|
|
{
|
2017-08-01 22:49:42 +00:00
|
|
|
GtkWidget *spinner;
|
2015-12-11 15:44:46 +00:00
|
|
|
|
|
|
|
spinner = gtk_spinner_new ();
|
|
|
|
gtk_spinner_start (GTK_SPINNER (spinner));
|
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
gtk_drag_source_set (spinner, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
|
|
|
|
gtk_drag_source_add_text_targets (spinner);
|
2015-12-11 15:44:46 +00:00
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
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);
|
2015-12-11 15:44:46 +00:00
|
|
|
|
2017-08-01 22:49:42 +00:00
|
|
|
return spinner;
|
2015-12-11 15:44:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main (int argc, char *Argv[])
|
|
|
|
{
|
|
|
|
GtkWidget *window;
|
|
|
|
GtkWidget *grid;
|
|
|
|
GtkWidget *entry;
|
|
|
|
|
2016-12-28 13:53:22 +00:00
|
|
|
gtk_init ();
|
2015-12-11 15:44:46 +00:00
|
|
|
|
|
|
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (window), "Drag And Drop");
|
|
|
|
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
|
|
|
|
|
|
|
|
grid = gtk_grid_new ();
|
|
|
|
g_object_set (grid,
|
|
|
|
"margin", 20,
|
|
|
|
"row-spacing", 20,
|
|
|
|
"column-spacing", 20,
|
|
|
|
NULL);
|
|
|
|
gtk_container_add (GTK_CONTAINER (window), grid);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), make_image ("dialog-warning", TOP_LEFT), 0, 0, 1, 1);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), make_image ("process-stop", BOTTOM_RIGHT), 1, 0, 1, 1);
|
|
|
|
|
|
|
|
entry = gtk_entry_new ();
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), entry, 0, 1, 2, 1);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2016-04-27 00:34:24 +00:00
|
|
|
gtk_grid_attach (GTK_GRID (grid), make_image2 ("dialog-question", TOP_LEFT), 0, 3, 1, 1);
|
2016-04-21 18:21:14 +00:00
|
|
|
|
2016-04-27 00:34:24 +00:00
|
|
|
gtk_grid_attach (GTK_GRID (grid), make_image2 ("dialog-information", CENTER), 1, 3, 1, 1);
|
2016-04-26 04:19:02 +00:00
|
|
|
|
2017-01-19 09:02:04 +00:00
|
|
|
gtk_widget_show (window);
|
2015-12-11 15:44:46 +00:00
|
|
|
gtk_main ();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|