summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-ico/ico-dialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/file-ico/ico-dialog.c')
-rw-r--r--plug-ins/file-ico/ico-dialog.c531
1 files changed, 531 insertions, 0 deletions
diff --git a/plug-ins/file-ico/ico-dialog.c b/plug-ins/file-ico/ico-dialog.c
new file mode 100644
index 0000000..84e9909
--- /dev/null
+++ b/plug-ins/file-ico/ico-dialog.c
@@ -0,0 +1,531 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * 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/>.
+ */
+
+
+#include <config.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+/* #define ICO_DBG */
+
+#include "ico.h"
+#include "ico-dialog.h"
+#include "ico-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void ico_dialog_bpp_changed (GtkWidget *combo,
+ GObject *hbox);
+static void ico_dialog_toggle_compress (GtkWidget *checkbox,
+ GObject *hbox);
+static void ico_dialog_check_compat (GtkWidget *dialog,
+ IcoSaveInfo *info);
+
+
+GtkWidget *
+ico_dialog_new (IcoSaveInfo *info)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *scrolled_window;
+ GtkWidget *viewport;
+ GtkWidget *warning;
+
+ dialog = gimp_export_dialog_new (_("Windows Icon"),
+ PLUG_IN_BINARY,
+ "plug-in-winicon");
+
+ /* We store an array that holds each icon's requested bit depth
+ with the dialog. It's queried when the dialog is closed so the
+ save routine knows what colormaps etc to generate in the saved
+ file. We store twice the number necessary because in the second
+ set, the color depths that are automatically suggested are stored
+ for later comparison.
+ */
+
+ g_object_set_data (G_OBJECT (dialog), "save_info", info);
+
+ 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 (gimp_export_dialog_get_content_area (dialog)),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ frame = gimp_frame_new (_("Icon Details"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (frame), scrolled_window);
+ gtk_widget_show (scrolled_window);
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
+ gtk_widget_show (viewport);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ g_object_set_data (G_OBJECT (dialog), "icons_vbox", vbox);
+ gtk_container_add (GTK_CONTAINER (viewport), vbox);
+ gtk_widget_show (vbox);
+
+ warning = g_object_new (GIMP_TYPE_HINT_BOX,
+ "icon-name", GIMP_ICON_DIALOG_WARNING,
+ "hint",
+ _("Large icons and compression are not supported "
+ "by all programs. Older applications may not "
+ "open this file correctly."),
+ NULL);
+ gtk_box_pack_end (GTK_BOX (main_vbox), warning, FALSE, FALSE, 0);
+ /* don't show the warning here */
+
+ g_object_set_data (G_OBJECT (dialog), "warning", warning);
+
+ return dialog;
+}
+
+static GtkWidget *
+ico_preview_new (gint32 layer)
+{
+ GtkWidget *image;
+ GdkPixbuf *pixbuf;
+ gint width = gimp_drawable_width (layer);
+ gint height = gimp_drawable_height (layer);
+
+ pixbuf = gimp_drawable_get_thumbnail (layer,
+ MIN (width, 128), MIN (height, 128),
+ GIMP_PIXBUF_SMALL_CHECKS);
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ return image;
+}
+
+/* This function creates and returns an hbox for an icon,
+ which then gets added to the dialog's main vbox. */
+static GtkWidget *
+ico_create_icon_hbox (GtkWidget *icon_preview,
+ gint32 layer,
+ gint layer_num,
+ IcoSaveInfo *info)
+{
+ static GtkSizeGroup *size = NULL;
+
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *alignment;
+ GtkWidget *combo;
+ GtkWidget *checkbox;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ alignment = gtk_alignment_new (1.0, 0.5, 0, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), alignment, FALSE, FALSE, 0);
+ gtk_widget_show (alignment);
+
+ /* To make life easier for the callbacks, we store the
+ layer's ID and stacking number with the hbox. */
+
+ g_object_set_data (G_OBJECT (hbox),
+ "icon_layer", GINT_TO_POINTER (layer));
+ g_object_set_data (G_OBJECT (hbox),
+ "icon_layer_num", GINT_TO_POINTER (layer_num));
+
+ g_object_set_data (G_OBJECT (hbox), "icon_preview", icon_preview);
+ gtk_container_add (GTK_CONTAINER (alignment), icon_preview);
+ gtk_widget_show (icon_preview);
+
+ if (! size)
+ size = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ gtk_size_group_add_widget (size, alignment);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ combo = gimp_int_combo_box_new (_("1 bpp, 1-bit alpha, 2-slot palette"), 1,
+ _("4 bpp, 1-bit alpha, 16-slot palette"), 4,
+ _("8 bpp, 1-bit alpha, 256-slot palette"), 8,
+ _("24 bpp, 1-bit alpha, no palette"), 24,
+ _("32 bpp, 8-bit alpha, no palette"), 32,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ info->depths[layer_num]);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (ico_dialog_bpp_changed),
+ hbox);
+
+ g_object_set_data (G_OBJECT (hbox), "icon_menu", combo);
+
+ gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+
+ checkbox = gtk_check_button_new_with_label (_("Compressed (PNG)"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
+ info->compress[layer_num]);
+ g_signal_connect (checkbox, "toggled",
+ G_CALLBACK (ico_dialog_toggle_compress), hbox);
+ gtk_box_pack_start (GTK_BOX (vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show (checkbox);
+
+ return hbox;
+}
+
+static GtkWidget *
+ico_dialog_get_layer_preview (GtkWidget *dialog,
+ gint32 layer)
+{
+ GtkWidget *preview;
+ GtkWidget *icon_hbox;
+ gchar key[ICO_MAXBUF];
+
+ g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
+ icon_hbox = g_object_get_data (G_OBJECT (dialog), key);
+
+ if (!icon_hbox)
+ {
+ D(("Something's wrong -- couldn't look up hbox by layer ID\n"));
+ return NULL;
+ }
+
+ preview = g_object_get_data (G_OBJECT (icon_hbox), "icon_preview");
+
+ if (!icon_hbox)
+ {
+ D(("Something's wrong -- couldn't look up preview from hbox\n"));
+ return NULL;
+ }
+
+ return preview;
+}
+
+static void
+ico_dialog_update_icon_preview (GtkWidget *dialog,
+ gint32 layer,
+ gint bpp)
+{
+ GtkWidget *preview = ico_dialog_get_layer_preview (dialog, layer);
+ GdkPixbuf *pixbuf;
+ const Babl *format;
+ gint w = gimp_drawable_width (layer);
+ gint h = gimp_drawable_height (layer);
+
+ if (! preview)
+ return;
+
+ switch (gimp_drawable_type (layer))
+ {
+ case GIMP_RGB_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ format = babl_format ("Y'A u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = gimp_drawable_get_format (layer);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ if (bpp <= 8)
+ {
+ GeglBuffer *buffer;
+ GeglBuffer *tmp;
+ gint32 image;
+ gint32 tmp_image;
+ gint32 tmp_layer;
+ guchar *buf;
+ guchar *cmap;
+ gint num_colors;
+
+ image = gimp_item_get_image (layer);
+
+ tmp_image = gimp_image_new (w, h, gimp_image_base_type (image));
+ gimp_image_undo_disable (tmp_image);
+
+ if (gimp_drawable_is_indexed (layer))
+ {
+ cmap = gimp_image_get_colormap (image, &num_colors);
+ gimp_image_set_colormap (tmp_image, cmap, num_colors);
+ g_free (cmap);
+ }
+
+ tmp_layer = gimp_layer_new (tmp_image, "temporary", w, h,
+ gimp_drawable_type (layer),
+ 100,
+ gimp_image_get_default_new_layer_mode (tmp_image));
+ gimp_image_insert_layer (tmp_image, tmp_layer, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer);
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ buf = g_malloc (w * h * 4);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);
+
+ g_object_unref (tmp);
+ g_object_unref (buffer);
+
+ if (gimp_drawable_is_indexed (layer))
+ gimp_image_convert_rgb (tmp_image);
+
+ gimp_image_convert_indexed (tmp_image,
+ GIMP_CONVERT_DITHER_FS,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ 1 << bpp, TRUE, FALSE, "dummy");
+
+ cmap = gimp_image_get_colormap (tmp_image, &num_colors);
+
+ if (num_colors == (1 << bpp) &&
+ ! ico_cmap_contains_black (cmap, num_colors))
+ {
+ /* Windows icons with color maps need the color black.
+ * We need to eliminate one more color to make room for black.
+ */
+ if (gimp_drawable_is_indexed (layer))
+ {
+ g_free (cmap);
+ cmap = gimp_image_get_colormap (image, &num_colors);
+ gimp_image_set_colormap (tmp_image, cmap, num_colors);
+ }
+ else if (gimp_drawable_is_gray (layer))
+ {
+ gimp_image_convert_grayscale (tmp_image);
+ }
+ else
+ {
+ gimp_image_convert_rgb (tmp_image);
+ }
+
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ gegl_buffer_set (tmp, GEGL_RECTANGLE (0, 0, w, h), 0,
+ format, buf, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (tmp);
+
+ if (!gimp_drawable_is_rgb (layer))
+ gimp_image_convert_rgb (tmp_image);
+
+ gimp_image_convert_indexed (tmp_image,
+ GIMP_CONVERT_DITHER_FS,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ (1 << bpp) - 1, TRUE, FALSE, "dummy");
+ }
+
+ g_free (cmap);
+ g_free (buf);
+
+ pixbuf = gimp_drawable_get_thumbnail (tmp_layer,
+ MIN (w, 128), MIN (h, 128),
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ gimp_image_delete (tmp_image);
+ }
+ else if (bpp == 24)
+ {
+ GeglBuffer *buffer;
+ GeglBuffer *tmp;
+ gint32 image;
+ gint32 tmp_image;
+ gint32 tmp_layer;
+ GimpParam *return_vals;
+ gint n_return_vals;
+
+ image = gimp_item_get_image (layer);
+
+ tmp_image = gimp_image_new (w, h, gimp_image_base_type (image));
+ gimp_image_undo_disable (tmp_image);
+
+ if (gimp_drawable_is_indexed (layer))
+ {
+ guchar *cmap;
+ gint num_colors;
+
+ cmap = gimp_image_get_colormap (image, &num_colors);
+ gimp_image_set_colormap (tmp_image, cmap, num_colors);
+ g_free (cmap);
+ }
+
+ tmp_layer = gimp_layer_new (tmp_image, "temporary", w, h,
+ gimp_drawable_type (layer),
+ 100,
+ gimp_image_get_default_new_layer_mode (tmp_image));
+ gimp_image_insert_layer (tmp_image, tmp_layer, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer);
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);
+
+ g_object_unref (tmp);
+ g_object_unref (buffer);
+
+ if (gimp_drawable_is_indexed (layer))
+ gimp_image_convert_rgb (tmp_image);
+
+ return_vals =
+ gimp_run_procedure ("plug-in-threshold-alpha", &n_return_vals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, tmp_image,
+ GIMP_PDB_DRAWABLE, tmp_layer,
+ GIMP_PDB_INT32, ICO_ALPHA_THRESHOLD,
+ GIMP_PDB_END);
+ gimp_destroy_params (return_vals, n_return_vals);
+
+ pixbuf = gimp_drawable_get_thumbnail (tmp_layer,
+ MIN (w, 128), MIN (h, 128),
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ gimp_image_delete (tmp_image);
+ }
+ else
+ {
+ pixbuf = gimp_drawable_get_thumbnail (layer,
+ MIN (w, 128), MIN (h, 128),
+ GIMP_PIXBUF_SMALL_CHECKS);
+ }
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf);
+ g_object_unref (pixbuf);
+}
+
+void
+ico_dialog_add_icon (GtkWidget *dialog,
+ gint32 layer,
+ gint layer_num)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *preview;
+ gchar key[ICO_MAXBUF];
+ IcoSaveInfo *info;
+
+ vbox = g_object_get_data (G_OBJECT (dialog), "icons_vbox");
+ info = g_object_get_data (G_OBJECT (dialog), "save_info");
+
+ preview = ico_preview_new (layer);
+ hbox = ico_create_icon_hbox (preview, layer, layer_num, info);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Let's make the hbox accessible through the layer ID */
+ g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
+ g_object_set_data (G_OBJECT (dialog), key, hbox);
+
+ ico_dialog_update_icon_preview (dialog, layer, info->depths[layer_num]);
+
+ ico_dialog_check_compat (dialog, info);
+}
+
+static void
+ico_dialog_bpp_changed (GtkWidget *combo,
+ GObject *hbox)
+{
+ GtkWidget *dialog;
+ gint32 layer;
+ gint layer_num;
+ gint bpp;
+ IcoSaveInfo *info;
+
+ dialog = gtk_widget_get_toplevel (combo);
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &bpp);
+
+ info = g_object_get_data (G_OBJECT (dialog), "save_info");
+ g_assert (info);
+
+ layer = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer"));
+ layer_num = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer_num"));
+
+ /* Update vector entry for later when we're actually saving,
+ and update the preview right away ... */
+ info->depths[layer_num] = bpp;
+ ico_dialog_update_icon_preview (dialog, layer, bpp);
+}
+
+static void
+ico_dialog_toggle_compress (GtkWidget *checkbox,
+ GObject *hbox)
+{
+ GtkWidget *dialog;
+ gint layer_num;
+ IcoSaveInfo *info;
+
+ dialog = gtk_widget_get_toplevel (checkbox);
+
+ info = g_object_get_data (G_OBJECT (dialog), "save_info");
+ g_assert (info);
+ layer_num = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer_num"));
+
+ /* Update vector entry for later when we're actually saving */
+ info->compress[layer_num] =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox));
+
+ ico_dialog_check_compat (dialog, info);
+}
+
+static void
+ico_dialog_check_compat (GtkWidget *dialog,
+ IcoSaveInfo *info)
+{
+ GtkWidget *warning;
+ gboolean warn = FALSE;
+ gint i;
+
+ for (i = 0; i < info->num_icons; i++)
+ {
+ if (gimp_drawable_width (info->layers[i]) > 255 ||
+ gimp_drawable_height (info->layers[i]) > 255 ||
+ info->compress[i])
+ {
+ warn = TRUE;
+ break;
+ }
+ }
+
+ warning = g_object_get_data (G_OBJECT (dialog), "warning");
+
+ gtk_widget_set_visible (warning, warn);
+}