home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / grid.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-28  |  36.1 KB  |  1,085 lines

  1. /* The GIMP -- an image manipulation program 
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis 
  3.  * 
  4.  * This program is free software; you can redistribute it and/or modify 
  5.  * it under the terms of the GNU General Public License as published by 
  6.  * the Free Software Foundation; either version 2 of the License, or 
  7.  * (at your option) any later version. 
  8.  *
  9.  * This program is distributed in the hope that it will be useful, 
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12.  * GNU General Public License for more details. 
  13.  * 
  14.  * You should have received a copy of the GNU General Public License 
  15.  * along with this program; if not, write to the Free Software 
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  17.  */
  18.  
  19. /* Original plug-in coded by Tim Newsome. 
  20.  * 
  21.  * Changed to make use of real-life units by Sven Neumann <sven@gimp.org>.
  22.  * 
  23.  * The interface code is heavily commented in the hope that it will
  24.  * help other plug-in developers to adapt their plug-ins to make use
  25.  * of the gimp_size_entry functionality. 
  26.  * 
  27.  * Note: There is a convenience constructor called gimp_coordinetes_new ()
  28.  *       which simplifies the task of setting up a standard X,Y sizeentry. 
  29.  *
  30.  * For more info and bugs see libgimp/gimpsizeentry.h and libgimp/gimpwidgets.h
  31.  *
  32.  * May 2000 tim copperfield [timecop@japan.co.jp]
  33.  * http://www.ne.jp/asahi/linux/timecop
  34.  * Added dynamic preview.  Due to weird implementation of signals from all
  35.  * controls, preview will not auto-update.  But this plugin isn't really
  36.  * crying for real-time updating either.
  37.  *
  38.  */
  39.  
  40. #include "config.h"
  41.  
  42. #include <stdlib.h>
  43. #include <stdio.h>
  44. #include <string.h>
  45.  
  46. #include <gtk/gtk.h>
  47.  
  48. #include <libgimp/gimp.h>
  49. #include <libgimp/gimpui.h>
  50.  
  51. #include "libgimp/stdplugins-intl.h"
  52.  
  53.  
  54. #define  SPIN_BUTTON_WIDTH   75
  55. #define  COLOR_BUTTON_WIDTH  55
  56. #define  PREVIEW_SIZE        128
  57.  
  58. /* Declare local functions. */
  59. static void   query  (void);
  60. static void   run    (gchar      *name,
  61.               gint        nparams,
  62.               GimpParam  *param,
  63.               gint       *nreturn_vals,
  64.               GimpParam **return_vals);
  65.  
  66. static guchar      best_cmap_match (guchar       *cmap, 
  67.                     gint          ncolors,
  68.                     guchar       *color);
  69. static void        doit            (gint32        image_ID, 
  70.                     GimpDrawable *drawable, 
  71.                     gboolean      preview_mode);
  72. static gint        dialog          (gint32        image_ID, 
  73.                     GimpDrawable *drawable);
  74. static GtkWidget * preview_widget  (GimpDrawable *drawable);
  75. static void        fill_preview    (GtkWidget    *preview_widget, 
  76.                     GimpDrawable *drawable);
  77.  
  78. GimpPlugInInfo PLUG_IN_INFO =
  79. {
  80.   NULL,  /* init_proc  */
  81.   NULL,  /* quit_proc  */
  82.   query, /* query_proc */
  83.   run,   /* run_proc   */
  84. };
  85.  
  86. gint sx1, sy1, sx2, sy2;
  87. gint run_flag = FALSE;
  88.  
  89. static GtkWidget *hcolor_button;
  90. static GtkWidget *vcolor_button;
  91. static guchar    *preview_bits;
  92. static GtkWidget *preview;
  93.  
  94.  
  95. typedef struct
  96. {
  97.   gint   hwidth;
  98.   gint   hspace;
  99.   gint   hoffset;
  100.   guint8 hcolor[4];
  101.   gint   vwidth;
  102.   gint   vspace;
  103.   gint   voffset;
  104.   guint8 vcolor[4];
  105.   gint   iwidth;
  106.   gint   ispace;
  107.   gint   ioffset;
  108.   guint8 icolor[4];
  109. }
  110. Config;
  111.  
  112. Config grid_cfg =
  113. {
  114.   1, 16, 8, { 0, 0, 128, 255 },    /* horizontal   */
  115.   1, 16, 8, { 0, 0, 128, 255 },    /* vertical     */
  116.   0,  2, 6, { 0, 0, 255, 255 },    /* intersection */
  117. };
  118.  
  119.  
  120. MAIN ()
  121.  
  122. static 
  123. void query (void)
  124. {
  125.   static GimpParamDef args[] =
  126.   {
  127.     { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
  128.     { GIMP_PDB_IMAGE,    "image",    "Input image" },
  129.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  130.  
  131.     { GIMP_PDB_INT32,    "hwidth",   "Horizontal Width   (>= 0)" },
  132.     { GIMP_PDB_INT32,    "hspace",   "Horizontal Spacing (>= 1)" },
  133.     { GIMP_PDB_INT32,    "hoffset",  "Horizontal Offset  (>= 0)" },
  134.     { GIMP_PDB_COLOR,    "hcolor",   "Horizontal Colour" },
  135.     { GIMP_PDB_INT8,     "hopacity", "Horizontal Opacity (0...255)" },
  136.  
  137.     { GIMP_PDB_INT32,    "vwidth",   "Vertical Width   (>= 0)" },
  138.     { GIMP_PDB_INT32,    "vspace",   "Vertical Spacing (>= 1)" },
  139.     { GIMP_PDB_INT32,    "voffset",  "Vertical Offset  (>= 0)" },
  140.     { GIMP_PDB_COLOR,    "vcolor",   "Vertical Colour" },
  141.     { GIMP_PDB_INT8,     "vopacity", "Vertical Opacity (0...255)" },
  142.  
  143.     { GIMP_PDB_INT32,    "iwidth",   "Intersection Width   (>= 0)" },
  144.     { GIMP_PDB_INT32,    "ispace",   "Intersection Spacing (>= 0)" },
  145.     { GIMP_PDB_INT32,    "ioffset",  "Intersection Offset  (>= 0)" },
  146.     { GIMP_PDB_COLOR,    "icolor",   "Intersection Colour" },
  147.     { GIMP_PDB_INT8,     "iopacity", "Intersection Opacity (0...255)" }
  148.   };
  149.   static gint nargs = sizeof (args) / sizeof (args[0]);
  150.  
  151.   gimp_install_procedure ("plug_in_grid",
  152.               "Draws a grid.",
  153.               "Draws a grid using the specified colors. "
  154.               "The grid origin is the upper left corner.",
  155.               "Tim Newsome",
  156.               "Tim Newsome, Sven Neumann, Tom Rathborne, TC",
  157.               "1997 - 2000",
  158.               N_("<Image>/Filters/Render/Pattern/Grid..."),
  159.               "RGB*, GRAY*, INDEXED*",
  160.               GIMP_PLUGIN,
  161.               nargs, 0,
  162.               args, NULL);
  163. }
  164.  
  165. static void
  166. run (gchar      *name, 
  167.      gint        n_params, 
  168.      GimpParam  *param, 
  169.      gint       *nreturn_vals,
  170.      GimpParam **return_vals)
  171. {
  172.   static GimpParam values[1];
  173.   GimpDrawable *drawable;
  174.   gint32 image_ID;
  175.   GimpRunModeType run_mode;
  176.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  177.  
  178.   *nreturn_vals = 1;
  179.   *return_vals = values;
  180.  
  181.   INIT_I18N_UI(); 
  182.  
  183.   run_mode = param[0].data.d_int32;
  184.   image_ID = param[1].data.d_int32;
  185.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  186.  
  187.   if (run_mode == GIMP_RUN_NONINTERACTIVE)
  188.     {
  189.       if (n_params != 18)
  190.     status = GIMP_PDB_CALLING_ERROR;
  191.  
  192.       if (status == GIMP_PDB_SUCCESS)
  193.     {
  194.       grid_cfg.hwidth    = MAX (0, param[3].data.d_int32);
  195.       grid_cfg.hspace    = MAX (1, param[4].data.d_int32);
  196.       grid_cfg.hoffset   = MAX (0, param[5].data.d_int32);
  197.       grid_cfg.hcolor[0] = param[6].data.d_color.red;
  198.       grid_cfg.hcolor[1] = param[6].data.d_color.green;
  199.       grid_cfg.hcolor[2] = param[6].data.d_color.blue;
  200.       grid_cfg.hcolor[3] = param[7].data.d_int8;
  201.  
  202.       grid_cfg.vwidth    = MAX (0, param[8].data.d_int32);
  203.       grid_cfg.vspace    = MAX (1, param[9].data.d_int32);
  204.       grid_cfg.voffset   = MAX (0, param[10].data.d_int32);
  205.       grid_cfg.vcolor[0] = param[11].data.d_color.red;
  206.       grid_cfg.vcolor[1] = param[11].data.d_color.green;
  207.       grid_cfg.vcolor[2] = param[11].data.d_color.blue;
  208.       grid_cfg.vcolor[3] = param[12].data.d_int8;
  209.  
  210.       grid_cfg.iwidth    = MAX (0, param[13].data.d_int32);
  211.       grid_cfg.ispace    = MAX (0, param[14].data.d_int32);
  212.       grid_cfg.ioffset   = MAX (0, param[15].data.d_int32);
  213.       grid_cfg.icolor[0] = param[16].data.d_color.red;
  214.       grid_cfg.icolor[1] = param[16].data.d_color.green;
  215.       grid_cfg.icolor[2] = param[16].data.d_color.blue;
  216.       grid_cfg.icolor[3] = param[17].data.d_int8;
  217.     }
  218.     }
  219.   else
  220.     {
  221.       /*  Possibly retrieve data  */
  222.       gimp_get_data ("plug_in_grid", &grid_cfg);
  223.     }
  224.  
  225.   if (run_mode == GIMP_RUN_INTERACTIVE)
  226.     {
  227.       if (!dialog (image_ID, drawable))
  228.     {
  229.       /* The dialog was closed, or something similarly evil happened. */
  230.       status = GIMP_PDB_EXECUTION_ERROR;
  231.     }
  232.       g_free(preview_bits);
  233.     }
  234.  
  235.   if (grid_cfg.hspace <= 0 || grid_cfg.vspace <= 0)
  236.     {
  237.       status = GIMP_PDB_EXECUTION_ERROR;
  238.     }
  239.  
  240.   if (status == GIMP_PDB_SUCCESS)
  241.     {
  242.       gimp_progress_init (_("Drawing Grid..."));
  243.       gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
  244.       
  245.       doit (image_ID, drawable, FALSE);
  246.       
  247.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  248.     gimp_displays_flush ();
  249.       
  250.       if (run_mode == GIMP_RUN_INTERACTIVE)
  251.     gimp_set_data ("plug_in_grid", &grid_cfg, sizeof (grid_cfg));
  252.  
  253.       gimp_drawable_detach (drawable);
  254.     }
  255.  
  256.   values[0].type = GIMP_PDB_STATUS;
  257.   values[0].data.d_status = status;
  258. }
  259.  
  260.  
  261. #define MAXDIFF 195076
  262.  
  263. static guchar
  264. best_cmap_match (guchar *cmap,
  265.          gint    ncolors,
  266.          guchar *color)
  267. {
  268.   guchar cmap_index = 0;
  269.   gint max = MAXDIFF;
  270.   gint i, diff, sum;
  271.  
  272.   for (i = 0; i < ncolors; i++)
  273.     {
  274.       diff = color[0] - *cmap++;
  275.       sum = SQR (diff);
  276.       diff = color[1] - *cmap++;
  277.       sum += SQR (diff);
  278.       diff = color[2] - *cmap++;
  279.       sum += SQR (diff);
  280.       
  281.       if (sum < max)
  282.     {
  283.       cmap_index = i;
  284.       max = sum;
  285.     }
  286.     }
  287.  
  288.   return cmap_index;
  289. }
  290.  
  291.  
  292. G_INLINE_FUNC void
  293. pix_composite (guchar   *p1, 
  294.            guchar    p2[4], 
  295.            gint      bytes,
  296.            gboolean  blend,
  297.            gboolean  alpha)
  298. {
  299.   gint b;
  300.  
  301.   if (alpha)
  302.     {
  303.       bytes--;
  304.     }
  305.   
  306.   if (blend)
  307.     {
  308.       for (b = 0; b < bytes; b++)
  309.     {
  310.       *p1 = *p1 * (1.0 - p2[3]/255.0) + p2[b] * p2[3]/255.0;
  311.       p1++;
  312.     }
  313.     }
  314.   else
  315.     {
  316.       /* blend should only be TRUE for indexed (bytes == 1) */
  317.       *p1++ = *p2;
  318.     }
  319.  
  320.   if (alpha && *p1 < 255)
  321.     {
  322.       b = *p1 + 255.0 * ((double)p2[3] / (255.0 - *p1));
  323.       *p1 = b > 255 ? 255 : b;
  324.     }
  325. }
  326.  
  327.  
  328. static void
  329. doit (gint32        image_ID,
  330.       GimpDrawable *drawable,
  331.       gboolean      preview_mode)
  332. {
  333.   GimpPixelRgn srcPR, destPR;
  334.   gint width, height, bytes;
  335.   gint x_offset, y_offset;
  336.   guchar *dest;
  337.   gint x, y;
  338.   gboolean alpha;
  339.   gboolean blend;
  340.   guchar hcolor[4];
  341.   guchar vcolor[4];
  342.   guchar icolor[4];
  343.   guchar *cmap;
  344.   gint ncolors;
  345.   
  346.   if (preview_mode) 
  347.     {
  348.       memcpy (hcolor, grid_cfg.hcolor, 4);
  349.       memcpy (vcolor, grid_cfg.vcolor, 4);
  350.       memcpy (icolor, grid_cfg.icolor, 4);
  351.       blend = TRUE;
  352.     } 
  353.   else 
  354.     {
  355.       switch (gimp_image_base_type (image_ID))
  356.     {
  357.     case GIMP_RGB:
  358.       memcpy (hcolor, grid_cfg.hcolor, 4);
  359.       memcpy (vcolor, grid_cfg.vcolor, 4);
  360.       memcpy (icolor, grid_cfg.icolor, 4);
  361.       blend = TRUE;
  362.       break;
  363.  
  364.     case GIMP_GRAY:
  365.       hcolor[0] = INTENSITY (grid_cfg.hcolor[0], grid_cfg.hcolor[1], grid_cfg.hcolor[2]);
  366.       hcolor[3] = grid_cfg.hcolor[3];
  367.       vcolor[0] = INTENSITY (grid_cfg.vcolor[0], grid_cfg.vcolor[1], grid_cfg.vcolor[2]);
  368.       vcolor[3] = grid_cfg.vcolor[3];
  369.       icolor[0] = INTENSITY (grid_cfg.icolor[0], grid_cfg.icolor[1], grid_cfg.icolor[2]);
  370.       icolor[3] = grid_cfg.icolor[3];
  371.       blend = TRUE;
  372.       break;
  373.  
  374.     case GIMP_INDEXED:
  375.       cmap = gimp_image_get_cmap (image_ID, &ncolors);
  376.       hcolor[0] = best_cmap_match (cmap, ncolors, grid_cfg.hcolor);
  377.       hcolor[3] = grid_cfg.hcolor[3];
  378.       vcolor[0] = best_cmap_match (cmap, ncolors, grid_cfg.vcolor);
  379.       vcolor[3] = grid_cfg.vcolor[3];
  380.       icolor[0] = best_cmap_match (cmap, ncolors, grid_cfg.icolor);
  381.       icolor[3] = grid_cfg.icolor[3];
  382.       g_free (cmap);
  383.       blend = FALSE;
  384.       break;
  385.  
  386.     default:
  387.       g_assert_not_reached ();
  388.       blend = FALSE;
  389.     }
  390.     }
  391.   
  392.   if (preview_mode) 
  393.     { 
  394.       sx1 = sy1 = 0;
  395.       sx2 = GTK_PREVIEW (preview)->buffer_width;
  396.       sy2 = GTK_PREVIEW (preview)->buffer_height;
  397.       width  = sx2;
  398.       height = sy2;
  399.       alpha  = 0;
  400.       bytes  = GTK_PREVIEW (preview)->bpp;
  401.     } 
  402.   else 
  403.     {
  404.       /* Get the input area. This is the bounding box of the selection in
  405.        *  the image (or the entire image if there is no selection). 
  406.        */
  407.       gimp_drawable_mask_bounds (drawable->id, &sx1, &sy1, &sx2, &sy2);
  408.       width  = gimp_drawable_width (drawable->id);
  409.       height = gimp_drawable_height (drawable->id);
  410.       alpha  = gimp_drawable_has_alpha (drawable->id);
  411.       bytes  = drawable->bpp;
  412.       
  413.       /*  initialize the pixel regions  */
  414.       gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
  415.       gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE);
  416.     }
  417.  
  418.   dest = g_malloc (width * bytes);
  419.   for (y = sy1; y < sy2; y++)
  420.     {
  421.       if (preview_mode) 
  422.     memcpy (dest, 
  423.         preview_bits + (GTK_PREVIEW (preview)->rowstride * y),
  424.         GTK_PREVIEW (preview)->rowstride);
  425.       else 
  426.     gimp_pixel_rgn_get_row (&srcPR, dest, sx1, y, (sx2 - sx1));
  427.  
  428.       y_offset = y - grid_cfg.voffset;
  429.       while (y_offset < 0)
  430.     y_offset += grid_cfg.vspace;
  431.  
  432.       if ((y_offset + (grid_cfg.vwidth / 2)) % grid_cfg.vspace < grid_cfg.vwidth)
  433.     {
  434.       for (x = sx1; x < sx2; x++)
  435.         {
  436.           pix_composite (&dest[(x-sx1) * bytes], hcolor, bytes, blend, alpha);
  437.         }
  438.     }
  439.  
  440.       if ((y_offset + (grid_cfg.iwidth / 2)) % grid_cfg.vspace < grid_cfg.iwidth)
  441.         {
  442.       for (x = sx1; x < sx2; x++)
  443.         {
  444.           x_offset = grid_cfg.hspace + x - grid_cfg.hoffset;
  445.           while (x_offset < 0)
  446.         x_offset += grid_cfg.hspace;
  447.  
  448.               if ((x_offset % grid_cfg.hspace >= grid_cfg.ispace
  449.            && 
  450.            x_offset % grid_cfg.hspace < grid_cfg.ioffset) 
  451.           || 
  452.           (grid_cfg.hspace - (x_offset % grid_cfg.hspace) >= grid_cfg.ispace
  453.            && 
  454.            grid_cfg.hspace - (x_offset % grid_cfg.hspace) < grid_cfg.ioffset))
  455.         {
  456.           pix_composite (&dest[(x-sx1) * bytes], icolor, bytes, blend, alpha);
  457.                 }
  458.         }
  459.         }
  460.  
  461.       for (x = sx1; x < sx2; x++)
  462.         {
  463.       x_offset = grid_cfg.hspace + x - grid_cfg.hoffset;
  464.       while (x_offset < 0)
  465.         x_offset += grid_cfg.hspace;
  466.  
  467.           if ((x_offset + (grid_cfg.hwidth / 2)) % grid_cfg.hspace < grid_cfg.hwidth)
  468.             {
  469.           pix_composite (&dest[(x-sx1) * bytes], vcolor, bytes, blend, alpha);
  470.             }
  471.  
  472.           if ((x_offset + (grid_cfg.iwidth / 2)) % grid_cfg.hspace < grid_cfg.iwidth
  473.           && 
  474.           ((y_offset % grid_cfg.vspace >= grid_cfg.ispace
  475.         && 
  476.         y_offset % grid_cfg.vspace < grid_cfg.ioffset)
  477.            || 
  478.            (grid_cfg.vspace - (y_offset % grid_cfg.vspace) >= grid_cfg.ispace
  479.         && 
  480.         grid_cfg.vspace - (y_offset % grid_cfg.vspace) < grid_cfg.ioffset)))
  481.             {
  482.           pix_composite (&dest[(x-sx1) * bytes], icolor, bytes, blend, alpha);
  483.             }
  484.         }
  485.       if (preview_mode) 
  486.     {
  487.       memcpy (GTK_PREVIEW (preview)->buffer + (GTK_PREVIEW (preview)->rowstride * y),
  488.           dest,
  489.           GTK_PREVIEW (preview)->rowstride);
  490.     } 
  491.       else 
  492.     {
  493.       gimp_pixel_rgn_set_row (&destPR, dest, sx1, y, (sx2-sx1) );
  494.       gimp_progress_update ((double) y / (double) (sy2 - sy1));
  495.     }
  496.     } 
  497.   g_free (dest);
  498.  
  499.   /*  update the timred region  */
  500.   if (preview_mode) 
  501.     {
  502.       gtk_widget_queue_draw (preview);
  503.     } 
  504.   else 
  505.     {
  506.       gimp_drawable_flush (drawable);
  507.       gimp_drawable_merge_shadow (drawable->id, TRUE);
  508.       gimp_drawable_update (drawable->id, sx1, sy1, sx2 - sx1, sy2 - sy1);
  509.     }
  510. }
  511.  
  512.  
  513. /***************************************************
  514.  * GUI stuff
  515.  */
  516.  
  517. static void
  518. ok_callback (GtkWidget *widget, 
  519.          gpointer   data)
  520. {
  521.   GtkWidget *entry;
  522.  
  523.   run_flag = TRUE;
  524.  
  525.   entry = gtk_object_get_data (GTK_OBJECT (data), "width");
  526.   grid_cfg.hwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  527.   grid_cfg.vwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  528.   grid_cfg.iwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  529.   
  530.   entry = gtk_object_get_data (GTK_OBJECT (data), "space");
  531.   grid_cfg.hspace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  532.   grid_cfg.vspace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  533.   grid_cfg.ispace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  534.  
  535.   entry = gtk_object_get_data (GTK_OBJECT (data), "offset");
  536.   grid_cfg.hoffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  537.   grid_cfg.voffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  538.   grid_cfg.ioffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  539.  
  540.   gtk_widget_destroy (GTK_WIDGET (data));
  541. }
  542.  
  543.  
  544. static void
  545. entry_callback (GtkWidget *widget, 
  546.         gpointer   data)
  547. {
  548.   static gdouble x = -1.0;
  549.   static gdouble y = -1.0;
  550.   gdouble new_x;
  551.   gdouble new_y;
  552.  
  553.   new_x = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
  554.   new_y = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
  555.  
  556.   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (data)))
  557.     {
  558.       if (new_x != x)
  559.     {
  560.       y = new_y = x = new_x;
  561.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1, y);
  562.     }
  563.       if (new_y != y)
  564.     {
  565.       x = new_x = y = new_y;
  566.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0, x);
  567.     }
  568.     }
  569.   else
  570.     {
  571.       if (new_x != x)
  572.     x = new_x;
  573.       if (new_y != y)
  574.     y = new_y;
  575.     }     
  576. }
  577.  
  578.  
  579. static void
  580. color_callback (GtkWidget *widget, 
  581.         gpointer   data)
  582. {
  583.   gint i;
  584.  
  585.   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (data)))
  586.     {
  587.       if (widget == hcolor_button)
  588.     {
  589.       for (i = 0; i < 4; i++)
  590.         grid_cfg.vcolor[i] = grid_cfg.hcolor[i];
  591.       gimp_color_button_update (GIMP_COLOR_BUTTON (vcolor_button));
  592.     }
  593.       else
  594.     {
  595.       for (i = 0; i < 4; i++)
  596.         grid_cfg.hcolor[i] = grid_cfg.vcolor[i];
  597.       gimp_color_button_update (GIMP_COLOR_BUTTON (hcolor_button));
  598.     }    
  599.     }
  600. }
  601.  
  602.  
  603. static void
  604. update_preview_callback (GtkWidget *widget, 
  605.              gpointer   data)
  606. {
  607.   GimpDrawable *drawable;
  608.   GtkWidget    *entry;
  609.  
  610.   drawable = gtk_object_get_data (GTK_OBJECT (widget), "drawable");
  611.  
  612.   entry = gtk_object_get_data (GTK_OBJECT (widget), "width");
  613.   grid_cfg.hwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  614.   grid_cfg.vwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  615.   grid_cfg.iwidth = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  616.  
  617.   entry = gtk_object_get_data (GTK_OBJECT (widget), "space");
  618.   grid_cfg.hspace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  619.   grid_cfg.vspace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  620.   grid_cfg.ispace = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  621.  
  622.   entry = gtk_object_get_data (GTK_OBJECT (widget), "offset");
  623.   grid_cfg.hoffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
  624.   grid_cfg.voffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
  625.   grid_cfg.ioffset = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
  626.  
  627.   doit (0, drawable, TRUE); /* we can set image_ID = 0 'cause we dont use it */
  628. }
  629.  
  630. static gint
  631. dialog (gint32        image_ID,
  632.     GimpDrawable *drawable)
  633. {
  634.   GtkWidget *dlg;
  635.   GtkWidget *main_hbox;
  636.   GtkWidget *main_vbox;
  637.   GtkWidget *frame;
  638.   GtkWidget *abox;
  639.  
  640.   GtkWidget *button;
  641.   GtkWidget *hbox;
  642.   GtkWidget *width;
  643.   GtkWidget *space;
  644.   GtkWidget *offset;
  645.   GtkWidget *chain_button;
  646.   GtkWidget *table;
  647.   GtkWidget *align;
  648.   GimpUnit   unit;
  649.   gdouble    xres;
  650.   gdouble    yres;
  651.  
  652.   gimp_ui_init ("grid", TRUE);
  653.  
  654.   dlg = gimp_dialog_new (_("Grid"), "grid",
  655.              gimp_standard_help_func, "filters/grid.html",
  656.              GTK_WIN_POS_MOUSE,
  657.              FALSE, TRUE, FALSE,
  658.  
  659.              _("OK"), ok_callback,
  660.              NULL, NULL, NULL, TRUE, FALSE,
  661.              _("Cancel"), gtk_widget_destroy,
  662.              NULL, 1, NULL, FALSE, TRUE,
  663.  
  664.              NULL);
  665.  
  666.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  667.               GTK_SIGNAL_FUNC (gtk_main_quit),
  668.               NULL);
  669.  
  670.   /*  Get the image resolution and unit  */
  671.   gimp_image_get_resolution (image_ID, &xres, &yres);
  672.   unit = gimp_image_get_unit (image_ID);
  673.  
  674.   /* main hbox: [   ][       ] */
  675.   main_hbox = gtk_hbox_new (FALSE, 2);
  676.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), main_hbox, FALSE, FALSE, 4);
  677.   /* hbox created and packed into the dialog */
  678.  
  679.   /* make a nice frame */
  680.   frame = gtk_frame_new (_("Preview"));
  681.   gtk_container_set_border_width (GTK_CONTAINER (frame), 4);
  682.   gtk_box_pack_start (GTK_BOX (main_hbox), frame, FALSE, FALSE, 0);
  683.   gtk_widget_show (frame);
  684.  
  685.   hbox = gtk_vbox_new (FALSE, 2);
  686.   gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
  687.   gtk_container_add (GTK_CONTAINER (frame), hbox);
  688.   gtk_widget_show (hbox);
  689.  
  690.   /* row 1 */
  691.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  692.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  693.   gtk_box_pack_start (GTK_BOX (hbox), abox, TRUE, TRUE, 0);
  694.   gtk_widget_show (abox);
  695.   frame = gtk_frame_new (NULL);
  696.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  697.   gtk_container_add (GTK_CONTAINER (abox), frame);
  698.   gtk_widget_show (frame);
  699.   preview = preview_widget (drawable); /* we are here */
  700.   gtk_container_add (GTK_CONTAINER (frame), preview);
  701.   doit (image_ID, drawable, TRUE); /* render preview */
  702.   gtk_widget_show (preview);
  703.  
  704.   /* row 2 */
  705.   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  706.   gtk_container_set_border_width (GTK_CONTAINER (abox), 4);
  707.   gtk_container_add (GTK_CONTAINER (hbox), abox);
  708.   gtk_widget_show (abox);
  709.   button = gtk_button_new_with_label (_("Update Preview"));
  710.   gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
  711.   gtk_object_set_data (GTK_OBJECT (button), "drawable", drawable);
  712.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  713.               GTK_SIGNAL_FUNC (update_preview_callback),
  714.               NULL);
  715.   gtk_container_add (GTK_CONTAINER (abox), button);
  716.   gtk_widget_show (button);
  717.   /* left side of the UI is done */
  718.  
  719.   /* right side */
  720.   frame = gtk_frame_new (_("Parameter Settings"));
  721.   gtk_container_set_border_width (GTK_CONTAINER (frame), 4);
  722.   gtk_box_pack_start (GTK_BOX (main_hbox), frame, FALSE, FALSE, 0);
  723.   gtk_widget_show (frame);
  724.  
  725.   main_vbox = gtk_vbox_new (FALSE, 4);
  726.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  727.   gtk_container_add (GTK_CONTAINER (frame), main_vbox);
  728.   gtk_widget_show (main_vbox);
  729.   
  730.   /*  The width entries  */
  731.   width = gimp_size_entry_new (3,                            /*  number_of_fields  */ 
  732.                    unit,                         /*  unit              */
  733.                    "%a",                         /*  unit_format       */
  734.                    TRUE,                         /*  menu_show_pixels  */
  735.                    TRUE,                         /*  menu_show_percent */
  736.                    FALSE,                        /*  show_refval       */
  737.                    SPIN_BUTTON_WIDTH,            /*  spinbutton_usize  */
  738.                    GIMP_SIZE_ENTRY_UPDATE_SIZE); /*  update_policy     */
  739.   gtk_object_set_data (GTK_OBJECT (button), "width", width);
  740.  
  741.   /*  set the unit back to pixels, since most times we will want pixels */
  742.   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (width), GIMP_UNIT_PIXEL);
  743.  
  744.   /*  set the resolution to the image resolution  */
  745.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 0, xres, TRUE);
  746.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 1, yres, TRUE);
  747.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 2, xres, TRUE);
  748.  
  749.   /*  set the size (in pixels) that will be treated as 0% and 100%  */
  750.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 0, 0.0, (gdouble)(drawable->width));
  751.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 1, 0.0, (gdouble)(drawable->height));
  752.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 2, 0.0, (gdouble)(drawable->width));
  753.  
  754.   /*  set upper and lower limits (in pixels)  */
  755.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 0, 0.0, 
  756.                      (gdouble)(drawable->width));
  757.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 1, 0.0, 
  758.                      (gdouble)(drawable->height));
  759.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 2, 0.0, 
  760.                      (gdouble)(MAX (drawable->width, drawable->height)));
  761.   gtk_table_set_col_spacing (GTK_TABLE (width), 2, 12);
  762.   gtk_table_set_col_spacing (GTK_TABLE (width), 3, 12);
  763.  
  764.   /*  initialize the values  */
  765.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 0, (gdouble)grid_cfg.hwidth);
  766.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 1, (gdouble)grid_cfg.vwidth);
  767.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 2, (gdouble)grid_cfg.iwidth);
  768.  
  769.   /*  attach labels  */
  770.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Horizontal"), 0, 1, 0.0);
  771.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Vertical"), 0, 2, 0.0);
  772.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Intersection"), 0, 3, 0.0);
  773.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Width: "), 1, 0, 0.0); 
  774.  
  775.   /*  put a chain_button under the size_entries  */
  776.   chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
  777.   if (grid_cfg.hwidth == grid_cfg.vwidth)
  778.     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
  779.   gtk_table_attach_defaults (GTK_TABLE (width), chain_button, 1, 3, 2, 3);
  780.   gtk_widget_show (chain_button);
  781.  
  782.   /*  connect to the 'value_changed' signal because we have to take care 
  783.       of keeping the entries in sync when the chainbutton is active        */
  784.   gtk_signal_connect (GTK_OBJECT (width), "value_changed", 
  785.               (GtkSignalFunc) entry_callback, chain_button);
  786.  
  787.   abox = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
  788.   gtk_container_add (GTK_CONTAINER (abox), width);
  789.   gtk_box_pack_start (GTK_BOX (main_vbox), abox, FALSE, FALSE, 4);
  790.   gtk_widget_show (width);
  791.   gtk_widget_show (abox);
  792.  
  793.   /*  The spacing entries  */
  794.   space = gimp_size_entry_new (3,                            /*  number_of_fields  */ 
  795.                    unit,                         /*  unit              */
  796.                    "%a",                         /*  unit_format       */
  797.                    TRUE,                         /*  menu_show_pixels  */
  798.                    TRUE,                         /*  menu_show_percent */
  799.                    FALSE,                        /*  show_refval       */
  800.                    SPIN_BUTTON_WIDTH,            /*  spinbutton_usize  */
  801.                    GIMP_SIZE_ENTRY_UPDATE_SIZE); /*  update_policy     */
  802.   gtk_object_set_data (GTK_OBJECT (button), "space", space);
  803.   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (space), GIMP_UNIT_PIXEL);
  804.  
  805.   /*  set the resolution to the image resolution  */
  806.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 0, xres, TRUE);
  807.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 1, yres, TRUE);
  808.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 2, xres, TRUE);
  809.  
  810.   /*  set the size (in pixels) that will be treated as 0% and 100%  */
  811.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 0, 0.0, (gdouble)(drawable->width));
  812.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 1, 0.0, (gdouble)(drawable->height));
  813.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 2, 0.0, (gdouble)(drawable->width));
  814.  
  815.   /*  set upper and lower limits (in pixels)  */
  816.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 0, 1.0, 
  817.                      (gdouble)(drawable->width));
  818.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 1, 1.0, 
  819.                      (gdouble)(drawable->height));
  820.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 2, 0.0, 
  821.                      (gdouble)(MAX (drawable->width, drawable->height)));
  822.   gtk_table_set_col_spacing (GTK_TABLE (space), 2, 12);
  823.   gtk_table_set_col_spacing (GTK_TABLE (space), 3, 12);
  824.  
  825.   /*  initialize the values  */
  826.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 0, (gdouble)grid_cfg.hspace);
  827.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 1, (gdouble)grid_cfg.vspace);
  828.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 2, (gdouble)grid_cfg.ispace);
  829.   /*  attach labels  */
  830.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (space), _("Spacing: "), 1, 0, 0.0); 
  831.  
  832.   /*  put a chain_button under the spacing_entries  */
  833.   chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
  834.   if (grid_cfg.hspace == grid_cfg.vspace)
  835.     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
  836.   gtk_table_attach_defaults (GTK_TABLE (space), chain_button, 1, 3, 2, 3);
  837.   gtk_widget_show (chain_button);
  838.  
  839.   /*  connect to the 'value_changed' and "unit_changed" signals because we have to 
  840.       take care of keeping the entries in sync when the chainbutton is active        */
  841.   gtk_signal_connect (GTK_OBJECT (space), "value_changed", 
  842.               (GtkSignalFunc) entry_callback, chain_button);
  843.   gtk_signal_connect (GTK_OBJECT (space), "unit_changed", 
  844.               (GtkSignalFunc) entry_callback, chain_button);
  845.  
  846.   abox = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
  847.   gtk_container_add (GTK_CONTAINER (abox), space);
  848.   gtk_box_pack_start (GTK_BOX (main_vbox), abox, FALSE, FALSE, 4);
  849.   gtk_widget_show (space);
  850.   gtk_widget_show (abox);
  851.  
  852.   /*  The offset entries  */
  853.   offset = gimp_size_entry_new (3,                            /*  number_of_fields  */ 
  854.                 unit,                         /*  unit              */
  855.                 "%a",                         /*  unit_format       */
  856.                 TRUE,                         /*  menu_show_pixels  */
  857.                 TRUE,                         /*  menu_show_percent */
  858.                 FALSE,                        /*  show_refval       */
  859.                 SPIN_BUTTON_WIDTH,            /*  spinbutton_usize  */
  860.                 GIMP_SIZE_ENTRY_UPDATE_SIZE); /*  update_policy     */
  861.   gtk_object_set_data (GTK_OBJECT (button), "offset", offset);
  862.   gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (offset), GIMP_UNIT_PIXEL);
  863.  
  864.   /*  set the resolution to the image resolution  */
  865.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 0, xres, TRUE);
  866.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 1, yres, TRUE);
  867.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 2, xres, TRUE);
  868.  
  869.   /*  set the size (in pixels) that will be treated as 0% and 100%  */
  870.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 0, 0.0, (gdouble)(drawable->width));
  871.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 1, 0.0, (gdouble)(drawable->height));
  872.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 2, 0.0, (gdouble)(drawable->width));
  873.  
  874.   /*  set upper and lower limits (in pixels)  */
  875.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 0, 0.0, 
  876.                      (gdouble)(drawable->width));
  877.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 1, 0.0, 
  878.                      (gdouble)(drawable->height));
  879.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 2, 0.0, 
  880.                      (gdouble)(MAX (drawable->width, drawable->height)));
  881.   gtk_table_set_col_spacing (GTK_TABLE (offset), 2, 12);
  882.   gtk_table_set_col_spacing (GTK_TABLE (offset), 3, 12);
  883.  
  884.   /*  initialize the values  */
  885.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 0, (gdouble)grid_cfg.hoffset);
  886.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 1, (gdouble)grid_cfg.voffset);
  887.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 2, (gdouble)grid_cfg.ioffset);
  888.  
  889.   /*  attach labels  */
  890.   gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (offset), _("Offset: "), 1, 0, 0.0); 
  891.  
  892.   /*  this is a weird hack: we put a table into the offset table  */
  893.   table = gtk_table_new (3, 3, FALSE);
  894.   gtk_table_attach_defaults (GTK_TABLE (offset), table, 1, 4, 2, 3);
  895.   gtk_table_set_row_spacing (GTK_TABLE (table), 0, 10);
  896.   gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
  897.  
  898.   /*  put a chain_button under the offset_entries  */
  899.   chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
  900.   if (grid_cfg.hoffset == grid_cfg.voffset)
  901.     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
  902.   gtk_table_attach_defaults (GTK_TABLE (table), chain_button, 0, 2, 0, 1);
  903.   gtk_widget_show (chain_button);
  904.  
  905.   /*  connect to the 'value_changed' and "unit_changed" signals because we have to 
  906.       take care of keeping the entries in sync when the chainbutton is active        */
  907.   gtk_signal_connect (GTK_OBJECT (offset), "value_changed", 
  908.               (GtkSignalFunc) entry_callback, chain_button);
  909.   gtk_signal_connect (GTK_OBJECT (offset), "unit_changed",
  910.               (GtkSignalFunc) entry_callback, chain_button);
  911.  
  912.   /*  put a chain_button under the color_buttons  */
  913.   chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
  914.   if ((guint32)(*grid_cfg.hcolor) == (guint32)(*grid_cfg.vcolor))
  915.     gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
  916.   gtk_table_attach_defaults (GTK_TABLE (table), chain_button, 0, 2, 2, 3);
  917.   gtk_widget_show (chain_button);
  918.  
  919.   /*  attach color selectors  */
  920.   hcolor_button = gimp_color_button_new (_("Horizontal Color"), COLOR_BUTTON_WIDTH, 16, 
  921.                      grid_cfg.hcolor, 4);
  922.   gtk_signal_connect (GTK_OBJECT (hcolor_button), "color_changed", 
  923.               (GtkSignalFunc) color_callback, chain_button);
  924.   align = gtk_alignment_new (0.0, 0.5, 0, 0);
  925.   gtk_container_add (GTK_CONTAINER (align),  hcolor_button);
  926.   gtk_table_attach_defaults (GTK_TABLE (table), align, 0, 1, 1, 2);
  927.   gtk_widget_show (hcolor_button);
  928.   gtk_widget_show (align);
  929.  
  930.   vcolor_button = gimp_color_button_new (_("Vertical Color"), COLOR_BUTTON_WIDTH, 16, 
  931.                      grid_cfg.vcolor, 4);
  932.   gtk_signal_connect (GTK_OBJECT (vcolor_button), "color_changed", 
  933.               (GtkSignalFunc) color_callback, chain_button);  
  934.   align = gtk_alignment_new (0.0, 0.5, 0, 0);
  935.   gtk_container_add (GTK_CONTAINER (align), vcolor_button);
  936.   gtk_table_attach_defaults (GTK_TABLE (table), align, 1, 2, 1, 2);
  937.   gtk_widget_show (vcolor_button);
  938.   gtk_widget_show (align);
  939.  
  940.   button = gimp_color_button_new (_("Intersection Color"), COLOR_BUTTON_WIDTH, 16, 
  941.                   grid_cfg.icolor, 4);
  942.   align = gtk_alignment_new (0.0, 0.5, 0, 0);
  943.   gtk_container_add (GTK_CONTAINER (align), button);
  944.   gtk_table_attach_defaults (GTK_TABLE (table), align, 2, 3, 1, 2);
  945.   gtk_widget_show (button);
  946.   gtk_widget_show (align);
  947.   gtk_widget_show (table);
  948.  
  949.   abox = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
  950.   gtk_container_add (GTK_CONTAINER (abox), offset);
  951.   gtk_box_pack_start (GTK_BOX (main_vbox), abox, FALSE, FALSE, 4);
  952.   gtk_widget_show (offset);
  953.   gtk_widget_show (abox);
  954.  
  955.   gtk_widget_show (main_hbox); 
  956.   gtk_widget_show (dlg);
  957.  
  958.   gtk_object_set_data (GTK_OBJECT (dlg), "width",  width);
  959.   gtk_object_set_data (GTK_OBJECT (dlg), "space",  space);  
  960.   gtk_object_set_data (GTK_OBJECT (dlg), "offset", offset);  
  961.  
  962.   gtk_main ();
  963.   gdk_flush ();
  964.  
  965.   return run_flag;
  966. }
  967.  
  968. static GtkWidget *
  969. preview_widget (GimpDrawable *drawable)
  970. {
  971.   gint       size;
  972.   GtkWidget *preview;
  973.  
  974.   preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  975.   fill_preview (preview, drawable);
  976.   size = GTK_PREVIEW (preview)->rowstride * GTK_PREVIEW (preview)->buffer_height;
  977.   preview_bits = g_malloc (size);
  978.   memcpy (preview_bits, GTK_PREVIEW (preview)->buffer, size);
  979.   
  980.   return preview;
  981. }
  982.  
  983. static void
  984. fill_preview (GtkWidget    *widget, 
  985.           GimpDrawable *drawable)
  986. {
  987.   GimpPixelRgn  srcPR;
  988.   gint          width;
  989.   gint          height;
  990.   gint          x1, x2, y1, y2;
  991.   gint          bpp;
  992.   gint          x, y;
  993.   guchar       *src;
  994.   gdouble       r, g, b, a;
  995.   gdouble       c0, c1;
  996.   guchar       *p0, *p1;
  997.   guchar       *even, *odd;
  998.   
  999.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  1000.  
  1001.   if (x2 - x1 > PREVIEW_SIZE)
  1002.     x2 = x1 + PREVIEW_SIZE;
  1003.   
  1004.   if (y2 - y1 > PREVIEW_SIZE)
  1005.     y2 = y1 + PREVIEW_SIZE;
  1006.   
  1007.   width  = x2 - x1;
  1008.   height = y2 - y1;
  1009.   bpp    = gimp_drawable_bpp (drawable->id);
  1010.   
  1011.   if (width < 1 || height < 1)
  1012.     return;
  1013.  
  1014.   gtk_preview_size (GTK_PREVIEW (widget), width, height);
  1015.  
  1016.   gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, x2, y2, FALSE, FALSE);
  1017.  
  1018.   even = g_malloc (width * 3);
  1019.   odd  = g_malloc (width * 3);
  1020.   src  = g_malloc (width * bpp);
  1021.  
  1022.   for (y = 0; y < height; y++)
  1023.     {
  1024.       gimp_pixel_rgn_get_row (&srcPR, src, x1, y + y1, width);
  1025.       p0 = even;
  1026.       p1 = odd;
  1027.       
  1028.       for (x = 0; x < width; x++) 
  1029.     {
  1030.       if (bpp == 4)
  1031.         {
  1032.           r = ((gdouble)src[x*4+0]) / 255.0;
  1033.           g = ((gdouble)src[x*4+1]) / 255.0;
  1034.           b = ((gdouble)src[x*4+2]) / 255.0;
  1035.           a = ((gdouble)src[x*4+3]) / 255.0;
  1036.         }
  1037.       else if (bpp == 3)
  1038.         {
  1039.           r = ((gdouble)src[x*3+0]) / 255.0;
  1040.           g = ((gdouble)src[x*3+1]) / 255.0;
  1041.           b = ((gdouble)src[x*3+2]) / 255.0;
  1042.           a = 1.0;
  1043.         }
  1044.       else
  1045.         {
  1046.           r = ((gdouble)src[x*bpp+0]) / 255.0;
  1047.           g = b = r;
  1048.           if (bpp == 2)
  1049.         a = ((gdouble)src[x*bpp+1]) / 255.0;
  1050.           else
  1051.         a = 1.0;
  1052.         }
  1053.     
  1054.     if ((x / GIMP_CHECK_SIZE) & 1) 
  1055.       {
  1056.         c0 = GIMP_CHECK_LIGHT;
  1057.         c1 = GIMP_CHECK_DARK;
  1058.       } 
  1059.     else 
  1060.       {
  1061.         c0 = GIMP_CHECK_DARK;
  1062.         c1 = GIMP_CHECK_LIGHT;
  1063.       }
  1064.     
  1065.     *p0++ = (c0 + (r - c0) * a) * 255.0;
  1066.     *p0++ = (c0 + (g - c0) * a) * 255.0;
  1067.     *p0++ = (c0 + (b - c0) * a) * 255.0;
  1068.     
  1069.     *p1++ = (c1 + (r - c1) * a) * 255.0;
  1070.     *p1++ = (c1 + (g - c1) * a) * 255.0;
  1071.     *p1++ = (c1 + (b - c1) * a) * 255.0;
  1072.     
  1073.       } /* for */
  1074.       
  1075.       if ((y / GIMP_CHECK_SIZE) & 1)
  1076.     gtk_preview_draw_row (GTK_PREVIEW (widget), (guchar *)odd,  0, y, width);
  1077.       else
  1078.     gtk_preview_draw_row (GTK_PREVIEW (widget), (guchar *)even, 0, y, width);
  1079.     }
  1080.  
  1081.   g_free (even);
  1082.   g_free (odd);
  1083.   g_free (src);
  1084. }
  1085.