home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 February / CMCD0205.ISO / Linux / gimp-2.2.0.tar.gz / gimp-2.2.0.tar / gimp-2.2.0 / libgimpwidgets / gimpscrolledpreview.c < prev    next >
C/C++ Source or Header  |  2004-10-27  |  22KB  |  633 lines

  1. /* LIBGIMP - The GIMP Library
  2.  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
  3.  *
  4.  * gimpscrolledpreview.c
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * This library is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this library; if not, write to the
  18.  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  19.  * Boston, MA 02111-1307, USA.
  20.  */
  21.  
  22. #include "config.h"
  23.  
  24. #include <gtk/gtk.h>
  25.  
  26. #include "libgimpmath/gimpmath.h"
  27.  
  28. #include "gimpwidgets.h"
  29.  
  30. #include "gimpscrolledpreview.h"
  31.  
  32. #include "libgimp/libgimp-intl.h"
  33.  
  34.  
  35. #define POPUP_SIZE       100
  36. #define PEN_WIDTH          3
  37.  
  38.  
  39. static void      gimp_scrolled_preview_class_init          (GimpScrolledPreviewClass *klass);
  40. static void      gimp_scrolled_preview_init                (GimpScrolledPreview      *preview);
  41. static void      gimp_scrolled_preview_dispose             (GObject                  *object);
  42.  
  43. static void      gimp_scrolled_preview_area_realize        (GtkWidget                *widget,
  44.                                                             GimpScrolledPreview      *preview);
  45. static void      gimp_scrolled_preview_area_unrealize      (GtkWidget                *widget,
  46.                                                             GimpScrolledPreview      *preview);
  47. static void      gimp_scrolled_preview_area_size_allocate  (GtkWidget                *widget,
  48.                                                             GtkAllocation            *allocation,
  49.                                                             GimpScrolledPreview      *preview);
  50. static gboolean  gimp_scrolled_preview_area_event          (GtkWidget                *area,
  51.                                                             GdkEvent                 *event,
  52.                                                             GimpScrolledPreview      *preview);
  53.  
  54. static void      gimp_scrolled_preview_h_scroll            (GtkAdjustment            *hadj,
  55.                                                             GimpPreview              *preview);
  56. static void      gimp_scrolled_preview_v_scroll            (GtkAdjustment            *vadj,
  57.                                                             GimpPreview              *preview);
  58.  
  59. static gboolean  gimp_scrolled_preview_nav_button_press    (GtkWidget                *widget,
  60.                                                             GdkEventButton           *event,
  61.                                                             GimpScrolledPreview      *preview);
  62.  
  63. static void      gimp_scrolled_preview_nav_popup_realize   (GtkWidget                *widget,
  64.                                                             GimpScrolledPreview      *preview);
  65. static void      gimp_scrolled_preview_nav_popup_unrealize (GtkWidget                *widget,
  66.                                                             GimpScrolledPreview      *preview);
  67. static gboolean  gimp_scrolled_preview_nav_popup_event     (GtkWidget                *widget,
  68.                                                             GdkEvent                 *event,
  69.                                                             GimpScrolledPreview      *preview);
  70. static gboolean  gimp_scrolled_preview_nav_popup_expose    (GtkWidget                *widget,
  71.                                                             GdkEventExpose           *event,
  72.                                                             GimpScrolledPreview      *preview);
  73. static void      gimp_scrolled_preview_set_cursor          (GimpPreview              *preview);
  74.  
  75.  
  76. static GimpPreviewClass *parent_class = NULL;
  77.  
  78.  
  79. GType
  80. gimp_scrolled_preview_get_type (void)
  81. {
  82.   static GType preview_type = 0;
  83.  
  84.   if (! preview_type)
  85.     {
  86.       static const GTypeInfo preview_info =
  87.       {
  88.         sizeof (GimpScrolledPreviewClass),
  89.         (GBaseInitFunc) NULL,
  90.         (GBaseFinalizeFunc) NULL,
  91.         (GClassInitFunc) gimp_scrolled_preview_class_init,
  92.         NULL,           /* class_finalize */
  93.         NULL,           /* class_data     */
  94.         sizeof (GimpScrolledPreview),
  95.         0,              /* n_preallocs    */
  96.         (GInstanceInitFunc) gimp_scrolled_preview_init,
  97.       };
  98.  
  99.       preview_type = g_type_register_static (GIMP_TYPE_PREVIEW,
  100.                                              "GimpScrolledPreview",
  101.                                              &preview_info,
  102.                                              G_TYPE_FLAG_ABSTRACT);
  103.     }
  104.  
  105.   return preview_type;
  106. }
  107.  
  108. static void
  109. gimp_scrolled_preview_class_init (GimpScrolledPreviewClass *klass)
  110. {
  111.   GObjectClass     *object_class  = G_OBJECT_CLASS (klass);
  112.   GimpPreviewClass *preview_class = GIMP_PREVIEW_CLASS (klass);
  113.  
  114.   parent_class = g_type_class_peek_parent (klass);
  115.  
  116.   object_class->dispose      = gimp_scrolled_preview_dispose;
  117.  
  118.   preview_class->set_cursor  = gimp_scrolled_preview_set_cursor;
  119. }
  120.  
  121. static void
  122. gimp_scrolled_preview_init (GimpScrolledPreview *preview)
  123. {
  124.   GtkWidget *image;
  125.   GtkObject *adj;
  126.  
  127.   preview->in_drag   = FALSE;
  128.   preview->nav_popup = NULL;
  129.  
  130.   /*  scrollbars  */
  131.   adj = gtk_adjustment_new (0, 0, GIMP_PREVIEW (preview)->width - 1, 1.0,
  132.                             GIMP_PREVIEW (preview)->width,
  133.                             GIMP_PREVIEW (preview)->width);
  134.  
  135.   g_signal_connect (adj, "value_changed",
  136.                     G_CALLBACK (gimp_scrolled_preview_h_scroll),
  137.                     preview);
  138.  
  139.   preview->hscr = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj));
  140.   gtk_range_set_update_policy (GTK_RANGE (preview->hscr),
  141.                                GTK_UPDATE_CONTINUOUS);
  142.   gtk_table_attach (GTK_TABLE (GIMP_PREVIEW (preview)->table),
  143.                     preview->hscr, 0,1, 1,2,
  144.                     GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0);
  145.  
  146.   adj = gtk_adjustment_new (0, 0, GIMP_PREVIEW (preview)->height - 1, 1.0,
  147.                             GIMP_PREVIEW (preview)->height,
  148.                             GIMP_PREVIEW (preview)->height);
  149.  
  150.   g_signal_connect (adj, "value_changed",
  151.                     G_CALLBACK (gimp_scrolled_preview_v_scroll),
  152.                     preview);
  153.  
  154.   preview->vscr = gtk_vscrollbar_new (GTK_ADJUSTMENT (adj));
  155.   gtk_range_set_update_policy (GTK_RANGE (preview->vscr),
  156.                                GTK_UPDATE_CONTINUOUS);
  157.   gtk_table_attach (GTK_TABLE (GIMP_PREVIEW (preview)->table),
  158.                     preview->vscr, 1,2, 0,1,
  159.                     GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
  160.  
  161.   g_signal_connect (GIMP_PREVIEW (preview)->area, "event",
  162.                     G_CALLBACK (gimp_scrolled_preview_area_event),
  163.                     preview);
  164.  
  165.   g_signal_connect (GIMP_PREVIEW (preview)->area, "realize",
  166.                     G_CALLBACK (gimp_scrolled_preview_area_realize),
  167.                     preview);
  168.   g_signal_connect (GIMP_PREVIEW (preview)->area, "unrealize",
  169.                     G_CALLBACK (gimp_scrolled_preview_area_unrealize),
  170.                     preview);
  171.  
  172.   g_signal_connect (GIMP_PREVIEW (preview)->area, "size_allocate",
  173.                     G_CALLBACK (gimp_scrolled_preview_area_size_allocate),
  174.                     preview);
  175.  
  176.   /*  navigation icon  */
  177.   preview->nav_icon = gtk_event_box_new ();
  178.   gtk_table_attach (GTK_TABLE (GIMP_PREVIEW(preview)->table),
  179.                     preview->nav_icon, 1,2, 1,2,
  180.                     GTK_SHRINK, GTK_SHRINK, 0, 0);
  181.  
  182.   image = gtk_image_new_from_stock (GIMP_STOCK_NAVIGATION, GTK_ICON_SIZE_MENU);
  183.   gtk_container_add (GTK_CONTAINER (preview->nav_icon), image);
  184.   gtk_widget_show (image);
  185.  
  186.   g_signal_connect (preview->nav_icon, "button_press_event",
  187.                     G_CALLBACK (gimp_scrolled_preview_nav_button_press),
  188.                     preview);
  189. }
  190.  
  191. static void
  192. gimp_scrolled_preview_dispose (GObject *object)
  193. {
  194.   GimpScrolledPreview *preview = GIMP_SCROLLED_PREVIEW (object);
  195.  
  196.   if (preview->nav_popup)
  197.     {
  198.       gtk_widget_destroy (preview->nav_popup);
  199.       preview->nav_popup = NULL;
  200.     }
  201.  
  202.   G_OBJECT_CLASS (parent_class)->dispose (object);
  203. }
  204.  
  205. static void
  206. gimp_scrolled_preview_area_realize (GtkWidget           *widget,
  207.                                     GimpScrolledPreview *preview)
  208. {
  209.   GdkDisplay *display = gtk_widget_get_display (widget);
  210.  
  211.   g_return_if_fail (preview->cursor_move == NULL);
  212.  
  213.   preview->cursor_move = gdk_cursor_new_for_display (display, GDK_FLEUR);
  214.  
  215.   gimp_scrolled_preview_set_cursor (GIMP_PREVIEW (preview));
  216. }
  217.  
  218. static void
  219. gimp_scrolled_preview_area_unrealize (GtkWidget           *widget,
  220.                                       GimpScrolledPreview *preview)
  221. {
  222.   if (preview->cursor_move)
  223.     {
  224.       gdk_cursor_unref (preview->cursor_move);
  225.       preview->cursor_move = NULL;
  226.     }
  227. }
  228.  
  229. static void
  230. gimp_scrolled_preview_area_size_allocate (GtkWidget           *widget,
  231.                                           GtkAllocation       *allocation,
  232.                                           GimpScrolledPreview *preview)
  233. {
  234.   GdkCursor *cursor = GIMP_PREVIEW (preview)->default_cursor;
  235.   gint       width  = GIMP_PREVIEW (preview)->xmax - GIMP_PREVIEW (preview)->xmin;
  236.   gint       height = GIMP_PREVIEW (preview)->ymax - GIMP_PREVIEW (preview)->ymin;
  237.  
  238.   GIMP_PREVIEW (preview)->width  = MIN (width,  allocation->width);
  239.   GIMP_PREVIEW (preview)->height = MIN (height, allocation->height);
  240.  
  241.   if (width > GIMP_PREVIEW (preview)->width)
  242.     {
  243.       GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
  244.  
  245.       adj->lower          = 0;
  246.       adj->upper          = width;
  247.       adj->page_size      = GIMP_PREVIEW (preview)->width;
  248.       adj->step_increment = 1.0;
  249.       adj->page_increment = MAX (adj->page_size / 2.0, adj->step_increment);
  250.  
  251.       gtk_adjustment_changed (adj);
  252.  
  253.       gtk_widget_show (preview->hscr);
  254.  
  255.       cursor = preview->cursor_move;
  256.     }
  257.   else
  258.     {
  259.       gtk_widget_hide (preview->hscr);
  260.     }
  261.  
  262.   if (height > GIMP_PREVIEW (preview)->height)
  263.     {
  264.       GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
  265.  
  266.       adj->lower          = 0;
  267.       adj->upper          = height;
  268.       adj->page_size      = GIMP_PREVIEW (preview)->height;
  269.       adj->step_increment = 1.0;
  270.       adj->page_increment = MAX (adj->page_size / 2.0, adj->step_increment);
  271.  
  272.       gtk_adjustment_changed (adj);
  273.  
  274.       gtk_widget_show (preview->vscr);
  275.  
  276.       cursor = preview->cursor_move;
  277.     }
  278.   else
  279.     {
  280.       gtk_widget_hide (preview->vscr);
  281.     }
  282.  
  283.   if (GTK_WIDGET_VISIBLE (preview->vscr) &&
  284.       GTK_WIDGET_VISIBLE (preview->hscr) &&
  285.       GIMP_PREVIEW_GET_CLASS (preview)->draw_thumb)
  286.     {
  287.       gtk_widget_show (preview->nav_icon);
  288.     }
  289.   else
  290.     {
  291.       gtk_widget_hide (preview->nav_icon);
  292.     }
  293.  
  294.   if (GTK_WIDGET_REALIZED (widget))
  295.     gdk_window_set_cursor (widget->window, cursor);
  296.  
  297.   gimp_preview_draw (GIMP_PREVIEW (preview));
  298.   gimp_preview_invalidate (GIMP_PREVIEW (preview));
  299. }
  300.  
  301. static gboolean
  302. gimp_scrolled_preview_area_event (GtkWidget           *area,
  303.                                   GdkEvent            *event,
  304.                                   GimpScrolledPreview *preview)
  305. {
  306.   GdkEventButton *button_event = (GdkEventButton *) event;
  307.  
  308.   switch (event->type)
  309.     {
  310.     case GDK_BUTTON_PRESS:
  311.       switch (button_event->button)
  312.         {
  313.         case 1:
  314.           gtk_widget_get_pointer (area, &preview->drag_x, &preview->drag_y);
  315.  
  316.           preview->drag_xoff = GIMP_PREVIEW (preview)->xoff;
  317.           preview->drag_yoff = GIMP_PREVIEW (preview)->yoff;
  318.  
  319.           preview->in_drag = TRUE;
  320.           gtk_grab_add (area);
  321.           break;
  322.  
  323.         case 2:
  324.           break;
  325.  
  326.         case 3:
  327.           return TRUE;
  328.         }
  329.       break;
  330.  
  331.     case GDK_BUTTON_RELEASE:
  332.       if (preview->in_drag && button_event->button == 1)
  333.         {
  334.           gtk_grab_remove (area);
  335.           preview->in_drag = FALSE;
  336.         }
  337.       break;
  338.  
  339.     case GDK_MOTION_NOTIFY:
  340.       if (preview->in_drag)
  341.         {
  342.           gint x, y;
  343.           gint dx, dy;
  344.           gint xoff, yoff;
  345.  
  346.           gtk_widget_get_pointer (area, &x, &y);
  347.  
  348.           dx = x - preview->drag_x;
  349.           dy = y - preview->drag_y;
  350.  
  351.           xoff = CLAMP (preview->drag_xoff - dx,
  352.                         0,
  353.                         GIMP_PREVIEW (preview)->xmax -
  354.                         GIMP_PREVIEW (preview)->xmin -
  355.                         GIMP_PREVIEW (preview)->width);
  356.           yoff = CLAMP (preview->drag_yoff - dy,
  357.                         0,
  358.                         GIMP_PREVIEW (preview)->ymax -
  359.                         GIMP_PREVIEW (preview)->ymin -
  360.                         GIMP_PREVIEW (preview)->height);
  361.  
  362.           if (GIMP_PREVIEW (preview)->xoff != xoff ||
  363.               GIMP_PREVIEW (preview)->yoff != yoff)
  364.             {
  365.               gtk_range_set_value (GTK_RANGE (preview->hscr), xoff);
  366.               gtk_range_set_value (GTK_RANGE (preview->vscr), yoff);
  367.  
  368.               gimp_preview_draw (GIMP_PREVIEW (preview));
  369.               gimp_preview_invalidate (GIMP_PREVIEW (preview));
  370.             }
  371.         }
  372.       break;
  373.  
  374.     default:
  375.       break;
  376.     }
  377.  
  378.   return FALSE;
  379. }
  380.  
  381. static void
  382. gimp_scrolled_preview_h_scroll (GtkAdjustment *hadj,
  383.                                 GimpPreview   *preview)
  384. {
  385.   preview->xoff = hadj->value;
  386.  
  387.   gimp_preview_area_set_offsets (GIMP_PREVIEW_AREA (preview->area),
  388.                                  preview->xoff, preview->yoff);
  389.  
  390.   if (! GIMP_SCROLLED_PREVIEW (preview)->in_drag)
  391.     {
  392.       gimp_preview_draw (preview);
  393.       gimp_preview_invalidate (preview);
  394.     }
  395. }
  396.  
  397. static void
  398. gimp_scrolled_preview_v_scroll (GtkAdjustment *vadj,
  399.                                 GimpPreview   *preview)
  400. {
  401.   preview->yoff = vadj->value;
  402.  
  403.   gimp_preview_area_set_offsets (GIMP_PREVIEW_AREA (preview->area),
  404.                                  preview->xoff, preview->yoff);
  405.  
  406.   if (! GIMP_SCROLLED_PREVIEW (preview)->in_drag)
  407.     {
  408.       gimp_preview_draw (preview);
  409.       gimp_preview_invalidate (preview);
  410.     }
  411. }
  412.  
  413. static gboolean
  414. gimp_scrolled_preview_nav_button_press (GtkWidget           *widget,
  415.                                         GdkEventButton      *event,
  416.                                         GimpScrolledPreview *preview)
  417. {
  418.   GimpPreview *gimp_preview = GIMP_PREVIEW (preview);
  419.  
  420.   if (preview->nav_popup)
  421.     return TRUE;
  422.  
  423.   if (event->type == GDK_BUTTON_PRESS && event->button == 1)
  424.     {
  425.       GtkWidget  *outer;
  426.       GtkWidget  *inner;
  427.       GtkWidget  *area;
  428.       GdkCursor  *cursor;
  429.       gint        x, y;
  430.       gdouble     h, v;
  431.  
  432.       preview->nav_popup = gtk_window_new (GTK_WINDOW_POPUP);
  433.  
  434.       gtk_window_set_screen (GTK_WINDOW (preview->nav_popup),
  435.                              gtk_widget_get_screen (widget));
  436.  
  437.       outer = gtk_frame_new (NULL);
  438.       gtk_frame_set_shadow_type (GTK_FRAME (outer), GTK_SHADOW_OUT);
  439.       gtk_container_add (GTK_CONTAINER (preview->nav_popup), outer);
  440.       gtk_widget_show (outer);
  441.  
  442.       inner = gtk_frame_new (NULL);
  443.       gtk_frame_set_shadow_type (GTK_FRAME (inner), GTK_SHADOW_IN);
  444.       gtk_container_add (GTK_CONTAINER (outer), inner);
  445.       gtk_widget_show (inner);
  446.  
  447.       area = gimp_preview_area_new ();
  448.       gtk_container_add (GTK_CONTAINER (inner), area);
  449.  
  450.       g_signal_connect (area, "realize",
  451.                         G_CALLBACK (gimp_scrolled_preview_nav_popup_realize),
  452.                         preview);
  453.       g_signal_connect (area, "unrealize",
  454.                         G_CALLBACK (gimp_scrolled_preview_nav_popup_unrealize),
  455.                         preview);
  456.       g_signal_connect (area, "event",
  457.                         G_CALLBACK (gimp_scrolled_preview_nav_popup_event),
  458.                         preview);
  459.       g_signal_connect_after (area, "expose_event",
  460.                               G_CALLBACK (gimp_scrolled_preview_nav_popup_expose),
  461.                               preview);
  462.  
  463.       GIMP_PREVIEW_GET_CLASS (preview)->draw_thumb (gimp_preview,
  464.                                                     GIMP_PREVIEW_AREA (area),
  465.                                                     POPUP_SIZE, POPUP_SIZE);
  466.       gtk_widget_realize (area);
  467.       gtk_widget_show (area);
  468.  
  469.       gdk_window_get_origin (widget->window, &x, &y);
  470.  
  471.       h = ((gdouble) (gimp_preview->xoff + gimp_preview->width  / 2) /
  472.            (gdouble) (gimp_preview->xmax - gimp_preview->xmin));
  473.       v = ((gdouble) (gimp_preview->yoff + gimp_preview->height / 2) /
  474.            (gdouble) (gimp_preview->ymax - gimp_preview->ymin));
  475.  
  476.       x += event->x - h * (gdouble) GIMP_PREVIEW_AREA (area)->width;
  477.       y += event->y - v * (gdouble) GIMP_PREVIEW_AREA (area)->height;
  478.  
  479.       gtk_window_move (GTK_WINDOW (preview->nav_popup),
  480.                        x - 2 * widget->style->xthickness,
  481.                        y - 2 * widget->style->ythickness);
  482.  
  483.       gtk_widget_show (preview->nav_popup);
  484.  
  485.       gtk_grab_add (area);
  486.  
  487.       cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
  488.                                            GDK_FLEUR);
  489.  
  490.       gdk_pointer_grab (area->window, TRUE,
  491.                         GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK,
  492.                         area->window, cursor,
  493.                         event->time);
  494.  
  495.       gdk_cursor_unref (cursor);
  496.     }
  497.  
  498.   return TRUE;
  499. }
  500.  
  501. static void
  502. gimp_scrolled_preview_nav_popup_realize (GtkWidget           *widget,
  503.                                          GimpScrolledPreview *preview)
  504. {
  505.   if (! preview->nav_gc)
  506.     {
  507.       preview->nav_gc = gdk_gc_new (widget->window);
  508.  
  509.       gdk_gc_set_function (preview->nav_gc, GDK_INVERT);
  510.       gdk_gc_set_line_attributes (preview->nav_gc,
  511.                                   PEN_WIDTH,
  512.                                   GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_ROUND);
  513.     }
  514. }
  515.  
  516. static void
  517. gimp_scrolled_preview_nav_popup_unrealize (GtkWidget           *widget,
  518.                                            GimpScrolledPreview *preview)
  519. {
  520.   if (preview->nav_gc)
  521.     {
  522.       g_object_unref (preview->nav_gc);
  523.       preview->nav_gc = NULL;
  524.     }
  525. }
  526.  
  527. static gboolean
  528. gimp_scrolled_preview_nav_popup_event (GtkWidget           *widget,
  529.                                        GdkEvent            *event,
  530.                                        GimpScrolledPreview *preview)
  531. {
  532.   switch (event->type)
  533.     {
  534.     case GDK_BUTTON_RELEASE:
  535.       {
  536.         GdkEventButton *button_event = (GdkEventButton *) event;
  537.  
  538.         if (button_event->button == 1)
  539.           {
  540.             gtk_grab_remove (widget);
  541.             gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
  542.                                         button_event->time);
  543.  
  544.             gtk_widget_destroy (preview->nav_popup);
  545.             preview->nav_popup = NULL;
  546.           }
  547.       }
  548.       break;
  549.  
  550.     case GDK_MOTION_NOTIFY:
  551.       {
  552.         GdkEventMotion *motion_event = (GdkEventMotion *) event;
  553.         GtkAdjustment  *hadj;
  554.         GtkAdjustment  *vadj;
  555.         gdouble         x, y;
  556.  
  557.         hadj = gtk_range_get_adjustment (GTK_RANGE (preview->hscr));
  558.         vadj = gtk_range_get_adjustment (GTK_RANGE (preview->vscr));
  559.  
  560.         x = (motion_event->x *
  561.              (hadj->upper - hadj->lower) / widget->allocation.width);
  562.         y = (motion_event->y *
  563.              (vadj->upper - vadj->lower) / widget->allocation.height);
  564.  
  565.         x += hadj->lower - hadj->page_size / 2;
  566.         y += vadj->lower - vadj->page_size / 2;
  567.  
  568.         gtk_adjustment_set_value (hadj, CLAMP (x,
  569.                                                hadj->lower,
  570.                                                hadj->upper - hadj->page_size));
  571.         gtk_adjustment_set_value (vadj, CLAMP (y,
  572.                                                vadj->lower,
  573.                                                vadj->upper - vadj->page_size));
  574.  
  575.         gtk_widget_queue_draw (widget);
  576.       }
  577.       break;
  578.  
  579.   default:
  580.       break;
  581.     }
  582.  
  583.   return FALSE;
  584. }
  585.  
  586. static gboolean
  587. gimp_scrolled_preview_nav_popup_expose (GtkWidget           *widget,
  588.                                         GdkEventExpose      *event,
  589.                                         GimpScrolledPreview *preview)
  590. {
  591.   GimpPreview *gimp_preview = GIMP_PREVIEW (preview);
  592.   gdouble      x, y;
  593.   gdouble      w, h;
  594.  
  595.   x = ((gdouble) gimp_preview->xoff /
  596.        (gdouble) (gimp_preview->xmax - gimp_preview->xmin));
  597.   y = ((gdouble) gimp_preview->yoff /
  598.        (gdouble) (gimp_preview->ymax - gimp_preview->ymin));
  599.  
  600.   w = ((gdouble) gimp_preview->width  /
  601.        (gdouble) (gimp_preview->xmax - gimp_preview->xmin));
  602.   h = ((gdouble) gimp_preview->height /
  603.        (gdouble) (gimp_preview->ymax - gimp_preview->ymin));
  604.  
  605.   gdk_gc_set_clip_rectangle (preview->nav_gc, &event->area);
  606.  
  607.   gdk_draw_rectangle (widget->window, preview->nav_gc,
  608.                       FALSE,
  609.                       x * (gdouble) widget->allocation.width  + PEN_WIDTH / 2,
  610.                       y * (gdouble) widget->allocation.height + PEN_WIDTH / 2,
  611.                       MAX (1,
  612.                            ceil (w * widget->allocation.width)  - PEN_WIDTH),
  613.                       MAX (1,
  614.                            ceil (h * widget->allocation.height) - PEN_WIDTH));
  615.  
  616.   return FALSE;
  617. }
  618.  
  619. static void
  620. gimp_scrolled_preview_set_cursor (GimpPreview *preview)
  621. {
  622.   if (preview->xmax - preview->xmin > preview->width  ||
  623.       preview->ymax - preview->ymin > preview->height)
  624.     {
  625.       gdk_window_set_cursor (preview->area->window,
  626.                              GIMP_SCROLLED_PREVIEW (preview)->cursor_move);
  627.     }
  628.   else
  629.     {
  630.       gdk_window_set_cursor (preview->area->window, preview->default_cursor);
  631.     }
  632. }
  633.