/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include "libpikamath/pikamath.h" #include "libpikawidgets/pikawidgets.h" #include "tools-types.h" #include "core/pikachannel-select.h" #include "core/pikachannel.h" #include "core/pikaimage.h" #include "core/pikalayer-floating-selection.h" #include "core/pikapickable.h" #include "widgets/pikahelp-ids.h" #include "widgets/pikawidgets-utils.h" #include "display/pikadisplay.h" #include "display/pikadisplayshell.h" #include "display/pikatoolrectangle.h" #include "pikaeditselectiontool.h" #include "pikarectangleoptions.h" #include "pikarectangleselecttool.h" #include "pikarectangleselectoptions.h" #include "pikatoolcontrol.h" #include "pika-intl.h" struct _PikaRectangleSelectToolPrivate { PikaChannelOps operation; /* remember for use when modifying */ gboolean use_saved_op; /* use operation or get from options */ gdouble press_x; gdouble press_y; PikaToolWidget *widget; PikaToolWidget *grab_widget; GList *bindings; }; static void pika_rectangle_select_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display); static void pika_rectangle_select_tool_button_press (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type, PikaDisplay *display); static void pika_rectangle_select_tool_button_release (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type, PikaDisplay *display); static void pika_rectangle_select_tool_motion (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaDisplay *display); static gboolean pika_rectangle_select_tool_key_press (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display); static void pika_rectangle_select_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display); static void pika_rectangle_select_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display); static void pika_rectangle_select_tool_options_notify (PikaTool *tool, PikaToolOptions *options, const GParamSpec *pspec); static gboolean pika_rectangle_select_tool_select (PikaRectangleSelectTool *rect_tool, gint x, gint y, gint w, gint h); static void pika_rectangle_select_tool_real_select (PikaRectangleSelectTool *rect_tool, PikaChannelOps operation, gint x, gint y, gint w, gint h); static void pika_rectangle_select_tool_rectangle_response (PikaToolWidget *widget, gint response_id, PikaRectangleSelectTool *rect_tool); static void pika_rectangle_select_tool_rectangle_change_complete (PikaToolWidget *widget, PikaRectangleSelectTool *rect_tool); static void pika_rectangle_select_tool_start (PikaRectangleSelectTool *rect_tool, PikaDisplay *display); static void pika_rectangle_select_tool_commit (PikaRectangleSelectTool *rect_tool); static void pika_rectangle_select_tool_halt (PikaRectangleSelectTool *rect_tool); static PikaChannelOps pika_rectangle_select_tool_get_operation (PikaRectangleSelectTool *rect_tool); static void pika_rectangle_select_tool_update_option_defaults (PikaRectangleSelectTool *rect_tool, gboolean ignore_pending); static void pika_rectangle_select_tool_update (PikaRectangleSelectTool *rect_tool); static void pika_rectangle_select_tool_auto_shrink (PikaRectangleSelectTool *rect_tool); G_DEFINE_TYPE_WITH_PRIVATE (PikaRectangleSelectTool, pika_rectangle_select_tool, PIKA_TYPE_SELECTION_TOOL) #define parent_class pika_rectangle_select_tool_parent_class void pika_rectangle_select_tool_register (PikaToolRegisterCallback callback, gpointer data) { (* callback) (PIKA_TYPE_RECTANGLE_SELECT_TOOL, PIKA_TYPE_RECTANGLE_SELECT_OPTIONS, pika_rectangle_select_options_gui, 0, "pika-rect-select-tool", _("Rectangle Select"), _("Rectangle Select Tool: Select a rectangular region"), N_("_Rectangle Select"), "R", NULL, PIKA_HELP_TOOL_RECT_SELECT, PIKA_ICON_TOOL_RECT_SELECT, data); } static void pika_rectangle_select_tool_class_init (PikaRectangleSelectToolClass *klass) { PikaToolClass *tool_class = PIKA_TOOL_CLASS (klass); tool_class->control = pika_rectangle_select_tool_control; tool_class->button_press = pika_rectangle_select_tool_button_press; tool_class->button_release = pika_rectangle_select_tool_button_release; tool_class->motion = pika_rectangle_select_tool_motion; tool_class->key_press = pika_rectangle_select_tool_key_press; tool_class->oper_update = pika_rectangle_select_tool_oper_update; tool_class->cursor_update = pika_rectangle_select_tool_cursor_update; tool_class->options_notify = pika_rectangle_select_tool_options_notify; klass->select = pika_rectangle_select_tool_real_select; } static void pika_rectangle_select_tool_init (PikaRectangleSelectTool *rect_tool) { PikaTool *tool = PIKA_TOOL (rect_tool); rect_tool->private = pika_rectangle_select_tool_get_instance_private (rect_tool); pika_tool_control_set_wants_click (tool->control, TRUE); pika_tool_control_set_active_modifiers (tool->control, PIKA_TOOL_ACTIVE_MODIFIERS_SEPARATE); pika_tool_control_set_precision (tool->control, PIKA_CURSOR_PRECISION_PIXEL_BORDER); pika_tool_control_set_tool_cursor (tool->control, PIKA_TOOL_CURSOR_RECT_SELECT); pika_tool_control_set_preserve (tool->control, FALSE); pika_tool_control_set_dirty_mask (tool->control, PIKA_DIRTY_IMAGE_SIZE | PIKA_DIRTY_SELECTION); pika_tool_control_set_dirty_action (tool->control, PIKA_TOOL_ACTION_COMMIT); } static void pika_rectangle_select_tool_control (PikaTool *tool, PikaToolAction action, PikaDisplay *display) { PikaRectangleSelectTool *rect_tool = PIKA_RECTANGLE_SELECT_TOOL (tool); switch (action) { case PIKA_TOOL_ACTION_PAUSE: case PIKA_TOOL_ACTION_RESUME: break; case PIKA_TOOL_ACTION_HALT: pika_rectangle_select_tool_halt (rect_tool); break; case PIKA_TOOL_ACTION_COMMIT: pika_rectangle_select_tool_commit (rect_tool); break; } PIKA_TOOL_CLASS (parent_class)->control (tool, action, display); } static void pika_rectangle_select_tool_button_press (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type, PikaDisplay *display) { PikaRectangleSelectTool *rect_tool = PIKA_RECTANGLE_SELECT_TOOL (tool); PikaRectangleSelectToolPrivate *private = rect_tool->private; PikaRectangleFunction function; if (tool->display && display != tool->display) pika_tool_control (tool, PIKA_TOOL_ACTION_COMMIT, tool->display); if (pika_selection_tool_start_edit (PIKA_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; pika_tool_rectangle_get_public_rect (PIKA_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) pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, display); else pika_tool_control (tool, PIKA_TOOL_ACTION_COMMIT, display); pika_rectangle_select_tool_update_option_defaults (rect_tool, TRUE); return; } if (! tool->display) { pika_rectangle_select_tool_start (rect_tool, display); pika_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. */ pika_tool_rectangle_set_function (PIKA_TOOL_RECTANGLE (private->widget), PIKA_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 & (pika_get_extend_selection_mask () | pika_get_modify_selection_mask ())) { pika_tool_rectangle_set_function (PIKA_TOOL_RECTANGLE (private->widget), PIKA_TOOL_RECTANGLE_CREATING); } if (pika_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 = pika_tool_rectangle_get_function (PIKA_TOOL_RECTANGLE (private->widget)); if (function == PIKA_TOOL_RECTANGLE_CREATING) private->use_saved_op = FALSE; pika_selection_tool_start_change ( PIKA_SELECTION_TOOL (tool), function == PIKA_TOOL_RECTANGLE_CREATING, pika_rectangle_select_tool_get_operation (rect_tool)); pika_tool_control_activate (tool->control); } static void pika_rectangle_select_tool_button_release (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonReleaseType release_type, PikaDisplay *display) { PikaRectangleSelectTool *rect_tool = PIKA_RECTANGLE_SELECT_TOOL (tool); PikaRectangleSelectToolPrivate *priv = rect_tool->private; pika_tool_control_halt (tool->control); pika_selection_tool_end_change (PIKA_SELECTION_TOOL (tool), /* if the user has not moved the mouse, * cancel the change */ release_type == PIKA_BUTTON_RELEASE_CLICK || release_type == PIKA_BUTTON_RELEASE_CANCEL); pika_tool_pop_status (tool, display); if (priv->grab_widget) { pika_tool_widget_button_release (priv->grab_widget, coords, time, state, release_type); priv->grab_widget = NULL; } } static void pika_rectangle_select_tool_motion (PikaTool *tool, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaDisplay *display) { PikaRectangleSelectTool *rect_tool = PIKA_RECTANGLE_SELECT_TOOL (tool); PikaRectangleSelectToolPrivate *priv = rect_tool->private; if (priv->grab_widget) { pika_tool_widget_motion (priv->grab_widget, coords, time, state); } } static gboolean pika_rectangle_select_tool_key_press (PikaTool *tool, GdkEventKey *kevent, PikaDisplay *display) { PikaRectangleSelectTool *rect_tool = PIKA_RECTANGLE_SELECT_TOOL (tool); PikaRectangleSelectToolPrivate *priv = rect_tool->private; if (priv->widget && display == tool->display) { if (pika_tool_widget_key_press (priv->widget, kevent)) return TRUE; } return pika_edit_selection_tool_key_press (tool, kevent, display); } static void pika_rectangle_select_tool_oper_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, gboolean proximity, PikaDisplay *display) { PikaRectangleSelectTool *rect_tool = PIKA_RECTANGLE_SELECT_TOOL (tool); PikaRectangleSelectToolPrivate *priv = rect_tool->private; if (priv->widget && display == tool->display) { pika_tool_widget_hover (priv->widget, coords, state, proximity); } PIKA_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity, display); } static void pika_rectangle_select_tool_cursor_update (PikaTool *tool, const PikaCoords *coords, GdkModifierType state, PikaDisplay *display) { PikaRectangleSelectTool *rect_tool = PIKA_RECTANGLE_SELECT_TOOL (tool); PikaRectangleSelectToolPrivate *private = rect_tool->private; PikaCursorType cursor = PIKA_CURSOR_CROSSHAIR_SMALL; PikaCursorModifier modifier = PIKA_CURSOR_MODIFIER_NONE; if (private->widget && display == tool->display) { pika_tool_widget_get_cursor (private->widget, coords, state, &cursor, NULL, &modifier); } pika_tool_control_set_cursor (tool->control, cursor); pika_tool_control_set_cursor_modifier (tool->control, modifier); /* override the previous if shift or ctrl are down */ if (state & (pika_get_extend_selection_mask () | pika_get_modify_selection_mask ())) { pika_tool_control_set_cursor (tool->control, PIKA_CURSOR_CROSSHAIR_SMALL); } PIKA_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display); } static void pika_rectangle_select_tool_options_notify (PikaTool *tool, PikaToolOptions *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")) { pika_rectangle_select_tool_update (PIKA_RECTANGLE_SELECT_TOOL (tool)); } PIKA_TOOL_CLASS (parent_class)->options_notify (tool, options, pspec); } static gboolean pika_rectangle_select_tool_select (PikaRectangleSelectTool *rect_tool, gint x, gint y, gint w, gint h) { PikaTool *tool = PIKA_TOOL (rect_tool); PikaImage *image = pika_display_get_image (tool->display); gboolean rectangle_exists; PikaChannelOps operation; pika_tool_pop_status (tool, tool->display); rectangle_exists = (x <= pika_image_get_width (image) && y <= pika_image_get_height (image) && x + w >= 0 && y + h >= 0 && w > 0 && h > 0); operation = pika_rectangle_select_tool_get_operation (rect_tool); /* if rectangle exists, turn it into a selection */ if (rectangle_exists) { pika_selection_tool_start_change (PIKA_SELECTION_TOOL (rect_tool), FALSE, operation); PIKA_RECTANGLE_SELECT_TOOL_GET_CLASS (rect_tool)->select (rect_tool, operation, x, y, w, h); pika_selection_tool_end_change (PIKA_SELECTION_TOOL (rect_tool), FALSE); } return rectangle_exists; } static void pika_rectangle_select_tool_real_select (PikaRectangleSelectTool *rect_tool, PikaChannelOps operation, gint x, gint y, gint w, gint h) { PikaTool *tool = PIKA_TOOL (rect_tool); PikaSelectionOptions *options = PIKA_SELECTION_TOOL_GET_OPTIONS (tool); PikaRectangleSelectOptions *rect_options; PikaChannel *channel; rect_options = PIKA_RECTANGLE_SELECT_TOOL_GET_OPTIONS (tool); channel = pika_image_get_mask (pika_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); pika_channel_select_round_rect (channel, x, y, w, h, radius, radius, operation, options->antialias, options->feather, options->feather_radius, options->feather_radius, TRUE); } else { pika_channel_select_rectangle (channel, x, y, w, h, operation, options->feather, options->feather_radius, options->feather_radius, TRUE); } } static void pika_rectangle_select_tool_rectangle_response (PikaToolWidget *widget, gint response_id, PikaRectangleSelectTool *rect_tool) { PikaTool *tool = PIKA_TOOL (rect_tool); switch (response_id) { case PIKA_TOOL_WIDGET_RESPONSE_CONFIRM: { gdouble x1, y1, x2, y2; pika_tool_rectangle_get_public_rect (PIKA_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 */ pika_rectangle_select_tool_commit (rect_tool); pika_tool_rectangle_get_public_rect (PIKA_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. */ pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, tool->display); } } else { pika_tool_control (tool, PIKA_TOOL_ACTION_COMMIT, tool->display); } } break; case PIKA_TOOL_WIDGET_RESPONSE_CANCEL: pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, tool->display); break; } } static void pika_rectangle_select_tool_rectangle_change_complete (PikaToolWidget *widget, PikaRectangleSelectTool *rect_tool) { pika_rectangle_select_tool_update (rect_tool); } static void pika_rectangle_select_tool_start (PikaRectangleSelectTool *rect_tool, PikaDisplay *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" }; PikaTool *tool = PIKA_TOOL (rect_tool); PikaRectangleSelectToolPrivate *private = rect_tool->private; PikaDisplayShell *shell = pika_display_get_shell (display); PikaRectangleSelectOptions *options; PikaToolWidget *widget; gboolean draw_ellipse; gint i; options = PIKA_RECTANGLE_SELECT_TOOL_GET_OPTIONS (rect_tool); tool->display = display; private->widget = widget = pika_tool_rectangle_new (shell); draw_ellipse = PIKA_RECTANGLE_SELECT_TOOL_GET_CLASS (rect_tool)->draw_ellipse; g_object_set (widget, "draw-ellipse", draw_ellipse, "status-title", draw_ellipse ? _("Ellipse: ") : _("Rectangle: "), NULL); pika_draw_tool_set_widget (PIKA_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); } pika_rectangle_options_connect (PIKA_RECTANGLE_OPTIONS (options), pika_display_get_image (shell->display), G_CALLBACK (pika_rectangle_select_tool_auto_shrink), rect_tool); g_signal_connect (widget, "response", G_CALLBACK (pika_rectangle_select_tool_rectangle_response), rect_tool); g_signal_connect (widget, "change-complete", G_CALLBACK (pika_rectangle_select_tool_rectangle_change_complete), rect_tool); pika_draw_tool_start (PIKA_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 pika_rectangle_select_tool_commit (PikaRectangleSelectTool *rect_tool) { PikaTool *tool = PIKA_TOOL (rect_tool); PikaRectangleSelectToolPrivate *priv = rect_tool->private; if (priv->widget) { gdouble x1, y1, x2, y2; gint w, h; pika_tool_rectangle_get_public_rect (PIKA_TOOL_RECTANGLE (priv->widget), &x1, &y1, &x2, &y2); w = x2 - x1; h = y2 - y1; if (w == 0 && h == 0) { PikaImage *image = pika_display_get_image (tool->display); PikaChannel *selection = pika_image_get_mask (image); gint press_x; gint press_y; if (pika_image_get_floating_selection (image)) { floating_sel_anchor (pika_image_get_floating_selection (image)); pika_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 (pika_pickable_get_opacity_at (PIKA_PICKABLE (selection), press_x, press_y) > 0.5) { gint x, y, w, h; if (pika_item_bounds (PIKA_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); } pika_rectangle_select_tool_update (rect_tool); } else { PikaChannelOps operation; /* prevent this change from halting the tool */ pika_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 = pika_rectangle_select_tool_get_operation (rect_tool); switch (operation) { case PIKA_CHANNEL_OP_REPLACE: case PIKA_CHANNEL_OP_INTERSECT: pika_channel_clear (selection, NULL, TRUE); pika_image_flush (image); break; case PIKA_CHANNEL_OP_ADD: case PIKA_CHANNEL_OP_SUBTRACT: default: /* Do nothing */ break; } pika_tool_control_pop_preserve (tool->control); } } pika_rectangle_select_tool_update_option_defaults (rect_tool, FALSE); } } static void pika_rectangle_select_tool_halt (PikaRectangleSelectTool *rect_tool) { PikaTool *tool = PIKA_TOOL (rect_tool); PikaRectangleSelectToolPrivate *priv = rect_tool->private; PikaRectangleSelectOptions *options; options = PIKA_RECTANGLE_SELECT_TOOL_GET_OPTIONS (rect_tool); if (tool->display) { PikaDisplayShell *shell = pika_display_get_shell (tool->display); pika_display_shell_set_highlight (shell, NULL, 0.0); pika_rectangle_options_disconnect (PIKA_RECTANGLE_OPTIONS (options), G_CALLBACK (pika_rectangle_select_tool_auto_shrink), rect_tool); } if (pika_draw_tool_is_active (PIKA_DRAW_TOOL (tool))) pika_draw_tool_stop (PIKA_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; pika_draw_tool_set_widget (PIKA_DRAW_TOOL (tool), NULL); g_clear_object (&priv->widget); tool->display = NULL; pika_rectangle_select_tool_update_option_defaults (rect_tool, TRUE); } static PikaChannelOps pika_rectangle_select_tool_get_operation (PikaRectangleSelectTool *rect_tool) { PikaRectangleSelectToolPrivate *priv = rect_tool->private; PikaSelectionOptions *options; options = PIKA_SELECTION_TOOL_GET_OPTIONS (rect_tool); if (priv->use_saved_op) return priv->operation; else return options->operation; } /** * pika_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 pika_rectangle_select_tool_update_option_defaults (PikaRectangleSelectTool *rect_tool, gboolean ignore_pending) { PikaRectangleSelectToolPrivate *priv = rect_tool->private; PikaTool *tool = PIKA_TOOL (rect_tool); PikaRectangleOptions *rect_options; rect_options = PIKA_RECTANGLE_OPTIONS (pika_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. */ pika_tool_rectangle_pending_size_set (PIKA_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 pika_rectangle_select_tool_update (PikaRectangleSelectTool *rect_tool) { PikaTool *tool = PIKA_TOOL (rect_tool); PikaRectangleSelectToolPrivate *priv = rect_tool->private; /* prevent change in selection from halting the tool */ pika_tool_control_push_preserve (tool->control, TRUE); if (tool->display && ! pika_tool_control_is_active (tool->control)) { gdouble x1, y1, x2, y2; pika_tool_rectangle_get_public_rect (PIKA_TOOL_RECTANGLE (priv->widget), &x1, &y1, &x2, &y2); pika_rectangle_select_tool_select (rect_tool, x1, y1, x2 - x1, y2 - y1); if (! priv->use_saved_op) { PikaSelectionOptions *options; options = PIKA_SELECTION_TOOL_GET_OPTIONS (tool); /* remember the operation now in case we modify the rectangle */ priv->operation = options->operation; priv->use_saved_op = TRUE; } } pika_tool_control_pop_preserve (tool->control); pika_rectangle_select_tool_update_option_defaults (rect_tool, FALSE); } static void pika_rectangle_select_tool_auto_shrink (PikaRectangleSelectTool *rect_tool) { PikaRectangleSelectToolPrivate *private = rect_tool->private; gboolean shrink_merged; g_object_get (pika_tool_get_options (PIKA_TOOL (rect_tool)), "shrink-merged", &shrink_merged, NULL); pika_tool_rectangle_auto_shrink (PIKA_TOOL_RECTANGLE (private->widget), shrink_merged); }