gimptransformtool.c 54.3 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
static TileManager *
119
                gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
120
                                                    GimpItem          *item,
121
                                                    gboolean           mask_empty,
122
                                                    GimpDisplay       *display);
123

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

135
136
static void     gimp_transform_tool_force_expose_preview (GimpTransformTool *tr_tool);

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

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


149
G_DEFINE_TYPE (GimpTransformTool, gimp_transform_tool, GIMP_TYPE_DRAW_TOOL)
150
151

#define parent_class gimp_transform_tool_parent_class
152

Nate Summers's avatar
Nate Summers committed
153
154
155
156

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

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

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

  draw_class->draw           = gimp_transform_tool_draw;
Michael Natterer's avatar
Michael Natterer committed
175
176

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

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

Michael Natterer's avatar
Michael Natterer committed
190
  tool = GIMP_TOOL (tr_tool);
Michael Natterer's avatar
Michael Natterer committed
191

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

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

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

208
  gimp_matrix3_identity (&tr_tool->transform);
209

210
211
212
213
214
215
216
  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;

217
  tr_tool->type             = GIMP_TRANSFORM_TYPE_LAYER;
218
  tr_tool->direction        = GIMP_TRANSFORM_FORWARD;
219

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

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
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);
252
253
254
255
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
256
257
258
259
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
260
261
262
263
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
264
      g_signal_connect_object (tool->tool_info->tool_options,
265
266
                               "notify::preview-type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
267
268
                               tr_tool, 0);
      g_signal_connect_object (tool->tool_info->tool_options,
269
270
                               "notify::grid-type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
271
                               tr_tool, 0);
272
      g_signal_connect_object (tool->tool_info->tool_options,
273
                               "notify::grid-size",
274
275
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
276
277
278
279
280
    }

  return object;
}

281
static void
282
gimp_transform_tool_finalize (GObject *object)
Nate Summers's avatar
Nate Summers committed
283
{
284
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (object);
285
286

  if (tr_tool->original)
287
    {
288
      tile_manager_unref (tr_tool->original);
289
290
      tr_tool->original = NULL;
    }
291

292
  if (tr_tool->dialog)
293
    {
294
295
      gtk_widget_destroy (tr_tool->dialog);
      tr_tool->dialog = NULL;
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
    }

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

313
static gboolean
314
gimp_transform_tool_initialize (GimpTool    *tool,
315
                                GimpDisplay *display)
316
317
318
{
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);

319
  if (display != tool->display)
320
    {
321
      gint i;
322
323

      /*  Set the pointer to the active display  */
324
325
      tool->display    = display;
      tool->drawable = gimp_image_active_drawable (display->image);
326
327

      /*  Initialize the transform tool dialog */
328
      if (! tr_tool->dialog)
329
330
331
332
333
334
        gimp_transform_tool_dialog (tr_tool);

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

337
      gimp_transform_tool_prepare (tr_tool, display);
338
339

      /*  Recalculate the transform tool  */
340
      gimp_transform_tool_recalc (tr_tool, display);
341
342

      /*  start drawing the bounding box and handles...  */
343
      gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
344
345
346
347
348

      tr_tool->function = TRANSFORM_CREATING;

      /*  Save the current transformation info  */
      for (i = 0; i < TRAN_INFO_SIZE; i++)
349
        tr_tool->old_trans_info[i] = tr_tool->trans_info[i];
350
    }
351
352

  return TRUE;
353
354
}

355
static void
356
gimp_transform_tool_control (GimpTool       *tool,
357
                             GimpToolAction  action,
358
                             GimpDisplay    *display)
359
{
360
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
361

362
363
364
365
  switch (action)
    {
    case PAUSE:
      break;
366

367
    case RESUME:
368
369
      gimp_transform_tool_bounds (tr_tool, display);
      gimp_transform_tool_recalc (tr_tool, display);
370
      break;
371

372
    case HALT:
373
      gimp_transform_tool_halt (tr_tool);
374
      return; /* don't upchain */
375
      break;
376

377
378
379
380
    default:
      break;
    }

381
  GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
382
383
384
}

static void
385
386
387
388
gimp_transform_tool_button_press (GimpTool        *tool,
                                  GimpCoords      *coords,
                                  guint32          time,
                                  GdkModifierType  state,
389
                                  GimpDisplay     *display)
Nate Summers's avatar
Nate Summers committed
390
{
391
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Nate Summers's avatar
Nate Summers committed
392

393
  if (tr_tool->function == TRANSFORM_CREATING && tr_tool->use_grid)
394
    gimp_transform_tool_oper_update (tool, coords, state, TRUE, display);
395

396
397
  tr_tool->lastx = tr_tool->startx = coords->x;
  tr_tool->lasty = tr_tool->starty = coords->y;
Nate Summers's avatar
Nate Summers committed
398

399
  gimp_tool_control_activate (tool->control);
Nate Summers's avatar
Nate Summers committed
400
401
}

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

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

  /*  if the 3rd button isn't pressed, transform the selected mask  */
417
  if (! (state & GDK_BUTTON3_MASK))
Nate Summers's avatar
Nate Summers committed
418
419
    {
      /* Shift-clicking is another way to approve the transform  */
420
      if ((state & GDK_SHIFT_MASK) || ! tr_tool->use_grid)
421
        {
422
          gimp_transform_tool_doit (tr_tool, display);
423
        }
Nate Summers's avatar
Nate Summers committed
424
425
426
    }
  else
    {
427
      gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
428

Michael Natterer's avatar
Michael Natterer committed
429
430
431
      /* 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
432
433
      /*  Restore the previous transformation info  */
      for (i = 0; i < TRAN_INFO_SIZE; i++)
434
        tr_tool->trans_info[i] = tr_tool->old_trans_info[i];
Nate Summers's avatar
Nate Summers committed
435

Michael Natterer's avatar
Michael Natterer committed
436
      /*  reget the selection bounds  */
437
      gimp_transform_tool_bounds (tr_tool, display);
Michael Natterer's avatar
Michael Natterer committed
438

Nate Summers's avatar
Nate Summers committed
439
      /*  recalculate the tool's transformation matrix  */
440
      gimp_transform_tool_recalc (tr_tool, display);
Nate Summers's avatar
Nate Summers committed
441

442
      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
443
    }
444
445

  gimp_tool_control_halt (tool->control);
Nate Summers's avatar
Nate Summers committed
446
447
}

448
static void
449
450
451
gimp_transform_tool_motion (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
452
                            GdkModifierType  state,
453
                            GimpDisplay     *display)
Nate Summers's avatar
Nate Summers committed
454
{
455
  GimpTransformTool      *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Michael Natterer's avatar
Michael Natterer committed
456
  GimpTransformToolClass *tr_tool_class;
Nate Summers's avatar
Nate Summers committed
457

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

462
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
463

Michael Natterer's avatar
Michael Natterer committed
464
465
466
  tr_tool->curx  = coords->x;
  tr_tool->cury  = coords->y;
  tr_tool->state = state;
Nate Summers's avatar
Nate Summers committed
467
468

  /*  recalculate the tool's transformation matrix  */
Michael Natterer's avatar
Michael Natterer committed
469
470
471
472
  tr_tool_class = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool);

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

475
476
      gimp_transform_tool_expose_preview (tr_tool);

477
      gimp_transform_tool_recalc (tr_tool, display);
Michael Natterer's avatar
Michael Natterer committed
478
479
480
481
    }

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

483
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
484
485
}

486
487
#define RESPONSE_RESET 1

488
static gboolean
489
490
gimp_transform_tool_key_press (GimpTool    *tool,
                               GdkEventKey *kevent,
491
                               GimpDisplay *display)
492
493
{
  GimpTransformTool *trans_tool = GIMP_TRANSFORM_TOOL (tool);
494
  GimpDrawTool      *draw_tool  = GIMP_DRAW_TOOL (tool);
495

496
  if (display == draw_tool->display)
497
498
499
    {
      switch (kevent->keyval)
        {
500
501
502
        case GDK_KP_Enter:
        case GDK_Return:
          gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, trans_tool);
503
          return TRUE;
504
505
506
507

        case GDK_Delete:
        case GDK_BackSpace:
          gimp_transform_tool_response (NULL, RESPONSE_RESET, trans_tool);
508
          return TRUE;
509
510
511
512

        case GDK_Escape:
          gimp_transform_tool_response (NULL, GTK_RESPONSE_CANCEL, trans_tool);
          return TRUE;
513
514
        }
    }
515
516

  return FALSE;
517
518
}

519
520
521
522
523
static void
gimp_transform_tool_modifier_key (GimpTool        *tool,
                                  GdkModifierType  key,
                                  gboolean         press,
                                  GdkModifierType  state,
524
                                  GimpDisplay     *display)
525
{
526
  GimpTransformOptions *options;
527

528
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
529

530
  if (key == GDK_CONTROL_MASK)
531
    {
532
      g_object_set (options,
533
534
                    "constrain-1", ! options->constrain_1,
                    NULL);
535
    }
536
  else if (key == GDK_MOD1_MASK)
537
    {
538
      g_object_set (options,
539
540
                    "constrain-2", ! options->constrain_2,
                    NULL);
541
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
  if (! tr_tool->use_grid)
    return;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

723
      for (i = 0, gci = 0; i < k; i++, gci += 4)
724
        {
725
726
727
728
729
730
          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);
731
        }
Nate Summers's avatar
Nate Summers committed
732
733
734
    }

  /*  draw the tool handles  */
735
736
737
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx1, tr_tool->ty1,
738
                              HANDLE_SIZE, HANDLE_SIZE,
739
740
741
742
743
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx2, tr_tool->ty2,
744
                              HANDLE_SIZE, HANDLE_SIZE,
745
746
747
748
749
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx3, tr_tool->ty3,
750
                              HANDLE_SIZE, HANDLE_SIZE,
751
752
753
754
755
                              GTK_ANCHOR_CENTER,
                              FALSE);
  gimp_draw_tool_draw_handle (draw_tool,
                              GIMP_HANDLE_SQUARE,
                              tr_tool->tx4, tr_tool->ty4,
756
                              HANDLE_SIZE, HANDLE_SIZE,
757
758
                              GTK_ANCHOR_CENTER,
                              FALSE);
Nate Summers's avatar
Nate Summers committed
759
760

  /*  draw the center  */
761
762
763
764
765
  if (tr_tool->use_center)
    {
      gimp_draw_tool_draw_handle (draw_tool,
                                  GIMP_HANDLE_FILLED_CIRCLE,
                                  tr_tool->tcx, tr_tool->tcy,
766
                                  HANDLE_SIZE, HANDLE_SIZE,
767
768
769
                                  GTK_ANCHOR_CENTER,
                                  FALSE);
    }
Nate Summers's avatar
Nate Summers committed
770

771
  if (tr_tool->type == GIMP_TRANSFORM_TYPE_PATH)
Nate Summers's avatar
Nate Summers committed
772
    {
773
774
775
      GimpVectors *vectors;
      GimpStroke  *stroke = NULL;
      GimpMatrix3  matrix = tr_tool->transform;
776

777
      vectors = gimp_image_get_active_vectors (tool->display->image);
778
779

      if (vectors)
780
        {
781
782
783
784
785
786
787
788
789
790
          if (tr_tool->direction == GIMP_TRANSFORM_BACKWARD)
            gimp_matrix3_invert (&matrix);

          while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
            {
              GArray   *coords;
              gboolean  closed;

              coords = gimp_stroke_interpolate (stroke, 1.0, &closed);

791
              if (coords && coords->len)
792
                {
793
794
                  gint i;

795
796
797
798
799
800
801
802
803
                  for (i = 0; i < coords->len; i++)
                    {
                      GimpCoords *curr = &g_array_index (coords, GimpCoords, i);

                      gimp_matrix3_transform_point (&matrix,
                                                    curr->x, curr->y,
                                                    &curr->x, &curr->y);
                    }

804
805
806
807
                  gimp_draw_tool_draw_strokes (draw_tool,
                                               &g_array_index (coords,
                                                               GimpCoords, 0),
                                               coords->len, FALSE, FALSE);
808
                }
809
810
811

              if (coords)
                g_array_free (coords, TRUE);
812
            }
813
        }
Nate Summers's avatar
Nate Summers committed
814
815
816
    }
}

817
818
static TileManager *
gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
819
                                    GimpItem          *active_item,
820
                                    gboolean           mask_empty,
821
                                    GimpDisplay       *display)
822
{
823
  GimpTool             *tool = GIMP_TOOL (tr_tool);
824
  GimpTransformOptions *options;
825
  GimpContext          *context;
826
  GimpProgress         *progress;
827
  TileManager          *ret  = NULL;
828

829
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
830
  context = GIMP_CONTEXT (options);
831

832
833
  if (tr_tool->dialog)
    gtk_widget_set_sensitive (tr_tool->dialog, FALSE);
834

835
  progress = gimp_progress_start (GIMP_PROGRESS (display),
836
                                  tr_tool->progress_text, FALSE);
837

838
  if (gimp_item_get_linked (active_item))
839
    gimp_item_linked_transform (active_item, context,
840
                                &tr_tool->transform,
841
                                options->direction,
842
                                options->interpolation,
843
844
                                options->supersample,
                                options->recursion_level,
845
                                options->clip,
846
                                progress);
847

848
849
850
851
852
853
854
855
856
857
858
859
  if (GIMP_IS_LAYER (active_item) &&
      gimp_layer_get_mask (GIMP_LAYER (active_item)) &&
      mask_empty)
    {
      GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (active_item));

      gimp_item_transform (GIMP_ITEM (mask), context,
                           &tr_tool->transform,
                           options->direction,
                           <