/* 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 #include #include "libgimpbase/gimpbase.h" #include "core-types.h" #include "base/temp-buf.h" #include "config/gimpcoreconfig.h" #include "gimp.h" #include "gimpimage.h" #include "gimpmarshal.h" #include "gimpundo.h" #include "gimpundostack.h" #include "gimp-intl.h" enum { POP, FREE, LAST_SIGNAL }; enum { PROP_0, PROP_IMAGE, PROP_UNDO_TYPE, PROP_DIRTY_MASK, PROP_DATA, PROP_SIZE, PROP_POP_FUNC, PROP_FREE_FUNC }; static void gimp_undo_class_init (GimpUndoClass *klass); static GObject * gimp_undo_constructor (GType type, guint n_params, GObjectConstructParam *params); static void gimp_undo_finalize (GObject *object); static void gimp_undo_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gimp_undo_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gint64 gimp_undo_get_memsize (GimpObject *object, gint64 *gui_size); static gboolean gimp_undo_get_popup_size (GimpViewable *viewable, gint width, gint height, gboolean dot_for_dot, gint *popup_width, gint *popup_height); static TempBuf * gimp_undo_get_new_preview (GimpViewable *viewable, gint width, gint height); 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 }; static GimpViewableClass *parent_class = NULL; GType gimp_undo_get_type (void) { static GType undo_type = 0; if (! undo_type) { static const GTypeInfo undo_info = { sizeof (GimpUndoClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gimp_undo_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GimpUndo), 0, /* n_preallocs */ NULL /* instance_init */ }; undo_type = g_type_register_static (GIMP_TYPE_VIEWABLE, "GimpUndo", &undo_info, 0); } return undo_type; } static void gimp_undo_class_init (GimpUndoClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass); parent_class = g_type_class_peek_parent (klass); undo_signals[POP] = g_signal_new ("pop", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpUndoClass, pop), NULL, NULL, gimp_marshal_VOID__ENUM_POINTER, G_TYPE_NONE, 2, GIMP_TYPE_UNDO_MODE, G_TYPE_POINTER); undo_signals[FREE] = g_signal_new ("free", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GimpUndoClass, free), NULL, NULL, gimp_marshal_VOID__ENUM, G_TYPE_NONE, 1, GIMP_TYPE_UNDO_MODE); object_class->constructor = gimp_undo_constructor; object_class->finalize = gimp_undo_finalize; object_class->set_property = gimp_undo_set_property; object_class->get_property = gimp_undo_get_property; gimp_object_class->get_memsize = gimp_undo_get_memsize; viewable_class->default_stock_id = "gtk-undo"; viewable_class->get_popup_size = gimp_undo_get_popup_size; viewable_class->get_new_preview = gimp_undo_get_new_preview; klass->pop = gimp_undo_real_pop; klass->free = gimp_undo_real_free; g_object_class_install_property (object_class, PROP_IMAGE, g_param_spec_object ("image", NULL, NULL, GIMP_TYPE_IMAGE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_UNDO_TYPE, g_param_spec_enum ("undo-type", NULL, NULL, GIMP_TYPE_UNDO_TYPE, GIMP_UNDO_GROUP_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_DIRTY_MASK, g_param_spec_flags ("dirty-mask", NULL, NULL, GIMP_TYPE_DIRTY_MASK, GIMP_DIRTY_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_DATA, g_param_spec_pointer ("data", NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SIZE, g_param_spec_int64 ("size", NULL, NULL, 0, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_POP_FUNC, g_param_spec_pointer ("pop-func", NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_FREE_FUNC, g_param_spec_pointer ("free-func", NULL, NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static GObject * gimp_undo_constructor (GType type, guint n_params, GObjectConstructParam *params) { GObject *object; GimpUndo *undo; object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params); undo = GIMP_UNDO (object); g_assert (GIMP_IS_IMAGE (undo->gimage)); undo->time = time (NULL); return object; } static void gimp_undo_finalize (GObject *object) { GimpUndo *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); undo->preview = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); } static void gimp_undo_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GimpUndo *undo = GIMP_UNDO (object); switch (property_id) { case PROP_IMAGE: /* don't ref */ undo->gimage = (GimpImage *) g_value_get_object (value); break; case PROP_UNDO_TYPE: undo->undo_type = g_value_get_enum (value); break; case PROP_DIRTY_MASK: undo->dirty_mask = g_value_get_flags (value); break; case PROP_DATA: undo->data = g_value_get_pointer (value); break; case PROP_SIZE: undo->size = g_value_get_int64 (value); break; case PROP_POP_FUNC: undo->pop_func = g_value_get_pointer (value); break; case PROP_FREE_FUNC: undo->free_func = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_undo_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GimpUndo *undo = GIMP_UNDO (object); switch (property_id) { case PROP_IMAGE: g_value_set_object (value, undo->gimage); break; case PROP_UNDO_TYPE: g_value_set_enum (value, undo->undo_type); break; case PROP_DIRTY_MASK: g_value_set_flags (value, undo->dirty_mask); break; case PROP_DATA: g_value_set_pointer (value, undo->data); break; case PROP_SIZE: g_value_set_int64 (value, undo->size); break; case PROP_POP_FUNC: g_value_set_pointer (value, undo->pop_func); break; case PROP_FREE_FUNC: g_value_set_pointer (value, undo->free_func); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gint64 gimp_undo_get_memsize (GimpObject *object, gint64 *gui_size) { GimpUndo *undo = GIMP_UNDO (object); gint64 memsize = undo->size; if (undo->preview) *gui_size += temp_buf_get_memsize (undo->preview); return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object, gui_size); } static gboolean gimp_undo_get_popup_size (GimpViewable *viewable, gint width, gint height, gboolean dot_for_dot, gint *popup_width, gint *popup_height) { GimpUndo *undo = GIMP_UNDO (viewable); if (undo->preview && (undo->preview->width > width || undo->preview->height > height)) { *popup_width = undo->preview->width; *popup_height = undo->preview->height; return TRUE; } return FALSE; } static TempBuf * gimp_undo_get_new_preview (GimpViewable *viewable, gint width, gint height) { GimpUndo *undo = GIMP_UNDO (viewable); if (undo->preview) { gint preview_width; gint preview_height; gimp_viewable_calc_preview_size (undo->preview->width, undo->preview->height, width, height, TRUE, 1.0, 1.0, &preview_width, &preview_height, NULL); if (preview_width < undo->preview->width && preview_height < undo->preview->height) { return temp_buf_scale (undo->preview, preview_width, preview_height); } return temp_buf_copy (undo->preview, NULL); } return NULL; } 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); } void gimp_undo_pop (GimpUndo *undo, GimpUndoMode undo_mode, GimpUndoAccumulator *accum) { g_return_if_fail (GIMP_IS_UNDO (undo)); g_return_if_fail (accum != NULL); if (undo->dirty_mask != GIMP_DIRTY_NONE) { switch (undo_mode) { case GIMP_UNDO_MODE_UNDO: gimp_image_clean (undo->gimage, undo->dirty_mask); break; case GIMP_UNDO_MODE_REDO: gimp_image_dirty (undo->gimage, undo->dirty_mask); break; } } g_signal_emit (undo, undo_signals[POP], 0, undo_mode, accum); } void gimp_undo_free (GimpUndo *undo, GimpUndoMode undo_mode) { g_return_if_fail (GIMP_IS_UNDO (undo)); g_signal_emit (undo, undo_signals[FREE], 0, undo_mode); } void gimp_undo_create_preview (GimpUndo *undo, gboolean create_now) { 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 gboolean gimp_undo_create_preview_idle (gpointer data) { GimpUndo *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; } static void gimp_undo_create_preview_private (GimpUndo *undo) { GimpImage *image = undo->gimage; GimpViewable *preview_viewable; GimpViewSize preview_size; gint width; gint height; switch (undo->undo_type) { case GIMP_UNDO_GROUP_IMAGE_QUICK_MASK: case GIMP_UNDO_GROUP_MASK: case GIMP_UNDO_MASK: preview_viewable = GIMP_VIEWABLE (gimp_image_get_mask (image)); break; default: preview_viewable = GIMP_VIEWABLE (image); break; } preview_size = image->gimp->config->undo_preview_size; if (image->width <= preview_size && image->height <= preview_size) { width = image->width; height = image->height; } else { if (image->width > image->height) { width = preview_size; height = MAX (1, (image->height * preview_size / image->width)); } else { height = preview_size; width = MAX (1, (image->width * preview_size / image->height)); } } undo->preview = gimp_viewable_get_new_preview (preview_viewable, width, height); gimp_viewable_invalidate_preview (GIMP_VIEWABLE (undo)); } void gimp_undo_refresh_preview (GimpUndo *undo) { g_return_if_fail (GIMP_IS_UNDO (undo)); if (undo->preview_idle_id) return; if (undo->preview) { temp_buf_free (undo->preview); undo->preview = NULL; gimp_undo_create_preview (undo, FALSE); } } const gchar * gimp_undo_type_to_name (GimpUndoType type) { const gchar *desc; if (gimp_enum_get_value (GIMP_TYPE_UNDO_TYPE, type, NULL, NULL, &desc, NULL)) return desc; else return ""; }