[gthumb] added ability to drag an image from the viewer



commit 8dd2ee9ab86af929163c5baccab2deeaa218840c
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Mon Apr 22 11:50:44 2019 +0200

    added ability to drag an image from the viewer
    
    Fixes https://gitlab.gnome.org/GNOME/gthumb/issues/24

 extensions/image_viewer/gth-image-viewer-page.c |  69 +++++++++++++
 gthumb/cairo-utils.c                            |  82 ++++++++++++++++
 gthumb/cairo-utils.h                            |   8 ++
 gthumb/gth-image-dragger.c                      | 125 ++++++++++++++++++++----
 gthumb/gth-image-dragger.h                      |  10 +-
 gthumb/gth-image-viewer.c                       |  31 +++++-
 6 files changed, 298 insertions(+), 27 deletions(-)
---
diff --git a/extensions/image_viewer/gth-image-viewer-page.c b/extensions/image_viewer/gth-image-viewer-page.c
index b1509d70..bf64b0ec 100644
--- a/extensions/image_viewer/gth-image-viewer-page.c
+++ b/extensions/image_viewer/gth-image-viewer-page.c
@@ -106,6 +106,7 @@ struct _GthImageViewerPagePrivate {
        gboolean           apply_icc_profile;
        GthFileData       *next_file_data[N_FORWARD_PRELOADERS];
        GthFileData       *prev_file_data[N_BACKWARD_PRELOADERS];
+       gulong             drag_data_get_event;
 };
 
 
@@ -1991,6 +1992,8 @@ gth_image_viewer_page_init (GthImageViewerPage *self)
                self->priv->next_file_data[i] = NULL;
        for (i = 0; i < N_BACKWARD_PRELOADERS; i++)
                self->priv->prev_file_data[i] = NULL;
+
+       self->priv->drag_data_get_event = 0;
 }
 
 
@@ -2096,6 +2099,70 @@ gth_image_viewer_page_reset (GthImageViewerPage *self)
 }
 
 
+static void
+viewer_drag_data_get_cb (GtkWidget        *widget,
+                        GdkDragContext   *context,
+                        GtkSelectionData *data,
+                        guint             info,
+                        guint             time,
+                        gpointer          user_data)
+{
+       GthImageViewerPage *self = user_data;
+       char               *uris[2];
+
+       if (self->priv->file_data == NULL)
+               return;
+
+       uris[0] = g_file_get_uri (self->priv->file_data->file);
+       uris[1] = NULL;
+       gtk_selection_data_set_uris (data, (char **) uris);
+
+       g_free (uris[0]);
+}
+
+
+static void
+_gth_image_viewer_page_enable_drag_source (GthImageViewerPage *self,
+                                          gboolean            enable)
+{
+       GthImageViewerTool *dragger;
+       GtkTargetList      *source_target_list;
+       GtkTargetEntry     *source_targets;
+       int                 n_source_targets;
+
+       dragger = gth_image_viewer_get_tool (GTH_IMAGE_VIEWER (self->priv->viewer));
+       if (! GTH_IS_IMAGE_DRAGGER (dragger))
+               return;
+
+       if (! enable) {
+               if (self->priv->drag_data_get_event > 0) {
+                       g_signal_handler_disconnect (self->priv->viewer, self->priv->drag_data_get_event);
+                       self->priv->drag_data_get_event = 0;
+               }
+               gth_image_dragger_disable_drag_source (GTH_IMAGE_DRAGGER (dragger));
+               return;
+       }
+
+       source_target_list = gtk_target_list_new (NULL, 0);
+       gtk_target_list_add_uri_targets (source_target_list, 0);
+       gtk_target_list_add_text_targets (source_target_list, 0);
+       source_targets = gtk_target_table_new_from_list (source_target_list, &n_source_targets);
+       gth_image_dragger_enable_drag_source (GTH_IMAGE_DRAGGER (dragger),
+                                             GDK_BUTTON1_MASK,
+                                             source_targets,
+                                             n_source_targets,
+                                             GDK_ACTION_COPY | GDK_ACTION_MOVE);
+       gtk_target_table_free (source_targets, n_source_targets);
+       gtk_target_list_unref (source_target_list);
+
+       if (self->priv->drag_data_get_event == 0)
+               self->priv->drag_data_get_event = g_signal_connect (self->priv->viewer,
+                                 "drag-data-get",
+                                 G_CALLBACK (viewer_drag_data_get_cb),
+                                 self);
+}
+
+
 void
 gth_image_viewer_page_reset_viewer_tool        (GthImageViewerPage *self)
 {
@@ -2112,6 +2179,8 @@ gth_image_viewer_page_reset_viewer_tool   (GthImageViewerPage *self)
                                          g_settings_get_enum (self->priv->settings, 
PREF_IMAGE_VIEWER_ZOOM_CHANGE));
        gth_image_viewer_set_reset_scrollbars (GTH_IMAGE_VIEWER (self->priv->viewer),
                                               g_settings_get_boolean (self->priv->settings, 
PREF_IMAGE_VIEWER_RESET_SCROLLBARS));
+
+       _gth_image_viewer_page_enable_drag_source (self, TRUE);
 }
 
 
diff --git a/gthumb/cairo-utils.c b/gthumb/cairo-utils.c
index babed8b8..77cc75d5 100644
--- a/gthumb/cairo-utils.c
+++ b/gthumb/cairo-utils.c
@@ -23,6 +23,8 @@
 #include <math.h>
 #include <string.h>
 #include "cairo-utils.h"
+#include "cairo-scale.h"
+#include "gth-image-utils.h"
 
 
 G_DEFINE_BOXED_TYPE (GthCairoSurface,
@@ -1208,3 +1210,83 @@ _cairo_create_checked_pattern (int size)
 
        return pattern;
 }
+
+
+void
+_cairo_draw_thumbnail_frame (cairo_t *cr,
+                            int      x,
+                            int      y,
+                            int      width,
+                            int      height)
+{
+       /* the drop shadow */
+
+       cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.33);
+       _cairo_draw_rounded_box (cr,
+                                x + 2,
+                                y + 2,
+                                width - 1,
+                                height - 1,
+                                0);
+       cairo_fill (cr);
+
+       /* the outer frame */
+
+       cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+       _cairo_draw_rounded_box (cr,
+                                x,
+                                y,
+                                width - 1,
+                                height - 1,
+                                0);
+       cairo_fill_preserve (cr);
+
+       cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.55);
+       cairo_stroke (cr);
+}
+
+
+#define DRAG_ICON_BORDER 8
+#define DRAG_ICON_THUMBNAIL_OFFSET 3
+
+
+cairo_surface_t *
+_cairo_create_dnd_icon (cairo_surface_t *image,
+                       int              icon_size,
+                       gboolean         multi_dnd)
+{
+       int              width, height;
+       cairo_surface_t *thumbnail;
+       cairo_surface_t *icon;
+       cairo_t         *cr;
+
+       width = cairo_image_surface_get_width (image);
+       height = cairo_image_surface_get_height (image);
+       scale_keeping_ratio (&width, &height, icon_size, icon_size, FALSE);
+       thumbnail = _cairo_image_surface_scale_fast (image, width, height);
+
+       icon = _cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                           width + DRAG_ICON_BORDER + (multi_dnd ? DRAG_ICON_BORDER : 0),
+                                           height + DRAG_ICON_BORDER + (multi_dnd ? DRAG_ICON_BORDER : 0));
+       cr = cairo_create (icon);
+
+       if (multi_dnd)
+               _cairo_draw_thumbnail_frame (cr, DRAG_ICON_THUMBNAIL_OFFSET, DRAG_ICON_THUMBNAIL_OFFSET, 
width + DRAG_ICON_BORDER - 1, height + DRAG_ICON_BORDER - 1);
+       _cairo_draw_thumbnail_frame (cr, 0, 0, width + DRAG_ICON_BORDER - 1, height + DRAG_ICON_BORDER - 1);
+
+
+       cairo_save (cr);
+       cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+       cairo_set_source_surface (cr, thumbnail, DRAG_ICON_THUMBNAIL_OFFSET, DRAG_ICON_THUMBNAIL_OFFSET);
+       cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
+       cairo_rectangle (cr, DRAG_ICON_THUMBNAIL_OFFSET, DRAG_ICON_THUMBNAIL_OFFSET, width, height);
+       cairo_fill (cr);
+       cairo_restore (cr);
+
+       cairo_surface_set_device_offset (icon, -width / 2, -height / 2);
+
+       cairo_surface_destroy (thumbnail);
+       cairo_destroy (cr);
+
+       return icon;
+}
diff --git a/gthumb/cairo-utils.h b/gthumb/cairo-utils.h
index 83888114..30be7e6c 100644
--- a/gthumb/cairo-utils.h
+++ b/gthumb/cairo-utils.h
@@ -266,5 +266,13 @@ void              _cairo_paint_grid                         (cairo_t
                                                             cairo_rectangle_int_t *rectangle,
                                                             GthGridType            grid_type);
 cairo_pattern_t * _cairo_create_checked_pattern                    (int                    size);
+void              _cairo_draw_thumbnail_frame               (cairo_t               *cr,
+                                                            int                    x,
+                                                            int                    y,
+                                                            int                    width,
+                                                            int                    height);
+cairo_surface_t * _cairo_create_dnd_icon                    (cairo_surface_t       *image,
+                                                            int                    icon_size,
+                                                            gboolean               multi_dnd);
 
 #endif /* CAIRO_UTILS_H */
diff --git a/gthumb/gth-image-dragger.c b/gthumb/gth-image-dragger.c
index 1a117d98..4433a123 100644
--- a/gthumb/gth-image-dragger.c
+++ b/gthumb/gth-image-dragger.c
@@ -32,6 +32,7 @@
 #define SIZE_TOO_BIG_FOR_SCALE_BILINEAR (3000 * 3000)
 #define MAX_ZOOM_LEVEL_FOR_HIGH_QUALITY 3.0
 #define FRAME_BORDER 15
+#define DRAG_ICON_SIZE 128
 
 
 /* Properties */
@@ -43,11 +44,20 @@ enum {
 
 struct _GthImageDraggerPrivate {
        GthImageViewer  *viewer;
-       gboolean         draggable;
+       gboolean         scrollable;
        gboolean         show_frame;
        cairo_surface_t *scaled;
        double           scaled_zoom;
        GthTask         *scale_task;
+
+       /* drag & drop */
+
+       gboolean         dnd_source_enabled;
+       GdkModifierType  dnd_start_button_mask;
+       GtkTargetList   *dnd_target_list;
+       GdkDragAction    dnd_actions;
+       gboolean         dnd_started;
+       gboolean         dnd_began;
 };
 
 
@@ -74,6 +84,10 @@ gth_image_dragger_finalize (GObject *object)
        _cairo_clear_surface (&self->priv->scaled);
        if (self->priv->scale_task != NULL)
                gth_task_cancel (self->priv->scale_task);
+       if (self->priv->dnd_target_list != NULL) {
+               gtk_target_list_unref (self->priv->dnd_target_list);
+               self->priv->dnd_target_list = NULL;
+       }
 
        /* Chain up */
        G_OBJECT_CLASS (gth_image_dragger_parent_class)->finalize (object);
@@ -157,8 +171,15 @@ gth_image_dragger_init (GthImageDragger *dragger)
        dragger->priv->scaled_zoom = 0;
        dragger->priv->scale_task = NULL;
        dragger->priv->viewer = NULL;
-       dragger->priv->draggable = FALSE;
+       dragger->priv->scrollable = FALSE;
        dragger->priv->show_frame = FALSE;
+
+       dragger->priv->dnd_source_enabled = FALSE;
+       dragger->priv->dnd_start_button_mask = 0;
+       dragger->priv->dnd_target_list = NULL;
+       dragger->priv->dnd_actions = 0;
+       dragger->priv->dnd_started = FALSE;
+       dragger->priv->dnd_began = FALSE;
 }
 
 
@@ -235,7 +256,7 @@ gth_image_dragger_size_allocate (GthImageViewerTool *base,
        h_upper = gtk_adjustment_get_upper (viewer->hadj);
        v_upper = gtk_adjustment_get_upper (viewer->vadj);
 
-       self->priv->draggable = (h_page_size > 0) && (v_page_size > 0) && ((h_upper > h_page_size) || 
(v_upper > v_page_size));
+       self->priv->scrollable = (h_page_size > 0) && (v_page_size > 0) && ((h_upper > h_page_size) || 
(v_upper > v_page_size));
        if (gtk_widget_get_realized (GTK_WIDGET (viewer)))
                _gth_image_dragger_update_cursor (self);
 }
@@ -310,11 +331,10 @@ gth_image_dragger_button_press (GthImageViewerTool *self,
        viewer = dragger->priv->viewer;
        widget = GTK_WIDGET (viewer);
 
-       if (! dragger->priv->draggable)
-               return FALSE;
-
-       if (((event->button == 1) || (event->button == 2)) &&
-                       ! viewer->dragging) {
+       if (dragger->priv->scrollable
+               && ! viewer->dragging
+               && ((event->button == 1) || (event->button == 2)))
+       {
                GdkCursor     *cursor;
                GdkGrabStatus  retval;
 
@@ -340,6 +360,18 @@ gth_image_dragger_button_press (GthImageViewerTool *self,
                return TRUE;
        }
 
+       if (dragger->priv->dnd_source_enabled
+               && ! viewer->dragging
+               && (event->button == 1)
+               && (event->type == GDK_BUTTON_PRESS)
+               && ! (event->state & GDK_CONTROL_MASK)
+               && ! (event->state & GDK_SHIFT_MASK))
+       {
+               viewer->pressed = TRUE;
+               viewer->dragging = TRUE;
+               dragger->priv->dnd_began = FALSE;
+       }
+
        return FALSE;
 }
 
@@ -351,16 +383,16 @@ gth_image_dragger_button_release (GthImageViewerTool *self,
        GthImageDragger *dragger;
        GthImageViewer  *viewer;
 
-       if ((event->button != 1) && (event->button != 2))
-               return FALSE;
-
        dragger = (GthImageDragger *) self;
        viewer = dragger->priv->viewer;
 
-       if (viewer->dragging)
+       if (dragger->priv->scrollable && viewer->dragging)
                gdk_seat_ungrab (gdk_device_get_seat (event->device));
 
-       return TRUE;
+       if (dragger->priv->dnd_source_enabled && viewer->dragging)
+               dragger->priv->dnd_began = FALSE;
+
+       return FALSE;
 }
 
 
@@ -374,16 +406,46 @@ gth_image_dragger_motion_notify (GthImageViewerTool *self,
        dragger = (GthImageDragger *) self;
        viewer = dragger->priv->viewer;
 
-       if (! viewer->pressed)
-               return FALSE;
+       if (dragger->priv->scrollable && viewer->dragging) {
+               gth_image_viewer_scroll_to (viewer,
+                                           viewer->drag_x_start - event->x,
+                                           viewer->drag_y_start - event->y);
+               return TRUE;
+       }
+
+       if (dragger->priv->dnd_source_enabled
+               && viewer->dragging
+               && ! dragger->priv->dnd_began
+               && gtk_drag_check_threshold (GTK_WIDGET (viewer),
+                                            viewer->drag_x_start,
+                                            viewer->drag_y_start,
+                                            event->x,
+                                            event->y))
+       {
+               GdkDragContext  *context;
+               cairo_surface_t *image;
+
+               context = gtk_drag_begin_with_coordinates (GTK_WIDGET (viewer),
+                                                          dragger->priv->dnd_target_list,
+                                                          dragger->priv->dnd_actions,
+                                                          1,
+                                                          (GdkEvent *) event,
+                                                          viewer->drag_x_start,
+                                                          viewer->drag_y_start);
+
+               image = gth_image_viewer_get_current_image (GTH_IMAGE_VIEWER (viewer));
+               if (image != NULL) {
+                       cairo_surface_t *icon = _cairo_create_dnd_icon (image, DRAG_ICON_SIZE, FALSE);
+                       gtk_drag_set_icon_surface (context, icon);
+                       cairo_surface_destroy (icon);
+               }
 
-       viewer->dragging = TRUE;
+               dragger->priv->dnd_began = TRUE;
 
-       gth_image_viewer_scroll_to (viewer,
-                                   viewer->drag_x_start - event->x,
-                                   viewer->drag_y_start - event->y);
+               return TRUE;
+       }
 
-       return TRUE;
+       return FALSE;
 }
 
 
@@ -594,3 +656,26 @@ gth_image_dragger_new (gboolean show_frame)
                                                    "show-frame", show_frame,
                                                    NULL);
 }
+
+
+void
+gth_image_dragger_enable_drag_source (GthImageDragger      *self,
+                                     GdkModifierType       start_button_mask,
+                                     const GtkTargetEntry *targets,
+                                     int                   n_targets,
+                                     GdkDragAction         actions)
+{
+       if (self->priv->dnd_target_list != NULL)
+               gtk_target_list_unref (self->priv->dnd_target_list);
+
+       self->priv->dnd_source_enabled = TRUE;
+       self->priv->dnd_start_button_mask = start_button_mask;
+       self->priv->dnd_target_list = gtk_target_list_new (targets, n_targets);
+       self->priv->dnd_actions = actions;
+}
+
+void
+gth_image_dragger_disable_drag_source (GthImageDragger      *self)
+{
+       self->priv->dnd_source_enabled = FALSE;
+}
diff --git a/gthumb/gth-image-dragger.h b/gthumb/gth-image-dragger.h
index 102d64b4..1effb230 100644
--- a/gthumb/gth-image-dragger.h
+++ b/gthumb/gth-image-dragger.h
@@ -51,8 +51,14 @@ struct _GthImageDraggerClass
        GObjectClass __parent_class;
 };
 
-GType                 gth_image_dragger_get_type (void);
-GthImageViewerTool *  gth_image_dragger_new      (gboolean show_frame);
+GType                  gth_image_dragger_get_type              (void);
+GthImageViewerTool *   gth_image_dragger_new                   (gboolean                show_frame);
+void                   gth_image_dragger_enable_drag_source    (GthImageDragger        *self,
+                                                                GdkModifierType         start_button_mask,
+                                                                const GtkTargetEntry   *targets,
+                                                                int                     n_targets,
+                                                                GdkDragAction           actions);
+void                   gth_image_dragger_disable_drag_source   (GthImageDragger        *self);
 
 G_END_DECLS
 
diff --git a/gthumb/gth-image-viewer.c b/gthumb/gth-image-viewer.c
index 923c3fc9..fc0e022a 100644
--- a/gthumb/gth-image-viewer.c
+++ b/gthumb/gth-image-viewer.c
@@ -863,6 +863,17 @@ gth_image_viewer_button_press (GtkWidget      *widget,
        return retval;
 }
 
+static void
+_gth_image_viewer_button_release (GthImageViewer *self,
+                                 GdkEventButton *event)
+{
+       gth_image_viewer_tool_button_release (self->priv->tool, event);
+
+       self->priv->just_focused = FALSE;
+       self->pressed = FALSE;
+       self->dragging = FALSE;
+}
+
 
 static gboolean
 gth_image_viewer_button_release (GtkWidget      *widget,
@@ -880,11 +891,7 @@ gth_image_viewer_button_release (GtkWidget      *widget,
                               0);
        }
 
-       gth_image_viewer_tool_button_release (self->priv->tool, event);
-
-       self->priv->just_focused = FALSE;
-       self->pressed = FALSE;
-       self->dragging = FALSE;
+       _gth_image_viewer_button_release (self, event);
 
        return FALSE;
 }
@@ -1010,6 +1017,19 @@ gth_image_viewer_scroll_event (GtkWidget      *widget,
 }
 
 
+static void
+gth_image_viewer_drag_end (GtkWidget      *widget,
+                          GdkDragContext *context)
+{
+       GthImageViewer *self;
+
+       g_return_if_fail (GTH_IS_IMAGE_VIEWER (widget));
+
+       self = GTH_IMAGE_VIEWER (widget);
+       _gth_image_viewer_button_release (self, NULL);
+}
+
+
 static void
 scroll_relative (GthImageViewer *self,
                 int             delta_x,
@@ -1355,6 +1375,7 @@ gth_image_viewer_class_init (GthImageViewerClass *class)
        widget_class->button_release_event = gth_image_viewer_button_release;
        widget_class->motion_notify_event = gth_image_viewer_motion_notify;
        widget_class->scroll_event = gth_image_viewer_scroll_event;
+       widget_class->drag_end = gth_image_viewer_drag_end;
 
        class->clicked      = NULL;
        class->zoom_changed = NULL;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]