This option behaves similarly to the other transform tool, however it's limited to "adjust" and "clip" only. Now that the flip tool can reflect across guides, this option is meaningful.
422 lines
14 KiB
C
422 lines
14 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "tools-types.h"
|
|
|
|
#include "config/gimpdisplayconfig.h"
|
|
|
|
#include "core/gimpdrawable-transform.h"
|
|
#include "core/gimpguide.h"
|
|
#include "core/gimpimage.h"
|
|
#include "core/gimpimage-guides.h"
|
|
#include "core/gimpitem-linked.h"
|
|
#include "core/gimplayer.h"
|
|
#include "core/gimplayermask.h"
|
|
#include "core/gimppickable.h"
|
|
|
|
#include "widgets/gimphelp-ids.h"
|
|
#include "widgets/gimpwidgets-utils.h"
|
|
|
|
#include "display/gimpcanvasitem.h"
|
|
#include "display/gimpdisplay.h"
|
|
#include "display/gimpdisplayshell.h"
|
|
#include "display/gimpdisplayshell-appearance.h"
|
|
|
|
#include "gimpflipoptions.h"
|
|
#include "gimpfliptool.h"
|
|
#include "gimptoolcontrol.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
static GimpDisplay * gimp_flip_tool_has_image (GimpTool *tool,
|
|
GimpImage *image);
|
|
static void gimp_flip_tool_modifier_key (GimpTool *tool,
|
|
GdkModifierType key,
|
|
gboolean press,
|
|
GdkModifierType state,
|
|
GimpDisplay *display);
|
|
static void gimp_flip_tool_oper_update (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
gboolean proximity,
|
|
GimpDisplay *display);
|
|
static void gimp_flip_tool_cursor_update (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
GimpDisplay *display);
|
|
|
|
static void gimp_flip_tool_draw (GimpDrawTool *draw_tool);
|
|
|
|
static gchar * gimp_flip_tool_get_undo_desc (GimpTransformTool *tool);
|
|
static GeglBuffer * gimp_flip_tool_transform (GimpTransformTool *tool,
|
|
GimpItem *item,
|
|
GeglBuffer *orig_buffer,
|
|
gint orig_offset_x,
|
|
gint orig_offset_y,
|
|
GimpColorProfile **buffer_profile,
|
|
gint *new_offset_x,
|
|
gint *new_offset_y);
|
|
|
|
static GimpOrientationType gimp_flip_tool_get_flip_type (GimpFlipTool *flip);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpFlipTool, gimp_flip_tool, GIMP_TYPE_TRANSFORM_TOOL)
|
|
|
|
#define parent_class gimp_flip_tool_parent_class
|
|
|
|
|
|
void
|
|
gimp_flip_tool_register (GimpToolRegisterCallback callback,
|
|
gpointer data)
|
|
{
|
|
(* callback) (GIMP_TYPE_FLIP_TOOL,
|
|
GIMP_TYPE_FLIP_OPTIONS,
|
|
gimp_flip_options_gui,
|
|
GIMP_CONTEXT_PROP_MASK_BACKGROUND,
|
|
"gimp-flip-tool",
|
|
_("Flip"),
|
|
_("Flip Tool: "
|
|
"Reverse the layer, selection or path horizontally or vertically"),
|
|
N_("_Flip"), "<shift>F",
|
|
NULL, GIMP_HELP_TOOL_FLIP,
|
|
GIMP_ICON_TOOL_FLIP,
|
|
data);
|
|
}
|
|
|
|
static void
|
|
gimp_flip_tool_class_init (GimpFlipToolClass *klass)
|
|
{
|
|
GimpToolClass *tool_class = GIMP_TOOL_CLASS (klass);
|
|
GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
|
|
GimpTransformToolClass *trans_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
|
|
|
|
tool_class->has_image = gimp_flip_tool_has_image;
|
|
tool_class->modifier_key = gimp_flip_tool_modifier_key;
|
|
tool_class->oper_update = gimp_flip_tool_oper_update;
|
|
tool_class->cursor_update = gimp_flip_tool_cursor_update;
|
|
|
|
draw_tool_class->draw = gimp_flip_tool_draw;
|
|
|
|
trans_class->get_undo_desc = gimp_flip_tool_get_undo_desc;
|
|
trans_class->transform = gimp_flip_tool_transform;
|
|
|
|
trans_class->ok_button_label = _("_Flip");
|
|
}
|
|
|
|
static void
|
|
gimp_flip_tool_init (GimpFlipTool *flip_tool)
|
|
{
|
|
GimpTool *tool = GIMP_TOOL (flip_tool);
|
|
|
|
gimp_tool_control_set_snap_to (tool->control, FALSE);
|
|
gimp_tool_control_set_precision (tool->control,
|
|
GIMP_CURSOR_PRECISION_PIXEL_CENTER);
|
|
gimp_tool_control_set_cursor (tool->control, GIMP_CURSOR_MOUSE);
|
|
gimp_tool_control_set_toggle_cursor (tool->control, GIMP_CURSOR_MOUSE);
|
|
gimp_tool_control_set_tool_cursor (tool->control,
|
|
GIMP_TOOL_CURSOR_FLIP_HORIZONTAL);
|
|
gimp_tool_control_set_toggle_tool_cursor (tool->control,
|
|
GIMP_TOOL_CURSOR_FLIP_VERTICAL);
|
|
|
|
flip_tool->guide = NULL;
|
|
}
|
|
|
|
static GimpDisplay *
|
|
gimp_flip_tool_has_image (GimpTool *tool,
|
|
GimpImage *image)
|
|
{
|
|
/* avoid comitting, and hence flipping, when changing tools */
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
gimp_flip_tool_modifier_key (GimpTool *tool,
|
|
GdkModifierType key,
|
|
gboolean press,
|
|
GdkModifierType state,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpFlipOptions *options = GIMP_FLIP_TOOL_GET_OPTIONS (tool);
|
|
|
|
if (key == gimp_get_toggle_behavior_mask ())
|
|
{
|
|
switch (options->flip_type)
|
|
{
|
|
case GIMP_ORIENTATION_HORIZONTAL:
|
|
g_object_set (options,
|
|
"flip-type", GIMP_ORIENTATION_VERTICAL,
|
|
NULL);
|
|
break;
|
|
|
|
case GIMP_ORIENTATION_VERTICAL:
|
|
g_object_set (options,
|
|
"flip-type", GIMP_ORIENTATION_HORIZONTAL,
|
|
NULL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_flip_tool_oper_update (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
gboolean proximity,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpFlipTool *flip = GIMP_FLIP_TOOL (tool);
|
|
GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool);
|
|
GimpFlipOptions *options = GIMP_FLIP_TOOL_GET_OPTIONS (tool);
|
|
GimpDisplayShell *shell = gimp_display_get_shell (display);
|
|
GimpImage *image = gimp_display_get_image (display);
|
|
GimpGuide *guide = NULL;
|
|
|
|
if (gimp_display_shell_get_show_guides (shell) &&
|
|
proximity)
|
|
{
|
|
gint snap_distance = display->config->snap_distance;
|
|
|
|
guide = gimp_image_find_guide (image, coords->x, coords->y,
|
|
FUNSCALEX (shell, snap_distance),
|
|
FUNSCALEY (shell, snap_distance));
|
|
}
|
|
|
|
if (flip->guide != guide ||
|
|
(guide && ! gimp_draw_tool_is_active (draw_tool)))
|
|
{
|
|
gimp_draw_tool_pause (draw_tool);
|
|
|
|
if (gimp_draw_tool_is_active (draw_tool) &&
|
|
draw_tool->display != display)
|
|
gimp_draw_tool_stop (draw_tool);
|
|
|
|
flip->guide = guide;
|
|
|
|
if (! gimp_draw_tool_is_active (draw_tool))
|
|
gimp_draw_tool_start (draw_tool, display);
|
|
|
|
gimp_draw_tool_resume (draw_tool);
|
|
}
|
|
|
|
gtk_widget_set_sensitive (options->direction_frame, guide == NULL);
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->oper_update (tool,
|
|
coords, state, proximity,
|
|
display);
|
|
}
|
|
|
|
static void
|
|
gimp_flip_tool_cursor_update (GimpTool *tool,
|
|
const GimpCoords *coords,
|
|
GdkModifierType state,
|
|
GimpDisplay *display)
|
|
{
|
|
GimpFlipTool *flip = GIMP_FLIP_TOOL (tool);
|
|
|
|
gimp_tool_control_set_toggled (tool->control,
|
|
gimp_flip_tool_get_flip_type (flip) ==
|
|
GIMP_ORIENTATION_VERTICAL);
|
|
|
|
GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
|
|
}
|
|
|
|
static void
|
|
gimp_flip_tool_draw (GimpDrawTool *draw_tool)
|
|
{
|
|
GimpFlipTool *flip = GIMP_FLIP_TOOL (draw_tool);
|
|
|
|
if (flip->guide)
|
|
{
|
|
GimpCanvasItem *item;
|
|
GimpGuideStyle style;
|
|
|
|
style = gimp_guide_get_style (flip->guide);
|
|
|
|
item = gimp_draw_tool_add_guide (draw_tool,
|
|
gimp_guide_get_orientation (flip->guide),
|
|
gimp_guide_get_position (flip->guide),
|
|
style);
|
|
gimp_canvas_item_set_highlight (item, TRUE);
|
|
}
|
|
}
|
|
|
|
static gchar *
|
|
gimp_flip_tool_get_undo_desc (GimpTransformTool *tr_tool)
|
|
{
|
|
GimpFlipTool *flip = GIMP_FLIP_TOOL (tr_tool);
|
|
|
|
switch (gimp_flip_tool_get_flip_type (flip))
|
|
{
|
|
case GIMP_ORIENTATION_HORIZONTAL:
|
|
return g_strdup (C_("undo-type", "Flip horizontally"));
|
|
|
|
case GIMP_ORIENTATION_VERTICAL:
|
|
return g_strdup (C_("undo-type", "Flip vertically"));
|
|
|
|
default:
|
|
/* probably this is not actually reached today, but
|
|
* could be if someone defined FLIP_DIAGONAL, say...
|
|
*/
|
|
return g_strdup (C_("undo-desc", "Flip"));
|
|
}
|
|
}
|
|
|
|
static GeglBuffer *
|
|
gimp_flip_tool_transform (GimpTransformTool *trans_tool,
|
|
GimpItem *active_item,
|
|
GeglBuffer *orig_buffer,
|
|
gint orig_offset_x,
|
|
gint orig_offset_y,
|
|
GimpColorProfile **buffer_profile,
|
|
gint *new_offset_x,
|
|
gint *new_offset_y)
|
|
{
|
|
GimpFlipTool *flip = GIMP_FLIP_TOOL (trans_tool);
|
|
GimpFlipOptions *options = GIMP_FLIP_TOOL_GET_OPTIONS (trans_tool);
|
|
GimpTransformOptions *tr_options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (trans_tool);
|
|
GimpContext *context = GIMP_CONTEXT (options);
|
|
GimpOrientationType flip_type = GIMP_ORIENTATION_UNKNOWN;
|
|
gdouble axis = 0.0;
|
|
gboolean clip_result = FALSE;
|
|
GeglBuffer *ret = NULL;
|
|
|
|
flip_type = gimp_flip_tool_get_flip_type (flip);
|
|
|
|
if (flip->guide)
|
|
{
|
|
axis = gimp_guide_get_position (flip->guide);
|
|
}
|
|
else
|
|
{
|
|
switch (flip_type)
|
|
{
|
|
case GIMP_ORIENTATION_HORIZONTAL:
|
|
axis = ((gdouble) trans_tool->x1 +
|
|
(gdouble) (trans_tool->x2 - trans_tool->x1) / 2.0);
|
|
break;
|
|
|
|
case GIMP_ORIENTATION_VERTICAL:
|
|
axis = ((gdouble) trans_tool->y1 +
|
|
(gdouble) (trans_tool->y2 - trans_tool->y1) / 2.0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (tr_options->clip)
|
|
{
|
|
case GIMP_TRANSFORM_RESIZE_ADJUST:
|
|
clip_result = FALSE;
|
|
break;
|
|
|
|
case GIMP_TRANSFORM_RESIZE_CLIP:
|
|
clip_result = TRUE;
|
|
break;
|
|
|
|
default:
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
|
|
if (orig_buffer)
|
|
{
|
|
/* this happens when transforming a selection cut out of a
|
|
* normal drawable, or the selection
|
|
*/
|
|
|
|
/* always clip the selction and unfloated channels
|
|
* so they keep their size
|
|
*/
|
|
if (GIMP_IS_CHANNEL (active_item) &&
|
|
! babl_format_has_alpha (gegl_buffer_get_format (orig_buffer)))
|
|
clip_result = TRUE;
|
|
|
|
ret = gimp_drawable_transform_buffer_flip (GIMP_DRAWABLE (active_item),
|
|
context,
|
|
orig_buffer,
|
|
orig_offset_x,
|
|
orig_offset_y,
|
|
flip_type, axis,
|
|
clip_result,
|
|
buffer_profile,
|
|
new_offset_x,
|
|
new_offset_y);
|
|
}
|
|
else
|
|
{
|
|
/* this happens for entire drawables, paths and layer groups */
|
|
|
|
/* always clip layer masks so they keep their size
|
|
*/
|
|
if (GIMP_IS_CHANNEL (active_item))
|
|
clip_result = TRUE;
|
|
|
|
if (gimp_item_get_linked (active_item))
|
|
{
|
|
gimp_item_linked_flip (active_item, context,
|
|
flip_type, axis, clip_result);
|
|
}
|
|
else
|
|
{
|
|
gimp_item_flip (active_item, context,
|
|
flip_type, axis, clip_result);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GimpOrientationType
|
|
gimp_flip_tool_get_flip_type (GimpFlipTool *flip)
|
|
{
|
|
GimpFlipOptions *options = GIMP_FLIP_TOOL_GET_OPTIONS (flip);
|
|
|
|
if (flip->guide)
|
|
{
|
|
switch (gimp_guide_get_orientation (flip->guide))
|
|
{
|
|
case GIMP_ORIENTATION_HORIZONTAL:
|
|
return GIMP_ORIENTATION_VERTICAL;
|
|
|
|
case GIMP_ORIENTATION_VERTICAL:
|
|
return GIMP_ORIENTATION_HORIZONTAL;
|
|
|
|
default:
|
|
return gimp_guide_get_orientation (flip->guide);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return options->flip_type;
|
|
}
|
|
}
|