gimptransformtool.c 55 KB
Newer Older
Nate Summers's avatar
Nate Summers committed
1
/* The GIMP -- an 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

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

Michael Natterer's avatar
Michael Natterer committed
34
#include "core/gimp.h"
35
#include "core/gimpchannel.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 "widgets/gimpviewabledialog.h"

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 63
#include "gimptransformtool.h"
#include "gimptransformtool-undo.h"
64

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

67

68
#define HANDLE_SIZE 10
Michael Natterer's avatar
Michael Natterer committed
69

70

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

73 74 75
static GObject * gimp_transform_tool_constructor   (GType              type,
                                                    guint              n_params,
                                                    GObjectConstructParam *params);
76
static void     gimp_transform_tool_finalize       (GObject           *object);
Michael Natterer's avatar
Michael Natterer committed
77

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

116
static void     gimp_transform_tool_draw           (GimpDrawTool      *draw_tool);
Michael Natterer's avatar
Michael Natterer committed
117

118 119
static void     gimp_transform_tool_dialog_update  (GimpTransformTool *tr_tool);

120
static TileManager *
121
                gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
122
                                                    GimpItem          *item,
123
                                                    gboolean           mask_empty,
124
                                                    GimpDisplay       *display);
125

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

137 138
static void     gimp_transform_tool_force_expose_preview (GimpTransformTool *tr_tool);

139
static void     gimp_transform_tool_response       (GtkWidget         *widget,
140
                                                    gint               response_id,
Michael Natterer's avatar
Michael Natterer committed
141
                                                    GimpTransformTool *tr_tool);
142

143 144 145 146 147 148
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);
149 150


151
G_DEFINE_TYPE (GimpTransformTool, gimp_transform_tool, GIMP_TYPE_DRAW_TOOL)
152 153

#define parent_class gimp_transform_tool_parent_class
154

Nate Summers's avatar
Nate Summers committed
155 156 157 158

static void
gimp_transform_tool_class_init (GimpTransformToolClass *klass)
{
159 160 161
  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
162

163
  object_class->constructor  = gimp_transform_tool_constructor;
164
  object_class->finalize     = gimp_transform_tool_finalize;
Nate Summers's avatar
Nate Summers committed
165

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

  draw_class->draw           = gimp_transform_tool_draw;
Michael Natterer's avatar
Michael Natterer committed
177 178

  klass->dialog              = NULL;
Michael Natterer's avatar
Michael Natterer committed
179
  klass->dialog_update       = NULL;
Michael Natterer's avatar
Michael Natterer committed
180 181 182
  klass->prepare             = NULL;
  klass->motion              = NULL;
  klass->recalc              = NULL;
183
  klass->transform           = gimp_transform_tool_real_transform;
Nate Summers's avatar
Nate Summers committed
184 185 186
}

static void
Michael Natterer's avatar
Michael Natterer committed
187
gimp_transform_tool_init (GimpTransformTool *tr_tool)
Nate Summers's avatar
Nate Summers committed
188
{
Michael Natterer's avatar
Michael Natterer committed
189
  GimpTool *tool;
190
  gint      i;
Nate Summers's avatar
Nate Summers committed
191

Michael Natterer's avatar
Michael Natterer committed
192
  tool = GIMP_TOOL (tr_tool);
Michael Natterer's avatar
Michael Natterer committed
193

Michael Natterer's avatar
Michael Natterer committed
194 195
  gimp_tool_control_set_scroll_lock (tool->control, TRUE);
  gimp_tool_control_set_preserve    (tool->control, FALSE);
196 197 198 199
  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
200

Michael Natterer's avatar
Michael Natterer committed
201 202
  tr_tool->function = TRANSFORM_CREATING;
  tr_tool->original = NULL;
Nate Summers's avatar
Nate Summers committed
203 204

  for (i = 0; i < TRAN_INFO_SIZE; i++)
205
    {
Michael Natterer's avatar
Michael Natterer committed
206 207
      tr_tool->trans_info[i]     = 0.0;
      tr_tool->old_trans_info[i] = 0.0;
208
    }
Nate Summers's avatar
Nate Summers committed
209

210
  gimp_matrix3_identity (&tr_tool->transform);
211

212 213 214
  tr_tool->use_grid         = FALSE;
  tr_tool->use_handles      = FALSE;
  tr_tool->use_center       = FALSE;
215 216 217 218 219
  tr_tool->ngx              = 0;
  tr_tool->ngy              = 0;
  tr_tool->grid_coords      = NULL;
  tr_tool->tgrid_coords     = NULL;

220
  tr_tool->type             = GIMP_TRANSFORM_TYPE_LAYER;
221
  tr_tool->direction        = GIMP_TRANSFORM_FORWARD;
222

223
  tr_tool->shell_desc       = NULL;
224
  tr_tool->progress_text    = _("Transforming");
225
  tr_tool->dialog           = NULL;
Nate Summers's avatar
Nate Summers committed
226 227
}

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
static GObject *
gimp_transform_tool_constructor (GType                  type,
                                 guint                  n_params,
                                 GObjectConstructParam *params)
{
  GObject           *object;
  GimpTool          *tool;
  GimpTransformTool *tr_tool;

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

  tool    = GIMP_TOOL (object);
  tr_tool = GIMP_TRANSFORM_TOOL (object);

  g_assert (GIMP_IS_TOOL_INFO (tool->tool_info));

  if (tr_tool->use_grid)
    {
      tr_tool->type =
        GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options)->type;
248

249 250 251 252 253 254 255
      tr_tool->direction =
        GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options)->direction;

      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::type",
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
256 257 258 259
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
260

261 262 263 264
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
265 266 267 268
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
269

270
      g_signal_connect_object (tool->tool_info->tool_options,
271 272
                               "notify::preview-type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
273 274
                               tr_tool, 0);
      g_signal_connect_object (tool->tool_info->tool_options,
275 276
                               "notify::grid-type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
277
                               tr_tool, 0);
278
      g_signal_connect_object (tool->tool_info->tool_options,
279
                               "notify::grid-size",
280 281
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
282 283
    }

284 285 286 287 288
  g_signal_connect_object (tool->tool_info->tool_options,
                           "notify::constrain",
                           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
gimp_transform_tool_initialize (GimpTool    *tool,
326
                                GimpDisplay *display)
327 328 329
{
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);

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

      /*  Set the pointer to the active display  */
335 336
      tool->display    = display;
      tool->drawable = gimp_image_active_drawable (display->image);
337 338

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

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

348
      gimp_transform_tool_prepare (tr_tool, display);
349 350

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

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

      tr_tool->function = TRANSFORM_CREATING;

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

  return TRUE;
364 365
}

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

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

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

383
    case GIMP_TOOL_ACTION_HALT:
384
      gimp_transform_tool_halt (tr_tool);
385
      return; /* don't upchain */
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
gimp_transform_tool_button_release (GimpTool        *tool,
                                    GimpCoords      *coords,
                                    guint32          time,
414
                                    GdkModifierType  state,
415
                                    GimpDisplay     *display)
Nate Summers's avatar
Nate Summers committed
416
{
417
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
418
  gint               i;
Nate Summers's avatar
Nate Summers committed
419 420

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

  /*  if the 3rd button isn't pressed, transform the selected mask  */
425
  if (! (state & GDK_BUTTON3_MASK))
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_doit (tr_tool, display);
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 441
      /*  Restore the previous transformation info  */
      for (i = 0; i < TRAN_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;
535

536
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
537

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

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

554 555 556
  tr_tool->function = TRANSFORM_HANDLE_NONE;

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

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

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

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

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

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

597 598 599 600 601 602 603 604 605 606
  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,
                                HANDLE_SIZE, HANDLE_SIZE,
                                GTK_ANCHOR_CENTER,
                                FALSE))
    {
      tr_tool->function = TRANSFORM_HANDLE_CENTER;
607 608 609
    }
}

610
static void
611 612
gimp_transform_tool_cursor_update (GimpTool        *tool,
                                   GimpCoords      *coords,
613
                                   GdkModifierType  state,
614
                                   GimpDisplay     *display)
Nate Summers's avatar
Nate Summers committed
615
{
616 617 618
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (tool);
  GimpTransformOptions *options;
  GimpCursorModifier    modifier = GIMP_CURSOR_MODIFIER_NONE;
Nate Summers's avatar
Nate Summers committed
619

620 621 622
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);

  if (tr_tool->use_handles)
Nate Summers's avatar
Nate Summers committed
623
    {
624 625 626 627 628 629
      switch (tr_tool->function)
        {
        case TRANSFORM_HANDLE_NW:
          gimp_tool_control_set_cursor (tool->control,
                                        GIMP_CURSOR_CORNER_TOP_LEFT);
          break;
Sven Neumann's avatar
Sven Neumann committed
630

631 632 633 634
        case TRANSFORM_HANDLE_NE:
          gimp_tool_control_set_cursor (tool->control,
                                        GIMP_CURSOR_CORNER_TOP_RIGHT);
          break;
635

636 637 638
        case TRANSFORM_HANDLE_SW:
          gimp_tool_control_set_cursor (tool->control,
                                        GIMP_CURSOR_CORNER_BOTTOM_LEFT);
639 640
          break;

641 642 643
        case TRANSFORM_HANDLE_SE:
          gimp_tool_control_set_cursor (tool->control,
                                        GIMP_CURSOR_CORNER_BOTTOM_RIGHT);
644
          break;
645

646 647
        default:
          gimp_tool_control_set_cursor (tool->control, GIMP_CURSOR_MOUSE);
Michael Natterer's avatar
Michael Natterer committed
648
        }
649
    }
Michael Natterer's avatar
Michael Natterer committed
650

651 652 653
  if (tr_tool->use_center && tr_tool->function == TRANSFORM_HANDLE_CENTER)
    {
      modifier = GIMP_CURSOR_MODIFIER_MOVE;
Michael Natterer's avatar
Michael Natterer committed
654
    }
655

656 657 658 659 660 661 662 663 664 665 666 667 668 669
  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;
    }

  gimp_tool_control_set_cursor_modifier (tool->control, modifier);

670
  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
Nate Summers's avatar
Nate Summers committed
671 672
}

673
static void
674
gimp_transform_tool_draw (GimpDrawTool *draw_tool)
Nate Summers's avatar
Nate Summers committed
675
{
676 677
  GimpTool             *tool    = GIMP_TOOL (draw_tool);
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (draw_tool);
678
  GimpTransformOptions *options;
679
  gdouble               z1, z2, z3, z4;
Nate Summers's avatar
Nate Summers committed
680

681
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
682
    {
683
      options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
684

685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
      /*  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));

      /*  Draw the grid (not for path transform since it looks ugly)  */

      if (tr_tool->type != GIMP_TRANSFORM_TYPE_PATH &&
          tr_tool->grid_coords                      &&
          tr_tool->tgrid_coords                     &&
          z1 * z2 > 0                               &&
          z3 * z4 > 0)
723
        {
724 725 726 727 728 729 730 731 732 733 734 735 736
          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);
            }
737
        }
Nate Summers's avatar
Nate Summers committed
738 739
    }

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
  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,
                                  HANDLE_SIZE, HANDLE_SIZE,
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx2, tr_tool->ty2,
                                  HANDLE_SIZE, HANDLE_SIZE,
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx3, tr_tool->ty3,
                                  HANDLE_SIZE, HANDLE_SIZE,
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_SQUARE,
                                  tr_tool->tx4, tr_tool->ty4,
                                  HANDLE_SIZE, HANDLE_SIZE,
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
    }
Nate Summers's avatar
Nate Summers committed
768 769

  /*  draw the center  */
770 771 772 773 774
  if (tr_tool->use_center)
    {
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_FILLED_CIRCLE,
                                  tr_tool->tcx, tr_tool->tcy,
775
                                  HANDLE_SIZE, HANDLE_SIZE,
776 777 778
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
    }
Nate Summers's avatar
Nate Summers committed
779

780
  if (tr_tool->type == GIMP_TRANSFORM_TYPE_PATH)