Commit 73da7c9a authored by Michael Natterer's avatar Michael Natterer 😴

app: add GimpDataLoaderFactory which loads data from an array of formats

specified by GimpDataLoaderEntry structs. Remove the same code from
GimpDataFactory and make it an abstract base class that only serves as
an interface for actual implementations. Also move around some stuff
in GimpDataFactory and remove virtual functions that were a bad idea
in the first place.
parent 0b4677ec
......@@ -155,6 +155,8 @@ libappcore_a_sources = \
gimpdata.h \
gimpdatafactory.c \
gimpdatafactory.h \
gimpdataloaderfactory.c \
gimpdataloaderfactory.h \
gimpdocumentlist.c \
gimpdocumentlist.h \
gimpdrawable.c \
......
......@@ -131,6 +131,7 @@ typedef struct _GimpToolInfo GimpToolInfo;
/* data objects */
typedef struct _GimpDataFactory GimpDataFactory;
typedef struct _GimpDataLoaderFactory GimpDataLoaderFactory;
typedef struct _GimpData GimpData;
typedef struct _GimpBrush GimpBrush;
typedef struct _GimpBrushCache GimpBrushCache;
......
......@@ -38,7 +38,7 @@
#include "gimpbrushclipboard.h"
#include "gimpbrushgenerated-load.h"
#include "gimpbrushpipe-load.h"
#include "gimpdatafactory.h"
#include "gimpdataloaderfactory.h"
#include "gimpdynamics.h"
#include "gimpdynamics-load.h"
#include "gimpgradient-load.h"
......@@ -62,7 +62,7 @@
void
gimp_data_factories_init (Gimp *gimp)
{
static const GimpDataFactoryLoaderEntry brush_loader_entries[] =
static const GimpDataLoaderEntry brush_loader_entries[] =
{
{ gimp_brush_load, GIMP_BRUSH_FILE_EXTENSION, FALSE },
{ gimp_brush_load, GIMP_BRUSH_PIXMAP_FILE_EXTENSION, FALSE },
......@@ -72,34 +72,34 @@ gimp_data_factories_init (Gimp *gimp)
{ gimp_brush_pipe_load, GIMP_BRUSH_PIPE_FILE_EXTENSION, FALSE }
};
static const GimpDataFactoryLoaderEntry dynamics_loader_entries[] =
static const GimpDataLoaderEntry dynamics_loader_entries[] =
{
{ gimp_dynamics_load, GIMP_DYNAMICS_FILE_EXTENSION, TRUE }
};
static const GimpDataFactoryLoaderEntry mybrush_loader_entries[] =
static const GimpDataLoaderEntry mybrush_loader_entries[] =
{
{ gimp_mybrush_load, GIMP_MYBRUSH_FILE_EXTENSION, FALSE }
};
static const GimpDataFactoryLoaderEntry pattern_loader_entries[] =
static const GimpDataLoaderEntry pattern_loader_entries[] =
{
{ gimp_pattern_load, GIMP_PATTERN_FILE_EXTENSION, FALSE },
{ gimp_pattern_load_pixbuf, NULL /* fallback loader */, FALSE }
};
static const GimpDataFactoryLoaderEntry gradient_loader_entries[] =
static const GimpDataLoaderEntry gradient_loader_entries[] =
{
{ gimp_gradient_load, GIMP_GRADIENT_FILE_EXTENSION, TRUE },
{ gimp_gradient_load_svg, GIMP_GRADIENT_SVG_FILE_EXTENSION, FALSE }
};
static const GimpDataFactoryLoaderEntry palette_loader_entries[] =
static const GimpDataLoaderEntry palette_loader_entries[] =
{
{ gimp_palette_load, GIMP_PALETTE_FILE_EXTENSION, TRUE }
};
static const GimpDataFactoryLoaderEntry tool_preset_loader_entries[] =
static const GimpDataLoaderEntry tool_preset_loader_entries[] =
{
{ gimp_tool_preset_load, GIMP_TOOL_PRESET_FILE_EXTENSION, TRUE }
};
......@@ -107,68 +107,74 @@ gimp_data_factories_init (Gimp *gimp)
g_return_if_fail (GIMP_IS_GIMP (gimp));
gimp->brush_factory =
gimp_data_factory_new (gimp,
GIMP_TYPE_BRUSH,
"brush-path", "brush-path-writable",
brush_loader_entries,
G_N_ELEMENTS (brush_loader_entries),
gimp_brush_new,
gimp_brush_get_standard);
gimp_data_loader_factory_new (gimp,
GIMP_TYPE_BRUSH,
"brush-path",
"brush-path-writable",
gimp_brush_new,
gimp_brush_get_standard,
brush_loader_entries,
G_N_ELEMENTS (brush_loader_entries));
gimp_object_set_static_name (GIMP_OBJECT (gimp->brush_factory),
"brush factory");
gimp->dynamics_factory =
gimp_data_factory_new (gimp,
GIMP_TYPE_DYNAMICS,
"dynamics-path", "dynamics-path-writable",
dynamics_loader_entries,
G_N_ELEMENTS (dynamics_loader_entries),
gimp_dynamics_new,
gimp_dynamics_get_standard);
gimp_data_loader_factory_new (gimp,
GIMP_TYPE_DYNAMICS,
"dynamics-path",
"dynamics-path-writable",
gimp_dynamics_new,
gimp_dynamics_get_standard,
dynamics_loader_entries,
G_N_ELEMENTS (dynamics_loader_entries));
gimp_object_set_static_name (GIMP_OBJECT (gimp->dynamics_factory),
"dynamics factory");
gimp->mybrush_factory =
gimp_data_factory_new (gimp,
GIMP_TYPE_MYBRUSH,
"mypaint-brush-path", "mypaint-brush-path-writable",
mybrush_loader_entries,
G_N_ELEMENTS (mybrush_loader_entries),
NULL,
NULL);
gimp_data_loader_factory_new (gimp,
GIMP_TYPE_MYBRUSH,
"mypaint-brush-path",
"mypaint-brush-path-writable",
NULL,
NULL,
mybrush_loader_entries,
G_N_ELEMENTS (mybrush_loader_entries));
gimp_object_set_static_name (GIMP_OBJECT (gimp->mybrush_factory),
"mypaint brush factory");
gimp->pattern_factory =
gimp_data_factory_new (gimp,
GIMP_TYPE_PATTERN,
"pattern-path", "pattern-path-writable",
pattern_loader_entries,
G_N_ELEMENTS (pattern_loader_entries),
NULL,
gimp_pattern_get_standard);
gimp_data_loader_factory_new (gimp,
GIMP_TYPE_PATTERN,
"pattern-path",
"pattern-path-writable",
NULL,
gimp_pattern_get_standard,
pattern_loader_entries,
G_N_ELEMENTS (pattern_loader_entries));
gimp_object_set_static_name (GIMP_OBJECT (gimp->pattern_factory),
"pattern factory");
gimp->gradient_factory =
gimp_data_factory_new (gimp,
GIMP_TYPE_GRADIENT,
"gradient-path", "gradient-path-writable",
gradient_loader_entries,
G_N_ELEMENTS (gradient_loader_entries),
gimp_gradient_new,
gimp_gradient_get_standard);
gimp_data_loader_factory_new (gimp,
GIMP_TYPE_GRADIENT,
"gradient-path",
"gradient-path-writable",
gimp_gradient_new,
gimp_gradient_get_standard,
gradient_loader_entries,
G_N_ELEMENTS (gradient_loader_entries));
gimp_object_set_static_name (GIMP_OBJECT (gimp->gradient_factory),
"gradient factory");
gimp->palette_factory =
gimp_data_factory_new (gimp,
GIMP_TYPE_PALETTE,
"palette-path", "palette-path-writable",
palette_loader_entries,
G_N_ELEMENTS (palette_loader_entries),
gimp_palette_new,
gimp_palette_get_standard);
gimp_data_loader_factory_new (gimp,
GIMP_TYPE_PALETTE,
"palette-path",
"palette-path-writable",
gimp_palette_new,
gimp_palette_get_standard,
palette_loader_entries,
G_N_ELEMENTS (palette_loader_entries));
gimp_object_set_static_name (GIMP_OBJECT (gimp->palette_factory),
"palette factory");
......@@ -179,13 +185,14 @@ gimp_data_factories_init (Gimp *gimp)
"font factory");
gimp->tool_preset_factory =
gimp_data_factory_new (gimp,
GIMP_TYPE_TOOL_PRESET,
"tool-preset-path", "tool-preset-path-writable",
tool_preset_loader_entries,
G_N_ELEMENTS (tool_preset_loader_entries),
gimp_tool_preset_new,
NULL);
gimp_data_loader_factory_new (gimp,
GIMP_TYPE_TOOL_PRESET,
"tool-preset-path",
"tool-preset-path-writable",
gimp_tool_preset_new,
NULL,
tool_preset_loader_entries,
G_N_ELEMENTS (tool_preset_loader_entries));
gimp_object_set_static_name (GIMP_OBJECT (gimp->tool_preset_factory),
"tool preset factory");
......
......@@ -31,7 +31,7 @@
#include "gimp-gradients.h"
#include "gimp-internal-data.h"
#include "gimpdata.h"
#include "gimpdatafactory.h"
#include "gimpdataloaderfactory.h"
#include "gimperror.h"
#include "gimpgradient-load.h"
......
This diff is collapsed.
......@@ -25,27 +25,13 @@
#include "gimpobject.h"
typedef void (* GimpDataForeachFunc) (GimpDataFactory *factory,
GimpData *data,
gpointer user_data);
typedef GimpData * (* GimpDataNewFunc) (GimpContext *context,
const gchar *name);
typedef GList * (* GimpDataLoadFunc) (GimpContext *context,
GFile *file,
GInputStream *input,
GError **error);
typedef GimpData * (* GimpDataGetStandardFunc) (GimpContext *context);
typedef struct _GimpDataFactoryLoaderEntry GimpDataFactoryLoaderEntry;
struct _GimpDataFactoryLoaderEntry
{
GimpDataLoadFunc load_func;
const gchar *extension;
gboolean writable;
};
typedef void (* GimpDataForeachFunc) (GimpDataFactory *factory,
GimpData *data,
gpointer user_data);
#define GIMP_TYPE_DATA_FACTORY (gimp_data_factory_get_type ())
......@@ -75,11 +61,7 @@ struct _GimpDataFactoryClass
void (* data_refresh) (GimpDataFactory *factory,
GimpContext *context);
void (* data_save) (GimpDataFactory *factory);
void (* data_free) (GimpDataFactory *factory);
GimpData * (* data_new) (GimpDataFactory *factory,
GimpContext *context,
const gchar *name);
GimpData * (* data_duplicate) (GimpDataFactory *factory,
GimpData *data);
gboolean (* data_delete) (GimpDataFactory *factory,
......@@ -89,16 +71,7 @@ struct _GimpDataFactoryClass
};
GType gimp_data_factory_get_type (void) G_GNUC_CONST;
GimpDataFactory * gimp_data_factory_new (Gimp *gimp,
GType data_type,
const gchar *path_property_name,
const gchar *writable_property_name,
const GimpDataFactoryLoaderEntry *loader_entries,
gint n_loader_entries,
GimpDataNewFunc new_func,
GimpDataGetStandardFunc get_standard_func);
GType gimp_data_factory_get_type (void) G_GNUC_CONST;
void gimp_data_factory_data_init (GimpDataFactory *factory,
GimpContext *context,
......@@ -112,17 +85,20 @@ void gimp_data_factory_data_free (GimpDataFactory *factory);
GimpAsyncSet * gimp_data_factory_get_async_set (GimpDataFactory *factory);
gboolean gimp_data_factory_data_wait (GimpDataFactory *factory);
gboolean gimp_data_factory_has_data_new_func (GimpDataFactory *factory);
GimpData * gimp_data_factory_data_new (GimpDataFactory *factory,
GimpContext *context,
const gchar *name);
GimpData * gimp_data_factory_data_get_standard (GimpDataFactory *factory,
GimpContext *context);
GimpData * gimp_data_factory_data_duplicate (GimpDataFactory *factory,
GimpData *data);
gboolean gimp_data_factory_data_delete (GimpDataFactory *factory,
GimpData *data,
gboolean delete_from_disk,
GError **error);
GimpData * gimp_data_factory_data_get_standard (GimpDataFactory *factory,
GimpContext *context);
gboolean gimp_data_factory_data_save_single (GimpDataFactory *factory,
GimpData *data,
GError **error);
......@@ -132,16 +108,15 @@ void gimp_data_factory_data_foreach (GimpDataFactory *factory,
GimpDataForeachFunc callback,
gpointer user_data);
Gimp * gimp_data_factory_get_gimp (GimpDataFactory *factory);
GType gimp_data_factory_get_data_type (GimpDataFactory *factory);
GimpContainer * gimp_data_factory_get_container (GimpDataFactory *factory);
GimpContainer * gimp_data_factory_get_container_obsolete
(GimpDataFactory *factory);
Gimp * gimp_data_factory_get_gimp (GimpDataFactory *factory);
gboolean gimp_data_factory_has_data_new_func (GimpDataFactory *factory);
GList * gimp_data_factory_get_data_path (GimpDataFactory *factory);
GList * gimp_data_factory_get_data_path_writable
(GimpDataFactory *factory);
#endif /* __GIMP_DATA_FACTORY_H__ */
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpdataloaderfactory.c
* Copyright (C) 2001-2018 Michael Natterer <mitch@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/>.
*/
#include "config.h"
#include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libgimpbase/gimpbase.h"
#include "core-types.h"
#include "gimp.h"
#include "gimp-utils.h"
#include "gimpcontainer.h"
#include "gimpdata.h"
#include "gimpdataloaderfactory.h"
#include "gimp-intl.h"
/* Data files that have this string in their path are considered
* obsolete and are only kept around for backwards compatibility
*/
#define GIMP_OBSOLETE_DATA_DIR_NAME "gimp-obsolete-files"
struct _GimpDataLoaderFactoryPrivate
{
const GimpDataLoaderEntry *loader_entries;
gint n_loader_entries;
};
#define GET_PRIVATE(obj) (((GimpDataLoaderFactory *) (obj))->priv)
static void gimp_data_loader_factory_data_init (GimpDataFactory *factory,
GimpContext *context);
static void gimp_data_loader_factory_data_refresh (GimpDataFactory *factory,
GimpContext *context);
static void gimp_data_loader_factory_load (GimpDataFactory *factory,
GimpContext *context,
GHashTable *cache);
static void gimp_data_loader_factory_load_directory (GimpDataFactory *factory,
GimpContext *context,
GHashTable *cache,
gboolean dir_writable,
GFile *directory,
GFile *top_directory);
static void gimp_data_loader_factory_load_data (GimpDataFactory *factory,
GimpContext *context,
GHashTable *cache,
gboolean dir_writable,
GFile *file,
GFileInfo *info,
GFile *top_directory);
G_DEFINE_TYPE (GimpDataLoaderFactory, gimp_data_loader_factory,
GIMP_TYPE_DATA_FACTORY)
#define parent_class gimp_data_loader_factory_parent_class
static void
gimp_data_loader_factory_class_init (GimpDataLoaderFactoryClass *klass)
{
GimpDataFactoryClass *factory_class = GIMP_DATA_FACTORY_CLASS (klass);
factory_class->data_init = gimp_data_loader_factory_data_init;
factory_class->data_refresh = gimp_data_loader_factory_data_refresh;
g_type_class_add_private (klass, sizeof (GimpDataLoaderFactoryPrivate));
}
static void
gimp_data_loader_factory_init (GimpDataLoaderFactory *factory)
{
factory->priv = G_TYPE_INSTANCE_GET_PRIVATE (factory,
GIMP_TYPE_DATA_LOADER_FACTORY,
GimpDataLoaderFactoryPrivate);
}
static void
gimp_data_loader_factory_data_init (GimpDataFactory *factory,
GimpContext *context)
{
gimp_data_loader_factory_load (factory, context, NULL);
}
static void
gimp_data_loader_factory_refresh_cache_add (GimpDataFactory *factory,
GimpData *data,
gpointer user_data)
{
GFile *file = gimp_data_get_file (data);
if (file)
{
GimpContainer *container = gimp_data_factory_get_container (factory);
GHashTable *cache = user_data;
GList *list;
g_object_ref (data);
gimp_container_remove (container, GIMP_OBJECT (data));
list = g_hash_table_lookup (cache, file);
list = g_list_prepend (list, data);
g_hash_table_insert (cache, file, list);
}
}
static gboolean
gimp_data_loader_factory_refresh_cache_remove (gpointer key,
gpointer value,
gpointer user_data)
{
GList *list;
for (list = value; list; list = list->next)
g_object_unref (list->data);
g_list_free (value);
return TRUE;
}
static void
gimp_data_loader_factory_data_refresh (GimpDataFactory *factory,
GimpContext *context)
{
GimpContainer *container = gimp_data_factory_get_container (factory);
GHashTable *cache;
gimp_container_freeze (container);
/* First, save all dirty data objects */
gimp_data_factory_data_save (factory);
cache = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal);
gimp_data_factory_data_foreach (factory, TRUE,
gimp_data_loader_factory_refresh_cache_add,
cache);
/* Now the cache contains a GFile => list-of-objects mapping of
* the old objects. So we should now traverse the directory and for
* each file load it only if its mtime is newer.
*
* Once a file was added, it is removed from the cache, so the only
* objects remaining there will be those that are not present on
* the disk (that have to be destroyed)
*/
gimp_data_loader_factory_load (factory, context, cache);
/* Now all the data is loaded. Free what remains in the cache */
g_hash_table_foreach_remove (cache,
gimp_data_loader_factory_refresh_cache_remove,
NULL);
g_hash_table_destroy (cache);
gimp_container_thaw (container);
}
/* public functions */
GimpDataFactory *
gimp_data_loader_factory_new (Gimp *gimp,
GType data_type,
const gchar *path_property_name,
const gchar *writable_property_name,
GimpDataNewFunc new_func,
GimpDataGetStandardFunc get_standard_func,
const GimpDataLoaderEntry *loader_entries,
gint n_loader_entries)
{
GimpDataLoaderFactory *factory;
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
g_return_val_if_fail (g_type_is_a (data_type, GIMP_TYPE_DATA), NULL);
g_return_val_if_fail (path_property_name != NULL, NULL);
g_return_val_if_fail (writable_property_name != NULL, NULL);
g_return_val_if_fail (loader_entries != NULL, NULL);
g_return_val_if_fail (n_loader_entries > 0, NULL);
factory = g_object_new (GIMP_TYPE_DATA_LOADER_FACTORY,
"gimp", gimp,
"data-type", data_type,
"path-property-name", path_property_name,
"writable-property-name", writable_property_name,
"new-func", new_func,
"get-standard-func", get_standard_func,
NULL);
factory->priv->loader_entries = loader_entries;
factory->priv->n_loader_entries = n_loader_entries;
return GIMP_DATA_FACTORY (factory);
}
/* private functions */
static void
gimp_data_loader_factory_load (GimpDataFactory *factory,
GimpContext *context,
GHashTable *cache)
{
GList *path;
GList *writable_path;
GList *list;
path = gimp_data_factory_get_data_path (factory);
writable_path = gimp_data_factory_get_data_path_writable (factory);
for (list = path; list; list = g_list_next (list))
{
gboolean dir_writable = FALSE;
if (g_list_find_custom (writable_path, list->data,
(GCompareFunc) gimp_file_compare))
dir_writable = TRUE;
gimp_data_loader_factory_load_directory (factory, context, cache,
dir_writable,
list->data,
list->data);
}
g_list_free_full (path, (GDestroyNotify) g_object_unref);
g_list_free_full (writable_path, (GDestroyNotify) g_object_unref);
}
static void
gimp_data_loader_factory_load_directory (GimpDataFactory *factory,
GimpContext *context,
GHashTable *cache,
gboolean dir_writable,
GFile *directory,
GFile *top_directory)
{
GFileEnumerator *enumerator;
enumerator = g_file_enumerate_children (directory,
G_FILE_ATTRIBUTE_STANDARD_NAME ","
G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
G_FILE_ATTRIBUTE_STANDARD_TYPE ","
G_FILE_ATTRIBUTE_TIME_MODIFIED,
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
if (enumerator)
{
GFileInfo *info;
while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
{
GFileType file_type;
GFile *child;
if (g_file_info_get_is_hidden (info))
{
g_object_unref (info);
continue;
}
file_type = g_file_info_get_file_type (info);
child = g_file_enumerator_get_child (enumerator, info);
if (file_type == G_FILE_TYPE_DIRECTORY)
{
gimp_data_loader_factory_load_directory (factory, context, cache,
dir_writable,
child,
top_directory);
}
else if (file_type == G_FILE_TYPE_REGULAR)
{
gimp_data_loader_factory_load_data (factory, context, cache,
dir_writable,
child, info,
top_directory);
}
g_object_unref (child);
g_object_unref (info);
}
g_object_unref (enumerator);
}
}
static void
gimp_data_loader_factory_load_data (GimpDataFactory *factory,
GimpContext *context,
GHashTable *cache,
gboolean dir_writable,
GFile *file,
GFileInfo *info,
GFile *top_directory)
{
GimpDataLoaderFactoryPrivate *priv = GET_PRIVATE (factory);
const GimpDataLoaderEntry *loader = NULL;
GimpContainer *container;
GimpContainer *container_obsolete;
GList *data_list = NULL;
GInputStream *input;
guint64 mtime;
gint i;
GError *error = NULL;
for (i = 0; i < priv->n_loader_entries; i++)
{
loader = &priv->loader_entries[i];
/* a loder matches if its extension matches, or if it doesn't
* have an extension, which is the case for the fallback loader,
* which must be last in the loader array
*/
if (! loader->extension ||
gimp_file_has_extension (file, loader->extension))
{
goto insert;
}
}
return;
insert:
container = gimp_data_factory_get_container (factory);
container_obsolete = gimp_data_factory_get_container_obsolete (factory);
mtime = g_file_info_get_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_MODIFIED);
if (cache)
{
GList *cached_data = g_hash_table_lookup (cache, file);
if (cached_data &&
gimp_data_get_mtime (cached_data->data) != 0 &&
gimp_data_get_mtime (cached_data->data) == mtime)
{
GList *list;
for (list = cached_data; list; list = g_list_next (list))
gimp_container_add (container, list->data);
return;
}
}
input = G_INPUT_STREAM (g_file_read (file, NULL, &error));
if (input)
{
GInputStream *buffered = g_buffered_input_stream_new (input);
data_list = loader->load_func (context, file, buffered, &error);
if (error)
{
g_prefix_error (&error,
_("Error loading '%s': "),
gimp_file_get_utf8_name (file));
}
else if (! data_list)
{
g_set_error (&error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Error loading '%s'"),
gimp_file_get_utf8_name (file));
}
g_object_unref (buffered);
g_object_unref (input);
}
else
{
g_prefix_error (&error,
_("Could not open '%s' for reading: "),
gimp_file_get_utf8_name (file));
}
if (G_LIKELY (data_list))
{
GList *list;
gchar *uri;