[gimp] libgimp: memory/lifecycle manage the new libgmp proxy objects



commit 75bf3865b8f539fbe40366b870db024c70dea6a3
Author: Michael Natterer <mitch gimp org>
Date:   Fri Aug 23 17:18:17 2019 +0200

    libgimp: memory/lifecycle manage the new libgmp proxy objects
    
    Turn GimpPlugIn into the main factory for all proxies and keep the
    main hash tables there. The hash tables keep the initial reference.
    
    For each GimpProcedure::run(), have s "sub-factory" which hands out
    proxies to the actual procedure code. Each run() has hash tables of
    its own which hold additional references. When run() is done, get rid
    of its hash tables and their references, *and* drop the main plug-in
    reference counts from the global hashes if the proxies' refcount has
    dropped to one.

 libgimp/gimpdisplay.c           |  51 ++++-----
 libgimp/gimpimage.c             |  53 ++++------
 libgimp/gimpitem.c              |  96 ++++-------------
 libgimp/gimpplugin-private.h    |  31 ++++--
 libgimp/gimpplugin.c            | 228 +++++++++++++++++++++++++++++++++++++++-
 libgimp/gimpprocedure-private.h |  46 ++++++++
 libgimp/gimpprocedure.c         | 119 +++++++++++++++++++++
 7 files changed, 467 insertions(+), 157 deletions(-)
---
diff --git a/libgimp/gimpdisplay.c b/libgimp/gimpdisplay.c
index 419fd59074..fe971cc3d1 100644
--- a/libgimp/gimpdisplay.c
+++ b/libgimp/gimpdisplay.c
@@ -23,7 +23,11 @@
 
 #include "gimp.h"
 
-#include "gimppixbuf.h"
+#include "libgimpbase/gimpwire.h" /* FIXME kill this include */
+
+#include "gimpplugin-private.h"
+#include "gimpprocedure-private.h"
+
 
 enum
 {
@@ -32,13 +36,12 @@ enum
   N_PROPS
 };
 
+
 struct _GimpDisplayPrivate
 {
   gint id;
 };
 
-static GHashTable *gimp_displays = NULL;
-
 
 static void       gimp_display_set_property  (GObject      *object,
                                               guint         property_id,
@@ -49,12 +52,14 @@ static void       gimp_display_get_property  (GObject      *object,
                                               GValue       *value,
                                               GParamSpec   *pspec);
 
+
 G_DEFINE_TYPE_WITH_PRIVATE (GimpDisplay, gimp_display, G_TYPE_OBJECT)
 
 #define parent_class gimp_display_parent_class
 
 static GParamSpec *props[N_PROPS] = { NULL, };
 
+
 static void
 gimp_display_class_init (GimpDisplayClass *klass)
 {
@@ -121,8 +126,7 @@ gimp_display_get_property (GObject    *object,
 }
 
 
-/* Public API. */
-
+/* Public API */
 
 /**
  * gimp_display_get_id:
@@ -142,44 +146,25 @@ gimp_display_get_id (GimpDisplay *display)
  * gimp_display_get_by_id:
  * @display_id: The display id.
  *
- * Creates a #GimpDisplay representing @display_id.
+ * Returns a #GimpDisplay representing @display_id.
  *
  * Returns: (nullable) (transfer none): a #GimpDisplay for @display_id or
  *          %NULL if @display_id does not represent a valid display.
- *          The object belongs to libgimp and you should not free it.
+ *          The object belongs to libgimp and you must not modify or
+ *          unref it.
  *
  * Since: 3.0
  **/
 GimpDisplay *
 gimp_display_get_by_id (gint32 display_id)
 {
-  GimpDisplay *display = NULL;
-
-  if (G_UNLIKELY (! gimp_displays))
-    gimp_displays = g_hash_table_new_full (g_direct_hash,
-                                           g_direct_equal,
-                                           NULL,
-                                           (GDestroyNotify) g_object_unref);
-
-  if (! _gimp_display_is_valid (display_id))
+  if (display_id > 0)
     {
-      g_hash_table_remove (gimp_displays, GINT_TO_POINTER (display_id));
-    }
-  else
-    {
-      display = g_hash_table_lookup (gimp_displays,
-                                     GINT_TO_POINTER (display_id));
-
-      if (! display)
-        {
-          display = g_object_new (GIMP_TYPE_DISPLAY,
-                                  "id", display_id,
-                                  NULL);
-          g_hash_table_insert (gimp_displays,
-                               GINT_TO_POINTER (display_id),
-                               display);
-        }
+      GimpPlugIn    *plug_in   = gimp_get_plug_in ();
+      GimpProcedure *procedure = _gimp_plug_in_get_procedure (plug_in);
+
+      return _gimp_procedure_get_display (procedure, display_id);
     }
 
-  return display;
+  return NULL;
 }
diff --git a/libgimp/gimpimage.c b/libgimp/gimpimage.c
index b3cc11686a..0f7e550a3c 100644
--- a/libgimp/gimpimage.c
+++ b/libgimp/gimpimage.c
@@ -22,7 +22,12 @@
 
 #include "gimp.h"
 
+#include "libgimpbase/gimpwire.h" /* FIXME kill this include */
+
 #include "gimppixbuf.h"
+#include "gimpplugin-private.h"
+#include "gimpprocedure-private.h"
+
 
 enum
 {
@@ -31,13 +36,12 @@ enum
   N_PROPS
 };
 
+
 struct _GimpImagePrivate
 {
   gint id;
 };
 
-static GHashTable *gimp_images = NULL;
-
 
 static void       gimp_image_set_property  (GObject      *object,
                                             guint         property_id,
@@ -48,12 +52,14 @@ static void       gimp_image_get_property  (GObject      *object,
                                             GValue       *value,
                                             GParamSpec   *pspec);
 
+
 G_DEFINE_TYPE_WITH_PRIVATE (GimpImage, gimp_image, G_TYPE_OBJECT)
 
 #define parent_class gimp_image_parent_class
 
 static GParamSpec *props[N_PROPS] = { NULL, };
 
+
 static void
 gimp_image_class_init (GimpImageClass *klass)
 {
@@ -120,8 +126,7 @@ gimp_image_get_property (GObject    *object,
 }
 
 
-/* Public API. */
-
+/* Public API */
 
 /**
  * gimp_image_get_id:
@@ -143,42 +148,23 @@ gimp_image_get_id (GimpImage *image)
  *
  * Returns: (nullable) (transfer none): a #GimpImage for @image_id or
  *          %NULL if @image_id does not represent a valid image.
- *          The object belongs to libgimp and you should not free it.
+ *          The object belongs to libgimp and you must not modify
+ *          or unref it.
  *
  * Since: 3.0
  **/
 GimpImage *
 gimp_image_get_by_id (gint32 image_id)
 {
-  GimpImage *image = NULL;
-
-  if (G_UNLIKELY (! gimp_images))
-    gimp_images = g_hash_table_new_full (g_direct_hash,
-                                         g_direct_equal,
-                                         NULL,
-                                         (GDestroyNotify) g_object_unref);
-
-  if (! _gimp_image_is_valid (image_id))
-    {
-      g_hash_table_remove (gimp_images, GINT_TO_POINTER (image_id));
-    }
-  else
+  if (image_id > 0)
     {
-      image = g_hash_table_lookup (gimp_images,
-                                   GINT_TO_POINTER (image_id));
-
-      if (! image)
-        {
-          image = g_object_new (GIMP_TYPE_IMAGE,
-                                "id", image_id,
-                                NULL);
-          g_hash_table_insert (gimp_images,
-                               GINT_TO_POINTER (image_id),
-                               image);
-        }
+      GimpPlugIn    *plug_in   = gimp_get_plug_in ();
+      GimpProcedure *procedure = _gimp_plug_in_get_procedure (plug_in);
+
+      return _gimp_procedure_get_image (procedure, image_id);
     }
 
-  return image;
+  return NULL;
 }
 
 /**
@@ -202,13 +188,14 @@ gimp_image_list (void)
   gint   i;
 
   ids = _gimp_image_list (&num_images);
+
   for (i = 0; i < num_images; i++)
     images = g_list_prepend (images,
                              gimp_image_get_by_id (ids[i]));
-  images = g_list_reverse (images);
+
   g_free (ids);
 
-  return images;
+  return g_list_reverse (images);
 }
 
 /**
diff --git a/libgimp/gimpitem.c b/libgimp/gimpitem.c
index 7877cb5a56..249533175c 100644
--- a/libgimp/gimpitem.c
+++ b/libgimp/gimpitem.c
@@ -23,7 +23,11 @@
 
 #include "gimp.h"
 
-#include "gimppixbuf.h"
+#include "libgimpbase/gimpwire.h" /* FIXME kill this include */
+
+#include "gimpplugin-private.h"
+#include "gimpprocedure-private.h"
+
 
 enum
 {
@@ -32,13 +36,12 @@ enum
   N_PROPS
 };
 
+
 struct _GimpItemPrivate
 {
   gint id;
 };
 
-static GHashTable *gimp_items = NULL;
-
 
 static void       gimp_item_set_property  (GObject      *object,
                                             guint         property_id,
@@ -49,12 +52,14 @@ static void       gimp_item_get_property  (GObject      *object,
                                             GValue       *value,
                                             GParamSpec   *pspec);
 
+
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GimpItem, gimp_item, G_TYPE_OBJECT)
 
 #define parent_class gimp_item_parent_class
 
 static GParamSpec *props[N_PROPS] = { NULL, };
 
+
 static void
 gimp_item_class_init (GimpItemClass *klass)
 {
@@ -142,89 +147,29 @@ gimp_item_get_id (GimpItem *item)
  * gimp_item_get_by_id:
  * @item_id: The item id.
  *
- * Creates a #GimpItem representing @item_id. Since #GimpItem is an
- * abstract class, the object real type will actually be the proper
+ * Returns a #GimpItem representing @item_id. Since #GimpItem is an
+ * abstract class, the real object type will actually be the proper
  * subclass.
  *
  * Returns: (nullable) (transfer none): a #GimpItem for @item_id or
  *          %NULL if @item_id does not represent a valid item.
- *          The object belongs to libgimp and you should not free it.
+ *          The object belongs to libgimp and you must not modify
+ *          or unref it.
  *
  * Since: 3.0
  **/
 GimpItem *
 gimp_item_get_by_id (gint32 item_id)
 {
-  GimpItem *item = NULL;
-
-  if (G_UNLIKELY (! gimp_items))
-    gimp_items = g_hash_table_new_full (g_direct_hash,
-                                        g_direct_equal,
-                                        NULL,
-                                        (GDestroyNotify) g_object_unref);
-
-  if (! _gimp_item_is_valid (item_id))
+  if (item_id > 0)
     {
-      g_hash_table_remove (gimp_items, GINT_TO_POINTER (item_id));
-    }
-  else
-    {
-      item = g_hash_table_lookup (gimp_items,
-                                  GINT_TO_POINTER (item_id));
-
-      if (item)
-        {
-          /* Make sure the item is the proper class, since it could be
-           * reused (which means we'd have cycled over the whole int
-           * range; not that likely yet still possible on a very very
-           * long run process).
-           */
-          if ((_gimp_item_is_layer (item_id) &&
-               ! GIMP_IS_LAYER (item))               ||
-              (_gimp_item_is_layer_mask (item_id) &&
-               ! GIMP_IS_LAYER_MASK (item))          ||
-              (_gimp_item_is_selection (item_id)  &&
-               ! GIMP_IS_SELECTION (item))           ||
-              (_gimp_item_is_channel (item_id)    &&
-               ! GIMP_IS_CHANNEL (item))             ||
-              (_gimp_item_is_vectors (item_id)    &&
-               ! GIMP_IS_VECTORS (item)))
-            {
-              g_hash_table_remove (gimp_items, GINT_TO_POINTER (item_id));
-              item = NULL;
-            }
-        }
-
-      if (! item)
-        {
-          if (_gimp_item_is_layer (item_id))
-            item = g_object_new (GIMP_TYPE_LAYER,
-                                 "id", item_id,
-                                 NULL);
-          else if (_gimp_item_is_layer_mask (item_id))
-            item = g_object_new (GIMP_TYPE_LAYER_MASK,
-                                 "id", item_id,
-                                 NULL);
-          else if (_gimp_item_is_selection (item_id))
-            item = g_object_new (GIMP_TYPE_SELECTION,
-                                 "id", item_id,
-                                 NULL);
-          else if (_gimp_item_is_channel (item_id))
-            item = g_object_new (GIMP_TYPE_CHANNEL,
-                                 "id", item_id,
-                                 NULL);
-          else if (_gimp_item_is_vectors (item_id))
-            item = g_object_new (GIMP_TYPE_VECTORS,
-                                 "id", item_id,
-                                 NULL);
-          if (item)
-            g_hash_table_insert (gimp_items,
-                                 GINT_TO_POINTER (item_id),
-                                 item);
-        }
+      GimpPlugIn    *plug_in   = gimp_get_plug_in ();
+      GimpProcedure *procedure = _gimp_plug_in_get_procedure (plug_in);
+
+      return _gimp_procedure_get_item (procedure, item_id);
     }
 
-  return item;
+  return NULL;
 }
 
 /**
@@ -239,7 +184,7 @@ gimp_item_get_by_id (gint32 item_id)
  * Returns: (element-type GimpItem) (transfer container):
  *          The item's list of children.
  *          The returned value must be freed with g_list_free(). Item
- *          elements belong to libgimp and must not be freed.
+ *          elements belong to libgimp and must not be unrefed.
  *
  * Since: 3.0
  **/
@@ -256,10 +201,9 @@ gimp_item_get_children (GimpItem *item)
   for (i = 0; i < num_items; i++)
     children = g_list_prepend (children, gimp_item_get_by_id (ids[i]));
 
-  children = g_list_reverse (children);
   g_free (ids);
 
-  return children;
+  return g_list_reverse (children);
 }
 
 /**
diff --git a/libgimp/gimpplugin-private.h b/libgimp/gimpplugin-private.h
index 55272deff4..ba2e4ba75d 100644
--- a/libgimp/gimpplugin-private.h
+++ b/libgimp/gimpplugin-private.h
@@ -25,17 +25,26 @@
 G_BEGIN_DECLS
 
 
-void         _gimp_plug_in_query             (GimpPlugIn      *plug_in);
-void         _gimp_plug_in_init              (GimpPlugIn      *plug_in);
-void         _gimp_plug_in_run               (GimpPlugIn      *plug_in);
-void         _gimp_plug_in_quit              (GimpPlugIn      *plug_in);
-
-GIOChannel * _gimp_plug_in_get_read_channel  (GimpPlugIn      *plug_in);
-GIOChannel * _gimp_plug_in_get_write_channel (GimpPlugIn      *plug_in);
-
-void         _gimp_plug_in_read_expect_msg   (GimpPlugIn      *plug_in,
-                                              GimpWireMessage *msg,
-                                              gint             type);
+void            _gimp_plug_in_query             (GimpPlugIn      *plug_in);
+void            _gimp_plug_in_init              (GimpPlugIn      *plug_in);
+void            _gimp_plug_in_run               (GimpPlugIn      *plug_in);
+void            _gimp_plug_in_quit              (GimpPlugIn      *plug_in);
+
+GIOChannel    * _gimp_plug_in_get_read_channel  (GimpPlugIn      *plug_in);
+GIOChannel    * _gimp_plug_in_get_write_channel (GimpPlugIn      *plug_in);
+
+void            _gimp_plug_in_read_expect_msg   (GimpPlugIn      *plug_in,
+                                                 GimpWireMessage *msg,
+                                                 gint             type);
+
+GimpProcedure * _gimp_plug_in_get_procedure     (GimpPlugIn      *plug_in);
+
+GimpDisplay   * _gimp_plug_in_get_display       (GimpPlugIn      *plug_in,
+                                                 gint32           display_id);
+GimpImage     * _gimp_plug_in_get_image         (GimpPlugIn      *plug_in,
+                                                 gint32           image_id);
+GimpItem      * _gimp_plug_in_get_item          (GimpPlugIn      *plug_in,
+                                                 gint32           item_id);
 
 
 G_END_DECLS
diff --git a/libgimp/gimpplugin.c b/libgimp/gimpplugin.c
index d4d6f8c777..e22fe41b12 100644
--- a/libgimp/gimpplugin.c
+++ b/libgimp/gimpplugin.c
@@ -36,6 +36,7 @@
 #include "gimpgpparams.h"
 #include "gimpplugin-private.h"
 #include "gimpplugin_pdb.h"
+#include "gimpprocedure-private.h"
 
 
 /**
@@ -89,6 +90,11 @@ struct _GimpPlugInPrivate
   GList      *menu_branches;
 
   GList      *temp_procedures;
+
+  GList      *procedure_stack;
+  GHashTable *displays;
+  GHashTable *images;
+  GHashTable *items;
 };
 
 
@@ -124,13 +130,20 @@ static void       gimp_plug_in_proc_run          (GimpPlugIn      *plug_in,
                                                   GPProcRun       *proc_run);
 static void       gimp_plug_in_temp_proc_run     (GimpPlugIn      *plug_in,
                                                   GPProcRun       *proc_run);
-static void       gimp_plug_in_proc_run_internal (GPProcRun       *proc_run,
+static void       gimp_plug_in_proc_run_internal (GimpPlugIn      *plug_in,
+                                                  GPProcRun       *proc_run,
                                                   GimpProcedure   *procedure,
                                                   GPProcReturn    *proc_return);
 static gboolean   gimp_plug_in_extension_read    (GIOChannel      *channel,
                                                   GIOCondition     condition,
                                                   gpointer         data);
 
+static void       gimp_plug_in_push_procedure    (GimpPlugIn      *plug_in,
+                                                  GimpProcedure   *procedure);
+static void       gimp_plug_in_pop_procedure     (GimpPlugIn      *plug_in,
+                                                  GimpProcedure   *procedure);
+static void       gimp_plug_in_destroy_proxies   (GimpPlugIn      *plug_in);
+
 
 G_DEFINE_TYPE_WITH_PRIVATE (GimpPlugIn, gimp_plug_in, G_TYPE_OBJECT)
 
@@ -225,6 +238,8 @@ gimp_plug_in_finalize (GObject *object)
 
   g_clear_pointer (&plug_in->priv->menu_branches, g_list_free);
 
+  gimp_plug_in_destroy_proxies (plug_in);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -1097,7 +1112,8 @@ gimp_plug_in_proc_run (GimpPlugIn *plug_in,
 
   if (procedure)
     {
-      gimp_plug_in_proc_run_internal (proc_run, procedure,
+      gimp_plug_in_proc_run_internal (plug_in,
+                                      proc_run, procedure,
                                       &proc_return);
       g_object_unref (procedure);
     }
@@ -1118,7 +1134,8 @@ gimp_plug_in_temp_proc_run (GimpPlugIn *plug_in,
 
   if (procedure)
     {
-      gimp_plug_in_proc_run_internal (proc_run, procedure,
+      gimp_plug_in_proc_run_internal (plug_in,
+                                      proc_run, procedure,
                                       &proc_return);
     }
 
@@ -1128,7 +1145,8 @@ gimp_plug_in_temp_proc_run (GimpPlugIn *plug_in,
 }
 
 static void
-gimp_plug_in_proc_run_internal (GPProcRun     *proc_run,
+gimp_plug_in_proc_run_internal (GimpPlugIn    *plug_in,
+                                GPProcRun     *proc_run,
                                 GimpProcedure *procedure,
                                 GPProcReturn  *proc_return)
 {
@@ -1141,7 +1159,10 @@ gimp_plug_in_proc_run_internal (GPProcRun     *proc_run,
                                               proc_run->nparams,
                                               FALSE, FALSE);
 
+  gimp_plug_in_push_procedure (plug_in, procedure);
   return_values = gimp_procedure_run (procedure, arguments);
+  gimp_plug_in_pop_procedure (plug_in, procedure);
+
   gimp_value_array_unref (arguments);
 
   proc_return->name    = proc_run->name;
@@ -1162,3 +1183,202 @@ gimp_plug_in_extension_read (GIOChannel  *channel,
 
   return G_SOURCE_CONTINUE;
 }
+
+
+/*  procedure stack / display-, image-, item-cache  */
+
+GimpProcedure *
+_gimp_plug_in_get_procedure (GimpPlugIn *plug_in)
+{
+  g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
+  g_return_val_if_fail (plug_in->priv->procedure_stack != NULL, NULL);
+
+  return plug_in->priv->procedure_stack->data;
+}
+
+static void
+gimp_plug_in_push_procedure (GimpPlugIn    *plug_in,
+                             GimpProcedure *procedure)
+{
+  plug_in->priv->procedure_stack =
+    g_list_prepend (plug_in->priv->procedure_stack, procedure);
+}
+
+static void
+gimp_plug_in_pop_procedure (GimpPlugIn    *plug_in,
+                            GimpProcedure *procedure)
+{
+  plug_in->priv->procedure_stack =
+    g_list_remove (plug_in->priv->procedure_stack, procedure);
+
+  _gimp_procedure_destroy_proxies (procedure);
+
+  if (plug_in->priv->procedure_stack)
+    {
+      GHashTableIter iter;
+      gpointer       key, value;
+
+      g_hash_table_iter_init (&iter, plug_in->priv->displays);
+      while (g_hash_table_iter_next (&iter, &key, &value))
+        {
+          GObject *object = value;
+
+          if (object->ref_count == 1)
+            g_hash_table_iter_remove (&iter);
+        }
+
+      g_hash_table_iter_init (&iter, plug_in->priv->images);
+      while (g_hash_table_iter_next (&iter, &key, &value))
+        {
+          GObject *object = value;
+
+          if (object->ref_count == 1)
+            g_hash_table_iter_remove (&iter);
+        }
+
+      g_hash_table_iter_init (&iter, plug_in->priv->items);
+      while (g_hash_table_iter_next (&iter, &key, &value))
+        {
+          GObject *object = value;
+
+          if (object->ref_count == 1)
+            g_hash_table_iter_remove (&iter);
+        }
+    }
+  else
+    {
+      gimp_plug_in_destroy_proxies (plug_in);
+    }
+}
+
+GimpDisplay *
+_gimp_plug_in_get_display (GimpPlugIn *plug_in,
+                           gint32      display_id)
+{
+  GimpDisplay *display = NULL;
+
+  g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
+
+  if (G_UNLIKELY (! plug_in->priv->displays))
+    plug_in->priv->displays =
+      g_hash_table_new_full (g_direct_hash,
+                             g_direct_equal,
+                             NULL,
+                             (GDestroyNotify) g_object_unref);
+
+  display = g_hash_table_lookup (plug_in->priv->displays,
+                                 GINT_TO_POINTER (display_id));
+
+  if (! display)
+    {
+      display = g_object_new (GIMP_TYPE_DISPLAY,
+                              "id", display_id,
+                              NULL);
+
+      g_hash_table_insert (plug_in->priv->displays,
+                           GINT_TO_POINTER (display_id),
+                           display);
+    }
+
+  return display;
+}
+
+GimpImage *
+_gimp_plug_in_get_image (GimpPlugIn *plug_in,
+                         gint32      image_id)
+{
+  GimpImage *image = NULL;
+
+  g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
+
+  if (G_UNLIKELY (! plug_in->priv->images))
+    plug_in->priv->images =
+      g_hash_table_new_full (g_direct_hash,
+                             g_direct_equal,
+                             NULL,
+                             (GDestroyNotify) g_object_unref);
+
+  image = g_hash_table_lookup (plug_in->priv->images,
+                               GINT_TO_POINTER (image_id));
+
+  if (! image)
+    {
+      image = g_object_new (GIMP_TYPE_IMAGE,
+                            "id", image_id,
+                            NULL);
+
+      g_hash_table_insert (plug_in->priv->images,
+                           GINT_TO_POINTER (image_id),
+                           image);
+    }
+
+  return image;
+}
+
+GimpItem *
+_gimp_plug_in_get_item (GimpPlugIn *plug_in,
+                        gint32      item_id)
+{
+  GimpItem *item = NULL;
+
+  g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
+
+  if (G_UNLIKELY (! plug_in->priv->items))
+    plug_in->priv->items =
+      g_hash_table_new_full (g_direct_hash,
+                             g_direct_equal,
+                             NULL,
+                             (GDestroyNotify) g_object_unref);
+
+  item = g_hash_table_lookup (plug_in->priv->items,
+                              GINT_TO_POINTER (item_id));
+
+  if (! item)
+    {
+      if (_gimp_item_is_layer (item_id))
+        {
+          item = g_object_new (GIMP_TYPE_LAYER,
+                               "id", item_id,
+                               NULL);
+        }
+      else if (_gimp_item_is_layer_mask (item_id))
+        {
+          item = g_object_new (GIMP_TYPE_LAYER_MASK,
+                               "id", item_id,
+                               NULL);
+        }
+      else if (_gimp_item_is_selection (item_id))
+        {
+          item = g_object_new (GIMP_TYPE_SELECTION,
+                               "id", item_id,
+                               NULL);
+        }
+      else if (_gimp_item_is_channel (item_id))
+        {
+          item = g_object_new (GIMP_TYPE_CHANNEL,
+                               "id", item_id,
+                               NULL);
+        }
+      else if (_gimp_item_is_vectors (item_id))
+        {
+          item = g_object_new (GIMP_TYPE_VECTORS,
+                               "id", item_id,
+                               NULL);
+        }
+
+      if (item)
+        g_hash_table_insert (plug_in->priv->items,
+                             GINT_TO_POINTER (item_id),
+                             item);
+    }
+
+  return item;
+}
+
+static void
+gimp_plug_in_destroy_proxies (GimpPlugIn *plug_in)
+{
+  g_clear_pointer (&plug_in->priv->displays, g_hash_table_unref);
+  g_clear_pointer (&plug_in->priv->images,   g_hash_table_unref);
+  g_clear_pointer (&plug_in->priv->items,    g_hash_table_unref);
+}
diff --git a/libgimp/gimpprocedure-private.h b/libgimp/gimpprocedure-private.h
new file mode 100644
index 0000000000..2c1e148e84
--- /dev/null
+++ b/libgimp/gimpprocedure-private.h
@@ -0,0 +1,46 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpprocedure-private.h
+ * Copyright (C) 2019 Michael Natterer <mitch gimp org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION)
+#error "Only <libgimp/gimp.h> can be included directly."
+#endif
+
+#ifndef __GIMP_PROCEDURE_PRIVATE_H__
+#define __GIMP_PROCEDURE_PRIVATE_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GimpDisplay * _gimp_procedure_get_display     (GimpProcedure *procedure,
+                                               gint32         display_id);
+GimpImage   * _gimp_procedure_get_image       (GimpProcedure *procedure,
+                                               gint32         image_id);
+GimpItem    * _gimp_procedure_get_item        (GimpProcedure *procedure,
+                                               gint32         item_id);
+
+void          _gimp_procedure_destroy_proxies (GimpProcedure *procedure);
+
+
+G_END_DECLS
+
+#endif  /*  __GIMP_PROCEDURE_H__  */
diff --git a/libgimp/gimpprocedure.c b/libgimp/gimpprocedure.c
index 7deb52d07a..20a3b825a5 100644
--- a/libgimp/gimpprocedure.c
+++ b/libgimp/gimpprocedure.c
@@ -34,6 +34,7 @@
 #include "gimppdb-private.h"
 #include "gimpplugin-private.h"
 #include "gimpplugin_pdb.h"
+#include "gimpprocedure-private.h"
 
 #include "libgimp-intl.h"
 
@@ -82,6 +83,10 @@ struct _GimpProcedurePrivate
   GDestroyNotify    run_data_destroy;
 
   gboolean          installed;
+
+  GHashTable       *displays;
+  GHashTable       *images;
+  GHashTable       *items;
 };
 
 
@@ -222,6 +227,8 @@ gimp_procedure_finalize (GObject *object)
       g_clear_pointer (&procedure->priv->values, g_free);
     }
 
+  _gimp_procedure_destroy_proxies (procedure);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -1641,3 +1648,115 @@ gimp_procedure_set_icon (GimpProcedure *procedure,
       g_return_if_reached ();
     }
 }
+
+
+/*  internal functions  */
+
+GimpDisplay *
+_gimp_procedure_get_display (GimpProcedure *procedure,
+                             gint32         display_id)
+{
+  GimpDisplay *display = NULL;
+
+  g_return_val_if_fail (GIMP_IS_PROCEDURE (procedure), NULL);
+  g_return_val_if_fail (_gimp_display_is_valid (display_id), NULL);
+
+  if (G_UNLIKELY (! procedure->priv->displays))
+    procedure->priv->displays =
+      g_hash_table_new_full (g_direct_hash,
+                             g_direct_equal,
+                             NULL,
+                             (GDestroyNotify) g_object_unref);
+
+  display = g_hash_table_lookup (procedure->priv->displays,
+                                 GINT_TO_POINTER (display_id));
+
+  if (! display)
+    {
+      display = _gimp_plug_in_get_display (procedure->priv->plug_in,
+                                           display_id);
+
+      if (display)
+        g_hash_table_insert (procedure->priv->displays,
+                             GINT_TO_POINTER (display_id),
+                             g_object_ref (display));
+    }
+
+  return display;
+}
+
+GimpImage *
+_gimp_procedure_get_image (GimpProcedure *procedure,
+                           gint32         image_id)
+{
+  GimpImage *image = NULL;
+
+  g_return_val_if_fail (GIMP_IS_PROCEDURE (procedure), NULL);
+  g_return_val_if_fail (_gimp_image_is_valid (image_id), NULL);
+
+  if (G_UNLIKELY (! procedure->priv->images))
+    procedure->priv->images =
+      g_hash_table_new_full (g_direct_hash,
+                             g_direct_equal,
+                             NULL,
+                             (GDestroyNotify) g_object_unref);
+
+  image = g_hash_table_lookup (procedure->priv->images,
+                               GINT_TO_POINTER (image_id));
+
+  if (! image)
+    {
+      image = _gimp_plug_in_get_image (procedure->priv->plug_in,
+                                       image_id);
+
+      if (image)
+        g_hash_table_insert (procedure->priv->images,
+                             GINT_TO_POINTER (image_id),
+                             g_object_ref (image));
+    }
+
+  return image;
+}
+
+GimpItem *
+_gimp_procedure_get_item (GimpProcedure *procedure,
+                          gint32         item_id)
+{
+  GimpItem *item = NULL;
+
+  g_return_val_if_fail (GIMP_IS_PROCEDURE (procedure), NULL);
+  g_return_val_if_fail (_gimp_item_is_valid (item_id), NULL);
+
+  if (G_UNLIKELY (! procedure->priv->items))
+    procedure->priv->items =
+      g_hash_table_new_full (g_direct_hash,
+                             g_direct_equal,
+                             NULL,
+                             (GDestroyNotify) g_object_unref);
+
+  item = g_hash_table_lookup (procedure->priv->items,
+                              GINT_TO_POINTER (item_id));
+
+  if (! item)
+    {
+      item = _gimp_plug_in_get_item (procedure->priv->plug_in,
+                                     item_id);
+
+      if (item)
+        g_hash_table_insert (procedure->priv->items,
+                             GINT_TO_POINTER (item_id),
+                             g_object_ref (item));
+    }
+
+  return item;
+}
+
+void
+_gimp_procedure_destroy_proxies (GimpProcedure *procedure)
+{
+  g_return_if_fail (GIMP_IS_PROCEDURE (procedure));
+
+  g_clear_pointer (&procedure->priv->displays, g_hash_table_unref);
+  g_clear_pointer (&procedure->priv->images,   g_hash_table_unref);
+  g_clear_pointer (&procedure->priv->items,    g_hash_table_unref);
+}


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