mirror of https://github.com/GNOME/gimp.git
app, devel-docs: saving the item sets in XCF (bumping to XCF 16).
We now save and load layer and channel item sets. Only missing set types are path ones, but the whole path item is just its own exception in the XCF format, and adding support for it, while keeping compatibility with older XCF seem like a small headache. I could do it, but I actually wonder if it is worth it. Would people really need to store sets of paths? Also this commit finally gets rid of any remnant of the old item "link" concept (I think), so we are getting close to merging the branch.
This commit is contained in:
parent
7c5e88323c
commit
362fae9147
|
@ -2914,6 +2914,21 @@ gimp_image_get_xcf_version (GimpImage *image,
|
|||
}
|
||||
}
|
||||
|
||||
if (gimp_image_get_stored_item_sets (image, GIMP_TYPE_LAYER) ||
|
||||
gimp_image_get_stored_item_sets (image, GIMP_TYPE_CHANNEL))
|
||||
{
|
||||
ADD_REASON (g_strdup_printf (_("Item set and pattern search in item's name were "
|
||||
"added in %s"), "GIMP 3.0.0"));
|
||||
version = MAX (16, version);
|
||||
}
|
||||
if (g_list_length (gimp_image_get_selected_channels (image)) > 1)
|
||||
{
|
||||
ADD_REASON (g_strdup_printf (_("Multiple channel selection was "
|
||||
"added in %s"), "GIMP 3.0.0"));
|
||||
version = MAX (16, version);
|
||||
}
|
||||
|
||||
|
||||
#undef ADD_REASON
|
||||
|
||||
switch (version)
|
||||
|
@ -2945,6 +2960,7 @@ gimp_image_get_xcf_version (GimpImage *image,
|
|||
break;
|
||||
case 14:
|
||||
case 15:
|
||||
case 16:
|
||||
if (gimp_version) *gimp_version = 300;
|
||||
if (version_string) *version_string = "GIMP 3.0";
|
||||
break;
|
||||
|
@ -5439,8 +5455,16 @@ gimp_image_store_item_set (GimpImage *image,
|
|||
|
||||
for (iter = *stored_sets; iter; iter = iter->next)
|
||||
{
|
||||
gboolean is_pattern;
|
||||
gboolean is_pattern2;
|
||||
GimpSelectMethod pattern_syntax;
|
||||
GimpSelectMethod pattern_syntax2;
|
||||
|
||||
is_pattern = gimp_item_list_is_pattern (iter->data, &pattern_syntax);
|
||||
is_pattern2 = gimp_item_list_is_pattern (set, &pattern_syntax2);
|
||||
|
||||
/* Remove a previous item set of same type and name. */
|
||||
if (gimp_item_list_is_pattern (iter->data) == gimp_item_list_is_pattern (set) &&
|
||||
if (is_pattern == is_pattern2 && (! is_pattern || pattern_syntax == pattern_syntax2) &&
|
||||
g_strcmp0 (gimp_object_get_name (iter->data), gimp_object_get_name (set)) == 0)
|
||||
break;
|
||||
}
|
||||
|
@ -5513,9 +5537,10 @@ gimp_image_unlink_item_set (GimpImage *image,
|
|||
/*
|
||||
* @gimp_image_get_stored_item_sets:
|
||||
* @image:
|
||||
* @item_type:
|
||||
*
|
||||
* Returns: (transfer none): the list of all the layer sets (which you
|
||||
* should not modify). Order of items is not relevant.
|
||||
* should not modify). Order of items is relevant.
|
||||
*/
|
||||
GList *
|
||||
gimp_image_get_stored_item_sets (GimpImage *image,
|
||||
|
|
|
@ -53,7 +53,6 @@ enum
|
|||
{
|
||||
REMOVED,
|
||||
VISIBILITY_CHANGED,
|
||||
LINKED_CHANGED,
|
||||
COLOR_TAG_CHANGED,
|
||||
LOCK_CONTENT_CHANGED,
|
||||
LOCK_POSITION_CHANGED,
|
||||
|
@ -71,7 +70,6 @@ enum
|
|||
PROP_OFFSET_X,
|
||||
PROP_OFFSET_Y,
|
||||
PROP_VISIBLE,
|
||||
PROP_LINKED,
|
||||
PROP_COLOR_TAG,
|
||||
PROP_LOCK_CONTENT,
|
||||
PROP_LOCK_POSITION,
|
||||
|
@ -97,7 +95,6 @@ struct _GimpItemPrivate
|
|||
guint visible : 1; /* item visibility */
|
||||
guint bind_visible_to_active : 1; /* visibility bound to active */
|
||||
|
||||
guint linked : 1; /* control linkage */
|
||||
guint lock_content : 1; /* content editability */
|
||||
guint lock_position : 1; /* content movability */
|
||||
guint lock_visibility : 1; /* automatic visibility change */
|
||||
|
@ -204,14 +201,6 @@ gimp_item_class_init (GimpItemClass *klass)
|
|||
NULL, NULL, NULL,
|
||||
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, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
gimp_item_signals[COLOR_TAG_CHANGED] =
|
||||
g_signal_new ("color-tag-changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
|
@ -257,7 +246,6 @@ gimp_item_class_init (GimpItemClass *klass)
|
|||
|
||||
klass->removed = NULL;
|
||||
klass->visibility_changed = NULL;
|
||||
klass->linked_changed = NULL;
|
||||
klass->color_tag_changed = NULL;
|
||||
klass->lock_content_changed = NULL;
|
||||
klass->lock_position_changed = NULL;
|
||||
|
@ -329,10 +317,6 @@ gimp_item_class_init (GimpItemClass *klass)
|
|||
TRUE,
|
||||
GIMP_PARAM_READABLE);
|
||||
|
||||
gimp_item_props[PROP_LINKED] = g_param_spec_boolean ("linked", NULL, NULL,
|
||||
FALSE,
|
||||
GIMP_PARAM_READABLE);
|
||||
|
||||
gimp_item_props[PROP_COLOR_TAG] = g_param_spec_enum ("color-tag", NULL, NULL,
|
||||
GIMP_TYPE_COLOR_TAG,
|
||||
GIMP_COLOR_TAG_NONE,
|
||||
|
@ -453,9 +437,6 @@ gimp_item_get_property (GObject *object,
|
|||
case PROP_VISIBLE:
|
||||
g_value_set_boolean (value, private->visible);
|
||||
break;
|
||||
case PROP_LINKED:
|
||||
g_value_set_boolean (value, private->linked);
|
||||
break;
|
||||
case PROP_COLOR_TAG:
|
||||
g_value_set_enum (value, private->color_tag);
|
||||
break;
|
||||
|
@ -2380,14 +2361,6 @@ gimp_item_bind_visible_to_active (GimpItem *item,
|
|||
gimp_filter_set_active (GIMP_FILTER (item), gimp_item_get_visible (item));
|
||||
}
|
||||
|
||||
gboolean
|
||||
gimp_item_get_linked (GimpItem *item)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_ITEM (item), FALSE);
|
||||
|
||||
return GET_PRIVATE (item)->linked;
|
||||
}
|
||||
|
||||
void
|
||||
gimp_item_set_color_tag (GimpItem *item,
|
||||
GimpColorTag color_tag,
|
||||
|
|
|
@ -44,7 +44,6 @@ struct _GimpItemClass
|
|||
/* signals */
|
||||
void (* removed) (GimpItem *item);
|
||||
void (* visibility_changed) (GimpItem *item);
|
||||
void (* linked_changed) (GimpItem *item);
|
||||
void (* color_tag_changed) (GimpItem *item);
|
||||
void (* lock_content_changed) (GimpItem *item);
|
||||
void (* lock_position_changed) (GimpItem *item);
|
||||
|
@ -364,8 +363,6 @@ gboolean gimp_item_is_visible (GimpItem *item);
|
|||
void gimp_item_bind_visible_to_active (GimpItem *item,
|
||||
gboolean bind);
|
||||
|
||||
gboolean gimp_item_get_linked (GimpItem *item);
|
||||
|
||||
void gimp_item_set_color_tag (GimpItem *item,
|
||||
GimpColorTag color_tag,
|
||||
gboolean push_undo);
|
||||
|
|
|
@ -65,7 +65,6 @@ struct _GimpItemListPrivate
|
|||
{
|
||||
GimpImage *image;
|
||||
|
||||
gchar *label; /* Item set name or pattern. */
|
||||
gboolean is_pattern; /* Whether a named fixed set or a pattern-search. */
|
||||
GimpSelectMethod select_method; /* Pattern format if is_pattern is TRUE */
|
||||
|
||||
|
@ -174,7 +173,6 @@ gimp_item_list_init (GimpItemList *set)
|
|||
{
|
||||
set->p = gimp_item_list_get_instance_private (set);
|
||||
|
||||
set->p->label = NULL;
|
||||
set->p->items = NULL;
|
||||
set->p->select_method = GIMP_SELECT_PLAIN_TEXT;
|
||||
set->p->is_pattern = FALSE;
|
||||
|
@ -188,7 +186,6 @@ gimp_item_list_constructed (GObject *object)
|
|||
G_OBJECT_CLASS (parent_class)->constructed (object);
|
||||
|
||||
gimp_assert (GIMP_IS_IMAGE (set->p->image));
|
||||
gimp_assert (set->p->items != NULL || set->p->is_pattern);
|
||||
gimp_assert (set->p->item_type == GIMP_TYPE_LAYER ||
|
||||
set->p->item_type == GIMP_TYPE_VECTORS ||
|
||||
set->p->item_type == GIMP_TYPE_CHANNEL);
|
||||
|
@ -244,7 +241,6 @@ gimp_item_list_finalize (GObject *object)
|
|||
g_list_free (set->p->items);
|
||||
g_list_free_full (set->p->deleted_items,
|
||||
(GDestroyNotify) gimp_item_list_free_deleted_item);
|
||||
g_free (set->p->label);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
@ -460,14 +456,47 @@ gimp_item_list_get_items (GimpItemList *set,
|
|||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_item_list_is_pattern:
|
||||
* @set: The #GimpItemList.
|
||||
* @pattern_syntax: The type of patterns @set handles.
|
||||
*
|
||||
* Indicate if @set is a pattern list. If the returned value is %TRUE,
|
||||
* then @pattern_syntax will be set to the syntax we are dealing with.
|
||||
*
|
||||
* Returns: %TRUE if @set is a pattern list, %FALSE if it is a named
|
||||
* list.
|
||||
*/
|
||||
gboolean
|
||||
gimp_item_list_is_pattern (GimpItemList *set)
|
||||
gimp_item_list_is_pattern (GimpItemList *set,
|
||||
GimpSelectMethod *pattern_syntax)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_ITEM_LIST (set), FALSE);
|
||||
|
||||
if (set->p->is_pattern && pattern_syntax)
|
||||
*pattern_syntax = set->p->select_method;
|
||||
|
||||
return (set->p->is_pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* gimp_item_list_is_pattern:
|
||||
* @set: The #GimpItemList.
|
||||
* @item: #GimpItem to add to @set.
|
||||
*
|
||||
* Add @item to the named list @set whose item type must also agree.
|
||||
*/
|
||||
void
|
||||
gimp_item_list_add (GimpItemList *set,
|
||||
GimpItem *item)
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_ITEM_LIST (set));
|
||||
g_return_if_fail (! gimp_item_list_is_pattern (set, NULL));
|
||||
g_return_if_fail (g_type_is_a (G_TYPE_FROM_INSTANCE (item), set->p->item_type));
|
||||
|
||||
set->p->items = g_list_prepend (set->p->items, item);
|
||||
}
|
||||
|
||||
|
||||
/* Private functions */
|
||||
|
||||
|
|
|
@ -61,7 +61,10 @@ GimpItemList * gimp_item_list_pattern_new (GimpImage *image,
|
|||
GType gimp_item_list_get_item_type (GimpItemList *set);
|
||||
GList * gimp_item_list_get_items (GimpItemList *set,
|
||||
GError **error);
|
||||
gboolean gimp_item_list_is_pattern (GimpItemList *set);
|
||||
gboolean gimp_item_list_is_pattern (GimpItemList *set,
|
||||
GimpSelectMethod *pattern_syntax);
|
||||
|
||||
void gimp_item_list_add (GimpItemList *set,
|
||||
GimpItem *item);
|
||||
|
||||
#endif /* __GIMP_ITEM_LIST_H__ */
|
||||
|
|
|
@ -394,7 +394,6 @@ EXPORTS
|
|||
gimp_item_get_by_ID
|
||||
gimp_item_get_ID
|
||||
gimp_item_get_image
|
||||
gimp_item_get_linked
|
||||
gimp_item_get_type
|
||||
gimp_item_get_visible
|
||||
gimp_item_height
|
||||
|
|
|
@ -60,12 +60,10 @@ gimp_pdb_compat_procs_register (GimpPDB *pdb,
|
|||
{ "gimp-image-active-drawable", "gimp-image-get-active-drawable" },
|
||||
{ "gimp-image-floating-selection", "gimp-image-get-floating-sel" },
|
||||
{ "gimp-layer-delete", "gimp-item-delete" },
|
||||
{ "gimp-layer-get-linked", "gimp-item-get-linked" },
|
||||
{ "gimp-layer-get-name", "gimp-item-get-name" },
|
||||
{ "gimp-layer-get-tattoo", "gimp-item-get-tattoo" },
|
||||
{ "gimp-layer-get-visible", "gimp-item-get-visible" },
|
||||
{ "gimp-layer-mask", "gimp-layer-get-mask" },
|
||||
{ "gimp-layer-set-linked", "gimp-item-set-linked" },
|
||||
{ "gimp-layer-set-name", "gimp-item-set-name" },
|
||||
{ "gimp-layer-set-tattoo", "gimp-item-set-tattoo" },
|
||||
{ "gimp-layer-set-visible", "gimp-item-set-visible" },
|
||||
|
@ -115,8 +113,6 @@ gimp_pdb_compat_procs_register (GimpPDB *pdb,
|
|||
{ "gimp-drawable-set-name", "gimp-item-set-name" },
|
||||
{ "gimp-drawable-get-visible", "gimp-item-get-visible" },
|
||||
{ "gimp-drawable-set-visible", "gimp-item-set-visible" },
|
||||
{ "gimp-drawable-get-linked", "gimp-item-get-linked" },
|
||||
{ "gimp-drawable-set-linked", "gimp-item-set-linked" },
|
||||
{ "gimp-drawable-get-tattoo", "gimp-item-get-tattoo" },
|
||||
{ "gimp-drawable-set-tattoo", "gimp-item-set-tattoo" },
|
||||
{ "gimp-drawable-parasite-find", "gimp-item-get-parasite" },
|
||||
|
@ -142,8 +138,6 @@ gimp_pdb_compat_procs_register (GimpPDB *pdb,
|
|||
{ "gimp-vectors-set-name", "gimp-item-set-name" },
|
||||
{ "gimp-vectors-get-visible", "gimp-item-get-visible" },
|
||||
{ "gimp-vectors-set-visible", "gimp-item-set-visible" },
|
||||
{ "gimp-vectors-get-linked", "gimp-item-get-linked" },
|
||||
{ "gimp-vectors-set-linked", "gimp-item-set-linked" },
|
||||
{ "gimp-vectors-get-tattoo", "gimp-item-get-tattoo" },
|
||||
{ "gimp-vectors-set-tattoo", "gimp-item-set-tattoo" },
|
||||
{ "gimp-vectors-parasite-find", "gimp-item-get-parasite" },
|
||||
|
|
|
@ -1122,7 +1122,7 @@ gimp_layer_tree_view_layer_links_changed (GimpImage *image,
|
|||
|
||||
label = gtk_label_new (gimp_object_get_name (iter->data));
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
if (gimp_item_list_is_pattern (iter->data))
|
||||
if (gimp_item_list_is_pattern (iter->data, NULL))
|
||||
{
|
||||
PangoAttrList *attrs = pango_attr_list_new ();
|
||||
|
||||
|
|
|
@ -256,6 +256,10 @@ xcf_load_image (Gimp *gimp,
|
|||
|
||||
GIMP_LOG (XCF, "image props loaded");
|
||||
|
||||
/* Order matters for item sets. */
|
||||
info->layer_sets = g_list_reverse (info->layer_sets);
|
||||
info->channel_sets = g_list_reverse (info->channel_sets);
|
||||
|
||||
/* check for a GimpGrid parasite */
|
||||
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
|
||||
gimp_grid_parasite_name ());
|
||||
|
@ -705,19 +709,49 @@ xcf_load_image (Gimp *gimp,
|
|||
_("Linked Channels"),
|
||||
info->linked_channels);
|
||||
gimp_image_store_item_set (image, set);
|
||||
g_clear_pointer (&info->linked_layers, g_list_free);
|
||||
g_clear_pointer (&info->linked_channels, g_list_free);
|
||||
}
|
||||
if (info->linked_paths)
|
||||
{
|
||||
/* It is kind of ugly but vectors are really implemented as
|
||||
* exception in our XCF spec and building over it seems like a
|
||||
* mistake. Since I'm seriously not sure this would be much of an
|
||||
* issue, I'll let it as it for now.
|
||||
* Note that it's still possible to multi-select paths. It's only
|
||||
* not possible to store these selections.
|
||||
*
|
||||
* Only warn for more than 1 linked path. Less is kind of
|
||||
* pointless and doesn't deserve worrying people for no reason.
|
||||
*/
|
||||
if (g_list_length (info->linked_paths) > 1)
|
||||
g_printerr ("xcf: some paths were linked. "
|
||||
"GIMP does not support linked paths since version 3.0.\n");
|
||||
|
||||
#if 0
|
||||
GimpItemList *set;
|
||||
|
||||
set = gimp_item_list_named_new (image, GIMP_TYPE_VECTORS,
|
||||
_("Linked Paths"),
|
||||
info->linked_paths);
|
||||
gimp_image_store_item_set (image, set);
|
||||
g_clear_pointer (&info->linked_layers, g_list_free);
|
||||
#endif
|
||||
g_clear_pointer (&info->linked_paths, g_list_free);
|
||||
}
|
||||
|
||||
for (iter = g_list_last (info->layer_sets); iter; iter = iter->prev)
|
||||
{
|
||||
if (iter->data)
|
||||
gimp_image_store_item_set (image, iter->data);
|
||||
}
|
||||
g_list_free (info->layer_sets);
|
||||
|
||||
for (iter = g_list_last (info->channel_sets); iter; iter = iter->prev)
|
||||
{
|
||||
if (iter->data)
|
||||
gimp_image_store_item_set (image, iter->data);
|
||||
}
|
||||
g_list_free (info->channel_sets);
|
||||
|
||||
if (info->file)
|
||||
gimp_image_set_file (image, info->file);
|
||||
|
||||
|
@ -1203,6 +1237,74 @@ xcf_load_image_props (XcfInfo *info,
|
|||
}
|
||||
break;
|
||||
|
||||
case PROP_ITEM_SET:
|
||||
{
|
||||
GimpItemList *set = NULL;
|
||||
gchar *label;
|
||||
GType item_type = 0;
|
||||
guint32 itype;
|
||||
guint32 method;
|
||||
|
||||
xcf_read_int32 (info, &itype, 1);
|
||||
xcf_read_int32 (info, &method, 1);
|
||||
xcf_read_string (info, &label, 1);
|
||||
|
||||
if (itype == 0)
|
||||
item_type = GIMP_TYPE_LAYER;
|
||||
else
|
||||
item_type = GIMP_TYPE_CHANNEL;
|
||||
|
||||
if (itype > 1)
|
||||
{
|
||||
g_printerr ("xcf: unsupported item set '%s' type: %d (skipping)\n",
|
||||
label ? label : "unnamed", itype);
|
||||
/* Only case where we break because we wouldn't even
|
||||
* know where to categorize the item set anyway. */
|
||||
break;
|
||||
}
|
||||
else if (label == NULL)
|
||||
{
|
||||
g_printerr ("xcf: item set without a name or pattern (skipping)\n");
|
||||
}
|
||||
else if (method != G_MAXUINT32 && method > GIMP_SELECT_GLOB_PATTERN)
|
||||
{
|
||||
g_printerr ("xcf: unsupported item set '%s' selection method attribute: 0x%x (skipping)\n",
|
||||
label, method);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (method == G_MAXUINT32)
|
||||
{
|
||||
/* Don't use gimp_item_list_named_new() because it
|
||||
* doesn't allow NULL items (it would try to get the
|
||||
* selected items instead).
|
||||
*/
|
||||
set = g_object_new (GIMP_TYPE_ITEM_LIST,
|
||||
"image", image,
|
||||
"name", label,
|
||||
"is-pattern", FALSE,
|
||||
"item-type", item_type,
|
||||
"items", NULL,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
set = gimp_item_list_pattern_new (image, item_type,
|
||||
method, label);
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: we are still adding invalid item sets as NULL on
|
||||
* purpose, in order not to break order-base association
|
||||
* between PROP_ITEM_SET and PROP_ITEM_SET_ITEM.
|
||||
*/
|
||||
if (item_type == GIMP_TYPE_LAYER)
|
||||
info->layer_sets = g_list_prepend (info->layer_sets, set);
|
||||
else
|
||||
info->channel_sets = g_list_prepend (info->channel_sets, set);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef GIMP_UNSTABLE
|
||||
g_printerr ("unexpected/unknown image property: %d (skipping)\n",
|
||||
|
@ -1579,6 +1681,29 @@ xcf_load_layer_props (XcfInfo *info,
|
|||
xcf_read_int32 (info, group_layer_flags, 1);
|
||||
break;
|
||||
|
||||
case PROP_ITEM_SET_ITEM:
|
||||
{
|
||||
GimpItemList *set;
|
||||
guint32 n;
|
||||
|
||||
xcf_read_int32 (info, &n, 1);
|
||||
set = g_list_nth_data (info->layer_sets, n);
|
||||
if (set == NULL)
|
||||
g_printerr ("xcf: layer '%s' cannot be added to unknown layer set at index %d (skipping)\n",
|
||||
gimp_object_get_name (*layer), n);
|
||||
else if (! g_type_is_a (G_TYPE_FROM_INSTANCE (*layer),
|
||||
gimp_item_list_get_item_type (set)))
|
||||
g_printerr ("xcf: layer '%s' cannot be added to item set '%s' with item type %s (skipping)\n",
|
||||
gimp_object_get_name (*layer), gimp_object_get_name (set),
|
||||
g_type_name (gimp_item_list_get_item_type (set)));
|
||||
else if (gimp_item_list_is_pattern (set, NULL))
|
||||
g_printerr ("xcf: layer '%s' cannot be added to pattern item set '%s' (skipping)\n",
|
||||
gimp_object_get_name (*layer), gimp_object_get_name (set));
|
||||
else
|
||||
gimp_item_list_add (set, GIMP_ITEM (*layer));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef GIMP_UNSTABLE
|
||||
g_printerr ("unexpected/unknown layer property: %d (skipping)\n",
|
||||
|
@ -1672,6 +1797,7 @@ xcf_check_layer_props (XcfInfo *info,
|
|||
case PROP_COMPOSITE_MODE:
|
||||
case PROP_TATTOO:
|
||||
case PROP_PARASITES:
|
||||
case PROP_ITEM_SET_ITEM:
|
||||
if (! xcf_skip_unknown_prop (info, prop_size))
|
||||
return FALSE;
|
||||
/* Just ignore for now. */
|
||||
|
@ -1896,6 +2022,25 @@ xcf_load_channel_props (XcfInfo *info,
|
|||
}
|
||||
break;
|
||||
|
||||
case PROP_ITEM_SET_ITEM:
|
||||
{
|
||||
GimpItemList *set;
|
||||
guint32 n;
|
||||
|
||||
xcf_read_int32 (info, &n, 1);
|
||||
set = g_list_nth_data (info->channel_sets, n);
|
||||
if (set == NULL)
|
||||
g_printerr ("xcf: unknown channel set: %d (skipping)\n", n);
|
||||
else if (! g_type_is_a (G_TYPE_FROM_INSTANCE (*channel),
|
||||
gimp_item_list_get_item_type (set)))
|
||||
g_printerr ("xcf: channel '%s' cannot be added to item set '%s' with item type %s (skipping)\n",
|
||||
gimp_object_get_name (*channel), gimp_object_get_name (set),
|
||||
g_type_name (gimp_item_list_get_item_type (set)));
|
||||
else
|
||||
gimp_item_list_add (set, GIMP_ITEM (*channel));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef GIMP_UNSTABLE
|
||||
g_printerr ("unexpected/unknown channel property: %d (skipping)\n",
|
||||
|
|
|
@ -65,6 +65,8 @@ typedef enum
|
|||
PROP_BLEND_SPACE = 37,
|
||||
PROP_FLOAT_COLOR = 38,
|
||||
PROP_SAMPLE_POINTS = 39,
|
||||
PROP_ITEM_SET = 40,
|
||||
PROP_ITEM_SET_ITEM = 41,
|
||||
} PropType;
|
||||
|
||||
typedef enum
|
||||
|
@ -116,6 +118,9 @@ struct _XcfInfo
|
|||
GList *linked_channels;
|
||||
GList *linked_paths;
|
||||
|
||||
GList *layer_sets;
|
||||
GList *channel_sets;
|
||||
|
||||
GimpDrawable *floating_sel_drawable;
|
||||
GimpLayer *floating_sel;
|
||||
goffset floating_sel_offset;
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "core/gimpimage-private.h"
|
||||
#include "core/gimpimage-sample-points.h"
|
||||
#include "core/gimpimage-symmetry.h"
|
||||
#include "core/gimpitemlist.h"
|
||||
#include "core/gimplayer.h"
|
||||
#include "core/gimplayermask.h"
|
||||
#include "core/gimpparasitelist.h"
|
||||
|
@ -487,6 +488,14 @@ xcf_save_image_props (XcfInfo *info,
|
|||
g_list_free_full (symmetry_parasites,
|
||||
(GDestroyNotify) gimp_parasite_free);
|
||||
|
||||
info->layer_sets = gimp_image_get_stored_item_sets (image, GIMP_TYPE_LAYER);
|
||||
info->channel_sets = gimp_image_get_stored_item_sets (image, GIMP_TYPE_CHANNEL);
|
||||
|
||||
for (iter = info->layer_sets; iter; iter = iter->next)
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_ITEM_SET, error, iter->data));
|
||||
for (iter = info->channel_sets; iter; iter = iter->next)
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_ITEM_SET, error, iter->data));
|
||||
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_END, error));
|
||||
|
||||
return TRUE;
|
||||
|
@ -499,6 +508,7 @@ xcf_save_layer_props (XcfInfo *info,
|
|||
GError **error)
|
||||
{
|
||||
GimpParasiteList *parasites;
|
||||
GList *iter;
|
||||
gint offset_x;
|
||||
gint offset_y;
|
||||
|
||||
|
@ -531,8 +541,6 @@ xcf_save_layer_props (XcfInfo *info,
|
|||
gimp_layer_get_opacity (layer)));
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_VISIBLE, error,
|
||||
gimp_item_get_visible (GIMP_ITEM (layer))));
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_LINKED, error,
|
||||
gimp_item_get_linked (GIMP_ITEM (layer))));
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_COLOR_TAG, error,
|
||||
gimp_item_get_color_tag (GIMP_ITEM (layer))));
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_LOCK_CONTENT, error,
|
||||
|
@ -612,6 +620,22 @@ xcf_save_layer_props (XcfInfo *info,
|
|||
parasites));
|
||||
}
|
||||
|
||||
for (iter = info->layer_sets; iter; iter = iter->next)
|
||||
{
|
||||
GimpItemList *set = iter->data;
|
||||
|
||||
if (! gimp_item_list_is_pattern (set, NULL))
|
||||
{
|
||||
GList *items = gimp_item_list_get_items (set, NULL);
|
||||
|
||||
if (g_list_find (items, GIMP_ITEM (layer)))
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_ITEM_SET_ITEM, error,
|
||||
g_list_position (info->layer_sets, iter)));
|
||||
|
||||
g_list_free (items);
|
||||
}
|
||||
}
|
||||
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_END, error));
|
||||
|
||||
return TRUE;
|
||||
|
@ -624,6 +648,7 @@ xcf_save_channel_props (XcfInfo *info,
|
|||
GError **error)
|
||||
{
|
||||
GimpParasiteList *parasites;
|
||||
GList *iter;
|
||||
|
||||
if (g_list_find (gimp_image_get_selected_channels (image), channel))
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_ACTIVE_CHANNEL, error));
|
||||
|
@ -637,8 +662,6 @@ xcf_save_channel_props (XcfInfo *info,
|
|||
gimp_channel_get_opacity (channel)));
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_VISIBLE, error,
|
||||
gimp_item_get_visible (GIMP_ITEM (channel))));
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_LINKED, error,
|
||||
gimp_item_get_linked (GIMP_ITEM (channel))));
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_COLOR_TAG, error,
|
||||
gimp_item_get_color_tag (GIMP_ITEM (channel))));
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_LOCK_CONTENT, error,
|
||||
|
@ -662,6 +685,22 @@ xcf_save_channel_props (XcfInfo *info,
|
|||
parasites));
|
||||
}
|
||||
|
||||
for (iter = info->channel_sets; iter; iter = iter->next)
|
||||
{
|
||||
GimpItemList *set = iter->data;
|
||||
|
||||
if (! gimp_item_list_is_pattern (set, NULL))
|
||||
{
|
||||
GList *items = gimp_item_list_get_items (set, NULL);
|
||||
|
||||
if (g_list_find (items, GIMP_ITEM (channel)))
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_ITEM_SET_ITEM, error,
|
||||
g_list_position (info->channel_sets, iter)));
|
||||
|
||||
g_list_free (items);
|
||||
}
|
||||
}
|
||||
|
||||
xcf_check_error (xcf_save_prop (info, image, PROP_END, error));
|
||||
|
||||
return TRUE;
|
||||
|
@ -850,6 +889,9 @@ xcf_save_prop (XcfInfo *info,
|
|||
break;
|
||||
|
||||
case PROP_LINKED:
|
||||
/* This code should not be called any longer. */
|
||||
g_return_val_if_reached (FALSE);
|
||||
#if 0
|
||||
{
|
||||
guint32 linked = va_arg (args, guint32);
|
||||
|
||||
|
@ -860,6 +902,7 @@ xcf_save_prop (XcfInfo *info,
|
|||
|
||||
xcf_write_int32_check_error (info, &linked, 1);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case PROP_COLOR_TAG:
|
||||
|
@ -1344,6 +1387,60 @@ xcf_save_prop (XcfInfo *info,
|
|||
xcf_write_int32_check_error (info, &flags, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_ITEM_SET:
|
||||
{
|
||||
GimpItemList *set = va_arg (args, GimpItemList *);
|
||||
const gchar *string;
|
||||
guint32 method;
|
||||
guint32 item_type;
|
||||
goffset base;
|
||||
goffset pos;
|
||||
|
||||
size = 0;
|
||||
|
||||
xcf_write_prop_type_check_error (info, prop_type);
|
||||
pos = info->cp;
|
||||
xcf_write_int32_check_error (info, &size, 1);
|
||||
base = info->cp;
|
||||
|
||||
if (gimp_item_list_get_item_type (set) == GIMP_TYPE_LAYER)
|
||||
item_type = 0;
|
||||
else if (gimp_item_list_get_item_type (set) == GIMP_TYPE_CHANNEL)
|
||||
item_type = 1;
|
||||
else if (gimp_item_list_get_item_type (set) == GIMP_TYPE_VECTORS)
|
||||
item_type = 2;
|
||||
else
|
||||
g_return_val_if_reached (FALSE);
|
||||
xcf_write_int32_check_error (info, &item_type, 1);
|
||||
|
||||
if (! gimp_item_list_is_pattern (set, &method))
|
||||
method = G_MAXUINT32;
|
||||
xcf_write_int32_check_error (info, &method, 1);
|
||||
|
||||
string = gimp_object_get_name (set);
|
||||
xcf_write_string_check_error (info, (gchar **) &string, 1);
|
||||
|
||||
/* go back to the saved position and write the length */
|
||||
size = info->cp - base;
|
||||
xcf_check_error (xcf_seek_pos (info, pos, error));
|
||||
xcf_write_int32_check_error (info, &size, 1);
|
||||
|
||||
xcf_check_error (xcf_seek_pos (info, base + size, error));
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_ITEM_SET_ITEM:
|
||||
{
|
||||
guint32 set_n = va_arg (args, guint32);
|
||||
|
||||
size = 4;
|
||||
|
||||
xcf_write_prop_type_check_error (info, prop_type);
|
||||
xcf_write_int32_check_error (info, &size, 1);
|
||||
xcf_write_int32_check_error (info, &set_n, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
@ -2074,7 +2171,8 @@ xcf_save_old_paths (XcfInfo *info,
|
|||
* around to fix that cruft */
|
||||
|
||||
name = (gchar *) gimp_object_get_name (vectors);
|
||||
locked = gimp_item_get_linked (GIMP_ITEM (vectors));
|
||||
/* The 'linked' concept does not exist anymore in GIMP 3.0 and over. */
|
||||
locked = 0;
|
||||
state = closed ? 4 : 2; /* EDIT : ADD (editing state, 1.2 compat) */
|
||||
version = 3;
|
||||
pathtype = 1; /* BEZIER (1.2 compat) */
|
||||
|
@ -2175,7 +2273,8 @@ xcf_save_vectors (XcfInfo *info,
|
|||
|
||||
name = gimp_object_get_name (vectors);
|
||||
visible = gimp_item_get_visible (GIMP_ITEM (vectors));
|
||||
linked = gimp_item_get_linked (GIMP_ITEM (vectors));
|
||||
/* The 'linked' concept does not exist anymore in GIMP 3.0 and over. */
|
||||
linked = 0;
|
||||
tattoo = gimp_item_get_tattoo (GIMP_ITEM (vectors));
|
||||
parasites = gimp_item_get_parasites (GIMP_ITEM (vectors));
|
||||
num_parasites = gimp_parasite_list_persistent_length (parasites);
|
||||
|
|
|
@ -2513,7 +2513,6 @@ gimp_item_parasite_list
|
|||
gimp_item_get_visible
|
||||
gimp_item_set_visible
|
||||
gimp_item_is_visible
|
||||
gimp_item_get_linked
|
||||
gimp_item_get_lock_content
|
||||
gimp_item_set_lock_content
|
||||
gimp_item_can_lock_content
|
||||
|
|
|
@ -185,6 +185,15 @@ Since GIMP 3.0.0, released on TODO.
|
|||
PROP_GUIDES now allows off-canvas guide positions, i.e. negative
|
||||
positions and over canvas-dimensions positions.
|
||||
|
||||
Version 16:
|
||||
Since GIMP 3.0.0, released on TODO.
|
||||
- Allows multiple channels to have the property PROP_ACTIVE_CHANNEL,
|
||||
hence multiple channels selected at once.
|
||||
- PROP_LINKED is deprecated. Old XCF files loaded by newer GIMP will
|
||||
transform linked items into stored item sets PROP_ITEM_SET.
|
||||
- New PROP_ITEM_SET and PROP_ITEM_SET_ITEM to store sets of layers,
|
||||
channels or paths.
|
||||
|
||||
1. BASIC CONCEPTS
|
||||
=================
|
||||
|
||||
|
@ -641,6 +650,10 @@ PROP_LINKED (editing state)
|
|||
all other linked elements will be transformed the same way.
|
||||
It appears in the property list for layers, channels and paths.
|
||||
|
||||
PROP_LINKED property is deprecated and must not be used since XCF
|
||||
version 16. XCF readers and writers are expected to convert linked
|
||||
items into item sets instead (see PROP_ITEM_SET).
|
||||
|
||||
PROP_LOCK_CONTENT (since version 3, editing state)
|
||||
uint32 28 Type identification
|
||||
uint32 4 Four bytes of payload
|
||||
|
@ -713,6 +726,19 @@ PROP_VISIBLE (essential)
|
|||
When reading old XCF files that lack this property, assume that
|
||||
layers are visible and channels are not.
|
||||
|
||||
PROP_ITEM_SET_ITEM (since GIMP 3.0)
|
||||
uint32 41 Type identification
|
||||
uint32 4 Four bytes of payload
|
||||
uint32 set The PROP_ITEM_SET this item is listed in.
|
||||
|
||||
PROP_ITEM_SET_ITEM can be assigned to layers, channels and paths. They are
|
||||
only organisational properties and have no consequence on render.
|
||||
|
||||
The 'set' attribute corresponds to the numbered PROP_ITEM_SET this
|
||||
item belongs to, considering that the appearance order of
|
||||
PROP_ITEM_SET properties matter. It can only belong to a named item
|
||||
set and all items in a set must be of the proper type.
|
||||
|
||||
|
||||
3. THE IMAGE STRUCTURE
|
||||
======================
|
||||
|
@ -921,7 +947,7 @@ PROP_PATHS
|
|||
|
||||
Note: the attribute 'linked' was formerly erroneously called 'locked'
|
||||
(but meant 'linked' anyway).
|
||||
|
||||
|
||||
A closed path is a path which has the last and the first point connected,
|
||||
for instance a triangle.
|
||||
|
||||
|
@ -956,7 +982,7 @@ PROP_RESOLUTION (not editing state, but not _really_ essential either)
|
|||
resolution.
|
||||
|
||||
PROP_SAMPLE_POINTS
|
||||
uint32 17 Type identification
|
||||
uint32 39 Type identification
|
||||
uint32 plength Total length of the following payload in bytes
|
||||
,---------------- Repeat for each sample point:
|
||||
| uint32 x X coordinate
|
||||
|
@ -1040,6 +1066,24 @@ PROP_VECTORS
|
|||
without parsing the individual parasites. (Note that this is _not_
|
||||
the case for PROP_PATHS).
|
||||
|
||||
PROP_ITEM_SET (since GIMP 3.0)
|
||||
uint32 40 Type identification
|
||||
uint32 plength Total length of the following payload in bytes
|
||||
uint32 item_type The type of item in this set:
|
||||
0: layers
|
||||
1: channels
|
||||
2: paths
|
||||
uint32 method Selection method:
|
||||
0: basic text search
|
||||
1: regular expression search
|
||||
2: glob pattern search
|
||||
0xffffffff (max uint32): named item set
|
||||
string label Pattern to use for selection or name of the item
|
||||
set if method is 0xffffffff.
|
||||
|
||||
They are only organisational properties and have no consequence on
|
||||
render. The order matters for display and also for PROP_ITEM_SET_ITEM.
|
||||
|
||||
|
||||
4. THE CHANNEL STRUCTURE
|
||||
========================
|
||||
|
|
Loading…
Reference in New Issue