/* 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 * * 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 "libpikacolor/pikacolor.h" #include "libpikawidgets/pikawidgets.h" #include "widgets-types.h" #include "core/pika.h" #include "core/pikacontext.h" #include "core/pikaimage.h" #include "core/pikaimage-colormap.h" #include "core/pikapalette.h" #include "core/pikaprojection.h" #include "pikacolordialog.h" #include "pikacolormapeditor.h" #include "pikacolormapselection.h" #include "pikadialogfactory.h" #include "pikadocked.h" #include "pikamenufactory.h" #include "pikawidgets-utils.h" #include "pika-intl.h" static void pika_colormap_editor_docked_iface_init (PikaDockedInterface *face); static void pika_colormap_editor_constructed (GObject *object); static void pika_colormap_editor_dispose (GObject *object); static void pika_colormap_editor_unmap (GtkWidget *widget); static void pika_colormap_editor_set_context (PikaDocked *docked, PikaContext *context); static void pika_colormap_editor_color_update (PikaColorDialog *dialog, const PikaRGB *color, PikaColorDialogState state, PikaColormapEditor *editor); static gboolean pika_colormap_editor_entry_button_press (GtkWidget *widget, GdkEvent *event, gpointer user_data); static gboolean pika_colormap_editor_entry_popup (GtkWidget *widget, gpointer user_data); static void pika_colormap_editor_color_clicked (PikaColormapEditor *editor, PikaPaletteEntry *entry, GdkModifierType state); static void pika_colormap_editor_notify_index (PikaColormapSelection *selection, const GParamSpec *pspec, PikaColormapEditor *editor); G_DEFINE_TYPE_WITH_CODE (PikaColormapEditor, pika_colormap_editor, PIKA_TYPE_IMAGE_EDITOR, G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED, pika_colormap_editor_docked_iface_init)) #define parent_class pika_colormap_editor_parent_class static PikaDockedInterface *parent_docked_iface = NULL; static void pika_colormap_editor_class_init (PikaColormapEditorClass* klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->constructed = pika_colormap_editor_constructed; object_class->dispose = pika_colormap_editor_dispose; widget_class->unmap = pika_colormap_editor_unmap; } static void pika_colormap_editor_docked_iface_init (PikaDockedInterface *iface) { parent_docked_iface = g_type_interface_peek_parent (iface); if (! parent_docked_iface) parent_docked_iface = g_type_default_interface_peek (PIKA_TYPE_DOCKED); iface->set_context = pika_colormap_editor_set_context; } static void pika_colormap_editor_init (PikaColormapEditor *editor) { } static void pika_colormap_editor_constructed (GObject *object) { PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (object); GdkModifierType extend_mask; GdkModifierType modify_mask; G_OBJECT_CLASS (parent_class)->constructed (object); /* Editor buttons. */ extend_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (object), GDK_MODIFIER_INTENT_EXTEND_SELECTION); modify_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (object), GDK_MODIFIER_INTENT_MODIFY_SELECTION); pika_editor_add_action_button (PIKA_EDITOR (editor), "colormap", "colormap-edit-color", NULL); pika_editor_add_action_button (PIKA_EDITOR (editor), "colormap", "colormap-delete-color", NULL); pika_editor_add_action_button (PIKA_EDITOR (editor), "colormap", "colormap-add-color-from-fg", "colormap-add-color-from-bg", pika_get_toggle_behavior_mask (), NULL); pika_editor_add_action_button (PIKA_EDITOR (editor), "colormap", "colormap-selection-replace", "colormap-selection-add", extend_mask, "colormap-selection-subtract", modify_mask, "colormap-selection-intersect", extend_mask | modify_mask, NULL); } static void pika_colormap_editor_dispose (GObject *object) { PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (object); g_clear_pointer (&editor->color_dialog, gtk_widget_destroy); G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_colormap_editor_unmap (GtkWidget *widget) { PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (widget); if (editor->color_dialog) gtk_widget_hide (editor->color_dialog); GTK_WIDGET_CLASS (parent_class)->unmap (widget); } static void pika_colormap_editor_set_context (PikaDocked *docked, PikaContext *context) { PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (docked); parent_docked_iface->set_context (docked, context); if (editor->selection) gtk_widget_destroy (editor->selection); editor->selection = NULL; /* Main selection widget. */ if (context) { editor->selection = pika_colormap_selection_new (context); gtk_box_pack_start (GTK_BOX (editor), editor->selection, TRUE, TRUE, 0); gtk_widget_show (editor->selection); g_signal_connect_swapped (editor->selection, "color-clicked", G_CALLBACK (pika_colormap_editor_color_clicked), editor); g_signal_connect_swapped (editor->selection, "color-activated", G_CALLBACK (pika_colormap_editor_edit_color), editor); g_signal_connect (editor->selection, "button-press-event", G_CALLBACK (pika_colormap_editor_entry_button_press), editor); g_signal_connect (editor->selection, "popup-menu", G_CALLBACK (pika_colormap_editor_entry_popup), editor); g_signal_connect (editor->selection, "notify::index", G_CALLBACK (pika_colormap_editor_notify_index), editor); } } /* public functions */ GtkWidget * pika_colormap_editor_new (PikaMenuFactory *menu_factory) { g_return_val_if_fail (PIKA_IS_MENU_FACTORY (menu_factory), NULL); return g_object_new (PIKA_TYPE_COLORMAP_EDITOR, "menu-factory", menu_factory, "menu-identifier", "", "ui-path", "/colormap-popup", NULL); } void pika_colormap_editor_edit_color (PikaColormapEditor *editor) { PikaImage *image; PikaRGB color; gchar *desc; gint index; g_return_if_fail (PIKA_IS_COLORMAP_EDITOR (editor)); image = PIKA_IMAGE_EDITOR (editor)->image; index = pika_colormap_selection_get_index (PIKA_COLORMAP_SELECTION (editor->selection), NULL); if (index == -1) /* No colormap. */ return; pika_image_get_colormap_entry (image, index, &color); desc = g_strdup_printf (_("Edit colormap entry #%d"), index); if (! editor->color_dialog) { editor->color_dialog = pika_color_dialog_new (PIKA_VIEWABLE (image), PIKA_IMAGE_EDITOR (editor)->context, FALSE, _("Edit Colormap Entry"), PIKA_ICON_COLORMAP, desc, GTK_WIDGET (editor), pika_dialog_factory_get_singleton (), "pika-colormap-editor-color-dialog", (const PikaRGB *) &color, TRUE, FALSE); g_signal_connect (editor->color_dialog, "destroy", G_CALLBACK (gtk_widget_destroyed), &editor->color_dialog); g_signal_connect (editor->color_dialog, "update", G_CALLBACK (pika_colormap_editor_color_update), editor); } else { pika_viewable_dialog_set_viewables (PIKA_VIEWABLE_DIALOG (editor->color_dialog), g_list_prepend (NULL, image), PIKA_IMAGE_EDITOR (editor)->context); g_object_set (editor->color_dialog, "description", desc, NULL); pika_color_dialog_set_color (PIKA_COLOR_DIALOG (editor->color_dialog), &color); if (! gtk_widget_get_visible (editor->color_dialog)) pika_dialog_factory_position_dialog (pika_dialog_factory_get_singleton (), "pika-colormap-editor-color-dialog", editor->color_dialog, pika_widget_get_monitor (GTK_WIDGET (editor))); } g_free (desc); gtk_window_present (GTK_WINDOW (editor->color_dialog)); } void pika_colormap_editor_delete_color (PikaColormapEditor *editor) { PikaColormapSelection *selection; PikaImage *image; gint index; g_return_if_fail (PIKA_IS_COLORMAP_EDITOR (editor)); g_return_if_fail (pika_colormap_editor_is_color_deletable (editor)); image = PIKA_IMAGE_EDITOR (editor)->image; selection = PIKA_COLORMAP_SELECTION (editor->selection); index = pika_colormap_selection_get_index (selection, NULL); pika_image_delete_colormap_entry (image, index, TRUE); } gboolean pika_colormap_editor_is_color_deletable (PikaColormapEditor *editor) { PikaColormapSelection *selection; PikaImage *image; gint index; g_return_val_if_fail (PIKA_IS_COLORMAP_EDITOR (editor), FALSE); image = PIKA_IMAGE_EDITOR (editor)->image; selection = PIKA_COLORMAP_SELECTION (editor->selection); index = pika_colormap_selection_get_index (selection, NULL); if (index == -1) /* No colormap. */ return FALSE; else return ! pika_image_colormap_is_index_used (image, index); } gint pika_colormap_editor_get_index (PikaColormapEditor *editor, const PikaRGB *search) { g_return_val_if_fail (PIKA_IS_COLORMAP_EDITOR (editor), 0); return pika_colormap_selection_get_index (PIKA_COLORMAP_SELECTION (editor->selection), search); } gboolean pika_colormap_editor_set_index (PikaColormapEditor *editor, gint index, PikaRGB *color) { g_return_val_if_fail (PIKA_IS_COLORMAP_EDITOR (editor), FALSE); return pika_colormap_selection_set_index (PIKA_COLORMAP_SELECTION (editor->selection), index, color); } gint pika_colormap_editor_max_index (PikaColormapEditor *editor) { g_return_val_if_fail (PIKA_IS_COLORMAP_EDITOR (editor), -1); return pika_colormap_selection_max_index (PIKA_COLORMAP_SELECTION (editor->selection)); } static void pika_colormap_editor_color_update (PikaColorDialog *dialog, const PikaRGB *color, PikaColorDialogState state, PikaColormapEditor *editor) { PikaImageEditor *image_editor = PIKA_IMAGE_EDITOR (editor); PikaImage *image = image_editor->image; gboolean push_undo = FALSE; switch (state) { case PIKA_COLOR_DIALOG_OK: push_undo = TRUE; if (state & pika_get_toggle_behavior_mask ()) pika_context_set_background (image_editor->context, color); else pika_context_set_foreground (image_editor->context, color); /* Fall through */ case PIKA_COLOR_DIALOG_CANCEL: gtk_widget_hide (editor->color_dialog); break; case PIKA_COLOR_DIALOG_UPDATE: break; } if (image) { gint col_index; col_index = pika_colormap_selection_get_index (PIKA_COLORMAP_SELECTION (editor->selection), NULL); if (push_undo) { PikaRGB old_color; pika_color_selection_get_old_color ( PIKA_COLOR_SELECTION (dialog->selection), &old_color); /* Restore old color for undo */ pika_image_set_colormap_entry (image, col_index, &old_color, FALSE); } pika_image_set_colormap_entry (image, col_index, color, push_undo); if (push_undo) pika_image_flush (image); else pika_projection_flush (pika_image_get_projection (image)); } } static gboolean pika_colormap_editor_entry_button_press (GtkWidget *widget, GdkEvent *event, gpointer user_data) { if (gdk_event_triggers_context_menu (event)) { pika_editor_popup_menu_at_pointer (PIKA_EDITOR (user_data), event); return GDK_EVENT_STOP; } return GDK_EVENT_PROPAGATE; } static gboolean pika_colormap_editor_entry_popup (GtkWidget *widget, gpointer user_data) { PikaColormapEditor *editor = PIKA_COLORMAP_EDITOR (user_data); PikaColormapSelection *selection = PIKA_COLORMAP_SELECTION (widget); PikaPaletteEntry *selected; GdkRectangle rect; selected = pika_colormap_selection_get_selected_entry (selection); if (!selected) return GDK_EVENT_PROPAGATE; pika_colormap_selection_get_entry_rect (selection, selected, &rect); return pika_editor_popup_menu_at_rect (PIKA_EDITOR (editor), gtk_widget_get_window (widget), &rect, GDK_GRAVITY_CENTER, GDK_GRAVITY_NORTH_WEST, NULL); } static void pika_colormap_editor_color_clicked (PikaColormapEditor *editor, PikaPaletteEntry *entry, GdkModifierType state) { PikaImageEditor *image_editor = PIKA_IMAGE_EDITOR (editor); if (state & pika_get_toggle_behavior_mask ()) pika_context_set_background (image_editor->context, &entry->color); else pika_context_set_foreground (image_editor->context, &entry->color); } static void pika_colormap_editor_notify_index (PikaColormapSelection *selection, const GParamSpec *pspec, PikaColormapEditor *editor) { g_return_if_fail (PIKA_IS_COLORMAP_EDITOR (editor)); pika_editor_set_action_sensitive (PIKA_EDITOR (editor), "colormap", "colormap-delete-color", pika_colormap_editor_is_color_deletable (editor), _("The color is used in this indexed image")); }