[gimp] libgimpconfig: add gimp_config_type_register()



commit 3598722020cb298dedd60088079bc24c8b5e6bc9
Author: Michael Natterer <mitch gimp org>
Date:   Mon Sep 16 10:45:22 2019 +0200

    libgimpconfig: add gimp_config_type_register()
    
    which implements a generalized (not GEGL operation specific) version
    of the dynamic config type creation code from gimp-operation-config.c.

 app/operations/gimp-operation-config.c | 238 +++++------------------------
 libgimpconfig/Makefile.gi              |   2 +
 libgimpconfig/gimpconfig-register.c    | 269 +++++++++++++++++++++++++++++++++
 libgimpconfig/gimpconfig-register.h    |  42 +++++
 libgimpconfig/gimpconfig.def           |   3 +-
 libgimpconfig/gimpconfig.h             |  11 +-
 libgimpconfig/meson.build              |   4 +-
 7 files changed, 360 insertions(+), 209 deletions(-)
---
diff --git a/app/operations/gimp-operation-config.c b/app/operations/gimp-operation-config.c
index f4ff88a095..efa08a3b1d 100644
--- a/app/operations/gimp-operation-config.c
+++ b/app/operations/gimp-operation-config.c
@@ -86,166 +86,6 @@ gimp_operation_config_get_container_table (Gimp *gimp)
   return config_containers;
 }
 
-static GValue *
-gimp_operation_config_value_new (GParamSpec *pspec)
-{
-  GValue *value = g_slice_new0 (GValue);
-
-  g_value_init (value, pspec->value_type);
-  g_param_value_set_default (pspec, value);
-
-  return value;
-}
-
-static void
-gimp_operation_config_value_free (GValue *value)
-{
-  g_value_unset (value);
-  g_slice_free (GValue, value);
-}
-
-static GHashTable *
-gimp_operation_config_get_properties (GObject *object)
-{
-  GHashTable *properties = g_object_get_data (object, "properties");
-
-  if (! properties)
-    {
-      properties = g_hash_table_new_full (g_str_hash,
-                                          g_str_equal,
-                                          (GDestroyNotify) g_free,
-                                          (GDestroyNotify) gimp_operation_config_value_free);
-
-      g_object_set_data_full (object, "properties", properties,
-                              (GDestroyNotify) g_hash_table_unref);
-    }
-
-  return properties;
-}
-
-static GValue *
-gimp_operation_config_value_get (GObject    *object,
-                                 GParamSpec *pspec)
-{
-  GHashTable *properties = gimp_operation_config_get_properties (object);
-  GValue     *value;
-
-  value = g_hash_table_lookup (properties, pspec->name);
-
-  if (! value)
-    {
-      value = gimp_operation_config_value_new (pspec);
-      g_hash_table_insert (properties, g_strdup (pspec->name), value);
-    }
-
-  return value;
-}
-
-static void
-gimp_operation_config_set_property (GObject      *object,
-                                    guint         property_id,
-                                    const GValue *value,
-                                    GParamSpec   *pspec)
-{
-  GValue *val = gimp_operation_config_value_get (object, pspec);
-
-  g_value_copy (value, val);
-}
-
-static void
-gimp_operation_config_get_property (GObject    *object,
-                                    guint       property_id,
-                                    GValue     *value,
-                                    GParamSpec *pspec)
-{
-  GValue *val = gimp_operation_config_value_get (object, pspec);
-
-  g_value_copy (val, value);
-}
-
-static void
-gimp_operation_config_class_init (GObjectClass *klass,
-                                  const gchar  *operation)
-{
-  GParamSpec **pspecs;
-  guint        n_pspecs;
-  gint         i;
-
-  klass->set_property = gimp_operation_config_set_property;
-  klass->get_property = gimp_operation_config_get_property;
-
-  pspecs = gegl_operation_list_properties (operation, &n_pspecs);
-
-  for (i = 0; i < n_pspecs; i++)
-    {
-      GParamSpec *pspec = pspecs[i];
-
-      if ((pspec->flags & G_PARAM_READABLE) &&
-          (pspec->flags & G_PARAM_WRITABLE) &&
-          strcmp (pspec->name, "input")     &&
-          strcmp (pspec->name, "output"))
-        {
-          GParamSpec *copy = gimp_config_param_spec_duplicate (pspec);
-
-          if (copy)
-            {
-              g_object_class_install_property (klass, i + 1, copy);
-            }
-          else if (! G_IS_PARAM_SPEC_OBJECT (pspec) &&
-                   ! G_IS_PARAM_SPEC_POINTER (pspec))
-            {
-              /*  silently ignore object properties  */
-
-              g_warning ("%s: not supported: %s (%s)\n", G_STRFUNC,
-                         g_type_name (G_TYPE_FROM_INSTANCE (pspec)), pspec->name);
-            }
-        }
-    }
-
-  g_free (pspecs);
-}
-
-static gboolean
-gimp_operation_config_equal (GimpConfig *a,
-                             GimpConfig *b)
-{
-  GList    *diff;
-  gboolean  equal = TRUE;
-
-  diff = gimp_config_diff (G_OBJECT (a), G_OBJECT (b),
-                           GIMP_CONFIG_PARAM_SERIALIZE);
-
-  if (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b))
-    {
-      GList *list;
-
-      for (list = diff; list; list = g_list_next (list))
-        {
-          GParamSpec *pspec = list->data;
-
-          if (pspec->owner_type == G_TYPE_FROM_INSTANCE (a))
-            {
-              equal = FALSE;
-              break;
-            }
-        }
-    }
-  else if (diff)
-    {
-      equal = FALSE;
-    }
-
-  g_list_free (diff);
-
-  return equal;
-}
-
-static void
-gimp_operation_config_config_iface_init (GimpConfigInterface *iface)
-{
-  iface->equal = gimp_operation_config_equal;
-}
-
 
 /*  public functions  */
 
@@ -265,7 +105,7 @@ gimp_operation_config_register (Gimp        *gimp,
   g_hash_table_insert (config_types,
                        g_strdup (operation),
                        (gpointer) config_type);
- }
+}
 
 GType
 gimp_operation_config_get_type (Gimp        *gimp,
@@ -278,6 +118,8 @@ gimp_operation_config_get_type (Gimp        *gimp,
 
   g_return_val_if_fail (GIMP_IS_GIMP (gimp), G_TYPE_NONE);
   g_return_val_if_fail (operation != NULL, G_TYPE_NONE);
+  g_return_val_if_fail (g_type_is_a (parent_type, GIMP_TYPE_OBJECT),
+                        G_TYPE_NONE);
 
   config_types = gimp_operation_config_get_type_table (gimp);
 
@@ -285,59 +127,51 @@ gimp_operation_config_get_type (Gimp        *gimp,
 
   if (! config_type)
     {
-      GTypeQuery query;
+      GParamSpec **pspecs;
+      guint        n_pspecs;
+      gchar       *type_name;
+      gint         i, j;
 
-      g_return_val_if_fail (g_type_is_a (parent_type, GIMP_TYPE_OBJECT),
-                            G_TYPE_NONE);
+      pspecs = gegl_operation_list_properties (operation, &n_pspecs);
 
-      g_type_query (parent_type, &query);
-
-      {
-        GTypeInfo info =
-        {
-          query.class_size,
-          (GBaseInitFunc) NULL,
-          (GBaseFinalizeFunc) NULL,
-          (GClassInitFunc) gimp_operation_config_class_init,
-          NULL,           /* class_finalize */
-          operation,
-          query.instance_size,
-          0,              /* n_preallocs */
-          (GInstanceInitFunc) NULL,
-        };
-
-        const GInterfaceInfo config_info =
+      for (i = 0, j = 0; i < n_pspecs; i++)
         {
-          (GInterfaceInitFunc) gimp_operation_config_config_iface_init,
-          NULL, /* interface_finalize */
-          NULL  /* interface_data     */
-        };
+          GParamSpec *pspec = pspecs[i];
 
-        gchar *type_name = g_strdup_printf ("GimpGegl-%s-config",
-                                            operation);
+          if ((pspec->flags & G_PARAM_READABLE) &&
+              (pspec->flags & G_PARAM_WRITABLE) &&
+              strcmp (pspec->name, "input")     &&
+              strcmp (pspec->name, "output"))
+            {
+              pspecs[j] = pspec;
+              j++;
+            }
+        }
 
-        g_strcanon (type_name,
-                    G_CSET_DIGITS "-" G_CSET_a_2_z G_CSET_A_2_Z, '-');
+      n_pspecs = j;
 
-        config_type = g_type_register_static (parent_type, type_name,
-                                              &info, 0);
+      type_name = g_strdup_printf ("GimpGegl-%s-config", operation);
 
-        g_free (type_name);
+      g_strcanon (type_name,
+                  G_CSET_DIGITS "-" G_CSET_a_2_z G_CSET_A_2_Z, '-');
 
-        g_type_add_interface_static (config_type, GIMP_TYPE_CONFIG,
-                                     &config_info);
+      config_type = gimp_config_type_register (parent_type,
+                                               type_name,
+                                               pspecs, n_pspecs);
 
-        if (icon_name && g_type_is_a (config_type, GIMP_TYPE_VIEWABLE))
-          {
-            GimpViewableClass *viewable_class = g_type_class_ref (config_type);
+      g_free (pspecs);
+      g_free (type_name);
 
-            viewable_class->default_icon_name = g_strdup (icon_name);
+      if (icon_name && g_type_is_a (config_type, GIMP_TYPE_VIEWABLE))
+        {
+          GimpViewableClass *viewable_class = g_type_class_ref (config_type);
 
-            g_type_class_unref (viewable_class);
-          }
+          viewable_class->default_icon_name = g_strdup (icon_name);
+
+          g_type_class_unref (viewable_class);
+        }
 
-        gimp_operation_config_register (gimp, operation, config_type);
-      }
+      gimp_operation_config_register (gimp, operation, config_type);
     }
 
   return config_type;
diff --git a/libgimpconfig/Makefile.gi b/libgimpconfig/Makefile.gi
index bd83600014..532b790d19 100644
--- a/libgimpconfig/Makefile.gi
+++ b/libgimpconfig/Makefile.gi
@@ -8,6 +8,7 @@ libgimpconfig_introspectable_headers =  \
        ../libgimpconfig/gimpconfig-error.h             \
        ../libgimpconfig/gimpconfig-params.h            \
        ../libgimpconfig/gimpconfig-path.h              \
+       ../libgimpconfig/gimpconfig-register.h          \
        ../libgimpconfig/gimpconfig-serialize.h         \
        ../libgimpconfig/gimpconfig-utils.h             \
        ../libgimpconfig/gimpconfigwriter.h             \
@@ -20,6 +21,7 @@ libgimpconfig_introspectable =        \
        ../libgimpconfig/gimpconfig-error.c             \
        ../libgimpconfig/gimpconfig-path.c              \
        ../libgimpconfig/gimpconfig-params.c            \
+       ../libgimpconfig/gimpconfig-register.c          \
        ../libgimpconfig/gimpconfig-serialize.c         \
        ../libgimpconfig/gimpconfig-utils.c             \
        ../libgimpconfig/gimpconfigwriter.c             \
diff --git a/libgimpconfig/gimpconfig-register.c b/libgimpconfig/gimpconfig-register.c
new file mode 100644
index 0000000000..4eaef927f4
--- /dev/null
+++ b/libgimpconfig/gimpconfig-register.c
@@ -0,0 +1,269 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpconfig-register.c
+ * Copyright (C) 2008-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/>.
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimpconfig.h"
+
+
+/*  local function prototypes  */
+
+static void       gimp_config_class_init        (GObjectClass         *klass,
+                                                 GParamSpec          **pspecs);
+static void       gimp_config_config_iface_init (GimpConfigInterface  *iface);
+
+static void       gimp_config_set_property      (GObject              *object,
+                                                 guint                 property_id,
+                                                 const GValue         *value,
+                                                 GParamSpec           *pspec);
+static void       gimp_config_get_property      (GObject              *object,
+                                                 guint                 property_id,
+                                                 GValue               *value,
+                                                 GParamSpec           *pspec);
+
+static gboolean   gimp_config_equal             (GimpConfig           *a,
+                                                 GimpConfig           *b);
+
+static GValue   * gimp_config_value_get         (GObject              *object,
+                                                 GParamSpec           *pspec);
+static GValue   * gimp_config_value_new         (GParamSpec           *pspec);
+static void       gimp_config_value_free        (GValue               *value);
+
+
+/*  public functions  */
+
+/**
+ * gimp_config_type_register:
+ * @parent_type: type from which this type will be derived
+ * @type_name:   string used as the name of the new type
+ * @pspecs:      array of #GParamSpec to install as properties on the new type
+ * @n_pspecs:    the number of param specs in @pspecs
+ *
+ * This function is a fancy wrapper around g_type_register_static().
+ * It creates a new object type as subclass of @parent_type, installs
+ * @pspecs on it and makes the new type implement the #GimpConfig
+ * interface.
+ *
+ * Returns: the newly registered #GType
+ *
+ * Since: 3.0
+ **/
+GType
+gimp_config_type_register (GType         parent_type,
+                           const gchar  *type_name,
+                           GParamSpec  **pspecs,
+                           gint          n_pspecs)
+{
+  GParamSpec **terminated_pspecs;
+  GTypeQuery   query;
+  GType        config_type;
+
+  g_return_val_if_fail (g_type_is_a (parent_type, G_TYPE_OBJECT), G_TYPE_NONE);
+  g_return_val_if_fail (type_name != NULL, G_TYPE_NONE);
+  g_return_val_if_fail (pspecs != NULL, G_TYPE_NONE);
+  g_return_val_if_fail (n_pspecs > 0, G_TYPE_NONE);
+
+  terminated_pspecs = g_new0 (GParamSpec *, n_pspecs + 1);
+
+  memcpy (terminated_pspecs, pspecs, sizeof (GParamSpec *) * n_pspecs);
+
+  g_type_query (parent_type, &query);
+
+  {
+    GTypeInfo info =
+    {
+      query.class_size,
+      (GBaseInitFunc) NULL,
+      (GBaseFinalizeFunc) NULL,
+      (GClassInitFunc) gimp_config_class_init,
+      NULL,           /* class_finalize */
+      terminated_pspecs,
+      query.instance_size,
+      0,              /* n_preallocs */
+      (GInstanceInitFunc) NULL,
+    };
+
+    const GInterfaceInfo config_info =
+    {
+      (GInterfaceInitFunc) gimp_config_config_iface_init,
+      NULL, /* interface_finalize */
+      NULL  /* interface_data     */
+    };
+
+    config_type = g_type_register_static (parent_type, type_name,
+                                          &info, 0);
+
+    g_type_add_interface_static (config_type, GIMP_TYPE_CONFIG,
+                                 &config_info);
+  }
+
+  return config_type;
+}
+
+
+/*  private functions  */
+
+static void
+gimp_config_class_init (GObjectClass  *klass,
+                        GParamSpec   **pspecs)
+{
+  gint i;
+
+  klass->set_property = gimp_config_set_property;
+  klass->get_property = gimp_config_get_property;
+
+  for (i = 0; pspecs[i] != NULL; i++)
+    {
+      GParamSpec *pspec = pspecs[i];
+      GParamSpec *copy  = gimp_config_param_spec_duplicate (pspec);
+
+      if (copy)
+        {
+          g_object_class_install_property (klass, i + 1, copy);
+        }
+      else if (! G_IS_PARAM_SPEC_OBJECT (pspec) &&
+               ! G_IS_PARAM_SPEC_POINTER (pspec))
+        {
+          /*  silently ignore object properties  */
+
+          g_warning ("%s: not supported: %s (%s)\n", G_STRFUNC,
+                     g_type_name (G_TYPE_FROM_INSTANCE (pspec)), pspec->name);
+        }
+    }
+}
+
+static void
+gimp_config_config_iface_init (GimpConfigInterface *iface)
+{
+  iface->equal = gimp_config_equal;
+}
+
+static void
+gimp_config_set_property (GObject      *object,
+                          guint         property_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  GValue *val = gimp_config_value_get (object, pspec);
+
+  g_value_copy (value, val);
+}
+
+static void
+gimp_config_get_property (GObject    *object,
+                          guint       property_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  GValue *val = gimp_config_value_get (object, pspec);
+
+  g_value_copy (val, value);
+}
+
+static gboolean
+gimp_config_equal (GimpConfig *a,
+                   GimpConfig *b)
+{
+  GList    *diff;
+  gboolean  equal = TRUE;
+
+  diff = gimp_config_diff (G_OBJECT (a), G_OBJECT (b),
+                           GIMP_CONFIG_PARAM_SERIALIZE);
+
+  if (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b))
+    {
+      GList *list;
+
+      for (list = diff; list; list = g_list_next (list))
+        {
+          GParamSpec *pspec = list->data;
+
+          if (pspec->owner_type == G_TYPE_FROM_INSTANCE (a))
+            {
+              equal = FALSE;
+              break;
+            }
+        }
+    }
+  else if (diff)
+    {
+      equal = FALSE;
+    }
+
+  g_list_free (diff);
+
+  return equal;
+}
+
+static GValue *
+gimp_config_value_get (GObject    *object,
+                       GParamSpec *pspec)
+{
+  GHashTable *properties;
+  GValue     *value;
+
+  properties = g_object_get_data (object, "gimp-config-properties");
+
+  if (! properties)
+    {
+      properties =
+        g_hash_table_new_full (g_str_hash,
+                               g_str_equal,
+                               (GDestroyNotify) g_free,
+                               (GDestroyNotify) gimp_config_value_free);
+
+      g_object_set_data_full (object, "gimp-config-properties", properties,
+                              (GDestroyNotify) g_hash_table_unref);
+    }
+
+  value = g_hash_table_lookup (properties, pspec->name);
+
+  if (! value)
+    {
+      value = gimp_config_value_new (pspec);
+      g_hash_table_insert (properties, g_strdup (pspec->name), value);
+    }
+
+  return value;
+}
+
+static GValue *
+gimp_config_value_new (GParamSpec *pspec)
+{
+  GValue *value = g_slice_new0 (GValue);
+
+  g_value_init (value, pspec->value_type);
+  g_param_value_set_default (pspec, value);
+
+  return value;
+}
+
+static void
+gimp_config_value_free (GValue *value)
+{
+  g_value_unset (value);
+  g_slice_free (GValue, value);
+}
diff --git a/libgimpconfig/gimpconfig-register.h b/libgimpconfig/gimpconfig-register.h
new file mode 100644
index 0000000000..9cfd722a45
--- /dev/null
+++ b/libgimpconfig/gimpconfig-register.h
@@ -0,0 +1,42 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
+ *
+ * gimpconfig-register.c
+ * Copyright (C) 2008-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_CONFIG_H_INSIDE__) && !defined (GIMP_CONFIG_COMPILATION)
+#error "Only <libgimpconfig/gimpconfig.h> can be included directly."
+#endif
+
+#ifndef __GIMP_CONFIG_REGISTER_H__
+#define __GIMP_CONFIG_REGISTER_H__
+
+G_BEGIN_DECLS
+
+/* For information look into the C source or the html documentation */
+
+
+GType   gimp_config_type_register (GType         parent_type,
+                                   const gchar  *type_name,
+                                   GParamSpec  **pspecs,
+                                   gint          n_pspecs);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CONFIG_REGISTER_H__ */
diff --git a/libgimpconfig/gimpconfig.def b/libgimpconfig/gimpconfig.def
index d1d8d69363..81371a8d0a 100644
--- a/libgimpconfig/gimpconfig.def
+++ b/libgimpconfig/gimpconfig.def
@@ -35,11 +35,11 @@ EXPORTS
        gimp_config_error_quark
        gimp_config_get_type
        gimp_config_is_equal_to
+       gimp_config_param_spec_duplicate
        gimp_config_path_expand
        gimp_config_path_expand_to_files
        gimp_config_path_get_type
        gimp_config_path_unexpand
-       gimp_config_param_spec_duplicate
        gimp_config_reset
        gimp_config_reset_properties
        gimp_config_reset_property
@@ -56,6 +56,7 @@ EXPORTS
        gimp_config_serialize_value
        gimp_config_string_append_escaped
        gimp_config_sync
+       gimp_config_type_register
        gimp_config_writer_close
        gimp_config_writer_comment
        gimp_config_writer_comment_mode
diff --git a/libgimpconfig/gimpconfig.h b/libgimpconfig/gimpconfig.h
index 1b2bb8e383..ae9f87fc3c 100644
--- a/libgimpconfig/gimpconfig.h
+++ b/libgimpconfig/gimpconfig.h
@@ -23,14 +23,15 @@
 
 #include <libgimpconfig/gimpconfigtypes.h>
 
-#include <libgimpconfig/gimpconfigwriter.h>
-#include <libgimpconfig/gimpconfig-iface.h>
-#include <libgimpconfig/gimpconfig-error.h>
-#include <libgimpconfig/gimpconfig-serialize.h>
 #include <libgimpconfig/gimpconfig-deserialize.h>
-#include <libgimpconfig/gimpconfig-utils.h>
+#include <libgimpconfig/gimpconfig-error.h>
+#include <libgimpconfig/gimpconfig-iface.h>
 #include <libgimpconfig/gimpconfig-params.h>
 #include <libgimpconfig/gimpconfig-path.h>
+#include <libgimpconfig/gimpconfig-register.h>
+#include <libgimpconfig/gimpconfig-serialize.h>
+#include <libgimpconfig/gimpconfig-utils.h>
+#include <libgimpconfig/gimpconfigwriter.h>
 #include <libgimpconfig/gimpscanner.h>
 
 #include <libgimpconfig/gimpcolorconfig.h>
diff --git a/libgimpconfig/meson.build b/libgimpconfig/meson.build
index e2af1f3dae..ce95f4a884 100644
--- a/libgimpconfig/meson.build
+++ b/libgimpconfig/meson.build
@@ -19,8 +19,9 @@ libgimpconfig_sources_introspectable = files(
   'gimpconfig-deserialize.c',
   'gimpconfig-error.c',
   'gimpconfig-iface.c',
-  'gimpconfig-path.c',
   'gimpconfig-params.c',
+  'gimpconfig-path.c',
+  'gimpconfig-register.c',
   'gimpconfig-serialize.c',
   'gimpconfig-utils.c',
   'gimpconfigwriter.c',
@@ -39,6 +40,7 @@ libgimpconfig_headers_introspectable = files(
   'gimpconfig-iface.h',
   'gimpconfig-params.h',
   'gimpconfig-path.h',
+  'gimpconfig-register.h',
   'gimpconfig-serialize.h',
   'gimpconfig-utils.h',
   'gimpconfigenums.h',


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