mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-20 11:37:14 +00:00
311c83b0a4
1999-08-19 Michael Natterer <mitschel@cs.tu-berlin.de> * app/Makefile.am * app/gimpdnd.h: new file containing the dnd data definitions. * app/disp_callbacks.[ch] * app/interface.c: drop layers on the toolbox to create a new image and on the display to copy it to the image's layer stack. * app/layers_dialog.c: drop layer on the "New" button to create an empty layer with the dropped layer's properties, to "Duplicate" to duplicate it and on the trashcan to delete it. Thanks to Andy for the ultra-cool dnd preview pixmap patch. * app/layer.[ch] * app/undo.c: renamed layer_mask() to layer_get_mask(). Prototyped some function headers. * app/disp_callbacks.c: Wheelmouse stuff: Shift+wheel scales the display. * app/airbrush.c * app/eraser.c * app/paint_options.h * app/paintbrush.c * app/pencil.c * app/tool_options.c: moved the "Incremental" toggle to the PaintOptions structure because it is used more often now.
2426 lines
53 KiB
C
2426 lines
53 KiB
C
/* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include "appenv.h"
|
|
#include "by_color_select.h"
|
|
#include "channel.h"
|
|
#include "drawable.h"
|
|
#include "errors.h"
|
|
#include "floating_sel.h"
|
|
#include "gdisplay.h"
|
|
#include "gdisplay_ops.h"
|
|
#include "gimage_mask.h"
|
|
#include "gimprc.h"
|
|
#include "layer.h"
|
|
#include "paint_core.h"
|
|
#include "paint_funcs.h"
|
|
#include "parasitelist.h"
|
|
#include "tools.h"
|
|
#include "transform_core.h"
|
|
#include "undo.h"
|
|
|
|
#include "drawable_pvt.h"
|
|
#include "layer_pvt.h"
|
|
#include "channel_pvt.h"
|
|
#include "tile_manager_pvt.h"
|
|
#include "tile.h" /* ick. */
|
|
|
|
#include "libgimp/parasite.h"
|
|
#include "gimpparasite.h"
|
|
|
|
|
|
|
|
typedef int (* UndoPopFunc) (GImage *, int, int, void *);
|
|
typedef void (* UndoFreeFunc) (int, void *);
|
|
|
|
typedef struct _undo Undo;
|
|
|
|
struct _undo
|
|
{
|
|
int type; /* undo type */
|
|
void * data; /* data to implement the undo */
|
|
long bytes; /* size of undo item */
|
|
|
|
UndoPopFunc pop_func; /* function pointer to undo pop proc */
|
|
UndoFreeFunc free_func; /* function with specifics for freeing */
|
|
};
|
|
|
|
/* Pop functions */
|
|
|
|
int undo_pop_image (GImage *, int, int, void *);
|
|
int undo_pop_mask (GImage *, int, int, void *);
|
|
int undo_pop_layer_displace (GImage *, int, int, void *);
|
|
int undo_pop_transform (GImage *, int, int, void *);
|
|
int undo_pop_paint (GImage *, int, int, void *);
|
|
int undo_pop_layer (GImage *, int, int, void *);
|
|
int undo_pop_layer_mod (GImage *, int, int, void *);
|
|
int undo_pop_layer_mask (GImage *, int, int, void *);
|
|
int undo_pop_channel (GImage *, int, int, void *);
|
|
int undo_pop_channel_mod (GImage *, int, int, void *);
|
|
int undo_pop_fs_to_layer (GImage *, int, int, void *);
|
|
int undo_pop_fs_rigor (GImage *, int, int, void *);
|
|
int undo_pop_fs_relax (GImage *, int, int, void *);
|
|
int undo_pop_gimage_mod (GImage *, int, int, void *);
|
|
int undo_pop_guide (GImage *, int, int, void *);
|
|
int undo_pop_parasite (GImage *, int, int, void *);
|
|
int undo_pop_qmask (GImage *, int, int, void *);
|
|
|
|
/* Free functions */
|
|
|
|
void undo_free_image (int, void *);
|
|
void undo_free_mask (int, void *);
|
|
void undo_free_layer_displace (int, void *);
|
|
void undo_free_transform (int, void *);
|
|
void undo_free_paint (int, void *);
|
|
void undo_free_layer (int, void *);
|
|
void undo_free_layer_mod (int, void *);
|
|
void undo_free_layer_mask (int, void *);
|
|
void undo_free_channel (int, void *);
|
|
void undo_free_channel_mod (int, void *);
|
|
void undo_free_fs_to_layer (int, void *);
|
|
void undo_free_fs_rigor (int, void *);
|
|
void undo_free_fs_relax (int, void *);
|
|
void undo_free_gimage_mod (int, void *);
|
|
void undo_free_guide (int, void *);
|
|
void undo_free_parasite (int, void *);
|
|
void undo_free_qmask (int, void *);
|
|
|
|
|
|
/* Sizing functions */
|
|
static int layer_size (Layer *);
|
|
static int channel_size (Channel *);
|
|
|
|
static int group_count = 0;
|
|
static int shrink_wrap = FALSE;
|
|
|
|
#define UNDO 0
|
|
#define REDO 1
|
|
|
|
static int
|
|
layer_size (Layer *layer)
|
|
{
|
|
int size;
|
|
|
|
size = sizeof (Layer) +
|
|
GIMP_DRAWABLE(layer)->width * GIMP_DRAWABLE(layer)->height * GIMP_DRAWABLE(layer)->bytes +
|
|
strlen (GIMP_DRAWABLE(layer)->name);
|
|
|
|
if (layer_get_mask (layer))
|
|
size += channel_size (GIMP_CHANNEL (layer_get_mask (layer)));
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
static int
|
|
channel_size (Channel *channel)
|
|
{
|
|
int size;
|
|
|
|
size = sizeof (Channel) + GIMP_DRAWABLE(channel)->width * GIMP_DRAWABLE(channel)->height +
|
|
strlen (GIMP_DRAWABLE(channel)->name);
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_list (GImage *gimage,
|
|
int state,
|
|
GSList *list)
|
|
{
|
|
GSList * orig;
|
|
Undo * undo;
|
|
|
|
orig = list;
|
|
|
|
while (list)
|
|
{
|
|
undo = (Undo *) list->data;
|
|
if (undo)
|
|
{
|
|
(* undo->free_func) (state, undo->data);
|
|
gimage->undo_bytes -= undo->bytes;
|
|
g_free (undo);
|
|
}
|
|
list = g_slist_next (list);
|
|
}
|
|
|
|
g_slist_free (orig);
|
|
}
|
|
|
|
|
|
static GSList *
|
|
remove_stack_bottom (GImage *gimage)
|
|
{
|
|
GSList *list;
|
|
GSList *last;
|
|
int in_group = 0;
|
|
|
|
list = gimage->undo_stack;
|
|
|
|
last = NULL;
|
|
while (list)
|
|
{
|
|
if (list->next == NULL)
|
|
{
|
|
if (last)
|
|
undo_free_list (gimage, UNDO, last->next);
|
|
else
|
|
undo_free_list (gimage, UNDO, list);
|
|
|
|
gimage->undo_levels --;
|
|
if (last)
|
|
last->next = NULL;
|
|
list = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (list->data == NULL)
|
|
in_group = (in_group) ? 0 : 1;
|
|
|
|
/* Make sure last points to only a single item, or the
|
|
* tail of an aggregate undo item
|
|
*/
|
|
if ((list->data && !in_group) ||
|
|
(!list->data && !in_group))
|
|
last = list;
|
|
|
|
list = g_slist_next (list);
|
|
}
|
|
}
|
|
|
|
if (last)
|
|
return gimage->undo_stack;
|
|
else
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
static int
|
|
undo_free_up_space (GImage *gimage)
|
|
{
|
|
/* If there are 0 levels of undo return FALSE.
|
|
*/
|
|
if (levels_of_undo == 0)
|
|
return FALSE;
|
|
|
|
/* Delete the item on the bottom of the stack if we have the maximum
|
|
* levels of undo already
|
|
*/
|
|
while (gimage->undo_levels >= levels_of_undo)
|
|
gimage->undo_stack = remove_stack_bottom (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static Undo *
|
|
undo_push (GImage *gimage,
|
|
long size,
|
|
int type)
|
|
{
|
|
Undo * new;
|
|
|
|
if (! gimage->undo_on)
|
|
return NULL;
|
|
|
|
size += sizeof (Undo);
|
|
|
|
if (gimage->redo_stack)
|
|
{
|
|
undo_free_list (gimage, REDO, gimage->redo_stack);
|
|
gimage->redo_stack = NULL;
|
|
}
|
|
|
|
if (!gimage->pushing_undo_group)
|
|
if (! undo_free_up_space (gimage))
|
|
return NULL;
|
|
|
|
new = (Undo *) g_malloc (sizeof (Undo));
|
|
|
|
new->bytes = size;
|
|
gimage->undo_bytes += size;
|
|
if (!gimage->pushing_undo_group) /* only increment levels if not in a group */
|
|
{
|
|
new->type = type;
|
|
gimage->undo_levels ++;
|
|
}
|
|
else
|
|
new->type = gimage->pushing_undo_group;
|
|
|
|
gimage->undo_stack = g_slist_prepend (gimage->undo_stack, (void *) new);
|
|
|
|
return new;
|
|
}
|
|
|
|
|
|
static int
|
|
pop_stack (GImage *gimage,
|
|
GSList **stack_ptr,
|
|
GSList **unstack_ptr,
|
|
int state)
|
|
{
|
|
Undo * object;
|
|
GSList *stack;
|
|
GSList *tmp;
|
|
int status = 0;
|
|
int in_group = 0;
|
|
int x, y;
|
|
GDisplay *gdisp;
|
|
|
|
/* Keep popping until we pop a valid object
|
|
* or get to the end of a group if we're in one
|
|
*/
|
|
while (*stack_ptr)
|
|
{
|
|
stack = *stack_ptr;
|
|
|
|
object = (Undo *) stack->data;
|
|
if (object == NULL)
|
|
{
|
|
in_group = (in_group) ? 0 : 1;
|
|
if (in_group)
|
|
gimage->undo_levels += (state == UNDO) ? -1 : 1;
|
|
|
|
if (status && !in_group)
|
|
status = 1;
|
|
else
|
|
status = 0;
|
|
}
|
|
else
|
|
{
|
|
status = (* object->pop_func) (gimage, state, object->type, object->data);
|
|
if (!in_group)
|
|
gimage->undo_levels += (state == UNDO) ? -1 : 1;
|
|
}
|
|
|
|
*unstack_ptr = g_slist_prepend (*unstack_ptr, (void *) object);
|
|
|
|
tmp = stack;
|
|
*stack_ptr = g_slist_next (*stack_ptr);
|
|
tmp->next = NULL;
|
|
g_slist_free (tmp);
|
|
|
|
if (status && !in_group)
|
|
{
|
|
/* Flush any image updates and displays */
|
|
gdisp = gdisplay_active();
|
|
if (gdisp != NULL) {
|
|
if (gdisp->disp_xoffset || gdisp->disp_yoffset)
|
|
{
|
|
gdk_window_get_size (gdisp->canvas->window, &x, &y);
|
|
if (gdisp->disp_yoffset)
|
|
{
|
|
gdisplay_expose_area (gdisp, 0, 0, gdisp->disp_width,
|
|
gdisp->disp_yoffset);
|
|
gdisplay_expose_area (gdisp, 0, gdisp->disp_yoffset + y,
|
|
gdisp->disp_width, gdisp->disp_height);
|
|
}
|
|
if (gdisp->disp_xoffset)
|
|
{
|
|
gdisplay_expose_area (gdisp, 0, 0, gdisp->disp_xoffset,
|
|
gdisp->disp_height);
|
|
gdisplay_expose_area (gdisp, gdisp->disp_xoffset + x, 0,
|
|
gdisp->disp_width, gdisp->disp_height);
|
|
}
|
|
}
|
|
}
|
|
gdisplays_flush ();
|
|
|
|
/* If the shrink_wrap flag was set */
|
|
if (shrink_wrap)
|
|
{
|
|
gdisplays_shrink_wrap (gimage);
|
|
shrink_wrap = FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop (GImage *gimage)
|
|
{
|
|
return pop_stack (gimage, &gimage->undo_stack, &gimage->redo_stack, UNDO);
|
|
}
|
|
|
|
|
|
int
|
|
undo_redo (GImage *gimage)
|
|
{
|
|
return pop_stack (gimage, &gimage->redo_stack, &gimage->undo_stack, REDO);
|
|
}
|
|
|
|
|
|
void
|
|
undo_free (GImage *gimage)
|
|
{
|
|
undo_free_list (gimage, UNDO, gimage->undo_stack);
|
|
undo_free_list (gimage, REDO, gimage->redo_stack);
|
|
gimage->undo_stack = NULL;
|
|
gimage->redo_stack = NULL;
|
|
gimage->undo_bytes = 0;
|
|
gimage->undo_levels = 0;
|
|
}
|
|
|
|
|
|
/**********************/
|
|
/* group Undo funcs */
|
|
|
|
int
|
|
undo_push_group_start (GImage *gimage,
|
|
int type)
|
|
{
|
|
if (! gimage->undo_on)
|
|
return FALSE;
|
|
|
|
group_count ++;
|
|
|
|
/* If we're already in a group...ignore */
|
|
if (group_count > 1)
|
|
return TRUE;
|
|
|
|
if (gimage->redo_stack)
|
|
{
|
|
undo_free_list (gimage, REDO, gimage->redo_stack);
|
|
gimage->redo_stack = NULL;
|
|
}
|
|
|
|
if (! undo_free_up_space (gimage))
|
|
return FALSE;
|
|
|
|
gimage->pushing_undo_group = type;
|
|
gimage->undo_stack = g_slist_prepend (gimage->undo_stack, NULL);
|
|
gimage->undo_levels++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_push_group_end (GImage *gimage)
|
|
{
|
|
if (! gimage->undo_on)
|
|
return FALSE;
|
|
|
|
group_count --;
|
|
|
|
if (group_count == 0)
|
|
{
|
|
gimage->pushing_undo_group = 0;
|
|
gimage->undo_stack = g_slist_prepend (gimage->undo_stack, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Image Undo functions */
|
|
|
|
typedef struct _image_undo ImageUndo;
|
|
|
|
struct _image_undo
|
|
{
|
|
TileManager *tiles;
|
|
GimpDrawable *drawable;
|
|
int x1, y1, x2, y2;
|
|
int sparse;
|
|
};
|
|
|
|
|
|
int
|
|
undo_push_image (GImage *gimage,
|
|
GimpDrawable *drawable,
|
|
int x1,
|
|
int y1,
|
|
int x2,
|
|
int y2)
|
|
{
|
|
long size;
|
|
Undo *new;
|
|
ImageUndo *image_undo;
|
|
TileManager *tiles;
|
|
PixelRegion srcPR, destPR;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (drawable);
|
|
|
|
x1 = BOUNDS (x1, 0, drawable_width (drawable));
|
|
y1 = BOUNDS (y1, 0, drawable_height (drawable));
|
|
x2 = BOUNDS (x2, 0, drawable_width (drawable));
|
|
y2 = BOUNDS (y2, 0, drawable_height (drawable));
|
|
|
|
size = (x2 - x1) * (y2 - y1) * drawable_bytes (drawable) + sizeof (void *) * 2;
|
|
|
|
if ((new = undo_push (gimage, size, IMAGE_UNDO)))
|
|
{
|
|
image_undo = (ImageUndo *) g_malloc (sizeof (ImageUndo));
|
|
|
|
/* If we cannot create a new temp buf--either because our parameters are
|
|
* degenerate or something else failed, simply return an unsuccessful push.
|
|
*/
|
|
tiles = tile_manager_new ((x2 - x1), (y2 - y1), drawable_bytes (drawable));
|
|
pixel_region_init (&srcPR, drawable_data (drawable), x1, y1, (x2 - x1), (y2 - y1), FALSE);
|
|
pixel_region_init (&destPR, tiles, 0, 0, (x2 - x1), (y2 - y1), TRUE);
|
|
copy_region (&srcPR, &destPR);
|
|
|
|
/* set the image undo structure */
|
|
image_undo->tiles = tiles;
|
|
image_undo->drawable = drawable;
|
|
image_undo->x1 = x1;
|
|
image_undo->y1 = y1;
|
|
image_undo->x2 = x2;
|
|
image_undo->y2 = y2;
|
|
image_undo->sparse = FALSE;
|
|
|
|
new->data = image_undo;
|
|
new->pop_func = undo_pop_image;
|
|
new->free_func = undo_free_image;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_push_image_mod (GImage *gimage,
|
|
GimpDrawable *drawable,
|
|
int x1,
|
|
int y1,
|
|
int x2,
|
|
int y2,
|
|
void *tiles_ptr,
|
|
int sparse)
|
|
{
|
|
long size;
|
|
int dwidth, dheight;
|
|
Undo * new;
|
|
ImageUndo *image_undo;
|
|
TileManager *tiles;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (drawable);
|
|
|
|
if (! tiles_ptr)
|
|
return FALSE;
|
|
|
|
dwidth = drawable_width (drawable);
|
|
dheight = drawable_height (drawable);
|
|
|
|
x1 = BOUNDS (x1, 0, dwidth);
|
|
y1 = BOUNDS (y1, 0, dheight);
|
|
x2 = BOUNDS (x2, 0, dwidth);
|
|
y2 = BOUNDS (y2, 0, dheight);
|
|
|
|
tiles = (TileManager *) tiles_ptr;
|
|
size = tiles->width * tiles->height *
|
|
tiles->bpp + sizeof (void *) * 2;
|
|
|
|
if ((new = undo_push (gimage, size, IMAGE_MOD_UNDO)))
|
|
{
|
|
image_undo = (ImageUndo *) g_malloc (sizeof (ImageUndo));
|
|
image_undo->tiles = tiles;
|
|
image_undo->drawable = drawable;
|
|
image_undo->x1 = x1;
|
|
image_undo->y1 = y1;
|
|
image_undo->x2 = x2;
|
|
image_undo->y2 = y2;
|
|
image_undo->sparse = sparse;
|
|
|
|
new->data = image_undo;
|
|
new->pop_func = undo_pop_image;
|
|
new->free_func = undo_free_image;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
tile_manager_destroy (tiles);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_image (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *image_undo_ptr)
|
|
{
|
|
ImageUndo *image_undo;
|
|
TileManager *tiles;
|
|
PixelRegion PR1, PR2;
|
|
int x, y;
|
|
int w, h;
|
|
|
|
image_undo = (ImageUndo *) image_undo_ptr;
|
|
tiles = image_undo->tiles;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
drawable_clean (image_undo->drawable);
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (image_undo->drawable);
|
|
break;
|
|
}
|
|
|
|
/* some useful values */
|
|
x = image_undo->x1;
|
|
y = image_undo->y1;
|
|
|
|
if (image_undo->sparse == FALSE)
|
|
{
|
|
w = tiles->width;
|
|
h = tiles->height;
|
|
|
|
pixel_region_init (&PR1, tiles, 0, 0, w, h, TRUE);
|
|
pixel_region_init (&PR2, drawable_data (image_undo->drawable), x, y, w, h, TRUE);
|
|
|
|
/* swap the regions */
|
|
swap_region (&PR1, &PR2);
|
|
}
|
|
else
|
|
{
|
|
int i, j;
|
|
Tile *src_tile;
|
|
Tile *dest_tile;
|
|
|
|
w = image_undo->x2 - image_undo->x1;
|
|
h = image_undo->y2 - image_undo->y1;
|
|
|
|
for (i = y; i < image_undo->y2; i += (TILE_HEIGHT - (i % TILE_HEIGHT)))
|
|
{
|
|
for (j = x; j < image_undo->x2; j += (TILE_WIDTH - (j % TILE_WIDTH)))
|
|
{
|
|
src_tile = tile_manager_get_tile (tiles, j, i, FALSE, FALSE);
|
|
if (tile_is_valid (src_tile) == TRUE)
|
|
{
|
|
/* swap tiles, not pixels! */
|
|
|
|
src_tile = tile_manager_get_tile (tiles, j, i, TRUE, FALSE /* TRUE */);
|
|
dest_tile = tile_manager_get_tile (drawable_data (image_undo->drawable), j, i, TRUE, FALSE /* TRUE */);
|
|
|
|
tile_manager_map_tile (tiles, j, i, dest_tile);
|
|
tile_manager_map_tile (drawable_data (image_undo->drawable), j, i, src_tile);
|
|
#if 0
|
|
swap_pixels (tile_data_pointer (src_tile, 0, 0),
|
|
tile_data_pointer (dest_tile, 0, 0),
|
|
tile_size (src_tile));
|
|
#endif
|
|
|
|
tile_release (dest_tile, FALSE /* TRUE */);
|
|
tile_release (src_tile, FALSE /* TRUE */);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
drawable_update (image_undo->drawable, x, y, w, h);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_image (int state,
|
|
void *image_undo_ptr)
|
|
{
|
|
ImageUndo *image_undo;
|
|
|
|
image_undo = (ImageUndo *) image_undo_ptr;
|
|
|
|
tile_manager_destroy (image_undo->tiles);
|
|
g_free (image_undo);
|
|
}
|
|
|
|
|
|
/******************************/
|
|
/* Mask Undo functions */
|
|
|
|
int
|
|
undo_push_mask (GImage *gimage,
|
|
void *mask_ptr)
|
|
{
|
|
Undo *new;
|
|
MaskUndo *mask_undo;
|
|
int size;
|
|
|
|
mask_undo = (MaskUndo *) mask_ptr;
|
|
if (mask_undo->tiles)
|
|
size = mask_undo->tiles->width * mask_undo->tiles->height;
|
|
else
|
|
size = 0;
|
|
|
|
if ((new = undo_push (gimage, size, MASK_UNDO)))
|
|
{
|
|
new->data = mask_undo;
|
|
new->pop_func = undo_pop_mask;
|
|
new->free_func = undo_free_mask;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (mask_undo->tiles)
|
|
tile_manager_destroy (mask_undo->tiles);
|
|
g_free (mask_undo);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_mask (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *mask_ptr)
|
|
{
|
|
MaskUndo *mask_undo;
|
|
TileManager *new_tiles;
|
|
Channel *sel_mask;
|
|
PixelRegion srcPR, destPR;
|
|
int selection;
|
|
int x1, y1, x2, y2;
|
|
int width, height;
|
|
unsigned char empty = 0;
|
|
|
|
width = height = 0;
|
|
|
|
mask_undo = (MaskUndo *) mask_ptr;
|
|
|
|
/* save current selection mask */
|
|
sel_mask = gimage_get_mask (gimage);
|
|
selection = channel_bounds (sel_mask, &x1, &y1, &x2, &y2);
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(sel_mask)->tiles, x1, y1, (x2 - x1), (y2 - y1), FALSE);
|
|
|
|
if (selection)
|
|
{
|
|
new_tiles = tile_manager_new (srcPR.w, srcPR.h, 1);
|
|
pixel_region_init (&destPR, new_tiles, 0, 0, srcPR.w, srcPR.h, TRUE);
|
|
|
|
copy_region (&srcPR, &destPR);
|
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(sel_mask)->tiles, x1, y1, (x2 - x1), (y2 - y1), TRUE);
|
|
color_region (&srcPR, &empty);
|
|
}
|
|
else
|
|
new_tiles = NULL;
|
|
|
|
if (mask_undo->tiles)
|
|
{
|
|
width = mask_undo->tiles->width;
|
|
height = mask_undo->tiles->height;
|
|
pixel_region_init (&srcPR, mask_undo->tiles, 0, 0, width, height, FALSE);
|
|
pixel_region_init (&destPR, GIMP_DRAWABLE(sel_mask)->tiles, mask_undo->x, mask_undo->y, width, height, TRUE);
|
|
copy_region (&srcPR, &destPR);
|
|
|
|
tile_manager_destroy (mask_undo->tiles);
|
|
}
|
|
|
|
/* invalidate the current bounds and boundary of the mask */
|
|
gimage_mask_invalidate (gimage);
|
|
|
|
if (mask_undo->tiles)
|
|
{
|
|
sel_mask->empty = FALSE;
|
|
sel_mask->x1 = mask_undo->x;
|
|
sel_mask->y1 = mask_undo->y;
|
|
sel_mask->x2 = mask_undo->x + width;
|
|
sel_mask->y2 = mask_undo->y + height;
|
|
}
|
|
else
|
|
{
|
|
sel_mask->empty = TRUE;
|
|
sel_mask->x1 = 0;
|
|
sel_mask->y1 = 0;
|
|
sel_mask->x2 = GIMP_DRAWABLE(sel_mask)->width;
|
|
sel_mask->y2 = GIMP_DRAWABLE(sel_mask)->height;
|
|
}
|
|
|
|
/* set the new mask undo parameters */
|
|
mask_undo->tiles = new_tiles;
|
|
mask_undo->x = x1;
|
|
mask_undo->y = y1;
|
|
|
|
/* we know the bounds */
|
|
sel_mask->bounds_known = TRUE;
|
|
|
|
/* if there is a "by color" selection dialog active
|
|
* for this gimage's mask, send it an update notice
|
|
*/
|
|
if (gimage->by_color_select)
|
|
by_color_select_initialize_by_image (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_mask (int state,
|
|
void *mask_ptr)
|
|
{
|
|
MaskUndo *mask_undo;
|
|
|
|
mask_undo = (MaskUndo *) mask_ptr;
|
|
if (mask_undo->tiles)
|
|
tile_manager_destroy (mask_undo->tiles);
|
|
g_free (mask_undo);
|
|
}
|
|
|
|
|
|
/***************************************/
|
|
/* Layer displacement Undo functions */
|
|
|
|
int
|
|
undo_push_layer_displace (GImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
Undo * new;
|
|
int * info;
|
|
|
|
if ((new = undo_push (gimage, 12, LAYER_DISPLACE_UNDO)))
|
|
{
|
|
new->data = (void *) g_malloc (sizeof (int) * 3);
|
|
new->pop_func = undo_pop_layer_displace;
|
|
new->free_func = undo_free_layer_displace;
|
|
|
|
info = (int *) new->data;
|
|
info[0] = drawable_ID(GIMP_DRAWABLE(layer));
|
|
info[1] = GIMP_DRAWABLE(layer)->offset_x;
|
|
info[2] = GIMP_DRAWABLE(layer)->offset_y;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_layer_displace (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *info_ptr)
|
|
{
|
|
Layer * layer;
|
|
int old_offsets[2];
|
|
int *info;
|
|
|
|
info = (int *) info_ptr;
|
|
layer = layer_get_ID (info[0]);
|
|
if (layer)
|
|
{
|
|
old_offsets[0] = GIMP_DRAWABLE(layer)->offset_x;
|
|
old_offsets[1] = GIMP_DRAWABLE(layer)->offset_y;
|
|
drawable_update (GIMP_DRAWABLE(layer), 0, 0,
|
|
GIMP_DRAWABLE(layer)->width,
|
|
GIMP_DRAWABLE(layer)->height);
|
|
|
|
GIMP_DRAWABLE(layer)->offset_x = info[1];
|
|
GIMP_DRAWABLE(layer)->offset_y = info[2];
|
|
drawable_update (GIMP_DRAWABLE(layer), 0, 0,
|
|
GIMP_DRAWABLE(layer)->width,
|
|
GIMP_DRAWABLE(layer)->height);
|
|
|
|
if (layer->mask)
|
|
{
|
|
GIMP_DRAWABLE(layer->mask)->offset_x = info[1];
|
|
GIMP_DRAWABLE(layer->mask)->offset_y = info[2];
|
|
drawable_update (GIMP_DRAWABLE(layer->mask), 0, 0,
|
|
GIMP_DRAWABLE(layer->mask)->width,
|
|
GIMP_DRAWABLE(layer->mask)->height);
|
|
}
|
|
|
|
|
|
/* invalidate the selection boundary because of a layer modification */
|
|
layer_invalidate_boundary (layer);
|
|
|
|
info[1] = old_offsets[0];
|
|
info[2] = old_offsets[1];
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_layer_displace (int state,
|
|
void *info_ptr)
|
|
{
|
|
g_free (info_ptr);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Transform Undo */
|
|
|
|
int
|
|
undo_push_transform (GImage *gimage,
|
|
void *tu_ptr)
|
|
{
|
|
Undo * new;
|
|
int size;
|
|
|
|
size = sizeof (TransformUndo);
|
|
|
|
if ((new = undo_push (gimage, size, TRANSFORM_UNDO)))
|
|
{
|
|
new->data = tu_ptr;
|
|
new->pop_func = undo_pop_transform;
|
|
new->free_func = undo_free_transform;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_free (tu_ptr);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_transform (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *tu_ptr)
|
|
{
|
|
TransformCore * tc;
|
|
TransformUndo * tu;
|
|
TileManager * temp;
|
|
double d;
|
|
int i;
|
|
|
|
/* Can't have ANY tool selected - maybe a plugin running */
|
|
if (active_tool == NULL)
|
|
return TRUE;
|
|
|
|
tc = (TransformCore *) active_tool->private;
|
|
tu = (TransformUndo *) tu_ptr;
|
|
|
|
paths_transform_do_undo(gimage,tu->path_undo);
|
|
|
|
/* only pop if the active tool is the tool that pushed this undo */
|
|
if (tu->tool_ID != active_tool->ID)
|
|
return TRUE;
|
|
|
|
/* swap the transformation information arrays */
|
|
for (i = 0; i < TRAN_INFO_SIZE; i++)
|
|
{
|
|
d = tu->trans_info[i];
|
|
tu->trans_info[i] = tc->trans_info[i];
|
|
tc->trans_info[i] = d;
|
|
}
|
|
|
|
/* swap the original buffer--the source buffer for repeated transforms
|
|
*/
|
|
temp = tu->original;
|
|
tu->original = tc->original;
|
|
tc->original = temp;
|
|
|
|
/* If we're re-implementing the first transform, reactivate tool */
|
|
if (state == REDO && tc->original)
|
|
{
|
|
active_tool->state = ACTIVE;
|
|
draw_core_resume (tc->core, active_tool);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_transform (int state,
|
|
void *tu_ptr)
|
|
{
|
|
TransformUndo * tu;
|
|
|
|
tu = (TransformUndo *) tu_ptr;
|
|
if (tu->original)
|
|
tile_manager_destroy (tu->original);
|
|
paths_transform_free_undo(tu->path_undo);
|
|
g_free (tu);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Paint Undo */
|
|
|
|
int
|
|
undo_push_paint (GImage *gimage,
|
|
void *pu_ptr)
|
|
{
|
|
Undo * new;
|
|
int size;
|
|
|
|
size = sizeof (PaintUndo);
|
|
|
|
if ((new = undo_push (gimage, size, PAINT_UNDO)))
|
|
{
|
|
new->data = pu_ptr;
|
|
new->pop_func = undo_pop_paint;
|
|
new->free_func = undo_free_paint;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_free (pu_ptr);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_paint (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *pu_ptr)
|
|
{
|
|
PaintCore * pc;
|
|
PaintUndo * pu;
|
|
double tmp;
|
|
|
|
/* Can't have ANY tool selected - maybe a plugin running */
|
|
if (active_tool == NULL)
|
|
return TRUE;
|
|
|
|
pc = (PaintCore *) active_tool->private;
|
|
pu = (PaintUndo *) pu_ptr;
|
|
|
|
/* only pop if the active tool is the tool that pushed this undo */
|
|
if (pu->tool_ID != active_tool->ID)
|
|
return TRUE;
|
|
|
|
/* swap the paint core information */
|
|
tmp = pc->lastx;
|
|
pc->lastx = pu->lastx;
|
|
pu->lastx = tmp;
|
|
|
|
tmp = pc->lasty;
|
|
pc->lasty = pu->lasty;
|
|
pu->lasty = tmp;
|
|
|
|
tmp = pc->lastpressure;
|
|
pc->lastpressure = pu->lastpressure;
|
|
pu->lastpressure = tmp;
|
|
|
|
tmp = pc->lastxtilt;
|
|
pc->lastxtilt = pu->lastxtilt;
|
|
pu->lastxtilt = tmp;
|
|
|
|
tmp = pc->lastytilt;
|
|
pc->lastytilt = pu->lastytilt;
|
|
pu->lastytilt = tmp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_paint (int state,
|
|
void *pu_ptr)
|
|
{
|
|
PaintUndo * pu;
|
|
|
|
pu = (PaintUndo *) pu_ptr;
|
|
g_free (pu);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* New Layer Undo */
|
|
|
|
int
|
|
undo_push_layer (GImage *gimage,
|
|
void *lu_ptr)
|
|
{
|
|
LayerUndo *lu;
|
|
Undo *new;
|
|
int size;
|
|
|
|
lu = (LayerUndo *) lu_ptr;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(lu->layer));
|
|
|
|
size = layer_size (lu->layer) + sizeof (LayerUndo);
|
|
|
|
if ((new = undo_push (gimage, size, LAYER_UNDO)))
|
|
{
|
|
new->data = lu_ptr;
|
|
new->pop_func = undo_pop_layer;
|
|
new->free_func = undo_free_layer;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* if this is a remove layer, delete the layer */
|
|
if (lu->undo_type == 1)
|
|
layer_unref (lu->layer);
|
|
g_free (lu);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_layer (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *lu_ptr)
|
|
{
|
|
LayerUndo *lu;
|
|
|
|
lu = (LayerUndo *) lu_ptr;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
drawable_clean (GIMP_DRAWABLE(lu->layer));
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(lu->layer));
|
|
break;
|
|
}
|
|
|
|
/* remove layer */
|
|
if ((state == UNDO && lu->undo_type == 0) ||
|
|
(state == REDO && lu->undo_type == 1))
|
|
{
|
|
/* record the current position */
|
|
lu->prev_position = gimage_get_layer_index (gimage, lu->layer);
|
|
/* set the previous layer */
|
|
gimage_set_active_layer (gimage, lu->prev_layer);
|
|
|
|
/* remove the layer */
|
|
gimage->layers = g_slist_remove (gimage->layers, lu->layer);
|
|
gimage->layer_stack = g_slist_remove (gimage->layer_stack, lu->layer);
|
|
|
|
/* reset the gimage values */
|
|
if (layer_is_floating_sel (lu->layer))
|
|
{
|
|
gimage->floating_sel = NULL;
|
|
/* reset the old drawable */
|
|
floating_sel_reset (lu->layer);
|
|
}
|
|
|
|
drawable_update (GIMP_DRAWABLE(lu->layer), 0, 0,
|
|
GIMP_DRAWABLE(lu->layer)->width,
|
|
GIMP_DRAWABLE(lu->layer)->height);
|
|
}
|
|
/* restore layer */
|
|
else
|
|
{
|
|
/* record the active layer */
|
|
lu->prev_layer = gimage->active_layer;
|
|
|
|
/* hide the current selection--for all views */
|
|
if (gimage->active_layer != NULL)
|
|
layer_invalidate_boundary ((gimage->active_layer));
|
|
|
|
/* if this is a floating selection, set the fs pointer */
|
|
if (layer_is_floating_sel (lu->layer))
|
|
gimage->floating_sel = lu->layer;
|
|
|
|
/* add the new layer */
|
|
gimage->layers = g_slist_insert (gimage->layers, lu->layer,
|
|
lu->prev_position);
|
|
gimage->layer_stack = g_slist_prepend (gimage->layer_stack, lu->layer);
|
|
gimage->active_layer = lu->layer;
|
|
|
|
drawable_update (GIMP_DRAWABLE(lu->layer), 0, 0,
|
|
GIMP_DRAWABLE(lu->layer)->width,
|
|
GIMP_DRAWABLE(lu->layer)->height);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_layer (int state,
|
|
void *lu_ptr)
|
|
{
|
|
LayerUndo *lu;
|
|
|
|
lu = (LayerUndo *) lu_ptr;
|
|
|
|
/* Only free the layer if we're freeing from the redo
|
|
* stack and it's a layer add, or if we're freeing from
|
|
* the undo stack and it's a layer remove
|
|
*/
|
|
if ((state == REDO && lu->undo_type == 0) ||
|
|
(state == UNDO && lu->undo_type == 1))
|
|
layer_unref (lu->layer);
|
|
|
|
g_free (lu);
|
|
}
|
|
|
|
|
|
|
|
/*********************************/
|
|
/* Layer Mod Undo */
|
|
|
|
int
|
|
undo_push_layer_mod (GImage *gimage,
|
|
void *layer_ptr)
|
|
{
|
|
Layer *layer;
|
|
Undo *new;
|
|
TileManager *tiles;
|
|
void **data;
|
|
int size;
|
|
|
|
layer = (Layer *) layer_ptr;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(layer));
|
|
|
|
tiles = GIMP_DRAWABLE(layer)->tiles;
|
|
tiles->x = GIMP_DRAWABLE(layer)->offset_x;
|
|
tiles->y = GIMP_DRAWABLE(layer)->offset_y;
|
|
size = GIMP_DRAWABLE(layer)->width * GIMP_DRAWABLE(layer)->height *
|
|
GIMP_DRAWABLE(layer)->bytes + sizeof (void *) * 3;
|
|
|
|
if ((new = undo_push (gimage, size, LAYER_MOD)))
|
|
{
|
|
data = (void **) g_malloc (sizeof (void *) * 3);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_layer_mod;
|
|
new->free_func = undo_free_layer_mod;
|
|
|
|
data[0] = layer_ptr;
|
|
data[1] = (void *) tiles;
|
|
data[2] = (void *) ((long) GIMP_DRAWABLE(layer)->type);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
tile_manager_destroy (tiles);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_layer_mod (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *data_ptr)
|
|
{
|
|
void **data;
|
|
int layer_type;
|
|
TileManager *tiles;
|
|
TileManager *temp;
|
|
Layer *layer;
|
|
|
|
data = (void **) data_ptr;
|
|
layer = (Layer *) data[0];
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
drawable_clean (GIMP_DRAWABLE(layer));
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(layer));
|
|
break;
|
|
}
|
|
|
|
tiles = (TileManager *) data[1];
|
|
|
|
/* Issue the first update */
|
|
gdisplays_update_area (gimage,
|
|
GIMP_DRAWABLE(layer)->offset_x, GIMP_DRAWABLE(layer)->offset_y,
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
|
|
|
|
/* Create a tile manager to store the current layer contents */
|
|
temp = GIMP_DRAWABLE(layer)->tiles;
|
|
temp->x = GIMP_DRAWABLE(layer)->offset_x;
|
|
temp->y = GIMP_DRAWABLE(layer)->offset_y;
|
|
layer_type = (long) data[2];
|
|
data[2] = (void *) ((long) GIMP_DRAWABLE(layer)->type);
|
|
|
|
/* restore the layer's data */
|
|
GIMP_DRAWABLE(layer)->tiles = tiles;
|
|
GIMP_DRAWABLE(layer)->offset_x = tiles->x;
|
|
GIMP_DRAWABLE(layer)->offset_y = tiles->y;
|
|
GIMP_DRAWABLE(layer)->width = tiles->width;
|
|
GIMP_DRAWABLE(layer)->height = tiles->height;
|
|
GIMP_DRAWABLE(layer)->bytes = tiles->bpp;
|
|
GIMP_DRAWABLE(layer)->type = layer_type;
|
|
GIMP_DRAWABLE(layer)->has_alpha = TYPE_HAS_ALPHA (layer_type);
|
|
|
|
if (layer->mask)
|
|
{
|
|
GIMP_DRAWABLE(layer->mask)->offset_x = tiles->x;
|
|
GIMP_DRAWABLE(layer->mask)->offset_y = tiles->y;
|
|
}
|
|
|
|
/* If the layer type changed, update the gdisplay titles */
|
|
if (GIMP_DRAWABLE(layer)->type != (long) data[2])
|
|
gdisplays_update_title (GIMP_DRAWABLE(layer)->gimage);
|
|
|
|
/* Set the new tile manager */
|
|
data[1] = temp;
|
|
|
|
/* Issue the second update */
|
|
drawable_update (GIMP_DRAWABLE(layer), 0, 0,
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_layer_mod (int state,
|
|
void *data_ptr)
|
|
{
|
|
void ** data;
|
|
|
|
data = (void **) data_ptr;
|
|
tile_manager_destroy ((TileManager *) data[1]);
|
|
g_free (data);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Layer Mask Undo */
|
|
|
|
int
|
|
undo_push_layer_mask (GImage *gimage,
|
|
void *lmu_ptr)
|
|
{
|
|
LayerMaskUndo *lmu;
|
|
Undo *new;
|
|
int size;
|
|
|
|
lmu = (LayerMaskUndo *) lmu_ptr;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(lmu->layer));
|
|
|
|
size = channel_size (GIMP_CHANNEL (lmu->mask)) + sizeof (LayerMaskUndo);
|
|
|
|
if ((new = undo_push (gimage, size, LAYER_MASK_UNDO)))
|
|
{
|
|
new->data = lmu_ptr;
|
|
new->pop_func = undo_pop_layer_mask;
|
|
new->free_func = undo_free_layer_mask;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (lmu->undo_type == 1)
|
|
layer_mask_delete (lmu->mask);
|
|
g_free (lmu);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_layer_mask (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *lmu_ptr)
|
|
{
|
|
LayerMaskUndo *lmu;
|
|
|
|
lmu = (LayerMaskUndo *) lmu_ptr;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
drawable_clean (GIMP_DRAWABLE(lmu->layer));
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(lmu->layer));
|
|
break;
|
|
}
|
|
|
|
/* remove layer mask */
|
|
if ((state == UNDO && lmu->undo_type == 0) ||
|
|
(state == REDO && lmu->undo_type == 1))
|
|
{
|
|
/* remove the layer mask */
|
|
lmu->layer->mask = NULL;
|
|
lmu->layer->apply_mask = 0;
|
|
lmu->layer->edit_mask = 0;
|
|
lmu->layer->show_mask = 0;
|
|
|
|
/* if this is redoing a remove operation &
|
|
* the mode of application was DISCARD or
|
|
* this is undoing an add...
|
|
*/
|
|
if ((state == REDO && lmu->mode == DISCARD) || state == UNDO)
|
|
drawable_update (GIMP_DRAWABLE(lmu->layer), 0, 0,
|
|
GIMP_DRAWABLE(lmu->layer)->width, GIMP_DRAWABLE(lmu->layer)->height);
|
|
}
|
|
/* restore layer */
|
|
else
|
|
{
|
|
lmu->layer->mask = lmu->mask;
|
|
lmu->layer->apply_mask = lmu->apply_mask;
|
|
lmu->layer->edit_mask = lmu->edit_mask;
|
|
lmu->layer->show_mask = lmu->show_mask;
|
|
|
|
gimage_set_layer_mask_edit (gimage, lmu->layer, lmu->edit_mask);
|
|
|
|
/* if this is undoing a remove operation &
|
|
* the mode of application was DISCARD or
|
|
* this is redoing an add
|
|
*/
|
|
if ((state == UNDO && lmu->mode == DISCARD) || state == REDO)
|
|
drawable_update (GIMP_DRAWABLE(lmu->layer), 0, 0,
|
|
GIMP_DRAWABLE(lmu->layer)->width, GIMP_DRAWABLE(lmu->layer)->height);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_layer_mask (int state,
|
|
void *lmu_ptr)
|
|
{
|
|
LayerMaskUndo *lmu;
|
|
|
|
lmu = (LayerMaskUndo *) lmu_ptr;
|
|
|
|
/* Only free the layer mask if we're freeing from the redo
|
|
* stack and it's a layer add, or if we're freeing from
|
|
* the undo stack and it's a layer remove
|
|
*/
|
|
if ((state == REDO && lmu->undo_type == 0) ||
|
|
(state == UNDO && lmu->undo_type == 1))
|
|
layer_mask_delete (lmu->mask);
|
|
|
|
g_free (lmu);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* New Channel Undo */
|
|
|
|
int
|
|
undo_push_channel (GImage *gimage,
|
|
void *cu_ptr)
|
|
{
|
|
ChannelUndo *cu;
|
|
Undo *new;
|
|
int size;
|
|
|
|
cu = (ChannelUndo *) cu_ptr;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(cu->channel));
|
|
|
|
size = channel_size (cu->channel) + sizeof (ChannelUndo);
|
|
|
|
if ((new = undo_push (gimage, size, CHANNEL_UNDO)))
|
|
{
|
|
new->data = cu_ptr;
|
|
new->pop_func = undo_pop_channel;
|
|
new->free_func = undo_free_channel;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (cu->undo_type == 1)
|
|
channel_delete (cu->channel);
|
|
g_free (cu);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_channel (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *cu_ptr)
|
|
{
|
|
ChannelUndo *cu;
|
|
|
|
cu = (ChannelUndo *) cu_ptr;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
drawable_clean (GIMP_DRAWABLE(cu->channel));
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(cu->channel));
|
|
break;
|
|
}
|
|
|
|
/* remove channel */
|
|
if ((state == UNDO && cu->undo_type == 0) ||
|
|
(state == REDO && cu->undo_type == 1))
|
|
{
|
|
/* record the current position */
|
|
cu->prev_position = gimage_get_channel_index (gimage, cu->channel);
|
|
|
|
/* remove the channel */
|
|
gimage->channels = g_slist_remove (gimage->channels, cu->channel);
|
|
|
|
/* set the previous channel */
|
|
gimage_set_active_channel (gimage, cu->prev_channel);
|
|
|
|
/* update the area */
|
|
drawable_update (GIMP_DRAWABLE(cu->channel), 0, 0,
|
|
GIMP_DRAWABLE(cu->channel)->width, GIMP_DRAWABLE(cu->channel)->height);
|
|
}
|
|
/* restore channel */
|
|
else
|
|
{
|
|
/* record the active channel */
|
|
cu->prev_channel = gimage->active_channel;
|
|
|
|
/* add the new channel */
|
|
gimage->channels = g_slist_insert (gimage->channels, cu->channel, cu->prev_position);
|
|
|
|
/* set the new channel */
|
|
gimage_set_active_channel (gimage, cu->channel);
|
|
|
|
/* update the area */
|
|
drawable_update (GIMP_DRAWABLE(cu->channel), 0, 0,
|
|
GIMP_DRAWABLE(cu->channel)->width, GIMP_DRAWABLE(cu->channel)->height);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_channel (int state,
|
|
void *cu_ptr)
|
|
{
|
|
ChannelUndo *cu;
|
|
|
|
cu = (ChannelUndo *) cu_ptr;
|
|
|
|
/* Only free the channel if we're freeing from the redo
|
|
* stack and it's a channel add, or if we're freeing from
|
|
* the undo stack and it's a channel remove
|
|
*/
|
|
if ((state == REDO && cu->undo_type == 0) ||
|
|
(state == UNDO && cu->undo_type == 1))
|
|
channel_delete (cu->channel);
|
|
|
|
g_free (cu);
|
|
}
|
|
|
|
|
|
|
|
/*********************************/
|
|
/* Channel Mod Undo */
|
|
|
|
int
|
|
undo_push_channel_mod (GImage *gimage,
|
|
void *channel_ptr)
|
|
{
|
|
Channel *channel;
|
|
TileManager *tiles;
|
|
Undo *new;
|
|
void **data;
|
|
int size;
|
|
|
|
channel = (Channel *) channel_ptr;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(channel));
|
|
|
|
tiles = GIMP_DRAWABLE(channel)->tiles;
|
|
size = GIMP_DRAWABLE(channel)->width * GIMP_DRAWABLE(channel)->height + sizeof (void *) * 2;
|
|
|
|
if ((new = undo_push (gimage, size, CHANNEL_MOD)))
|
|
{
|
|
data = (void **) g_malloc (sizeof (void *) * 2);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_channel_mod;
|
|
new->free_func = undo_free_channel_mod;
|
|
|
|
data[0] = channel_ptr;
|
|
data[1] = (void *) tiles;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
tile_manager_destroy (tiles);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_channel_mod (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *data_ptr)
|
|
{
|
|
void **data;
|
|
TileManager *tiles;
|
|
TileManager *temp;
|
|
Channel *channel;
|
|
|
|
data = (void **) data_ptr;
|
|
channel = (Channel *) data[0];
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
drawable_clean (GIMP_DRAWABLE(channel));
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(channel));
|
|
break;
|
|
}
|
|
|
|
tiles = (TileManager *) data[1];
|
|
|
|
/* Issue the first update */
|
|
drawable_update (GIMP_DRAWABLE(channel), 0, 0,
|
|
GIMP_DRAWABLE(channel)->width, GIMP_DRAWABLE(channel)->height);
|
|
|
|
temp = GIMP_DRAWABLE(channel)->tiles;
|
|
GIMP_DRAWABLE(channel)->tiles = tiles;
|
|
GIMP_DRAWABLE(channel)->width = tiles->width;
|
|
GIMP_DRAWABLE(channel)->height = tiles->height;
|
|
|
|
/* Set the new buffer */
|
|
data[1] = temp;
|
|
|
|
/* Issue the second update */
|
|
drawable_update (GIMP_DRAWABLE(channel), 0, 0,
|
|
GIMP_DRAWABLE(channel)->width, GIMP_DRAWABLE(channel)->height);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_channel_mod (int state,
|
|
void *data_ptr)
|
|
{
|
|
void ** data;
|
|
|
|
data = (void **) data_ptr;
|
|
tile_manager_destroy ((TileManager *) data[1]);
|
|
g_free (data);
|
|
}
|
|
|
|
|
|
/**************************************/
|
|
/* Floating Selection to Layer Undo */
|
|
|
|
int
|
|
undo_push_fs_to_layer (GImage *gimage,
|
|
void *fsu_ptr)
|
|
{
|
|
FStoLayerUndo *fsu;
|
|
Undo *new;
|
|
int size;
|
|
|
|
fsu = (FStoLayerUndo *) fsu_ptr;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(fsu->layer));
|
|
|
|
size = sizeof (FStoLayerUndo);
|
|
|
|
if ((new = undo_push (gimage, size, CHANNEL_MOD)))
|
|
{
|
|
new->data = fsu_ptr;
|
|
new->pop_func = undo_pop_fs_to_layer;
|
|
new->free_func = undo_free_fs_to_layer;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
tile_manager_destroy (fsu->layer->fs.backing_store);
|
|
g_free (fsu);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_fs_to_layer (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *fsu_ptr)
|
|
{
|
|
FStoLayerUndo *fsu;
|
|
|
|
fsu = (FStoLayerUndo *) fsu_ptr;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
drawable_clean (GIMP_DRAWABLE(fsu->layer));
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
drawable_dirty (GIMP_DRAWABLE(fsu->layer));
|
|
break;
|
|
}
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
/* Update the preview for the floating sel */
|
|
drawable_invalidate_preview (GIMP_DRAWABLE(fsu->layer));
|
|
|
|
fsu->layer->fs.drawable = fsu->drawable;
|
|
gimage->active_layer = fsu->layer;
|
|
gimage->floating_sel = fsu->layer;
|
|
|
|
/* restore the contents of the drawable */
|
|
floating_sel_store (fsu->layer,
|
|
GIMP_DRAWABLE(fsu->layer)->offset_x,
|
|
GIMP_DRAWABLE(fsu->layer)->offset_y,
|
|
GIMP_DRAWABLE(fsu->layer)->width,
|
|
GIMP_DRAWABLE(fsu->layer)->height);
|
|
fsu->layer->fs.initial = TRUE;
|
|
|
|
/* clear the selection */
|
|
layer_invalidate_boundary (fsu->layer);
|
|
|
|
/* Update the preview for the gimage and underlying drawable */
|
|
drawable_invalidate_preview (GIMP_DRAWABLE(fsu->layer));
|
|
break;
|
|
|
|
case REDO:
|
|
/* restore the contents of the drawable */
|
|
floating_sel_restore (fsu->layer,
|
|
GIMP_DRAWABLE(fsu->layer)->offset_x,
|
|
GIMP_DRAWABLE(fsu->layer)->offset_y,
|
|
GIMP_DRAWABLE(fsu->layer)->width,
|
|
GIMP_DRAWABLE(fsu->layer)->height);
|
|
|
|
/* Update the preview for the gimage and underlying drawable */
|
|
drawable_invalidate_preview (GIMP_DRAWABLE(fsu->layer));
|
|
|
|
/* clear the selection */
|
|
layer_invalidate_boundary (fsu->layer);
|
|
|
|
/* update the pointers */
|
|
fsu->layer->fs.drawable = NULL;
|
|
gimage->floating_sel = NULL;
|
|
|
|
/* Update the fs drawable */
|
|
drawable_invalidate_preview (GIMP_DRAWABLE(fsu->layer));
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_fs_to_layer (int state,
|
|
void *fsu_ptr)
|
|
{
|
|
FStoLayerUndo *fsu;
|
|
|
|
fsu = (FStoLayerUndo *) fsu_ptr;
|
|
|
|
if (state == UNDO)
|
|
tile_manager_destroy (fsu->layer->fs.backing_store);
|
|
g_free (fsu);
|
|
}
|
|
|
|
|
|
/***********************************/
|
|
/* Floating Selection Rigor Undo */
|
|
|
|
int
|
|
undo_push_fs_rigor (GImage *gimage,
|
|
int layer_id)
|
|
{
|
|
Undo *new;
|
|
int size;
|
|
|
|
size = sizeof (int);
|
|
|
|
if ((new = undo_push (gimage, size, FS_RIGOR)))
|
|
{
|
|
new->data = g_new (int, 1);
|
|
new->pop_func = undo_pop_fs_rigor;
|
|
new->free_func = undo_free_fs_rigor;
|
|
|
|
*((int *) new->data) = layer_id;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_fs_rigor (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *layer_ptr)
|
|
{
|
|
int layer_id;
|
|
Layer *floating_layer;
|
|
|
|
layer_id = *((int *) layer_ptr);
|
|
if ((floating_layer = layer_get_ID (layer_id)) == NULL)
|
|
return FALSE;
|
|
if (! layer_is_floating_sel (floating_layer))
|
|
return FALSE;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
/* restore the contents of drawable the floating layer is attached to */
|
|
if (floating_layer->fs.initial == FALSE)
|
|
floating_sel_restore (floating_layer,
|
|
GIMP_DRAWABLE(floating_layer)->offset_x,
|
|
GIMP_DRAWABLE(floating_layer)->offset_y,
|
|
GIMP_DRAWABLE(floating_layer)->width,
|
|
GIMP_DRAWABLE(floating_layer)->height);
|
|
floating_layer->fs.initial = TRUE;
|
|
break;
|
|
|
|
case REDO:
|
|
/* store the affected area from the drawable in the backing store */
|
|
floating_sel_store (floating_layer,
|
|
GIMP_DRAWABLE(floating_layer)->offset_x,
|
|
GIMP_DRAWABLE(floating_layer)->offset_y,
|
|
GIMP_DRAWABLE(floating_layer)->width,
|
|
GIMP_DRAWABLE(floating_layer)->height);
|
|
floating_layer->fs.initial = TRUE;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_fs_rigor (int state,
|
|
void *layer_ptr)
|
|
{
|
|
g_free (layer_ptr);
|
|
}
|
|
|
|
|
|
/***********************************/
|
|
/* Floating Selection Relax Undo */
|
|
|
|
int
|
|
undo_push_fs_relax (GImage *gimage,
|
|
int layer_id)
|
|
{
|
|
Undo *new;
|
|
int size;
|
|
|
|
size = sizeof (int);
|
|
|
|
if ((new = undo_push (gimage, size, FS_RELAX)))
|
|
{
|
|
new->data = g_new (int, 1);
|
|
new->pop_func = undo_pop_fs_relax;
|
|
new->free_func = undo_free_fs_relax;
|
|
|
|
*((int *) new->data) = layer_id;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_fs_relax (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *layer_ptr)
|
|
{
|
|
int layer_id;
|
|
Layer *floating_layer;
|
|
|
|
layer_id = *((int *) layer_ptr);
|
|
if ((floating_layer = layer_get_ID (layer_id)) == NULL)
|
|
return FALSE;
|
|
if (! layer_is_floating_sel (floating_layer))
|
|
return FALSE;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
/* store the affected area from the drawable in the backing store */
|
|
floating_sel_store (floating_layer,
|
|
GIMP_DRAWABLE(floating_layer)->offset_x,
|
|
GIMP_DRAWABLE(floating_layer)->offset_y,
|
|
GIMP_DRAWABLE(floating_layer)->width,
|
|
GIMP_DRAWABLE(floating_layer)->height);
|
|
floating_layer->fs.initial = TRUE;
|
|
break;
|
|
|
|
case REDO:
|
|
/* restore the contents of drawable the floating layer is attached to */
|
|
if (floating_layer->fs.initial == FALSE)
|
|
floating_sel_restore (floating_layer,
|
|
GIMP_DRAWABLE(floating_layer)->offset_x,
|
|
GIMP_DRAWABLE(floating_layer)->offset_y,
|
|
GIMP_DRAWABLE(floating_layer)->width,
|
|
GIMP_DRAWABLE(floating_layer)->height);
|
|
floating_layer->fs.initial = TRUE;
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_fs_relax (int state,
|
|
void *layer_ptr)
|
|
{
|
|
g_free (layer_ptr);
|
|
}
|
|
|
|
|
|
/*********************/
|
|
/* GImage Mod Undo */
|
|
|
|
int
|
|
undo_push_gimage_mod (GImage *gimage)
|
|
{
|
|
Undo *new;
|
|
int *data;
|
|
int size;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
|
|
size = sizeof (int) * 3;
|
|
|
|
if ((new = undo_push (gimage, size, GIMAGE_MOD)))
|
|
{
|
|
data = (int *) g_malloc (sizeof (int) * 3);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_gimage_mod;
|
|
new->free_func = undo_free_gimage_mod;
|
|
|
|
data[0] = gimage->width;
|
|
data[1] = gimage->height;
|
|
data[2] = gimage->base_type;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_gimage_mod (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *data_ptr)
|
|
{
|
|
int *data;
|
|
int tmp;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
break;
|
|
}
|
|
|
|
data = (int *) data_ptr;
|
|
tmp = data[0];
|
|
data[0] = gimage->width;
|
|
gimage->width = tmp;
|
|
tmp = data[1];
|
|
data[1] = gimage->height;
|
|
gimage->height = tmp;
|
|
tmp = data[2];
|
|
data[2] = gimage->base_type;
|
|
gimage->base_type = tmp;
|
|
|
|
gimage_projection_realloc (gimage);
|
|
|
|
gimage_mask_invalidate (gimage);
|
|
channel_invalidate_previews (gimage);
|
|
layer_invalidate_previews (gimage);
|
|
gimage_invalidate_preview (gimage);
|
|
gdisplays_update_full (gimage);
|
|
gdisplays_update_title (gimage);
|
|
|
|
gimp_image_colormap_changed (gimage, -1);
|
|
|
|
if (gimage->width != (int) data[0] || gimage->height != (int) data[1])
|
|
shrink_wrap = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_gimage_mod (int state,
|
|
void *data_ptr)
|
|
{
|
|
g_free (data_ptr);
|
|
}
|
|
|
|
/***********/
|
|
/* Qmask */
|
|
|
|
typedef struct _QmaskUndo QmaskUndo;
|
|
|
|
struct _QmaskUndo
|
|
{
|
|
GImage *gimage;
|
|
int qmask;
|
|
int orig;
|
|
};
|
|
|
|
|
|
int
|
|
undo_push_qmask (GImage *gimage,
|
|
int qmask)
|
|
{
|
|
Undo *new;
|
|
QmaskUndo *data;
|
|
long size;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
|
|
size = sizeof (QmaskUndo);
|
|
|
|
if ((new = undo_push (gimage, size, GIMAGE_MOD)))
|
|
{
|
|
data = g_new (QmaskUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_qmask;
|
|
new->free_func = undo_free_qmask;
|
|
|
|
data->gimage = gimage;
|
|
data->qmask = qmask;
|
|
data->orig = data->qmask;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
undo_pop_qmask (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *data_ptr)
|
|
{
|
|
QmaskUndo *data;
|
|
int tmp;
|
|
|
|
data = data_ptr;
|
|
|
|
tmp = data->qmask;
|
|
data->qmask = data->orig;
|
|
data->orig = tmp;
|
|
|
|
gimage->qmask_state = data->qmask;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_qmask (int state,
|
|
void *data_ptr)
|
|
{
|
|
g_free (data_ptr);
|
|
}
|
|
|
|
/***********/
|
|
/* Guide */
|
|
|
|
typedef struct _GuideUndo GuideUndo;
|
|
|
|
struct _GuideUndo
|
|
{
|
|
GImage *gimage;
|
|
Guide *guide;
|
|
Guide orig;
|
|
};
|
|
int
|
|
undo_push_guide (GImage *gimage,
|
|
void *guide)
|
|
{
|
|
Undo *new;
|
|
GuideUndo *data;
|
|
long size;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
|
|
size = sizeof (GuideUndo);
|
|
|
|
if ((new = undo_push (gimage, size, GIMAGE_MOD)))
|
|
{
|
|
((Guide *)(guide))->ref_count++;
|
|
data = g_new (GuideUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_guide;
|
|
new->free_func = undo_free_guide;
|
|
|
|
data->gimage = gimage;
|
|
data->guide = guide;
|
|
data->orig = *(data->guide);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_guide (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *data_ptr)
|
|
{
|
|
GuideUndo *data;
|
|
Guide tmp;
|
|
int tmp_ref;
|
|
|
|
data = data_ptr;
|
|
|
|
gdisplays_expose_guide (gimage, data->guide);
|
|
gdisplays_expose_guide (gimage, &data->orig);
|
|
|
|
tmp_ref = data->guide->ref_count;
|
|
tmp = *(data->guide);
|
|
*(data->guide) = data->orig;
|
|
data->guide->ref_count = tmp_ref;
|
|
data->orig = tmp;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_guide (int state,
|
|
void *data_ptr)
|
|
{
|
|
GuideUndo *data;
|
|
|
|
data = data_ptr;
|
|
|
|
data->guide->ref_count--;
|
|
if (data->guide->position < 0 && data->guide->ref_count <= 0)
|
|
gimage_delete_guide (data->gimage, data->guide);
|
|
|
|
g_free (data_ptr);
|
|
}
|
|
|
|
|
|
/************/
|
|
/* Parasite */
|
|
|
|
typedef struct _ParasiteUndo ParasiteUndo;
|
|
|
|
struct _ParasiteUndo
|
|
{
|
|
GImage *gimage;
|
|
GimpDrawable *drawable;
|
|
Parasite *parasite;
|
|
char *name;
|
|
};
|
|
|
|
int
|
|
undo_push_image_parasite (GImage *gimage,
|
|
void *parasite)
|
|
{
|
|
Undo *new;
|
|
ParasiteUndo *data;
|
|
long size;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
|
|
size = sizeof (ParasiteUndo);
|
|
|
|
if ((new = undo_push (gimage, size, GIMAGE_MOD)))
|
|
{
|
|
data = g_new (ParasiteUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_parasite;
|
|
new->free_func = undo_free_parasite;
|
|
|
|
data->gimage = gimage;
|
|
data->drawable = NULL;
|
|
data->name = g_strdup (parasite_name (parasite));
|
|
data->parasite = parasite_copy (gimp_image_find_parasite (gimage, data->name));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
undo_push_image_parasite_remove (GImage *gimage,
|
|
const char *name)
|
|
{
|
|
Undo *new;
|
|
ParasiteUndo *data;
|
|
long size;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
|
|
size = sizeof (ParasiteUndo);
|
|
|
|
if ((new = undo_push (gimage, size, GIMAGE_MOD)))
|
|
{
|
|
data = g_new (ParasiteUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_parasite;
|
|
new->free_func = undo_free_parasite;
|
|
|
|
data->gimage = gimage;
|
|
data->drawable = NULL;
|
|
data->name = g_strdup (name);
|
|
data->parasite = parasite_copy (gimp_image_find_parasite (gimage, data->name));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
undo_push_drawable_parasite (GImage *gimage,
|
|
GimpDrawable *drawable,
|
|
void *parasite)
|
|
{
|
|
Undo *new;
|
|
ParasiteUndo *data;
|
|
long size;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
|
|
size = sizeof (ParasiteUndo);
|
|
|
|
if ((new = undo_push (gimage, size, GIMAGE_MOD)))
|
|
{
|
|
data = g_new (ParasiteUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_parasite;
|
|
new->free_func = undo_free_parasite;
|
|
|
|
data->gimage = NULL;
|
|
data->drawable = drawable;
|
|
data->name = g_strdup (parasite_name (parasite));
|
|
data->parasite = parasite_copy (gimp_drawable_find_parasite (drawable, data->name));
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
undo_push_drawable_parasite_remove (GImage *gimage,
|
|
GimpDrawable *drawable,
|
|
const char *name)
|
|
{
|
|
Undo *new;
|
|
ParasiteUndo *data;
|
|
long size;
|
|
|
|
/* increment the dirty flag for this gimage */
|
|
gimage_dirty (gimage);
|
|
|
|
size = sizeof (ParasiteUndo);
|
|
|
|
if ((new = undo_push (gimage, size, GIMAGE_MOD)))
|
|
{
|
|
data = g_new (ParasiteUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_parasite;
|
|
new->free_func = undo_free_parasite;
|
|
|
|
data->gimage = NULL;
|
|
data->drawable = drawable;
|
|
data->name = g_strdup (name);
|
|
data->parasite = parasite_copy (gimp_drawable_find_parasite (drawable, data->name));
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
int
|
|
undo_pop_parasite (GImage *gimage,
|
|
int state,
|
|
int type,
|
|
void *data_ptr)
|
|
{
|
|
ParasiteUndo *data;
|
|
Parasite *tmp;
|
|
|
|
data = data_ptr;
|
|
|
|
tmp = data->parasite;
|
|
|
|
if (data->gimage)
|
|
{
|
|
data->parasite = parasite_copy (gimp_image_find_parasite (gimage,
|
|
data->name));
|
|
if (tmp)
|
|
parasite_list_add (data->gimage->parasites, tmp);
|
|
else
|
|
parasite_list_remove (data->gimage->parasites, data->name);
|
|
}
|
|
else if (data->drawable)
|
|
{
|
|
data->parasite = parasite_copy (gimp_drawable_find_parasite (data->drawable,
|
|
data->name));
|
|
if (tmp)
|
|
parasite_list_add (data->drawable->parasites, tmp);
|
|
else
|
|
parasite_list_remove (data->drawable->parasites, data->name);
|
|
}
|
|
else
|
|
{
|
|
data->parasite = parasite_copy (gimp_find_parasite (data->name));
|
|
if (tmp)
|
|
gimp_attach_parasite (tmp);
|
|
else
|
|
gimp_detach_parasite (data->name);
|
|
}
|
|
|
|
if (tmp)
|
|
parasite_free (tmp);
|
|
|
|
/* if ((tmp && parasite_is_persistant(tmp)) || */
|
|
/* (data->parasite && parasite_is_persistant(data->parasite))) */
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimage_clean (gimage);
|
|
break;
|
|
case REDO:
|
|
gimage_dirty (gimage);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
undo_free_parasite (int state,
|
|
void *data_ptr)
|
|
{
|
|
ParasiteUndo *data;
|
|
|
|
data = data_ptr;
|
|
|
|
if (data->parasite)
|
|
parasite_free (data->parasite);
|
|
if (data->name)
|
|
g_free (data->name);
|
|
|
|
g_free (data_ptr);
|
|
}
|
|
|