[gthumb] image navigator: moved the overview area to its own widget



commit a239b05b26318669ea52cbdede13dbcdf4b55b2b
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Wed Oct 30 22:10:05 2013 +0100

    image navigator: moved the overview area to its own widget

 gthumb/Makefile.am           |    2 +
 gthumb/gth-image-navigator.c |  317 +++++---------------------
 gthumb/gth-image-overview.c  |  513 ++++++++++++++++++++++++++++++++++++++++++
 gthumb/gth-image-overview.h  |   67 ++++++
 4 files changed, 643 insertions(+), 256 deletions(-)
---
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index 9d6586c..61ebdd7 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -66,6 +66,7 @@ PUBLIC_HEADER_FILES =                                         \
        gth-image-list-task.h                           \
        gth-image-loader.h                              \
        gth-image-navigator.h                           \
+       gth-image-overview.h                            \
        gth-image-preloader.h                           \
        gth-image-saver.h                               \
        gth-image-selector.h                            \
@@ -199,6 +200,7 @@ gthumb_SOURCES =                                    \
        gth-image-list-task.c                           \
        gth-image-loader.c                              \
        gth-image-navigator.c                           \
+       gth-image-overview.c                            \
        gth-image-preloader.c                           \
        gth-image-saver.c                               \
        gth-image-selector.c                            \
diff --git a/gthumb/gth-image-navigator.c b/gthumb/gth-image-navigator.c
index 685a573..161bbec 100644
--- a/gthumb/gth-image-navigator.c
+++ b/gthumb/gth-image-navigator.c
@@ -24,16 +24,14 @@
 #include <gdk/gdkkeysyms.h>
 #include "cairo-scale.h"
 #include "gth-image-navigator.h"
+#include "gth-image-overview.h"
 #include "gth-image-viewer.h"
 #include "gtk-utils.h"
 #include "pixbuf-utils.h"
 
 
-#define VISIBLE_AREA_BORDER 2.0
-#define POPUP_BORDER        2
-#define POPUP_BORDER_2      (POPUP_BORDER*2)
-#define POPUP_MAX_WIDTH     180
-#define POPUP_MAX_HEIGHT    180
+#define POPUP_BORDER   2
+#define POPUP_BORDER_2 (POPUP_BORDER*2)
 
 
 /* Properties */
@@ -329,99 +327,17 @@ gth_image_navigator_class_init (GthImageNavigatorClass *klass)
 
 
 typedef struct {
-       GthImageViewer        *viewer;
-       int                    x_root, y_root;
-       GtkWidget             *popup_win;
-       GtkWidget             *preview;
-       cairo_surface_t       *image;
-       int                    image_width, image_height;
-       int                    original_width, original_height;
-       int                    window_max_width, window_max_height;
-       int                    popup_x, popup_y, popup_width, popup_height;
-       cairo_rectangle_int_t  visible_area;
-       double                 zoom_factor;
-       double                 quality_zoom;
+       GthImageViewer  *viewer;
+       GtkWidget       *popup_win;
+       GtkWidget       *overview;
 } NavigatorPopup;
 
 
 static void
-get_visible_area_origin_as_double (NavigatorPopup *nav_popup,
-                                  int             mx,
-                                  int             my,
-                                  double         *x,
-                                  double         *y)
+navigator_popup_close (NavigatorPopup *nav_popup)
 {
-       *x = MIN (mx - POPUP_BORDER, nav_popup->window_max_width);
-       *y = MIN (my - POPUP_BORDER, nav_popup->window_max_height);
-
-       if (*x - nav_popup->visible_area.width / 2.0 < 0.0)
-               *x = nav_popup->visible_area.width / 2.0;
-
-       if (*y - nav_popup->visible_area.height / 2.0 < 0.0)
-               *y = nav_popup->visible_area.height / 2.0;
-
-       if (*x + nav_popup->visible_area.width / 2.0 > nav_popup->popup_width - 0)
-               *x = nav_popup->popup_width - 0 - nav_popup->visible_area.width / 2.0;
-
-       if (*y + nav_popup->visible_area.height / 2.0 > nav_popup->popup_height - 0)
-               *y = nav_popup->popup_height - 0 - nav_popup->visible_area.height / 2.0;
-
-       *x = *x - nav_popup->visible_area.width / 2.0;
-       *y = *y - nav_popup->visible_area.height / 2.0;
-}
-
-
-static void
-update_popup_geometry (NavigatorPopup *nav_popup)
-{
-       int           zoomed_width;
-       int           zoomed_height;
-       GtkAllocation allocation;
-       int           scroll_offset_x;
-       int           scroll_offset_y;
-
-       zoomed_width = nav_popup->original_width * gth_image_viewer_get_zoom (nav_popup->viewer);
-       zoomed_height = nav_popup->original_height * gth_image_viewer_get_zoom (nav_popup->viewer);
-
-       nav_popup->window_max_width = MIN (zoomed_width, POPUP_MAX_WIDTH);
-       nav_popup->window_max_height = MIN (zoomed_width, POPUP_MAX_HEIGHT);
-       nav_popup->zoom_factor = MIN ((double) (nav_popup->window_max_width) / zoomed_width,
-                                     (double) (nav_popup->window_max_height) / zoomed_height);
-       nav_popup->quality_zoom = (double) nav_popup->original_width / nav_popup->image_width;
-
-       /* popup window size */
-
-       nav_popup->popup_width  = MAX ((int) floor (nav_popup->zoom_factor * zoomed_width + 0.5), 1);
-       nav_popup->popup_height = MAX ((int) floor (nav_popup->zoom_factor * zoomed_height + 0.5), 1);
-
-       cairo_surface_destroy (nav_popup->image);
-       nav_popup->image = _cairo_image_surface_scale_bilinear (gth_image_viewer_get_current_image 
(nav_popup->viewer),
-                                                               nav_popup->popup_width,
-                                                               nav_popup->popup_height);
-
-       /* visible area size */
-
-       gtk_widget_get_allocation (GTK_WIDGET (nav_popup->viewer), &allocation);
-       nav_popup->visible_area.width = (allocation.width - GTH_IMAGE_VIEWER_FRAME_BORDER2) * 
nav_popup->zoom_factor;
-       nav_popup->visible_area.width = MAX (nav_popup->visible_area.width, POPUP_BORDER);
-       nav_popup->visible_area.width = MIN (nav_popup->visible_area.width, nav_popup->popup_width);
-
-       nav_popup->visible_area.height = (allocation.height - GTH_IMAGE_VIEWER_FRAME_BORDER2) * 
nav_popup->zoom_factor;
-       nav_popup->visible_area.height = MAX (nav_popup->visible_area.height, POPUP_BORDER);
-       nav_popup->visible_area.height = MIN (nav_popup->visible_area.height, nav_popup->popup_height);
-
-       /* visible area position */
-
-       gth_image_viewer_get_scroll_offset (nav_popup->viewer, &scroll_offset_x, &scroll_offset_y);
-       nav_popup->visible_area.x = scroll_offset_x * (nav_popup->zoom_factor * nav_popup->quality_zoom);
-       nav_popup->visible_area.y = scroll_offset_y * (nav_popup->zoom_factor * nav_popup->quality_zoom);
-
-       /* popup window position */
-
-       nav_popup->popup_x = MIN ((int) nav_popup->x_root - nav_popup->visible_area.x - POPUP_BORDER - 
nav_popup->visible_area.width / 2,
-                               gdk_screen_width () - nav_popup->popup_width - POPUP_BORDER_2);
-       nav_popup->popup_y = MIN ((int) nav_popup->y_root - nav_popup->visible_area.y - POPUP_BORDER - 
nav_popup->visible_area.height / 2,
-                               gdk_screen_height () - nav_popup->popup_height - POPUP_BORDER_2);
+       gtk_widget_destroy (nav_popup->popup_win);
+       g_free (nav_popup);
 }
 
 
@@ -430,43 +346,16 @@ popup_window_event_cb (GtkWidget *widget,
                       GdkEvent  *event,
                       gpointer   data)
 {
-       NavigatorPopup  *nav_popup = data;
-       GthImageViewer  *viewer = nav_popup->viewer;
-       GdkModifierType  mask;
-       int              mx, my;
-       double           x, y;
+       NavigatorPopup *nav_popup = data;
 
        switch (event->type) {
        case GDK_BUTTON_RELEASE:
-               /* Release keyboard focus. */
-               /*gdk_keyboard_ungrab (GDK_CURRENT_TIME);*/
-               /*gtk_grab_remove (nav_popup->popup_win);*/
-               gdk_device_ungrab (gdk_event_get_device (event), ((GdkEventButton *)event)->time);
-
-               gtk_widget_destroy (nav_popup->popup_win);
-               cairo_surface_destroy (nav_popup->image);
-               g_free (nav_popup);
-
+               navigator_popup_close (nav_popup);
                return TRUE;
 
        case GDK_MOTION_NOTIFY:
-               gdk_window_get_device_position (gtk_widget_get_window (widget),
-                                               gdk_event_get_device (event),
-                                               &mx,
-                                               &my,
-                                               &mask);
-
-               get_visible_area_origin_as_double (nav_popup, mx, my, &x, &y);
-               nav_popup->visible_area.x = (int) x;
-               nav_popup->visible_area.y = (int) y;
-
-               mx = (int) (x / (nav_popup->quality_zoom * nav_popup->zoom_factor));
-               my = (int) (y / (nav_popup->quality_zoom * nav_popup->zoom_factor));
-               gth_image_viewer_set_scroll_offset (viewer, mx, my);
-
-               gtk_widget_queue_draw (widget);
-               gdk_window_process_updates (gtk_widget_get_window (widget), TRUE);
-
+               gtk_widget_event (nav_popup->overview, event);
+               gtk_widget_queue_draw (nav_popup->overview);
                return TRUE;
 
        case GDK_KEY_PRESS:
@@ -476,22 +365,19 @@ popup_window_event_cb (GtkWidget *widget,
                case GDK_KEY_1:
                        switch (event->key.keyval) {
                        case GDK_KEY_plus:
-                               gth_image_viewer_zoom_in (viewer);
+                               gth_image_viewer_zoom_in (nav_popup->viewer);
                                break;
                        case GDK_KEY_minus:
-                               gth_image_viewer_zoom_out (viewer);
+                               gth_image_viewer_zoom_out (nav_popup->viewer);
                                break;
                        case GDK_KEY_1:
-                               gth_image_viewer_set_zoom (viewer, 1.0);
+                               gth_image_viewer_set_zoom (nav_popup->viewer, 1.0);
                                break;
                        }
+                       break;
 
-                       update_popup_geometry (nav_popup);
-                       nav_popup->visible_area.x = MAX (nav_popup->visible_area.x, 0);
-                       nav_popup->visible_area.x = MIN (nav_popup->visible_area.x, nav_popup->popup_width - 
nav_popup->visible_area.width);
-                       nav_popup->visible_area.y = MAX (nav_popup->visible_area.y, 0);
-                       nav_popup->visible_area.y = MIN (nav_popup->visible_area.y, nav_popup->popup_height - 
nav_popup->visible_area.height);
-                       gtk_widget_queue_draw (widget);
+               case GDK_KEY_Escape:
+                       navigator_popup_close (nav_popup);
                        break;
 
                default:
@@ -508,108 +394,30 @@ popup_window_event_cb (GtkWidget *widget,
 
 
 static void
-nav_window_grab_pointer (NavigatorPopup *nav_popup,
-                        GdkDevice      *device)
-{
-       GdkCursor *cursor;
-
-       gtk_grab_add (nav_popup->popup_win);
-
-       cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (nav_popup->viewer)), 
GDK_FLEUR);
-       gdk_device_grab (device,
-                        gtk_widget_get_window (nav_popup->popup_win),
-                        GDK_OWNERSHIP_WINDOW,
-                        TRUE,
-                        (GDK_BUTTON_RELEASE_MASK
-                         | GDK_POINTER_MOTION_HINT_MASK
-                         | GDK_BUTTON_MOTION_MASK),
-                        cursor,
-                        0);
-       g_object_unref (cursor);
-
-       /* Capture keyboard events. */
-
-       /*gdk_keyboard_grab (gtk_widget_get_window (nav_popup->popup_win), TRUE, GDK_CURRENT_TIME);*/
-        gtk_widget_grab_focus (nav_popup->popup_win);
-}
-
-
-static gboolean
-navigator_popup_draw_cb (GtkWidget      *widget,
-                        cairo_t        *cr,
-                        NavigatorPopup *nav_popup)
-{
-       if (nav_popup->image == NULL)
-               return FALSE;
-
-       cairo_save (cr);
-       cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
-       cairo_set_source_surface (cr, nav_popup->image, 0, 0);
-       cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
-       cairo_rectangle (cr,
-                        0, 0,
-                        cairo_image_surface_get_width (nav_popup->image),
-                        cairo_image_surface_get_height (nav_popup->image));
-       cairo_fill (cr);
-       cairo_restore (cr);
-
-       cairo_save (cr);
-       cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
-       cairo_rectangle (cr, 0, 0, nav_popup->popup_width, nav_popup->popup_height);
-       cairo_fill (cr);
-       cairo_restore (cr);
-
-       if ((nav_popup->visible_area.width < nav_popup->popup_width)
-           || (nav_popup->visible_area.height < nav_popup->popup_height))
-       {
-               cairo_save (cr);
-               cairo_rectangle (cr,
-                                nav_popup->visible_area.x,
-                                nav_popup->visible_area.y,
-                                nav_popup->visible_area.width,
-                                nav_popup->visible_area.height);
-               cairo_clip (cr);
-               cairo_set_source_surface (cr, nav_popup->image, 0, 0);
-               cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
-               cairo_rectangle (cr,
-                                0, 0,
-                                cairo_image_surface_get_width (nav_popup->image),
-                                cairo_image_surface_get_height (nav_popup->image));
-               cairo_fill (cr);
-               cairo_restore (cr);
-
-               cairo_save (cr);
-               cairo_set_line_width (cr, VISIBLE_AREA_BORDER);
-               cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
-               cairo_rectangle (cr,
-                                nav_popup->visible_area.x + 1.0,
-                                nav_popup->visible_area.y + 1.0,
-                                nav_popup->visible_area.width - VISIBLE_AREA_BORDER,
-                                nav_popup->visible_area.height - VISIBLE_AREA_BORDER);
-               cairo_stroke (cr);
-               cairo_restore (cr);
-       }
-
-       return TRUE;
-}
-
-
-static void
 navigator_event_area_button_press_event_cb (GtkWidget      *widget,
                                            GdkEventButton *event,
                                            gpointer        user_data)
 {
-       GthImageNavigator *self = user_data;
-       NavigatorPopup    *nav_popup;
-       GtkWidget         *out_frame;
-       GtkWidget         *in_frame;
+       GthImageNavigator       *self = user_data;
+       NavigatorPopup          *nav_popup;
+       GtkWidget               *out_frame;
+       GtkWidget               *in_frame;
+       int                      popup_x, popup_y;
+       int                      popup_width, popup_height;
+       cairo_rectangle_int_t    visible_area;
 
        if ((self->priv->viewer == NULL) || gth_image_viewer_is_void (GTH_IMAGE_VIEWER (self->priv->viewer)))
                return;
 
+       /* create the popup and its content */
+
        nav_popup = g_new0 (NavigatorPopup, 1);
        nav_popup->viewer = GTH_IMAGE_VIEWER (self->priv->viewer);
        nav_popup->popup_win = gtk_window_new (GTK_WINDOW_POPUP);
+       g_signal_connect (G_OBJECT (nav_popup->popup_win),
+                         "event",
+                         G_CALLBACK (popup_window_event_cb),
+                         nav_popup);
        gtk_window_set_wmclass (GTK_WINDOW (nav_popup->popup_win), "", "gthumb_navigator");
 
        out_frame = gtk_frame_new (NULL);
@@ -620,45 +428,40 @@ navigator_event_area_button_press_event_cb (GtkWidget      *widget,
        gtk_frame_set_shadow_type (GTK_FRAME (in_frame), GTK_SHADOW_IN);
        gtk_container_add (GTK_CONTAINER (out_frame), in_frame);
 
-       nav_popup->preview = gtk_drawing_area_new ();
-       gtk_container_add (GTK_CONTAINER (in_frame), nav_popup->preview);
-       g_signal_connect (G_OBJECT (nav_popup->preview),
-                         "draw",
-                         G_CALLBACK (navigator_popup_draw_cb),
-                         nav_popup);
-
-       nav_popup->x_root = event->x_root;
-       nav_popup->y_root = event->y_root;
-
-       nav_popup->image_width = gth_image_viewer_get_image_width (GTH_IMAGE_VIEWER (self->priv->viewer));
-       nav_popup->image_height = gth_image_viewer_get_image_height (GTH_IMAGE_VIEWER (self->priv->viewer));
-       gth_image_viewer_get_original_size (GTH_IMAGE_VIEWER (self->priv->viewer),
-                                           &nav_popup->original_width,
-                                           &nav_popup->original_height);
-       update_popup_geometry (nav_popup);
-
-       g_signal_connect (G_OBJECT (nav_popup->popup_win),
-                         "event",
-                         G_CALLBACK (popup_window_event_cb),
-                         nav_popup);
-
+       nav_popup->overview = gth_image_overview_new (GTH_IMAGE_VIEWER (self->priv->viewer));
+       gtk_container_add (GTK_CONTAINER (in_frame), nav_popup->overview);
+
+       /* show the popup */
+
+       gth_image_overview_get_size (GTH_IMAGE_OVERVIEW (nav_popup->overview),
+                                    &popup_width,
+                                    &popup_height);
+       gth_image_overview_get_visible_area (GTH_IMAGE_OVERVIEW (nav_popup->overview),
+                                            &visible_area.x,
+                                            &visible_area.y,
+                                            &visible_area.width,
+                                            &visible_area.height);
+       popup_x = MIN (event->x_root - visible_area.x - POPUP_BORDER - visible_area.width / 2,
+                      gdk_screen_width () - popup_width - POPUP_BORDER_2);
+       popup_y = MIN (event->y_root - visible_area.y - POPUP_BORDER - visible_area.height / 2,
+                      gdk_screen_height () - popup_height - POPUP_BORDER_2);
        gtk_window_move (GTK_WINDOW (nav_popup->popup_win),
-                        nav_popup->popup_x,
-                        nav_popup->popup_y);
-
+                        popup_x,
+                        popup_y);
        gtk_window_set_default_size (GTK_WINDOW (nav_popup->popup_win),
-                                    nav_popup->popup_width + POPUP_BORDER_2,
-                                    nav_popup->popup_height + POPUP_BORDER_2);
-
+                                    popup_width + POPUP_BORDER_2,
+                                    popup_height + POPUP_BORDER_2);
        gtk_widget_show_all (nav_popup->popup_win);
 
-       nav_window_grab_pointer (nav_popup, gdk_event_get_device ((GdkEvent *) event));
+       gth_image_overview_activate_scrolling (GTH_IMAGE_OVERVIEW (nav_popup->overview), TRUE, event);
 }
 
 
 static void
 gth_image_navigator_init (GthImageNavigator *self)
 {
+       GtkWidget *navigator_icon;
+
        self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), GTH_TYPE_IMAGE_NAVIGATOR, GthImageNavigatorPrivate);
 
        gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
@@ -680,10 +483,12 @@ gth_image_navigator_init (GthImageNavigator *self)
 
        /* navigator event area */
 
+       navigator_icon = gtk_image_new_from_icon_name ("image-navigator", GTK_ICON_SIZE_MENU);
+       gtk_image_set_pixel_size (GTK_IMAGE (navigator_icon), 12);
+
        self->priv->navigator_event_area = gtk_event_box_new ();
        gtk_widget_set_parent (GTK_WIDGET (self->priv->navigator_event_area), GTK_WIDGET (self));
-       gtk_container_add (GTK_CONTAINER (self->priv->navigator_event_area),
-                          gtk_image_new_from_icon_name ("image-navigator", GTK_ICON_SIZE_MENU));
+       gtk_container_add (GTK_CONTAINER (self->priv->navigator_event_area), navigator_icon);
        g_signal_connect (G_OBJECT (self->priv->navigator_event_area),
                          "button_press_event",
                          G_CALLBACK (navigator_event_area_button_press_event_cb),
@@ -695,7 +500,7 @@ gth_image_navigator_init (GthImageNavigator *self)
 }
 
 
-G_DEFINE_TYPE(GthImageNavigator, gth_image_navigator, GTK_TYPE_CONTAINER)
+G_DEFINE_TYPE (GthImageNavigator, gth_image_navigator, GTK_TYPE_CONTAINER)
 
 
 GtkWidget *
diff --git a/gthumb/gth-image-overview.c b/gthumb/gth-image-overview.c
new file mode 100644
index 0000000..db07a9c
--- /dev/null
+++ b/gthumb/gth-image-overview.c
@@ -0,0 +1,513 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2013 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <gdk/gdkkeysyms.h>
+#include "cairo-scale.h"
+#include "gth-image-overview.h"
+#include "gth-image-viewer.h"
+
+
+#define VISIBLE_AREA_BORDER 2.0
+#define MAX_SIZE 180
+
+
+/* Properties */
+enum {
+        PROP_0,
+        PROP_VIEWER
+};
+
+
+struct _GthImageOverviewPrivate {
+       GthImageViewer          *viewer;
+       cairo_surface_t         *image;
+       int                      original_width;
+       int                      original_height;
+       int                      max_width;
+       int                      max_height;
+       int                      overview_width;
+       int                      overview_height;
+       cairo_rectangle_int_t    visible_area;
+       double                   zoom_factor;
+       double                   quality_zoom;
+       gulong                   zoom_changed_id;
+       gboolean                 scrolling_active;
+};
+
+
+G_DEFINE_TYPE_WITH_CODE (GthImageOverview, gth_image_overview, GTK_TYPE_WIDGET,
+                        G_ADD_PRIVATE (GthImageOverview))
+
+
+static void
+gth_image_overview_finalize (GObject *object)
+{
+       GthImageOverview *self;
+
+       g_return_if_fail (GTH_IS_IMAGE_OVERVIEW (object));
+
+       self = GTH_IMAGE_OVERVIEW (object);
+
+       if (self->priv->zoom_changed_id > 0) {
+               g_signal_handler_disconnect (self->priv->viewer, self->priv->zoom_changed_id);
+               self->priv->zoom_changed_id = 0;
+       }
+       cairo_surface_destroy (self->priv->image);
+
+       G_OBJECT_CLASS (gth_image_overview_parent_class)->finalize (object);
+}
+
+
+static void
+_gth_image_overview_update_visible_area (GthImageOverview *self)
+{
+       cairo_surface_t  *image;
+       int               zoomed_width;
+       int               zoomed_height;
+       GtkAllocation     allocation;
+       int               scroll_offset_x;
+       int               scroll_offset_y;
+
+       image = gth_image_viewer_get_current_image (self->priv->viewer);
+       if (image == NULL) {
+               cairo_surface_destroy (self->priv->image);
+               self->priv->image = NULL;
+               self->priv->max_width = 0;
+               self->priv->max_height = 0;
+               self->priv->overview_width = 0;
+               self->priv->overview_height = 0;
+               self->priv->visible_area.width = 0;
+               self->priv->visible_area.height = 0;
+               gtk_widget_queue_draw (GTK_WIDGET (self));
+
+               return;
+       }
+
+       gth_image_viewer_get_original_size (self->priv->viewer, &self->priv->original_width, 
&self->priv->original_height);
+       zoomed_width = self->priv->original_width * gth_image_viewer_get_zoom (self->priv->viewer);
+       zoomed_height = self->priv->original_height * gth_image_viewer_get_zoom (self->priv->viewer);
+       self->priv->max_width = MIN (zoomed_width, MAX_SIZE);
+       self->priv->max_height = MIN (zoomed_width, MAX_SIZE);
+       self->priv->zoom_factor = MIN ((double) (self->priv->max_width) / zoomed_width,
+                                      (double) (self->priv->max_height) / zoomed_height);
+       self->priv->quality_zoom = (double) self->priv->original_width / cairo_image_surface_get_width 
(image);
+       self->priv->overview_width = MAX ((int) floor (self->priv->zoom_factor * zoomed_width + 0.5), 1);
+       self->priv->overview_height = MAX ((int) floor (self->priv->zoom_factor * zoomed_height + 0.5), 1);
+
+       cairo_surface_destroy (self->priv->image);
+       self->priv->image = _cairo_image_surface_scale_bilinear (image,
+                                                                self->priv->overview_width,
+                                                                self->priv->overview_height);
+
+       /* visible area size */
+
+       gtk_widget_get_allocation (GTK_WIDGET (self->priv->viewer), &allocation);
+       self->priv->visible_area.width = (allocation.width - (gth_image_viewer_get_frame_border 
(self->priv->viewer) * 2)) * self->priv->zoom_factor;
+       self->priv->visible_area.width = MIN (self->priv->visible_area.width, self->priv->max_width);
+       self->priv->visible_area.height = (allocation.height - (gth_image_viewer_get_frame_border 
(self->priv->viewer) * 2)) * self->priv->zoom_factor;
+       self->priv->visible_area.height = MIN (self->priv->visible_area.height, self->priv->max_height);
+
+       /* visible area position */
+
+       gth_image_viewer_get_scroll_offset (self->priv->viewer, &scroll_offset_x, &scroll_offset_y);
+       self->priv->visible_area.x = scroll_offset_x * (self->priv->zoom_factor * self->priv->quality_zoom);
+       self->priv->visible_area.y = scroll_offset_y * (self->priv->zoom_factor * self->priv->quality_zoom);
+
+       gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+
+static void
+viewer_zoom_changed_cb (GthImageViewer *viewer,
+                       gpointer        user_data)
+{
+       _gth_image_overview_update_visible_area (GTH_IMAGE_OVERVIEW (user_data));
+}
+
+
+static void
+_gth_image_overview_set_viewer (GthImageOverview *self,
+                               GtkWidget         *viewer)
+{
+       if (self->priv->viewer == (GthImageViewer *) viewer)
+               return;
+
+       if (self->priv->zoom_changed_id > 0) {
+               g_signal_handler_disconnect (self->priv->viewer, self->priv->zoom_changed_id);
+               self->priv->zoom_changed_id = 0;
+       }
+       self->priv->viewer = NULL;
+
+       if (viewer == NULL)
+               return;
+
+       self->priv->viewer = (GthImageViewer *) viewer;
+       _gth_image_overview_update_visible_area (self);
+       self->priv->zoom_changed_id = g_signal_connect (self->priv->viewer,
+                                                       "zoom-changed",
+                                                       G_CALLBACK (viewer_zoom_changed_cb),
+                                                       self);
+
+       g_object_notify (G_OBJECT (self), "viewer");
+}
+
+
+static void
+gth_image_overview_set_property (GObject      *object,
+                                guint         property_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+       GthImageOverview *self = GTH_IMAGE_OVERVIEW (object);
+
+       switch (property_id) {
+       case PROP_VIEWER:
+               _gth_image_overview_set_viewer (self, g_value_get_object (value));
+               break;
+
+       default:
+               break;
+       }
+}
+
+
+static void
+gth_image_overview_get_property (GObject    *object,
+                                guint       property_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+       GthImageOverview *self = GTH_IMAGE_OVERVIEW (object);
+
+       switch (property_id) {
+       case PROP_VIEWER:
+               g_value_set_object (value, self->priv->viewer);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+               break;
+       }
+}
+
+
+static void
+gth_image_overview_realize (GtkWidget *widget)
+{
+       GtkAllocation  allocation;
+       GdkWindowAttr  attributes;
+       int            attributes_mask;
+       GdkWindow     *window;
+
+       g_return_if_fail (GTH_IS_IMAGE_OVERVIEW (widget));
+
+       gtk_widget_set_realized (widget, TRUE);
+       gtk_widget_get_allocation (widget, &allocation);
+
+       attributes.window_type = GDK_WINDOW_CHILD;
+       attributes.x           = allocation.x;
+       attributes.y           = allocation.y;
+       attributes.width       = allocation.width;
+       attributes.height      = allocation.height;
+       attributes.wclass      = GDK_INPUT_OUTPUT;
+       attributes.visual      = gtk_widget_get_visual (widget);
+       attributes.event_mask  = (gtk_widget_get_events (widget)
+                                 | GDK_EXPOSURE_MASK
+                                 | GDK_BUTTON_PRESS_MASK
+                                 | GDK_BUTTON_RELEASE_MASK
+                                 | GDK_POINTER_MOTION_MASK
+                                 | GDK_POINTER_MOTION_HINT_MASK
+                                 | GDK_BUTTON_MOTION_MASK);
+       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+       window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
+       gtk_widget_register_window (widget, window);
+       gtk_widget_set_window (widget, window);
+       gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
+}
+
+
+static void
+gth_image_overview_size_allocate (GtkWidget     *widget,
+                                 GtkAllocation *allocation)
+{
+       gtk_widget_set_allocation (widget, allocation);
+       if (gtk_widget_get_realized (widget))
+               gdk_window_move_resize (gtk_widget_get_window (widget),
+                                       allocation->x,
+                                       allocation->y,
+                                       allocation->width,
+                                       allocation->height);
+       _gth_image_overview_update_visible_area (GTH_IMAGE_OVERVIEW (widget));
+}
+
+
+static gboolean
+gth_image_overview_draw (GtkWidget *widget,
+                        cairo_t   *cr)
+{
+       GthImageOverview *self;
+
+       self = GTH_IMAGE_OVERVIEW (widget);
+
+       if (self->priv->image == NULL)
+               return FALSE;
+
+       cairo_save (cr);
+       cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+       cairo_set_source_surface (cr, self->priv->image, 0, 0);
+       cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
+       cairo_rectangle (cr, 0, 0, self->priv->overview_width, self->priv->overview_height);
+       cairo_fill (cr);
+       cairo_restore (cr);
+
+       cairo_save (cr);
+       cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
+       cairo_rectangle (cr, 0, 0, self->priv->overview_width, self->priv->overview_height);
+       cairo_fill (cr);
+       cairo_restore (cr);
+
+       if ((self->priv->visible_area.width < self->priv->overview_width)
+           || (self->priv->visible_area.height < self->priv->overview_height))
+       {
+               cairo_save (cr);
+               cairo_rectangle (cr,
+                                self->priv->visible_area.x,
+                                self->priv->visible_area.y,
+                                self->priv->visible_area.width,
+                                self->priv->visible_area.height);
+               cairo_clip (cr);
+               cairo_set_source_surface (cr, self->priv->image, 0, 0);
+               cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_FAST);
+               cairo_rectangle (cr, 0, 0, self->priv->overview_width, self->priv->overview_height);
+               cairo_fill (cr);
+               cairo_restore (cr);
+
+               cairo_save (cr);
+               cairo_set_line_width (cr, VISIBLE_AREA_BORDER);
+               cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+               cairo_rectangle (cr,
+                                self->priv->visible_area.x + 1.0,
+                                self->priv->visible_area.y + 1.0,
+                                self->priv->visible_area.width - VISIBLE_AREA_BORDER,
+                                self->priv->visible_area.height - VISIBLE_AREA_BORDER);
+               cairo_stroke (cr);
+               cairo_restore (cr);
+       }
+
+       return TRUE;
+}
+
+
+static gboolean
+gth_image_overview_button_press_event (GtkWidget       *widget,
+                                      GdkEventButton  *event)
+{
+       gth_image_overview_activate_scrolling (GTH_IMAGE_OVERVIEW (widget), TRUE, event);
+        return FALSE;
+}
+
+
+static gboolean
+gth_image_overview_button_release_event (GtkWidget      *widget,
+                                        GdkEventButton *event)
+{
+       gth_image_overview_activate_scrolling (GTH_IMAGE_OVERVIEW (widget), FALSE, event);
+       return FALSE;
+}
+
+
+/* -- gth_image_overview_motion_notify_event -- */
+
+
+static void
+get_visible_area_origin_as_double (GthImageOverview *self,
+                                  int               mx,
+                                  int               my,
+                                  double           *x,
+                                  double           *y)
+{
+       *x = MIN (mx, self->priv->max_width);
+       *y = MIN (my, self->priv->max_height);
+
+       if (*x - self->priv->visible_area.width / 2.0 < 0.0)
+               *x = self->priv->visible_area.width / 2.0;
+
+       if (*y - self->priv->visible_area.height / 2.0 < 0.0)
+               *y = self->priv->visible_area.height / 2.0;
+
+       if (*x + self->priv->visible_area.width / 2.0 > self->priv->overview_width - 0)
+               *x = self->priv->overview_width - 0 - self->priv->visible_area.width / 2.0;
+
+       if (*y + self->priv->visible_area.height / 2.0 > self->priv->overview_height - 0)
+               *y = self->priv->overview_height - 0 - self->priv->visible_area.height / 2.0;
+
+       *x = *x - self->priv->visible_area.width / 2.0;
+       *y = *y - self->priv->visible_area.height / 2.0;
+}
+
+
+static gboolean
+gth_image_overview_motion_notify_event (GtkWidget      *widget,
+                                       GdkEventMotion *event)
+{
+       GthImageOverview *self;
+       int               mx, my;
+       double            x, y;
+
+       self = GTH_IMAGE_OVERVIEW (widget);
+
+       if (! self->priv->scrolling_active)
+               return FALSE;
+
+       gdk_window_get_device_position (gtk_widget_get_window (widget),
+                                       gdk_event_get_device ((GdkEvent *) event),
+                                       &mx,
+                                       &my,
+                                       NULL);
+
+       get_visible_area_origin_as_double (self, mx, my, &x, &y);
+       self->priv->visible_area.x = (int) x;
+       self->priv->visible_area.y = (int) y;
+
+       mx = (int) (x / (self->priv->quality_zoom * self->priv->zoom_factor));
+       my = (int) (y / (self->priv->quality_zoom * self->priv->zoom_factor));
+       gth_image_viewer_set_scroll_offset (self->priv->viewer, mx, my);
+
+       return FALSE;
+}
+
+
+static void
+gth_image_overview_class_init (GthImageOverviewClass *klass)
+{
+       GObjectClass   *object_class;
+       GtkWidgetClass *widget_class;
+
+       object_class = (GObjectClass *) klass;
+       object_class->finalize = gth_image_overview_finalize;
+       object_class->set_property = gth_image_overview_set_property;
+       object_class->get_property = gth_image_overview_get_property;
+
+       widget_class = (GtkWidgetClass *) klass;
+       widget_class->realize = gth_image_overview_realize;
+       widget_class->size_allocate = gth_image_overview_size_allocate;
+       widget_class->draw = gth_image_overview_draw;
+       widget_class->button_press_event = gth_image_overview_button_press_event;
+       widget_class->button_release_event = gth_image_overview_button_release_event;
+       widget_class->motion_notify_event = gth_image_overview_motion_notify_event;
+
+       /* properties */
+
+       g_object_class_install_property (object_class,
+                                        PROP_VIEWER,
+                                        g_param_spec_object ("viewer",
+                                                              "Viewer",
+                                                              "The image viewer to use",
+                                                              GTH_TYPE_IMAGE_VIEWER,
+                                                              G_PARAM_READWRITE));
+}
+
+
+static void
+gth_image_overview_init (GthImageOverview *self)
+{
+       self->priv = gth_image_overview_get_instance_private (self);
+       self->priv->viewer = NULL;
+       self->priv->image = NULL;
+       self->priv->visible_area.width = 0;
+       self->priv->visible_area.height = 0;
+       self->priv->zoom_factor = 1.0;
+       self->priv->zoom_changed_id = 0;
+       self->priv->scrolling_active = FALSE;
+}
+
+
+GtkWidget *
+gth_image_overview_new (GthImageViewer *viewer)
+{
+       g_return_val_if_fail (viewer != NULL, NULL);
+       return (GtkWidget *) g_object_new (GTH_TYPE_IMAGE_OVERVIEW, "viewer", viewer, NULL);
+}
+
+
+void
+gth_image_overview_get_size (GthImageOverview  *self,
+                            int                *width,
+                            int                *height)
+{
+       if (width != NULL) *width = self->priv->overview_width;
+       if (height != NULL) *height = self->priv->overview_height;
+}
+
+
+void
+gth_image_overview_get_visible_area (GthImageOverview  *self,
+                                    int                *x,
+                                    int                *y,
+                                    int                *width,
+                                    int                *height)
+{
+       if (x != NULL) *x = self->priv->visible_area.x;
+       if (y != NULL) *y = self->priv->visible_area.y;
+       if (width != NULL) *width = self->priv->visible_area.width;
+       if (height != NULL) *height = self->priv->visible_area.height;
+}
+
+
+void
+gth_image_overview_activate_scrolling (GthImageOverview        *self,
+                                      gboolean          active,
+                                      GdkEventButton   *event)
+{
+       g_return_if_fail (event != NULL);
+
+       if (active && ! self->priv->scrolling_active) {
+               GdkCursor *cursor;
+
+               gtk_widget_realize (GTK_WIDGET (self));
+               gtk_grab_add (GTK_WIDGET (self));
+
+               /* capture mouse events */
+
+               cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET 
(self->priv->viewer)), GDK_FLEUR);
+               gdk_device_grab (gdk_event_get_device ((GdkEvent *) event),
+                                gtk_widget_get_window (GTK_WIDGET (self)),
+                                GDK_OWNERSHIP_WINDOW,
+                                FALSE,
+                                GDK_ALL_EVENTS_MASK,
+                                cursor,
+                                event->time);
+               g_object_unref (cursor);
+
+               /* capture keyboard events. */
+
+               gdk_keyboard_grab (gtk_widget_get_window (GTK_WIDGET (self)), FALSE, event->time);
+               gtk_widget_grab_focus (GTK_WIDGET (self));
+       }
+       else if (! active && self->priv->scrolling_active) {
+               gdk_device_ungrab (gdk_event_get_device ((GdkEvent *) event), event->time);
+               gdk_keyboard_ungrab (event->time);
+       }
+
+       self->priv->scrolling_active = active;
+}
diff --git a/gthumb/gth-image-overview.h b/gthumb/gth-image-overview.h
new file mode 100644
index 0000000..3a88a8c
--- /dev/null
+++ b/gthumb/gth-image-overview.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2013 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_IMAGE_OVERVIEW_H
+#define GTH_IMAGE_OVERVIEW_H
+
+#include <gtk/gtk.h>
+#include "gth-image-viewer.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_IMAGE_OVERVIEW            (gth_image_overview_get_type ())
+#define GTH_IMAGE_OVERVIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_IMAGE_OVERVIEW, 
GthImageOverview))
+#define GTH_IMAGE_OVERVIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_IMAGE_OVERVIEW, 
GthImageOverviewClass))
+#define GTH_IS_IMAGE_OVERVIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_IMAGE_OVERVIEW))
+#define GTH_IS_IMAGE_OVERVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_IMAGE_OVERVIEW))
+#define GTH_IMAGE_OVERVIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_IMAGE_OVERVIEW, 
GthImageOverviewClass))
+
+typedef struct _GthImageOverview        GthImageOverview;
+typedef struct _GthImageOverviewClass   GthImageOverviewClass;
+typedef struct _GthImageOverviewPrivate GthImageOverviewPrivate;
+
+struct _GthImageOverview {
+       GtkWidget __parent;
+       GthImageOverviewPrivate *priv;
+};
+
+struct _GthImageOverviewClass {
+       GtkWidgetClass __parent;
+};
+
+GType          gth_image_overview_get_type             (void);
+GtkWidget *    gth_image_overview_new                  (GthImageViewer         *viewer);
+void            gth_image_overview_get_size            (GthImageOverview       *viewer,
+                                                        int                    *width,
+                                                        int                    *height);
+void            gth_image_overview_get_visible_area    (GthImageOverview       *viewer,
+                                                        int                    *x,
+                                                        int                    *y,
+                                                        int                    *width,
+                                                        int                    *height);
+void           gth_image_overview_activate_scrolling   (GthImageOverview       *self,
+                                                        gboolean                active,
+                                                        GdkEventButton         *event);
+
+G_END_DECLS
+
+#endif /* GTH_IMAGE_OVERVIEW_H */
+


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