2001-10-31 Michael Natterer <mitch@gimp.org> Chopped up the display stuff (beware: unfinished)... The plan is that GimpDisplay is the object which collects updates from the image, compresses them and waits for the GIMP to be idle to actually paint them. It should be a non-GUI object which is the model for the actual widget to connect to. GimpDisplayShell has all the widgets and handles painting and exposing of the result. Nobody should actually be required to update ot look at it as it should be a view on the GimpDisplay object. Much stuff is still in the wrong place and the functions don't follow their files' filename namespace any more. More to come... * app/display/Makefile.am * app/display/gimpdisplay-ops.[ch]: removed. It's functions didn't belong together anyway. * app/display/gimpdisplay-area.[ch]: new files: the GimpArea functions. * app/display/gimpdisplay-handlers.[ch]: new files: signal handlers for GimpImage signals. Mostly from app/gui.c. * app/display/gimpdisplay.[ch]: removed all widgets and other GUI stuff. There is still much undecided here... * app/display/gimpdisplayshell.[ch]: actually use the object and filled it with all the stuff from GimpDisplay. * app/display/gimpdisplay-callbacks.[ch] * app/display/gimpdisplay-foreach.[ch] * app/display/gimpdisplay-render.c * app/display/gimpdisplay-scale.[ch] * app/display/gimpdisplay-scroll.[ch] * app/display/gimpdisplay-selection.c: changed accordingly. * app/core/gimp.[ch]: return a GimpObject from gimp_create_display() so it can be used as single GUI independent point to create displays, require the initial scale as parameter. * app/core/gimpcontext.c: changed the ugly EEKWrapper according to the GimpDisplay structure changes. Bugfix: set the image to NULL in gimp_context_display_destroyed(). * app/core/gimpedit.c * app/core/gimpimage-new.c: changed gimp_create_display() calls accordingly. * app/core/gimpimage-convert.c: invalidate the layer & image previews here, not in the caller. * app/core/gimpimage-crop.c: update the whole image after cropping. * app/core/gimpimage.[ch]: added gimp_image_find_guide(), gimp_image_snap_point() and gimp_image_snap_rectangle(). Added "resolution_changed" and "unit_changed" signals and corresp. public convenience functions to emit them. * app/core/gimplayer.c: emit the image's "alpha_changed" signal when adding alpha to the bottom (and only) layer of the image. * app/gimpprogress.c * app/image_map.c * app/nav_window.c * app/qmask.c * app/undo.c * app/user_install.c: changed accordingly. * app/gui/edit-commands.c * app/gui/file-commands.c * app/gui/file-open-dialog.c * app/gui/image-commands.c * app/gui/info-window.c * app/gui/preferences-dialog.c * app/gui/toolbox.c * app/gui/view-commands.c: ditto. * app/gui/gui.[ch]: removed most gimp->images handlers as the displays connect to them themselves now. chaged gui_display_new() according to the gimp_create_display() changes. Added gui_get_screen_resolution(). * app/tools/gimpbezierselecttool.c * app/tools/gimpblendtool.c * app/tools/gimpbucketfilltool.c * app/tools/gimpbycolorselecttool.c * app/tools/gimpclonetool.c * app/tools/gimpcolorpickertool.c * app/tools/gimpcroptool.c * app/tools/gimpdrawtool.c * app/tools/gimpeditselectiontool.c * app/tools/gimpfliptool.c * app/tools/gimpfreeselecttool.c * app/tools/gimpfuzzyselecttool.c * app/tools/gimpinktool.c * app/tools/gimpiscissorstool.c * app/tools/gimpmagnifytool.c * app/tools/gimpmeasuretool.c * app/tools/gimpmovetool.c * app/tools/gimppainttool.c * app/tools/gimppathtool.c * app/tools/gimprectselecttool.c * app/tools/gimpselectiontool.c * app/tools/gimptexttool.c * app/tools/gimptool.c * app/tools/gimptransformtool.c * app/tools/xinput_airbrush.c: lots of changes because GimpDisplay has become two objects. Lots of gdisp->shell casting uglyness added. This is fine because exactly these parts will have to go away. (GimpDisplay will provide methods for XOR drawing upon the display in image coordinates without the need to transform coordinates all the time. Also the tools shouldn't see GdkEvents but get more useful virtual functions which speak in image coordinates too). * app/widgets/gimpcomponentlistitem.c: removed a now useless image update. * tools/pdbgen/pdb/display.pdb: use gimp_create_display(). * app/pdb/display_cmds.c: regenerated.
3018 lines
70 KiB
C
3018 lines
70 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 "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
#include "tools/tools-types.h"
|
|
|
|
#include "base/pixel-region.h"
|
|
#include "base/tile-manager.h"
|
|
#include "base/tile.h"
|
|
|
|
#include "paint-funcs/paint-funcs.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimpchannel.h"
|
|
#include "core/gimpcontainer.h"
|
|
#include "core/gimpcontext.h"
|
|
#include "core/gimpcoreconfig.h"
|
|
#include "core/gimpimage.h"
|
|
#include "core/gimpimage-mask.h"
|
|
#include "core/gimplayer.h"
|
|
#include "core/gimplayermask.h"
|
|
#include "core/gimpparasite.h"
|
|
#include "core/gimpparasitelist.h"
|
|
|
|
#include "display/gimpdisplay.h"
|
|
#include "display/gimpdisplay-foreach.h"
|
|
#include "display/gimpdisplayshell.h"
|
|
|
|
#include "tools/gimpbycolorselecttool.h"
|
|
#include "tools/gimptool.h"
|
|
#include "tools/gimpdrawtool.h"
|
|
#include "tools/gimppainttool.h"
|
|
#include "tools/gimptransformtool.h"
|
|
#include "tools/tool_manager.h"
|
|
|
|
#include "floating_sel.h"
|
|
#include "path_transform.h"
|
|
#include "undo.h"
|
|
|
|
#include "libgimp/gimpintl.h"
|
|
|
|
|
|
/*#define DEBUG*/
|
|
|
|
#ifdef DEBUG
|
|
#define TRC(x) printf x
|
|
#else
|
|
#define TRC(x)
|
|
#endif
|
|
|
|
|
|
typedef enum
|
|
{
|
|
UNDO = 0,
|
|
REDO = 1
|
|
} UndoState;
|
|
|
|
|
|
typedef gboolean (* UndoPopFunc) (GimpImage *, UndoState, UndoType, gpointer);
|
|
typedef void (* UndoFreeFunc) (UndoState, UndoType, gpointer);
|
|
|
|
typedef struct _Undo Undo;
|
|
|
|
struct _Undo
|
|
{
|
|
UndoType type; /* undo type */
|
|
gpointer data; /* data to implement the undo, NULL for group */
|
|
glong bytes; /* size of undo item */
|
|
gboolean dirties_image; /* TRUE if undo mutates image */
|
|
gboolean group_boundary; /* TRUE if this is the start/end of a group */
|
|
|
|
UndoPopFunc pop_func; /* function pointer to undo pop proc */
|
|
UndoFreeFunc free_func; /* function with specifics for freeing */
|
|
};
|
|
|
|
/* Pop functions */
|
|
|
|
static gboolean undo_pop_image (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_mask (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_layer_displace (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_transform (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_paint (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_layer (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_layer_mod (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_layer_mask (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_channel (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_channel_mod (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_fs_to_layer (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_fs_rigor (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_fs_relax (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_gimage_mod (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_guide (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_parasite (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_qmask (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_resolution (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_layer_rename (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_layer_reposition (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
static gboolean undo_pop_cantundo (GimpImage *,
|
|
UndoState, UndoType, gpointer);
|
|
|
|
|
|
/* Free functions */
|
|
|
|
static void undo_free_image (UndoState, UndoType, gpointer);
|
|
static void undo_free_mask (UndoState, UndoType, gpointer);
|
|
static void undo_free_layer_displace (UndoState, UndoType, gpointer);
|
|
static void undo_free_transform (UndoState, UndoType, gpointer);
|
|
static void undo_free_paint (UndoState, UndoType, gpointer);
|
|
static void undo_free_layer (UndoState, UndoType, gpointer);
|
|
static void undo_free_layer_mod (UndoState, UndoType, gpointer);
|
|
static void undo_free_layer_mask (UndoState, UndoType, gpointer);
|
|
static void undo_free_channel (UndoState, UndoType, gpointer);
|
|
static void undo_free_channel_mod (UndoState, UndoType, gpointer);
|
|
static void undo_free_fs_to_layer (UndoState, UndoType, gpointer);
|
|
static void undo_free_fs_rigor (UndoState, UndoType, gpointer);
|
|
static void undo_free_fs_relax (UndoState, UndoType, gpointer);
|
|
static void undo_free_gimage_mod (UndoState, UndoType, gpointer);
|
|
static void undo_free_guide (UndoState, UndoType, gpointer);
|
|
static void undo_free_parasite (UndoState, UndoType, gpointer);
|
|
static void undo_free_qmask (UndoState, UndoType, gpointer);
|
|
static void undo_free_resolution (UndoState, UndoType, gpointer);
|
|
static void undo_free_layer_rename (UndoState, UndoType, gpointer);
|
|
static void undo_free_layer_reposition(UndoState, UndoType, gpointer);
|
|
static void undo_free_cantundo (UndoState, UndoType, gpointer);
|
|
|
|
|
|
/* Sizing functions */
|
|
static gint layer_size (GimpLayer *layer);
|
|
static gint channel_size (GimpChannel *channel);
|
|
|
|
static const gchar * undo_type_to_name (UndoType undo_type);
|
|
|
|
static Undo * undo_new (UndoType undo_type,
|
|
glong size,
|
|
gboolean dirties_image);
|
|
|
|
|
|
static gboolean mode_changed = FALSE;
|
|
static gboolean size_changed = FALSE;
|
|
static gboolean resolution_changed = FALSE;
|
|
static gboolean unit_changed = FALSE;
|
|
|
|
|
|
static gint
|
|
layer_size (GimpLayer *layer)
|
|
{
|
|
gint size;
|
|
|
|
size =
|
|
sizeof (GimpLayer) +
|
|
GIMP_DRAWABLE (layer)->width * GIMP_DRAWABLE (layer)->height *
|
|
GIMP_DRAWABLE (layer)->bytes +
|
|
strlen (GIMP_OBJECT (layer)->name);
|
|
|
|
if (gimp_layer_get_mask (layer))
|
|
size += channel_size (GIMP_CHANNEL (gimp_layer_get_mask (layer)));
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
static gint
|
|
channel_size (GimpChannel *channel)
|
|
{
|
|
gint size;
|
|
|
|
size =
|
|
sizeof (GimpChannel) +
|
|
GIMP_DRAWABLE (channel)->width * GIMP_DRAWABLE (channel)->height +
|
|
strlen (GIMP_OBJECT (channel)->name);
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_list (GimpImage *gimage,
|
|
UndoState state,
|
|
GSList *list)
|
|
{
|
|
GSList *orig;
|
|
Undo *undo;
|
|
|
|
orig = list;
|
|
|
|
while (list)
|
|
{
|
|
undo = (Undo *) list->data;
|
|
if (undo)
|
|
{
|
|
if (undo->free_func)
|
|
(*undo->free_func) (state, undo->type, undo->data);
|
|
gimage->undo_bytes -= undo->bytes;
|
|
g_free (undo);
|
|
}
|
|
list = g_slist_next (list);
|
|
}
|
|
|
|
g_slist_free (orig);
|
|
}
|
|
|
|
|
|
static GSList *
|
|
remove_stack_bottom (GimpImage *gimage)
|
|
{
|
|
GSList *list;
|
|
GSList *last;
|
|
Undo *object;
|
|
gboolean in_group = FALSE;
|
|
|
|
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;
|
|
|
|
gimp_image_undo_event (gimage, UNDO_EXPIRED);
|
|
}
|
|
else
|
|
{
|
|
object = (Undo *) list->data;
|
|
if (object->group_boundary)
|
|
in_group = (in_group) ? FALSE : TRUE;
|
|
|
|
/* Make sure last points to only a single item, or the
|
|
* tail of an aggregate undo item
|
|
*/
|
|
if (!in_group)
|
|
last = list;
|
|
|
|
list = g_slist_next (list);
|
|
}
|
|
}
|
|
|
|
if (last)
|
|
return gimage->undo_stack;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Allocate and initialise a new Undo. Leaves data and function
|
|
* pointers zeroed ready to be filled in by caller. */
|
|
static Undo *
|
|
undo_new (UndoType type,
|
|
glong size,
|
|
gboolean dirties_image)
|
|
{
|
|
Undo *new;
|
|
|
|
new = g_new0 (Undo, 1);
|
|
|
|
new->type = type;
|
|
new->bytes = size;
|
|
new->dirties_image = dirties_image;
|
|
|
|
return new;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_free_up_space (GimpImage *gimage)
|
|
{
|
|
/* If there are 0 levels of undo return FALSE. */
|
|
if (gimage->gimp->config->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 >= gimage->gimp->config->levels_of_undo)
|
|
gimage->undo_stack = remove_stack_bottom (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static Undo *
|
|
undo_push (GimpImage *gimage,
|
|
glong size,
|
|
UndoType type,
|
|
gboolean dirties_image)
|
|
{
|
|
Undo *new;
|
|
|
|
/* Does this undo dirty the image? If so, we always want to mark
|
|
* image dirty, even if we can't actually push the undo. */
|
|
if (dirties_image)
|
|
gimp_image_dirty (gimage);
|
|
|
|
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 the image was dirty, but could become clean by redo-ing
|
|
* some actions, then it should now become 'infinitely' dirty.
|
|
* This is because we've just nuked the actions that would allow
|
|
* the image to become clean again. The only hope for salvation
|
|
* is to save the image now! -- austin */
|
|
if (gimage->dirty < 0)
|
|
gimage->dirty = 10000;
|
|
}
|
|
|
|
if (gimage->pushing_undo_group == UNDO_NULL)
|
|
if (! undo_free_up_space (gimage))
|
|
return NULL;
|
|
|
|
new = undo_new (type, size, dirties_image);
|
|
|
|
gimage->undo_bytes += size;
|
|
|
|
/* only increment levels if not in a group */
|
|
if (gimage->pushing_undo_group == UNDO_NULL)
|
|
gimage->undo_levels ++;
|
|
|
|
gimage->undo_stack = g_slist_prepend (gimage->undo_stack, (gpointer) new);
|
|
TRC (("undo_push: %s\n", undo_type_to_name (type)));
|
|
|
|
/* lastly, tell people about the newly pushed undo (must come after
|
|
* modification of undo_stack). */
|
|
if (gimage->pushing_undo_group == UNDO_NULL)
|
|
gimp_image_undo_event (gimage, UNDO_PUSHED);
|
|
|
|
return new;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
pop_stack (GimpImage *gimage,
|
|
GSList **stack_ptr,
|
|
GSList **unstack_ptr,
|
|
UndoState state)
|
|
{
|
|
Undo *object;
|
|
GSList *stack;
|
|
GSList *tmp;
|
|
gint status = 0;
|
|
gint in_group = 0;
|
|
gint x, y;
|
|
GimpDisplay *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->group_boundary)
|
|
{
|
|
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
|
|
{
|
|
TRC (("undo_pop: %s\n", undo_type_to_name (object->type)));
|
|
status = (* object->pop_func) (gimage, state, object->type,
|
|
object->data);
|
|
|
|
if (object->dirties_image)
|
|
{
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
gimp_image_clean (gimage);
|
|
break;
|
|
case REDO:
|
|
gimp_image_dirty (gimage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!in_group)
|
|
gimage->undo_levels += (state == UNDO) ? -1 : 1;
|
|
}
|
|
|
|
*unstack_ptr = g_slist_prepend (*unstack_ptr, (gpointer) 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 = gimp_context_get_display (gimp_get_user_context (gimage->gimp));
|
|
|
|
if (gdisp)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
|
|
shell = GIMP_DISPLAY_SHELL (gdisp->shell);
|
|
|
|
if (shell->gdisp->disp_xoffset || shell->gdisp->disp_yoffset)
|
|
{
|
|
gdk_drawable_get_size (shell->canvas->window, &x, &y);
|
|
|
|
if (shell->gdisp->disp_yoffset)
|
|
{
|
|
gimp_display_shell_add_expose_area (shell,
|
|
0, 0,
|
|
shell->gdisp->disp_width,
|
|
shell->gdisp->disp_yoffset);
|
|
gimp_display_shell_add_expose_area (shell,
|
|
0, shell->gdisp->disp_yoffset + y,
|
|
shell->gdisp->disp_width,
|
|
shell->gdisp->disp_height);
|
|
}
|
|
|
|
if (shell->gdisp->disp_xoffset)
|
|
{
|
|
gimp_display_shell_add_expose_area (shell,
|
|
0, 0,
|
|
shell->gdisp->disp_xoffset,
|
|
shell->gdisp->disp_height);
|
|
gimp_display_shell_add_expose_area (shell,
|
|
shell->gdisp->disp_xoffset + x, 0,
|
|
shell->gdisp->disp_width,
|
|
shell->gdisp->disp_height);
|
|
}
|
|
}
|
|
}
|
|
|
|
gdisplays_flush ();
|
|
|
|
/* If the mode_changed flag was set */
|
|
if (mode_changed)
|
|
{
|
|
gimp_image_mode_changed (gimage);
|
|
|
|
mode_changed = FALSE;
|
|
}
|
|
|
|
/* If the size_changed flag was set */
|
|
if (size_changed)
|
|
{
|
|
gimp_viewable_size_changed (GIMP_VIEWABLE (gimage));
|
|
|
|
size_changed = FALSE;
|
|
}
|
|
|
|
/* If the resolution_changed flag was set */
|
|
if (resolution_changed)
|
|
{
|
|
gimp_image_resolution_changed (gimage);
|
|
|
|
resolution_changed = FALSE;
|
|
}
|
|
|
|
/* If the unit_changed flag was set */
|
|
if (unit_changed)
|
|
{
|
|
gimp_image_unit_changed (gimage);
|
|
|
|
unit_changed = FALSE;
|
|
}
|
|
|
|
/* let others know that we just popped an action */
|
|
gimp_image_undo_event (gimage,
|
|
(state == UNDO)? UNDO_POPPED : UNDO_REDO);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
undo_pop (GimpImage *gimage)
|
|
{
|
|
/* Very bad idea to pop an action off the undo stack if we're in the
|
|
* middle of a group, since the whole group won't be popped. Might
|
|
* leave unbalanced group start marker earlier in the stack too,
|
|
* causing much confusion when it's later reached and
|
|
* mis-interpreted as a group start. */
|
|
g_return_val_if_fail (gimage->pushing_undo_group == UNDO_NULL, FALSE);
|
|
|
|
return pop_stack (gimage, &gimage->undo_stack, &gimage->redo_stack, UNDO);
|
|
}
|
|
|
|
|
|
gboolean
|
|
undo_redo (GimpImage *gimage)
|
|
{
|
|
/* ditto for redo stack */
|
|
g_return_val_if_fail (gimage->pushing_undo_group == UNDO_NULL, FALSE);
|
|
|
|
return pop_stack (gimage, &gimage->redo_stack, &gimage->undo_stack, REDO);
|
|
}
|
|
|
|
|
|
|
|
/* Return the name of the action that would be used if undo_pop or
|
|
* undo_redo is called, or NULL if there are no actions pushed on the
|
|
* stack. */
|
|
static const gchar *
|
|
undo_get_topitem_name (GSList *stack)
|
|
{
|
|
Undo *object;
|
|
|
|
if (!stack)
|
|
return NULL;
|
|
|
|
object = stack->data;
|
|
|
|
/* For group boundaries, the type of the boundary marker is the
|
|
* type of the whole group (but each individual action in the
|
|
* group retains its own type so layer/channel unrefs work
|
|
* correctly).
|
|
*/
|
|
|
|
return undo_type_to_name (object->type);
|
|
}
|
|
|
|
/* Return the type of the top undo action */
|
|
static UndoType
|
|
undo_get_topitem_type (GSList *stack)
|
|
{
|
|
Undo *object;
|
|
|
|
if (!stack)
|
|
return UNDO_NULL;
|
|
|
|
object = stack->data;
|
|
|
|
/* For group boundaries, the type of the boundary marker is the
|
|
* type of the whole group (but each individual action in the
|
|
* group retains its own type so layer/channel unrefs work
|
|
* correctly).
|
|
*/
|
|
|
|
return object->type;
|
|
}
|
|
|
|
|
|
const gchar *
|
|
undo_get_undo_name (GimpImage *gimage)
|
|
{
|
|
g_return_val_if_fail (gimage != NULL, NULL);
|
|
|
|
/* don't want to encourage undo while a group is open */
|
|
if (gimage->pushing_undo_group != UNDO_NULL)
|
|
return NULL;
|
|
|
|
return undo_get_topitem_name (gimage->undo_stack);
|
|
}
|
|
|
|
UndoType
|
|
undo_get_undo_top_type (GimpImage *gimage)
|
|
{
|
|
g_return_val_if_fail (gimage != NULL, UNDO_NULL);
|
|
|
|
/* don't want to encourage undo while a group is open */
|
|
if (gimage->pushing_undo_group != UNDO_NULL)
|
|
return UNDO_NULL;
|
|
|
|
return undo_get_topitem_type (gimage->undo_stack);
|
|
}
|
|
|
|
|
|
const gchar *
|
|
undo_get_redo_name (GimpImage *gimage)
|
|
{
|
|
g_return_val_if_fail (gimage != NULL, NULL);
|
|
|
|
/* don't want to encourage redo while a group is open */
|
|
if (gimage->pushing_undo_group != UNDO_NULL)
|
|
return NULL;
|
|
|
|
return undo_get_topitem_name (gimage->redo_stack);
|
|
}
|
|
|
|
|
|
static void
|
|
undo_map_over_stack (GSList *stack,
|
|
undo_map_fn fn,
|
|
gpointer data)
|
|
{
|
|
gboolean in_group = FALSE;
|
|
gint count = 0;
|
|
Undo *object;
|
|
|
|
while (stack)
|
|
{
|
|
object = (Undo *) stack->data;
|
|
if (object->group_boundary)
|
|
in_group = !in_group;
|
|
|
|
/* Keep track of group length. 0 means not in group (or group
|
|
* end marker), 1 is group start marker, >= 2 are group
|
|
* members.
|
|
*/
|
|
if (in_group)
|
|
count++;
|
|
else
|
|
count = 0;
|
|
|
|
/* Is this a group end marker or non-grouped action? */
|
|
if (count == 0)
|
|
{
|
|
if (fn (undo_type_to_name (object->type), data))
|
|
return; /* early termination option exercised */
|
|
}
|
|
|
|
stack = g_slist_next (stack);
|
|
}
|
|
}
|
|
|
|
void
|
|
undo_map_over_undo_stack (GimpImage *gimage,
|
|
undo_map_fn fn,
|
|
gpointer data)
|
|
{
|
|
/* shouldn't have group open */
|
|
g_return_if_fail (gimage->pushing_undo_group == UNDO_NULL);
|
|
undo_map_over_stack (gimage->undo_stack, fn, data);
|
|
}
|
|
|
|
void
|
|
undo_map_over_redo_stack (GimpImage *gimage,
|
|
undo_map_fn fn,
|
|
gpointer data)
|
|
{
|
|
/* shouldn't have group open */
|
|
g_return_if_fail (gimage->pushing_undo_group == UNDO_NULL);
|
|
undo_map_over_stack (gimage->redo_stack, fn, data);
|
|
}
|
|
|
|
|
|
void
|
|
undo_free (GimpImage *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;
|
|
|
|
/* If the image was dirty, but could become clean by redo-ing
|
|
* some actions, then it should now become 'infinitely' dirty.
|
|
* This is because we've just nuked the actions that would allow
|
|
* the image to become clean again. The only hope for salvation
|
|
* is to save the image now! -- austin
|
|
*/
|
|
if (gimage->dirty < 0)
|
|
gimage->dirty = 10000;
|
|
|
|
/* The same applies to the case where the image would become clean
|
|
* due to undo actions, but since user can't undo without an undo
|
|
* stack, that's not so much a problem.
|
|
*/
|
|
gimp_image_undo_event (gimage, UNDO_FREE);
|
|
}
|
|
|
|
|
|
/**********************/
|
|
/* group Undo funcs */
|
|
|
|
gboolean
|
|
undo_push_group_start (GimpImage *gimage,
|
|
UndoType type)
|
|
{
|
|
Undo *boundary_marker;
|
|
|
|
if (! gimage->undo_on)
|
|
return FALSE;
|
|
|
|
/* Bad idea to push 0 as the group type, since that won't
|
|
* be recognized as the start of the group later.
|
|
*/
|
|
g_return_val_if_fail (type != 0, FALSE);
|
|
|
|
gimage->group_count ++;
|
|
TRC (("group_start (%s)\n", undo_type_to_name (type)));
|
|
|
|
/* If we're already in a group...ignore */
|
|
if (gimage->group_count > 1)
|
|
return TRUE;
|
|
|
|
if (gimage->redo_stack)
|
|
{
|
|
undo_free_list (gimage, REDO, gimage->redo_stack);
|
|
gimage->redo_stack = NULL;
|
|
/* If the image was dirty, but could become clean by redo-ing
|
|
* some actions, then it should now become 'infinitely' dirty.
|
|
* This is because we've just nuked the actions that would allow
|
|
* the image to become clean again. The only hope for salvation
|
|
* is to save the image now! -- austin
|
|
*/
|
|
if (gimage->dirty < 0)
|
|
gimage->dirty = 10000;
|
|
}
|
|
|
|
if (! undo_free_up_space (gimage))
|
|
return FALSE;
|
|
|
|
boundary_marker = undo_new (type, sizeof (Undo), FALSE);
|
|
gimage->undo_bytes += boundary_marker->bytes;
|
|
boundary_marker->group_boundary = TRUE;
|
|
|
|
gimage->pushing_undo_group = type;
|
|
gimage->undo_stack = g_slist_prepend (gimage->undo_stack, boundary_marker);
|
|
gimage->undo_levels++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
undo_push_group_end (GimpImage *gimage)
|
|
{
|
|
Undo *boundary_marker;
|
|
|
|
if (! gimage->undo_on)
|
|
return FALSE;
|
|
|
|
gimage->group_count --;
|
|
TRC (("group end\n"));
|
|
|
|
if (gimage->group_count == 0)
|
|
{
|
|
boundary_marker = undo_new (gimage->pushing_undo_group,
|
|
sizeof (Undo), FALSE);
|
|
boundary_marker->group_boundary = TRUE;
|
|
gimage->undo_stack = g_slist_prepend (gimage->undo_stack,
|
|
boundary_marker);
|
|
gimage->undo_bytes += boundary_marker->bytes;
|
|
|
|
gimage->pushing_undo_group = UNDO_NULL;
|
|
|
|
/* Do it here, since undo_push doesn't emit this event while in the
|
|
* middle of a group */
|
|
gimp_image_undo_event (gimage, UNDO_PUSHED);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Image Undo functions */
|
|
|
|
typedef struct _ImageUndo ImageUndo;
|
|
|
|
struct _ImageUndo
|
|
{
|
|
TileManager *tiles;
|
|
GimpDrawable *drawable;
|
|
gint x1, y1, x2, y2;
|
|
gboolean sparse;
|
|
};
|
|
|
|
|
|
gboolean
|
|
undo_push_image (GimpImage *gimage,
|
|
GimpDrawable *drawable,
|
|
gint x1,
|
|
gint y1,
|
|
gint x2,
|
|
gint y2)
|
|
{
|
|
glong size;
|
|
Undo *new;
|
|
ImageUndo *image_undo;
|
|
TileManager *tiles;
|
|
PixelRegion srcPR, destPR;
|
|
|
|
x1 = CLAMP (x1, 0, gimp_drawable_width (drawable));
|
|
y1 = CLAMP (y1, 0, gimp_drawable_height (drawable));
|
|
x2 = CLAMP (x2, 0, gimp_drawable_width (drawable));
|
|
y2 = CLAMP (y2, 0, gimp_drawable_height (drawable));
|
|
|
|
size = (x2 - x1) * (y2 - y1) * gimp_drawable_bytes (drawable) + sizeof (gpointer) * 2;
|
|
|
|
if ((new = undo_push (gimage, size, IMAGE_UNDO, TRUE)))
|
|
{
|
|
image_undo = g_new (ImageUndo, 1);
|
|
|
|
/* 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),
|
|
gimp_drawable_bytes (drawable));
|
|
pixel_region_init (&srcPR, gimp_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;
|
|
}
|
|
|
|
|
|
gboolean
|
|
undo_push_image_mod (GimpImage *gimage,
|
|
GimpDrawable *drawable,
|
|
gint x1,
|
|
gint y1,
|
|
gint x2,
|
|
gint y2,
|
|
gpointer tiles_ptr,
|
|
gboolean sparse)
|
|
{
|
|
glong size;
|
|
gint dwidth, dheight;
|
|
Undo *new;
|
|
ImageUndo *image_undo;
|
|
TileManager *tiles;
|
|
|
|
if (! tiles_ptr)
|
|
return FALSE;
|
|
|
|
dwidth = gimp_drawable_width (drawable);
|
|
dheight = gimp_drawable_height (drawable);
|
|
|
|
x1 = CLAMP (x1, 0, dwidth);
|
|
y1 = CLAMP (y1, 0, dheight);
|
|
x2 = CLAMP (x2, 0, dwidth);
|
|
y2 = CLAMP (y2, 0, dheight);
|
|
|
|
tiles = (TileManager *) tiles_ptr;
|
|
size =
|
|
tile_manager_width (tiles) *
|
|
tile_manager_height (tiles) *
|
|
tile_manager_bpp (tiles) +
|
|
sizeof (gpointer) * 2;
|
|
|
|
if ((new = undo_push (gimage, size, IMAGE_MOD_UNDO, TRUE)))
|
|
{
|
|
image_undo = g_new (ImageUndo, 1);
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_image (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer image_undo_ptr)
|
|
{
|
|
ImageUndo *image_undo;
|
|
TileManager *tiles;
|
|
PixelRegion PR1, PR2;
|
|
gint x, y;
|
|
gint w, h;
|
|
|
|
image_undo = (ImageUndo *) image_undo_ptr;
|
|
tiles = image_undo->tiles;
|
|
|
|
/* some useful values */
|
|
x = image_undo->x1;
|
|
y = image_undo->y1;
|
|
|
|
if (image_undo->sparse == FALSE)
|
|
{
|
|
w = tile_manager_width (tiles);
|
|
h = tile_manager_height (tiles);
|
|
|
|
pixel_region_init (&PR1, tiles,
|
|
0, 0, w, h, TRUE);
|
|
pixel_region_init (&PR2, gimp_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 (gimp_drawable_data (image_undo->drawable), j, i, TRUE, FALSE /* TRUE */);
|
|
|
|
tile_manager_map_tile (tiles, j, i, dest_tile);
|
|
tile_manager_map_tile (gimp_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 */);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gimp_drawable_update (image_undo->drawable, x, y, w, h);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_image (UndoState state,
|
|
UndoType type,
|
|
gpointer 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 */
|
|
|
|
gboolean
|
|
undo_push_mask (GimpImage *gimage,
|
|
gpointer mask_ptr)
|
|
{
|
|
Undo *new;
|
|
MaskUndo *mask_undo;
|
|
gint size;
|
|
|
|
mask_undo = (MaskUndo *) mask_ptr;
|
|
|
|
if (mask_undo->tiles)
|
|
size =
|
|
tile_manager_width (mask_undo->tiles) *
|
|
tile_manager_height (mask_undo->tiles);
|
|
else
|
|
size = 0;
|
|
|
|
if ((new = undo_push (gimage, size, MASK_UNDO, FALSE)))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_mask (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer mask_ptr)
|
|
{
|
|
MaskUndo *mask_undo;
|
|
TileManager *new_tiles;
|
|
GimpChannel *sel_mask;
|
|
PixelRegion srcPR, destPR;
|
|
gint selection;
|
|
gint x1, y1, x2, y2;
|
|
gint width, height;
|
|
guchar empty = 0;
|
|
|
|
width = height = 0;
|
|
|
|
mask_undo = (MaskUndo *) mask_ptr;
|
|
|
|
/* save current selection mask */
|
|
sel_mask = gimp_image_get_mask (gimage);
|
|
selection = gimp_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 = tile_manager_width (mask_undo->tiles);
|
|
height = tile_manager_height (mask_undo->tiles);
|
|
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;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_mask (UndoState state,
|
|
UndoType type,
|
|
gpointer 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 */
|
|
|
|
typedef struct _LayerDisplayUndo LayerDisplaceUndo;
|
|
|
|
struct _LayerDisplayUndo
|
|
{
|
|
gint info[3];
|
|
PathUndo *path_undo;
|
|
};
|
|
|
|
gboolean
|
|
undo_push_layer_displace (GimpImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
Undo *new;
|
|
LayerDisplaceUndo *ldu;
|
|
|
|
if ((new = undo_push (gimage, 12, LAYER_DISPLACE_UNDO, TRUE)))
|
|
{
|
|
new->data = g_new (LayerDisplaceUndo, 1);
|
|
new->pop_func = undo_pop_layer_displace;
|
|
new->free_func = undo_free_layer_displace;
|
|
|
|
ldu = (LayerDisplaceUndo *) new->data;
|
|
ldu->info[0] = gimp_drawable_get_ID (GIMP_DRAWABLE (layer));
|
|
ldu->info[1] = GIMP_DRAWABLE (layer)->offset_x;
|
|
ldu->info[2] = GIMP_DRAWABLE (layer)->offset_y;
|
|
ldu->path_undo = path_transform_start_undo (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_layer_displace (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer info_ptr)
|
|
{
|
|
GimpLayer *layer;
|
|
gint old_offsets[2];
|
|
LayerDisplaceUndo *ldu;
|
|
|
|
ldu = (LayerDisplaceUndo *) info_ptr;
|
|
layer = (GimpLayer *) gimp_drawable_get_by_ID (gimage->gimp, ldu->info[0]);
|
|
if (layer)
|
|
{
|
|
old_offsets[0] = GIMP_DRAWABLE (layer)->offset_x;
|
|
old_offsets[1] = GIMP_DRAWABLE (layer)->offset_y;
|
|
gimp_drawable_update (GIMP_DRAWABLE (layer),
|
|
0, 0,
|
|
GIMP_DRAWABLE (layer)->width,
|
|
GIMP_DRAWABLE (layer)->height);
|
|
|
|
GIMP_DRAWABLE (layer)->offset_x = ldu->info[1];
|
|
GIMP_DRAWABLE (layer)->offset_y = ldu->info[2];
|
|
gimp_drawable_update (GIMP_DRAWABLE (layer),
|
|
0, 0,
|
|
GIMP_DRAWABLE (layer)->width,
|
|
GIMP_DRAWABLE (layer)->height);
|
|
|
|
if (layer->mask)
|
|
{
|
|
GIMP_DRAWABLE (layer->mask)->offset_x = ldu->info[1];
|
|
GIMP_DRAWABLE (layer->mask)->offset_y = ldu->info[2];
|
|
gimp_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 */
|
|
gimp_layer_invalidate_boundary (layer);
|
|
|
|
ldu->info[1] = old_offsets[0];
|
|
ldu->info[2] = old_offsets[1];
|
|
|
|
/* Now undo paths bits */
|
|
if (ldu->path_undo)
|
|
path_transform_do_undo (gimage, ldu->path_undo);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_layer_displace (UndoState state,
|
|
UndoType type,
|
|
gpointer info_ptr)
|
|
{
|
|
LayerDisplaceUndo * ldu;
|
|
|
|
ldu = (LayerDisplaceUndo *) info_ptr;
|
|
|
|
/* Free mem held for paths undo stuff */
|
|
if (ldu->path_undo)
|
|
path_transform_free_undo (ldu->path_undo);
|
|
|
|
g_free (info_ptr);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Transform Undo */
|
|
|
|
gboolean
|
|
undo_push_transform (GimpImage *gimage,
|
|
gpointer tu_ptr)
|
|
{
|
|
Undo *new;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (TransformUndo), TRANSFORM_UNDO, FALSE)))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_transform (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer tu_ptr)
|
|
{
|
|
GimpTool *active_tool;
|
|
GimpTransformTool *tt;
|
|
TransformUndo *tu;
|
|
TileManager *temp;
|
|
gdouble d;
|
|
gint i;
|
|
|
|
active_tool = tool_manager_get_active (gimage->gimp);
|
|
|
|
/* Can't have ANY tool selected - maybe a plugin running */
|
|
if (active_tool == NULL)
|
|
return TRUE;
|
|
|
|
tt = (GimpTransformTool *) active_tool;
|
|
tu = (TransformUndo *) tu_ptr;
|
|
|
|
path_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] = tt->trans_info[i];
|
|
tt->trans_info[i] = d;
|
|
}
|
|
|
|
/* swap the original buffer--the source buffer for repeated transforms
|
|
*/
|
|
temp = tu->original;
|
|
tu->original = tt->original;
|
|
tt->original = temp;
|
|
|
|
/* If we're re-implementing the first transform, reactivate tool */
|
|
if (state == REDO && tt->original)
|
|
{
|
|
active_tool->state = ACTIVE;
|
|
|
|
gimp_draw_tool_resume (GIMP_DRAW_TOOL (tt));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_transform (UndoState state,
|
|
UndoType type,
|
|
gpointer tu_ptr)
|
|
{
|
|
TransformUndo * tu;
|
|
|
|
tu = (TransformUndo *) tu_ptr;
|
|
if (tu->original)
|
|
tile_manager_destroy (tu->original);
|
|
path_transform_free_undo (tu->path_undo);
|
|
g_free (tu);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Paint Undo */
|
|
|
|
gboolean
|
|
undo_push_paint (GimpImage *gimage,
|
|
gpointer pu_ptr)
|
|
{
|
|
Undo *new;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (PaintUndo), PAINT_UNDO, FALSE)))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
undo_pop_paint (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer pu_ptr)
|
|
{
|
|
GimpTool *active_tool;
|
|
GimpPaintTool *pt;
|
|
PaintUndo *pu;
|
|
gdouble tmp;
|
|
|
|
active_tool = tool_manager_get_active (gimage->gimp);
|
|
|
|
/* Can't have ANY tool selected - maybe a plugin running */
|
|
if (active_tool == NULL)
|
|
return TRUE;
|
|
|
|
pt = (GimpPaintTool *) active_tool;
|
|
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 = pt->lastx;
|
|
pt->lastx = pu->lastx;
|
|
pu->lastx = tmp;
|
|
|
|
tmp = pt->lasty;
|
|
pt->lasty = pu->lasty;
|
|
pu->lasty = tmp;
|
|
|
|
tmp = pt->lastpressure;
|
|
pt->lastpressure = pu->lastpressure;
|
|
pu->lastpressure = tmp;
|
|
|
|
tmp = pt->lastxtilt;
|
|
pt->lastxtilt = pu->lastxtilt;
|
|
pu->lastxtilt = tmp;
|
|
|
|
tmp = pt->lastytilt;
|
|
pt->lastytilt = pu->lastytilt;
|
|
pu->lastytilt = tmp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
undo_free_paint (UndoState state,
|
|
UndoType type,
|
|
gpointer pu_ptr)
|
|
{
|
|
PaintUndo *pu;
|
|
|
|
pu = (PaintUndo *) pu_ptr;
|
|
g_free (pu);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* New Layer Undo */
|
|
|
|
gboolean
|
|
undo_push_layer (GimpImage *gimage,
|
|
UndoType type,
|
|
gpointer lu_ptr)
|
|
{
|
|
LayerUndo *lu;
|
|
Undo *new;
|
|
gint size;
|
|
|
|
lu = (LayerUndo *) lu_ptr;
|
|
|
|
g_return_val_if_fail (type == LAYER_ADD_UNDO ||
|
|
type == LAYER_REMOVE_UNDO,
|
|
FALSE);
|
|
|
|
size = layer_size (lu->layer) + sizeof (LayerUndo);
|
|
|
|
if ((new = undo_push (gimage, size, type, TRUE)))
|
|
{
|
|
g_object_ref (G_OBJECT (lu->layer));
|
|
|
|
new->data = lu_ptr;
|
|
new->pop_func = undo_pop_layer;
|
|
new->free_func = undo_free_layer;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_free (lu);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_layer (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer lu_ptr)
|
|
{
|
|
LayerUndo *lu;
|
|
|
|
lu = (LayerUndo *) lu_ptr;
|
|
|
|
/* remove layer */
|
|
if ((state == UNDO && type == LAYER_ADD_UNDO) ||
|
|
(state == REDO && type == LAYER_REMOVE_UNDO))
|
|
{
|
|
/* record the current position */
|
|
lu->prev_position = gimp_image_get_layer_index (gimage, lu->layer);
|
|
/* set the previous layer */
|
|
gimp_image_set_active_layer (gimage, lu->prev_layer);
|
|
|
|
/* remove the layer */
|
|
gimp_container_remove (gimage->layers, GIMP_OBJECT (lu->layer));
|
|
gimage->layer_stack = g_slist_remove (gimage->layer_stack, lu->layer);
|
|
|
|
/* reset the gimage values */
|
|
if (gimp_layer_is_floating_sel (lu->layer))
|
|
{
|
|
gimage->floating_sel = NULL;
|
|
/* reset the old drawable */
|
|
floating_sel_reset (lu->layer);
|
|
|
|
gimp_image_floating_selection_changed (gimage);
|
|
}
|
|
|
|
gimp_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 = gimp_image_get_active_layer (gimage);
|
|
|
|
/* hide the current selection--for all views */
|
|
if (gimp_image_get_active_layer (gimage))
|
|
gimp_layer_invalidate_boundary (gimp_image_get_active_layer (gimage));
|
|
|
|
/* if this is a floating selection, set the fs pointer */
|
|
if (gimp_layer_is_floating_sel (lu->layer))
|
|
gimage->floating_sel = lu->layer;
|
|
|
|
/* add the new layer */
|
|
gimp_container_insert (gimage->layers,
|
|
GIMP_OBJECT (lu->layer), lu->prev_position);
|
|
gimp_image_set_active_layer (gimage, lu->layer);
|
|
|
|
if (gimp_layer_is_floating_sel (lu->layer))
|
|
gimp_image_floating_selection_changed (gimage);
|
|
|
|
gimp_drawable_update (GIMP_DRAWABLE (lu->layer),
|
|
0, 0,
|
|
GIMP_DRAWABLE (lu->layer)->width,
|
|
GIMP_DRAWABLE (lu->layer)->height);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_layer (UndoState state,
|
|
UndoType type,
|
|
gpointer lu_ptr)
|
|
{
|
|
LayerUndo *lu;
|
|
|
|
lu = (LayerUndo *) lu_ptr;
|
|
|
|
g_object_unref (G_OBJECT (lu->layer));
|
|
g_free (lu);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Layer Mod Undo */
|
|
|
|
gboolean
|
|
undo_push_layer_mod (GimpImage *gimage,
|
|
gpointer layer_ptr)
|
|
{
|
|
GimpLayer *layer;
|
|
Undo *new;
|
|
TileManager *tiles;
|
|
gpointer *data;
|
|
gint size;
|
|
|
|
layer = (GimpLayer *) layer_ptr;
|
|
|
|
tiles = GIMP_DRAWABLE (layer)->tiles;
|
|
tile_manager_set_offsets (tiles,
|
|
GIMP_DRAWABLE (layer)->offset_x,
|
|
GIMP_DRAWABLE (layer)->offset_y);
|
|
|
|
size = (GIMP_DRAWABLE (layer)->width * GIMP_DRAWABLE (layer)->height *
|
|
GIMP_DRAWABLE (layer)->bytes + sizeof (gpointer) * 3);
|
|
|
|
if ((new = undo_push (gimage, size, LAYER_MOD, TRUE)))
|
|
{
|
|
data = g_new (gpointer, 3);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_layer_mod;
|
|
new->free_func = undo_free_layer_mod;
|
|
|
|
data[0] = layer_ptr;
|
|
data[1] = (gpointer) tiles;
|
|
data[2] = (gpointer) ((glong) GIMP_DRAWABLE (layer)->type);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
tile_manager_destroy (tiles);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_layer_mod (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
gpointer *data;
|
|
gint layer_type;
|
|
TileManager *tiles;
|
|
TileManager *temp;
|
|
GimpLayer *layer;
|
|
gboolean old_has_alpha;
|
|
|
|
data = (gpointer *) data_ptr;
|
|
layer = (GimpLayer *) data[0];
|
|
|
|
tiles = (TileManager *) data[1];
|
|
|
|
/* Issue the first update */
|
|
gimp_image_update (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;
|
|
tile_manager_set_offsets (temp,
|
|
GIMP_DRAWABLE (layer)->offset_x,
|
|
GIMP_DRAWABLE (layer)->offset_y);
|
|
layer_type = (glong) data[2];
|
|
data[2] = (gpointer) ((glong) GIMP_DRAWABLE (layer)->type);
|
|
|
|
old_has_alpha = GIMP_DRAWABLE (layer)->has_alpha;
|
|
|
|
/* restore the layer's data */
|
|
GIMP_DRAWABLE (layer)->tiles = tiles;
|
|
GIMP_DRAWABLE (layer)->width = tile_manager_width (tiles);
|
|
GIMP_DRAWABLE (layer)->height = tile_manager_height (tiles);
|
|
GIMP_DRAWABLE (layer)->bytes = tile_manager_bpp (tiles);
|
|
GIMP_DRAWABLE (layer)->type = layer_type;
|
|
GIMP_DRAWABLE (layer)->has_alpha = GIMP_IMAGE_TYPE_HAS_ALPHA (layer_type);
|
|
tile_manager_get_offsets (tiles,
|
|
&(GIMP_DRAWABLE (layer)->offset_x),
|
|
&(GIMP_DRAWABLE (layer)->offset_y));
|
|
|
|
if (layer->mask)
|
|
{
|
|
tile_manager_get_offsets (tiles,
|
|
&(GIMP_DRAWABLE (layer->mask)->offset_x),
|
|
&(GIMP_DRAWABLE (layer->mask)->offset_y));
|
|
}
|
|
|
|
if (GIMP_DRAWABLE (layer)->has_alpha != old_has_alpha &&
|
|
GIMP_DRAWABLE (layer)->gimage->layers->num_children == 1)
|
|
{
|
|
gimp_image_alpha_changed (GIMP_DRAWABLE (layer)->gimage);
|
|
}
|
|
|
|
/* Set the new tile manager */
|
|
data[1] = temp;
|
|
|
|
/* Issue the second update */
|
|
gimp_drawable_update (GIMP_DRAWABLE (layer),
|
|
0, 0,
|
|
GIMP_DRAWABLE (layer)->width,
|
|
GIMP_DRAWABLE (layer)->height);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_layer_mod (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
gpointer *data;
|
|
|
|
data = (gpointer *) data_ptr;
|
|
tile_manager_destroy ((TileManager *) data[1]);
|
|
g_free (data);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* Layer Mask Undo */
|
|
|
|
gboolean
|
|
undo_push_layer_mask (GimpImage *gimage,
|
|
UndoType type,
|
|
gpointer lmu_ptr)
|
|
{
|
|
LayerMaskUndo *lmu;
|
|
Undo *new;
|
|
gint size;
|
|
|
|
lmu = (LayerMaskUndo *) lmu_ptr;
|
|
|
|
g_return_val_if_fail (type == LAYER_MASK_ADD_UNDO ||
|
|
type == LAYER_MASK_REMOVE_UNDO,
|
|
FALSE);
|
|
|
|
size = channel_size (GIMP_CHANNEL (lmu->mask)) + sizeof (LayerMaskUndo);
|
|
|
|
if ((new = undo_push (gimage, size, type, TRUE)))
|
|
{
|
|
new->data = lmu_ptr;
|
|
new->pop_func = undo_pop_layer_mask;
|
|
new->free_func = undo_free_layer_mask;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (type == LAYER_MASK_REMOVE_UNDO)
|
|
g_object_unref (G_OBJECT (lmu->mask));
|
|
|
|
g_free (lmu);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_layer_mask (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer lmu_ptr)
|
|
{
|
|
LayerMaskUndo *lmu;
|
|
|
|
lmu = (LayerMaskUndo *) lmu_ptr;
|
|
|
|
/* remove layer mask */
|
|
if ((state == UNDO && type == LAYER_MASK_ADD_UNDO) ||
|
|
(state == REDO && type == LAYER_MASK_REMOVE_UNDO))
|
|
{
|
|
gimp_layer_apply_mask (lmu->layer, lmu->mode, FALSE);
|
|
}
|
|
/* restore layer */
|
|
else
|
|
{
|
|
gimp_layer_add_mask (lmu->layer, lmu->mask, FALSE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_layer_mask (UndoState state,
|
|
UndoType type,
|
|
gpointer 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 && type == LAYER_MASK_ADD_UNDO) ||
|
|
(state == UNDO && type == LAYER_MASK_REMOVE_UNDO))
|
|
g_object_unref (G_OBJECT (lmu->mask));
|
|
|
|
g_free (lmu);
|
|
}
|
|
|
|
|
|
/*********************************/
|
|
/* New Channel Undo */
|
|
|
|
gboolean
|
|
undo_push_channel (GimpImage *gimage,
|
|
UndoType type,
|
|
gpointer cu_ptr)
|
|
{
|
|
ChannelUndo *cu;
|
|
Undo *new;
|
|
gint size;
|
|
|
|
g_return_val_if_fail (type == CHANNEL_ADD_UNDO ||
|
|
type == CHANNEL_REMOVE_UNDO,
|
|
FALSE);
|
|
|
|
cu = (ChannelUndo *) cu_ptr;
|
|
|
|
size = channel_size (cu->channel) + sizeof (ChannelUndo);
|
|
|
|
if ((new = undo_push (gimage, size, type, TRUE)))
|
|
{
|
|
g_object_ref (G_OBJECT (cu->channel));
|
|
|
|
new->data = cu_ptr;
|
|
new->pop_func = undo_pop_channel;
|
|
new->free_func = undo_free_channel;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_free (cu);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_channel (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer cu_ptr)
|
|
{
|
|
ChannelUndo *cu;
|
|
|
|
cu = (ChannelUndo *) cu_ptr;
|
|
|
|
/* remove channel */
|
|
if ((state == UNDO && type == CHANNEL_ADD_UNDO) ||
|
|
(state == REDO && type == CHANNEL_REMOVE_UNDO))
|
|
{
|
|
/* record the current position */
|
|
cu->prev_position = gimp_image_get_channel_index (gimage, cu->channel);
|
|
|
|
/* remove the channel */
|
|
gimp_container_remove (gimage->channels, GIMP_OBJECT (cu->channel));
|
|
|
|
/* set the previous channel */
|
|
gimp_image_set_active_channel (gimage, cu->prev_channel);
|
|
|
|
/* update the area */
|
|
gimp_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 = gimp_image_get_active_channel (gimage);
|
|
|
|
/* add the new channel */
|
|
gimp_container_insert (gimage->channels,
|
|
GIMP_OBJECT (cu->channel), cu->prev_position);
|
|
|
|
/* set the new channel */
|
|
gimp_image_set_active_channel (gimage, cu->channel);
|
|
|
|
/* update the area */
|
|
gimp_drawable_update (GIMP_DRAWABLE (cu->channel),
|
|
0, 0,
|
|
GIMP_DRAWABLE (cu->channel)->width,
|
|
GIMP_DRAWABLE (cu->channel)->height);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_channel (UndoState state,
|
|
UndoType type,
|
|
gpointer cu_ptr)
|
|
{
|
|
ChannelUndo *cu;
|
|
|
|
cu = (ChannelUndo *) cu_ptr;
|
|
|
|
g_object_unref (G_OBJECT (cu->channel));
|
|
|
|
g_free (cu);
|
|
}
|
|
|
|
|
|
|
|
/*********************************/
|
|
/* Channel Mod Undo */
|
|
|
|
gboolean
|
|
undo_push_channel_mod (GimpImage *gimage,
|
|
gpointer channel_ptr)
|
|
{
|
|
GimpChannel *channel;
|
|
TileManager *tiles;
|
|
Undo *new;
|
|
gpointer *data;
|
|
gint size;
|
|
|
|
channel = (GimpChannel *) channel_ptr;
|
|
|
|
tiles = GIMP_DRAWABLE (channel)->tiles;
|
|
size = GIMP_DRAWABLE (channel)->width * GIMP_DRAWABLE (channel)->height +
|
|
sizeof (gpointer) * 2;
|
|
|
|
if ((new = undo_push (gimage, size, CHANNEL_MOD, TRUE)))
|
|
{
|
|
data = g_new (gpointer, 2);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_channel_mod;
|
|
new->free_func = undo_free_channel_mod;
|
|
|
|
data[0] = channel_ptr;
|
|
data[1] = (gpointer) tiles;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
tile_manager_destroy (tiles);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_channel_mod (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
gpointer *data;
|
|
TileManager *tiles;
|
|
TileManager *temp;
|
|
GimpChannel *channel;
|
|
|
|
data = (gpointer *) data_ptr;
|
|
channel = (GimpChannel *) data[0];
|
|
|
|
tiles = (TileManager *) data[1];
|
|
|
|
/* Issue the first update */
|
|
gimp_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 = tile_manager_width (tiles);
|
|
GIMP_DRAWABLE (channel)->height = tile_manager_height (tiles);
|
|
GIMP_CHANNEL (channel)->bounds_known = FALSE;
|
|
/* #4840. set to FALSE because bounds
|
|
reflect previous tile set */
|
|
|
|
/* Set the new buffer */
|
|
data[1] = temp;
|
|
|
|
/* Issue the second update */
|
|
gimp_drawable_update (GIMP_DRAWABLE (channel),
|
|
0, 0,
|
|
GIMP_DRAWABLE (channel)->width,
|
|
GIMP_DRAWABLE (channel)->height);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_channel_mod (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
gpointer *data;
|
|
|
|
data = (gpointer *) data_ptr;
|
|
tile_manager_destroy ((TileManager *) data[1]);
|
|
g_free (data);
|
|
}
|
|
|
|
|
|
/**************************************/
|
|
/* Floating Selection to Layer Undo */
|
|
|
|
gboolean
|
|
undo_push_fs_to_layer (GimpImage *gimage,
|
|
gpointer fsu_ptr)
|
|
{
|
|
FStoLayerUndo *fsu;
|
|
Undo *new;
|
|
|
|
fsu = (FStoLayerUndo *) fsu_ptr;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (FStoLayerUndo), FS_TO_LAYER_UNDO, TRUE)))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_fs_to_layer (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer fsu_ptr)
|
|
{
|
|
FStoLayerUndo *fsu;
|
|
|
|
fsu = (FStoLayerUndo *) fsu_ptr;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
/* Update the preview for the floating sel */
|
|
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (fsu->layer));
|
|
|
|
fsu->layer->fs.drawable = fsu->drawable;
|
|
gimp_image_set_active_layer (gimage, 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 */
|
|
gimp_layer_invalidate_boundary (fsu->layer);
|
|
|
|
/* Update the preview for the gimage and underlying drawable */
|
|
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (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 */
|
|
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (fsu->layer));
|
|
|
|
/* clear the selection */
|
|
gimp_layer_invalidate_boundary (fsu->layer);
|
|
|
|
/* update the pointers */
|
|
fsu->layer->fs.drawable = NULL;
|
|
gimage->floating_sel = NULL;
|
|
|
|
/* Update the fs drawable */
|
|
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (fsu->layer));
|
|
break;
|
|
}
|
|
|
|
gimp_image_floating_selection_changed (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_fs_to_layer (UndoState state,
|
|
UndoType type,
|
|
gpointer 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 */
|
|
|
|
gboolean
|
|
undo_push_fs_rigor (GimpImage *gimage,
|
|
gint layer_ID)
|
|
{
|
|
Undo *new;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (gint32), FS_RIGOR, FALSE)))
|
|
{
|
|
new->data = g_new (gint32, 1);
|
|
new->pop_func = undo_pop_fs_rigor;
|
|
new->free_func = undo_free_fs_rigor;
|
|
|
|
*((gint32 *) new->data) = layer_ID;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_fs_rigor (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer layer_ptr)
|
|
{
|
|
gint32 layer_ID;
|
|
GimpLayer *floating_layer;
|
|
|
|
layer_ID = *((gint32 *) layer_ptr);
|
|
|
|
if ((floating_layer = (GimpLayer *) gimp_drawable_get_by_ID (gimage->gimp, layer_ID)) == NULL)
|
|
return FALSE;
|
|
|
|
if (! gimp_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;
|
|
}
|
|
|
|
gimp_image_floating_selection_changed (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_fs_rigor (UndoState state,
|
|
UndoType type,
|
|
gpointer layer_ptr)
|
|
{
|
|
g_free (layer_ptr);
|
|
}
|
|
|
|
|
|
/***********************************/
|
|
/* Floating Selection Relax Undo */
|
|
|
|
gboolean
|
|
undo_push_fs_relax (GimpImage *gimage,
|
|
gint32 layer_ID)
|
|
{
|
|
Undo *new;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (gint32), FS_RELAX, FALSE)))
|
|
{
|
|
new->data = g_new (gint32, 1);
|
|
new->pop_func = undo_pop_fs_relax;
|
|
new->free_func = undo_free_fs_relax;
|
|
|
|
*((gint32 *) new->data) = layer_ID;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_fs_relax (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer layer_ptr)
|
|
{
|
|
gint32 layer_ID;
|
|
GimpLayer *floating_layer;
|
|
|
|
layer_ID = *((gint32 *) layer_ptr);
|
|
|
|
if ((floating_layer = (GimpLayer *) gimp_drawable_get_by_ID (gimage->gimp, layer_ID)) == NULL)
|
|
return FALSE;
|
|
|
|
if (! gimp_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;
|
|
}
|
|
|
|
gimp_image_floating_selection_changed (gimage);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_fs_relax (UndoState state,
|
|
UndoType type,
|
|
gpointer layer_ptr)
|
|
{
|
|
g_free (layer_ptr);
|
|
}
|
|
|
|
|
|
/*********************/
|
|
/* GImage Mod Undo */
|
|
|
|
gboolean
|
|
undo_push_gimage_mod (GimpImage *gimage)
|
|
{
|
|
Undo *new;
|
|
gint *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (gint) * 3, GIMAGE_MOD, TRUE)))
|
|
{
|
|
data = g_new (gint, 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;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_gimage_mod (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
gint *data;
|
|
gint tmp;
|
|
|
|
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;
|
|
|
|
gimp_image_projection_realloc (gimage);
|
|
|
|
gimage_mask_invalidate (gimage);
|
|
|
|
gimp_image_colormap_changed (gimage, -1);
|
|
|
|
if (data[2] != gimage->base_type)
|
|
mode_changed = TRUE;
|
|
|
|
if (gimage->width != (int) data[0] || gimage->height != (int) data[1])
|
|
size_changed = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_gimage_mod (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
g_free (data_ptr);
|
|
}
|
|
|
|
/***********/
|
|
/* Qmask */
|
|
|
|
typedef struct _QmaskUndo QmaskUndo;
|
|
|
|
struct _QmaskUndo
|
|
{
|
|
GimpImage *gimage;
|
|
gboolean qmask;
|
|
};
|
|
|
|
|
|
gboolean
|
|
undo_push_qmask (GimpImage *gimage)
|
|
{
|
|
Undo *new;
|
|
QmaskUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (QmaskUndo), QMASK_UNDO, TRUE)))
|
|
{
|
|
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 = gimage->qmask_state;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
undo_pop_qmask (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
QmaskUndo *data;
|
|
gint tmp;
|
|
|
|
data = (QmaskUndo *) data_ptr;
|
|
|
|
tmp = gimage->qmask_state;
|
|
gimage->qmask_state = data->qmask;
|
|
data->qmask = tmp;
|
|
|
|
/* make sure the buttons on all displays are updated */
|
|
gdisplays_flush ();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_qmask (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
g_free (data_ptr);
|
|
}
|
|
|
|
/***********/
|
|
/* Guide */
|
|
|
|
typedef struct _GuideUndo GuideUndo;
|
|
|
|
struct _GuideUndo
|
|
{
|
|
GimpImage *gimage;
|
|
GimpGuide *guide;
|
|
GimpGuide orig;
|
|
};
|
|
|
|
gboolean
|
|
undo_push_guide (GimpImage *gimage,
|
|
gpointer guide)
|
|
{
|
|
Undo *new;
|
|
GuideUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (GuideUndo), GUIDE_UNDO, TRUE)))
|
|
{
|
|
((GimpGuide *) (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;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_guide (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
GuideUndo *data;
|
|
GimpGuide tmp;
|
|
gint tmp_ref;
|
|
|
|
data = data_ptr;
|
|
|
|
gdisplays_expose_guide (gimage, data->guide);
|
|
|
|
tmp_ref = data->guide->ref_count;
|
|
tmp = *(data->guide);
|
|
*(data->guide) = data->orig;
|
|
data->guide->ref_count = tmp_ref;
|
|
data->orig = tmp;
|
|
|
|
gdisplays_expose_guide (gimage, data->guide);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_guide (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
GuideUndo *data;
|
|
|
|
data = data_ptr;
|
|
|
|
data->guide->ref_count--;
|
|
if (data->guide->position < 0 && data->guide->ref_count <= 0)
|
|
{
|
|
gimp_image_remove_guide (data->gimage, data->guide);
|
|
g_free (data->guide);
|
|
}
|
|
g_free (data);
|
|
}
|
|
|
|
/****************/
|
|
/* Resolution */
|
|
|
|
typedef struct _ResolutionUndo ResolutionUndo;
|
|
|
|
struct _ResolutionUndo
|
|
{
|
|
gdouble xres;
|
|
gdouble yres;
|
|
GimpUnit unit;
|
|
};
|
|
|
|
gboolean
|
|
undo_push_resolution (GimpImage *gimage)
|
|
{
|
|
Undo *new;
|
|
ResolutionUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (ResolutionUndo), RESOLUTION_UNDO, TRUE)))
|
|
{
|
|
data = g_new (ResolutionUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_resolution;
|
|
new->free_func = undo_free_resolution;
|
|
|
|
data->xres = gimage->xresolution;
|
|
data->yres = gimage->yresolution;
|
|
data->unit = gimage->unit;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
undo_pop_resolution (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
ResolutionUndo *data;
|
|
|
|
data = data_ptr;
|
|
|
|
if (ABS (data->xres - gimage->xresolution) >= 1e-5 ||
|
|
ABS (data->yres - gimage->yresolution) >= 1e-5)
|
|
{
|
|
gdouble old_xres;
|
|
gdouble old_yres;
|
|
|
|
old_xres = gimage->xresolution;
|
|
old_yres = gimage->yresolution;
|
|
|
|
gimage->xresolution = data->xres;
|
|
gimage->yresolution = data->yres;
|
|
|
|
data->xres = old_xres;
|
|
data->yres = old_yres;
|
|
|
|
resolution_changed = TRUE;
|
|
}
|
|
|
|
if (data->unit != gimage->unit)
|
|
{
|
|
GimpUnit old_unit;
|
|
|
|
old_unit = gimage->unit;
|
|
gimage->unit = data->unit;
|
|
data->unit = old_unit;
|
|
|
|
unit_changed = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
undo_free_resolution (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
g_free (data_ptr);
|
|
}
|
|
|
|
|
|
/************/
|
|
/* Parasite */
|
|
|
|
typedef struct _ParasiteUndo ParasiteUndo;
|
|
|
|
struct _ParasiteUndo
|
|
{
|
|
GimpImage *gimage;
|
|
GimpDrawable *drawable;
|
|
GimpParasite *parasite;
|
|
gchar *name;
|
|
};
|
|
|
|
gboolean
|
|
undo_push_image_parasite (GimpImage *gimage,
|
|
gpointer parasite)
|
|
{
|
|
Undo *new;
|
|
ParasiteUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (ParasiteUndo), PARASITE_ATTACH_UNDO, TRUE)))
|
|
{
|
|
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 (gimp_parasite_name (parasite));
|
|
data->parasite = gimp_parasite_copy (gimp_image_parasite_find (gimage, data->name));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
undo_push_image_parasite_remove (GimpImage *gimage,
|
|
const gchar *name)
|
|
{
|
|
Undo *new;
|
|
ParasiteUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (ParasiteUndo), PARASITE_REMOVE_UNDO, TRUE)))
|
|
{
|
|
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 = gimp_parasite_copy (gimp_image_parasite_find (gimage, data->name));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
undo_push_drawable_parasite (GimpImage *gimage,
|
|
GimpDrawable *drawable,
|
|
gpointer parasite)
|
|
{
|
|
Undo *new;
|
|
ParasiteUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (ParasiteUndo), GIMAGE_MOD, TRUE)))
|
|
{
|
|
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 (gimp_parasite_name (parasite));
|
|
data->parasite = gimp_parasite_copy (gimp_drawable_parasite_find (drawable, data->name));
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
undo_push_drawable_parasite_remove (GimpImage *gimage,
|
|
GimpDrawable *drawable,
|
|
const gchar *name)
|
|
{
|
|
Undo *new;
|
|
ParasiteUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (ParasiteUndo), GIMAGE_MOD, TRUE)))
|
|
{
|
|
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 = gimp_parasite_copy (gimp_drawable_parasite_find (drawable, data->name));
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
undo_pop_parasite (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
ParasiteUndo *data;
|
|
GimpParasite *tmp;
|
|
|
|
data = data_ptr;
|
|
|
|
tmp = data->parasite;
|
|
|
|
if (data->gimage)
|
|
{
|
|
data->parasite =
|
|
gimp_parasite_copy (gimp_image_parasite_find (gimage, data->name));
|
|
|
|
if (tmp)
|
|
gimp_parasite_list_add (data->gimage->parasites, tmp);
|
|
else
|
|
gimp_parasite_list_remove (data->gimage->parasites, data->name);
|
|
}
|
|
else if (data->drawable)
|
|
{
|
|
data->parasite =
|
|
gimp_parasite_copy (gimp_drawable_parasite_find (data->drawable,
|
|
data->name));
|
|
if (tmp)
|
|
gimp_parasite_list_add (data->drawable->parasites, tmp);
|
|
else
|
|
gimp_parasite_list_remove (data->drawable->parasites, data->name);
|
|
}
|
|
else
|
|
{
|
|
data->parasite = gimp_parasite_copy (gimp_parasite_find (gimage->gimp,
|
|
data->name));
|
|
if (tmp)
|
|
gimp_parasite_attach (gimage->gimp, tmp);
|
|
else
|
|
gimp_parasite_detach (gimage->gimp, data->name);
|
|
}
|
|
|
|
if (tmp)
|
|
gimp_parasite_free (tmp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_parasite (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
ParasiteUndo *data;
|
|
|
|
data = data_ptr;
|
|
|
|
if (data->parasite)
|
|
gimp_parasite_free (data->parasite);
|
|
if (data->name)
|
|
g_free (data->name);
|
|
|
|
g_free (data_ptr);
|
|
}
|
|
|
|
|
|
|
|
/*************/
|
|
/* Layer re-position */
|
|
|
|
typedef struct _LayerRepositionUndo LayerRepositionUndo;
|
|
|
|
struct _LayerRepositionUndo
|
|
{
|
|
GimpLayer *layer;
|
|
gint old_position;
|
|
};
|
|
|
|
gboolean
|
|
undo_push_layer_reposition (GimpImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
Undo *new;
|
|
LayerRepositionUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (LayerRepositionUndo), LAYER_REPOSITION_UNDO, TRUE)))
|
|
{
|
|
data = g_new (LayerRepositionUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_layer_reposition;
|
|
new->free_func = undo_free_layer_reposition;
|
|
|
|
data->layer = layer;
|
|
data->old_position = gimp_image_get_layer_index (gimage, layer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
undo_pop_layer_reposition (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
void *data_ptr)
|
|
{
|
|
LayerRepositionUndo *data = data_ptr;
|
|
gint pos;
|
|
|
|
/* what's the layer's current index? */
|
|
pos = gimp_image_get_layer_index (gimage, data->layer);
|
|
gimp_image_position_layer (gimage, data->layer, data->old_position, FALSE);
|
|
|
|
data->old_position = pos;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
undo_free_layer_reposition (UndoState state,
|
|
UndoType type,
|
|
void *data_ptr)
|
|
{
|
|
g_free (data_ptr);
|
|
}
|
|
|
|
|
|
/*************/
|
|
/* Layer name change */
|
|
|
|
typedef struct _LayerRenameUndo LayerRenameUndo;
|
|
|
|
struct _LayerRenameUndo
|
|
{
|
|
GimpLayer *layer;
|
|
gchar *old_name;
|
|
};
|
|
|
|
gboolean
|
|
undo_push_layer_rename (GimpImage *gimage,
|
|
GimpLayer *layer)
|
|
{
|
|
Undo *new;
|
|
LayerRenameUndo *data;
|
|
|
|
if ((new = undo_push (gimage,
|
|
sizeof (LayerRenameUndo), LAYER_RENAME_UNDO, TRUE)))
|
|
{
|
|
data = g_new (LayerRenameUndo, 1);
|
|
new->data = data;
|
|
new->pop_func = undo_pop_layer_rename;
|
|
new->free_func = undo_free_layer_rename;
|
|
|
|
data->layer = layer;
|
|
data->old_name = g_strdup (gimp_object_get_name (GIMP_OBJECT (layer)));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
undo_pop_layer_rename (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
void *data_ptr)
|
|
{
|
|
LayerRenameUndo *data = data_ptr;
|
|
gchar *tmp;
|
|
|
|
tmp = g_strdup (gimp_object_get_name (GIMP_OBJECT (data->layer)));
|
|
gimp_object_set_name (GIMP_OBJECT (data->layer), data->old_name);
|
|
g_free (data->old_name);
|
|
data->old_name = tmp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
undo_free_layer_rename (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
LayerRenameUndo *data = data_ptr;
|
|
|
|
g_free (data->old_name);
|
|
g_free (data);
|
|
}
|
|
|
|
|
|
|
|
/*************/
|
|
/* Something for which programmer is too lazy to write an undo
|
|
* function for.
|
|
*/
|
|
|
|
gboolean
|
|
undo_push_cantundo (GimpImage *gimage,
|
|
const gchar *action)
|
|
{
|
|
Undo *new;
|
|
|
|
/* 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. */
|
|
|
|
new = undo_push (gimage, 0, GIMAGE_MOD, TRUE);
|
|
if (!new)
|
|
return FALSE;
|
|
|
|
new->data = (void*) action;
|
|
new->pop_func = undo_pop_cantundo;
|
|
new->free_func = undo_free_cantundo;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
undo_pop_cantundo (GimpImage *gimage,
|
|
UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
gchar *action = data_ptr;
|
|
|
|
switch (state)
|
|
{
|
|
case UNDO:
|
|
g_message (_("Can't undo %s"), action);
|
|
break;
|
|
|
|
case REDO:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
undo_free_cantundo (UndoState state,
|
|
UndoType type,
|
|
gpointer data_ptr)
|
|
{
|
|
/* nothing to free */
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A "ok" to the name means I've checked where it's used and
|
|
it seems plausible. -- austin 23/9/99 */
|
|
static struct undo_name_t
|
|
{
|
|
UndoType type;
|
|
const gchar *name;
|
|
}
|
|
undo_name[] =
|
|
{
|
|
{ UNDO_NULL, N_("<<invalid>>") },
|
|
{ IMAGE_UNDO, N_("image") },
|
|
{ IMAGE_MOD_UNDO, N_("image mod") },
|
|
{ MASK_UNDO, N_("mask") },
|
|
{ LAYER_DISPLACE_UNDO, N_("layer move") }, /* ok */
|
|
{ TRANSFORM_UNDO, N_("transform") },
|
|
{ PAINT_UNDO, N_("paint") },
|
|
{ LAYER_ADD_UNDO, N_("new layer") },
|
|
{ LAYER_REMOVE_UNDO, N_("delete layer") },
|
|
{ LAYER_MOD, N_("layer mod") },
|
|
{ LAYER_MASK_ADD_UNDO, N_("add layer mask") }, /* ok */
|
|
{ LAYER_MASK_REMOVE_UNDO, N_("delete layer mask") }, /* ok */
|
|
{ LAYER_RENAME_UNDO, N_("rename layer") },
|
|
{ LAYER_REPOSITION_UNDO, N_("layer reposition") }, /* ok */
|
|
{ CHANNEL_ADD_UNDO, N_("new channel") },
|
|
{ CHANNEL_REMOVE_UNDO, N_("delete channel") },
|
|
{ CHANNEL_MOD, N_("channel mod") },
|
|
{ FS_TO_LAYER_UNDO, N_("FS to layer") }, /* ok */
|
|
{ GIMAGE_MOD, N_("gimage") },
|
|
{ FS_RIGOR, N_("FS rigor") },
|
|
{ FS_RELAX, N_("FS relax") },
|
|
{ GUIDE_UNDO, N_("guide") },
|
|
{ TEXT_UNDO, N_("text") },
|
|
{ FLOAT_MASK_UNDO, N_("float selection") },
|
|
{ EDIT_PASTE_UNDO, N_("paste") },
|
|
{ EDIT_CUT_UNDO, N_("cut") },
|
|
{ TRANSFORM_CORE_UNDO, N_("transform core") },
|
|
{ PAINT_CORE_UNDO, N_("paint core") },
|
|
{ FLOATING_LAYER_UNDO, N_("floating layer") }, /* unused! */
|
|
{ LINKED_LAYER_UNDO, N_("linked layer") },
|
|
{ LAYER_APPLY_MASK_UNDO, N_("apply layer mask") }, /* ok */
|
|
{ LAYER_MERGE_UNDO, N_("layer merge") },
|
|
{ FS_ANCHOR_UNDO, N_("FS anchor") },
|
|
{ GIMAGE_MOD_UNDO, N_("gimage mod") },
|
|
{ CROP_UNDO, N_("crop") },
|
|
{ LAYER_SCALE_UNDO, N_("layer scale") },
|
|
{ LAYER_RESIZE_UNDO, N_("layer resize") },
|
|
{ QMASK_UNDO, N_("quickmask") },
|
|
{ PARASITE_ATTACH_UNDO, N_("attach parasite") },
|
|
{ PARASITE_REMOVE_UNDO, N_("remove parasite") },
|
|
{ RESOLUTION_UNDO, N_("resolution change") },
|
|
{ IMAGE_SCALE_UNDO, N_("image scale") },
|
|
{ IMAGE_RESIZE_UNDO, N_("image resize") },
|
|
{ MISC_UNDO, N_("misc") }
|
|
};
|
|
|
|
static const gchar *
|
|
undo_type_to_name (UndoType type)
|
|
{
|
|
gint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (undo_name); i++)
|
|
if (undo_name[i].type == type)
|
|
return gettext (undo_name[i].name);
|
|
|
|
/* no name found */
|
|
return "";
|
|
}
|