diff --git a/app/core/core-enums.c b/app/core/core-enums.c index 827f0b5f11..6e4708b1b1 100644 --- a/app/core/core-enums.c +++ b/app/core/core-enums.c @@ -1229,6 +1229,7 @@ gimp_undo_type_get_type (void) { GIMP_UNDO_GROUP_PARASITE_ATTACH, "GIMP_UNDO_GROUP_PARASITE_ATTACH", "group-parasite-attach" }, { GIMP_UNDO_GROUP_PARASITE_REMOVE, "GIMP_UNDO_GROUP_PARASITE_REMOVE", "group-parasite-remove" }, { GIMP_UNDO_GROUP_PATHS_IMPORT, "GIMP_UNDO_GROUP_PATHS_IMPORT", "group-paths-import" }, + { GIMP_UNDO_GROUP_FILTER_VISIBILITY, "GIMP_UNDO_GROUP_FILTER_VISIBILITY", "group-filter-visibility" }, { GIMP_UNDO_GROUP_MISC, "GIMP_UNDO_GROUP_MISC", "group-misc" }, { GIMP_UNDO_IMAGE_TYPE, "GIMP_UNDO_IMAGE_TYPE", "image-type" }, { GIMP_UNDO_IMAGE_PRECISION, "GIMP_UNDO_IMAGE_PRECISION", "image-precision" }, @@ -1288,6 +1289,7 @@ gimp_undo_type_get_type (void) { GIMP_UNDO_FILTER_REMOVE, "GIMP_UNDO_FILTER_REMOVE", "filter-remove" }, { GIMP_UNDO_FILTER_REORDER, "GIMP_UNDO_FILTER_REORDER", "filter-reorder" }, { GIMP_UNDO_FILTER_MODIFIED, "GIMP_UNDO_FILTER_MODIFIED", "filter-modified" }, + { GIMP_UNDO_FILTER_VISIBILITY, "GIMP_UNDO_FILTER_VISIBILITY", "filter-visibility" }, { GIMP_UNDO_CANT, "GIMP_UNDO_CANT", "cant" }, { 0, NULL, NULL } }; @@ -1342,6 +1344,7 @@ gimp_undo_type_get_type (void) { GIMP_UNDO_GROUP_PARASITE_ATTACH, NC_("undo-type", "Attach parasite"), NULL }, { GIMP_UNDO_GROUP_PARASITE_REMOVE, NC_("undo-type", "Remove parasite"), NULL }, { GIMP_UNDO_GROUP_PATHS_IMPORT, NC_("undo-type", "Import paths"), NULL }, + { GIMP_UNDO_GROUP_FILTER_VISIBILITY, NC_("undo-type", "Effects visibility"), NULL }, { GIMP_UNDO_GROUP_MISC, NC_("undo-type", "Plug-In"), NULL }, { GIMP_UNDO_IMAGE_TYPE, NC_("undo-type", "Image type"), NULL }, { GIMP_UNDO_IMAGE_PRECISION, NC_("undo-type", "Image precision"), NULL }, @@ -1401,6 +1404,7 @@ gimp_undo_type_get_type (void) { GIMP_UNDO_FILTER_REMOVE, NC_("undo-type", "Remove effect"), NULL }, { GIMP_UNDO_FILTER_REORDER, NC_("undo-type", "Reorder effect"), NULL }, { GIMP_UNDO_FILTER_MODIFIED, NC_("undo-type", "Effect modification"), NULL }, + { GIMP_UNDO_FILTER_VISIBILITY, NC_("undo-type", "Effect visibility"), NULL }, { GIMP_UNDO_CANT, NC_("undo-type", "Not undoable"), NULL }, { 0, NULL, NULL } }; diff --git a/app/core/core-enums.h b/app/core/core-enums.h index 5db3dfd541..87808de60b 100644 --- a/app/core/core-enums.h +++ b/app/core/core-enums.h @@ -578,6 +578,7 @@ typedef enum /*< pdb-skip >*/ GIMP_UNDO_GROUP_PARASITE_ATTACH, /*< desc="Attach parasite" >*/ GIMP_UNDO_GROUP_PARASITE_REMOVE, /*< desc="Remove parasite" >*/ GIMP_UNDO_GROUP_PATHS_IMPORT, /*< desc="Import paths" >*/ + GIMP_UNDO_GROUP_FILTER_VISIBILITY, /*< desc="Effects visibility" >*/ GIMP_UNDO_GROUP_MISC, /*< desc="Plug-In" >*/ GIMP_UNDO_GROUP_LAST = GIMP_UNDO_GROUP_MISC, /*< skip >*/ @@ -642,6 +643,7 @@ typedef enum /*< pdb-skip >*/ GIMP_UNDO_FILTER_REMOVE, /*< desc="Remove effect" >*/ GIMP_UNDO_FILTER_REORDER, /*< desc="Reorder effect" >*/ GIMP_UNDO_FILTER_MODIFIED, /*< desc="Effect modification" >*/ + GIMP_UNDO_FILTER_VISIBILITY, /*< desc="Effect visibility" >*/ GIMP_UNDO_CANT /*< desc="Not undoable" >*/ } GimpUndoType; diff --git a/app/core/gimpdrawablefilterundo.c b/app/core/gimpdrawablefilterundo.c index a2d7522ec1..8ac3557c20 100644 --- a/app/core/gimpdrawablefilterundo.c +++ b/app/core/gimpdrawablefilterundo.c @@ -32,7 +32,9 @@ #include "gimpdrawablefilter.h" #include "gimpdrawablefilterundo.h" #include "gimpimage.h" +#include "gimpimage-undo.h" #include "gimpitem.h" +#include "gimpundostack.h" enum @@ -140,6 +142,7 @@ gimp_drawable_filter_undo_constructed (GObject *object) g_value_unset (&value); } + df_undo->active = gimp_filter_get_active (GIMP_FILTER (df_undo->filter)); df_undo->opacity = gimp_drawable_filter_get_opacity (df_undo->filter); df_undo->paint_mode = gimp_drawable_filter_get_paint_mode (df_undo->filter); df_undo->blend_space = gimp_drawable_filter_get_blend_space (df_undo->filter); @@ -267,6 +270,19 @@ gimp_drawable_filter_undo_pop (GimpUndo *undo, df_undo->row_index); gimp_drawable_update (drawable, 0, 0, -1, -1); } + else if (undo->undo_type == GIMP_UNDO_FILTER_VISIBILITY) + { + GimpFilter *gfilter; + gboolean active; + + gfilter = GIMP_FILTER (filter); + active = gimp_filter_get_active (gfilter); + + gimp_filter_set_active (gfilter, df_undo->active); + gimp_drawable_filter_apply (filter, NULL); + + df_undo->active = active; + } else if (undo->undo_type == GIMP_UNDO_FILTER_MODIFIED) { GeglNode *op; @@ -357,3 +373,52 @@ gimp_drawable_filter_undo_free (GimpUndo *undo, GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode); } + +/** + * Checks whether the undo step of toggling all filters in @filter_list can be + * compressed. It will be compressed if: + * - The previous undo is a group of filter visibility changes + * - The group has changes for every filters in the list + * - The group only has changes for filters from the list + */ +GimpUndo * +gimp_drawable_filter_undo_can_compress_visibility (GimpImage *image, + GList *filter_list) +{ + GimpUndo *undo; + GimpUndoStack *undo_stack; + gint n_items; + + g_return_val_if_fail (image != NULL, NULL); + g_return_val_if_fail (filter_list != NULL, NULL); + + undo = gimp_image_undo_can_compress (image, + GIMP_TYPE_UNDO_STACK, + GIMP_UNDO_GROUP_FILTER_VISIBILITY); + if (undo == NULL) + return NULL; + + undo_stack = GIMP_UNDO_STACK (undo); + n_items = gimp_container_get_n_children (undo_stack->undos); + + if (n_items != g_list_length (filter_list)) + return NULL; + + for (int i = 0; i < n_items; i++) + { + const GimpObject *sub_undo; + const GimpDrawableFilterUndo *sub_filter_undo; + + sub_undo = gimp_container_get_child_by_index (undo_stack->undos, i); + sub_filter_undo = GIMP_DRAWABLE_FILTER_UNDO (sub_undo); + + if (sub_filter_undo == NULL || + GIMP_UNDO (sub_undo)->undo_type != GIMP_UNDO_FILTER_VISIBILITY || + ! g_list_find (filter_list, sub_filter_undo->filter)) + { + return NULL; + } + } + + return undo; +} diff --git a/app/core/gimpdrawablefilterundo.h b/app/core/gimpdrawablefilterundo.h index 1298c4b318..733dda3389 100644 --- a/app/core/gimpdrawablefilterundo.h +++ b/app/core/gimpdrawablefilterundo.h @@ -39,6 +39,7 @@ struct _GimpDrawableFilterUndo GimpDrawableFilter *filter; gint row_index; + gboolean active; GeglNode *node; gdouble opacity; @@ -55,7 +56,10 @@ struct _GimpDrawableFilterUndoClass }; -GType gimp_drawable_filter_undo_get_type (void) G_GNUC_CONST; +GType gimp_drawable_filter_undo_get_type (void) G_GNUC_CONST; + +GimpUndo * gimp_drawable_filter_undo_can_compress_visibility (GimpImage *image, + GList *filter_list); #endif /* __GIMP_DRAWABLE_FILTER_UNDO_H__ */ diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c index 468146e13a..0cef34cd0c 100644 --- a/app/core/gimpimage-undo-push.c +++ b/app/core/gimpimage-undo-push.c @@ -384,6 +384,22 @@ gimp_image_undo_push_filter_modified (GimpImage *image, NULL); } +GimpUndo * +gimp_image_undo_push_filter_visibility (GimpImage *image, + const gchar *undo_desc, + GimpDrawable *drawable, + GimpDrawableFilter *filter) +{ + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL); + + return gimp_image_undo_push (image, GIMP_TYPE_DRAWABLE_FILTER_UNDO, + GIMP_UNDO_FILTER_VISIBILITY, undo_desc, + GIMP_DIRTY_DRAWABLE, + "filter", filter, + NULL); +} + /****************/ /* Mask Undos */ diff --git a/app/core/gimpimage-undo-push.h b/app/core/gimpimage-undo-push.h index c13850ce98..cf863a65dc 100644 --- a/app/core/gimpimage-undo-push.h +++ b/app/core/gimpimage-undo-push.h @@ -101,6 +101,11 @@ GimpUndo * gimp_image_undo_push_filter_modified (GimpImage *image, GimpDrawable *drawable, GimpDrawableFilter *filter); +GimpUndo * gimp_image_undo_push_filter_visibility (GimpImage *image, + const gchar *undo_desc, + GimpDrawable *drawable, + GimpDrawableFilter + *filter); /* mask undos */ diff --git a/app/core/gimpimage-undo.c b/app/core/gimpimage-undo.c index 68692403ed..080b7112f2 100644 --- a/app/core/gimpimage-undo.c +++ b/app/core/gimpimage-undo.c @@ -686,6 +686,9 @@ gimp_image_undo_dirty_from_type (GimpUndoType undo_type) case GIMP_UNDO_GROUP_PATHS_IMPORT: return GIMP_DIRTY_IMAGE_STRUCTURE | GIMP_DIRTY_PATH; + case GIMP_UNDO_GROUP_FILTER_VISIBILITY: + return GIMP_DIRTY_DRAWABLE; + case GIMP_UNDO_GROUP_MISC: return GIMP_DIRTY_ALL; diff --git a/app/core/gimpundo.c b/app/core/gimpundo.c index a87e24cdfe..dc173f4424 100644 --- a/app/core/gimpundo.c +++ b/app/core/gimpundo.c @@ -539,11 +539,13 @@ gimp_undo_is_weak (GimpUndo *undo) case GIMP_UNDO_GROUP_ITEM_VISIBILITY: case GIMP_UNDO_GROUP_ITEM_PROPERTIES: case GIMP_UNDO_GROUP_LAYER_APPLY_MASK: + case GIMP_UNDO_GROUP_FILTER_VISIBILITY: case GIMP_UNDO_ITEM_VISIBILITY: case GIMP_UNDO_LAYER_MODE: case GIMP_UNDO_LAYER_OPACITY: case GIMP_UNDO_LAYER_MASK_APPLY: case GIMP_UNDO_LAYER_MASK_SHOW: + case GIMP_UNDO_FILTER_VISIBILITY: return TRUE; break; diff --git a/app/widgets/gimpdrawabletreeview-filters.c b/app/widgets/gimpdrawabletreeview-filters.c index f532fb95ec..bd049719d2 100644 --- a/app/widgets/gimpdrawabletreeview-filters.c +++ b/app/widgets/gimpdrawabletreeview-filters.c @@ -38,6 +38,7 @@ #include "core/gimpdrawable.h" #include "core/gimpdrawable-filters.h" #include "core/gimpdrawablefilter.h" +#include "core/gimpdrawablefilterundo.h" #include "core/gimplist.h" #include "core/gimpimage.h" #include "core/gimpimage-undo.h" @@ -593,15 +594,41 @@ gimp_drawable_filters_editor_view_visible_cell_toggled (GtkCellRendererToggle *t if (GIMP_IS_DRAWABLE_FILTER (filter)) { GimpDrawable *drawable; + GimpImage *image; + GimpContext *context; gboolean active; + GimpUndo *undo; + gboolean push_undo = TRUE; g_object_get (toggle, "active", &active, NULL); drawable = gimp_drawable_filter_get_drawable (filter); + image = gimp_item_get_image (GIMP_ITEM (drawable)); + context = gimp_container_view_get_context (GIMP_CONTAINER_VIEW (view)); + + + undo = gimp_image_undo_can_compress(image, + GIMP_TYPE_DRAWABLE_FILTER_UNDO, + GIMP_UNDO_FILTER_VISIBILITY); + + if (undo != NULL && GIMP_DRAWABLE_FILTER_UNDO (undo)->filter == filter) + { + push_undo = FALSE; + } + else + { + gimp_image_undo_push_filter_visibility (image, + _("Effect visibility"), + drawable, + filter); + } gimp_filter_set_active (GIMP_FILTER (filter), ! active); + if (! push_undo) + gimp_undo_refresh_preview (undo, context); + gimp_drawable_update (drawable, 0, 0, -1, -1); gimp_image_flush (gimp_item_get_image (GIMP_ITEM (drawable))); } @@ -657,26 +684,77 @@ gimp_drawable_filters_editor_visible_all_toggled (GtkWidget *widget, GimpDrawableTreeView *view) { GimpDrawableTreeViewFiltersEditor *editor = view->editor; + GimpImage *image; GimpContainer *filters; GList *list; + GList *iter; + gint n_filters = 0; gboolean visible; + GimpUndo *undo; visible = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + image = gimp_item_tree_view_get_image (GIMP_ITEM_TREE_VIEW (view)); filters = gimp_drawable_get_filters (editor->drawable); + list = GIMP_LIST (filters)->queue->head; - for (list = GIMP_LIST (filters)->queue->head; - list; - list = g_list_next (list)) + undo = gimp_drawable_filter_undo_can_compress_visibility (image, list); + + for (iter = list; iter; iter = g_list_next (iter)) { - if (GIMP_IS_DRAWABLE_FILTER (list->data)) + if (GIMP_IS_DRAWABLE_FILTER (iter->data) && + visible != gimp_filter_get_active (GIMP_FILTER (iter->data))) { - GimpFilter *filter = list->data; + n_filters++; + } + } + + if (n_filters <= 0) + return; + + if (undo == NULL) + { + gimp_image_undo_group_start (image, + GIMP_UNDO_GROUP_FILTER_VISIBILITY, + "Effects visibility"); + } + + for (iter = list; iter; iter = g_list_next (iter)) + { + if (GIMP_IS_DRAWABLE_FILTER (iter->data)) + { + GimpFilter *filter = iter->data; + GimpDrawableFilter *dfilter = iter->data; + + /** + * Undos are pushed for every filters, even if they weren't + * actually toggled, so that the undo group can be compressed with + * subsequent toggles. + */ + if (undo == NULL) + { + gimp_image_undo_push_filter_visibility (image, + _("Effect visibility"), + editor->drawable, + dfilter); + } gimp_filter_set_active (filter, visible); } } + if (undo == NULL) + { + gimp_image_undo_group_end (image); + } + else + { + GimpContext *context; + + context = gimp_container_view_get_context (GIMP_CONTAINER_VIEW (view)); + gimp_undo_refresh_preview (undo, context); + } + gimp_drawable_update (editor->drawable, 0, 0, -1, -1); gimp_image_flush (gimp_item_get_image (GIMP_ITEM (editor->drawable))); }