[gnome-shell] st: Make BoxLayout use a Clutter layout manager internally



commit aa394754b6d78e682f99d3aff7f170bea04b0a8a
Author: Florian Müllner <fmuellner gnome org>
Date:   Tue Jul 2 21:47:44 2013 +0200

    st: Make BoxLayout use a Clutter layout manager internally
    
    With the BoxLayout containers in St and Mx and the ClutterBoxLayout
    manager, we now have three more or less diverged implementations of
    the same layout policy.
    
    While removing StBoxLayout entirely in favor of ClutterLayoutManager
    would be the fashionable thing to do, there are obvious drawbacks:
    
     - it is the only actor we have that implements the scrollable interface
     - it conveniently exposes its spacing property in CSS
     - last but not least, it is used all over the place
    
    So do the next best thing and make our implementation use the
    Clutter layout manager internally - that way, the change is
    transparent to users, while we get to refer most of the tricky
    bits to Clutter. win-win!
    
    https://bugzilla.gnome.org/show_bug.cgi?id=703810

 src/st/st-box-layout.c |  559 ++++-------------------------------------------
 1 files changed, 48 insertions(+), 511 deletions(-)
---
diff --git a/src/st/st-box-layout.c b/src/st/st-box-layout.c
index 227f597..e333dcd 100644
--- a/src/st/st-box-layout.c
+++ b/src/st/st-box-layout.c
@@ -80,11 +80,6 @@ enum {
 
 struct _StBoxLayoutPrivate
 {
-  guint         spacing;
-
-  guint         is_vertical : 1;
-  guint         is_pack_start : 1;
-
   StAdjustment *hadjustment;
   StAdjustment *vadjustment;
 };
@@ -195,16 +190,21 @@ st_box_layout_get_property (GObject    *object,
                             GParamSpec *pspec)
 {
   StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv;
+  ClutterLayoutManager *layout;
   StAdjustment *adjustment;
+  ClutterOrientation orientation;
 
   switch (property_id)
     {
     case PROP_VERTICAL:
-      g_value_set_boolean (value, priv->is_vertical);
+      layout = clutter_actor_get_layout_manager (CLUTTER_ACTOR (object));
+      orientation = clutter_box_layout_get_orientation (CLUTTER_BOX_LAYOUT (layout));
+      g_value_set_boolean (value, orientation == CLUTTER_ORIENTATION_VERTICAL);
       break;
 
     case PROP_PACK_START:
-      g_value_set_boolean (value, priv->is_pack_start);
+      layout = clutter_actor_get_layout_manager (CLUTTER_ACTOR (object));
+      g_value_set_boolean (value, clutter_box_layout_get_pack_start (CLUTTER_BOX_LAYOUT (layout)));
       break;
 
     case PROP_HADJUST:
@@ -278,337 +278,27 @@ st_box_layout_dispose (GObject *object)
 }
 
 static void
-get_content_preferred_width (StBoxLayout *self,
-                             gfloat       for_height,
-                             gfloat      *min_width_p,
-                             gfloat      *natural_width_p)
-{
-  StBoxLayoutPrivate *priv = self->priv;
-  gint n_children = 0;
-  gfloat min_width, natural_width;
-  ClutterActor *child;
-
-  min_width = 0;
-  natural_width = 0;
-
-  for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
-       child != NULL;
-       child = clutter_actor_get_next_sibling (child))
-    {
-      gfloat child_min = 0, child_nat = 0;
-      gboolean child_fill;
-
-      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
-        continue;
-
-      n_children++;
-
-      if (priv->is_vertical)
-        {
-          _st_actor_get_preferred_width (child, -1, FALSE,
-                                         &child_min, &child_nat);
-          min_width = MAX (child_min, min_width);
-          natural_width = MAX (child_nat, natural_width);
-        }
-      else
-        {
-          clutter_container_child_get (CLUTTER_CONTAINER (self), child,
-                                       "y-fill", &child_fill,
-                                       NULL);
-          _st_actor_get_preferred_width (child, for_height, child_fill,
-                                         &child_min, &child_nat);
-          min_width += child_min;
-          natural_width += child_nat;
-        }
-    }
-
-  if (!priv->is_vertical && n_children > 1)
-    {
-      min_width += priv->spacing * (n_children - 1);
-      natural_width += priv->spacing * (n_children - 1);
-    }
-
-  if (min_width_p)
-    *min_width_p = min_width;
-
-  if (natural_width_p)
-    *natural_width_p = natural_width;
-}
-
-static void
-st_box_layout_get_preferred_width (ClutterActor *actor,
-                                   gfloat        for_height,
-                                   gfloat       *min_width_p,
-                                   gfloat       *natural_width_p)
-{
-  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
-
-  st_theme_node_adjust_for_height (theme_node, &for_height);
-
-  get_content_preferred_width (ST_BOX_LAYOUT (actor), for_height,
-                               min_width_p, natural_width_p);
-
-  st_theme_node_adjust_preferred_width (theme_node,
-                                        min_width_p, natural_width_p);
-}
-
-static void
-get_content_preferred_height (StBoxLayout *self,
-                              gfloat       for_width,
-                              gfloat      *min_height_p,
-                              gfloat      *natural_height_p)
-{
-  StBoxLayoutPrivate *priv = self->priv;
-  gint n_children = 0;
-  gfloat min_height, natural_height;
-  ClutterActor *child;
-
-  min_height = 0;
-  natural_height = 0;
-
-  for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
-       child != NULL;
-       child = clutter_actor_get_next_sibling (child))
-    {
-      gfloat child_min = 0, child_nat = 0;
-      gboolean child_fill = FALSE;
-
-      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
-        continue;
-
-      n_children++;
-
-      if (priv->is_vertical)
-        {
-          clutter_container_child_get ((ClutterContainer*) self, child,
-                                       "x-fill", &child_fill,
-                                       NULL);
-        }
-      _st_actor_get_preferred_height (child,
-                                      (priv->is_vertical) ? for_width : -1,
-                                      child_fill,
-                                      &child_min,
-                                      &child_nat);
-
-      if (!priv->is_vertical)
-        {
-          min_height = MAX (child_min, min_height);
-          natural_height = MAX (child_nat, natural_height);
-        }
-      else
-        {
-          min_height += child_min;
-          natural_height += child_nat;
-        }
-    }
-
-  if (priv->is_vertical && n_children > 1)
-    {
-      min_height += priv->spacing * (n_children - 1);
-      natural_height += priv->spacing * (n_children - 1);
-    }
-
-  if (min_height_p)
-    *min_height_p = min_height;
-
-  if (natural_height_p)
-    *natural_height_p = natural_height;
-}
-
-static void
-st_box_layout_get_preferred_height (ClutterActor *actor,
-                                    gfloat        for_width,
-                                    gfloat       *min_height_p,
-                                    gfloat       *natural_height_p)
-{
-  StBoxLayout *self = ST_BOX_LAYOUT (actor);
-  StBoxLayoutPrivate *priv = self->priv;
-  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
-
-  st_theme_node_adjust_for_width (theme_node, &for_width);
-
-  if (priv->hadjustment)
-    {
-      /* If we're scrolled, the parent calls us with the width that
-       * we'll actually get, which can be smaller than the minimum
-       * width that we give our contents.
-       */
-      gfloat min_width;
-
-      get_content_preferred_width (self, -1, &min_width, NULL);
-      for_width = MAX (for_width, min_width);
-    }
-
-  get_content_preferred_height (self, for_width,
-                                min_height_p, natural_height_p);
-
-  st_theme_node_adjust_preferred_height (theme_node,
-                                         min_height_p, natural_height_p);
-}
-
-typedef struct {
-  int child_index;
-  gfloat shrink_amount;
-} BoxChildShrink;
-
-/* Sort with the greatest shrink amount first */
-static int
-compare_by_shrink_amount (const void *a,
-                          const void *b)
-{
-  float diff = ((const BoxChildShrink *)a)->shrink_amount - ((const BoxChildShrink *)b)->shrink_amount;
-  return diff < 0 ? 1 : (diff == 0 ? 0 : -1);
-}
-
-/* Sort in ascending order by child index */
-static int
-compare_by_child_index (const void *a,
-                        const void *b)
-{
-  return ((const BoxChildShrink *)a)->child_index - ((const BoxChildShrink *)b)->child_index;
-}
-
-static BoxChildShrink *
-compute_shrinks (StBoxLayout *self,
-                 gfloat       for_length,
-                 gfloat       total_shrink)
-{
-  StBoxLayoutPrivate *priv = self->priv;
-  int n_children = clutter_actor_get_n_children (CLUTTER_ACTOR (self));
-  BoxChildShrink *shrinks = g_new0 (BoxChildShrink, n_children);
-  gfloat shrink_so_far;
-  gfloat base_shrink = 0; /* the "= 0" is just to make gcc happy */
-  int n_shrink_children;
-  ClutterActor *child;
-  int i = 0;
-
-  /* The effect that we want is that all the children get an equal chance
-   * to expand from their minimum size up to the natural size. Or to put
-   * it a different way, we want to start by shrinking only the child that
-   * can shrink most, then shrink that and the next most shrinkable child,
-   * to the point where we are shrinking everything.
-   */
-
-  /* Find the amount of possible shrink for each child */
-  int n_possible_shrink_children = 0;
-  for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
-       child != NULL;
-       child = clutter_actor_get_next_sibling (child))
-    {
-      gfloat child_min, child_nat;
-      gboolean child_fill;
-
-      shrinks[i].child_index = i;
-      if (CLUTTER_ACTOR_IS_VISIBLE (child))
-        {
-          if (priv->is_vertical)
-            {
-              clutter_container_child_get ((ClutterContainer*) self, child,
-                                           "x-fill", &child_fill,
-                                           NULL);
-              _st_actor_get_preferred_height (child,
-                                              for_length, child_fill,
-                                              &child_min, &child_nat);
-            }
-          else
-            {
-              clutter_container_child_get ((ClutterContainer*) self, child,
-                                           "y-fill", &child_fill,
-                                           NULL);
-              _st_actor_get_preferred_width (child,
-                                             for_length, child_fill,
-                                             &child_min, &child_nat);
-            }
-
-          shrinks[i].shrink_amount = MAX (0., child_nat - child_min);
-          n_possible_shrink_children++;
-        }
-      else
-        {
-          shrinks[i].shrink_amount = -1.;
-        }
-
-      i++;
-    }
-
-  /* We want to process children starting from the child with the maximum available
-   * shrink, so sort in this order; !visible children end up at the end */
-  qsort (shrinks, n_children, sizeof (BoxChildShrink), compare_by_shrink_amount);
-
-  /*   +--+
-   *   |  |
-   *   |  | +--
-   *   |  | | |
-   *   |  | | | +-+
-   * --+--+-+-+-+-+----------
-   *   |  | | | | | +-+ +-+
-   *   |  | | | | | | | | |
-   * --+--+-+-+-+-+-+-+------
-   *
-   * We are trying to find the correct position for the upper line the "water mark"
-   * so that total of the portion of the bars above the line is equal to the total
-   * amount we want to shrink.
-   */
-
-  /* Start by moving the line downward, top-of-bar by top-of-bar */
-  shrink_so_far = 0;
-  for (n_shrink_children = 1; n_shrink_children <= n_possible_shrink_children; n_shrink_children++)
-    {
-      if (n_shrink_children < n_possible_shrink_children)
-        base_shrink = shrinks[n_shrink_children].shrink_amount;
-      else
-        base_shrink = 0;
-      shrink_so_far += n_shrink_children * (shrinks[n_shrink_children - 1].shrink_amount - base_shrink);
-
-      if (shrink_so_far >= total_shrink || n_shrink_children == n_possible_shrink_children)
-        break;
-    }
-
-  /* OK, we found enough shrinkage, move it back upwards to the right position */
-  base_shrink += (shrink_so_far - total_shrink) / n_shrink_children;
-  if (base_shrink < 0) /* can't shrink that much, probably round-off error */
-    base_shrink = 0;
-
-  /* Assign the portion above the base shrink line to the shrink_amount */
-  for (i = 0; i < n_shrink_children; i++)
-    shrinks[i].shrink_amount -= base_shrink;
-  for (; i < n_children; i++)
-    shrinks[i].shrink_amount = 0;
-
-  /* And sort back to their original order */
-  qsort (shrinks, n_children, sizeof (BoxChildShrink), compare_by_child_index);
-
-  return shrinks;
-}
-
-static void
 st_box_layout_allocate (ClutterActor          *actor,
                         const ClutterActorBox *box,
                         ClutterAllocationFlags flags)
 {
   StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv;
   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
+  ClutterLayoutManager *layout = clutter_actor_get_layout_manager (actor);
   ClutterActorBox content_box;
   gfloat avail_width, avail_height, min_width, natural_width, min_height, natural_height;
-  gfloat position, next_position;
-  gint n_expand_children = 0, i;
-  gfloat expand_amount, shrink_amount;
-  BoxChildShrink *shrinks = NULL;
-  gboolean flip = (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
-                   && (!priv->is_vertical);
-  ClutterActor *child;
 
-  clutter_actor_set_allocation (actor, box, flags);
+  CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->allocate (actor, box, flags);
 
   st_theme_node_get_content_box (theme_node, box, &content_box);
+  clutter_actor_box_get_size (&content_box, &avail_width, &avail_height);
 
-  avail_width  = content_box.x2 - content_box.x1;
-  avail_height = content_box.y2 - content_box.y1;
-
-  get_content_preferred_width (ST_BOX_LAYOUT (actor), avail_height,
-                               &min_width, &natural_width);
-  get_content_preferred_height (ST_BOX_LAYOUT (actor), MAX (avail_width, min_width),
-                                &min_height, &natural_height);
+  clutter_layout_manager_get_preferred_width (layout, CLUTTER_CONTAINER (actor),
+                                              avail_height,
+                                              &min_width, &natural_width);
+  clutter_layout_manager_get_preferred_height (layout, CLUTTER_CONTAINER (actor),
+                                               MAX (avail_width, min_width),
+                                               &min_height, &natural_height);
 
 
   /* update adjustments for scrolling */
@@ -643,175 +333,6 @@ st_box_layout_allocate (ClutterActor          *actor,
       prev_value = st_adjustment_get_value (priv->hadjustment);
       st_adjustment_set_value (priv->hadjustment, prev_value);
     }
-
-  if (avail_height < min_height)
-    {
-      avail_height = min_height;
-      content_box.y2 = content_box.y1 + avail_height;
-    }
-
-  if (avail_width < min_width)
-    {
-      avail_width = min_width;
-      content_box.x2 = content_box.x1 + avail_width;
-    }
-
-  if (priv->is_vertical)
-    {
-      expand_amount = MAX (0, avail_height - natural_height);
-      shrink_amount = MAX (0, natural_height - avail_height);
-    }
-  else
-    {
-      expand_amount = MAX (0, avail_width - natural_width);
-      shrink_amount = MAX (0, natural_width - avail_width);
-    }
-
-  if (expand_amount > 0)
-    {
-      /* count the number of children with expand set to TRUE */
-      n_expand_children = 0;
-      for (child = clutter_actor_get_first_child (actor);
-           child != NULL;
-           child = clutter_actor_get_next_sibling (child))
-        {
-          gboolean expand;
-
-          if (!CLUTTER_ACTOR_IS_VISIBLE (child) ||
-              clutter_actor_get_fixed_position_set (child))
-            continue;
-
-          clutter_container_child_get ((ClutterContainer *) actor,
-                                       child,
-                                       "expand", &expand,
-                                       NULL);
-          if (expand)
-            n_expand_children++;
-        }
-
-      if (n_expand_children == 0)
-        expand_amount = 0;
-    }
-  else if (shrink_amount > 0)
-    {
-      shrinks = compute_shrinks (ST_BOX_LAYOUT (actor),
-                                 priv->is_vertical ? avail_width : avail_height,
-                                 shrink_amount);
-     }
-
-  if (priv->is_vertical)
-    position = content_box.y1;
-  else if (flip)
-    position = content_box.x2;
-  else
-    position = content_box.x1;
-
-  if (priv->is_pack_start)
-    {
-      child = clutter_actor_get_last_child (actor);
-      i = clutter_actor_get_n_children (actor);
-    }
-  else
-    {
-      child = clutter_actor_get_first_child (actor);
-      i = 0;
-    }
-
-  while (child != NULL)
-    {
-      ClutterActorBox child_box;
-      gfloat child_min, child_nat, child_allocated;
-      gboolean xfill, yfill, expand;
-      StAlign xalign, yalign;
-      gdouble xalign_f, yalign_f;
-
-      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
-        goto next_child;
-
-      clutter_container_child_get ((ClutterContainer*) actor, child,
-                                   "x-fill", &xfill,
-                                   "y-fill", &yfill,
-                                   "x-align", &xalign,
-                                   "y-align", &yalign,
-                                   "expand", &expand,
-                                   NULL);
-
-      st_get_align_factors (xalign, yalign, &xalign_f, &yalign_f);
-
-      if (priv->is_vertical)
-        {
-          _st_actor_get_preferred_height (child, avail_width, xfill,
-                                          &child_min, &child_nat);
-        }
-      else
-        {
-          _st_actor_get_preferred_width (child, avail_height, yfill,
-                                         &child_min, &child_nat);
-        }
-
-      child_allocated = child_nat;
-      if (expand_amount > 0 && expand)
-        child_allocated +=  expand_amount / n_expand_children;
-      else if (shrink_amount > 0)
-        child_allocated -= shrinks[i].shrink_amount;
-
-      if (flip)
-        next_position = position - child_allocated;
-      else
-        next_position = position + child_allocated;
-
-      if (priv->is_vertical)
-        {
-          child_box.y1 = (int)(0.5 + position);
-          child_box.y2 = (int)(0.5 + next_position);
-          child_box.x1 = content_box.x1;
-          child_box.x2 = content_box.x2;
-
-          clutter_actor_allocate_align_fill (child, &child_box,
-                                             xalign_f, yalign_f,
-                                             xfill, yfill, flags);
-        }
-      else
-        {
-          if (flip)
-            {
-              child_box.x1 = (int)(0.5 + next_position);
-              child_box.x2 = (int)(0.5 + position);
-            }
-          else
-            {
-              child_box.x1 = (int)(0.5 + position);
-              child_box.x2 = (int)(0.5 + next_position);
-            }
-
-          child_box.y1 = content_box.y1;
-          child_box.y2 = content_box.y2;
-
-          clutter_actor_allocate_align_fill (child, &child_box,
-                                             xalign_f, yalign_f,
-                                             xfill, yfill, flags);
-        }
-
-      if (flip)
-        position = next_position - priv->spacing;
-      else
-        position = next_position + priv->spacing;
-
-    next_child:
-      if (priv->is_pack_start)
-        {
-          child = clutter_actor_get_previous_sibling (child);
-          i--;
-        }
-      else
-        {
-          child = clutter_actor_get_next_sibling (child);
-          i++;
-        }
-    }
-
-  if (shrinks)
-    g_free (shrinks);
 }
 
 static void
@@ -1012,13 +533,13 @@ st_box_layout_style_changed (StWidget *self)
 {
   StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (self)->priv;
   StThemeNode *theme_node = st_widget_get_theme_node (self);
-  int old_spacing = priv->spacing;
+  ClutterBoxLayout *layout;
   double spacing;
 
+  layout = CLUTTER_BOX_LAYOUT (clutter_actor_get_layout_manager (CLUTTER_ACTOR (self)));
+
   spacing = st_theme_node_get_length (theme_node, "spacing");
-  priv->spacing = (int)(spacing + 0.5);
-  if (priv->spacing != old_spacing)
-    clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
+  clutter_box_layout_set_spacing (layout, (int)(spacing + 0.5));
 
   ST_WIDGET_CLASS (st_box_layout_parent_class)->style_changed (self);
 }
@@ -1038,8 +559,6 @@ st_box_layout_class_init (StBoxLayoutClass *klass)
   object_class->dispose = st_box_layout_dispose;
 
   actor_class->allocate = st_box_layout_allocate;
-  actor_class->get_preferred_width = st_box_layout_get_preferred_width;
-  actor_class->get_preferred_height = st_box_layout_get_preferred_height;
   actor_class->apply_transform = st_box_layout_apply_transform;
 
   actor_class->paint = st_box_layout_paint;
@@ -1077,7 +596,13 @@ st_box_layout_class_init (StBoxLayoutClass *klass)
 static void
 st_box_layout_init (StBoxLayout *self)
 {
+  ClutterLayoutManager *layout;
+
   self->priv = BOX_LAYOUT_PRIVATE (self);
+  layout = clutter_box_layout_new ();
+  g_signal_connect_swapped (layout, "layout-changed",
+                            G_CALLBACK (clutter_actor_queue_relayout), self);
+  clutter_actor_set_layout_manager (CLUTTER_ACTOR (self), layout);
 }
 
 /**
@@ -1105,13 +630,18 @@ void
 st_box_layout_set_vertical (StBoxLayout *box,
                             gboolean     vertical)
 {
+  ClutterLayoutManager *layout;
+  ClutterOrientation orientation;
+
   g_return_if_fail (ST_IS_BOX_LAYOUT (box));
 
-  if (box->priv->is_vertical != vertical)
-    {
-      box->priv->is_vertical = vertical;
-      clutter_actor_queue_relayout ((ClutterActor*) box);
+  layout = clutter_actor_get_layout_manager (CLUTTER_ACTOR (box));
+  orientation = vertical ? CLUTTER_ORIENTATION_VERTICAL
+                         : CLUTTER_ORIENTATION_HORIZONTAL;
 
+  if (clutter_box_layout_get_orientation (CLUTTER_BOX_LAYOUT (layout)) != orientation)
+    {
+      clutter_box_layout_set_orientation (CLUTTER_BOX_LAYOUT (layout), orientation);
       g_object_notify (G_OBJECT (box), "vertical");
     }
 }
@@ -1127,9 +657,14 @@ st_box_layout_set_vertical (StBoxLayout *box,
 gboolean
 st_box_layout_get_vertical (StBoxLayout *box)
 {
+  ClutterLayoutManager *layout;
+  ClutterOrientation orientation;
+
   g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE);
 
-  return box->priv->is_vertical;
+  layout = clutter_actor_get_layout_manager (CLUTTER_ACTOR (box));
+  orientation = clutter_box_layout_get_orientation (CLUTTER_BOX_LAYOUT (layout));
+  return orientation == CLUTTER_ORIENTATION_VERTICAL;
 }
 
 /**
@@ -1144,13 +679,15 @@ void
 st_box_layout_set_pack_start (StBoxLayout *box,
                               gboolean     pack_start)
 {
+  ClutterBoxLayout *layout;
+
   g_return_if_fail (ST_IS_BOX_LAYOUT (box));
 
-  if (box->priv->is_pack_start != pack_start)
-    {
-      box->priv->is_pack_start = pack_start;
-      clutter_actor_queue_relayout ((ClutterActor*) box);
+  layout = CLUTTER_BOX_LAYOUT (clutter_actor_get_layout_manager (CLUTTER_ACTOR (box)));
 
+  if (clutter_box_layout_get_pack_start (layout) != pack_start)
+    {
+      clutter_box_layout_set_pack_start (layout, pack_start);
       g_object_notify (G_OBJECT (box), "pack-start");
     }
 }
@@ -1168,5 +705,5 @@ st_box_layout_get_pack_start (StBoxLayout *box)
 {
   g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE);
 
-  return box->priv->is_pack_start;
+  return clutter_box_layout_get_pack_start (CLUTTER_BOX_LAYOUT (clutter_actor_get_layout_manager 
(CLUTTER_ACTOR (box))));
 }


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