[bijiben/wip/sadiq/rewrite: 3/15] Add new item base class



commit 68d291edfce626abbd69a9273cee0909d1283c2c
Author: Mohammed Sadiq <sadiq sadiqpk org>
Date:   Fri Feb 23 22:10:58 2018 +0530

    Add new item base class

 src/notes/bjb-item.c |  661 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/notes/bjb-item.h |   96 ++++++++
 2 files changed, 757 insertions(+), 0 deletions(-)
---
diff --git a/src/notes/bjb-item.c b/src/notes/bjb-item.c
new file mode 100644
index 0000000..0271309
--- /dev/null
+++ b/src/notes/bjb-item.c
@@ -0,0 +1,661 @@
+/* bjb-item.c
+ *
+ * Copyright 2018 Mohammed Sadiq <sadiq sadiqpk 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "bjb-item"
+
+#include "config.h"
+
+#include "bjb-trace.h"
+
+#include "bjb-item.h"
+
+/**
+ * SECTION: bjb-item
+ * @title: BjbItem
+ * @short_description: The base class for Notes and Notebooks
+ *
+ * #BjbItem as such is not very useful, but used for derived classes
+ * like #BjbNote and #BjbNotebook
+ */
+
+typedef struct
+{
+  gchar *uid;
+  gchar *title;
+
+  /*
+   * The text content excluding formating. That is, plain text with no
+   * XML/HTML tags, Markdown, or whatever is used for formatting of note
+   */
+  gchar *text_content;
+
+  /*
+   * FIXME: text_content and raw_content are a kind copies, may result
+   * around 1 MiB of RAM for a few 100 notes with 1000+ characters.
+   */
+  /*
+   * The raw content of note, including HTML, Markdown, or what ever is
+   * used for formatting. This is usually the content of the note file.
+   * Should be set to NULL if text_content and raw_content are same.
+   */
+  gchar *raw_content;
+  GdkRGBA rgba;
+
+  /* The last modified time of the item */
+  gint64 modification_time;
+
+  /* The creation time of the item */
+  gint64 creation_time;
+  gboolean modified;
+} BjbItemPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (BjbItem, bjb_item, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_UID,
+  PROP_TITLE,
+  PROP_ICON,
+  PROP_THUMBNAIL,
+  PROP_RGBA,
+  PROP_CREATION_TIME,
+  PROP_MODIFICATION_TIME,
+  PROP_MODIFIED,
+  N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+static void
+bjb_item_get_property (GObject    *object,
+                       guint       prop_id,
+                       GValue     *value,
+                       GParamSpec *pspec)
+{
+  BjbItem *self = (BjbItem *)object;
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_UID:
+      g_value_set_string (value, priv->uid);
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, priv->title);
+      break;
+
+    case PROP_ICON:
+      g_value_set_object (value, bjb_item_get_icon (self, 1, FALSE));
+      break;
+
+    case PROP_THUMBNAIL:
+      g_value_set_object (value, bjb_item_get_thumbnail (self, 1, FALSE));
+      break;
+
+    case PROP_RGBA:
+      g_value_set_boxed (value, &priv->rgba);
+      break;
+
+    case PROP_CREATION_TIME:
+      g_value_set_int64 (value, priv->creation_time);
+      break;
+
+    case PROP_MODIFICATION_TIME:
+      g_value_set_int64 (value, priv->modification_time);
+      break;
+
+    case PROP_MODIFIED:
+      g_value_set_boolean (value, priv->modified);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+bjb_item_set_property (GObject      *object,
+                       guint         prop_id,
+                       const GValue *value,
+                       GParamSpec   *pspec)
+{
+  BjbItem *self = (BjbItem *)object;
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_UID:
+      bjb_item_set_uid (self, g_value_get_string (value));
+      break;
+
+    case PROP_TITLE:
+      bjb_item_set_title (self, g_value_get_string (value));
+      break;
+
+    case PROP_RGBA:
+      bjb_item_set_rgba (self, g_value_get_boxed (value));
+      break;
+
+    case PROP_CREATION_TIME:
+      priv->creation_time = g_value_get_int64 (value);
+      break;
+
+    case PROP_MODIFICATION_TIME:
+      priv->modification_time = g_value_get_int64 (value);
+      break;
+
+    case PROP_MODIFIED:
+      priv->modified = g_value_get_boolean (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+bjb_item_finalize (GObject *object)
+{
+  BjbItem *self = (BjbItem *)object;
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  BJB_ENTRY;
+
+  g_clear_pointer (&priv->uid, g_free);
+  g_clear_pointer (&priv->title, g_free);
+
+  G_OBJECT_CLASS (bjb_item_parent_class)->finalize (object);
+
+  BJB_EXIT;
+}
+
+static void
+bjb_item_real_set_title (BjbItem     *self,
+                         const gchar *title)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_assert (BJB_IS_ITEM (self));
+
+  /* If the new and old titles are same, don't bother changing */
+  if (g_strcmp0 (priv->title, title) == 0)
+    return;
+
+  g_free (priv->title);
+  priv->title = g_strdup (title);
+
+  priv->modified = TRUE;
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]);
+}
+
+static const gchar *
+bjb_item_real_get_title (BjbItem *self)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_assert (BJB_IS_ITEM (self));
+
+  return priv->title;
+}
+
+static void
+bjb_item_class_init (BjbItemClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = bjb_item_get_property;
+  object_class->set_property = bjb_item_set_property;
+  object_class->finalize = bjb_item_finalize;
+
+  klass->get_title = bjb_item_real_get_title;
+  klass->set_title = bjb_item_real_set_title;
+
+  properties[PROP_UID] =
+    g_param_spec_string ("uid",
+                         "UID",
+                         "A Unique Identifier of the item",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * BjbItem:title:
+   *
+   * The name of the Item. May be %NULL if title is empty.
+   */
+  properties[PROP_TITLE] =
+    g_param_spec_string ("title",
+                         "Title",
+                         "The name of the Item",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * BjbItem:icon:
+   *
+   * The #GdkPixbuf icon representing the item.
+   *
+   */
+  properties[PROP_ICON] =
+    g_param_spec_object ("icon",
+                         "Icon",
+                         "The icon representing the item",
+                         GDK_TYPE_PIXBUF,
+                         G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * BjbItem:thumbnail:
+   *
+   * The #GdkPixbuf thumbnail representing the item.
+   *
+   */
+  properties[PROP_THUMBNAIL] =
+    g_param_spec_object ("thumbnail",
+                         "Thumbnail",
+                         "The thumbnail of the item",
+                         GDK_TYPE_PIXBUF,
+                         G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * BjbItem:rgba:
+   *
+   * The #GdkRGBA color of the item. Set to %NULL if the item doesn't
+   * support color.
+   *
+   */
+  properties[PROP_RGBA] =
+    g_param_spec_boxed ("rgba",
+                        "RGBA Color",
+                        "The RGBA color of the item",
+                        GDK_TYPE_RGBA,
+                        G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * BjbItem:modification-time:
+   *
+   * The date and time (in seconds since UNIX epoch) the item was last
+   * modified. A value of 0 denotes that the item doesn't support this
+   * feature, or the note is item is not yet saved. To check if the
+   * item is saved use bjb_item_is_new().
+   *
+   * If the item has some uid, it should be set with bjb_item_set_uid()
+   * before getting modification-time. Otherwise, modification-time will
+   * always return the current time.
+   */
+  properties[PROP_MODIFICATION_TIME] =
+    g_param_spec_int64 ("modification-time",
+                        "Modification Time",
+                        "The time in seconds the item was last modified",
+                        0, G_MAXINT64, 0,
+                        /* TODO: explicit notify? */
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * BjbItem:creation-time:
+   *
+   * The date and time (in seconds since UNIX epoch) the item was
+   * created. A value of 0 denotes that the item doesn't support this
+   * feature.
+   *
+   * The creation time can be the time the file was created (if the
+   * backend is a file), or the time at which the data was saved to
+   * database, etc.
+   *
+   * If item isn't saved yet, the current time is returned on every
+   * request until the item is saved.
+   */
+  properties[PROP_CREATION_TIME] =
+    g_param_spec_int64 ("creation-time",
+                        "Creation Time",
+                        "The time in seconds the item was created",
+                        0, G_MAXINT64, 0,
+                        /* TODO: explicit notify? */
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  /**
+   * BjbItem:modified:
+   *
+   * The modification state of the item. %TRUE if the item
+   * has modified since last save.
+   *
+   * As derived classes can have more properties than this base class,
+   * the implementation should do bjb_item_unset_modified() after the
+   * item have saved. Also on save, uid should be set.
+   */
+  properties[PROP_MODIFIED] =
+    g_param_spec_boolean ("modified",
+                          "Modified",
+                          "The modified state of the item",
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+bjb_item_init (BjbItem *self)
+{
+}
+
+/**
+ * bjb_item_get_uid:
+ * @self: a #BjbItem
+ *
+ * Get the unique id of the item.
+ *
+ * Returns (transfer none) (nullable): the uid of the item
+ */
+const gchar *
+bjb_item_get_uid (BjbItem *self)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_val_if_fail (BJB_IS_ITEM (self), NULL);
+
+  return priv->uid;
+}
+
+/**
+ * bjb_item_set_uid:
+ * @self: a #BjbItem
+ * @uid: (nullable): a text to set as uid
+ *
+ * Set A unique identifier of the item. This can be a URL, URN,
+ * Primary key of a database table or any kind of unique string
+ * that can be used as an identifier.
+ *
+ * the uid of a saved item (ie, saved to file, goa, etc.) should not be %NULL
+ */
+void
+bjb_item_set_uid (BjbItem     *self,
+                  const gchar *uid)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_if_fail (BJB_IS_ITEM (self));
+
+  if (g_strcmp0 (priv->uid, uid) == 0)
+    return;
+
+  g_free (priv->uid);
+  priv->uid = g_strdup (uid);
+
+  priv->modified = TRUE;
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UID]);
+}
+
+/**
+ * bjb_item_get_title:
+ * @self: a #BjbItem
+ *
+ * Get the title/name of the item
+ *
+ * Returns (transfer none) (nullable): the title of the item.
+ */
+const gchar *
+bjb_item_get_title (BjbItem *self)
+{
+  gchar *title;
+
+  BJB_ENTRY;
+
+  g_return_val_if_fail (BJB_IS_ITEM (self), NULL);
+
+  title = BJB_ITEM_GET_CLASS (self)->get_title (self);
+
+  BJB_RETURN (title);
+}
+
+/**
+ * bjb_item_set_title:
+ * @self: a #BjbItem
+ * @title: (nullable): a text to set as title
+ *
+ * Set the title of the item. Can be %NULL.
+ */
+void
+bjb_item_set_title (BjbItem     *self,
+                    const gchar *title)
+{
+  BJB_ENTRY;
+
+  g_return_if_fail (BJB_IS_ITEM (self));
+
+  BJB_ITEM_GET_CLASS (self)->set_title (self, title);
+
+  BJB_EXIT;
+}
+
+/**
+ * bjb_item_get_rgba:
+ * @self: a #BjbItem
+ * @rgba (out): a location for #GdkRGBA
+ *
+ * Get the #GdkRGBA of the item
+ */
+void
+bjb_item_get_rgba (BjbItem *self,
+                   GdkRGBA *rgba)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_if_fail (BJB_IS_ITEM (self));
+  g_return_if_fail (rgba != NULL);
+
+  rgba->red = priv->rgba.red;
+  rgba->green = priv->rgba.green;
+  rgba->blue = priv->rgba.blue;
+  rgba->alpha = priv->rgba.alpha;
+}
+
+/**
+ * bjb_item_set_rgba:
+ * @self: a #BjbItem
+ * @rgba: a #GdkRGBA
+ *
+ * Set the #GdkRGBA of the item
+ */
+void
+bjb_item_set_rgba (BjbItem       *self,
+                   const GdkRGBA *rgba)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_if_fail (BJB_IS_ITEM (self));
+  g_return_if_fail (rgba != NULL);
+
+  if (gdk_rgba_equal (&priv->rgba, rgba))
+    return;
+
+  priv->rgba.red = rgba->red;
+  priv->rgba.green = rgba->green;
+  priv->rgba.blue = rgba->blue;
+  priv->rgba.alpha = rgba->alpha;
+
+  priv->modified = TRUE;
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RGBA]);
+}
+
+/**
+ * bjb_item_get_creation_time:
+ * @self: a #BjbItem
+ *
+ * Get the creation time of the item. The time returned is the
+ * seconds since UNIX epoch.
+ *
+ * A value of zero means the item isn't yet saved or the item
+ * doesn't support creation-time property. bjb_item_is_new()
+ * can be used to check if item is saved.
+ *
+ * Returns: a signed integer representing creation time
+ */
+gint64
+bjb_item_get_creation_time (BjbItem *self)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_val_if_fail (BJB_IS_ITEM (self), 0);
+
+  return priv->creation_time;
+}
+
+/**
+ * bjb_item_get_modification_time:
+ * @self: a #BjbItem
+ *
+ * Get the modification time of the item. The time returned is
+ * the seconds since UNIX epoch.
+ *
+ * A value of zero means the item isn't yet saved or the item
+ * doesn't support modification-time property. bjb_item_is_new()
+ * can be used to check if item is saved.
+ *
+ * Returns: a signed integer representing modification time
+ */
+gint64
+bjb_item_get_modification_time (BjbItem *self)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_val_if_fail (BJB_IS_ITEM (self), 0);
+
+  return priv->modification_time;
+}
+
+/**
+ * bjb_item_set_modified:
+ * @self: a #BjbItem
+ *
+ * Mark the item as modified
+ */
+void
+bjb_item_set_modified (BjbItem *self)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_if_fail (BJB_IS_ITEM (self));
+
+  priv->modified = TRUE;
+}
+
+/**
+ * bjb_item_set_modified:
+ * @self: a #BjbItem
+ *
+ * Unmark the item as modified
+ */
+void
+bjb_item_unset_modified (BjbItem *self)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_if_fail (BJB_IS_ITEM (self));
+
+  priv->modified = FALSE;
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODIFIED]);
+}
+
+/**
+ * bjb_item_is_modified:
+ * @self: a #BjbItem
+ *
+ * Get if the item is modified
+ *
+ * Returns: a boolean
+ */
+gboolean
+bjb_item_is_modified (BjbItem *self)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_val_if_fail (BJB_IS_ITEM (self), FALSE);
+
+  return priv->modified;
+}
+
+/**
+ * bjb_item_is_new:
+ * @self: a #BjbItem
+ *
+ * Get if the item is new (not yet saved).
+ *
+ * Returns: a boolean
+ */
+gboolean
+bjb_item_is_new (BjbItem *self)
+{
+  BjbItemPrivate *priv = bjb_item_get_instance_private (self);
+
+  g_return_val_if_fail (BJB_IS_ITEM (self), FALSE);
+
+  /* If uid is NULL, that means the item isn't yet saved */
+  return priv->uid == NULL;
+}
+
+/**
+ * bjb_item_get_thumbnail:
+ * @self: a #BjbItem
+ *
+ * Get the thumbnail preview icon of the item, usable for
+ * Grid view.
+ *
+ * Returns: (transfer none): the thumbnail for the item
+ */
+GdkPixbuf *
+bjb_item_get_thumbnail (BjbItem  *self,
+                        gint      scale,
+                        gboolean  force_update)
+{
+  GdkPixbuf *thumbnail;
+
+  BJB_ENTRY;
+
+  g_return_val_if_fail (BJB_IS_ITEM (self), NULL);
+
+  thumbnail = BJB_ITEM_GET_CLASS (self)->get_thumbnail (self, scale, force_update);
+
+  BJB_RETURN (thumbnail);
+}
+
+
+/**
+ * bjb_item_get_icon:
+ * @self: a #BjbItem
+ *
+ * Get the icon of the item, usable for List view.
+ *
+ * Returns: (transfer none): the icon for the item
+ */
+GdkPixbuf *
+bjb_item_get_icon (BjbItem  *self,
+                   gint      scale,
+                   gboolean  force_update)
+{
+  GdkPixbuf *icon;
+
+  BJB_ENTRY;
+
+  g_return_val_if_fail (BJB_IS_ITEM (self), NULL);
+
+  icon = BJB_ITEM_GET_CLASS (self)->get_icon (self, scale, force_update);
+
+  BJB_RETURN (icon);
+}
diff --git a/src/notes/bjb-item.h b/src/notes/bjb-item.h
new file mode 100644
index 0000000..934d65a
--- /dev/null
+++ b/src/notes/bjb-item.h
@@ -0,0 +1,96 @@
+/* bjb-item.h
+ *
+ * Copyright 2018 Mohammed Sadiq <sadiq sadiqpk 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define BJB_THUMBNAIL_HEIGHT 200
+#define BJB_THUMBNAIL_WIDTH  200
+
+#define BJB_ICON_HEIGHT (BJB_THUMBNAIL_HEIGHT / 6)
+#define BJB_ICON_WIDTH  (BJB_THUMBNAIL_WIDTH / 6)
+
+#define BJB_TYPE_ITEM (bjb_item_get_type ())
+
+G_DECLARE_DERIVABLE_TYPE (BjbItem, bjb_item, BJB, ITEM, GObject)
+
+/**
+ * BjbItemClass:
+ * @parent_class: the parent class
+ * @get_title: The vfunc to get title. Useful to override if the title
+ * is saved with formatting (like HTML/Markdown content)
+ * @set_title: The vfunc to set title. Useful to override if you want
+ * some transformations on title (like stripping some text from title).
+ * @get_title should always return a plain text without any formatting.
+ * @get_thumbnail: vfunc to return a thumbnail of the item. Usually
+ * used on Grid view
+ * @get_icon: vfunc to return an icon of the item.
+ */
+struct _BjbItemClass
+{
+  GObjectClass parent_class;
+
+  const gchar *(*get_title)     (BjbItem     *self);
+  void         (*set_title)     (BjbItem     *self,
+                                 const gchar *title);
+
+  GdkPixbuf   *(*get_thumbnail) (BjbItem     *self,
+                                 gint         scale,
+                                 gboolean     force_update);
+  GdkPixbuf   *(*get_icon)      (BjbItem     *self,
+                                 gint         scale,
+                                 gboolean     force_update);
+
+};
+
+const gchar *bjb_item_get_uid               (BjbItem       *self);
+void         bjb_item_set_uid               (BjbItem       *self,
+                                             const gchar   *uid);
+
+const gchar *bjb_item_get_title             (BjbItem       *self);
+void         bjb_item_set_title             (BjbItem       *self,
+                                             const gchar   *title);
+
+void         bjb_item_get_rgba              (BjbItem       *self,
+                                             GdkRGBA       *color);
+void         bjb_item_set_rgba              (BjbItem       *self,
+                                             const GdkRGBA *color);
+
+gint64       bjb_item_get_creation_time     (BjbItem       *self);
+gint64       bjb_item_get_modification_time (BjbItem       *self);
+
+void         bjb_item_set_modified          (BjbItem       *self);
+void         bjb_item_unset_modified        (BjbItem       *self);
+gboolean     bjb_item_is_modified           (BjbItem       *self);
+
+gboolean     bjb_item_is_new                (BjbItem       *self);
+
+GdkPixbuf   *bjb_item_get_thumbnail         (BjbItem       *self,
+                                             gint           scale,
+                                             gboolean       force_update);
+GdkPixbuf   *bjb_item_get_icon              (BjbItem       *self,
+                                             gint           scale,
+                                             gboolean       force_update);
+
+G_END_DECLS


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