/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * 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 #include #include "libgimpbase/gimpbase.h" #include "libgimpconfig/gimpconfig.h" #include "core-types.h" #include "base/pixel-region.h" #include "base/tile-manager.h" #include "paint-funcs/paint-funcs.h" #include "gimp-parasites.h" #include "gimp.h" #include "gimpgrid.h" #include "gimpimage-colormap.h" #include "gimpimage-grid.h" #include "gimpimage-guides.h" #include "gimpimage-sample-points.h" #include "gimpimage-undo.h" #include "gimpimage.h" #include "gimpitemundo.h" #include "gimplayer-floating-sel.h" #include "gimplayer.h" #include "gimplayermask.h" #include "gimplist.h" #include "gimpparasitelist.h" #include "gimpselection.h" #include "text/gimptextlayer.h" #include "text/gimptextundo.h" #include "vectors/gimpvectors.h" #include "gimp-intl.h" /*********************/ /* Image Type Undo */ /*********************/ typedef struct _ImageTypeUndo ImageTypeUndo; struct _ImageTypeUndo { GimpImageBaseType base_type; }; static gboolean undo_pop_image_type (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_image_type (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_image_type (GimpImage *gimage, const gchar *undo_desc) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (ImageTypeUndo), sizeof (ImageTypeUndo), GIMP_UNDO_IMAGE_TYPE, undo_desc, GIMP_DIRTY_IMAGE, undo_pop_image_type, undo_free_image_type, NULL))) { ImageTypeUndo *itu = new->data; itu->base_type = gimage->base_type; return TRUE; } return FALSE; } static gboolean undo_pop_image_type (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ImageTypeUndo *itu = undo->data; GimpImageBaseType tmp; tmp = itu->base_type; itu->base_type = undo->gimage->base_type; g_object_set (undo->gimage, "base-type", tmp, NULL); gimp_image_colormap_changed (undo->gimage, -1); if (itu->base_type != undo->gimage->base_type) accum->mode_changed = TRUE; return TRUE; } static void undo_free_image_type (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /*********************/ /* Image Size Undo */ /*********************/ typedef struct _ImageSizeUndo ImageSizeUndo; struct _ImageSizeUndo { gint width; gint height; }; static gboolean undo_pop_image_size (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_image_size (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_image_size (GimpImage *gimage, const gchar *undo_desc) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (ImageSizeUndo), sizeof (ImageSizeUndo), GIMP_UNDO_IMAGE_SIZE, undo_desc, GIMP_DIRTY_IMAGE | GIMP_DIRTY_IMAGE_SIZE, undo_pop_image_size, undo_free_image_size, NULL))) { ImageSizeUndo *isu = new->data; isu->width = gimage->width; isu->height = gimage->height; return TRUE; } return FALSE; } static gboolean undo_pop_image_size (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ImageSizeUndo *isu = undo->data; gint width; gint height; width = isu->width; height = isu->height; isu->width = undo->gimage->width; isu->height = undo->gimage->height; g_object_set (undo->gimage, "width", width, "height", height, NULL); gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (gimp_image_get_mask (undo->gimage))); if (undo->gimage->width != isu->width || undo->gimage->height != isu->height) accum->size_changed = TRUE; return TRUE; } static void undo_free_image_size (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /***************************/ /* Image Resolution Undo */ /***************************/ typedef struct _ResolutionUndo ResolutionUndo; struct _ResolutionUndo { gdouble xres; gdouble yres; GimpUnit unit; }; static gboolean undo_pop_image_resolution (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_image_resolution (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_image_resolution (GimpImage *gimage, const gchar *undo_desc) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (ResolutionUndo), sizeof (ResolutionUndo), GIMP_UNDO_IMAGE_RESOLUTION, undo_desc, GIMP_DIRTY_IMAGE, undo_pop_image_resolution, undo_free_image_resolution, NULL))) { ResolutionUndo *ru = new->data; ru->xres = gimage->xresolution; ru->yres = gimage->yresolution; ru->unit = gimage->resolution_unit; return TRUE; } return FALSE; } static gboolean undo_pop_image_resolution (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ResolutionUndo *ru = undo->data; if (ABS (ru->xres - undo->gimage->xresolution) >= 1e-5 || ABS (ru->yres - undo->gimage->yresolution) >= 1e-5) { gdouble xres; gdouble yres; xres = undo->gimage->xresolution; yres = undo->gimage->yresolution; undo->gimage->xresolution = ru->xres; undo->gimage->yresolution = ru->yres; ru->xres = xres; ru->yres = yres; accum->resolution_changed = TRUE; } if (ru->unit != undo->gimage->resolution_unit) { GimpUnit unit; unit = undo->gimage->resolution_unit; undo->gimage->resolution_unit = ru->unit; ru->unit = unit; accum->unit_changed = TRUE; } return TRUE; } static void undo_free_image_resolution (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /****************/ /* Guide Undo */ /****************/ typedef struct _GuideUndo GuideUndo; struct _GuideUndo { GimpGuide *guide; gint position; GimpOrientationType orientation; }; static gboolean undo_pop_image_guide (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_image_guide (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_image_guide (GimpImage *gimage, const gchar *undo_desc, GimpGuide *guide) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (guide != NULL, FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (GuideUndo), sizeof (GuideUndo), GIMP_UNDO_IMAGE_GUIDE, undo_desc, GIMP_DIRTY_IMAGE_META, undo_pop_image_guide, undo_free_image_guide, NULL))) { GuideUndo *gu = new->data; gu->guide = gimp_image_guide_ref (guide); gu->position = guide->position; gu->orientation = guide->orientation; return TRUE; } return FALSE; } static gboolean undo_pop_image_guide (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { GuideUndo *gu = undo->data; gint old_position; GimpOrientationType old_orientation; old_position = gu->guide->position; old_orientation = gu->guide->orientation; /* add and move guides manually (nor using the gimp_image_guide * API), because we might be in the middle of an image resizing * undo group and the guide's position might be temporarily out of * image. */ if (gu->guide->position == -1) { undo->gimage->guides = g_list_prepend (undo->gimage->guides, gu->guide); gu->guide->position = gu->position; gimp_image_guide_ref (gu->guide); gimp_image_update_guide (undo->gimage, gu->guide); } else if (gu->position == -1) { gimp_image_remove_guide (undo->gimage, gu->guide, FALSE); } else { gimp_image_update_guide (undo->gimage, gu->guide); gu->guide->position = gu->position; gimp_image_update_guide (undo->gimage, gu->guide); } gu->guide->orientation = gu->orientation; gu->position = old_position; gu->orientation = old_orientation; return TRUE; } static void undo_free_image_guide (GimpUndo *undo, GimpUndoMode undo_mode) { GuideUndo *gu = undo->data; gimp_image_guide_unref (gu->guide); g_free (gu); } /****************/ /* Grid Undo */ /****************/ typedef struct _GridUndo GridUndo; struct _GridUndo { GimpGrid *grid; }; static gboolean undo_pop_image_grid (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_image_grid (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_image_grid (GimpImage *gimage, const gchar *undo_desc, GimpGrid *grid) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_GRID (grid), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (GridUndo), sizeof (GridUndo), GIMP_UNDO_IMAGE_GRID, undo_desc, GIMP_DIRTY_IMAGE_META, undo_pop_image_grid, undo_free_image_grid, NULL))) { GridUndo *gu = new->data; gu->grid = gimp_config_duplicate (GIMP_CONFIG (grid)); return TRUE; } return FALSE; } static gboolean undo_pop_image_grid (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { GridUndo *gu = undo->data; GimpGrid *grid; grid = gimp_config_duplicate (GIMP_CONFIG (undo->gimage->grid)); gimp_image_set_grid (undo->gimage, gu->grid, FALSE); g_object_unref (gu->grid); gu->grid = grid; return TRUE; } static void undo_free_image_grid (GimpUndo *undo, GimpUndoMode undo_mode) { GridUndo *gu = undo->data; if (gu->grid) g_object_unref (gu->grid); g_free (gu); } /**********************/ /* Sampe Point Undo */ /**********************/ typedef struct _SamplePointUndo SamplePointUndo; struct _SamplePointUndo { GimpSamplePoint *sample_point; gint x; gint y; }; static gboolean undo_pop_image_sample_point (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_image_sample_point (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_image_sample_point (GimpImage *gimage, const gchar *undo_desc, GimpSamplePoint *sample_point) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (sample_point != NULL, FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (SamplePointUndo), sizeof (SamplePointUndo), GIMP_UNDO_IMAGE_SAMPLE_POINT, undo_desc, GIMP_DIRTY_IMAGE_META, undo_pop_image_sample_point, undo_free_image_sample_point, NULL))) { SamplePointUndo *spu = new->data; spu->sample_point = gimp_image_sample_point_ref (sample_point); spu->x = sample_point->x; spu->y = sample_point->y; return TRUE; } return FALSE; } static gboolean undo_pop_image_sample_point (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { SamplePointUndo *spu = undo->data; gint old_x; gint old_y; old_x = spu->sample_point->x; old_y = spu->sample_point->y; /* add and move sample points manually (nor using the * gimp_image_sample_point API), because we might be in the middle * of an image resizing undo group and the sample point's position * might be temporarily out of image. */ if (spu->sample_point->x == -1) { undo->gimage->sample_points = g_list_append (undo->gimage->sample_points, spu->sample_point); spu->sample_point->x = spu->x; spu->sample_point->y = spu->y; gimp_image_sample_point_ref (spu->sample_point); gimp_image_sample_point_added (undo->gimage, spu->sample_point); gimp_image_update_sample_point (undo->gimage, spu->sample_point); } else if (spu->x == -1) { gimp_image_remove_sample_point (undo->gimage, spu->sample_point, FALSE); } else { gimp_image_update_sample_point (undo->gimage, spu->sample_point); spu->sample_point->x = spu->x; spu->sample_point->y = spu->y; gimp_image_update_sample_point (undo->gimage, spu->sample_point); } spu->x = old_x; spu->y = old_y; return TRUE; } static void undo_free_image_sample_point (GimpUndo *undo, GimpUndoMode undo_mode) { SamplePointUndo *gu = undo->data; gimp_image_sample_point_unref (gu->sample_point); g_free (gu); } /*******************/ /* Colormap Undo */ /*******************/ typedef struct _ColormapUndo ColormapUndo; struct _ColormapUndo { gint num_colors; guchar *cmap; }; static gboolean undo_pop_image_colormap (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_image_colormap (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_image_colormap (GimpImage *gimage, const gchar *undo_desc) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (ColormapUndo), sizeof (ColormapUndo), GIMP_UNDO_IMAGE_COLORMAP, undo_desc, GIMP_DIRTY_IMAGE, undo_pop_image_colormap, undo_free_image_colormap, NULL))) { ColormapUndo *cu = new->data; cu->num_colors = gimp_image_get_colormap_size (gimage); cu->cmap = g_memdup (gimp_image_get_colormap (gimage), cu->num_colors * 3); return TRUE; } return FALSE; } static gboolean undo_pop_image_colormap (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ColormapUndo *cu = undo->data; guchar *cmap; gint num_colors; num_colors = gimp_image_get_colormap_size (undo->gimage); cmap = g_memdup (gimp_image_get_colormap (undo->gimage), num_colors * 3); gimp_image_set_colormap (undo->gimage, cu->cmap, cu->num_colors, FALSE); if (cu->cmap) g_free (cu->cmap); cu->num_colors = num_colors; cu->cmap = cmap; return TRUE; } static void undo_free_image_colormap (GimpUndo *undo, GimpUndoMode undo_mode) { ColormapUndo *cu = undo->data; if (cu->cmap) g_free (cu->cmap); g_free (cu); } /*******************/ /* Drawable Undo */ /*******************/ typedef struct _DrawableUndo DrawableUndo; struct _DrawableUndo { TileManager *tiles; gboolean sparse; gint x, y, width, height; }; static gboolean undo_pop_drawable (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_drawable (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_drawable (GimpImage *gimage, const gchar *undo_desc, GimpDrawable *drawable, TileManager *tiles, gboolean sparse, gint x, gint y, gint width, gint height) { GimpItem *item; GimpUndo *new; gint64 size; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); g_return_val_if_fail (tiles != NULL, FALSE); g_return_val_if_fail (sparse == TRUE || tile_manager_width (tiles) == width, FALSE); g_return_val_if_fail (sparse == TRUE || tile_manager_height (tiles) == height, FALSE); item = GIMP_ITEM (drawable); g_return_val_if_fail (gimp_item_is_attached (item), FALSE); g_return_val_if_fail (sparse == FALSE || tile_manager_width (tiles) == gimp_item_width (item), FALSE); g_return_val_if_fail (sparse == FALSE || tile_manager_height (tiles) == gimp_item_height (item), FALSE); size = sizeof (DrawableUndo) + tile_manager_get_memsize (tiles, sparse); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (DrawableUndo), GIMP_UNDO_DRAWABLE, undo_desc, GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, undo_pop_drawable, undo_free_drawable, "item", item, NULL))) { DrawableUndo *drawable_undo = new->data; drawable_undo->tiles = tile_manager_ref (tiles); drawable_undo->sparse = sparse; drawable_undo->x = x; drawable_undo->y = y; drawable_undo->width = width; drawable_undo->height = height; return TRUE; } return FALSE; } static gboolean undo_pop_drawable (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { DrawableUndo *drawable_undo = undo->data; undo->size -= tile_manager_get_memsize (drawable_undo->tiles, drawable_undo->sparse); gimp_drawable_swap_pixels (GIMP_DRAWABLE (GIMP_ITEM_UNDO (undo)->item), drawable_undo->tiles, drawable_undo->sparse, drawable_undo->x, drawable_undo->y, drawable_undo->width, drawable_undo->height); undo->size += tile_manager_get_memsize (drawable_undo->tiles, drawable_undo->sparse); return TRUE; } static void undo_free_drawable (GimpUndo *undo, GimpUndoMode undo_mode) { DrawableUndo *drawable_undo = undo->data; tile_manager_unref (drawable_undo->tiles); g_free (drawable_undo); } /***********************/ /* Drawable Mod Undo */ /***********************/ typedef struct _DrawableModUndo DrawableModUndo; struct _DrawableModUndo { TileManager *tiles; GimpImageType type; gint offset_x; gint offset_y; }; static gboolean undo_pop_drawable_mod (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_drawable_mod (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_drawable_mod (GimpImage *gimage, const gchar *undo_desc, GimpDrawable *drawable) { GimpUndo *new; gint64 size; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE); size = sizeof (DrawableModUndo) + tile_manager_get_memsize (drawable->tiles, FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (DrawableModUndo), GIMP_UNDO_DRAWABLE_MOD, undo_desc, GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, undo_pop_drawable_mod, undo_free_drawable_mod, "item", drawable, NULL))) { DrawableModUndo *drawable_undo = new->data; drawable_undo->tiles = tile_manager_ref (drawable->tiles); drawable_undo->type = drawable->type; drawable_undo->offset_x = GIMP_ITEM (drawable)->offset_x; drawable_undo->offset_y = GIMP_ITEM (drawable)->offset_y; return TRUE; } return FALSE; } static gboolean undo_pop_drawable_mod (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { DrawableModUndo *drawable_undo = undo->data; GimpDrawable *drawable = GIMP_DRAWABLE (GIMP_ITEM_UNDO (undo)->item); TileManager *tiles; GimpImageType drawable_type; gint offset_x, offset_y; undo->size -= tile_manager_get_memsize (drawable_undo->tiles, FALSE); tiles = drawable_undo->tiles; drawable_type = drawable_undo->type; offset_x = drawable_undo->offset_x; offset_y = drawable_undo->offset_y; drawable_undo->tiles = tile_manager_ref (drawable->tiles); drawable_undo->type = drawable->type; drawable_undo->offset_x = GIMP_ITEM (drawable)->offset_x; drawable_undo->offset_y = GIMP_ITEM (drawable)->offset_y; gimp_drawable_set_tiles_full (drawable, FALSE, NULL, tiles, drawable_type, offset_x, offset_y); tile_manager_unref (tiles); undo->size += tile_manager_get_memsize (drawable_undo->tiles, FALSE); return TRUE; } static void undo_free_drawable_mod (GimpUndo *undo, GimpUndoMode undo_mode) { DrawableModUndo *drawable_undo = undo->data; tile_manager_unref (drawable_undo->tiles); g_free (drawable_undo); } /***************/ /* Mask Undo */ /***************/ typedef struct _MaskUndo MaskUndo; struct _MaskUndo { TileManager *tiles; /* the actual mask */ gint x, y; /* offsets */ }; static gboolean undo_pop_mask (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_mask (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_mask (GimpImage *gimage, const gchar *undo_desc, GimpChannel *mask) { TileManager *undo_tiles = NULL; gint x1, y1, x2, y2; GimpUndo *new; gint64 size; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (mask), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (mask)), FALSE); size = sizeof (MaskUndo); if (gimp_channel_bounds (mask, &x1, &y1, &x2, &y2)) { GimpDrawable *drawable = GIMP_DRAWABLE (mask); PixelRegion srcPR, destPR; undo_tiles = tile_manager_new (x2 - x1, y2 - y1, gimp_drawable_bytes (drawable)); pixel_region_init (&srcPR, gimp_drawable_data (drawable), x1, y1, x2 - x1, y2 - y1, FALSE); pixel_region_init (&destPR, undo_tiles, 0, 0, x2 - x1, y2 - y1, TRUE); copy_region (&srcPR, &destPR); size += tile_manager_get_memsize (undo_tiles, FALSE); } if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (MaskUndo), GIMP_UNDO_MASK, undo_desc, GIMP_IS_SELECTION (mask) ? GIMP_DIRTY_SELECTION : GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, undo_pop_mask, undo_free_mask, "item", mask, NULL))) { MaskUndo *mask_undo = new->data; mask_undo->tiles = undo_tiles; mask_undo->x = x1; mask_undo->y = y1; return TRUE; } if (undo_tiles) tile_manager_unref (undo_tiles); return FALSE; } static gboolean undo_pop_mask (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { MaskUndo *mu = undo->data; GimpChannel *channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item); TileManager *new_tiles; PixelRegion srcPR, destPR; gint x1, y1, x2, y2; gint width = 0; gint height = 0; guchar empty = 0; if (mu->tiles) undo->size -= tile_manager_get_memsize (mu->tiles, FALSE); if (gimp_channel_bounds (channel, &x1, &y1, &x2, &y2)) { new_tiles = tile_manager_new ((x2 - x1), (y2 - y1), 1); pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles, x1, y1, (x2 - x1), (y2 - y1), FALSE); pixel_region_init (&destPR, new_tiles, 0, 0, (x2 - x1), (y2 - y1), TRUE); copy_region (&srcPR, &destPR); pixel_region_init (&srcPR, GIMP_DRAWABLE (channel)->tiles, x1, y1, (x2 - x1), (y2 - y1), TRUE); color_region (&srcPR, &empty); } else { new_tiles = NULL; } if (mu->tiles) { width = tile_manager_width (mu->tiles); height = tile_manager_height (mu->tiles); pixel_region_init (&srcPR, mu->tiles, 0, 0, width, height, FALSE); pixel_region_init (&destPR, GIMP_DRAWABLE (channel)->tiles, mu->x, mu->y, width, height, TRUE); copy_region (&srcPR, &destPR); tile_manager_unref (mu->tiles); } /* invalidate the current bounds and boundary of the mask */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (channel)); if (mu->tiles) { channel->empty = FALSE; channel->x1 = mu->x; channel->y1 = mu->y; channel->x2 = mu->x + width; channel->y2 = mu->y + height; } else { channel->empty = TRUE; channel->x1 = 0; channel->y1 = 0; channel->x2 = GIMP_ITEM (channel)->width; channel->y2 = GIMP_ITEM (channel)->height; } /* we know the bounds */ channel->bounds_known = TRUE; /* set the new mask undo parameters */ mu->tiles = new_tiles; mu->x = x1; mu->y = y1; gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, GIMP_ITEM (channel)->width, GIMP_ITEM (channel)->height); if (mu->tiles) undo->size += tile_manager_get_memsize (mu->tiles, FALSE); return TRUE; } static void undo_free_mask (GimpUndo *undo, GimpUndoMode undo_mode) { MaskUndo *mu = undo->data; if (mu->tiles) tile_manager_unref (mu->tiles); g_free (mu); } /**********************/ /* Item Rename Undo */ /**********************/ typedef struct _ItemRenameUndo ItemRenameUndo; struct _ItemRenameUndo { gchar *name; }; static gboolean undo_pop_item_rename (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_item_rename (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_item_rename (GimpImage *gimage, const gchar *undo_desc, GimpItem *item) { GimpUndo *new; gint64 size; const gchar *name; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); g_return_val_if_fail (gimp_item_is_attached (item), FALSE); name = gimp_object_get_name (GIMP_OBJECT (item)); size = sizeof (ItemRenameUndo) + strlen (name) + 1; if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (ItemRenameUndo), GIMP_UNDO_ITEM_RENAME, undo_desc, GIMP_DIRTY_ITEM_META, undo_pop_item_rename, undo_free_item_rename, "item", item, NULL))) { ItemRenameUndo *iru = new->data; iru->name = g_strdup (name); return TRUE; } return FALSE; } static gboolean undo_pop_item_rename (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ItemRenameUndo *iru = undo->data; GimpItem *item = GIMP_ITEM_UNDO (undo)->item; gchar *tmp; undo->size -= strlen (iru->name); tmp = g_strdup (gimp_object_get_name (GIMP_OBJECT (item))); gimp_object_set_name (GIMP_OBJECT (item), iru->name); g_free (iru->name); iru->name = tmp; undo->size += strlen (iru->name); return TRUE; } static void undo_free_item_rename (GimpUndo *undo, GimpUndoMode undo_mode) { ItemRenameUndo *iru = undo->data; g_free (iru->name); g_free (iru); } /****************************/ /* Item displacement Undo */ /****************************/ typedef struct _ItemDisplaceUndo ItemDisplaceUndo; struct _ItemDisplaceUndo { gint old_offset_x; gint old_offset_y; }; static gboolean undo_pop_item_displace (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_item_displace (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_item_displace (GimpImage *gimage, const gchar *undo_desc, GimpItem *item) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); g_return_val_if_fail (gimp_item_is_attached (item), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (ItemDisplaceUndo), sizeof (ItemDisplaceUndo), GIMP_UNDO_ITEM_DISPLACE, undo_desc, GIMP_IS_DRAWABLE (item) ? GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE : GIMP_DIRTY_ITEM | GIMP_DIRTY_VECTORS, undo_pop_item_displace, undo_free_item_displace, "item", item, NULL))) { ItemDisplaceUndo *idu = new->data; gimp_item_offsets (item, &idu->old_offset_x, &idu->old_offset_y); return TRUE; } return FALSE; } static gboolean undo_pop_item_displace (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ItemDisplaceUndo *idu = undo->data; GimpItem *item = GIMP_ITEM_UNDO (undo)->item; gint offset_x; gint offset_y; gimp_item_offsets (item, &offset_x, &offset_y); gimp_item_translate (item, idu->old_offset_x - offset_x, idu->old_offset_y - offset_y, FALSE); idu->old_offset_x = offset_x; idu->old_offset_y = offset_y; return TRUE; } static void undo_free_item_displace (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /******************************/ /* Item Visibility Undo */ /******************************/ typedef struct _ItemVisibilityUndo ItemVisibilityUndo; struct _ItemVisibilityUndo { gboolean old_visible; }; static gboolean undo_pop_item_visibility (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_item_visibility (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_item_visibility (GimpImage *gimage, const gchar *undo_desc, GimpItem *item) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); g_return_val_if_fail (gimp_item_is_attached (item), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (ItemVisibilityUndo), sizeof (ItemVisibilityUndo), GIMP_UNDO_ITEM_VISIBILITY, undo_desc, GIMP_DIRTY_ITEM_META, undo_pop_item_visibility, undo_free_item_visibility, "item", item, NULL))) { ItemVisibilityUndo *ivu = new->data; ivu->old_visible = gimp_item_get_visible (item); return TRUE; } return FALSE; } static gboolean undo_pop_item_visibility (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ItemVisibilityUndo *ivu = undo->data; GimpItem *item = GIMP_ITEM_UNDO (undo)->item; gboolean visible; visible = gimp_item_get_visible (item); gimp_item_set_visible (item, ivu->old_visible, FALSE); ivu->old_visible = visible; return TRUE; } static void undo_free_item_visibility (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /**********************/ /* Item linked Undo */ /**********************/ typedef struct _ItemLinkedUndo ItemLinkedUndo; struct _ItemLinkedUndo { gboolean old_linked; }; static gboolean undo_pop_item_linked (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_item_linked (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_item_linked (GimpImage *gimage, const gchar *undo_desc, GimpItem *item) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); g_return_val_if_fail (gimp_item_is_attached (item), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (ItemLinkedUndo), sizeof (ItemLinkedUndo), GIMP_UNDO_ITEM_LINKED, undo_desc, GIMP_DIRTY_ITEM_META, undo_pop_item_linked, undo_free_item_linked, "item", item, NULL))) { ItemLinkedUndo *ilu = new->data; ilu->old_linked = gimp_item_get_linked (item); return TRUE; } return FALSE; } static gboolean undo_pop_item_linked (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ItemLinkedUndo *ilu = undo->data; GimpItem *item = GIMP_ITEM_UNDO (undo)->item; gboolean linked; linked = gimp_item_get_linked (item); gimp_item_set_linked (item, ilu->old_linked, FALSE); ilu->old_linked = linked; return TRUE; } static void undo_free_item_linked (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /***************************/ /* Layer Add/Remove Undo */ /***************************/ typedef struct _LayerUndo LayerUndo; struct _LayerUndo { gint prev_position; /* former position in list */ GimpLayer *prev_layer; /* previous active layer */ }; static gboolean undo_push_layer (GimpImage *gimage, const gchar *undo_desc, GimpUndoType type, GimpLayer *layer, gint prev_position, GimpLayer *prev_layer); static gboolean undo_pop_layer (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_layer (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_layer_add (GimpImage *gimage, const gchar *undo_desc, GimpLayer *layer, gint prev_position, GimpLayer *prev_layer) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); g_return_val_if_fail (! gimp_item_is_attached (GIMP_ITEM (layer)), FALSE); g_return_val_if_fail (prev_layer == NULL || GIMP_IS_LAYER (prev_layer), FALSE); return undo_push_layer (gimage, undo_desc, GIMP_UNDO_LAYER_ADD, layer, prev_position, prev_layer); } gboolean gimp_image_undo_push_layer_remove (GimpImage *gimage, const gchar *undo_desc, GimpLayer *layer, gint prev_position, GimpLayer *prev_layer) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), FALSE); g_return_val_if_fail (prev_layer == NULL || GIMP_IS_LAYER (prev_layer), FALSE); return undo_push_layer (gimage, undo_desc, GIMP_UNDO_LAYER_REMOVE, layer, prev_position, prev_layer); } static gboolean undo_push_layer (GimpImage *gimage, const gchar *undo_desc, GimpUndoType type, GimpLayer *layer, gint prev_position, GimpLayer *prev_layer) { GimpUndo *new; gint64 size; size = sizeof (LayerUndo); if (type == GIMP_UNDO_LAYER_REMOVE) size += gimp_object_get_memsize (GIMP_OBJECT (layer), NULL); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (LayerUndo), type, undo_desc, GIMP_DIRTY_IMAGE_STRUCTURE, undo_pop_layer, undo_free_layer, "item", layer, NULL))) { LayerUndo *lu = new->data; lu->prev_position = prev_position; lu->prev_layer = prev_layer; return TRUE; } return FALSE; } static gboolean undo_pop_layer (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { LayerUndo *lu = undo->data; GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); gboolean old_has_alpha; old_has_alpha = gimp_image_has_alpha (undo->gimage); if ((undo_mode == GIMP_UNDO_MODE_UNDO && undo->undo_type == GIMP_UNDO_LAYER_ADD) || (undo_mode == GIMP_UNDO_MODE_REDO && undo->undo_type == GIMP_UNDO_LAYER_REMOVE)) { /* remove layer */ undo->size += gimp_object_get_memsize (GIMP_OBJECT (layer), NULL); /* record the current position */ lu->prev_position = gimp_image_get_layer_index (undo->gimage, layer); gimp_container_remove (undo->gimage->layers, GIMP_OBJECT (layer)); undo->gimage->layer_stack = g_slist_remove (undo->gimage->layer_stack, layer); if (gimp_layer_is_floating_sel (layer)) { /* invalidate the boundary *before* setting the * floating_sel pointer to NULL because the selection's * outline is affected by the floating_sel and won't be * completely cleared otherwise (bug #160247). */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (layer)); undo->gimage->floating_sel = NULL; /* activate the underlying drawable */ floating_sel_activate_drawable (layer); gimp_image_floating_selection_changed (undo->gimage); } else if (layer == gimp_image_get_active_layer (undo->gimage)) { if (lu->prev_layer) { gimp_image_set_active_layer (undo->gimage, lu->prev_layer); } else if (undo->gimage->layer_stack) { gimp_image_set_active_layer (undo->gimage, undo->gimage->layer_stack->data); } else { gimp_image_set_active_layer (undo->gimage, NULL); } } gimp_item_removed (GIMP_ITEM (layer)); } else { /* restore layer */ undo->size -= gimp_object_get_memsize (GIMP_OBJECT (layer), NULL); /* record the active layer */ lu->prev_layer = gimp_image_get_active_layer (undo->gimage); /* if this is a floating selection, set the fs pointer */ if (gimp_layer_is_floating_sel (layer)) undo->gimage->floating_sel = layer; gimp_container_insert (undo->gimage->layers, GIMP_OBJECT (layer), lu->prev_position); gimp_image_set_active_layer (undo->gimage, layer); if (gimp_layer_is_floating_sel (layer)) gimp_image_floating_selection_changed (undo->gimage); GIMP_ITEM (layer)->removed = FALSE; if (layer->mask) GIMP_ITEM (layer->mask)->removed = FALSE; } if (old_has_alpha != gimp_image_has_alpha (undo->gimage)) accum->alpha_changed = TRUE; return TRUE; } static void undo_free_layer (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /***************************/ /* Layer re-position Undo */ /***************************/ typedef struct _LayerRepositionUndo LayerRepositionUndo; struct _LayerRepositionUndo { gint old_position; }; static gboolean undo_pop_layer_reposition (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_layer_reposition (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_layer_reposition (GimpImage *gimage, const gchar *undo_desc, GimpLayer *layer) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (LayerRepositionUndo), sizeof (LayerRepositionUndo), GIMP_UNDO_LAYER_REPOSITION, undo_desc, GIMP_DIRTY_IMAGE_STRUCTURE, undo_pop_layer_reposition, undo_free_layer_reposition, "item", layer, NULL))) { LayerRepositionUndo *lru = new->data; lru->old_position = gimp_image_get_layer_index (gimage, layer); return TRUE; } return FALSE; } static gboolean undo_pop_layer_reposition (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { LayerRepositionUndo *lru = undo->data; GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); gint pos; /* what's the layer's current index? */ pos = gimp_image_get_layer_index (undo->gimage, layer); gimp_image_position_layer (undo->gimage, layer, lru->old_position, FALSE, NULL); lru->old_position = pos; return TRUE; } static void undo_free_layer_reposition (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /***************************/ /* Layer properties Undo */ /***************************/ typedef struct _LayerPropertiesUndo LayerPropertiesUndo; struct _LayerPropertiesUndo { GimpLayerModeEffects old_mode; gdouble old_opacity; gboolean old_lock_alpha; }; static gboolean undo_push_layer_properties (GimpImage *gimage, GimpUndoType undo_type, const gchar *undo_desc, GimpLayer *layer); static gboolean undo_pop_layer_properties (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_layer_properties (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_layer_mode (GimpImage *gimage, const gchar *undo_desc, GimpLayer *layer) { return undo_push_layer_properties (gimage, GIMP_UNDO_LAYER_MODE, undo_desc, layer); } gboolean gimp_image_undo_push_layer_opacity (GimpImage *gimage, const gchar *undo_desc, GimpLayer *layer) { return undo_push_layer_properties (gimage, GIMP_UNDO_LAYER_OPACITY, undo_desc, layer); } gboolean gimp_image_undo_push_layer_lock_alpha (GimpImage *gimage, const gchar *undo_desc, GimpLayer *layer) { return undo_push_layer_properties (gimage, GIMP_UNDO_LAYER_LOCK_ALPHA, undo_desc, layer); } static gboolean undo_push_layer_properties (GimpImage *gimage, GimpUndoType undo_type, const gchar *undo_desc, GimpLayer *layer) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (LayerPropertiesUndo), sizeof (LayerPropertiesUndo), undo_type, undo_desc, GIMP_DIRTY_ITEM_META, undo_pop_layer_properties, undo_free_layer_properties, "item", layer, NULL))) { LayerPropertiesUndo *lpu = new->data; lpu->old_mode = gimp_layer_get_mode (layer); lpu->old_opacity = gimp_layer_get_opacity (layer); lpu->old_lock_alpha = gimp_layer_get_lock_alpha (layer); return TRUE; } return FALSE; } static gboolean undo_pop_layer_properties (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { LayerPropertiesUndo *lpu = undo->data; GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); switch (undo->undo_type) { case GIMP_UNDO_LAYER_MODE: { GimpLayerModeEffects mode; mode = gimp_layer_get_mode (layer); gimp_layer_set_mode (layer, lpu->old_mode, FALSE); lpu->old_mode = mode; } break; case GIMP_UNDO_LAYER_OPACITY: { gdouble opacity; opacity = gimp_layer_get_opacity (layer); gimp_layer_set_opacity (layer, lpu->old_opacity, FALSE); lpu->old_opacity = opacity; } break; case GIMP_UNDO_LAYER_LOCK_ALPHA: { gboolean lock_alpha; lock_alpha = gimp_layer_get_lock_alpha (layer); gimp_layer_set_lock_alpha (layer, lpu->old_lock_alpha, FALSE); lpu->old_lock_alpha = lock_alpha; } break; default: g_return_val_if_reached (FALSE); break; } return TRUE; } static void undo_free_layer_properties (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /*********************/ /* Text Layer Undo */ /*********************/ gboolean gimp_image_undo_push_text_layer (GimpImage *gimage, const gchar *undo_desc, GimpTextLayer *layer, const GParamSpec *pspec) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_TEXT_UNDO, 0, 0, GIMP_UNDO_TEXT_LAYER, undo_desc, GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, NULL, NULL, "item", layer, "param", pspec, NULL))) { return TRUE; } return FALSE; } /******************************/ /* Text Layer Modified Undo */ /******************************/ typedef struct _TextLayerModifiedUndo TextLayerModifiedUndo; struct _TextLayerModifiedUndo { gboolean old_modified; }; static gboolean undo_pop_text_layer_modified (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_text_layer_modified (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_text_layer_modified (GimpImage *gimage, const gchar *undo_desc, GimpTextLayer *layer) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (TextLayerModifiedUndo), sizeof (TextLayerModifiedUndo), GIMP_UNDO_TEXT_LAYER_MODIFIED, undo_desc, GIMP_DIRTY_ITEM_META, undo_pop_text_layer_modified, undo_free_text_layer_modified, "item", layer, NULL))) { TextLayerModifiedUndo *modified_undo = new->data; modified_undo->old_modified = layer->modified; return TRUE; } return FALSE; } static gboolean undo_pop_text_layer_modified (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { TextLayerModifiedUndo *modified_undo = undo->data; GimpTextLayer *layer; gboolean modified; layer = GIMP_TEXT_LAYER (GIMP_ITEM_UNDO (undo)->item); #if 0 g_print ("setting layer->modified from %s to %s\n", layer->modified ? "TRUE" : "FALSE", modified_undo->old_modified ? "TRUE" : "FALSE"); #endif modified = layer->modified; g_object_set (layer, "modified", modified_undo->old_modified, NULL); modified_undo->old_modified = modified; gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer)); return TRUE; } static void undo_free_text_layer_modified (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /********************************/ /* Layer Mask Add/Remove Undo */ /********************************/ typedef struct _LayerMaskUndo LayerMaskUndo; struct _LayerMaskUndo { GimpLayerMask *mask; }; static gboolean undo_push_layer_mask (GimpImage *gimage, const gchar *undo_desc, GimpUndoType type, GimpLayer *layer, GimpLayerMask *mask); static gboolean undo_pop_layer_mask (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_layer_mask (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_layer_mask_add (GimpImage *gimage, const gchar *undo_desc, GimpLayer *layer, GimpLayerMask *mask) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), FALSE); g_return_val_if_fail (GIMP_IS_LAYER_MASK (mask), FALSE); g_return_val_if_fail (! gimp_item_is_attached (GIMP_ITEM (mask)), FALSE); return undo_push_layer_mask (gimage, undo_desc, GIMP_UNDO_LAYER_MASK_ADD, layer, mask); } gboolean gimp_image_undo_push_layer_mask_remove (GimpImage *gimage, const gchar *undo_desc, GimpLayer *layer, GimpLayerMask *mask) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (layer), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)), FALSE); g_return_val_if_fail (GIMP_IS_LAYER_MASK (mask), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (mask)), FALSE); g_return_val_if_fail (mask->layer == layer, FALSE); g_return_val_if_fail (layer->mask == mask, FALSE); return undo_push_layer_mask (gimage, undo_desc, GIMP_UNDO_LAYER_MASK_REMOVE, layer, mask); } static gboolean undo_push_layer_mask (GimpImage *gimage, const gchar *undo_desc, GimpUndoType type, GimpLayer *layer, GimpLayerMask *mask) { GimpUndo *new; gint64 size; size = sizeof (LayerMaskUndo); if (type == GIMP_UNDO_LAYER_MASK_REMOVE) size += gimp_object_get_memsize (GIMP_OBJECT (mask), NULL); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (LayerMaskUndo), type, undo_desc, GIMP_DIRTY_IMAGE_STRUCTURE, undo_pop_layer_mask, undo_free_layer_mask, "item", layer, NULL))) { LayerMaskUndo *lmu = new->data; lmu->mask = g_object_ref (mask); return TRUE; } return FALSE; } static gboolean undo_pop_layer_mask (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { LayerMaskUndo *lmu = undo->data; GimpLayer *layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); if ((undo_mode == GIMP_UNDO_MODE_UNDO && undo->undo_type == GIMP_UNDO_LAYER_MASK_ADD) || (undo_mode == GIMP_UNDO_MODE_REDO && undo->undo_type == GIMP_UNDO_LAYER_MASK_REMOVE)) { /* remove layer mask */ undo->size += gimp_object_get_memsize (GIMP_OBJECT (lmu->mask), NULL); gimp_layer_apply_mask (layer, GIMP_MASK_DISCARD, FALSE); } else { /* restore layer */ undo->size -= gimp_object_get_memsize (GIMP_OBJECT (lmu->mask), NULL); gimp_layer_add_mask (layer, lmu->mask, FALSE); GIMP_ITEM (lmu->mask)->removed = FALSE; } return TRUE; } static void undo_free_layer_mask (GimpUndo *undo, GimpUndoMode undo_mode) { LayerMaskUndo *lmu = undo->data; g_object_unref (lmu->mask); g_free (lmu); } /******************************/ /* Layer Mask Property Undo */ /******************************/ typedef struct _LayerMaskPropertyUndo LayerMaskPropertyUndo; struct _LayerMaskPropertyUndo { gboolean old_apply; gboolean old_show; }; static gboolean undo_push_layer_mask_properties (GimpImage *gimage, GimpUndoType undo_type, const gchar *undo_desc, GimpLayerMask *mask); static gboolean undo_pop_layer_mask_properties (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_layer_mask_properties (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_layer_mask_apply (GimpImage *gimage, const gchar *undo_desc, GimpLayerMask *mask) { return undo_push_layer_mask_properties (gimage, GIMP_UNDO_LAYER_MASK_APPLY, undo_desc, mask); } gboolean gimp_image_undo_push_layer_mask_show (GimpImage *gimage, const gchar *undo_desc, GimpLayerMask *mask) { return undo_push_layer_mask_properties (gimage, GIMP_UNDO_LAYER_MASK_SHOW, undo_desc, mask); } static gboolean undo_push_layer_mask_properties (GimpImage *gimage, GimpUndoType undo_type, const gchar *undo_desc, GimpLayerMask *mask) { GimpUndo *new; gint64 size; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER_MASK (mask), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (mask)), FALSE); size = sizeof (LayerMaskPropertyUndo); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (LayerMaskPropertyUndo), undo_type, undo_desc, GIMP_DIRTY_ITEM_META, undo_pop_layer_mask_properties, undo_free_layer_mask_properties, "item", mask, NULL))) { LayerMaskPropertyUndo *lmp_undo = new->data; lmp_undo->old_apply = gimp_layer_mask_get_apply (mask); lmp_undo->old_show = gimp_layer_mask_get_show (mask); return TRUE; } return FALSE; } static gboolean undo_pop_layer_mask_properties (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { LayerMaskPropertyUndo *lmp_undo = undo->data; GimpLayerMask *mask; gboolean val; mask = GIMP_LAYER_MASK (GIMP_ITEM_UNDO (undo)->item); switch (undo->undo_type) { case GIMP_UNDO_LAYER_MASK_APPLY: val = gimp_layer_mask_get_apply (mask); gimp_layer_mask_set_apply (mask, lmp_undo->old_apply, FALSE); lmp_undo->old_apply = val; break; case GIMP_UNDO_LAYER_MASK_SHOW: val = gimp_layer_mask_get_show (mask); gimp_layer_mask_set_show (mask, lmp_undo->old_show, FALSE); lmp_undo->old_show = val; break; default: g_return_val_if_reached (FALSE); break; } return TRUE; } static void undo_free_layer_mask_properties (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /*****************************/ /* Add/Remove Channel Undo */ /*****************************/ typedef struct _ChannelUndo ChannelUndo; struct _ChannelUndo { gint prev_position; /* former position in list */ GimpChannel *prev_channel; /* previous active channel */ }; static gboolean undo_push_channel (GimpImage *gimage, const gchar *undo_desc, GimpUndoType type, GimpChannel *channel, gint prev_position, GimpChannel *prev_channel); static gboolean undo_pop_channel (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_channel (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_channel_add (GimpImage *gimage, const gchar *undo_desc, GimpChannel *channel, gint prev_position, GimpChannel *prev_channel) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); g_return_val_if_fail (! gimp_item_is_attached (GIMP_ITEM (channel)), FALSE); g_return_val_if_fail (prev_channel == NULL || GIMP_IS_CHANNEL (prev_channel), FALSE); return undo_push_channel (gimage, undo_desc, GIMP_UNDO_CHANNEL_ADD, channel, prev_position, prev_channel); } gboolean gimp_image_undo_push_channel_remove (GimpImage *gimage, const gchar *undo_desc, GimpChannel *channel, gint prev_position, GimpChannel *prev_channel) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)), FALSE); g_return_val_if_fail (prev_channel == NULL || GIMP_IS_CHANNEL (prev_channel), FALSE); return undo_push_channel (gimage, undo_desc, GIMP_UNDO_CHANNEL_REMOVE, channel, prev_position, prev_channel); } static gboolean undo_push_channel (GimpImage *gimage, const gchar *undo_desc, GimpUndoType type, GimpChannel *channel, gint prev_position, GimpChannel *prev_channel) { GimpUndo *new; gint64 size; size = sizeof (ChannelUndo); if (type == GIMP_UNDO_CHANNEL_REMOVE) size += gimp_object_get_memsize (GIMP_OBJECT (channel), NULL); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (ChannelUndo), type, undo_desc, GIMP_DIRTY_IMAGE_STRUCTURE, undo_pop_channel, undo_free_channel, "item", channel, NULL))) { ChannelUndo *cu = new->data; cu->prev_position = prev_position; cu->prev_channel = prev_channel; return TRUE; } return FALSE; } static gboolean undo_pop_channel (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ChannelUndo *cu = undo->data; GimpChannel *channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item); if ((undo_mode == GIMP_UNDO_MODE_UNDO && undo->undo_type == GIMP_UNDO_CHANNEL_ADD) || (undo_mode == GIMP_UNDO_MODE_REDO && undo->undo_type == GIMP_UNDO_CHANNEL_REMOVE)) { /* remove channel */ undo->size += gimp_object_get_memsize (GIMP_OBJECT (channel), NULL); /* record the current position */ cu->prev_position = gimp_image_get_channel_index (undo->gimage, channel); gimp_container_remove (undo->gimage->channels, GIMP_OBJECT (channel)); gimp_item_removed (GIMP_ITEM (channel)); if (channel == gimp_image_get_active_channel (undo->gimage)) { if (cu->prev_channel) gimp_image_set_active_channel (undo->gimage, cu->prev_channel); else gimp_image_unset_active_channel (undo->gimage); } } else { /* restore channel */ undo->size -= gimp_object_get_memsize (GIMP_OBJECT (channel), NULL); /* record the active channel */ cu->prev_channel = gimp_image_get_active_channel (undo->gimage); gimp_container_insert (undo->gimage->channels, GIMP_OBJECT (channel), cu->prev_position); gimp_image_set_active_channel (undo->gimage, channel); GIMP_ITEM (channel)->removed = FALSE; } return TRUE; } static void undo_free_channel (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /******************************/ /* Channel re-position Undo */ /******************************/ typedef struct _ChannelRepositionUndo ChannelRepositionUndo; struct _ChannelRepositionUndo { gint old_position; }; static gboolean undo_pop_channel_reposition (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_channel_reposition (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_channel_reposition (GimpImage *gimage, const gchar *undo_desc, GimpChannel *channel) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (ChannelRepositionUndo), sizeof (ChannelRepositionUndo), GIMP_UNDO_CHANNEL_REPOSITION, undo_desc, GIMP_DIRTY_IMAGE_STRUCTURE, undo_pop_channel_reposition, undo_free_channel_reposition, "item", channel, NULL))) { ChannelRepositionUndo *cru = new->data; cru->old_position = gimp_image_get_channel_index (gimage, channel); return TRUE; } return FALSE; } static gboolean undo_pop_channel_reposition (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ChannelRepositionUndo *cru = undo->data; GimpChannel *channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item); gint pos; pos = gimp_image_get_channel_index (undo->gimage, channel); gimp_image_position_channel (undo->gimage, channel, cru->old_position, FALSE, NULL); cru->old_position = pos; return TRUE; } static void undo_free_channel_reposition (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /************************/ /* Channel color Undo */ /************************/ typedef struct _ChannelColorUndo ChannelColorUndo; struct _ChannelColorUndo { GimpRGB old_color; }; static gboolean undo_pop_channel_color (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_channel_color (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_channel_color (GimpImage *gimage, const gchar *undo_desc, GimpChannel *channel) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_CHANNEL (channel), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (channel)), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (ChannelColorUndo), sizeof (ChannelColorUndo), GIMP_UNDO_CHANNEL_COLOR, undo_desc, GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE, undo_pop_channel_color, undo_free_channel_color, "item", channel, NULL))) { ChannelColorUndo *ccu = new->data; gimp_channel_get_color (channel , &ccu->old_color); return TRUE; } return FALSE; } static gboolean undo_pop_channel_color (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ChannelColorUndo *ccu = undo->data; GimpChannel *channel = GIMP_CHANNEL (GIMP_ITEM_UNDO (undo)->item); GimpRGB color; gimp_channel_get_color (channel, &color); gimp_channel_set_color (channel, &ccu->old_color, FALSE); ccu->old_color = color; return TRUE; } static void undo_free_channel_color (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /*****************************/ /* Add/Remove Vectors Undo */ /*****************************/ typedef struct _VectorsUndo VectorsUndo; struct _VectorsUndo { gint prev_position; /* former position in list */ GimpVectors *prev_vectors; /* previous active vectors */ }; static gboolean undo_push_vectors (GimpImage *gimage, const gchar *undo_desc, GimpUndoType type, GimpVectors *vectors, gint prev_position, GimpVectors *prev_vectors); static gboolean undo_pop_vectors (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_vectors (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_vectors_add (GimpImage *gimage, const gchar *undo_desc, GimpVectors *vectors, gint prev_position, GimpVectors *prev_vectors) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); g_return_val_if_fail (! gimp_item_is_attached (GIMP_ITEM (vectors)), FALSE); g_return_val_if_fail (prev_vectors == NULL || GIMP_IS_VECTORS (prev_vectors), FALSE); return undo_push_vectors (gimage, undo_desc, GIMP_UNDO_VECTORS_ADD, vectors, prev_position, prev_vectors); } gboolean gimp_image_undo_push_vectors_remove (GimpImage *gimage, const gchar *undo_desc, GimpVectors *vectors, gint prev_position, GimpVectors *prev_vectors) { g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (vectors)), FALSE); g_return_val_if_fail (prev_vectors == NULL || GIMP_IS_VECTORS (prev_vectors), FALSE); return undo_push_vectors (gimage, undo_desc, GIMP_UNDO_VECTORS_REMOVE, vectors, prev_position, prev_vectors); } static gboolean undo_push_vectors (GimpImage *gimage, const gchar *undo_desc, GimpUndoType type, GimpVectors *vectors, gint prev_position, GimpVectors *prev_vectors) { GimpUndo *new; gint64 size; size = sizeof (VectorsUndo); if (type == GIMP_UNDO_VECTORS_REMOVE) size += gimp_object_get_memsize (GIMP_OBJECT (vectors), NULL); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (VectorsUndo), type, undo_desc, GIMP_DIRTY_IMAGE_STRUCTURE, undo_pop_vectors, undo_free_vectors, "item", vectors, NULL))) { VectorsUndo *vu = new->data; vu->prev_position = prev_position; vu->prev_vectors = prev_vectors; return TRUE; } return FALSE; } static gboolean undo_pop_vectors (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { VectorsUndo *vu = undo->data; GimpVectors *vectors = GIMP_VECTORS (GIMP_ITEM_UNDO (undo)->item); if ((undo_mode == GIMP_UNDO_MODE_UNDO && undo->undo_type == GIMP_UNDO_VECTORS_ADD) || (undo_mode == GIMP_UNDO_MODE_REDO && undo->undo_type == GIMP_UNDO_VECTORS_REMOVE)) { /* remove vectors */ undo->size += gimp_object_get_memsize (GIMP_OBJECT (vectors), NULL); /* record the current position */ vu->prev_position = gimp_image_get_vectors_index (undo->gimage, vectors); gimp_container_remove (undo->gimage->vectors, GIMP_OBJECT (vectors)); gimp_item_removed (GIMP_ITEM (vectors)); if (vectors == gimp_image_get_active_vectors (undo->gimage)) gimp_image_set_active_vectors (undo->gimage, vu->prev_vectors); } else { /* restore vectors */ undo->size -= gimp_object_get_memsize (GIMP_OBJECT (vectors), NULL); /* record the active vectors */ vu->prev_vectors = gimp_image_get_active_vectors (undo->gimage); gimp_container_insert (undo->gimage->vectors, GIMP_OBJECT (vectors), vu->prev_position); gimp_image_set_active_vectors (undo->gimage, vectors); GIMP_ITEM (vectors)->removed = FALSE; } return TRUE; } static void undo_free_vectors (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /**********************/ /* Vectors Mod Undo */ /**********************/ typedef struct _VectorsModUndo VectorsModUndo; struct _VectorsModUndo { GimpVectors *vectors; }; static gboolean undo_pop_vectors_mod (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_vectors_mod (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_vectors_mod (GimpImage *gimage, const gchar *undo_desc, GimpVectors *vectors) { GimpVectors *copy; GimpUndo *new; gint64 size; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (vectors)), FALSE); copy = GIMP_VECTORS (gimp_item_duplicate (GIMP_ITEM (vectors), G_TYPE_FROM_INSTANCE (vectors), FALSE)); size = (sizeof (VectorsModUndo) + gimp_object_get_memsize (GIMP_OBJECT (copy), NULL)); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, size, sizeof (VectorsModUndo), GIMP_UNDO_VECTORS_MOD, undo_desc, GIMP_DIRTY_ITEM | GIMP_DIRTY_VECTORS, undo_pop_vectors_mod, undo_free_vectors_mod, "item", vectors, NULL))) { VectorsModUndo *vmu = new->data; vmu->vectors = copy; return TRUE; } if (copy) g_object_unref (copy); return FALSE; } static gboolean undo_pop_vectors_mod (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { VectorsModUndo *vmu = undo->data; GimpVectors *vectors = GIMP_VECTORS (GIMP_ITEM_UNDO (undo)->item); GimpVectors *temp; undo->size -= gimp_object_get_memsize (GIMP_OBJECT (vmu->vectors), NULL); temp = vmu->vectors; vmu->vectors = GIMP_VECTORS (gimp_item_duplicate (GIMP_ITEM (vectors), G_TYPE_FROM_INSTANCE (vectors), FALSE)); gimp_vectors_freeze (vectors); gimp_vectors_copy_strokes (temp, vectors); GIMP_ITEM (vectors)->width = GIMP_ITEM (temp)->width; GIMP_ITEM (vectors)->height = GIMP_ITEM (temp)->height; GIMP_ITEM (vectors)->offset_x = GIMP_ITEM (temp)->offset_x; GIMP_ITEM (vectors)->offset_y = GIMP_ITEM (temp)->offset_y; g_object_unref (temp); gimp_vectors_thaw (vectors); undo->size += gimp_object_get_memsize (GIMP_OBJECT (vmu->vectors), NULL); return TRUE; } static void undo_free_vectors_mod (GimpUndo *undo, GimpUndoMode undo_mode) { VectorsModUndo *vmu = undo->data; g_object_unref (vmu->vectors); g_free (vmu); } /******************************/ /* Vectors re-position Undo */ /******************************/ typedef struct _VectorsRepositionUndo VectorsRepositionUndo; struct _VectorsRepositionUndo { gint old_position; }; static gboolean undo_pop_vectors_reposition (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_vectors_reposition (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_vectors_reposition (GimpImage *gimage, const gchar *undo_desc, GimpVectors *vectors) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (vectors)), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, sizeof (VectorsRepositionUndo), sizeof (VectorsRepositionUndo), GIMP_UNDO_VECTORS_REPOSITION, undo_desc, GIMP_DIRTY_IMAGE_STRUCTURE, undo_pop_vectors_reposition, undo_free_vectors_reposition, "item", vectors, NULL))) { VectorsRepositionUndo *vru = new->data; vru->old_position = gimp_image_get_vectors_index (gimage, vectors); return TRUE; } return FALSE; } static gboolean undo_pop_vectors_reposition (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { VectorsRepositionUndo *vru = undo->data; GimpVectors *vectors = GIMP_VECTORS (GIMP_ITEM_UNDO (undo)->item); gint pos; /* what's the vectors's current index? */ pos = gimp_image_get_vectors_index (undo->gimage, vectors); gimp_image_position_vectors (undo->gimage, vectors, vru->old_position, FALSE, NULL); vru->old_position = pos; return TRUE; } static void undo_free_vectors_reposition (GimpUndo *undo, GimpUndoMode undo_mode) { g_free (undo->data); } /**************************************/ /* Floating Selection to Layer Undo */ /**************************************/ typedef struct _FStoLayerUndo FStoLayerUndo; struct _FStoLayerUndo { GimpLayer *floating_layer; /* the floating layer */ GimpDrawable *drawable; /* drawable of floating sel */ }; static gboolean undo_pop_fs_to_layer (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_fs_to_layer (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_fs_to_layer (GimpImage *gimage, const gchar *undo_desc, GimpLayer *floating_layer, GimpDrawable *drawable) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (floating_layer), FALSE); g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (FStoLayerUndo), sizeof (FStoLayerUndo), GIMP_UNDO_FS_TO_LAYER, undo_desc, GIMP_DIRTY_IMAGE_STRUCTURE, undo_pop_fs_to_layer, undo_free_fs_to_layer, NULL))) { FStoLayerUndo *fsu = new->data; fsu->floating_layer = floating_layer; fsu->drawable = drawable; return TRUE; } tile_manager_unref (floating_layer->fs.backing_store); floating_layer->fs.backing_store = NULL; return FALSE; } static gboolean undo_pop_fs_to_layer (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { FStoLayerUndo *fsu = undo->data; switch (undo_mode) { case GIMP_UNDO_MODE_UNDO: /* Update the preview for the floating sel */ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (fsu->floating_layer)); fsu->floating_layer->fs.drawable = fsu->drawable; gimp_image_set_active_layer (undo->gimage, fsu->floating_layer); undo->gimage->floating_sel = fsu->floating_layer; /* store the contents of the drawable */ floating_sel_store (fsu->floating_layer, GIMP_ITEM (fsu->floating_layer)->offset_x, GIMP_ITEM (fsu->floating_layer)->offset_y, GIMP_ITEM (fsu->floating_layer)->width, GIMP_ITEM (fsu->floating_layer)->height); fsu->floating_layer->fs.initial = TRUE; /* clear the selection */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (fsu->floating_layer)); break; case GIMP_UNDO_MODE_REDO: /* restore the contents of the drawable */ floating_sel_restore (fsu->floating_layer, GIMP_ITEM (fsu->floating_layer)->offset_x, GIMP_ITEM (fsu->floating_layer)->offset_y, GIMP_ITEM (fsu->floating_layer)->width, GIMP_ITEM (fsu->floating_layer)->height); /* Update the preview for the underlying drawable */ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (fsu->floating_layer)); /* clear the selection */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (fsu->floating_layer)); /* update the pointers */ fsu->floating_layer->fs.drawable = NULL; undo->gimage->floating_sel = NULL; break; } gimp_object_name_changed (GIMP_OBJECT (fsu->floating_layer)); gimp_drawable_update (GIMP_DRAWABLE (fsu->floating_layer), 0, 0, GIMP_ITEM (fsu->floating_layer)->width, GIMP_ITEM (fsu->floating_layer)->height); gimp_image_floating_selection_changed (undo->gimage); return TRUE; } static void undo_free_fs_to_layer (GimpUndo *undo, GimpUndoMode undo_mode) { FStoLayerUndo *fsu = undo->data; if (undo_mode == GIMP_UNDO_MODE_UNDO) { tile_manager_unref (fsu->floating_layer->fs.backing_store); fsu->floating_layer->fs.backing_store = NULL; } g_free (fsu); } /***********************************/ /* Floating Selection Rigor Undo */ /***********************************/ static gboolean undo_pop_fs_rigor (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); gboolean gimp_image_undo_push_fs_rigor (GimpImage *gimage, const gchar *undo_desc, GimpLayer *floating_layer) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (floating_layer), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, 0, 0, GIMP_UNDO_FS_RIGOR, undo_desc, GIMP_DIRTY_NONE, undo_pop_fs_rigor, NULL, "item", floating_layer, NULL))) { return TRUE; } return FALSE; } static gboolean undo_pop_fs_rigor (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { GimpLayer *floating_layer; floating_layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); if (! gimp_layer_is_floating_sel (floating_layer)) return FALSE; switch (undo_mode) { case GIMP_UNDO_MODE_UNDO: floating_sel_relax (floating_layer, FALSE); break; case GIMP_UNDO_MODE_REDO: floating_sel_rigor (floating_layer, FALSE); break; } return TRUE; } /***********************************/ /* Floating Selection Relax Undo */ /***********************************/ static gboolean undo_pop_fs_relax (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); gboolean gimp_image_undo_push_fs_relax (GimpImage *gimage, const gchar *undo_desc, GimpLayer *floating_layer) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_LAYER (floating_layer), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_ITEM_UNDO, 0, 0, GIMP_UNDO_FS_RELAX, undo_desc, GIMP_DIRTY_NONE, undo_pop_fs_relax, NULL, "item", floating_layer, NULL))) { return TRUE; } return FALSE; } static gboolean undo_pop_fs_relax (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { GimpLayer *floating_layer; floating_layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); if (! gimp_layer_is_floating_sel (floating_layer)) return FALSE; switch (undo_mode) { case GIMP_UNDO_MODE_UNDO: floating_sel_rigor (floating_layer, FALSE); break; case GIMP_UNDO_MODE_REDO: floating_sel_relax (floating_layer, FALSE); break; } return TRUE; } /*******************/ /* Parasite Undo */ /*******************/ typedef struct _ParasiteUndo ParasiteUndo; struct _ParasiteUndo { GimpImage *gimage; GimpItem *item; GimpParasite *parasite; gchar *name; }; static gboolean undo_pop_parasite (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); static void undo_free_parasite (GimpUndo *undo, GimpUndoMode undo_mode); gboolean gimp_image_undo_push_image_parasite (GimpImage *gimage, const gchar *undo_desc, gpointer parasite) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (ParasiteUndo), sizeof (ParasiteUndo), GIMP_UNDO_PARASITE_ATTACH, undo_desc, GIMP_DIRTY_IMAGE_META, undo_pop_parasite, undo_free_parasite, NULL))) { ParasiteUndo *pu = new->data; pu->gimage = gimage; pu->item = NULL; pu->name = g_strdup (gimp_parasite_name (parasite)); pu->parasite = gimp_parasite_copy (gimp_image_parasite_find (gimage, pu->name)); return TRUE; } return FALSE; } gboolean gimp_image_undo_push_image_parasite_remove (GimpImage *gimage, const gchar *undo_desc, const gchar *name) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (ParasiteUndo), sizeof (ParasiteUndo), GIMP_UNDO_PARASITE_REMOVE, undo_desc, GIMP_DIRTY_IMAGE_META, undo_pop_parasite, undo_free_parasite, NULL))) { ParasiteUndo *pu = new->data; pu->gimage = gimage; pu->item = NULL; pu->name = g_strdup (name); pu->parasite = gimp_parasite_copy (gimp_image_parasite_find (gimage, pu->name)); return TRUE; } return FALSE; } gboolean gimp_image_undo_push_item_parasite (GimpImage *gimage, const gchar *undo_desc, GimpItem *item, gpointer parasite) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); g_return_val_if_fail (gimp_item_is_attached (item), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (ParasiteUndo), sizeof (ParasiteUndo), GIMP_UNDO_PARASITE_ATTACH, undo_desc, GIMP_DIRTY_ITEM_META, undo_pop_parasite, undo_free_parasite, NULL))) { ParasiteUndo *pu = new->data; pu->gimage = NULL; pu->item = item; pu->name = g_strdup (gimp_parasite_name (parasite)); pu->parasite = gimp_parasite_copy (gimp_item_parasite_find (item, pu->name)); return TRUE; } return FALSE; } gboolean gimp_image_undo_push_item_parasite_remove (GimpImage *gimage, const gchar *undo_desc, GimpItem *item, const gchar *name) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE); g_return_val_if_fail (gimp_item_is_attached (item), FALSE); if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, sizeof (ParasiteUndo), sizeof (ParasiteUndo), GIMP_UNDO_PARASITE_REMOVE, undo_desc, GIMP_DIRTY_ITEM_META, undo_pop_parasite, undo_free_parasite, NULL))) { ParasiteUndo *pu = new->data; pu->gimage = NULL; pu->item = item; pu->name = g_strdup (name); pu->parasite = gimp_parasite_copy (gimp_item_parasite_find (item, pu->name)); return TRUE; } return FALSE; } static gboolean undo_pop_parasite (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { ParasiteUndo *pu = undo->data; GimpParasite *tmp; tmp = pu->parasite; if (pu->gimage) { pu->parasite = gimp_parasite_copy (gimp_image_parasite_find (undo->gimage, pu->name)); if (tmp) gimp_parasite_list_add (pu->gimage->parasites, tmp); else gimp_parasite_list_remove (pu->gimage->parasites, pu->name); } else if (pu->item) { pu->parasite = gimp_parasite_copy (gimp_item_parasite_find (pu->item, pu->name)); if (tmp) gimp_parasite_list_add (pu->item->parasites, tmp); else gimp_parasite_list_remove (pu->item->parasites, pu->name); } else { pu->parasite = gimp_parasite_copy (gimp_parasite_find (undo->gimage->gimp, pu->name)); if (tmp) gimp_parasite_attach (undo->gimage->gimp, tmp); else gimp_parasite_detach (undo->gimage->gimp, pu->name); } if (tmp) gimp_parasite_free (tmp); return TRUE; } static void undo_free_parasite (GimpUndo *undo, GimpUndoMode undo_mode) { ParasiteUndo *pu = undo->data; if (pu->parasite) gimp_parasite_free (pu->parasite); if (pu->name) g_free (pu->name); g_free (pu); } /******************************************************************************/ /* Something for which programmer is too lazy to write an undo function for */ /******************************************************************************/ static gboolean undo_pop_cantundo (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum); gboolean gimp_image_undo_push_cantundo (GimpImage *gimage, const gchar *undo_desc) { GimpUndo *new; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); /* This is the sole purpose of this type of undo: the ability to * mark an image as having been mutated, without really providing * any adequate undo facility. */ if ((new = gimp_image_undo_push (gimage, GIMP_TYPE_UNDO, 0, 0, GIMP_UNDO_CANT, undo_desc, GIMP_DIRTY_ALL, undo_pop_cantundo, NULL, NULL))) { return TRUE; } return FALSE; } static gboolean undo_pop_cantundo (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { switch (undo_mode) { case GIMP_UNDO_MODE_UNDO: g_message (_("Can't undo %s"), GIMP_OBJECT (undo)->name); break; case GIMP_UNDO_MODE_REDO: break; } return TRUE; }