gimpimageundo.c 16 KB
Newer Older
1 2 3
/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7 8 9 10 11 12 13 14
 * (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
15
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17 18 19
 */

#include "config.h"

20 21
#include <string.h>

22
#include <cairo.h>
23
#include <gegl.h>
24

25
#include "libgimpbase/gimpbase.h"
26
#include "libgimpcolor/gimpcolor.h"
27
#include "libgimpconfig/gimpconfig.h"
28 29 30

#include "core-types.h"

31
#include "gimp-utils.h"
32
#include "gimpdrawable.h"
33
#include "gimpgrid.h"
34
#include "gimpimage.h"
35 36
#include "gimpimage-colormap.h"
#include "gimpimage-grid.h"
37
#include "gimpimage-private.h"
38
#include "gimpimageundo.h"
39
#include "gimpparasitelist.h"
40 41


42 43 44
enum
{
  PROP_0,
45 46
  PROP_PREVIOUS_ORIGIN_X,
  PROP_PREVIOUS_ORIGIN_Y,
47 48
  PROP_PREVIOUS_WIDTH,
  PROP_PREVIOUS_HEIGHT,
49 50
  PROP_GRID,
  PROP_PARASITE_NAME
51 52 53
};


54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
static void     gimp_image_undo_constructed  (GObject             *object);
static void     gimp_image_undo_set_property (GObject             *object,
                                              guint                property_id,
                                              const GValue        *value,
                                              GParamSpec          *pspec);
static void     gimp_image_undo_get_property (GObject             *object,
                                              guint                property_id,
                                              GValue              *value,
                                              GParamSpec          *pspec);

static gint64   gimp_image_undo_get_memsize  (GimpObject          *object,
                                              gint64              *gui_size);

static void     gimp_image_undo_pop          (GimpUndo            *undo,
                                              GimpUndoMode         undo_mode,
                                              GimpUndoAccumulator *accum);
static void     gimp_image_undo_free         (GimpUndo            *undo,
                                              GimpUndoMode         undo_mode);
72 73 74 75 76 77 78 79 80 81


G_DEFINE_TYPE (GimpImageUndo, gimp_image_undo, GIMP_TYPE_UNDO)

#define parent_class gimp_image_undo_parent_class


static void
gimp_image_undo_class_init (GimpImageUndoClass *klass)
{
82 83 84 85
  GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
  GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
  GimpUndoClass   *undo_class        = GIMP_UNDO_CLASS (klass);

86
  object_class->constructed      = gimp_image_undo_constructed;
87 88
  object_class->set_property     = gimp_image_undo_set_property;
  object_class->get_property     = gimp_image_undo_get_property;
89

90
  gimp_object_class->get_memsize = gimp_image_undo_get_memsize;
91

92 93
  undo_class->pop                = gimp_image_undo_pop;
  undo_class->free               = gimp_image_undo_free;
94

95
  g_object_class_install_property (object_class, PROP_PREVIOUS_ORIGIN_X,
96 97 98 99 100 101
                                   g_param_spec_int ("previous-origin-x",
                                                     NULL, NULL,
                                                     -GIMP_MAX_IMAGE_SIZE,
                                                     GIMP_MAX_IMAGE_SIZE,
                                                     0,
                                                     GIMP_PARAM_READWRITE));
102 103

  g_object_class_install_property (object_class, PROP_PREVIOUS_ORIGIN_Y,
104 105 106 107 108 109
                                   g_param_spec_int ("previous-origin-y",
                                                     NULL, NULL,
                                                     -GIMP_MAX_IMAGE_SIZE,
                                                     GIMP_MAX_IMAGE_SIZE,
                                                     0,
                                                     GIMP_PARAM_READWRITE));
110

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
  g_object_class_install_property (object_class, PROP_PREVIOUS_WIDTH,
                                   g_param_spec_int ("previous-width",
                                                     NULL, NULL,
                                                     -GIMP_MAX_IMAGE_SIZE,
                                                     GIMP_MAX_IMAGE_SIZE,
                                                     0,
                                                     GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_PREVIOUS_HEIGHT,
                                   g_param_spec_int ("previous-height",
                                                     NULL, NULL,
                                                     -GIMP_MAX_IMAGE_SIZE,
                                                     GIMP_MAX_IMAGE_SIZE,
                                                     0,
                                                     GIMP_PARAM_READWRITE));

127 128 129 130 131
  g_object_class_install_property (object_class, PROP_GRID,
                                   g_param_spec_object ("grid", NULL, NULL,
                                                        GIMP_TYPE_GRID,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT_ONLY));
132 133 134 135 136 137 138

  g_object_class_install_property (object_class, PROP_PARASITE_NAME,
                                   g_param_spec_string ("parasite-name",
                                                        NULL, NULL,
                                                        NULL,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT_ONLY));
139 140 141 142 143 144 145
}

static void
gimp_image_undo_init (GimpImageUndo *undo)
{
}

146 147
static void
gimp_image_undo_constructed (GObject *object)
148
{
149
  GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object);
150 151
  GimpImage     *image;

152 153
  if (G_OBJECT_CLASS (parent_class)->constructed)
    G_OBJECT_CLASS (parent_class)->constructed (object);
154 155 156

  image = GIMP_UNDO (object)->image;

157
  switch (GIMP_UNDO (object)->undo_type)
158
    {
159
    case GIMP_UNDO_IMAGE_TYPE:
160
      image_undo->base_type = gimp_image_base_type (image);
161 162 163
      break;

    case GIMP_UNDO_IMAGE_SIZE:
164 165
      image_undo->width  = gimp_image_get_width  (image);
      image_undo->height = gimp_image_get_height (image);
166 167 168
      break;

    case GIMP_UNDO_IMAGE_RESOLUTION:
169 170 171
      gimp_image_get_resolution (image,
                                 &image_undo->xresolution,
                                 &image_undo->yresolution);
172
      image_undo->resolution_unit = gimp_image_get_unit (image);
173 174 175
      break;

    case GIMP_UNDO_IMAGE_GRID:
176
      g_assert (GIMP_IS_GRID (image_undo->grid));
177 178 179
      break;

    case GIMP_UNDO_IMAGE_COLORMAP:
180 181
      image_undo->num_colors = gimp_image_get_colormap_size (image);
      image_undo->colormap   = g_memdup (gimp_image_get_colormap (image),
182
                                         GIMP_IMAGE_COLORMAP_SIZE);
183 184
      break;

185 186 187 188 189 190 191 192
    case GIMP_UNDO_PARASITE_ATTACH:
    case GIMP_UNDO_PARASITE_REMOVE:
      g_assert (image_undo->parasite_name != NULL);

      image_undo->parasite = gimp_parasite_copy
        (gimp_image_parasite_find (image, image_undo->parasite_name));
      break;

193 194
    default:
      g_assert_not_reached ();
195
    }
196 197
}

198 199 200 201 202 203 204 205 206 207
static void
gimp_image_undo_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object);

  switch (property_id)
    {
208
    case PROP_PREVIOUS_ORIGIN_X:
209
      image_undo->previous_origin_x = g_value_get_int (value);
210 211
      break;
    case PROP_PREVIOUS_ORIGIN_Y:
212
      image_undo->previous_origin_y = g_value_get_int (value);
213
      break;
214 215 216 217 218 219
    case PROP_PREVIOUS_WIDTH:
      image_undo->previous_width = g_value_get_int (value);
      break;
    case PROP_PREVIOUS_HEIGHT:
      image_undo->previous_height = g_value_get_int (value);
      break;
220 221 222 223 224 225 226 227
    case PROP_GRID:
      {
        GimpGrid *grid = g_value_get_object (value);

        if (grid)
          image_undo->grid = gimp_config_duplicate (GIMP_CONFIG (grid));
      }
      break;
228 229 230
    case PROP_PARASITE_NAME:
      image_undo->parasite_name = g_value_dup_string (value);
      break;
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_image_undo_get_property (GObject    *object,
                              guint       property_id,
                              GValue     *value,
                              GParamSpec *pspec)
{
  GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object);

  switch (property_id)
    {
248
    case PROP_PREVIOUS_ORIGIN_X:
249
      g_value_set_int (value, image_undo->previous_origin_x);
250 251
      break;
    case PROP_PREVIOUS_ORIGIN_Y:
252
      g_value_set_int (value, image_undo->previous_origin_y);
253
      break;
254 255 256 257 258 259
    case PROP_PREVIOUS_WIDTH:
      g_value_set_int (value, image_undo->previous_width);
      break;
    case PROP_PREVIOUS_HEIGHT:
      g_value_set_int (value, image_undo->previous_height);
      break;
260 261 262
    case PROP_GRID:
      g_value_set_object (value, image_undo->grid);
       break;
263 264 265
    case PROP_PARASITE_NAME:
      g_value_set_string (value, image_undo->parasite_name);
      break;
266 267 268 269 270 271 272

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

273 274 275 276 277 278 279 280
static gint64
gimp_image_undo_get_memsize (GimpObject *object,
                             gint64     *gui_size)
{
  GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object);
  gint64         memsize    = 0;

  if (image_undo->colormap)
281
    memsize += GIMP_IMAGE_COLORMAP_SIZE;
282

283 284 285 286
  memsize += gimp_object_get_memsize (GIMP_OBJECT (image_undo->grid),
                                      gui_size);
  memsize += gimp_string_get_memsize (image_undo->parasite_name);
  memsize += gimp_parasite_get_memsize (image_undo->parasite, gui_size);
287

288 289 290 291
  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}

292 293 294 295 296
static void
gimp_image_undo_pop (GimpUndo            *undo,
                     GimpUndoMode         undo_mode,
                     GimpUndoAccumulator *accum)
{
297 298 299
  GimpImageUndo    *image_undo = GIMP_IMAGE_UNDO (undo);
  GimpImage        *image      = undo->image;
  GimpImagePrivate *private    = GIMP_IMAGE_GET_PRIVATE (image);
300 301 302

  GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum);

303
  switch (undo->undo_type)
304
    {
305 306 307
    case GIMP_UNDO_IMAGE_TYPE:
      {
        GimpImageBaseType base_type;
308

309
        base_type = image_undo->base_type;
310
        image_undo->base_type = gimp_image_base_type (image);
311
        g_object_set (image, "base-type", base_type, NULL);
312

313
        gimp_image_colormap_changed (image, -1);
314

315
        if (image_undo->base_type != gimp_image_base_type (image))
316 317 318
          accum->mode_changed = TRUE;
      }
      break;
319

320 321
    case GIMP_UNDO_IMAGE_SIZE:
      {
322 323 324 325
        gint width;
        gint height;
        gint previous_origin_x;
        gint previous_origin_y;
326 327
        gint previous_width;
        gint previous_height;
328 329 330 331 332

        width             = image_undo->width;
        height            = image_undo->height;
        previous_origin_x = image_undo->previous_origin_x;
        previous_origin_y = image_undo->previous_origin_y;
333 334
        previous_width    = image_undo->previous_width;
        previous_height   = image_undo->previous_height;
335 336 337 338 339 340

        /* Transform to a redo */
        image_undo->width             = gimp_image_get_width  (image);
        image_undo->height            = gimp_image_get_height (image);
        image_undo->previous_origin_x = -previous_origin_x;
        image_undo->previous_origin_y = -previous_origin_y;
341 342
        image_undo->previous_width    = width;
        image_undo->previous_height   = height;
343

344
        g_object_set (image,
345 346 347
                      "width",  width,
                      "height", height,
                      NULL);
348

349
        gimp_drawable_invalidate_boundary
350
          (GIMP_DRAWABLE (gimp_image_get_mask (image)));
351

352 353
        if (gimp_image_get_width  (image) != image_undo->width ||
            gimp_image_get_height (image) != image_undo->height)
354 355 356 357
          {
            accum->size_changed      = TRUE;
            accum->previous_origin_x = previous_origin_x;
            accum->previous_origin_y = previous_origin_y;
358 359
            accum->previous_width    = previous_width;
            accum->previous_height   = previous_height;
360
          }
361 362 363 364
      }
      break;

    case GIMP_UNDO_IMAGE_RESOLUTION:
365 366 367
      {
        gdouble xres;
        gdouble yres;
368

369
        gimp_image_get_resolution (image, &xres, &yres);
370

371 372 373
        if (ABS (image_undo->xresolution - xres) >= 1e-5 ||
            ABS (image_undo->yresolution - yres) >= 1e-5)
          {
374 375
            private->xresolution = image_undo->xresolution;
            private->yresolution = image_undo->yresolution;
376

377 378
            image_undo->xresolution = xres;
            image_undo->yresolution = yres;
379

380 381 382
            accum->resolution_changed = TRUE;
          }
      }
383

384
      if (image_undo->resolution_unit != gimp_image_get_unit (image))
385 386 387
        {
          GimpUnit unit;

388
          unit = gimp_image_get_unit (image);
389
          private->resolution_unit = image_undo->resolution_unit;
390 391 392 393
          image_undo->resolution_unit = unit;

          accum->unit_changed = TRUE;
        }
394
      break;
395

396 397 398
    case GIMP_UNDO_IMAGE_GRID:
      {
        GimpGrid *grid;
399

400
        grid = gimp_config_duplicate (GIMP_CONFIG (gimp_image_get_grid (image)));
401

402
        gimp_image_set_grid (image, image_undo->grid, FALSE);
403

404 405 406 407 408 409 410 411 412
        g_object_unref (image_undo->grid);
        image_undo->grid = grid;
      }
      break;

    case GIMP_UNDO_IMAGE_COLORMAP:
      {
        guchar *colormap;
        gint    num_colors;
413

414 415
        num_colors = gimp_image_get_colormap_size (image);
        colormap   = g_memdup (gimp_image_get_colormap (image),
416
                               GIMP_IMAGE_COLORMAP_SIZE);
417

418
        gimp_image_set_colormap (image,
419 420
                                 image_undo->colormap, image_undo->num_colors,
                                 FALSE);
421

422 423
        if (image_undo->colormap)
          g_free (image_undo->colormap);
424

425 426 427 428 429
        image_undo->num_colors = num_colors;
        image_undo->colormap   = colormap;
      }
      break;

430 431 432
    case GIMP_UNDO_PARASITE_ATTACH:
    case GIMP_UNDO_PARASITE_REMOVE:
      {
433 434
        GimpParasite *parasite = image_undo->parasite;
        const gchar  *name;
435 436

        image_undo->parasite = gimp_parasite_copy
437
          (gimp_image_parasite_find (image, image_undo->parasite_name));
438 439

        if (parasite)
440
          gimp_parasite_list_add (private->parasites, parasite);
441
        else
442
          gimp_parasite_list_remove (private->parasites,
443 444
                                     image_undo->parasite_name);

445 446 447 448 449
        name = parasite ? parasite->name : image_undo->parasite_name;

        if (strcmp (name, "icc-profile") == 0)
          gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image));

450 451 452 453 454
        if (parasite)
          gimp_parasite_free (parasite);
      }
      break;

455
    default:
456 457 458
      g_assert_not_reached ();
    }
}
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476

static void
gimp_image_undo_free (GimpUndo     *undo,
                      GimpUndoMode  undo_mode)
{
  GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (undo);

  if (image_undo->grid)
    {
      g_object_unref (image_undo->grid);
      image_undo->grid = NULL;
    }

  if (image_undo->colormap)
    {
      g_free (image_undo->colormap);
      image_undo->colormap = NULL;
    }
477

478 479 480 481 482 483 484 485 486 487 488 489
  if (image_undo->parasite_name)
    {
      g_free (image_undo->parasite_name);
      image_undo->parasite_name = NULL;
    }

  if (image_undo->parasite)
    {
      gimp_parasite_free (image_undo->parasite);
      image_undo->parasite = NULL;
    }

490
  GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
491
}