gimptransformtool.c 64.8 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
  g_signal_connect_object (options, "notify::constrain",
286 287 288
                           G_CALLBACK (gimp_transform_tool_dialog_update),
                           tr_tool, G_CONNECT_SWAPPED);

289 290 291
  return object;
}

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

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

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

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

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

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

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

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

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

349
      gimp_transform_tool_prepare (tr_tool, display);
350 351

      /*  Recalculate the transform tool  */
352
      gimp_transform_tool_recalc (tr_tool, display);
353 354

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

      tr_tool->function = TRANSFORM_CREATING;

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

  return TRUE;
365 366
}

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

374 375
  switch (action)
    {
376
    case GIMP_TOOL_ACTION_PAUSE:
377
      break;
378

379
    case GIMP_TOOL_ACTION_RESUME:
380 381
      gimp_transform_tool_bounds (tr_tool, display);
      gimp_transform_tool_recalc (tr_tool, display);
382
      break;
383

384
    case GIMP_TOOL_ACTION_HALT:
385
      gimp_transform_tool_halt (tr_tool);
386 387 388
      break;
    }

389
  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
390 391 392
}

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

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

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

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

410
static void
411 412 413 414 415 416
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
417
{
418
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
419
  gint               i;
Nate Summers's avatar
Nate Summers committed
420 421

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

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

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

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

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

450
      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
451
    }
452 453

  gimp_tool_control_halt (tool->control);
Nate Summers's avatar
Nate Summers committed
454 455
}

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

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

470
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
471

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

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

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

483 484
      gimp_transform_tool_expose_preview (tr_tool);

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

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

491
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
492 493
}

494 495
#define RESPONSE_RESET 1

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

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

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

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

  return FALSE;
525 526
}

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

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

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

552 553 554
  tr_tool->function = TRANSFORM_HANDLE_NONE;

  if (display != tool->display)
555 556
    return;

557
  if (tr_tool->use_handles)
558 559 560 561
    {
      gdouble closest_dist;
      gdouble dist;

562 563 564 565
      dist = gimp_draw_tool_calc_distance_square (draw_tool, display,
                                                  coords->x, coords->y,
                                                  tr_tool->tx1, tr_tool->ty1);
      closest_dist = dist;
566
      tr_tool->function = TRANSFORM_HANDLE_NW;
567

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

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

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

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

602 603 604 605
          if (gimp_draw_tool_on_handle (draw_tool, display,
                                        coords->x, coords->y,
                                        GIMP_HANDLE_SQUARE,
                                        x, y,
606
                                        tr_tool->handle_w, tr_tool->handle_h,
607 608 609 610 611 612 613 614
                                        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;
615

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

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

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

657 658 659 660 661
  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,
662 663
                                MIN (tr_tool->handle_w, tr_tool->handle_h),
                                MIN (tr_tool->handle_w, tr_tool->handle_h),
664 665 666 667
                                GTK_ANCHOR_CENTER,
                                FALSE))
    {
      tr_tool->function = TRANSFORM_HANDLE_CENTER;
668 669 670
    }
}

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

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

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

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

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

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

704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
        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;

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

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

731 732 733 734 735 736 737 738 739 740 741 742
  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
743
  gimp_tool_control_set_cursor          (tool->control, cursor);
744 745
  gimp_tool_control_set_cursor_modifier (tool->control, modifier);

746
  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
Nate Summers's avatar
Nate Summers committed
747 748
}

749
static void
750
gimp_transform_tool_draw (GimpDrawTool *draw_tool)
Nate Summers's avatar
Nate Summers committed
751
{
752 753 754
  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
755

756
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
757
    {
758 759 760 761 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
      /*  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));

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

811 812
  gimp_transform_tool_handles_recalc (tr_tool, tool->display);

813 814 815 816 817 818
  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,
819
                                  tr_tool->handle_w, tr_tool->handle_h,
820 821 822 823 824
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx2, tr_tool->ty2,
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->tx3, tr_tool->ty3,
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->tx4, tr_tool->ty4,
837
                                  tr_tool->handle_w, tr_tool->handle_h,
838 839
                                  GTK_ANCHOR_CENTER,
                                  FALSE);