gimptransformtool.c 50.7 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"
Michael Natterer's avatar
Michael Natterer committed
42
#include "core/gimptoolinfo.h"
43

44
45
46
#include "vectors/gimpvectors.h"
#include "vectors/gimpstroke.h"

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

50
#include "display/gimpdisplay.h"
Michael Natterer's avatar
Michael Natterer committed
51
#include "display/gimpprogress.h"
52
53
54
#include "display/gimpdisplayshell.h"
#include "display/gimpdisplayshell-appearance.h"
#include "display/gimpdisplayshell-transform.h"
Nate Summers's avatar
Nate Summers committed
55

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

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

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

69

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

72

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

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

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

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

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

122
static TileManager *
123
                gimp_transform_tool_real_transform (GimpTransformTool *tr_tool,
124
                                                    GimpItem          *item,
125
126
                                                    GimpDisplay       *gdisp);

127
128
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
129
                                                    GimpDisplay       *gdisp);
130
131
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
132
                                                    GimpDisplay       *gdisp);
133
static void     gimp_transform_tool_recalc         (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);
137
static void     gimp_transform_tool_grid_recalc    (GimpTransformTool *tr_tool);
Michael Natterer's avatar
Michael Natterer committed
138

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

143
144
145
146
147
148
149
150
151
static void     gimp_transform_tool_notify_type    (GimpTransformOptions *options,
                                                    GParamSpec           *pspec,
                                                    GimpTransformTool    *tr_tool);
static void     gimp_transform_tool_notify_grid    (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
  GObjectClass      *object_class;
Nate Summers's avatar
Nate Summers committed
188
189
  GimpToolClass     *tool_class;
  GimpDrawToolClass *draw_class;
Nate Summers's avatar
Nate Summers committed
190

191
192
193
  object_class = G_OBJECT_CLASS (klass);
  tool_class   = GIMP_TOOL_CLASS (klass);
  draw_class   = GIMP_DRAW_TOOL_CLASS (klass);
Nate Summers's avatar
Nate Summers committed
194

195
  parent_class = g_type_class_peek_parent (klass);
Nate Summers's avatar
Nate Summers committed
196

197
  object_class->constructor  = gimp_transform_tool_constructor;
198
  object_class->finalize     = gimp_transform_tool_finalize;
Nate Summers's avatar
Nate Summers committed
199

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

  draw_class->draw           = gimp_transform_tool_draw;
Michael Natterer's avatar
Michael Natterer committed
211
212
213
214
215

  klass->dialog              = NULL;
  klass->prepare             = NULL;
  klass->motion              = NULL;
  klass->recalc              = NULL;
216
  klass->transform           = gimp_transform_tool_real_transform;
Nate Summers's avatar
Nate Summers committed
217
218
219
}

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

Michael Natterer's avatar
Michael Natterer committed
225
  tool = GIMP_TOOL (tr_tool);
Michael Natterer's avatar
Michael Natterer committed
226

Michael Natterer's avatar
Michael Natterer committed
227
228
229
  gimp_tool_control_set_scroll_lock (tool->control, TRUE);
  gimp_tool_control_set_preserve    (tool->control, FALSE);

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

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

239
  gimp_matrix3_identity (&tr_tool->transform);
240

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

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

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

  return object;
}

304
static void
305
gimp_transform_tool_finalize (GObject *object)
Nate Summers's avatar
Nate Summers committed
306
{
307
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (object);
308
309

  if (tr_tool->original)
310
    {
311
      tile_manager_unref (tr_tool->original);
312
313
      tr_tool->original = NULL;
    }
314

315
  if (tr_tool->info_dialog)
316
    {
317
318
      info_dialog_free (tr_tool->info_dialog);
      tr_tool->info_dialog = NULL;
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
    }

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

336
static gboolean
337
338
339
340
341
342
343
344
gimp_transform_tool_initialize (GimpTool    *tool,
				GimpDisplay *gdisp)
{
  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (tool);

  if (gdisp != tool->gdisp)
    {
      GimpDrawable *drawable = gimp_image_active_drawable (gdisp->gimage);
345
      gint          i;
346
347
348
349

      if (GIMP_IS_LAYER (drawable) &&
          gimp_layer_get_mask (GIMP_LAYER (drawable)))
        {
350
          g_message (_("Transformations do not work on "
351
                       "layers that contain layer masks."));
352
          return FALSE;
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
        }

      /*  Set the pointer to the active display  */
      tool->gdisp    = gdisp;
      tool->drawable = drawable;

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

  return TRUE;
385
386
}

387
static void
388
389
390
gimp_transform_tool_control (GimpTool       *tool,
			     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_recalc (tr_tool, gdisp);
401
      break;
402

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

408
409
410
411
    default:
      break;
    }

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

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

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

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

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

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

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

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

      /*  Restore the previous transformation info  */
      for (i = 0; i < TRAN_INFO_SIZE; i++)
462
	tr_tool->trans_info[i] = tr_tool->old_trans_info[i];
Nate Summers's avatar
Nate Summers committed
463
464

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

467
      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
468
    }
469
470

  gimp_tool_control_halt (tool->control);
Nate Summers's avatar
Nate Summers committed
471
472
}

473
static void
474
475
476
477
478
gimp_transform_tool_motion (GimpTool        *tool,
                            GimpCoords      *coords,
                            guint32          time,
		            GdkModifierType  state,
		            GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
479
{
480
  GimpTransformTool      *tr_tool = GIMP_TRANSFORM_TOOL (tool);
Michael Natterer's avatar
Michael Natterer committed
481
  GimpTransformToolClass *tr_tool_class;
Nate Summers's avatar
Nate Summers committed
482

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

487
  gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
488

Michael Natterer's avatar
Michael Natterer committed
489
490
491
  tr_tool->curx  = coords->x;
  tr_tool->cury  = coords->y;
  tr_tool->state = state;
Nate Summers's avatar
Nate Summers committed
492
493

  /*  recalculate the tool's transformation matrix  */
Michael Natterer's avatar
Michael Natterer committed
494
495
496
497
498
  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
499

Michael Natterer's avatar
Michael Natterer committed
500
501
502
503
504
505
      if (tr_tool_class->recalc)
        tr_tool_class->recalc (tr_tool, gdisp);
    }

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

507
  gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
Nate Summers's avatar
Nate Summers committed
508
509
}

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
#define RESPONSE_RESET 1

static void
gimp_transform_tool_key_press (GimpTool    *tool,
                               GdkEventKey *kevent,
                               GimpDisplay *gdisp)
{
  GimpTransformTool *trans_tool = GIMP_TRANSFORM_TOOL (tool);
  GimpDrawTool      *draw_tool = GIMP_DRAW_TOOL (tool);

  if (gdisp == draw_tool->gdisp)
    {
      switch (kevent->keyval)
        {
          case GDK_KP_Enter:
          case GDK_Return:
            gimp_transform_tool_response (NULL, GTK_RESPONSE_OK, trans_tool);
            break;

          case GDK_Delete:
          case GDK_BackSpace:
            gimp_transform_tool_response (NULL, RESPONSE_RESET, trans_tool);
            break;

          default:
            break;
        }
    }
}

540
541
542
543
544
545
546
static void
gimp_transform_tool_modifier_key (GimpTool        *tool,
                                  GdkModifierType  key,
                                  gboolean         press,
                                  GdkModifierType  state,
                                  GimpDisplay     *gdisp)
{
547
  GimpTransformOptions *options;
548

549
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
550

551
  if (key == GDK_CONTROL_MASK)
552
    {
553
      g_object_set (options,
554
555
                    "constrain-1", ! options->constrain_1,
                    NULL);
556
    }
557
  else if (key == GDK_MOD1_MASK)
558
    {
559
      g_object_set (options,
560
561
                    "constrain-2", ! options->constrain_2,
                    NULL);
562
563
564
    }
}

565
566
567
568
569
570
static void
gimp_transform_tool_oper_update (GimpTool        *tool,
                                 GimpCoords      *coords,
                                 GdkModifierType  state,
                                 GimpDisplay     *gdisp)
{
571
572
  GimpTransformTool *tr_tool   = GIMP_TRANSFORM_TOOL (tool);
  GimpDrawTool      *draw_tool = GIMP_DRAW_TOOL (tool);
573

574
575
576
  if (! tr_tool->use_grid)
    return;

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
  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)
	{
	  closest_dist = dist;
	  tr_tool->function = TRANSFORM_HANDLE_2;
	}

      dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                           coords->x, coords->y,
                                           tr_tool->tx3, tr_tool->ty3);
      if (dist < closest_dist)
	{
	  closest_dist = dist;
	  tr_tool->function = TRANSFORM_HANDLE_3;
	}

      dist = gimp_draw_tool_calc_distance (draw_tool, gdisp,
                                           coords->x, coords->y,
                                           tr_tool->tx4, tr_tool->ty4);
      if (dist < closest_dist)
	{
	  closest_dist = dist;
	  tr_tool->function = TRANSFORM_HANDLE_4;
	}

      if (gimp_draw_tool_on_handle (draw_tool, gdisp,
                                    coords->x, coords->y,
                                    GIMP_HANDLE_CIRCLE,
                                    tr_tool->tcx, tr_tool->tcy,
618
                                    HANDLE_SIZE, HANDLE_SIZE,
619
620
621
622
623
624
625
626
                                    GTK_ANCHOR_CENTER,
                                    FALSE))
	{
	  tr_tool->function = TRANSFORM_HANDLE_CENTER;
	}
    }
}

627
static void
628
629
630
631
gimp_transform_tool_cursor_update (GimpTool        *tool,
                                   GimpCoords      *coords,
			           GdkModifierType  state,
			           GimpDisplay     *gdisp)
Nate Summers's avatar
Nate Summers committed
632
{
633
  GimpTransformTool    *tr_tool = GIMP_TRANSFORM_TOOL (tool);
634
  GimpTransformOptions *options;
635

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

Michael Natterer's avatar
Michael Natterer committed
638
  if (tr_tool->use_grid)
Nate Summers's avatar
Nate Summers committed
639
    {
640
641
642
      GimpChannel        *selection = gimp_image_get_mask (gdisp->gimage);
      GdkCursorType       ctype     = GDK_TOP_LEFT_ARROW;
      GimpCursorModifier  cmodifier = GIMP_CURSOR_MODIFIER_NONE;
643

644
      switch (options->type)
Michael Natterer's avatar
Michael Natterer committed
645
        {
646
647
        case GIMP_TRANSFORM_TYPE_LAYER:
          {
648
            GimpDrawable *drawable = gimp_image_active_drawable (gdisp->gimage);
649
650
651
652
653
654

            if (drawable)
              {
                if (GIMP_IS_LAYER (drawable) &&
                    gimp_layer_get_mask (GIMP_LAYER (drawable)))
                  {
655
                    ctype = GIMP_CURSOR_BAD;
656
657
658
                  }
                else if (gimp_display_coords_in_active_drawable (gdisp, coords))
                  {
659
660
                    if (gimp_channel_is_empty (selection) ||
                        gimp_channel_value (selection, coords->x, coords->y))
661
                      {
662
                        ctype = GIMP_CURSOR_MOUSE;
663
664
665
666
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
              ctype = 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
            ctype = GIMP_CURSOR_MOUSE;
680
          else
681
            ctype = GIMP_CURSOR_BAD;
682
          break;
Michael Natterer's avatar
Michael Natterer committed
683
        }
684

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

      gimp_tool_control_set_cursor          (tool->control, ctype);
      gimp_tool_control_set_cursor_modifier (tool->control, cmodifier);
    }
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
851
                                    GimpDisplay       *gdisp)
{
852
  GimpTool             *tool = GIMP_TOOL (tr_tool);
853
  GimpTransformOptions *options;
854
  GimpContext          *context;
855
  GimpProgress         *progress;
856
  TileManager          *ret  = NULL;
857

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

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

  progress = gimp_progress_start (gdisp, tr_tool->progress_text, FALSE,
                                  NULL, NULL);

867
  if (gimp_item_get_linked (active_item))
868
    gimp_item_linked_transform (active_item, context,
869
                                &tr_tool->transform,
870
                                options->direction,
871
                                options->interpolation,
872
873
                                options->supersample,
                                options->recursion_level,
874
                                options->clip,
875
876
877
                                progress ?
                                gimp_progress_update_and_flush : NULL,
                                progress);
878

879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
  switch (options->type)
    {
    case GIMP_TRANSFORM_TYPE_LAYER:
    case GIMP_TRANSFORM_TYPE_SELECTION:
      {
        gboolean clip_result = options->clip;

        /*  always clip the selction and unfloated channels
         *  so they keep their size
         */
        if (GIMP_IS_CHANNEL (active_item) &&
            tile_manager_bpp (tr_tool->original) == 1)
          clip_result = TRUE;

        ret =
          gimp_drawable_transform_tiles_affine (GIMP_DRAWABLE (active_item),
895
                                                context,
896
                                                tr_tool->original,
897
                                                &tr_tool->transform,
898
899
                                                options->direction,
                                                options->interpolation,
900
                                                options->supersample,
901
                                                options->recursion_level,
902
903
                                                clip_result,
                                                progress ?
904
                                                gimp_progress_update_and_flush :
905
906
907
908
                                                NULL,
                                                progress);
      }
      break;
909

910
    case GIMP_TRANSFORM_TYPE_PATH:
911
      gimp_item_transform (active_item, context,
912
913
914
                           &tr_tool->transform,
                           options->direction,
                           options->interpolation,
915
916
                           options->supersample,
                           options->recursion_level,
917
918
919
920
921
                           options->clip,
                           progress ?
                           gimp_progress_update_and_flush :
                           NULL,
                           progress);
922
923
      break;
    }
924
925
926
927
928
929
930

  if (progress)
    gimp_progress_end (progress);

  return ret;
}

Michael Natterer's avatar
Michael Natterer committed
931
932
933
934
static void
gimp_transform_tool_doit (GimpTransformTool  *tr_tool,
		          GimpDisplay        *gdisp)
{
935
  GimpTool             *tool        = GIMP_TOOL (tr_tool);
936
  GimpTransformOptions *options;
937
  GimpContext          *context;
938
  GimpItem             *active_item = NULL;
939
940
  TileManager          *new_tiles;
  gboolean              new_layer;
Michael Natterer's avatar
Michael Natterer committed
941

942
  options = GIMP_TRANSFORM_OPTIONS (tool->tool_info->tool_options);
943
  context = GIMP_CONTEXT (options);
Michael Natterer's avatar
Michael Natterer committed
944

945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
  switch (options->type)
    {
    case GIMP_TRANSFORM_TYPE_LAYER:
      active_item = (GimpItem *) gimp_image_active_drawable (gdisp->gimage);
      break;

    case GIMP_TRANSFORM_TYPE_SELECTION:
      active_item = (GimpItem *) gimp_image_get_mask (gdisp->gimage);
      break;

    case GIMP_TRANSFORM_TYPE_PATH:
      active_item = (GimpItem *) gimp_image_get_active_vectors (gdisp->gimage);
      break;
    }

  if (! active_item)
    return;

  gimp_set_busy (gdisp->gimage->gimp);

Michael Natterer's avatar
Michael Natterer committed
965
  /* undraw the tool before we muck around with the transform matrix */
966
  gimp_draw_tool_stop (GIMP_DRAW_TOOL (tr_tool));
Michael Natterer's avatar
Michael Natterer committed
967

968
  /*  We're going to dirty this image, but we want to keep the tool around  */
969
  gimp_tool_control_set_preserve (tool->control, TRUE);
Michael Natterer's avatar
Michael Natterer committed
970
971

  /*  Start a transform undo group  */
972
  gimp_image_undo_group_start (gdisp->gimage, GIMP_UNDO_GROUP_TRANSFORM,
973
                               tool->tool_info->blurb);
Michael Natterer's avatar
Michael Natterer committed
974
975
976
977
978
979
980
981
982
983
984
985

  /* With the old UI, if original is NULL, then this is the
   * first transformation. In the new UI, it is always so, right?
   */
  g_assert (tr_tool->original == NULL);

  /*  Copy the current selection to the transform tool's private
   *  selection pointer, so that the original source can be repeatedly
   *  modified.
   */
  tool->drawable = gimp_image_active_drawable (gdisp->gimage);

986
987
988
989
  switch (options->type)
    {
    case GIMP_TRANSFORM_TYPE_LAYER:
      tr_tool->original = gimp_drawable_transform_cut (tool->drawable,
990
                                                       context,
991
992
993
994
                                                       &new_layer);
      break;

    case GIMP_TRANSFORM_TYPE_SELECTION:
995
      tr_tool->original = tile_manager_ref (GIMP_DRAWABLE (active_item)->tiles);
996
997
998
999
1000
1001
1002
      tile_manager_set_offsets (tr_tool->original, 0, 0);
      break;

    case GIMP_TRANSFORM_TYPE_PATH:
      tr_tool->original = NULL;
      break;
    }
Michael Natterer's avatar
Michael Natterer committed
1003
1004
1005

  /*  Send the request for the transformation to the tool...
   */
1006
  new_tiles = GIMP_TRANSFORM_TOOL_GET_CLASS (tr_tool)->transform (tr_tool,
1007
                                                                  active_item,
1008
                                                                  gdisp);
Michael Natterer's avatar
Michael Natterer committed
1009

Michael Natterer's avatar
Michael Natterer committed
1010
  gimp_transform_tool_prepare (tr_tool, gdisp);
Michael Natterer's avatar
Michael Natterer committed
1011
1012
  gimp_transform_tool_recalc (tr_tool, gdisp);

1013
  switch (options->type)
Michael Natterer's avatar
Michael Natterer committed
1014
    {
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
    case GIMP_TRANSFORM_TYPE_LAYER:
      if (new_tiles)
        {
          /*  paste the new transformed image to the gimage...also implement
           *  undo...
           */
          /*  FIXME: we should check if the drawable is still valid  */
          gimp_drawable_transform_paste (tool->drawable,
                                         new_tiles,
                                         new_layer);
          tile_manager_unref (new_tiles);
        }
      break;

     case GIMP_TRANSFORM_TYPE_SELECTION:
      if (new_tiles)
        {
1032
1033
          GimpDrawable *drawable = GIMP_DRAWABLE (active_item);

1034
          gimp_channel_push_undo (gimp_image_get_mask (gdisp->gimage), NULL);
Michael Natterer's avatar
Michael Natterer committed
1035

1036
          gimp_drawable_set_tiles (drawable, FALSE, NULL, new_tiles);
1037
          tile_manager_unref (new_tiles);
1038
        }
1039
1040
1041

      tile_manager_unref (tr_tool->original);
      tr_tool->original = NULL;
1042
1043
1044
      break;

    case GIMP_TRANSFORM_TYPE_PATH:
1045
      /*  Nothing to be done  */
1046
      break;