1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 /* Original plug-in coded by Tim Newsome.
19 *
20 * Changed to make use of real-life units by Sven Neumann <sven@gimp.org>.
21 *
22 * The interface code is heavily commented in the hope that it will
23 * help other plug-in developers to adapt their plug-ins to make use
24 * of the gimp_size_entry functionality.
25 *
26 * Note: There is a convenience constructor called gimp_coordinetes_new ()
27 * which simplifies the task of setting up a standard X,Y sizeentry.
28 *
29 * For more info and bugs see libgimp/gimpsizeentry.h and libgimp/gimpwidgets.h
30 *
31 * May 2000 tim copperfield [timecop@japan.co.jp]
32 * http://www.ne.jp/asahi/linux/timecop
33 * Added dynamic preview. Due to weird implementation of signals from all
34 * controls, preview will not auto-update. But this plugin isn't really
35 * crying for real-time updating either.
36 *
37 */
38
39 #include "config.h"
40
41 #include <string.h>
42
43 #include <libgimp/gimp.h>
44 #include <libgimp/gimpui.h>
45
46 #include "libgimp/stdplugins-intl.h"
47
48
49 #define PLUG_IN_PROC "plug-in-grid"
50 #define PLUG_IN_BINARY "grid"
51 #define PLUG_IN_ROLE "gimp-grid"
52 #define SPIN_BUTTON_WIDTH 8
53 #define COLOR_BUTTON_WIDTH 55
54
55
56 /* Declare local functions. */
57 static void query (void);
58 static void run (const gchar *name,
59 gint nparams,
60 const GimpParam *param,
61 gint *nreturn_vals,
62 GimpParam **return_vals);
63
64 static guchar best_cmap_match (const guchar *cmap,
65 gint ncolors,
66 const GimpRGB *color);
67 static void grid (gint32 image_ID,
68 gint32 drawable_ID,
69 GimpPreview *preview);
70 static gint dialog (gint32 image_ID,
71 gint32 drawable_ID);
72
73 const GimpPlugInInfo PLUG_IN_INFO =
74 {
75 NULL, /* init_proc */
76 NULL, /* quit_proc */
77 query, /* query_proc */
78 run, /* run_proc */
79 };
80
81 static gint sx1, sy1, sx2, sy2;
82
83 static GtkWidget *main_dialog = NULL;
84 static GtkWidget *hcolor_button = NULL;
85 static GtkWidget *vcolor_button = NULL;
86
87 typedef struct
88 {
89 gint hwidth;
90 gint hspace;
91 gint hoffset;
92 GimpRGB hcolor;
93 gint vwidth;
94 gint vspace;
95 gint voffset;
96 GimpRGB vcolor;
97 gint iwidth;
98 gint ispace;
99 gint ioffset;
100 GimpRGB icolor;
101 } Config;
102
103 static Config grid_cfg =
104 {
105 1, 16, 8, { 0.0, 0.0, 0.0, 1.0 }, /* horizontal */
106 1, 16, 8, { 0.0, 0.0, 0.0, 1.0 }, /* vertical */
107 0, 2, 6, { 0.0, 0.0, 0.0, 1.0 }, /* intersection */
108 };
109
110
MAIN()111 MAIN ()
112
113 static
114 void query (void)
115 {
116 static const GimpParamDef args[] =
117 {
118 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
119 { GIMP_PDB_IMAGE, "image", "Input image" },
120 { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
121
122 { GIMP_PDB_INT32, "hwidth", "Horizontal Width (>= 0)" },
123 { GIMP_PDB_INT32, "hspace", "Horizontal Spacing (>= 1)" },
124 { GIMP_PDB_INT32, "hoffset", "Horizontal Offset (>= 0)" },
125 { GIMP_PDB_COLOR, "hcolor", "Horizontal Colour" },
126 { GIMP_PDB_INT8, "hopacity", "Horizontal Opacity (0...255)" },
127
128 { GIMP_PDB_INT32, "vwidth", "Vertical Width (>= 0)" },
129 { GIMP_PDB_INT32, "vspace", "Vertical Spacing (>= 1)" },
130 { GIMP_PDB_INT32, "voffset", "Vertical Offset (>= 0)" },
131 { GIMP_PDB_COLOR, "vcolor", "Vertical Colour" },
132 { GIMP_PDB_INT8, "vopacity", "Vertical Opacity (0...255)" },
133
134 { GIMP_PDB_INT32, "iwidth", "Intersection Width (>= 0)" },
135 { GIMP_PDB_INT32, "ispace", "Intersection Spacing (>= 0)" },
136 { GIMP_PDB_INT32, "ioffset", "Intersection Offset (>= 0)" },
137 { GIMP_PDB_COLOR, "icolor", "Intersection Colour" },
138 { GIMP_PDB_INT8, "iopacity", "Intersection Opacity (0...255)" }
139 };
140
141 gimp_install_procedure (PLUG_IN_PROC,
142 N_("Draw a grid on the image"),
143 "Draws a grid using the specified colors. "
144 "The grid origin is the upper left corner.",
145 "Tim Newsome",
146 "Tim Newsome, Sven Neumann, Tom Rathborne, TC",
147 "1997 - 2000",
148 N_("_Grid (legacy)..."),
149 "RGB*, GRAY*, INDEXED*",
150 GIMP_PLUGIN,
151 G_N_ELEMENTS (args), 0,
152 args, NULL);
153
154 gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Pattern");
155 }
156
157 static void
run(const gchar * name,gint n_params,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)158 run (const gchar *name,
159 gint n_params,
160 const GimpParam *param,
161 gint *nreturn_vals,
162 GimpParam **return_vals)
163 {
164 static GimpParam values[1];
165 gint32 image_ID;
166 gint32 drawable_ID;
167 GimpRunMode run_mode;
168 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
169
170 INIT_I18N ();
171 gegl_init (NULL, NULL);
172
173 *nreturn_vals = 1;
174 *return_vals = values;
175
176 run_mode = param[0].data.d_int32;
177 image_ID = param[1].data.d_int32;
178 drawable_ID = param[2].data.d_drawable;
179
180 if (run_mode == GIMP_RUN_NONINTERACTIVE)
181 {
182 if (n_params != 18)
183 status = GIMP_PDB_CALLING_ERROR;
184
185 if (status == GIMP_PDB_SUCCESS)
186 {
187 grid_cfg.hwidth = MAX (0, param[3].data.d_int32);
188 grid_cfg.hspace = MAX (1, param[4].data.d_int32);
189 grid_cfg.hoffset = MAX (0, param[5].data.d_int32);
190 grid_cfg.hcolor = param[6].data.d_color;
191
192 gimp_rgb_set_alpha (&(grid_cfg.hcolor),
193 ((double) param[7].data.d_int8) / 255.0);
194
195
196 grid_cfg.vwidth = MAX (0, param[8].data.d_int32);
197 grid_cfg.vspace = MAX (1, param[9].data.d_int32);
198 grid_cfg.voffset = MAX (0, param[10].data.d_int32);
199 grid_cfg.vcolor = param[11].data.d_color;
200
201 gimp_rgb_set_alpha (&(grid_cfg.vcolor),
202 ((double) param[12].data.d_int8) / 255.0);
203
204
205
206 grid_cfg.iwidth = MAX (0, param[13].data.d_int32);
207 grid_cfg.ispace = MAX (0, param[14].data.d_int32);
208 grid_cfg.ioffset = MAX (0, param[15].data.d_int32);
209 grid_cfg.icolor = param[16].data.d_color;
210
211 gimp_rgb_set_alpha (&(grid_cfg.icolor),
212 ((double) (guint) param[17].data.d_int8) / 255.0);
213
214
215 }
216 }
217 else
218 {
219 gimp_context_get_foreground (&grid_cfg.hcolor);
220 grid_cfg.vcolor = grid_cfg.icolor = grid_cfg.hcolor;
221
222 /* Possibly retrieve data */
223 gimp_get_data (PLUG_IN_PROC, &grid_cfg);
224 }
225
226 if (run_mode == GIMP_RUN_INTERACTIVE)
227 {
228 if (! dialog (image_ID, drawable_ID))
229 {
230 /* The dialog was closed, or something similarly evil happened. */
231 status = GIMP_PDB_EXECUTION_ERROR;
232 }
233 }
234
235 if (grid_cfg.hspace <= 0 || grid_cfg.vspace <= 0)
236 {
237 status = GIMP_PDB_EXECUTION_ERROR;
238 }
239
240 if (status == GIMP_PDB_SUCCESS)
241 {
242 gimp_progress_init (_("Drawing grid"));
243
244 grid (image_ID, drawable_ID, NULL);
245
246 if (run_mode != GIMP_RUN_NONINTERACTIVE)
247 gimp_displays_flush ();
248
249 if (run_mode == GIMP_RUN_INTERACTIVE)
250 gimp_set_data (PLUG_IN_PROC, &grid_cfg, sizeof (grid_cfg));
251 }
252
253 values[0].type = GIMP_PDB_STATUS;
254 values[0].data.d_status = status;
255 }
256
257
258 #define MAXDIFF 195076
259
260 static guchar
best_cmap_match(const guchar * cmap,gint ncolors,const GimpRGB * color)261 best_cmap_match (const guchar *cmap,
262 gint ncolors,
263 const GimpRGB *color)
264 {
265 guchar cmap_index = 0;
266 gint max = MAXDIFF;
267 gint i, diff, sum;
268 guchar r, g, b;
269
270 gimp_rgb_get_uchar (color, &r, &g, &b);
271
272 for (i = 0; i < ncolors; i++)
273 {
274 diff = r - *cmap++;
275 sum = SQR (diff);
276 diff = g - *cmap++;
277 sum += SQR (diff);
278 diff = b - *cmap++;
279 sum += SQR (diff);
280
281 if (sum < max)
282 {
283 cmap_index = i;
284 max = sum;
285 }
286 }
287
288 return cmap_index;
289 }
290
291 static inline void
pix_composite(guchar * p1,guchar p2[4],gint bytes,gboolean blend,gboolean alpha)292 pix_composite (guchar *p1,
293 guchar p2[4],
294 gint bytes,
295 gboolean blend,
296 gboolean alpha)
297 {
298 gint b;
299
300 if (blend)
301 {
302 if (alpha)
303 bytes--;
304
305 for (b = 0; b < bytes; b++)
306 {
307 *p1 = *p1 * (1.0 - p2[3]/255.0) + p2[b] * p2[3]/255.0;
308 p1++;
309 }
310 }
311 else
312 {
313 /* blend should only be TRUE for indexed (bytes == 1) */
314 *p1++ = *p2;
315 }
316
317 if (alpha && *p1 < 255)
318 {
319 b = *p1 + 255.0 * ((gdouble) p2[3] / (255.0 - *p1));
320
321 *p1 = b > 255 ? 255 : b;
322 }
323 }
324
325 static void
grid(gint32 image_ID,gint32 drawable_ID,GimpPreview * preview)326 grid (gint32 image_ID,
327 gint32 drawable_ID,
328 GimpPreview *preview)
329 {
330 GeglBuffer *src_buffer;
331 GeglBuffer *dest_buffer;
332 const Babl *format;
333 gint bytes;
334 gint x_offset;
335 gint y_offset;
336 guchar *dest;
337 guchar *buffer = NULL;
338 gint x, y;
339 gboolean alpha;
340 gboolean blend;
341 guchar hcolor[4];
342 guchar vcolor[4];
343 guchar icolor[4];
344 guchar *cmap;
345 gint ncolors;
346
347 gimp_rgba_get_uchar (&grid_cfg.hcolor,
348 hcolor, hcolor + 1, hcolor + 2, hcolor + 3);
349 gimp_rgba_get_uchar (&grid_cfg.vcolor,
350 vcolor, vcolor + 1, vcolor + 2, vcolor + 3);
351 gimp_rgba_get_uchar (&grid_cfg.icolor,
352 icolor, icolor + 1, icolor + 2, icolor + 3);
353
354 alpha = gimp_drawable_has_alpha (drawable_ID);
355
356 switch (gimp_image_base_type (image_ID))
357 {
358 case GIMP_RGB:
359 blend = TRUE;
360
361 if (alpha)
362 format = babl_format ("R'G'B'A u8");
363 else
364 format = babl_format ("R'G'B' u8");
365 break;
366
367 case GIMP_GRAY:
368 hcolor[0] = gimp_rgb_luminance_uchar (&grid_cfg.hcolor);
369 vcolor[0] = gimp_rgb_luminance_uchar (&grid_cfg.vcolor);
370 icolor[0] = gimp_rgb_luminance_uchar (&grid_cfg.icolor);
371 blend = TRUE;
372
373 if (alpha)
374 format = babl_format ("Y'A u8");
375 else
376 format = babl_format ("Y' u8");
377 break;
378
379 case GIMP_INDEXED:
380 cmap = gimp_image_get_colormap (image_ID, &ncolors);
381
382 hcolor[0] = best_cmap_match (cmap, ncolors, &grid_cfg.hcolor);
383 vcolor[0] = best_cmap_match (cmap, ncolors, &grid_cfg.vcolor);
384 icolor[0] = best_cmap_match (cmap, ncolors, &grid_cfg.icolor);
385
386 g_free (cmap);
387 blend = FALSE;
388
389 format = gimp_drawable_get_format (drawable_ID);
390 break;
391
392 default:
393 g_assert_not_reached ();
394 blend = FALSE;
395 }
396
397 bytes = babl_format_get_bytes_per_pixel (format);
398
399 if (preview)
400 {
401 gimp_preview_get_position (preview, &sx1, &sy1);
402 gimp_preview_get_size (preview, &sx2, &sy2);
403
404 buffer = g_new (guchar, bytes * sx2 * sy2);
405
406 sx2 += sx1;
407 sy2 += sy1;
408 }
409 else
410 {
411 gint w, h;
412
413 if (! gimp_drawable_mask_intersect (drawable_ID,
414 &sx1, &sy1, &w, &h))
415 return;
416
417 sx2 = sx1 + w;
418 sy2 = sy1 + h;
419
420 dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
421 }
422
423 src_buffer = gimp_drawable_get_buffer (drawable_ID);
424
425 dest = g_new (guchar, (sx2 - sx1) * bytes);
426
427 for (y = sy1; y < sy2; y++)
428 {
429 gegl_buffer_get (src_buffer, GEGL_RECTANGLE (sx1, y, sx2 - sx1, 1), 1.0,
430 format, dest,
431 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
432
433 y_offset = y - grid_cfg.hoffset;
434 while (y_offset < 0)
435 y_offset += grid_cfg.hspace;
436
437 if ((y_offset +
438 (grid_cfg.hwidth / 2)) % grid_cfg.hspace < grid_cfg.hwidth)
439 {
440 for (x = sx1; x < sx2; x++)
441 {
442 pix_composite (&dest[(x-sx1) * bytes],
443 hcolor, bytes, blend, alpha);
444 }
445 }
446
447 for (x = sx1; x < sx2; x++)
448 {
449 x_offset = grid_cfg.vspace + x - grid_cfg.voffset;
450 while (x_offset < 0)
451 x_offset += grid_cfg.vspace;
452
453 if ((x_offset +
454 (grid_cfg.vwidth / 2)) % grid_cfg.vspace < grid_cfg.vwidth)
455 {
456 pix_composite (&dest[(x-sx1) * bytes],
457 vcolor, bytes, blend, alpha);
458 }
459
460 if ((x_offset +
461 (grid_cfg.iwidth / 2)) % grid_cfg.vspace < grid_cfg.iwidth
462 &&
463 ((y_offset % grid_cfg.hspace >= grid_cfg.ispace
464 &&
465 y_offset % grid_cfg.hspace < grid_cfg.ioffset)
466 ||
467 (grid_cfg.hspace -
468 (y_offset % grid_cfg.hspace) >= grid_cfg.ispace
469 &&
470 grid_cfg.hspace -
471 (y_offset % grid_cfg.hspace) < grid_cfg.ioffset)))
472 {
473 pix_composite (&dest[(x-sx1) * bytes],
474 icolor, bytes, blend, alpha);
475 }
476 }
477
478 if ((y_offset +
479 (grid_cfg.iwidth / 2)) % grid_cfg.hspace < grid_cfg.iwidth)
480 {
481 for (x = sx1; x < sx2; x++)
482 {
483 x_offset = grid_cfg.vspace + x - grid_cfg.voffset;
484 while (x_offset < 0)
485 x_offset += grid_cfg.vspace;
486
487 if ((x_offset % grid_cfg.vspace >= grid_cfg.ispace
488 &&
489 x_offset % grid_cfg.vspace < grid_cfg.ioffset)
490 ||
491 (grid_cfg.vspace -
492 (x_offset % grid_cfg.vspace) >= grid_cfg.ispace
493 &&
494 grid_cfg.vspace -
495 (x_offset % grid_cfg.vspace) < grid_cfg.ioffset))
496 {
497 pix_composite (&dest[(x-sx1) * bytes],
498 icolor, bytes, blend, alpha);
499 }
500 }
501 }
502
503 if (preview)
504 {
505 memcpy (buffer + (y - sy1) * (sx2 - sx1) * bytes,
506 dest,
507 (sx2 - sx1) * bytes);
508 }
509 else
510 {
511 gegl_buffer_set (dest_buffer,
512 GEGL_RECTANGLE (sx1, y, sx2 - sx1, 1), 0,
513 format, dest,
514 GEGL_AUTO_ROWSTRIDE);
515
516 if (y % 16 == 0)
517 gimp_progress_update ((gdouble) y / (gdouble) (sy2 - sy1));
518 }
519 }
520
521 g_free (dest);
522
523 g_object_unref (src_buffer);
524
525 if (preview)
526 {
527 gimp_preview_draw_buffer (preview, buffer, bytes * (sx2 - sx1));
528 g_free (buffer);
529 }
530 else
531 {
532 gimp_progress_update (1.0);
533
534 g_object_unref (dest_buffer);
535
536 gimp_drawable_merge_shadow (drawable_ID, TRUE);
537 gimp_drawable_update (drawable_ID,
538 sx1, sy1, sx2 - sx1, sy2 - sy1);
539 }
540 }
541
542
543 /***************************************************
544 * GUI stuff
545 */
546
547
548 static void
update_values(void)549 update_values (void)
550 {
551 GtkWidget *entry;
552
553 entry = g_object_get_data (G_OBJECT (main_dialog), "width");
554
555 grid_cfg.hwidth =
556 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
557 grid_cfg.vwidth =
558 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
559 grid_cfg.iwidth =
560 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
561
562 entry = g_object_get_data (G_OBJECT (main_dialog), "space");
563
564 grid_cfg.hspace =
565 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
566 grid_cfg.vspace =
567 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
568 grid_cfg.ispace =
569 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
570
571 entry = g_object_get_data (G_OBJECT (main_dialog), "offset");
572
573 grid_cfg.hoffset =
574 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
575 grid_cfg.voffset =
576 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
577 grid_cfg.ioffset =
578 RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
579 }
580
581 static void
update_preview(GimpPreview * preview,gpointer drawable_ID)582 update_preview (GimpPreview *preview,
583 gpointer drawable_ID)
584 {
585 update_values ();
586
587 grid (gimp_item_get_image (GPOINTER_TO_INT (drawable_ID)),
588 GPOINTER_TO_INT (drawable_ID),
589 preview);
590 }
591
592 static void
entry_callback(GtkWidget * widget,gpointer data)593 entry_callback (GtkWidget *widget,
594 gpointer data)
595 {
596 static gdouble x = -1.0;
597 static gdouble y = -1.0;
598 gdouble new_x;
599 gdouble new_y;
600
601 new_x = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
602 new_y = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
603
604 if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (data)))
605 {
606 if (new_x != x)
607 {
608 y = new_y = x = new_x;
609 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1, y);
610 }
611 if (new_y != y)
612 {
613 x = new_x = y = new_y;
614 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0, x);
615 }
616 }
617 else
618 {
619 x = new_x;
620 y = new_y;
621 }
622 }
623
624 static void
color_callback(GtkWidget * widget,gpointer data)625 color_callback (GtkWidget *widget,
626 gpointer data)
627 {
628 if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (data)))
629 {
630 GimpRGB color;
631
632 gimp_color_button_get_color (GIMP_COLOR_BUTTON (widget), &color);
633
634 if (widget == vcolor_button)
635 gimp_color_button_set_color (GIMP_COLOR_BUTTON (hcolor_button), &color);
636 else if (widget == hcolor_button)
637 gimp_color_button_set_color (GIMP_COLOR_BUTTON (vcolor_button), &color);
638 }
639 }
640
641
642 static gint
dialog(gint32 image_ID,gint32 drawable_ID)643 dialog (gint32 image_ID,
644 gint32 drawable_ID)
645 {
646 GimpColorConfig *config;
647 GtkWidget *dlg;
648 GtkWidget *main_vbox;
649 GtkWidget *vbox;
650 GtkSizeGroup *group;
651 GtkWidget *label;
652 GtkWidget *preview;
653 GtkWidget *button;
654 GtkWidget *width;
655 GtkWidget *space;
656 GtkWidget *offset;
657 GtkWidget *chain_button;
658 GtkWidget *table;
659 GimpUnit unit;
660 gint d_width;
661 gint d_height;
662 gdouble xres;
663 gdouble yres;
664 gboolean run;
665
666 g_return_val_if_fail (main_dialog == NULL, FALSE);
667
668 gimp_ui_init (PLUG_IN_BINARY, TRUE);
669
670 d_width = gimp_drawable_width (drawable_ID);
671 d_height = gimp_drawable_height (drawable_ID);
672
673 main_dialog = dlg = gimp_dialog_new (_("Grid"), PLUG_IN_ROLE,
674 NULL, 0,
675 gimp_standard_help_func, PLUG_IN_PROC,
676
677 _("_Cancel"), GTK_RESPONSE_CANCEL,
678 _("_OK"), GTK_RESPONSE_OK,
679
680 NULL);
681
682 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
683 GTK_RESPONSE_OK,
684 GTK_RESPONSE_CANCEL,
685 -1);
686
687 gimp_window_set_transient (GTK_WINDOW (dlg));
688
689 /* Get the image resolution and unit */
690 gimp_image_get_resolution (image_ID, &xres, &yres);
691 unit = gimp_image_get_unit (image_ID);
692
693 main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
694 gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
695 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
696 main_vbox, TRUE, TRUE, 0);
697 gtk_widget_show (main_vbox);
698
699 preview = gimp_drawable_preview_new_from_drawable_id (drawable_ID);
700 gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
701 gtk_widget_show (preview);
702
703 g_signal_connect (preview, "invalidated",
704 G_CALLBACK (update_preview),
705 GINT_TO_POINTER (drawable_ID));
706
707 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
708 gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
709 gtk_widget_show (vbox);
710
711 /* The width entries */
712 width = gimp_size_entry_new (3, /* number_of_fields */
713 unit, /* unit */
714 "%a", /* unit_format */
715 TRUE, /* menu_show_pixels */
716 TRUE, /* menu_show_percent */
717 FALSE, /* show_refval */
718 SPIN_BUTTON_WIDTH, /* spinbutton_usize */
719 GIMP_SIZE_ENTRY_UPDATE_SIZE); /* update_policy */
720
721
722 gtk_box_pack_start (GTK_BOX (vbox), width, FALSE, FALSE, 0);
723 gtk_widget_show (width);
724
725 /* set the unit back to pixels, since most times we will want pixels */
726 gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (width), GIMP_UNIT_PIXEL);
727
728 /* set the resolution to the image resolution */
729 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 0, xres, TRUE);
730 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 1, yres, TRUE);
731 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 2, xres, TRUE);
732
733 /* set the size (in pixels) that will be treated as 0% and 100% */
734 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 0, 0.0, d_height);
735 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 1, 0.0, d_width);
736 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 2, 0.0, d_width);
737
738 /* set upper and lower limits (in pixels) */
739 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 0, 0.0,
740 d_height);
741 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 1, 0.0,
742 d_width);
743 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 2, 0.0,
744 MAX (d_width, d_height));
745 gtk_table_set_row_spacing (GTK_TABLE (width), 0, 6);
746 gtk_table_set_col_spacings (GTK_TABLE (width), 6);
747 gtk_table_set_col_spacing (GTK_TABLE (width), 2, 12);
748
749 /* initialize the values */
750 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 0, grid_cfg.hwidth);
751 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 1, grid_cfg.vwidth);
752 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 2, grid_cfg.iwidth);
753
754 /* attach labels */
755 gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Horizontal\nLines"),
756 0, 1, 0.0);
757 gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Vertical\nLines"),
758 0, 2, 0.0);
759 gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Intersection"),
760 0, 3, 0.0);
761
762 label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Width:"),
763 1, 0, 0.0);
764
765 group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
766 gtk_size_group_add_widget (group, label);
767 g_object_unref (group);
768
769 /* put a chain_button under the size_entries */
770 chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
771 if (grid_cfg.hwidth == grid_cfg.vwidth)
772 gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
773 gtk_table_attach_defaults (GTK_TABLE (width), chain_button, 1, 3, 2, 3);
774 gtk_widget_show (chain_button);
775
776 /* connect to the 'value-changed' signal because we have to take care
777 * of keeping the entries in sync when the chainbutton is active
778 */
779 g_signal_connect (width, "value-changed",
780 G_CALLBACK (entry_callback),
781 chain_button);
782 g_signal_connect_swapped (width, "value-changed",
783 G_CALLBACK (gimp_preview_invalidate),
784 preview);
785
786 /* The spacing entries */
787 space = gimp_size_entry_new (3, /* number_of_fields */
788 unit, /* unit */
789 "%a", /* unit_format */
790 TRUE, /* menu_show_pixels */
791 TRUE, /* menu_show_percent */
792 FALSE, /* show_refval */
793 SPIN_BUTTON_WIDTH, /* spinbutton_usize */
794 GIMP_SIZE_ENTRY_UPDATE_SIZE); /* update_policy */
795
796 gtk_box_pack_start (GTK_BOX (vbox), space, FALSE, FALSE, 0);
797 gtk_widget_show (space);
798
799 gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (space), GIMP_UNIT_PIXEL);
800
801 /* set the resolution to the image resolution */
802 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 0, xres, TRUE);
803 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 1, yres, TRUE);
804 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 2, xres, TRUE);
805
806 /* set the size (in pixels) that will be treated as 0% and 100% */
807 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 0, 0.0, d_height);
808 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 1, 0.0, d_width);
809 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 2, 0.0, d_width);
810
811 /* set upper and lower limits (in pixels) */
812 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 0, 1.0,
813 d_height);
814 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 1, 1.0,
815 d_width);
816 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 2, 0.0,
817 MAX (d_width, d_height));
818 gtk_table_set_col_spacings (GTK_TABLE (space), 6);
819 gtk_table_set_col_spacing (GTK_TABLE (space), 2, 12);
820
821 /* initialize the values */
822 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 0, grid_cfg.hspace);
823 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 1, grid_cfg.vspace);
824 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 2, grid_cfg.ispace);
825
826 /* attach labels */
827 label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (space), _("Spacing:"),
828 1, 0, 0.0);
829 gtk_size_group_add_widget (group, label);
830
831 /* put a chain_button under the spacing_entries */
832 chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
833 if (grid_cfg.hspace == grid_cfg.vspace)
834 gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
835 gtk_table_attach_defaults (GTK_TABLE (space), chain_button, 1, 3, 2, 3);
836 gtk_widget_show (chain_button);
837
838 /* connect to the 'value-changed' and "unit-changed" signals because
839 * we have to take care of keeping the entries in sync when the
840 * chainbutton is active
841 */
842 g_signal_connect (space, "value-changed",
843 G_CALLBACK (entry_callback),
844 chain_button);
845 g_signal_connect (space, "unit-changed",
846 G_CALLBACK (entry_callback),
847 chain_button);
848 g_signal_connect_swapped (space, "value-changed",
849 G_CALLBACK (gimp_preview_invalidate),
850 preview);
851
852 /* The offset entries */
853 offset = gimp_size_entry_new (3, /* number_of_fields */
854 unit, /* unit */
855 "%a", /* unit_format */
856 TRUE, /* menu_show_pixels */
857 TRUE, /* menu_show_percent */
858 FALSE, /* show_refval */
859 SPIN_BUTTON_WIDTH, /* spinbutton_usize */
860 GIMP_SIZE_ENTRY_UPDATE_SIZE); /* update_policy */
861
862 gtk_box_pack_start (GTK_BOX (vbox), offset, FALSE, FALSE, 0);
863 gtk_widget_show (offset);
864
865 gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (offset), GIMP_UNIT_PIXEL);
866
867 /* set the resolution to the image resolution */
868 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 0, xres, TRUE);
869 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 1, yres, TRUE);
870 gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 2, xres, TRUE);
871
872 /* set the size (in pixels) that will be treated as 0% and 100% */
873 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 0, 0.0, d_height);
874 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 1, 0.0, d_width);
875 gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 2, 0.0, d_width);
876
877 /* set upper and lower limits (in pixels) */
878 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 0, 0.0,
879 d_height);
880 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 1, 0.0,
881 d_width);
882 gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 2, 0.0,
883 MAX (d_width, d_height));
884 gtk_table_set_col_spacings (GTK_TABLE (offset), 6);
885 gtk_table_set_col_spacing (GTK_TABLE (offset), 2, 12);
886
887 /* initialize the values */
888 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 0, grid_cfg.hoffset);
889 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 1, grid_cfg.voffset);
890 gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 2, grid_cfg.ioffset);
891
892 /* attach labels */
893 label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (offset), _("Offset:"),
894 1, 0, 0.0);
895 gtk_size_group_add_widget (group, label);
896
897 /* this is a weird hack: we put a table into the offset table */
898 table = gtk_table_new (3, 3, FALSE);
899 gtk_table_attach_defaults (GTK_TABLE (offset), table, 1, 4, 2, 3);
900 gtk_table_set_row_spacing (GTK_TABLE (table), 0, 10);
901 gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
902
903 /* put a chain_button under the offset_entries */
904 chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
905 if (grid_cfg.hoffset == grid_cfg.voffset)
906 gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
907 gtk_table_attach_defaults (GTK_TABLE (table), chain_button, 0, 2, 0, 1);
908 gtk_widget_show (chain_button);
909
910 /* connect to the 'value-changed' and "unit-changed" signals because
911 * we have to take care of keeping the entries in sync when the
912 * chainbutton is active
913 */
914 g_signal_connect (offset, "value-changed",
915 G_CALLBACK (entry_callback),
916 chain_button);
917 g_signal_connect (offset, "unit-changed",
918 G_CALLBACK (entry_callback),
919 chain_button);
920 g_signal_connect_swapped (offset, "value-changed",
921 G_CALLBACK (gimp_preview_invalidate),
922 preview);
923
924 /* put a chain_button under the color_buttons */
925 chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
926 if (gimp_rgba_distance (&grid_cfg.hcolor, &grid_cfg.vcolor) < 0.0001)
927 gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
928 gtk_table_attach_defaults (GTK_TABLE (table), chain_button, 0, 2, 2, 3);
929 gtk_widget_show (chain_button);
930
931 /* attach color selectors */
932 hcolor_button = gimp_color_button_new (_("Horizontal Color"),
933 COLOR_BUTTON_WIDTH, 16,
934 &grid_cfg.hcolor,
935 GIMP_COLOR_AREA_SMALL_CHECKS);
936 gimp_color_button_set_update (GIMP_COLOR_BUTTON (hcolor_button), TRUE);
937 gtk_table_attach_defaults (GTK_TABLE (table), hcolor_button, 0, 1, 1, 2);
938 gtk_widget_show (hcolor_button);
939
940 config = gimp_get_color_configuration ();
941 gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (hcolor_button),
942 config);
943
944 g_signal_connect (hcolor_button, "color-changed",
945 G_CALLBACK (gimp_color_button_get_color),
946 &grid_cfg.hcolor);
947 g_signal_connect (hcolor_button, "color-changed",
948 G_CALLBACK (color_callback),
949 chain_button);
950 g_signal_connect_swapped (hcolor_button, "color-changed",
951 G_CALLBACK (gimp_preview_invalidate),
952 preview);
953
954 vcolor_button = gimp_color_button_new (_("Vertical Color"),
955 COLOR_BUTTON_WIDTH, 16,
956 &grid_cfg.vcolor,
957 GIMP_COLOR_AREA_SMALL_CHECKS);
958 gimp_color_button_set_update (GIMP_COLOR_BUTTON (vcolor_button), TRUE);
959 gtk_table_attach_defaults (GTK_TABLE (table), vcolor_button, 1, 2, 1, 2);
960 gtk_widget_show (vcolor_button);
961
962 gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (vcolor_button),
963 config);
964
965 g_signal_connect (vcolor_button, "color-changed",
966 G_CALLBACK (gimp_color_button_get_color),
967 &grid_cfg.vcolor);
968 g_signal_connect (vcolor_button, "color-changed",
969 G_CALLBACK (color_callback),
970 chain_button);
971 g_signal_connect_swapped (vcolor_button, "color-changed",
972 G_CALLBACK (gimp_preview_invalidate),
973 preview);
974
975 button = gimp_color_button_new (_("Intersection Color"),
976 COLOR_BUTTON_WIDTH, 16,
977 &grid_cfg.icolor,
978 GIMP_COLOR_AREA_SMALL_CHECKS);
979 gimp_color_button_set_update (GIMP_COLOR_BUTTON (button), TRUE);
980 gtk_table_attach_defaults (GTK_TABLE (table), button, 2, 3, 1, 2);
981 gtk_widget_show (button);
982
983 gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (button),
984 config);
985 g_object_unref (config);
986
987 g_signal_connect (button, "color-changed",
988 G_CALLBACK (gimp_color_button_get_color),
989 &grid_cfg.icolor);
990 g_signal_connect_swapped (button, "color-changed",
991 G_CALLBACK (gimp_preview_invalidate),
992 preview);
993
994 gtk_widget_show (table);
995
996 gtk_widget_show (dlg);
997
998 g_object_set_data (G_OBJECT (dlg), "width", width);
999 g_object_set_data (G_OBJECT (dlg), "space", space);
1000 g_object_set_data (G_OBJECT (dlg), "offset", offset);
1001
1002 run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
1003
1004 if (run)
1005 update_values ();
1006
1007 gtk_widget_destroy (dlg);
1008
1009 return run;
1010 }
1011
1012