1 /**********************************************************************
2    GIMP - The GNU Image Manipulation Program
3    Copyright (C) 1995 Spencer Kimball and Peter Mattis
4 
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  *********************************************************************/
18 
19 #include "config.h"
20 
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <glib/gstdio.h>
26 
27 #include <libgimp/gimp.h>
28 #include <libgimp/gimpui.h>
29 
30 #include "fractal-explorer.h"
31 #include "fractal-explorer-dialogs.h"
32 
33 #include "libgimp/stdplugins-intl.h"
34 
35 
36 #define ZOOM_UNDO_SIZE 100
37 
38 
39 static gint              n_gradient_samples = 0;
40 static gdouble          *gradient_samples = NULL;
41 static gchar            *gradient_name    = NULL;
42 static gboolean          ready_now = FALSE;
43 static gchar            *tpath = NULL;
44 static DialogElements   *elements = NULL;
45 static GtkWidget        *cmap_preview;
46 static GtkWidget        *maindlg;
47 
48 static explorer_vals_t  zooms[ZOOM_UNDO_SIZE];
49 static gint             zoomindex = 0;
50 static gint             zoommax = 0;
51 
52 static gint              oldxpos = -1;
53 static gint              oldypos = -1;
54 static gdouble           x_press = -1.0;
55 static gdouble           y_press = -1.0;
56 
57 static explorer_vals_t standardvals =
58 {
59   0,
60   -2.0,
61   2.0,
62   -1.5,
63   1.5,
64   50.0,
65   -0.75,
66   -0.2,
67   0,
68   1.0,
69   1.0,
70   1.0,
71   1,
72   1,
73   0,
74   0,
75   0,
76   0,
77   1,
78   256,
79   0
80 };
81 
82 /**********************************************************************
83  FORWARD DECLARATIONS
84  *********************************************************************/
85 
86 static void load_file_chooser_response (GtkFileChooser *chooser,
87                                         gint            response_id,
88                                         gpointer        data);
89 static void save_file_chooser_response (GtkFileChooser *chooser,
90                                         gint            response_id,
91                                         gpointer        data);
92 static void create_load_file_chooser   (GtkWidget      *widget,
93                                         GtkWidget      *dialog);
94 static void create_save_file_chooser   (GtkWidget      *widget,
95                                         GtkWidget      *dialog);
96 
97 static void cmap_preview_size_allocate (GtkWidget      *widget,
98                                         GtkAllocation  *allocation);
99 
100 /**********************************************************************
101  CALLBACKS
102  *********************************************************************/
103 
104 static void
dialog_response(GtkWidget * widget,gint response_id,gpointer data)105 dialog_response (GtkWidget *widget,
106                  gint       response_id,
107                  gpointer   data)
108 {
109   switch (response_id)
110     {
111     case GTK_RESPONSE_OK:
112       wint.run = TRUE;
113       gtk_widget_destroy (widget);
114       break;
115 
116     default:
117       gtk_widget_destroy (widget);
118       break;
119     }
120 }
121 
122 static void
dialog_reset_callback(GtkWidget * widget,gpointer data)123 dialog_reset_callback (GtkWidget *widget,
124                        gpointer   data)
125 {
126   wvals.xmin = standardvals.xmin;
127   wvals.xmax = standardvals.xmax;
128   wvals.ymin = standardvals.ymin;
129   wvals.ymax = standardvals.ymax;
130   wvals.iter = standardvals.iter;
131   wvals.cx   = standardvals.cx;
132   wvals.cy   = standardvals.cy;
133 
134   dialog_change_scale ();
135   set_cmap_preview ();
136   dialog_update_preview ();
137 }
138 
139 static void
dialog_redraw_callback(GtkWidget * widget,gpointer data)140 dialog_redraw_callback (GtkWidget *widget,
141                         gpointer   data)
142 {
143   gint alwaysprev = wvals.alwayspreview;
144 
145   wvals.alwayspreview = TRUE;
146   set_cmap_preview ();
147   dialog_update_preview ();
148   wvals.alwayspreview = alwaysprev;
149 }
150 
151 static void
dialog_undo_zoom_callback(GtkWidget * widget,gpointer data)152 dialog_undo_zoom_callback (GtkWidget *widget,
153                            gpointer   data)
154 {
155   if (zoomindex > 0)
156     {
157       zooms[zoomindex] = wvals;
158       zoomindex--;
159       wvals = zooms[zoomindex];
160       dialog_change_scale ();
161       set_cmap_preview ();
162       dialog_update_preview ();
163     }
164 }
165 
166 static void
dialog_redo_zoom_callback(GtkWidget * widget,gpointer data)167 dialog_redo_zoom_callback (GtkWidget *widget,
168                            gpointer   data)
169 {
170   if (zoomindex < zoommax)
171     {
172       zoomindex++;
173       wvals = zooms[zoomindex];
174       dialog_change_scale ();
175       set_cmap_preview ();
176       dialog_update_preview ();
177     }
178 }
179 
180 static void
dialog_step_in_callback(GtkWidget * widget,gpointer data)181 dialog_step_in_callback (GtkWidget *widget,
182                          gpointer   data)
183 {
184   double xdifferenz;
185   double ydifferenz;
186 
187   if (zoomindex < ZOOM_UNDO_SIZE - 1)
188     {
189       zooms[zoomindex]=wvals;
190       zoomindex++;
191     }
192   zoommax = zoomindex;
193 
194   xdifferenz =  wvals.xmax - wvals.xmin;
195   ydifferenz =  wvals.ymax - wvals.ymin;
196   wvals.xmin += 1.0 / 6.0 * xdifferenz;
197   wvals.ymin += 1.0 / 6.0 * ydifferenz;
198   wvals.xmax -= 1.0 / 6.0 * xdifferenz;
199   wvals.ymax -= 1.0 / 6.0 * ydifferenz;
200   zooms[zoomindex] = wvals;
201 
202   dialog_change_scale ();
203   set_cmap_preview ();
204   dialog_update_preview ();
205 }
206 
207 static void
dialog_step_out_callback(GtkWidget * widget,gpointer data)208 dialog_step_out_callback (GtkWidget *widget,
209                           gpointer   data)
210 {
211   gdouble xdifferenz;
212   gdouble ydifferenz;
213 
214   if (zoomindex < ZOOM_UNDO_SIZE - 1)
215     {
216       zooms[zoomindex]=wvals;
217       zoomindex++;
218     }
219   zoommax = zoomindex;
220 
221   xdifferenz =  wvals.xmax - wvals.xmin;
222   ydifferenz =  wvals.ymax - wvals.ymin;
223   wvals.xmin -= 1.0 / 4.0 * xdifferenz;
224   wvals.ymin -= 1.0 / 4.0 * ydifferenz;
225   wvals.xmax += 1.0 / 4.0 * xdifferenz;
226   wvals.ymax += 1.0 / 4.0 * ydifferenz;
227   zooms[zoomindex] = wvals;
228 
229   dialog_change_scale ();
230   set_cmap_preview ();
231   dialog_update_preview ();
232 }
233 
234 static void
explorer_toggle_update(GtkWidget * widget,gpointer data)235 explorer_toggle_update (GtkWidget *widget,
236                         gpointer   data)
237 {
238   gimp_toggle_button_update (widget, data);
239 
240   set_cmap_preview ();
241   dialog_update_preview ();
242 }
243 
244 static void
explorer_radio_update(GtkWidget * widget,gpointer data)245 explorer_radio_update  (GtkWidget *widget,
246                         gpointer   data)
247 {
248   gboolean c_sensitive;
249 
250   gimp_radio_button_update (widget, data);
251 
252   switch (wvals.fractaltype)
253     {
254     case TYPE_MANDELBROT:
255     case TYPE_SIERPINSKI:
256       c_sensitive = FALSE;
257       break;
258 
259     default:
260       c_sensitive = TRUE;
261       break;
262     }
263 
264   gimp_scale_entry_set_sensitive (elements->cx, c_sensitive);
265   gimp_scale_entry_set_sensitive (elements->cy, c_sensitive);
266 
267   set_cmap_preview ();
268   dialog_update_preview ();
269 }
270 
271 static void
explorer_double_adjustment_update(GtkAdjustment * adjustment,gpointer data)272 explorer_double_adjustment_update (GtkAdjustment *adjustment,
273                                    gpointer       data)
274 {
275   gimp_double_adjustment_update (adjustment, data);
276 
277   set_cmap_preview ();
278   dialog_update_preview ();
279 }
280 
281 static void
explorer_number_of_colors_callback(GtkAdjustment * adjustment,gpointer data)282 explorer_number_of_colors_callback (GtkAdjustment *adjustment,
283                                     gpointer       data)
284 {
285   gimp_int_adjustment_update (adjustment, data);
286 
287   g_free (gradient_samples);
288 
289   if (! gradient_name)
290     gradient_name = gimp_context_get_gradient ();
291 
292   gimp_gradient_get_uniform_samples (gradient_name,
293                                      wvals.ncolors,
294                                      wvals.gradinvert,
295                                      &n_gradient_samples,
296                                      &gradient_samples);
297 
298   set_cmap_preview ();
299   dialog_update_preview ();
300 }
301 
302 static void
explorer_gradient_select_callback(GimpGradientSelectButton * gradient_button,const gchar * name,gint width,const gdouble * gradient_data,gboolean dialog_closing,gpointer data)303 explorer_gradient_select_callback (GimpGradientSelectButton *gradient_button,
304                                    const gchar              *name,
305                                    gint                      width,
306                                    const gdouble            *gradient_data,
307                                    gboolean                  dialog_closing,
308                                    gpointer                  data)
309 {
310   g_free (gradient_name);
311   g_free (gradient_samples);
312 
313   gradient_name = g_strdup (name);
314 
315   gimp_gradient_get_uniform_samples (gradient_name,
316                                      wvals.ncolors,
317                                      wvals.gradinvert,
318                                      &n_gradient_samples,
319                                      &gradient_samples);
320 
321   if (wvals.colormode == 1)
322     {
323       set_cmap_preview ();
324       dialog_update_preview ();
325     }
326 }
327 
328 static void
preview_draw_crosshair(gint px,gint py)329 preview_draw_crosshair (gint px,
330                         gint py)
331 {
332   gint     x, y;
333   guchar  *p_ul;
334 
335   p_ul = wint.wimage + 3 * (preview_width * py + 0);
336 
337   for (x = 0; x < preview_width; x++)
338     {
339       p_ul[0] ^= 254;
340       p_ul[1] ^= 254;
341       p_ul[2] ^= 254;
342       p_ul += 3;
343     }
344 
345   p_ul = wint.wimage + 3 * (preview_width * 0 + px);
346 
347   for (y = 0; y < preview_height; y++)
348     {
349       p_ul[0] ^= 254;
350       p_ul[1] ^= 254;
351       p_ul[2] ^= 254;
352       p_ul += 3 * preview_width;
353     }
354 }
355 
356 static void
preview_redraw(void)357 preview_redraw (void)
358 {
359   gimp_preview_area_draw (GIMP_PREVIEW_AREA (wint.preview),
360                           0, 0, preview_width, preview_height,
361                           GIMP_RGB_IMAGE,
362                           wint.wimage, preview_width * 3);
363 
364   gtk_widget_queue_draw (wint.preview);
365 }
366 
367 static gboolean
preview_button_press_event(GtkWidget * widget,GdkEventButton * event)368 preview_button_press_event (GtkWidget      *widget,
369                             GdkEventButton *event)
370 {
371   if (event->button == 1)
372     {
373       x_press = event->x;
374       y_press = event->y;
375       xbild = preview_width;
376       ybild = preview_height;
377       xdiff = (xmax - xmin) / xbild;
378       ydiff = (ymax - ymin) / ybild;
379 
380       preview_draw_crosshair (x_press, y_press);
381       preview_redraw ();
382     }
383   return TRUE;
384 }
385 
386 static gboolean
preview_motion_notify_event(GtkWidget * widget,GdkEventButton * event)387 preview_motion_notify_event (GtkWidget      *widget,
388                              GdkEventButton *event)
389 {
390   if (oldypos != -1)
391     {
392       preview_draw_crosshair (oldxpos, oldypos);
393     }
394 
395   oldxpos = event->x;
396   oldypos = event->y;
397 
398   if ((oldxpos >= 0.0) &&
399       (oldypos >= 0.0) &&
400       (oldxpos < preview_width) &&
401       (oldypos < preview_height))
402     {
403       preview_draw_crosshair (oldxpos, oldypos);
404     }
405   else
406     {
407       oldypos = -1;
408       oldxpos = -1;
409     }
410 
411   preview_redraw ();
412 
413   return TRUE;
414 }
415 
416 static gboolean
preview_leave_notify_event(GtkWidget * widget,GdkEventButton * event)417 preview_leave_notify_event (GtkWidget      *widget,
418                             GdkEventButton *event)
419 {
420   if (oldypos != -1)
421     {
422       preview_draw_crosshair (oldxpos, oldypos);
423     }
424   oldxpos = -1;
425   oldypos = -1;
426 
427   preview_redraw ();
428 
429   gdk_window_set_cursor (gtk_widget_get_window (maindlg), NULL);
430 
431   return TRUE;
432 }
433 
434 static gboolean
preview_enter_notify_event(GtkWidget * widget,GdkEventButton * event)435 preview_enter_notify_event (GtkWidget      *widget,
436                             GdkEventButton *event)
437 {
438   static GdkCursor *cursor = NULL;
439 
440   if (! cursor)
441     {
442       GdkDisplay *display = gtk_widget_get_display (maindlg);
443 
444       cursor = gdk_cursor_new_for_display (display, GDK_TCROSS);
445 
446     }
447 
448   gdk_window_set_cursor (gtk_widget_get_window (maindlg), cursor);
449 
450   return TRUE;
451 }
452 
453 static gboolean
preview_button_release_event(GtkWidget * widget,GdkEventButton * event)454 preview_button_release_event (GtkWidget      *widget,
455                               GdkEventButton *event)
456 {
457   gdouble l_xmin;
458   gdouble l_xmax;
459   gdouble l_ymin;
460   gdouble l_ymax;
461 
462   if (event->button == 1)
463     {
464       gdouble x_release, y_release;
465 
466       x_release = event->x;
467       y_release = event->y;
468 
469       if ((x_press >= 0.0) && (y_press >= 0.0) &&
470           (x_release >= 0.0) && (y_release >= 0.0) &&
471           (x_press < preview_width) && (y_press < preview_height) &&
472           (x_release < preview_width) && (y_release < preview_height))
473         {
474           l_xmin = (wvals.xmin +
475                     (wvals.xmax - wvals.xmin) * (x_press / preview_width));
476           l_xmax = (wvals.xmin +
477                     (wvals.xmax - wvals.xmin) * (x_release / preview_width));
478           l_ymin = (wvals.ymin +
479                     (wvals.ymax - wvals.ymin) * (y_press / preview_height));
480           l_ymax = (wvals.ymin +
481                     (wvals.ymax - wvals.ymin) * (y_release / preview_height));
482 
483           if (zoomindex < ZOOM_UNDO_SIZE - 1)
484             {
485               zooms[zoomindex] = wvals;
486               zoomindex++;
487             }
488           zoommax = zoomindex;
489           wvals.xmin = l_xmin;
490           wvals.xmax = l_xmax;
491           wvals.ymin = l_ymin;
492           wvals.ymax = l_ymax;
493           dialog_change_scale ();
494           dialog_update_preview ();
495           oldypos = oldxpos = -1;
496         }
497     }
498 
499   return TRUE;
500 }
501 
502 /**********************************************************************
503  FUNCTION: explorer_dialog
504  *********************************************************************/
505 
506 gint
explorer_dialog(void)507 explorer_dialog (void)
508 {
509   GtkWidget *dialog;
510   GtkWidget *top_hbox;
511   GtkWidget *left_vbox;
512   GtkWidget *abox;
513   GtkWidget *vbox;
514   GtkWidget *bbox;
515   GtkWidget *frame;
516   GtkWidget *toggle;
517   GtkWidget *toggle_vbox;
518   GtkWidget *toggle_vbox2;
519   GtkWidget *toggle_vbox3;
520   GtkWidget *notebook;
521   GtkWidget *hbox;
522   GtkWidget *table;
523   GtkWidget *button;
524   GtkWidget *gradient;
525   gchar     *path;
526   gchar     *gradient_name;
527   GSList    *group = NULL;
528   gint       i;
529 
530   gimp_ui_init (PLUG_IN_BINARY, TRUE);
531 
532   path = gimp_gimprc_query ("fractalexplorer-path");
533 
534   if (path)
535     {
536       fractalexplorer_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
537       g_free (path);
538     }
539   else
540     {
541       gchar *gimprc    = gimp_personal_rc_file ("gimprc");
542       gchar *full_path = gimp_config_build_data_path ("fractalexplorer");
543       gchar *esc_path  = g_strescape (full_path, NULL);
544       g_free (full_path);
545 
546       g_message (_("No %s in gimprc:\n"
547                    "You need to add an entry like\n"
548                    "(%s \"%s\")\n"
549                    "to your %s file."),
550                  "fractalexplorer-path",
551                  "fractalexplorer-path",
552                  esc_path, gimp_filename_to_utf8 (gimprc));
553 
554       g_free (gimprc);
555       g_free (esc_path);
556     }
557 
558   wint.wimage = g_new (guchar, preview_width * preview_height * 3);
559   elements    = g_new (DialogElements, 1);
560 
561   dialog = maindlg =
562     gimp_dialog_new (_("Fractal Explorer"), PLUG_IN_ROLE,
563                      NULL, 0,
564                      gimp_standard_help_func, PLUG_IN_PROC,
565 
566                      _("_Cancel"), GTK_RESPONSE_CANCEL,
567                      _("_OK"),     GTK_RESPONSE_OK,
568 
569                      NULL);
570 
571   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
572                                            GTK_RESPONSE_OK,
573                                            GTK_RESPONSE_CANCEL,
574                                            -1);
575 
576   gimp_window_set_transient (GTK_WINDOW (dialog));
577 
578   g_signal_connect (dialog, "response",
579                     G_CALLBACK (dialog_response),
580                     NULL);
581 
582   g_signal_connect (dialog, "destroy",
583                     G_CALLBACK (gtk_main_quit),
584                     NULL);
585 
586   top_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
587   gtk_container_set_border_width (GTK_CONTAINER (top_hbox), 12);
588   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
589                       top_hbox, FALSE, FALSE, 0);
590   gtk_widget_show (top_hbox);
591 
592   left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
593   gtk_box_pack_start (GTK_BOX (top_hbox), left_vbox, FALSE, FALSE, 0);
594   gtk_widget_show (left_vbox);
595 
596   /*  Preview  */
597   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
598   gtk_box_pack_start (GTK_BOX (left_vbox), vbox, FALSE, FALSE, 0);
599   gtk_widget_show (vbox);
600 
601   abox = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
602   gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
603   gtk_widget_show (abox);
604 
605   frame = gtk_frame_new (NULL);
606   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
607   gtk_container_add (GTK_CONTAINER (abox), frame);
608   gtk_widget_show (frame);
609 
610   wint.preview = gimp_preview_area_new ();
611   gtk_widget_set_size_request (wint.preview, preview_width, preview_height);
612   gtk_container_add (GTK_CONTAINER (frame), wint.preview);
613 
614   g_signal_connect (wint.preview, "button-press-event",
615                     G_CALLBACK (preview_button_press_event),
616                     NULL);
617   g_signal_connect (wint.preview, "button-release-event",
618                     G_CALLBACK (preview_button_release_event),
619                     NULL);
620   g_signal_connect (wint.preview, "motion-notify-event",
621                     G_CALLBACK (preview_motion_notify_event),
622                     NULL);
623   g_signal_connect (wint.preview, "leave-notify-event",
624                     G_CALLBACK (preview_leave_notify_event),
625                     NULL);
626   g_signal_connect (wint.preview, "enter-notify-event",
627                     G_CALLBACK (preview_enter_notify_event),
628                     NULL);
629 
630   gtk_widget_set_events (wint.preview, (GDK_BUTTON_PRESS_MASK |
631                                         GDK_BUTTON_RELEASE_MASK |
632                                         GDK_POINTER_MOTION_MASK |
633                                         GDK_LEAVE_NOTIFY_MASK |
634                                         GDK_ENTER_NOTIFY_MASK));
635   gtk_widget_show (wint.preview);
636 
637   toggle = gtk_check_button_new_with_mnemonic (_("Re_altime preview"));
638   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
639   g_signal_connect (toggle, "toggled",
640                     G_CALLBACK (explorer_toggle_update),
641                     &wvals.alwayspreview);
642   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
643                                 wvals.alwayspreview);
644   gtk_widget_show (toggle);
645   gimp_help_set_help_data (toggle, _("If enabled the preview will "
646                                      "be redrawn automatically"), NULL);
647 
648   button = gtk_button_new_with_mnemonic (_("R_edraw preview"));
649   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
650   g_signal_connect (button, "clicked",
651                     G_CALLBACK (dialog_redraw_callback),
652                     dialog);
653   gtk_widget_show (button);
654 
655   /*  Zoom Options  */
656   frame = gimp_frame_new (_("Zoom"));
657   gtk_box_pack_start (GTK_BOX (left_vbox), frame, FALSE, FALSE, 0);
658   gtk_widget_show (frame);
659 
660   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
661   gtk_container_add (GTK_CONTAINER (frame), vbox);
662   gtk_widget_show (vbox);
663 
664   bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
665   gtk_box_set_homogeneous (GTK_BOX (bbox), TRUE);
666   gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
667   gtk_widget_show (bbox);
668 
669   button = gtk_button_new_with_mnemonic (_("Zoom _In"));
670   gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
671   gtk_widget_show (button);
672 
673   g_signal_connect (button, "clicked",
674                     G_CALLBACK (dialog_step_in_callback),
675                     dialog);
676 
677   button = gtk_button_new_with_mnemonic (_("Zoom _Out"));
678   gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
679   gtk_widget_show (button);
680 
681   g_signal_connect (button, "clicked",
682                     G_CALLBACK (dialog_step_out_callback),
683                     dialog);
684 
685   bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
686   gtk_box_set_homogeneous (GTK_BOX (bbox), TRUE);
687   gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
688   gtk_widget_show (bbox);
689 
690   button = gtk_button_new_with_mnemonic (_("_Undo"));
691   gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
692   gtk_widget_show (button);
693 
694   gimp_help_set_help_data (button, _("Undo last zoom change"), NULL);
695 
696   g_signal_connect (button, "clicked",
697                     G_CALLBACK (dialog_undo_zoom_callback),
698                     dialog);
699 
700   button = gtk_button_new_with_mnemonic (_("_Redo"));
701   gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
702   gtk_widget_show (button);
703 
704   gimp_help_set_help_data (button, _("Redo last zoom change"), NULL);
705 
706   g_signal_connect (button, "clicked",
707                     G_CALLBACK (dialog_redo_zoom_callback),
708                     dialog);
709 
710   /*  Create notebook  */
711   notebook = gtk_notebook_new ();
712   gtk_box_pack_start (GTK_BOX (top_hbox), notebook, FALSE, FALSE, 0);
713   gtk_widget_show (notebook);
714 
715   /*  "Parameters" page  */
716   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
717   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
718   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
719                             gtk_label_new_with_mnemonic (_("_Parameters")));
720   gtk_widget_show (vbox);
721 
722   frame = gimp_frame_new (_("Fractal Parameters"));
723   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
724   gtk_widget_show (frame);
725 
726   table = gtk_table_new (8, 3, FALSE);
727   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
728   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
729   gtk_table_set_row_spacing (GTK_TABLE (table), 6, 12);
730   gtk_container_add (GTK_CONTAINER (frame), table);
731   gtk_widget_show (table);
732 
733   elements->xmin =
734     gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
735                           _("Left:"), SCALE_WIDTH, 10,
736                           wvals.xmin, -3, 3, 0.001, 0.01, 5,
737                           TRUE, 0, 0, NULL, NULL);
738   g_signal_connect (elements->xmin, "value-changed",
739                     G_CALLBACK (explorer_double_adjustment_update),
740                     &wvals.xmin);
741 
742   elements->xmax =
743     gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
744                           _("Right:"), SCALE_WIDTH, 10,
745                           wvals.xmax, -3, 3, 0.001, 0.01, 5,
746                           TRUE, 0, 0, NULL, NULL);
747   g_signal_connect (elements->xmax, "value-changed",
748                     G_CALLBACK (explorer_double_adjustment_update),
749                     &wvals.xmax);
750 
751   elements->ymin =
752     gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
753                           _("Top:"), SCALE_WIDTH, 10,
754                           wvals.ymin, -3, 3, 0.001, 0.01, 5,
755                           TRUE, 0, 0, NULL, NULL);
756   g_signal_connect (elements->ymin, "value-changed",
757                     G_CALLBACK (explorer_double_adjustment_update),
758                     &wvals.ymin);
759 
760   elements->ymax =
761     gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
762                           _("Bottom:"), SCALE_WIDTH, 10,
763                           wvals.ymax, -3, 3, 0.001, 0.01, 5,
764                           TRUE, 0, 0, NULL, NULL);
765   g_signal_connect (elements->ymax, "value-changed",
766                     G_CALLBACK (explorer_double_adjustment_update),
767                     &wvals.ymax);
768 
769   elements->iter =
770     gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
771                           _("Iterations:"), SCALE_WIDTH, 10,
772                           wvals.iter, 1, 1000, 1, 10, 0,
773                           TRUE, 0, 0,
774                           _("The higher the number of iterations, "
775                             "the more details will be calculated"), NULL);
776   g_signal_connect (elements->iter, "value-changed",
777                     G_CALLBACK (explorer_double_adjustment_update),
778                     &wvals.iter);
779 
780   elements->cx =
781     gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
782                           _("CX:"), SCALE_WIDTH, 10,
783                           wvals.cx, -2.5, 2.5, 0.001, 0.01, 5,
784                           TRUE, 0, 0,
785                           _("Changes aspect of fractal"), NULL);
786   g_signal_connect (elements->cx, "value-changed",
787                     G_CALLBACK (explorer_double_adjustment_update),
788                     &wvals.cx);
789 
790   elements->cy =
791     gimp_scale_entry_new (GTK_TABLE (table), 0, 6,
792                           _("CY:"), SCALE_WIDTH, 10,
793                           wvals.cy, -2.5, 2.5, 0.001, 0.01, 5,
794                           TRUE, 0, 0,
795                           _("Changes aspect of fractal"), NULL);
796   g_signal_connect (elements->cy, "value-changed",
797                     G_CALLBACK (explorer_double_adjustment_update),
798                     &wvals.cy);
799 
800   bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
801   gtk_box_set_homogeneous (GTK_BOX (bbox), TRUE);
802   gtk_table_attach_defaults (GTK_TABLE (table), bbox, 0, 3, 7, 8);
803   gtk_widget_show (bbox);
804 
805   button = gtk_button_new_with_mnemonic (_("_Open"));
806   gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
807   g_signal_connect (button, "clicked",
808                     G_CALLBACK (create_load_file_chooser),
809                     dialog);
810   gtk_widget_show (button);
811   gimp_help_set_help_data (button, _("Load a fractal from file"), NULL);
812 
813   button = gtk_button_new_with_mnemonic (_("_Reset"));
814   gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
815   g_signal_connect (button, "clicked",
816                     G_CALLBACK (dialog_reset_callback),
817                     dialog);
818   gtk_widget_show (button);
819   gimp_help_set_help_data (button, _("Reset parameters to default values"),
820                            NULL);
821 
822   button = gtk_button_new_with_mnemonic (_("_Save"));
823   gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
824   g_signal_connect (button, "clicked",
825                     G_CALLBACK (create_save_file_chooser),
826                     dialog);
827   gtk_widget_show (button);
828   gimp_help_set_help_data (button, _("Save active fractal to file"), NULL);
829 
830   /*  Fractal type toggle box  */
831   frame = gimp_frame_new (_("Fractal Type"));
832   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
833   gtk_widget_show (frame);
834 
835   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
836   gtk_container_add (GTK_CONTAINER (frame), hbox);
837   gtk_widget_show (hbox);
838 
839   toggle_vbox =
840     gimp_int_radio_group_new (FALSE, NULL,
841                               G_CALLBACK (explorer_radio_update),
842                               &wvals.fractaltype, wvals.fractaltype,
843 
844                               _("Mandelbrot"), TYPE_MANDELBROT,
845                               &(elements->type[TYPE_MANDELBROT]),
846                               _("Julia"),      TYPE_JULIA,
847                               &(elements->type[TYPE_JULIA]),
848                               _("Barnsley 1"), TYPE_BARNSLEY_1,
849                               &(elements->type[TYPE_BARNSLEY_1]),
850                               _("Barnsley 2"), TYPE_BARNSLEY_2,
851                               &(elements->type[TYPE_BARNSLEY_2]),
852                               _("Barnsley 3"), TYPE_BARNSLEY_3,
853                               &(elements->type[TYPE_BARNSLEY_3]),
854                               _("Spider"),     TYPE_SPIDER,
855                               &(elements->type[TYPE_SPIDER]),
856                               _("Man'o'war"),  TYPE_MAN_O_WAR,
857                               &(elements->type[TYPE_MAN_O_WAR]),
858                               _("Lambda"),     TYPE_LAMBDA,
859                               &(elements->type[TYPE_LAMBDA]),
860                               _("Sierpinski"), TYPE_SIERPINSKI,
861                               &(elements->type[TYPE_SIERPINSKI]),
862 
863                               NULL);
864 
865   toggle_vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
866   for (i = TYPE_BARNSLEY_2; i <= TYPE_SPIDER; i++)
867     {
868       g_object_ref (elements->type[i]);
869 
870       gtk_widget_hide (elements->type[i]);
871       gtk_container_remove (GTK_CONTAINER (toggle_vbox), elements->type[i]);
872       gtk_box_pack_start (GTK_BOX (toggle_vbox2), elements->type[i],
873                           FALSE, FALSE, 0);
874       gtk_widget_show (elements->type[i]);
875 
876       g_object_unref (elements->type[i]);
877     }
878 
879   toggle_vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
880   for (i = TYPE_MAN_O_WAR; i <= TYPE_SIERPINSKI; i++)
881     {
882       g_object_ref (elements->type[i]);
883 
884       gtk_widget_hide (elements->type[i]);
885       gtk_container_remove (GTK_CONTAINER (toggle_vbox), elements->type[i]);
886       gtk_box_pack_start (GTK_BOX (toggle_vbox3), elements->type[i],
887                           FALSE, FALSE, 0);
888       gtk_widget_show (elements->type[i]);
889 
890       g_object_unref (elements->type[i]);
891     }
892 
893   gtk_box_pack_start (GTK_BOX (hbox), toggle_vbox, FALSE, FALSE, 0);
894   gtk_widget_show (toggle_vbox);
895 
896   gtk_box_pack_start (GTK_BOX (hbox), toggle_vbox2, FALSE, FALSE, 0);
897   gtk_widget_show (toggle_vbox2);
898 
899   gtk_box_pack_start (GTK_BOX (hbox), toggle_vbox3, FALSE, FALSE, 0);
900   gtk_widget_show (toggle_vbox3);
901 
902   /*  Color page  */
903   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
904   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
905   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
906                             gtk_label_new_with_mnemonic (_("Co_lors")));
907   gtk_widget_show (vbox);
908 
909   /*  Number of Colors frame  */
910   frame = gimp_frame_new (_("Number of Colors"));
911   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
912   gtk_widget_show (frame);
913 
914   table = gtk_table_new (2, 3, FALSE);
915   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
916   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
917   gtk_container_add (GTK_CONTAINER (frame), table);
918   gtk_widget_show (table);
919 
920   elements->ncol =
921     gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
922                           _("Number of colors:"), SCALE_WIDTH, 0,
923                           wvals.ncolors, 2, MAXNCOLORS, 1, 10, 0,
924                           TRUE, 0, 0,
925                           _("Change the number of colors in the mapping"),
926                           NULL);
927   g_signal_connect (elements->ncol, "value-changed",
928                     G_CALLBACK (explorer_number_of_colors_callback),
929                     &wvals.ncolors);
930 
931   elements->useloglog = toggle =
932     gtk_check_button_new_with_label (_("Use loglog smoothing"));
933   gtk_table_attach_defaults (GTK_TABLE (table), toggle, 0, 3, 1, 2);
934   g_signal_connect (toggle, "toggled",
935                     G_CALLBACK (explorer_toggle_update),
936                     &wvals.useloglog);
937   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), wvals.useloglog);
938   gtk_widget_show (toggle);
939   gimp_help_set_help_data (toggle, _("Use log log smoothing to eliminate "
940                                      "\"banding\" in the result"), NULL);
941 
942   /*  Color Density frame  */
943   frame = gimp_frame_new (_("Color Density"));
944   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
945   gtk_widget_show (frame);
946 
947   table = gtk_table_new (3, 3, FALSE);
948   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
949   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
950   gtk_container_add (GTK_CONTAINER (frame), table);
951   gtk_widget_show (table);
952 
953   elements->red =
954     gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
955                           _("Red:"), SCALE_WIDTH, 0,
956                           wvals.redstretch, 0, 1, 0.01, 0.1, 2,
957                           TRUE, 0, 0,
958                           _("Change the intensity of the red channel"), NULL);
959   g_signal_connect (elements->red, "value-changed",
960                     G_CALLBACK (explorer_double_adjustment_update),
961                     &wvals.redstretch);
962 
963   elements->green =
964     gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
965                           _("Green:"), SCALE_WIDTH, 0,
966                           wvals.greenstretch, 0, 1, 0.01, 0.1, 2,
967                           TRUE, 0, 0,
968                           _("Change the intensity of the green channel"), NULL);
969   g_signal_connect (elements->green, "value-changed",
970                     G_CALLBACK (explorer_double_adjustment_update),
971                     &wvals.greenstretch);
972 
973   elements->blue =
974     gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
975                           _("Blue:"), SCALE_WIDTH, 0,
976                           wvals.bluestretch, 0, 1, 0.01, 0.1, 2,
977                           TRUE, 0, 0,
978                           _("Change the intensity of the blue channel"), NULL);
979   g_signal_connect (elements->blue, "value-changed",
980                     G_CALLBACK (explorer_double_adjustment_update),
981                     &wvals.bluestretch);
982 
983   /*  Color Function frame  */
984   frame = gimp_frame_new (_("Color Function"));
985   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
986   gtk_widget_show (frame);
987 
988   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
989   gtk_container_add (GTK_CONTAINER (frame), hbox);
990   gtk_widget_show (hbox);
991 
992   /*  Redmode radio frame  */
993   frame = gimp_int_radio_group_new (TRUE, _("Red"),
994                                     G_CALLBACK (explorer_radio_update),
995                                     &wvals.redmode, wvals.redmode,
996 
997                                     _("Sine"),                    SINUS,
998                                     &elements->redmode[SINUS],
999                                     _("Cosine"),                  COSINUS,
1000                                     &elements->redmode[COSINUS],
1001                                     C_("color-function", "None"), NONE,
1002                                     &elements->redmode[NONE],
1003 
1004                                     NULL);
1005   gimp_help_set_help_data (elements->redmode[SINUS],
1006                            _("Use sine-function for this color component"),
1007                            NULL);
1008   gimp_help_set_help_data (elements->redmode[COSINUS],
1009                            _("Use cosine-function for this color "
1010                              "component"), NULL);
1011   gimp_help_set_help_data (elements->redmode[NONE],
1012                            _("Use linear mapping instead of any "
1013                              "trigonometrical function for this color "
1014                              "channel"), NULL);
1015   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
1016   gtk_widget_show (frame);
1017 
1018   toggle_vbox = gtk_bin_get_child (GTK_BIN (frame));
1019 
1020   elements->redinvert = toggle =
1021     gtk_check_button_new_with_label (_("Inversion"));
1022   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), wvals.redinvert);
1023   gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
1024   g_signal_connect (toggle, "toggled",
1025                     G_CALLBACK (explorer_toggle_update),
1026                     &wvals.redinvert);
1027   gtk_widget_show (toggle);
1028   gimp_help_set_help_data (toggle,
1029                            _("If you enable this option higher color values "
1030                              "will be swapped with lower ones and vice "
1031                              "versa"), NULL);
1032 
1033   /*  Greenmode radio frame  */
1034   frame = gimp_int_radio_group_new (TRUE, _("Green"),
1035                                     G_CALLBACK (explorer_radio_update),
1036                                     &wvals.greenmode, wvals.greenmode,
1037 
1038                                     _("Sine"),                    SINUS,
1039                                     &elements->greenmode[SINUS],
1040                                     _("Cosine"),                  COSINUS,
1041                                     &elements->greenmode[COSINUS],
1042                                     C_("color-function", "None"), NONE,
1043                                     &elements->greenmode[NONE],
1044 
1045                                     NULL);
1046   gimp_help_set_help_data (elements->greenmode[SINUS],
1047                            _("Use sine-function for this color component"),
1048                            NULL);
1049   gimp_help_set_help_data (elements->greenmode[COSINUS],
1050                            _("Use cosine-function for this color "
1051                              "component"), NULL);
1052   gimp_help_set_help_data (elements->greenmode[NONE],
1053                            _("Use linear mapping instead of any "
1054                              "trigonometrical function for this color "
1055                              "channel"), NULL);
1056   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
1057   gtk_widget_show (frame);
1058 
1059   toggle_vbox = gtk_bin_get_child (GTK_BIN (frame));
1060 
1061   elements->greeninvert = toggle =
1062     gtk_check_button_new_with_label (_("Inversion"));
1063   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), wvals.greeninvert);
1064   gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
1065   g_signal_connect (toggle, "toggled",
1066                     G_CALLBACK (explorer_toggle_update),
1067                     &wvals.greeninvert);
1068   gtk_widget_show (toggle);
1069   gimp_help_set_help_data (toggle,
1070                            _("If you enable this option higher color values "
1071                              "will be swapped with lower ones and vice "
1072                              "versa"), NULL);
1073 
1074   /*  Bluemode radio frame  */
1075   frame = gimp_int_radio_group_new (TRUE, _("Blue"),
1076                                     G_CALLBACK (explorer_radio_update),
1077                                     &wvals.bluemode, wvals.bluemode,
1078 
1079                                     _("Sine"),                    SINUS,
1080                                     &elements->bluemode[SINUS],
1081                                     _("Cosine"),                  COSINUS,
1082                                     &elements->bluemode[COSINUS],
1083                                     C_("color-function", "None"), NONE,
1084                                     &elements->bluemode[NONE],
1085 
1086                                     NULL);
1087   gimp_help_set_help_data (elements->bluemode[SINUS],
1088                            _("Use sine-function for this color component"),
1089                            NULL);
1090   gimp_help_set_help_data (elements->bluemode[COSINUS],
1091                            _("Use cosine-function for this color "
1092                              "component"), NULL);
1093   gimp_help_set_help_data (elements->bluemode[NONE],
1094                            _("Use linear mapping instead of any "
1095                              "trigonometrical function for this color "
1096                              "channel"), NULL);
1097   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
1098   gtk_widget_show (frame);
1099 
1100   toggle_vbox = gtk_bin_get_child (GTK_BIN (frame));
1101 
1102   elements->blueinvert = toggle =
1103     gtk_check_button_new_with_label (_("Inversion"));
1104   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON( toggle), wvals.blueinvert);
1105   gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
1106   g_signal_connect (toggle, "toggled",
1107                     G_CALLBACK (explorer_toggle_update),
1108                     &wvals.blueinvert);
1109   gtk_widget_show (toggle);
1110   gimp_help_set_help_data (toggle,
1111                            _("If you enable this option higher color values "
1112                              "will be swapped with lower ones and vice "
1113                              "versa"), NULL);
1114 
1115   /*  Colormode toggle box  */
1116   frame = gimp_frame_new (_("Color Mode"));
1117   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
1118   gtk_widget_show (frame);
1119 
1120   toggle_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
1121   gtk_container_add (GTK_CONTAINER (frame), toggle_vbox);
1122   gtk_widget_show (toggle_vbox);
1123 
1124   toggle = elements->colormode[0] =
1125     gtk_radio_button_new_with_label (group, _("As specified above"));
1126   group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
1127   gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
1128   g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
1129                      GINT_TO_POINTER (0));
1130   g_signal_connect (toggle, "toggled",
1131                     G_CALLBACK (explorer_radio_update),
1132                     &wvals.colormode);
1133   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
1134                                 wvals.colormode == 0);
1135   gtk_widget_show (toggle);
1136   gimp_help_set_help_data (toggle,
1137                            _("Create a color-map with the options you "
1138                              "specified above (color density/function). The "
1139                              "result is visible in the preview image"), NULL);
1140 
1141   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1142   gtk_box_pack_start (GTK_BOX (toggle_vbox), hbox, FALSE, FALSE, 0);
1143   gtk_widget_show (hbox);
1144 
1145   toggle = elements->colormode[1] =
1146     gtk_radio_button_new_with_label (group,
1147                                      _("Apply active gradient to final image"));
1148   group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
1149   gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
1150   g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
1151                      GINT_TO_POINTER (1));
1152   g_signal_connect (toggle, "toggled",
1153                     G_CALLBACK (explorer_radio_update),
1154                     &wvals.colormode);
1155   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
1156                                 wvals.colormode == 1);
1157   gtk_widget_show (toggle);
1158   gimp_help_set_help_data (toggle,
1159                            _("Create a color-map using a gradient from "
1160                              "the gradient editor"), NULL);
1161 
1162   gradient_name = gimp_context_get_gradient ();
1163 
1164   gimp_gradient_get_uniform_samples (gradient_name,
1165                                      wvals.ncolors,
1166                                      wvals.gradinvert,
1167                                      &n_gradient_samples,
1168                                      &gradient_samples);
1169 
1170   gradient = gimp_gradient_select_button_new (_("FractalExplorer Gradient"),
1171                                               gradient_name);
1172   g_signal_connect (gradient, "gradient-set",
1173                     G_CALLBACK (explorer_gradient_select_callback), NULL);
1174   g_free (gradient_name);
1175   gtk_box_pack_start (GTK_BOX (hbox), gradient, FALSE, FALSE, 0);
1176   gtk_widget_show (gradient);
1177 
1178   abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
1179   {
1180     gint xsize, ysize;
1181 
1182     for (ysize = 1; ysize * ysize * ysize < 8192; ysize++) /**/;
1183     xsize = wvals.ncolors / ysize;
1184     while (xsize * ysize < 8192) xsize++;
1185 
1186     gtk_widget_set_size_request (abox, xsize, ysize * 4);
1187   }
1188   gtk_box_pack_start (GTK_BOX (toggle_vbox), abox, FALSE, FALSE, 0);
1189   gtk_widget_show (abox);
1190 
1191   cmap_preview = gimp_preview_area_new ();
1192   gtk_widget_set_size_request (cmap_preview, 32, 32);
1193   gtk_container_add (GTK_CONTAINER (abox), cmap_preview);
1194   g_signal_connect (cmap_preview, "size-allocate",
1195                     G_CALLBACK (cmap_preview_size_allocate), NULL);
1196   gtk_widget_show (cmap_preview);
1197 
1198   frame = add_objects_list ();
1199   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame,
1200                             gtk_label_new_with_mnemonic (_("_Fractals")));
1201   gtk_widget_show (frame);
1202 
1203   gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 0);
1204 
1205   gtk_widget_show (dialog);
1206   ready_now = TRUE;
1207 
1208   set_cmap_preview ();
1209   dialog_update_preview ();
1210 
1211   gtk_main ();
1212 
1213   g_free (wint.wimage);
1214 
1215   return wint.run;
1216 }
1217 
1218 
1219 /**********************************************************************
1220  FUNCTION: dialog_update_preview
1221  *********************************************************************/
1222 
1223 void
dialog_update_preview(void)1224 dialog_update_preview (void)
1225 {
1226   gint     ycoord;
1227   guchar  *p_ul;
1228 
1229   if (NULL == wint.preview)
1230     return;
1231 
1232   if (ready_now && wvals.alwayspreview)
1233     {
1234       xmin = wvals.xmin;
1235       xmax = wvals.xmax;
1236       ymin = wvals.ymin;
1237       ymax = wvals.ymax;
1238       xbild = preview_width;
1239       ybild = preview_height;
1240       xdiff = (xmax - xmin) / xbild;
1241       ydiff = (ymax - ymin) / ybild;
1242 
1243       p_ul = wint.wimage;
1244 
1245       for (ycoord = 0; ycoord < preview_height; ycoord++)
1246         {
1247           explorer_render_row (NULL,
1248                                p_ul,
1249                                ycoord,
1250                                preview_width,
1251                                3);
1252           p_ul += preview_width * 3;
1253         }
1254 
1255       preview_redraw ();
1256     }
1257 }
1258 
1259 /**********************************************************************
1260  FUNCTION: cmap_preview_size_allocate()
1261  *********************************************************************/
1262 
1263 static void
cmap_preview_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1264 cmap_preview_size_allocate (GtkWidget     *widget,
1265                             GtkAllocation *allocation)
1266 {
1267   gint             i;
1268   gint             x;
1269   gint             y;
1270   gint             j;
1271   guchar          *b;
1272   GimpPreviewArea *preview = GIMP_PREVIEW_AREA (widget);
1273 
1274   b = g_new (guchar, allocation->width * allocation->height * 3);
1275 
1276   for (y = 0; y < allocation->height; y++)
1277     {
1278       for (x = 0; x < allocation->width; x++)
1279         {
1280           i = x + (y / 4) * allocation->width;
1281           if (i > wvals.ncolors)
1282             {
1283               for (j = 0; j < 3; j++)
1284                 b[(y*allocation->width + x) * 3 + j] = 0;
1285             }
1286           else
1287             {
1288               b[(y*allocation->width + x) * 3]     = colormap[i].r;
1289               b[(y*allocation->width + x) * 3 + 1] = colormap[i].g;
1290               b[(y*allocation->width + x) * 3 + 2] = colormap[i].b;
1291             }
1292         }
1293     }
1294   gimp_preview_area_draw (preview,
1295                           0, 0, allocation->width, allocation->height,
1296                           GIMP_RGB_IMAGE, b, allocation->width*3);
1297   gtk_widget_queue_draw (cmap_preview);
1298 
1299   g_free (b);
1300 
1301 }
1302 
1303 /**********************************************************************
1304  FUNCTION: set_cmap_preview()
1305  *********************************************************************/
1306 
1307 void
set_cmap_preview(void)1308 set_cmap_preview (void)
1309 {
1310   gint    xsize, ysize;
1311 
1312   if (NULL == cmap_preview)
1313     return;
1314 
1315   make_color_map ();
1316 
1317   for (ysize = 1; ysize * ysize * ysize < wvals.ncolors; ysize++)
1318     /**/;
1319   xsize = wvals.ncolors / ysize;
1320   while (xsize * ysize < wvals.ncolors)
1321     xsize++;
1322 
1323   gtk_widget_set_size_request (cmap_preview, xsize, ysize * 4);
1324 }
1325 
1326 /**********************************************************************
1327  FUNCTION: make_color_map()
1328  *********************************************************************/
1329 
1330 void
make_color_map(void)1331 make_color_map (void)
1332 {
1333   gint     i;
1334   gint     r;
1335   gint     gr;
1336   gint     bl;
1337   gdouble  redstretch;
1338   gdouble  greenstretch;
1339   gdouble  bluestretch;
1340   gdouble  pi = atan (1) * 4;
1341 
1342   /*  get gradient samples if they don't exist -- fixes gradient color
1343    *  mode for noninteractive use (bug #103470).
1344    */
1345   if (gradient_samples == NULL)
1346     {
1347       gchar *gradient_name = gimp_context_get_gradient ();
1348 
1349       gimp_gradient_get_uniform_samples (gradient_name,
1350                                          wvals.ncolors,
1351                                          wvals.gradinvert,
1352                                          &n_gradient_samples,
1353                                          &gradient_samples);
1354 
1355       g_free (gradient_name);
1356     }
1357 
1358   redstretch   = wvals.redstretch * 127.5;
1359   greenstretch = wvals.greenstretch * 127.5;
1360   bluestretch  = wvals.bluestretch * 127.5;
1361 
1362   for (i = 0; i < wvals.ncolors; i++)
1363     if (wvals.colormode == 1)
1364       {
1365         colormap[i].r = (guchar)(gradient_samples[i * 4] * 255.9);
1366         colormap[i].g = (guchar)(gradient_samples[i * 4 + 1] * 255.9);
1367         colormap[i].b = (guchar)(gradient_samples[i * 4 + 2] * 255.9);
1368       }
1369     else
1370       {
1371         double x = (i*2.0) / wvals.ncolors;
1372         r = gr = bl = 0;
1373 
1374         switch (wvals.redmode)
1375           {
1376           case SINUS:
1377             r = (int) redstretch *(1.0 + sin((x - 1) * pi));
1378             break;
1379           case COSINUS:
1380             r = (int) redstretch *(1.0 + cos((x - 1) * pi));
1381             break;
1382           case NONE:
1383             r = (int)(redstretch *(x));
1384             break;
1385           default:
1386             break;
1387           }
1388 
1389         switch (wvals.greenmode)
1390           {
1391           case SINUS:
1392             gr = (int) greenstretch *(1.0 + sin((x - 1) * pi));
1393             break;
1394           case COSINUS:
1395             gr = (int) greenstretch *(1.0 + cos((x - 1) * pi));
1396             break;
1397           case NONE:
1398             gr = (int)(greenstretch *(x));
1399             break;
1400           default:
1401             break;
1402           }
1403 
1404         switch (wvals.bluemode)
1405           {
1406           case SINUS:
1407             bl = (int) bluestretch * (1.0 + sin ((x - 1) * pi));
1408             break;
1409           case COSINUS:
1410             bl = (int) bluestretch * (1.0 + cos ((x - 1) * pi));
1411             break;
1412           case NONE:
1413             bl = (int) (bluestretch * x);
1414             break;
1415           default:
1416             break;
1417           }
1418 
1419         r  = MIN (r,  255);
1420         gr = MIN (gr, 255);
1421         bl = MIN (bl, 255);
1422 
1423         if (wvals.redinvert)
1424           r = 255 - r;
1425 
1426         if (wvals.greeninvert)
1427           gr = 255 - gr;
1428 
1429         if (wvals.blueinvert)
1430           bl = 255 - bl;
1431 
1432         colormap[i].r = r;
1433         colormap[i].g = gr;
1434         colormap[i].b = bl;
1435       }
1436 }
1437 
1438 /**********************************************************************
1439  FUNCTION: dialog_change_scale
1440  *********************************************************************/
1441 
1442 void
dialog_change_scale(void)1443 dialog_change_scale (void)
1444 {
1445   ready_now = FALSE;
1446 
1447   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->xmin), wvals.xmin);
1448   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->xmax), wvals.xmax);
1449   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->ymin), wvals.ymin);
1450   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->ymax), wvals.ymax);
1451   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->iter), wvals.iter);
1452   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->cx),   wvals.cx);
1453   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->cy),   wvals.cy);
1454 
1455   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->red),  wvals.redstretch);
1456   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->green),wvals.greenstretch);
1457   gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->blue), wvals.bluestretch);
1458 
1459   gtk_toggle_button_set_active
1460     (GTK_TOGGLE_BUTTON (elements->type[wvals.fractaltype]), TRUE);
1461 
1462   gtk_toggle_button_set_active
1463     (GTK_TOGGLE_BUTTON (elements->redmode[wvals.redmode]), TRUE);
1464   gtk_toggle_button_set_active
1465     (GTK_TOGGLE_BUTTON (elements->greenmode[wvals.greenmode]), TRUE);
1466   gtk_toggle_button_set_active
1467     (GTK_TOGGLE_BUTTON (elements->bluemode[wvals.bluemode]), TRUE);
1468 
1469   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (elements->redinvert),
1470                                 wvals.redinvert);
1471   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (elements->greeninvert),
1472                                 wvals.greeninvert);
1473   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (elements->blueinvert),
1474                                 wvals.blueinvert);
1475 
1476   gtk_toggle_button_set_active
1477     (GTK_TOGGLE_BUTTON (elements->colormode[wvals.colormode]), TRUE);
1478 
1479   ready_now = TRUE;
1480 }
1481 
1482 
1483 /**********************************************************************
1484  FUNCTION: save_options
1485  *********************************************************************/
1486 
1487 static void
save_options(FILE * fp)1488 save_options (FILE * fp)
1489 {
1490   gchar buf[64];
1491 
1492   /* Save options */
1493 
1494   fprintf (fp, "fractaltype: %i\n", wvals.fractaltype);
1495 
1496   g_ascii_dtostr (buf, sizeof (buf), wvals.xmin);
1497   fprintf (fp, "xmin: %s\n", buf);
1498 
1499   g_ascii_dtostr (buf, sizeof (buf), wvals.xmax);
1500   fprintf (fp, "xmax: %s\n", buf);
1501 
1502   g_ascii_dtostr (buf, sizeof (buf), wvals.ymin);
1503   fprintf (fp, "ymin: %s\n", buf);
1504 
1505   g_ascii_dtostr (buf, sizeof (buf), wvals.ymax);
1506   fprintf (fp, "ymax: %s\n", buf);
1507 
1508   g_ascii_dtostr (buf, sizeof (buf), wvals.iter);
1509   fprintf (fp, "iter: %s\n", buf);
1510 
1511   g_ascii_dtostr (buf, sizeof (buf), wvals.cx);
1512   fprintf (fp, "cx: %s\n", buf);
1513 
1514   g_ascii_dtostr (buf, sizeof (buf), wvals.cy);
1515   fprintf (fp, "cy: %s\n", buf);
1516 
1517   g_ascii_dtostr (buf, sizeof (buf), wvals.redstretch * 128.0);
1518   fprintf (fp, "redstretch: %s\n", buf);
1519 
1520   g_ascii_dtostr (buf, sizeof (buf), wvals.greenstretch * 128.0);
1521   fprintf (fp, "greenstretch: %s\n", buf);
1522 
1523   g_ascii_dtostr (buf, sizeof (buf), wvals.bluestretch * 128.0);
1524   fprintf (fp, "bluestretch: %s\n", buf);
1525 
1526   fprintf (fp, "redmode: %i\n", wvals.redmode);
1527   fprintf (fp, "greenmode: %i\n", wvals.greenmode);
1528   fprintf (fp, "bluemode: %i\n", wvals.bluemode);
1529   fprintf (fp, "redinvert: %i\n", wvals.redinvert);
1530   fprintf (fp, "greeninvert: %i\n", wvals.greeninvert);
1531   fprintf (fp, "blueinvert: %i\n", wvals.blueinvert);
1532   fprintf (fp, "colormode: %i\n", wvals.colormode);
1533   fputs ("#**********************************************************************\n", fp);
1534   fprintf(fp, "<EOF>\n");
1535   fputs ("#**********************************************************************\n", fp);
1536 }
1537 
1538 static void
save_callback(void)1539 save_callback (void)
1540 {
1541   FILE        *fp;
1542   const gchar *savename = filename;
1543 
1544   fp = g_fopen (savename, "wt+");
1545 
1546   if (!fp)
1547     {
1548       g_message (_("Could not open '%s' for writing: %s"),
1549                  gimp_filename_to_utf8 (savename), g_strerror (errno));
1550       return;
1551     }
1552 
1553   /* Write header out */
1554   fputs (FRACTAL_HEADER, fp);
1555   fputs ("#**********************************************************************\n", fp);
1556   fputs ("# This is a data file for the Fractal Explorer plug-in for GIMP       *\n", fp);
1557   fputs ("#**********************************************************************\n", fp);
1558 
1559   save_options (fp);
1560 
1561   if (ferror (fp))
1562     g_message (_("Could not write '%s': %s"),
1563                gimp_filename_to_utf8 (savename), g_strerror (ferror (fp)));
1564 
1565   fclose (fp);
1566 }
1567 
1568 static void
save_file_chooser_response(GtkFileChooser * chooser,gint response_id,gpointer data)1569 save_file_chooser_response (GtkFileChooser *chooser,
1570                             gint            response_id,
1571                             gpointer        data)
1572 {
1573   if (response_id == GTK_RESPONSE_OK)
1574     {
1575       filename = gtk_file_chooser_get_filename (chooser);
1576 
1577       save_callback ();
1578     }
1579 
1580   gtk_widget_destroy (GTK_WIDGET (chooser));
1581 }
1582 
1583 static void
file_chooser_set_default_folder(GtkFileChooser * chooser)1584 file_chooser_set_default_folder (GtkFileChooser *chooser)
1585 {
1586   GList *path_list;
1587   gchar *dir;
1588 
1589   if (! fractalexplorer_path)
1590     return;
1591 
1592   path_list = gimp_path_parse (fractalexplorer_path, 256, FALSE, NULL);
1593 
1594   dir = gimp_path_get_user_writable_dir (path_list);
1595 
1596   if (! dir)
1597     dir = g_strdup (gimp_directory ());
1598 
1599   gtk_file_chooser_set_current_folder (chooser, dir);
1600 
1601   g_free (dir);
1602   gimp_path_free (path_list);
1603 }
1604 
1605 static void
load_file_chooser_response(GtkFileChooser * chooser,gint response_id,gpointer data)1606 load_file_chooser_response (GtkFileChooser *chooser,
1607                             gint            response_id,
1608                             gpointer        data)
1609 {
1610   if (response_id == GTK_RESPONSE_OK)
1611     {
1612       filename = gtk_file_chooser_get_filename (chooser);
1613 
1614       if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
1615         {
1616           explorer_load ();
1617         }
1618 
1619       gtk_widget_show (maindlg);
1620       dialog_change_scale ();
1621       set_cmap_preview ();
1622       dialog_update_preview ();
1623     }
1624 
1625   gtk_widget_destroy (GTK_WIDGET (chooser));
1626 }
1627 
1628 static void
create_load_file_chooser(GtkWidget * widget,GtkWidget * dialog)1629 create_load_file_chooser (GtkWidget *widget,
1630                           GtkWidget *dialog)
1631 {
1632   static GtkWidget *window = NULL;
1633 
1634   if (!window)
1635     {
1636       window =
1637         gtk_file_chooser_dialog_new (_("Load Fractal Parameters"),
1638                                      GTK_WINDOW (dialog),
1639                                      GTK_FILE_CHOOSER_ACTION_OPEN,
1640 
1641                                      _("_Cancel"), GTK_RESPONSE_CANCEL,
1642                                      _("_Open"),   GTK_RESPONSE_OK,
1643 
1644                                      NULL);
1645 
1646       gtk_dialog_set_default_response (GTK_DIALOG (window), GTK_RESPONSE_OK);
1647 
1648       gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
1649                                                GTK_RESPONSE_OK,
1650                                                GTK_RESPONSE_CANCEL,
1651                                                -1);
1652 
1653       file_chooser_set_default_folder (GTK_FILE_CHOOSER (window));
1654 
1655       g_signal_connect (window, "destroy",
1656                         G_CALLBACK (gtk_widget_destroyed),
1657                         &window);
1658       g_signal_connect (window, "response",
1659                         G_CALLBACK (load_file_chooser_response),
1660                         window);
1661     }
1662 
1663   gtk_window_present (GTK_WINDOW (window));
1664 }
1665 
1666 static void
create_save_file_chooser(GtkWidget * widget,GtkWidget * dialog)1667 create_save_file_chooser (GtkWidget *widget,
1668                           GtkWidget *dialog)
1669 {
1670   static GtkWidget *window = NULL;
1671 
1672   if (! window)
1673     {
1674       window =
1675         gtk_file_chooser_dialog_new (_("Save Fractal Parameters"),
1676                                      GTK_WINDOW (dialog),
1677                                      GTK_FILE_CHOOSER_ACTION_SAVE,
1678 
1679                                      _("_Cancel"), GTK_RESPONSE_CANCEL,
1680                                      _("_Save"),   GTK_RESPONSE_OK,
1681 
1682                                      NULL);
1683 
1684       gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
1685                                                GTK_RESPONSE_OK,
1686                                                GTK_RESPONSE_CANCEL,
1687                                                -1);
1688       gtk_dialog_set_default_response (GTK_DIALOG (window), GTK_RESPONSE_OK);
1689 
1690       gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (window),
1691                                                       TRUE);
1692       g_signal_connect (window, "destroy",
1693                         G_CALLBACK (gtk_widget_destroyed),
1694                         &window);
1695       g_signal_connect (window, "response",
1696                         G_CALLBACK (save_file_chooser_response),
1697                         window);
1698     }
1699 
1700   if (tpath)
1701     {
1702       gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (window), tpath);
1703     }
1704   else
1705     {
1706       file_chooser_set_default_folder (GTK_FILE_CHOOSER (window));
1707     }
1708 
1709   gtk_window_present (GTK_WINDOW (window));
1710 }
1711 
1712 gchar*
get_line(gchar * buf,gint s,FILE * from,gint init)1713 get_line (gchar *buf,
1714           gint   s,
1715           FILE  *from,
1716           gint   init)
1717 {
1718   gint   slen;
1719   gchar *ret;
1720 
1721   if (init)
1722     line_no = 1;
1723   else
1724     line_no++;
1725 
1726   do
1727     {
1728       ret = fgets (buf, s, from);
1729     }
1730   while (!ferror (from) && buf[0] == '#');
1731 
1732   slen = strlen (buf);
1733 
1734   /* The last newline is a pain */
1735   if (slen > 0)
1736     buf[slen - 1] = '\0';
1737 
1738   if (ferror (from))
1739     {
1740       g_warning ("Error reading file");
1741       return NULL;
1742     }
1743 
1744   return ret;
1745 }
1746 
1747 gint
load_options(fractalexplorerOBJ * xxx,FILE * fp)1748 load_options (fractalexplorerOBJ *xxx,
1749               FILE               *fp)
1750 {
1751   gchar load_buf[MAX_LOAD_LINE];
1752   gchar str_buf[MAX_LOAD_LINE];
1753   gchar opt_buf[MAX_LOAD_LINE];
1754 
1755   /*  default values  */
1756   xxx->opts = standardvals;
1757   xxx->opts.gradinvert    = FALSE;
1758 
1759   get_line (load_buf, MAX_LOAD_LINE, fp, 0);
1760 
1761   while (!feof (fp) && strcmp (load_buf, "<EOF>"))
1762     {
1763       /* Get option name */
1764       sscanf (load_buf, "%255s %255s", str_buf, opt_buf);
1765 
1766       if (!strcmp (str_buf, "fractaltype:"))
1767         {
1768           gint sp = 0;
1769 
1770           sp = atoi (opt_buf);
1771           if (sp < 0)
1772             return -1;
1773           xxx->opts.fractaltype = sp;
1774         }
1775       else if (!strcmp (str_buf, "xmin:"))
1776         {
1777           xxx->opts.xmin = g_ascii_strtod (opt_buf, NULL);
1778         }
1779       else if (!strcmp (str_buf, "xmax:"))
1780         {
1781           xxx->opts.xmax = g_ascii_strtod (opt_buf, NULL);
1782         }
1783       else if (!strcmp(str_buf, "ymin:"))
1784         {
1785           xxx->opts.ymin = g_ascii_strtod (opt_buf, NULL);
1786         }
1787       else if (!strcmp (str_buf, "ymax:"))
1788         {
1789           xxx->opts.ymax = g_ascii_strtod (opt_buf, NULL);
1790         }
1791       else if (!strcmp(str_buf, "redstretch:"))
1792         {
1793           gdouble sp = g_ascii_strtod (opt_buf, NULL);
1794           xxx->opts.redstretch = sp / 128.0;
1795         }
1796       else if (!strcmp(str_buf, "greenstretch:"))
1797         {
1798           gdouble sp = g_ascii_strtod (opt_buf, NULL);
1799           xxx->opts.greenstretch = sp / 128.0;
1800         }
1801       else if (!strcmp (str_buf, "bluestretch:"))
1802         {
1803           gdouble sp = g_ascii_strtod (opt_buf, NULL);
1804           xxx->opts.bluestretch = sp / 128.0;
1805         }
1806       else if (!strcmp (str_buf, "iter:"))
1807         {
1808           xxx->opts.iter = g_ascii_strtod (opt_buf, NULL);
1809         }
1810       else if (!strcmp(str_buf, "cx:"))
1811         {
1812           xxx->opts.cx = g_ascii_strtod (opt_buf, NULL);
1813         }
1814       else if (!strcmp (str_buf, "cy:"))
1815         {
1816           xxx->opts.cy = g_ascii_strtod (opt_buf, NULL);
1817         }
1818       else if (!strcmp(str_buf, "redmode:"))
1819         {
1820           xxx->opts.redmode = atoi (opt_buf);
1821         }
1822       else if (!strcmp(str_buf, "greenmode:"))
1823         {
1824           xxx->opts.greenmode = atoi (opt_buf);
1825         }
1826       else if (!strcmp(str_buf, "bluemode:"))
1827         {
1828           xxx->opts.bluemode = atoi (opt_buf);
1829         }
1830       else if (!strcmp (str_buf, "redinvert:"))
1831         {
1832           xxx->opts.redinvert = atoi (opt_buf);
1833         }
1834       else if (!strcmp (str_buf, "greeninvert:"))
1835         {
1836           xxx->opts.greeninvert = atoi (opt_buf);
1837         }
1838       else if (!strcmp(str_buf, "blueinvert:"))
1839         {
1840           xxx->opts.blueinvert = atoi (opt_buf);
1841         }
1842       else if (!strcmp (str_buf, "colormode:"))
1843         {
1844           xxx->opts.colormode = atoi (opt_buf);
1845         }
1846 
1847       get_line (load_buf, MAX_LOAD_LINE, fp, 0);
1848     }
1849 
1850   return 0;
1851 }
1852 
1853 void
explorer_load(void)1854 explorer_load (void)
1855 {
1856   FILE  *fp;
1857   gchar  load_buf[MAX_LOAD_LINE];
1858 
1859   g_assert (filename != NULL);
1860 
1861   fp = g_fopen (filename, "rt");
1862 
1863   if (!fp)
1864     {
1865       g_message (_("Could not open '%s' for reading: %s"),
1866                  gimp_filename_to_utf8 (filename), g_strerror (errno));
1867       return;
1868     }
1869   get_line (load_buf, MAX_LOAD_LINE, fp, 1);
1870 
1871   if (strncmp (FRACTAL_HEADER, load_buf, strlen (load_buf)))
1872     {
1873       g_message (_("'%s' is not a FractalExplorer file"),
1874                  gimp_filename_to_utf8 (filename));
1875       fclose (fp);
1876       return;
1877     }
1878   if (load_options (current_obj,fp))
1879     {
1880       g_message (_("'%s' is corrupt. Line %d Option section incorrect"),
1881                  gimp_filename_to_utf8 (filename), line_no);
1882       fclose (fp);
1883       return;
1884     }
1885 
1886   wvals = current_obj->opts;
1887 
1888   fclose (fp);
1889 }
1890