gimptransformtool.c 64.6 KB
Newer Older
1
/* GIMP - The GNU 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

#include "libgimpmath/gimpmath.h"
27
#include "libgimpconfig/gimpconfig.h"
Nate Summers's avatar
Nate Summers committed
28 29
#include "libgimpwidgets/gimpwidgets.h"

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

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

35
#include "core/gimp.h"
36
#include "core/gimpchannel.h"
37
#include "core/gimpcontext.h"
Michael Natterer's avatar
Michael Natterer committed
38
#include "core/gimpdrawable-transform.h"
39
#include "core/gimpimage.h"
40
#include "core/gimpimage-undo.h"
41
#include "core/gimpimage-undo-push.h"
42
#include "core/gimpitem-linked.h"
43
#include "core/gimplayer.h"
44
#include "core/gimplayermask.h"
45
#include "core/gimppickable.h"
46
#include "core/gimpprogress.h"
Michael Natterer's avatar
Michael Natterer committed
47
#include "core/gimptoolinfo.h"
48

49 50 51
#include "vectors/gimpvectors.h"
#include "vectors/gimpstroke.h"

52
#include "widgets/gimpdialogfactory.h"
53
#include "widgets/gimptooldialog.h"
Michael Natterer's avatar
Michael Natterer committed
54 55
#include "widgets/gimpviewabledialog.h"

56
#include "display/gimpdisplay.h"
57 58 59
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-appearance.h"
#include "display/gimpdisplayshell-transform.h"
Nate Summers's avatar
Nate Summers committed
60

61
#include "gimptoolcontrol.h"
62
#include "gimptransformoptions.h"
63 64
#include "gimptransformtool.h"
#include "gimptransformtool-undo.h"
65

66
#include "gimp-intl.h"
Nate Summers's avatar
Nate Summers committed
67

68

69 70
#define HANDLE_SIZE     25
#define MIN_HANDLE_SIZE  6
Michael Natterer's avatar
Michael Natterer committed
71

72

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

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

119
static void     gimp_transform_tool_draw           (GimpDrawTool      *draw_tool);
Michael Natterer's avatar
Michael Natterer committed
120

121 122
static void     gimp_transform_tool_dialog_update  (GimpTransformTool *tr_tool);

123
static TileManager *
124
                gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
125
                                                    GimpItem          *item,
126
                                                    gboolean           mask_empty,
127
                                                    GimpDisplay       *display);
128

129 130
static void     gimp_transform_tool_halt           (GimpTransformTool *tr_tool);
static void     gimp_transform_tool_bounds         (GimpTransformTool *tr_tool,
131
                                                    GimpDisplay       *display);
132 133
static void     gimp_transform_tool_dialog         (GimpTransformTool *tr_tool);
static void     gimp_transform_tool_prepare        (GimpTransformTool *tr_tool,
134
                                                    GimpDisplay       *display);
135
static void     gimp_transform_tool_doit           (GimpTransformTool *tr_tool,
136
                                                    GimpDisplay       *display);
Michael Natterer's avatar
Michael Natterer committed
137
static void     gimp_transform_tool_transform_bounding_box (GimpTransformTool *tr_tool);
138
static void     gimp_transform_tool_grid_recalc    (GimpTransformTool *tr_tool);
Michael Natterer's avatar
Michael Natterer committed
139

140 141
static void     gimp_transform_tool_handles_recalc (GimpTransformTool *tr_tool,
                                                    GimpDisplay       *display);
142 143
static void     gimp_transform_tool_force_expose_preview (GimpTransformTool *tr_tool);

144
static void     gimp_transform_tool_response       (GtkWidget         *widget,
145
                                                    gint               response_id,
Michael Natterer's avatar
Michael Natterer committed
146
                                                    GimpTransformTool *tr_tool);
147

148 149 150 151 152 153
static void     gimp_transform_tool_notify_type    (GimpTransformOptions *options,
                                                    GParamSpec           *pspec,
                                                    GimpTransformTool    *tr_tool);
static void     gimp_transform_tool_notify_preview (GimpTransformOptions *options,
                                                    GParamSpec           *pspec,
                                                    GimpTransformTool    *tr_tool);
154 155


156
G_DEFINE_TYPE (GimpTransformTool, gimp_transform_tool, GIMP_TYPE_DRAW_TOOL)
157 158

#define parent_class gimp_transform_tool_parent_class
159

Nate Summers's avatar
Nate Summers committed
160 161 162 163

static void
gimp_transform_tool_class_init (GimpTransformToolClass *klass)
{
164 165 166
  GObjectClass      *object_class = G_OBJECT_CLASS (klass);
  GimpToolClass     *tool_class   = GIMP_TOOL_CLASS (klass);
  GimpDrawToolClass *draw_class   = GIMP_DRAW_TOOL_CLASS (klass);
Nate Summers's avatar
Nate Summers committed
167

168
  object_class->constructor  = gimp_transform_tool_constructor;
169
  object_class->finalize     = gimp_transform_tool_finalize;
Nate Summers's avatar
Nate Summers committed
170

171
  tool_class->initialize     = gimp_transform_tool_initialize;
172
  tool_class->control        = gimp_transform_tool_control;
Nate Summers's avatar
Nate Summers committed
173 174 175
  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;
176
  tool_class->key_press      = gimp_transform_tool_key_press;
177
  tool_class->modifier_key   = gimp_transform_tool_modifier_key;
178
  tool_class->active_modifier_key = gimp_transform_tool_modifier_key;
179
  tool_class->oper_update    = gimp_transform_tool_oper_update;
Nate Summers's avatar
Nate Summers committed
180
  tool_class->cursor_update  = gimp_transform_tool_cursor_update;
Nate Summers's avatar
Nate Summers committed
181 182

  draw_class->draw           = gimp_transform_tool_draw;
Michael Natterer's avatar
Michael Natterer committed
183 184

  klass->dialog              = NULL;
Michael Natterer's avatar
Michael Natterer committed
185
  klass->dialog_update       = NULL;
Michael Natterer's avatar
Michael Natterer committed
186 187 188
  klass->prepare             = NULL;
  klass->motion              = NULL;
  klass->recalc              = NULL;
189
  klass->transform           = gimp_transform_tool_real_transform;
Nate Summers's avatar
Nate Summers committed
190 191 192
}

static void
Michael Natterer's avatar
Michael Natterer committed
193
gimp_transform_tool_init (GimpTransformTool *tr_tool)
Nate Summers's avatar
Nate Summers committed
194
{
Michael Natterer's avatar
Michael Natterer committed
195
  GimpTool *tool;
196
  gint      i;
Nate Summers's avatar
Nate Summers committed
197

Michael Natterer's avatar
Michael Natterer committed
198
  tool = GIMP_TOOL (tr_tool);
Michael Natterer's avatar
Michael Natterer committed
199

Michael Natterer's avatar
Michael Natterer committed
200 201
  gimp_tool_control_set_scroll_lock (tool->control, TRUE);
  gimp_tool_control_set_preserve    (tool->control, FALSE);
202 203 204 205
  gimp_tool_control_set_dirty_mask  (tool->control,
                                     GIMP_DIRTY_IMAGE_SIZE |
                                     GIMP_DIRTY_DRAWABLE   |
                                     GIMP_DIRTY_SELECTION);
Michael Natterer's avatar
Michael Natterer committed
206

Michael Natterer's avatar
Michael Natterer committed
207 208
  tr_tool->function = TRANSFORM_CREATING;
  tr_tool->original = NULL;
Nate Summers's avatar
Nate Summers committed
209

210
  for (i = 0; i < TRANS_INFO_SIZE; i++)
211
    {
Michael Natterer's avatar
Michael Natterer committed
212 213
      tr_tool->trans_info[i]     = 0.0;
      tr_tool->old_trans_info[i] = 0.0;
214
    }
Nate Summers's avatar
Nate Summers committed
215

216
  gimp_matrix3_identity (&tr_tool->transform);
217

218 219 220
  tr_tool->use_grid         = FALSE;
  tr_tool->use_handles      = FALSE;
  tr_tool->use_center       = FALSE;
221
  tr_tool->use_mid_handles  = FALSE;
222 223 224 225

  tr_tool->handle_w         = HANDLE_SIZE;
  tr_tool->handle_h         = HANDLE_SIZE;

226 227 228 229 230
  tr_tool->ngx              = 0;
  tr_tool->ngy              = 0;
  tr_tool->grid_coords      = NULL;
  tr_tool->tgrid_coords     = NULL;

231
  tr_tool->type             = GIMP_TRANSFORM_TYPE_LAYER;
232
  tr_tool->direction        = GIMP_TRANSFORM_FORWARD;
233

234 235
  tr_tool->undo_desc        = NULL;

236
  tr_tool->shell_desc       = NULL;
237
  tr_tool->progress_text    = _("Transforming");
238
  tr_tool->dialog           = NULL;
Nate Summers's avatar
Nate Summers committed
239 240
}

241 242 243 244 245
static GObject *
gimp_transform_tool_constructor (GType                  type,
                                 guint                  n_params,
                                 GObjectConstructParam *params)
{
246 247 248 249
  GObject              *object;
  GimpTool             *tool;
  GimpTransformTool    *tr_tool;
  GimpTransformOptions *options;
250 251 252 253 254

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

  tool    = GIMP_TOOL (object);
  tr_tool = GIMP_TRANSFORM_TOOL (object);
255
  options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
256 257 258

  if (tr_tool->use_grid)
    {
259 260
      tr_tool->type      = options->type;
      tr_tool->direction = options->direction;
261

262
      g_signal_connect_object (options, "notify::type",
263 264
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
265
      g_signal_connect_object (options, "notify::type",
266 267
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
268

269
      g_signal_connect_object (options, "notify::direction",
270 271
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
272
      g_signal_connect_object (options, "notify::direction",
273 274
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
275

276
      g_signal_connect_object (options, "notify::preview-type",
277
                               G_CALLBACK (gimp_transform_tool_notify_preview),
278
                               tr_tool, 0);
279
      g_signal_connect_object (options, "notify::grid-type",
280
                               G_CALLBACK (gimp_transform_tool_notify_preview),
281
                               tr_tool, 0);
282
      g_signal_connect_object (options, "notify::grid-size",
283 284
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
285 286
    }

287
  g_signal_connect_object (options, "notify::constrain",
288 289 290
                           G_CALLBACK (gimp_transform_tool_dialog_update),
                           tr_tool, G_CONNECT_SWAPPED);

291 292 293
  return object;
}

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

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

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

  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);
}

326
static gboolean
327 328 329
gimp_transform_tool_initialize (GimpTool     *tool,
                                GimpDisplay  *display,
                                GError      **error)
330 331 332
{
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);

333
  if (display != tool->display)
334
    {
335
      gint i;
336 337

      /*  Set the pointer to the active display  */
Michael Natterer's avatar
Michael Natterer committed
338
      tool->display  = display;
339
      tool->drawable = gimp_image_active_drawable (display->image);
340 341

      /*  Initialize the transform tool dialog */
342
      if (! tr_tool->dialog)
343 344 345 346 347 348
        gimp_transform_tool_dialog (tr_tool);

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

351
      gimp_transform_tool_prepare (tr_tool, display);
352 353

      /*  Recalculate the transform tool  */
354
      gimp_transform_tool_recalc (tr_tool, display);
355 356

      /*  start drawing the bounding box and handles...  */
357
      gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
358 359 360 361

      tr_tool->function = TRANSFORM_CREATING;

      /*  Save the current transformation info  */
362
      for (i = 0; i < TRANS_INFO_SIZE; i++)
363
        tr_tool->old_trans_info[i] = tr_tool->trans_info[i];
364
    }
365 366

  return TRUE;
367 368
}

369
static void
370
gimp_transform_tool_control (GimpTool       *tool,
371
                             GimpToolAction  action,
372
                             GimpDisplay    *display)
373
{
374
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
375

376 377
  switch (action)
    {
378
    case GIMP_TOOL_ACTION_PAUSE:
379
      break;
380

381
    case GIMP_TOOL_ACTION_RESUME:
382 383
      gimp_transform_tool_bounds (tr_tool, display);
      gimp_transform_tool_recalc (tr_tool, display);
384
      break;
385

386
    case GIMP_TOOL_ACTION_HALT:
387
      gimp_transform_tool_halt (tr_tool);
388 389 390
      break;
    }

391
  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
392 393 394
}

static void
395 396 397 398
gimp_transform_tool_button_press (GimpTool        *tool,
                                  GimpCoords      *coords,
                                  guint32          time,
                                  GdkModifierType  state,
399
                                  GimpDisplay     *display)
Nate Summers's avatar
Nate Summers committed
400
{
401
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
402

403
  if (tr_tool->function == TRANSFORM_CREATING)
404
    gimp_transform_tool_oper_update (tool, coords, state, TRUE, display);
405

406 407
  tr_tool->lastx = tr_tool->startx = coords->x;
  tr_tool->lasty = tr_tool->starty = coords->y;
Nate Summers's avatar
Nate Summers committed
408

409
  gimp_tool_control_activate (tool->control);
Nate Summers's avatar
Nate Summers committed
410 411
}

412
static void
413 414 415
gimp_transform_tool_button_release (GimpTool        *tool,
                                    GimpCoords      *coords,
                                    guint32          time,
416
                                    GdkModifierType  state,
417
                                    GimpDisplay     *display)
Nate Summers's avatar
Nate Summers committed
418
{
419
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
420
  gint               i;
Nate Summers's avatar
Nate Summers committed
421 422

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

  /*  if the 3rd button isn't pressed, transform the selected mask  */
427
  if (! (state & GDK_BUTTON3_MASK))
Nate Summers's avatar
Nate Summers committed
428 429
    {
      /* Shift-clicking is another way to approve the transform  */
430
      if ((state & GDK_SHIFT_MASK) || ! tr_tool->use_grid)
431
        {
432
          gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, tr_tool);
433
        }
Nate Summers's avatar
Nate Summers committed
434 435 436
    }
  else
    {
437
      gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
438

Michael Natterer's avatar
Michael Natterer committed
439 440 441
      /* get rid of preview artifacts left outside the drawable's area */
      gimp_transform_tool_expose_preview (tr_tool);

Nate Summers's avatar
Nate Summers committed
442
      /*  Restore the previous transformation info  */
443
      for (i = 0; i < TRANS_INFO_SIZE; i++)
444
        tr_tool->trans_info[i] = tr_tool->old_trans_info[i];
Nate Summers's avatar
Nate Summers committed
445

Michael Natterer's avatar
Michael Natterer committed
446
      /*  reget the selection bounds  */
447
      gimp_transform_tool_bounds (tr_tool, display);
Michael Natterer's avatar
Michael Natterer committed
448

Nate Summers's avatar
Nate Summers committed
449
      /*  recalculate the tool's transformation matrix  */
450
      gimp_transform_tool_recalc (tr_tool, display);
Nate Summers's avatar
Nate Summers committed
451

452
      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
453
    }
454 455

  gimp_tool_control_halt (tool->control);
Nate Summers's avatar
Nate Summers committed
456 457
}

458
static void
459 460 461
gimp_transform_tool_motion (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
462
                            GdkModifierType  state,
463
                            GimpDisplay     *display)
Nate Summers's avatar
Nate Summers committed
464
{
465
  GimpTransformTool      *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Michael Natterer's avatar
Michael Natterer committed
466
  GimpTransformToolClass *tr_tool_class;
Nate Summers's avatar
Nate Summers committed
467

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

472
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
473

Michael Natterer's avatar
Michael Natterer committed
474 475 476
  tr_tool->curx  = coords->x;
  tr_tool->cury  = coords->y;
  tr_tool->state = state;
Nate Summers's avatar
Nate Summers committed
477 478

  /*  recalculate the tool's transformation matrix  */
Michael Natterer's avatar
Michael Natterer committed
479 480 481 482
  tr_tool_class = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool);

  if (tr_tool_class->motion)
    {
483
      tr_tool_class->motion (tr_tool, display);
Nate Summers's avatar
Nate Summers committed
484

485 486
      gimp_transform_tool_expose_preview (tr_tool);

487
      gimp_transform_tool_recalc (tr_tool, display);
Michael Natterer's avatar
Michael Natterer committed
488 489 490 491
    }

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

493
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
494 495
}

496 497
#define RESPONSE_RESET 1

498
static gboolean
499 500
gimp_transform_tool_key_press (GimpTool    *tool,
                               GdkEventKey *kevent,
501
                               GimpDisplay *display)
502 503
{
  GimpTransformTool *trans_tool = GIMP_TRANSFORM_TOOL (tool);
504
  GimpDrawTool      *draw_tool  = GIMP_DRAW_TOOL (tool);
505

506
  if (display == draw_tool->display)
507 508 509
    {
      switch (kevent->keyval)
        {
510 511 512
        case GDK_KP_Enter:
        case GDK_Return:
          gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, trans_tool);
513
          return TRUE;
514 515 516 517

        case GDK_Delete:
        case GDK_BackSpace:
          gimp_transform_tool_response (NULL, RESPONSE_RESET, trans_tool);
518
          return TRUE;
519 520 521 522

        case GDK_Escape:
          gimp_transform_tool_response (NULL, GTK_RESPONSE_CANCEL, trans_tool);
          return TRUE;
523 524
        }
    }
525 526

  return FALSE;
527 528
}

529 530 531 532 533
static void
gimp_transform_tool_modifier_key (GimpTool        *tool,
                                  GdkModifierType  key,
                                  gboolean         press,
                                  GdkModifierType  state,
534
                                  GimpDisplay     *display)
535
{
536
  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
537

538
  if (key == GDK_CONTROL_MASK)
539 540 541
    g_object_set (options,
                  "constrain", ! options->constrain,
                  NULL);
542 543
}

544 545 546 547
static void
gimp_transform_tool_oper_update (GimpTool        *tool,
                                 GimpCoords      *coords,
                                 GdkModifierType  state,
548
                                 gboolean         proximity,
549
                                 GimpDisplay     *display)
550
{
551 552
  GimpTransformTool *tr_tool   = GIMP_TRANSFORM_TOOL (tool);
  GimpDrawTool      *draw_tool = GIMP_DRAW_TOOL (tool);
553

554 555 556
  tr_tool->function = TRANSFORM_HANDLE_NONE;

  if (display != tool->display)
557 558
    return;

559
  if (tr_tool->use_handles)
560 561 562 563
    {
      gdouble closest_dist;
      gdouble dist;

564
      closest_dist = gimp_draw_tool_calc_distance (draw_tool, display,
565 566
                                                   coords->x, coords->y,
                                                   tr_tool->tx1, tr_tool->ty1);
567
      tr_tool->function = TRANSFORM_HANDLE_NW;
568

569
      dist = gimp_draw_tool_calc_distance (draw_tool, display,
570 571 572
                                           coords->x, coords->y,
                                           tr_tool->tx2, tr_tool->ty2);
      if (dist < closest_dist)
573 574
        {
          closest_dist = dist;
575
          tr_tool->function = TRANSFORM_HANDLE_NE;
576
        }
577

578
      dist = gimp_draw_tool_calc_distance (draw_tool, display,
579 580 581
                                           coords->x, coords->y,
                                           tr_tool->tx3, tr_tool->ty3);
      if (dist < closest_dist)
582 583
        {
          closest_dist = dist;
584
          tr_tool->function = TRANSFORM_HANDLE_SW;
585
        }
586

587
      dist = gimp_draw_tool_calc_distance (draw_tool, display,
588 589 590
                                           coords->x, coords->y,
                                           tr_tool->tx4, tr_tool->ty4);
      if (dist < closest_dist)
591 592
        {
          closest_dist = dist;
593
          tr_tool->function = TRANSFORM_HANDLE_SE;
594
        }
595 596 597 598 599 600 601

      if (tr_tool->use_mid_handles)
        {
          gdouble x, y;

          x = (tr_tool->tx1 + tr_tool->tx2) / 2.0;
          y = (tr_tool->ty1 + tr_tool->ty2) / 2.0;
602

603 604 605 606
          if (gimp_draw_tool_on_handle (draw_tool, display,
                                        coords->x, coords->y,
                                        GIMP_HANDLE_SQUARE,
                                        x, y,
607
                                        tr_tool->handle_w, tr_tool->handle_h,
608 609 610 611 612 613 614 615
                                        GTK_ANCHOR_CENTER,
                                        FALSE))
            {
              tr_tool->function = TRANSFORM_HANDLE_N;
            }

          x = (tr_tool->tx2 + tr_tool->tx4) / 2.0;
          y = (tr_tool->ty2 + tr_tool->ty4) / 2.0;
616

617 618 619 620
          if (gimp_draw_tool_on_handle (draw_tool, display,
                                        coords->x, coords->y,
                                        GIMP_HANDLE_SQUARE,
                                        x, y,
621
                                        tr_tool->handle_w, tr_tool->handle_h,
622 623 624 625 626 627 628 629
                                        GTK_ANCHOR_CENTER,
                                        FALSE))
            {
              tr_tool->function = TRANSFORM_HANDLE_E;
            }

          x = (tr_tool->tx3 + tr_tool->tx4) / 2.0;
          y = (tr_tool->ty3 + tr_tool->ty4) / 2.0;
630

631 632 633 634
          if (gimp_draw_tool_on_handle (draw_tool, display,
                                        coords->x, coords->y,
                                        GIMP_HANDLE_SQUARE,
                                        x, y,
635
                                        tr_tool->handle_w, tr_tool->handle_h,
636 637 638 639 640 641 642 643
                                        GTK_ANCHOR_CENTER,
                                        FALSE))
            {
              tr_tool->function = TRANSFORM_HANDLE_S;
            }

          x = (tr_tool->tx3 + tr_tool->tx1) / 2.0;
          y = (tr_tool->ty3 + tr_tool->ty1) / 2.0;
644

645 646 647 648
          if (gimp_draw_tool_on_handle (draw_tool, display,
                                        coords->x, coords->y,
                                        GIMP_HANDLE_SQUARE,
                                        x, y,
649
                                        tr_tool->handle_w, tr_tool->handle_h,
650 651 652 653 654 655
                                        GTK_ANCHOR_CENTER,
                                        FALSE))
            {
              tr_tool->function = TRANSFORM_HANDLE_W;
            }
        }
656
    }
657

658 659 660 661 662
  if (tr_tool->use_center &&
      gimp_draw_tool_on_handle (draw_tool, display,
                                coords->x, coords->y,
                                GIMP_HANDLE_CIRCLE,
                                tr_tool->tcx, tr_tool->tcy,
663 664
                                MIN (tr_tool->handle_w, tr_tool->handle_h),
                                MIN (tr_tool->handle_w, tr_tool->handle_h),
665 666 667 668
                                GTK_ANCHOR_CENTER,
                                FALSE))
    {
      tr_tool->function = TRANSFORM_HANDLE_CENTER;
669 670 671
    }
}

672
static void
673 674
gimp_transform_tool_cursor_update (GimpTool        *tool,
                                   GimpCoords      *coords,
675
                                   GdkModifierType  state,
676
                                   GimpDisplay     *display)
Nate Summers's avatar
Nate Summers committed
677
{
678
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (tool);
679
  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
Michael Natterer's avatar
Michael Natterer committed
680
  GimpCursorType        cursor;
681
  GimpCursorModifier    modifier = GIMP_CURSOR_MODIFIER_NONE;
Nate Summers's avatar
Nate Summers committed
682

Michael Natterer's avatar
Michael Natterer committed
683 684
  cursor = gimp_tool_control_get_cursor (tool->control);

685
  if (tr_tool->use_handles)
Nate Summers's avatar
Nate Summers committed
686
    {
687 688 689
      switch (tr_tool->function)
        {
        case TRANSFORM_HANDLE_NW:
Michael Natterer's avatar
Michael Natterer committed
690
          cursor = GIMP_CURSOR_CORNER_TOP_LEFT;
691
          break;
Sven Neumann's avatar
Sven Neumann committed
692

693
        case TRANSFORM_HANDLE_NE:
Michael Natterer's avatar
Michael Natterer committed
694
          cursor = GIMP_CURSOR_CORNER_TOP_RIGHT;
695
          break;
696

697
        case TRANSFORM_HANDLE_SW:
Michael Natterer's avatar
Michael Natterer committed
698
          cursor = GIMP_CURSOR_CORNER_BOTTOM_LEFT;
699 700
          break;

701
        case TRANSFORM_HANDLE_SE:
Michael Natterer's avatar
Michael Natterer committed
702
          cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
703
          break;
704

705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
        case TRANSFORM_HANDLE_N:
          cursor = GIMP_CURSOR_SIDE_TOP;
          break;

        case TRANSFORM_HANDLE_E:
          cursor = GIMP_CURSOR_SIDE_RIGHT;
          break;

        case TRANSFORM_HANDLE_S:
          cursor = GIMP_CURSOR_SIDE_BOTTOM;
          break;

        case TRANSFORM_HANDLE_W:
          cursor = GIMP_CURSOR_SIDE_LEFT;
          break;

721
        default:
Michael Natterer's avatar
Michael Natterer committed
722 723
          cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
          break;
Michael Natterer's avatar
Michael Natterer committed
724
        }
725
    }
Michael Natterer's avatar
Michael Natterer committed
726

727 728 729
  if (tr_tool->use_center && tr_tool->function == TRANSFORM_HANDLE_CENTER)
    {
      modifier = GIMP_CURSOR_MODIFIER_MOVE;
Michael Natterer's avatar
Michael Natterer committed
730
    }
731

732 733 734 735 736 737 738 739 740 741 742 743
  switch (options->type)
    {
    case GIMP_TRANSFORM_TYPE_LAYER:
    case GIMP_TRANSFORM_TYPE_SELECTION:
      break;

    case GIMP_TRANSFORM_TYPE_PATH:
      if (! gimp_image_get_active_vectors (display->image))
        modifier