gimptransformtool.c 54.6 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 215 216 217 218
  tr_tool->use_grid         = TRUE;
  tr_tool->use_center       = TRUE;
  tr_tool->ngx              = 0;
  tr_tool->ngy              = 0;
  tr_tool->grid_coords      = NULL;
  tr_tool->tgrid_coords     = NULL;

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

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

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
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;
      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);
254 255 256 257
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
258

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

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

282 283 284 285 286
  g_signal_connect_object (tool->tool_info->tool_options,
                           "notify::constrain",
                           G_CALLBACK (gimp_transform_tool_dialog_update),
                           tr_tool, G_CONNECT_SWAPPED);

287 288 289
  return object;
}

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

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

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

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

322
static gboolean
323
gimp_transform_tool_initialize (GimpTool    *tool,
324
                                GimpDisplay *display)
325 326 327
{
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);

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

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

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

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

346
      gimp_transform_tool_prepare (tr_tool, display);
347 348

      /*  Recalculate the transform tool  */
349
      gimp_transform_tool_recalc (tr_tool, display);
350 351

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

      tr_tool->function = TRANSFORM_CREATING;

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

  return TRUE;
362 363
}

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

371 372 373 374
  switch (action)
    {
    case PAUSE:
      break;
375

376
    case RESUME:
377 378
      gimp_transform_tool_bounds (tr_tool, display);
      gimp_transform_tool_recalc (tr_tool, display);
379
      break;
380

381
    case HALT:
382
      gimp_transform_tool_halt (tr_tool);
383
      return; /* don't upchain */
384
      break;
385

386 387 388 389
    default:
      break;
    }

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

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

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

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

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

411
static void
412 413 414
gimp_transform_tool_button_release (GimpTool        *tool,
                                    GimpCoords      *coords,
                                    guint32          time,
415
                                    GdkModifierType  state,
416
                                    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 425
    return;

  /*  if the 3rd button isn't pressed, transform the selected mask  */
426
  if (! (state & GDK_BUTTON3_MASK))
Nate Summers's avatar
Nate Summers committed
427 428
    {
      /* Shift-clicking is another way to approve the transform  */
429
      if ((state & GDK_SHIFT_MASK) || ! tr_tool->use_grid)
430
        {
431
          gimp_transform_tool_doit (tr_tool, display);
432
        }
Nate Summers's avatar
Nate Summers committed
433 434 435
    }
  else
    {
436
      gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
437

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

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

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

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

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

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

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

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

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

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

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

484 485
      gimp_transform_tool_expose_preview (tr_tool);

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

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

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

495 496
#define RESPONSE_RESET 1

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

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

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

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

  return FALSE;
526 527
}

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

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

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

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

555 556 557
  if (! tr_tool->use_grid)
    return;

558
  if (display == tool->display)
559 560 561 562
    {
      gdouble closest_dist;
      gdouble dist;

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

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

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

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

595
      if (gimp_draw_tool_on_handle (draw_tool, display,
596 597 598
                                    coords->x, coords->y,
                                    GIMP_HANDLE_CIRCLE,
                                    tr_tool->tcx, tr_tool->tcy,
599
                                    HANDLE_SIZE, HANDLE_SIZE,
600 601
                                    GTK_ANCHOR_CENTER,
                                    FALSE))
602 603 604
        {
          tr_tool->function = TRANSFORM_HANDLE_CENTER;
        }
605 606 607
    }
}

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

617
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
Nate Summers's avatar
Nate Summers committed
618

Michael Natterer's avatar
Michael Natterer committed
619
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
620
    {
621
      GimpChannel        *selection = gimp_image_get_mask (display->image);
622
      GimpCursorType      cursor    = GIMP_CURSOR_MOUSE;
623
      GimpCursorModifier  modifier  = GIMP_CURSOR_MODIFIER_NONE;
624

625
      switch (options->type)
Michael Natterer's avatar
Michael Natterer committed
626
        {
627
        case GIMP_TRANSFORM_TYPE_LAYER:
628
          if (gimp_image_coords_in_active_drawable (display->image, coords))
629 630
            {
              if (gimp_channel_is_empty (selection) ||
631 632
                  gimp_pickable_get_opacity_at (GIMP_PICKABLE (selection),
                                                coords->x, coords->y))
633 634 635 636
                {
                  cursor = GIMP_CURSOR_MOUSE;
                }
            }
637 638 639
          break;

        case GIMP_TRANSFORM_TYPE_SELECTION:
640
          if (gimp_channel_is_empty (selection) ||
641 642
              gimp_pickable_get_opacity_at (GIMP_PICKABLE (selection),
                                            coords->x, coords->y))
Michael Natterer's avatar
Michael Natterer committed
643
            {
644
              cursor = GIMP_CURSOR_MOUSE;
Michael Natterer's avatar
Michael Natterer committed
645
            }
646 647 648
          break;

        case GIMP_TRANSFORM_TYPE_PATH:
649
          if (gimp_image_get_active_vectors (display->image))
650
            cursor = GIMP_CURSOR_MOUSE;
651
          else
652
            cursor = GIMP_CURSOR_BAD;
653
          break;
Michael Natterer's avatar
Michael Natterer committed
654
        }
655

Michael Natterer's avatar
Michael Natterer committed
656 657
      if (tr_tool->use_center && tr_tool->function == TRANSFORM_HANDLE_CENTER)
        {
658
          modifier = GIMP_CURSOR_MODIFIER_MOVE;
Michael Natterer's avatar
Michael Natterer committed
659 660
        }

661 662
      gimp_tool_control_set_cursor          (tool->control, cursor);
      gimp_tool_control_set_cursor_modifier (tool->control, modifier);
Michael Natterer's avatar
Michael Natterer committed
663
    }
664

665
  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
Nate Summers's avatar
Nate Summers committed
666 667
}

668
static void
669
gimp_transform_tool_draw (GimpDrawTool *draw_tool)
Nate Summers's avatar
Nate Summers committed
670
{
671 672
  GimpTool             *tool    = GIMP_TOOL (draw_tool);
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (draw_tool);
673
  GimpTransformOptions *options;
674
  gdouble               z1, z2, z3, z4;
Nate Summers's avatar
Nate Summers committed
675

676 677 678
  if (! tr_tool->use_grid)
    return;

679
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
680

Nate Summers's avatar
Nate Summers committed
681
  /*  draw the bounding box  */
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
  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);
Nate Summers's avatar
Nate Summers committed
698

699 700
  /* We test if the transformed polygon is convex.
   * if z1 and z2 have the same sign as well as z3 and z4
701 702
   * the polygon is convex.
   */
703 704 705 706 707 708 709 710
  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));
711

712
  /*  Draw the grid (not for path transform since it looks ugly)  */
Nate Summers's avatar
Nate Summers committed
713

714 715 716
  if (tr_tool->type != GIMP_TRANSFORM_TYPE_PATH &&
      tr_tool->grid_coords                      &&
      tr_tool->tgrid_coords                     &&
717 718
      z1 * z2 > 0                               &&
      z3 * z4 > 0)
Nate Summers's avatar
Nate Summers committed
719
    {
720 721
      gint gci, i, k;

Nate Summers's avatar
Nate Summers committed
722
      k = tr_tool->ngx + tr_tool->ngy;
723

724
      for (i = 0, gci = 0; i < k; i++, gci += 4)
725
        {
726 727 728 729 730 731
          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);
732
        }