gimptransformtool.c 65 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/gimpcontext.h"
Michael Natterer's avatar
Michael Natterer committed
37
#include "core/gimpdrawable-transform.h"
38
#include "core/gimpimage.h"
39
#include "core/gimpimage-undo.h"
40
#include "core/gimpimage-undo-push.h"
41
#include "core/gimpitem-linked.h"
42
#include "core/gimplayer.h"
43
#include "core/gimplayermask.h"
44
#include "core/gimppickable.h"
45
#include "core/gimpprogress.h"
Michael Natterer's avatar
Michael Natterer committed
46
#include "core/gimptoolinfo.h"
47

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

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

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

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

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

66

67 68
#define HANDLE_SIZE     25
#define MIN_HANDLE_SIZE  6
Michael Natterer's avatar
Michael Natterer committed
69

70

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

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
static GObject * gimp_transform_tool_constructor (GType                  type,
                                                  guint                  n_params,
                                                  GObjectConstructParam *params);
static void   gimp_transform_tool_finalize       (GObject               *object);

static gboolean gimp_transform_tool_initialize   (GimpTool              *tool,
                                                  GimpDisplay           *display,
                                                  GError               **error);
static void   gimp_transform_tool_control        (GimpTool              *tool,
                                                  GimpToolAction         action,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_button_press   (GimpTool              *tool,
                                                  GimpCoords            *coords,
                                                  guint32                time,
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_button_release (GimpTool              *tool,
                                                  GimpCoords            *coords,
                                                  guint32                time,
                                                  GdkModifierType        state,
                                                  GimpButtonReleaseType  release_type,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_motion         (GimpTool              *tool,
                                                  GimpCoords            *coords,
                                                  guint32                time,
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);
static gboolean gimp_transform_tool_key_press    (GimpTool              *tool,
                                                  GdkEventKey           *kevent,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_modifier_key   (GimpTool              *tool,
                                                  GdkModifierType        key,
                                                  gboolean               press,
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_oper_update    (GimpTool              *tool,
                                                  GimpCoords            *coords,
                                                  GdkModifierType        state,
                                                  gboolean               proximity,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_cursor_update  (GimpTool              *tool,
                                                  GimpCoords            *coords,
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);

static void   gimp_transform_tool_draw           (GimpDrawTool          *draw_tool);

static void   gimp_transform_tool_dialog_update  (GimpTransformTool     *tr_tool);
121

122
static TileManager *
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
              gimp_transform_tool_real_transform (GimpTransformTool     *tr_tool,
                                                  GimpItem              *item,
                                                  gboolean               mask_empty,
                                                  GimpDisplay           *display);

static void   gimp_transform_tool_halt           (GimpTransformTool     *tr_tool);
static void   gimp_transform_tool_bounds         (GimpTransformTool     *tr_tool,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_dialog         (GimpTransformTool     *tr_tool);
static void   gimp_transform_tool_prepare        (GimpTransformTool     *tr_tool,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_doit           (GimpTransformTool     *tr_tool,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_transform_bounding_box (GimpTransformTool *tr_tool);
static void   gimp_transform_tool_grid_recalc    (GimpTransformTool     *tr_tool);

static void   gimp_transform_tool_handles_recalc (GimpTransformTool     *tr_tool,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_force_expose_preview (GimpTransformTool *tr_tool);

static void   gimp_transform_tool_response       (GtkWidget             *widget,
                                                  gint                   response_id,
                                                  GimpTransformTool     *tr_tool);

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);
153 154


155
G_DEFINE_TYPE (GimpTransformTool, gimp_transform_tool, GIMP_TYPE_DRAW_TOOL)
156 157

#define parent_class gimp_transform_tool_parent_class
158

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

static void
gimp_transform_tool_class_init (GimpTransformToolClass *klass)
{
163 164 165
  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
166

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

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

181
  draw_class->draw                = gimp_transform_tool_draw;
Michael Natterer's avatar
Michael Natterer committed
182

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

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

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

Michael Natterer's avatar
Michael Natterer committed
199 200
  gimp_tool_control_set_scroll_lock (tool->control, TRUE);
  gimp_tool_control_set_preserve    (tool->control, FALSE);
201 202 203 204
  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
205

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

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

215
  gimp_matrix3_identity (&tr_tool->transform);
216

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

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

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

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

233 234
  tr_tool->undo_desc        = NULL;

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

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

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

  tool    = GIMP_TOOL (object);
  tr_tool = GIMP_TRANSFORM_TOOL (object);
253
  options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
254 255 256

  if (tr_tool->use_grid)
    {
257 258
      tr_tool->type      = options->type;
      tr_tool->direction = options->direction;
259

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

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

274
      g_signal_connect_object (options, "notify::preview-type",
275
                               G_CALLBACK (gimp_transform_tool_notify_preview),
276
                               tr_tool, 0);
277
      g_signal_connect_object (options, "notify::grid-type",
278
                               G_CALLBACK (gimp_transform_tool_notify_preview),
279
                               tr_tool, 0);
280
      g_signal_connect_object (options, "notify::grid-size",
281 282
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
283 284 285 286
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::preview-opacity",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
287 288
    }

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

293 294 295
  return object;
}

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

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

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

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

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

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

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

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

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

353
      gimp_transform_tool_prepare (tr_tool, display);
354 355

      /*  Recalculate the transform tool  */
356
      gimp_transform_tool_recalc (tr_tool, display);
357 358

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

      tr_tool->function = TRANSFORM_CREATING;

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

  return TRUE;
369 370
}

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

378 379
  switch (action)
    {
380
    case GIMP_TOOL_ACTION_PAUSE:
381
      break;
382

383
    case GIMP_TOOL_ACTION_RESUME:
384 385
      gimp_transform_tool_bounds (tr_tool, display);
      gimp_transform_tool_recalc (tr_tool, display);
386
      break;
387

388
    case GIMP_TOOL_ACTION_HALT:
389
      gimp_transform_tool_halt (tr_tool);
390 391 392
      break;
    }

393
  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
394 395 396
}

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

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

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

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

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

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

429
  if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
Nate Summers's avatar
Nate Summers committed
430 431
    {
      /* Shift-clicking is another way to approve the transform  */
432
      if ((state & GDK_SHIFT_MASK) || ! tr_tool->use_grid)
433
        {
434
          gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, tr_tool);
435
        }
Nate Summers's avatar
Nate Summers committed
436 437 438
    }
  else
    {
439
      gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
440

Michael Natterer's avatar
Michael Natterer committed
441 442 443
      /* 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
444
      /*  Restore the previous transformation info  */
445
      for (i = 0; i < TRANS_INFO_SIZE; i++)
446
        tr_tool->trans_info[i] = tr_tool->old_trans_info[i];
Nate Summers's avatar
Nate Summers committed
447

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

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

454
      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
455
    }
456 457

  gimp_tool_control_halt (tool->control);
Nate Summers's avatar
Nate Summers committed
458 459
}

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

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

474
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
475

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

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

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

487 488
      gimp_transform_tool_expose_preview (tr_tool);

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

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

495
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
496 497
}

498 499
#define RESPONSE_RESET 1

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

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

        case GDK_Delete:
        case GDK_BackSpace:
          gimp_transform_tool_response (NULL, RESPONSE_RESET, trans_tool);
521
          return TRUE;
522 523 524 525

        case GDK_Escape:
          gimp_transform_tool_response (NULL, GTK_RESPONSE_CANCEL, trans_tool);
          return TRUE;
526 527
        }
    }
528 529

  return FALSE;
530 531
}

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

541
  if (key == GDK_CONTROL_MASK)
542 543 544
    g_object_set (options,
                  "constrain", ! options->constrain,
                  NULL);
545 546
}

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

557 558 559
  tr_tool->function = TRANSFORM_HANDLE_NONE;

  if (display != tool->display)
560 561
    return;

562
  if (tr_tool->use_handles)
563 564 565 566
    {
      gdouble closest_dist;
      gdouble dist;

567 568 569 570
      dist = gimp_draw_tool_calc_distance_square (draw_tool, display,
                                                  coords->x, coords->y,
                                                  tr_tool->tx1, tr_tool->ty1);
      closest_dist = dist;
571
      tr_tool->function = TRANSFORM_HANDLE_NW;
572

573 574 575
      dist = gimp_draw_tool_calc_distance_square (draw_tool, display,
                                                  coords->x, coords->y,
                                                  tr_tool->tx2, tr_tool->ty2);
576
      if (dist < closest_dist)
577 578
        {
          closest_dist = dist;
579
          tr_tool->function = TRANSFORM_HANDLE_NE;
580
        }
581

582 583 584
      dist = gimp_draw_tool_calc_distance_square (draw_tool, display,
                                                  coords->x, coords->y,
                                                  tr_tool->tx3, tr_tool->ty3);
585
      if (dist < closest_dist)
586 587
        {
          closest_dist = dist;
588
          tr_tool->function = TRANSFORM_HANDLE_SW;
589
        }
590

591 592 593
      dist = gimp_draw_tool_calc_distance_square (draw_tool, display,
                                                  coords->x, coords->y,
                                                  tr_tool->tx4, tr_tool->ty4);
594
      if (dist < closest_dist)
595 596
        {
          closest_dist = dist;
597
          tr_tool->function = TRANSFORM_HANDLE_SE;
598
        }
599 600 601 602 603 604 605

      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;
606

607 608 609 610
          if (gimp_draw_tool_on_handle (draw_tool, display,
                                        coords->x, coords->y,
                                        GIMP_HANDLE_SQUARE,
                                        x, y,
611
                                        tr_tool->handle_w, tr_tool->handle_h,
612 613 614 615 616 617 618 619
                                        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;
620

621 622 623 624
          if (gimp_draw_tool_on_handle (draw_tool, display,
                                        coords->x, coords->y,
                                        GIMP_HANDLE_SQUARE,
                                        x, y,
625
                                        tr_tool->handle_w, tr_tool->handle_h,
626 627 628 629 630 631 632 633
                                        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;
634

635 636 637 638
          if (gimp_draw_tool_on_handle (draw_tool, display,
                                        coords->x, coords->y,
                                        GIMP_HANDLE_SQUARE,
                                        x, y,
639
                                        tr_tool->handle_w, tr_tool->handle_h,
640 641 642 643 644 645 646 647
                                        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;
648

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

662 663 664 665 666
  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,
667 668
                                MIN (tr_tool->handle_w, tr_tool->handle_h),
                                MIN (tr_tool->handle_w, tr_tool->handle_h),
669 670 671 672
                                GTK_ANCHOR_CENTER,
                                FALSE))
    {
      tr_tool->function = TRANSFORM_HANDLE_CENTER;
673 674 675
    }
}

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

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

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

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

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

705
        case TRANSFORM_HANDLE_SE:
Michael Natterer's avatar
Michael Natterer committed
706
          cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
707
          break;
708

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
        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;

725
        default:
Michael Natterer's avatar
Michael Natterer committed
726 727
          cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
          break;
Michael Natterer's avatar
Michael Natterer committed
728
        }
729
    }
Michael Natterer's avatar
Michael Natterer committed
730

731 732 733
  if (tr_tool->use_center && tr_tool->function == TRANSFORM_HANDLE_CENTER)
    {
      modifier = GIMP_CURSOR_MODIFIER_MOVE;
Michael Natterer's avatar
Michael Natterer committed
734
    }
735

736 737 738 739 740 741 742 743 744 745 746 747
  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 = GIMP_CURSOR_MODIFIER_BAD;
      break;
    }

Michael Natterer's avatar
Michael Natterer committed
748
  gimp_tool_control_set_cursor          (tool->control, cursor);
749 750
  gimp_tool_control_set_cursor_modifier (tool->control, modifier);

751
  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
Nate Summers's avatar
Nate Summers committed
752 753
}

754
static void
755
gimp_transform_tool_draw (GimpDrawTool *draw_tool)
Nate Summers's avatar
Nate Summers committed
756
{
757 758 759
  GimpTool          *tool    = GIMP_TOOL (draw_tool);
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (draw_tool);
  gdouble            z1, z2, z3, z4;
Nate Summers's avatar
Nate Summers committed
760

761
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
762
    {
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
      /*  draw the bounding box  */
      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);

      /* We test if the transformed polygon is convex.
       * if z1 and z2 have the same sign as well as z3 and z4
       * the polygon is convex.
       */
      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));

794 795 796 797
      /*  draw the grid  */
      if (tr_tool->grid_coords  &&
          tr_tool->tgrid_coords &&
          z1 * z2 > 0           &&
798
          z3 * z4 > 0)
799
        {
800 801 802 803 804 805 806 807 808 809 810 811 812
          gint gci, i, k;

          k = tr_tool->ngx + tr_tool->ngy;

          for (i = 0, gci = 0; i < k; i++, gci += 4)
            {
              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);
            }
813
        }
Nate Summers's avatar
Nate Summers committed
814 815
    }

816 817
  gimp_transform_tool_handles_recalc (tr_tool, tool->display);

818 819 820 821 822 823
  if (tr_tool->use_handles)
    {
      /*  draw the tool handles  */
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx1, tr_tool->ty1,
824
                                  tr_tool->handle_w, tr_tool->handle_h,
825 826 827 828 829
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx2, tr_tool->ty2,
830
                                  tr_tool->handle_w, tr_tool->handle_h,
831 832 833 834 835
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx3, tr_tool->ty3,
836
                                  tr_tool->handle_w, tr_tool->handle_h,
837 838 839 840 841
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx4, tr_tool->ty4,
Sven Neumann's avatar