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 / gimpcolorarea.c < prev    next >
C/C++ Source or Header  |  2004-07-26  |  18KB  |  618 lines

  1. /* LIBGIMP - The GIMP Library
  2.  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
  3.  *
  4.  * gimpcolorarea.c
  5.  * Copyright (C) 2001-2002  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.  * Library 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 "libgimpcolor/gimpcolor.h"
  28. #include "libgimpbase/gimpbase.h"
  29.  
  30. #include "gimpwidgetstypes.h"
  31.  
  32. #include "gimpcolorarea.h"
  33.  
  34.  
  35. #define DRAG_PREVIEW_SIZE   32
  36. #define DRAG_ICON_OFFSET    -8
  37.  
  38.  
  39. enum
  40. {
  41.   COLOR_CHANGED,
  42.   LAST_SIGNAL
  43. };
  44.  
  45.  
  46. static void     gimp_color_area_class_init    (GimpColorAreaClass *klass);
  47. static void     gimp_color_area_init          (GimpColorArea      *area);
  48.  
  49. static void     gimp_color_area_finalize      (GObject            *object);
  50.  
  51. static void     gimp_color_area_size_allocate (GtkWidget          *widget,
  52.                                                GtkAllocation      *allocation);
  53. static gboolean gimp_color_area_expose        (GtkWidget          *widget,
  54.                                                GdkEventExpose     *event);
  55. static void     gimp_color_area_render        (GimpColorArea      *area);
  56.  
  57. static void  gimp_color_area_drag_begin         (GtkWidget        *widget,
  58.                          GdkDragContext   *context);
  59. static void  gimp_color_area_drag_end           (GtkWidget        *widget,
  60.                          GdkDragContext   *context);
  61. static void  gimp_color_area_drag_data_received (GtkWidget        *widget,
  62.                          GdkDragContext   *context,
  63.                          gint              x,
  64.                          gint              y,
  65.                          GtkSelectionData *selection_data,
  66.                          guint             info,
  67.                          guint             time);
  68. static void  gimp_color_area_drag_data_get      (GtkWidget        *widget,
  69.                          GdkDragContext   *context,
  70.                          GtkSelectionData *selection_data,
  71.                          guint             info,
  72.                          guint             time);
  73.  
  74.  
  75. static const GtkTargetEntry target = { "application/x-color", 0 };
  76.  
  77. static guint gimp_color_area_signals[LAST_SIGNAL] = { 0 };
  78.  
  79. static GtkDrawingAreaClass * parent_class = NULL;
  80.  
  81.  
  82. GType
  83. gimp_color_area_get_type (void)
  84. {
  85.   static GType area_type = 0;
  86.  
  87.   if (! area_type)
  88.     {
  89.       static const GTypeInfo area_info =
  90.       {
  91.         sizeof (GimpColorAreaClass),
  92.         (GBaseInitFunc) NULL,
  93.         (GBaseFinalizeFunc) NULL,
  94.         (GClassInitFunc) gimp_color_area_class_init,
  95.         NULL,           /* class_finalize */
  96.         NULL,           /* class_data     */
  97.         sizeof (GimpColorArea),
  98.         0,              /* n_preallocs    */
  99.         (GInstanceInitFunc) gimp_color_area_init,
  100.       };
  101.  
  102.       area_type = g_type_register_static (GTK_TYPE_DRAWING_AREA,
  103.                                           "GimpColorArea",
  104.                                           &area_info, 0);
  105.     }
  106.  
  107.   return area_type;
  108. }
  109.  
  110. static void
  111. gimp_color_area_class_init (GimpColorAreaClass *klass)
  112. {
  113.   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
  114.   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  115.  
  116.   parent_class = g_type_class_peek_parent (klass);
  117.  
  118.   gimp_color_area_signals[COLOR_CHANGED] =
  119.     g_signal_new ("color_changed",
  120.                   G_TYPE_FROM_CLASS (klass),
  121.                   G_SIGNAL_RUN_FIRST,
  122.                   G_STRUCT_OFFSET (GimpColorAreaClass, color_changed),
  123.                   NULL, NULL,
  124.                   g_cclosure_marshal_VOID__VOID,
  125.                   G_TYPE_NONE, 0);
  126.  
  127.   object_class->finalize           = gimp_color_area_finalize;
  128.  
  129.   widget_class->size_allocate      = gimp_color_area_size_allocate;
  130.   widget_class->expose_event       = gimp_color_area_expose;
  131.  
  132.   widget_class->drag_begin         = gimp_color_area_drag_begin;
  133.   widget_class->drag_end           = gimp_color_area_drag_end;
  134.   widget_class->drag_data_received = gimp_color_area_drag_data_received;
  135.   widget_class->drag_data_get      = gimp_color_area_drag_data_get;
  136.  
  137.   klass->color_changed             = NULL;
  138. }
  139.  
  140. static void
  141. gimp_color_area_init (GimpColorArea *area)
  142. {
  143.   area->buf       = NULL;
  144.   area->width     = 0;
  145.   area->height    = 0;
  146.   area->rowstride = 0;
  147.  
  148.   area->type      = GIMP_COLOR_AREA_FLAT;
  149.   gimp_rgba_set (&area->color, 0.0, 0.0, 0.0, 1.0);
  150.  
  151.   area->draw_border  = FALSE;
  152.   area->needs_render = TRUE;
  153. }
  154.  
  155. static void
  156. gimp_color_area_finalize (GObject *object)
  157. {
  158.   GimpColorArea *area = GIMP_COLOR_AREA (object);
  159.  
  160.   if (area->buf)
  161.     {
  162.       g_free (area->buf);
  163.       area->buf = NULL;
  164.     }
  165.  
  166.   G_OBJECT_CLASS (parent_class)->finalize (object);
  167. }
  168.  
  169. static void
  170. gimp_color_area_size_allocate (GtkWidget     *widget,
  171.                    GtkAllocation *allocation)
  172. {
  173.   GimpColorArea *area = GIMP_COLOR_AREA (widget);
  174.  
  175.   if (GTK_WIDGET_CLASS (parent_class)->size_allocate)
  176.     GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
  177.  
  178.   if (widget->allocation.width  != area->width ||
  179.       widget->allocation.height != area->height)
  180.     {
  181.       area->width  = widget->allocation.width;
  182.       area->height = widget->allocation.height;
  183.  
  184.       area->rowstride = (area->width * 3 + 3) & ~0x3;
  185.  
  186.       g_free (area->buf);
  187.       area->buf = g_new (guchar, area->rowstride * area->height);
  188.  
  189.       area->needs_render = TRUE;
  190.     }
  191. }
  192.  
  193. static gboolean
  194. gimp_color_area_expose (GtkWidget      *widget,
  195.                         GdkEventExpose *event)
  196. {
  197.   GimpColorArea *area = GIMP_COLOR_AREA (widget);
  198.   guchar        *buf;
  199.  
  200.   if (! area->buf || !GTK_WIDGET_DRAWABLE (widget))
  201.     return FALSE;
  202.  
  203.   if (area->needs_render)
  204.     gimp_color_area_render (area);
  205.  
  206.   buf = area->buf + event->area.y * area->rowstride + event->area.x * 3;
  207.  
  208.   gdk_draw_rgb_image_dithalign (widget->window,
  209.                                 widget->style->black_gc,
  210.                                 event->area.x,
  211.                                 event->area.y,
  212.                                 event->area.width,
  213.                                 event->area.height,
  214.                                 GDK_RGB_DITHER_MAX,
  215.                                 buf,
  216.                                 area->rowstride,
  217.                                 event->area.x,
  218.                                 event->area.y);
  219.  
  220.   if (area->draw_border)
  221.     gdk_draw_rectangle (widget->window,
  222.                         widget->style->fg_gc[widget->state],
  223.                         FALSE,
  224.                         0, 0,
  225.                         area->width - 1, area->height - 1);
  226.  
  227.   return FALSE;
  228. }
  229.  
  230. /**
  231.  * gimp_color_area_new:
  232.  * @color:     A pointer to a #GimpRGB struct.
  233.  * @type:      The type of color area to create.
  234.  * @drag_mask: The event_mask that should trigger drags.
  235.  *
  236.  * Creates a new #GimpColorArea widget.
  237.  *
  238.  * This returns a preview area showing the color. It handles color
  239.  * DND. If the color changes, the "color_changed" signal is emitted.
  240.  *
  241.  * Returns: Pointer to the new #GimpColorArea widget.
  242.  **/
  243. GtkWidget *
  244. gimp_color_area_new (const GimpRGB     *color,
  245.              GimpColorAreaType  type,
  246.              GdkModifierType    drag_mask)
  247. {
  248.   GimpColorArea *area;
  249.  
  250.   g_return_val_if_fail (color != NULL, NULL);
  251.  
  252.   area = g_object_new (GIMP_TYPE_COLOR_AREA, NULL);
  253.  
  254.   area->color = *color;
  255.   area->type  = type;
  256.  
  257.   gtk_drag_dest_set (GTK_WIDGET (area),
  258.                      GTK_DEST_DEFAULT_HIGHLIGHT |
  259.                      GTK_DEST_DEFAULT_MOTION |
  260.                      GTK_DEST_DEFAULT_DROP,
  261.                      &target, 1,
  262.                      GDK_ACTION_COPY);
  263.  
  264.   drag_mask &= (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK);
  265.  
  266.   if (drag_mask)
  267.     gtk_drag_source_set (GTK_WIDGET (area),
  268.              drag_mask,
  269.              &target, 1,
  270.              GDK_ACTION_COPY | GDK_ACTION_MOVE);
  271.  
  272.   return GTK_WIDGET (area);
  273. }
  274.  
  275. /**
  276.  * gimp_color_area_set_color:
  277.  * @area: Pointer to a #GimpColorArea.
  278.  * @color: Pointer to a #GimpRGB struct that defines the new color.
  279.  *
  280.  * Sets @area to a different @color.
  281.  **/
  282. void
  283. gimp_color_area_set_color (GimpColorArea *area,
  284.                            const GimpRGB *color)
  285. {
  286.   g_return_if_fail (GIMP_IS_COLOR_AREA (area));
  287.   g_return_if_fail (color != NULL);
  288.  
  289.   if (area->type == GIMP_COLOR_AREA_FLAT)
  290.     {
  291.       if (gimp_rgb_distance (&area->color, color) < 0.000001)
  292.         return;
  293.     }
  294.   else
  295.     {
  296.       if (gimp_rgba_distance (&area->color, color) < 0.000001)
  297.         return;
  298.     }
  299.  
  300.   area->color = *color;
  301.  
  302.   area->needs_render = TRUE;
  303.   gtk_widget_queue_draw (GTK_WIDGET (area));
  304.  
  305.   g_signal_emit (area, gimp_color_area_signals[COLOR_CHANGED], 0);
  306. }
  307.  
  308. /**
  309.  * gimp_color_area_get_color:
  310.  * @area: Pointer to a #GimpColorArea.
  311.  * @color: Pointer to a #GimpRGB struct that is used to return the color.
  312.  *
  313.  * Retrieves the current color of the @area.
  314.  **/
  315. void
  316. gimp_color_area_get_color (GimpColorArea *area,
  317.                            GimpRGB       *color)
  318. {
  319.   g_return_if_fail (GIMP_IS_COLOR_AREA (area));
  320.   g_return_if_fail (color != NULL);
  321.  
  322.   *color = area->color;
  323. }
  324.  
  325. /**
  326.  * gimp_color_area_has_alpha:
  327.  * @area: Pointer to a #GimpColorArea.
  328.  *
  329.  * Checks whether the @area shows transparency information. This is determined
  330.  * via the @area's #GimpColorAreaType.
  331.  *
  332.  * Returns: %TRUE if @area shows transparency information, %FALSE otherwise.
  333.  **/
  334. gboolean
  335. gimp_color_area_has_alpha (GimpColorArea *area)
  336. {
  337.   g_return_val_if_fail (GIMP_IS_COLOR_AREA (area), FALSE);
  338.  
  339.   return area->type != GIMP_COLOR_AREA_FLAT;
  340. }
  341.  
  342. /**
  343.  * gimp_color_area_set_type:
  344.  * @area: Pointer to a #GimpColorArea.
  345.  * @type: A #GimpColorAreaType.
  346.  *
  347.  * Allows to change the type of @area. The #GimpColorAreaType determines
  348.  * whether the widget shows transparency information and chooses the size of
  349.  * the checkerboard used to do that.
  350.  **/
  351. void
  352. gimp_color_area_set_type (GimpColorArea     *area,
  353.                           GimpColorAreaType  type)
  354. {
  355.   g_return_if_fail (GIMP_IS_COLOR_AREA (area));
  356.  
  357.   area->type = type;
  358.  
  359.   area->needs_render = TRUE;
  360.   gtk_widget_queue_draw (GTK_WIDGET (area));
  361. }
  362.  
  363. /**
  364.  * gimp_color_area_set_draw_border:
  365.  * @area: Pointer to a #GimpColorArea.
  366.  * @draw_border: whether to draw a border or not
  367.  *
  368.  * The @area can draw a thin border in the foreground color around
  369.  * itself.  This function allows to toggle this behaviour on and
  370.  * off. The default is not draw a border.
  371.  **/
  372. void
  373. gimp_color_area_set_draw_border (GimpColorArea *area,
  374.                                  gboolean       draw_border)
  375. {
  376.   g_return_if_fail (GIMP_IS_COLOR_AREA (area));
  377.  
  378.   area->draw_border = draw_border ? TRUE : FALSE;
  379.  
  380.   gtk_widget_queue_draw (GTK_WIDGET (area));
  381. }
  382.  
  383. void
  384. _gimp_color_area_render_buf (GimpColorAreaType  type,
  385.                              guchar            *buf,
  386.                              guint              width,
  387.                              guint              height,
  388.                              guint              rowstride,
  389.                              GimpRGB           *color)
  390. {
  391.   guint    x, y;
  392.   guint    check_size = 0;
  393.   guchar   light[3];
  394.   guchar   dark[3];
  395.   guchar   opaque[3];
  396.   guchar  *p;
  397.   gdouble  frac;
  398.  
  399.   switch (type)
  400.     {
  401.     case GIMP_COLOR_AREA_FLAT:
  402.       check_size = 0;
  403.       break;
  404.  
  405.     case GIMP_COLOR_AREA_SMALL_CHECKS:
  406.       check_size = GIMP_CHECK_SIZE_SM;
  407.       break;
  408.  
  409.     case GIMP_COLOR_AREA_LARGE_CHECKS:
  410.       check_size = GIMP_CHECK_SIZE;
  411.       break;
  412.     }
  413.  
  414.   gimp_rgb_get_uchar (color, opaque, opaque + 1, opaque + 2);
  415.  
  416.   if (color->a == 1.0 || !check_size)
  417.     {
  418.       for (y = 0; y < height; y++)
  419.         {
  420.           p = buf + y * rowstride;
  421.  
  422.           for (x = 0; x < width; x++)
  423.             {
  424.               *p++ = opaque[0];
  425.               *p++ = opaque[1];
  426.               *p++ = opaque[2];
  427.             }
  428.         }
  429.  
  430.       return;
  431.     }
  432.  
  433.   light[0] = (GIMP_CHECK_LIGHT +
  434.               (color->r - GIMP_CHECK_LIGHT) * color->a) * 255.999;
  435.   dark[0]  = (GIMP_CHECK_DARK +
  436.               (color->r - GIMP_CHECK_DARK)  * color->a) * 255.999;
  437.   light[1] = (GIMP_CHECK_LIGHT +
  438.               (color->g - GIMP_CHECK_LIGHT) * color->a) * 255.999;
  439.   dark[1]  = (GIMP_CHECK_DARK +
  440.               (color->g - GIMP_CHECK_DARK)  * color->a) * 255.999;
  441.   light[2] = (GIMP_CHECK_LIGHT +
  442.               (color->b - GIMP_CHECK_LIGHT) * color->a) * 255.999;
  443.   dark[2]  = (GIMP_CHECK_DARK +
  444.               (color->b - GIMP_CHECK_DARK)  * color->a) * 255.999;
  445.  
  446.   for (y = 0; y < height; y++)
  447.     {
  448.       p = buf + y * rowstride;
  449.  
  450.       for (x = 0; x < width; x++)
  451.         {
  452.           if ((width - x) * height > y * width)
  453.             {
  454.               *p++ = opaque[0];
  455.               *p++ = opaque[1];
  456.               *p++ = opaque[2];
  457.  
  458.               continue;
  459.             }
  460.  
  461.           frac = y - (gdouble) ((width - x) * height) / (gdouble) width;
  462.  
  463.           if (((x / check_size) ^ (y / check_size)) & 1)
  464.             {
  465.               if ((gint) frac)
  466.                 {
  467.                   *p++ = light[0];
  468.                   *p++ = light[1];
  469.                   *p++ = light[2];
  470.                 }
  471.               else
  472.                 {
  473.                   *p++ = ((gdouble) light[0]  * frac +
  474.                           (gdouble) opaque[0] * (1.0 - frac));
  475.                   *p++ = ((gdouble) light[1]  * frac +
  476.                           (gdouble) opaque[1] * (1.0 - frac));
  477.                   *p++ = ((gdouble) light[2]  * frac +
  478.                           (gdouble) opaque[2] * (1.0 - frac));
  479.                 }
  480.             }
  481.           else
  482.             {
  483.               if ((gint) frac)
  484.                 {
  485.                   *p++ = dark[0];
  486.                   *p++ = dark[1];
  487.                   *p++ = dark[2];
  488.                 }
  489.               else
  490.                 {
  491.                   *p++ = ((gdouble) dark[0] * frac +
  492.                           (gdouble) opaque[0] * (1.0 - frac));
  493.                   *p++ = ((gdouble) dark[1] * frac +
  494.                           (gdouble) opaque[1] * (1.0 - frac));
  495.                   *p++ = ((gdouble) dark[2] * frac +
  496.                           (gdouble) opaque[2] * (1.0 - frac));
  497.                 }
  498.             }
  499.         }
  500.     }
  501. }
  502. static void
  503. gimp_color_area_render (GimpColorArea *area)
  504. {
  505.   area->needs_render = FALSE;
  506.  
  507.   if (! area->buf)
  508.     return;
  509.  
  510.   _gimp_color_area_render_buf (area->type,
  511.                                area->buf,
  512.                                area->width, area->height, area->rowstride,
  513.                                &area->color);
  514. }
  515.  
  516. static void
  517. gimp_color_area_drag_begin (GtkWidget      *widget,
  518.                 GdkDragContext *context)
  519. {
  520.   GimpRGB    color;
  521.   GtkWidget *window;
  522.   GtkWidget *frame;
  523.   GtkWidget *color_area;
  524.  
  525.   window = gtk_window_new (GTK_WINDOW_POPUP);
  526.   gtk_widget_realize (window);
  527.  
  528.   frame = gtk_frame_new (NULL);
  529.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
  530.   gtk_container_add (GTK_CONTAINER (window), frame);
  531.  
  532.   gimp_color_area_get_color (GIMP_COLOR_AREA (widget), &color);
  533.  
  534.   color_area = gimp_color_area_new (&color,
  535.                                     GIMP_COLOR_AREA (widget)->type,
  536.                                     0);
  537.  
  538.   gtk_widget_set_size_request (color_area,
  539.                                DRAG_PREVIEW_SIZE, DRAG_PREVIEW_SIZE);
  540.   gtk_container_add (GTK_CONTAINER (frame), color_area);
  541.   gtk_widget_show (color_area);
  542.   gtk_widget_show (frame);
  543.  
  544.   g_object_set_data_full (G_OBJECT (widget),
  545.                           "gimp-color-area-drag-window",
  546.                           window,
  547.                           (GDestroyNotify) gtk_widget_destroy);
  548.  
  549.   gtk_drag_set_icon_widget (context, window,
  550.                             DRAG_ICON_OFFSET, DRAG_ICON_OFFSET);
  551. }
  552.  
  553. static void
  554. gimp_color_area_drag_end (GtkWidget      *widget,
  555.                           GdkDragContext *context)
  556. {
  557.   g_object_set_data (G_OBJECT (widget),
  558.                      "gimp-color-area-drag-window", NULL);
  559. }
  560.  
  561. static void
  562. gimp_color_area_drag_data_received (GtkWidget        *widget,
  563.                                     GdkDragContext   *context,
  564.                                     gint              x,
  565.                                     gint              y,
  566.                                     GtkSelectionData *selection_data,
  567.                                     guint             info,
  568.                                     guint             time)
  569. {
  570.   GimpColorArea *area = GIMP_COLOR_AREA (widget);
  571.   GimpRGB        color;
  572.   guint16       *vals;
  573.  
  574.   if (selection_data->length < 0)
  575.     return;
  576.  
  577.   if ((selection_data->format != 16) ||
  578.       (selection_data->length != 8))
  579.     {
  580.       g_warning ("Received invalid color data");
  581.       return;
  582.     }
  583.  
  584.   vals = (guint16 *)selection_data->data;
  585.  
  586.   gimp_rgba_set (&color,
  587.                  (gdouble) vals[0] / 0xffff,
  588.                  (gdouble) vals[1] / 0xffff,
  589.                  (gdouble) vals[2] / 0xffff,
  590.                  (gdouble) vals[3] / 0xffff);
  591.  
  592.   gimp_color_area_set_color (area, &color);
  593. }
  594.  
  595. static void
  596. gimp_color_area_drag_data_get (GtkWidget        *widget,
  597.                                GdkDragContext   *context,
  598.                                GtkSelectionData *selection_data,
  599.                                guint             info,
  600.                                guint             time)
  601. {
  602.   GimpColorArea *area = GIMP_COLOR_AREA (widget);
  603.   guint16        vals[4];
  604.  
  605.   vals[0] = area->color.r * 0xffff;
  606.   vals[1] = area->color.g * 0xffff;
  607.   vals[2] = area->color.b * 0xffff;
  608.  
  609.   if (area->type == GIMP_COLOR_AREA_FLAT)
  610.     vals[3] = 0xffff;
  611.   else
  612.     vals[3] = area->color.a * 0xffff;
  613.  
  614.   gtk_selection_data_set (selection_data,
  615.                           gdk_atom_intern ("application/x-color", FALSE),
  616.                           16, (guchar *) vals, 8);
  617. }
  618.