gimptransformtool.c 65.1 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
{
194
  GimpTool *tool = GIMP_TOOL (tr_tool);
195
  gint      i;
Nate Summers's avatar
Nate Summers committed
196

197 198
  gimp_tool_control_set_action_value_1 (tool->control,
                                        "tools/tools-transform-preview-opacity-set");
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->progress_text    = _("Transforming");
237
  tr_tool->dialog           = NULL;
Nate Summers's avatar
Nate Summers committed
238 239
}

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

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

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

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

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

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

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

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

294 295 296
  return object;
}

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

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

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

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

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

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

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

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

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

354
      gimp_transform_tool_prepare (tr_tool, display);
355 356

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

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

      tr_tool->function = TRANSFORM_CREATING;

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

  return TRUE;
370 371
}

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

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

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

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

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

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

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

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

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

415
static void
416 417 418 419 420 421
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
422
{
423
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
424
  gint               i;
Nate Summers's avatar
Nate Summers committed
425 426

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

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

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

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

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

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

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

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

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

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

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

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

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

488 489
      gimp_transform_tool_expose_preview (tr_tool);

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

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

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

499 500
#define RESPONSE_RESET 1

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

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

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

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

  return FALSE;
531 532
}

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

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

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

558 559 560
  tr_tool->function = TRANSFORM_HANDLE_NONE;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

737 738 739 740 741 742 743 744 745 746 747 748
  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
749
  gimp_tool_control_set_cursor          (tool->control, cursor);
750 751
  gimp_tool_control_set_cursor_modifier (tool->control, modifier);

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

755
static void
756
gimp_transform_tool_draw (GimpDrawTool *draw_tool)
Nate Summers's avatar
Nate Summers committed
757
{
758 759 760
  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
761

762
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
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 794
      /*  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));

795 796 797 798
      /*  draw the grid  */
      if (tr_tool->grid_coords  &&
          tr_tool->tgrid_coords &&
          z1 * z2 > 0           &&
799
          z3 * z4 > 0)
800
        {
801 802 803 804 805 806 807 808 809 810 811 812 813
          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);
            }
814
        }
Nate Summers's avatar
Nate Summers committed
815 816
    }

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

819 820 821 822 823 824
  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,
825
                                  tr_tool->handle_w, tr_tool->handle_h,
826 827 828 829 830
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx2, tr_tool->ty2,
831
                                  tr_tool->handle_w, tr_tool->handle_h,
832 833 834 835 836
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx3, tr_tool->ty3,
837
                                  tr_tool->handle_w, tr_tool->handle_h,
838 839 840 841 842
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx4, tr_tool->ty4,