gimptransformtool.c 46.4 KB
Newer Older
Nate Summers's avatar
Nate Summers committed
1
/* The GIMP -- an image manipulation program
Nate Summers's avatar
Nate Summers committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include <stdlib.h>

#include <gtk/gtk.h>
24
#include <gdk/gdkkeysyms.h>
Nate Summers's avatar
Nate Summers committed
25 26 27 28

#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"

Michael Natterer's avatar
Michael Natterer committed
29
#include "tools-types.h"
30

Michael Natterer's avatar
Michael Natterer committed
31
#include "base/tile-manager.h"
32

Michael Natterer's avatar
Michael Natterer committed
33
#include "core/gimp.h"
34
#include "core/gimpchannel.h"
35
#include "core/gimpcontext.h"
Michael Natterer's avatar
Michael Natterer committed
36
#include "core/gimpdrawable-transform.h"
37
#include "core/gimpimage.h"
38
#include "core/gimpimage-undo.h"
39
#include "core/gimpimage-undo-push.h"
40
#include "core/gimpitem-linked.h"
41
#include "core/gimplayer.h"
Michael Natterer's avatar
Michael Natterer committed
42
#include "core/gimptoolinfo.h"
43

44 45 46
#include "vectors/gimpvectors.h"
#include "vectors/gimpstroke.h"

47
#include "widgets/gimpdialogfactory.h"
Michael Natterer's avatar
Michael Natterer committed
48 49
#include "widgets/gimpviewabledialog.h"

50
#include "display/gimpdisplay.h"
Michael Natterer's avatar
Michael Natterer committed
51
#include "display/gimpprogress.h"
Nate Summers's avatar
Nate Summers committed
52

53 54 55 56
#ifdef __GNUC__
#warning FIXME #include "gui/gui-types.h"
#endif
#include "gui/gui-types.h"
57 58
#include "gui/info-dialog.h"

59
#include "gimptoolcontrol.h"
60
#include "gimptransformoptions.h"
61 62
#include "gimptransformtool.h"
#include "gimptransformtool-undo.h"
63

64
#include "gimp-intl.h"
Nate Summers's avatar
Nate Summers committed
65

66

67
#define HANDLE_SIZE 10
Michael Natterer's avatar
Michael Natterer committed
68

69

Michael Natterer's avatar
Michael Natterer committed
70 71 72 73 74
/*  local function prototypes  */

static void   gimp_transform_tool_init        (GimpTransformTool      *tool);
static void   gimp_transform_tool_class_init  (GimpTransformToolClass *tool);

75 76 77
static GObject * gimp_transform_tool_constructor   (GType              type,
                                                    guint              n_params,
                                                    GObjectConstructParam *params);
78
static void     gimp_transform_tool_finalize       (GObject           *object);
Michael Natterer's avatar
Michael Natterer committed
79

80
static gboolean gimp_transform_tool_initialize     (GimpTool          *tool,
81
                                                    GimpDisplay       *gdisp);
82
static void     gimp_transform_tool_control        (GimpTool          *tool,
83
                                                    GimpToolAction     action,
Michael Natterer's avatar
Michael Natterer committed
84
                                                    GimpDisplay       *gdisp);
85
static void     gimp_transform_tool_button_press   (GimpTool          *tool,
Michael Natterer's avatar
Michael Natterer committed
86 87 88 89
                                                    GimpCoords        *coords,
                                                    guint32            time,
                                                    GdkModifierType    state,
                                                    GimpDisplay       *gdisp);
90
static void     gimp_transform_tool_button_release (GimpTool          *tool,
Michael Natterer's avatar
Michael Natterer committed
91 92 93 94
                                                    GimpCoords        *coords,
                                                    guint32            time,
                                                    GdkModifierType    state,
                                                    GimpDisplay       *gdisp);
95
static void     gimp_transform_tool_motion         (GimpTool          *tool,
Michael Natterer's avatar
Michael Natterer committed
96 97 98 99
                                                    GimpCoords        *coords,
                                                    guint32            time,
                                                    GdkModifierType    state,
                                                    GimpDisplay       *gdisp);
100 101 102
static void     gimp_transform_tool_key_press      (GimpTool          *tool,
                                                    GdkEventKey       *kevent,
                                                    GimpDisplay       *gdisp);
103
static void     gimp_transform_tool_modifier_key   (GimpTool          *tool,
104 105 106 107
                                                    GdkModifierType    key,
                                                    gboolean           press,
                                                    GdkModifierType    state,
                                                    GimpDisplay       *gdisp);
108
static void     gimp_transform_tool_oper_update    (GimpTool          *tool,
109 110 111
                                                    GimpCoords        *coords,
                                                    GdkModifierType    state,
                                                    GimpDisplay       *gdisp);
112
static void     gimp_transform_tool_cursor_update  (GimpTool          *tool,
Michael Natterer's avatar
Michael Natterer committed
113 114 115 116
                                                    GimpCoords        *coords,
                                                    GdkModifierType    state,
                                                    GimpDisplay       *gdisp);

117
static void     gimp_transform_tool_draw           (GimpDrawTool      *draw_tool);
Michael Natterer's avatar
Michael Natterer committed
118

119
static TileManager *
120
                gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
121
                                                    GimpItem          *item,
122 123
                                                    GimpDisplay       *gdisp);

124 125
static void     gimp_transform_tool_halt           (GimpTransformTool *tr_tool);
static void     gimp_transform_tool_bounds         (GimpTransformTool *tr_tool,
Michael Natterer's avatar
Michael Natterer committed
126
                                                    GimpDisplay       *gdisp);
127 128
static void     gimp_transform_tool_dialog         (GimpTransformTool *tr_tool);
static void     gimp_transform_tool_prepare        (GimpTransformTool *tr_tool,
Michael Natterer's avatar
Michael Natterer committed
129
                                                    GimpDisplay       *gdisp);
130
static void     gimp_transform_tool_recalc         (GimpTransformTool *tr_tool,
Michael Natterer's avatar
Michael Natterer committed
131
                                                    GimpDisplay       *gdisp);
132
static void     gimp_transform_tool_doit           (GimpTransformTool *tr_tool,
Michael Natterer's avatar
Michael Natterer committed
133
                                                    GimpDisplay       *gdisp);
134
static void     gimp_transform_tool_grid_recalc    (GimpTransformTool *tr_tool);
Michael Natterer's avatar
Michael Natterer committed
135

136
static void     gimp_transform_tool_response       (GtkWidget         *widget,
137
                                                    gint               response_id,
Michael Natterer's avatar
Michael Natterer committed
138
                                                    GimpTransformTool *tr_tool);
139

140
static void     gimp_transform_tool_notify_type (GimpTransformOptions *options,
141 142
                                                 GParamSpec           *pspec,
                                                 GimpTransformTool    *tr_tool);
143
static void     gimp_transform_tool_notify_grid (GimpTransformOptions *options,
144 145 146
                                                 GParamSpec           *pspec,
                                                 GimpTransformTool    *tr_tool);

147

148
static GimpDrawToolClass *parent_class = NULL;
149

150 151

GType
Nate Summers's avatar
Nate Summers committed
152 153
gimp_transform_tool_get_type (void)
{
154
  static GType tool_type = 0;
Nate Summers's avatar
Nate Summers committed
155 156 157

  if (! tool_type)
    {
158
      static const GTypeInfo tool_info =
Nate Summers's avatar
Nate Summers committed
159 160
      {
        sizeof (GimpTransformToolClass),
161 162 163 164 165 166 167 168
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_transform_tool_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data     */
	sizeof (GimpTransformTool),
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_transform_tool_init,
Nate Summers's avatar
Nate Summers committed
169 170
      };

171
      tool_type = g_type_register_static (GIMP_TYPE_DRAW_TOOL,
172
					  "GimpTransformTool",
173
                                          &tool_info, 0);
Nate Summers's avatar
Nate Summers committed
174 175 176 177 178 179 180 181
    }

  return tool_type;
}

static void
gimp_transform_tool_class_init (GimpTransformToolClass *klass)
{
182
  GObjectClass      *object_class;
Nate Summers's avatar
Nate Summers committed
183 184
  GimpToolClass     *tool_class;
  GimpDrawToolClass *draw_class;
Nate Summers's avatar
Nate Summers committed
185

186 187 188
  object_class = G_OBJECT_CLASS (klass);
  tool_class   = GIMP_TOOL_CLASS (klass);
  draw_class   = GIMP_DRAW_TOOL_CLASS (klass);
Nate Summers's avatar
Nate Summers committed
189

190
  parent_class = g_type_class_peek_parent (klass);
Nate Summers's avatar
Nate Summers committed
191

192
  object_class->constructor  = gimp_transform_tool_constructor;
193
  object_class->finalize     = gimp_transform_tool_finalize;
Nate Summers's avatar
Nate Summers committed
194

195
  tool_class->initialize     = gimp_transform_tool_initialize;
196
  tool_class->control        = gimp_transform_tool_control;
Nate Summers's avatar
Nate Summers committed
197 198 199
  tool_class->button_press   = gimp_transform_tool_button_press;
  tool_class->button_release = gimp_transform_tool_button_release;
  tool_class->motion         = gimp_transform_tool_motion;
200
  tool_class->key_press      = gimp_transform_tool_key_press;
201
  tool_class->modifier_key   = gimp_transform_tool_modifier_key;
202
  tool_class->oper_update    = gimp_transform_tool_oper_update;
Nate Summers's avatar
Nate Summers committed
203
  tool_class->cursor_update  = gimp_transform_tool_cursor_update;
Nate Summers's avatar
Nate Summers committed
204 205

  draw_class->draw           = gimp_transform_tool_draw;
Michael Natterer's avatar
Michael Natterer committed
206 207 208 209 210

  klass->dialog              = NULL;
  klass->prepare             = NULL;
  klass->motion              = NULL;
  klass->recalc              = NULL;
211
  klass->transform           = gimp_transform_tool_real_transform;
Nate Summers's avatar
Nate Summers committed
212 213 214
}

static void
Michael Natterer's avatar
Michael Natterer committed
215
gimp_transform_tool_init (GimpTransformTool *tr_tool)
Nate Summers's avatar
Nate Summers committed
216
{
Michael Natterer's avatar
Michael Natterer committed
217
  GimpTool *tool;
218
  gint      i;
Nate Summers's avatar
Nate Summers committed
219

Michael Natterer's avatar
Michael Natterer committed
220
  tool = GIMP_TOOL (tr_tool);
Michael Natterer's avatar
Michael Natterer committed
221

Michael Natterer's avatar
Michael Natterer committed
222 223 224
  gimp_tool_control_set_scroll_lock (tool->control, TRUE);
  gimp_tool_control_set_preserve    (tool->control, FALSE);

Michael Natterer's avatar
Michael Natterer committed
225 226
  tr_tool->function = TRANSFORM_CREATING;
  tr_tool->original = NULL;
Nate Summers's avatar
Nate Summers committed
227 228

  for (i = 0; i < TRAN_INFO_SIZE; i++)
229
    {
Michael Natterer's avatar
Michael Natterer committed
230 231
      tr_tool->trans_info[i]     = 0.0;
      tr_tool->old_trans_info[i] = 0.0;
232
    }
Nate Summers's avatar
Nate Summers committed
233

234
  gimp_matrix3_identity (&tr_tool->transform);
235

236 237 238 239 240 241 242
  tr_tool->use_grid         = TRUE;
  tr_tool->use_center       = TRUE;
  tr_tool->ngx              = 0;
  tr_tool->ngy              = 0;
  tr_tool->grid_coords      = NULL;
  tr_tool->tgrid_coords     = NULL;

243
  tr_tool->type             = GIMP_TRANSFORM_TYPE_LAYER;
244
  tr_tool->direction        = GIMP_TRANSFORM_FORWARD;
245

246 247 248
  tr_tool->shell_desc       = NULL;
  tr_tool->progress_text    = _("Transforming...");
  tr_tool->info_dialog      = NULL;
Nate Summers's avatar
Nate Summers committed
249 250
}

251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
static GObject *
gimp_transform_tool_constructor (GType                  type,
                                 guint                  n_params,
                                 GObjectConstructParam *params)
{
  GObject           *object;
  GimpTool          *tool;
  GimpTransformTool *tr_tool;

  object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);

  tool    = GIMP_TOOL (object);
  tr_tool = GIMP_TRANSFORM_TOOL (object);

  g_assert (GIMP_IS_TOOL_INFO (tool->tool_info));

  if (tr_tool->use_grid)
    {
      tr_tool->type =
        GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options)->type;
      tr_tool->direction =
        GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options)->direction;

      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::type",
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::grid-type",
                               G_CALLBACK (gimp_transform_tool_notify_grid),
                               tr_tool, 0);
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::grid-size",
                               G_CALLBACK (gimp_transform_tool_notify_grid),
                               tr_tool, 0);
    }

  return object;
}

295
static void
296
gimp_transform_tool_finalize (GObject *object)
Nate Summers's avatar
Nate Summers committed
297
{
298
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (object);
299 300

  if (tr_tool->original)
301
    {
302
      tile_manager_unref (tr_tool->original);
303 304
      tr_tool->original = NULL;
    }
305

306
  if (tr_tool->info_dialog)
307
    {
308 309
      info_dialog_free (tr_tool->info_dialog);
      tr_tool->info_dialog = NULL;
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    }

  if (tr_tool->grid_coords)
    {
      g_free (tr_tool->grid_coords);
      tr_tool->grid_coords = NULL;
    }

  if (tr_tool->tgrid_coords)
    {
      g_free (tr_tool->tgrid_coords);
      tr_tool->tgrid_coords = NULL;
    }

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

327
static gboolean
328 329 330 331 332 333 334 335
gimp_transform_tool_initialize (GimpTool    *tool,
				GimpDisplay *gdisp)
{
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);

  if (gdisp != tool->gdisp)
    {
      GimpDrawable *drawable = gimp_image_active_drawable (gdisp->gimage);
336
      gint          i;
337 338 339 340

      if (GIMP_IS_LAYER (drawable) &&
          gimp_layer_get_mask (GIMP_LAYER (drawable)))
        {
341
          g_message (_("Transformations do not work on "
342
                       "layers that contain layer masks."));
343
          return FALSE;
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
        }

      /*  Set the pointer to the active display  */
      tool->gdisp    = gdisp;
      tool->drawable = drawable;

      /*  Initialize the transform tool dialog */
      if (! tr_tool->info_dialog)
        gimp_transform_tool_dialog (tr_tool);

      /*  Find the transform bounds for some tools (like scale,
       *  perspective) that actually need the bounds for
       *  initializing
       */
      gimp_transform_tool_bounds (tr_tool, gdisp);

      gimp_transform_tool_prepare (tr_tool, gdisp);

      /*  Recalculate the transform tool  */
      gimp_transform_tool_recalc (tr_tool, gdisp);

      /*  start drawing the bounding box and handles...  */
      gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), gdisp);

      tr_tool->function = TRANSFORM_CREATING;

      /*  Save the current transformation info  */
      for (i = 0; i < TRAN_INFO_SIZE; i++)
	tr_tool->old_trans_info[i] = tr_tool->trans_info[i];
    }
374 375

  return TRUE;
376 377
}

378
static void
379 380 381
gimp_transform_tool_control (GimpTool       *tool,
			     GimpToolAction  action,
			     GimpDisplay    *gdisp)
382
{
383
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
384

385 386 387 388
  switch (action)
    {
    case PAUSE:
      break;
389

390
    case RESUME:
Michael Natterer's avatar
Michael Natterer committed
391
      gimp_transform_tool_recalc (tr_tool, gdisp);
392
      break;
393

394
    case HALT:
395
      gimp_transform_tool_halt (tr_tool);
396
      return; /* don't upchain */
397
      break;
398

399 400 401 402
    default:
      break;
    }

Michael Natterer's avatar
Michael Natterer committed
403
  GIMP_TOOL_CLASS (parent_class)->control (tool, action, gdisp);
404 405 406
}

static void
407 408 409 410 411
gimp_transform_tool_button_press (GimpTool        *tool,
                                  GimpCoords      *coords,
                                  guint32          time,
                                  GdkModifierType  state,
			          GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
412
{
413
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
414

415 416
  if (tr_tool->function == TRANSFORM_CREATING && tr_tool->use_grid)
    gimp_transform_tool_oper_update (tool, coords, state, gdisp);
417

418 419
  tr_tool->lastx = tr_tool->startx = coords->x;
  tr_tool->lasty = tr_tool->starty = coords->y;
Nate Summers's avatar
Nate Summers committed
420

421
  gimp_tool_control_activate (tool->control);
Nate Summers's avatar
Nate Summers committed
422 423
}

424
static void
425 426 427 428 429
gimp_transform_tool_button_release (GimpTool        *tool,
                                    GimpCoords      *coords,
                                    guint32          time,
			            GdkModifierType  state,
			            GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
430
{
431
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
432
  gint               i;
Nate Summers's avatar
Nate Summers committed
433 434

  /*  if we are creating, there is nothing to be done...exit  */
435
  if (tr_tool->function == TRANSFORM_CREATING && tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
436 437 438
    return;

  /*  if the 3rd button isn't pressed, transform the selected mask  */
439
  if (! (state & GDK_BUTTON3_MASK))
Nate Summers's avatar
Nate Summers committed
440 441
    {
      /* Shift-clicking is another way to approve the transform  */
442
      if ((state & GDK_SHIFT_MASK) || ! tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
443
	{
Michael Natterer's avatar
Michael Natterer committed
444
	  gimp_transform_tool_doit (tr_tool, gdisp);
Nate Summers's avatar
Nate Summers committed
445 446 447 448
	}
    }
  else
    {
449
      gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
450 451 452

      /*  Restore the previous transformation info  */
      for (i = 0; i < TRAN_INFO_SIZE; i++)
453
	tr_tool->trans_info[i] = tr_tool->old_trans_info[i];
Nate Summers's avatar
Nate Summers committed
454 455

      /*  recalculate the tool's transformation matrix  */
Michael Natterer's avatar
Michael Natterer committed
456
      gimp_transform_tool_recalc (tr_tool, gdisp);
Nate Summers's avatar
Nate Summers committed
457

458
      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
459
    }
460 461

  gimp_tool_control_halt (tool->control);
Nate Summers's avatar
Nate Summers committed
462 463
}

464
static void
465 466 467 468 469
gimp_transform_tool_motion (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
		            GdkModifierType  state,
		            GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
470
{
471
  GimpTransformTool      *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Michael Natterer's avatar
Michael Natterer committed
472
  GimpTransformToolClass *tr_tool_class;
Nate Summers's avatar
Nate Summers committed
473

474
  /*  if we are creating, there is nothing to be done so exit.  */
475
  if (tr_tool->function == TRANSFORM_CREATING || ! tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
476 477
    return;

478
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
479

Michael Natterer's avatar
Michael Natterer committed
480 481 482
  tr_tool->curx  = coords->x;
  tr_tool->cury  = coords->y;
  tr_tool->state = state;
Nate Summers's avatar
Nate Summers committed
483 484

  /*  recalculate the tool's transformation matrix  */
Michael Natterer's avatar
Michael Natterer committed
485 486 487 488 489
  tr_tool_class = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool);

  if (tr_tool_class->motion)
    {
      tr_tool_class->motion (tr_tool, gdisp);
Nate Summers's avatar
Nate Summers committed
490

Michael Natterer's avatar
Michael Natterer committed
491 492 493 494 495 496
      if (tr_tool_class->recalc)
        tr_tool_class->recalc (tr_tool, gdisp);
    }

  tr_tool->lastx = tr_tool->curx;
  tr_tool->lasty = tr_tool->cury;
Nate Summers's avatar
Nate Summers committed
497

498
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
499 500
}

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
#define RESPONSE_RESET 1

static void
gimp_transform_tool_key_press (GimpTool    *tool,
                               GdkEventKey *kevent,
                               GimpDisplay *gdisp)
{
  GimpTransformTool *trans_tool = GIMP_TRANSFORM_TOOL (tool);
  GimpDrawTool      *draw_tool = GIMP_DRAW_TOOL (tool);

  if (gdisp == draw_tool->gdisp)
    {
      switch (kevent->keyval)
        {
          case GDK_KP_Enter:
          case GDK_Return:
            gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, trans_tool);
            break;

          case GDK_Delete:
          case GDK_BackSpace:
            gimp_transform_tool_response (NULL, RESPONSE_RESET, trans_tool);
            break;

          default:
            break;
        }
    }
}

531 532 533 534 535 536 537
static void
gimp_transform_tool_modifier_key (GimpTool        *tool,
                                  GdkModifierType  key,
                                  gboolean         press,
                                  GdkModifierType  state,
                                  GimpDisplay     *gdisp)
{
538
  GimpTransformOptions *options;
539

540
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
541

542
  if (key == GDK_CONTROL_MASK)
543
    {
544
      g_object_set (options,
545 546
                    "constrain-1", ! options->constrain_1,
                    NULL);
547
    }
548
  else if (key == GDK_MOD1_MASK)
549
    {
550
      g_object_set (options,
551 552
                    "constrain-2", ! options->constrain_2,
                    NULL);
553 554 555
    }
}

556 557 558 559 560 561
static void
gimp_transform_tool_oper_update (GimpTool        *tool,
                                 GimpCoords      *coords,
                                 GdkModifierType  state,
                                 GimpDisplay     *gdisp)
{
562 563
  GimpTransformTool *tr_tool   = GIMP_TRANSFORM_TOOL (tool);
  GimpDrawTool      *draw_tool = GIMP_DRAW_TOOL (tool);
564

565 566 567
  if (! tr_tool->use_grid)
    return;

568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
  if (gdisp == tool->gdisp)
    {
      gdouble closest_dist;
      gdouble dist;

      closest_dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                                   coords->x, coords->y,
                                                   tr_tool->tx1, tr_tool->ty1);
      tr_tool->function = TRANSFORM_HANDLE_1;

      dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                           coords->x, coords->y,
                                           tr_tool->tx2, tr_tool->ty2);
      if (dist < closest_dist)
	{
	  closest_dist = dist;
	  tr_tool->function = TRANSFORM_HANDLE_2;
	}

      dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                           coords->x, coords->y,
                                           tr_tool->tx3, tr_tool->ty3);
      if (dist < closest_dist)
	{
	  closest_dist = dist;
	  tr_tool->function = TRANSFORM_HANDLE_3;
	}

      dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                           coords->x, coords->y,
                                           tr_tool->tx4, tr_tool->ty4);
      if (dist < closest_dist)
	{
	  closest_dist = dist;
	  tr_tool->function = TRANSFORM_HANDLE_4;
	}

      if (gimp_draw_tool_on_handle (draw_tool, gdisp,
                                    coords->x, coords->y,
                                    GIMP_HANDLE_CIRCLE,
                                    tr_tool->tcx, tr_tool->tcy,
609
                                    HANDLE_SIZE, HANDLE_SIZE,
610 611 612 613 614 615 616 617
                                    GTK_ANCHOR_CENTER,
                                    FALSE))
	{
	  tr_tool->function = TRANSFORM_HANDLE_CENTER;
	}
    }
}

618
static void
619 620 621 622
gimp_transform_tool_cursor_update (GimpTool        *tool,
                                   GimpCoords      *coords,
			           GdkModifierType  state,
			           GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
623
{
624
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (tool);
625
  GimpTransformOptions *options;
626

627
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
Nate Summers's avatar
Nate Summers committed
628

Michael Natterer's avatar
Michael Natterer committed
629
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
630
    {
631 632 633
      GimpChannel        *selection = gimp_image_get_mask (gdisp->gimage);
      GdkCursorType       ctype     = GDK_TOP_LEFT_ARROW;
      GimpCursorModifier  cmodifier = GIMP_CURSOR_MODIFIER_NONE;
634

635
      switch (options->type)
Michael Natterer's avatar
Michael Natterer committed
636
        {
637 638
        case GIMP_TRANSFORM_TYPE_LAYER:
          {
639
            GimpDrawable *drawable = gimp_image_active_drawable (gdisp->gimage);
640 641 642 643 644 645

            if (drawable)
              {
                if (GIMP_IS_LAYER (drawable) &&
                    gimp_layer_get_mask (GIMP_LAYER (drawable)))
                  {
646
                    ctype = GIMP_CURSOR_BAD;
647 648 649
                  }
                else if (gimp_display_coords_in_active_drawable (gdisp, coords))
                  {
650 651
                    if (gimp_channel_is_empty (selection) ||
                        gimp_channel_value (selection, coords->x, coords->y))
652
                      {
653
                        ctype = GIMP_CURSOR_MOUSE;
654 655 656 657 658 659 660
                      }
                  }
              }
          }
          break;

        case GIMP_TRANSFORM_TYPE_SELECTION:
661 662
          if (gimp_channel_is_empty (selection) ||
              gimp_channel_value (selection, coords->x, coords->y))
Michael Natterer's avatar
Michael Natterer committed
663
            {
664
              ctype = GIMP_CURSOR_MOUSE;
Michael Natterer's avatar
Michael Natterer committed
665
            }
666 667 668 669
          break;

        case GIMP_TRANSFORM_TYPE_PATH:
          if (gimp_image_get_active_vectors (gdisp->gimage))
670
            ctype = GIMP_CURSOR_MOUSE;
671
          else
672
            ctype = GIMP_CURSOR_BAD;
673
          break;
Michael Natterer's avatar
Michael Natterer committed
674
        }
675

Michael Natterer's avatar
Michael Natterer committed
676 677 678 679 680 681 682 683
      if (tr_tool->use_center && tr_tool->function == TRANSFORM_HANDLE_CENTER)
        {
          cmodifier = GIMP_CURSOR_MODIFIER_MOVE;
        }

      gimp_tool_control_set_cursor          (tool->control, ctype);
      gimp_tool_control_set_cursor_modifier (tool->control, cmodifier);
    }
684 685

  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, gdisp);
Nate Summers's avatar
Nate Summers committed
686 687
}

688
static void
689
gimp_transform_tool_draw (GimpDrawTool *draw_tool)
Nate Summers's avatar
Nate Summers committed
690
{
691 692
  GimpTool             *tool    = GIMP_TOOL (draw_tool);
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (draw_tool);
693
  GimpTransformOptions *options;
694
  gdouble               z1, z2, z3, z4;
Nate Summers's avatar
Nate Summers committed
695

696 697 698
  if (! tr_tool->use_grid)
    return;

699
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
700

Nate Summers's avatar
Nate Summers committed
701
  /*  draw the bounding box  */
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
  gimp_draw_tool_draw_line (draw_tool,
                            tr_tool->tx1, tr_tool->ty1,
                            tr_tool->tx2, tr_tool->ty2,
                            FALSE);
  gimp_draw_tool_draw_line (draw_tool,
                            tr_tool->tx2, tr_tool->ty2,
                            tr_tool->tx4, tr_tool->ty4,
                            FALSE);
  gimp_draw_tool_draw_line (draw_tool,
                            tr_tool->tx3, tr_tool->ty3,
                            tr_tool->tx4, tr_tool->ty4,
                            FALSE);
  gimp_draw_tool_draw_line (draw_tool,
                            tr_tool->tx3, tr_tool->ty3,
                            tr_tool->tx1, tr_tool->ty1,
                            FALSE);
Nate Summers's avatar
Nate Summers committed
718

719 720
  /* We test if the transformed polygon is convex.
   * if z1 and z2 have the same sign as well as z3 and z4
721 722
   * the polygon is convex.
   */
723 724 725 726 727 728 729 730
  z1 = ((tr_tool->tx2 - tr_tool->tx1) * (tr_tool->ty4 - tr_tool->ty1) -
        (tr_tool->tx4 - tr_tool->tx1) * (tr_tool->ty2 - tr_tool->ty1));
  z2 = ((tr_tool->tx4 - tr_tool->tx1) * (tr_tool->ty3 - tr_tool->ty1) -
        (tr_tool->tx3 - tr_tool->tx1) * (tr_tool->ty4 - tr_tool->ty1));
  z3 = ((tr_tool->tx4 - tr_tool->tx2) * (tr_tool->ty3 - tr_tool->ty2) -
        (tr_tool->tx3 - tr_tool->tx2) * (tr_tool->ty4 - tr_tool->ty2));
  z4 = ((tr_tool->tx3 - tr_tool->tx2) * (tr_tool->ty1 - tr_tool->ty2) -
        (tr_tool->tx1 - tr_tool->tx2) * (tr_tool->ty3 - tr_tool->ty2));
731

732
  /*  Draw the grid (not for path transform since it looks ugly)  */
Nate Summers's avatar
Nate Summers committed
733

734 735 736
  if (tr_tool->type != GIMP_TRANSFORM_TYPE_PATH &&
      tr_tool->grid_coords                      &&
      tr_tool->tgrid_coords                     &&
737 738
      z1 * z2 > 0                               &&
      z3 * z4 > 0)
Nate Summers's avatar
Nate Summers committed
739
    {
740 741
      gint gci, i, k;

Nate Summers's avatar
Nate Summers committed
742
      k = tr_tool->ngx + tr_tool->ngy;
743

744
      for (i = 0, gci = 0; i < k; i++, gci += 4)
Nate Summers's avatar
Nate Summers committed
745
	{
746 747 748 749 750 751
          gimp_draw_tool_draw_line (draw_tool,
                                    tr_tool->tgrid_coords[gci],
                                    tr_tool->tgrid_coords[gci + 1],
                                    tr_tool->tgrid_coords[gci + 2],
                                    tr_tool->tgrid_coords[gci + 3],
                                    FALSE);
Nate Summers's avatar
Nate Summers committed
752 753 754 755
	}
    }

  /*  draw the tool handles  */
756 757 758
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx1, tr_tool->ty1,
759
                              HANDLE_SIZE, HANDLE_SIZE,
760 761 762 763 764
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx2, tr_tool->ty2,
765
                              HANDLE_SIZE, HANDLE_SIZE,
766 767 768 769 770
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx3, tr_tool->ty3,
771
                              HANDLE_SIZE, HANDLE_SIZE,
772 773 774 775 776
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx4, tr_tool->ty4,
777
                              HANDLE_SIZE, HANDLE_SIZE,
778 779
                              GTK_ANCHOR_CENTER,
                              FALSE);
Nate Summers's avatar
Nate Summers committed
780 781

  /*  draw the center  */
782 783 784 785 786
  if (tr_tool->use_center)
    {
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_FILLED_CIRCLE,
                                  tr_tool->tcx, tr_tool->tcy,
787
                                  HANDLE_SIZE, HANDLE_SIZE,
788 789 790
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
    }
Nate Summers's avatar
Nate Summers committed
791

792
  if (tr_tool->type == GIMP_TRANSFORM_TYPE_PATH)
Nate Summers's avatar
Nate Summers committed
793
    {
794 795 796
      GimpVectors *vectors;
      GimpStroke  *stroke = NULL;
      GimpMatrix3  matrix = tr_tool->transform;
797

798 799 800
      vectors = gimp_image_get_active_vectors (tool->gdisp->gimage);

      if (vectors)
801
        {
802 803 804 805 806 807 808 809 810 811
          if (tr_tool->direction == GIMP_TRANSFORM_BACKWARD)
            gimp_matrix3_invert (&matrix);

          while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
            {
              GArray   *coords;
              gboolean  closed;

              coords = gimp_stroke_interpolate (stroke, 1.0, &closed);

812
              if (coords && coords->len)
813
                {
814 815
                  gint i;

816 817 818 819 820 821 822 823 824
                  for (i = 0; i < coords->len; i++)
                    {
                      GimpCoords *curr = &g_array_index (coords, GimpCoords, i);

                      gimp_matrix3_transform_point (&matrix,
                                                    curr->x, curr->y,
                                                    &curr->x, &curr->y);
                    }

825 826 827 828
                  gimp_draw_tool_draw_strokes (draw_tool,
                                               &g_array_index (coords,
                                                               GimpCoords, 0),
                                               coords->len, FALSE, FALSE);
829
                }
830 831 832

              if (coords)
                g_array_free (coords, TRUE);
833
            }
834
        }
Nate Summers's avatar
Nate Summers committed
835 836 837
    }
}

838 839
static TileManager *
gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
840
                                    GimpItem          *active_item,
841 842
                                    GimpDisplay       *gdisp)
{
843
  GimpTool             *tool = GIMP_TOOL (tr_tool);
844
  GimpTransformOptions *options;
845
  GimpContext          *context;
846
  GimpProgress         *progress;
847
  TileManager          *ret  = NULL;
848

849
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
850
  context = GIMP_CONTEXT (options);
851 852 853 854 855 856 857

  if (tr_tool->info_dialog)
    gtk_widget_set_sensitive (GTK_WIDGET (tr_tool->info_dialog->shell), FALSE);

  progress = gimp_progress_start (gdisp, tr_tool->progress_text, FALSE,
                                  NULL, NULL);

858
  if (gimp_item_get_linked (active_item))
859
    gimp_item_linked_transform (active_item, context,
860
                                &tr_tool->transform,
861
                                options->direction,
862
                                options->interpolation,
863 864
                                options->supersample,
                                options->recursion_level,
865
                                options->clip,
866 867 868
                                progress ?
                                gimp_progress_update_and_flush : NULL,
                                progress);
869

870 871 872