mirror of https://github.com/GNOME/gimp.git
app: fix gimp_data_get_identifier() and add a concept of data collection.
The way we were creating a GimpData identifier was simply wrong, because it was based on the assumption that the source file uniquely identifies a GimpData (cf. gimp_tagged_get_identifier() which clearly says that the returned string must uniquely identify this data). The very simple counter-examples for why this is a mistake to consider a data file to be a good unique identifier are collection files. For instance, TTC files (TrueType Collection) contain multiple fonts. Instead I am adding the concept of "collection" with the assumption that **within a given collection**, data names are unique (I do hope this to be and stay true). So I add gimp_data_get_identifiers() and gimp_data_identify() to get identifiers and check for identity. The collection will use the old implementation of gimp_data_get_identifier() because it is quite nice to have paths relative to data and config directories (it allows some cases of data relocation without losing data identification). The new implementation to compute a GimpTagged identifier on the other hand will construct a string from the quality of being internal or not, the data name and its collection.
This commit is contained in:
parent
41ed091879
commit
1a9c470b82
|
@ -65,10 +65,10 @@ struct _GimpDataPrivate
|
||||||
gint freeze_count;
|
gint freeze_count;
|
||||||
gint64 mtime;
|
gint64 mtime;
|
||||||
|
|
||||||
/* Identifies the GimpData object across sessions. Used when there
|
/* Identifies the collection this GimpData belongs to.
|
||||||
* is not a filename associated with the object.
|
* Used when there is not a filename associated with the object.
|
||||||
*/
|
*/
|
||||||
gchar *identifier;
|
gchar *collection;
|
||||||
|
|
||||||
GList *tags;
|
GList *tags;
|
||||||
};
|
};
|
||||||
|
@ -108,6 +108,8 @@ static GList * gimp_data_get_tags (GimpTagged *tagged);
|
||||||
static gchar * gimp_data_get_identifier (GimpTagged *tagged);
|
static gchar * gimp_data_get_identifier (GimpTagged *tagged);
|
||||||
static gchar * gimp_data_get_checksum (GimpTagged *tagged);
|
static gchar * gimp_data_get_checksum (GimpTagged *tagged);
|
||||||
|
|
||||||
|
static gchar * gimp_data_get_collection (GimpData *data);
|
||||||
|
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_CODE (GimpData, gimp_data, GIMP_TYPE_RESOURCE,
|
G_DEFINE_TYPE_WITH_CODE (GimpData, gimp_data, GIMP_TYPE_RESOURCE,
|
||||||
G_ADD_PRIVATE (GimpData)
|
G_ADD_PRIVATE (GimpData)
|
||||||
|
@ -239,7 +241,7 @@ gimp_data_finalize (GObject *object)
|
||||||
private->tags = NULL;
|
private->tags = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_clear_pointer (&private->identifier, g_free);
|
g_clear_pointer (&private->collection, g_free);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
@ -391,6 +393,9 @@ gimp_data_real_compare (GimpData *data1,
|
||||||
if (private1->deletable != private2->deletable)
|
if (private1->deletable != private2->deletable)
|
||||||
return private1->deletable ? -1 : 1;
|
return private1->deletable ? -1 : 1;
|
||||||
|
|
||||||
|
if (g_strcmp0 (private1->collection, private2->collection) != 0)
|
||||||
|
return g_strcmp0 (private1->collection, private2->collection);
|
||||||
|
|
||||||
return gimp_object_name_collate ((GimpObject *) data1,
|
return gimp_object_name_collate ((GimpObject *) data1,
|
||||||
(GimpObject *) data2);
|
(GimpObject *) data2);
|
||||||
}
|
}
|
||||||
|
@ -448,8 +453,53 @@ gimp_data_get_tags (GimpTagged *tagged)
|
||||||
static gchar *
|
static gchar *
|
||||||
gimp_data_get_identifier (GimpTagged *tagged)
|
gimp_data_get_identifier (GimpTagged *tagged)
|
||||||
{
|
{
|
||||||
GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (tagged);
|
GimpData *data = GIMP_DATA (tagged);
|
||||||
|
GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (data);
|
||||||
gchar *identifier = NULL;
|
gchar *identifier = NULL;
|
||||||
|
gchar *collection = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (private->internal || private->file != NULL, NULL);
|
||||||
|
|
||||||
|
collection = gimp_data_get_collection (data);
|
||||||
|
/* The identifier is guaranteed to be unique because we use 2 directory
|
||||||
|
* separators between the collection and the data name. Since the collection
|
||||||
|
* is either a controlled internal name or built from g_file_get_path(), which
|
||||||
|
* is guaranteed to be a canonical path, we know it won't contain 2
|
||||||
|
* separators. Therefore it should be impossible to construct a file path able
|
||||||
|
* to create duplicate identifiers.
|
||||||
|
* The last point is obviously that it should not be possible to have
|
||||||
|
* duplicate data names in a single collection. So every identifier should be
|
||||||
|
* unique.
|
||||||
|
*/
|
||||||
|
identifier = g_strdup_printf ("%s:%s%s%s%s",
|
||||||
|
private->internal ? "internal" : "external",
|
||||||
|
collection, G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S,
|
||||||
|
gimp_object_get_name (GIMP_OBJECT (data)));
|
||||||
|
g_free (collection);
|
||||||
|
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
gimp_data_get_checksum (GimpTagged *tagged)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A data collection name is either generated from the file path, or set when
|
||||||
|
* marking a data as internal.
|
||||||
|
* Several data objects may belong to a same collection. A very common example
|
||||||
|
* of this in fonts are collections of fonts (e.g. TrueType Collection .TTC
|
||||||
|
* files).
|
||||||
|
*/
|
||||||
|
static gchar *
|
||||||
|
gimp_data_get_collection (GimpData *data)
|
||||||
|
{
|
||||||
|
GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (data);
|
||||||
|
gchar *collection = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (private->internal || private->file != NULL, NULL);
|
||||||
|
|
||||||
if (private->file)
|
if (private->file)
|
||||||
{
|
{
|
||||||
|
@ -463,7 +513,7 @@ gimp_data_get_identifier (GimpTagged *tagged)
|
||||||
tmp = g_strconcat ("${gimp_data_dir}",
|
tmp = g_strconcat ("${gimp_data_dir}",
|
||||||
path + strlen (data_dir),
|
path + strlen (data_dir),
|
||||||
NULL);
|
NULL);
|
||||||
identifier = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
|
collection = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
|
||||||
g_free (tmp);
|
g_free (tmp);
|
||||||
}
|
}
|
||||||
else if (g_str_has_prefix (path, gimp_dir))
|
else if (g_str_has_prefix (path, gimp_dir))
|
||||||
|
@ -471,7 +521,7 @@ gimp_data_get_identifier (GimpTagged *tagged)
|
||||||
tmp = g_strconcat ("${gimp_dir}",
|
tmp = g_strconcat ("${gimp_dir}",
|
||||||
path + strlen (gimp_dir),
|
path + strlen (gimp_dir),
|
||||||
NULL);
|
NULL);
|
||||||
identifier = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
|
collection = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
|
||||||
g_free (tmp);
|
g_free (tmp);
|
||||||
}
|
}
|
||||||
else if (g_str_has_prefix (path, MYPAINT_BRUSHES_DIR))
|
else if (g_str_has_prefix (path, MYPAINT_BRUSHES_DIR))
|
||||||
|
@ -479,36 +529,30 @@ gimp_data_get_identifier (GimpTagged *tagged)
|
||||||
tmp = g_strconcat ("${mypaint_brushes_dir}",
|
tmp = g_strconcat ("${mypaint_brushes_dir}",
|
||||||
path + strlen (MYPAINT_BRUSHES_DIR),
|
path + strlen (MYPAINT_BRUSHES_DIR),
|
||||||
NULL);
|
NULL);
|
||||||
identifier = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
|
collection = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
|
||||||
g_free (tmp);
|
g_free (tmp);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
identifier = g_filename_to_utf8 (path, -1,
|
collection = g_filename_to_utf8 (path, -1,
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! identifier)
|
if (! collection)
|
||||||
{
|
{
|
||||||
g_printerr ("%s: failed to convert '%s' to utf8.\n",
|
g_printerr ("%s: failed to convert '%s' to utf8.\n",
|
||||||
G_STRFUNC, path);
|
G_STRFUNC, path);
|
||||||
identifier = g_strdup (path);
|
collection = g_strdup (path);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free (path);
|
g_free (path);
|
||||||
}
|
}
|
||||||
else if (private->internal)
|
else if (private->internal)
|
||||||
{
|
{
|
||||||
identifier = g_strdup (private->identifier);
|
collection = g_strdup (private->collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
return identifier;
|
return collection;
|
||||||
}
|
|
||||||
|
|
||||||
static gchar *
|
|
||||||
gimp_data_get_checksum (GimpTagged *tagged)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1197,19 +1241,20 @@ gimp_data_duplicate (GimpData *data)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gimp_data_make_internal:
|
* gimp_data_make_internal:
|
||||||
* @data: a #GimpData object.
|
* @data: a #GimpData object.
|
||||||
|
* @collection: internal collection title @data belongs to.
|
||||||
*
|
*
|
||||||
* Mark @data as "internal" to Gimp, which means that it will not be
|
* Mark @data as "internal" to Gimp, which means that it will not be
|
||||||
* saved to disk. Note that if you do this, later calls to
|
* saved to disk. Note that if you do this, later calls to
|
||||||
* gimp_data_save() and gimp_data_delete_from_disk() will
|
* gimp_data_save() and gimp_data_delete_from_disk() will
|
||||||
* automatically return successfully without giving any warning.
|
* automatically return successfully without giving any warning.
|
||||||
*
|
*
|
||||||
* The identifier name shall be an untranslated globally unique string
|
* The @collection shall be an untranslated globally unique string
|
||||||
* that identifies the internal object across sessions.
|
* that identifies the internal object collection across sessions.
|
||||||
**/
|
**/
|
||||||
void
|
void
|
||||||
gimp_data_make_internal (GimpData *data,
|
gimp_data_make_internal (GimpData *data,
|
||||||
const gchar *identifier)
|
const gchar *collection)
|
||||||
{
|
{
|
||||||
GimpDataPrivate *private;
|
GimpDataPrivate *private;
|
||||||
|
|
||||||
|
@ -1219,8 +1264,8 @@ gimp_data_make_internal (GimpData *data,
|
||||||
|
|
||||||
g_clear_object (&private->file);
|
g_clear_object (&private->file);
|
||||||
|
|
||||||
g_free (private->identifier);
|
g_free (private->collection);
|
||||||
private->identifier = g_strdup (identifier);
|
private->collection = g_strdup (collection);
|
||||||
|
|
||||||
private->writable = FALSE;
|
private->writable = FALSE;
|
||||||
private->deletable = FALSE;
|
private->deletable = FALSE;
|
||||||
|
@ -1265,6 +1310,67 @@ gimp_data_compare (GimpData *data1,
|
||||||
return GIMP_DATA_GET_CLASS (data1)->compare (data1, data2);
|
return GIMP_DATA_GET_CLASS (data1)->compare (data1, data2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gimp_data_identify:
|
||||||
|
* @data: a #GimpData object.
|
||||||
|
* @name: name of the #GimpData object.
|
||||||
|
* @collection: text uniquely identifying the collection @data belongs to.
|
||||||
|
* @is_internal: whether this is internal data.
|
||||||
|
*
|
||||||
|
* Determine whether (@name, @collection, @is_internal) uniquely identify @data.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the triplet identifies @data, %FALSE otherwise.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
gimp_data_identify (GimpData *data,
|
||||||
|
const gchar *name,
|
||||||
|
const gchar *collection,
|
||||||
|
gboolean is_internal)
|
||||||
|
{
|
||||||
|
gchar *current_collection = gimp_data_get_collection (data);
|
||||||
|
gboolean identified;
|
||||||
|
|
||||||
|
identified = (is_internal == gimp_data_is_internal (data) &&
|
||||||
|
g_strcmp0 (collection, current_collection) == 0 &&
|
||||||
|
g_strcmp0 (name, gimp_object_get_name (GIMP_OBJECT (data))) == 0);
|
||||||
|
|
||||||
|
g_free (current_collection);
|
||||||
|
|
||||||
|
return identified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gimp_data_get_identifiers:
|
||||||
|
* @data: a #GimpData object.
|
||||||
|
* @name: name of the #GimpData object.
|
||||||
|
* @collection: text uniquely identifying the collection @data belongs to.
|
||||||
|
* @is_internal: whether this is internal data.
|
||||||
|
*
|
||||||
|
* Generates a triplet of identifiers which, together, should uniquely identify
|
||||||
|
* this @data.
|
||||||
|
* @name will be the same value as gimp_object_get_name() and @is_internal the
|
||||||
|
* same value as returned by gimp_data_is_internal(), except that it is not
|
||||||
|
* enough because two data from different sources may end up having the same
|
||||||
|
* name. Nevertheless all data names within a single collection of data are
|
||||||
|
* unique. @collection therefore identifies the source collection. And these 3
|
||||||
|
* identifiers together are enough to identify a GimpData.
|
||||||
|
*
|
||||||
|
* Internally the collection will likely be a single file name, therefore
|
||||||
|
* @collection will be constructed from the file name (if it exists, or an
|
||||||
|
* opaque identifier string otherwise, for internal data). Nevertheless you
|
||||||
|
* should not take this for granted and should always use this string as an
|
||||||
|
* opaque identifier only to be reused in gimp_data_identify().
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
gimp_data_get_identifiers (GimpData *data,
|
||||||
|
gchar **name,
|
||||||
|
gchar **collection,
|
||||||
|
gboolean *is_internal)
|
||||||
|
{
|
||||||
|
*collection = gimp_data_get_collection (data);
|
||||||
|
*name = g_strdup (gimp_object_get_name (GIMP_OBJECT (data)));
|
||||||
|
*is_internal = gimp_data_is_internal (data);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* gimp_data_error_quark:
|
* gimp_data_error_quark:
|
||||||
*
|
*
|
||||||
|
|
|
@ -122,11 +122,19 @@ gboolean gimp_data_is_duplicatable (GimpData *data);
|
||||||
GimpData * gimp_data_duplicate (GimpData *data);
|
GimpData * gimp_data_duplicate (GimpData *data);
|
||||||
|
|
||||||
void gimp_data_make_internal (GimpData *data,
|
void gimp_data_make_internal (GimpData *data,
|
||||||
const gchar *identifier);
|
const gchar *collection);
|
||||||
gboolean gimp_data_is_internal (GimpData *data);
|
gboolean gimp_data_is_internal (GimpData *data);
|
||||||
|
|
||||||
gint gimp_data_compare (GimpData *data1,
|
gint gimp_data_compare (GimpData *data1,
|
||||||
GimpData *data2);
|
GimpData *data2);
|
||||||
|
gboolean gimp_data_identify (GimpData *data,
|
||||||
|
const gchar *name,
|
||||||
|
const gchar *collection_id,
|
||||||
|
gboolean is_internal);
|
||||||
|
void gimp_data_get_identifiers (GimpData *data,
|
||||||
|
gchar **name,
|
||||||
|
gchar **collection_id,
|
||||||
|
gboolean *is_internal);
|
||||||
|
|
||||||
#define GIMP_DATA_ERROR (gimp_data_error_quark ())
|
#define GIMP_DATA_ERROR (gimp_data_error_quark ())
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue