[libdazzle] dock-bin: simplify and fix child visible state tracking



commit 5b6883eaa19cb2a3206205a09dd34e8952523e60
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jul 3 01:49:47 2017 -0700

    dock-bin: simplify and fix child visible state tracking
    
    This was a complex disaster. It's still not great, but it is much easier
    to reason about than it was.

 src/panel/dzl-dock-bin.c |  369 ++++++++++++++++------------------------------
 1 files changed, 124 insertions(+), 245 deletions(-)
---
diff --git a/src/panel/dzl-dock-bin.c b/src/panel/dzl-dock-bin.c
index c5be083..d73f436 100644
--- a/src/panel/dzl-dock-bin.c
+++ b/src/panel/dzl-dock-bin.c
@@ -127,6 +127,9 @@ typedef struct
 static void dzl_dock_bin_init_buildable_iface (GtkBuildableIface    *iface);
 static void dzl_dock_bin_init_dock_iface      (DzlDockInterface     *iface);
 static void dzl_dock_bin_init_dock_item_iface (DzlDockItemInterface *iface);
+static void dzl_dock_bin_create_edge          (DzlDockBin           *self,
+                                               DzlDockBinChild      *child,
+                                               DzlDockBinChildType   type);
 
 G_DEFINE_TYPE_EXTENDED (DzlDockBin, dzl_dock_bin, GTK_TYPE_CONTAINER, 0,
                         G_ADD_PRIVATE (DzlDockBin)
@@ -155,196 +158,161 @@ enum {
 
 static GParamSpec *properties [N_PROPS];
 static GParamSpec *child_properties [N_CHILD_PROPS];
+static const gchar *pinned_names[] = {
+  "left-pinned",
+  "right-pinned",
+  "top-pinned",
+  "bottom-pinned",
+  NULL,
+  NULL
+};
+static const gchar *visible_names[] = {
+  "left-visible",
+  "right-visible",
+  "top-visible",
+  "bottom-visible",
+  NULL,
+  NULL
+};
 
-static gint
-dzl_dock_bin_child_compare (gconstpointer a,
-                            gconstpointer b)
-{
-  const DzlDockBinChild *child_a = a;
-  const DzlDockBinChild *child_b = b;
-
-  if (child_a->type == DZL_DOCK_BIN_CHILD_CENTER)
-    return 1;
-  else if (child_b->type == DZL_DOCK_BIN_CHILD_CENTER)
-    return -1;
-
-  if ((child_a->pinned ^ child_b->pinned) != 0)
-    return child_a->pinned - child_b->pinned;
-
-  return child_a->priority - child_b->priority;
-}
-
-static void
-dzl_dock_bin_resort_children (DzlDockBin *self)
+static DzlDockBinChild *
+dzl_dock_bin_get_child_typed (DzlDockBin          *self,
+                              DzlDockBinChildType  type)
 {
   DzlDockBinPrivate *priv = dzl_dock_bin_get_instance_private (self);
 
   g_assert (DZL_IS_DOCK_BIN (self));
+  g_assert (type >= DZL_DOCK_BIN_CHILD_LEFT);
+  g_assert (type < LAST_DZL_DOCK_BIN_CHILD);
 
-  /*
-   * Sort the children by priority/pinned status, but do not change
-   * the position of the DZL_DOCK_BIN_CHILD_CENTER child. It should
-   * always be at the last position.
-   */
+  for (guint i = 0; i < G_N_ELEMENTS (priv->children); i++)
+    {
+      if (priv->children[i].type == type)
+        return &priv->children[i];
+    }
 
-  g_qsort_with_data (&priv->children[0],
-                     DZL_DOCK_BIN_CHILD_CENTER,
-                     sizeof (DzlDockBinChild),
-                     (GCompareDataFunc)dzl_dock_bin_child_compare,
-                     NULL);
+  g_assert_not_reached ();
 
-  gtk_widget_queue_allocate (GTK_WIDGET (self));
+  return NULL;
 }
 
-static GAction *
-dzl_dock_bin_get_visible_action_for_type (DzlDockBin          *self,
-                                          DzlDockBinChildType  type)
+static GtkWidget *
+get_child_widget (DzlDockBin          *self,
+                  DzlDockBinChildType  type)
 {
-  DzlDockBinPrivate *priv = dzl_dock_bin_get_instance_private (self);
-  const gchar *name = NULL;
-
-  g_assert (DZL_IS_DOCK_BIN (self));
-
   switch (type)
     {
     case DZL_DOCK_BIN_CHILD_LEFT:
-      name = "left-visible";
-      break;
+      return dzl_dock_bin_get_left_edge (self);
 
     case DZL_DOCK_BIN_CHILD_RIGHT:
-      name = "right-visible";
-      break;
+      return dzl_dock_bin_get_right_edge (self);
 
     case DZL_DOCK_BIN_CHILD_TOP:
-      name = "top-visible";
-      break;
+      return dzl_dock_bin_get_top_edge (self);
 
     case DZL_DOCK_BIN_CHILD_BOTTOM:
-      name = "bottom-visible";
-      break;
+      return dzl_dock_bin_get_bottom_edge (self);
 
     case DZL_DOCK_BIN_CHILD_CENTER:
     case LAST_DZL_DOCK_BIN_CHILD:
     default:
-      g_assert_not_reached ();
+      return NULL;
     }
-
-  return g_action_map_lookup_action (G_ACTION_MAP (priv->actions), name);
 }
 
 static gboolean
-get_visible (DzlDockBin  *self,
-             const gchar *action_name)
+get_visible (DzlDockBin          *self,
+             DzlDockBinChildType  type)
 {
-  DzlDockBinPrivate *priv = dzl_dock_bin_get_instance_private (self);
-  GAction *action;
+  GtkWidget *child;
 
   g_assert (DZL_IS_DOCK_BIN (self));
-  g_assert (action_name != NULL);
-
-  action = g_action_map_lookup_action (G_ACTION_MAP (priv->actions), action_name);
-
-  if (action != NULL)
-    {
-      GVariant *v = g_action_get_state (action);
-      gboolean ret = v ? g_variant_get_boolean (v) : FALSE;
-
-      g_clear_pointer (&v, g_variant_unref);
+  g_assert (type >= DZL_DOCK_BIN_CHILD_LEFT);
+  g_assert (type <= DZL_DOCK_BIN_CHILD_BOTTOM);
 
-      return ret;
-    }
+  child = get_child_widget (self, type);
 
-  return FALSE;
+  return DZL_IS_DOCK_REVEALER (child) &&
+         dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (child));
 }
 
 static void
-dzl_dock_bin_update_actions (DzlDockBin *self)
+set_visible (DzlDockBin          *self,
+             DzlDockBinChildType  type,
+             gboolean             visible)
 {
-  DzlDockBinPrivate *priv = dzl_dock_bin_get_instance_private (self);
-  guint i;
+  GtkWidget *child;
 
   g_assert (DZL_IS_DOCK_BIN (self));
+  g_assert (type >= DZL_DOCK_BIN_CHILD_LEFT);
+  g_assert (type <= DZL_DOCK_BIN_CHILD_BOTTOM);
 
-  /*
-   * We need to walk each of the children edges looking for widgets
-   * that are visible. If so, we need to keep the edge action enabled.
-   * Otherwise disable it, so any buttons representing the action get
-   * properly desensitized.
-   */
+  child = get_child_widget (self, type);
 
-  for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
+  if (DZL_IS_DOCK_REVEALER (child))
     {
-      DzlDockBinChild *child = &priv->children [i];
-      GAction *action;
-      gboolean enabled = FALSE;
-
-      if (child->type == DZL_DOCK_BIN_CHILD_CENTER)
-        continue;
-
-      action = dzl_dock_bin_get_visible_action_for_type (self, child->type);
-
-      if (child->widget != NULL)
-        enabled = dzl_dock_item_has_widgets (DZL_DOCK_ITEM (child->widget));
-
-      g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled);
+      dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (child), visible);
+      g_object_notify (G_OBJECT (self), visible_names [type]);
+      gtk_widget_queue_resize (GTK_WIDGET (self));
     }
 }
 
-static gboolean
-map_boolean_to_variant (GBinding     *binding,
-                        const GValue *from_value,
-                        GValue       *to_value,
-                        gpointer      user_data)
+static gint
+dzl_dock_bin_child_compare (gconstpointer a,
+                            gconstpointer b)
 {
-  g_assert (G_IS_BINDING (binding));
+  const DzlDockBinChild *child_a = a;
+  const DzlDockBinChild *child_b = b;
 
-  if (g_value_get_boolean (from_value))
-    g_value_set_variant (to_value, g_variant_new_boolean (TRUE));
-  else
-    g_value_set_variant (to_value, g_variant_new_boolean (FALSE));
+  if (child_a->type == DZL_DOCK_BIN_CHILD_CENTER)
+    return 1;
+  else if (child_b->type == DZL_DOCK_BIN_CHILD_CENTER)
+    return -1;
 
-  return TRUE;
+  if ((child_a->pinned ^ child_b->pinned) != 0)
+    return child_a->pinned - child_b->pinned;
+
+  return child_a->priority - child_b->priority;
 }
 
-static DzlDockBinChild *
-dzl_dock_bin_get_child (DzlDockBin *self,
-                        GtkWidget  *widget)
+static void
+dzl_dock_bin_resort_children (DzlDockBin *self)
 {
   DzlDockBinPrivate *priv = dzl_dock_bin_get_instance_private (self);
-  guint i;
 
   g_assert (DZL_IS_DOCK_BIN (self));
-  g_assert (GTK_IS_WIDGET (widget));
 
-  for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
-    {
-      DzlDockBinChild *child = &priv->children [i];
-
-      if ((GtkWidget *)child->widget == widget)
-        return child;
-    }
+  /*
+   * Sort the children by priority/pinned status, but do not change
+   * the position of the DZL_DOCK_BIN_CHILD_CENTER child. It should
+   * always be at the last position.
+   */
 
-  g_assert_not_reached ();
+  g_qsort_with_data (&priv->children[0],
+                     DZL_DOCK_BIN_CHILD_CENTER,
+                     sizeof (DzlDockBinChild),
+                     (GCompareDataFunc)dzl_dock_bin_child_compare,
+                     NULL);
 
-  return NULL;
+  gtk_widget_queue_allocate (GTK_WIDGET (self));
 }
 
 static DzlDockBinChild *
-dzl_dock_bin_get_child_typed (DzlDockBin          *self,
-                              DzlDockBinChildType  type)
+dzl_dock_bin_get_child (DzlDockBin *self,
+                        GtkWidget  *widget)
 {
   DzlDockBinPrivate *priv = dzl_dock_bin_get_instance_private (self);
   guint i;
 
   g_assert (DZL_IS_DOCK_BIN (self));
-  g_assert (type >= DZL_DOCK_BIN_CHILD_LEFT);
-  g_assert (type < LAST_DZL_DOCK_BIN_CHILD);
+  g_assert (GTK_IS_WIDGET (widget));
 
   for (i = 0; i < G_N_ELEMENTS (priv->children); i++)
     {
       DzlDockBinChild *child = &priv->children [i];
 
-      if (child->type == type)
+      if ((GtkWidget *)child->widget == widget)
         return child;
     }
 
@@ -990,41 +958,6 @@ dzl_dock_bin_size_allocate (GtkWidget     *widget,
 }
 
 static void
-dzl_dock_bin_visible_change_state (GSimpleAction *action,
-                                   GVariant      *state,
-                                   gpointer       user_data)
-{
-  DzlDockBin *self = user_data;
-  DzlDockBinChild *child;
-  DzlDockBinChildType type;
-  const gchar *action_name;
-  gboolean reveal_child;
-
-  g_assert (DZL_IS_DOCK_BIN (self));
-  g_assert (G_IS_SIMPLE_ACTION (action));
-  g_assert (state != NULL);
-  g_assert (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN));
-
-  action_name = g_action_get_name (G_ACTION (action));
-  reveal_child = g_variant_get_boolean (state);
-
-  if (g_str_has_prefix (action_name, "left"))
-    type = DZL_DOCK_BIN_CHILD_LEFT;
-  else if (g_str_has_prefix (action_name, "right"))
-    type = DZL_DOCK_BIN_CHILD_RIGHT;
-  else if (g_str_has_prefix (action_name, "top"))
-    type = DZL_DOCK_BIN_CHILD_TOP;
-  else if (g_str_has_prefix (action_name, "bottom"))
-    type = DZL_DOCK_BIN_CHILD_BOTTOM;
-  else
-    return;
-
-  child = dzl_dock_bin_get_child_typed (self, type);
-
-  dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (child->widget), reveal_child);
-}
-
-static void
 dzl_dock_bin_set_child_pinned (DzlDockBin *self,
                                GtkWidget  *widget,
                                gboolean    pinned)
@@ -1531,9 +1464,9 @@ dzl_dock_bin_create_edge (DzlDockBin          *self,
   DzlDockBinPrivate *priv = dzl_dock_bin_get_instance_private (self);
   g_autoptr(GSimpleActionGroup) map = NULL;
   g_autoptr(GAction) pinned = NULL;
-  const gchar *name = NULL;
-  GAction *action;
-  gboolean reveal_child = FALSE;
+  g_autoptr(GPropertyAction) visible = NULL;
+  const gchar *visible_name;
+  const gchar *pinned_name;
 
   g_assert (DZL_IS_DOCK_BIN (self));
   g_assert (child != NULL);
@@ -1556,67 +1489,43 @@ dzl_dock_bin_create_edge (DzlDockBin          *self,
       return;
     }
 
-  /*
-   * If the user set the initial state for the edge before we created the
-   * edge, the value for it will be the state to the action. We need to
-   * grab that and apply it for our initial state.
-   */
-  switch (type)
-    {
-    case DZL_DOCK_BIN_CHILD_LEFT:   reveal_child = get_visible (self, "left-visible");   break;
-    case DZL_DOCK_BIN_CHILD_RIGHT:  reveal_child = get_visible (self, "right-visible");  break;
-    case DZL_DOCK_BIN_CHILD_TOP:    reveal_child = get_visible (self, "top-visible");    break;
-    case DZL_DOCK_BIN_CHILD_BOTTOM: reveal_child = get_visible (self, "bottom-visible"); break;
-    case DZL_DOCK_BIN_CHILD_CENTER:
-    case LAST_DZL_DOCK_BIN_CHILD:
-    default:
-      break;
-    }
-
   g_object_set (child->widget,
                 "edge", (GtkPositionType)type,
-                "reveal-child", reveal_child,
+                "reveal-child", FALSE,
                 NULL);
 
   gtk_widget_set_parent (g_object_ref_sink (child->widget), GTK_WIDGET (self));
 
-  action = dzl_dock_bin_get_visible_action_for_type (self, type);
-  g_object_bind_property_full (child->widget, "reveal-child",
-                               action, "state",
-                               G_BINDING_SYNC_CREATE,
-                               map_boolean_to_variant,
-                               NULL, NULL, NULL);
-
   dzl_dock_item_adopt (DZL_DOCK_ITEM (self), DZL_DOCK_ITEM (child->widget));
 
   /* Action for panel children to easily activate */
   map = g_simple_action_group_new ();
-  pinned = dzl_child_property_action_new ("pinned",
-                                          GTK_CONTAINER (self),
-                                          child->widget,
-                                          "pinned");
+  pinned = dzl_child_property_action_new ("pinned", GTK_CONTAINER (self), child->widget, "pinned");
   g_action_map_add_action (G_ACTION_MAP (map), pinned);
   gtk_widget_insert_action_group (child->widget, "panel", G_ACTION_GROUP (map));
   g_clear_object (&pinned);
 
-  /* Action for global widgetry to activate */
-  if (child->type == DZL_DOCK_BIN_CHILD_LEFT)
-    name = "left-pinned";
-  else if (child->type == DZL_DOCK_BIN_CHILD_RIGHT)
-    name = "right-pinned";
-  else if (child->type == DZL_DOCK_BIN_CHILD_TOP)
-    name = "top-pinned";
-  else if (child->type == DZL_DOCK_BIN_CHILD_BOTTOM)
-    name = "bottom-pinned";
-  pinned = dzl_child_property_action_new (name,
+  visible_name = visible_names [child->type];
+  pinned_name = pinned_names [child->type];
+
+  /* Add our pinned action */
+  pinned = dzl_child_property_action_new (pinned_name,
                                           GTK_CONTAINER (self),
                                           child->widget,
                                           "pinned");
   g_action_map_add_action (G_ACTION_MAP (priv->actions), pinned);
 
+  /* Add our visible action */
+  visible = g_property_action_new (visible_name, self, visible_name);
+  g_action_map_add_action (G_ACTION_MAP (priv->actions), G_ACTION (visible));
+
   if (child->pinned)
     gtk_style_context_add_class (gtk_widget_get_style_context (child->widget),
                                  DZL_DOCK_BIN_STYLE_CLASS_PINNED);
+
+  g_object_notify (G_OBJECT (self), visible_name);
+
+  gtk_widget_queue_resize (GTK_WIDGET (self));
 }
 
 static void
@@ -1710,19 +1619,19 @@ dzl_dock_bin_get_property (GObject    *object,
   switch (prop_id)
     {
     case PROP_LEFT_VISIBLE:
-      g_value_set_boolean (value, get_visible (self, "left-visible"));
+      g_value_set_boolean (value, get_visible (self, DZL_DOCK_BIN_CHILD_LEFT));
       break;
 
     case PROP_RIGHT_VISIBLE:
-      g_value_set_boolean (value, get_visible (self, "right-visible"));
+      g_value_set_boolean (value, get_visible (self, DZL_DOCK_BIN_CHILD_RIGHT));
       break;
 
     case PROP_TOP_VISIBLE:
-      g_value_set_boolean (value, get_visible (self, "top-visible"));
+      g_value_set_boolean (value, get_visible (self, DZL_DOCK_BIN_CHILD_TOP));
       break;
 
     case PROP_BOTTOM_VISIBLE:
-      g_value_set_boolean (value, get_visible (self, "bottom-visible"));
+      g_value_set_boolean (value, get_visible (self, DZL_DOCK_BIN_CHILD_BOTTOM));
       break;
 
     case PROP_MANAGER:
@@ -1741,36 +1650,23 @@ dzl_dock_bin_set_property (GObject      *object,
                            GParamSpec   *pspec)
 {
   DzlDockBin *self = DZL_DOCK_BIN (object);
-  DzlDockBinChild *child;
 
   switch (prop_id)
     {
     case PROP_LEFT_VISIBLE:
-      child = dzl_dock_bin_get_child_typed (self, DZL_DOCK_BIN_CHILD_LEFT);
-      if (child->widget)
-        dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (child->widget),
-                                            g_value_get_boolean (value));
+      set_visible (self, DZL_DOCK_BIN_CHILD_LEFT, g_value_get_boolean (value));
       break;
 
     case PROP_RIGHT_VISIBLE:
-      child = dzl_dock_bin_get_child_typed (self, DZL_DOCK_BIN_CHILD_RIGHT);
-      if (child->widget)
-        dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (child->widget),
-                                            g_value_get_boolean (value));
+      set_visible (self, DZL_DOCK_BIN_CHILD_RIGHT, g_value_get_boolean (value));
       break;
 
     case PROP_TOP_VISIBLE:
-      child = dzl_dock_bin_get_child_typed (self, DZL_DOCK_BIN_CHILD_TOP);
-      if (child->widget)
-        dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (child->widget),
-                                            g_value_get_boolean (value));
+      set_visible (self, DZL_DOCK_BIN_CHILD_TOP, g_value_get_boolean (value));
       break;
 
     case PROP_BOTTOM_VISIBLE:
-      child = dzl_dock_bin_get_child_typed (self, DZL_DOCK_BIN_CHILD_BOTTOM);
-      if (child->widget)
-        dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (child->widget),
-                                            g_value_get_boolean (value));
+      set_visible (self, DZL_DOCK_BIN_CHILD_BOTTOM, g_value_get_boolean (value));
       break;
 
     case PROP_MANAGER:
@@ -1880,21 +1776,13 @@ dzl_dock_bin_init (DzlDockBin *self)
   static GtkTargetEntry drag_entries[] = {
     { (gchar *)"DZL_DOCK_BIN_WIDGET", GTK_TARGET_SAME_APP, 0 },
   };
-  static const GActionEntry entries[] = {
-    { "left-visible", NULL, NULL, "false", dzl_dock_bin_visible_change_state },
-    { "right-visible", NULL, NULL, "false", dzl_dock_bin_visible_change_state },
-    { "top-visible", NULL, NULL, "false", dzl_dock_bin_visible_change_state },
-    { "bottom-visible", NULL, NULL, "false", dzl_dock_bin_visible_change_state },
-  };
+  g_autoptr(GPropertyAction) left = NULL;
+  g_autoptr(GPropertyAction) right = NULL;
+  g_autoptr(GPropertyAction) bottom = NULL;
+  g_autoptr(GPropertyAction) top = NULL;
 
   gtk_widget_set_has_window (GTK_WIDGET (self), TRUE);
 
-  priv->actions = g_simple_action_group_new ();
-  g_action_map_add_action_entries (G_ACTION_MAP (priv->actions),
-                                   entries, G_N_ELEMENTS (entries),
-                                   self);
-  gtk_widget_insert_action_group (GTK_WIDGET (self), "dockbin", G_ACTION_GROUP (priv->actions));
-
   dzl_dock_bin_create_pan_gesture (self);
 
   gtk_drag_dest_set (GTK_WIDGET (self),
@@ -1911,6 +1799,9 @@ dzl_dock_bin_init (DzlDockBin *self)
   dzl_dock_bin_init_child (self, &priv->children [2], DZL_DOCK_BIN_CHILD_BOTTOM);
   dzl_dock_bin_init_child (self, &priv->children [3], DZL_DOCK_BIN_CHILD_TOP);
   dzl_dock_bin_init_child (self, &priv->children [4], DZL_DOCK_BIN_CHILD_CENTER);
+
+  priv->actions = g_simple_action_group_new ();
+  gtk_widget_insert_action_group (GTK_WIDGET (self), "dockbin", G_ACTION_GROUP (priv->actions));
 }
 
 GtkWidget *
@@ -2064,8 +1955,7 @@ dzl_dock_bin_add_child (GtkBuildable *buildable,
   else
     parent = dzl_dock_bin_get_left_edge (self);
 
-  if (DZL_IS_DOCK_BIN_EDGE (parent))
-    gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (child));
+  gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (child));
 }
 
 static GObject *
@@ -2114,9 +2004,9 @@ dzl_dock_bin_present_child (DzlDockItem *item,
       DzlDockBinChild *child = &priv->children [i];
 
       if (DZL_IS_DOCK_BIN_EDGE (child->widget) &&
-          gtk_widget_is_ancestor (GTK_WIDGET (child->widget), child->widget))
+          gtk_widget_is_ancestor (GTK_WIDGET (widget), child->widget))
         {
-          dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (child->widget), TRUE);
+          set_visible (self, child->type, TRUE);
           return;
         }
     }
@@ -2212,21 +2102,10 @@ dzl_dock_bin_minimize (DzlDockItem     *item,
 }
 
 static void
-dzl_dock_bin_update_visibility (DzlDockItem *item)
-{
-  DzlDockBin *self = (DzlDockBin *)item;
-
-  g_assert (DZL_IS_DOCK_BIN (self));
-
-  dzl_dock_bin_update_actions (self);
-}
-
-static void
 dzl_dock_bin_init_dock_item_iface (DzlDockItemInterface *iface)
 {
   iface->present_child = dzl_dock_bin_present_child;
   iface->get_child_visible = dzl_dock_bin_get_child_visible;
   iface->set_child_visible = dzl_dock_bin_set_child_visible;
   iface->minimize = dzl_dock_bin_minimize;
-  iface->update_visibility = dzl_dock_bin_update_visibility;
 }


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