| #include <gtk/gtk.h> |
| |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #elif defined (G_OS_WIN32) |
| #include <io.h> |
| #endif |
| |
| static GdkTexture * |
| render_paintable_to_texture (GdkPaintable *paintable) |
| { |
| GtkSnapshot *snapshot; |
| GskRenderNode *node; |
| int width, height; |
| cairo_surface_t *surface; |
| cairo_t *cr; |
| GdkTexture *texture; |
| GBytes *bytes; |
| |
| width = gdk_paintable_get_intrinsic_width (paintable); |
| if (width == 0) |
| width = 32; |
| |
| height = gdk_paintable_get_intrinsic_height (paintable); |
| if (height == 0) |
| height = 32; |
| |
| surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); |
| |
| snapshot = gtk_snapshot_new (); |
| gdk_paintable_snapshot (paintable, snapshot, width, height); |
| node = gtk_snapshot_free_to_node (snapshot); |
| |
| cr = cairo_create (surface); |
| gsk_render_node_draw (node, cr); |
| cairo_destroy (cr); |
| |
| gsk_render_node_unref (node); |
| |
| bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface), |
| cairo_image_surface_get_height (surface) |
| * cairo_image_surface_get_stride (surface), |
| (GDestroyNotify) cairo_surface_destroy, |
| cairo_surface_reference (surface)); |
| texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface), |
| cairo_image_surface_get_height (surface), |
| GDK_MEMORY_DEFAULT, |
| bytes, |
| cairo_image_surface_get_stride (surface)); |
| g_bytes_unref (bytes); |
| cairo_surface_destroy (surface); |
| |
| return texture; |
| } |
| |
| static GdkTexture * |
| get_image_texture (GtkImage *image) |
| { |
| GtkIconTheme *icon_theme; |
| const char *icon_name; |
| int width = 48; |
| GdkPaintable *paintable = NULL; |
| GdkTexture *texture = NULL; |
| GtkIconPaintable *icon; |
| |
| switch (gtk_image_get_storage_type (image)) |
| { |
| case GTK_IMAGE_PAINTABLE: |
| paintable = gtk_image_get_paintable (image); |
| break; |
| case GTK_IMAGE_ICON_NAME: |
| icon_name = gtk_image_get_icon_name (image); |
| icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (image))); |
| icon = gtk_icon_theme_lookup_icon (icon_theme, |
| icon_name, |
| NULL, |
| width, 1, |
| gtk_widget_get_direction (GTK_WIDGET (image)), |
| 0); |
| paintable = GDK_PAINTABLE (icon); |
| break; |
| case GTK_IMAGE_GICON: |
| case GTK_IMAGE_EMPTY: |
| default: |
| g_warning ("Image storage type %d not handled", |
| gtk_image_get_storage_type (image)); |
| } |
| |
| if (paintable) |
| texture = render_paintable_to_texture (paintable); |
| |
| g_object_unref (paintable); |
| |
| return texture; |
| } |
| |
| enum { |
| TOP_LEFT, |
| CENTER, |
| BOTTOM_RIGHT |
| }; |
| |
| static void |
| got_texture (GObject *source, |
| GAsyncResult *result, |
| gpointer data) |
| { |
| GdkDrop *drop = GDK_DROP (source); |
| GtkWidget *image = data; |
| const GValue *value; |
| GError *error = NULL; |
| |
| value = gdk_drop_read_value_finish (drop, result, &error); |
| if (value) |
| { |
| GdkTexture *texture = 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_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_content_formats_contain_gtype (gdk_drop_get_formats (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_widget_get_parent (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_widget_get_parent (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 (); |
| gtk_widget_set_parent (popover, image); |
| g_object_set_data (G_OBJECT (image), "popover", popover); |
| |
| box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); |
| gtk_popover_set_child (GTK_POPOVER (popover), box); |
| button = gtk_button_new_with_label ("Copy"); |
| g_signal_connect (button, "clicked", G_CALLBACK (do_copy), NULL); |
| gtk_box_append (GTK_BOX (box), button); |
| button = gtk_button_new_with_label ("Move"); |
| g_signal_connect (button, "clicked", G_CALLBACK (do_copy), NULL); |
| gtk_box_append (GTK_BOX (box), button); |
| button = gtk_button_new_with_label ("Cancel"); |
| g_signal_connect (button, "clicked", G_CALLBACK (do_cancel), NULL); |
| gtk_box_append (GTK_BOX (box), button); |
| } |
| gtk_popover_popup (GTK_POPOVER (popover)); |
| } |
| |
| static gboolean |
| delayed_deny (gpointer data) |
| { |
| GtkDropTargetAsync *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_async_reject_drop (dest, drop); |
| } |
| |
| return G_SOURCE_REMOVE; |
| } |
| |
| static gboolean |
| image_drag_accept (GtkDropTargetAsync *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); |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| image_drag_drop (GtkDropTarget *dest, |
| GdkDrop *drop, |
| double x, |
| double 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) |
| { |
| GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source)); |
| GtkIconPaintable *icon; |
| int hot_x, hot_y; |
| int size = 48; |
| |
| if (widget == NULL) |
| return; |
| |
| icon = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_for_display (gtk_widget_get_display (widget)), |
| icon_name, |
| NULL, |
| size, 1, |
| gtk_widget_get_direction (widget), |
| 0); |
| 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, GDK_PAINTABLE (icon), hot_x, hot_y); |
| g_object_unref (icon); |
| } |
| |
| static GdkContentProvider * |
| drag_prepare (GtkDragSource *source, |
| double x, |
| double y) |
| { |
| GtkImage *image = GTK_IMAGE (gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (source))); |
| GdkTexture *texture; |
| GdkContentProvider *content; |
| |
| content = gdk_content_provider_new_typed (G_TYPE_STRING, gtk_image_get_icon_name (GTK_IMAGE (image))); |
| |
| texture = get_image_texture (image); |
| if (texture) |
| { |
| content = gdk_content_provider_new_union ((GdkContentProvider *[2]) { |
| gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, texture), |
| content, |
| }, 2); |
| g_object_unref (texture); |
| } |
| |
| return content; |
| } |
| |
| 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; |
| } |
| |
| static GtkWidget * |
| make_image (const char *icon_name, int hotspot) |
| { |
| GtkWidget *image; |
| GtkDragSource *source; |
| GtkDropTargetAsync *dest; |
| GdkContentFormats *formats; |
| GdkContentFormatsBuilder *builder; |
| |
| image = gtk_image_new_from_icon_name (icon_name); |
| gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE); |
| |
| builder = gdk_content_formats_builder_new (); |
| gdk_content_formats_builder_add_gtype (builder, GDK_TYPE_TEXTURE); |
| gdk_content_formats_builder_add_gtype (builder, G_TYPE_STRING); |
| formats = gdk_content_formats_builder_free_to_formats (builder); |
| |
| source = gtk_drag_source_new (); |
| gtk_drag_source_set_actions (source, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK); |
| update_source_icon (source, icon_name, hotspot); |
| |
| g_signal_connect (source, "prepare", G_CALLBACK (drag_prepare), NULL); |
| g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), NULL); |
| g_signal_connect (source, "drag-end", G_CALLBACK (drag_end), NULL); |
| g_signal_connect (source, "drag-cancel", G_CALLBACK (drag_cancel), NULL); |
| gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source)); |
| |
| dest = gtk_drop_target_async_new (formats, GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_ASK); |
| g_signal_connect (dest, "accept", G_CALLBACK (image_drag_accept), image); |
| g_signal_connect (dest, "drop", G_CALLBACK (image_drag_drop), image); |
| gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest)); |
| |
| return image; |
| } |
| |
| static void |
| spinner_drag_begin (GtkDragSource *source, |
| GdkDrag *drag, |
| GtkWidget *widget) |
| { |
| GdkPaintable *paintable; |
| |
| paintable = gtk_widget_paintable_new (widget); |
| gtk_drag_source_set_icon (source, paintable, 0, 0); |
| g_object_unref (paintable); |
| } |
| |
| static GtkWidget * |
| make_spinner (void) |
| { |
| GtkWidget *spinner; |
| GtkDragSource *source; |
| GdkContentProvider *content; |
| |
| spinner = gtk_spinner_new (); |
| gtk_spinner_start (GTK_SPINNER (spinner)); |
| |
| content = gdk_content_provider_new_typed (G_TYPE_STRING, "ACTIVE"); |
| 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_object_unref (content); |
| |
| return spinner; |
| } |
| |
| int |
| main (int argc, char *Argv[]) |
| { |
| GtkWidget *window; |
| GtkWidget *grid; |
| GtkWidget *entry; |
| |
| gtk_init (); |
| |
| window = gtk_window_new (); |
| 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-start", 20, |
| "margin-end", 20, |
| "margin-top", 20, |
| "margin-bottom", 20, |
| "row-spacing", 20, |
| "column-spacing", 20, |
| NULL); |
| gtk_window_set_child (GTK_WINDOW (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); |
| |
| gtk_grid_attach (GTK_GRID (grid), make_image ("dialog-question", TOP_LEFT), 0, 3, 1, 1); |
| |
| gtk_grid_attach (GTK_GRID (grid), make_image ("dialog-information", CENTER), 1, 3, 1, 1); |
| |
| gtk_widget_show (window); |
| while (TRUE) |
| g_main_context_iteration (NULL, TRUE); |
| |
| return 0; |
| } |