/* 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 #include "libpikabase/pikabase.h" #include "libpikacolor/pikacolor.h" #include "libpikawidgets/pikawidgets.h" #include "actions-types.h" #include "operations/layer-modes/pika-layer-modes.h" #include "core/pika.h" #include "core/pikabrushgenerated.h" #include "core/pikacontext.h" #include "core/pikadatafactory.h" #include "core/pikalist.h" #include "core/pikapaintinfo.h" #include "core/pikatoolinfo.h" #include "paint/pikapaintoptions.h" #include "widgets/pikadialogfactory.h" #include "widgets/pikasessioninfo.h" #include "widgets/pikapaletteeditor.h" #include "widgets/pikacolormapeditor.h" #include "actions.h" #include "context-commands.h" #include "pika-intl.h" /* local function prototypes */ static void context_select_object (PikaActionSelectType select_type, PikaContext *context, PikaContainer *container); static gint context_paint_mode_index (PikaLayerMode paint_mode, const PikaLayerMode *modes, gint n_modes); static void context_select_color (PikaActionSelectType select_type, PikaRGB *color, gboolean use_colormap, gboolean use_palette); static gint context_get_color_index (gboolean use_colormap, gboolean use_palette, const PikaRGB *color); static gint context_max_color_index (gboolean use_colormap, gboolean use_palette); static gboolean context_set_color_index (gint index, gboolean use_colormap, gboolean use_palette, PikaRGB *color); static PikaPaletteEditor * context_get_palette_editor (void); static PikaColormapEditor * context_get_colormap_editor (void); /* public functions */ void context_colors_default_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; return_if_no_context (context, data); pika_context_set_default_colors (context); } void context_colors_swap_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; return_if_no_context (context, data); pika_context_swap_colors (context); } #define SELECT_COLOR_CMD_CALLBACK(name, fgbg, use_colormap, use_palette) \ void \ context_##name##_##fgbg##ground_cmd_callback (PikaAction *action, \ GVariant *value, \ gpointer data) \ { \ PikaContext *context; \ PikaRGB color; \ PikaActionSelectType select_type; \ return_if_no_context (context, data); \ \ select_type = (PikaActionSelectType) g_variant_get_int32 (value); \ \ pika_context_get_##fgbg##ground (context, &color); \ context_select_color (select_type, &color, \ use_colormap, use_palette); \ pika_context_set_##fgbg##ground (context, &color); \ } SELECT_COLOR_CMD_CALLBACK (palette, fore, FALSE, TRUE) SELECT_COLOR_CMD_CALLBACK (palette, back, FALSE, TRUE) SELECT_COLOR_CMD_CALLBACK (colormap, fore, TRUE, FALSE) SELECT_COLOR_CMD_CALLBACK (colormap, back, TRUE, FALSE) SELECT_COLOR_CMD_CALLBACK (swatch, fore, TRUE, TRUE) SELECT_COLOR_CMD_CALLBACK (swatch, back, TRUE, TRUE) void context_foreground_red_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_foreground (context, &color); color.r = action_select_value (select_type, color.r, 0.0, 1.0, 1.0, 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); pika_context_set_foreground (context, &color); } void context_foreground_green_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_foreground (context, &color); color.g = action_select_value (select_type, color.g, 0.0, 1.0, 1.0, 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); pika_context_set_foreground (context, &color); } void context_foreground_blue_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_foreground (context, &color); color.b = action_select_value (select_type, color.b, 0.0, 1.0, 1.0, 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); pika_context_set_foreground (context, &color); } void context_background_red_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_background (context, &color); color.r = action_select_value (select_type, color.r, 0.0, 1.0, 1.0, 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); pika_context_set_background (context, &color); } void context_background_green_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_background (context, &color); color.g = action_select_value (select_type, color.g, 0.0, 1.0, 1.0, 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); pika_context_set_background (context, &color); } void context_background_blue_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_background (context, &color); color.b = action_select_value (select_type, color.b, 0.0, 1.0, 1.0, 1.0 / 255.0, 0.01, 0.1, 0.0, FALSE); pika_context_set_background (context, &color); } void context_foreground_hue_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaHSV hsv; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_foreground (context, &color); pika_rgb_to_hsv (&color, &hsv); hsv.h = action_select_value (select_type, hsv.h, 0.0, 1.0, 1.0, 1.0 / 360.0, 0.01, 0.1, 0.0, FALSE); pika_hsv_to_rgb (&hsv, &color); pika_context_set_foreground (context, &color); } void context_foreground_saturation_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaHSV hsv; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_foreground (context, &color); pika_rgb_to_hsv (&color, &hsv); hsv.s = action_select_value (select_type, hsv.s, 0.0, 1.0, 1.0, 0.01, 0.01, 0.1, 0.0, FALSE); pika_hsv_to_rgb (&hsv, &color); pika_context_set_foreground (context, &color); } void context_foreground_value_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaHSV hsv; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_foreground (context, &color); pika_rgb_to_hsv (&color, &hsv); hsv.v = action_select_value (select_type, hsv.v, 0.0, 1.0, 1.0, 0.01, 0.01, 0.1, 0.0, FALSE); pika_hsv_to_rgb (&hsv, &color); pika_context_set_foreground (context, &color); } void context_background_hue_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaHSV hsv; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_background (context, &color); pika_rgb_to_hsv (&color, &hsv); hsv.h = action_select_value (select_type, hsv.h, 0.0, 1.0, 1.0, 1.0 / 360.0, 0.01, 0.1, 0.0, FALSE); pika_hsv_to_rgb (&hsv, &color); pika_context_set_background (context, &color); } void context_background_saturation_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaHSV hsv; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_background (context, &color); pika_rgb_to_hsv (&color, &hsv); hsv.s = action_select_value (select_type, hsv.s, 0.0, 1.0, 1.0, 0.01, 0.01, 0.1, 0.0, FALSE); pika_hsv_to_rgb (&hsv, &color); pika_context_set_background (context, &color); } void context_background_value_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaRGB color; PikaHSV hsv; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); pika_context_get_background (context, &color); pika_rgb_to_hsv (&color, &hsv); hsv.v = action_select_value (select_type, hsv.v, 0.0, 1.0, 1.0, 0.01, 0.01, 0.1, 0.0, FALSE); pika_hsv_to_rgb (&hsv, &color); pika_context_set_background (context, &color); } void context_opacity_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaToolInfo *tool_info; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); tool_info = pika_context_get_tool (context); if (tool_info && PIKA_IS_TOOL_OPTIONS (tool_info->tool_options)) { action_select_property (select_type, action_data_get_display (data), G_OBJECT (tool_info->tool_options), "opacity", 1.0 / 255.0, 0.01, 0.1, 0.1, FALSE); } } void context_paint_mode_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaToolInfo *tool_info; PikaLayerMode *modes; gint n_modes; PikaLayerMode paint_mode; gint index; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); paint_mode = pika_context_get_paint_mode (context); modes = pika_layer_mode_get_context_array (paint_mode, PIKA_LAYER_MODE_CONTEXT_PAINT, &n_modes); index = context_paint_mode_index (paint_mode, modes, n_modes); index = action_select_value (select_type, index, 0, n_modes - 1, 0, 0.0, 1.0, 1.0, 0.0, FALSE); paint_mode = modes[index]; g_free (modes); pika_context_set_paint_mode (context, paint_mode); tool_info = pika_context_get_tool (context); if (tool_info && PIKA_IS_TOOL_OPTIONS (tool_info->tool_options)) { PikaDisplay *display; const char *value_desc; pika_enum_get_value (PIKA_TYPE_LAYER_MODE, paint_mode, NULL, NULL, &value_desc, NULL); display = action_data_get_display (data); if (value_desc && display) { action_message (display, G_OBJECT (tool_info->tool_options), _("Paint Mode: %s"), value_desc); } } } void context_tool_select_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); context_select_object (select_type, context, context->pika->tool_info_list); } void context_brush_select_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); context_select_object (select_type, context, pika_data_factory_get_container (context->pika->brush_factory)); } void context_pattern_select_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); context_select_object (select_type, context, pika_data_factory_get_container (context->pika->pattern_factory)); } void context_palette_select_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); context_select_object (select_type, context, pika_data_factory_get_container (context->pika->palette_factory)); } void context_gradient_select_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); context_select_object (select_type, context, pika_data_factory_get_container (context->pika->gradient_factory)); } void context_font_select_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); context_select_object (select_type, context, pika_data_factory_get_container (context->pika->font_factory)); } void context_brush_spacing_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaBrush *brush; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); brush = pika_context_get_brush (context); if (PIKA_IS_BRUSH (brush) && pika_data_is_writable (PIKA_DATA (brush))) { action_select_property (select_type, action_data_get_display (data), G_OBJECT (brush), "spacing", 1.0, 5.0, 20.0, 0.1, FALSE); } } void context_brush_shape_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaBrush *brush; PikaBrushGeneratedShape shape; return_if_no_context (context, data); shape = (PikaBrushGeneratedShape) g_variant_get_int32 (value); brush = pika_context_get_brush (context); if (PIKA_IS_BRUSH_GENERATED (brush) && pika_data_is_writable (PIKA_DATA (brush))) { PikaBrushGenerated *generated = PIKA_BRUSH_GENERATED (brush); PikaDisplay *display; const char *value_desc; pika_brush_generated_set_shape (generated, shape); pika_enum_get_value (PIKA_TYPE_BRUSH_GENERATED_SHAPE, shape, NULL, NULL, &value_desc, NULL); display = action_data_get_display (data); if (value_desc && display) { action_message (display, G_OBJECT (brush), _("Brush Shape: %s"), value_desc); } } } void context_brush_radius_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaBrush *brush; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); brush = pika_context_get_brush (context); if (PIKA_IS_BRUSH_GENERATED (brush) && pika_data_is_writable (PIKA_DATA (brush))) { PikaBrushGenerated *generated = PIKA_BRUSH_GENERATED (brush); PikaDisplay *display; gdouble radius; gdouble min_radius; radius = pika_brush_generated_get_radius (generated); /* If the user uses a high precision radius adjustment command * then we allow a minimum radius of 0.1 px, otherwise we set the * minimum radius to 1.0 px and adjust the radius to 1.0 px if it * is less than 1.0 px. This prevents irritating 0.1, 1.1, 2.1 etc * radius sequences when 1.0 px steps are used. */ switch (select_type) { case PIKA_ACTION_SELECT_SMALL_PREVIOUS: case PIKA_ACTION_SELECT_SMALL_NEXT: case PIKA_ACTION_SELECT_PERCENT_PREVIOUS: case PIKA_ACTION_SELECT_PERCENT_NEXT: min_radius = 0.1; break; default: min_radius = 1.0; if (radius < 1.0) radius = 1.0; break; } radius = action_select_value (select_type, radius, min_radius, 4000.0, min_radius, 0.1, 1.0, 10.0, 0.05, FALSE); pika_brush_generated_set_radius (generated, radius); display = action_data_get_display (data); if (display) { action_message (action_data_get_display (data), G_OBJECT (brush), _("Brush Radius: %2.2f"), radius); } } } void context_brush_spikes_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaBrush *brush; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); brush = pika_context_get_brush (context); if (PIKA_IS_BRUSH_GENERATED (brush) && pika_data_is_writable (PIKA_DATA (brush))) { action_select_property (select_type, action_data_get_display (data), G_OBJECT (brush), "spikes", 0.0, 1.0, 4.0, 0.1, FALSE); } } void context_brush_hardness_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaBrush *brush; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); brush = pika_context_get_brush (context); if (PIKA_IS_BRUSH_GENERATED (brush) && pika_data_is_writable (PIKA_DATA (brush))) { action_select_property (select_type, action_data_get_display (data), G_OBJECT (brush), "hardness", 0.001, 0.01, 0.1, 0.1, FALSE); } } void context_brush_aspect_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaBrush *brush; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); brush = pika_context_get_brush (context); if (PIKA_IS_BRUSH_GENERATED (brush) && pika_data_is_writable (PIKA_DATA (brush))) { action_select_property (select_type, action_data_get_display (data), G_OBJECT (brush), "aspect-ratio", 0.1, 1.0, 4.0, 0.1, FALSE); } } void context_brush_angle_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaBrush *brush; PikaActionSelectType select_type; return_if_no_context (context, data); select_type = (PikaActionSelectType) g_variant_get_int32 (value); brush = pika_context_get_brush (context); if (PIKA_IS_BRUSH_GENERATED (brush) && pika_data_is_writable (PIKA_DATA (brush))) { PikaBrushGenerated *generated = PIKA_BRUSH_GENERATED (brush); PikaDisplay *display; gdouble angle; angle = pika_brush_generated_get_angle (generated); if (select_type == PIKA_ACTION_SELECT_FIRST) angle = 0.0; else if (select_type == PIKA_ACTION_SELECT_LAST) angle = 90.0; else angle = action_select_value (select_type, angle, 0.0, 180.0, 0.0, 0.1, 1.0, 15.0, 0.1, TRUE); pika_brush_generated_set_angle (generated, angle); display = action_data_get_display (data); if (display) { action_message (action_data_get_display (data), G_OBJECT (brush), _("Brush Angle: %2.2f"), angle); } } } void context_toggle_dynamics_cmd_callback (PikaAction *action, GVariant *value, gpointer data) { PikaContext *context; PikaPaintInfo *paint_info; gboolean enabled; return_if_no_context (context, data); paint_info = pika_context_get_paint_info (context); if (paint_info) { PikaPaintOptions *paint_options; PikaDisplay *display; paint_options = paint_info->paint_options; enabled = pika_paint_options_are_dynamics_enabled (paint_options); pika_paint_options_enable_dynamics (paint_options, ! enabled); display = action_data_get_display (data); if (enabled) action_message (display, G_OBJECT (paint_options), _("Dynamics disabled")); else action_message (display, G_OBJECT (paint_options), _("Dynamics enabled")); } } /* private functions */ static void context_select_object (PikaActionSelectType select_type, PikaContext *context, PikaContainer *container) { PikaObject *current; current = pika_context_get_by_type (context, pika_container_get_children_type (container)); current = action_select_object (select_type, container, current); if (current) pika_context_set_by_type (context, pika_container_get_children_type (container), current); } static gint context_paint_mode_index (PikaLayerMode paint_mode, const PikaLayerMode *modes, gint n_modes) { gint i = 0; while (i < (n_modes - 1) && modes[i] != paint_mode) i++; return i; } static void context_select_color (PikaActionSelectType select_type, PikaRGB *color, gboolean use_colormap, gboolean use_palette) { gint index; gint max; index = context_get_color_index (use_colormap, use_palette, color); max = context_max_color_index (use_colormap, use_palette); index = action_select_value (select_type, index, 0, max, 0, 0, 1, 4, 0, FALSE); context_set_color_index (index, use_colormap, use_palette, color); } static gint context_get_color_index (gboolean use_colormap, gboolean use_palette, const PikaRGB *color) { if (use_colormap) { PikaColormapEditor *editor = context_get_colormap_editor (); if (editor) { gint index = pika_colormap_editor_get_index (editor, color); if (index != -1) return index; } } if (use_palette) { PikaPaletteEditor *editor = context_get_palette_editor (); if (editor) { gint index = pika_palette_editor_get_index (editor, color); if (index != -1) return index; } } return 0; } static gint context_max_color_index (gboolean use_colormap, gboolean use_palette) { if (use_colormap) { PikaColormapEditor *editor = context_get_colormap_editor (); if (editor) { gint index = pika_colormap_editor_max_index (editor); if (index != -1) return index; } } if (use_palette) { PikaPaletteEditor *editor = context_get_palette_editor (); if (editor) { gint index = pika_palette_editor_max_index (editor); if (index != -1) return index; } } return 0; } static gboolean context_set_color_index (gint index, gboolean use_colormap, gboolean use_palette, PikaRGB *color) { if (use_colormap) { PikaColormapEditor *editor = context_get_colormap_editor (); if (editor && pika_colormap_editor_set_index (editor, index, color)) return TRUE; } if (use_palette) { PikaPaletteEditor *editor = context_get_palette_editor (); if (editor && pika_palette_editor_set_index (editor, index, color)) return TRUE; } return FALSE; } static PikaPaletteEditor * context_get_palette_editor (void) { GtkWidget *widget; g_return_val_if_fail (PIKA_IS_DIALOG_FACTORY (pika_dialog_factory_get_singleton ()), NULL); widget = pika_dialog_factory_find_widget (pika_dialog_factory_get_singleton (), "pika-palette-editor"); if (widget) return PIKA_PALETTE_EDITOR (gtk_bin_get_child (GTK_BIN (widget))); return NULL; } static PikaColormapEditor * context_get_colormap_editor (void) { GtkWidget *widget; g_return_val_if_fail (PIKA_IS_DIALOG_FACTORY (pika_dialog_factory_get_singleton ()), NULL); widget = pika_dialog_factory_find_widget (pika_dialog_factory_get_singleton (), "pika-indexed-palette"); if (widget) return PIKA_COLORMAP_EDITOR (gtk_bin_get_child (GTK_BIN (widget))); return NULL; }