Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  cml-explorer.c   Sprache: C

 

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * CML_explorer.c
 * Time-stamp: <2000-02-13 18:18:37 yasuhiro>
 * Copyright (C) 1997 Shuji Narazaki <narazaki@InetQ.or.jp>
 * Version: 1.0.11
 * URL: http://www.inetq.or.jp/~narazaki/TheGIMP/
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Comment:
 *  CML is the abbreviation of Coupled-Map Lattice that is a model of
 *  complex systems, proposed by a physicist[1,2].
 *
 *  Similar models are summaried as follows:
 *
 *                      Value    Time     Space
 *  Coupled-Map Lattice cont.    discrete discrete
 *  Cellular Automata   discrete discrete discrete
 *  Differential Eq.    cont.    cont.    cont.
 *
 *  (But this program uses a parameter: hold-rate to avoid very fast changes.
 *  Thus time is rather continuous than discrete.
 *  Yes, this change to model changes the output completely.)
 *
 *  References:
 *  1. Kunihiko Kaneko, Period-doubling of kind-antikink patterns,
 *     quasi-periodicity in antiferro-like structures and spatial
 *     intermittency in coupled map lattices -- Toward a prelude to a
 *     "field theory of chaos", Prog. Theor. Phys. 72 (1984) 480.
 *
 *  2. Kunihiko Kaneko ed., Theory and Applications of Coupled Map
 *     Lattices (Wiley, 1993).
 *
 *  About Parameter File:
 *  I assume that the possible longest line in CMP parameter file is 1023.
 *  Please read CML_save_to_file_callback if you want know details of syntax.
 *
 *  Format version 1.0 starts with:
 *    ; This is a parameter file for CML_explorer
 *    ; File format version: 1.0
 *    ;
 *      Hue
 *
 *  The old format for CML_explorer included in gimp-0.99.[89] is:
 *    ; CML parameter file (version: 1.0)
 *    ; Hue
 *
 * (This file format is interpreted as format version 0.99 now.)
 *
 * Thanks:
 *  This version contains patches from:
 *    Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu>
 *    Sean P Cier <scier@andrew.cmu.edu>
 *    David Mosberger-Tang <davidm@azstarnet.com>
 *    Michael Sweet <mike@easysw.com>
 *
 */


#include "config.h"

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <glib/gstdio.h>

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "libgimp/stdplugins-intl.h"


#define PARAM_FILE_FORMAT_VERSION 1.0
#define PLUG_IN_PROC              "plug-in-cml-explorer"
#define PLUG_IN_BINARY            "cml-explorer"
#define PLUG_IN_ROLE              "gimp-cml-explorer"
#define VALS                      CML_explorer_vals
#define PROGRESS_UPDATE_NUM        100
#define CML_LINE_SIZE             1024
#define TILE_CACHE_SIZE             32
#define PREVIEW_WIDTH               64
#define PREVIEW_HEIGHT             220

#define CANNONIZE(p, x)      (255*(((p).range_h - (p).range_l)*(x) + (p).range_l))
#define HCANNONIZE(p, x)     (254*(((p).range_h - (p).range_l)*(x) + (p).range_l))
#define POS_IN_TORUS(i,size) ((i < 0) ? size + i : ((size <= i) ? i - size : i))

typedef struct _WidgetEntry WidgetEntry;

struct _WidgetEntry
{
  GtkWidget  *widget;
  gpointer    value;
  void      (*updater) (WidgetEntry *);
};

enum
{
  CML_KEEP_VALUES,
  CML_KEEP_FIRST,
  CML_FILL,
  CML_LOGIST,
  CML_LOGIST_STEP,
  CML_POWER,
  CML_POWER_STEP,
  CML_REV_POWER,
  CML_REV_POWER_STEP,
  CML_DELTA,
  CML_DELTA_STEP,
  CML_SIN_CURVE,
  CML_SIN_CURVE_STEP,
  CML_NUM_VALUES
};

static const gchar *function_names[CML_NUM_VALUES] =
{
  N_("Keep image's values"),
  N_("Keep the first value"),
  N_("Fill with parameter k"),
  N_("k{x(1-x)}^p"),
  N_("k{x(1-x)}^p stepped"),
  N_("kx^p"),
  N_("kx^p stepped"),
  N_("k(1-x^p)"),
  N_("k(1-x^p) stepped"),
  N_("Delta function"),
  N_("Delta function stepped"),
  N_("sin^p-based function"),
  N_("sin^p, stepped")
};

enum
{
  COMP_NONE,
  COMP_MAX_LINEAR,
  COMP_MAX_LINEAR_P1,
  COMP_MAX_LINEAR_M1,
  COMP_MIN_LINEAR,
  COMP_MIN_LINEAR_P1,
  COMP_MIN_LINEAR_M1,
  COMP_MAX_LINEAR_P1L,
  COMP_MAX_LINEAR_P1U,
  COMP_MAX_LINEAR_M1L,
  COMP_MAX_LINEAR_M1U,
  COMP_MIN_LINEAR_P1L,
  COMP_MIN_LINEAR_P1U,
  COMP_MIN_LINEAR_M1L,
  COMP_MIN_LINEAR_M1U,
  COMP_NUM_VALUES
};

static const gchar *composition_names[COMP_NUM_VALUES] =
{
  NC_("cml-composition", "None"),
  N_("Max (x, -)"),
  N_("Max (x+d, -)"),
  N_("Max (x-d, -)"),
  N_("Min (x, -)"),
  N_("Min (x+d, -)"),
  N_("Min (x-d, -)"),
  N_("Max (x+d, -), (x < 0.5)"),
  N_("Max (x+d, -), (0.5 < x)"),
  N_("Max (x-d, -), (x < 0.5)"),
  N_("Max (x-d, -), (0.5 < x)"),
  N_("Min (x+d, -), (x < 0.5)"),
  N_("Min (x+d, -), (0.5 < x)"),
  N_("Min (x-d, -), (x < 0.5)"),
  N_("Min (x-d, -), (0.5 < x)")
};

enum
{
  STANDARD,
  AVERAGE,
  ANTILOG,
  RAND_POWER0,
  RAND_POWER1,
  RAND_POWER2,
  MULTIPLY_RANDOM0,
  MULTIPLY_RANDOM1,
  MULTIPLY_GRADIENT,
  RAND_AND_P,
  ARRANGE_NUM_VALUES
};

static const gchar *arrange_names[ARRANGE_NUM_VALUES] =
{
  N_("Standard"),
  N_("Use average value"),
  N_("Use reverse value"),
  N_("With random power (0,10)"),
  N_("With random power (0,1)"),
  N_("With gradient power (0,1)"),
  N_("Multiply random value (0,1)"),
  N_("Multiply random value (0,2)"),
  N_("Multiply gradient (0,1)"),
  N_("With p and random (0,1)"),
};

enum
{
  CML_INITIAL_RANDOM_INDEPENDENT = 6,
  CML_INITIAL_RANDOM_SHARED,
  CML_INITIAL_RANDOM_FROM_SEED,
  CML_INITIAL_RANDOM_FROM_SEED_SHARED,
  CML_INITIAL_NUM_VALUES
};

static const gchar *initial_value_names[CML_INITIAL_NUM_VALUES] =
{
  N_("All black"),
  N_("All gray"),
  N_("All white"),
  N_("The first row of the image"),
  N_("Continuous gradient"),
  N_("Continuous gradient, without gap"),
  N_("Random, channel independent"),
  N_("Random shared"),
  N_("Random from seed"),
  N_("Random from seed (shared)")
};

#define CML_PARAM_NUM   15

typedef struct
{
  gint    function;
  gint    composition;
  gint    arrange;
  gint    cyclic_range;
  gdouble mod_rate;             /* diff / old-value */
  gdouble env_sensitivity;      /* self-diff : env-diff */
  gint    diffusion_dist;
  gdouble ch_sensitivity;
  gint    range_num;
  gdouble power;
  gdouble parameter_k;
  gdouble range_l;
  gdouble range_h;
  gdouble mutation_rate;
  gdouble mutation_dist;
} CML_PARAM;

typedef struct
{
  CML_PARAM hue;
  CML_PARAM sat;
  CML_PARAM val;
  gint      initial_value;
  gint      scale;
  gint      start_offset;
  gint      seed;
} ValueType;

static ValueType VALS =
{
  /* function      composition  arra
    cyc chng sens  diff cor  n  pow  k    (l,h)   rnd  dist */

  {
    CML_SIN_CURVE, COMP_NONE,   STANDARD,
    1,  0.5, 0.7,  2,   0.0, 1, 1.0, 1.0, 0, 1,   0.0, 0.1
  },
  {
    CML_FILL,      COMP_NONE,    STANDARD,
    0,  0.6, 0.1,  2,   0.0, 1, 1.4, 0.9, 0, 0.9, 0.0, 0.1
  },
  {
    CML_FILL,      COMP_NONE,    STANDARD,
    0,  0.5, 0.2,  2,   0.0, 1, 2.0, 1.0, 0, 0.9, 0.0, 0.1
  },
  6,    /* random value 1 */
  1,    /* scale */
  0,    /* start_offset */
  0,    /* seed */
};

static CML_PARAM *channel_params[] =
{
  &VALS.hue,
  &VALS.sat,
  &VALS.val
};

static const gchar *channel_names[] =
{
  N_("Hue"),
  N_("Saturation"),
  N_("Value")
};

static const gchar *load_channel_names[] =
{
  N_("(None)"),
  N_("Hue"),
  N_("Saturation"),
  N_("Value")
};


typedef struct _Explorer      Explorer;
typedef struct _ExplorerClass ExplorerClass;

struct _Explorer
{
  GimpPlugIn parent_instance;
};

struct _ExplorerClass
{
  GimpPlugInClass parent_class;
};


#define EXPLORER_TYPE  (explorer_get_type ())
#define EXPLORER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EXPLORER_TYPE, Explorer))

GType                   explorer_get_type         (void) G_GNUC_CONST;

static GList          * explorer_query_procedures (GimpPlugIn           *plug_in);
static GimpProcedure  * explorer_create_procedure (GimpPlugIn           *plug_in,
                                                   const gchar          *name);

static GimpValueArray * explorer_run              (GimpProcedure        *procedure,
                                                   GimpRunMode           run_mode,
                                                   GimpImage            *image,
                                                   GimpDrawable        **drawables,
                                                   GimpProcedureConfig  *config,
                                                   gpointer              run_data);

static gboolean          CML_main_function        (gboolean              preview_p);
static void              CML_compute_next_step    (gint                  size,
                                                   gdouble             **h,
                                                   gdouble             **s,
                                                   gdouble             **v,
                                                   gdouble             **hn,
                                                   gdouble             **sn,
                                                   gdouble             **vn,
                                                   gdouble             **haux,
                                                   gdouble             **saux,
                                                   gdouble             **vaux);
static gdouble           CML_next_value           (gdouble              *vec,
                                                   gint                  pos,
                                                   gint                  size,
                                                   gdouble               c1,
                                                   gdouble               c2,
                                                   CML_PARAM            *param,
                                                   gdouble               aux);
static gdouble           logistic_function        (CML_PARAM            *param,
                                                   gdouble               x,
                                                   gdouble               power);


static gint        CML_explorer_dialog            (GimpProcedureConfig  *config);
static GtkWidget * CML_dialog_channel_panel_new   (CML_PARAM            *param,
                                                   gint                  channel_id);
static GtkWidget * CML_dialog_advanced_panel_new  (void);

static void     CML_explorer_toggle_entry_init    (WidgetEntry          *widget_entry,
                                                   GtkWidget            *widget,
                                                   gpointer              value_ptr);

static void     CML_explorer_int_entry_init       (WidgetEntry          *widget_entry,
                                                   GtkWidget            *scale,
                                                   gpointer               value_ptr);

static void     CML_explorer_double_entry_init    (WidgetEntry          *widget_entry,
                                                   GtkWidget            *scale,
                                                   gpointer              value_ptr);

static void     CML_explorer_menu_update          (GtkWidget             *widget,
                                                   gpointer               data);
static void     CML_initial_value_menu_update     (GtkWidget             *widget,
                                                   gpointer               data);
static void     CML_explorer_menu_entry_init      (WidgetEntry           *widget_entry,
                                                   GtkWidget             *widget,
                                                   gpointer               value_ptr);

static void    preview_update                      (void);
static void    function_graph_new                  (GtkWidget            *widget,
                                                    gpointer             *data);
static void    CML_set_or_randomize_seed_callback  (GtkWidget            *widget,
                                                    gpointer              data);
static void    CML_copy_parameters_callback        (GtkWidget            *widget,
                                                    gpointer              data);
static void    CML_initial_value_sensitives_update (void);

static void    CML_save_to_file_callback           (GtkWidget            *widget,
                                                    GimpProcedureConfig  *config);
static void    CML_save_to_file_response           (GtkWidget            *dialog,
                                                    gint                  response_id,
                                                    GimpProcedureConfig  *config);

static void    CML_preview_update_callback         (GtkWidget            *widget,
                                                    gpointer              data);
static void    CML_load_from_file_callback         (GtkWidget            *widget,
                                                    GimpProcedureConfig  *config);
static gboolean CML_load_parameter_file            (GFile                *file,
                                                    gboolean              interactive_mode,
                                                    GimpProcedureConfig  *config,
                                                    GError              **error);
static void    CML_load_from_file_response         (GtkWidget            *dialog,
                                                    gint                  response_id,
                                                    GimpProcedureConfig  *config);
static gint    parse_line_to_gint                  (GDataInputStream         *input,
                                                    gboolean             *flag,
                                                    GError              **error);
static gdouble parse_line_to_gdouble               (GDataInputStream         *input,
                                                    gboolean             *flag,
                                                    GError              **error);


G_DEFINE_TYPE (Explorer, explorer, GIMP_TYPE_PLUG_IN)

GIMP_MAIN (EXPLORER_TYPE)
DEFINE_STD_SET_I18N


static GtkWidget   *preview;
static WidgetEntry  widget_pointers[4][CML_PARAM_NUM];

static guchar          *img;
static gint             img_stride;
static cairo_surface_t *buffer;

typedef struct
{
  GtkWidget *widget;
  gint       logic;
} CML_sensitive_widget_table;

#define RANDOM_SENSITIVES_NUM 5
#define GRAPHSIZE 256

static CML_sensitive_widget_table random_sensitives[RANDOM_SENSITIVES_NUM] =
{
  { NULL, 0 },
  { NULL, 0 },
  { NULL, 0 },
  { NULL, 0 },
  { NULL, 0 }
};

static GRand        *gr;
static GimpDrawable *drawable = NULL;
static gint          copy_source = 0;
static gint          copy_destination = 0;
static gint          selective_load_source = 0;
static gint          selective_load_destination = 0;
static gboolean      CML_preview_defer = FALSE;

static gdouble  *mem_chank0 = NULL;
static gint      mem_chank0_size = 0;
static guchar   *mem_chank1 = NULL;
static gint      mem_chank1_size = 0;
static guchar   *mem_chank2 = NULL;
static gint      mem_chank2_size = 0;


static void
explorer_class_init (ExplorerClass *klass)
{
  GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);

  plug_in_class->query_procedures = explorer_query_procedures;
  plug_in_class->create_procedure = explorer_create_procedure;
  plug_in_class->set_i18n         = STD_SET_I18N;
}

static void
explorer_init (Explorer *explorer)
{
}

static GList *
explorer_query_procedures (GimpPlugIn *plug_in)
{
  return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}

static GimpProcedure *
explorer_create_procedure (GimpPlugIn  *plug_in,
                           const gchar *name)
{
  GimpProcedure *procedure = NULL;

  if (! strcmp (name, PLUG_IN_PROC))
    {
      procedure = gimp_image_procedure_new (plug_in, name,
                                            GIMP_PDB_PROC_TYPE_PLUGIN,
                                            explorer_run, NULL, NULL);

      gimp_procedure_set_image_types (procedure, "RGB*, GRAY*");
      gimp_procedure_set_sensitivity_mask (procedure,
                                           GIMP_PROCEDURE_SENSITIVE_DRAWABLE);

      gimp_procedure_set_menu_label (procedure, _("CML _Explorer..."));
      gimp_procedure_add_menu_path (procedure,
                                    "<Image>/Filters/Render/Pattern");

      gimp_procedure_set_documentation (procedure,
                                        _("Create abstract Coupled-Map "
                                          "Lattice patterns"),
                                        _("Make an image of Coupled-Map Lattice "
                                          "(CML). CML is a kind of Cellular "
                                          "Automata on continuous (value) "
                                          "domain. In GIMP_RUN_NONINTERACTIVE, "
                                          "the name of a parameter file is "
                                          "passed as the 4th arg. You can "
                                          "control CML_explorer via parameter "
                                          "file."),
                                        /*  Or do you want to call me
                                         *  with over 50 args?
                                         */

                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Shuji Narazaki (narazaki@InetQ.or.jp); "
                                      "http://www.inetq.or.jp/~narazaki/TheGIMP/",
                                      "Shuji Narazaki",
                                      "1997");

      gimp_procedure_add_file_argument (procedure, "parameter-file",
                                        _("Parameter File"),
                                        _("The parameter file from which CML_explorer makes an image. "
                                          "This argument is only used in non-interactive runs."),
                                        GIMP_FILE_CHOOSER_ACTION_OPEN,
                                        FALSE, NULL,
                                        G_PARAM_READWRITE);

      gimp_procedure_add_bytes_aux_argument (procedure, "settings-data",
                                             "Settings data",
                                             "TODO: eventually we must implement proper args for every settings",
                                             GIMP_PARAM_READWRITE);
    }

  return procedure;
}

static GimpValueArray *
explorer_run (GimpProcedure        *procedure,
              GimpRunMode           run_mode,
              GimpImage            *image,
              GimpDrawable        **drawables,
              GimpProcedureConfig  *config,
              gpointer              run_data)
{
  GBytes *settings_bytes = NULL;
  GError *error          = NULL;

  if (gimp_core_object_array_get_length ((GObject **) drawables) != 1)
    {
      g_set_error (&error, GIMP_PLUG_IN_ERROR, 0,
                   _("Procedure '%s' only works with one drawable."),
                   PLUG_IN_PROC);

      return gimp_procedure_new_return_values (procedure,
                                               GIMP_PDB_CALLING_ERROR,
                                               error);
    }
  else
    {
      drawable = drawables[0];
    }

  gegl_init (NULL, NULL);

  /* Temporary code replacing legacy gimp_[gs]et_data() using an AUX argument.
   * This doesn't actually fix the "Reset to initial values|factory defaults"
   * features, but at least makes per-run value storage work.
   * TODO: eventually we want proper separate arguments as a complete fix.
   */

  g_object_get (config, "settings-data", &settings_bytes, NULL);
  if (settings_bytes != NULL && g_bytes_get_size (settings_bytes) == sizeof (ValueType))
    VALS = *((ValueType *) g_bytes_get_data (settings_bytes, NULL));
  g_bytes_unref (settings_bytes);

  switch (run_mode)
    {
    case GIMP_RUN_INTERACTIVE:
      if (! CML_explorer_dialog (config))
        return gimp_procedure_new_return_values (procedure,
                                                 GIMP_PDB_CANCEL,
                                                 NULL);
      break;

    case GIMP_RUN_NONINTERACTIVE:
      {
        GFile *file = NULL;

        g_object_get (config,
                      "parameter-file", &file,
                      NULL);

        if (file == NULL)
          {
            g_set_error (&error, GIMP_PLUG_IN_ERROR, 0,
                         "Procedure '%s' requires \"parameter-file\" argument "
                         "in non-interactive runs.",
                         PLUG_IN_PROC);

            return gimp_procedure_new_return_values (procedure,
                                                     GIMP_PDB_CALLING_ERROR,
                                                     error);
          }
        else if (! CML_load_parameter_file (file, FALSE, config, &error))
          {
            g_object_unref (file);

            return gimp_procedure_new_return_values (procedure,
                                                     GIMP_PDB_CALLING_ERROR,
                                                     error);
          }

        g_object_unref (file);
      }
      break;

    case GIMP_RUN_WITH_LAST_VALS:
      break;
    }

  if (CML_main_function (FALSE))
    {
      if (run_mode != GIMP_RUN_NONINTERACTIVE)
        gimp_displays_flush();
    }
  else
    {
      return gimp_procedure_new_return_values (procedure,
                                               GIMP_PDB_EXECUTION_ERROR,
                                               NULL);
    }

  settings_bytes = g_bytes_new (&VALS, sizeof (ValueType));
  g_object_set (config, "settings-data", settings_bytes, NULL);
  g_bytes_unref (settings_bytes);

  g_free (mem_chank0);
  g_free (mem_chank1);
  g_free (mem_chank2);

  return gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL);
}

static gboolean
CML_main_function (gboolean preview_p)
{
  GeglBuffer *src_buffer;
  GeglBuffer *dest_buffer;
  const Babl *src_format;
  const Babl *dest_format;
  const Babl *to_hsv;
  const Babl *to_rgb;
  guchar     *dest_buf = NULL;
  guchar     *src_buf  = NULL;
  gint        x, y;
  gint        dx, dy;
  gboolean    dest_has_alpha = FALSE;
  gboolean    dest_is_gray   = FALSE;
  gboolean    src_has_alpha  = FALSE;
  gboolean    src_is_gray    = FALSE;
  gint        total, processed = 0;
  gint        keep_height = 1;
  gint        cell_num, width_by_pixel, height_by_pixel;
  gint        index;
  gint        src_bpp, src_bpl;
  gint        dest_bpp, dest_bpl;
  gdouble    *hues, *sats, *vals;
  gdouble    *newh, *news, *newv;
  gdouble    *haux, *saux, *vaux;

  if (! gimp_drawable_mask_intersect (drawable,
                                      &x, &y,
                                      &width_by_pixel, &height_by_pixel))
    return TRUE;

  to_hsv = babl_fish (babl_format ("R'G'B' float"), babl_format ("HSV float"));
  to_rgb = babl_fish (babl_format ("HSV float"), babl_format ("R'G'B' float"));

  src_has_alpha = dest_has_alpha = gimp_drawable_has_alpha (drawable);
  src_is_gray   = dest_is_gray   = gimp_drawable_is_gray (drawable);

  if (src_is_gray)
    {
      if (src_has_alpha)
        src_format = babl_format ("Y'A u8");
      else
        src_format = babl_format ("Y' u8");
    }
  else
    {
      if (src_has_alpha)
        src_format = babl_format ("R'G'B'A u8");
      else
        src_format = babl_format ("R'G'B' u8");
    }

  dest_format = src_format;

  src_bpp = dest_bpp = babl_format_get_bytes_per_pixel (src_format);

  if (preview_p)
    {
      dest_format = babl_format ("R'G'B' u8");

      dest_has_alpha = FALSE;
      dest_bpp       = 3;

      if (width_by_pixel > PREVIEW_WIDTH)      /* preview < drawable (selection) */
        width_by_pixel = PREVIEW_WIDTH;
      if (height_by_pixel > PREVIEW_HEIGHT)
        height_by_pixel = PREVIEW_HEIGHT;
    }

  dest_bpl = width_by_pixel * dest_bpp;
  src_bpl = width_by_pixel * src_bpp;
  cell_num = (width_by_pixel - 1)/ VALS.scale + 1;
  total = height_by_pixel * width_by_pixel;

  if (total < 1)
    return FALSE;

  keep_height = VALS.scale;

  /* configure reusable memories */
  if (mem_chank0_size < 9 * cell_num * sizeof (gdouble))
    {
      g_free (mem_chank0);
      mem_chank0_size = 9 * cell_num * sizeof (gdouble);
      mem_chank0 = (gdouble *) g_malloc (mem_chank0_size);
    }

  hues = mem_chank0;
  sats = mem_chank0 + cell_num;
  vals = mem_chank0 + 2 * cell_num;
  newh = mem_chank0 + 3 * cell_num;
  news = mem_chank0 + 4 * cell_num;
  newv = mem_chank0 + 5 * cell_num;
  haux = mem_chank0 + 6 * cell_num;
  saux = mem_chank0 + 7 * cell_num;
  vaux = mem_chank0 + 8 * cell_num;

  if (mem_chank1_size < src_bpl * keep_height)
    {
      g_free (mem_chank1);
      mem_chank1_size = src_bpl * keep_height;
      mem_chank1 = (guchar *) g_malloc (mem_chank1_size);
    }
  src_buf = mem_chank1;

  if (mem_chank2_size < dest_bpl * keep_height)
    {
      g_free (mem_chank2);
      mem_chank2_size = dest_bpl * keep_height;
      mem_chank2 = (guchar *) g_malloc (mem_chank2_size);
    }
  dest_buf = mem_chank2;

  if (! preview_p)
    dest_buffer = gimp_drawable_get_shadow_buffer (drawable);

  src_buffer = gimp_drawable_get_buffer (drawable);

  gr = g_rand_new ();
  if (VALS.initial_value == CML_INITIAL_RANDOM_FROM_SEED)
    g_rand_set_seed (gr, VALS.seed);

  for (index = 0; index < cell_num; index++)
    {
      switch (VALS.hue.arrange)
        {
        case RAND_POWER0:
          haux [index] = g_rand_double_range (gr, 0, 10);
          break;
        case RAND_POWER2:
        case MULTIPLY_GRADIENT:
          haux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256;
          break;
        case RAND_POWER1:
        case MULTIPLY_RANDOM0:
          haux [index] = g_rand_double (gr);
          break;
        case MULTIPLY_RANDOM1:
          haux [index] = g_rand_double_range (gr, 0, 2);
          break;
        case RAND_AND_P:
          haux [index] = ((index % (2 * VALS.hue.diffusion_dist) == 0) ?
                          g_rand_double (gr) : VALS.hue.power);
          break;
        default:
          haux [index] = VALS.hue.power;
          break;
        }

      switch (VALS.sat.arrange)
        {
        case RAND_POWER0:
          saux [index] = g_rand_double_range (gr, 0, 10);
          break;
        case RAND_POWER2:
        case MULTIPLY_GRADIENT:
          saux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256;
          break;
        case RAND_POWER1:
        case MULTIPLY_RANDOM0:
          saux [index] = g_rand_double (gr);
          break;
        case MULTIPLY_RANDOM1:
          saux [index] = g_rand_double_range (gr, 0, 2);
          break;
        case RAND_AND_P:
          saux [index] = ((index % (2 * VALS.sat.diffusion_dist) == 0) ?
                          g_rand_double (gr) : VALS.sat.power);
          break;
        default:
          saux [index] = VALS.sat.power;
          break;
        }

      switch (VALS.val.arrange)
        {
        case RAND_POWER0:
          vaux [index] = g_rand_double_range (gr, 0, 10);
          break;
        case RAND_POWER2:
        case MULTIPLY_GRADIENT:
          vaux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256;
          break;
        case RAND_POWER1:
        case MULTIPLY_RANDOM0:
          vaux [index] = g_rand_double (gr);
          break;
        case MULTIPLY_RANDOM1:
          vaux [index] = g_rand_double_range (gr, 0, 2);
          break;
        case RAND_AND_P:
          vaux [index] = ((index % (2 * VALS.val.diffusion_dist) == 0) ?
                          g_rand_double (gr) : VALS.val.power);
          break;
        default:
          vaux [index] = VALS.val.power;
          break;
        }

      switch (VALS.initial_value)
        {
        case 0:
        case 1:
        case 2:
          hues[index] = sats[index] = vals[index] = 0.5 * (VALS.initial_value);
          break;
        case 3:                 /* use the values of the image (drawable) */
          break;                /* copy from the drawable after this loop */
        case 4:                 /* grandient 1 */
          hues[index] = sats[index] = vals[index]
            = (gdouble) (index % 256) / (gdouble) 256;
          break;                /* gradinet 2 */
        case 5:
          hues[index] = sats[index] = vals[index]
            = (gdouble) abs ((index % 511) - 255) / (gdouble) 256;
          break;
        case CML_INITIAL_RANDOM_INDEPENDENT:
        case CML_INITIAL_RANDOM_FROM_SEED:
          hues[index] = g_rand_double (gr);
          sats[index] = g_rand_double (gr);
          vals[index] = g_rand_double (gr);
          break;
        case CML_INITIAL_RANDOM_SHARED:
        case CML_INITIAL_RANDOM_FROM_SEED_SHARED:
          hues[index] = sats[index] = vals[index] = g_rand_double (gr);
          break;
        }
    }

  if (VALS.initial_value == 3)
    {
      int index;

      for (index = 0;
           index < MIN (cell_num, width_by_pixel / VALS.scale);
           index++)
        {
          gfloat hsv[3] = {0, 0, 0};

          gegl_buffer_sample (src_buffer, x + (index * VALS.scale), y, NULL,
                              buffer, babl_format ("HSV float"),
                              GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);


          hues[index] = hsv[0];
          sats[index] = hsv[1];
          vals[index] = hsv[2];
        }
    }

  if (! preview_p)
    gimp_progress_init (_("CML Explorer: evoluting"));

  /* rolling start */
  for (index = 0; index < VALS.start_offset; index++)
    CML_compute_next_step (cell_num, &hues, &sats, &vals, &newh, &news, &newv,
                           &haux, &saux, &vaux);

  /* rendering */
  for (dy = 0; dy < height_by_pixel; dy += VALS.scale)
    {
      gint r, g, b, h, s, v;
      gint offset_x, offset_y, dest_offset;

      if (height_by_pixel < dy + keep_height)
        keep_height = height_by_pixel - dy;

      if ((VALS.hue.function == CML_KEEP_VALUES) ||
          (VALS.sat.function == CML_KEEP_VALUES) ||
          (VALS.val.function == CML_KEEP_VALUES))
        {
          gegl_buffer_get (src_buffer,
                           GEGL_RECTANGLE (x, y + dy,
                                           width_by_pixel, keep_height), 1.0,
                           src_format, src_buf,
                           GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
        }

      CML_compute_next_step (cell_num,
                             &hues, &sats, &vals,
                             &newh, &news, &newv,
                             &haux, &saux, &vaux);

      for (dx = 0; dx < cell_num; dx++)
        {
          h = r = HCANNONIZE (VALS.hue, hues[dx]);
          s = g = CANNONIZE (VALS.sat, sats[dx]);
          v = b = CANNONIZE (VALS.val, vals[dx]);

          if (! dest_is_gray)
            {
              gfloat hsv[3] = { h / 255.0, s / 255.0, v / 255.0 };
              gfloat rgb[3];

              babl_process (to_rgb, hsv, rgb, 1);

              r = ROUND (rgb[0] * 255.0);
              g = ROUND (rgb[1] * 255.0);
              b = ROUND (rgb[2] * 255.0);
            }

          /* render destination */
          for (offset_y = 0;
               (offset_y < VALS.scale) && (dy + offset_y < height_by_pixel);
               offset_y++)
            for (offset_x = 0;
                 (offset_x < VALS.scale) && (dx * VALS.scale + offset_x < width_by_pixel);
                 offset_x++)
              {
                if ((VALS.hue.function == CML_KEEP_VALUES) ||
                    (VALS.sat.function == CML_KEEP_VALUES) ||
                    (VALS.val.function == CML_KEEP_VALUES))
                  {
                    int rgbi[3];
                    int i;

                    for (i = 0; i < src_bpp; i++)
                      rgbi[i] = src_buf[offset_y * src_bpl
                                        + (dx * VALS.scale + offset_x) * src_bpp + i];
                    if (src_is_gray && (VALS.val.function == CML_KEEP_VALUES))
                      {
                        b = rgbi[0];
                      }
                    else
                      {
                        gfloat rgb[3];
                        gfloat hsv[3];

                        for (i = 0; i < 3; i++)
                          rgb[i] = (gfloat) rgbi[i] / 255.0;

                        babl_process (to_hsv, rgb, hsv, 1);

                        if (VALS.hue.function != CML_KEEP_VALUES)
                          hsv[0] = (gfloat) h / 255.0;

                        if  (VALS.sat.function != CML_KEEP_VALUES)
                          hsv[1] = (gfloat) s / 255.0;

                        if (VALS.val.function != CML_KEEP_VALUES)
                          hsv[2] = (gfloat) v / 255.0;

                        babl_process (to_rgb, hsv, rgb, 1);

                        r = ROUND (rgb[0] * 255.0);
                        g = ROUND (rgb[1] * 255.0);
                        b = ROUND (rgb[2] * 255.0);
                      }
                  }

                dest_offset = (offset_y * dest_bpl +
                               (dx * VALS.scale + offset_x) * dest_bpp);

                if (dest_is_gray)
                  {
                    dest_buf[dest_offset++] = b;
                    if (preview_p)
                      {
                        dest_buf[dest_offset++] = b;
                        dest_buf[dest_offset++] = b;
                      }
                  }
                else
                  {
                    dest_buf[dest_offset++] = r;
                    dest_buf[dest_offset++] = g;
                    dest_buf[dest_offset++] = b;
                  }
                if (dest_has_alpha)
                  dest_buf[dest_offset] = 255;

                if ((!preview_p) &&
                    (++processed % (total / PROGRESS_UPDATE_NUM + 1)) == 0)
                  gimp_progress_update ((gdouble) processed / (gdouble) total);
              }
        }

      if (preview_p)
        {
          gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
                                  0, dy,
                                  width_by_pixel, keep_height,
                                  GIMP_RGB_IMAGE,
                                  dest_buf,
                                  dest_bpl);
        }
      else
        {
          gegl_buffer_set (dest_buffer,
                           GEGL_RECTANGLE (x, y + dy,
                                           width_by_pixel, keep_height), 0,
                           dest_format, dest_buf,
                           GEGL_AUTO_ROWSTRIDE);
        }
    }

  g_object_unref (src_buffer);

  if (preview_p)
    {
      gtk_widget_queue_draw (preview);
    }
  else
    {
      gimp_progress_update (1.0);

      g_object_unref (dest_buffer);

      gimp_drawable_merge_shadow (drawable, TRUE);
      gimp_drawable_update (drawable,
                            x, y, width_by_pixel, height_by_pixel);
    }

  g_rand_free (gr);

  return TRUE;
}

static void
CML_compute_next_step (gint      size,
                       gdouble **h,
                       gdouble **s,
                       gdouble **v,
                       gdouble **hn,
                       gdouble **sn,
                       gdouble **vn,
                       gdouble **haux,
                       gdouble **saux,
                       gdouble **vaux)
{
  gint  index;

  for (index = 0; index < size; index++)
    (*hn)[index] = CML_next_value (*h, index, size,
                                   (*s)[POS_IN_TORUS (index, size)],
                                   (*v)[POS_IN_TORUS (index, size)],
                                   &VALS.hue,
                                   (*haux)[POS_IN_TORUS (index , size)]);
  for (index = 0; index < size; index++)
    (*sn)[index] = CML_next_value (*s, index, size,
                                   (*v)[POS_IN_TORUS (index   , size)],
                                   (*h)[POS_IN_TORUS (index   , size)],
                                   &VALS.sat,
                                   (*saux)[POS_IN_TORUS (index , size)]);
  for (index = 0; index < size; index++)
    (*vn)[index] = CML_next_value (*v, index, size,
                                   (*h)[POS_IN_TORUS (index   , size)],
                                   (*s)[POS_IN_TORUS (index   , size)],
                                   &VALS.val,
                                   (*vaux)[POS_IN_TORUS (index , size)]);

#define GD_SWAP(x, y)   { gdouble *tmp = *x; *x = *y; *y = tmp; }
  GD_SWAP (h, hn);
  GD_SWAP (s, sn);
  GD_SWAP (v, vn);
#undef  SWAP
}

#define LOGISTICS(x)    logistic_function (param, x, power)
#define ENV_FACTOR(x)   (param->env_sensitivity * LOGISTICS (x))
#define CHN_FACTOR(x)   (param->ch_sensitivity * LOGISTICS (x))

static gdouble
CML_next_value (gdouble   *vec,
                gint       pos,
                gint       size,
                gdouble    c1,
                gdouble    c2,
                CML_PARAM *param,
                gdouble    power)
{
  gdouble val = vec[pos];
  gdouble diff = 0;
  gdouble self_diff = 0;
  gdouble by_env = 0;
  gdouble self_mod_rate = 0;
  gdouble hold_rate = 1 - param->mod_rate;
  gdouble env_factor = 0;
  gint    index;

  self_mod_rate = (1 - param->env_sensitivity - param->ch_sensitivity);

  switch (param->arrange)
    {
    case ANTILOG:
      self_diff = self_mod_rate * LOGISTICS (1 - vec[pos]);
      for (index = 1; index <= param->diffusion_dist / 2; index++)
        env_factor += ENV_FACTOR (1 - vec[POS_IN_TORUS (pos + index, size)])
          + ENV_FACTOR (1 - vec[POS_IN_TORUS (pos - index, size)]);
      if ((param->diffusion_dist % 2) == 1)
        env_factor += (ENV_FACTOR (1 - vec[POS_IN_TORUS (pos + index, size)])
                       + ENV_FACTOR (1 - vec[POS_IN_TORUS (pos - index, size)])) / 2;
      env_factor /= (gdouble) param->diffusion_dist;
      by_env = env_factor + (CHN_FACTOR (1 - c1) + CHN_FACTOR (1 - c2)) / 2;
      diff = param->mod_rate * (self_diff + by_env);
      val = hold_rate * vec[pos] + diff;
      break;
    case AVERAGE:
      self_diff = self_mod_rate * LOGISTICS (vec[pos]);
      for (index = 1; index <= param->diffusion_dist / 2; index++)
        env_factor += vec[POS_IN_TORUS (pos + index, size)] + vec[POS_IN_TORUS (pos - index, size)];
      if ((param->diffusion_dist % 2) == 1)
        env_factor += (vec[POS_IN_TORUS (pos + index, size)] + vec[POS_IN_TORUS (pos - index, size)]) / 2;
      env_factor /= (gdouble) param->diffusion_dist;
      by_env = ENV_FACTOR (env_factor) + (CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2;
      diff = param->mod_rate * (self_diff + by_env);
      val = hold_rate * vec[pos] + diff;
      break;
    case MULTIPLY_RANDOM0:
    case MULTIPLY_RANDOM1:
    case MULTIPLY_GRADIENT:
      {
        gdouble tmp;

        tmp = power;
        power = param->power;
        self_diff = self_mod_rate * LOGISTICS (vec[pos]);
        for (index = 1; index <= param->diffusion_dist / 2; index++)
          env_factor += ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)])
            + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)]);
        if ((param->diffusion_dist % 2) == 1)
          env_factor += (ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)])
                         + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)])) / 2;
        env_factor /= (gdouble) param->diffusion_dist;
        by_env = (env_factor + CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2;
        diff = pow (param->mod_rate * (self_diff + by_env), tmp);
        val = hold_rate * vec[pos] + diff;
        break;
      }
    case STANDARD:
    case RAND_POWER0:
    case RAND_POWER1:
    case RAND_POWER2:
    case RAND_AND_P:
    default:
      self_diff = self_mod_rate * LOGISTICS (vec[pos]);

      for (index = 1; index <= param->diffusion_dist / 2; index++)
        env_factor += ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)])
          + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)]);
      if ((param->diffusion_dist % 2) == 1)
        env_factor += (ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)])
                       + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)])) / 2;
      env_factor /= (gdouble) param->diffusion_dist;
      by_env = env_factor + (CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2;
      diff = param->mod_rate * (self_diff + by_env);
      val = hold_rate * vec[pos] + diff;
      break;
    }
  /* finalize */
  if (g_rand_double (gr) < param->mutation_rate)
    {
      val += ((g_rand_double (gr) < 0.5) ? -1.0 : 1.0) * param->mutation_dist * g_rand_double (gr);
    }
  if (param->cyclic_range)
    {
      if (1.0 < val)
        val = val - (int) val;
      else if (val < 0.0)
        val = val - floor (val);
    }
  else
    /* The range of val should be [0,1], not [0,1).
      Cannonization shuold be done in color mapping phase. */

    val = CLAMP (val, 0.0, 1);

  return val;
}
#undef LOGISTICS
#undef ENV_FACTOR
#undef CHN_FACTOR


static gdouble
logistic_function (CML_PARAM *param,
                   gdouble    x,
                   gdouble    power)
{
  gdouble x1 = x;
  gdouble result = 0;
  gint n = param->range_num;
  gint step;

  step = (int) (x * (gdouble) n);
  x1 = (x - ((gdouble) step / (gdouble) n)) * n;
  switch (param->function)
    {
    case CML_KEEP_VALUES:
    case CML_KEEP_FIRST:
      result = x;
      return result;
      break;
    case CML_FILL:
      result = CLAMP (param->parameter_k, 0.0, 1.0);
      return result;
      break;
    case CML_LOGIST:
      result = param->parameter_k * pow (4 * x1 * (1.0 - x1), power);
      break;
    case CML_LOGIST_STEP:
      result = param->parameter_k * pow (4 * x1 * (1.0 - x1), power);
      result = (result + step) / (gdouble) n;
      break;
    case CML_POWER:
      result = param->parameter_k * pow (x1, power);
      break;
    case CML_POWER_STEP:
      result = param->parameter_k * pow (x1, power);
      result = (result + step) / (gdouble) n;
      break;
    case CML_REV_POWER:
      result = param->parameter_k * (1 - pow (x1, power));
      break;
    case CML_REV_POWER_STEP:
      result = param->parameter_k * (1 - pow (x1, power));
      result = (result + step) / (gdouble) n;
      break;
    case CML_DELTA:
      result = param->parameter_k * 2 * ((x1 < 0.5) ? x1 : (1.0 - x1));
      break;
    case CML_DELTA_STEP:
      result = param->parameter_k * 2 * ((x1 < 0.5) ? x1 : (1.0 - x1));
      result = (result + step) / (gdouble) n;
      break;
    case CML_SIN_CURVE:
      if (1.0 < power)
        result = 0.5 * (sin (G_PI * ABS (x1 - 0.5) / power) / sin (G_PI * 0.5 / power) + 1);
      else
        result = 0.5 * (pow (sin (G_PI * ABS (x1 - 0.5)), power) + 1);
      if (x1 < 0.5) result = 1 - result;
      break;
    case CML_SIN_CURVE_STEP:
      if (1.0 < power)
        result = 0.5 * (sin (G_PI * ABS (x1 - 0.5) / power) / sin (G_PI * 0.5 / power) + 1);
      else
        result = 0.5 * (pow (sin (G_PI * ABS (x1 - 0.5)), power) + 1);
      if (x1 < 0.5) result = 1 - result;
      result = (result + step) / (gdouble) n;
      break;
    }
  switch (param->composition)
    {
    case COMP_NONE:
      break;
    case COMP_MAX_LINEAR:
      result = MAX ((gdouble) x, (gdouble) result);
      break;
    case COMP_MAX_LINEAR_P1:
      result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MAX_LINEAR_P1L:
      if (x < 0.5)
        result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MAX_LINEAR_P1U:
      if (0.5 < x)
        result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MAX_LINEAR_M1:
      result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MAX_LINEAR_M1L:
      if (x < 0.5)
        result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MAX_LINEAR_M1U:
      if (0.5 < x)
        result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MIN_LINEAR:
      result = MIN ((gdouble) x, (gdouble) result);
      break;
    case COMP_MIN_LINEAR_P1:
      result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MIN_LINEAR_P1L:
      if (x < 0.5)
        result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MIN_LINEAR_P1U:
      if (0.5 < x)
        result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MIN_LINEAR_M1:
      result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MIN_LINEAR_M1L:
      if (x < 0.5)
        result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    case COMP_MIN_LINEAR_M1U:
      if (0.5 < x)
        result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
      break;
    }
  return result;
}

/* dialog stuff */
static gint
CML_explorer_dialog (GimpProcedureConfig *config)
{
  GtkWidget *dialog;
  GtkWidget *hbox;
  GtkWidget *vbox;
  GtkWidget *frame;
  GtkWidget *bbox;
  GtkWidget *button;
  gboolean   run;

  gimp_ui_init (PLUG_IN_BINARY);

  dialog = gimp_dialog_new (_("Coupled-Map-Lattice Explorer"), PLUG_IN_ROLE,
                            NULL, 0,
                            gimp_standard_help_func, PLUG_IN_PROC,

                            _("_Cancel"), GTK_RESPONSE_CANCEL,
                            _("_OK"),     GTK_RESPONSE_OK,

                            NULL);

  gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);

  gimp_window_set_transient (GTK_WINDOW (dialog));

  CML_preview_defer = TRUE;

  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
                      hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
  gtk_widget_show (vbox);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_widget_set_halign (frame, GTK_ALIGN_CENTER);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);

  preview = gimp_preview_area_new ();
  gtk_widget_set_size_request (preview,
                               PREVIEW_WIDTH, PREVIEW_HEIGHT);
  gtk_container_add (GTK_CONTAINER (frame), preview);
  gtk_widget_show (preview);

  bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
  gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
  gtk_widget_show (bbox);

  button = gtk_button_new_with_label (_("New Seed"));
  gtk_container_add (GTK_CONTAINER (bbox), button);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (CML_preview_update_callback),
                    &VALS);

  random_sensitives[0].widget = button;
  random_sensitives[0].logic  = TRUE;

  button = gtk_button_new_with_label (_("Fix Seed"));
  gtk_container_add (GTK_CONTAINER (bbox), button);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (CML_set_or_randomize_seed_callback),
                    &VALS);

  random_sensitives[1].widget = button;
  random_sensitives[1].logic  = TRUE;

  button = gtk_button_new_with_label (_("Random Seed"));
  gtk_container_add (GTK_CONTAINER (bbox), button);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (CML_set_or_randomize_seed_callback),
                    &VALS);

  random_sensitives[2].widget = button;
  random_sensitives[2].logic  = FALSE;

  bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
  gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
  gtk_widget_show (bbox);

  button = gtk_button_new_with_mnemonic (_("_Open"));
  gtk_container_add (GTK_CONTAINER (bbox), button);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (CML_load_from_file_callback),
                    config);

  button = gtk_button_new_with_mnemonic (_("_Save"));
  gtk_container_add (GTK_CONTAINER (bbox), button);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (CML_save_to_file_callback),
                    config);

  {
    GtkWidget *notebook;
    GtkWidget *page;

    notebook = gtk_notebook_new ();
    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
    gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
    gtk_widget_show (notebook);

    page = CML_dialog_channel_panel_new (&VALS.hue, 0);
    gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page,
                              gtk_label_new_with_mnemonic (_("_Hue")));

    page = CML_dialog_channel_panel_new (&VALS.sat, 1);
    gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page,
                              gtk_label_new_with_mnemonic (_("Sat_uration")));

    page = CML_dialog_channel_panel_new (&VALS.val, 2);
    gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page,
                              gtk_label_new_with_mnemonic (_("_Value")));

    page = CML_dialog_advanced_panel_new ();
    gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page,
                              gtk_label_new_with_mnemonic (_("_Advanced")));

    {
      GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
      GtkWidget    *grid;
      GtkWidget    *label;
      GtkWidget    *combo;
      GtkWidget    *frame;
      GtkWidget    *vbox;
      GtkWidget    *scale;

      vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
      gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
      gtk_widget_show (vbox);

      frame = gimp_frame_new (_("Channel Independent Parameters"));
      gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
      gtk_widget_show (frame);

      grid = gtk_grid_new ();
      gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
      gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
      gtk_container_add (GTK_CONTAINER (frame), grid);
      gtk_widget_show (grid);

      combo = gimp_int_combo_box_new_array (CML_INITIAL_NUM_VALUES,
                                            initial_value_names);
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
                                     VALS.initial_value);

      g_signal_connect (combo, "changed",
                        G_CALLBACK (CML_initial_value_menu_update),
                        &VALS.initial_value);

      CML_explorer_menu_entry_init (&widget_pointers[3][0],
                                    combo, &VALS.initial_value);
      label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 0,
                                        _("Initial value:"), 0.0, 0.5,
                                        combo, 2);
      gtk_size_group_add_widget (group, label);
      g_object_unref (group);

      scale = gimp_scale_entry_new (_("Zoom scale:"), VALS.scale, 1, 10, 0);
      gtk_size_group_add_widget (group, gimp_labeled_get_label (GIMP_LABELED (scale)));
      CML_explorer_int_entry_init (&widget_pointers[3][1],
                                   scale, &VALS.scale);
      gtk_grid_attach (GTK_GRID (grid), scale, 0, 1, 3, 1);
      gtk_widget_show (scale);

      scale = gimp_scale_entry_new (_("Start offset:"), VALS.start_offset, 0, 100, 0);
      gtk_size_group_add_widget (group, gimp_labeled_get_label (GIMP_LABELED (scale)));
      CML_explorer_int_entry_init (&widget_pointers[3][2],
                                   scale, &VALS.start_offset);
      gtk_grid_attach (GTK_GRID (grid), scale, 0, 2, 3, 1);
      gtk_widget_show (scale);

      frame =
        gimp_frame_new (_("Seed of Random (only for \"From Seed\" Modes)"));
      gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
      gtk_widget_show (frame);

      grid = gtk_grid_new ();
      gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
      gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
      gtk_container_add (GTK_CONTAINER (frame), grid);
      gtk_widget_show (grid);

      scale = gimp_scale_entry_new (_("Seed:"), VALS.seed, 0, (guint32) -1, 0);
      gtk_size_group_add_widget (group, gimp_labeled_get_label (GIMP_LABELED (scale)));
      CML_explorer_int_entry_init (&widget_pointers[3][3],
                                   scale, &VALS.seed);
      gtk_grid_attach (GTK_GRID (grid), scale, 0, 0, 3, 1);
      gtk_widget_show (scale);

      random_sensitives[3].widget = grid;
      random_sensitives[3].logic  = FALSE;

      button =
        gtk_button_new_with_label
        (_("Switch to \"From seed\" With the Last Seed"));
      gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 3, 1);
      gtk_widget_show (button);

      g_signal_connect (button, "clicked",
                        G_CALLBACK (CML_set_or_randomize_seed_callback),
                        &VALS);

      random_sensitives[4].widget = button;
      random_sensitives[4].logic  = TRUE;

      gimp_help_set_help_data (button,
                               _("\"Fix seed\" button is an alias of me.\n"
                                 "The same seed produces the same image, "
                                 "if (1) the widths of images are same "
                                 "(this is the reason why image on drawable "
                                 "is different from preview), and (2) all "
                                 "mutation rates equal to zero."), NULL);

      gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
                                gtk_label_new_with_mnemonic (_("O_thers")));
    }

    {
      GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
      GtkWidget    *grid;
      GtkWidget    *frame;
      GtkWidget    *label;
      GtkWidget    *combo;
      GtkWidget    *vbox;

      vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
      gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
      gtk_widget_show (vbox);

      frame = gimp_frame_new (_("Copy Settings"));
      gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
      gtk_widget_show (frame);

      grid = gtk_grid_new ();
      gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
      gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
      gtk_container_add (GTK_CONTAINER (frame), grid);
      gtk_widget_show (grid);

      combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (channel_names),
                                            channel_names);
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), copy_source);

      g_signal_connect (combo, "changed",
                        G_CALLBACK (gimp_int_combo_box_get_active),
                        ©_source);

      label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 0,
                                        _("Source channel:"), 0.0, 0.5,
                                        combo, 1);
      gtk_size_group_add_widget (group, label);
      g_object_unref (group);

      combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (channel_names),
                                            channel_names);
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
                                     copy_destination);

      g_signal_connect (combo, "changed",
                        G_CALLBACK (gimp_int_combo_box_get_active),
                        ©_destination);

      label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
                                        _("Destination channel:"), 0.0, 0.5,
                                        combo, 1);
      gtk_size_group_add_widget (group, label);

      button = gtk_button_new_with_label (_("Copy Parameters"));
      gtk_grid_attach (GTK_GRID (grid), button, 0, 2, 2, 1);
      gtk_widget_show (button);

      g_signal_connect (button, "clicked",
                        G_CALLBACK (CML_copy_parameters_callback),
                        &VALS);

      frame = gimp_frame_new (_("Selective Load Settings"));
      gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
      gtk_widget_show (frame);

      grid = gtk_grid_new ();
      gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
      gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
      gtk_container_add (GTK_CONTAINER (frame), grid);
      gtk_widget_show (grid);

      combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (load_channel_names),
                                            load_channel_names);
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
                                     selective_load_source);

      g_signal_connect (combo, "changed",
                        G_CALLBACK (gimp_int_combo_box_get_active),
                        &selective_load_source);

      label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 0,
                                        _("Source channel in file:"),
                                        0.0, 0.5,
                                        combo, 1);
      gtk_size_group_add_widget (group, label);

      combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (load_channel_names),
                                            load_channel_names);
      gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
                                     selective_load_destination);

      g_signal_connect (combo, "changed",
                        G_CALLBACK (gimp_int_combo_box_get_active),
                        &selective_load_destination);

      label = gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1,
                                        _("Destination channel:"),
                                        0.0, 0.5,
                                        combo, 1);
      gtk_size_group_add_widget (group, label);

      gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
                                gtk_label_new_with_mnemonic (_("_Misc")));
    }
  }

  CML_initial_value_sensitives_update ();

  gtk_widget_show (dialog);

  img_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, GRAPHSIZE);
  img = g_malloc0 (img_stride * GRAPHSIZE);

  buffer = cairo_image_surface_create_for_data (img, CAIRO_FORMAT_RGB24,
                                                GRAPHSIZE,
                                                GRAPHSIZE,
                                                img_stride);

  /*  Displaying preview might takes a long time. Thus, first, dialog itself
   *  should be shown before making preview in it.
   */

  CML_preview_defer = FALSE;
  preview_update ();

  run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);

  gtk_widget_destroy (dialog);
  g_free (img);
  cairo_surface_destroy (buffer);

  return run;
}

static GtkWidget *
CML_dialog_channel_panel_new (CML_PARAM *param,
                              gint       channel_id)
{
  GtkWidget *grid;
  GtkWidget *combo;
  GtkWidget *toggle;
  GtkWidget *button;
  GtkWidget *scale;
  gpointer  *chank;
  gint       index = 0;

  grid = gtk_grid_new ();
  gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
  gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
  gtk_widget_show (grid);

  combo = gimp_int_combo_box_new_array (CML_NUM_VALUES, function_names);
  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), param->function);

  g_signal_connect (combo, "changed",
                    G_CALLBACK (CML_explorer_menu_update),
                    ¶m->function);

  CML_explorer_menu_entry_init (&widget_pointers[channel_id][index],
                                combo, ¶m->function);
  gimp_grid_attach_aligned (GTK_GRID (grid), 0, index,
                            _("Function type:"), 0.0, 0.5,
                            combo, 2);
  index++;

  combo = gimp_int_combo_box_new_array (COMP_NUM_VALUES, composition_names);

  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
                                 param->composition);

  g_signal_connect (combo, "changed",
                    G_CALLBACK (CML_explorer_menu_update),
                    ¶m->composition);

  CML_explorer_menu_entry_init (&widget_pointers[channel_id][index],
                                combo, ¶m->composition);
  gimp_grid_attach_aligned (GTK_GRID (grid), 0, index,
                            _("Composition:"), 0.0, 0.5,
                            combo, 2);
  index++;

  combo = gimp_int_combo_box_new_array (ARRANGE_NUM_VALUES, arrange_names);
  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), param->arrange);

  g_signal_connect (combo, "changed",
                    G_CALLBACK (CML_explorer_menu_update),
                    ¶m->arrange);

  CML_explorer_menu_entry_init (&widget_pointers[channel_id][index],
                                combo, ¶m->arrange);
  gimp_grid_attach_aligned (GTK_GRID (grid), 0, index,
                            _("Misc arrange:"), 0.0, 0.5,
                            combo, 2);
  index++;

  toggle = gtk_check_button_new_with_label (_("Use cyclic range"));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
                                param->cyclic_range);
  gtk_grid_attach (GTK_GRID (grid), toggle, 0, index, 3, 1);
  CML_explorer_toggle_entry_init (&widget_pointers[channel_id][index],
                                  toggle, ¶m->cyclic_range);
  gtk_widget_show (toggle);
  index++;

  scale = gimp_scale_entry_new (_("Modification rate:"), param->mod_rate, 0.0, 1.0, 2);
  CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
                                  scale, ¶m->mod_rate);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
  gtk_widget_show (scale);
  index++;

  scale = gimp_scale_entry_new (_("Environment sensitivity:"), param->env_sensitivity, 0.0, 1.0, 2);
  CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
                                  scale, ¶m->env_sensitivity);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
  gtk_widget_show (scale);
  index++;

  scale = gimp_scale_entry_new (_("Diffusion distance:"), param->diffusion_dist, 2, 10, 0);
  CML_explorer_int_entry_init (&widget_pointers[channel_id][index],
                               scale, ¶m->diffusion_dist);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
  gtk_widget_show (scale);
  index++;

  scale = gimp_scale_entry_new (_("# of subranges:"), param->range_num, 1, 10, 0);
  CML_explorer_int_entry_init (&widget_pointers[channel_id][index],
                               scale, ¶m->range_num);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
  gtk_widget_show (scale);
  index++;

  scale = gimp_scale_entry_new (_("P(ower factor):"), param->power, 0.0, 10.0, 2);
  gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scale), 0.1, 1.0);
  CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
                                  scale, ¶m->power);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
  gtk_widget_show (scale);
  index++;

  scale = gimp_scale_entry_new (_("Parameter k:"), param->parameter_k, 0.0, 10.0, 2);
  gimp_label_spin_set_increments (GIMP_LABEL_SPIN (scale), 0.1, 1.0);
  CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
                                  scale, ¶m->parameter_k);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
  gtk_widget_show (scale);
  index++;

  scale = gimp_scale_entry_new (_("Range low:"), param->range_l, 0.0, 1.0, 2);
  CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
                                  scale, ¶m->range_l);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
  gtk_widget_show (scale);
  index++;

  scale = gimp_scale_entry_new (_("Range high:"), param->range_h, 0.0, 1.0, 2);
  CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
                                  scale, ¶m->range_h);
  gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
  gtk_widget_show (scale);
  index++;

  chank = g_new (gpointer, 2);
  chank[0] = GINT_TO_POINTER (channel_id);
  chank[1] = param;

  button = gtk_button_new_with_label (_("Plot a Graph of the Settings"));
  gtk_grid_attach (GTK_GRID (grid), button, 0, index, 3, 1);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (function_graph_new),
                    chank);
  return grid;
}

static GtkWidget *
CML_dialog_advanced_panel_new (void)
{
  GtkWidget *vbox;
  GtkWidget *subframe;
  GtkWidget *grid;
  GtkWidget *scale;
  gint       index = 0;
  gint       widget_offset = 12;
  gint       channel_id;
  CML_PARAM *param;

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
  gtk_widget_show (vbox);

  for (channel_id = 0; channel_id < 3; channel_id++)
    {
      param = (CML_PARAM *)&VALS + channel_id;

      subframe = gimp_frame_new (gettext (channel_names[channel_id]));
      gtk_box_pack_start (GTK_BOX (vbox), subframe, FALSE, FALSE, 0);
      gtk_widget_show (subframe);

      grid = gtk_grid_new ();
      gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
      gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
      gtk_container_add (GTK_CONTAINER (subframe), grid);
      gtk_widget_show (grid);

      index = 0;

      scale = gimp_scale_entry_new (_("Channel sensitivity:"), param->ch_sensitivity, 0.0, 1.0, 2);
      CML_explorer_double_entry_init (&widget_pointers[channel_id][index +
                                                                  widget_offset],
                                      scale, ¶m->ch_sensitivity);
      gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
      gtk_widget_show (scale);
      index++;

      scale = gimp_scale_entry_new (_("Mutation rate:"), param->mutation_rate, 0.0, 1.0, 2);
      CML_explorer_double_entry_init (&widget_pointers[channel_id][index +
                                                                  widget_offset],
                                      scale, ¶m->mutation_rate);
      gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
      gtk_widget_show (scale);
      index++;

      scale = gimp_scale_entry_new (_("Mutation distance:"), param->mutation_dist, 0.0, 1.0, 2);
      CML_explorer_double_entry_init (&widget_pointers[channel_id][index +
                                                                  widget_offset],
                                      scale, ¶m->mutation_dist);
      gtk_grid_attach (GTK_GRID (grid), scale, 0, index, 3, 1);
      gtk_widget_show (scale);
    }
  return vbox;
}

static void
preview_update (void)
{
  if (! CML_preview_defer)
    CML_main_function (TRUE);
}

static gboolean
function_graph_draw (GtkWidget *widget,
                     cairo_t   *cr,
                     gpointer  *data)
{
  gint        x, y;
  gint        rgbi[3];
  gfloat      hsv[3];
  gfloat      rgb[3];
  gint        channel_id = GPOINTER_TO_INT (data[0]);
  CML_PARAM  *param      = data[1];
  const Babl *fish;

  fish = babl_fish (babl_format ("HSV float"), babl_format ("R'G'B' float"));

  cairo_set_line_width (cr, 1.0);

  cairo_surface_flush (buffer);

  for (x = 0; x < GRAPHSIZE; x++)
    {
      /* hue */
      rgbi[0] = rgbi[1] = rgbi[2] = 127;
      if ((0 <= channel_id) && (channel_id <= 2))
        rgbi[channel_id] = CANNONIZE ((*param), ((gdouble) x / (gdouble) 255));

      hsv[0] = (gdouble) rgbi[0] / 255.0;
      hsv[1] = (gdouble) rgbi[1] / 255.0;
      hsv[2] = (gdouble) rgbi[2] / 255.0;

      babl_process (fish, hsv, rgb, 1);

      for (y = 0; y < GRAPHSIZE; y++)
        {
          GIMP_CAIRO_RGB24_SET_PIXEL((img+(y*img_stride+x*4)),
                                     ROUND (rgb[0] * 255.0),
                                     ROUND (rgb[1] * 255.0),
                                     ROUND (rgb[2] * 255.0));
        }
    }

  cairo_surface_mark_dirty (buffer);

  cairo_set_source_surface (cr, buffer, 0.0, 0.0);

  cairo_paint (cr);
  cairo_translate (cr, 0.5, 0.5);

  cairo_move_to (cr, 0, 255);
  cairo_line_to (cr, 255, 0);
  cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
  cairo_stroke (cr);

  y = 255 * CLAMP (logistic_function (param, 0, param->power),
                     0, 1.0);
  cairo_move_to (cr, 0, 255-y);
  for (x = 0; x < GRAPHSIZE; x++)
    {
      /* curve */
      y = 255 * CLAMP (logistic_function (param, x/(gdouble)255, param->power),
                       0, 1.0);
      cairo_line_to (cr, x, 255-y);
    }

  cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
  cairo_stroke (cr);

  return TRUE;
}

static void
function_graph_new (GtkWidget *widget,
                    gpointer  *data)
{
  GtkWidget *dialog;
  GtkWidget *frame;
  GtkWidget *preview;

  dialog = gimp_dialog_new (_("Graph of the Current Settings"), PLUG_IN_ROLE,
                            gtk_widget_get_toplevel (widget), 0,
                            gimp_standard_help_func, PLUG_IN_PROC,

                            _("_Close"), GTK_RESPONSE_CLOSE,

                            NULL);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
                      frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);

  preview = gtk_drawing_area_new ();
  gtk_widget_set_size_request (preview, GRAPHSIZE, GRAPHSIZE);
  gtk_container_add (GTK_CONTAINER (frame), preview);
  gtk_widget_show (preview);
  g_signal_connect (preview, "draw",
                    G_CALLBACK (function_graph_draw),
                    data);

  gtk_widget_show (dialog);

  gimp_dialog_run (GIMP_DIALOG (dialog));

  gtk_widget_destroy (dialog);
}

static void
CML_set_or_randomize_seed_callback (GtkWidget *widget,
                                    gpointer   data)
{
  CML_preview_defer = TRUE;

  switch (VALS.initial_value)
    {
    case CML_INITIAL_RANDOM_INDEPENDENT:
      VALS.initial_value = CML_INITIAL_RANDOM_FROM_SEED;
      break;
    case CML_INITIAL_RANDOM_SHARED:
      VALS.initial_value = CML_INITIAL_RANDOM_FROM_SEED_SHARED;
      break;
    case CML_INITIAL_RANDOM_FROM_SEED:
      VALS.initial_value = CML_INITIAL_RANDOM_INDEPENDENT;
      break;
    case CML_INITIAL_RANDOM_FROM_SEED_SHARED:
      VALS.initial_value = CML_INITIAL_RANDOM_SHARED;
      break;
    default:
      break;
    }
  if (widget_pointers[3][3].widget && widget_pointers[3][3].updater)
    (widget_pointers[3][3].updater) (widget_pointers[3]+3);
  if (widget_pointers[3][0].widget && widget_pointers[3][0].updater)
    (widget_pointers[3][0].updater) (widget_pointers[3]);

  CML_initial_value_sensitives_update ();

  CML_preview_defer = FALSE;
}

static void
CML_copy_parameters_callback (GtkWidget *widget,
                              gpointer   data)
{
  gint index;
  WidgetEntry *widgets;

  if (copy_source == copy_destination)
    {
      g_message (_("Warning: the source and the destination are the same channel."));
      return;
    }
  *channel_params[copy_destination] = *channel_params[copy_source];
  CML_preview_defer = TRUE;
  widgets = widget_pointers[copy_destination];

  for (index = 0; index < CML_PARAM_NUM; index++)
    if (widgets[index].widget && widgets[index].updater)
      (widgets[index].updater) (widgets + index);

  CML_preview_defer = FALSE;
  preview_update ();
}

static void
CML_initial_value_sensitives_update (void)
{
  gint  i = 0;
  gint  flag1, flag2;

  flag1 = (CML_INITIAL_RANDOM_INDEPENDENT <= VALS.initial_value)
    & (VALS.initial_value <= CML_INITIAL_RANDOM_FROM_SEED_SHARED);
  flag2 = (CML_INITIAL_RANDOM_INDEPENDENT <= VALS.initial_value)
    & (VALS.initial_value <= CML_INITIAL_RANDOM_SHARED);

  for (; i < G_N_ELEMENTS (random_sensitives) ; i++)
    if (random_sensitives[i].widget)
      gtk_widget_set_sensitive (random_sensitives[i].widget,
                                flag1 & (random_sensitives[i].logic == flag2));
}

static void
CML_preview_update_callback (GtkWidget *widget,
                             gpointer   data)
{
  WidgetEntry seed_widget = widget_pointers[3][3];

  preview_update ();

  CML_preview_defer = TRUE;

  if (seed_widget.widget && seed_widget.updater)
    seed_widget.updater (&seed_widget);

  CML_preview_defer = FALSE;
}

/*  parameter file saving functions  */

static void
CML_save_to_file_callback (GtkWidget           *widget,
                           GimpProcedureConfig *config)
{
  static GtkWidget *dialog = NULL;
  GFile            *file   = NULL;

  if (! dialog)
    {
      dialog =
        gtk_file_chooser_dialog_new (_("Save CML Explorer Parameters"),
                                     GTK_WINDOW (gtk_widget_get_toplevel (widget)),
                                     GTK_FILE_CHOOSER_ACTION_SAVE,

                                     _("_Cancel"), GTK_RESPONSE_CANCEL,
                                     _("_Save"),   GTK_RESPONSE_OK,

                                     NULL);

      gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                               GTK_RESPONSE_OK,
                                               GTK_RESPONSE_CANCEL,
                                               -1);
      gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);

      gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
                                                      TRUE);

      g_signal_connect (dialog, "response",
                        G_CALLBACK (CML_save_to_file_response),
                        config);
      g_signal_connect (dialog, "delete-event",
                        G_CALLBACK (gtk_true),
                        NULL);
    }

  g_object_get (config, "parameter-file", &file, NULL);
  if (file != NULL)
    {
      GError *error = NULL;

      gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), file, &error);
      if (error != NULL)
        g_printerr ("%s: failed to set file '%s'.\n", PLUG_IN_PROC,
                    g_file_peek_path (file));
      g_clear_error (&error);
    }
  g_clear_object (&file);

  gtk_window_present (GTK_WINDOW (dialog));
}

static void
CML_save_to_file_response (GtkWidget           *dialog,
                           gint                 response_id,
                           GimpProcedureConfig *config)
{
  GFile             *file;
  GFileOutputStream *fstream;
  GOutputStream     *output;
  GError            *error = NULL;
  gint               channel_id;

  if (response_id != GTK_RESPONSE_OK)
    {
      gtk_widget_hide (dialog);
      return;
    }

  file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
  if (! file)
    return;

  fstream = g_file_replace (file, NULL, FALSE,
                            G_FILE_CREATE_REPLACE_DESTINATION,
                            NULL, &error);
  output  = G_OUTPUT_STREAM (fstream);

  if (error != NULL)
    {
      g_message (_("Could not open '%s' for writing: %s"),
                 g_file_peek_path (file), error->message);
      g_object_unref (file);
      g_clear_error (&error);
      return;
    }

  if (! g_output_stream_printf (output, NULL, NULL, &error,
                                "; This is a parameter file for CML_explorer\n") ||
      ! g_output_stream_printf (output, NULL, NULL, &error,
                                "; File format version: %1.1f\n", PARAM_FILE_FORMAT_VERSION) ||
      ! g_output_stream_printf (output, NULL, NULL, &error, ";\n"))
    {
      g_message (_("Could not write to '%s': %s"),
                 g_file_peek_path (file), error->message);
      g_object_unref (file);
      g_clear_error (&error);
      g_object_unref (fstream);
      return;
    }

  for (channel_id = 0; channel_id < 3; channel_id++)
    {
      gchar buf[G_ASCII_DTOSTR_BUF_SIZE];

      CML_PARAM param = *(CML_PARAM *)(channel_params[channel_id]);

      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "\t%s\n", channel_names[channel_id]))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Function_type    : %d (%s)\n",
                                    param.function, function_names[param.function]))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Compostion_type  : %d (%s)\n",
                                    param.composition, composition_names[param.composition]))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Arrange          : %d (%s)\n",
                                    param.arrange, arrange_names[param.arrange]))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Cyclic_range     : %d (%s)\n",
                                    param.cyclic_range, (param.cyclic_range ? "TRUE" : "FALSE")))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Mod. rate        : %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.mod_rate)))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Env_sensitivtiy  : %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.env_sensitivity)))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Diffusion dist.  : %d\n", param.diffusion_dist))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Ch. sensitivity  : %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.ch_sensitivity)))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Num. of Subranges: %d\n", param.range_num))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Power_factor     : %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.power)))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Parameter_k      : %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.parameter_k)))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Range_low        : %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.range_l)))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Range_high       : %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.range_h)))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Mutation_rate    : %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.mutation_rate)))
        break;
      if (! g_output_stream_printf (output, NULL, NULL, &error,
                                    "Mutation_distance: %s\n",
                                    g_ascii_dtostr (buf, sizeof (buf), param.mutation_dist)))
        break;
    }

  if (error != NULL)
    {
      g_message (_("Could not write to '%s': %s"),
                 g_file_peek_path (file), error->message);
      g_object_unref (file);
      g_clear_error (&error);
      g_object_unref (fstream);
      return;
    }

  if (! g_output_stream_printf (output, NULL, NULL, &error, "\n") ||
      ! g_output_stream_printf (output, NULL, NULL, &error,
                                "Initial value  : %d (%s)\n", VALS.initial_value, initial_value_names[VALS.initial_value]) ||
      ! g_output_stream_printf (output, NULL, NULL, &error,
                                "Zoom scale     : %d\n", VALS.scale) ||
      ! g_output_stream_printf (output, NULL, NULL, &error,
                                "Start offset   : %d\n", VALS.start_offset) ||
      ! g_output_stream_printf (output, NULL, NULL, &error,
                                "Random seed    : %d\n", VALS.seed))
    {
      g_message (_("Could not write to '%s': %s"),
                 g_file_peek_path (file), error->message);
      g_object_unref (file);
      g_clear_error (&error);
      g_object_unref (fstream);
      return;
    }
  g_object_unref (fstream);

  g_message (_("Parameters were saved to '%s'"), g_file_peek_path (file));

  g_object_set (config, "parameter-file", file, NULL);

  g_clear_object (&file);

  gtk_widget_hide (dialog);
}

/*  parameter file loading functions  */

static void
CML_load_from_file_callback (GtkWidget           *widget,
                             GimpProcedureConfig *config)
{
  static GtkWidget *dialog = NULL;
  GFile            *file   = NULL;

  if (! dialog)
    {
      dialog =
        gtk_file_chooser_dialog_new (_("Load CML Explorer Parameters"),
                                     GTK_WINDOW (gtk_widget_get_toplevel (widget)),
                                     GTK_FILE_CHOOSER_ACTION_OPEN,

                                     _("_Cancel"), GTK_RESPONSE_CANCEL,
                                     _("_Open"),   GTK_RESPONSE_OK,

                                     NULL);

      gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                               GTK_RESPONSE_OK,
                                               GTK_RESPONSE_CANCEL,
                                               -1);

      gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);

      g_signal_connect (dialog, "response",
                        G_CALLBACK (CML_load_from_file_response),
                        config);
      g_signal_connect (dialog, "delete-event",
                        G_CALLBACK (gtk_true),
                        NULL);
    }

  g_object_get (config, "parameter-file", &file, NULL);
  if (file != NULL)
    {
      GError *error = NULL;

      gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), file, &error);
      if (error != NULL)
        g_printerr ("%s: failed to set file '%s'.\n", PLUG_IN_PROC,
                    g_file_peek_path (file));
      g_clear_error (&error);
    }
  g_clear_object (&file);

  gtk_window_present (GTK_WINDOW (dialog));
}

static void
CML_load_from_file_response (GtkWidget           *dialog,
                             gint                 response_id,
                             GimpProcedureConfig *config)
{
  if (response_id == GTK_RESPONSE_OK)
    {
      GFile    *file = NULL;
      gint      channel_id;
      gboolean  flag = TRUE;
      GError   *error = NULL;

      file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));

      flag = CML_load_parameter_file (file, TRUE, config, &error);

      if (error != NULL)
        g_printerr ("%s: loading file '%s' failed: %s\n", PLUG_IN_PROC,
                    g_file_peek_path (file), error->message);

      g_clear_error (&error);
      g_clear_object (&file);

      if (flag)
        {
          WidgetEntry *widgets;
          gint         index;

          CML_preview_defer = TRUE;

          for (channel_id = 0; channel_id < 3; channel_id++)
            {
              widgets = widget_pointers[channel_id];
              for (index = 0; index < CML_PARAM_NUM; index++)
                if (widgets[index].widget && widgets[index].updater)
                  (widgets[index].updater) (widgets + index);
            }
          /* channel independent parameters */
          widgets = widget_pointers[3];
          for (index = 0; index < 4; index++)
            if (widgets[index].widget && widgets[index].updater)
              (widgets[index].updater) (widgets + index);

          CML_preview_defer = FALSE;

          preview_update ();
        }
    }

  gtk_widget_hide (GTK_WIDGET (dialog));
}

static gboolean
CML_load_parameter_file (GFile                *file,
                         gboolean              interactive_mode,
                         GimpProcedureConfig  *config,
                         GError              **error)
{
  GIOStream        *iostream;
  GDataInputStream *input;
  gint              channel_id;
  gboolean          flag = TRUE;
  CML_PARAM         ch[3];
  gint              initial_value = 0;
  gint              scale = 1;
  gint              start_offset = 0;
  gint              seed = 0;
  gint              old2new_function_id[] = { 3, 4, 5, 6, 7, 9, 10, 11, 1, 2 };
  gdouble           version = 0.99;

  iostream = G_IO_STREAM (g_file_open_readwrite (file, NULL, error));

  if (iostream == NULL)
    return FALSE;

  input = g_data_input_stream_new (g_io_stream_get_input_stream (iostream));

  version = parse_line_to_gdouble (input, &flag, error); /* old format returns 1 */
  if (version == 1.0)
    {
      version = 0.99;
    }
  else if (! flag)
    {
      flag = TRUE;
      g_clear_error (error);
      version = parse_line_to_gdouble (input, &flag, error); /* maybe new format */
      if (flag)
        /* one more comment line */
        g_free (g_data_input_stream_read_line (input, NULL, NULL, NULL));
    }
  if (version == 0)
    {
      if (interactive_mode)
        g_message (_("Error: \"%s\" is not a CML parameter file."), g_file_peek_path (file));
      g_object_unref (iostream);
      return FALSE;
    }
  if (interactive_mode)
    {
      if (version < PARAM_FILE_FORMAT_VERSION)
        g_message (_("Warning: '%s' is an old format file."),
                   g_file_peek_path (file));

      if (PARAM_FILE_FORMAT_VERSION < version)
        g_message (_("Warning: '%s' is a parameter file for a newer "
                     "version of CML Explorer."),
                   g_file_peek_path (file));
    }
  for (channel_id = 0; flag && (channel_id < 3); channel_id++)
    {
      gchar *line;

      /* patched by Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu> */
      /* skip channel name */
      line = g_data_input_stream_read_line (input, NULL, NULL, NULL);
      if (line == NULL)
        {
          flag = FALSE;
          break;
        }
      ch[channel_id].function = parse_line_to_gint (input, &flag, error);
      if (version < 1.0)
        ch[channel_id].function = old2new_function_id [ch[channel_id].function];
      if (1.0 <= version)
        ch[channel_id].composition = parse_line_to_gint (input, &flag, error);
      else
        ch[channel_id].composition = COMP_NONE;
      ch[channel_id].arrange = parse_line_to_gint (input, &flag, error);
      ch[channel_id].cyclic_range = parse_line_to_gint (input, &flag, error);
      ch[channel_id].mod_rate = parse_line_to_gdouble (input, &flag, error);
      ch[channel_id].env_sensitivity = parse_line_to_gdouble (input, &flag, error);
      if (1.0 <= version)
        ch[channel_id].diffusion_dist = parse_line_to_gint (input, &flag, error);
      else
        ch[channel_id].diffusion_dist = 2;
      ch[channel_id].ch_sensitivity = parse_line_to_gdouble (input, &flag, error);
      ch[channel_id].range_num = parse_line_to_gint (input, &flag, error);
      ch[channel_id].power = parse_line_to_gdouble (input, &flag, error);
      ch[channel_id].parameter_k = parse_line_to_gdouble (input, &flag, error);
      ch[channel_id].range_l = parse_line_to_gdouble (input, &flag, error);
      ch[channel_id].range_h = parse_line_to_gdouble (input, &flag, error);
      ch[channel_id].mutation_rate = parse_line_to_gdouble (input, &flag, error);
      ch[channel_id].mutation_dist = parse_line_to_gdouble (input, &flag, error);
    }
  if (flag)
    {
      gchar    *line;
      gboolean  dummy = TRUE;

      /* skip a line */
      line = g_data_input_stream_read_line (input, NULL, NULL, NULL);
      if (line == NULL)
        {
          dummy = FALSE;
        }
      else
        {
          g_free (line);
          initial_value = parse_line_to_gint (input, &dummy, error);
          scale = parse_line_to_gint (input, &dummy, error);
          start_offset = parse_line_to_gint (input, &dummy, error);
          seed = parse_line_to_gint (input, &dummy, error);
        }
      if (! dummy)
        {
          initial_value = 0;
          scale = 1;
          start_offset = 0;
          seed = 0;
        }
    }

  if (! flag)
    {
      if (interactive_mode)
        gimp_message (_("Error: failed to load parameters"));
    }
  else
    {
      if ((selective_load_source == 0) || (selective_load_destination == 0))
        {
          VALS.hue = ch[0];
          VALS.sat = ch[1];
          VALS.val = ch[2];

          VALS.initial_value = initial_value;
          VALS.scale = scale;
          VALS.start_offset = start_offset;
          VALS.seed = seed;
        }
      else
        {
          memcpy ((CML_PARAM *)&VALS + (selective_load_destination - 1),
                  (void *)&ch[selective_load_source - 1],
                  sizeof (CML_PARAM));
        }

      g_object_set (config, "parameter-file", file, NULL);
    }

  g_object_unref (input);
  g_object_unref (iostream);
  return flag;
}

static gint
parse_line_to_gint (GDataInputStream  *input,
                    gboolean          *flag,
                    GError           **error)
{
  gint   i;
  gchar *line;
  gchar *str;
  gsize  read;

  if (! *flag)
    return 0;

  line = g_data_input_stream_read_line (input, &read, NULL, error);
  if (line == NULL)
    {
      /* set FALSE if fail to parse */
      *flag = FALSE;
      return 0.0;
    }

  line[read] = '\0';
  str = line;
  while (read > 1 && *str != ':')
    {
      str++;
      read--;
    }

  if (read == 1)
    {
      g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                   "%s: invalid line in the CML file: %s",
                   PLUG_IN_PROC, line);
      *flag = FALSE;
      g_free (line);
      return 0.0;
    }

  i = atoi (str + 1);
  g_free (line);

  return i;
}

static gdouble
parse_line_to_gdouble (GDataInputStream  *input,
                       gboolean          *flag,
                       GError           **error)
{
  gdouble  d;
  gchar   *line;
  gchar   *str;
  gsize    read;

  if (! *flag)
    return 0.0;

  line = g_data_input_stream_read_line (input, &read, NULL, error);

  if (line == NULL)
    {
      /* set FALSE if fail to parse */
      *flag = FALSE;
      return 0.0;
    }

  line[read] = '\0';
  str = line;
  while (read > 1 && *str != ':')
    {
      str++;
      read--;
    }

  if (read == 1)
    {
      g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
                   "%s: invalid line in the CML file: %s",
                   PLUG_IN_PROC, line);
      *flag = FALSE;
      g_free (line);
      return 0.0;
    }

  d = g_ascii_strtod (str + 1, NULL);
  g_free (line);

  return d;
}


/*  toggle button functions  */

static void
CML_explorer_toggle_update (GtkWidget *widget,
                            gpointer   data)
{
  gimp_toggle_button_update (widget, data);

  preview_update ();
}

static void
CML_explorer_toggle_entry_change_value (WidgetEntry *widget_entry)
{
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget_entry->widget),
                                *(gint *) (widget_entry->value));
}

static void
CML_explorer_toggle_entry_init (WidgetEntry *widget_entry,
                                GtkWidget   *widget,
                                gpointer     value_ptr)
{
  g_signal_connect (widget, "toggled",
                    G_CALLBACK (CML_explorer_toggle_update),
                    value_ptr);

  widget_entry->widget  = widget;
  widget_entry->value   = value_ptr;
  widget_entry->updater = CML_explorer_toggle_entry_change_value;
}

/*  int adjustment functions  */

static void
CML_explorer_int_adjustment_update (GimpLabelSpin *scale,
                                    gint          *value)
{
  *value = (gint) gimp_label_spin_get_value (scale);

  preview_update ();
}

static void
CML_explorer_int_entry_change_value (WidgetEntry *widget_entry)
{
  GimpLabelSpin *scale = GIMP_LABEL_SPIN (widget_entry->widget);

  gimp_label_spin_set_value (scale, *(gint *) (widget_entry->value));
}

static void
CML_explorer_int_entry_init (WidgetEntry *widget_entry,
                             GtkWidget   *scale,
                             gpointer     value_ptr)
{
  g_signal_connect (scale, "value-changed",
                    G_CALLBACK (CML_explorer_int_adjustment_update),
                    value_ptr);

  widget_entry->widget  = scale;
  widget_entry->value   = value_ptr;
  widget_entry->updater = CML_explorer_int_entry_change_value;
}

/*  double adjustment functions  */

static void
CML_explorer_double_adjustment_update (GimpLabelSpin *scale,
                                       gdouble       *value)
{
  *value = gimp_label_spin_get_value (scale);

  preview_update ();
}

static void
CML_explorer_double_entry_change_value (WidgetEntry *widget_entry)
{
  GimpLabelSpin *scale = GIMP_LABEL_SPIN (widget_entry->widget);

  gimp_label_spin_set_value (scale, *(gdouble *) (widget_entry->value));
}

static void
CML_explorer_double_entry_init (WidgetEntry *widget_entry,
                                GtkWidget   *scale,
                                gpointer     value_ptr)
{
  g_signal_connect (scale, "value-changed",
                    G_CALLBACK (CML_explorer_double_adjustment_update),
                    value_ptr);

  widget_entry->widget  = scale;
  widget_entry->value   = value_ptr;
  widget_entry->updater = CML_explorer_double_entry_change_value;
}

/*  menu functions  */

static void
CML_explorer_menu_update (GtkWidget *widget,
                          gpointer   data)
{
  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);

  preview_update ();
}

static void
CML_initial_value_menu_update (GtkWidget *widget,
                               gpointer   data)
{
  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);

  CML_initial_value_sensitives_update ();
  preview_update ();
}

static void
CML_explorer_menu_entry_change_value (WidgetEntry *widget_entry)
{
  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget_entry->widget),
                                 *(gint *) (widget_entry->value));
}

static void
CML_explorer_menu_entry_init (WidgetEntry *widget_entry,
                              GtkWidget   *widget,
                              gpointer     value_ptr)
{
  widget_entry->widget  = widget;
  widget_entry->value   = value_ptr;
  widget_entry->updater = CML_explorer_menu_entry_change_value;
}

Messung
CHZG
839710093

¤ Dauer der Verarbeitung: 0.44 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104852
#Domains=417162