gimp/plug-ins/metadata/xmp-model.c

929 lines
31 KiB
C

/* xmp-model.c - treeview model for XMP metadata
*
* Copyright (C) 2004-2005, Raphaël Quinet <raphael@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 2 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "libgimp/stdplugins-intl.h"
#include "xmp-schemas.h"
#include "xmp-parse.h"
#include "xmp-model.h"
/* The main part of the XMPModel structure is the GtkTreeStore in
* which all references to XMP properties are stored. In the tree,
* the elements at the root level are the schemas (namespaces) and the
* children of the schemas are the XMP properties.
*
* If the XMP file contains a schema that is not part of the XMP
* specification or a known extension (e.g., IPTC Core), it will be
* included in the custom_schemas list and the corresponding element
* in the tree will get a reference to that list element instead of a
* reference to one of the static schema definitions found in
* xmp-schemas.c. Same for custom properties inside a known or custom
* schema.
*/
struct _XMPModel
{
GtkTreeStore *treestore;
GSList *custom_schemas;
GSList *custom_properties;
XMPSchema *cached_schema;
GtkTreeIter cached_schema_iter;
};
/**
* xmp_model_new:
*
* Return value: a new #XMPModel.
**/
XMPModel *
xmp_model_new (void)
{
XMPModel *xmp_model;
xmp_model = g_new (XMPModel, 1);
/* columns defined by the XMPModelColumns enum */
xmp_model->treestore =
gtk_tree_store_new (XMP_MODEL_NUM_COLUMNS,
G_TYPE_STRING, /* COL_XMP_NAME */
G_TYPE_STRING, /* COL_XMP_VALUE */
G_TYPE_POINTER, /* COL_XMP_VALUE_RAW */
G_TYPE_POINTER, /* COL_XMP_TYPE_XREF */
G_TYPE_POINTER, /* COL_XMP_WIDGET_XREF */
G_TYPE_INT, /* COL_XMP_EDITABLE */
GDK_TYPE_PIXBUF, /* COL_XMP_EDIT_ICON */
G_TYPE_BOOLEAN, /* COL_XMP_VISIBLE */
G_TYPE_INT, /* COL_XMP_WEIGHT */
G_TYPE_BOOLEAN /* COL_XMP_WEIGHT_SET */
);
xmp_model->custom_schemas = NULL;
xmp_model->custom_properties = NULL;
xmp_model->cached_schema = NULL;
return xmp_model;
}
/**
* xmp_model_free:
* @xmp_model: an #XMPModel
*
* Frees an #XMPModel.
**/
void
xmp_model_free (XMPModel *xmp_model)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeIter child;
gchar **value_array;
gint i;
g_return_if_fail (xmp_model != NULL);
/* we used XMP_FLAG_DEFER_VALUE_FREE for the parser, so now we must free
all value arrays */
model = xmp_model_get_tree_model (xmp_model);
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
{
do
{
if (gtk_tree_model_iter_children (model, &child, &iter))
{
gchar **last_value_array = NULL;
do
{
gtk_tree_model_get (model, &child,
COL_XMP_VALUE_RAW, &value_array,
-1);
if (value_array != last_value_array)
{
/* FIXME: this does not free everything */
for (i = 0; value_array[i] != NULL; i++)
g_free (value_array[i]);
g_free (value_array);
}
last_value_array = value_array;
}
while (gtk_tree_model_iter_next (model, &child));
}
}
while (gtk_tree_model_iter_next (model, &iter));
}
g_object_unref (xmp_model->treestore);
/* FIXME: free custom schemas */
g_free (xmp_model);
}
/**
* xmp_model_is_empty:
* @xmp_model: an #XMPModel
*
* Return value: %TRUE if @xmp_model is empty (no shemas, no properties)
**/
gboolean
xmp_model_is_empty (XMPModel *xmp_model)
{
GtkTreeIter iter;
g_return_val_if_fail (xmp_model != NULL, TRUE);
if ((xmp_model->custom_schemas != NULL)
|| (xmp_model->custom_properties != NULL))
return FALSE;
return !gtk_tree_model_get_iter_first (GTK_TREE_MODEL (xmp_model->treestore),
&iter);
}
/* check if the given schema_uri matches a known schema; else return NULL */
static XMPSchema *
find_xmp_schema (XMPModel *xmp_model,
const gchar *schema_uri)
{
int i;
GSList *list;
const gchar *c;
/* check if we know about this schema (exact match for URI) */
for (i = 0; xmp_schemas[i].uri != NULL; ++i)
{
if (! strcmp (xmp_schemas[i].uri, schema_uri))
{
#ifdef DEBUG_XMP_MODEL
if (xmp_schemas[i].name != NULL)
g_print ("%s \t[%s]\n", xmp_schemas[i].name, xmp_schemas[i].uri);
else
g_print ("(no name) \t[%s]\n", xmp_schemas[i].uri);
#endif
return &(xmp_schemas[i]);
}
}
/* this is not a standard shema; now check the custom schemas */
for (list = xmp_model->custom_schemas; list != NULL; list = list->next)
{
if (! strcmp (((XMPSchema *)(list->data))->uri, schema_uri))
{
#ifdef DEBUG_XMP_MODEL
g_print ("CUSTOM %s \t[%s]\n",
((XMPSchema *)(list->data))->name,
((XMPSchema *)(list->data))->uri);
#endif
return (XMPSchema *)(list->data);
}
}
/* now check for some common errors and results of bad encoding: */
/* - check for "http:" without "//", or missing "http://" */
for (i = 0; xmp_schemas[i].uri != NULL; ++i)
{
if (g_str_has_prefix (xmp_schemas[i].uri, "http://")
&& ((! strcmp (xmp_schemas[i].uri + 7, schema_uri))
|| (g_str_has_prefix (schema_uri, "http:")
&& ! strcmp (xmp_schemas[i].uri + 7, schema_uri + 5))
))
{
#ifdef DEBUG_XMP_MODEL
g_print ("%s \t~~~[%s]\n", xmp_schemas[i].name, xmp_schemas[i].uri);
#endif
return &(xmp_schemas[i]);
}
}
/* - check for errors such as "name (uri)" or "name (prefix, uri)" */
for (c = schema_uri; *c; c++)
if ((*c == '(') || (*c == ' ') || (*c == ','))
{
int len;
c++;
while (*c == ' ')
c++;
if (! *c)
break;
for (len = 1; c[len]; len++)
if ((c[len] == ')') || (c[len] == ' '))
break;
for (i = 0; xmp_schemas[i].uri != NULL; ++i)
{
if (! strncmp (xmp_schemas[i].uri, c, len))
{
#ifdef DEBUG_XMP_MODEL
g_print ("%s \t~~~[%s]\n", xmp_schemas[i].name,
xmp_schemas[i].uri);
#endif
return &(xmp_schemas[i]);
}
}
}
#ifdef DEBUG_XMP_MODEL
g_print ("Unknown schema URI %s\n", schema_uri);
#endif
return NULL;
}
/* check if the given prefix matches a known schema; else return NULL */
static XMPSchema *
find_xmp_schema_prefix (XMPModel *xmp_model,
const gchar *prefix)
{
int i;
GSList *list;
for (i = 0; xmp_schemas[i].uri != NULL; ++i)
if (! strcmp (xmp_schemas[i].prefix, prefix))
return &(xmp_schemas[i]);
for (list = xmp_model->custom_schemas; list != NULL; list = list->next)
if (! strcmp (((XMPSchema *)(list->data))->prefix, prefix))
return (XMPSchema *)(list->data);
return NULL;
}
/* make the next lookup a bit faster if the tree is not modified */
static void
cache_iter_for_schema (XMPModel *xmp_model,
XMPSchema *schema,
GtkTreeIter *iter)
{
xmp_model->cached_schema = schema;
if (iter != NULL)
memcpy (&(xmp_model->cached_schema_iter), iter, sizeof (GtkTreeIter));
}
/* find the GtkTreeIter for the given schema and return TRUE if the schema was
found in the tree; else return FALSE */
static gboolean
find_iter_for_schema (XMPModel *xmp_model,
XMPSchema *schema,
GtkTreeIter *iter)
{
XMPSchema *schema_xref;
/* common case: return the cached iter */
if (schema == xmp_model->cached_schema)
{
memcpy (iter, &(xmp_model->cached_schema_iter), sizeof (GtkTreeIter));
return TRUE;
}
/* find where this schema has been stored in the tree */
if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (xmp_model->treestore),
iter))
return FALSE;
do
{
gtk_tree_model_get (GTK_TREE_MODEL (xmp_model->treestore), iter,
COL_XMP_TYPE_XREF, &schema_xref,
-1);
if (schema_xref == schema)
{
cache_iter_for_schema (xmp_model, schema, iter);
return TRUE;
}
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (xmp_model->treestore),
iter));
return FALSE;
}
/* remove a property from the list of children of schema_iter */
static void
find_and_remove_property (XMPModel *xmp_model,
XMPProperty *property,
GtkTreeIter *schema_iter)
{
GtkTreeIter child_iter;
XMPProperty *property_xref;
if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (xmp_model->treestore),
&child_iter, schema_iter))
return;
for (;;)
{
gtk_tree_model_get (GTK_TREE_MODEL (xmp_model->treestore), &child_iter,
COL_XMP_TYPE_XREF, &property_xref,
-1);
if (property_xref == property)
{
if (! gtk_tree_store_remove (GTK_TREE_STORE (xmp_model->treestore),
&child_iter))
break;
}
else
{
if (! gtk_tree_model_iter_next (GTK_TREE_MODEL(xmp_model->treestore),
&child_iter))
break;
}
}
}
/* add a schema to the tree */
static void
add_known_schema (XMPModel *xmp_model,
XMPSchema *schema,
GtkTreeIter *iter)
{
gtk_tree_store_append (xmp_model->treestore, iter, NULL);
gtk_tree_store_set (xmp_model->treestore, iter,
COL_XMP_NAME, schema->name,
COL_XMP_VALUE, schema->uri,
COL_XMP_VALUE_RAW, NULL,
COL_XMP_TYPE_XREF, schema,
COL_XMP_WIDGET_XREF, NULL,
COL_XMP_EDITABLE, FALSE,
COL_XMP_EDIT_ICON, NULL,
COL_XMP_VISIBLE, FALSE,
COL_XMP_WEIGHT, PANGO_WEIGHT_BOLD,
COL_XMP_WEIGHT_SET, TRUE,
-1);
cache_iter_for_schema (xmp_model, schema, iter);
}
/* called by the XMP parser - new schema */
static gpointer
parse_start_schema (XMPParseContext *context,
const gchar *ns_uri,
const gchar *ns_prefix,
gpointer user_data,
GError **error)
{
XMPModel *xmp_model = user_data;
GtkTreeIter iter;
XMPSchema *schema;
g_return_val_if_fail (xmp_model != NULL, NULL);
schema = find_xmp_schema (xmp_model, ns_uri);
if (schema == NULL)
{
/* add schema to custom_schemas */
schema = g_new (XMPSchema, 1);
schema->uri = g_strdup (ns_uri);
schema->prefix = g_strdup (ns_prefix);
schema->name = schema->uri;
schema->properties = NULL;
xmp_model->custom_schemas = g_slist_prepend (xmp_model->custom_schemas,
schema);
}
else if (find_iter_for_schema (xmp_model, schema, &iter))
{
/* already in the tree, so no need to add it again */
return schema;
}
/* schemas with NULL names are special and should not go in the tree */
if (schema->name == NULL)
{
cache_iter_for_schema (xmp_model, NULL, NULL);
return schema;
}
/* if the schema is not in the tree yet, add it now */
add_known_schema (xmp_model, schema, &iter);
return schema;
}
/* called by the XMP parser - end of schema */
static void
parse_end_schema (XMPParseContext *context,
gpointer ns_user_data,
gpointer user_data,
GError **error)
{
XMPModel *xmp_model = user_data;
XMPSchema *schema = ns_user_data;
g_return_if_fail (xmp_model != NULL);
g_return_if_fail (schema != NULL);
xmp_model->cached_schema = NULL;
#ifdef DEBUG_XMP_MODEL
if (schema->name)
g_print ("End of %s\n", schema->name);
#endif
}
/* called by the XMP parser - new property */
static void
parse_set_property (XMPParseContext *context,
const gchar *name,
XMPParseType type,
const gchar **value,
gpointer ns_user_data,
gpointer user_data,
GError **error)
{
XMPModel *xmp_model = user_data;
XMPSchema *schema = ns_user_data;
int i;
const gchar *ns_prefix;
XMPProperty *property;
GtkTreeIter iter;
GtkTreeIter child_iter;
gchar *tmp_name;
gchar *tmp_value;
g_return_if_fail (xmp_model != NULL);
g_return_if_fail (schema != NULL);
if (! find_iter_for_schema (xmp_model, schema, &iter))
{
g_warning ("Unable to set XMP property '%s' because its schema is bad",
name);
return;
}
ns_prefix = schema->prefix;
property = NULL;
if (schema->properties != NULL)
for (i = 0; schema->properties[i].name != NULL; ++i)
if (! strcmp (schema->properties[i].name, name))
{
property = &(schema->properties[i]);
break;
}
/* if the same property was already present, remove it (replace it) */
if (property != NULL)
find_and_remove_property (xmp_model, property, &iter);
switch (type)
{
case XMP_PTYPE_TEXT:
#ifdef DEBUG_XMP_MODEL
g_print ("\t%s:%s = \"%s\"\n", ns_prefix, name, value[0]);
#endif
if (property != NULL)
/* FIXME */;
else
{
property = g_new (XMPProperty, 1);
property->name = g_strdup (name);
property->type = XMP_TYPE_TEXT;
property->editable = TRUE;
xmp_model->custom_properties =
g_slist_prepend (xmp_model->custom_properties, property);
}
gtk_tree_store_append (xmp_model->treestore, &child_iter, &iter);
gtk_tree_store_set (xmp_model->treestore, &child_iter,
COL_XMP_NAME, name,
COL_XMP_VALUE, value[0],
COL_XMP_VALUE_RAW, value,
COL_XMP_TYPE_XREF, property,
COL_XMP_WIDGET_XREF, NULL,
COL_XMP_EDITABLE, property->editable,
COL_XMP_EDIT_ICON, NULL,
COL_XMP_VISIBLE, TRUE,
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
COL_XMP_WEIGHT_SET, FALSE,
-1);
break;
case XMP_PTYPE_RESOURCE:
#ifdef DEBUG_XMP_MODEL
g_print ("\t%s:%s @ = \"%s\"\n", ns_prefix, name,
value[0]);
#endif
if (property != NULL)
/* FIXME */;
else
{
property = g_new (XMPProperty, 1);
property->name = g_strdup (name);
property->type = XMP_TYPE_URI;
property->editable = TRUE;
xmp_model->custom_properties =
g_slist_prepend (xmp_model->custom_properties, property);
}
tmp_name = g_strconcat (name, " @", NULL);
gtk_tree_store_append (xmp_model->treestore, &child_iter, &iter);
gtk_tree_store_set (xmp_model->treestore, &child_iter,
COL_XMP_NAME, tmp_name,
COL_XMP_VALUE, value[0],
COL_XMP_VALUE_RAW, value,
COL_XMP_TYPE_XREF, property,
COL_XMP_WIDGET_XREF, NULL,
COL_XMP_EDITABLE, property->editable,
COL_XMP_EDIT_ICON, NULL,
COL_XMP_VISIBLE, TRUE,
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
COL_XMP_WEIGHT_SET, FALSE,
-1);
g_free (tmp_name);
break;
case XMP_PTYPE_ORDERED_LIST:
case XMP_PTYPE_UNORDERED_LIST:
#ifdef DEBUG_XMP_MODEL
g_print ("\t%s:%s [] =", ns_prefix, name);
for (i = 0; value[i] != NULL; i++)
if (i == 0)
g_print (" \"%s\"", value[i]);
else
g_print (", \"%s\"", value[i]);
g_print ("\n");
#endif
if (property != NULL)
/* FIXME */;
else
{
property = g_new (XMPProperty, 1);
property->name = g_strdup (name);
property->type = ((type == XMP_PTYPE_ORDERED_LIST)
? XMP_TYPE_TEXT_BAG
: XMP_TYPE_TEXT_SEQ);
property->editable = TRUE;
xmp_model->custom_properties =
g_slist_prepend (xmp_model->custom_properties, property);
}
tmp_name = g_strconcat (name, " []", NULL);
tmp_value = g_strjoinv ("; ", (gchar **) value);
gtk_tree_store_append (xmp_model->treestore, &child_iter, &iter);
gtk_tree_store_set (xmp_model->treestore, &child_iter,
COL_XMP_NAME, tmp_name,
COL_XMP_VALUE, tmp_value,
COL_XMP_VALUE_RAW, value,
COL_XMP_TYPE_XREF, property,
COL_XMP_WIDGET_XREF, NULL,
COL_XMP_EDITABLE, property->editable,
COL_XMP_EDIT_ICON, NULL,
COL_XMP_VISIBLE, TRUE,
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
COL_XMP_WEIGHT_SET, FALSE,
-1);
g_free (tmp_value);
g_free (tmp_name);
break;
case XMP_PTYPE_ALT_THUMBS:
#ifdef DEBUG_XMP_MODEL
for (i = 0; value[i] != NULL; i += 2)
g_print ("\t%s:%s [size:%d] = \"...\"\n", ns_prefix, name,
*(int *)(value[i]));
g_print ("\n");
#endif
if (property != NULL)
/* FIXME */;
else
{
property = g_new (XMPProperty, 1);
property->name = g_strdup (name);
property->type = XMP_TYPE_THUMBNAIL_ALT;
property->editable = TRUE;
xmp_model->custom_properties =
g_slist_prepend (xmp_model->custom_properties, property);
}
tmp_name = g_strconcat (name, " []", NULL);
gtk_tree_store_append (xmp_model->treestore, &child_iter, &iter);
gtk_tree_store_set (xmp_model->treestore, &child_iter,
COL_XMP_NAME, tmp_name,
COL_XMP_VALUE, "[FIXME: display thumbnails]",
COL_XMP_VALUE_RAW, value,
COL_XMP_TYPE_XREF, property,
COL_XMP_WIDGET_XREF, NULL,
COL_XMP_EDITABLE, property->editable,
COL_XMP_EDIT_ICON, NULL,
COL_XMP_VISIBLE, TRUE,
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
COL_XMP_WEIGHT_SET, FALSE,
-1);
g_free (tmp_name);
break;
case XMP_PTYPE_ALT_LANG:
#ifdef DEBUG_XMP_MODEL
for (i = 0; value[i] != NULL; i += 2)
g_print ("\t%s:%s [lang:%s] = \"%s\"\n", ns_prefix, name,
value[i], value[i + 1]);
#endif
if (property != NULL)
/* FIXME */;
else
{
property = g_new (XMPProperty, 1);
property->name = g_strdup (name);
property->type = XMP_TYPE_LANG_ALT;
property->editable = TRUE;
xmp_model->custom_properties =
g_slist_prepend (xmp_model->custom_properties, property);
}
for (i = 0; value[i] != NULL; i += 2)
{
tmp_name = g_strconcat (name, " [", value[i], "]", NULL);
gtk_tree_store_append (xmp_model->treestore, &child_iter, &iter);
gtk_tree_store_set (xmp_model->treestore, &child_iter,
COL_XMP_NAME, tmp_name,
COL_XMP_VALUE, value[i + 1],
COL_XMP_VALUE_RAW, value,
COL_XMP_TYPE_XREF, property,
COL_XMP_WIDGET_XREF, NULL,
COL_XMP_EDITABLE, property->editable,
COL_XMP_EDIT_ICON, NULL,
COL_XMP_VISIBLE, TRUE,
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
COL_XMP_WEIGHT_SET, FALSE,
-1);
g_free (tmp_name);
}
break;
case XMP_PTYPE_STRUCTURE:
#ifdef DEBUG_XMP_MODEL
for (i = 2; value[i] != NULL; i += 2)
g_print ("\t%s:%s [%s] = \"%s\"\n", ns_prefix, name,
value[i], value[i + 1]);
#endif
if (property != NULL)
/* FIXME */;
else
{
property = g_new (XMPProperty, 1);
property->name = g_strdup (name);
property->type = XMP_TYPE_GENERIC_STRUCTURE;
property->editable = TRUE;
xmp_model->custom_properties =
g_slist_prepend (xmp_model->custom_properties, property);
}
for (i = 2; value[i] != NULL; i += 2)
{
tmp_name = g_strconcat (name, " [", value[i], "]", NULL);
gtk_tree_store_append (xmp_model->treestore, &child_iter, &iter);
gtk_tree_store_set (xmp_model->treestore, &child_iter,
COL_XMP_NAME, tmp_name,
COL_XMP_VALUE, value[i + 1],
COL_XMP_VALUE_RAW, value,
COL_XMP_TYPE_XREF, property,
COL_XMP_WIDGET_XREF, NULL,
COL_XMP_EDITABLE, property->editable,
COL_XMP_EDIT_ICON, NULL,
COL_XMP_VISIBLE, TRUE,
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
COL_XMP_WEIGHT_SET, FALSE,
-1);
g_free (tmp_name);
}
break;
default:
#ifdef DEBUG_XMP_MODEL
g_print ("\t%s:%s = ?\n", ns_prefix, name);
#endif
break;
}
}
/* called by the XMP parser - parse error */
static void
parse_error (XMPParseContext *context,
GError *error,
gpointer user_data)
{
g_warning ("While parsing XMP metadata:\n%s\n", error->message);
}
static XMPParser xmp_parser = {
parse_start_schema,
parse_end_schema,
parse_set_property,
parse_error
};
/**
* xmp_model_parse_buffer:
* @xmp_model: pointer to the #XMPModel in which the results will be stored
* @buffer: buffer to be parsed
* @buffer_length: length of the @buffer
* @skip_other_data: if %TRUE, allow arbitrary data before XMP packet marker
* @error: return location for a #GError
*
* Parse a buffer containing XMP metadata and merge the parsed contents into
* the supplied @xmp_model. If @skip_other_data is %TRUE, then the parser
* will try to find the <?xpacket...?> marker in the buffer, skipping any
* unknown data found before it.
*
* Return value: %TRUE on success, %FALSE if an error was set
*
* (Note: this calls the functions from xmp_parse.c, which will call the
* functions in this file through the xmp_parser structure defined above.)
**/
gboolean
xmp_model_parse_buffer (XMPModel *xmp_model,
const gchar *buffer,
gssize buffer_length,
gboolean skip_other_data,
GError **error)
{
XMPParseFlags flags;
XMPParseContext *context;
flags = XMP_FLAG_DEFER_VALUE_FREE; /* we will free the array ourselves */
if (skip_other_data)
flags |= XMP_FLAG_FIND_XPACKET;
context = xmp_parse_context_new (&xmp_parser, flags, xmp_model, NULL);
if (! xmp_parse_context_parse (context, buffer, buffer_length, error))
{
xmp_parse_context_free (context);
return FALSE;
}
if (! xmp_parse_context_end_parse (context, error))
{
xmp_parse_context_free (context);
return FALSE;
}
xmp_parse_context_free (context);
return TRUE;
}
/**
* xmp_model_parse_file:
* @xmp_model: pointer to the #XMPModel in which the results will be stored
* @filename: name of the file containing XMP metadata to parse
* @error: return location for a #GError
*
* Try to find XMP metadata in a file and merge its contents into the supplied
* @xmp_model.
*
* Return value: %TRUE on success, %FALSE if an error was set
**/
gboolean
xmp_model_parse_file (XMPModel *xmp_model,
const gchar *filename,
GError **error)
{
gchar *buffer;
gssize buffer_length;
g_return_val_if_fail (filename != NULL, FALSE);
if (! g_file_get_contents (filename, &buffer, &buffer_length, error))
return FALSE;
if (! xmp_model_parse_buffer (xmp_model, buffer, buffer_length,
TRUE, error))
return FALSE;
g_free (buffer);
return TRUE;
}
/**
* xmp_model_get_tree_model:
* @xmp_model: pointer to an #XMPModel
*
* Return a pointer to the #GtkTreeModel contained in the #XMPModel.
**/
GtkTreeModel *
xmp_model_get_tree_model (XMPModel *xmp_model)
{
g_return_val_if_fail (xmp_model != NULL, NULL);
return GTK_TREE_MODEL (xmp_model->treestore);
}
/**
* xmp_model_get_scalar_property:
* @xmp_model: pointer to an #XMPModel
* @schema_name: full URI or usual prefix of the schema
* @property_name: name of the property to store
*
* Store a new value for the specified XMP property.
*
* Return value: string representation of the value of that property, or %NULL if the property does not exist
**/
const gchar *
xmp_model_get_scalar_property (XMPModel *xmp_model,
const gchar *schema_name,
const gchar *property_name)
{
XMPSchema *schema;
GtkTreeIter iter;
XMPProperty *property = NULL;
GtkTreeIter child_iter;
int i;
XMPProperty *property_xref;
const gchar *value;
g_return_val_if_fail (xmp_model != NULL, NULL);
g_return_val_if_fail (schema_name != NULL, NULL);
g_return_val_if_fail (property_name != NULL, NULL);
schema = find_xmp_schema (xmp_model, schema_name);
if (! schema)
schema = find_xmp_schema_prefix (xmp_model, schema_name);
if (! schema)
return NULL;
if (! find_iter_for_schema (xmp_model, schema, &iter))
return NULL;
if (schema->properties != NULL)
for (i = 0; schema->properties[i].name != NULL; ++i)
if (! strcmp (schema->properties[i].name, property_name))
{
property = &(schema->properties[i]);
break;
}
if (property == NULL)
return NULL;
if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (xmp_model->treestore),
&child_iter, &iter))
return NULL;
do
{
gtk_tree_model_get (GTK_TREE_MODEL (xmp_model->treestore), &child_iter,
COL_XMP_TYPE_XREF, &property_xref,
COL_XMP_VALUE, &value,
-1);
if (property_xref == property)
return value;
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL(xmp_model->treestore),
&child_iter));
return NULL;
}
/**
* xmp_model_set_scalar_property:
* @xmp_model: pointer to an #XMPModel
* @schema_name: full URI or usual prefix of the schema
* @property_name: name of the property to store
* @property_value: value to store
*
* Store a new value for the specified XMP property.
*
* Return value: %TRUE if the property was set, %FALSE if an error occured (for example, the @schema_name is invalid)
**/
gboolean
xmp_model_set_scalar_property (XMPModel *xmp_model,
const gchar *schema_name,
const gchar *property_name,
const gchar *property_value)
{
XMPSchema *schema;
GtkTreeIter iter;
XMPProperty *property = NULL;
GtkTreeIter child_iter;
int i;
gchar **value;
g_return_val_if_fail (xmp_model != NULL, FALSE);
g_return_val_if_fail (schema_name != NULL, FALSE);
g_return_val_if_fail (property_name != NULL, FALSE);
g_return_val_if_fail (property_value != NULL, FALSE);
schema = find_xmp_schema (xmp_model, schema_name);
if (! schema)
schema = find_xmp_schema_prefix (xmp_model, schema_name);
if (! schema)
return FALSE;
if (! find_iter_for_schema (xmp_model, schema, &iter))
add_known_schema (xmp_model, schema, &iter);
if (schema->properties != NULL)
for (i = 0; schema->properties[i].name != NULL; ++i)
if (! strcmp (schema->properties[i].name, property_name))
{
property = &(schema->properties[i]);
break;
}
if (property != NULL)
find_and_remove_property (xmp_model, property, &iter);
else
{
property = g_new (XMPProperty, 1);
property->name = g_strdup (property_name);
property->type = XMP_TYPE_TEXT;
property->editable = TRUE;
xmp_model->custom_properties =
g_slist_prepend (xmp_model->custom_properties, property);
}
value = g_new (gchar *, 2);
value[0] = g_strdup (property_value);
value[1] = NULL;
gtk_tree_store_append (xmp_model->treestore, &child_iter, &iter);
gtk_tree_store_set (xmp_model->treestore, &child_iter,
COL_XMP_NAME, g_strdup (property_name),
COL_XMP_VALUE, value[0],
COL_XMP_VALUE_RAW, value,
COL_XMP_TYPE_XREF, property,
COL_XMP_WIDGET_XREF, NULL,
COL_XMP_EDITABLE, property->editable,
COL_XMP_EDIT_ICON, NULL,
COL_XMP_VISIBLE, TRUE,
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
COL_XMP_WEIGHT_SET, FALSE,
-1);
return TRUE;
}