This patch conditionally adds an alpha channel to a layer when using the Crop Tool, under the following conditions: 1) "Selected layers only" is checked 2) "Allow growing" is checked 3) "Fill with" is set to transparency 4) The crop rectangle is larger than the width or height of the layer 5) The layer does not already have an alpha channel.
772 lines
27 KiB
C
772 lines
27 KiB
C
/* 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 "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "tools-types.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimpdrawable.h"
|
|
#include "core/gimpimage.h"
|
|
#include "core/gimpimage-crop.h"
|
|
#include "core/gimpimage-undo.h"
|
|
#include "core/gimpitem.h"
|
|
#include "core/gimplayer.h"
|
|
#include "core/gimptoolinfo.h"
|
|
|
|
#include "widgets/gimphelp-ids.h"
|
|
|
|
#include "display/gimpdisplay.h"
|
|
#include "display/gimpdisplayshell.h"
|
|
#include "display/gimptoolrectangle.h"
|
|
|
|
#include "gimpcropoptions.h"
|
|
#include "gimpcroptool.h"
|
|
#include "gimprectangleoptions.h"
|
|
#include "gimptoolcontrol.h"
|
|
#include "gimptools-utils.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
static void gimp_crop_tool_constructed (GObject *object);
|
|
static void gimp_crop_tool_dispose (GObject *object);
|
|
|
|
static void gimp_crop_tool_control (GimpTool *tool,
|
|
GimpToolAction action,
|
|
GimpDisplay *display);
|
|
static void gimp_crop_tool_button_press (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpButtonPressType press_type,
|
|
GimpDisplay *display);
|
|
static void gimp_crop_tool_button_release (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpButtonReleaseType release_type,
|
|
GimpDisplay *display);
|
|
static void gimp_crop_tool_motion (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpDisplay *display);
|
|
static void gimp_crop_tool_options_notify (GimpTool *tool,
|
|
GimpToolOptions *options,
|
|
const GParamSpec *pspec);
|
|
|
|
static void gimp_crop_tool_rectangle_changed (GimpToolWidget *rectangle,
|
|
GimpCropTool *crop_tool);
|
|
static void gimp_crop_tool_rectangle_response (GimpToolWidget *rectangle,
|
|
gint response_id,
|
|
GimpCropTool *crop_tool);
|
|
static void gimp_crop_tool_rectangle_change_complete (GimpToolRectangle *rectangle,
|
|
GimpCropTool *crop_tool);
|
|
|
|
static void gimp_crop_tool_start (GimpCropTool *crop_tool,
|
|
GimpDisplay *display);
|
|
static void gimp_crop_tool_commit (GimpCropTool *crop_tool);
|
|
static void gimp_crop_tool_halt (GimpCropTool *crop_tool);
|
|
|
|
static void gimp_crop_tool_update_option_defaults (GimpCropTool *crop_tool,
|
|
gboolean ignore_pending);
|
|
static GimpRectangleConstraint
|
|
gimp_crop_tool_get_constraint (GimpCropTool *crop_tool);
|
|
|
|
static void gimp_crop_tool_image_changed (GimpCropTool *crop_tool,
|
|
GimpImage *image,
|
|
GimpContext *context);
|
|
static void gimp_crop_tool_image_size_changed (GimpCropTool *crop_tool);
|
|
static void gimp_crop_tool_image_selected_layers_changed (GimpCropTool *crop_tool);
|
|
static void gimp_crop_tool_layer_size_changed (GimpCropTool *crop_tool);
|
|
|
|
static void gimp_crop_tool_auto_shrink (GimpCropTool *crop_tool);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpCropTool, gimp_crop_tool, GIMP_TYPE_DRAW_TOOL)
|
|
|
|
#define parent_class gimp_crop_tool_parent_class
|
|
|
|
|
|
/* public functions */
|
|
|
|
void
|
|
gimp_crop_tool_register (GimpToolRegisterCallback callback,
|
|
gpointer data)
|
|
{
|
|
(* callback) (GIMP_TYPE_CROP_TOOL,
|
|
GIMP_TYPE_CROP_OPTIONS,
|
|
gimp_crop_options_gui,
|
|
GIMP_CONTEXT_PROP_MASK_FOREGROUND |
|
|
GIMP_CONTEXT_PROP_MASK_BACKGROUND |
|
|
GIMP_CONTEXT_PROP_MASK_PATTERN,
|
|
"gimp-crop-tool",
|
|
_("Crop"),
|
|
_("Crop Tool: Remove edge areas from image or layer"),
|
|
N_("_Crop"), "<shift>C",
|
|
NULL, GIMP_HELP_TOOL_CROP,
|
|
GIMP_ICON_TOOL_CROP,
|
|
data);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_class_init (GimpCropToolClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
|
|
|
|
object_class->constructed = gimp_crop_tool_constructed;
|
|
object_class->dispose = gimp_crop_tool_dispose;
|
|
|
|
tool_class->control = gimp_crop_tool_control;
|
|
tool_class->button_press = gimp_crop_tool_button_press;
|
|
tool_class->button_release = gimp_crop_tool_button_release;
|
|
tool_class->motion = gimp_crop_tool_motion;
|
|
tool_class->options_notify = gimp_crop_tool_options_notify;
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_init (GimpCropTool *crop_tool)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (crop_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_cursor (tool->control,
|
|
GIMP_CURSOR_CROSSHAIR_SMALL);
|
|
gimp_tool_control_set_tool_cursor (tool->control,
|
|
GIMP_TOOL_CURSOR_CROP);
|
|
|
|
gimp_draw_tool_set_default_status (GIMP_DRAW_TOOL (tool),
|
|
_("Click-Drag to draw a crop rectangle"));
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_constructed (GObject *object)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (object);
|
|
GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
|
|
GimpCropOptions *options = GIMP_CROP_TOOL_GET_OPTIONS (crop_tool);
|
|
GimpContext *context;
|
|
GimpToolInfo *tool_info;
|
|
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
|
|
tool_info = GIMP_TOOL (crop_tool)->tool_info;
|
|
|
|
context = gimp_get_user_context (tool_info->gimp);
|
|
|
|
g_signal_connect_object (context, "image-changed",
|
|
G_CALLBACK (gimp_crop_tool_image_changed),
|
|
crop_tool,
|
|
G_CONNECT_SWAPPED);
|
|
|
|
/* Make sure we are connected to "size-changed" for the initial
|
|
* image.
|
|
*/
|
|
gimp_crop_tool_image_changed (crop_tool,
|
|
gimp_context_get_image (context),
|
|
context);
|
|
|
|
/* The Crop Tool is only destructive when "Delete Pixels" is enabled */
|
|
GIMP_TOOL_GET_CLASS (tool)->is_destructive = options->delete_pixels;
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_dispose (GObject *object)
|
|
{
|
|
GimpCropTool *crop_tool = GIMP_CROP_TOOL (object);
|
|
|
|
/* Clean up current_image and current_layers. */
|
|
gimp_crop_tool_image_changed (crop_tool, NULL, NULL);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_control (GimpTool *tool,
|
|
GimpToolAction action,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
|
|
|
|
switch (action)
|
|
{
|
|
case GIMP_TOOL_ACTION_PAUSE:
|
|
case GIMP_TOOL_ACTION_RESUME:
|
|
break;
|
|
|
|
case GIMP_TOOL_ACTION_HALT:
|
|
gimp_crop_tool_halt (crop_tool);
|
|
break;
|
|
|
|
case GIMP_TOOL_ACTION_COMMIT:
|
|
gimp_crop_tool_commit (crop_tool);
|
|
break;
|
|
}
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_button_press (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpButtonPressType press_type,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
|
|
|
|
if (tool->display && display != tool->display)
|
|
gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
|
|
|
|
if (! tool->display)
|
|
{
|
|
gimp_crop_tool_start (crop_tool, display);
|
|
|
|
gimp_tool_widget_hover (crop_tool->widget, coords, state, TRUE);
|
|
|
|
/* HACK: force CREATING on a newly created rectangle; otherwise,
|
|
* property bindings would cause the rectangle to start with the
|
|
* size from tool options.
|
|
*/
|
|
gimp_tool_rectangle_set_function (GIMP_TOOL_RECTANGLE (crop_tool->widget),
|
|
GIMP_TOOL_RECTANGLE_CREATING);
|
|
}
|
|
|
|
if (gimp_tool_widget_button_press (crop_tool->widget, coords, time, state,
|
|
press_type))
|
|
{
|
|
crop_tool->grab_widget = crop_tool->widget;
|
|
}
|
|
|
|
gimp_tool_control_activate (tool->control);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_button_release (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpButtonReleaseType release_type,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
|
|
|
|
gimp_tool_control_halt (tool->control);
|
|
|
|
if (crop_tool->grab_widget)
|
|
{
|
|
gimp_tool_widget_button_release (crop_tool->grab_widget,
|
|
coords, time, state, release_type);
|
|
crop_tool->grab_widget = NULL;
|
|
}
|
|
|
|
gimp_tool_push_status (tool, display, _("Click or press Enter to crop"));
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_motion (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
|
|
|
|
if (crop_tool->grab_widget)
|
|
{
|
|
gimp_tool_widget_motion (crop_tool->grab_widget, coords, time, state);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_options_notify (GimpTool *tool,
|
|
GimpToolOptions *options,
|
|
const GParamSpec *pspec)
|
|
{
|
|
GimpCropTool *crop_tool = GIMP_CROP_TOOL (tool);
|
|
GimpCropOptions *crop_options = GIMP_CROP_TOOL_GET_OPTIONS (crop_tool);
|
|
|
|
if (! strcmp (pspec->name, "layer-only") ||
|
|
! strcmp (pspec->name, "allow-growing"))
|
|
{
|
|
if (crop_tool->widget)
|
|
{
|
|
gimp_tool_rectangle_set_constraint (GIMP_TOOL_RECTANGLE (crop_tool->widget),
|
|
gimp_crop_tool_get_constraint (crop_tool));
|
|
}
|
|
else
|
|
{
|
|
gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
}
|
|
else if (! strcmp (pspec->name, "delete-pixels"))
|
|
{
|
|
GIMP_TOOL_GET_CLASS (tool)->is_destructive = crop_options->delete_pixels;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_rectangle_changed (GimpToolWidget *rectangle,
|
|
GimpCropTool *crop_tool)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_rectangle_response (GimpToolWidget *rectangle,
|
|
gint response_id,
|
|
GimpCropTool *crop_tool)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (crop_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_crop_tool_rectangle_change_complete (GimpToolRectangle *rectangle,
|
|
GimpCropTool *crop_tool)
|
|
{
|
|
gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_start (GimpCropTool *crop_tool,
|
|
GimpDisplay *display)
|
|
{
|
|
static const gchar *properties[] =
|
|
{
|
|
"highlight",
|
|
"highlight-opacity",
|
|
"guide",
|
|
"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 (crop_tool);
|
|
GimpDisplayShell *shell = gimp_display_get_shell (display);
|
|
GimpCropOptions *options = GIMP_CROP_TOOL_GET_OPTIONS (crop_tool);
|
|
GimpToolWidget *widget;
|
|
gint i;
|
|
|
|
tool->display = display;
|
|
|
|
crop_tool->widget = widget = gimp_tool_rectangle_new (shell);
|
|
|
|
g_object_set (widget,
|
|
"status-title", _("Crop to: "),
|
|
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);
|
|
|
|
crop_tool->bindings = g_list_prepend (crop_tool->bindings, binding);
|
|
}
|
|
|
|
gimp_rectangle_options_connect (GIMP_RECTANGLE_OPTIONS (options),
|
|
gimp_display_get_image (shell->display),
|
|
G_CALLBACK (gimp_crop_tool_auto_shrink),
|
|
crop_tool);
|
|
|
|
gimp_tool_rectangle_set_constraint (GIMP_TOOL_RECTANGLE (widget),
|
|
gimp_crop_tool_get_constraint (crop_tool));
|
|
|
|
g_signal_connect (widget, "changed",
|
|
G_CALLBACK (gimp_crop_tool_rectangle_changed),
|
|
crop_tool);
|
|
g_signal_connect (widget, "response",
|
|
G_CALLBACK (gimp_crop_tool_rectangle_response),
|
|
crop_tool);
|
|
g_signal_connect (widget, "change-complete",
|
|
G_CALLBACK (gimp_crop_tool_rectangle_change_complete),
|
|
crop_tool);
|
|
|
|
gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_commit (GimpCropTool *crop_tool)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (crop_tool);
|
|
|
|
if (tool->display)
|
|
{
|
|
GimpCropOptions *options = GIMP_CROP_TOOL_GET_OPTIONS (tool);
|
|
GimpImage *image = gimp_display_get_image (tool->display);
|
|
gdouble x, y;
|
|
gdouble x2, y2;
|
|
gint w, h;
|
|
|
|
gimp_tool_rectangle_get_public_rect (GIMP_TOOL_RECTANGLE (crop_tool->widget),
|
|
&x, &y, &x2, &y2);
|
|
w = x2 - x;
|
|
h = y2 - y;
|
|
|
|
gimp_tool_pop_status (tool, tool->display);
|
|
|
|
/* if rectangle exists, crop it */
|
|
if (w > 0 && h > 0)
|
|
{
|
|
if (options->layer_only)
|
|
{
|
|
GList *layers = gimp_image_get_selected_layers (image);
|
|
GList *iter;
|
|
gint off_x, off_y;
|
|
gchar *undo_text;
|
|
|
|
if (! layers)
|
|
{
|
|
gimp_tool_message_literal (tool, tool->display,
|
|
_("There are no selected layers to crop."));
|
|
return;
|
|
}
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
if (! gimp_item_is_content_locked (GIMP_ITEM (iter->data), NULL))
|
|
break;
|
|
|
|
if (iter == NULL)
|
|
{
|
|
gimp_tool_message_literal (tool, tool->display,
|
|
_("All selected layers' pixels are locked."));
|
|
gimp_tools_blink_lock_box (tool->display->gimp, GIMP_ITEM (layers->data));
|
|
return;
|
|
}
|
|
|
|
undo_text = ngettext ("Resize Layer", "Resize %d layers",
|
|
g_list_length (layers));
|
|
undo_text = g_strdup_printf (undo_text, g_list_length (layers));
|
|
gimp_image_undo_group_start (image,
|
|
GIMP_UNDO_GROUP_IMAGE_CROP,
|
|
undo_text);
|
|
g_free (undo_text);
|
|
for (iter = layers; iter; iter = iter->next)
|
|
{
|
|
gimp_item_get_offset (GIMP_ITEM (iter->data), &off_x, &off_y);
|
|
|
|
off_x -= x;
|
|
off_y -= y;
|
|
|
|
/* If we have the crop tool set to allow growing and to fill
|
|
* with transparency, and the crop rectangle is larger than
|
|
* the layer, then we add transparency if there is none */
|
|
if (options->allow_growing &&
|
|
(gimp_item_get_width (GIMP_ITEM (iter->data)) < w ||
|
|
gimp_item_get_width (GIMP_ITEM (iter->data)) < h) &&
|
|
options->fill_type == GIMP_FILL_TRANSPARENT &&
|
|
! gimp_drawable_has_alpha (GIMP_DRAWABLE (iter->data)))
|
|
{
|
|
gimp_layer_add_alpha (GIMP_LAYER (iter->data));
|
|
}
|
|
|
|
gimp_item_resize (GIMP_ITEM (iter->data),
|
|
GIMP_CONTEXT (options), options->fill_type,
|
|
w, h, off_x, off_y);
|
|
}
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
else
|
|
{
|
|
gimp_image_crop (image,
|
|
GIMP_CONTEXT (options), GIMP_FILL_TRANSPARENT,
|
|
x, y, w, h, options->delete_pixels);
|
|
}
|
|
|
|
gimp_image_flush (image);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_halt (GimpCropTool *crop_tool)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (crop_tool);
|
|
GimpCropOptions *options = GIMP_CROP_TOOL_GET_OPTIONS (crop_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_crop_tool_auto_shrink),
|
|
crop_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 (crop_tool->bindings, (GDestroyNotify) g_object_unref);
|
|
crop_tool->bindings = NULL;
|
|
|
|
gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), NULL);
|
|
g_clear_object (&crop_tool->widget);
|
|
|
|
tool->display = NULL;
|
|
g_list_free (tool->drawables);
|
|
tool->drawables = NULL;
|
|
|
|
gimp_crop_tool_update_option_defaults (crop_tool, TRUE);
|
|
}
|
|
|
|
/**
|
|
* gimp_crop_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_crop_tool_update_option_defaults (GimpCropTool *crop_tool,
|
|
gboolean ignore_pending)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (crop_tool);
|
|
GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (crop_tool->widget);
|
|
GimpRectangleOptions *options;
|
|
|
|
options = GIMP_RECTANGLE_OPTIONS (GIMP_TOOL_GET_OPTIONS (tool));
|
|
|
|
if (rectangle && ! ignore_pending)
|
|
{
|
|
/* There is a pending rectangle and we should not ignore it, so
|
|
* set default Fixed: Aspect ratio to the same as the current
|
|
* pending rectangle width/height.
|
|
*/
|
|
|
|
gimp_tool_rectangle_pending_size_set (rectangle,
|
|
G_OBJECT (options),
|
|
"default-aspect-numerator",
|
|
"default-aspect-denominator");
|
|
|
|
g_object_set (G_OBJECT (options),
|
|
"use-string-current", TRUE,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
/* There is no pending rectangle, set default Fixed: Aspect
|
|
* ratio to that of the current image/layer.
|
|
*/
|
|
|
|
if (! rectangle)
|
|
{
|
|
/* ugly hack: if we don't have a widget, construct a temporary one
|
|
* so that we can use it to call
|
|
* gimp_tool_rectangle_constraint_size_set().
|
|
*/
|
|
|
|
GimpContext *context = gimp_get_user_context (tool->tool_info->gimp);
|
|
GimpDisplay *display = gimp_context_get_display (context);
|
|
|
|
if (display)
|
|
{
|
|
GimpDisplayShell *shell = gimp_display_get_shell (display);
|
|
|
|
rectangle = GIMP_TOOL_RECTANGLE (gimp_tool_rectangle_new (shell));
|
|
|
|
gimp_tool_rectangle_set_constraint (
|
|
rectangle, gimp_crop_tool_get_constraint (crop_tool));
|
|
}
|
|
}
|
|
|
|
if (rectangle)
|
|
{
|
|
gimp_tool_rectangle_constraint_size_set (rectangle,
|
|
G_OBJECT (options),
|
|
"default-aspect-numerator",
|
|
"default-aspect-denominator");
|
|
|
|
if (! crop_tool->widget)
|
|
g_object_unref (rectangle);
|
|
}
|
|
|
|
g_object_set (G_OBJECT (options),
|
|
"use-string-current", FALSE,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static GimpRectangleConstraint
|
|
gimp_crop_tool_get_constraint (GimpCropTool *crop_tool)
|
|
{
|
|
GimpCropOptions *crop_options = GIMP_CROP_TOOL_GET_OPTIONS (crop_tool);
|
|
|
|
if (crop_options->allow_growing)
|
|
{
|
|
return GIMP_RECTANGLE_CONSTRAIN_NONE;
|
|
}
|
|
else
|
|
{
|
|
return crop_options->layer_only ? GIMP_RECTANGLE_CONSTRAIN_DRAWABLE :
|
|
GIMP_RECTANGLE_CONSTRAIN_IMAGE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_image_changed (GimpCropTool *crop_tool,
|
|
GimpImage *image,
|
|
GimpContext *context)
|
|
{
|
|
if (crop_tool->current_image)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (crop_tool->current_image,
|
|
gimp_crop_tool_image_size_changed,
|
|
NULL);
|
|
g_signal_handlers_disconnect_by_func (crop_tool->current_image,
|
|
gimp_crop_tool_image_selected_layers_changed,
|
|
NULL);
|
|
}
|
|
|
|
g_set_weak_pointer (&crop_tool->current_image, image);
|
|
|
|
if (crop_tool->current_image)
|
|
{
|
|
g_signal_connect_object (crop_tool->current_image, "size-changed",
|
|
G_CALLBACK (gimp_crop_tool_image_size_changed),
|
|
crop_tool,
|
|
G_CONNECT_SWAPPED);
|
|
g_signal_connect_object (crop_tool->current_image, "selected-layers-changed",
|
|
G_CALLBACK (gimp_crop_tool_image_selected_layers_changed),
|
|
crop_tool,
|
|
G_CONNECT_SWAPPED);
|
|
}
|
|
|
|
/* Make sure we are connected to "size-changed" for the initial
|
|
* layer.
|
|
*/
|
|
gimp_crop_tool_image_selected_layers_changed (crop_tool);
|
|
|
|
gimp_crop_tool_update_option_defaults (GIMP_CROP_TOOL (crop_tool), FALSE);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_image_size_changed (GimpCropTool *crop_tool)
|
|
{
|
|
gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_image_selected_layers_changed (GimpCropTool *crop_tool)
|
|
{
|
|
GList *iter;
|
|
|
|
if (crop_tool->current_layers)
|
|
{
|
|
for (iter = crop_tool->current_layers; iter; iter = iter->next)
|
|
{
|
|
if (iter->data)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (iter->data,
|
|
gimp_crop_tool_layer_size_changed,
|
|
NULL);
|
|
|
|
g_clear_weak_pointer (&iter->data);
|
|
}
|
|
}
|
|
g_list_free (crop_tool->current_layers);
|
|
crop_tool->current_layers = NULL;
|
|
}
|
|
|
|
if (crop_tool->current_image)
|
|
{
|
|
crop_tool->current_layers = gimp_image_get_selected_layers (crop_tool->current_image);
|
|
crop_tool->current_layers = g_list_copy (crop_tool->current_layers);
|
|
}
|
|
else
|
|
{
|
|
crop_tool->current_layers = NULL;
|
|
}
|
|
|
|
if (crop_tool->current_layers)
|
|
{
|
|
for (iter = crop_tool->current_layers; iter; iter = iter->next)
|
|
{
|
|
/* NOT g_set_weak_pointer() because the pointer is already set */
|
|
g_object_add_weak_pointer (G_OBJECT (iter->data),
|
|
(gpointer) &iter->data);
|
|
|
|
g_signal_connect_object (iter->data, "size-changed",
|
|
G_CALLBACK (gimp_crop_tool_layer_size_changed),
|
|
crop_tool,
|
|
G_CONNECT_SWAPPED);
|
|
}
|
|
}
|
|
|
|
gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_layer_size_changed (GimpCropTool *crop_tool)
|
|
{
|
|
gimp_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
|
|
static void
|
|
gimp_crop_tool_auto_shrink (GimpCropTool *crop_tool)
|
|
{
|
|
gboolean shrink_merged ;
|
|
|
|
g_object_get (gimp_tool_get_options (GIMP_TOOL (crop_tool)),
|
|
"shrink-merged", &shrink_merged,
|
|
NULL);
|
|
|
|
gimp_tool_rectangle_auto_shrink (GIMP_TOOL_RECTANGLE (crop_tool->widget),
|
|
shrink_merged);
|
|
}
|