Lazza

Gimp kaleidoscope.c

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