Lazza

Gimp kaleidoscope.c

Nov 1st, 2008
138
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * Kaleidoscope
  5.  * Copyright (C) 1999, 2002 Kelly Martin, updated 2005 by Matthew Plough
  6.  * kelly@gimp.org
  7.  * Derived from WhirlPinch
  8.  * Copyright (C) 1997 Federico Mena Quintero
  9.  * federico@nuclecu.unam.mx
  10.  * Copyright (C) 1997 Scott Goehring
  11.  * scott@poverty.bloomington.in.us
  12.  *
  13.  * This program is free software; you can redistribute it and/or modify
  14.  * it under the terms of the GNU General Public License as published by
  15.  * the Free Software Foundation; either version 2 of the License, or
  16.  * (at your option) any later version.
  17.  *
  18.  * This program is distributed in the hope that it will be useful,
  19.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.  * GNU General Public License for more details.
  22.  *
  23.  * You should have received a copy of the GNU General Public License
  24.  * along with this program; if not, write to the Free Software
  25.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  26.  */
  27.  
  28. /* Version 0.10-2
  29.  *
  30.  * Updated for GIMP 2. Still needs some dialog cleanup.
  31.  *
  32.  * Version 0.10
  33.  *
  34.  * Made the user interface rather less unintuitive
  35.  * Added the "wrap" option
  36.  *
  37.  * Version 0.02
  38.  *
  39.  * Added edge handling options.
  40.  *
  41.  * Version 0.01
  42.  *
  43.  * First version.
  44.  */
  45.  
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48.  
  49. #include <gtk/gtk.h>
  50. #include <libgimp/gimp.h>
  51. #include <libgimp/gimpcompat.h>
  52.  
  53. #define PLUG_IN_NAME    "plug_in_kaleidoscope"
  54.  
  55. #include <libintl.h> /* i18n - gettext */
  56. #define _(String) gettext (String)
  57. #ifdef gettext_noop
  58. # define N_(String) gettext_noop (String)
  59. #else
  60. # define N_(String) (String)
  61. #endif
  62.  
  63. /***** Magic numbers *****/
  64.  
  65. #define PREVIEW_SIZE 128
  66. #define SCALE_WIDTH  200
  67. #define ENTRY_WIDTH  60
  68.  
  69. #define CHECK_SIZE  8
  70. #define CHECK_DARK  ((int) (1.0 / 3.0 * 255))
  71. #define CHECK_LIGHT ((int) (2.0 / 3.0 * 255))
  72.  
  73. #define BORDER_FILL 0
  74. #define BORDER_WRAP 1
  75. #define BORDER_SMEAR 2
  76.  
  77.  
  78. /***** Types *****/
  79.  
  80. typedef struct {
  81.   gdouble angle1;
  82.   gdouble angle2;
  83.   gint    nsegs;
  84.   gdouble cen_x;
  85.   gdouble cen_y;
  86.   gdouble off_x;
  87.   gdouble off_y;
  88.   gint    border;
  89. } kaleidoscope_vals_t;
  90.  
  91. typedef struct {
  92.   GtkWidget *preview;
  93.   guchar    *check_row_0;
  94.   guchar    *check_row_1;
  95.   guchar    *image;
  96.   guchar    *dimage;
  97.  
  98.   gint run;
  99. } kaleidoscope_interface_t;
  100.  
  101. typedef struct {
  102.   gint       col, row;
  103.   gint       img_width, img_height, img_bpp, img_has_alpha;
  104.   gint       tile_width, tile_height;
  105.   guchar     bg_color[4];
  106.   GimpDrawable *drawable;
  107.   GimpTile     *tile;
  108.   gint       wrap;
  109. } pixel_fetcher_t;
  110.  
  111.  
  112. /***** Prototypes *****/
  113.  
  114. static void query(void);
  115. static void run(const gchar    *name,
  116.         gint      nparams,
  117.         const GimpParam  *param,
  118.         gint     *nreturn_vals,
  119.         GimpParam **return_vals);
  120.  
  121. static void   kaleidoscope(void);
  122. static int    calc_undistorted_coords(double wx, double wy,
  123.                       double angle1, double angle2, int nsegs,
  124.                       double cen_x, double cen_y,
  125.                       double off_x, double off_y,
  126.                       double *x, double *y);
  127. static guchar bilinear(double x, double y, guchar *values);
  128.  
  129. static pixel_fetcher_t *pixel_fetcher_new(GimpDrawable *drawable, int wrap);
  130. static void             pixel_fetcher_set_bg_color(pixel_fetcher_t *pf, guchar r, guchar g, guchar b, guchar a);
  131. static void             pixel_fetcher_get_pixel(pixel_fetcher_t *pf, int x, int y, guchar *pixel);
  132. static void             pixel_fetcher_destroy(pixel_fetcher_t *pf);
  133.  
  134. static void build_preview_source_image(void);
  135.  
  136. static gint kaleidoscope_dialog(void);
  137. static void dialog_update_preview(void);
  138. static void dialog_create_value(char *title, GtkTable *table, int row, gdouble *value,
  139.                 double left, double right, double step);
  140. static void dialog_scale_update(GtkAdjustment *adjustment, gdouble *value);
  141. static void dialog_entry_update(GtkWidget *widget, gdouble *value);
  142. static void dialog_create_int_value(char *title, GtkTable *table, int row, gint *value,
  143.                     gint left, gint right, gint step);
  144. static void dialog_int_scale_update(GtkAdjustment *adjustment, gint *value);
  145. static void dialog_int_entry_update(GtkWidget *widget, gint *value);
  146.  
  147. static void dialog_create_toggle(char *title, GtkTable *table, int row, gint *value);
  148. static void dialog_toggle_update(GtkWidget *widget, gint *value);
  149.  
  150. static void dialog_close_callback(GtkWidget *widget, gpointer data);
  151. static void dialog_ok_callback(GtkWidget *widget, gpointer data);
  152. static void dialog_cancel_callback(GtkWidget *widget, gpointer data);
  153.  
  154.  
  155. /***** Variables *****/
  156.  
  157. GimpPlugInInfo PLUG_IN_INFO = {
  158.   NULL,   /* init_proc */
  159.   NULL,   /* quit_proc */
  160.   query,  /* query_proc */
  161.   run     /* run_proc */
  162. };
  163.  
  164. static kaleidoscope_vals_t wpvals = {
  165.   0.0,  /* angle1 */
  166.   0.0,  /* angle2 */
  167.   6,    /* nsegs */
  168.   0.0,  /* x center */
  169.   0.0,  /* y center */
  170.   0.0,  /* x offset */
  171.   0.0,   /* y offset */
  172.   0                 /* border handling */
  173. }; /* wpvals */
  174.  
  175. static kaleidoscope_interface_t wpint = {
  176.   NULL,  /* preview */
  177.   NULL,  /* check_row_0 */
  178.   NULL,  /* check_row_1 */
  179.   NULL,  /* image */
  180.   NULL,  /* dimage */
  181.   FALSE  /* run */
  182. }; /* wpint */
  183.  
  184. static GimpDrawable *drawable;
  185.  
  186. static gint img_width, img_height, img_bpp, img_has_alpha;
  187. static gint sel_x1, sel_y1, sel_x2, sel_y2;
  188. static gint sel_width, sel_height;
  189. static gint preview_width, preview_height;
  190.  
  191. static double scale_x, scale_y;
  192. static double radius;
  193.  
  194.  
  195. /***** Functions *****/
  196.  
  197. /*****/
  198.  
  199.  
  200. /*****/
  201.  
  202. static void
  203. query(void)
  204. {
  205.   static GimpParamDef args[] = {
  206.     { GIMP_PDB_INT32,    "run_mode",  "Interactive, non-interactive" },
  207.     { GIMP_PDB_IMAGE,    "image",     "Input image" },
  208.     { GIMP_PDB_DRAWABLE, "drawable",  N_("Input drawable") },
  209.     { GIMP_PDB_FLOAT,    "angle1",    N_("Angle of leading edge of viewing slice") },
  210.     { GIMP_PDB_FLOAT,    "angle2",    N_("Rollback angle") },
  211.     { GIMP_PDB_INT32,    "nsegs",     N_("Number of segments") },
  212.     { GIMP_PDB_INT32,    "border",    N_("Fill (0) or wrap (1) on edge") }
  213.    
  214.   }; /* args */
  215.  
  216.   static GimpParamDef *return_vals  = NULL;
  217.   static int        nargs        = sizeof(args) / sizeof(args[0]);
  218.   static int        nreturn_vals = 0;
  219.  
  220.   gimp_install_procedure(PLUG_IN_NAME,
  221.              _("Simulate looking at an image thru a kaleidoscope"),
  222.              _("Simulate looking at an image thru a kaleidoscope"),
  223.              "Kelly Martin",
  224.              "Kelly Martin",
  225.              "1999-2002",
  226.              "<Image>/Filters/Distorts/Kaleidoscope",
  227.              "RGB*, GRAY*",
  228.              GIMP_PLUGIN,
  229.              nargs,
  230.              nreturn_vals,
  231.              args,
  232.              return_vals);
  233. } /* query */
  234.  
  235.  
  236. /*****/
  237.  
  238. static void
  239. run (const gchar    *name,
  240.      gint      nparams,
  241.      const GimpParam  *param,
  242.      gint     *nreturn_vals,
  243.      GimpParam **return_vals)
  244. {
  245.   static GimpParam values[1];
  246.  
  247.   GimpRunMode run_mode;
  248.   GimpPDBStatusType status;
  249.   double       xhsiz, yhsiz;
  250.   int          pwidth, pheight;
  251.  
  252.   status   = GIMP_PDB_SUCCESS;
  253.   run_mode = param[0].data.d_int32;
  254.  
  255.   values[0].type          = GIMP_PDB_STATUS;
  256.   values[0].data.d_status = status;
  257.  
  258.   *nreturn_vals = 1;
  259.   *return_vals  = values;
  260.  
  261.   /* Get the active drawable info */
  262.  
  263.   drawable = gimp_drawable_get(param[2].data.d_drawable);
  264.  
  265.   img_width     = gimp_drawable_width(drawable->drawable_id);
  266.   img_height    = gimp_drawable_height(drawable->drawable_id);
  267.   img_bpp       = gimp_drawable_bpp(drawable->drawable_id);
  268.   img_has_alpha = gimp_drawable_has_alpha(drawable->drawable_id);
  269.  
  270.   gimp_drawable_mask_bounds(drawable->drawable_id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);
  271.  
  272.   /* Calculate scaling parameters */
  273.  
  274.   sel_width  = sel_x2 - sel_x1;
  275.   sel_height = sel_y2 - sel_y1;
  276.  
  277.   xhsiz = (double) (sel_width - 1) / 2.0;
  278.   yhsiz = (double) (sel_height - 1) / 2.0;
  279.  
  280.   if (xhsiz < yhsiz) {
  281.     scale_x = yhsiz / xhsiz;
  282.     scale_y = 1.0;
  283.   } else if (xhsiz > yhsiz) {
  284.     scale_x = 1.0;
  285.     scale_y = xhsiz / yhsiz;
  286.   } else {
  287.     scale_x = 1.0;
  288.     scale_y = 1.0;
  289.   } /* else */
  290.  
  291.   radius = MAX(xhsiz, yhsiz);
  292.  
  293.   wpvals.cen_x = sel_width  / 2 + sel_x1;
  294.   wpvals.cen_y = sel_height / 2 + sel_y1;
  295.  
  296.   /* Calculate preview size */
  297.  
  298.   if (sel_width > sel_height) {
  299.     pwidth  = MIN(sel_width, PREVIEW_SIZE);
  300.     pheight = sel_height * pwidth / sel_width;
  301.   } else {
  302.     pheight = MIN(sel_height, PREVIEW_SIZE);
  303.     pwidth  = sel_width * pheight / sel_height;
  304.   } /* else */
  305.  
  306.   preview_width  = MAX(pwidth, 2); /* Min size is 2 */
  307.   preview_height = MAX(pheight, 2);
  308.  
  309.   /* See how we will run */
  310.  
  311.   /* if debugging, pause to allow attachment of gdb */
  312.   /*getch();*/
  313.   switch (run_mode) {
  314.   case GIMP_RUN_INTERACTIVE:
  315.     /* Possibly retrieve data */
  316.  
  317.     gimp_get_data(name, &wpvals);
  318.  
  319.     /* Get information from the dialog */
  320.  
  321.     if (!kaleidoscope_dialog())
  322.       return;
  323.  
  324.     break;
  325.  
  326.   case GIMP_RUN_NONINTERACTIVE:
  327.     /* Make sure all the arguments are present */
  328.      
  329.     if (nparams != 11)
  330.       status = GIMP_PDB_CALLING_ERROR;
  331.      
  332.     if (status == GIMP_PDB_SUCCESS) {
  333.       wpvals.angle1 = param[3].data.d_float;
  334.       wpvals.angle2 = param[4].data.d_float;
  335.       wpvals.nsegs  = param[5].data.d_int32;
  336.       wpvals.cen_x  = param[6].data.d_float;
  337.       wpvals.cen_y  = param[7].data.d_float;
  338.       wpvals.off_x  = param[8].data.d_float;
  339.       wpvals.off_y  = param[9].data.d_float;
  340.       wpvals.border = param[10].data.d_int32;
  341.     } /* if */
  342.      
  343.     break;
  344.      
  345.   case GIMP_RUN_WITH_LAST_VALS:
  346.     /* Possibly retrieve data */
  347.  
  348.     gimp_get_data(PLUG_IN_NAME, &wpvals);
  349.     break;
  350.  
  351.   default:
  352.     break;
  353.   } /* switch */
  354.  
  355.   /* Distort the image */
  356.  
  357.   if ((status == GIMP_PDB_SUCCESS) &&
  358.       (gimp_drawable_is_rgb(drawable->drawable_id) ||
  359.        gimp_drawable_is_gray(drawable->drawable_id))) {
  360.     /* Set the tile cache size */
  361.  
  362.     gimp_tile_cache_ntiles(2 * (drawable->width + gimp_tile_width() - 1) / gimp_tile_width());
  363.  
  364.     /* Run! */
  365.  
  366.     kaleidoscope();
  367.  
  368.     /* If run mode is interactive, flush displays */
  369.  
  370.     if (run_mode != GIMP_RUN_NONINTERACTIVE)
  371.       gimp_displays_flush();
  372.  
  373.     /* Store data */
  374.  
  375.     if (run_mode == GIMP_RUN_INTERACTIVE)
  376.       gimp_set_data(PLUG_IN_NAME, &wpvals, sizeof(kaleidoscope_vals_t));
  377.   } else if (status == GIMP_PDB_SUCCESS)
  378.     status = GIMP_PDB_EXECUTION_ERROR;
  379.  
  380.   values[0].data.d_status = status;
  381.  
  382.   gimp_drawable_detach(drawable);
  383. } /* run */
  384.  
  385.  
  386. /*****/
  387.  
  388. static void
  389. kaleidoscope(void)
  390. {
  391.   GimpPixelRgn        dest_rgn;
  392.   gint             progress, max_progress;
  393.   guchar          *top_row, *bot_row;
  394.   guchar          *top_p, *bot_p;
  395.   gint             row, col;
  396.   guchar           pixel[4][4];
  397.   guchar           values[4];
  398.   double           cx, cy;
  399.   double       angle1, angle2;
  400.   int              ix, iy;
  401.   int              i;
  402.   GimpRGB          bg_temp;
  403.   guchar           bg_color[4];
  404.   pixel_fetcher_t *pft;
  405.  
  406.   /* Initialize rows */
  407.  
  408.   top_row = g_malloc(img_bpp * sel_width);
  409.   bot_row = g_malloc(img_bpp * sel_width);
  410.  
  411.   /* Initialize pixel region */
  412.  
  413.   gimp_pixel_rgn_init(&dest_rgn, drawable, sel_x1, sel_y1, sel_width, sel_height, TRUE, TRUE);
  414.  
  415.   pft = pixel_fetcher_new(drawable, wpvals.border == BORDER_WRAP);
  416.  
  417.   gimp_palette_get_background(&bg_temp);
  418.   bg_color[0] = (guchar)(bg_temp.r * 255);
  419.   bg_color[1] = (guchar)(bg_temp.g * 255);
  420.   bg_color[2] = (guchar)(bg_temp.b * 255);
  421.   bg_color[3] = (guchar)(bg_temp.a * 255);
  422.  
  423.  
  424.   pixel_fetcher_set_bg_color(pft,
  425.                  bg_color[0],
  426.                  bg_color[1],
  427.                  bg_color[2],
  428.                  (img_has_alpha ? 0 : 255));
  429.  
  430.   progress     = 0;
  431.   max_progress = sel_width * sel_height;
  432.  
  433.   gimp_progress_init(_("Kaleidoscoping..."));
  434.  
  435.   angle1   = wpvals.angle1 * G_PI / 180;
  436.   angle2   = wpvals.angle2 * G_PI / 180;
  437.  
  438.   for (row = sel_y1; row < (sel_y1 + sel_y2); row++) {
  439.     top_p = top_row;
  440.     bot_p = bot_row + img_bpp * (sel_width - 1);
  441.  
  442.     for (col = sel_x1; col < sel_x2; col++) {
  443.       if (calc_undistorted_coords(col, row, angle1, angle2,
  444.                   wpvals.nsegs,
  445.                   wpvals.cen_x, wpvals.cen_y,
  446.                   wpvals.off_x, wpvals.off_y,
  447.                   &cx, &cy)) {
  448.     /* We are inside the distortion area */
  449.  
  450.     /* Top */
  451.  
  452.     if (cx >= 0.0)
  453.       ix = (int) cx;
  454.     else
  455.       ix = -((int) -cx + 1);
  456.  
  457.     if (cy >= 0.0)
  458.       iy = (int) cy;
  459.     else
  460.       iy = -((int) -cy + 1);
  461.  
  462.     pixel_fetcher_get_pixel(pft, ix,     iy,     pixel[0]);
  463.     pixel_fetcher_get_pixel(pft, ix + 1, iy,     pixel[1]);
  464.     pixel_fetcher_get_pixel(pft, ix,     iy + 1, pixel[2]);
  465.     pixel_fetcher_get_pixel(pft, ix + 1, iy + 1, pixel[3]);
  466.  
  467.     for (i = 0; i < img_bpp; i++) {
  468.       values[0] = pixel[0][i];
  469.       values[1] = pixel[1][i];
  470.       values[2] = pixel[2][i];
  471.       values[3] = pixel[3][i];
  472.  
  473.       *top_p++ = bilinear(cx, cy, values);
  474.     } /* for */
  475.  
  476.       } else {
  477.     /* We are outside the distortion area; just copy the source pixels */
  478.  
  479.     /* Top */
  480.  
  481.     pixel_fetcher_get_pixel(pft, col, row, pixel[0]);
  482.  
  483.     for (i = 0; i < img_bpp; i++)
  484.       *top_p++ = pixel[0][i];
  485.  
  486.       } /* else */
  487.     } /* for */
  488.  
  489.     /* Paint rows to image */
  490.  
  491.     gimp_pixel_rgn_set_row(&dest_rgn, top_row, sel_x1, row, sel_width);
  492.  
  493.     /* Update progress */
  494.  
  495.     progress += sel_width;
  496.     gimp_progress_update((double) progress / max_progress);
  497.   } /* for */
  498.  
  499.   pixel_fetcher_destroy(pft);
  500.  
  501.   g_free(top_row);
  502.  
  503.   gimp_drawable_flush(drawable);
  504.   gimp_drawable_merge_shadow(drawable->drawable_id, TRUE);
  505.   gimp_drawable_update(drawable->drawable_id, sel_x1, sel_y1, sel_width, sel_height);
  506. } /* kaleidoscope */
  507.  
  508.  
  509. /*****/
  510.  
  511. static int
  512. calc_undistorted_coords(double wx, double wy,
  513.             double angle1, double angle2, int nsegs,
  514.             double cen_x, double cen_y,
  515.             double off_x, double off_y,
  516.             double *x, double *y)
  517. {
  518.   double dx, dy;
  519.   double r, ang;
  520.  
  521.   double awidth = G_PI/nsegs;
  522.   double mult;
  523.  
  524.   dx = wx - cen_x;
  525.   dy = wy - cen_y;
  526.  
  527.   r = sqrt(dx*dx+dy*dy);
  528.   if (r == 0.0) {
  529.     *x = wx + off_x;
  530.     *y = wy + off_y;
  531.     return TRUE;
  532.   }
  533.  
  534.   ang = atan2(dy,dx) - angle1 - angle2;
  535.   while (ang<0.0) ang = ang + 2*G_PI;
  536.  
  537.   mult = ceil(ang/awidth) - 1;
  538.   ang = ang - mult*awidth;
  539.   if (((int) mult) % 2 == 1) ang = awidth - ang;
  540.   ang = ang + angle1;
  541.  
  542.   *x = r*cos(ang) + off_x;
  543.   *y = r*sin(ang) + off_y;
  544.  
  545.   return TRUE;
  546. } /* calc_undistorted_coords */
  547.  
  548.  
  549. /*****/
  550.  
  551. static guchar
  552. bilinear(double x, double y, guchar *values)
  553. {
  554.   double m0, m1;
  555.  
  556.   x = fmod(x, 1.0);
  557.   y = fmod(y, 1.0);
  558.  
  559.   if (x < 0.0)
  560.     x += 1.0;
  561.  
  562.   if (y < 0.0)
  563.     y += 1.0;
  564.  
  565.   m0 = (double) values[0] + x * ((double) values[1] - values[0]);
  566.   m1 = (double) values[2] + x * ((double) values[3] - values[2]);
  567.  
  568.   return (guchar) (m0 + y * (m1 - m0));
  569. } /* bilinear */
  570.  
  571.  
  572. /*****/
  573.  
  574. static pixel_fetcher_t *
  575. pixel_fetcher_new(GimpDrawable *drawable, gint wrap)
  576. {
  577.   pixel_fetcher_t *pf;
  578.  
  579.   pf = g_malloc(sizeof(pixel_fetcher_t));
  580.  
  581.   pf->col           = -1;
  582.   pf->row           = -1;
  583.   pf->img_width     = gimp_drawable_width(drawable->drawable_id);
  584.   pf->img_height    = gimp_drawable_height(drawable->drawable_id);
  585.   pf->img_bpp       = gimp_drawable_bpp(drawable->drawable_id);
  586.   pf->img_has_alpha = gimp_drawable_has_alpha(drawable->drawable_id);
  587.   pf->tile_width    = gimp_tile_width();
  588.   pf->tile_height   = gimp_tile_height();
  589.   pf->bg_color[0]   = 0;
  590.   pf->bg_color[1]   = 0;
  591.   pf->bg_color[2]   = 0;
  592.   pf->bg_color[3]   = 0;
  593.   pf->wrap      = wrap;
  594.  
  595.   pf->drawable    = drawable;
  596.   pf->tile        = NULL;
  597.  
  598.   return pf;
  599. } /* pixel_fetcher_new */
  600.  
  601.  
  602. /*****/
  603.  
  604. static void
  605. pixel_fetcher_set_bg_color(pixel_fetcher_t *pf, guchar r, guchar g, guchar b, guchar a)
  606. {
  607.   pf->bg_color[0] = r;
  608.   pf->bg_color[1] = g;
  609.   pf->bg_color[2] = b;
  610.  
  611.   if (pf->img_has_alpha)
  612.     pf->bg_color[pf->img_bpp - 1] = a;
  613. } /* pixel_fetcher_set_bg_color */
  614.  
  615.  
  616. /*****/
  617.  
  618. static void
  619. pixel_fetcher_get_pixel(pixel_fetcher_t *pf, int x, int y, guchar *pixel)
  620. {
  621.   gint    col, row;
  622.   gint    coloff, rowoff;
  623.   guchar *p;
  624.   int     i;
  625.  
  626.   if (!pf->wrap &&
  627.       ((x < sel_x1) || (x >= sel_x2) ||
  628.        (y < sel_y1) || (y >= sel_y2))) {
  629.     for (i = 0; i < pf->img_bpp; i++)
  630.       pixel[i] = pf->bg_color[i];
  631.  
  632.     return;
  633.   } /* if */
  634.  
  635.   while (x < sel_x1)  { x += sel_x2 - sel_x1; }
  636.   while (x >= sel_x2) { x -= sel_x2 - sel_x1; }
  637.   while (y < sel_y1)  { y += sel_y2 - sel_y1; }
  638.   while (y >= sel_y2) { y -= sel_y2 - sel_y1; }
  639.  
  640.   col    = x / pf->tile_width;
  641.   coloff = x % pf->tile_width;
  642.   row    = y / pf->tile_height;
  643.   rowoff = y % pf->tile_height;
  644.  
  645.   if ((col != pf->col) ||
  646.       (row != pf->row) ||
  647.       (pf->tile == NULL)) {
  648.     if (pf->tile != NULL)
  649.       gimp_tile_unref(pf->tile, FALSE);
  650.  
  651.     pf->tile = gimp_drawable_get_tile(pf->drawable, FALSE, row, col);
  652.     gimp_tile_ref(pf->tile);
  653.  
  654.     pf->col = col;
  655.     pf->row = row;
  656.   } /* if */
  657.  
  658.   p = pf->tile->data + pf->img_bpp * (pf->tile->ewidth * rowoff + coloff);
  659.  
  660.   for (i = pf->img_bpp; i; i--)
  661.     *pixel++ = *p++;
  662. } /* pixel_fetcher_get_pixel */
  663.  
  664.  
  665. /*****/
  666.  
  667. static void
  668. pixel_fetcher_destroy(pixel_fetcher_t *pf)
  669. {
  670.   if (pf->tile != NULL)
  671.     gimp_tile_unref(pf->tile, FALSE);
  672.  
  673.   g_free(pf);
  674. } /* pixel_fetcher_destroy */
  675.  
  676.  
  677. /*****/
  678.  
  679. static void
  680. build_preview_source_image(void)
  681. {
  682.   double           left, right, bottom, top;
  683.   double           px, py;
  684.   double           dx, dy;
  685.   int              x, y;
  686.   guchar          *p;
  687.   guchar           pixel[4];
  688.   pixel_fetcher_t *pf;
  689.  
  690.   wpint.check_row_0 = g_malloc(preview_width * sizeof(guchar));
  691.   wpint.check_row_1 = g_malloc(preview_width * sizeof(guchar));
  692.   wpint.image       = g_malloc(preview_width * preview_height * 4 * sizeof(guchar));
  693.   wpint.dimage      = g_malloc(preview_width * preview_height * 3 * sizeof(guchar));
  694.  
  695.   left   = sel_x1;
  696.   right  = sel_x2 - 1;
  697.   bottom = sel_y2 - 1;
  698.   top    = sel_y1;
  699.  
  700.   dx = (right - left) / (preview_width - 1);
  701.   dy = (bottom - top) / (preview_height - 1);
  702.  
  703.   py = top;
  704.  
  705.   pf = pixel_fetcher_new(drawable, wpvals.border == BORDER_WRAP);
  706.  
  707.   p = wpint.image;
  708.  
  709.   for (y = 0; y < preview_height; y++) {
  710.     px = left;
  711.  
  712.     for (x = 0; x < preview_width; x++) {
  713.       /* Checks */
  714.  
  715.       if ((x / CHECK_SIZE) & 1) {
  716.     wpint.check_row_0[x] = CHECK_DARK;
  717.     wpint.check_row_1[x] = CHECK_LIGHT;
  718.       } else {
  719.     wpint.check_row_0[x] = CHECK_LIGHT;
  720.     wpint.check_row_1[x] = CHECK_DARK;
  721.       } /* else */
  722.  
  723.       /* Thumbnail image */
  724.  
  725.       pixel_fetcher_get_pixel(pf, (int) px, (int) py, pixel);
  726.  
  727.       if (img_bpp < 3) {
  728.     if (img_has_alpha)
  729.       pixel[3] = pixel[1];
  730.     else
  731.       pixel[3] = 255;
  732.  
  733.     pixel[1] = pixel[0];
  734.     pixel[2] = pixel[0];
  735.       } else
  736.     if (!img_has_alpha)
  737.       pixel[3] = 255;
  738.  
  739.       *p++ = pixel[0];
  740.       *p++ = pixel[1];
  741.       *p++ = pixel[2];
  742.       *p++ = pixel[3];
  743.  
  744.       px += dx;
  745.     } /* for */
  746.  
  747.     py += dy;
  748.   } /* for */
  749.  
  750.   pixel_fetcher_destroy(pf);
  751. } /* build_preview_source_image */
  752.  
  753.  
  754. /*****/
  755.  
  756. static gint
  757. kaleidoscope_dialog(void)
  758. {
  759.   GtkWidget  *dialog;
  760.   GtkWidget  *top_table;
  761.   GtkWidget  *frame;
  762.   GtkWidget  *table;
  763.   GtkWidget  *button;
  764.   gint        argc;
  765.   gchar     **argv;
  766.  
  767.   argc    = 1;
  768.   argv    = g_new(gchar *, 1);
  769.   argv[0] = g_strdup("kaleidoscope");
  770.  
  771.   gtk_init(&argc, &argv);
  772.   gtk_rc_parse (gimp_gtkrc ());
  773.  
  774.   gdk_set_use_xshm(gimp_use_xshm());
  775.  
  776.   gtk_preview_set_gamma(gimp_gamma());
  777.   gtk_preview_set_install_cmap(gimp_install_cmap());
  778.  
  779.   gtk_widget_set_default_visual(gtk_preview_get_visual());
  780.   gtk_widget_set_default_colormap(gtk_preview_get_cmap());
  781.  
  782.   build_preview_source_image();
  783.  
  784.   dialog = gtk_dialog_new();
  785.   gtk_window_set_title(GTK_WINDOW(dialog), _("Kaleidoscope"));
  786.   gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
  787.   gtk_container_border_width(GTK_CONTAINER(dialog), 0);
  788.   gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
  789.              (GtkSignalFunc) dialog_close_callback,
  790.              NULL);
  791.  
  792.   top_table = gtk_table_new(2, 3, FALSE);
  793.   gtk_container_border_width(GTK_CONTAINER(top_table), 6);
  794.   gtk_table_set_row_spacings(GTK_TABLE(top_table), 4);
  795.   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), top_table, FALSE, FALSE, 0);
  796.   gtk_widget_show(top_table);
  797.  
  798.   /* Preview */
  799.  
  800.   frame = gtk_frame_new(NULL);
  801.   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
  802.   gtk_table_attach(GTK_TABLE(top_table), frame, 1, 2, 0, 1, 0, 0, 0, 0);
  803.   gtk_widget_show(frame);
  804.  
  805.   wpint.preview = gtk_preview_new(GTK_PREVIEW_COLOR);
  806.   gtk_preview_size(GTK_PREVIEW(wpint.preview), preview_width, preview_height);
  807.   gtk_container_add(GTK_CONTAINER(frame), wpint.preview);
  808.   gtk_widget_show(wpint.preview);
  809.  
  810.   /* Controls */
  811.  
  812.   table = gtk_table_new(8, 3, FALSE);
  813.   gtk_container_border_width(GTK_CONTAINER(table), 0);
  814.   gtk_table_attach(GTK_TABLE(top_table), table, 0, 3, 1, 2, GTK_EXPAND | GTK_FILL, 0, 0, 0);
  815.   gtk_widget_show(table);
  816.  
  817.   dialog_create_value(_("Angle 1"), GTK_TABLE(table), 0, &wpvals.angle1, -360.0, 360.0, 1.0);
  818.    
  819.   dialog_create_value(_("Angle 2"), GTK_TABLE(table), 1, &wpvals.angle2, -360.0, 360.0, 1.0);
  820.    
  821.   dialog_create_int_value(_("Number of segments"), GTK_TABLE(table), 2, &wpvals.nsegs, 1, 16, 1);
  822.  
  823.   dialog_create_value(_("X center"), GTK_TABLE(table), 3, &wpvals.cen_x, sel_x1, sel_x2, 1.0);
  824.    
  825.   dialog_create_value(_("Y center"), GTK_TABLE(table), 4, &wpvals.cen_y, sel_y1, sel_y2, 1.0);
  826.  
  827.   dialog_create_value(_("X offset"), GTK_TABLE(table), 5, &wpvals.off_x, -img_width, img_width, 1.0);
  828.    
  829.   dialog_create_value(_("Y offset"), GTK_TABLE(table), 6, &wpvals.off_y, -img_height, img_height, 1.0);
  830.  
  831.   dialog_create_toggle(_("Wrap?"), GTK_TABLE(table), 7, &wpvals.border);
  832.    
  833.   /* Buttons */
  834.  
  835.   gtk_container_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 6);
  836.  
  837.   button = gtk_button_new_with_label(_("OK"));
  838.   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
  839.   gtk_signal_connect(GTK_OBJECT(button), "clicked",
  840.              (GtkSignalFunc) dialog_ok_callback,
  841.              dialog);
  842.   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, TRUE, TRUE, 0);
  843.   gtk_widget_grab_default(button);
  844.   gtk_widget_show(button);
  845.  
  846.   button = gtk_button_new_with_label(_("Cancel"));
  847.   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
  848.   gtk_signal_connect(GTK_OBJECT(button), "clicked",
  849.              (GtkSignalFunc) dialog_cancel_callback,
  850.              dialog);
  851.   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, TRUE, TRUE, 0);
  852.   gtk_widget_show(button);
  853.  
  854.   /* Done */
  855.  
  856.   gtk_widget_show(dialog);
  857.   dialog_update_preview();
  858.  
  859.   gtk_main();
  860.   gdk_flush();
  861.  
  862.   g_free(wpint.check_row_0);
  863.   g_free(wpint.check_row_1);
  864.   g_free(wpint.image);
  865.   g_free(wpint.dimage);
  866.  
  867.   return wpint.run;
  868. } /* kaleidoscope_dialog */
  869.  
  870.  
  871. /*****/
  872.  
  873. static void
  874. dialog_update_preview(void)
  875. {
  876.   double  left, right, bottom, top;
  877.   double  dx, dy;
  878.   double  px, py;
  879.   double  cx, cy;
  880.   int     ix, iy;
  881.   int     x, y;
  882.   double  scale_x, scale_y;
  883.   guchar *p_ul, *i, *p;
  884.   guchar *check_ul;
  885.   int     check;
  886.   guchar  outside[4];
  887.   GimpRGB bgcolor;
  888.   double  angle1, angle2;
  889.  
  890.   gimp_palette_get_background(&bgcolor);
  891.   outside[0] = (guchar)(bgcolor.r * 255);
  892.   outside[1] = (guchar)(bgcolor.g * 255);
  893.   outside[2] = (guchar)(bgcolor.b * 255);
  894.   outside[3] = (img_has_alpha ? 0 : 255);
  895.  
  896.   if (img_bpp < 3) {
  897.     outside[1] = outside[0];
  898.     outside[2] = outside[0];
  899.   } /* if */
  900.  
  901.   left   = sel_x1;
  902.   right  = sel_x2 - 1;
  903.   bottom = sel_y2 - 1;
  904.   top    = sel_y1;
  905.  
  906.   dx = (right - left) / (preview_width - 1);
  907.   dy = (bottom - top) / (preview_height - 1);
  908.  
  909.   scale_x = (double) preview_width / (right - left + 1);
  910.   scale_y = (double) preview_height / (bottom - top + 1);
  911.  
  912.   angle1   = wpvals.angle1 * G_PI / 180;
  913.   angle2   = wpvals.angle2 * G_PI / 180;
  914.  
  915.   py = top;
  916.  
  917.   p_ul = wpint.dimage;
  918.  
  919.   for (y = 0; y < preview_height; y++) {
  920.     px = left;
  921.  
  922.     if ((y / CHECK_SIZE) & 1)
  923.       check_ul = wpint.check_row_0;
  924.     else
  925.       check_ul = wpint.check_row_1;
  926.  
  927.     for (x = 0; x < preview_width; x++) {
  928.       calc_undistorted_coords(px, py, angle1, angle2, wpvals.nsegs,
  929.                   wpvals.cen_x, wpvals.cen_y,
  930.                   wpvals.off_x, wpvals.off_y,
  931.                   &cx, &cy);
  932.  
  933.       cx = (cx - left) * scale_x;
  934.       cy = (cy - top) * scale_y;
  935.  
  936.       ix = (int) (cx + 0.5);
  937.       iy = (int) (cy + 0.5);
  938.  
  939.       check = check_ul[x];
  940.      
  941.       if (wpvals.border == BORDER_WRAP) {
  942.     while (ix < 0) ix += preview_width;
  943.     while (ix >= preview_width) ix -= preview_width;
  944.     while (iy < 0) iy += preview_height;
  945.     while (iy >= preview_height) iy -= preview_height;
  946.       }
  947.  
  948.       if ((ix >= 0) && (ix < preview_width) &&
  949.       (iy >= 0) && (iy < preview_height))
  950.     i = wpint.image + 4 * (preview_width * iy + ix);
  951.       else
  952.     i = outside;
  953.  
  954.       p_ul[0] = check + ((i[0] - check) * i[3]) / 255;
  955.       p_ul[1] = check + ((i[1] - check) * i[3]) / 255;
  956.       p_ul[2] = check + ((i[2] - check) * i[3]) / 255;
  957.  
  958.       p_ul += 3;
  959.  
  960.       px += dx;
  961.     } /* for */
  962.  
  963.     py += dy;
  964.   } /* for */
  965.  
  966.   p = wpint.dimage;
  967.  
  968.   for (y = 0; y < preview_height; y++) {
  969.     gtk_preview_draw_row(GTK_PREVIEW(wpint.preview), p, 0, y, preview_width);
  970.  
  971.     p += preview_width * 3;
  972.   } /* for */
  973.  
  974.   gtk_widget_draw(wpint.preview, NULL);
  975.   gdk_flush();
  976. } /* dialog_update_preview */
  977.  
  978.  
  979. /*****/
  980.  
  981. static void
  982. dialog_create_value(char *title, GtkTable *table, int row, gdouble *value,
  983.             double left, double right, double step)
  984. {
  985.   GtkWidget *label;
  986.   GtkWidget *scale;
  987.   GtkWidget *entry;
  988.   GtkObject *scale_data;
  989.   char       buf[256];
  990.  
  991.   label = gtk_label_new(title);
  992.   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
  993.   gtk_table_attach(table, label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0);
  994.   gtk_widget_show(label);
  995.  
  996.   scale_data = gtk_adjustment_new(*value, left, right,
  997.                   step,
  998.                   step,
  999.                   0.0);
  1000.  
  1001.   gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed",
  1002.              (GtkSignalFunc) dialog_scale_update,
  1003.              value);
  1004.  
  1005.   scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data));
  1006.   gtk_widget_set_usize(scale, SCALE_WIDTH, 0);
  1007.   gtk_table_attach(table, scale, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  1008.   gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
  1009.   gtk_scale_set_digits(GTK_SCALE(scale), 3);
  1010.   gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS);
  1011.   gtk_widget_show(scale);
  1012.  
  1013.   entry = gtk_entry_new();
  1014.   gtk_object_set_user_data(GTK_OBJECT(entry), scale_data);
  1015.   gtk_object_set_user_data(scale_data, entry);
  1016.   gtk_widget_set_usize(entry, ENTRY_WIDTH, 0);
  1017.   sprintf(buf, "%0.3f", *value);
  1018.   gtk_entry_set_text(GTK_ENTRY(entry), buf);
  1019.   gtk_signal_connect(GTK_OBJECT(entry), "changed",
  1020.              (GtkSignalFunc) dialog_entry_update,
  1021.              value);
  1022.   gtk_table_attach(GTK_TABLE(table), entry, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, 4, 0);
  1023.   gtk_widget_show(entry);
  1024. } /* dialog_create_value */
  1025.  
  1026.  
  1027. /*****/
  1028.  
  1029. static void
  1030. dialog_scale_update(GtkAdjustment *adjustment, gdouble *value)
  1031. {
  1032.   GtkWidget *entry;
  1033.   char       buf[256];
  1034.  
  1035.   if (*value != adjustment->value) {
  1036.     *value = adjustment->value;
  1037.  
  1038.     entry = gtk_object_get_user_data(GTK_OBJECT(adjustment));
  1039.     sprintf(buf, "%0.3f", *value);
  1040.  
  1041.     gtk_signal_handler_block_by_data(GTK_OBJECT(entry), value);
  1042.     gtk_entry_set_text(GTK_ENTRY(entry), buf);
  1043.     gtk_signal_handler_unblock_by_data(GTK_OBJECT(entry), value);
  1044.  
  1045.     dialog_update_preview();
  1046.   } /* if */
  1047. } /* dialog_scale_update */
  1048.  
  1049.  
  1050. /*****/
  1051.  
  1052. static void
  1053. dialog_entry_update(GtkWidget *widget, gdouble *value)
  1054. {
  1055.   GtkAdjustment *adjustment;
  1056.   gdouble        new_value;
  1057.  
  1058.   new_value = atof(gtk_entry_get_text(GTK_ENTRY(widget)));
  1059.  
  1060.   if (*value != new_value) {
  1061.     adjustment = gtk_object_get_user_data(GTK_OBJECT(widget));
  1062.  
  1063.     if ((new_value >= adjustment->lower) &&
  1064.     (new_value <= adjustment->upper)) {
  1065.       *value            = new_value;
  1066.       adjustment->value = new_value;
  1067.  
  1068.       gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed");
  1069.  
  1070.       dialog_update_preview();
  1071.     } /* if */
  1072.   } /* if */
  1073. } /* dialog_entry_update */
  1074.  
  1075. static void
  1076. dialog_create_int_value(char *title, GtkTable *table, int row, gint *value,
  1077.             int left, int right, int step)
  1078. {
  1079.   GtkWidget *label;
  1080.   GtkWidget *scale;
  1081.   GtkWidget *entry;
  1082.   GtkObject *scale_data;
  1083.   char       buf[256];
  1084.  
  1085.   label = gtk_label_new(title);
  1086.   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
  1087.   gtk_table_attach(table, label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0);
  1088.   gtk_widget_show(label);
  1089.  
  1090.   scale_data = gtk_adjustment_new(1.0 * (*value),
  1091.                   1.0*left,
  1092.                   1.0*right,
  1093.                   1.0*step,
  1094.                   1.0*step,
  1095.                   0.0);
  1096.  
  1097.   gtk_signal_connect(GTK_OBJECT(scale_data), "value_changed",
  1098.              (GtkSignalFunc) dialog_int_scale_update,
  1099.              value);
  1100.  
  1101.   scale = gtk_hscale_new(GTK_ADJUSTMENT(scale_data));
  1102.   gtk_widget_set_usize(scale, SCALE_WIDTH, 0);
  1103.   gtk_table_attach(table, scale, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  1104.   gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
  1105.   gtk_scale_set_digits(GTK_SCALE(scale), 3);
  1106.   gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS);
  1107.   gtk_widget_show(scale);
  1108.  
  1109.   entry = gtk_entry_new();
  1110.   gtk_object_set_user_data(GTK_OBJECT(entry), scale_data);
  1111.   gtk_object_set_user_data(scale_data, entry);
  1112.   gtk_widget_set_usize(entry, ENTRY_WIDTH, 0);
  1113.   sprintf(buf, "%d", *value);
  1114.   gtk_entry_set_text(GTK_ENTRY(entry), buf);
  1115.   gtk_signal_connect(GTK_OBJECT(entry), "changed",
  1116.              (GtkSignalFunc) dialog_int_entry_update,
  1117.              value);
  1118.   gtk_table_attach(GTK_TABLE(table), entry, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, 4, 0);
  1119.   gtk_widget_show(entry);
  1120. } /* dialog_create_int_value */
  1121.  
  1122.  
  1123. /*****/
  1124.  
  1125. static void
  1126. dialog_int_scale_update(GtkAdjustment *adjustment, gint *value)
  1127. {
  1128.   GtkWidget *entry;
  1129.   char       buf[256];
  1130.  
  1131.   if (*value != (int) adjustment->value) {
  1132.     *value = (int) adjustment->value;
  1133.  
  1134.     entry = gtk_object_get_user_data(GTK_OBJECT(adjustment));
  1135.     sprintf(buf, "%d", *value);
  1136.  
  1137.     gtk_signal_handler_block_by_data(GTK_OBJECT(entry), value);
  1138.     gtk_entry_set_text(GTK_ENTRY(entry), buf);
  1139.     gtk_signal_handler_unblock_by_data(GTK_OBJECT(entry), value);
  1140.  
  1141.     dialog_update_preview();
  1142.   } /* if */
  1143. } /* dialog_scale_update */
  1144.  
  1145.  
  1146. /*****/
  1147.  
  1148. static void
  1149. dialog_int_entry_update(GtkWidget *widget, gint *value)
  1150. {
  1151.   GtkAdjustment *adjustment;
  1152.   gint           new_int_value;
  1153.  
  1154.   new_int_value = atoi(gtk_entry_get_text(GTK_ENTRY(widget)));
  1155.  
  1156.   if (*value != new_int_value) {
  1157.     adjustment = gtk_object_get_user_data(GTK_OBJECT(widget));
  1158.  
  1159.     if ((new_int_value >= adjustment->lower) &&
  1160.     (new_int_value <= adjustment->upper)) {
  1161.       *value            = new_int_value;
  1162.       adjustment->value = 1.0*new_int_value;
  1163.  
  1164.       gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed");
  1165.  
  1166.       dialog_update_preview();
  1167.     } /* if */
  1168.   } /* if */
  1169. } /* dialog_entry_update */
  1170.  
  1171.  
  1172. /*****/
  1173.  
  1174. static void
  1175. dialog_create_toggle(char *title, GtkTable *table, int row, gint *value)
  1176. {
  1177.   GtkWidget *label;
  1178.   GtkWidget *toggle;
  1179.  
  1180.   label = gtk_label_new(title);
  1181.   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
  1182.   gtk_table_attach(table, label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 4, 0);
  1183.   gtk_widget_show(label);
  1184.  
  1185.   toggle = gtk_toggle_button_new();
  1186.  
  1187.   gtk_signal_connect(GTK_OBJECT(toggle), "clicked",
  1188.              (GtkSignalFunc) dialog_toggle_update,
  1189.              value);
  1190.  
  1191.   gtk_table_attach(table, toggle, 1, 2, row, row + 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  1192.   gtk_widget_show(toggle);
  1193.  
  1194. } /* dialog_create_value */
  1195.  
  1196.  
  1197. /*****/
  1198.  
  1199. static void
  1200. dialog_toggle_update(GtkWidget *widget, gint *value)
  1201. {
  1202.   gint           new_int_value;
  1203.  
  1204.   new_int_value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
  1205.  
  1206.   if (*value != new_int_value) {
  1207.     *value = new_int_value;
  1208.     dialog_update_preview();
  1209.   }
  1210.  
  1211. } /* dialog_entry_update */
  1212.  
  1213.  
  1214.  
  1215. /*****/
  1216.  
  1217. static void
  1218. dialog_close_callback(GtkWidget *widget, gpointer data)
  1219. {
  1220.   gtk_main_quit();
  1221. } /* dialog_close_callback */
  1222.  
  1223.  
  1224. /*****/
  1225.  
  1226. static void
  1227. dialog_ok_callback(GtkWidget *widget, gpointer data)
  1228. {
  1229.   wpint.run = TRUE;
  1230.   gtk_widget_destroy(GTK_WIDGET(data));
  1231. } /* dialog_ok_callback */
  1232.  
  1233.  
  1234. /*****/
  1235.  
  1236. static void
  1237. dialog_cancel_callback(GtkWidget *widget, gpointer data)
  1238. {
  1239.   gtk_widget_destroy(GTK_WIDGET(data));
  1240. } /* dialog_cancel_callback */
  1241.  
  1242.  
  1243. MAIN()
RAW Paste Data