diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
commit | 5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch) | |
tree | cbffb45144febf451e54061db2b21395faf94bfe /app/tools/gimprectangleselecttool.c | |
parent | Initial commit. (diff) | |
download | gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.tar.xz gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.zip |
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/tools/gimprectangleselecttool.c')
-rw-r--r-- | app/tools/gimprectangleselecttool.c | 918 |
1 files changed, 918 insertions, 0 deletions
diff --git a/app/tools/gimprectangleselecttool.c b/app/tools/gimprectangleselecttool.c new file mode 100644 index 0000000..65f596d --- /dev/null +++ b/app/tools/gimprectangleselecttool.c @@ -0,0 +1,918 @@ +/* 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 <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <gegl.h> +#include <gtk/gtk.h> + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "tools-types.h" + +#include "core/gimpchannel-select.h" +#include "core/gimpchannel.h" +#include "core/gimpimage.h" +#include "core/gimplayer-floating-selection.h" +#include "core/gimppickable.h" + +#include "widgets/gimphelp-ids.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimptoolrectangle.h" + +#include "gimpeditselectiontool.h" +#include "gimprectangleoptions.h" +#include "gimprectangleselecttool.h" +#include "gimprectangleselectoptions.h" +#include "gimptoolcontrol.h" + +#include "gimp-intl.h" + + +struct _GimpRectangleSelectToolPrivate +{ + GimpChannelOps operation; /* remember for use when modifying */ + gboolean use_saved_op; /* use operation or get from options */ + + gdouble press_x; + gdouble press_y; + + GimpToolWidget *widget; + GimpToolWidget *grab_widget; + GList *bindings; +}; + + +static void gimp_rectangle_select_tool_control (GimpTool *tool, + GimpToolAction action, + GimpDisplay *display); +static void gimp_rectangle_select_tool_button_press (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type, + GimpDisplay *display); +static void gimp_rectangle_select_tool_button_release (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type, + GimpDisplay *display); +static void gimp_rectangle_select_tool_motion (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplay *display); +static gboolean gimp_rectangle_select_tool_key_press (GimpTool *tool, + GdkEventKey *kevent, + GimpDisplay *display); +static void gimp_rectangle_select_tool_oper_update (GimpTool *tool, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity, + GimpDisplay *display); +static void gimp_rectangle_select_tool_cursor_update (GimpTool *tool, + const GimpCoords *coords, + GdkModifierType state, + GimpDisplay *display); +static void gimp_rectangle_select_tool_options_notify (GimpTool *tool, + GimpToolOptions *options, + const GParamSpec *pspec); + +static gboolean gimp_rectangle_select_tool_select (GimpRectangleSelectTool *rect_tool, + gint x, + gint y, + gint w, + gint h); +static void gimp_rectangle_select_tool_real_select (GimpRectangleSelectTool *rect_tool, + GimpChannelOps operation, + gint x, + gint y, + gint w, + gint h); + +static void gimp_rectangle_select_tool_rectangle_response + (GimpToolWidget *widget, + gint response_id, + GimpRectangleSelectTool *rect_tool); +static void gimp_rectangle_select_tool_rectangle_change_complete + (GimpToolWidget *widget, + GimpRectangleSelectTool *rect_tool); + +static void gimp_rectangle_select_tool_start (GimpRectangleSelectTool *rect_tool, + GimpDisplay *display); +static void gimp_rectangle_select_tool_commit (GimpRectangleSelectTool *rect_tool); +static void gimp_rectangle_select_tool_halt (GimpRectangleSelectTool *rect_tool); + +static GimpChannelOps + gimp_rectangle_select_tool_get_operation (GimpRectangleSelectTool *rect_tool); +static void gimp_rectangle_select_tool_update_option_defaults + (GimpRectangleSelectTool *rect_tool, + gboolean ignore_pending); +static void gimp_rectangle_select_tool_update (GimpRectangleSelectTool *rect_tool); +static void gimp_rectangle_select_tool_auto_shrink (GimpRectangleSelectTool *rect_tool); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpRectangleSelectTool, gimp_rectangle_select_tool, + GIMP_TYPE_SELECTION_TOOL) + +#define parent_class gimp_rectangle_select_tool_parent_class + + +void +gimp_rectangle_select_tool_register (GimpToolRegisterCallback callback, + gpointer data) +{ + (* callback) (GIMP_TYPE_RECTANGLE_SELECT_TOOL, + GIMP_TYPE_RECTANGLE_SELECT_OPTIONS, + gimp_rectangle_select_options_gui, + 0, + "gimp-rect-select-tool", + _("Rectangle Select"), + _("Rectangle Select Tool: Select a rectangular region"), + N_("_Rectangle Select"), "R", + NULL, GIMP_HELP_TOOL_RECT_SELECT, + GIMP_ICON_TOOL_RECT_SELECT, + data); +} + +static void +gimp_rectangle_select_tool_class_init (GimpRectangleSelectToolClass *klass) +{ + GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass); + + tool_class->control = gimp_rectangle_select_tool_control; + tool_class->button_press = gimp_rectangle_select_tool_button_press; + tool_class->button_release = gimp_rectangle_select_tool_button_release; + tool_class->motion = gimp_rectangle_select_tool_motion; + tool_class->key_press = gimp_rectangle_select_tool_key_press; + tool_class->oper_update = gimp_rectangle_select_tool_oper_update; + tool_class->cursor_update = gimp_rectangle_select_tool_cursor_update; + tool_class->options_notify = gimp_rectangle_select_tool_options_notify; + + klass->select = gimp_rectangle_select_tool_real_select; +} + +static void +gimp_rectangle_select_tool_init (GimpRectangleSelectTool *rect_tool) +{ + GimpTool *tool = GIMP_TOOL (rect_tool); + + rect_tool->private = + gimp_rectangle_select_tool_get_instance_private (rect_tool); + + gimp_tool_control_set_wants_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_PIXEL_BORDER); + gimp_tool_control_set_tool_cursor (tool->control, + GIMP_TOOL_CURSOR_RECT_SELECT); + gimp_tool_control_set_preserve (tool->control, FALSE); + gimp_tool_control_set_dirty_mask (tool->control, + GIMP_DIRTY_IMAGE_SIZE | + GIMP_DIRTY_SELECTION); + gimp_tool_control_set_dirty_action (tool->control, + GIMP_TOOL_ACTION_COMMIT); +} + +static void +gimp_rectangle_select_tool_control (GimpTool *tool, + GimpToolAction action, + GimpDisplay *display) +{ + GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool); + + switch (action) + { + case GIMP_TOOL_ACTION_PAUSE: + case GIMP_TOOL_ACTION_RESUME: + break; + + case GIMP_TOOL_ACTION_HALT: + gimp_rectangle_select_tool_halt (rect_tool); + break; + + case GIMP_TOOL_ACTION_COMMIT: + gimp_rectangle_select_tool_commit (rect_tool); + break; + } + + GIMP_TOOL_CLASS (parent_class)->control (tool, action, display); +} + +static void +gimp_rectangle_select_tool_button_press (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type, + GimpDisplay *display) +{ + GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool); + GimpRectangleSelectToolPrivate *private = rect_tool->private; + GimpRectangleFunction function; + + if (tool->display && display != tool->display) + gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, tool->display); + + if (gimp_selection_tool_start_edit (GIMP_SELECTION_TOOL (tool), + display, coords)) + { + /* In some cases we want to finish the rectangle select tool + * and hand over responsibility to the selection tool + */ + + gboolean zero_rect = FALSE; + + if (private->widget) + { + gdouble x1, y1, x2, y2; + + gimp_tool_rectangle_get_public_rect (GIMP_TOOL_RECTANGLE (private->widget), + &x1, &y1, &x2, &y2); + if (x1 == x2 && y1 == y2) + zero_rect = TRUE; + } + + /* Don't commit a zero-size rectangle, it would look like a + * click to commit() and that could anchor the floating + * selection or do other evil things. Instead, simply cancel a + * zero-size rectangle. See bug #796073. + */ + if (zero_rect) + gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display); + else + gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, display); + + gimp_rectangle_select_tool_update_option_defaults (rect_tool, TRUE); + return; + } + + if (! tool->display) + { + gimp_rectangle_select_tool_start (rect_tool, display); + + gimp_tool_widget_hover (private->widget, coords, state, TRUE); + + /* HACK: force CREATING on a newly created rectangle; otherwise, + * the above binding of properties would cause the rectangle to + * start with the size from tool options. + */ + gimp_tool_rectangle_set_function (GIMP_TOOL_RECTANGLE (private->widget), + GIMP_TOOL_RECTANGLE_CREATING); + } + + /* if the shift or ctrl keys are down, we don't want to adjust, we + * want to create a new rectangle, regardless of pointer loc + */ + if (state & (gimp_get_extend_selection_mask () | + gimp_get_modify_selection_mask ())) + { + gimp_tool_rectangle_set_function (GIMP_TOOL_RECTANGLE (private->widget), + GIMP_TOOL_RECTANGLE_CREATING); + } + + if (gimp_tool_widget_button_press (private->widget, coords, time, state, + press_type)) + { + private->grab_widget = private->widget; + } + + private->press_x = coords->x; + private->press_y = coords->y; + + /* if we have an existing rectangle in the current display, then + * we have already "executed", and need to undo at this point, + * unless the user has done something in the meantime + */ + function = + gimp_tool_rectangle_get_function (GIMP_TOOL_RECTANGLE (private->widget)); + + if (function == GIMP_TOOL_RECTANGLE_CREATING) + private->use_saved_op = FALSE; + + gimp_selection_tool_start_change ( + GIMP_SELECTION_TOOL (tool), + function == GIMP_TOOL_RECTANGLE_CREATING, + gimp_rectangle_select_tool_get_operation (rect_tool)); + + gimp_tool_control_activate (tool->control); +} + +static void +gimp_rectangle_select_tool_button_release (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type, + GimpDisplay *display) +{ + GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool); + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + + gimp_tool_control_halt (tool->control); + + gimp_selection_tool_end_change (GIMP_SELECTION_TOOL (tool), + /* if the user has not moved the mouse, + * cancel the change + */ + release_type == GIMP_BUTTON_RELEASE_CLICK || + release_type == GIMP_BUTTON_RELEASE_CANCEL); + + gimp_tool_pop_status (tool, display); + + if (priv->grab_widget) + { + gimp_tool_widget_button_release (priv->grab_widget, + coords, time, state, release_type); + priv->grab_widget = NULL; + } +} + +static void +gimp_rectangle_select_tool_motion (GimpTool *tool, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpDisplay *display) +{ + GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool); + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + + if (priv->grab_widget) + { + gimp_tool_widget_motion (priv->grab_widget, coords, time, state); + } +} + +static gboolean +gimp_rectangle_select_tool_key_press (GimpTool *tool, + GdkEventKey *kevent, + GimpDisplay *display) +{ + GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool); + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + + if (priv->widget && display == tool->display) + { + if (gimp_tool_widget_key_press (priv->widget, kevent)) + return TRUE; + } + + return gimp_edit_selection_tool_key_press (tool, kevent, display); +} + +static void +gimp_rectangle_select_tool_oper_update (GimpTool *tool, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity, + GimpDisplay *display) +{ + GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool); + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + + if (priv->widget && display == tool->display) + { + gimp_tool_widget_hover (priv->widget, coords, state, proximity); + } + + GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity, + display); +} + +static void +gimp_rectangle_select_tool_cursor_update (GimpTool *tool, + const GimpCoords *coords, + GdkModifierType state, + GimpDisplay *display) +{ + GimpRectangleSelectTool *rect_tool = GIMP_RECTANGLE_SELECT_TOOL (tool); + GimpRectangleSelectToolPrivate *private = rect_tool->private; + GimpCursorType cursor = GIMP_CURSOR_CROSSHAIR_SMALL; + GimpCursorModifier modifier = GIMP_CURSOR_MODIFIER_NONE; + + if (private->widget && display == tool->display) + { + gimp_tool_widget_get_cursor (private->widget, coords, state, + &cursor, NULL, &modifier); + } + + gimp_tool_control_set_cursor (tool->control, cursor); + gimp_tool_control_set_cursor_modifier (tool->control, modifier); + + /* override the previous if shift or ctrl are down */ + if (state & (gimp_get_extend_selection_mask () | + gimp_get_modify_selection_mask ())) + { + gimp_tool_control_set_cursor (tool->control, + GIMP_CURSOR_CROSSHAIR_SMALL); + } + + GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display); +} + +static void +gimp_rectangle_select_tool_options_notify (GimpTool *tool, + GimpToolOptions *options, + const GParamSpec *pspec) +{ + if (! strcmp (pspec->name, "antialias") || + ! strcmp (pspec->name, "feather") || + ! strcmp (pspec->name, "feather-radius") || + ! strcmp (pspec->name, "round-corners") || + ! strcmp (pspec->name, "corner-radius")) + { + gimp_rectangle_select_tool_update (GIMP_RECTANGLE_SELECT_TOOL (tool)); + } + + GIMP_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec); +} + +static gboolean +gimp_rectangle_select_tool_select (GimpRectangleSelectTool *rect_tool, + gint x, + gint y, + gint w, + gint h) +{ + GimpTool *tool = GIMP_TOOL (rect_tool); + GimpImage *image = gimp_display_get_image (tool->display); + gboolean rectangle_exists; + GimpChannelOps operation; + + gimp_tool_pop_status (tool, tool->display); + + rectangle_exists = (x <= gimp_image_get_width (image) && + y <= gimp_image_get_height (image) && + x + w >= 0 && + y + h >= 0 && + w > 0 && + h > 0); + + operation = gimp_rectangle_select_tool_get_operation (rect_tool); + + /* if rectangle exists, turn it into a selection */ + if (rectangle_exists) + { + gimp_selection_tool_start_change (GIMP_SELECTION_TOOL (rect_tool), + FALSE, + operation); + + GIMP_RECTANGLE_SELECT_TOOL_GET_CLASS (rect_tool)->select (rect_tool, + operation, + x, y, w, h); + + gimp_selection_tool_end_change (GIMP_SELECTION_TOOL (rect_tool), + FALSE); + } + + return rectangle_exists; +} + +static void +gimp_rectangle_select_tool_real_select (GimpRectangleSelectTool *rect_tool, + GimpChannelOps operation, + gint x, + gint y, + gint w, + gint h) +{ + GimpTool *tool = GIMP_TOOL (rect_tool); + GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool); + GimpRectangleSelectOptions *rect_options; + GimpChannel *channel; + + rect_options = GIMP_RECTANGLE_SELECT_TOOL_GET_OPTIONS (tool); + + channel = gimp_image_get_mask (gimp_display_get_image (tool->display)); + + if (rect_options->round_corners) + { + /* To prevent elliptification of the rectangle, + * we must cap the corner radius. + */ + gdouble max = MIN (w / 2.0, h / 2.0); + gdouble radius = MIN (rect_options->corner_radius, max); + + gimp_channel_select_round_rect (channel, + x, y, w, h, + radius, radius, + operation, + options->antialias, + options->feather, + options->feather_radius, + options->feather_radius, + TRUE); + } + else + { + gimp_channel_select_rectangle (channel, + x, y, w, h, + operation, + options->feather, + options->feather_radius, + options->feather_radius, + TRUE); + } +} + +static void +gimp_rectangle_select_tool_rectangle_response (GimpToolWidget *widget, + gint response_id, + GimpRectangleSelectTool *rect_tool) +{ + GimpTool *tool = GIMP_TOOL (rect_tool); + + switch (response_id) + { + case GIMP_TOOL_WIDGET_RESPONSE_CONFIRM: + { + gdouble x1, y1, x2, y2; + + gimp_tool_rectangle_get_public_rect (GIMP_TOOL_RECTANGLE (widget), + &x1, &y1, &x2, &y2); + if (x1 == x2 && y1 == y2) + { + /* if there are no extents, we got here because of a + * click, call commit() directly because we might want to + * reconfigure the rectangle and continue, instead of + * HALTing it like calling COMMIT would do + */ + gimp_rectangle_select_tool_commit (rect_tool); + + gimp_tool_rectangle_get_public_rect (GIMP_TOOL_RECTANGLE (widget), + &x1, &y1, &x2, &y2); + if (x1 == x2 && y1 == y2) + { + /* if there still is no rectangle after the + * tool_commit(), the click was outside the selection + * and we HALT to get rid of a zero-size tool widget. + */ + gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display); + } + } + else + { + 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_rectangle_select_tool_rectangle_change_complete (GimpToolWidget *widget, + GimpRectangleSelectTool *rect_tool) +{ + gimp_rectangle_select_tool_update (rect_tool); +} + +static void +gimp_rectangle_select_tool_start (GimpRectangleSelectTool *rect_tool, + GimpDisplay *display) +{ + static const gchar *properties[] = + { + "highlight", + "highlight-opacity", + "guide", + "round-corners", + "corner-radius", + "x", + "y", + "width", + "height", + "fixed-rule-active", + "fixed-rule", + "desired-fixed-width", + "desired-fixed-height", + "desired-fixed-size-width", + "desired-fixed-size-height", + "aspect-numerator", + "aspect-denominator", + "fixed-center" + }; + + GimpTool *tool = GIMP_TOOL (rect_tool); + GimpRectangleSelectToolPrivate *private = rect_tool->private; + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpRectangleSelectOptions *options; + GimpToolWidget *widget; + gboolean draw_ellipse; + gint i; + + options = GIMP_RECTANGLE_SELECT_TOOL_GET_OPTIONS (rect_tool); + + tool->display = display; + + private->widget = widget = gimp_tool_rectangle_new (shell); + + draw_ellipse = GIMP_RECTANGLE_SELECT_TOOL_GET_CLASS (rect_tool)->draw_ellipse; + + g_object_set (widget, + "draw-ellipse", draw_ellipse, + "status-title", draw_ellipse ? _("Ellipse: ") : _("Rectangle: "), + NULL); + + gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), widget); + + for (i = 0; i < G_N_ELEMENTS (properties); i++) + { + GBinding *binding = + g_object_bind_property (G_OBJECT (options), properties[i], + G_OBJECT (widget), properties[i], + G_BINDING_SYNC_CREATE | + G_BINDING_BIDIRECTIONAL); + + private->bindings = g_list_prepend (private->bindings, binding); + } + + gimp_rectangle_options_connect (GIMP_RECTANGLE_OPTIONS (options), + gimp_display_get_image (shell->display), + G_CALLBACK (gimp_rectangle_select_tool_auto_shrink), + rect_tool); + + g_signal_connect (widget, "response", + G_CALLBACK (gimp_rectangle_select_tool_rectangle_response), + rect_tool); + g_signal_connect (widget, "change-complete", + G_CALLBACK (gimp_rectangle_select_tool_rectangle_change_complete), + rect_tool); + + gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display); +} + +/* This function is called if the user clicks and releases the left + * button without moving it. There are the things we might want + * to do here: + * 1) If there is an existing rectangle and we are inside it, we + * convert it into a selection. + * 2) If there is an existing rectangle and we are outside it, we + * clear it. + * 3) If there is no rectangle and there is a floating selection, + * we anchor it. + * 4) If there is no rectangle and we are inside the selection, we + * create a rectangle from the selection bounds. + * 5) If there is no rectangle and we are outside the selection, + * we clear the selection. + */ +static void +gimp_rectangle_select_tool_commit (GimpRectangleSelectTool *rect_tool) +{ + GimpTool *tool = GIMP_TOOL (rect_tool); + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + + if (priv->widget) + { + gdouble x1, y1, x2, y2; + gint w, h; + + gimp_tool_rectangle_get_public_rect (GIMP_TOOL_RECTANGLE (priv->widget), + &x1, &y1, &x2, &y2); + w = x2 - x1; + h = y2 - y1; + + if (w == 0 && h == 0) + { + GimpImage *image = gimp_display_get_image (tool->display); + GimpChannel *selection = gimp_image_get_mask (image); + gint press_x; + gint press_y; + + if (gimp_image_get_floating_selection (image)) + { + floating_sel_anchor (gimp_image_get_floating_selection (image)); + gimp_image_flush (image); + + return; + } + + press_x = ROUND (priv->press_x); + press_y = ROUND (priv->press_y); + + /* if the click was inside the marching ants */ + if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (selection), + press_x, press_y) > 0.5) + { + gint x, y, w, h; + + if (gimp_item_bounds (GIMP_ITEM (selection), &x, &y, &w, &h)) + { + g_object_set (priv->widget, + "x1", (gdouble) x, + "y1", (gdouble) y, + "x2", (gdouble) (x + w), + "y2", (gdouble) (y + h), + NULL); + } + + gimp_rectangle_select_tool_update (rect_tool); + } + else + { + GimpChannelOps operation; + + /* prevent this change from halting the tool */ + gimp_tool_control_push_preserve (tool->control, TRUE); + + /* We can conceptually think of a click outside of the + * selection as adding a 0px selection. Behave intuitively + * for the current selection mode + */ + operation = gimp_rectangle_select_tool_get_operation (rect_tool); + + switch (operation) + { + case GIMP_CHANNEL_OP_REPLACE: + case GIMP_CHANNEL_OP_INTERSECT: + gimp_channel_clear (selection, NULL, TRUE); + gimp_image_flush (image); + break; + + case GIMP_CHANNEL_OP_ADD: + case GIMP_CHANNEL_OP_SUBTRACT: + default: + /* Do nothing */ + break; + } + + gimp_tool_control_pop_preserve (tool->control); + } + } + + gimp_rectangle_select_tool_update_option_defaults (rect_tool, FALSE); + } +} + +static void +gimp_rectangle_select_tool_halt (GimpRectangleSelectTool *rect_tool) +{ + GimpTool *tool = GIMP_TOOL (rect_tool); + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + GimpRectangleSelectOptions *options; + + options = GIMP_RECTANGLE_SELECT_TOOL_GET_OPTIONS (rect_tool); + + if (tool->display) + { + GimpDisplayShell *shell = gimp_display_get_shell (tool->display); + + gimp_display_shell_set_highlight (shell, NULL, 0.0); + + gimp_rectangle_options_disconnect (GIMP_RECTANGLE_OPTIONS (options), + G_CALLBACK (gimp_rectangle_select_tool_auto_shrink), + rect_tool); + } + + if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool))) + gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool)); + + /* disconnect bindings manually so they are really gone *now*, we + * might be in the middle of a signal emission that keeps the + * widget and its bindings alive. + */ + g_list_free_full (priv->bindings, (GDestroyNotify) g_object_unref); + priv->bindings = NULL; + + gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), NULL); + g_clear_object (&priv->widget); + + tool->display = NULL; + + gimp_rectangle_select_tool_update_option_defaults (rect_tool, TRUE); +} + +static GimpChannelOps +gimp_rectangle_select_tool_get_operation (GimpRectangleSelectTool *rect_tool) +{ + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + GimpSelectionOptions *options; + + options = GIMP_SELECTION_TOOL_GET_OPTIONS (rect_tool); + + if (priv->use_saved_op) + return priv->operation; + else + return options->operation; +} + +/** + * gimp_rectangle_select_tool_update_option_defaults: + * @crop_tool: + * @ignore_pending: %TRUE to ignore any pending crop rectangle. + * + * Sets the default Fixed: Aspect ratio and Fixed: Size option + * properties. + */ +static void +gimp_rectangle_select_tool_update_option_defaults (GimpRectangleSelectTool *rect_tool, + gboolean ignore_pending) +{ + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + GimpTool *tool = GIMP_TOOL (rect_tool); + GimpRectangleOptions *rect_options; + + rect_options = GIMP_RECTANGLE_OPTIONS (gimp_tool_get_options (tool)); + + if (priv->widget && ! ignore_pending) + { + /* There is a pending rectangle and we should not ignore it, so + * set default Fixed: Size to the same as the current pending + * rectangle width/height. + */ + + gimp_tool_rectangle_pending_size_set (GIMP_TOOL_RECTANGLE (priv->widget), + G_OBJECT (rect_options), + "default-aspect-numerator", + "default-aspect-denominator"); + + g_object_set (G_OBJECT (rect_options), + "use-string-current", TRUE, + NULL); + } + else + { + g_object_set (G_OBJECT (rect_options), + "default-aspect-numerator", 1.0, + "default-aspect-denominator", 1.0, + NULL); + + g_object_set (G_OBJECT (rect_options), + "use-string-current", FALSE, + NULL); + } +} + +static void +gimp_rectangle_select_tool_update (GimpRectangleSelectTool *rect_tool) +{ + GimpTool *tool = GIMP_TOOL (rect_tool); + GimpRectangleSelectToolPrivate *priv = rect_tool->private; + + /* prevent change in selection from halting the tool */ + gimp_tool_control_push_preserve (tool->control, TRUE); + + if (tool->display && ! gimp_tool_control_is_active (tool->control)) + { + gdouble x1, y1, x2, y2; + + gimp_tool_rectangle_get_public_rect (GIMP_TOOL_RECTANGLE (priv->widget), + &x1, &y1, &x2, &y2); + + gimp_rectangle_select_tool_select (rect_tool, + x1, y1, x2 - x1, y2 - y1); + + if (! priv->use_saved_op) + { + GimpSelectionOptions *options; + + options = GIMP_SELECTION_TOOL_GET_OPTIONS (tool); + + /* remember the operation now in case we modify the rectangle */ + priv->operation = options->operation; + priv->use_saved_op = TRUE; + } + } + + gimp_tool_control_pop_preserve (tool->control); + + gimp_rectangle_select_tool_update_option_defaults (rect_tool, FALSE); +} + +static void +gimp_rectangle_select_tool_auto_shrink (GimpRectangleSelectTool *rect_tool) +{ + GimpRectangleSelectToolPrivate *private = rect_tool->private; + gboolean shrink_merged; + + g_object_get (gimp_tool_get_options (GIMP_TOOL (rect_tool)), + "shrink-merged", &shrink_merged, + NULL); + + gimp_tool_rectangle_auto_shrink (GIMP_TOOL_RECTANGLE (private->widget), + shrink_merged); +} |