summaryrefslogtreecommitdiffstats
path: root/plug-ins/common/film.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/common/film.c')
-rw-r--r--plug-ins/common/film.c1287
1 files changed, 1287 insertions, 0 deletions
diff --git a/plug-ins/common/film.c b/plug-ins/common/film.c
new file mode 100644
index 0000000..59548b9
--- /dev/null
+++ b/plug-ins/common/film.c
@@ -0,0 +1,1287 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * Film plug-in (C) 1997 Peter Kirchgessner
+ * e-mail: pkirchg@aol.com, WWW: http://members.aol.com/pkirchg
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This plug-in generates a film roll with several images
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-film"
+#define PLUG_IN_BINARY "film"
+#define PLUG_IN_ROLE "gimp-film"
+
+/* Maximum number of pictures per film */
+#define MAX_FILM_PICTURES 64
+#define COLOR_BUTTON_WIDTH 50
+#define COLOR_BUTTON_HEIGHT 20
+
+#define FONT_LEN 256
+
+/* Define how the plug-in works. Values marked (r) are with regard */
+/* to film_height (i.e. it should be a value from 0.0 to 1.0) */
+typedef struct
+{
+ gint film_height; /* height of the film */
+ GimpRGB film_color; /* color of film */
+ gdouble picture_height; /* height of picture (r) */
+ gdouble picture_space; /* space between pictures (r) */
+ gdouble hole_offset; /* distance from hole to edge of film (r) */
+ gdouble hole_width; /* width of hole (r) */
+ gdouble hole_height; /* height of holes (r) */
+ gdouble hole_space; /* distance of holes (r) */
+ gdouble number_height; /* height of picture numbering (r) */
+ gint number_start; /* number for first picture */
+ GimpRGB number_color; /* color of number */
+ gchar number_font[FONT_LEN]; /* font family to use for numbering */
+ gint number_pos[2]; /* flags where to draw numbers (top/bottom) */
+ gint keep_height; /* flag if to keep max. image height */
+ gint num_images; /* number of images */
+ gint32 image[MAX_FILM_PICTURES]; /* list of image IDs */
+} FilmVals;
+
+/* Data to use for the dialog */
+typedef struct
+{
+ GtkObject *advanced_adj[7];
+ GtkTreeModel *image_list_all;
+ GtkTreeModel *image_list_film;
+} FilmInterface;
+
+
+/* Declare local functions
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+static gint32 create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageType gdtype,
+ gint32 *layer_ID);
+
+static gchar * compose_image_name (gint32 image_ID);
+
+static gint32 film (void);
+
+static gboolean check_filmvals (void);
+
+static void set_pixels (gint numpix,
+ guchar *dst,
+ GimpRGB *color);
+
+static guchar * create_hole_rgb (gint width,
+ gint height);
+
+static void draw_number (gint32 layer_ID,
+ gint num,
+ gint x,
+ gint y,
+ gint height);
+
+
+static void add_list_item_callback (GtkWidget *widget,
+ GtkTreeSelection *sel);
+static void del_list_item_callback (GtkWidget *widget,
+ GtkTreeSelection *sel);
+
+static GtkTreeModel * add_image_list (gboolean add_box_flag,
+ gint n,
+ gint32 *image_id,
+ GtkWidget *hbox);
+
+static gboolean film_dialog (gint32 image_ID);
+static void film_reset_callback (GtkWidget *widget,
+ gpointer data);
+static void film_font_select_callback (GimpFontSelectButton *button,
+ const gchar *name,
+ gboolean closing,
+ gpointer data);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static gdouble advanced_defaults[] =
+{
+ 0.695, /* Picture height */
+ 0.040, /* Picture spacing */
+ 0.058, /* Hole offset to edge of film */
+ 0.052, /* Hole width */
+ 0.081, /* Hole height */
+ 0.081, /* Hole distance */
+ 0.052 /* Image number height */
+};
+
+static FilmVals filmvals =
+{
+ 256, /* Height of film */
+ { 0.0, 0.0, 0.0, 1.0 }, /* Color of film */
+ 0.695, /* Picture height */
+ 0.040, /* Picture spacing */
+ 0.058, /* Hole offset to edge of film */
+ 0.052, /* Hole width */
+ 0.081, /* Hole height */
+ 0.081, /* Hole distance */
+ 0.052, /* Image number height */
+ 1, /* Start index of numbering */
+ { 0.93, 0.61, 0.0, 1.0 }, /* Color of number */
+ "Monospace", /* Font family for numbering */
+ { TRUE, TRUE }, /* Numbering on top and bottom */
+ 0, /* Don't keep max. image height */
+ 0, /* Number of images */
+ { 0 } /* Input image list */
+};
+
+
+static FilmInterface filmint =
+{
+ { NULL }, /* advanced adjustments */
+ NULL, NULL /* image list widgets */
+};
+
+
+static GimpRunMode run_mode;
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (only used as default image in interactive mode)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (not used)" },
+ { GIMP_PDB_INT32, "film-height", "Height of film (0: fit to images)" },
+ { GIMP_PDB_COLOR, "film-color", "Color of the film" },
+ { GIMP_PDB_INT32, "number-start", "Start index for numbering" },
+ { GIMP_PDB_STRING, "number-font", "Font for drawing numbers" },
+ { GIMP_PDB_COLOR, "number-color", "Color for numbers" },
+ { GIMP_PDB_INT32, "at-top", "Flag for drawing numbers at top of film" },
+ { GIMP_PDB_INT32, "at-bottom", "Flag for drawing numbers at bottom of film" },
+ { GIMP_PDB_INT32, "num-images", "Number of images to be used for film" },
+ { GIMP_PDB_INT32ARRAY, "image-ids", "num-images image IDs to be used for film" }
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "new-image", "Output image" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Combine several images on a film strip"),
+ "Compose several images to a roll film",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner (peter@kirchgessner.net)",
+ "1997",
+ N_("_Filmstrip..."),
+ "INDEXED*, GRAY*, RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Combine");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint k;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_int32 = -1;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &filmvals);
+
+ /* First acquire information with a dialog */
+ if (! film_dialog (param[1].data.d_int32))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ /* Also we want to have some images to compose */
+ if ((nparams != 12) || (param[10].data.d_int32 < 1))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ filmvals.keep_height = (param[3].data.d_int32 <= 0);
+ filmvals.film_height = (filmvals.keep_height ?
+ 128 : param[3].data.d_int32);
+ filmvals.film_color = param[4].data.d_color;
+ filmvals.number_start = param[5].data.d_int32;
+ g_strlcpy (filmvals.number_font, param[6].data.d_string, FONT_LEN);
+ filmvals.number_color = param[7].data.d_color;
+ filmvals.number_pos[0] = param[8].data.d_int32;
+ filmvals.number_pos[1] = param[9].data.d_int32;
+ filmvals.num_images = param[10].data.d_int32;
+ if (filmvals.num_images > MAX_FILM_PICTURES)
+ filmvals.num_images = MAX_FILM_PICTURES;
+ for (k = 0; k < filmvals.num_images; k++)
+ filmvals.image[k] = param[11].data.d_int32array[k];
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &filmvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (! check_filmvals ())
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_progress_init (_("Composing images"));
+
+ image_ID = film ();
+
+ if (image_ID < 0)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ values[1].data.d_int32 = image_ID;
+ gimp_image_undo_enable (image_ID);
+ gimp_image_clean_all (image_ID);
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_display_new (image_ID);
+ }
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &filmvals, sizeof (FilmVals));
+ }
+
+ values[0].data.d_status = status;
+}
+
+/* Compose a roll film image from several images */
+static gint32
+film (void)
+{
+ gint width, height;
+ guchar *hole;
+ gint film_height, film_width;
+ gint picture_width, picture_height;
+ gint picture_space, picture_x0, picture_y0;
+ gint hole_offset, hole_width, hole_height, hole_space, hole_x;
+ gint number_height, num_images, num_pictures;
+ gint j, k, picture_count;
+ gdouble f;
+ gint num_layers;
+ gint32 *image_ID_src, image_ID_dst, layer_ID_src, layer_ID_dst;
+ gint image_ID_tmp;
+ gint32 *layers;
+ gint new_layer;
+ gint floating_sel;
+
+ /* initialize */
+
+ layers = NULL;
+
+ num_images = filmvals.num_images;
+ image_ID_src = filmvals.image;
+
+ if (num_images <= 0)
+ return (-1);
+
+ gimp_context_push ();
+ gimp_context_set_foreground (&filmvals.number_color);
+ gimp_context_set_background (&filmvals.film_color);
+
+ if (filmvals.keep_height) /* Search maximum picture height */
+ {
+ picture_height = 0;
+ for (j = 0; j < num_images; j++)
+ {
+ height = gimp_image_height (image_ID_src[j]);
+ if (height > picture_height) picture_height = height;
+ }
+ film_height = (int)(picture_height / filmvals.picture_height + 0.5);
+ filmvals.film_height = film_height;
+ }
+ else
+ {
+ film_height = filmvals.film_height;
+ picture_height = (int)(film_height * filmvals.picture_height + 0.5);
+ }
+
+ picture_space = (int)(film_height * filmvals.picture_space + 0.5);
+ picture_y0 = (film_height - picture_height)/2;
+
+ number_height = film_height * filmvals.number_height;
+
+ /* Calculate total film width */
+ film_width = 0;
+ num_pictures = 0;
+ for (j = 0; j < num_images; j++)
+ {
+ layers = gimp_image_get_layers (image_ID_src[j], &num_layers);
+ /* Get scaled image size */
+ width = gimp_image_width (image_ID_src[j]);
+ height = gimp_image_height (image_ID_src[j]);
+ f = ((double)picture_height) / (double)height;
+ picture_width = width * f;
+
+ for (k = 0; k < num_layers; k++)
+ {
+ if (gimp_layer_is_floating_sel (layers[k]))
+ continue;
+
+ film_width += (picture_space/2); /* Leading space */
+ film_width += picture_width; /* Scaled image width */
+ film_width += (picture_space/2); /* Trailing space */
+ num_pictures++;
+ }
+
+ g_free (layers);
+ }
+
+#ifdef FILM_DEBUG
+ g_printerr ("film_height = %d, film_width = %d\n", film_height, film_width);
+ g_printerr ("picture_height = %d, picture_space = %d, picture_y0 = %d\n",
+ picture_height, picture_space, picture_y0);
+ g_printerr ("Number of pictures = %d\n", num_pictures);
+#endif
+
+ image_ID_dst = create_new_image (_("Untitled"),
+ (guint) film_width, (guint) film_height,
+ GIMP_RGB_IMAGE, &layer_ID_dst);
+
+ /* Fill film background */
+ gimp_drawable_fill (layer_ID_dst, GIMP_FILL_BACKGROUND);
+
+ /* Draw all the holes */
+ hole_offset = film_height * filmvals.hole_offset;
+ hole_width = film_height * filmvals.hole_width;
+ hole_height = film_height * filmvals.hole_height;
+ hole_space = film_height * filmvals.hole_space;
+ hole_x = hole_space / 2;
+
+#ifdef FILM_DEBUG
+ g_printerr ("hole_x %d hole_offset %d hole_width %d hole_height %d hole_space %d\n",
+ hole_x, hole_offset, hole_width, hole_height, hole_space );
+#endif
+
+ hole = create_hole_rgb (hole_width, hole_height);
+ if (hole)
+ {
+ GeglBuffer *buffer = gimp_drawable_get_buffer (layer_ID_dst);
+
+ while (hole_x < film_width)
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (hole_x,
+ hole_offset,
+ hole_width,
+ hole_height), 0,
+ babl_format ("R'G'B' u8"), hole,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (hole_x,
+ film_height - hole_offset - hole_height,
+ hole_width,
+ hole_height), 0,
+ babl_format ("R'G'B' u8"), hole,
+ GEGL_AUTO_ROWSTRIDE);
+
+ hole_x += hole_width + hole_space;
+ }
+
+ g_object_unref (buffer);
+ g_free (hole);
+ }
+
+ /* Compose all images and layers */
+ picture_x0 = 0;
+ picture_count = 0;
+ for (j = 0; j < num_images; j++)
+ {
+ image_ID_tmp = gimp_image_duplicate (image_ID_src[j]);
+ width = gimp_image_width (image_ID_tmp);
+ height = gimp_image_height (image_ID_tmp);
+ f = ((gdouble) picture_height) / (gdouble) height;
+ picture_width = width * f;
+ if (gimp_image_base_type (image_ID_tmp) != GIMP_RGB)
+ gimp_image_convert_rgb (image_ID_tmp);
+ gimp_image_scale (image_ID_tmp, picture_width, picture_height);
+
+ layers = gimp_image_get_layers (image_ID_tmp, &num_layers);
+ for (k = 0; k < num_layers; k++)
+ {
+ if (gimp_layer_is_floating_sel (layers[k]))
+ continue;
+
+ picture_x0 += picture_space / 2;
+
+ layer_ID_src = layers[k];
+ gimp_layer_resize_to_image_size (layer_ID_src);
+ new_layer = gimp_layer_new_from_drawable (layer_ID_src,
+ image_ID_dst);
+ gimp_image_insert_layer (image_ID_dst, new_layer, -1, -1);
+ gimp_layer_set_offsets (new_layer, picture_x0, picture_y0);
+
+ /* Draw picture numbers */
+ if ((number_height > 0) &&
+ (filmvals.number_pos[0] || filmvals.number_pos[1]))
+ {
+ if (filmvals.number_pos[0])
+ draw_number (layer_ID_dst,
+ filmvals.number_start + picture_count,
+ picture_x0 + picture_width/2,
+ (hole_offset-number_height)/2, number_height);
+ if (filmvals.number_pos[1])
+ draw_number (layer_ID_dst,
+ filmvals.number_start + picture_count,
+ picture_x0 + picture_width/2,
+ film_height - (hole_offset + number_height)/2,
+ number_height);
+ }
+
+ picture_x0 += picture_width + (picture_space/2);
+
+ gimp_progress_update (((gdouble) (picture_count + 1)) /
+ (gdouble) num_pictures);
+
+ picture_count++;
+ }
+
+ g_free (layers);
+ gimp_image_delete (image_ID_tmp);
+ }
+ gimp_progress_update (1.0);
+
+ gimp_image_flatten (image_ID_dst);
+
+ /* Drawing text/numbers leaves us with a floating selection. Stop it */
+ floating_sel = gimp_image_get_floating_sel (image_ID_dst);
+ if (floating_sel != -1)
+ gimp_floating_sel_anchor (floating_sel);
+
+ gimp_context_pop ();
+
+ return image_ID_dst;
+}
+
+/* Check filmvals. Unreasonable values are reset to a default. */
+/* If this is not possible, FALSE is returned. Otherwise TRUE is returned. */
+static gboolean
+check_filmvals (void)
+{
+ if (filmvals.film_height < 10)
+ filmvals.film_height = 10;
+
+ if (filmvals.number_start < 0)
+ filmvals.number_start = 0;
+
+ if (filmvals.number_font[0] == '\0')
+ strcpy (filmvals.number_font, "Monospace");
+
+ if (filmvals.num_images < 1)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Assigns numpix pixels starting at dst with color r,g,b */
+static void
+set_pixels (gint numpix,
+ guchar *dst,
+ GimpRGB *color)
+{
+ register gint k;
+ register guchar ur, ug, ub, *udest;
+
+ ur = color->r * 255.999;
+ ug = color->g * 255.999;
+ ub = color->b * 255.999;
+ k = numpix;
+ udest = dst;
+
+ while (k-- > 0)
+ {
+ *(udest++) = ur;
+ *(udest++) = ug;
+ *(udest++) = ub;
+ }
+}
+
+/* Create the RGB-pixels that make up the hole */
+static guchar *
+create_hole_rgb (gint width,
+ gint height)
+{
+ guchar *hole, *top, *bottom;
+ gint radius, length, k;
+
+ hole = g_new (guchar, width * height * 3);
+
+ /* Fill a rectangle with white */
+ memset (hole, 255, width * height * 3);
+ radius = height / 4;
+ if (radius > width / 2)
+ radius = width / 2;
+ top = hole;
+ bottom = hole + (height-1)*width*3;
+ for (k = radius-1; k > 0; k--) /* Rounding corners */
+ {
+ length = (int)(radius - sqrt ((gdouble) (radius * radius - k * k))
+ - 0.5);
+ if (length > 0)
+ {
+ set_pixels (length, top, &filmvals.film_color);
+ set_pixels (length, top + (width-length)*3, &filmvals.film_color);
+ set_pixels (length, bottom, &filmvals.film_color);
+ set_pixels (length, bottom + (width-length)*3, &filmvals.film_color);
+ }
+ top += width*3;
+ bottom -= width*3;
+ }
+
+ return hole;
+}
+
+/* Draw the number of the picture onto the film */
+static void
+draw_number (gint32 layer_ID,
+ gint num,
+ gint x,
+ gint y,
+ gint height)
+{
+ gchar buf[32];
+ gint k, delta, max_delta;
+ gint32 image_ID;
+ gint32 text_layer_ID;
+ gint text_width, text_height, text_ascent, descent;
+ gchar *fontname = filmvals.number_font;
+
+ g_snprintf (buf, sizeof (buf), "%d", num);
+
+ image_ID = gimp_item_get_image (layer_ID);
+
+ max_delta = height / 10;
+ if (max_delta < 1)
+ max_delta = 1;
+
+ /* Numbers don't need the descent. Inquire it and move the text down */
+ for (k = 0; k < max_delta * 2 + 1; k++)
+ {
+ /* Try different font sizes if inquire of extent failed */
+ gboolean success;
+
+ delta = (k+1) / 2;
+
+ if ((k & 1) == 0)
+ delta = -delta;
+
+ success = gimp_text_get_extents_fontname (buf,
+ height + delta, GIMP_PIXELS,
+ fontname,
+ &text_width, &text_height,
+ &text_ascent, &descent);
+
+ if (success)
+ {
+ height += delta;
+ break;
+ }
+ }
+
+ text_layer_ID = gimp_text_fontname (image_ID, layer_ID,
+ x, y + descent / 2,
+ buf, 1, FALSE,
+ height, GIMP_PIXELS,
+ fontname);
+
+ if (text_layer_ID == -1)
+ g_message ("draw_number: Error in drawing text\n");
+}
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageType gdtype,
+ gint32 *layer_ID)
+{
+ gint32 image_ID;
+ GimpImageBaseType gitype;
+
+ if ((gdtype == GIMP_GRAY_IMAGE) || (gdtype == GIMP_GRAYA_IMAGE))
+ gitype = GIMP_GRAY;
+ else if ((gdtype == GIMP_INDEXED_IMAGE) || (gdtype == GIMP_INDEXEDA_IMAGE))
+ gitype = GIMP_INDEXED;
+ else
+ gitype = GIMP_RGB;
+
+ image_ID = gimp_image_new (width, height, gitype);
+ gimp_image_set_filename (image_ID, filename);
+
+ gimp_image_undo_disable (image_ID);
+ *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ return image_ID;
+}
+
+static gchar *
+compose_image_name (gint32 image_ID)
+{
+ gchar *image_name;
+ gchar *name;
+
+ /* Compose a name of the basename and the image-ID */
+
+ name = gimp_image_get_name (image_ID);
+
+ image_name = g_strdup_printf ("%s-%d", name, image_ID);
+
+ g_free (name);
+
+ return image_name;
+}
+
+static void
+add_list_item_callback (GtkWidget *widget,
+ GtkTreeSelection *sel)
+{
+ GtkTreeModel *model;
+ GList *paths;
+ GList *list;
+
+ paths = gtk_tree_selection_get_selected_rows (sel, &model);
+
+ for (list = paths; list; list = g_list_next (list))
+ {
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter (model, &iter, list->data))
+ {
+ gint32 image_ID;
+ gchar *name;
+
+ gtk_tree_model_get (model, &iter,
+ 0, &image_ID,
+ 1, &name,
+ -1);
+
+ gtk_list_store_append (GTK_LIST_STORE (filmint.image_list_film),
+ &iter);
+
+ gtk_list_store_set (GTK_LIST_STORE (filmint.image_list_film),
+ &iter,
+ 0, image_ID,
+ 1, name,
+ -1);
+
+ g_free (name);
+ }
+
+ gtk_tree_path_free (list->data);
+ }
+
+ g_list_free (paths);
+}
+
+static void
+del_list_item_callback (GtkWidget *widget,
+ GtkTreeSelection *sel)
+{
+ GtkTreeModel *model;
+ GList *paths;
+ GList *references = NULL;
+ GList *list;
+
+ paths = gtk_tree_selection_get_selected_rows (sel, &model);
+
+ for (list = paths; list; list = g_list_next (list))
+ {
+ GtkTreeRowReference *ref;
+
+ ref = gtk_tree_row_reference_new (model, list->data);
+ references = g_list_prepend (references, ref);
+ gtk_tree_path_free (list->data);
+ }
+
+ g_list_free (paths);
+
+ for (list = references; list; list = g_list_next (list))
+ {
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_row_reference_get_path (list->data);
+
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+
+ gtk_tree_path_free (path);
+ gtk_tree_row_reference_free (list->data);
+ }
+
+ g_list_free (references);
+}
+
+static GtkTreeModel *
+add_image_list (gboolean add_box_flag,
+ gint n,
+ gint32 *image_id,
+ GtkWidget *hbox)
+{
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *scrolled_win;
+ GtkWidget *tv;
+ GtkWidget *button;
+ GtkListStore *store;
+ GtkTreeSelection *sel;
+ gint i;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ label = gtk_label_new (add_box_flag ?
+ _("Available images:") :
+ _("On film:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_win);
+
+ store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
+ tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tv), FALSE);
+
+ if (! add_box_flag)
+ gtk_tree_view_set_reorderable (GTK_TREE_VIEW (tv), TRUE);
+
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv), 0, NULL,
+ gtk_cell_renderer_text_new (),
+ "text", 1,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (scrolled_win), tv);
+ gtk_widget_show (tv);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
+
+ for (i = 0; i < n; i++)
+ {
+ GtkTreeIter iter;
+ gchar *name;
+
+ gtk_list_store_append (store, &iter);
+
+ name = compose_image_name (image_id[i]);
+
+ gtk_list_store_set (store, &iter,
+ 0, image_id[i],
+ 1, name,
+ -1);
+
+ g_free (name);
+ }
+
+ button = gtk_button_new_with_mnemonic (add_box_flag ?
+ _("_Add") : _("_Remove"));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ add_box_flag ?
+ G_CALLBACK (add_list_item_callback) :
+ G_CALLBACK (del_list_item_callback),
+ sel);
+
+ return GTK_TREE_MODEL (store);
+}
+
+static void
+create_selection_tab (GtkWidget *notebook,
+ gint32 image_ID)
+{
+ GimpColorConfig *config;
+ GtkSizeGroup *group;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *frame;
+ GtkWidget *toggle;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ GtkWidget *button;
+ GtkWidget *font_button;
+ gint32 *image_id_list;
+ gint nimages, j;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), hbox,
+ gtk_label_new_with_mnemonic (_("Selection")));
+ gtk_widget_show (hbox);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* Film height/color */
+ frame = gimp_frame_new (_("Filmstrip"));
+ gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* Keep maximum image height */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Fit height to images"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &filmvals.keep_height);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Film height */
+ adj = GTK_ADJUSTMENT (gtk_adjustment_new (filmvals.film_height, 10,
+ GIMP_MAX_IMAGE_SIZE, 1, 10, 0));
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Height:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ gtk_size_group_add_widget (group, label);
+ g_object_unref (group);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &filmvals.film_height);
+
+ g_object_bind_property (toggle, "active",
+ spinbutton, "sensitive",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+ g_object_bind_property (toggle, "active",
+ /* FIXME: eeeeeek */
+ g_list_nth_data (gtk_container_get_children (GTK_CONTAINER (table)), 1), "sensitive",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ filmvals.keep_height);
+
+ /* Film color */
+ button = gimp_color_button_new (_("Select Film Color"),
+ COLOR_BUTTON_WIDTH, COLOR_BUTTON_HEIGHT,
+ &filmvals.film_color,
+ GIMP_COLOR_AREA_FLAT);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Co_lor:"), 0.0, 0.5,
+ button, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ g_signal_connect (button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &filmvals.film_color);
+
+ config = gimp_get_color_configuration ();
+ gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (button), config);
+
+ /* Film numbering: Startindex/Font/color */
+ frame = gimp_frame_new (_("Numbering"));
+ gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Startindex */
+ adj = GTK_ADJUSTMENT (gtk_adjustment_new (filmvals.number_start, 0,
+ GIMP_MAX_IMAGE_SIZE, 1, 10, 0));
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Start _index:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ gtk_size_group_add_widget (group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &filmvals.number_start);
+
+ /* Fontfamily for numbering */
+ font_button = gimp_font_select_button_new (NULL, filmvals.number_font);
+ g_signal_connect (font_button, "font-set",
+ G_CALLBACK (film_font_select_callback), &filmvals);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Font:"), 0.0, 0.5,
+ font_button, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ /* Numbering color */
+ button = gimp_color_button_new (_("Select Number Color"),
+ COLOR_BUTTON_WIDTH, COLOR_BUTTON_HEIGHT,
+ &filmvals.number_color,
+ GIMP_COLOR_AREA_FLAT);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Co_lor:"), 0.0, 0.5,
+ button, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ g_signal_connect (button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &filmvals.number_color);
+
+ gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (button), config);
+ g_object_unref (config);
+
+ for (j = 0; j < 2; j++)
+ {
+ toggle = gtk_check_button_new_with_mnemonic (j ? _("At _bottom")
+ : _("At _top"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ filmvals.number_pos[j]);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &filmvals.number_pos[j]);
+ }
+
+
+ /*** The right frame keeps the image selection ***/
+ frame = gimp_frame_new (_("Image Selection"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+
+ /* Get a list of all image names */
+ image_id_list = gimp_image_list (&nimages);
+ filmint.image_list_all = add_image_list (TRUE, nimages, image_id_list, hbox);
+
+ /* Get a list of the images used for the film */
+ filmint.image_list_film = add_image_list (FALSE, 1, &image_ID, hbox);
+
+ gtk_widget_show (hbox);
+}
+
+static void
+create_advanced_tab (GtkWidget *notebook)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkObject *adj;
+ GtkWidget *button;
+ gint row;
+
+ frame = gimp_frame_new (_("All Values are Fractions of the Strip Height"));
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame,
+ gtk_label_new_with_mnemonic (_("Ad_vanced")));
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (7, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 1, 12);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 5, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ row = 0;
+
+ filmint.advanced_adj[0] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Image _height:"), 0, 0,
+ filmvals.picture_height,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.picture_height);
+
+ filmint.advanced_adj[1] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Image spac_ing:"), 0, 0,
+ filmvals.picture_space,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.picture_space);
+
+ filmint.advanced_adj[2] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Hole offset:"), 0, 0,
+ filmvals.hole_offset,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.hole_offset);
+
+ filmint.advanced_adj[3] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Ho_le width:"), 0, 0,
+ filmvals.hole_width,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.hole_width);
+
+ filmint.advanced_adj[4] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Hol_e height:"), 0, 0,
+ filmvals.hole_height,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.hole_height);
+
+ filmint.advanced_adj[5] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Hole sp_acing:"), 0, 0,
+ filmvals.hole_space,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.hole_space);
+
+ filmint.advanced_adj[6] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Number height:"), 0, 0,
+ filmvals.number_height,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.number_height);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic (_("Re_set"));
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (film_reset_callback),
+ NULL);
+}
+
+static gboolean
+film_dialog (gint32 image_ID)
+{
+ GtkWidget *dlg;
+ GtkWidget *main_vbox;
+ GtkWidget *notebook;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dlg = gimp_dialog_new (_("Filmstrip"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (main_vbox), notebook, TRUE, TRUE, 0);
+
+ create_selection_tab (notebook, image_ID);
+ create_advanced_tab (notebook);
+
+ gtk_widget_show (notebook);
+
+ gtk_widget_show (dlg);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ gint num_images = 0;
+ gboolean iter_valid;
+ GtkTreeIter iter;
+
+ for (iter_valid = gtk_tree_model_get_iter_first (filmint.image_list_film,
+ &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (filmint.image_list_film,
+ &iter))
+ {
+ gint image_ID;
+
+ gtk_tree_model_get (filmint.image_list_film, &iter,
+ 0, &image_ID,
+ -1);
+
+ if ((image_ID >= 0) && (num_images < MAX_FILM_PICTURES))
+ filmvals.image[num_images++] = image_ID;
+ }
+
+ filmvals.num_images = num_images;
+ }
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
+
+static void
+film_reset_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (advanced_defaults) ; i++)
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (filmint.advanced_adj[i]),
+ advanced_defaults[i]);
+}
+
+static void
+film_font_select_callback (GimpFontSelectButton *button,
+ const gchar *name,
+ gboolean closing,
+ gpointer data)
+{
+ FilmVals *vals = (FilmVals *) data;
+
+ g_strlcpy (vals->number_font, name, FONT_LEN);
+}