/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * 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 . */ /* * This plug-in generates a film roll with several images */ #include "config.h" #include #include #include #include "libpika/stdplugins-intl.h" #define PLUG_IN_PROC "plug-in-film" #define PLUG_IN_BINARY "film" #define PLUG_IN_ROLE "pika-film" /* Maximum number of pictures per film */ #define MAX_FILM_PICTURES 1024 #define COLOR_BUTTON_WIDTH 50 #define COLOR_BUTTON_HEIGHT 20 #define FONT_LEN 256 /* Data to use for the dialog */ typedef struct { GtkWidget *scales[7]; GtkTreeModel *image_list_all; GtkTreeModel *image_list_film; } FilmInterface; typedef struct _Film Film; typedef struct _FilmClass FilmClass; struct _Film { PikaPlugIn parent_instance; }; struct _FilmClass { PikaPlugInClass parent_class; }; #define FILM_TYPE (film_get_type ()) #define FILM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FILM_TYPE, Film)) GType film_get_type (void) G_GNUC_CONST; static GList * film_query_procedures (PikaPlugIn *plug_in); static PikaProcedure * film_create_procedure (PikaPlugIn *plug_in, const gchar *name); static PikaValueArray * film_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, PikaProcedureConfig *config, gpointer run_data); static PikaImage * create_new_image (guint width, guint height, PikaImageType gdtype, PikaLayer **layer); static PikaImage * film (PikaProcedureConfig *config); static gboolean check_filmvals (PikaProcedureConfig *config, GError **error); static void set_pixels (gint numpix, guchar *dst, PikaRGB *color); static guchar * create_hole_rgb (gint width, gint height, PikaRGB *film_color); static void draw_number (PikaLayer *layer, gint num, gint x, gint y, gint height, PikaFont *font); static gchar * compose_image_name (PikaImage *image); 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, GList *images, GtkWidget *hbox); static void create_selection_tab (PikaProcedureDialog *dialog, PikaImage *image); static gboolean film_dialog (PikaImage *image, PikaProcedure *procedure, PikaProcedureConfig *config); G_DEFINE_TYPE (Film, film, PIKA_TYPE_PLUG_IN) PIKA_MAIN (FILM_TYPE) DEFINE_STD_SET_I18N static FilmInterface filmint = { { NULL }, /* advanced adjustments */ NULL, NULL /* image list widgets */ }; static void film_class_init (FilmClass *klass) { PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass); plug_in_class->query_procedures = film_query_procedures; plug_in_class->create_procedure = film_create_procedure; plug_in_class->set_i18n = STD_SET_I18N; } static void film_init (Film *film) { } static GList * film_query_procedures (PikaPlugIn *plug_in) { return g_list_append (NULL, g_strdup (PLUG_IN_PROC)); } static PikaProcedure * film_create_procedure (PikaPlugIn *plug_in, const gchar *name) { PikaProcedure *procedure = NULL; const PikaRGB default_film_color = { 0.0, 0.0, 0.0, 1.0 }; const PikaRGB default_number_color = { 0.93, 0.61, 0.0, 1.0 }; if (! strcmp (name, PLUG_IN_PROC)) { procedure = pika_image_procedure_new (plug_in, name, PIKA_PDB_PROC_TYPE_PLUGIN, film_run, NULL, NULL); pika_procedure_set_image_types (procedure, "*"); pika_procedure_set_sensitivity_mask (procedure, PIKA_PROCEDURE_SENSITIVE_DRAWABLE | PIKA_PROCEDURE_SENSITIVE_DRAWABLES | PIKA_PROCEDURE_SENSITIVE_NO_DRAWABLES); pika_procedure_set_menu_label (procedure, _("_Filmstrip...")); pika_procedure_add_menu_path (procedure, "/Filters/Combine"); pika_procedure_set_documentation (procedure, _("Combine several images on a " "film strip"), "Compose several images to a roll film", name); pika_procedure_set_attribution (procedure, "Peter Kirchgessner", "Peter Kirchgessner (peter@kirchgessner.net)", "1997"); PIKA_PROC_ARG_INT (procedure, "film-height", "Film _height", "Height of film (0: fit to images)", 0, PIKA_MAX_IMAGE_SIZE, 0, G_PARAM_READWRITE); PIKA_PROC_ARG_RGB (procedure, "film-color", "_Film color", "Color of the film", TRUE, &default_film_color, G_PARAM_READWRITE); PIKA_PROC_ARG_INT (procedure, "number-start", "Start _index", "Start index for numbering", 0, G_MAXINT, 1, G_PARAM_READWRITE); PIKA_PROC_ARG_FONT (procedure, "number-font", "Number _font", "Font for drawing numbers", G_PARAM_READWRITE); PIKA_PROC_ARG_RGB (procedure, "number-color", "_Number color", "Color for numbers", TRUE, &default_number_color, G_PARAM_READWRITE); PIKA_PROC_ARG_BOOLEAN (procedure, "at-top", "At _top", "Draw numbers at top", TRUE, G_PARAM_READWRITE); PIKA_PROC_ARG_BOOLEAN (procedure, "at-bottom", "At _bottom", "Draw numbers at bottom", TRUE, G_PARAM_READWRITE); /* Arguments ignored in interactive mode. */ PIKA_PROC_ARG_OBJECT_ARRAY (procedure, "images", "Images", "Images to be used for film", PIKA_TYPE_IMAGE, G_PARAM_READWRITE); /* The more specific settings. */ PIKA_PROC_ARG_DOUBLE (procedure, "picture-height", _("Image _height"), _("As fraction of the strip height"), 0.0, 1.0, 0.695, G_PARAM_READWRITE); PIKA_PROC_ARG_DOUBLE (procedure, "picture-spacing", _("Image s_pacing"), _("The spacing between 2 images, as fraction of the strip height"), 0.0, 1.0, 0.040, G_PARAM_READWRITE); PIKA_PROC_ARG_DOUBLE (procedure, "hole-offset", _("Hole offse_t"), _("The offset from the edge of film, as fraction of the strip height"), 0.0, 1.0, 0.058, G_PARAM_READWRITE); PIKA_PROC_ARG_DOUBLE (procedure, "hole-width", _("Hole _width"), _("The width of the holes, as fraction of the strip height"), 0.0, 1.0, 0.052, G_PARAM_READWRITE); PIKA_PROC_ARG_DOUBLE (procedure, "hole-height", _("Hole hei_ght"), _("The height of the holes, as fraction of the strip height"), 0.0, 1.0, 0.081, G_PARAM_READWRITE); PIKA_PROC_ARG_DOUBLE (procedure, "hole-spacing", _("Hole _distance"), _("The distance between holes, as fraction of the strip height"), 0.0, 1.0, 0.081, G_PARAM_READWRITE); PIKA_PROC_ARG_DOUBLE (procedure, "number-height", _("_Number height"), _("The height of drawn numbers, as fraction of the strip height"), 0.0, 1.0, 0.052, G_PARAM_READWRITE); /* Auxiliary argument mostly for the GUI. */ PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "keep-height", _("F_it height to images"), "Keep maximum image height", TRUE, G_PARAM_READWRITE); /* Returned image. */ PIKA_PROC_VAL_IMAGE (procedure, "new-image", "New image", "Output image", FALSE, G_PARAM_READWRITE); } return procedure; } static PikaValueArray * film_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, PikaProcedureConfig *config, gpointer run_data) { PikaValueArray *return_vals = NULL; PikaPDBStatusType status = PIKA_PDB_SUCCESS; GError *error = NULL; gint film_height; gegl_init (NULL, NULL); switch (run_mode) { case PIKA_RUN_INTERACTIVE: { GParamSpec *spec; gint default_value; /* In interactive mode, not only do we always use the current image's * height as initial value, but also as factory default. Any other * value makes no sense as a "default". */ default_value = pika_image_get_height (image); spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config), "film-height"); G_PARAM_SPEC_INT (spec)->default_value = default_value; g_object_set (config, "film-height", default_value, NULL); if (! film_dialog (image, procedure, config)) return pika_procedure_new_return_values (procedure, PIKA_PDB_CANCEL, NULL); } break; case PIKA_RUN_NONINTERACTIVE: g_object_get (config, "film-height", &film_height, NULL); g_object_set (config, "keep-height", (film_height == 0), NULL); break; case PIKA_RUN_WITH_LAST_VALS: break; default: break; } if (! check_filmvals (config, &error)) status = PIKA_PDB_CALLING_ERROR; if (status == PIKA_PDB_SUCCESS) { PikaImage *image; pika_progress_init (_("Composing images")); image = film (config); if (! image) { status = PIKA_PDB_EXECUTION_ERROR; } else { return_vals = pika_procedure_new_return_values (procedure, status, NULL); PIKA_VALUES_SET_IMAGE (return_vals, 1, image); pika_image_undo_enable (image); pika_image_clean_all (image); if (run_mode != PIKA_RUN_NONINTERACTIVE) pika_display_new (image); } } if (! return_vals) return_vals = pika_procedure_new_return_values (procedure, status, error); return return_vals; } /* Compose a roll film image from several images */ static PikaImage * film (PikaProcedureConfig *config) { PikaObjectArray *images; gint width, height; guchar *hole; gint film_height; gint film_width; gint picture_width; gdouble picture_height; gdouble picture_space; gint picture_x0; gint picture_y0; gdouble hole_offset; gdouble hole_width; gdouble hole_height; gdouble hole_space; gint hole_x; gdouble number_height; gint num_pictures; gint number_start; gboolean at_top; gboolean at_bottom; gint picture_count; PikaRGB *number_color; PikaRGB *film_color; gboolean keep_height; gdouble f; PikaImage *image_dst; PikaImage *image_tmp; PikaLayer *layer_src; PikaLayer *layer_dst; PikaLayer *new_layer; PikaLayer *floating_sel; PikaFont *number_font; GList *layers = NULL; GList *iter2; gint i; g_object_get (config, "images", &images, "film-height", &film_height, "keep-height", &keep_height, "number-color", &number_color, "number-font", &number_font, "film-color", &film_color, "picture-height", &picture_height, "picture-spacing", &picture_space, "number-height", &number_height, "hole-offset", &hole_offset, "hole-width", &hole_width, "hole-height", &hole_height, "hole-spacing", &hole_space, "number-start", &number_start, "at-top", &at_top, "at-bottom", &at_bottom, NULL); if (images->length <= 0) return NULL; pika_context_push (); pika_context_set_foreground (number_color); pika_context_set_background (film_color); if (keep_height) /* Search maximum picture height */ { gdouble max_height = 0; for (i = 0; i < images->length; i++) { PikaImage *image = PIKA_IMAGE (images->data[i]); height = pika_image_get_height (image); if ((gdouble) height > max_height) max_height = (gdouble) height; } film_height = (int) (max_height / picture_height + 0.5); } picture_height = film_height * picture_height + 0.5; picture_space = (gdouble) film_height * picture_space + 0.5; picture_y0 = (film_height - picture_height) / 2; number_height = film_height * number_height; /* Calculate total film width */ film_width = 0; num_pictures = 0; for (i = 0; i < images->length; i++) { PikaImage *image = PIKA_IMAGE (images->data[i]); layers = pika_image_list_layers (image); /* Get scaled image size */ width = pika_image_get_width (image); height = pika_image_get_height (image); f = picture_height / (gdouble) height; picture_width = (gint) width * f; for (iter2 = layers; iter2; iter2 = g_list_next (iter2)) { if (pika_layer_is_floating_sel (iter2->data)) continue; film_width += (gint) (picture_space / 2.0); /* Leading space */ film_width += picture_width; /* Scaled image width */ film_width += (gint) (picture_space / 2.0); /* Trailing space */ num_pictures++; } g_list_free (layers); } #ifdef FILM_DEBUG g_printerr ("film_height = %d, film_width = %d\n", film_height, film_width); g_printerr ("picture_height = %f, picture_space = %f, picture_y0 = %d\n", picture_height, picture_space, picture_y0); g_printerr ("Number of pictures = %d\n", num_pictures); #endif image_dst = create_new_image ((guint) film_width, (guint) film_height, PIKA_RGB_IMAGE, &layer_dst); /* Fill film background */ pika_drawable_fill (PIKA_DRAWABLE (layer_dst), PIKA_FILL_BACKGROUND); /* Draw all the holes */ hole_offset = film_height * hole_offset; hole_width = film_height * hole_width; hole_height = film_height * hole_height; hole_space = film_height * 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 ((gint) hole_width, (gint) hole_height, film_color); if (hole) { GeglBuffer *buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer_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 (i = 0; i < images->length; i++) { PikaImage *image = PIKA_IMAGE (images->data[i]); image_tmp = pika_image_duplicate (image); width = pika_image_get_width (image_tmp); height = pika_image_get_height (image_tmp); f = ((gdouble) picture_height) / (gdouble) height; picture_width = width * f; if (pika_image_get_base_type (image_tmp) != PIKA_RGB) pika_image_convert_rgb (image_tmp); pika_image_scale (image_tmp, picture_width, picture_height); layers = pika_image_list_layers (image_tmp); for (iter2 = layers; iter2; iter2 = g_list_next (iter2)) { if (pika_layer_is_floating_sel (iter2->data)) continue; picture_x0 += picture_space / 2; layer_src = iter2->data; pika_layer_resize_to_image_size (layer_src); new_layer = pika_layer_new_from_drawable (PIKA_DRAWABLE (layer_src), image_dst); pika_image_insert_layer (image_dst, new_layer, NULL, -1); pika_layer_set_offsets (new_layer, picture_x0, picture_y0); /* Draw picture numbers */ if (number_height > 0.0 && (at_top || at_bottom)) { if (at_top) draw_number (layer_dst, number_start + picture_count, picture_x0 + picture_width/2, (hole_offset-number_height)/2, number_height, number_font); if (at_bottom) draw_number (layer_dst, number_start + picture_count, picture_x0 + picture_width/2, film_height - (hole_offset + number_height)/2, number_height, number_font); } picture_x0 += picture_width + (picture_space/2); pika_progress_update (((gdouble) (picture_count + 1)) / (gdouble) num_pictures); picture_count++; } g_list_free (layers); pika_image_delete (image_tmp); } pika_progress_update (1.0); pika_image_flatten (image_dst); /* Drawing text/numbers leaves us with a floating selection. Stop it */ floating_sel = pika_image_get_floating_sel (image_dst); if (floating_sel) pika_floating_sel_anchor (floating_sel); pika_context_pop (); pika_object_array_free (images); g_free (number_color); g_free (film_color); g_clear_object (&number_font); return image_dst; } /* Unreasonable values are reset to a default. */ /* If this is not possible, FALSE is returned. Otherwise TRUE is returned. */ static gboolean check_filmvals (PikaProcedureConfig *config, GError **error) { PikaFont *font = NULL; PikaObjectArray *images; gint film_height; gint i, j; gboolean success = FALSE; g_object_get (config, "images", &images, "number-font", &font, "film-height", &film_height, NULL); if (film_height < 10) g_object_set (config, "film-height", 10, NULL); if (font == NULL) { g_assert (PIKA_IS_FONT (pika_context_get_font ())); g_object_set (config, "number-font", pika_context_get_font (), NULL); } if (images != NULL) { for (i = 0, j = 0; i < images->length; i++) { if (pika_image_is_valid (PIKA_IMAGE (images->data[i]))) { images->data[j] = images->data[i]; j++; } } if (j > 0) { images->length = j; g_object_set (config, "images", images, NULL); success = TRUE; } } if (images == NULL || images->length == 0) g_set_error_literal (error, PIKA_PLUG_IN_ERROR, 0, _("\"Filmstrip\" cannot be run without any input images")); pika_object_array_free (images); g_clear_object (&font); return success; } /* Assigns numpix pixels starting at dst with color r,g,b */ static void set_pixels (gint numpix, guchar *dst, PikaRGB *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, PikaRGB *film_color) { guchar *hole, *top, *bottom; gint radius, length, k; if (width <= 0 || height <= 0) return NULL; 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, film_color); set_pixels (length, top + (width-length)*3, film_color); set_pixels (length, bottom, film_color); set_pixels (length, bottom + (width-length)*3, film_color); } top += width*3; bottom -= width*3; } return hole; } /* Draw the number of the picture onto the film */ static void draw_number (PikaLayer *layer, gint num, gint x, gint y, gint height, PikaFont *font) { gchar buf[32]; gint k, delta, max_delta; PikaImage *image; PikaLayer *text_layer; gint text_width, text_height, text_ascent, descent; g_snprintf (buf, sizeof (buf), "%d", num); image = pika_item_get_image (PIKA_ITEM (layer)); 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 = pika_text_get_extents_font (buf, height + delta, font, &text_width, &text_height, &text_ascent, &descent); if (success) { height += delta; break; } } text_layer = pika_text_font (image, PIKA_DRAWABLE (layer), x, y - descent / 2, buf, 1, FALSE, height, font); if (! text_layer) g_message ("draw_number: Error in drawing text\n"); } /* Create an image. Sets layer, drawable and rgn. Returns image */ static PikaImage * create_new_image (guint width, guint height, PikaImageType gdtype, PikaLayer **layer) { PikaImage *image; PikaImageBaseType gitype; if ((gdtype == PIKA_GRAY_IMAGE) || (gdtype == PIKA_GRAYA_IMAGE)) gitype = PIKA_GRAY; else if ((gdtype == PIKA_INDEXED_IMAGE) || (gdtype == PIKA_INDEXEDA_IMAGE)) gitype = PIKA_INDEXED; else gitype = PIKA_RGB; image = pika_image_new (width, height, gitype); pika_image_undo_disable (image); *layer = pika_layer_new (image, _("Background"), width, height, gdtype, 100, pika_image_get_default_new_layer_mode (image)); pika_image_insert_layer (image, *layer, NULL, 0); return image; } static gchar * compose_image_name (PikaImage *image) { gchar *image_name; gchar *name; /* Compose a name of the basename and the image-ID */ name = pika_image_get_name (image); image_name = g_strdup_printf ("%s-%d", name, pika_image_get_id (image)); 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)) { PikaImage *image; gchar *name; gtk_tree_model_get (model, &iter, 0, &image, 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, 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, GList *images, GtkWidget *hbox) { GtkWidget *vbox; GtkWidget *label; GtkWidget *scrolled_win; GtkWidget *tv; GtkWidget *button; GtkListStore *store; GtkTreeSelection *sel; GList *list; 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 (list = images; list; list = list->next) { GtkTreeIter iter; gchar *name; gtk_list_store_append (store, &iter); name = compose_image_name (list->data); gtk_list_store_set (store, &iter, 0, pika_image_get_id (list->data), 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 (PikaProcedureDialog *dialog, PikaImage *image) { GtkWidget *paned; GtkWidget *frame; GtkWidget *hbox; GList *image_list; pika_procedure_dialog_fill_frame (dialog, "fit-height-frame", "keep-height", TRUE, "film-height"); pika_procedure_dialog_fill_box (dialog, "filmstrip-box", "fit-height-frame", "film-color", NULL); pika_procedure_dialog_get_label (dialog, "filmstrip-title", _("Filmstrip"), FALSE, FALSE); pika_procedure_dialog_fill_frame (dialog, "filmstrip-frame", "filmstrip-title", FALSE, "filmstrip-box"); pika_procedure_dialog_fill_box (dialog, "numbering-box", "number-start", "number-font", "number-color", "at-top", "at-bottom", NULL); pika_procedure_dialog_get_label (dialog, "numbering-title", _("Numbering"), FALSE, FALSE); pika_procedure_dialog_fill_frame (dialog, "numbering-frame", "numbering-title", FALSE, "numbering-box"); pika_procedure_dialog_fill_box (dialog, "selection-box", "filmstrip-frame", "numbering-frame", NULL); paned = pika_procedure_dialog_fill_paned (dialog, "selection-paned", GTK_ORIENTATION_HORIZONTAL, "selection-box", NULL); /*** The right frame keeps the image selection ***/ frame = pika_frame_new (_("Image Selection")); gtk_widget_set_hexpand (frame, TRUE); 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); gtk_widget_show (hbox); /* Get a list of all image names */ image_list = pika_list_images (); filmint.image_list_all = add_image_list (TRUE, image_list, hbox); g_list_free (image_list); /* Get a list of the images used for the film */ image_list = g_list_prepend (NULL, image); filmint.image_list_film = add_image_list (FALSE, image_list, hbox); g_list_free (image_list); gtk_paned_pack2 (GTK_PANED (paned), frame, TRUE, FALSE); } static gboolean film_dialog (PikaImage *image, PikaProcedure *procedure, PikaProcedureConfig *config) { GtkWidget *dialog; gboolean run; pika_ui_init (PLUG_IN_BINARY); dialog = pika_procedure_dialog_new (procedure, PIKA_PROCEDURE_CONFIG (config), _("Filmstrip")); create_selection_tab (PIKA_PROCEDURE_DIALOG (dialog), image); /* Create Advanced tab. */ pika_procedure_dialog_fill_box (PIKA_PROCEDURE_DIALOG (dialog), "advanced-box", "picture-height", "picture-spacing", "hole-offset", "hole-width", "hole-height", "hole-spacing", "number-height", NULL); pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog), "advanced-frame-label", _("All Values are Fractions of the Strip Height"), FALSE, FALSE); pika_procedure_dialog_fill_frame (PIKA_PROCEDURE_DIALOG (dialog), "advanced-frame", "advanced-frame-label", FALSE, "advanced-box"); /* Fill the notebook. */ pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog), "advanced-tab", _("Ad_vanced"), FALSE, TRUE); pika_procedure_dialog_get_label (PIKA_PROCEDURE_DIALOG (dialog), "selection-tab", _("_Selection"), FALSE, TRUE); pika_procedure_dialog_fill_notebook (PIKA_PROCEDURE_DIALOG (dialog), "main-notebook", "selection-tab", "selection-paned", "advanced-tab", "advanced-frame", NULL); pika_procedure_dialog_fill (PIKA_PROCEDURE_DIALOG (dialog), "main-notebook", NULL); run = pika_procedure_dialog_run (PIKA_PROCEDURE_DIALOG (dialog)); if (run) { PikaObjectArray *images_array; PikaImage *images[MAX_FILM_PICTURES]; 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)) { images[num_images] = pika_image_get_by_id (image_id); num_images++; } } images_array = pika_object_array_new (PIKA_TYPE_IMAGE, (GObject **) images, num_images, TRUE); g_object_set (config, "images", images_array, NULL); pika_object_array_free (images_array); } gtk_widget_destroy (dialog); return run; }