[gimp/gimp-attributes-wip: 1/2] introducing gimp-attributes Instead of gexiv2-metadata, two new classes are introduced, GimpAttribut
- From: Hartmut Kuhse <hartmutkuhse src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gimp-attributes-wip: 1/2] introducing gimp-attributes Instead of gexiv2-metadata, two new classes are introduced, GimpAttribut
- Date: Tue, 30 Sep 2014 18:57:52 +0000 (UTC)
commit 6c0f0ad5c3f42d84a90057a2fbdc71b8805ebcbd
Author: Hartmut Kuhse <hk_priv gmx de>
Date: Tue Sep 30 20:50:44 2014 +0200
introducing gimp-attributes
Instead of gexiv2-metadata, two new classes are introduced, GimpAttribute
and GimpAttributes, which stores GimpAttribute objects.
The aim is more internal flexibility for metadata/attributes.
GimpMetadata still exist as an interface between GExiv2, reading the metadata
from an image, and GimpAttribute, storing the metadata and more.
GimpAttributes are stored for GimpImage (as the older metadata object)
and GimpItem (GimpLayer / GimpChannel)
A new dialog for attributes as a plug-in for images(<Image>/Image/Image Attributes),
layers (<Layers>/Layer Attributes) and channels (<Channels>/Channel Attributes).
A dialog for editing IPTC Attributes for images, layers and channel (at the same places).
This dialog is older and will not be enhanced.
A dialog for editing image copyright and description whats o ever.
As IPTC is (or will be) dead and xmp shall be the common standard, with the relevant
IPTC tags mapped in xmp, i decided to take the tags from Adobe Lightroom 's "IPTC Extension"
tags for dealing with digital rights (<Image>/Image/Digital Rights).
I am very proud of this dialog, it was very hard work and is still in progress.
app/core/Makefile.am | 2 +
app/core/core-enums.c | 8 +-
app/core/core-enums.h | 4 +-
app/core/core-types.h | 1 +
app/core/gimpchannel.c | 14 +
app/core/gimpimage-duplicate.c | 20 +-
app/core/gimpimage-merge.c | 23 +-
app/core/gimpimage-metadata.c | 163 +-
app/core/gimpimage-metadata.h | 8 +-
app/core/gimpimage-undo-push.c | 21 +-
app/core/gimpimage-undo-push.h | 6 +-
app/core/gimpimage.c | 70 +-
app/core/gimpimageundo.c | 30 +-
app/core/gimpimageundo.h | 2 +-
app/core/gimpitem-metadata.c | 73 +
app/core/gimpitem-metadata.h | 28 +
app/core/gimpitemundo.c | 74 +-
app/core/gimpitemundo.h | 2 +
app/core/gimplayer.c | 12 +-
app/core/gimpviewable.c | 46 +-
app/core/gimpviewable.h | 9 +-
app/core/gimpviewableundo.c | 175 ++
app/core/gimpviewableundo.h | 51 +
app/file/file-open.c | 2 +-
app/pdb/image-cmds.c | 118 +-
app/pdb/internal-procs.c | 2 +-
app/pdb/item-cmds.c | 122 ++
app/xcf/xcf-load.c | 164 +-
app/xcf/xcf-save.c | 70 +-
libgimp/Makefile.am | 3 +
libgimp/gimp.def | 6 +-
libgimp/gimp.h | 1 +
libgimp/gimpimage.c | 52 +-
libgimp/gimpimage.h | 5 +
libgimp/gimpimage_pdb.c | 48 +-
libgimp/gimpimage_pdb.h | 6 +-
libgimp/gimpitem.c | 86 +
libgimp/gimpitem.h | 40 +
libgimp/gimpitem_pdb.c | 62 +
libgimp/gimpitem_pdb.h | 83 +-
libgimp/gimpmetadata.c | 368 ++--
libgimp/gimpmetadata.h | 5 +-
libgimp/gimpviewable.c | 84 +
libgimp/gimpviewable_pdb.c | 100 +
libgimpbase/Makefile.am | 12 +
libgimpbase/gimpattribute.c | 1858 ++++++++++++++++
libgimpbase/gimpattribute.h | 95 +
libgimpbase/gimpattributes-image.c | 282 +++
libgimpbase/gimpattributes-image.h | 47 +
libgimpbase/gimpattributes.c | 1657 ++++++++++++++
libgimpbase/gimpattributes.h | 74 +
libgimpbase/gimpbase.def | 50 +-
libgimpbase/gimpbase.h | 3 +
libgimpbase/gimpbasetypes.h | 2 +
libgimpbase/gimpmetadata.c | 501 +-----
libgimpbase/gimpmetadata.h | 21 -
libgimpbase/gimprational.c | 301 +++
libgimpbase/gimprational.h | 68 +
plug-ins/Makefile.am | 1 +
plug-ins/common/Makefile.am | 61 +-
plug-ins/common/attributes.c | 876 ++++++++
plug-ins/common/file-jp2-load.c | 8 +-
plug-ins/common/file-png.c | 38 +-
plug-ins/common/file-tiff-load.c | 8 +-
plug-ins/common/file-tiff-save.c | 24 +-
plug-ins/common/gimprc.common | 3 +-
plug-ins/common/iptc.c | 513 +++++
plug-ins/file-jpeg/jpeg-load.c | 13 +-
plug-ins/file-jpeg/jpeg-load.h | 1 +
plug-ins/file-jpeg/jpeg-save.c | 3 +-
plug-ins/file-jpeg/jpeg.c | 22 +-
plug-ins/file-psd/psd-save.c | 15 +-
plug-ins/file-psd/psd.c | 2 +-
plug-ins/metainfo/Makefile.am | 61 +
plug-ins/metainfo/interface.c | 236 ++
plug-ins/metainfo/interface.h | 49 +
plug-ins/metainfo/metainfo-helper.c | 272 +++
plug-ins/metainfo/metainfo-helper.h | 102 +
plug-ins/metainfo/metainfo.c | 156 ++
plug-ins/metainfo/metainfo.h | 29 +
plug-ins/metainfo/page-administration.c | 1039 +++++++++
plug-ins/metainfo/page-administration.h | 33 +
plug-ins/metainfo/page-artwork.c | 1067 +++++++++
plug-ins/metainfo/page-artwork.h | 33 +
plug-ins/metainfo/page-description.c | 927 ++++++++
plug-ins/metainfo/page-description.h | 33 +
plug-ins/metainfo/page-rights.c | 1117 ++++++++++
plug-ins/metainfo/page-rights.h | 33 +
plug-ins/ui/Makefile.am | 5 +-
plug-ins/ui/plug-in-attributes.ui | 346 +++
plug-ins/ui/plug-in-iptc.ui | 779 +++++++
plug-ins/ui/plug-in-metadata.ui | 908 --------
plug-ins/ui/plug-in-metainfo.ui | 3556 +++++++++++++++++++++++++++++++
po-plug-ins/POTFILES.in | 10 +-
tools/pdbgen/pdb/image.pdb | 40 +-
tools/pdbgen/pdb/item.pdb | 78 +-
tools/pdbgen/pdb/viewable.pdb | 109 +
97 files changed, 17744 insertions(+), 2102 deletions(-)
---
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index e5b44d8..0d105bf 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -266,6 +266,8 @@ libappcore_a_sources = \
gimpitem-exclusive.h \
gimpitem-linked.c \
gimpitem-linked.h \
+ gimpitem-metadata.c \
+ gimpitem-metadata.h \
gimpitem-preview.c \
gimpitem-preview.h \
gimpitempropundo.c \
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index c6dcc62..5d97822 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -866,13 +866,14 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_IMAGE_SIZE, "GIMP_UNDO_IMAGE_SIZE", "image-size" },
{ GIMP_UNDO_IMAGE_RESOLUTION, "GIMP_UNDO_IMAGE_RESOLUTION", "image-resolution" },
{ GIMP_UNDO_IMAGE_GRID, "GIMP_UNDO_IMAGE_GRID", "image-grid" },
- { GIMP_UNDO_IMAGE_METADATA, "GIMP_UNDO_IMAGE_METADATA", "image-metadata" },
{ GIMP_UNDO_IMAGE_COLORMAP, "GIMP_UNDO_IMAGE_COLORMAP", "image-colormap" },
+ { GIMP_UNDO_IMAGE_ATTRIBUTES, "GIMP_UNDO_IMAGE_ATTRIBUTES", "image-attributes" },
{ GIMP_UNDO_GUIDE, "GIMP_UNDO_GUIDE", "guide" },
{ GIMP_UNDO_SAMPLE_POINT, "GIMP_UNDO_SAMPLE_POINT", "sample-point" },
{ GIMP_UNDO_DRAWABLE, "GIMP_UNDO_DRAWABLE", "drawable" },
{ GIMP_UNDO_DRAWABLE_MOD, "GIMP_UNDO_DRAWABLE_MOD", "drawable-mod" },
{ GIMP_UNDO_MASK, "GIMP_UNDO_MASK", "mask" },
+ { GIMP_UNDO_ITEM_ATTRIBUTES, "GIMP_UNDO_ITEM_ATTRIBUTES", "item-attributes" },
{ GIMP_UNDO_ITEM_REORDER, "GIMP_UNDO_ITEM_REORDER", "item-reorder" },
{ GIMP_UNDO_ITEM_RENAME, "GIMP_UNDO_ITEM_RENAME", "item-rename" },
{ GIMP_UNDO_ITEM_DISPLACE, "GIMP_UNDO_ITEM_DISPLACE", "item-displace" },
@@ -908,6 +909,7 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_FOREGROUND_SELECT, "GIMP_UNDO_FOREGROUND_SELECT", "foreground-select" },
{ GIMP_UNDO_PARASITE_ATTACH, "GIMP_UNDO_PARASITE_ATTACH", "parasite-attach" },
{ GIMP_UNDO_PARASITE_REMOVE, "GIMP_UNDO_PARASITE_REMOVE", "parasite-remove" },
+ { GIMP_UNDO_VIEWABLE_ATTRIBUTES, "GIMP_UNDO_VIEWABLE_ATTRIBUTES", "viewable-attributes" },
{ GIMP_UNDO_CANT, "GIMP_UNDO_CANT", "cant" },
{ 0, NULL, NULL }
};
@@ -957,13 +959,14 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_IMAGE_SIZE, NC_("undo-type", "Image size"), NULL },
{ GIMP_UNDO_IMAGE_RESOLUTION, NC_("undo-type", "Image resolution change"), NULL },
{ GIMP_UNDO_IMAGE_GRID, NC_("undo-type", "Grid"), NULL },
- { GIMP_UNDO_IMAGE_METADATA, NC_("undo-type", "Change metadata"), NULL },
{ GIMP_UNDO_IMAGE_COLORMAP, NC_("undo-type", "Change indexed palette"), NULL },
+ { GIMP_UNDO_IMAGE_ATTRIBUTES, NC_("undo-type", "Changed attributes"), NULL },
{ GIMP_UNDO_GUIDE, NC_("undo-type", "Guide"), NULL },
{ GIMP_UNDO_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL },
{ GIMP_UNDO_DRAWABLE, NC_("undo-type", "Layer/Channel"), NULL },
{ GIMP_UNDO_DRAWABLE_MOD, NC_("undo-type", "Layer/Channel modification"), NULL },
{ GIMP_UNDO_MASK, NC_("undo-type", "Selection mask"), NULL },
+ { GIMP_UNDO_ITEM_ATTRIBUTES, NC_("undo-type", "Changed attributes"), NULL },
{ GIMP_UNDO_ITEM_REORDER, NC_("undo-type", "Reorder item"), NULL },
{ GIMP_UNDO_ITEM_RENAME, NC_("undo-type", "Rename item"), NULL },
{ GIMP_UNDO_ITEM_DISPLACE, NC_("undo-type", "Move item"), NULL },
@@ -999,6 +1002,7 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_FOREGROUND_SELECT, NC_("undo-type", "Select foreground"), NULL },
{ GIMP_UNDO_PARASITE_ATTACH, NC_("undo-type", "Attach parasite"), NULL },
{ GIMP_UNDO_PARASITE_REMOVE, NC_("undo-type", "Remove parasite"), NULL },
+ { GIMP_UNDO_VIEWABLE_ATTRIBUTES, "GIMP_UNDO_VIEWABLE_ATTRIBUTES", NULL },
{ GIMP_UNDO_CANT, NC_("undo-type", "Not undoable"), NULL },
{ 0, NULL, NULL }
};
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index 81e20f8..9f75c22 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -426,13 +426,14 @@ typedef enum /*< pdb-skip >*/
GIMP_UNDO_IMAGE_SIZE, /*< desc="Image size" >*/
GIMP_UNDO_IMAGE_RESOLUTION, /*< desc="Image resolution change" >*/
GIMP_UNDO_IMAGE_GRID, /*< desc="Grid" >*/
- GIMP_UNDO_IMAGE_METADATA, /*< desc="Change metadata" >*/
GIMP_UNDO_IMAGE_COLORMAP, /*< desc="Change indexed palette" >*/
+ GIMP_UNDO_IMAGE_ATTRIBUTES, /*< desc="Change attributes >*/
GIMP_UNDO_GUIDE, /*< desc="Guide" >*/
GIMP_UNDO_SAMPLE_POINT, /*< desc="Sample Point" >*/
GIMP_UNDO_DRAWABLE, /*< desc="Layer/Channel" >*/
GIMP_UNDO_DRAWABLE_MOD, /*< desc="Layer/Channel modification" >*/
GIMP_UNDO_MASK, /*< desc="Selection mask" >*/
+ GIMP_UNDO_ITEM_ATTRIBUTES, /*< desc="Change attributes >*/
GIMP_UNDO_ITEM_REORDER, /*< desc="Reorder item" >*/
GIMP_UNDO_ITEM_RENAME, /*< desc="Rename item" >*/
GIMP_UNDO_ITEM_DISPLACE, /*< desc="Move item" >*/
@@ -468,6 +469,7 @@ typedef enum /*< pdb-skip >*/
GIMP_UNDO_FOREGROUND_SELECT, /*< desc="Select foreground" >*/
GIMP_UNDO_PARASITE_ATTACH, /*< desc="Attach parasite" >*/
GIMP_UNDO_PARASITE_REMOVE, /*< desc="Remove parasite" >*/
+ GIMP_UNDO_VIEWABLE_ATTRIBUTES, /*< desc="Change attributes >*/
GIMP_UNDO_CANT /*< desc="Not undoable" >*/
} GimpUndoType;
diff --git a/app/core/core-types.h b/app/core/core-types.h
index d1c077b..5634349 100644
--- a/app/core/core-types.h
+++ b/app/core/core-types.h
@@ -166,6 +166,7 @@ typedef struct _GimpSamplePointUndo GimpSamplePointUndo;
typedef struct _GimpFloatingSelUndo GimpFloatingSelUndo;
typedef struct _GimpUndoStack GimpUndoStack;
typedef struct _GimpUndoAccumulator GimpUndoAccumulator;
+typedef struct _GimpViewableUndo GimpViewableUndo;
/* misc objects */
diff --git a/app/core/gimpchannel.c b/app/core/gimpchannel.c
index 0e70915..7e239dc 100644
--- a/app/core/gimpchannel.c
+++ b/app/core/gimpchannel.c
@@ -23,6 +23,7 @@
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
+#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpcolor/gimpcolor.h"
@@ -44,6 +45,7 @@
#include "gimpimage-quick-mask.h"
#include "gimpimage-undo.h"
#include "gimpimage-undo-push.h"
+#include "gimpitem-metadata.h"
#include "gimpchannel.h"
#include "gimpchannel-select.h"
#include "gimpcontext.h"
@@ -493,6 +495,9 @@ gimp_channel_duplicate (GimpItem *item,
if (GIMP_IS_CHANNEL (new_item))
{
+ GimpAttributes *attributes;
+ GimpAttributes *new_attributes;
+
GimpChannel *channel = GIMP_CHANNEL (item);
GimpChannel *new_channel = GIMP_CHANNEL (new_item);
@@ -506,6 +511,15 @@ gimp_channel_duplicate (GimpItem *item,
new_channel->y1 = channel->y1;
new_channel->x2 = channel->x2;
new_channel->y2 = channel->y2;
+
+ attributes = gimp_item_get_attributes (item);
+ if (attributes)
+ {
+ new_attributes = gimp_attributes_duplicate (attributes);
+ gimp_item_set_attributes (new_item, new_attributes, FALSE);
+ g_object_unref (new_attributes);
+ }
+
}
return new_item;
diff --git a/app/core/gimpimage-duplicate.c b/app/core/gimpimage-duplicate.c
index da4469b..5a48e5c 100644
--- a/app/core/gimpimage-duplicate.c
+++ b/app/core/gimpimage-duplicate.c
@@ -72,7 +72,7 @@ static void gimp_image_duplicate_sample_points (GimpImage *image,
GimpImage *new_image);
static void gimp_image_duplicate_grid (GimpImage *image,
GimpImage *new_image);
-static void gimp_image_duplicate_metadata (GimpImage *image,
+static void gimp_image_duplicate_attributes (GimpImage *image,
GimpImage *new_image);
static void gimp_image_duplicate_quick_mask (GimpImage *image,
GimpImage *new_image);
@@ -147,8 +147,8 @@ gimp_image_duplicate (GimpImage *image)
/* Copy the grid */
gimp_image_duplicate_grid (image, new_image);
- /* Copy the metadata */
- gimp_image_duplicate_metadata (image, new_image);
+ /* Copy the attributes */
+ gimp_image_duplicate_attributes (image, new_image);
/* Copy the quick mask info */
gimp_image_duplicate_quick_mask (image, new_image);
@@ -474,16 +474,16 @@ gimp_image_duplicate_grid (GimpImage *image,
}
static void
-gimp_image_duplicate_metadata (GimpImage *image,
- GimpImage *new_image)
+gimp_image_duplicate_attributes (GimpImage *image,
+ GimpImage *new_image)
{
- GimpMetadata *metadata = gimp_image_get_metadata (image);
+ GimpAttributes *attributes = gimp_image_get_attributes (image);
- if (metadata)
+ if (attributes)
{
- metadata = gimp_metadata_duplicate (metadata);
- gimp_image_set_metadata (new_image, metadata, FALSE);
- g_object_unref (metadata);
+ attributes = gimp_attributes_duplicate (attributes);
+ gimp_image_set_attributes (new_image, attributes, FALSE);
+ g_object_unref (attributes);
}
}
diff --git a/app/core/gimpimage-merge.c b/app/core/gimpimage-merge.c
index aec9c75..a779966 100644
--- a/app/core/gimpimage-merge.c
+++ b/app/core/gimpimage-merge.c
@@ -40,8 +40,10 @@
#include "gimpgrouplayer.h"
#include "gimpimage.h"
#include "gimpimage-merge.h"
+#include "gimpimage-metadata.h"
#include "gimpimage-undo.h"
#include "gimpitemstack.h"
+#include "gimpitem-metadata.h"
#include "gimplayer-floating-sel.h"
#include "gimplayermask.h"
#include "gimpmarshal.h"
@@ -426,6 +428,7 @@ gimp_image_merge_layers (GimpImage *image,
GimpLayer *layer;
GimpLayer *bottom_layer;
GimpParasiteList *parasites;
+ GimpAttributes *attributes;
gint count;
gint x1, y1, x2, y2;
gint off_x, off_y;
@@ -520,8 +523,8 @@ gimp_image_merge_layers (GimpImage *image,
(gimp_drawable_is_indexed (GIMP_DRAWABLE (layer)) &&
! gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))))
{
- GeglColor *color;
- GimpRGB bg;
+ GeglColor *color;
+ GimpRGB bg;
merge_layer = gimp_layer_new (image, (x2 - x1), (y2 - y1),
gimp_image_get_layer_format (image, FALSE),
@@ -581,6 +584,22 @@ gimp_image_merge_layers (GimpImage *image,
bottom_layer = layer;
+ /* Copy the attributes of the bottom layer to the new layer */
+
+ attributes = gimp_item_get_attributes (GIMP_ITEM (bottom_layer));
+
+ if (!attributes)
+ attributes = gimp_image_get_attributes (image);
+
+ if (attributes)
+ {
+ GimpAttributes *new_attributes;
+
+ new_attributes = gimp_attributes_duplicate (attributes);
+ gimp_item_set_attributes (GIMP_ITEM (merge_layer), new_attributes, TRUE);
+ g_object_unref (new_attributes);
+ }
+
/* Copy the tattoo and parasites of the bottom layer to the new layer */
gimp_item_set_tattoo (GIMP_ITEM (merge_layer),
gimp_item_get_tattoo (GIMP_ITEM (bottom_layer)));
diff --git a/app/core/gimpimage-metadata.c b/app/core/gimpimage-metadata.c
index 459dac1..c244952 100644
--- a/app/core/gimpimage-metadata.c
+++ b/app/core/gimpimage-metadata.c
@@ -33,75 +33,136 @@
/* public functions */
-GimpMetadata *
-gimp_image_get_metadata (GimpImage *image)
-{
- GimpImagePrivate *private;
-
- g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
-
- private = GIMP_IMAGE_GET_PRIVATE (image);
-
- return private->metadata;
-}
+//void
+//gimp_image_set_metadata (GimpImage *image,
+// GimpMetadata *metadata,
+// gboolean push_undo)
+//{
+// GimpImagePrivate *private;
+//
+// g_return_if_fail (GIMP_IS_IMAGE (image));
+//
+// private = GIMP_IMAGE_GET_PRIVATE (image);
+//
+// if (metadata != private->metadata)
+// {
+// if (push_undo)
+// gimp_image_undo_push_image_metadata (image, NULL);
+//
+// if (private->metadata)
+// g_object_unref (private->metadata);
+//
+// private->metadata = metadata;
+//
+// if (private->metadata)
+// {
+// gdouble xres, yres;
+//
+// g_object_ref (private->metadata);
+//
+// gimp_metadata_set_pixel_size (metadata,
+// gimp_image_get_width (image),
+// gimp_image_get_height (image));
+//
+// switch (gimp_image_get_component_type (image))
+// {
+// case GIMP_COMPONENT_TYPE_U8:
+// gimp_metadata_set_bits_per_sample (metadata, 8);
+// break;
+//
+// case GIMP_COMPONENT_TYPE_U16:
+// case GIMP_COMPONENT_TYPE_HALF:
+// gimp_metadata_set_bits_per_sample (metadata, 16);
+// break;
+//
+// case GIMP_COMPONENT_TYPE_U32:
+// case GIMP_COMPONENT_TYPE_FLOAT:
+// gimp_metadata_set_bits_per_sample (metadata, 32);
+// break;
+//
+// case GIMP_COMPONENT_TYPE_DOUBLE:
+// gimp_metadata_set_bits_per_sample (metadata, 64);
+// break;
+// }
+//
+// gimp_image_get_resolution (image, &xres, &yres);
+// gimp_metadata_set_resolution (metadata, xres, yres,
+// gimp_image_get_unit (image));
+// }
+//
+// g_object_notify (G_OBJECT (image), "metadata");
+// }
+//}
void
-gimp_image_set_metadata (GimpImage *image,
- GimpMetadata *metadata,
- gboolean push_undo)
+gimp_image_set_attributes (GimpImage *image,
+ GimpAttributes *attributes,
+ gboolean push_undo)
{
- GimpImagePrivate *private;
+ GimpViewable *viewable;
+ gdouble xres, yres;
g_return_if_fail (GIMP_IS_IMAGE (image));
- private = GIMP_IMAGE_GET_PRIVATE (image);
+ if (! attributes)
+ return;
+
+ if (push_undo)
+ gimp_image_undo_push_image_attributes (image, NULL);
- if (metadata != private->metadata)
+ viewable = GIMP_VIEWABLE (image);
+
+ if (viewable)
{
- if (push_undo)
- gimp_image_undo_push_image_metadata (image, NULL);
- if (private->metadata)
- g_object_unref (private->metadata);
+ gimp_attributes_set_pixel_size (attributes,
+ gimp_image_get_width (image),
+ gimp_image_get_height (image));
- private->metadata = metadata;
+ switch (gimp_image_get_component_type (image))
+ {
+ case GIMP_COMPONENT_TYPE_U8:
+ gimp_attributes_set_bits_per_sample (attributes, 8);
+ break;
- if (private->metadata)
- {
- gdouble xres, yres;
+ case GIMP_COMPONENT_TYPE_U16:
+ case GIMP_COMPONENT_TYPE_HALF:
+ gimp_attributes_set_bits_per_sample (attributes, 16);
+ break;
- g_object_ref (private->metadata);
+ case GIMP_COMPONENT_TYPE_U32:
+ case GIMP_COMPONENT_TYPE_FLOAT:
+ gimp_attributes_set_bits_per_sample (attributes, 32);
+ break;
- gimp_metadata_set_pixel_size (metadata,
- gimp_image_get_width (image),
- gimp_image_get_height (image));
+ case GIMP_COMPONENT_TYPE_DOUBLE:
+ gimp_attributes_set_bits_per_sample (attributes, 64);
+ break;
+ }
- switch (gimp_image_get_component_type (image))
- {
- case GIMP_COMPONENT_TYPE_U8:
- gimp_metadata_set_bits_per_sample (metadata, 8);
- break;
+ gimp_image_get_resolution (image, &xres, &yres);
+ gimp_attributes_set_resolution (attributes, xres, yres,
+ gimp_image_get_unit (image));
- case GIMP_COMPONENT_TYPE_U16:
- case GIMP_COMPONENT_TYPE_HALF:
- gimp_metadata_set_bits_per_sample (metadata, 16);
- break;
+ gimp_viewable_set_attributes (viewable, attributes);
+ }
+}
- case GIMP_COMPONENT_TYPE_U32:
- case GIMP_COMPONENT_TYPE_FLOAT:
- gimp_metadata_set_bits_per_sample (metadata, 32);
- break;
+GimpAttributes *
+gimp_image_get_attributes (GimpImage *image)
+{
+ GimpViewable *viewable;
+ GimpAttributes *attributes;
- case GIMP_COMPONENT_TYPE_DOUBLE:
- gimp_metadata_set_bits_per_sample (metadata, 64);
- break;
- }
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
- gimp_image_get_resolution (image, &xres, &yres);
- gimp_metadata_set_resolution (metadata, xres, yres,
- gimp_image_get_unit (image));
- }
+ viewable = GIMP_VIEWABLE (image);
- g_object_notify (G_OBJECT (image), "metadata");
+ if (viewable)
+ {
+ attributes = gimp_viewable_get_attributes (viewable);
+ return attributes;
}
+
+ return NULL;
}
diff --git a/app/core/gimpimage-metadata.h b/app/core/gimpimage-metadata.h
index 1f90d34..77bb083 100644
--- a/app/core/gimpimage-metadata.h
+++ b/app/core/gimpimage-metadata.h
@@ -19,10 +19,10 @@
#define __GIMP_IMAGE_METADATA_H__
-GimpMetadata * gimp_image_get_metadata (GimpImage *image);
-void gimp_image_set_metadata (GimpImage *image,
- GimpMetadata *metadata,
- gboolean push_undo);
+void gimp_image_set_attributes (GimpImage *image,
+ GimpAttributes *attributes,
+ gboolean push_undo);
+GimpAttributes * gimp_image_get_attributes (GimpImage *image);
#endif /* __GIMP_IMAGE_METADATA_H__ */
diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c
index 066f35a..26f7042 100644
--- a/app/core/gimpimage-undo-push.c
+++ b/app/core/gimpimage-undo-push.c
@@ -49,6 +49,8 @@
#include "gimpsamplepoint.h"
#include "gimpsamplepointundo.h"
#include "gimpselection.h"
+#include "gimpviewable.h"
+#include "gimpviewableundo.h"
#include "text/gimptextlayer.h"
#include "text/gimptextundo.h"
@@ -149,17 +151,32 @@ gimp_image_undo_push_image_colormap (GimpImage *image,
}
GimpUndo *
-gimp_image_undo_push_image_metadata (GimpImage *image,
+gimp_image_undo_push_image_attributes (GimpImage *image,
const gchar *undo_desc)
{
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
return gimp_image_undo_push (image, GIMP_TYPE_IMAGE_UNDO,
- GIMP_UNDO_IMAGE_METADATA, undo_desc,
+ GIMP_UNDO_IMAGE_ATTRIBUTES, undo_desc,
GIMP_DIRTY_IMAGE_META,
NULL);
}
+
+GimpUndo *
+gimp_image_undo_push_item_attributes (GimpImage *image,
+ const gchar *undo_desc,
+ GimpItem *item)
+{
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+
+ return gimp_image_undo_push (image, GIMP_TYPE_ITEM_UNDO,
+ GIMP_UNDO_ITEM_ATTRIBUTES, undo_desc,
+ GIMP_DIRTY_IMAGE_META,
+ "item", item,
+ NULL);
+}
+
GimpUndo *
gimp_image_undo_push_image_parasite (GimpImage *image,
const gchar *undo_desc,
diff --git a/app/core/gimpimage-undo-push.h b/app/core/gimpimage-undo-push.h
index 28d463d..390ba73 100644
--- a/app/core/gimpimage-undo-push.h
+++ b/app/core/gimpimage-undo-push.h
@@ -38,7 +38,7 @@ GimpUndo * gimp_image_undo_push_image_grid (GimpImage *image,
GimpGrid *grid);
GimpUndo * gimp_image_undo_push_image_colormap (GimpImage *image,
const gchar *undo_desc);
-GimpUndo * gimp_image_undo_push_image_metadata (GimpImage *image,
+GimpUndo * gimp_image_undo_push_image_attributes (GimpImage *image,
const gchar *undo_desc);
GimpUndo * gimp_image_undo_push_image_parasite (GimpImage *image,
const gchar *undo_desc,
@@ -113,6 +113,9 @@ GimpUndo * gimp_image_undo_push_item_parasite_remove(GimpImage *image,
const gchar *undo_desc,
GimpItem *item,
const gchar *name);
+GimpUndo * gimp_image_undo_push_item_attributes (GimpImage *image,
+ const gchar *undo_desc,
+ GimpItem *item);
/* layer undos */
@@ -229,5 +232,4 @@ GimpUndo * gimp_image_undo_push_fs_to_layer (GimpImage *image,
GimpUndo * gimp_image_undo_push_cantundo (GimpImage *image,
const gchar *undo_desc);
-
#endif /* __GIMP_IMAGE_UNDO_PUSH_H__ */
diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c
index 3a45bb0..bf6a937 100644
--- a/app/core/gimpimage.c
+++ b/app/core/gimpimage.c
@@ -848,7 +848,6 @@ gimp_image_set_property (GObject *object,
case PROP_PRECISION:
private->precision = g_value_get_enum (value);
break;
- case PROP_METADATA:
case PROP_BUFFER:
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -885,9 +884,6 @@ gimp_image_get_property (GObject *object,
case PROP_PRECISION:
g_value_set_enum (value, private->precision);
break;
- case PROP_METADATA:
- g_value_set_object (value, gimp_image_get_metadata (image));
- break;
case PROP_BUFFER:
g_value_set_object (value, gimp_image_get_buffer (GIMP_PICKABLE (image)));
break;
@@ -1191,10 +1187,10 @@ gimp_image_get_size (GimpViewable *viewable,
static void
gimp_image_size_changed (GimpViewable *viewable)
{
- GimpImage *image = GIMP_IMAGE (viewable);
- GimpMetadata *metadata;
- GList *all_items;
- GList *list;
+ GimpImage *image = GIMP_IMAGE (viewable);
+ GimpAttributes *attributes;
+ GList *all_items;
+ GList *list;
if (GIMP_VIEWABLE_CLASS (parent_class)->size_changed)
GIMP_VIEWABLE_CLASS (parent_class)->size_changed (viewable);
@@ -1219,12 +1215,15 @@ gimp_image_size_changed (GimpViewable *viewable)
gimp_viewable_size_changed (GIMP_VIEWABLE (gimp_image_get_mask (image)));
- metadata = gimp_image_get_metadata (image);
- if (metadata)
- gimp_metadata_set_pixel_size (metadata,
- gimp_image_get_width (image),
- gimp_image_get_height (image));
+ attributes = gimp_image_get_attributes (image);
+ if (attributes)
+ {
+ gimp_attributes_set_pixel_size (attributes,
+ gimp_image_get_width (image),
+ gimp_image_get_height (image));
+
+ }
gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
}
@@ -1251,31 +1250,32 @@ gimp_image_real_mode_changed (GimpImage *image)
static void
gimp_image_real_precision_changed (GimpImage *image)
{
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
- metadata = gimp_image_get_metadata (image);
- if (metadata)
+ attributes = gimp_image_get_attributes (image);
+
+ if (attributes)
{
switch (gimp_image_get_component_type (image))
- {
+ {
case GIMP_COMPONENT_TYPE_U8:
- gimp_metadata_set_bits_per_sample (metadata, 8);
+ gimp_attributes_set_bits_per_sample (attributes, 8);
break;
case GIMP_COMPONENT_TYPE_U16:
case GIMP_COMPONENT_TYPE_HALF:
- gimp_metadata_set_bits_per_sample (metadata, 16);
+ gimp_attributes_set_bits_per_sample (attributes, 16);
break;
case GIMP_COMPONENT_TYPE_U32:
case GIMP_COMPONENT_TYPE_FLOAT:
- gimp_metadata_set_bits_per_sample (metadata, 32);
+ gimp_attributes_set_bits_per_sample (attributes, 32);
break;
case GIMP_COMPONENT_TYPE_DOUBLE:
- gimp_metadata_set_bits_per_sample (metadata, 64);
+ gimp_attributes_set_bits_per_sample (attributes, 64);
break;
- }
+ }
}
gimp_projectable_structure_changed (GIMP_PROJECTABLE (image));
@@ -1284,16 +1284,17 @@ gimp_image_real_precision_changed (GimpImage *image)
static void
gimp_image_real_resolution_changed (GimpImage *image)
{
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
- metadata = gimp_image_get_metadata (image);
- if (metadata)
+ attributes = gimp_image_get_attributes (image);
+
+ if (attributes)
{
gdouble xres, yres;
gimp_image_get_resolution (image, &xres, &yres);
- gimp_metadata_set_resolution (metadata, xres, yres,
- gimp_image_get_unit (image));
+ gimp_attributes_set_resolution (attributes, xres, yres,
+ gimp_image_get_unit (image));
}
}
@@ -1314,16 +1315,17 @@ gimp_image_real_size_changed_detailed (GimpImage *image,
static void
gimp_image_real_unit_changed (GimpImage *image)
{
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
+
+ attributes = gimp_image_get_attributes (image);
- metadata = gimp_image_get_metadata (image);
- if (metadata)
+ if (attributes)
{
gdouble xres, yres;
gimp_image_get_resolution (image, &xres, &yres);
- gimp_metadata_set_resolution (metadata, xres, yres,
- gimp_image_get_unit (image));
+ gimp_attributes_set_resolution (attributes, xres, yres,
+ gimp_image_get_unit (image));
}
}
@@ -2322,10 +2324,6 @@ gimp_image_get_xcf_version (GimpImage *image,
version = MAX (3, version);
}
- /* need version 6 for new metadata */
- if (gimp_image_get_metadata (image))
- version = MAX (6, version);
-
/* need version 7 for high bit depth images */
if (gimp_image_get_precision (image) != GIMP_PRECISION_U8_GAMMA)
version = MAX (7, version);
diff --git a/app/core/gimpimageundo.c b/app/core/gimpimageundo.c
index dea3570..ce1cca7 100644
--- a/app/core/gimpimageundo.c
+++ b/app/core/gimpimageundo.c
@@ -188,9 +188,9 @@ gimp_image_undo_constructed (GObject *object)
GIMP_IMAGE_COLORMAP_SIZE);
break;
- case GIMP_UNDO_IMAGE_METADATA:
- image_undo->metadata =
- gimp_metadata_duplicate (gimp_image_get_metadata (image));
+ case GIMP_UNDO_IMAGE_ATTRIBUTES:
+ image_undo->attributes =
+ gimp_attributes_duplicate (gimp_image_get_attributes (image));
break;
case GIMP_UNDO_PARASITE_ATTACH:
@@ -291,8 +291,8 @@ gimp_image_undo_get_memsize (GimpObject *object,
if (image_undo->colormap)
memsize += GIMP_IMAGE_COLORMAP_SIZE;
- if (image_undo->metadata)
- memsize += gimp_g_object_get_memsize (G_OBJECT (image_undo->metadata));
+ if (image_undo->attributes)
+ memsize += gimp_g_object_get_memsize (G_OBJECT (image_undo->attributes));
memsize += gimp_object_get_memsize (GIMP_OBJECT (image_undo->grid),
gui_size);
@@ -457,17 +457,17 @@ gimp_image_undo_pop (GimpUndo *undo,
}
break;
- case GIMP_UNDO_IMAGE_METADATA:
+ case GIMP_UNDO_IMAGE_ATTRIBUTES:
{
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
- metadata = gimp_metadata_duplicate (gimp_image_get_metadata (image));
+ attributes = gimp_attributes_duplicate (gimp_image_get_attributes (image));
- gimp_image_set_metadata (image, image_undo->metadata, FALSE);
+ gimp_image_set_attributes (image, image_undo->attributes, FALSE);
- if (image_undo->metadata)
- g_object_unref (image_undo->metadata);
- image_undo->metadata = metadata;
+ if (image_undo->attributes)
+ g_object_unref (image_undo->attributes);
+ image_undo->attributes = attributes;
}
break;
@@ -519,10 +519,10 @@ gimp_image_undo_free (GimpUndo *undo,
image_undo->colormap = NULL;
}
- if (image_undo->metadata)
+ if (image_undo->attributes)
{
- g_object_unref (image_undo->metadata);
- image_undo->metadata = NULL;
+ g_object_unref (image_undo->attributes);
+ image_undo->attributes = NULL;
}
if (image_undo->parasite_name)
diff --git a/app/core/gimpimageundo.h b/app/core/gimpimageundo.h
index b6a9b01..3bc52e8 100644
--- a/app/core/gimpimageundo.h
+++ b/app/core/gimpimageundo.h
@@ -50,7 +50,7 @@ struct _GimpImageUndo
GimpGrid *grid;
gint num_colors;
guchar *colormap;
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
gchar *parasite_name;
GimpParasite *parasite;
};
diff --git a/app/core/gimpitem-metadata.c b/app/core/gimpitem-metadata.c
new file mode 100644
index 0000000..ee9bfbd
--- /dev/null
+++ b/app/core/gimpitem-metadata.c
@@ -0,0 +1,73 @@
+/* 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "core-types.h"
+
+#include "gimpimage.h"
+#include "gimpitem.h"
+#include "gimpitem-metadata.h"
+
+
+/* public functions */
+
+void
+gimp_item_set_attributes (GimpItem *item,
+ GimpAttributes *attributes,
+ gboolean push_undo)
+{
+ GimpViewable *viewable;
+
+ g_return_if_fail (GIMP_IS_ITEM (item));
+
+ if (push_undo)
+ gimp_image_undo_push_item_attributes (gimp_item_get_image (item),
+ NULL,
+ item);
+
+ viewable = GIMP_VIEWABLE (item);
+
+ if (viewable)
+ {
+ gimp_viewable_set_attributes (viewable, attributes);
+ }
+}
+
+GimpAttributes *
+gimp_item_get_attributes (GimpItem *item)
+{
+ GimpViewable *viewable;
+ GimpAttributes *attributes;
+
+ g_return_val_if_fail (GIMP_IS_ITEM (item), NULL);
+
+ viewable = GIMP_VIEWABLE (item);
+
+ if (viewable)
+ {
+ attributes = gimp_viewable_get_attributes (viewable);
+ return attributes;
+ }
+
+ return NULL;
+}
diff --git a/app/core/gimpitem-metadata.h b/app/core/gimpitem-metadata.h
new file mode 100644
index 0000000..df2d0b1
--- /dev/null
+++ b/app/core/gimpitem-metadata.h
@@ -0,0 +1,28 @@
+/* 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/>.
+ */
+
+#ifndef __GIMP_ITEM_METADATA_H__
+#define __GIMP_ITEM_METADATA_H__
+
+
+void gimp_item_set_attributes (GimpItem *item,
+ GimpAttributes *attributes,
+ gboolean push_undo);
+GimpAttributes * gimp_item_get_attributes (GimpItem *item);
+
+
+#endif /* __GIMP_ITEM_METADATA_H__ */
diff --git a/app/core/gimpitemundo.c b/app/core/gimpitemundo.c
index 34c9539..aceb6c7 100644
--- a/app/core/gimpitemundo.c
+++ b/app/core/gimpitemundo.c
@@ -22,8 +22,11 @@
#include "core-types.h"
+#include "libgimpbase/gimpbase.h"
+
#include "gimpimage.h"
#include "gimpitem.h"
+#include "gimpitem-metadata.h"
#include "gimpitemundo.h"
@@ -34,18 +37,21 @@ enum
};
-static void gimp_item_undo_constructed (GObject *object);
-static void gimp_item_undo_set_property (GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gimp_item_undo_get_property (GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec);
+static void gimp_item_undo_constructed (GObject *object);
+static void gimp_item_undo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_item_undo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
-static void gimp_item_undo_free (GimpUndo *undo,
- GimpUndoMode undo_mode);
+static void gimp_item_undo_free (GimpUndo *undo,
+ GimpUndoMode undo_mode);
+static void gimp_item_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum);
G_DEFINE_TYPE (GimpItemUndo, gimp_item_undo, GIMP_TYPE_UNDO)
@@ -63,6 +69,7 @@ gimp_item_undo_class_init (GimpItemUndoClass *klass)
object_class->set_property = gimp_item_undo_set_property;
object_class->get_property = gimp_item_undo_get_property;
+ undo_class->pop = gimp_item_undo_pop;
undo_class->free = gimp_item_undo_free;
g_object_class_install_property (object_class, PROP_ITEM,
@@ -84,7 +91,14 @@ gimp_item_undo_constructed (GObject *object)
G_OBJECT_CLASS (parent_class)->constructed (object);
- g_assert (GIMP_IS_ITEM (item_undo->item));
+ switch (GIMP_UNDO (object)->undo_type)
+ {
+ case GIMP_UNDO_ITEM_ATTRIBUTES:
+ item_undo->attributes =
+ gimp_attributes_duplicate (gimp_item_get_attributes (item_undo->item));
+ break;
+ }
+
}
static void
@@ -139,5 +153,41 @@ gimp_item_undo_free (GimpUndo *undo,
item_undo->item = NULL;
}
+ if (item_undo->attributes)
+ {
+ g_object_unref (item_undo->attributes);
+ item_undo->attributes = NULL;
+ }
+
GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
}
+
+static void
+gimp_item_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum)
+{
+ GimpItemUndo *item_undo = GIMP_ITEM_UNDO (undo);
+ GimpItem *item = GIMP_ITEM_UNDO (undo)->item;
+
+ switch (undo->undo_type)
+ {
+ case GIMP_UNDO_ITEM_ATTRIBUTES:
+ {
+ GimpAttributes *attributes;
+
+ attributes = gimp_attributes_duplicate (gimp_item_get_attributes (item));
+
+ gimp_item_set_attributes (item, item_undo->attributes, FALSE);
+
+ if (item_undo->attributes)
+ g_object_unref (item_undo->attributes);
+ item_undo->attributes = attributes;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+
+ }
+}
diff --git a/app/core/gimpitemundo.h b/app/core/gimpitemundo.h
index 2c137b4..4b002d9 100644
--- a/app/core/gimpitemundo.h
+++ b/app/core/gimpitemundo.h
@@ -37,6 +37,8 @@ struct _GimpItemUndo
GimpUndo parent_instance;
GimpItem *item; /* the item this undo is for */
+ GimpAttributes *attributes;
+
};
struct _GimpItemUndoClass
diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c
index af47dd0..5ca547f 100644
--- a/app/core/gimplayer.c
+++ b/app/core/gimplayer.c
@@ -705,7 +705,9 @@ static GimpItem *
gimp_layer_duplicate (GimpItem *item,
GType new_type)
{
- GimpItem *new_item;
+ GimpItem *new_item;
+ GimpAttributes *attributes;
+ GimpAttributes *new_attributes;
g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);
@@ -736,6 +738,14 @@ gimp_layer_duplicate (GimpItem *item,
new_layer->edit_mask = layer->edit_mask;
new_layer->show_mask = layer->show_mask;
}
+
+ attributes = gimp_viewable_get_attributes (GIMP_VIEWABLE (layer));
+ if (attributes)
+ {
+ new_attributes = gimp_attributes_duplicate (attributes);
+ gimp_viewable_set_attributes (GIMP_VIEWABLE (new_layer), new_attributes);
+ g_object_unref (new_attributes);
+ }
}
return new_item;
diff --git a/app/core/gimpviewable.c b/app/core/gimpviewable.c
index 319a284..2d357cf 100644
--- a/app/core/gimpviewable.c
+++ b/app/core/gimpviewable.c
@@ -26,6 +26,7 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
+#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpconfig/gimpconfig.h"
@@ -125,7 +126,6 @@ static gboolean gimp_viewable_deserialize_property (GimpConfig *config,
GScanner *scanner,
GTokenType *expected);
-
G_DEFINE_TYPE_WITH_CODE (GimpViewable, gimp_viewable, GIMP_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
gimp_viewable_config_iface_init))
@@ -210,6 +210,7 @@ gimp_viewable_class_init (GimpViewableClass *klass)
static void
gimp_viewable_init (GimpViewable *viewable)
{
+ viewable->attributes = NULL;
}
static void
@@ -222,6 +223,7 @@ gimp_viewable_config_iface_init (GimpConfigInterface *iface)
static void
gimp_viewable_finalize (GObject *object)
{
+ GimpViewable *viewable = GIMP_VIEWABLE (object);
GimpViewablePrivate *private = GET_PRIVATE (object);
if (private->icon_name)
@@ -247,6 +249,11 @@ gimp_viewable_finalize (GObject *object)
g_object_unref (private->preview_pixbuf);
private->preview_pixbuf = NULL;
}
+ if (viewable->attributes)
+ {
+ g_object_unref (viewable->attributes);
+ viewable->attributes = NULL;
+ }
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -575,8 +582,23 @@ gimp_viewable_invalidate_preview (GimpViewable *viewable)
void
gimp_viewable_size_changed (GimpViewable *viewable)
{
+ GimpAttributes *attributes = NULL;
+ gint width, height;
+
g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
+ gimp_viewable_get_size (viewable, &width, &height);
+
+ attributes = gimp_viewable_get_attributes (viewable);
+
+ if (attributes)
+ {
+ gimp_attributes_set_pixel_size (attributes,
+ width,
+ height);
+
+ }
+
g_signal_emit (viewable, viewable_signals[SIZE_CHANGED], 0);
}
@@ -1300,6 +1322,28 @@ gimp_viewable_set_expanded (GimpViewable *viewable,
GIMP_VIEWABLE_GET_CLASS (viewable)->set_expanded (viewable, expanded);
}
+void
+gimp_viewable_set_attributes (GimpViewable *viewable,
+ GimpAttributes *attributes)
+{
+ g_return_if_fail (GIMP_IS_VIEWABLE (viewable));
+
+ if (attributes)
+ {
+
+ viewable->attributes = attributes;
+ g_object_ref (viewable->attributes);
+ }
+}
+
+GimpAttributes *
+gimp_viewable_get_attributes (GimpViewable *viewable)
+{
+ g_return_val_if_fail (GIMP_IS_VIEWABLE (viewable), NULL);
+
+ return viewable->attributes;
+}
+
gboolean
gimp_viewable_is_ancestor (GimpViewable *ancestor,
GimpViewable *descendant)
diff --git a/app/core/gimpviewable.h b/app/core/gimpviewable.h
index cb55175..b8a1332 100644
--- a/app/core/gimpviewable.h
+++ b/app/core/gimpviewable.h
@@ -43,7 +43,8 @@ typedef struct _GimpViewableClass GimpViewableClass;
struct _GimpViewable
{
- GimpObject parent_instance;
+ GimpObject parent_instance;
+ GimpAttributes *attributes;
};
struct _GimpViewableClass
@@ -97,6 +98,7 @@ struct _GimpViewableClass
void (* set_expanded) (GimpViewable *viewable,
gboolean expand);
gboolean (* get_expanded) (GimpViewable *viewable);
+
};
@@ -179,7 +181,10 @@ GimpContainer * gimp_viewable_get_children (GimpViewable *viewable);
gboolean gimp_viewable_get_expanded (GimpViewable *viewable);
void gimp_viewable_set_expanded (GimpViewable *viewable,
gboolean expanded);
-
+void gimp_viewable_set_attributes (GimpViewable *viewable,
+ GimpAttributes *attributes);
+GimpAttributes *
+ gimp_viewable_get_attributes (GimpViewable *viewable);
gboolean gimp_viewable_is_ancestor (GimpViewable *ancestor,
GimpViewable *descendant);
diff --git a/app/core/gimpviewableundo.c b/app/core/gimpviewableundo.c
new file mode 100644
index 0000000..a62e92c
--- /dev/null
+++ b/app/core/gimpviewableundo.c
@@ -0,0 +1,175 @@
+/* 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "core-types.h"
+
+#include "gimpviewable.h"
+#include "gimpviewableundo.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_VIEWABLE
+};
+
+
+static void gimp_viewable_undo_constructed (GObject *object);
+static void gimp_viewable_undo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_viewable_undo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_viewable_undo_free (GimpUndo *undo,
+ GimpUndoMode undo_mode);
+
+
+G_DEFINE_TYPE (GimpViewableUndo, gimp_viewable_undo, GIMP_TYPE_UNDO)
+
+#define parent_class gimp_viewable_undo_parent_class
+
+
+static void
+gimp_viewable_undo_class_init (GimpViewableUndoClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass);
+
+ object_class->constructed = gimp_viewable_undo_constructed;
+ object_class->set_property = gimp_viewable_undo_set_property;
+ object_class->get_property = gimp_viewable_undo_get_property;
+
+ undo_class->pop = gimp_viewable_undo_pop;
+ undo_class->free = gimp_viewable_undo_free;
+
+ g_object_class_install_property (object_class, PROP_VIEWABLE,
+ g_param_spec_object ("viewable", NULL, NULL,
+ GIMP_TYPE_VIEWABLE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_viewable_undo_init (GimpViewableUndo *undo)
+{
+}
+
+static void
+gimp_viewable_undo_constructed (GObject *object)
+{
+ GimpViewableUndo *viewable_undo = GIMP_VIEWABLE_UNDO (object);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ g_assert (GIMP_IS_VIEWABLE (viewable_undo->viewable));
+}
+
+static void
+gimp_viewable_undo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpViewableUndo *viewable_undo = GIMP_VIEWABLE_UNDO (object);
+
+ switch (property_id)
+ {
+ case PROP_VIEWABLE:
+ viewable_undo->viewable = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_viewable_undo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpViewableUndo *viewable_undo = GIMP_VIEWABLE_UNDO (object);
+
+ switch (property_id)
+ {
+ case PROP_VIEWABLE:
+ g_value_set_object (value, viewable_undo->viewable);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_viewable_undo_free (GimpUndo *undo,
+ GimpUndoMode undo_mode)
+{
+ GimpViewableUndo *viewable_undo = GIMP_VIEWABLE_UNDO (undo);
+
+ if (viewable_undo->viewable)
+ {
+ g_object_unref (viewable_undo->viewable);
+ viewable_undo->viewable = NULL;
+ }
+
+ GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
+}
+
+static void
+gimp_viewable_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum)
+{
+ GimpViewableUndo *viewable_undo = GIMP_VIEWABLE_UNDO (undo);
+ GimpViewable *viewable = GIMP_VIEWABLE_UNDO (undo)->viewable;
+
+ switch (undo->undo_type)
+ {
+ case GIMP_UNDO_IMAGE_ATTRIBUTES:
+ {
+ GimpAttributes *attributes;
+
+ attributes = gimp_attributes_duplicate (gimp_image_get_attributes (image));
+
+ gimp_image_set_attributes (image, image_undo->attributes, FALSE);
+
+ if (image_undo->attributes)
+ g_object_unref (image_undo->attributes);
+ image_undo->attributes = attributes;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+
+ }
+}
\ No newline at end of file
diff --git a/app/core/gimpviewableundo.h b/app/core/gimpviewableundo.h
new file mode 100644
index 0000000..848098c
--- /dev/null
+++ b/app/core/gimpviewableundo.h
@@ -0,0 +1,51 @@
+/* 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/>.
+ */
+
+#ifndef __GIMP_VIEWABLE_UNDO_H__
+#define __GIMP_VIEWABLE_UNDO_H__
+
+
+#include "gimpundo.h"
+
+
+#define GIMP_TYPE_VIEWABLE_UNDO (gimp_viewable_undo_get_type ())
+#define GIMP_VIEWABLE_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_VIEWABLE_UNDO,
GimpViewableUndo))
+#define GIMP_VIEWABLE_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_VIEWABLE_UNDO,
GimpViewableUndoClass))
+#define GIMP_IS_VIEWABLE_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_VIEWABLE_UNDO))
+#define GIMP_IS_VIEWABLE_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_VIEWABLE_UNDO))
+#define GIMP_VIEWABLE_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_VIEWABLE_UNDO,
GimpViewableUndoClass))
+
+
+typedef struct _GimpViewableUndoClass GimpViewableUndoClass;
+
+struct _GimpViewableUndo
+{
+ GimpUndo parent_instance;
+
+ GimpViewable *viewable; /* the viewable this undo is for */
+};
+
+struct _GimpViewableUndoClass
+{
+ GimpUndoClass parent_class;
+};
+
+
+GType gimp_viewable_undo_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_VIEWABLE_UNDO_H__ */
diff --git a/app/file/file-open.c b/app/file/file-open.c
index 59f0000..39c6d93 100644
--- a/app/file/file-open.c
+++ b/app/file/file-open.c
@@ -139,7 +139,7 @@ file_open_image (Gimp *gimp,
G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
{
g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
- _("Premission denied"));
+ _("Permission denied"));
return NULL;
}
}
diff --git a/app/pdb/image-cmds.c b/app/pdb/image-cmds.c
index 1633473..ecd63f0 100644
--- a/app/pdb/image-cmds.c
+++ b/app/pdb/image-cmds.c
@@ -1707,64 +1707,64 @@ image_set_colormap_invoker (GimpProcedure *procedure,
}
static GimpValueArray *
-image_get_metadata_invoker (GimpProcedure *procedure,
- Gimp *gimp,
- GimpContext *context,
- GimpProgress *progress,
- const GimpValueArray *args,
- GError **error)
+image_set_attributes_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
{
gboolean success = TRUE;
- GimpValueArray *return_vals;
GimpImage *image;
- gchar *metadata_string = NULL;
+ const gchar *attributes_string;
image = gimp_value_get_image (gimp_value_array_index (args, 0), gimp);
+ attributes_string = g_value_get_string (gimp_value_array_index (args, 1));
if (success)
{
- GimpMetadata *metadata = gimp_image_get_metadata (image);
+ GimpAttributes *attributes = gimp_attributes_deserialize (attributes_string);
- if (metadata)
- metadata_string = gimp_metadata_serialize (metadata);
- }
-
- return_vals = gimp_procedure_get_return_values (procedure, success,
- error ? *error : NULL);
+ gimp_image_set_attributes (image, attributes, TRUE);
- if (success)
- g_value_take_string (gimp_value_array_index (return_vals, 1), metadata_string);
+ if (attributes)
+ g_object_unref (attributes);
+ }
- return return_vals;
+ return gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
}
static GimpValueArray *
-image_set_metadata_invoker (GimpProcedure *procedure,
- Gimp *gimp,
- GimpContext *context,
- GimpProgress *progress,
- const GimpValueArray *args,
- GError **error)
+image_get_attributes_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
{
gboolean success = TRUE;
+ GimpValueArray *return_vals;
GimpImage *image;
- const gchar *metadata_string;
+ gchar *attributes_string = NULL;
image = gimp_value_get_image (gimp_value_array_index (args, 0), gimp);
- metadata_string = g_value_get_string (gimp_value_array_index (args, 1));
if (success)
{
- GimpMetadata *metadata = gimp_metadata_deserialize (metadata_string);
-
- gimp_image_set_metadata (image, metadata, TRUE);
+ GimpAttributes *attributes = gimp_image_get_attributes (image);
- if (metadata)
- g_object_unref (metadata);
+ if (attributes)
+ attributes_string = gimp_attributes_serialize (attributes);
}
- return gimp_procedure_get_return_values (procedure, success,
- error ? *error : NULL);
+ return_vals = gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
+
+ if (success)
+ g_value_take_string (gimp_value_array_index (return_vals, 1), attributes_string);
+
+ return return_vals;
}
static GimpValueArray *
@@ -4622,15 +4622,15 @@ register_image_procs (GimpPDB *pdb)
g_object_unref (procedure);
/*
- * gimp-image-get-metadata
+ * gimp-image-set-attributes
*/
- procedure = gimp_procedure_new (image_get_metadata_invoker);
+ procedure = gimp_procedure_new (image_set_attributes_invoker);
gimp_object_set_static_name (GIMP_OBJECT (procedure),
- "gimp-image-get-metadata");
+ "gimp-image-set-attributes");
gimp_procedure_set_static_strings (procedure,
- "gimp-image-get-metadata",
- "Returns the image's metadata.",
- "Returns exif/iptc/xmp metadata from the image.",
+ "gimp-image-set-attributes",
+ "Set the image's attributes.",
+ "Sets attributes on the image.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
@@ -4641,26 +4641,26 @@ register_image_procs (GimpPDB *pdb)
"The image",
pdb->gimp, FALSE,
GIMP_PARAM_READWRITE));
- gimp_procedure_add_return_value (procedure,
- gimp_param_spec_string ("metadata-string",
- "metadata string",
- "The exif/ptc/xmp metadata as a string",
- FALSE, FALSE, FALSE,
- NULL,
- GIMP_PARAM_READWRITE));
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_string ("attributes-string",
+ "attributes string",
+ "The attributes as a xml string",
+ FALSE, FALSE, FALSE,
+ NULL,
+ GIMP_PARAM_READWRITE));
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
- * gimp-image-set-metadata
+ * gimp-image-get-attributes
*/
- procedure = gimp_procedure_new (image_set_metadata_invoker);
+ procedure = gimp_procedure_new (image_get_attributes_invoker);
gimp_object_set_static_name (GIMP_OBJECT (procedure),
- "gimp-image-set-metadata");
+ "gimp-image-get-attributes");
gimp_procedure_set_static_strings (procedure,
- "gimp-image-set-metadata",
- "Set the image's metadata.",
- "Sets exif/iptc/xmp metadata on the image.",
+ "gimp-image-get-attributes",
+ "Returns the image's attributes.",
+ "Returns attributes from the image.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
@@ -4671,13 +4671,13 @@ register_image_procs (GimpPDB *pdb)
"The image",
pdb->gimp, FALSE,
GIMP_PARAM_READWRITE));
- gimp_procedure_add_argument (procedure,
- gimp_param_spec_string ("metadata-string",
- "metadata string",
- "The exif/ptc/xmp metadata as a string",
- FALSE, FALSE, FALSE,
- NULL,
- GIMP_PARAM_READWRITE));
+ gimp_procedure_add_return_value (procedure,
+ gimp_param_spec_string ("attributes-string",
+ "attributes string",
+ "The attributes as a xml string",
+ FALSE, FALSE, FALSE,
+ NULL,
+ GIMP_PARAM_READWRITE));
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
diff --git a/app/pdb/internal-procs.c b/app/pdb/internal-procs.c
index 1639970..872acab 100644
--- a/app/pdb/internal-procs.c
+++ b/app/pdb/internal-procs.c
@@ -28,7 +28,7 @@
#include "internal-procs.h"
-/* 732 procedures registered total */
+/* 734 procedures registered total */
void
internal_procs_init (GimpPDB *pdb)
diff --git a/app/pdb/item-cmds.c b/app/pdb/item-cmds.c
index 6194593..762fc74 100644
--- a/app/pdb/item-cmds.c
+++ b/app/pdb/item-cmds.c
@@ -30,6 +30,7 @@
#include "pdb-types.h"
#include "core/gimpimage.h"
+#include "core/gimpitem-metadata.h"
#include "core/gimpitem.h"
#include "core/gimplayermask.h"
#include "core/gimplist.h"
@@ -769,6 +770,67 @@ item_set_tattoo_invoker (GimpProcedure *procedure,
}
static GimpValueArray *
+item_get_attributes_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
+{
+ gboolean success = TRUE;
+ GimpValueArray *return_vals;
+ GimpItem *item;
+ gchar *attributes_string = NULL;
+
+ item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
+
+ if (success)
+ {
+ GimpAttributes *attributes = gimp_item_get_attributes (item);
+
+ if (attributes)
+ attributes_string = gimp_attributes_serialize (attributes);
+ }
+
+ return_vals = gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
+
+ if (success)
+ g_value_take_string (gimp_value_array_index (return_vals, 1), attributes_string);
+
+ return return_vals;
+}
+
+static GimpValueArray *
+item_set_attributes_invoker (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ const GimpValueArray *args,
+ GError **error)
+{
+ gboolean success = TRUE;
+ GimpItem *item;
+ const gchar *attributes_string;
+
+ item = gimp_value_get_item (gimp_value_array_index (args, 0), gimp);
+ attributes_string = g_value_get_string (gimp_value_array_index (args, 1));
+
+ if (success)
+ {
+ GimpAttributes *attributes = gimp_attributes_deserialize (attributes_string);
+
+ gimp_item_set_attributes (item, attributes, TRUE);
+
+ if (attributes)
+ g_object_unref (attributes);
+ }
+
+ return gimp_procedure_get_return_values (procedure, success,
+ error ? *error : NULL);
+}
+
+static GimpValueArray *
item_attach_parasite_invoker (GimpProcedure *procedure,
Gimp *gimp,
GimpContext *context,
@@ -1618,6 +1680,66 @@ register_item_procs (GimpPDB *pdb)
g_object_unref (procedure);
/*
+ * gimp-item-get-attributes
+ */
+ procedure = gimp_procedure_new (item_get_attributes_invoker);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-item-get-attributes");
+ gimp_procedure_set_static_strings (procedure,
+ "gimp-item-get-attributes",
+ "Returns the item's attributes.",
+ "Returns attributes from the item.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996",
+ NULL);
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_item_id ("item",
+ "item",
+ "The item",
+ pdb->gimp, FALSE,
+ GIMP_PARAM_READWRITE));
+ gimp_procedure_add_return_value (procedure,
+ gimp_param_spec_string ("attributes-string",
+ "attributes string",
+ "The attributes as a xml string",
+ FALSE, FALSE, FALSE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_pdb_register_procedure (pdb, procedure);
+ g_object_unref (procedure);
+
+ /*
+ * gimp-item-set-attributes
+ */
+ procedure = gimp_procedure_new (item_set_attributes_invoker);
+ gimp_object_set_static_name (GIMP_OBJECT (procedure),
+ "gimp-item-set-attributes");
+ gimp_procedure_set_static_strings (procedure,
+ "gimp-item-set-attributes",
+ "Set the item's attributes.",
+ "Sets attributes on the item.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996",
+ NULL);
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_item_id ("item",
+ "item",
+ "The item",
+ pdb->gimp, FALSE,
+ GIMP_PARAM_READWRITE));
+ gimp_procedure_add_argument (procedure,
+ gimp_param_spec_string ("attributes-string",
+ "attributes string",
+ "The attributes as a xml string",
+ FALSE, FALSE, FALSE,
+ NULL,
+ GIMP_PARAM_READWRITE));
+ gimp_pdb_register_procedure (pdb, procedure);
+ g_object_unref (procedure);
+
+ /*
* gimp-item-attach-parasite
*/
procedure = gimp_procedure_new (item_attach_parasite_invoker);
diff --git a/app/xcf/xcf-load.c b/app/xcf/xcf-load.c
index f9f1e6a..e2473d9 100644
--- a/app/xcf/xcf-load.c
+++ b/app/xcf/xcf-load.c
@@ -47,6 +47,7 @@
#include "core/gimpimage-sample-points.h"
#include "core/gimpimage-undo.h"
#include "core/gimpitemstack.h"
+#include "core/gimpitem-metadata.h"
#include "core/gimplayer-floating-sel.h"
#include "core/gimplayermask.h"
#include "core/gimpparasitelist.h"
@@ -244,30 +245,39 @@ xcf_load_image (Gimp *gimp,
}
}
- /* check for a metadata parasite */
+ /* check for an attributes parasite */
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
- "gimp-image-metadata");
+ "gimp-image-attributes");
if (parasite)
{
GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
- GimpMetadata *metadata;
- const gchar *meta_string;
+ GimpAttributes *attributes;
+ const gchar *attributes_string;
- meta_string = (gchar *) gimp_parasite_data (parasite);
- metadata = gimp_metadata_deserialize (meta_string);
+ attributes_string = (gchar *) gimp_parasite_data (parasite);
+ attributes = gimp_attributes_deserialize (attributes_string);
- if (metadata)
+ if (attributes)
{
- has_metadata = TRUE;
-
- gimp_image_set_metadata (image, metadata, FALSE);
- g_object_unref (metadata);
+ gimp_image_set_attributes (image, attributes, FALSE);
+ g_object_unref (attributes);
}
gimp_parasite_list_remove (private->parasites,
gimp_parasite_name (parasite));
}
+ /* check for a metadata parasite */
+ parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
+ "gimp-image-metadata");
+ if (parasite)
+ {
+ GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
+
+ gimp_parasite_list_remove (private->parasites,
+ gimp_parasite_name (parasite));
+ }
+
/* migrate the old "exif-data" parasite */
parasite = gimp_image_parasite_find (GIMP_IMAGE (image),
"exif-data");
@@ -283,13 +293,11 @@ xcf_load_image (Gimp *gimp,
}
else
{
- GimpMetadata *metadata = gimp_image_get_metadata (image);
- GError *my_error = NULL;
+ GimpMetadata *metadata = NULL;
+ GimpAttributes *attributes = NULL;
+ GError *my_error = NULL;
- if (metadata)
- g_object_ref (metadata);
- else
- metadata = gimp_metadata_new ();
+ metadata = gimp_metadata_new ();
if (! gimp_metadata_set_from_exif (metadata,
gimp_parasite_data (parasite),
@@ -305,7 +313,12 @@ xcf_load_image (Gimp *gimp,
}
else
{
- gimp_image_set_metadata (image, metadata, FALSE);
+ attributes = gimp_attributes_new ();
+ attributes = gimp_attributes_from_metadata (attributes, metadata);
+
+ gimp_image_set_attributes (image, attributes, FALSE);
+
+ g_object_unref (attributes);
}
g_object_unref (metadata);
@@ -330,13 +343,11 @@ xcf_load_image (Gimp *gimp,
}
else
{
- GimpMetadata *metadata = gimp_image_get_metadata (image);
- GError *my_error = NULL;
+ GimpMetadata *metadata = NULL;
+ GimpAttributes *attributes = NULL;
+ GError *my_error = NULL;
- if (metadata)
- g_object_ref (metadata);
- else
- metadata = gimp_metadata_new ();
+ metadata = gimp_metadata_new ();
if (! gimp_metadata_set_from_xmp (metadata,
gimp_parasite_data (parasite),
@@ -352,7 +363,12 @@ xcf_load_image (Gimp *gimp,
}
else
{
- gimp_image_set_metadata (image, metadata, FALSE);
+ attributes = gimp_attributes_new ();
+ attributes = gimp_attributes_from_metadata (attributes, metadata);
+
+ gimp_image_set_attributes (image, attributes, FALSE);
+
+ g_object_unref (attributes);
}
g_object_unref (metadata);
@@ -1350,25 +1366,26 @@ xcf_load_layer (XcfInfo *info,
GimpImage *image,
GList **item_path)
{
- GimpLayer *layer;
- GimpLayerMask *layer_mask;
- guint32 hierarchy_offset;
- guint32 layer_mask_offset;
- gboolean apply_mask = TRUE;
- gboolean edit_mask = FALSE;
- gboolean show_mask = FALSE;
- gboolean active;
- gboolean floating;
- guint32 group_layer_flags = 0;
- guint32 text_layer_flags = 0;
- gint width;
- gint height;
- gint type;
- GimpImageBaseType base_type;
- gboolean has_alpha;
- const Babl *format;
- gboolean is_fs_drawable;
- gchar *name;
+ GimpLayer *layer;
+ GimpLayerMask *layer_mask;
+ const GimpParasite *parasite;
+ guint32 hierarchy_offset;
+ guint32 layer_mask_offset;
+ gboolean apply_mask = TRUE;
+ gboolean edit_mask = FALSE;
+ gboolean show_mask = FALSE;
+ gboolean active;
+ gboolean floating;
+ guint32 group_layer_flags = 0;
+ guint32 text_layer_flags = 0;
+ gint width;
+ gint height;
+ gint type;
+ GimpImageBaseType base_type;
+ gboolean has_alpha;
+ const Babl *format;
+ gboolean is_fs_drawable;
+ gchar *name;
/* check and see if this is the drawable the floating selection
* is attached to. if it is then we'll do the attachment in our caller.
@@ -1445,6 +1462,29 @@ xcf_load_layer (XcfInfo *info,
GIMP_LOG (XCF, "layer props loaded");
+ parasite = gimp_item_parasite_find (GIMP_ITEM (layer),
+ "gimp-item-attributes");
+ if (parasite)
+ {
+ GimpAttributes *attributes;
+ const gchar *attributes_string;
+
+ attributes_string = (gchar *) gimp_parasite_data (parasite);
+ attributes = gimp_attributes_deserialize (attributes_string);
+
+ if (attributes)
+ {
+ gimp_item_set_attributes (GIMP_ITEM (layer), attributes, FALSE);
+ g_object_unref (attributes);
+ }
+
+ gimp_item_parasite_detach (GIMP_ITEM (layer),
+ "gimp-item-attributes",
+ FALSE);
+ }
+
+ GIMP_LOG (XCF, "layer attributes loaded");
+
xcf_progress_update (info);
/* call the evil text layer hack that might change our layer pointer */
@@ -1534,13 +1574,14 @@ static GimpChannel *
xcf_load_channel (XcfInfo *info,
GimpImage *image)
{
- GimpChannel *channel;
- guint32 hierarchy_offset;
- gint width;
- gint height;
- gboolean is_fs_drawable;
- gchar *name;
- GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE };
+ GimpChannel *channel;
+ const GimpParasite *parasite;
+ guint32 hierarchy_offset;
+ gint width;
+ gint height;
+ gboolean is_fs_drawable;
+ gchar *name;
+ GimpRGB color = { 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE };
/* check and see if this is the drawable the floating selection
* is attached to. if it is then we'll do the attachment in our caller.
@@ -1565,6 +1606,27 @@ xcf_load_channel (XcfInfo *info,
if (!xcf_load_channel_props (info, image, &channel))
goto error;
+ parasite = gimp_item_parasite_find (GIMP_ITEM (channel),
+ "gimp-item-attributes");
+ if (parasite)
+ {
+ GimpAttributes *attributes;
+ const gchar *attributes_string;
+
+ attributes_string = (gchar *) gimp_parasite_data (parasite);
+ attributes = gimp_attributes_deserialize (attributes_string);
+
+ if (attributes)
+ {
+ gimp_item_set_attributes (GIMP_ITEM (channel), attributes, FALSE);
+ g_object_unref (attributes);
+ }
+
+ gimp_item_parasite_detach (GIMP_ITEM (channel),
+ "gimp-item-attributes",
+ FALSE);
+ }
+
xcf_progress_update (info);
/* read the hierarchy and layer mask offsets */
diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c
index 1ceb521..0bf88c2 100644
--- a/app/xcf/xcf-save.c
+++ b/app/xcf/xcf-save.c
@@ -45,6 +45,7 @@
#include "core/gimpimage-metadata.h"
#include "core/gimpimage-private.h"
#include "core/gimpimage-sample-points.h"
+#include "core/gimpitem-metadata.h"
#include "core/gimplayer.h"
#include "core/gimplayermask.h"
#include "core/gimpparasitelist.h"
@@ -355,10 +356,11 @@ xcf_save_image_props (XcfInfo *info,
GimpImage *image,
GError **error)
{
- GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
- GimpParasite *grid_parasite = NULL;
- GimpParasite *meta_parasite = NULL;
- GimpUnit unit = gimp_image_get_unit (image);
+ GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image);
+ GimpParasite *grid_parasite = NULL;
+ GimpParasite *meta_parasite = NULL;
+ GimpParasite *attributes_parasite = NULL;
+ GimpUnit unit = gimp_image_get_unit (image);
gdouble xres;
gdouble yres;
@@ -410,21 +412,21 @@ xcf_save_image_props (XcfInfo *info,
gimp_parasite_list_add (private->parasites, grid_parasite);
}
- if (gimp_image_get_metadata (image))
+ if (gimp_image_get_attributes (image))
{
- GimpMetadata *metadata = gimp_image_get_metadata (image);
- gchar *meta_string;
+ GimpAttributes *attributes = gimp_image_get_attributes (image);
+ gchar *attributes_string;
- meta_string = gimp_metadata_serialize (metadata);
+ attributes_string = gimp_attributes_serialize (attributes);
- if (meta_string)
+ if (attributes_string)
{
- meta_parasite = gimp_parasite_new ("gimp-image-metadata",
- GIMP_PARASITE_PERSISTENT,
- strlen (meta_string) + 1,
- meta_string);
- gimp_parasite_list_add (private->parasites, meta_parasite);
- g_free (meta_string);
+ attributes_parasite = gimp_parasite_new ("gimp-image-attributes",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (attributes_string) + 1,
+ attributes_string);
+ gimp_parasite_list_add (private->parasites, attributes_parasite);
+ g_free (attributes_string);
}
}
@@ -462,6 +464,25 @@ xcf_save_layer_props (XcfInfo *info,
GimpParasiteList *parasites;
gint offset_x;
gint offset_y;
+ GimpParasite *attributes_parasite = NULL;
+
+ if (gimp_item_get_attributes (GIMP_ITEM (layer)))
+ {
+ GimpAttributes *attributes = gimp_item_get_attributes (GIMP_ITEM (layer));
+ gchar *attributes_string;
+
+ attributes_string = gimp_attributes_serialize (attributes);
+
+ if (attributes_string)
+ {
+ attributes_parasite = gimp_parasite_new ("gimp-item-attributes",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (attributes_string) + 1,
+ attributes_string);
+ gimp_item_parasite_attach (GIMP_ITEM (layer), attributes_parasite, FALSE);
+ g_free (attributes_string);
+ }
+ }
if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
xcf_check_error (xcf_save_prop (info, image, PROP_GROUP_ITEM, error));
@@ -573,6 +594,25 @@ xcf_save_channel_props (XcfInfo *info,
{
GimpParasiteList *parasites;
guchar col[3];
+ GimpParasite *attributes_parasite = NULL;
+
+ if (gimp_item_get_attributes (GIMP_ITEM (channel)))
+ {
+ GimpAttributes *attributes = gimp_item_get_attributes (GIMP_ITEM (channel));
+ gchar *attributes_string;
+
+ attributes_string = gimp_attributes_serialize (attributes);
+
+ if (attributes_string)
+ {
+ attributes_parasite = gimp_parasite_new ("gimp-item-attributes",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (attributes_string) + 1,
+ attributes_string);
+ gimp_item_parasite_attach (GIMP_ITEM (channel), attributes_parasite, FALSE);
+ g_free (attributes_string);
+ }
+ }
if (channel == gimp_image_get_active_channel (image))
xcf_check_error (xcf_save_prop (info, image, PROP_ACTIVE_CHANNEL, error));
diff --git a/libgimp/Makefile.am b/libgimp/Makefile.am
index d1540d4..0d5a47a 100644
--- a/libgimp/Makefile.am
+++ b/libgimp/Makefile.am
@@ -206,6 +206,8 @@ libgimp_sources = \
gimpgradientselect.h \
gimpimage.c \
gimpimage.h \
+ gimpitem.c \
+ gimpitem.h \
gimplayer.c \
gimplayer.h \
gimppalette.c \
@@ -327,6 +329,7 @@ gimpinclude_HEADERS = \
gimpgradients.h \
gimpgradientselect.h \
gimpimage.h \
+ gimpitem.h \
gimplayer.h \
gimppalette.h \
gimppalettes.h \
diff --git a/libgimp/gimp.def b/libgimp/gimp.def
index 111f76e..daf7c96 100644
--- a/libgimp/gimp.def
+++ b/libgimp/gimp.def
@@ -389,6 +389,7 @@ EXPORTS
gimp_image_get_active_drawable
gimp_image_get_active_layer
gimp_image_get_active_vectors
+ gimp_image_get_attributes
gimp_image_get_channel_by_name
gimp_image_get_channel_by_tattoo
gimp_image_get_channel_position
@@ -408,7 +409,6 @@ EXPORTS
gimp_image_get_layer_by_tattoo
gimp_image_get_layer_position
gimp_image_get_layers
- gimp_image_get_metadata
gimp_image_get_name
gimp_image_get_parasite
gimp_image_get_parasite_list
@@ -485,12 +485,12 @@ EXPORTS
gimp_image_set_active_channel
gimp_image_set_active_layer
gimp_image_set_active_vectors
+ gimp_image_set_attributes
gimp_image_set_cmap
gimp_image_set_colormap
gimp_image_set_component_active
gimp_image_set_component_visible
gimp_image_set_filename
- gimp_image_set_metadata
gimp_image_set_resolution
gimp_image_set_tattoo_state
gimp_image_set_unit
@@ -510,6 +510,7 @@ EXPORTS
gimp_item_attach_parasite
gimp_item_delete
gimp_item_detach_parasite
+ gimp_item_get_attributes
gimp_item_get_children
gimp_item_get_image
gimp_item_get_linked
@@ -530,6 +531,7 @@ EXPORTS
gimp_item_is_text_layer
gimp_item_is_valid
gimp_item_is_vectors
+ gimp_item_set_attributes
gimp_item_set_linked
gimp_item_set_lock_content
gimp_item_set_lock_position
diff --git a/libgimp/gimp.h b/libgimp/gimp.h
index 5a05e4e..eb78f6f 100644
--- a/libgimp/gimp.h
+++ b/libgimp/gimp.h
@@ -44,6 +44,7 @@
#include <libgimp/gimpgradients.h>
#include <libgimp/gimpgradientselect.h>
#include <libgimp/gimpimage.h>
+#include <libgimp/gimpitem.h>
#include <libgimp/gimplayer.h>
#include <libgimp/gimppalette.h>
#include <libgimp/gimppalettes.h>
diff --git a/libgimp/gimpimage.c b/libgimp/gimpimage.c
index 2c324ad..246aed4 100644
--- a/libgimp/gimpimage.c
+++ b/libgimp/gimpimage.c
@@ -101,61 +101,61 @@ gimp_image_get_thumbnail_data (gint32 image_ID,
}
/**
- * gimp_image_get_metadata:
+ * gimp_image_get_attributes:
* @image_ID: The image.
*
- * Returns the image's metadata.
+ * Returns the image's attributes.
*
- * Returns exif/iptc/xmp metadata from the image.
+ * Returns attributes from the image.
*
- * Returns: The exif/ptc/xmp metadata, or %NULL if there is none.
+ * Returns: The attributes, or %NULL if there is none.
*
* Since: GIMP 2.10
**/
-GimpMetadata *
-gimp_image_get_metadata (gint32 image_ID)
+GimpAttributes *
+gimp_image_get_attributes (gint32 image_ID)
{
- GimpMetadata *metadata = NULL;
- gchar *metadata_string;
+ GimpAttributes *attributes = NULL;
+ gchar *attributes_string = NULL;
- metadata_string = _gimp_image_get_metadata (image_ID);
- if (metadata_string)
+ attributes_string = _gimp_image_get_attributes (image_ID);
+ if (attributes_string)
{
- metadata = gimp_metadata_deserialize (metadata_string);
- g_free (metadata_string);
+ attributes = gimp_attributes_deserialize (attributes_string);
+ g_free (attributes_string);
}
- return metadata;
+ return attributes;
}
/**
- * gimp_image_set_metadata:
+ * gimp_image_set_attributes:
* @image_ID: The image.
- * @metadata: The exif/ptc/xmp metadata.
+ * @metadata: The GimpAttributes object.
*
- * Set the image's metadata.
+ * Set the image's attributes.
*
- * Sets exif/iptc/xmp metadata on the image, or deletes it if
- * @metadata is %NULL.
+ * Sets attributes on the image, or deletes it if
+ * @attributes is %NULL.
*
* Returns: TRUE on success.
*
* Since: GIMP 2.10
**/
gboolean
-gimp_image_set_metadata (gint32 image_ID,
- GimpMetadata *metadata)
+gimp_image_set_attributes (gint32 image_ID,
+ GimpAttributes *attributes)
{
- gchar *metadata_string = NULL;
+ gchar *attributes_string = NULL;
gboolean success;
- if (metadata)
- metadata_string = gimp_metadata_serialize (metadata);
+ if (attributes)
+ attributes_string = gimp_attributes_serialize (attributes);
- success = _gimp_image_set_metadata (image_ID, metadata_string);
+ success = _gimp_image_set_attributes (image_ID, attributes_string);
- if (metadata_string)
- g_free (metadata_string);
+ if (attributes_string)
+ g_free (attributes_string);
return success;
}
diff --git a/libgimp/gimpimage.h b/libgimp/gimpimage.h
index 376b033..4995154 100644
--- a/libgimp/gimpimage.h
+++ b/libgimp/gimpimage.h
@@ -44,6 +44,11 @@ guchar * gimp_image_get_thumbnail_data (gint32 image_ID,
GimpMetadata * gimp_image_get_metadata (gint32 image_ID);
gboolean gimp_image_set_metadata (gint32 image_ID,
GimpMetadata *metadata);
+
+
+gboolean gimp_image_set_attributes (gint32 image_ID,
+ GimpAttributes *attributes);
+GimpAttributes *gimp_image_get_attributes (gint32 image_ID);
GIMP_DEPRECATED_FOR(gimp_image_get_colormap)
guchar * gimp_image_get_cmap (gint32 image_ID,
diff --git a/libgimp/gimpimage_pdb.c b/libgimp/gimpimage_pdb.c
index 638f65a..0428f3e 100644
--- a/libgimp/gimpimage_pdb.c
+++ b/libgimp/gimpimage_pdb.c
@@ -1800,65 +1800,65 @@ _gimp_image_set_colormap (gint32 image_ID,
}
/**
- * _gimp_image_get_metadata:
+ * _gimp_image_set_attributes:
* @image_ID: The image.
+ * @attributes_string: The attributes as a xml string.
*
- * Returns the image's metadata.
+ * Set the image's attributes.
*
- * Returns exif/iptc/xmp metadata from the image.
+ * Sets attributes on the image.
*
- * Returns: The exif/ptc/xmp metadata as a string.
+ * Returns: TRUE on success.
**/
-gchar *
-_gimp_image_get_metadata (gint32 image_ID)
+gboolean
+_gimp_image_set_attributes (gint32 image_ID,
+ const gchar *attributes_string)
{
GimpParam *return_vals;
gint nreturn_vals;
- gchar *metadata_string = NULL;
+ gboolean success = TRUE;
- return_vals = gimp_run_procedure ("gimp-image-get-metadata",
+ return_vals = gimp_run_procedure ("gimp-image-set-attributes",
&nreturn_vals,
GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_STRING, attributes_string,
GIMP_PDB_END);
- if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
- metadata_string = g_strdup (return_vals[1].data.d_string);
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
gimp_destroy_params (return_vals, nreturn_vals);
- return metadata_string;
+ return success;
}
/**
- * _gimp_image_set_metadata:
+ * _gimp_image_get_attributes:
* @image_ID: The image.
- * @metadata_string: The exif/ptc/xmp metadata as a string.
*
- * Set the image's metadata.
+ * Returns the image's attributes.
*
- * Sets exif/iptc/xmp metadata on the image.
+ * Returns attributes from the image.
*
- * Returns: TRUE on success.
+ * Returns: The attributes as a xml string.
**/
-gboolean
-_gimp_image_set_metadata (gint32 image_ID,
- const gchar *metadata_string)
+gchar *
+_gimp_image_get_attributes (gint32 image_ID)
{
GimpParam *return_vals;
gint nreturn_vals;
- gboolean success = TRUE;
+ gchar *attributes_string = NULL;
- return_vals = gimp_run_procedure ("gimp-image-set-metadata",
+ return_vals = gimp_run_procedure ("gimp-image-get-attributes",
&nreturn_vals,
GIMP_PDB_IMAGE, image_ID,
- GIMP_PDB_STRING, metadata_string,
GIMP_PDB_END);
- success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ attributes_string = g_strdup (return_vals[1].data.d_string);
gimp_destroy_params (return_vals, nreturn_vals);
- return success;
+ return attributes_string;
}
/**
diff --git a/libgimp/gimpimage_pdb.h b/libgimp/gimpimage_pdb.h
index e5a2466..642edb2 100644
--- a/libgimp/gimpimage_pdb.h
+++ b/libgimp/gimpimage_pdb.h
@@ -148,9 +148,9 @@ G_GNUC_INTERNAL guint8* _gimp_image_get_colormap (gint32
G_GNUC_INTERNAL gboolean _gimp_image_set_colormap (gint32 image_ID,
gint num_bytes,
const guint8 *colormap);
-G_GNUC_INTERNAL gchar* _gimp_image_get_metadata (gint32 image_ID);
-G_GNUC_INTERNAL gboolean _gimp_image_set_metadata (gint32 image_ID,
- const gchar *metadata_string);
+G_GNUC_INTERNAL gboolean _gimp_image_set_attributes (gint32 image_ID,
+ const gchar *attributes_string);
+G_GNUC_INTERNAL gchar* _gimp_image_get_attributes (gint32 image_ID);
gboolean gimp_image_clean_all (gint32 image_ID);
gboolean gimp_image_is_dirty (gint32 image_ID);
G_GNUC_INTERNAL gboolean _gimp_image_thumbnail (gint32 image_ID,
diff --git a/libgimp/gimpitem.c b/libgimp/gimpitem.c
new file mode 100644
index 0000000..60a3ea6
--- /dev/null
+++ b/libgimp/gimpitem.c
@@ -0,0 +1,86 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpitem.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+#include "gimpitem.h"
+
+
+/**
+ * gimp_item_get_attributes:
+ * @item_ID: The item.
+ *
+ * Returns the item's attributes.
+ *
+ * Returns attributes from the item.
+ *
+ * Returns: The attributes, or %NULL if there is none.
+ *
+ * Since: GIMP 2.10
+ **/
+GimpAttributes *
+gimp_item_get_attributes (gint32 item_ID)
+{
+ GimpAttributes *attributes = NULL;
+ gchar *attributes_string;
+
+ attributes_string = _gimp_item_get_attributes (item_ID);
+ if (attributes_string)
+ {
+ attributes = gimp_attributes_deserialize (attributes_string);
+ g_free (attributes_string);
+ }
+
+ return attributes;
+}
+
+/**
+ * gimp_item_set_attributes:
+ * @item_ID: The item.
+ * @metadata: The GimpAttributes object.
+ *
+ * Set the item's attributes.
+ *
+ * Sets attributes on the item, or deletes it if
+ * @attributes is %NULL.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: GIMP 2.10
+ **/
+gboolean
+gimp_item_set_attributes (gint32 item_ID,
+ GimpAttributes *attributes)
+{
+ gchar *attributes_string = NULL;
+ gboolean success;
+
+ if (attributes)
+ attributes_string = gimp_attributes_serialize (attributes);
+
+ success = _gimp_item_set_attributes (item_ID, attributes_string);
+
+ if (attributes_string)
+ g_free (attributes_string);
+
+ return success;
+}
+
diff --git a/libgimp/gimpitem.h b/libgimp/gimpitem.h
new file mode 100644
index 0000000..cfbc7dc
--- /dev/null
+++ b/libgimp/gimpitem.h
@@ -0,0 +1,40 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpitem.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_ITEM_H__
+#define __GIMP_ITEM_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+gboolean gimp_item_set_attributes (gint32 item_ID,
+ GimpAttributes *attributes);
+GimpAttributes *gimp_item_get_attributes (gint32 item_ID);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_ITEM_H__ */
diff --git a/libgimp/gimpitem_pdb.c b/libgimp/gimpitem_pdb.c
index c0f5939..09ba1eb 100644
--- a/libgimp/gimpitem_pdb.c
+++ b/libgimp/gimpitem_pdb.c
@@ -869,6 +869,68 @@ gimp_item_set_tattoo (gint32 item_ID,
}
/**
+ * _gimp_item_get_attributes:
+ * @item_ID: The item.
+ *
+ * Returns the item's attributes.
+ *
+ * Returns attributes from the item.
+ *
+ * Returns: The attributes as a xml string.
+ **/
+gchar *
+_gimp_item_get_attributes (gint32 item_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *attributes_string = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-item-get-attributes",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ attributes_string = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return attributes_string;
+}
+
+/**
+ * _gimp_item_set_attributes:
+ * @item_ID: The item.
+ * @attributes_string: The attributes as a xml string.
+ *
+ * Set the item's attributes.
+ *
+ * Sets attributes on the item.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_item_set_attributes (gint32 item_ID,
+ const gchar *attributes_string)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-item-set-attributes",
+ &nreturn_vals,
+ GIMP_PDB_ITEM, item_ID,
+ GIMP_PDB_STRING, attributes_string,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
+/**
* gimp_item_attach_parasite:
* @item_ID: The item.
* @parasite: The parasite to attach to the item.
diff --git a/libgimp/gimpitem_pdb.h b/libgimp/gimpitem_pdb.h
index c564f0f..8508f43 100644
--- a/libgimp/gimpitem_pdb.h
+++ b/libgimp/gimpitem_pdb.h
@@ -32,46 +32,49 @@ G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
-gboolean gimp_item_is_valid (gint32 item_ID);
-gint32 gimp_item_get_image (gint32 item_ID);
-gboolean gimp_item_delete (gint32 item_ID);
-gboolean gimp_item_is_drawable (gint32 item_ID);
-gboolean gimp_item_is_layer (gint32 item_ID);
-gboolean gimp_item_is_text_layer (gint32 item_ID);
-gboolean gimp_item_is_channel (gint32 item_ID);
-gboolean gimp_item_is_layer_mask (gint32 item_ID);
-gboolean gimp_item_is_selection (gint32 item_ID);
-gboolean gimp_item_is_vectors (gint32 item_ID);
-gboolean gimp_item_is_group (gint32 item_ID);
-gint32 gimp_item_get_parent (gint32 item_ID);
-gint* gimp_item_get_children (gint32 item_ID,
- gint *num_children);
-gchar* gimp_item_get_name (gint32 item_ID);
-gboolean gimp_item_set_name (gint32 item_ID,
- const gchar *name);
-gboolean gimp_item_get_visible (gint32 item_ID);
-gboolean gimp_item_set_visible (gint32 item_ID,
- gboolean visible);
-gboolean gimp_item_get_linked (gint32 item_ID);
-gboolean gimp_item_set_linked (gint32 item_ID,
- gboolean linked);
-gboolean gimp_item_get_lock_content (gint32 item_ID);
-gboolean gimp_item_set_lock_content (gint32 item_ID,
- gboolean lock_content);
-gboolean gimp_item_get_lock_position (gint32 item_ID);
-gboolean gimp_item_set_lock_position (gint32 item_ID,
- gboolean lock_position);
-gint gimp_item_get_tattoo (gint32 item_ID);
-gboolean gimp_item_set_tattoo (gint32 item_ID,
- gint tattoo);
-gboolean gimp_item_attach_parasite (gint32 item_ID,
- const GimpParasite *parasite);
-gboolean gimp_item_detach_parasite (gint32 item_ID,
- const gchar *name);
-GimpParasite* gimp_item_get_parasite (gint32 item_ID,
- const gchar *name);
-gchar** gimp_item_get_parasite_list (gint32 item_ID,
- gint *num_parasites);
+gboolean gimp_item_is_valid (gint32 item_ID);
+gint32 gimp_item_get_image (gint32 item_ID);
+gboolean gimp_item_delete (gint32 item_ID);
+gboolean gimp_item_is_drawable (gint32 item_ID);
+gboolean gimp_item_is_layer (gint32 item_ID);
+gboolean gimp_item_is_text_layer (gint32 item_ID);
+gboolean gimp_item_is_channel (gint32 item_ID);
+gboolean gimp_item_is_layer_mask (gint32 item_ID);
+gboolean gimp_item_is_selection (gint32 item_ID);
+gboolean gimp_item_is_vectors (gint32 item_ID);
+gboolean gimp_item_is_group (gint32 item_ID);
+gint32 gimp_item_get_parent (gint32 item_ID);
+gint* gimp_item_get_children (gint32 item_ID,
+ gint *num_children);
+gchar* gimp_item_get_name (gint32 item_ID);
+gboolean gimp_item_set_name (gint32 item_ID,
+ const gchar *name);
+gboolean gimp_item_get_visible (gint32 item_ID);
+gboolean gimp_item_set_visible (gint32 item_ID,
+ gboolean visible);
+gboolean gimp_item_get_linked (gint32 item_ID);
+gboolean gimp_item_set_linked (gint32 item_ID,
+ gboolean linked);
+gboolean gimp_item_get_lock_content (gint32 item_ID);
+gboolean gimp_item_set_lock_content (gint32 item_ID,
+ gboolean lock_content);
+gboolean gimp_item_get_lock_position (gint32 item_ID);
+gboolean gimp_item_set_lock_position (gint32 item_ID,
+ gboolean lock_position);
+gint gimp_item_get_tattoo (gint32 item_ID);
+gboolean gimp_item_set_tattoo (gint32 item_ID,
+ gint tattoo);
+G_GNUC_INTERNAL gchar* _gimp_item_get_attributes (gint32 item_ID);
+G_GNUC_INTERNAL gboolean _gimp_item_set_attributes (gint32 item_ID,
+ const gchar *attributes_string);
+gboolean gimp_item_attach_parasite (gint32 item_ID,
+ const GimpParasite *parasite);
+gboolean gimp_item_detach_parasite (gint32 item_ID,
+ const gchar *name);
+GimpParasite* gimp_item_get_parasite (gint32 item_ID,
+ const gchar *name);
+gchar** gimp_item_get_parasite_list (gint32 item_ID,
+ gint *num_parasites);
G_END_DECLS
diff --git a/libgimp/gimpmetadata.c b/libgimp/gimpmetadata.c
index b3dd0ea..3e61fd1 100644
--- a/libgimp/gimpmetadata.c
+++ b/libgimp/gimpmetadata.c
@@ -28,6 +28,7 @@
#include "gimp.h"
#include "gimpui.h"
#include "gimpmetadata.h"
+#include "libgimpbase/gimpbase.h"
#include "libgimp-intl.h"
@@ -44,7 +45,6 @@ static gboolean gimp_image_metadata_rotate_dialog (gint32 image_I
GExiv2Orientation orientation,
const gchar *parasite_name);
-
/* public functions */
/**
@@ -122,60 +122,78 @@ gimp_image_metadata_load_prepare (gint32 image_ID,
*/
void
gimp_image_metadata_load_finish (gint32 image_ID,
+ gint32 layer_ID,
const gchar *mime_type,
GimpMetadata *metadata,
GimpMetadataLoadFlags flags,
gboolean interactive)
{
+ GimpAttributes *attributes = NULL;
+
g_return_if_fail (image_ID > 0);
g_return_if_fail (mime_type != NULL);
g_return_if_fail (GEXIV2_IS_METADATA (metadata));
- if (flags & GIMP_METADATA_LOAD_COMMENT)
+ if (flags & GIMP_METADATA_LOAD_ORIENTATION)
{
- gchar *comment;
+ gimp_image_metadata_rotate_query (image_ID, mime_type,
+ metadata, interactive);
+ }
- comment = gexiv2_metadata_get_tag_interpreted_string (metadata,
- "Exif.Photo.UserComment");
- if (! comment)
- comment = gexiv2_metadata_get_tag_interpreted_string (metadata,
- "Exif.Image.ImageDescription");
+ attributes = gimp_attributes_from_metadata (attributes, metadata);
- if (comment)
+ if (attributes)
+ {
+ if (flags & GIMP_METADATA_LOAD_COMMENT)
{
- GimpParasite *parasite;
+ GimpAttribute *attribute = NULL;
+ gchar *comment = NULL;
+
+ attribute = gimp_attributes_get_attribute (attributes, "Exif.Photo.UserComment");
+ if (attribute)
+ comment = gimp_attribute_get_string (attribute);
+ else
+ {
+ attribute = gimp_attributes_get_attribute (attributes, "Exif.Image.ImageDescription");
+ if (attribute)
+ comment = gimp_attribute_get_string (attribute);
+ }
- parasite = gimp_parasite_new ("gimp-comment",
- GIMP_PARASITE_PERSISTENT,
- strlen (comment) + 1,
- comment);
- g_free (comment);
+ if (comment)
+ {
+ GimpParasite *parasite;
- gimp_image_attach_parasite (image_ID, parasite);
- gimp_parasite_free (parasite);
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment) + 1,
+ comment);
+ g_free (comment);
+
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
}
- }
+ if (flags & GIMP_METADATA_LOAD_RESOLUTION)
+ {
+ gdouble xres;
+ gdouble yres;
+ GimpUnit unit;
- if (flags & GIMP_METADATA_LOAD_RESOLUTION)
- {
- gdouble xres;
- gdouble yres;
- GimpUnit unit;
+ if (gimp_attributes_get_resolution (attributes, &xres, &yres, &unit))
+ {
+ gimp_image_set_resolution (image_ID, xres, yres);
+ gimp_image_set_unit (image_ID, unit);
+ }
+ }
- if (gimp_metadata_get_resolution (metadata, &xres, &yres, &unit))
+ gimp_image_set_attributes (image_ID, attributes);
+ if (layer_ID > 0)
{
- gimp_image_set_resolution (image_ID, xres, yres);
- gimp_image_set_unit (image_ID, unit);
+ gimp_item_set_attributes (layer_ID, attributes);
}
- }
+ g_object_unref (attributes);
- if (flags & GIMP_METADATA_LOAD_ORIENTATION)
- {
- gimp_image_metadata_rotate_query (image_ID, mime_type,
- metadata, interactive);
}
-
- gimp_image_set_metadata (image_ID, metadata);
}
/**
@@ -197,12 +215,12 @@ gimp_image_metadata_load_finish (gint32 image_ID,
*
* Since: GIMP 2.10
*/
-GimpMetadata *
+GimpAttributes *
gimp_image_metadata_save_prepare (gint32 image_ID,
const gchar *mime_type,
GimpMetadataSaveFlags *suggested_flags)
{
- GimpMetadata *metadata;
+ GimpAttributes *attributes = NULL;
g_return_val_if_fail (image_ID > 0, NULL);
g_return_val_if_fail (mime_type != NULL, NULL);
@@ -210,128 +228,27 @@ gimp_image_metadata_save_prepare (gint32 image_ID,
*suggested_flags = GIMP_METADATA_SAVE_ALL;
- metadata = gimp_image_get_metadata (image_ID);
+ attributes = gimp_image_get_attributes (image_ID);
- if (metadata)
+ if (attributes)
{
- GDateTime *datetime;
- const GimpParasite *comment_parasite;
- const gchar *comment = NULL;
- gint image_width;
- gint image_height;
- gdouble xres;
- gdouble yres;
- gchar buffer[32];
-
- image_width = gimp_image_width (image_ID);
- image_height = gimp_image_height (image_ID);
-
- datetime = g_date_time_new_now_local ();
-
- comment_parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
- if (comment_parasite)
- comment = gimp_parasite_data (comment_parasite);
-
/* Exif */
- if (! gexiv2_metadata_has_exif (metadata))
+ if (! gimp_attributes_has_tag_type (attributes, TAG_EXIF))
*suggested_flags &= ~GIMP_METADATA_SAVE_EXIF;
- if (comment)
- {
- gexiv2_metadata_set_tag_string (metadata,
- "Exif.Photo.UserComment",
- comment);
- gexiv2_metadata_set_tag_string (metadata,
- "Exif.Image.ImageDescription",
- comment);
- }
-
- g_snprintf (buffer, sizeof (buffer),
- "%d:%02d:%02d %02d:%02d:%02d",
- g_date_time_get_year (datetime),
- g_date_time_get_month (datetime),
- g_date_time_get_day_of_month (datetime),
- g_date_time_get_hour (datetime),
- g_date_time_get_minute (datetime),
- g_date_time_get_second (datetime));
- gexiv2_metadata_set_tag_string (metadata,
- "Exif.Image.DateTime",
- buffer);
-
- gexiv2_metadata_set_tag_string (metadata,
- "Exif.Image.Software",
- PACKAGE_STRING);
-
- gimp_metadata_set_pixel_size (metadata,
- image_width, image_height);
-
- gimp_image_get_resolution (image_ID, &xres, &yres);
- gimp_metadata_set_resolution (metadata, xres, yres,
- gimp_image_get_unit (image_ID));
/* XMP */
- if (! gexiv2_metadata_has_xmp (metadata))
+ if (! gimp_attributes_has_tag_type (attributes, TAG_XMP))
*suggested_flags &= ~GIMP_METADATA_SAVE_XMP;
- gexiv2_metadata_set_tag_string (metadata,
- "Xmp.dc.Format",
- mime_type);
-
- if (! g_strcmp0 (mime_type, "image/tiff"))
- {
- /* TIFF specific XMP data */
-
- g_snprintf (buffer, sizeof (buffer), "%d", image_width);
- gexiv2_metadata_set_tag_string (metadata,
- "Xmp.tiff.ImageWidth",
- buffer);
-
- g_snprintf (buffer, sizeof (buffer), "%d", image_height);
- gexiv2_metadata_set_tag_string (metadata,
- "Xmp.tiff.ImageLength",
- buffer);
-
- g_snprintf (buffer, sizeof (buffer),
- "%d:%02d:%02d %02d:%02d:%02d",
- g_date_time_get_year (datetime),
- g_date_time_get_month (datetime),
- g_date_time_get_day_of_month (datetime),
- g_date_time_get_hour (datetime),
- g_date_time_get_minute (datetime),
- g_date_time_get_second (datetime));
- gexiv2_metadata_set_tag_string (metadata,
- "Xmp.tiff.DateTime",
- buffer);
- }
/* IPTC */
- if (! gexiv2_metadata_has_iptc (metadata))
+ if (! gimp_attributes_has_tag_type (attributes, TAG_IPTC))
*suggested_flags &= ~GIMP_METADATA_SAVE_IPTC;
- g_snprintf (buffer, sizeof (buffer),
- "%d-%d-%d",
- g_date_time_get_year (datetime),
- g_date_time_get_month (datetime),
- g_date_time_get_day_of_month (datetime));
- gexiv2_metadata_set_tag_string (metadata,
- "Iptc.Application2.DateCreated",
- buffer);
-
- g_snprintf (buffer, sizeof (buffer),
- "%02d:%02d:%02d-%02d:%02d",
- g_date_time_get_hour (datetime),
- g_date_time_get_minute (datetime),
- g_date_time_get_second (datetime),
- g_date_time_get_hour (datetime),
- g_date_time_get_minute (datetime));
- gexiv2_metadata_set_tag_string (metadata,
- "Iptc.Application2.TimeCreated",
- buffer);
-
- g_date_time_unref (datetime);
/* Thumbnail */
@@ -339,7 +256,7 @@ gimp_image_metadata_save_prepare (gint32 image_ID,
*suggested_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
}
- return metadata;
+ return attributes;
}
/**
@@ -362,22 +279,25 @@ gimp_image_metadata_save_prepare (gint32 image_ID,
gboolean
gimp_image_metadata_save_finish (gint32 image_ID,
const gchar *mime_type,
- GimpMetadata *metadata,
+ GimpAttributes *attributes,
GimpMetadataSaveFlags flags,
GFile *file,
GError **error)
{
GExiv2Metadata *new_metadata;
- gboolean support_exif;
- gboolean support_xmp;
- gboolean support_iptc;
- gchar *value;
gboolean success = FALSE;
- gint i;
+ gchar buffer[32];
+ GDateTime *datetime;
+ const GimpParasite *comment_parasite;
+ const gchar *comment = NULL;
+ gint image_width;
+ gint image_height;
+ gdouble xres;
+ gdouble yres;
g_return_val_if_fail (image_ID > 0, FALSE);
g_return_val_if_fail (mime_type != NULL, FALSE);
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
+ g_return_val_if_fail (attributes != NULL, FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@@ -387,74 +307,138 @@ gimp_image_metadata_save_finish (gint32 image_ID,
GIMP_METADATA_SAVE_THUMBNAIL)))
return TRUE;
+ image_width = gimp_image_width (image_ID);
+ image_height = gimp_image_height (image_ID);
+
+ datetime = g_date_time_new_now_local ();
+
+ comment_parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
+ if (comment_parasite)
+ comment = gimp_parasite_data (comment_parasite);
+
/* read metadata from saved file */
new_metadata = gimp_metadata_load_from_file (file, error);
if (! new_metadata)
return FALSE;
- support_exif = gexiv2_metadata_get_supports_exif (new_metadata);
- support_xmp = gexiv2_metadata_get_supports_xmp (new_metadata);
- support_iptc = gexiv2_metadata_get_supports_iptc (new_metadata);
+ /* Exif */
- if ((flags & GIMP_METADATA_SAVE_EXIF) && support_exif)
+ if (flags & GIMP_METADATA_SAVE_EXIF)
{
- gchar **exif_data = gexiv2_metadata_get_exif_tags (metadata);
- for (i = 0; exif_data[i] != NULL; i++)
+ if (comment)
{
- if (! gexiv2_metadata_has_tag (new_metadata, exif_data[i]) &&
- gimp_metadata_is_tag_supported (exif_data[i], mime_type))
- {
- value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
- gexiv2_metadata_set_tag_string (new_metadata, exif_data[i],
- value);
- g_free (value);
- }
+ gimp_attributes_new_attribute (attributes,
+ "Exif.Photo.UserComment",
+ (gchar *) comment,
+ TYPE_UNICODE);
+ gimp_attributes_new_attribute (attributes,
+ "Exif.Image.ImageDescription",
+ (gchar *) comment,
+ TYPE_ASCII);
}
- g_strfreev (exif_data);
+ g_snprintf (buffer, sizeof (buffer),
+ "%d:%02d:%02d %02d:%02d:%02d",
+ g_date_time_get_year (datetime),
+ g_date_time_get_month (datetime),
+ g_date_time_get_day_of_month (datetime),
+ g_date_time_get_hour (datetime),
+ g_date_time_get_minute (datetime),
+ g_date_time_get_second (datetime));
+ gimp_attributes_new_attribute (attributes,
+ "Exif.Image.DateTime",
+ buffer,
+ TYPE_ASCII);
+
+ gimp_attributes_new_attribute (attributes,
+ "Exif.Image.Software",
+ buffer,
+ TYPE_ASCII);
+
+ gimp_attributes_set_pixel_size (attributes,
+ image_width, image_height);
+
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+ gimp_attributes_set_resolution (attributes, xres, yres,
+ gimp_image_get_unit (image_ID));
}
- if ((flags & GIMP_METADATA_SAVE_XMP) && support_xmp)
+ /* XMP */
+
+ if (flags & GIMP_METADATA_SAVE_XMP)
{
- gchar **xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+ gimp_attributes_new_attribute (attributes,
+ "Xmp.dc.Format",
+ (gchar *) mime_type,
+ TYPE_ASCII);
- for (i = 0; xmp_data[i] != NULL; i++)
+ if (! g_strcmp0 (mime_type, "image/tiff"))
{
- if (! gexiv2_metadata_has_tag (new_metadata, xmp_data[i]) &&
- gimp_metadata_is_tag_supported (xmp_data[i], mime_type))
- {
- value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
- gexiv2_metadata_set_tag_string (new_metadata, xmp_data[i],
- value);
- g_free (value);
- }
- }
+ /* TIFF specific XMP data */
+
+ g_snprintf (buffer, sizeof (buffer), "%d", image_width);
+ gimp_attributes_new_attribute (attributes,
+ "Xmp.tiff.ImageWidth",
+ buffer,
+ TYPE_ASCII);
- g_strfreev (xmp_data);
+ g_snprintf (buffer, sizeof (buffer), "%d", image_height);
+ gimp_attributes_new_attribute (attributes,
+ "Xmp.tiff.ImageLength",
+ buffer,
+ TYPE_ASCII);
+
+ g_snprintf (buffer, sizeof (buffer),
+ "%d:%02d:%02d %02d:%02d:%02d",
+ g_date_time_get_year (datetime),
+ g_date_time_get_month (datetime),
+ g_date_time_get_day_of_month (datetime),
+ g_date_time_get_hour (datetime),
+ g_date_time_get_minute (datetime),
+ g_date_time_get_second (datetime));
+ gimp_attributes_new_attribute (attributes,
+ "Xmp.tiff.DateTime",
+ buffer,
+ TYPE_ASCII);
+ }
}
- if ((flags & GIMP_METADATA_SAVE_IPTC) && support_iptc)
+ /* IPTC */
+
+ if (flags & GIMP_METADATA_SAVE_IPTC)
{
- gchar **iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
- for (i = 0; iptc_data[i] != NULL; i++)
- {
- if (! gexiv2_metadata_has_tag (new_metadata, iptc_data[i]) &&
- gimp_metadata_is_tag_supported (iptc_data[i], mime_type))
- {
- value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
- gexiv2_metadata_set_tag_string (new_metadata, iptc_data[i],
- value);
- g_free (value);
- }
- }
+ g_snprintf (buffer, sizeof (buffer),
+ "%d-%d-%d",
+ g_date_time_get_year (datetime),
+ g_date_time_get_month (datetime),
+ g_date_time_get_day_of_month (datetime));
+ gimp_attributes_new_attribute (attributes,
+ "Iptc.Application2.DateCreated",
+ buffer,
+ TYPE_ASCII);
+
+ g_snprintf (buffer, sizeof (buffer),
+ "%02d:%02d:%02d",
+ g_date_time_get_hour (datetime),
+ g_date_time_get_minute (datetime),
+ g_date_time_get_second (datetime));
+ gimp_attributes_new_attribute (attributes,
+ "Iptc.Application2.TimeCreated",
+ buffer,
+ TYPE_ASCII);
+
+ g_date_time_unref (datetime);
- g_strfreev (iptc_data);
}
- if (flags & GIMP_METADATA_SAVE_THUMBNAIL)
+ gimp_attributes_to_metadata (attributes, new_metadata, mime_type);
+
+ /* Thumbnail */
+
+ if ((flags & GIMP_METADATA_SAVE_THUMBNAIL) && g_strcmp0 (mime_type, "image/tiff"))
{
GdkPixbuf *thumb_pixbuf;
gchar *thumb_buffer;
diff --git a/libgimp/gimpmetadata.h b/libgimp/gimpmetadata.h
index 7cd4674..b7261e3 100644
--- a/libgimp/gimpmetadata.h
+++ b/libgimp/gimpmetadata.h
@@ -35,17 +35,18 @@ GimpMetadata * gimp_image_metadata_load_prepare (gint32 image_ID
GFile *file,
GError **error);
void gimp_image_metadata_load_finish (gint32 image_ID,
+ gint32 layer_ID,
const gchar *mime_type,
GimpMetadata *metadata,
GimpMetadataLoadFlags flags,
gboolean interactive);
-GimpMetadata * gimp_image_metadata_save_prepare (gint32 image_ID,
+GimpAttributes * gimp_image_metadata_save_prepare (gint32 image_ID,
const gchar *mime_type,
GimpMetadataSaveFlags *suggested_flags);
gboolean gimp_image_metadata_save_finish (gint32 image_ID,
const gchar *mime_type,
- GimpMetadata *metadata,
+ GimpAttributes *attributes,
GimpMetadataSaveFlags flags,
GFile *file,
GError **error);
diff --git a/libgimp/gimpviewable.c b/libgimp/gimpviewable.c
new file mode 100644
index 0000000..47b9e5c
--- /dev/null
+++ b/libgimp/gimpviewable.c
@@ -0,0 +1,84 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
+ *
+ * gimpimage.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gimp.h"
+#include "gimpimage.h"
+
+/**
+ * gimp_viewable_get_attributes:
+ * @object_ID: The viewable object.
+ *
+ * Returns the viewable's attributes.
+ *
+ * Returns attributes from the viewable.
+ *
+ * Returns: The attributes, or %NULL if there is none.
+ *
+ * Since: GIMP 2.10
+ **/
+GimpAttributes *
+gimp_viewable_get_attributes (gint32 object_ID)
+{
+ GimpAttributes *attributes = NULL;
+ gchar *attributes_string;
+
+ attributes_string = _gimp_viewable_get_attributes (object_ID);
+ if (attributes_string)
+ {
+ attributes = gimp_attributes_deserialize (NULL, attributes_string);
+ g_free (attributes_string);
+ }
+
+ return attributes;
+}
+
+/**
+ * gimp_viewable_set_attributes:
+ * @object_ID: The viewable object.
+ * @attributes: The GimpAttributes object.
+ *
+ * Set the viewable's attributes.
+ *
+ * Sets attributes on the viewable, or deletes it if
+ * @attributes is %NULL.
+ *
+ * Returns: TRUE on success.
+ *
+ * Since: GIMP 2.10
+ **/
+gboolean
+gimp_viewable_set_attributes (gint32 object_ID,
+ GimpAttributes *attributes)
+{
+ gchar *attributes_string = NULL;
+ gboolean success;
+
+ if (attributes)
+ attributes_string = gimp_attributes_serialize (attributes);
+
+ success = _gimp_viewable_set_attributes (object_ID, attributes_string);
+
+ if (attributes_string)
+ g_free (attributes_string);
+
+ return success;
+}
diff --git a/libgimp/gimpviewable_pdb.c b/libgimp/gimpviewable_pdb.c
new file mode 100644
index 0000000..9d1fddc
--- /dev/null
+++ b/libgimp/gimpviewable_pdb.c
@@ -0,0 +1,100 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpimage_pdb.c
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This file is auto-generated by pdbgen.pl */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimpimage
+ * @title: gimpimage
+ * @short_description: Operations on complete images.
+ *
+ * Operations on complete images: creation, resizing/rescaling, and
+ * operations involving multiple layers.
+ **/
+
+/**
+ * _gimp_viewable_get_attributes:
+ * @object_ID: The viewable object.
+ *
+ * Returns the viewable's attributes.
+ *
+ * Returns attributes from the viewable.
+ *
+ * Returns: The attributes as a xml string.
+ **/
+gchar *
+_gimp_viewable_get_attributes (gint32 object_ID)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gchar *attributes_string = NULL;
+
+ return_vals = gimp_run_procedure ("gimp-viewable-get-attributes",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, object_ID,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ attributes_string = g_strdup (return_vals[1].data.d_string);
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return attributes_string;
+}
+
+/**
+ * _gimp_viewable_set_attributes:
+ * @object_ID: The viewable object.
+ * @attributes_string: The attributes as a string.
+ *
+ * Set the viewable's attributes.
+ *
+ * Sets attributes on the viewable.
+ *
+ * Returns: TRUE on success.
+ **/
+gboolean
+_gimp_image_set_attributes (gint32 object_ID,
+ const gchar *attributes_string)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gboolean success = TRUE;
+
+ return_vals = gimp_run_procedure ("gimp-viewable-set-attributes",
+ &nreturn_vals,
+ GIMP_PDB_IMAGE, object_ID,
+ GIMP_PDB_STRING, attributes_string,
+ GIMP_PDB_END);
+
+ success = return_vals[0].data.d_status == GIMP_PDB_SUCCESS;
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+
+ return success;
+}
+
diff --git a/libgimpbase/Makefile.am b/libgimpbase/Makefile.am
index c97e68f..bf60920 100644
--- a/libgimpbase/Makefile.am
+++ b/libgimpbase/Makefile.am
@@ -90,6 +90,12 @@ libgimpbase_sources = \
gimpparam.h \
gimpversion.h \
\
+ gimpattribute.c \
+ gimpattribute.h \
+ gimpattributes.c \
+ gimpattributes.h \
+ gimpattributes-image.c \
+ gimpattributes-image.h \
gimpbase-private.c \
gimpbase-private.h \
gimpchecks.c \
@@ -110,6 +116,8 @@ libgimpbase_sources = \
gimpparasiteio.h \
gimpprotocol.c \
gimpprotocol.h \
+ gimprational.c \
+ gimprational.h \
gimprectangle.c \
gimprectangle.h \
gimpreloc.c \
@@ -143,6 +151,9 @@ libgimpbaseinclude_HEADERS = \
gimpparam.h \
gimpversion.h \
\
+ gimpattribute.h \
+ gimpattributes.h \
+ gimpattributes-image.h \
gimpchecks.h \
gimpdatafiles.h \
gimpenv.h \
@@ -150,6 +161,7 @@ libgimpbaseinclude_HEADERS = \
gimpmetadata.h \
gimpparasite.h \
gimpparasiteio.h \
+ gimprational.h \
gimprectangle.h \
gimpsignal.h \
gimpunit.h \
diff --git a/libgimpbase/gimpattribute.c b/libgimpbase/gimpattribute.c
new file mode 100644
index 0000000..c1645d0
--- /dev/null
+++ b/libgimpbase/gimpattribute.c
@@ -0,0 +1,1858 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpattribute.c
+ * Copyright (C) 2014 Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+
+#ifdef G_OS_WIN32
+#endif
+
+#include "gimpbasetypes.h"
+#include "gimpattribute.h"
+#include "gimprational.h"
+
+#define _g_free0(var) (var = (g_free (var), NULL))
+#define _g_regex_unref0(var) ((var == NULL) ? NULL : (var = (g_regex_unref (var), NULL)))
+#define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL)))
+
+typedef struct _TagTime TagTime;
+typedef struct _TagDate TagDate;
+
+struct _TagTime {
+ int tag_time_sec;
+ int tag_time_min;
+ int tag_time_hour;
+ int tag_tz_hour;
+ int tag_tz_min;
+ };
+
+struct _TagDate {
+ int tag_year;
+ int tag_month;
+ int tag_day;
+ };
+
+
+typedef struct _GimpAttributeClass GimpAttributeClass;
+typedef struct _GimpAttributePrivate GimpAttributePrivate;
+
+struct _GimpAttribute {
+ GObject parent_instance;
+ GimpAttributePrivate *priv;
+};
+
+struct _GimpAttributeClass {
+ GObjectClass parent_class;
+};
+
+struct _GimpAttributePrivate {
+ gchar *name;
+
+ gchar *tag_value;
+ gchar *interpreted_value;
+
+ gchar *exif_type;
+
+ GimpAttributeValueType value_type;
+ GimpAttributeTagType tag_type;
+
+ gchar *attribute_type;
+ gchar *attribute_ifd;
+ gchar *attribute_tag;
+ gboolean is_new_name_space;
+
+ gboolean has_structure;
+ GimpAttributeStructureType structure_type;
+ GSList *attribute_structure;
+
+};
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_LONG_VALUE,
+ PROP_SLONG_VALUE,
+ PROP_SHORT_VALUE,
+ PROP_SSHORT_VALUE,
+ PROP_DATE_VALUE,
+ PROP_TIME_VALUE,
+ PROP_ASCII_VALUE,
+ PROP_BYTE_VALUE,
+ PROP_MULTIPLE_VALUE,
+ PROP_RATIONAL_VALUE,
+ PROP_SRATIONAL_VALUE
+};
+
+static const gchar *xmp_namespaces[] =
+{
+ "dc",
+ "xmp",
+ "xmpRights",
+ "xmpMM",
+ "xmpBJ",
+ "xmpTPg",
+ "xmpDM",
+ "pdf",
+ "photoshop",
+ "crs",
+ "tiff",
+ "exif",
+ "aux",
+ "plus",
+ "digiKam",
+ "iptc",
+ "iptcExt",
+ "Iptc4xmpCore",
+ "Iptc4xmpExt",
+ "MicrosoftPhoto",
+ "kipi",
+ "mediapro",
+ "expressionmedia",
+ "MP",
+ "MPRI",
+ "MPReg",
+ "mwg-rs"
+};
+
+
+static gpointer gimp_attribute_parent_class = NULL;
+static gint counter = 0;
+
+
+static GimpAttribute* gimp_attribute_construct (GType object_type,
+ const gchar *name);
+static void gimp_attribute_finalize (GObject *obj);
+static void gimp_attribute_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_attribute_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_attribute_set_name (GimpAttribute *attribute,
+ const gchar *value);
+static gboolean get_tag_time (gchar *input,
+ TagTime *tm);
+static gboolean get_tag_date (gchar *input,
+ TagDate *dt);
+static gchar * gimp_attribute_escape_value (gchar *name,
+ gchar *value,
+ gboolean *encoded);
+static gchar* string_replace_str (const gchar *original,
+ const gchar *old_pattern,
+ const gchar *replacement);
+static gint string_index_of (gchar *haystack,
+ gchar *needle,
+ gint start_index);
+static glong string_strnlen (gchar *str,
+ glong maxlen);
+static gchar* string_substring (gchar *attribute,
+ glong offset,
+ glong len);
+
+
+/**
+ * gimp_attribute_new_string:
+ *
+ * @name: a constant #gchar that describes the name of the attribute
+ * @value: a #gchar value, representing a time
+ * @type: a #GimpAttributeTagType
+ *
+ * Creates a new #GimpAttribute object with @name as name and a #gchar @value
+ * as value of type @type.
+ * @value is converted to the correct type.
+ *
+ * Return value: a new @GimpAttribute
+ *
+ * Since: 2.10
+ */
+GimpAttribute*
+gimp_attribute_new_string (const gchar *name,
+ gchar *value,
+ GimpAttributeValueType type)
+{
+ GimpAttribute *attribute = NULL;
+ GimpAttributePrivate *private;
+
+ attribute = gimp_attribute_construct (GIMP_TYPE_ATTRIBUTE, name);
+ if (attribute)
+ {
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ private->tag_value = g_strdup (value);
+ private->value_type = type;
+ }
+
+ return attribute;
+}
+
+/**
+ * gimp_attribute_get_name:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: full name of the #GimpAttribute object
+ * e.g. "Exif.Image.XResolution"
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_name (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gchar *) private->name;
+}
+
+/**
+ * gimp_attribute_get_attribute_type:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: attribute type of the #GimpAttribute object
+ * e.g. "exif", "iptc", ...
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_attribute_type (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gchar *) private->attribute_type;
+}
+
+/**
+ * gimp_attribute_get_attribute_ifd:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: ifd of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_attribute_ifd (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gchar *) private->attribute_ifd;
+}
+
+/**
+ * gimp_attribute_get_attribute_tag:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: tag of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+const gchar*
+gimp_attribute_get_attribute_tag (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gchar *) private->attribute_tag;
+}
+
+/**
+ * gimp_attribute_set_value_type:
+ *
+ * @attribute: a @GimpAttribute
+ * @value_type: a @GimpAttributeValueType
+ *
+ * sets value type of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+void
+gimp_attribute_set_value_type (GimpAttribute* attribute,
+ GimpAttributeValueType value_type)
+{
+ GimpAttributePrivate *private;
+ g_return_if_fail (attribute != NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ private->value_type = value_type;
+}
+
+/**
+ * gimp_attribute_get_value_type:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: value type of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+GimpAttributeValueType
+gimp_attribute_get_value_type (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, TYPE_INVALID);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return private->value_type;
+}
+
+/**
+ * gimp_attribute_get_attribute_structure:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: attribute_structure of the #GimpAttribute object
+ * This is the start value of a xmpBag sequence.
+ *
+ * Since: 2.10
+ */
+GSList *
+gimp_attribute_get_attribute_structure (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, NULL);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ return private->attribute_structure;
+}
+/**
+ * gimp_attribute_is_new_namespace:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: if namespace must be defined or not
+ *
+ * Since: 2.10
+ */
+const gboolean
+gimp_attribute_is_new_namespace (GimpAttribute* attribute)
+{
+ GimpAttributePrivate *private;
+ g_return_val_if_fail (attribute != NULL, FALSE);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ return (const gboolean) private->is_new_name_space;
+}
+
+/**
+ * gimp_attribute_get_value:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: #GValue of the #GimpAttribute object
+ * The type of value is represented by glib-types
+ *
+ * Since: 2.10
+ */
+GValue
+gimp_attribute_get_value (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+ GValue val = G_VALUE_INIT;
+ gchar *value_string;
+
+ g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), val);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ value_string = private->tag_value;
+
+ switch (private->value_type)
+ {
+ case TYPE_LONG:
+ {
+ g_value_init (&val, G_TYPE_ULONG);
+ g_value_set_ulong (&val, (gulong) atol (value_string));
+ }
+ break;
+ case TYPE_SLONG:
+ {
+ g_value_init (&val, G_TYPE_LONG);
+ g_value_set_long (&val, (glong) atol (value_string));
+ }
+ break;
+ case TYPE_SHORT:
+ {
+ g_value_init (&val, G_TYPE_UINT);
+ g_value_set_uint (&val, (guint) atoi (value_string));
+ }
+ break;
+ case TYPE_SSHORT:
+ {
+ g_value_init (&val, G_TYPE_INT);
+ g_value_set_int (&val, (gint) atoi (value_string));
+ }
+ break;
+ case TYPE_DATE:
+ {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value_string);
+ }
+ break;
+ case TYPE_TIME:
+ {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value_string);
+ }
+ break;
+ case TYPE_ASCII:
+ {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value_string);
+ }
+ break;
+ case TYPE_UNICODE:
+ {
+ g_value_init (&val, G_TYPE_STRING);
+ g_value_set_string (&val, value_string);
+ }
+ break;
+ case TYPE_BYTE:
+ {
+ g_value_init (&val, G_TYPE_UCHAR);
+ g_value_set_uchar (&val, value_string[0]);
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ gchar **result;
+
+ g_value_init (&val, G_TYPE_STRV);
+ result = g_strsplit (value_string, "\n", 0);
+ g_value_set_boxed (&val, result);
+ }
+ break;
+ case TYPE_RATIONAL:
+ {
+ gchar **nom;
+ gchar **rats;
+ gint count;
+ gint i;
+ Rational *rval;
+ GArray *rational_array;
+
+ rats = g_strsplit (value_string, " ", -1);
+
+ count = 0;
+ while (rats[count])
+ count++;
+
+ rational_array = g_array_new (TRUE, TRUE, sizeof (RationalValue));
+
+ for (i = 0; i < count; i++)
+ {
+ RationalValue or;
+
+ nom = g_strsplit (rats[i], "/", 2);
+
+ if (nom[0] && nom[1])
+ {
+ or.nom = (guint) atoi (nom [0]);
+ or.denom = (guint) atoi (nom [1]);
+ }
+ else
+ count--;
+
+ rational_array = g_array_append_val (rational_array, or);
+
+ g_strfreev (nom);
+ }
+
+ rval = g_slice_new (Rational);
+
+ rval->rational_array = rational_array;
+ rval->length = count;
+
+ g_value_init (&val, GIMP_TYPE_RATIONAL);
+ g_value_set_boxed (&val, rval);
+
+ g_strfreev (rats);
+ }
+ break;
+ case TYPE_SRATIONAL:
+ {
+ gchar **nom;
+ gchar **srats;
+ gint count;
+ gint i;
+ SRational *srval;
+ GArray *srational_array;
+
+ srats = g_strsplit (value_string, " ", -1);
+
+ count = 0;
+ while (srats[count])
+ count++;
+
+ srational_array = g_array_new (TRUE, TRUE, sizeof (SRationalValue));
+
+ for (i = 0; i < count; i++)
+ {
+ SRationalValue or;
+
+ nom = g_strsplit (srats[i], "/", 2);
+
+ if (nom[0] && nom[1])
+ {
+ or.nom = (gint) atoi (nom [0]);
+ or.denom = (guint) atoi (nom [1]);
+ }
+ else
+ count--;
+
+ srational_array = g_array_append_val (srational_array, or);
+
+ g_strfreev (nom);
+ }
+
+ srval = g_slice_new (SRational);
+
+ srval->srational_array = srational_array;
+ srval->length = count;
+
+ g_value_init (&val, GIMP_TYPE_SRATIONAL);
+ g_value_set_boxed (&val, srval);
+
+ g_strfreev (srats);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+ }
+
+ return val;
+}
+
+/**
+ * gimp_attribute_get_string:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * Return value: newly allocated #gchar string representation of the #GimpAttribute object.
+ * The return is always a valid value according to the #GimpAttributeValueType
+ *
+ * Since: 2.10
+ */
+gchar*
+gimp_attribute_get_string (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+ gchar *val = NULL;
+ TagDate dt = {0};
+ TagTime tm = {0};
+
+ g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ switch (private->value_type)
+ {
+ case TYPE_INVALID:
+ break;
+ case TYPE_LONG:
+ {
+ val = g_strdup_printf ("%lu", (gulong) atol (private->tag_value));
+ }
+ break;
+ case TYPE_SLONG:
+ {
+ val = g_strdup_printf ("%ld", (glong) atol (private->tag_value));
+ }
+ break;
+ case TYPE_FLOAT:
+ {
+ val = g_strdup_printf ("%.5f", (gfloat) atof (private->tag_value));
+ }
+ break;
+ case TYPE_DOUBLE:
+ {
+ val = g_strdup_printf ("%.6f", (gdouble) atof (private->tag_value));
+ }
+ break;
+ case TYPE_SHORT:
+ {
+ val = g_strdup_printf ("%u", (gushort) atoi (private->tag_value));
+ }
+ break;
+ case TYPE_SSHORT:
+ {
+ val = g_strdup_printf ("%d", (gshort) atoi (private->tag_value));
+ }
+ break;
+ case TYPE_DATE:
+ {
+ get_tag_date (private->tag_value, &dt);
+
+ val = g_strdup_printf ("%4d-%02d-%02d", dt.tag_year,
+ dt.tag_month,
+ dt.tag_day);
+ }
+ break;
+ case TYPE_TIME:
+ {
+ gchar *tz_h = NULL;
+ get_tag_time (private->tag_value, &tm);
+
+ tz_h = tm.tag_tz_hour < 0 ?
+ g_strdup_printf ("-%02d", tm.tag_tz_hour) :
+ g_strdup_printf ("+%02d", tm.tag_tz_hour);
+
+ val = g_strdup_printf ("%02d:%02d:%02d%s:%02d", tm.tag_time_hour,
+ tm.tag_time_min,
+ tm.tag_time_sec,
+ tz_h,
+ tm.tag_tz_min);
+ g_free (tz_h);
+ }
+ break;
+ case TYPE_ASCII:
+ {
+ val = g_strdup (private->tag_value);
+ }
+ break;
+ case TYPE_UNICODE:
+ {
+ val = g_strdup (private->tag_value);
+ }
+ break;
+ case TYPE_BYTE:
+ {
+ val = g_strdup_printf ("%c", private->tag_value[0]);
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ val = g_strdup (private->tag_value);
+ }
+ break;
+ case TYPE_RATIONAL:
+ {
+ gint i;
+ GString *string;
+ Rational *rational;
+
+ string_to_rational (private->tag_value, &rational);
+
+ string = g_string_new (NULL);
+
+ for (i = 0; i < rational->length; i++)
+ {
+ RationalValue or;
+
+ or = g_array_index (rational->rational_array, RationalValue, i);
+
+ g_string_append_printf (string, "%u/%u ",
+ or.nom,
+ or.denom);
+ }
+ g_string_truncate (string, string->len-1);
+ val = g_string_free (string, FALSE);
+ }
+ break;
+ case TYPE_SRATIONAL:
+ {
+ gint i;
+ GString *string;
+ SRational *srational;
+
+ string_to_srational (private->tag_value, &srational);
+
+ string = g_string_new (NULL);
+
+ for (i = 0; i < srational->length; i++)
+ {
+ SRationalValue or;
+
+ or = g_array_index (srational->srational_array, SRationalValue, i);
+
+ g_string_append_printf (string, "%u/%u ",
+ or.nom,
+ or.denom);
+ }
+
+ g_string_truncate (string, string->len-1);
+ val = g_string_free (string, FALSE);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+ }
+ return val;
+}
+
+/**
+ * gimp_attribute_get_interpreted_string:
+ *
+ * @attribute: a #GimpAttribute
+ *
+ * returns the interpreted value or the pure string, if no
+ * interpreted value is found
+ *
+ * Since: 2.10
+ */
+const gchar *
+gimp_attribute_get_interpreted_string (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail(GIMP_IS_ATTRIBUTE (attribute), NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ if (private->interpreted_value)
+ return (const gchar *) private->interpreted_value;
+ else
+ return (const gchar *) private->tag_value;
+}
+
+/**
+ * gimp_attribute_set_interpreted_string:
+ *
+ * @attribute: a #GimpAttribute
+ * @value: a constant #gchar
+ *
+ * sets the interpreted string of the #GimpAttribute object to
+ * a @value
+ *
+ * @value can be freed after
+ *
+ * Since: 2.10
+ */
+void
+gimp_attribute_set_interpreted_string (GimpAttribute* attribute, const gchar* value)
+{
+ GimpAttributePrivate *private;
+
+ g_return_if_fail(GIMP_IS_ATTRIBUTE (attribute));
+ g_return_if_fail (value != NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ private->interpreted_value = g_strdup (value);
+}
+
+/**
+ * gimp_attribute_get_tag_type:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ *
+ * Return value: #GimpAttributeTagType
+ * The tag type of the Attribute
+ *
+ * Since: 2.10
+ */
+GimpAttributeTagType
+gimp_attribute_get_tag_type (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, TAG_INVALID);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ return private->tag_type;
+}
+
+void
+gimp_attribute_print (GimpAttribute *attribute)
+{
+ const gchar *tag;
+ const gchar *interpreted;
+ gchar *value;
+
+ g_return_if_fail(GIMP_IS_ATTRIBUTE (attribute));
+
+ tag = gimp_attribute_get_name (attribute);
+ value = gimp_attribute_get_string (attribute);
+ interpreted = gimp_attribute_get_interpreted_string (attribute);
+
+ g_print ("---\n%p\nTag: %s\n\tValue:%s\n\tInterpreted value:%s\n", attribute, tag, value, interpreted);
+
+}
+
+/**
+ * gimp_attribute_get_xml:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * returns the xml representation of the #GimpAttribute
+ * in form:
+ *
+ * <tag name="name" type="GimpAttributeValueType">
+ * <value [encoding=base64]>value</value>
+ * [<interpreted [encoding=base64]>interpreted value</interpreted>]
+ * </tag>
+ *
+ * Return value: #gchar xml representation of the #GimpAttribute object
+ *
+ * Since: 2.10
+ */
+gchar*
+gimp_attribute_get_xml (GimpAttribute *attribute)
+{
+ gchar *v_escaped = NULL;
+ gchar *i_escaped = NULL;
+ gchar *interpreted_tag_elem = NULL;
+ gboolean is_interpreted = FALSE;
+ gboolean encoded;
+ gchar *start_tag_elem;
+ gchar *end_tag_elem;
+ gchar *value_tag_elem;
+ gchar *struct_tag_elem = NULL;
+ gboolean utf = TRUE;
+ GimpAttributePrivate *private;
+ GString *xml;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTE (attribute), NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ xml = g_string_new (NULL);
+
+ if(private->interpreted_value && g_strcmp0 (private->tag_value, private->interpreted_value))
+ {
+ is_interpreted = TRUE;
+ }
+
+ v_escaped = gimp_attribute_escape_value (private->name, private->tag_value, &encoded);
+
+ start_tag_elem = g_strdup_printf (" <tag name=\"%s\" type=\"%d\">\n", private->name, private->value_type);
+ end_tag_elem = g_strdup_printf (" </tag>");
+
+ g_string_append (xml, start_tag_elem);
+
+ if (encoded)
+ {
+ value_tag_elem = g_strdup_printf (" <value encoding=\"base64\">%s</value>\n", v_escaped);
+ }
+ else
+ {
+ value_tag_elem = g_strdup_printf (" <value>%s</value>\n", v_escaped);
+ }
+
+ g_string_append (xml, value_tag_elem);
+
+ if (private->has_structure)
+ {
+ struct_tag_elem = g_strdup_printf (" <structure>%d</structure>\n", private->structure_type);
+ g_string_append (xml, struct_tag_elem);
+ }
+
+ if (is_interpreted && utf)
+ {
+ i_escaped = gimp_attribute_escape_value (private->name, private->interpreted_value, &encoded);
+
+ if (encoded)
+ {
+ interpreted_tag_elem = g_strdup_printf (" <interpreted encoding=\"base64\">%s</interpreted>\n",
i_escaped);
+ }
+ else
+ {
+ interpreted_tag_elem = g_strdup_printf (" <interpreted>%s</interpreted>\n", i_escaped);
+ }
+
+ g_string_append (xml, interpreted_tag_elem);
+ }
+
+ g_string_append (xml, end_tag_elem);
+
+ g_free (v_escaped);
+ g_free (start_tag_elem);
+ g_free (end_tag_elem);
+ g_free (value_tag_elem);
+ if (i_escaped)
+ g_free (i_escaped);
+ if (interpreted_tag_elem)
+ g_free (interpreted_tag_elem);
+ if (struct_tag_elem)
+ g_free (interpreted_tag_elem);
+
+ return g_string_free (xml, FALSE);
+}
+
+/**
+ * gimp_attribute_get_value_type_from_string:
+ *
+ * @exiv_tag_type_string: a @gchar
+ *
+ * converts a string representation tag type from gexiv2/exiv2
+ * to a #GimpAttributeValueType
+ *
+ * Return value: #GimpAttributeValueType
+ *
+ * Since: 2.10
+ */
+GimpAttributeValueType
+gimp_attribute_get_value_type_from_string (const gchar* string)
+{
+ GimpAttributeValueType type;
+ gchar *lowchar;
+
+ if (! string)
+ return TYPE_INVALID;
+
+ lowchar = g_ascii_strdown (string, -1);
+
+ if (!g_strcmp0 (lowchar, "invalid"))
+ type = TYPE_INVALID;
+ else if (!g_strcmp0 (lowchar, "byte"))
+ type = TYPE_BYTE;
+ else if (!g_strcmp0 (lowchar, "ascii"))
+ type = TYPE_ASCII;
+ else if (!g_strcmp0 (lowchar, "short"))
+ type = TYPE_SHORT;
+ else if (!g_strcmp0 (lowchar, "long"))
+ type = TYPE_LONG;
+ else if (!g_strcmp0 (lowchar, "rational"))
+ type = TYPE_RATIONAL;
+ else if (!g_strcmp0 (lowchar, "sbyte"))
+ type = TYPE_BYTE;
+ else if (!g_strcmp0 (lowchar, "undefined"))
+ type = TYPE_ASCII;
+ else if (!g_strcmp0 (lowchar, "sshort"))
+ type = TYPE_SSHORT;
+ else if (!g_strcmp0 (lowchar, "slong"))
+ type = TYPE_SLONG;
+ else if (!g_strcmp0 (lowchar, "srational"))
+ type = TYPE_SRATIONAL;
+ else if (!g_strcmp0 (lowchar, "float"))
+ type = TYPE_FLOAT;
+ else if (!g_strcmp0 (lowchar, "double"))
+ type = TYPE_DOUBLE;
+ else if (!g_strcmp0 (lowchar, "ifd"))
+ type = TYPE_ASCII;
+ else if (!g_strcmp0 (lowchar, "string"))
+ type = TYPE_UNICODE;
+ else if (!g_strcmp0 (lowchar, "date"))
+ type = TYPE_DATE;
+ else if (!g_strcmp0 (lowchar, "time"))
+ type = TYPE_TIME;
+ else if (!g_strcmp0 (lowchar, "comment"))
+ type = TYPE_UNICODE;
+ else if (!g_strcmp0 (lowchar, "directory"))
+ type = TYPE_UNICODE;
+ else if (!g_strcmp0 (lowchar, "xmptext"))
+ type = TYPE_ASCII;
+ else if (!g_strcmp0 (lowchar, "xmpalt"))
+ type = TYPE_MULTIPLE;
+ else if (!g_strcmp0 (lowchar, "xmpbag"))
+ type = TYPE_MULTIPLE;
+ else if (!g_strcmp0 (lowchar, "xmpseq"))
+ type = TYPE_MULTIPLE;
+ else if (!g_strcmp0 (lowchar, "langalt"))
+ type = TYPE_MULTIPLE;
+ else
+ type = TYPE_INVALID;
+
+ g_free (lowchar);
+
+ return type;
+}
+
+/**
+ * gimp_attribute_is_valid:
+ *
+ * @attribute: a @GimpAttribute
+ *
+ * checks, if @attribute is valid.
+ * A @GimpAttribute is valid, if the @GimpAttributeValueType
+ * has a valid entry.
+ *
+ * Return value: @gboolean: TRUE if valid, FALSE otherwise
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_attribute_is_valid (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, FALSE);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ if (private->value_type == TYPE_INVALID)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/*
+ * internal functions
+ */
+
+/**
+ * gimp_attribute_set_name:
+ *
+ * @attribute: a #GimpAttribute
+ * @value: a constant #gchar
+ *
+ * sets the name of the #GimpAttribute object to
+ * a @value
+ *
+ * @value can be freed after
+ *
+ * Since: 2.10
+ */
+static void
+gimp_attribute_set_name (GimpAttribute* attribute, const gchar* value)
+{
+ gchar **split_name;
+ gchar *lowchar;
+ GimpAttributePrivate *private;
+
+ g_return_if_fail (attribute != NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ _g_free0 (private->name);
+
+ private->name = g_strdup (value);
+
+ private->name = string_replace_str (private->name, "Iptc4xmpExt", "iptcExt");
+ private->name = string_replace_str (private->name, "Iptc4xmpCore", "iptc");
+
+// if (! g_strcmp0 (private->name, "Exif.Image.ResolutionUnit"))
+// {
+// g_print ("found: %s\n", private->name);
+// }
+
+ split_name = g_strsplit (private->name, ".", 3);
+
+ if (split_name [0])
+ private->attribute_type = split_name [0];
+ if (split_name [1])
+ private->attribute_ifd = split_name [1];
+ if (split_name [2])
+ private->attribute_tag = split_name [2];
+
+ attribute->priv->is_new_name_space = FALSE;
+
+ lowchar = g_ascii_strdown (private->attribute_type, -1);
+
+ if (!g_strcmp0 (lowchar, "exif"))
+ {
+ private->tag_type = TAG_EXIF;
+ }
+ else if (!g_strcmp0 (lowchar, "xmp"))
+ {
+ gint j;
+ gint p1 = 0;
+ gint p2 = 0;
+ gboolean is_known = FALSE;
+ private->tag_type = TAG_XMP;
+
+ while (p2 != -1)
+ {
+
+ p2 = string_index_of (private->name, "[", p1);
+
+ if (p2 > -1)
+ {
+ gchar *struct_string = NULL;
+
+ private->has_structure = TRUE;
+ private->structure_type = STRUCTURE_TYPE_BAG;
+
+ struct_string = string_substring (private->name, 0, p2);
+ private->attribute_structure = g_slist_prepend (private->attribute_structure, struct_string);
+
+ p1 = p2 + 1;
+ }
+ }
+
+ for (j = 0; j < G_N_ELEMENTS (xmp_namespaces); j++)
+ {
+ if (! g_strcmp0 (private->attribute_ifd, xmp_namespaces[j]))
+ {
+ is_known = TRUE;
+ break;
+ }
+ }
+ if (! is_known)
+ {
+ private->is_new_name_space = TRUE;
+ }
+ }
+ else if (!g_strcmp0 (lowchar, "iptc"))
+ {
+ private->tag_type = TAG_IPTC;
+ }
+ else if (!g_strcmp0 (lowchar, "gimp"))
+ {
+ private->tag_type = TAG_GIMP;
+ }
+ else
+ {
+ private->tag_type = TAG_MISC;
+ }
+
+ g_free (lowchar);
+}
+
+/**
+ * gimp_attribute_set_structure_type:
+ *
+ * @attribute: a #GimpAttribute
+ * @value: a GimpAttributeStructureType
+ *
+ * Sets the structure type, Bag, Seq, None, etc.
+ * The structure is not checked.
+ * The structure type is only relevant for
+ * GimpAttribute, that are part of a structure.
+ *
+ * Since : 2.10
+ */
+void
+gimp_attribute_set_structure_type (GimpAttribute *attribute,
+ GimpAttributeStructureType value)
+{
+ GimpAttributePrivate *private;
+
+ g_return_if_fail (attribute != NULL);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ private->structure_type = value;
+}
+
+/**
+ * gimp_attribute_get_structure_type:
+ *
+ * @attribute: a #GimpAttribute
+ *
+ * The structure type, Bag, Seq, None, etc.
+ * Return value: a #GimpAttributeStructureType
+ *
+ * Since : 2.10
+ */
+GimpAttributeStructureType
+gimp_attribute_get_structure_type (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, STRUCTURE_TYPE_NONE);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ return private->structure_type;
+
+}
+
+/**
+ * gimp_attribute_has_structure:
+ *
+ * @attribute: a #GimpAttribute
+ *
+ * Return value: TRUE, if @attribute is part
+ * of a structure.
+ *
+ * Since : 2.10
+ */
+gboolean
+gimp_attribute_has_structure (GimpAttribute *attribute)
+{
+ GimpAttributePrivate *private;
+
+ g_return_val_if_fail (attribute != NULL, FALSE);
+
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ return private->has_structure;
+}
+
+/**
+ * gimp_attribute_escape_value:
+ *
+ * @name: a gchar *
+ * @value: a gchar*
+ * @encoded: a pointer to a gboolean
+ *
+ * converts @value into an escaped string for GMarkup.
+ * If @value is not valid UTF-8, it is converted
+ * into a base64 coded string and @encoded is set to TRUE,
+ * otherwise FALSE
+ *
+ * Return value: a new allocated @gchar *
+ *
+ * Since : 2.10
+ */
+static gchar *
+gimp_attribute_escape_value (gchar *name,
+ gchar *value,
+ gboolean *encoded)
+{
+
+ if (!g_utf8_validate (value, -1, NULL))
+ {
+ gchar *enc_val = NULL;
+
+ *encoded = TRUE;
+ enc_val = g_base64_encode ((const guchar *) value,
+ strlen (value) + 1);
+
+ return enc_val;
+ }
+ *encoded = FALSE;
+
+ return g_markup_escape_text (value, -1);
+}
+
+/**
+ * gimp_attribute_copy:
+ *
+ * @attribute: a #GimpAttribute
+ *
+ * duplicates a #GimpAttribute object
+ *
+ * Return value: a new @GimpAttribute or %NULL
+ *
+ * Since : 2.10
+ */
+GimpAttribute*
+gimp_attribute_copy (GimpAttribute *attribute)
+{
+ GimpAttribute *new_attribute = NULL;
+ GimpAttributePrivate *private;
+ GimpAttributePrivate *new_private;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTE (attribute), NULL);
+
+ new_attribute = (GimpAttribute*) g_object_new (GIMP_TYPE_ATTRIBUTE, NULL);
+
+ if (new_attribute)
+ {
+ private = GIMP_ATTRIBUTE_GET_PRIVATE(attribute);
+ new_private = GIMP_ATTRIBUTE_GET_PRIVATE(new_attribute);
+ if (private->name)
+ new_private->name = g_strdup (private->name);
+
+ if (private->tag_value)
+ new_private->tag_value = g_strdup (private->tag_value);
+
+ if (private->interpreted_value)
+ new_private->interpreted_value = g_strdup (private->interpreted_value);
+
+ if (private->exif_type)
+ new_private->exif_type = g_strdup (private->exif_type);
+
+ if (private->attribute_type)
+ new_private->attribute_type = g_strdup (private->attribute_type);
+
+ if (private->attribute_ifd)
+ new_private->attribute_ifd = g_strdup (private->attribute_ifd);
+
+ if (private->attribute_tag)
+ new_private->attribute_tag = g_strdup (private->attribute_tag);
+
+ new_private->is_new_name_space = private->is_new_name_space;
+ new_private->value_type = private->value_type;
+ new_private->tag_type = private->tag_type;
+
+ return new_attribute;
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_attribute_construct:
+ *
+ * @object_type: a #GType
+ * @name: a constant #gchar that describes the name of the attribute
+ *
+ * constructs a new #GimpAttribute object
+ *
+ * Return value: a new @GimpAttribute or %NULL if no @name is set
+ *
+ * Since : 2.10
+ */
+static GimpAttribute*
+gimp_attribute_construct (GType object_type,
+ const gchar *name)
+{
+ GimpAttribute * attribute = NULL;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ attribute = (GimpAttribute*) g_object_new (object_type, NULL);
+ if (attribute)
+ {
+ gimp_attribute_set_name (attribute, name);
+ }
+ return attribute;
+}
+
+/**
+ * gimp_attribute_class_init:
+ *
+ * class initializer
+ */
+static void
+gimp_attribute_class_init (GimpAttributeClass * klass)
+{
+ gimp_attribute_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof(GimpAttributePrivate));
+
+ G_OBJECT_CLASS (klass)->get_property = gimp_attribute_get_property;
+ G_OBJECT_CLASS (klass)->set_property = gimp_attribute_set_property;
+ G_OBJECT_CLASS (klass)->finalize = gimp_attribute_finalize;
+}
+
+/**
+ * gimp_attribute_instance_init:
+ *
+ * instance initializer
+ */
+static void
+gimp_attribute_instance_init (GimpAttribute * attribute)
+{
+ GimpAttributePrivate *private;
+ attribute->priv = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ private->name = NULL;
+ private->tag_value = NULL;
+ private->interpreted_value = NULL;
+ private->exif_type = NULL;
+ private->value_type = TYPE_INVALID;
+ private->attribute_type = NULL;
+ private->attribute_ifd = NULL;
+ private->attribute_tag = NULL;
+ private->attribute_structure = NULL;
+ private->is_new_name_space = FALSE;
+ private->has_structure = FALSE;
+ private->structure_type = STRUCTURE_TYPE_NONE;
+}
+
+/**
+ * gimp_attribute_finalize:
+ *
+ * instance finalizer
+ */
+static void
+gimp_attribute_finalize (GObject* obj)
+{
+ GimpAttribute *attribute;
+ GimpAttributePrivate *private;
+
+ g_return_if_fail (GIMP_IS_ATTRIBUTE (obj));
+
+ attribute = GIMP_ATTRIBUTE (obj);
+ private = GIMP_ATTRIBUTE_GET_PRIVATE (attribute);
+
+ counter++;
+
+ if(private->name)
+ _g_free0 (private->name);
+
+ if(private->tag_value)
+ _g_free0 (private->tag_value);
+
+ if(private->interpreted_value)
+ _g_free0 (private->interpreted_value);
+
+ if(private->exif_type)
+ _g_free0 (private->exif_type);
+
+ if(private->attribute_type)
+ _g_free0 (private->attribute_type);
+
+ if(private->attribute_ifd)
+ _g_free0 (private->attribute_ifd);
+
+ if(private->attribute_tag)
+ _g_free0 (private->attribute_tag);
+
+ if(private->attribute_structure)
+ g_slist_free_full (private->attribute_structure, g_free);
+
+ G_OBJECT_CLASS (gimp_attribute_parent_class)->finalize (obj);
+}
+
+/**
+ * gimp_attribute_get_property
+ *
+ * instance get property
+ */
+static void
+gimp_attribute_get_property (GObject * object,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+}
+
+/**
+ * gimp_attribute_set_property
+ *
+ * instance set property
+ */
+static void
+gimp_attribute_set_property (GObject * object,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+}
+
+/**
+ * gimp_attribute_get_type
+ *
+ * Return value: #GimpAttribute type
+ */
+GType
+gimp_attribute_get_type (void)
+{
+ static volatile gsize gimp_attribute_type_id__volatile = 0;
+ if (g_once_init_enter(&gimp_attribute_type_id__volatile))
+ {
+ static const GTypeInfo g_define_type_info =
+ { sizeof(GimpAttributeClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_attribute_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof(GimpAttribute),
+ 0,
+ (GInstanceInitFunc) gimp_attribute_instance_init,
+ NULL
+ };
+
+ GType gimp_attribute_type_id;
+ gimp_attribute_type_id = g_type_register_static (G_TYPE_OBJECT,
+ "GimpAttribute",
+ &g_define_type_info,
+ 0);
+
+ g_once_init_leave(&gimp_attribute_type_id__volatile,
+ gimp_attribute_type_id);
+ }
+ return gimp_attribute_type_id__volatile;
+}
+
+
+/*
+ * Helper functions
+ */
+
+/**
+ * get_tag_time:
+ *
+ * @input: a #gchar array
+ * @tm: a pointer to a #TagTime struct
+ *
+ * converts a ISO 8601 IPTC Time to a TagTime structure
+ * Input is:
+ * HHMMSS
+ * HHMMSS:HHMM
+ *
+ * Return value: #gboolean for success
+ *
+ * Since: 2.10
+ */
+static gboolean get_tag_time (gchar *input, TagTime *tm)
+{
+ long val;
+ GString *string = NULL;
+ gchar *tmpdate;
+ gboolean flong;
+
+ g_return_val_if_fail (input != NULL, FALSE);
+
+ input = g_strstrip (input);
+
+ if (*input == '\0' || !g_ascii_isdigit (*input))
+ return FALSE;
+
+ string = g_string_new (NULL);
+
+ val = strtoul (input, (char **)&input, 10);
+
+ if (val < 25) /* HH:MM:SS+/-HH:MM */
+ {
+ flong = FALSE;
+ g_string_append_printf (string, "%02ld", val);
+ }
+ else
+ {
+ flong = TRUE;
+ g_string_append_printf (string, "%06ld", val);
+ }
+
+
+ if (! flong) /* exactly 2 times */
+ {
+ input++;
+ val = strtoul (input, (char **)&input, 10);
+ g_string_append_printf (string, "%02ld", val);
+ input++;
+ val = strtoul (input, (char **)&input, 10);
+ g_string_append_printf (string, "%02ld", val);
+ }
+
+ tmpdate = g_string_free (string, FALSE);
+
+ val = strtoul (tmpdate, (char **)&tmpdate, 10);
+
+ /* hhmmss */
+
+ tm->tag_time_sec = val % 100;
+ tm->tag_time_min = (val % 10000) / 100;
+ tm->tag_time_hour = val / 10000;
+
+ string = g_string_new (NULL);
+
+ if (flong) /* :HHSS for time zone */
+ {
+ val = 0L;
+
+ if (*input == ':')
+ {
+ input++;
+ val = strtoul (input, (char **)&input, 10);
+ }
+ g_string_append_printf (string, "%05ld", val);
+ }
+ else
+ {
+ val = 0L;
+
+ if (*input == '+' || *input == '-') /* +/- HH:MM for time zone */
+ {
+ val = strtoul (input, (char **)&input, 10);
+ g_string_append_printf (string, "%02ld", val);
+ input++;
+ val = strtoul (input, (char **)&input, 10);
+ g_string_append_printf (string, "%02ld", val);
+ }
+ else
+ {
+ g_string_append_printf (string, "%04ld", val);
+ }
+ }
+
+ tmpdate = g_string_free (string, FALSE);
+
+ val = strtoul (tmpdate, (char **)&tmpdate, 10);
+
+ tm->tag_tz_min = (val % 100) / 100;
+ tm->tag_tz_hour = val / 100;
+
+ return *input == '\0';
+}
+
+/**
+ * get_tag_date:
+ *
+ * @input: a #gchar array
+ * @dt: a pointer to a #TagDate struct
+ *
+ * converts a ISO 8601 IPTC Date to a TagDate structure
+ * Input is:
+ * CCYYMMDD
+ *
+ * Return value: #gboolean for success
+ *
+ * Since: 2.10
+ */
+static gboolean get_tag_date (gchar *input, TagDate *dt)
+{
+ long val;
+ GString *string = NULL;
+ gchar *tmpdate;
+ gboolean eod;
+
+ g_return_val_if_fail (input != NULL, FALSE);
+
+ input = g_strstrip (input);
+
+ if (*input == '\0' || !g_ascii_isdigit (*input))
+ return FALSE;
+
+ string = g_string_new (NULL);
+ eod = FALSE;
+
+ while (! eod)
+ {
+ val = strtoul (input, (char **)&input, 10);
+
+ if (*input == '-' ||
+ *input == ':')
+ {
+ input++;
+ }
+ else
+ {
+ eod = TRUE;
+ }
+
+ if (val < 10)
+ g_string_append_printf (string, "%02ld", val);
+ else
+ g_string_append_printf (string, "%ld", val);
+ }
+ tmpdate = g_string_free (string, FALSE);
+
+ val = strtoul (tmpdate, (char **)&tmpdate, 10);
+
+ /* YYYYMMDD */
+ dt->tag_day = val % 100;
+ dt->tag_month = (val % 10000) / 100;
+ dt->tag_year = val / 10000;
+
+ return *input == '\0';
+}
+
+/**
+ * string_replace_str:
+ *
+ * @original: the original string
+ * @old_pattern: the pattern to replace
+ * @replacement: the replacement
+ *
+ * replaces @old_pattern by @replacement in @original
+ * This routine is copied from VALA.
+ *
+ * Return value: the new string.
+ *
+ * Since: 2.10
+ */
+static gchar*
+string_replace_str (const gchar* original, const gchar* old_pattern, const gchar* replacement) {
+ gchar *result = NULL;
+ GError *_inner_error_ = NULL;
+
+ g_return_val_if_fail (original != NULL, NULL);
+ g_return_val_if_fail (old_pattern != NULL, NULL);
+ g_return_val_if_fail (replacement != NULL, NULL);
+
+ {
+ GRegex *regex = NULL;
+ const gchar *_tmp0_ = NULL;
+ gchar *_tmp1_ = NULL;
+ gchar *_tmp2_ = NULL;
+ GRegex *_tmp3_ = NULL;
+ GRegex *_tmp4_ = NULL;
+ gchar *_tmp5_ = NULL;
+ GRegex *_tmp6_ = NULL;
+ const gchar *_tmp7_ = NULL;
+ gchar *_tmp8_ = NULL;
+ gchar *_tmp9_ = NULL;
+
+ _tmp0_ = old_pattern;
+ _tmp1_ = g_regex_escape_string (_tmp0_, -1);
+ _tmp2_ = _tmp1_;
+ _tmp3_ = g_regex_new (_tmp2_, 0, 0, &_inner_error_);
+ _tmp4_ = _tmp3_;
+ _g_free0 (_tmp2_);
+ regex = _tmp4_;
+
+ if (_inner_error_ != NULL)
+ {
+ if (_inner_error_->domain == G_REGEX_ERROR)
+ {
+ goto __catch0_g_regex_error;
+ }
+ g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+
+ _tmp6_ = regex;
+ _tmp7_ = replacement;
+ _tmp8_ = g_regex_replace_literal (_tmp6_, original, (gssize) (-1), 0, _tmp7_, 0, &_inner_error_);
+ _tmp5_ = _tmp8_;
+
+ if (_inner_error_ != NULL)
+ {
+ _g_regex_unref0 (regex);
+ if (_inner_error_->domain == G_REGEX_ERROR)
+ {
+ goto __catch0_g_regex_error;
+ }
+ _g_regex_unref0 (regex);
+ g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+
+ _tmp9_ = _tmp5_;
+ _tmp5_ = NULL;
+ result = _tmp9_;
+ _g_free0 (_tmp5_);
+ _g_regex_unref0 (regex);
+ return result;
+ }
+
+ goto __finally0;
+ __catch0_g_regex_error:
+ {
+ GError* e = NULL;
+ e = _inner_error_;
+ _inner_error_ = NULL;
+ g_assert_not_reached ();
+ _g_error_free0 (e);
+ }
+
+ __finally0:
+ if (_inner_error_ != NULL)
+ {
+ g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+}
+
+/**
+ * string_index_of:
+ *
+ * @haystack: a #gchar
+ * @needle: a #gchar
+ * @start_index: a #gint
+ *
+ *
+ * Return value: #gint that points to the position
+ * of @needle in @haystack, starting at @start_index,
+ * or -1, if @needle is not found
+ *
+ * Since: 2.10
+ */
+static gint
+string_index_of (gchar* haystack, gchar* needle, gint start_index)
+{
+ gint result = 0;
+ gchar* temp1_ = NULL;
+ g_return_val_if_fail (haystack != NULL, -1);
+ g_return_val_if_fail (needle != NULL, -1);
+ temp1_ = strstr (((gchar*) haystack) + start_index, needle);
+ if (temp1_ != NULL)
+ {
+ result = (gint) (temp1_ - ((gchar*) haystack));
+ return result;
+ }
+ else
+ {
+ result = -1;
+ return result;
+ }
+}
+
+/**
+ * string_strnlen:
+ *
+ * @str: a #gchar
+ * @maxlen: a #glong
+ *
+ * Returns the length of @str or @maxlen, if @str
+ * is longer that @maxlen
+ *
+ * Return value: #glong
+ *
+ * Since: 2.10
+ */
+static glong
+string_strnlen (gchar* str, glong maxlen)
+{
+ glong result = 0L;
+ gchar* temp1_ = NULL;
+ temp1_ = memchr (str, 0, (gsize) maxlen);
+ if (temp1_ == NULL)
+ {
+ return maxlen;
+ }
+ else
+ {
+ result = (glong) (temp1_ - str);
+ return result;
+ }
+}
+
+/**
+ * string_substring:
+ *
+ * @string: a #gchar
+ * @offset: a #glong
+ * @len: a #glong
+ *
+ * Returns a substring of @string, starting at @offset
+ * with a legth of @len
+ *
+ * Return value: #gchar to be freed if no longer use
+ *
+ * Since: 2.10
+ */
+static gchar*
+string_substring (gchar* string, glong offset, glong len)
+{
+ gchar* result = NULL;
+ glong string_length = 0L;
+ gboolean _tmp0_ = FALSE;
+ g_return_val_if_fail(string != NULL, NULL);
+ if (offset >= ((glong) 0))
+ {
+ _tmp0_ = len >= ((glong) 0);
+ }
+ else
+ {
+ _tmp0_ = FALSE;
+ }
+
+ if (_tmp0_)
+ {
+ string_length = string_strnlen ((gchar*) string, offset + len);
+ }
+ else
+ {
+ string_length = (glong) strlen (string);
+ }
+
+ if (offset < ((glong) 0))
+ {
+ offset = string_length + offset;
+ g_return_val_if_fail (offset >= ((glong ) 0), NULL);
+ }
+ else
+ {
+ g_return_val_if_fail (offset <= string_length, NULL);
+ }
+ if (len < ((glong) 0))
+ {
+ len = string_length - offset;
+ }
+
+ g_return_val_if_fail ((offset + len) <= string_length, NULL);
+ result = g_strndup (((gchar*) string) + offset, (gsize) len);
+ return result;
+}
+
diff --git a/libgimpbase/gimpattribute.h b/libgimpbase/gimpattribute.h
new file mode 100644
index 0000000..875036d
--- /dev/null
+++ b/libgimpbase/gimpattribute.h
@@ -0,0 +1,95 @@
+/* gimpattribute.h generated by valac 0.24.0, the Vala compiler, do not modify */
+
+
+#ifndef __GIMPATTRIBUTE_H__
+#define __GIMPATTRIBUTE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_ATTRIBUTE (gimp_attribute_get_type ())
+#define GIMP_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ATTRIBUTE,
GimpAttribute))
+#define GIMP_ATTRIBUTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ATTRIBUTE,
GimpAttributeClass))
+#define GIMP_IS_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ATTRIBUTE))
+#define GIMP_IS_ATTRIBUTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ATTRIBUTE))
+#define GIMP_ATTRIBUTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ATTRIBUTE,
GimpAttributeClass))
+#define GIMP_ATTRIBUTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIMP_TYPE_ATTRIBUTE,
GimpAttributePrivate))
+
+#define GIMP_TYPE_ATTRIBUTE_TAG_TYPE (gimp_attribute_tag_type_get_type ())
+
+GType gimp_attribute_tag_type_get_type (void) G_GNUC_CONST;
+
+typedef enum {
+ TYPE_INVALID,
+ TYPE_LONG,
+ TYPE_SLONG,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_SHORT,
+ TYPE_SSHORT,
+ TYPE_DATE,
+ TYPE_TIME,
+ TYPE_ASCII,
+ TYPE_UNICODE,
+ TYPE_BYTE,
+ TYPE_MULTIPLE,
+ TYPE_RATIONAL,
+ TYPE_SRATIONAL,
+ TYPE_UNKNOWN,
+ NUM_VALUE_TYPES
+} GimpAttributeValueType;
+
+typedef enum {
+ STRUCTURE_TYPE_BAG,
+ STRUCTURE_TYPE_SEQ,
+ STRUCTURE_TYPE_ALT,
+ STRUCTURE_TYPE_LANG,
+ STRUCTURE_TYPE_NONE,
+ STRUCTURE_TYPE_STRUCT,
+ NUM_STRUCTURE_TYPES
+} GimpAttributeStructureType;
+
+typedef enum {
+ TAG_INVALID,
+ TAG_EXIF,
+ TAG_XMP,
+ TAG_IPTC,
+ TAG_GIMP,
+ TAG_MISC,
+ NUM_TAG_TYPES
+} GimpAttributeTagType;
+
+GType gimp_attribute_get_type (void) G_GNUC_CONST;
+
+GimpAttribute* gimp_attribute_new_string (const gchar *name,
+ gchar *value,
+ GimpAttributeValueType type);
+const gchar* gimp_attribute_get_interpreted_string (GimpAttribute
*attribute);
+void gimp_attribute_set_interpreted_string (GimpAttribute
*attribute,
+ const gchar
*value);
+GimpAttribute* gimp_attribute_copy (GimpAttribute
*attribute);
+gboolean gimp_attribute_is_valid (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_attribute_type (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_attribute_ifd (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_attribute_tag (GimpAttribute
*attribute);
+const gboolean gimp_attribute_is_new_namespace (GimpAttribute
*attribute);
+const gchar* gimp_attribute_get_name (GimpAttribute
*attribute);
+GValue gimp_attribute_get_value (GimpAttribute
*attribute);
+gchar* gimp_attribute_get_string (GimpAttribute
*attribute);
+GimpAttributeTagType gimp_attribute_get_tag_type (GimpAttribute
*attribute);
+GimpAttributeValueType gimp_attribute_get_value_type_from_string (const gchar
*string);
+GimpAttributeValueType gimp_attribute_get_value_type (GimpAttribute
*attribute);
+void gimp_attribute_set_value_type (GimpAttribute
*attribute,
+ GimpAttributeValueType
value_type);
+gboolean gimp_attribute_has_structure (GimpAttribute
*attribute);
+GSList* gimp_attribute_get_attribute_structure (GimpAttribute
*attribute);
+void gimp_attribute_set_structure_type (GimpAttribute
*attribute,
+ GimpAttributeStructureType
value);
+GimpAttributeStructureType gimp_attribute_get_structure_type (GimpAttribute
*attribute);
+gchar* gimp_attribute_get_xml (GimpAttribute
*attribute);
+void gimp_attribute_print (GimpAttribute
*attribute);
+
+G_END_DECLS
+
+#endif
diff --git a/libgimpbase/gimpattributes-image.c b/libgimpbase/gimpattributes-image.c
new file mode 100644
index 0000000..b8918fa
--- /dev/null
+++ b/libgimpbase/gimpattributes-image.c
@@ -0,0 +1,282 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpattributes-image.c
+ * Copyright (C) 2014 Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <gexiv2/gexiv2.h>
+
+#ifdef G_OS_WIN32
+#endif
+
+#include "libgimpmath/gimpmath.h"
+
+#include "gimprational.h"
+#include "gimpbasetypes.h"
+#include "gimpattribute.h"
+#include "gimpattributes.h"
+#include "gimplimits.h"
+#include "gimpmetadata.h"
+#include "gimpunit.h"
+#include "gimpattributes-image.h"
+
+/**
+ * SECTION: gimpattributes-image
+ * @title: gimpattributes-image
+ * @short_description: handle image data.
+ * @see_also: GimpAttribute, GimpAttributes
+ *
+ * handle image data.
+ **/
+
+
+/**
+ * gimp_metadata_set_resolution:
+ * @metadata: A #GimpMetadata instance.
+ * @xres: The image's X Resolution, in ppi
+ * @yres: The image's Y Resolution, in ppi
+ * @unit: The image's unit
+ *
+ * Sets Exif.Image.XResolution, Exif.Image.YResolution and
+ * Exif.Image.ResolutionUnit @metadata.
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_attributes_set_resolution (GimpAttributes *attributes,
+ gdouble xres,
+ gdouble yres,
+ GimpUnit unit)
+{
+ gchar buffer[64];
+ gint exif_unit;
+ gint factor;
+
+ g_return_if_fail (GIMP_IS_ATTRIBUTES (attributes));
+
+ if (gimp_unit_is_metric (unit))
+ {
+ xres /= 2.54;
+ yres /= 2.54;
+
+ exif_unit = 3;
+ }
+ else
+ {
+ exif_unit = 2;
+ }
+
+ for (factor = 1; factor <= 100 /* arbitrary */; factor++)
+ {
+ if (fabs (xres * factor - ROUND (xres * factor)) < 0.01 &&
+ fabs (yres * factor - ROUND (yres * factor)) < 0.01)
+ break;
+ }
+
+ g_snprintf (buffer, sizeof (buffer), "%d/%d", ROUND (xres * factor), factor);
+ gimp_attributes_new_attribute (attributes, "Exif.Image.XResolution", buffer, TYPE_RATIONAL);
+
+ g_snprintf (buffer, sizeof (buffer), "%d/%d", ROUND (yres * factor), factor);
+ gimp_attributes_new_attribute (attributes, "Exif.Image.YResolution", buffer, TYPE_RATIONAL);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", exif_unit);
+ gimp_attributes_new_attribute (attributes, "Exif.Image.ResolutionUnit", buffer, TYPE_SHORT);
+}
+
+/**
+ * gimp_attributes_get_resolution:
+ * @metadata: A #GimpMetadata instance.
+ * @xres: Return location for the X Resolution, in ppi
+ * @yres: Return location for the Y Resolution, in ppi
+ * @unit: Return location for the unit unit
+ *
+ * Returns values based on Exif.Image.XResolution,
+ * Exif.Image.YResolution and Exif.Image.ResolutionUnit of @metadata.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: GIMP 2.10
+ */
+gboolean
+gimp_attributes_get_resolution (GimpAttributes *attributes,
+ gdouble *xres,
+ gdouble *yres,
+ GimpUnit *unit)
+{
+ GimpAttribute *x_attribute;
+ GimpAttribute *y_attribute;
+ GimpAttribute *res_attribute;
+
+ gint xnom, xdenom;
+ gint ynom, ydenom;
+ gint exif_unit = 2;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTES (attributes), FALSE);
+
+ x_attribute = gimp_attributes_get_attribute (attributes, "Exif.Image.XResolution");
+ y_attribute = gimp_attributes_get_attribute (attributes, "Exif.Image.YResolution");
+ res_attribute = gimp_attributes_get_attribute (attributes, "Exif.Image.ResolutionUnit");
+
+ if (x_attribute)
+ {
+ GValue value;
+ Rational *rats;
+ gint *_nom;
+ gint *_denom;
+ gint l;
+
+ value = gimp_attribute_get_value (x_attribute);
+
+ rats = g_value_get_boxed (&value);
+
+ rational_to_int (rats, &_nom, &_denom, &l);
+
+ if (l > 0)
+ {
+ xnom = _nom[0];
+ xdenom = _denom[0];
+ }
+ rational_free (rats);
+ }
+ else
+ return FALSE;
+
+ if (y_attribute)
+ {
+ GValue value;
+ Rational *rats;
+ gint *_nom;
+ gint *_denom;
+ gint l;
+
+ value = gimp_attribute_get_value (y_attribute);
+
+ rats = g_value_get_boxed (&value);
+
+ rational_to_int (rats, &_nom, &_denom, &l);
+
+ if (l > 0)
+ {
+ ynom = _nom[0];
+ ydenom = _denom[0];
+ }
+ rational_free (rats);
+ }
+ else
+ return FALSE;
+
+ if (res_attribute)
+ {
+ GValue value = gimp_attribute_get_value (res_attribute);
+
+ exif_unit = (gint) g_value_get_uint (&value);
+ }
+
+ if (xnom != 0 && xdenom != 0 &&
+ ynom != 0 && ydenom != 0)
+ {
+ gdouble xresolution = (gdouble) xnom / (gdouble) xdenom;
+ gdouble yresolution = (gdouble) ynom / (gdouble) ydenom;
+
+ if (exif_unit == 3)
+ {
+ xresolution *= 2.54;
+ yresolution *= 2.54;
+ }
+
+ if (xresolution >= GIMP_MIN_RESOLUTION &&
+ xresolution <= GIMP_MAX_RESOLUTION &&
+ yresolution >= GIMP_MIN_RESOLUTION &&
+ yresolution <= GIMP_MAX_RESOLUTION)
+ {
+ if (xres)
+ *xres = xresolution;
+
+ if (yres)
+ *yres = yresolution;
+
+ if (unit)
+ {
+ if (exif_unit == 3)
+ *unit = GIMP_UNIT_MM;
+ else
+ *unit = GIMP_UNIT_INCH;
+ }
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * gimp_attributes_set_bits_per_sample:
+ * @metadata: A #GimpMetadata instance.
+ * @bps: Bytes per pixel, per component
+ *
+ * Sets Exif.Image.BitsPerSample on @metadata.
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_attributes_set_bits_per_sample (GimpAttributes *attributes,
+ gint bps)
+{
+ gchar buffer[32];
+
+ g_return_if_fail (GIMP_IS_ATTRIBUTES (attributes));
+
+ g_snprintf (buffer, sizeof (buffer), "%d", bps);
+ gimp_attributes_new_attribute (attributes, "Exif.Image.BitsPerSample", buffer, TYPE_SHORT);
+}
+
+/**
+ * gimp_attributes_set_pixel_size:
+ * @metadata: A #GimpMetadata instance.
+ * @width: Width in pixels
+ * @height: Height in pixels
+ *
+ * Sets Exif.Image.ImageWidth and Exif.Image.ImageLength on @metadata.
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_attributes_set_pixel_size (GimpAttributes *attributes,
+ gint width,
+ gint height)
+{
+ gchar buffer[32];
+
+ g_return_if_fail (GIMP_IS_ATTRIBUTES (attributes));
+
+ g_snprintf (buffer, sizeof (buffer), "%d", width);
+ gimp_attributes_new_attribute (attributes, "Exif.Image.ImageWidth", buffer, TYPE_LONG);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", height);
+ gimp_attributes_new_attribute (attributes, "Exif.Image.ImageLength", buffer, TYPE_LONG);
+}
diff --git a/libgimpbase/gimpattributes-image.h b/libgimpbase/gimpattributes-image.h
new file mode 100644
index 0000000..711a902
--- /dev/null
+++ b/libgimpbase/gimpattributes-image.h
@@ -0,0 +1,47 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpattributes-image.h
+ * Copyright (C) 2014 Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __GIMPATTRIBUTES_IMAGE_H__
+#define __GIMPATTRIBUTES_IMAGE_H__
+
+G_BEGIN_DECLS
+
+gboolean gimp_attributes_get_resolution (GimpAttributes *attributes,
+ gdouble *xres,
+ gdouble *yres,
+ GimpUnit *unit);
+
+void gimp_attributes_set_resolution (GimpAttributes *attributes,
+ gdouble xres,
+ gdouble yres,
+ GimpUnit unit);
+
+void gimp_attributes_set_bits_per_sample (GimpAttributes *attributes,
+ gint bps);
+
+void gimp_attributes_set_pixel_size (GimpAttributes *attributes,
+ gint width,
+ gint height);
+
+G_END_DECLS
+
+#endif
diff --git a/libgimpbase/gimpattributes.c b/libgimpbase/gimpattributes.c
new file mode 100644
index 0000000..7739702
--- /dev/null
+++ b/libgimpbase/gimpattributes.c
@@ -0,0 +1,1657 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpattributes.c
+ * Copyright (C) 2014 Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <gexiv2/gexiv2.h>
+
+#ifdef G_OS_WIN32
+#endif
+
+#include "gimpbasetypes.h"
+#include "gimpattribute.h"
+#include "gimpattributes.h"
+#include "gimpmetadata.h"
+
+/**
+ * SECTION: gimpattributes
+ * @title: gimpattributes
+ * @short_description: Class to store #GimpAttribute objects.
+ * @see_also: GimpAttribute,
+ *
+ * Class to store #GimpAttribute objects.
+ **/
+
+#define _g_free0(var) (var = (g_free (var), NULL))
+
+typedef struct _GimpAttributesClass GimpAttributesClass;
+typedef struct _GimpAttributesPrivate GimpAttributesPrivate;
+
+struct _GimpAttributes {
+ GObject parent_instance;
+ GimpAttributesPrivate *priv;
+};
+
+struct _GimpAttributesClass {
+ GObjectClass parent_class;
+};
+
+struct _GimpAttributesPrivate {
+ GHashTable *attribute_table;
+ GList *sorted_key_list;
+};
+
+typedef struct
+{
+ gchar name[1024];
+ gboolean base64;
+ GimpAttributeValueType type;
+ GimpAttributes *attributes;
+} GimpAttributesParseData;
+
+struct Namespaces{
+ const gchar *namespace_name;
+ const gchar *namespace_URI;
+};
+
+struct Namespaces namespaces_table[] = {
+ {"gimp", "http://www.gimp.org/ns/2.10/" },
+ {"dwc", "http://rs.tdwg.org/dwc/terms/" },
+ {"lr", "http://ns.adobe.com/lr/1.0/" }
+};
+
+
+static gpointer gimp_attributes_parent_class = NULL;
+static GimpAttribute *current_attribute = NULL;
+
+
+
+static GimpAttributes* gimp_attributes_construct (GType
object_type);
+static void gimp_attributes_finalize (GObject *obj);
+static void gimp_attributes_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_attributes_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_attributes_deserialize_error (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data);
+static GQuark gimp_attributes_error_quark (void);
+static void gimp_attributes_deserialize_start_element (GMarkupParseContext *context,
+ const gchar
*element_name,
+ const gchar
**attribute_names,
+ const gchar
**attribute_values,
+ gpointer user_data,
+ GError **error);
+static void gimp_attributes_deserialize_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error);
+static void gimp_attributes_deserialize_end_element (GMarkupParseContext *context,
+ const gchar
*element_name,
+ gpointer user_data,
+ GError **error);
+static const gchar* gimp_attributes_name_to_value (const gchar
**attribute_names,
+ const gchar
**attribute_values,
+ const gchar *name);
+
+
+static gboolean has_xmp_structure (GSList *xmp_list,
+ const gchar *entry);
+
+/**
+ * gimp_attributes_new:
+ *
+ * returns a new #GimpAttributes object
+ *
+ * Return value: a new @GimpAttributes object
+ *
+ * Since : 2.10
+ */
+GimpAttributes *
+gimp_attributes_new (void)
+{
+ return gimp_attributes_construct (GIMP_TYPE_ATTRIBUTES);
+}
+
+/**
+ * gimp_attributes_construct:
+ *
+ * @object_type: a #GType
+ *
+ * constructs a new #GimpAttributes object
+ *
+ * Return value: a new @GimpAttributes
+ *
+ * Since : 2.10
+ */
+static GimpAttributes*
+gimp_attributes_construct (GType object_type)
+{
+ GimpAttributes * attributes = NULL;
+
+ attributes = (GimpAttributes*) g_object_new (object_type, NULL);
+ return attributes;
+}
+
+/**
+ * gimp_attributes_class_init:
+ *
+ * class initializer
+ */
+static void
+gimp_attributes_class_init (GimpAttributesClass * klass)
+{
+ gimp_attributes_parent_class = g_type_class_peek_parent (klass);
+ g_type_class_add_private (klass, sizeof(GimpAttributesPrivate));
+
+ G_OBJECT_CLASS (klass)->get_property = gimp_attributes_get_property;
+ G_OBJECT_CLASS (klass)->set_property = gimp_attributes_set_property;
+ G_OBJECT_CLASS (klass)->finalize = gimp_attributes_finalize;
+}
+
+/**
+ * gimp_attributes_instance_init:
+ *
+ * instance initializer
+ */
+static void
+gimp_attributes_instance_init (GimpAttributes * attributes)
+{
+ attributes->priv = GIMP_ATTRIBUTES_GET_PRIVATE (attributes);
+ attributes->priv->attribute_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
g_object_unref);
+ attributes->priv->sorted_key_list = NULL;
+}
+
+/**
+ * gimp_attributes_finalize:
+ *
+ * instance finalizer
+ */
+static void
+gimp_attributes_finalize (GObject* obj)
+{
+ GimpAttributes * attributes;
+
+ attributes = G_TYPE_CHECK_INSTANCE_CAST (obj, GIMP_TYPE_ATTRIBUTES, GimpAttributes);
+
+ g_hash_table_unref (attributes->priv->attribute_table);
+ g_list_free_full (attributes->priv->sorted_key_list, (GDestroyNotify) g_free);
+
+ G_OBJECT_CLASS (gimp_attributes_parent_class)->finalize (obj);
+}
+
+/**
+ * gimp_attributes_get_property
+ *
+ * instance get property
+ */
+static void
+gimp_attributes_get_property (GObject * object,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+}
+
+/**
+ * gimp_attributes_set_property
+ *
+ * instance set property
+ */
+static void
+gimp_attributes_set_property (GObject * object,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+}
+
+/**
+ * gimp_attributes_get_type
+ *
+ * Return value: #GimpAttributes type
+ */
+GType
+gimp_attributes_get_type (void)
+{
+ static volatile gsize gimp_attributes_type_id__volatile = 0;
+ if (g_once_init_enter(&gimp_attributes_type_id__volatile))
+ {
+ static const GTypeInfo g_define_type_info =
+ { sizeof(GimpAttributesClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_attributes_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof(GimpAttributes),
+ 0,
+ (GInstanceInitFunc) gimp_attributes_instance_init,
+ NULL
+ };
+
+ GType gimp_attributes_type_id;
+ gimp_attributes_type_id = g_type_register_static (G_TYPE_OBJECT,
+ "GimpAttributes",
+ &g_define_type_info,
+ 0);
+
+ g_once_init_leave(&gimp_attributes_type_id__volatile,
+ gimp_attributes_type_id);
+ }
+ return gimp_attributes_type_id__volatile;
+}
+
+/**
+ * gimp_attributes_size:
+ *
+ * @attributes: a #GimpAttributes
+ *
+ * Return value: a #gint: amount of #GimpAttribute objects in
+ * the #GimpAttributes container
+ *
+ * Since : 2.10
+ */
+gint
+gimp_attributes_size (GimpAttributes *attributes)
+{
+ GimpAttributesPrivate *private = GIMP_ATTRIBUTES_GET_PRIVATE (attributes);
+
+ return g_hash_table_size (private->attribute_table);
+}
+
+/**
+ * gimp_attributes_duplicate:
+ *
+ * @attributes: a #GimpAttributes
+ *
+ * Duplicates the #GimpAttributes object with all the #GimpAttribute objects
+ * Return value: a copy of the @attributes object
+ *
+ * Since : 2.10
+ */
+GimpAttributes *
+gimp_attributes_duplicate (GimpAttributes *attributes)
+{
+ GimpAttributes * new_attributes = NULL;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTES (attributes), NULL);
+
+ if (attributes)
+ {
+ gchar *xml;
+
+ xml = gimp_attributes_serialize (attributes);
+ new_attributes = gimp_attributes_deserialize (xml);
+ }
+
+ return new_attributes;
+}
+
+/**
+ * gimp_attributes_get_table:
+ *
+ * @attributes: a #GimpAttributes
+ *
+ * Return value: the #GHashTable
+ *
+ * Since : 2.10
+ */
+GHashTable *
+gimp_attributes_get_table (GimpAttributes *attributes)
+{
+ return attributes->priv->attribute_table;
+}
+
+/**
+ * gimp_attributes_add_attribute:
+ *
+ * @attributes: a #GimpAttributes
+ * @attribute : a #GimpAttribute
+ *
+ * stores the @attribute in the @attributes container
+ *
+ * Since : 2.10
+ */
+void
+gimp_attributes_add_attribute (GimpAttributes *attributes,
+ GimpAttribute *attribute)
+{
+ const gchar *name;
+ const gchar *lowchar;
+
+ g_return_if_fail (GIMP_IS_ATTRIBUTES (attributes));
+ g_return_if_fail (GIMP_IS_ATTRIBUTE (attribute));
+
+ name = gimp_attribute_get_name (attribute);
+
+ if (name)
+ {
+ lowchar = g_ascii_strdown (name, -1);
+
+ /* FIXME: really simply add? That means, that an older value is overwritten */
+
+ if (g_hash_table_insert (attributes->priv->attribute_table, (gpointer) g_strdup (lowchar), (gpointer)
attribute))
+ attributes->priv->sorted_key_list = g_list_insert_sorted (attributes->priv->sorted_key_list,
+ (gpointer) g_strdup (lowchar),
+ (GCompareFunc) g_strcmp0);
+ }
+}
+
+/**
+ * gimp_attributes_get_attribute:
+ *
+ * @attributes: a #GimpAttributes
+ * @name : a #gchar array
+ *
+ * gets the #GimpAttribute object with @name
+ *
+ * Return value: the #GimpAttribute object if found, NULL otherwise.
+ *
+ * Since : 2.10
+ */
+GimpAttribute *
+gimp_attributes_get_attribute (GimpAttributes *attributes,
+ const gchar *name)
+{
+ gchar *lowchar;
+ GimpAttribute *attribute_data = NULL;
+ gpointer *data;
+
+ lowchar = g_ascii_strdown (name, -1);
+
+ data = g_hash_table_lookup (attributes->priv->attribute_table, (gpointer) lowchar);
+ if (data)
+ {
+ attribute_data = (GimpAttribute *) data;
+ }
+
+ g_free (lowchar);
+
+ return attribute_data;
+}
+
+/**
+ * gimp_attributes_remove_attribute:
+ *
+ * @attributes: a #GimpAttributes
+ * @name : a #gchar array
+ *
+ * removes the #GimpAttribute object with @name from the @attributes container
+ *
+ * Return value: TRUE, if removing was successful, FALSE otherwise.
+ *
+ * Since : 2.10
+ */
+gboolean
+gimp_attributes_remove_attribute (GimpAttributes *attributes,
+ const gchar *name)
+{
+ gchar *lowchar;
+ gboolean success;
+
+ lowchar = g_ascii_strdown (name, -1);
+
+ success = g_hash_table_remove (attributes->priv->attribute_table, (gpointer) lowchar);
+
+ attributes->priv->sorted_key_list = g_list_remove (attributes->priv->sorted_key_list,
+ (gpointer) lowchar);
+ g_free (lowchar);
+
+ return success;
+
+}
+
+/**
+ * gimp_attributes_has_attribute:
+ *
+ * @attributes: a #GimpAttributes
+ * @name : a #gchar array
+ *
+ * tests, if a #GimpAttribute object with @name is in the @attributes container
+ *
+ * Return value: TRUE if yes, FALSE otherwise.
+ *
+ * Since : 2.10
+ */
+gboolean
+gimp_attributes_has_attribute (GimpAttributes *attributes,
+ const gchar *name)
+{
+ gchar *lowchar;
+ gboolean success;
+
+ lowchar = g_ascii_strdown (name, -1);
+
+ success = g_hash_table_contains (attributes->priv->attribute_table, (gpointer) lowchar);
+
+ g_free (lowchar);
+
+ return success;
+}
+
+/**
+ * gimp_attributes_new_attribute:
+ *
+ * @attributes: a #GimpAttributes
+ * @name : a #gchar array
+ * @value: a #gchar array
+ * @type : a #GimpAttributeValueType
+ *
+ * adds a #GimpAttribute object to @attributes container.
+ * The #GimpAttribute object is created from the
+ * @name,
+ * @value and
+ * @type parameters.
+ *
+ * Return value: TRUE if successful, FALSE otherwise.
+ *
+ * Since : 2.10
+ */
+gboolean
+gimp_attributes_new_attribute (GimpAttributes *attributes,
+ const gchar *name,
+ gchar *value,
+ GimpAttributeValueType type)
+{
+ GimpAttribute *attribute;
+
+ attribute = gimp_attribute_new_string (name, value, type);
+ if (attribute)
+ {
+ gimp_attributes_add_attribute (attributes, attribute);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_attributes_serialize:
+ *
+ * @attributes: a #GimpAttributes
+ *
+ * creates a xml representation of all #GimpAttribute objects in the #GimpAttributes container.
+ * see #GimpAttribute:gimp_attribute_get_xml
+ *
+ * Return value: a new #gchar array, the xml representation of the #GimpAttributes object.
+ *
+ * Since : 2.10
+ */
+gchar *
+gimp_attributes_serialize (GimpAttributes *attributes)
+{
+ GString *string;
+ GList *key_list;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTES (attributes), NULL);
+
+ string = g_string_new (NULL);
+
+ g_string_append (string, "<?xml version='1.0' encoding='UTF-8'?>\n");
+ g_string_append (string, "<attributes>\n");
+
+ for (key_list = attributes->priv->sorted_key_list; key_list; key_list = key_list->next)
+ {
+ gchar *xml = NULL;
+ GimpAttribute *attribute = NULL;
+ gchar *p_key = (gchar *) key_list->data;
+
+ attribute = gimp_attributes_get_attribute (attributes, p_key);
+
+ xml = gimp_attribute_get_xml (attribute);
+
+ g_string_append_printf (string, "%s\n", xml);
+
+ g_free (xml);
+ }
+
+ g_string_append (string, "</attributes>\n");
+
+ return g_string_free (string, FALSE);
+}
+
+/**
+ * gimp_attributes_deserialize:
+ *
+ * @xml: a #gchar array
+ *
+ * parses a xml representation of a #GimpAttributes container.
+ * see
+ * #GimpAttributes:gimp_attributes_deserialize_start_element
+ * #GimpAttributes:gimp_attributes_deserialize_end_element
+ * #GimpAttributes:gimp_attributes_deserialize_text
+ * #GimpAttributes:gimp_attributes_deserialize_error
+ *
+ * Return value: a new #GimpAttributes object.
+ *
+ * Since : 2.10
+ */
+GimpAttributes *
+gimp_attributes_deserialize (const gchar *xml)
+{
+ GMarkupParser *markup_parser = g_slice_new (GMarkupParser);
+ GimpAttributesParseData *parse_data = g_slice_new (GimpAttributesParseData);
+ GMarkupParseContext *context;
+ GimpAttributes *attributes;
+
+ g_return_val_if_fail (xml != NULL, NULL);
+
+ attributes = gimp_attributes_new ();
+
+ parse_data->attributes = attributes;
+
+ markup_parser->start_element = gimp_attributes_deserialize_start_element;
+ markup_parser->end_element = gimp_attributes_deserialize_end_element;
+ markup_parser->text = gimp_attributes_deserialize_text;
+ markup_parser->passthrough = NULL;
+ markup_parser->error = gimp_attributes_deserialize_error;
+
+ context = g_markup_parse_context_new (markup_parser, 0, parse_data, NULL);
+
+ g_markup_parse_context_parse (context,
+ xml, strlen (xml),
+ NULL);
+
+ g_markup_parse_context_unref (context);
+
+ g_slice_free (GMarkupParser, markup_parser);
+ g_slice_free (GimpAttributesParseData, parse_data);
+
+ return attributes;
+}
+
+
+/**
+ * gimp_attributes_from_metadata:
+ * @attributes: The attributes the metadata will be added to, may be %NULL
+ * @metadata: The metadata in gexiv2 format
+ *
+ * Converts the @metadata retrieved from a file into
+ * a #GimpAttributes object
+ *
+ * Return value: The #GimpAttributes object
+ *
+ * Since: GIMP 2.10
+ */
+GimpAttributes *
+gimp_attributes_from_metadata (GimpAttributes *attributes, GimpMetadata *metadata)
+{
+ const gchar *tag_type;
+ GimpAttribute *attribute;
+ GimpAttributeValueType attrib_type;
+ gint i;
+ GimpAttributes *new_attributes = NULL;
+
+ g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), NULL);
+
+ if (!attributes)
+ {
+ new_attributes = gimp_attributes_new ();
+ }
+ else
+ {
+ new_attributes = gimp_attributes_duplicate (attributes);
+ g_object_unref (attributes);
+ }
+
+ if (new_attributes)
+ {
+ gchar **exif_data;
+ gchar **xmp_data;
+ gchar **iptc_data;
+ gboolean no_interpreted = TRUE; /*FIXME: No interpreted String possible */
+
+ exif_data = gexiv2_metadata_get_exif_tags (metadata);
+
+ for (i = 0; exif_data[i] != NULL; i++)
+ {
+ gchar *interpreted_value = NULL;
+ gchar *value = NULL;
+ gboolean interpreted = FALSE;
+
+ value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
+ interpreted_value = gexiv2_metadata_get_tag_interpreted_string (metadata, exif_data[i]);
+ tag_type = gexiv2_metadata_get_tag_type (exif_data[i]);
+ attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+
+ interpreted = g_strcmp0 (value, interpreted_value);
+
+ attribute = gimp_attribute_new_string (exif_data[i], value, attrib_type);
+ if (gimp_attribute_is_valid (attribute))
+ {
+ if (no_interpreted)
+ {
+ if (interpreted)
+ gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+ }
+
+ gimp_attributes_add_attribute (new_attributes, attribute);
+ }
+ else
+ {
+ g_object_unref (attribute);
+ }
+
+ g_free (interpreted_value);
+ g_free (value);
+ }
+
+ g_strfreev (exif_data);
+
+ xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
+
+ for (i = 0; xmp_data[i] != NULL; i++)
+ {
+ gchar *interpreted_value = NULL;
+ gchar *value = NULL;
+ gboolean interpreted = FALSE;
+
+ value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
+ interpreted_value = gexiv2_metadata_get_tag_interpreted_string (metadata, xmp_data[i]);
+ tag_type = gexiv2_metadata_get_tag_type (xmp_data[i]);
+ attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+
+ interpreted = g_strcmp0 (value, interpreted_value);
+
+ attribute = gimp_attribute_new_string (xmp_data[i], value, attrib_type);
+
+ if (gimp_attribute_is_valid (attribute))
+ {
+ if (no_interpreted)
+ {
+ if (interpreted)
+ gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+ }
+ gimp_attributes_add_attribute (new_attributes, attribute);
+ }
+ else
+ {
+ g_object_unref (attribute);
+ }
+
+ g_free (value);
+ }
+
+ g_strfreev (xmp_data);
+
+ iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
+
+ for (i = 0; iptc_data[i] != NULL; i++)
+ {
+ gchar *interpreted_value = NULL;
+ gchar *value = NULL;
+ gboolean interpreted = FALSE;
+
+ value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
+ interpreted_value = gexiv2_metadata_get_tag_interpreted_string (metadata, iptc_data[i]);
+ tag_type = gexiv2_metadata_get_tag_type (iptc_data[i]);
+ attrib_type = gimp_attribute_get_value_type_from_string (tag_type);
+
+ interpreted = g_strcmp0 (value, interpreted_value);
+
+ attribute = gimp_attribute_new_string (iptc_data[i], value, attrib_type);
+ if (gimp_attribute_is_valid (attribute))
+ {
+ if (no_interpreted)
+ {
+ if (interpreted)
+ gimp_attribute_set_interpreted_string (attribute, interpreted_value);
+ }
+ gimp_attributes_add_attribute (new_attributes, attribute);
+ }
+ else
+ {
+ g_object_unref (attribute);
+ }
+
+ g_free (value);
+ }
+
+ g_strfreev (iptc_data);
+ }
+
+ return new_attributes;
+}
+
+/**
+ * gimp_attributes_print:
+ * @attributes: The #GimpAttributes
+ *
+ * prints out information of attributes
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_attributes_print (GimpAttributes *attributes)
+{
+ gint i;
+ GList *key_list = NULL;
+
+
+ g_return_if_fail (GIMP_IS_ATTRIBUTES (attributes));
+
+ i = 0;
+
+ for (key_list = attributes->priv->sorted_key_list; key_list; key_list = key_list->next)
+ {
+ const gchar *tag;
+ const gchar *interpreted;
+ gchar *value;
+ GimpAttribute *attribute;
+ gchar *p_key = (gchar *) key_list->data;
+
+ attribute = gimp_attributes_get_attribute (attributes, p_key);
+
+ if (! attribute)
+ continue;
+
+ i++;
+
+ tag = gimp_attribute_get_name (attribute);
+ value = gimp_attribute_get_string (attribute);
+ interpreted = gimp_attribute_get_interpreted_string (attribute);
+
+ g_print ("%p: %s\n%04d. Tag: %s\n\tValue:%s\n\tInterpreted value:%s\n", attribute, p_key, i, tag,
value, interpreted);
+
+ if (value)
+ g_free (value);
+ }
+}
+
+/**
+ * gimp_attributes_to_metadata:
+ * @attributes: The #GimpAttributes
+ * @metadata: The #GimpMetadata
+ * @mime_type: the mime type of the image
+ *
+ * Converts @attributes to @metadata in gexiv2 format
+ *
+ * Since: GIMP 2.10
+ */
+void
+gimp_attributes_to_metadata (GimpAttributes *attributes,
+ GimpMetadata *metadata,
+ const gchar *mime_type)
+{
+ gchar *o_packet = NULL;
+ gboolean write_tag = FALSE;
+ gboolean namespace = FALSE;
+ gboolean check_mime = TRUE;
+ gboolean support_exif;
+ gboolean support_xmp;
+ gboolean support_iptc;
+ GSList *xmp_structure_list = NULL;
+ GList *key_list = NULL;
+ gint i;
+
+
+ g_return_if_fail (GIMP_IS_ATTRIBUTES (attributes));
+ g_return_if_fail (GEXIV2_IS_METADATA (metadata));
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ gexiv2_metadata_register_xmp_namespace (n_space.namespace_URI, n_space.namespace_name);
+ }
+
+ support_exif = gexiv2_metadata_get_supports_exif (metadata);
+ support_xmp = gexiv2_metadata_get_supports_xmp (metadata);
+ support_iptc = gexiv2_metadata_get_supports_iptc (metadata);
+
+ for (key_list = attributes->priv->sorted_key_list; key_list; key_list = key_list->next)
+ {
+ const gchar *tag;
+ const gchar *ns_name;
+ gchar *p_key = (gchar *) key_list->data;
+ gchar *value = NULL;
+ gchar *category = NULL;
+ gboolean is_xmp = FALSE;
+ gboolean has_structure = FALSE;
+ GimpAttribute *attribute;
+ GimpAttributeValueType tag_value_type;
+ GError *error = NULL;
+
+ write_tag = FALSE;
+ namespace = FALSE;
+
+ attribute = gimp_attributes_get_attribute (attributes, p_key);
+
+ if (! attribute)
+ continue;
+
+ tag = gimp_attribute_get_name (attribute);
+ has_structure = gimp_attribute_has_structure (attribute);
+
+ if (mime_type)
+ check_mime = gimp_metadata_is_tag_supported (tag, mime_type);
+
+ if (check_mime)
+ {
+ gchar *t_packet = NULL;
+
+ tag_value_type = gimp_attribute_get_value_type (attribute);
+ value = gimp_attribute_get_string (attribute);
+ category = g_ascii_strdown (gimp_attribute_get_attribute_type (attribute), -1);
+
+ if (tag && value && category)
+ {
+ if(! g_strcmp0 (category, "exif") && support_exif)
+ {
+ write_tag = TRUE;
+ }
+ else if(! g_strcmp0 (category, "xmp") && support_xmp)
+ {
+ write_tag = TRUE;
+ is_xmp = TRUE;
+
+ namespace = gimp_attribute_is_new_namespace (attribute);
+
+ if (namespace)
+ {
+ write_tag = FALSE;
+
+ ns_name = gimp_attribute_get_attribute_ifd (attribute);
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ if(! g_strcmp0 (ns_name, n_space.namespace_name))
+ {
+ write_tag = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (write_tag && has_structure)
+ {
+ gboolean success = TRUE;
+ GSList *structure;
+ GSList *list;
+ GimpAttributeStructureType structure_type;
+
+ structure = gimp_attribute_get_attribute_structure (attribute);
+ structure_type = gimp_attribute_get_structure_type (attribute);
+
+ for (list = structure; list; list = list->next)
+ {
+ const gchar *structure_element = (const gchar*) list->data;
+ gboolean has_tag = gexiv2_metadata_has_tag (metadata, structure_element);
+
+ if (!has_tag && ! has_xmp_structure (xmp_structure_list, structure_element))
+ {
+ switch (structure_type)
+ {
+ case STRUCTURE_TYPE_ALT:
+ success = gexiv2_metadata_set_xmp_tag_struct (metadata, structure_element,
GEXIV2_STRUCTURE_XA_ALT); /*start block*/
+ break;
+ case STRUCTURE_TYPE_BAG:
+ success = gexiv2_metadata_set_xmp_tag_struct (metadata, structure_element,
GEXIV2_STRUCTURE_XA_BAG); /*start block*/
+ break;
+ case STRUCTURE_TYPE_SEQ:
+ success = gexiv2_metadata_set_xmp_tag_struct (metadata, structure_element,
GEXIV2_STRUCTURE_XA_SEQ); /*start block*/
+ break;
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (success)
+ xmp_structure_list = g_slist_prepend (xmp_structure_list,
(gpointer)structure_element);
+ }
+ }
+ }
+ }
+ else if(! g_strcmp0 (category, "iptc") && support_iptc)
+ {
+ write_tag = TRUE;
+ }
+ else
+ {
+ write_tag = FALSE;
+ }
+
+ if (write_tag)
+ {
+ switch (tag_value_type)
+ {
+ case TYPE_INVALID:
+ break;
+ case TYPE_LONG:
+ case TYPE_SLONG:
+ case TYPE_FLOAT:
+ case TYPE_DOUBLE:
+ case TYPE_SHORT:
+ case TYPE_SSHORT:
+ case TYPE_DATE:
+ case TYPE_TIME:
+ case TYPE_ASCII:
+ case TYPE_UNICODE:
+ case TYPE_BYTE:
+ case TYPE_RATIONAL:
+ case TYPE_SRATIONAL:
+ {
+ gexiv2_metadata_set_tag_string (metadata, tag, value);
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ GValue h;
+ gchar **values;
+
+ h = gimp_attribute_get_value (attribute);
+ values = (gchar **) g_value_get_boxed (&h);
+ gexiv2_metadata_set_tag_multiple (metadata, tag, (const gchar **) values);
+ g_strfreev (values);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+
+ }
+
+ if (is_xmp)
+ {
+ t_packet = gexiv2_metadata_generate_xmp_packet (metadata, useCompactFormat |
omitAllFormatting, 0, &error);
+ if (error)
+ {
+ g_print ("error: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ if (! g_strcmp0 (t_packet, o_packet))
+ {
+ gexiv2_metadata_clear_tag (metadata, tag);
+ g_print ("cleared to metadata:\n%s, %s\n", tag, value);
+ }
+ else
+ {
+ o_packet = g_strdup (t_packet);
+ }
+ }
+ }
+ }
+
+ if (t_packet)
+ g_free (t_packet);
+ if (value)
+ g_free (value);
+ if (category)
+ g_free (category);
+ }
+ }
+
+ if (o_packet)
+ g_free (o_packet);
+ if (xmp_structure_list)
+ g_slist_free (xmp_structure_list);
+}
+
+
+#if 0
+/**
+ * gimp_attributes_to_xmp_packet:
+ * @attributes: The #GimpAttributes
+ * @mime_type : a mime_type
+ *
+ * placeholder, until gexiv2 can generate xmp packet.
+ * always returns NULL
+ *
+ * Since: GIMP 2.10
+ */
+const gchar *
+gimp_attributes_to_xmp_packet (GimpAttributes *attributes,
+ const gchar *mime_type)
+
+{
+ return NULL;
+}
+#endif
+
+/**
+ * gimp_attributes_to_xmp_packet:
+ * @attributes: The #GimpAttributes
+ * @mime_type : a mime_type
+ *
+ * Converts @attributes to a xmp packet
+ * It looks like an ugly hack, but let
+ * gexiv2/exiv2 do all the hard work.
+ *
+ * Return value: a #gchar*, representing a xml packet.
+ *
+ * Since: GIMP 2.10
+ */
+const gchar *
+gimp_attributes_to_xmp_packet (GimpAttributes *attributes,
+ const gchar *mime_type)
+
+{
+ gint i;
+ const gchar *packet_string;
+ gchar *o_packet = NULL;
+ gboolean check_mime = TRUE;
+ gboolean write_tag = FALSE;
+ gboolean namespace = FALSE;
+ gboolean support_exif;
+ gboolean support_xmp;
+ gboolean support_iptc;
+ GimpMetadata *metadata;
+ GSList *xmp_structure_list = NULL;
+ GList *key_list = NULL;
+ GError *error = NULL;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTES (attributes), NULL);
+
+ metadata = gimp_metadata_new ();
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ gexiv2_metadata_register_xmp_namespace (n_space.namespace_URI, n_space.namespace_name);
+ }
+
+ support_exif = gexiv2_metadata_get_supports_exif (metadata);
+ support_xmp = gexiv2_metadata_get_supports_xmp (metadata);
+ support_iptc = gexiv2_metadata_get_supports_iptc (metadata);
+
+ for (key_list = attributes->priv->sorted_key_list; key_list; key_list = key_list->next)
+ {
+ gchar *p_key = (gchar *) key_list->data;
+
+ const gchar *tag;
+ const gchar *attribute_tag;
+ const gchar *ns_name;
+ gchar *new_tag = NULL;
+ gchar *value = NULL;
+ gchar *category = NULL;
+ gboolean has_structure;
+ gboolean temp_attribute = FALSE;
+ GimpAttribute *attribute;
+ GimpAttributeValueType tag_value_type;
+
+ write_tag = FALSE;
+ namespace = FALSE;
+
+ attribute = gimp_attributes_get_attribute (attributes, p_key);
+
+ if (! attribute)
+ continue;
+
+ tag = gimp_attribute_get_name (attribute);
+ attribute_tag = gimp_attribute_get_attribute_tag (attribute);
+ has_structure = gimp_attribute_has_structure (attribute);
+
+ if (mime_type)
+ check_mime = gimp_metadata_is_tag_supported (tag, mime_type);
+
+ if (check_mime)
+ {
+ gchar *sec_tag = NULL;
+ gchar *t_packet = NULL;
+
+ tag_value_type = gimp_attribute_get_value_type (attribute);
+ value = gimp_attribute_get_string (attribute);
+ category = g_ascii_strdown (gimp_attribute_get_attribute_type (attribute), -1);
+
+ if (tag && value && category)
+ {
+ if(! g_strcmp0 (category, "exif") && support_exif)
+ {
+ new_tag = g_strdup_printf ("Xmp.exif.%s", attribute_tag);
+
+ write_tag = TRUE;
+
+// /* Now for some specialities */
+// if (! g_strcmp0 (new_tag, "Xmp.exif.ISOSpeedRatings"))
+// {
+// g_print ("ungültig\n");
+// attribute = gimp_attribute_new_string ("Xmp.exif.ISOSpeedRatings", value,
TYPE_ASCII);
+// if (attribute)
+// {
+// temp_attribute = TRUE;
+// tag_value_type = TYPE_ASCII;
+// }
+// else
+// {
+// write_tag = FALSE;
+// }
+// }
+ }
+ else if(! g_strcmp0 (category, "xmp") && support_xmp)
+ {
+ new_tag = g_strdup_printf ("%s", tag);
+
+ write_tag = TRUE;
+
+ namespace = gimp_attribute_is_new_namespace (attribute);
+
+ if (namespace)
+ {
+ write_tag = FALSE;
+
+ ns_name = gimp_attribute_get_attribute_ifd (attribute);
+
+ for (i = 0; i < G_N_ELEMENTS (namespaces_table); i++)
+ {
+ struct Namespaces n_space = namespaces_table[i];
+ if(! g_strcmp0 (ns_name, n_space.namespace_name))
+ {
+ write_tag = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (write_tag && has_structure)
+ {
+ gboolean success = TRUE;
+ GSList *structure;
+ GSList *list;
+ GimpAttributeStructureType structure_type;
+
+ structure = gimp_attribute_get_attribute_structure (attribute);
+ structure_type = gimp_attribute_get_structure_type (attribute);
+
+ for (list = structure; list; list = list->next)
+ {
+ const gchar *structure_element = (const gchar*) list->data;
+ gboolean has_tag = gexiv2_metadata_has_tag (metadata, structure_element);
+
+ if (!has_tag && ! has_xmp_structure (xmp_structure_list, structure_element))
+ {
+ switch (structure_type)
+ {
+ case STRUCTURE_TYPE_ALT:
+ success = gexiv2_metadata_set_xmp_tag_struct (metadata, structure_element,
GEXIV2_STRUCTURE_XA_ALT); /*start block*/
+ break;
+ case STRUCTURE_TYPE_BAG:
+ success = gexiv2_metadata_set_xmp_tag_struct (metadata, structure_element,
GEXIV2_STRUCTURE_XA_BAG); /*start block*/
+ break;
+ case STRUCTURE_TYPE_SEQ:
+ success = gexiv2_metadata_set_xmp_tag_struct (metadata, structure_element,
GEXIV2_STRUCTURE_XA_SEQ); /*start block*/
+ break;
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (success)
+ xmp_structure_list = g_slist_prepend (xmp_structure_list,
(gpointer)structure_element);
+ }
+ }
+ }
+ }
+ else if(! g_strcmp0 (category, "iptc") && support_iptc)
+ {
+ new_tag = g_strdup_printf ("Xmp.iptc.%s", attribute_tag);
+ sec_tag = g_strdup_printf ("Xmp.iptcExt.%s", attribute_tag);
+ write_tag = TRUE;
+ }
+ else
+ {
+ write_tag = FALSE;
+ }
+
+ if (write_tag)
+ {
+ switch (tag_value_type)
+ {
+ case TYPE_INVALID:
+ break;
+ case TYPE_LONG:
+ case TYPE_SLONG:
+ case TYPE_FLOAT:
+ case TYPE_DOUBLE:
+ case TYPE_SHORT:
+ case TYPE_SSHORT:
+ case TYPE_DATE:
+ case TYPE_TIME:
+ case TYPE_ASCII:
+ case TYPE_UNICODE:
+ case TYPE_BYTE:
+ case TYPE_RATIONAL:
+ case TYPE_SRATIONAL:
+ {
+ gexiv2_metadata_set_tag_string (metadata, new_tag, value);
+ if (sec_tag)
+ {
+ gexiv2_metadata_set_tag_string (metadata, sec_tag, value);
+ g_free (sec_tag);
+ }
+
+ }
+ break;
+ case TYPE_MULTIPLE:
+ {
+ GValue h;
+ gchar **values;
+
+ h = gimp_attribute_get_value (attribute);
+ values = (gchar **) g_value_get_boxed (&h);
+ gexiv2_metadata_set_tag_multiple (metadata, new_tag, (const gchar **) values);
+ if (sec_tag)
+ {
+ gexiv2_metadata_set_tag_multiple (metadata, sec_tag, (const gchar **) values);
+ g_free (sec_tag);
+ }
+ g_strfreev (values);
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ break;
+
+ }
+
+ t_packet = gexiv2_metadata_generate_xmp_packet (metadata, useCompactFormat |
omitAllFormatting, 0, &error);
+
+ if (! t_packet || ! g_strcmp0 (t_packet, o_packet))
+ {
+ gexiv2_metadata_clear_tag (metadata, new_tag);
+ }
+ else
+ {
+ o_packet = g_strdup (t_packet);
+ }
+ }
+ }
+
+ if (t_packet)
+ g_free (t_packet);
+ if (value)
+ g_free (value);
+ if (category)
+ g_free (category);
+ if (new_tag)
+ g_free (new_tag);
+
+ if (temp_attribute)
+ g_object_unref (attribute);
+ }
+ }
+
+ if (o_packet)
+ g_free (o_packet);
+ if (xmp_structure_list)
+ g_slist_free (xmp_structure_list);
+
+ packet_string = gexiv2_metadata_generate_xmp_packet (metadata, useCompactFormat | writeAliasComments, 0,
&error);
+ return packet_string;
+}
+
+/**
+ * gimp_attributes_has_tag_type:
+ * @attributes: The attributes
+ * @tag_type: The #GimpAttributeTagType to test
+ *
+ * tests, if @attributes contains at least one tag of @tag_type
+ *
+ * Return value: TRUE if found, FALSE otherwise
+ *
+ * Since: GIMP 2.10
+ */
+gboolean
+gimp_attributes_has_tag_type (GimpAttributes *attributes,
+ GimpAttributeTagType tag_type)
+{
+ GHashTableIter iter;
+ gpointer p_key, p_value;
+
+ g_return_val_if_fail (GIMP_IS_ATTRIBUTES (attributes), FALSE);
+
+ g_hash_table_iter_init (&iter, attributes->priv->attribute_table);
+
+ while (g_hash_table_iter_next (&iter, &p_key, &p_value))
+ {
+ GimpAttribute *attribute;
+
+ attribute = (GimpAttribute *) p_value;
+
+ if (gimp_attribute_get_tag_type (attribute) == tag_type)
+ return TRUE;
+
+ }
+ return FALSE;
+}
+
+/**
+ * gimp_attributes_error_quark:
+ *
+ * Return value: #GQuark
+ *
+ * Since: GIMP 2.10
+ */
+static GQuark
+gimp_attributes_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("gimp-attributes-error-quark");
+
+ return quark;
+}
+
+/**
+ * gimp_attributes_deserialize_error:
+ *
+ * Error while parsing
+ *
+ * Since: GIMP 2.10
+ */
+static void
+gimp_attributes_deserialize_error (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+ g_printerr ("XML parse error: %s\n", error->message);
+}
+
+/**
+ * gimp_attributes_name_to_value:
+ *
+ * @attribute_names : #gchar **
+ * @attribute_values : #gchar **
+ * @name : #gchar *
+ *
+ * searches for values in @attribute_values by a given @name (parsing xml)
+ *
+ * Since: GIMP 2.10
+ */
+static const gchar*
+gimp_attributes_name_to_value (const gchar **attribute_names,
+ const gchar **attribute_values,
+ const gchar *name)
+{
+ while (*attribute_names)
+ {
+ if (! strcmp (*attribute_names, name))
+ {
+ return *attribute_values;
+ }
+
+ attribute_names++;
+ attribute_values++;
+ }
+
+ return NULL;
+}
+
+/**
+ * gimp_attributes_deserialize_start_element:
+ *
+ * @context : #GMarkupParseContext
+ * @element_name : #gchar *
+ * @attribute_names : #gchar **
+ * @attribute_values : #gchar **
+ * @user_data : #gpointer to #GimpAttributesParseData struct
+ * @error : #GError **
+ *
+ * start of a tag (parsing xml)
+ *
+ * Since: GIMP 2.10
+ */
+static void
+gimp_attributes_deserialize_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ GimpAttributesParseData *parse_data = user_data;
+
+ if (! strcmp (element_name, "tag"))
+ {
+ const gchar *name = NULL;
+ const gchar *type = NULL;
+
+ name = gimp_attributes_name_to_value (attribute_names,
+ attribute_values,
+ "name");
+ type = gimp_attributes_name_to_value (attribute_names,
+ attribute_values,
+ "type");
+
+ if (! name)
+ {
+ g_set_error (error, gimp_attributes_error_quark (), 1001,
+ "Element 'tag' does not contain required attribute 'name'.");
+ return;
+ }
+
+ if (! type)
+ {
+ g_set_error (error, gimp_attributes_error_quark (), 1001,
+ "Element 'tag' does not contain required attribute 'type'.");
+ return;
+ }
+
+ strncpy (parse_data->name, name, sizeof (parse_data->name));
+ parse_data->name[sizeof (parse_data->name) - 1] = 0;
+
+ parse_data->type = (GimpAttributeValueType) atoi (type);
+ }
+ else if (! strcmp (element_name, "value"))
+ {
+ const gchar *encoding = NULL;
+
+ encoding = gimp_attributes_name_to_value (attribute_names,
+ attribute_values,
+ "encoding");
+
+ parse_data->base64 = (encoding && ! strcmp (encoding, "base64"));
+ }
+ else if (! strcmp (element_name, "interpreted"))
+ {
+ const gchar *encoding = NULL;
+
+ encoding = gimp_attributes_name_to_value (attribute_names,
+ attribute_values,
+ "encoding");
+
+ parse_data->base64 = (encoding && ! strcmp (encoding, "base64"));
+ }
+
+}
+
+/**
+ * gimp_attributes_deserialize_text:
+ *
+ * @context : #GMarkupParseContext
+ * @text : const #gchar *
+ * @text_len : #gsize
+ * @user_data : #gpointer to #GimpAttributesParseData struct
+ * @error : #GError **
+ *
+ * text of a tag (parsing xml)
+ *
+ * Since: GIMP 2.10
+ */
+static void
+gimp_attributes_deserialize_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ GimpAttributesParseData *parse_data = user_data;
+ GimpAttribute *attribute;
+ const gchar *current_element;
+
+ current_element = g_markup_parse_context_get_element (context);
+
+ if (! g_strcmp0 (current_element, "value"))
+ {
+ gchar *value = g_strndup (text, text_len);
+
+ if (parse_data->base64)
+ {
+ guchar *decoded;
+ gsize len;
+
+ decoded = g_base64_decode (value, &len);
+
+ if (decoded[len - 1] == '\0')
+ attribute = gimp_attribute_new_string (parse_data->name,
+ (gchar *)decoded,
+ parse_data->type);
+
+ g_free (decoded);
+ }
+ else
+ {
+ attribute = gimp_attribute_new_string (parse_data->name,
+ value,
+ parse_data->type);
+ }
+
+ g_free (value);
+
+ if (gimp_attribute_is_valid (attribute))
+ {
+ gimp_attributes_add_attribute (parse_data->attributes, attribute);
+ current_attribute = attribute;
+ }
+ else
+ g_object_unref (attribute);
+
+ }
+ else if (! g_strcmp0 (current_element, "structure"))
+ {
+ GimpAttribute *attribute = NULL;
+ gchar *value = NULL;
+
+ attribute = current_attribute;
+
+ if (attribute)
+ {
+ gint v;
+ GimpAttributeStructureType struct_type = STRUCTURE_TYPE_BAG;
+
+ value = g_strndup (text, text_len);
+
+ v = atoi (value);
+
+ if (v > -1)
+ struct_type = (GimpAttributeStructureType) v;
+
+ gimp_attribute_set_structure_type (attribute, struct_type);
+
+ g_free (value);
+ }
+ }
+ else if (! g_strcmp0 (current_element, "interpreted"))
+ {
+ GimpAttribute *attribute = NULL;
+ gchar *value = NULL;
+
+ attribute = current_attribute;
+
+ if (attribute)
+ {
+ value = g_strndup (text, text_len);
+
+ if (parse_data->base64)
+ {
+ guchar *decoded = NULL;
+ gsize len;
+
+ decoded = g_base64_decode (value, &len);
+
+ if (decoded[len - 1] == '\0')
+ gimp_attribute_set_interpreted_string (attribute, (const gchar *)decoded);
+
+ g_free (decoded);
+ }
+ else
+ {
+ gimp_attribute_set_interpreted_string (attribute, (const gchar *)value);
+ }
+ g_free (value);
+ }
+ }
+}
+
+/**
+ * gimp_attributes_deserialize_end_element:
+ *
+ * @context : #GMarkupParseContext
+ * @element_name : #gchar *
+ * @user_data : #gpointer to #GimpAttributesParseData struct
+ * @error : #GError **
+ *
+ * end of a tag (parsing xml)
+ *
+ * Since: GIMP 2.10
+ */
+static void
+gimp_attributes_deserialize_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ GimpAttributesParseData *parse_data = user_data;
+
+ if (! strcmp (element_name, "tag"))
+ {
+ current_attribute = NULL;
+ }
+ else if (! strcmp (element_name, "value"))
+ {
+ parse_data->base64 = FALSE;
+ }
+ else if (! strcmp (element_name, "interpreted"))
+ {
+ parse_data->base64 = FALSE;
+ }
+}
+
+static gboolean
+has_xmp_structure (GSList *xmp_list, const gchar *entry)
+{
+ GSList *list;
+
+ for (list = xmp_list; list; list = list->next)
+ {
+ const gchar *to_test = (const gchar*) list->data;
+
+ if (! g_strcmp0 (to_test, entry))
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/libgimpbase/gimpattributes.h b/libgimpbase/gimpattributes.h
new file mode 100644
index 0000000..86bbe26
--- /dev/null
+++ b/libgimpbase/gimpattributes.h
@@ -0,0 +1,74 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimpattributes.h
+ * Copyright (C) 2014 Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __GIMPATTRIBUTES_H__
+#define __GIMPATTRIBUTES_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_ATTRIBUTES (gimp_attributes_get_type ())
+#define GIMP_ATTRIBUTES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ATTRIBUTES,
GimpAttributes))
+#define GIMP_ATTRIBUTES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ATTRIBUTES,
GimpAttributesClass))
+#define GIMP_IS_ATTRIBUTES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ATTRIBUTES))
+#define GIMP_IS_ATTRIBUTES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ATTRIBUTES))
+#define GIMP_ATTRIBUTES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ATTRIBUTES,
GimpAttributesClass))
+#define GIMP_ATTRIBUTES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GIMP_TYPE_ATTRIBUTES,
GimpAttributesPrivate))
+
+
+GType gimp_attributes_get_type (void)
G_GNUC_CONST;
+GimpAttributes * gimp_attributes_new (void);
+gint gimp_attributes_size
(GimpAttributes *attributes);
+GimpAttributes * gimp_attributes_duplicate
(GimpAttributes *attributes);
+GHashTable * gimp_attributes_get_table
(GimpAttributes *attributes);
+void gimp_attributes_add_attribute
(GimpAttributes *attributes,
+
GimpAttribute *attribute);
+GimpAttribute * gimp_attributes_get_attribute
(GimpAttributes *attributes,
+ const
gchar *name);
+gboolean gimp_attributes_remove_attribute
(GimpAttributes *attributes,
+ const
gchar *name);
+gboolean gimp_attributes_has_attribute
(GimpAttributes *attributes,
+ const
gchar *name);
+gboolean gimp_attributes_new_attribute
(GimpAttributes *attributes,
+ const
gchar *name,
+ gchar
*value,
+
GimpAttributeValueType
+
type);
+
+GimpAttributes * gimp_attributes_from_metadata
(GimpAttributes *attributes,
+
GimpMetadata *metadata);
+void gimp_attributes_to_metadata
(GimpAttributes *attributes,
+
GimpMetadata *metadata,
+ const
gchar *mime_type);
+const gchar * gimp_attributes_to_xmp_packet
(GimpAttributes *attributes,
+ const
gchar *mime_type);
+gchar * gimp_attributes_serialize
(GimpAttributes *attributes);
+GimpAttributes * gimp_attributes_deserialize (const
gchar *xml);
+gboolean gimp_attributes_has_tag_type
(GimpAttributes *attributes,
+
GimpAttributeTagType tag_type);
+void gimp_attributes_print
(GimpAttributes *attributes);
+
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/libgimpbase/gimpbase.def b/libgimpbase/gimpbase.def
index 4832eee..a4befd3 100644
--- a/libgimpbase/gimpbase.def
+++ b/libgimpbase/gimpbase.def
@@ -1,6 +1,47 @@
EXPORTS
gimp_add_mask_type_get_type
gimp_any_to_utf8
+ gimp_attribute_copy
+ gimp_attribute_get_attribute_type
+ gimp_attribute_get_attribute_ifd
+ gimp_attribute_get_attribute_structure
+ gimp_attribute_get_attribute_tag
+ gimp_attribute_get_interpreted_string
+ gimp_attribute_get_name
+ gimp_attribute_get_string
+ gimp_attribute_get_structure_type
+ gimp_attribute_get_tag_type
+ gimp_attribute_get_value
+ gimp_attribute_get_value_type
+ gimp_attribute_get_value_type_from_string
+ gimp_attribute_get_xml
+ gimp_attribute_has_structure
+ gimp_attribute_is_valid
+ gimp_attribute_new_string
+ gimp_attribute_print
+ gimp_attribute_set_interpreted_string
+ gimp_attribute_set_structure_type
+ gimp_attribute_set_value_type
+ gimp_attributes_add_attribute
+ gimp_attributes_deserialize
+ gimp_attributes_duplicate
+ gimp_attributes_from_metadata
+ gimp_attributes_get_attribute
+ gimp_attributes_get_resolution
+ gimp_attributes_get_table
+ gimp_attributes_has_tag_type
+ gimp_attributes_new
+ gimp_attributes_new_attribute
+ gimp_attributes_print
+ gimp_attributes_remove_attribute
+ gimp_attributes_has_attribute
+ gimp_attributes_serialize
+ gimp_attributes_set_bits_per_sample
+ gimp_attributes_set_pixel_size
+ gimp_attributes_set_resolution
+ gimp_attributes_size
+ gimp_attributes_to_metadata
+ gimp_attributes_to_xmp_packet
gimp_base_init
gimp_blend_mode_get_type
gimp_brush_generated_shape_get_type
@@ -66,19 +107,12 @@ EXPORTS
gimp_memsize_to_string
gimp_merge_type_get_type
gimp_message_handler_type_get_type
- gimp_metadata_deserialize
- gimp_metadata_duplicate
- gimp_metadata_get_resolution
gimp_metadata_is_tag_supported
gimp_metadata_load_from_file
gimp_metadata_new
gimp_metadata_save_to_file
- gimp_metadata_serialize
- gimp_metadata_set_bits_per_sample
gimp_metadata_set_from_exif
gimp_metadata_set_from_xmp
- gimp_metadata_set_pixel_size
- gimp_metadata_set_resolution
gimp_micro_version
gimp_minor_version
gimp_offset_type_get_type
@@ -203,3 +237,5 @@ EXPORTS
gp_tile_ack_write
gp_tile_data_write
gp_tile_req_write
+ rational_free
+ srational_free
diff --git a/libgimpbase/gimpbase.h b/libgimpbase/gimpbase.h
index a5682e7..35bace3 100644
--- a/libgimpbase/gimpbase.h
+++ b/libgimpbase/gimpbase.h
@@ -23,6 +23,9 @@
#include <libgimpbase/gimpbasetypes.h>
+#include <libgimpbase/gimpattribute.h>
+#include <libgimpbase/gimpattributes.h>
+#include <libgimpbase/gimpattributes-image.h>
#include <libgimpbase/gimpchecks.h>
#include <libgimpbase/gimpcpuaccel.h>
#include <libgimpbase/gimpdatafiles.h>
diff --git a/libgimpbase/gimpbasetypes.h b/libgimpbase/gimpbasetypes.h
index bef9904..85a699c 100644
--- a/libgimpbase/gimpbasetypes.h
+++ b/libgimpbase/gimpbasetypes.h
@@ -45,6 +45,8 @@ G_BEGIN_DECLS
#endif
+typedef struct _GimpAttribute GimpAttribute;
+typedef struct _GimpAttributes GimpAttributes;
typedef struct _GimpParasite GimpParasite;
typedef struct _GimpDatafileData GimpDatafileData;
typedef struct _GimpEnumDesc GimpEnumDesc;
diff --git a/libgimpbase/gimpmetadata.c b/libgimpbase/gimpmetadata.c
index 73ff2fc..fab2842 100644
--- a/libgimpbase/gimpmetadata.c
+++ b/libgimpbase/gimpmetadata.c
@@ -92,7 +92,6 @@ static const gchar *unsupported_tags[] =
"Exif.Image.ClipPath",
"Exif.Image.XClipPathUnits",
"Exif.Image.YClipPathUnits",
- "Xmp.xmpMM.History",
"Exif.Image.XPTitle",
"Exif.Image.XPComment",
"Exif.Image.XPAuthor",
@@ -100,8 +99,7 @@ static const gchar *unsupported_tags[] =
"Exif.Image.XPSubject",
"Exif.Image.DNGVersion",
"Exif.Image.DNGBackwardVersion",
- "Exif.Iop"
-};
+ "Exif.Iop"};
static const guint8 minimal_exif[] =
{
@@ -173,35 +171,6 @@ gimp_metadata_new (void)
return metadata;
}
-/**
- * gimp_metadata_duplicate:
- * @metadata: The object to duplicate, or %NULL.
- *
- * Duplicates a #GimpMetadata instance.
- *
- * Return value: The new #GimpMetadata, or %NULL if @metadata is %NULL.
- *
- * Since: GIMP 2.10
- */
-GimpMetadata *
-gimp_metadata_duplicate (GimpMetadata *metadata)
-{
- GimpMetadata *new_metadata = NULL;
-
- g_return_val_if_fail (metadata == NULL || GEXIV2_IS_METADATA (metadata), NULL);
-
- if (metadata)
- {
- gchar *xml;
-
- xml = gimp_metadata_serialize (metadata);
- new_metadata = gimp_metadata_deserialize (xml);
- g_free (xml);
- }
-
- return new_metadata;
-}
-
typedef struct
{
gchar name[1024];
@@ -209,290 +178,6 @@ typedef struct
GimpMetadata *metadata;
} GimpMetadataParseData;
-static const gchar*
-gimp_metadata_attribute_name_to_value (const gchar **attribute_names,
- const gchar **attribute_values,
- const gchar *name)
-{
- while (*attribute_names)
- {
- if (! strcmp (*attribute_names, name))
- {
- return *attribute_values;
- }
-
- attribute_names++;
- attribute_values++;
- }
-
- return NULL;
-}
-
-static void
-gimp_metadata_deserialize_start_element (GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- gpointer user_data,
- GError **error)
-{
- GimpMetadataParseData *parse_data = user_data;
-
- if (! strcmp (element_name, "tag"))
- {
- const gchar *name;
- const gchar *encoding;
-
- name = gimp_metadata_attribute_name_to_value (attribute_names,
- attribute_values,
- "name");
- encoding = gimp_metadata_attribute_name_to_value (attribute_names,
- attribute_values,
- "encoding");
-
- if (! name)
- {
- g_set_error (error, gimp_metadata_error_quark (), 1001,
- "Element 'tag' does not contain required attribute 'name'.");
- return;
- }
-
- strncpy (parse_data->name, name, sizeof (parse_data->name));
- parse_data->name[sizeof (parse_data->name) - 1] = 0;
-
- parse_data->base64 = (encoding && ! strcmp (encoding, "base64"));
- }
-}
-
-static void
-gimp_metadata_deserialize_end_element (GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error)
-{
-}
-
-static void
-gimp_metadata_deserialize_text (GMarkupParseContext *context,
- const gchar *text,
- gsize text_len,
- gpointer user_data,
- GError **error)
-{
- GimpMetadataParseData *parse_data = user_data;
- const gchar *current_element;
-
- current_element = g_markup_parse_context_get_element (context);
-
- if (! g_strcmp0 (current_element, "tag"))
- {
- gchar *value = g_strndup (text, text_len);
-
- if (parse_data->base64)
- {
- guchar *decoded;
- gsize len;
-
- decoded = g_base64_decode (value, &len);
-
- if (decoded[len - 1] == '\0')
- gexiv2_metadata_set_tag_string (parse_data->metadata,
- parse_data->name,
- (const gchar *) decoded);
-
- g_free (decoded);
- }
- else
- {
- gexiv2_metadata_set_tag_string (parse_data->metadata,
- parse_data->name,
- value);
- }
-
- g_free (value);
- }
-}
-
-static void
-gimp_metadata_deserialize_error (GMarkupParseContext *context,
- GError *error,
- gpointer user_data)
-{
- g_printerr ("Metadata parse error: %s\n", error->message);
-}
-
-/**
- * gimp_metadata_deserialize:
- * @metadata_xml: A string of serialized metadata XML.
- *
- * Deserializes a string of XML that has been created by
- * gimp_metadata_serialize().
- *
- * Return value: The new #GimpMetadata.
- *
- * Since: GIMP 2.10
- */
-GimpMetadata *
-gimp_metadata_deserialize (const gchar *metadata_xml)
-{
- GimpMetadata *metadata;
- GMarkupParser markup_parser;
- GimpMetadataParseData parse_data;
- GMarkupParseContext *context;
-
- g_return_val_if_fail (metadata_xml != NULL, NULL);
-
- metadata = gimp_metadata_new ();
-
- parse_data.metadata = metadata;
-
- markup_parser.start_element = gimp_metadata_deserialize_start_element;
- markup_parser.end_element = gimp_metadata_deserialize_end_element;
- markup_parser.text = gimp_metadata_deserialize_text;
- markup_parser.passthrough = NULL;
- markup_parser.error = gimp_metadata_deserialize_error;
-
- context = g_markup_parse_context_new (&markup_parser, 0, &parse_data, NULL);
-
- g_markup_parse_context_parse (context,
- metadata_xml, strlen (metadata_xml),
- NULL);
-
- g_markup_parse_context_unref (context);
-
- return metadata;
-}
-
-static gchar *
-gimp_metadata_escape (const gchar *name,
- const gchar *value,
- gboolean *base64)
-{
- if (! g_utf8_validate (value, -1, NULL))
- {
- gchar *encoded;
-
- encoded = g_base64_encode ((const guchar *) value, strlen (value) + 1);
-
- g_printerr ("Invalid UTF-8 in metadata value %s, encoding as base64: %s\n",
- name, encoded);
-
- *base64 = TRUE;
-
- return encoded;
- }
-
- *base64 = FALSE;
-
- return g_markup_escape_text (value, -1);
-}
-
-static void
-gimp_metadata_append_tag (GString *string,
- const gchar *name,
- gchar *value,
- gboolean base64)
-{
- if (value)
- {
- if (base64)
- {
- g_string_append_printf (string, " <tag name=\"%s\" encoding=\"base64\">%s</tag>\n",
- name, value);
- }
- else
- {
- g_string_append_printf (string, " <tag name=\"%s\">%s</tag>\n",
- name, value);
- }
-
- g_free (value);
- }
-}
-
-/**
- * gimp_metadata_serialize:
- * @metadata: A #GimpMetadata instance.
- *
- * Serializes @metadata into an XML string that can later be deserialized
- * using gimp_metadata_deserialize().
- *
- * Return value: The serialized XML string.
- *
- * Since: GIMP 2.10
- */
-gchar *
-gimp_metadata_serialize (GimpMetadata *metadata)
-{
- GString *string;
- gchar **exif_data = NULL;
- gchar **iptc_data = NULL;
- gchar **xmp_data = NULL;
- gchar *value;
- gchar *escaped;
- gboolean base64;
- gint i;
-
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), NULL);
-
- string = g_string_new (NULL);
-
- g_string_append (string, "<?xml version='1.0' encoding='UTF-8'?>\n");
- g_string_append (string, "<metadata>\n");
-
- exif_data = gexiv2_metadata_get_exif_tags (metadata);
-
- if (exif_data)
- {
- for (i = 0; exif_data[i] != NULL; i++)
- {
- value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
- escaped = gimp_metadata_escape (exif_data[i], value, &base64);
- g_free (value);
-
- gimp_metadata_append_tag (string, exif_data[i], escaped, base64);
- }
-
- g_strfreev (exif_data);
- }
-
- xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
-
- if (xmp_data)
- {
- for (i = 0; xmp_data[i] != NULL; i++)
- {
- value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
- escaped = gimp_metadata_escape (xmp_data[i], value, &base64);
- g_free (value);
-
- gimp_metadata_append_tag (string, xmp_data[i], escaped, base64);
- }
-
- g_strfreev (xmp_data);
- }
-
- iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
-
- if (iptc_data)
- {
- for (i = 0; iptc_data[i] != NULL; i++)
- {
- value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
- escaped = gimp_metadata_escape (iptc_data[i], value, &base64);
- g_free (value);
-
- gimp_metadata_append_tag (string, iptc_data[i], escaped, base64);
- }
-
- g_strfreev (iptc_data);
- }
-
- g_string_append (string, "</metadata>\n");
-
- return g_string_free (string, FALSE);
-}
-
/**
* gimp_metadata_load_from_file:
* @file: The #GFile to load the metadata from
@@ -719,190 +404,6 @@ gimp_metadata_set_from_xmp (GimpMetadata *metadata,
}
/**
- * gimp_metadata_set_pixel_size:
- * @metadata: A #GimpMetadata instance.
- * @width: Width in pixels
- * @height: Height in pixels
- *
- * Sets Exif.Image.ImageWidth and Exif.Image.ImageLength on @metadata.
- *
- * Since: GIMP 2.10
- */
-void
-gimp_metadata_set_pixel_size (GimpMetadata *metadata,
- gint width,
- gint height)
-{
- gchar buffer[32];
-
- g_return_if_fail (GEXIV2_IS_METADATA (metadata));
-
- g_snprintf (buffer, sizeof (buffer), "%d", width);
- gexiv2_metadata_set_tag_string (metadata, "Exif.Image.ImageWidth", buffer);
-
- g_snprintf (buffer, sizeof (buffer), "%d", height);
- gexiv2_metadata_set_tag_string (metadata, "Exif.Image.ImageLength", buffer);
-}
-
-/**
- * gimp_metadata_set_bits_per_sample:
- * @metadata: A #GimpMetadata instance.
- * @bps: Bytes per pixel, per component
- *
- * Sets Exif.Image.BitsPerSample on @metadata.
- *
- * Since: GIMP 2.10
- */
-void
-gimp_metadata_set_bits_per_sample (GimpMetadata *metadata,
- gint bps)
-{
- gchar buffer[32];
-
- g_return_if_fail (GEXIV2_IS_METADATA (metadata));
-
- g_snprintf (buffer, sizeof (buffer), "%d %d %d", bps, bps, bps);
- gexiv2_metadata_set_tag_string (metadata, "Exif.Image.BitsPerSample", buffer);
-}
-
-/**
- * gimp_metadata_get_resolution:
- * @metadata: A #GimpMetadata instance.
- * @xres: Return location for the X Resolution, in ppi
- * @yres: Return location for the Y Resolution, in ppi
- * @unit: Return location for the unit unit
- *
- * Returns values based on Exif.Image.XResolution,
- * Exif.Image.YResolution and Exif.Image.ResolutionUnit of @metadata.
- *
- * Return value: %TRUE on success, %FALSE otherwise.
- *
- * Since: GIMP 2.10
- */
-gboolean
-gimp_metadata_get_resolution (GimpMetadata *metadata,
- gdouble *xres,
- gdouble *yres,
- GimpUnit *unit)
-{
- gint xnom, xdenom;
- gint ynom, ydenom;
-
- g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
-
- if (gexiv2_metadata_get_exif_tag_rational (metadata,
- "Exif.Image.XResolution",
- &xnom, &xdenom) &&
- gexiv2_metadata_get_exif_tag_rational (metadata,
- "Exif.Image.YResolution",
- &ynom, &ydenom))
- {
- gchar *un;
- gint exif_unit = 2;
-
- un = gexiv2_metadata_get_tag_string (metadata,
- "Exif.Image.ResolutionUnit");
- if (un)
- {
- exif_unit = atoi (un);
- g_free (un);
- }
-
- if (xnom != 0 && xdenom != 0 &&
- ynom != 0 && ydenom != 0)
- {
- gdouble xresolution = (gdouble) xnom / (gdouble) xdenom;
- gdouble yresolution = (gdouble) ynom / (gdouble) ydenom;
-
- if (exif_unit == 3)
- {
- xresolution *= 2.54;
- yresolution *= 2.54;
- }
-
- if (xresolution >= GIMP_MIN_RESOLUTION &&
- xresolution <= GIMP_MAX_RESOLUTION &&
- yresolution >= GIMP_MIN_RESOLUTION &&
- yresolution <= GIMP_MAX_RESOLUTION)
- {
- if (xres)
- *xres = xresolution;
-
- if (yres)
- *yres = yresolution;
-
- if (unit)
- {
- if (exif_unit == 3)
- *unit = GIMP_UNIT_MM;
- else
- *unit = GIMP_UNIT_INCH;
- }
-
- return TRUE;
- }
- }
- }
-
- return FALSE;
-}
-
-/**
- * gimp_metadata_set_resolution:
- * @metadata: A #GimpMetadata instance.
- * @xres: The image's X Resolution, in ppi
- * @yres: The image's Y Resolution, in ppi
- * @unit: The image's unit
- *
- * Sets Exif.Image.XResolution, Exif.Image.YResolution and
- * Exif.Image.ResolutionUnit @metadata.
- *
- * Since: GIMP 2.10
- */
-void
-gimp_metadata_set_resolution (GimpMetadata *metadata,
- gdouble xres,
- gdouble yres,
- GimpUnit unit)
-{
- gchar buffer[32];
- gint exif_unit;
- gint factor;
-
- g_return_if_fail (GEXIV2_IS_METADATA (metadata));
-
- if (gimp_unit_is_metric (unit))
- {
- xres /= 2.54;
- yres /= 2.54;
-
- exif_unit = 3;
- }
- else
- {
- exif_unit = 2;
- }
-
- for (factor = 1; factor <= 100 /* arbitrary */; factor++)
- {
- if (fabs (xres * factor - ROUND (xres * factor)) < 0.01 &&
- fabs (yres * factor - ROUND (yres * factor)) < 0.01)
- break;
- }
-
- gexiv2_metadata_set_exif_tag_rational (metadata,
- "Exif.Image.XResolution",
- ROUND (xres * factor), factor);
-
- gexiv2_metadata_set_exif_tag_rational (metadata,
- "Exif.Image.YResolution",
- ROUND (yres * factor), factor);
-
- g_snprintf (buffer, sizeof (buffer), "%d", exif_unit);
- gexiv2_metadata_set_tag_string (metadata, "Exif.Image.ResolutionUnit", buffer);
-}
-
-/**
* gimp_metadata_is_tag_supported:
* @tag: A metadata tag name
* @mime_type: A mime type
diff --git a/libgimpbase/gimpmetadata.h b/libgimpbase/gimpmetadata.h
index 03bca87..95ae641 100644
--- a/libgimpbase/gimpmetadata.h
+++ b/libgimpbase/gimpmetadata.h
@@ -45,11 +45,6 @@ typedef enum
GimpMetadata * gimp_metadata_new (void);
-GimpMetadata * gimp_metadata_duplicate (GimpMetadata *metadata);
-
-GimpMetadata * gimp_metadata_deserialize (const gchar *metadata_xml);
-gchar * gimp_metadata_serialize (GimpMetadata *metadata);
-
GimpMetadata * gimp_metadata_load_from_file (GFile *file,
GError **error);
gboolean gimp_metadata_save_to_file (GimpMetadata *metadata,
@@ -64,22 +59,6 @@ gboolean gimp_metadata_set_from_xmp (GimpMetadata *metadata,
const guchar *xmp_data,
gint xmp_data_length,
GError **error);
-
-void gimp_metadata_set_pixel_size (GimpMetadata *metadata,
- gint width,
- gint height);
-void gimp_metadata_set_bits_per_sample (GimpMetadata *metadata,
- gint bits_per_sample);
-
-gboolean gimp_metadata_get_resolution (GimpMetadata *metadata,
- gdouble *xres,
- gdouble *yres,
- GimpUnit *unit);
-void gimp_metadata_set_resolution (GimpMetadata *metadata,
- gdouble xres,
- gdouble yres,
- GimpUnit unit);
-
gboolean gimp_metadata_is_tag_supported (const gchar *tag,
const gchar *mime_type);
diff --git a/libgimpbase/gimprational.c b/libgimpbase/gimprational.c
new file mode 100644
index 0000000..93d6754
--- /dev/null
+++ b/libgimpbase/gimprational.c
@@ -0,0 +1,301 @@
+/*
+ * gimprational.c
+ *
+ * Created on: 19.09.2014
+ * Author: kuhse
+ */
+
+#include <stdlib.h>
+
+#include "gimprational.h"
+
+
+static gpointer rational_copy (gpointer data);
+static gpointer srational_copy (gpointer data);
+
+
+G_DEFINE_BOXED_TYPE (Rational, rational,
+ rational_copy,
+ rational_free)
+G_DEFINE_BOXED_TYPE (SRational, srational,
+ srational_copy,
+ srational_free)
+
+
+/**
+ * string_to_rational:
+ *
+ * @string: a #gchar array
+ * @rational: a pointer to a #Rational struct
+ *
+ * converts a string, representing one/more rational values into
+ * a #Rational struct.
+ *
+ * Since: 2.10
+ */
+void
+string_to_rational (gchar *string, Rational **rational)
+{
+ gchar **nom;
+ gchar **rats;
+ gint count;
+ gint i;
+ Rational *rval;
+ GArray *rational_array;
+
+ rats = g_strsplit (string, " ", -1);
+
+ count = 0;
+ while (rats[count])
+ count++;
+
+ rational_array = g_array_new (TRUE, TRUE, sizeof (RationalValue));
+
+ for (i = 0; i < count; i++)
+ {
+ RationalValue or;
+
+ nom = g_strsplit (rats[i], "/", 2);
+
+ if (nom[0] && nom[1])
+ {
+ or.nom = (guint) atoi (nom [0]);
+ or.denom = (guint) atoi (nom [1]);
+ }
+ else
+ count--;
+
+ rational_array = g_array_append_val (rational_array, or);
+
+ g_strfreev (nom);
+ }
+
+ g_strfreev (rats);
+
+ rval = g_slice_new (Rational);
+
+ rval->rational_array = rational_array;
+ rval->length = count;
+
+ *rational = rval;
+}
+
+/**
+ * rational_to_string:
+ *
+ * @rational : a pointer to a @Rational struct
+ * @nom : a pointer to a #gint array
+ * @denom : a pointer to a #gint array
+ * @length : a pointer to a #gint
+ *
+ * converts a @rational to nom/denum gchar arrays
+ *
+ * Since: 2.10
+ */
+void
+rational_to_int (Rational *rational, gint **nom, gint **denom, gint *length)
+{
+ gint i;
+ gint *_nom;
+ gint *_denom;
+
+ g_return_if_fail (rational != NULL);
+
+ _nom = g_new (gint, rational->length);
+ _denom = g_new (gint, rational->length);
+
+ for (i = 0; i < rational->length; i++)
+ {
+ RationalValue one;
+ one = g_array_index(rational->rational_array, RationalValue, i);
+ _nom[i] = one.nom;
+ _denom[i] = one.denom;
+ }
+
+ *nom = _nom;
+ *denom = _denom;
+ *length = rational->length;
+}
+
+/**
+ * rational_copy:
+ *
+ * @data: a #gpointer to a #Rational structure
+ *
+ * copy part of the #Rational type
+ *
+ * Since: 2.10
+ */
+static gpointer
+rational_copy (gpointer data)
+{
+ struct _Rational *rational = (struct _Rational *) data;
+ struct _Rational *copy = g_slice_new (Rational);
+ gint i;
+ GArray *rlacp;
+
+ if (!data)
+ return NULL;
+
+ rlacp = g_array_new (TRUE, TRUE, sizeof (RationalValue));
+
+ for (i = 0; i < rational->length; i++)
+ {
+ RationalValue or;
+ RationalValue cor;
+
+ or = g_array_index (rational->rational_array, RationalValue, i);
+
+ cor.nom = or.nom;
+ cor.denom = or.denom;
+
+ rlacp = g_array_append_val (rlacp, cor);
+ }
+
+ copy->rational_array = rlacp;
+ copy->length = rational->length;
+
+ return (gpointer) copy;
+}
+
+/**
+ * rational_free:
+ *
+ * @data: a #gpointer to a #Rational structure
+ *
+ * free part of the #Rational type
+ *
+ * Since: 2.10
+ */
+void
+rational_free (gpointer data)
+{
+ struct _Rational *rational = (struct _Rational *) data;
+
+ if (rational)
+ {
+ if (rational->rational_array)
+ g_array_free (rational->rational_array, TRUE);
+ rational->length = 0;
+ }
+}
+
+/**
+ * string_to_srational:
+ *
+ * @string: a #gchar array
+ * @srational: a pointer to a #Rational struct
+ *
+ * converts a string, representing one/more srational values into
+ * a #SRational struct.
+ *
+ * Since: 2.10
+ */
+void
+string_to_srational (gchar *string, SRational **srational)
+{
+ gchar **nom;
+ gchar **srats;
+ gint count;
+ gint i;
+ SRational *srval;
+ GArray *srational_array;
+
+ srats = g_strsplit (string, " ", -1);
+
+ count = 0;
+ while (srats[count])
+ count++;
+
+ srational_array = g_array_new (TRUE, TRUE, sizeof (SRationalValue));
+
+ for (i = 0; i < count; i++)
+ {
+ SRationalValue or;
+
+ nom = g_strsplit (srats[i], "/", 2);
+
+ if (nom[0] && nom[1])
+ {
+ or.nom = (gint) atoi (nom [0]);
+ or.denom = (guint) atoi (nom [1]);
+ }
+ else
+ count--;
+
+ srational_array = g_array_append_val (srational_array, or);
+
+ g_strfreev (nom);
+ }
+
+ g_strfreev (srats);
+
+ srval = g_slice_new (SRational);
+
+ srval->srational_array = srational_array;
+ srval->length = count;
+
+ *srational = srval;
+}
+/**
+ * srational_copy:
+ *
+ * @data: a #gpointer to a #SRational structure
+ *
+ * copy part of the #SRational type
+ *
+ * Since: 2.10
+ */
+static gpointer
+srational_copy (gpointer data)
+{
+ struct _SRational *srational = (struct _SRational *) data;
+ struct _SRational *copy = g_slice_new (SRational);
+ gint i;
+ GArray *rlacp;
+
+ if (!data)
+ return NULL;
+
+ rlacp = g_array_new (TRUE, TRUE, sizeof (SRationalValue));
+
+ for (i = 0; i < srational->length; i++)
+ {
+ SRationalValue or;
+ SRationalValue cor;
+
+ or = g_array_index (srational->srational_array, SRationalValue, i);
+
+ cor.nom = or.nom;
+ cor.denom = or.denom;
+
+ rlacp = g_array_append_val (rlacp, cor);
+ }
+
+ copy->srational_array = rlacp;
+ copy->length = srational->length;
+
+ return (gpointer) copy;
+}
+
+/**
+ * srational_free:
+ *
+ * @data: a #gpointer to a #SRational structure
+ *
+ * free part of the #SRational type
+ *
+ * Since: 2.10
+ */
+void
+srational_free (gpointer data)
+{
+ struct _SRational *srational = (struct _SRational *) data;
+
+ if (srational)
+ {
+ if (srational->srational_array)
+ g_array_free (srational->srational_array, TRUE);
+ srational->length = 0;
+ }
+}
diff --git a/libgimpbase/gimprational.h b/libgimpbase/gimprational.h
new file mode 100644
index 0000000..ab020c8
--- /dev/null
+++ b/libgimpbase/gimprational.h
@@ -0,0 +1,68 @@
+/*
+ * gimprational.h
+ *
+ * Created on: 19.09.2014
+ * Author: kuhse
+ */
+
+#ifndef __GIMPRATIONAL_H__
+#define __GIMPRATIONAL_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_RATIONAL (rational_get_type ())
+#define GIMP_TYPE_SRATIONAL (srational_get_type ())
+
+typedef struct _RationalValue RationalValue;
+
+struct _RationalValue
+{
+ guint nom;
+ guint denom;
+};
+
+typedef struct _SRationalValue SRationalValue;
+
+struct _SRationalValue
+{
+ gint nom;
+ guint denom;
+};
+
+typedef struct _Rational Rational;
+
+struct _Rational
+{
+ GArray *rational_array;
+ gint length;
+};
+
+typedef struct _SRational SRational;
+
+struct _SRational
+{
+ GArray *srational_array;
+ gint length;
+};
+
+GType rational_get_type (void) G_GNUC_CONST;
+GType srational_get_type (void) G_GNUC_CONST;
+
+void rational_free (gpointer data);
+void srational_free (gpointer data);
+
+void string_to_rational (gchar *string,
+ Rational **rational);
+void rational_to_int (Rational *rational,
+ gint **nom,
+ gint **denom,
+ gint *length);
+void string_to_srational (gchar *string,
+ SRational **srational);
+
+
+G_END_DECLS
+
+#endif /* __GIMPRATIONAL_H__ */
diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am
index bcc3e26..d0a101a 100644
--- a/plug-ins/Makefile.am
+++ b/plug-ins/Makefile.am
@@ -53,6 +53,7 @@ SUBDIRS = \
lighting \
map-object \
maze \
+ metainfo \
pagecurl \
$(print) \
selection-to-path \
diff --git a/plug-ins/common/Makefile.am b/plug-ins/common/Makefile.am
index 6ff1821..606b2b5 100644
--- a/plug-ins/common/Makefile.am
+++ b/plug-ins/common/Makefile.am
@@ -47,6 +47,7 @@ libexec_PROGRAMS = \
align-layers \
animation-optimize \
animation-play \
+ attributes \
blinds \
blur \
blur-gauss-selective \
@@ -122,13 +123,13 @@ libexec_PROGRAMS = \
guillotine \
hot \
illusion \
+ iptc \
iwarp \
jigsaw \
lcms \
lens-flare \
$(MAIL) \
max-rgb \
- metadata \
newsprint \
nl-filter \
noise-solid \
@@ -239,6 +240,24 @@ animation_play_LDADD = \
$(INTLLIBS) \
$(animation_play_RC)
+attributes_SOURCES = \
+ attributes.c
+
+attributes_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(attributes_RC)
+
blinds_SOURCES = \
blinds.c
@@ -1566,6 +1585,26 @@ illusion_LDADD = \
$(INTLLIBS) \
$(illusion_RC)
+iptc_CFLAGS = $(GEXIV2_CFLAGS)
+
+iptc_SOURCES = \
+ iptc.c
+
+iptc_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(iptc_RC)
+
iwarp_SOURCES = \
iwarp.c
@@ -1672,26 +1711,6 @@ max_rgb_LDADD = \
$(INTLLIBS) \
$(max_rgb_RC)
-metadata_CFLAGS = $(GEXIV2_CFLAGS)
-
-metadata_SOURCES = \
- metadata.c
-
-metadata_LDADD = \
- $(libgimpui) \
- $(libgimpwidgets) \
- $(libgimpmodule) \
- $(libgimp) \
- $(libgimpmath) \
- $(libgimpconfig) \
- $(libgimpcolor) \
- $(libgimpbase) \
- $(GTK_LIBS) \
- $(GEXIV2_LIBS) \
- $(RT_LIBS) \
- $(INTLLIBS) \
- $(metadata_RC)
-
newsprint_SOURCES = \
newsprint.c
diff --git a/plug-ins/common/attributes.c b/plug-ins/common/attributes.c
new file mode 100644
index 0000000..09b9126
--- /dev/null
+++ b/plug-ins/common/attributes.c
@@ -0,0 +1,876 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * attributes.c
+ * Copyright (C) 2014 Hartmut Kuhse <hatti gimp org>
+ *
+ * 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/>.
+ *
+ * additions: share/gimp/2.0/ui/plug-ins/plug-in-attributes.ui
+ * This plug-in reads in a (libgimpbase/gimpattributes.c) object and
+ * presents it:
+ * as GtkTreeView lists
+ * in a GtkNotebook.
+ * The GtkNotebook pages represents the type of attributes, as "exif",
+ * "xmp", "iptc" or more.
+ * The expanders of the GtkTreeView represents the IFD (Exif) or Key (XMP, IPTC).
+ * of the attribute.
+ * The list entry of the GtkTreeView represents the tag of the attribute.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_IMAGE_PROC "plug-in-image-attributes-viewer"
+#define PLUG_IN_LAYER_PROC "plug-in-layer-attributes-viewer"
+#define PLUG_IN_CHANNEL_PROC "plug-in-channel-attributes-viewer"
+#define PLUG_IN_HELP "plug-in-attributes-viewer"
+#define PLUG_IN_BINARY "attributes"
+
+#define THUMB_SIZE 48
+#define RESPONSE_EXPORT 1
+
+typedef enum
+{
+ ATT_IMAGE,
+ ATT_LAYER,
+ ATT_CHANNEL
+} AttributesSource;
+
+enum
+{
+ C_IFD = 0,
+ C_TAG,
+ C_VALUE,
+ NUM_COL
+};
+
+/* local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static void attributes_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static gboolean attributes_dialog (gint32 image_id,
+ AttributesSource source,
+ GimpAttributes *attributes);
+static void attributes_dialog_set_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder);
+static GtkTreeIter *attributes_get_parent (const gchar *type,
+ const gchar *name);
+static void attributes_add_parent (const gchar *type,
+ const gchar *name,
+ GtkTreeIter *iter);
+static GtkTreeStore *attributes_notebook_page_get (GtkWidget *notebook,
+ const gchar *name);
+static GtkWidget *attributes_treeview_new (void);
+static void attributes_file_export_dialog (GtkWidget *parent,
+ GimpAttributes *attributes);
+static void attributes_export_dialog_response (GtkWidget *dlg,
+ gint response_id,
+ gpointer data);
+
+static void attributes_message_dialog (GtkMessageType type,
+ GtkWindow *parent,
+ const gchar *title,
+ const gchar *message);
+
+/* local variables */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static GHashTable *ifd_table = NULL;
+static GHashTable *tab_table = NULL;
+
+/* functions */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef image_attribs_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" }
+ };
+
+ static const GimpParamDef layer_attribs_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_LAYER, "layer", "Input layer" }
+ };
+
+ static const GimpParamDef channel_attribs_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_CHANNEL, "channel", "Input channel" }
+ };
+
+ gimp_install_procedure (PLUG_IN_IMAGE_PROC,
+ N_("View attributes (Exif, IPTC, XMP, ...)"),
+ "View attributes information attached to the "
+ "current image. This can include Exif, IPTC, "
+ "XMP and/or other information. Some or all of these "
+ "attributes will be saved in the file, depending on "
+ "the output file format.",
+ "Hartmut Kuhse",
+ "Hartmut Kuhse",
+ "2014",
+ N_("Image Attributes"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (image_attribs_args), 0,
+ image_attribs_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_IMAGE_PROC, "<Image>/Image");
+
+ gimp_install_procedure (PLUG_IN_LAYER_PROC,
+ N_("View attributes (Exif, IPTC, XMP, ...)"),
+ "View attributes information attached to the "
+ "current layer. This can include Exif, IPTC, "
+ "XMP and/or other information.",
+ "Hartmut Kuhse",
+ "Hartmut Kuhse",
+ "2014",
+ N_("Layer Attributes"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (layer_attribs_args), 0,
+ layer_attribs_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_LAYER_PROC, "<Layers>");
+
+ gimp_install_procedure (PLUG_IN_CHANNEL_PROC,
+ N_("View attributes (Exif, IPTC, XMP, ...)"),
+ "View attributes information attached to the "
+ "current channel. This can include Exif, IPTC, "
+ "XMP and/or other information.",
+ "Hartmut Kuhse",
+ "Hartmut Kuhse",
+ "2014",
+ N_("Channel Attributes"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (channel_attribs_args), 0,
+ channel_attribs_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_CHANNEL_PROC, "<Channels>");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpAttributes *attributes;
+ AttributesSource source;
+ gint32 item_ID;
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ INIT_I18N();
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ if (! strcmp (name, PLUG_IN_IMAGE_PROC))
+ {
+ item_ID = param[1].data.d_image;
+
+ attributes = gimp_image_get_attributes (item_ID);
+ source = ATT_IMAGE;
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else if (! strcmp (name, PLUG_IN_LAYER_PROC))
+ {
+ item_ID = param[2].data.d_layer;
+
+ attributes = gimp_item_get_attributes (item_ID);
+ source = ATT_LAYER;
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else if (! strcmp (name, PLUG_IN_CHANNEL_PROC))
+ {
+ item_ID = param[2].data.d_channel;
+
+ attributes = gimp_item_get_attributes (item_ID);
+ source = ATT_CHANNEL;
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (attributes)
+ {
+ ifd_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ tab_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ attributes_dialog (item_ID, source, attributes);
+ }
+ else
+ {
+ g_message (_("This image has no attributes attached to it."));
+ }
+
+ if (attributes)
+ g_object_unref (attributes);
+
+ if (ifd_table)
+ g_hash_table_unref (ifd_table);
+
+ values[0].data.d_status = status;
+}
+
+static void
+attributes_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+
+ GimpAttributes *attributes = (GimpAttributes *) data;
+ switch (response_id)
+ {
+ case RESPONSE_EXPORT:
+ attributes_file_export_dialog (widget, attributes);
+ break;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static gboolean
+attributes_dialog (gint32 item_id,
+ AttributesSource source,
+ GimpAttributes *attributes)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *attributes_vbox;
+ GtkWidget *content_area;
+ GdkPixbuf *pixbuf;
+ GtkWidget *label_header;
+ GtkWidget *label_info;
+ GtkWidget *thumb_box;
+
+ gchar *header;
+ gchar *ui_file;
+ gchar *title;
+ gchar *fname;
+ gchar *role;
+ GError *error = NULL;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ switch (source)
+ {
+ case ATT_IMAGE:
+ fname = g_filename_display_basename (gimp_image_get_uri (item_id));
+ header = g_strdup_printf ("Image");
+ role = g_strdup_printf ("gimp-image-attributes-dialog");
+ pixbuf = gimp_image_get_thumbnail (item_id, THUMB_SIZE, THUMB_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ break;
+ case ATT_LAYER:
+ fname = gimp_item_get_name (item_id);
+ header = g_strdup_printf ("Layer");
+ role = g_strdup_printf ("gimp-layer-attributes-dialog");
+ pixbuf = gimp_drawable_get_thumbnail (item_id, THUMB_SIZE, THUMB_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+ break;
+ case ATT_CHANNEL:
+ fname = gimp_item_get_name (item_id);
+ header = g_strdup_printf ("Channel");
+ role = g_strdup_printf ("gimp-channel-attributes-dialog");
+ pixbuf = gimp_drawable_get_thumbnail (item_id, THUMB_SIZE, THUMB_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+ break;
+ default:
+ fname = g_strdup_printf ("unknown");
+ header = g_strdup_printf ("Unknown");
+ role = g_strdup_printf ("gimp-attributes-dialog");
+ pixbuf = NULL;
+ break;
+ }
+
+ title = g_strdup_printf ("Attributes: %s", fname);
+
+ builder = gtk_builder_new ();
+
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-attributes.ui", NULL);
+
+ if (! gtk_builder_add_from_file (builder, ui_file, &error))
+ {
+ g_printerr ("Error occured while loading UI file!\n");
+ g_printerr ("Message: %s\n", error->message);
+ g_clear_error (&error);
+ g_free (ui_file);
+ g_object_unref (builder);
+ return FALSE;
+ }
+
+ g_free (ui_file);
+
+ dialog = gimp_dialog_new (title,
+ role,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_HELP,
+ _("_Export XMP..."), RESPONSE_EXPORT,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ NULL);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (attributes_dialog_response),
+ attributes);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ g_free (title);
+ g_free (role);
+
+ gtk_window_set_default_size (GTK_WINDOW (dialog),
+ 650,
+ 550);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ RESPONSE_EXPORT,
+ GTK_RESPONSE_CLOSE,
+ -1);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ attributes_vbox = GTK_WIDGET (gtk_builder_get_object (builder,
+ "attributes-vbox"));
+ gtk_container_set_border_width (GTK_CONTAINER (attributes_vbox), 2);
+ gtk_box_pack_start (GTK_BOX (content_area), attributes_vbox, TRUE, TRUE, 0);
+
+ label_header = GTK_WIDGET (gtk_builder_get_object (builder, "label-header"));
+ gimp_label_set_attributes (GTK_LABEL (label_header),
+ PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
+ PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
+ -1);
+ gtk_label_set_text (GTK_LABEL (label_header), header);
+
+ label_info = GTK_WIDGET (gtk_builder_get_object (builder, "label-info"));
+ gimp_label_set_attributes (GTK_LABEL (label_info),
+ PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
+ -1);
+ gtk_label_set_text (GTK_LABEL (label_info), fname);
+
+ g_free (header);
+ g_free (fname);
+
+ if (pixbuf)
+ {
+ GtkWidget *image;
+
+ thumb_box = GTK_WIDGET (gtk_builder_get_object (builder, "thumb-box"));
+
+ if (thumb_box)
+ {
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ gtk_box_pack_end (GTK_BOX (thumb_box), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+ }
+ }
+
+ attributes_dialog_set_attributes (attributes, builder);
+
+ gtk_widget_show (GTK_WIDGET (dialog));
+
+ gtk_main ();
+
+ return TRUE;
+}
+
+
+/* private functions */
+
+static void
+attributes_dialog_set_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder)
+{
+ GtkTreeStore *exif_store;
+ GtkTreeStore *xmp_store;
+ GtkTreeStore *iptc_store;
+ GtkTreeView *exif_treeview;
+ GtkTreeView *xmp_treeview;
+ GtkTreeView *iptc_treeview;
+ GtkWidget *attributes_notebook;
+ GHashTableIter hash_iter;
+ GHashTable *table;
+ gpointer p_key, p_value;
+
+ exif_store = GTK_TREE_STORE (gtk_builder_get_object (builder,
+ "exif-treestore"));
+ xmp_store = GTK_TREE_STORE (gtk_builder_get_object (builder,
+ "xmp-treestore"));
+ iptc_store = GTK_TREE_STORE (gtk_builder_get_object (builder,
+ "iptc-treestore"));
+
+ exif_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+ "exif-treeview"));
+ xmp_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+ "xmp-treeview"));
+ iptc_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
+ "iptc-treeview"));
+
+ attributes_notebook = GTK_WIDGET (gtk_builder_get_object (builder,
+ "attributes-notebook"));
+
+ table = gimp_attributes_get_table (attributes);
+
+ g_hash_table_iter_init (&hash_iter, table);
+
+ while (g_hash_table_iter_next (&hash_iter, &p_key, &p_value))
+ {
+ const gchar *name;
+ const gchar *type;
+ const gchar *ifd;
+ const gchar *tag;
+ const gchar *value;
+ gchar *value_utf;
+ GimpAttribute *attribute;
+ GtkTreeIter *parent = NULL;
+ GtkTreeIter *iter = g_slice_new (GtkTreeIter);
+
+ attribute = (GimpAttribute *) p_value;
+
+ name = gimp_attribute_get_name (attribute);
+ type = gimp_attribute_get_attribute_type (attribute);
+ ifd = gimp_attribute_get_attribute_ifd (attribute);
+ tag = gimp_attribute_get_attribute_tag (attribute);
+ value = gimp_attribute_get_interpreted_string (attribute);
+
+ value_utf = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+
+ parent = attributes_get_parent (type, ifd);
+
+ switch (gimp_attribute_get_tag_type (attribute))
+ {
+ case TAG_EXIF:
+ {
+ gtk_tree_store_append (exif_store, iter, parent);
+
+ if (!parent)
+ {
+ GtkTreeIter child;
+ gtk_tree_store_set (exif_store, iter,
+ C_IFD, ifd,
+ C_TAG, "",
+ C_VALUE, "",
+ -1);
+ parent = iter;
+ gtk_tree_store_append (exif_store, &child, parent);
+ gtk_tree_store_set (exif_store, &child,
+ C_IFD, "",
+ C_TAG, tag,
+ C_VALUE, value_utf,
+ -1);
+ attributes_add_parent (type, ifd, parent);
+ }
+ else
+ {
+
+ gtk_tree_store_set (exif_store, iter,
+ C_IFD, "",
+ C_TAG, tag,
+ C_VALUE, value_utf,
+ -1);
+ }
+ }
+ break;
+ case TAG_XMP:
+ {
+ gtk_tree_store_append (xmp_store, iter, parent);
+ if (!parent)
+ {
+ GtkTreeIter child;
+ gtk_tree_store_set (xmp_store, iter,
+ C_IFD, ifd,
+ C_TAG, "",
+ C_VALUE, "",
+ -1);
+ parent = iter;
+ gtk_tree_store_append (xmp_store, &child, parent);
+ gtk_tree_store_set (xmp_store, &child,
+ C_IFD, "",
+ C_TAG, tag,
+ C_VALUE, value_utf,
+ -1);
+ attributes_add_parent (type, ifd, parent);
+ }
+ else
+ {
+
+ gtk_tree_store_set (xmp_store, iter,
+ C_IFD, "",
+ C_TAG, tag,
+ C_VALUE, value_utf,
+ -1);
+ }
+ }
+ break;
+ case TAG_IPTC:
+ {
+ gtk_tree_store_append (iptc_store, iter, parent);
+ if (!parent)
+ {
+ GtkTreeIter child;
+ gtk_tree_store_set (iptc_store, iter,
+ C_IFD, ifd,
+ C_TAG, "",
+ C_VALUE, "",
+ -1);
+ parent = iter;
+ gtk_tree_store_append (iptc_store, &child, parent);
+ gtk_tree_store_set (iptc_store, &child,
+ C_IFD, "",
+ C_TAG, tag,
+ C_VALUE, value_utf,
+ -1);
+ attributes_add_parent (type, ifd, parent);
+ }
+ else
+ {
+
+ gtk_tree_store_set (iptc_store, iter,
+ C_IFD, "",
+ C_TAG, tag,
+ C_VALUE, value_utf,
+ -1);
+ }
+ }
+ break;
+ default:
+ {
+ GtkTreeStore *treestore;
+ GtkTreeIter iter;
+
+ treestore = attributes_notebook_page_get (attributes_notebook, type);
+
+ gtk_tree_store_append (treestore, &iter, NULL);
+ gtk_tree_store_set (treestore, &iter,
+ C_IFD, "",
+ C_TAG, name,
+ C_VALUE, value_utf,
+ -1);
+ }
+ break;
+ }
+ }
+ gtk_tree_view_expand_all (exif_treeview);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(exif_store),
+ C_TAG, GTK_SORT_ASCENDING);
+
+ gtk_tree_view_expand_all (xmp_treeview);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(xmp_store),
+ C_TAG, GTK_SORT_ASCENDING);
+ gtk_tree_view_expand_all (iptc_treeview);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(iptc_store),
+ C_TAG, GTK_SORT_ASCENDING);
+}
+
+static GtkTreeIter *
+attributes_get_parent (const gchar *type, const gchar *name)
+{
+ gchar *key;
+ gchar *lowchar;
+ GtkTreeIter *iter;
+ gpointer *data;
+
+ key = g_strdup_printf ("%s.%s", type, name);
+ lowchar = g_ascii_strdown (key, -1);
+ g_free (key);
+
+ data = g_hash_table_lookup (ifd_table, (gpointer) lowchar);
+ g_free (lowchar);
+
+ if (data)
+ {
+ iter = (GtkTreeIter *) data;
+ return iter;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+static void
+attributes_add_parent (const gchar *type, const gchar *name, GtkTreeIter *iter)
+{
+ gchar *key;
+ gchar *lowchar;
+
+ key = g_strdup_printf ("%s.%s", type, name);
+ lowchar = g_ascii_strdown (key, -1);
+ g_free (key);
+
+ g_hash_table_insert (ifd_table, (gpointer) lowchar, (gpointer) iter);
+
+}
+
+static GtkTreeStore *
+attributes_notebook_page_get (GtkWidget *notebook,
+ const gchar *name)
+{
+ GtkWidget *scrolledwindow;
+ GtkWidget *treeview;
+ gchar *lowchar;
+ gpointer *data;
+ GtkTreeStore *treestore;
+
+ lowchar = g_ascii_strdown (name, -1);
+
+ data = g_hash_table_lookup (tab_table, (gpointer) lowchar);
+
+ if (data)
+ {
+ treestore = (GtkTreeStore *) data;
+ g_free (lowchar);
+ }
+ else
+ {
+ treeview = attributes_treeview_new ();
+
+ scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_show (scrolledwindow);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_SHADOW_IN);
+
+ gtk_widget_show (treeview);
+ gtk_container_add (GTK_CONTAINER (scrolledwindow), treeview);
+ gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), FALSE);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
+ scrolledwindow,
+ gtk_label_new (name));
+
+ treestore = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)));
+
+ g_hash_table_insert (tab_table, (gpointer) lowchar, (gpointer) treestore);
+ }
+ return treestore;
+}
+
+static GtkWidget *
+attributes_treeview_new (void)
+{
+ GtkWidget *treeview;
+ GtkTreeStore *treestore;
+ GtkCellRenderer *ifd_renderer;
+ GtkCellRenderer *tag_renderer;
+ GtkCellRenderer *val_renderer;
+ GtkTreeViewColumn *ifd_column;
+ GtkTreeViewColumn *tag_column;
+ GtkTreeViewColumn *val_column;
+
+ ifd_renderer = gtk_cell_renderer_text_new ();
+ tag_renderer = gtk_cell_renderer_text_new ();
+ val_renderer = gtk_cell_renderer_text_new ();
+ treestore = gtk_tree_store_new (NUM_COL, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ treeview = gtk_tree_view_new ();
+ gtk_tree_view_set_model(GTK_TREE_VIEW (treeview), GTK_TREE_MODEL(treestore));
+
+ ifd_column = gtk_tree_view_column_new_with_attributes ("", ifd_renderer, "text", C_IFD, NULL);
+ tag_column = gtk_tree_view_column_new_with_attributes ("Tag", tag_renderer, "text", C_TAG, NULL);
+ val_column = gtk_tree_view_column_new_with_attributes ("Value", val_renderer, "text", C_VALUE, NULL);
+
+ gtk_tree_view_column_set_resizable (ifd_column, TRUE);
+ gtk_tree_view_column_set_resizable (tag_column, TRUE);
+ gtk_tree_view_column_set_resizable (val_column, TRUE);
+
+ gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), ifd_column, -1);
+ gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), tag_column, -1);
+ gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), val_column, -1);
+
+ return treeview;
+}
+
+/* select file to export */
+static void
+attributes_file_export_dialog (GtkWidget *parent,
+ GimpAttributes *attributes)
+{
+ static GtkWidget *dlg = NULL;
+// GTK_WINDOW (parent),
+
+ if (! dlg)
+ {
+ dlg = gtk_file_chooser_dialog_new (_("Export XMP to File"),
+ GTK_WINDOW (parent),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dlg),
+ TRUE);
+
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dlg);
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (attributes_export_dialog_response),
+ attributes);
+ }
+ gtk_window_present (GTK_WINDOW (dlg));
+// gtk_dialog_run (GTK_DIALOG (dlg));
+}
+
+/* save XMP metadata to a file (only XMP, nothing else) */
+static void
+attributes_export_dialog_response (GtkWidget *dlg,
+ gint response_id,
+ gpointer data)
+{
+ GimpAttributes *attributes = (GimpAttributes *) data;
+
+ g_return_if_fail (attributes != NULL);
+
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ GString *buffer;
+ const gchar *xmp_data = NULL;
+ gchar *filename = NULL;
+ int fd;
+
+ xmp_data = gimp_attributes_to_xmp_packet (attributes, "file/xmp");
+
+ if (!xmp_data)
+ {
+ attributes_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
+ _("XMP creation failed"),
+ _("Cannot create xmp packet"));
+ return;
+ }
+
+ buffer = g_string_new (xmp_data);
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
+ fd = g_open (filename, O_CREAT | O_TRUNC | O_WRONLY | _O_BINARY, 0666);
+ if (fd < 0)
+ {
+ attributes_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
+ _("Open failed"),
+ _("Cannot create file"));
+ g_string_free (buffer, TRUE);
+ g_free (filename);
+ return;
+ }
+
+ if (write (fd, buffer->str, buffer->len) < 0)
+ {
+ attributes_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
+ _("Save failed"),
+ _("Some error occurred while saving"));
+ g_string_free (buffer, TRUE);
+ g_free (filename);
+ return;
+ }
+
+ if (close (fd) < 0)
+ {
+ attributes_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
+ _("Save failed"),
+ _("Could not close the file"));
+ g_string_free (buffer, TRUE);
+ g_free (filename);
+ return;
+ }
+
+ g_string_free (buffer, TRUE);
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dlg); /* FIXME: destroy or unmap? */
+}
+
+/* show a transient message dialog */
+static void
+attributes_message_dialog (GtkMessageType type,
+ GtkWindow *parent,
+ const gchar *title,
+ const gchar *message)
+{
+ GtkWidget *dlg;
+
+ dlg = gtk_message_dialog_new (parent, 0, type, GTK_BUTTONS_OK, "%s", message);
+
+ if (title)
+ gtk_window_set_title (GTK_WINDOW (dlg), title);
+
+ gtk_window_set_role (GTK_WINDOW (dlg), "metadata-message");
+ gtk_dialog_run (GTK_DIALOG (dlg));
+ gtk_widget_destroy (dlg);
+}
diff --git a/plug-ins/common/file-jp2-load.c b/plug-ins/common/file-jp2-load.c
index 261142b..e68e5b9 100644
--- a/plug-ins/common/file-jp2-load.c
+++ b/plug-ins/common/file-jp2-load.c
@@ -57,6 +57,7 @@ static void run (const gchar *name,
gint *nreturn_vals,
GimpParam **return_vals);
static gint32 load_image (const gchar *filename,
+ gint32 *layer_ID,
GError **error);
static void load_icc_profile (jas_image_t *jas_image,
gint image_ID);
@@ -125,6 +126,7 @@ run (const gchar *name,
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint image_ID;
GError *error = NULL;
+ gint32 layer_ID;
run_mode = param[0].data.d_int32;
@@ -153,7 +155,7 @@ run (const gchar *name,
break;
}
- image_ID = load_image (param[1].data.d_string, &error);
+ image_ID = load_image (param[1].data.d_string, &layer_ID, &error);
if (image_ID != -1)
{
@@ -167,7 +169,7 @@ run (const gchar *name,
{
GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
- gimp_image_metadata_load_finish (image_ID, "image/jp2",
+ gimp_image_metadata_load_finish (image_ID, layer_ID, "image/jp2",
metadata, flags,
interactive);
@@ -202,13 +204,13 @@ run (const gchar *name,
static gint32
load_image (const gchar *filename,
+ gint32 *layer_ID,
GError **error)
{
gint fd;
jas_stream_t *stream;
gint32 image_ID = -1;
jas_image_t *image;
- gint32 layer_ID;
GimpImageType image_type;
GimpImageBaseType base_type;
gint width;
diff --git a/plug-ins/common/file-png.c b/plug-ins/common/file-png.c
index 44d7aac..c9ec00f 100644
--- a/plug-ins/common/file-png.c
+++ b/plug-ins/common/file-png.c
@@ -140,6 +140,7 @@ static void run (const gchar *name,
GimpParam **return_vals);
static gint32 load_image (const gchar *filename,
+ gint32 *layer_ID,
gboolean interactive,
gboolean *resolution_loaded,
GError **error);
@@ -147,6 +148,7 @@ static gboolean save_image (const gchar *filename,
gint32 image_ID,
gint32 drawable_ID,
gint32 orig_image_ID,
+ gint *bits_depth,
GError **error);
static int respin_cmap (png_structp pp,
@@ -416,6 +418,7 @@ run (const gchar *name,
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint32 image_ID;
+ gint32 layer_ID;
gint32 drawable_ID;
GError *error = NULL;
@@ -448,6 +451,7 @@ run (const gchar *name,
}
image_ID = load_image (param[1].data.d_string,
+ &layer_ID,
interactive,
&resolution_loaded,
&error);
@@ -467,7 +471,7 @@ run (const gchar *name,
if (resolution_loaded)
flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
- gimp_image_metadata_load_finish (image_ID, "image/png",
+ gimp_image_metadata_load_finish (image_ID, layer_ID, "image/png",
metadata, flags,
interactive);
@@ -489,9 +493,10 @@ run (const gchar *name,
strcmp (name, SAVE2_PROC) == 0 ||
strcmp (name, SAVE_DEFAULTS_PROC) == 0)
{
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
GimpMetadataSaveFlags metadata_flags;
gint32 orig_image_ID;
+ gint bits_depth;
GimpExportReturn export = GIMP_EXPORT_CANCEL;
gboolean alpha;
@@ -524,9 +529,9 @@ run (const gchar *name,
break;
}
- metadata = gimp_image_metadata_save_prepare (orig_image_ID,
- "image/png",
- &metadata_flags);
+ attributes = gimp_image_metadata_save_prepare (orig_image_ID,
+ "image/png",
+ &metadata_flags);
pngvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
pngvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
@@ -608,13 +613,19 @@ run (const gchar *name,
if (status == GIMP_PDB_SUCCESS)
{
if (save_image (param[3].data.d_string,
- image_ID, drawable_ID, orig_image_ID, &error))
+ image_ID, drawable_ID, orig_image_ID, &bits_depth, &error))
{
- if (metadata)
+ if (attributes)
{
GFile *file;
+ gchar *val = g_strdup_printf ("%u", (gushort) bits_depth);
+
+ gimp_attributes_add_attribute (attributes,
+ gimp_attribute_new_string ("Exif.Image.BitsPerSample",
+ val, TYPE_SHORT)
+ );
- gimp_metadata_set_bits_per_sample (metadata, 8);
+ g_free (val);
if (pngvals.save_exif)
metadata_flags |= GIMP_METADATA_SAVE_EXIF;
@@ -639,8 +650,9 @@ run (const gchar *name,
file = g_file_new_for_path (param[3].data.d_string);
gimp_image_metadata_save_finish (orig_image_ID,
"image/png",
- metadata, metadata_flags,
+ attributes, metadata_flags,
file, NULL);
+ g_object_unref (attributes);
g_object_unref (file);
}
@@ -655,8 +667,6 @@ run (const gchar *name,
if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image_ID);
- if (metadata)
- g_object_unref (metadata);
}
else if (strcmp (name, GET_DEFAULTS_PROC) == 0)
{
@@ -788,6 +798,7 @@ get_bit_depth_for_palette (int num_palette)
*/
static gint32
load_image (const gchar *filename,
+ gint32 *layer_ID,
gboolean interactive,
gboolean *resolution_loaded,
GError **error)
@@ -1013,6 +1024,8 @@ load_image (const gchar *filename,
layer_type, 100, GIMP_NORMAL_MODE);
gimp_image_insert_layer (image, layer, -1, 0);
+ *layer_ID = layer;
+
if (layer_type == GIMP_INDEXED_IMAGE)
file_format = gimp_drawable_get_format (layer);
@@ -1410,6 +1423,7 @@ save_image (const gchar *filename,
gint32 image_ID,
gint32 drawable_ID,
gint32 orig_image_ID,
+ gint *bits_depth,
GError **error)
{
gint i, k, /* Looping vars */
@@ -1449,6 +1463,8 @@ save_image (const gchar *filename,
else
bit_depth = 16;
+ *bits_depth = bit_depth;
+
pp = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pp)
{
diff --git a/plug-ins/common/file-tiff-load.c b/plug-ins/common/file-tiff-load.c
index 647d608..d9f7d23 100644
--- a/plug-ins/common/file-tiff-load.c
+++ b/plug-ins/common/file-tiff-load.c
@@ -97,6 +97,7 @@ static gboolean load_dialog (TIFF *tif,
TiffSelectedPages *pages);
static gint32 load_image (const gchar *filename,
+ gint32 *layer_ID,
TIFF *tif,
TiffSelectedPages *pages,
gboolean *resolution_loaded,
@@ -262,11 +263,12 @@ run (const gchar *name,
if (run_it)
{
gint32 image;
+ gint32 layer_ID;
gboolean resolution_loaded = FALSE;
gimp_set_data (LOAD_PROC, &target, sizeof (target));
- image = load_image (param[1].data.d_string, tif, &pages,
+ image = load_image (param[1].data.d_string, &layer_ID, tif, &pages,
&resolution_loaded,
&error);
@@ -290,7 +292,7 @@ run (const gchar *name,
if (resolution_loaded)
flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
- gimp_image_metadata_load_finish (image, "image/tiff",
+ gimp_image_metadata_load_finish (image, layer_ID, "image/tiff",
metadata, flags,
run_mode == GIMP_RUN_INTERACTIVE);
@@ -518,6 +520,7 @@ load_dialog (TIFF *tif,
static gint32
load_image (const gchar *filename,
+ gint32 *layer_ID,
TIFF *tif,
TiffSelectedPages *pages,
gboolean *resolution_loaded,
@@ -1113,6 +1116,7 @@ load_image (const gchar *filename,
g_free (name);
}
+ *layer_ID = layer;
channel[0].ID = layer;
channel[0].buffer = gimp_drawable_get_buffer (layer);
channel[0].format = base_format;
diff --git a/plug-ins/common/file-tiff-save.c b/plug-ins/common/file-tiff-save.c
index 45fdbef..57fd017 100644
--- a/plug-ins/common/file-tiff-save.c
+++ b/plug-ins/common/file-tiff-save.c
@@ -234,7 +234,7 @@ run (const gchar *name,
{
/* Plug-in is either file_tiff_save or file_tiff_save2 */
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
GimpMetadataSaveFlags metadata_flags;
GimpParasite *parasite;
gint32 image = param[1].data.d_int32;
@@ -264,9 +264,9 @@ run (const gchar *name,
break;
}
- metadata = gimp_image_metadata_save_prepare (orig_image,
- "image/tiff",
- &metadata_flags);
+ attributes = gimp_image_metadata_save_prepare (orig_image,
+ "image/tiff",
+ &metadata_flags);
tsvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
tsvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
@@ -357,11 +357,17 @@ run (const gchar *name,
if (save_image (param[3].data.d_string, image, drawable, orig_image,
&saved_bpp, &error))
{
- if (metadata)
+ if (attributes)
{
GFile *file;
+ gchar *val = g_strdup_printf ("%u", (gushort) saved_bpp);
+
+ gimp_attributes_add_attribute (attributes,
+ gimp_attribute_new_string ("Exif.Image.BitsPerSample",
+ val, TYPE_SHORT)
+ );
- gimp_metadata_set_bits_per_sample (metadata, saved_bpp);
+ g_free (val);
if (tsvals.save_exif)
metadata_flags |= GIMP_METADATA_SAVE_EXIF;
@@ -386,8 +392,9 @@ run (const gchar *name,
file = g_file_new_for_path (param[3].data.d_string);
gimp_image_metadata_save_finish (image,
"image/tiff",
- metadata, metadata_flags,
+ attributes, metadata_flags,
file, NULL);
+ g_object_unref (attributes);
g_object_unref (file);
}
@@ -402,9 +409,6 @@ run (const gchar *name,
if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image);
-
- if (metadata)
- g_object_unref (metadata);
}
else
{
diff --git a/plug-ins/common/gimprc.common b/plug-ins/common/gimprc.common
index 421fb1e..2195d64 100644
--- a/plug-ins/common/gimprc.common
+++ b/plug-ins/common/gimprc.common
@@ -1,6 +1,7 @@
align_layers_RC = align-layers.rc.o
animation_optimize_RC = animation-optimize.rc.o
animation_play_RC = animation-play.rc.o
+attributes_RC = attributes.rc.o
blinds_RC = blinds.rc.o
blur_RC = blur.rc.o
blur_gauss_selective_RC = blur-gauss-selective.rc.o
@@ -76,13 +77,13 @@ grid_RC = grid.rc.o
guillotine_RC = guillotine.rc.o
hot_RC = hot.rc.o
illusion_RC = illusion.rc.o
+iptc_RC = iptc.rc.o
iwarp_RC = iwarp.rc.o
jigsaw_RC = jigsaw.rc.o
lcms_RC = lcms.rc.o
lens_flare_RC = lens-flare.rc.o
mail_RC = mail.rc.o
max_rgb_RC = max-rgb.rc.o
-metadata_RC = metadata.rc.o
newsprint_RC = newsprint.rc.o
nl_filter_RC = nl-filter.rc.o
noise_solid_RC = noise-solid.rc.o
diff --git a/plug-ins/common/iptc.c b/plug-ins/common/iptc.c
new file mode 100644
index 0000000..943c892
--- /dev/null
+++ b/plug-ins/common/iptc.c
@@ -0,0 +1,513 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * iptc.c
+ * Copyright (C) 2013 Hartmut Kuhse
+ *
+ * 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 <gegl.h>
+#include <gtk/gtk.h>
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_IMAGE_PROC "plug-in-image-iptc-editor"
+#define PLUG_IN_LAYER_PROC "plug-in-layer-iptc-editor"
+#define PLUG_IN_CHANNEL_PROC "plug-in-channel-iptc-editor"
+#define PLUG_IN_HELP "plug-in-iptc-editor"
+#define PLUG_IN_BINARY "iptc"
+
+#define THUMB_SIZE 48
+
+#define IPTC_PREFIX "Iptc."
+
+typedef struct
+{
+ gchar *tag;
+ gchar *mode;
+} iptc_tag;
+
+typedef enum
+{
+ ATT_IMAGE,
+ ATT_LAYER,
+ ATT_CHANNEL
+} AttributesSource;
+
+/* local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean iptc_dialog (gint32 image_id,
+ GimpAttributes *attributes,
+ AttributesSource source,
+ GtkBuilder *builder);
+
+static void iptc_dialog_set_iptc (GimpAttributes *attributes,
+ GtkBuilder *builder);
+
+static void iptc_change_iptc (GimpAttributes *attributes,
+ GtkBuilder *builder);
+
+
+/* local variables */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static const iptc_tag const iptc_tags[] =
+{
+ { "Iptc.Application2.Byline", "single" },
+ { "Iptc.Application2.BylineTitle", "single" },
+ { "Iptc.Application2.Caption", "multi" },
+ { "Iptc.Application2.Category", "single" },
+ { "Iptc.Application2.City", "single" },
+ { "Iptc.Application2.Copyright", "single" },
+ { "Iptc.Application2.CountryName", "single" },
+ { "Iptc.Application2.Credit", "single" },
+ { "Iptc.Application2.Headline", "multi" },
+ { "Iptc.Application2.Keywords", "multi" },
+ { "Iptc.Application2.ObjectName", "single" },
+ { "Iptc.Application2.ProvinceState", "single" },
+ { "Iptc.Application2.Source", "single" },
+ { "Iptc.Application2.SpecialInstructions", "multi" },
+ { "Iptc.Application2.SubLocation", "single" },
+ { "Iptc.Application2.SuppCategory", "multi" },
+ { "Iptc.Application2.TransmissionReference", "single" },
+ { "Iptc.Application2.Urgency", "single" },
+ { "Iptc.Application2.Writer", "single" }
+};
+
+/* functions */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef image_attribs_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" }
+ };
+
+ static const GimpParamDef layer_attribs_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_LAYER, "layer", "Input layer" }
+ };
+
+ static const GimpParamDef channel_attribs_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_CHANNEL, "channel", "Input channel" }
+ };
+
+ gimp_install_procedure (PLUG_IN_IMAGE_PROC,
+ N_("View and edit IPTC data"),
+ "View and edit IPTC information attached to the "
+ "current image. This iptc will be saved in the "
+ "file, depending on the output file format.",
+ "Hartmut Kuhse",
+ "Hartmut Kuhse",
+ "2014",
+ N_("Edit IPTC Metadata"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (image_attribs_args), 0,
+ image_attribs_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_IMAGE_PROC, "<Image>/Image");
+
+ gimp_install_procedure (PLUG_IN_LAYER_PROC,
+ N_("View and edit IPTC data"),
+ "View and edit IPTC information attached to the "
+ "current layer. This iptc will be saved in the "
+ "xcf file.",
+ "Hartmut Kuhse",
+ "Hartmut Kuhse",
+ "2014",
+ N_("Edit layers IPTC Metadata"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (layer_attribs_args), 0,
+ layer_attribs_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_LAYER_PROC, "<Layers>");
+
+ gimp_install_procedure (PLUG_IN_CHANNEL_PROC,
+ N_("View and edit IPTC data"),
+ "View and edit IPTC information attached to the "
+ "current channel. This iptc will be saved in the "
+ "xcf file.",
+ "Hartmut Kuhse",
+ "Hartmut Kuhse",
+ "2014",
+ N_("Edit channels IPTC Metadata"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (channel_attribs_args), 0,
+ channel_attribs_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_CHANNEL_PROC, "<Channels>");
+
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpAttributes *attributes;
+ GtkBuilder *builder;
+ AttributesSource source;
+ gint32 item_ID;
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ INIT_I18N();
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ if (! strcmp (name, PLUG_IN_IMAGE_PROC))
+ {
+ item_ID = param[1].data.d_image;
+
+ attributes = gimp_image_get_attributes (item_ID);
+ source = ATT_IMAGE;
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else if (! strcmp (name, PLUG_IN_LAYER_PROC))
+ {
+ item_ID = param[2].data.d_layer;
+
+ attributes = gimp_item_get_attributes (item_ID);
+ source = ATT_LAYER;
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else if (! strcmp (name, PLUG_IN_CHANNEL_PROC))
+ {
+ item_ID = param[2].data.d_channel;
+
+ attributes = gimp_item_get_attributes (item_ID);
+ source = ATT_CHANNEL;
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (! attributes)
+ {
+ attributes = gimp_attributes_new();
+ }
+
+ builder = gtk_builder_new ();
+
+ if (status == GIMP_PDB_SUCCESS && iptc_dialog (item_ID, attributes, source, builder))
+ {
+
+ iptc_change_iptc (attributes, builder);
+
+ if (! strcmp (name, PLUG_IN_IMAGE_PROC))
+ {
+ gimp_image_set_attributes (item_ID, attributes);
+ }
+ else if (! strcmp (name, PLUG_IN_LAYER_PROC))
+ {
+ gimp_item_set_attributes (item_ID, attributes);
+ }
+ else if (! strcmp (name, PLUG_IN_CHANNEL_PROC))
+ {
+ gimp_item_set_attributes (item_ID, attributes);
+ }
+ status = GIMP_PDB_SUCCESS;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ g_object_unref (attributes);
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+iptc_dialog (gint32 item_id,
+ GimpAttributes *attributes,
+ AttributesSource source,
+ GtkBuilder *builder)
+{
+ GtkWidget *dialog;
+ GtkWidget *iptc_vbox;
+ GtkWidget *content_area;
+ GdkPixbuf *pixbuf;
+ GtkWidget *label_header;
+ GtkWidget *label_info;
+ GtkWidget *thumb_box;
+ gchar *ui_file;
+ gchar *title;
+ gchar *fname;
+ gchar *header;
+ gchar *role;
+ GError *error = NULL;
+ gboolean run;
+
+
+ switch (source)
+ {
+ case ATT_IMAGE:
+ fname = g_filename_display_basename (gimp_image_get_uri (item_id));
+ header = g_strdup_printf ("Image");
+ title = g_strdup_printf ("Image: %s", fname);
+ role = g_strdup_printf ("gimp-image-iptc-dialog");
+ pixbuf = gimp_image_get_thumbnail (item_id, THUMB_SIZE, THUMB_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ break;
+ case ATT_LAYER:
+ fname = gimp_item_get_name (item_id);
+ header = g_strdup_printf ("Layer");
+ title = g_strdup_printf ("Layer: %s", fname);
+ role = g_strdup_printf ("gimp-layer-iptc-dialog");
+ pixbuf = gimp_drawable_get_thumbnail (item_id, THUMB_SIZE, THUMB_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+ break;
+ case ATT_CHANNEL:
+ fname = gimp_item_get_name (item_id);
+ header = g_strdup_printf ("Channel");
+ title = g_strdup_printf ("Channel: %s", fname);
+ role = g_strdup_printf ("gimp-channel-iptc-dialog");
+ pixbuf = gimp_drawable_get_thumbnail (item_id, THUMB_SIZE, THUMB_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+ break;
+ default:
+ fname = g_strdup_printf ("unknown");
+ header = g_strdup_printf ("Unknown");
+ title = g_strdup_printf ("Attributes: %s", fname);
+ role = g_strdup_printf ("gimp-iptc-dialog");
+ pixbuf = NULL;
+ break;
+ }
+
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-iptc.ui", NULL);
+
+ if (! gtk_builder_add_from_file (builder, ui_file, &error))
+ {
+ g_printerr ("Error occured while loading UI file!\n");
+ g_printerr ("Message: %s\n", error->message);
+ g_clear_error (&error);
+ g_free (ui_file);
+ g_object_unref (builder);
+ return FALSE;
+ }
+
+ g_free (ui_file);
+
+ dialog = gimp_dialog_new (title,
+ role,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_HELP,
+
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+
+ NULL);
+
+ g_free (title);
+ g_free (role);
+
+ gtk_window_set_default_size (GTK_WINDOW (dialog),
+ 600,
+ -1);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CLOSE,
+ -1);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ iptc_vbox = GTK_WIDGET (gtk_builder_get_object (builder,
+ "iptc-vbox"));
+ gtk_container_set_border_width (GTK_CONTAINER (iptc_vbox), 2);
+ gtk_box_pack_start (GTK_BOX (content_area), iptc_vbox, TRUE, TRUE, 0);
+
+ label_header = GTK_WIDGET (gtk_builder_get_object (builder, "label-header"));
+ gimp_label_set_attributes (GTK_LABEL (label_header),
+ PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
+ PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
+ -1);
+ gtk_label_set_text (GTK_LABEL (label_header), header);
+
+ label_info = GTK_WIDGET (gtk_builder_get_object (builder, "label-info"));
+ gimp_label_set_attributes (GTK_LABEL (label_info),
+ PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
+ -1);
+ gtk_label_set_text (GTK_LABEL (label_info), fname);
+
+ g_free (header);
+ g_free (fname);
+
+ if (pixbuf)
+ {
+ GtkWidget *image;
+
+ thumb_box = GTK_WIDGET (gtk_builder_get_object (builder, "thumb-box"));
+
+ if (thumb_box)
+ {
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ gtk_box_pack_end (GTK_BOX (thumb_box), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+ }
+ }
+
+ iptc_dialog_set_iptc (attributes, builder);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ return run;
+}
+
+
+/* private functions */
+
+static void
+iptc_dialog_set_iptc (GimpAttributes *attributes,
+ GtkBuilder *builder)
+{
+ gchar *value;
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (iptc_tags); i++)
+ {
+ GimpAttribute *attribute = NULL;
+ GtkWidget *widget = NULL;
+
+ attribute = gimp_attributes_get_attribute (attributes, iptc_tags[i].tag);
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, iptc_tags[i].tag));
+
+ if (attribute && widget)
+ {
+ value = gimp_attribute_get_string (attribute);
+
+ if (GTK_IS_ENTRY (widget))
+ {
+ GtkEntry *entry_widget = GTK_ENTRY (widget);
+
+ gtk_entry_set_text (entry_widget, value);
+ }
+ else if (GTK_IS_TEXT_VIEW (widget))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_set_text (buffer, value, -1);
+ }
+ g_free (value);
+ }
+ }
+}
+
+static void
+iptc_change_iptc (GimpAttributes *attributes,
+ GtkBuilder *builder)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (iptc_tags); i++)
+ {
+ GimpAttribute *attribute;
+
+ GObject *object = gtk_builder_get_object (builder,
+ iptc_tags[i].tag);
+
+ if (! strcmp ("single", iptc_tags[i].mode))
+ {
+ GtkEntry *entry = GTK_ENTRY (object);
+ const gchar *text = gtk_entry_get_text (entry);
+
+ if (strcmp ("", text))
+ {
+ attribute = gimp_attribute_new_string (iptc_tags[i].tag, (gchar *) text, TYPE_UNICODE);
+ gimp_attributes_add_attribute (attributes, attribute);
+ }
+ else
+ {
+ gimp_attributes_remove_attribute (attributes, iptc_tags[i].tag);
+ }
+ }
+ else if (!strcmp ("multi", iptc_tags[i].mode))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (object);
+ GtkTextBuffer *buffer;
+ GtkTextIter start;
+ GtkTextIter end;
+ gchar *text;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+
+ if (strcmp ("", text))
+ {
+ attribute = gimp_attribute_new_string (iptc_tags[i].tag, (gchar *) text, TYPE_MULTIPLE);
+ gimp_attributes_add_attribute (attributes, attribute);
+ }
+ else
+ {
+ gimp_attributes_remove_attribute (attributes, iptc_tags[i].tag);
+ }
+
+ g_free (text);
+ }
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-load.c b/plug-ins/file-jpeg/jpeg-load.c
index 40aa0e6..b52e2e6 100644
--- a/plug-ins/file-jpeg/jpeg-load.c
+++ b/plug-ins/file-jpeg/jpeg-load.c
@@ -58,12 +58,13 @@ gint32 preview_layer_ID;
gint32
load_image (const gchar *filename,
GimpRunMode runmode,
+ gint32 *layer_ID,
gboolean preview,
gboolean *resolution_loaded,
GError **error)
{
gint32 volatile image_ID;
- gint32 layer_ID;
+ gint32 _layer_ID;
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
jpeg_saved_marker_ptr marker;
@@ -229,11 +230,11 @@ load_image (const gchar *filename,
cinfo.output_width,
cinfo.output_height,
layer_type, 100, GIMP_NORMAL_MODE);
- layer_ID = preview_layer_ID;
+ _layer_ID = preview_layer_ID;
}
else
{
- layer_ID = gimp_layer_new (image_ID, _("Background"),
+ _layer_ID = gimp_layer_new (image_ID, _("Background"),
cinfo.output_width,
cinfo.output_height,
layer_type, 100, GIMP_NORMAL_MODE);
@@ -338,7 +339,7 @@ load_image (const gchar *filename,
* loop counter, so that we don't have to keep track ourselves.
*/
- buffer = gimp_drawable_get_buffer (layer_ID);
+ buffer = gimp_drawable_get_buffer (_layer_ID);
format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8");
while (cinfo.output_scanline < cinfo.output_height)
@@ -407,7 +408,9 @@ load_image (const gchar *filename,
gimp_progress_update (1.0);
}
- gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+ gimp_image_insert_layer (image_ID, _layer_ID, -1, 0);
+
+ *layer_ID = _layer_ID;
return image_ID;
}
diff --git a/plug-ins/file-jpeg/jpeg-load.h b/plug-ins/file-jpeg/jpeg-load.h
index d4e61b0..281e03d 100644
--- a/plug-ins/file-jpeg/jpeg-load.h
+++ b/plug-ins/file-jpeg/jpeg-load.h
@@ -20,6 +20,7 @@
gint32 load_image (const gchar *filename,
GimpRunMode runmode,
+ gint32 *layer_ID,
gboolean preview,
gboolean *resolution_loaded,
GError **error);
diff --git a/plug-ins/file-jpeg/jpeg-save.c b/plug-ins/file-jpeg/jpeg-save.c
index eb45b15..269f311 100644
--- a/plug-ins/file-jpeg/jpeg-save.c
+++ b/plug-ins/file-jpeg/jpeg-save.c
@@ -186,6 +186,7 @@ background_jpeg_save (PreviewPersistent *pp)
GFile *file = g_file_new_for_path (pp->file_name);
GFileInfo *info;
gchar *text;
+ gint32 layer_ID;
GError *error = NULL;
info = g_file_query_info (file,
@@ -216,7 +217,7 @@ background_jpeg_save (PreviewPersistent *pp)
g_object_unref (file);
/* and load the preview */
- load_image (pp->file_name, GIMP_RUN_NONINTERACTIVE, TRUE, NULL, NULL);
+ load_image (pp->file_name, GIMP_RUN_NONINTERACTIVE, &layer_ID, TRUE, NULL, NULL);
}
/* we cleanup here (load_image doesn't run in the background) */
diff --git a/plug-ins/file-jpeg/jpeg.c b/plug-ins/file-jpeg/jpeg.c
index ef2c15c..3606fc1 100644
--- a/plug-ins/file-jpeg/jpeg.c
+++ b/plug-ins/file-jpeg/jpeg.c
@@ -173,6 +173,7 @@ run (const gchar *name,
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint32 image_ID;
+ gint32 layer_ID;
gint32 drawable_ID;
GimpParasite *parasite;
GError *error = NULL;
@@ -210,7 +211,7 @@ run (const gchar *name,
break;
}
- image_ID = load_image (param[1].data.d_string, run_mode, FALSE,
+ image_ID = load_image (param[1].data.d_string, run_mode, &layer_ID, FALSE,
&resolution_loaded, &error);
if (image_ID != -1)
@@ -223,12 +224,12 @@ run (const gchar *name,
if (metadata)
{
- GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
if (resolution_loaded)
flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
- gimp_image_metadata_load_finish (image_ID, "image/jpeg",
+ gimp_image_metadata_load_finish (image_ID, layer_ID, "image/jpeg",
metadata, flags,
load_interactive);
@@ -286,7 +287,7 @@ run (const gchar *name,
}
else if (strcmp (name, SAVE_PROC) == 0)
{
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
GimpMetadataSaveFlags metadata_flags;
gint32 orig_image_ID;
GimpExportReturn export = GIMP_EXPORT_CANCEL;
@@ -336,7 +337,7 @@ run (const gchar *name,
break;
}
- metadata = gimp_image_metadata_save_prepare (orig_image_ID,
+ attributes = gimp_image_metadata_save_prepare (orig_image_ID,
"image/jpeg",
&metadata_flags);
@@ -534,11 +535,12 @@ run (const gchar *name,
/* write metadata */
- if (metadata)
+ if (attributes)
{
GFile *file;
+ GimpAttribute *attribute;
- gimp_metadata_set_bits_per_sample (metadata, 8);
+ gimp_attributes_new_attribute (attributes, "Exif.Image.BitsPerSample", "8", TYPE_SHORT);
if (jsvals.save_exif)
metadata_flags |= GIMP_METADATA_SAVE_EXIF;
@@ -563,14 +565,12 @@ run (const gchar *name,
file = g_file_new_for_path (param[3].data.d_string);
gimp_image_metadata_save_finish (orig_image_ID,
"image/jpeg",
- metadata, metadata_flags,
+ attributes, metadata_flags,
file, NULL);
g_object_unref (file);
+ g_object_unref (attributes);
}
}
-
- if (metadata)
- g_object_unref (metadata);
}
else
{
diff --git a/plug-ins/file-psd/psd-save.c b/plug-ins/file-psd/psd-save.c
index 376c9a2..dde2abc 100644
--- a/plug-ins/file-psd/psd-save.c
+++ b/plug-ins/file-psd/psd-save.c
@@ -271,7 +271,7 @@ run (const gchar *name,
{
gint32 image_id;
gint32 drawable_id;
- GimpMetadata *metadata;
+ GimpAttributes *attributes;
GimpMetadataSaveFlags metadata_flags;
GimpExportReturn export = GIMP_EXPORT_IGNORE;
@@ -306,26 +306,26 @@ run (const gchar *name,
break;
}
- metadata = gimp_image_metadata_save_prepare (image_id,
+ attributes = gimp_image_metadata_save_prepare (image_id,
"image/x-psd",
&metadata_flags);
if (save_image (param[3].data.d_string, image_id, &error))
{
- if (metadata)
+ if (attributes)
{
GFile *file;
- gimp_metadata_set_bits_per_sample (metadata, 8);
+ gimp_attributes_new_attribute (attributes, "Exif.Image.BitsPerSample", "8", TYPE_SHORT);
file = g_file_new_for_path (param[3].data.d_string);
gimp_image_metadata_save_finish (image_id,
"image/x-psd",
- metadata, metadata_flags,
+ attributes, metadata_flags,
file, NULL);
g_object_unref (file);
- g_object_unref (metadata);
+ g_object_unref (attributes);
}
values[0].data.d_status = GIMP_PDB_SUCCESS;
@@ -344,9 +344,6 @@ run (const gchar *name,
if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image_id);
-
- if (metadata)
- g_object_unref (metadata);
}
}
diff --git a/plug-ins/file-psd/psd.c b/plug-ins/file-psd/psd.c
index a7911e6..1310dc0 100644
--- a/plug-ins/file-psd/psd.c
+++ b/plug-ins/file-psd/psd.c
@@ -226,7 +226,7 @@ run (const gchar *name,
if (resolution_loaded)
flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
- gimp_image_metadata_load_finish (image_ID, "image/x-psd",
+ gimp_image_metadata_load_finish (image_ID, -1, "image/x-psd",
metadata, flags,
interactive);
diff --git a/plug-ins/metainfo/Makefile.am b/plug-ins/metainfo/Makefile.am
new file mode 100644
index 0000000..ffb8138
--- /dev/null
+++ b/plug-ins/metainfo/Makefile.am
@@ -0,0 +1,61 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+metainfo_RC = metainfo.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins
+
+libexec_PROGRAMS = metainfo
+
+metainfo_SOURCES = \
+ metainfo.c \
+ metainfo.h \
+ interface.h \
+ interface.c \
+ metainfo-helper.c \
+ metainfo-helper.h \
+ page-description.c \
+ page-description.h \
+ page-artwork.c \
+ page-artwork.h \
+ page-administration.c \
+ page-administration.h \
+ page-rights.c \
+ page-rights.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GEXIV2_LIBS) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metainfo_RC)
diff --git a/plug-ins/metainfo/interface.c b/plug-ins/metainfo/interface.c
new file mode 100644
index 0000000..7235d42
--- /dev/null
+++ b/plug-ins/metainfo/interface.c
@@ -0,0 +1,236 @@
+/* interface.c - user interface for the metadata editor
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "interface.h"
+#include "metainfo.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static gboolean save_attributes_to_image;
+static GtkWidget *save_button;
+
+/* show a transient message dialog */
+void
+metainfo_message_dialog (GtkMessageType type,
+ GtkWindow *parent,
+ const gchar *title,
+ const gchar *message)
+{
+ GtkWidget *dlg;
+
+ dlg = gtk_message_dialog_new (parent, 0, type, GTK_BUTTONS_OK, "%s", message);
+
+ if (title)
+ gtk_window_set_title (GTK_WINDOW (dlg), title);
+
+ gtk_window_set_role (GTK_WINDOW (dlg), "metainfo-message");
+ gtk_dialog_run (GTK_DIALOG (dlg));
+ gtk_widget_destroy (dlg);
+}
+
+static void
+metainfo_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ GimpAttributes *attributes = (GimpAttributes *) data;
+
+ switch (response_id)
+ {
+ case METAINFO_RESPONSE_SAVE:
+ page_description_save_to_attributes (attributes);
+ page_artwork_save_to_attributes (attributes);
+ page_administration_save_to_attributes (attributes);
+ page_rights_save_to_attributes (attributes);
+ save_attributes_to_image = TRUE;
+ break;
+ case GTK_RESPONSE_OK:
+ default:
+ save_attributes_to_image = FALSE;
+ break;
+ }
+ gtk_widget_destroy (widget);
+}
+
+gboolean
+metainfo_get_save_attributes (void)
+{
+ return save_attributes_to_image;
+}
+
+gboolean
+metainfo_dialog (gint32 image_id,
+ GimpAttributes *attributes)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *attributes_vbox;
+ GtkWidget *content_area;
+ GdkPixbuf *pixbuf;
+ GtkWidget *label_header;
+ GtkWidget *label_info;
+ GtkWidget *thumb_box;
+
+ gchar *header;
+ gchar *ui_file;
+ gchar *title;
+ gchar *fname;
+ gchar *role;
+ GError *error = NULL;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ fname = g_filename_display_basename (gimp_image_get_uri (image_id));
+ header = g_strdup_printf ("Image");
+ role = g_strdup_printf ("gimp-image-metainfo-dialog");
+ pixbuf = gimp_image_get_thumbnail (image_id, THUMB_SIZE, THUMB_SIZE,
+ GIMP_PIXBUF_SMALL_CHECKS);
+ title = g_strdup_printf ("Digital Rights: %s", fname);
+
+ builder = gtk_builder_new ();
+
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-metainfo.ui", NULL);
+
+ if (! gtk_builder_add_from_file (builder, ui_file, &error))
+ {
+ g_printerr ("Error occured while loading UI file!\n");
+ g_printerr ("Message: %s\n", error->message);
+ g_clear_error (&error);
+ g_free (ui_file);
+ g_object_unref (builder);
+ return FALSE;
+ }
+
+ g_free (ui_file);
+
+ dialog = gimp_dialog_new (title,
+ role,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_HELP,
+
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ save_button = gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_SAVE, METAINFO_RESPONSE_SAVE);
+ gtk_widget_set_sensitive (GTK_WIDGET (save_button), FALSE);
+ set_save_attributes_button (save_button);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (metainfo_dialog_response),
+ attributes);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ g_free (title);
+ g_free (role);
+
+ gtk_window_set_default_size (GTK_WINDOW (dialog),
+ 250,
+ 500);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ METAINFO_RESPONSE_SAVE,
+ GTK_RESPONSE_OK,
+ -1);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ attributes_vbox = GTK_WIDGET (gtk_builder_get_object (builder,
+ "metainfo-vbox"));
+
+ gtk_container_set_border_width (GTK_CONTAINER (attributes_vbox), 2);
+ gtk_box_pack_start (GTK_BOX (content_area), attributes_vbox, TRUE, TRUE, 0);
+
+ label_header = GTK_WIDGET (gtk_builder_get_object (builder, "label-header"));
+ gimp_label_set_attributes (GTK_LABEL (label_header),
+ PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
+ PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
+ -1);
+ gtk_label_set_text (GTK_LABEL (label_header), header);
+
+ label_info = GTK_WIDGET (gtk_builder_get_object (builder, "label-info"));
+ gimp_label_set_attributes (GTK_LABEL (label_info),
+ PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
+ -1);
+ gtk_label_set_text (GTK_LABEL (label_info), fname);
+
+ g_free (header);
+ g_free (fname);
+
+ if (pixbuf)
+ {
+ GtkWidget *image;
+
+ thumb_box = GTK_WIDGET (gtk_builder_get_object (builder, "thumb-box"));
+
+ if (thumb_box)
+ {
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ gtk_box_pack_end (GTK_BOX (thumb_box), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+ }
+ }
+
+ page_description_read_from_attributes (attributes,
+ builder);
+ page_artwork_read_from_attributes (attributes,
+ builder);
+ page_administration_read_from_attributes (attributes,
+ builder);
+ page_rights_read_from_attributes (attributes,
+ builder);
+
+ gtk_widget_show (GTK_WIDGET (dialog));
+
+ gtk_main ();
+
+ return TRUE;
+}
diff --git a/plug-ins/metainfo/interface.h b/plug-ins/metainfo/interface.h
new file mode 100644
index 0000000..a1589ff
--- /dev/null
+++ b/plug-ins/metainfo/interface.h
@@ -0,0 +1,49 @@
+/* interface.h - user interface for the metadata editor
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+#include <glib-object.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "page-description.h"
+#include "page-artwork.h"
+#include "page-administration.h"
+#include "page-rights.h"
+
+G_BEGIN_DECLS
+
+#define THUMB_SIZE 48
+
+#define METAINFO_RESPONSE_SAVE 201
+
+void metainfo_message_dialog (GtkMessageType type,
+ GtkWindow *parent,
+ const gchar *title,
+ const gchar *message);
+gboolean metainfo_get_save_attributes (void);
+
+gboolean metainfo_dialog (gint32 image_ID,
+ GimpAttributes *attributes);
+
+G_END_DECLS
+
+#endif /* INTERFACE_H */
diff --git a/plug-ins/metainfo/metainfo-helper.c b/plug-ins/metainfo/metainfo-helper.c
new file mode 100644
index 0000000..4005193
--- /dev/null
+++ b/plug-ins/metainfo/metainfo-helper.c
@@ -0,0 +1,272 @@
+/* metainfo-helper.c
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metainfo-helper.h"
+
+static GtkWidget *save_attributes_button = NULL;
+/**
+ * string_replace_str:
+ *
+ * @original: the original string
+ * @old_pattern: the pattern to replace
+ * @replacement: the replacement
+ *
+ * replaces @old_pattern by @replacement in @original
+ * This routine is copied from VALA.
+ *
+ * Return value: the new string.
+ *
+ * Since: 2.10
+ */
+gchar*
+string_replace_str (const gchar* original, const gchar* old_pattern, const gchar* replacement) {
+ gchar *result = NULL;
+ GError *_inner_error_ = NULL;
+
+ g_return_val_if_fail (original != NULL, NULL);
+ g_return_val_if_fail (old_pattern != NULL, NULL);
+ g_return_val_if_fail (replacement != NULL, NULL);
+
+ {
+ GRegex *regex = NULL;
+ const gchar *_tmp0_ = NULL;
+ gchar *_tmp1_ = NULL;
+ gchar *_tmp2_ = NULL;
+ GRegex *_tmp3_ = NULL;
+ GRegex *_tmp4_ = NULL;
+ gchar *_tmp5_ = NULL;
+ GRegex *_tmp6_ = NULL;
+ const gchar *_tmp7_ = NULL;
+ gchar *_tmp8_ = NULL;
+ gchar *_tmp9_ = NULL;
+
+ _tmp0_ = old_pattern;
+ _tmp1_ = g_regex_escape_string (_tmp0_, -1);
+ _tmp2_ = _tmp1_;
+ _tmp3_ = g_regex_new (_tmp2_, 0, 0, &_inner_error_);
+ _tmp4_ = _tmp3_;
+ _g_free0 (_tmp2_);
+ regex = _tmp4_;
+
+ if (_inner_error_ != NULL)
+ {
+ if (_inner_error_->domain == G_REGEX_ERROR)
+ {
+ goto __catch0_g_regex_error;
+ }
+ g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+
+ _tmp6_ = regex;
+ _tmp7_ = replacement;
+ _tmp8_ = g_regex_replace_literal (_tmp6_, original, (gssize) (-1), 0, _tmp7_, 0, &_inner_error_);
+ _tmp5_ = _tmp8_;
+
+ if (_inner_error_ != NULL)
+ {
+ _g_regex_unref0 (regex);
+ if (_inner_error_->domain == G_REGEX_ERROR)
+ {
+ goto __catch0_g_regex_error;
+ }
+ _g_regex_unref0 (regex);
+ g_critical ("file %s: line %d: unexpected error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+
+ _tmp9_ = _tmp5_;
+ _tmp5_ = NULL;
+ result = _tmp9_;
+ _g_free0 (_tmp5_);
+ _g_regex_unref0 (regex);
+ return result;
+ }
+
+ goto __finally0;
+ __catch0_g_regex_error:
+ {
+ GError* e = NULL;
+ e = _inner_error_;
+ _inner_error_ = NULL;
+ g_assert_not_reached ();
+ _g_error_free0 (e);
+ }
+
+ __finally0:
+ if (_inner_error_ != NULL)
+ {
+ g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__,
_inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
+ g_clear_error (&_inner_error_);
+ return NULL;
+ }
+}
+
+/**
+ * string_index_of:
+ *
+ * @haystack: a #gchar
+ * @needle: a #gchar
+ * @start_index: a #gint
+ *
+ *
+ * Return value: #gint that points to the position
+ * of @needle in @haystack, starting at @start_index,
+ * or -1, if @needle is not found
+ *
+ * Since: 2.10
+ */
+gint
+string_index_of (const gchar* haystack, const gchar* needle, gint start_index)
+{
+ gint result = 0;
+ gchar* temp1_ = NULL;
+ g_return_val_if_fail (haystack != NULL, -1);
+ g_return_val_if_fail (needle != NULL, -1);
+ temp1_ = strstr (((gchar*) haystack) + start_index, needle);
+ if (temp1_ != NULL)
+ {
+ result = (gint) (temp1_ - ((gchar*) haystack));
+ return result;
+ }
+ else
+ {
+ result = -1;
+ return result;
+ }
+}
+
+/**
+ * string_strnlen:
+ *
+ * @str: a #gchar
+ * @maxlen: a #glong
+ *
+ * Returns the length of @str or @maxlen, if @str
+ * is longer that @maxlen
+ *
+ * Return value: #glong
+ *
+ * Since: 2.10
+ */
+glong
+string_strnlen (gchar* str, glong maxlen)
+{
+ glong result = 0L;
+ gchar* temp1_ = NULL;
+ temp1_ = memchr (str, 0, (gsize) maxlen);
+ if (temp1_ == NULL)
+ {
+ return maxlen;
+ }
+ else
+ {
+ result = (glong) (temp1_ - str);
+ return result;
+ }
+}
+
+/**
+ * string_substring:
+ *
+ * @string: a #gchar
+ * @offset: a #glong
+ * @len: a #glong
+ *
+ * Returns a substring of @string, starting at @offset
+ * with a legth of @len
+ *
+ * Return value: #gchar to be freed if no longer use
+ *
+ * Since: 2.10
+ */
+gchar*
+string_substring (const gchar* string, glong offset, glong len)
+{
+ gchar* result = NULL;
+ glong string_length = 0L;
+ gboolean _tmp0_ = FALSE;
+ g_return_val_if_fail(string != NULL, NULL);
+ if (offset >= ((glong) 0))
+ {
+ _tmp0_ = len >= ((glong) 0);
+ }
+ else
+ {
+ _tmp0_ = FALSE;
+ }
+
+ if (_tmp0_)
+ {
+ string_length = string_strnlen ((gchar*) string, offset + len);
+ }
+ else
+ {
+ string_length = (glong) strlen (string);
+ }
+
+ if (offset < ((glong) 0))
+ {
+ offset = string_length + offset;
+ g_return_val_if_fail (offset >= ((glong ) 0), NULL);
+ }
+ else
+ {
+ g_return_val_if_fail (offset <= string_length, NULL);
+ }
+ if (len < ((glong) 0))
+ {
+ len = string_length - offset;
+ }
+
+ g_return_val_if_fail ((offset + len) <= string_length, NULL);
+ result = g_strndup (((gchar*) string) + offset, (gsize) len);
+ return result;
+}
+
+GObject *
+get_widget_from_label (GtkBuilder *builder, const gchar *label)
+{
+ GObject *obj;
+
+ obj = gtk_builder_get_object (builder, label);
+
+ return obj;
+}
+
+void
+set_save_attributes_button (GtkButton *button)
+{
+ save_attributes_button = GTK_WIDGET (button);
+}
+
+void
+set_save_attributes_button_sensitive (gboolean sensitive)
+{
+ gtk_widget_set_sensitive (save_attributes_button, sensitive);
+}
diff --git a/plug-ins/metainfo/metainfo-helper.h b/plug-ins/metainfo/metainfo-helper.h
new file mode 100644
index 0000000..07fabf4
--- /dev/null
+++ b/plug-ins/metainfo/metainfo-helper.h
@@ -0,0 +1,102 @@
+/* metainfo-helper.h
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __METAINFO_HELPER_H__
+#define __METAINFO_HELPER_H__
+
+#include <glib-object.h>
+
+#define _g_free0(var) (var = (g_free (var), NULL))
+#define _g_regex_unref0(var) ((var == NULL) ? NULL : (var = (g_regex_unref (var), NULL)))
+#define _g_error_free0(var) ((var == NULL) ? NULL : (var = (g_error_free (var), NULL)))
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ WIDGET_TYPE_ENTRY,
+ WIDGET_TYPE_COMBOBOX
+} WidgetType;
+
+typedef enum
+{
+ LIST_TYPE_NONE,
+ LIST_TYPE_SEQ,
+ LIST_TYPE_BAG,
+ LIST_TYPE_LANGALT
+} ListType;
+
+typedef struct _ComboBoxData ComboBoxData;
+
+struct _ComboBoxData
+{
+ const gchar *val_in_combo; /* translateable - value shown in the combobox */
+ const gchar *val_in_tag; /* value saved as tag value */
+};
+
+
+typedef struct _MetadataEntry MetadataEntry;
+
+struct _MetadataEntry
+{
+ gchar *label; /* translateable - label of the widget */
+ const gchar *ui_label; /* name of the label widget in GtkBuilder ui file */
+ const gchar *ui_entry; /* name of the entry widget in GtkBuilder ui file */
+ const gchar *xmp_tag; /* xmp tag, saved in GimpAttribute.name */
+ WidgetType widget_type; /* type of entry widget in GtkBuilder ui : GtkEntry
or GtkComboBox */
+ gint number_of_comboarray; /*number of entry in ComboBoxData - array. Only valid
for combobox entries */
+};
+
+typedef struct _StructureElement StructureElement;
+
+struct _StructureElement
+{
+ gint number_of_element; /* simply the number, corresponding to
STRUCTURES_ON_PAGE */
+ const gchar *identifier; /* translateble - identifier for combobox entries */
+ const gchar *struct_tag; /* structure tag without array number */
+ GimpAttributeStructureType struct_type; /* type of structure, gexiv2 cannot get the right
list type from tag*/
+ const gchar *struct_combo_widget; /* name of the combobox widget for this structure */
+ const gchar *struct_liststore_widget; /* name of the liststore of the combobox for this
structure */
+ const gchar *add_widget; /* name of the add structure button for this
structure */
+ const gchar *remove_widget; /* name of the remove structure button for this
structure */
+};
+
+
+gchar* string_replace_str (const gchar *original,
+ const gchar *old_pattern,
+ const gchar *replacement);
+gint string_index_of (const gchar *haystack,
+ const gchar *needle,
+ gint start_index);
+glong string_strnlen (gchar *str,
+ glong maxlen);
+gchar* string_substring (const gchar *attribute,
+ glong offset,
+ glong len);
+GObject* get_widget_from_label (GtkBuilder *builder,
+ const gchar *label);
+void set_save_attributes_button (GtkButton *button);
+void set_save_attributes_button_sensitive (gboolean sensitive);
+
+
+
+
+G_END_DECLS
+
+#endif /* __METAINFO_HELPER_H__ */
diff --git a/plug-ins/metainfo/metainfo.c b/plug-ins/metainfo/metainfo.c
new file mode 100644
index 0000000..55224f4
--- /dev/null
+++ b/plug-ins/metainfo/metainfo.c
@@ -0,0 +1,156 @@
+/* metainfo.c - main() for the metainfo editor
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metainfo.h"
+#include "interface.h"
+
+
+/* prototypes of local functions */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+/* local variables */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/* local functions */
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef editor_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" }
+ };
+
+
+ gimp_install_procedure (EDITOR_PROC,
+ N_("View and edit metainformation"),
+ "View and edit meta information reagarding digital "
+ "rights, attached to the current image. "
+ "This metainformation will be saved in the file, "
+ "depending on the output file format.",
+ "Hartmut Kuhse <hatti gimp org>",
+ "Hartmut Kuhse <hatti gimp org>",
+ "2014",
+ N_("Digital Rights"),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (editor_args), 0,
+ editor_args, NULL);
+
+ gimp_plugin_menu_register (EDITOR_PROC, "<Image>/Image");
+ // XXX gimp_plugin_icon_register (EDITOR_PROC, GIMP_ICON_TYPE_STOCK_ID,
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[4];
+ gint32 image_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpAttributes *attributes;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ INIT_I18N();
+
+ if (! strcmp (name, EDITOR_PROC))
+ image_ID = param[1].data.d_image;
+ else
+ image_ID = param[0].data.d_image;
+
+
+
+ /* Now check what we are supposed to do */
+if (! strcmp (name, EDITOR_PROC))
+ {
+ GimpRunMode run_mode;
+
+ run_mode = param[0].data.d_int32;
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ attributes = gimp_image_get_attributes (image_ID);
+
+ if (! attributes)
+ {
+ attributes = gimp_attributes_new ();
+ }
+
+ if (attributes)
+ {
+ if (! metainfo_dialog (image_ID, attributes))
+ status = GIMP_PDB_CANCEL;
+ else
+ {
+ if (metainfo_get_save_attributes())
+ gimp_image_set_attributes (image_ID, attributes);
+ status = GIMP_PDB_SUCCESS;
+ }
+ }
+ else
+ {
+ g_message (_("General attributes failure."));
+ }
+ }
+ else
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ }
+
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/metainfo/metainfo.h b/plug-ins/metainfo/metainfo.h
new file mode 100644
index 0000000..e50a6bb
--- /dev/null
+++ b/plug-ins/metainfo/metainfo.h
@@ -0,0 +1,29 @@
+/* metadata.h - defines for the metadata editor
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __METAINFO_H__
+#define __METAINFO_H__
+
+
+#define EDITOR_PROC "plug-in-metainfo-editor"
+#define PLUG_IN_BINARY "metainfo"
+#define PLUG_IN_ROLE "plug-in-metainfo"
+#define PLUG_IN_HELP "plug-in-metainfo"
+
+#endif /* __METAINFO_H__ */
diff --git a/plug-ins/metainfo/page-administration.c b/plug-ins/metainfo/page-administration.c
new file mode 100644
index 0000000..64793b8
--- /dev/null
+++ b/plug-ins/metainfo/page-administration.c
@@ -0,0 +1,1039 @@
+/* page-administration.c
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metainfo-helper.h"
+#include "page-administration.h"
+
+
+static void page_administration_init_ui (GtkBuilder *builder);
+static void page_administration_set_entry_sensitive (GtkBuilder *builder,
+ const gchar *struct_name,
+ gboolean sensitive);
+static void page_administration_init_combobox (GtkBuilder *builder);
+static void page_administration_set_to_ui (GtkBuilder *builder,
+ gint repaint);
+static void page_administration_entry_activate_cb (GtkWidget *widget,
+ gpointer userdata);
+static gboolean page_administration_entry_focus_out_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata);
+static gboolean page_administration_store_in_hash_table (const gchar *entry_name,
+ const gchar *value,
+ gint nr);
+static void page_administration_structure_add (GtkButton *button,
+ gpointer userdata);
+static void page_administration_structure_remove (GtkButton *button,
+ gpointer userdata);
+static void page_administration_entry_combo_changed_callback (GtkWidget *combo,
+ gpointer userdata);
+static void page_administration_combobox_changed_callback (GtkWidget *combo,
+ gpointer userdata);
+static void page_administration_structure_save (GtkBuilder *builder,
+ gint
struct_number);
+
+
+#define STRUCTURES_ON_PAGE 2
+#define COMBOBOX_WITH_DATA 1
+
+
+static GHashTable *elements_table;
+static gint curr_shown_structure[STRUCTURES_ON_PAGE];
+static gint highest_structure[STRUCTURES_ON_PAGE];
+
+static StructureElement struct_element [] =
+ {
+ {0,
+ N_("Image supplier"),
+ "Xmp.plus.ImageSupplier",
+ STRUCTURE_TYPE_SEQ,
+ "imagesupplier-combo",
+ "imagesupplier-liststore",
+ "imagesupplier-button-plus",
+ "imagesupplier-button-minus"},
+
+ {1,
+ N_("Registry ID"),
+ "Xmp.iptcExt.RegistryId",
+ STRUCTURE_TYPE_BAG,
+ "registryid-combo",
+ "registryid-liststore",
+ "registryid-button-plus",
+ "registryid-button-minus"}
+ };
+
+
+static ComboBoxData combobox_data[][256] =
+{
+ {
+ {N_(" "), ""},
+ {N_("Original digital capture of a real life scene"),
"http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture"},
+ {N_("Digitised from a negative on film"),
"http://cv.iptc.org/newscodes/digitalsourcetype/negativeFilm"},
+ {N_("Digitised from a positive on film"),
"http://cv.iptc.org/newscodes/digitalsourcetype/positiveFilm"},
+ {N_("Digitised from a print on non-transparent medium"),
"http://cv.iptc.org/newscodes/digitalsourcetype/print"},
+ {N_("Created by software"),
"http://cv.iptc.org/newscodes/digitalsourcetype/softwareImage"}
+ },
+};
+
+static MetadataEntry administration_entries[] =
+ {
+ {N_("Image supplier name"),
+ "imagesupplier-name-label",
+ "imagesupplier-name-entry",
+ "Xmp.plus.ImageSupplier[x]/plus:ImageSupplierName",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Image supplier ID"),
+ "imagesupplier-id-label",
+ "imagesupplier-id-entry",
+ "Xmp.plus.ImageSupplier[x]/plus:ImageSupplierID",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Image supplier image ID"),
+ "imagesupplier-imageid-label",
+ "imagesupplier-imageid-entry",
+ "Xmp.plus.ImageSupplierImageID",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Registry organistaion ID"),
+ "regorgid-label",
+ "regorgid-entry",
+ "Xmp.iptcExt.RegistryId[x]/iptcExt:RegOrgId",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Registry item ID"),
+ "regitemid-label",
+ "regitemid-entry",
+ "Xmp.iptcExt.RegistryId[x]/iptcExt:RegItemId",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Max available height"),
+ "maxavailheight-label",
+ "maxavailheight-entry",
+ "Xmp.iptcExt.MaxAvailHeight",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Max available width"),
+ "maxavailwidth-label",
+ "maxavailwidth-entry",
+ "Xmp.iptcExt.MaxAvailWidth",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Digital source type"),
+ "digitalsourcetype-label",
+ "digitalsourcetype-combobox",
+ "Xmp.iptcExt.DigitalSourceType",
+ WIDGET_TYPE_COMBOBOX,
+ 0}
+ };
+
+
+static void
+page_administration_init_ui (GtkBuilder *builder)
+{
+ GObject *obj;
+ gint i;
+
+ elements_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ for (i = 0; i < G_N_ELEMENTS(administration_entries); i++)
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, administration_entries[i].ui_label));
+
+ gtk_label_set_text (GTK_LABEL (obj), administration_entries[i].label);
+
+ obj = G_OBJECT (get_widget_from_label (builder, administration_entries[i].ui_entry));
+ gtk_widget_set_name (GTK_WIDGET (obj), administration_entries[i].ui_entry);
+
+ if (administration_entries[i].widget_type == WIDGET_TYPE_ENTRY)
+ {
+ g_signal_connect (GTK_WIDGET (obj), "focus-out-event",
+ G_CALLBACK (page_administration_entry_focus_out_cb),
+ (gpointer) administration_entries[i].ui_entry);
+ g_signal_connect (GTK_ENTRY (obj), "activate",
+ G_CALLBACK (page_administration_entry_activate_cb),
+ (gpointer) administration_entries[i].ui_entry);
+ }
+ else if (administration_entries[i].widget_type == WIDGET_TYPE_COMBOBOX)
+ {
+ gint array_nr = administration_entries[i].number_of_comboarray;
+
+ if (array_nr >= 0)
+ {
+ gint i;
+ GtkTreeIter iter;
+ GtkListStore *liststore;
+ gint array_length;
+
+ liststore = GTK_LIST_STORE(gtk_combo_box_get_model (GTK_COMBO_BOX (obj)));
+
+ array_length = G_N_ELEMENTS(combobox_data[array_nr]);
+
+ for (i = 0; i < array_length; i++) /*get info about structure */
+ {
+ const gchar *val_in_combo = combobox_data[array_nr][i].val_in_combo;
+
+ if (! val_in_combo)
+ break;
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, val_in_combo,
+ -1);
+ }
+
+ g_signal_connect (obj, "changed",
+ G_CALLBACK (page_administration_entry_combo_changed_callback),
+ GINT_TO_POINTER (array_nr));
+ }
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(struct_element); i++) /*get info about structure */
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].add_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].add_widget);
+ g_signal_connect (obj, "clicked",
+ G_CALLBACK (page_administration_structure_add),
+ builder);
+
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].remove_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].remove_widget);
+ g_signal_connect (obj, "clicked",
+ G_CALLBACK (page_administration_structure_remove),
+ builder);
+
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].struct_combo_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].struct_combo_widget);
+ g_signal_connect (obj, "changed",
+ G_CALLBACK (page_administration_combobox_changed_callback),
+ builder);
+ }
+}
+
+static void
+page_administration_set_entry_sensitive (GtkBuilder *builder,
+ const gchar *struct_name,
+ gboolean sensitive)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS(administration_entries); i++)
+ {
+ const gchar *tag;
+ GObject *obj;
+
+ tag = administration_entries[i].xmp_tag;
+ if (g_str_has_prefix (tag, struct_name))
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, administration_entries[i].ui_entry));
+ gtk_widget_set_sensitive (GTK_WIDGET (obj), sensitive);
+ }
+ }
+
+}
+
+void
+page_administration_read_from_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder)
+{
+ gint i;
+ gint sct;
+
+ g_return_if_fail (attributes != NULL);
+
+ page_administration_init_ui (builder);
+
+ for (i = 0; i < G_N_ELEMENTS(administration_entries); i++)
+ {
+ GimpAttribute *attribute = NULL;
+ const gchar *o_tag = NULL;
+ gchar *tag = NULL;
+ gint p;
+ gint counter;
+
+ o_tag = administration_entries[i].xmp_tag;
+
+ tag =g_strdup (o_tag);
+
+// if (g_str_has_prefix (tag, "Xmp.iptcExt.Add"))
+// g_print ("found\n");
+
+ p = string_index_of (tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+ gint j;
+ gchar *struct_string = NULL;
+
+ /* check last number */
+
+ struct_string = string_substring (tag, 0, p); /* get structure tag */
+
+ for (j = 0; j < G_N_ELEMENTS(struct_element); j++) /*get info about structure */
+ {
+ if (! g_strcmp0 (struct_string, struct_element[j].struct_tag)) /* is a structure */
+ {
+ GimpAttribute *struct_attribute = NULL;
+ gint num = highest_structure[struct_element[j].number_of_element];
+
+ counter = 0; /*start the loop */
+
+ while (struct_attribute ||
+ counter == 0 || /* at least once, because counter IS 0 */
+ counter < num) /* at least until the highest structure */
+ {
+ gchar *nr_string = NULL;
+ gchar *new_tag = NULL;
+ gchar *value;
+
+ counter ++;
+ nr_string = g_strdup_printf ("[%d]", counter);
+ new_tag = string_replace_str (tag, "[x]", nr_string);
+
+ struct_attribute = gimp_attributes_get_attribute (attributes, new_tag);
+
+ if (struct_attribute)
+ {
+ gint sct;
+ value = gimp_attribute_get_string (struct_attribute);
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (new_tag, struct_element[sct].struct_tag))
+ {
+ highest_structure[struct_element[sct].number_of_element] = counter;
+ break;
+ }
+ }
+ g_hash_table_insert (elements_table,
+ (gpointer) g_strdup (new_tag),
+ (gpointer) g_strdup (value));
+
+ gimp_attributes_remove_attribute (attributes, new_tag);
+ g_free (value);
+ }
+ g_free (nr_string);
+ g_free (new_tag);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ attribute = gimp_attributes_get_attribute (attributes, tag);
+ if (attribute)
+ {
+ gchar *value = gimp_attribute_get_string (attribute);
+ g_hash_table_insert (elements_table,
+ (gpointer) g_strdup (administration_entries[i].xmp_tag),
+ (gpointer) g_strdup (value));
+
+ gimp_attributes_remove_attribute (attributes, administration_entries[i].xmp_tag);
+ g_free (value);
+ }
+ }
+ g_free (tag);
+ }
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (highest_structure[sct] > 0)
+ curr_shown_structure[sct] = 1;
+ else
+ curr_shown_structure[sct] = 0;
+ }
+
+ page_administration_init_combobox (builder);
+
+ page_administration_set_to_ui (builder,
+ -1); /* all */
+}
+
+void
+page_administration_save_to_attributes (GimpAttributes *attributes)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+
+ g_hash_table_iter_init (&iter, elements_table);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ GimpAttribute *attribute = NULL;
+ gchar *tag = (gchar *) key;
+ gchar *value = NULL;
+ gint p;
+ gint sct;
+ gint i;
+
+ value = (gchar *) g_hash_table_lookup (elements_table, (gpointer) tag);
+ attribute = gimp_attribute_new_string (tag, value, TYPE_ASCII);
+
+ if (attribute)
+ {
+ p = string_index_of (tag, "[", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (tag, struct_element[sct].struct_tag))
+ {
+ gimp_attribute_set_structure_type (attribute, struct_element[sct].struct_type);
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(administration_entries); i++)
+ {
+ gchar *t_tag;
+ gint p1;
+ gint p2;
+
+ p1 = string_index_of (tag, "[", 0); /* is it a structure tag? */
+ if (p > -1) /* yes! */
+ {
+ gchar *t1;
+ gchar *t2;
+ gint l;
+
+ p2 = string_index_of (tag, "]", p1);
+
+ l = strlen (tag);
+
+ t1 = string_substring (tag, 0, p);
+ t2 = string_substring (tag, p2 + 1, l - (p2 + 1));
+
+ t_tag = g_strdup_printf ("%s[x]%s", t1, t2);
+ }
+ else
+ {
+ t_tag = g_strdup (tag);
+ }
+
+
+ if (! g_strcmp0 (t_tag, administration_entries[i].xmp_tag))
+ {
+ if (administration_entries[i].number_of_comboarray > -1)
+ {
+ gint combobox_data_counter;
+ gint array_length;
+ gint number_in_comboarray = administration_entries[i].number_of_comboarray;
+
+ array_length = G_N_ELEMENTS(combobox_data[number_in_comboarray]);
+
+ for (combobox_data_counter = 0; combobox_data_counter < array_length;
combobox_data_counter++)
+ {
+ const gchar *val_in_tag =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_tag;
+ const gchar *interpreted_val;
+
+ if (! val_in_tag)
+ break;
+
+ if (! g_strcmp0 (value, val_in_tag))
+ {
+ interpreted_val =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_combo;
+ gimp_attribute_set_interpreted_string (attribute, interpreted_val);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ gimp_attributes_add_attribute (attributes, attribute);
+ }
+ }
+}
+
+static void
+page_administration_entry_activate_cb (GtkWidget *widget, gpointer userdata)
+{
+ const gchar *entry_name = (const gchar *) userdata;
+ const gchar *value = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ page_administration_store_in_hash_table (entry_name, value, 0);
+}
+
+static gboolean
+page_administration_entry_focus_out_cb (GtkWidget *widget, GdkEvent *event, gpointer userdata)
+{
+ const gchar *entry_name = (const gchar *) userdata;
+ const gchar *value = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ page_administration_store_in_hash_table (entry_name, value, 0);
+
+ return FALSE;
+}
+
+static void
+page_administration_entry_combo_changed_callback (GtkWidget *combo, gpointer userdata)
+{
+ const gchar *widget_name;
+ const gchar *value;
+ gint array_nr = GPOINTER_TO_INT (userdata);
+ gint index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ value = combobox_data[array_nr][index].val_in_tag;
+ widget_name = gtk_widget_get_name (GTK_WIDGET (combo));
+
+ page_administration_store_in_hash_table (widget_name, value, 0);
+}
+
+static gboolean
+page_administration_store_in_hash_table (const gchar *entry_name, const gchar *value, gint nr)
+{
+ gint i;
+ const gchar *o_tag;
+ gint p;
+ gint number = 0;
+ gboolean success;
+
+ g_return_val_if_fail (entry_name != NULL, FALSE);
+
+ if (nr > 0)
+ number = nr;
+
+ success = FALSE;
+
+ for (i = 0; i < G_N_ELEMENTS(administration_entries); i++)
+ {
+ if (! g_strcmp0 (entry_name, administration_entries[i].ui_entry))
+ {
+ gchar *new_tag;
+ gchar *nr_string;
+
+ o_tag = administration_entries[i].xmp_tag;
+
+ new_tag =g_strdup (o_tag);
+
+ p = string_index_of (new_tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes */
+ {
+ gint sct;
+
+ if (number <= 0)
+ {
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_strcmp0 (o_tag, struct_element[sct].struct_tag))
+ {
+ number = curr_shown_structure[struct_element[sct].number_of_element];
+ break;
+ }
+ }
+ }
+
+ nr_string = g_strdup_printf ("[%d]", number);
+ new_tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ g_free (nr_string);
+ if (number <=0 )
+ {
+ g_free (new_tag);
+ return FALSE;
+ }
+ }
+
+ if (value && g_strcmp0 (value, ""))
+ {
+ if (g_hash_table_insert (elements_table, (gpointer) g_strdup (new_tag), (gpointer) g_strdup
(value)))
+ success = TRUE;
+ }
+ else
+ {
+ if (g_hash_table_remove (elements_table, (gpointer) new_tag))
+ success = TRUE;
+ }
+
+ set_save_attributes_button_sensitive (TRUE);
+
+ g_free (new_tag);
+ break;
+ }
+ }
+ return success;
+}
+
+static void
+page_administration_structure_add (GtkButton *button, gpointer userdata)
+{
+ gint number_to_add;
+ GtkComboBox *combo;
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ const gchar *widget_name;
+ gchar *line;
+ gint sct;
+ gint repaint;
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (button));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].add_widget))
+ {
+ page_administration_structure_save (builder, struct_element[sct].number_of_element);
+ number_to_add = ++highest_structure[struct_element[sct].number_of_element];
+
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+ line = g_strdup_printf ("%s [%d]", struct_element[sct].identifier, number_to_add);
+ curr_shown_structure [struct_element[sct].number_of_element] = number_to_add;
+
+ repaint = struct_element[sct].number_of_element;
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_administration_combobox_changed_callback), builder);
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, line,
+ -1);
+
+ gtk_combo_box_set_active (combo, number_to_add-1);
+
+ if (number_to_add == 1)
+ page_administration_set_entry_sensitive (builder, struct_element[sct].struct_tag, TRUE);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_administration_combobox_changed_callback), builder);
+
+ g_free (line);
+ break;
+ }
+ }
+
+ page_administration_set_to_ui (builder, repaint);
+}
+
+static void
+page_administration_structure_remove (GtkButton *button, gpointer userdata)
+{
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkListStore *liststore;
+ GHashTableIter iter_remove;
+ gboolean found;
+ gchar *nr_string;
+ gchar *nr_string_new;
+ gpointer key, value;
+ GSList *delete_key_list = NULL;
+ GSList *list;
+ gchar *tag_prefix;
+ gchar *new_key;
+ gint number_to_delete;
+ GtkComboBox *combo;
+ GtkTreeIter combo_iter;
+ const gchar *widget_name;
+ gint sct;
+ gint repaint;
+ gint combo_to_del;
+
+ GHashTable *reorder_table;
+
+ reorder_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (button));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].remove_widget))
+ {
+ number_to_delete = curr_shown_structure[sct];
+ tag_prefix = g_strdup (struct_element[sct].struct_tag);
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+ combo_to_del = highest_structure[sct] - 1;
+
+ if (number_to_delete == highest_structure[struct_element[sct].number_of_element])
+ curr_shown_structure[struct_element[sct].number_of_element] = number_to_delete - 1;
+ else
+ curr_shown_structure[struct_element[sct].number_of_element] = number_to_delete;
+
+ highest_structure[sct]--;
+ repaint = struct_element[sct].number_of_element;
+ break;
+ }
+ }
+
+ nr_string = g_strdup_printf ("%s[%d]", tag_prefix, number_to_delete);
+
+ /* remove entries */
+ {
+
+ g_hash_table_iter_init (&iter_remove, elements_table);
+ while (g_hash_table_iter_next (&iter_remove, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ if (g_str_has_prefix (tag, nr_string))
+ {
+ delete_key_list = g_slist_prepend (delete_key_list, g_strdup (tag));
+ }
+ }
+
+ for (list = delete_key_list; list; list = list->next)
+ {
+ gchar *key = (gchar *) list->data;
+ g_hash_table_remove (elements_table, key);
+ }
+
+ g_slist_free_full (delete_key_list, g_free);
+
+ }
+ /* reorder entries */
+
+ found = TRUE;
+
+ {
+ while (found) /* first: element table */
+ {
+ GHashTableIter iter_reorder;
+
+ delete_key_list = NULL;
+ found = FALSE;
+
+ nr_string_new = g_strdup_printf ("%s[%d]", tag_prefix, number_to_delete + 1);
+
+ g_hash_table_iter_init (&iter_reorder, elements_table);
+ while (g_hash_table_iter_next (&iter_reorder, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ gchar *tag_value = (gchar *) value;
+
+ if (g_str_has_prefix (tag, nr_string_new))
+ {
+ found = TRUE;
+ new_key = string_replace_str (tag, nr_string_new, nr_string);
+ g_hash_table_insert (reorder_table, g_strdup (new_key), g_strdup (tag_value));
+ delete_key_list = g_slist_prepend (delete_key_list, g_strdup (tag));
+ }
+ }
+
+ for (list = delete_key_list; list; list = list->next)
+ {
+ gchar *key = (gchar *) list->data;
+ g_hash_table_remove (elements_table, key);
+ }
+ g_slist_free_full (delete_key_list, g_free);
+
+ g_hash_table_iter_init (&iter_reorder, reorder_table);
+ while (g_hash_table_iter_next (&iter_reorder, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ gchar *tag_value = (gchar *) value;
+
+ g_hash_table_insert (elements_table, g_strdup (tag), g_strdup (tag_value));
+ }
+
+ g_hash_table_remove_all (reorder_table);
+
+ nr_string = g_strdup (nr_string_new);
+ number_to_delete ++;
+ }
+
+ }
+
+ g_free (tag_prefix);
+ g_free (nr_string);
+ g_free (nr_string_new);
+ g_free (new_key);
+
+ g_hash_table_unref (reorder_table);
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_administration_combobox_changed_callback), builder);
+
+ gtk_combo_box_set_active (combo, combo_to_del);
+ if (gtk_combo_box_get_active_iter (combo, &combo_iter))
+ gtk_list_store_remove (liststore, &combo_iter);
+
+ if (curr_shown_structure[repaint] == 0)
+ page_administration_set_entry_sensitive (builder, struct_element[sct].struct_tag, FALSE);
+ else
+ gtk_combo_box_set_active (combo, curr_shown_structure[repaint] - 1);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_administration_combobox_changed_callback), builder);
+
+ page_administration_set_to_ui (builder, repaint);
+}
+
+static void
+page_administration_combobox_changed_callback (GtkWidget *combo, gpointer userdata)
+{
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkComboBox *combobox;
+ gint nr;
+ const gchar *widget_name;
+ gint sct;
+ gint repaint;
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (combo));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].struct_combo_widget))
+ {
+ page_administration_structure_save (builder, struct_element[sct].number_of_element);
+ combobox = GTK_COMBO_BOX (get_widget_from_label (builder,
struct_element[sct].struct_combo_widget));
+ repaint = struct_element[sct].number_of_element;
+ break;
+ }
+ }
+
+ nr = gtk_combo_box_get_active (combobox);
+ nr++;
+ curr_shown_structure[repaint] = nr;
+
+ page_administration_set_to_ui (builder, repaint);
+}
+
+static void
+page_administration_structure_save (GtkBuilder *builder, gint struct_number)
+{
+ gint i;
+ const gchar *prefix = struct_element[struct_number].struct_tag;
+
+ for (i = 0; i < G_N_ELEMENTS(administration_entries); i++)
+ {
+ GObject *obj;
+ gchar *tag = NULL;
+ const gchar *o_tag;
+
+ if (curr_shown_structure[struct_number] > 0)
+ {
+ o_tag = administration_entries[i].xmp_tag;
+ if (g_str_has_prefix (o_tag, prefix))
+ {
+ gchar *nr_string;
+
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[struct_number]);
+ tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ obj = get_widget_from_label (builder, administration_entries[i].ui_entry);
+
+ switch (administration_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gchar *value;
+ value = g_strdup (gtk_entry_get_text (GTK_ENTRY (obj)));
+ if (value && g_strcmp0 (value, ""))
+ g_hash_table_insert (elements_table, (gpointer) g_strdup (tag), (gpointer) g_strdup
(value));
+ else
+ g_hash_table_remove (elements_table, (gpointer) tag);
+ }
+ break;
+ case WIDGET_TYPE_COMBOBOX:
+ break;
+ default:
+ break;
+ }
+
+ g_free (nr_string);
+ g_free (tag);
+ }
+ }
+ }
+}
+
+static void
+page_administration_set_to_ui (GtkBuilder *builder, gint repaint)
+{
+ gint i;
+ gint sct;
+ const gchar *prefix;
+
+ if (repaint != -1)
+ prefix = struct_element[repaint].struct_tag;
+ else
+ prefix = NULL;
+
+ for (i = 0; i < G_N_ELEMENTS(administration_entries); i++)
+ {
+ GObject *obj;
+ const gchar *o_tag;
+ gchar *value = NULL;
+ gchar *new_tag;
+ gint p;
+
+ o_tag = administration_entries[i].xmp_tag;
+
+ obj = get_widget_from_label (builder, administration_entries[i].ui_entry);
+
+ if (prefix)
+ {
+ if (! g_str_has_prefix (o_tag, prefix))
+ continue;
+ }
+
+ p = string_index_of (o_tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+ gchar *nr_string;
+
+ if (repaint != -1)
+ {
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[repaint]);
+ }
+ else
+ {
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (o_tag, struct_element[sct].struct_tag))
+ {
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[sct]);
+ break;
+ }
+ }
+ }
+
+ new_tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ g_free (nr_string);
+ }
+ else
+ {
+ new_tag = g_strdup (o_tag);
+ }
+
+ value = (gchar *) g_hash_table_lookup (elements_table, new_tag);
+
+ switch (administration_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gtk_entry_set_text (GTK_ENTRY (obj), "");
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (value)
+ {
+ switch (administration_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gtk_entry_set_text (GTK_ENTRY (obj), value);
+ }
+ break;
+ case WIDGET_TYPE_COMBOBOX:
+ {
+ gint combobox_data_counter;
+ gint array_length;
+ gint number_in_comboarray = administration_entries[i].number_of_comboarray;
+
+ array_length = G_N_ELEMENTS(combobox_data[number_in_comboarray]);
+
+ for (combobox_data_counter = 0; combobox_data_counter < array_length;
combobox_data_counter++)
+ {
+ const gchar *val_in_tag =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_tag;
+
+ if (! val_in_tag)
+ break;
+
+ if (! g_strcmp0 (value, val_in_tag))
+ {
+ g_signal_handlers_block_by_func (obj,
+ G_CALLBACK
(page_administration_entry_combo_changed_callback),
+ GINT_TO_POINTER (number_in_comboarray));
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (obj), combobox_data_counter);
+
+ g_signal_handlers_unblock_by_func (obj,
+ G_CALLBACK
(page_administration_entry_combo_changed_callback),
+ GINT_TO_POINTER (number_in_comboarray));
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ g_free (new_tag);
+ }
+}
+
+static void
+page_administration_init_combobox (GtkBuilder *builder)
+{
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ GtkComboBox *combo;
+ gint sct;
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ gchar *line;
+ gint i;
+ gint high;
+ gint active;
+
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+
+ high = highest_structure[struct_element[sct].number_of_element];
+ active = curr_shown_structure[struct_element[sct].number_of_element];
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_administration_combobox_changed_callback), builder);
+
+ for (i = 0; i < high; i++)
+ {
+ line = g_strdup_printf ("%s [%d]",struct_element[sct].identifier, i + 1);
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, line,
+ -1);
+ g_free (line);
+ }
+
+ gtk_combo_box_set_active (combo, active-1);
+
+ if (active >= 1)
+ page_administration_set_entry_sensitive (builder, struct_element[sct].struct_tag, TRUE);
+ else
+ page_administration_set_entry_sensitive (builder, struct_element[sct].struct_tag, FALSE);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_administration_combobox_changed_callback), builder);
+
+ }
+}
diff --git a/plug-ins/metainfo/page-administration.h b/plug-ins/metainfo/page-administration.h
new file mode 100644
index 0000000..801d0d5
--- /dev/null
+++ b/plug-ins/metainfo/page-administration.h
@@ -0,0 +1,33 @@
+/* page-administration.h
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PAGE_ADMINISTRATION_H__
+#define __PAGE_ADMINISTRATION_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void page_administration_read_from_attributes (GimpAttributes
*attributes,
+ GtkBuilder
*builder);
+void page_administration_save_to_attributes (GimpAttributes
*attributes);
+
+G_END_DECLS
+
+#endif /* __PAGE_ADMINISTRATION_H__ */
diff --git a/plug-ins/metainfo/page-artwork.c b/plug-ins/metainfo/page-artwork.c
new file mode 100644
index 0000000..1c3f9a6
--- /dev/null
+++ b/plug-ins/metainfo/page-artwork.c
@@ -0,0 +1,1067 @@
+/* page-artwork.c
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metainfo-helper.h"
+#include "page-artwork.h"
+
+
+static void page_artwork_init_ui (GtkBuilder *builder);
+static void page_artwork_set_entry_sensitive (GtkBuilder *builder,
+ const gchar *struct_name,
+ gboolean sensitive);
+static void page_artwork_init_combobox (GtkBuilder *builder);
+static void page_artwork_set_to_ui (GtkBuilder *builder,
+ gint repaint);
+static void page_artwork_entry_activate_cb (GtkWidget *widget,
+ gpointer userdata);
+static gboolean page_artwork_entry_focus_out_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata);
+static gboolean page_artwork_store_in_hash_table (const gchar *entry_name,
+ const gchar *value,
+ gint nr);
+static void page_artwork_structure_add (GtkButton *button,
+ gpointer userdata);
+static void page_artwork_structure_remove (GtkButton *button,
+ gpointer userdata);
+static void page_artwork_entry_combo_changed_callback (GtkWidget *combo,
+ gpointer userdata);
+static void page_artwork_combobox_changed_callback (GtkWidget *combo,
+ gpointer userdata);
+static void page_artwork_structure_save (GtkBuilder *builder,
+ gint struct_number);
+
+
+#define STRUCTURES_ON_PAGE 1
+#define COMBOBOX_WITH_DATA 2
+
+
+static GHashTable *elements_table;
+static gint curr_shown_structure[STRUCTURES_ON_PAGE];
+static gint highest_structure[STRUCTURES_ON_PAGE];
+
+static StructureElement struct_element [] =
+ {
+ {0,
+ N_("Artwork or Object"),
+ "Xmp.iptcExt.ArtworkOrObject",
+ STRUCTURE_TYPE_BAG,
+ "artworkorobject-combo",
+ "artworkorobject-liststore",
+ "artworkorobject-button-plus",
+ "artworkorobject-button-minus"}
+ };
+
+
+static ComboBoxData combobox_data[][256] =
+{
+ {
+ {N_(" "), ""},
+ {N_("unknown"), "http://ns.useplus.org/ldf/vocab/AG-UNK"},
+ {N_("Age 25 or over"), "http://ns.useplus.org/ldf/vocab/AG-A25"},
+ {N_("Age 24"), "http://ns.useplus.org/ldf/vocab/AG-A24"},
+ {N_("Age 23"), "http://ns.useplus.org/ldf/vocab/AG-A23"},
+ {N_("Age 22"), "http://ns.useplus.org/ldf/vocab/AG-A22"},
+ {N_("Age 21"), "http://ns.useplus.org/ldf/vocab/AG-A21"},
+ {N_("Age 20"), "http://ns.useplus.org/ldf/vocab/AG-A20"},
+ {N_("Age 19"), "http://ns.useplus.org/ldf/vocab/AG-A19"},
+ {N_("Age 18"), "http://ns.useplus.org/ldf/vocab/AG-A18"},
+ {N_("Age 17"), "http://ns.useplus.org/ldf/vocab/AG-A17"},
+ {N_("Age 16"), "http://ns.useplus.org/ldf/vocab/AG-A16"},
+ {N_("Age 15"), "http://ns.useplus.org/ldf/vocab/AG-A15"},
+ {N_("Age 14 or under"), "http://ns.useplus.org/ldf/vocab/AG-U14"}
+ },
+ {
+ {N_(" "), ""},
+ {N_("None"), "http://ns.useplus.org/ldf/vocab/MR-NON"},
+ {N_("Not Applicable"), "http://ns.useplus.org/ldf/vocab/MR-NAP"},
+ {N_("Unlimited Model Releases"), "http://ns.useplus.org/ldf/vocab/MR-UMR"},
+ {N_("Limited or Incomplete Model Releases"), "http://ns.useplus.org/ldf/vocab/MR-LMR"}
+ },
+};
+
+static MetadataEntry artwork_entries[] =
+ {
+ {N_("Title"),
+ "artworkorobject-title-label",
+ "artworkorobject-title-entry",
+ "Xmp.iptcExt.ArtworkOrObject[x]/iptcExt:AOTitle",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Date created"),
+ "artworkorobject-datecreated-label",
+ "artworkorobject-datecreated-entry",
+ "Xmp.iptcExt.ArtworkOrObject[x]/iptcExt:AODateCreated",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Creator"),
+ "artworkorobject-creator-label",
+ "artworkorobject-creator-entry",
+ "Xmp.iptcExt.ArtworkOrObject[x]/iptcExt:AOCreator",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Source"),
+ "artworkorobject-source-label",
+ "artworkorobject-source-entry",
+ "Xmp.iptcExt.ArtworkOrObject[x]/iptcExt:AOSource",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Source inventory number"),
+ "artworkorobject-sourceinvno-label",
+ "artworkorobject-sourceinvno-entry",
+ "Xmp.iptcExt.ArtworkOrObject[x]/iptcExt:AOSourceInvNo",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Copyright notice"),
+ "artworkorobject-copyrightnotice-label",
+ "artworkorobject-copyrightnotice-entry",
+ "Xmp.iptcExt.ArtworkOrObject[x]/iptcExt:AOCopyrightNotice",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+
+ {N_("Additional model information"),
+ "addlmodelinfo-label",
+ "addlmodelinfo-entry",
+ "Xmp.iptcExt.AddlModelInfo",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Model age"),
+ "modelage-label",
+ "modelage-entry",
+ "Xmp.iptcExt.ModelAge",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Model release ID"),
+ "modelreleaseid-label",
+ "modelreleaseid-entry",
+ "Xmp.plus.ModelReleaseID",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Minor model age"),
+ "minormodelagedisclosure-label",
+ "minormodelagedisclosure-combobox",
+ "Xmp.plus.MinorModelAgeDisclosure",
+ WIDGET_TYPE_COMBOBOX,
+ 0},
+
+ {N_("Model release status"),
+ "modelreleasestatus-label",
+ "modelreleasestatus-combobox",
+ "Xmp.plus.ModelReleaseStatus",
+ WIDGET_TYPE_COMBOBOX,
+ 1}
+ };
+
+
+static void
+page_artwork_init_ui (GtkBuilder *builder)
+{
+ GObject *obj;
+ gint i;
+
+ elements_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ for (i = 0; i < G_N_ELEMENTS(artwork_entries); i++)
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, artwork_entries[i].ui_label));
+
+ gtk_label_set_text (GTK_LABEL (obj), artwork_entries[i].label);
+
+ obj = G_OBJECT (get_widget_from_label (builder, artwork_entries[i].ui_entry));
+ gtk_widget_set_name (GTK_WIDGET (obj), artwork_entries[i].ui_entry);
+
+ if (artwork_entries[i].widget_type == WIDGET_TYPE_ENTRY)
+ {
+ g_signal_connect (GTK_WIDGET (obj), "focus-out-event",
+ G_CALLBACK (page_artwork_entry_focus_out_cb),
+ (gpointer) artwork_entries[i].ui_entry);
+ g_signal_connect (GTK_ENTRY (obj), "activate",
+ G_CALLBACK (page_artwork_entry_activate_cb),
+ (gpointer) artwork_entries[i].ui_entry);
+ }
+ else if (artwork_entries[i].widget_type == WIDGET_TYPE_COMBOBOX)
+ {
+ gint array_nr = artwork_entries[i].number_of_comboarray;
+
+ if (array_nr >= 0)
+ {
+ gint i;
+ GtkTreeIter iter;
+ GtkListStore *liststore;
+ gint array_length;
+
+ liststore = GTK_LIST_STORE(gtk_combo_box_get_model (GTK_COMBO_BOX (obj)));
+
+ array_length = G_N_ELEMENTS(combobox_data[array_nr]);
+
+ for (i = 0; i < array_length; i++) /*get info about structure */
+ {
+ const gchar *val_in_combo = combobox_data[array_nr][i].val_in_combo;
+
+ if (! val_in_combo)
+ break;
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, val_in_combo,
+ -1);
+ }
+
+ g_signal_connect (obj, "changed",
+ G_CALLBACK (page_artwork_entry_combo_changed_callback),
+ GINT_TO_POINTER (array_nr));
+ }
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(struct_element); i++) /*get info about structure */
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].add_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].add_widget);
+ g_signal_connect (obj, "clicked",
+ G_CALLBACK (page_artwork_structure_add),
+ builder);
+
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].remove_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].remove_widget);
+ g_signal_connect (obj, "clicked",
+ G_CALLBACK (page_artwork_structure_remove),
+ builder);
+
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].struct_combo_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].struct_combo_widget);
+ g_signal_connect (obj, "changed",
+ G_CALLBACK (page_artwork_combobox_changed_callback),
+ builder);
+ }
+}
+
+static void
+page_artwork_set_entry_sensitive (GtkBuilder *builder,
+ const gchar *struct_name,
+ gboolean sensitive)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS(artwork_entries); i++)
+ {
+ const gchar *tag;
+ GObject *obj;
+
+ tag = artwork_entries[i].xmp_tag;
+ if (g_str_has_prefix (tag, struct_name))
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, artwork_entries[i].ui_entry));
+ gtk_widget_set_sensitive (GTK_WIDGET (obj), sensitive);
+ }
+ }
+
+}
+
+void
+page_artwork_read_from_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder)
+{
+ gint i;
+ gint sct;
+
+ g_return_if_fail (attributes != NULL);
+
+ page_artwork_init_ui (builder);
+
+ for (i = 0; i < G_N_ELEMENTS(artwork_entries); i++)
+ {
+ GimpAttribute *attribute = NULL;
+ const gchar *o_tag = NULL;
+ gchar *tag = NULL;
+ gint p;
+ gint counter;
+
+ o_tag = artwork_entries[i].xmp_tag;
+
+ tag =g_strdup (o_tag);
+
+// if (g_str_has_prefix (tag, "Xmp.iptcExt.Add"))
+// g_print ("found\n");
+
+ p = string_index_of (tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+ gint j;
+ gchar *struct_string = NULL;
+
+ /* check last number */
+
+ struct_string = string_substring (tag, 0, p); /* get structure tag */
+
+ for (j = 0; j < G_N_ELEMENTS(struct_element); j++) /*get info about structure */
+ {
+ if (! g_strcmp0 (struct_string, struct_element[j].struct_tag)) /* is a structure */
+ {
+ GimpAttribute *struct_attribute = NULL;
+ gint num = highest_structure[struct_element[j].number_of_element];
+
+ counter = 0; /*start the loop */
+
+ while (struct_attribute ||
+ counter == 0 || /* at least once, because counter IS 0 */
+ counter < num) /* at least until the highest structure */
+ {
+ gchar *nr_string = NULL;
+ gchar *new_tag = NULL;
+ gchar *value;
+
+ counter ++;
+ nr_string = g_strdup_printf ("[%d]", counter);
+ new_tag = string_replace_str (tag, "[x]", nr_string);
+
+ struct_attribute = gimp_attributes_get_attribute (attributes, new_tag);
+
+ if (struct_attribute)
+ {
+ gint sct;
+ value = gimp_attribute_get_string (struct_attribute);
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (new_tag, struct_element[sct].struct_tag))
+ {
+ highest_structure[struct_element[sct].number_of_element] = counter;
+ break;
+ }
+ }
+ g_hash_table_insert (elements_table,
+ (gpointer) g_strdup (new_tag),
+ (gpointer) g_strdup (value));
+
+ gimp_attributes_remove_attribute (attributes, new_tag);
+ g_free (value);
+ }
+ g_free (nr_string);
+ g_free (new_tag);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ attribute = gimp_attributes_get_attribute (attributes, tag);
+ if (attribute)
+ {
+ gchar *value = gimp_attribute_get_string (attribute);
+ g_hash_table_insert (elements_table,
+ (gpointer) g_strdup (artwork_entries[i].xmp_tag),
+ (gpointer) g_strdup (value));
+
+ gimp_attributes_remove_attribute (attributes, artwork_entries[i].xmp_tag);
+ g_free (value);
+ }
+ }
+ g_free (tag);
+ }
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (highest_structure[sct] > 0)
+ curr_shown_structure[sct] = 1;
+ else
+ curr_shown_structure[sct] = 0;
+ }
+
+ page_artwork_init_combobox (builder);
+
+ page_artwork_set_to_ui (builder,
+ -1); /* all */
+}
+
+void
+page_artwork_save_to_attributes (GimpAttributes *attributes)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+
+ g_hash_table_iter_init (&iter, elements_table);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ GimpAttribute *attribute = NULL;
+ gchar *tag = (gchar *) key;
+ gchar *value = NULL;
+ gint p;
+ gint sct;
+ gint i;
+
+ value = (gchar *) g_hash_table_lookup (elements_table, (gpointer) tag);
+ attribute = gimp_attribute_new_string (tag, value, TYPE_ASCII);
+
+ if (attribute)
+ {
+ p = string_index_of (tag, "[", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (tag, struct_element[sct].struct_tag))
+ {
+ gimp_attribute_set_structure_type (attribute, struct_element[sct].struct_type);
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(artwork_entries); i++)
+ {
+ gchar *t_tag;
+ gint p1;
+ gint p2;
+
+ p1 = string_index_of (tag, "[", 0); /* is it a structure tag? */
+ if (p > -1) /* yes! */
+ {
+ gchar *t1;
+ gchar *t2;
+ gint l;
+
+ p2 = string_index_of (tag, "]", p1);
+
+ l = strlen (tag);
+
+ t1 = string_substring (tag, 0, p);
+ t2 = string_substring (tag, p2 + 1, l - (p2 + 1));
+
+ t_tag = g_strdup_printf ("%s[x]%s", t1, t2);
+ }
+ else
+ {
+ t_tag = g_strdup (tag);
+ }
+
+
+ if (! g_strcmp0 (t_tag, artwork_entries[i].xmp_tag))
+ {
+ if (artwork_entries[i].number_of_comboarray > -1)
+ {
+ gint combobox_data_counter;
+ gint array_length;
+ gint number_in_comboarray = artwork_entries[i].number_of_comboarray;
+
+ array_length = G_N_ELEMENTS(combobox_data[number_in_comboarray]);
+
+ for (combobox_data_counter = 0; combobox_data_counter < array_length;
combobox_data_counter++)
+ {
+ const gchar *val_in_tag =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_tag;
+ const gchar *interpreted_val;
+
+ if (! val_in_tag)
+ break;
+
+ if (! g_strcmp0 (value, val_in_tag))
+ {
+ interpreted_val =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_combo;
+ gimp_attribute_set_interpreted_string (attribute, interpreted_val);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ gimp_attributes_add_attribute (attributes, attribute);
+ }
+ }
+}
+
+static void
+page_artwork_entry_activate_cb (GtkWidget *widget, gpointer userdata)
+{
+ const gchar *entry_name = (const gchar *) userdata;
+ const gchar *value = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ page_artwork_store_in_hash_table (entry_name, value, 0);
+}
+
+static gboolean
+page_artwork_entry_focus_out_cb (GtkWidget *widget, GdkEvent *event, gpointer userdata)
+{
+ const gchar *entry_name = (const gchar *) userdata;
+ const gchar *value = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ page_artwork_store_in_hash_table (entry_name, value, 0);
+
+ return FALSE;
+}
+
+static void
+page_artwork_entry_combo_changed_callback (GtkWidget *combo, gpointer userdata)
+{
+ const gchar *widget_name;
+ const gchar *value;
+ gint array_nr = GPOINTER_TO_INT (userdata);
+ gint index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ value = combobox_data[array_nr][index].val_in_tag;
+ widget_name = gtk_widget_get_name (GTK_WIDGET (combo));
+
+ page_artwork_store_in_hash_table (widget_name, value, 0);
+}
+
+static gboolean
+page_artwork_store_in_hash_table (const gchar *entry_name, const gchar *value, gint nr)
+{
+ gint i;
+ const gchar *o_tag;
+ gint p;
+ gint number = 0;
+ gboolean success;
+
+ g_return_val_if_fail (entry_name != NULL, FALSE);
+
+ if (nr > 0)
+ number = nr;
+
+ success = FALSE;
+
+ for (i = 0; i < G_N_ELEMENTS(artwork_entries); i++)
+ {
+ if (! g_strcmp0 (entry_name, artwork_entries[i].ui_entry))
+ {
+ gchar *new_tag;
+ gchar *nr_string;
+
+ o_tag = artwork_entries[i].xmp_tag;
+
+ new_tag =g_strdup (o_tag);
+
+ p = string_index_of (new_tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes */
+ {
+ gint sct;
+
+ if (number <= 0)
+ {
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_strcmp0 (o_tag, struct_element[sct].struct_tag))
+ {
+ number = curr_shown_structure[struct_element[sct].number_of_element];
+ break;
+ }
+ }
+ }
+
+ nr_string = g_strdup_printf ("[%d]", number);
+ new_tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ g_free (nr_string);
+ if (number <=0 )
+ {
+ g_free (new_tag);
+ return FALSE;
+ }
+ }
+
+ if (value && g_strcmp0 (value, ""))
+ {
+ if (g_hash_table_insert (elements_table, (gpointer) g_strdup (new_tag), (gpointer) g_strdup
(value)))
+ success = TRUE;
+ }
+ else
+ {
+ if (g_hash_table_remove (elements_table, (gpointer) new_tag))
+ success = TRUE;
+ }
+
+ set_save_attributes_button_sensitive (TRUE);
+
+ g_free (new_tag);
+ break;
+ }
+ }
+ return success;
+}
+
+static void
+page_artwork_structure_add (GtkButton *button, gpointer userdata)
+{
+ gint number_to_add;
+ GtkComboBox *combo;
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ const gchar *widget_name;
+ gchar *line;
+ gint sct;
+ gint repaint;
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (button));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].add_widget))
+ {
+ page_artwork_structure_save (builder, struct_element[sct].number_of_element);
+ number_to_add = ++highest_structure[struct_element[sct].number_of_element];
+
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+ line = g_strdup_printf ("%s [%d]", struct_element[sct].identifier, number_to_add);
+ curr_shown_structure [struct_element[sct].number_of_element] = number_to_add;
+
+ repaint = struct_element[sct].number_of_element;
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_artwork_combobox_changed_callback), builder);
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, line,
+ -1);
+
+ gtk_combo_box_set_active (combo, number_to_add-1);
+
+ if (number_to_add == 1)
+ page_artwork_set_entry_sensitive (builder, struct_element[sct].struct_tag, TRUE);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_artwork_combobox_changed_callback), builder);
+
+ g_free (line);
+ break;
+ }
+ }
+
+ page_artwork_set_to_ui (builder, repaint);
+}
+
+static void
+page_artwork_structure_remove (GtkButton *button, gpointer userdata)
+{
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkListStore *liststore;
+ GHashTableIter iter_remove;
+ gboolean found;
+ gchar *nr_string;
+ gchar *nr_string_new;
+ gpointer key, value;
+ GSList *delete_key_list = NULL;
+ GSList *list;
+ gchar *tag_prefix;
+ gchar *new_key;
+ gint number_to_delete;
+ GtkComboBox *combo;
+ GtkTreeIter combo_iter;
+ const gchar *widget_name;
+ gint sct;
+ gint repaint;
+ gint combo_to_del;
+
+ GHashTable *reorder_table;
+
+ reorder_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (button));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].remove_widget))
+ {
+ number_to_delete = curr_shown_structure[sct];
+ tag_prefix = g_strdup (struct_element[sct].struct_tag);
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+ combo_to_del = highest_structure[sct] - 1;
+
+ if (number_to_delete == highest_structure[struct_element[sct].number_of_element])
+ curr_shown_structure[struct_element[sct].number_of_element] = number_to_delete - 1;
+ else
+ curr_shown_structure[struct_element[sct].number_of_element] = number_to_delete;
+
+ highest_structure[sct]--;
+ repaint = struct_element[sct].number_of_element;
+ break;
+ }
+ }
+
+ nr_string = g_strdup_printf ("%s[%d]", tag_prefix, number_to_delete);
+
+ /* remove entries */
+ {
+
+ g_hash_table_iter_init (&iter_remove, elements_table);
+ while (g_hash_table_iter_next (&iter_remove, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ if (g_str_has_prefix (tag, nr_string))
+ {
+ delete_key_list = g_slist_prepend (delete_key_list, g_strdup (tag));
+ }
+ }
+
+ for (list = delete_key_list; list; list = list->next)
+ {
+ gchar *key = (gchar *) list->data;
+ g_hash_table_remove (elements_table, key);
+ }
+
+ g_slist_free_full (delete_key_list, g_free);
+
+ }
+ /* reorder entries */
+
+ found = TRUE;
+
+ {
+ while (found) /* first: element table */
+ {
+ GHashTableIter iter_reorder;
+
+ delete_key_list = NULL;
+ found = FALSE;
+
+ nr_string_new = g_strdup_printf ("%s[%d]", tag_prefix, number_to_delete + 1);
+
+ g_hash_table_iter_init (&iter_reorder, elements_table);
+ while (g_hash_table_iter_next (&iter_reorder, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ gchar *tag_value = (gchar *) value;
+
+ if (g_str_has_prefix (tag, nr_string_new))
+ {
+ found = TRUE;
+ new_key = string_replace_str (tag, nr_string_new, nr_string);
+ g_hash_table_insert (reorder_table, g_strdup (new_key), g_strdup (tag_value));
+ delete_key_list = g_slist_prepend (delete_key_list, g_strdup (tag));
+ }
+ }
+
+ for (list = delete_key_list; list; list = list->next)
+ {
+ gchar *key = (gchar *) list->data;
+ g_hash_table_remove (elements_table, key);
+ }
+ g_slist_free_full (delete_key_list, g_free);
+
+ g_hash_table_iter_init (&iter_reorder, reorder_table);
+ while (g_hash_table_iter_next (&iter_reorder, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ gchar *tag_value = (gchar *) value;
+
+ g_hash_table_insert (elements_table, g_strdup (tag), g_strdup (tag_value));
+ }
+
+ g_hash_table_remove_all (reorder_table);
+
+ nr_string = g_strdup (nr_string_new);
+ number_to_delete ++;
+ }
+
+ }
+
+ g_free (tag_prefix);
+ g_free (nr_string);
+ g_free (nr_string_new);
+ g_free (new_key);
+
+ g_hash_table_unref (reorder_table);
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK (page_artwork_combobox_changed_callback),
builder);
+
+ gtk_combo_box_set_active (combo, combo_to_del);
+ if (gtk_combo_box_get_active_iter (combo, &combo_iter))
+ gtk_list_store_remove (liststore, &combo_iter);
+
+ if (curr_shown_structure[repaint] == 0)
+ page_artwork_set_entry_sensitive (builder, struct_element[sct].struct_tag, FALSE);
+ else
+ gtk_combo_box_set_active (combo, curr_shown_structure[repaint] - 1);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK (page_artwork_combobox_changed_callback),
builder);
+
+ page_artwork_set_to_ui (builder, repaint);
+}
+
+static void
+page_artwork_combobox_changed_callback (GtkWidget *combo, gpointer userdata)
+{
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkComboBox *combobox;
+ gint nr;
+ const gchar *widget_name;
+ gint sct;
+ gint repaint;
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (combo));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].struct_combo_widget))
+ {
+ page_artwork_structure_save (builder, struct_element[sct].number_of_element);
+ combobox = GTK_COMBO_BOX (get_widget_from_label (builder,
struct_element[sct].struct_combo_widget));
+ repaint = struct_element[sct].number_of_element;
+ break;
+ }
+ }
+
+ nr = gtk_combo_box_get_active (combobox);
+ nr++;
+ curr_shown_structure[repaint] = nr;
+
+ page_artwork_set_to_ui (builder, repaint);
+}
+
+static void
+page_artwork_structure_save (GtkBuilder *builder, gint struct_number)
+{
+ gint i;
+ const gchar *prefix = struct_element[struct_number].struct_tag;
+
+ for (i = 0; i < G_N_ELEMENTS(artwork_entries); i++)
+ {
+ GObject *obj;
+ gchar *tag = NULL;
+ const gchar *o_tag;
+
+ if (curr_shown_structure[struct_number] > 0)
+ {
+ o_tag = artwork_entries[i].xmp_tag;
+ if (g_str_has_prefix (o_tag, prefix))
+ {
+ gchar *nr_string;
+
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[struct_number]);
+ tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ obj = get_widget_from_label (builder, artwork_entries[i].ui_entry);
+
+ switch (artwork_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gchar *value;
+ value = g_strdup (gtk_entry_get_text (GTK_ENTRY (obj)));
+ if (value && g_strcmp0 (value, ""))
+ g_hash_table_insert (elements_table, (gpointer) g_strdup (tag), (gpointer) g_strdup
(value));
+ else
+ g_hash_table_remove (elements_table, (gpointer) tag);
+ }
+ break;
+ case WIDGET_TYPE_COMBOBOX:
+ break;
+ default:
+ break;
+ }
+
+ g_free (nr_string);
+ g_free (tag);
+ }
+ }
+ }
+}
+
+static void
+page_artwork_set_to_ui (GtkBuilder *builder, gint repaint)
+{
+ gint i;
+ gint sct;
+ const gchar *prefix;
+
+ if (repaint != -1)
+ prefix = struct_element[repaint].struct_tag;
+ else
+ prefix = NULL;
+
+ for (i = 0; i < G_N_ELEMENTS(artwork_entries); i++)
+ {
+ GObject *obj;
+ const gchar *o_tag;
+ gchar *value = NULL;
+ gchar *new_tag;
+ gint p;
+
+ o_tag = artwork_entries[i].xmp_tag;
+
+ obj = get_widget_from_label (builder, artwork_entries[i].ui_entry);
+
+ if (prefix)
+ {
+ if (! g_str_has_prefix (o_tag, prefix))
+ continue;
+ }
+
+ p = string_index_of (o_tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+ gchar *nr_string;
+
+ if (repaint != -1)
+ {
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[repaint]);
+ }
+ else
+ {
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (o_tag, struct_element[sct].struct_tag))
+ {
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[sct]);
+ break;
+ }
+ }
+ }
+
+ new_tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ g_free (nr_string);
+ }
+ else
+ {
+ new_tag = g_strdup (o_tag);
+ }
+
+ value = (gchar *) g_hash_table_lookup (elements_table, new_tag);
+
+ switch (artwork_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gtk_entry_set_text (GTK_ENTRY (obj), "");
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (value)
+ {
+ switch (artwork_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gtk_entry_set_text (GTK_ENTRY (obj), value);
+ }
+ break;
+ case WIDGET_TYPE_COMBOBOX:
+ {
+ gint combobox_data_counter;
+ gint array_length;
+ gint number_in_comboarray = artwork_entries[i].number_of_comboarray;
+
+ array_length = G_N_ELEMENTS(combobox_data[number_in_comboarray]);
+
+ for (combobox_data_counter = 0; combobox_data_counter < array_length;
combobox_data_counter++)
+ {
+ const gchar *val_in_tag =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_tag;
+
+ if (! val_in_tag)
+ break;
+
+ if (! g_strcmp0 (value, val_in_tag))
+ {
+ g_signal_handlers_block_by_func (obj,
+ G_CALLBACK
(page_artwork_entry_combo_changed_callback),
+ GINT_TO_POINTER (number_in_comboarray));
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (obj), combobox_data_counter);
+
+ g_signal_handlers_unblock_by_func (obj,
+ G_CALLBACK
(page_artwork_entry_combo_changed_callback),
+ GINT_TO_POINTER (number_in_comboarray));
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ g_free (new_tag);
+ }
+}
+
+static void
+page_artwork_init_combobox (GtkBuilder *builder)
+{
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ GtkComboBox *combo;
+ gint sct;
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ gchar *line;
+ gint i;
+ gint high;
+ gint active;
+
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+
+ high = highest_structure[struct_element[sct].number_of_element];
+ active = curr_shown_structure[struct_element[sct].number_of_element];
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_artwork_combobox_changed_callback), builder);
+
+ for (i = 0; i < high; i++)
+ {
+ line = g_strdup_printf ("%s [%d]",struct_element[sct].identifier, i + 1);
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, line,
+ -1);
+ g_free (line);
+ }
+
+ gtk_combo_box_set_active (combo, active-1);
+
+ if (active >= 1)
+ page_artwork_set_entry_sensitive (builder, struct_element[sct].struct_tag, TRUE);
+ else
+ page_artwork_set_entry_sensitive (builder, struct_element[sct].struct_tag, FALSE);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_artwork_combobox_changed_callback), builder);
+
+ }
+}
diff --git a/plug-ins/metainfo/page-artwork.h b/plug-ins/metainfo/page-artwork.h
new file mode 100644
index 0000000..7b3ce1a
--- /dev/null
+++ b/plug-ins/metainfo/page-artwork.h
@@ -0,0 +1,33 @@
+/* page-artwork.h
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PAGE_ARTWORK_H__
+#define __PAGE_ARTWORK_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void page_artwork_read_from_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder);
+void page_artwork_save_to_attributes (GimpAttributes *attributes);
+
+G_END_DECLS
+
+#endif /* __PAGE_ARTWORK_H__ */
diff --git a/plug-ins/metainfo/page-description.c b/plug-ins/metainfo/page-description.c
new file mode 100644
index 0000000..e24fc82
--- /dev/null
+++ b/plug-ins/metainfo/page-description.c
@@ -0,0 +1,927 @@
+/* page-description.c
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metainfo-helper.h"
+#include "page-description.h"
+
+
+static void page_description_init_ui (GtkBuilder *builder);
+static void page_description_set_entry_sensitive (GtkBuilder *builder,
+ const gchar *struct_name,
+ gboolean sensitive);
+static void page_description_init_combobox (GtkBuilder *builder);
+static void page_description_set_to_ui (GtkBuilder *builder,
+ gint repaint);
+static void page_description_entry_activate_cb (GtkWidget *widget,
+ gpointer userdata);
+static gboolean page_description_entry_focus_out_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata);
+static gboolean page_description_store_in_hash_table (gchar *entry_name,
+ gchar *value,
+ gint nr);
+static void page_description_structure_add (GtkButton *button,
+ gpointer userdata);
+static void page_description_structure_remove (GtkButton *button,
+ gpointer userdata);
+static void page_description_combobox_changed_callback (GtkWidget *button,
+ gpointer userdata);
+static void page_description_structure_save (GtkBuilder *builder,
+ gint struct_number);
+
+
+#define STRUCTURES_ON_PAGE 2
+
+
+static GHashTable *elements_table;
+static gint curr_shown_structure[STRUCTURES_ON_PAGE];
+static gint highest_structure[STRUCTURES_ON_PAGE];
+
+static StructureElement struct_element [] =
+ {
+ {0,
+ N_("Location created"),
+ "Xmp.iptcExt.LocationCreated",
+ STRUCTURE_TYPE_BAG,
+ "location-created-combo",
+ "location-created-liststore",
+ "location-created-button-plus",
+ "location-created-button-minus"},
+
+ {1,
+ N_("Location shown"),
+ "Xmp.iptcExt.LocationShown",
+ STRUCTURE_TYPE_SEQ,
+ "location-shown-combo",
+ "location-shown-liststore",
+ "location-shown-button-plus",
+ "location-shown-button-minus"}
+ };
+
+static MetadataEntry description_entries[] =
+ {
+ {N_("Person in image"),
+ "personinimage-label",
+ "personinimage-entry",
+ "Xmp.iptcExt.PersonInImage",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Event"),
+ "event-label",
+ "event-entry",
+ "Xmp.iptcExt.Event",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Sublocation"),
+ "location-created-sublocation-label",
+ "location-created-sublocation-entry",
+ "Xmp.iptcExt.LocationCreated[x]/iptcExt:Sublocation",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("City"),
+ "location-created-city-label",
+ "location-created-city-entry",
+ "Xmp.iptcExt.LocationCreated[x]/iptcExt:City",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Province/State"),
+ "location-created-provincestate-label",
+ "location-created-provincestate-entry",
+ "Xmp.iptcExt.LocationCreated[x]/iptcExt:ProvinceState",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Countryname"),
+ "location-created-countryname-label",
+ "location-created-countryname-entry",
+ "Xmp.iptcExt.LocationCreated[x]/iptcExt:CountryName",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Countrycode"),
+ "location-created-countrycode-label",
+ "location-created-countrycode-entry",
+ "Xmp.iptcExt.LocationCreated[x]/iptcExt:CountryCode",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Worldregion"),
+ "location-created-worldregion-label",
+ "location-created-worldregion-entry",
+ "Xmp.iptcExt.LocationCreated[x]/iptcExt:WorldRegion",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+
+ {N_("Sublocation"),
+ "location-shown-sublocation-label",
+ "location-shown-sublocation-entry",
+ "Xmp.iptcExt.LocationShown[x]/iptcExt:Sublocation",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("City"),
+ "location-shown-city-label",
+ "location-shown-city-entry",
+ "Xmp.iptcExt.LocationShown[x]/iptcExt:City",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Province/State"),
+ "location-shown-provincestate-label",
+ "location-shown-provincestate-entry",
+ "Xmp.iptcExt.LocationShown[x]/iptcExt:ProvinceState",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Countryname"),
+ "location-shown-countryname-label",
+ "location-shown-countryname-entry",
+ "Xmp.iptcExt.LocationShown[x]/iptcExt:CountryName",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Countrycode"),
+ "location-shown-countrycode-label",
+ "location-shown-countrycode-entry",
+ "Xmp.iptcExt.LocationShown[x]/iptcExt:CountryCode",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Worldregion"),
+ "location-shown-worldregion-label",
+ "location-shown-worldregion-entry",
+ "Xmp.iptcExt.LocationShown[x]/iptcExt:WorldRegion",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Name"),
+ "orginimage-name-label",
+ "orginimage-name-entry",
+ "Xmp.iptcExt.OrganisationInImageName",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Code"),
+ "orginimage-code-label",
+ "orginimage-code-entry",
+ "Xmp.iptcExt.OrganisationInImageCode",
+ WIDGET_TYPE_ENTRY,
+ -1}
+ };
+
+
+static void
+page_description_init_ui (GtkBuilder *builder)
+{
+ GObject *obj;
+ gint i;
+
+ elements_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ for (i = 0; i < G_N_ELEMENTS(description_entries); i++)
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, description_entries[i].ui_label));
+
+ gtk_label_set_text (GTK_LABEL (obj), description_entries[i].label);
+
+ obj = G_OBJECT (get_widget_from_label (builder, description_entries[i].ui_entry));
+ gtk_widget_set_name (GTK_WIDGET (obj), description_entries[i].ui_entry);
+ g_signal_connect (GTK_WIDGET (obj), "focus-out-event",
+ G_CALLBACK (page_description_entry_focus_out_cb),
+ (gpointer) description_entries[i].ui_entry);
+ g_signal_connect (GTK_ENTRY (obj), "activate",
+ G_CALLBACK (page_description_entry_activate_cb),
+ (gpointer) description_entries[i].ui_entry);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(struct_element); i++) /*get info about structure */
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].add_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].add_widget);
+ g_signal_connect (obj, "clicked",
+ G_CALLBACK (page_description_structure_add),
+ builder);
+
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].remove_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].remove_widget);
+ g_signal_connect (obj, "clicked",
+ G_CALLBACK (page_description_structure_remove),
+ builder);
+
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].struct_combo_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].struct_combo_widget);
+ g_signal_connect (obj, "changed", G_CALLBACK (page_description_combobox_changed_callback), builder);
+ }
+}
+
+static void
+page_description_set_entry_sensitive (GtkBuilder *builder,
+ const gchar *struct_name,
+ gboolean sensitive)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS(description_entries); i++)
+ {
+ const gchar *tag;
+ GObject *obj;
+
+ tag = description_entries[i].xmp_tag;
+ if (g_str_has_prefix (tag, struct_name))
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, description_entries[i].ui_entry));
+ gtk_widget_set_sensitive (GTK_WIDGET (obj), sensitive);
+ }
+ }
+
+}
+
+void
+page_description_read_from_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder)
+{
+ gint i;
+ gint sct;
+
+ g_return_if_fail (attributes != NULL);
+
+ page_description_init_ui (builder);
+
+ for (i = 0; i < G_N_ELEMENTS(description_entries); i++)
+ {
+ GimpAttribute *attribute = NULL;
+ const gchar *o_tag = NULL;
+ gchar *tag = NULL;
+ gint p;
+ gint counter;
+
+ o_tag = description_entries[i].xmp_tag;
+
+ tag =g_strdup (o_tag);
+
+ p = string_index_of (tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+ gint j;
+ gchar *struct_string = NULL;
+
+ /* check last number */
+
+ struct_string = string_substring (tag, 0, p); /* get structure tag */
+
+ for (j = 0; j < G_N_ELEMENTS(struct_element); j++) /*get info about structure */
+ {
+ if (! g_strcmp0 (struct_string, struct_element[j].struct_tag)) /* is a structure */
+ {
+ GimpAttribute *struct_attribute = NULL;
+ gint num = highest_structure[struct_element[j].number_of_element];
+
+ counter = 0; /*start the loop */
+
+ while (struct_attribute ||
+ counter == 0 || /* at least once, because counter IS 0 */
+ counter < num) /* at least until the highest structure */
+ {
+ gchar *nr_string = NULL;
+ gchar *new_tag = NULL;
+ gchar *value;
+
+ counter ++;
+ nr_string = g_strdup_printf ("[%d]", counter);
+ new_tag = string_replace_str (tag, "[x]", nr_string);
+
+ struct_attribute = gimp_attributes_get_attribute (attributes, new_tag);
+
+ if (struct_attribute)
+ {
+ gint sct;
+ value = gimp_attribute_get_string (struct_attribute);
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (new_tag, struct_element[sct].struct_tag))
+ {
+ highest_structure[struct_element[sct].number_of_element] = counter;
+ break;
+ }
+ }
+ g_hash_table_insert (elements_table,
+ (gpointer) g_strdup (new_tag),
+ (gpointer) g_strdup (value));
+
+ gimp_attributes_remove_attribute (attributes, new_tag);
+ g_free (value);
+ }
+ g_free (nr_string);
+ g_free (new_tag);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ attribute = gimp_attributes_get_attribute (attributes, tag);
+ if (attribute)
+ {
+ gchar *value = gimp_attribute_get_string (attribute);
+ g_hash_table_insert (elements_table,
+ (gpointer) g_strdup (description_entries[i].xmp_tag),
+ (gpointer) g_strdup (value));
+
+ gimp_attributes_remove_attribute (attributes, description_entries[i].xmp_tag);
+ g_free (value);
+ }
+ }
+ g_free (tag);
+ }
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (highest_structure[sct] > 0)
+ curr_shown_structure[sct] = 1;
+ else
+ curr_shown_structure[sct] = 0;
+ }
+
+ page_description_init_combobox (builder);
+
+ page_description_set_to_ui (builder,
+ -1); /* all */
+}
+
+void
+page_description_save_to_attributes (GimpAttributes *attributes)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+
+ g_hash_table_iter_init (&iter, elements_table);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ GimpAttribute *attribute = NULL;
+ gchar *tag = (gchar *) key;
+ gchar *value = NULL;
+ gint p;
+ gint sct;
+
+ value = (gchar *) g_hash_table_lookup (elements_table, (gpointer) tag);
+ attribute = gimp_attribute_new_string (tag, value, TYPE_ASCII);
+
+ if (attribute)
+ {
+ p = string_index_of (tag, "[", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (tag, struct_element[sct].struct_tag))
+ {
+ gimp_attribute_set_structure_type (attribute, struct_element[sct].struct_type);
+ break;
+ }
+ }
+ }
+
+ gimp_attributes_add_attribute (attributes, attribute);
+ }
+ }
+}
+
+static void
+page_description_entry_activate_cb (GtkWidget *widget, gpointer userdata)
+{
+ gchar *entry_name = (gchar *) userdata;
+ gchar *value = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
+
+ page_description_store_in_hash_table (entry_name, value, 0);
+}
+
+static gboolean
+page_description_entry_focus_out_cb (GtkWidget *widget, GdkEvent *event, gpointer userdata)
+{
+ gchar *entry_name = (gchar *) userdata;
+ gchar *value = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
+
+ page_description_store_in_hash_table (entry_name, value, 0);
+
+ return FALSE;
+}
+
+static gboolean
+page_description_store_in_hash_table (gchar *entry_name, gchar *value, gint nr)
+{
+ gint i;
+ const gchar *o_tag;
+ gint p;
+ gint number = 0;
+ gboolean success;
+
+ g_return_val_if_fail (entry_name != NULL, FALSE);
+
+ if (nr > 0)
+ number = nr;
+
+ success = FALSE;
+
+ for (i = 0; i < G_N_ELEMENTS(description_entries); i++)
+ {
+ if (! g_strcmp0 (entry_name, description_entries[i].ui_entry))
+ {
+ gchar *new_tag;
+ gchar *nr_string;
+
+ o_tag = description_entries[i].xmp_tag;
+
+ new_tag =g_strdup (o_tag);
+
+ p = string_index_of (new_tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes */
+ {
+ gint sct;
+
+ if (number <= 0)
+ {
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_strcmp0 (o_tag, struct_element[sct].struct_tag))
+ {
+ number = curr_shown_structure[struct_element[sct].number_of_element];
+ break;
+ }
+ }
+ }
+
+ nr_string = g_strdup_printf ("[%d]", number);
+ new_tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ g_free (nr_string);
+
+ if (number <= 0)
+ {
+ g_free (new_tag);
+ return FALSE;
+ }
+ }
+
+ if (value && g_strcmp0 (value, ""))
+ {
+ if (g_hash_table_insert (elements_table, (gpointer) g_strdup (new_tag), (gpointer) g_strdup
(value)))
+ success = TRUE;
+ }
+ else
+ {
+ if (g_hash_table_remove (elements_table, (gpointer) new_tag))
+ success = TRUE;
+ }
+
+ set_save_attributes_button_sensitive (TRUE);
+
+ g_free (new_tag);
+ break;
+ }
+ }
+ return success;
+}
+
+static void
+page_description_structure_add (GtkButton *button, gpointer userdata)
+{
+ gint number_to_add;
+ GtkComboBox *combo;
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ const gchar *widget_name;
+ gchar *line;
+ gint sct;
+ gint repaint;
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (button));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].add_widget))
+ {
+ page_description_structure_save (builder, struct_element[sct].number_of_element);
+ number_to_add = ++highest_structure[struct_element[sct].number_of_element];
+
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+ line = g_strdup_printf ("%s [%d]", struct_element[sct].identifier, number_to_add);
+ curr_shown_structure [struct_element[sct].number_of_element] = number_to_add;
+
+ repaint = struct_element[sct].number_of_element;
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_description_combobox_changed_callback), builder);
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, line,
+ -1);
+
+ gtk_combo_box_set_active (combo, number_to_add-1);
+
+ if (number_to_add == 1)
+ page_description_set_entry_sensitive (builder, struct_element[sct].struct_tag, TRUE);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_description_combobox_changed_callback), builder);
+
+ g_free (line);
+ break;
+ }
+ }
+
+ page_description_set_to_ui (builder, repaint);
+}
+
+static void
+page_description_structure_remove (GtkButton *button, gpointer userdata)
+{
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkListStore *liststore;
+ GHashTableIter iter_remove;
+ gboolean found;
+ gchar *nr_string;
+ gchar *nr_string_new;
+ gpointer key, value;
+ GSList *delete_key_list = NULL;
+ GSList *list;
+ gchar *tag_prefix;
+ gchar *new_key;
+ gint number_to_delete;
+ GtkComboBox *combo;
+ GtkTreeIter combo_iter;
+ const gchar *widget_name;
+ gint sct;
+ gint repaint;
+ gint combo_to_del;
+
+ GHashTable *reorder_table;
+
+ reorder_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (button));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].remove_widget))
+ {
+ number_to_delete = curr_shown_structure[sct];
+ tag_prefix = g_strdup (struct_element[sct].struct_tag);
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+ combo_to_del = highest_structure[sct] - 1;
+
+ if (number_to_delete == highest_structure[struct_element[sct].number_of_element])
+ curr_shown_structure[struct_element[sct].number_of_element] = number_to_delete - 1;
+ else
+ curr_shown_structure[struct_element[sct].number_of_element] = number_to_delete;
+
+ highest_structure[sct]--;
+ repaint = struct_element[sct].number_of_element;
+ break;
+ }
+ }
+
+ nr_string = g_strdup_printf ("%s[%d]", tag_prefix, number_to_delete);
+
+ /* remove entries */
+ {
+
+ g_hash_table_iter_init (&iter_remove, elements_table);
+ while (g_hash_table_iter_next (&iter_remove, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ if (g_str_has_prefix (tag, nr_string))
+ {
+ delete_key_list = g_slist_prepend (delete_key_list, g_strdup (tag));
+ }
+ }
+
+ for (list = delete_key_list; list; list = list->next)
+ {
+ gchar *key = (gchar *) list->data;
+ g_hash_table_remove (elements_table, key);
+ }
+
+ g_slist_free_full (delete_key_list, g_free);
+
+ }
+ /* reorder entries */
+
+ found = TRUE;
+
+ {
+ while (found) /* first: element table */
+ {
+ GHashTableIter iter_reorder;
+
+ delete_key_list = NULL;
+ found = FALSE;
+
+ nr_string_new = g_strdup_printf ("%s[%d]", tag_prefix, number_to_delete + 1);
+
+ g_hash_table_iter_init (&iter_reorder, elements_table);
+ while (g_hash_table_iter_next (&iter_reorder, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ gchar *tag_value = (gchar *) value;
+
+ if (g_str_has_prefix (tag, nr_string_new))
+ {
+ found = TRUE;
+ new_key = string_replace_str (tag, nr_string_new, nr_string);
+ g_hash_table_insert (reorder_table, g_strdup (new_key), g_strdup (tag_value));
+ delete_key_list = g_slist_prepend (delete_key_list, g_strdup (tag));
+ }
+ }
+
+ for (list = delete_key_list; list; list = list->next)
+ {
+ gchar *key = (gchar *) list->data;
+ g_hash_table_remove (elements_table, key);
+ }
+ g_slist_free_full (delete_key_list, g_free);
+
+ g_hash_table_iter_init (&iter_reorder, reorder_table);
+ while (g_hash_table_iter_next (&iter_reorder, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ gchar *tag_value = (gchar *) value;
+
+ g_hash_table_insert (elements_table, g_strdup (tag), g_strdup (tag_value));
+ }
+
+ g_hash_table_remove_all (reorder_table);
+
+ nr_string = g_strdup (nr_string_new);
+ number_to_delete ++;
+ }
+
+ }
+
+ g_free (tag_prefix);
+ g_free (nr_string);
+ g_free (nr_string_new);
+ g_free (new_key);
+
+ g_hash_table_unref (reorder_table);
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_description_combobox_changed_callback), builder);
+
+ gtk_combo_box_set_active (combo, combo_to_del);
+ if (gtk_combo_box_get_active_iter (combo, &combo_iter))
+ gtk_list_store_remove (liststore, &combo_iter);
+
+ if (curr_shown_structure[repaint] == 0)
+ page_description_set_entry_sensitive (builder, struct_element[sct].struct_tag, FALSE);
+ else
+ gtk_combo_box_set_active (combo, curr_shown_structure[repaint] - 1);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_description_combobox_changed_callback), builder);
+
+ page_description_set_to_ui (builder, repaint);
+}
+
+static void
+page_description_combobox_changed_callback (GtkWidget *combo, gpointer userdata)
+{
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkComboBox *combobox;
+ gint nr;
+ const gchar *widget_name;
+ gint sct;
+ gint repaint;
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (combo));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].struct_combo_widget))
+ {
+ page_description_structure_save (builder, struct_element[sct].number_of_element);
+ combobox = GTK_COMBO_BOX (get_widget_from_label (builder,
struct_element[sct].struct_combo_widget));
+ repaint = struct_element[sct].number_of_element;
+ break;
+ }
+ }
+
+ nr = gtk_combo_box_get_active (combobox);
+ nr++;
+ curr_shown_structure[repaint] = nr;
+
+ page_description_set_to_ui (builder, repaint);
+}
+
+static void
+page_description_structure_save (GtkBuilder *builder, gint struct_number)
+{
+ gint i;
+ const gchar *prefix = struct_element[struct_number].struct_tag;
+
+ for (i = 0; i < G_N_ELEMENTS(description_entries); i++)
+ {
+ GObject *obj;
+ gchar *tag = NULL;
+ const gchar *o_tag;
+
+ if (curr_shown_structure[struct_number] > 0)
+ {
+ o_tag = description_entries[i].xmp_tag;
+ if (g_str_has_prefix (o_tag, prefix))
+ {
+ gchar *nr_string;
+
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[struct_number]);
+ tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ obj = get_widget_from_label (builder, description_entries[i].ui_entry);
+
+ switch (description_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gchar *value;
+ value = g_strdup (gtk_entry_get_text (GTK_ENTRY (obj)));
+ if (value && g_strcmp0 (value, ""))
+ g_hash_table_insert (elements_table, (gpointer) g_strdup (tag), (gpointer) g_strdup
(value));
+ else
+ g_hash_table_remove (elements_table, (gpointer) tag);
+ }
+ break;
+ case WIDGET_TYPE_COMBOBOX: /*no combobox in "description" */
+ break;
+ default:
+ break;
+ }
+
+ g_free (nr_string);
+ g_free (tag);
+ }
+ }
+ }
+}
+
+static void
+page_description_set_to_ui (GtkBuilder *builder, gint repaint)
+{
+ gint i;
+ gint sct;
+ const gchar *prefix;
+
+ if (repaint != -1)
+ prefix = struct_element[repaint].struct_tag;
+ else
+ prefix = NULL;
+
+ for (i = 0; i < G_N_ELEMENTS(description_entries); i++)
+ {
+ GObject *obj;
+ const gchar *o_tag;
+ gchar *value = NULL;
+ gchar *new_tag;
+ gint p;
+
+ o_tag = description_entries[i].xmp_tag;
+
+ obj = get_widget_from_label (builder, description_entries[i].ui_entry);
+
+ if (prefix)
+ {
+ if (! g_str_has_prefix (o_tag, prefix))
+ continue;
+ }
+
+ p = string_index_of (o_tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+ gchar *nr_string;
+
+ if (repaint != -1)
+ {
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[repaint]);
+ }
+ else
+ {
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (o_tag, struct_element[sct].struct_tag))
+ {
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[sct]);
+ break;
+ }
+ }
+ }
+
+ new_tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ g_free (nr_string);
+ }
+ else
+ {
+ new_tag = g_strdup (o_tag);
+ }
+
+ value = (gchar *) g_hash_table_lookup (elements_table, new_tag);
+ gtk_entry_set_text (GTK_ENTRY (obj), "");
+
+ if (value)
+ {
+ switch (description_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gtk_entry_set_text (GTK_ENTRY (obj), value);
+ }
+ break;
+ case WIDGET_TYPE_COMBOBOX: /*no combobox in "description" */
+ break;
+ default:
+ break;
+ }
+ }
+ g_free (new_tag);
+ }
+}
+
+static void
+page_description_init_combobox (GtkBuilder *builder)
+{
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ GtkComboBox *combo;
+ gint sct;
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ gchar *line;
+ gint i;
+ gint high;
+ gint active;
+
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+
+ high = highest_structure[struct_element[sct].number_of_element];
+ active = curr_shown_structure[struct_element[sct].number_of_element];
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_description_combobox_changed_callback), builder);
+
+ for (i = 0; i < high; i++)
+ {
+ line = g_strdup_printf ("%s [%d]",struct_element[sct].identifier, i + 1);
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, line,
+ -1);
+ g_free (line);
+ }
+
+ gtk_combo_box_set_active (combo, active-1);
+
+ if (active >= 1)
+ page_description_set_entry_sensitive (builder, struct_element[sct].struct_tag, TRUE);
+ else
+ page_description_set_entry_sensitive (builder, struct_element[sct].struct_tag, FALSE);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_description_combobox_changed_callback), builder);
+
+ }
+}
diff --git a/plug-ins/metainfo/page-description.h b/plug-ins/metainfo/page-description.h
new file mode 100644
index 0000000..fecb0ca
--- /dev/null
+++ b/plug-ins/metainfo/page-description.h
@@ -0,0 +1,33 @@
+/* page-description.h
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PAGE_DESCRIPTION_H__
+#define __PAGE_DESCRIPTION_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void page_description_read_from_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder);
+void page_description_save_to_attributes (GimpAttributes
*attributes);
+
+G_END_DECLS
+
+#endif /* __PAGE_DESCRIPTION_H__ */
diff --git a/plug-ins/metainfo/page-rights.c b/plug-ins/metainfo/page-rights.c
new file mode 100644
index 0000000..2c292f9
--- /dev/null
+++ b/plug-ins/metainfo/page-rights.c
@@ -0,0 +1,1117 @@
+/* page-administration.c
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metainfo-helper.h"
+#include "page-rights.h"
+
+
+static void page_rights_init_ui (GtkBuilder *builder);
+static void page_rights_set_entry_sensitive (GtkBuilder *builder,
+ const gchar *struct_name,
+ gboolean sensitive);
+static void page_rights_init_combobox (GtkBuilder *builder);
+static void page_rights_set_to_ui (GtkBuilder *builder,
+ gint repaint);
+static void page_rights_entry_activate_cb (GtkWidget *widget,
+ gpointer userdata);
+static gboolean page_rights_entry_focus_out_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata);
+static gboolean page_rights_store_in_hash_table (const gchar *entry_name,
+ const gchar *value,
+ gint nr);
+static void page_rights_structure_add (GtkButton *button,
+ gpointer userdata);
+static void page_rights_structure_remove (GtkButton *button,
+ gpointer userdata);
+static void page_rights_entry_combo_changed_callback (GtkWidget *combo,
+ gpointer userdata);
+static void page_rights_combobox_changed_callback (GtkWidget *combo,
+ gpointer userdata);
+static void page_rights_structure_save (GtkBuilder *builder,
+ gint struct_number);
+
+
+#define STRUCTURES_ON_PAGE 3
+#define COMBOBOX_WITH_DATA 1
+
+
+static GHashTable *elements_table;
+static gint curr_shown_structure[STRUCTURES_ON_PAGE];
+static gint highest_structure[STRUCTURES_ON_PAGE];
+
+static StructureElement struct_element [] =
+ {
+ {0,
+ N_("Image creator"),
+ "Xmp.plus.ImageCreator",
+ STRUCTURE_TYPE_SEQ,
+ "imagecreator-combo",
+ "imagecreator-liststore",
+ "imagecreator-button-plus",
+ "imagecreator-button-minus"},
+
+ {1,
+ N_("Copyright Owner"),
+ "Xmp.plus.CopyrightOwner",
+ STRUCTURE_TYPE_SEQ,
+ "copyrightowner-combo",
+ "copyrightowner-liststore",
+ "copyrightowner-button-plus",
+ "copyrightowner-button-minus"},
+
+ {2,
+ N_("Licensor"),
+ "Xmp.plus.Licensor",
+ STRUCTURE_TYPE_BAG,
+ "licensor-combo",
+ "licensor-liststore",
+ "licensor-button-plus",
+ "licensor-button-minus"}
+ };
+
+
+static ComboBoxData combobox_data[][256] =
+{
+ {
+ {N_(" "), ""},
+ {N_("None"), "http://ns.useplus.org/ldf/vocab/PR-NON"},
+ {N_("Not applicable"), "http://ns.useplus.org/ldf/vocab/PR-NAP"},
+ {N_("Unlimited property release"), "http://ns.useplus.org/ldf/vocab/PR-UPR"},
+ {N_("Limited or Incomplete Property Releases"), "http://ns.useplus.org/ldf/vocab/PR-LPR"}
+ },
+};
+
+static MetadataEntry rights_entries[] =
+ {
+ {N_("Image creator name"),
+ "imagecreator-name-label",
+ "imagecreator-name-entry",
+ "Xmp.plus.ImageCreator[x]/plus:ImageCreatorName",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Image creator ID"),
+ "imagecreator-id-label",
+ "imagecreator-id-entry",
+ "Xmp.plus.ImageCreator[x]/plus:ImageCreatorID",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Copyright owner name"),
+ "copyrightowner-name-label",
+ "copyrightowner-name-entry",
+ "Xmp.plus.CopyrightOwner[x]/plus:CopyrightOwnerName",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Copyright owner ID"),
+ "copyrightowner-id-label",
+ "copyrightowner-id-entry",
+ "Xmp.plus.CopyrightOwner[x]/plus:CopyrightOwnerID",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Name"),
+ "licensor-name-label",
+ "licensor-name-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorName",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("ID"),
+ "licensor-id-label",
+ "licensor-id-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorID",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Address"),
+ "licensor-streetaddress-label",
+ "licensor-streetaddress-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorStreetAddress",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Extended Address"),
+ "licensor-extendedaddress-label",
+ "licensor-extendedaddress-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorExtendedAddress",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("City"),
+ "licensor-city-label",
+ "licensor-city-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorCity",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Region"),
+ "licensor-region-label",
+ "licensor-region-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorRegion",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Postal code"),
+ "licensor-postalcode-label",
+ "licensor-postalcode-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorPostalCode",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Country"),
+ "licensor-country-label",
+ "licensor-country-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorCountry",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Telephone 1"),
+ "licensor-telephone1-label",
+ "licensor-telephone1-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorTelephone1",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Telephone 2"),
+ "licensor-telephone2-label",
+ "licensor-telephone2-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorTelephone2",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("E-Mail"),
+ "licensor-email-label",
+ "licensor-email-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorEmail",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("URL"),
+ "licensor-url-label",
+ "licensor-url-entry",
+ "Xmp.plus.Licensor[x]/plus:LicensorURL",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Release ID"),
+ "release-id-label",
+ "release-id-entry",
+ "Xmp.plus.PropertyReleaseID",
+ WIDGET_TYPE_ENTRY,
+ -1},
+
+ {N_("Release status"),
+ "release-status-label",
+ "release-status-combobox",
+ "Xmp.plus.PropertyReleaseStatus",
+ WIDGET_TYPE_COMBOBOX,
+ 0}
+ };
+
+
+static void
+page_rights_init_ui (GtkBuilder *builder)
+{
+ GObject *obj;
+ gint i;
+
+ elements_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ for (i = 0; i < G_N_ELEMENTS(rights_entries); i++)
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, rights_entries[i].ui_label));
+
+ gtk_label_set_text (GTK_LABEL (obj), rights_entries[i].label);
+
+ obj = G_OBJECT (get_widget_from_label (builder, rights_entries[i].ui_entry));
+ gtk_widget_set_name (GTK_WIDGET (obj), rights_entries[i].ui_entry);
+
+ if (rights_entries[i].widget_type == WIDGET_TYPE_ENTRY)
+ {
+ g_signal_connect (GTK_WIDGET (obj), "focus-out-event",
+ G_CALLBACK (page_rights_entry_focus_out_cb),
+ (gpointer) rights_entries[i].ui_entry);
+ g_signal_connect (GTK_ENTRY (obj), "activate",
+ G_CALLBACK (page_rights_entry_activate_cb),
+ (gpointer) rights_entries[i].ui_entry);
+ }
+ else if (rights_entries[i].widget_type == WIDGET_TYPE_COMBOBOX)
+ {
+ gint array_nr = rights_entries[i].number_of_comboarray;
+
+ if (array_nr >= 0)
+ {
+ gint i;
+ GtkTreeIter iter;
+ GtkListStore *liststore;
+ gint array_length;
+
+ liststore = GTK_LIST_STORE(gtk_combo_box_get_model (GTK_COMBO_BOX (obj)));
+
+ array_length = G_N_ELEMENTS(combobox_data[array_nr]);
+
+ for (i = 0; i < array_length; i++) /*get info about structure */
+ {
+ const gchar *val_in_combo = combobox_data[array_nr][i].val_in_combo;
+
+ if (! val_in_combo)
+ break;
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, val_in_combo,
+ -1);
+ }
+
+ g_signal_connect (obj, "changed",
+ G_CALLBACK (page_rights_entry_combo_changed_callback),
+ GINT_TO_POINTER (array_nr));
+ }
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(struct_element); i++) /*get info about structure */
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].add_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].add_widget);
+ g_signal_connect (obj, "clicked",
+ G_CALLBACK (page_rights_structure_add),
+ builder);
+
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].remove_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].remove_widget);
+ g_signal_connect (obj, "clicked",
+ G_CALLBACK (page_rights_structure_remove),
+ builder);
+
+ obj = G_OBJECT (get_widget_from_label (builder, struct_element[i].struct_combo_widget));
+ gtk_widget_set_name (GTK_WIDGET (obj), struct_element[i].struct_combo_widget);
+ g_signal_connect (obj, "changed",
+ G_CALLBACK (page_rights_combobox_changed_callback),
+ builder);
+ }
+}
+
+static void
+page_rights_set_entry_sensitive (GtkBuilder *builder,
+ const gchar *struct_name,
+ gboolean sensitive)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS(rights_entries); i++)
+ {
+ const gchar *tag;
+ GObject *obj;
+
+ tag = rights_entries[i].xmp_tag;
+ if (g_str_has_prefix (tag, struct_name))
+ {
+ obj = G_OBJECT (get_widget_from_label (builder, rights_entries[i].ui_entry));
+ gtk_widget_set_sensitive (GTK_WIDGET (obj), sensitive);
+ }
+ }
+
+}
+
+void
+page_rights_read_from_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder)
+{
+ gint i;
+ gint sct;
+
+ g_return_if_fail (attributes != NULL);
+
+ page_rights_init_ui (builder);
+
+ for (i = 0; i < G_N_ELEMENTS(rights_entries); i++)
+ {
+ GimpAttribute *attribute = NULL;
+ const gchar *o_tag = NULL;
+ gchar *tag = NULL;
+ gint p;
+ gint counter;
+
+ o_tag = rights_entries[i].xmp_tag;
+
+ tag =g_strdup (o_tag);
+
+// if (g_str_has_prefix (tag, "Xmp.plus.ImageCreator"))
+// g_print ("found\n");
+//
+ p = string_index_of (tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+ gint j;
+ gchar *struct_string = NULL;
+
+ /* check last number */
+
+ struct_string = string_substring (tag, 0, p); /* get structure tag */
+
+ for (j = 0; j < G_N_ELEMENTS(struct_element); j++) /*get info about structure */
+ {
+ if (! g_strcmp0 (struct_string, struct_element[j].struct_tag)) /* is a structure */
+ {
+ GimpAttribute *struct_attribute = NULL;
+ gint num = highest_structure[struct_element[j].number_of_element];
+
+ counter = 0; /*start the loop */
+
+ while (struct_attribute ||
+ counter == 0 || /* at least once, because counter IS 0 */
+ counter < num) /* at least until the highest structure */
+ {
+ gchar *nr_string = NULL;
+ gchar *new_tag = NULL;
+ gchar *value;
+
+ counter ++;
+ nr_string = g_strdup_printf ("[%d]", counter);
+ new_tag = string_replace_str (tag, "[x]", nr_string);
+
+ struct_attribute = gimp_attributes_get_attribute (attributes, new_tag);
+
+ if (struct_attribute)
+ {
+ gint sct;
+ value = gimp_attribute_get_string (struct_attribute);
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (new_tag, struct_element[sct].struct_tag))
+ {
+ highest_structure[struct_element[sct].number_of_element] = counter;
+ break;
+ }
+ }
+ g_hash_table_insert (elements_table,
+ (gpointer) g_strdup (new_tag),
+ (gpointer) g_strdup (value));
+
+ gimp_attributes_remove_attribute (attributes, new_tag);
+ g_free (value);
+ }
+ g_free (nr_string);
+ g_free (new_tag);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ attribute = gimp_attributes_get_attribute (attributes, tag);
+ if (attribute)
+ {
+ gchar *value = gimp_attribute_get_string (attribute);
+ g_hash_table_insert (elements_table,
+ (gpointer) g_strdup (rights_entries[i].xmp_tag),
+ (gpointer) g_strdup (value));
+
+ gimp_attributes_remove_attribute (attributes, rights_entries[i].xmp_tag);
+ g_free (value);
+ }
+ }
+ g_free (tag);
+ }
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (highest_structure[sct] > 0)
+ curr_shown_structure[sct] = 1;
+ else
+ curr_shown_structure[sct] = 0;
+ }
+
+ page_rights_init_combobox (builder);
+
+ page_rights_set_to_ui (builder,
+ -1); /* all */
+}
+
+void
+page_rights_save_to_attributes (GimpAttributes *attributes)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+
+ g_hash_table_iter_init (&iter, elements_table);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ GimpAttribute *attribute = NULL;
+ gchar *tag = (gchar *) key;
+ gchar *value = NULL;
+ gint p;
+ gint sct;
+ gint i;
+
+ value = (gchar *) g_hash_table_lookup (elements_table, (gpointer) tag);
+ attribute = gimp_attribute_new_string (tag, value, TYPE_ASCII);
+
+ if (attribute)
+ {
+ p = string_index_of (tag, "[", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (tag, struct_element[sct].struct_tag))
+ {
+ gimp_attribute_set_structure_type (attribute, struct_element[sct].struct_type);
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(rights_entries); i++)
+ {
+ gchar *t_tag;
+ gint p1;
+ gint p2;
+
+ p1 = string_index_of (tag, "[", 0); /* is it a structure tag? */
+ if (p > -1) /* yes! */
+ {
+ gchar *t1;
+ gchar *t2;
+ gint l;
+
+ p2 = string_index_of (tag, "]", p1);
+
+ l = strlen (tag);
+
+ t1 = string_substring (tag, 0, p);
+ t2 = string_substring (tag, p2 + 1, l - (p2 + 1));
+
+ t_tag = g_strdup_printf ("%s[x]%s", t1, t2);
+ }
+ else
+ {
+ t_tag = g_strdup (tag);
+ }
+
+
+ if (! g_strcmp0 (t_tag, rights_entries[i].xmp_tag))
+ {
+ if (rights_entries[i].number_of_comboarray > -1)
+ {
+ gint combobox_data_counter;
+ gint array_length;
+ gint number_in_comboarray = rights_entries[i].number_of_comboarray;
+
+ array_length = G_N_ELEMENTS(combobox_data[number_in_comboarray]);
+
+ for (combobox_data_counter = 0; combobox_data_counter < array_length;
combobox_data_counter++)
+ {
+ const gchar *val_in_tag =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_tag;
+ const gchar *interpreted_val;
+
+ if (! val_in_tag)
+ break;
+
+ if (! g_strcmp0 (value, val_in_tag))
+ {
+ interpreted_val =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_combo;
+ gimp_attribute_set_interpreted_string (attribute, interpreted_val);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ gimp_attributes_add_attribute (attributes, attribute);
+ }
+ }
+}
+
+static void
+page_rights_entry_activate_cb (GtkWidget *widget, gpointer userdata)
+{
+ const gchar *entry_name = (const gchar *) userdata;
+ const gchar *value = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ page_rights_store_in_hash_table (entry_name, value, 0);
+}
+
+static gboolean
+page_rights_entry_focus_out_cb (GtkWidget *widget, GdkEvent *event, gpointer userdata)
+{
+ const gchar *entry_name = (const gchar *) userdata;
+ const gchar *value = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ page_rights_store_in_hash_table (entry_name, value, 0);
+
+ return FALSE;
+}
+
+static void
+page_rights_entry_combo_changed_callback (GtkWidget *combo, gpointer userdata)
+{
+ const gchar *widget_name;
+ const gchar *value;
+ gint array_nr = GPOINTER_TO_INT (userdata);
+ gint index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ value = combobox_data[array_nr][index].val_in_tag;
+ widget_name = gtk_widget_get_name (GTK_WIDGET (combo));
+
+ page_rights_store_in_hash_table (widget_name, value, 0);
+}
+
+static gboolean
+page_rights_store_in_hash_table (const gchar *entry_name, const gchar *value, gint nr)
+{
+ gint i;
+ const gchar *o_tag;
+ gint p;
+ gint number = 0;
+ gboolean success;
+
+ g_return_val_if_fail (entry_name != NULL, FALSE);
+
+ if (nr > 0)
+ number = nr;
+
+ success = FALSE;
+
+ for (i = 0; i < G_N_ELEMENTS(rights_entries); i++)
+ {
+ if (! g_strcmp0 (entry_name, rights_entries[i].ui_entry))
+ {
+ gchar *new_tag;
+ gchar *nr_string;
+
+ o_tag = rights_entries[i].xmp_tag;
+
+ new_tag =g_strdup (o_tag);
+
+ p = string_index_of (new_tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes */
+ {
+ gint sct;
+
+ if (number <= 0)
+ {
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_strcmp0 (o_tag, struct_element[sct].struct_tag))
+ {
+ number = curr_shown_structure[struct_element[sct].number_of_element];
+ break;
+ }
+ }
+ }
+
+ nr_string = g_strdup_printf ("[%d]", number);
+ new_tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ g_free (nr_string);
+ if (number <=0 )
+ {
+ g_free (new_tag);
+ return FALSE;
+ }
+ }
+
+ if (value && g_strcmp0 (value, ""))
+ {
+ if (g_hash_table_insert (elements_table, (gpointer) g_strdup (new_tag), (gpointer) g_strdup
(value)))
+ success = TRUE;
+ }
+ else
+ {
+ if (g_hash_table_remove (elements_table, (gpointer) new_tag))
+ success = TRUE;
+ }
+
+ set_save_attributes_button_sensitive (TRUE);
+
+ g_free (new_tag);
+ break;
+ }
+ }
+ return success;
+}
+
+static void
+page_rights_structure_add (GtkButton *button, gpointer userdata)
+{
+ gint number_to_add;
+ GtkComboBox *combo;
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ const gchar *widget_name;
+ gchar *line;
+ gint sct;
+ gint repaint;
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (button));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].add_widget))
+ {
+ page_rights_structure_save (builder, struct_element[sct].number_of_element);
+ number_to_add = ++highest_structure[struct_element[sct].number_of_element];
+
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+ line = g_strdup_printf ("%s [%d]", struct_element[sct].identifier, number_to_add);
+ curr_shown_structure [struct_element[sct].number_of_element] = number_to_add;
+
+ repaint = struct_element[sct].number_of_element;
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK
(page_rights_combobox_changed_callback), builder);
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, line,
+ -1);
+
+ gtk_combo_box_set_active (combo, number_to_add-1);
+
+ if (number_to_add == 1)
+ page_rights_set_entry_sensitive (builder, struct_element[sct].struct_tag, TRUE);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_rights_combobox_changed_callback), builder);
+
+ g_free (line);
+ break;
+ }
+ }
+
+ page_rights_set_to_ui (builder, repaint);
+}
+
+static void
+page_rights_structure_remove (GtkButton *button, gpointer userdata)
+{
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkListStore *liststore;
+ GHashTableIter iter_remove;
+ gboolean found;
+ gchar *nr_string;
+ gchar *nr_string_new;
+ gpointer key, value;
+ GSList *delete_key_list = NULL;
+ GSList *list;
+ gchar *tag_prefix;
+ gchar *new_key;
+ gint number_to_delete;
+ GtkComboBox *combo;
+ GtkTreeIter combo_iter;
+ const gchar *widget_name;
+ gint sct;
+ gint repaint;
+ gint combo_to_del;
+
+ GHashTable *reorder_table;
+
+ reorder_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (button));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].remove_widget))
+ {
+ number_to_delete = curr_shown_structure[sct];
+ tag_prefix = g_strdup (struct_element[sct].struct_tag);
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+ combo_to_del = highest_structure[sct] - 1;
+
+ if (number_to_delete == highest_structure[struct_element[sct].number_of_element])
+ curr_shown_structure[struct_element[sct].number_of_element] = number_to_delete - 1;
+ else
+ curr_shown_structure[struct_element[sct].number_of_element] = number_to_delete;
+
+ highest_structure[sct]--;
+ repaint = struct_element[sct].number_of_element;
+ break;
+ }
+ }
+
+ nr_string = g_strdup_printf ("%s[%d]", tag_prefix, number_to_delete);
+
+ /* remove entries */
+ {
+
+ g_hash_table_iter_init (&iter_remove, elements_table);
+ while (g_hash_table_iter_next (&iter_remove, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ if (g_str_has_prefix (tag, nr_string))
+ {
+ delete_key_list = g_slist_prepend (delete_key_list, g_strdup (tag));
+ }
+ }
+
+ for (list = delete_key_list; list; list = list->next)
+ {
+ gchar *key = (gchar *) list->data;
+ g_hash_table_remove (elements_table, key);
+ }
+
+ g_slist_free_full (delete_key_list, g_free);
+
+ }
+ /* reorder entries */
+
+ found = TRUE;
+
+ {
+ while (found) /* first: element table */
+ {
+ GHashTableIter iter_reorder;
+
+ delete_key_list = NULL;
+ found = FALSE;
+
+ nr_string_new = g_strdup_printf ("%s[%d]", tag_prefix, number_to_delete + 1);
+
+ g_hash_table_iter_init (&iter_reorder, elements_table);
+ while (g_hash_table_iter_next (&iter_reorder, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ gchar *tag_value = (gchar *) value;
+
+ if (g_str_has_prefix (tag, nr_string_new))
+ {
+ found = TRUE;
+ new_key = string_replace_str (tag, nr_string_new, nr_string);
+ g_hash_table_insert (reorder_table, g_strdup (new_key), g_strdup (tag_value));
+ delete_key_list = g_slist_prepend (delete_key_list, g_strdup (tag));
+ }
+ }
+
+ for (list = delete_key_list; list; list = list->next)
+ {
+ gchar *key = (gchar *) list->data;
+ g_hash_table_remove (elements_table, key);
+ }
+ g_slist_free_full (delete_key_list, g_free);
+
+ g_hash_table_iter_init (&iter_reorder, reorder_table);
+ while (g_hash_table_iter_next (&iter_reorder, &key, &value))
+ {
+ gchar *tag = (gchar *) key;
+ gchar *tag_value = (gchar *) value;
+
+ g_hash_table_insert (elements_table, g_strdup (tag), g_strdup (tag_value));
+ }
+
+ g_hash_table_remove_all (reorder_table);
+
+ nr_string = g_strdup (nr_string_new);
+ number_to_delete ++;
+ }
+
+ }
+
+ g_free (tag_prefix);
+ g_free (nr_string);
+ g_free (nr_string_new);
+ g_free (new_key);
+
+ g_hash_table_unref (reorder_table);
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK (page_rights_combobox_changed_callback),
builder);
+
+ gtk_combo_box_set_active (combo, combo_to_del);
+ if (gtk_combo_box_get_active_iter (combo, &combo_iter))
+ gtk_list_store_remove (liststore, &combo_iter);
+
+ if (curr_shown_structure[repaint] == 0)
+ page_rights_set_entry_sensitive (builder, struct_element[sct].struct_tag, FALSE);
+ else
+ gtk_combo_box_set_active (combo, curr_shown_structure[repaint] - 1);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK (page_rights_combobox_changed_callback),
builder);
+
+ page_rights_set_to_ui (builder, repaint);
+}
+
+static void
+page_rights_combobox_changed_callback (GtkWidget *combo, gpointer userdata)
+{
+ GtkBuilder *builder = (GtkBuilder *) userdata;
+ GtkComboBox *combobox;
+ gint nr;
+ const gchar *widget_name;
+ gint sct;
+ gint repaint;
+
+ widget_name = gtk_widget_get_name (GTK_WIDGET (combo));
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (! g_strcmp0 (widget_name, struct_element[sct].struct_combo_widget))
+ {
+ page_rights_structure_save (builder, struct_element[sct].number_of_element);
+ combobox = GTK_COMBO_BOX (get_widget_from_label (builder,
struct_element[sct].struct_combo_widget));
+ repaint = struct_element[sct].number_of_element;
+ break;
+ }
+ }
+
+ nr = gtk_combo_box_get_active (combobox);
+ nr++;
+ curr_shown_structure[repaint] = nr;
+
+ page_rights_set_to_ui (builder, repaint);
+}
+
+static void
+page_rights_structure_save (GtkBuilder *builder, gint struct_number)
+{
+ gint i;
+ const gchar *prefix = struct_element[struct_number].struct_tag;
+
+ for (i = 0; i < G_N_ELEMENTS(rights_entries); i++)
+ {
+ GObject *obj;
+ gchar *tag = NULL;
+ const gchar *o_tag;
+
+ if (curr_shown_structure[struct_number] > 0)
+ {
+ o_tag = rights_entries[i].xmp_tag;
+ if (g_str_has_prefix (o_tag, prefix))
+ {
+ gchar *nr_string;
+
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[struct_number]);
+ tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ obj = get_widget_from_label (builder, rights_entries[i].ui_entry);
+
+ switch (rights_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gchar *value;
+ value = g_strdup (gtk_entry_get_text (GTK_ENTRY (obj)));
+ if (value && g_strcmp0 (value, ""))
+ g_hash_table_insert (elements_table, (gpointer) g_strdup (tag), (gpointer) g_strdup
(value));
+ else
+ g_hash_table_remove (elements_table, (gpointer) tag);
+ }
+ break;
+ case WIDGET_TYPE_COMBOBOX:
+ break;
+ default:
+ break;
+ }
+
+ g_free (nr_string);
+ g_free (tag);
+ }
+ }
+ }
+}
+
+static void
+page_rights_set_to_ui (GtkBuilder *builder, gint repaint)
+{
+ gint i;
+ gint sct;
+ const gchar *prefix;
+
+ if (repaint != -1)
+ prefix = struct_element[repaint].struct_tag;
+ else
+ prefix = NULL;
+
+ for (i = 0; i < G_N_ELEMENTS(rights_entries); i++)
+ {
+ GObject *obj;
+ const gchar *o_tag;
+ gchar *value = NULL;
+ gchar *new_tag;
+ gint p;
+
+ o_tag = rights_entries[i].xmp_tag;
+
+ obj = get_widget_from_label (builder, rights_entries[i].ui_entry);
+
+ if (prefix)
+ {
+ if (! g_str_has_prefix (o_tag, prefix))
+ continue;
+ }
+
+ p = string_index_of (o_tag, "[x]", 0); /* is it a structure tag? */
+
+ if (p > -1) /* yes! */
+ {
+ gchar *nr_string;
+
+ if (repaint != -1)
+ {
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[repaint]);
+ }
+ else
+ {
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ if (g_str_has_prefix (o_tag, struct_element[sct].struct_tag))
+ {
+ nr_string = g_strdup_printf ("[%d]", curr_shown_structure[sct]);
+ break;
+ }
+ }
+ }
+
+ new_tag = string_replace_str (o_tag, "[x]", nr_string);
+
+ g_free (nr_string);
+ }
+ else
+ {
+ new_tag = g_strdup (o_tag);
+ }
+
+ value = (gchar *) g_hash_table_lookup (elements_table, new_tag);
+
+ switch (rights_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gtk_entry_set_text (GTK_ENTRY (obj), "");
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (value)
+ {
+ switch (rights_entries[i].widget_type)
+ {
+ case WIDGET_TYPE_ENTRY:
+ {
+ gtk_entry_set_text (GTK_ENTRY (obj), value);
+ }
+ break;
+ case WIDGET_TYPE_COMBOBOX:
+ {
+ gint combobox_data_counter;
+ gint array_length;
+ gint number_in_comboarray = rights_entries[i].number_of_comboarray;
+
+ array_length = G_N_ELEMENTS(combobox_data[number_in_comboarray]);
+
+ for (combobox_data_counter = 0; combobox_data_counter < array_length;
combobox_data_counter++)
+ {
+ const gchar *val_in_tag =
combobox_data[number_in_comboarray][combobox_data_counter].val_in_tag;
+
+ if (! val_in_tag)
+ break;
+
+ if (! g_strcmp0 (value, val_in_tag))
+ {
+ g_signal_handlers_block_by_func (obj,
+ G_CALLBACK
(page_rights_entry_combo_changed_callback),
+ GINT_TO_POINTER (number_in_comboarray));
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (obj), combobox_data_counter);
+
+ g_signal_handlers_unblock_by_func (obj,
+ G_CALLBACK
(page_rights_entry_combo_changed_callback),
+ GINT_TO_POINTER (number_in_comboarray));
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ g_free (new_tag);
+ }
+}
+
+static void
+page_rights_init_combobox (GtkBuilder *builder)
+{
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ GtkComboBox *combo;
+ gint sct;
+
+ for (sct = 0; sct < STRUCTURES_ON_PAGE; sct ++)
+ {
+ gchar *line;
+ gint i;
+ gint high;
+ gint active;
+
+ liststore = GTK_LIST_STORE (get_widget_from_label (builder,
struct_element[sct].struct_liststore_widget));
+ combo = GTK_COMBO_BOX (get_widget_from_label (builder, struct_element[sct].struct_combo_widget));
+
+ high = highest_structure[struct_element[sct].number_of_element];
+ active = curr_shown_structure[struct_element[sct].number_of_element];
+
+ g_signal_handlers_block_by_func (G_OBJECT (combo), G_CALLBACK (page_rights_combobox_changed_callback),
builder);
+
+ for (i = 0; i < high; i++)
+ {
+ line = g_strdup_printf ("%s [%d]",struct_element[sct].identifier, i + 1);
+
+ gtk_list_store_append(liststore, &iter);
+
+ gtk_list_store_set (liststore, &iter,
+ 0, line,
+ -1);
+ g_free (line);
+ }
+
+ gtk_combo_box_set_active (combo, active-1);
+
+ if (active >= 1)
+ page_rights_set_entry_sensitive (builder, struct_element[sct].struct_tag, TRUE);
+ else
+ page_rights_set_entry_sensitive (builder, struct_element[sct].struct_tag, FALSE);
+
+ g_signal_handlers_unblock_by_func(G_OBJECT (combo), G_CALLBACK
(page_rights_combobox_changed_callback), builder);
+
+ }
+}
diff --git a/plug-ins/metainfo/page-rights.h b/plug-ins/metainfo/page-rights.h
new file mode 100644
index 0000000..fac3922
--- /dev/null
+++ b/plug-ins/metainfo/page-rights.h
@@ -0,0 +1,33 @@
+/* page-rights.h
+ *
+ * Copyright (C) 2014, Hartmut Kuhse <hatti gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PAGE_RIGHTS_H__
+#define __PAGE_RIGHTS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void page_rights_read_from_attributes (GimpAttributes *attributes,
+ GtkBuilder *builder);
+void page_rights_save_to_attributes (GimpAttributes *attributes);
+
+G_END_DECLS
+
+#endif /* __PAGE_RIGHTS_H__ */
diff --git a/plug-ins/ui/Makefile.am b/plug-ins/ui/Makefile.am
index aec82b9..b8d32ad 100644
--- a/plug-ins/ui/Makefile.am
+++ b/plug-ins/ui/Makefile.am
@@ -4,6 +4,9 @@ uidata_DATA = \
plug-in-file-gif.ui \
plug-in-file-png.ui \
plug-in-file-tiff.ui \
- plug-in-metadata.ui
+ plug-in-attributes.ui \
+ plug-in-iptc.ui \
+ plug-in-metainfo.ui
+
EXTRA_DIST = $(uidata_DATA)
diff --git a/plug-ins/ui/plug-in-attributes.ui b/plug-ins/ui/plug-in-attributes.ui
new file mode 100644
index 0000000..951bf4a
--- /dev/null
+++ b/plug-ins/ui/plug-in-attributes.ui
@@ -0,0 +1,346 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkTreeStore" id="exif-treestore">
+ <columns>
+ <!-- column-name c-exif-ifd -->
+ <column type="gchararray"/>
+ <!-- column-name c-exif-tag -->
+ <column type="gchararray"/>
+ <!-- column-name c-exif-value -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkVBox" id="attributes-vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">out</property>
+ <child>
+ <object class="GtkHBox" id="header-hbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label-header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label-info">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="thumb-box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="attributes-notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="scrollable">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="exif-scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">6</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="exif-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">exif-treestore</property>
+ <property name="headers_clickable">False</property>
+ <property name="expander_column">exif_ifd_column</property>
+ <property name="rules_hint">True</property>
+ <property name="search_column">1</property>
+ <property name="enable_tree_lines">True</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="exif_ifd_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Exif IFD</property>
+ <child>
+ <object class="GtkCellRendererText" id="exif_ifd_cell_renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="exif_tag_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Exif Tag</property>
+ <property name="expand">True</property>
+ <property name="sort_column_id">1</property>
+ <child>
+ <object class="GtkCellRendererText" id="exif_tag_renderer_text"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="exif_value_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Value</property>
+ <child>
+ <object class="GtkCellRendererText" id="exif_value_cell_renderer"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="exif">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</property>
+ <property name="label" translatable="yes">Exif</property>
+ </object>
+ <packing>
+ <property name="tab_expand">True</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="xmp-scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">6</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="xmp-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">xmp-treestore</property>
+ <property name="headers_clickable">False</property>
+ <property name="rules_hint">True</property>
+ <property name="search_column">1</property>
+ <property name="enable_tree_lines">True</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="xmp_schema_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">XMP schema</property>
+ <child>
+ <object class="GtkCellRendererText" id="xmp_schema_cell_renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="xmp_tag_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">XMP Property</property>
+ <property name="expand">True</property>
+ <property name="sort_column_id">1</property>
+ <child>
+ <object class="GtkCellRendererText" id="xmp_tag_cell_renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="xmp_value_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Value</property>
+ <child>
+ <object class="GtkCellRendererText" id="xmp_value_cell_renderer"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="xmp">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</property>
+ <property name="label" translatable="yes">XMP</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc-scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">6</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="iptc-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">iptc-treestore</property>
+ <property name="headers_clickable">False</property>
+ <property name="rules_hint">True</property>
+ <property name="search_column">1</property>
+ <property name="enable_tree_lines">True</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="iptc_key_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">IPTC key</property>
+ <child>
+ <object class="GtkCellRendererText" id="iptc_key_cell_renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="iptc_tag_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">IPTC Tag</property>
+ <property name="expand">True</property>
+ <property name="sort_column_id">1</property>
+ <child>
+ <object class="GtkCellRendererText" id="iptc_tag_cell_renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="iptc_value_column">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Value</property>
+ <child>
+ <object class="GtkCellRendererText" id="iptc_value_cell_renderer"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="iptc">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</property>
+ <property name="label" translatable="yes">IPTC</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkTreeStore" id="iptc-treestore">
+ <columns>
+ <!-- column-name c-iptc-ifd -->
+ <column type="gchararray"/>
+ <!-- column-name c-iptc-tag -->
+ <column type="gchararray"/>
+ <!-- column-name c-iptc-value -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkTreeStore" id="xmp-treestore">
+ <columns>
+ <!-- column-name c-xmp-ifd -->
+ <column type="gchararray"/>
+ <!-- column-name c-xmp-tag -->
+ <column type="gchararray"/>
+ <!-- column-name c-xmp-value -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+</interface>
diff --git a/plug-ins/ui/plug-in-iptc.ui b/plug-ins/ui/plug-in-iptc.ui
new file mode 100644
index 0000000..1b95e5a
--- /dev/null
+++ b/plug-ins/ui/plug-in-iptc.ui
@@ -0,0 +1,779 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.24"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkVBox" id="iptc-vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkNotebook" id="iptc-notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="desctable">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">8</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">3</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Title</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_author">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Author</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_authortitle">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Authortitle</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_copyright">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Copyright</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_caption">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Caption</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_captionwriter">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Captionwriter</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_headline">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Headline</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_specialinstruct">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Special
+Instructions</property>
+ </object>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.ObjectName">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.Byline">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.BylineTitle">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.Copyright">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.Writer">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="caption_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.Caption">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="headline_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.Headline">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="instruct_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.SpecialInstructions">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="iptcdesc">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">2</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">Description</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">3</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_keywords1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Keywords</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_category1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Category</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_suppcat1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Supplemental
+Category</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_urgency1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Urgency</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow10">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.Keywords">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow12">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Iptc.Application2.SuppCategory">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.Urgency">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.Category">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="iptckeys">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">2</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">Keywords/Categories</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">9</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">3</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkHSeparator" id="hseparator4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_credit">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Credit</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_source">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Source</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.Credit">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.Source">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_transmission">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Transmission
+reference</property>
+ </object>
+ <packing>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.TransmissionReference">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_city">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">City</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.City">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_sublocation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Sublocation</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.SubLocation">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_province">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Province/State</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.ProvinceState">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_country">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Country</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Iptc.Application2.CountryName">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="iptccredits">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">2</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">Credits/Origin</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/plug-ins/ui/plug-in-metainfo.ui b/plug-ins/ui/plug-in-metainfo.ui
new file mode 100644
index 0000000..1d449cc
--- /dev/null
+++ b/plug-ins/ui/plug-in-metainfo.ui
@@ -0,0 +1,3556 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.24"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkListStore" id="artworkorobject-liststore">
+ <columns>
+ <!-- column-name Artwork -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="copyrightowner-liststore">
+ <columns>
+ <!-- column-name Owner -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="digitalsourcetype-liststore">
+ <columns>
+ <!-- column-name Digital -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="imagecreator-liststore">
+ <columns>
+ <!-- column-name Creator -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="imagesupplier-liststore">
+ <columns>
+ <!-- column-name Supplier -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="licensor-liststore">
+ <columns>
+ <!-- column-name Licensor -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="location-created-liststore">
+ <columns>
+ <!-- column-name Location -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="location-shown-liststore">
+ <columns>
+ <!-- column-name Location -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="minormodelagedisclosure-liststore">
+ <columns>
+ <!-- column-name Model -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="modelreleasestatus-liststore">
+ <columns>
+ <!-- column-name Model -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="release-status-liststore">
+ <columns>
+ <!-- column-name Status -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkVBox" id="metainfo-vbox">
+ <property name="width_request">600</property>
+ <property name="height_request">300</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">out</property>
+ <child>
+ <object class="GtkHBox" id="header-hbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label-header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label-info">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="thumb-box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">etched-in</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">11</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">4</property>
+ <property name="row_spacing">8</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkExpander" id="location-shown-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">8</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-shown-sublocation-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Sublocation</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-shown-city-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">City</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-shown-provincestate-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Province / State</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-shown-countryname-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Countryname</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-shown-countrycode-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Countrycode</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-shown-worldregion-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Worldregion</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="location-shown-button-plus">
+ <property name="label" translatable="yes">+</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="location-shown-button-minus">
+ <property name="label" translatable="yes">-</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment17">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="location-shown-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">location-shown-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-shown-sublocation-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-shown-city-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-shown-provincestate-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-shown-countryname-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-shown-countrycode-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-shown-worldregion-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="location-shown-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Location shown</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="organisationinimage-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkAlignment" id="alignment18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="orginimage-name-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Org. in image</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="orginimage-code-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Org. in image
code</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="orginimage-name-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="orginimage-code-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="organisationinimage-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Organisation in image</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="location-created-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">8</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="location-created-city-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">City</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-created-provincestate-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Province / State</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-created-countryname-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Countryname</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-created-countrycode-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Countrycode</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-created-worldregion-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Worldregion</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="location-created-button-plus">
+ <property name="label" translatable="yes">+</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="location-created-button-minus">
+ <property name="label" translatable="yes">-</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment16">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="location-created-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">location-created-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="location-created-sublocation-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Sublocation</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-created-sublocation-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-created-city-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-created-provincestate-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-created-countryname-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-created-countrycode-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="location-created-worldregion-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="location-created-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Location created</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed1">
+ <property name="height_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">8</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <child>
+ <object class="GtkLabel" id="personinimage-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="ypad">8</property>
+ <property name="label" translatable="yes">Person in Image</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="personinimage-entry">
+ <property name="width_request">0</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="event-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Event</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="event-entry">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="common-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Common</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="desc-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Description</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkViewport" id="viewport2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">etched-in</property>
+ <child>
+ <object class="GtkTable" id="table5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkExpander" id="artworkorobject-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">8</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="artworkorobject-title-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Title</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="artworkorobject-datecreated-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Date created</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="artworkorobject-creator-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Creator</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="artworkorobject-source-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Source</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="artworkorobject-sourceinvno-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Source inventory
number</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="artworkorobject-copyrightnotice-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Copyright notice</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="artworkorobject-title-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="artworkorobject-datecreated-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="artworkorobject-creator-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="artworkorobject-source-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="artworkorobject-sourceinvno-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="artworkorobject-copyrightnotice-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="artworkorobject-button-plus">
+ <property name="label" translatable="yes">+</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="artworkorobject-button-minus">
+ <property name="label" translatable="yes">-</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment30">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="artworkorobject-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">artworkorobject-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="artworkorobject-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Artwork or object</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="y_padding">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">16</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="addlmodelinfo-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Additional model
information</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="modelage-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Model age</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="minormodelagedisclosure-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Minor model age</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="modelreleasestatus-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Model release
status</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="modelreleaseid-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Model release ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="modelage-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="minormodelagedisclosure-combobox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">minormodelagedisclosure-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext4"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="modelreleasestatus-combobox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">modelreleasestatus-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext5"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="modelreleaseid-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="addlmodelinfo-entry">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="modelframe-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Model</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed6">
+ <property name="height_request">230</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="art-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Artwork / Model</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkViewport" id="viewport3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">etched-in</property>
+ <child>
+ <object class="GtkTable" id="table6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">7</property>
+ <property name="n_columns">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkExpander" id="registryid-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="regorgid-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Registry Organistaion
ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="regitemid-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Registry item ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="regorgid-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="regitemid-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="registryid-button-plus">
+ <property name="label" translatable="yes">+</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="registryid-button-minus">
+ <property name="label" translatable="yes">-</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment32">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="registryid-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">registryid-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext7"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="registryid-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Registry ID</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+ <property name="y_padding">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed7">
+ <property name="height_request">175</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">4</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">4</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="maxavailheight-label">
+ <property name="width_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Max available height</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="maxavailwidth-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Max available width</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="digitalsourcetype-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Digital Source Type</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment35">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="digitalsourcetype-combobox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">digitalsourcetype-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext8"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="maxavailheight-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="maxavailwidth-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="y_padding">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="imagesupplier-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="bottom_padding">6</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="imagesupplier-imageid-label">
+ <property name="width_request">160</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Image supplier image
ID</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment4">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="imagesupplier-imageid-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK |
GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="imagesupplier-button-plus">
+ <property name="label" translatable="yes">+</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="imagesupplier-button-minus">
+ <property name="label" translatable="yes">-</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="imagesupplier-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">imagesupplier-liststore</property>
+ <child>
+ <object class="GtkCellRendererText"
id="cellrenderertext6"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkLabel" id="imagesupplier-name-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Image supplier
name</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="imagesupplier-id-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Image supplier
ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="imagesupplier-name-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="imagesupplier-id-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="imagesupplier-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Image Supplier</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+ <property name="y_padding">8</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="admin-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Administration</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkViewport" id="viewport4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="table12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">8</property>
+ <property name="n_columns">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkExpander" id="imagecreator-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkAlignment" id="alignment11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkLabel" id="imagecreator-name-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Image creator
name</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="imagecreator-id-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Image creator ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="imagecreator-name-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="imagecreator-id-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="imagecreator-button-plus">
+ <property name="label" translatable="yes">+</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="imagecreator-button-minus">
+ <property name="label" translatable="yes">-</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment36">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="imagecreator-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">imagecreator-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext9"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="imagecreator-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Image creator</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+ <property name="y_padding">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="copyrightowner-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkAlignment" id="alignment12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkLabel" id="copyrightowner-name-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Copyright owner
name</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="copyrightowner-id-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Copyright owner
ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="copyrightowner-name-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="copyrightowner-id-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="copyrightowner-button-plus">
+ <property name="label" translatable="yes">+</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="copyrightowner-button-minus">
+ <property name="label" translatable="yes">-</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment37">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="copyrightowner-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">copyrightowner-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext10"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="copyrightowner-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Copyright owner</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
+ <property name="y_padding">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="licensor-expander">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">14</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="licensor-name-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Name</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-id-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-streetaddress-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Address</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-extendedaddress-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Extended address</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-city-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">City</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-region-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Region</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-name-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-id-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-streetaddress-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-extendedaddress-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-city-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-region-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkButton" id="licensor-button-plus">
+ <property name="label" translatable="yes">+</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="licensor-button-minus">
+ <property name="label" translatable="yes">-</property>
+ <property name="width_request">20</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">13</property>
+ <property name="bottom_attach">14</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment38">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBox" id="licensor-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">licensor-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext11"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">13</property>
+ <property name="bottom_attach">14</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-postalcode-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Postal code</property>
+ </object>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-country-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Country</property>
+ </object>
+ <packing>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-telephone1-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Telephone 1</property>
+ </object>
+ <packing>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-telephone2-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Telephone 2</property>
+ </object>
+ <packing>
+ <property name="top_attach">10</property>
+ <property name="bottom_attach">11</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-email-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">Email</property>
+ </object>
+ <packing>
+ <property name="top_attach">11</property>
+ <property name="bottom_attach">12</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="licensor-url-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="label" translatable="yes">URL</property>
+ </object>
+ <packing>
+ <property name="top_attach">12</property>
+ <property name="bottom_attach">13</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-postalcode-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-country-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-telephone1-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-telephone2-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">10</property>
+ <property name="bottom_attach">11</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-email-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">11</property>
+ <property name="bottom_attach">12</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="licensor-url-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">12</property>
+ <property name="bottom_attach">13</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="licensor-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Licensor</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_padding">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">4</property>
+ <child>
+ <object class="GtkTable" id="table16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">16</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="release-id-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Release ID</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="release-status-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Release status</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="release-status-combobox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">release-status-liststore</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext12"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="release-id-entry">
+ <property name="width_request">350</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="realease-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Release</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFixed" id="fixed10">
+ <property name="height_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="rights-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Rights</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkListStore" id="registryid-liststore">
+ <columns>
+ <!-- column-name Registry -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+</interface>
diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in
index 93dc7ff..e512d53 100644
--- a/po-plug-ins/POTFILES.in
+++ b/po-plug-ins/POTFILES.in
@@ -6,6 +6,7 @@
plug-ins/common/align-layers.c
plug-ins/common/animation-optimize.c
plug-ins/common/animation-play.c
+plug-ins/common/attributes.c
plug-ins/common/blinds.c
plug-ins/common/blur.c
plug-ins/common/blur-gauss-selective.c
@@ -87,7 +88,6 @@ plug-ins/common/lcms.c
plug-ins/common/lens-flare.c
plug-ins/common/mail.c
plug-ins/common/max-rgb.c
-plug-ins/common/metadata.c
plug-ins/common/newsprint.c
plug-ins/common/nl-filter.c
plug-ins/common/noise-solid.c
@@ -171,7 +171,8 @@ plug-ins/gimpressionist/utils.c
[type: gettext/glade]plug-ins/ui/plug-in-file-gif.ui
[type: gettext/glade]plug-ins/ui/plug-in-file-png.ui
[type: gettext/glade]plug-ins/ui/plug-in-file-tiff.ui
-[type: gettext/glade]plug-ins/ui/plug-in-metadata.ui
+[type: gettext/glade]plug-ins/ui/plug-in-metainfo.ui
+[type: gettext/glade]plug-ins/ui/plug-in-attributes.ui
plug-ins/gradient-flare/gradient-flare.c
plug-ins/help-browser/dialog.c
plug-ins/help-browser/help-browser.c
@@ -233,6 +234,11 @@ plug-ins/maze/maze-algorithms.c
plug-ins/maze/maze-dialog.c
plug-ins/maze/maze.c
plug-ins/maze/maze.h
+plug-ins/metainfo/interface.c
+plug-ins/metainfo/page-administration.c
+plug-ins/metainfo/page-rights.c
+plug-ins/metainfo/page-description.c
+plug-ins/metainfo/page-artwork.c
plug-ins/pagecurl/pagecurl.c
plug-ins/print/print-draw-page.c
plug-ins/print/print-page-layout.c
diff --git a/tools/pdbgen/pdb/image.pdb b/tools/pdbgen/pdb/image.pdb
index be468f8..73237bb 100644
--- a/tools/pdbgen/pdb/image.pdb
+++ b/tools/pdbgen/pdb/image.pdb
@@ -1625,11 +1625,11 @@ CODE
);
}
-sub image_get_metadata {
- $blurb = "Returns the image's metadata.";
- $help = 'Returns exif/iptc/xmp metadata from the image.';
+sub image_get_attributes {
+ $blurb = "Returns the image's attributes.";
+ $help = 'Returns attributes from the image.';
- &std_pdb_misc('2013', '2.10');
+ &std_pdb_misc('2014', '2.10');
@inargs = (
{ name => 'image', type => 'image',
@@ -1637,44 +1637,44 @@ sub image_get_metadata {
);
@outargs = (
- { name => 'metadata_string', type => 'string', wrap => 1,
- desc => 'The exif/ptc/xmp metadata as a string'}
+ { name => 'attributes_string', type => 'string', wrap => 1,
+ desc => 'The attributes as a xml string'}
);
%invoke = (
code => <<'CODE'
{
- GimpMetadata *metadata = gimp_image_get_metadata (image);
+ GimpAttributes *attributes = gimp_image_get_attributes (image);
- if (metadata)
- metadata_string = gimp_metadata_serialize (metadata);
+ if (attributes)
+ attributes_string = gimp_attributes_serialize (attributes);
}
CODE
);
}
-sub image_set_metadata {
- $blurb = "Set the image's metadata.";
- $help = 'Sets exif/iptc/xmp metadata on the image.';
+sub image_set_attributes {
+ $blurb = "Set the image's attributes.";
+ $help = 'Sets attributes on the image.';
- &std_pdb_misc('2013', '2.10');
+ &std_pdb_misc('2014', '2.10');
@inargs = (
{ name => 'image', type => 'image',
desc => 'The image' },
- { name => 'metadata_string', type => 'string', wrap => 1,
- desc => 'The exif/ptc/xmp metadata as a string' }
+ { name => 'attributes_string', type => 'string', wrap => 1,
+ desc => 'The attributes as a xml string' }
);
%invoke = (
code => <<'CODE'
{
- GimpMetadata *metadata = gimp_metadata_deserialize (metadata_string);
+ GimpAttributes *attributes = gimp_attributes_deserialize (attributes_string);
- gimp_image_set_metadata (image, metadata, TRUE);
+ gimp_image_set_attributes (image, attributes, TRUE);
- if (metadata)
- g_object_unref (metadata);
+ if (attributes)
+ g_object_unref (attributes);
}
CODE
);
@@ -3095,7 +3095,7 @@ CODE
image_flatten image_merge_visible_layers image_merge_down
image_add_layer_mask image_remove_layer_mask
image_get_colormap image_set_colormap
- image_get_metadata image_set_metadata
+ image_set_attributes image_get_attributes
image_clean_all image_is_dirty
image_thumbnail
image_get_active_layer image_set_active_layer
diff --git a/tools/pdbgen/pdb/item.pdb b/tools/pdbgen/pdb/item.pdb
index 6d99a51..7eaac52 100644
--- a/tools/pdbgen/pdb/item.pdb
+++ b/tools/pdbgen/pdb/item.pdb
@@ -734,6 +734,61 @@ CODE
);
}
+sub item_get_attributes {
+ $blurb = "Returns the item's attributes.";
+ $help = 'Returns attributes from the item.';
+
+ &std_pdb_misc('2014', '2.10');
+
+ @inargs = (
+ { name => 'item', type => 'item',
+ desc => 'The item' }
+ );
+
+ @outargs = (
+ { name => 'attributes_string', type => 'string', wrap => 1,
+ desc => 'The attributes as a xml string'}
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ GimpAttributes *attributes = gimp_item_get_attributes (item);
+
+ if (attributes)
+ attributes_string = gimp_attributes_serialize (attributes);
+}
+CODE
+ );
+}
+
+sub item_set_attributes {
+ $blurb = "Set the item's attributes.";
+ $help = 'Sets attributes on the item.';
+
+ &std_pdb_misc('2014', '2.10');
+
+ @inargs = (
+ { name => 'item', type => 'item',
+ desc => 'The item' },
+ { name => 'attributes_string', type => 'string', wrap => 1,
+ desc => 'The attributes as a xml string' }
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ GimpAttributes *attributes = gimp_attributes_deserialize (attributes_string);
+
+ gimp_item_set_attributes (item, attributes, TRUE);
+
+ if (attributes)
+ g_object_unref (attributes);
+}
+CODE
+ );
+}
+
sub item_attach_parasite {
$blurb = 'Add a parasite to an item.';
@@ -847,10 +902,12 @@ CODE
}
@headers = qw("core/gimplayermask.h"
+ "libgimpbase/gimpbase.h"
"core/gimplist.h"
"core/gimpselection.h"
+ "core/gimpitem-metadata.h"
"text/gimptextlayer.h"
- "vectors/gimpvectors.h"
+ "vectors/gimpvectors.h"
"gimppdb-utils.h"
"gimppdbcontext.h"
"gimp-intl.h");
@@ -861,22 +918,23 @@ CODE
item_is_drawable
item_is_layer
item_is_text_layer
- item_is_channel
- item_is_layer_mask
- item_is_selection
- item_is_vectors
- item_is_group
+ item_is_channel
+ item_is_layer_mask
+ item_is_selection
+ item_is_vectors
+ item_is_group
item_get_parent
- item_get_children
+ item_get_children
item_get_name item_set_name
item_get_visible item_set_visible
item_get_linked item_set_linked
item_get_lock_content item_set_lock_content
item_get_lock_position item_set_lock_position
item_get_tattoo item_set_tattoo
- item_attach_parasite item_detach_parasite
- item_get_parasite
- item_get_parasite_list);
+ item_get_attributes item_set_attributes
+ item_attach_parasite item_detach_parasite
+ item_get_parasite
+ item_get_parasite_list);
%exports = (app => [ procs], lib => [ procs]);
diff --git a/tools/pdbgen/pdb/viewable.pdb b/tools/pdbgen/pdb/viewable.pdb
new file mode 100644
index 0000000..ba80b52
--- /dev/null
+++ b/tools/pdbgen/pdb/viewable.pdb
@@ -0,0 +1,109 @@
+# 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/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub viewable_get_attributes {
+ $blurb = "Returns the viewables's attributes.";
+ $help = <<'HELP';
+This procedure returns the stored attributes of a viewable
+GimpViewable is one of the lowest class in gimp, that other
+clsses are derived from.
+That is:
+image
+Layer
+Layer mask
+Grouplayer
+Brush/BrushPipe
+HELP
+
+ &std_pdb_misc('2014', '2.10');
+
+ @inargs = (
+ { name => 'object', type => 'GObject',
+ desc => 'The object' }
+ );
+
+ @outargs = (
+ { name => 'attributes_string', type => 'string', wrap => 1,
+ desc => 'The attributes as a xml string'}
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ GimpAttributes *attributes = gimp_viewable_get_attributes (object);
+
+ if (attributes)
+ attributes_string = gimp_attributes_serialize (metadata);
+}
+CODE
+ );
+}
+
+sub viewable_set_attributes {
+ $blurb = "Set the viewables attributes.";
+ $help = <<'HELP';
+This procedure set attributes of a viewable.
+GimpViewable is one of the lowest class in gimp, that other
+clsses are derived from.
+That is:
+image
+Layer
+Layer mask
+Grouplayer
+Brush/BrushPipe
+HELP
+
+ &std_pdb_misc('2014', '2.10');
+
+ @inargs = (
+ { name => 'object', type => 'GObject',
+ desc => 'The image' },
+ { name => 'attributes_string', type => 'string', wrap => 1,
+ desc => 'The attributes as a xml string' }
+ );
+
+ %invoke = (
+ code => <<'CODE'
+{
+ GimpAttributes *attributes = gimp_attributes_deserialize (NULL, attributes_string);
+
+ gimp_viewable_set_attributes (image, attributes);
+
+ if (attributes)
+ g_object_unref (attributes);
+}
+CODE
+ );
+}
+
+
+ headers = qw("libgimpbase/gimpbase.h"
+ "core/gimp.h"
+ "core/gimpviewable.h");
+
+ procs = qw(image_get_attributes
+ image_set_attributes);
+
+%exports = (app => [ procs], lib => [ procs]);
+
+$desc = 'Viewable attributes';
+$doc_title = 'viewableattributes';
+$doc_short_desc = 'Operations related to viewable attribuites.';
+$doc_long_desc = 'Operations related to viewable attribuites.';
+
+1;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]