Reimplemented the undo history:

2003-02-20  Michael Natterer  <mitch@gimp.org>

	Reimplemented the undo history:

	* app/Makefile.am
	* app/undo_history.[ch]: removed.

	Changes/cleanups to the undo system to enable/simplify the new
	undo history implementation:

	* app/core/core-types.h: removed enum undo_event_t. Removed the
	GimpImage parameter from GimpUndoPopFunc and GimpUndoFreeFunc
	because GimpUndo has a GimpImage pointer now (see below).

	* app/core/core-enums.[ch]: added enum GimpUndoEvent. Added an
	enum value for REDO_EXPIRED.

	* app/core/gimpimage.[ch]: added a GimpUndo pointer to the
	"undo_event" signal which needs to be passed for all events except
	UNDO_FREE.

	* app/display/gimpdisplayshell-handlers.c: changed accordingly.

	* app/core/gimpundo.[ch]: added a GimpImage pointer to the
	GimpUndo struct. Removed GimpImage parameters all over the
	place. Added preview stuff. The preview creation needs to be
	triggered explicitly using gimp_undo_create_preview() because the
	GimpUndo can't know when it's possible to create the preview.

	* app/core/gimpimage-undo-push.c
	* app/paint/gimppaintcore-undo.c
	* app/tools/gimptransformtool-undo.c: changed accordingly, cleanup.

	* app/core/gimpundostack.[ch]: ditto. Return the freed undo from
	gimp_undo_stack_free_bottom(). Removed unused container signal
	handlers.

	* app/core/gimpimage-undo.c: free the redo stack the same way old
	undos are freed (from bottom up). Emit "undo_event" with event ==
	REDO_EXPIRED for each removed redo.

	* app/core/gimpmarshal.list: added new marshallers.

	New undo history implementation:

	* app/widgets/Makefile.am
	* app/widgets/widgets-types.h
	* app/widgets/gimpundoeditor.[ch]
	* app/widgets/gimpundopreview.[ch]: new widgets for the undo
	step previews and the history itself.

	* app/widgets/gimppreview-utils.c: added GimpUndoPreview to the
	list of possible preview types.

	* app/gui/dialogs-constructors.[ch]
	* app/gui/dialogs-menu.c
	* app/gui/dialogs.c
	* app/gui/image-menu.c
	* app/gui/toolbox-menu.c: removed the old and added the new undo
	history to the dialog factory and the various dialog menus.

	* app/widgets/gimpdnd.[ch]: don't warn if a GType has no
	corresponding DND type. Instead, return FALSE from the function
	that failed.

	* app/widgets/gimppreview.c: check the return value of gimpdnd
	functions.  Not only add drag sources but also remove them when no
	longer needed.

	* app/widgets/gimpselectioneditor.h: removed unneeded inclusion of
	"gui/gui-types.h".
This commit is contained in:
Michael Natterer
2003-02-20 12:47:42 +00:00
committed by Michael Natterer
parent a20fc0fb51
commit c8b4394d71
44 changed files with 1543 additions and 1552 deletions

View File

@ -1,3 +1,75 @@
2003-02-20 Michael Natterer <mitch@gimp.org>
Reimplemented the undo history:
* app/Makefile.am
* app/undo_history.[ch]: removed.
Changes/cleanups to the undo system to enable/simplify the new
undo history implementation:
* app/core/core-types.h: removed enum undo_event_t. Removed the
GimpImage parameter from GimpUndoPopFunc and GimpUndoFreeFunc
because GimpUndo has a GimpImage pointer now (see below).
* app/core/core-enums.[ch]: added enum GimpUndoEvent. Added an
enum value for REDO_EXPIRED.
* app/core/gimpimage.[ch]: added a GimpUndo pointer to the
"undo_event" signal which needs to be passed for all events except
UNDO_FREE.
* app/display/gimpdisplayshell-handlers.c: changed accordingly.
* app/core/gimpundo.[ch]: added a GimpImage pointer to the
GimpUndo struct. Removed GimpImage parameters all over the
place. Added preview stuff. The preview creation needs to be
triggered explicitly using gimp_undo_create_preview() because the
GimpUndo can't know when it's possible to create the preview.
* app/core/gimpimage-undo-push.c
* app/paint/gimppaintcore-undo.c
* app/tools/gimptransformtool-undo.c: changed accordingly, cleanup.
* app/core/gimpundostack.[ch]: ditto. Return the freed undo from
gimp_undo_stack_free_bottom(). Removed unused container signal
handlers.
* app/core/gimpimage-undo.c: free the redo stack the same way old
undos are freed (from bottom up). Emit "undo_event" with event ==
REDO_EXPIRED for each removed redo.
* app/core/gimpmarshal.list: added new marshallers.
New undo history implementation:
* app/widgets/Makefile.am
* app/widgets/widgets-types.h
* app/widgets/gimpundoeditor.[ch]
* app/widgets/gimpundopreview.[ch]: new widgets for the undo
step previews and the history itself.
* app/widgets/gimppreview-utils.c: added GimpUndoPreview to the
list of possible preview types.
* app/gui/dialogs-constructors.[ch]
* app/gui/dialogs-menu.c
* app/gui/dialogs.c
* app/gui/image-menu.c
* app/gui/toolbox-menu.c: removed the old and added the new undo
history to the dialog factory and the various dialog menus.
* app/widgets/gimpdnd.[ch]: don't warn if a GType has no
corresponding DND type. Instead, return FALSE from the function
that failed.
* app/widgets/gimppreview.c: check the return value of gimpdnd
functions. Not only add drag sources but also remove them when no
longer needed.
* app/widgets/gimpselectioneditor.h: removed unneeded inclusion of
"gui/gui-types.h".
2003-02-19 Tor Lillqvist <tml@iki.fi>
* libgimpbase/gimpbase.def: Add gimp_utf8_strtrim. Sort. Remove

View File

@ -35,13 +35,6 @@ scriptdata =
bin_PROGRAMS = gimp-1.3
##
## gui stuff for gui/, display/ or widgets/
##
gui_sources = \
undo_history.c \
undo_history.h
##
## stuff which is about to be replaced by new subsystems
##
@ -69,7 +62,6 @@ stuff_sources = \
libgimp_glue.h
gimp_1_3_SOURCES = \
$(gui_sources) \
$(bye_sources) \
$(stuff_sources)

View File

@ -381,6 +381,29 @@ gimp_undo_mode_get_type (void)
}
static const GEnumValue gimp_undo_event_enum_values[] =
{
{ GIMP_UNDO_EVENT_UNDO_PUSHED, "GIMP_UNDO_EVENT_UNDO_PUSHED", "undo-pushed" },
{ GIMP_UNDO_EVENT_UNDO_EXPIRED, "GIMP_UNDO_EVENT_UNDO_EXPIRED", "undo-expired" },
{ GIMP_UNDO_EVENT_REDO_EXPIRED, "GIMP_UNDO_EVENT_REDO_EXPIRED", "redo-expired" },
{ GIMP_UNDO_EVENT_UNDO, "GIMP_UNDO_EVENT_UNDO", "undo" },
{ GIMP_UNDO_EVENT_REDO, "GIMP_UNDO_EVENT_REDO", "redo" },
{ GIMP_UNDO_EVENT_UNDO_FREE, "GIMP_UNDO_EVENT_UNDO_FREE", "undo-free" },
{ 0, NULL, NULL }
};
GType
gimp_undo_event_get_type (void)
{
static GType enum_type = 0;
if (!enum_type)
enum_type = g_enum_register_static ("GimpUndoEvent", gimp_undo_event_enum_values);
return enum_type;
}
static const GEnumValue gimp_undo_type_enum_values[] =
{
{ GIMP_UNDO_GROUP_NONE, N_("<<invalid>>"), "group-none" },

View File

@ -280,6 +280,21 @@ typedef enum /*< pdb-skip >*/
} GimpUndoMode;
#define GIMP_TYPE_UNDO_EVENT (gimp_undo_event_get_type ())
GType gimp_undo_event_get_type (void) G_GNUC_CONST;
typedef enum /*< pdb-skip >*/
{
GIMP_UNDO_EVENT_UNDO_PUSHED, /* a new undo has been added to the undo stack */
GIMP_UNDO_EVENT_UNDO_EXPIRED, /* an undo has been freed from the undo stack */
GIMP_UNDO_EVENT_REDO_EXPIRED, /* a redo has been freed from the redo stack */
GIMP_UNDO_EVENT_UNDO, /* an undo has been executed */
GIMP_UNDO_EVENT_REDO, /* a redo has been executed */
GIMP_UNDO_EVENT_UNDO_FREE /* all undo and redo info has been cleared */
} GimpUndoEvent;
#define GIMP_TYPE_UNDO_TYPE (gimp_undo_type_get_type ())
GType gimp_undo_type_get_type (void) G_GNUC_CONST;

View File

@ -44,18 +44,6 @@ typedef enum
GIMP_POINTS
} SizeType;
/* Argument to undo_event signal emitted by images */
typedef enum /*< pdb-skip >*/ /*< skip >*/
{
UNDO_PUSHED, /* a new undo has been added to the undo stack */
UNDO_EXPIRED, /* an undo has been freed from the undo stack */
UNDO_POPPED, /* an undo has been executed and moved to redo stack */
UNDO_REDO, /* a redo has been executed and moved to undo stack */
UNDO_FREE /* all undo and redo info has been cleared */
} undo_event_t;
/* base objects */
@ -160,11 +148,9 @@ typedef void (* GimpInitStatusFunc) (const gchar *text1,
typedef GimpData * (* GimpDataObjectLoaderFunc) (const gchar *filename);
typedef gboolean (* GimpUndoPopFunc) (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
typedef void (* GimpUndoFreeFunc) (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode);

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,7 @@ static void gimp_image_undo_pop_stack (GimpImage *gimage,
GimpUndoStack *redo_stack,
GimpUndoMode undo_mode);
static void gimp_image_undo_free_space (GimpImage *gimage);
static void gimp_image_undo_free_redo (GimpImage *gimage);
static const gchar * gimp_image_undo_type_to_name (GimpUndoType type);
@ -78,8 +79,13 @@ gimp_image_undo_free (GimpImage *gimage)
{
g_return_if_fail (GIMP_IS_IMAGE (gimage));
gimp_undo_free (GIMP_UNDO (gimage->undo_stack), gimage, GIMP_UNDO_MODE_UNDO);
gimp_undo_free (GIMP_UNDO (gimage->redo_stack), gimage, GIMP_UNDO_MODE_REDO);
/* Emit the UNDO_FREE event before actually freeing everything
* so the views can properly detach from the undo items
*/
gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_UNDO_FREE, NULL);
gimp_undo_free (GIMP_UNDO (gimage->undo_stack), GIMP_UNDO_MODE_UNDO);
gimp_undo_free (GIMP_UNDO (gimage->redo_stack), GIMP_UNDO_MODE_REDO);
/* If the image was dirty, but could become clean by redo-ing
* some actions, then it should now become 'infinitely' dirty.
@ -94,7 +100,6 @@ gimp_image_undo_free (GimpImage *gimage)
* 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);
}
gboolean
@ -124,8 +129,7 @@ gimp_image_undo_group_start (GimpImage *gimage,
return TRUE;
/* nuke the redo stack */
gimp_undo_free (GIMP_UNDO (gimage->redo_stack), gimage,
GIMP_UNDO_MODE_REDO);
gimp_image_undo_free_redo (gimage);
/* If the image was dirty, but could become clean by redo-ing
* some actions, then it should now become 'infinitely' dirty.
@ -170,7 +174,8 @@ gimp_image_undo_group_end (GimpImage *gimage)
/* 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);
gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_UNDO_PUSHED,
gimp_undo_stack_peek (gimage->undo_stack));
}
return TRUE;
@ -205,8 +210,7 @@ gimp_image_undo_push (GimpImage *gimage,
return NULL;
/* nuke the redo stack */
gimp_undo_free (GIMP_UNDO (gimage->redo_stack), gimage,
GIMP_UNDO_MODE_REDO);
gimp_image_undo_free_redo (gimage);
/* If the image was dirty, but could become clean by redo-ing
* some actions, then it should now become 'infinitely' dirty.
@ -220,7 +224,8 @@ gimp_image_undo_push (GimpImage *gimage,
if (struct_size > 0)
undo_struct = g_malloc0 (struct_size);
new = gimp_undo_new (type,
new = gimp_undo_new (gimage,
type,
name,
undo_struct, size,
dirties_image,
@ -232,7 +237,7 @@ gimp_image_undo_push (GimpImage *gimage,
gimp_image_undo_free_space (gimage);
gimp_image_undo_event (gimage, UNDO_PUSHED);
gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_UNDO_PUSHED, new);
}
else
{
@ -292,7 +297,8 @@ gimp_image_undo_pop_stack (GimpImage *gimage,
/* let others know that we just popped an action */
gimp_image_undo_event (gimage,
(undo_mode == GIMP_UNDO_MODE_UNDO) ?
UNDO_POPPED : UNDO_REDO);
GIMP_UNDO_EVENT_UNDO : GIMP_UNDO_EVENT_REDO,
undo);
}
static void
@ -322,7 +328,10 @@ gimp_image_undo_free_space (GimpImage *gimage)
while ((gimp_object_get_memsize (GIMP_OBJECT (container)) > undo_size) ||
(gimp_container_num_children (container) > max_undo_levels))
{
gimp_undo_stack_free_bottom (gimage->undo_stack, GIMP_UNDO_MODE_UNDO);
GimpUndo *freed;
freed = gimp_undo_stack_free_bottom (gimage->undo_stack,
GIMP_UNDO_MODE_UNDO);
#if 0
g_print ("freed one step: undo_steps: %d undo_bytes: %d\n",
@ -330,13 +339,47 @@ gimp_image_undo_free_space (GimpImage *gimage)
gimp_object_get_memsize (GIMP_OBJECT (container)));
#endif
gimp_image_undo_event (gimage, UNDO_EXPIRED);
gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_UNDO_EXPIRED, freed);
g_object_unref (freed);
if (gimp_container_num_children (container) <= min_undo_levels)
return;
}
}
static void
gimp_image_undo_free_redo (GimpImage *gimage)
{
GimpContainer *container;
container = gimage->redo_stack->undos;
#if 0
g_print ("redo_steps: %d redo_bytes: %d\n",
gimp_container_num_children (container),
gimp_object_get_memsize (GIMP_OBJECT (container)));
#endif
while (gimp_container_num_children (container) > 0)
{
GimpUndo *freed;
freed = gimp_undo_stack_free_bottom (gimage->redo_stack,
GIMP_UNDO_MODE_REDO);
#if 0
g_print ("freed one step: redo_steps: %d redo_bytes: %d\n",
gimp_container_num_children (container),
gimp_object_get_memsize (GIMP_OBJECT (container)));
#endif
gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_REDO_EXPIRED, freed);
g_object_unref (freed);
}
}
static const gchar *
gimp_image_undo_type_to_name (GimpUndoType type)
{

View File

@ -371,9 +371,10 @@ gimp_image_class_init (GimpImageClass *klass)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpImageClass, undo_event),
NULL, NULL,
gimp_marshal_VOID__INT,
G_TYPE_NONE, 1,
G_TYPE_INT);
gimp_marshal_VOID__ENUM_OBJECT,
G_TYPE_NONE, 2,
GIMP_TYPE_UNDO_EVENT,
GIMP_TYPE_UNDO);
gimp_image_signals[FLUSH] =
g_signal_new ("flush",
@ -1574,10 +1575,14 @@ gimp_image_undo_start (GimpImage *gimage)
}
void
gimp_image_undo_event (GimpImage *gimage,
gint event)
gimp_image_undo_event (GimpImage *gimage,
GimpUndoEvent event,
GimpUndo *undo)
{
g_signal_emit (gimage, gimp_image_signals[UNDO_EVENT], 0, event);
g_return_if_fail (GIMP_IS_IMAGE (gimage));
g_return_if_fail (event == GIMP_UNDO_EVENT_UNDO_FREE || GIMP_IS_UNDO (undo));
g_signal_emit (gimage, gimp_image_signals[UNDO_EVENT], 0, event, undo);
}

View File

@ -198,7 +198,8 @@ struct _GimpImageClass
gint color_index);
void (* undo_start) (GimpImage *gimage);
void (* undo_event) (GimpImage *gimage,
gint event);
GimpUndoEvent event,
GimpUndo *undo);
void (* flush) (GimpImage *gimage);
};
@ -299,7 +300,8 @@ gboolean gimp_image_undo_freeze (GimpImage *gimage);
gboolean gimp_image_undo_thaw (GimpImage *gimage);
void gimp_image_undo_start (GimpImage *gimage);
void gimp_image_undo_event (GimpImage *gimage,
gint event);
GimpUndoEvent event,
GimpUndo *undo);
gint gimp_image_dirty (GimpImage *gimage);
gint gimp_image_clean (GimpImage *gimage);
void gimp_image_clean_all (GimpImage *gimage);

View File

@ -46,6 +46,8 @@ POINTER: VOID
VOID: BOXED
VOID: DOUBLE
VOID: ENUM
VOID: ENUM, OBJECT
VOID: ENUM, POINTER
VOID: INT
VOID: INT, BOOLEAN, INT, OBJECT
VOID: INT, INT

View File

@ -27,6 +27,7 @@
#include "gimpimage.h"
#include "gimpmarshal.h"
#include "gimpundo.h"
#include "gimpundostack.h"
enum
@ -37,24 +38,25 @@ enum
};
static void gimp_undo_class_init (GimpUndoClass *klass);
static void gimp_undo_init (GimpUndo *undo);
static void gimp_undo_class_init (GimpUndoClass *klass);
static void gimp_undo_init (GimpUndo *undo);
static void gimp_undo_finalize (GObject *object);
static void gimp_undo_finalize (GObject *object);
static gsize gimp_undo_get_memsize (GimpObject *object);
static gsize gimp_undo_get_memsize (GimpObject *object);
static TempBuf * gimp_undo_get_preview (GimpViewable *viewable,
gint width,
gint height);
static TempBuf * gimp_undo_get_preview (GimpViewable *viewable,
gint width,
gint height);
static void gimp_undo_real_pop (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
static void gimp_undo_real_free (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode);
static void gimp_undo_real_pop (GimpUndo *undo,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
static void gimp_undo_real_free (GimpUndo *undo,
GimpUndoMode undo_mode);
static gboolean gimp_undo_create_preview_idle (gpointer data);
static void gimp_undo_create_preview_private (GimpUndo *undo);
static guint undo_signals[LAST_SIGNAL] = { 0 };
@ -109,9 +111,8 @@ gimp_undo_class_init (GimpUndoClass *klass)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpUndoClass, pop),
NULL, NULL,
gimp_marshal_VOID__OBJECT_ENUM_POINTER,
G_TYPE_NONE, 3,
GIMP_TYPE_IMAGE,
gimp_marshal_VOID__ENUM_POINTER,
G_TYPE_NONE, 2,
GIMP_TYPE_UNDO_MODE,
G_TYPE_POINTER);
@ -121,9 +122,8 @@ gimp_undo_class_init (GimpUndoClass *klass)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpUndoClass, free),
NULL, NULL,
gimp_marshal_VOID__OBJECT_ENUM,
G_TYPE_NONE, 2,
GIMP_TYPE_IMAGE,
gimp_marshal_VOID__ENUM,
G_TYPE_NONE, 1,
GIMP_TYPE_UNDO_MODE);
object_class->finalize = gimp_undo_finalize;
@ -139,6 +139,8 @@ gimp_undo_class_init (GimpUndoClass *klass)
static void
gimp_undo_init (GimpUndo *undo)
{
undo->gimage = NULL;
undo->undo_type = 0;
undo->data = NULL;
undo->dirties_image = FALSE;
undo->pop_func = NULL;
@ -153,6 +155,12 @@ gimp_undo_finalize (GObject *object)
undo = GIMP_UNDO (object);
if (undo->preview_idle_id)
{
g_source_remove (undo->preview_idle_id);
undo->preview_idle_id = 0;
}
if (undo->preview)
{
temp_buf_free (undo->preview);
@ -183,11 +191,29 @@ gimp_undo_get_preview (GimpViewable *viewable,
gint width,
gint height)
{
return (GIMP_UNDO (viewable)->preview);
return GIMP_UNDO (viewable)->preview;
}
static void
gimp_undo_real_pop (GimpUndo *undo,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum)
{
if (undo->pop_func)
undo->pop_func (undo, undo_mode, accum);
}
static void
gimp_undo_real_free (GimpUndo *undo,
GimpUndoMode undo_mode)
{
if (undo->free_func)
undo->free_func (undo, undo_mode);
}
GimpUndo *
gimp_undo_new (GimpUndoType undo_type,
gimp_undo_new (GimpImage *gimage,
GimpUndoType undo_type,
const gchar *name,
gpointer data,
gsize size,
@ -197,13 +223,15 @@ gimp_undo_new (GimpUndoType undo_type,
{
GimpUndo *undo;
g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
g_return_val_if_fail (name != NULL, NULL);
//g_return_val_if_fail (size == 0 || data != NULL, NULL);
g_return_val_if_fail (size == 0 || data != NULL, NULL);
undo = g_object_new (GIMP_TYPE_UNDO,
"name", name,
NULL);
undo->gimage = gimage;
undo->undo_type = undo_type;
undo->data = data;
undo->size = size;
@ -216,26 +244,24 @@ gimp_undo_new (GimpUndoType undo_type,
void
gimp_undo_pop (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum)
{
g_return_if_fail (GIMP_IS_UNDO (undo));
g_return_if_fail (GIMP_IS_IMAGE (gimage));
g_return_if_fail (accum != NULL);
g_signal_emit (undo, undo_signals[POP], 0, gimage, undo_mode, accum);
g_signal_emit (undo, undo_signals[POP], 0, undo_mode, accum);
if (undo->dirties_image)
{
switch (undo_mode)
{
case GIMP_UNDO_MODE_UNDO:
gimp_image_clean (gimage);
gimp_image_clean (undo->gimage);
break;
case GIMP_UNDO_MODE_REDO:
gimp_image_dirty (gimage);
gimp_image_dirty (undo->gimage);
break;
}
}
@ -243,30 +269,86 @@ gimp_undo_pop (GimpUndo *undo,
void
gimp_undo_free (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode)
{
g_return_if_fail (GIMP_IS_UNDO (undo));
g_return_if_fail (GIMP_IS_IMAGE (gimage));
g_signal_emit (undo, undo_signals[FREE], 0, gimage, undo_mode);
g_signal_emit (undo, undo_signals[FREE], 0, undo_mode);
}
static void
gimp_undo_real_pop (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum)
void
gimp_undo_create_preview (GimpUndo *undo,
gboolean create_now)
{
if (undo->pop_func)
undo->pop_func (undo, gimage, undo_mode, accum);
g_return_if_fail (GIMP_IS_UNDO (undo));
if (undo->preview || undo->preview_idle_id)
return;
if (create_now)
gimp_undo_create_preview_private (undo);
else
undo->preview_idle_id = g_idle_add (gimp_undo_create_preview_idle, undo);
}
static void
gimp_undo_real_free (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode)
static gboolean
gimp_undo_create_preview_idle (gpointer data)
{
if (undo->free_func)
undo->free_func (undo, gimage, undo_mode);
GimpUndo *undo;
undo = GIMP_UNDO (data);
if (undo == gimp_undo_stack_peek (undo->gimage->undo_stack))
{
gimp_undo_create_preview_private (undo);
}
undo->preview_idle_id = 0;
return FALSE;
}
void
gimp_undo_create_preview_private (GimpUndo *undo)
{
GimpViewable *preview_viewable;
gint width;
gint height;
switch (undo->undo_type)
{
case GIMP_UNDO_GROUP_IMAGE_QMASK:
case GIMP_UNDO_IMAGE_QMASK:
case GIMP_UNDO_MASK:
preview_viewable = GIMP_VIEWABLE (gimp_image_get_mask (undo->gimage));
break;
default:
preview_viewable = GIMP_VIEWABLE (undo->gimage);
break;
}
if (undo->gimage->width <= 64 && undo->gimage->height <= 64)
{
width = undo->gimage->width;
height = undo->gimage->height;
}
else
{
if (undo->gimage->width > undo->gimage->height)
{
width = 64;
height = MAX (1, undo->gimage->height * 64 / undo->gimage->width);
}
else
{
height = 64;
width = MAX (1, undo->gimage->width * 64 / undo->gimage->height);
}
}
undo->preview = gimp_viewable_get_new_preview (preview_viewable,
width, height);
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (undo));
}

View File

@ -49,6 +49,8 @@ struct _GimpUndo
{
GimpViewable parent_instance;
GimpImage *gimage; /* the image this undo is part of */
GimpUndoType undo_type; /* undo type */
gpointer data; /* data to implement the undo */
gsize size; /* size of undo item */
@ -58,6 +60,7 @@ struct _GimpUndo
GimpUndoFreeFunc free_func; /* function pointer to free undo data */
TempBuf *preview;
guint preview_idle_id;
};
struct _GimpUndoClass
@ -65,32 +68,32 @@ struct _GimpUndoClass
GimpViewableClass parent_class;
void (* pop) (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
void (* free) (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode);
};
GType gimp_undo_get_type (void) G_GNUC_CONST;
GType gimp_undo_get_type (void) G_GNUC_CONST;
GimpUndo * gimp_undo_new (GimpUndoType undo_type,
const gchar *name,
gpointer data,
gsize size,
gboolean dirties_image,
GimpUndoPopFunc pop_func,
GimpUndoFreeFunc free_func);
GimpUndo * gimp_undo_new (GimpImage *gimage,
GimpUndoType undo_type,
const gchar *name,
gpointer data,
gsize size,
gboolean dirties_image,
GimpUndoPopFunc pop_func,
GimpUndoFreeFunc free_func);
void gimp_undo_pop (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
void gimp_undo_free (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode);
void gimp_undo_pop (GimpUndo *undo,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
void gimp_undo_free (GimpUndo *undo,
GimpUndoMode undo_mode);
void gimp_undo_create_preview (GimpUndo *undo,
gboolean create_now);
#endif /* __GIMP_UNDO_H__ */

View File

@ -28,27 +28,18 @@
#include "gimpundostack.h"
static void gimp_undo_stack_class_init (GimpUndoStackClass *klass);
static void gimp_undo_stack_init (GimpUndoStack *stack);
static void gimp_undo_stack_class_init (GimpUndoStackClass *klass);
static void gimp_undo_stack_init (GimpUndoStack *stack);
static void gimp_undo_stack_finalize (GObject *object);
static void gimp_undo_stack_finalize (GObject *object);
static gsize gimp_undo_stack_get_memsize (GimpObject *object);
static gsize gimp_undo_stack_get_memsize (GimpObject *object);
static void gimp_undo_stack_pop (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
static void gimp_undo_stack_free (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode);
static void gimp_undo_stack_add_callback (GimpContainer *container,
GimpObject *object,
gpointer data);
static void gimp_undo_stack_remove_callback (GimpContainer *container,
GimpObject *object,
gpointer data);
static void gimp_undo_stack_pop (GimpUndo *undo,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
static void gimp_undo_stack_free (GimpUndo *undo,
GimpUndoMode undo_mode);
static GimpUndoClass *parent_class = NULL;
@ -106,21 +97,8 @@ gimp_undo_stack_class_init (GimpUndoStackClass *klass)
static void
gimp_undo_stack_init (GimpUndoStack *stack)
{
GimpContainer *undos;
undos = gimp_list_new (GIMP_TYPE_UNDO,
GIMP_CONTAINER_POLICY_STRONG);
stack->undos = undos;
g_signal_connect (undos, "add",
G_CALLBACK (gimp_undo_stack_add_callback),
stack);
g_signal_connect (undos, "remove",
G_CALLBACK (gimp_undo_stack_remove_callback),
stack);
stack->gimage = NULL;
stack->undos = gimp_list_new (GIMP_TYPE_UNDO,
GIMP_CONTAINER_POLICY_STRONG);
}
static void
@ -155,7 +133,6 @@ gimp_undo_stack_get_memsize (GimpObject *object)
static void
gimp_undo_stack_pop (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum)
{
@ -172,13 +149,12 @@ gimp_undo_stack_pop (GimpUndo *undo,
child = GIMP_UNDO (list->data);
gimp_undo_pop (child, gimage, undo_mode, accum);
gimp_undo_pop (child, undo_mode, accum);
}
}
static void
gimp_undo_stack_free (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode)
{
GimpUndoStack *stack;
@ -194,7 +170,7 @@ gimp_undo_stack_free (GimpUndo *undo,
child = GIMP_UNDO (list->data);
gimp_undo_free (child, gimage, undo_mode);
gimp_undo_free (child, undo_mode);
g_object_unref (child);
}
@ -212,7 +188,7 @@ gimp_undo_stack_new (GimpImage *gimage)
stack = GIMP_UNDO_STACK (g_object_new (GIMP_TYPE_UNDO_STACK, NULL));
stack->gimage = gimage;
GIMP_UNDO (stack)->gimage = gimage;
return stack;
}
@ -232,45 +208,49 @@ gimp_undo_stack_pop_undo (GimpUndoStack *stack,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum)
{
GimpObject *object;
GimpUndo *undo;
g_return_val_if_fail (GIMP_IS_UNDO_STACK (stack), NULL);
g_return_val_if_fail (accum != NULL, NULL);
object = gimp_container_get_child_by_index (GIMP_CONTAINER (stack->undos),0);
if (object)
undo = (GimpUndo *)
gimp_container_get_child_by_index (GIMP_CONTAINER (stack->undos), 0);
if (undo)
{
gimp_container_remove (GIMP_CONTAINER (stack->undos), object);
gimp_undo_pop (GIMP_UNDO (object), stack->gimage, undo_mode, accum);
gimp_container_remove (GIMP_CONTAINER (stack->undos), GIMP_OBJECT (undo));
gimp_undo_pop (undo, undo_mode, accum);
return GIMP_UNDO (object);
return undo;
}
return NULL;
}
void
GimpUndo *
gimp_undo_stack_free_bottom (GimpUndoStack *stack,
GimpUndoMode undo_mode)
{
GimpObject *object;
gint n_children;
GimpUndo *undo;
gint n_children;
g_return_if_fail (GIMP_IS_UNDO_STACK (stack));
g_return_val_if_fail (GIMP_IS_UNDO_STACK (stack), NULL);
n_children = gimp_container_num_children (GIMP_CONTAINER (stack->undos));
object = gimp_container_get_child_by_index (GIMP_CONTAINER (stack->undos),
n_children - 1);
undo = (GimpUndo *)
gimp_container_get_child_by_index (GIMP_CONTAINER (stack->undos),
n_children - 1);
if (object)
if (undo)
{
gimp_container_remove (GIMP_CONTAINER (stack->undos), object);
gimp_undo_free (GIMP_UNDO (object), stack->gimage, undo_mode);
gimp_container_remove (GIMP_CONTAINER (stack->undos), GIMP_OBJECT (undo));
gimp_undo_free (undo, undo_mode);
g_object_unref (object);
return undo;
}
return NULL;
}
GimpUndo *
@ -284,27 +264,3 @@ gimp_undo_stack_peek (GimpUndoStack *stack)
return (object ? GIMP_UNDO (object) : NULL);
}
static void
gimp_undo_stack_add_callback (GimpContainer *container,
GimpObject *object,
gpointer data)
{
GimpUndo *undo;
GimpUndo *stack;
undo = GIMP_UNDO (object);
stack = GIMP_UNDO (data);
}
static void
gimp_undo_stack_remove_callback (GimpContainer *container,
GimpObject *object,
gpointer data)
{
GimpUndo *undo;
GimpUndo *stack;
undo = GIMP_UNDO (object);
stack = GIMP_UNDO (data);
}

View File

@ -37,7 +37,6 @@ struct _GimpUndoStack
{
GimpUndo parent_instance;
GimpImage *gimage;
GimpContainer *undos;
};
@ -57,7 +56,7 @@ GimpUndo * gimp_undo_stack_pop_undo (GimpUndoStack *stack,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
void gimp_undo_stack_free_bottom (GimpUndoStack *stack,
GimpUndo * gimp_undo_stack_free_bottom (GimpUndoStack *stack,
GimpUndoMode undo_mode);
GimpUndo * gimp_undo_stack_peek (GimpUndoStack *stack);

View File

@ -60,6 +60,7 @@
#include "widgets/gimpselectioneditor.h"
#include "widgets/gimptoolbox.h"
#include "widgets/gimptoolbox-color-area.h"
#include "widgets/gimpundoeditor.h"
#include "widgets/gimpvectorslistview.h"
#include "display/gimpdisplay.h"
@ -82,8 +83,6 @@
#include "tool-options-dialog.h"
#include "vectors-commands.h"
#include "undo_history.h"
#include "libgimp/gimpintl.h"
@ -128,6 +127,8 @@ static void dialogs_set_indexed_palette_context_func (GimpDockable *dockable,
GimpContext *context);
static void dialogs_set_selection_editor_context_func (GimpDockable *dockable,
GimpContext *context);
static void dialogs_set_undo_history_context_func (GimpDockable *dockable,
GimpContext *context);
static void dialogs_set_navigation_context_func (GimpDockable *dockable,
GimpContext *context);
@ -150,6 +151,9 @@ static void dialogs_indexed_palette_image_changed (GimpContext *context
static void dialogs_selection_editor_image_changed (GimpContext *context,
GimpImage *gimage,
GimpSelectionEditor *editor);
static void dialogs_undo_history_image_changed (GimpContext *context,
GimpImage *gimage,
GimpUndoEditor *editor);
static void dialogs_navigation_display_changed (GimpContext *context,
GimpDisplay *gdisp,
GimpNavigationView *view);
@ -183,31 +187,6 @@ dialogs_module_browser_get (GimpDialogFactory *factory,
return module_browser_new (context->gimp);
}
GtkWidget *
dialogs_undo_history_get (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size)
{
GimpImage *gimage;
GtkWidget *undo_history;
gimage = gimp_context_get_image (context);
if (! gimage)
return NULL;
undo_history = g_object_get_data (G_OBJECT (gimage), "gimp-undo-history");
if (! undo_history)
{
undo_history = undo_history_new (gimage);
g_object_set_data (G_OBJECT (gimage), "gimp-undo-history", undo_history);
}
return undo_history;
}
GtkWidget *
dialogs_display_filters_get (GimpDialogFactory *factory,
GimpContext *context,
@ -795,6 +774,30 @@ dialogs_selection_editor_new (GimpDialogFactory *factory,
return dockable;
}
GtkWidget *
dialogs_undo_history_new (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size)
{
GimpImage *gimage;
GtkWidget *view;
GtkWidget *dockable;
gimage = gimp_context_get_image (context);
view = gimp_undo_editor_new (gimage);
dockable = dialogs_dockable_new (view,
_("Undo History"), _("Undo"),
GTK_STOCK_UNDO,
dialogs_stock_text_tab_func,
dialogs_set_undo_history_context_func);
gimp_dockable_set_context (GIMP_DOCKABLE (dockable), context);
return dockable;
}
/***** misc dockables *****/
@ -1380,6 +1383,41 @@ dialogs_set_selection_editor_context_func (GimpDockable *dockable,
}
}
static void
dialogs_set_undo_history_context_func (GimpDockable *dockable,
GimpContext *context)
{
GimpUndoEditor *view;
view = (GimpUndoEditor *) g_object_get_data (G_OBJECT (dockable),
"gimp-dialogs-view");
if (view)
{
if (dockable->context)
{
g_signal_handlers_disconnect_by_func (dockable->context,
dialogs_undo_history_image_changed,
view);
}
if (context)
{
g_signal_connect (context, "image_changed",
G_CALLBACK (dialogs_undo_history_image_changed),
view);
dialogs_undo_history_image_changed (context,
gimp_context_get_image (context),
view);
}
else
{
dialogs_undo_history_image_changed (NULL, NULL, view);
}
}
}
static void
dialogs_set_navigation_context_func (GimpDockable *dockable,
GimpContext *context)
@ -1470,6 +1508,14 @@ dialogs_selection_editor_image_changed (GimpContext *context,
gimp_selection_editor_set_image (editor, gimage);
}
static void
dialogs_undo_history_image_changed (GimpContext *context,
GimpImage *gimage,
GimpUndoEditor *editor)
{
gimp_undo_editor_set_image (editor, gimage);
}
static void
dialogs_navigation_display_changed (GimpContext *context,
GimpDisplay *gdisp,

View File

@ -31,9 +31,6 @@ GtkWidget * dialogs_preferences_get (GimpDialogFactory *factory,
GtkWidget * dialogs_module_browser_get (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
GtkWidget * dialogs_undo_history_get (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
GtkWidget * dialogs_display_filters_get (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
@ -126,6 +123,9 @@ GtkWidget * dialogs_indexed_palette_new (GimpDialogFactory *factory,
GtkWidget * dialogs_selection_editor_new (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
GtkWidget * dialogs_undo_history_new (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
GtkWidget * dialogs_color_editor_new (GimpDialogFactory *factory,
GimpContext *context,

View File

@ -43,7 +43,6 @@ static const GimpDialogFactoryEntry toplevel_entries[] =
{ "gimp-device-status-dialog", dialogs_device_status_get, 32, TRUE, TRUE, FALSE, TRUE },
{ "gimp-preferences-dialog", dialogs_preferences_get, 32, TRUE, FALSE, FALSE, TRUE },
{ "gimp-module-browser-dialog", dialogs_module_browser_get, 32, TRUE, FALSE, FALSE, TRUE },
{ "gimp-undo-history-dialog", dialogs_undo_history_get, 32, FALSE, FALSE, FALSE, TRUE },
{ "gimp-display-filters-dialog", dialogs_display_filters_get, 32, FALSE, FALSE, FALSE, TRUE },
{ "gimp-tips-dialog", dialogs_tips_get, 32, TRUE, FALSE, FALSE, TRUE },
{ "gimp-about-dialog", dialogs_about_get, 32, TRUE, FALSE, FALSE, TRUE }
@ -76,6 +75,7 @@ static const GimpDialogFactoryEntry dock_entries[] =
{ "gimp-path-list", dialogs_path_list_view_new, 32, TRUE, FALSE, FALSE, TRUE },
{ "gimp-indexed-palette", dialogs_indexed_palette_new, 32, FALSE, FALSE, FALSE, TRUE },
{ "gimp-selection-editor", dialogs_selection_editor_new, 0, FALSE, FALSE, FALSE, TRUE },
{ "gimp-undo-history", dialogs_undo_history_new, 0, FALSE, FALSE, FALSE, TRUE },
{ "gimp-color-editor", dialogs_color_editor_new, 0, FALSE, FALSE, FALSE, TRUE },

View File

@ -44,7 +44,8 @@
static void gimp_display_shell_clean_dirty_handler (GimpImage *gimage,
GimpDisplayShell *shell);
static void gimp_display_shell_undo_event_handler (GimpImage *gimage,
gint event,
GimpUndoEvent event,
GimpUndo *undo,
GimpDisplayShell *shell);
static void gimp_display_shell_name_changed_handler (GimpImage *gimage,
GimpDisplayShell *shell);
@ -267,7 +268,8 @@ gimp_display_shell_clean_dirty_handler (GimpImage *gimage,
static void
gimp_display_shell_undo_event_handler (GimpImage *gimage,
gint event,
GimpUndoEvent event,
GimpUndo *undo,
GimpDisplayShell *shell)
{
shell->title_dirty = TRUE;

View File

@ -60,6 +60,7 @@
#include "widgets/gimpselectioneditor.h"
#include "widgets/gimptoolbox.h"
#include "widgets/gimptoolbox-color-area.h"
#include "widgets/gimpundoeditor.h"
#include "widgets/gimpvectorslistview.h"
#include "display/gimpdisplay.h"
@ -82,8 +83,6 @@
#include "tool-options-dialog.h"
#include "vectors-commands.h"
#include "undo_history.h"
#include "libgimp/gimpintl.h"
@ -128,6 +127,8 @@ static void dialogs_set_indexed_palette_context_func (GimpDockable *dockable,
GimpContext *context);
static void dialogs_set_selection_editor_context_func (GimpDockable *dockable,
GimpContext *context);
static void dialogs_set_undo_history_context_func (GimpDockable *dockable,
GimpContext *context);
static void dialogs_set_navigation_context_func (GimpDockable *dockable,
GimpContext *context);
@ -150,6 +151,9 @@ static void dialogs_indexed_palette_image_changed (GimpContext *context
static void dialogs_selection_editor_image_changed (GimpContext *context,
GimpImage *gimage,
GimpSelectionEditor *editor);
static void dialogs_undo_history_image_changed (GimpContext *context,
GimpImage *gimage,
GimpUndoEditor *editor);
static void dialogs_navigation_display_changed (GimpContext *context,
GimpDisplay *gdisp,
GimpNavigationView *view);
@ -183,31 +187,6 @@ dialogs_module_browser_get (GimpDialogFactory *factory,
return module_browser_new (context->gimp);
}
GtkWidget *
dialogs_undo_history_get (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size)
{
GimpImage *gimage;
GtkWidget *undo_history;
gimage = gimp_context_get_image (context);
if (! gimage)
return NULL;
undo_history = g_object_get_data (G_OBJECT (gimage), "gimp-undo-history");
if (! undo_history)
{
undo_history = undo_history_new (gimage);
g_object_set_data (G_OBJECT (gimage), "gimp-undo-history", undo_history);
}
return undo_history;
}
GtkWidget *
dialogs_display_filters_get (GimpDialogFactory *factory,
GimpContext *context,
@ -795,6 +774,30 @@ dialogs_selection_editor_new (GimpDialogFactory *factory,
return dockable;
}
GtkWidget *
dialogs_undo_history_new (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size)
{
GimpImage *gimage;
GtkWidget *view;
GtkWidget *dockable;
gimage = gimp_context_get_image (context);
view = gimp_undo_editor_new (gimage);
dockable = dialogs_dockable_new (view,
_("Undo History"), _("Undo"),
GTK_STOCK_UNDO,
dialogs_stock_text_tab_func,
dialogs_set_undo_history_context_func);
gimp_dockable_set_context (GIMP_DOCKABLE (dockable), context);
return dockable;
}
/***** misc dockables *****/
@ -1380,6 +1383,41 @@ dialogs_set_selection_editor_context_func (GimpDockable *dockable,
}
}
static void
dialogs_set_undo_history_context_func (GimpDockable *dockable,
GimpContext *context)
{
GimpUndoEditor *view;
view = (GimpUndoEditor *) g_object_get_data (G_OBJECT (dockable),
"gimp-dialogs-view");
if (view)
{
if (dockable->context)
{
g_signal_handlers_disconnect_by_func (dockable->context,
dialogs_undo_history_image_changed,
view);
}
if (context)
{
g_signal_connect (context, "image_changed",
G_CALLBACK (dialogs_undo_history_image_changed),
view);
dialogs_undo_history_image_changed (context,
gimp_context_get_image (context),
view);
}
else
{
dialogs_undo_history_image_changed (NULL, NULL, view);
}
}
}
static void
dialogs_set_navigation_context_func (GimpDockable *dockable,
GimpContext *context)
@ -1470,6 +1508,14 @@ dialogs_selection_editor_image_changed (GimpContext *context,
gimp_selection_editor_set_image (editor, gimage);
}
static void
dialogs_undo_history_image_changed (GimpContext *context,
GimpImage *gimage,
GimpUndoEditor *editor)
{
gimp_undo_editor_set_image (editor, gimage);
}
static void
dialogs_navigation_display_changed (GimpContext *context,
GimpDisplay *gdisp,

View File

@ -31,9 +31,6 @@ GtkWidget * dialogs_preferences_get (GimpDialogFactory *factory,
GtkWidget * dialogs_module_browser_get (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
GtkWidget * dialogs_undo_history_get (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
GtkWidget * dialogs_display_filters_get (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
@ -126,6 +123,9 @@ GtkWidget * dialogs_indexed_palette_new (GimpDialogFactory *factory,
GtkWidget * dialogs_selection_editor_new (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
GtkWidget * dialogs_undo_history_new (GimpDialogFactory *factory,
GimpContext *context,
gint preview_size);
GtkWidget * dialogs_color_editor_new (GimpDialogFactory *factory,
GimpContext *context,

View File

@ -67,6 +67,8 @@ GimpItemFactoryEntry dialogs_menu_entries[] =
"<StockItem>", GIMP_STOCK_TOOL_RECT_SELECT),
ADD_TAB (N_("/Add Tab/Navigation..."), "gimp-navigation-view",
"<StockItem>", GIMP_STOCK_NAVIGATION),
ADD_TAB (N_("/Add Tab/Undo History..."), "gimp-undo-history",
"<StockItem>", GTK_STOCK_UNDO),
MENU_SEPARATOR ("/Add Tab/---"),

View File

@ -43,7 +43,6 @@ static const GimpDialogFactoryEntry toplevel_entries[] =
{ "gimp-device-status-dialog", dialogs_device_status_get, 32, TRUE, TRUE, FALSE, TRUE },
{ "gimp-preferences-dialog", dialogs_preferences_get, 32, TRUE, FALSE, FALSE, TRUE },
{ "gimp-module-browser-dialog", dialogs_module_browser_get, 32, TRUE, FALSE, FALSE, TRUE },
{ "gimp-undo-history-dialog", dialogs_undo_history_get, 32, FALSE, FALSE, FALSE, TRUE },
{ "gimp-display-filters-dialog", dialogs_display_filters_get, 32, FALSE, FALSE, FALSE, TRUE },
{ "gimp-tips-dialog", dialogs_tips_get, 32, TRUE, FALSE, FALSE, TRUE },
{ "gimp-about-dialog", dialogs_about_get, 32, TRUE, FALSE, FALSE, TRUE }
@ -76,6 +75,7 @@ static const GimpDialogFactoryEntry dock_entries[] =
{ "gimp-path-list", dialogs_path_list_view_new, 32, TRUE, FALSE, FALSE, TRUE },
{ "gimp-indexed-palette", dialogs_indexed_palette_new, 32, FALSE, FALSE, FALSE, TRUE },
{ "gimp-selection-editor", dialogs_selection_editor_new, 0, FALSE, FALSE, FALSE, TRUE },
{ "gimp-undo-history", dialogs_undo_history_new, 0, FALSE, FALSE, FALSE, TRUE },
{ "gimp-color-editor", dialogs_color_editor_new, 0, FALSE, FALSE, FALSE, TRUE },

View File

@ -466,13 +466,6 @@ GimpItemFactoryEntry image_menu_entries[] =
NULL,
"layers/flatten_image.html", NULL },
MENU_SEPARATOR ("/Image/---"),
{ { N_("/Image/Undo History..."), NULL,
dialogs_create_toplevel_cmd_callback, 0 },
"gimp-undo-history-dialog",
"dialogs/undo_history.html", NULL },
/* <Image>/Layer */
/* <Image>/Layer/Stack */
@ -707,6 +700,11 @@ GimpItemFactoryEntry image_menu_entries[] =
"<StockItem>", GIMP_STOCK_NAVIGATION },
"gimp-navigation-view",
NULL, NULL },
{ { N_("/Dialogs/Undo History..."), NULL,
dialogs_create_dockable_cmd_callback, 0,
"<StockItem>", GTK_STOCK_UNDO },
"gimp-undo-history",
NULL, NULL },
MENU_SEPARATOR ("/Dialogs/---"),
@ -1277,7 +1275,6 @@ image_menu_update (GtkItemFactory *item_factory,
SET_SENSITIVE ("/Image/Duplicate", gdisp);
SET_SENSITIVE ("/Image/Merge Visible Layers...", gdisp && !fs && !aux && lp);
SET_SENSITIVE ("/Image/Flatten Image", gdisp && !fs && !aux && lp);
SET_SENSITIVE ("/Image/Undo History...", gdisp);
/* Layer */

View File

@ -125,6 +125,11 @@ GimpItemFactoryEntry toolbox_menu_entries[] =
"<StockItem>", GIMP_STOCK_NAVIGATION },
"gimp-navigation-view",
NULL, NULL },
{ { N_("/File/Dialogs/Undo History..."), NULL,
dialogs_create_dockable_cmd_callback, 0,
"<StockItem>", GTK_STOCK_UNDO },
"gimp-undo-history",
NULL, NULL },
MENU_SEPARATOR ("/File/Dialogs/---"),

View File

@ -466,13 +466,6 @@ GimpItemFactoryEntry image_menu_entries[] =
NULL,
"layers/flatten_image.html", NULL },
MENU_SEPARATOR ("/Image/---"),
{ { N_("/Image/Undo History..."), NULL,
dialogs_create_toplevel_cmd_callback, 0 },
"gimp-undo-history-dialog",
"dialogs/undo_history.html", NULL },
/* <Image>/Layer */
/* <Image>/Layer/Stack */
@ -707,6 +700,11 @@ GimpItemFactoryEntry image_menu_entries[] =
"<StockItem>", GIMP_STOCK_NAVIGATION },
"gimp-navigation-view",
NULL, NULL },
{ { N_("/Dialogs/Undo History..."), NULL,
dialogs_create_dockable_cmd_callback, 0,
"<StockItem>", GTK_STOCK_UNDO },
"gimp-undo-history",
NULL, NULL },
MENU_SEPARATOR ("/Dialogs/---"),
@ -1277,7 +1275,6 @@ image_menu_update (GtkItemFactory *item_factory,
SET_SENSITIVE ("/Image/Duplicate", gdisp);
SET_SENSITIVE ("/Image/Merge Visible Layers...", gdisp && !fs && !aux && lp);
SET_SENSITIVE ("/Image/Flatten Image", gdisp && !fs && !aux && lp);
SET_SENSITIVE ("/Image/Undo History...", gdisp);
/* Layer */

View File

@ -125,6 +125,11 @@ GimpItemFactoryEntry toolbox_menu_entries[] =
"<StockItem>", GIMP_STOCK_NAVIGATION },
"gimp-navigation-view",
NULL, NULL },
{ { N_("/File/Dialogs/Undo History..."), NULL,
dialogs_create_dockable_cmd_callback, 0,
"<StockItem>", GTK_STOCK_UNDO },
"gimp-undo-history",
NULL, NULL },
MENU_SEPARATOR ("/File/Dialogs/---"),

View File

@ -44,11 +44,9 @@ struct _PaintUndo
};
static gboolean undo_pop_paint (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
static void undo_free_paint (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode);
gboolean
@ -86,7 +84,6 @@ gimp_paint_core_push_undo (GimpImage *gimage,
static gboolean
undo_pop_paint (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum)
{
@ -109,7 +106,6 @@ undo_pop_paint (GimpUndo *undo,
static void
undo_free_paint (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode)
{
PaintUndo *pu;

View File

@ -54,11 +54,9 @@ struct _TransformUndo
};
static gboolean undo_pop_transform (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
static void undo_free_transform (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode);
gboolean
@ -102,13 +100,12 @@ gimp_transform_tool_push_undo (GimpImage *gimage,
static gboolean
undo_pop_transform (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum)
{
GimpTool *active_tool;
active_tool = tool_manager_get_active (gimage->gimp);
active_tool = tool_manager_get_active (undo->gimage->gimp);
if (GIMP_IS_TRANSFORM_TOOL (active_tool))
{
@ -118,7 +115,7 @@ undo_pop_transform (GimpUndo *undo,
tt = GIMP_TRANSFORM_TOOL (active_tool);
tu = (TransformUndo *) undo->data;
path_transform_do_undo (gimage, tu->path_undo);
path_transform_do_undo (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)
@ -156,7 +153,6 @@ undo_pop_transform (GimpUndo *undo,
static void
undo_free_transform (GimpUndo *undo,
GimpImage *gimage,
GimpUndoMode undo_mode)
{
TransformUndo * tu;
@ -165,6 +161,8 @@ undo_free_transform (GimpUndo *undo,
if (tu->original)
tile_manager_destroy (tu->original);
path_transform_free_undo (tu->path_undo);
g_free (tu);
}

View File

@ -1,917 +0,0 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995-1999 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.
*
* Undo history browser by Austin Donnelly <austin@gimp.org>
*/
/* TODO:
*
* - reuse the L&C previews?
* Currently we use gimp_image_construct_composite_preview ()
* which makes use of the preview_cache on a per layer basis.
*
* - work out which (if any) is the clean image, and mark it as such.
* Currently, it's on the wrong line.
*
* - undo names are less than useful. This isn't a problem with
* undo_history.c itself, more with the rather chaotic way
* people have of picking an undo type when pushing undos, and
* inconsistent use of undo groups. Maybe rather than
* specifying an (enum) type, it should be a const char * ?
*
* BUGS:
* - clean pixmap in wrong place
*
* Initial rev 0.01, (c) 19 Sept 1999 Austin Donnelly <austin@gimp.org>
*
*/
#include "config.h"
#ifdef __GNUC__
#warning GTK_DISABLE_DEPRECATED
#endif
#undef GTK_DISABLE_DEPRECATED
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "display/display-types.h"
#include "config/gimpcoreconfig.h"
#include "base/pixel-region.h"
#include "base/temp-buf.h"
#include "paint-funcs/paint-funcs.h"
#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpdrawable.h"
#include "core/gimpimage.h"
#include "core/gimpimage-mask.h"
#include "core/gimpimage-undo.h"
#include "core/gimpundostack.h"
#include "file/file-utils.h"
#include "widgets/gimpviewabledialog.h"
#include "libgimp/gimpintl.h"
#include "pixmaps/yes.xpm"
#include "pixmaps/question.xpm"
typedef struct
{
GimpImage *gimage; /* image we're tracking undo info for */
GtkWidget *shell; /* dialog window */
GtkWidget *clist; /* list of undo actions */
GtkWidget *undo_button; /* button to undo an operation */
GtkWidget *redo_button; /* button to redo an operation */
int old_selection; /* previous selection in the clist */
int preview_size; /* size of the previews (from preferences) */
} undo_history_st;
typedef struct
{
GtkCList *clist;
gint row;
gint size;
GimpImage *gimage;
} idle_preview_args;
/*
* Theory of operation.
*
* Keep a clist. Each row of the clist corresponds to an image as it
* was at some time in the past, present or future. The selected row
* is the present image. Rows below the selected one are in the
* future - as redo operations are performed, they become the current
* image. Rows above the selected one are in the past - undo
* operations move the highlight up.
*
* The slight fly in the ointment is that if rows are images, then how
* should they be labelled? An undo or redo operation goes _between_
* two image states - it isn't an image state. It's a pretty
* arbitrary decision, but I've chosen to label a row with the name of
* the action that brought the image into the state represented by
* that row. Thus, there is a special first row without a meaningful
* label, which represents the image state before the first action has
* been done to it. The choice is between a special first row or a
* special last row. Since people mostly work near the leading edge,
* not often going all the way back, I've chosen to put the special
* case out of common sight.
*
* So, the undo stack contents appear above the selected row, and the
* redo stack below it.
*
* The clist is initialised by mapping over the undo and redo stack.
*
* Once initialised, the dialog listens to undo_event signals from the
* gimage. These undo events allow us to track changes to the undo
* and redo stacks. We follow the events, making parallel changes to
* the clist. If we ever get out of sync, there is no mechanism to
* notice or re-sync. A few g_return_if_fails should catch some of
* these cases.
*
* User clicks changing the selected row in the clist turn into
* multiple calls to undo_pop or undo_redo, with appropriate signals
* blocked so we don't get our own events back.
*
* The "Close" button hides the dialog, rather than destroying it.
* This may well need to be changed, since the dialog will continue to
* track updates, and if it's generating previews this might take too
* long for large images.
*
* The dialog is destroyed when the gimage it is tracking is
* destroyed. Note that a File/Revert destroys the current gimage and
* so blows the undo/redo stacks.
*
* --austin, 19/9/1999
*/
/**************************************************************/
/* Static Data */
static GdkPixmap *clean_pixmap = NULL;
static GdkBitmap *clean_mask = NULL;
static GdkPixmap *clear_pixmap = NULL;
static GdkBitmap *clear_mask = NULL;
static void undo_history_undo_event (GtkWidget *widget,
gint ev,
gpointer data);
static void undo_history_clean_callback (GtkWidget *widget,
gpointer data);
static void undo_history_select_row_callback (GtkWidget *widget,
gint row,
gint column,
gpointer event,
gpointer data);
/**************************************************************/
/* Local functions */
static MaskBuf *
mask_render_preview (GimpImage *gimage,
gint *pwidth,
gint *pheight)
{
GimpChannel *mask;
MaskBuf *scaled_buf = NULL;
PixelRegion srcPR, destPR;
gint subsample;
gint width, height;
gint scale;
mask = gimp_image_get_mask (gimage);
if ((gimp_drawable_width (GIMP_DRAWABLE(mask)) > *pwidth) ||
(gimp_drawable_height (GIMP_DRAWABLE(mask)) > *pheight))
{
if (((gfloat) gimp_drawable_width (GIMP_DRAWABLE (mask)) / (gfloat) *pwidth) >
((gfloat) gimp_drawable_height (GIMP_DRAWABLE (mask)) / (gfloat) *pheight))
{
width = *pwidth;
height = (gimp_drawable_height (GIMP_DRAWABLE (mask)) * (*pwidth)) / gimp_drawable_width (GIMP_DRAWABLE (mask));
}
else
{
width = (gimp_drawable_width (GIMP_DRAWABLE (mask)) * (*pheight)) / gimp_drawable_height (GIMP_DRAWABLE (mask));
height = *pheight;
}
scale = TRUE;
}
else
{
width = gimp_drawable_width (GIMP_DRAWABLE (mask));
height = gimp_drawable_height (GIMP_DRAWABLE (mask));
scale = FALSE;
}
/* if the mask is empty, no need to scale and update again */
if (gimp_image_mask_is_empty (gimage))
return NULL;
if (scale)
{
/* calculate 'acceptable' subsample */
subsample = 1;
while ((width * (subsample + 1) * 2 < gimp_drawable_width (GIMP_DRAWABLE (mask))) &&
(height * (subsample + 1) * 2 < gimp_drawable_height (GIMP_DRAWABLE (mask))))
subsample = subsample + 1;
pixel_region_init (&srcPR, gimp_drawable_data (GIMP_DRAWABLE (mask)),
0, 0,
gimp_drawable_width (GIMP_DRAWABLE (mask)),
gimp_drawable_height (GIMP_DRAWABLE (mask)), FALSE);
scaled_buf = mask_buf_new (width, height);
destPR.bytes = 1;
destPR.x = 0;
destPR.y = 0;
destPR.w = width;
destPR.h = height;
destPR.rowstride = srcPR.bytes * width;
destPR.data = mask_buf_data (scaled_buf);
destPR.tiles = NULL;
subsample_region (&srcPR, &destPR, subsample);
}
else
{
pixel_region_init (&srcPR, gimp_drawable_data (GIMP_DRAWABLE (mask)),
0, 0,
gimp_drawable_width (GIMP_DRAWABLE (mask)),
gimp_drawable_height (GIMP_DRAWABLE (mask)), FALSE);
scaled_buf = mask_buf_new (width, height);
destPR.bytes = 1;
destPR.x = 0;
destPR.y = 0;
destPR.w = width;
destPR.h = height;
destPR.rowstride = srcPR.bytes * width;
destPR.data = mask_buf_data (scaled_buf);
destPR.tiles = NULL;
copy_region (&srcPR, &destPR);
}
*pheight = height;
*pwidth = width;
return scaled_buf;
}
static gint
undo_history_set_pixmap_idle (gpointer data)
{
idle_preview_args *idle = data;
static GdkGC *gc = NULL;
TempBuf *buf = NULL;
GdkPixmap *pixmap;
GimpUndoType utype;
MaskBuf *mbuf = NULL;
guchar *src;
gdouble r, g, b, a;
gdouble c0, c1;
guchar *p0, *p1, *even, *odd;
gint width, height, bpp;
gint x, y;
if (!gc)
gc = gdk_gc_new (GTK_WIDGET (idle->clist)->window);
width = idle->gimage->width;
height = idle->gimage->height;
/* Get right aspect ratio */
if (width > height)
{
height = (gint)(((gdouble)idle->size * (gdouble)height) / (gdouble)width + 0.5);
width = (gint)(((gdouble)width * (gdouble)height)/ (gdouble)idle->gimage->height + 0.5);
}
else
{
width = (gint)(((gdouble)idle->size * (gdouble)width) / (gdouble)height + 0.5);
height = (gint)(((gdouble)height * (gdouble)width ) /(gdouble) idle->gimage->width + 0.5);
}
utype = gimp_undo_stack_peek (idle->gimage->undo_stack)->undo_type;
if ((utype != GIMP_UNDO_MASK && utype != GIMP_UNDO_IMAGE_QMASK) ||
(mbuf = mask_render_preview (idle->gimage, &width, &height)) == NULL)
{
buf = gimp_viewable_get_new_preview (GIMP_VIEWABLE (idle->gimage),
width,
height);
bpp = buf->bytes;
src = temp_buf_data (buf);
}
else
{
src = mask_buf_data (mbuf);
bpp = 1; /* Always the case for masks */
}
pixmap = gdk_pixmap_new (GTK_WIDGET (idle->clist)->window,
width + 2, height + 2,
-1);
gdk_draw_rectangle (pixmap,
GTK_WIDGET (idle->clist)->style->black_gc,
TRUE,
0, 0,
width + 2, height + 2);
even = g_malloc (width * 3);
odd = g_malloc (width * 3);
for (y = 0; y < height; y++)
{
p0 = even;
p1 = odd;
for (x = 0; x < width; x++)
{
if (bpp == 4)
{
r = ((gdouble) src[x*4+0]) / 255.0;
g = ((gdouble) src[x*4+1]) / 255.0;
b = ((gdouble) src[x*4+2]) / 255.0;
a = ((gdouble) src[x*4+3]) / 255.0;
}
else if (bpp == 3)
{
r = ((gdouble) src[x*3+0]) / 255.0;
g = ((gdouble) src[x*3+1]) / 255.0;
b = ((gdouble) src[x*3+2]) / 255.0;
a = 1.0;
}
else
{
r = ((gdouble) src[x*bpp+0]) / 255.0;
g = b = r;
if (bpp == 2)
a = ((gdouble) src[x*bpp+1]) / 255.0;
else
a = 1.0;
}
if ((x / GIMP_CHECK_SIZE_SM) & 1)
{
c0 = GIMP_CHECK_LIGHT;
c1 = GIMP_CHECK_DARK;
}
else
{
c0 = GIMP_CHECK_DARK;
c1 = GIMP_CHECK_LIGHT;
}
*p0++ = (c0 + (r - c0) * a) * 255.0;
*p0++ = (c0 + (g - c0) * a) * 255.0;
*p0++ = (c0 + (b - c0) * a) * 255.0;
*p1++ = (c1 + (r - c1) * a) * 255.0;
*p1++ = (c1 + (g - c1) * a) * 255.0;
*p1++ = (c1 + (b - c1) * a) * 255.0;
}
if ((y / GIMP_CHECK_SIZE_SM) & 1)
{
gdk_draw_rgb_image (pixmap, gc,
1, y + 1,
width, 1,
GDK_RGB_DITHER_NORMAL,
(guchar *) odd, 3);
}
else
{
gdk_draw_rgb_image (pixmap, gc,
1, y + 1,
width, 1,
GDK_RGB_DITHER_NORMAL,
(guchar *) even, 3);
}
src += width * bpp;
}
g_free (even);
g_free (odd);
if (buf)
temp_buf_free (buf);
if (mbuf)
mask_buf_free (mbuf);
gtk_clist_set_row_data (idle->clist, idle->row, (gpointer)2);
gtk_clist_set_pixmap (idle->clist, idle->row, 0, pixmap, NULL);
g_object_unref (pixmap);
return (FALSE);
}
/* check if a preview is already made, otherwise g_idle_add the pixmap func */
static void
undo_history_set_pixmap (GtkCList *clist,
gint row,
gint size,
GimpImage *gimage)
{
static idle_preview_args idle;
if (!size || (GPOINTER_TO_INT (gtk_clist_get_row_data (clist, row))) == 2)
return;
idle.clist = clist;
idle.row = row;
idle.size = size;
idle.gimage = gimage;
g_idle_add (undo_history_set_pixmap_idle, &idle);
}
/* close button clicked */
static void
undo_history_close_callback (GtkWidget *widget,
gpointer data)
{
undo_history_st *st = data;
gtk_widget_hide (GTK_WIDGET (st->shell));
}
/* The gimage and shell destroy callbacks are split so we can:
* a) blow the shell when the image dissappears
* b) disconnect from the image if the shell dissappears (we don't
* want signals from the image to carry on using "st" once it's
* been freed.
*/
/* gimage renamed */
static void
undo_history_gimage_rename_callback (GimpImage *gimage,
gpointer data)
{
undo_history_st *st = data;
gchar *basename;
gchar *title;
basename = g_path_get_basename (gimp_image_get_uri (gimage));
title = g_strdup_printf (_("Undo History: %s"), basename);
g_free (basename);
gtk_window_set_title (GTK_WINDOW (st->shell), title);
g_free (title);
}
static void
undo_history_shell_destroy_callback (GtkWidget *widget,
gpointer data)
{
undo_history_st *st = data;
if (st->gimage)
{
g_signal_handlers_disconnect_by_func (st->gimage,
undo_history_undo_event,
st);
g_signal_handlers_disconnect_by_func (st->gimage,
undo_history_gimage_rename_callback,
st);
g_signal_handlers_disconnect_by_func (st->gimage,
undo_history_clean_callback,
st);
g_object_unref (st->gimage);
}
g_free (st);
}
/* undo button clicked */
static void
undo_history_undo_callback (GtkWidget *widget,
gpointer data)
{
undo_history_st *st = data;
if (gimp_image_undo (st->gimage))
gimp_image_flush (st->gimage);
}
/* redo button clicked */
static void
undo_history_redo_callback (GtkWidget *widget,
gpointer data)
{
undo_history_st *st = data;
if (gimp_image_redo (st->gimage))
gimp_image_flush (st->gimage);
}
/* Always start clist with dummy entry for image state before
* the first action on the undo stack */
static void
undo_history_prepend_special (GtkCList *clist)
{
gchar *name = _("[ base image ]");
gchar *namelist[3];
gint row;
namelist[0] = NULL;
namelist[1] = NULL;
namelist[2] = name;
row = gtk_clist_prepend (clist, namelist);
}
/* Recalculate which of the undo and redo buttons are meant to be sensitive */
static void
undo_history_set_sensitive (undo_history_st *st,
gint rows)
{
gtk_widget_set_sensitive (st->undo_button, (st->old_selection != 0));
gtk_widget_set_sensitive (st->redo_button, (st->old_selection != rows-1));
}
/* Track undo_event signals, telling us of changes to the undo and
* redo stacks. */
static void
undo_history_undo_event (GtkWidget *widget,
gint ev,
gpointer data)
{
undo_history_st *st = data;
undo_event_t event = ev;
const gchar *name;
gchar *namelist[3];
GList *list;
gint cur_selection;
GtkCList *clist;
gint row;
GdkPixmap *pixmap;
GdkBitmap *mask;
list = GTK_CLIST (st->clist)->selection;
g_return_if_fail (list != NULL);
cur_selection = GPOINTER_TO_INT (list->data);
clist = GTK_CLIST (st->clist);
/* block select events */
g_signal_handlers_block_by_func (st->clist,
undo_history_select_row_callback,
st);
switch (event)
{
case UNDO_PUSHED:
/* clip everything after the current selection (ie, the
* actions that are from the redo stack) */
gtk_clist_freeze (clist);
while (clist->rows > cur_selection + 1)
gtk_clist_remove (clist, cur_selection + 1);
/* find out what's new */
name = gimp_object_get_name (GIMP_OBJECT (gimp_undo_stack_peek (st->gimage->undo_stack)));
namelist[0] = NULL;
namelist[1] = NULL;
namelist[2] = (char *) name;
row = gtk_clist_append (clist, namelist);
g_assert (clist->rows == cur_selection + 2);
undo_history_set_pixmap (clist, row, st->preview_size, st->gimage);
/* always force selection to bottom, and scroll to it */
gtk_clist_select_row (clist, clist->rows - 1, -1);
gtk_clist_thaw (clist);
gtk_clist_moveto (clist, clist->rows - 1, 0, 1.0, 0.0);
cur_selection = clist->rows - 1;
break;
case UNDO_EXPIRED:
/* remove earliest row, but not our special first one */
if (gtk_clist_get_pixmap (clist, 1, 0, &pixmap, &mask))
gtk_clist_set_pixmap (clist, 0, 0, pixmap, mask);
gtk_clist_remove (clist, 1);
break;
case UNDO_POPPED:
/* move hilight up one */
g_return_if_fail (cur_selection >= 1);
gtk_clist_select_row (clist, cur_selection - 1, -1);
cur_selection--;
undo_history_set_pixmap (clist, cur_selection, st->preview_size, st->gimage);
if ( !(gtk_clist_row_is_visible (clist, cur_selection) & GTK_VISIBILITY_FULL))
gtk_clist_moveto (clist, cur_selection, 0, 0.0, 0.0);
break;
case UNDO_REDO:
/* move hilight down one */
g_return_if_fail (cur_selection+1 < clist->rows);
gtk_clist_select_row (clist, cur_selection+1, -1);
cur_selection++;
undo_history_set_pixmap (clist, cur_selection, st->preview_size, st->gimage);
if ( !(gtk_clist_row_is_visible (clist, cur_selection) & GTK_VISIBILITY_FULL))
gtk_clist_moveto (clist, cur_selection, 0, 1.0, 0.0);
break;
case UNDO_FREE:
/* clear all info other that the special first line */
gtk_clist_freeze (clist);
gtk_clist_clear (clist);
undo_history_prepend_special (clist);
gtk_clist_thaw (clist);
cur_selection = 0;
break;
}
/* if the image is clean, set the clean pixmap */
if (st->gimage->dirty == 0)
gtk_clist_set_pixmap (clist, cur_selection, 1, clean_pixmap, clean_mask);
g_signal_handlers_unblock_by_func (st->clist,
undo_history_select_row_callback,
st);
st->old_selection = cur_selection;
undo_history_set_sensitive (st, clist->rows);
}
static void
undo_history_select_row_callback (GtkWidget *widget,
gint row,
gint column,
gpointer event,
gpointer data)
{
undo_history_st *st = data;
gint cur_selection;
cur_selection = row;
if (cur_selection == st->old_selection)
return;
/* Disable undo_event signals while we do these multiple undo or
* redo actions. */
g_signal_handlers_block_by_func (st->gimage,
undo_history_undo_event, st);
while (cur_selection < st->old_selection)
{
gimp_image_undo (st->gimage);
st->old_selection--;
}
while (cur_selection > st->old_selection)
{
gimp_image_redo (st->gimage);
st->old_selection++;
}
gimp_image_flush (st->gimage);
undo_history_set_pixmap (GTK_CLIST (widget),
cur_selection, st->preview_size, st->gimage);
/* if the image is clean, set the clean pixmap */
if (st->gimage->dirty == 0)
gtk_clist_set_pixmap (GTK_CLIST (widget),
cur_selection, 1, clean_pixmap, clean_mask);
g_signal_handlers_unblock_by_func (st->gimage,
undo_history_undo_event, st);
undo_history_set_sensitive (st, GTK_CLIST(st->clist)->rows);
}
static void
undo_history_clean_callback (GtkWidget *widget,
gpointer data)
{
undo_history_st *st = data;
gint i;
gint nrows;
GtkCList *clist;
if (st->gimage->dirty != 0)
return;
/*
* The image has become clean. Remove the clean_pixmap from
* all entries. It will be set in the undo_event or select_row
* callbacks.
* Ugly, but works better than before. The actual problem is
* that the "clean" signal is emitted before UNDO_POPPED event,
* so we can not simply set the clean pixmap here.
*/
clist = GTK_CLIST (st->clist);
nrows = clist->rows;
gtk_clist_freeze (clist);
for (i=0; i < nrows; i++)
gtk_clist_set_text (clist, i, 1, NULL);
gtk_clist_thaw (clist);
}
/* Used to build up initial contents of clist */
static void
undo_history_init_undo (gpointer undo,
gpointer data)
{
undo_history_st *st = data;
gchar *namelist[3];
gint row;
namelist[0] = NULL;
namelist[1] = NULL;
namelist[2] = (gchar *) gimp_object_get_name (GIMP_OBJECT (undo));
row = gtk_clist_prepend (GTK_CLIST (st->clist), namelist);
gtk_clist_set_pixmap (GTK_CLIST (st->clist), row, 0,
clear_pixmap, clear_mask);
}
static void
undo_history_init_redo (gpointer undo,
gpointer data)
{
undo_history_st *st = data;
gchar *namelist[3];
gint row;
namelist[0] = NULL; namelist[1] = NULL;
namelist[1] = NULL;
namelist[2] = (gchar *) gimp_object_get_name (GIMP_OBJECT (undo));
row = gtk_clist_append (GTK_CLIST (st->clist), namelist);
gtk_clist_set_pixmap (GTK_CLIST (st->clist), row, 0,
clear_pixmap, clear_mask);
}
/*************************************************************/
/* Publicly exported function */
GtkWidget *
undo_history_new (GimpImage *gimage)
{
undo_history_st *st;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *button;
GtkWidget *scrolled_win;
st = g_new0 (undo_history_st, 1);
st->gimage = gimage;
g_object_ref (gimage);
st->preview_size = gimage->gimp->config->preview_size;
/* gimage signals */
g_signal_connect (gimage, "undo_event",
G_CALLBACK (undo_history_undo_event),
st);
g_signal_connect (gimage, "name_changed",
G_CALLBACK (undo_history_gimage_rename_callback),
st);
g_signal_connect (gimage, "clean",
G_CALLBACK (undo_history_clean_callback),
st);
/* The shell and main vbox */
st->shell =
gimp_viewable_dialog_new (GIMP_VIEWABLE (gimage),
_("Undo History"), "undo_history",
GTK_STOCK_UNDO,
_("Image Undo History"),
gimp_standard_help_func,
"dialogs/undo_history.html",
GTK_STOCK_CLOSE, undo_history_close_callback,
st, NULL, NULL, TRUE, TRUE,
NULL);
vbox = gtk_vbox_new (FALSE, 2);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (st->shell)->vbox), vbox);
gtk_widget_show (vbox);
g_signal_connect (st->shell, "destroy",
G_CALLBACK (undo_history_shell_destroy_callback),
st);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_usize (GTK_WIDGET (scrolled_win),
160 + st->preview_size,
4 * (MAX (st->preview_size, 16) + 6));
/* clist of undo actions */
st->clist = gtk_clist_new (3);
gtk_clist_set_selection_mode (GTK_CLIST (st->clist), GTK_SELECTION_BROWSE);
gtk_clist_set_reorderable (GTK_CLIST (st->clist), FALSE);
gtk_clist_set_row_height (GTK_CLIST (st->clist), MAX (st->preview_size, 16) + 4);
gtk_clist_set_column_width (GTK_CLIST (st->clist), 0, st->preview_size + 2);
gtk_clist_set_column_width (GTK_CLIST (st->clist), 1, 18);
gtk_clist_set_column_min_width (GTK_CLIST (st->clist), 2, 64);
/* allocate the pixmaps if not already done */
if (!clean_pixmap)
{
GtkStyle *style;
gtk_widget_realize (st->shell);
style = gtk_widget_get_style (st->shell);
clean_pixmap =
gdk_pixmap_create_from_xpm_d (st->shell->window,
&clean_mask,
&style->bg[GTK_STATE_NORMAL],
yes_xpm);
clear_pixmap =
gdk_pixmap_create_from_xpm_d (st->shell->window,
&clear_mask,
&style->bg[GTK_STATE_NORMAL],
question_xpm);
}
/* work out the initial contents */
gimp_container_foreach (GIMP_CONTAINER (st->gimage->undo_stack->undos),
undo_history_init_undo, st);
/* force selection to bottom */
gtk_clist_select_row (GTK_CLIST (st->clist),
GTK_CLIST (st->clist)->rows - 1, -1);
gimp_container_foreach (GIMP_CONTAINER (st->gimage->redo_stack->undos),
undo_history_init_redo, st);
undo_history_prepend_special (GTK_CLIST (st->clist));
st->old_selection = GPOINTER_TO_INT(GTK_CLIST(st->clist)->selection->data);
/* draw the preview of the current state */
undo_history_set_pixmap (GTK_CLIST (st->clist),
st->old_selection, st->preview_size, st->gimage);
g_signal_connect (st->clist, "select_row",
G_CALLBACK (undo_history_select_row_callback),
st);
/* if the image is clean, set the clean pixmap */
if (st->gimage->dirty == 0)
gtk_clist_set_pixmap (GTK_CLIST (st->clist), st->old_selection, 1, clean_pixmap, clean_mask);
gtk_widget_show (GTK_WIDGET (st->clist));
gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
gtk_widget_show (GTK_WIDGET (scrolled_win));
gtk_container_add (GTK_CONTAINER (scrolled_win), st->clist);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_POLICY_NEVER,
GTK_POLICY_ALWAYS);
hbox = gtk_hbox_new (FALSE, 6);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
st->undo_button = button = gtk_button_new_from_stock (GTK_STOCK_UNDO);
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (undo_history_undo_callback),
st);
gtk_widget_show (GTK_WIDGET (button));
st->redo_button = button = gtk_button_new_from_stock (GTK_STOCK_REDO);
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (undo_history_redo_callback),
st);
gtk_widget_show (GTK_WIDGET (button));
undo_history_set_sensitive (st, GTK_CLIST (st->clist)->rows);
gtk_widget_show (GTK_WIDGET (st->shell));
gtk_clist_moveto (GTK_CLIST (st->clist), st->old_selection, 0, 0.5, 0.0);
return st->shell;
}

View File

@ -1,26 +0,0 @@
/* 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.
*/
#ifndef __UNDO_HISTORY_H__
#define __UNDO_HISTORY_H__
GtkWidget * undo_history_new (GimpImage *gimage);
#endif /* __UNDO_HISTORY_H__ */

View File

@ -151,6 +151,10 @@ libappwidgets_a_sources = \
gimptoolbox-indicator-area.h \
gimptoolinfopreview.c \
gimptoolinfopreview.h \
gimpundoeditor.c \
gimpundoeditor.h \
gimpundopreview.c \
gimpundopreview.h \
gimpvectorslistview.c \
gimpvectorslistview.h \
gimpviewabledialog.c \

View File

@ -1141,16 +1141,11 @@ gimp_dnd_data_type_get_by_g_type (GType type)
{
dnd_type = GIMP_DND_TYPE_TOOL;
}
else
{
g_warning ("%s(): unsupported GType \"%s\"",
G_GNUC_FUNCTION, g_type_name (type));
}
return dnd_type;
}
void
gboolean
gimp_dnd_drag_source_set_by_type (GtkWidget *widget,
GdkModifierType start_button_mask,
GType type,
@ -1158,20 +1153,22 @@ gimp_dnd_drag_source_set_by_type (GtkWidget *widget,
{
GimpDndType dnd_type;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
if (dnd_type == GIMP_DND_TYPE_NONE)
return;
return FALSE;
gtk_drag_source_set (widget, start_button_mask,
&dnd_data_defs[dnd_type].target_entry,
1,
actions);
return TRUE;
}
void
gboolean
gimp_dnd_drag_dest_set_by_type (GtkWidget *widget,
GtkDestDefaults flags,
GType type,
@ -1179,20 +1176,22 @@ gimp_dnd_drag_dest_set_by_type (GtkWidget *widget,
{
GimpDndType dnd_type;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
if (dnd_type == GIMP_DND_TYPE_NONE)
return;
return FALSE;
gtk_drag_dest_set (widget, flags,
&dnd_data_defs[dnd_type].target_entry,
1,
actions);
return TRUE;
}
void
gboolean
gimp_dnd_viewable_source_set (GtkWidget *widget,
GType type,
GimpDndDragViewableFunc get_viewable_func,
@ -1200,36 +1199,40 @@ gimp_dnd_viewable_source_set (GtkWidget *widget,
{
GimpDndType dnd_type;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (get_viewable_func != NULL);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_return_val_if_fail (get_viewable_func != NULL, FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
if (dnd_type == GIMP_DND_TYPE_NONE)
return;
return FALSE;
gimp_dnd_data_source_set (dnd_type, widget,
G_CALLBACK (get_viewable_func),
data);
return TRUE;
}
void
gboolean
gimp_dnd_viewable_source_unset (GtkWidget *widget,
GType type)
{
GimpDndType dnd_type;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
if (dnd_type == GIMP_DND_TYPE_NONE)
return;
return FALSE;
gimp_dnd_data_source_unset (widget);
return TRUE;
}
void
gboolean
gimp_dnd_viewable_dest_add (GtkWidget *widget,
GType type,
GimpDndDropViewableFunc set_viewable_func,
@ -1237,33 +1240,37 @@ gimp_dnd_viewable_dest_add (GtkWidget *widget,
{
GimpDndType dnd_type;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (set_viewable_func != NULL);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
g_return_val_if_fail (set_viewable_func != NULL, FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
if (dnd_type == GIMP_DND_TYPE_NONE)
return;
return FALSE;
gimp_dnd_data_dest_add (dnd_type, widget,
G_CALLBACK (set_viewable_func),
data);
data);
return TRUE;
}
void
gboolean
gimp_dnd_viewable_dest_remove (GtkWidget *widget,
GType type)
{
GimpDndType dnd_type;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
dnd_type = gimp_dnd_data_type_get_by_g_type (type);
if (dnd_type == GIMP_DND_TYPE_NONE)
return;
return FALSE;
gimp_dnd_data_dest_remove (dnd_type, widget);
return TRUE;
}
GimpViewable *

View File

@ -145,30 +145,30 @@ typedef GimpViewable * (* GimpDndDragViewableFunc) (GtkWidget *widget,
gpointer data);
void gimp_dnd_drag_source_set_by_type (GtkWidget *widget,
GdkModifierType start_button_mask,
GType type,
GdkDragAction actions);
void gimp_dnd_viewable_source_set (GtkWidget *widget,
GType type,
GimpDndDragViewableFunc get_viewable_func,
gpointer data);
void gimp_dnd_viewable_source_unset (GtkWidget *widget,
GType type);
gboolean gimp_dnd_drag_source_set_by_type (GtkWidget *widget,
GdkModifierType start_button_mask,
GType type,
GdkDragAction actions);
gboolean gimp_dnd_viewable_source_set (GtkWidget *widget,
GType type,
GimpDndDragViewableFunc get_viewable_func,
gpointer data);
gboolean gimp_dnd_viewable_source_unset (GtkWidget *widget,
GType type);
void gimp_dnd_drag_dest_set_by_type (GtkWidget *widget,
GtkDestDefaults flags,
GType type,
GdkDragAction actions);
gboolean gimp_dnd_drag_dest_set_by_type (GtkWidget *widget,
GtkDestDefaults flags,
GType type,
GdkDragAction actions);
void gimp_dnd_viewable_dest_add (GtkWidget *widget,
GType type,
GimpDndDropViewableFunc set_viewable_func,
gpointer data);
void gimp_dnd_viewable_dest_remove (GtkWidget *widget,
GType type);
gboolean gimp_dnd_viewable_dest_add (GtkWidget *widget,
GType type,
GimpDndDropViewableFunc set_viewable_func,
gpointer data);
gboolean gimp_dnd_viewable_dest_remove (GtkWidget *widget,
GType type);
GimpViewable * gimp_dnd_get_drag_data (GtkWidget *widget);
GimpViewable * gimp_dnd_get_drag_data (GtkWidget *widget);
#endif /* __GIMP_DND_H__ */

View File

@ -34,6 +34,7 @@
#include "core/gimppalette.h"
#include "core/gimppattern.h"
#include "core/gimptoolinfo.h"
#include "core/gimpundo.h"
#include "gimpbrushpreview.h"
#include "gimpbufferpreview.h"
@ -44,6 +45,7 @@
#include "gimppalettepreview.h"
#include "gimppatternpreview.h"
#include "gimptoolinfopreview.h"
#include "gimpundopreview.h"
GType
@ -90,6 +92,10 @@ gimp_preview_type_from_viewable_type (GType viewable_type)
{
type = GIMP_TYPE_IMAGEFILE_PREVIEW;
}
else if (g_type_is_a (viewable_type, GIMP_TYPE_UNDO))
{
type = GIMP_TYPE_UNDO_PREVIEW;
}
return type;
}

View File

@ -630,17 +630,28 @@ gimp_preview_set_viewable (GimpPreview *preview,
g_signal_handlers_disconnect_by_func (preview->viewable,
G_CALLBACK (gimp_preview_size_changed),
preview);
if (! viewable && ! preview->is_popup)
{
if (gimp_dnd_viewable_source_unset (GTK_WIDGET (preview),
G_TYPE_FROM_INSTANCE (preview->viewable)))
{
gtk_drag_source_unset (GTK_WIDGET (preview));
}
}
}
else if (viewable && ! preview->is_popup)
{
gimp_dnd_drag_source_set_by_type (GTK_WIDGET (preview),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
if (gimp_dnd_drag_source_set_by_type (GTK_WIDGET (preview),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
viewable_type,
GDK_ACTION_COPY))
{
gimp_dnd_viewable_source_set (GTK_WIDGET (preview),
viewable_type,
GDK_ACTION_COPY);
gimp_dnd_viewable_source_set (GTK_WIDGET (preview),
viewable_type,
gimp_preview_drag_viewable,
NULL);
gimp_preview_drag_viewable,
NULL);
}
}
preview->viewable = viewable;

View File

@ -630,17 +630,28 @@ gimp_preview_set_viewable (GimpPreview *preview,
g_signal_handlers_disconnect_by_func (preview->viewable,
G_CALLBACK (gimp_preview_size_changed),
preview);
if (! viewable && ! preview->is_popup)
{
if (gimp_dnd_viewable_source_unset (GTK_WIDGET (preview),
G_TYPE_FROM_INSTANCE (preview->viewable)))
{
gtk_drag_source_unset (GTK_WIDGET (preview));
}
}
}
else if (viewable && ! preview->is_popup)
{
gimp_dnd_drag_source_set_by_type (GTK_WIDGET (preview),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
if (gimp_dnd_drag_source_set_by_type (GTK_WIDGET (preview),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
viewable_type,
GDK_ACTION_COPY))
{
gimp_dnd_viewable_source_set (GTK_WIDGET (preview),
viewable_type,
GDK_ACTION_COPY);
gimp_dnd_viewable_source_set (GTK_WIDGET (preview),
viewable_type,
gimp_preview_drag_viewable,
NULL);
gimp_preview_drag_viewable,
NULL);
}
}
preview->viewable = viewable;

View File

@ -22,8 +22,6 @@
#include "gimpeditor.h"
#include "gui/gui-types.h" /* temp hack */
#define GIMP_TYPE_SELECTION_EDITOR (gimp_selection_editor_get_type ())
#define GIMP_SELECTION_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_SELECTION_EDITOR, GimpSelectionEditor))

View File

@ -0,0 +1,422 @@
/* 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 <gtk/gtk.h>
#include "libgimpwidgets/gimpwidgets.h"
#include "widgets-types.h"
#include "core/gimp.h"
#include "core/gimplist.h"
#include "core/gimpimage.h"
#include "core/gimpimage-undo.h"
#include "core/gimpundostack.h"
#include "gimpcontainerlistview.h"
#include "gimpundoeditor.h"
#include "libgimp/gimpintl.h"
static void gimp_undo_editor_class_init (GimpUndoEditorClass *klass);
static void gimp_undo_editor_init (GimpUndoEditor *undo_editor);
static void gimp_undo_editor_destroy (GtkObject *object);
static void gimp_undo_editor_undo_clicked (GtkWidget *widget,
GimpUndoEditor *editor);
static void gimp_undo_editor_redo_clicked (GtkWidget *widget,
GimpUndoEditor *editor);
static void gimp_undo_editor_undo_event (GimpImage *gimage,
GimpUndoEvent event,
GimpUndo *undo,
GimpUndoEditor *editor);
static void gimp_undo_editor_select_item (GimpContainerView *view,
GimpUndo *undo,
gpointer insert_data,
GimpUndoEditor *editor);
static GimpEditorClass *parent_class = NULL;
GType
gimp_undo_editor_get_type (void)
{
static GType editor_type = 0;
if (! editor_type)
{
static const GTypeInfo editor_info =
{
sizeof (GimpUndoEditorClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gimp_undo_editor_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GimpUndoEditor),
0, /* n_preallocs */
(GInstanceInitFunc) gimp_undo_editor_init,
};
editor_type = g_type_register_static (GIMP_TYPE_EDITOR,
"GimpUndoEditor",
&editor_info, 0);
}
return editor_type;
}
static void
gimp_undo_editor_class_init (GimpUndoEditorClass *klass)
{
GtkObjectClass *object_class;
object_class = GTK_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->destroy = gimp_undo_editor_destroy;
}
static void
gimp_undo_editor_init (GimpUndoEditor *undo_editor)
{
undo_editor->gimage = NULL;
undo_editor->container = NULL;
undo_editor->view = gimp_container_list_view_new (NULL,
NULL,
GIMP_PREVIEW_SIZE_MEDIUM,
FALSE, 3, 3);
gtk_box_pack_start (GTK_BOX (undo_editor), undo_editor->view, TRUE, TRUE, 0);
gtk_widget_show (undo_editor->view);
g_signal_connect (undo_editor->view, "select_item",
G_CALLBACK (gimp_undo_editor_select_item),
undo_editor);
undo_editor->undo_button =
gimp_editor_add_button (GIMP_EDITOR (undo_editor),
GTK_STOCK_UNDO,
_("Undo"), NULL,
G_CALLBACK (gimp_undo_editor_undo_clicked),
NULL,
undo_editor);
undo_editor->redo_button =
gimp_editor_add_button (GIMP_EDITOR (undo_editor),
GTK_STOCK_REDO,
_("Redo"), NULL,
G_CALLBACK (gimp_undo_editor_redo_clicked),
NULL,
undo_editor);
gtk_widget_set_sensitive (GTK_WIDGET (undo_editor), FALSE);
}
static void
gimp_undo_editor_destroy (GtkObject *object)
{
GimpUndoEditor *editor;
editor = GIMP_UNDO_EDITOR (object);
if (editor->gimage)
gimp_undo_editor_set_image (editor, NULL);
GTK_OBJECT_CLASS (parent_class)->destroy (object);
}
/* public functions */
GtkWidget *
gimp_undo_editor_new (GimpImage *gimage)
{
GimpUndoEditor *editor;
g_return_val_if_fail (! gimage || GIMP_IS_IMAGE (gimage), NULL);
editor = g_object_new (GIMP_TYPE_UNDO_EDITOR, NULL);
gimp_undo_editor_set_image (editor, gimage);
return GTK_WIDGET (editor);
}
void
gimp_undo_editor_set_image (GimpUndoEditor *editor,
GimpImage *gimage)
{
g_return_if_fail (GIMP_IS_UNDO_EDITOR (editor));
g_return_if_fail (! gimage || GIMP_IS_IMAGE (gimage));
if (gimage == editor->gimage)
return;
if (editor->gimage)
{
gimp_container_view_set_container (GIMP_CONTAINER_VIEW (editor->view),
NULL);
g_object_unref (editor->container);
editor->container = NULL;
g_object_unref (editor->base_item);
editor->base_item = NULL;
g_signal_handlers_disconnect_by_func (editor->gimage,
gimp_undo_editor_undo_event,
editor);
}
else if (gimage)
{
gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
}
editor->gimage = gimage;
if (gimage)
{
GimpUndo *top_undo_item;
GimpUndo *top_redo_item;
GList *list;
/* create a container as model for the undo history list */
editor->container = gimp_list_new (GIMP_TYPE_UNDO,
GIMP_CONTAINER_POLICY_STRONG);
editor->base_item = gimp_undo_new (gimage,
GIMP_UNDO_GROUP_NONE,
_("[ Base Image ]"),
NULL, 0, FALSE, NULL, NULL);
/* the list prepends its items, so first add the redo items... */
for (list = GIMP_LIST (gimage->redo_stack->undos)->list;
list;
list = g_list_next (list))
{
gimp_container_add (editor->container, GIMP_OBJECT (list->data));
}
/* ...reverse the list so the redo items are in ascending order... */
gimp_list_reverse (GIMP_LIST (editor->container));
/* ...then add the undo items in descending order... */
for (list = GIMP_LIST (gimage->undo_stack->undos)->list;
list;
list = g_list_next (list))
{
gimp_container_add (editor->container, GIMP_OBJECT (list->data));
}
/* ...finally, the first item is the special "base_item" which stands
* for the image with no more undos available to pop
*/
gimp_container_add (editor->container, GIMP_OBJECT (editor->base_item));
/* display the container */
gimp_container_view_set_container (GIMP_CONTAINER_VIEW (editor->view),
editor->container);
/* get the top item of both stacks */
top_undo_item = gimp_undo_stack_peek (gimage->undo_stack);
top_redo_item = gimp_undo_stack_peek (gimage->redo_stack);
gtk_widget_set_sensitive (editor->undo_button, top_undo_item != NULL);
gtk_widget_set_sensitive (editor->redo_button, top_redo_item != NULL);
g_signal_handlers_block_by_func (editor->view,
gimp_undo_editor_select_item,
editor);
/* select the current state of the image */
if (top_undo_item)
{
gimp_container_view_select_item (GIMP_CONTAINER_VIEW (editor->view),
GIMP_VIEWABLE (top_undo_item));
gimp_undo_create_preview (top_undo_item, FALSE);
}
else
{
gimp_container_view_select_item (GIMP_CONTAINER_VIEW (editor->view),
GIMP_VIEWABLE (editor->base_item));
gimp_undo_create_preview (editor->base_item, TRUE);
}
g_signal_handlers_unblock_by_func (editor->view,
gimp_undo_editor_select_item,
editor);
g_signal_connect (gimage, "undo_event",
G_CALLBACK (gimp_undo_editor_undo_event),
editor);
}
else
{
gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
}
}
static void
gimp_undo_editor_undo_clicked (GtkWidget *widget,
GimpUndoEditor *editor)
{
if (editor->gimage)
{
if (gimp_image_undo (editor->gimage))
gimp_image_flush (editor->gimage);
}
}
static void
gimp_undo_editor_redo_clicked (GtkWidget *widget,
GimpUndoEditor *editor)
{
if (editor->gimage)
{
if (gimp_image_redo (editor->gimage))
gimp_image_flush (editor->gimage);
}
}
static void
gimp_undo_editor_undo_event (GimpImage *gimage,
GimpUndoEvent event,
GimpUndo *undo,
GimpUndoEditor *editor)
{
GimpUndo *top_undo_item;
GimpUndo *top_redo_item;
top_undo_item = gimp_undo_stack_peek (gimage->undo_stack);
top_redo_item = gimp_undo_stack_peek (gimage->redo_stack);
g_signal_handlers_block_by_func (editor->view,
gimp_undo_editor_select_item,
editor);
switch (event)
{
case GIMP_UNDO_EVENT_UNDO_PUSHED:
gimp_container_insert (editor->container, GIMP_OBJECT (undo), -1);
gimp_container_view_select_item (GIMP_CONTAINER_VIEW (editor->view),
GIMP_VIEWABLE (undo));
gimp_undo_create_preview (undo, FALSE);
break;
case GIMP_UNDO_EVENT_UNDO_EXPIRED:
case GIMP_UNDO_EVENT_REDO_EXPIRED:
gimp_container_remove (editor->container, GIMP_OBJECT (undo));
break;
case GIMP_UNDO_EVENT_UNDO:
if (top_undo_item)
{
gimp_container_view_select_item (GIMP_CONTAINER_VIEW (editor->view),
GIMP_VIEWABLE (top_undo_item));
gimp_undo_create_preview (top_undo_item, FALSE);
}
else
{
gimp_container_view_select_item (GIMP_CONTAINER_VIEW (editor->view),
GIMP_VIEWABLE (editor->base_item));
gimp_undo_create_preview (editor->base_item, TRUE);
}
break;
case GIMP_UNDO_EVENT_REDO:
gimp_container_view_select_item (GIMP_CONTAINER_VIEW (editor->view),
GIMP_VIEWABLE (top_undo_item));
gimp_undo_create_preview (top_undo_item, FALSE);
break;
case GIMP_UNDO_EVENT_UNDO_FREE:
gimp_undo_editor_set_image (editor, NULL);
break;
}
g_signal_handlers_unblock_by_func (editor->view,
gimp_undo_editor_select_item,
editor);
gtk_widget_set_sensitive (editor->undo_button, top_undo_item != NULL);
gtk_widget_set_sensitive (editor->redo_button, top_redo_item != NULL);
}
static void
gimp_undo_editor_select_item (GimpContainerView *view,
GimpUndo *undo,
gpointer insert_data,
GimpUndoEditor *editor)
{
GimpUndo *top_undo_item;
GimpUndo *top_redo_item;
top_undo_item = gimp_undo_stack_peek (editor->gimage->undo_stack);
if (undo == editor->base_item)
{
/* the base_image was selected, pop all available undo items
*/
while (top_undo_item != NULL)
{
gimp_image_undo (editor->gimage);
top_undo_item = gimp_undo_stack_peek (editor->gimage->undo_stack);
}
}
else if (gimp_container_have (editor->gimage->undo_stack->undos,
GIMP_OBJECT (undo)))
{
/* the selected item is on the undo stack, pop undos until it
* is on the of the undo stack
*/
while (top_undo_item != undo)
{
gimp_image_undo (editor->gimage);
top_undo_item = gimp_undo_stack_peek (editor->gimage->undo_stack);
}
}
else if (gimp_container_have (editor->gimage->redo_stack->undos,
GIMP_OBJECT (undo)))
{
/* the selected item is on the redo stack, pop redos until it
* is on top of the undo stack
*/
while (top_undo_item != undo)
{
gimp_image_redo (editor->gimage);
top_undo_item = gimp_undo_stack_peek (editor->gimage->undo_stack);
}
}
gimp_image_flush (editor->gimage);
top_redo_item = gimp_undo_stack_peek (editor->gimage->redo_stack);
gtk_widget_set_sensitive (editor->undo_button, top_undo_item != NULL);
gtk_widget_set_sensitive (editor->redo_button, top_redo_item != NULL);
}

View File

@ -0,0 +1,64 @@
/* 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.
*/
#ifndef __GIMP_UNDO_EDITOR_H__
#define __GIMP_UNDO_EDITOR_H__
#include "gimpeditor.h"
#define GIMP_TYPE_UNDO_EDITOR (gimp_undo_editor_get_type ())
#define GIMP_UNDO_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UNDO_EDITOR, GimpUndoEditor))
#define GIMP_UNDO_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_UNDO_EDITOR, GimpUndoEditorClass))
#define GIMP_IS_UNDO_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_UNDO_EDITOR))
#define GIMP_IS_UNDO_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_UNDO_EDITOR))
#define GIMP_UNDO_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_UNDO_EDITOR, GimpUndoEditorClass))
typedef struct _GimpUndoEditorClass GimpUndoEditorClass;
struct _GimpUndoEditor
{
GimpEditor parent_instance;
GimpImage *gimage;
GimpContainer *container;
GtkWidget *view;
GimpUndo *base_item;
GtkWidget *undo_button;
GtkWidget *redo_button;
};
struct _GimpUndoEditorClass
{
GimpEditorClass parent_class;
};
GType gimp_undo_editor_get_type (void) G_GNUC_CONST;
GtkWidget * gimp_undo_editor_new (GimpImage *gimage);
void gimp_undo_editor_set_image (GimpUndoEditor *editor,
GimpImage *gimage);
#endif /* __GIMP_UNDO_EDITOR_H__ */

View File

@ -0,0 +1,161 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpUndoPreview Widget
* Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
*
* 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 <gtk/gtk.h>
#include "widgets-types.h"
#include "base/temp-buf.h"
#include "core/gimpundo.h"
#include "gimpundopreview.h"
static void gimp_undo_preview_class_init (GimpUndoPreviewClass *klass);
static void gimp_undo_preview_init (GimpUndoPreview *preview);
static void gimp_undo_preview_render (GimpPreview *preview);
static GimpPreviewClass *parent_class = NULL;
GType
gimp_undo_preview_get_type (void)
{
static GType preview_type = 0;
if (! preview_type)
{
static const GTypeInfo preview_info =
{
sizeof (GimpUndoPreviewClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) gimp_undo_preview_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GimpUndoPreview),
0, /* n_preallocs */
(GInstanceInitFunc) gimp_undo_preview_init,
};
preview_type = g_type_register_static (GIMP_TYPE_PREVIEW,
"GimpUndoPreview",
&preview_info, 0);
}
return preview_type;
}
static void
gimp_undo_preview_class_init (GimpUndoPreviewClass *klass)
{
GimpPreviewClass *preview_class;
preview_class = GIMP_PREVIEW_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
preview_class->render = gimp_undo_preview_render;
}
static void
gimp_undo_preview_init (GimpUndoPreview *preview)
{
}
static void
gimp_undo_preview_render (GimpPreview *preview)
{
GimpUndo *undo;
gint width;
gint height;
gint preview_width;
gint preview_height;
gboolean scaling_up;
TempBuf *render_buf;
gboolean free_buf = FALSE;
undo = GIMP_UNDO (preview->viewable);
width = preview->width;
height = preview->height;
render_buf = gimp_viewable_get_preview (preview->viewable, width, height);
if (! render_buf)
return;
gimp_preview_calc_size (preview,
render_buf->width,
render_buf->height,
width,
height,
1.0, 1.0,
&preview_width,
&preview_height,
&scaling_up);
if (preview_width != render_buf->width ||
preview_height != render_buf->height)
{
render_buf = temp_buf_scale (render_buf, preview_width, preview_height);
free_buf = TRUE;
}
if (preview_width < width) render_buf->x = (width - preview_width) / 2;
if (preview_height < height) render_buf->y = (height - preview_height) / 2;
if (render_buf->x || render_buf->y)
{
TempBuf *temp_buf;
guchar white[4] = { 255, 255, 255, 255 };
temp_buf = temp_buf_new (width, height,
render_buf->bytes,
0, 0,
white);
temp_buf_copy_area (render_buf, temp_buf,
0, 0,
render_buf->width,
render_buf->height,
render_buf->x,
render_buf->y);
if (free_buf)
temp_buf_free (render_buf);
render_buf = temp_buf;
free_buf = TRUE;
}
gimp_preview_render_and_flush (preview, render_buf, -1);
if (free_buf)
temp_buf_free (render_buf);
}

View File

@ -0,0 +1,51 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* GimpUndoPreview Widget
* Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
*
* 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.
*/
#ifndef __GIMP_UNDO_PREVIEW_H__
#define __GIMP_UNDO_PREVIEW_H__
#include "gimppreview.h"
#define GIMP_TYPE_UNDO_PREVIEW (gimp_undo_preview_get_type ())
#define GIMP_UNDO_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_UNDO_PREVIEW, GimpUndoPreview))
#define GIMP_UNDO_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_UNDO_PREVIEW, GimpUndoPreviewClass))
#define GIMP_IS_UNDO_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GIMP_TYPE_UNDO_PREVIEW))
#define GIMP_IS_UNDO_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_UNDO_PREVIEW))
#define GIMP_UNDO_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_UNDO_PREVIEW, GimpUndoPreviewClass))
typedef struct _GimpUndoPreviewClass GimpUndoPreviewClass;
struct _GimpUndoPreview
{
GimpPreview parent_instance;
};
struct _GimpUndoPreviewClass
{
GimpPreviewClass parent_class;
};
GType gimp_undo_preview_get_type (void) G_GNUC_CONST;
#endif /* __GIMP_UNDO_PREVIEW_H__ */

View File

@ -630,17 +630,28 @@ gimp_preview_set_viewable (GimpPreview *preview,
g_signal_handlers_disconnect_by_func (preview->viewable,
G_CALLBACK (gimp_preview_size_changed),
preview);
if (! viewable && ! preview->is_popup)
{
if (gimp_dnd_viewable_source_unset (GTK_WIDGET (preview),
G_TYPE_FROM_INSTANCE (preview->viewable)))
{
gtk_drag_source_unset (GTK_WIDGET (preview));
}
}
}
else if (viewable && ! preview->is_popup)
{
gimp_dnd_drag_source_set_by_type (GTK_WIDGET (preview),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
if (gimp_dnd_drag_source_set_by_type (GTK_WIDGET (preview),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
viewable_type,
GDK_ACTION_COPY))
{
gimp_dnd_viewable_source_set (GTK_WIDGET (preview),
viewable_type,
GDK_ACTION_COPY);
gimp_dnd_viewable_source_set (GTK_WIDGET (preview),
viewable_type,
gimp_preview_drag_viewable,
NULL);
gimp_preview_drag_viewable,
NULL);
}
}
preview->viewable = viewable;

View File

@ -630,17 +630,28 @@ gimp_preview_set_viewable (GimpPreview *preview,
g_signal_handlers_disconnect_by_func (preview->viewable,
G_CALLBACK (gimp_preview_size_changed),
preview);
if (! viewable && ! preview->is_popup)
{
if (gimp_dnd_viewable_source_unset (GTK_WIDGET (preview),
G_TYPE_FROM_INSTANCE (preview->viewable)))
{
gtk_drag_source_unset (GTK_WIDGET (preview));
}
}
}
else if (viewable && ! preview->is_popup)
{
gimp_dnd_drag_source_set_by_type (GTK_WIDGET (preview),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
if (gimp_dnd_drag_source_set_by_type (GTK_WIDGET (preview),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
viewable_type,
GDK_ACTION_COPY))
{
gimp_dnd_viewable_source_set (GTK_WIDGET (preview),
viewable_type,
GDK_ACTION_COPY);
gimp_dnd_viewable_source_set (GTK_WIDGET (preview),
viewable_type,
gimp_preview_drag_viewable,
NULL);
gimp_preview_drag_viewable,
NULL);
}
}
preview->viewable = viewable;

View File

@ -38,16 +38,17 @@ typedef struct _GimpMenuFactory GimpMenuFactory;
/* widgets */
typedef struct _GimpPreview GimpPreview;
typedef struct _GimpImagePreview GimpImagePreview;
typedef struct _GimpDrawablePreview GimpDrawablePreview;
typedef struct _GimpImagefilePreview GimpImagefilePreview;
typedef struct _GimpBrushPreview GimpBrushPreview;
typedef struct _GimpNavigationPreview GimpNavigationPreview;
typedef struct _GimpPatternPreview GimpPatternPreview;
typedef struct _GimpPalettePreview GimpPalettePreview;
typedef struct _GimpGradientPreview GimpGradientPreview;
typedef struct _GimpToolInfoPreview GimpToolInfoPreview;
typedef struct _GimpBufferPreview GimpBufferPreview;
typedef struct _GimpDrawablePreview GimpDrawablePreview;
typedef struct _GimpGradientPreview GimpGradientPreview;
typedef struct _GimpImagePreview GimpImagePreview;
typedef struct _GimpImagefilePreview GimpImagefilePreview;
typedef struct _GimpNavigationPreview GimpNavigationPreview;
typedef struct _GimpPalettePreview GimpPalettePreview;
typedef struct _GimpPatternPreview GimpPatternPreview;
typedef struct _GimpToolInfoPreview GimpToolInfoPreview;
typedef struct _GimpUndoPreview GimpUndoPreview;
typedef struct _GimpContainerMenu GimpContainerMenu;
typedef struct _GimpContainerMenuImpl GimpContainerMenuImpl;
@ -63,6 +64,7 @@ typedef struct _GimpBrushEditor GimpBrushEditor;
typedef struct _GimpGradientEditor GimpGradientEditor;
typedef struct _GimpPaletteEditor GimpPaletteEditor;
typedef struct _GimpSelectionEditor GimpSelectionEditor;
typedef struct _GimpUndoEditor GimpUndoEditor;
typedef struct _GimpContainerView GimpContainerView;
typedef struct _GimpContainerListView GimpContainerListView;