gimptransformtool.c 55.4 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/gimpprogress.h"
Michael Natterer's avatar
Michael Natterer committed
45
#include "core/gimptoolinfo.h"
46

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

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

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

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

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

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

71

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

74

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

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

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

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

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

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

130
131
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
132
                                                    GimpDisplay       *gdisp);
133
134
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
135
                                                    GimpDisplay       *gdisp);
136
static void     gimp_transform_tool_doit           (GimpTransformTool *tr_tool,
Michael Natterer's avatar
Michael Natterer committed
137
                                                    GimpDisplay       *gdisp);
Michael Natterer's avatar
Michael Natterer committed
138
static void     gimp_transform_tool_transform_bounding_box (GimpTransformTool *tr_tool);
139
static void     gimp_transform_tool_grid_recalc    (GimpTransformTool *tr_tool);
Michael Natterer's avatar
Michael Natterer committed
140

141
142
static void     gimp_transform_tool_force_expose_preview (GimpTransformTool *tr_tool);

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

147
148
149
150
151
152
static void     gimp_transform_tool_notify_type    (GimpTransformOptions *options,
                                                    GParamSpec           *pspec,
                                                    GimpTransformTool    *tr_tool);
static void     gimp_transform_tool_notify_preview (GimpTransformOptions *options,
                                                    GParamSpec           *pspec,
                                                    GimpTransformTool    *tr_tool);
153

154
static GimpDrawToolClass *parent_class = NULL;
155

156
157

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

  if (! tool_type)
    {
164
      static const GTypeInfo tool_info =
Nate Summers's avatar
Nate Summers committed
165
166
      {
        sizeof (GimpTransformToolClass),
167
168
169
170
171
172
173
174
        (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
175
176
      };

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

  return tool_type;
}

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

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

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

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

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

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

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

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

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

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

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

241
  gimp_matrix3_identity (&tr_tool->transform);
242

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

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

253
254
255
  tr_tool->shell_desc       = NULL;
  tr_tool->progress_text    = _("Transforming...");
  tr_tool->info_dialog      = NULL;
Nate Summers's avatar
Nate Summers committed
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
284
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);
285
286
287
288
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
289
290
291
292
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_type),
                               tr_tool, 0);
293
294
295
296
      g_signal_connect_object (tool->tool_info->tool_options,
                               "notify::direction",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
297
      g_signal_connect_object (tool->tool_info->tool_options,
298
299
                               "notify::preview-type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
300
301
                               tr_tool, 0);
      g_signal_connect_object (tool->tool_info->tool_options,
302
303
                               "notify::grid-type",
                               G_CALLBACK (gimp_transform_tool_notify_preview),
304
                               tr_tool, 0);
305
      g_signal_connect_object (tool->tool_info->tool_options,
306
                               "notify::grid-size",
307
308
                               G_CALLBACK (gimp_transform_tool_notify_preview),
                               tr_tool, 0);
309
310
311
312
313
    }

  return object;
}

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

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

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

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

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

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

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

      /*  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++)
382
        tr_tool->old_trans_info[i] = tr_tool->trans_info[i];
383
    }
384
385

  return TRUE;
386
387
}

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

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

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

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

410
411
412
413
    default:
      break;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  /*  recalculate the tool's transformation matrix  */
Michael Natterer's avatar
Michael Natterer committed
502
503
504
505
506
  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
507

508
509
      gimp_transform_tool_expose_preview (tr_tool);

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

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

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

519
520
#define RESPONSE_RESET 1

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

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

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

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

  return FALSE;
550
551
}

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

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

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

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

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

589
590
591
592
593
594
595
596
597
598
599
600
601
602
  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)
603
604
605
606
        {
          closest_dist = dist;
          tr_tool->function = TRANSFORM_HANDLE_2;
        }
607
608
609
610
611

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

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

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

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

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

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

656
      switch (options->type)
Michael Natterer's avatar
Michael Natterer committed
657
        {
658
        case GIMP_TRANSFORM_TYPE_LAYER:
659
660
661
662
663
664
665
666
          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;
                }
            }
667
668
669
          break;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      if (vectors)
810
        {
811
812
813
814
815
816
817
818
819
820
          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);

821
              if (coords && coords->len)
822
                {
823
824
                  gint i;

825
826
827
828
829
830
831
832
833
                  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);
                    }

834
835
836
837
                  gimp_draw_tool_draw_strokes (draw_tool,
                                               &g_array_index (coords,
                                                               GimpCoords, 0),
                                               coords->len, FALSE, FALSE);
838
                }
839
840
841

              if (coords)
                g_array_free (coords, TRUE);
842
            }
843
        }
Nate Summers's avatar
Nate Summers committed
844
845
846
    }
}

847
848
static TileManager *
gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
849
                                    GimpItem          *active_item,
850
                                    gboolean           mask_empty,
851
852
                                    GimpDisplay       *gdisp)
{
853
  GimpTool             *tool = GIMP_TOOL (tr_tool);
854
  GimpTransformOptions *options;
855
  GimpContext          *context;
856
  GimpProgress         *progress;
857
  TileManager          *ret  = NULL;
858

859
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
860
  context = GIMP_CONTEXT (options);
861
862
863
864

  if (tr_tool->info_dialog)
    gtk_widget_set_sensitive (GTK_WIDGET (tr_tool->info_dialog->shell), FALSE);

865
866
  progress = gimp_progress_start (GIMP_PROGRESS (gdisp),
                                  tr_tool->progress_text, FALSE);
867

868
  if (gimp_item_get_linked (active_item))
869
    gimp_item_linked_transform (active_item, context,
870
                                &tr_tool->transform,
871
                                options->direction,
872
                                options->interpolation,
873
874
                                options->supersample,
                                options->recursion_level,
875
                                options->resize,
876
                                progress);
877

878
879
880
881
882
883
884
885
886
887
888
889
  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,
                           options->interpolation,
                           options->supersample,
                           options->recursion_level,
890
                           options->resize,
891
892
893
                           progress);
    }

894
895
896
897
898
  switch (options->type)
    {
    case GIMP_TRANSFORM_TYPE_LAYER:
    case GIMP_TRANSFORM_TYPE_SELECTION:
      {
899
        GimpTransformResize resize = options->resize;
900
901
902
903

        /*  always clip the selction and unfloated channels
         *  so they keep their size
         */
904
905
906
907
        if (tr_tool->original)
          {
            if (GIMP_IS_CHANNEL (active_item) &&
                tile_manager_bpp (tr_tool->original) == 1)
908
909
910
              {
                resize = GIMP_TRANSFORM_SIZE_CLIP;
              }
911
912
913
914
915
916
917
918
919
920

            ret =
              gimp_drawable_transform_tiles_affine (GIMP_DRAWABLE (active_item),
                                                    context,
                                                    tr_tool->original,
                                                    &tr_tool->transform,
                                                    options->direction,
                                                    options->interpolation,
                                                    options->supersample,
                                                    options->recursion_level,
921
                                                    resize,
922
923
                                                    progress);
          }
924
925
      }
      break;
926

927
    case GIMP_TRANSFORM_TYPE_PATH:
928
      gimp_item_transform (active_item, context,
929
930
931
                           &tr_tool->transform,
                           options->direction,
                           options->interpolation,
932
933
                           options->supersample,
                           options->recursion_level,
934
                           options->resize,
935
                           progress);
936
937
      break;
    }
938
939
940
941
942
943
944

  if (progress)
    gimp_progress_end (progress);

  return ret;
}

Michael Natterer's avatar
Michael Natterer committed
945
static void
946
947
gimp_transform_tool_doit (GimpTransformTool *tr_tool,
                          GimpDisplay       *gdisp)
Michael Natterer's avatar
Michael Natterer committed
948
{
949
  GimpTool             *tool        = GIMP_TOOL (tr_tool);
950
  GimpTransformOptions *options;
951
  GimpContext          *context;
952
  GimpItem             *active_item = NULL;
953
954
  TileManager          *new_tiles;
  gboolean              new_layer;