gimptransformtool.c 54.9 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 27 28

#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"

Michael Natterer's avatar
Michael Natterer committed
29
#include "tools-types.h"
30

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

Michael Natterer's avatar
Michael Natterer committed
33
#include "core/gimp.h"
34
#include "core/gimpchannel.h"
35
#include "core/gimpcontext.h"
Michael Natterer's avatar
Michael Natterer committed
36
#include "core/gimpdrawable-transform.h"
37
#include "core/gimpimage.h"
38
#include "core/gimpimage-undo.h"
39
#include "core/gimpimage-undo-push.h"
40
#include "core/gimpitem-linked.h"
41
#include "core/gimplayer.h"
42
#include "core/gimplayermask.h"
43
#include "core/gimpprogress.h"
Michael Natterer's avatar
Michael Natterer committed
44
#include "core/gimptoolinfo.h"
45

46 47 48
#include "vectors/gimpvectors.h"
#include "vectors/gimpstroke.h"

49
#include "widgets/gimpdialogfactory.h"
Michael Natterer's avatar
Michael Natterer committed
50 51
#include "widgets/gimpviewabledialog.h"

52
#include "display/gimpdisplay.h"
53 54 55
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-appearance.h"
#include "display/gimpdisplayshell-transform.h"
Nate Summers's avatar
Nate Summers committed
56

57
#ifdef __GNUC__
58
#warning FIXME #include "dialogs/dialogs-types.h"
59
#endif
60 61
#include "dialogs/dialogs-types.h"
#include "dialogs/info-dialog.h"
62

63
#include "gimptoolcontrol.h"
64
#include "gimptransformoptions.h"
65 66
#include "gimptransformtool.h"
#include "gimptransformtool-undo.h"
67

68
#include "gimp-intl.h"
Nate Summers's avatar
Nate Summers committed
69

70

71
#define HANDLE_SIZE 10
Michael Natterer's avatar
Michael Natterer committed
72

73

Michael Natterer's avatar
Michael Natterer committed
74 75 76 77 78
/*  local function prototypes  */

static void   gimp_transform_tool_init        (GimpTransformTool      *tool);
static void   gimp_transform_tool_class_init  (GimpTransformToolClass *tool);

79 80 81
static GObject * gimp_transform_tool_constructor   (GType              type,
                                                    guint              n_params,
                                                    GObjectConstructParam *params);
82
static void     gimp_transform_tool_finalize       (GObject           *object);
Michael Natterer's avatar
Michael Natterer committed
83

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

121
static void     gimp_transform_tool_draw           (GimpDrawTool      *draw_tool);
Michael Natterer's avatar
Michael Natterer committed
122

123
static TileManager *
124
                gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
125
                                                    GimpItem          *item,
126
                                                    gboolean           mask_empty,
127 128
                                                    GimpDisplay       *gdisp);

129 130
static void     gimp_transform_tool_halt           (GimpTransformTool *tr_tool);
static void     gimp_transform_tool_bounds         (GimpTransformTool *tr_tool,
Michael Natterer's avatar
Michael Natterer committed
131
                                                    GimpDisplay       *gdisp);
132 133
static void     gimp_transform_tool_dialog         (GimpTransformTool *tr_tool);
static void     gimp_transform_tool_prepare        (GimpTransformTool *tr_tool,
Michael Natterer's avatar
Michael Natterer committed
134
                                                    GimpDisplay       *gdisp);
135
static void     gimp_transform_tool_doit           (GimpTransformTool *tr_tool,
Michael Natterer's avatar
Michael Natterer committed
136
                                                    GimpDisplay       *gdisp);
Michael Natterer's avatar
Michael Natterer committed
137
static void     gimp_transform_tool_transform_bounding_box (GimpTransformTool *tr_tool);
138
static void     gimp_transform_tool_grid_recalc    (GimpTransformTool *tr_tool);
Michael Natterer's avatar
Michael Natterer committed
139

140 141
static void     gimp_transform_tool_force_expose_preview (GimpTransformTool *tr_tool);

142
static void     gimp_transform_tool_response       (GtkWidget         *widget,
143
                                                    gint               response_id,
Michael Natterer's avatar
Michael Natterer committed
144
                                                    GimpTransformTool *tr_tool);
145

146 147 148 149 150 151
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);
152

153
static GimpDrawToolClass *parent_class = NULL;
154

155 156

GType
Nate Summers's avatar
Nate Summers committed
157 158
gimp_transform_tool_get_type (void)
{
159
  static GType tool_type = 0;
Nate Summers's avatar
Nate Summers committed
160 161 162

  if (! tool_type)
    {
163
      static const GTypeInfo tool_info =
Nate Summers's avatar
Nate Summers committed
164 165
      {
        sizeof (GimpTransformToolClass),
166 167 168 169 170 171 172 173
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) gimp_transform_tool_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data     */
        sizeof (GimpTransformTool),
        0,              /* n_preallocs    */
        (GInstanceInitFunc) gimp_transform_tool_init,
Nate Summers's avatar
Nate Summers committed
174 175
      };

176
      tool_type = g_type_register_static (GIMP_TYPE_DRAW_TOOL,
177
                                          "GimpTransformTool",
178
                                          &tool_info, 0);
Nate Summers's avatar
Nate Summers committed
179 180 181 182 183 184 185 186
    }

  return tool_type;
}

static void
gimp_transform_tool_class_init (GimpTransformToolClass *klass)
{
187 188 189
  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
190

191
  parent_class = g_type_class_peek_parent (klass);
Nate Summers's avatar
Nate Summers committed
192

193
  object_class->constructor  = gimp_transform_tool_constructor;
194
  object_class->finalize     = gimp_transform_tool_finalize;
Nate Summers's avatar
Nate Summers committed
195

196
  tool_class->initialize     = gimp_transform_tool_initialize;
197
  tool_class->control        = gimp_transform_tool_control;
Nate Summers's avatar
Nate Summers committed
198 199 200
  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;
201
  tool_class->key_press      = gimp_transform_tool_key_press;
202
  tool_class->modifier_key   = gimp_transform_tool_modifier_key;
203
  tool_class->oper_update    = gimp_transform_tool_oper_update;
Nate Summers's avatar
Nate Summers committed
204
  tool_class->cursor_update  = gimp_transform_tool_cursor_update;
Nate Summers's avatar
Nate Summers committed
205 206

  draw_class->draw           = gimp_transform_tool_draw;
Michael Natterer's avatar
Michael Natterer committed
207 208

  klass->dialog              = NULL;
Michael Natterer's avatar
Michael Natterer committed
209
  klass->dialog_update       = NULL;
Michael Natterer's avatar
Michael Natterer committed
210 211 212
  klass->prepare             = NULL;
  klass->motion              = NULL;
  klass->recalc              = NULL;
213
  klass->transform           = gimp_transform_tool_real_transform;
Nate Summers's avatar
Nate Summers committed
214 215 216
}

static void
Michael Natterer's avatar
Michael Natterer committed
217
gimp_transform_tool_init (GimpTransformTool *tr_tool)
Nate Summers's avatar
Nate Summers committed
218
{
Michael Natterer's avatar
Michael Natterer committed
219
  GimpTool *tool;
220
  gint      i;
Nate Summers's avatar
Nate Summers committed
221

Michael Natterer's avatar
Michael Natterer committed
222
  tool = GIMP_TOOL (tr_tool);
Michael Natterer's avatar
Michael Natterer committed
223

Michael Natterer's avatar
Michael Natterer committed
224 225
  gimp_tool_control_set_scroll_lock (tool->control, TRUE);
  gimp_tool_control_set_preserve    (tool->control, FALSE);
226 227 228 229
  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
230

Michael Natterer's avatar
Michael Natterer committed
231 232
  tr_tool->function = TRANSFORM_CREATING;
  tr_tool->original = NULL;
Nate Summers's avatar
Nate Summers committed
233 234

  for (i = 0; i < TRAN_INFO_SIZE; i++)
235
    {
Michael Natterer's avatar
Michael Natterer committed
236 237
      tr_tool->trans_info[i]     = 0.0;
      tr_tool->old_trans_info[i] = 0.0;
238
    }
Nate Summers's avatar
Nate Summers committed
239

240
  gimp_matrix3_identity (&tr_tool->transform);
241

242 243 244 245 246 247 248
  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;

249
  tr_tool->type             = GIMP_TRANSFORM_TYPE_LAYER;
250
  tr_tool->direction        = GIMP_TRANSFORM_FORWARD;
251

252 253 254
  tr_tool->shell_desc       = NULL;
  tr_tool->progress_text    = _("Transforming...");
  tr_tool->info_dialog      = NULL;
Nate Summers's avatar
Nate Summers committed
255 256
}

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
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);
284 285 286 287
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
288 289 290 291
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
292 293 294 295
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
296
      g_signal_connect_object (tool->tool_info->tool_options,
297 298
                               "notify::preview-type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
299 300
                               tr_tool, 0);
      g_signal_connect_object (tool->tool_info->tool_options,
301 302
                               "notify::grid-type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
303
                               tr_tool, 0);
304
      g_signal_connect_object (tool->tool_info->tool_options,
305
                               "notify::grid-size",
306 307
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
308 309 310 311 312
    }

  return object;
}

313
static void
314
gimp_transform_tool_finalize (GObject *object)
Nate Summers's avatar
Nate Summers committed
315
{
316
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (object);
317 318

  if (tr_tool->original)
319
    {
320
      tile_manager_unref (tr_tool->original);
321 322
      tr_tool->original = NULL;
    }
323

324
  if (tr_tool->info_dialog)
325
    {
326 327
      info_dialog_free (tr_tool->info_dialog);
      tr_tool->info_dialog = NULL;
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
    }

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

345
static gboolean
346
gimp_transform_tool_initialize (GimpTool    *tool,
347
                                GimpDisplay *gdisp)
348 349 350 351 352
{
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);

  if (gdisp != tool->gdisp)
    {
353
      gint i;
354 355 356

      /*  Set the pointer to the active display  */
      tool->gdisp    = gdisp;
357
      tool->drawable = gimp_image_active_drawable (gdisp->gimage);
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380

      /*  Initialize the transform tool dialog */
      if (! tr_tool->info_dialog)
        gimp_transform_tool_dialog (tr_tool);

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

      gimp_transform_tool_prepare (tr_tool, gdisp);

      /*  Recalculate the transform tool  */
      gimp_transform_tool_recalc (tr_tool, gdisp);

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

      tr_tool->function = TRANSFORM_CREATING;

      /*  Save the current transformation info  */
      for (i = 0; i < TRAN_INFO_SIZE; i++)
381
        tr_tool->old_trans_info[i] = tr_tool->trans_info[i];
382
    }
383 384

  return TRUE;
385 386
}

387
static void
388
gimp_transform_tool_control (GimpTool       *tool,
389 390
                             GimpToolAction  action,
                             GimpDisplay    *gdisp)
391
{
392
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
393

394 395 396 397
  switch (action)
    {
    case PAUSE:
      break;
398

399
    case RESUME:
Michael Natterer's avatar
Michael Natterer committed
400
      gimp_transform_tool_bounds (tr_tool, gdisp);
Michael Natterer's avatar
Michael Natterer committed
401
      gimp_transform_tool_recalc (tr_tool, gdisp);
402
      break;
403

404
    case HALT:
405
      gimp_transform_tool_halt (tr_tool);
406
      return; /* don't upchain */
407
      break;
408

409 410 411 412
    default:
      break;
    }

Michael Natterer's avatar
Michael Natterer committed
413
  GIMP_TOOL_CLASS (parent_class)->control (tool, action, gdisp);
414 415 416
}

static void
417 418 419 420
gimp_transform_tool_button_press (GimpTool        *tool,
                                  GimpCoords      *coords,
                                  guint32          time,
                                  GdkModifierType  state,
421
                                  GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
422
{
423
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
424

425 426
  if (tr_tool->function == TRANSFORM_CREATING && tr_tool->use_grid)
    gimp_transform_tool_oper_update (tool, coords, state, gdisp);
427

428 429
  tr_tool->lastx = tr_tool->startx = coords->x;
  tr_tool->lasty = tr_tool->starty = coords->y;
Nate Summers's avatar
Nate Summers committed
430

431
  gimp_tool_control_activate (tool->control);
Nate Summers's avatar
Nate Summers committed
432 433
}

434
static void
435 436 437
gimp_transform_tool_button_release (GimpTool        *tool,
                                    GimpCoords      *coords,
                                    guint32          time,
438 439
                                    GdkModifierType  state,
                                    GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
440
{
441
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
442
  gint               i;
Nate Summers's avatar
Nate Summers committed
443 444

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

  /*  if the 3rd button isn't pressed, transform the selected mask  */
449
  if (! (state & GDK_BUTTON3_MASK))
Nate Summers's avatar
Nate Summers committed
450 451
    {
      /* Shift-clicking is another way to approve the transform  */
452
      if ((state & GDK_SHIFT_MASK) || ! tr_tool->use_grid)
453 454 455
        {
          gimp_transform_tool_doit (tr_tool, gdisp);
        }
Nate Summers's avatar
Nate Summers committed
456 457 458
    }
  else
    {
459
      gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
460

Michael Natterer's avatar
Michael Natterer committed
461 462 463
      /* 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
464 465
      /*  Restore the previous transformation info  */
      for (i = 0; i < TRAN_INFO_SIZE; i++)
466
        tr_tool->trans_info[i] = tr_tool->old_trans_info[i];
Nate Summers's avatar
Nate Summers committed
467

Michael Natterer's avatar
Michael Natterer committed
468 469 470
      /*  reget the selection bounds  */
      gimp_transform_tool_bounds (tr_tool, gdisp);

Nate Summers's avatar
Nate Summers committed
471
      /*  recalculate the tool's transformation matrix  */
Michael Natterer's avatar
Michael Natterer committed
472
      gimp_transform_tool_recalc (tr_tool, gdisp);
Nate Summers's avatar
Nate Summers committed
473

474
      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
475
    }
476 477

  gimp_tool_control_halt (tool->control);
Nate Summers's avatar
Nate Summers committed
478 479
}

480
static void
481 482 483
gimp_transform_tool_motion (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
484 485
                            GdkModifierType  state,
                            GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
486
{
487
  GimpTransformTool      *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Michael Natterer's avatar
Michael Natterer committed
488
  GimpTransformToolClass *tr_tool_class;
Nate Summers's avatar
Nate Summers committed
489

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

494
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
495

Michael Natterer's avatar
Michael Natterer committed
496 497 498
  tr_tool->curx  = coords->x;
  tr_tool->cury  = coords->y;
  tr_tool->state = state;
Nate Summers's avatar
Nate Summers committed
499 500

  /*  recalculate the tool's transformation matrix  */
Michael Natterer's avatar
Michael Natterer committed
501 502 503 504 505
  tr_tool_class = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool);

  if (tr_tool_class->motion)
    {
      tr_tool_class->motion (tr_tool, gdisp);
Nate Summers's avatar
Nate Summers committed
506

507 508
      gimp_transform_tool_expose_preview (tr_tool);

Michael Natterer's avatar
Michael Natterer committed
509
      gimp_transform_tool_recalc (tr_tool, gdisp);
Michael Natterer's avatar
Michael Natterer committed
510 511 512 513
    }

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

515
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
516 517
}

518 519
#define RESPONSE_RESET 1

520
static gboolean
521 522 523 524 525
gimp_transform_tool_key_press (GimpTool    *tool,
                               GdkEventKey *kevent,
                               GimpDisplay *gdisp)
{
  GimpTransformTool *trans_tool = GIMP_TRANSFORM_TOOL (tool);
526
  GimpDrawTool      *draw_tool  = GIMP_DRAW_TOOL (tool);
527 528 529 530 531

  if (gdisp == draw_tool->gdisp)
    {
      switch (kevent->keyval)
        {
532 533 534
        case GDK_KP_Enter:
        case GDK_Return:
          gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, trans_tool);
535
          return TRUE;
536 537 538 539

        case GDK_Delete:
        case GDK_BackSpace:
          gimp_transform_tool_response (NULL, RESPONSE_RESET, trans_tool);
540
          return TRUE;
541 542 543 544

        case GDK_Escape:
          gimp_transform_tool_response (NULL, GTK_RESPONSE_CANCEL, trans_tool);
          return TRUE;
545 546
        }
    }
547 548

  return FALSE;
549 550
}

551 552 553 554 555 556 557
static void
gimp_transform_tool_modifier_key (GimpTool        *tool,
                                  GdkModifierType  key,
                                  gboolean         press,
                                  GdkModifierType  state,
                                  GimpDisplay     *gdisp)
{
558
  GimpTransformOptions *options;
559

560
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
561

562
  if (key == GDK_CONTROL_MASK)
563
    {
564
      g_object_set (options,
565 566
                    "constrain-1", ! options->constrain_1,
                    NULL);
567
    }
568
  else if (key == GDK_MOD1_MASK)
569
    {
570
      g_object_set (options,
571 572
                    "constrain-2", ! options->constrain_2,
                    NULL);
573 574 575
    }
}

576 577 578 579 580 581
static void
gimp_transform_tool_oper_update (GimpTool        *tool,
                                 GimpCoords      *coords,
                                 GdkModifierType  state,
                                 GimpDisplay     *gdisp)
{
582 583
  GimpTransformTool *tr_tool   = GIMP_TRANSFORM_TOOL (tool);
  GimpDrawTool      *draw_tool = GIMP_DRAW_TOOL (tool);
584

585 586 587
  if (! tr_tool->use_grid)
    return;

588 589 590 591 592 593 594 595 596 597 598 599 600 601
  if (gdisp == tool->gdisp)
    {
      gdouble closest_dist;
      gdouble dist;

      closest_dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                                   coords->x, coords->y,
                                                   tr_tool->tx1, tr_tool->ty1);
      tr_tool->function = TRANSFORM_HANDLE_1;

      dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                           coords->x, coords->y,
                                           tr_tool->tx2, tr_tool->ty2);
      if (dist < closest_dist)
602 603 604 605
        {
          closest_dist = dist;
          tr_tool->function = TRANSFORM_HANDLE_2;
        }
606 607 608 609 610

      dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                           coords->x, coords->y,
                                           tr_tool->tx3, tr_tool->ty3);
      if (dist < closest_dist)
611 612 613 614
        {
          closest_dist = dist;
          tr_tool->function = TRANSFORM_HANDLE_3;
        }
615 616 617 618 619

      dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                           coords->x, coords->y,
                                           tr_tool->tx4, tr_tool->ty4);
      if (dist < closest_dist)
620 621 622 623
        {
          closest_dist = dist;
          tr_tool->function = TRANSFORM_HANDLE_4;
        }
624 625 626 627 628

      if (gimp_draw_tool_on_handle (draw_tool, gdisp,
                                    coords->x, coords->y,
                                    GIMP_HANDLE_CIRCLE,
                                    tr_tool->tcx, tr_tool->tcy,
629
                                    HANDLE_SIZE, HANDLE_SIZE,
630 631
                                    GTK_ANCHOR_CENTER,
                                    FALSE))
632 633 634
        {
          tr_tool->function = TRANSFORM_HANDLE_CENTER;
        }
635 636 637
    }
}

638
static void
639 640
gimp_transform_tool_cursor_update (GimpTool        *tool,
                                   GimpCoords      *coords,
641 642
                                   GdkModifierType  state,
                                   GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
643
{
644
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (tool);
645
  GimpTransformOptions *options;
646

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

Michael Natterer's avatar
Michael Natterer committed
649
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
650
    {
651
      GimpChannel        *selection = gimp_image_get_mask (gdisp->gimage);
652 653
      GimpCursorType      cursor    = GDK_TOP_LEFT_ARROW;
      GimpCursorModifier  modifier  = GIMP_CURSOR_MODIFIER_NONE;
654

655
      switch (options->type)
Michael Natterer's avatar
Michael Natterer committed
656
        {
657
        case GIMP_TRANSFORM_TYPE_LAYER:
658 659 660 661 662 663 664 665
          if (gimp_image_coords_in_active_drawable (gdisp->gimage, coords))
            {
              if (gimp_channel_is_empty (selection) ||
                  gimp_channel_value (selection, coords->x, coords->y))
                {
                  cursor = GIMP_CURSOR_MOUSE;
                }
            }
666 667 668
          break;

        case GIMP_TRANSFORM_TYPE_SELECTION:
669 670
          if (gimp_channel_is_empty (selection) ||
              gimp_channel_value (selection, coords->x, coords->y))
Michael Natterer's avatar
Michael Natterer committed
671
            {
672
              cursor = GIMP_CURSOR_MOUSE;
Michael Natterer's avatar
Michael Natterer committed
673
            }
674 675 676 677
          break;

        case GIMP_TRANSFORM_TYPE_PATH:
          if (gimp_image_get_active_vectors (gdisp->gimage))
678
            cursor = GIMP_CURSOR_MOUSE;
679
          else
680
            cursor = GIMP_CURSOR_BAD;
681
          break;
Michael Natterer's avatar
Michael Natterer committed
682
        }
683

Michael Natterer's avatar
Michael Natterer committed
684 685
      if (tr_tool->use_center && tr_tool->function == TRANSFORM_HANDLE_CENTER)
        {
686
          modifier = GIMP_CURSOR_MODIFIER_MOVE;
Michael Natterer's avatar
Michael Natterer committed
687 688
        }

689 690
      gimp_tool_control_set_cursor          (tool->control, cursor);
      gimp_tool_control_set_cursor_modifier (tool->control, modifier);
Michael Natterer's avatar
Michael Natterer committed
691
    }
692 693

  GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, gdisp);
Nate Summers's avatar
Nate Summers committed
694 695
}

696
static void
697
gimp_transform_tool_draw (GimpDrawTool *draw_tool)
Nate Summers's avatar
Nate Summers committed
698
{
699 700
  GimpTool             *tool    = GIMP_TOOL (draw_tool);
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (draw_tool);
701
  GimpTransformOptions *options;
702
  gdouble               z1, z2, z3, z4;
Nate Summers's avatar
Nate Summers committed
703

704 705 706
  if (! tr_tool->use_grid)
    return;

707
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
708

Nate Summers's avatar
Nate Summers committed
709
  /*  draw the bounding box  */
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
  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
726

727 728
  /* We test if the transformed polygon is convex.
   * if z1 and z2 have the same sign as well as z3 and z4
729 730
   * the polygon is convex.
   */
731 732 733 734 735 736 737 738
  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));
739

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

742 743 744
  if (tr_tool->type != GIMP_TRANSFORM_TYPE_PATH &&
      tr_tool->grid_coords                      &&
      tr_tool->tgrid_coords                     &&
745 746
      z1 * z2 > 0                               &&
      z3 * z4 > 0)
Nate Summers's avatar
Nate Summers committed
747
    {
748 749
      gint gci, i, k;

Nate Summers's avatar
Nate Summers committed
750
      k = tr_tool->ngx + tr_tool->ngy;
751

752
      for (i = 0, gci = 0; i < k; i++, gci += 4)
753
        {
754 755 756 757 758 759
          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);
760
        }
Nate Summers's avatar
Nate Summers committed
761 762 763
    }

  /*  draw the tool handles  */
764 765 766
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx1, tr_tool->ty1,
767
                              HANDLE_SIZE, HANDLE_SIZE,
768 769 770 771 772
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx2, tr_tool->ty2,
773
                              HANDLE_SIZE, HANDLE_SIZE,
774 775 776 777 778
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx3, tr_tool->ty3,
779
                              HANDLE_SIZE, HANDLE_SIZE,
780 781 782 783 784
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx4, tr_tool->ty4,
785
                              HANDLE_SIZE, HANDLE_SIZE,
786 787
                              GTK_ANCHOR_CENTER,
                              FALSE);
Nate Summers's avatar
Nate Summers committed
788 789

  /*  draw the center  */
790 791 792 793 794
  if (tr_tool->use_center)
    {
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_FILLED_CIRCLE,
                                  tr_tool->tcx, tr_tool->tcy,
795
                                  HANDLE_SIZE, HANDLE_SIZE,
796 797 798
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
    }
Nate Summers's avatar
Nate Summers committed
799

800
  if (tr_tool->type == GIMP_TRANSFORM_TYPE_PATH)
Nate Summers's avatar
Nate Summers committed
801
    {
802 803 804
      GimpVectors *vectors;
      GimpStroke  *stroke = NULL;
      GimpMatrix3  matrix = tr_tool->transform;
805

806 807 808
      vectors = gimp_image_get_active_vectors (tool->gdisp->gimage);

      if (vectors)
809
        {