[gnome-control-center/fractional-scaling] Display: adapt display config to new Mutter interface with scaling per mode



commit 5689bb7f99a47790b08958929b4b06a21e8aecf9
Author: Marco Trevisan (Treviño) <mail 3v1n0 net>
Date:   Wed Jun 7 20:36:17 2017 +0800

    Display: adapt display config to new Mutter interface with scaling per mode
    
    New Mutter GetCurrentState now provides a list of available scales,
    we need to use these values in order to define an UI that shows them all.

 panels/display/cc-display-config-dbus.c |   84 ++++++++++---------
 panels/display/cc-display-config-rr.c   |   19 ++---
 panels/display/cc-display-config.c      |   12 ++--
 panels/display/cc-display-config.h      |    9 +--
 panels/display/cc-display-panel.c       |  135 +++++++++++++++++++++++--------
 5 files changed, 165 insertions(+), 94 deletions(-)
---
diff --git a/panels/display/cc-display-config-dbus.c b/panels/display/cc-display-config-dbus.c
index 29114ff..08d08d4 100644
--- a/panels/display/cc-display-config-dbus.c
+++ b/panels/display/cc-display-config-dbus.c
@@ -21,17 +21,17 @@
 
 #include "cc-display-config-dbus.h"
 
-#define MODE_FORMAT "(iiddu)"
+#define MODE_FORMAT "(iiddadu)"
 #define MODES_FORMAT "a" MODE_FORMAT
-
 #define MONITOR_SPEC_FORMAT "(ssss)"
 #define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})"
 #define MONITORS_FORMAT "a" MONITOR_FORMAT
 
-#define LOGICAL_MONITOR_FORMAT "(iiduba" MONITOR_SPEC_FORMAT "a{sv})"
+#define LOGICAL_MONITOR_MONITORS_FORMAT "a" MONITOR_SPEC_FORMAT
+#define LOGICAL_MONITOR_FORMAT "(iidub" LOGICAL_MONITOR_MONITORS_FORMAT "a{sv})"
 #define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT
 
-#define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "ad" "a{sv})"
+#define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "a{sv})"
 
 typedef enum _CcDisplayModeFlags
 {
@@ -47,6 +47,7 @@ struct _CcDisplayModeDBus
   int height;
   double refresh_rate;
   double preferred_scale;
+  GArray *supported_scales;
   guint32 flags;
 };
 
@@ -80,6 +81,28 @@ cc_display_mode_dbus_get_resolution (CcDisplayMode *pself,
     *h = self->height;
 }
 
+static const double *
+cc_display_mode_dbus_get_supported_scales (CcDisplayMode *pself)
+{
+  CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
+
+  return (const double *) self->supported_scales->data;
+}
+
+static gboolean
+cc_display_mode_dbus_is_supported_scale (CcDisplayMode *pself,
+                                         double scale)
+{
+  CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (pself);
+
+  guint i;
+  for (i = 0; i < self->supported_scales->len; i++)
+    if (g_array_index (self->supported_scales, double, i) == scale)
+      return TRUE;
+  return FALSE;
+}
+
+
 static gboolean
 cc_display_mode_dbus_is_interlaced (CcDisplayMode *pself)
 {
@@ -106,11 +129,16 @@ cc_display_mode_dbus_get_freq_f (CcDisplayMode *pself)
 static void
 cc_display_mode_dbus_init (CcDisplayModeDBus *self)
 {
+  self->supported_scales = g_array_new (TRUE, TRUE, sizeof (double));
 }
 
 static void
 cc_display_mode_dbus_finalize (GObject *object)
 {
+  CcDisplayModeDBus *self = CC_DISPLAY_MODE_DBUS (object);
+
+  g_array_free (self->supported_scales, TRUE);
+
   G_OBJECT_CLASS (cc_display_mode_dbus_parent_class)->finalize (object);
 }
 
@@ -123,6 +151,7 @@ cc_display_mode_dbus_class_init (CcDisplayModeDBusClass *klass)
   gobject_class->finalize = cc_display_mode_dbus_finalize;
 
   parent_class->get_resolution = cc_display_mode_dbus_get_resolution;
+  parent_class->get_supported_scales = cc_display_mode_dbus_get_supported_scales;
   parent_class->is_interlaced = cc_display_mode_dbus_is_interlaced;
   parent_class->get_freq = cc_display_mode_dbus_get_freq;
   parent_class->get_freq_f = cc_display_mode_dbus_get_freq_f;
@@ -131,6 +160,8 @@ cc_display_mode_dbus_class_init (CcDisplayModeDBusClass *klass)
 static CcDisplayModeDBus *
 cc_display_mode_dbus_new (GVariant *variant)
 {
+  double d;
+  GVariantIter *scales_iter;
   CcDisplayModeDBus *self = g_object_new (CC_TYPE_DISPLAY_MODE_DBUS, NULL);
 
   g_variant_get (variant, MODE_FORMAT,
@@ -138,8 +169,14 @@ cc_display_mode_dbus_new (GVariant *variant)
                  &self->height,
                  &self->refresh_rate,
                  &self->preferred_scale,
+                 &scales_iter,
                  &self->flags);
 
+  while (g_variant_iter_next (scales_iter, "d", &d))
+    g_array_append_val (self->supported_scales, d);
+
+  g_variant_iter_free (scales_iter);
+
   return self;
 }
 
@@ -262,9 +299,6 @@ static void
 cc_display_config_dbus_ensure_gapless (CcDisplayConfigDBus *self);
 static void
 cc_display_config_dbus_make_linear (CcDisplayConfigDBus *self);
-static gboolean
-cc_display_config_dbus_is_supported_scale (CcDisplayConfigDBus *self,
-                                           double scale);
 
 
 static const char *
@@ -623,7 +657,10 @@ cc_display_monitor_dbus_set_scale (CcDisplayMonitor *pself,
 {
   CcDisplayMonitorDBus *self = CC_DISPLAY_MONITOR_DBUS (pself);
 
-  if (!cc_display_config_dbus_is_supported_scale (self->config, scale))
+  if (!self->current_mode)
+    return;
+
+  if (!cc_display_mode_dbus_is_supported_scale (self->current_mode, scale))
     return;
 
   if (!self->logical_monitor)
@@ -812,8 +849,6 @@ struct _CcDisplayConfigDBus
   gboolean supports_changing_layout_mode;
   CcDisplayLayoutMode layout_mode;
 
-  GArray *supported_scales;
-
   GList *monitors;
   CcDisplayMonitorDBus *primary;
 
@@ -1115,14 +1150,6 @@ cc_display_config_dbus_apply (CcDisplayConfig *pself,
   return config_apply (self, CC_DISPLAY_CONFIG_METHOD_PERSISTENT, error);
 }
 
-static const double *
-cc_display_config_dbus_get_supported_scales (CcDisplayConfig *pself)
-{
-  CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (pself);
-
-  return (const double *) self->supported_scales->data;
-}
-
 static gboolean
 cc_display_config_dbus_is_layout_logical (CcDisplayConfig *pself)
 {
@@ -1138,7 +1165,6 @@ cc_display_config_dbus_init (CcDisplayConfigDBus *self)
   self->supports_mirroring = TRUE;
   self->supports_changing_layout_mode = FALSE;
   self->layout_mode = CC_DISPLAY_LAYOUT_MODE_LOGICAL;
-  self->supported_scales = g_array_new (TRUE, TRUE, sizeof (double));
   self->logical_monitors = g_hash_table_new (NULL, NULL);
 }
 
@@ -1265,9 +1291,7 @@ cc_display_config_dbus_constructed (GObject *object)
   CcDisplayConfigDBus *self = CC_DISPLAY_CONFIG_DBUS (object);
   GVariantIter *monitors;
   GVariantIter *logical_monitors;
-  GVariantIter *scales;
   GVariantIter *props;
-  double d;
   const char *s;
   GVariant *v;
 
@@ -1276,12 +1300,8 @@ cc_display_config_dbus_constructed (GObject *object)
                  &self->serial,
                  &monitors,
                  &logical_monitors,
-                 &scales,
                  &props);
 
-  while (g_variant_iter_next (scales, "d", &d))
-    g_array_append_val (self->supported_scales, d);
-
   while (g_variant_iter_next (props, "{&sv}", &s, &v))
     {
       if (g_str_equal (s, "supports-mirroring"))
@@ -1308,7 +1328,6 @@ cc_display_config_dbus_constructed (GObject *object)
 
   g_variant_iter_free (monitors);
   g_variant_iter_free (logical_monitors);
-  g_variant_iter_free (scales);
   g_variant_iter_free (props);
 
   G_OBJECT_CLASS (cc_display_config_dbus_parent_class)->constructed (object);
@@ -1364,7 +1383,6 @@ cc_display_config_dbus_finalize (GObject *object)
   g_clear_pointer (&self->state, g_variant_unref);
   g_clear_object (&self->connection);
 
-  g_array_free (self->supported_scales, TRUE);
   g_list_foreach (self->monitors, (GFunc) g_object_unref, NULL);
   g_clear_pointer (&self->monitors, g_list_free);
   g_clear_pointer (&self->logical_monitors, g_hash_table_destroy);
@@ -1392,7 +1410,6 @@ cc_display_config_dbus_class_init (CcDisplayConfigDBusClass *klass)
   parent_class->is_cloning = cc_display_config_dbus_is_cloning;
   parent_class->set_cloning = cc_display_config_dbus_set_cloning;
   parent_class->get_cloning_modes = cc_display_config_dbus_get_cloning_modes;
-  parent_class->get_supported_scales = cc_display_config_dbus_get_supported_scales;
   parent_class->is_layout_logical = cc_display_config_dbus_is_layout_logical;
 
   pspec = g_param_spec_variant ("state",
@@ -1632,14 +1649,3 @@ cc_display_config_dbus_make_linear (CcDisplayConfigDBus *self)
 
   g_list_free (logical_monitors);
 }
-
-static gboolean
-cc_display_config_dbus_is_supported_scale (CcDisplayConfigDBus *self,
-                                           double scale)
-{
-  guint i;
-  for (i = 0; i < self->supported_scales->len; i++)
-    if (g_array_index (self->supported_scales, double, i) == scale)
-      return TRUE;
-  return FALSE;
-}
diff --git a/panels/display/cc-display-config-rr.c b/panels/display/cc-display-config-rr.c
index 805d44e..1d979a1 100644
--- a/panels/display/cc-display-config-rr.c
+++ b/panels/display/cc-display-config-rr.c
@@ -46,6 +46,14 @@ cc_display_mode_rr_get_resolution (CcDisplayMode *pself,
     *h = gnome_rr_mode_get_height (self->rr_mode);
 }
 
+static double rr_supported_scales[] = { 1.0, 0.0 };
+
+static const double *
+cc_display_mode_rr_get_supported_scales (CcDisplayMode *pself)
+{
+  return rr_supported_scales;
+}
+
 static gboolean
 cc_display_mode_rr_is_interlaced (CcDisplayMode *pself)
 {
@@ -90,6 +98,7 @@ cc_display_mode_rr_class_init (CcDisplayModeRRClass *klass)
   gobject_class->finalize = cc_display_mode_rr_finalize;
 
   parent_class->get_resolution = cc_display_mode_rr_get_resolution;
+  parent_class->get_supported_scales = cc_display_mode_rr_get_supported_scales;
   parent_class->is_interlaced = cc_display_mode_rr_is_interlaced;
   parent_class->get_freq = cc_display_mode_rr_get_freq;
   parent_class->get_freq_f = cc_display_mode_rr_get_freq_f;
@@ -429,9 +438,6 @@ cc_display_monitor_rr_new (GnomeRROutput     *output,
   return CC_DISPLAY_MONITOR (self);
 }
 
-
-static double rr_supported_scales[] = { 1.0, 0.0 };
-
 struct _CcDisplayConfigRR
 {
   CcDisplayConfig parent_instance;
@@ -556,12 +562,6 @@ cc_display_config_rr_get_cloning_modes (CcDisplayConfig *pself)
   return self->clone_modes;
 }
 
-static const double *
-cc_display_config_rr_get_supported_scales (CcDisplayConfig *pself)
-{
-  return rr_supported_scales;
-}
-
 static gboolean
 cc_display_config_rr_is_layout_logical (CcDisplayConfig *pself)
 {
@@ -687,7 +687,6 @@ cc_display_config_rr_class_init (CcDisplayConfigRRClass *klass)
   parent_class->is_cloning = cc_display_config_rr_is_cloning;
   parent_class->set_cloning = cc_display_config_rr_set_cloning;
   parent_class->get_cloning_modes = cc_display_config_rr_get_cloning_modes;
-  parent_class->get_supported_scales = cc_display_config_rr_get_supported_scales;
   parent_class->is_layout_logical = cc_display_config_rr_is_layout_logical;
 
   pspec = g_param_spec_object ("gnome-rr-screen",
diff --git a/panels/display/cc-display-config.c b/panels/display/cc-display-config.c
index 237ff6b..a702897 100644
--- a/panels/display/cc-display-config.c
+++ b/panels/display/cc-display-config.c
@@ -39,6 +39,12 @@ cc_display_mode_get_resolution (CcDisplayMode *self, int *w, int *h)
   return CC_DISPLAY_MODE_GET_CLASS (self)->get_resolution (self, w, h);
 }
 
+const double *
+cc_display_mode_get_supported_scales (CcDisplayMode *self)
+{
+  return CC_DISPLAY_MODE_GET_CLASS (self)->get_supported_scales (self);
+}
+
 gboolean
 cc_display_mode_is_interlaced (CcDisplayMode *self)
 {
@@ -272,12 +278,6 @@ cc_display_config_get_cloning_modes (CcDisplayConfig *self)
   return CC_DISPLAY_CONFIG_GET_CLASS (self)->get_cloning_modes (self);
 }
 
-const double *
-cc_display_config_get_supported_scales (CcDisplayConfig *self)
-{
-  return CC_DISPLAY_CONFIG_GET_CLASS (self)->get_supported_scales (self);
-}
-
 gboolean
 cc_display_config_is_layout_logical (CcDisplayConfig *self)
 {
diff --git a/panels/display/cc-display-config.h b/panels/display/cc-display-config.h
index b126a8a..785f92a 100644
--- a/panels/display/cc-display-config.h
+++ b/panels/display/cc-display-config.h
@@ -79,6 +79,7 @@ struct _CcDisplayModeClass
   GObjectClass parent_class;
 
   void (*get_resolution) (CcDisplayMode *self, int *w, int *h);
+  const double * (*get_supported_scales) (CcDisplayMode *self);
   gboolean (*is_interlaced) (CcDisplayMode *self);
   int (*get_freq) (CcDisplayMode *self);
   double (*get_freq_f) (CcDisplayMode *self);
@@ -134,7 +135,6 @@ struct _CcDisplayConfigClass
   gboolean (*is_cloning) (CcDisplayConfig *self);
   void (*set_cloning) (CcDisplayConfig *self, gboolean clone);
   GList * (*get_cloning_modes) (CcDisplayConfig *self);
-  const double * (*get_supported_scales) (CcDisplayConfig *self);
   gboolean (*is_layout_logical) (CcDisplayConfig *self);
 };
 
@@ -147,7 +147,6 @@ gboolean cc_display_config_apply (CcDisplayConfig *config, GError **error);
 gboolean cc_display_config_is_cloning (CcDisplayConfig *config);
 void cc_display_config_set_cloning (CcDisplayConfig *config, gboolean clone);
 GList *cc_display_config_get_cloning_modes (CcDisplayConfig *config);
-const double *cc_display_config_get_supported_scales (CcDisplayConfig *self);
 gboolean cc_display_config_is_layout_logical (CcDisplayConfig *self);
 
 const char * cc_display_monitor_get_display_name (CcDisplayMonitor *monitor);
@@ -186,11 +185,9 @@ void cc_display_monitor_set_position (CcDisplayMonitor *monitor,
                                       int x, int y);
 
 void cc_display_mode_get_resolution (CcDisplayMode *mode,
-                                    int *width,
-                                    int *height);
-void cc_display_mode_get_dimensions (CcDisplayMode *mode,
                                      int *width,
-                                     int *hegiht);
+                                     int *height);
+const double *cc_display_mode_get_supported_scales (CcDisplayMode *self);
 gboolean cc_display_mode_is_interlaced (CcDisplayMode *mode);
 int cc_display_mode_get_freq (CcDisplayMode *mode);
 double cc_display_mode_get_freq_f (CcDisplayMode *mode);
diff --git a/panels/display/cc-display-panel.c b/panels/display/cc-display-panel.c
index 9847b94..10ce278 100644
--- a/panels/display/cc-display-panel.c
+++ b/panels/display/cc-display-panel.c
@@ -82,6 +82,7 @@ struct _CcDisplayPanelPrivate
   GtkWidget *arrange_button;
   GtkWidget *res_combo;
   GtkWidget *freq_combo;
+  GtkWidget *scale_slider;
   GHashTable *res_freqs;
   GtkWidget *scaling_switch;
   GtkWidget *rotate_left_button;
@@ -1698,6 +1699,72 @@ setup_frequency_combo_box (CcDisplayPanel *panel,
     gtk_combo_box_set_active (GTK_COMBO_BOX (priv->freq_combo), 0);
 }
 
+static guint
+n_supported_scales (CcDisplayMode *mode)
+{
+  const double *scales = cc_display_mode_get_supported_scales (mode);
+  guint n = 0;
+
+  while (scales[n] != 0.0)
+    n++;
+
+  return n;
+}
+
+static void
+setup_scaling_slider (CcDisplayPanel *panel,
+                      CcDisplayMode  *resolution_mode)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  guint i;
+  guint n;
+  const double *scales;
+
+  scales = cc_display_mode_get_supported_scales (resolution_mode);
+  n = n_supported_scales (resolution_mode);
+
+  if (n > 0)
+    {
+      GtkAdjustment *adj;
+      double current_scale;
+      int current_index = -1;
+
+      current_scale = cc_display_monitor_get_scale (priv->current_output);
+      adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale_slider));
+
+      gtk_scale_clear_marks (GTK_SCALE (priv->scale_slider));
+
+      gtk_adjustment_set_step_increment (adj, 1);
+      gtk_adjustment_set_lower (adj, 0);
+      gtk_adjustment_set_upper (adj, n-1);
+      gtk_scale_set_digits (GTK_SCALE (priv->scale_slider), 0);
+
+      for (i = 0; i < n; ++i)
+        {
+          double rounded_scale = round (scales[i] * 10) / 10;
+          double integral;
+          double fractional = modf (rounded_scale, &integral);
+
+          if (scales[i] == current_scale || (current_index < 0 && scales[i] == 1.0))
+            current_index = i;
+
+          if (fractional != 0 && (fractional != 0.5 || integral > 0))
+            continue;
+
+          gchar *s = g_strdup_printf ("%.2lg×", scales[i]);
+          gtk_scale_add_mark (GTK_SCALE (priv->scale_slider), i, GTK_POS_TOP, s);
+          g_free (s);
+        }
+
+      gtk_widget_show (priv->scale_slider);
+      gtk_range_set_value (GTK_RANGE (priv->scale_slider), current_index);
+    }
+  else
+    {
+      gtk_widget_hide (priv->scale_slider);
+    }
+}
+
 static void
 free_mode_list (gpointer key,
                 gpointer value,
@@ -1782,6 +1849,7 @@ setup_resolution_combo_box (CcDisplayPanel  *panel,
     gtk_combo_box_set_active (GTK_COMBO_BOX (priv->res_combo), 0);
 
   setup_frequency_combo_box (panel, current_mode);
+  setup_scaling_slider (panel, current_mode);
 }
 
 
@@ -1988,6 +2056,7 @@ res_combo_changed (GtkComboBox    *combo,
       update_apply_button (panel);
 
       setup_frequency_combo_box (panel, mode);
+      setup_scaling_slider (panel, mode);
     }
 }
 
@@ -2041,16 +2110,17 @@ underscan_switch_toggled (CcDisplayPanel *panel)
   update_apply_button (panel);
 }
 
-static guint
-n_supported_scales (CcDisplayConfig *config)
+static double
+scale_slider_get_selected_scale (CcDisplayPanel *panel)
 {
-  const double *scales = cc_display_config_get_supported_scales (config);
-  guint n = 0;
+  CcDisplayPanelPrivate *priv = panel->priv;
+  CcDisplayMode *mode = cc_display_monitor_get_mode (priv->current_output);
+  const double *scales = cc_display_mode_get_supported_scales (mode);
+  int selected = gtk_range_get_value (GTK_RANGE (priv->scale_slider));
 
-  while (scales[n] != 0.0)
-    n++;
+  g_return_val_if_fail (selected < n_supported_scales (mode), 1.0);
 
-  return n;
+  return scales[selected];
 }
 
 static void
@@ -2058,10 +2128,18 @@ scale_slider_changed (GtkRange *slider,
                       CcDisplayPanel *panel)
 {
   cc_display_monitor_set_scale (panel->priv->current_output,
-                                gtk_range_get_value (slider));
+                                scale_slider_get_selected_scale (panel));
   update_apply_button (panel);
 }
 
+static char *
+scale_slider_format_value (GtkScale *slider,
+                           double value,
+                           CcDisplayPanel *panel)
+{
+  return g_strdup_printf ("%.3g", scale_slider_get_selected_scale (panel));
+}
+
 static void
 show_setup_dialog (CcDisplayPanel *panel)
 {
@@ -2298,31 +2376,22 @@ show_setup_dialog (CcDisplayPanel *panel)
   grid_pos++;
 
   /* scale */
-  if (n_supported_scales (priv->current_config) > 1)
-  {
-    GtkWidget *slider;
-    const double *scales = cc_display_config_get_supported_scales (priv->current_config);
-    guint n = n_supported_scales (priv->current_config);
-    guint i = 0;
-
-    slider = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL,
-                                       scales[0], scales[n - 1],
-                                       scales[1] - scales[0]);
-    gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE);
-    gtk_scale_set_has_origin (GTK_SCALE (slider), FALSE);
-    for (i = 0; i < n; i++)
-      {
-        gchar *s = g_strdup_printf ("%.1lg×", scales[i]);
-        gtk_scale_add_mark (GTK_SCALE (slider), scales[i], GTK_POS_TOP, s);
-        g_free (s);
-      }
-    gtk_range_set_value (GTK_RANGE (slider), cc_display_monitor_get_scale (priv->current_output));
-    g_signal_connect (slider, "value-changed", G_CALLBACK (scale_slider_changed), panel);
-
-    gtk_grid_attach (GTK_GRID (priv->config_grid), slider, 1, grid_pos, 1, 1);
-    gtk_widget_set_halign (priv->freq_combo, GTK_ALIGN_CENTER);
-    grid_pos++;
-  }
+  priv->scale_slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, NULL);
+  gtk_widget_set_no_show_all (priv->scale_slider, TRUE);
+
+  g_signal_connect (priv->scale_slider, "format-value",
+                    G_CALLBACK (scale_slider_format_value), panel);
+  g_signal_connect (priv->scale_slider, "value-changed",
+                    G_CALLBACK (scale_slider_changed), panel);
+
+  gtk_scale_set_draw_value (GTK_SCALE (priv->scale_slider), TRUE);
+  gtk_range_set_round_digits (GTK_RANGE (priv->scale_slider), 2);
+  gtk_scale_set_value_pos (GTK_SCALE (priv->scale_slider), GTK_POS_BOTTOM);
+  gtk_scale_set_has_origin (GTK_SCALE (priv->scale_slider), FALSE);
+  gtk_grid_attach (GTK_GRID (priv->config_grid),
+                   priv->scale_slider, 1, grid_pos, 1, 1);
+  gtk_widget_set_halign (priv->freq_combo, GTK_ALIGN_CENTER);
+  grid_pos++;
 
   was_clone = clone = cc_display_config_is_cloning (priv->current_config);
   primary = cc_display_monitor_is_primary (priv->current_output);


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