home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / ifscompose / ifscompose.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-20  |  77.7 KB  |  2,712 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * IfsCompose is a interface for creating IFS fractals by
  5.  * direct manipulation.
  6.  * Copyright (C) 1997 Owen Taylor
  7.  *
  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20.  */
  21.  
  22. /* TODO
  23.  * ----
  24.  *
  25.  * 1. Run in non-interactive mode (need to figure out useful
  26.  *    way for a script to give the 19N paramters for an image).
  27.  *    Perhaps just support saving parameters to a file, script
  28.  *    passes file name.
  29.  * 2. Save settings on a per-layer basis (long term, needs GIMP
  30.  *    support to do properly). Load/save from affine parameters?
  31.  * 3. Figure out if we need multiple phases for supersampled
  32.  *    brushes.
  33.  * 4. (minor) Make undo work correctly when focus is in entry widget.
  34.  */
  35. #include "config.h"
  36.  
  37. #include <stdlib.h>
  38. #include <stdio.h>
  39. #include <string.h>
  40. #include <ctype.h>
  41.  
  42. #include <gtk/gtk.h>
  43.  
  44. #include <libgimp/gimp.h>
  45. #include <libgimp/gimpui.h>
  46.  
  47. #include "libgimp/stdplugins-intl.h"
  48.  
  49. #include "ifscompose.h"
  50.  
  51. #define SCALE_WIDTH            150
  52. #define ENTRY_WIDTH             60
  53. #define DESIGN_AREA_MAX_SIZE   256
  54.  
  55. #define PREVIEW_RENDER_CHUNK 10000
  56.  
  57. #define UNDO_LEVELS             10
  58.  
  59. #define IFSCOMPOSE_PARASITE "ifscompose-parasite"
  60. #define IFSCOMPOSE_DATA     "plug_in_ifscompose"
  61.  
  62. typedef enum
  63. {
  64.   OP_TRANSLATE,
  65.   OP_ROTATE,            /* or scale */
  66.   OP_STRETCH
  67. } DesignOp;
  68.  
  69. typedef enum
  70. {
  71.   VALUE_PAIR_INT,
  72.   VALUE_PAIR_DOUBLE
  73. } ValuePairType;
  74.  
  75. typedef struct
  76. {
  77.   GtkObject *adjustment;
  78.   GtkWidget *scale;
  79.   GtkWidget *entry;
  80.  
  81.   ValuePairType type;
  82.  
  83.   union
  84.   {
  85.     gdouble *d;
  86.     gint    *i;
  87.   } data;
  88.  
  89.   gint entry_handler_id;
  90. } ValuePair;
  91.  
  92. typedef struct
  93. {
  94.   IfsComposeVals   ifsvals;
  95.   AffElement     **elements;
  96.   gint            *element_selected;
  97.   gint             current_element;
  98. } UndoItem;
  99.  
  100. typedef struct
  101. {
  102.   IfsColor  *color;
  103.   gchar     *name;
  104.   GtkWidget *hbox;
  105.   GtkWidget *orig_preview;
  106.   GtkWidget *button;
  107.   gint       fixed_point;
  108. } ColorMap;
  109.  
  110. typedef struct
  111. {
  112.   GtkWidget *dialog;
  113.  
  114.   ValuePair *iterations_pair;
  115.   ValuePair *subdivide_pair;
  116.   ValuePair *radius_pair;
  117.   ValuePair *memory_pair;
  118. } IfsOptionsDialog;
  119.  
  120. typedef struct
  121. {
  122.   GtkWidget *area;
  123.   GtkWidget *op_menu;
  124.   GdkPixmap *pixmap;
  125.  
  126.   DesignOp   op;
  127.   gdouble    op_x;
  128.   gdouble    op_y;
  129.   gdouble    op_xcenter;
  130.   gdouble    op_ycenter;
  131.   gdouble    op_center_x;
  132.   gdouble    op_center_y;
  133.   guint      button_state;
  134.   gint       num_selected;
  135.  
  136.   GdkGC     *selected_gc;
  137. } IfsDesignArea;
  138.  
  139. typedef struct
  140. {
  141.   ValuePair *prob_pair;
  142.   ValuePair *x_pair;
  143.   ValuePair *y_pair;
  144.   ValuePair *scale_pair;
  145.   ValuePair *angle_pair;
  146.   ValuePair *asym_pair;
  147.   ValuePair *shear_pair;
  148.   GtkWidget *flip_check_button;
  149.  
  150.   ColorMap  *red_cmap;
  151.   ColorMap  *green_cmap;
  152.   ColorMap  *blue_cmap;
  153.   ColorMap  *black_cmap;
  154.   ColorMap  *target_cmap;
  155.   ValuePair *hue_scale_pair;
  156.   ValuePair *value_scale_pair;
  157.   GtkWidget *simple_button;
  158.   GtkWidget *full_button;
  159.   GtkWidget *current_frame;
  160.  
  161.   GtkWidget *move_button;
  162.   gint       move_handler;
  163.   GtkWidget *rotate_button;
  164.   gint       rotate_handler;
  165.   GtkWidget *stretch_button;
  166.   gint       stretch_handler;
  167.  
  168.   GtkWidget *preview;
  169.   guchar    *preview_data;
  170.   gint       preview_iterations;
  171.  
  172.   gint       drawable_width;
  173.   gint       drawable_height;
  174.  
  175.   AffElement     *selected_orig;
  176.   gint            current_element;
  177.   AffElementVals  current_vals;
  178.   gint            auto_preview;
  179.  
  180.   gboolean   in_update;        /* true if we're currently in
  181.                    update_values() - don't do anything
  182.                    on updates */
  183. } IfsDialog;
  184.  
  185. typedef struct
  186. {
  187.   gint run;
  188. } IfsComposeInterface;
  189.  
  190. /* Declare local functions.
  191.  */
  192. static void      query  (void);
  193. static void      run    (gchar      *name,
  194.              gint        nparams,
  195.              GimpParam  *param,
  196.              gint       *nreturn_vals,
  197.              GimpParam **return_vals);
  198.  
  199. /*  user interface functions  */
  200. static gint       ifs_compose_dialog     (GimpDrawable *drawable);
  201. static void       ifs_options_dialog     (void);
  202. static GtkWidget *ifs_compose_trans_page (void);
  203. static GtkWidget *ifs_compose_color_page (void);
  204. static void       design_op_menu_popup   (gint button, guint32 activate_time);
  205. static void       design_op_menu_create  (GtkWidget *window);
  206. static void       design_area_create     (GtkWidget *window, gint design_width,
  207.                       gint design_height);
  208.  
  209. /* functions for drawing design window */
  210. static void update_values                   (void);
  211. static void set_current_element             (gint index);
  212. static gint design_area_expose              (GtkWidget *widget,
  213.                          GdkEventExpose *event);
  214. static gint design_area_button_press        (GtkWidget *widget,
  215.                          GdkEventButton *event);
  216. static gint design_area_button_release      (GtkWidget *widget,
  217.                          GdkEventButton *event);
  218. static void design_area_select_all_callback (GtkWidget *w, gpointer data);
  219. static gint design_area_configure           (GtkWidget *widget,
  220.                          GdkEventConfigure *event);
  221. static gint design_area_motion              (GtkWidget *widget,
  222.                          GdkEventMotion *event);
  223. static void design_area_redraw              (void);
  224.  
  225. /* Undo ring functions */
  226. static void undo_begin    (void);
  227. static void undo_update   (gint element);
  228. static void undo_exchange (gint el);
  229. static void undo          (void);
  230. static void redo          (void);
  231.  
  232. static void recompute_center              (gboolean   save_undo);
  233. static void recompute_center_cb           (GtkWidget *widget, 
  234.                            gpointer   data);
  235.  
  236. static void ifs_compose                   (GimpDrawable *drawable);
  237.  
  238. static void color_map_set_preview_color   (GtkWidget *preview,
  239.                        IfsColor  *color);
  240. static ColorMap *color_map_create         (gchar     *name,
  241.                        IfsColor  *orig_color,
  242.                        IfsColor  *data, 
  243.                        gint       fixed_point);
  244. static void color_map_color_changed_cb    (GtkWidget *widget,
  245.                        ColorMap  *color_map);
  246. static void color_map_update               (ColorMap *color_map);
  247.  
  248. /* interface functions */
  249. static void simple_color_toggled          (GtkWidget *widget,gpointer data);
  250. static void simple_color_set_sensitive    (void);
  251. static void val_changed_update            (void);
  252. static ValuePair *value_pair_create       (gpointer   data,
  253.                        gdouble    lower, 
  254.                        gdouble    upper,
  255.                        gboolean   create_scale, 
  256.                        ValuePairType type);
  257. static void value_pair_update             (ValuePair *value_pair);
  258. static void value_pair_entry_callback     (GtkWidget *w,
  259.                        ValuePair *value_pair);
  260. static void value_pair_destroy_callback   (GtkWidget *widget,
  261.                        ValuePair *value_pair);
  262. static void value_pair_button_release     (GtkWidget *widget,
  263.                        GdkEventButton *event,
  264.                        gpointer   data);
  265. static void value_pair_scale_callback     (GtkAdjustment *adjustment,
  266.                        ValuePair *value_pair);
  267.  
  268. static void auto_preview_callback         (GtkWidget *widget, gpointer data);
  269. static void design_op_callback            (GtkWidget *widget, gpointer data);
  270. static void design_op_update_callback     (GtkWidget *widget, gpointer data);
  271. static void flip_check_button_callback    (GtkWidget *widget, gpointer data);
  272. static gint preview_idle_render           (void);
  273.  
  274. static void ifs_compose_set_defaults      (void);
  275. static void ifs_compose_defaults_callback (GtkWidget *widget,
  276.                        gpointer   data);
  277. static void ifs_compose_new_callback      (GtkWidget *widget,
  278.                        gpointer   data);
  279. static void ifs_compose_delete_callback   (GtkWidget *widget,
  280.                        gpointer   data);
  281. static void ifs_compose_preview_callback  (GtkWidget *widget,
  282.                        GtkWidget *preview);
  283.  
  284. static void ifs_compose_close_callback    (GtkWidget *widget,
  285.                        GtkWidget **destroyed_widget);
  286. static void ifs_compose_ok_callback       (GtkWidget *widget,
  287.                        GtkWidget *window);
  288.  
  289. /*
  290.  *  Some static variables
  291.  */
  292.  
  293. IfsDialog        *ifsD = NULL;
  294. IfsOptionsDialog *ifsOptD = NULL;
  295. IfsDesignArea    *ifsDesign = NULL;
  296.  
  297. static AffElement **elements = NULL;
  298. static gint        *element_selected = NULL;
  299. /* labels are generated by printing this int */
  300. static gint         count_for_naming = 0;
  301.  
  302. static UndoItem undo_ring[UNDO_LEVELS];
  303. static gint     undo_cur = -1;
  304. static gint     undo_num = 0;
  305. static gint     undo_start = 0;
  306.  
  307. /* num_elements = 0, signals not inited */
  308. static IfsComposeVals ifsvals =
  309. {
  310.   0,                /* num_elements */
  311.   50000,            /* iterations */
  312.   4096,                /* max_memory */
  313.   4,                /* subdivide */
  314.   0.75,                /* radius */
  315.   1.0,                /* aspect ratio */
  316.   0.5,                /* center_x */
  317.   0.5,                /* center_y */
  318. };
  319.  
  320. static IfsComposeInterface ifscint =
  321. {
  322.   FALSE,        /* run */
  323. };
  324.  
  325. GimpPlugInInfo PLUG_IN_INFO =
  326. {
  327.   NULL,    /* init_proc */
  328.   NULL,    /* quit_proc */
  329.   query,   /* query_proc */
  330.   run,     /* run_proc */
  331. };
  332.  
  333.  
  334. MAIN ()
  335.  
  336. static void
  337. query (void)
  338. {
  339.   static GimpParamDef args[] =
  340.   {
  341.     { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
  342.     { GIMP_PDB_IMAGE,    "image",    "Input image" },
  343.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  344.   };
  345.  
  346.   static GimpParamDef *return_vals = NULL;
  347.  
  348.   static int nargs = sizeof (args) / sizeof (args[0]);
  349.   static int nreturn_vals = 0;
  350.  
  351.   INIT_I18N ();
  352.  
  353.   gimp_install_procedure ("plug_in_ifs_compose",
  354.               "Create an Iterated Function System Fractal",
  355.               "Interactively create an Iterated Function System fractal. "
  356.               "Use the window on the upper left to adjust the component "
  357.               "transformations of the fractal. The operation that is performed "
  358.               "is selected by the buttons underneath the window, or from a "
  359.               "menu popped up by the right mouse button. The fractal will be "
  360.               "rendered with a transparent background if the current image has "
  361.               "a transparent background.",
  362.               "Owen Taylor",
  363.               "Owen Taylor",
  364.               "1997",
  365.               N_("<Image>/Filters/Render/Nature/IfsCompose..."),
  366.               "RGB*, GRAY*",
  367.               GIMP_PLUGIN,
  368.               nargs, nreturn_vals,
  369.               args, return_vals);
  370. }
  371.  
  372. static void
  373. run (gchar      *name,
  374.      gint        nparams,
  375.      GimpParam  *param,
  376.      gint       *nreturn_vals,
  377.      GimpParam **return_vals)
  378. {
  379.   static GimpParam   values[1];
  380.   GimpDrawable      *active_drawable;
  381.   GimpRunModeType    run_mode;
  382.   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  383.   GimpParasite      *parasite = NULL;
  384.   gboolean           found_parasite;
  385.  
  386.   run_mode = param[0].data.d_int32;
  387.  
  388.   values[0].type = GIMP_PDB_STATUS;
  389.   values[0].data.d_status = status;
  390.  
  391.   *nreturn_vals = 1;
  392.   *return_vals = values;
  393.  
  394.   INIT_I18N_UI(); 
  395.  
  396.   /* kill (getpid(), 19); */
  397.  
  398.   /*  Get the active drawable  */
  399.   active_drawable = gimp_drawable_get (param[2].data.d_drawable);
  400.  
  401.   switch (run_mode)
  402.     {
  403.     case GIMP_RUN_INTERACTIVE:
  404.       /*  Possibly retrieve data; first look for a parasite -
  405.        *  if not found, fall back to global values
  406.        */
  407.       parasite = gimp_drawable_parasite_find (active_drawable->id,
  408.                           IFSCOMPOSE_PARASITE);
  409.       found_parasite = FALSE;
  410.       if (parasite)
  411.     {
  412.       found_parasite = ifsvals_parse_string (gimp_parasite_data (parasite),
  413.                          &ifsvals, &elements);
  414.       gimp_parasite_free (parasite);
  415.     }
  416.  
  417.       if (!found_parasite)
  418.     {
  419.       gint length;
  420.       gchar *data; 
  421.       
  422.       length = gimp_get_data_size (IFSCOMPOSE_DATA);
  423.       if (length)
  424.         {
  425.           data = g_new (gchar, length);
  426.           gimp_get_data (IFSCOMPOSE_DATA, data);
  427.  
  428.           ifsvals_parse_string (data, &ifsvals, &elements);
  429.           g_free (data);
  430.         }
  431.     }
  432.  
  433.       /* after ifsvals_parse_string, need to set up naming */
  434.       count_for_naming = ifsvals.num_elements;
  435.       /*  First acquire information with a dialog  */
  436.       if (! ifs_compose_dialog (active_drawable))
  437.     return;
  438.       break;
  439.  
  440.     case GIMP_RUN_NONINTERACTIVE:
  441.       /*  Make sure all the arguments are there!  */
  442.       status = GIMP_PDB_CALLING_ERROR;
  443.       break;
  444.  
  445.     case GIMP_RUN_WITH_LAST_VALS:
  446.       /*  Possibly retrieve data  */
  447.     {
  448.       gint length;
  449.       gchar *data; 
  450.       
  451.       length = gimp_get_data_size (IFSCOMPOSE_DATA);
  452.       if (length)
  453.         {
  454.           data = g_new (gchar, length);
  455.           gimp_get_data (IFSCOMPOSE_DATA, data);
  456.  
  457.           ifsvals_parse_string (data, &ifsvals, &elements);
  458.           g_free (data);
  459.         }
  460.           else
  461.             ifs_compose_set_defaults ();
  462.     }
  463.       break;
  464.  
  465.     default:
  466.       break;
  467.     }
  468.  
  469.   /*  Render the fractal  */
  470.   if ((status == GIMP_PDB_SUCCESS) &&
  471.       (gimp_drawable_is_rgb (active_drawable->id) ||
  472.        gimp_drawable_is_gray (active_drawable->id)))
  473.     {
  474.       /*  set the tile cache size so that the operation works well  */
  475.       gimp_tile_cache_ntiles (2 * (MAX (active_drawable->width, active_drawable->height) /
  476.                    gimp_tile_width () + 1));
  477.  
  478.       /*  run the effect  */
  479.       ifs_compose (active_drawable);
  480.  
  481.       /*  If the run mode is interactive, flush the displays  */
  482.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  483.     gimp_displays_flush ();
  484.  
  485.       /*  Store data for next invocation - both globally and
  486.        *  as a parasite on this layer
  487.        */
  488.       if (run_mode == GIMP_RUN_INTERACTIVE)
  489.     {
  490.       gchar *str = ifsvals_stringify (&ifsvals, elements);
  491.       GimpParasite *parasite;
  492.  
  493.       gimp_set_data (IFSCOMPOSE_DATA, str, strlen(str)+1);
  494.       parasite = gimp_parasite_new (IFSCOMPOSE_PARASITE,
  495.                     GIMP_PARASITE_PERSISTENT |
  496.                     GIMP_PARASITE_UNDOABLE,
  497.                     strlen(str)+1, str);
  498.       gimp_drawable_parasite_attach (active_drawable->id, parasite);
  499.       gimp_parasite_free (parasite);
  500.  
  501.       g_free (str);
  502.     }
  503.     }
  504.   else if (status == GIMP_PDB_SUCCESS)
  505.     {
  506.       status = GIMP_PDB_EXECUTION_ERROR;
  507.     }
  508.  
  509.   values[0].data.d_status = status;
  510.  
  511.   gimp_drawable_detach (active_drawable);
  512. }
  513.  
  514. static GtkWidget *
  515. ifs_compose_trans_page (void)
  516. {
  517.   GtkWidget *vbox;
  518.   GtkWidget *table;
  519.   GtkWidget *label;
  520.  
  521.   vbox = gtk_vbox_new (FALSE, 0);
  522.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  523.  
  524.   table = gtk_table_new (3, 6, FALSE);
  525.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  526.   gtk_table_set_col_spacing (GTK_TABLE (table), 1, 6);
  527.   gtk_table_set_col_spacing (GTK_TABLE (table), 3, 6);
  528.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  529.   gtk_table_set_row_spacing (GTK_TABLE (table), 1, 4);
  530.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  531.   gtk_widget_show (table);
  532.  
  533.   /* X */
  534.  
  535.   label = gtk_label_new (_("X:"));
  536.   gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
  537.   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
  538.             GTK_FILL, GTK_FILL, 0, 0);
  539.   gtk_widget_show (label);
  540.  
  541.   ifsD->x_pair = value_pair_create (&ifsD->current_vals.x, 0.0, 1.0, FALSE,
  542.                     VALUE_PAIR_DOUBLE);
  543.   gtk_table_attach (GTK_TABLE(table), ifsD->x_pair->entry, 1, 2, 0, 1,
  544.             GTK_FILL, GTK_FILL, 0, 0);
  545.   gtk_widget_show (ifsD->x_pair->entry);
  546.  
  547.   /* Y */
  548.  
  549.   label = gtk_label_new (_("Y:"));
  550.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  551.   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
  552.             GTK_FILL, GTK_FILL, 0, 0);
  553.   gtk_widget_show (label);
  554.  
  555.   ifsD->y_pair = value_pair_create (&ifsD->current_vals.y, 0.0, 1.0, FALSE,
  556.                     VALUE_PAIR_DOUBLE);
  557.   gtk_table_attach (GTK_TABLE(table), ifsD->y_pair->entry, 1, 2, 1, 2,
  558.             GTK_FILL, GTK_FILL, 0, 0);
  559.   gtk_widget_show (ifsD->y_pair->entry);
  560.  
  561.   /* Scale */
  562.  
  563.   label = gtk_label_new (_("Scale:"));
  564.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  565.   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
  566.             GTK_FILL, GTK_FILL, 0, 0);
  567.   gtk_widget_show (label);
  568.  
  569.   ifsD->scale_pair = value_pair_create (&ifsD->current_vals.scale, 0.0, 1.0, 
  570.                         FALSE, VALUE_PAIR_DOUBLE);
  571.   gtk_table_attach (GTK_TABLE (table), ifsD->scale_pair->entry, 3, 4, 0, 1,
  572.             GTK_FILL, GTK_FILL, 0, 0);
  573.   gtk_widget_show (ifsD->scale_pair->entry);
  574.  
  575.   /* Angle */
  576.  
  577.   label = gtk_label_new (_("Angle:"));
  578.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
  579.   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2,
  580.             GTK_FILL, GTK_FILL, 0, 0);
  581.   gtk_widget_show (label);
  582.  
  583.   ifsD->angle_pair = value_pair_create (&ifsD->current_vals.theta, -180, 180,
  584.                     FALSE, VALUE_PAIR_DOUBLE);
  585.   gtk_table_attach (GTK_TABLE (table), ifsD->angle_pair->entry, 3, 4, 1, 2,
  586.             GTK_FILL, GTK_FILL, 0, 0);
  587.   gtk_widget_show (ifsD->angle_pair->entry);
  588.  
  589.   /* Asym */
  590.  
  591.   label = gtk_label_new (_("Asymmetry:"));
  592.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  593.   gtk_table_attach (GTK_TABLE (table), label, 4, 5, 0, 1,
  594.             GTK_FILL, GTK_FILL, 0, 0);
  595.   gtk_widget_show (label);
  596.  
  597.   ifsD->asym_pair = value_pair_create (&ifsD->current_vals.asym, 0.10, 10.0,
  598.                        FALSE, VALUE_PAIR_DOUBLE);
  599.   gtk_table_attach (GTK_TABLE (table), ifsD->asym_pair->entry, 5, 6, 0, 1,
  600.             GTK_FILL, GTK_FILL, 0, 0);
  601.   gtk_widget_show (ifsD->asym_pair->entry);
  602.  
  603.   /* Shear */
  604.  
  605.   label = gtk_label_new (_("Shear:"));
  606.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  607.   gtk_table_attach (GTK_TABLE (table), label, 4, 5, 1, 2,
  608.             GTK_FILL, GTK_FILL, 0, 0);
  609.   gtk_widget_show (label);
  610.  
  611.   ifsD->shear_pair = value_pair_create (&ifsD->current_vals.shear, -10.0, 10.0,
  612.                     FALSE, VALUE_PAIR_DOUBLE);
  613.   gtk_table_attach (GTK_TABLE (table), ifsD->shear_pair->entry, 5, 6, 1, 2,
  614.             GTK_FILL, GTK_FILL, 0, 0);
  615.   gtk_widget_show (ifsD->shear_pair->entry);
  616.  
  617.   /* Flip */
  618.  
  619.   ifsD->flip_check_button = gtk_check_button_new_with_label (_("Flip"));
  620.   gtk_table_attach (GTK_TABLE (table), ifsD->flip_check_button, 0, 6, 2, 3,
  621.             GTK_FILL, GTK_FILL, 0, 0);
  622.   gtk_signal_connect (GTK_OBJECT (ifsD->flip_check_button), "toggled",
  623.               GTK_SIGNAL_FUNC (flip_check_button_callback),
  624.               NULL);
  625.   gtk_widget_show (ifsD->flip_check_button);
  626.  
  627.   return vbox;
  628. }
  629.  
  630. static GtkWidget *
  631. ifs_compose_color_page (void)
  632. {
  633.   GtkWidget *vbox;
  634.   GtkWidget *table;
  635.   GtkWidget *label;
  636.   GSList    *group = NULL;
  637.   IfsColor   color;
  638.  
  639.   vbox = gtk_vbox_new (FALSE, 0);
  640.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  641.  
  642.   table = gtk_table_new (3, 5, FALSE);
  643.   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
  644.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  645.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  646.   gtk_widget_show (table);
  647.  
  648.   /* Simple color control section */
  649.  
  650.   ifsD->simple_button = gtk_radio_button_new_with_label (group, _("Simple"));
  651.   gtk_table_attach (GTK_TABLE (table), ifsD->simple_button, 0, 1, 0, 2,
  652.             GTK_FILL, GTK_FILL, 0, 0);
  653.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (ifsD->simple_button));
  654.   gtk_signal_connect (GTK_OBJECT (ifsD->simple_button), "toggled",
  655.               GTK_SIGNAL_FUNC (simple_color_toggled),
  656.               NULL);
  657.   gtk_widget_show (ifsD->simple_button);
  658.  
  659.   color.vals[0] = 1.0;
  660.   color.vals[1] = 0.0;
  661.   color.vals[2] = 0.0;
  662.   ifsD->target_cmap = color_map_create (_("IfsCompose: Target"), NULL,
  663.                     &ifsD->current_vals.target_color, TRUE);
  664.   gtk_table_attach (GTK_TABLE (table), ifsD->target_cmap->hbox, 1, 2, 0, 2,
  665.             GTK_FILL, 0, 0, 0);
  666.   gtk_widget_show (ifsD->target_cmap->hbox);
  667.  
  668.   label = gtk_label_new (_("Scale Hue by:"));
  669.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  670.   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
  671.             GTK_FILL, GTK_FILL, 0, 0);
  672.   gtk_widget_show (label);
  673.  
  674.   ifsD->hue_scale_pair = value_pair_create (&ifsD->current_vals.hue_scale,
  675.                         0.0, 1.0, TRUE, VALUE_PAIR_DOUBLE);
  676.   gtk_table_attach (GTK_TABLE (table), ifsD->hue_scale_pair->scale, 3, 4, 0, 1,
  677.             GTK_FILL, GTK_FILL, 0, 0);
  678.   gtk_widget_show (ifsD->hue_scale_pair->scale);
  679.   gtk_table_attach (GTK_TABLE (table), ifsD->hue_scale_pair->entry, 4, 5, 0, 1,
  680.             GTK_FILL, GTK_FILL, 0, 0);
  681.   gtk_widget_show (ifsD->hue_scale_pair->entry);
  682.  
  683.   label = gtk_label_new (_("Scale Value by:"));
  684.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  685.   gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2,
  686.             GTK_FILL, GTK_FILL, 0, 0);
  687.   gtk_widget_show (label);
  688.  
  689.   ifsD->value_scale_pair = value_pair_create (&ifsD->current_vals.value_scale,
  690.                           0.0, 1.0, TRUE, VALUE_PAIR_DOUBLE);
  691.   gtk_table_attach (GTK_TABLE (table), ifsD->value_scale_pair->scale,
  692.             3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
  693.   gtk_widget_show (ifsD->value_scale_pair->scale);
  694.   gtk_table_attach (GTK_TABLE (table), ifsD->value_scale_pair->entry,
  695.             4, 5, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
  696.   gtk_widget_show (ifsD->value_scale_pair->entry);
  697.  
  698.   /* Full color control section */
  699.  
  700.   ifsD->full_button = gtk_radio_button_new_with_label (group, _("Full"));
  701.   gtk_table_attach (GTK_TABLE (table), ifsD->full_button, 0, 1, 2, 3,
  702.             GTK_FILL, GTK_FILL, 0, 0);
  703.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (ifsD->full_button));
  704.   gtk_widget_show (ifsD->full_button);
  705.  
  706.   color.vals[0] = 1.0;
  707.   color.vals[1] = 0.0;
  708.   color.vals[2] = 0.0;
  709.   ifsD->red_cmap = color_map_create (_("IfsCompose: Red"),&color,
  710.                      &ifsD->current_vals.red_color, FALSE);
  711.   gtk_table_attach (GTK_TABLE (table), ifsD->red_cmap->hbox, 1, 2, 2, 3,
  712.             GTK_FILL, GTK_FILL, 0, 0);
  713.   gtk_widget_show (ifsD->red_cmap->hbox);
  714.  
  715.   color.vals[0] = 0.0;
  716.   color.vals[1] = 1.0;
  717.   color.vals[2] = 0.0;
  718.   ifsD->green_cmap = color_map_create( _("IfsCompose: Green"),&color,
  719.                        &ifsD->current_vals.green_color, FALSE);
  720.   gtk_table_attach (GTK_TABLE (table), ifsD->green_cmap->hbox, 2, 3, 2, 3,
  721.             GTK_FILL, GTK_FILL, 0, 0);
  722.   gtk_widget_show (ifsD->green_cmap->hbox);
  723.  
  724.   color.vals[0] = 0.0;
  725.   color.vals[1] = 0.0;
  726.   color.vals[2] = 2.0;
  727.   ifsD->blue_cmap = color_map_create (_("IfsCompose: Blue"),&color,
  728.                       &ifsD->current_vals.blue_color, FALSE);
  729.   gtk_table_attach (GTK_TABLE (table), ifsD->blue_cmap->hbox, 3, 4, 2, 3,
  730.             GTK_FILL, GTK_FILL, 0, 0);
  731.   gtk_widget_show (ifsD->blue_cmap->hbox);
  732.  
  733.   color.vals[0] = 0.0;
  734.   color.vals[1] = 0.0;
  735.   color.vals[2] = 0.0;
  736.   ifsD->black_cmap = color_map_create (_("IfsCompose: Black"), &color,
  737.                        &ifsD->current_vals.black_color, FALSE);
  738.   gtk_table_attach (GTK_TABLE (table), ifsD->black_cmap->hbox, 4, 5, 2, 3,
  739.             GTK_FILL, GTK_FILL, 0, 0);
  740.   gtk_widget_show (ifsD->black_cmap->hbox);
  741.  
  742.   return vbox;
  743. }
  744.  
  745. static gint
  746. ifs_compose_dialog (GimpDrawable *drawable)
  747. {
  748.   GtkWidget *dlg;
  749.   GtkWidget *label;
  750.   GtkWidget *button;
  751.   GtkWidget *check_button;
  752.   GtkWidget *vbox;
  753.   GtkWidget *hbox;
  754.   GtkWidget *util_hbox;
  755.   GtkWidget *main_vbox;
  756.   GtkWidget *alignment;
  757.   GtkWidget *aspect_frame;
  758.   GtkWidget *notebook;
  759.   GtkWidget *page;
  760.  
  761.   gint design_width;
  762.   gint design_height;
  763.  
  764.   design_width  = drawable->width;
  765.   design_height = drawable->height;
  766.  
  767.   if (design_width > design_height)
  768.     {
  769.       if (design_width > DESIGN_AREA_MAX_SIZE)
  770.     {
  771.       design_height = design_height * DESIGN_AREA_MAX_SIZE / design_width;
  772.       design_width = DESIGN_AREA_MAX_SIZE;
  773.     }
  774.     }
  775.   else
  776.     {
  777.       if (design_height > DESIGN_AREA_MAX_SIZE)
  778.     {
  779.       design_width = design_width * DESIGN_AREA_MAX_SIZE / design_height;
  780.       design_height = DESIGN_AREA_MAX_SIZE;
  781.     }
  782.     }
  783.  
  784.   ifsD = g_new (IfsDialog, 1);
  785.   ifsD->auto_preview  = TRUE;
  786.   ifsD->drawable_width = drawable->width;
  787.   ifsD->drawable_height = drawable->height;
  788.  
  789.   ifsD->selected_orig = NULL;
  790.  
  791.   ifsD->preview_data = NULL;
  792.   ifsD->preview_iterations = 0;
  793.  
  794.   ifsD->in_update = 0;
  795.  
  796.   gimp_ui_init ("ifscompose", TRUE);
  797.  
  798.   dlg = gimp_dialog_new (_("IfsCompose"), "ifscompose",
  799.              gimp_standard_help_func, "filters/ifscompose.html",
  800.              GTK_WIN_POS_MOUSE,
  801.              FALSE, TRUE, FALSE,
  802.  
  803.              _("OK"), ifs_compose_ok_callback,
  804.              NULL, NULL, NULL, TRUE, FALSE,
  805.              _("New"), ifs_compose_new_callback,
  806.              NULL, NULL, NULL, FALSE, FALSE,
  807.              _("Delete"), ifs_compose_delete_callback,
  808.              NULL, NULL, NULL, FALSE, FALSE,
  809.              _("Reset"), ifs_compose_defaults_callback,
  810.              NULL, NULL, NULL, FALSE, FALSE,
  811.              _("Cancel"), gtk_widget_destroy,
  812.              NULL, 1, NULL, FALSE, TRUE,
  813.  
  814.              NULL);
  815.  
  816.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  817.               GTK_SIGNAL_FUNC (ifs_compose_close_callback),
  818.               &dlg);
  819.  
  820.   /*  The main vbox */
  821.   main_vbox = gtk_vbox_new (FALSE, 6);
  822.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
  823.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), main_vbox,
  824.               TRUE, TRUE, 0);
  825.  
  826.   /*  The design area */
  827.  
  828.   hbox = gtk_hbox_new (FALSE, 6);
  829.   gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
  830.  
  831.   aspect_frame = gtk_aspect_frame_new (NULL,
  832.                        0.5, 0.5,
  833.                        (gdouble) design_width / design_height,
  834.                        0);
  835.   gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN);
  836.   gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 0);
  837.  
  838.   design_area_create (dlg, design_width, design_height);
  839.   gtk_container_add (GTK_CONTAINER (aspect_frame), ifsDesign->area);
  840.  
  841.   gtk_widget_show (ifsDesign->area);
  842.   gtk_widget_show (aspect_frame);
  843.  
  844.   /*  The Preview  */
  845.  
  846.   aspect_frame = gtk_aspect_frame_new (NULL,
  847.                        0.5, 0.5,
  848.                        (gdouble) design_width / design_height,
  849.                        0);
  850.   gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN);
  851.   gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 0);
  852.  
  853.   ifsD->preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  854.   gtk_preview_size (GTK_PREVIEW (ifsD->preview), design_width, design_height);
  855.   gtk_container_add (GTK_CONTAINER (aspect_frame), ifsD->preview);
  856.   gtk_widget_show (ifsD->preview);
  857.  
  858.   gtk_widget_show (aspect_frame);
  859.  
  860.   gtk_widget_show (hbox);
  861.  
  862.   /* Iterations and preview options */
  863.  
  864.   hbox = gtk_hbox_new (FALSE, 4);
  865.   gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
  866.  
  867.   alignment = gtk_alignment_new (1.0, 0.5, 0.5, 0.0);
  868.   gtk_box_pack_start (GTK_BOX (hbox), alignment, TRUE, TRUE, 0);
  869.  
  870.   util_hbox = gtk_hbox_new (FALSE, 4);
  871.   gtk_container_add (GTK_CONTAINER (alignment), util_hbox);
  872.  
  873.   ifsD->move_button = gtk_toggle_button_new_with_label (_("Move"));
  874.   gtk_box_pack_start (GTK_BOX (util_hbox), ifsD->move_button,
  875.               TRUE, TRUE, 0);
  876.   gtk_widget_show (ifsD->move_button);
  877.   ifsD->move_handler =
  878.     gtk_signal_connect (GTK_OBJECT (ifsD->move_button),"toggled",
  879.             GTK_SIGNAL_FUNC (design_op_callback),
  880.             (gpointer) ((long) OP_TRANSLATE));
  881.  
  882.   ifsD->rotate_button = gtk_toggle_button_new_with_label (_("Rotate/Scale"));
  883.   gtk_box_pack_start (GTK_BOX (util_hbox), ifsD->rotate_button,
  884.               TRUE, TRUE, 0);
  885.   gtk_widget_show (ifsD->rotate_button);
  886.   ifsD->rotate_handler =
  887.     gtk_signal_connect (GTK_OBJECT (ifsD->rotate_button), "toggled",
  888.             GTK_SIGNAL_FUNC (design_op_callback),
  889.             (gpointer) ((long) OP_ROTATE));
  890.  
  891.   ifsD->stretch_button = gtk_toggle_button_new_with_label (_("Stretch"));
  892.   gtk_box_pack_start (GTK_BOX (util_hbox), ifsD->stretch_button,
  893.               TRUE, TRUE, 0);
  894.   gtk_widget_show (ifsD->stretch_button);
  895.   ifsD->stretch_handler =
  896.     gtk_signal_connect (GTK_OBJECT (ifsD->stretch_button), "toggled",
  897.             GTK_SIGNAL_FUNC (design_op_callback),
  898.             (gpointer) ((long) OP_STRETCH));
  899.  
  900.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->move_button), TRUE);
  901.  
  902.   gtk_widget_show (alignment);
  903.   gtk_widget_show (util_hbox);
  904.  
  905.   alignment = gtk_alignment_new (1.0, 0.5, 0.5, 0.0);
  906.   gtk_box_pack_start (GTK_BOX (hbox), alignment, TRUE, TRUE, 0);
  907.  
  908.   util_hbox = gtk_hbox_new (FALSE, 4);
  909.   gtk_container_add (GTK_CONTAINER (alignment), util_hbox);
  910.  
  911.   button = gtk_button_new_with_label (_("Render Options"));
  912.   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
  913.                  GTK_SIGNAL_FUNC (ifs_options_dialog),
  914.                  NULL);
  915.   gtk_box_pack_start (GTK_BOX (util_hbox), button, TRUE, TRUE, 0);
  916.   gtk_widget_show (button);
  917.  
  918.   button = gtk_button_new_with_label (_("Preview"));
  919.   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
  920.                  GTK_SIGNAL_FUNC (ifs_compose_preview_callback),
  921.                  GTK_OBJECT (ifsD->preview));
  922.   gtk_box_pack_start (GTK_BOX (util_hbox), button, TRUE, TRUE, 0);
  923.   gtk_widget_show (button);
  924.  
  925.   check_button = gtk_check_button_new_with_label (_("Auto"));
  926.   gtk_box_pack_start (GTK_BOX (util_hbox), check_button, TRUE, TRUE, 0);
  927.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
  928.                 ifsD->auto_preview);
  929.   gtk_signal_connect (GTK_OBJECT (check_button), "toggled",
  930.               GTK_SIGNAL_FUNC (auto_preview_callback),
  931.               NULL);
  932.   gtk_widget_show (check_button);
  933.  
  934.   gtk_widget_show (util_hbox);
  935.   gtk_widget_show (alignment);
  936.   gtk_widget_show (hbox);
  937.  
  938.   /* The current transformation frame */
  939.  
  940.   ifsD->current_frame = gtk_frame_new (NULL);
  941.   gtk_frame_set_shadow_type (GTK_FRAME (ifsD->current_frame),
  942.                  GTK_SHADOW_ETCHED_IN);
  943.   gtk_box_pack_start (GTK_BOX (main_vbox), ifsD->current_frame,
  944.               FALSE, FALSE, 0);
  945.  
  946.   vbox = gtk_vbox_new (FALSE, 4);
  947.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  948.   gtk_container_add (GTK_CONTAINER (ifsD->current_frame), vbox);
  949.  
  950.   /* The notebook */
  951.  
  952.   notebook = gtk_notebook_new ();
  953.   gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
  954.   gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0);
  955.   gtk_widget_show (notebook);
  956.  
  957.   page = ifs_compose_trans_page ();
  958.   label = gtk_label_new (_("Spatial Transformation"));
  959.   gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
  960.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label);
  961.   gtk_widget_show (page);
  962.  
  963.   page = ifs_compose_color_page ();
  964.   label = gtk_label_new (_("Color Transformation"));
  965.   gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
  966.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label);
  967.   gtk_widget_show (page);
  968.  
  969.   /* The probability entry */
  970.  
  971.   hbox = gtk_hbox_new (FALSE, 4);
  972.   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  973.  
  974.   label = gtk_label_new (_("Relative Probability:"));
  975.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  976.   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  977.   gtk_widget_show (label);
  978.  
  979.   ifsD->prob_pair = value_pair_create (&ifsD->current_vals.prob, 0.0, 5.0, TRUE,
  980.                        VALUE_PAIR_DOUBLE);
  981.   gtk_box_pack_start (GTK_BOX (hbox), ifsD->prob_pair->scale, TRUE, TRUE, 0);
  982.   gtk_widget_show (ifsD->prob_pair->scale);
  983.   gtk_box_pack_start (GTK_BOX (hbox), ifsD->prob_pair->entry, FALSE, TRUE, 0);
  984.   gtk_widget_show (ifsD->prob_pair->entry);
  985.  
  986.   gtk_widget_show (hbox);
  987.   gtk_widget_show (vbox);
  988.   gtk_widget_show (ifsD->current_frame);
  989.  
  990.   gtk_widget_show (main_vbox);
  991.  
  992.   if (ifsvals.num_elements == 0)
  993.     {
  994.       ifs_compose_set_defaults();
  995.       if (ifsD->auto_preview)
  996.     ifs_compose_preview_callback (NULL, ifsD->preview);
  997.     }
  998.   else
  999.     {
  1000.       gint i;
  1001.       gdouble ratio = (gdouble) ifsD->drawable_height / ifsD->drawable_width;
  1002.  
  1003.       element_selected = g_new (gint, ifsvals.num_elements);
  1004.       element_selected[0] = TRUE;
  1005.       for (i = 1; i < ifsvals.num_elements; i++)
  1006.     element_selected[i] = FALSE;
  1007.  
  1008.       if (ratio != ifsvals.aspect_ratio)
  1009.     {
  1010.       /* Adjust things so that what fit onto the old image, fits
  1011.          onto the new image */
  1012.       Aff2 t1,t2,t3;
  1013.       gdouble x_offset, y_offset;
  1014.       gdouble center_x, center_y;
  1015.       gdouble scale;
  1016.  
  1017.       if (ratio < ifsvals.aspect_ratio)
  1018.         {
  1019.           scale = ratio/ifsvals.aspect_ratio;
  1020.           x_offset = (1-scale)/2;
  1021.           y_offset = 0;
  1022.         }
  1023.       else
  1024.         {
  1025.           scale = 1;
  1026.           x_offset = 0;
  1027.           y_offset = (ratio - ifsvals.aspect_ratio)/2;
  1028.         }
  1029.       aff2_scale (&t1, scale, 0);
  1030.       aff2_translate (&t2, x_offset, y_offset);
  1031.       aff2_compose (&t3, &t2, &t1);
  1032.       aff2_invert (&t1, &t3);
  1033.  
  1034.       aff2_apply (&t3, ifsvals.center_x, ifsvals.center_y, ¢er_x,
  1035.               ¢er_y);
  1036.  
  1037.       for (i = 0; i < ifsvals.num_elements; i++)
  1038.         {
  1039.           aff_element_compute_trans (elements[i],1,ifsvals.aspect_ratio,
  1040.                      ifsvals.center_x, ifsvals.center_y);
  1041.           aff2_compose (&t2, &elements[i]->trans, &t1);
  1042.           aff2_compose (&elements[i]->trans, &t3, &t2);
  1043.           aff_element_decompose_trans (elements[i],&elements[i]->trans,
  1044.                        1, ifsvals.aspect_ratio,
  1045.                        center_x, center_y);
  1046.         }
  1047.       ifsvals.center_x = center_x;
  1048.       ifsvals.center_y = center_y;
  1049.  
  1050.       ifsvals.aspect_ratio = ratio;
  1051.     }
  1052.  
  1053.       for (i = 0; i < ifsvals.num_elements; i++)
  1054.     aff_element_compute_color_trans (elements[i]);
  1055.       /* boundary and spatial transformations will be computed
  1056.      when the design_area gets a ConfigureNotify event */
  1057.  
  1058.       set_current_element (0);
  1059.       if (ifsD->auto_preview)
  1060.     ifs_compose_preview_callback (NULL, ifsD->preview);
  1061.  
  1062.       ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements);
  1063.     }
  1064.  
  1065.   gtk_widget_show (dlg);
  1066.   gtk_main ();
  1067.  
  1068.   gtk_object_unref (GTK_OBJECT (ifsDesign->op_menu));
  1069.   
  1070.   if (dlg)
  1071.     gtk_widget_destroy (dlg);
  1072.  
  1073.   if (ifsOptD)
  1074.     gtk_widget_destroy (ifsOptD->dialog);
  1075.  
  1076.   gdk_flush ();
  1077.  
  1078.   gdk_gc_destroy (ifsDesign->selected_gc);
  1079.  
  1080.   g_free(ifsD);
  1081.  
  1082.   return ifscint.run;
  1083. }
  1084.  
  1085. static void
  1086. design_area_create (GtkWidget *window,
  1087.             gint       design_width,
  1088.             gint       design_height)
  1089. {
  1090.   ifsDesign = g_new (IfsDesignArea, 1);
  1091.  
  1092.   ifsDesign->op = OP_TRANSLATE;
  1093.   ifsDesign->button_state = 0;
  1094.   ifsDesign->pixmap = NULL;
  1095.   ifsDesign->selected_gc = NULL;
  1096.  
  1097.   ifsDesign->area = gtk_drawing_area_new ();
  1098.   gtk_drawing_area_size (GTK_DRAWING_AREA (ifsDesign->area),
  1099.              design_width, design_height);
  1100.  
  1101.   gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "expose_event",
  1102.               (GtkSignalFunc)design_area_expose,
  1103.               NULL);
  1104.   gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "button_press_event",
  1105.               (GtkSignalFunc)design_area_button_press,
  1106.               NULL);
  1107.   gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "button_release_event",
  1108.               (GtkSignalFunc)design_area_button_release,
  1109.               NULL);
  1110.   gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "motion_notify_event",
  1111.               (GtkSignalFunc)design_area_motion,
  1112.               NULL);
  1113.   gtk_signal_connect (GTK_OBJECT (ifsDesign->area), "configure_event",
  1114.               (GtkSignalFunc) design_area_configure,
  1115.               NULL);
  1116.   gtk_widget_set_events (ifsDesign->area,
  1117.              GDK_EXPOSURE_MASK |
  1118.              GDK_BUTTON_PRESS_MASK |
  1119.              GDK_BUTTON_RELEASE_MASK |
  1120.              GDK_POINTER_MOTION_MASK |
  1121.              GDK_POINTER_MOTION_HINT_MASK);
  1122.  
  1123.   design_op_menu_create (window);
  1124. }
  1125.  
  1126. static void
  1127. design_op_menu_create (GtkWidget *window)
  1128. {
  1129.   GtkWidget     *menu_item;
  1130.   GtkAccelGroup *accel_group;
  1131.  
  1132.   ifsDesign->op_menu = gtk_menu_new();
  1133.   gtk_object_ref (GTK_OBJECT (ifsDesign->op_menu));
  1134.   gtk_object_sink (GTK_OBJECT (ifsDesign->op_menu));
  1135.  
  1136.   accel_group = gtk_accel_group_new();
  1137.   gtk_menu_set_accel_group(GTK_MENU(ifsDesign->op_menu), accel_group);
  1138.   gtk_window_add_accel_group(GTK_WINDOW(window),accel_group);
  1139.  
  1140.   menu_item = gtk_menu_item_new_with_label(_("Move"));
  1141.   gtk_menu_append(GTK_MENU(ifsDesign->op_menu),menu_item);
  1142.   gtk_widget_show(menu_item);
  1143.   gtk_signal_connect(GTK_OBJECT(menu_item),"activate",
  1144.              (GtkSignalFunc)design_op_update_callback,
  1145.              (gpointer)((long)OP_TRANSLATE));
  1146.   gtk_widget_add_accelerator(menu_item,
  1147.                  "activate",
  1148.                  accel_group,
  1149.                  'M', 0,
  1150.                  GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
  1151.  
  1152.   menu_item = gtk_menu_item_new_with_label(_("Rotate/Scale"));
  1153.   gtk_menu_append(GTK_MENU(ifsDesign->op_menu),menu_item);
  1154.   gtk_widget_show(menu_item);
  1155.   gtk_signal_connect(GTK_OBJECT(menu_item),"activate",
  1156.              (GtkSignalFunc)design_op_update_callback,
  1157.              (gpointer)((long)OP_ROTATE));
  1158.   gtk_widget_add_accelerator(menu_item,
  1159.                  "activate",
  1160.                  accel_group,
  1161.                  'R', 0,
  1162.                  GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
  1163.  
  1164.   menu_item = gtk_menu_item_new_with_label(_("Stretch"));
  1165.   gtk_menu_append(GTK_MENU(ifsDesign->op_menu),menu_item);
  1166.   gtk_widget_show(menu_item);
  1167.   gtk_signal_connect(GTK_OBJECT(menu_item),"activate",
  1168.              (GtkSignalFunc)design_op_update_callback,
  1169.              (gpointer)((long)OP_STRETCH));
  1170.   gtk_widget_add_accelerator(menu_item,
  1171.                  "activate",
  1172.                  accel_group,
  1173.                  'S', 0,
  1174.                  GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
  1175.  
  1176.   /* A separator */
  1177.   menu_item = gtk_menu_item_new();
  1178.   gtk_menu_append(GTK_MENU(ifsDesign->op_menu),menu_item);
  1179.   gtk_widget_show(menu_item);
  1180.  
  1181.   menu_item = gtk_menu_item_new_with_label(_("Select All"));
  1182.   gtk_menu_append(GTK_MENU(ifsDesign->op_menu),menu_item);
  1183.   gtk_widget_show(menu_item);
  1184.   gtk_signal_connect(GTK_OBJECT(menu_item),"activate",
  1185.              (GtkSignalFunc)design_area_select_all_callback,
  1186.              NULL);
  1187.   gtk_widget_add_accelerator(menu_item,
  1188.                  "activate",
  1189.                  accel_group,
  1190.                  'A', GDK_CONTROL_MASK,
  1191.                  GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
  1192.  
  1193.   menu_item = gtk_menu_item_new_with_label(_("Recompute Center"));
  1194.   gtk_menu_append(GTK_MENU(ifsDesign->op_menu),menu_item);
  1195.   gtk_widget_show(menu_item);
  1196.   gtk_signal_connect(GTK_OBJECT(menu_item),"activate",
  1197.              (GtkSignalFunc)recompute_center_cb,
  1198.              NULL);
  1199.   gtk_widget_add_accelerator(menu_item,
  1200.                  "activate",
  1201.                  accel_group,
  1202.                  'R', GDK_MOD1_MASK,
  1203.                  GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
  1204.  
  1205.   menu_item = gtk_menu_item_new_with_label(_("Undo"));
  1206.   gtk_menu_append(GTK_MENU(ifsDesign->op_menu),menu_item);
  1207.   gtk_widget_show(menu_item);
  1208.   gtk_signal_connect(GTK_OBJECT(menu_item),"activate",
  1209.              (GtkSignalFunc)undo,
  1210.              NULL);
  1211.   gtk_widget_add_accelerator(menu_item,
  1212.                  "activate",
  1213.                  accel_group,
  1214.                  'Z', GDK_CONTROL_MASK,
  1215.                  GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
  1216.  
  1217.   menu_item = gtk_menu_item_new_with_label(_("Redo"));
  1218.   gtk_menu_append(GTK_MENU(ifsDesign->op_menu),menu_item);
  1219.   gtk_widget_show(menu_item);
  1220.   gtk_signal_connect(GTK_OBJECT(menu_item),"activate",
  1221.              (GtkSignalFunc)redo,
  1222.              NULL);
  1223.   gtk_widget_add_accelerator(menu_item,
  1224.                  "activate",
  1225.                  accel_group,
  1226.                  'R', GDK_CONTROL_MASK,
  1227.                  GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
  1228. }
  1229.  
  1230. static void
  1231. design_op_menu_popup (gint    button,
  1232.               guint32 activate_time)
  1233. {
  1234.   gtk_menu_popup (GTK_MENU (ifsDesign->op_menu),
  1235.           NULL, NULL, NULL, NULL,
  1236.           button, activate_time);
  1237. }
  1238.  
  1239. static void
  1240. ifs_options_dialog (void)
  1241. {
  1242.   GtkWidget *table;
  1243.   GtkWidget *label;
  1244.  
  1245.   if (!ifsOptD)
  1246.     {
  1247.       ifsOptD = g_new (IfsOptionsDialog, 1);
  1248.  
  1249.       ifsOptD->dialog =
  1250.     gimp_dialog_new (_("IfsCompose Options"), "ifscompose",
  1251.              gimp_standard_help_func, "filters/ifscompose.html",
  1252.              GTK_WIN_POS_MOUSE,
  1253.              FALSE, TRUE, FALSE,
  1254.  
  1255.              _("Close"), gtk_widget_hide,
  1256.              NULL, 1, NULL, TRUE, TRUE,
  1257.  
  1258.              NULL);
  1259.  
  1260.       /* Table of options */
  1261.  
  1262.       table = gtk_table_new (4, 3, FALSE);
  1263.       gtk_container_set_border_width (GTK_CONTAINER (table), 6);
  1264.       gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  1265.       gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  1266.       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (ifsOptD->dialog)->vbox), table,
  1267.               FALSE, FALSE, 0);
  1268.       gtk_widget_show (table);
  1269.  
  1270.       label = gtk_label_new (_("Max. Memory:"));
  1271.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  1272.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
  1273.             GTK_FILL, GTK_FILL, 0, 0);
  1274.       gtk_widget_show (label);
  1275.  
  1276.       ifsOptD->memory_pair = value_pair_create (&ifsvals.max_memory,
  1277.                         1, 1000000, FALSE,
  1278.                         VALUE_PAIR_INT);
  1279.       gtk_table_attach (GTK_TABLE (table), ifsOptD->memory_pair->entry,
  1280.             1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
  1281.       gtk_widget_show (ifsOptD->memory_pair->entry);
  1282.  
  1283.       label = gtk_label_new (_("Iterations:"));
  1284.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  1285.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
  1286.             GTK_FILL, GTK_FILL, 0, 0);
  1287.       gtk_widget_show (label);
  1288.  
  1289.       ifsOptD->iterations_pair = value_pair_create (&ifsvals.iterations,
  1290.                             1, 10000000, FALSE,
  1291.                             VALUE_PAIR_INT);
  1292.       gtk_table_attach (GTK_TABLE (table), ifsOptD->iterations_pair->entry,
  1293.             1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
  1294.       gtk_widget_show (ifsOptD->iterations_pair->entry);
  1295.       gtk_widget_show (label);
  1296.  
  1297.       label = gtk_label_new (_("Subdivide:"));
  1298.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  1299.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
  1300.             GTK_FILL, GTK_FILL, 0, 0);
  1301.       gtk_widget_show (label);
  1302.  
  1303.       ifsOptD->subdivide_pair = value_pair_create (&ifsvals.subdivide,
  1304.                            1, 10, FALSE,
  1305.                            VALUE_PAIR_INT);
  1306.       gtk_table_attach (GTK_TABLE (table), ifsOptD->subdivide_pair->entry,
  1307.             1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
  1308.       gtk_widget_show (ifsOptD->subdivide_pair->entry);
  1309.  
  1310.       label = gtk_label_new (_("Spot Radius:"));
  1311.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  1312.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
  1313.             GTK_FILL, GTK_FILL, 0, 0);
  1314.       gtk_widget_show (label);
  1315.  
  1316.       ifsOptD->radius_pair = value_pair_create (&ifsvals.radius,
  1317.                         0, 5, TRUE,
  1318.                         VALUE_PAIR_DOUBLE);
  1319.       gtk_table_attach (GTK_TABLE (table), ifsOptD->radius_pair->scale,
  1320.             1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
  1321.       gtk_widget_show (ifsOptD->radius_pair->scale);
  1322.       gtk_table_attach (GTK_TABLE (table), ifsOptD->radius_pair->entry,
  1323.             2, 3, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
  1324.       gtk_widget_show (ifsOptD->radius_pair->entry);
  1325.  
  1326.       value_pair_update (ifsOptD->iterations_pair);
  1327.       value_pair_update (ifsOptD->subdivide_pair);
  1328.       value_pair_update (ifsOptD->memory_pair);
  1329.       value_pair_update (ifsOptD->radius_pair);
  1330.  
  1331.       gtk_widget_show (ifsOptD->dialog);
  1332.     }
  1333.   else
  1334.     {
  1335.       if (!GTK_WIDGET_VISIBLE (ifsOptD->dialog))
  1336.     gtk_widget_show (ifsOptD->dialog);
  1337.     }
  1338. }
  1339.  
  1340. static void
  1341. ifs_compose (GimpDrawable *drawable)
  1342. {
  1343.   gint i,j;
  1344.   GimpImageType type = gimp_drawable_type (drawable->id);
  1345.   gchar *buffer;
  1346.  
  1347.   gint width = drawable->width;
  1348.   gint height = drawable->height;
  1349.   gint num_bands,band_height,band_y,band_no;
  1350.   guchar *data;
  1351.   guchar *mask = NULL;
  1352.   guchar *nhits;
  1353.   guchar rc,gc,bc;
  1354.  
  1355.   num_bands = ceil((gdouble)(width*height*SQR(ifsvals.subdivide)*5)
  1356.            / (1024 * ifsvals.max_memory));
  1357.   band_height = (height + num_bands - 1) / num_bands; 
  1358.   
  1359.   if (band_height > height)
  1360.     band_height = height;
  1361.  
  1362.   mask = g_new(guchar,width*band_height*SQR(ifsvals.subdivide));
  1363.   data = g_new(guchar,width*band_height*SQR(ifsvals.subdivide)*3);
  1364.   nhits = g_new(guchar,width*band_height*SQR(ifsvals.subdivide));
  1365.   gimp_palette_get_background ( &rc, &gc, &bc );
  1366.  
  1367.   band_y = 0;
  1368.   for (band_no = 0; band_no < num_bands; band_no++)
  1369.     {
  1370.       guchar *ptr;
  1371.       guchar *maskptr;
  1372.       guchar *dest;
  1373.       guchar *destrow;
  1374.       guchar maskval;
  1375.       GimpPixelRgn dest_rgn;
  1376.       gint progress;
  1377.       gint max_progress;
  1378.  
  1379.       gpointer pr;
  1380.  
  1381.       buffer = g_strdup_printf (_("Rendering IFS (%d/%d)..."), 
  1382.                 band_no+1, num_bands);
  1383.       gimp_progress_init(buffer);
  1384.       g_free (buffer);
  1385.  
  1386.       /* render the band to a buffer */
  1387.       if (band_y + band_height > height)
  1388.     band_height = height - band_y;
  1389.  
  1390.       /* we don't need to clear data since we store nhits */
  1391.       memset(mask, 0, width*band_height*SQR(ifsvals.subdivide));
  1392.       memset(nhits, 0, width*band_height*SQR(ifsvals.subdivide));
  1393.  
  1394.       ifs_render(elements, ifsvals.num_elements, width, height, ifsvals.iterations,
  1395.          &ifsvals, band_y, band_height, data, mask, nhits, FALSE);
  1396.  
  1397.       /* transfer the image to the drawable */
  1398.  
  1399.       
  1400.       buffer = g_strdup_printf (_("Copying IFS to image (%d/%d)..."), 
  1401.                 band_no+1,num_bands);
  1402.       gimp_progress_init(buffer);
  1403.       g_free (buffer);
  1404.  
  1405.       progress = 0;
  1406.       max_progress = band_height * width;
  1407.  
  1408.       gimp_pixel_rgn_init (&dest_rgn, drawable, 0, band_y,
  1409.                width, band_height, TRUE, TRUE);
  1410.  
  1411.       for (pr = gimp_pixel_rgns_register (1, &dest_rgn); pr != NULL; pr = gimp_pixel_rgns_process (pr))
  1412.     {
  1413.       destrow = dest_rgn.data;
  1414.  
  1415.       for (j = dest_rgn.y; j < (dest_rgn.y + dest_rgn.h); j++)
  1416.         {
  1417.           dest = destrow;
  1418.  
  1419.           for (i = dest_rgn.x; i < (dest_rgn.x + dest_rgn.w); i++)
  1420.         {
  1421.           /* Accumulate a reduced pixel */
  1422.  
  1423.           gint ii,jj;
  1424.           gint rtot=0;
  1425.           gint btot=0;
  1426.           gint gtot=0;
  1427.           gint mtot=0;
  1428.           for (jj=0;jj<ifsvals.subdivide;jj++)
  1429.             {
  1430.               ptr = data + 3 *
  1431.             (((j-band_y)*ifsvals.subdivide+jj)*ifsvals.subdivide*width +
  1432.              i*ifsvals.subdivide);
  1433.  
  1434.               maskptr = mask +
  1435.             ((j-band_y)*ifsvals.subdivide+jj)*ifsvals.subdivide*width +
  1436.             i*ifsvals.subdivide;
  1437.               for (ii=0;ii<ifsvals.subdivide;ii++)
  1438.             {
  1439.               maskval = *maskptr++;
  1440.               mtot += maskval;
  1441.               rtot += maskval* *ptr++;
  1442.               gtot += maskval* *ptr++;
  1443.               btot += maskval* *ptr++;
  1444.             }
  1445.             }
  1446.           if (mtot)
  1447.             {
  1448.               rtot /= mtot;
  1449.               gtot /= mtot;
  1450.               btot /= mtot;
  1451.               mtot /= SQR(ifsvals.subdivide);
  1452.             }
  1453.           /* and store it */
  1454.           switch (type)
  1455.             {
  1456.              case GIMP_GRAY_IMAGE:
  1457.               *dest++ = (mtot*(rtot+btot+gtot)+
  1458.                  (255-mtot)*(rc+gc+bc))/(3*255);
  1459.               break;
  1460.             case GIMP_GRAYA_IMAGE:
  1461.               *dest++ = (rtot+btot+gtot)/3;
  1462.               *dest++ = mtot;
  1463.               break;
  1464.             case GIMP_RGB_IMAGE:
  1465.               *dest++ = (mtot*rtot + (255-mtot)*rc)/255;
  1466.               *dest++ = (mtot*gtot + (255-mtot)*gc)/255;
  1467.               *dest++ = (mtot*btot + (255-mtot)*bc)/255;
  1468.               break;
  1469.             case GIMP_RGBA_IMAGE:
  1470.               *dest++ = rtot;
  1471.               *dest++ = gtot;
  1472.               *dest++ = btot;
  1473.               *dest++ = mtot;
  1474.               break;
  1475.             case GIMP_INDEXED_IMAGE:
  1476.             case GIMP_INDEXEDA_IMAGE:
  1477.               g_error("Indexed images not supported by IfsCompose");
  1478.               break;
  1479.             }
  1480.         }
  1481.           destrow += dest_rgn.rowstride;;
  1482.         }
  1483.       progress += dest_rgn.w * dest_rgn.h;
  1484.       gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
  1485.     }
  1486.       band_y += band_height;
  1487.     }
  1488.  
  1489.   g_free(mask);
  1490.   g_free(data);
  1491.   g_free(nhits);
  1492.  
  1493.   gimp_drawable_flush (drawable);
  1494.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  1495.   gimp_drawable_update (drawable->id,0,0,width,height);
  1496. }
  1497.  
  1498. static void
  1499. update_values (void)
  1500. {
  1501.   ifsD->in_update = TRUE;
  1502.  
  1503.   ifsD->current_vals = elements[ifsD->current_element]->v;
  1504.   ifsD->current_vals.theta *= 180/G_PI;
  1505.  
  1506.   value_pair_update(ifsD->prob_pair);
  1507.   value_pair_update(ifsD->x_pair);
  1508.   value_pair_update(ifsD->y_pair);
  1509.   value_pair_update(ifsD->scale_pair);
  1510.   value_pair_update(ifsD->angle_pair);
  1511.   value_pair_update(ifsD->asym_pair);
  1512.   value_pair_update(ifsD->shear_pair);
  1513.   color_map_update(ifsD->red_cmap);
  1514.   color_map_update(ifsD->green_cmap);
  1515.   color_map_update(ifsD->blue_cmap);
  1516.   color_map_update(ifsD->black_cmap);
  1517.   color_map_update(ifsD->target_cmap);
  1518.   value_pair_update(ifsD->hue_scale_pair);
  1519.   value_pair_update(ifsD->value_scale_pair);
  1520.   if (elements[ifsD->current_element]->v.simple_color)
  1521.     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->simple_button),
  1522.                  TRUE);
  1523.   else
  1524.     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->full_button),
  1525.                  TRUE);
  1526.   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ifsD->flip_check_button),
  1527.                   elements[ifsD->current_element]->v.flip);
  1528.  
  1529.   ifsD->in_update = FALSE;
  1530.  
  1531.   simple_color_set_sensitive();
  1532. }
  1533.  
  1534. static void
  1535. set_current_element (gint index)
  1536. {
  1537.   ifsD->current_element = index;
  1538.  
  1539.   gtk_frame_set_label(GTK_FRAME(ifsD->current_frame),elements[index]->name);
  1540.  
  1541.   update_values();
  1542. }
  1543.  
  1544. static gint
  1545. design_area_expose (GtkWidget      *widget,
  1546.             GdkEventExpose *event)
  1547. {
  1548.   gint i;
  1549.   gint cx,cy;
  1550.  
  1551.   if (!ifsDesign->selected_gc)
  1552.     {
  1553.       ifsDesign->selected_gc = gdk_gc_new(ifsDesign->area->window);
  1554.       gdk_gc_set_line_attributes(ifsDesign->selected_gc,2,
  1555.                  GDK_LINE_SOLID,GDK_CAP_ROUND,
  1556.                  GDK_JOIN_ROUND);
  1557.     }
  1558.  
  1559.   gdk_draw_rectangle(ifsDesign->pixmap,
  1560.              widget->style->bg_gc[widget->state],
  1561.              TRUE,
  1562.              event->area.x,
  1563.              event->area.y,
  1564.              event->area.width,event->area.height);
  1565.  
  1566.   /* draw an indicator for the center */
  1567.  
  1568.   cx = ifsvals.center_x * widget->allocation.width;
  1569.   cy = ifsvals.center_y * widget->allocation.width;
  1570.   gdk_draw_line(ifsDesign->pixmap,
  1571.         widget->style->fg_gc[widget->state],
  1572.         cx - 10, cy, cx + 10, cy);
  1573.   gdk_draw_line(ifsDesign->pixmap,
  1574.         widget->style->fg_gc[widget->state],
  1575.         cx, cy - 10, cx, cy + 10);
  1576.  
  1577.   for (i=0;i<ifsvals.num_elements;i++)
  1578.     {
  1579.       aff_element_draw(elements[i], element_selected[i],
  1580.                widget->allocation.width,
  1581.                widget->allocation.height,
  1582.                ifsDesign->pixmap,
  1583.                widget->style->fg_gc[widget->state],
  1584.                ifsDesign->selected_gc,
  1585.                ifsDesign->area->style->font);
  1586.     }
  1587.  
  1588.   gdk_draw_pixmap(widget->window,
  1589.           widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
  1590.           ifsDesign->pixmap,
  1591.           event->area.x, event->area.y,
  1592.           event->area.x, event->area.y,
  1593.           event->area.width, event->area.height);
  1594.  
  1595.   return FALSE;
  1596. }
  1597.  
  1598. static gint
  1599. design_area_configure (GtkWidget         *widget,
  1600.                GdkEventConfigure *event)
  1601. {
  1602.   gint i;
  1603.   gdouble width = widget->allocation.width;
  1604.   gdouble height = widget->allocation.height;
  1605.  
  1606.   for (i=0;i<ifsvals.num_elements;i++)
  1607.     aff_element_compute_trans(elements[i],width,height,
  1608.                   ifsvals.center_x, ifsvals.center_y);
  1609.   for (i=0;i<ifsvals.num_elements;i++)
  1610.     aff_element_compute_boundary(elements[i],width,height,
  1611.                  elements, ifsvals.num_elements);
  1612.  
  1613.   if (ifsDesign->pixmap)
  1614.     {
  1615.       gdk_pixmap_unref(ifsDesign->pixmap);
  1616.     }
  1617.   ifsDesign->pixmap = gdk_pixmap_new(widget->window,
  1618.                      widget->allocation.width,
  1619.                      widget->allocation.height,
  1620.                      gtk_preview_get_visual()->depth);
  1621.  
  1622.   return FALSE;
  1623. }
  1624.  
  1625. static gint
  1626. design_area_button_press (GtkWidget      *widget,
  1627.               GdkEventButton *event)
  1628. {
  1629.   gint i;
  1630.   gdouble width = ifsDesign->area->allocation.width;
  1631.   gint old_current;
  1632.  
  1633.   gtk_widget_grab_focus(widget);
  1634.  
  1635.   if (event->button != 1 ||
  1636.       (ifsDesign->button_state & GDK_BUTTON1_MASK))
  1637.     {
  1638.       if (event->button == 3)
  1639.       design_op_menu_popup(event->button, event->time);
  1640.       return FALSE;
  1641.     }
  1642.  
  1643.   old_current = ifsD->current_element;
  1644.   ifsD->current_element = -1;
  1645.  
  1646.   /* Find out where the button press was */
  1647.   for (i=0;i<ifsvals.num_elements;i++)
  1648.     {
  1649.       if ( ipolygon_contains(elements[i]->click_boundary,event->x,event->y) )
  1650.     {
  1651.       set_current_element(i);
  1652.       break;
  1653.     }
  1654.     }
  1655.  
  1656.   /* if the user started manipulating an object, set up a new
  1657.      position on the undo ring */
  1658.   if (ifsD->current_element >= 0)
  1659.     undo_begin();
  1660.  
  1661.   if (!(event->state & GDK_SHIFT_MASK)
  1662.       && ( (ifsD->current_element<0)
  1663.        || !element_selected[ifsD->current_element] ))
  1664.     {
  1665.       for (i=0;i<ifsvals.num_elements;i++)
  1666.     element_selected[i] = 0;
  1667.     }
  1668.  
  1669.   if (ifsD->current_element >= 0)
  1670.     {
  1671.       ifsDesign->button_state |= GDK_BUTTON1_MASK;
  1672.  
  1673.       element_selected[ifsD->current_element] = TRUE;
  1674.  
  1675.       ifsDesign->num_selected = 0;
  1676.       ifsDesign->op_xcenter = 0.0;
  1677.       ifsDesign->op_ycenter = 0.0;
  1678.       for (i=0;i<ifsvals.num_elements;i++)
  1679.     {
  1680.       if (element_selected[i])
  1681.         {
  1682.           ifsD->selected_orig[i] = *elements[i];
  1683.           ifsDesign->op_xcenter += elements[i]->v.x;
  1684.           ifsDesign->op_ycenter += elements[i]->v.y;
  1685.           ifsDesign->num_selected++;
  1686.           undo_update(i);
  1687.         }
  1688.     }
  1689.       ifsDesign->op_xcenter /= ifsDesign->num_selected;
  1690.       ifsDesign->op_ycenter /= ifsDesign->num_selected;
  1691.       ifsDesign->op_x = (gdouble)event->x/width;
  1692.       ifsDesign->op_y = (gdouble)event->y/width;
  1693.       ifsDesign->op_center_x = ifsvals.center_x;
  1694.       ifsDesign->op_center_y = ifsvals.center_y;
  1695.     }
  1696.   else
  1697.     {
  1698.       ifsD->current_element = old_current;
  1699.       element_selected[old_current] = TRUE;
  1700.     }
  1701.  
  1702.   design_area_redraw();
  1703.  
  1704.   return FALSE;
  1705. }
  1706.  
  1707. static gint
  1708. design_area_button_release (GtkWidget      *widget,
  1709.                 GdkEventButton *event)
  1710. {
  1711.   if (event->button == 1 &&
  1712.       (ifsDesign->button_state & GDK_BUTTON1_MASK))
  1713.     {
  1714.       ifsDesign->button_state &= ~GDK_BUTTON1_MASK;
  1715.       if (ifsD->auto_preview)
  1716.     ifs_compose_preview_callback(NULL, ifsD->preview);
  1717.     }
  1718.   return FALSE;
  1719. }
  1720.  
  1721. static gint
  1722. design_area_motion (GtkWidget      *widget,
  1723.             GdkEventMotion *event)
  1724. {
  1725.   gint i;
  1726.   gdouble xo;
  1727.   gdouble yo;
  1728.   gdouble xn;
  1729.   gdouble yn;
  1730.   gint px,py;
  1731.   gdouble width = ifsDesign->area->allocation.width;
  1732.   gdouble height = ifsDesign->area->allocation.height;
  1733.  
  1734.   Aff2 trans,t1,t2,t3;
  1735.  
  1736.   if (!(ifsDesign->button_state & GDK_BUTTON1_MASK)) return FALSE;
  1737.  
  1738.   if (event->is_hint)
  1739.     {
  1740.       gtk_widget_get_pointer(ifsDesign->area, &px, &py);
  1741.       event->x = px;
  1742.       event->y = py;
  1743.     }
  1744.  
  1745.   xo = (ifsDesign->op_x - ifsDesign->op_xcenter);
  1746.   yo = (ifsDesign->op_y - ifsDesign->op_ycenter);
  1747.   xn = (gdouble)event->x/width - ifsDesign->op_xcenter;
  1748.   yn = (gdouble)event->y/width - ifsDesign->op_ycenter;
  1749.  
  1750.   /*  for (i=0;i<num_elements;i++)
  1751.     {
  1752.       gtk_widget_draw(widget,&elements[i]->bounding_box);
  1753.     } */
  1754.  
  1755.   switch (ifsDesign->op)
  1756.     {
  1757.     case OP_ROTATE:
  1758.       {
  1759.     aff2_translate(&t1,-ifsDesign->op_xcenter*width,
  1760.                -ifsDesign->op_ycenter*width);
  1761.     aff2_scale(&t2,
  1762.            sqrt((SQR(xn)+SQR(yn))/(SQR(xo)+SQR(yo))),
  1763.            0);
  1764.     aff2_compose(&t3, &t2, &t1);
  1765.     aff2_rotate(&t1, - atan2(yn,xn) + atan2(yo,xo));
  1766.     aff2_compose(&t2, &t1, &t3);
  1767.     aff2_translate(&t3,ifsDesign->op_xcenter*width,
  1768.                ifsDesign->op_ycenter*width);
  1769.     aff2_compose(&trans, &t3, &t2);
  1770.     break;
  1771.       }
  1772.     case OP_STRETCH:
  1773.       {
  1774.     aff2_translate(&t1,-ifsDesign->op_xcenter*width,
  1775.                -ifsDesign->op_ycenter*width);
  1776.     aff2_compute_stretch(&t2, xo, yo, xn, yn);
  1777.     aff2_compose(&t3, &t2, &t1);
  1778.     aff2_translate(&t1,ifsDesign->op_xcenter*width,
  1779.                ifsDesign->op_ycenter*width);
  1780.     aff2_compose(&trans, &t1, &t3);
  1781.     break;
  1782.       }
  1783.     case OP_TRANSLATE:
  1784.       {
  1785.     aff2_translate(&trans,(xn-xo)*width,(yn-yo)*width);
  1786.     break;
  1787.       }
  1788.     }
  1789.  
  1790.   for (i=0;i<ifsvals.num_elements;i++)
  1791.     if (element_selected[i])
  1792.       {
  1793.     if (ifsDesign->num_selected == ifsvals.num_elements)
  1794.       {
  1795.         gdouble cx,cy;
  1796.         aff2_invert(&t1, &trans);
  1797.         aff2_compose(&t2, &trans, &ifsD->selected_orig[i].trans);
  1798.         aff2_compose(&elements[i]->trans, &t2, &t1);
  1799.  
  1800.         cx = ifsDesign->op_center_x * width;
  1801.         cy = ifsDesign->op_center_y * width;
  1802.         aff2_apply(&trans,cx,cy,&cx,&cy);
  1803.         ifsvals.center_x = cx / width;
  1804.         ifsvals.center_y = cy / width;
  1805.       }
  1806.     else
  1807.       {
  1808.         aff2_compose(&elements[i]->trans, &trans,
  1809.              &ifsD->selected_orig[i].trans);
  1810.       }
  1811.     aff_element_decompose_trans(elements[i],&elements[i]->trans,
  1812.                     width,height, ifsvals.center_x,
  1813.                     ifsvals.center_y);
  1814.     aff_element_compute_trans(elements[i],width,height,
  1815.                   ifsvals.center_x, ifsvals.center_y);
  1816.       }
  1817.  
  1818.   update_values();
  1819.   design_area_redraw();
  1820.  
  1821.   return FALSE;
  1822. }
  1823.  
  1824. static void
  1825. design_area_redraw (void)
  1826. {
  1827.   gint i;
  1828.   gdouble width = ifsDesign->area->allocation.width;
  1829.   gdouble height = ifsDesign->area->allocation.height;
  1830.  
  1831.   for (i=0;i<ifsvals.num_elements;i++)
  1832.     {
  1833.       aff_element_compute_boundary(elements[i],width,height,
  1834.                    elements,ifsvals.num_elements);
  1835.     }
  1836.   gtk_widget_draw(ifsDesign->area,NULL);
  1837. }
  1838.  
  1839. /* Undo ring functions */
  1840. static void
  1841. undo_begin (void)
  1842. {
  1843.   gint i,j;
  1844.   gint to_delete;
  1845.   gint new_index;
  1846.  
  1847.   if (undo_cur == UNDO_LEVELS-1)
  1848.     {
  1849.       to_delete = 1;
  1850.       undo_start = (undo_start + 1)%UNDO_LEVELS;
  1851.     }
  1852.   else
  1853.     {
  1854.       undo_cur++;
  1855.       to_delete = undo_num - undo_cur;
  1856.     }
  1857.  
  1858.   undo_num = undo_num - to_delete + 1;
  1859.   new_index = (undo_start+undo_cur)%UNDO_LEVELS;
  1860.  
  1861.   /* remove any redo elements or the oldest element if necessary */
  1862.   for (j=new_index;to_delete>0;j=(j+1)%UNDO_LEVELS,to_delete--)
  1863.     {
  1864.       for (i=0;i<undo_ring[j].ifsvals.num_elements;i++)
  1865.     if (undo_ring[j].elements[i])
  1866.       aff_element_free(undo_ring[j].elements[i]);
  1867.       g_free(undo_ring[j].elements);
  1868.       g_free(undo_ring[j].element_selected);
  1869.     }
  1870.  
  1871.   undo_ring[new_index].ifsvals = ifsvals;
  1872.   undo_ring[new_index].elements = g_new(AffElement *,ifsvals.num_elements);
  1873.   undo_ring[new_index].element_selected = g_new(gint,ifsvals.num_elements);
  1874.   undo_ring[new_index].current_element = ifsD->current_element;
  1875.  
  1876.   for (i=0;i<ifsvals.num_elements;i++)
  1877.     {
  1878.       undo_ring[new_index].elements[i] = NULL;
  1879.       undo_ring[new_index].element_selected[i] = element_selected[i];
  1880.     }
  1881. }
  1882.  
  1883. static void
  1884. undo_update (gint el)
  1885. {
  1886.   AffElement *elem;
  1887.   /* initialize */
  1888.   
  1889.   elem = NULL;
  1890.  
  1891.   if (!undo_ring[(undo_start+undo_cur)%UNDO_LEVELS].elements[el])
  1892.     undo_ring[(undo_start+undo_cur)%UNDO_LEVELS].elements[el]
  1893.       = elem = g_new(AffElement,1);
  1894.  
  1895.   *elem = *elements[el];
  1896.   elem->draw_boundary = NULL;
  1897.   elem->click_boundary = NULL;
  1898. }
  1899.  
  1900. static void
  1901. undo_exchange (gint el)
  1902. {
  1903.   gint i;
  1904.   AffElement **telements;
  1905.   gint *tselected;
  1906.   IfsComposeVals tifsvals;
  1907.   gint tcurrent;
  1908.   gdouble width = ifsDesign->area->allocation.width;
  1909.   gdouble height = ifsDesign->area->allocation.height;
  1910.  
  1911.   /* swap the arrays and values*/
  1912.   telements = elements;
  1913.   elements = undo_ring[el].elements;
  1914.   undo_ring[el].elements = telements;
  1915.  
  1916.   tifsvals = ifsvals;
  1917.   ifsvals = undo_ring[el].ifsvals;
  1918.   undo_ring[el].ifsvals = tifsvals;
  1919.  
  1920.   tselected = element_selected;
  1921.   element_selected = undo_ring[el].element_selected;
  1922.   undo_ring[el].element_selected = tselected;
  1923.  
  1924.   tcurrent = ifsD->current_element;
  1925.   ifsD->current_element = undo_ring[el].current_element;
  1926.   undo_ring[el].current_element = tcurrent;
  1927.  
  1928.   /* now swap back any unchanged elements */
  1929.   for (i=0;i<ifsvals.num_elements;i++)
  1930.     if (!elements[i])
  1931.       {
  1932.     elements[i] = undo_ring[el].elements[i];
  1933.     undo_ring[el].elements[i] = 0;
  1934.       }
  1935.     else
  1936.       aff_element_compute_trans(elements[i],width,height,
  1937.                 ifsvals.center_x,ifsvals.center_y);
  1938.  
  1939.   set_current_element(ifsD->current_element);
  1940.  
  1941.   design_area_redraw();
  1942.  
  1943.   if (ifsD->auto_preview)
  1944.     ifs_compose_preview_callback(NULL, ifsD->preview);
  1945. }
  1946.  
  1947. static void
  1948. undo (void)
  1949. {
  1950.   if (undo_cur >= 0)
  1951.     {
  1952.       undo_exchange((undo_start+undo_cur)%UNDO_LEVELS);
  1953.       undo_cur--;
  1954.     }
  1955. }
  1956.  
  1957. static void
  1958. redo (void)
  1959. {
  1960.   if (undo_cur != undo_num - 1)
  1961.     {
  1962.       undo_cur++;
  1963.       undo_exchange((undo_start+undo_cur)%UNDO_LEVELS);
  1964.     }
  1965. }
  1966.  
  1967. static void
  1968. design_area_select_all_callback (GtkWidget *w,
  1969.                  gpointer   data)
  1970. {
  1971.   gint i;
  1972.  
  1973.   for (i=0;i<ifsvals.num_elements;i++)
  1974.     element_selected[i] = TRUE;
  1975.  
  1976.   design_area_redraw();
  1977. }
  1978.  
  1979. /*  Interface functions  */
  1980.  
  1981. static void
  1982. val_changed_update (void)
  1983. {
  1984.   gdouble width = ifsDesign->area->allocation.width;
  1985.   gdouble height = ifsDesign->area->allocation.height;
  1986.   AffElement *cur = elements[ifsD->current_element];
  1987.  
  1988.   if (ifsD->in_update)
  1989.     return;
  1990.  
  1991.   undo_begin();
  1992.   undo_update(ifsD->current_element);
  1993.  
  1994.   cur->v = ifsD->current_vals;
  1995.   cur->v.theta *= G_PI/180.0;
  1996.   aff_element_compute_trans(cur,width,height,
  1997.                   ifsvals.center_x, ifsvals.center_y);
  1998.   aff_element_compute_color_trans(cur);
  1999.  
  2000.   design_area_redraw();
  2001.  
  2002.   if (ifsD->auto_preview)
  2003.     ifs_compose_preview_callback(NULL, ifsD->preview);
  2004. }
  2005.  
  2006. /* Pseudo-widget representing a color mapping */
  2007.  
  2008. #define COLOR_SAMPLE_SIZE 30
  2009.  
  2010. static void
  2011. color_map_set_preview_color (GtkWidget *preview,
  2012.                  IfsColor  *color)
  2013. {
  2014.   gint i;
  2015.   guchar buf[3*COLOR_SAMPLE_SIZE];
  2016.  
  2017.   for (i=0;i<COLOR_SAMPLE_SIZE;i++)
  2018.     {
  2019.       buf[3*i] = (guint)(255.999*color->vals[0]);
  2020.       buf[3*i+1] = (guint)(255.999*color->vals[1]);
  2021.       buf[3*i+2] = (guint)(255.999*color->vals[2]);
  2022.     }
  2023.   for (i=0;i<COLOR_SAMPLE_SIZE;i++)
  2024.     gtk_preview_draw_row(GTK_PREVIEW(preview),buf,0,i,COLOR_SAMPLE_SIZE);
  2025.  
  2026.   gtk_widget_draw (preview, NULL);
  2027. }
  2028.  
  2029. static ColorMap *
  2030. color_map_create (gchar    *name,
  2031.           IfsColor *orig_color,
  2032.           IfsColor *data,
  2033.           gint      fixed_point)
  2034. {
  2035.   GtkWidget *frame;
  2036.   GtkWidget *arrow;
  2037.  
  2038.   ColorMap *color_map = g_new (ColorMap,1);
  2039.   color_map->name = name;
  2040.   color_map->color = data;
  2041.   color_map->fixed_point = fixed_point;
  2042.  
  2043.   color_map->hbox = gtk_hbox_new (FALSE,2);
  2044.  
  2045.   frame = gtk_frame_new (NULL);
  2046.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  2047.   gtk_box_pack_start (GTK_BOX (color_map->hbox), frame, FALSE, FALSE, 0);
  2048.   gtk_widget_show (frame);
  2049.  
  2050.   color_map->orig_preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  2051.   gtk_preview_size (GTK_PREVIEW (color_map->orig_preview),
  2052.             COLOR_SAMPLE_SIZE, COLOR_SAMPLE_SIZE);
  2053.   gtk_container_add (GTK_CONTAINER(frame), color_map->orig_preview);
  2054.   gtk_widget_show (color_map->orig_preview);
  2055.  
  2056.   if (fixed_point)
  2057.     color_map_set_preview_color(color_map->orig_preview,data);
  2058.   else
  2059.     color_map_set_preview_color(color_map->orig_preview,orig_color);
  2060.  
  2061.   arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_IN);
  2062.   gtk_box_pack_start (GTK_BOX (color_map->hbox), arrow, FALSE, FALSE, 0);
  2063.   gtk_widget_show (arrow);
  2064.  
  2065.   color_map->button = gimp_color_button_double_new (color_map->name,
  2066.                             COLOR_SAMPLE_SIZE, COLOR_SAMPLE_SIZE,
  2067.                             color_map->color->vals, 3);
  2068.  
  2069.   gtk_box_pack_start (GTK_BOX (color_map->hbox), color_map->button, 
  2070.               FALSE, FALSE, 0);
  2071.   gtk_widget_show (color_map->button);
  2072.  
  2073.   gtk_signal_connect (GTK_OBJECT (color_map->button), "color_changed",
  2074.               GTK_SIGNAL_FUNC (color_map_color_changed_cb),
  2075.               color_map);
  2076.  
  2077.   return color_map;
  2078. }
  2079.  
  2080. static void
  2081. color_map_color_changed_cb (GtkWidget *widget,
  2082.                 ColorMap  *color_map)
  2083. {
  2084.   undo_begin ();
  2085.   undo_update (ifsD->current_element);
  2086.  
  2087.   elements[ifsD->current_element]->v = ifsD->current_vals;
  2088.   elements[ifsD->current_element]->v.theta *= G_PI/180.0;
  2089.   aff_element_compute_color_trans(elements[ifsD->current_element]);
  2090.  
  2091.   update_values();
  2092.  
  2093.   if (ifsD->auto_preview)
  2094.     ifs_compose_preview_callback(NULL,ifsD->preview);
  2095. }
  2096.  
  2097. static void
  2098. color_map_update (ColorMap *color_map)
  2099. {
  2100.   gimp_color_button_update (GIMP_COLOR_BUTTON (color_map->button));
  2101.  
  2102.   if (color_map->fixed_point)
  2103.     color_map_set_preview_color(color_map->orig_preview, color_map->color);
  2104. }
  2105.  
  2106. static void
  2107. simple_color_set_sensitive (void)
  2108. {
  2109.   gint sc = elements[ifsD->current_element]->v.simple_color;
  2110.  
  2111.   gtk_widget_set_sensitive(ifsD->target_cmap->hbox,sc);
  2112.   gtk_widget_set_sensitive(ifsD->hue_scale_pair->scale,sc);
  2113.   gtk_widget_set_sensitive(ifsD->hue_scale_pair->entry,sc);
  2114.   gtk_widget_set_sensitive(ifsD->value_scale_pair->scale,sc);
  2115.   gtk_widget_set_sensitive(ifsD->value_scale_pair->entry,sc);
  2116.  
  2117.   gtk_widget_set_sensitive(ifsD->red_cmap->hbox,!sc);
  2118.   gtk_widget_set_sensitive(ifsD->green_cmap->hbox,!sc);
  2119.   gtk_widget_set_sensitive(ifsD->blue_cmap->hbox,!sc);
  2120.   gtk_widget_set_sensitive(ifsD->black_cmap->hbox,!sc);
  2121. }
  2122.  
  2123. static void
  2124. simple_color_toggled (GtkWidget *widget,
  2125.               gpointer   data)
  2126. {
  2127.   AffElement *cur = elements[ifsD->current_element];
  2128.   cur->v.simple_color = GTK_TOGGLE_BUTTON(widget)->active;
  2129.   ifsD->current_vals.simple_color = cur->v.simple_color;
  2130.   if (cur->v.simple_color)
  2131.     {
  2132.       aff_element_compute_color_trans(cur);
  2133.       val_changed_update();
  2134.     }
  2135.   simple_color_set_sensitive();
  2136. }
  2137.  
  2138. /* Generic mechanism for scale/entry combination (possibly without
  2139.    scale) */
  2140.  
  2141. static ValuePair *
  2142. value_pair_create (gpointer      data,
  2143.            gdouble       lower,
  2144.            gdouble       upper,
  2145.            gboolean      create_scale,
  2146.            ValuePairType type)
  2147. {
  2148.  
  2149.   ValuePair *value_pair = g_new(ValuePair,1);
  2150.   value_pair->data.d = data;
  2151.   value_pair->type = type;
  2152.  
  2153.   value_pair->adjustment =
  2154.     gtk_adjustment_new (1.0, lower, upper,
  2155.             (upper-lower) / 100, (upper-lower) / 10,
  2156.             0.0);
  2157.   /* We need to sink the adjustment, since we may not create a scale for
  2158.    * it, so nobody will assume the initial refcount
  2159.    */
  2160.   gtk_object_ref (value_pair->adjustment);
  2161.   gtk_object_sink (value_pair->adjustment);
  2162.   gtk_signal_connect (GTK_OBJECT (value_pair->adjustment), "value_changed",
  2163.               GTK_SIGNAL_FUNC (value_pair_scale_callback),
  2164.               value_pair);
  2165.  
  2166.   if (create_scale)
  2167.     {
  2168.       value_pair->scale = gtk_hscale_new(GTK_ADJUSTMENT (value_pair->adjustment));
  2169.       gtk_widget_ref (value_pair->scale);
  2170.  
  2171.       if (type == VALUE_PAIR_INT)
  2172.       gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 0);
  2173.       else
  2174.       gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 2);
  2175.  
  2176.       gtk_scale_set_draw_value (GTK_SCALE (value_pair->scale), FALSE);
  2177.       gtk_signal_connect (GTK_OBJECT (value_pair->scale),
  2178.               "button_release_event",
  2179.               (GtkSignalFunc) value_pair_button_release,
  2180.               NULL);
  2181.     }
  2182.   else
  2183.     value_pair->scale = NULL;
  2184.  
  2185.   /* We destroy the value pair when the entry is destroyed, so
  2186.    * we don't need to hold a refcount on the entry
  2187.    */
  2188.  
  2189.   value_pair->entry = gtk_entry_new ();
  2190.   gtk_widget_set_usize (value_pair->entry, ENTRY_WIDTH, 0);
  2191.   value_pair->entry_handler_id =
  2192.     gtk_signal_connect (GTK_OBJECT (value_pair->entry), "changed",
  2193.             GTK_SIGNAL_FUNC (value_pair_entry_callback),
  2194.             value_pair);
  2195.   gtk_signal_connect (GTK_OBJECT (value_pair->entry), "destroy",
  2196.               GTK_SIGNAL_FUNC (value_pair_destroy_callback),
  2197.               value_pair);
  2198.  
  2199.   return value_pair;
  2200. }
  2201.  
  2202. static void
  2203. value_pair_update (ValuePair *value_pair)
  2204. {
  2205.   gchar buffer[32];
  2206.  
  2207.   if (value_pair->type == VALUE_PAIR_INT)
  2208.     {
  2209.       GTK_ADJUSTMENT(value_pair->adjustment)->value = *value_pair->data.i;
  2210.       sprintf (buffer, "%d", *value_pair->data.i);
  2211.     }
  2212.   else
  2213.     {
  2214.       GTK_ADJUSTMENT(value_pair->adjustment)->value = *value_pair->data.d;
  2215.       sprintf (buffer, "%0.2f", *value_pair->data.d);
  2216.     }
  2217.   gtk_signal_emit_by_name (value_pair->adjustment, "value_changed");
  2218.  
  2219.   gtk_signal_handler_block(GTK_OBJECT(value_pair->entry),
  2220.                value_pair->entry_handler_id);
  2221.   gtk_entry_set_text (GTK_ENTRY (value_pair->entry), buffer);
  2222.   gtk_signal_handler_unblock(GTK_OBJECT(value_pair->entry),
  2223.                  value_pair->entry_handler_id);
  2224. }
  2225.  
  2226. static void
  2227. value_pair_button_release (GtkWidget      *widget,
  2228.                GdkEventButton *event,
  2229.                gpointer        data)
  2230. {
  2231.   val_changed_update ();
  2232. }
  2233.  
  2234. static void
  2235. value_pair_scale_callback (GtkAdjustment *adjustment,
  2236.                ValuePair     *value_pair)
  2237. {
  2238.   gchar buffer[32];
  2239.   gint changed = FALSE;
  2240.  
  2241.   if (value_pair->type == VALUE_PAIR_DOUBLE)
  2242.     {
  2243.       if ((gfloat)*value_pair->data.d != adjustment->value)
  2244.     {
  2245.       changed = TRUE;
  2246.       *value_pair->data.d = adjustment->value;
  2247.       sprintf (buffer, "%0.2f", adjustment->value);
  2248.     }
  2249.     }
  2250.   else
  2251.     {
  2252.       if (*value_pair->data.i != (gint)adjustment->value)
  2253.     {
  2254.       changed = TRUE;
  2255.       *value_pair->data.i = adjustment->value;
  2256.       sprintf (buffer, "%d", (gint)adjustment->value);
  2257.     }
  2258.     }
  2259.   if (changed)
  2260.     {
  2261.       gtk_signal_handler_block(GTK_OBJECT(value_pair->entry),
  2262.                    value_pair->entry_handler_id);
  2263.       gtk_entry_set_text (GTK_ENTRY (value_pair->entry), buffer);
  2264.       gtk_signal_handler_unblock(GTK_OBJECT(value_pair->entry),
  2265.                  value_pair->entry_handler_id);
  2266.     }
  2267. }
  2268.  
  2269. static void
  2270. value_pair_entry_callback (GtkWidget *widget,
  2271.                ValuePair *value_pair)
  2272. {
  2273.   GtkAdjustment *adjustment = GTK_ADJUSTMENT (value_pair->adjustment);
  2274.   gdouble new_value;
  2275.   gdouble old_value;
  2276.  
  2277.   if (value_pair->type == VALUE_PAIR_INT)
  2278.     {
  2279.       old_value = *value_pair->data.i;
  2280.       new_value = atoi(gtk_entry_get_text(GTK_ENTRY(widget)));
  2281.     }
  2282.   else
  2283.     {
  2284.       old_value = *value_pair->data.d;
  2285.       new_value = atof(gtk_entry_get_text(GTK_ENTRY(widget)));
  2286.     }
  2287.  
  2288.   if (floor(0.5+old_value*10000) != floor(0.5+new_value*10000))
  2289.     {
  2290.       if ((new_value >= adjustment->lower) &&
  2291.       (new_value <= adjustment->upper))
  2292.     {
  2293.       if (value_pair->type == VALUE_PAIR_INT)
  2294.         *value_pair->data.i = new_value;
  2295.       else
  2296.         *value_pair->data.d = new_value;
  2297.       adjustment->value = new_value;
  2298.       gtk_signal_emit_by_name(GTK_OBJECT(adjustment), "value_changed");
  2299.  
  2300.       val_changed_update();
  2301.     }
  2302.     }
  2303. }
  2304.  
  2305. static void
  2306. value_pair_destroy_callback (GtkWidget *widget,
  2307.                  ValuePair *value_pair)
  2308. {
  2309.   if (value_pair->scale)
  2310.     gtk_object_unref (GTK_OBJECT (value_pair->scale));
  2311.   gtk_object_unref (value_pair->adjustment);
  2312. }
  2313.  
  2314. static void
  2315. design_op_callback (GtkWidget *widget,
  2316.             gpointer   data)
  2317. {
  2318.   DesignOp op = (DesignOp)data;
  2319.  
  2320.   if (op != ifsDesign->op)
  2321.     {
  2322.       switch (ifsDesign->op)
  2323.     {
  2324.     case OP_TRANSLATE:
  2325.       gtk_signal_handler_block(GTK_OBJECT(ifsD->move_button),
  2326.                    ifsD->move_handler);
  2327.       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ifsD->move_button),
  2328.                       FALSE);
  2329.       gtk_signal_handler_unblock(GTK_OBJECT(ifsD->move_button),
  2330.                      ifsD->move_handler);
  2331.       break;
  2332.     case OP_ROTATE:
  2333.       gtk_signal_handler_block(GTK_OBJECT(ifsD->rotate_button),
  2334.                    ifsD->rotate_handler);
  2335.       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ifsD->rotate_button),
  2336.                       FALSE);
  2337.       gtk_signal_handler_unblock(GTK_OBJECT(ifsD->rotate_button),
  2338.                      ifsD->rotate_handler);
  2339.       break;
  2340.     case OP_STRETCH:
  2341.       gtk_signal_handler_block(GTK_OBJECT(ifsD->stretch_button),
  2342.                    ifsD->stretch_handler);
  2343.       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ifsD->stretch_button),
  2344.                       FALSE);
  2345.       gtk_signal_handler_unblock(GTK_OBJECT(ifsD->stretch_button),
  2346.                      ifsD->stretch_handler);
  2347.       break;
  2348.     }
  2349.       ifsDesign->op = op;
  2350.     }
  2351.   else
  2352.     {
  2353.       GTK_TOGGLE_BUTTON(widget)->active = TRUE;
  2354.     }
  2355. }
  2356.  
  2357. static void
  2358. design_op_update_callback (GtkWidget *widget,
  2359.                gpointer   data)
  2360. {
  2361.   DesignOp op = (DesignOp)data;
  2362.  
  2363.   if (op != ifsDesign->op)
  2364.     {
  2365.       switch (op)
  2366.     {
  2367.     case OP_TRANSLATE:
  2368.       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(ifsD->move_button),
  2369.                     TRUE);
  2370.       break;
  2371.     case OP_ROTATE:
  2372.       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(ifsD->rotate_button),
  2373.                     TRUE);
  2374.       break;
  2375.     case OP_STRETCH:
  2376.       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(ifsD->stretch_button),
  2377.                     TRUE);
  2378.       break;
  2379.     }
  2380.     }
  2381. }
  2382.  
  2383. static void
  2384. recompute_center_cb (GtkWidget *widget,
  2385.              gpointer   data)
  2386. {
  2387.   recompute_center (TRUE);
  2388. }
  2389.  
  2390. static void
  2391. recompute_center (gboolean save_undo)
  2392. {
  2393.   gint    i;
  2394.   gdouble x,y;
  2395.   gdouble center_x = 0.0;
  2396.   gdouble center_y = 0.0;
  2397.  
  2398.   gdouble width = ifsDesign->area->allocation.width;
  2399.   gdouble height = ifsDesign->area->allocation.height;
  2400.  
  2401.   if (save_undo)
  2402.     undo_begin();
  2403.  
  2404.   for (i=0;i<ifsvals.num_elements;i++)
  2405.     {
  2406.       if (save_undo)
  2407.     undo_update(i);
  2408.       aff_element_compute_trans(elements[i],1,ifsvals.aspect_ratio,
  2409.                 ifsvals.center_x, ifsvals.center_y);
  2410.       aff2_fixed_point(&elements[i]->trans,&x,&y);
  2411.       center_x += x;
  2412.       center_y += y;
  2413.     }
  2414.  
  2415.   ifsvals.center_x = center_x/ifsvals.num_elements;
  2416.   ifsvals.center_y = center_y/ifsvals.num_elements;
  2417.  
  2418.   for (i=0;i<ifsvals.num_elements;i++)
  2419.     {
  2420.     aff_element_decompose_trans(elements[i],&elements[i]->trans,
  2421.                     1,ifsvals.aspect_ratio,
  2422.                     ifsvals.center_x, ifsvals.center_y);
  2423.     }
  2424.  
  2425.   if (width > 1 && height > 1)
  2426.     {
  2427.       for (i=0;i<ifsvals.num_elements;i++)
  2428.     aff_element_compute_trans(elements[i],width,height,
  2429.                   ifsvals.center_x, ifsvals.center_y);
  2430.       design_area_redraw();
  2431.       update_values();
  2432.     }
  2433. }
  2434.  
  2435. static void
  2436. auto_preview_callback (GtkWidget *widget,
  2437.                gpointer   data)
  2438. {
  2439.   if (ifsD->auto_preview)
  2440.     {
  2441.       ifsD->auto_preview = 0;
  2442.     }
  2443.   else
  2444.     {
  2445.       ifsD->auto_preview = 1;
  2446.       ifs_compose_preview_callback(NULL, ifsD->preview);
  2447.     }
  2448. }
  2449.  
  2450. static void
  2451. flip_check_button_callback (GtkWidget *widget,
  2452.                 gpointer   data)
  2453. {
  2454.   ifsD->current_vals.flip = GTK_TOGGLE_BUTTON(widget)->active;
  2455.   val_changed_update ();
  2456. }
  2457.  
  2458. static void
  2459. ifs_compose_set_defaults (void)
  2460. {
  2461.   gint i;
  2462.   IfsColor color;
  2463.   guchar rc,bc,gc;
  2464.  
  2465.   gimp_palette_get_foreground (&rc,&gc,&bc);
  2466.  
  2467.   color.vals[0] = (gdouble)rc/255;
  2468.   color.vals[1] = (gdouble)gc/255;
  2469.   color.vals[2] = (gdouble)bc/255;
  2470.  
  2471.   ifsvals.aspect_ratio = (gdouble)ifsD->drawable_height/ifsD->drawable_width;
  2472.  
  2473.   for (i=0;i<ifsvals.num_elements;i++)
  2474.     aff_element_free(elements[i]);
  2475.   count_for_naming = 0;
  2476.  
  2477.   ifsvals.num_elements = 3;
  2478.   elements = g_realloc(elements, ifsvals.num_elements*sizeof(AffElement *));
  2479.   element_selected = g_realloc(element_selected,
  2480.                    ifsvals.num_elements*sizeof(gint));
  2481.  
  2482.   elements[0] = aff_element_new(0.3,0.37*ifsvals.aspect_ratio,color,
  2483.                 ++count_for_naming);
  2484.   element_selected[0] = FALSE;
  2485.   elements[1] = aff_element_new(0.7,0.37*ifsvals.aspect_ratio,color,
  2486.                 ++count_for_naming);
  2487.   element_selected[1] = FALSE;
  2488.   elements[2] = aff_element_new(0.5,0.7*ifsvals.aspect_ratio,color,
  2489.                 ++count_for_naming);
  2490.   element_selected[2] = FALSE;
  2491.  
  2492.   ifsvals.center_x = 0.5;
  2493.   ifsvals.center_y = 0.5*ifsvals.aspect_ratio;
  2494.   ifsvals.iterations = ifsD->drawable_height*ifsD->drawable_width;
  2495.  
  2496.   ifsvals.subdivide = 3;
  2497.   ifsvals.max_memory = 4096;
  2498.  
  2499.   if (ifsOptD)
  2500.     {
  2501.       value_pair_update(ifsOptD->iterations_pair);
  2502.       value_pair_update(ifsOptD->subdivide_pair);
  2503.       value_pair_update(ifsOptD->radius_pair);
  2504.       value_pair_update(ifsOptD->memory_pair);
  2505.     }
  2506.  
  2507.   ifsvals.radius = 0.7;
  2508.  
  2509.   set_current_element(0);
  2510.   element_selected[0] = TRUE;
  2511.   recompute_center(FALSE);
  2512.  
  2513.   if (ifsD->selected_orig)
  2514.     g_free(ifsD->selected_orig);
  2515.  
  2516.   ifsD->selected_orig = g_new(AffElement,ifsvals.num_elements);
  2517. }
  2518.  
  2519. static void
  2520. ifs_compose_defaults_callback (GtkWidget *widget,
  2521.                    gpointer   data)
  2522. {
  2523.   gint i;
  2524.   gdouble width = ifsDesign->area->allocation.width;
  2525.   gdouble height = ifsDesign->area->allocation.height;
  2526.  
  2527.   undo_begin();
  2528.   for (i=0;i<ifsvals.num_elements;i++)
  2529.     undo_update(i);
  2530.  
  2531.   ifs_compose_set_defaults();
  2532.  
  2533.   if (ifsD->auto_preview)
  2534.     ifs_compose_preview_callback(NULL, ifsD->preview);
  2535.  
  2536.   for (i=0;i<ifsvals.num_elements;i++)
  2537.     aff_element_compute_trans(elements[i],width,height,
  2538.                   ifsvals.center_x, ifsvals.center_y);
  2539.  
  2540.   design_area_redraw();
  2541. }
  2542.  
  2543. static void
  2544. ifs_compose_new_callback (GtkWidget *widget,
  2545.               gpointer   data)
  2546. {
  2547.   IfsColor color;
  2548.   guchar rc,bc,gc;
  2549.   gint i;
  2550.   gdouble width = ifsDesign->area->allocation.width;
  2551.   gdouble height = ifsDesign->area->allocation.height;
  2552.   AffElement *elem;
  2553.  
  2554.   undo_begin();
  2555.  
  2556.   gimp_palette_get_foreground (&rc,&gc,&bc);
  2557.  
  2558.   color.vals[0] = (gdouble)rc/255;
  2559.   color.vals[1] = (gdouble)gc/255;
  2560.   color.vals[2] = (gdouble)bc/255;
  2561.  
  2562.   elem = aff_element_new(0.5, 0.5*height/width,color,
  2563.              ++count_for_naming);
  2564.  
  2565.   ifsvals.num_elements++;
  2566.   elements = g_realloc(elements, ifsvals.num_elements*sizeof(AffElement *));
  2567.   element_selected = g_realloc(element_selected,
  2568.                    ifsvals.num_elements*sizeof(gint));
  2569.  
  2570.   for (i=0;i<ifsvals.num_elements-1;i++)
  2571.     element_selected[i] = FALSE;
  2572.   element_selected[ifsvals.num_elements-1] = TRUE;
  2573.  
  2574.   elements[ifsvals.num_elements-1] = elem;
  2575.   set_current_element(ifsvals.num_elements-1);
  2576.  
  2577.   ifsD->selected_orig = g_realloc(ifsD->selected_orig,
  2578.                   ifsvals.num_elements*sizeof(AffElement));
  2579.   aff_element_compute_trans(elem,width,height,
  2580.                   ifsvals.center_x, ifsvals.center_y);
  2581.  
  2582.   design_area_redraw();
  2583.  
  2584.   if (ifsD->auto_preview)
  2585.     ifs_compose_preview_callback(NULL, ifsD->preview);
  2586. }
  2587.  
  2588. static void
  2589. ifs_compose_delete_callback (GtkWidget *widget,
  2590.                  gpointer   data)
  2591. {
  2592.   gint i;
  2593.   gint new_current;
  2594.   if (ifsvals.num_elements <= 2)
  2595.     return;
  2596.  
  2597.   undo_begin();
  2598.   undo_update(ifsD->current_element);
  2599.  
  2600.   aff_element_free(elements[ifsD->current_element]);
  2601.  
  2602.   if (ifsD->current_element < ifsvals.num_elements-1)
  2603.     {
  2604.       undo_update(ifsvals.num_elements-1);
  2605.       elements[ifsD->current_element] = elements[ifsvals.num_elements-1];
  2606.       new_current = ifsD->current_element;
  2607.     }
  2608.   else
  2609.     new_current = ifsvals.num_elements-2;
  2610.  
  2611.   ifsvals.num_elements--;
  2612.  
  2613.   for (i=0;i<ifsvals.num_elements;i++)
  2614.     if (element_selected[i])
  2615.       {
  2616.     new_current = i;
  2617.     break;
  2618.       }
  2619.  
  2620.   element_selected[new_current] = TRUE;
  2621.   set_current_element(new_current);
  2622.  
  2623.   design_area_redraw();
  2624.  
  2625.   if (ifsD->auto_preview)
  2626.     ifs_compose_preview_callback(NULL, ifsD->preview);
  2627. }
  2628.  
  2629. static void
  2630. ifs_compose_close_callback (GtkWidget *widget,
  2631.                 GtkWidget **destroyed_widget)
  2632. {
  2633.   *destroyed_widget = NULL;
  2634.   gtk_main_quit ();
  2635. }
  2636.  
  2637. static gint
  2638. preview_idle_render (void)
  2639. {
  2640.   gint i;
  2641.   gint width = GTK_WIDGET(ifsD->preview)->requisition.width;
  2642.   gint height = GTK_WIDGET(ifsD->preview)->requisition.height;
  2643.  
  2644.   gint iterations = PREVIEW_RENDER_CHUNK;
  2645.   if (iterations > ifsD->preview_iterations)
  2646.     iterations = ifsD->preview_iterations;
  2647.  
  2648.   for (i=0;i<ifsvals.num_elements;i++)
  2649.     aff_element_compute_trans(elements[i], width, height,
  2650.                   ifsvals.center_x, ifsvals.center_y);
  2651.  
  2652.   ifs_render(elements,ifsvals.num_elements,width,height,
  2653.          iterations,&ifsvals,0,height,
  2654.          ifsD->preview_data,NULL,NULL,TRUE);
  2655.  
  2656.   for (i=0;i<ifsvals.num_elements;i++)
  2657.     aff_element_compute_trans(elements[i],
  2658.                   ifsDesign->area->allocation.width,
  2659.                   ifsDesign->area->allocation.height,
  2660.                   ifsvals.center_x, ifsvals.center_y);
  2661.  
  2662.   ifsD->preview_iterations -= iterations;
  2663.  
  2664.   for (i = 0; i < height; i++)
  2665.     gtk_preview_draw_row (GTK_PREVIEW (ifsD->preview),
  2666.               ifsD->preview_data + i * width * 3,
  2667.               0, i, width);
  2668.   gtk_widget_draw (ifsD->preview, NULL);
  2669.  
  2670.   return (ifsD->preview_iterations != 0);
  2671. }
  2672.  
  2673. static void
  2674. ifs_compose_preview_callback (GtkWidget *widget,
  2675.                   GtkWidget *preview)
  2676. {
  2677.   /* Expansion isn't really supported for previews */
  2678.   gint i;
  2679.   gint width = GTK_WIDGET(ifsD->preview)->requisition.width;
  2680.   gint height = GTK_WIDGET(ifsD->preview)->requisition.height;
  2681.   guchar rc,gc,bc;
  2682.   guchar *ptr;
  2683.  
  2684.   if (!ifsD->preview_data)
  2685.     ifsD->preview_data = g_new(guchar,3*width*height);
  2686.  
  2687.   gimp_palette_get_background ( &rc, &gc, &bc );
  2688.  
  2689.   ptr = ifsD->preview_data;
  2690.   for (i=0;i<width*height;i++)
  2691.     {
  2692.       *ptr++ = rc;
  2693.       *ptr++ = gc;
  2694.       *ptr++ = bc;
  2695.     }
  2696.  
  2697.   if (ifsD->preview_iterations == 0)
  2698.     gtk_idle_add ((GtkFunction)preview_idle_render, NULL);
  2699.  
  2700.   ifsD->preview_iterations = ifsvals.iterations*((gdouble)width*height/
  2701.                  (ifsD->drawable_width*ifsD->drawable_height));
  2702. }
  2703.  
  2704. static void
  2705. ifs_compose_ok_callback (GtkWidget *widget,
  2706.              GtkWidget *window)
  2707. {
  2708.   ifscint.run = TRUE;
  2709.  
  2710.   gtk_widget_destroy (window);
  2711. }
  2712.