From b0beb0197a4bd2c14ded20da976d4affd0c94729 Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Sat, 14 Apr 2018 00:52:20 +0200 Subject: [PATCH] Bug 795230 - Rename Blend tool and provide PDB compatibility Rename the tool and its options, and the gradient sub-struct of paint options. --- app/core/gimptoolpreset.c | 4 +- app/dialogs/dialogs.c | 2 +- app/paint/gimppaintoptions.c | 34 +- app/paint/gimppaintoptions.h | 12 +- app/tools/Makefile.am | 12 +- app/tools/gimp-tools.c | 4 +- app/tools/gimpblendtool-editor.c | 2518 ----------------- app/tools/gimpblendtool-editor.h | 48 - app/tools/gimpblendtool.c | 1073 ------- ...mpblendoptions.c => gimpgradientoptions.c} | 109 +- ...mpblendoptions.h => gimpgradientoptions.h} | 28 +- app/tools/gimpgradienttool-editor.c | 2517 ++++++++++++++++ app/tools/gimpgradienttool-editor.h | 48 + app/tools/gimpgradienttool.c | 1073 +++++++ .../{gimpblendtool.h => gimpgradienttool.h} | 38 +- app/tools/tool_manager.c | 2 +- app/widgets/gimphelp-ids.h | 2 +- data/tool-presets/FX/FX-Radial-Softlight.gtp | 6 +- data/tool-presets/FX/Vignette.gtp | 6 +- menus/image-menu.xml.in | 2 +- po/POTFILES.in | 6 +- 21 files changed, 3772 insertions(+), 3772 deletions(-) delete mode 100644 app/tools/gimpblendtool-editor.c delete mode 100644 app/tools/gimpblendtool-editor.h delete mode 100644 app/tools/gimpblendtool.c rename app/tools/{gimpblendoptions.c => gimpgradientoptions.c} (77%) rename app/tools/{gimpblendoptions.h => gimpgradientoptions.h} (53%) create mode 100644 app/tools/gimpgradienttool-editor.c create mode 100644 app/tools/gimpgradienttool-editor.h create mode 100644 app/tools/gimpgradienttool.c rename app/tools/{gimpblendtool.h => gimpgradienttool.h} (62%) diff --git a/app/core/gimptoolpreset.c b/app/core/gimptoolpreset.c index 0a1a584296..d26cafd2fc 100644 --- a/app/core/gimptoolpreset.c +++ b/app/core/gimptoolpreset.c @@ -36,7 +36,7 @@ /* The defaults are "everything except color", which is problematic - * with gradients, which is why we special case the blend tool in + * with gradients, which is why we special case the gradient tool in * gimp_tool_preset_set_options(). */ #define DEFAULT_USE_FG_BG FALSE @@ -541,7 +541,7 @@ gimp_tool_preset_set_options (GimpToolPreset *preset, g_object_set (preset, "use-font", FALSE, NULL); /* see comment above the DEFAULT defines at the top of the file */ - if (! g_strcmp0 ("gimp-blend-tool", + if (! g_strcmp0 ("gimp-gradient-tool", gimp_object_get_name (preset->tool_options->tool_info))) g_object_set (preset, "use-gradient", TRUE, NULL); diff --git a/app/dialogs/dialogs.c b/app/dialogs/dialogs.c index 9e36d5b2c1..0445962e06 100644 --- a/app/dialogs/dialogs.c +++ b/app/dialogs/dialogs.c @@ -223,7 +223,6 @@ static GtkWidget * dialogs_restore_window (GimpDialogFactory *factory, static const GimpDialogFactoryEntry entries[] = { /* foreign toplevels without constructor */ - FOREIGN ("gimp-blend-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-brightness-contrast-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-color-balance-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-color-picker-tool-dialog", TRUE, TRUE), @@ -233,6 +232,7 @@ static const GimpDialogFactoryEntry entries[] = FOREIGN ("gimp-desaturate-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-foreground-select-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-gegl-tool-dialog", TRUE, FALSE), + FOREIGN ("gimp-gradient-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-hue-saturation-tool-dialog", TRUE, FALSE), FOREIGN ("gimp-levels-tool-dialog", TRUE, TRUE), FOREIGN ("gimp-measure-tool-dialog", TRUE, FALSE), diff --git a/app/paint/gimppaintoptions.c b/app/paint/gimppaintoptions.c index de5d2194a0..0c13501c96 100644 --- a/app/paint/gimppaintoptions.c +++ b/app/paint/gimppaintoptions.c @@ -445,7 +445,7 @@ gimp_paint_options_init (GimpPaintOptions *options) options->jitter_options = g_slice_new0 (GimpJitterOptions); options->fade_options = g_slice_new0 (GimpFadeOptions); - options->gradient_options = g_slice_new0 (GimpGradientOptions); + options->gradient_options = g_slice_new0 (GimpGradientPaintOptions); options->smoothing_options = g_slice_new0 (GimpSmoothingOptions); } @@ -468,10 +468,10 @@ gimp_paint_options_finalize (GObject *object) { GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object); - g_slice_free (GimpJitterOptions, options->jitter_options); - g_slice_free (GimpFadeOptions, options->fade_options); - g_slice_free (GimpGradientOptions, options->gradient_options); - g_slice_free (GimpSmoothingOptions, options->smoothing_options); + g_slice_free (GimpJitterOptions, options->jitter_options); + g_slice_free (GimpFadeOptions, options->fade_options); + g_slice_free (GimpGradientPaintOptions, options->gradient_options); + g_slice_free (GimpSmoothingOptions, options->smoothing_options); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -482,11 +482,11 @@ gimp_paint_options_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object); - GimpFadeOptions *fade_options = options->fade_options; - GimpJitterOptions *jitter_options = options->jitter_options; - GimpGradientOptions *gradient_options = options->gradient_options; - GimpSmoothingOptions *smoothing_options = options->smoothing_options; + GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object); + GimpFadeOptions *fade_options = options->fade_options; + GimpJitterOptions *jitter_options = options->jitter_options; + GimpGradientPaintOptions *gradient_options = options->gradient_options; + GimpSmoothingOptions *smoothing_options = options->smoothing_options; switch (property_id) { @@ -645,11 +645,11 @@ gimp_paint_options_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object); - GimpFadeOptions *fade_options = options->fade_options; - GimpJitterOptions *jitter_options = options->jitter_options; - GimpGradientOptions *gradient_options = options->gradient_options; - GimpSmoothingOptions *smoothing_options = options->smoothing_options; + GimpPaintOptions *options = GIMP_PAINT_OPTIONS (object); + GimpFadeOptions *fade_options = options->fade_options; + GimpJitterOptions *jitter_options = options->jitter_options; + GimpGradientPaintOptions *gradient_options = options->gradient_options; + GimpSmoothingOptions *smoothing_options = options->smoothing_options; switch (property_id) { @@ -958,8 +958,8 @@ gimp_paint_options_get_gradient_color (GimpPaintOptions *paint_options, if (gimp_dynamics_is_output_enabled (dynamics, GIMP_DYNAMICS_OUTPUT_COLOR)) { - GimpGradientOptions *gradient_options = paint_options->gradient_options; - GimpGradient *gradient; + GimpGradientPaintOptions *gradient_options = paint_options->gradient_options; + GimpGradient *gradient; gradient = gimp_context_get_gradient (GIMP_CONTEXT (paint_options)); diff --git a/app/paint/gimppaintoptions.h b/app/paint/gimppaintoptions.h index 19b18e5bc3..7933d64c42 100644 --- a/app/paint/gimppaintoptions.h +++ b/app/paint/gimppaintoptions.h @@ -31,10 +31,10 @@ GIMP_CONTEXT_PROP_MASK_PALETTE -typedef struct _GimpJitterOptions GimpJitterOptions; -typedef struct _GimpFadeOptions GimpFadeOptions; -typedef struct _GimpGradientOptions GimpGradientOptions; -typedef struct _GimpSmoothingOptions GimpSmoothingOptions; +typedef struct _GimpJitterOptions GimpJitterOptions; +typedef struct _GimpFadeOptions GimpFadeOptions; +typedef struct _GimpGradientPaintOptions GimpGradientPaintOptions; +typedef struct _GimpSmoothingOptions GimpSmoothingOptions; struct _GimpJitterOptions { @@ -50,7 +50,7 @@ struct _GimpFadeOptions GimpRepeatMode fade_repeat; }; -struct _GimpGradientOptions +struct _GimpGradientPaintOptions { gboolean gradient_reverse; GimpGradientBlendColorSpace gradient_blend_color_space; @@ -109,7 +109,7 @@ struct _GimpPaintOptions gboolean dynamics_expanded; GimpFadeOptions *fade_options; - GimpGradientOptions *gradient_options; + GimpGradientPaintOptions *gradient_options; GimpSmoothingOptions *smoothing_options; GimpViewType brush_view_type; diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am index b4e3cbea93..46770cd7bd 100644 --- a/app/tools/Makefile.am +++ b/app/tools/Makefile.am @@ -26,12 +26,6 @@ libapptools_a_sources = \ gimpalignoptions.h \ gimpaligntool.c \ gimpaligntool.h \ - gimpblendoptions.c \ - gimpblendoptions.h \ - gimpblendtool.c \ - gimpblendtool.h \ - gimpblendtool-editor.c \ - gimpblendtool-editor.h \ gimpbrightnesscontrasttool.c \ gimpbrightnesscontrasttool.h \ gimpbrushtool.c \ @@ -102,6 +96,12 @@ libapptools_a_sources = \ gimpgegltool.h \ gimpgenerictransformtool.c \ gimpgenerictransformtool.h \ + gimpgradientoptions.c \ + gimpgradientoptions.h \ + gimpgradienttool.c \ + gimpgradienttool.h \ + gimpgradienttool-editor.c \ + gimpgradienttool-editor.h \ gimpguidetool.c \ gimpguidetool.h \ gimphandletransformoptions.c \ diff --git a/app/tools/gimp-tools.c b/app/tools/gimp-tools.c index 9b9c4d0c1c..d511e743d4 100644 --- a/app/tools/gimp-tools.c +++ b/app/tools/gimp-tools.c @@ -41,7 +41,6 @@ #include "gimpairbrushtool.h" #include "gimpaligntool.h" -#include "gimpblendtool.h" #include "gimpbrightnesscontrasttool.h" #include "gimpbucketfilltool.h" #include "gimpbycolorselecttool.h" @@ -59,6 +58,7 @@ #include "gimpforegroundselecttool.h" #include "gimpfuzzyselecttool.h" #include "gimpgegltool.h" +#include "gimpgradienttool.h" #include "gimphandletransformtool.h" #include "gimphealtool.h" #include "gimpinktool.h" @@ -159,7 +159,7 @@ gimp_tools_init (Gimp *gimp) gimp_seamless_clone_tool_register, gimp_text_tool_register, gimp_bucket_fill_tool_register, - gimp_blend_tool_register, + gimp_gradient_tool_register, gimp_pencil_tool_register, gimp_paintbrush_tool_register, gimp_eraser_tool_register, diff --git a/app/tools/gimpblendtool-editor.c b/app/tools/gimpblendtool-editor.c deleted file mode 100644 index 9f1b1dc25d..0000000000 --- a/app/tools/gimpblendtool-editor.c +++ /dev/null @@ -1,2518 +0,0 @@ -/* 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 - -#include "libgimpbase/gimpbase.h" -#include "libgimpmath/gimpmath.h" -#include "libgimpwidgets/gimpwidgets.h" - -#include "tools-types.h" - -#include "operations/gimp-operation-config.h" - -#include "core/gimpdata.h" -#include "core/gimpgradient.h" -#include "core/gimp-gradients.h" -#include "core/gimpimage.h" - -#include "widgets/gimpcolorpanel.h" -#include "widgets/gimpeditor.h" -#include "widgets/gimpwidgets-utils.h" - -#include "display/gimpdisplay.h" -#include "display/gimpdisplayshell.h" -#include "display/gimptoolgui.h" -#include "display/gimptoolline.h" - -#include "gimpblendoptions.h" -#include "gimpblendtool.h" -#include "gimpblendtool-editor.h" - -#include "gimp-intl.h" - - -#define EPSILON 2e-10 - - -typedef enum -{ - DIRECTION_NONE, - DIRECTION_LEFT, - DIRECTION_RIGHT -} Direction; - - -typedef struct -{ - /* line endpoints at the beginning of the operation */ - gdouble start_x; - gdouble start_y; - gdouble end_x; - gdouble end_y; - - /* copy of the gradient at the beginning of the operation, owned by the blend - * info, or NULL, if the gradient isn't affected - */ - GimpGradient *gradient; - - /* handle added by the operation, or HANDLE_NONE */ - gint added_handle; - /* handle removed by the operation, or HANDLE_NONE */ - gint removed_handle; - /* selected handle at the end of the operation, or HANDLE_NONE */ - gint selected_handle; -} BlendInfo; - - -/* local function prototypes */ - -static gboolean gimp_blend_tool_editor_line_can_add_slider (GimpToolLine *line, - gdouble value, - GimpBlendTool *blend_tool); -static gint gimp_blend_tool_editor_line_add_slider (GimpToolLine *line, - gdouble value, - GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_line_prepare_to_remove_slider (GimpToolLine *line, - gint slider, - gboolean remove, - GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_line_remove_slider (GimpToolLine *line, - gint slider, - GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_line_selection_changed (GimpToolLine *line, - GimpBlendTool *blend_tool); -static gboolean gimp_blend_tool_editor_line_handle_clicked (GimpToolLine *line, - gint handle, - GdkModifierType state, - GimpButtonPressType press_type, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_gui_response (GimpToolGui *gui, - gint response_id, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_color_entry_color_clicked (GimpColorButton *button, - GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_color_entry_color_changed (GimpColorButton *button, - GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_color_entry_color_response (GimpColorButton *button, - GimpColorDialogState state, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_color_entry_type_changed (GtkComboBox *combo, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_endpoint_se_value_changed (GimpSizeEntry *se, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_stop_se_value_changed (GimpSizeEntry *se, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_stop_delete_clicked (GtkWidget *button, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_midpoint_se_value_changed (GimpSizeEntry *se, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_midpoint_type_changed (GtkComboBox *combo, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_midpoint_color_changed (GtkComboBox *combo, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_midpoint_new_stop_clicked (GtkWidget *button, - GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_midpoint_center_clicked (GtkWidget *button, - GimpBlendTool *blend_tool); - -static gboolean gimp_blend_tool_editor_flush_idle (GimpBlendTool *blend_tool); - -static gboolean gimp_blend_tool_editor_is_gradient_editable (GimpBlendTool *blend_tool); - -static gboolean gimp_blend_tool_editor_handle_is_endpoint (GimpBlendTool *blend_tool, - gint handle); -static gboolean gimp_blend_tool_editor_handle_is_stop (GimpBlendTool *blend_tool, - gint handle); -static gboolean gimp_blend_tool_editor_handle_is_midpoint (GimpBlendTool *blend_tool, - gint handle); -static GimpGradientSegment * gimp_blend_tool_editor_handle_get_segment (GimpBlendTool *blend_tool, - gint handle); - -static void gimp_blend_tool_editor_block_handlers (GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_unblock_handlers (GimpBlendTool *blend_tool); -static gboolean gimp_blend_tool_editor_are_handlers_blocked (GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_freeze_gradient (GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_thaw_gradient (GimpBlendTool *blend_tool); - -static gint gimp_blend_tool_editor_add_stop (GimpBlendTool *blend_tool, - gdouble value); -static void gimp_blend_tool_editor_delete_stop (GimpBlendTool *blend_tool, - gint slider); -static gint gimp_blend_tool_editor_midpoint_to_stop (GimpBlendTool *blend_tool, - gint slider); - -static void gimp_blend_tool_editor_update_sliders (GimpBlendTool *blend_tool); - -static void gimp_blend_tool_editor_purge_gradient_history (GSList **stack); -static void gimp_blend_tool_editor_purge_gradient (GimpBlendTool *blend_tool); - -static GtkWidget * gimp_blend_tool_editor_color_entry_new (GimpBlendTool *blend_tool, - const gchar *title, - Direction direction, - GtkWidget *chain_button, - GtkWidget **color_panel, - GtkWidget **type_combo); -static void gimp_blend_tool_editor_init_endpoint_gui (GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_init_stop_gui (GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_init_midpoint_gui (GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_update_endpoint_gui (GimpBlendTool *blend_tool, - gint selection); -static void gimp_blend_tool_editor_update_stop_gui (GimpBlendTool *blend_tool, - gint selection); -static void gimp_blend_tool_editor_update_midpoint_gui (GimpBlendTool *blend_tool, - gint selection); -static void gimp_blend_tool_editor_update_gui (GimpBlendTool *blend_tool); - -static BlendInfo * gimp_blend_tool_editor_blend_info_new (GimpBlendTool *blend_tool); -static void gimp_blend_tool_editor_blend_info_free (BlendInfo *info); -static void gimp_blend_tool_editor_blend_info_apply (GimpBlendTool *blend_tool, - const BlendInfo *info, - gboolean set_selection); -static gboolean gimp_blend_tool_editor_blend_info_is_trivial (GimpBlendTool *blend_tool, - const BlendInfo *info); - - -/* private functions */ - - -static gboolean -gimp_blend_tool_editor_line_can_add_slider (GimpToolLine *line, - gdouble value, - GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - gdouble offset = options->offset / 100.0; - - return gimp_blend_tool_editor_is_gradient_editable (blend_tool) && - value >= offset; -} - -static gint -gimp_blend_tool_editor_line_add_slider (GimpToolLine *line, - gdouble value, - GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); - gdouble offset = options->offset / 100.0; - - /* adjust slider value according to the offset */ - value = (value - offset) / (1.0 - offset); - - /* flip the slider value, if necessary */ - if (paint_options->gradient_options->gradient_reverse) - value = 1.0 - value; - - return gimp_blend_tool_editor_add_stop (blend_tool, value); -} - -static void -gimp_blend_tool_editor_line_prepare_to_remove_slider (GimpToolLine *line, - gint slider, - gboolean remove, - GimpBlendTool *blend_tool) -{ - if (remove) - { - BlendInfo *info; - GimpGradient *tentative_gradient; - - /* show a tentative gradient, demonstrating the result of actually - * removing the slider - */ - - info = blend_tool->undo_stack->data; - - if (info->added_handle == slider) - { - /* see comment in gimp_blend_tool_editor_delete_stop() */ - - gimp_assert (info->gradient != NULL); - - tentative_gradient = g_object_ref (info->gradient); - } - else - { - GimpGradientSegment *seg; - gint i; - - tentative_gradient = - GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (blend_tool->gradient))); - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, slider); - - i = gimp_gradient_segment_range_get_n_segments (blend_tool->gradient, - blend_tool->gradient->segments, - seg) - 1; - - seg = gimp_gradient_segment_get_nth (tentative_gradient->segments, i); - - gimp_gradient_segment_range_merge (tentative_gradient, - seg, seg->next, NULL, NULL); - } - - gimp_blend_tool_set_tentative_gradient (blend_tool, tentative_gradient); - - g_object_unref (tentative_gradient); - } - else - { - gimp_blend_tool_set_tentative_gradient (blend_tool, NULL); - } -} - -static void -gimp_blend_tool_editor_line_remove_slider (GimpToolLine *line, - gint slider, - GimpBlendTool *blend_tool) -{ - gimp_blend_tool_editor_delete_stop (blend_tool, slider); - gimp_blend_tool_set_tentative_gradient (blend_tool, NULL); -} - -static void -gimp_blend_tool_editor_line_selection_changed (GimpToolLine *line, - GimpBlendTool *blend_tool) -{ - gint selection; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - if (blend_tool->gui) - { - /* hide all color dialogs */ - gimp_color_panel_dialog_response ( - GIMP_COLOR_PANEL (blend_tool->endpoint_color_panel), - GIMP_COLOR_DIALOG_OK); - gimp_color_panel_dialog_response ( - GIMP_COLOR_PANEL (blend_tool->stop_left_color_panel), - GIMP_COLOR_DIALOG_OK); - gimp_color_panel_dialog_response ( - GIMP_COLOR_PANEL (blend_tool->stop_right_color_panel), - GIMP_COLOR_DIALOG_OK); - - /* reset the stop colors chain button */ - if (gimp_blend_tool_editor_handle_is_stop (blend_tool, selection)) - { - const GimpGradientSegment *seg; - gboolean homogeneous; - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, - selection); - - homogeneous = seg->right_color.r == seg->next->left_color.r && - seg->right_color.g == seg->next->left_color.g && - seg->right_color.b == seg->next->left_color.b && - seg->right_color.a == seg->next->left_color.a && - seg->right_color_type == seg->next->left_color_type; - - gimp_chain_button_set_active ( - GIMP_CHAIN_BUTTON (blend_tool->stop_chain_button), homogeneous); - } - } - - gimp_blend_tool_editor_update_gui (blend_tool); -} - -static gboolean -gimp_blend_tool_editor_line_handle_clicked (GimpToolLine *line, - gint handle, - GdkModifierType state, - GimpButtonPressType press_type, - GimpBlendTool *blend_tool) -{ - if (gimp_blend_tool_editor_handle_is_midpoint (blend_tool, handle)) - { - if (press_type == GIMP_BUTTON_PRESS_DOUBLE && - gimp_blend_tool_editor_is_gradient_editable (blend_tool)) - { - gint stop; - - stop = gimp_blend_tool_editor_midpoint_to_stop (blend_tool, handle); - - gimp_tool_line_set_selection (line, stop); - - /* return FALSE, so that the new slider can be dragged immediately */ - return FALSE; - } - } - - return FALSE; -} - - -static void -gimp_blend_tool_editor_gui_response (GimpToolGui *gui, - gint response_id, - GimpBlendTool *blend_tool) -{ - switch (response_id) - { - default: - gimp_tool_line_set_selection (GIMP_TOOL_LINE (blend_tool->widget), - GIMP_TOOL_LINE_HANDLE_NONE); - break; - } -} - -static void -gimp_blend_tool_editor_color_entry_color_clicked (GimpColorButton *button, - GimpBlendTool *blend_tool) -{ - gimp_blend_tool_editor_start_edit (blend_tool); -} - -static void -gimp_blend_tool_editor_color_entry_color_changed (GimpColorButton *button, - GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); - gint selection; - GimpRGB color; - Direction direction; - GtkWidget *chain_button; - GimpGradientSegment *seg; - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - gimp_color_button_get_color (button, &color); - - direction = - GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), - "gimp-blend-tool-editor-direction")); - chain_button = g_object_get_data (G_OBJECT (button), - "gimp-blend-tool-editor-chain-button"); - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - /* swap the endpoint handles, if necessary */ - if (paint_options->gradient_options->gradient_reverse) - { - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - selection = GIMP_TOOL_LINE_HANDLE_END; - break; - - case GIMP_TOOL_LINE_HANDLE_END: - selection = GIMP_TOOL_LINE_HANDLE_START; - break; - } - } - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - seg->left_color = color; - seg->left_color_type = GIMP_GRADIENT_COLOR_FIXED; - break; - - case GIMP_TOOL_LINE_HANDLE_END: - seg->right_color = color; - seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED; - break; - - default: - if (direction == DIRECTION_LEFT || - (chain_button && - gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chain_button)))) - { - seg->right_color = color; - seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED; - } - - if (direction == DIRECTION_RIGHT || - (chain_button && - gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chain_button)))) - { - seg->next->left_color = color; - seg->next->left_color_type = GIMP_GRADIENT_COLOR_FIXED; - } - } - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static void -gimp_blend_tool_editor_color_entry_color_response (GimpColorButton *button, - GimpColorDialogState state, - GimpBlendTool *blend_tool) -{ - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static void -gimp_blend_tool_editor_color_entry_type_changed (GtkComboBox *combo, - GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); - gint selection; - gint color_type; - Direction direction; - GtkWidget *chain_button; - GimpGradientSegment *seg; - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &color_type)) - return; - - direction = - GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), - "gimp-blend-tool-editor-direction")); - chain_button = g_object_get_data (G_OBJECT (combo), - "gimp-blend-tool-editor-chain-button"); - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - /* swap the endpoint handles, if necessary */ - if (paint_options->gradient_options->gradient_reverse) - { - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - selection = GIMP_TOOL_LINE_HANDLE_END; - break; - - case GIMP_TOOL_LINE_HANDLE_END: - selection = GIMP_TOOL_LINE_HANDLE_START; - break; - } - } - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - seg->left_color_type = color_type; - break; - - case GIMP_TOOL_LINE_HANDLE_END: - seg->right_color_type = color_type; - break; - - default: - if (direction == DIRECTION_LEFT || - (chain_button && - gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chain_button)))) - { - seg->right_color_type = color_type; - } - - if (direction == DIRECTION_RIGHT || - (chain_button && - gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chain_button)))) - { - seg->next->left_color_type = color_type; - } - } - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static void -gimp_blend_tool_editor_endpoint_se_value_changed (GimpSizeEntry *se, - GimpBlendTool *blend_tool) -{ - gint selection; - gdouble x; - gdouble y; - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - x = gimp_size_entry_get_refval (se, 0); - y = gimp_size_entry_get_refval (se, 1); - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_block_handlers (blend_tool); - - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - g_object_set (blend_tool->widget, - "x1", x, - "y1", y, - NULL); - break; - - case GIMP_TOOL_LINE_HANDLE_END: - g_object_set (blend_tool->widget, - "x2", x, - "y2", y, - NULL); - break; - - default: - gimp_assert_not_reached (); - } - - gimp_blend_tool_editor_unblock_handlers (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static void -gimp_blend_tool_editor_stop_se_value_changed (GimpSizeEntry *se, - GimpBlendTool *blend_tool) -{ - gint selection; - gdouble value; - GimpGradientSegment *seg; - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - if (selection == GIMP_TOOL_LINE_HANDLE_NONE) - return; - - value = gimp_size_entry_get_refval (se, 0) / 100.0; - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - gimp_gradient_segment_range_compress (blend_tool->gradient, - seg, seg, - seg->left, value); - gimp_gradient_segment_range_compress (blend_tool->gradient, - seg->next, seg->next, - value, seg->next->right); - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static void -gimp_blend_tool_editor_stop_delete_clicked (GtkWidget *button, - GimpBlendTool *blend_tool) -{ - gint selection; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - gimp_blend_tool_editor_delete_stop (blend_tool, selection); -} - -static void -gimp_blend_tool_editor_midpoint_se_value_changed (GimpSizeEntry *se, - GimpBlendTool *blend_tool) -{ - gint selection; - gdouble value; - GimpGradientSegment *seg; - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - if (selection == GIMP_TOOL_LINE_HANDLE_NONE) - return; - - value = gimp_size_entry_get_refval (se, 0) / 100.0; - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - seg->middle = value; - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static void -gimp_blend_tool_editor_midpoint_type_changed (GtkComboBox *combo, - GimpBlendTool *blend_tool) -{ - gint selection; - gint type; - GimpGradientSegment *seg; - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &type)) - return; - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - seg->type = type; - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static void -gimp_blend_tool_editor_midpoint_color_changed (GtkComboBox *combo, - GimpBlendTool *blend_tool) -{ - gint selection; - gint color; - GimpGradientSegment *seg; - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &color)) - return; - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - seg->color = color; - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static void -gimp_blend_tool_editor_midpoint_new_stop_clicked (GtkWidget *button, - GimpBlendTool *blend_tool) -{ - gint selection; - gint stop; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - stop = gimp_blend_tool_editor_midpoint_to_stop (blend_tool, selection); - - gimp_tool_line_set_selection (GIMP_TOOL_LINE (blend_tool->widget), stop); -} - -static void -gimp_blend_tool_editor_midpoint_center_clicked (GtkWidget *button, - GimpBlendTool *blend_tool) -{ - gint selection; - GimpGradientSegment *seg; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - gimp_gradient_segment_range_recenter_handles (blend_tool->gradient, seg, seg); - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static gboolean -gimp_blend_tool_editor_flush_idle (GimpBlendTool *blend_tool) -{ - GimpDisplay *display = GIMP_TOOL (blend_tool)->display; - - gimp_image_flush (gimp_display_get_image (display)); - - blend_tool->flush_idle_id = 0; - - return G_SOURCE_REMOVE; -} - -static gboolean -gimp_blend_tool_editor_is_gradient_editable (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - - return ! options->modify_active || - gimp_data_is_writable (GIMP_DATA (blend_tool->gradient)); -} - -static gboolean -gimp_blend_tool_editor_handle_is_endpoint (GimpBlendTool *blend_tool, - gint handle) -{ - return handle == GIMP_TOOL_LINE_HANDLE_START || - handle == GIMP_TOOL_LINE_HANDLE_END; -} - -static gboolean -gimp_blend_tool_editor_handle_is_stop (GimpBlendTool *blend_tool, - gint handle) -{ - gint n_sliders; - - gimp_tool_line_get_sliders (GIMP_TOOL_LINE (blend_tool->widget), &n_sliders); - - return handle >= 0 && handle < n_sliders / 2; -} - -static gboolean -gimp_blend_tool_editor_handle_is_midpoint (GimpBlendTool *blend_tool, - gint handle) -{ - gint n_sliders; - - gimp_tool_line_get_sliders (GIMP_TOOL_LINE (blend_tool->widget), &n_sliders); - - return handle >= n_sliders / 2; -} - -static GimpGradientSegment * -gimp_blend_tool_editor_handle_get_segment (GimpBlendTool *blend_tool, - gint handle) -{ - switch (handle) - { - case GIMP_TOOL_LINE_HANDLE_START: - return blend_tool->gradient->segments; - - case GIMP_TOOL_LINE_HANDLE_END: - return gimp_gradient_segment_get_last (blend_tool->gradient->segments); - - default: - { - const GimpControllerSlider *sliders; - gint n_sliders; - gint seg_i; - - sliders = gimp_tool_line_get_sliders (GIMP_TOOL_LINE (blend_tool->widget), - &n_sliders); - - gimp_assert (handle >= 0 && handle < n_sliders); - - seg_i = GPOINTER_TO_INT (sliders[handle].data); - - return gimp_gradient_segment_get_nth (blend_tool->gradient->segments, - seg_i); - } - } -} - -static void -gimp_blend_tool_editor_block_handlers (GimpBlendTool *blend_tool) -{ - blend_tool->block_handlers_count++; -} - -static void -gimp_blend_tool_editor_unblock_handlers (GimpBlendTool *blend_tool) -{ - gimp_assert (blend_tool->block_handlers_count > 0); - - blend_tool->block_handlers_count--; -} - -static gboolean -gimp_blend_tool_editor_are_handlers_blocked (GimpBlendTool *blend_tool) -{ - return blend_tool->block_handlers_count > 0; -} - -static void -gimp_blend_tool_editor_freeze_gradient (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpGradient *custom; - BlendInfo *info; - - gimp_blend_tool_editor_block_handlers (blend_tool); - - custom = gimp_gradients_get_custom (GIMP_CONTEXT (options)->gimp); - - if (blend_tool->gradient == custom || options->modify_active) - { - gimp_assert (gimp_blend_tool_editor_is_gradient_editable (blend_tool)); - - gimp_data_freeze (GIMP_DATA (blend_tool->gradient)); - } - else - { - /* copy the active gradient to the custom gradient, and make the custom - * gradient active. - */ - gimp_data_freeze (GIMP_DATA (custom)); - - gimp_data_copy (GIMP_DATA (custom), GIMP_DATA (blend_tool->gradient)); - - gimp_context_set_gradient (GIMP_CONTEXT (options), custom); - - gimp_assert (blend_tool->gradient == custom); - gimp_assert (gimp_blend_tool_editor_is_gradient_editable (blend_tool)); - } - - if (blend_tool->edit_count > 0) - { - info = blend_tool->undo_stack->data; - - if (! info->gradient) - { - info->gradient = - GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (blend_tool->gradient))); - } - } -} - -static void -gimp_blend_tool_editor_thaw_gradient(GimpBlendTool *blend_tool) -{ - gimp_data_thaw (GIMP_DATA (blend_tool->gradient)); - - gimp_blend_tool_editor_update_sliders (blend_tool); - gimp_blend_tool_editor_update_gui (blend_tool); - - gimp_blend_tool_editor_unblock_handlers (blend_tool); -} - -static gint -gimp_blend_tool_editor_add_stop (GimpBlendTool *blend_tool, - gdouble value) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); - GimpGradientSegment *seg; - gint stop; - BlendInfo *info; - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - gimp_gradient_split_at (blend_tool->gradient, - GIMP_CONTEXT (options), NULL, value, - paint_options->gradient_options->gradient_blend_color_space, - &seg, NULL); - - stop = - gimp_gradient_segment_range_get_n_segments (blend_tool->gradient, - blend_tool->gradient->segments, - seg) - 1; - - info = blend_tool->undo_stack->data; - info->added_handle = stop; - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); - - return stop; -} - -static void -gimp_blend_tool_editor_delete_stop (GimpBlendTool *blend_tool, - gint slider) -{ - BlendInfo *info; - - gimp_assert (gimp_blend_tool_editor_handle_is_stop (blend_tool, slider)); - - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - info = blend_tool->undo_stack->data; - - if (info->added_handle == slider) - { - /* when removing a stop that was added as part of the current action, - * restore the original gradient at the beginning of the action, rather - * than deleting the stop from the current gradient, so that the affected - * midpoint returns to its state at the beginning of the action, instead - * of being reset. - * - * note that this assumes that the gradient hasn't changed in any other - * way during the action, which is ugly, but currently always true. - */ - - gimp_assert (info->gradient != NULL); - - gimp_data_copy (GIMP_DATA (blend_tool->gradient), - GIMP_DATA (info->gradient)); - - g_clear_object (&info->gradient); - - info->added_handle = GIMP_TOOL_LINE_HANDLE_NONE; - } - else - { - GimpGradientSegment *seg; - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, slider); - - gimp_gradient_segment_range_merge (blend_tool->gradient, - seg, seg->next, NULL, NULL); - - info->removed_handle = slider; - } - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); -} - -static gint -gimp_blend_tool_editor_midpoint_to_stop (GimpBlendTool *blend_tool, - gint slider) -{ - const GimpControllerSlider *sliders; - - gimp_assert (gimp_blend_tool_editor_handle_is_midpoint (blend_tool, slider)); - - sliders = gimp_tool_line_get_sliders (GIMP_TOOL_LINE (blend_tool->widget), - NULL); - - if (sliders[slider].value > sliders[slider].min + EPSILON && - sliders[slider].value < sliders[slider].max - EPSILON) - { - const GimpGradientSegment *seg; - gint stop; - BlendInfo *info; - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, slider); - - stop = gimp_blend_tool_editor_add_stop (blend_tool, seg->middle); - - info = blend_tool->undo_stack->data; - info->removed_handle = slider; - - slider = stop; - } - - return slider; -} - -static void -gimp_blend_tool_editor_update_sliders (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); - gdouble offset = options->offset / 100.0; - gboolean editable; - GimpControllerSlider *sliders; - gint n_sliders; - gint n_segments; - GimpGradientSegment *seg; - GimpControllerSlider *slider; - gint i; - - if (! blend_tool->widget || options->instant) - return; - - editable = gimp_blend_tool_editor_is_gradient_editable (blend_tool); - - n_segments = gimp_gradient_segment_range_get_n_segments ( - blend_tool->gradient, blend_tool->gradient->segments, NULL); - - n_sliders = (n_segments - 1) + /* gradient stops, between each adjacent - * pair of segments */ - (n_segments); /* midpoints, inside each segment */ - - sliders = g_new (GimpControllerSlider, n_sliders); - - slider = sliders; - - /* initialize the gradient-stop sliders */ - for (seg = blend_tool->gradient->segments, i = 0; - seg->next; - seg = seg->next, i++) - { - *slider = GIMP_CONTROLLER_SLIDER_DEFAULT; - - slider->value = seg->right; - slider->min = seg->left; - slider->max = seg->next->right; - - slider->movable = editable; - slider->removable = editable; - - slider->data = GINT_TO_POINTER (i); - - slider++; - } - - /* initialize the midpoint sliders */ - for (seg = blend_tool->gradient->segments, i = 0; - seg; - seg = seg->next, i++) - { - *slider = GIMP_CONTROLLER_SLIDER_DEFAULT; - - slider->value = seg->middle; - slider->min = seg->left; - slider->max = seg->right; - - /* hide midpoints of zero-length segments, since they'd otherwise - * prevent the segment's endpoints from being selected - */ - slider->visible = fabs (slider->max - slider->min) > EPSILON; - slider->movable = editable; - - slider->autohide = TRUE; - slider->type = GIMP_HANDLE_FILLED_CIRCLE; - slider->size = 0.6; - - slider->data = GINT_TO_POINTER (i); - - slider++; - } - - /* flip the slider limits and values, if necessary */ - if (paint_options->gradient_options->gradient_reverse) - { - for (i = 0; i < n_sliders; i++) - { - gdouble temp; - - sliders[i].value = 1.0 - sliders[i].value; - temp = sliders[i].min; - sliders[i].min = 1.0 - sliders[i].max; - sliders[i].max = 1.0 - temp; - } - } - - /* adjust the sliders according to the offset */ - for (i = 0; i < n_sliders; i++) - { - sliders[i].value = (1.0 - offset) * sliders[i].value + offset; - sliders[i].min = (1.0 - offset) * sliders[i].min + offset; - sliders[i].max = (1.0 - offset) * sliders[i].max + offset; - } - - /* avoid updating the gradient in gimp_blend_tool_editor_line_changed() */ - gimp_blend_tool_editor_block_handlers (blend_tool); - - gimp_tool_line_set_sliders (GIMP_TOOL_LINE (blend_tool->widget), - sliders, n_sliders); - - gimp_blend_tool_editor_unblock_handlers (blend_tool); - - g_free (sliders); -} - -static void -gimp_blend_tool_editor_purge_gradient_history (GSList **stack) -{ - GSList *link; - - /* eliminate all history steps that modify the gradient */ - while ((link = *stack)) - { - BlendInfo *info = link->data; - - if (info->gradient) - { - gimp_blend_tool_editor_blend_info_free (info); - - *stack = g_slist_delete_link (*stack, link); - } - else - { - stack = &link->next; - } - } -} - -static void -gimp_blend_tool_editor_purge_gradient (GimpBlendTool *blend_tool) -{ - if (blend_tool->widget) - { - gimp_blend_tool_editor_update_sliders (blend_tool); - - gimp_tool_line_set_selection (GIMP_TOOL_LINE (blend_tool->widget), - GIMP_TOOL_LINE_HANDLE_NONE); - } - - gimp_blend_tool_editor_purge_gradient_history (&blend_tool->undo_stack); - gimp_blend_tool_editor_purge_gradient_history (&blend_tool->redo_stack); -} - -static GtkWidget * -gimp_blend_tool_editor_color_entry_new (GimpBlendTool *blend_tool, - const gchar *title, - Direction direction, - GtkWidget *chain_button, - GtkWidget **color_panel, - GtkWidget **type_combo) -{ - GimpContext *context = GIMP_CONTEXT (GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool)); - GtkWidget *hbox; - GtkWidget *button; - GtkWidget *combo; - GimpRGB color = {}; - - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); - - /* the color panel */ - *color_panel = button = gimp_color_panel_new (title, &color, - GIMP_COLOR_AREA_SMALL_CHECKS, - 24, 24); - gimp_color_button_set_update (GIMP_COLOR_BUTTON (button), TRUE); - gimp_color_panel_set_context (GIMP_COLOR_PANEL (button), context); - gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); - gtk_widget_show (button); - - g_object_set_data (G_OBJECT (button), - "gimp-blend-tool-editor-direction", - GINT_TO_POINTER (direction)); - g_object_set_data (G_OBJECT (button), - "gimp-blend-tool-editor-chain-button", - chain_button); - - g_signal_connect (button, "clicked", - G_CALLBACK (gimp_blend_tool_editor_color_entry_color_clicked), - blend_tool); - g_signal_connect (button, "color-changed", - G_CALLBACK (gimp_blend_tool_editor_color_entry_color_changed), - blend_tool); - g_signal_connect (button, "response", - G_CALLBACK (gimp_blend_tool_editor_color_entry_color_response), - blend_tool); - - /* the color type combo */ - *type_combo = combo = gimp_enum_combo_box_new (GIMP_TYPE_GRADIENT_COLOR); - gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, TRUE, 0); - gtk_widget_show (combo); - - g_object_set_data (G_OBJECT (combo), - "gimp-blend-tool-editor-direction", - GINT_TO_POINTER (direction)); - g_object_set_data (G_OBJECT (combo), - "gimp-blend-tool-editor-chain-button", - chain_button); - - g_signal_connect (combo, "changed", - G_CALLBACK (gimp_blend_tool_editor_color_entry_type_changed), - blend_tool); - - return hbox; -} - -static void -gimp_blend_tool_editor_init_endpoint_gui (GimpBlendTool *blend_tool) -{ - GimpDisplay *display = GIMP_TOOL (blend_tool)->display; - GimpDisplayShell *shell = gimp_display_get_shell (display); - GimpImage *image = gimp_display_get_image (display); - gdouble xres; - gdouble yres; - GtkWidget *editor; - GtkWidget *table; - GtkWidget *label; - GtkWidget *spinbutton; - GtkWidget *se; - GtkWidget *hbox; - gint row = 0; - - gimp_image_get_resolution (image, &xres, &yres); - - /* the endpoint editor */ - blend_tool->endpoint_editor = - editor = gimp_editor_new (); - gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (blend_tool->gui)), - editor, FALSE, TRUE, 0); - - /* the main table */ - table = gtk_table_new (1, 2, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacings (GTK_TABLE (table), 4); - gtk_box_pack_start (GTK_BOX (editor), table, FALSE, TRUE, 0); - gtk_widget_show (table); - - /* the position labels */ - label = gtk_label_new (_("X:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - label = gtk_label_new (_("Y:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row + 1, row + 2, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - /* the position size entry */ - spinbutton = gtk_spin_button_new_with_range (0.0, 0.0, 1.0); - gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); - gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 6); - - blend_tool->endpoint_se = - se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a", - TRUE, TRUE, FALSE, 6, - GIMP_SIZE_ENTRY_UPDATE_SIZE); - gtk_table_set_row_spacings (GTK_TABLE (se), 4); - gtk_table_set_col_spacings (GTK_TABLE (se), 2); - - gimp_size_entry_add_field (GIMP_SIZE_ENTRY (se), - GTK_SPIN_BUTTON (spinbutton), NULL); - gtk_table_attach_defaults (GTK_TABLE (se), spinbutton, 1, 2, 0, 1); - gtk_widget_show (spinbutton); - - gtk_table_attach (GTK_TABLE (table), se, 1, 2, row, row + 2, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (se); - - gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (se), shell->unit); - - gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (se), 0, xres, FALSE); - gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (se), 1, yres, FALSE); - - gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (se), 0, - -GIMP_MAX_IMAGE_SIZE, - GIMP_MAX_IMAGE_SIZE); - gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (se), 1, - -GIMP_MAX_IMAGE_SIZE, - GIMP_MAX_IMAGE_SIZE); - - gimp_size_entry_set_size (GIMP_SIZE_ENTRY (se), 0, - 0, gimp_image_get_width (image)); - gimp_size_entry_set_size (GIMP_SIZE_ENTRY (se), 1, - 0, gimp_image_get_height (image)); - - g_signal_connect (se, "value-changed", - G_CALLBACK (gimp_blend_tool_editor_endpoint_se_value_changed), - blend_tool); - - row += 2; - - /* the color label */ - label = gtk_label_new (_("Color:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - /* the color entry */ - hbox = gimp_blend_tool_editor_color_entry_new ( - blend_tool, _("Change Endpoint Color"), DIRECTION_NONE, NULL, - &blend_tool->endpoint_color_panel, &blend_tool->endpoint_type_combo); - gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (hbox); - - row++; -} - -static void -gimp_blend_tool_editor_init_stop_gui (GimpBlendTool *blend_tool) -{ - GtkWidget *editor; - GtkWidget *table; - GtkWidget *label; - GtkWidget *se; - GtkWidget *table2; - GtkWidget *button; - GtkWidget *hbox; - GtkWidget *separator; - gint row = 0; - - /* the stop editor */ - blend_tool->stop_editor = - editor = gimp_editor_new (); - gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (blend_tool->gui)), - editor, FALSE, TRUE, 0); - - /* the main table */ - table = gtk_table_new (1, 2, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacings (GTK_TABLE (table), 4); - gtk_box_pack_start (GTK_BOX (editor), table, FALSE, TRUE, 0); - gtk_widget_show (table); - - /* the position label */ - label = gtk_label_new (_("Position:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - /* the position size entry */ - blend_tool->stop_se = - se = gimp_size_entry_new (1, GIMP_UNIT_PERCENT, "%a", - FALSE, TRUE, FALSE, 6, - GIMP_SIZE_ENTRY_UPDATE_SIZE); - gimp_size_entry_show_unit_menu (GIMP_SIZE_ENTRY (se), FALSE); - gtk_table_attach (GTK_TABLE (table), se, 1, 2, row, row + 1, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (se); - - g_signal_connect (se, "value-changed", - G_CALLBACK (gimp_blend_tool_editor_stop_se_value_changed), - blend_tool); - - row++; - - /* the color labels */ - label = gtk_label_new (_("Left color:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - label = gtk_label_new (_("Right color:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row + 1, row + 2, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - /* the color entries table */ - table2 = gtk_table_new (1, 2, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table2), 4); - gtk_table_set_col_spacings (GTK_TABLE (table2), 2); - gtk_table_attach (GTK_TABLE (table), table2, 1, 2, row, row + 2, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (table2); - - /* the color entries chain button */ - blend_tool->stop_chain_button = - button = gimp_chain_button_new (GIMP_CHAIN_RIGHT); - gtk_table_attach (GTK_TABLE (table2), button, 1, 2, 0, 2, - GTK_SHRINK | GTK_FILL, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - 0, 0); - gtk_widget_show (button); - - /* the color entries */ - hbox = gimp_blend_tool_editor_color_entry_new ( - blend_tool, _("Change Stop Color"), DIRECTION_LEFT, button, - &blend_tool->stop_left_color_panel, &blend_tool->stop_left_type_combo); - gtk_table_attach (GTK_TABLE (table2), hbox, 0, 1, 0, 1, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (hbox); - - hbox = gimp_blend_tool_editor_color_entry_new ( - blend_tool, _("Change Stop Color"), DIRECTION_RIGHT, button, - &blend_tool->stop_right_color_panel, &blend_tool->stop_right_type_combo); - gtk_table_attach (GTK_TABLE (table2), hbox, 0, 1, 1, 2, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (hbox); - - row += 2; - - /* the action buttons separator */ - separator = gtk_hseparator_new (); - gtk_table_attach (GTK_TABLE (table), separator, 0, 2, row, row + 1, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (separator); - - row++; - - /* the delete button */ - gimp_editor_add_button (GIMP_EDITOR (editor), - GIMP_ICON_EDIT_DELETE, _("Delete stop"), - NULL, - G_CALLBACK (gimp_blend_tool_editor_stop_delete_clicked), - NULL, blend_tool); -} - -static void -gimp_blend_tool_editor_init_midpoint_gui (GimpBlendTool *blend_tool) -{ - GtkWidget *editor; - GtkWidget *table; - GtkWidget *label; - GtkWidget *se; - GtkWidget *combo; - GtkWidget *separator; - gint row = 0; - - /* the stop editor */ - blend_tool->midpoint_editor = - editor = gimp_editor_new (); - gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (blend_tool->gui)), - editor, FALSE, TRUE, 0); - - /* the main table */ - table = gtk_table_new (1, 2, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacings (GTK_TABLE (table), 4); - gtk_box_pack_start (GTK_BOX (editor), table, FALSE, TRUE, 0); - gtk_widget_show (table); - - /* the position label */ - label = gtk_label_new (_("Position:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - /* the position size entry */ - blend_tool->midpoint_se = - se = gimp_size_entry_new (1, GIMP_UNIT_PERCENT, "%a", - FALSE, TRUE, FALSE, 6, - GIMP_SIZE_ENTRY_UPDATE_SIZE); - gimp_size_entry_show_unit_menu (GIMP_SIZE_ENTRY (se), FALSE); - gtk_table_attach (GTK_TABLE (table), se, 1, 2, row, row + 1, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (se); - - g_signal_connect (se, "value-changed", - G_CALLBACK (gimp_blend_tool_editor_midpoint_se_value_changed), - blend_tool); - - row++; - - /* the type label */ - label = gtk_label_new (_("Blending:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - /* the type combo */ - blend_tool->midpoint_type_combo = - combo = gimp_enum_combo_box_new (GIMP_TYPE_GRADIENT_SEGMENT_TYPE); - gtk_table_attach (GTK_TABLE (table), combo, 1, 2, row, row + 1, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (combo); - - g_signal_connect (combo, "changed", - G_CALLBACK (gimp_blend_tool_editor_midpoint_type_changed), - blend_tool); - - row++; - - /* the color label */ - label = gtk_label_new (_("Coloring:")); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, - GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); - gtk_widget_show (label); - - /* the color combo */ - blend_tool->midpoint_color_combo = - combo = gimp_enum_combo_box_new (GIMP_TYPE_GRADIENT_SEGMENT_COLOR); - gtk_table_attach (GTK_TABLE (table), combo, 1, 2, row, row + 1, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (combo); - - g_signal_connect (combo, "changed", - G_CALLBACK (gimp_blend_tool_editor_midpoint_color_changed), - blend_tool); - - row++; - - /* the action buttons separator */ - separator = gtk_hseparator_new (); - gtk_table_attach (GTK_TABLE (table), separator, 0, 2, row, row + 1, - GTK_SHRINK | GTK_FILL | GTK_EXPAND, - GTK_SHRINK | GTK_FILL, - 0, 0); - gtk_widget_show (separator); - - row++; - - /* the new stop button */ - blend_tool->midpoint_new_stop_button = - gimp_editor_add_button (GIMP_EDITOR (editor), - GIMP_ICON_DOCUMENT_NEW, _("New stop at midpoint"), - NULL, - G_CALLBACK (gimp_blend_tool_editor_midpoint_new_stop_clicked), - NULL, blend_tool); - - /* the center button */ - blend_tool->midpoint_center_button = - gimp_editor_add_button (GIMP_EDITOR (editor), - GIMP_ICON_CENTER_HORIZONTAL, _("Center midpoint"), - NULL, - G_CALLBACK (gimp_blend_tool_editor_midpoint_center_clicked), - NULL, blend_tool); -} - -static void -gimp_blend_tool_editor_update_endpoint_gui (GimpBlendTool *blend_tool, - gint selection) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); - GimpContext *context = GIMP_CONTEXT (options); - gboolean editable; - GimpGradientSegment *seg; - const gchar *title; - gdouble x; - gdouble y; - GimpRGB color; - GimpGradientColor color_type; - - editable = gimp_blend_tool_editor_is_gradient_editable (blend_tool); - - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - g_object_get (blend_tool->widget, - "x1", &x, - "y1", &y, - NULL); - break; - - case GIMP_TOOL_LINE_HANDLE_END: - g_object_get (blend_tool->widget, - "x2", &x, - "y2", &y, - NULL); - break; - - default: - gimp_assert_not_reached (); - } - - /* swap the endpoint handles, if necessary */ - if (paint_options->gradient_options->gradient_reverse) - { - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - selection = GIMP_TOOL_LINE_HANDLE_END; - break; - - case GIMP_TOOL_LINE_HANDLE_END: - selection = GIMP_TOOL_LINE_HANDLE_START; - break; - } - } - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - title = _("Start Endpoint"); - - gimp_gradient_segment_get_left_flat_color (blend_tool->gradient, context, - seg, &color); - color_type = seg->left_color_type; - break; - - case GIMP_TOOL_LINE_HANDLE_END: - title = _("End Endpoint"); - - gimp_gradient_segment_get_right_flat_color (blend_tool->gradient, context, - seg, &color); - color_type = seg->right_color_type; - break; - - default: - gimp_assert_not_reached (); - } - - gimp_tool_gui_set_title (blend_tool->gui, title); - - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (blend_tool->endpoint_se), 0, x); - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (blend_tool->endpoint_se), 1, y); - - gimp_color_button_set_color ( - GIMP_COLOR_BUTTON (blend_tool->endpoint_color_panel), &color); - gimp_int_combo_box_set_active ( - GIMP_INT_COMBO_BOX (blend_tool->endpoint_type_combo), color_type); - - gtk_widget_set_sensitive (blend_tool->endpoint_color_panel, editable); - gtk_widget_set_sensitive (blend_tool->endpoint_type_combo, editable); - - gtk_widget_show (blend_tool->endpoint_editor); -} - -static void -gimp_blend_tool_editor_update_stop_gui (GimpBlendTool *blend_tool, - gint selection) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpContext *context = GIMP_CONTEXT (options); - gboolean editable; - GimpGradientSegment *seg; - gint index; - gchar *title; - gdouble min; - gdouble max; - gdouble value; - GimpRGB left_color; - GimpGradientColor left_color_type; - GimpRGB right_color; - GimpGradientColor right_color_type; - - editable = gimp_blend_tool_editor_is_gradient_editable (blend_tool); - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - index = GPOINTER_TO_INT ( - gimp_tool_line_get_sliders (GIMP_TOOL_LINE (blend_tool->widget), - NULL)[selection].data); - - title = g_strdup_printf (_("Stop %d"), index + 1); - - min = seg->left; - max = seg->next->right; - value = seg->right; - - gimp_gradient_segment_get_right_flat_color (blend_tool->gradient, context, - seg, &left_color); - left_color_type = seg->right_color_type; - - gimp_gradient_segment_get_left_flat_color (blend_tool->gradient, context, - seg->next, &right_color); - right_color_type = seg->next->left_color_type; - - gimp_tool_gui_set_title (blend_tool->gui, title); - - gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (blend_tool->stop_se), - 0, 100.0 * min, 100.0 * max); - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (blend_tool->stop_se), - 0, 100.0 * value); - - gimp_color_button_set_color ( - GIMP_COLOR_BUTTON (blend_tool->stop_left_color_panel), &left_color); - gimp_int_combo_box_set_active ( - GIMP_INT_COMBO_BOX (blend_tool->stop_left_type_combo), left_color_type); - - gimp_color_button_set_color ( - GIMP_COLOR_BUTTON (blend_tool->stop_right_color_panel), &right_color); - gimp_int_combo_box_set_active ( - GIMP_INT_COMBO_BOX (blend_tool->stop_right_type_combo), right_color_type); - - gtk_widget_set_sensitive (blend_tool->stop_se, editable); - gtk_widget_set_sensitive (blend_tool->stop_left_color_panel, editable); - gtk_widget_set_sensitive (blend_tool->stop_left_type_combo, editable); - gtk_widget_set_sensitive (blend_tool->stop_right_color_panel, editable); - gtk_widget_set_sensitive (blend_tool->stop_right_type_combo, editable); - gtk_widget_set_sensitive (blend_tool->stop_chain_button, editable); - gtk_widget_set_sensitive ( - GTK_WIDGET (gimp_editor_get_button_box (GIMP_EDITOR (blend_tool->stop_editor))), - editable); - - g_free (title); - - gtk_widget_show (blend_tool->stop_editor); -} - -static void -gimp_blend_tool_editor_update_midpoint_gui (GimpBlendTool *blend_tool, - gint selection) -{ - gboolean editable; - const GimpGradientSegment *seg; - gint index; - gchar *title; - gdouble min; - gdouble max; - gdouble value; - GimpGradientSegmentType type; - GimpGradientSegmentColor color; - - editable = gimp_blend_tool_editor_is_gradient_editable (blend_tool); - - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, selection); - - index = GPOINTER_TO_INT ( - gimp_tool_line_get_sliders (GIMP_TOOL_LINE (blend_tool->widget), - NULL)[selection].data); - - title = g_strdup_printf (_("Midpoint %d"), index + 1); - - min = seg->left; - max = seg->right; - value = seg->middle; - type = seg->type; - color = seg->color; - - gimp_tool_gui_set_title (blend_tool->gui, title); - - gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (blend_tool->midpoint_se), - 0, 100.0 * min, 100.0 * max); - gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (blend_tool->midpoint_se), - 0, 100.0 * value); - - gimp_int_combo_box_set_active ( - GIMP_INT_COMBO_BOX (blend_tool->midpoint_type_combo), type); - - gimp_int_combo_box_set_active ( - GIMP_INT_COMBO_BOX (blend_tool->midpoint_color_combo), color); - - gtk_widget_set_sensitive (blend_tool->midpoint_new_stop_button, - value > min + EPSILON && value < max - EPSILON); - gtk_widget_set_sensitive (blend_tool->midpoint_center_button, - fabs (value - (min + max) / 2.0) > EPSILON); - - gtk_widget_set_sensitive (blend_tool->midpoint_se, editable); - gtk_widget_set_sensitive (blend_tool->midpoint_type_combo, editable); - gtk_widget_set_sensitive (blend_tool->midpoint_color_combo, editable); - gtk_widget_set_sensitive ( - GTK_WIDGET (gimp_editor_get_button_box (GIMP_EDITOR (blend_tool->midpoint_editor))), - editable); - - g_free (title); - - gtk_widget_show (blend_tool->midpoint_editor); -} - -static void -gimp_blend_tool_editor_update_gui (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - - if (blend_tool->gradient && blend_tool->widget && ! options->instant) - { - gint selection; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - if (selection != GIMP_TOOL_LINE_HANDLE_NONE) - { - if (! blend_tool->gui) - { - GimpDisplayShell *shell; - - shell = gimp_tool_widget_get_shell (blend_tool->widget); - - blend_tool->gui = - gimp_tool_gui_new (GIMP_TOOL (blend_tool)->tool_info, - NULL, NULL, NULL, NULL, - gtk_widget_get_screen (GTK_WIDGET (shell)), - gimp_widget_get_monitor (GTK_WIDGET (shell)), - TRUE, - - _("_Close"), GTK_RESPONSE_CLOSE, - - NULL); - - gimp_tool_gui_set_shell (blend_tool->gui, shell); - gimp_tool_gui_set_viewable (blend_tool->gui, - GIMP_VIEWABLE (blend_tool->gradient)); - gimp_tool_gui_set_auto_overlay (blend_tool->gui, TRUE); - - g_signal_connect (blend_tool->gui, "response", - G_CALLBACK (gimp_blend_tool_editor_gui_response), - blend_tool); - - gimp_blend_tool_editor_init_endpoint_gui (blend_tool); - gimp_blend_tool_editor_init_stop_gui (blend_tool); - gimp_blend_tool_editor_init_midpoint_gui (blend_tool); - } - - gimp_blend_tool_editor_block_handlers (blend_tool); - - if (gimp_blend_tool_editor_handle_is_endpoint (blend_tool, selection)) - gimp_blend_tool_editor_update_endpoint_gui (blend_tool, selection); - else - gtk_widget_hide (blend_tool->endpoint_editor); - - if (gimp_blend_tool_editor_handle_is_stop (blend_tool, selection)) - gimp_blend_tool_editor_update_stop_gui (blend_tool, selection); - else - gtk_widget_hide (blend_tool->stop_editor); - - if (gimp_blend_tool_editor_handle_is_midpoint (blend_tool, selection)) - gimp_blend_tool_editor_update_midpoint_gui (blend_tool, selection); - else - gtk_widget_hide (blend_tool->midpoint_editor); - - gimp_blend_tool_editor_unblock_handlers (blend_tool); - - gimp_tool_gui_show (blend_tool->gui); - - return; - } - } - - if (blend_tool->gui) - gimp_tool_gui_hide (blend_tool->gui); -} - -static BlendInfo * -gimp_blend_tool_editor_blend_info_new (GimpBlendTool *blend_tool) -{ - BlendInfo *info = g_slice_new (BlendInfo); - - info->start_x = blend_tool->start_x; - info->start_y = blend_tool->start_y; - info->end_x = blend_tool->end_x; - info->end_y = blend_tool->end_y; - - info->gradient = NULL; - - info->added_handle = GIMP_TOOL_LINE_HANDLE_NONE; - info->removed_handle = GIMP_TOOL_LINE_HANDLE_NONE; - info->selected_handle = GIMP_TOOL_LINE_HANDLE_NONE; - - return info; -} - -static void -gimp_blend_tool_editor_blend_info_free (BlendInfo *info) -{ - if (info->gradient) - g_object_unref (info->gradient); - - g_slice_free (BlendInfo, info); -} - -static void -gimp_blend_tool_editor_blend_info_apply (GimpBlendTool *blend_tool, - const BlendInfo *info, - gboolean set_selection) -{ - gint selection; - - gimp_assert (blend_tool->widget != NULL); - gimp_assert (blend_tool->gradient != NULL); - - /* pick the handle to select */ - if (info->gradient) - { - if (info->removed_handle != GIMP_TOOL_LINE_HANDLE_NONE) - { - /* we're undoing a stop-deletion or midpoint-to-stop operation; - * select the removed handle - */ - selection = info->removed_handle; - } - else if (info->added_handle != GIMP_TOOL_LINE_HANDLE_NONE) - { - /* we're undoing a stop addition operation */ - gimp_assert (gimp_blend_tool_editor_handle_is_stop (blend_tool, - info->added_handle)); - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - /* if the selected handle is a stop... */ - if (gimp_blend_tool_editor_handle_is_stop (blend_tool, selection)) - { - /* if the added handle is selected, clear the selection */ - if (selection == info->added_handle) - selection = GIMP_TOOL_LINE_HANDLE_NONE; - /* otherwise, keep the currently selected stop, possibly - * adjusting its handle index - */ - else if (selection > info->added_handle) - selection--; - } - /* otherwise, if the selected handle is a midpoint... */ - else if (gimp_blend_tool_editor_handle_is_midpoint (blend_tool, selection)) - { - const GimpControllerSlider *sliders; - gint seg_i; - - sliders = - gimp_tool_line_get_sliders (GIMP_TOOL_LINE (blend_tool->widget), - NULL); - - seg_i = GPOINTER_TO_INT (sliders[selection].data); - - /* if the midpoint belongs to one of the two segments incident to - * the added stop, clear the selection - */ - if (seg_i == info->added_handle || - seg_i == info->added_handle + 1) - { - selection = GIMP_TOOL_LINE_HANDLE_NONE; - } - /* otherwise, keep the currently selected stop, adjusting its - * handle index - */ - else - { - /* midpoint handles follow stop handles; since we removed a - * stop, we must decrement the handle index - */ - selection--; - - if (seg_i > info->added_handle) - selection--; - } - } - /* otherwise, don't change the selection */ - else - { - set_selection = FALSE; - } - } - else if (info->selected_handle != GIMP_TOOL_LINE_HANDLE_NONE) - { - /* we're undoing a property change operation; select the handle - * corresponding to the affected object - */ - selection = info->selected_handle; - } - else - { - /* something went wrong... */ - g_warn_if_reached (); - - set_selection = FALSE; - } - } - else if ((info->start_x != blend_tool->start_x || - info->start_y != blend_tool->start_y) && - (info->end_x == blend_tool->end_x && - info->end_y == blend_tool->end_y)) - { - /* we're undoing a start-endpoint move operation; select the start - * endpoint - */ - selection = GIMP_TOOL_LINE_HANDLE_START; - } - else if ((info->end_x != blend_tool->end_x || - info->end_y != blend_tool->end_y) && - (info->start_x == blend_tool->start_x && - info->start_y == blend_tool->start_y)) - - { - /* we're undoing am end-endpoint move operation; select the end - * endpoint - */ - selection = GIMP_TOOL_LINE_HANDLE_END; - } - else - { - /* we're undoing a line move operation; don't change the selection */ - set_selection = FALSE; - } - - gimp_blend_tool_editor_block_handlers (blend_tool); - - g_object_set (blend_tool->widget, - "x1", info->start_x, - "y1", info->start_y, - "x2", info->end_x, - "y2", info->end_y, - NULL); - - if (info->gradient) - { - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - gimp_data_copy (GIMP_DATA (blend_tool->gradient), - GIMP_DATA (info->gradient)); - - gimp_blend_tool_editor_thaw_gradient (blend_tool); - } - - if (set_selection) - { - gimp_tool_line_set_selection (GIMP_TOOL_LINE (blend_tool->widget), - selection); - } - - gimp_blend_tool_editor_update_gui (blend_tool); - - gimp_blend_tool_editor_unblock_handlers (blend_tool); -} - -static gboolean -gimp_blend_tool_editor_blend_info_is_trivial (GimpBlendTool *blend_tool, - const BlendInfo *info) -{ - const GimpGradientSegment *seg1; - const GimpGradientSegment *seg2; - - if (info->start_x != blend_tool->start_x || - info->start_y != blend_tool->start_y || - info->end_x != blend_tool->end_x || - info->end_y != blend_tool->end_y) - { - return FALSE; - } - - if (info->gradient) - { - for (seg1 = info->gradient->segments, seg2 = blend_tool->gradient->segments; - seg1 && seg2; - seg1 = seg1->next, seg2 = seg2->next) - { - if (memcmp (seg1, seg2, G_STRUCT_OFFSET (GimpGradientSegment, prev))) - return FALSE; - } - - if (seg1 || seg2) - return FALSE; - } - - return TRUE; -} - - -/* public functions */ - - -void -gimp_blend_tool_editor_options_notify (GimpBlendTool *blend_tool, - GimpToolOptions *options, - const GParamSpec *pspec) -{ - if (! strcmp (pspec->name, "modify-active")) - { - gimp_blend_tool_editor_update_sliders (blend_tool); - gimp_blend_tool_editor_update_gui (blend_tool); - } - else if (! strcmp (pspec->name, "gradient-reverse")) - { - gimp_blend_tool_editor_update_sliders (blend_tool); - - /* if an endpoint is selected, swap the selected endpoint */ - if (blend_tool->widget) - { - gint selection; - - selection = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - switch (selection) - { - case GIMP_TOOL_LINE_HANDLE_START: - gimp_tool_line_set_selection (GIMP_TOOL_LINE (blend_tool->widget), - GIMP_TOOL_LINE_HANDLE_END); - break; - - case GIMP_TOOL_LINE_HANDLE_END: - gimp_tool_line_set_selection (GIMP_TOOL_LINE (blend_tool->widget), - GIMP_TOOL_LINE_HANDLE_START); - break; - } - } - } - else if (blend_tool->render_node && - gegl_node_find_property (blend_tool->render_node, pspec->name)) - { - gimp_blend_tool_editor_update_sliders (blend_tool); - } -} - -void -gimp_blend_tool_editor_start (GimpBlendTool *blend_tool) -{ - g_signal_connect (blend_tool->widget, "can-add-slider", - G_CALLBACK (gimp_blend_tool_editor_line_can_add_slider), - blend_tool); - g_signal_connect (blend_tool->widget, "add-slider", - G_CALLBACK (gimp_blend_tool_editor_line_add_slider), - blend_tool); - g_signal_connect (blend_tool->widget, "prepare-to-remove-slider", - G_CALLBACK (gimp_blend_tool_editor_line_prepare_to_remove_slider), - blend_tool); - g_signal_connect (blend_tool->widget, "remove-slider", - G_CALLBACK (gimp_blend_tool_editor_line_remove_slider), - blend_tool); - g_signal_connect (blend_tool->widget, "selection-changed", - G_CALLBACK (gimp_blend_tool_editor_line_selection_changed), - blend_tool); - g_signal_connect (blend_tool->widget, "handle-clicked", - G_CALLBACK (gimp_blend_tool_editor_line_handle_clicked), - blend_tool); -} - -void -gimp_blend_tool_editor_halt (GimpBlendTool *blend_tool) -{ - g_clear_object (&blend_tool->gui); - - blend_tool->edit_count = 0; - - if (blend_tool->undo_stack) - { - g_slist_free_full (blend_tool->undo_stack, - (GDestroyNotify) gimp_blend_tool_editor_blend_info_free); - blend_tool->undo_stack = NULL; - } - - if (blend_tool->redo_stack) - { - g_slist_free_full (blend_tool->redo_stack, - (GDestroyNotify) gimp_blend_tool_editor_blend_info_free); - blend_tool->redo_stack = NULL; - } - - if (blend_tool->flush_idle_id) - { - g_source_remove (blend_tool->flush_idle_id); - blend_tool->flush_idle_id = 0; - } -} - -gboolean -gimp_blend_tool_editor_line_changed (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); - gdouble offset = options->offset / 100.0; - const GimpControllerSlider *sliders; - gint n_sliders; - gint i; - GimpGradientSegment *seg; - gboolean changed = FALSE; - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return FALSE; - - if (! blend_tool->gradient || offset == 1.0) - return FALSE; - - sliders = gimp_tool_line_get_sliders (GIMP_TOOL_LINE (blend_tool->widget), - &n_sliders); - - if (n_sliders == 0) - return FALSE; - - /* update the midpoints first, since moving the gradient stops may change the - * gradient's midpoints w.r.t. the sliders, but not the other way around. - */ - for (seg = blend_tool->gradient->segments, i = n_sliders / 2; - seg; - seg = seg->next, i++) - { - gdouble value; - - value = sliders[i].value; - - /* adjust slider value according to the offset */ - value = (value - offset) / (1.0 - offset); - - /* flip the slider value, if necessary */ - if (paint_options->gradient_options->gradient_reverse) - value = 1.0 - value; - - if (fabs (value - seg->middle) > EPSILON) - { - if (! changed) - { - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - /* refetch the segment, since the gradient might have changed */ - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, i); - - changed = TRUE; - } - - seg->middle = value; - } - } - - /* update the gradient stops */ - for (seg = blend_tool->gradient->segments, i = 0; - seg->next; - seg = seg->next, i++) - { - gdouble value; - - value = sliders[i].value; - - /* adjust slider value according to the offset */ - value = (value - offset) / (1.0 - offset); - - /* flip the slider value, if necessary */ - if (paint_options->gradient_options->gradient_reverse) - value = 1.0 - value; - - if (fabs (value - seg->right) > EPSILON) - { - if (! changed) - { - gimp_blend_tool_editor_start_edit (blend_tool); - gimp_blend_tool_editor_freeze_gradient (blend_tool); - - /* refetch the segment, since the gradient might have changed */ - seg = gimp_blend_tool_editor_handle_get_segment (blend_tool, i); - - changed = TRUE; - } - - gimp_gradient_segment_range_compress (blend_tool->gradient, - seg, seg, - seg->left, value); - gimp_gradient_segment_range_compress (blend_tool->gradient, - seg->next, seg->next, - value, seg->next->right); - } - } - - if (changed) - { - gimp_blend_tool_editor_thaw_gradient (blend_tool); - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); - } - - gimp_blend_tool_editor_update_gui (blend_tool); - - return changed; -} - -void -gimp_blend_tool_editor_fg_bg_changed (GimpBlendTool *blend_tool) -{ - gimp_blend_tool_editor_update_gui (blend_tool); -} - -void -gimp_blend_tool_editor_gradient_dirty (GimpBlendTool *blend_tool) -{ - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - gimp_blend_tool_editor_purge_gradient (blend_tool); -} - -void -gimp_blend_tool_editor_gradient_changed (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpContext *context = GIMP_CONTEXT (options); - - if (options->modify_active_frame) - { - gtk_widget_set_sensitive (options->modify_active_frame, - blend_tool->gradient != - gimp_gradients_get_custom (context->gimp)); - } - - if (options->modify_active_hint) - { - gtk_widget_set_visible (options->modify_active_hint, - blend_tool->gradient && - ! gimp_data_is_writable (GIMP_DATA (blend_tool->gradient))); - } - - if (gimp_blend_tool_editor_are_handlers_blocked (blend_tool)) - return; - - gimp_blend_tool_editor_purge_gradient (blend_tool); -} - -const gchar * -gimp_blend_tool_editor_can_undo (GimpBlendTool *blend_tool) -{ - if (! blend_tool->undo_stack || blend_tool->edit_count > 0) - return NULL; - - return _("Blend Step"); -} - -const gchar * -gimp_blend_tool_editor_can_redo (GimpBlendTool *blend_tool) -{ - if (! blend_tool->redo_stack || blend_tool->edit_count > 0) - return NULL; - - return _("Blend Step"); -} - -gboolean -gimp_blend_tool_editor_undo (GimpBlendTool *blend_tool) -{ - GimpTool *tool = GIMP_TOOL (blend_tool); - BlendInfo *info; - BlendInfo *new_info; - - gimp_assert (blend_tool->undo_stack != NULL); - gimp_assert (blend_tool->edit_count == 0); - - info = blend_tool->undo_stack->data; - - new_info = gimp_blend_tool_editor_blend_info_new (blend_tool); - - if (info->gradient) - { - new_info->gradient = - GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (blend_tool->gradient))); - - /* swap the added and removed handles, so that blend_info_apply() does - * the right thing on redo - */ - new_info->added_handle = info->removed_handle; - new_info->removed_handle = info->added_handle; - new_info->selected_handle = info->selected_handle; - } - - blend_tool->undo_stack = g_slist_remove (blend_tool->undo_stack, info); - blend_tool->redo_stack = g_slist_prepend (blend_tool->redo_stack, new_info); - - gimp_blend_tool_editor_blend_info_apply (blend_tool, info, TRUE); - gimp_blend_tool_editor_blend_info_free (info); - - /* the initial state of the blend tool is not useful; we might as well halt */ - if (! blend_tool->undo_stack) - gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display); - - return TRUE; -} - -gboolean -gimp_blend_tool_editor_redo (GimpBlendTool *blend_tool) -{ - BlendInfo *info; - BlendInfo *new_info; - - gimp_assert (blend_tool->redo_stack != NULL); - gimp_assert (blend_tool->edit_count == 0); - - info = blend_tool->redo_stack->data; - - new_info = gimp_blend_tool_editor_blend_info_new (blend_tool); - - if (info->gradient) - { - new_info->gradient = - GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (blend_tool->gradient))); - - /* swap the added and removed handles, so that blend_info_apply() does - * the right thing on undo - */ - new_info->added_handle = info->removed_handle; - new_info->removed_handle = info->added_handle; - new_info->selected_handle = info->selected_handle; - } - - blend_tool->redo_stack = g_slist_remove (blend_tool->redo_stack, info); - blend_tool->undo_stack = g_slist_prepend (blend_tool->undo_stack, new_info); - - gimp_blend_tool_editor_blend_info_apply (blend_tool, info, TRUE); - gimp_blend_tool_editor_blend_info_free (info); - - return TRUE; -} - -void -gimp_blend_tool_editor_start_edit (GimpBlendTool *blend_tool) -{ - if (blend_tool->edit_count++ == 0) - { - BlendInfo *info; - - info = gimp_blend_tool_editor_blend_info_new (blend_tool); - - blend_tool->undo_stack = g_slist_prepend (blend_tool->undo_stack, info); - - /* update the undo actions / menu items */ - if (! blend_tool->flush_idle_id) - { - blend_tool->flush_idle_id = - g_idle_add ((GSourceFunc) gimp_blend_tool_editor_flush_idle, - blend_tool); - } - } -} - -void -gimp_blend_tool_editor_end_edit (GimpBlendTool *blend_tool, - gboolean cancel) -{ - /* can happen when halting using esc */ - if (blend_tool->edit_count == 0) - return; - - if (--blend_tool->edit_count == 0) - { - BlendInfo *info = blend_tool->undo_stack->data; - - info->selected_handle = - gimp_tool_line_get_selection (GIMP_TOOL_LINE (blend_tool->widget)); - - if (cancel || - gimp_blend_tool_editor_blend_info_is_trivial (blend_tool, info)) - { - /* if the edit is canceled, or if nothing changed, undo the last - * step - */ - gimp_blend_tool_editor_blend_info_apply (blend_tool, info, FALSE); - - blend_tool->undo_stack = g_slist_remove (blend_tool->undo_stack, - info); - gimp_blend_tool_editor_blend_info_free (info); - } - else - { - /* otherwise, blow the redo stack */ - g_slist_free_full (blend_tool->redo_stack, - (GDestroyNotify) gimp_blend_tool_editor_blend_info_free); - blend_tool->redo_stack = NULL; - } - - /* update the undo actions / menu items */ - if (! blend_tool->flush_idle_id) - { - blend_tool->flush_idle_id = - g_idle_add ((GSourceFunc) gimp_blend_tool_editor_flush_idle, - blend_tool); - } - } -} diff --git a/app/tools/gimpblendtool-editor.h b/app/tools/gimpblendtool-editor.h deleted file mode 100644 index 33883716c3..0000000000 --- a/app/tools/gimpblendtool-editor.h +++ /dev/null @@ -1,48 +0,0 @@ -/* 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 . - */ - -#ifndef __GIMP_BLEND_TOOL_EDITOR_H__ -#define __GIMP_BLEND_TOOL_EDITOR_H__ - - -void gimp_blend_tool_editor_options_notify (GimpBlendTool *blend_tool, - GimpToolOptions *options, - const GParamSpec *pspec); - -void gimp_blend_tool_editor_start (GimpBlendTool *blend_tool); -void gimp_blend_tool_editor_halt (GimpBlendTool *blend_tool); - -gboolean gimp_blend_tool_editor_line_changed (GimpBlendTool *blend_tool); - -void gimp_blend_tool_editor_fg_bg_changed (GimpBlendTool *blend_tool); - -void gimp_blend_tool_editor_gradient_dirty (GimpBlendTool *blend_tool); - -void gimp_blend_tool_editor_gradient_changed (GimpBlendTool *blend_tool); - -const gchar * gimp_blend_tool_editor_can_undo (GimpBlendTool *blend_tool); -const gchar * gimp_blend_tool_editor_can_redo (GimpBlendTool *blend_tool); - -gboolean gimp_blend_tool_editor_undo (GimpBlendTool *blend_tool); -gboolean gimp_blend_tool_editor_redo (GimpBlendTool *blend_tool); - -void gimp_blend_tool_editor_start_edit (GimpBlendTool *blend_tool); -void gimp_blend_tool_editor_end_edit (GimpBlendTool *blend_tool, - gboolean cancel); - - -#endif /* __GIMP_BLEND_TOOL_EDITOR_H__ */ diff --git a/app/tools/gimpblendtool.c b/app/tools/gimpblendtool.c deleted file mode 100644 index ea70d9af87..0000000000 --- a/app/tools/gimpblendtool.c +++ /dev/null @@ -1,1073 +0,0 @@ -/* GIMP - The GNU Image Manipulation Program - * Copyright (C) 1995 Spencer Kimball and Peter Mattis - * - * Major improvements for interactivity - * Copyright (C) 2014 Michael Henning - * - * 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 "libgimpwidgets/gimpwidgets.h" - -#include "tools-types.h" - -#include "operations/gimp-operation-config.h" - -#include "core/gimpdrawable.h" -#include "core/gimpdrawable-gradient.h" -#include "core/gimpdrawablefilter.h" -#include "core/gimperror.h" -#include "core/gimpgradient.h" -#include "core/gimpimage.h" -#include "core/gimpprogress.h" -#include "core/gimpprojection.h" - -#include "widgets/gimphelp-ids.h" -#include "widgets/gimpwidgets-utils.h" - -#include "display/gimpdisplay.h" -#include "display/gimptoolline.h" - -#include "gimpblendoptions.h" -#include "gimpblendtool.h" -#include "gimpblendtool-editor.h" -#include "gimptoolcontrol.h" - -#include "gimp-intl.h" - - -/* local function prototypes */ - -static void gimp_blend_tool_dispose (GObject *object); - -static gboolean gimp_blend_tool_initialize (GimpTool *tool, - GimpDisplay *display, - GError **error); -static void gimp_blend_tool_control (GimpTool *tool, - GimpToolAction action, - GimpDisplay *display); -static void gimp_blend_tool_button_press (GimpTool *tool, - const GimpCoords *coords, - guint32 time, - GdkModifierType state, - GimpButtonPressType press_type, - GimpDisplay *display); -static void gimp_blend_tool_button_release (GimpTool *tool, - const GimpCoords *coords, - guint32 time, - GdkModifierType state, - GimpButtonReleaseType release_type, - GimpDisplay *display); -static void gimp_blend_tool_motion (GimpTool *tool, - const GimpCoords *coords, - guint32 time, - GdkModifierType state, - GimpDisplay *display); -static gboolean gimp_blend_tool_key_press (GimpTool *tool, - GdkEventKey *kevent, - GimpDisplay *display); -static void gimp_blend_tool_modifier_key (GimpTool *tool, - GdkModifierType key, - gboolean press, - GdkModifierType state, - GimpDisplay *display); -static void gimp_blend_tool_cursor_update (GimpTool *tool, - const GimpCoords *coords, - GdkModifierType state, - GimpDisplay *display); -static const gchar * gimp_blend_tool_can_undo (GimpTool *tool, - GimpDisplay *display); -static const gchar * gimp_blend_tool_can_redo (GimpTool *tool, - GimpDisplay *display); -static gboolean gimp_blend_tool_undo (GimpTool *tool, - GimpDisplay *display); -static gboolean gimp_blend_tool_redo (GimpTool *tool, - GimpDisplay *display); -static void gimp_blend_tool_options_notify (GimpTool *tool, - GimpToolOptions *options, - const GParamSpec *pspec); - -static void gimp_blend_tool_start (GimpBlendTool *blend_tool, - const GimpCoords *coords, - GimpDisplay *display); -static void gimp_blend_tool_halt (GimpBlendTool *blend_tool); -static void gimp_blend_tool_commit (GimpBlendTool *blend_tool); - -static void gimp_blend_tool_line_changed (GimpToolWidget *widget, - GimpBlendTool *blend_tool); -static void gimp_blend_tool_line_response (GimpToolWidget *widget, - gint response_id, - GimpBlendTool *blend_tool); - -static void gimp_blend_tool_precalc_shapeburst (GimpBlendTool *blend_tool); - -static void gimp_blend_tool_create_graph (GimpBlendTool *blend_tool); -static void gimp_blend_tool_update_graph (GimpBlendTool *blend_tool); - -static void gimp_blend_tool_fg_bg_changed (GimpBlendTool *blend_tool); - -static void gimp_blend_tool_gradient_dirty (GimpBlendTool *blend_tool); -static void gimp_blend_tool_set_gradient (GimpBlendTool *blend_tool, - GimpGradient *gradient); - -static gboolean gimp_blend_tool_is_shapeburst (GimpBlendTool *blend_tool); - -static void gimp_blend_tool_create_filter (GimpBlendTool *blend_tool, - GimpDrawable *drawable); -static void gimp_blend_tool_filter_flush (GimpDrawableFilter *filter, - GimpTool *tool); - - -G_DEFINE_TYPE (GimpBlendTool, gimp_blend_tool, GIMP_TYPE_DRAW_TOOL) - -#define parent_class gimp_blend_tool_parent_class - - -void -gimp_blend_tool_register (GimpToolRegisterCallback callback, - gpointer data) -{ - (* callback) (GIMP_TYPE_BLEND_TOOL, - GIMP_TYPE_BLEND_OPTIONS, - gimp_blend_options_gui, - GIMP_CONTEXT_PROP_MASK_FOREGROUND | - GIMP_CONTEXT_PROP_MASK_BACKGROUND | - GIMP_CONTEXT_PROP_MASK_OPACITY | - GIMP_CONTEXT_PROP_MASK_PAINT_MODE | - GIMP_CONTEXT_PROP_MASK_GRADIENT, - "gimp-blend-tool", - _("Blend"), - _("Blend Tool: Fill selected area with a color gradient"), - N_("Blen_d"), "L", - NULL, GIMP_HELP_TOOL_BLEND, - GIMP_ICON_TOOL_GRADIENT, - data); -} - -static void -gimp_blend_tool_class_init (GimpBlendToolClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass); - - object_class->dispose = gimp_blend_tool_dispose; - - tool_class->initialize = gimp_blend_tool_initialize; - tool_class->control = gimp_blend_tool_control; - tool_class->button_press = gimp_blend_tool_button_press; - tool_class->button_release = gimp_blend_tool_button_release; - tool_class->motion = gimp_blend_tool_motion; - tool_class->key_press = gimp_blend_tool_key_press; - tool_class->modifier_key = gimp_blend_tool_modifier_key; - tool_class->cursor_update = gimp_blend_tool_cursor_update; - tool_class->can_undo = gimp_blend_tool_can_undo; - tool_class->can_redo = gimp_blend_tool_can_redo; - tool_class->undo = gimp_blend_tool_undo; - tool_class->redo = gimp_blend_tool_redo; - tool_class->options_notify = gimp_blend_tool_options_notify; -} - -static void -gimp_blend_tool_init (GimpBlendTool *blend_tool) -{ - GimpTool *tool = GIMP_TOOL (blend_tool); - - gimp_tool_control_set_scroll_lock (tool->control, TRUE); - gimp_tool_control_set_preserve (tool->control, FALSE); - gimp_tool_control_set_dirty_mask (tool->control, - GIMP_DIRTY_IMAGE | - GIMP_DIRTY_IMAGE_STRUCTURE | - GIMP_DIRTY_DRAWABLE | - GIMP_DIRTY_ACTIVE_DRAWABLE); - gimp_tool_control_set_wants_click (tool->control, TRUE); - gimp_tool_control_set_wants_double_click (tool->control, TRUE); - gimp_tool_control_set_active_modifiers (tool->control, - GIMP_TOOL_ACTIVE_MODIFIERS_SEPARATE); - gimp_tool_control_set_precision (tool->control, - GIMP_CURSOR_PRECISION_SUBPIXEL); - gimp_tool_control_set_tool_cursor (tool->control, - GIMP_TOOL_CURSOR_GRADIENT); - gimp_tool_control_set_action_opacity (tool->control, - "context/context-opacity-set"); - gimp_tool_control_set_action_object_1 (tool->control, - "context/context-gradient-select-set"); - - gimp_draw_tool_set_default_status (GIMP_DRAW_TOOL (tool), - _("Click-Drag to draw a gradient")); -} - -static void -gimp_blend_tool_dispose (GObject *object) -{ - GimpBlendTool *blend_tool = GIMP_BLEND_TOOL (object); - - gimp_blend_tool_set_gradient (blend_tool, NULL); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static gboolean -gimp_blend_tool_initialize (GimpTool *tool, - GimpDisplay *display, - GError **error) -{ - GimpImage *image = gimp_display_get_image (display); - GimpDrawable *drawable = gimp_image_get_active_drawable (image); - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (tool); - - if (! GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error)) - { - return FALSE; - } - - if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) - { - g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, - _("Cannot modify the pixels of layer groups.")); - return FALSE; - } - - if (gimp_item_is_content_locked (GIMP_ITEM (drawable))) - { - g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, - _("The active layer's pixels are locked.")); - return FALSE; - } - - if (! gimp_item_is_visible (GIMP_ITEM (drawable))) - { - g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, - _("The active layer is not visible.")); - return FALSE; - } - - if (! gimp_context_get_gradient (GIMP_CONTEXT (options))) - { - g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, - _("No gradient available for use with this tool.")); - return FALSE; - } - - return TRUE; -} - -static void -gimp_blend_tool_control (GimpTool *tool, - GimpToolAction action, - GimpDisplay *display) -{ - GimpBlendTool *blend_tool = GIMP_BLEND_TOOL (tool); - - switch (action) - { - case GIMP_TOOL_ACTION_PAUSE: - case GIMP_TOOL_ACTION_RESUME: - break; - - case GIMP_TOOL_ACTION_HALT: - gimp_blend_tool_halt (blend_tool); - break; - - case GIMP_TOOL_ACTION_COMMIT: - gimp_blend_tool_commit (blend_tool); - break; - } - - GIMP_TOOL_CLASS (parent_class)->control (tool, action, display); -} - -static void -gimp_blend_tool_button_press (GimpTool *tool, - const GimpCoords *coords, - guint32 time, - GdkModifierType state, - GimpButtonPressType press_type, - GimpDisplay *display) -{ - GimpBlendTool *blend_tool = GIMP_BLEND_TOOL (tool); - - if (tool->display && display != tool->display) - gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display); - - if (! blend_tool->widget) - { - gimp_blend_tool_start (blend_tool, coords, display); - - gimp_tool_widget_hover (blend_tool->widget, coords, state, TRUE); - } - - /* call start_edit() before widget_button_press(), because we need to record - * the undo state before widget_button_press() potentially changes it. note - * that if widget_button_press() return FALSE, nothing changes and no undo - * step is created. - */ - if (press_type == GIMP_BUTTON_PRESS_NORMAL) - gimp_blend_tool_editor_start_edit (blend_tool); - - if (gimp_tool_widget_button_press (blend_tool->widget, coords, time, state, - press_type)) - { - blend_tool->grab_widget = blend_tool->widget; - } - - if (press_type == GIMP_BUTTON_PRESS_NORMAL) - gimp_tool_control_activate (tool->control); -} - -static void -gimp_blend_tool_button_release (GimpTool *tool, - const GimpCoords *coords, - guint32 time, - GdkModifierType state, - GimpButtonReleaseType release_type, - GimpDisplay *display) -{ - GimpBlendTool *blend_tool = GIMP_BLEND_TOOL (tool); - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (tool); - - gimp_tool_pop_status (tool, display); - - gimp_tool_control_halt (tool->control); - - if (blend_tool->grab_widget) - { - gimp_tool_widget_button_release (blend_tool->grab_widget, - coords, time, state, release_type); - blend_tool->grab_widget = NULL; - - if (options->instant) - { - if (release_type == GIMP_BUTTON_RELEASE_CANCEL) - gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display); - else - gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display); - } - } - - if (! options->instant) - { - gimp_blend_tool_editor_end_edit (blend_tool, - release_type == - GIMP_BUTTON_RELEASE_CANCEL); - } -} - -static void -gimp_blend_tool_motion (GimpTool *tool, - const GimpCoords *coords, - guint32 time, - GdkModifierType state, - GimpDisplay *display) -{ - GimpBlendTool *blend_tool = GIMP_BLEND_TOOL (tool); - - if (blend_tool->grab_widget) - { - gimp_tool_widget_motion (blend_tool->grab_widget, coords, time, state); - } -} - -static gboolean -gimp_blend_tool_key_press (GimpTool *tool, - GdkEventKey *kevent, - GimpDisplay *display) -{ - GimpBlendTool *blend_tool = GIMP_BLEND_TOOL (tool); - GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool); - gboolean result; - - /* call start_edit() before widget_key_press(), because we need to record the - * undo state before widget_key_press() potentially changes it. note that if - * widget_key_press() return FALSE, nothing changes and no undo step is - * created. - */ - if (display == draw_tool->display) - gimp_blend_tool_editor_start_edit (blend_tool); - - result = GIMP_TOOL_CLASS (parent_class)->key_press (tool, kevent, display); - - if (display == draw_tool->display) - gimp_blend_tool_editor_end_edit (blend_tool, FALSE); - - return result; -} - -static void -gimp_blend_tool_modifier_key (GimpTool *tool, - GdkModifierType key, - gboolean press, - GdkModifierType state, - GimpDisplay *display) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (tool); - - if (key == gimp_get_extend_selection_mask ()) - { - if (options->instant_toggle && - gtk_widget_get_sensitive (options->instant_toggle)) - { - g_object_set (options, - "instant", ! options->instant, - NULL); - } - } -} - -static void -gimp_blend_tool_cursor_update (GimpTool *tool, - const GimpCoords *coords, - GdkModifierType state, - GimpDisplay *display) -{ - GimpImage *image = gimp_display_get_image (display); - GimpDrawable *drawable = gimp_image_get_active_drawable (image); - - if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) || - gimp_item_is_content_locked (GIMP_ITEM (drawable)) || - ! gimp_item_is_visible (GIMP_ITEM (drawable))) - { - gimp_tool_set_cursor (tool, display, - gimp_tool_control_get_cursor (tool->control), - gimp_tool_control_get_tool_cursor (tool->control), - GIMP_CURSOR_MODIFIER_BAD); - return; - } - - GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display); -} - -static const gchar * -gimp_blend_tool_can_undo (GimpTool *tool, - GimpDisplay *display) -{ - return gimp_blend_tool_editor_can_undo (GIMP_BLEND_TOOL (tool)); -} - -static const gchar * -gimp_blend_tool_can_redo (GimpTool *tool, - GimpDisplay *display) -{ - return gimp_blend_tool_editor_can_redo (GIMP_BLEND_TOOL (tool)); -} - -static gboolean -gimp_blend_tool_undo (GimpTool *tool, - GimpDisplay *display) -{ - return gimp_blend_tool_editor_undo (GIMP_BLEND_TOOL (tool)); -} - -static gboolean -gimp_blend_tool_redo (GimpTool *tool, - GimpDisplay *display) -{ - return gimp_blend_tool_editor_redo (GIMP_BLEND_TOOL (tool)); -} - -static void -gimp_blend_tool_options_notify (GimpTool *tool, - GimpToolOptions *options, - const GParamSpec *pspec) -{ - GimpContext *context = GIMP_CONTEXT (options); - GimpBlendTool *blend_tool = GIMP_BLEND_TOOL (tool); - - if (! strcmp (pspec->name, "gradient")) - { - gimp_blend_tool_set_gradient (blend_tool, context->gradient); - - if (blend_tool->filter) - gimp_drawable_filter_apply (blend_tool->filter, NULL); - } - else if (blend_tool->render_node && - gegl_node_find_property (blend_tool->render_node, pspec->name)) - { - /* Sync any property changes on the config object that match the op */ - GValue value = G_VALUE_INIT; - - g_value_init (&value, pspec->value_type); - - g_object_get_property (G_OBJECT (options), pspec->name, &value); - gegl_node_set_property (blend_tool->render_node, pspec->name, &value); - - g_value_unset (&value); - - if (! strcmp (pspec->name, "gradient-type")) - { - GimpRepeatMode gradient_repeat; - GimpRepeatMode node_repeat; - GimpGradientType gradient_type; - - gradient_repeat = GIMP_PAINT_OPTIONS (options)->gradient_options->gradient_repeat; - gradient_type = GIMP_BLEND_OPTIONS (options)->gradient_type; - gegl_node_get (blend_tool->render_node, - "gradient-repeat", &node_repeat, - NULL); - - if (gradient_type >= GIMP_GRADIENT_SHAPEBURST_ANGULAR) - { - /* These gradient types are only meant to work with repeat - * value of "none" so these are the only ones where we - * don't keep the render node and the blend options in - * sync. - * We could instead reset the "gradient-repeat" value on - * GimpBlendOptions, but I assume one would want to revert - * back to the last set value if changing back the - * gradient type. So instead we just make the option - * insensitive (both in GUI and in render). - */ - if (node_repeat != GIMP_REPEAT_NONE) - gegl_node_set (blend_tool->render_node, - "gradient-repeat", GIMP_REPEAT_NONE, - NULL); - } - else if (node_repeat != gradient_repeat) - { - gegl_node_set (blend_tool->render_node, - "gradient-repeat", gradient_repeat, - NULL); - } - - if (gimp_blend_tool_is_shapeburst (blend_tool)) - gimp_blend_tool_precalc_shapeburst (blend_tool); - - gimp_blend_tool_update_graph (blend_tool); - } - - gimp_drawable_filter_apply (blend_tool->filter, NULL); - } - else if (blend_tool->render_node && - gimp_blend_tool_is_shapeburst (blend_tool) && - g_strcmp0 (pspec->name, "distance-metric") == 0) - { - g_clear_object (&blend_tool->dist_buffer); - gimp_blend_tool_precalc_shapeburst (blend_tool); - gimp_blend_tool_update_graph (blend_tool); - gimp_drawable_filter_apply (blend_tool->filter, NULL); - } - else if (blend_tool->filter && - ! strcmp (pspec->name, "opacity")) - { - gimp_drawable_filter_set_opacity (blend_tool->filter, - gimp_context_get_opacity (context)); - } - else if (blend_tool->filter && - ! strcmp (pspec->name, "paint-mode")) - { - gimp_drawable_filter_set_mode (blend_tool->filter, - gimp_context_get_paint_mode (context), - GIMP_LAYER_COLOR_SPACE_AUTO, - GIMP_LAYER_COLOR_SPACE_AUTO, - GIMP_LAYER_COMPOSITE_AUTO); - } - - gimp_blend_tool_editor_options_notify (blend_tool, options, pspec); -} - -static void -gimp_blend_tool_start (GimpBlendTool *blend_tool, - const GimpCoords *coords, - GimpDisplay *display) -{ - GimpTool *tool = GIMP_TOOL (blend_tool); - GimpDisplayShell *shell = gimp_display_get_shell (display); - GimpImage *image = gimp_display_get_image (display); - GimpDrawable *drawable = gimp_image_get_active_drawable (image); - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpContext *context = GIMP_CONTEXT (options); - - if (options->instant_toggle) - gtk_widget_set_sensitive (options->instant_toggle, FALSE); - - tool->display = display; - tool->drawable = drawable; - - blend_tool->start_x = coords->x; - blend_tool->start_y = coords->y; - blend_tool->end_x = coords->x; - blend_tool->end_y = coords->y; - - blend_tool->widget = gimp_tool_line_new (shell, - blend_tool->start_x, - blend_tool->start_y, - blend_tool->end_x, - blend_tool->end_y); - - g_object_set (blend_tool->widget, - "status-title", _("Blend: "), - NULL); - - gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), blend_tool->widget); - - g_signal_connect (blend_tool->widget, "changed", - G_CALLBACK (gimp_blend_tool_line_changed), - blend_tool); - g_signal_connect (blend_tool->widget, "response", - G_CALLBACK (gimp_blend_tool_line_response), - blend_tool); - - g_signal_connect_swapped (context, "background-changed", - G_CALLBACK (gimp_blend_tool_fg_bg_changed), - blend_tool); - g_signal_connect_swapped (context, "foreground-changed", - G_CALLBACK (gimp_blend_tool_fg_bg_changed), - blend_tool); - - gimp_blend_tool_create_filter (blend_tool, drawable); - - /* Initially sync all of the properties */ - gimp_operation_config_sync_node (G_OBJECT (options), - blend_tool->render_node); - - /* We don't allow repeat values for some shapes. */ - if (options->gradient_type >= GIMP_GRADIENT_SHAPEBURST_ANGULAR) - gegl_node_set (blend_tool->render_node, - "gradient-repeat", GIMP_REPEAT_NONE, - NULL); - - /* Connect signal handlers for the gradient */ - gimp_blend_tool_set_gradient (blend_tool, context->gradient); - - if (gimp_blend_tool_is_shapeburst (blend_tool)) - gimp_blend_tool_precalc_shapeburst (blend_tool); - - gimp_draw_tool_start (GIMP_DRAW_TOOL (blend_tool), display); - - gimp_blend_tool_editor_start (blend_tool); -} - -static void -gimp_blend_tool_halt (GimpBlendTool *blend_tool) -{ - GimpTool *tool = GIMP_TOOL (blend_tool); - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpContext *context = GIMP_CONTEXT (options); - - gimp_blend_tool_editor_halt (blend_tool); - - if (blend_tool->graph) - { - g_clear_object (&blend_tool->graph); - blend_tool->render_node = NULL; -#if 0 - blend_tool->subtract_node = NULL; - blend_tool->divide_node = NULL; -#endif - blend_tool->dist_node = NULL; - } - - g_clear_object (&blend_tool->dist_buffer); - - if (blend_tool->filter) - { - gimp_tool_control_push_preserve (tool->control, TRUE); - - gimp_drawable_filter_abort (blend_tool->filter); - g_object_unref (blend_tool->filter); - blend_tool->filter = NULL; - - gimp_tool_control_pop_preserve (tool->control); - - gimp_image_flush (gimp_display_get_image (tool->display)); - } - - gimp_blend_tool_set_tentative_gradient (blend_tool, NULL); - - g_signal_handlers_disconnect_by_func (context, - G_CALLBACK (gimp_blend_tool_fg_bg_changed), - blend_tool); - - if (tool->display) - gimp_tool_pop_status (tool, tool->display); - - if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (blend_tool))) - gimp_draw_tool_stop (GIMP_DRAW_TOOL (blend_tool)); - - gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), NULL); - g_clear_object (&blend_tool->widget); - - tool->display = NULL; - tool->drawable = NULL; - - if (options->instant_toggle) - gtk_widget_set_sensitive (options->instant_toggle, TRUE); -} - -static void -gimp_blend_tool_commit (GimpBlendTool *blend_tool) -{ - GimpTool *tool = GIMP_TOOL (blend_tool); - - if (blend_tool->filter) - { - gimp_tool_control_push_preserve (tool->control, TRUE); - - gimp_drawable_filter_commit (blend_tool->filter, - GIMP_PROGRESS (tool), FALSE); - g_clear_object (&blend_tool->filter); - - gimp_tool_control_pop_preserve (tool->control); - - gimp_image_flush (gimp_display_get_image (tool->display)); - } -} - -static void -gimp_blend_tool_line_changed (GimpToolWidget *widget, - GimpBlendTool *blend_tool) -{ - gdouble start_x; - gdouble start_y; - gdouble end_x; - gdouble end_y; - gboolean update = FALSE; - - g_object_get (widget, - "x1", &start_x, - "y1", &start_y, - "x2", &end_x, - "y2", &end_y, - NULL); - - if (start_x != blend_tool->start_x || - start_y != blend_tool->start_y || - end_x != blend_tool->end_x || - end_y != blend_tool->end_y) - { - blend_tool->start_x = start_x; - blend_tool->start_y = start_y; - blend_tool->end_x = end_x; - blend_tool->end_y = end_y; - - update = TRUE; - } - - if (gimp_blend_tool_editor_line_changed (blend_tool)) - update = TRUE; - - if (update) - { - gimp_blend_tool_update_graph (blend_tool); - gimp_drawable_filter_apply (blend_tool->filter, NULL); - } -} - -static void -gimp_blend_tool_line_response (GimpToolWidget *widget, - gint response_id, - GimpBlendTool *blend_tool) -{ - GimpTool *tool = GIMP_TOOL (blend_tool); - - switch (response_id) - { - case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM: - gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, tool->display); - break; - - case GIMP_TOOL_WIDGET_RESPONSE_CANCEL: - gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display); - break; - } -} - -static void -gimp_blend_tool_precalc_shapeburst (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpTool *tool = GIMP_TOOL (blend_tool); - gint x, y, width, height; - - if (blend_tool->dist_buffer || ! tool->drawable) - return; - - if (! gimp_item_mask_intersect (GIMP_ITEM (tool->drawable), - &x, &y, &width, &height)) - return; - - blend_tool->dist_buffer = - gimp_drawable_gradient_shapeburst_distmap (tool->drawable, - options->distance_metric, - GEGL_RECTANGLE (x, y, width, height), - GIMP_PROGRESS (blend_tool)); - - if (blend_tool->dist_node) - gegl_node_set (blend_tool->dist_node, - "buffer", blend_tool->dist_buffer, - NULL); - - gimp_progress_end (GIMP_PROGRESS (blend_tool)); -} - - -/* gegl graph stuff */ - -static void -gimp_blend_tool_create_graph (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpContext *context = GIMP_CONTEXT (options); - GeglNode *output; - - /* render_node is not supposed to be recreated */ - g_return_if_fail (blend_tool->graph == NULL); - - blend_tool->graph = gegl_node_new (); - - blend_tool->dist_node = - gegl_node_new_child (blend_tool->graph, - "operation", "gegl:buffer-source", - "buffer", blend_tool->dist_buffer, - NULL); - -#if 0 - blend_tool->subtract_node = - gegl_node_new_child (blend_tool->graph, - "operation", "gegl:subtract", - NULL); - - blend_tool->divide_node = - gegl_node_new_child (blend_tool->graph, - "operation", "gegl:divide", - NULL); -#endif - - blend_tool->render_node = - gegl_node_new_child (blend_tool->graph, - "operation", "gimp:gradient", - "context", context, - NULL); - - output = gegl_node_get_output_proxy (blend_tool->graph, "output"); - - gegl_node_link_many (blend_tool->dist_node, -#if 0 - blend_tool->subtract_node, - blend_tool->divide_node, -#endif - blend_tool->render_node, - output, - NULL); - - gimp_blend_tool_update_graph (blend_tool); -} - -static void -gimp_blend_tool_update_graph (GimpBlendTool *blend_tool) -{ - GimpTool *tool = GIMP_TOOL (blend_tool); - gint off_x, off_y; - - gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y); - -#if 0 - if (gimp_blend_tool_is_shapeburst (blend_tool)) - { - gfloat start, end; - - gegl_buffer_get (blend_tool->dist_buffer, - GEGL_RECTANGLE (blend_tool->start_x - off_x, - blend_tool->start_y - off_y, - 1, 1), - 1.0, babl_format("Y float"), &start, - GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); - - gegl_buffer_get (blend_tool->dist_buffer, - GEGL_RECTANGLE (blend_tool->end_x - off_x, - blend_tool->end_y - off_y, - 1, 1), - 1.0, babl_format("Y float"), &end, - GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); - - if (start != end) - { - gegl_node_set (blend_tool->subtract_node, - "value", (gdouble) start, - NULL); - gegl_node_set (blend_tool->divide_node, - "value", (gdouble) (end - start), - NULL); - } - } - else -#endif - { - gegl_node_set (blend_tool->render_node, - "start_x", blend_tool->start_x - off_x, - "start_y", blend_tool->start_y - off_y, - "end_x", blend_tool->end_x - off_x, - "end_y", blend_tool->end_y - off_y, - NULL); - } -} - -static void -gimp_blend_tool_fg_bg_changed (GimpBlendTool *blend_tool) -{ - if (! blend_tool->filter || ! blend_tool->gradient) - return; - - if (gimp_gradient_has_fg_bg_segments (blend_tool->gradient)) - { - /* Set a property on the node. Otherwise it will cache and refuse to update */ - gegl_node_set (blend_tool->render_node, - "gradient", blend_tool->gradient, - NULL); - - /* Update the filter */ - gimp_drawable_filter_apply (blend_tool->filter, NULL); - - gimp_blend_tool_editor_fg_bg_changed (blend_tool); - } -} - -static void -gimp_blend_tool_gradient_dirty (GimpBlendTool *blend_tool) -{ - if (! blend_tool->filter) - return; - - if (! blend_tool->tentative_gradient) - { - /* Set a property on the node. Otherwise it will cache and refuse to update */ - gegl_node_set (blend_tool->render_node, - "gradient", blend_tool->gradient, - NULL); - - /* Update the filter */ - gimp_drawable_filter_apply (blend_tool->filter, NULL); - } - - gimp_blend_tool_editor_gradient_dirty (blend_tool); -} - -static void -gimp_blend_tool_set_gradient (GimpBlendTool *blend_tool, - GimpGradient *gradient) -{ - if (blend_tool->gradient) - { - g_signal_handlers_disconnect_by_func (blend_tool->gradient, - G_CALLBACK (gimp_blend_tool_gradient_dirty), - blend_tool); - - g_object_unref (blend_tool->gradient); - } - - blend_tool->gradient = gradient; - - if (blend_tool->gradient) - { - g_object_ref (gradient); - - g_signal_connect_swapped (blend_tool->gradient, "dirty", - G_CALLBACK (gimp_blend_tool_gradient_dirty), - blend_tool); - - if (blend_tool->render_node) - gegl_node_set (blend_tool->render_node, - "gradient", blend_tool->gradient, - NULL); - } - - gimp_blend_tool_editor_gradient_changed (blend_tool); -} - -static gboolean -gimp_blend_tool_is_shapeburst (GimpBlendTool *blend_tool) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - - return options->gradient_type >= GIMP_GRADIENT_SHAPEBURST_ANGULAR && - options->gradient_type <= GIMP_GRADIENT_SHAPEBURST_DIMPLED; -} - - -/* image map stuff */ - -static void -gimp_blend_tool_create_filter (GimpBlendTool *blend_tool, - GimpDrawable *drawable) -{ - GimpBlendOptions *options = GIMP_BLEND_TOOL_GET_OPTIONS (blend_tool); - GimpContext *context = GIMP_CONTEXT (options); - - if (! blend_tool->graph) - gimp_blend_tool_create_graph (blend_tool); - - blend_tool->filter = gimp_drawable_filter_new (drawable, - C_("undo-type", "Blend"), - blend_tool->graph, - GIMP_ICON_TOOL_GRADIENT); - - gimp_drawable_filter_set_region (blend_tool->filter, - GIMP_FILTER_REGION_DRAWABLE); - gimp_drawable_filter_set_opacity (blend_tool->filter, - gimp_context_get_opacity (context)); - gimp_drawable_filter_set_mode (blend_tool->filter, - gimp_context_get_paint_mode (context), - GIMP_LAYER_COLOR_SPACE_AUTO, - GIMP_LAYER_COLOR_SPACE_AUTO, - GIMP_LAYER_COMPOSITE_AUTO); - - g_signal_connect (blend_tool->filter, "flush", - G_CALLBACK (gimp_blend_tool_filter_flush), - blend_tool); -} - -static void -gimp_blend_tool_filter_flush (GimpDrawableFilter *filter, - GimpTool *tool) -{ - GimpImage *image = gimp_display_get_image (tool->display); - - gimp_projection_flush (gimp_image_get_projection (image)); -} - - -/* protected functions */ - - -void -gimp_blend_tool_set_tentative_gradient (GimpBlendTool *blend_tool, - GimpGradient *gradient) -{ - g_return_if_fail (GIMP_IS_BLEND_TOOL (blend_tool)); - g_return_if_fail (gradient == NULL || GIMP_IS_GRADIENT (gradient)); - - if (gradient != blend_tool->tentative_gradient) - { - g_clear_object (&blend_tool->tentative_gradient); - - blend_tool->tentative_gradient = gradient; - - if (gradient) - g_object_ref (gradient); - - if (blend_tool->render_node) - { - gegl_node_set (blend_tool->render_node, - "gradient", gradient ? gradient : blend_tool->gradient, - NULL); - - gimp_drawable_filter_apply (blend_tool->filter, NULL); - } - } -} diff --git a/app/tools/gimpblendoptions.c b/app/tools/gimpgradientoptions.c similarity index 77% rename from app/tools/gimpblendoptions.c rename to app/tools/gimpgradientoptions.c index 7ef1d99df2..391db53c56 100644 --- a/app/tools/gimpblendoptions.c +++ b/app/tools/gimpgradientoptions.c @@ -33,7 +33,7 @@ #include "widgets/gimpviewablebox.h" #include "widgets/gimpwidgets-utils.h" -#include "gimpblendoptions.h" +#include "gimpgradientoptions.h" #include "gimppaintoptions-gui.h" #include "gimp-intl.h" @@ -55,33 +55,34 @@ enum }; -static void gimp_blend_options_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec); -static void gimp_blend_options_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec); +static void gimp_gradient_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_gradient_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); -static void blend_options_repeat_gradient_type_notify (GimpBlendOptions *options, - GParamSpec *pspec, - GtkWidget *repeat_combo); -static void blend_options_metric_gradient_type_notify (GimpBlendOptions *options, - GParamSpec *pspec, - GtkWidget *repeat_combo); +static void gradient_options_repeat_gradient_type_notify (GimpGradientOptions *options, + GParamSpec *pspec, + GtkWidget *repeat_combo); +static void gradient_options_metric_gradient_type_notify (GimpGradientOptions *options, + GParamSpec *pspec, + GtkWidget *repeat_combo); -G_DEFINE_TYPE (GimpBlendOptions, gimp_blend_options, GIMP_TYPE_PAINT_OPTIONS) +G_DEFINE_TYPE (GimpGradientOptions, gimp_gradient_options, + GIMP_TYPE_PAINT_OPTIONS) static void -gimp_blend_options_class_init (GimpBlendOptionsClass *klass) +gimp_gradient_options_class_init (GimpGradientOptionsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->set_property = gimp_blend_options_set_property; - object_class->get_property = gimp_blend_options_get_property; + object_class->set_property = gimp_gradient_options_set_property; + object_class->get_property = gimp_gradient_options_get_property; GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OFFSET, "offset", @@ -153,17 +154,17 @@ gimp_blend_options_class_init (GimpBlendOptionsClass *klass) } static void -gimp_blend_options_init (GimpBlendOptions *options) +gimp_gradient_options_init (GimpGradientOptions *options) { } static void -gimp_blend_options_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) +gimp_gradient_options_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { - GimpBlendOptions *options = GIMP_BLEND_OPTIONS (object); + GimpGradientOptions *options = GIMP_GRADIENT_OPTIONS (object); switch (property_id) { @@ -209,12 +210,12 @@ gimp_blend_options_set_property (GObject *object, } static void -gimp_blend_options_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) +gimp_gradient_options_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - GimpBlendOptions *options = GIMP_BLEND_OPTIONS (object); + GimpGradientOptions *options = GIMP_GRADIENT_OPTIONS (object); switch (property_id) { @@ -260,21 +261,21 @@ gimp_blend_options_get_property (GObject *object, } GtkWidget * -gimp_blend_options_gui (GimpToolOptions *tool_options) +gimp_gradient_options_gui (GimpToolOptions *tool_options) { - GObject *config = G_OBJECT (tool_options); - GimpContext *context = GIMP_CONTEXT (tool_options); - GimpBlendOptions *options = GIMP_BLEND_OPTIONS (tool_options); - GtkWidget *vbox = gimp_paint_options_gui (tool_options); - GtkWidget *vbox2; - GtkWidget *frame; - GtkWidget *scale; - GtkWidget *combo; - GtkWidget *button; - GtkWidget *label; - gchar *str; - GdkModifierType extend_mask; - GimpGradient *gradient; + GObject *config = G_OBJECT (tool_options); + GimpContext *context = GIMP_CONTEXT (tool_options); + GimpGradientOptions *options = GIMP_GRADIENT_OPTIONS (tool_options); + GtkWidget *vbox = gimp_paint_options_gui (tool_options); + GtkWidget *vbox2; + GtkWidget *frame; + GtkWidget *scale; + GtkWidget *combo; + GtkWidget *button; + GtkWidget *label; + gchar *str; + GdkModifierType extend_mask; + GimpGradient *gradient; extend_mask = gimp_get_extend_selection_mask (); @@ -316,9 +317,9 @@ gimp_blend_options_gui (GimpToolOptions *tool_options) gtk_widget_show (combo); g_signal_connect (config, "notify::gradient-type", - G_CALLBACK (blend_options_metric_gradient_type_notify), + G_CALLBACK (gradient_options_metric_gradient_type_notify), combo); - blend_options_metric_gradient_type_notify (options, NULL, combo); + gradient_options_metric_gradient_type_notify (options, NULL, combo); /* the repeat option */ combo = gimp_prop_enum_combo_box_new (config, "gradient-repeat", 0, 0); @@ -328,9 +329,9 @@ gimp_blend_options_gui (GimpToolOptions *tool_options) gtk_widget_show (combo); g_signal_connect (config, "notify::gradient-type", - G_CALLBACK (blend_options_repeat_gradient_type_notify), + G_CALLBACK (gradient_options_repeat_gradient_type_notify), combo); - blend_options_repeat_gradient_type_notify (options, NULL, combo); + gradient_options_repeat_gradient_type_notify (options, NULL, combo); /* the offset scale */ scale = gimp_prop_spin_scale_new (config, "offset", NULL, @@ -410,18 +411,18 @@ gimp_blend_options_gui (GimpToolOptions *tool_options) } static void -blend_options_repeat_gradient_type_notify (GimpBlendOptions *options, - GParamSpec *pspec, - GtkWidget *repeat_combo) +gradient_options_repeat_gradient_type_notify (GimpGradientOptions *options, + GParamSpec *pspec, + GtkWidget *repeat_combo) { gtk_widget_set_sensitive (repeat_combo, options->gradient_type < GIMP_GRADIENT_SHAPEBURST_ANGULAR); } static void -blend_options_metric_gradient_type_notify (GimpBlendOptions *options, - GParamSpec *pspec, - GtkWidget *repeat_combo) +gradient_options_metric_gradient_type_notify (GimpGradientOptions *options, + GParamSpec *pspec, + GtkWidget *repeat_combo) { gtk_widget_set_sensitive (repeat_combo, options->gradient_type >= GIMP_GRADIENT_SHAPEBURST_ANGULAR && diff --git a/app/tools/gimpblendoptions.h b/app/tools/gimpgradientoptions.h similarity index 53% rename from app/tools/gimpblendoptions.h rename to app/tools/gimpgradientoptions.h index 53a02f5f9d..0a6e596ccd 100644 --- a/app/tools/gimpblendoptions.h +++ b/app/tools/gimpgradientoptions.h @@ -15,25 +15,25 @@ * along with this program. If not, see . */ -#ifndef __GIMP_BLEND_OPTIONS_H__ -#define __GIMP_BLEND_OPTIONS_H__ +#ifndef __GIMP_GRADIENT_OPTIONS_H__ +#define __GIMP_GRADIENT_OPTIONS_H__ #include "paint/gimppaintoptions.h" -#define GIMP_TYPE_BLEND_OPTIONS (gimp_blend_options_get_type ()) -#define GIMP_BLEND_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BLEND_OPTIONS, GimpBlendOptions)) -#define GIMP_BLEND_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BLEND_OPTIONS, GimpBlendOptionsClass)) -#define GIMP_IS_BLEND_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BLEND_OPTIONS)) -#define GIMP_IS_BLEND_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BLEND_OPTIONS)) -#define GIMP_BLEND_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BLEND_OPTIONS, GimpBlendOptionsClass)) +#define GIMP_TYPE_GRADIENT_OPTIONS (gimp_gradient_options_get_type ()) +#define GIMP_GRADIENT_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GRADIENT_OPTIONS, GimpGradientOptions)) +#define GIMP_GRADIENT_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GRADIENT_OPTIONS, GimpGradientOptionsClass)) +#define GIMP_IS_GRADIENT_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GRADIENT_OPTIONS)) +#define GIMP_IS_GRADIENT_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GRADIENT_OPTIONS)) +#define GIMP_GRADIENT_OPTIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GRADIENT_OPTIONS, GimpGradientOptionsClass)) -typedef struct _GimpBlendOptions GimpBlendOptions; -typedef struct _GimpPaintOptionsClass GimpBlendOptionsClass; +typedef struct _GimpGradientOptions GimpGradientOptions; +typedef struct _GimpPaintOptionsClass GimpGradientOptionsClass; -struct _GimpBlendOptions +struct _GimpGradientOptions { GimpPaintOptions paint_options; @@ -57,9 +57,9 @@ struct _GimpBlendOptions }; -GType gimp_blend_options_get_type (void) G_GNUC_CONST; +GType gimp_gradient_options_get_type (void) G_GNUC_CONST; -GtkWidget * gimp_blend_options_gui (GimpToolOptions *tool_options); +GtkWidget * gimp_gradient_options_gui (GimpToolOptions *tool_options); -#endif /* __GIMP_BLEND_OPTIONS_H__ */ +#endif /* __GIMP_GRADIENT_OPTIONS_H__ */ diff --git a/app/tools/gimpgradienttool-editor.c b/app/tools/gimpgradienttool-editor.c new file mode 100644 index 0000000000..37b7c81f48 --- /dev/null +++ b/app/tools/gimpgradienttool-editor.c @@ -0,0 +1,2517 @@ +/* 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 + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "tools-types.h" + +#include "operations/gimp-operation-config.h" + +#include "core/gimpdata.h" +#include "core/gimpgradient.h" +#include "core/gimp-gradients.h" +#include "core/gimpimage.h" + +#include "widgets/gimpcolorpanel.h" +#include "widgets/gimpeditor.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimptoolgui.h" +#include "display/gimptoolline.h" + +#include "gimpgradientoptions.h" +#include "gimpgradienttool.h" +#include "gimpgradienttool-editor.h" + +#include "gimp-intl.h" + + +#define EPSILON 2e-10 + + +typedef enum +{ + DIRECTION_NONE, + DIRECTION_LEFT, + DIRECTION_RIGHT +} Direction; + + +typedef struct +{ + /* line endpoints at the beginning of the operation */ + gdouble start_x; + gdouble start_y; + gdouble end_x; + gdouble end_y; + + /* copy of the gradient at the beginning of the operation, owned by the gradient + * info, or NULL, if the gradient isn't affected + */ + GimpGradient *gradient; + + /* handle added by the operation, or HANDLE_NONE */ + gint added_handle; + /* handle removed by the operation, or HANDLE_NONE */ + gint removed_handle; + /* selected handle at the end of the operation, or HANDLE_NONE */ + gint selected_handle; +} GradientInfo; + + +/* local function prototypes */ + +static gboolean gimp_gradient_tool_editor_line_can_add_slider (GimpToolLine *line, + gdouble value, + GimpGradientTool *gradient_tool); +static gint gimp_gradient_tool_editor_line_add_slider (GimpToolLine *line, + gdouble value, + GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_line_prepare_to_remove_slider (GimpToolLine *line, + gint slider, + gboolean remove, + GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_line_remove_slider (GimpToolLine *line, + gint slider, + GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_line_selection_changed (GimpToolLine *line, + GimpGradientTool *gradient_tool); +static gboolean gimp_gradient_tool_editor_line_handle_clicked (GimpToolLine *line, + gint handle, + GdkModifierType state, + GimpButtonPressType press_type, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_gui_response (GimpToolGui *gui, + gint response_id, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_color_entry_color_clicked (GimpColorButton *button, + GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_color_entry_color_changed (GimpColorButton *button, + GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_color_entry_color_response (GimpColorButton *button, + GimpColorDialogState state, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_color_entry_type_changed (GtkComboBox *combo, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_endpoint_se_value_changed (GimpSizeEntry *se, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_stop_se_value_changed (GimpSizeEntry *se, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_stop_delete_clicked (GtkWidget *button, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_midpoint_se_value_changed (GimpSizeEntry *se, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_midpoint_type_changed (GtkComboBox *combo, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_midpoint_color_changed (GtkComboBox *combo, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_midpoint_new_stop_clicked (GtkWidget *button, + GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_midpoint_center_clicked (GtkWidget *button, + GimpGradientTool *gradient_tool); + +static gboolean gimp_gradient_tool_editor_flush_idle (GimpGradientTool *gradient_tool); + +static gboolean gimp_gradient_tool_editor_is_gradient_editable (GimpGradientTool *gradient_tool); + +static gboolean gimp_gradient_tool_editor_handle_is_endpoint (GimpGradientTool *gradient_tool, + gint handle); +static gboolean gimp_gradient_tool_editor_handle_is_stop (GimpGradientTool *gradient_tool, + gint handle); +static gboolean gimp_gradient_tool_editor_handle_is_midpoint (GimpGradientTool *gradient_tool, + gint handle); +static GimpGradientSegment * gimp_gradient_tool_editor_handle_get_segment (GimpGradientTool *gradient_tool, + gint handle); + +static void gimp_gradient_tool_editor_block_handlers (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_unblock_handlers (GimpGradientTool *gradient_tool); +static gboolean gimp_gradient_tool_editor_are_handlers_blocked (GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_freeze_gradient (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_thaw_gradient (GimpGradientTool *gradient_tool); + +static gint gimp_gradient_tool_editor_add_stop (GimpGradientTool *gradient_tool, + gdouble value); +static void gimp_gradient_tool_editor_delete_stop (GimpGradientTool *gradient_tool, + gint slider); +static gint gimp_gradient_tool_editor_midpoint_to_stop (GimpGradientTool *gradient_tool, + gint slider); + +static void gimp_gradient_tool_editor_update_sliders (GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_editor_purge_gradient_history (GSList **stack); +static void gimp_gradient_tool_editor_purge_gradient (GimpGradientTool *gradient_tool); + +static GtkWidget * gimp_gradient_tool_editor_color_entry_new (GimpGradientTool *gradient_tool, + const gchar *title, + Direction direction, + GtkWidget *chain_button, + GtkWidget **color_panel, + GtkWidget **type_combo); +static void gimp_gradient_tool_editor_init_endpoint_gui (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_init_stop_gui (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_init_midpoint_gui (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_update_endpoint_gui (GimpGradientTool *gradient_tool, + gint selection); +static void gimp_gradient_tool_editor_update_stop_gui (GimpGradientTool *gradient_tool, + gint selection); +static void gimp_gradient_tool_editor_update_midpoint_gui (GimpGradientTool *gradient_tool, + gint selection); +static void gimp_gradient_tool_editor_update_gui (GimpGradientTool *gradient_tool); + +static GradientInfo * gimp_gradient_tool_editor_gradient_info_new (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_editor_gradient_info_free (GradientInfo *info); +static void gimp_gradient_tool_editor_gradient_info_apply (GimpGradientTool *gradient_tool, + const GradientInfo *info, + gboolean set_selection); +static gboolean gimp_gradient_tool_editor_gradient_info_is_trivial (GimpGradientTool *gradient_tool, + const GradientInfo *info); + + +/* private functions */ + + +static gboolean +gimp_gradient_tool_editor_line_can_add_slider (GimpToolLine *line, + gdouble value, + GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + gdouble offset = options->offset / 100.0; + + return gimp_gradient_tool_editor_is_gradient_editable (gradient_tool) && + value >= offset; +} + +static gint +gimp_gradient_tool_editor_line_add_slider (GimpToolLine *line, + gdouble value, + GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); + gdouble offset = options->offset / 100.0; + + /* adjust slider value according to the offset */ + value = (value - offset) / (1.0 - offset); + + /* flip the slider value, if necessary */ + if (paint_options->gradient_options->gradient_reverse) + value = 1.0 - value; + + return gimp_gradient_tool_editor_add_stop (gradient_tool, value); +} + +static void +gimp_gradient_tool_editor_line_prepare_to_remove_slider (GimpToolLine *line, + gint slider, + gboolean remove, + GimpGradientTool *gradient_tool) +{ + if (remove) + { + GradientInfo *info; + GimpGradient *tentative_gradient; + + /* show a tentative gradient, demonstrating the result of actually + * removing the slider + */ + + info = gradient_tool->undo_stack->data; + + if (info->added_handle == slider) + { + /* see comment in gimp_gradient_tool_editor_delete_stop() */ + + gimp_assert (info->gradient != NULL); + + tentative_gradient = g_object_ref (info->gradient); + } + else + { + GimpGradientSegment *seg; + gint i; + + tentative_gradient = + GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (gradient_tool->gradient))); + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, slider); + + i = gimp_gradient_segment_range_get_n_segments (gradient_tool->gradient, + gradient_tool->gradient->segments, + seg) - 1; + + seg = gimp_gradient_segment_get_nth (tentative_gradient->segments, i); + + gimp_gradient_segment_range_merge (tentative_gradient, + seg, seg->next, NULL, NULL); + } + + gimp_gradient_tool_set_tentative_gradient (gradient_tool, tentative_gradient); + + g_object_unref (tentative_gradient); + } + else + { + gimp_gradient_tool_set_tentative_gradient (gradient_tool, NULL); + } +} + +static void +gimp_gradient_tool_editor_line_remove_slider (GimpToolLine *line, + gint slider, + GimpGradientTool *gradient_tool) +{ + gimp_gradient_tool_editor_delete_stop (gradient_tool, slider); + gimp_gradient_tool_set_tentative_gradient (gradient_tool, NULL); +} + +static void +gimp_gradient_tool_editor_line_selection_changed (GimpToolLine *line, + GimpGradientTool *gradient_tool) +{ + gint selection; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + if (gradient_tool->gui) + { + /* hide all color dialogs */ + gimp_color_panel_dialog_response ( + GIMP_COLOR_PANEL (gradient_tool->endpoint_color_panel), + GIMP_COLOR_DIALOG_OK); + gimp_color_panel_dialog_response ( + GIMP_COLOR_PANEL (gradient_tool->stop_left_color_panel), + GIMP_COLOR_DIALOG_OK); + gimp_color_panel_dialog_response ( + GIMP_COLOR_PANEL (gradient_tool->stop_right_color_panel), + GIMP_COLOR_DIALOG_OK); + + /* reset the stop colors chain button */ + if (gimp_gradient_tool_editor_handle_is_stop (gradient_tool, selection)) + { + const GimpGradientSegment *seg; + gboolean homogeneous; + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, + selection); + + homogeneous = seg->right_color.r == seg->next->left_color.r && + seg->right_color.g == seg->next->left_color.g && + seg->right_color.b == seg->next->left_color.b && + seg->right_color.a == seg->next->left_color.a && + seg->right_color_type == seg->next->left_color_type; + + gimp_chain_button_set_active ( + GIMP_CHAIN_BUTTON (gradient_tool->stop_chain_button), homogeneous); + } + } + + gimp_gradient_tool_editor_update_gui (gradient_tool); +} + +static gboolean +gimp_gradient_tool_editor_line_handle_clicked (GimpToolLine *line, + gint handle, + GdkModifierType state, + GimpButtonPressType press_type, + GimpGradientTool *gradient_tool) +{ + if (gimp_gradient_tool_editor_handle_is_midpoint (gradient_tool, handle)) + { + if (press_type == GIMP_BUTTON_PRESS_DOUBLE && + gimp_gradient_tool_editor_is_gradient_editable (gradient_tool)) + { + gint stop; + + stop = gimp_gradient_tool_editor_midpoint_to_stop (gradient_tool, handle); + + gimp_tool_line_set_selection (line, stop); + + /* return FALSE, so that the new slider can be dragged immediately */ + return FALSE; + } + } + + return FALSE; +} + + +static void +gimp_gradient_tool_editor_gui_response (GimpToolGui *gui, + gint response_id, + GimpGradientTool *gradient_tool) +{ + switch (response_id) + { + default: + gimp_tool_line_set_selection (GIMP_TOOL_LINE (gradient_tool->widget), + GIMP_TOOL_LINE_HANDLE_NONE); + break; + } +} + +static void +gimp_gradient_tool_editor_color_entry_color_clicked (GimpColorButton *button, + GimpGradientTool *gradient_tool) +{ + gimp_gradient_tool_editor_start_edit (gradient_tool); +} + +static void +gimp_gradient_tool_editor_color_entry_color_changed (GimpColorButton *button, + GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); + gint selection; + GimpRGB color; + Direction direction; + GtkWidget *chain_button; + GimpGradientSegment *seg; + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + gimp_color_button_get_color (button, &color); + + direction = + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), + "gimp-gradient-tool-editor-direction")); + chain_button = g_object_get_data (G_OBJECT (button), + "gimp-gradient-tool-editor-chain-button"); + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + /* swap the endpoint handles, if necessary */ + if (paint_options->gradient_options->gradient_reverse) + { + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + selection = GIMP_TOOL_LINE_HANDLE_END; + break; + + case GIMP_TOOL_LINE_HANDLE_END: + selection = GIMP_TOOL_LINE_HANDLE_START; + break; + } + } + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + seg->left_color = color; + seg->left_color_type = GIMP_GRADIENT_COLOR_FIXED; + break; + + case GIMP_TOOL_LINE_HANDLE_END: + seg->right_color = color; + seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED; + break; + + default: + if (direction == DIRECTION_LEFT || + (chain_button && + gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chain_button)))) + { + seg->right_color = color; + seg->right_color_type = GIMP_GRADIENT_COLOR_FIXED; + } + + if (direction == DIRECTION_RIGHT || + (chain_button && + gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chain_button)))) + { + seg->next->left_color = color; + seg->next->left_color_type = GIMP_GRADIENT_COLOR_FIXED; + } + } + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static void +gimp_gradient_tool_editor_color_entry_color_response (GimpColorButton *button, + GimpColorDialogState state, + GimpGradientTool *gradient_tool) +{ + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static void +gimp_gradient_tool_editor_color_entry_type_changed (GtkComboBox *combo, + GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); + gint selection; + gint color_type; + Direction direction; + GtkWidget *chain_button; + GimpGradientSegment *seg; + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &color_type)) + return; + + direction = + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combo), + "gimp-gradient-tool-editor-direction")); + chain_button = g_object_get_data (G_OBJECT (combo), + "gimp-gradient-tool-editor-chain-button"); + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + /* swap the endpoint handles, if necessary */ + if (paint_options->gradient_options->gradient_reverse) + { + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + selection = GIMP_TOOL_LINE_HANDLE_END; + break; + + case GIMP_TOOL_LINE_HANDLE_END: + selection = GIMP_TOOL_LINE_HANDLE_START; + break; + } + } + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + seg->left_color_type = color_type; + break; + + case GIMP_TOOL_LINE_HANDLE_END: + seg->right_color_type = color_type; + break; + + default: + if (direction == DIRECTION_LEFT || + (chain_button && + gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chain_button)))) + { + seg->right_color_type = color_type; + } + + if (direction == DIRECTION_RIGHT || + (chain_button && + gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chain_button)))) + { + seg->next->left_color_type = color_type; + } + } + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static void +gimp_gradient_tool_editor_endpoint_se_value_changed (GimpSizeEntry *se, + GimpGradientTool *gradient_tool) +{ + gint selection; + gdouble x; + gdouble y; + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + x = gimp_size_entry_get_refval (se, 0); + y = gimp_size_entry_get_refval (se, 1); + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_block_handlers (gradient_tool); + + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + g_object_set (gradient_tool->widget, + "x1", x, + "y1", y, + NULL); + break; + + case GIMP_TOOL_LINE_HANDLE_END: + g_object_set (gradient_tool->widget, + "x2", x, + "y2", y, + NULL); + break; + + default: + gimp_assert_not_reached (); + } + + gimp_gradient_tool_editor_unblock_handlers (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static void +gimp_gradient_tool_editor_stop_se_value_changed (GimpSizeEntry *se, + GimpGradientTool *gradient_tool) +{ + gint selection; + gdouble value; + GimpGradientSegment *seg; + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + if (selection == GIMP_TOOL_LINE_HANDLE_NONE) + return; + + value = gimp_size_entry_get_refval (se, 0) / 100.0; + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + gimp_gradient_segment_range_compress (gradient_tool->gradient, + seg, seg, + seg->left, value); + gimp_gradient_segment_range_compress (gradient_tool->gradient, + seg->next, seg->next, + value, seg->next->right); + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static void +gimp_gradient_tool_editor_stop_delete_clicked (GtkWidget *button, + GimpGradientTool *gradient_tool) +{ + gint selection; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + gimp_gradient_tool_editor_delete_stop (gradient_tool, selection); +} + +static void +gimp_gradient_tool_editor_midpoint_se_value_changed (GimpSizeEntry *se, + GimpGradientTool *gradient_tool) +{ + gint selection; + gdouble value; + GimpGradientSegment *seg; + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + if (selection == GIMP_TOOL_LINE_HANDLE_NONE) + return; + + value = gimp_size_entry_get_refval (se, 0) / 100.0; + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + seg->middle = value; + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static void +gimp_gradient_tool_editor_midpoint_type_changed (GtkComboBox *combo, + GimpGradientTool *gradient_tool) +{ + gint selection; + gint type; + GimpGradientSegment *seg; + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &type)) + return; + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + seg->type = type; + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static void +gimp_gradient_tool_editor_midpoint_color_changed (GtkComboBox *combo, + GimpGradientTool *gradient_tool) +{ + gint selection; + gint color; + GimpGradientSegment *seg; + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &color)) + return; + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + seg->color = color; + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static void +gimp_gradient_tool_editor_midpoint_new_stop_clicked (GtkWidget *button, + GimpGradientTool *gradient_tool) +{ + gint selection; + gint stop; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + stop = gimp_gradient_tool_editor_midpoint_to_stop (gradient_tool, selection); + + gimp_tool_line_set_selection (GIMP_TOOL_LINE (gradient_tool->widget), stop); +} + +static void +gimp_gradient_tool_editor_midpoint_center_clicked (GtkWidget *button, + GimpGradientTool *gradient_tool) +{ + gint selection; + GimpGradientSegment *seg; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + gimp_gradient_segment_range_recenter_handles (gradient_tool->gradient, seg, seg); + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static gboolean +gimp_gradient_tool_editor_flush_idle (GimpGradientTool *gradient_tool) +{ + GimpDisplay *display = GIMP_TOOL (gradient_tool)->display; + + gimp_image_flush (gimp_display_get_image (display)); + + gradient_tool->flush_idle_id = 0; + + return G_SOURCE_REMOVE; +} + +static gboolean +gimp_gradient_tool_editor_is_gradient_editable (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + + return ! options->modify_active || + gimp_data_is_writable (GIMP_DATA (gradient_tool->gradient)); +} + +static gboolean +gimp_gradient_tool_editor_handle_is_endpoint (GimpGradientTool *gradient_tool, + gint handle) +{ + return handle == GIMP_TOOL_LINE_HANDLE_START || + handle == GIMP_TOOL_LINE_HANDLE_END; +} + +static gboolean +gimp_gradient_tool_editor_handle_is_stop (GimpGradientTool *gradient_tool, + gint handle) +{ + gint n_sliders; + + gimp_tool_line_get_sliders (GIMP_TOOL_LINE (gradient_tool->widget), &n_sliders); + + return handle >= 0 && handle < n_sliders / 2; +} + +static gboolean +gimp_gradient_tool_editor_handle_is_midpoint (GimpGradientTool *gradient_tool, + gint handle) +{ + gint n_sliders; + + gimp_tool_line_get_sliders (GIMP_TOOL_LINE (gradient_tool->widget), &n_sliders); + + return handle >= n_sliders / 2; +} + +static GimpGradientSegment * +gimp_gradient_tool_editor_handle_get_segment (GimpGradientTool *gradient_tool, + gint handle) +{ + switch (handle) + { + case GIMP_TOOL_LINE_HANDLE_START: + return gradient_tool->gradient->segments; + + case GIMP_TOOL_LINE_HANDLE_END: + return gimp_gradient_segment_get_last (gradient_tool->gradient->segments); + + default: + { + const GimpControllerSlider *sliders; + gint n_sliders; + gint seg_i; + + sliders = gimp_tool_line_get_sliders (GIMP_TOOL_LINE (gradient_tool->widget), + &n_sliders); + + gimp_assert (handle >= 0 && handle < n_sliders); + + seg_i = GPOINTER_TO_INT (sliders[handle].data); + + return gimp_gradient_segment_get_nth (gradient_tool->gradient->segments, + seg_i); + } + } +} + +static void +gimp_gradient_tool_editor_block_handlers (GimpGradientTool *gradient_tool) +{ + gradient_tool->block_handlers_count++; +} + +static void +gimp_gradient_tool_editor_unblock_handlers (GimpGradientTool *gradient_tool) +{ + gimp_assert (gradient_tool->block_handlers_count > 0); + + gradient_tool->block_handlers_count--; +} + +static gboolean +gimp_gradient_tool_editor_are_handlers_blocked (GimpGradientTool *gradient_tool) +{ + return gradient_tool->block_handlers_count > 0; +} + +static void +gimp_gradient_tool_editor_freeze_gradient (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpGradient *custom; + GradientInfo *info; + + gimp_gradient_tool_editor_block_handlers (gradient_tool); + + custom = gimp_gradients_get_custom (GIMP_CONTEXT (options)->gimp); + + if (gradient_tool->gradient == custom || options->modify_active) + { + gimp_assert (gimp_gradient_tool_editor_is_gradient_editable (gradient_tool)); + + gimp_data_freeze (GIMP_DATA (gradient_tool->gradient)); + } + else + { + /* copy the active gradient to the custom gradient, and make the custom + * gradient active. + */ + gimp_data_freeze (GIMP_DATA (custom)); + + gimp_data_copy (GIMP_DATA (custom), GIMP_DATA (gradient_tool->gradient)); + + gimp_context_set_gradient (GIMP_CONTEXT (options), custom); + + gimp_assert (gradient_tool->gradient == custom); + gimp_assert (gimp_gradient_tool_editor_is_gradient_editable (gradient_tool)); + } + + if (gradient_tool->edit_count > 0) + { + info = gradient_tool->undo_stack->data; + + if (! info->gradient) + { + info->gradient = + GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (gradient_tool->gradient))); + } + } +} + +static void +gimp_gradient_tool_editor_thaw_gradient (GimpGradientTool *gradient_tool) +{ + gimp_data_thaw (GIMP_DATA (gradient_tool->gradient)); + + gimp_gradient_tool_editor_update_sliders (gradient_tool); + gimp_gradient_tool_editor_update_gui (gradient_tool); + + gimp_gradient_tool_editor_unblock_handlers (gradient_tool); +} + +static gint +gimp_gradient_tool_editor_add_stop (GimpGradientTool *gradient_tool, + gdouble value) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); + GimpGradientSegment *seg; + gint stop; + GradientInfo *info; + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + gimp_gradient_split_at (gradient_tool->gradient, + GIMP_CONTEXT (options), NULL, value, + paint_options->gradient_options->gradient_blend_color_space, + &seg, NULL); + + stop = + gimp_gradient_segment_range_get_n_segments (gradient_tool->gradient, + gradient_tool->gradient->segments, + seg) - 1; + + info = gradient_tool->undo_stack->data; + info->added_handle = stop; + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); + + return stop; +} + +static void +gimp_gradient_tool_editor_delete_stop (GimpGradientTool *gradient_tool, + gint slider) +{ + GradientInfo *info; + + gimp_assert (gimp_gradient_tool_editor_handle_is_stop (gradient_tool, slider)); + + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + info = gradient_tool->undo_stack->data; + + if (info->added_handle == slider) + { + /* when removing a stop that was added as part of the current action, + * restore the original gradient at the beginning of the action, rather + * than deleting the stop from the current gradient, so that the affected + * midpoint returns to its state at the beginning of the action, instead + * of being reset. + * + * note that this assumes that the gradient hasn't changed in any other + * way during the action, which is ugly, but currently always true. + */ + + gimp_assert (info->gradient != NULL); + + gimp_data_copy (GIMP_DATA (gradient_tool->gradient), + GIMP_DATA (info->gradient)); + + g_clear_object (&info->gradient); + + info->added_handle = GIMP_TOOL_LINE_HANDLE_NONE; + } + else + { + GimpGradientSegment *seg; + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, slider); + + gimp_gradient_segment_range_merge (gradient_tool->gradient, + seg, seg->next, NULL, NULL); + + info->removed_handle = slider; + } + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); +} + +static gint +gimp_gradient_tool_editor_midpoint_to_stop (GimpGradientTool *gradient_tool, + gint slider) +{ + const GimpControllerSlider *sliders; + + gimp_assert (gimp_gradient_tool_editor_handle_is_midpoint (gradient_tool, slider)); + + sliders = gimp_tool_line_get_sliders (GIMP_TOOL_LINE (gradient_tool->widget), + NULL); + + if (sliders[slider].value > sliders[slider].min + EPSILON && + sliders[slider].value < sliders[slider].max - EPSILON) + { + const GimpGradientSegment *seg; + gint stop; + GradientInfo *info; + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, slider); + + stop = gimp_gradient_tool_editor_add_stop (gradient_tool, seg->middle); + + info = gradient_tool->undo_stack->data; + info->removed_handle = slider; + + slider = stop; + } + + return slider; +} + +static void +gimp_gradient_tool_editor_update_sliders (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); + gdouble offset = options->offset / 100.0; + gboolean editable; + GimpControllerSlider *sliders; + gint n_sliders; + gint n_segments; + GimpGradientSegment *seg; + GimpControllerSlider *slider; + gint i; + + if (! gradient_tool->widget || options->instant) + return; + + editable = gimp_gradient_tool_editor_is_gradient_editable (gradient_tool); + + n_segments = gimp_gradient_segment_range_get_n_segments ( + gradient_tool->gradient, gradient_tool->gradient->segments, NULL); + + n_sliders = (n_segments - 1) + /* gradient stops, between each adjacent + * pair of segments */ + (n_segments); /* midpoints, inside each segment */ + + sliders = g_new (GimpControllerSlider, n_sliders); + + slider = sliders; + + /* initialize the gradient-stop sliders */ + for (seg = gradient_tool->gradient->segments, i = 0; + seg->next; + seg = seg->next, i++) + { + *slider = GIMP_CONTROLLER_SLIDER_DEFAULT; + + slider->value = seg->right; + slider->min = seg->left; + slider->max = seg->next->right; + + slider->movable = editable; + slider->removable = editable; + + slider->data = GINT_TO_POINTER (i); + + slider++; + } + + /* initialize the midpoint sliders */ + for (seg = gradient_tool->gradient->segments, i = 0; + seg; + seg = seg->next, i++) + { + *slider = GIMP_CONTROLLER_SLIDER_DEFAULT; + + slider->value = seg->middle; + slider->min = seg->left; + slider->max = seg->right; + + /* hide midpoints of zero-length segments, since they'd otherwise + * prevent the segment's endpoints from being selected + */ + slider->visible = fabs (slider->max - slider->min) > EPSILON; + slider->movable = editable; + + slider->autohide = TRUE; + slider->type = GIMP_HANDLE_FILLED_CIRCLE; + slider->size = 0.6; + + slider->data = GINT_TO_POINTER (i); + + slider++; + } + + /* flip the slider limits and values, if necessary */ + if (paint_options->gradient_options->gradient_reverse) + { + for (i = 0; i < n_sliders; i++) + { + gdouble temp; + + sliders[i].value = 1.0 - sliders[i].value; + temp = sliders[i].min; + sliders[i].min = 1.0 - sliders[i].max; + sliders[i].max = 1.0 - temp; + } + } + + /* adjust the sliders according to the offset */ + for (i = 0; i < n_sliders; i++) + { + sliders[i].value = (1.0 - offset) * sliders[i].value + offset; + sliders[i].min = (1.0 - offset) * sliders[i].min + offset; + sliders[i].max = (1.0 - offset) * sliders[i].max + offset; + } + + /* avoid updating the gradient in gimp_gradient_tool_editor_line_changed() */ + gimp_gradient_tool_editor_block_handlers (gradient_tool); + + gimp_tool_line_set_sliders (GIMP_TOOL_LINE (gradient_tool->widget), + sliders, n_sliders); + + gimp_gradient_tool_editor_unblock_handlers (gradient_tool); + + g_free (sliders); +} + +static void +gimp_gradient_tool_editor_purge_gradient_history (GSList **stack) +{ + GSList *link; + + /* eliminate all history steps that modify the gradient */ + while ((link = *stack)) + { + GradientInfo *info = link->data; + + if (info->gradient) + { + gimp_gradient_tool_editor_gradient_info_free (info); + + *stack = g_slist_delete_link (*stack, link); + } + else + { + stack = &link->next; + } + } +} + +static void +gimp_gradient_tool_editor_purge_gradient (GimpGradientTool *gradient_tool) +{ + if (gradient_tool->widget) + { + gimp_gradient_tool_editor_update_sliders (gradient_tool); + + gimp_tool_line_set_selection (GIMP_TOOL_LINE (gradient_tool->widget), + GIMP_TOOL_LINE_HANDLE_NONE); + } + + gimp_gradient_tool_editor_purge_gradient_history (&gradient_tool->undo_stack); + gimp_gradient_tool_editor_purge_gradient_history (&gradient_tool->redo_stack); +} + +static GtkWidget * +gimp_gradient_tool_editor_color_entry_new (GimpGradientTool *gradient_tool, + const gchar *title, + Direction direction, + GtkWidget *chain_button, + GtkWidget **color_panel, + GtkWidget **type_combo) +{ + GimpContext *context = GIMP_CONTEXT (GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool)); + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *combo; + GimpRGB color = {}; + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); + + /* the color panel */ + *color_panel = button = gimp_color_panel_new (title, &color, + GIMP_COLOR_AREA_SMALL_CHECKS, + 24, 24); + gimp_color_button_set_update (GIMP_COLOR_BUTTON (button), TRUE); + gimp_color_panel_set_context (GIMP_COLOR_PANEL (button), context); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); + gtk_widget_show (button); + + g_object_set_data (G_OBJECT (button), + "gimp-gradient-tool-editor-direction", + GINT_TO_POINTER (direction)); + g_object_set_data (G_OBJECT (button), + "gimp-gradient-tool-editor-chain-button", + chain_button); + + g_signal_connect (button, "clicked", + G_CALLBACK (gimp_gradient_tool_editor_color_entry_color_clicked), + gradient_tool); + g_signal_connect (button, "color-changed", + G_CALLBACK (gimp_gradient_tool_editor_color_entry_color_changed), + gradient_tool); + g_signal_connect (button, "response", + G_CALLBACK (gimp_gradient_tool_editor_color_entry_color_response), + gradient_tool); + + /* the color type combo */ + *type_combo = combo = gimp_enum_combo_box_new (GIMP_TYPE_GRADIENT_COLOR); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, TRUE, 0); + gtk_widget_show (combo); + + g_object_set_data (G_OBJECT (combo), + "gimp-gradient-tool-editor-direction", + GINT_TO_POINTER (direction)); + g_object_set_data (G_OBJECT (combo), + "gimp-gradient-tool-editor-chain-button", + chain_button); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_gradient_tool_editor_color_entry_type_changed), + gradient_tool); + + return hbox; +} + +static void +gimp_gradient_tool_editor_init_endpoint_gui (GimpGradientTool *gradient_tool) +{ + GimpDisplay *display = GIMP_TOOL (gradient_tool)->display; + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpImage *image = gimp_display_get_image (display); + gdouble xres; + gdouble yres; + GtkWidget *editor; + GtkWidget *table; + GtkWidget *label; + GtkWidget *spinbutton; + GtkWidget *se; + GtkWidget *hbox; + gint row = 0; + + gimp_image_get_resolution (image, &xres, &yres); + + /* the endpoint editor */ + gradient_tool->endpoint_editor = + editor = gimp_editor_new (); + gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (gradient_tool->gui)), + editor, FALSE, TRUE, 0); + + /* the main table */ + table = gtk_table_new (1, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_box_pack_start (GTK_BOX (editor), table, FALSE, TRUE, 0); + gtk_widget_show (table); + + /* the position labels */ + label = gtk_label_new (_("X:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Y:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row + 1, row + 2, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* the position size entry */ + spinbutton = gtk_spin_button_new_with_range (0.0, 0.0, 1.0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); + gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 6); + + gradient_tool->endpoint_se = + se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a", + TRUE, TRUE, FALSE, 6, + GIMP_SIZE_ENTRY_UPDATE_SIZE); + gtk_table_set_row_spacings (GTK_TABLE (se), 4); + gtk_table_set_col_spacings (GTK_TABLE (se), 2); + + gimp_size_entry_add_field (GIMP_SIZE_ENTRY (se), + GTK_SPIN_BUTTON (spinbutton), NULL); + gtk_table_attach_defaults (GTK_TABLE (se), spinbutton, 1, 2, 0, 1); + gtk_widget_show (spinbutton); + + gtk_table_attach (GTK_TABLE (table), se, 1, 2, row, row + 2, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (se); + + gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (se), shell->unit); + + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (se), 0, xres, FALSE); + gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (se), 1, yres, FALSE); + + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (se), 0, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE); + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (se), 1, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE); + + gimp_size_entry_set_size (GIMP_SIZE_ENTRY (se), 0, + 0, gimp_image_get_width (image)); + gimp_size_entry_set_size (GIMP_SIZE_ENTRY (se), 1, + 0, gimp_image_get_height (image)); + + g_signal_connect (se, "value-changed", + G_CALLBACK (gimp_gradient_tool_editor_endpoint_se_value_changed), + gradient_tool); + + row += 2; + + /* the color label */ + label = gtk_label_new (_("Color:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* the color entry */ + hbox = gimp_gradient_tool_editor_color_entry_new ( + gradient_tool, _("Change Endpoint Color"), DIRECTION_NONE, NULL, + &gradient_tool->endpoint_color_panel, &gradient_tool->endpoint_type_combo); + gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (hbox); + + row++; +} + +static void +gimp_gradient_tool_editor_init_stop_gui (GimpGradientTool *gradient_tool) +{ + GtkWidget *editor; + GtkWidget *table; + GtkWidget *label; + GtkWidget *se; + GtkWidget *table2; + GtkWidget *button; + GtkWidget *hbox; + GtkWidget *separator; + gint row = 0; + + /* the stop editor */ + gradient_tool->stop_editor = + editor = gimp_editor_new (); + gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (gradient_tool->gui)), + editor, FALSE, TRUE, 0); + + /* the main table */ + table = gtk_table_new (1, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_box_pack_start (GTK_BOX (editor), table, FALSE, TRUE, 0); + gtk_widget_show (table); + + /* the position label */ + label = gtk_label_new (_("Position:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* the position size entry */ + gradient_tool->stop_se = + se = gimp_size_entry_new (1, GIMP_UNIT_PERCENT, "%a", + FALSE, TRUE, FALSE, 6, + GIMP_SIZE_ENTRY_UPDATE_SIZE); + gimp_size_entry_show_unit_menu (GIMP_SIZE_ENTRY (se), FALSE); + gtk_table_attach (GTK_TABLE (table), se, 1, 2, row, row + 1, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (se); + + g_signal_connect (se, "value-changed", + G_CALLBACK (gimp_gradient_tool_editor_stop_se_value_changed), + gradient_tool); + + row++; + + /* the color labels */ + label = gtk_label_new (_("Left color:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + label = gtk_label_new (_("Right color:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row + 1, row + 2, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* the color entries table */ + table2 = gtk_table_new (1, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table2), 4); + gtk_table_set_col_spacings (GTK_TABLE (table2), 2); + gtk_table_attach (GTK_TABLE (table), table2, 1, 2, row, row + 2, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (table2); + + /* the color entries chain button */ + gradient_tool->stop_chain_button = + button = gimp_chain_button_new (GIMP_CHAIN_RIGHT); + gtk_table_attach (GTK_TABLE (table2), button, 1, 2, 0, 2, + GTK_SHRINK | GTK_FILL, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + 0, 0); + gtk_widget_show (button); + + /* the color entries */ + hbox = gimp_gradient_tool_editor_color_entry_new ( + gradient_tool, _("Change Stop Color"), DIRECTION_LEFT, button, + &gradient_tool->stop_left_color_panel, &gradient_tool->stop_left_type_combo); + gtk_table_attach (GTK_TABLE (table2), hbox, 0, 1, 0, 1, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (hbox); + + hbox = gimp_gradient_tool_editor_color_entry_new ( + gradient_tool, _("Change Stop Color"), DIRECTION_RIGHT, button, + &gradient_tool->stop_right_color_panel, &gradient_tool->stop_right_type_combo); + gtk_table_attach (GTK_TABLE (table2), hbox, 0, 1, 1, 2, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (hbox); + + row += 2; + + /* the action buttons separator */ + separator = gtk_hseparator_new (); + gtk_table_attach (GTK_TABLE (table), separator, 0, 2, row, row + 1, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (separator); + + row++; + + /* the delete button */ + gimp_editor_add_button (GIMP_EDITOR (editor), + GIMP_ICON_EDIT_DELETE, _("Delete stop"), + NULL, + G_CALLBACK (gimp_gradient_tool_editor_stop_delete_clicked), + NULL, gradient_tool); +} + +static void +gimp_gradient_tool_editor_init_midpoint_gui (GimpGradientTool *gradient_tool) +{ + GtkWidget *editor; + GtkWidget *table; + GtkWidget *label; + GtkWidget *se; + GtkWidget *combo; + GtkWidget *separator; + gint row = 0; + + /* the stop editor */ + gradient_tool->midpoint_editor = + editor = gimp_editor_new (); + gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (gradient_tool->gui)), + editor, FALSE, TRUE, 0); + + /* the main table */ + table = gtk_table_new (1, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_box_pack_start (GTK_BOX (editor), table, FALSE, TRUE, 0); + gtk_widget_show (table); + + /* the position label */ + label = gtk_label_new (_("Position:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* the position size entry */ + gradient_tool->midpoint_se = + se = gimp_size_entry_new (1, GIMP_UNIT_PERCENT, "%a", + FALSE, TRUE, FALSE, 6, + GIMP_SIZE_ENTRY_UPDATE_SIZE); + gimp_size_entry_show_unit_menu (GIMP_SIZE_ENTRY (se), FALSE); + gtk_table_attach (GTK_TABLE (table), se, 1, 2, row, row + 1, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (se); + + g_signal_connect (se, "value-changed", + G_CALLBACK (gimp_gradient_tool_editor_midpoint_se_value_changed), + gradient_tool); + + row++; + + /* the type label */ + label = gtk_label_new (_("Gradienting:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* the type combo */ + gradient_tool->midpoint_type_combo = + combo = gimp_enum_combo_box_new (GIMP_TYPE_GRADIENT_SEGMENT_TYPE); + gtk_table_attach (GTK_TABLE (table), combo, 1, 2, row, row + 1, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (combo); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_gradient_tool_editor_midpoint_type_changed), + gradient_tool); + + row++; + + /* the color label */ + label = gtk_label_new (_("Coloring:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1, + GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0); + gtk_widget_show (label); + + /* the color combo */ + gradient_tool->midpoint_color_combo = + combo = gimp_enum_combo_box_new (GIMP_TYPE_GRADIENT_SEGMENT_COLOR); + gtk_table_attach (GTK_TABLE (table), combo, 1, 2, row, row + 1, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (combo); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_gradient_tool_editor_midpoint_color_changed), + gradient_tool); + + row++; + + /* the action buttons separator */ + separator = gtk_hseparator_new (); + gtk_table_attach (GTK_TABLE (table), separator, 0, 2, row, row + 1, + GTK_SHRINK | GTK_FILL | GTK_EXPAND, + GTK_SHRINK | GTK_FILL, + 0, 0); + gtk_widget_show (separator); + + row++; + + /* the new stop button */ + gradient_tool->midpoint_new_stop_button = + gimp_editor_add_button (GIMP_EDITOR (editor), + GIMP_ICON_DOCUMENT_NEW, _("New stop at midpoint"), + NULL, + G_CALLBACK (gimp_gradient_tool_editor_midpoint_new_stop_clicked), + NULL, gradient_tool); + + /* the center button */ + gradient_tool->midpoint_center_button = + gimp_editor_add_button (GIMP_EDITOR (editor), + GIMP_ICON_CENTER_HORIZONTAL, _("Center midpoint"), + NULL, + G_CALLBACK (gimp_gradient_tool_editor_midpoint_center_clicked), + NULL, gradient_tool); +} + +static void +gimp_gradient_tool_editor_update_endpoint_gui (GimpGradientTool *gradient_tool, + gint selection) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); + GimpContext *context = GIMP_CONTEXT (options); + gboolean editable; + GimpGradientSegment *seg; + const gchar *title; + gdouble x; + gdouble y; + GimpRGB color; + GimpGradientColor color_type; + + editable = gimp_gradient_tool_editor_is_gradient_editable (gradient_tool); + + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + g_object_get (gradient_tool->widget, + "x1", &x, + "y1", &y, + NULL); + break; + + case GIMP_TOOL_LINE_HANDLE_END: + g_object_get (gradient_tool->widget, + "x2", &x, + "y2", &y, + NULL); + break; + + default: + gimp_assert_not_reached (); + } + + /* swap the endpoint handles, if necessary */ + if (paint_options->gradient_options->gradient_reverse) + { + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + selection = GIMP_TOOL_LINE_HANDLE_END; + break; + + case GIMP_TOOL_LINE_HANDLE_END: + selection = GIMP_TOOL_LINE_HANDLE_START; + break; + } + } + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + title = _("Start Endpoint"); + + gimp_gradient_segment_get_left_flat_color (gradient_tool->gradient, context, + seg, &color); + color_type = seg->left_color_type; + break; + + case GIMP_TOOL_LINE_HANDLE_END: + title = _("End Endpoint"); + + gimp_gradient_segment_get_right_flat_color (gradient_tool->gradient, context, + seg, &color); + color_type = seg->right_color_type; + break; + + default: + gimp_assert_not_reached (); + } + + gimp_tool_gui_set_title (gradient_tool->gui, title); + + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (gradient_tool->endpoint_se), 0, x); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (gradient_tool->endpoint_se), 1, y); + + gimp_color_button_set_color ( + GIMP_COLOR_BUTTON (gradient_tool->endpoint_color_panel), &color); + gimp_int_combo_box_set_active ( + GIMP_INT_COMBO_BOX (gradient_tool->endpoint_type_combo), color_type); + + gtk_widget_set_sensitive (gradient_tool->endpoint_color_panel, editable); + gtk_widget_set_sensitive (gradient_tool->endpoint_type_combo, editable); + + gtk_widget_show (gradient_tool->endpoint_editor); +} + +static void +gimp_gradient_tool_editor_update_stop_gui (GimpGradientTool *gradient_tool, + gint selection) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpContext *context = GIMP_CONTEXT (options); + gboolean editable; + GimpGradientSegment *seg; + gint index; + gchar *title; + gdouble min; + gdouble max; + gdouble value; + GimpRGB left_color; + GimpGradientColor left_color_type; + GimpRGB right_color; + GimpGradientColor right_color_type; + + editable = gimp_gradient_tool_editor_is_gradient_editable (gradient_tool); + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + index = GPOINTER_TO_INT ( + gimp_tool_line_get_sliders (GIMP_TOOL_LINE (gradient_tool->widget), + NULL)[selection].data); + + title = g_strdup_printf (_("Stop %d"), index + 1); + + min = seg->left; + max = seg->next->right; + value = seg->right; + + gimp_gradient_segment_get_right_flat_color (gradient_tool->gradient, context, + seg, &left_color); + left_color_type = seg->right_color_type; + + gimp_gradient_segment_get_left_flat_color (gradient_tool->gradient, context, + seg->next, &right_color); + right_color_type = seg->next->left_color_type; + + gimp_tool_gui_set_title (gradient_tool->gui, title); + + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (gradient_tool->stop_se), + 0, 100.0 * min, 100.0 * max); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (gradient_tool->stop_se), + 0, 100.0 * value); + + gimp_color_button_set_color ( + GIMP_COLOR_BUTTON (gradient_tool->stop_left_color_panel), &left_color); + gimp_int_combo_box_set_active ( + GIMP_INT_COMBO_BOX (gradient_tool->stop_left_type_combo), left_color_type); + + gimp_color_button_set_color ( + GIMP_COLOR_BUTTON (gradient_tool->stop_right_color_panel), &right_color); + gimp_int_combo_box_set_active ( + GIMP_INT_COMBO_BOX (gradient_tool->stop_right_type_combo), right_color_type); + + gtk_widget_set_sensitive (gradient_tool->stop_se, editable); + gtk_widget_set_sensitive (gradient_tool->stop_left_color_panel, editable); + gtk_widget_set_sensitive (gradient_tool->stop_left_type_combo, editable); + gtk_widget_set_sensitive (gradient_tool->stop_right_color_panel, editable); + gtk_widget_set_sensitive (gradient_tool->stop_right_type_combo, editable); + gtk_widget_set_sensitive (gradient_tool->stop_chain_button, editable); + gtk_widget_set_sensitive ( + GTK_WIDGET (gimp_editor_get_button_box (GIMP_EDITOR (gradient_tool->stop_editor))), + editable); + + g_free (title); + + gtk_widget_show (gradient_tool->stop_editor); +} + +static void +gimp_gradient_tool_editor_update_midpoint_gui (GimpGradientTool *gradient_tool, + gint selection) +{ + gboolean editable; + const GimpGradientSegment *seg; + gint index; + gchar *title; + gdouble min; + gdouble max; + gdouble value; + GimpGradientSegmentType type; + GimpGradientSegmentColor color; + + editable = gimp_gradient_tool_editor_is_gradient_editable (gradient_tool); + + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, selection); + + index = GPOINTER_TO_INT ( + gimp_tool_line_get_sliders (GIMP_TOOL_LINE (gradient_tool->widget), + NULL)[selection].data); + + title = g_strdup_printf (_("Midpoint %d"), index + 1); + + min = seg->left; + max = seg->right; + value = seg->middle; + type = seg->type; + color = seg->color; + + gimp_tool_gui_set_title (gradient_tool->gui, title); + + gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (gradient_tool->midpoint_se), + 0, 100.0 * min, 100.0 * max); + gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (gradient_tool->midpoint_se), + 0, 100.0 * value); + + gimp_int_combo_box_set_active ( + GIMP_INT_COMBO_BOX (gradient_tool->midpoint_type_combo), type); + + gimp_int_combo_box_set_active ( + GIMP_INT_COMBO_BOX (gradient_tool->midpoint_color_combo), color); + + gtk_widget_set_sensitive (gradient_tool->midpoint_new_stop_button, + value > min + EPSILON && value < max - EPSILON); + gtk_widget_set_sensitive (gradient_tool->midpoint_center_button, + fabs (value - (min + max) / 2.0) > EPSILON); + + gtk_widget_set_sensitive (gradient_tool->midpoint_se, editable); + gtk_widget_set_sensitive (gradient_tool->midpoint_type_combo, editable); + gtk_widget_set_sensitive (gradient_tool->midpoint_color_combo, editable); + gtk_widget_set_sensitive ( + GTK_WIDGET (gimp_editor_get_button_box (GIMP_EDITOR (gradient_tool->midpoint_editor))), + editable); + + g_free (title); + + gtk_widget_show (gradient_tool->midpoint_editor); +} + +static void +gimp_gradient_tool_editor_update_gui (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + + if (gradient_tool->gradient && gradient_tool->widget && ! options->instant) + { + gint selection; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + if (selection != GIMP_TOOL_LINE_HANDLE_NONE) + { + if (! gradient_tool->gui) + { + GimpDisplayShell *shell; + + shell = gimp_tool_widget_get_shell (gradient_tool->widget); + + gradient_tool->gui = + gimp_tool_gui_new (GIMP_TOOL (gradient_tool)->tool_info, + NULL, NULL, NULL, NULL, + gtk_widget_get_screen (GTK_WIDGET (shell)), + gimp_widget_get_monitor (GTK_WIDGET (shell)), + TRUE, + + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + gimp_tool_gui_set_shell (gradient_tool->gui, shell); + gimp_tool_gui_set_viewable (gradient_tool->gui, + GIMP_VIEWABLE (gradient_tool->gradient)); + gimp_tool_gui_set_auto_overlay (gradient_tool->gui, TRUE); + + g_signal_connect (gradient_tool->gui, "response", + G_CALLBACK (gimp_gradient_tool_editor_gui_response), + gradient_tool); + + gimp_gradient_tool_editor_init_endpoint_gui (gradient_tool); + gimp_gradient_tool_editor_init_stop_gui (gradient_tool); + gimp_gradient_tool_editor_init_midpoint_gui (gradient_tool); + } + + gimp_gradient_tool_editor_block_handlers (gradient_tool); + + if (gimp_gradient_tool_editor_handle_is_endpoint (gradient_tool, selection)) + gimp_gradient_tool_editor_update_endpoint_gui (gradient_tool, selection); + else + gtk_widget_hide (gradient_tool->endpoint_editor); + + if (gimp_gradient_tool_editor_handle_is_stop (gradient_tool, selection)) + gimp_gradient_tool_editor_update_stop_gui (gradient_tool, selection); + else + gtk_widget_hide (gradient_tool->stop_editor); + + if (gimp_gradient_tool_editor_handle_is_midpoint (gradient_tool, selection)) + gimp_gradient_tool_editor_update_midpoint_gui (gradient_tool, selection); + else + gtk_widget_hide (gradient_tool->midpoint_editor); + + gimp_gradient_tool_editor_unblock_handlers (gradient_tool); + + gimp_tool_gui_show (gradient_tool->gui); + + return; + } + } + + if (gradient_tool->gui) + gimp_tool_gui_hide (gradient_tool->gui); +} + +static GradientInfo * +gimp_gradient_tool_editor_gradient_info_new (GimpGradientTool *gradient_tool) +{ + GradientInfo *info = g_slice_new (GradientInfo); + + info->start_x = gradient_tool->start_x; + info->start_y = gradient_tool->start_y; + info->end_x = gradient_tool->end_x; + info->end_y = gradient_tool->end_y; + + info->gradient = NULL; + + info->added_handle = GIMP_TOOL_LINE_HANDLE_NONE; + info->removed_handle = GIMP_TOOL_LINE_HANDLE_NONE; + info->selected_handle = GIMP_TOOL_LINE_HANDLE_NONE; + + return info; +} + +static void +gimp_gradient_tool_editor_gradient_info_free (GradientInfo *info) +{ + if (info->gradient) + g_object_unref (info->gradient); + + g_slice_free (GradientInfo, info); +} + +static void +gimp_gradient_tool_editor_gradient_info_apply (GimpGradientTool *gradient_tool, + const GradientInfo *info, + gboolean set_selection) +{ + gint selection; + + gimp_assert (gradient_tool->widget != NULL); + gimp_assert (gradient_tool->gradient != NULL); + + /* pick the handle to select */ + if (info->gradient) + { + if (info->removed_handle != GIMP_TOOL_LINE_HANDLE_NONE) + { + /* we're undoing a stop-deletion or midpoint-to-stop operation; + * select the removed handle + */ + selection = info->removed_handle; + } + else if (info->added_handle != GIMP_TOOL_LINE_HANDLE_NONE) + { + /* we're undoing a stop addition operation */ + gimp_assert (gimp_gradient_tool_editor_handle_is_stop (gradient_tool, + info->added_handle)); + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + /* if the selected handle is a stop... */ + if (gimp_gradient_tool_editor_handle_is_stop (gradient_tool, selection)) + { + /* if the added handle is selected, clear the selection */ + if (selection == info->added_handle) + selection = GIMP_TOOL_LINE_HANDLE_NONE; + /* otherwise, keep the currently selected stop, possibly + * adjusting its handle index + */ + else if (selection > info->added_handle) + selection--; + } + /* otherwise, if the selected handle is a midpoint... */ + else if (gimp_gradient_tool_editor_handle_is_midpoint (gradient_tool, selection)) + { + const GimpControllerSlider *sliders; + gint seg_i; + + sliders = + gimp_tool_line_get_sliders (GIMP_TOOL_LINE (gradient_tool->widget), + NULL); + + seg_i = GPOINTER_TO_INT (sliders[selection].data); + + /* if the midpoint belongs to one of the two segments incident to + * the added stop, clear the selection + */ + if (seg_i == info->added_handle || + seg_i == info->added_handle + 1) + { + selection = GIMP_TOOL_LINE_HANDLE_NONE; + } + /* otherwise, keep the currently selected stop, adjusting its + * handle index + */ + else + { + /* midpoint handles follow stop handles; since we removed a + * stop, we must decrement the handle index + */ + selection--; + + if (seg_i > info->added_handle) + selection--; + } + } + /* otherwise, don't change the selection */ + else + { + set_selection = FALSE; + } + } + else if (info->selected_handle != GIMP_TOOL_LINE_HANDLE_NONE) + { + /* we're undoing a property change operation; select the handle + * corresponding to the affected object + */ + selection = info->selected_handle; + } + else + { + /* something went wrong... */ + g_warn_if_reached (); + + set_selection = FALSE; + } + } + else if ((info->start_x != gradient_tool->start_x || + info->start_y != gradient_tool->start_y) && + (info->end_x == gradient_tool->end_x && + info->end_y == gradient_tool->end_y)) + { + /* we're undoing a start-endpoint move operation; select the start + * endpoint + */ + selection = GIMP_TOOL_LINE_HANDLE_START; + } + else if ((info->end_x != gradient_tool->end_x || + info->end_y != gradient_tool->end_y) && + (info->start_x == gradient_tool->start_x && + info->start_y == gradient_tool->start_y)) + + { + /* we're undoing am end-endpoint move operation; select the end + * endpoint + */ + selection = GIMP_TOOL_LINE_HANDLE_END; + } + else + { + /* we're undoing a line move operation; don't change the selection */ + set_selection = FALSE; + } + + gimp_gradient_tool_editor_block_handlers (gradient_tool); + + g_object_set (gradient_tool->widget, + "x1", info->start_x, + "y1", info->start_y, + "x2", info->end_x, + "y2", info->end_y, + NULL); + + if (info->gradient) + { + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + gimp_data_copy (GIMP_DATA (gradient_tool->gradient), + GIMP_DATA (info->gradient)); + + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + } + + if (set_selection) + { + gimp_tool_line_set_selection (GIMP_TOOL_LINE (gradient_tool->widget), + selection); + } + + gimp_gradient_tool_editor_update_gui (gradient_tool); + + gimp_gradient_tool_editor_unblock_handlers (gradient_tool); +} + +static gboolean +gimp_gradient_tool_editor_gradient_info_is_trivial (GimpGradientTool *gradient_tool, + const GradientInfo *info) +{ + const GimpGradientSegment *seg1; + const GimpGradientSegment *seg2; + + if (info->start_x != gradient_tool->start_x || + info->start_y != gradient_tool->start_y || + info->end_x != gradient_tool->end_x || + info->end_y != gradient_tool->end_y) + { + return FALSE; + } + + if (info->gradient) + { + for (seg1 = info->gradient->segments, seg2 = gradient_tool->gradient->segments; + seg1 && seg2; + seg1 = seg1->next, seg2 = seg2->next) + { + if (memcmp (seg1, seg2, G_STRUCT_OFFSET (GimpGradientSegment, prev))) + return FALSE; + } + + if (seg1 || seg2) + return FALSE; + } + + return TRUE; +} + + +/* public functions */ + +void +gimp_gradient_tool_editor_options_notify (GimpGradientTool *gradient_tool, + GimpToolOptions *options, + const GParamSpec *pspec) +{ + if (! strcmp (pspec->name, "modify-active")) + { + gimp_gradient_tool_editor_update_sliders (gradient_tool); + gimp_gradient_tool_editor_update_gui (gradient_tool); + } + else if (! strcmp (pspec->name, "gradient-reverse")) + { + gimp_gradient_tool_editor_update_sliders (gradient_tool); + + /* if an endpoint is selected, swap the selected endpoint */ + if (gradient_tool->widget) + { + gint selection; + + selection = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + switch (selection) + { + case GIMP_TOOL_LINE_HANDLE_START: + gimp_tool_line_set_selection (GIMP_TOOL_LINE (gradient_tool->widget), + GIMP_TOOL_LINE_HANDLE_END); + break; + + case GIMP_TOOL_LINE_HANDLE_END: + gimp_tool_line_set_selection (GIMP_TOOL_LINE (gradient_tool->widget), + GIMP_TOOL_LINE_HANDLE_START); + break; + } + } + } + else if (gradient_tool->render_node && + gegl_node_find_property (gradient_tool->render_node, pspec->name)) + { + gimp_gradient_tool_editor_update_sliders (gradient_tool); + } +} + +void +gimp_gradient_tool_editor_start (GimpGradientTool *gradient_tool) +{ + g_signal_connect (gradient_tool->widget, "can-add-slider", + G_CALLBACK (gimp_gradient_tool_editor_line_can_add_slider), + gradient_tool); + g_signal_connect (gradient_tool->widget, "add-slider", + G_CALLBACK (gimp_gradient_tool_editor_line_add_slider), + gradient_tool); + g_signal_connect (gradient_tool->widget, "prepare-to-remove-slider", + G_CALLBACK (gimp_gradient_tool_editor_line_prepare_to_remove_slider), + gradient_tool); + g_signal_connect (gradient_tool->widget, "remove-slider", + G_CALLBACK (gimp_gradient_tool_editor_line_remove_slider), + gradient_tool); + g_signal_connect (gradient_tool->widget, "selection-changed", + G_CALLBACK (gimp_gradient_tool_editor_line_selection_changed), + gradient_tool); + g_signal_connect (gradient_tool->widget, "handle-clicked", + G_CALLBACK (gimp_gradient_tool_editor_line_handle_clicked), + gradient_tool); +} + +void +gimp_gradient_tool_editor_halt (GimpGradientTool *gradient_tool) +{ + g_clear_object (&gradient_tool->gui); + + gradient_tool->edit_count = 0; + + if (gradient_tool->undo_stack) + { + g_slist_free_full (gradient_tool->undo_stack, + (GDestroyNotify) gimp_gradient_tool_editor_gradient_info_free); + gradient_tool->undo_stack = NULL; + } + + if (gradient_tool->redo_stack) + { + g_slist_free_full (gradient_tool->redo_stack, + (GDestroyNotify) gimp_gradient_tool_editor_gradient_info_free); + gradient_tool->redo_stack = NULL; + } + + if (gradient_tool->flush_idle_id) + { + g_source_remove (gradient_tool->flush_idle_id); + gradient_tool->flush_idle_id = 0; + } +} + +gboolean +gimp_gradient_tool_editor_line_changed (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpPaintOptions *paint_options = GIMP_PAINT_OPTIONS (options); + gdouble offset = options->offset / 100.0; + const GimpControllerSlider *sliders; + gint n_sliders; + gint i; + GimpGradientSegment *seg; + gboolean changed = FALSE; + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return FALSE; + + if (! gradient_tool->gradient || offset == 1.0) + return FALSE; + + sliders = gimp_tool_line_get_sliders (GIMP_TOOL_LINE (gradient_tool->widget), + &n_sliders); + + if (n_sliders == 0) + return FALSE; + + /* update the midpoints first, since moving the gradient stops may change the + * gradient's midpoints w.r.t. the sliders, but not the other way around. + */ + for (seg = gradient_tool->gradient->segments, i = n_sliders / 2; + seg; + seg = seg->next, i++) + { + gdouble value; + + value = sliders[i].value; + + /* adjust slider value according to the offset */ + value = (value - offset) / (1.0 - offset); + + /* flip the slider value, if necessary */ + if (paint_options->gradient_options->gradient_reverse) + value = 1.0 - value; + + if (fabs (value - seg->middle) > EPSILON) + { + if (! changed) + { + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + /* refetch the segment, since the gradient might have changed */ + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, i); + + changed = TRUE; + } + + seg->middle = value; + } + } + + /* update the gradient stops */ + for (seg = gradient_tool->gradient->segments, i = 0; + seg->next; + seg = seg->next, i++) + { + gdouble value; + + value = sliders[i].value; + + /* adjust slider value according to the offset */ + value = (value - offset) / (1.0 - offset); + + /* flip the slider value, if necessary */ + if (paint_options->gradient_options->gradient_reverse) + value = 1.0 - value; + + if (fabs (value - seg->right) > EPSILON) + { + if (! changed) + { + gimp_gradient_tool_editor_start_edit (gradient_tool); + gimp_gradient_tool_editor_freeze_gradient (gradient_tool); + + /* refetch the segment, since the gradient might have changed */ + seg = gimp_gradient_tool_editor_handle_get_segment (gradient_tool, i); + + changed = TRUE; + } + + gimp_gradient_segment_range_compress (gradient_tool->gradient, + seg, seg, + seg->left, value); + gimp_gradient_segment_range_compress (gradient_tool->gradient, + seg->next, seg->next, + value, seg->next->right); + } + } + + if (changed) + { + gimp_gradient_tool_editor_thaw_gradient (gradient_tool); + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); + } + + gimp_gradient_tool_editor_update_gui (gradient_tool); + + return changed; +} + +void +gimp_gradient_tool_editor_fg_bg_changed (GimpGradientTool *gradient_tool) +{ + gimp_gradient_tool_editor_update_gui (gradient_tool); +} + +void +gimp_gradient_tool_editor_gradient_dirty (GimpGradientTool *gradient_tool) +{ + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + gimp_gradient_tool_editor_purge_gradient (gradient_tool); +} + +void +gimp_gradient_tool_editor_gradient_changed (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpContext *context = GIMP_CONTEXT (options); + + if (options->modify_active_frame) + { + gtk_widget_set_sensitive (options->modify_active_frame, + gradient_tool->gradient != + gimp_gradients_get_custom (context->gimp)); + } + + if (options->modify_active_hint) + { + gtk_widget_set_visible (options->modify_active_hint, + gradient_tool->gradient && + ! gimp_data_is_writable (GIMP_DATA (gradient_tool->gradient))); + } + + if (gimp_gradient_tool_editor_are_handlers_blocked (gradient_tool)) + return; + + gimp_gradient_tool_editor_purge_gradient (gradient_tool); +} + +const gchar * +gimp_gradient_tool_editor_can_undo (GimpGradientTool *gradient_tool) +{ + if (! gradient_tool->undo_stack || gradient_tool->edit_count > 0) + return NULL; + + return _("Gradient Step"); +} + +const gchar * +gimp_gradient_tool_editor_can_redo (GimpGradientTool *gradient_tool) +{ + if (! gradient_tool->redo_stack || gradient_tool->edit_count > 0) + return NULL; + + return _("Gradient Step"); +} + +gboolean +gimp_gradient_tool_editor_undo (GimpGradientTool *gradient_tool) +{ + GimpTool *tool = GIMP_TOOL (gradient_tool); + GradientInfo *info; + GradientInfo *new_info; + + gimp_assert (gradient_tool->undo_stack != NULL); + gimp_assert (gradient_tool->edit_count == 0); + + info = gradient_tool->undo_stack->data; + + new_info = gimp_gradient_tool_editor_gradient_info_new (gradient_tool); + + if (info->gradient) + { + new_info->gradient = + GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (gradient_tool->gradient))); + + /* swap the added and removed handles, so that gradient_info_apply() does + * the right thing on redo + */ + new_info->added_handle = info->removed_handle; + new_info->removed_handle = info->added_handle; + new_info->selected_handle = info->selected_handle; + } + + gradient_tool->undo_stack = g_slist_remove (gradient_tool->undo_stack, info); + gradient_tool->redo_stack = g_slist_prepend (gradient_tool->redo_stack, new_info); + + gimp_gradient_tool_editor_gradient_info_apply (gradient_tool, info, TRUE); + gimp_gradient_tool_editor_gradient_info_free (info); + + /* the initial state of the gradient tool is not useful; we might as well halt */ + if (! gradient_tool->undo_stack) + gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display); + + return TRUE; +} + +gboolean +gimp_gradient_tool_editor_redo (GimpGradientTool *gradient_tool) +{ + GradientInfo *info; + GradientInfo *new_info; + + gimp_assert (gradient_tool->redo_stack != NULL); + gimp_assert (gradient_tool->edit_count == 0); + + info = gradient_tool->redo_stack->data; + + new_info = gimp_gradient_tool_editor_gradient_info_new (gradient_tool); + + if (info->gradient) + { + new_info->gradient = + GIMP_GRADIENT (gimp_data_duplicate (GIMP_DATA (gradient_tool->gradient))); + + /* swap the added and removed handles, so that gradient_info_apply() does + * the right thing on undo + */ + new_info->added_handle = info->removed_handle; + new_info->removed_handle = info->added_handle; + new_info->selected_handle = info->selected_handle; + } + + gradient_tool->redo_stack = g_slist_remove (gradient_tool->redo_stack, info); + gradient_tool->undo_stack = g_slist_prepend (gradient_tool->undo_stack, new_info); + + gimp_gradient_tool_editor_gradient_info_apply (gradient_tool, info, TRUE); + gimp_gradient_tool_editor_gradient_info_free (info); + + return TRUE; +} + +void +gimp_gradient_tool_editor_start_edit (GimpGradientTool *gradient_tool) +{ + if (gradient_tool->edit_count++ == 0) + { + GradientInfo *info; + + info = gimp_gradient_tool_editor_gradient_info_new (gradient_tool); + + gradient_tool->undo_stack = g_slist_prepend (gradient_tool->undo_stack, info); + + /* update the undo actions / menu items */ + if (! gradient_tool->flush_idle_id) + { + gradient_tool->flush_idle_id = + g_idle_add ((GSourceFunc) gimp_gradient_tool_editor_flush_idle, + gradient_tool); + } + } +} + +void +gimp_gradient_tool_editor_end_edit (GimpGradientTool *gradient_tool, + gboolean cancel) +{ + /* can happen when halting using esc */ + if (gradient_tool->edit_count == 0) + return; + + if (--gradient_tool->edit_count == 0) + { + GradientInfo *info = gradient_tool->undo_stack->data; + + info->selected_handle = + gimp_tool_line_get_selection (GIMP_TOOL_LINE (gradient_tool->widget)); + + if (cancel || + gimp_gradient_tool_editor_gradient_info_is_trivial (gradient_tool, info)) + { + /* if the edit is canceled, or if nothing changed, undo the last + * step + */ + gimp_gradient_tool_editor_gradient_info_apply (gradient_tool, info, FALSE); + + gradient_tool->undo_stack = g_slist_remove (gradient_tool->undo_stack, + info); + gimp_gradient_tool_editor_gradient_info_free (info); + } + else + { + /* otherwise, blow the redo stack */ + g_slist_free_full (gradient_tool->redo_stack, + (GDestroyNotify) gimp_gradient_tool_editor_gradient_info_free); + gradient_tool->redo_stack = NULL; + } + + /* update the undo actions / menu items */ + if (! gradient_tool->flush_idle_id) + { + gradient_tool->flush_idle_id = + g_idle_add ((GSourceFunc) gimp_gradient_tool_editor_flush_idle, + gradient_tool); + } + } +} diff --git a/app/tools/gimpgradienttool-editor.h b/app/tools/gimpgradienttool-editor.h new file mode 100644 index 0000000000..d27a2fd3df --- /dev/null +++ b/app/tools/gimpgradienttool-editor.h @@ -0,0 +1,48 @@ +/* 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 . + */ + +#ifndef __GIMP_GRADIENT_TOOL_EDITOR_H__ +#define __GIMP_GRADIENT_TOOL_EDITOR_H__ + + +void gimp_gradient_tool_editor_options_notify (GimpGradientTool *gradient_tool, + GimpToolOptions *options, + const GParamSpec *pspec); + +void gimp_gradient_tool_editor_start (GimpGradientTool *gradient_tool); +void gimp_gradient_tool_editor_halt (GimpGradientTool *gradient_tool); + +gboolean gimp_gradient_tool_editor_line_changed (GimpGradientTool *gradient_tool); + +void gimp_gradient_tool_editor_fg_bg_changed (GimpGradientTool *gradient_tool); + +void gimp_gradient_tool_editor_gradient_dirty (GimpGradientTool *gradient_tool); + +void gimp_gradient_tool_editor_gradient_changed (GimpGradientTool *gradient_tool); + +const gchar * gimp_gradient_tool_editor_can_undo (GimpGradientTool *gradient_tool); +const gchar * gimp_gradient_tool_editor_can_redo (GimpGradientTool *gradient_tool); + +gboolean gimp_gradient_tool_editor_undo (GimpGradientTool *gradient_tool); +gboolean gimp_gradient_tool_editor_redo (GimpGradientTool *gradient_tool); + +void gimp_gradient_tool_editor_start_edit (GimpGradientTool *gradient_tool); +void gimp_gradient_tool_editor_end_edit (GimpGradientTool *gradient_tool, + gboolean cancel); + + +#endif /* __GIMP_GRADIENT_TOOL_EDITOR_H__ */ diff --git a/app/tools/gimpgradienttool.c b/app/tools/gimpgradienttool.c new file mode 100644 index 0000000000..e71a01c279 --- /dev/null +++ b/app/tools/gimpgradienttool.c @@ -0,0 +1,1073 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Major improvements for interactivity + * Copyright (C) 2014 Michael Henning + * + * 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 "libgimpwidgets/gimpwidgets.h" + +#include "tools-types.h" + +#include "operations/gimp-operation-config.h" + +#include "core/gimpdrawable.h" +#include "core/gimpdrawable-gradient.h" +#include "core/gimpdrawablefilter.h" +#include "core/gimperror.h" +#include "core/gimpgradient.h" +#include "core/gimpimage.h" +#include "core/gimpprogress.h" +#include "core/gimpprojection.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimptoolline.h" + +#include "gimpgradientoptions.h" +#include "gimpgradienttool.h" +#include "gimpgradienttool-editor.h" +#include "gimptoolcontrol.h" + +#include "gimp-intl.h" + + +/* local function prototypes */ + +static void gimp_gradient_tool_dispose (GObject *object); + +static gboolean gimp_gradient_tool_initialize (GimpTool *tool, + GimpDisplay *display, + GError **error); +static void gimp_gradient_tool_control (GimpTool *tool, + GimpToolAction action, + GimpDisplay *display); +static void gimp_gradient_tool_button_press (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type, + GimpDisplay *display); +static void gimp_gradient_tool_button_release (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type, + GimpDisplay *display); +static void gimp_gradient_tool_motion (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplay *display); +static gboolean gimp_gradient_tool_key_press (GimpTool *tool, + GdkEventKey *kevent, + GimpDisplay *display); +static void gimp_gradient_tool_modifier_key (GimpTool *tool, + GdkModifierType key, + gboolean press, + GdkModifierType state, + GimpDisplay *display); +static void gimp_gradient_tool_cursor_update (GimpTool *tool, + const GimpCoords *coords, + GdkModifierType state, + GimpDisplay *display); +static const gchar * gimp_gradient_tool_can_undo (GimpTool *tool, + GimpDisplay *display); +static const gchar * gimp_gradient_tool_can_redo (GimpTool *tool, + GimpDisplay *display); +static gboolean gimp_gradient_tool_undo (GimpTool *tool, + GimpDisplay *display); +static gboolean gimp_gradient_tool_redo (GimpTool *tool, + GimpDisplay *display); +static void gimp_gradient_tool_options_notify (GimpTool *tool, + GimpToolOptions *options, + const GParamSpec *pspec); + +static void gimp_gradient_tool_start (GimpGradientTool *gradient_tool, + const GimpCoords *coords, + GimpDisplay *display); +static void gimp_gradient_tool_halt (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_commit (GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_line_changed (GimpToolWidget *widget, + GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_line_response (GimpToolWidget *widget, + gint response_id, + GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_precalc_shapeburst (GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_create_graph (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_update_graph (GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_fg_bg_changed (GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_gradient_dirty (GimpGradientTool *gradient_tool); +static void gimp_gradient_tool_set_gradient (GimpGradientTool *gradient_tool, + GimpGradient *gradient); + +static gboolean gimp_gradient_tool_is_shapeburst (GimpGradientTool *gradient_tool); + +static void gimp_gradient_tool_create_filter (GimpGradientTool *gradient_tool, + GimpDrawable *drawable); +static void gimp_gradient_tool_filter_flush (GimpDrawableFilter *filter, + GimpTool *tool); + + +G_DEFINE_TYPE (GimpGradientTool, gimp_gradient_tool, GIMP_TYPE_DRAW_TOOL) + +#define parent_class gimp_gradient_tool_parent_class + + +void +gimp_gradient_tool_register (GimpToolRegisterCallback callback, + gpointer data) +{ + (* callback) (GIMP_TYPE_GRADIENT_TOOL, + GIMP_TYPE_GRADIENT_OPTIONS, + gimp_gradient_options_gui, + GIMP_CONTEXT_PROP_MASK_FOREGROUND | + GIMP_CONTEXT_PROP_MASK_BACKGROUND | + GIMP_CONTEXT_PROP_MASK_OPACITY | + GIMP_CONTEXT_PROP_MASK_PAINT_MODE | + GIMP_CONTEXT_PROP_MASK_GRADIENT, + "gimp-gradient-tool", + _("Gradient"), + _("Gradient Tool: Fill selected area with a color gradient"), + N_("Blen_d"), "L", + NULL, GIMP_HELP_TOOL_GRADIENT, + GIMP_ICON_TOOL_GRADIENT, + data); +} + +static void +gimp_gradient_tool_class_init (GimpGradientToolClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass); + + object_class->dispose = gimp_gradient_tool_dispose; + + tool_class->initialize = gimp_gradient_tool_initialize; + tool_class->control = gimp_gradient_tool_control; + tool_class->button_press = gimp_gradient_tool_button_press; + tool_class->button_release = gimp_gradient_tool_button_release; + tool_class->motion = gimp_gradient_tool_motion; + tool_class->key_press = gimp_gradient_tool_key_press; + tool_class->modifier_key = gimp_gradient_tool_modifier_key; + tool_class->cursor_update = gimp_gradient_tool_cursor_update; + tool_class->can_undo = gimp_gradient_tool_can_undo; + tool_class->can_redo = gimp_gradient_tool_can_redo; + tool_class->undo = gimp_gradient_tool_undo; + tool_class->redo = gimp_gradient_tool_redo; + tool_class->options_notify = gimp_gradient_tool_options_notify; +} + +static void +gimp_gradient_tool_init (GimpGradientTool *gradient_tool) +{ + GimpTool *tool = GIMP_TOOL (gradient_tool); + + gimp_tool_control_set_scroll_lock (tool->control, TRUE); + gimp_tool_control_set_preserve (tool->control, FALSE); + gimp_tool_control_set_dirty_mask (tool->control, + GIMP_DIRTY_IMAGE | + GIMP_DIRTY_IMAGE_STRUCTURE | + GIMP_DIRTY_DRAWABLE | + GIMP_DIRTY_ACTIVE_DRAWABLE); + gimp_tool_control_set_wants_click (tool->control, TRUE); + gimp_tool_control_set_wants_double_click (tool->control, TRUE); + gimp_tool_control_set_active_modifiers (tool->control, + GIMP_TOOL_ACTIVE_MODIFIERS_SEPARATE); + gimp_tool_control_set_precision (tool->control, + GIMP_CURSOR_PRECISION_SUBPIXEL); + gimp_tool_control_set_tool_cursor (tool->control, + GIMP_TOOL_CURSOR_GRADIENT); + gimp_tool_control_set_action_opacity (tool->control, + "context/context-opacity-set"); + gimp_tool_control_set_action_object_1 (tool->control, + "context/context-gradient-select-set"); + + gimp_draw_tool_set_default_status (GIMP_DRAW_TOOL (tool), + _("Click-Drag to draw a gradient")); +} + +static void +gimp_gradient_tool_dispose (GObject *object) +{ + GimpGradientTool *gradient_tool = GIMP_GRADIENT_TOOL (object); + + gimp_gradient_tool_set_gradient (gradient_tool, NULL); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static gboolean +gimp_gradient_tool_initialize (GimpTool *tool, + GimpDisplay *display, + GError **error) +{ + GimpImage *image = gimp_display_get_image (display); + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (tool); + + if (! GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error)) + { + return FALSE; + } + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("Cannot modify the pixels of layer groups.")); + return FALSE; + } + + if (gimp_item_is_content_locked (GIMP_ITEM (drawable))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("The active layer's pixels are locked.")); + return FALSE; + } + + if (! gimp_item_is_visible (GIMP_ITEM (drawable))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("The active layer is not visible.")); + return FALSE; + } + + if (! gimp_context_get_gradient (GIMP_CONTEXT (options))) + { + g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED, + _("No gradient available for use with this tool.")); + return FALSE; + } + + return TRUE; +} + +static void +gimp_gradient_tool_control (GimpTool *tool, + GimpToolAction action, + GimpDisplay *display) +{ + GimpGradientTool *gradient_tool = GIMP_GRADIENT_TOOL (tool); + + switch (action) + { + case GIMP_TOOL_ACTION_PAUSE: + case GIMP_TOOL_ACTION_RESUME: + break; + + case GIMP_TOOL_ACTION_HALT: + gimp_gradient_tool_halt (gradient_tool); + break; + + case GIMP_TOOL_ACTION_COMMIT: + gimp_gradient_tool_commit (gradient_tool); + break; + } + + GIMP_TOOL_CLASS (parent_class)->control (tool, action, display); +} + +static void +gimp_gradient_tool_button_press (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type, + GimpDisplay *display) +{ + GimpGradientTool *gradient_tool = GIMP_GRADIENT_TOOL (tool); + + if (tool->display && display != tool->display) + gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display); + + if (! gradient_tool->widget) + { + gimp_gradient_tool_start (gradient_tool, coords, display); + + gimp_tool_widget_hover (gradient_tool->widget, coords, state, TRUE); + } + + /* call start_edit() before widget_button_press(), because we need to record + * the undo state before widget_button_press() potentially changes it. note + * that if widget_button_press() return FALSE, nothing changes and no undo + * step is created. + */ + if (press_type == GIMP_BUTTON_PRESS_NORMAL) + gimp_gradient_tool_editor_start_edit (gradient_tool); + + if (gimp_tool_widget_button_press (gradient_tool->widget, coords, time, state, + press_type)) + { + gradient_tool->grab_widget = gradient_tool->widget; + } + + if (press_type == GIMP_BUTTON_PRESS_NORMAL) + gimp_tool_control_activate (tool->control); +} + +static void +gimp_gradient_tool_button_release (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type, + GimpDisplay *display) +{ + GimpGradientTool *gradient_tool = GIMP_GRADIENT_TOOL (tool); + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (tool); + + gimp_tool_pop_status (tool, display); + + gimp_tool_control_halt (tool->control); + + if (gradient_tool->grab_widget) + { + gimp_tool_widget_button_release (gradient_tool->grab_widget, + coords, time, state, release_type); + gradient_tool->grab_widget = NULL; + + if (options->instant) + { + if (release_type == GIMP_BUTTON_RELEASE_CANCEL) + gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display); + else + gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display); + } + } + + if (! options->instant) + { + gimp_gradient_tool_editor_end_edit (gradient_tool, + release_type == + GIMP_BUTTON_RELEASE_CANCEL); + } +} + +static void +gimp_gradient_tool_motion (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplay *display) +{ + GimpGradientTool *gradient_tool = GIMP_GRADIENT_TOOL (tool); + + if (gradient_tool->grab_widget) + { + gimp_tool_widget_motion (gradient_tool->grab_widget, coords, time, state); + } +} + +static gboolean +gimp_gradient_tool_key_press (GimpTool *tool, + GdkEventKey *kevent, + GimpDisplay *display) +{ + GimpGradientTool *gradient_tool = GIMP_GRADIENT_TOOL (tool); + GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool); + gboolean result; + + /* call start_edit() before widget_key_press(), because we need to record the + * undo state before widget_key_press() potentially changes it. note that if + * widget_key_press() return FALSE, nothing changes and no undo step is + * created. + */ + if (display == draw_tool->display) + gimp_gradient_tool_editor_start_edit (gradient_tool); + + result = GIMP_TOOL_CLASS (parent_class)->key_press (tool, kevent, display); + + if (display == draw_tool->display) + gimp_gradient_tool_editor_end_edit (gradient_tool, FALSE); + + return result; +} + +static void +gimp_gradient_tool_modifier_key (GimpTool *tool, + GdkModifierType key, + gboolean press, + GdkModifierType state, + GimpDisplay *display) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (tool); + + if (key == gimp_get_extend_selection_mask ()) + { + if (options->instant_toggle && + gtk_widget_get_sensitive (options->instant_toggle)) + { + g_object_set (options, + "instant", ! options->instant, + NULL); + } + } +} + +static void +gimp_gradient_tool_cursor_update (GimpTool *tool, + const GimpCoords *coords, + GdkModifierType state, + GimpDisplay *display) +{ + GimpImage *image = gimp_display_get_image (display); + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + + if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) || + gimp_item_is_content_locked (GIMP_ITEM (drawable)) || + ! gimp_item_is_visible (GIMP_ITEM (drawable))) + { + gimp_tool_set_cursor (tool, display, + gimp_tool_control_get_cursor (tool->control), + gimp_tool_control_get_tool_cursor (tool->control), + GIMP_CURSOR_MODIFIER_BAD); + return; + } + + GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display); +} + +static const gchar * +gimp_gradient_tool_can_undo (GimpTool *tool, + GimpDisplay *display) +{ + return gimp_gradient_tool_editor_can_undo (GIMP_GRADIENT_TOOL (tool)); +} + +static const gchar * +gimp_gradient_tool_can_redo (GimpTool *tool, + GimpDisplay *display) +{ + return gimp_gradient_tool_editor_can_redo (GIMP_GRADIENT_TOOL (tool)); +} + +static gboolean +gimp_gradient_tool_undo (GimpTool *tool, + GimpDisplay *display) +{ + return gimp_gradient_tool_editor_undo (GIMP_GRADIENT_TOOL (tool)); +} + +static gboolean +gimp_gradient_tool_redo (GimpTool *tool, + GimpDisplay *display) +{ + return gimp_gradient_tool_editor_redo (GIMP_GRADIENT_TOOL (tool)); +} + +static void +gimp_gradient_tool_options_notify (GimpTool *tool, + GimpToolOptions *options, + const GParamSpec *pspec) +{ + GimpContext *context = GIMP_CONTEXT (options); + GimpGradientTool *gradient_tool = GIMP_GRADIENT_TOOL (tool); + + if (! strcmp (pspec->name, "gradient")) + { + gimp_gradient_tool_set_gradient (gradient_tool, context->gradient); + + if (gradient_tool->filter) + gimp_drawable_filter_apply (gradient_tool->filter, NULL); + } + else if (gradient_tool->render_node && + gegl_node_find_property (gradient_tool->render_node, pspec->name)) + { + /* Sync any property changes on the config object that match the op */ + GValue value = G_VALUE_INIT; + + g_value_init (&value, pspec->value_type); + + g_object_get_property (G_OBJECT (options), pspec->name, &value); + gegl_node_set_property (gradient_tool->render_node, pspec->name, &value); + + g_value_unset (&value); + + if (! strcmp (pspec->name, "gradient-type")) + { + GimpRepeatMode gradient_repeat; + GimpRepeatMode node_repeat; + GimpGradientType gradient_type; + + gradient_repeat = GIMP_PAINT_OPTIONS (options)->gradient_options->gradient_repeat; + gradient_type = GIMP_GRADIENT_OPTIONS (options)->gradient_type; + gegl_node_get (gradient_tool->render_node, + "gradient-repeat", &node_repeat, + NULL); + + if (gradient_type >= GIMP_GRADIENT_SHAPEBURST_ANGULAR) + { + /* These gradient types are only meant to work with repeat + * value of "none" so these are the only ones where we + * don't keep the render node and the gradient options in + * sync. + * We could instead reset the "gradient-repeat" value on + * GimpGradientOptions, but I assume one would want to revert + * back to the last set value if changing back the + * gradient type. So instead we just make the option + * insensitive (both in GUI and in render). + */ + if (node_repeat != GIMP_REPEAT_NONE) + gegl_node_set (gradient_tool->render_node, + "gradient-repeat", GIMP_REPEAT_NONE, + NULL); + } + else if (node_repeat != gradient_repeat) + { + gegl_node_set (gradient_tool->render_node, + "gradient-repeat", gradient_repeat, + NULL); + } + + if (gimp_gradient_tool_is_shapeburst (gradient_tool)) + gimp_gradient_tool_precalc_shapeburst (gradient_tool); + + gimp_gradient_tool_update_graph (gradient_tool); + } + + gimp_drawable_filter_apply (gradient_tool->filter, NULL); + } + else if (gradient_tool->render_node && + gimp_gradient_tool_is_shapeburst (gradient_tool) && + g_strcmp0 (pspec->name, "distance-metric") == 0) + { + g_clear_object (&gradient_tool->dist_buffer); + gimp_gradient_tool_precalc_shapeburst (gradient_tool); + gimp_gradient_tool_update_graph (gradient_tool); + gimp_drawable_filter_apply (gradient_tool->filter, NULL); + } + else if (gradient_tool->filter && + ! strcmp (pspec->name, "opacity")) + { + gimp_drawable_filter_set_opacity (gradient_tool->filter, + gimp_context_get_opacity (context)); + } + else if (gradient_tool->filter && + ! strcmp (pspec->name, "paint-mode")) + { + gimp_drawable_filter_set_mode (gradient_tool->filter, + gimp_context_get_paint_mode (context), + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COMPOSITE_AUTO); + } + + gimp_gradient_tool_editor_options_notify (gradient_tool, options, pspec); +} + +static void +gimp_gradient_tool_start (GimpGradientTool *gradient_tool, + const GimpCoords *coords, + GimpDisplay *display) +{ + GimpTool *tool = GIMP_TOOL (gradient_tool); + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpImage *image = gimp_display_get_image (display); + GimpDrawable *drawable = gimp_image_get_active_drawable (image); + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpContext *context = GIMP_CONTEXT (options); + + if (options->instant_toggle) + gtk_widget_set_sensitive (options->instant_toggle, FALSE); + + tool->display = display; + tool->drawable = drawable; + + gradient_tool->start_x = coords->x; + gradient_tool->start_y = coords->y; + gradient_tool->end_x = coords->x; + gradient_tool->end_y = coords->y; + + gradient_tool->widget = gimp_tool_line_new (shell, + gradient_tool->start_x, + gradient_tool->start_y, + gradient_tool->end_x, + gradient_tool->end_y); + + g_object_set (gradient_tool->widget, + "status-title", _("Gradient: "), + NULL); + + gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), gradient_tool->widget); + + g_signal_connect (gradient_tool->widget, "changed", + G_CALLBACK (gimp_gradient_tool_line_changed), + gradient_tool); + g_signal_connect (gradient_tool->widget, "response", + G_CALLBACK (gimp_gradient_tool_line_response), + gradient_tool); + + g_signal_connect_swapped (context, "background-changed", + G_CALLBACK (gimp_gradient_tool_fg_bg_changed), + gradient_tool); + g_signal_connect_swapped (context, "foreground-changed", + G_CALLBACK (gimp_gradient_tool_fg_bg_changed), + gradient_tool); + + gimp_gradient_tool_create_filter (gradient_tool, drawable); + + /* Initially sync all of the properties */ + gimp_operation_config_sync_node (G_OBJECT (options), + gradient_tool->render_node); + + /* We don't allow repeat values for some shapes. */ + if (options->gradient_type >= GIMP_GRADIENT_SHAPEBURST_ANGULAR) + gegl_node_set (gradient_tool->render_node, + "gradient-repeat", GIMP_REPEAT_NONE, + NULL); + + /* Connect signal handlers for the gradient */ + gimp_gradient_tool_set_gradient (gradient_tool, context->gradient); + + if (gimp_gradient_tool_is_shapeburst (gradient_tool)) + gimp_gradient_tool_precalc_shapeburst (gradient_tool); + + gimp_draw_tool_start (GIMP_DRAW_TOOL (gradient_tool), display); + + gimp_gradient_tool_editor_start (gradient_tool); +} + +static void +gimp_gradient_tool_halt (GimpGradientTool *gradient_tool) +{ + GimpTool *tool = GIMP_TOOL (gradient_tool); + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpContext *context = GIMP_CONTEXT (options); + + gimp_gradient_tool_editor_halt (gradient_tool); + + if (gradient_tool->graph) + { + g_clear_object (&gradient_tool->graph); + gradient_tool->render_node = NULL; +#if 0 + gradient_tool->subtract_node = NULL; + gradient_tool->divide_node = NULL; +#endif + gradient_tool->dist_node = NULL; + } + + g_clear_object (&gradient_tool->dist_buffer); + + if (gradient_tool->filter) + { + gimp_tool_control_push_preserve (tool->control, TRUE); + + gimp_drawable_filter_abort (gradient_tool->filter); + g_object_unref (gradient_tool->filter); + gradient_tool->filter = NULL; + + gimp_tool_control_pop_preserve (tool->control); + + gimp_image_flush (gimp_display_get_image (tool->display)); + } + + gimp_gradient_tool_set_tentative_gradient (gradient_tool, NULL); + + g_signal_handlers_disconnect_by_func (context, + G_CALLBACK (gimp_gradient_tool_fg_bg_changed), + gradient_tool); + + if (tool->display) + gimp_tool_pop_status (tool, tool->display); + + if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (gradient_tool))) + gimp_draw_tool_stop (GIMP_DRAW_TOOL (gradient_tool)); + + gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), NULL); + g_clear_object (&gradient_tool->widget); + + tool->display = NULL; + tool->drawable = NULL; + + if (options->instant_toggle) + gtk_widget_set_sensitive (options->instant_toggle, TRUE); +} + +static void +gimp_gradient_tool_commit (GimpGradientTool *gradient_tool) +{ + GimpTool *tool = GIMP_TOOL (gradient_tool); + + if (gradient_tool->filter) + { + gimp_tool_control_push_preserve (tool->control, TRUE); + + gimp_drawable_filter_commit (gradient_tool->filter, + GIMP_PROGRESS (tool), FALSE); + g_clear_object (&gradient_tool->filter); + + gimp_tool_control_pop_preserve (tool->control); + + gimp_image_flush (gimp_display_get_image (tool->display)); + } +} + +static void +gimp_gradient_tool_line_changed (GimpToolWidget *widget, + GimpGradientTool *gradient_tool) +{ + gdouble start_x; + gdouble start_y; + gdouble end_x; + gdouble end_y; + gboolean update = FALSE; + + g_object_get (widget, + "x1", &start_x, + "y1", &start_y, + "x2", &end_x, + "y2", &end_y, + NULL); + + if (start_x != gradient_tool->start_x || + start_y != gradient_tool->start_y || + end_x != gradient_tool->end_x || + end_y != gradient_tool->end_y) + { + gradient_tool->start_x = start_x; + gradient_tool->start_y = start_y; + gradient_tool->end_x = end_x; + gradient_tool->end_y = end_y; + + update = TRUE; + } + + if (gimp_gradient_tool_editor_line_changed (gradient_tool)) + update = TRUE; + + if (update) + { + gimp_gradient_tool_update_graph (gradient_tool); + gimp_drawable_filter_apply (gradient_tool->filter, NULL); + } +} + +static void +gimp_gradient_tool_line_response (GimpToolWidget *widget, + gint response_id, + GimpGradientTool *gradient_tool) +{ + GimpTool *tool = GIMP_TOOL (gradient_tool); + + switch (response_id) + { + case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM: + gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, tool->display); + break; + + case GIMP_TOOL_WIDGET_RESPONSE_CANCEL: + gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display); + break; + } +} + +static void +gimp_gradient_tool_precalc_shapeburst (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpTool *tool = GIMP_TOOL (gradient_tool); + gint x, y, width, height; + + if (gradient_tool->dist_buffer || ! tool->drawable) + return; + + if (! gimp_item_mask_intersect (GIMP_ITEM (tool->drawable), + &x, &y, &width, &height)) + return; + + gradient_tool->dist_buffer = + gimp_drawable_gradient_shapeburst_distmap (tool->drawable, + options->distance_metric, + GEGL_RECTANGLE (x, y, width, height), + GIMP_PROGRESS (gradient_tool)); + + if (gradient_tool->dist_node) + gegl_node_set (gradient_tool->dist_node, + "buffer", gradient_tool->dist_buffer, + NULL); + + gimp_progress_end (GIMP_PROGRESS (gradient_tool)); +} + + +/* gegl graph stuff */ + +static void +gimp_gradient_tool_create_graph (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpContext *context = GIMP_CONTEXT (options); + GeglNode *output; + + /* render_node is not supposed to be recreated */ + g_return_if_fail (gradient_tool->graph == NULL); + + gradient_tool->graph = gegl_node_new (); + + gradient_tool->dist_node = + gegl_node_new_child (gradient_tool->graph, + "operation", "gegl:buffer-source", + "buffer", gradient_tool->dist_buffer, + NULL); + +#if 0 + gradient_tool->subtract_node = + gegl_node_new_child (gradient_tool->graph, + "operation", "gegl:subtract", + NULL); + + gradient_tool->divide_node = + gegl_node_new_child (gradient_tool->graph, + "operation", "gegl:divide", + NULL); +#endif + + gradient_tool->render_node = + gegl_node_new_child (gradient_tool->graph, + "operation", "gimp:gradient", + "context", context, + NULL); + + output = gegl_node_get_output_proxy (gradient_tool->graph, "output"); + + gegl_node_link_many (gradient_tool->dist_node, +#if 0 + gradient_tool->subtract_node, + gradient_tool->divide_node, +#endif + gradient_tool->render_node, + output, + NULL); + + gimp_gradient_tool_update_graph (gradient_tool); +} + +static void +gimp_gradient_tool_update_graph (GimpGradientTool *gradient_tool) +{ + GimpTool *tool = GIMP_TOOL (gradient_tool); + gint off_x, off_y; + + gimp_item_get_offset (GIMP_ITEM (tool->drawable), &off_x, &off_y); + +#if 0 + if (gimp_gradient_tool_is_shapeburst (gradient_tool)) + { + gfloat start, end; + + gegl_buffer_get (gradient_tool->dist_buffer, + GEGL_RECTANGLE (gradient_tool->start_x - off_x, + gradient_tool->start_y - off_y, + 1, 1), + 1.0, babl_format("Y float"), &start, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + gegl_buffer_get (gradient_tool->dist_buffer, + GEGL_RECTANGLE (gradient_tool->end_x - off_x, + gradient_tool->end_y - off_y, + 1, 1), + 1.0, babl_format("Y float"), &end, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + if (start != end) + { + gegl_node_set (gradient_tool->subtract_node, + "value", (gdouble) start, + NULL); + gegl_node_set (gradient_tool->divide_node, + "value", (gdouble) (end - start), + NULL); + } + } + else +#endif + { + gegl_node_set (gradient_tool->render_node, + "start_x", gradient_tool->start_x - off_x, + "start_y", gradient_tool->start_y - off_y, + "end_x", gradient_tool->end_x - off_x, + "end_y", gradient_tool->end_y - off_y, + NULL); + } +} + +static void +gimp_gradient_tool_fg_bg_changed (GimpGradientTool *gradient_tool) +{ + if (! gradient_tool->filter || ! gradient_tool->gradient) + return; + + if (gimp_gradient_has_fg_bg_segments (gradient_tool->gradient)) + { + /* Set a property on the node. Otherwise it will cache and refuse to update */ + gegl_node_set (gradient_tool->render_node, + "gradient", gradient_tool->gradient, + NULL); + + /* Update the filter */ + gimp_drawable_filter_apply (gradient_tool->filter, NULL); + + gimp_gradient_tool_editor_fg_bg_changed (gradient_tool); + } +} + +static void +gimp_gradient_tool_gradient_dirty (GimpGradientTool *gradient_tool) +{ + if (! gradient_tool->filter) + return; + + if (! gradient_tool->tentative_gradient) + { + /* Set a property on the node. Otherwise it will cache and refuse to update */ + gegl_node_set (gradient_tool->render_node, + "gradient", gradient_tool->gradient, + NULL); + + /* Update the filter */ + gimp_drawable_filter_apply (gradient_tool->filter, NULL); + } + + gimp_gradient_tool_editor_gradient_dirty (gradient_tool); +} + +static void +gimp_gradient_tool_set_gradient (GimpGradientTool *gradient_tool, + GimpGradient *gradient) +{ + if (gradient_tool->gradient) + { + g_signal_handlers_disconnect_by_func (gradient_tool->gradient, + G_CALLBACK (gimp_gradient_tool_gradient_dirty), + gradient_tool); + + g_object_unref (gradient_tool->gradient); + } + + gradient_tool->gradient = gradient; + + if (gradient_tool->gradient) + { + g_object_ref (gradient); + + g_signal_connect_swapped (gradient_tool->gradient, "dirty", + G_CALLBACK (gimp_gradient_tool_gradient_dirty), + gradient_tool); + + if (gradient_tool->render_node) + gegl_node_set (gradient_tool->render_node, + "gradient", gradient_tool->gradient, + NULL); + } + + gimp_gradient_tool_editor_gradient_changed (gradient_tool); +} + +static gboolean +gimp_gradient_tool_is_shapeburst (GimpGradientTool *gradient_tool) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + + return options->gradient_type >= GIMP_GRADIENT_SHAPEBURST_ANGULAR && + options->gradient_type <= GIMP_GRADIENT_SHAPEBURST_DIMPLED; +} + + +/* image map stuff */ + +static void +gimp_gradient_tool_create_filter (GimpGradientTool *gradient_tool, + GimpDrawable *drawable) +{ + GimpGradientOptions *options = GIMP_GRADIENT_TOOL_GET_OPTIONS (gradient_tool); + GimpContext *context = GIMP_CONTEXT (options); + + if (! gradient_tool->graph) + gimp_gradient_tool_create_graph (gradient_tool); + + gradient_tool->filter = gimp_drawable_filter_new (drawable, + C_("undo-type", "Gradient"), + gradient_tool->graph, + GIMP_ICON_TOOL_GRADIENT); + + gimp_drawable_filter_set_region (gradient_tool->filter, + GIMP_FILTER_REGION_DRAWABLE); + gimp_drawable_filter_set_opacity (gradient_tool->filter, + gimp_context_get_opacity (context)); + gimp_drawable_filter_set_mode (gradient_tool->filter, + gimp_context_get_paint_mode (context), + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COLOR_SPACE_AUTO, + GIMP_LAYER_COMPOSITE_AUTO); + + g_signal_connect (gradient_tool->filter, "flush", + G_CALLBACK (gimp_gradient_tool_filter_flush), + gradient_tool); +} + +static void +gimp_gradient_tool_filter_flush (GimpDrawableFilter *filter, + GimpTool *tool) +{ + GimpImage *image = gimp_display_get_image (tool->display); + + gimp_projection_flush (gimp_image_get_projection (image)); +} + + +/* protected functions */ + + +void +gimp_gradient_tool_set_tentative_gradient (GimpGradientTool *gradient_tool, + GimpGradient *gradient) +{ + g_return_if_fail (GIMP_IS_GRADIENT_TOOL (gradient_tool)); + g_return_if_fail (gradient == NULL || GIMP_IS_GRADIENT (gradient)); + + if (gradient != gradient_tool->tentative_gradient) + { + g_clear_object (&gradient_tool->tentative_gradient); + + gradient_tool->tentative_gradient = gradient; + + if (gradient) + g_object_ref (gradient); + + if (gradient_tool->render_node) + { + gegl_node_set (gradient_tool->render_node, + "gradient", gradient ? gradient : gradient_tool->gradient, + NULL); + + gimp_drawable_filter_apply (gradient_tool->filter, NULL); + } + } +} diff --git a/app/tools/gimpblendtool.h b/app/tools/gimpgradienttool.h similarity index 62% rename from app/tools/gimpblendtool.h rename to app/tools/gimpgradienttool.h index 08862bc65b..23d68558eb 100644 --- a/app/tools/gimpblendtool.h +++ b/app/tools/gimpgradienttool.h @@ -15,27 +15,27 @@ * along with this program. If not, see . */ -#ifndef __GIMP_BLEND_TOOL_H__ -#define __GIMP_BLEND_TOOL_H__ +#ifndef __GIMP_GRADIENT_TOOL_H__ +#define __GIMP_GRADIENT_TOOL_H__ #include "gimpdrawtool.h" -#define GIMP_TYPE_BLEND_TOOL (gimp_blend_tool_get_type ()) -#define GIMP_BLEND_TOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_BLEND_TOOL, GimpBlendTool)) -#define GIMP_BLEND_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_BLEND_TOOL, GimpBlendToolClass)) -#define GIMP_IS_BLEND_TOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_BLEND_TOOL)) -#define GIMP_IS_BLEND_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_BLEND_TOOL)) -#define GIMP_BLEND_TOOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_BLEND_TOOL, GimpBlendToolClass)) +#define GIMP_TYPE_GRADIENT_TOOL (gimp_gradient_tool_get_type ()) +#define GIMP_GRADIENT_TOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_GRADIENT_TOOL, GimpGradientTool)) +#define GIMP_GRADIENT_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_GRADIENT_TOOL, GimpGradientToolClass)) +#define GIMP_IS_GRADIENT_TOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_GRADIENT_TOOL)) +#define GIMP_IS_GRADIENT_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_GRADIENT_TOOL)) +#define GIMP_GRADIENT_TOOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_GRADIENT_TOOL, GimpGradientToolClass)) -#define GIMP_BLEND_TOOL_GET_OPTIONS(t) (GIMP_BLEND_OPTIONS (gimp_tool_get_options (GIMP_TOOL (t)))) +#define GIMP_GRADIENT_TOOL_GET_OPTIONS(t) (GIMP_GRADIENT_OPTIONS (gimp_tool_get_options (GIMP_TOOL (t)))) -typedef struct _GimpBlendTool GimpBlendTool; -typedef struct _GimpBlendToolClass GimpBlendToolClass; +typedef struct _GimpGradientTool GimpGradientTool; +typedef struct _GimpGradientToolClass GimpGradientToolClass; -struct _GimpBlendTool +struct _GimpGradientTool { GimpDrawTool parent_instance; @@ -90,22 +90,22 @@ struct _GimpBlendTool GtkWidget *midpoint_center_button; }; -struct _GimpBlendToolClass +struct _GimpGradientToolClass { GimpDrawToolClass parent_class; }; -void gimp_blend_tool_register (GimpToolRegisterCallback callback, - gpointer data); +void gimp_gradient_tool_register (GimpToolRegisterCallback callback, + gpointer data); -GType gimp_blend_tool_get_type (void) G_GNUC_CONST; +GType gimp_gradient_tool_get_type (void) G_GNUC_CONST; /* protected functions */ -void gimp_blend_tool_set_tentative_gradient (GimpBlendTool *blend_tool, - GimpGradient *gradient); +void gimp_gradient_tool_set_tentative_gradient (GimpGradientTool *gradient_tool, + GimpGradient *gradient); -#endif /* __GIMP_BLEND_TOOL_H__ */ +#endif /* __GIMP_GRADIENT_TOOL_H__ */ diff --git a/app/tools/tool_manager.c b/app/tools/tool_manager.c index 53f75beea2..3bd698bf63 100644 --- a/app/tools/tool_manager.c +++ b/app/tools/tool_manager.c @@ -894,7 +894,7 @@ tool_manager_cast_spell (GimpToolInfo *tool_info) { { .sequence = "gimp-warp-tool\0" "gimp-iscissors-tool\0" - "gimp-blend-tool\0" + "gimp-gradient-tool\0" "gimp-vector-tool\0" "gimp-ellipse-select-tool\0" "gimp-rect-select-tool\0", diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h index b71df70e85..254e9394f0 100644 --- a/app/widgets/gimphelp-ids.h +++ b/app/widgets/gimphelp-ids.h @@ -287,7 +287,6 @@ #define GIMP_HELP_TOOL_AIRBRUSH "gimp-tool-airbrush" #define GIMP_HELP_TOOL_ALIGN "gimp-tool-align" -#define GIMP_HELP_TOOL_BLEND "gimp-tool-blend" #define GIMP_HELP_TOOL_BRIGHTNESS_CONTRAST "gimp-tool-brightness-contrast" #define GIMP_HELP_TOOL_BUCKET_FILL "gimp-tool-bucket-fill" #define GIMP_HELP_TOOL_BY_COLOR_SELECT "gimp-tool-by-color-select" @@ -308,6 +307,7 @@ #define GIMP_HELP_TOOL_FOREGROUND_SELECT "gimp-tool-foreground-select" #define GIMP_HELP_TOOL_FUZZY_SELECT "gimp-tool-fuzzy-select" #define GIMP_HELP_TOOL_GEGL "gimp-tool-gegl" +#define GIMP_HELP_TOOL_GRADIENT "gimp-tool-gradient" #define GIMP_HELP_TOOL_HANDLE_TRANSFORM "gimp-tool-handle-transform" #define GIMP_HELP_TOOL_HEAL "gimp-tool-heal" #define GIMP_HELP_TOOL_HUE_SATURATION "gimp-tool-hue-saturation" diff --git a/data/tool-presets/FX/FX-Radial-Softlight.gtp b/data/tool-presets/FX/FX-Radial-Softlight.gtp index 3d642bf7cc..8c277af174 100644 --- a/data/tool-presets/FX/FX-Radial-Softlight.gtp +++ b/data/tool-presets/FX/FX-Radial-Softlight.gtp @@ -1,9 +1,9 @@ # GIMP tool preset file -(icon-name "gimp-tool-blend") +(icon-name "gimp-tool-gradient") (name "FX Radial Softlight") -(tool-options "GimpBlendOptions" - (tool "gimp-blend-tool") +(tool-options "GimpGradientOptions" + (tool "gimp-gradient-tool") (foreground (color-rgb 0.102075 0.183901 0.594317)) (paint-mode softlight-mode) (gradient "FG to Transparent") diff --git a/data/tool-presets/FX/Vignette.gtp b/data/tool-presets/FX/Vignette.gtp index 34948364dd..476312ac21 100644 --- a/data/tool-presets/FX/Vignette.gtp +++ b/data/tool-presets/FX/Vignette.gtp @@ -1,9 +1,9 @@ # GIMP tool preset file -(icon-name "gimp-tool-blend") +(icon-name "gimp-tool-gradient") (name "Vignette") -(tool-options "GimpBlendOptions" - (tool "gimp-blend-tool") +(tool-options "GimpGradientOptions" + (tool "gimp-gradient-tool") (opacity 0.750000) (paint-mode multiply-mode) (gradient "FG to Transparent") diff --git a/menus/image-menu.xml.in b/menus/image-menu.xml.in index 47553b66ef..13e6560005 100644 --- a/menus/image-menu.xml.in +++ b/menus/image-menu.xml.in @@ -637,7 +637,7 @@ - + diff --git a/po/POTFILES.in b/po/POTFILES.in index 86780799ff..ae06cdbadd 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -405,9 +405,6 @@ app/tools/gimp-tools.c app/tools/gimpairbrushtool.c app/tools/gimpalignoptions.c app/tools/gimpaligntool.c -app/tools/gimpblendoptions.c -app/tools/gimpblendtool.c -app/tools/gimpblendtool-editor.c app/tools/gimpbrightnesscontrasttool.c app/tools/gimpbucketfilloptions.c app/tools/gimpbucketfilltool.c @@ -439,6 +436,9 @@ app/tools/gimpfreeselecttool.c app/tools/gimpfuzzyselecttool.c app/tools/gimpgegltool.c app/tools/gimpgenerictransformtool.c +app/tools/gimpgradientptions.c +app/tools/gimpgradienttool.c +app/tools/gimpgradienttool-editor.c app/tools/gimpguidetool.c app/tools/gimphandletransformoptions.c app/tools/gimphandletransformtool.c