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