1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 * Film plug-in (C) 1997 Peter Kirchgessner
4 * e-mail: pkirchg@aol.com, WWW: http://members.aol.com/pkirchg
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 /*
21 * This plug-in generates a film roll with several images
22 */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include <libgimp/gimp.h>
29 #include <libgimp/gimpui.h>
30
31 #include "libgimp/stdplugins-intl.h"
32
33
34 #define PLUG_IN_PROC "plug-in-film"
35 #define PLUG_IN_BINARY "film"
36 #define PLUG_IN_ROLE "gimp-film"
37
38 /* Maximum number of pictures per film */
39 #define MAX_FILM_PICTURES 64
40 #define COLOR_BUTTON_WIDTH 50
41 #define COLOR_BUTTON_HEIGHT 20
42
43 #define FONT_LEN 256
44
45 /* Define how the plug-in works. Values marked (r) are with regard */
46 /* to film_height (i.e. it should be a value from 0.0 to 1.0) */
47 typedef struct
48 {
49 gint film_height; /* height of the film */
50 GimpRGB film_color; /* color of film */
51 gdouble picture_height; /* height of picture (r) */
52 gdouble picture_space; /* space between pictures (r) */
53 gdouble hole_offset; /* distance from hole to edge of film (r) */
54 gdouble hole_width; /* width of hole (r) */
55 gdouble hole_height; /* height of holes (r) */
56 gdouble hole_space; /* distance of holes (r) */
57 gdouble number_height; /* height of picture numbering (r) */
58 gint number_start; /* number for first picture */
59 GimpRGB number_color; /* color of number */
60 gchar number_font[FONT_LEN]; /* font family to use for numbering */
61 gint number_pos[2]; /* flags where to draw numbers (top/bottom) */
62 gint keep_height; /* flag if to keep max. image height */
63 gint num_images; /* number of images */
64 gint32 image[MAX_FILM_PICTURES]; /* list of image IDs */
65 } FilmVals;
66
67 /* Data to use for the dialog */
68 typedef struct
69 {
70 GtkObject *advanced_adj[7];
71 GtkTreeModel *image_list_all;
72 GtkTreeModel *image_list_film;
73 } FilmInterface;
74
75
76 /* Declare local functions
77 */
78 static void query (void);
79 static void run (const gchar *name,
80 gint nparams,
81 const GimpParam *param,
82 gint *nreturn_vals,
83 GimpParam **return_vals);
84
85
86 static gint32 create_new_image (const gchar *filename,
87 guint width,
88 guint height,
89 GimpImageType gdtype,
90 gint32 *layer_ID);
91
92 static gchar * compose_image_name (gint32 image_ID);
93
94 static gint32 film (void);
95
96 static gboolean check_filmvals (void);
97
98 static void set_pixels (gint numpix,
99 guchar *dst,
100 GimpRGB *color);
101
102 static guchar * create_hole_rgb (gint width,
103 gint height);
104
105 static void draw_number (gint32 layer_ID,
106 gint num,
107 gint x,
108 gint y,
109 gint height);
110
111
112 static void add_list_item_callback (GtkWidget *widget,
113 GtkTreeSelection *sel);
114 static void del_list_item_callback (GtkWidget *widget,
115 GtkTreeSelection *sel);
116
117 static GtkTreeModel * add_image_list (gboolean add_box_flag,
118 gint n,
119 gint32 *image_id,
120 GtkWidget *hbox);
121
122 static gboolean film_dialog (gint32 image_ID);
123 static void film_reset_callback (GtkWidget *widget,
124 gpointer data);
125 static void film_font_select_callback (GimpFontSelectButton *button,
126 const gchar *name,
127 gboolean closing,
128 gpointer data);
129
130
131 const GimpPlugInInfo PLUG_IN_INFO =
132 {
133 NULL, /* init_proc */
134 NULL, /* quit_proc */
135 query, /* query_proc */
136 run, /* run_proc */
137 };
138
139 static gdouble advanced_defaults[] =
140 {
141 0.695, /* Picture height */
142 0.040, /* Picture spacing */
143 0.058, /* Hole offset to edge of film */
144 0.052, /* Hole width */
145 0.081, /* Hole height */
146 0.081, /* Hole distance */
147 0.052 /* Image number height */
148 };
149
150 static FilmVals filmvals =
151 {
152 256, /* Height of film */
153 { 0.0, 0.0, 0.0, 1.0 }, /* Color of film */
154 0.695, /* Picture height */
155 0.040, /* Picture spacing */
156 0.058, /* Hole offset to edge of film */
157 0.052, /* Hole width */
158 0.081, /* Hole height */
159 0.081, /* Hole distance */
160 0.052, /* Image number height */
161 1, /* Start index of numbering */
162 { 0.93, 0.61, 0.0, 1.0 }, /* Color of number */
163 "Monospace", /* Font family for numbering */
164 { TRUE, TRUE }, /* Numbering on top and bottom */
165 0, /* Don't keep max. image height */
166 0, /* Number of images */
167 { 0 } /* Input image list */
168 };
169
170
171 static FilmInterface filmint =
172 {
173 { NULL }, /* advanced adjustments */
174 NULL, NULL /* image list widgets */
175 };
176
177
178 static GimpRunMode run_mode;
179
180
MAIN()181 MAIN ()
182
183 static void
184 query (void)
185 {
186 static const GimpParamDef args[] =
187 {
188 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
189 { GIMP_PDB_IMAGE, "image", "Input image (only used as default image in interactive mode)" },
190 { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (not used)" },
191 { GIMP_PDB_INT32, "film-height", "Height of film (0: fit to images)" },
192 { GIMP_PDB_COLOR, "film-color", "Color of the film" },
193 { GIMP_PDB_INT32, "number-start", "Start index for numbering" },
194 { GIMP_PDB_STRING, "number-font", "Font for drawing numbers" },
195 { GIMP_PDB_COLOR, "number-color", "Color for numbers" },
196 { GIMP_PDB_INT32, "at-top", "Flag for drawing numbers at top of film" },
197 { GIMP_PDB_INT32, "at-bottom", "Flag for drawing numbers at bottom of film" },
198 { GIMP_PDB_INT32, "num-images", "Number of images to be used for film" },
199 { GIMP_PDB_INT32ARRAY, "image-ids", "num-images image IDs to be used for film" }
200 };
201
202 static const GimpParamDef return_vals[] =
203 {
204 { GIMP_PDB_IMAGE, "new-image", "Output image" }
205 };
206
207 gimp_install_procedure (PLUG_IN_PROC,
208 N_("Combine several images on a film strip"),
209 "Compose several images to a roll film",
210 "Peter Kirchgessner",
211 "Peter Kirchgessner (peter@kirchgessner.net)",
212 "1997",
213 N_("_Filmstrip..."),
214 "INDEXED*, GRAY*, RGB*",
215 GIMP_PLUGIN,
216 G_N_ELEMENTS (args),
217 G_N_ELEMENTS (return_vals),
218 args, return_vals);
219
220 gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Combine");
221 }
222
223 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)224 run (const gchar *name,
225 gint nparams,
226 const GimpParam *param,
227 gint *nreturn_vals,
228 GimpParam **return_vals)
229 {
230 static GimpParam values[2];
231 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
232 gint32 image_ID;
233 gint k;
234
235 INIT_I18N ();
236 gegl_init (NULL, NULL);
237
238 run_mode = param[0].data.d_int32;
239
240 *nreturn_vals = 2;
241 *return_vals = values;
242
243 values[0].type = GIMP_PDB_STATUS;
244 values[0].data.d_status = status;
245 values[1].type = GIMP_PDB_IMAGE;
246 values[1].data.d_int32 = -1;
247
248 switch (run_mode)
249 {
250 case GIMP_RUN_INTERACTIVE:
251 /* Possibly retrieve data */
252 gimp_get_data (PLUG_IN_PROC, &filmvals);
253
254 /* First acquire information with a dialog */
255 if (! film_dialog (param[1].data.d_int32))
256 return;
257 break;
258
259 case GIMP_RUN_NONINTERACTIVE:
260 /* Make sure all the arguments are there! */
261 /* Also we want to have some images to compose */
262 if ((nparams != 12) || (param[10].data.d_int32 < 1))
263 {
264 status = GIMP_PDB_CALLING_ERROR;
265 }
266 else
267 {
268 filmvals.keep_height = (param[3].data.d_int32 <= 0);
269 filmvals.film_height = (filmvals.keep_height ?
270 128 : param[3].data.d_int32);
271 filmvals.film_color = param[4].data.d_color;
272 filmvals.number_start = param[5].data.d_int32;
273 g_strlcpy (filmvals.number_font, param[6].data.d_string, FONT_LEN);
274 filmvals.number_color = param[7].data.d_color;
275 filmvals.number_pos[0] = param[8].data.d_int32;
276 filmvals.number_pos[1] = param[9].data.d_int32;
277 filmvals.num_images = param[10].data.d_int32;
278 if (filmvals.num_images > MAX_FILM_PICTURES)
279 filmvals.num_images = MAX_FILM_PICTURES;
280 for (k = 0; k < filmvals.num_images; k++)
281 filmvals.image[k] = param[11].data.d_int32array[k];
282 }
283 break;
284
285 case GIMP_RUN_WITH_LAST_VALS:
286 /* Possibly retrieve data */
287 gimp_get_data (PLUG_IN_PROC, &filmvals);
288 break;
289
290 default:
291 break;
292 }
293
294 if (! check_filmvals ())
295 status = GIMP_PDB_CALLING_ERROR;
296
297 if (status == GIMP_PDB_SUCCESS)
298 {
299 gimp_progress_init (_("Composing images"));
300
301 image_ID = film ();
302
303 if (image_ID < 0)
304 {
305 status = GIMP_PDB_EXECUTION_ERROR;
306 }
307 else
308 {
309 values[1].data.d_int32 = image_ID;
310 gimp_image_undo_enable (image_ID);
311 gimp_image_clean_all (image_ID);
312 if (run_mode != GIMP_RUN_NONINTERACTIVE)
313 gimp_display_new (image_ID);
314 }
315
316 /* Store data */
317 if (run_mode == GIMP_RUN_INTERACTIVE)
318 gimp_set_data (PLUG_IN_PROC, &filmvals, sizeof (FilmVals));
319 }
320
321 values[0].data.d_status = status;
322 }
323
324 /* Compose a roll film image from several images */
325 static gint32
film(void)326 film (void)
327 {
328 gint width, height;
329 guchar *hole;
330 gint film_height, film_width;
331 gint picture_width, picture_height;
332 gint picture_space, picture_x0, picture_y0;
333 gint hole_offset, hole_width, hole_height, hole_space, hole_x;
334 gint number_height, num_images, num_pictures;
335 gint j, k, picture_count;
336 gdouble f;
337 gint num_layers;
338 gint32 *image_ID_src, image_ID_dst, layer_ID_src, layer_ID_dst;
339 gint image_ID_tmp;
340 gint32 *layers;
341 gint new_layer;
342 gint floating_sel;
343
344 /* initialize */
345
346 layers = NULL;
347
348 num_images = filmvals.num_images;
349 image_ID_src = filmvals.image;
350
351 if (num_images <= 0)
352 return (-1);
353
354 gimp_context_push ();
355 gimp_context_set_foreground (&filmvals.number_color);
356 gimp_context_set_background (&filmvals.film_color);
357
358 if (filmvals.keep_height) /* Search maximum picture height */
359 {
360 picture_height = 0;
361 for (j = 0; j < num_images; j++)
362 {
363 height = gimp_image_height (image_ID_src[j]);
364 if (height > picture_height) picture_height = height;
365 }
366 film_height = (int)(picture_height / filmvals.picture_height + 0.5);
367 filmvals.film_height = film_height;
368 }
369 else
370 {
371 film_height = filmvals.film_height;
372 picture_height = (int)(film_height * filmvals.picture_height + 0.5);
373 }
374
375 picture_space = (int)(film_height * filmvals.picture_space + 0.5);
376 picture_y0 = (film_height - picture_height)/2;
377
378 number_height = film_height * filmvals.number_height;
379
380 /* Calculate total film width */
381 film_width = 0;
382 num_pictures = 0;
383 for (j = 0; j < num_images; j++)
384 {
385 layers = gimp_image_get_layers (image_ID_src[j], &num_layers);
386 /* Get scaled image size */
387 width = gimp_image_width (image_ID_src[j]);
388 height = gimp_image_height (image_ID_src[j]);
389 f = ((double)picture_height) / (double)height;
390 picture_width = width * f;
391
392 for (k = 0; k < num_layers; k++)
393 {
394 if (gimp_layer_is_floating_sel (layers[k]))
395 continue;
396
397 film_width += (picture_space/2); /* Leading space */
398 film_width += picture_width; /* Scaled image width */
399 film_width += (picture_space/2); /* Trailing space */
400 num_pictures++;
401 }
402
403 g_free (layers);
404 }
405
406 #ifdef FILM_DEBUG
407 g_printerr ("film_height = %d, film_width = %d\n", film_height, film_width);
408 g_printerr ("picture_height = %d, picture_space = %d, picture_y0 = %d\n",
409 picture_height, picture_space, picture_y0);
410 g_printerr ("Number of pictures = %d\n", num_pictures);
411 #endif
412
413 image_ID_dst = create_new_image (_("Untitled"),
414 (guint) film_width, (guint) film_height,
415 GIMP_RGB_IMAGE, &layer_ID_dst);
416
417 /* Fill film background */
418 gimp_drawable_fill (layer_ID_dst, GIMP_FILL_BACKGROUND);
419
420 /* Draw all the holes */
421 hole_offset = film_height * filmvals.hole_offset;
422 hole_width = film_height * filmvals.hole_width;
423 hole_height = film_height * filmvals.hole_height;
424 hole_space = film_height * filmvals.hole_space;
425 hole_x = hole_space / 2;
426
427 #ifdef FILM_DEBUG
428 g_printerr ("hole_x %d hole_offset %d hole_width %d hole_height %d hole_space %d\n",
429 hole_x, hole_offset, hole_width, hole_height, hole_space );
430 #endif
431
432 hole = create_hole_rgb (hole_width, hole_height);
433 if (hole)
434 {
435 GeglBuffer *buffer = gimp_drawable_get_buffer (layer_ID_dst);
436
437 while (hole_x < film_width)
438 {
439 gegl_buffer_set (buffer,
440 GEGL_RECTANGLE (hole_x,
441 hole_offset,
442 hole_width,
443 hole_height), 0,
444 babl_format ("R'G'B' u8"), hole,
445 GEGL_AUTO_ROWSTRIDE);
446
447 gegl_buffer_set (buffer,
448 GEGL_RECTANGLE (hole_x,
449 film_height - hole_offset - hole_height,
450 hole_width,
451 hole_height), 0,
452 babl_format ("R'G'B' u8"), hole,
453 GEGL_AUTO_ROWSTRIDE);
454
455 hole_x += hole_width + hole_space;
456 }
457
458 g_object_unref (buffer);
459 g_free (hole);
460 }
461
462 /* Compose all images and layers */
463 picture_x0 = 0;
464 picture_count = 0;
465 for (j = 0; j < num_images; j++)
466 {
467 image_ID_tmp = gimp_image_duplicate (image_ID_src[j]);
468 width = gimp_image_width (image_ID_tmp);
469 height = gimp_image_height (image_ID_tmp);
470 f = ((gdouble) picture_height) / (gdouble) height;
471 picture_width = width * f;
472 if (gimp_image_base_type (image_ID_tmp) != GIMP_RGB)
473 gimp_image_convert_rgb (image_ID_tmp);
474 gimp_image_scale (image_ID_tmp, picture_width, picture_height);
475
476 layers = gimp_image_get_layers (image_ID_tmp, &num_layers);
477 for (k = 0; k < num_layers; k++)
478 {
479 if (gimp_layer_is_floating_sel (layers[k]))
480 continue;
481
482 picture_x0 += picture_space / 2;
483
484 layer_ID_src = layers[k];
485 gimp_layer_resize_to_image_size (layer_ID_src);
486 new_layer = gimp_layer_new_from_drawable (layer_ID_src,
487 image_ID_dst);
488 gimp_image_insert_layer (image_ID_dst, new_layer, -1, -1);
489 gimp_layer_set_offsets (new_layer, picture_x0, picture_y0);
490
491 /* Draw picture numbers */
492 if ((number_height > 0) &&
493 (filmvals.number_pos[0] || filmvals.number_pos[1]))
494 {
495 if (filmvals.number_pos[0])
496 draw_number (layer_ID_dst,
497 filmvals.number_start + picture_count,
498 picture_x0 + picture_width/2,
499 (hole_offset-number_height)/2, number_height);
500 if (filmvals.number_pos[1])
501 draw_number (layer_ID_dst,
502 filmvals.number_start + picture_count,
503 picture_x0 + picture_width/2,
504 film_height - (hole_offset + number_height)/2,
505 number_height);
506 }
507
508 picture_x0 += picture_width + (picture_space/2);
509
510 gimp_progress_update (((gdouble) (picture_count + 1)) /
511 (gdouble) num_pictures);
512
513 picture_count++;
514 }
515
516 g_free (layers);
517 gimp_image_delete (image_ID_tmp);
518 }
519 gimp_progress_update (1.0);
520
521 gimp_image_flatten (image_ID_dst);
522
523 /* Drawing text/numbers leaves us with a floating selection. Stop it */
524 floating_sel = gimp_image_get_floating_sel (image_ID_dst);
525 if (floating_sel != -1)
526 gimp_floating_sel_anchor (floating_sel);
527
528 gimp_context_pop ();
529
530 return image_ID_dst;
531 }
532
533 /* Check filmvals. Unreasonable values are reset to a default. */
534 /* If this is not possible, FALSE is returned. Otherwise TRUE is returned. */
535 static gboolean
check_filmvals(void)536 check_filmvals (void)
537 {
538 if (filmvals.film_height < 10)
539 filmvals.film_height = 10;
540
541 if (filmvals.number_start < 0)
542 filmvals.number_start = 0;
543
544 if (filmvals.number_font[0] == '\0')
545 strcpy (filmvals.number_font, "Monospace");
546
547 if (filmvals.num_images < 1)
548 return FALSE;
549
550 return TRUE;
551 }
552
553 /* Assigns numpix pixels starting at dst with color r,g,b */
554 static void
set_pixels(gint numpix,guchar * dst,GimpRGB * color)555 set_pixels (gint numpix,
556 guchar *dst,
557 GimpRGB *color)
558 {
559 register gint k;
560 register guchar ur, ug, ub, *udest;
561
562 ur = color->r * 255.999;
563 ug = color->g * 255.999;
564 ub = color->b * 255.999;
565 k = numpix;
566 udest = dst;
567
568 while (k-- > 0)
569 {
570 *(udest++) = ur;
571 *(udest++) = ug;
572 *(udest++) = ub;
573 }
574 }
575
576 /* Create the RGB-pixels that make up the hole */
577 static guchar *
create_hole_rgb(gint width,gint height)578 create_hole_rgb (gint width,
579 gint height)
580 {
581 guchar *hole, *top, *bottom;
582 gint radius, length, k;
583
584 hole = g_new (guchar, width * height * 3);
585
586 /* Fill a rectangle with white */
587 memset (hole, 255, width * height * 3);
588 radius = height / 4;
589 if (radius > width / 2)
590 radius = width / 2;
591 top = hole;
592 bottom = hole + (height-1)*width*3;
593 for (k = radius-1; k > 0; k--) /* Rounding corners */
594 {
595 length = (int)(radius - sqrt ((gdouble) (radius * radius - k * k))
596 - 0.5);
597 if (length > 0)
598 {
599 set_pixels (length, top, &filmvals.film_color);
600 set_pixels (length, top + (width-length)*3, &filmvals.film_color);
601 set_pixels (length, bottom, &filmvals.film_color);
602 set_pixels (length, bottom + (width-length)*3, &filmvals.film_color);
603 }
604 top += width*3;
605 bottom -= width*3;
606 }
607
608 return hole;
609 }
610
611 /* Draw the number of the picture onto the film */
612 static void
draw_number(gint32 layer_ID,gint num,gint x,gint y,gint height)613 draw_number (gint32 layer_ID,
614 gint num,
615 gint x,
616 gint y,
617 gint height)
618 {
619 gchar buf[32];
620 gint k, delta, max_delta;
621 gint32 image_ID;
622 gint32 text_layer_ID;
623 gint text_width, text_height, text_ascent, descent;
624 gchar *fontname = filmvals.number_font;
625
626 g_snprintf (buf, sizeof (buf), "%d", num);
627
628 image_ID = gimp_item_get_image (layer_ID);
629
630 max_delta = height / 10;
631 if (max_delta < 1)
632 max_delta = 1;
633
634 /* Numbers don't need the descent. Inquire it and move the text down */
635 for (k = 0; k < max_delta * 2 + 1; k++)
636 {
637 /* Try different font sizes if inquire of extent failed */
638 gboolean success;
639
640 delta = (k+1) / 2;
641
642 if ((k & 1) == 0)
643 delta = -delta;
644
645 success = gimp_text_get_extents_fontname (buf,
646 height + delta, GIMP_PIXELS,
647 fontname,
648 &text_width, &text_height,
649 &text_ascent, &descent);
650
651 if (success)
652 {
653 height += delta;
654 break;
655 }
656 }
657
658 text_layer_ID = gimp_text_fontname (image_ID, layer_ID,
659 x, y + descent / 2,
660 buf, 1, FALSE,
661 height, GIMP_PIXELS,
662 fontname);
663
664 if (text_layer_ID == -1)
665 g_message ("draw_number: Error in drawing text\n");
666 }
667
668 /* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
669 static gint32
create_new_image(const gchar * filename,guint width,guint height,GimpImageType gdtype,gint32 * layer_ID)670 create_new_image (const gchar *filename,
671 guint width,
672 guint height,
673 GimpImageType gdtype,
674 gint32 *layer_ID)
675 {
676 gint32 image_ID;
677 GimpImageBaseType gitype;
678
679 if ((gdtype == GIMP_GRAY_IMAGE) || (gdtype == GIMP_GRAYA_IMAGE))
680 gitype = GIMP_GRAY;
681 else if ((gdtype == GIMP_INDEXED_IMAGE) || (gdtype == GIMP_INDEXEDA_IMAGE))
682 gitype = GIMP_INDEXED;
683 else
684 gitype = GIMP_RGB;
685
686 image_ID = gimp_image_new (width, height, gitype);
687 gimp_image_set_filename (image_ID, filename);
688
689 gimp_image_undo_disable (image_ID);
690 *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height,
691 gdtype,
692 100,
693 gimp_image_get_default_new_layer_mode (image_ID));
694 gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
695
696 return image_ID;
697 }
698
699 static gchar *
compose_image_name(gint32 image_ID)700 compose_image_name (gint32 image_ID)
701 {
702 gchar *image_name;
703 gchar *name;
704
705 /* Compose a name of the basename and the image-ID */
706
707 name = gimp_image_get_name (image_ID);
708
709 image_name = g_strdup_printf ("%s-%d", name, image_ID);
710
711 g_free (name);
712
713 return image_name;
714 }
715
716 static void
add_list_item_callback(GtkWidget * widget,GtkTreeSelection * sel)717 add_list_item_callback (GtkWidget *widget,
718 GtkTreeSelection *sel)
719 {
720 GtkTreeModel *model;
721 GList *paths;
722 GList *list;
723
724 paths = gtk_tree_selection_get_selected_rows (sel, &model);
725
726 for (list = paths; list; list = g_list_next (list))
727 {
728 GtkTreeIter iter;
729
730 if (gtk_tree_model_get_iter (model, &iter, list->data))
731 {
732 gint32 image_ID;
733 gchar *name;
734
735 gtk_tree_model_get (model, &iter,
736 0, &image_ID,
737 1, &name,
738 -1);
739
740 gtk_list_store_append (GTK_LIST_STORE (filmint.image_list_film),
741 &iter);
742
743 gtk_list_store_set (GTK_LIST_STORE (filmint.image_list_film),
744 &iter,
745 0, image_ID,
746 1, name,
747 -1);
748
749 g_free (name);
750 }
751
752 gtk_tree_path_free (list->data);
753 }
754
755 g_list_free (paths);
756 }
757
758 static void
del_list_item_callback(GtkWidget * widget,GtkTreeSelection * sel)759 del_list_item_callback (GtkWidget *widget,
760 GtkTreeSelection *sel)
761 {
762 GtkTreeModel *model;
763 GList *paths;
764 GList *references = NULL;
765 GList *list;
766
767 paths = gtk_tree_selection_get_selected_rows (sel, &model);
768
769 for (list = paths; list; list = g_list_next (list))
770 {
771 GtkTreeRowReference *ref;
772
773 ref = gtk_tree_row_reference_new (model, list->data);
774 references = g_list_prepend (references, ref);
775 gtk_tree_path_free (list->data);
776 }
777
778 g_list_free (paths);
779
780 for (list = references; list; list = g_list_next (list))
781 {
782 GtkTreePath *path;
783 GtkTreeIter iter;
784
785 path = gtk_tree_row_reference_get_path (list->data);
786
787 if (gtk_tree_model_get_iter (model, &iter, path))
788 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
789
790 gtk_tree_path_free (path);
791 gtk_tree_row_reference_free (list->data);
792 }
793
794 g_list_free (references);
795 }
796
797 static GtkTreeModel *
add_image_list(gboolean add_box_flag,gint n,gint32 * image_id,GtkWidget * hbox)798 add_image_list (gboolean add_box_flag,
799 gint n,
800 gint32 *image_id,
801 GtkWidget *hbox)
802 {
803 GtkWidget *vbox;
804 GtkWidget *label;
805 GtkWidget *scrolled_win;
806 GtkWidget *tv;
807 GtkWidget *button;
808 GtkListStore *store;
809 GtkTreeSelection *sel;
810 gint i;
811
812 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
813 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
814 gtk_widget_show (vbox);
815
816 label = gtk_label_new (add_box_flag ?
817 _("Available images:") :
818 _("On film:"));
819 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
820 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
821 gtk_widget_show (label);
822
823 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
824 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
825 GTK_SHADOW_IN);
826 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
827 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
828 gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
829 gtk_widget_show (scrolled_win);
830
831 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
832 tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
833 g_object_unref (store);
834
835 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tv), FALSE);
836
837 if (! add_box_flag)
838 gtk_tree_view_set_reorderable (GTK_TREE_VIEW (tv), TRUE);
839
840 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv), 0, NULL,
841 gtk_cell_renderer_text_new (),
842 "text", 1,
843 NULL);
844
845 gtk_container_add (GTK_CONTAINER (scrolled_win), tv);
846 gtk_widget_show (tv);
847
848 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
849 gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
850
851 for (i = 0; i < n; i++)
852 {
853 GtkTreeIter iter;
854 gchar *name;
855
856 gtk_list_store_append (store, &iter);
857
858 name = compose_image_name (image_id[i]);
859
860 gtk_list_store_set (store, &iter,
861 0, image_id[i],
862 1, name,
863 -1);
864
865 g_free (name);
866 }
867
868 button = gtk_button_new_with_mnemonic (add_box_flag ?
869 _("_Add") : _("_Remove"));
870 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
871 gtk_widget_show (button);
872
873 g_signal_connect (button, "clicked",
874 add_box_flag ?
875 G_CALLBACK (add_list_item_callback) :
876 G_CALLBACK (del_list_item_callback),
877 sel);
878
879 return GTK_TREE_MODEL (store);
880 }
881
882 static void
create_selection_tab(GtkWidget * notebook,gint32 image_ID)883 create_selection_tab (GtkWidget *notebook,
884 gint32 image_ID)
885 {
886 GimpColorConfig *config;
887 GtkSizeGroup *group;
888 GtkWidget *vbox;
889 GtkWidget *vbox2;
890 GtkWidget *hbox;
891 GtkWidget *table;
892 GtkWidget *label;
893 GtkWidget *frame;
894 GtkWidget *toggle;
895 GtkWidget *spinbutton;
896 GtkAdjustment *adj;
897 GtkWidget *button;
898 GtkWidget *font_button;
899 gint32 *image_id_list;
900 gint nimages, j;
901
902 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
903 gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
904 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), hbox,
905 gtk_label_new_with_mnemonic (_("Selection")));
906 gtk_widget_show (hbox);
907
908 vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
909 gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
910 gtk_widget_show (vbox2);
911
912 group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
913
914 /* Film height/color */
915 frame = gimp_frame_new (_("Filmstrip"));
916 gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
917 gtk_widget_show (frame);
918
919 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
920 gtk_container_add (GTK_CONTAINER (frame), vbox);
921 gtk_widget_show (vbox);
922
923 /* Keep maximum image height */
924 toggle = gtk_check_button_new_with_mnemonic (_("_Fit height to images"));
925 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
926 gtk_widget_show (toggle);
927
928 g_signal_connect (toggle, "toggled",
929 G_CALLBACK (gimp_toggle_button_update),
930 &filmvals.keep_height);
931
932 table = gtk_table_new (2, 2, FALSE);
933 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
934 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
935 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
936 gtk_widget_show (table);
937
938 /* Film height */
939 adj = GTK_ADJUSTMENT (gtk_adjustment_new (filmvals.film_height, 10,
940 GIMP_MAX_IMAGE_SIZE, 1, 10, 0));
941 spinbutton = gimp_spin_button_new (adj, 1, 0);
942 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
943
944 label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
945 _("_Height:"), 0.0, 0.5,
946 spinbutton, 1, TRUE);
947 gtk_size_group_add_widget (group, label);
948 g_object_unref (group);
949
950 g_signal_connect (adj, "value-changed",
951 G_CALLBACK (gimp_int_adjustment_update),
952 &filmvals.film_height);
953
954 g_object_bind_property (toggle, "active",
955 spinbutton, "sensitive",
956 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
957 g_object_bind_property (toggle, "active",
958 /* FIXME: eeeeeek */
959 g_list_nth_data (gtk_container_get_children (GTK_CONTAINER (table)), 1), "sensitive",
960 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
961
962 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
963 filmvals.keep_height);
964
965 /* Film color */
966 button = gimp_color_button_new (_("Select Film Color"),
967 COLOR_BUTTON_WIDTH, COLOR_BUTTON_HEIGHT,
968 &filmvals.film_color,
969 GIMP_COLOR_AREA_FLAT);
970 label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
971 _("Co_lor:"), 0.0, 0.5,
972 button, 1, FALSE);
973 gtk_size_group_add_widget (group, label);
974
975 g_signal_connect (button, "color-changed",
976 G_CALLBACK (gimp_color_button_get_color),
977 &filmvals.film_color);
978
979 config = gimp_get_color_configuration ();
980 gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (button), config);
981
982 /* Film numbering: Startindex/Font/color */
983 frame = gimp_frame_new (_("Numbering"));
984 gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, 0);
985 gtk_widget_show (frame);
986
987 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
988 gtk_container_add (GTK_CONTAINER (frame), vbox);
989 gtk_widget_show (vbox);
990
991 table = gtk_table_new (3, 2, FALSE);
992 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
993 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
994 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
995 gtk_widget_show (table);
996
997 /* Startindex */
998 adj = GTK_ADJUSTMENT (gtk_adjustment_new (filmvals.number_start, 0,
999 GIMP_MAX_IMAGE_SIZE, 1, 10, 0));
1000 spinbutton = gimp_spin_button_new (adj, 1, 0);
1001 gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
1002
1003 label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
1004 _("Start _index:"), 0.0, 0.5,
1005 spinbutton, 1, TRUE);
1006 gtk_size_group_add_widget (group, label);
1007
1008 g_signal_connect (adj, "value-changed",
1009 G_CALLBACK (gimp_int_adjustment_update),
1010 &filmvals.number_start);
1011
1012 /* Fontfamily for numbering */
1013 font_button = gimp_font_select_button_new (NULL, filmvals.number_font);
1014 g_signal_connect (font_button, "font-set",
1015 G_CALLBACK (film_font_select_callback), &filmvals);
1016 label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
1017 _("_Font:"), 0.0, 0.5,
1018 font_button, 1, FALSE);
1019 gtk_size_group_add_widget (group, label);
1020
1021 /* Numbering color */
1022 button = gimp_color_button_new (_("Select Number Color"),
1023 COLOR_BUTTON_WIDTH, COLOR_BUTTON_HEIGHT,
1024 &filmvals.number_color,
1025 GIMP_COLOR_AREA_FLAT);
1026 label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
1027 _("Co_lor:"), 0.0, 0.5,
1028 button, 1, FALSE);
1029 gtk_size_group_add_widget (group, label);
1030
1031 g_signal_connect (button, "color-changed",
1032 G_CALLBACK (gimp_color_button_get_color),
1033 &filmvals.number_color);
1034
1035 gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (button), config);
1036 g_object_unref (config);
1037
1038 for (j = 0; j < 2; j++)
1039 {
1040 toggle = gtk_check_button_new_with_mnemonic (j ? _("At _bottom")
1041 : _("At _top"));
1042 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1043 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
1044 filmvals.number_pos[j]);
1045 gtk_widget_show (toggle);
1046
1047 g_signal_connect (toggle, "toggled",
1048 G_CALLBACK (gimp_toggle_button_update),
1049 &filmvals.number_pos[j]);
1050 }
1051
1052
1053 /*** The right frame keeps the image selection ***/
1054 frame = gimp_frame_new (_("Image Selection"));
1055 gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
1056 gtk_widget_show (frame);
1057
1058 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1059 gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
1060 gtk_container_add (GTK_CONTAINER (frame), hbox);
1061
1062 /* Get a list of all image names */
1063 image_id_list = gimp_image_list (&nimages);
1064 filmint.image_list_all = add_image_list (TRUE, nimages, image_id_list, hbox);
1065
1066 /* Get a list of the images used for the film */
1067 filmint.image_list_film = add_image_list (FALSE, 1, &image_ID, hbox);
1068
1069 gtk_widget_show (hbox);
1070 }
1071
1072 static void
create_advanced_tab(GtkWidget * notebook)1073 create_advanced_tab (GtkWidget *notebook)
1074 {
1075 GtkWidget *vbox;
1076 GtkWidget *hbox;
1077 GtkWidget *table;
1078 GtkWidget *frame;
1079 GtkObject *adj;
1080 GtkWidget *button;
1081 gint row;
1082
1083 frame = gimp_frame_new (_("All Values are Fractions of the Strip Height"));
1084 gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
1085 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame,
1086 gtk_label_new_with_mnemonic (_("Ad_vanced")));
1087 gtk_widget_show (frame);
1088
1089 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1090 gtk_container_add (GTK_CONTAINER (frame), vbox);
1091 gtk_widget_show (vbox);
1092
1093 table = gtk_table_new (7, 3, FALSE);
1094 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1095 gtk_table_set_col_spacings (GTK_TABLE (table), 6);
1096 gtk_table_set_row_spacing (GTK_TABLE (table), 1, 12);
1097 gtk_table_set_row_spacing (GTK_TABLE (table), 5, 12);
1098 gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1099 gtk_widget_show (table);
1100
1101 row = 0;
1102
1103 filmint.advanced_adj[0] = adj =
1104 gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
1105 _("Image _height:"), 0, 0,
1106 filmvals.picture_height,
1107 0.0, 1.0, 0.001, 0.01, 3,
1108 TRUE, 0, 0,
1109 NULL, NULL);
1110 g_signal_connect (adj, "value-changed",
1111 G_CALLBACK (gimp_double_adjustment_update),
1112 &filmvals.picture_height);
1113
1114 filmint.advanced_adj[1] = adj =
1115 gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
1116 _("Image spac_ing:"), 0, 0,
1117 filmvals.picture_space,
1118 0.0, 1.0, 0.001, 0.01, 3,
1119 TRUE, 0, 0,
1120 NULL, NULL);
1121 g_signal_connect (adj, "value-changed",
1122 G_CALLBACK (gimp_double_adjustment_update),
1123 &filmvals.picture_space);
1124
1125 filmint.advanced_adj[2] = adj =
1126 gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
1127 _("_Hole offset:"), 0, 0,
1128 filmvals.hole_offset,
1129 0.0, 1.0, 0.001, 0.01, 3,
1130 TRUE, 0, 0,
1131 NULL, NULL);
1132 g_signal_connect (adj, "value-changed",
1133 G_CALLBACK (gimp_double_adjustment_update),
1134 &filmvals.hole_offset);
1135
1136 filmint.advanced_adj[3] = adj =
1137 gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
1138 _("Ho_le width:"), 0, 0,
1139 filmvals.hole_width,
1140 0.0, 1.0, 0.001, 0.01, 3,
1141 TRUE, 0, 0,
1142 NULL, NULL);
1143 g_signal_connect (adj, "value-changed",
1144 G_CALLBACK (gimp_double_adjustment_update),
1145 &filmvals.hole_width);
1146
1147 filmint.advanced_adj[4] = adj =
1148 gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
1149 _("Hol_e height:"), 0, 0,
1150 filmvals.hole_height,
1151 0.0, 1.0, 0.001, 0.01, 3,
1152 TRUE, 0, 0,
1153 NULL, NULL);
1154 g_signal_connect (adj, "value-changed",
1155 G_CALLBACK (gimp_double_adjustment_update),
1156 &filmvals.hole_height);
1157
1158 filmint.advanced_adj[5] = adj =
1159 gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
1160 _("Hole sp_acing:"), 0, 0,
1161 filmvals.hole_space,
1162 0.0, 1.0, 0.001, 0.01, 3,
1163 TRUE, 0, 0,
1164 NULL, NULL);
1165 g_signal_connect (adj, "value-changed",
1166 G_CALLBACK (gimp_double_adjustment_update),
1167 &filmvals.hole_space);
1168
1169 filmint.advanced_adj[6] = adj =
1170 gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
1171 _("_Number height:"), 0, 0,
1172 filmvals.number_height,
1173 0.0, 1.0, 0.001, 0.01, 3,
1174 TRUE, 0, 0,
1175 NULL, NULL);
1176 g_signal_connect (adj, "value-changed",
1177 G_CALLBACK (gimp_double_adjustment_update),
1178 &filmvals.number_height);
1179
1180 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1181 gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1182 gtk_widget_show (hbox);
1183
1184 button = gtk_button_new_with_mnemonic (_("Re_set"));
1185 gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1186 gtk_widget_show (button);
1187
1188 g_signal_connect (button, "clicked",
1189 G_CALLBACK (film_reset_callback),
1190 NULL);
1191 }
1192
1193 static gboolean
film_dialog(gint32 image_ID)1194 film_dialog (gint32 image_ID)
1195 {
1196 GtkWidget *dlg;
1197 GtkWidget *main_vbox;
1198 GtkWidget *notebook;
1199 gboolean run;
1200
1201 gimp_ui_init (PLUG_IN_BINARY, TRUE);
1202
1203 dlg = gimp_dialog_new (_("Filmstrip"), PLUG_IN_ROLE,
1204 NULL, 0,
1205 gimp_standard_help_func, PLUG_IN_PROC,
1206
1207 _("_Cancel"), GTK_RESPONSE_CANCEL,
1208 _("_OK"), GTK_RESPONSE_OK,
1209
1210 NULL);
1211
1212 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
1213 GTK_RESPONSE_OK,
1214 GTK_RESPONSE_CANCEL,
1215 -1);
1216
1217 gimp_window_set_transient (GTK_WINDOW (dlg));
1218
1219 main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1220 gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
1221 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
1222 main_vbox, TRUE, TRUE, 0);
1223 gtk_widget_show (main_vbox);
1224
1225 notebook = gtk_notebook_new ();
1226 gtk_box_pack_start (GTK_BOX (main_vbox), notebook, TRUE, TRUE, 0);
1227
1228 create_selection_tab (notebook, image_ID);
1229 create_advanced_tab (notebook);
1230
1231 gtk_widget_show (notebook);
1232
1233 gtk_widget_show (dlg);
1234
1235 run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
1236
1237 if (run)
1238 {
1239 gint num_images = 0;
1240 gboolean iter_valid;
1241 GtkTreeIter iter;
1242
1243 for (iter_valid = gtk_tree_model_get_iter_first (filmint.image_list_film,
1244 &iter);
1245 iter_valid;
1246 iter_valid = gtk_tree_model_iter_next (filmint.image_list_film,
1247 &iter))
1248 {
1249 gint image_ID;
1250
1251 gtk_tree_model_get (filmint.image_list_film, &iter,
1252 0, &image_ID,
1253 -1);
1254
1255 if ((image_ID >= 0) && (num_images < MAX_FILM_PICTURES))
1256 filmvals.image[num_images++] = image_ID;
1257 }
1258
1259 filmvals.num_images = num_images;
1260 }
1261
1262 gtk_widget_destroy (dlg);
1263
1264 return run;
1265 }
1266
1267 static void
film_reset_callback(GtkWidget * widget,gpointer data)1268 film_reset_callback (GtkWidget *widget,
1269 gpointer data)
1270 {
1271 gint i;
1272
1273 for (i = 0; i < G_N_ELEMENTS (advanced_defaults) ; i++)
1274 gtk_adjustment_set_value (GTK_ADJUSTMENT (filmint.advanced_adj[i]),
1275 advanced_defaults[i]);
1276 }
1277
1278 static void
film_font_select_callback(GimpFontSelectButton * button,const gchar * name,gboolean closing,gpointer data)1279 film_font_select_callback (GimpFontSelectButton *button,
1280 const gchar *name,
1281 gboolean closing,
1282 gpointer data)
1283 {
1284 FilmVals *vals = (FilmVals *) data;
1285
1286 g_strlcpy (vals->number_font, name, FONT_LEN);
1287 }
1288