753 lines
26 KiB
C
753 lines
26 KiB
C
/* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libpikawidgets/pikawidgets.h"
|
|
|
|
#include "tools-types.h"
|
|
|
|
#include "core/pika.h"
|
|
#include "core/pikaimage.h"
|
|
#include "core/pikaimage-crop.h"
|
|
#include "core/pikaimage-undo.h"
|
|
#include "core/pikaitem.h"
|
|
#include "core/pikatoolinfo.h"
|
|
|
|
#include "widgets/pikahelp-ids.h"
|
|
|
|
#include "display/pikadisplay.h"
|
|
#include "display/pikadisplayshell.h"
|
|
#include "display/pikatoolrectangle.h"
|
|
|
|
#include "pikacropoptions.h"
|
|
#include "pikacroptool.h"
|
|
#include "pikarectangleoptions.h"
|
|
#include "pikatoolcontrol.h"
|
|
#include "pikatools-utils.h"
|
|
|
|
#include "pika-intl.h"
|
|
|
|
|
|
static void pika_crop_tool_constructed (GObject *object);
|
|
static void pika_crop_tool_dispose (GObject *object);
|
|
|
|
static void pika_crop_tool_control (PikaTool *tool,
|
|
PikaToolAction action,
|
|
PikaDisplay *display);
|
|
static void pika_crop_tool_button_press (PikaTool *tool,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaButtonPressType press_type,
|
|
PikaDisplay *display);
|
|
static void pika_crop_tool_button_release (PikaTool *tool,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaButtonReleaseType release_type,
|
|
PikaDisplay *display);
|
|
static void pika_crop_tool_motion (PikaTool *tool,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaDisplay *display);
|
|
static void pika_crop_tool_options_notify (PikaTool *tool,
|
|
PikaToolOptions *options,
|
|
const GParamSpec *pspec);
|
|
|
|
static void pika_crop_tool_rectangle_changed (PikaToolWidget *rectangle,
|
|
PikaCropTool *crop_tool);
|
|
static void pika_crop_tool_rectangle_response (PikaToolWidget *rectangle,
|
|
gint response_id,
|
|
PikaCropTool *crop_tool);
|
|
static void pika_crop_tool_rectangle_change_complete (PikaToolRectangle *rectangle,
|
|
PikaCropTool *crop_tool);
|
|
|
|
static void pika_crop_tool_start (PikaCropTool *crop_tool,
|
|
PikaDisplay *display);
|
|
static void pika_crop_tool_commit (PikaCropTool *crop_tool);
|
|
static void pika_crop_tool_halt (PikaCropTool *crop_tool);
|
|
|
|
static void pika_crop_tool_update_option_defaults (PikaCropTool *crop_tool,
|
|
gboolean ignore_pending);
|
|
static PikaRectangleConstraint
|
|
pika_crop_tool_get_constraint (PikaCropTool *crop_tool);
|
|
|
|
static void pika_crop_tool_image_changed (PikaCropTool *crop_tool,
|
|
PikaImage *image,
|
|
PikaContext *context);
|
|
static void pika_crop_tool_image_size_changed (PikaCropTool *crop_tool);
|
|
static void pika_crop_tool_image_selected_layers_changed (PikaCropTool *crop_tool);
|
|
static void pika_crop_tool_layer_size_changed (PikaCropTool *crop_tool);
|
|
|
|
static void pika_crop_tool_auto_shrink (PikaCropTool *crop_tool);
|
|
|
|
|
|
G_DEFINE_TYPE (PikaCropTool, pika_crop_tool, PIKA_TYPE_DRAW_TOOL)
|
|
|
|
#define parent_class pika_crop_tool_parent_class
|
|
|
|
|
|
/* public functions */
|
|
|
|
void
|
|
pika_crop_tool_register (PikaToolRegisterCallback callback,
|
|
gpointer data)
|
|
{
|
|
(* callback) (PIKA_TYPE_CROP_TOOL,
|
|
PIKA_TYPE_CROP_OPTIONS,
|
|
pika_crop_options_gui,
|
|
PIKA_CONTEXT_PROP_MASK_FOREGROUND |
|
|
PIKA_CONTEXT_PROP_MASK_BACKGROUND |
|
|
PIKA_CONTEXT_PROP_MASK_PATTERN,
|
|
"pika-crop-tool",
|
|
_("Crop"),
|
|
_("Crop Tool: Remove edge areas from image or layer"),
|
|
N_("_Crop"), "<shift>C",
|
|
NULL, PIKA_HELP_TOOL_CROP,
|
|
PIKA_ICON_TOOL_CROP,
|
|
data);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_class_init (PikaCropToolClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
PikaToolClass *tool_class = PIKA_TOOL_CLASS (klass);
|
|
|
|
object_class->constructed = pika_crop_tool_constructed;
|
|
object_class->dispose = pika_crop_tool_dispose;
|
|
|
|
tool_class->control = pika_crop_tool_control;
|
|
tool_class->button_press = pika_crop_tool_button_press;
|
|
tool_class->button_release = pika_crop_tool_button_release;
|
|
tool_class->motion = pika_crop_tool_motion;
|
|
tool_class->options_notify = pika_crop_tool_options_notify;
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_init (PikaCropTool *crop_tool)
|
|
{
|
|
PikaTool *tool = PIKA_TOOL (crop_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_cursor (tool->control,
|
|
PIKA_CURSOR_CROSSHAIR_SMALL);
|
|
pika_tool_control_set_tool_cursor (tool->control,
|
|
PIKA_TOOL_CURSOR_CROP);
|
|
|
|
pika_draw_tool_set_default_status (PIKA_DRAW_TOOL (tool),
|
|
_("Click-Drag to draw a crop rectangle"));
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_constructed (GObject *object)
|
|
{
|
|
PikaCropTool *crop_tool = PIKA_CROP_TOOL (object);
|
|
PikaContext *context;
|
|
PikaToolInfo *tool_info;
|
|
|
|
G_OBJECT_CLASS (parent_class)->constructed (object);
|
|
|
|
tool_info = PIKA_TOOL (crop_tool)->tool_info;
|
|
|
|
context = pika_get_user_context (tool_info->pika);
|
|
|
|
g_signal_connect_object (context, "image-changed",
|
|
G_CALLBACK (pika_crop_tool_image_changed),
|
|
crop_tool,
|
|
G_CONNECT_SWAPPED);
|
|
|
|
/* Make sure we are connected to "size-changed" for the initial
|
|
* image.
|
|
*/
|
|
pika_crop_tool_image_changed (crop_tool,
|
|
pika_context_get_image (context),
|
|
context);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_dispose (GObject *object)
|
|
{
|
|
PikaCropTool *crop_tool = PIKA_CROP_TOOL (object);
|
|
|
|
/* Clean up current_image and current_layers. */
|
|
pika_crop_tool_image_changed (crop_tool, NULL, NULL);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_control (PikaTool *tool,
|
|
PikaToolAction action,
|
|
PikaDisplay *display)
|
|
{
|
|
PikaCropTool *crop_tool = PIKA_CROP_TOOL (tool);
|
|
|
|
switch (action)
|
|
{
|
|
case PIKA_TOOL_ACTION_PAUSE:
|
|
case PIKA_TOOL_ACTION_RESUME:
|
|
break;
|
|
|
|
case PIKA_TOOL_ACTION_HALT:
|
|
pika_crop_tool_halt (crop_tool);
|
|
break;
|
|
|
|
case PIKA_TOOL_ACTION_COMMIT:
|
|
pika_crop_tool_commit (crop_tool);
|
|
break;
|
|
}
|
|
|
|
PIKA_TOOL_CLASS (parent_class)->control (tool, action, display);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_button_press (PikaTool *tool,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaButtonPressType press_type,
|
|
PikaDisplay *display)
|
|
{
|
|
PikaCropTool *crop_tool = PIKA_CROP_TOOL (tool);
|
|
|
|
if (tool->display && display != tool->display)
|
|
pika_tool_control (tool, PIKA_TOOL_ACTION_HALT, tool->display);
|
|
|
|
if (! tool->display)
|
|
{
|
|
pika_crop_tool_start (crop_tool, display);
|
|
|
|
pika_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.
|
|
*/
|
|
pika_tool_rectangle_set_function (PIKA_TOOL_RECTANGLE (crop_tool->widget),
|
|
PIKA_TOOL_RECTANGLE_CREATING);
|
|
}
|
|
|
|
if (pika_tool_widget_button_press (crop_tool->widget, coords, time, state,
|
|
press_type))
|
|
{
|
|
crop_tool->grab_widget = crop_tool->widget;
|
|
}
|
|
|
|
pika_tool_control_activate (tool->control);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_button_release (PikaTool *tool,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaButtonReleaseType release_type,
|
|
PikaDisplay *display)
|
|
{
|
|
PikaCropTool *crop_tool = PIKA_CROP_TOOL (tool);
|
|
|
|
pika_tool_control_halt (tool->control);
|
|
|
|
if (crop_tool->grab_widget)
|
|
{
|
|
pika_tool_widget_button_release (crop_tool->grab_widget,
|
|
coords, time, state, release_type);
|
|
crop_tool->grab_widget = NULL;
|
|
}
|
|
|
|
pika_tool_push_status (tool, display, _("Click or press Enter to crop"));
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_motion (PikaTool *tool,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaDisplay *display)
|
|
{
|
|
PikaCropTool *crop_tool = PIKA_CROP_TOOL (tool);
|
|
|
|
if (crop_tool->grab_widget)
|
|
{
|
|
pika_tool_widget_motion (crop_tool->grab_widget, coords, time, state);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_options_notify (PikaTool *tool,
|
|
PikaToolOptions *options,
|
|
const GParamSpec *pspec)
|
|
{
|
|
PikaCropTool *crop_tool = PIKA_CROP_TOOL (tool);
|
|
|
|
if (! strcmp (pspec->name, "layer-only") ||
|
|
! strcmp (pspec->name, "allow-growing"))
|
|
{
|
|
if (crop_tool->widget)
|
|
{
|
|
pika_tool_rectangle_set_constraint (PIKA_TOOL_RECTANGLE (crop_tool->widget),
|
|
pika_crop_tool_get_constraint (crop_tool));
|
|
}
|
|
else
|
|
{
|
|
pika_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_rectangle_changed (PikaToolWidget *rectangle,
|
|
PikaCropTool *crop_tool)
|
|
{
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_rectangle_response (PikaToolWidget *rectangle,
|
|
gint response_id,
|
|
PikaCropTool *crop_tool)
|
|
{
|
|
PikaTool *tool = PIKA_TOOL (crop_tool);
|
|
|
|
switch (response_id)
|
|
{
|
|
case PIKA_TOOL_WIDGET_RESPONSE_CONFIRM:
|
|
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_crop_tool_rectangle_change_complete (PikaToolRectangle *rectangle,
|
|
PikaCropTool *crop_tool)
|
|
{
|
|
pika_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_start (PikaCropTool *crop_tool,
|
|
PikaDisplay *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"
|
|
};
|
|
|
|
PikaTool *tool = PIKA_TOOL (crop_tool);
|
|
PikaDisplayShell *shell = pika_display_get_shell (display);
|
|
PikaCropOptions *options = PIKA_CROP_TOOL_GET_OPTIONS (crop_tool);
|
|
PikaToolWidget *widget;
|
|
gint i;
|
|
|
|
tool->display = display;
|
|
|
|
crop_tool->widget = widget = pika_tool_rectangle_new (shell);
|
|
|
|
g_object_set (widget,
|
|
"status-title", _("Crop to: "),
|
|
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);
|
|
|
|
crop_tool->bindings = g_list_prepend (crop_tool->bindings, binding);
|
|
}
|
|
|
|
pika_rectangle_options_connect (PIKA_RECTANGLE_OPTIONS (options),
|
|
pika_display_get_image (shell->display),
|
|
G_CALLBACK (pika_crop_tool_auto_shrink),
|
|
crop_tool);
|
|
|
|
pika_tool_rectangle_set_constraint (PIKA_TOOL_RECTANGLE (widget),
|
|
pika_crop_tool_get_constraint (crop_tool));
|
|
|
|
g_signal_connect (widget, "changed",
|
|
G_CALLBACK (pika_crop_tool_rectangle_changed),
|
|
crop_tool);
|
|
g_signal_connect (widget, "response",
|
|
G_CALLBACK (pika_crop_tool_rectangle_response),
|
|
crop_tool);
|
|
g_signal_connect (widget, "change-complete",
|
|
G_CALLBACK (pika_crop_tool_rectangle_change_complete),
|
|
crop_tool);
|
|
|
|
pika_draw_tool_start (PIKA_DRAW_TOOL (tool), display);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_commit (PikaCropTool *crop_tool)
|
|
{
|
|
PikaTool *tool = PIKA_TOOL (crop_tool);
|
|
|
|
if (tool->display)
|
|
{
|
|
PikaCropOptions *options = PIKA_CROP_TOOL_GET_OPTIONS (tool);
|
|
PikaImage *image = pika_display_get_image (tool->display);
|
|
gdouble x, y;
|
|
gdouble x2, y2;
|
|
gint w, h;
|
|
|
|
pika_tool_rectangle_get_public_rect (PIKA_TOOL_RECTANGLE (crop_tool->widget),
|
|
&x, &y, &x2, &y2);
|
|
w = x2 - x;
|
|
h = y2 - y;
|
|
|
|
pika_tool_pop_status (tool, tool->display);
|
|
|
|
/* if rectangle exists, crop it */
|
|
if (w > 0 && h > 0)
|
|
{
|
|
if (options->layer_only)
|
|
{
|
|
GList *layers = pika_image_get_selected_layers (image);
|
|
GList *iter;
|
|
gint off_x, off_y;
|
|
gchar *undo_text;
|
|
|
|
if (! layers)
|
|
{
|
|
pika_tool_message_literal (tool, tool->display,
|
|
_("There are no selected layers to crop."));
|
|
return;
|
|
}
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
if (! pika_item_is_content_locked (PIKA_ITEM (iter->data), NULL))
|
|
break;
|
|
|
|
if (iter == NULL)
|
|
{
|
|
pika_tool_message_literal (tool, tool->display,
|
|
_("All selected layers' pixels are locked."));
|
|
pika_tools_blink_lock_box (tool->display->pika, PIKA_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));
|
|
pika_image_undo_group_start (image,
|
|
PIKA_UNDO_GROUP_IMAGE_CROP,
|
|
undo_text);
|
|
g_free (undo_text);
|
|
for (iter = layers; iter; iter = iter->next)
|
|
{
|
|
pika_item_get_offset (PIKA_ITEM (iter->data), &off_x, &off_y);
|
|
|
|
off_x -= x;
|
|
off_y -= y;
|
|
|
|
pika_item_resize (PIKA_ITEM (iter->data),
|
|
PIKA_CONTEXT (options), options->fill_type,
|
|
w, h, off_x, off_y);
|
|
}
|
|
pika_image_undo_group_end (image);
|
|
}
|
|
else
|
|
{
|
|
pika_image_crop (image,
|
|
PIKA_CONTEXT (options), PIKA_FILL_TRANSPARENT,
|
|
x, y, w, h, options->delete_pixels);
|
|
}
|
|
|
|
pika_image_flush (image);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_halt (PikaCropTool *crop_tool)
|
|
{
|
|
PikaTool *tool = PIKA_TOOL (crop_tool);
|
|
PikaCropOptions *options = PIKA_CROP_TOOL_GET_OPTIONS (crop_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_crop_tool_auto_shrink),
|
|
crop_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 (crop_tool->bindings, (GDestroyNotify) g_object_unref);
|
|
crop_tool->bindings = NULL;
|
|
|
|
pika_draw_tool_set_widget (PIKA_DRAW_TOOL (tool), NULL);
|
|
g_clear_object (&crop_tool->widget);
|
|
|
|
tool->display = NULL;
|
|
g_list_free (tool->drawables);
|
|
tool->drawables = NULL;
|
|
|
|
pika_crop_tool_update_option_defaults (crop_tool, TRUE);
|
|
}
|
|
|
|
/**
|
|
* pika_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
|
|
pika_crop_tool_update_option_defaults (PikaCropTool *crop_tool,
|
|
gboolean ignore_pending)
|
|
{
|
|
PikaTool *tool = PIKA_TOOL (crop_tool);
|
|
PikaToolRectangle *rectangle = PIKA_TOOL_RECTANGLE (crop_tool->widget);
|
|
PikaRectangleOptions *options;
|
|
|
|
options = PIKA_RECTANGLE_OPTIONS (PIKA_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.
|
|
*/
|
|
|
|
pika_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
|
|
* pika_tool_rectangle_constraint_size_set().
|
|
*/
|
|
|
|
PikaContext *context = pika_get_user_context (tool->tool_info->pika);
|
|
PikaDisplay *display = pika_context_get_display (context);
|
|
|
|
if (display)
|
|
{
|
|
PikaDisplayShell *shell = pika_display_get_shell (display);
|
|
|
|
rectangle = PIKA_TOOL_RECTANGLE (pika_tool_rectangle_new (shell));
|
|
|
|
pika_tool_rectangle_set_constraint (
|
|
rectangle, pika_crop_tool_get_constraint (crop_tool));
|
|
}
|
|
}
|
|
|
|
if (rectangle)
|
|
{
|
|
pika_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 PikaRectangleConstraint
|
|
pika_crop_tool_get_constraint (PikaCropTool *crop_tool)
|
|
{
|
|
PikaCropOptions *crop_options = PIKA_CROP_TOOL_GET_OPTIONS (crop_tool);
|
|
|
|
if (crop_options->allow_growing)
|
|
{
|
|
return PIKA_RECTANGLE_CONSTRAIN_NONE;
|
|
}
|
|
else
|
|
{
|
|
return crop_options->layer_only ? PIKA_RECTANGLE_CONSTRAIN_DRAWABLE :
|
|
PIKA_RECTANGLE_CONSTRAIN_IMAGE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_image_changed (PikaCropTool *crop_tool,
|
|
PikaImage *image,
|
|
PikaContext *context)
|
|
{
|
|
if (crop_tool->current_image)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (crop_tool->current_image,
|
|
pika_crop_tool_image_size_changed,
|
|
NULL);
|
|
g_signal_handlers_disconnect_by_func (crop_tool->current_image,
|
|
pika_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 (pika_crop_tool_image_size_changed),
|
|
crop_tool,
|
|
G_CONNECT_SWAPPED);
|
|
g_signal_connect_object (crop_tool->current_image, "selected-layers-changed",
|
|
G_CALLBACK (pika_crop_tool_image_selected_layers_changed),
|
|
crop_tool,
|
|
G_CONNECT_SWAPPED);
|
|
}
|
|
|
|
/* Make sure we are connected to "size-changed" for the initial
|
|
* layer.
|
|
*/
|
|
pika_crop_tool_image_selected_layers_changed (crop_tool);
|
|
|
|
pika_crop_tool_update_option_defaults (PIKA_CROP_TOOL (crop_tool), FALSE);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_image_size_changed (PikaCropTool *crop_tool)
|
|
{
|
|
pika_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_image_selected_layers_changed (PikaCropTool *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,
|
|
pika_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 = pika_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 (pika_crop_tool_layer_size_changed),
|
|
crop_tool,
|
|
G_CONNECT_SWAPPED);
|
|
}
|
|
}
|
|
|
|
pika_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_layer_size_changed (PikaCropTool *crop_tool)
|
|
{
|
|
pika_crop_tool_update_option_defaults (crop_tool, FALSE);
|
|
}
|
|
|
|
static void
|
|
pika_crop_tool_auto_shrink (PikaCropTool *crop_tool)
|
|
{
|
|
gboolean shrink_merged ;
|
|
|
|
g_object_get (pika_tool_get_options (PIKA_TOOL (crop_tool)),
|
|
"shrink-merged", &shrink_merged,
|
|
NULL);
|
|
|
|
pika_tool_rectangle_auto_shrink (PIKA_TOOL_RECTANGLE (crop_tool->widget),
|
|
shrink_merged);
|
|
}
|