Dirty flag now correct in all cases. Can be displayed in image window

Mon Aug 23 10:15:32 EDT 1999  Austin Donnelly  <austin@gimp.org>

	Dirty flag now correct in all cases.  Can be displayed in image
	window title too.  See NOTE near gimp_image_dirty() for details.

	* app/fileops.c: gimp_image_clean_all() after reverting an image.
	* app/gdisplay.c: register handlers for gimage dirty and clean
	    signals to update image title.  New image-title-format
	    expansion: %Dx expands to x if the image is dirty.
	* app/gdisplay_ops.c: gimage->dirty flags != 0 is the correct
	    condition to test to see if an image is dirty.
	* app/gimpdrawable.c: gimp_image_dirty() should never be called
	    except from an undo_push_* function.  Call
	    undo_push_cantundo() if you want to dirty the image but can't
	    be bothered writing an undo handler (be ashamed of yourself!).
	* app/gimpimage.c: new gimage signal: clean.  Emitted when an undo
	    operation takes place.  Gimage changes when either dirty or
	    clean is emitted, so if you need to update previews etc, look
	    for both!  Move group_count into gimage structure, since
	    leaving it as a static in undo.c is bad if two undo groups are
	    started on different images at the same time.  More changes
	    of gimp_image_dirty() to undo_push_cantundo()
	    (parasite-related, plus layer moves).  See the NOTE on dirty
	    counter near gimp_image_dirty() for the full story.
	    gimp_image_dirty() and gimp_image_clean() simplified - counter
	    can go negative.
	* app/gimpimageP.h: group_count moved from undo.c
	* app/layers_dialog.c: push undo for layer name change, rather
	    than dirtying the image.
	* app/undo.c: layer rename undo functions
	    added. undo_push_cantundo() convenience functions added.
	    group_count made per-gimage since everything else is.  When
	    blowing away redo stack, make image infinitely dirty if redo
	    info contained file save point.
	* app/undo.h: added undo_push_layer_rename() and
	    undo_push_cantundo().
	* TODO: added idea for undo history window.
This commit is contained in:
EDT 1999 Austin Donnelly 1999-08-23 14:34:58 +00:00 committed by Austin Donnelly
parent ef4cb06bb7
commit 0a7dca9110
25 changed files with 972 additions and 188 deletions

View File

@ -1,3 +1,41 @@
Mon Aug 23 10:15:32 EDT 1999 Austin Donnelly <austin@gimp.org>
Dirty flag now correct in all cases. Can be displayed in image
window title too. See NOTE near gimp_image_dirty() for details.
* app/fileops.c: gimp_image_clean_all() after reverting an image.
* app/gdisplay.c: register handlers for gimage dirty and clean
signals to update image title. New image-title-format
expansion: %Dx expands to x if the image is dirty.
* app/gdisplay_ops.c: gimage->dirty flags != 0 is the correct
condition to test to see if an image is dirty.
* app/gimpdrawable.c: gimp_image_dirty() should never be called
except from an undo_push_* function. Call
undo_push_cantundo() if you want to dirty the image but can't
be bothered writing an undo handler (be ashamed of yourself!).
* app/gimpimage.c: new gimage signal: clean. Emitted when an undo
operation takes place. Gimage changes when either dirty or
clean is emitted, so if you need to update previews etc, look
for both! Move group_count into gimage structure, since
leaving it as a static in undo.c is bad if two undo groups are
started on different images at the same time. More changes
of gimp_image_dirty() to undo_push_cantundo()
(parasite-related, plus layer moves). See the NOTE on dirty
counter near gimp_image_dirty() for the full story.
gimp_image_dirty() and gimp_image_clean() simplified - counter
can go negative.
* app/gimpimageP.h: group_count moved from undo.c
* app/layers_dialog.c: push undo for layer name change, rather
than dirtying the image.
* app/undo.c: layer rename undo functions
added. undo_push_cantundo() convenience functions added.
group_count made per-gimage since everything else is. When
blowing away redo stack, make image infinitely dirty if redo
info contained file save point.
* app/undo.h: added undo_push_layer_rename() and
undo_push_cantundo().
* TODO: added idea for undo history window.
1999-08-23 Michael Natterer <mitschel@cs.tu-berlin.de> 1999-08-23 Michael Natterer <mitschel@cs.tu-berlin.de>
* app/bucket_fill.[ch]: export bucket_fill_region(). * app/bucket_fill.[ch]: export bucket_fill_region().

9
TODO
View File

@ -406,6 +406,13 @@ named undo
meaningful undo history display, so you can undo multiple meaningful undo history display, so you can undo multiple
levels in one go. levels in one go.
undo history
The "type" argument to the undo_push_* functions isn't really
used for anything. We could use it to do the named undos
described above, and allow a "history" window to list the
undo-able actions. Clicking on the relevant line would
roll-back to that point. -- austin.
gradient map layer gradient map layer
map values from current gradient to value/intensity map values from current gradient to value/intensity
@ -431,5 +438,3 @@ Grid
bitmap. limited utility for char's, but a couple of nice sets bitmap. limited utility for char's, but a couple of nice sets
of winding fonts could make it interestings. And you could of winding fonts could make it interestings. And you could
use words or strings. use words or strings.

View File

@ -490,7 +490,7 @@ gimp_drawable_attach_parasite (GimpDrawable *drawable,
!parasite_compare( parasite, !parasite_compare( parasite,
gimp_drawable_find_parasite gimp_drawable_find_parasite
(drawable, parasite_name (parasite)))) (drawable, parasite_name (parasite))))
gimp_image_dirty (drawable->gimage); undo_push_cantundo (drawable->gimage, _("parasite attach to drawable"));
parasite_list_add (drawable->parasites, parasite); parasite_list_add (drawable->parasites, parasite);
if (parasite_has_flag (parasite, PARASITE_ATTACH_PARENT)) if (parasite_has_flag (parasite, PARASITE_ATTACH_PARENT))
@ -523,7 +523,7 @@ gimp_drawable_detach_parasite (GimpDrawable *drawable,
undo_push_drawable_parasite_remove (drawable->gimage, drawable, undo_push_drawable_parasite_remove (drawable->gimage, drawable,
parasite_name (p)); parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (drawable->gimage); undo_push_cantundo (drawable->gimage, _("detach parasite from drawable"));
parasite_list_remove (drawable->parasites, parasite); parasite_list_remove (drawable->parasites, parasite);
} }

View File

@ -91,6 +91,7 @@ guint32 next_guide_id = 1; /* For generating guide_ID handles for PDB stuff */
*/ */
enum { enum {
CLEAN,
DIRTY, DIRTY,
REPAINT, REPAINT,
RENAME, RENAME,
@ -118,6 +119,9 @@ gimp_image_class_init (GimpImageClass *klass)
object_class->destroy = gimp_image_destroy; object_class->destroy = gimp_image_destroy;
gimp_image_signals[CLEAN] =
gimp_signal_new ("clean", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void);
gimp_image_signals[DIRTY] = gimp_image_signals[DIRTY] =
gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0, gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void); gimp_sigtype_void);
@ -165,6 +169,7 @@ gimp_image_init (GimpImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 0; gimage->undo_levels = 0;
gimage->group_count = 0;
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[0] = FALSE;
gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[1] = FALSE;
@ -1069,7 +1074,7 @@ gimp_image_attach_parasite (GimpImage *gimage,
&& !parasite_compare (parasite, && !parasite_compare (parasite,
gimp_image_find_parasite(gimage, gimp_image_find_parasite(gimage,
parasite_name(parasite)))) parasite_name(parasite))))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("attach parasite to image"));
parasite_list_add (gimage->parasites, parasite); parasite_list_add (gimage->parasites, parasite);
@ -1092,7 +1097,7 @@ gimp_image_detach_parasite (GimpImage *gimage,
if (parasite_is_undoable (p)) if (parasite_is_undoable (p))
undo_push_image_parasite_remove (gimage, parasite_name (p)); undo_push_image_parasite_remove (gimage, parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("detach parasite from image"));
parasite_list_remove (gimage->parasites, parasite); parasite_list_remove (gimage->parasites, parasite);
} }
@ -2089,7 +2094,9 @@ gimp_image_raise_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); /* Dirty the image, but we're too lazy to provide a
* proper undo. */
undo_push_cantundo (gimage, _("raise layer"));
return prev_layer; return prev_layer;
} }
@ -2163,7 +2170,7 @@ gimp_image_lower_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer"));
return next_layer; return next_layer;
} }
@ -2246,7 +2253,7 @@ gimp_image_raise_layer_to_top (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("raise layer to top"));
return layer; return layer;
} }
@ -2341,7 +2348,7 @@ gimp_image_lower_layer_to_bottom (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer to bottom"));
return layer_arg; return layer_arg;
} }
@ -2420,7 +2427,7 @@ gimp_image_position_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("re-position layer"));
return layer_arg; return layer_arg;
} }
@ -3340,12 +3347,40 @@ gimp_image_enable_undo (GimpImage *gimage)
return gimp_image_thaw_undo (gimage); return gimp_image_thaw_undo (gimage);
} }
/* NOTE about the gimage->dirty counter:
* If 0, then the image is clean (ie, copy on disk is the same as the one
* in memory).
* If positive, then that's the number of dirtying operations done
* on the image since the last save.
* If negative, then user has hit undo and gone back in time prior
* to the saved copy. Hitting redo will eventually come back to
* the saved copy.
*
* The image is dirty (ie, needs saving) if counter is non-zero.
*
* If the counter is around 10000, this is due to undo-ing back
* before a saved version, then mutating the image (thus destroying
* the redo stack). Once this has happened, it's impossible to get
* the image back to the state on disk, since the redo info has been
* freed. See undo.c for the gorey details.
*/
/*
* NEVER CALL gimp_image_dirty() directly!
*
* If your code has just dirtied the image, push an undo instead.
* Failing that, push the trivial undo which tells the user the
* command is not undoable: undo_push_cantundo() (But really, it would
* be best to push a proper undo). If you just dirty the image
* without pushing an undo then the dirty count is increased, but
* popping that many undo actions won't lead to a clean image.
*/
gint gint
gimp_image_dirty (GimpImage *gimage) gimp_image_dirty (GimpImage *gimage)
{ {
/* if (gimage->dirty < 0)
gimage->dirty = 2;
else */
gimage->dirty++; gimage->dirty++;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]); gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]);
@ -3355,10 +3390,9 @@ gimp_image_dirty (GimpImage *gimage)
gint gint
gimp_image_clean (GimpImage *gimage) gimp_image_clean (GimpImage *gimage)
{ {
/* if (gimage->dirty <= 0)
gimage->dirty = 0;
else */
gimage->dirty--; gimage->dirty--;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
return gimage->dirty; return gimage->dirty;
} }
@ -3366,6 +3400,8 @@ void
gimp_image_clean_all (GimpImage *gimage) gimp_image_clean_all (GimpImage *gimage)
{ {
gimage->dirty = 0; gimage->dirty = 0;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
} }
Layer * Layer *

View File

@ -91,6 +91,7 @@ guint32 next_guide_id = 1; /* For generating guide_ID handles for PDB stuff */
*/ */
enum { enum {
CLEAN,
DIRTY, DIRTY,
REPAINT, REPAINT,
RENAME, RENAME,
@ -118,6 +119,9 @@ gimp_image_class_init (GimpImageClass *klass)
object_class->destroy = gimp_image_destroy; object_class->destroy = gimp_image_destroy;
gimp_image_signals[CLEAN] =
gimp_signal_new ("clean", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void);
gimp_image_signals[DIRTY] = gimp_image_signals[DIRTY] =
gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0, gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void); gimp_sigtype_void);
@ -165,6 +169,7 @@ gimp_image_init (GimpImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 0; gimage->undo_levels = 0;
gimage->group_count = 0;
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[0] = FALSE;
gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[1] = FALSE;
@ -1069,7 +1074,7 @@ gimp_image_attach_parasite (GimpImage *gimage,
&& !parasite_compare (parasite, && !parasite_compare (parasite,
gimp_image_find_parasite(gimage, gimp_image_find_parasite(gimage,
parasite_name(parasite)))) parasite_name(parasite))))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("attach parasite to image"));
parasite_list_add (gimage->parasites, parasite); parasite_list_add (gimage->parasites, parasite);
@ -1092,7 +1097,7 @@ gimp_image_detach_parasite (GimpImage *gimage,
if (parasite_is_undoable (p)) if (parasite_is_undoable (p))
undo_push_image_parasite_remove (gimage, parasite_name (p)); undo_push_image_parasite_remove (gimage, parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("detach parasite from image"));
parasite_list_remove (gimage->parasites, parasite); parasite_list_remove (gimage->parasites, parasite);
} }
@ -2089,7 +2094,9 @@ gimp_image_raise_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); /* Dirty the image, but we're too lazy to provide a
* proper undo. */
undo_push_cantundo (gimage, _("raise layer"));
return prev_layer; return prev_layer;
} }
@ -2163,7 +2170,7 @@ gimp_image_lower_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer"));
return next_layer; return next_layer;
} }
@ -2246,7 +2253,7 @@ gimp_image_raise_layer_to_top (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("raise layer to top"));
return layer; return layer;
} }
@ -2341,7 +2348,7 @@ gimp_image_lower_layer_to_bottom (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer to bottom"));
return layer_arg; return layer_arg;
} }
@ -2420,7 +2427,7 @@ gimp_image_position_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("re-position layer"));
return layer_arg; return layer_arg;
} }
@ -3340,12 +3347,40 @@ gimp_image_enable_undo (GimpImage *gimage)
return gimp_image_thaw_undo (gimage); return gimp_image_thaw_undo (gimage);
} }
/* NOTE about the gimage->dirty counter:
* If 0, then the image is clean (ie, copy on disk is the same as the one
* in memory).
* If positive, then that's the number of dirtying operations done
* on the image since the last save.
* If negative, then user has hit undo and gone back in time prior
* to the saved copy. Hitting redo will eventually come back to
* the saved copy.
*
* The image is dirty (ie, needs saving) if counter is non-zero.
*
* If the counter is around 10000, this is due to undo-ing back
* before a saved version, then mutating the image (thus destroying
* the redo stack). Once this has happened, it's impossible to get
* the image back to the state on disk, since the redo info has been
* freed. See undo.c for the gorey details.
*/
/*
* NEVER CALL gimp_image_dirty() directly!
*
* If your code has just dirtied the image, push an undo instead.
* Failing that, push the trivial undo which tells the user the
* command is not undoable: undo_push_cantundo() (But really, it would
* be best to push a proper undo). If you just dirty the image
* without pushing an undo then the dirty count is increased, but
* popping that many undo actions won't lead to a clean image.
*/
gint gint
gimp_image_dirty (GimpImage *gimage) gimp_image_dirty (GimpImage *gimage)
{ {
/* if (gimage->dirty < 0)
gimage->dirty = 2;
else */
gimage->dirty++; gimage->dirty++;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]); gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]);
@ -3355,10 +3390,9 @@ gimp_image_dirty (GimpImage *gimage)
gint gint
gimp_image_clean (GimpImage *gimage) gimp_image_clean (GimpImage *gimage)
{ {
/* if (gimage->dirty <= 0)
gimage->dirty = 0;
else */
gimage->dirty--; gimage->dirty--;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
return gimage->dirty; return gimage->dirty;
} }
@ -3366,6 +3400,8 @@ void
gimp_image_clean_all (GimpImage *gimage) gimp_image_clean_all (GimpImage *gimage)
{ {
gimage->dirty = 0; gimage->dirty = 0;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
} }
Layer * Layer *

View File

@ -91,6 +91,7 @@ guint32 next_guide_id = 1; /* For generating guide_ID handles for PDB stuff */
*/ */
enum { enum {
CLEAN,
DIRTY, DIRTY,
REPAINT, REPAINT,
RENAME, RENAME,
@ -118,6 +119,9 @@ gimp_image_class_init (GimpImageClass *klass)
object_class->destroy = gimp_image_destroy; object_class->destroy = gimp_image_destroy;
gimp_image_signals[CLEAN] =
gimp_signal_new ("clean", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void);
gimp_image_signals[DIRTY] = gimp_image_signals[DIRTY] =
gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0, gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void); gimp_sigtype_void);
@ -165,6 +169,7 @@ gimp_image_init (GimpImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 0; gimage->undo_levels = 0;
gimage->group_count = 0;
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[0] = FALSE;
gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[1] = FALSE;
@ -1069,7 +1074,7 @@ gimp_image_attach_parasite (GimpImage *gimage,
&& !parasite_compare (parasite, && !parasite_compare (parasite,
gimp_image_find_parasite(gimage, gimp_image_find_parasite(gimage,
parasite_name(parasite)))) parasite_name(parasite))))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("attach parasite to image"));
parasite_list_add (gimage->parasites, parasite); parasite_list_add (gimage->parasites, parasite);
@ -1092,7 +1097,7 @@ gimp_image_detach_parasite (GimpImage *gimage,
if (parasite_is_undoable (p)) if (parasite_is_undoable (p))
undo_push_image_parasite_remove (gimage, parasite_name (p)); undo_push_image_parasite_remove (gimage, parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("detach parasite from image"));
parasite_list_remove (gimage->parasites, parasite); parasite_list_remove (gimage->parasites, parasite);
} }
@ -2089,7 +2094,9 @@ gimp_image_raise_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); /* Dirty the image, but we're too lazy to provide a
* proper undo. */
undo_push_cantundo (gimage, _("raise layer"));
return prev_layer; return prev_layer;
} }
@ -2163,7 +2170,7 @@ gimp_image_lower_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer"));
return next_layer; return next_layer;
} }
@ -2246,7 +2253,7 @@ gimp_image_raise_layer_to_top (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("raise layer to top"));
return layer; return layer;
} }
@ -2341,7 +2348,7 @@ gimp_image_lower_layer_to_bottom (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer to bottom"));
return layer_arg; return layer_arg;
} }
@ -2420,7 +2427,7 @@ gimp_image_position_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("re-position layer"));
return layer_arg; return layer_arg;
} }
@ -3340,12 +3347,40 @@ gimp_image_enable_undo (GimpImage *gimage)
return gimp_image_thaw_undo (gimage); return gimp_image_thaw_undo (gimage);
} }
/* NOTE about the gimage->dirty counter:
* If 0, then the image is clean (ie, copy on disk is the same as the one
* in memory).
* If positive, then that's the number of dirtying operations done
* on the image since the last save.
* If negative, then user has hit undo and gone back in time prior
* to the saved copy. Hitting redo will eventually come back to
* the saved copy.
*
* The image is dirty (ie, needs saving) if counter is non-zero.
*
* If the counter is around 10000, this is due to undo-ing back
* before a saved version, then mutating the image (thus destroying
* the redo stack). Once this has happened, it's impossible to get
* the image back to the state on disk, since the redo info has been
* freed. See undo.c for the gorey details.
*/
/*
* NEVER CALL gimp_image_dirty() directly!
*
* If your code has just dirtied the image, push an undo instead.
* Failing that, push the trivial undo which tells the user the
* command is not undoable: undo_push_cantundo() (But really, it would
* be best to push a proper undo). If you just dirty the image
* without pushing an undo then the dirty count is increased, but
* popping that many undo actions won't lead to a clean image.
*/
gint gint
gimp_image_dirty (GimpImage *gimage) gimp_image_dirty (GimpImage *gimage)
{ {
/* if (gimage->dirty < 0)
gimage->dirty = 2;
else */
gimage->dirty++; gimage->dirty++;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]); gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]);
@ -3355,10 +3390,9 @@ gimp_image_dirty (GimpImage *gimage)
gint gint
gimp_image_clean (GimpImage *gimage) gimp_image_clean (GimpImage *gimage)
{ {
/* if (gimage->dirty <= 0)
gimage->dirty = 0;
else */
gimage->dirty--; gimage->dirty--;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
return gimage->dirty; return gimage->dirty;
} }
@ -3366,6 +3400,8 @@ void
gimp_image_clean_all (GimpImage *gimage) gimp_image_clean_all (GimpImage *gimage)
{ {
gimage->dirty = 0; gimage->dirty = 0;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
} }
Layer * Layer *

View File

@ -91,6 +91,7 @@ guint32 next_guide_id = 1; /* For generating guide_ID handles for PDB stuff */
*/ */
enum { enum {
CLEAN,
DIRTY, DIRTY,
REPAINT, REPAINT,
RENAME, RENAME,
@ -118,6 +119,9 @@ gimp_image_class_init (GimpImageClass *klass)
object_class->destroy = gimp_image_destroy; object_class->destroy = gimp_image_destroy;
gimp_image_signals[CLEAN] =
gimp_signal_new ("clean", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void);
gimp_image_signals[DIRTY] = gimp_image_signals[DIRTY] =
gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0, gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void); gimp_sigtype_void);
@ -165,6 +169,7 @@ gimp_image_init (GimpImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 0; gimage->undo_levels = 0;
gimage->group_count = 0;
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[0] = FALSE;
gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[1] = FALSE;
@ -1069,7 +1074,7 @@ gimp_image_attach_parasite (GimpImage *gimage,
&& !parasite_compare (parasite, && !parasite_compare (parasite,
gimp_image_find_parasite(gimage, gimp_image_find_parasite(gimage,
parasite_name(parasite)))) parasite_name(parasite))))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("attach parasite to image"));
parasite_list_add (gimage->parasites, parasite); parasite_list_add (gimage->parasites, parasite);
@ -1092,7 +1097,7 @@ gimp_image_detach_parasite (GimpImage *gimage,
if (parasite_is_undoable (p)) if (parasite_is_undoable (p))
undo_push_image_parasite_remove (gimage, parasite_name (p)); undo_push_image_parasite_remove (gimage, parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("detach parasite from image"));
parasite_list_remove (gimage->parasites, parasite); parasite_list_remove (gimage->parasites, parasite);
} }
@ -2089,7 +2094,9 @@ gimp_image_raise_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); /* Dirty the image, but we're too lazy to provide a
* proper undo. */
undo_push_cantundo (gimage, _("raise layer"));
return prev_layer; return prev_layer;
} }
@ -2163,7 +2170,7 @@ gimp_image_lower_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer"));
return next_layer; return next_layer;
} }
@ -2246,7 +2253,7 @@ gimp_image_raise_layer_to_top (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("raise layer to top"));
return layer; return layer;
} }
@ -2341,7 +2348,7 @@ gimp_image_lower_layer_to_bottom (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer to bottom"));
return layer_arg; return layer_arg;
} }
@ -2420,7 +2427,7 @@ gimp_image_position_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("re-position layer"));
return layer_arg; return layer_arg;
} }
@ -3340,12 +3347,40 @@ gimp_image_enable_undo (GimpImage *gimage)
return gimp_image_thaw_undo (gimage); return gimp_image_thaw_undo (gimage);
} }
/* NOTE about the gimage->dirty counter:
* If 0, then the image is clean (ie, copy on disk is the same as the one
* in memory).
* If positive, then that's the number of dirtying operations done
* on the image since the last save.
* If negative, then user has hit undo and gone back in time prior
* to the saved copy. Hitting redo will eventually come back to
* the saved copy.
*
* The image is dirty (ie, needs saving) if counter is non-zero.
*
* If the counter is around 10000, this is due to undo-ing back
* before a saved version, then mutating the image (thus destroying
* the redo stack). Once this has happened, it's impossible to get
* the image back to the state on disk, since the redo info has been
* freed. See undo.c for the gorey details.
*/
/*
* NEVER CALL gimp_image_dirty() directly!
*
* If your code has just dirtied the image, push an undo instead.
* Failing that, push the trivial undo which tells the user the
* command is not undoable: undo_push_cantundo() (But really, it would
* be best to push a proper undo). If you just dirty the image
* without pushing an undo then the dirty count is increased, but
* popping that many undo actions won't lead to a clean image.
*/
gint gint
gimp_image_dirty (GimpImage *gimage) gimp_image_dirty (GimpImage *gimage)
{ {
/* if (gimage->dirty < 0)
gimage->dirty = 2;
else */
gimage->dirty++; gimage->dirty++;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]); gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]);
@ -3355,10 +3390,9 @@ gimp_image_dirty (GimpImage *gimage)
gint gint
gimp_image_clean (GimpImage *gimage) gimp_image_clean (GimpImage *gimage)
{ {
/* if (gimage->dirty <= 0)
gimage->dirty = 0;
else */
gimage->dirty--; gimage->dirty--;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
return gimage->dirty; return gimage->dirty;
} }
@ -3366,6 +3400,8 @@ void
gimp_image_clean_all (GimpImage *gimage) gimp_image_clean_all (GimpImage *gimage)
{ {
gimage->dirty = 0; gimage->dirty = 0;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
} }
Layer * Layer *

View File

@ -91,6 +91,7 @@ guint32 next_guide_id = 1; /* For generating guide_ID handles for PDB stuff */
*/ */
enum { enum {
CLEAN,
DIRTY, DIRTY,
REPAINT, REPAINT,
RENAME, RENAME,
@ -118,6 +119,9 @@ gimp_image_class_init (GimpImageClass *klass)
object_class->destroy = gimp_image_destroy; object_class->destroy = gimp_image_destroy;
gimp_image_signals[CLEAN] =
gimp_signal_new ("clean", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void);
gimp_image_signals[DIRTY] = gimp_image_signals[DIRTY] =
gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0, gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void); gimp_sigtype_void);
@ -165,6 +169,7 @@ gimp_image_init (GimpImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 0; gimage->undo_levels = 0;
gimage->group_count = 0;
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[0] = FALSE;
gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[1] = FALSE;
@ -1069,7 +1074,7 @@ gimp_image_attach_parasite (GimpImage *gimage,
&& !parasite_compare (parasite, && !parasite_compare (parasite,
gimp_image_find_parasite(gimage, gimp_image_find_parasite(gimage,
parasite_name(parasite)))) parasite_name(parasite))))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("attach parasite to image"));
parasite_list_add (gimage->parasites, parasite); parasite_list_add (gimage->parasites, parasite);
@ -1092,7 +1097,7 @@ gimp_image_detach_parasite (GimpImage *gimage,
if (parasite_is_undoable (p)) if (parasite_is_undoable (p))
undo_push_image_parasite_remove (gimage, parasite_name (p)); undo_push_image_parasite_remove (gimage, parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("detach parasite from image"));
parasite_list_remove (gimage->parasites, parasite); parasite_list_remove (gimage->parasites, parasite);
} }
@ -2089,7 +2094,9 @@ gimp_image_raise_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); /* Dirty the image, but we're too lazy to provide a
* proper undo. */
undo_push_cantundo (gimage, _("raise layer"));
return prev_layer; return prev_layer;
} }
@ -2163,7 +2170,7 @@ gimp_image_lower_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer"));
return next_layer; return next_layer;
} }
@ -2246,7 +2253,7 @@ gimp_image_raise_layer_to_top (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("raise layer to top"));
return layer; return layer;
} }
@ -2341,7 +2348,7 @@ gimp_image_lower_layer_to_bottom (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer to bottom"));
return layer_arg; return layer_arg;
} }
@ -2420,7 +2427,7 @@ gimp_image_position_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("re-position layer"));
return layer_arg; return layer_arg;
} }
@ -3340,12 +3347,40 @@ gimp_image_enable_undo (GimpImage *gimage)
return gimp_image_thaw_undo (gimage); return gimp_image_thaw_undo (gimage);
} }
/* NOTE about the gimage->dirty counter:
* If 0, then the image is clean (ie, copy on disk is the same as the one
* in memory).
* If positive, then that's the number of dirtying operations done
* on the image since the last save.
* If negative, then user has hit undo and gone back in time prior
* to the saved copy. Hitting redo will eventually come back to
* the saved copy.
*
* The image is dirty (ie, needs saving) if counter is non-zero.
*
* If the counter is around 10000, this is due to undo-ing back
* before a saved version, then mutating the image (thus destroying
* the redo stack). Once this has happened, it's impossible to get
* the image back to the state on disk, since the redo info has been
* freed. See undo.c for the gorey details.
*/
/*
* NEVER CALL gimp_image_dirty() directly!
*
* If your code has just dirtied the image, push an undo instead.
* Failing that, push the trivial undo which tells the user the
* command is not undoable: undo_push_cantundo() (But really, it would
* be best to push a proper undo). If you just dirty the image
* without pushing an undo then the dirty count is increased, but
* popping that many undo actions won't lead to a clean image.
*/
gint gint
gimp_image_dirty (GimpImage *gimage) gimp_image_dirty (GimpImage *gimage)
{ {
/* if (gimage->dirty < 0)
gimage->dirty = 2;
else */
gimage->dirty++; gimage->dirty++;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]); gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]);
@ -3355,10 +3390,9 @@ gimp_image_dirty (GimpImage *gimage)
gint gint
gimp_image_clean (GimpImage *gimage) gimp_image_clean (GimpImage *gimage)
{ {
/* if (gimage->dirty <= 0)
gimage->dirty = 0;
else */
gimage->dirty--; gimage->dirty--;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
return gimage->dirty; return gimage->dirty;
} }
@ -3366,6 +3400,8 @@ void
gimp_image_clean_all (GimpImage *gimage) gimp_image_clean_all (GimpImage *gimage)
{ {
gimage->dirty = 0; gimage->dirty = 0;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
} }
Layer * Layer *

View File

@ -42,6 +42,7 @@
#include "tile.h" /* ick. */ #include "tile.h" /* ick. */
#include "libgimp/parasite.h" #include "libgimp/parasite.h"
#include "libgimp/gimpintl.h"
#include "gimpparasite.h" #include "gimpparasite.h"
@ -80,6 +81,9 @@ int undo_pop_gimage_mod (GImage *, int, int, void *);
int undo_pop_guide (GImage *, int, int, void *); int undo_pop_guide (GImage *, int, int, void *);
int undo_pop_parasite (GImage *, int, int, void *); int undo_pop_parasite (GImage *, int, int, void *);
int undo_pop_qmask (GImage *, int, int, void *); int undo_pop_qmask (GImage *, int, int, void *);
static int undo_pop_layer_rename (GImage *, int, int, void *);
static int undo_pop_cantundo (GImage *, int, int, void *);
/* Free functions */ /* Free functions */
@ -100,13 +104,14 @@ void undo_free_gimage_mod (int, void *);
void undo_free_guide (int, void *); void undo_free_guide (int, void *);
void undo_free_parasite (int, void *); void undo_free_parasite (int, void *);
void undo_free_qmask (int, void *); void undo_free_qmask (int, void *);
static void undo_free_layer_rename (int, void *);
static void undo_free_cantundo (int, void *);
/* Sizing functions */ /* Sizing functions */
static int layer_size (Layer *); static int layer_size (Layer *);
static int channel_size (Channel *); static int channel_size (Channel *);
static int group_count = 0;
static int shrink_wrap = FALSE; static int shrink_wrap = FALSE;
#define UNDO 0 #define UNDO 0
@ -249,6 +254,14 @@ undo_push (GImage *gimage,
{ {
undo_free_list (gimage, REDO, gimage->redo_stack); undo_free_list (gimage, REDO, gimage->redo_stack);
gimage->redo_stack = NULL; 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) if (!gimage->pushing_undo_group)
@ -384,6 +397,18 @@ undo_free (GImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 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. */
} }
@ -397,16 +422,23 @@ undo_push_group_start (GImage *gimage,
if (! gimage->undo_on) if (! gimage->undo_on)
return FALSE; return FALSE;
group_count ++; gimage->group_count ++;
/* If we're already in a group...ignore */ /* If we're already in a group...ignore */
if (group_count > 1) if (gimage->group_count > 1)
return TRUE; return TRUE;
if (gimage->redo_stack) if (gimage->redo_stack)
{ {
undo_free_list (gimage, REDO, gimage->redo_stack); undo_free_list (gimage, REDO, gimage->redo_stack);
gimage->redo_stack = NULL; 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)) if (! undo_free_up_space (gimage))
@ -426,9 +458,9 @@ undo_push_group_end (GImage *gimage)
if (! gimage->undo_on) if (! gimage->undo_on)
return FALSE; return FALSE;
group_count --; gimage->group_count --;
if (group_count == 0) if (gimage->group_count == 0)
{ {
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->undo_stack = g_slist_prepend (gimage->undo_stack, NULL); gimage->undo_stack = g_slist_prepend (gimage->undo_stack, NULL);
@ -2423,3 +2455,134 @@ undo_free_parasite (int state,
g_free (data_ptr); g_free (data_ptr);
} }
/*************/
/* Layer name change */
typedef struct {
Layer *layer;
gchar *old_name;
} LayerRenameUndo;
int
undo_push_layer_rename (GImage *gimage, Layer *layer)
{
Undo *new;
LayerRenameUndo *data;
long size;
/* increment the dirty flag for this gimage */
gimage_dirty (gimage);
size = sizeof (LayerRenameUndo);
if ((new = undo_push (gimage, size, LAYER_CHANGE)))
{
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 (layer_get_name (layer));
return TRUE;
}
return FALSE;
}
static int
undo_pop_layer_rename (GImage *gimage,
int state,
int type,
void *data_ptr)
{
LayerRenameUndo *data = data_ptr;
gchar *tmp;
tmp = g_strdup (layer_get_name (data->layer));
layer_set_name (data->layer, data->old_name);
g_free (data->old_name);
data->old_name = tmp;
switch (state) {
case UNDO:
gimp_image_clean (gimage);
break;
case REDO:
gimp_image_dirty (gimage);
break;
}
return TRUE;
}
static void
undo_free_layer_rename (int state,
void *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.
*/
int
undo_push_cantundo (GImage *gimage,
const char *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. */
gimp_image_dirty (gimage);
new = undo_push (gimage, 0, GIMAGE_MOD);
if (!new)
return FALSE;
new->data = (void*)action;
new->pop_func = undo_pop_cantundo;
new->free_func = undo_free_cantundo;
return TRUE;
}
static int
undo_pop_cantundo (GImage *gimage,
int state,
int type,
void *data_ptr)
{
char *action = data_ptr;
switch (state) {
case UNDO:
g_message (_("Can't undo %s"), action);
gimp_image_clean (gimage);
break;
case REDO:
gimp_image_dirty (gimage);
break;
}
return TRUE;
}
static void
undo_free_cantundo (int state, void *data_ptr)
{
}

View File

@ -86,6 +86,8 @@ int undo_push_image_parasite_remove (GImage *, const char *);
int undo_push_drawable_parasite_remove (GImage *, GimpDrawable *, int undo_push_drawable_parasite_remove (GImage *, GimpDrawable *,
const char *); const char *);
int undo_push_qmask (GImage *, int); int undo_push_qmask (GImage *, int);
int undo_push_layer_rename (GImage *, Layer *);
int undo_push_cantundo (GImage *, const char *);
int undo_pop (GImage *); int undo_pop (GImage *);
int undo_redo (GImage *); int undo_redo (GImage *);

View File

@ -91,6 +91,7 @@ guint32 next_guide_id = 1; /* For generating guide_ID handles for PDB stuff */
*/ */
enum { enum {
CLEAN,
DIRTY, DIRTY,
REPAINT, REPAINT,
RENAME, RENAME,
@ -118,6 +119,9 @@ gimp_image_class_init (GimpImageClass *klass)
object_class->destroy = gimp_image_destroy; object_class->destroy = gimp_image_destroy;
gimp_image_signals[CLEAN] =
gimp_signal_new ("clean", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void);
gimp_image_signals[DIRTY] = gimp_image_signals[DIRTY] =
gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0, gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void); gimp_sigtype_void);
@ -165,6 +169,7 @@ gimp_image_init (GimpImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 0; gimage->undo_levels = 0;
gimage->group_count = 0;
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[0] = FALSE;
gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[1] = FALSE;
@ -1069,7 +1074,7 @@ gimp_image_attach_parasite (GimpImage *gimage,
&& !parasite_compare (parasite, && !parasite_compare (parasite,
gimp_image_find_parasite(gimage, gimp_image_find_parasite(gimage,
parasite_name(parasite)))) parasite_name(parasite))))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("attach parasite to image"));
parasite_list_add (gimage->parasites, parasite); parasite_list_add (gimage->parasites, parasite);
@ -1092,7 +1097,7 @@ gimp_image_detach_parasite (GimpImage *gimage,
if (parasite_is_undoable (p)) if (parasite_is_undoable (p))
undo_push_image_parasite_remove (gimage, parasite_name (p)); undo_push_image_parasite_remove (gimage, parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("detach parasite from image"));
parasite_list_remove (gimage->parasites, parasite); parasite_list_remove (gimage->parasites, parasite);
} }
@ -2089,7 +2094,9 @@ gimp_image_raise_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); /* Dirty the image, but we're too lazy to provide a
* proper undo. */
undo_push_cantundo (gimage, _("raise layer"));
return prev_layer; return prev_layer;
} }
@ -2163,7 +2170,7 @@ gimp_image_lower_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer"));
return next_layer; return next_layer;
} }
@ -2246,7 +2253,7 @@ gimp_image_raise_layer_to_top (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("raise layer to top"));
return layer; return layer;
} }
@ -2341,7 +2348,7 @@ gimp_image_lower_layer_to_bottom (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer to bottom"));
return layer_arg; return layer_arg;
} }
@ -2420,7 +2427,7 @@ gimp_image_position_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("re-position layer"));
return layer_arg; return layer_arg;
} }
@ -3340,12 +3347,40 @@ gimp_image_enable_undo (GimpImage *gimage)
return gimp_image_thaw_undo (gimage); return gimp_image_thaw_undo (gimage);
} }
/* NOTE about the gimage->dirty counter:
* If 0, then the image is clean (ie, copy on disk is the same as the one
* in memory).
* If positive, then that's the number of dirtying operations done
* on the image since the last save.
* If negative, then user has hit undo and gone back in time prior
* to the saved copy. Hitting redo will eventually come back to
* the saved copy.
*
* The image is dirty (ie, needs saving) if counter is non-zero.
*
* If the counter is around 10000, this is due to undo-ing back
* before a saved version, then mutating the image (thus destroying
* the redo stack). Once this has happened, it's impossible to get
* the image back to the state on disk, since the redo info has been
* freed. See undo.c for the gorey details.
*/
/*
* NEVER CALL gimp_image_dirty() directly!
*
* If your code has just dirtied the image, push an undo instead.
* Failing that, push the trivial undo which tells the user the
* command is not undoable: undo_push_cantundo() (But really, it would
* be best to push a proper undo). If you just dirty the image
* without pushing an undo then the dirty count is increased, but
* popping that many undo actions won't lead to a clean image.
*/
gint gint
gimp_image_dirty (GimpImage *gimage) gimp_image_dirty (GimpImage *gimage)
{ {
/* if (gimage->dirty < 0)
gimage->dirty = 2;
else */
gimage->dirty++; gimage->dirty++;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]); gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]);
@ -3355,10 +3390,9 @@ gimp_image_dirty (GimpImage *gimage)
gint gint
gimp_image_clean (GimpImage *gimage) gimp_image_clean (GimpImage *gimage)
{ {
/* if (gimage->dirty <= 0)
gimage->dirty = 0;
else */
gimage->dirty--; gimage->dirty--;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
return gimage->dirty; return gimage->dirty;
} }
@ -3366,6 +3400,8 @@ void
gimp_image_clean_all (GimpImage *gimage) gimp_image_clean_all (GimpImage *gimage)
{ {
gimage->dirty = 0; gimage->dirty = 0;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
} }
Layer * Layer *

View File

@ -91,6 +91,7 @@ guint32 next_guide_id = 1; /* For generating guide_ID handles for PDB stuff */
*/ */
enum { enum {
CLEAN,
DIRTY, DIRTY,
REPAINT, REPAINT,
RENAME, RENAME,
@ -118,6 +119,9 @@ gimp_image_class_init (GimpImageClass *klass)
object_class->destroy = gimp_image_destroy; object_class->destroy = gimp_image_destroy;
gimp_image_signals[CLEAN] =
gimp_signal_new ("clean", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void);
gimp_image_signals[DIRTY] = gimp_image_signals[DIRTY] =
gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0, gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void); gimp_sigtype_void);
@ -165,6 +169,7 @@ gimp_image_init (GimpImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 0; gimage->undo_levels = 0;
gimage->group_count = 0;
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[0] = FALSE;
gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[1] = FALSE;
@ -1069,7 +1074,7 @@ gimp_image_attach_parasite (GimpImage *gimage,
&& !parasite_compare (parasite, && !parasite_compare (parasite,
gimp_image_find_parasite(gimage, gimp_image_find_parasite(gimage,
parasite_name(parasite)))) parasite_name(parasite))))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("attach parasite to image"));
parasite_list_add (gimage->parasites, parasite); parasite_list_add (gimage->parasites, parasite);
@ -1092,7 +1097,7 @@ gimp_image_detach_parasite (GimpImage *gimage,
if (parasite_is_undoable (p)) if (parasite_is_undoable (p))
undo_push_image_parasite_remove (gimage, parasite_name (p)); undo_push_image_parasite_remove (gimage, parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("detach parasite from image"));
parasite_list_remove (gimage->parasites, parasite); parasite_list_remove (gimage->parasites, parasite);
} }
@ -2089,7 +2094,9 @@ gimp_image_raise_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); /* Dirty the image, but we're too lazy to provide a
* proper undo. */
undo_push_cantundo (gimage, _("raise layer"));
return prev_layer; return prev_layer;
} }
@ -2163,7 +2170,7 @@ gimp_image_lower_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer"));
return next_layer; return next_layer;
} }
@ -2246,7 +2253,7 @@ gimp_image_raise_layer_to_top (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("raise layer to top"));
return layer; return layer;
} }
@ -2341,7 +2348,7 @@ gimp_image_lower_layer_to_bottom (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer to bottom"));
return layer_arg; return layer_arg;
} }
@ -2420,7 +2427,7 @@ gimp_image_position_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("re-position layer"));
return layer_arg; return layer_arg;
} }
@ -3340,12 +3347,40 @@ gimp_image_enable_undo (GimpImage *gimage)
return gimp_image_thaw_undo (gimage); return gimp_image_thaw_undo (gimage);
} }
/* NOTE about the gimage->dirty counter:
* If 0, then the image is clean (ie, copy on disk is the same as the one
* in memory).
* If positive, then that's the number of dirtying operations done
* on the image since the last save.
* If negative, then user has hit undo and gone back in time prior
* to the saved copy. Hitting redo will eventually come back to
* the saved copy.
*
* The image is dirty (ie, needs saving) if counter is non-zero.
*
* If the counter is around 10000, this is due to undo-ing back
* before a saved version, then mutating the image (thus destroying
* the redo stack). Once this has happened, it's impossible to get
* the image back to the state on disk, since the redo info has been
* freed. See undo.c for the gorey details.
*/
/*
* NEVER CALL gimp_image_dirty() directly!
*
* If your code has just dirtied the image, push an undo instead.
* Failing that, push the trivial undo which tells the user the
* command is not undoable: undo_push_cantundo() (But really, it would
* be best to push a proper undo). If you just dirty the image
* without pushing an undo then the dirty count is increased, but
* popping that many undo actions won't lead to a clean image.
*/
gint gint
gimp_image_dirty (GimpImage *gimage) gimp_image_dirty (GimpImage *gimage)
{ {
/* if (gimage->dirty < 0)
gimage->dirty = 2;
else */
gimage->dirty++; gimage->dirty++;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]); gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]);
@ -3355,10 +3390,9 @@ gimp_image_dirty (GimpImage *gimage)
gint gint
gimp_image_clean (GimpImage *gimage) gimp_image_clean (GimpImage *gimage)
{ {
/* if (gimage->dirty <= 0)
gimage->dirty = 0;
else */
gimage->dirty--; gimage->dirty--;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
return gimage->dirty; return gimage->dirty;
} }
@ -3366,6 +3400,8 @@ void
gimp_image_clean_all (GimpImage *gimage) gimp_image_clean_all (GimpImage *gimage)
{ {
gimage->dirty = 0; gimage->dirty = 0;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
} }
Layer * Layer *

View File

@ -70,6 +70,7 @@ static void gdisplay_paint_area (GDisplay *, int, int, int, int);
static void gdisplay_draw_cursor (GDisplay *); static void gdisplay_draw_cursor (GDisplay *);
static void gdisplay_display_area (GDisplay *, int, int, int, int); static void gdisplay_display_area (GDisplay *, int, int, int, int);
static guint gdisplay_hash (GDisplay *); static guint gdisplay_hash (GDisplay *);
static void gdisplay_cleandirty_handler (GimpImage *, void *);
static GHashTable *display_ht = NULL; static GHashTable *display_ht = NULL;
@ -155,6 +156,13 @@ gdisplay_new (GimpImage *gimage,
lc_dialog_preview_update(gimage); lc_dialog_preview_update(gimage);
/* We're interested in clean and dirty signals so we can update the
* title if need be. */
gtk_signal_connect (GTK_OBJECT (gimage), "dirty",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gtk_signal_connect (GTK_OBJECT (gimage), "clean",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
return gdisp; return gdisp;
} }
@ -259,7 +267,18 @@ gdisplay_format_title (GDisplay *gdisp,
"%d", 100 * SCALEDEST (gdisp) / SCALESRC (gdisp)); "%d", 100 * SCALEDEST (gdisp) / SCALESRC (gdisp));
break; break;
/* other cool things to be added: case 'D': /* dirty flag */
if (format[1] == 0)
{
g_warning("image-title-format string ended within %%D-sequence");
break;
}
if (gimage->dirty)
title[i++] = format[1];
format++;
break;
/* Other cool things to be added:
* %m = memory used by picture * %m = memory used by picture
* some kind of resolution / image size thing * some kind of resolution / image size thing
* people seem to want to know the active layer name * people seem to want to know the active layer name
@ -313,6 +332,9 @@ gdisplay_delete (GDisplay *gdisp)
gdisp->idle_render.active = FALSE; gdisp->idle_render.active = FALSE;
} }
/* get rid of signals handled by this display */
gtk_signal_disconnect_by_data (GTK_OBJECT (gdisp->gimage), gdisp);
if (gdisp->scroll_gc) if (gdisp->scroll_gc)
gdk_gc_destroy (gdisp->scroll_gc); gdk_gc_destroy (gdisp->scroll_gc);
@ -2042,7 +2064,7 @@ gdisplays_dirty ()
/* traverse the linked list of displays */ /* traverse the linked list of displays */
while (list) while (list)
{ {
if (((GDisplay *) list->data)->gimage->dirty > 0) if (((GDisplay *) list->data)->gimage->dirty != 0)
dirty = 1; dirty = 1;
list = g_slist_next (list); list = g_slist_next (list);
} }
@ -2158,6 +2180,7 @@ gdisplay_reconnect (GDisplay *gdisp, GimpImage *gimage)
gdisp->idle_render.active = FALSE; gdisp->idle_render.active = FALSE;
} }
gtk_signal_disconnect_by_data (GTK_OBJECT (gdisp->gimage), gdisp);
gimage_delete (gdisp->gimage); gimage_delete (gdisp->gimage);
instance = gimage->instance_count; instance = gimage->instance_count;
@ -2167,8 +2190,24 @@ gdisplay_reconnect (GDisplay *gdisp, GimpImage *gimage)
gdisp->gimage = gimage; gdisp->gimage = gimage;
gdisp->instance = instance; gdisp->instance = instance;
/* reconnect our clean / dirty signals */
gtk_signal_connect (GTK_OBJECT (gimage), "dirty",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gtk_signal_connect (GTK_OBJECT (gimage), "clean",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gdisplays_update_title (gimage); gdisplays_update_title (gimage);
gdisplay_expose_full (gdisp); gdisplay_expose_full (gdisp);
gdisplay_flush (gdisp); gdisplay_flush (gdisp);
} }
/* Called whenever the underlying gimage is dirtied or cleaned */
static void
gdisplay_cleandirty_handler (GimpImage *gimage, void *data)
{
GDisplay *gdisp = data;
gdisplay_update_title (gdisp);
}

View File

@ -120,7 +120,7 @@ gdisplay_close_window (GDisplay *gdisp,
* to an image canvas. (a gimage with ref_count = 1) * to an image canvas. (a gimage with ref_count = 1)
*/ */
if (!kill_it && (gdisp->gimage->ref_count == 1) && if (!kill_it && (gdisp->gimage->ref_count == 1) &&
(gdisp->gimage->dirty > 0) && confirm_on_close) gdisp->gimage->dirty && confirm_on_close)
{ {
gdisplay_close_warning_dialog gdisplay_close_warning_dialog
(g_basename (gimage_filename (gdisp->gimage)), gdisp); (g_basename (gimage_filename (gdisp->gimage)), gdisp);

View File

@ -70,6 +70,7 @@ static void gdisplay_paint_area (GDisplay *, int, int, int, int);
static void gdisplay_draw_cursor (GDisplay *); static void gdisplay_draw_cursor (GDisplay *);
static void gdisplay_display_area (GDisplay *, int, int, int, int); static void gdisplay_display_area (GDisplay *, int, int, int, int);
static guint gdisplay_hash (GDisplay *); static guint gdisplay_hash (GDisplay *);
static void gdisplay_cleandirty_handler (GimpImage *, void *);
static GHashTable *display_ht = NULL; static GHashTable *display_ht = NULL;
@ -155,6 +156,13 @@ gdisplay_new (GimpImage *gimage,
lc_dialog_preview_update(gimage); lc_dialog_preview_update(gimage);
/* We're interested in clean and dirty signals so we can update the
* title if need be. */
gtk_signal_connect (GTK_OBJECT (gimage), "dirty",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gtk_signal_connect (GTK_OBJECT (gimage), "clean",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
return gdisp; return gdisp;
} }
@ -259,7 +267,18 @@ gdisplay_format_title (GDisplay *gdisp,
"%d", 100 * SCALEDEST (gdisp) / SCALESRC (gdisp)); "%d", 100 * SCALEDEST (gdisp) / SCALESRC (gdisp));
break; break;
/* other cool things to be added: case 'D': /* dirty flag */
if (format[1] == 0)
{
g_warning("image-title-format string ended within %%D-sequence");
break;
}
if (gimage->dirty)
title[i++] = format[1];
format++;
break;
/* Other cool things to be added:
* %m = memory used by picture * %m = memory used by picture
* some kind of resolution / image size thing * some kind of resolution / image size thing
* people seem to want to know the active layer name * people seem to want to know the active layer name
@ -313,6 +332,9 @@ gdisplay_delete (GDisplay *gdisp)
gdisp->idle_render.active = FALSE; gdisp->idle_render.active = FALSE;
} }
/* get rid of signals handled by this display */
gtk_signal_disconnect_by_data (GTK_OBJECT (gdisp->gimage), gdisp);
if (gdisp->scroll_gc) if (gdisp->scroll_gc)
gdk_gc_destroy (gdisp->scroll_gc); gdk_gc_destroy (gdisp->scroll_gc);
@ -2042,7 +2064,7 @@ gdisplays_dirty ()
/* traverse the linked list of displays */ /* traverse the linked list of displays */
while (list) while (list)
{ {
if (((GDisplay *) list->data)->gimage->dirty > 0) if (((GDisplay *) list->data)->gimage->dirty != 0)
dirty = 1; dirty = 1;
list = g_slist_next (list); list = g_slist_next (list);
} }
@ -2158,6 +2180,7 @@ gdisplay_reconnect (GDisplay *gdisp, GimpImage *gimage)
gdisp->idle_render.active = FALSE; gdisp->idle_render.active = FALSE;
} }
gtk_signal_disconnect_by_data (GTK_OBJECT (gdisp->gimage), gdisp);
gimage_delete (gdisp->gimage); gimage_delete (gdisp->gimage);
instance = gimage->instance_count; instance = gimage->instance_count;
@ -2167,8 +2190,24 @@ gdisplay_reconnect (GDisplay *gdisp, GimpImage *gimage)
gdisp->gimage = gimage; gdisp->gimage = gimage;
gdisp->instance = instance; gdisp->instance = instance;
/* reconnect our clean / dirty signals */
gtk_signal_connect (GTK_OBJECT (gimage), "dirty",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gtk_signal_connect (GTK_OBJECT (gimage), "clean",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gdisplays_update_title (gimage); gdisplays_update_title (gimage);
gdisplay_expose_full (gdisp); gdisplay_expose_full (gdisp);
gdisplay_flush (gdisp); gdisplay_flush (gdisp);
} }
/* Called whenever the underlying gimage is dirtied or cleaned */
static void
gdisplay_cleandirty_handler (GimpImage *gimage, void *data)
{
GDisplay *gdisp = data;
gdisplay_update_title (gdisp);
}

View File

@ -535,6 +535,7 @@ file_revert_callback (GtkWidget *w,
{ {
undo_free (gimage); undo_free (gimage);
gdisplay_reconnect (gdisplay, gimage); gdisplay_reconnect (gdisplay, gimage);
gimp_image_clean_all (gimage);
} }
else else
g_message (_("Revert failed.")); g_message (_("Revert failed."));

View File

@ -70,6 +70,7 @@ static void gdisplay_paint_area (GDisplay *, int, int, int, int);
static void gdisplay_draw_cursor (GDisplay *); static void gdisplay_draw_cursor (GDisplay *);
static void gdisplay_display_area (GDisplay *, int, int, int, int); static void gdisplay_display_area (GDisplay *, int, int, int, int);
static guint gdisplay_hash (GDisplay *); static guint gdisplay_hash (GDisplay *);
static void gdisplay_cleandirty_handler (GimpImage *, void *);
static GHashTable *display_ht = NULL; static GHashTable *display_ht = NULL;
@ -155,6 +156,13 @@ gdisplay_new (GimpImage *gimage,
lc_dialog_preview_update(gimage); lc_dialog_preview_update(gimage);
/* We're interested in clean and dirty signals so we can update the
* title if need be. */
gtk_signal_connect (GTK_OBJECT (gimage), "dirty",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gtk_signal_connect (GTK_OBJECT (gimage), "clean",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
return gdisp; return gdisp;
} }
@ -259,7 +267,18 @@ gdisplay_format_title (GDisplay *gdisp,
"%d", 100 * SCALEDEST (gdisp) / SCALESRC (gdisp)); "%d", 100 * SCALEDEST (gdisp) / SCALESRC (gdisp));
break; break;
/* other cool things to be added: case 'D': /* dirty flag */
if (format[1] == 0)
{
g_warning("image-title-format string ended within %%D-sequence");
break;
}
if (gimage->dirty)
title[i++] = format[1];
format++;
break;
/* Other cool things to be added:
* %m = memory used by picture * %m = memory used by picture
* some kind of resolution / image size thing * some kind of resolution / image size thing
* people seem to want to know the active layer name * people seem to want to know the active layer name
@ -313,6 +332,9 @@ gdisplay_delete (GDisplay *gdisp)
gdisp->idle_render.active = FALSE; gdisp->idle_render.active = FALSE;
} }
/* get rid of signals handled by this display */
gtk_signal_disconnect_by_data (GTK_OBJECT (gdisp->gimage), gdisp);
if (gdisp->scroll_gc) if (gdisp->scroll_gc)
gdk_gc_destroy (gdisp->scroll_gc); gdk_gc_destroy (gdisp->scroll_gc);
@ -2042,7 +2064,7 @@ gdisplays_dirty ()
/* traverse the linked list of displays */ /* traverse the linked list of displays */
while (list) while (list)
{ {
if (((GDisplay *) list->data)->gimage->dirty > 0) if (((GDisplay *) list->data)->gimage->dirty != 0)
dirty = 1; dirty = 1;
list = g_slist_next (list); list = g_slist_next (list);
} }
@ -2158,6 +2180,7 @@ gdisplay_reconnect (GDisplay *gdisp, GimpImage *gimage)
gdisp->idle_render.active = FALSE; gdisp->idle_render.active = FALSE;
} }
gtk_signal_disconnect_by_data (GTK_OBJECT (gdisp->gimage), gdisp);
gimage_delete (gdisp->gimage); gimage_delete (gdisp->gimage);
instance = gimage->instance_count; instance = gimage->instance_count;
@ -2167,8 +2190,24 @@ gdisplay_reconnect (GDisplay *gdisp, GimpImage *gimage)
gdisp->gimage = gimage; gdisp->gimage = gimage;
gdisp->instance = instance; gdisp->instance = instance;
/* reconnect our clean / dirty signals */
gtk_signal_connect (GTK_OBJECT (gimage), "dirty",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gtk_signal_connect (GTK_OBJECT (gimage), "clean",
GTK_SIGNAL_FUNC(gdisplay_cleandirty_handler), gdisp);
gdisplays_update_title (gimage); gdisplays_update_title (gimage);
gdisplay_expose_full (gdisp); gdisplay_expose_full (gdisp);
gdisplay_flush (gdisp); gdisplay_flush (gdisp);
} }
/* Called whenever the underlying gimage is dirtied or cleaned */
static void
gdisplay_cleandirty_handler (GimpImage *gimage, void *data)
{
GDisplay *gdisp = data;
gdisplay_update_title (gdisp);
}

View File

@ -120,7 +120,7 @@ gdisplay_close_window (GDisplay *gdisp,
* to an image canvas. (a gimage with ref_count = 1) * to an image canvas. (a gimage with ref_count = 1)
*/ */
if (!kill_it && (gdisp->gimage->ref_count == 1) && if (!kill_it && (gdisp->gimage->ref_count == 1) &&
(gdisp->gimage->dirty > 0) && confirm_on_close) gdisp->gimage->dirty && confirm_on_close)
{ {
gdisplay_close_warning_dialog gdisplay_close_warning_dialog
(g_basename (gimage_filename (gdisp->gimage)), gdisp); (g_basename (gimage_filename (gdisp->gimage)), gdisp);

View File

@ -490,7 +490,7 @@ gimp_drawable_attach_parasite (GimpDrawable *drawable,
!parasite_compare( parasite, !parasite_compare( parasite,
gimp_drawable_find_parasite gimp_drawable_find_parasite
(drawable, parasite_name (parasite)))) (drawable, parasite_name (parasite))))
gimp_image_dirty (drawable->gimage); undo_push_cantundo (drawable->gimage, _("parasite attach to drawable"));
parasite_list_add (drawable->parasites, parasite); parasite_list_add (drawable->parasites, parasite);
if (parasite_has_flag (parasite, PARASITE_ATTACH_PARENT)) if (parasite_has_flag (parasite, PARASITE_ATTACH_PARENT))
@ -523,7 +523,7 @@ gimp_drawable_detach_parasite (GimpDrawable *drawable,
undo_push_drawable_parasite_remove (drawable->gimage, drawable, undo_push_drawable_parasite_remove (drawable->gimage, drawable,
parasite_name (p)); parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (drawable->gimage); undo_push_cantundo (drawable->gimage, _("detach parasite from drawable"));
parasite_list_remove (drawable->parasites, parasite); parasite_list_remove (drawable->parasites, parasite);
} }

View File

@ -91,6 +91,7 @@ guint32 next_guide_id = 1; /* For generating guide_ID handles for PDB stuff */
*/ */
enum { enum {
CLEAN,
DIRTY, DIRTY,
REPAINT, REPAINT,
RENAME, RENAME,
@ -118,6 +119,9 @@ gimp_image_class_init (GimpImageClass *klass)
object_class->destroy = gimp_image_destroy; object_class->destroy = gimp_image_destroy;
gimp_image_signals[CLEAN] =
gimp_signal_new ("clean", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void);
gimp_image_signals[DIRTY] = gimp_image_signals[DIRTY] =
gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0, gimp_signal_new ("dirty", GTK_RUN_FIRST, type, 0,
gimp_sigtype_void); gimp_sigtype_void);
@ -165,6 +169,7 @@ gimp_image_init (GimpImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 0; gimage->undo_levels = 0;
gimage->group_count = 0;
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->comp_preview_valid[0] = FALSE; gimage->comp_preview_valid[0] = FALSE;
gimage->comp_preview_valid[1] = FALSE; gimage->comp_preview_valid[1] = FALSE;
@ -1069,7 +1074,7 @@ gimp_image_attach_parasite (GimpImage *gimage,
&& !parasite_compare (parasite, && !parasite_compare (parasite,
gimp_image_find_parasite(gimage, gimp_image_find_parasite(gimage,
parasite_name(parasite)))) parasite_name(parasite))))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("attach parasite to image"));
parasite_list_add (gimage->parasites, parasite); parasite_list_add (gimage->parasites, parasite);
@ -1092,7 +1097,7 @@ gimp_image_detach_parasite (GimpImage *gimage,
if (parasite_is_undoable (p)) if (parasite_is_undoable (p))
undo_push_image_parasite_remove (gimage, parasite_name (p)); undo_push_image_parasite_remove (gimage, parasite_name (p));
else if (parasite_is_persistent (p)) else if (parasite_is_persistent (p))
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("detach parasite from image"));
parasite_list_remove (gimage->parasites, parasite); parasite_list_remove (gimage->parasites, parasite);
} }
@ -2089,7 +2094,9 @@ gimp_image_raise_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); /* Dirty the image, but we're too lazy to provide a
* proper undo. */
undo_push_cantundo (gimage, _("raise layer"));
return prev_layer; return prev_layer;
} }
@ -2163,7 +2170,7 @@ gimp_image_lower_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer"));
return next_layer; return next_layer;
} }
@ -2246,7 +2253,7 @@ gimp_image_raise_layer_to_top (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("raise layer to top"));
return layer; return layer;
} }
@ -2341,7 +2348,7 @@ gimp_image_lower_layer_to_bottom (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("lower layer to bottom"));
return layer_arg; return layer_arg;
} }
@ -2420,7 +2427,7 @@ gimp_image_position_layer (GimpImage *gimage,
/* invalidate the composite preview */ /* invalidate the composite preview */
gimp_image_invalidate_preview (gimage); gimp_image_invalidate_preview (gimage);
gimp_image_dirty (gimage); undo_push_cantundo (gimage, _("re-position layer"));
return layer_arg; return layer_arg;
} }
@ -3340,12 +3347,40 @@ gimp_image_enable_undo (GimpImage *gimage)
return gimp_image_thaw_undo (gimage); return gimp_image_thaw_undo (gimage);
} }
/* NOTE about the gimage->dirty counter:
* If 0, then the image is clean (ie, copy on disk is the same as the one
* in memory).
* If positive, then that's the number of dirtying operations done
* on the image since the last save.
* If negative, then user has hit undo and gone back in time prior
* to the saved copy. Hitting redo will eventually come back to
* the saved copy.
*
* The image is dirty (ie, needs saving) if counter is non-zero.
*
* If the counter is around 10000, this is due to undo-ing back
* before a saved version, then mutating the image (thus destroying
* the redo stack). Once this has happened, it's impossible to get
* the image back to the state on disk, since the redo info has been
* freed. See undo.c for the gorey details.
*/
/*
* NEVER CALL gimp_image_dirty() directly!
*
* If your code has just dirtied the image, push an undo instead.
* Failing that, push the trivial undo which tells the user the
* command is not undoable: undo_push_cantundo() (But really, it would
* be best to push a proper undo). If you just dirty the image
* without pushing an undo then the dirty count is increased, but
* popping that many undo actions won't lead to a clean image.
*/
gint gint
gimp_image_dirty (GimpImage *gimage) gimp_image_dirty (GimpImage *gimage)
{ {
/* if (gimage->dirty < 0)
gimage->dirty = 2;
else */
gimage->dirty++; gimage->dirty++;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]); gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[DIRTY]);
@ -3355,10 +3390,9 @@ gimp_image_dirty (GimpImage *gimage)
gint gint
gimp_image_clean (GimpImage *gimage) gimp_image_clean (GimpImage *gimage)
{ {
/* if (gimage->dirty <= 0)
gimage->dirty = 0;
else */
gimage->dirty--; gimage->dirty--;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
return gimage->dirty; return gimage->dirty;
} }
@ -3366,6 +3400,8 @@ void
gimp_image_clean_all (GimpImage *gimage) gimp_image_clean_all (GimpImage *gimage)
{ {
gimage->dirty = 0; gimage->dirty = 0;
gtk_signal_emit(GTK_OBJECT(gimage), gimp_image_signals[CLEAN]);
} }
Layer * Layer *

View File

@ -97,6 +97,7 @@ struct _GimpImage
GSList *redo_stack; /* stack for redo operations */ GSList *redo_stack; /* stack for redo operations */
gint undo_bytes; /* bytes in undo stack */ gint undo_bytes; /* bytes in undo stack */
gint undo_levels; /* levels in undo stack */ gint undo_levels; /* levels in undo stack */
gint group_count; /* nested undo groups */
gint pushing_undo_group; /* undo group status flag */ gint pushing_undo_group; /* undo group status flag */
/* Composite preview */ /* Composite preview */

View File

@ -3471,17 +3471,19 @@ edit_layer_query_ok_callback (GtkWidget *widget,
if ((layer = options->layer)) if ((layer = options->layer))
{ {
/* Set the new layer name */ /* Set the new layer name */
if (GIMP_DRAWABLE (layer)->name) if (GIMP_DRAWABLE (layer)->name && layer_is_floating_sel (layer))
{ {
/* If the layer is a floating selection, make it a layer */ /* If the layer is a floating selection, make it a layer */
if (layer_is_floating_sel (layer))
{
floating_sel_to_layer (layer); floating_sel_to_layer (layer);
} }
else
{
/* We're doing a plain rename */
undo_push_layer_rename (options->gimage, layer);
} }
layer_set_name (layer, layer_set_name (layer,
gtk_entry_get_text (GTK_ENTRY (options->name_entry))); gtk_entry_get_text (GTK_ENTRY (options->name_entry)));
gimage_dirty (options->gimage);
} }
gdisplays_flush (); gdisplays_flush ();

View File

@ -3471,17 +3471,19 @@ edit_layer_query_ok_callback (GtkWidget *widget,
if ((layer = options->layer)) if ((layer = options->layer))
{ {
/* Set the new layer name */ /* Set the new layer name */
if (GIMP_DRAWABLE (layer)->name) if (GIMP_DRAWABLE (layer)->name && layer_is_floating_sel (layer))
{ {
/* If the layer is a floating selection, make it a layer */ /* If the layer is a floating selection, make it a layer */
if (layer_is_floating_sel (layer))
{
floating_sel_to_layer (layer); floating_sel_to_layer (layer);
} }
else
{
/* We're doing a plain rename */
undo_push_layer_rename (options->gimage, layer);
} }
layer_set_name (layer, layer_set_name (layer,
gtk_entry_get_text (GTK_ENTRY (options->name_entry))); gtk_entry_get_text (GTK_ENTRY (options->name_entry)));
gimage_dirty (options->gimage);
} }
gdisplays_flush (); gdisplays_flush ();

View File

@ -42,6 +42,7 @@
#include "tile.h" /* ick. */ #include "tile.h" /* ick. */
#include "libgimp/parasite.h" #include "libgimp/parasite.h"
#include "libgimp/gimpintl.h"
#include "gimpparasite.h" #include "gimpparasite.h"
@ -80,6 +81,9 @@ int undo_pop_gimage_mod (GImage *, int, int, void *);
int undo_pop_guide (GImage *, int, int, void *); int undo_pop_guide (GImage *, int, int, void *);
int undo_pop_parasite (GImage *, int, int, void *); int undo_pop_parasite (GImage *, int, int, void *);
int undo_pop_qmask (GImage *, int, int, void *); int undo_pop_qmask (GImage *, int, int, void *);
static int undo_pop_layer_rename (GImage *, int, int, void *);
static int undo_pop_cantundo (GImage *, int, int, void *);
/* Free functions */ /* Free functions */
@ -100,13 +104,14 @@ void undo_free_gimage_mod (int, void *);
void undo_free_guide (int, void *); void undo_free_guide (int, void *);
void undo_free_parasite (int, void *); void undo_free_parasite (int, void *);
void undo_free_qmask (int, void *); void undo_free_qmask (int, void *);
static void undo_free_layer_rename (int, void *);
static void undo_free_cantundo (int, void *);
/* Sizing functions */ /* Sizing functions */
static int layer_size (Layer *); static int layer_size (Layer *);
static int channel_size (Channel *); static int channel_size (Channel *);
static int group_count = 0;
static int shrink_wrap = FALSE; static int shrink_wrap = FALSE;
#define UNDO 0 #define UNDO 0
@ -249,6 +254,14 @@ undo_push (GImage *gimage,
{ {
undo_free_list (gimage, REDO, gimage->redo_stack); undo_free_list (gimage, REDO, gimage->redo_stack);
gimage->redo_stack = NULL; 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) if (!gimage->pushing_undo_group)
@ -384,6 +397,18 @@ undo_free (GImage *gimage)
gimage->redo_stack = NULL; gimage->redo_stack = NULL;
gimage->undo_bytes = 0; gimage->undo_bytes = 0;
gimage->undo_levels = 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. */
} }
@ -397,16 +422,23 @@ undo_push_group_start (GImage *gimage,
if (! gimage->undo_on) if (! gimage->undo_on)
return FALSE; return FALSE;
group_count ++; gimage->group_count ++;
/* If we're already in a group...ignore */ /* If we're already in a group...ignore */
if (group_count > 1) if (gimage->group_count > 1)
return TRUE; return TRUE;
if (gimage->redo_stack) if (gimage->redo_stack)
{ {
undo_free_list (gimage, REDO, gimage->redo_stack); undo_free_list (gimage, REDO, gimage->redo_stack);
gimage->redo_stack = NULL; 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)) if (! undo_free_up_space (gimage))
@ -426,9 +458,9 @@ undo_push_group_end (GImage *gimage)
if (! gimage->undo_on) if (! gimage->undo_on)
return FALSE; return FALSE;
group_count --; gimage->group_count --;
if (group_count == 0) if (gimage->group_count == 0)
{ {
gimage->pushing_undo_group = 0; gimage->pushing_undo_group = 0;
gimage->undo_stack = g_slist_prepend (gimage->undo_stack, NULL); gimage->undo_stack = g_slist_prepend (gimage->undo_stack, NULL);
@ -2423,3 +2455,134 @@ undo_free_parasite (int state,
g_free (data_ptr); g_free (data_ptr);
} }
/*************/
/* Layer name change */
typedef struct {
Layer *layer;
gchar *old_name;
} LayerRenameUndo;
int
undo_push_layer_rename (GImage *gimage, Layer *layer)
{
Undo *new;
LayerRenameUndo *data;
long size;
/* increment the dirty flag for this gimage */
gimage_dirty (gimage);
size = sizeof (LayerRenameUndo);
if ((new = undo_push (gimage, size, LAYER_CHANGE)))
{
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 (layer_get_name (layer));
return TRUE;
}
return FALSE;
}
static int
undo_pop_layer_rename (GImage *gimage,
int state,
int type,
void *data_ptr)
{
LayerRenameUndo *data = data_ptr;
gchar *tmp;
tmp = g_strdup (layer_get_name (data->layer));
layer_set_name (data->layer, data->old_name);
g_free (data->old_name);
data->old_name = tmp;
switch (state) {
case UNDO:
gimp_image_clean (gimage);
break;
case REDO:
gimp_image_dirty (gimage);
break;
}
return TRUE;
}
static void
undo_free_layer_rename (int state,
void *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.
*/
int
undo_push_cantundo (GImage *gimage,
const char *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. */
gimp_image_dirty (gimage);
new = undo_push (gimage, 0, GIMAGE_MOD);
if (!new)
return FALSE;
new->data = (void*)action;
new->pop_func = undo_pop_cantundo;
new->free_func = undo_free_cantundo;
return TRUE;
}
static int
undo_pop_cantundo (GImage *gimage,
int state,
int type,
void *data_ptr)
{
char *action = data_ptr;
switch (state) {
case UNDO:
g_message (_("Can't undo %s"), action);
gimp_image_clean (gimage);
break;
case REDO:
gimp_image_dirty (gimage);
break;
}
return TRUE;
}
static void
undo_free_cantundo (int state, void *data_ptr)
{
}

View File

@ -86,6 +86,8 @@ int undo_push_image_parasite_remove (GImage *, const char *);
int undo_push_drawable_parasite_remove (GImage *, GimpDrawable *, int undo_push_drawable_parasite_remove (GImage *, GimpDrawable *,
const char *); const char *);
int undo_push_qmask (GImage *, int); int undo_push_qmask (GImage *, int);
int undo_push_layer_rename (GImage *, Layer *);
int undo_push_cantundo (GImage *, const char *);
int undo_pop (GImage *); int undo_pop (GImage *);
int undo_redo (GImage *); int undo_redo (GImage *);