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



commit 73da7c9a546e91631f46ce447de5e5610d0b8bf8
Author: Michael Natterer <mitch gimp org>
Date:   Mon Jun 4 13:12:14 2018 +0200

    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.

 app/core/Makefile.am             |   2 +
 app/core/core-types.h            |   1 +
 app/core/gimp-data-factories.c   | 121 ++++----
 app/core/gimp-internal-data.c    |   2 +-
 app/core/gimpdatafactory.c       | 598 +++++++--------------------------------
 app/core/gimpdatafactory.h       |  47 +--
 app/core/gimpdataloaderfactory.c | 467 ++++++++++++++++++++++++++++++
 app/core/gimpdataloaderfactory.h |  80 ++++++
 app/text/gimpfontfactory.c       |  15 +-
 po/POTFILES.in                   |   1 +
 10 files changed, 730 insertions(+), 604 deletions(-)
---
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index 54a6b92774..31392be3a2 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -155,6 +155,8 @@ libappcore_a_sources = \
        gimpdata.h                              \
        gimpdatafactory.c                       \
        gimpdatafactory.h                       \
+       gimpdataloaderfactory.c                 \
+       gimpdataloaderfactory.h                 \
        gimpdocumentlist.c                      \
        gimpdocumentlist.h                      \
        gimpdrawable.c                          \
diff --git a/app/core/core-types.h b/app/core/core-types.h
index fd16dfb0ec..a9709f53fa 100644
--- a/app/core/core-types.h
+++ b/app/core/core-types.h
@@ -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;
diff --git a/app/core/gimp-data-factories.c b/app/core/gimp-data-factories.c
index 5add6ea8d8..f952b2fd59 100644
--- a/app/core/gimp-data-factories.c
+++ b/app/core/gimp-data-factories.c
@@ -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");
 
diff --git a/app/core/gimp-internal-data.c b/app/core/gimp-internal-data.c
index 9ebd9b44f5..094eeb738c 100644
--- a/app/core/gimp-internal-data.c
+++ b/app/core/gimp-internal-data.c
@@ -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"
 
diff --git a/app/core/gimpdatafactory.c b/app/core/gimpdatafactory.c
index 900c4a900e..4df5b9dd2f 100644
--- a/app/core/gimpdatafactory.c
+++ b/app/core/gimpdatafactory.c
@@ -20,9 +20,6 @@
 
 #include "config.h"
 
-#include <stdlib.h>
-#include <string.h>
-
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gegl.h>
 
@@ -46,12 +43,6 @@
 #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"
-
-
 enum
 {
   PROP_0,
@@ -66,22 +57,19 @@ enum
 
 struct _GimpDataFactoryPrivate
 {
-  Gimp                             *gimp;
-
-  GType                             data_type;
-  GimpContainer                    *container;
-  GimpContainer                    *container_obsolete;
+  Gimp                    *gimp;
 
-  gchar                            *path_property_name;
-  gchar                            *writable_property_name;
+  GType                    data_type;
+  GimpContainer           *container;
+  GimpContainer           *container_obsolete;
 
-  GimpDataNewFunc                   data_new_func;
-  GimpDataGetStandardFunc           data_get_standard_func;
+  gchar                   *path_property_name;
+  gchar                   *writable_property_name;
 
-  GimpAsyncSet                     *async_set;
+  GimpDataNewFunc          data_new_func;
+  GimpDataGetStandardFunc  data_get_standard_func;
 
-  const GimpDataFactoryLoaderEntry *loader_entries;
-  gint                              n_loader_entries;
+  GimpAsyncSet            *async_set;
 };
 
 #define GET_PRIVATE(obj) (((GimpDataFactory *) (obj))->priv)
@@ -101,15 +89,7 @@ static void       gimp_data_factory_finalize            (GObject             *ob
 static gint64     gimp_data_factory_get_memsize         (GimpObject          *object,
                                                          gint64              *gui_size);
 
-static void       gimp_data_factory_real_data_init      (GimpDataFactory     *factory,
-                                                         GimpContext         *context);
-static void       gimp_data_factory_real_data_refresh   (GimpDataFactory     *factory,
-                                                         GimpContext         *context);
 static void       gimp_data_factory_real_data_save      (GimpDataFactory     *factory);
-static void       gimp_data_factory_real_data_free      (GimpDataFactory     *factory);
-static GimpData * gimp_data_factory_real_data_new       (GimpDataFactory     *factory,
-                                                         GimpContext         *context,
-                                                         const gchar         *name);
 static GimpData * gimp_data_factory_real_data_duplicate (GimpDataFactory     *factory,
                                                          GimpData            *data);
 static gboolean   gimp_data_factory_real_data_delete    (GimpDataFactory     *factory,
@@ -117,33 +97,14 @@ static gboolean   gimp_data_factory_real_data_delete    (GimpDataFactory     *fa
                                                          gboolean             delete_from_disk,
                                                          GError             **error);
 
-static void    gimp_data_factory_path_notify            (GObject             *object,
+static void       gimp_data_factory_path_notify         (GObject             *object,
                                                          const GParamSpec    *pspec,
                                                          GimpDataFactory     *factory);
-
-static void    gimp_data_factory_data_load              (GimpDataFactory     *factory,
-                                                         GimpContext         *context,
-                                                         GHashTable          *cache);
-
-static GFile * gimp_data_factory_get_save_dir           (GimpDataFactory     *factory,
+static GFile    * gimp_data_factory_get_save_dir        (GimpDataFactory     *factory,
                                                          GError             **error);
 
-static void    gimp_data_factory_load_directory         (GimpDataFactory     *factory,
-                                                         GimpContext         *context,
-                                                         GHashTable          *cache,
-                                                         gboolean             dir_writable,
-                                                         GFile               *directory,
-                                                         GFile               *top_directory);
-static void    gimp_data_factory_load_data              (GimpDataFactory     *factory,
-                                                         GimpContext         *context,
-                                                         GHashTable          *cache,
-                                                         gboolean             dir_writable,
-                                                         GFile               *file,
-                                                         GFileInfo           *info,
-                                                         GFile               *top_directory);
-
 
-G_DEFINE_TYPE (GimpDataFactory, gimp_data_factory, GIMP_TYPE_OBJECT)
+G_DEFINE_ABSTRACT_TYPE (GimpDataFactory, gimp_data_factory, GIMP_TYPE_OBJECT)
 
 #define parent_class gimp_data_factory_parent_class
 
@@ -161,11 +122,9 @@ gimp_data_factory_class_init (GimpDataFactoryClass *klass)
 
   gimp_object_class->get_memsize = gimp_data_factory_get_memsize;
 
-  klass->data_init               = gimp_data_factory_real_data_init;
-  klass->data_refresh            = gimp_data_factory_real_data_refresh;
+  klass->data_init               = NULL;
+  klass->data_refresh            = NULL;
   klass->data_save               = gimp_data_factory_real_data_save;
-  klass->data_free               = gimp_data_factory_real_data_free;
-  klass->data_new                = gimp_data_factory_real_data_new;
   klass->data_duplicate          = gimp_data_factory_real_data_duplicate;
   klass->data_delete             = gimp_data_factory_real_data_delete;
 
@@ -229,6 +188,8 @@ gimp_data_factory_constructed (GObject *object)
 
   gimp_assert (GIMP_IS_GIMP (priv->gimp));
   gimp_assert (g_type_is_a (priv->data_type, GIMP_TYPE_DATA));
+  gimp_assert (GIMP_DATA_FACTORY_GET_CLASS (object)->data_init != NULL);
+  gimp_assert (GIMP_DATA_FACTORY_GET_CLASS (object)->data_refresh != NULL);
 
   priv->container = gimp_list_new (priv->data_type, TRUE);
   gimp_list_set_sort_func (GIMP_LIST (priv->container),
@@ -356,87 +317,6 @@ gimp_data_factory_get_memsize (GimpObject *object,
                                                                   gui_size);
 }
 
-static void
-gimp_data_factory_real_data_init (GimpDataFactory *factory,
-                                  GimpContext     *context)
-{
-  gimp_data_factory_data_load (factory, context, NULL);
-}
-
-static void
-gimp_data_factory_refresh_cache_add (GimpDataFactory *factory,
-                                     GimpData        *data,
-                                     gpointer         user_data)
-{
-  GFile *file = gimp_data_get_file (data);
-
-  if (file)
-    {
-      GHashTable *cache = user_data;
-      GList      *list;
-
-      g_object_ref (data);
-
-      gimp_container_remove (factory->priv->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_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_factory_real_data_refresh (GimpDataFactory *factory,
-                                     GimpContext     *context)
-{
-  GimpDataFactoryPrivate *priv = GET_PRIVATE (factory);
-  GHashTable             *cache;
-
-  gimp_container_freeze (priv->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_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_factory_data_load (factory, context, cache);
-
-  /*  Now all the data is loaded. Free what remains in the cache  */
-  g_hash_table_foreach_remove (cache,
-                               gimp_data_factory_refresh_cache_remove, NULL);
-
-  g_hash_table_destroy (cache);
-
-  gimp_container_thaw (priv->container);
-}
-
 static void
 gimp_data_factory_real_data_save (GimpDataFactory *factory)
 {
@@ -504,51 +384,6 @@ gimp_data_factory_real_data_save (GimpDataFactory *factory)
   g_list_free (dirty);
 }
 
-static void
-gimp_data_factory_remove_cb (GimpDataFactory *factory,
-                             GimpData        *data,
-                             gpointer         user_data)
-{
-  gimp_container_remove (factory->priv->container, GIMP_OBJECT (data));
-}
-
-static void
-gimp_data_factory_real_data_free (GimpDataFactory *factory)
-{
-  gimp_container_freeze (factory->priv->container);
-
-  gimp_data_factory_data_foreach (factory, TRUE,
-                                  gimp_data_factory_remove_cb, NULL);
-
-  gimp_container_thaw (factory->priv->container);
-}
-
-static GimpData *
-gimp_data_factory_real_data_new (GimpDataFactory *factory,
-                                 GimpContext     *context,
-                                 const gchar     *name)
-{
-  GimpDataFactoryPrivate *priv = GET_PRIVATE (factory);
-
-  if (priv->data_new_func)
-    {
-      GimpData *data = priv->data_new_func (context, name);
-
-      if (data)
-        {
-          gimp_container_add (priv->container, GIMP_OBJECT (data));
-          g_object_unref (data);
-
-          return data;
-        }
-
-      g_warning ("%s: GimpDataFactory::data_new_func() returned NULL",
-                 G_STRFUNC);
-    }
-
-  return NULL;
-}
-
 static GimpData *
 gimp_data_factory_real_data_duplicate (GimpDataFactory *factory,
                                        GimpData        *data)
@@ -597,61 +432,15 @@ gimp_data_factory_real_data_delete (GimpDataFactory  *factory,
                                     gboolean          delete_from_disk,
                                     GError          **error)
 {
-  GimpDataFactoryPrivate *priv   = GET_PRIVATE (factory);
-  gboolean                retval = TRUE;
-
-  if (gimp_container_have (priv->container, GIMP_OBJECT (data)))
-    {
-      g_object_ref (data);
+  if (delete_from_disk && gimp_data_get_file (data))
+    return gimp_data_delete_from_disk (data, error);
 
-      gimp_container_remove (priv->container, GIMP_OBJECT (data));
-
-      if (delete_from_disk && gimp_data_get_file (data))
-        retval = gimp_data_delete_from_disk (data, error);
-
-      g_object_unref (data);
-    }
-
-  return retval;
+  return TRUE;
 }
 
 
 /*  public functions  */
 
-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)
-{
-  GimpDataFactory *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_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 factory;
-}
-
 void
 gimp_data_factory_data_init (GimpDataFactory *factory,
                              GimpContext     *context,
@@ -727,13 +516,29 @@ gimp_data_factory_data_save (GimpDataFactory *factory)
     GIMP_DATA_FACTORY_GET_CLASS (factory)->data_save (factory);
 }
 
+static void
+gimp_data_factory_data_free_foreach (GimpDataFactory *factory,
+                                     GimpData        *data,
+                                     gpointer         user_data)
+{
+  gimp_container_remove (factory->priv->container, GIMP_OBJECT (data));
+}
+
 void
 gimp_data_factory_data_free (GimpDataFactory *factory)
 {
   g_return_if_fail (GIMP_IS_DATA_FACTORY (factory));
 
   if (! gimp_container_is_empty (factory->priv->container))
-    GIMP_DATA_FACTORY_GET_CLASS (factory)->data_free (factory);
+    {
+      gimp_container_freeze (factory->priv->container);
+
+      gimp_data_factory_data_foreach (factory, TRUE,
+                                      gimp_data_factory_data_free_foreach,
+                                      NULL);
+
+      gimp_container_thaw (factory->priv->container);
+    }
 }
 
 GimpAsyncSet *
@@ -765,18 +570,58 @@ gimp_data_factory_data_wait (GimpDataFactory *factory)
   return TRUE;
 }
 
+gboolean
+gimp_data_factory_has_data_new_func (GimpDataFactory *factory)
+{
+  g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), FALSE);
+
+  return factory->priv->data_new_func != NULL;
+}
+
 GimpData *
 gimp_data_factory_data_new (GimpDataFactory *factory,
                             GimpContext     *context,
                             const gchar     *name)
 {
+  GimpDataFactoryPrivate *priv;
+
   g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL);
   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
   g_return_val_if_fail (name != NULL, NULL);
   g_return_val_if_fail (*name != '\0', NULL);
 
-  return GIMP_DATA_FACTORY_GET_CLASS (factory)->data_new (factory, context,
-                                                          name);
+  priv = GET_PRIVATE (factory);
+
+  if (priv->data_new_func)
+    {
+      GimpData *data = priv->data_new_func (context, name);
+
+      if (data)
+        {
+          gimp_container_add (priv->container, GIMP_OBJECT (data));
+          g_object_unref (data);
+
+          return data;
+        }
+
+      g_warning ("%s: GimpDataFactory::data_new_func() returned NULL",
+                 G_STRFUNC);
+    }
+
+  return NULL;
+}
+
+GimpData *
+gimp_data_factory_data_get_standard (GimpDataFactory *factory,
+                                     GimpContext     *context)
+{
+  g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL);
+  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+  if (factory->priv->data_get_standard_func)
+    return factory->priv->data_get_standard_func (context);
+
+  return NULL;
 }
 
 GimpData *
@@ -795,26 +640,29 @@ gimp_data_factory_data_delete (GimpDataFactory  *factory,
                                gboolean          delete_from_disk,
                                GError          **error)
 {
+  GimpDataFactoryPrivate *priv;
+  gboolean                retval = TRUE;
+
   g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), FALSE);
   g_return_val_if_fail (GIMP_IS_DATA (data), FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-  return GIMP_DATA_FACTORY_GET_CLASS (factory)->data_delete (factory, data,
-                                                             delete_from_disk,
-                                                             error);
-}
+  priv = GET_PRIVATE (factory);
 
-GimpData *
-gimp_data_factory_data_get_standard (GimpDataFactory *factory,
-                                     GimpContext     *context)
-{
-  g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL);
-  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+  if (gimp_container_have (priv->container, GIMP_OBJECT (data)))
+    {
+      g_object_ref (data);
 
-  if (factory->priv->data_get_standard_func)
-    return factory->priv->data_get_standard_func (context);
+      gimp_container_remove (priv->container, GIMP_OBJECT (data));
 
-  return NULL;
+      retval = GIMP_DATA_FACTORY_GET_CLASS (factory)->data_delete (factory, data,
+                                                                   delete_from_disk,
+                                                                   error);
+
+      g_object_unref (data);
+    }
+
+  return retval;
 }
 
 gboolean
@@ -899,6 +747,14 @@ gimp_data_factory_data_foreach (GimpDataFactory     *factory,
     }
 }
 
+Gimp *
+gimp_data_factory_get_gimp (GimpDataFactory *factory)
+{
+  g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL);
+
+  return factory->priv->gimp;
+}
+
 GType
 gimp_data_factory_get_data_type (GimpDataFactory *factory)
 {
@@ -923,22 +779,6 @@ gimp_data_factory_get_container_obsolete (GimpDataFactory *factory)
   return factory->priv->container_obsolete;
 }
 
-Gimp *
-gimp_data_factory_get_gimp (GimpDataFactory *factory)
-{
-  g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), NULL);
-
-  return factory->priv->gimp;
-}
-
-gboolean
-gimp_data_factory_has_data_new_func (GimpDataFactory *factory)
-{
-  g_return_val_if_fail (GIMP_IS_DATA_FACTORY (factory), FALSE);
-
-  return factory->priv->data_new_func != NULL;
-}
-
 GList *
 gimp_data_factory_get_data_path (GimpDataFactory *factory)
 {
@@ -1000,36 +840,6 @@ gimp_data_factory_path_notify (GObject          *object,
   gimp_unset_busy (priv->gimp);
 }
 
-static void
-gimp_data_factory_data_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_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 GFile *
 gimp_data_factory_get_save_dir (GimpDataFactory  *factory,
                                 GError          **error)
@@ -1100,207 +910,3 @@ gimp_data_factory_get_save_dir (GimpDataFactory  *factory,
 
   return writable_dir;
 }
-
-static void
-gimp_data_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_factory_load_directory (factory, context, cache,
-                                                dir_writable,
-                                                child,
-                                                top_directory);
-            }
-          else if (file_type == G_FILE_TYPE_REGULAR)
-            {
-              gimp_data_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_factory_load_data (GimpDataFactory *factory,
-                             GimpContext     *context,
-                             GHashTable      *cache,
-                             gboolean         dir_writable,
-                             GFile           *file,
-                             GFileInfo       *info,
-                             GFile           *top_directory)
-{
-  const GimpDataFactoryLoaderEntry *loader    = NULL;
-  GList                            *data_list = NULL;
-  GInputStream                     *input;
-  guint64                           mtime;
-  gint                              i;
-  GError                           *error = NULL;
-
-  for (i = 0; i < factory->priv->n_loader_entries; i++)
-    {
-      loader = &factory->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:
-  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 (factory->priv->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;
-      gboolean  obsolete;
-      gboolean  writable  = FALSE;
-      gboolean  deletable = FALSE;
-
-      uri = g_file_get_uri (file);
-
-      obsolete = (strstr (uri, GIMP_OBSOLETE_DATA_DIR_NAME) != 0);
-
-      g_free (uri);
-
-      /* obsolete files are immutable, don't check their writability */
-      if (! obsolete)
-        {
-          deletable = (g_list_length (data_list) == 1 && dir_writable);
-          writable  = (deletable && loader->writable);
-        }
-
-      for (list = data_list; list; list = g_list_next (list))
-        {
-          GimpData *data = list->data;
-
-          gimp_data_set_file (data, file, writable, deletable);
-          gimp_data_set_mtime (data, mtime);
-          gimp_data_clean (data);
-
-          if (obsolete)
-            {
-              gimp_container_add (factory->priv->container_obsolete,
-                                  GIMP_OBJECT (data));
-            }
-          else
-            {
-              gimp_data_set_folder_tags (data, top_directory);
-
-              gimp_container_add (factory->priv->container,
-                                  GIMP_OBJECT (data));
-            }
-
-          g_object_unref (data);
-        }
-
-      g_list_free (data_list);
-    }
-
-  /*  not else { ... } because loader->load_func() can return a list
-   *  of data objects *and* an error message if loading failed after
-   *  something was already loaded
-   */
-  if (G_UNLIKELY (error))
-    {
-      gimp_message (factory->priv->gimp, NULL, GIMP_MESSAGE_ERROR,
-                    _("Failed to load data:\n\n%s"), error->message);
-      g_clear_error (&error);
-    }
-}
diff --git a/app/core/gimpdatafactory.h b/app/core/gimpdatafactory.h
index 4f3f0036ff..577faa3529 100644
--- a/app/core/gimpdatafactory.h
+++ b/app/core/gimpdatafactory.h
@@ -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__  */
diff --git a/app/core/gimpdataloaderfactory.c b/app/core/gimpdataloaderfactory.c
new file mode 100644
index 0000000000..b2a550a1b7
--- /dev/null
+++ b/app/core/gimpdataloaderfactory.c
@@ -0,0 +1,467 @@
+/* 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;
+      gboolean  obsolete;
+      gboolean  writable  = FALSE;
+      gboolean  deletable = FALSE;
+
+      uri = g_file_get_uri (file);
+
+      obsolete = (strstr (uri, GIMP_OBSOLETE_DATA_DIR_NAME) != 0);
+
+      g_free (uri);
+
+      /* obsolete files are immutable, don't check their writability */
+      if (! obsolete)
+        {
+          deletable = (g_list_length (data_list) == 1 && dir_writable);
+          writable  = (deletable && loader->writable);
+        }
+
+      for (list = data_list; list; list = g_list_next (list))
+        {
+          GimpData *data = list->data;
+
+          gimp_data_set_file (data, file, writable, deletable);
+          gimp_data_set_mtime (data, mtime);
+          gimp_data_clean (data);
+
+          if (obsolete)
+            {
+              gimp_container_add (container_obsolete,
+                                  GIMP_OBJECT (data));
+            }
+          else
+            {
+              gimp_data_set_folder_tags (data, top_directory);
+
+              gimp_container_add (container,
+                                  GIMP_OBJECT (data));
+            }
+
+          g_object_unref (data);
+        }
+
+      g_list_free (data_list);
+    }
+
+  /*  not else { ... } because loader->load_func() can return a list
+   *  of data objects *and* an error message if loading failed after
+   *  something was already loaded
+   */
+  if (G_UNLIKELY (error))
+    {
+      gimp_message (gimp_data_factory_get_gimp (factory), NULL,
+                    GIMP_MESSAGE_ERROR,
+                    _("Failed to load data:\n\n%s"), error->message);
+      g_clear_error (&error);
+    }
+}
diff --git a/app/core/gimpdataloaderfactory.h b/app/core/gimpdataloaderfactory.h
new file mode 100644
index 0000000000..db113e3c84
--- /dev/null
+++ b/app/core/gimpdataloaderfactory.h
@@ -0,0 +1,80 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpdataloaderfactory.h
+ * 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/>.
+ */
+
+#ifndef __GIMP_DATA_LOADER_FACTORY_H__
+#define __GIMP_DATA_LOADER_FACTORY_H__
+
+
+#include "gimpdatafactory.h"
+
+
+typedef GList * (* GimpDataLoadFunc) (GimpContext   *context,
+                                      GFile         *file,
+                                      GInputStream  *input,
+                                      GError       **error);
+
+
+typedef struct _GimpDataLoaderEntry GimpDataLoaderEntry;
+
+struct _GimpDataLoaderEntry
+{
+  GimpDataLoadFunc  load_func;
+  const gchar      *extension;
+  gboolean          writable;
+};
+
+
+#define GIMP_TYPE_DATA_LOADER_FACTORY            (gimp_data_loader_factory_get_type ())
+#define GIMP_DATA_LOADER_FACTORY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_DATA_LOADER_FACTORY, GimpDataLoaderFactory))
+#define GIMP_DATA_LOADER_FACTORY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_DATA_LOADER_FACTORY, GimpDataLoaderFactoryClass))
+#define GIMP_IS_DATA_LOADER_FACTORY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_DATA_LOADER_FACTORY))
+#define GIMP_IS_DATA_LOADER_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_DATA_LOADER_FACTORY))
+#define GIMP_DATA_LOADER_FACTORY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_DATA_LOADER_FACTORY, GimpDataLoaderFactoryClass))
+
+
+typedef struct _GimpDataLoaderFactoryPrivate GimpDataLoaderFactoryPrivate;
+typedef struct _GimpDataLoaderFactoryClass   GimpDataLoaderFactoryClass;
+
+struct _GimpDataLoaderFactory
+{
+  GimpDataFactory               parent_instance;
+
+  GimpDataLoaderFactoryPrivate *priv;
+};
+
+struct _GimpDataLoaderFactoryClass
+{
+  GimpDataFactoryClass  parent_class;
+};
+
+
+GType             gimp_data_loader_factory_get_type (void) G_GNUC_CONST;
+
+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);
+
+
+#endif  /*  __GIMP_DATA_LOADER_FACTORY_H__  */
diff --git a/app/text/gimpfontfactory.c b/app/text/gimpfontfactory.c
index 390067dbe9..3715ca4e97 100644
--- a/app/text/gimpfontfactory.c
+++ b/app/text/gimpfontfactory.c
@@ -70,9 +70,6 @@ static void       gimp_font_factory_data_init       (GimpDataFactory *factory,
 static void       gimp_font_factory_data_refresh    (GimpDataFactory *factory,
                                                      GimpContext     *context);
 static void       gimp_font_factory_data_save       (GimpDataFactory *factory);
-static GimpData * gimp_font_factory_data_new        (GimpDataFactory *factory,
-                                                     GimpContext     *context,
-                                                     const gchar     *name);
 static GimpData * gimp_font_factory_data_duplicate  (GimpDataFactory *factory,
                                                      GimpData        *data);
 static gboolean   gimp_font_factory_data_delete     (GimpDataFactory *factory,
@@ -109,7 +106,6 @@ gimp_font_factory_class_init (GimpFontFactoryClass *klass)
   factory_class->data_init      = gimp_font_factory_data_init;
   factory_class->data_refresh   = gimp_font_factory_data_refresh;
   factory_class->data_save      = gimp_font_factory_data_save;
-  factory_class->data_new       = gimp_font_factory_data_new;
   factory_class->data_duplicate = gimp_font_factory_data_duplicate;
   factory_class->data_delete    = gimp_font_factory_data_delete;
 
@@ -175,14 +171,6 @@ gimp_font_factory_data_save (GimpDataFactory *factory)
   FcInitReinitialize ();
 }
 
-static GimpData *
-gimp_font_factory_data_new (GimpDataFactory *factory,
-                            GimpContext     *context,
-                            const gchar     *name)
-{
-  return NULL;
-}
-
 static GimpData *
 gimp_font_factory_data_duplicate (GimpDataFactory *factory,
                                   GimpData        *data)
@@ -196,8 +184,7 @@ gimp_font_factory_data_delete (GimpDataFactory  *factory,
                                gboolean          delete_from_disk,
                                GError          **error)
 {
-  return GIMP_DATA_FACTORY_CLASS (parent_class)->data_delete (factory, data,
-                                                              FALSE, error);
+  return TRUE;
 }
 
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 56db2dcdbe..d09949544e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -130,6 +130,7 @@ app/core/gimpcurve-load.c
 app/core/gimpcurve-save.c
 app/core/gimpdata.c
 app/core/gimpdatafactory.c
+app/core/gimpdataloaderfactory.c
 app/core/gimpdrawable.c
 app/core/gimpdrawable-bucket-fill.c
 app/core/gimpdrawable-edit.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]