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 / gimpoffsetarea.c < prev    next >
C/C++ Source or Header  |  2004-11-08  |  14KB  |  487 lines

  1. /* LIBGIMP - The GIMP Library
  2.  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
  3.  *
  4.  * gimpoffsetarea.c
  5.  * Copyright (C) 2001  Sven Neumann <sven@gimp.org>
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the
  19.  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20.  * Boston, MA 02111-1307, USA.
  21.  */
  22.  
  23. #include "config.h"
  24.  
  25. #include <gtk/gtk.h>
  26.  
  27. #include "gimpwidgetstypes.h"
  28.  
  29. #include "gimpwidgetsmarshal.h"
  30. #include "gimpoffsetarea.h"
  31.  
  32.  
  33. #define DRAWING_AREA_SIZE 200
  34.  
  35.  
  36. enum
  37. {
  38.   OFFSETS_CHANGED,
  39.   LAST_SIGNAL
  40. };
  41.  
  42.  
  43. static void      gimp_offset_area_class_init (GimpOffsetAreaClass *klass);
  44. static void      gimp_offset_area_init       (GimpOffsetArea      *area);
  45.  
  46. static void      gimp_offset_area_resize        (GimpOffsetArea   *area);
  47. static void      gimp_offset_area_size_allocate (GtkWidget        *widget,
  48.                                                  GtkAllocation    *allocation);
  49. static gboolean  gimp_offset_area_event         (GtkWidget        *widget,
  50.                                                  GdkEvent         *event);
  51. static gboolean  gimp_offset_area_expose_event  (GtkWidget        *widget,
  52.                                                  GdkEventExpose   *eevent);
  53.  
  54.  
  55. static guint gimp_offset_area_signals[LAST_SIGNAL] = { 0 };
  56.  
  57. static GtkDrawingAreaClass *parent_class = NULL;
  58.  
  59.  
  60. GType
  61. gimp_offset_area_get_type (void)
  62. {
  63.   static GType area_type = 0;
  64.  
  65.   if (! area_type)
  66.     {
  67.       static const GTypeInfo area_info =
  68.       {
  69.         sizeof (GimpOffsetAreaClass),
  70.     (GBaseInitFunc) NULL,
  71.     (GBaseFinalizeFunc) NULL,
  72.     (GClassInitFunc) gimp_offset_area_class_init,
  73.     NULL,        /* class_finalize */
  74.     NULL,        /* class_data     */
  75.     sizeof (GimpOffsetArea),
  76.     0,              /* n_preallocs    */
  77.     (GInstanceInitFunc) gimp_offset_area_init,
  78.       };
  79.  
  80.       area_type = g_type_register_static (GTK_TYPE_DRAWING_AREA,
  81.                                           "GimpOffsetArea",
  82.                                           &area_info, 0);
  83.     }
  84.  
  85.   return area_type;
  86. }
  87.  
  88. static void
  89. gimp_offset_area_class_init (GimpOffsetAreaClass *klass)
  90. {
  91.   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  92.  
  93.   parent_class = g_type_class_peek_parent (klass);
  94.  
  95.   gimp_offset_area_signals[OFFSETS_CHANGED] =
  96.     g_signal_new ("offsets_changed",
  97.           G_TYPE_FROM_CLASS (klass),
  98.           G_SIGNAL_RUN_FIRST,
  99.           G_STRUCT_OFFSET (GimpOffsetAreaClass, offsets_changed),
  100.           NULL, NULL,
  101.           _gimp_widgets_marshal_VOID__INT_INT,
  102.           G_TYPE_NONE, 2,
  103.           G_TYPE_INT,
  104.           G_TYPE_INT);
  105.  
  106.   widget_class->event         = gimp_offset_area_event;
  107.   widget_class->expose_event  = gimp_offset_area_expose_event;
  108.   widget_class->size_allocate = gimp_offset_area_size_allocate;
  109. }
  110.  
  111. static void
  112. gimp_offset_area_init (GimpOffsetArea *area)
  113. {
  114.   area->orig_width      = 0;
  115.   area->orig_height     = 0;
  116.   area->width           = 0;
  117.   area->height          = 0;
  118.   area->offset_x        = 0;
  119.   area->offset_y        = 0;
  120.   area->display_ratio_x = 1.0;
  121.   area->display_ratio_y = 1.0;
  122.  
  123.   gtk_widget_add_events (GTK_WIDGET (area),
  124.                          GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
  125. }
  126.  
  127. /**
  128.  * gimp_offset_area_new:
  129.  * @orig_width: the original width
  130.  * @orig_height: the original height
  131.  *
  132.  * Creates a new #GimpOffsetArea widget. A #GimpOffsetArea can be used
  133.  * when resizing an image or a drawable to allow the user to interactively
  134.  * specify the new offsets.
  135.  *
  136.  * Return value: the new #GimpOffsetArea widget.
  137.  **/
  138. GtkWidget *
  139. gimp_offset_area_new (gint orig_width,
  140.                       gint orig_height)
  141. {
  142.   GimpOffsetArea *area;
  143.  
  144.   g_return_val_if_fail (orig_width  > 0, NULL);
  145.   g_return_val_if_fail (orig_height > 0, NULL);
  146.  
  147.   area = g_object_new (GIMP_TYPE_OFFSET_AREA, NULL);
  148.  
  149.   area->orig_width  = area->width  = orig_width;
  150.   area->orig_height = area->height = orig_height;
  151.  
  152.   gimp_offset_area_resize (area);
  153.  
  154.   return GTK_WIDGET (area);
  155. }
  156.  
  157. /**
  158.  * gimp_offset_area_set_pixbuf:
  159.  * @offset_area: a #GimpOffsetArea.
  160.  * @pixbuf: a #GdkPixbuf.
  161.  *
  162.  * Sets the pixbuf which represents the original image/drawable which
  163.  * is being offset.
  164.  *
  165.  * Since: GIMP 2.2
  166.  **/
  167. void
  168. gimp_offset_area_set_pixbuf (GimpOffsetArea *area,
  169.                              GdkPixbuf      *pixbuf)
  170. {
  171.   g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
  172.   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
  173.  
  174.   g_object_set_data_full (G_OBJECT (area), "pixbuf",
  175.                           gdk_pixbuf_copy (pixbuf),
  176.                           (GDestroyNotify) g_object_unref);
  177.  
  178.   gtk_widget_queue_draw (GTK_WIDGET (area));
  179. }
  180.  
  181. /**
  182.  * gimp_offset_area_set_size:
  183.  * @offset_area: a #GimpOffsetArea.
  184.  * @width: the new width
  185.  * @height: the new height
  186.  *
  187.  * Sets the size of the image/drawable displayed by the #GimpOffsetArea.
  188.  * If the offsets change as a result of this change, the %offsets_changed
  189.  * signal is emitted.
  190.  **/
  191. void
  192. gimp_offset_area_set_size (GimpOffsetArea *area,
  193.                            gint            width,
  194.                            gint            height)
  195. {
  196.   g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
  197.   g_return_if_fail (width > 0 && height > 0);
  198.  
  199.   if (area->width != width || area->height != height)
  200.     {
  201.       gint offset_x;
  202.       gint offset_y;
  203.  
  204.       area->width  = width;
  205.       area->height = height;
  206.  
  207.       if (area->orig_width <= area->width)
  208.         offset_x = CLAMP (area->offset_x, 0, area->width - area->orig_width);
  209.       else
  210.         offset_x = CLAMP (area->offset_x, area->width - area->orig_width, 0);
  211.  
  212.       if (area->orig_height <= area->height)
  213.         offset_y = CLAMP (area->offset_y, 0, area->height - area->orig_height);
  214.       else
  215.         offset_y = CLAMP (area->offset_y, area->height - area->orig_height, 0);
  216.  
  217.       if (offset_x != area->offset_x || offset_y != area->offset_y)
  218.         {
  219.           area->offset_x = offset_x;
  220.           area->offset_y = offset_y;
  221.  
  222.           g_signal_emit (area,
  223.                          gimp_offset_area_signals[OFFSETS_CHANGED], 0,
  224.                          offset_x, offset_y);
  225.         }
  226.  
  227.       gimp_offset_area_resize (area);
  228.     }
  229. }
  230.  
  231. /**
  232.  * gimp_offset_area_set_offsets:
  233.  * @offset_area: a #GimpOffsetArea.
  234.  * @offset_x: the X offset
  235.  * @offset_y: the Y offset
  236.  *
  237.  * Sets the offsets of the image/drawable displayed by the #GimpOffsetArea.
  238.  * It does not emit the %offsets_changed signal.
  239.  **/
  240. void
  241. gimp_offset_area_set_offsets (GimpOffsetArea *area,
  242.                               gint            offset_x,
  243.                               gint            offset_y)
  244. {
  245.   g_return_if_fail (GIMP_IS_OFFSET_AREA (area));
  246.  
  247.   if (area->offset_x != offset_x || area->offset_y != offset_y)
  248.     {
  249.       if (area->orig_width <= area->width)
  250.         area->offset_x = CLAMP (offset_x, 0, area->width - area->orig_width);
  251.       else
  252.         area->offset_x = CLAMP (offset_x, area->width - area->orig_width, 0);
  253.  
  254.       if (area->orig_height <= area->height)
  255.         area->offset_y = CLAMP (offset_y, 0, area->height - area->orig_height);
  256.       else
  257.         area->offset_y = CLAMP (offset_y, area->height - area->orig_height, 0);
  258.  
  259.       gtk_widget_queue_draw (GTK_WIDGET (area));
  260.     }
  261. }
  262.  
  263. static void
  264. gimp_offset_area_resize (GimpOffsetArea *area)
  265. {
  266.   gint    width;
  267.   gint    height;
  268.   gdouble ratio;
  269.  
  270.   if (area->orig_width == 0 || area->orig_height == 0)
  271.     return;
  272.  
  273.   if (area->orig_width <= area->width)
  274.     width = area->width;
  275.   else
  276.     width = area->orig_width * 2 - area->width;
  277.  
  278.   if (area->orig_height <= area->height)
  279.     height = area->height;
  280.   else
  281.     height = area->orig_height * 2 - area->height;
  282.  
  283.   ratio = (gdouble) DRAWING_AREA_SIZE / (gdouble) MAX (width, height);
  284.  
  285.   width  = ratio * (gdouble) width;
  286.   height = ratio * (gdouble) height;
  287.  
  288.   gtk_widget_set_size_request (GTK_WIDGET (area), width, height);
  289.   gtk_widget_queue_resize (GTK_WIDGET (area));
  290. }
  291.  
  292. static void
  293. gimp_offset_area_size_allocate (GtkWidget     *widget,
  294.                                 GtkAllocation *allocation)
  295. {
  296.   GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
  297.   GdkPixbuf      *pixbuf;
  298.  
  299.   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
  300.  
  301.   area->display_ratio_x = ((gdouble) allocation->width /
  302.                            ((area->orig_width <= area->width) ?
  303.                             area->width :
  304.                             area->orig_width * 2 - area->width));
  305.  
  306.   area->display_ratio_y = ((gdouble) allocation->height /
  307.                            ((area->orig_height <= area->height) ?
  308.                             area->height :
  309.                             area->orig_height * 2 - area->height));
  310.  
  311.   pixbuf = g_object_get_data (G_OBJECT (area), "pixbuf");
  312.  
  313.   if (pixbuf)
  314.     {
  315.       GdkPixbuf *copy;
  316.       gint       pixbuf_width;
  317.       gint       pixbuf_height;
  318.  
  319.       pixbuf_width  = area->display_ratio_x * area->orig_width;
  320.       pixbuf_height = area->display_ratio_y * area->orig_height;
  321.  
  322.       copy = g_object_get_data (G_OBJECT (area), "pixbuf-copy");
  323.  
  324.       if (copy &&
  325.           (pixbuf_width  != gdk_pixbuf_get_width (copy) ||
  326.            pixbuf_height != gdk_pixbuf_get_height (copy)))
  327.         {
  328.           copy = NULL;
  329.         }
  330.  
  331.       if (! copy)
  332.         {
  333.           copy = gdk_pixbuf_scale_simple (pixbuf, pixbuf_width, pixbuf_height,
  334.                                           GDK_INTERP_NEAREST);
  335.  
  336.           g_object_set_data_full (G_OBJECT (area), "pixbuf-copy",
  337.                                   copy, (GDestroyNotify) g_object_unref);
  338.         }
  339.     }
  340. }
  341.  
  342. static gboolean
  343. gimp_offset_area_event (GtkWidget *widget,
  344.                         GdkEvent  *event)
  345. {
  346.   static gint orig_offset_x = 0;
  347.   static gint orig_offset_y = 0;
  348.   static gint start_x       = 0;
  349.   static gint start_y       = 0;
  350.  
  351.   GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
  352.   gint            offset_x;
  353.   gint            offset_y;
  354.  
  355.   if (area->orig_width == 0 || area->orig_height == 0)
  356.     return FALSE;
  357.  
  358.   switch (event->type)
  359.     {
  360.     case GDK_BUTTON_PRESS:
  361.       gdk_pointer_grab (widget->window, FALSE,
  362.             (GDK_BUTTON1_MOTION_MASK |
  363.              GDK_BUTTON_RELEASE_MASK),
  364.             NULL, NULL, event->button.time);
  365.  
  366.       orig_offset_x = area->offset_x;
  367.       orig_offset_y = area->offset_y;
  368.       start_x = event->button.x;
  369.       start_y = event->button.y;
  370.       break;
  371.  
  372.     case GDK_MOTION_NOTIFY:
  373.       offset_x = (orig_offset_x +
  374.                   (event->motion.x - start_x) / area->display_ratio_x);
  375.       offset_y = (orig_offset_y +
  376.                   (event->motion.y - start_y) / area->display_ratio_y);
  377.  
  378.       if (area->offset_x != offset_x || area->offset_y != offset_y)
  379.         {
  380.           gimp_offset_area_set_offsets (area, offset_x, offset_y);
  381.  
  382.           g_signal_emit (area,
  383.                          gimp_offset_area_signals[OFFSETS_CHANGED], 0,
  384.                          area->offset_x, area->offset_y);
  385.         }
  386.       break;
  387.  
  388.     case GDK_BUTTON_RELEASE:
  389.       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
  390.                                   event->button.time);
  391.       start_x = start_y = 0;
  392.       break;
  393.  
  394.     default:
  395.       break;
  396.     }
  397.  
  398.   return FALSE;
  399. }
  400.  
  401. static gboolean
  402. gimp_offset_area_expose_event (GtkWidget      *widget,
  403.                                GdkEventExpose *eevent)
  404. {
  405.   GimpOffsetArea *area = GIMP_OFFSET_AREA (widget);
  406.   GdkPixbuf      *pixbuf;
  407.   gint            w, h;
  408.   gint            x, y;
  409.  
  410.   x = (area->display_ratio_x *
  411.        ((area->orig_width <= area->width) ?
  412.         area->offset_x :
  413.         area->offset_x + area->orig_width - area->width));
  414.  
  415.   y = (area->display_ratio_y *
  416.        ((area->orig_height <= area->height) ?
  417.         area->offset_y :
  418.         area->offset_y + area->orig_height - area->height));
  419.  
  420.   w = area->display_ratio_x * area->orig_width;
  421.   h = area->display_ratio_y * area->orig_height;
  422.  
  423.   pixbuf = g_object_get_data (G_OBJECT (widget), "pixbuf-copy");
  424.  
  425.   if (pixbuf)
  426.     {
  427.       gdk_draw_pixbuf (widget->window, widget->style->black_gc,
  428.                        pixbuf, 0, 0, x, y, w, h, GDK_RGB_DITHER_NORMAL, 0, 0);
  429.       gdk_draw_rectangle (widget->window, widget->style->black_gc, FALSE,
  430.                           x, y, w - 1, h - 1);
  431.     }
  432.   else
  433.     {
  434.       gtk_paint_shadow (widget->style, widget->window, GTK_STATE_NORMAL,
  435.                         GTK_SHADOW_OUT,
  436.                         NULL, widget, NULL,
  437.                         x, y, w, h);
  438.     }
  439.  
  440.   if (area->orig_width > area->width || area->orig_height > area->height)
  441.     {
  442.        if (area->orig_width > area->width)
  443.     {
  444.       x = area->display_ratio_x * (area->orig_width - area->width);
  445.       w = area->display_ratio_x * area->width;
  446.     }
  447.       else
  448.     {
  449.       x = -1;
  450.       w = widget->allocation.width + 2;
  451.     }
  452.  
  453.       if (area->orig_height > area->height)
  454.     {
  455.       y = area->display_ratio_y * (area->orig_height - area->height);
  456.       h = area->display_ratio_y * area->height;
  457.     }
  458.       else
  459.     {
  460.       y = -1;
  461.       h = widget->allocation.height + 2;
  462.     }
  463.  
  464.       if (pixbuf)
  465.         {
  466.           GdkGC *gc = gdk_gc_new (widget->window);
  467.  
  468.           gdk_gc_set_function (gc, GDK_INVERT);
  469.           gdk_gc_set_line_attributes (gc, 3,
  470.                                       GDK_LINE_SOLID, GDK_CAP_BUTT,
  471.                                       GDK_JOIN_ROUND);
  472.  
  473.           gdk_draw_rectangle (widget->window, gc, FALSE,
  474.                               x + 1, y + 1, w - 3, h - 3);
  475.  
  476.           g_object_unref (gc);
  477.        }
  478.       else
  479.         {
  480.           gdk_draw_rectangle (widget->window, widget->style->black_gc, FALSE,
  481.                               x, y, w, h);
  482.         }
  483.     }
  484.  
  485.   return FALSE;
  486. }
  487.