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 / gflare / gflare.c < prev    next >
C/C++ Source or Header  |  1998-02-20  |  147KB  |  5,592 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * GFlare plug-in -- lense flare effect by using custom gradients
  5.  * Copyright (C) 1997 Eiichi Takamori <taka@ma1.sekyou.ne.jp>
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 2 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software
  19.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  *
  22.  * A fair proportion of this code was taken from GIMP & Script-fu
  23.  * copyrighted by Spencer Kimball and Peter Mattis, and from Gradient
  24.  * Editor copyrighted by Federico Mena Quintero. (See copyright notice
  25.  * below) Thanks for senior GIMP hackers!!
  26.  *
  27.  * The GIMP -- an image manipulation program
  28.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  29.  *
  30.  * Gradient editor module copyight (C) 1996-1997 Federico Mena Quintero
  31.  * federico@nuclecu.unam.mx
  32.  */
  33.  
  34. /*
  35.   version 0.25
  36.  */
  37.  
  38.  
  39. #ifdef RCSID
  40. static char rcsid[] = "$Id: gflare.c,v 1.1 1998/02/20 08:48:28 yosh Exp $";
  41. #endif
  42.  
  43. #include <stdio.h>
  44. #include <stdlib.h>
  45. #include <string.h>
  46. #include <math.h>
  47. #include <ctype.h>
  48. #include <time.h>
  49. #include <sys/stat.h>
  50. #include <unistd.h>
  51. #include <sys/types.h>
  52. #include <dirent.h>
  53. #include <gtk/gtk.h>
  54. #include <libgimp/gimp.h>
  55. #include "asupsample.h"
  56. #include "gtkmultioptionmenu.h"
  57.  
  58. /* #define DEBUG */
  59.  
  60. #ifdef DEBUG
  61. #define DEBUG_PRINT(X) printf X
  62. #else
  63. #define DEBUG_PRINT(X)
  64. #endif
  65.  
  66. #define BOUNDS(a,x,y)    ((a < x) ? x : ((a > y) ? y : a))
  67. #define LUMINOSITY(PIX) (PIX[0] * 0.30 + PIX[1] * 0.59 + PIX[2] * 0.11)
  68. #define OFFSETOF(t,f)    ((int) ((char*) &((t*) 0)->f))
  69.  
  70. #define GRADIENT_NAME_MAX 256
  71. #define GRADIENT_RESOLUTION 360
  72.  
  73. #define GFLARE_NAME_MAX 256
  74. #define GFLARE_FILE_HEADER   "GIMP GFlare 0.25\n"
  75. #define SFLARE_NUM    30
  76.  
  77. #define DLG_PREVIEW_WIDTH 256
  78. #define DLG_PREVIEW_HEIGHT 256
  79. #define DLG_PREVIEW_MASK   GDK_EXPOSURE_MASK | \
  80.                GDK_BUTTON_PRESS_MASK
  81. #define DLG_LISTBOX_WIDTH 80
  82. #define DLG_LISTBOX_HEIGHT 40
  83.  
  84. #define ED_PREVIEW_WIDTH 256
  85. #define ED_PREVIEW_HEIGHT 256
  86.  
  87. #define GM_PREVIEW_WIDTH    80
  88. #define GM_PREVIEW_HEIGHT    16
  89. #define GM_MENU_MAX   20
  90.  
  91. #define ENTSCALE_SCALE_WIDTH 100
  92. #define ENTSCALE_ENTRY_WIDTH 50
  93.  
  94. #define ENTRY_WIDTH 40
  95.  
  96. #define CHECK_SIZE 4
  97. #define LIGHTCHECK 192
  98. #define DARKCHECK  128
  99. #ifndef OPAQUE
  100. #define OPAQUE       255
  101. #endif
  102. #define GRAY50       128
  103.  
  104. /* drawable coord to preview coord */
  105. #define DX2PX(DX)  ((double) PREVIEW_WIDTH * ((DX) - dinfo->win.x0) / (dinfo->win.x1 - dinfo->win.x0))
  106. #define DY2PY(DY)  ((double) PREVIEW_HEIGHT * ((DY) - dinfo->win.y0) / (dinfo->win.y1 - dinfo->win.y0))
  107. #define GRADIENT_CACHE_SIZE    32
  108.  
  109. #define CALC_GLOW    0x01
  110. #define CALC_RAYS    0x02
  111. #define CALC_SFLARE    0x04
  112.  
  113. typedef struct _Preview Preview;
  114.  
  115. typedef gchar    GradientName[GRADIENT_NAME_MAX];
  116.  
  117. typedef enum
  118. {
  119.   GF_NORMAL = 0,
  120.   GF_ADDITION,
  121.   GF_OVERLAY,
  122.   GF_SCREEN,
  123.   GF_NUM_MODES
  124. } GFlareMode;
  125.  
  126. typedef enum
  127. {
  128.   GF_CIRCLE = 0,
  129.   GF_POLYGON,
  130.   GF_NUM_SHAPES
  131. } GFlareShape;
  132.  
  133. typedef struct {
  134.   gchar        *name;
  135.   gchar        *filename;
  136.   gdouble    glow_opacity;
  137.   GFlareMode    glow_mode;
  138.   gdouble    rays_opacity;
  139.   GFlareMode    rays_mode;
  140.   gdouble    sflare_opacity;
  141.   GFlareMode    sflare_mode;
  142.   GradientName    glow_radial;
  143.   GradientName    glow_angular;
  144.   GradientName    glow_angular_size;
  145.   gdouble    glow_size;
  146.   gdouble    glow_rotation;
  147.   gdouble    glow_hue;
  148.   GradientName    rays_radial;
  149.   GradientName    rays_angular;
  150.   GradientName    rays_angular_size;
  151.   gdouble    rays_size;
  152.   gdouble    rays_rotation;
  153.   gdouble    rays_hue;
  154.   gint        rays_nspikes;
  155.   gdouble    rays_thickness;
  156.   GradientName    sflare_radial;
  157.   GradientName    sflare_sizefac;
  158.   GradientName    sflare_probability;
  159.   gdouble    sflare_size;
  160.   gdouble    sflare_rotation;
  161.   gdouble    sflare_hue;
  162.   GFlareShape    sflare_shape;
  163.   gint        sflare_nverts;
  164.   gint        sflare_seed;
  165. } GFlare;
  166.  
  167. typedef struct {
  168.   FILE        *fp;
  169.   int        error;
  170. } GFlareFile;
  171.  
  172.  
  173. typedef enum {
  174.   PAGE_SETTINGS,
  175.   PAGE_SELECTOR,
  176.   PAGE_GENERAL,
  177.   PAGE_GLOW,
  178.   PAGE_RAYS,
  179.   PAGE_SFLARE
  180. } PageNum;
  181.  
  182.  
  183. typedef struct {
  184.   gint        init;
  185.   GFlare    *gflare;
  186.   GtkWidget    *shell;
  187.   Preview    *preview;
  188.   struct {
  189.     gdouble x0, y0, x1, y1;
  190.   }        pwin;
  191.   gint        update_preview;
  192.   GtkWidget    *notebook;
  193.   GtkWidget    *xentry;
  194.   gint        xentry_id;
  195.   GtkWidget    *yentry;
  196.   gint        yentry_id;
  197.   GtkWidget    *asupsample_frame;
  198.   GtkWidget    *selector_list;
  199.   gint        init_params_done;
  200. } GFlareDialog;
  201.  
  202. typedef void    (*GFlareEditorCallback) (gint updated, gpointer data);
  203.  
  204. typedef struct {
  205.   gint        init;
  206.   gint        run;
  207.   GFlareEditorCallback    callback;
  208.   gpointer    calldata;
  209.   GFlare    *target_gflare;
  210.   GFlare    *gflare;
  211.   GtkWidget    *shell;
  212.   Preview    *preview;
  213.   GtkWidget    *notebook;
  214.   PageNum    cur_page;
  215.   GtkWidget    *polygon_entry;
  216.   GtkWidget    *polygon_toggle;
  217.   gint        init_params_done;
  218. } GFlareEditor;
  219.  
  220. typedef struct
  221. {
  222.   gdouble    x0;
  223.   gdouble    y0;
  224.   gdouble    x1;
  225.   gdouble    y1;
  226. } CalcBounds;
  227.  
  228. typedef struct
  229. {
  230.   gint        init;
  231.   gint        type;
  232.   GFlare *    gflare;
  233.   gdouble    xcenter;
  234.   gdouble    ycenter;
  235.   gdouble    radius;
  236.   gdouble    rotation;
  237.   gdouble    hue;
  238.   gdouble    vangle;
  239.   gdouble    vlength;
  240.  
  241.   gint        glow_opacity;
  242.   CalcBounds    glow_bounds;
  243.   guchar *    glow_radial;
  244.   guchar *    glow_angular;
  245.   guchar *    glow_angular_size;
  246.   gdouble    glow_radius;
  247.   gdouble    glow_rotation;
  248.  
  249.   gint        rays_opacity;
  250.   CalcBounds    rays_bounds;
  251.   guchar *    rays_radial;
  252.   guchar *    rays_angular;
  253.   guchar *    rays_angular_size;
  254.   gdouble    rays_radius;
  255.   gdouble    rays_rotation;
  256.   gdouble    rays_spike_mod;
  257.   gdouble    rays_thinness;
  258.  
  259.   gint        sflare_opacity;
  260.   GList *    sflare_list;
  261.   guchar *    sflare_radial;
  262.   guchar *    sflare_sizefac;
  263.   guchar *    sflare_probability;
  264.   gdouble    sflare_radius;
  265.   gdouble    sflare_rotation;
  266.   GFlareShape    sflare_shape;
  267.   gdouble    sflare_angle;
  268.   gdouble    sflare_factor;
  269. } CalcParams;
  270.  
  271. /*
  272.  * What's the difference between (structure) CalcParams and GFlare ?
  273.  * well, radius and lengths are actual length for CalcParams where
  274.  * they are typically 0 to 100 for GFlares, and angles are M_PI based
  275.  * (radian) for CalcParams where they are degree for GFlares. et cetra.
  276.  * This is because convienience for dialog processing and for calculating.
  277.  * these conversion is taken place in calc init routines. see below.
  278.  */
  279.  
  280. typedef struct
  281. {
  282.   gdouble    xcenter;
  283.   gdouble    ycenter;
  284.   gdouble    radius;
  285.   CalcBounds    bounds;
  286. } CalcSFlare;
  287.  
  288.  
  289. typedef struct
  290. {
  291.   gint            is_color;
  292.   gint            has_alpha;
  293.   gint            x1, y1, x2, y2;        /* mask bounds */
  294.   gint            tile_width, tile_height;
  295.             /* these values don't belong to drawable, though. */
  296. } DrawableInfo;
  297.  
  298. typedef struct
  299. {
  300.   GTile *    tile;
  301.   gint        col;
  302.   gint        row;
  303.   gint        shadow;
  304.   gint        dirty;
  305. } TileKeeper;
  306.  
  307. typedef struct _GradientMenu    GradientMenu;
  308. typedef void (*GradientMenuCallback) ( gchar *gradient_name,
  309.                        gpointer data );
  310. struct _GradientMenu {
  311.   GtkWidget        *preview;
  312.   GtkWidget        *option_menu;
  313.   GradientMenuCallback    callback;
  314.   gpointer        callback_data;
  315.   GradientName        gradient_name;
  316. };
  317.  
  318. typedef gint (*PreviewInitFunc) (Preview *preview, gpointer data);
  319. typedef void (*PreviewRenderFunc) (Preview *preview, guchar *buffer, gint y, gpointer data);
  320. typedef void (*PreviewDeinitFunc) (Preview *preview, gpointer data);
  321.  
  322. struct _Preview
  323. {
  324.   GtkWidget        *widget;
  325.   gint            width;
  326.   gint            height;
  327.   PreviewInitFunc    init_func;
  328.   gpointer        init_data;
  329.   PreviewRenderFunc    render_func;
  330.   gpointer        render_data;
  331.   PreviewDeinitFunc    deinit_func;
  332.   gpointer        deinit_data;
  333.   gint            timeout_tag;
  334.   gint            idle_tag;
  335.   gint            init_done;
  336.   gint            current_y;
  337.   gint            drawn_y;
  338.   guchar        *buffer;
  339. }; /* Preview */
  340.  
  341. typedef struct
  342. {
  343.   gint            tag;
  344.   gint            got_gradients;
  345.   gint            current_y;
  346.   gint            drawn_y;
  347.   PreviewRenderFunc    render_func;
  348.   guchar        *buffer;
  349. } PreviewIdle;
  350.  
  351. typedef struct _GradientCacheItem  GradientCacheItem;
  352. struct _GradientCacheItem
  353. {
  354.   GradientCacheItem    *next;
  355.   GradientCacheItem    *prev;
  356.   GradientName        name;
  357.   guchar        values[4*GRADIENT_RESOLUTION];
  358. };
  359.  
  360.  
  361.  
  362. typedef enum {
  363.   ENTSCALE_INT,
  364.   ENTSCALE_DOUBLE
  365. } EntscaleType;
  366.  
  367. typedef void (*EntscaleCallbackFunc) (gdouble new_val, gpointer data);
  368.  
  369. typedef struct {
  370.   GtkObject        *adjustment;
  371.   GtkWidget        *entry;
  372.   EntscaleType        type;
  373.   gchar            fmt_string[16];
  374.   gint            constraint;
  375.   EntscaleCallbackFunc    callback;
  376.   gpointer        call_data;
  377. } Entscale;
  378.  
  379.  
  380. typedef struct {
  381.   gint        xcenter;
  382.   gint        ycenter;
  383.   gdouble    radius;
  384.   gdouble    rotation;
  385.   gdouble    hue;
  386.   gdouble    vangle;
  387.   gdouble    vlength;
  388.   gint        use_asupsample;
  389.   gint        asupsample_max_depth;
  390.   gdouble    asupsample_threshold;
  391.   gchar        gflare_name[GFLARE_NAME_MAX];
  392. } PluginValues;
  393.  
  394. typedef struct {
  395.   gint        run;
  396. } PluginInterface;
  397.  
  398. typedef void (*QueryFunc) (GtkWidget *, gpointer, gpointer);
  399.  
  400. /***
  401. ***    Global Functions Prototypes
  402. **/
  403.  
  404. /* *** INSERT-FILE "proto/gflare.c.pro" *** */
  405. /* gflare.c */
  406. int main (int argc, char **argv);
  407. void plugin_query (void);
  408. void plugin_run (gchar *name, gint nparams, GParam *param, gint *nreturn_vals, GParam **return_vals);
  409. void plug_in_parse_gflare_path (void);
  410. GFlare *gflare_new_with_default (gchar *new_name);
  411. GFlare *gflare_dup (GFlare *src, gchar *new_name);
  412. void gflare_copy (GFlare *dest, GFlare *src);
  413. GFlare *gflare_load (char *filename, char *name);
  414. void gflare_save (GFlare *gflare);
  415. void gflare_name_copy (gchar *dest, gchar *src);
  416. gint gflares_list_insert (GFlare *gflare);
  417. GFlare *gflares_list_lookup (gchar *name);
  418. gint gflares_list_index (GFlare *gflare);
  419. gint gflares_list_remove (GFlare *gflare);
  420. void gflares_list_load_all (void);
  421. void gflares_list_free_all (void);
  422. void calc_init_params (GFlare *gflare, gint calc_type, gdouble xcenter, gdouble ycenter, gdouble radius, gdouble rotation, gdouble hue, gdouble vangle, gdouble vlength);
  423. int calc_init_progress (void);
  424. void calc_deinit (void);
  425. void calc_glow_pix (guchar *dest_pix, gdouble x, gdouble y);
  426. void calc_rays_pix (guchar *dest_pix, gdouble x, gdouble y);
  427. void calc_sflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix);
  428. void calc_gflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix);
  429. int dlg_run (void);
  430. void dlg_preview_calc_window (void);
  431. GtkWidget *ed_mode_menu_new (GFlareMode *mode_var);
  432. Preview *preview_new (gint width, gint height, PreviewInitFunc init_func, gpointer init_data, PreviewRenderFunc render_func, gpointer render_data, PreviewDeinitFunc deinit_func, gpointer deinit_data);
  433. void preview_free (Preview *preview);
  434. void preview_render_start (Preview *preview);
  435. void preview_render_end (Preview *preview);
  436. void preview_rgba_to_rgb (guchar *dest, gint x, gint y, guchar *src);
  437. void gradient_menu_init (void);
  438. void gradient_menu_rescan (void);
  439. GradientMenu *gradient_menu_new (GradientMenuCallback callback, gpointer callback_data, gchar *default_gradient_name);
  440. void gradient_menu_destroy (GradientMenu *gm);
  441. void gradient_name_copy (gchar *dest, gchar *src);
  442. void gradient_name_encode (guchar *dest, guchar *src);
  443. void gradient_name_decode (guchar *dest, guchar *src);
  444. void gradient_init (void);
  445. void gradient_free (void);
  446. char **gradient_get_list (gint *num_gradients);
  447. void gradient_get_values (gchar *gradient_name, guchar *values, gint nvalues);
  448. void gradient_cache_flush (void);
  449. void entscale_new (GtkWidget *table, gint x, gint y, gchar *caption, EntscaleType type, gpointer variable, gdouble min, gdouble max, gdouble step, gint constraint, EntscaleCallbackFunc callback, gpointer call_data);
  450. GtkWidget *query_string_box (char *title, char *message, char *initial, QueryFunc callback, gpointer data);
  451.  
  452. /* *** INSERT-FILE-END *** */
  453.  
  454. /**
  455. ***    Variables
  456. **/
  457.  
  458. GPlugInInfo PLUG_IN_INFO =
  459. {
  460.   NULL,       /* init_proc */
  461.   NULL,       /* quit_proc */
  462.   plugin_query,      /* query_proc */
  463.   plugin_run,       /* run_proc */
  464. };
  465.  
  466. PluginValues pvals =
  467. {
  468.   128,                /* xcenter */
  469.   128,                /* ycenter */
  470.   100.0,            /* radius */
  471.   0.0,                /* rotation */
  472.   0.0,                /* hue */
  473.   60.0,                /* vangle */
  474.   400.0,            /* vlength */
  475.   FALSE,            /* use_asupsample */
  476.   3,                /* asupsample_max_depth */
  477.   0.2,                /* asupsample_threshold */
  478.   "Default"            /* gflare_name */
  479. };
  480.  
  481. PluginInterface pint =
  482. {
  483.   FALSE                /* run */
  484. };
  485.  
  486. GFlare default_gflare =
  487. {
  488.   NULL,                /* name */
  489.   NULL,                /* filename */
  490.   100,                /* glow_opacity */
  491.   GF_NORMAL,            /* glow_mode */
  492.   100,                /* rays_opacity */
  493.   GF_NORMAL,            /* rays_mode */
  494.   100,                /* sflare_opacity */
  495.   GF_NORMAL,            /* sflare_mode */
  496.   "%red_grad",            /* glow_radial */
  497.   "%white",            /* glow_angular */
  498.   "%white",            /* glow_angular_size */
  499.   100.0,            /* glow_size */
  500.   0.0,                /* glow_rotation */
  501.   0.0,                /* glow_hue */
  502.   "%white_grad",        /* rays_radial */
  503.   "%random",            /* rays_angular */
  504.   "%random",            /* rays_angular_size */
  505.   100.0,            /* rays_size */
  506.   0.0,                /* rays_rotation */
  507.   0.0,                /* rays_hue */
  508.   40,                /* rays_nspikes */
  509.   20.0,                /* rays_thickness */
  510.   "%white_grad",        /* sflare_radial */
  511.   "%random",            /* sflare_sizefac */
  512.   "%random",            /* sflare_probability */
  513.   40.0,                /* sflare_size */
  514.   0.0,                /* sflare_rotation */
  515.   0.0,                /* sflare_hue */
  516.   GF_CIRCLE,            /* sflare_shape */
  517.   6,                /* sflare_nverts */
  518.   1                /* sflare_seed */
  519. };
  520.  
  521. static gchar *gflare_modes[] =
  522. {
  523.   "NORMAL",
  524.   "ADDITION",
  525.   "OVERLAY",
  526.   "SCREEN"
  527. };
  528.  
  529. static gchar *gflare_shapes[] =
  530. {
  531.   "CIRCLE",
  532.   "POLYGON"
  533. };
  534.  
  535. static GDrawable *        drawable;
  536. static DrawableInfo        dinfo;
  537. static TileKeeper *        tk_read;
  538. static TileKeeper *        tk_write;
  539. static GFlareDialog *        dlg = NULL;
  540. static GFlareEditor *        ed = NULL;
  541. static GList *            gflares_list = NULL;
  542. static gint            num_gflares = 0;
  543. static GList *            gflare_path_list;
  544. static CalcParams        calc;
  545. GList *                gradient_menus;
  546. static char **            gradient_names = NULL;
  547. static int            num_gradient_names = 0;
  548. static GradientCacheItem *    gradient_cache_head  = NULL;
  549. static gint            gradient_cache_count = 0;
  550.  
  551.  
  552. static char *internal_gradients[] =
  553. {
  554.   "%white", "%white_grad", "%red_grad", "%blue_grad", "%yellow_grad", "%random"
  555. };
  556. static int internal_ngradients = sizeof (internal_gradients) / sizeof (internal_gradients[0]);
  557.  
  558. #ifdef DEBUG
  559. static int    get_values_external_count = 0;
  560. static clock_t    get_values_external_clock = 0;
  561. #endif
  562.  
  563.  
  564. /**
  565. ***    +++ Static Functions Prototypes
  566. **/
  567.  
  568. /* *** INSERT-FILE "proto/gflare.c.prs" *** */
  569.  static void plugin_do (void);
  570.  static void plugin_do_non_asupsample (void);
  571.  static void plugin_do_asupsample (void);
  572.  static void plugin_render_func (double x, double y, color_t *color, gpointer data);
  573.  static void plugin_put_pixel_func (int ix, int iy, color_t color, gpointer data);
  574.  static void plugin_progress_func (int y1, int y2, int curr_y, gpointer data);
  575.  static TileKeeper *tile_keeper_new (gint shadow);
  576.  static guchar *tile_keeper_provide (TileKeeper *tk, gint ix, gint iy, gint dirty);
  577.  static void tile_keeper_free (TileKeeper *tk);
  578.  static GFlare *gflare_new (void);
  579.  static void gflare_free (GFlare *gflare);
  580.  static void gflare_read_int (gint *intvar, GFlareFile *gf);
  581.  static void gflare_read_double (gdouble *dblvar, GFlareFile *gf);
  582.  static void gflare_read_gradient_name (gchar *name, GFlareFile *gf);
  583.  static void gflare_read_shape (GFlareShape *shape, GFlareFile *gf);
  584.  static void gflare_read_mode (GFlareMode *mode, GFlareFile *gf);
  585.  static void gflare_write_gradient_name (gchar *name, FILE *fp);
  586.  static gint calc_sample_one_gradient (void);
  587.  static void calc_place_sflare (void);
  588.  static void calc_get_gradient (guchar *pix, guchar *gradient, gdouble pos);
  589.  static gdouble fmod_positive (gdouble x, gdouble m);
  590.  static void calc_paint_func (guchar *dest, guchar *src1, guchar *src2, gint opacity, GFlareMode mode);
  591.  static void calc_combine (guchar *dest, guchar *src1, guchar *src2, gint opacity);
  592.  static void calc_addition (guchar *dest, guchar *src1, guchar *src2);
  593.  static void calc_screen (guchar *dest, guchar *src1, guchar *src2);
  594.  static void calc_overlay (guchar *dest, guchar *src1, guchar *src2);
  595.  static void rgb_to_hsv (int *r, int *g, int *b);
  596.  static void hsv_to_rgb (int *h, int *s, int *v);
  597.  static void dlg_ok_callback (GtkWidget *widget, gpointer data);
  598.  static void dlg_close_callback (GtkWidget *widget, gpointer data);
  599.  static void dlg_rescan_callback (GtkWidget *widget, gpointer data);
  600.  static void dlg_setup_gflare (void);
  601.  static void dlg_page_map_callback (GtkWidget *widget, gpointer data);
  602.  static gint dlg_preview_handle_event (GtkWidget *widget, GdkEvent *event);
  603.  static void dlg_preview_update (void);
  604.  static gint dlg_preview_init_func (Preview *preview, gpointer data);
  605.  static void dlg_preview_render_func (Preview *preview, guchar *dest, gint y, gpointer data);
  606.  static void dlg_preview_deinit_func (Preview *preview, gpointer data);
  607.  static void dlg_make_page_settings (GFlareDialog *dlg, GtkWidget *notebook);
  608.  static void dlg_position_entry_callback (GtkWidget *widget, gpointer data);
  609.  static void dlg_entscale_callback (gdouble new_val, gpointer data);
  610.  static void dlg_use_asupsample_callback (GtkWidget *widget, gpointer data);
  611.  static void dlg_iscale_callback (GtkAdjustment *adjustment, gpointer data);
  612.  static void dlg_dscale_callback (GtkAdjustment *adjustment, gpointer data);
  613.  static void dlg_update_preview_callback (GtkWidget *widget, gpointer data);
  614.  static void dlg_make_page_selector (GFlareDialog *dlg, GtkWidget *notebook);
  615.  static void dlg_selector_setup_listbox (void);
  616.  static void dlg_selector_insert (GFlare *gflare, int pos, int select);
  617.  static void dlg_selector_list_item_callback (GtkWidget *widget, gpointer data);
  618.  static void dlg_selector_new_callback (GtkWidget *widget, gpointer data);
  619.  static void dlg_selector_new_ok_callback (GtkWidget *widget, gpointer client_data, gpointer call_data);
  620.  static void dlg_selector_edit_callback (GtkWidget *widget, gpointer data);
  621.  static void dlg_selector_edit_done_callback (gint updated, gpointer data);
  622.  static void dlg_selector_copy_callback (GtkWidget *widget, gpointer data);
  623.  static void dlg_selector_copy_ok_callback (GtkWidget *widget, gpointer client_data, gpointer call_data);
  624.  static void dlg_selector_delete_callback (GtkWidget *widget, gpointer data);
  625.  static void dlg_selector_delete_ok_callback (GtkWidget *widget, gpointer client_data);
  626.  static void dlg_selector_delete_cancel_callback (GtkWidget *widget, gpointer client_data);
  627.  static void ed_run (GFlare *target_gflare, GFlareEditorCallback callback, gpointer calldata);
  628.  static void ed_close_callback (GtkWidget *widget, gpointer data);
  629.  static void ed_ok_callback (GtkWidget *widget, gpointer data);
  630.  static void ed_rescan_callback (GtkWidget *widget, gpointer data);
  631.  static void ed_make_page_general (GFlareEditor *ed, GtkWidget *notebook);
  632.  static void ed_make_page_glow (GFlareEditor *ed, GtkWidget *notebook);
  633.  static void ed_make_page_rays (GFlareEditor *ed, GtkWidget *notebook);
  634.  static void ed_make_page_sflare (GFlareEditor *ed, GtkWidget *notebook);
  635.  static void ed_put_mode_menu (GtkWidget *table, gint x, gint y, gchar *caption, GtkWidget *option_menu);
  636.  static void ed_put_gradient_menu (GtkWidget *table, gint x, gint y, gchar *caption, GradientMenu *gm);
  637.  static void ed_entscale_callback (gdouble new_val, gpointer data);
  638.  static void ed_mode_menu_callback (GtkWidget *widget, gpointer data);
  639.  static void ed_gradient_menu_callback (gchar *gradient_name, gpointer data);
  640.  static void ed_shape_radio_callback (GtkWidget *widget, gpointer data);
  641.  static void ed_ientry_callback (GtkWidget *widget, gpointer data);
  642.  static void ed_page_map_callback (GtkWidget *widget, gpointer data);
  643.  static void ed_preview_update (void);
  644.  static gint ed_preview_init_func (Preview *preview, gpointer data);
  645.  static void ed_preview_deinit_func (Preview *preview, gpointer data);
  646.  static void ed_preview_render_func (Preview *preview, guchar *buffer, gint y, gpointer data);
  647.  static void ed_preview_render_general (guchar *buffer, gint y);
  648.  static void ed_preview_render_glow (guchar *buffer, gint y);
  649.  static void ed_preview_render_rays (guchar *buffer, gint y);
  650.  static void ed_preview_render_sflare (guchar *buffer, gint y);
  651.  static gint preview_render_start_2 (Preview *preview);
  652.  static gint preview_handle_idle (Preview *preview);
  653.  static void gm_gradient_get_list (void);
  654.  static GtkWidget *gm_menu_new (GradientMenu *gm, gchar *default_gradient_name);
  655.  static GtkWidget *gm_menu_create_sub_menus (GradientMenu *gm, gint start_n, gchar **active_name_ptr, gchar *default_gradient_name);
  656.  static void gm_menu_item_callback (GtkWidget *w, gpointer data);
  657.  static void gm_preview_draw (GtkWidget *preview, gchar *gradient_name);
  658.  static void gm_option_menu_destroy_callback (GtkWidget *w, gpointer data);
  659.  static void gradient_get_values_internal (gchar *gradient_name, guchar *values, gint nvalues);
  660.  static void gradient_get_blend (guchar *fg, guchar *bg, guchar *values, gint nvalues);
  661.  static void gradient_get_random (gint seed, guchar *values, gint nvalues);
  662.  static void gradient_get_default (gchar *name, guchar *values, gint nvalues);
  663.  static void gradient_get_values_external (gchar *gradient_name, guchar *values, gint nvalues);
  664.  static void gradient_get_values_real_external (gchar *gradient_name, guchar *values, gint nvalues);
  665.  static GradientCacheItem *gradient_cache_lookup (gchar *name, gint *found);
  666.  static void gradient_cache_zorch (void);
  667.  static int entscale_get_precision (gdouble step);
  668.  static void entscale_destroy_callback (GtkWidget *widget, gpointer data);
  669.  static void entscale_scale_update (GtkAdjustment *adjustment, gpointer data);
  670.  static void entscale_entry_update (GtkWidget *widget, gpointer data);
  671.  static void query_box_cancel_callback (GtkWidget *w, gpointer client_data);
  672.  static void query_box_ok_callback (GtkWidget *w, gpointer client_data);
  673.  
  674. /* *** INSERT-FILE-END *** */
  675.  
  676.  
  677. /*************************************************************************/
  678. /**                                    **/
  679. /**        +++ Plug-in Interfaces                    **/
  680. /**                                    **/
  681. /*************************************************************************/
  682.  
  683. MAIN ();
  684.  
  685. void
  686. plugin_query()
  687. {
  688.   static GParamDef args[]=
  689.     {
  690.       { PARAM_INT32, "run_mode", "Interactive, non-interactive" },
  691.       { PARAM_IMAGE, "image", "Input image (unused)" },
  692.       { PARAM_DRAWABLE, "drawable", "Input drawable" },
  693.       { PARAM_STRING, "gflare_name", "The name of GFlare" },
  694.       { PARAM_INT32, "xcenter", "X coordinate of center of GFlare" },
  695.       { PARAM_INT32, "ycenter", "Y coordinate of center of GFlare" },
  696.       { PARAM_FLOAT, "radius",    "Radius of GFlare (pixel)" },
  697.       { PARAM_FLOAT, "rotation", "Rotation of GFlare (degree)" },
  698.       { PARAM_FLOAT, "hue", "Hue rotation of GFlare (degree)" },
  699.       { PARAM_FLOAT, "vangle", "Vector angle for second flares (degree)" },
  700.       { PARAM_FLOAT, "vlength", "Vector length for second flares (percentage to Radius)" },
  701.       { PARAM_INT32, "use_asupsample", "Whether it uses or not adaptive supersampling while rendering (boolean)" },
  702.       { PARAM_INT32, "asupsample_max_depth", "Max depth for adaptive supersampling"},
  703.       { PARAM_FLOAT, "asupsample_threshold", "Threshold for adaptive supersampling"}
  704.    };
  705.   static GParamDef *return_vals = NULL;
  706.   static gint nargs = sizeof (args) / sizeof (args[0]);
  707.   static gint nreturn_vals = 0;
  708.   gchar     *help_string =
  709.     " This plug-in produces a lense flare effect using custom gradients."
  710.     " In interactive call, the user can edit his/her own favorite lense flare"
  711.     " (GFlare) and render it. Edited gflare is saved automatically to"
  712.     " the directory in gflare-path, if it is defined in gimprc."
  713.     " In non-interactive call, the user can only render one of GFlare"
  714.     " which has been stored in gflare-path already.";
  715.  
  716.  
  717.   gimp_install_procedure ("plug_in_gflare",
  718.               "Produce lense flare effect using custom gradients",
  719.               help_string,
  720.               "Eiichi Takamori",
  721.               "Eiichi Takamori, and a lot of GIMP people",
  722.               "1997",
  723.               "<Image>/Filters/Light Effects/GFlare",
  724.               "RGB*, GRAY*",
  725.               PROC_PLUG_IN,
  726.               nargs, nreturn_vals,
  727.               args, return_vals);
  728. }
  729.  
  730. void
  731. plugin_run (gchar   *name,
  732.      gint    nparams,
  733.      GParam  *param,
  734.      gint    *nreturn_vals,
  735.      GParam  **return_vals)
  736. {
  737.   static GParam values[1];
  738.   GRunModeType run_mode;
  739.   GStatusType status = STATUS_SUCCESS;
  740.  
  741.   /* Initialize */
  742.   run_mode = param[0].data.d_int32;
  743.  
  744.   *nreturn_vals = 1;
  745.   *return_vals = values;
  746.  
  747.   values[0].type = PARAM_STATUS;
  748.   values[0].data.d_status = status;
  749.  
  750.   /*
  751.    *    Get the specified drawable and its info (global variable)
  752.    */
  753.  
  754.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  755.   dinfo.is_color  = gimp_drawable_color (drawable->id);
  756.   dinfo.has_alpha = gimp_drawable_has_alpha (drawable->id);
  757.   gimp_drawable_mask_bounds (drawable->id, &dinfo.x1, &dinfo.y1,
  758.                  &dinfo.x2, &dinfo.y2);
  759.   dinfo.tile_width = gimp_tile_width ();
  760.   dinfo.tile_height = gimp_tile_height ();
  761.  
  762.   /*
  763.    *    Start gradient caching
  764.    */
  765.  
  766.   gradient_init ();
  767.  
  768.   /*
  769.    *    Parse gflare path from gimprc and load gflares
  770.    */
  771.  
  772.   plug_in_parse_gflare_path ();
  773.   gflares_list_load_all ();
  774.  
  775.   gimp_tile_cache_ntiles ( drawable->width / gimp_tile_width () + 2);
  776.  
  777.  
  778.   switch (run_mode)
  779.     {
  780.     case RUN_INTERACTIVE:
  781.       /*  Possibly retrieve data  */
  782.       gimp_get_data ("plug_in_gflare", &pvals);
  783.  
  784.       /*  First acquire information with a dialog  */
  785.       if (! dlg_run ())
  786.     {
  787.       gimp_drawable_detach (drawable);
  788.       return;
  789.     }
  790.       break;
  791.  
  792.     case RUN_NONINTERACTIVE:
  793. #if 0
  794.       printf("Currently non interactive call of gradient flare is not supported\n");
  795.       status = STATUS_CALLING_ERROR;
  796.       break;
  797. #endif
  798.       if (nparams != 14)
  799.     status = STATUS_CALLING_ERROR;
  800.       if (status == STATUS_SUCCESS)
  801.     {
  802.       gflare_name_copy (pvals.gflare_name, param[3].data.d_string);
  803.       pvals.xcenter             = param[4].data.d_int32;
  804.       pvals.ycenter             = param[5].data.d_int32;
  805.       pvals.radius             = param[6].data.d_float;
  806.       pvals.rotation         = param[7].data.d_float;
  807.       pvals.hue             = param[8].data.d_float;
  808.       pvals.vangle             = param[9].data.d_float;
  809.       pvals.vlength             = param[10].data.d_float;
  810.       pvals.use_asupsample         = param[11].data.d_int32;
  811.       pvals.asupsample_max_depth = param[12].data.d_int32;
  812.       pvals.asupsample_threshold = param[13].data.d_float;
  813.     }
  814.       if (pvals.radius <= 0)
  815.     status = STATUS_CALLING_ERROR;
  816.       break;
  817.  
  818.     case RUN_WITH_LAST_VALS:
  819.       /*  Possibly retrieve data  */
  820.       gimp_get_data ("plug_in_gflare", &pvals);
  821.       break;
  822.  
  823.     default:
  824.       break;
  825.     }
  826.  
  827.   if (status == STATUS_SUCCESS)
  828.     {
  829.       /*  Make sure that the drawable is gray or RGB color  */
  830.       if (gimp_drawable_color (drawable->id) || gimp_drawable_gray (drawable->id))
  831.     {
  832.       gimp_progress_init ("Gradient Flare...");
  833.       plugin_do ();
  834.  
  835.       if (run_mode != RUN_NONINTERACTIVE)
  836.         gimp_displays_flush ();
  837.  
  838.       /*  Store data  */
  839.       if (run_mode == RUN_INTERACTIVE)
  840.         gimp_set_data ("plug_in_gflare", &pvals, sizeof (PluginValues));
  841.     }
  842.       else
  843.     {
  844.       /* gimp_message ("gflare: cannot operate on indexed color images"); */
  845.       status = STATUS_EXECUTION_ERROR;
  846.     }
  847.     }
  848.  
  849.   values[0].data.d_status = status;
  850.  
  851.   /*
  852.    *    Deinitialization
  853.    */
  854.   gradient_free ();
  855.   gimp_drawable_detach (drawable);
  856. }
  857.  
  858. /*
  859.  *    Query gimprc for gflare-path, and parse it.
  860.  *    This code is based on script_fu_find_scripts ()
  861.  */
  862. void
  863. plug_in_parse_gflare_path ()
  864. {
  865.   GParam *return_vals;
  866.   gint nreturn_vals;
  867.   gchar *path_string;
  868.   gchar *home;
  869.   gchar *path;
  870.   gchar *token;
  871.   struct stat filestat;
  872.   gint    err;
  873.  
  874.   gflare_path_list = NULL;
  875.  
  876.   return_vals = gimp_run_procedure ("gimp_gimprc_query",
  877.                     &nreturn_vals,
  878.                     PARAM_STRING, "gflare-path",
  879.                     PARAM_END);
  880.  
  881.   if (return_vals[0].data.d_status != STATUS_SUCCESS || return_vals[1].data.d_string == NULL)
  882.     {
  883.       DEBUG_PRINT(("No gflare-path in gimprc: gflare_path_list is NULL\n"));
  884.       gimp_destroy_params (return_vals, nreturn_vals);
  885.       return;
  886.     }
  887.  
  888.   path_string = g_strdup (return_vals[1].data.d_string);
  889.   gimp_destroy_params (return_vals, nreturn_vals);
  890.  
  891.   /* Set local path to contain temp_path, where (supposedly)
  892.    * there may be working files.
  893.    */
  894.   home = getenv ("HOME");
  895.  
  896.   /* Search through all directories in the  path */
  897.  
  898.   token = strtok (path_string, ":");
  899.  
  900.   while (token)
  901.     {
  902.       if (*token == '\0')
  903.     {
  904.       token = strtok (NULL, ":");
  905.       continue;
  906.     }
  907.  
  908.       if (*token == '~')
  909.     {
  910.       path = g_malloc (strlen (home) + strlen (token) + 2);
  911.       sprintf (path, "%s%s", home, token + 1);
  912.     }
  913.       else
  914.     {
  915.       path = g_malloc (strlen (token) + 2);
  916.       strcpy (path, token);
  917.     } /* else */
  918.  
  919.       /* Check if directory exists */
  920.       err = stat (path, &filestat);
  921.  
  922.       if (!err && S_ISDIR (filestat.st_mode))
  923.     {
  924.       if (path[strlen (path) - 1] != '/')
  925.         strcat (path, "/");
  926.  
  927.       DEBUG_PRINT(("Added `%s' to gflare_path_list\n", path));
  928.       gflare_path_list = g_list_append (gflare_path_list, path);
  929.     }
  930.       else
  931.     {
  932.       DEBUG_PRINT(("Not found `%s'\n", path));
  933.       g_free (path);
  934.     }
  935.  
  936.       token = strtok (NULL, ":");
  937.     }
  938.   g_free (path_string);
  939.  
  940. }
  941.  
  942.  
  943. static void
  944. plugin_do ()
  945. {
  946.   GFlare    *gflare;
  947.  
  948.   gflare = gflares_list_lookup (pvals.gflare_name);
  949.   if (gflare == NULL)
  950.     {
  951.       /* FIXME */
  952.       g_warning ("Not found %s\n", pvals.gflare_name);
  953.       return;
  954.     }
  955.  
  956.   /* Initialize calc params and gradients */
  957.   calc_init_params (gflare, CALC_GLOW | CALC_RAYS | CALC_SFLARE,
  958.             pvals.xcenter, pvals.ycenter,
  959.             pvals.radius, pvals.rotation, pvals.hue,
  960.             pvals.vangle, pvals.vlength);
  961.   while (calc_init_progress ()) ;
  962.  
  963.   /* Render it ! */
  964.   if (pvals.use_asupsample)
  965.     plugin_do_asupsample ();
  966.   else
  967.     plugin_do_non_asupsample ();
  968.  
  969.   /* Clean up */
  970.   calc_deinit ();
  971.   gimp_drawable_flush (drawable);
  972.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  973.   gimp_drawable_update (drawable->id, dinfo.x1, dinfo.y1,
  974.             (dinfo.x2 - dinfo.x1), (dinfo.y2 - dinfo.y1));
  975. }
  976.  
  977. /* these routines should be almost rewritten anyway */
  978.  
  979. static void
  980. plugin_do_non_asupsample ()
  981. {
  982.   GPixelRgn    src_rgn, dest_rgn;
  983.   gpointer    pr;
  984.   guchar    *src_row, *dest_row;
  985.   guchar    *src, *dest;
  986.   gint        row, col;
  987.   gint        x, y;
  988.   gint        b;
  989.   gint        progress, max_progress;
  990.   guchar    src_pix[4], dest_pix[4];
  991.  
  992.   progress = 0;
  993.   max_progress = (dinfo.x2 - dinfo.x1) * (dinfo.y2 - dinfo.y1);
  994.  
  995.   gimp_pixel_rgn_init (&src_rgn, drawable, dinfo.x1, dinfo.y1, dinfo.x2, dinfo.y2, FALSE, FALSE);
  996.   gimp_pixel_rgn_init (&dest_rgn, drawable, dinfo.x1, dinfo.y1, dinfo.x2, dinfo.y2, TRUE, TRUE);
  997.  
  998.   for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
  999.        pr != NULL; pr = gimp_pixel_rgns_process (pr))
  1000.     {
  1001.       src_row  = src_rgn.data;
  1002.       dest_row = dest_rgn.data;
  1003.  
  1004.       for ( row = 0, y = src_rgn.y; row < src_rgn.h; row++, y++)
  1005.     {
  1006.       src = src_row;
  1007.       dest = dest_row;
  1008.  
  1009.       for ( col = 0, x = src_rgn.x; col < src_rgn.w; col++, x++)
  1010.         {
  1011.           for (b = 0; b < 3; b++)
  1012.         src_pix[b] = dinfo.is_color ? src[b] : src[0];
  1013.           src_pix[3] = dinfo.has_alpha ? src[src_rgn.bpp - 1] : OPAQUE;
  1014.  
  1015.           calc_gflare_pix (dest_pix, x, y, src_pix);
  1016.  
  1017.           if (dinfo.is_color)
  1018.         for (b = 0; b < 3; b++)
  1019.           dest[b] = dest_pix[b];
  1020.           else
  1021.         dest[0] = LUMINOSITY (dest_pix);
  1022.  
  1023.           if (dinfo.has_alpha)
  1024.         dest[src_rgn.bpp - 1] = dest_pix[3];
  1025.  
  1026.           src  += src_rgn.bpp;
  1027.           dest += dest_rgn.bpp;
  1028.         }
  1029.       src_row  += src_rgn.rowstride;
  1030.       dest_row += dest_rgn.rowstride;
  1031.     }
  1032.       /* Update progress */
  1033.       progress += src_rgn.w * src_rgn.h;
  1034.       gimp_progress_update ((double) progress / (double) max_progress);
  1035.     }
  1036. }
  1037.  
  1038. static void
  1039. plugin_do_asupsample ()
  1040. {
  1041.   tk_read  = tile_keeper_new (FALSE);
  1042.   tk_write = tile_keeper_new (TRUE);
  1043.  
  1044.   adaptive_supersample_area (dinfo.x1, dinfo.y1, dinfo.x2 - 1, dinfo.y2 - 1,
  1045.                  pvals.asupsample_max_depth,
  1046.                  pvals.asupsample_threshold,
  1047.                  (render_func_t) plugin_render_func,
  1048.                  NULL,
  1049.                  (put_pixel_func_t) plugin_put_pixel_func,
  1050.                  NULL,
  1051.                  (progress_func_t) plugin_progress_func,
  1052.                  NULL);
  1053.   tile_keeper_free (tk_read);
  1054.   tile_keeper_free (tk_write);
  1055. }
  1056.  
  1057. /*
  1058.   Adaptive supersampling callback functions
  1059.  
  1060.   These routines may look messy, since adaptive supersampling needs
  1061.   pixel values in `double' (from 0.0 to 1.0) but calc_*_pix () returns
  1062.   guchar values. */
  1063.  
  1064. static void
  1065. plugin_render_func (double x, double y, color_t *color, gpointer data)
  1066. {
  1067.   guchar    src_pix[4];
  1068.   guchar    flare_pix[4];
  1069.   guchar    *src;
  1070.   gint        b;
  1071.   gint        ix, iy;
  1072.  
  1073.   /* translate (0.5, 0.5) before convert to `int' so that it can surely
  1074.      point the center of pixel */
  1075.   ix = floor (x + 0.5);
  1076.   iy = floor (y + 0.5);
  1077.  
  1078.   src = tile_keeper_provide (tk_read, ix, iy, FALSE);
  1079.  
  1080.   for (b = 0; b < 3; b++)
  1081.     src_pix[b] = dinfo.is_color ? src[b] : src[0];
  1082.   src_pix[3] = dinfo.has_alpha ? src[drawable->bpp - 1] : OPAQUE;
  1083.  
  1084.   calc_gflare_pix (flare_pix, x, y, src_pix);
  1085.  
  1086.   color->r = flare_pix[0] / 255.0;
  1087.   color->g = flare_pix[1] / 255.0;
  1088.   color->b = flare_pix[2] / 255.0;
  1089.   color->a = flare_pix[3] / 255.0;
  1090. }
  1091.  
  1092. static void
  1093. plugin_put_pixel_func (int ix, int iy, color_t color, gpointer data)
  1094. {
  1095.   guchar    *dest;
  1096.  
  1097.   dest = tile_keeper_provide (tk_write, ix, iy, TRUE);
  1098.  
  1099.   if (dinfo.is_color)
  1100.     {
  1101.       dest[0] = color.r * 255;
  1102.       dest[1] = color.g * 255;
  1103.       dest[2] = color.b * 255;
  1104.     }
  1105.   else
  1106.     dest[0] = (color.r * 0.30 + color.g * 0.59 + color.b * 0.11) * 255;
  1107.  
  1108.   if (dinfo.has_alpha)
  1109.     dest[drawable->bpp - 1] = color.a * 255;
  1110. }
  1111.  
  1112. static void
  1113. plugin_progress_func (int y1, int y2, int curr_y, gpointer data)
  1114. {
  1115.   gimp_progress_update ((double) curr_y / (double) (y2 - y1));
  1116. }
  1117.  
  1118. /**
  1119. ***    The Tile Keeper
  1120. **/
  1121.  
  1122. static TileKeeper *
  1123. tile_keeper_new (gint shadow)
  1124. {
  1125.   TileKeeper    *tk;
  1126.  
  1127.   tk = g_new0 (TileKeeper, 1);
  1128.   tk->tile      = NULL;
  1129.   tk->col      = 0;
  1130.   tk->row      = 0;
  1131.   tk->shadow      = shadow;
  1132.   tk->dirty      = FALSE;
  1133.  
  1134.   return tk;
  1135. }
  1136.  
  1137. /*
  1138.   Return the pointer to specified pixel in allocated tile
  1139.   */
  1140. static guchar *
  1141. tile_keeper_provide (TileKeeper *tk, gint ix, gint iy, gint dirty)
  1142. {
  1143.   static guchar black[4];
  1144.   gint        col, row, offx, offy;
  1145.  
  1146.   if (ix < 0 || ix >= drawable->width || iy < 0 || iy >= drawable->height)
  1147.     {
  1148.       black[0] = black[1] = black[2] = black[3] = 0;
  1149.       return black;
  1150.     }
  1151.  
  1152.   col  = ix / dinfo.tile_width;
  1153.   row  = iy / dinfo.tile_height;
  1154.   offx = ix % dinfo.tile_width;
  1155.   offy = iy % dinfo.tile_height;
  1156.  
  1157.   if (tk->tile == NULL || col != tk->col || row != tk->row)
  1158.     {
  1159.       if (tk->tile)
  1160.     gimp_tile_unref (tk->tile, tk->dirty);
  1161.       tk->col    = col;
  1162.       tk->row    = row;
  1163.       tk->tile    = gimp_drawable_get_tile (drawable, tk->shadow, row, col);
  1164.       tk->dirty = FALSE;
  1165.       gimp_tile_ref (tk->tile);
  1166.     }
  1167.  
  1168.   tk->dirty |= dirty;
  1169.   return tk->tile->data + (offy * tk->tile->ewidth + offx) * tk->tile->bpp;
  1170. }
  1171.  
  1172. static void
  1173. tile_keeper_free (TileKeeper *tk)
  1174. {
  1175.   if (tk->tile)
  1176.     gimp_tile_unref (tk->tile, tk->dirty);
  1177.   g_free (tk);
  1178. }
  1179.  
  1180.  
  1181. /*************************************************************************/
  1182. /**                                    **/
  1183. /**        +++ GFlare Routines                    **/
  1184. /**                                    **/
  1185. /*************************************************************************/
  1186.  
  1187. /*
  1188.  *    These code are more or less based on Quartic's gradient.c,
  1189.  *    other gimp sources, and script-fu.
  1190.  */
  1191.  
  1192. static GFlare *
  1193. gflare_new ()
  1194. {
  1195.   GFlare    *gflare;
  1196.  
  1197.   gflare = g_new0 (GFlare, 1);
  1198.   gflare->name = NULL;
  1199.   gflare->filename = NULL;
  1200.   return gflare;
  1201. }
  1202.  
  1203. GFlare *
  1204. gflare_new_with_default (gchar *new_name)
  1205. {
  1206.   DEBUG_PRINT (("gflare_new_with_default %s\n", new_name));
  1207.  
  1208.   return gflare_dup (&default_gflare, new_name);
  1209. }
  1210.  
  1211. GFlare *
  1212. gflare_dup (GFlare *src, gchar *new_name)
  1213. {
  1214.   GFlare    *dest;
  1215.  
  1216.   DEBUG_PRINT (("gflare_dup %s\n", new_name));
  1217.  
  1218.   dest = g_new0 (GFlare, 1);
  1219.  
  1220.   memcpy (dest, src, sizeof(GFlare));
  1221.  
  1222.   dest->name = g_strdup (new_name);
  1223.   dest->filename = NULL;
  1224.  
  1225.   return dest;
  1226. }
  1227.  
  1228. void
  1229. gflare_copy (GFlare *dest, GFlare *src)
  1230. {
  1231.   gchar *name, *filename;
  1232.  
  1233.   DEBUG_PRINT (("gflare_copy\n"));
  1234.  
  1235.   name = dest->name;
  1236.   filename = dest->filename;
  1237.  
  1238.   memcpy (dest, src, sizeof (GFlare));
  1239.  
  1240.   dest->name = name;
  1241.   dest->filename =filename;
  1242. }
  1243.  
  1244.  
  1245. static void
  1246. gflare_free (GFlare *gflare)
  1247. {
  1248.   g_assert (gflare != NULL);
  1249.  
  1250.   if (gflare->name)
  1251.     g_free (gflare->name);
  1252.   if (gflare->filename)
  1253.     g_free (gflare->filename);
  1254.   g_free (gflare);
  1255. }
  1256.  
  1257. GFlare *
  1258. gflare_load (char *filename, char *name)
  1259. {
  1260.   FILE        *fp;
  1261.   GFlareFile    *gf;
  1262.   GFlare    *gflare;
  1263.   gchar        header[256];
  1264.  
  1265.   DEBUG_PRINT (("gflare_load: %s, %s\n", filename, name));
  1266.   g_assert (filename != NULL);
  1267.  
  1268.   fp = fopen (filename, "r");
  1269.   if (!fp)
  1270.     {
  1271.       g_warning ("not found: %s", filename);
  1272.       return NULL;
  1273.     }
  1274.  
  1275.   if (fgets (header, sizeof(header), fp) == NULL
  1276.       || strcmp (header, GFLARE_FILE_HEADER) != 0)
  1277.     {
  1278.       g_warning ("not valid GFlare file: %s", filename);
  1279.       fclose (fp);
  1280.       return NULL;
  1281.     }
  1282.  
  1283.   gf = g_new (GFlareFile, 1);
  1284.   gf->fp = fp;
  1285.   gf->error = FALSE;
  1286.  
  1287.   gflare = gflare_new ();
  1288.   gflare->name = g_strdup (name);
  1289.   gflare->filename = g_strdup (filename);
  1290.  
  1291.   gflare_read_double   (&gflare->glow_opacity, gf);
  1292.   gflare_read_mode     (&gflare->glow_mode, gf);
  1293.   gflare_read_double   (&gflare->rays_opacity, gf);
  1294.   gflare_read_mode     (&gflare->rays_mode, gf);
  1295.   gflare_read_double   (&gflare->sflare_opacity, gf);
  1296.   gflare_read_mode     (&gflare->sflare_mode, gf);
  1297.  
  1298.   gflare_read_gradient_name (gflare->glow_radial, gf);
  1299.   gflare_read_gradient_name (gflare->glow_angular, gf);
  1300.   gflare_read_gradient_name (gflare->glow_angular_size, gf);
  1301.   gflare_read_double   (&gflare->glow_size, gf);
  1302.   gflare_read_double   (&gflare->glow_rotation, gf);
  1303.   gflare_read_double   (&gflare->glow_hue, gf);
  1304.  
  1305.   gflare_read_gradient_name (gflare->rays_radial, gf);
  1306.   gflare_read_gradient_name (gflare->rays_angular, gf);
  1307.   gflare_read_gradient_name (gflare->rays_angular_size, gf);
  1308.   gflare_read_double   (&gflare->rays_size, gf);
  1309.   gflare_read_double   (&gflare->rays_rotation, gf);
  1310.   gflare_read_double   (&gflare->rays_hue, gf);
  1311.   gflare_read_int      (&gflare->rays_nspikes, gf);
  1312.   gflare_read_double   (&gflare->rays_thickness, gf);
  1313.  
  1314.   gflare_read_gradient_name (gflare->sflare_radial, gf);
  1315.   gflare_read_gradient_name (gflare->sflare_sizefac, gf);
  1316.   gflare_read_gradient_name (gflare->sflare_probability, gf);
  1317.   gflare_read_double   (&gflare->sflare_size, gf);
  1318.   gflare_read_double   (&gflare->sflare_hue, gf);
  1319.   gflare_read_double   (&gflare->sflare_rotation, gf);
  1320.   gflare_read_shape    (&gflare->sflare_shape, gf);
  1321.   gflare_read_int      (&gflare->sflare_nverts, gf);
  1322.   gflare_read_int      (&gflare->sflare_seed, gf);
  1323.  
  1324.   fclose (gf->fp);
  1325.  
  1326.   if (gf->error)
  1327.     {
  1328.       g_warning ("invalid formatted GFlare file: %s\n", filename);
  1329.       g_free (gflare);
  1330.       g_free (gf);
  1331.       return NULL;
  1332.     }
  1333.  
  1334.   DEBUG_PRINT (("Loaded %s\n", filename));
  1335.   g_free (gf);
  1336.  
  1337.   return gflare;
  1338. }
  1339.  
  1340. static void
  1341. gflare_read_int (gint *intvar, GFlareFile *gf)
  1342. {
  1343.   if (gf->error)
  1344.     return;
  1345.  
  1346.   if (fscanf (gf->fp, "%d", intvar) != 1)
  1347.     gf->error = TRUE;
  1348. }
  1349.  
  1350. static void
  1351. gflare_read_double (gdouble *dblvar, GFlareFile *gf)
  1352. {
  1353.   if (gf->error)
  1354.     return;
  1355.  
  1356.   if (fscanf (gf->fp, "%le", dblvar) != 1)
  1357.     gf->error = TRUE;
  1358. }
  1359.  
  1360. static void
  1361. gflare_read_gradient_name (GradientName name, GFlareFile *gf)
  1362. {
  1363.   gchar        tmp[1024], dec[1024];
  1364.  
  1365.   if (gf->error)
  1366.     return;
  1367.  
  1368.   /* FIXME: this is buggy */
  1369.  
  1370.   if (fscanf (gf->fp, "%s", tmp) == 1)
  1371.     {
  1372.       /* @GRADIENT_NAME */
  1373.       gradient_name_decode ((guchar*) dec, (guchar*) tmp);
  1374.       gradient_name_copy (name, tmp);
  1375.       DEBUG_PRINT (("read_gradient_name: \"%s\" => \"%s\"\n", tmp, dec));
  1376.     }
  1377.   else
  1378.     gf->error = TRUE;
  1379. }
  1380.  
  1381. static void
  1382. gflare_read_shape (GFlareShape *shape, GFlareFile *gf)
  1383. {
  1384.   gchar tmp[1024];
  1385.   gint    i;
  1386.  
  1387.   if (gf->error)
  1388.     return;
  1389.  
  1390.   if (fscanf (gf->fp, "%s", tmp) == 1)
  1391.     {
  1392.       for (i = 0; i < GF_NUM_SHAPES; i++)
  1393.     if (strcmp (tmp, gflare_shapes[i]) == 0)
  1394.       {
  1395.         *shape = i;
  1396.         return;
  1397.       }
  1398.     }
  1399.   gf->error = TRUE;
  1400. }
  1401.  
  1402. static void
  1403. gflare_read_mode (GFlareMode *mode, GFlareFile *gf)
  1404. {
  1405.   gchar tmp[1024];
  1406.   gint    i;
  1407.  
  1408.   if (gf->error)
  1409.     return;
  1410.  
  1411.   if (fscanf (gf->fp, "%s", tmp) == 1)
  1412.     {
  1413.       for (i = 0; i < GF_NUM_MODES; i++)
  1414.     if (strcmp (tmp, gflare_modes[i]) == 0)
  1415.       {
  1416.         *mode = i;
  1417.         return;
  1418.       }
  1419.     }
  1420.   gf->error = TRUE;
  1421. }
  1422.  
  1423. void
  1424. gflare_save (GFlare *gflare)
  1425. {
  1426.   FILE    *fp;
  1427.   gchar *path;
  1428.   static int  message_ok = FALSE;
  1429.   static char *message =
  1430.     "GFlare `%s' is not saved.    If you add a new entry in gimprc, like:\n"
  1431.     "(gflare-path \"${gimp_dir}/gflare\")\n"
  1432.     "and make a directory ~/.gimp/gflare, then you can save your own GFlare's\n"
  1433.     "into that directory.";
  1434.  
  1435.   if (gflare->filename == NULL)
  1436.     {
  1437.       if (gflare_path_list == NULL)
  1438.     {
  1439.       if (!message_ok)
  1440.         g_message (message,
  1441.                gflare->name);
  1442.       message_ok = TRUE;
  1443.       return;
  1444.     }
  1445.  
  1446.       /* get first entry of path */
  1447.       path = gflare_path_list->data;
  1448.  
  1449.       gflare->filename = g_malloc (strlen (path) + strlen (gflare->name) + 2);
  1450.       sprintf (gflare->filename, "%s%s", path, gflare->name);
  1451.     }
  1452.  
  1453.   fp = fopen (gflare->filename, "w");
  1454.   if (!fp)
  1455.     {
  1456.       g_warning ("could not open \"%s\"", gflare->filename);
  1457.       return;
  1458.     }
  1459.  
  1460.   fprintf (fp, "%s", GFLARE_FILE_HEADER);
  1461.   fprintf (fp, "%f %s\n", gflare->glow_opacity, gflare_modes[gflare->glow_mode]);
  1462.   fprintf (fp, "%f %s\n", gflare->rays_opacity, gflare_modes[gflare->rays_mode]);
  1463.   fprintf (fp, "%f %s\n", gflare->sflare_opacity, gflare_modes[gflare->sflare_mode]);
  1464.  
  1465.   gflare_write_gradient_name (gflare->glow_radial, fp);
  1466.   gflare_write_gradient_name (gflare->glow_angular, fp);
  1467.   gflare_write_gradient_name (gflare->glow_angular_size, fp);
  1468.   fprintf (fp, "%f %f %f\n", gflare->glow_size, gflare->glow_rotation, gflare->glow_hue);
  1469.  
  1470.   gflare_write_gradient_name (gflare->rays_radial, fp);
  1471.   gflare_write_gradient_name (gflare->rays_angular, fp);
  1472.   gflare_write_gradient_name (gflare->rays_angular_size, fp);
  1473.   fprintf (fp, "%f %f %f\n", gflare->rays_size, gflare->rays_rotation, gflare->rays_hue);
  1474.   fprintf (fp, "%d %f\n", gflare->rays_nspikes, gflare->rays_thickness);
  1475.  
  1476.   gflare_write_gradient_name (gflare->sflare_radial, fp);
  1477.   gflare_write_gradient_name (gflare->sflare_sizefac, fp);
  1478.   gflare_write_gradient_name (gflare->sflare_probability, fp);
  1479.   fprintf (fp, "%f %f %f\n", gflare->sflare_size, gflare->sflare_rotation, gflare->sflare_hue);
  1480.   fprintf (fp, "%s %d %d\n", gflare_shapes[gflare->sflare_shape], gflare->sflare_nverts, gflare->sflare_seed);
  1481.  
  1482.   fclose (fp);
  1483.   DEBUG_PRINT (("Saved %s\n", gflare->filename));
  1484. }
  1485.  
  1486. static void
  1487. gflare_write_gradient_name (GradientName name, FILE *fp)
  1488. {
  1489.   gchar        enc[1024];
  1490.  
  1491.   /* @GRADIENT_NAME */
  1492.  
  1493.   /* encode white spaces and control characters (if any) */
  1494.   gradient_name_encode ((guchar*) enc, (guchar*) name);
  1495.  
  1496.   fprintf (fp, "%s\n", enc);
  1497.   DEBUG_PRINT (("write_gradient_name: \"%s\" => \"%s\"\n", name, enc));
  1498. }
  1499.  
  1500. void
  1501. gflare_name_copy (gchar *dest, gchar *src)
  1502. {
  1503.   strncpy (dest, src, GFLARE_NAME_MAX);
  1504.   dest[GFLARE_NAME_MAX-1] = '\0';
  1505. }
  1506.  
  1507.  
  1508. /*************************************************************************/
  1509. /**                                    **/
  1510. /**        +++ GFlares List                    **/
  1511. /**                                    **/
  1512. /*************************************************************************/
  1513.  
  1514. gint
  1515. gflares_list_insert (GFlare *gflare)
  1516. {
  1517.   GList        *tmp;
  1518.   GFlare    *g;
  1519.   int        n;
  1520.  
  1521.   /*
  1522.    *    Insert gflare in alphabetical order
  1523.    */
  1524.  
  1525.   n = 0;
  1526.   tmp = gflares_list;
  1527.  
  1528.   while (tmp) {
  1529.     g = tmp->data;
  1530.  
  1531.     if (strcmp (gflare->name, g->name) <= 0)
  1532.       break;
  1533.     n++;
  1534.     tmp = tmp->next;
  1535.   }
  1536.  
  1537.   num_gflares++;
  1538.   gflares_list = g_list_insert (gflares_list, gflare, n);
  1539.  
  1540.   DEBUG_PRINT (("gflares_list_insert %s => %d\n", gflare->name, n));
  1541.  
  1542.   return n;
  1543. }
  1544.  
  1545. GFlare *
  1546. gflares_list_lookup (gchar *name)
  1547. {
  1548.   GList        *tmp;
  1549.   GFlare    *gflare;
  1550.  
  1551.   DEBUG_PRINT (("gflares_list_lookup %s\n", name));
  1552.  
  1553.   tmp = gflares_list;
  1554.   while (tmp)
  1555.     {
  1556.       gflare = tmp->data;
  1557.       tmp = tmp->next;
  1558.       if (strcmp (gflare->name, name) == 0)
  1559.     return gflare;
  1560.     }
  1561.   return NULL;
  1562. }
  1563.  
  1564. gint
  1565. gflares_list_index (GFlare *gflare)
  1566. {
  1567.   GList        *tmp;
  1568.   gint        n;
  1569.  
  1570.   DEBUG_PRINT (("gflares_list_index %s\n", gflare->name));
  1571.  
  1572.   n = 0;
  1573.   tmp = gflares_list;
  1574.   while (tmp)
  1575.     {
  1576.       if (tmp->data == gflare)
  1577.     return n;
  1578.       tmp = tmp->next;
  1579.       n++;
  1580.     }
  1581.   return -1;
  1582. }
  1583.  
  1584. gint
  1585. gflares_list_remove (GFlare *gflare)
  1586. {
  1587.   GList        *tmp;
  1588.   gint        n;
  1589.  
  1590.   DEBUG_PRINT (("gflares_list_remove %s\n", gflare->name));
  1591.  
  1592.   n = 0;
  1593.   tmp = gflares_list;
  1594.   while (tmp)
  1595.     {
  1596.       if (tmp->data == gflare)
  1597.     {
  1598.       /* Found! */
  1599.       if (tmp->next == NULL)
  1600.       num_gflares--;
  1601.       gflares_list = g_list_remove (gflares_list, gflare);
  1602.       return n;
  1603.     }
  1604.       tmp = tmp->next;
  1605.       n++;
  1606.     }
  1607.   return -1;
  1608. }
  1609.  
  1610. /*
  1611.   Load all gflares, which are founded in gflare-path-list, into gflares_list.
  1612.  
  1613.   gflares-path-list must be initialized first. (plug_in_parse_gflare_path ())
  1614.  */
  1615. void
  1616. gflares_list_load_all ()
  1617. {
  1618.   GFlare    *gflare;
  1619.   GList        *list;
  1620.   gchar        *path;
  1621.   gchar        *filename;
  1622.   DIR        *dir;
  1623.   struct dirent *dir_ent;
  1624.   struct stat    filestat;
  1625.   gint        err;
  1626.  
  1627. #if 0    /* @@@ */
  1628.   printf("Waiting... (pid %d)\n", getpid());
  1629.   kill(getpid(), 19); /* SIGSTOP */
  1630. #endif
  1631.  
  1632.   /*  Make sure to clear any existing gflares  */
  1633.   gflares_list_free_all ();
  1634.  
  1635.   list = gflare_path_list;
  1636.   while (list)
  1637.     {
  1638.       path = list->data;
  1639.       list = list->next;
  1640.  
  1641.       /* Open directory */
  1642.       dir = opendir (path);
  1643.  
  1644.       if (!dir)
  1645.     g_warning("error reading GFlare directory \"%s\"", path);
  1646.       else
  1647.     {
  1648.       while ((dir_ent = readdir (dir)))
  1649.         {
  1650.           filename = g_malloc (strlen(path) + strlen (dir_ent->d_name) + 1);
  1651.  
  1652.           sprintf (filename, "%s%s", path, dir_ent->d_name);
  1653.  
  1654.           /* Check the file and see that it is not a sub-directory */
  1655.           err = stat (filename, &filestat);
  1656.  
  1657.           if (!err && S_ISREG (filestat.st_mode))
  1658.         {
  1659.           gflare = gflare_load (filename, dir_ent->d_name);
  1660.           if (gflare)
  1661.             gflares_list_insert (gflare);
  1662.         }
  1663.  
  1664.           g_free (filename);
  1665.         } /* while */
  1666.  
  1667.       closedir (dir);
  1668.     } /* else */
  1669.     }
  1670. }
  1671.  
  1672. void
  1673. gflares_list_free_all ()
  1674. {
  1675.   GList *list;
  1676.   GFlare *gflare;
  1677.  
  1678.   list = gflares_list;
  1679.   while (list)
  1680.     {
  1681.       gflare = (GFlare *) list->data;
  1682.       gflare_free (gflare);
  1683.       list = list->next;
  1684.     }
  1685.  
  1686.   g_list_free (gflares_list);
  1687.   gflares_list = NULL;
  1688. }
  1689.  
  1690.  
  1691.  
  1692. /*************************************************************************/
  1693. /**                                    **/
  1694. /**        +++ Calculator                        **/
  1695. /**                                    **/
  1696. /*************************************************************************/
  1697.  
  1698.  
  1699. /*
  1700.  * These routines calculates pixel values of particular gflare, at
  1701.  * specified point.  The client which wants to get benefit from these
  1702.  * calculation routines must call calc_init_params() first, and
  1703.  * iterate calling calc_init_progress() until it returns FALSE. and
  1704.  * must call calc_deinit() when job is done.
  1705.  */
  1706.  
  1707. void
  1708. calc_init_params (GFlare *gflare, gint calc_type,
  1709.           gdouble xcenter, gdouble ycenter,
  1710.           gdouble radius, gdouble rotation, gdouble hue,
  1711.           gdouble vangle, gdouble vlength)
  1712. {
  1713.   DEBUG_PRINT (("//// calc_init_params ////\n"));
  1714.   calc.type       = calc_type;
  1715.   calc.gflare       = gflare;
  1716.   calc.xcenter       = xcenter;
  1717.   calc.ycenter       = ycenter;
  1718.   calc.radius       = radius;
  1719.   calc.rotation       = rotation * M_PI / 180.0;
  1720.   calc.hue       = hue;
  1721.   calc.vangle       = vangle * M_PI / 180.0;
  1722.   calc.vlength       = radius * vlength / 100.0;
  1723.   calc.glow_radius   = radius * gflare->glow_size / 100.0;
  1724.   calc.rays_radius   = radius * gflare->rays_size / 100.0;
  1725.   calc.sflare_radius = radius * gflare->sflare_size / 100.0;
  1726.   calc.glow_rotation = (rotation + gflare->glow_rotation) * M_PI / 180.0;
  1727.   calc.rays_rotation = (rotation + gflare->rays_rotation) * M_PI / 180.0;
  1728.   calc.sflare_rotation = (rotation + gflare->sflare_rotation) * M_PI / 180.0;
  1729.   calc.glow_opacity  = gflare->glow_opacity * 255 / 100.0;
  1730.   calc.rays_opacity  = gflare->rays_opacity * 255 / 100.0;
  1731.   calc.sflare_opacity = gflare->sflare_opacity * 255 / 100.0;
  1732.  
  1733.   calc.glow_bounds.x0 = calc.xcenter - calc.glow_radius - 0.1;
  1734.   calc.glow_bounds.x1 = calc.xcenter + calc.glow_radius + 0.1;
  1735.   calc.glow_bounds.y0 = calc.ycenter - calc.glow_radius - 0.1;
  1736.   calc.glow_bounds.y1 = calc.ycenter + calc.glow_radius + 0.1;
  1737.   calc.rays_bounds.x0 = calc.xcenter - calc.rays_radius - 0.1;
  1738.   calc.rays_bounds.x1 = calc.xcenter + calc.rays_radius + 0.1;
  1739.   calc.rays_bounds.y0 = calc.ycenter - calc.rays_radius - 0.1;
  1740.   calc.rays_bounds.y1 = calc.ycenter + calc.rays_radius + 0.1;
  1741.  
  1742.   /* Thanks to Marcelo Malheiros for this algorithm */
  1743.   calc.rays_thinness = log (gflare->rays_thickness / 100.0) / log(0.8);
  1744.  
  1745.   calc.rays_spike_mod    = 1.0 / (2 * gflare->rays_nspikes);
  1746.  
  1747.   /*
  1748.     Initialize part of sflare
  1749.     The rest will be initialized in calc_sflare()
  1750.    */
  1751.   calc.sflare_list = NULL;
  1752.   calc.sflare_shape = gflare->sflare_shape;
  1753.   if (calc.sflare_shape == GF_POLYGON)
  1754.     {
  1755.       calc.sflare_angle = 2 * M_PI / (2 * gflare->sflare_nverts);
  1756.       calc.sflare_factor = 1.0 / cos (calc.sflare_angle);
  1757.     }
  1758.  
  1759.   calc.glow_radial     = NULL;
  1760.   calc.glow_angular     = NULL;
  1761.   calc.glow_angular_size = NULL;
  1762.   calc.rays_radial     = NULL;
  1763.   calc.rays_angular     = NULL;
  1764.   calc.rays_angular_size = NULL;
  1765.   calc.sflare_radial     = NULL;
  1766.   calc.sflare_sizefac     = NULL;
  1767.   calc.sflare_probability = NULL;
  1768.  
  1769.   calc.init = TRUE;
  1770. }
  1771.  
  1772. int
  1773. calc_init_progress ()
  1774. {
  1775.   if (calc_sample_one_gradient ())
  1776.     return TRUE;
  1777.   calc_place_sflare ();
  1778.   return FALSE;
  1779. }
  1780.  
  1781. /*
  1782.    Store samples of gradient into an array
  1783.    this routine is called during Calc initialization
  1784.    this code is very messy... :( */
  1785. static int
  1786. calc_sample_one_gradient ()
  1787. {
  1788.   static struct {
  1789.     guchar    **values;
  1790.     gint    name_offset;
  1791.     gint    hue_offset;
  1792.     gint    gray;
  1793.   } table[] = {
  1794.     { &calc.glow_radial, OFFSETOF (GFlare, glow_radial), OFFSETOF (GFlare, glow_hue), FALSE },
  1795.     { &calc.glow_angular, OFFSETOF (GFlare, glow_angular), 0, FALSE },
  1796.     { &calc.glow_angular_size, OFFSETOF (GFlare, glow_angular_size), 0, TRUE },
  1797.     { &calc.rays_radial, OFFSETOF (GFlare, rays_radial), OFFSETOF (GFlare, rays_hue), FALSE },
  1798.     { &calc.rays_angular, OFFSETOF (GFlare, rays_angular), 0, FALSE },
  1799.     { &calc.rays_angular_size, OFFSETOF (GFlare, rays_angular_size), 0, TRUE },
  1800.     { &calc.sflare_radial, OFFSETOF (GFlare, sflare_radial), OFFSETOF (GFlare, sflare_hue), FALSE },
  1801.     { &calc.sflare_sizefac, OFFSETOF (GFlare, sflare_sizefac), 0, TRUE },
  1802.     { &calc.sflare_probability, OFFSETOF (GFlare, sflare_probability), 0, TRUE },
  1803.   };
  1804.   GFlare    *gflare = calc.gflare;
  1805.   GradientName    *grad_name;
  1806.   guchar    *gradient;
  1807.   gdouble    hue_deg;
  1808.   gint        i, j, hue;
  1809.  
  1810.   for (i = 0; i < sizeof (table) / sizeof (table[0]); i++)
  1811.     {
  1812.       if (*(table[i].values) == NULL)
  1813.     {
  1814.       /* @GRADIENT_NAME */
  1815.       grad_name = (GradientName *) ((char*) gflare + table[i].name_offset);
  1816.       gradient = *(table[i].values) = g_new (guchar, 4 * GRADIENT_RESOLUTION);
  1817.       gradient_get_values (*grad_name, gradient, GRADIENT_RESOLUTION);
  1818.  
  1819.       /*
  1820.        * Do hue rotation, if needed
  1821.        */
  1822.  
  1823.       if (table[i].hue_offset != 0)
  1824.         {
  1825.           hue_deg = calc.hue + *(gdouble *) ((char*) gflare + table[i].hue_offset);
  1826.           hue = (gint) (hue_deg / 360.0 * 256.0) % 256;
  1827.           if (hue < 0)
  1828.         hue += 256;
  1829.           g_assert (0 <= hue && hue < 256);
  1830.  
  1831.           if (hue > 0)
  1832.         {
  1833.           for (j = 0; j < GRADIENT_RESOLUTION; j++)
  1834.             {
  1835.               gint    r, g, b;
  1836.  
  1837.               r = gradient[j*4];
  1838.               g = gradient[j*4+1];
  1839.               b = gradient[j*4+2];
  1840.  
  1841.               rgb_to_hsv (&r, &g, &b);
  1842.               r = (r + hue) % 256;
  1843.               hsv_to_rgb (&r, &g, &b);
  1844.  
  1845.               gradient[j*4] = r;
  1846.               gradient[j*4+1] = g;
  1847.               gradient[j*4+2] = b;
  1848.             }
  1849.         }
  1850.         }
  1851.  
  1852.       /*
  1853.        *    Grayfy gradient, if needed
  1854.        */
  1855.  
  1856.       if (table[i].gray)
  1857.         {
  1858.           for (j = 0; j < GRADIENT_RESOLUTION; j++)
  1859.         /* the first byte is enough */
  1860.         gradient[j*4] = LUMINOSITY ((gradient + j*4));
  1861.  
  1862.         }
  1863.  
  1864.       /* sampling of one gradient is done */
  1865.       return TRUE;
  1866.     }
  1867.     }
  1868.   return FALSE;
  1869. }
  1870.  
  1871. static void
  1872. calc_place_sflare ()
  1873. {
  1874.   GFlare    *gflare;
  1875.   CalcSFlare    *sflare;
  1876.   gdouble    prob[GRADIENT_RESOLUTION];
  1877.   gdouble    sum, sum2;
  1878.   gdouble    pos;
  1879.   gdouble    rnd, sizefac;
  1880.   int        n;
  1881.   int        i;
  1882.  
  1883.   if ((calc.type & CALC_SFLARE) == 0)
  1884.     return;
  1885.  
  1886.   DEBUG_PRINT (("calc_place_sflare\n"));
  1887.  
  1888.   gflare = calc.gflare;
  1889.  
  1890.   /*
  1891.     Calc cumulative probability
  1892.     */
  1893.  
  1894.   sum = 0.0;
  1895.   for (i = 0; i < GRADIENT_RESOLUTION; i++)
  1896.     {
  1897.       /* probability gradient was grayfied already */
  1898.       prob[i] = calc.sflare_probability[i*4];
  1899.       sum += prob[i];
  1900.     }
  1901.  
  1902.   if (sum == 0.0)
  1903.     sum = 1.0;
  1904.  
  1905.   sum2 = 0;
  1906.   for (i = 0; i < GRADIENT_RESOLUTION; i++)
  1907.     {
  1908.       sum2 += prob[i];        /* cumulation */
  1909.       prob[i] = sum2 / sum;
  1910.     }
  1911.  
  1912.   if (gflare->sflare_seed == -1)
  1913.     srand (time (NULL));
  1914.   else
  1915.     srand (gflare->sflare_seed);
  1916.  
  1917.   for (n = 0; n < SFLARE_NUM; n++)
  1918.     {
  1919.       sflare = g_new (CalcSFlare, 1);
  1920.       rnd = (double) rand () / RAND_MAX;
  1921.       for (i = 0; i < GRADIENT_RESOLUTION; i++)
  1922.     if (prob[i] >= rnd)
  1923.       break;
  1924.       if (i >= GRADIENT_RESOLUTION)
  1925.     i = GRADIENT_RESOLUTION - 1;
  1926.  
  1927.       /* sizefac gradient was grayfied already */
  1928.       sizefac = calc.sflare_sizefac[i*4] / 255.0;
  1929.       sizefac = pow (sizefac, 5.0);
  1930.  
  1931.       pos = (double) (i - GRADIENT_RESOLUTION / 2) / GRADIENT_RESOLUTION;
  1932.       sflare->xcenter = calc.xcenter + cos (calc.vangle) * calc.vlength * pos;
  1933.       sflare->ycenter = calc.ycenter - sin (calc.vangle) * calc.vlength * pos;
  1934.       sflare->radius = sizefac * calc.sflare_radius; /* FIXME */
  1935.       sflare->bounds.x0 = sflare->xcenter - sflare->radius - 1;
  1936.       sflare->bounds.x1 = sflare->xcenter + sflare->radius + 1;
  1937.       sflare->bounds.y0 = sflare->ycenter - sflare->radius - 1;
  1938.       sflare->bounds.y1 = sflare->ycenter + sflare->radius + 1;
  1939.       calc.sflare_list = g_list_append (calc.sflare_list, sflare);
  1940.     }
  1941. }
  1942.  
  1943.  
  1944. void
  1945. calc_deinit ()
  1946. {
  1947.   GList        *list;
  1948.  
  1949.   DEBUG_PRINT (("\\\\\\\\ calc_deinit \\\\\\\\ \n"));
  1950.  
  1951.   if (!calc.init)
  1952.     {
  1953.       g_warning("calc_deinit: not initialized");
  1954.       return;
  1955.     }
  1956.  
  1957.   list = calc.sflare_list;
  1958.   while (list)
  1959.     {
  1960.       g_free (list->data);
  1961.       list = list->next;
  1962.     }
  1963.   g_list_free (calc.sflare_list);
  1964.  
  1965.   g_free (calc.glow_radial);
  1966.   g_free (calc.glow_angular);
  1967.   g_free (calc.glow_angular_size);
  1968.   g_free (calc.rays_radial);
  1969.   g_free (calc.rays_angular);
  1970.   g_free (calc.rays_angular_size);
  1971.   g_free (calc.sflare_radial);
  1972.   g_free (calc.sflare_sizefac);
  1973.   g_free (calc.sflare_probability);
  1974.  
  1975.   calc.init = FALSE;
  1976. }
  1977.  
  1978. /*
  1979.  *  Get sample value at specified position of a gradient
  1980.  *
  1981.  *  gradient samples are stored into array at the time of
  1982.  *  calc_sample_one_gradients (), and it is now linear interpolated.
  1983.  *
  1984.  *  INPUT:
  1985.  *    guchar    gradient[4*GRADIENT_RESOLUTION]        gradient array(RGBA)
  1986.  *    gdouble pos                    position (0<=pos<=1)
  1987.  *  OUTPUT:
  1988.  *    guchar    pix[4]
  1989.  */
  1990. static void
  1991. calc_get_gradient(guchar *pix, guchar *gradient, gdouble pos)
  1992. {
  1993.   gint        ipos;
  1994.   gdouble    frac;
  1995.   gint        i;
  1996.  
  1997.   if (pos < 0 || pos > 1)
  1998.     {
  1999.       pix[0] = pix[1] = pix[2] = pix[3] = 0;
  2000.       return;
  2001.     }
  2002.   pos *= GRADIENT_RESOLUTION - 1.0001;
  2003.   ipos = (gint) pos;    frac = pos - ipos;
  2004.   gradient += ipos * 4;
  2005.  
  2006.   for (i = 0; i < 4; i++)
  2007.     {
  2008.       pix[i] = gradient[i] * (1 - frac) + gradient[i+4] * frac;
  2009.     }
  2010. }
  2011.  
  2012. /* I need fmod to return always positive value */
  2013. static gdouble
  2014. fmod_positive (gdouble x, gdouble m)
  2015. {
  2016.   return x - floor (x/m) * m;
  2017. }
  2018.  
  2019. /*
  2020.  *  Calc glow's pixel (RGBA) value
  2021.  *  INPUT:
  2022.  *    gdouble x, y            image coordinates
  2023.  *  OUTPUT:
  2024.  *    guchar    pix[4]
  2025.  */
  2026. void
  2027. calc_glow_pix (guchar *dest_pix, gdouble x, gdouble y)
  2028. {
  2029.   gdouble radius, angle;
  2030.   gdouble angular_size;
  2031.   guchar  radial_pix[4], angular_pix[4], size_pix[4];
  2032.   gint      i;
  2033.  
  2034.   if ((calc.type & CALC_GLOW) == 0
  2035.       || x < calc.glow_bounds.x0 || x > calc.glow_bounds.x1
  2036.       || y < calc.glow_bounds.y0 || y > calc.glow_bounds.y1)
  2037.     {
  2038.       memset (dest_pix, 0, 4);
  2039.       return;
  2040.     }
  2041.  
  2042.   x -= calc.xcenter;
  2043.   y -= calc.ycenter;
  2044.   radius = sqrt (x*x + y*y) / calc.glow_radius;
  2045.   angle = (atan2 (-y, x) + calc.glow_rotation ) / (2*M_PI);
  2046.   angle = fmod_positive (angle, 1.0);
  2047.  
  2048.   calc_get_gradient (size_pix, calc.glow_angular_size, angle);
  2049.   /* angular_size gradient was grayfied already */
  2050.   angular_size = size_pix[0] / 255.0;
  2051.   radius /= (angular_size+0.0001);    /* in case angular_size == 0.0 */
  2052.   if( radius < 0 || radius > 1 )
  2053.     {
  2054.       memset (dest_pix, 0, 4);
  2055.       return;
  2056.     }
  2057.  
  2058.   calc_get_gradient (radial_pix, calc.glow_radial, radius);
  2059.   calc_get_gradient (angular_pix, calc.glow_angular, angle);
  2060.  
  2061.   for (i = 0; i < 4; i++)
  2062.     dest_pix[i] = radial_pix[i] * angular_pix[i] / 255;
  2063. }
  2064.  
  2065. /*
  2066.  *  Calc rays's pixel (RGBA) value
  2067.  *
  2068.  */
  2069. void
  2070. calc_rays_pix (guchar *dest_pix, gdouble x, gdouble y)
  2071. {
  2072.   gdouble radius, angle;
  2073.   gdouble angular_size;
  2074.   gdouble spike_frac, spike_inten, spike_angle;
  2075.   guchar  radial_pix[4], angular_pix[4], size_pix[4];
  2076.   gint      i;
  2077.  
  2078.   if ((calc.type & CALC_RAYS) == 0
  2079.       || x < calc.rays_bounds.x0 || x > calc.rays_bounds.x1
  2080.       || y < calc.rays_bounds.y0 || y > calc.rays_bounds.y1)
  2081.     {
  2082.       memset (dest_pix, 0, 4);
  2083.       return;
  2084.     }
  2085.  
  2086.   x -= calc.xcenter;
  2087.   y -= calc.ycenter;
  2088.   radius = sqrt (x*x + y*y) / calc.rays_radius;
  2089.   angle = (atan2 (-y, x) + calc.rays_rotation ) / (2*M_PI);
  2090.   angle = fmod_positive (angle, 1.0);    /* make sure 0 <= angle < 1.0 */
  2091.   spike_frac = fmod (angle, calc.rays_spike_mod * 2);
  2092.   spike_angle = angle - spike_frac + calc.rays_spike_mod;
  2093.   spike_frac = (angle - spike_angle) / calc.rays_spike_mod;
  2094.   /* spike_frac is between -1.0 and 1.0 here (except round error...) */
  2095.  
  2096.   spike_inten = pow (1.0 - fabs (spike_frac), calc.rays_thinness);
  2097.  
  2098.   calc_get_gradient (size_pix, calc.rays_angular_size, spike_angle);
  2099.   /* angular_size gradient was grayfied already */
  2100.   angular_size = size_pix[0] / 255.0;
  2101.   radius /= (angular_size+0.0001);    /* in case angular_size == 0.0 */
  2102.   if( radius < 0 || radius > 1 )
  2103.     {
  2104.       memset (dest_pix, 0, 4);
  2105.       return;
  2106.     }
  2107.  
  2108.   calc_get_gradient (radial_pix, calc.rays_radial, radius);
  2109.   calc_get_gradient (angular_pix, calc.rays_angular, spike_angle);
  2110.  
  2111.   for (i = 0; i < 3; i++)
  2112.     dest_pix[i] =  radial_pix[i] * angular_pix[i] / 255;
  2113.   dest_pix[3] = spike_inten * radial_pix[3] * angular_pix[3] / 255;
  2114.  
  2115. }
  2116.  
  2117.  
  2118. /*
  2119.  *  Calc sflare's pixel (RGBA) value
  2120.  *
  2121.  *  the sflare (second flares) are needed to be rendered one each
  2122.  *  sequencially, onto the source image, such as like usual layer
  2123.  *  operations. So the function takes src_pix as argment.  glow, rays
  2124.  *  routines don't have src_pix as argment, because of convienience.
  2125.  *
  2126.  *  @JAPANESE
  2127.  *  sflare $B$OJ#?t$N%U%l%"$r=g$K(B($B%l%$%dE*$K(B)$B$+$V$;$J$,$iIA2h$9$kI,MW$,(B
  2128.  *  $B$"$k$N$G!"$3$l$@$1(B src_pix $B$r0z?t$K$H$C$F(B paint_func $B$rE,MQ$9$k!#(B
  2129.  *  glow, rays $B$O4J0W2=$N$?$a$K$J$7!#(B
  2130.  */
  2131. void
  2132. calc_sflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix)
  2133. {
  2134.   GList        *list;
  2135.   CalcSFlare    *sflare;
  2136.   gdouble    sx, sy, th;
  2137.   gdouble    radius, angle;
  2138.   guchar    radial_pix[4], tmp_pix[4];
  2139.  
  2140.   memcpy (dest_pix, src_pix, 4);
  2141.  
  2142.   if ((calc.type & CALC_SFLARE) == 0)
  2143.     return;
  2144.  
  2145.   list = calc.sflare_list;
  2146.   while (list)
  2147.     {
  2148.       sflare = list->data;
  2149.       list = list->next;
  2150.  
  2151.       if (x < sflare->bounds.x0 || x > sflare->bounds.x1
  2152.       || y < sflare->bounds.y0 || y > sflare->bounds.y1)
  2153.     continue;
  2154.       sx = x - sflare->xcenter;
  2155.       sy = y - sflare->ycenter;
  2156.       radius = sqrt (sx * sx + sy * sy) / sflare->radius;
  2157.       if (calc.sflare_shape == GF_POLYGON)
  2158.     {
  2159.       angle = atan2 (-sy, sx) - calc.vangle + calc.sflare_rotation;
  2160.       th = fmod_positive (angle, calc.sflare_angle * 2) - calc.sflare_angle;
  2161.       radius *= cos (th) * calc.sflare_factor;
  2162.     }
  2163.       if (radius < 0 || radius > 1)
  2164.     continue;
  2165.  
  2166.       calc_get_gradient (radial_pix, calc.sflare_radial, radius);
  2167.       memcpy (tmp_pix, dest_pix, 4);
  2168.       calc_paint_func (dest_pix, tmp_pix, radial_pix,
  2169.                calc.sflare_opacity, calc.gflare->sflare_mode);
  2170.     }
  2171. }
  2172.  
  2173.  
  2174.  
  2175. void
  2176. calc_gflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix)
  2177. {
  2178.   GFlare    *gflare = calc.gflare;
  2179.   guchar    glow_pix[4], rays_pix[4];
  2180.   guchar    tmp_pix[4];
  2181.  
  2182.   memcpy (dest_pix, src_pix, 4);
  2183.  
  2184.   if (calc.type & CALC_GLOW)
  2185.     {
  2186.       memcpy (tmp_pix, dest_pix, 4);
  2187.       calc_glow_pix (glow_pix, x, y);
  2188.       calc_paint_func (dest_pix, tmp_pix, glow_pix,
  2189.                calc.glow_opacity, gflare->glow_mode);
  2190.     }
  2191.   if (calc.type & CALC_RAYS)
  2192.     {
  2193.       memcpy (tmp_pix, dest_pix, 4);
  2194.       calc_rays_pix (rays_pix, x, y);
  2195.       calc_paint_func (dest_pix, tmp_pix, rays_pix,
  2196.                calc.rays_opacity, gflare->rays_mode);
  2197.     }
  2198.   if (calc.type & CALC_SFLARE)
  2199.     {
  2200.       memcpy (tmp_pix, dest_pix, 4);
  2201.       calc_sflare_pix (dest_pix, x, y, tmp_pix);
  2202.     }
  2203. }
  2204.  
  2205. /*
  2206.     Paint func routines, such as Normal, Addition, ...
  2207.  */
  2208. static void
  2209. calc_paint_func (guchar *dest, guchar *src1, guchar *src2, gint opacity,
  2210.          GFlareMode mode)
  2211. {
  2212.   guchar    buf[4], *s=buf;
  2213.  
  2214.   if (src2[3] == 0 || opacity <= 0)
  2215.     {
  2216.       memcpy (dest, src1, 4);
  2217.       return;
  2218.     }
  2219.  
  2220.   switch (mode)
  2221.     {
  2222.     case GF_NORMAL:
  2223.       s = src2;
  2224.       break;
  2225.     case GF_ADDITION:
  2226.       calc_addition (s, src1, src2);
  2227.       break;
  2228.     case GF_OVERLAY:
  2229.       calc_overlay (s, src1, src2);
  2230.       break;
  2231.     case GF_SCREEN:
  2232.       calc_screen (s, src1, src2);
  2233.       break;
  2234.     default:
  2235.       s = src2;
  2236.       break;
  2237.     }
  2238.   calc_combine (dest, src1, s, opacity);
  2239. }
  2240.  
  2241. static void
  2242. calc_combine (guchar *dest, guchar *src1, guchar *src2, gint opacity)
  2243. {
  2244.   gdouble    s1_a, s2_a, new_a;
  2245.   gdouble    ratio, compl_ratio;
  2246.   gint        i;
  2247.  
  2248.   s1_a = src1[3] / 255.0;
  2249.   s2_a = src2[3] * opacity / 65025.0;
  2250.   new_a     = s1_a + (1.0 - s1_a) * s2_a;
  2251.  
  2252.   if (new_a != 0.0)
  2253.     ratio = s2_a / new_a;
  2254.   else
  2255.     ratio = 0.0;
  2256.  
  2257.   compl_ratio = 1.0 - ratio;
  2258.  
  2259.   for (i = 0; i < 3; i++)
  2260.     dest[i] = src1[i] * compl_ratio + src2[i] * ratio;
  2261.  
  2262.   dest[3] = new_a * 255.0;
  2263. }
  2264.  
  2265. static void
  2266. calc_addition (guchar *dest, guchar *src1, guchar *src2)
  2267. {
  2268.   gint        tmp, i;
  2269.  
  2270.   for (i = 0; i < 3; i++)
  2271.     {
  2272.       tmp = src1[i] + src2[i];
  2273.       dest[i] = tmp <= 255 ? tmp: 255;
  2274.     }
  2275.   dest[3] = MIN (src1[3], src2[3]);
  2276. }
  2277.  
  2278. static void
  2279. calc_screen (guchar *dest, guchar *src1, guchar *src2)
  2280. {
  2281.   gint        i;
  2282.  
  2283.   for (i = 0; i < 3; i++)
  2284.     {
  2285.       dest[i] = 255 - ((255 - src1[i]) * (255 - src2[i])) / 255;
  2286.     }
  2287.   dest[3] = MIN (src1[3], src2[3]);
  2288. }
  2289.  
  2290. static void
  2291. calc_overlay (guchar *dest, guchar *src1, guchar *src2)
  2292. {
  2293.   gint        screen, mult, i;
  2294.  
  2295.   for (i = 0; i < 3; i++)
  2296.     {
  2297.       screen = 255 - ((255 - src1[i]) * (255 - src2[i])) / 255;
  2298.       mult = (src1[i] * src2[i]) / 255;
  2299.       dest[i] = (screen * src1[i] + mult * (255 - src1[i])) / 255;
  2300.     }
  2301.   dest[3] = MIN (src1[3], src2[3]);
  2302. }
  2303.  
  2304. /*
  2305.     HSV routines, needed for Hue Rotation
  2306.     taken from app/paint_funcs.c
  2307.     And I don't know what's the difference between HSV and HSL.
  2308.  */
  2309. static void
  2310. rgb_to_hsv (int *r,
  2311.         int *g,
  2312.         int *b)
  2313. {
  2314.   int red, green, blue;
  2315.   float h, s, v;
  2316.   int min, max;
  2317.   int delta;
  2318.  
  2319.   red = *r;
  2320.   green = *g;
  2321.   blue = *b;
  2322.  
  2323.   if (red > green)
  2324.     {
  2325.       if (red > blue)
  2326.     max = red;
  2327.       else
  2328.     max = blue;
  2329.  
  2330.       if (green < blue)
  2331.     min = green;
  2332.       else
  2333.     min = blue;
  2334.     }
  2335.   else
  2336.     {
  2337.       if (green > blue)
  2338.     max = green;
  2339.       else
  2340.     max = blue;
  2341.  
  2342.       if (red < blue)
  2343.     min = red;
  2344.       else
  2345.     min = blue;
  2346.     }
  2347.  
  2348.   v = max;
  2349.  
  2350.   if (max != 0)
  2351.     s = ((max - min) * 255) / (float) max;
  2352.   else
  2353.     s = 0;
  2354.  
  2355.   if (s == 0)
  2356.     h = 0;
  2357.   else
  2358.     {
  2359.       delta = max - min;
  2360.       if (red == max)
  2361.     h = (green - blue) / (float) delta;
  2362.       else if (green == max)
  2363.     h = 2 + (blue - red) / (float) delta;
  2364.       else if (blue == max)
  2365.     h = 4 + (red - green) / (float) delta;
  2366.       h *= 42.5;
  2367.  
  2368.       if (h < 0)
  2369.     h += 255;
  2370.       if (h > 255)
  2371.     h -= 255;
  2372.     }
  2373.  
  2374.   *r = h;
  2375.   *g = s;
  2376.   *b = v;
  2377. }
  2378.  
  2379. static void
  2380. hsv_to_rgb (int *h,
  2381.         int *s,
  2382.         int *v)
  2383. {
  2384.   float hue, saturation, value;
  2385.   float f, p, q, t;
  2386.  
  2387.   if (*s == 0)
  2388.     {
  2389.       *h = *v;
  2390.       *s = *v;
  2391.       *v = *v;
  2392.     }
  2393.   else
  2394.     {
  2395.       hue = *h * 6.0 / 255.0;
  2396.       saturation = *s / 255.0;
  2397.       value = *v / 255.0;
  2398.  
  2399.       f = hue - (int) hue;
  2400.       p = value * (1.0 - saturation);
  2401.       q = value * (1.0 - (saturation * f));
  2402.       t = value * (1.0 - (saturation * (1.0 - f)));
  2403.  
  2404.       switch ((int) hue)
  2405.     {
  2406.     case 0:
  2407.       *h = value * 255;
  2408.       *s = t * 255;
  2409.       *v = p * 255;
  2410.       break;
  2411.     case 1:
  2412.       *h = q * 255;
  2413.       *s = value * 255;
  2414.       *v = p * 255;
  2415.       break;
  2416.     case 2:
  2417.       *h = p * 255;
  2418.       *s = value * 255;
  2419.       *v = t * 255;
  2420.       break;
  2421.     case 3:
  2422.       *h = p * 255;
  2423.       *s = q * 255;
  2424.       *v = value * 255;
  2425.       break;
  2426.     case 4:
  2427.       *h = t * 255;
  2428.       *s = p * 255;
  2429.       *v = value * 255;
  2430.       break;
  2431.     case 5:
  2432.       *h = value * 255;
  2433.       *s = p * 255;
  2434.       *v = q * 255;
  2435.       break;
  2436.     }
  2437.     }
  2438. }
  2439.  
  2440. /*************************************************************************/
  2441. /**                                    **/
  2442. /**            Main Dialog                    **/
  2443. /**            +++ dlg                        **/
  2444. /**                                    **/
  2445. /*************************************************************************/
  2446.  
  2447. /*
  2448.     This is gflare main dialog, one which opens in first.
  2449.  */
  2450.  
  2451. int
  2452. dlg_run ()
  2453. {
  2454.   GtkWidget    *shell;
  2455.   GtkWidget    *button;
  2456.   GtkWidget    *table;
  2457.   GtkWidget    *prv_frame;
  2458.   GtkWidget    *notebook;
  2459.   guchar    *color_cube;
  2460.   gchar        **argv;
  2461.   gint        argc;
  2462.  
  2463.   /*
  2464.    *    Init Gdk & Gtk stuff
  2465.    */
  2466.   argc = 1;
  2467.   argv = g_new (gchar *, 1);
  2468.   argv[0] = g_strdup ("gflare");
  2469.  
  2470.   gtk_init (&argc, &argv);
  2471.   gtk_rc_parse (gimp_gtkrc ());
  2472.  
  2473.   gdk_set_use_xshm (gimp_use_xshm ());
  2474.   gtk_preview_set_gamma (gimp_gamma ());
  2475.   gtk_preview_set_install_cmap (gimp_install_cmap ());
  2476.   color_cube = gimp_color_cube ();
  2477.   gtk_preview_set_color_cube (color_cube[0], color_cube[1],
  2478.                   color_cube[2], color_cube[3]);
  2479.  
  2480.   gtk_widget_set_default_visual (gtk_preview_get_visual ());
  2481.   gtk_widget_set_default_colormap (gtk_preview_get_cmap ());
  2482.  
  2483.   /* gdk_set_show_events (TRUE);
  2484.      gdk_set_debug_level (3); */
  2485.  
  2486.   /*
  2487.    *    Init Main Dialog
  2488.    */
  2489.  
  2490. #if 0        /* @@@ debug stuff */
  2491.   printf("Waiting... (pid %d)\n", getpid());
  2492.   kill(getpid(), 19); /* SIGSTOP */
  2493. #endif
  2494.  
  2495.   pint.run = FALSE;
  2496.   dlg = g_new (GFlareDialog, 1);
  2497.   dlg->init = TRUE;
  2498.   dlg->update_preview = TRUE;
  2499.  
  2500.   gradient_menu_init(); /* FIXME: this should go elsewhere  */
  2501.   dlg_setup_gflare ();
  2502.  
  2503.   g_assert (gflares_list != NULL);
  2504.   g_assert (dlg->gflare != NULL);
  2505.   g_assert (dlg->gflare->name != NULL);
  2506.  
  2507.   /*
  2508.    *    Dialog Shell
  2509.    */
  2510.   shell = dlg->shell = gtk_dialog_new ();
  2511.   /*  gtk_widget_set_name (dlg, "gflare"); */
  2512.   gtk_window_set_title (GTK_WINDOW (shell), "GFlare");
  2513.   gtk_window_position (GTK_WINDOW (shell), GTK_WIN_POS_MOUSE);
  2514.   gtk_signal_connect (GTK_OBJECT (shell), "destroy",
  2515.               (GtkSignalFunc) dlg_close_callback,
  2516.               NULL);
  2517.  
  2518.   /*
  2519.    *    Action area
  2520.    */
  2521.   button = gtk_button_new_with_label ("OK");
  2522.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  2523.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  2524.               (GtkSignalFunc) dlg_ok_callback,
  2525.               shell);
  2526.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->action_area), button, TRUE, TRUE, 0);
  2527.   gtk_widget_grab_default (button);
  2528.   gtk_widget_show (button);
  2529.  
  2530.   button = gtk_button_new_with_label ("Cancel");
  2531.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  2532.   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
  2533.                  (GtkSignalFunc) gtk_widget_destroy,
  2534.                  GTK_OBJECT (shell));
  2535.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->action_area), button, TRUE, TRUE, 0);
  2536.   gtk_widget_show (button);
  2537.  
  2538.   button = gtk_button_new_with_label ("Rescan Gradients");
  2539.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  2540.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  2541.               (GtkSignalFunc) dlg_rescan_callback,
  2542.               NULL);
  2543.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->action_area),
  2544.               button, TRUE, TRUE, 0);
  2545.   gtk_widget_show (button);
  2546.  
  2547.   /*
  2548.    *    Preview
  2549.    */
  2550.  
  2551.   dlg->preview = preview_new (DLG_PREVIEW_WIDTH, DLG_PREVIEW_HEIGHT,
  2552.                   dlg_preview_init_func, NULL,
  2553.                   dlg_preview_render_func, NULL,
  2554.                   dlg_preview_deinit_func, NULL);
  2555.  
  2556.   gtk_widget_set_events (GTK_WIDGET (dlg->preview->widget), DLG_PREVIEW_MASK);
  2557.   gtk_signal_connect (GTK_OBJECT(dlg->preview->widget), "event",
  2558.               (GtkSignalFunc) dlg_preview_handle_event,
  2559.               NULL);
  2560.   dlg_preview_calc_window ();
  2561.  
  2562.   prv_frame = gtk_frame_new (NULL);
  2563.   gtk_frame_set_shadow_type (GTK_FRAME (prv_frame), GTK_SHADOW_IN);
  2564.   gtk_container_add (GTK_CONTAINER (prv_frame), dlg->preview->widget);
  2565.   gtk_widget_show (prv_frame);
  2566.  
  2567.   /*
  2568.    *    Notebook
  2569.    */
  2570.  
  2571.   notebook = dlg->notebook = gtk_notebook_new ();
  2572.   gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
  2573.  
  2574.   dlg_make_page_settings (dlg, notebook);
  2575.   dlg_make_page_selector (dlg, notebook);
  2576.   gtk_widget_show (notebook);
  2577.  
  2578.   /*
  2579.    *    pack them
  2580.    */
  2581.  
  2582.   table = gtk_table_new (1, 2, FALSE);
  2583.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  2584.   gtk_table_attach (GTK_TABLE (table), prv_frame, 0, 1, 0, 1,
  2585.             0, 0, 0, 0);
  2586.   gtk_table_attach (GTK_TABLE (table), notebook, 1, 2, 0, 1,
  2587.             GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
  2588.   gtk_widget_show (table);
  2589.  
  2590.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->vbox), table, TRUE, TRUE, 0);
  2591.   DEBUG_PRINT (("shell is shown\n"));
  2592.  
  2593.   gtk_widget_show (shell);
  2594.  
  2595.   /*
  2596.    *    Make sure the selector page is realized
  2597.    *    This idea is from app/layers_dialog.c
  2598.    */
  2599.   DEBUG_PRINT (("pages are shown\n"));
  2600.  
  2601.   gtk_notebook_set_page (GTK_NOTEBOOK (notebook), 1);
  2602.   gtk_notebook_set_page (GTK_NOTEBOOK (notebook), 0);
  2603.  
  2604.  
  2605.   /*
  2606.    *    Initialization done
  2607.    */
  2608.   dlg->init = FALSE;
  2609.   dlg_preview_update ();
  2610.  
  2611.   DEBUG_PRINT (("dlg init done\n"));
  2612.  
  2613.   gtk_main ();
  2614.   gdk_flush ();
  2615.  
  2616.   return pint.run;
  2617. }
  2618.  
  2619. static void
  2620. dlg_ok_callback (GtkWidget *widget,
  2621.          gpointer   data)
  2622. {
  2623.   /* @GFLARE_NAME */
  2624.   gflare_name_copy(pvals.gflare_name, dlg->gflare->name);
  2625.  
  2626.   pint.run = TRUE;
  2627.   gtk_widget_destroy (GTK_WIDGET (data));
  2628. }
  2629.  
  2630.  
  2631. static void
  2632. dlg_close_callback (GtkWidget *widget,
  2633.             gpointer   data)
  2634. {
  2635.   gtk_main_quit ();
  2636. }
  2637.  
  2638. static void
  2639. dlg_rescan_callback (GtkWidget *widget, gpointer data)
  2640. {
  2641.   dlg->init = TRUE;
  2642.   gradient_menu_rescan ();
  2643.   dlg->init = FALSE;
  2644.   dlg_preview_update ();
  2645.   gtk_widget_draw (dlg->notebook, NULL);
  2646. }
  2647.  
  2648.  
  2649. static void
  2650. dlg_setup_gflare ()
  2651. {
  2652.   dlg->gflare = gflares_list_lookup (pvals.gflare_name);
  2653.  
  2654.   if (dlg->gflare == NULL)
  2655.     {
  2656.       g_warning ("Not found `%s': used `Default' instead.", pvals.gflare_name);
  2657.       dlg->gflare = gflares_list_lookup ("Default");
  2658.       if (dlg->gflare == NULL)
  2659.     {
  2660.       g_warning ("`Default' is created.");
  2661.       dlg->gflare = gflare_new_with_default ("Default");
  2662.       gflares_list_insert (dlg->gflare);
  2663.     }
  2664.     }
  2665. }
  2666.  
  2667. static void
  2668. dlg_page_map_callback (GtkWidget *widget, gpointer data)
  2669. {
  2670.   /* dlg_preview_update (); */
  2671. }
  2672.  
  2673. /***********************************/
  2674. /**    Main Dialog / Preview      **/
  2675. /***********************************/
  2676.  
  2677. /*
  2678.  *    Calculate preview's window, ie. translation of preview widget and
  2679.  *    drawable.
  2680.  *
  2681.  *    x0, x1, y0, y1 are drawable coord, corresponding with top left
  2682.  *    corner of preview widget, etc.
  2683.  */
  2684. void
  2685. dlg_preview_calc_window ()
  2686. {
  2687.   int            is_wide;
  2688.   gdouble        offx, offy;
  2689.  
  2690.   is_wide = ((double) DLG_PREVIEW_HEIGHT * drawable->width
  2691.          >= (double) DLG_PREVIEW_WIDTH * drawable->height);
  2692.   if (is_wide)
  2693.     {
  2694.       offy = ((double) drawable->width * DLG_PREVIEW_HEIGHT / DLG_PREVIEW_WIDTH) / 2.0;
  2695.  
  2696.       dlg->pwin.x0 = 0;
  2697.       dlg->pwin.x1 = drawable->width;
  2698.       dlg->pwin.y0 = drawable->height / 2.0 - offy;
  2699.       dlg->pwin.y1 = drawable->height / 2.0 + offy;
  2700.     }
  2701.   else
  2702.     {
  2703.       offx = ((double) drawable->height * DLG_PREVIEW_WIDTH / DLG_PREVIEW_HEIGHT) / 2.0;
  2704.  
  2705.       dlg->pwin.x0 = drawable->width / 2.0 - offx;
  2706.       dlg->pwin.x1 = drawable->width / 2.0 + offx;
  2707.       dlg->pwin.y0 = 0;
  2708.       dlg->pwin.y1 = drawable->height;
  2709.     }
  2710. }
  2711.  
  2712. gint
  2713. dlg_preview_handle_event (GtkWidget *widget, GdkEvent *event)
  2714. {
  2715.   GdkEventButton *bevent;
  2716.   gint         bx, by, x, y;
  2717.   gchar         buf[256];
  2718.  
  2719.   switch (event->type)
  2720.     {
  2721.     case GDK_BUTTON_PRESS:
  2722.       bevent = (GdkEventButton *) event;
  2723.       bx = bevent->x;
  2724.       by = bevent->y;
  2725.  
  2726.       /* convert widget coord to drawable coord */
  2727.       x = dlg->pwin.x0 + (double) (dlg->pwin.x1 - dlg->pwin.x0)
  2728.                     * bx / DLG_PREVIEW_WIDTH;
  2729.       y = dlg->pwin.y0 + (double) (dlg->pwin.y1 - dlg->pwin.y0)
  2730.                     * by / DLG_PREVIEW_HEIGHT;
  2731.       DEBUG_PRINT (("dlg_preview_handle_event: bxy [%d,%d] xy [%d,%d]\n",
  2732.             bx, by, x, y));
  2733.  
  2734.       if ((x != pvals.xcenter || y != pvals.ycenter))
  2735.     {
  2736.       if (x != pvals.xcenter)
  2737.         {
  2738.           pvals.xcenter = x;
  2739.           gtk_signal_handler_block (GTK_OBJECT (dlg->xentry), dlg->xentry_id);
  2740.           sprintf (buf, "%d", x);
  2741.           gtk_entry_set_text (GTK_ENTRY (dlg->xentry), buf);
  2742.           gtk_signal_handler_unblock (GTK_OBJECT (dlg->xentry), dlg->xentry_id);
  2743.         }
  2744.       if (y != pvals.ycenter)
  2745.         {
  2746.           pvals.ycenter = y;
  2747.           gtk_signal_handler_block (GTK_OBJECT (dlg->yentry), dlg->yentry_id);
  2748.           sprintf (buf, "%d", y);
  2749.           gtk_entry_set_text (GTK_ENTRY (dlg->yentry), buf);
  2750.           gtk_signal_handler_unblock (GTK_OBJECT (dlg->yentry), dlg->yentry_id);
  2751.         }
  2752.       dlg_preview_update ();
  2753.     }
  2754.       return TRUE;
  2755.     default:
  2756.       break;
  2757.     }
  2758.   return FALSE;
  2759. }
  2760.  
  2761. static void
  2762. dlg_preview_update ()
  2763. {
  2764.   if (dlg->init)
  2765.     return;
  2766.  
  2767.   if (dlg->update_preview)
  2768.     {
  2769.       dlg->init_params_done = FALSE;
  2770.       preview_render_start (dlg->preview);
  2771.     }
  2772. }
  2773.  
  2774. /*    preview callbacks    */
  2775. static gint
  2776. dlg_preview_init_func (Preview *preview, gpointer data)
  2777. {
  2778.   /* call init_params first, and iterate init_progress while
  2779.      it returns true */
  2780.   if (dlg->init_params_done == FALSE)
  2781.     {
  2782.       calc_init_params (dlg->gflare,
  2783.             CALC_GLOW | CALC_RAYS | CALC_SFLARE,
  2784.             pvals.xcenter, pvals.ycenter,
  2785.             pvals.radius, pvals.rotation, pvals.hue,
  2786.             pvals.vangle, pvals.vlength);
  2787.       dlg->init_params_done = TRUE;
  2788.       return TRUE;
  2789.     }
  2790.   return calc_init_progress ();
  2791. }
  2792.  
  2793. /* render preview
  2794.    do what "preview" means, ie. render lense flare effect onto drawable */
  2795. static void
  2796. dlg_preview_render_func (Preview *preview, guchar *dest, gint y, gpointer data)
  2797. {
  2798.   GPixelRgn    srcPR;
  2799.   gint        x;
  2800.   gint        dx, dy;        /* drawable x, y */
  2801.   guchar    *src_row, *src;
  2802.   guchar    src_pix[4], dest_pix[4];
  2803.   gint        b;
  2804.  
  2805.   dy = dlg->pwin.y0 + (double) (dlg->pwin.y1 - dlg->pwin.y0) * y / DLG_PREVIEW_HEIGHT;
  2806.   if (dy < 0 || dy >= drawable->height)
  2807.     {
  2808.       memset (dest, GRAY50, 3 * DLG_PREVIEW_WIDTH);
  2809.       return;
  2810.     }
  2811.  
  2812.   src_row = g_new (guchar, drawable->bpp * drawable->width);
  2813.   gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE);
  2814.   gimp_pixel_rgn_get_row (&srcPR, src_row, 0, dy, drawable->width);
  2815.  
  2816.   for (x = 0; x < DLG_PREVIEW_HEIGHT; x++)
  2817.     {
  2818.       dx = dlg->pwin.x0 + (double) (dlg->pwin.x1 - dlg->pwin.x0) * x / DLG_PREVIEW_WIDTH;
  2819.       if (dx < 0 || dx >= drawable->width)
  2820.     {
  2821.       for (b = 0; b < 3; b++)
  2822.         *dest++ = GRAY50;
  2823.       continue;
  2824.     }
  2825.  
  2826.       /* Get drawable pix value */
  2827.       src = &src_row[dx * drawable->bpp];
  2828.  
  2829.       for (b = 0; b < 3; b++)
  2830.     src_pix[b] = dinfo.is_color ? src[b] : src[0];
  2831.       src_pix[3] = dinfo.has_alpha ? src[drawable->bpp-1] : OPAQUE;
  2832.  
  2833.       /* Get GFlare pix value */
  2834.  
  2835.       calc_gflare_pix (dest_pix, dx, dy, src_pix);
  2836.  
  2837.       /* Draw gray check if needed */
  2838.       preview_rgba_to_rgb (dest, x, y, dest_pix);
  2839.       dest += 3;
  2840.     }
  2841.  
  2842.   g_free (src_row);
  2843. }
  2844.  
  2845. static void
  2846. dlg_preview_deinit_func (Preview *preview, gpointer data)
  2847. {
  2848.   if (dlg->init_params_done)
  2849.     {
  2850.       calc_deinit ();
  2851.       dlg->init_params_done = TRUE;
  2852.     }
  2853. }
  2854.  
  2855. /*****************************************/
  2856. /**    Main Dialog / Settings Page    **/
  2857. /*****************************************/
  2858.  
  2859. static void
  2860. dlg_make_page_settings (GFlareDialog *dlg, GtkWidget *notebook)
  2861. {
  2862.   GtkWidget    *table;
  2863.   GtkWidget    *note_label;
  2864.   GtkWidget    *button;
  2865.   GtkWidget    *asup_frame;
  2866.   GtkWidget    *asup_table;
  2867.   GtkWidget    *label;
  2868.   GtkWidget    *scale;
  2869.   GtkObject    *scale_data;
  2870.   GtkWidget    *entry;
  2871.   gchar        buffer[256];
  2872.  
  2873.   table = gtk_table_new (10, 2, FALSE);
  2874.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  2875.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  2876.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  2877.  
  2878.   label = gtk_label_new ("Center X:");
  2879.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  2880.   gtk_table_attach (GTK_TABLE(table), label, 0, 1, 0, 1,
  2881.             GTK_FILL, GTK_FILL, 0, 0);
  2882.   gtk_widget_show (label);
  2883.   entry = dlg->xentry = gtk_entry_new ();
  2884.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  2885.   sprintf( buffer, "%d", pvals.xcenter );
  2886.   gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  2887.   dlg->xentry_id =
  2888.     gtk_signal_connect (GTK_OBJECT (entry), "changed",
  2889.             (GtkSignalFunc) &dlg_position_entry_callback,
  2890.             &pvals.xcenter );
  2891.   gtk_table_attach (GTK_TABLE(table), entry, 1, 2, 0, 1,
  2892.             GTK_FILL, GTK_FILL, 0, 0);
  2893.   gtk_widget_show (entry);
  2894.  
  2895.   label = gtk_label_new ("Center Y:");
  2896.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  2897.   gtk_table_attach (GTK_TABLE(table), label, 0, 1, 1, 2,
  2898.             GTK_FILL, GTK_FILL, 0, 0);
  2899.   gtk_widget_show (label);
  2900.   entry = dlg->yentry = gtk_entry_new ();
  2901.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  2902.   sprintf( buffer, "%d", pvals.ycenter );
  2903.   gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  2904.   dlg->yentry_id =
  2905.     gtk_signal_connect (GTK_OBJECT (entry), "changed",
  2906.             (GtkSignalFunc) &dlg_position_entry_callback,
  2907.             &pvals.ycenter );
  2908.   gtk_table_attach (GTK_TABLE(table), entry, 1, 2, 1, 2,
  2909.             GTK_FILL, GTK_FILL, 0, 0);
  2910.   gtk_widget_show (entry);
  2911.  
  2912.   entscale_new (table, 0, 2, "Radius:",
  2913.         ENTSCALE_DOUBLE, &pvals.radius,
  2914.         0.0, drawable->width/2, 1.0, FALSE,
  2915.         &dlg_entscale_callback, (gpointer) PAGE_SETTINGS);
  2916.   entscale_new (table, 0, 3, "Rotation:",
  2917.         ENTSCALE_DOUBLE, &pvals.rotation,
  2918.         -180.0, 180.0, 1.0, FALSE,
  2919.         &dlg_entscale_callback, (gpointer) PAGE_SETTINGS);
  2920.   entscale_new (table, 0, 4, "Hue Rotation:",
  2921.         ENTSCALE_DOUBLE, &pvals.hue,
  2922.         -180.0, 180.0, 1.0, FALSE,
  2923.         &dlg_entscale_callback, (gpointer) PAGE_SETTINGS);
  2924.   entscale_new (table, 0, 5, "Vector Angle:",
  2925.         ENTSCALE_DOUBLE, &pvals.vangle,
  2926.         0.0, 359.0, 1.0, FALSE,
  2927.         &dlg_entscale_callback, (gpointer) PAGE_SETTINGS);
  2928.   entscale_new (table, 0, 6, "Vector Length:",
  2929.         ENTSCALE_DOUBLE, &pvals.vlength,
  2930.         1, 1000, 1.0, FALSE,
  2931.         &dlg_entscale_callback, (gpointer) PAGE_SETTINGS);
  2932.   /**
  2933.   ***    Asupsample settings
  2934.   ***    This code is stolen from gimp-0.99.x/app/blend.c
  2935.   **/
  2936.  
  2937.   button = gtk_check_button_new_with_label ("Adaptive Supersampling");
  2938.   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), pvals.use_asupsample );
  2939.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  2940.               (GtkSignalFunc) dlg_use_asupsample_callback,
  2941.               &pvals.use_asupsample );
  2942.   gtk_table_attach (GTK_TABLE(table), button, 0, 2, 7, 8,
  2943.             GTK_FILL, 0, 0, 0);
  2944.   gtk_widget_show (button);
  2945.  
  2946.   /*  asupsample frame */
  2947.   asup_frame = dlg->asupsample_frame = gtk_frame_new (NULL);
  2948.   gtk_frame_set_shadow_type (GTK_FRAME (asup_frame), GTK_SHADOW_ETCHED_IN);
  2949.   gtk_widget_set_sensitive (asup_frame, pvals.use_asupsample);
  2950.  
  2951.   asup_table = gtk_table_new (2, 2, FALSE);
  2952.   gtk_container_border_width (GTK_CONTAINER (asup_table), 5);
  2953.   gtk_container_add (GTK_CONTAINER (asup_frame), asup_table);
  2954.  
  2955.   label = gtk_label_new ("Max depth:");
  2956.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
  2957.   gtk_table_attach (GTK_TABLE (asup_table), label, 0, 1, 0, 1,
  2958.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2);
  2959.   gtk_widget_show (label);
  2960.  
  2961.   scale_data = gtk_adjustment_new (pvals.asupsample_max_depth,
  2962.                    1.0, 10.0, 1.0, 1.0, 1.0);
  2963.   scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_data));
  2964.   gtk_scale_set_digits (GTK_SCALE (scale), 0);
  2965.   gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
  2966.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  2967.               (GtkSignalFunc) dlg_iscale_callback,
  2968.               &pvals.asupsample_max_depth);
  2969.   gtk_table_attach (GTK_TABLE (asup_table), scale, 1, 2, 0, 1,
  2970.             GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_SHRINK, 4, 2);
  2971.   gtk_widget_show (scale);
  2972.  
  2973.   label = gtk_label_new ("Threshold:");
  2974.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
  2975.   gtk_table_attach (GTK_TABLE (asup_table), label, 0, 1, 1, 2,
  2976.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2);
  2977.   gtk_widget_show (label);
  2978.  
  2979.   scale_data = gtk_adjustment_new (pvals.asupsample_threshold,
  2980.                    0.0, 4.0, 0.01, 0.01, 0.0);
  2981.   scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_data));
  2982.   gtk_scale_set_digits (GTK_SCALE (scale), 2);
  2983.   gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
  2984.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  2985.               (GtkSignalFunc) dlg_dscale_callback,
  2986.               &pvals.asupsample_threshold);
  2987.   gtk_table_attach (GTK_TABLE (asup_table), scale, 1, 2, 1, 2,
  2988.             GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_SHRINK, 4, 2);
  2989.   gtk_widget_show (scale);
  2990.  
  2991.   gtk_widget_show (asup_table);
  2992.   gtk_table_attach (GTK_TABLE(table), asup_frame, 0, 2, 8, 9,
  2993.             GTK_FILL, 0, 0, 0);
  2994.   gtk_widget_show (asup_frame);
  2995.  
  2996.   button = gtk_check_button_new_with_label ("Auto update preview");
  2997.   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), dlg->update_preview );
  2998.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  2999.               (GtkSignalFunc) dlg_update_preview_callback,
  3000.               &dlg->update_preview );
  3001.   gtk_table_attach (GTK_TABLE(table), button, 0, 2, 9, 10,
  3002.             GTK_FILL, 0, 0, 0);
  3003.   gtk_widget_show (button);
  3004.  
  3005.   /*
  3006.    *    Create Page
  3007.    */
  3008.   note_label = gtk_label_new ("Settings");
  3009.   gtk_misc_set_alignment (GTK_MISC (note_label), 0.5, 0.5);
  3010.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), table, note_label);
  3011.   gtk_signal_connect (GTK_OBJECT (table), "map",
  3012.               (GtkSignalFunc) dlg_page_map_callback,
  3013.               (gpointer) PAGE_SETTINGS);
  3014.   gtk_widget_show (note_label);
  3015.   gtk_widget_show (table);
  3016. }
  3017.  
  3018.  
  3019. static void
  3020. dlg_position_entry_callback (GtkWidget *widget, gpointer data)
  3021. {
  3022.   gint *var = data;
  3023.   gint new_val;
  3024.  
  3025.   DEBUG_PRINT (("dlg_position_entry_callback\n"));
  3026.  
  3027.   new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
  3028.   if (*var != new_val)
  3029.     {
  3030.       *var = new_val;
  3031.       dlg_preview_update ();
  3032.     }
  3033. }
  3034.  
  3035. static void
  3036. dlg_entscale_callback (gdouble new_val, gpointer data)
  3037. {
  3038.   DEBUG_PRINT (("dlg_entscale_callback\n"));
  3039.  
  3040.   dlg_preview_update ();
  3041. }
  3042.  
  3043.  
  3044. static void
  3045. dlg_use_asupsample_callback (GtkWidget *widget, gpointer data)
  3046. {
  3047.   gint            *toggle_val = (gint *) data;
  3048.  
  3049.   if (GTK_TOGGLE_BUTTON (widget)->active)
  3050.     *toggle_val = TRUE;
  3051.   else
  3052.     *toggle_val = FALSE;
  3053.  
  3054.   if (!dlg->init)
  3055.     gtk_widget_set_sensitive (dlg->asupsample_frame, *toggle_val);
  3056. }
  3057.  
  3058. static void
  3059. dlg_iscale_callback (GtkAdjustment *adjustment, gpointer data)
  3060. {
  3061.   *(gint*) data = (gint) adjustment->value;
  3062.  
  3063.   /* dlg_preview_update (); */
  3064. }
  3065.  
  3066. static void
  3067. dlg_dscale_callback (GtkAdjustment *adjustment, gpointer data)
  3068. {
  3069.   *(gdouble*) data = adjustment->value;
  3070.  
  3071.   /* dlg_preview_update (); */
  3072. }
  3073.  
  3074. static void
  3075. dlg_update_preview_callback (GtkWidget *widget, gpointer data)
  3076. {
  3077.   gint    *toggle_val = (gint *) data;
  3078.  
  3079.   if (GTK_TOGGLE_BUTTON (widget)->active)
  3080.     *toggle_val = TRUE;
  3081.   else
  3082.     *toggle_val = FALSE;
  3083.  
  3084.   dlg_preview_update ();
  3085. }
  3086.  
  3087. /*****************************************/
  3088. /**    Main Dialog / Selector Page    **/
  3089. /*****************************************/
  3090.  
  3091. static void
  3092. dlg_make_page_selector (GFlareDialog *dlg, GtkWidget *notebook)
  3093. {
  3094.   GtkWidget    *vbox;
  3095.   GtkWidget    *hbox;
  3096.   GtkWidget    *listbox;
  3097.   GtkWidget    *list;
  3098.   GtkWidget    *button;
  3099.   GtkWidget    *note_label;
  3100.   int        i;
  3101.   static struct {
  3102.     char        *label;
  3103.     GtkSignalFunc    callback;
  3104.   } buttons[] = {
  3105.     { "New",    (GtkSignalFunc) &dlg_selector_new_callback},
  3106.     { "Edit",    (GtkSignalFunc) &dlg_selector_edit_callback},
  3107.     { "Copy",    (GtkSignalFunc) &dlg_selector_copy_callback},
  3108.     { "Delete", (GtkSignalFunc) &dlg_selector_delete_callback}
  3109.   };
  3110.  
  3111.   DEBUG_PRINT (("dlg_make_page_selector\n"));
  3112.  
  3113.   vbox = gtk_vbox_new (FALSE, 0);
  3114.  
  3115.   /*
  3116.    *    List Box
  3117.    */
  3118.  
  3119.   listbox = gtk_scrolled_window_new (NULL, NULL);
  3120.   /* gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
  3121.                   GTK_POLICY_AUTOMATIC,
  3122.                   GTK_POLICY_ALWAYS); */
  3123.   gtk_widget_set_usize (listbox, DLG_LISTBOX_WIDTH, DLG_LISTBOX_HEIGHT);
  3124.   gtk_box_pack_start (GTK_BOX (vbox), listbox, TRUE, TRUE, 0);
  3125.   gtk_widget_show (listbox);
  3126.  
  3127.   list = dlg->selector_list = gtk_list_new ();
  3128.   gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_BROWSE);
  3129.   gtk_container_add (GTK_CONTAINER (listbox), list);
  3130.   gtk_widget_show (list);
  3131.  
  3132.   dlg_selector_setup_listbox ();
  3133.  
  3134.   /*
  3135.    *    Buttons
  3136.    */
  3137.   hbox = gtk_hbox_new (FALSE, 0);
  3138.   for (i = 0; i < sizeof (buttons) / sizeof (buttons[0]); i++)
  3139.     {
  3140.       button = gtk_button_new_with_label (buttons[i].label);
  3141.       gtk_signal_connect (GTK_OBJECT (button), "clicked",
  3142.               buttons[i].callback, button);
  3143.       gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  3144.       gtk_widget_show (button);
  3145.     }
  3146.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  3147.   gtk_widget_show (hbox);
  3148.  
  3149.   gtk_widget_show (vbox);
  3150.  
  3151.   /*
  3152.    *    Create Page
  3153.    */
  3154.   note_label = gtk_label_new ("Selector");
  3155.   gtk_widget_show (note_label);
  3156.   gtk_misc_set_alignment (GTK_MISC (note_label), 0.5, 0.5);
  3157.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, note_label);
  3158.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3159.               (GtkSignalFunc) dlg_page_map_callback,
  3160.               (gpointer) PAGE_SELECTOR);
  3161.   gtk_widget_show (note_label);
  3162.   gtk_widget_show (vbox);
  3163. }
  3164.  
  3165.  
  3166. /*
  3167.  *    Set up selector's listbox, according to gflares_list
  3168.  */
  3169. static void
  3170. dlg_selector_setup_listbox ()
  3171. {
  3172.   GList        *list;
  3173.   GFlare    *gflare;
  3174.   int        n;
  3175.  
  3176.   DEBUG_PRINT (("dlg_selector_setup_listbox\n"));
  3177.  
  3178.   list = gflares_list;
  3179.   n = 0;
  3180.  
  3181.   while (list)
  3182.     {
  3183.       gflare = list->data;
  3184.  
  3185.       /*
  3186.     dlg->gflare should be valid (ie. not NULL) here.
  3187.     */
  3188.       if (gflare == dlg->gflare)
  3189.     dlg_selector_insert (gflare, n, 1);
  3190.       else
  3191.     dlg_selector_insert (gflare, n, 0);
  3192.  
  3193.       list = list->next;
  3194.       n++;
  3195.     }
  3196. }
  3197.  
  3198. /*
  3199.  *    Insert new list_item to selector's listbox
  3200.  */
  3201. static void
  3202. dlg_selector_insert (GFlare *gflare, int pos, int select)
  3203. {
  3204.   GtkWidget    *list_item;
  3205.   GList        *list;
  3206.  
  3207.   DEBUG_PRINT (("dlg_selector_insert %s %d\n", gflare->name, pos));
  3208.  
  3209.   list_item = gtk_list_item_new_with_label (gflare->name);
  3210.   /* gflare->list_item = list_item; */
  3211.   gtk_signal_connect (GTK_OBJECT (list_item), "select",
  3212.               (GtkSignalFunc) dlg_selector_list_item_callback,
  3213.               (gpointer) gflare);
  3214.   gtk_widget_show (list_item);
  3215.  
  3216.   list = g_list_append (NULL, list_item);
  3217.   gtk_list_insert_items (GTK_LIST (dlg->selector_list), list, pos);
  3218.  
  3219.   if (select)
  3220.     gtk_list_select_item (GTK_LIST (dlg->selector_list), pos);
  3221. }
  3222.  
  3223. static void
  3224. dlg_selector_list_item_callback (GtkWidget *widget, gpointer data)
  3225. {
  3226.   if (widget->state != GTK_STATE_SELECTED)
  3227.     return;
  3228.  
  3229.   dlg->gflare = data;
  3230.  
  3231.   dlg_preview_update ();
  3232. }
  3233.  
  3234.  
  3235. /*
  3236.  *    "New" button in Selector page
  3237.  */
  3238. static void
  3239. dlg_selector_new_callback (GtkWidget *widget,
  3240.                gpointer data)
  3241. {
  3242.   query_string_box ( "New GFlare",
  3243.              "Enter a name for the new GFlare",
  3244.              "untitled",
  3245.              dlg_selector_new_ok_callback, dlg);
  3246. }
  3247.  
  3248. static void
  3249. dlg_selector_new_ok_callback (GtkWidget *widget, gpointer client_data, gpointer call_data)
  3250. {
  3251.   char        *new_name = call_data;
  3252.   GFlare    *gflare;
  3253.   gint        pos;
  3254.  
  3255.   g_assert (new_name != NULL);
  3256.  
  3257.   if (gflares_list_lookup (new_name))
  3258.     {
  3259.       /* message_box (); */
  3260.       g_warning ("The name `%s' is used already!", new_name);
  3261.       return;
  3262.     }
  3263.  
  3264.   gflare = gflare_new_with_default (new_name);
  3265.  
  3266.   pos = gflares_list_insert (gflare);
  3267.   dlg_selector_insert (gflare, pos, 1);
  3268.  
  3269.   dlg->gflare = gflare;
  3270.   dlg_preview_update ();
  3271. }
  3272.  
  3273. /*
  3274.  *    "Edit" button in Selector page
  3275.  */
  3276. static void
  3277. dlg_selector_edit_callback (GtkWidget *widget,
  3278.                gpointer data)
  3279. {
  3280.   preview_render_end (dlg->preview);
  3281.   gtk_widget_set_sensitive (dlg->shell, FALSE);
  3282.   ed_run (dlg->gflare, dlg_selector_edit_done_callback, NULL);
  3283. }
  3284.  
  3285. static void
  3286. dlg_selector_edit_done_callback (gint updated, gpointer data)
  3287. {
  3288.   gtk_widget_set_sensitive (dlg->shell, TRUE);
  3289.   if (updated)
  3290.     {
  3291.       gflare_save (dlg->gflare);
  3292.     }
  3293.   dlg_preview_update ();
  3294. }
  3295.  
  3296.  
  3297. /*
  3298.  *    "Copy" button in Selector page
  3299.  */
  3300. static void
  3301. dlg_selector_copy_callback (GtkWidget *widget,
  3302.                 gpointer data)
  3303. {
  3304.   char *name;
  3305.  
  3306.   name = g_new (gchar, strlen (dlg->gflare->name) + 6);
  3307.   sprintf (name, "%s copy", dlg->gflare->name);
  3308.  
  3309.   query_string_box ( "Copy GFlare",
  3310.              "Enter a name for the copied GFlare",
  3311.              name,
  3312.              dlg_selector_copy_ok_callback, dlg);
  3313. }
  3314.  
  3315.  
  3316. static void
  3317. dlg_selector_copy_ok_callback (GtkWidget *widget, gpointer client_data, gpointer call_data)
  3318. {
  3319.   char        *copy_name = call_data;
  3320.   GFlare    *gflare;
  3321.   gint        pos;
  3322.  
  3323.   g_assert (copy_name != NULL);
  3324.  
  3325.   if (gflares_list_lookup (copy_name))
  3326.     {
  3327.       /* message_box (); */
  3328.       g_warning ("The name `%s' is used already!", copy_name);
  3329.       return;
  3330.     }
  3331.  
  3332.   gflare = gflare_dup (dlg->gflare, copy_name);
  3333.  
  3334.   pos = gflares_list_insert (gflare);
  3335.   dlg_selector_insert (gflare, pos, 1);
  3336.  
  3337.   dlg->gflare = gflare;
  3338.   gflare_save (dlg->gflare);
  3339.   dlg_preview_update ();
  3340. }
  3341.  
  3342. /*
  3343.  *    "Delete" button in Selector page
  3344.  */
  3345. static void
  3346. dlg_selector_delete_callback (GtkWidget *widget,
  3347.                   gpointer data)
  3348. {
  3349.   GtkWidget *dialog;
  3350.   GtkWidget *vbox;
  3351.   GtkWidget *label;
  3352.   GtkWidget *button;
  3353.   char        *str;
  3354.  
  3355.   if (num_gflares <= 1)
  3356.     {
  3357.       /* message_box () */
  3358.       g_warning ("Cannot delete!! There must be at least one GFlare.");
  3359.       return;
  3360.     }
  3361.  
  3362.   dialog = gtk_dialog_new();
  3363.   gtk_window_set_title(GTK_WINDOW(dialog), "Delete GFlare");
  3364.   gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
  3365.   gtk_container_border_width(GTK_CONTAINER(dialog), 0);
  3366.  
  3367.   vbox = gtk_vbox_new(FALSE, 0);
  3368.   gtk_container_border_width(GTK_CONTAINER(vbox), 8);
  3369.   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
  3370.              FALSE, FALSE, 0);
  3371.   gtk_widget_show(vbox);
  3372.  
  3373.   /* Question */
  3374.  
  3375.   label = gtk_label_new("Are you sure you want to delete");
  3376.   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
  3377.   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
  3378.   gtk_widget_show(label);
  3379.  
  3380.   str = g_malloc((strlen(dlg->gflare->name) + 32 * sizeof(char)));
  3381.   sprintf(str, "\"%s\" from the list and from disk?", dlg->gflare->name);
  3382.  
  3383.   label = gtk_label_new(str);
  3384.   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
  3385.   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
  3386.   gtk_widget_show(label);
  3387.  
  3388.   g_free(str);
  3389.  
  3390.   /* Buttons */
  3391.  
  3392.   button = gtk_button_new_with_label ("Delete");
  3393.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  3394.               (GtkSignalFunc) dlg_selector_delete_ok_callback,
  3395.               (gpointer) dialog);
  3396.   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
  3397.   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
  3398.              button, TRUE, TRUE, 0);
  3399.   gtk_widget_grab_default(button);
  3400.   gtk_widget_show(button);
  3401.  
  3402.   button = gtk_button_new_with_label ("Cancel");
  3403.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  3404.               (GtkSignalFunc) dlg_selector_delete_cancel_callback,
  3405.               (gpointer) dialog);
  3406.   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
  3407.   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
  3408.              button, TRUE, TRUE, 0);
  3409.   gtk_widget_show(button);
  3410.  
  3411.   /* Show! */
  3412.  
  3413.   gtk_widget_show(dialog);
  3414.   gtk_widget_set_sensitive(dlg->shell, FALSE);
  3415. }
  3416.  
  3417. static void
  3418. dlg_selector_delete_ok_callback(GtkWidget *widget, gpointer client_data)
  3419. {
  3420.   GFlare    *old_gflare;
  3421.   GList        *tmp;
  3422.   int        i, new_i;
  3423.  
  3424.   gtk_widget_destroy(GTK_WIDGET(client_data));
  3425.   gtk_widget_set_sensitive(dlg->shell, TRUE);
  3426.  
  3427.   i = gflares_list_index (dlg->gflare);
  3428.  
  3429.   if (i >= 0)
  3430.     {
  3431.       /* Remove current gflare from gflares_list and free it */
  3432.       old_gflare = dlg->gflare;
  3433.       gflares_list_remove (dlg->gflare);
  3434.       dlg->gflare = NULL;
  3435.  
  3436.       /* Remove from listbox */
  3437.       gtk_list_clear_items (GTK_LIST (dlg->selector_list), i, i + 1);
  3438.  
  3439.       /* Calculate new position of gflare and select it */
  3440.       new_i = (i < num_gflares) ? i : num_gflares - 1;
  3441.       if ((tmp = g_list_nth (gflares_list, new_i)))
  3442.       dlg->gflare = tmp->data;
  3443.       gtk_list_select_item (GTK_LIST (dlg->selector_list), new_i);
  3444.  
  3445.       /* Delete old one from disk and memory */
  3446.       if (old_gflare->filename)
  3447.     unlink (old_gflare->filename);
  3448.       gflare_free (old_gflare);
  3449.  
  3450.       /* Update */
  3451.       dlg_preview_update ();
  3452.     }
  3453.   else
  3454.     g_warning ("not found %s in gflares_list", dlg->gflare->name);
  3455. }
  3456.  
  3457. static void
  3458. dlg_selector_delete_cancel_callback (GtkWidget *widget, gpointer client_data)
  3459. {
  3460.   gtk_widget_destroy(GTK_WIDGET(client_data));
  3461.   gtk_widget_set_sensitive(dlg->shell, TRUE);
  3462. }
  3463.  
  3464. /*************************************************************************/
  3465. /**                                    **/
  3466. /**            GFlare Editor                    **/
  3467. /**            +++ ed                        **/
  3468. /**                                    **/
  3469. /*************************************************************************/
  3470.  
  3471. /*
  3472.     This is gflare editor dilaog, one which opens by clicking
  3473.     "Edit" button on the selector page in the main dialog.
  3474.  */
  3475.  
  3476. static void
  3477. ed_run (GFlare             *target_gflare,
  3478.     GFlareEditorCallback callback,
  3479.     gpointer         calldata)
  3480. {
  3481.   GtkWidget    *shell;
  3482.   GtkWidget    *table;
  3483.   GtkWidget    *prv_frame;
  3484.   GtkWidget    *notebook;
  3485.   GtkWidget    *button;
  3486.  
  3487.   if (!ed)
  3488.     ed = g_new0 (GFlareEditor, 1);
  3489.   ed->init = TRUE;
  3490.   ed->run  = FALSE;
  3491.   ed->target_gflare = target_gflare;
  3492.   ed->gflare = gflare_dup (target_gflare, target_gflare->name);
  3493.   ed->callback = callback;
  3494.   ed->calldata = calldata;
  3495.  
  3496.   /*
  3497.    *    Dialog Shell
  3498.    */
  3499.   shell = ed->shell = gtk_dialog_new ();
  3500.   gtk_window_set_title (GTK_WINDOW (shell), "GFlare Editor");
  3501.   gtk_window_position (GTK_WINDOW (shell), GTK_WIN_POS_MOUSE);
  3502.   gtk_signal_connect (GTK_OBJECT (shell), "destroy",
  3503.               (GtkSignalFunc) ed_close_callback,
  3504.               NULL);
  3505.  
  3506.   /*
  3507.    *    Action area
  3508.    */
  3509.  
  3510.   button = gtk_button_new_with_label ("OK");
  3511.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  3512.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  3513.               (GtkSignalFunc) ed_ok_callback,
  3514.               shell);
  3515.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->action_area),
  3516.               button, TRUE, TRUE, 0);
  3517.   gtk_widget_grab_default (button);
  3518.   gtk_widget_show (button);
  3519.  
  3520.   button = gtk_button_new_with_label ("Cancel");
  3521.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  3522.   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
  3523.                  (GtkSignalFunc) gtk_widget_destroy,
  3524.                  GTK_OBJECT (shell));
  3525.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->action_area),
  3526.               button, TRUE, TRUE, 0);
  3527.   gtk_widget_show (button);
  3528.  
  3529.   button = gtk_button_new_with_label ("Rescan Gradients");
  3530.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  3531.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  3532.               (GtkSignalFunc) ed_rescan_callback,
  3533.               NULL);
  3534.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->action_area),
  3535.               button, TRUE, TRUE, 0);
  3536.   gtk_widget_show (button);
  3537.  
  3538.   /*
  3539.    *    Preview
  3540.    */
  3541.  
  3542.   ed->preview = preview_new (ED_PREVIEW_WIDTH, ED_PREVIEW_HEIGHT,
  3543.                  ed_preview_init_func, NULL,
  3544.                  ed_preview_render_func, NULL,
  3545.                  ed_preview_deinit_func, NULL);
  3546.  
  3547.   prv_frame = gtk_frame_new (NULL);
  3548.   gtk_frame_set_shadow_type (GTK_FRAME (prv_frame), GTK_SHADOW_IN);
  3549.   gtk_container_add (GTK_CONTAINER (prv_frame), ed->preview->widget);
  3550.   gtk_widget_show (prv_frame);
  3551.  
  3552.  
  3553.   /*
  3554.    *    Notebook
  3555.    */
  3556.   notebook = ed->notebook = gtk_notebook_new ();
  3557.   gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
  3558.  
  3559.   ed_make_page_general (ed, notebook);
  3560.   ed_make_page_glow (ed, notebook);
  3561.   ed_make_page_rays (ed, notebook);
  3562.   ed_make_page_sflare (ed, notebook);
  3563.   gtk_widget_show (notebook);
  3564.  
  3565.   /*
  3566.    *    pack them
  3567.    */
  3568.  
  3569.   table = gtk_table_new (1, 2, FALSE);
  3570.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  3571.   gtk_table_attach (GTK_TABLE (table), prv_frame, 0, 1, 0, 1,
  3572.             0, 0, 0, 0);
  3573.   gtk_table_attach (GTK_TABLE (table), notebook, 1, 2, 0, 1,
  3574.             GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
  3575.   gtk_widget_show (table);
  3576.  
  3577.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->vbox), table, TRUE, TRUE, 0);
  3578.   gtk_widget_show (shell);
  3579.  
  3580.   ed->init = FALSE;
  3581.   ed_preview_update ();
  3582. }
  3583.  
  3584. static void
  3585. ed_close_callback (GtkWidget *widget,
  3586.            gpointer   data)
  3587. {
  3588.   preview_free (ed->preview);
  3589.   gflare_free (ed->gflare);
  3590.   if (ed->callback)
  3591.     (*ed->callback) (ed->run, ed->calldata);
  3592. }
  3593.  
  3594. static void
  3595. ed_ok_callback (GtkWidget *widget,
  3596.          gpointer   data)
  3597. {
  3598.   ed->run = TRUE;
  3599.   gflare_copy (ed->target_gflare, ed->gflare);
  3600.   gtk_widget_destroy (ed->shell);
  3601. }
  3602.  
  3603. static void
  3604. ed_rescan_callback (GtkWidget *widget, gpointer data)
  3605. {
  3606.   ed->init = TRUE;
  3607.   gradient_menu_rescan ();
  3608.   ed->init = FALSE;
  3609.   ed_preview_update ();
  3610.   gtk_widget_draw (ed->notebook, NULL);
  3611. }
  3612.  
  3613.  
  3614. static void
  3615. ed_make_page_general (GFlareEditor *ed, GtkWidget *notebook)
  3616. {
  3617.   GFlare    *gflare = ed->gflare;
  3618.   GtkWidget    *vbox;
  3619.   GtkWidget    *table;
  3620.   GtkWidget    *note_label;
  3621.   GtkWidget    *option_menu;
  3622.  
  3623.   vbox = gtk_vbox_new (FALSE, 0);
  3624.  
  3625.   /*
  3626.    *    Scales
  3627.    */
  3628.  
  3629.   table = gtk_table_new (6, 2, FALSE);
  3630.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  3631.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  3632.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  3633.  
  3634.   entscale_new (table, 0, 0, "Glow Opacity (%):",
  3635.         ENTSCALE_DOUBLE, &gflare->glow_opacity,
  3636.         0.0, 100.0, 1.0, TRUE,
  3637.         &ed_entscale_callback, (gpointer) PAGE_GENERAL);
  3638.  
  3639.   option_menu = ed_mode_menu_new (&gflare->glow_mode);
  3640.   ed_put_mode_menu (table, 0, 1, "Glow Mode:", option_menu);
  3641.  
  3642.   entscale_new (table, 0, 2, "Rays Opacity (%):",
  3643.         ENTSCALE_DOUBLE, &gflare->rays_opacity,
  3644.         0.0, 100.0, 1.0, TRUE,
  3645.         &ed_entscale_callback, (gpointer) PAGE_GENERAL);
  3646.  
  3647.   option_menu = ed_mode_menu_new (&gflare->rays_mode);
  3648.   ed_put_mode_menu (table, 0, 3, "Rays Mode:", option_menu);
  3649.  
  3650.   entscale_new (table, 0, 4, "Second Flares Opacity (%):",
  3651.         ENTSCALE_DOUBLE, &gflare->sflare_opacity,
  3652.         0.0, 100.0, 1.0, TRUE,
  3653.         &ed_entscale_callback, (gpointer) PAGE_GENERAL);
  3654.  
  3655.   option_menu = ed_mode_menu_new (&gflare->sflare_mode);
  3656.   ed_put_mode_menu (table, 0, 5, "Second Flares Mode:", option_menu);
  3657.  
  3658.   gtk_widget_show (table);
  3659.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  3660.  
  3661.   /*
  3662.    *    Create Page
  3663.    */
  3664.   note_label = gtk_label_new ("General");
  3665.   gtk_misc_set_alignment (GTK_MISC (note_label), 0.5, 0.5);
  3666.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, note_label);
  3667.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3668.               (GtkSignalFunc) ed_page_map_callback,
  3669.               (gpointer) PAGE_GENERAL);
  3670.   gtk_widget_show (note_label);
  3671.   gtk_widget_show (vbox);
  3672. }
  3673.  
  3674. static void
  3675. ed_make_page_glow (GFlareEditor *ed, GtkWidget *notebook)
  3676. {
  3677.   GFlare    *gflare = ed->gflare;
  3678.   GradientMenu    *gm;
  3679.   GtkWidget    *vbox;
  3680.   GtkWidget    *table;
  3681.   GtkWidget    *note_label;
  3682.  
  3683.   vbox = gtk_vbox_new (FALSE, 0);
  3684.  
  3685.   /*
  3686.    *    Gradient Menus
  3687.    */
  3688.  
  3689.   table = gtk_table_new (3, 3, FALSE);
  3690.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  3691.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  3692.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  3693.  
  3694.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3695.               gflare->glow_radial, gflare->glow_radial );
  3696.   ed_put_gradient_menu (table, 0, 0, "Radial Gradient:", gm);
  3697.  
  3698.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3699.               gflare->glow_angular, gflare->glow_angular );
  3700.   ed_put_gradient_menu (table, 0, 1, "Angular Gradient:", gm);
  3701.  
  3702.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3703.               gflare->glow_angular_size, gflare->glow_angular_size );
  3704.   ed_put_gradient_menu (table, 0, 2, "Angular Size Gradient:", gm);
  3705.  
  3706.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  3707.   gtk_widget_show (table);
  3708.  
  3709.   /*
  3710.    *    Scales
  3711.    */
  3712.  
  3713.   table = gtk_table_new (3, 2, FALSE);
  3714.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  3715.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  3716.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  3717.   entscale_new (table, 0, 0, "Size (%):",
  3718.         ENTSCALE_DOUBLE, &gflare->glow_size,
  3719.         0.0, 200.0, 1.0, FALSE,
  3720.         &ed_entscale_callback, (gpointer) PAGE_GLOW);
  3721.  
  3722.   entscale_new (table, 0, 1, "Rotation:",
  3723.         ENTSCALE_DOUBLE, &gflare->glow_rotation,
  3724.         -180.0, 180.0, 1.0, FALSE,
  3725.         &ed_entscale_callback, (gpointer) PAGE_GLOW);
  3726.  
  3727.   entscale_new (table, 0, 2, "Hue Rotation:",
  3728.         ENTSCALE_DOUBLE, &gflare->glow_hue,
  3729.         -180.0, 180.0, 1.0, FALSE,
  3730.         &ed_entscale_callback, (gpointer) PAGE_GLOW);
  3731.  
  3732.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  3733.   gtk_widget_show (table);
  3734.  
  3735.   /*
  3736.    *    Create Page
  3737.    */
  3738.   note_label = gtk_label_new ("Glow");
  3739.   gtk_misc_set_alignment (GTK_MISC (note_label), 0.5, 0.5);
  3740.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, note_label );
  3741.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3742.               (GtkSignalFunc) ed_page_map_callback,
  3743.               (gpointer) PAGE_GLOW);
  3744.   gtk_widget_show (note_label);
  3745.   gtk_widget_show (vbox);
  3746. }
  3747.  
  3748. static void
  3749. ed_make_page_rays (GFlareEditor *ed, GtkWidget *notebook)
  3750. {
  3751.   GFlare    *gflare = ed->gflare;
  3752.   GradientMenu    *gm;
  3753.   GtkWidget    *vbox;
  3754.   GtkWidget    *table;
  3755.   GtkWidget    *note_label;
  3756.  
  3757.   vbox = gtk_vbox_new (FALSE, 0);
  3758.  
  3759.   /*
  3760.    *    Gradient Menus
  3761.    */
  3762.  
  3763.   table = gtk_table_new (3, 3, FALSE);
  3764.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  3765.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  3766.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  3767.  
  3768.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3769.               gflare->rays_radial, gflare->rays_radial );
  3770.   ed_put_gradient_menu (table, 0, 0, "Radial Gradient:", gm);
  3771.  
  3772.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3773.               gflare->rays_angular, gflare->rays_angular );
  3774.   ed_put_gradient_menu (table, 0, 1, "Angular Gradient:", gm);
  3775.  
  3776.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3777.               gflare->rays_angular_size, gflare->rays_angular_size );
  3778.   ed_put_gradient_menu (table, 0, 2, "Angular Size Gradient:", gm);
  3779.  
  3780.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  3781.   gtk_widget_show (table);
  3782.  
  3783.   /*
  3784.    *    Scales
  3785.    */
  3786.  
  3787.   table = gtk_table_new (5, 2, FALSE);
  3788.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  3789.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  3790.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  3791.  
  3792.   entscale_new (table, 0, 0, "Size (%):",
  3793.         ENTSCALE_DOUBLE, &gflare->rays_size,
  3794.         0.0, 200.0, 1.0, FALSE,
  3795.         &ed_entscale_callback, (gpointer) PAGE_RAYS);
  3796.   entscale_new (table, 0, 1, "Rotation:",
  3797.         ENTSCALE_DOUBLE, &gflare->rays_rotation,
  3798.         -180.0, 180.0, 1.0, FALSE,
  3799.         &ed_entscale_callback, (gpointer) PAGE_RAYS);
  3800.   entscale_new (table, 0, 2, "Hue Rotation:",
  3801.         ENTSCALE_DOUBLE, &gflare->rays_hue,
  3802.         -180.0, 180.0, 1.0, FALSE,
  3803.         &ed_entscale_callback, (gpointer) PAGE_RAYS);
  3804.   entscale_new (table, 0, 3, "# of Spikes:",
  3805.         ENTSCALE_INT, &gflare->rays_nspikes,
  3806.         1, 300, 1, FALSE,
  3807.         &ed_entscale_callback, (gpointer) PAGE_RAYS);
  3808.   entscale_new (table, 0, 4, "Spike Thickness:",
  3809.         ENTSCALE_DOUBLE, &gflare->rays_thickness,
  3810.         1.0, 100.0, 1.0, FALSE,
  3811.         &ed_entscale_callback, (gpointer) PAGE_RAYS);
  3812.  
  3813.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  3814.   gtk_widget_show (table);
  3815.  
  3816.   /*
  3817.    *    Create Pages
  3818.    */
  3819.  
  3820.   note_label = gtk_label_new ("Rays");
  3821.   gtk_misc_set_alignment (GTK_MISC (note_label), 0.5, 0.5);
  3822.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, note_label );
  3823.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3824.               (GtkSignalFunc) ed_page_map_callback,
  3825.               (gpointer) PAGE_RAYS);
  3826.   gtk_widget_show (note_label);
  3827.   gtk_widget_show (vbox);
  3828. }
  3829.  
  3830. static void
  3831. ed_make_page_sflare (GFlareEditor *ed, GtkWidget *notebook)
  3832. {
  3833.   GFlare    *gflare = ed->gflare;
  3834.   GradientMenu    *gm;
  3835.   GtkWidget    *note_label;
  3836.   GtkWidget    *vbox;
  3837.   GtkWidget    *table;
  3838.   GtkWidget    *shape_frame;
  3839.   GtkWidget    *shape_vbox;
  3840.   GSList    *shape_group = NULL;
  3841.   GtkWidget    *polygon_hbox;
  3842.   GtkWidget    *seed_hbox;
  3843.   GtkWidget    *toggle;
  3844.   GtkWidget    *label;
  3845.   GtkWidget    *entry;
  3846.   char        buf[256];
  3847.  
  3848.  
  3849.   vbox = gtk_vbox_new (FALSE, 0);
  3850.  
  3851.   /*
  3852.    *    Gradient Menus
  3853.    */
  3854.  
  3855.   table = gtk_table_new (3, 3, FALSE);
  3856.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  3857.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  3858.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  3859.  
  3860.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3861.               gflare->sflare_radial, gflare->sflare_radial );
  3862.   ed_put_gradient_menu (table, 0, 0, "Radial Gradient:", gm);
  3863.  
  3864.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3865.               gflare->sflare_sizefac, gflare->sflare_sizefac );
  3866.   ed_put_gradient_menu (table, 0, 1, "Size Factor Gradient:", gm);
  3867.  
  3868.   gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
  3869.               gflare->sflare_probability, gflare->sflare_probability );
  3870.   ed_put_gradient_menu (table, 0, 2, "Probability Gradient:", gm);
  3871.  
  3872.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  3873.   gtk_widget_show (table);
  3874.  
  3875.   /*
  3876.    *    Scales
  3877.    */
  3878.  
  3879.   table = gtk_table_new (4, 2, FALSE);
  3880.   gtk_container_border_width (GTK_CONTAINER (table), 10);
  3881.   gtk_table_set_row_spacings (GTK_TABLE (table), 3);
  3882.   gtk_table_set_col_spacings (GTK_TABLE (table), 10);
  3883.  
  3884.   entscale_new (table, 0, 0, "Size (%):",
  3885.         ENTSCALE_DOUBLE, &gflare->sflare_size,
  3886.         0.0, 200.0, 1.0, FALSE,
  3887.         &ed_entscale_callback, (gpointer) PAGE_SFLARE);
  3888.   entscale_new (table, 0, 1, "Rotation:",
  3889.         ENTSCALE_DOUBLE, &gflare->sflare_rotation,
  3890.         -180.0, 180.0, 1.0, FALSE,
  3891.         &ed_entscale_callback, (gpointer) PAGE_SFLARE);
  3892.   entscale_new (table, 0, 2, "Hue Rotation:",
  3893.         ENTSCALE_DOUBLE, &gflare->sflare_hue,
  3894.         -180.0, 180.0, 1.0, FALSE,
  3895.         &ed_entscale_callback, (gpointer) PAGE_SFLARE);
  3896.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  3897.   gtk_widget_show (table);
  3898.  
  3899.   /*
  3900.    *    Shape Radio Button Frame
  3901.    */
  3902.  
  3903.   shape_vbox = gtk_vbox_new (FALSE, 5);
  3904.  
  3905.   toggle = gtk_radio_button_new_with_label (shape_group, "Circle");
  3906.   shape_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  3907.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  3908.               (GtkSignalFunc) ed_shape_radio_callback,
  3909.               (gpointer) GF_CIRCLE);
  3910.   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle),
  3911.                    gflare->sflare_shape == GF_CIRCLE);
  3912.   gtk_widget_show (toggle);
  3913.   gtk_box_pack_start (GTK_BOX (shape_vbox), toggle, FALSE, FALSE, 0);
  3914.  
  3915.   toggle = ed->polygon_toggle =
  3916.     gtk_radio_button_new_with_label (shape_group, "Polygon");
  3917.   shape_group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  3918.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  3919.               (GtkSignalFunc) ed_shape_radio_callback,
  3920.               (gpointer) GF_POLYGON);
  3921.   gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle),
  3922.                    gflare->sflare_shape == GF_POLYGON);
  3923.   gtk_widget_show (toggle);
  3924.  
  3925.   entry = ed->polygon_entry = gtk_entry_new ();
  3926.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  3927.   sprintf (buf, "%d", gflare->sflare_nverts);
  3928.   gtk_entry_set_text (GTK_ENTRY (entry), buf);
  3929.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  3930.               (GtkSignalFunc) ed_ientry_callback,
  3931.               &gflare->sflare_nverts);
  3932.   gtk_widget_show (entry);
  3933.  
  3934.   polygon_hbox = gtk_hbox_new (FALSE, 1);
  3935.   gtk_box_pack_start (GTK_BOX (polygon_hbox), toggle, FALSE, FALSE, 0);
  3936.   gtk_box_pack_start (GTK_BOX (polygon_hbox), entry, FALSE, FALSE, 0);
  3937.   gtk_widget_show (polygon_hbox);
  3938.  
  3939.   gtk_box_pack_start (GTK_BOX (shape_vbox), polygon_hbox, FALSE, FALSE, 0);
  3940.   gtk_widget_show (shape_vbox);
  3941.  
  3942.   shape_frame = gtk_frame_new ("Shape of Second Flares");
  3943.   gtk_frame_set_shadow_type (GTK_FRAME (shape_frame), GTK_SHADOW_ETCHED_IN);
  3944.   gtk_container_add (GTK_CONTAINER (shape_frame), shape_vbox);
  3945.   gtk_widget_show (shape_frame);
  3946.   gtk_box_pack_start (GTK_BOX (vbox), shape_frame, FALSE, FALSE, 0);
  3947.  
  3948.   /*
  3949.    *    Random Seed Entry
  3950.    */
  3951.  
  3952.   seed_hbox = gtk_hbox_new (FALSE, 5);
  3953.   label = gtk_label_new ("Random Seed (-1 means current time):");
  3954.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  3955.   gtk_widget_show (label);
  3956.   gtk_box_pack_start (GTK_BOX (seed_hbox), label, FALSE, FALSE, 0);
  3957.   entry = gtk_entry_new ();
  3958.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  3959.   sprintf (buf, "%d", gflare->sflare_seed);
  3960.   gtk_entry_set_text (GTK_ENTRY (entry), buf);
  3961.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  3962.               (GtkSignalFunc) ed_ientry_callback,
  3963.               &gflare->sflare_seed);
  3964.   gtk_widget_show (entry);
  3965.   gtk_box_pack_start (GTK_BOX (seed_hbox), entry, FALSE, TRUE, 0);
  3966.   gtk_widget_show (seed_hbox);
  3967.  
  3968.   gtk_box_pack_start (GTK_BOX (vbox), seed_hbox, FALSE, FALSE, 0);
  3969.  
  3970.   /*
  3971.    *    Create Pages
  3972.    */
  3973.   note_label = gtk_label_new ("Second Flares");
  3974.   gtk_misc_set_alignment (GTK_MISC (note_label), 0.5, 0.5);
  3975.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, note_label );
  3976.   gtk_signal_connect (GTK_OBJECT (vbox), "map",
  3977.               (GtkSignalFunc) ed_page_map_callback,
  3978.               (gpointer) PAGE_SFLARE);
  3979.   gtk_widget_show (note_label);
  3980.   gtk_widget_show (vbox);
  3981. }
  3982.  
  3983. GtkWidget *
  3984. ed_mode_menu_new (GFlareMode *mode_var)
  3985. {
  3986.   GtkWidget    *option_menu;
  3987.   GtkWidget    *menu;
  3988.   GtkWidget    *menuitem;
  3989.   gint        i, j;
  3990.   gchar        buf[256];
  3991.   GFlareMode    mode;
  3992.  
  3993.   option_menu = gtk_option_menu_new ();
  3994.   menu = gtk_menu_new ();
  3995.  
  3996.   for (i = 0; i < GF_NUM_MODES; i++)
  3997.     {
  3998.       /* Capitalize */
  3999.       strcpy (buf, gflare_modes[i]);
  4000.       if (islower (buf[0]))        /* for BSD? */
  4001.     buf[0] = toupper (buf[0]);
  4002.       for (j = 1; buf[j]; j++)
  4003.     if (isupper (buf[j]))
  4004.       buf[j] = tolower (buf[j]);
  4005.  
  4006.       menuitem = gtk_menu_item_new_with_label (buf);
  4007.       gtk_object_set_user_data (GTK_OBJECT (menuitem), (gpointer) i);
  4008.       gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
  4009.               (GtkSignalFunc) ed_mode_menu_callback,
  4010.               mode_var);
  4011.       gtk_widget_show (menuitem);
  4012.       gtk_menu_append (GTK_MENU (menu), menuitem);
  4013.     }
  4014.   mode = *mode_var;
  4015.   if (mode < 0 || mode >= GF_NUM_MODES)
  4016.     mode = GF_NORMAL;
  4017.   gtk_menu_set_active (GTK_MENU (menu), mode);
  4018.   gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
  4019.   gtk_widget_show (option_menu);
  4020.  
  4021.   return option_menu;
  4022. }
  4023.  
  4024. static void
  4025. ed_put_mode_menu (GtkWidget *table, gint x, gint y,
  4026.           gchar *caption, GtkWidget *option_menu)
  4027. {
  4028.   GtkWidget    *label;
  4029.  
  4030.   label = gtk_label_new (caption);
  4031.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  4032.   gtk_widget_show (label);
  4033.  
  4034.   gtk_table_attach (GTK_TABLE (table), label,
  4035.             x     , x + 1, y, y + 1,
  4036.             GTK_FILL, 0, 0, 0);
  4037.   gtk_table_attach (GTK_TABLE (table), option_menu,
  4038.             x + 1, x + 2, y, y + 1,
  4039.             0, 0, 0, 0);
  4040. }
  4041.  
  4042. /*
  4043.   puts gradient menu with caption into table
  4044.   occupies 1 row and 3 cols in table
  4045.  */
  4046. static void
  4047. ed_put_gradient_menu (GtkWidget *table, gint x, gint y,
  4048.               gchar *caption, GradientMenu *gm)
  4049. {
  4050.   GtkWidget    *label;
  4051.  
  4052.   label = gtk_label_new (caption);
  4053.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  4054.   gtk_widget_show (label);
  4055.  
  4056.   gtk_object_set_user_data (GTK_OBJECT (gm->option_menu), ed);
  4057.   gtk_table_attach (GTK_TABLE (table), label,
  4058.             x     , x + 1, y, y + 1,
  4059.             GTK_FILL, 0, 0, 0);
  4060.   gtk_table_attach (GTK_TABLE (table), gm->preview,
  4061.             x + 1, x + 2, y, y + 1,
  4062.             0, 0, 0, 0);
  4063.   gtk_table_attach (GTK_TABLE (table), gm->option_menu,
  4064.             x + 2, x + 3, y, y + 1,
  4065.             0, 0, 0, 0);
  4066. }
  4067.  
  4068. static void
  4069. ed_entscale_callback (gdouble new_val, gpointer data)
  4070. {
  4071.   DEBUG_PRINT (("ed_entscale_callback\n"));
  4072.   ed_preview_update ();
  4073. }
  4074.  
  4075. static void
  4076. ed_mode_menu_callback (GtkWidget *widget, gpointer data)
  4077. {
  4078.   GFlareMode    *mode_var;
  4079.  
  4080.   mode_var = data;
  4081.   *mode_var = (GFlareMode) gtk_object_get_user_data (GTK_OBJECT (widget));
  4082.  
  4083.   ed_preview_update ();
  4084. }
  4085.  
  4086. static void
  4087. ed_gradient_menu_callback (gchar *gradient_name, gpointer data)
  4088. {
  4089.   gchar            *dest_string = data;
  4090.  
  4091.   /* @GRADIENT_NAME */
  4092.   gradient_name_copy (dest_string, gradient_name);
  4093.   ed_preview_update ();
  4094. }
  4095.  
  4096. static void
  4097. ed_shape_radio_callback (GtkWidget *widget, gpointer data)
  4098. {
  4099.   gint        state;
  4100.  
  4101.   state = GTK_TOGGLE_BUTTON (widget)->active;
  4102.   if (state)
  4103.     {
  4104.       ed->gflare->sflare_shape = (GFlareShape) data;
  4105.     }
  4106.   if (!ed->init && widget == ed->polygon_toggle)
  4107.     gtk_widget_set_sensitive (ed->polygon_entry, state);
  4108.  
  4109.   ed_preview_update ();
  4110. }
  4111.  
  4112. static void
  4113. ed_ientry_callback (GtkWidget *widget, gpointer data)
  4114. {
  4115.   gint        new_val;
  4116.  
  4117.   new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
  4118.   *(gint *)data = new_val;
  4119.  
  4120.   ed_preview_update ();
  4121. }
  4122.  
  4123.  
  4124. /*
  4125.   NOTE: This is hack, because this code depends on internal "map"
  4126.   signal of changing pages of gtknotebook.
  4127.  */
  4128. static void
  4129. ed_page_map_callback (GtkWidget *widget, gpointer data)
  4130. {
  4131.   gint        page_num = (gint) data;
  4132.  
  4133.   DEBUG_PRINT(("ed_page_map_callback\n"));
  4134.  
  4135.   ed->cur_page = page_num;
  4136.   ed_preview_update ();
  4137. }
  4138.  
  4139.  
  4140. static void
  4141. ed_preview_update ()
  4142. {
  4143.   if (ed->init)
  4144.     return;
  4145.  
  4146.   ed->init_params_done = FALSE;
  4147.   preview_render_start (ed->preview);
  4148. }
  4149.  
  4150. static gint
  4151. ed_preview_init_func (Preview *preview, gpointer data)
  4152. {
  4153.   int    type = 0;
  4154.  
  4155.   if (ed->init_params_done == FALSE)
  4156.     {
  4157.       switch (ed->cur_page)
  4158.     {
  4159.     case PAGE_GENERAL:
  4160.       type = (CALC_GLOW | CALC_RAYS | CALC_SFLARE);
  4161.       break;
  4162.     case PAGE_GLOW:
  4163.       type = CALC_GLOW;
  4164.       break;
  4165.     case PAGE_RAYS:
  4166.       type = CALC_RAYS;
  4167.       break;
  4168.     case PAGE_SFLARE:
  4169.       type = CALC_SFLARE;
  4170.       break;
  4171.     default:
  4172.       g_warning ("ed_preview_edit_func: bad page");
  4173.       break;
  4174.     }
  4175.       calc_init_params (ed->gflare, type,
  4176.             ED_PREVIEW_WIDTH/2, ED_PREVIEW_HEIGHT/2,
  4177.             ED_PREVIEW_WIDTH/2, 0.0, 0.0,
  4178.             pvals.vangle, pvals.vlength);
  4179.  
  4180.       ed->init_params_done = TRUE;
  4181.       return TRUE;
  4182.     }
  4183.   return calc_init_progress ();
  4184. }
  4185.  
  4186. static void
  4187. ed_preview_deinit_func (Preview *preview, gpointer data)
  4188. {
  4189.   if (ed->init_params_done)
  4190.     {
  4191.       calc_deinit ();
  4192.       ed->init_params_done = FALSE;
  4193.     }
  4194. }
  4195.  
  4196. static void
  4197. ed_preview_render_func (Preview *preview, guchar *buffer, gint y, gpointer data)
  4198. {
  4199.   switch (ed->cur_page)
  4200.     {
  4201.     case PAGE_GENERAL:
  4202.       ed_preview_render_general (buffer, y);
  4203.       break;
  4204.     case PAGE_GLOW:
  4205.       ed_preview_render_glow (buffer, y);
  4206.       break;
  4207.     case PAGE_RAYS:
  4208.       ed_preview_render_rays (buffer, y);
  4209.       break;
  4210.     case PAGE_SFLARE:
  4211.       ed_preview_render_sflare (buffer, y);
  4212.       break;
  4213.     default:
  4214.       g_warning ("hmm, bad page in ed_preview_render_func ()");
  4215.       break;
  4216.     }
  4217. }
  4218.  
  4219. static void
  4220. ed_preview_render_general (guchar *buffer, gint y)
  4221. {
  4222.   int        x, i;
  4223.   guchar    gflare_pix[4];
  4224.   static guchar src_pix[4] = {0, 0, 0, OPAQUE};
  4225.   int        gflare_a;
  4226.  
  4227.   for (x = 0; x < ED_PREVIEW_WIDTH; x++)
  4228.     {
  4229.       calc_gflare_pix (gflare_pix, x, y, src_pix);
  4230.       gflare_a = gflare_pix[3];
  4231.  
  4232.       for (i = 0; i < 3; i++)
  4233.     {
  4234.       *buffer++ = gflare_pix[i] * gflare_a / 255;
  4235.     }
  4236.     }
  4237. }
  4238.  
  4239.  
  4240. static void
  4241. ed_preview_render_glow (guchar *buffer, gint y)
  4242. {
  4243.   int        x, i;
  4244.   guchar    pix[4];
  4245.  
  4246.   for (x = 0; x < ED_PREVIEW_WIDTH; x++)
  4247.     {
  4248.       calc_glow_pix (pix, x, y);
  4249.       for (i = 0; i < 3; i++)
  4250.     *buffer++ = pix[i] * pix[3] / 255;
  4251.     }
  4252. }
  4253.  
  4254. static void
  4255. ed_preview_render_rays (guchar *buffer, gint y)
  4256. {
  4257.   int        x, i;
  4258.   guchar    pix[4];
  4259.  
  4260.   for (x = 0; x < ED_PREVIEW_WIDTH; x++)
  4261.     {
  4262.       calc_rays_pix (pix, x, y);
  4263.       for (i = 0; i < 3; i++)
  4264.     *buffer++ = pix[i] * pix[3] / 255;
  4265.     }
  4266. }
  4267.  
  4268. static void
  4269. ed_preview_render_sflare (guchar *buffer, gint y)
  4270. {
  4271.   int        x, i;
  4272.   guchar    pix[4];
  4273.   static guchar src_pix[4] = {0, 0, 0, OPAQUE};
  4274.  
  4275.   for (x = 0; x < ED_PREVIEW_WIDTH; x++)
  4276.     {
  4277.       calc_sflare_pix (pix, x, y, src_pix);
  4278.       for (i = 0; i < 3; i++)
  4279.     *buffer++ = pix[i] * pix[3] / 255;
  4280.     }
  4281. }
  4282.  
  4283. /*************************************************************************/
  4284. /**                                    **/
  4285. /**            +++ Preview                    **/
  4286. /**                                    **/
  4287. /*************************************************************************/
  4288.  
  4289. /*
  4290.     this is generic preview routines.
  4291.  */
  4292.  
  4293.  
  4294. /*
  4295.     Routines to render the preview in background
  4296.  */
  4297. Preview *
  4298. preview_new (gint           width,
  4299.          gint           height,
  4300.          PreviewInitFunc       init_func,
  4301.          gpointer           init_data,
  4302.          PreviewRenderFunc       render_func,
  4303.          gpointer           render_data,
  4304.          PreviewDeinitFunc       deinit_func,
  4305.          gpointer           deinit_data)
  4306. {
  4307.   Preview    *preview;
  4308.  
  4309.   preview = g_new0 (Preview, 1);
  4310.  
  4311.   preview->widget = gtk_preview_new (GTK_PREVIEW_COLOR);
  4312.   gtk_object_set_user_data (GTK_OBJECT (preview->widget), preview);
  4313.   gtk_preview_size (GTK_PREVIEW (preview->widget), width, height);
  4314.   gtk_widget_show (preview->widget);
  4315.  
  4316.   preview->width       = width;
  4317.   preview->height       = height;
  4318.   preview->init_func       = init_func;
  4319.   preview->init_data       = init_data;
  4320.   preview->render_func       = render_func;
  4321.   preview->render_data       = render_data;
  4322.   preview->deinit_func       = deinit_func;
  4323.   preview->deinit_data       = deinit_data;
  4324.   preview->idle_tag       = -1;
  4325.   preview->buffer       = g_new (guchar, width * 3);
  4326.  
  4327.   return preview;
  4328. }
  4329.  
  4330. void
  4331. preview_free (Preview *preview)
  4332. {
  4333.   preview_render_end (preview);
  4334.   /* not destroy preview->widget */
  4335.   g_free (preview->buffer);
  4336.   g_free (preview);
  4337. }
  4338.  
  4339.  
  4340. /*
  4341.   Start rendering of the preview in background using an idle event.
  4342.   If already started and not yet finished, stop it first.
  4343.  */
  4344. void
  4345. preview_render_start (Preview *preview)
  4346. {
  4347.   DEBUG_PRINT(("preview_render_start\n"));
  4348.  
  4349.   preview_render_end (preview);
  4350.  
  4351.   preview->init_done = FALSE;
  4352.   preview->current_y = 0;
  4353.   preview->drawn_y = 0;
  4354.   preview->timeout_tag = gtk_timeout_add (100, (GtkFunction) preview_render_start_2, preview);
  4355. }
  4356.  
  4357. static gint
  4358. preview_render_start_2 (Preview *preview)
  4359. {
  4360.   preview->timeout_tag = -1;
  4361.   preview->idle_tag = gtk_idle_add ((GtkFunction) preview_handle_idle, preview);
  4362.   return FALSE;
  4363. }
  4364.  
  4365.  
  4366. void
  4367. preview_render_end (Preview *preview)
  4368. {
  4369.   if (preview->timeout_tag >= 0)
  4370.     {
  4371.       gtk_timeout_remove (preview->timeout_tag);
  4372.       preview->timeout_tag = -1;
  4373.     }
  4374.   if (preview->idle_tag >= 0)
  4375.     {
  4376.       if (preview->deinit_func)
  4377.     (*preview->deinit_func) (preview, preview->deinit_data);
  4378.  
  4379.       gtk_idle_remove (preview->idle_tag);
  4380.       preview->idle_tag = -1;
  4381.       DEBUG_PRINT(("preview_render_end\n\n"));
  4382.     }
  4383. }
  4384.  
  4385. /*
  4386.   Handle an idle event.
  4387.   Return FALSE if done, TRUE otherwise.
  4388.  */
  4389. static gint
  4390. preview_handle_idle (Preview *preview)
  4391. {
  4392.   gint            done = FALSE;
  4393.   GdkRectangle        draw_rect;
  4394.  
  4395.   if (preview->init_done == FALSE)
  4396.     {
  4397.       if (preview->init_func &&
  4398.       (*preview->init_func) (preview, preview->init_data))
  4399.     return TRUE;
  4400.       preview->init_done = TRUE;
  4401.     }
  4402.  
  4403.   if (preview->render_func)
  4404.     (*preview->render_func) (preview, preview->buffer, preview->current_y,
  4405.                  preview->render_data);
  4406.   else
  4407.     memset (preview->buffer, 0, preview->width * 3);
  4408.  
  4409.   gtk_preview_draw_row (GTK_PREVIEW (preview->widget), preview->buffer,
  4410.             0, preview->current_y, preview->width);
  4411.  
  4412.   if (++preview->current_y >= preview->height)
  4413.     done = TRUE;
  4414.  
  4415.   if (done || preview->current_y % 20 == 0)
  4416.     {
  4417.       draw_rect.x      = 0;
  4418.       draw_rect.y      = preview->drawn_y;
  4419.       draw_rect.width  = preview->width;
  4420.       draw_rect.height = preview->current_y - preview->drawn_y;
  4421.       preview->drawn_y = preview->current_y;
  4422.       gtk_widget_draw (preview->widget, &draw_rect);
  4423.     }
  4424.  
  4425.   if (done)
  4426.     {
  4427.       preview_render_end (preview);
  4428.       return FALSE;
  4429.     }
  4430.  
  4431.   return TRUE;
  4432. }
  4433.  
  4434. /*
  4435.   Convert RGBA to RGB with rendering gray check if needed.
  4436.     (from nova.c)
  4437.   input:  guchar src[4]        RGBA pixel
  4438.   output: guchar dest[3]    RGB pixel
  4439.  */
  4440.  
  4441. void
  4442. preview_rgba_to_rgb (guchar *dest, gint x, gint y, guchar *src)
  4443. {
  4444.   gint src_a;
  4445.   gint check;
  4446.   gint b;
  4447.  
  4448.   src_a = src[3];
  4449.  
  4450.   if (src_a == OPAQUE)    /* full opaque */
  4451.     {
  4452.       for (b = 0; b < 3; b++)
  4453.     dest[b] = src[b];
  4454.     }
  4455.   else
  4456.     {
  4457.       if ((x % (CHECK_SIZE*2) < CHECK_SIZE) ^
  4458.       (y % (CHECK_SIZE*2) < CHECK_SIZE))
  4459.     check = LIGHTCHECK;
  4460.       else
  4461.     check = DARKCHECK;
  4462.  
  4463.       if (src_a == 0)    /* full transparent */
  4464.     {
  4465.       for (b = 0; b < 3; b++)
  4466.         dest[b] = check;
  4467.     }
  4468.       else
  4469.     {
  4470.       for (b = 0; b < 3; b++)
  4471.         dest[b] = (src[b] * src_a + check * (OPAQUE-src_a)) / OPAQUE;
  4472.     }
  4473.     }
  4474. }
  4475.  
  4476. /*************************************************************************/
  4477. /**                                    **/
  4478. /**            +++ Gradient Menu                **/
  4479. /**            +++ gm                        **/
  4480. /**                                    **/
  4481. /*************************************************************************/
  4482.  
  4483. void
  4484. gradient_menu_init ()
  4485. {
  4486.   gm_gradient_get_list ();
  4487.   gradient_menus = NULL;
  4488. }
  4489.  
  4490. void
  4491. gradient_menu_rescan ()
  4492. {
  4493.   GList        *tmp;
  4494.   GradientMenu    *gm;
  4495.   GtkWidget    *menu;
  4496.  
  4497.   /* Detach and destroy menus first */
  4498.   tmp = gradient_menus;
  4499.   while (tmp)
  4500.     {
  4501.       gm  = tmp->data;
  4502.       tmp = tmp->next;
  4503.       menu = GTK_MULTI_OPTION_MENU (gm->option_menu)->menu;
  4504.       if (menu)
  4505.     {
  4506.       gtk_multi_option_menu_remove_menu (GTK_MULTI_OPTION_MENU (gm->option_menu));
  4507.       gtk_widget_destroy (menu);
  4508.     }
  4509.     }
  4510.  
  4511.   /* reget list of gradient names */
  4512.   gm_gradient_get_list ();
  4513.  
  4514.   /* Create menus and attach them again */
  4515.   tmp = gradient_menus;
  4516.   while (tmp)
  4517.     {
  4518.       GtkWidget *parent;
  4519.  
  4520.       gm  = tmp->data;
  4521.       tmp = tmp->next;
  4522.       /* @GRADIENT_NAME */
  4523.       menu = gm_menu_new (gm, gm->gradient_name);
  4524.  
  4525.       /*
  4526.     FIXME:
  4527.     This is a kind of hack so that it doesn't mess up gtknotebook
  4528.     to set menu into an option menu which is not shown (but
  4529.     GTK_WIDGET_VISIBLE)
  4530.        */
  4531.       parent = gm->option_menu->parent;
  4532.       if (0 && (parent != NULL) && GTK_CHECK_TYPE (parent, gtk_container_get_type ()))
  4533.     {
  4534.         gtk_container_block_resize (GTK_CONTAINER (parent));
  4535.         gtk_multi_option_menu_set_menu (GTK_MULTI_OPTION_MENU (gm->option_menu), menu);
  4536.         gtk_container_unblock_resize (GTK_CONTAINER (parent));
  4537.     }
  4538.       else
  4539.         gtk_multi_option_menu_set_menu (GTK_MULTI_OPTION_MENU (gm->option_menu), menu);
  4540.     }
  4541. }
  4542.  
  4543. GradientMenu *
  4544. gradient_menu_new (GradientMenuCallback callback,
  4545.            gpointer            callback_data,
  4546.            gchar            *default_gradient_name)
  4547. {
  4548.   GtkWidget    *menu;
  4549.   GradientMenu    *gm;
  4550.  
  4551.   gm = g_new (GradientMenu, 1);
  4552.  
  4553.   gm->callback = callback;
  4554.   gm->callback_data = callback_data;
  4555.  
  4556.   gm->preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  4557.   gtk_preview_size( GTK_PREVIEW (gm->preview),
  4558.             GM_PREVIEW_WIDTH,
  4559.             GM_PREVIEW_HEIGHT );
  4560.  
  4561.   gm->option_menu = gtk_multi_option_menu_new ();
  4562.  
  4563.   /* @GRADIENT_NAME */
  4564.   menu = gm_menu_new (gm, default_gradient_name);
  4565.   gtk_multi_option_menu_set_menu (GTK_MULTI_OPTION_MENU (gm->option_menu), menu);
  4566.  
  4567.   gtk_widget_show (gm->preview);
  4568.   gtk_widget_show (gm->option_menu);
  4569.  
  4570.   gradient_menus = g_list_append (gradient_menus, gm);
  4571.   gtk_signal_connect (GTK_OBJECT (gm->option_menu), "destroy",
  4572.               (GtkSignalFunc) &gm_option_menu_destroy_callback, gm);
  4573.  
  4574.   return gm;
  4575. }
  4576.  
  4577. void
  4578. gradient_menu_destroy (GradientMenu *gm)
  4579. {
  4580.   gtk_widget_destroy (gm->preview);
  4581.   gtk_widget_destroy (gm->option_menu);      /* gm is removed from gradient_menus too */
  4582.  
  4583.   g_free (gm);
  4584. }
  4585.  
  4586. /* Local Functions */
  4587.  
  4588. static void
  4589. gm_gradient_get_list ()
  4590. {
  4591.   int    i;
  4592.  
  4593.   if (gradient_names)
  4594.     {
  4595.       for (i = 0; i < num_gradient_names; i++)
  4596.     g_free (gradient_names[i]);
  4597.       g_free (gradient_names);
  4598.     }
  4599.  
  4600.   gradient_cache_flush ();    /* to make sure */
  4601.   gradient_names = gradient_get_list (&num_gradient_names);
  4602. }
  4603.  
  4604. /*
  4605.  *  Create GtkMenu and arrange GtkMenuItem's of gradient names into it
  4606.  */
  4607.  
  4608. static GtkWidget *
  4609. gm_menu_new (GradientMenu *gm, gchar *default_gradient_name )
  4610. {
  4611.   GtkWidget    *menu;
  4612.   GtkWidget    *menuitem;
  4613.   gchar        *active_name;
  4614.  
  4615.   menu = gtk_menu_new ();
  4616.  
  4617.   if (num_gradient_names == 0)
  4618.     {
  4619.       menuitem = gtk_menu_item_new_with_label ("none");
  4620.       gtk_widget_set_sensitive (menuitem, FALSE);
  4621.       gtk_widget_show (menuitem);
  4622.       gtk_menu_append (GTK_MENU (menu), menuitem);
  4623.       gtk_menu_set_active (GTK_MENU (menu), 0);
  4624.     }
  4625.   else /* num_gradient_names == 0 */
  4626.     {
  4627.       menu = gm_menu_create_sub_menus (gm, 0, &active_name,
  4628.                        default_gradient_name);
  4629.       if (active_name == NULL)
  4630.     {
  4631.       active_name = gradient_names[0];
  4632.       g_warning ("Not found \"%s\": used \"%s\" instead",
  4633.              default_gradient_name, active_name);
  4634.     }
  4635.  
  4636.       gradient_name_copy (gm->gradient_name, active_name);
  4637.       gm_preview_draw (gm->preview, active_name);
  4638.       if ( GTK_WIDGET_VISIBLE (gm->preview) && GTK_WIDGET_MAPPED(gm->preview))
  4639.     {
  4640.       DEBUG_PRINT (("gm_menu_new: preview is visible and mapped\n"));
  4641.       gtk_widget_draw (gm->preview, NULL);
  4642.     }
  4643.       if (gm->callback)
  4644.     (* gm->callback) ( active_name, gm->callback_data );
  4645.  
  4646.     } /* num_gradient_names == 0 */
  4647.  
  4648.   return menu;
  4649. }
  4650.  
  4651. static GtkWidget *
  4652. gm_menu_create_sub_menus (GradientMenu *gm,
  4653.               gint start_n,
  4654.               gchar **active_name_ptr,
  4655.               gchar *default_gradient_name)
  4656. {
  4657.   GtkWidget    *menu, *sub_menu;
  4658.   gchar        *sub_active_name;
  4659.   GtkWidget    *menuitem;
  4660.   gchar        *name;
  4661.   gint        active_i = 0;
  4662.   gint        i, n;
  4663.  
  4664.   *active_name_ptr = NULL;
  4665.   if (start_n >= num_gradient_names)
  4666.     {
  4667.       return NULL;
  4668.     }
  4669.  
  4670.   /* gradient_names[] are malloced strings and alive during
  4671.      this menuitem lives */
  4672.  
  4673.   menu = gtk_menu_new ();
  4674.   for (i = 0, n = start_n; i < GM_MENU_MAX && n < num_gradient_names; i++, n++)
  4675.     {
  4676.       name = gradient_names[n];
  4677.       if (strcmp (name, default_gradient_name) == 0)
  4678.     {
  4679.       active_i = i;
  4680.       *active_name_ptr = name;
  4681.     }
  4682.       menuitem = gtk_menu_item_new_with_label (name);
  4683.       gtk_object_set_user_data (GTK_OBJECT (menuitem), gm);
  4684.       gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
  4685.               (GtkSignalFunc) gm_menu_item_callback,
  4686.               name);
  4687.       gtk_menu_append (GTK_MENU (menu), menuitem);
  4688.       gtk_widget_show (menuitem);
  4689.     } /* for */
  4690.  
  4691.   sub_menu = gm_menu_create_sub_menus (gm, n, &sub_active_name,
  4692.                        default_gradient_name);
  4693.   if (sub_menu)
  4694.     {
  4695.       active_i += 2;
  4696.  
  4697.       /* hline */
  4698.       menuitem = gtk_menu_item_new ();
  4699.       gtk_widget_show (menuitem);
  4700.       gtk_menu_prepend (GTK_MENU (menu), menuitem);
  4701.  
  4702.       menuitem = gtk_menu_item_new_with_label ("More...");
  4703.       gtk_widget_show (menuitem);
  4704.       gtk_menu_prepend (GTK_MENU (menu), menuitem);
  4705.       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), sub_menu);
  4706.  
  4707.       if (sub_active_name)
  4708.     {
  4709.       *active_name_ptr = sub_active_name;
  4710.       active_i = 0; /* "More ..." */
  4711.     }
  4712.     }
  4713.  
  4714.   gtk_menu_set_active (GTK_MENU (menu), active_i);
  4715.   return menu;
  4716. }
  4717.  
  4718. static void
  4719. gm_menu_item_callback (GtkWidget *w, gpointer data)
  4720. {
  4721.   GradientMenu        *gm;
  4722.   gchar            *gradient_name = (gchar *) data;
  4723.  
  4724.   DEBUG_PRINT(("gm_menu_item_callback\n"));
  4725.  
  4726.   gm =    (GradientMenu *) gtk_object_get_user_data (GTK_OBJECT (w));
  4727.   gradient_name_copy (gm->gradient_name, gradient_name);
  4728.  
  4729.   gm_preview_draw (gm->preview, gradient_name);
  4730.   if (GTK_WIDGET_VISIBLE (gm->preview) && GTK_WIDGET_MAPPED(gm->preview))
  4731.     {
  4732.       DEBUG_PRINT(("gm_menu_item_callback: preview is visible and mapped\n"));
  4733.       gtk_widget_draw (gm->preview, NULL);
  4734.     }
  4735.  
  4736.   if (gm->callback)
  4737.     (* gm->callback) ( gradient_name, gm->callback_data );
  4738. }
  4739.  
  4740. static void
  4741. gm_preview_draw (GtkWidget *preview, gchar *gradient_name)
  4742. {
  4743.   guchar    values[GM_PREVIEW_WIDTH][4];
  4744.   gint        nvalues = GM_PREVIEW_WIDTH;
  4745.   int        row, irow, col;
  4746.   guchar    dest_row[GM_PREVIEW_WIDTH][3];
  4747.   guchar    *dest, *src;
  4748.   int        check, b;
  4749.   const int    alpha = 3;
  4750.  
  4751.   gradient_get_values (gradient_name, (guchar *)values, nvalues);
  4752.  
  4753.   for( row = 0; row < GM_PREVIEW_HEIGHT; row += CHECK_SIZE )
  4754.     {
  4755.       for( col = 0; col < GM_PREVIEW_WIDTH; col++ )
  4756.     {
  4757.       dest = dest_row[col];
  4758.       src = values[col];
  4759.  
  4760.       if( src[alpha] == OPAQUE )
  4761.         {
  4762.           /* no alpha channel or opaque -- simple way */
  4763.           for ( b = 0; b < alpha; b++ )
  4764.         dest[b] = src[b];
  4765.         }
  4766.       else
  4767.         {
  4768.           /* more or less transparent */
  4769.           if( ( col % (CHECK_SIZE*2) < CHECK_SIZE ) ^
  4770.           ( row % (CHECK_SIZE*2) < CHECK_SIZE ) )
  4771.         check = LIGHTCHECK;
  4772.           else
  4773.         check = DARKCHECK;
  4774.  
  4775.           if ( src[alpha] == 0 )
  4776.         {
  4777.           /* full transparent -- check */
  4778.           for ( b = 0; b < alpha; b++ )
  4779.             dest[b] = check;
  4780.         }
  4781.           else
  4782.         {
  4783.           /* middlemost transparent -- mix check and src */
  4784.           for ( b = 0; b < alpha; b++ )
  4785.             dest[b] = ( src[b]*src[alpha] + check*(OPAQUE-src[alpha]) ) / OPAQUE;
  4786.         }
  4787.         }
  4788.     }
  4789.       for( irow = 0; irow < CHECK_SIZE && row + irow < GM_PREVIEW_HEIGHT; irow++ )
  4790.     {
  4791.       gtk_preview_draw_row( GTK_PREVIEW (preview), (guchar*) dest_row,
  4792.                 0, row + irow, GM_PREVIEW_WIDTH );
  4793.     }
  4794.     }
  4795.  
  4796. }
  4797.  
  4798. static void
  4799. gm_option_menu_destroy_callback (GtkWidget *w, gpointer data)
  4800. {
  4801.   GradientMenu *gm = data;
  4802.   gradient_menus = g_list_remove (gradient_menus, gm);
  4803. }
  4804.  
  4805. /*************************************************************************/
  4806. /**                                    **/
  4807. /**            +++ Gradients                    **/
  4808. /**                                    **/
  4809. /*************************************************************************/
  4810.  
  4811. /*
  4812.     Manage both internal and external gradients: list up, cache,
  4813.     sampling, etc.
  4814.  
  4815.     External gradients are cached.
  4816.  */
  4817.  
  4818.  
  4819. void
  4820. gradient_name_copy (gchar *dest, gchar *src)
  4821. {
  4822.   strncpy (dest, src, GRADIENT_NAME_MAX);
  4823.   dest[GRADIENT_NAME_MAX-1] = '\0';
  4824. }
  4825.  
  4826. /*
  4827.   Translate SPACE to "\\040", etc.
  4828.  */
  4829. void
  4830. gradient_name_encode (guchar *dest, guchar *src)
  4831. {
  4832.   int    cnt = GRADIENT_NAME_MAX - 1;
  4833.  
  4834.   while (*src && cnt--)
  4835.     {
  4836.       if (iscntrl (*src) || isspace (*src) || *src == '\\')
  4837.     {
  4838.       sprintf (dest, "\\%03o", *src++);
  4839.       dest += 4;
  4840.     }
  4841.       else
  4842.     *dest++ = *src++;
  4843.     }
  4844.   *dest = '\0';
  4845. }
  4846.  
  4847. /*
  4848.   Translate "\\040" to SPACE, etc.
  4849.  */
  4850. void
  4851. gradient_name_decode (guchar *dest, guchar *src)
  4852. {
  4853.   int    cnt = GRADIENT_NAME_MAX - 1;
  4854.   int    tmp;
  4855.  
  4856.   while (*src && cnt--)
  4857.     {
  4858.       if (*src == '\\' && *(src+1) && *(src+2) && *(src+3))
  4859.     {
  4860.       sscanf (src+1, "%3o", &tmp);
  4861.       *dest++ = tmp;
  4862.       src += 4;
  4863.     }
  4864.       else
  4865.     *dest++ = *src++;
  4866.     }
  4867.   *dest = '\0';
  4868. }
  4869.  
  4870.  
  4871. void
  4872. gradient_init ()
  4873. {
  4874.   gradient_cache_head = NULL;
  4875.   gradient_cache_count = 0;
  4876. }
  4877.  
  4878. void
  4879. gradient_free ()
  4880. {
  4881.   gradient_cache_flush ();
  4882. }
  4883.  
  4884. char **
  4885. gradient_get_list (gint *num_gradients)
  4886. {
  4887.   gchar          **gradients;
  4888.   gchar          **external_gradients = NULL;
  4889.   gint          external_ngradients = 0;
  4890.   gint          i, n;
  4891.  
  4892.  
  4893.   gradient_cache_flush ();
  4894.   external_gradients = gimp_gradients_get_list (&external_ngradients);
  4895.  
  4896.   *num_gradients = internal_ngradients + external_ngradients;
  4897.   gradients = g_new (gchar *, *num_gradients);
  4898.  
  4899.   n = 0;
  4900.   for (i = 0; i < internal_ngradients; i++)
  4901.     {
  4902.       gradients[n++] = g_strdup (internal_gradients[i]);
  4903.     }
  4904.   for (i = 0; i < external_ngradients; i++)
  4905.     {
  4906.       gradients[n++] = g_strdup (external_gradients[i]);
  4907.     }
  4908.  
  4909.   return gradients;
  4910. }
  4911.  
  4912. void
  4913. gradient_get_values (gchar *gradient_name, guchar *values, gint nvalues)
  4914. {
  4915.   /* DEBUG_PRINT (("gradient_get_values: %s %d\n", gradient_name, nvalues)); */
  4916.  
  4917.   /*
  4918.     Criteria to distinguish internal and external is rather simple here.
  4919.     It should be fixed later.
  4920.    */
  4921.   if (gradient_name[0] == '%')
  4922.     gradient_get_values_internal (gradient_name, values, nvalues);
  4923.   else
  4924.     gradient_get_values_external (gradient_name, values, nvalues);
  4925. }
  4926.  
  4927. static void
  4928. gradient_get_values_internal (gchar *gradient_name, guchar *values, gint nvalues)
  4929. {
  4930.   static guchar white[4] = {255,255,255,255};
  4931.   static guchar white_trans[4] = {255,255,255,0};
  4932.   static guchar red_trans[4] = {255,0,0,0};
  4933.   static guchar blue_trans[4] = {0,0,255,0};
  4934.   static guchar yellow_trans[4] = {255,255,0,0};
  4935.  
  4936.   /*
  4937.     The internal gradients here are example --
  4938.     What kind of internals would be useful ?
  4939.    */
  4940.   if( !strcmp(gradient_name, "%white"))
  4941.     {
  4942.       gradient_get_blend (white, white, values, nvalues);
  4943.     }
  4944.   else if( !strcmp(gradient_name, "%white_grad"))
  4945.     {
  4946.       gradient_get_blend (white, white_trans, values, nvalues);
  4947.     }
  4948.   else if (!strcmp (gradient_name, "%red_grad" ))
  4949.     {
  4950.       gradient_get_blend (white, red_trans, values, nvalues);
  4951.     }
  4952.   else if (!strcmp (gradient_name, "%blue_grad" ))
  4953.     {
  4954.       gradient_get_blend (white, blue_trans, values, nvalues);
  4955.     }
  4956.   else if (!strcmp (gradient_name, "%yellow_grad" ))
  4957.     {
  4958.       gradient_get_blend (white, yellow_trans, values, nvalues);
  4959.     }
  4960.   else if (!strcmp (gradient_name, "%random" ))
  4961.     {
  4962.       gradient_get_random (1, values, nvalues);
  4963.     }
  4964.   else
  4965.     {
  4966.       gradient_get_default (gradient_name, values, nvalues);
  4967.     }
  4968. }
  4969.  
  4970. static void
  4971. gradient_get_blend (guchar *fg, guchar *bg, guchar *values, gint nvalues)
  4972. {
  4973.   gdouble    x;
  4974.   int        i, j;
  4975.   guchar    *v = values;
  4976.  
  4977.   for (i=0; i<nvalues; i++)
  4978.     {
  4979.       x = (double) i / nvalues;
  4980.       for (j = 0; j < 4; j++)
  4981.     *v++ = fg[j] * (1 - x) + bg[j] * x;
  4982.     }
  4983. }
  4984.  
  4985. static void
  4986. gradient_get_random (gint seed, guchar *values, gint nvalues)
  4987. {
  4988.   int        i, j;
  4989.   int        inten;
  4990.   guchar    *v = values;
  4991.  
  4992.   /*
  4993.     This is really simple  -- gaussian noise might be better
  4994.    */
  4995.   srand (seed);
  4996.   for (i = 0; i < nvalues; i++)
  4997.     {
  4998.       inten = rand () % 256;
  4999.       for (j = 0; j < 3; j++)
  5000.     *v++ = inten;
  5001.       *v++ = 255;
  5002.     }
  5003. }
  5004.  
  5005. static void
  5006. gradient_get_default (gchar *name, guchar *values, gint nvalues)
  5007. {
  5008.   double    e[3];
  5009.   double    x;
  5010.   int        i, j;
  5011.   guchar    *v = values;
  5012.  
  5013.   /*
  5014.     Create gradient by name
  5015.    */
  5016.   name++;
  5017.   for (j = 0; j < 3; j++)
  5018.     e[j] = name[j] / 255.0;
  5019.  
  5020.   for (i = 0; i < nvalues; i++)
  5021.     {
  5022.       x = (double) i / nvalues;
  5023.       for (j = 0; j < 3; j++)
  5024.     *v++ = 255 * pow (x, e[j]);
  5025.       *v++ = 255;
  5026.     }
  5027. }
  5028.  
  5029. /*
  5030.   Caching gradients is really needed. It really takes 0.2 seconds each
  5031.   time to resample an external gradient. (And this plug-in has
  5032.   currently 6 gradient menus.)
  5033.  
  5034.   However, this caching routine is not too good. It picks up just
  5035.   GRADIENT_RESOLUTION samples everytime, and rescales it later.     And
  5036.   cached values are stored in guchar array. No accuracy.
  5037.  */
  5038. static void
  5039. gradient_get_values_external (gchar *gradient_name, guchar *values, gint nvalues)
  5040. {
  5041.   GradientCacheItem *ci;
  5042.   gint            found;
  5043. #ifdef DEBUG
  5044.   clock_t    clk = clock ();
  5045. #endif
  5046.  
  5047.   g_return_if_fail (nvalues >= 2);
  5048.  
  5049.   ci = gradient_cache_lookup (gradient_name, &found);
  5050.   if (!found)
  5051.     {
  5052.       gradient_get_values_real_external (gradient_name, ci->values, GRADIENT_RESOLUTION);
  5053.     }
  5054.   if (nvalues == GRADIENT_RESOLUTION)
  5055.     {
  5056.       memcpy (values, ci->values, 4 * GRADIENT_RESOLUTION);
  5057.     }
  5058.   else
  5059.     {
  5060.       double    pos, frac;
  5061.       int    ipos;
  5062.       int    i, j;
  5063.  
  5064.       for (i = 0; i < nvalues; i++)
  5065.     {
  5066.       pos = ((double) i / (nvalues - 1)) * (GRADIENT_RESOLUTION - 1);
  5067.       g_assert (0 <= pos && pos <= GRADIENT_RESOLUTION - 1);
  5068.       ipos = (int) pos; frac = pos - ipos;
  5069.       if (frac == 0.0)
  5070.         {
  5071.           memcpy (&values[4 * i], &ci->values[4 * ipos], 4);
  5072.         }
  5073.       else
  5074.         for (j = 0; j < 4; j++)
  5075.           values[4 * i + j] = ci->values[4 * ipos + j] * (1 - frac)
  5076.                 + ci->values[4 * (ipos + 1) + j] * frac;
  5077.     }
  5078.     }
  5079.  
  5080. #ifdef DEBUG
  5081.   get_values_external_clock += clock () - clk;
  5082.   get_values_external_count ++;
  5083. #endif
  5084.  
  5085. }
  5086.  
  5087. static void
  5088. gradient_get_values_real_external (gchar *gradient_name, guchar *values, gint nvalues)
  5089. {
  5090.   gchar        *old_name;
  5091.   gdouble    *tmp_values;
  5092.   int        i, j;
  5093.  
  5094.   old_name = gimp_gradients_get_active ();
  5095.  
  5096.   gimp_gradients_set_active (gradient_name);
  5097.  
  5098.   tmp_values = gimp_gradients_sample_uniform (nvalues);
  5099.   for (i = 0; i < nvalues; i++)
  5100.     for (j = 0; j < 4; j++)
  5101.       values[4*i+j] = (guchar) (tmp_values[4*i+j] * 255);
  5102.  
  5103.   gimp_gradients_set_active (old_name);
  5104.  
  5105.   g_free (tmp_values);
  5106.   g_free (old_name);
  5107. }
  5108.  
  5109. void
  5110. gradient_cache_flush ()
  5111. {
  5112.   GradientCacheItem    *ci, *tmp;
  5113.  
  5114.   ci = gradient_cache_head;
  5115.   while (ci)
  5116.     {
  5117.       tmp = ci->next;
  5118.       g_free (ci);
  5119.       ci = tmp;
  5120.     }
  5121.   gradient_cache_head = NULL;
  5122.   gradient_cache_count = 0;
  5123. }
  5124.  
  5125. static GradientCacheItem *
  5126. gradient_cache_lookup (gchar *name, gint *found)
  5127. {
  5128.   GradientCacheItem    *ci;
  5129.  
  5130.   ci = gradient_cache_head;
  5131.   while (ci)
  5132.     {
  5133.       if (!strcmp (ci->name, name))
  5134.     break;
  5135.       ci = ci->next;
  5136.     }
  5137.   if (ci)
  5138.     {
  5139.       *found = TRUE;
  5140.       if (!ci->prev)
  5141.     {
  5142.       g_assert (ci == gradient_cache_head);
  5143.       return ci;
  5144.     }
  5145.       ci->prev->next = ci->next;
  5146.       if (ci->next)
  5147.     ci->next->prev = ci->prev;
  5148.       ci->next = gradient_cache_head;
  5149.       gradient_cache_head->prev = ci;
  5150.       gradient_cache_head = ci;
  5151.       ci->prev = NULL;
  5152.       return ci;
  5153.     }
  5154.   else
  5155.     {
  5156.       *found = FALSE;
  5157.       while (gradient_cache_count >= GRADIENT_CACHE_SIZE)
  5158.     gradient_cache_zorch();
  5159.       ci = g_new (GradientCacheItem, 1);
  5160.       strncpy (ci->name, name, GRADIENT_NAME_MAX - 1);
  5161.       ci->next = gradient_cache_head;
  5162.       ci->prev = NULL;
  5163.       if (gradient_cache_head)
  5164.     gradient_cache_head->prev = ci;
  5165.       gradient_cache_head = ci;
  5166.       ++gradient_cache_count;
  5167.       return ci;
  5168.     }
  5169. }
  5170.  
  5171. static void
  5172. gradient_cache_zorch ()
  5173. {
  5174.   GradientCacheItem    *ci = gradient_cache_head;
  5175.  
  5176.   while (ci && ci->next)
  5177.     {
  5178.       ci = ci->next;
  5179.     }
  5180.   if (ci)
  5181.     {
  5182.       g_assert (ci->next == NULL);
  5183.       if (ci->prev)
  5184.     ci->prev->next = NULL;
  5185.       else
  5186.     gradient_cache_head = NULL;
  5187.       g_free (ci);
  5188.       --gradient_cache_count;
  5189.     }
  5190. }
  5191.  
  5192. #ifdef DEBUG
  5193. void
  5194. gradient_report ()
  5195. {
  5196.   double total = (double) get_values_external_clock / CLOCKS_PER_SEC;
  5197.  
  5198.   printf( "gradient_get_values_external %.2f sec. / %d times (ave %.2f sec.)\n",
  5199.       total,
  5200.       get_values_external_count,
  5201.       total / get_values_external_count );
  5202. }
  5203. #endif
  5204.  
  5205. /*************************************************************************/
  5206. /**                                    **/
  5207. /**            +++ Entscale                    **/
  5208. /**                                    **/
  5209. /*************************************************************************/
  5210.  
  5211. /* these routines are taken from my private subroutine collection. */
  5212.  
  5213. /* -*- mode:c -*- */
  5214. /*
  5215.   Entry and Scale pair (int and double integrated)
  5216.  
  5217.   This is an attempt to combine entscale_int and double.
  5218.   Never compiled yet.
  5219.  
  5220.   TODO:
  5221.   - Do the proper thing when the user changes value in entry,
  5222.   so that callback should not be called when value is actually not changed.
  5223.   - Update delay
  5224.  */
  5225.  
  5226. /* $Id: gflare.c,v 1.1 1998/02/20 08:48:28 yosh Exp $ */
  5227.  
  5228.  
  5229. static void   entscale_destroy_callback (GtkWidget *widget,
  5230.                      gpointer data);
  5231. static void   entscale_scale_update (GtkAdjustment *adjustment,
  5232.                      gpointer       data);
  5233. static void   entscale_entry_update (GtkWidget *widget,
  5234.                      gpointer    data);
  5235. /*
  5236.  *  Create an entry, a scale and a label, then attach them to
  5237.  *  specified table.
  5238.  *  1 row and 2 cols of table are needed.
  5239.  *
  5240.  *  Input:
  5241.  *    table:      table which entscale is attached to
  5242.  *    x, y:      starting row and col in table
  5243.  *    caption:      label string
  5244.  *    type:      type of variable (ENTSCALE_INT or ENTSCALE_DOUBLE)
  5245.  *    variable:      pointer to variable
  5246.  *    min, max:      boundary of scale
  5247.  *    step:      step of scale (ignored when (type == ENTSCALE_INT))
  5248.  *    constraint: (bool) true iff the value of *variable should be
  5249.  *          constraint by min and max
  5250.  *    callback:      called when the value is actually changed
  5251.  *          (*variable is automatically changed, so there's no
  5252.  *          need of callback func usually.)
  5253.  *    call_data:  data for callback func
  5254.  */
  5255.  
  5256. void
  5257. entscale_new (GtkWidget *table, gint x, gint y, gchar *caption,
  5258.           EntscaleType type, gpointer variable,
  5259.           gdouble min, gdouble max, gdouble step,
  5260.           gint constraint,
  5261.           EntscaleCallbackFunc callback,
  5262.           gpointer call_data)
  5263. {
  5264.   Entscale    *entscale;
  5265.   GtkWidget    *hbox;
  5266.   GtkWidget    *label;
  5267.   GtkWidget    *entry;
  5268.   GtkWidget    *scale;
  5269.   GtkObject    *adjustment;
  5270.   gchar        buffer[256];
  5271.   gdouble    val;
  5272.   gdouble    constraint_val;
  5273.  
  5274.   entscale = g_new0 (Entscale, 1 );
  5275.   entscale->type    = type;
  5276.   entscale->constraint    = constraint;
  5277.   entscale->callback    = callback;
  5278.   entscale->call_data    = call_data;
  5279.  
  5280.   switch (type)
  5281.     {
  5282.     case ENTSCALE_INT:
  5283.       step = 1.0;
  5284.       strcpy (entscale->fmt_string, "%.0f");
  5285.       val = *(gint *)variable;
  5286.       break;
  5287.     case ENTSCALE_DOUBLE:
  5288.       sprintf (entscale->fmt_string, "%%.%df", entscale_get_precision (step) + 1);
  5289.       val = *(gdouble *)variable;
  5290.       break;
  5291.     default:
  5292.       g_error ("TYPE must be either ENTSCALE_INT or ENTSCALE_DOUBLE");
  5293.       val = 0;
  5294.       break;
  5295.     }
  5296.  
  5297.   label = gtk_label_new (caption);
  5298.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  5299.  
  5300.   /*
  5301.     If the first arg of gtk_adjustment_new() isn't between min and
  5302.     max, it is automatically corrected by gtk later with
  5303.     "value_changed" signal. I don't like this, since I want to leave
  5304.     *variable untouched when `constraint' is false.
  5305.     The lines below might look oppositely, but this is OK.
  5306.    */
  5307.   if (constraint)
  5308.     constraint_val = val;
  5309.   else
  5310.     constraint_val = BOUNDS (val, min, max);
  5311.  
  5312.   adjustment = entscale->adjustment =
  5313.     gtk_adjustment_new (constraint_val, min, max, step, step, 0.0);
  5314.   scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
  5315.   gtk_widget_set_usize (scale, ENTSCALE_SCALE_WIDTH, 0);
  5316.   gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
  5317.  
  5318.   entry = entscale->entry = gtk_entry_new ();
  5319.   gtk_widget_set_usize (entry, ENTSCALE_ENTRY_WIDTH, 0);
  5320.   sprintf (buffer, entscale->fmt_string, val);
  5321.   gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  5322.  
  5323.   /* entscale is done */
  5324.   gtk_object_set_user_data (GTK_OBJECT(adjustment), entscale);
  5325.   gtk_object_set_user_data (GTK_OBJECT(entry), entscale);
  5326.  
  5327.   /* now ready for signals */
  5328.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  5329.               (GtkSignalFunc) entscale_entry_update,
  5330.               variable);
  5331.   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
  5332.               (GtkSignalFunc) entscale_scale_update,
  5333.               variable);
  5334.   gtk_signal_connect (GTK_OBJECT (entry), "destroy",
  5335.               (GtkSignalFunc) entscale_destroy_callback,
  5336.               NULL );
  5337.  
  5338.   /* start packing */
  5339.   hbox = gtk_hbox_new (FALSE, 5);
  5340.   gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
  5341.   gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, TRUE, 0);
  5342.  
  5343.   gtk_table_attach (GTK_TABLE (table), label, x, x+1, y, y+1,
  5344.             GTK_FILL, GTK_FILL, 0, 0);
  5345.   gtk_table_attach (GTK_TABLE (table), hbox, x+1, x+2, y, y+1,
  5346.             GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  5347.  
  5348.   gtk_widget_show (label);
  5349.   gtk_widget_show (entry);
  5350.   gtk_widget_show (scale);
  5351.   gtk_widget_show (hbox);
  5352. }
  5353.  
  5354. static int
  5355. entscale_get_precision (gdouble step)
  5356. {
  5357.   int precision;
  5358.   if (step <= 0)
  5359.     return 0;
  5360.   for (precision = 0; pow (0.1, precision) > step; precision++) ;
  5361.   return precision;
  5362. }
  5363.  
  5364. static void
  5365. entscale_destroy_callback (GtkWidget *widget,
  5366.                gpointer data)
  5367. {
  5368.   Entscale *entscale;
  5369.  
  5370.   entscale = gtk_object_get_user_data (GTK_OBJECT (widget));
  5371.   g_free (entscale);
  5372. }
  5373. static void
  5374. entscale_scale_update (GtkAdjustment *adjustment,
  5375.                gpointer         data)
  5376. {
  5377.   Entscale    *entscale;
  5378.   GtkEntry    *entry;
  5379.   gchar        buffer[256];
  5380.   gdouble    old_val, new_val;
  5381.  
  5382.   entscale = gtk_object_get_user_data (GTK_OBJECT (adjustment));
  5383.  
  5384.   new_val = adjustment->value;
  5385.   /* adjustmet->value is always constrainted */
  5386.  
  5387.   switch (entscale->type)
  5388.     {
  5389.     case ENTSCALE_INT:
  5390.       old_val = *(gint*) data;
  5391.       *(gint*) data = (gint) new_val;
  5392.       DEBUG_PRINT (("entscale_scale_update(int): fmt=\"%s\" old=%g new=%g\n",
  5393.             entscale->fmt_string, old_val, new_val));
  5394.       break;
  5395.     case ENTSCALE_DOUBLE:
  5396.       old_val = *(gdouble*) data;
  5397.       *(gdouble*) data = new_val;
  5398.       DEBUG_PRINT (("entscale_scale_update(double): fmt=\"%s\" old=%g new=%g\n",
  5399.             entscale->fmt_string, old_val, new_val));
  5400.       break;
  5401.     default:
  5402.       g_warning ("entscale_scale_update: invalid type");
  5403.       return;
  5404.     }
  5405.  
  5406.   entry = GTK_ENTRY (entscale->entry);
  5407.   sprintf (buffer, entscale->fmt_string, new_val);
  5408.  
  5409.   /* avoid infinite loop (scale, entry, scale, entry ...) */
  5410.   gtk_signal_handler_block_by_data (GTK_OBJECT(entry), data);
  5411.   gtk_entry_set_text (entry, buffer);
  5412.   gtk_signal_handler_unblock_by_data (GTK_OBJECT(entry), data);
  5413.  
  5414.   if (entscale->callback && (old_val != new_val))
  5415.     (*entscale->callback) (new_val, entscale->call_data);
  5416. }
  5417.  
  5418. static void
  5419. entscale_entry_update (GtkWidget *widget,
  5420.                gpointer      data)
  5421. {
  5422.   Entscale    *entscale;
  5423.   GtkAdjustment *adjustment;
  5424.   gdouble    old_val, val, new_val, constraint_val;
  5425.  
  5426.   entscale = gtk_object_get_user_data (GTK_OBJECT (widget));
  5427.   adjustment = GTK_ADJUSTMENT (entscale->adjustment);
  5428.  
  5429.   val = atof (gtk_entry_get_text (GTK_ENTRY (widget)));
  5430.  
  5431.   constraint_val = BOUNDS (val, adjustment->lower, adjustment->upper);
  5432.  
  5433.   if (entscale->constraint)
  5434.     new_val = constraint_val;
  5435.   else
  5436.     new_val = val;
  5437.  
  5438.   switch (entscale->type)
  5439.     {
  5440.     case ENTSCALE_INT:
  5441.       old_val = *(gint*) data;
  5442.       *(gint*) data = (gint) new_val;
  5443.       DEBUG_PRINT (("entscale_entry_update(int): old=%g new=%g const=%g\n",
  5444.             old_val, new_val, constraint_val));
  5445.       break;
  5446.     case ENTSCALE_DOUBLE:
  5447.       old_val = *(gdouble*) data;
  5448.       *(gdouble*) data = new_val;
  5449.       DEBUG_PRINT (("entscale_entry_update(double): old=%g new=%g const=%g\n",
  5450.             old_val, new_val, constraint_val));
  5451.       break;
  5452.     default:
  5453.       g_warning ("entscale_entry_update: invalid type");
  5454.       return;
  5455.     }
  5456.  
  5457.   adjustment->value = constraint_val;
  5458.   /* avoid infinite loop (scale, entry, scale, entry ...) */
  5459.   gtk_signal_handler_block_by_data (GTK_OBJECT(adjustment), data );
  5460.   gtk_signal_emit_by_name (GTK_OBJECT(adjustment), "value_changed");
  5461.   gtk_signal_handler_unblock_by_data (GTK_OBJECT(adjustment), data );
  5462.  
  5463.   if (entscale->callback && (old_val != new_val))
  5464.     (*entscale->callback) (new_val, entscale->call_data);
  5465. }
  5466.  
  5467. /*************************************************************************/
  5468. /**                                    **/
  5469. /**            +++ Miscellaneous                **/
  5470. /**                                    **/
  5471. /*************************************************************************/
  5472.  
  5473. /*
  5474.   These query box functions are yanked from app/interface.c.    taka
  5475.  */
  5476.  
  5477. /*
  5478.  *  A text string query box
  5479.  */
  5480.  
  5481. typedef struct _QueryBox QueryBox;
  5482.  
  5483. struct _QueryBox
  5484. {
  5485.   GtkWidget *qbox;
  5486.   GtkWidget *entry;
  5487.   QueryFunc callback;
  5488.   gpointer data;
  5489. };
  5490.  
  5491. static void query_box_cancel_callback (GtkWidget *, gpointer);
  5492. static void query_box_ok_callback (GtkWidget *, gpointer);
  5493.  
  5494. GtkWidget *
  5495. query_string_box (char          *title,
  5496.           char          *message,
  5497.           char          *initial,
  5498.           QueryFunc    callback,
  5499.           gpointer     data)
  5500. {
  5501.   QueryBox  *query_box;
  5502.   GtkWidget *qbox;
  5503.   GtkWidget *vbox;
  5504.   GtkWidget *label;
  5505.   GtkWidget *entry;
  5506.   GtkWidget *button;
  5507.  
  5508.   query_box = (QueryBox *) g_malloc (sizeof (QueryBox));
  5509.  
  5510.   qbox = gtk_dialog_new ();
  5511.   gtk_window_set_title (GTK_WINDOW (qbox), title);
  5512.   gtk_window_position (GTK_WINDOW (qbox), GTK_WIN_POS_MOUSE);
  5513.  
  5514.   gtk_container_border_width (GTK_CONTAINER (GTK_DIALOG (qbox)->action_area), 2);
  5515.  
  5516.   button = gtk_button_new_with_label ("OK");
  5517.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  5518.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  5519.               (GtkSignalFunc) query_box_ok_callback,
  5520.               query_box);
  5521.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (qbox)->action_area), button, TRUE, TRUE, 0);
  5522.   gtk_widget_grab_default (button);
  5523.   gtk_widget_show (button);
  5524.  
  5525.   button = gtk_button_new_with_label ("Cancel");
  5526.   GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  5527.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  5528.               (GtkSignalFunc) query_box_cancel_callback,
  5529.               query_box);
  5530.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (qbox)->action_area), button, TRUE, TRUE, 0);
  5531.   gtk_widget_show (button);
  5532.  
  5533.   vbox = gtk_vbox_new (FALSE, 1);
  5534.   gtk_container_border_width (GTK_CONTAINER (vbox), 2);
  5535.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (qbox)->vbox), vbox);
  5536.   gtk_widget_show (vbox);
  5537.  
  5538.   label = gtk_label_new (message);
  5539.   gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, FALSE, 0);
  5540.   gtk_widget_show (label);
  5541.  
  5542.   entry = gtk_entry_new ();
  5543.   gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
  5544.   if (initial)
  5545.     gtk_entry_set_text (GTK_ENTRY (entry), initial);
  5546.   gtk_widget_show (entry);
  5547.  
  5548.   query_box->qbox = qbox;
  5549.   query_box->entry = entry;
  5550.   query_box->callback = callback;
  5551.   query_box->data = data;
  5552.  
  5553.   gtk_widget_show (qbox);
  5554.  
  5555.   return qbox;
  5556. }
  5557.  
  5558. static void
  5559. query_box_cancel_callback (GtkWidget *w,
  5560.                gpointer   client_data)
  5561. {
  5562.   QueryBox *query_box;
  5563.  
  5564.   query_box = (QueryBox *) client_data;
  5565.  
  5566.   /*  Destroy the box  */
  5567.   gtk_widget_destroy (query_box->qbox);
  5568.  
  5569.   g_free (query_box);
  5570. }
  5571.  
  5572. static void
  5573. query_box_ok_callback (GtkWidget *w,
  5574.                gpointer      client_data)
  5575. {
  5576.   QueryBox *query_box;
  5577.   char *string;
  5578.  
  5579.   query_box = (QueryBox *) client_data;
  5580.  
  5581.   /*  Get the entry data  */
  5582.   string = g_strdup (gtk_entry_get_text (GTK_ENTRY (query_box->entry)));
  5583.  
  5584.   /*  Call the user defined callback  */
  5585.   (* query_box->callback) (w, query_box->data, (gpointer) string);
  5586.  
  5587.   /*  Destroy the box  */
  5588.   gtk_widget_destroy (query_box->qbox);
  5589.  
  5590.   g_free (query_box);
  5591. }
  5592.