gimptransformtool.c 65.4 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
 * 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>

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

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

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

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

36
#include "core/gimp.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 "display/gimpdisplay.h"
56 57 58
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-appearance.h"
#include "display/gimpdisplayshell-transform.h"
Nate Summers's avatar
Nate Summers committed
59

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

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

67

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

71

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

74 75 76 77 78 79 80 81 82 83 84 85
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,
86
                                                  const GimpCoords      *coords,
87 88 89 90
                                                  guint32                time,
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_button_release (GimpTool              *tool,
91
                                                  const GimpCoords      *coords,
92 93 94 95 96
                                                  guint32                time,
                                                  GdkModifierType        state,
                                                  GimpButtonReleaseType  release_type,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_motion         (GimpTool              *tool,
97
                                                  const GimpCoords      *coords,
98 99 100 101 102 103 104 105 106 107 108 109
                                                  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,
110
                                                  const GimpCoords      *coords,
111 112 113 114
                                                  GdkModifierType        state,
                                                  gboolean               proximity,
                                                  GimpDisplay           *display);
static void   gimp_transform_tool_cursor_update  (GimpTool              *tool,
115
                                                  const GimpCoords      *coords,
116 117 118 119 120 121
                                                  GdkModifierType        state,
                                                  GimpDisplay           *display);

static void   gimp_transform_tool_draw           (GimpDrawTool          *draw_tool);

static void   gimp_transform_tool_dialog_update  (GimpTransformTool     *tr_tool);
122

123
static TileManager *
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 153
              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);
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 169
  object_class->constructor       = gimp_transform_tool_constructor;
  object_class->finalize          = gimp_transform_tool_finalize;
Nate Summers's avatar
Nate Summers committed
170

171 172 173 174 175 176 177
  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;
178
  tool_class->active_modifier_key = gimp_transform_tool_modifier_key;
179 180
  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
181

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

184 185 186 187 188 189
  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
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
{
195
  GimpTool *tool = GIMP_TOOL (tr_tool);
196
  gint      i;
Nate Summers's avatar
Nate Summers committed
197

198 199
  gimp_tool_control_set_action_value_1 (tool->control,
                                        "tools/tools-transform-preview-opacity-set");
Michael Natterer's avatar
Michael Natterer committed
200

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

Michael Natterer's avatar
Michael Natterer committed
210 211
  tr_tool->function = TRANSFORM_CREATING;
  tr_tool->original = NULL;
Nate Summers's avatar
Nate Summers committed
212

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

219
  gimp_matrix3_identity (&tr_tool->transform);
220

221 222 223
  tr_tool->use_grid         = FALSE;
  tr_tool->use_handles      = FALSE;
  tr_tool->use_center       = FALSE;
224
  tr_tool->use_mid_handles  = FALSE;
225 226 227 228

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

229 230 231 232 233
  tr_tool->ngx              = 0;
  tr_tool->ngy              = 0;
  tr_tool->grid_coords      = NULL;
  tr_tool->tgrid_coords     = NULL;

234
  tr_tool->type             = GIMP_TRANSFORM_TYPE_LAYER;
235
  tr_tool->direction        = GIMP_TRANSFORM_FORWARD;
236

237 238
  tr_tool->undo_desc        = NULL;

239
  tr_tool->progress_text    = _("Transforming");
240
  tr_tool->dialog           = NULL;
Nate Summers's avatar
Nate Summers committed
241 242
}

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

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

  tool    = GIMP_TOOL (object);
  tr_tool = GIMP_TRANSFORM_TOOL (object);
257
  options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
258 259 260

  if (tr_tool->use_grid)
    {
261 262
      tr_tool->type      = options->type;
      tr_tool->direction = options->direction;
263

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

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

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

293
  g_signal_connect_object (options, "notify::constrain",
294 295 296
                           G_CALLBACK (gimp_transform_tool_dialog_update),
                           tr_tool, G_CONNECT_SWAPPED);

297 298 299
  return object;
}

300
static void
301
gimp_transform_tool_finalize (GObject *object)
Nate Summers's avatar
Nate Summers committed
302
{
303
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (object);
304 305

  if (tr_tool->original)
306
    {
307
      tile_manager_unref (tr_tool->original);
308 309
      tr_tool->original = NULL;
    }
310

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

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

332
static gboolean
333 334 335
gimp_transform_tool_initialize (GimpTool     *tool,
                                GimpDisplay  *display,
                                GError      **error)
336 337 338
{
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);

339
  if (display != tool->display)
340
    {
341
      gint i;
342 343

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

      /*  Initialize the transform tool dialog */
348
      if (! tr_tool->dialog)
349 350 351 352 353 354
        gimp_transform_tool_dialog (tr_tool);

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

357
      gimp_transform_tool_prepare (tr_tool, display);
358 359

      /*  Recalculate the transform tool  */
360
      gimp_transform_tool_recalc (tr_tool, display);
361 362

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

365 366
      gimp_transform_tool_expose_preview (tr_tool);

367 368 369
      tr_tool->function = TRANSFORM_CREATING;

      /*  Save the current transformation info  */
370
      for (i = 0; i < TRANS_INFO_SIZE; i++)
371
        tr_tool->old_trans_info[i] = tr_tool->trans_info[i];
372
    }
373 374

  return TRUE;
375 376
}

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

384 385
  switch (action)
    {
386
    case GIMP_TOOL_ACTION_PAUSE:
387
      break;
388

389
    case GIMP_TOOL_ACTION_RESUME:
390 391
      gimp_transform_tool_bounds (tr_tool, display);
      gimp_transform_tool_recalc (tr_tool, display);
392
      break;
393

394
    case GIMP_TOOL_ACTION_HALT:
395
      gimp_transform_tool_halt (tr_tool);
396 397 398
      break;
    }

399
  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
400 401 402
}

static void
403 404 405 406 407
gimp_transform_tool_button_press (GimpTool         *tool,
                                  const GimpCoords *coords,
                                  guint32           time,
                                  GdkModifierType   state,
                                  GimpDisplay      *display)
Nate Summers's avatar
Nate Summers committed
408
{
409
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
410

411
  if (tr_tool->function == TRANSFORM_CREATING)
412
    gimp_transform_tool_oper_update (tool, coords, state, TRUE, display);
413

414 415
  tr_tool->lastx = tr_tool->startx = coords->x;
  tr_tool->lasty = tr_tool->starty = coords->y;
Nate Summers's avatar
Nate Summers committed
416

417
  gimp_tool_control_activate (tool->control);
Nate Summers's avatar
Nate Summers committed
418 419
}

420
static void
421
gimp_transform_tool_button_release (GimpTool              *tool,
422
                                    const GimpCoords      *coords,
423 424 425 426
                                    guint32                time,
                                    GdkModifierType        state,
                                    GimpButtonReleaseType  release_type,
                                    GimpDisplay           *display)
Nate Summers's avatar
Nate Summers committed
427
{
428
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
429
  gint               i;
Nate Summers's avatar
Nate Summers committed
430 431

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

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

Michael Natterer's avatar
Michael Natterer committed
447 448 449
      /* 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
450
      /*  Restore the previous transformation info  */
451
      for (i = 0; i < TRANS_INFO_SIZE; i++)
452
        tr_tool->trans_info[i] = tr_tool->old_trans_info[i];
Nate Summers's avatar
Nate Summers committed
453

Michael Natterer's avatar
Michael Natterer committed
454
      /*  reget the selection bounds  */
455
      gimp_transform_tool_bounds (tr_tool, display);
Michael Natterer's avatar
Michael Natterer committed
456

Nate Summers's avatar
Nate Summers committed
457
      /*  recalculate the tool's transformation matrix  */
458
      gimp_transform_tool_recalc (tr_tool, display);
Nate Summers's avatar
Nate Summers committed
459

460
      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
461
    }
462 463

  gimp_tool_control_halt (tool->control);
Nate Summers's avatar
Nate Summers committed
464 465
}

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

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

480
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
481

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

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

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

493
      gimp_transform_tool_recalc (tr_tool, display);
494 495

      gimp_transform_tool_expose_preview (tr_tool);
Michael Natterer's avatar
Michael Natterer committed
496 497 498 499
    }

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

501
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
502 503
}

504 505
#define RESPONSE_RESET 1

506
static gboolean
507 508
gimp_transform_tool_key_press (GimpTool    *tool,
                               GdkEventKey *kevent,
509
                               GimpDisplay *display)
510 511
{
  GimpTransformTool *trans_tool = GIMP_TRANSFORM_TOOL (tool);
512
  GimpDrawTool      *draw_tool  = GIMP_DRAW_TOOL (tool);
513

514
  if (display == draw_tool->display)
515 516 517
    {
      switch (kevent->keyval)
        {
518
        case GDK_Return:
519 520
        case GDK_KP_Enter:
        case GDK_ISO_Enter:
521
          gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, trans_tool);
522
          return TRUE;
523 524 525

        case GDK_BackSpace:
          gimp_transform_tool_response (NULL, RESPONSE_RESET, trans_tool);
526
          return TRUE;
527 528 529 530

        case GDK_Escape:
          gimp_transform_tool_response (NULL, GTK_RESPONSE_CANCEL, trans_tool);
          return TRUE;
531 532
        }
    }
533 534

  return FALSE;
535 536
}

537 538 539 540 541
static void
gimp_transform_tool_modifier_key (GimpTool        *tool,
                                  GdkModifierType  key,
                                  gboolean         press,
                                  GdkModifierType  state,
542
                                  GimpDisplay     *display)
543
{
544
  GimpTransformOptions *options = GIMP_TRANSFORM_TOOL_GET_OPTIONS (tool);
545

546
  if (key == GDK_CONTROL_MASK)
547 548 549
    g_object_set (options,
                  "constrain", ! options->constrain,
                  NULL);
550 551
}

552
static void
553 554 555 556 557
gimp_transform_tool_oper_update (GimpTool         *tool,
                                 const GimpCoords *coords,
                                 GdkModifierType   state,
                                 gboolean          proximity,
                                 GimpDisplay      *display)
558
{
559 560
  GimpTransformTool *tr_tool   = GIMP_TRANSFORM_TOOL (tool);
  GimpDrawTool      *draw_tool = GIMP_DRAW_TOOL (tool);
561

562 563 564
  tr_tool->function = TRANSFORM_HANDLE_NONE;

  if (display != tool->display)
565 566
    return;

567
  if (tr_tool->use_handles)
568 569 570 571
    {
      gdouble closest_dist;
      gdouble dist;

572 573 574 575
      dist = gimp_draw_tool_calc_distance_square (draw_tool, display,
                                                  coords->x, coords->y,
                                                  tr_tool->tx1, tr_tool->ty1);
      closest_dist = dist;
576
      tr_tool->function = TRANSFORM_HANDLE_NW;
577

578 579 580
      dist = gimp_draw_tool_calc_distance_square (draw_tool, display,
                                                  coords->x, coords->y,
                                                  tr_tool->tx2, tr_tool->ty2);
581
      if (dist < closest_dist)
582 583
        {
          closest_dist = dist;
584
          tr_tool->function = TRANSFORM_HANDLE_NE;
585
        }
586

587 588 589
      dist = gimp_draw_tool_calc_distance_square (draw_tool, display,
                                                  coords->x, coords->y,
                                                  tr_tool->tx3, tr_tool->ty3);
590
      if (dist < closest_dist)
591 592
        {
          closest_dist = dist;
593
          tr_tool->function = TRANSFORM_HANDLE_SW;
594
        }
595

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

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

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

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

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

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

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

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

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

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

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

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

710
        case TRANSFORM_HANDLE_SE:
Michael Natterer's avatar
Michael Natterer committed
711
          cursor = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
712
          break;
713

714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
        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;

730
        default:
Michael Natterer's avatar
Michael Natterer committed
731 732
          cursor = GIMP_CURSOR_CROSSHAIR_SMALL;
          break;
Michael Natterer's avatar
Michael Natterer committed
733
        }
734
    }
Michael Natterer's avatar
Michael Natterer committed
735

736 737 738
  if (tr_tool->use_center && tr_tool->function == TRANSFORM_HANDLE_CENTER)
    {
      modifier = GIMP_CURSOR_MODIFIER_MOVE;
Michael Natterer's avatar
Michael Natterer committed
739
    }
740

741 742 743 744 745 746 747 748 749 750 751 752
  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
753
  gimp_tool_control_set_cursor          (tool->control, cursor);
754 755
  gimp_tool_control_set_cursor_modifier (tool->control, modifier);

756
  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
Nate Summers's avatar
Nate Summers committed
757 758
}

759
static void
760
gimp_transform_tool_draw (GimpDrawTool *draw_tool)
Nate Summers's avatar
Nate Summers committed
761
{
762 763 764
  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
765

766
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
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 795 796 797 798
      /*  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));

799 800 801 802
      /*  draw the grid  */
      if (tr_tool->grid_coords  &&
          tr_tool->tgrid_coords &&
          z1 * z2 > 0           &&
803
          z3 * z4 > 0)
804
        {
805 806 807 808 809 <