Gimp/app/tools/gimpsheartool.c
Mikael Magnusson f5b08f33aa transformtool: Infinite undo
add undo and redo buttons, can undo all interactions. The reset button
is equivalent to undoing all operations and lets you press redo to get
back to before you reset. Doing something after undo will of course
clear all redo events.
2012-08-20 15:22:21 +02:00

314 lines
9.7 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 <stdlib.h>
#include <string.h>
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "tools-types.h"
#include "core/gimp-transform-utils.h"
#include "widgets/gimphelp-ids.h"
#include "gimpsheartool.h"
#include "gimptoolcontrol.h"
#include "gimptransformoptions.h"
#include "gimp-intl.h"
/* index into trans_info array */
enum
{
HORZ_OR_VERT,
XSHEAR,
YSHEAR
};
/* the minimum movement before direction of shear can be determined (pixels) */
#define MIN_MOVE 5
#define SB_WIDTH 10
/* local function prototypes */
static void gimp_shear_tool_dialog (GimpTransformTool *tr_tool);
static void gimp_shear_tool_dialog_update (GimpTransformTool *tr_tool);
static void gimp_shear_tool_prepare (GimpTransformTool *tr_tool);
static void gimp_shear_tool_motion (GimpTransformTool *tr_tool);
static void gimp_shear_tool_recalc_matrix (GimpTransformTool *tr_tool);
static gchar * gimp_shear_tool_get_undo_desc (GimpTransformTool *tr_tool);
static void shear_x_mag_changed (GtkAdjustment *adj,
GimpTransformTool *tr_tool);
static void shear_y_mag_changed (GtkAdjustment *adj,
GimpTransformTool *tr_tool);
G_DEFINE_TYPE (GimpShearTool, gimp_shear_tool, GIMP_TYPE_TRANSFORM_TOOL)
void
gimp_shear_tool_register (GimpToolRegisterCallback callback,
gpointer data)
{
(* callback) (GIMP_TYPE_SHEAR_TOOL,
GIMP_TYPE_TRANSFORM_OPTIONS,
gimp_transform_options_gui,
0,
"gimp-shear-tool",
_("Shear"),
_("Shear Tool: Shear the layer, selection or path"),
N_("S_hear"), "<shift>S",
NULL, GIMP_HELP_TOOL_SHEAR,
GIMP_STOCK_TOOL_SHEAR,
data);
}
static void
gimp_shear_tool_class_init (GimpShearToolClass *klass)
{
GimpTransformToolClass *trans_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
trans_class->dialog = gimp_shear_tool_dialog;
trans_class->dialog_update = gimp_shear_tool_dialog_update;
trans_class->prepare = gimp_shear_tool_prepare;
trans_class->motion = gimp_shear_tool_motion;
trans_class->recalc_matrix = gimp_shear_tool_recalc_matrix;
trans_class->get_undo_desc = gimp_shear_tool_get_undo_desc;
}
static void
gimp_shear_tool_init (GimpShearTool *shear_tool)
{
GimpTool *tool = GIMP_TOOL (shear_tool);
GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (shear_tool);
gimp_tool_control_set_tool_cursor (tool->control, GIMP_TOOL_CURSOR_SHEAR);
tr_tool->progress_text = _("Shearing");
tr_tool->use_grid = TRUE;
}
static void
gimp_shear_tool_dialog (GimpTransformTool *tr_tool)
{
GimpShearTool *shear = GIMP_SHEAR_TOOL (tr_tool);
GtkWidget *table;
GtkWidget *button;
table = gtk_table_new (2, 2, FALSE);
gtk_container_set_border_width (GTK_CONTAINER (table), 6);
gtk_table_set_row_spacings (GTK_TABLE (table), 2);
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (tr_tool->dialog))),
table, FALSE, FALSE, 0);
gtk_widget_show (table);
button = gimp_spin_button_new ((GtkObject **) &shear->x_adj,
0, -65536, 65536, 1, 15, 0, 1, 0);
gtk_entry_set_width_chars (GTK_ENTRY (button), SB_WIDTH);
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, _("Shear magnitude _X:"),
0.0, 0.5, button, 1, TRUE);
g_signal_connect (shear->x_adj, "value-changed",
G_CALLBACK (shear_x_mag_changed),
tr_tool);
button = gimp_spin_button_new ((GtkObject **) &shear->y_adj,
0, -65536, 65536, 1, 15, 0, 1, 0);
gtk_entry_set_width_chars (GTK_ENTRY (button), SB_WIDTH);
gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("Shear magnitude _Y:"),
0.0, 0.5, button, 1, TRUE);
g_signal_connect (shear->y_adj, "value-changed",
G_CALLBACK (shear_y_mag_changed),
tr_tool);
}
static void
gimp_shear_tool_dialog_update (GimpTransformTool *tr_tool)
{
GimpShearTool *shear = GIMP_SHEAR_TOOL (tr_tool);
gtk_adjustment_set_value (shear->x_adj, tr_tool->trans_info[XSHEAR]);
gtk_adjustment_set_value (shear->y_adj, tr_tool->trans_info[YSHEAR]);
}
static void
gimp_shear_tool_prepare (GimpTransformTool *tr_tool)
{
tr_tool->trans_info[HORZ_OR_VERT] = GIMP_ORIENTATION_UNKNOWN;
tr_tool->trans_info[XSHEAR] = 0.0;
tr_tool->trans_info[YSHEAR] = 0.0;
}
static void
gimp_shear_tool_motion (GimpTransformTool *tr_tool)
{
gdouble diffx = tr_tool->curx - tr_tool->lastx;
gdouble diffy = tr_tool->cury - tr_tool->lasty;
/* If we haven't yet decided on which way to control shearing
* decide using the maximum differential
*/
if (tr_tool->trans_info[HORZ_OR_VERT] == GIMP_ORIENTATION_UNKNOWN)
{
if (abs (diffx) > MIN_MOVE || abs (diffy) > MIN_MOVE)
{
if (abs (diffx) > abs (diffy))
{
tr_tool->trans_info[HORZ_OR_VERT] = GIMP_ORIENTATION_HORIZONTAL;
tr_tool->trans_info[XSHEAR] = 0.0;
}
else
{
tr_tool->trans_info[HORZ_OR_VERT] = GIMP_ORIENTATION_VERTICAL;
tr_tool->trans_info[XSHEAR] = 0.0;
}
}
/* set the current coords to the last ones */
else
{
tr_tool->curx = tr_tool->lastx;
tr_tool->cury = tr_tool->lasty;
}
}
/* if the direction is known, keep track of the magnitude */
if (tr_tool->trans_info[HORZ_OR_VERT] == GIMP_ORIENTATION_HORIZONTAL)
{
if (tr_tool->cury > (tr_tool->ty1 + tr_tool->ty3) / 2)
tr_tool->trans_info[XSHEAR] += diffx;
else
tr_tool->trans_info[XSHEAR] -= diffx;
}
else if (tr_tool->trans_info[HORZ_OR_VERT] == GIMP_ORIENTATION_VERTICAL)
{
if (tr_tool->curx > (tr_tool->tx1 + tr_tool->tx2) / 2)
tr_tool->trans_info[YSHEAR] += diffy;
else
tr_tool->trans_info[YSHEAR] -= diffy;
}
}
static void
gimp_shear_tool_recalc_matrix (GimpTransformTool *tr_tool)
{
gdouble amount;
if (tr_tool->trans_info[XSHEAR] == 0.0 &&
tr_tool->trans_info[YSHEAR] == 0.0)
{
tr_tool->trans_info[HORZ_OR_VERT] = GIMP_ORIENTATION_UNKNOWN;
}
if (tr_tool->trans_info[HORZ_OR_VERT] == GIMP_ORIENTATION_HORIZONTAL)
amount = tr_tool->trans_info[XSHEAR];
else
amount = tr_tool->trans_info[YSHEAR];
gimp_matrix3_identity (&tr_tool->transform);
gimp_transform_matrix_shear (&tr_tool->transform,
tr_tool->x1,
tr_tool->y1,
tr_tool->x2 - tr_tool->x1,
tr_tool->y2 - tr_tool->y1,
tr_tool->trans_info[HORZ_OR_VERT],
amount);
}
static gchar *
gimp_shear_tool_get_undo_desc (GimpTransformTool *tr_tool)
{
gdouble x = tr_tool->trans_info[XSHEAR];
gdouble y = tr_tool->trans_info[YSHEAR];
switch ((gint) tr_tool->trans_info[HORZ_OR_VERT])
{
case GIMP_ORIENTATION_HORIZONTAL:
return g_strdup_printf (C_("undo-type", "Shear horizontally by %-3.3g"),
x);
case GIMP_ORIENTATION_VERTICAL:
return g_strdup_printf (C_("undo-type", "Shear vertically by %-3.3g"),
y);
default:
/* e.g. user entered numbers but no notification callback */
return g_strdup_printf (C_("undo-type", "Shear horizontally by %-3.3g, vertically by %-3.3g"),
x, y);
}
}
static void
shear_x_mag_changed (GtkAdjustment *adj,
GimpTransformTool *tr_tool)
{
gdouble value = gtk_adjustment_get_value (adj);
if (value != tr_tool->trans_info[XSHEAR])
{
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tr_tool));
if (tr_tool->trans_info[HORZ_OR_VERT] == GIMP_ORIENTATION_UNKNOWN)
tr_tool->trans_info[HORZ_OR_VERT] = GIMP_ORIENTATION_HORIZONTAL;
tr_tool->trans_info[XSHEAR] = value;
gimp_transform_tool_push_internal_undo (tr_tool);
gimp_transform_tool_recalc_matrix (tr_tool);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tr_tool));
}
}
static void
shear_y_mag_changed (GtkAdjustment *adj,
GimpTransformTool *tr_tool)
{
gdouble value = gtk_adjustment_get_value (adj);
if (value != tr_tool->trans_info[YSHEAR])
{
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tr_tool));
if (tr_tool->trans_info[HORZ_OR_VERT] == GIMP_ORIENTATION_UNKNOWN)
tr_tool->trans_info[HORZ_OR_VERT] = GIMP_ORIENTATION_VERTICAL;
tr_tool->trans_info[YSHEAR] = value;
gimp_transform_tool_push_internal_undo (tr_tool);
gimp_transform_tool_recalc_matrix (tr_tool);
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tr_tool));
}
}