/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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 . */ #include "config.h" #include #include #include "libgimpwidgets/gimpwidgets.h" #include "dialogs-types.h" #include "core/gimp.h" #include "core/gimpcontainer-filter.h" #include "core/gimpcontext.h" #include "core/gimpdatafactory.h" #include "core/gimpimage.h" #include "core/gimplist.h" #include "core/gimppalette.h" #include "widgets/gimphelp-ids.h" #include "widgets/gimpviewablebox.h" #include "widgets/gimpviewabledialog.h" #include "widgets/gimpwidgets-utils.h" #include "convert-indexed-dialog.h" #include "gimp-intl.h" typedef struct _IndexedDialog IndexedDialog; struct _IndexedDialog { GimpImage *image; GimpConvertPaletteType palette_type; gint max_colors; gboolean remove_duplicates; GimpConvertDitherType dither_type; gboolean dither_alpha; gboolean dither_text_layers; GimpPalette *custom_palette; GimpConvertIndexedCallback callback; gpointer user_data; GtkWidget *dialog; GimpContext *context; GimpContainer *container; }; static void convert_dialog_free (IndexedDialog *private); static void convert_dialog_response (GtkWidget *widget, gint response_id, IndexedDialog *private); static GtkWidget * convert_dialog_palette_box (IndexedDialog *private); static gboolean convert_dialog_palette_filter (GimpObject *object, gpointer user_data); static void convert_dialog_palette_changed (GimpContext *context, GimpPalette *palette, IndexedDialog *private); /* public functions */ GtkWidget * convert_indexed_dialog_new (GimpImage *image, GimpContext *context, GtkWidget *parent, GimpConvertPaletteType palette_type, gint max_colors, gboolean remove_duplicates, GimpConvertDitherType dither_type, gboolean dither_alpha, gboolean dither_text_layers, GimpPalette *custom_palette, GimpConvertIndexedCallback callback, gpointer user_data) { IndexedDialog *private; GtkWidget *dialog; GtkWidget *button; GtkWidget *main_vbox; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *label; GtkAdjustment *adjustment; GtkWidget *spinbutton; GtkWidget *frame; GtkWidget *toggle; GtkWidget *palette_box; GtkWidget *combo; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); g_return_val_if_fail (GTK_IS_WIDGET (parent), NULL); g_return_val_if_fail (custom_palette == NULL || GIMP_IS_PALETTE (custom_palette), NULL); g_return_val_if_fail (callback != NULL, NULL); private = g_slice_new0 (IndexedDialog); private->image = image; private->palette_type = palette_type; private->max_colors = max_colors; private->remove_duplicates = remove_duplicates; private->dither_type = dither_type; private->dither_alpha = dither_alpha; private->dither_text_layers = dither_text_layers; private->custom_palette = custom_palette; private->callback = callback; private->user_data = user_data; private->dialog = dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (image), context, _("Indexed Color Conversion"), "gimp-image-convert-indexed", GIMP_ICON_CONVERT_INDEXED, _("Convert Image to Indexed Colors"), parent, gimp_standard_help_func, GIMP_HELP_IMAGE_CONVERT_INDEXED, _("_Cancel"), GTK_RESPONSE_CANCEL, _("C_onvert"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); g_object_weak_ref (G_OBJECT (dialog), (GWeakNotify) convert_dialog_free, private); g_signal_connect (dialog, "response", G_CALLBACK (convert_dialog_response), private); palette_box = convert_dialog_palette_box (private); 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 (dialog))), main_vbox, TRUE, TRUE, 0); gtk_widget_show (main_vbox); /* palette */ frame = gimp_enum_radio_frame_new_with_range (GIMP_TYPE_CONVERT_PALETTE_TYPE, GIMP_CONVERT_PALETTE_GENERATE, (palette_box ? GIMP_CONVERT_PALETTE_CUSTOM : GIMP_CONVERT_PALETTE_MONO), gtk_label_new (_("Colormap")), G_CALLBACK (gimp_radio_button_update), &private->palette_type, &button); gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (button), private->palette_type); gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); /* max n_colors */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gimp_enum_radio_frame_add (GTK_FRAME (frame), hbox, GIMP_CONVERT_PALETTE_GENERATE, TRUE); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (_("_Maximum number of colors:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); if (private->max_colors == 256 && gimp_image_has_alpha (image)) private->max_colors = 255; adjustment = (GtkAdjustment *) gtk_adjustment_new (private->max_colors, 2, 256, 1, 8, 0); spinbutton = gtk_spin_button_new (adjustment, 1.0, 0); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton); gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0); gtk_widget_show (spinbutton); g_signal_connect (adjustment, "value-changed", G_CALLBACK (gimp_int_adjustment_update), &private->max_colors); /* custom palette */ if (palette_box) { gimp_enum_radio_frame_add (GTK_FRAME (frame), palette_box, GIMP_CONVERT_PALETTE_CUSTOM, TRUE); gtk_widget_show (palette_box); } vbox = gtk_bin_get_child (GTK_BIN (frame)); toggle = gtk_check_button_new_with_mnemonic (_("_Remove unused and duplicate " "colors from colormap")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), private->remove_duplicates); gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 3); gtk_widget_show (toggle); g_signal_connect (toggle, "toggled", G_CALLBACK (gimp_toggle_button_update), &private->remove_duplicates); g_object_bind_property (button, "active", toggle, "sensitive", G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); /* dithering */ frame = gimp_frame_new (_("Dithering")); gtk_box_pack_start (GTK_BOX (main_vbox), 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); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (_("Color _dithering:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); combo = gimp_enum_combo_box_new (GIMP_TYPE_CONVERT_DITHER_TYPE); gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); gtk_widget_show (combo); gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), private->dither_type, G_CALLBACK (gimp_int_combo_box_get_active), &private->dither_type); toggle = gtk_check_button_new_with_mnemonic (_("Enable dithering of _transparency")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), private->dither_alpha); 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), &private->dither_alpha); toggle = gtk_check_button_new_with_mnemonic (_("Enable dithering of text layers")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), private->dither_text_layers); 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), &private->dither_text_layers); gimp_help_set_help_data (toggle, _("Dithering text layers will make them uneditable"), NULL); return dialog; } /* private functions */ static void convert_dialog_free (IndexedDialog *private) { if (private->container) g_object_unref (private->container); if (private->context) g_object_unref (private->context); g_slice_free (IndexedDialog, private); } static void convert_dialog_response (GtkWidget *dialog, gint response_id, IndexedDialog *private) { if (response_id == GTK_RESPONSE_OK) { private->callback (dialog, private->image, private->palette_type, private->max_colors, private->remove_duplicates, private->dither_type, private->dither_alpha, private->dither_text_layers, private->custom_palette, private->user_data); } else { gtk_widget_destroy (dialog); } } static GtkWidget * convert_dialog_palette_box (IndexedDialog *private) { Gimp *gimp = private->image->gimp; GList *list; GimpPalette *web_palette = NULL; gboolean custom_found = FALSE; /* We can't dither to > 256 colors */ private->container = gimp_container_filter (gimp_data_factory_get_container (gimp->palette_factory), convert_dialog_palette_filter, NULL); if (gimp_container_is_empty (private->container)) { g_object_unref (private->container); private->container = NULL; return NULL; } private->context = gimp_context_new (gimp, "convert-dialog", NULL); for (list = GIMP_LIST (private->container)->queue->head; list; list = g_list_next (list)) { GimpPalette *palette = list->data; /* Preferentially, the initial default is 'Web' if available */ if (web_palette == NULL && g_ascii_strcasecmp (gimp_object_get_name (palette), "Web") == 0) { web_palette = palette; } if (private->custom_palette == palette) custom_found = TRUE; } if (! custom_found) { if (web_palette) private->custom_palette = web_palette; else private->custom_palette = GIMP_LIST (private->container)->queue->head->data; } gimp_context_set_palette (private->context, private->custom_palette); g_signal_connect (private->context, "palette-changed", G_CALLBACK (convert_dialog_palette_changed), private); return gimp_palette_box_new (private->container, private->context, NULL, 4); } static gboolean convert_dialog_palette_filter (GimpObject *object, gpointer user_data) { GimpPalette *palette = GIMP_PALETTE (object); return (gimp_palette_get_n_colors (palette) > 0 && gimp_palette_get_n_colors (palette) <= 256); } static void convert_dialog_palette_changed (GimpContext *context, GimpPalette *palette, IndexedDialog *private) { if (! palette) return; if (gimp_palette_get_n_colors (palette) > 256) { gimp_message (private->image->gimp, G_OBJECT (private->dialog), GIMP_MESSAGE_WARNING, _("Cannot convert to a palette " "with more than 256 colors.")); } else { private->custom_palette = palette; } }