mirror of https://github.com/GNOME/gimp.git
1924 lines
55 KiB
C
1924 lines
55 KiB
C
/* GIMP - The GNU 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <gegl.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
|
|
#include "core-types.h"
|
|
|
|
#include "gimp.h"
|
|
#include "gimp-parasites.h"
|
|
#include "gimpchannel.h"
|
|
#include "gimpimage.h"
|
|
#include "gimpimage-undo.h"
|
|
#include "gimpimage-undo-push.h"
|
|
#include "gimpitem.h"
|
|
#include "gimpitem-preview.h"
|
|
#include "gimpitemtree.h"
|
|
#include "gimplist.h"
|
|
#include "gimpmarshal.h"
|
|
#include "gimpparasitelist.h"
|
|
#include "gimpprogress.h"
|
|
#include "gimpstrokeoptions.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
enum
|
|
{
|
|
REMOVED,
|
|
VISIBILITY_CHANGED,
|
|
LINKED_CHANGED,
|
|
LOCK_CONTENT_CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_ID,
|
|
PROP_WIDTH,
|
|
PROP_HEIGHT,
|
|
PROP_OFFSET_X,
|
|
PROP_OFFSET_Y,
|
|
PROP_VISIBLE,
|
|
PROP_LINKED,
|
|
PROP_LOCK_CONTENT
|
|
};
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
static void gimp_item_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gimp_item_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gimp_item_finalize (GObject *object);
|
|
|
|
static gint64 gimp_item_get_memsize (GimpObject *object,
|
|
gint64 *gui_size);
|
|
|
|
static void gimp_item_real_visibility_changed (GimpItem *item);
|
|
|
|
static gboolean gimp_item_real_is_content_locked (const GimpItem *item);
|
|
static GimpItem * gimp_item_real_duplicate (GimpItem *item,
|
|
GType new_type);
|
|
static void gimp_item_real_convert (GimpItem *item,
|
|
GimpImage *dest_image);
|
|
static gboolean gimp_item_real_rename (GimpItem *item,
|
|
const gchar *new_name,
|
|
const gchar *undo_desc,
|
|
GError **error);
|
|
static void gimp_item_real_translate (GimpItem *item,
|
|
gint offset_x,
|
|
gint offset_y,
|
|
gboolean push_undo);
|
|
static void gimp_item_real_scale (GimpItem *item,
|
|
gint new_width,
|
|
gint new_height,
|
|
gint new_offset_x,
|
|
gint new_offset_y,
|
|
GimpInterpolationType interpolation,
|
|
GimpProgress *progress);
|
|
static void gimp_item_real_resize (GimpItem *item,
|
|
GimpContext *context,
|
|
gint new_width,
|
|
gint new_height,
|
|
gint offset_x,
|
|
gint offset_y);
|
|
static GeglNode * gimp_item_real_get_node (GimpItem *item);
|
|
static void gimp_item_sync_offset_node (GimpItem *item);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpItem, gimp_item, GIMP_TYPE_VIEWABLE)
|
|
|
|
#define parent_class gimp_item_parent_class
|
|
|
|
static guint gimp_item_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
static void
|
|
gimp_item_class_init (GimpItemClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
|
|
GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
|
|
|
|
gimp_item_signals[REMOVED] =
|
|
g_signal_new ("removed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpItemClass, removed),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
gimp_item_signals[VISIBILITY_CHANGED] =
|
|
g_signal_new ("visibility-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpItemClass, visibility_changed),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
gimp_item_signals[LINKED_CHANGED] =
|
|
g_signal_new ("linked-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpItemClass, linked_changed),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
gimp_item_signals[LOCK_CONTENT_CHANGED] =
|
|
g_signal_new ("lock-content-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GimpItemClass, lock_content_changed),
|
|
NULL, NULL,
|
|
gimp_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
object_class->set_property = gimp_item_set_property;
|
|
object_class->get_property = gimp_item_get_property;
|
|
object_class->finalize = gimp_item_finalize;
|
|
|
|
gimp_object_class->get_memsize = gimp_item_get_memsize;
|
|
|
|
viewable_class->get_preview_size = gimp_item_get_preview_size;
|
|
viewable_class->get_popup_size = gimp_item_get_popup_size;
|
|
|
|
klass->removed = NULL;
|
|
klass->visibility_changed = gimp_item_real_visibility_changed;
|
|
klass->linked_changed = NULL;
|
|
klass->lock_content_changed = NULL;
|
|
|
|
klass->is_attached = NULL;
|
|
klass->is_content_locked = gimp_item_real_is_content_locked;
|
|
klass->get_tree = NULL;
|
|
klass->duplicate = gimp_item_real_duplicate;
|
|
klass->convert = gimp_item_real_convert;
|
|
klass->rename = gimp_item_real_rename;
|
|
klass->translate = gimp_item_real_translate;
|
|
klass->scale = gimp_item_real_scale;
|
|
klass->resize = gimp_item_real_resize;
|
|
klass->flip = NULL;
|
|
klass->rotate = NULL;
|
|
klass->transform = NULL;
|
|
klass->stroke = NULL;
|
|
klass->to_selection = NULL;
|
|
klass->get_node = gimp_item_real_get_node;
|
|
|
|
klass->default_name = NULL;
|
|
klass->rename_desc = NULL;
|
|
klass->translate_desc = NULL;
|
|
klass->scale_desc = NULL;
|
|
klass->resize_desc = NULL;
|
|
klass->flip_desc = NULL;
|
|
klass->rotate_desc = NULL;
|
|
klass->transform_desc = NULL;
|
|
|
|
g_object_class_install_property (object_class, PROP_ID,
|
|
g_param_spec_int ("id", NULL, NULL,
|
|
0, G_MAXINT, 0,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_WIDTH,
|
|
g_param_spec_int ("width", NULL, NULL,
|
|
1, GIMP_MAX_IMAGE_SIZE, 1,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_HEIGHT,
|
|
g_param_spec_int ("height", NULL, NULL,
|
|
1, GIMP_MAX_IMAGE_SIZE, 1,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_OFFSET_X,
|
|
g_param_spec_int ("offset-x", NULL, NULL,
|
|
-GIMP_MAX_IMAGE_SIZE,
|
|
GIMP_MAX_IMAGE_SIZE, 0,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_OFFSET_Y,
|
|
g_param_spec_int ("offset-y", NULL, NULL,
|
|
-GIMP_MAX_IMAGE_SIZE,
|
|
GIMP_MAX_IMAGE_SIZE, 0,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_VISIBLE,
|
|
g_param_spec_boolean ("visible", NULL, NULL,
|
|
TRUE,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_LINKED,
|
|
g_param_spec_boolean ("linked", NULL, NULL,
|
|
FALSE,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_LOCK_CONTENT,
|
|
g_param_spec_boolean ("lock-content",
|
|
NULL, NULL,
|
|
FALSE,
|
|
GIMP_PARAM_READABLE));
|
|
}
|
|
|
|
static void
|
|
gimp_item_init (GimpItem *item)
|
|
{
|
|
g_object_force_floating (G_OBJECT (item));
|
|
|
|
item->ID = 0;
|
|
item->tattoo = 0;
|
|
item->image = NULL;
|
|
item->parasites = gimp_parasite_list_new ();
|
|
item->width = 0;
|
|
item->height = 0;
|
|
item->offset_x = 0;
|
|
item->offset_y = 0;
|
|
item->visible = TRUE;
|
|
item->linked = FALSE;
|
|
item->lock_content = FALSE;
|
|
item->removed = FALSE;
|
|
item->node = NULL;
|
|
item->offset_node = NULL;
|
|
}
|
|
|
|
static void
|
|
gimp_item_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id)
|
|
{
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_item_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_ID:
|
|
g_value_set_int (value, item->ID);
|
|
break;
|
|
case PROP_WIDTH:
|
|
g_value_set_int (value, item->width);
|
|
break;
|
|
case PROP_HEIGHT:
|
|
g_value_set_int (value, item->height);
|
|
break;
|
|
case PROP_OFFSET_X:
|
|
g_value_set_int (value, item->offset_x);
|
|
break;
|
|
case PROP_OFFSET_Y:
|
|
g_value_set_int (value, item->offset_y);
|
|
break;
|
|
case PROP_VISIBLE:
|
|
g_value_set_boolean (value, item->visible);
|
|
break;
|
|
case PROP_LINKED:
|
|
g_value_set_boolean (value, item->linked);
|
|
break;
|
|
case PROP_LOCK_CONTENT:
|
|
g_value_set_boolean (value, item->lock_content);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_item_finalize (GObject *object)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (object);
|
|
|
|
if (item->node)
|
|
{
|
|
g_object_unref (item->node);
|
|
item->node = NULL;
|
|
}
|
|
|
|
if (item->image && item->image->gimp)
|
|
{
|
|
g_hash_table_remove (item->image->gimp->item_table,
|
|
GINT_TO_POINTER (item->ID));
|
|
item->image = NULL;
|
|
}
|
|
|
|
if (item->parasites)
|
|
{
|
|
g_object_unref (item->parasites);
|
|
item->parasites = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static gint64
|
|
gimp_item_get_memsize (GimpObject *object,
|
|
gint64 *gui_size)
|
|
{
|
|
GimpItem *item = GIMP_ITEM (object);
|
|
gint64 memsize = 0;
|
|
|
|
memsize += gimp_object_get_memsize (GIMP_OBJECT (item->parasites), gui_size);
|
|
|
|
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
|
|
gui_size);
|
|
}
|
|
|
|
static void
|
|
gimp_item_real_visibility_changed (GimpItem *item)
|
|
{
|
|
if (! item->node)
|
|
return;
|
|
|
|
if (gimp_item_get_visible (item))
|
|
{
|
|
/* Leave this up to subclasses */
|
|
}
|
|
else
|
|
{
|
|
GeglNode *input;
|
|
GeglNode *output;
|
|
|
|
input = gegl_node_get_input_proxy (item->node, "input");
|
|
output = gegl_node_get_output_proxy (item->node, "output");
|
|
|
|
gegl_node_connect_to (input, "output",
|
|
output, "input");
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gimp_item_real_is_content_locked (const GimpItem *item)
|
|
{
|
|
GimpItem *parent = gimp_item_get_parent (item);
|
|
|
|
if (parent && gimp_item_is_content_locked (parent))
|
|
return TRUE;
|
|
|
|
return item->lock_content;
|
|
}
|
|
|
|
static GimpItem *
|
|
gimp_item_real_duplicate (GimpItem *item,
|
|
GType new_type)
|
|
{
|
|
GimpItem *new_item;
|
|
gchar *new_name;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (item->image), NULL);
|
|
g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_ITEM), NULL);
|
|
|
|
/* formulate the new name */
|
|
{
|
|
const gchar *name;
|
|
gchar *ext;
|
|
gint number;
|
|
gint len;
|
|
|
|
name = gimp_object_get_name (item);
|
|
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
|
|
ext = strrchr (name, '#');
|
|
len = strlen (_("copy"));
|
|
|
|
if ((strlen (name) >= len &&
|
|
strcmp (&name[strlen (name) - len], _("copy")) == 0) ||
|
|
(ext && (number = atoi (ext + 1)) > 0 &&
|
|
((int)(log10 (number) + 1)) == strlen (ext + 1)))
|
|
{
|
|
/* don't have redundant "copy"s */
|
|
new_name = g_strdup (name);
|
|
}
|
|
else
|
|
{
|
|
new_name = g_strdup_printf (_("%s copy"), name);
|
|
}
|
|
}
|
|
|
|
new_item = g_object_new (new_type, NULL);
|
|
|
|
gimp_item_configure (new_item, gimp_item_get_image (item),
|
|
item->offset_x, item->offset_y,
|
|
gimp_item_get_width (item),
|
|
gimp_item_get_height (item),
|
|
new_name);
|
|
|
|
g_free (new_name);
|
|
|
|
g_object_unref (new_item->parasites);
|
|
new_item->parasites = gimp_parasite_list_copy (item->parasites);
|
|
|
|
gimp_item_set_visible (new_item, gimp_item_get_visible (item), FALSE);
|
|
gimp_item_set_linked (new_item, gimp_item_get_linked (item), FALSE);
|
|
|
|
if (gimp_item_can_lock_content (new_item))
|
|
gimp_item_set_lock_content (new_item, gimp_item_get_lock_content (item),
|
|
FALSE);
|
|
|
|
return new_item;
|
|
}
|
|
|
|
static void
|
|
gimp_item_real_convert (GimpItem *item,
|
|
GimpImage *dest_image)
|
|
{
|
|
gimp_item_set_image (item, dest_image);
|
|
}
|
|
|
|
static gboolean
|
|
gimp_item_real_rename (GimpItem *item,
|
|
const gchar *new_name,
|
|
const gchar *undo_desc,
|
|
GError **error)
|
|
{
|
|
if (gimp_item_is_attached (item))
|
|
gimp_item_tree_rename_item (gimp_item_get_tree (item), item,
|
|
new_name, TRUE, undo_desc);
|
|
else
|
|
gimp_object_set_name (GIMP_OBJECT (item), new_name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gimp_item_real_translate (GimpItem *item,
|
|
gint offset_x,
|
|
gint offset_y,
|
|
gboolean push_undo)
|
|
{
|
|
gimp_item_set_offset (item,
|
|
item->offset_x + offset_x,
|
|
item->offset_y + offset_y);
|
|
}
|
|
|
|
static void
|
|
gimp_item_real_scale (GimpItem *item,
|
|
gint new_width,
|
|
gint new_height,
|
|
gint new_offset_x,
|
|
gint new_offset_y,
|
|
GimpInterpolationType interpolation,
|
|
GimpProgress *progress)
|
|
{
|
|
if (item->width != new_width)
|
|
{
|
|
item->width = new_width;
|
|
g_object_notify (G_OBJECT (item), "width");
|
|
}
|
|
|
|
if (item->height != new_height)
|
|
{
|
|
item->height = new_height;
|
|
g_object_notify (G_OBJECT (item), "height");
|
|
}
|
|
|
|
gimp_item_set_offset (item, new_offset_x, new_offset_y);
|
|
}
|
|
|
|
static void
|
|
gimp_item_real_resize (GimpItem *item,
|
|
GimpContext *context,
|
|
gint new_width,
|
|
gint new_height,
|
|
gint offset_x,
|
|
gint offset_y)
|
|
{
|
|
if (item->width != new_width)
|
|
{
|
|
item->width = new_width;
|
|
g_object_notify (G_OBJECT (item), "width");
|
|
}
|
|
|
|
if (item->height != new_height)
|
|
{
|
|
item->height = new_height;
|
|
g_object_notify (G_OBJECT (item), "height");
|
|
}
|
|
|
|
gimp_item_set_offset (item,
|
|
item->offset_x - offset_x,
|
|
item->offset_y - offset_y);
|
|
}
|
|
|
|
static GeglNode *
|
|
gimp_item_real_get_node (GimpItem *item)
|
|
{
|
|
item->node = gegl_node_new ();
|
|
|
|
return item->node;
|
|
}
|
|
|
|
static void
|
|
gimp_item_sync_offset_node (GimpItem *item)
|
|
{
|
|
if (item->offset_node)
|
|
gegl_node_set (item->offset_node,
|
|
"x", (gdouble) item->offset_x,
|
|
"y", (gdouble) item->offset_y,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* gimp_item_remove:
|
|
* @item: the #GimpItem to remove.
|
|
*
|
|
* This function sets the 'removed' flag on @item to #TRUE, and emits
|
|
* a 'removed' signal on the item.
|
|
*/
|
|
void
|
|
gimp_item_removed (GimpItem *item)
|
|
{
|
|
GimpContainer *children;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
|
|
item->removed = TRUE;
|
|
|
|
children = gimp_viewable_get_children (GIMP_VIEWABLE (item));
|
|
|
|
if (children)
|
|
gimp_container_foreach (children, (GFunc) gimp_item_removed, NULL);
|
|
|
|
g_signal_emit (item, gimp_item_signals[REMOVED], 0);
|
|
}
|
|
|
|
/**
|
|
* gimp_item_is_removed:
|
|
* @item: the #GimpItem to check.
|
|
*
|
|
* Returns: %TRUE if the 'removed' flag is set for @item, %FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
gimp_item_is_removed (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
return item->removed;
|
|
}
|
|
|
|
/**
|
|
* gimp_item_configure:
|
|
* @item: The #GimpItem to configure.
|
|
* @image: The #GimpImage to which the item belongs.
|
|
* @offset_x: The X offset to assign the item.
|
|
* @offset_y: The Y offset to assign the item.
|
|
* @width: The width to assign the item.
|
|
* @height: The height to assign the item.
|
|
* @name: The name to assign the item.
|
|
*
|
|
* This function is used to configure a new item. First, if the item
|
|
* does not already have an ID, it is assigned the next available
|
|
* one, and then inserted into the Item Hash Table. Next, it is
|
|
* given basic item properties as specified by the arguments.
|
|
*/
|
|
void
|
|
gimp_item_configure (GimpItem *item,
|
|
GimpImage *image,
|
|
gint offset_x,
|
|
gint offset_y,
|
|
gint width,
|
|
gint height,
|
|
const gchar *name)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (GIMP_IS_IMAGE (image));
|
|
|
|
g_object_freeze_notify (G_OBJECT (item));
|
|
|
|
if (item->ID == 0)
|
|
{
|
|
do
|
|
{
|
|
item->ID = image->gimp->next_item_ID++;
|
|
|
|
if (image->gimp->next_item_ID == G_MAXINT)
|
|
image->gimp->next_item_ID = 1;
|
|
}
|
|
while (g_hash_table_lookup (image->gimp->item_table,
|
|
GINT_TO_POINTER (item->ID)));
|
|
|
|
g_hash_table_insert (image->gimp->item_table,
|
|
GINT_TO_POINTER (item->ID),
|
|
item);
|
|
|
|
gimp_item_set_image (item, image);
|
|
|
|
g_object_notify (G_OBJECT (item), "id");
|
|
}
|
|
|
|
if (item->width != width)
|
|
{
|
|
item->width = width;
|
|
g_object_notify (G_OBJECT (item), "width");
|
|
}
|
|
|
|
if (item->height != height)
|
|
{
|
|
item->height = height;
|
|
g_object_notify (G_OBJECT (item), "height");
|
|
}
|
|
|
|
gimp_item_set_offset (item, offset_x, offset_y);
|
|
|
|
if (name && strlen (name))
|
|
gimp_object_set_name (GIMP_OBJECT (item), name);
|
|
else
|
|
gimp_object_set_static_name (GIMP_OBJECT (item),
|
|
GIMP_ITEM_GET_CLASS (item)->default_name);
|
|
|
|
g_object_thaw_notify (G_OBJECT (item));
|
|
}
|
|
|
|
/**
|
|
* gimp_item_is_attached:
|
|
* @item: The #GimpItem to check.
|
|
*
|
|
* Returns: %TRUE if the item is attached to an image, %FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
gimp_item_is_attached (const GimpItem *item)
|
|
{
|
|
GimpItem *parent;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
parent = gimp_item_get_parent (item);
|
|
|
|
if (parent)
|
|
return gimp_item_is_attached (parent);
|
|
|
|
return GIMP_ITEM_GET_CLASS (item)->is_attached (item);
|
|
}
|
|
|
|
GimpItem *
|
|
gimp_item_get_parent (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
return GIMP_ITEM (gimp_viewable_get_parent (GIMP_VIEWABLE (item)));
|
|
}
|
|
|
|
GimpItemTree *
|
|
gimp_item_get_tree (GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
if (GIMP_ITEM_GET_CLASS (item)->get_tree)
|
|
return GIMP_ITEM_GET_CLASS (item)->get_tree (item);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GimpContainer *
|
|
gimp_item_get_container (GimpItem *item)
|
|
{
|
|
GimpItem *parent;
|
|
GimpItemTree *tree;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
parent = gimp_item_get_parent (item);
|
|
|
|
if (parent)
|
|
return gimp_viewable_get_children (GIMP_VIEWABLE (parent));
|
|
|
|
tree = gimp_item_get_tree (item);
|
|
|
|
if (tree)
|
|
return tree->container;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GList *
|
|
gimp_item_get_container_iter (GimpItem *item)
|
|
{
|
|
GimpContainer *container;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
container = gimp_item_get_container (item);
|
|
|
|
if (container)
|
|
return GIMP_LIST (container)->list;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
gint
|
|
gimp_item_get_index (GimpItem *item)
|
|
{
|
|
GimpContainer *container;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), -1);
|
|
|
|
container = gimp_item_get_container (item);
|
|
|
|
if (container)
|
|
return gimp_container_get_child_index (container, GIMP_OBJECT (item));
|
|
|
|
return -1;
|
|
}
|
|
|
|
GList *
|
|
gimp_item_get_path (GimpItem *item)
|
|
{
|
|
GimpContainer *container;
|
|
GList *path = NULL;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
g_return_val_if_fail (gimp_item_is_attached (item), NULL);
|
|
|
|
container = gimp_item_get_container (item);
|
|
|
|
while (container)
|
|
{
|
|
guint32 index = gimp_container_get_child_index (container,
|
|
GIMP_OBJECT (item));
|
|
|
|
path = g_list_prepend (path, GUINT_TO_POINTER (index));
|
|
|
|
item = gimp_item_get_parent (item);
|
|
|
|
if (item)
|
|
container = gimp_item_get_container (item);
|
|
else
|
|
container = NULL;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* gimp_item_duplicate:
|
|
* @item: The #GimpItem to duplicate.
|
|
* @new_type: The type to make the new item.
|
|
*
|
|
* Returns: the newly created item.
|
|
*/
|
|
GimpItem *
|
|
gimp_item_duplicate (GimpItem *item,
|
|
GType new_type)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (item->image), NULL);
|
|
g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_ITEM), NULL);
|
|
|
|
return GIMP_ITEM_GET_CLASS (item)->duplicate (item, new_type);
|
|
}
|
|
|
|
/**
|
|
* gimp_item_convert:
|
|
* @item: The #GimpItem to convert.
|
|
* @dest_image: The #GimpImage in which to place the converted item.
|
|
* @new_type: The type to convert the item to.
|
|
*
|
|
* Returns: the new item that results from the conversion.
|
|
*/
|
|
GimpItem *
|
|
gimp_item_convert (GimpItem *item,
|
|
GimpImage *dest_image,
|
|
GType new_type)
|
|
{
|
|
GimpItem *new_item;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (item->image), NULL);
|
|
g_return_val_if_fail (GIMP_IS_IMAGE (dest_image), NULL);
|
|
g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_ITEM), NULL);
|
|
|
|
new_item = gimp_item_duplicate (item, new_type);
|
|
|
|
if (new_item)
|
|
GIMP_ITEM_GET_CLASS (new_item)->convert (new_item, dest_image);
|
|
|
|
return new_item;
|
|
}
|
|
|
|
/**
|
|
* gimp_item_rename:
|
|
* @item: The #GimpItem to rename.
|
|
* @new_name: The new name to give the item.
|
|
* @error: Return location for error message.
|
|
*
|
|
* This function assigns a new name to the item, if the desired name is
|
|
* different from the name it already has, and pushes an entry onto the
|
|
* undo stack for the item's image. If @new_name is NULL or empty, the
|
|
* default name for the item's class is used. If the name is changed,
|
|
* the GimpObject::name-changed signal is emitted for the item.
|
|
*
|
|
* Returns: %TRUE if the @item could be renamed, %FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
gimp_item_rename (GimpItem *item,
|
|
const gchar *new_name,
|
|
GError **error)
|
|
{
|
|
GimpItemClass *item_class;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
|
|
if (! new_name || ! *new_name)
|
|
new_name = item_class->default_name;
|
|
|
|
if (strcmp (new_name, gimp_object_get_name (item)))
|
|
return item_class->rename (item, new_name, item_class->rename_desc, error);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gimp_item_get_width:
|
|
* @item: The #GimpItem to check.
|
|
*
|
|
* Returns: The width of the item.
|
|
*/
|
|
gint
|
|
gimp_item_get_width (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), -1);
|
|
|
|
return item->width;
|
|
}
|
|
|
|
/**
|
|
* gimp_item_get_height:
|
|
* @item: The #GimpItem to check.
|
|
*
|
|
* Returns: The height of the item.
|
|
*/
|
|
gint
|
|
gimp_item_get_height (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), -1);
|
|
|
|
return item->height;
|
|
}
|
|
|
|
void
|
|
gimp_item_set_size (GimpItem *item,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
|
|
if (item->width != width ||
|
|
item->height != height)
|
|
{
|
|
g_object_freeze_notify (G_OBJECT (item));
|
|
|
|
if (item->width != width)
|
|
{
|
|
item->width = width;
|
|
g_object_notify (G_OBJECT (item), "width");
|
|
}
|
|
|
|
if (item->height != height)
|
|
{
|
|
item->height = height;
|
|
g_object_notify (G_OBJECT (item), "height");
|
|
}
|
|
|
|
g_object_thaw_notify (G_OBJECT (item));
|
|
|
|
gimp_viewable_size_changed (GIMP_VIEWABLE (item));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gimp_item_get_offset:
|
|
* @item: The #GimpItem to check.
|
|
* @offset_x: Return location for the item's X offset.
|
|
* @offset_y: Return location for the item's Y offset.
|
|
*
|
|
* Reveals the X and Y offsets of the item.
|
|
*/
|
|
void
|
|
gimp_item_get_offset (const GimpItem *item,
|
|
gint *offset_x,
|
|
gint *offset_y)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
|
|
if (offset_x) *offset_x = item->offset_x;
|
|
if (offset_y) *offset_y = item->offset_y;
|
|
}
|
|
|
|
void
|
|
gimp_item_set_offset (GimpItem *item,
|
|
gint offset_x,
|
|
gint offset_y)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
|
|
g_object_freeze_notify (G_OBJECT (item));
|
|
|
|
if (item->offset_x != offset_x)
|
|
{
|
|
item->offset_x = offset_x;
|
|
g_object_notify (G_OBJECT (item), "offset-x");
|
|
}
|
|
|
|
if (item->offset_y != offset_y)
|
|
{
|
|
item->offset_y = offset_y;
|
|
g_object_notify (G_OBJECT (item), "offset-y");
|
|
}
|
|
|
|
gimp_item_sync_offset_node (item);
|
|
|
|
g_object_thaw_notify (G_OBJECT (item));
|
|
}
|
|
|
|
gint
|
|
gimp_item_get_offset_x (GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), 0);
|
|
|
|
return item->offset_x;
|
|
}
|
|
|
|
gint
|
|
gimp_item_get_offset_y (GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), 0);
|
|
|
|
return item->offset_y;
|
|
}
|
|
|
|
/**
|
|
* gimp_item_translate:
|
|
* @item: The #GimpItem to move.
|
|
* @offset_x: Increment to the X offset of the item.
|
|
* @offset_y: Increment to the Y offset of the item.
|
|
* @push_undo: If #TRUE, create an entry in the image's undo stack
|
|
* for this action.
|
|
*
|
|
* Adds the specified increments to the X and Y offsets for the item.
|
|
*/
|
|
void
|
|
gimp_item_translate (GimpItem *item,
|
|
gint offset_x,
|
|
gint offset_y,
|
|
gboolean push_undo)
|
|
{
|
|
GimpItemClass *item_class;
|
|
GimpImage *image;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
image = gimp_item_get_image (item);
|
|
|
|
if (! gimp_item_is_attached (item))
|
|
push_undo = FALSE;
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_DISPLACE,
|
|
item_class->translate_desc);
|
|
|
|
item_class->translate (item, offset_x, offset_y, push_undo);
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
/**
|
|
* gimp_item_check_scaling:
|
|
* @item: Item to check
|
|
* @new_width: proposed width of item, in pixels
|
|
* @new_height: proposed height of item, in pixels
|
|
*
|
|
* Scales item dimensions, then snaps them to pixel centers
|
|
*
|
|
* Returns: #FALSE if any dimension reduces to zero as a result
|
|
* of this; otherwise, returns #TRUE.
|
|
**/
|
|
gboolean
|
|
gimp_item_check_scaling (const GimpItem *item,
|
|
gint new_width,
|
|
gint new_height)
|
|
{
|
|
GimpImage *image;
|
|
gdouble img_scale_w;
|
|
gdouble img_scale_h;
|
|
gint new_item_width;
|
|
gint new_item_height;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
image = gimp_item_get_image (item);
|
|
|
|
img_scale_w = ((gdouble) new_width /
|
|
(gdouble) gimp_image_get_width (image));
|
|
img_scale_h = ((gdouble) new_height /
|
|
(gdouble) gimp_image_get_height (image));
|
|
new_item_width = ROUND (img_scale_w * (gdouble) gimp_item_get_width (item));
|
|
new_item_height = ROUND (img_scale_h * (gdouble) gimp_item_get_height (item));
|
|
|
|
return (new_item_width > 0 && new_item_height > 0);
|
|
}
|
|
|
|
void
|
|
gimp_item_scale (GimpItem *item,
|
|
gint new_width,
|
|
gint new_height,
|
|
gint new_offset_x,
|
|
gint new_offset_y,
|
|
GimpInterpolationType interpolation,
|
|
GimpProgress *progress)
|
|
{
|
|
GimpItemClass *item_class;
|
|
GimpImage *image;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
if (new_width < 1 || new_height < 1)
|
|
return;
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
image = gimp_item_get_image (item);
|
|
|
|
if (gimp_item_is_attached (item))
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_SCALE,
|
|
item_class->scale_desc);
|
|
|
|
g_object_freeze_notify (G_OBJECT (item));
|
|
|
|
item_class->scale (item, new_width, new_height, new_offset_x, new_offset_y,
|
|
interpolation, progress);
|
|
|
|
g_object_thaw_notify (G_OBJECT (item));
|
|
|
|
if (gimp_item_is_attached (item))
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
/**
|
|
* gimp_item_scale_by_factors:
|
|
* @item: Item to be transformed by explicit width and height factors.
|
|
* @w_factor: scale factor to apply to width and horizontal offset
|
|
* @h_factor: scale factor to apply to height and vertical offset
|
|
* @interpolation:
|
|
* @progress:
|
|
*
|
|
* Scales item dimensions and offsets by uniform width and
|
|
* height factors.
|
|
*
|
|
* Use gimp_item_scale_by_factors() in circumstances when the same
|
|
* width and height scaling factors are to be uniformly applied to a
|
|
* set of items. In this context, the item's dimensions and offsets
|
|
* from the sides of the containing image all change by these
|
|
* predetermined factors. By fiat, the fixed point of the transform is
|
|
* the upper left hand corner of the image. Returns #FALSE if a
|
|
* requested scale factor is zero or if a scaling zero's out a item
|
|
* dimension; returns #TRUE otherwise.
|
|
*
|
|
* Use gimp_item_scale() in circumstances where new item width
|
|
* and height dimensions are predetermined instead.
|
|
*
|
|
* Side effects: Undo set created for item. Old item imagery
|
|
* scaled & painted to new item tiles.
|
|
*
|
|
* Returns: #TRUE, if the scaled item has positive dimensions
|
|
* #FALSE if the scaled item has at least one zero dimension
|
|
**/
|
|
gboolean
|
|
gimp_item_scale_by_factors (GimpItem *item,
|
|
gdouble w_factor,
|
|
gdouble h_factor,
|
|
GimpInterpolationType interpolation,
|
|
GimpProgress *progress)
|
|
{
|
|
gint new_width, new_height;
|
|
gint new_offset_x, new_offset_y;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
|
|
|
|
if (w_factor == 0.0 || h_factor == 0.0)
|
|
{
|
|
g_warning ("%s: requested width or height scale equals zero", G_STRFUNC);
|
|
return FALSE;
|
|
}
|
|
|
|
new_offset_x = ROUND (w_factor * (gdouble) item->offset_x);
|
|
new_offset_y = ROUND (h_factor * (gdouble) item->offset_y);
|
|
new_width = ROUND (w_factor * (gdouble) gimp_item_get_width (item));
|
|
new_height = ROUND (h_factor * (gdouble) gimp_item_get_height (item));
|
|
|
|
if (new_width != 0 && new_height != 0)
|
|
{
|
|
gimp_item_scale (item,
|
|
new_width, new_height,
|
|
new_offset_x, new_offset_y,
|
|
interpolation, progress);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* gimp_item_scale_by_origin:
|
|
* @item: The item to be transformed by width & height scale factors
|
|
* @new_width: The width that item will acquire
|
|
* @new_height: The height that the item will acquire
|
|
* @interpolation:
|
|
* @progress:
|
|
* @local_origin: sets fixed point of the scaling transform. See below.
|
|
*
|
|
* Sets item dimensions to new_width and
|
|
* new_height. Derives vertical and horizontal scaling
|
|
* transforms from new width and height. If local_origin is
|
|
* #TRUE, the fixed point of the scaling transform coincides
|
|
* with the item's center point. Otherwise, the fixed
|
|
* point is taken to be [-item->offset_x, -item->offset_y].
|
|
*
|
|
* Since this function derives scale factors from new and
|
|
* current item dimensions, these factors will vary from
|
|
* item to item because of aliasing artifacts; factor
|
|
* variations among items can be quite large where item
|
|
* dimensions approach pixel dimensions. Use
|
|
* gimp_item_scale_by_factors() where constant scales are to
|
|
* be uniformly applied to a number of items.
|
|
*
|
|
* Side effects: undo set created for item.
|
|
* Old item imagery scaled
|
|
* & painted to new item tiles
|
|
**/
|
|
void
|
|
gimp_item_scale_by_origin (GimpItem *item,
|
|
gint new_width,
|
|
gint new_height,
|
|
GimpInterpolationType interpolation,
|
|
GimpProgress *progress,
|
|
gboolean local_origin)
|
|
{
|
|
gint new_offset_x, new_offset_y;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
if (new_width == 0 || new_height == 0)
|
|
{
|
|
g_warning ("%s: requested width or height equals zero", G_STRFUNC);
|
|
return;
|
|
}
|
|
|
|
if (local_origin)
|
|
{
|
|
new_offset_x = (item->offset_x +
|
|
((gimp_item_get_width (item) - new_width) / 2.0));
|
|
new_offset_y = (item->offset_y +
|
|
((gimp_item_get_height (item) - new_height) / 2.0));
|
|
}
|
|
else
|
|
{
|
|
new_offset_x = (gint) (((gdouble) new_width *
|
|
(gdouble) item->offset_x /
|
|
(gdouble) gimp_item_get_width (item)));
|
|
|
|
new_offset_y = (gint) (((gdouble) new_height *
|
|
(gdouble) item->offset_y /
|
|
(gdouble) gimp_item_get_height (item)));
|
|
}
|
|
|
|
gimp_item_scale (item,
|
|
new_width, new_height,
|
|
new_offset_x, new_offset_y,
|
|
interpolation, progress);
|
|
}
|
|
|
|
void
|
|
gimp_item_resize (GimpItem *item,
|
|
GimpContext *context,
|
|
gint new_width,
|
|
gint new_height,
|
|
gint offset_x,
|
|
gint offset_y)
|
|
{
|
|
GimpItemClass *item_class;
|
|
GimpImage *image;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (GIMP_IS_CONTEXT (context));
|
|
|
|
if (new_width < 1 || new_height < 1)
|
|
return;
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
image = gimp_item_get_image (item);
|
|
|
|
if (gimp_item_is_attached (item))
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE,
|
|
item_class->resize_desc);
|
|
|
|
g_object_freeze_notify (G_OBJECT (item));
|
|
|
|
item_class->resize (item, context, new_width, new_height, offset_x, offset_y);
|
|
|
|
g_object_thaw_notify (G_OBJECT (item));
|
|
|
|
if (gimp_item_is_attached (item))
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
void
|
|
gimp_item_flip (GimpItem *item,
|
|
GimpContext *context,
|
|
GimpOrientationType flip_type,
|
|
gdouble axis,
|
|
gboolean clip_result)
|
|
{
|
|
GimpItemClass *item_class;
|
|
GimpImage *image;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (gimp_item_is_attached (item));
|
|
g_return_if_fail (GIMP_IS_CONTEXT (context));
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
image = gimp_item_get_image (item);
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM,
|
|
item_class->flip_desc);
|
|
|
|
g_object_freeze_notify (G_OBJECT (item));
|
|
|
|
item_class->flip (item, context, flip_type, axis, clip_result);
|
|
|
|
g_object_thaw_notify (G_OBJECT (item));
|
|
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
void
|
|
gimp_item_rotate (GimpItem *item,
|
|
GimpContext *context,
|
|
GimpRotationType rotate_type,
|
|
gdouble center_x,
|
|
gdouble center_y,
|
|
gboolean clip_result)
|
|
{
|
|
GimpItemClass *item_class;
|
|
GimpImage *image;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (gimp_item_is_attached (item));
|
|
g_return_if_fail (GIMP_IS_CONTEXT (context));
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
image = gimp_item_get_image (item);
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM,
|
|
item_class->rotate_desc);
|
|
|
|
g_object_freeze_notify (G_OBJECT (item));
|
|
|
|
item_class->rotate (item, context, rotate_type, center_x, center_y,
|
|
clip_result);
|
|
|
|
g_object_thaw_notify (G_OBJECT (item));
|
|
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
void
|
|
gimp_item_transform (GimpItem *item,
|
|
GimpContext *context,
|
|
const GimpMatrix3 *matrix,
|
|
GimpTransformDirection direction,
|
|
GimpInterpolationType interpolation,
|
|
gint recursion_level,
|
|
GimpTransformResize clip_result,
|
|
GimpProgress *progress)
|
|
{
|
|
GimpItemClass *item_class;
|
|
GimpImage *image;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (gimp_item_is_attached (item));
|
|
g_return_if_fail (GIMP_IS_CONTEXT (context));
|
|
g_return_if_fail (matrix != NULL);
|
|
g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
image = gimp_item_get_image (item);
|
|
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TRANSFORM,
|
|
item_class->transform_desc);
|
|
|
|
g_object_freeze_notify (G_OBJECT (item));
|
|
|
|
item_class->transform (item, context, matrix, direction, interpolation,
|
|
recursion_level, clip_result, progress);
|
|
|
|
g_object_thaw_notify (G_OBJECT (item));
|
|
|
|
gimp_image_undo_group_end (image);
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_stroke (GimpItem *item,
|
|
GimpDrawable *drawable,
|
|
GimpContext *context,
|
|
GimpStrokeOptions *stroke_options,
|
|
gboolean use_default_values,
|
|
gboolean push_undo,
|
|
GimpProgress *progress,
|
|
GError **error)
|
|
{
|
|
GimpItemClass *item_class;
|
|
gboolean retval = FALSE;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
g_return_val_if_fail (gimp_item_is_attached (item), FALSE);
|
|
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
|
|
g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), FALSE);
|
|
g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE);
|
|
g_return_val_if_fail (GIMP_IS_STROKE_OPTIONS (stroke_options), FALSE);
|
|
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
|
|
if (item_class->stroke)
|
|
{
|
|
GimpImage *image = gimp_item_get_image (item);
|
|
|
|
gimp_stroke_options_prepare (stroke_options, context, use_default_values);
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_PAINT,
|
|
item_class->stroke_desc);
|
|
|
|
retval = item_class->stroke (item, drawable, stroke_options, push_undo,
|
|
progress, error);
|
|
|
|
if (push_undo)
|
|
gimp_image_undo_group_end (image);
|
|
|
|
gimp_stroke_options_finish (stroke_options);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
void
|
|
gimp_item_to_selection (GimpItem *item,
|
|
GimpChannelOps op,
|
|
gboolean antialias,
|
|
gboolean feather,
|
|
gdouble feather_radius_x,
|
|
gdouble feather_radius_y)
|
|
{
|
|
GimpItemClass *item_class;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (gimp_item_is_attached (item));
|
|
|
|
item_class = GIMP_ITEM_GET_CLASS (item);
|
|
|
|
if (item_class->to_selection)
|
|
item_class->to_selection (item, op, antialias,
|
|
feather, feather_radius_x, feather_radius_y);
|
|
}
|
|
|
|
GeglNode *
|
|
gimp_item_get_node (GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
if (item->node)
|
|
return item->node;
|
|
|
|
return GIMP_ITEM_GET_CLASS (item)->get_node (item);
|
|
}
|
|
|
|
GeglNode *
|
|
gimp_item_peek_node (GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
return item->node;
|
|
}
|
|
|
|
GeglNode *
|
|
gimp_item_get_offset_node (GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
if (! item->offset_node)
|
|
{
|
|
GeglNode *node = gimp_item_get_node (item);
|
|
|
|
item->offset_node =
|
|
gegl_node_new_child (node,
|
|
"operation", "gegl:translate",
|
|
"x", (gdouble) item->offset_x,
|
|
"y", (gdouble) item->offset_y,
|
|
NULL);
|
|
}
|
|
|
|
return item->offset_node;
|
|
}
|
|
|
|
gint
|
|
gimp_item_get_ID (GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), -1);
|
|
|
|
return item->ID;
|
|
}
|
|
|
|
GimpItem *
|
|
gimp_item_get_by_ID (Gimp *gimp,
|
|
gint item_id)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
|
|
|
|
if (gimp->item_table == NULL)
|
|
return NULL;
|
|
|
|
return (GimpItem *) g_hash_table_lookup (gimp->item_table,
|
|
GINT_TO_POINTER (item_id));
|
|
}
|
|
|
|
GimpTattoo
|
|
gimp_item_get_tattoo (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), 0);
|
|
|
|
return item->tattoo;
|
|
}
|
|
|
|
void
|
|
gimp_item_set_tattoo (GimpItem *item,
|
|
GimpTattoo tattoo)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
|
|
item->tattoo = tattoo;
|
|
}
|
|
|
|
GimpImage *
|
|
gimp_item_get_image (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
return item->image;
|
|
}
|
|
|
|
void
|
|
gimp_item_set_image (GimpItem *item,
|
|
GimpImage *image)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (! gimp_item_is_attached (item));
|
|
g_return_if_fail (image == NULL || GIMP_IS_IMAGE (image));
|
|
|
|
if (image == NULL)
|
|
{
|
|
item->tattoo = 0;
|
|
}
|
|
else if (item->tattoo == 0 || item->image != image)
|
|
{
|
|
item->tattoo = gimp_image_get_new_tattoo (image);
|
|
}
|
|
|
|
item->image = image;
|
|
}
|
|
|
|
void
|
|
gimp_item_parasite_attach (GimpItem *item,
|
|
const GimpParasite *parasite)
|
|
{
|
|
GimpParasite copy;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (parasite != NULL);
|
|
|
|
/* make a temporary copy of the GimpParasite struct because
|
|
* gimp_parasite_shift_parent() changes it
|
|
*/
|
|
copy = *parasite;
|
|
|
|
if (gimp_item_is_attached (item))
|
|
{
|
|
/* only set the dirty bit manually if we can be saved and the new
|
|
* parasite differs from the current one and we aren't undoable
|
|
*/
|
|
if (gimp_parasite_is_undoable (©))
|
|
{
|
|
/* do a group in case we have attach_parent set */
|
|
gimp_image_undo_group_start (item->image,
|
|
GIMP_UNDO_GROUP_PARASITE_ATTACH,
|
|
C_("undo-type", "Attach Parasite"));
|
|
|
|
gimp_image_undo_push_item_parasite (item->image, NULL, item, ©);
|
|
}
|
|
else if (gimp_parasite_is_persistent (©) &&
|
|
! gimp_parasite_compare (©,
|
|
gimp_item_parasite_find
|
|
(item, gimp_parasite_name (©))))
|
|
{
|
|
gimp_image_undo_push_cantundo (item->image,
|
|
C_("undo-type", "Attach Parasite to Item"));
|
|
}
|
|
}
|
|
|
|
gimp_parasite_list_add (item->parasites, ©);
|
|
|
|
if (gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_PARENT))
|
|
{
|
|
gimp_parasite_shift_parent (©);
|
|
gimp_image_parasite_attach (item->image, ©);
|
|
}
|
|
else if (gimp_parasite_has_flag (©, GIMP_PARASITE_ATTACH_GRANDPARENT))
|
|
{
|
|
gimp_parasite_shift_parent (©);
|
|
gimp_parasite_shift_parent (©);
|
|
gimp_parasite_attach (item->image->gimp, ©);
|
|
}
|
|
|
|
if (gimp_item_is_attached (item) &&
|
|
gimp_parasite_is_undoable (©))
|
|
{
|
|
gimp_image_undo_group_end (item->image);
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_item_parasite_detach (GimpItem *item,
|
|
const gchar *name)
|
|
{
|
|
const GimpParasite *parasite;
|
|
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (name != NULL);
|
|
|
|
parasite = gimp_parasite_list_find (item->parasites, name);
|
|
|
|
if (! parasite)
|
|
return;
|
|
|
|
if (gimp_parasite_is_undoable (parasite))
|
|
{
|
|
gimp_image_undo_push_item_parasite_remove (item->image,
|
|
C_("undo-type", "Remove Parasite from Item"),
|
|
item,
|
|
gimp_parasite_name (parasite));
|
|
}
|
|
else if (gimp_parasite_is_persistent (parasite))
|
|
{
|
|
gimp_image_undo_push_cantundo (item->image,
|
|
C_("undo-type", "Remove Parasite from Item"));
|
|
}
|
|
|
|
gimp_parasite_list_remove (item->parasites, name);
|
|
}
|
|
|
|
const GimpParasite *
|
|
gimp_item_parasite_find (const GimpItem *item,
|
|
const gchar *name)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
|
|
return gimp_parasite_list_find (item->parasites, name);
|
|
}
|
|
|
|
static void
|
|
gimp_item_parasite_list_foreach_func (gchar *name,
|
|
GimpParasite *parasite,
|
|
gchar ***cur)
|
|
{
|
|
*(*cur)++ = (gchar *) g_strdup (name);
|
|
}
|
|
|
|
gchar **
|
|
gimp_item_parasite_list (const GimpItem *item,
|
|
gint *count)
|
|
{
|
|
gchar **list;
|
|
gchar **cur;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
|
|
g_return_val_if_fail (count != NULL, NULL);
|
|
|
|
*count = gimp_parasite_list_length (item->parasites);
|
|
|
|
cur = list = g_new (gchar *, *count);
|
|
|
|
gimp_parasite_list_foreach (item->parasites,
|
|
(GHFunc) gimp_item_parasite_list_foreach_func,
|
|
&cur);
|
|
|
|
return list;
|
|
}
|
|
|
|
void
|
|
gimp_item_set_visible (GimpItem *item,
|
|
gboolean visible,
|
|
gboolean push_undo)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
|
|
visible = visible ? TRUE : FALSE;
|
|
|
|
if (gimp_item_get_visible (item) != visible)
|
|
{
|
|
if (push_undo && gimp_item_is_attached (item))
|
|
{
|
|
GimpImage *image = gimp_item_get_image (item);
|
|
|
|
if (image)
|
|
gimp_image_undo_push_item_visibility (image, NULL, item);
|
|
}
|
|
|
|
item->visible = visible;
|
|
|
|
g_signal_emit (item, gimp_item_signals[VISIBILITY_CHANGED], 0);
|
|
|
|
g_object_notify (G_OBJECT (item), "visible");
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_get_visible (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
return item->visible;
|
|
}
|
|
|
|
void
|
|
gimp_item_set_linked (GimpItem *item,
|
|
gboolean linked,
|
|
gboolean push_undo)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
|
|
linked = linked ? TRUE : FALSE;
|
|
|
|
if (gimp_item_get_linked (item) != linked)
|
|
{
|
|
if (push_undo && gimp_item_is_attached (item))
|
|
{
|
|
GimpImage *image = gimp_item_get_image (item);
|
|
|
|
if (image)
|
|
gimp_image_undo_push_item_linked (image, NULL, item);
|
|
}
|
|
|
|
item->linked = linked;
|
|
|
|
g_signal_emit (item, gimp_item_signals[LINKED_CHANGED], 0);
|
|
|
|
g_object_notify (G_OBJECT (item), "linked");
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_get_linked (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
return item->linked;
|
|
}
|
|
|
|
void
|
|
gimp_item_set_lock_content (GimpItem *item,
|
|
gboolean lock_content,
|
|
gboolean push_undo)
|
|
{
|
|
g_return_if_fail (GIMP_IS_ITEM (item));
|
|
g_return_if_fail (gimp_item_can_lock_content (item));
|
|
|
|
lock_content = lock_content ? TRUE : FALSE;
|
|
|
|
if (gimp_item_get_lock_content (item) != lock_content)
|
|
{
|
|
if (push_undo && gimp_item_is_attached (item))
|
|
{
|
|
/* Right now I don't think this should be pushed. */
|
|
#if 0
|
|
GimpImage *image = gimp_item_get_image (item);
|
|
|
|
gimp_image_undo_push_item_lock_content (image, NULL, item);
|
|
#endif
|
|
}
|
|
|
|
item->lock_content = lock_content;
|
|
|
|
g_signal_emit (item, gimp_item_signals[LOCK_CONTENT_CHANGED], 0);
|
|
|
|
g_object_notify (G_OBJECT (item), "lock-content");
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_get_lock_content (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
return item->lock_content;
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_can_lock_content (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_is_content_locked (const GimpItem *item)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
return GIMP_ITEM_GET_CLASS (item)->is_content_locked (item);
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_mask_bounds (GimpItem *item,
|
|
gint *x1,
|
|
gint *y1,
|
|
gint *x2,
|
|
gint *y2)
|
|
{
|
|
GimpImage *image;
|
|
GimpChannel *selection;
|
|
gint tmp_x1, tmp_y1;
|
|
gint tmp_x2, tmp_y2;
|
|
gboolean retval;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
g_return_val_if_fail (gimp_item_is_attached (item), FALSE);
|
|
|
|
image = gimp_item_get_image (item);
|
|
selection = gimp_image_get_mask (image);
|
|
|
|
if (GIMP_ITEM (selection) != item &&
|
|
gimp_channel_bounds (selection, &tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2))
|
|
{
|
|
gint off_x, off_y;
|
|
|
|
gimp_item_get_offset (item, &off_x, &off_y);
|
|
|
|
tmp_x1 = CLAMP (tmp_x1 - off_x, 0, gimp_item_get_width (item));
|
|
tmp_y1 = CLAMP (tmp_y1 - off_y, 0, gimp_item_get_height (item));
|
|
tmp_x2 = CLAMP (tmp_x2 - off_x, 0, gimp_item_get_width (item));
|
|
tmp_y2 = CLAMP (tmp_y2 - off_y, 0, gimp_item_get_height (item));
|
|
|
|
retval = TRUE;
|
|
}
|
|
else
|
|
{
|
|
tmp_x1 = 0;
|
|
tmp_y1 = 0;
|
|
tmp_x2 = gimp_item_get_width (item);
|
|
tmp_y2 = gimp_item_get_height (item);
|
|
|
|
retval = FALSE;
|
|
}
|
|
|
|
if (x1) *x1 = tmp_x1;
|
|
if (y1) *y1 = tmp_y1;
|
|
if (x2) *x2 = tmp_x2;
|
|
if (y2) *y2 = tmp_y2;
|
|
|
|
return retval;;
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_mask_intersect (GimpItem *item,
|
|
gint *x,
|
|
gint *y,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
GimpImage *image;
|
|
GimpChannel *selection;
|
|
gint tmp_x, tmp_y;
|
|
gint tmp_width, tmp_height;
|
|
gboolean retval;
|
|
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
g_return_val_if_fail (gimp_item_is_attached (item), FALSE);
|
|
|
|
image = gimp_item_get_image (item);
|
|
selection = gimp_image_get_mask (image);
|
|
|
|
if (GIMP_ITEM (selection) != item &&
|
|
gimp_channel_bounds (selection, &tmp_x, &tmp_y, &tmp_width, &tmp_height))
|
|
{
|
|
gint off_x, off_y;
|
|
|
|
gimp_item_get_offset (item, &off_x, &off_y);
|
|
|
|
tmp_width -= tmp_x;
|
|
tmp_height -= tmp_y;
|
|
|
|
retval = gimp_rectangle_intersect (tmp_x - off_x, tmp_y - off_y,
|
|
tmp_width, tmp_height,
|
|
0, 0,
|
|
gimp_item_get_width (item),
|
|
gimp_item_get_height (item),
|
|
&tmp_x, &tmp_y,
|
|
&tmp_width, &tmp_height);
|
|
}
|
|
else
|
|
{
|
|
tmp_x = 0;
|
|
tmp_y = 0;
|
|
tmp_width = gimp_item_get_width (item);
|
|
tmp_height = gimp_item_get_height (item);
|
|
|
|
retval = TRUE;
|
|
}
|
|
|
|
if (x) *x = tmp_x;
|
|
if (y) *y = tmp_y;
|
|
if (width) *width = tmp_width;
|
|
if (height) *height = tmp_height;
|
|
|
|
return retval;
|
|
}
|
|
|
|
gboolean
|
|
gimp_item_is_in_set (GimpItem *item,
|
|
GimpItemSet set)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
|
|
|
switch (set)
|
|
{
|
|
case GIMP_ITEM_SET_NONE:
|
|
return FALSE;
|
|
|
|
case GIMP_ITEM_SET_ALL:
|
|
return TRUE;
|
|
|
|
case GIMP_ITEM_SET_IMAGE_SIZED:
|
|
return (gimp_item_get_width (item) == gimp_image_get_width (item->image) &&
|
|
gimp_item_get_height (item) == gimp_image_get_height (item->image));
|
|
|
|
case GIMP_ITEM_SET_VISIBLE:
|
|
return gimp_item_get_visible (item);
|
|
|
|
case GIMP_ITEM_SET_LINKED:
|
|
return gimp_item_get_linked (item);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|