From 29ddc678d374f296c7df21402598b9907fc30666 Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Tue, 25 Nov 2003 16:12:53 +0000 Subject: [PATCH] added GIMP_UNDO_EVENT_UNDO_FREEZE and GIMP_UNDO_EVENT_UNDO_THAW. 2003-11-25 Michael Natterer * app/core/core-enums.[ch]: added GIMP_UNDO_EVENT_UNDO_FREEZE and GIMP_UNDO_EVENT_UNDO_THAW. * app/core/gimpimage.c: emit undo events in gimp_image_undo_freeze() and gimp_image_undo_thaw(). * app/widgets/gimpundoeditor.c: made it aware of FREEZE/THAW signals and robust against evil stuff like freezing/thawing the undo in the middle of an open undo group. Fixes bug #124421. * plug-ins/script-fu/scripts/circuit.scm: push and undo group instead of disabling/enabling undo. --- ChangeLog | 15 ++ app/core/core-enums.c | 2 + app/core/core-enums.h | 4 +- app/core/gimpimage.c | 9 +- app/widgets/gimpundoeditor.c | 223 +++++++++++++++---------- plug-ins/script-fu/scripts/circuit.scm | 7 +- 6 files changed, 167 insertions(+), 93 deletions(-) diff --git a/ChangeLog b/ChangeLog index 156ee7eb47..221f463578 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2003-11-25 Michael Natterer + + * app/core/core-enums.[ch]: added GIMP_UNDO_EVENT_UNDO_FREEZE + and GIMP_UNDO_EVENT_UNDO_THAW. + + * app/core/gimpimage.c: emit undo events in + gimp_image_undo_freeze() and gimp_image_undo_thaw(). + + * app/widgets/gimpundoeditor.c: made it aware of FREEZE/THAW signals + and robust against evil stuff like freezing/thawing the undo + in the middle of an open undo group. Fixes bug #124421. + + * plug-ins/script-fu/scripts/circuit.scm: push and undo group + instead of disabling/enabling undo. + 2003-11-25 Sven Neumann * app/gui/dialogs.c (toplevel_entries): remember the size of the diff --git a/app/core/core-enums.c b/app/core/core-enums.c index bd5e480eab..e6750566fd 100644 --- a/app/core/core-enums.c +++ b/app/core/core-enums.c @@ -530,6 +530,8 @@ static const GEnumValue gimp_undo_event_enum_values[] = { 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" }, + { GIMP_UNDO_EVENT_UNDO_FREEZE, "GIMP_UNDO_EVENT_UNDO_FREEZE", "undo-freeze" }, + { GIMP_UNDO_EVENT_UNDO_THAW, "GIMP_UNDO_EVENT_UNDO_THAW", "undo-thaw" }, { 0, NULL, NULL } }; diff --git a/app/core/core-enums.h b/app/core/core-enums.h index 10ee8361e6..3537dcd637 100644 --- a/app/core/core-enums.h +++ b/app/core/core-enums.h @@ -370,7 +370,9 @@ typedef enum /*< pdb-skip >*/ 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 */ + GIMP_UNDO_EVENT_UNDO_FREE, /* all undo and redo info has been cleared */ + GIMP_UNDO_EVENT_UNDO_FREEZE, /* undo has been frozen */ + GIMP_UNDO_EVENT_UNDO_THAW /* undo has been thawn */ } GimpUndoEvent; diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c index f5d55b101b..8cd79ac120 100644 --- a/app/core/gimpimage.c +++ b/app/core/gimpimage.c @@ -1475,6 +1475,8 @@ gimp_image_undo_freeze (GimpImage *gimage) gimage->undo_on = FALSE; + gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_UNDO_FREEZE, NULL); + return TRUE; } @@ -1485,6 +1487,8 @@ gimp_image_undo_thaw (GimpImage *gimage) gimage->undo_on = TRUE; + gimp_image_undo_event (gimage, GIMP_UNDO_EVENT_UNDO_THAW, NULL); + return TRUE; } @@ -1502,7 +1506,10 @@ gimp_image_undo_event (GimpImage *gimage, GimpUndo *undo) { g_return_if_fail (GIMP_IS_IMAGE (gimage)); - g_return_if_fail (event == GIMP_UNDO_EVENT_UNDO_FREE || GIMP_IS_UNDO (undo)); + g_return_if_fail (((event == GIMP_UNDO_EVENT_UNDO_FREE || + event == GIMP_UNDO_EVENT_UNDO_FREEZE || + event == GIMP_UNDO_EVENT_UNDO_THAW) && undo == NULL) || + GIMP_IS_UNDO (undo)); g_signal_emit (gimage, gimp_image_signals[UNDO_EVENT], 0, event, undo); } diff --git a/app/widgets/gimpundoeditor.c b/app/widgets/gimpundoeditor.c index a99f81679d..24cf585ac8 100644 --- a/app/widgets/gimpundoeditor.c +++ b/app/widgets/gimpundoeditor.c @@ -43,6 +43,9 @@ static void gimp_undo_editor_init (GimpUndoEditor *undo_editor); static void gimp_undo_editor_set_image (GimpImageEditor *editor, GimpImage *gimage); +static void gimp_undo_editor_fill (GimpUndoEditor *editor); +static void gimp_undo_editor_clear (GimpUndoEditor *editor); + static void gimp_undo_editor_undo_clicked (GtkWidget *widget, GimpImageEditor *editor); static void gimp_undo_editor_redo_clicked (GtkWidget *widget, @@ -138,19 +141,11 @@ static void gimp_undo_editor_set_image (GimpImageEditor *image_editor, GimpImage *gimage) { - GimpUndoEditor *editor; - - editor = GIMP_UNDO_EDITOR (image_editor); + GimpUndoEditor *editor = GIMP_UNDO_EDITOR (image_editor); if (image_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; + gimp_undo_editor_clear (editor); g_signal_handlers_disconnect_by_func (image_editor->gimage, gimp_undo_editor_undo_event, @@ -159,78 +154,11 @@ gimp_undo_editor_set_image (GimpImageEditor *image_editor, GIMP_IMAGE_EDITOR_CLASS (parent_class)->set_image (image_editor, gimage); - if (gimage) + if (image_editor->gimage) { - GimpUndo *top_undo_item; - GimpUndo *top_redo_item; - GList *list; + gimp_undo_editor_fill (editor); - /* 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_signal_connect (image_editor->gimage, "undo_event", G_CALLBACK (gimp_undo_editor_undo_event), editor); } @@ -257,6 +185,107 @@ gimp_undo_editor_new (GimpImage *gimage) /* private functions */ +static void +gimp_undo_editor_fill (GimpUndoEditor *editor) +{ + GimpImage *gimage; + GimpUndo *top_undo_item; + GimpUndo *top_redo_item; + GList *list; + + gimage = GIMP_IMAGE_EDITOR (editor)->gimage; + + /* 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)) + { + /* Don't add the topmost item if it is an open undo group, + * it will be added upon closing of the group. + */ + if (list->prev || ! GIMP_IS_UNDO_STACK (list->data) || + gimage->pushing_undo_group == GIMP_UNDO_GROUP_NONE) + { + 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); +} + +static void +gimp_undo_editor_clear (GimpUndoEditor *editor) +{ + if (editor->container) + { + gimp_container_view_set_container (GIMP_CONTAINER_VIEW (editor->view), + NULL); + g_object_unref (editor->container); + editor->container = NULL; + } + + if (editor->base_item) + { + g_object_unref (editor->base_item); + editor->base_item = NULL; + } +} + static void gimp_undo_editor_undo_clicked (GtkWidget *widget, GimpImageEditor *editor) @@ -291,17 +320,21 @@ gimp_undo_editor_undo_event (GimpImage *gimage, 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: + g_signal_handlers_block_by_func (editor->view, + gimp_undo_editor_select_item, + editor); + 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); + + g_signal_handlers_unblock_by_func (editor->view, + gimp_undo_editor_select_item, + editor); break; case GIMP_UNDO_EVENT_UNDO_EXPIRED: @@ -311,6 +344,10 @@ gimp_undo_editor_undo_event (GimpImage *gimage, case GIMP_UNDO_EVENT_UNDO: case GIMP_UNDO_EVENT_REDO: + g_signal_handlers_block_by_func (editor->view, + gimp_undo_editor_select_item, + editor); + if (top_undo_item) { gimp_container_view_select_item (GIMP_CONTAINER_VIEW (editor->view), @@ -323,17 +360,25 @@ gimp_undo_editor_undo_event (GimpImage *gimage, 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); break; case GIMP_UNDO_EVENT_UNDO_FREE: - gimp_image_editor_set_image (GIMP_IMAGE_EDITOR (editor), NULL); + gimp_undo_editor_clear (editor); + break; + + case GIMP_UNDO_EVENT_UNDO_FREEZE: + gimp_undo_editor_clear (editor); + break; + + case GIMP_UNDO_EVENT_UNDO_THAW: + gimp_undo_editor_fill (editor); 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); } diff --git a/plug-ins/script-fu/scripts/circuit.scm b/plug-ins/script-fu/scripts/circuit.scm index b7e079530d..cd986a07e6 100644 --- a/plug-ins/script-fu/scripts/circuit.scm +++ b/plug-ins/script-fu/scripts/circuit.scm @@ -43,7 +43,8 @@ (old-fg (car (gimp-palette-get-foreground))) ) - (gimp-image-undo-disable image) + (gimp-undo-push-group-start image) + (gimp-layer-add-alpha drawable) (if (= (car (gimp-selection-is-empty image)) TRUE) @@ -116,9 +117,11 @@ (if (= keep-selection FALSE) (gimp-selection-none image)) - (gimp-image-undo-enable image) (gimp-image-remove-channel image active-selection) (gimp-image-set-active-layer image drawable) + + (gimp-undo-push-group-end image) + (gimp-displays-flush))) (script-fu-register "script-fu-circuit"