1 /* Plug-in to load and export .gih (GIMP Brush Pipe) files.
2  *
3  * Copyright (C) 1999 Tor Lillqvist
4  * Copyright (C) 2000 Jens Lautenbacher, Sven Neumann
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20   /* Example of how to call file_gih_save from script-fu:
21 
22   (let ((ranks (cons-array 1 'byte)))
23     (aset ranks 0 12)
24     (file-gih-save 1
25                    img
26                    drawable
27                    "foo.gih"
28                    "foo.gih"
29                    100
30                    "test brush"
31                    125
32                    125
33                    3
34                    4
35                    1
36                    ranks
37                    1
38                    '("random")))
39   */
40 
41 
42 #include "config.h"
43 
44 #include <libgimp/gimp.h>
45 #include <libgimp/gimpui.h>
46 #include <libgimpbase/gimpparasiteio.h>
47 
48 #include "libgimp/stdplugins-intl.h"
49 
50 
51 #define SAVE_PROC      "file-gih-save"
52 #define PLUG_IN_BINARY "file-gih"
53 #define PLUG_IN_ROLE   "gimp-file-gih"
54 
55 
56 /* Parameters applicable each time we export a gih, exported in the
57  * main gimp application between invocations of this plug-in.
58  */
59 typedef struct
60 {
61   gchar description[256];
62   gint  spacing;
63 } BrushInfo;
64 
65 typedef struct
66 {
67   GimpOrientationType  orientation;
68   gint32               image;
69   gint32               toplayer;
70   gint                 nguides;
71   gint32              *guides;
72   gint                *value;
73   GtkWidget           *count_label;    /* Corresponding count adjustment, */
74   gint                *count;          /* cols or rows                    */
75   gint                *other_count;    /* and the other one               */
76   GtkAdjustment       *ncells;
77   GtkAdjustment       *rank0;
78   GtkWidget           *warning_label;
79   GtkWidget           *rank_entry[GIMP_PIXPIPE_MAXDIM];
80   GtkWidget           *mode_entry[GIMP_PIXPIPE_MAXDIM];
81 } SizeAdjustmentData;
82 
83 
84 /*  local function prototypes  */
85 
86 static void      query           (void);
87 static void      run             (const gchar      *name,
88                                   gint              nparams,
89                                   const GimpParam  *param,
90                                   gint             *nreturn_vals,
91                                   GimpParam       **return_vals);
92 
93 static gboolean  gih_save_dialog (gint32            image_ID);
94 
95 
96 /*  private variables  */
97 
98 const GimpPlugInInfo PLUG_IN_INFO =
99 {
100   NULL,  /* init_proc  */
101   NULL,  /* quit_proc  */
102   query, /* query_proc */
103   run,   /* run_proc   */
104 };
105 
106 static BrushInfo info =
107 {
108   "GIMP Brush Pipe",
109   20
110 };
111 
112 static gint              num_layers = 0;
113 static GimpPixPipeParams gihparams  = { 0, };
114 
115 static const gchar * const selection_modes[] = { "incremental",
116                                                  "angular",
117                                                  "random",
118                                                  "velocity",
119                                                  "pressure",
120                                                  "xtilt",
121                                                  "ytilt" };
122 
123 
MAIN()124 MAIN ()
125 
126 static void
127 query (void)
128 {
129   static const GimpParamDef gih_save_args[] =
130   {
131     { GIMP_PDB_INT32,       "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
132     { GIMP_PDB_IMAGE,       "image",        "Input image" },
133     { GIMP_PDB_DRAWABLE,    "drawable",     "Drawable to export" },
134     { GIMP_PDB_STRING,      "uri",          "The URI of the file to export the brush pipe in" },
135     { GIMP_PDB_STRING,      "raw-uri",      "The URI of the file to export the brush pipe in" },
136     { GIMP_PDB_INT32,       "spacing",      "Spacing of the brush" },
137     { GIMP_PDB_STRING,      "description",  "Short description of the brush pipe" },
138     { GIMP_PDB_INT32,       "cell-width",   "Width of the brush cells" },
139     { GIMP_PDB_INT32,       "cell-height",  "Width of the brush cells" },
140     { GIMP_PDB_INT8,        "display-cols", "Display column number" },
141     { GIMP_PDB_INT8,        "display-rows", "Display row number" },
142     { GIMP_PDB_INT32,       "dimension",    "Dimension of the brush pipe" },
143     /* The number of rank and sel args depend on the dimension */
144     { GIMP_PDB_INT8ARRAY,   "rank",         "Ranks of the dimensions" },
145     { GIMP_PDB_INT32,       "dimension",    "Dimension (again)" },
146     { GIMP_PDB_STRINGARRAY, "sel",          "Selection modes" }
147   };
148 
149   gimp_install_procedure (SAVE_PROC,
150                           "exports images in GIMP brush pipe format",
151                           "This plug-in exports an image in the GIMP brush pipe "
152                           "format. For a colored brush pipe, RGBA layers are "
153                           "used, otherwise the layers should be grayscale "
154                           "masks. The image can be multi-layered, and "
155                           "additionally the layers can be divided into a "
156                           "rectangular array of brushes.",
157                           "Tor Lillqvist",
158                           "Tor Lillqvist",
159                           "1999",
160                           N_("GIMP brush (animated)"),
161                           "RGB*, GRAY*",
162                           GIMP_PLUGIN,
163                           G_N_ELEMENTS (gih_save_args), 0,
164                           gih_save_args, NULL);
165 
166   gimp_plugin_icon_register (SAVE_PROC, GIMP_ICON_TYPE_ICON_NAME,
167                              (const guint8 *) GIMP_ICON_BRUSH);
168   gimp_register_file_handler_mime (SAVE_PROC, "image/x-gimp-gih");
169   gimp_register_file_handler_uri (SAVE_PROC);
170   gimp_register_save_handler (SAVE_PROC, "gih", "");
171 }
172 
173 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)174 run (const gchar      *name,
175      gint              nparams,
176      const GimpParam  *param,
177      gint             *nreturn_vals,
178      GimpParam       **return_vals)
179 {
180   static GimpParam   values[2];
181   GimpRunMode        run_mode;
182   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
183   gint32             image_ID;
184   gint32             drawable_ID;
185   GimpExportReturn   export = GIMP_EXPORT_CANCEL;
186   GError            *error  = NULL;
187   gint               i;
188 
189   INIT_I18N();
190 
191   run_mode = param[0].data.d_int32;
192 
193   *return_vals  = values;
194   *nreturn_vals = 1;
195 
196   values[0].type          = GIMP_PDB_STATUS;
197   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
198 
199   if (strcmp (name, SAVE_PROC) == 0)
200     {
201       GFile        *file;
202       GimpParasite *parasite;
203       gint32        orig_image_ID;
204 
205       image_ID    = param[1].data.d_int32;
206       drawable_ID = param[2].data.d_int32;
207       file        = g_file_new_for_uri (param[3].data.d_string);
208 
209       orig_image_ID = image_ID;
210 
211       switch (run_mode)
212         {
213         case GIMP_RUN_INTERACTIVE:
214         case GIMP_RUN_WITH_LAST_VALS:
215           gimp_ui_init (PLUG_IN_BINARY, FALSE);
216 
217           export = gimp_export_image (&image_ID, &drawable_ID, "GIH",
218                                       GIMP_EXPORT_CAN_HANDLE_RGB   |
219                                       GIMP_EXPORT_CAN_HANDLE_GRAY  |
220                                       GIMP_EXPORT_CAN_HANDLE_ALPHA |
221                                       GIMP_EXPORT_CAN_HANDLE_LAYERS);
222 
223           if (export == GIMP_EXPORT_CANCEL)
224             {
225               values[0].data.d_status = GIMP_PDB_CANCEL;
226               return;
227             }
228 
229           /*  Possibly retrieve data  */
230           gimp_get_data (SAVE_PROC, &info);
231 
232           parasite = gimp_image_get_parasite (orig_image_ID,
233                                               "gimp-brush-pipe-name");
234           if (parasite)
235             {
236               strncpy (info.description,
237                        gimp_parasite_data (parasite),
238                        MIN (sizeof (info.description),
239                             gimp_parasite_data_size (parasite)));
240               info.description[sizeof (info.description) - 1] = '\0';
241 
242               gimp_parasite_free (parasite);
243             }
244           else
245             {
246               gchar *name = g_path_get_basename (gimp_file_get_utf8_name (file));
247 
248               if (g_str_has_suffix (name, ".gih"))
249                 name[strlen (name) - 4] = '\0';
250 
251               if (strlen (name))
252                 {
253                   strncpy (info.description, name, sizeof (info.description));
254                   info.description[sizeof (info.description) - 1] = '\0';
255                 }
256 
257               g_free (name);
258             }
259 
260           parasite = gimp_image_get_parasite (orig_image_ID,
261                                               "gimp-brush-pipe-spacing");
262           if (parasite)
263             {
264               info.spacing = atoi (gimp_parasite_data (parasite));
265               gimp_parasite_free (parasite);
266             }
267           break;
268 
269         default:
270           break;
271         }
272 
273       g_free (gimp_image_get_layers (image_ID, &num_layers));
274 
275       gimp_pixpipe_params_init (&gihparams);
276 
277       switch (run_mode)
278         {
279         case GIMP_RUN_INTERACTIVE:
280           gihparams.ncells = (num_layers * gihparams.rows * gihparams.cols);
281 
282           gihparams.cellwidth  = gimp_image_width (image_ID)  / gihparams.cols;
283           gihparams.cellheight = gimp_image_height (image_ID) / gihparams.rows;
284 
285           parasite = gimp_image_get_parasite (orig_image_ID,
286                                               "gimp-brush-pipe-parameters");
287           if (parasite)
288             {
289               gimp_pixpipe_params_parse (gimp_parasite_data (parasite),
290                                          &gihparams);
291               gimp_parasite_free (parasite);
292             }
293 
294           /* Force default rank to same as number of cells if there is
295            * just one dim
296            */
297           if (gihparams.dim == 1)
298             gihparams.rank[0] = gihparams.ncells;
299 
300           if (! gih_save_dialog (image_ID))
301             status = GIMP_PDB_CANCEL;
302           break;
303 
304         case GIMP_RUN_NONINTERACTIVE:
305           if (nparams != 15)
306             {
307               status = GIMP_PDB_CALLING_ERROR;
308             }
309           else
310             {
311               info.spacing = param[5].data.d_int32;
312               strncpy (info.description, param[6].data.d_string,
313                        sizeof (info.description));
314               info.description[sizeof (info.description) - 1] = '\0';
315 
316               gihparams.cellwidth  = param[7].data.d_int32;
317               gihparams.cellheight = param[8].data.d_int32;
318               gihparams.cols       = param[9].data.d_int8;
319               gihparams.rows       = param[10].data.d_int8;
320               gihparams.dim        = param[11].data.d_int32;
321               gihparams.ncells     = 1;
322 
323               if (param[13].data.d_int32 != gihparams.dim)
324                 {
325                   status = GIMP_PDB_CALLING_ERROR;
326                 }
327               else
328                 {
329                   for (i = 0; i < gihparams.dim; i++)
330                     {
331                       gihparams.rank[i]      = param[12].data.d_int8array[i];
332                       gihparams.selection[i] = g_strdup (param[14].data.d_stringarray[i]);
333                       gihparams.ncells       *= gihparams.rank[i];
334                     }
335                 }
336             }
337           break;
338 
339         case GIMP_RUN_WITH_LAST_VALS:
340           parasite = gimp_image_get_parasite (orig_image_ID,
341                                               "gimp-brush-pipe-parameters");
342           if (parasite)
343             {
344               gimp_pixpipe_params_parse (gimp_parasite_data (parasite),
345                                          &gihparams);
346               gimp_parasite_free (parasite);
347             }
348           break;
349         }
350 
351       if (status == GIMP_PDB_SUCCESS)
352         {
353           GimpParam *save_retvals;
354           gint       n_save_retvals;
355           gchar      spacing[8];
356           gchar     *paramstring;
357 
358           paramstring = gimp_pixpipe_params_build (&gihparams);
359 
360           save_retvals =
361             gimp_run_procedure ("file-gih-save-internal",
362                                 &n_save_retvals,
363                                 GIMP_PDB_INT32,    GIMP_RUN_NONINTERACTIVE,
364                                 GIMP_PDB_IMAGE,    image_ID,
365                                 GIMP_PDB_DRAWABLE, drawable_ID,
366                                 GIMP_PDB_STRING,   param[3].data.d_string,
367                                 GIMP_PDB_STRING,   param[4].data.d_string,
368                                 GIMP_PDB_INT32,    info.spacing,
369                                 GIMP_PDB_STRING,   info.description,
370                                 GIMP_PDB_STRING,   paramstring,
371                                 GIMP_PDB_END);
372 
373           if (save_retvals[0].data.d_status == GIMP_PDB_SUCCESS)
374             {
375               gimp_set_data (SAVE_PROC, &info, sizeof (info));
376 
377               parasite = gimp_parasite_new ("gimp-brush-pipe-name",
378                                             GIMP_PARASITE_PERSISTENT,
379                                             strlen (info.description) + 1,
380                                             info.description);
381               gimp_image_attach_parasite (orig_image_ID, parasite);
382               gimp_parasite_free (parasite);
383 
384               g_snprintf (spacing, sizeof (spacing), "%d",
385                           info.spacing);
386 
387               parasite = gimp_parasite_new ("gimp-brush-pipe-spacing",
388                                             GIMP_PARASITE_PERSISTENT,
389                                             strlen (spacing) + 1, spacing);
390               gimp_image_attach_parasite (orig_image_ID, parasite);
391               gimp_parasite_free (parasite);
392 
393               parasite = gimp_parasite_new ("gimp-brush-pipe-parameters",
394                                             GIMP_PARASITE_PERSISTENT,
395                                             strlen (paramstring) + 1,
396                                             paramstring);
397               gimp_image_attach_parasite (orig_image_ID, parasite);
398               gimp_parasite_free (parasite);
399             }
400           else
401             {
402               g_set_error (&error, 0, 0,
403                            "Running procedure 'file-gih-save-internal' "
404                            "failed: %s",
405                            gimp_get_pdb_error ());
406 
407               status = GIMP_PDB_EXECUTION_ERROR;
408             }
409 
410           g_free (paramstring);
411         }
412 
413       gimp_pixpipe_params_free (&gihparams);
414 
415       if (export == GIMP_EXPORT_EXPORT)
416         gimp_image_delete (image_ID);
417     }
418   else
419     {
420       status = GIMP_PDB_CALLING_ERROR;
421     }
422 
423   if (status != GIMP_PDB_SUCCESS && error)
424     {
425       *nreturn_vals = 2;
426       values[1].type          = GIMP_PDB_STRING;
427       values[1].data.d_string = error->message;
428     }
429 
430   values[0].data.d_status = status;
431 }
432 
433 
434 /*  save routines */
435 
436 static void
size_adjustment_callback(GtkWidget * widget,SizeAdjustmentData * adj)437 size_adjustment_callback (GtkWidget          *widget,
438                           SizeAdjustmentData *adj)
439 {
440   gint  i;
441   gint  size;
442   gint  newn;
443   gchar buf[10];
444 
445   for (i = 0; i < adj->nguides; i++)
446     gimp_image_delete_guide (adj->image, adj->guides[i]);
447 
448   g_free (adj->guides);
449   adj->guides = NULL;
450   gimp_displays_flush ();
451 
452   *(adj->value) = gtk_adjustment_get_value (GTK_ADJUSTMENT (widget));
453 
454   if (adj->orientation == GIMP_ORIENTATION_VERTICAL)
455     {
456       size = gimp_image_width (adj->image);
457       newn = size / *(adj->value);
458       adj->nguides = newn - 1;
459       adj->guides = g_new (gint32, adj->nguides);
460       for (i = 0; i < adj->nguides; i++)
461         adj->guides[i] = gimp_image_add_vguide (adj->image,
462                                                 *(adj->value) * (i+1));
463     }
464   else
465     {
466       size = gimp_image_height (adj->image);
467       newn = size / *(adj->value);
468       adj->nguides = newn - 1;
469       adj->guides = g_new (gint32, adj->nguides);
470       for (i = 0; i < adj->nguides; i++)
471         adj->guides[i] = gimp_image_add_hguide (adj->image,
472                                                 *(adj->value) * (i+1));
473     }
474   gimp_displays_flush ();
475   g_snprintf (buf, sizeof (buf), "%2d", newn);
476   gtk_label_set_text (GTK_LABEL (adj->count_label), buf);
477 
478   *(adj->count) = newn;
479 
480   gtk_widget_set_visible (GTK_WIDGET (adj->warning_label),
481                           newn * *(adj->value) != size);
482 
483   if (adj->ncells != NULL)
484     gtk_adjustment_set_value (GTK_ADJUSTMENT (adj->ncells),
485                               *(adj->other_count) * *(adj->count));
486   if (adj->rank0 != NULL)
487     gtk_adjustment_set_value (GTK_ADJUSTMENT (adj->rank0),
488                               *(adj->other_count) * *(adj->count));
489 }
490 
491 static void
entry_callback(GtkWidget * widget,gpointer data)492 entry_callback (GtkWidget *widget,
493                 gpointer   data)
494 {
495   if (data == info.description)
496     {
497       strncpy (info.description, gtk_entry_get_text (GTK_ENTRY (widget)),
498                sizeof (info.description));
499       info.description[sizeof (info.description) - 1]  = 0;
500     }
501 }
502 
503 static void
cb_callback(GtkWidget * widget,gpointer data)504 cb_callback (GtkWidget *widget,
505              gpointer   data)
506 {
507   gint index;
508 
509   index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
510 
511   *((const gchar **) data) = selection_modes [index];
512 }
513 
514 static void
dim_callback(GtkAdjustment * adjustment,SizeAdjustmentData * data)515 dim_callback (GtkAdjustment      *adjustment,
516               SizeAdjustmentData *data)
517 {
518   gint i;
519 
520   gihparams.dim = RINT (gtk_adjustment_get_value (adjustment));
521 
522   for (i = 0; i < GIMP_PIXPIPE_MAXDIM; i++)
523     {
524       gtk_widget_set_sensitive (data->rank_entry[i], i < gihparams.dim);
525       gtk_widget_set_sensitive (data->mode_entry[i], i < gihparams.dim);
526     }
527 }
528 
529 static gboolean
gih_save_dialog(gint32 image_ID)530 gih_save_dialog (gint32 image_ID)
531 {
532   GtkWidget          *dialog;
533   GtkWidget          *table;
534   GtkWidget          *dimtable;
535   GtkWidget          *label;
536   GtkAdjustment      *adjustment;
537   GtkWidget          *spinbutton;
538   GtkWidget          *entry;
539   GtkWidget          *box;
540   GtkWidget          *cb;
541   gint                i;
542   gchar               buffer[100];
543   SizeAdjustmentData  cellw_adjust;
544   SizeAdjustmentData  cellh_adjust;
545   gint32             *layer_ID;
546   gint32              nlayers;
547   gboolean            run;
548 
549   dialog = gimp_export_dialog_new (_("Brush Pipe"), PLUG_IN_BINARY, SAVE_PROC);
550 
551   /* The main table */
552   table = gtk_table_new (8, 2, FALSE);
553   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
554   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
555   gtk_container_set_border_width (GTK_CONTAINER (table), 12);
556   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
557                       table, TRUE, TRUE, 0);
558   gtk_widget_show (table);
559 
560   /*
561    * Description: ___________
562    */
563   entry = gtk_entry_new ();
564   gtk_widget_set_size_request (entry, 200, -1);
565   gtk_entry_set_text (GTK_ENTRY (entry), info.description);
566   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
567                              _("_Description:"), 0.0, 0.5,
568                              entry, 1, FALSE);
569 
570   g_signal_connect (entry, "changed",
571                     G_CALLBACK (entry_callback),
572                     info.description);
573 
574   /*
575    * Spacing: __
576    */
577   adjustment = (GtkAdjustment *) gtk_adjustment_new (info.spacing,
578                                                      1, 1000, 1, 10, 0);
579   spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
580   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
581   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
582                              _("_Spacing (percent):"), 0.0, 0.5,
583                              spinbutton, 1, TRUE);
584 
585   g_signal_connect (adjustment, "value-changed",
586                     G_CALLBACK (gimp_int_adjustment_update),
587                     &info.spacing);
588 
589   /*
590    * Cell size: __ x __ pixels
591    */
592   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
593 
594   adjustment = (GtkAdjustment *)
595     gtk_adjustment_new (gihparams.cellwidth,
596                         2, gimp_image_width (image_ID), 1, 10, 0);
597   spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
598   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
599   gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
600   gtk_widget_show (spinbutton);
601 
602   layer_ID = gimp_image_get_layers (image_ID, &nlayers);
603   cellw_adjust.orientation = GIMP_ORIENTATION_VERTICAL;
604   cellw_adjust.image       = image_ID;
605   cellw_adjust.toplayer    = layer_ID[nlayers-1];
606   cellw_adjust.nguides     = 0;
607   cellw_adjust.guides      = NULL;
608   cellw_adjust.value       = &gihparams.cellwidth;
609 
610   g_signal_connect (adjustment, "value-changed",
611                     G_CALLBACK (size_adjustment_callback),
612                     &cellw_adjust);
613 
614   label = gtk_label_new ("x");
615   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
616   gtk_widget_show (label);
617 
618   adjustment = (GtkAdjustment *)
619     gtk_adjustment_new (gihparams.cellheight,
620                         2, gimp_image_height (image_ID), 1, 10, 0);
621   spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
622   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
623   gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
624   gtk_widget_show (spinbutton);
625 
626   cellh_adjust.orientation = GIMP_ORIENTATION_HORIZONTAL;
627   cellh_adjust.image       = image_ID;
628   cellh_adjust.toplayer    = layer_ID[nlayers-1];
629   cellh_adjust.nguides     = 0;
630   cellh_adjust.guides      = NULL;
631   cellh_adjust.value       = &gihparams.cellheight;
632 
633   g_signal_connect (adjustment, "value-changed",
634                     G_CALLBACK (size_adjustment_callback),
635                     &cellh_adjust);
636 
637   label = gtk_label_new ( _("Pixels"));
638   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
639   gtk_widget_show (label);
640 
641   gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
642                              _("Ce_ll size:"), 0.0, 0.5,
643                              box, 1, FALSE);
644 
645   g_free (layer_ID);
646 
647   /*
648    * Number of cells: ___
649    */
650   adjustment = (GtkAdjustment *)
651     gtk_adjustment_new (gihparams.ncells, 1, 1000, 1, 10, 0);
652   spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
653   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
654   gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
655                              _("_Number of cells:"), 0.0, 0.5,
656                              spinbutton, 1, TRUE);
657 
658   g_signal_connect (adjustment, "value-changed",
659                     G_CALLBACK (gimp_int_adjustment_update),
660                     &gihparams.ncells);
661 
662   if (gihparams.dim == 1)
663     cellw_adjust.ncells = cellh_adjust.ncells = adjustment;
664   else
665     cellw_adjust.ncells = cellh_adjust.ncells = NULL;
666 
667   /*
668    * Display as: __ rows x __ cols
669    */
670   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
671 
672   g_snprintf (buffer, sizeof (buffer), "%2d", gihparams.rows);
673   label = gtk_label_new (buffer);
674   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
675   cellh_adjust.count_label = label;
676   cellh_adjust.count       = &gihparams.rows;
677   cellh_adjust.other_count = &gihparams.cols;
678   gtk_widget_show (label);
679 
680   label = gtk_label_new (_(" Rows of "));
681   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
682   gtk_widget_show (label);
683 
684   g_snprintf (buffer, sizeof (buffer), "%2d", gihparams.cols);
685   label = gtk_label_new (buffer);
686   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
687   cellw_adjust.count_label = label;
688   cellw_adjust.count       = &gihparams.cols;
689   cellw_adjust.other_count = &gihparams.rows;
690   gtk_widget_show (label);
691 
692   label = gtk_label_new (_(" Columns on each layer"));
693   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
694   gtk_widget_show (label);
695 
696   label = gtk_label_new (_(" (Width Mismatch!) "));
697   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
698   cellw_adjust.warning_label = label;
699 
700   label = gtk_label_new (_(" (Height Mismatch!) "));
701   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
702   cellh_adjust.warning_label = label;
703 
704   gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
705                              _("Display as:"), 0.0, 0.5,
706                              box, 1, FALSE);
707 
708   /*
709    * Dimension: ___
710    */
711   adjustment = (GtkAdjustment *)
712     gtk_adjustment_new (gihparams.dim, 1, GIMP_PIXPIPE_MAXDIM, 1, 1, 0);
713   spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
714   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
715   gimp_table_attach_aligned (GTK_TABLE (table), 0, 5,
716                              _("Di_mension:"), 0.0, 0.5,
717                              spinbutton, 1, TRUE);
718 
719   g_signal_connect (adjustment, "value-changed",
720                     G_CALLBACK (dim_callback),
721                     &cellw_adjust);
722 
723   /*
724    * Ranks / Selection: ______ ______ ______ ______ ______
725    */
726 
727   dimtable = gtk_table_new (2, GIMP_PIXPIPE_MAXDIM, FALSE);
728   gtk_table_set_col_spacings (GTK_TABLE (dimtable), 4);
729   for (i = 0; i < GIMP_PIXPIPE_MAXDIM; i++)
730     {
731       gint j;
732 
733       adjustment = (GtkAdjustment *)
734         gtk_adjustment_new (gihparams.rank[i], 1, 100, 1, 1, 0);
735       spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
736       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
737       gtk_table_attach (GTK_TABLE (dimtable), spinbutton, 0, 1, i, i + 1,
738                         GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
739 
740       gtk_widget_show (spinbutton);
741 
742       if (i >= gihparams.dim)
743         gtk_widget_set_sensitive (spinbutton, FALSE);
744 
745       g_signal_connect (adjustment, "value-changed",
746                         G_CALLBACK (gimp_int_adjustment_update),
747                         &gihparams.rank[i]);
748 
749       cellw_adjust.rank_entry[i] = cellh_adjust.rank_entry[i] = spinbutton;
750 
751       if (i == 0)
752         {
753           if (gihparams.dim == 1)
754             cellw_adjust.rank0 = cellh_adjust.rank0 = adjustment;
755           else
756             cellw_adjust.rank0 = cellh_adjust.rank0 = NULL;
757         }
758 
759       cb = gtk_combo_box_text_new ();
760 
761       for (j = 0; j < G_N_ELEMENTS (selection_modes); j++)
762         gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cb),
763                                         selection_modes[j]);
764       gtk_combo_box_set_active (GTK_COMBO_BOX (cb), 2);  /* random */
765 
766       if (gihparams.selection[i])
767         for (j = 0; j < G_N_ELEMENTS (selection_modes); j++)
768           if (!strcmp (gihparams.selection[i], selection_modes[j]))
769             {
770               gtk_combo_box_set_active (GTK_COMBO_BOX (cb), j);
771               break;
772             }
773 
774       gtk_table_attach (GTK_TABLE (dimtable), cb, 1, 2, i, i + 1,
775                         GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
776 
777       gtk_widget_show (cb);
778 
779       if (i >= gihparams.dim)
780         gtk_widget_set_sensitive (cb, FALSE);
781 
782       g_signal_connect (GTK_COMBO_BOX (cb), "changed",
783                         G_CALLBACK (cb_callback),
784                         &gihparams.selection[i]);
785 
786       cellw_adjust.mode_entry[i] = cellh_adjust.mode_entry[i] = cb;
787 
788 
789     }
790 
791   gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
792                              _("Ranks:"), 0.0, 0.0,
793                              dimtable, 1, FALSE);
794 
795   gtk_widget_show (dialog);
796 
797   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
798 
799   if (run)
800     {
801       gint i;
802 
803       for (i = 0; i < GIMP_PIXPIPE_MAXDIM; i++)
804         gihparams.selection[i] = g_strdup (gihparams.selection[i]);
805 
806       /* Fix up bogus values */
807       gihparams.ncells = MIN (gihparams.ncells,
808                               num_layers * gihparams.rows * gihparams.cols);
809     }
810 
811   gtk_widget_destroy (dialog);
812 
813   for (i = 0; i < cellw_adjust.nguides; i++)
814     gimp_image_delete_guide (image_ID, cellw_adjust.guides[i]);
815   for (i = 0; i < cellh_adjust.nguides; i++)
816     gimp_image_delete_guide (image_ID, cellh_adjust.guides[i]);
817 
818   return run;
819 }
820