home *** CD-ROM | disk | FTP | other *** search
/ ftp.sunet.sepub/pictures / 2014.11.ftp.sunet.se-pictures.tar / ftp.sunet.se / pub / pictures / ACiD-artpacks / programs / unix / editors / gimp-plugins-unstable-0_99_23_tar.gz / gimp-plugins-unstable-0_99_23_tar / gimp-plugins-unstable-0.99.23 / holes / holes.c next >
C/C++ Source or Header  |  1998-02-19  |  26KB  |  905 lines

  1. /* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer
  2.  * Kimball and Peter Mattis * * This program is free software; you can
  3.  * redistribute it and/or modify * it under the terms of the GNU General
  4.  * Public License as published by * the Free Software Foundation; either
  5.  * version 2 of the License, or * (at your option) any later version. * *
  6.  * This program is distributed in the hope that it will be useful, * but
  7.  * WITHOUT ANY WARRANTY; without even the implied warranty of *
  8.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU
  9.  * General Public License for more details. * * You should have received a
  10.  * copy of the GNU General Public License * along with this program; if not,
  11.  * write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA
  12.  * 02139, USA. */
  13. /* Holes 0.5 */
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <time.h>
  17. #include "libgimp/gimp.h"
  18. #include "gtk/gtk.h"
  19.  
  20. /* Declare local functions. */
  21. static void query(void);
  22. static void run(char *name,
  23.                int nparams,
  24.                GParam * param,
  25.                int *nreturn_vals,
  26.                GParam ** return_vals);
  27.  
  28. static void doit(GDrawable * drawable);
  29.  
  30. /* UI funcs */
  31. #define ENTRY_WIDTH 50
  32. #define SCALE_WIDTH 150
  33.  
  34. static void UI_int_entryscale_new ( GtkTable *table, gint x, gint y,
  35.              guchar *caption, gint *intvar,
  36.              gint min, gint max, gint constraint);
  37. static void UI_paired_entry_destroy_callback (GtkWidget *widget,
  38.                     gpointer data);
  39. static void UI_paired_int_scale_update (GtkAdjustment *adjustment,
  40.                   gpointer      data);
  41. static void UI_paired_int_entry_update (GtkWidget *widget,
  42.                   gpointer   data);
  43. static void UI_float_entryscale_new ( GtkTable *table, gint x, gint y,
  44.              guchar *caption, gfloat *var,
  45.              gfloat min, gfloat max, gint constraint);
  46. static void UI_paired_float_scale_update (GtkAdjustment *adjustment,
  47.                   gpointer      data);
  48. static void UI_paired_float_entry_update (GtkWidget *widget,
  49.                   gpointer   data);
  50. static void UI_close_callback (GtkWidget *widget,
  51.              gpointer   data);
  52. static void UI_ok_callback (GtkWidget *widget,
  53.               gpointer   data);
  54. static gint dialog();
  55.  
  56.  
  57.  
  58.  
  59.  
  60. GPlugInInfo PLUG_IN_INFO =
  61. {
  62.     NULL,                                                    /* init_proc */
  63.     NULL,                                                    /* quit_proc */
  64.     query,                                                /* query_proc */
  65.     run,                                                    /* run_proc */
  66. };
  67.  
  68.  
  69. gint bytes;
  70. gint sx1, sy1, sx2, sy2;
  71. guchar *shape_data= NULL, *tmp= NULL;
  72. gint curve[256];
  73.  
  74. typedef struct {
  75.   gint run;
  76. } HolesInterface;
  77.  
  78. typedef struct {
  79.   GtkObject     *adjustment;
  80.   GtkWidget     *entry;
  81.   gint          constraint;
  82. } EntryScaleData;
  83.  
  84. static HolesInterface pint =
  85. {
  86.   FALSE     /* run */
  87. };
  88.  
  89. typedef struct {
  90.   float density;
  91.   gint shape, size, flag;
  92. } holes_parameters;
  93.  
  94. /* possible shapes */
  95. #define SQUARE_SHAPE 0
  96. #define CIRCLE_SHAPE 1
  97. #define DIAMOND_SHAPE 2
  98. static holes_parameters params = { -3.1, CIRCLE_SHAPE, 8, TRUE };
  99.  
  100.  
  101. MAIN()
  102.  
  103. static void query()
  104. {
  105.     static GParamDef args[] =
  106.     {
  107.         {PARAM_INT32, "run_mode", "Interactive, non-interactive"},
  108.         {PARAM_IMAGE, "image", "Input image (unused)"},
  109.         {PARAM_DRAWABLE, "drawable", "Input drawable"},
  110.         {PARAM_FLOAT, "density", "Density (actually the log of the density)"},
  111.         {PARAM_INT32, "shape", "shape (0= square, 1= round, 2= diamond)"},
  112.         {PARAM_INT32, "size", "size (in pixels)"},
  113.         {PARAM_INT32, "flag", "Clear it if you want to make holes in your image, or set it if you want to keep the painted (I mean opaque) regions."},
  114.     };
  115.     static GParamDef *return_vals = NULL;
  116.     static int nargs = sizeof(args) / sizeof(args[0]);
  117.     static int nreturn_vals = 0;
  118.  
  119.     gimp_install_procedure("plug_in_holes",
  120.                 "make bucks on a image.",
  121.                 "makes holes in the alpha channel of an image, with a density depending on the actual transparency of this image. (so the image must have an alpha channel...)",
  122.                 "Xavier Bouchoux",
  123.                 "Xavier Bouchoux",
  124.                 "1997",
  125.                 "<Image>/Image/Alpha/Holes",
  126.                 "RGBA, GRAYA, INDEXEDA",
  127.                 PROC_PLUG_IN,
  128.                 nargs, nreturn_vals,
  129.                 args, return_vals
  130.     );
  131. }
  132.  
  133. static void run(char *name, int n_params, GParam * param, int *nreturn_vals,
  134.                                 GParam ** return_vals)
  135. {
  136.     static GParam values[1];
  137.     GDrawable *drawable;
  138.     GRunModeType run_mode;
  139.     GStatusType status = STATUS_SUCCESS;
  140.  
  141.     /* initialize */
  142.  
  143.     drawable = NULL;
  144.  
  145.     *nreturn_vals = 1;
  146.     *return_vals = values;
  147.  
  148.     run_mode = param[0].data.d_int32;
  149.  
  150.     if (run_mode == RUN_NONINTERACTIVE) {
  151.         if (n_params != 7) {
  152.             status = STATUS_CALLING_ERROR;
  153.         } else {
  154.             params.density = param[3].data.d_float;
  155.             params.shape = param[4].data.d_int32;
  156.             params.size = param[5].data.d_int32;
  157.             params.flag = param[6].data.d_int32;
  158.         }
  159.     } else {
  160.         /*  Possibly retrieve data  */
  161.         gimp_get_data("plug_in_holes", ¶ms);
  162.  
  163.         if ((run_mode == RUN_INTERACTIVE) ) {
  164.             /* Oh boy. We get to do a dialog box, because we can't really expect the
  165.              * user to set us up with the right values using gdb.
  166.              */
  167.           if (!dialog()) {
  168.             /* The dialog was closed, or something similarly evil happened. */
  169.             status = STATUS_EXECUTION_ERROR;
  170.           }
  171.         }
  172.     }
  173.  
  174.     if (status == STATUS_SUCCESS) {
  175.         /*  Get the specified drawable  */
  176.         drawable = gimp_drawable_get(param[2].data.d_drawable);
  177.  
  178.         /*  Make sure that the drawable is gray or RGB color  */
  179.         if (gimp_drawable_has_alpha (drawable->id) && (gimp_drawable_color(drawable->id) || gimp_drawable_gray(drawable->id) ||gimp_drawable_indexed (drawable->id))) {
  180.             gimp_progress_init("holes");
  181.             gimp_tile_cache_ntiles(4);
  182.  
  183.             srand(time(NULL));
  184.             doit(drawable);
  185.  
  186.             if (run_mode != RUN_NONINTERACTIVE)
  187.                     gimp_displays_flush();
  188.  
  189.             if (run_mode == RUN_INTERACTIVE)
  190.                 gimp_set_data("plug_in_holes", ¶ms, sizeof(params));
  191.         } else {
  192.             status = STATUS_EXECUTION_ERROR;
  193.         }
  194.     }
  195.  
  196.     values[0].type = PARAM_STATUS;
  197.     values[0].data.d_status = status;
  198.  
  199.     gimp_drawable_detach(drawable);
  200. }
  201.  
  202.  
  203. static void make_curve (gdouble  sigma)
  204. {
  205.   gdouble sigma2;
  206.   gint i;
  207.  
  208.   sigma2 = 2 * sigma * sigma;
  209.  
  210.   if (params.flag)
  211.     for (i = 0; i < 256; i++)
  212.       {
  213.     curve[i] = (gint) (exp (- (i/22.0 * i/22.0) / sigma2) * RAND_MAX);
  214.       }
  215.   else
  216.     for (i = 0; i < 256; i++)
  217.       {
  218.     curve[255-i] = (gint) (exp (- (i/22.0 * i/22.0) / sigma2) * RAND_MAX);
  219.       }
  220.  
  221. }
  222.  
  223.  
  224. /* Prepare the shape data: makes a buffer with the shape calculated with
  225.  * the goood size.
  226.  */
  227. static int prepare_shape()
  228. {
  229.   int i,j, center;
  230.  
  231.   shape_data = malloc(params.size*params.size);
  232.   if (shape_data == NULL)
  233.     return 0;
  234.  
  235.   switch (params.shape) {
  236.   case SQUARE_SHAPE:
  237.     for (i=0;i<params.size; i++)
  238.       for (j=0; j<params.size; j++)
  239.     shape_data[i*params.size+j]=255;
  240.     break;
  241.   case CIRCLE_SHAPE:
  242.      center= params.size/2;
  243.      for (i=0;i<params.size; i++)
  244.       for (j=0; j<params.size; j++)
  245.     if (((i-center)*(i-center)+(j-center)*(j-center))<center*center)
  246.       shape_data[i*params.size+j]=255;
  247.     else
  248.       shape_data[i*params.size+j]=0;
  249.     break;
  250.   case DIAMOND_SHAPE:
  251.      center= params.size/2;
  252.      for (i=0; i<params.size; i++)
  253.        for (j=0; j<params.size; j++)
  254.          if ((abs(i-center)+abs(j-center))<center)
  255.           shape_data[i*params.size+j]=255;
  256.         else
  257.           shape_data[i*params.size+j]=0;
  258.      break;
  259.   default:
  260.     return 0;
  261.   }
  262.   return 1;
  263. }
  264.  
  265. /*
  266.  * Puts the shape (=the buck) in the alpha layer of the destination image,
  267.  * at position x,y
  268.  */
  269. static void make_hole(GPixelRgn * region, int x, int y)
  270. {
  271.     int j, k, startx=0, starty=0;
  272.     int rx1, rx2, ry1, ry2;
  273.     int new;
  274.  
  275.     rx1 = x - params.size / 2;
  276.     rx2 = rx1 + params.size;
  277.     ry1 = y - params.size  / 2;
  278.     ry2 = ry1 + params.size;
  279.  
  280.     /* clipping */
  281.     if (rx1 < sx1) {
  282.       startx=sx1-rx1;
  283.       rx1= sx1;
  284.     }
  285.     if (ry1 < sy1) {
  286.       starty=sy1-ry1;
  287.       ry1= sy1;
  288.     }
  289.     rx2 = rx2 > sx2 ? sx2 : rx2;
  290.     ry2 = ry2 > sy2 ? sy2 : ry2;
  291.  
  292.      gimp_pixel_rgn_get_rect(region, tmp, rx1, ry1, rx2 - rx1, ry2 - ry1);
  293.     for (k = 0; k < (ry2 - ry1); k++) {
  294.         for (j = 0; j < (rx2 - rx1); j++) {
  295.           new= tmp[((rx2-rx1)*k+j) * bytes + (bytes-1)]-
  296.                        shape_data[params.size*(k+starty)+j+startx];
  297.           tmp[((rx2-rx1)*k+j) * bytes + (bytes-1)]= (new<=0? 0 : new);
  298.         }
  299.     }
  300.     gimp_pixel_rgn_set_rect(region, tmp, rx1, ry1, rx2 - rx1, ry2 - ry1);
  301. }
  302.  
  303. static void make_hole_inv(GPixelRgn * region, int x, int y)
  304. {
  305.     int j, k, startx=0, starty=0;
  306.     int rx1, rx2, ry1, ry2;
  307.     int new;
  308.  
  309.     rx1 = x - params.size / 2;
  310.     rx2 = rx1 + params.size;
  311.     ry1 = y - params.size  / 2;
  312.     ry2 = ry1 + params.size;
  313.  
  314.     /* clipping */
  315.     if (rx1 < sx1) {
  316.       startx=sx1-rx1;
  317.       rx1= sx1;
  318.     }
  319.     if (ry1 < sy1) {
  320.       starty=sy1-ry1;
  321.       ry1= sy1;
  322.     }
  323.     rx2 = rx2 > sx2 ? sx2 : rx2;
  324.     ry2 = ry2 > sy2 ? sy2 : ry2;
  325.  
  326.      gimp_pixel_rgn_get_rect(region, tmp, rx1, ry1, rx2 - rx1, ry2 - ry1);
  327.     for (k = 0; k < (ry2 - ry1); k++) {
  328.         for (j = 0; j < (rx2 - rx1); j++) {
  329.           new= tmp[((rx2-rx1)*k+j) * bytes + (bytes-1)]+
  330.                        shape_data[params.size*(k+starty)+j+startx];
  331.           tmp[((rx2-rx1)*k+j) * bytes + (bytes-1)]= (new>255? 255 : new);
  332.         }
  333.     }
  334.     gimp_pixel_rgn_set_rect(region, tmp, rx1, ry1, rx2 - rx1, ry2 - ry1);
  335. }
  336.  
  337. static void doit(GDrawable * drawable)
  338. {
  339.     GPixelRgn srcPR, destPR;
  340.     gint width, height;
  341.     int x, y;
  342.     int col, row, b;
  343.     guchar *src_row, *dest_row;
  344.     guchar *src, *dest;
  345.     gint progress, max_progress;
  346.     gpointer pr;
  347.     guchar val_alpha;
  348.  
  349.     /* Get the input area. This is the bounding box of the selection in
  350.      *  the image (or the entire image if there is no selection). Only
  351.      *  operating on the input area is simply an optimization. It doesn't
  352.      *  need to be done for correct operation. (It simply makes it go
  353.      *  faster, since fewer pixels need to be operated on).
  354.      */
  355.     gimp_drawable_mask_bounds(drawable->id, &sx1, &sy1, &sx2, &sy2);
  356.  
  357.     /* Get the size of the input image. (This will/must be the same
  358.      *  as the size of the output image.
  359.      */
  360.     width = drawable->width;
  361.     height = drawable->height;
  362.     bytes = drawable->bpp;
  363.  
  364.     /* initialize buffers and data */
  365.     tmp = (guchar *) malloc(params.size * params.size * bytes);
  366.     if (tmp == NULL) {
  367.         return;
  368.     }
  369.     if (!prepare_shape())
  370.       return;
  371.     make_curve(exp(-params.density));
  372.  
  373.     progress = 0;
  374.     max_progress = (sx2 - sx1) * (sy2 - sy1) * 2;
  375.  
  376.  
  377.  
  378.     /*  initialize the pixel regions  */
  379.     gimp_pixel_rgn_init(&srcPR, drawable, sx1, sy1, sx2-sx1, sy2-sy1, FALSE, FALSE);
  380.     gimp_pixel_rgn_init(&destPR, drawable, sx1, sy1, sx2-sx1, sy2-sy1, TRUE, TRUE);
  381.  
  382.     /* First off, copy the old one to the new one. */
  383.     if (params.flag)
  384.       val_alpha=0;
  385.     else
  386.       val_alpha=255;
  387.  
  388.     for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR);
  389.          pr != NULL; pr = gimp_pixel_rgns_process (pr)) {
  390.       src_row = srcPR.data;
  391.       dest_row = destPR.data;
  392.       for ( row = 0; row < srcPR.h; row++) {
  393.         src = src_row;
  394.         dest = dest_row;
  395.         for ( col = 0; col < srcPR.w; col++) {
  396.           for (b=0; b< bytes-1; b++)
  397.         *dest++ = *src++;
  398.           src++;       /* set alpha to opaque */
  399.           *dest++ = val_alpha;
  400.         }
  401.         src_row += srcPR.rowstride;
  402.         dest_row += destPR.rowstride;
  403.       }
  404.       progress += srcPR.w * srcPR.h;
  405.       gimp_progress_update ((double) progress / (double) max_progress);
  406.     }
  407.  
  408.     /* Do the effect */
  409.     gimp_pixel_rgn_init(&srcPR, drawable, sx1, sy1, sx2-sx1, sy2-sy1, FALSE, FALSE);
  410.     if (params.flag) {
  411.       for (pr = gimp_pixel_rgns_register (1, &srcPR);
  412.            pr != NULL; pr = gimp_pixel_rgns_process (pr)) {
  413.         src_row = srcPR.data;
  414.         for ( row = 0, y = srcPR.y; row < srcPR.h; row++, y++) {
  415.           src = src_row;
  416.           for ( col = 0, x = srcPR.x; col < srcPR.w; col++, x++) {
  417.         if (curve[src[bytes-1]]<rand()) {
  418.           make_hole_inv(&destPR, x, y);
  419.         }
  420.         src+= bytes;
  421.           }
  422.           src_row += srcPR.rowstride;
  423.         }
  424.         progress += srcPR.w * srcPR.h;
  425.         gimp_progress_update ((double) progress / (double) max_progress);
  426.       }
  427.     } else {
  428.       for (pr = gimp_pixel_rgns_register (1, &srcPR);
  429.            pr != NULL; pr = gimp_pixel_rgns_process (pr)) {
  430.         src_row = srcPR.data;
  431.         for ( row = 0, y = srcPR.y; row < srcPR.h; row++, y++) {
  432.           src = src_row;
  433.           for ( col = 0, x = srcPR.x; col < srcPR.w; col++, x++) {
  434.         if (curve[src[bytes-1]]<rand()) {
  435.           make_hole(&destPR, x, y);
  436.         }
  437.         src+= bytes;
  438.           }
  439.           src_row += srcPR.rowstride;
  440.         }
  441.         progress += srcPR.w * srcPR.h;
  442.         gimp_progress_update ((double) progress / (double) max_progress);
  443.       }
  444.     }
  445.     /* free the mem */
  446.     free(tmp);
  447.     free(shape_data);
  448.  
  449.  
  450.     /*  update the region  */
  451.     gimp_drawable_flush(drawable);
  452.     gimp_drawable_merge_shadow(drawable->id, TRUE);
  453.     gimp_drawable_update(drawable->id, sx1, sy1, sx2 - sx1, sy2 - sy1);
  454. }
  455.  
  456. /***************************************************
  457.  * GUI stuff
  458.  */
  459. /*===================================================================
  460.  
  461.          Entry - Scale Pair
  462.  
  463. ====================================================================*/
  464.  
  465.  
  466. /***********************************************************************/
  467. /*                                                                     */
  468. /*    Create new entry-scale pair with label. (int)                    */
  469. /*    1 row and 2 cols of table are needed.                            */
  470. /*                                                                     */
  471. /*    `x' and `y' means starting row and col in `table'.               */
  472. /*                                                                     */
  473. /*    `caption' is label string.                                       */
  474. /*                                                                     */
  475. /*    `min', `max' are boundary of scale.                              */
  476. /*                                                                     */
  477. /*    `constraint' means whether value of *intvar should be constraint */
  478. /*    by scale adjustment, e.g. between `min' and `max'.               */
  479. /*                                                                     */
  480. /***********************************************************************/
  481.  
  482. static void
  483. UI_int_entryscale_new ( GtkTable *table, gint x, gint y,
  484.              guchar *caption, gint *intvar,
  485.              gint min, gint max, gint constraint)
  486. {
  487.   GtkWidget *hbox;
  488.   GtkWidget *label;
  489.   GtkWidget *entry;
  490.   GtkWidget *scale;
  491.   GtkObject *adjustment;
  492.   EntryScaleData *userdata;
  493.   guchar    buffer[256];
  494.  
  495.   label = gtk_label_new (caption);
  496.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  497.  
  498.   adjustment = gtk_adjustment_new ( *intvar, min, max, 1.0, 1.0, 0.0);
  499.   scale = gtk_hscale_new ( GTK_ADJUSTMENT(adjustment) );
  500.   gtk_widget_set_usize (scale, SCALE_WIDTH, 0);
  501.   gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
  502.  
  503.   entry = gtk_entry_new ();
  504.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  505.   sprintf( buffer, "%d", *intvar );
  506.   gtk_entry_set_text( GTK_ENTRY (entry), buffer );
  507.  
  508.  
  509.   userdata = g_new ( EntryScaleData, 1 );
  510.   userdata->entry = entry;
  511.   userdata->adjustment = adjustment;
  512.   userdata->constraint = constraint;
  513.   gtk_object_set_user_data (GTK_OBJECT(entry), userdata);
  514.   gtk_object_set_user_data (GTK_OBJECT(adjustment), userdata);
  515.  
  516.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  517.               (GtkSignalFunc) UI_paired_int_entry_update,
  518.               intvar);
  519.   gtk_signal_connect ( adjustment, "value_changed",
  520.               (GtkSignalFunc) UI_paired_int_scale_update,
  521.               intvar);
  522.   gtk_signal_connect ( GTK_OBJECT( entry ), "destroy",
  523.               (GtkSignalFunc) UI_paired_entry_destroy_callback,
  524.               userdata );
  525.  
  526.  
  527.   hbox = gtk_hbox_new ( FALSE, 5 );
  528.   gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
  529.   gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, TRUE, 0);
  530.  
  531.   gtk_table_attach (GTK_TABLE (table), label, x, x+1, y, y+1,
  532.             GTK_FILL, GTK_FILL, 0, 0);
  533.   gtk_table_attach (GTK_TABLE (table), hbox, x+1, x+2, y, y+1,
  534.             GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  535.  
  536.   gtk_widget_show (label);
  537.   gtk_widget_show (entry);
  538.   gtk_widget_show (scale);
  539.   gtk_widget_show (hbox);
  540. }
  541.  
  542.  
  543. /*
  544.    when destroyed, userdata is destroyed too
  545. */
  546. static void
  547. UI_paired_entry_destroy_callback (GtkWidget *widget,
  548.                     gpointer data)
  549. {
  550.   EntryScaleData *userdata;
  551.   userdata = data;
  552.   g_free ( userdata );
  553. }
  554.  
  555. /* scale callback (int) */
  556. /* ==================== */
  557.  
  558. static void
  559. UI_paired_int_scale_update (GtkAdjustment *adjustment,
  560.                   gpointer      data)
  561. {
  562.   EntryScaleData *userdata;
  563.   GtkEntry *entry;
  564.   gchar buffer[256];
  565.   gint *val, new_val;
  566.  
  567.   val = data;
  568.   new_val = (gint) adjustment->value;
  569.  
  570.   *val = new_val;
  571.  
  572.   userdata = gtk_object_get_user_data (GTK_OBJECT (adjustment));
  573.   entry = GTK_ENTRY( userdata->entry );
  574.   sprintf (buffer, "%d", (int) new_val );
  575.   /* avoid infinite loop (scale, entry, scale, entry ...) */
  576.   gtk_signal_handler_block_by_data ( GTK_OBJECT(entry), data );
  577.   gtk_entry_set_text ( entry, buffer);
  578.   gtk_signal_handler_unblock_by_data ( GTK_OBJECT(entry), data );
  579. }
  580.  
  581. /*
  582.    entry callback (int)
  583. */
  584.  
  585. static void
  586. UI_paired_int_entry_update (GtkWidget *widget,
  587.                   gpointer   data)
  588. {
  589.   EntryScaleData *userdata;
  590.   GtkAdjustment *adjustment;
  591.   int new_val, constraint_val;
  592.   int *val;
  593.  
  594.   val = data;
  595.   new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
  596.   *val = new_val;
  597.  
  598.   userdata = gtk_object_get_user_data (GTK_OBJECT (widget));
  599.   adjustment = GTK_ADJUSTMENT( userdata->adjustment );
  600.  
  601.   constraint_val = new_val;
  602.   if ( constraint_val < adjustment->lower )
  603.     constraint_val = adjustment->lower;
  604.   if ( constraint_val > adjustment->upper )
  605.     constraint_val = adjustment->upper;
  606.  
  607.   if ( userdata->constraint )
  608.     *val = constraint_val;
  609.   else
  610.     *val = new_val;
  611.  
  612.   adjustment->value = constraint_val;
  613.   gtk_signal_handler_block_by_data ( GTK_OBJECT(adjustment), data );
  614.   gtk_signal_emit_by_name ( GTK_OBJECT(adjustment), "value_changed");
  615.   gtk_signal_handler_unblock_by_data ( GTK_OBJECT(adjustment), data );
  616. }
  617.  
  618.  
  619.  
  620. /* The same thing, but with floats... */
  621.  
  622. static void
  623. UI_float_entryscale_new ( GtkTable *table, gint x, gint y,
  624.              guchar *caption, gfloat *var,
  625.              gfloat min, gfloat max, gint constraint)
  626. {
  627.   GtkWidget *hbox;
  628.   GtkWidget *label;
  629.   GtkWidget *entry;
  630.   GtkWidget *scale;
  631.   GtkObject *adjustment;
  632.   EntryScaleData *userdata;
  633.   guchar    buffer[256];
  634.  
  635.   label = gtk_label_new (caption);
  636.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  637.  
  638.   adjustment = gtk_adjustment_new ( *var, min, max, 1.0, 1.0, 0.0);
  639.   scale = gtk_hscale_new ( GTK_ADJUSTMENT(adjustment) );
  640.   gtk_widget_set_usize (scale, SCALE_WIDTH, 0);
  641.   gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
  642.  
  643.   entry = gtk_entry_new ();
  644.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  645.   sprintf( buffer, "%1.2f", *var );
  646.   gtk_entry_set_text( GTK_ENTRY (entry), buffer );
  647.  
  648.  
  649.   userdata = g_new ( EntryScaleData, 1 );
  650.   userdata->entry = entry;
  651.   userdata->adjustment = adjustment;
  652.   userdata->constraint = constraint;
  653.   gtk_object_set_user_data (GTK_OBJECT(entry), userdata);
  654.   gtk_object_set_user_data (GTK_OBJECT(adjustment), userdata);
  655.  
  656.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  657.               (GtkSignalFunc) UI_paired_float_entry_update,
  658.               var);
  659.   gtk_signal_connect ( adjustment, "value_changed",
  660.               (GtkSignalFunc) UI_paired_float_scale_update,
  661.               var);
  662.   gtk_signal_connect ( GTK_OBJECT( entry ), "destroy",
  663.               (GtkSignalFunc) UI_paired_entry_destroy_callback,
  664.               userdata );
  665.  
  666.  
  667.   hbox = gtk_hbox_new ( FALSE, 5 );
  668.   gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
  669.   gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, TRUE, 0);
  670.  
  671.   gtk_table_attach (GTK_TABLE (table), label, x, x+1, y, y+1,
  672.             GTK_FILL, GTK_FILL, 0, 0);
  673.   gtk_table_attach (GTK_TABLE (table), hbox, x+1, x+2, y, y+1,
  674.             GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  675.  
  676.   gtk_widget_show (label);
  677.   gtk_widget_show (entry);
  678.   gtk_widget_show (scale);
  679.   gtk_widget_show (hbox);
  680. }
  681.  
  682.  
  683. /* scale callback (float) */
  684. /* ==================== */
  685.  
  686. static void
  687. UI_paired_float_scale_update (GtkAdjustment *adjustment,
  688.                   gpointer      data)
  689. {
  690.   EntryScaleData *userdata;
  691.   GtkEntry *entry;
  692.   gchar buffer[256];
  693.   gfloat *val, new_val;
  694.  
  695.   val = data;
  696.   new_val = (gfloat) adjustment->value;
  697.  
  698.   *val = new_val;
  699.  
  700.   userdata = gtk_object_get_user_data (GTK_OBJECT (adjustment));
  701.   entry = GTK_ENTRY( userdata->entry );
  702.   sprintf (buffer, "%1.2f", (gfloat) new_val );
  703.   /* avoid infinite loop (scale, entry, scale, entry ...) */
  704.   gtk_signal_handler_block_by_data ( GTK_OBJECT(entry), data );
  705.   gtk_entry_set_text ( entry, buffer);
  706.   gtk_signal_handler_unblock_by_data ( GTK_OBJECT(entry), data );
  707. }
  708.  
  709. /*
  710.    entry callback (float)
  711. */
  712.  
  713. static void
  714. UI_paired_float_entry_update (GtkWidget *widget,
  715.                   gpointer   data)
  716. {
  717.   EntryScaleData *userdata;
  718.   GtkAdjustment *adjustment;
  719.   gfloat new_val, constraint_val;
  720.   gfloat *val;
  721.  
  722.   val = data;
  723.   new_val = atof (gtk_entry_get_text (GTK_ENTRY (widget)));
  724.   *val = new_val;
  725.  
  726.   userdata = gtk_object_get_user_data (GTK_OBJECT (widget));
  727.   adjustment = GTK_ADJUSTMENT( userdata->adjustment );
  728.  
  729.   constraint_val = new_val;
  730.   if ( constraint_val < adjustment->lower )
  731.     constraint_val = adjustment->lower;
  732.   if ( constraint_val > adjustment->upper )
  733.     constraint_val = adjustment->upper;
  734.  
  735.   if ( userdata->constraint )
  736.     *val = constraint_val;
  737.   else
  738.     *val = new_val;
  739.  
  740.   adjustment->value = constraint_val;
  741.   gtk_signal_handler_block_by_data ( GTK_OBJECT(adjustment), data );
  742.   gtk_signal_emit_by_name ( GTK_OBJECT(adjustment), "value_changed");
  743.   gtk_signal_handler_unblock_by_data ( GTK_OBJECT(adjustment), data );
  744. }
  745.  
  746.  
  747. static void
  748. UI_toggle_update (GtkWidget *widget,
  749.                gpointer   data)
  750. {
  751.   int *toggle_val;
  752.  
  753.   toggle_val = (int *) data;
  754.  
  755.   if (GTK_TOGGLE_BUTTON (widget)->active)
  756.     *toggle_val = TRUE;
  757.   else
  758.     *toggle_val = FALSE;
  759. }
  760.  
  761.  
  762. void shape_radio_callback(GtkWidget *widget, GtkRadioButton *button)
  763. {
  764.   gint id;
  765.   /* Get radio button ID */
  766.   id=(gint)gtk_object_get_data(GTK_OBJECT(button),"Radio_ID");
  767.  
  768.   if (GTK_TOGGLE_BUTTON(button)->active==TRUE) {
  769.       params.shape= id;
  770.     }
  771. }
  772.  
  773.  
  774.  
  775. /***  Dialog interface ***/
  776.  
  777. static void
  778. UI_close_callback (GtkWidget *widget,
  779.              gpointer   data)
  780. {
  781.   gtk_main_quit ();
  782. }
  783.  
  784. static void
  785. UI_ok_callback (GtkWidget *widget,
  786.               gpointer   data)
  787. {
  788.   pint.run = TRUE;
  789.   gtk_widget_destroy (GTK_WIDGET (data));
  790. }
  791.  
  792. static gint
  793. dialog ()
  794. {
  795.   GtkWidget *dlg;
  796.   GtkWidget *frame;
  797.   GtkWidget *table;
  798.   GtkWidget *button;
  799.   GtkWidget *toggle;
  800.   GSList *group = NULL;
  801.  
  802.   gchar **argv;
  803.   gint  argc;
  804.  
  805.   argc = 1;
  806.   argv = g_new (gchar *, 1);
  807.   argv[0] = g_strdup ("holes");
  808.  
  809.   gtk_init (&argc, &argv);
  810.   gtk_rc_parse (gimp_gtkrc ());
  811.  
  812.   dlg = gtk_dialog_new ();
  813.   gtk_window_set_title (GTK_WINDOW (dlg), "Holes");
  814.   gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
  815.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  816.               (GtkSignalFunc) UI_close_callback,
  817.               NULL);
  818.  
  819.   /*  Action area  */
  820.   button = gtk_button_new_with_label ("OK");
  821.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  822.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  823.                       (GtkSignalFunc) UI_ok_callback,
  824.                       dlg);
  825.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
  826.   gtk_widget_grab_default (button);
  827.   gtk_widget_show (button);
  828.  
  829.   button = gtk_button_new_with_label ("Cancel");
  830.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  831.   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
  832.                  (GtkSignalFunc) gtk_widget_destroy,
  833.                  GTK_OBJECT (dlg));
  834.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
  835.   gtk_widget_show (button);
  836.  
  837.   /*  parameter settings  */
  838.   frame = gtk_frame_new ("Parameter Settings");
  839.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  840.   gtk_container_border_width (GTK_CONTAINER (frame), 10);
  841.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  842.   table = gtk_table_new (6, 2, FALSE);
  843.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  844.   gtk_container_add (GTK_CONTAINER (frame), table);
  845.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  846.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  847.  
  848.   UI_int_entryscale_new( GTK_TABLE (table), 0, 1,
  849.               "Size:", ¶ms.size,
  850.               1, 100, TRUE );
  851.   UI_float_entryscale_new( GTK_TABLE (table), 0, 2,
  852.               "Density:", ¶ms.density,
  853.               -5.0, 5.0, TRUE );
  854.   toggle = gtk_check_button_new_with_label ("Keep opaque");
  855.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 3, 4,
  856.                     GTK_FILL, GTK_FILL, 0, 0);
  857.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  858.                       (GtkSignalFunc) UI_toggle_update,
  859.                       ¶ms.flag);
  860.   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), params.flag);
  861.   gtk_widget_show(toggle);
  862.  
  863.   toggle = gtk_radio_button_new_with_label (group,"square shaped holes");
  864.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  865.   gtk_table_attach (GTK_TABLE (table), toggle, 1, 2, 3, 4,
  866.                     GTK_FILL, GTK_FILL, 0, 0);
  867.   gtk_signal_connect_object (GTK_OBJECT (toggle), "toggled",
  868.               (GtkSignalFunc) shape_radio_callback,
  869.               (gpointer)toggle);
  870.   gtk_object_set_data(GTK_OBJECT(toggle),"Radio_ID",(gpointer)SQUARE_SHAPE);
  871.   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), (params.shape == SQUARE_SHAPE));
  872.   gtk_widget_show (toggle);
  873.  
  874.   toggle = gtk_radio_button_new_with_label (group,"round shaped holes");
  875.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  876.   gtk_table_attach (GTK_TABLE (table), toggle, 1, 2, 4, 5,
  877.                     GTK_FILL, GTK_FILL, 0, 0);
  878.   gtk_signal_connect_object (GTK_OBJECT (toggle), "toggled",
  879.               (GtkSignalFunc) shape_radio_callback,
  880.               (gpointer)toggle);
  881.   gtk_object_set_data(GTK_OBJECT(toggle),"Radio_ID",(gpointer)CIRCLE_SHAPE);
  882.   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), (params.shape == CIRCLE_SHAPE));
  883.   gtk_widget_show (toggle);
  884.  
  885.   toggle = gtk_radio_button_new_with_label (group,"diamond shaped holes");
  886.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  887.   gtk_table_attach (GTK_TABLE (table), toggle, 1, 2, 5, 6,
  888.                     GTK_FILL, GTK_FILL, 0, 0);
  889.   gtk_signal_connect_object (GTK_OBJECT (toggle), "toggled",
  890.               (GtkSignalFunc) shape_radio_callback,
  891.               (gpointer)toggle);
  892.   gtk_object_set_data(GTK_OBJECT(toggle),"Radio_ID",(gpointer)DIAMOND_SHAPE);
  893.   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), (params.shape == DIAMOND_SHAPE));
  894.   gtk_widget_show (toggle);
  895.  
  896.   gtk_widget_show (frame);
  897.   gtk_widget_show (table);
  898.   gtk_widget_show (dlg);
  899.  
  900.   gtk_main ();
  901.   gdk_flush ();
  902.  
  903.   return pint.run;
  904. }
  905.