[gtk/widget-class-actions: 5/22] Allow registering actions per-class



commit 66e81438cb0252a940d2c260257b44dc5390ccce
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Jun 14 12:12:10 2019 +0000

    Allow registering actions per-class
    
    Add a facility to register and install actions
    at class init time. We avoid creating an action
    group for these by teaching the action muxer
    about these actions.

 docs/reference/gtk/gtk4-sections.txt |  17 +++-
 gtk/gtkactionmuxer.c                 | 138 ++++++++++++++++++++++++++++---
 gtk/gtkactionmuxerprivate.h          |  22 ++++-
 gtk/gtkapplication.c                 |   2 +-
 gtk/gtkwidget.c                      | 154 ++++++++++++++++++++++++++++++++++-
 gtk/gtkwidget.h                      | 100 +++++++++++++++++++++++
 6 files changed, 414 insertions(+), 19 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 786bcde361..8dfe7b67e3 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -4549,10 +4549,6 @@ gtk_widget_get_opacity
 gtk_widget_set_opacity
 gtk_widget_get_overflow
 gtk_widget_set_overflow
-gtk_widget_insert_action_group
-gtk_widget_list_action_prefixes
-gtk_widget_activate_action
-gtk_widget_activate_default
 gtk_widget_measure
 gtk_widget_snapshot_child
 gtk_widget_get_next_sibling
@@ -4629,6 +4625,19 @@ gtk_widget_class_set_connect_func
 gtk_widget_observe_children
 gtk_widget_observe_controllers
 
+<SUBSECTION Actions>
+gtk_widget_insert_action_group
+gtk_widget_activate_action
+gtk_widget_activate_default
+GtkWidgetActionActivateFunc
+GtkWidgetActionQueryFunc
+GtkWidgetActionChangeStateFunc
+GtkWidgetActionQueryStateFunc
+gtk_widget_class_install_action
+gtk_widget_class_install_stateful_action
+gtk_widget_notify_class_action_enabled
+gtk_widget_notify_class_action_state
+
 <SUBSECTION Standard>
 GTK_WIDGET
 GTK_IS_WIDGET
diff --git a/gtk/gtkactionmuxer.c b/gtk/gtkactionmuxer.c
index f358f8cbbd..bcc3e3370a 100644
--- a/gtk/gtkactionmuxer.c
+++ b/gtk/gtkactionmuxer.c
@@ -72,6 +72,7 @@ struct _GtkActionMuxer
   GtkActionMuxer *parent;
 
   GtkWidget *widget;
+  GPtrArray *widget_actions;
 };
 
 G_DEFINE_TYPE_WITH_CODE (GtkActionMuxer, gtk_action_muxer, G_TYPE_OBJECT,
@@ -83,6 +84,7 @@ enum
   PROP_0,
   PROP_PARENT,
   PROP_WIDGET,
+  PROP_WIDGET_ACTIONS,
   NUM_PROPERTIES
 };
 
@@ -108,7 +110,7 @@ typedef struct
 static void
 gtk_action_muxer_append_group_actions (const char *prefix,
                                        Group      *group,
-                                       GArray     *actions)
+                                       GHashTable *actions)
 {
   gchar **group_actions;
   gchar **action;
@@ -116,10 +118,8 @@ gtk_action_muxer_append_group_actions (const char *prefix,
   group_actions = g_action_group_list_actions (group->group);
   for (action = group_actions; *action; action++)
     {
-      gchar *fullname;
-
-      fullname = g_strconcat (prefix, ".", *action, NULL);
-      g_array_append_val (actions, fullname);
+      char *name = g_strconcat (prefix, ".", *action, NULL);
+      g_hash_table_add (actions, name);
     }
 
   g_strfreev (group_actions);
@@ -129,9 +129,11 @@ static gchar **
 gtk_action_muxer_list_actions (GActionGroup *action_group)
 {
   GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group);
-  GArray *actions;
+  GHashTable *actions;
+  char **keys;
 
-  actions = g_array_new (TRUE, FALSE, sizeof (gchar *));
+  actions = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                   g_free, NULL);
 
   for ( ; muxer != NULL; muxer = muxer->parent)
     {
@@ -139,12 +141,28 @@ gtk_action_muxer_list_actions (GActionGroup *action_group)
       const char *prefix;
       Group *group;
 
+      if (muxer->widget_actions)
+        {
+          int i;
+
+          for (i = 0; i < muxer->widget_actions->len; i++)
+            {
+              GtkWidgetAction *action = g_ptr_array_index (muxer->widget_actions, i);
+              g_hash_table_add (actions, g_strdup (action->name));
+            }
+        }
+
       g_hash_table_iter_init (&iter, muxer->groups);
       while (g_hash_table_iter_next (&iter, (gpointer *)&prefix, (gpointer *)&group))
         gtk_action_muxer_append_group_actions (prefix, group, actions);
     }
 
-  return (gchar **)(void *) g_array_free (actions, FALSE);
+  keys = (char **)g_hash_table_get_keys_as_array (actions, NULL);
+
+  g_hash_table_steal_all (actions);
+  g_hash_table_unref (actions);
+
+  return (char **)keys;
 }
 
 static Group *
@@ -183,7 +201,7 @@ gtk_action_muxer_find (GtkActionMuxer  *muxer,
   return group->group;
 }
 
-static void
+void
 gtk_action_muxer_action_enabled_changed (GtkActionMuxer *muxer,
                                          const gchar    *action_name,
                                          gboolean        enabled)
@@ -223,7 +241,7 @@ gtk_action_muxer_parent_action_enabled_changed (GActionGroup *action_group,
   gtk_action_muxer_action_enabled_changed (muxer, action_name, enabled);
 }
 
-static void
+void
 gtk_action_muxer_action_state_changed (GtkActionMuxer *muxer,
                                        const gchar    *action_name,
                                        GVariant       *state)
@@ -401,6 +419,53 @@ gtk_action_muxer_query_action (GActionGroup        *action_group,
   Group *group;
   const gchar *unprefixed_name;
 
+  if (muxer->widget_actions)
+    {
+      int i;
+
+      for (i = 0; i < muxer->widget_actions->len; i++)
+        {
+          GtkWidgetAction *action = g_ptr_array_index (muxer->widget_actions, i);
+          if (strcmp (action->name, action_name) == 0)
+            {
+              if (action->query)
+                {
+                  action->query (muxer->widget,
+                                 action->name,
+                                 enabled,
+                                 parameter_type);
+                }
+              else
+                {
+                  if (enabled)
+                    *enabled = TRUE;
+                  if (parameter_type)
+                    *parameter_type = NULL;
+                }
+
+              if (action->query_state)
+                {
+                  action->query_state (muxer->widget,
+                                       action->name,
+                                       state_type,
+                                       state_hint,
+                                       state);
+                }
+              else
+                {
+                  if (state_type)
+                    *state_type = NULL;
+                  if (state_hint)
+                    *state_hint = NULL;
+                  if (state)
+                    *state = NULL;
+                }
+
+              return TRUE;
+            }
+       }
+    }
+
   group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
 
   if (group)
@@ -424,6 +489,22 @@ gtk_action_muxer_activate_action (GActionGroup *action_group,
   Group *group;
   const gchar *unprefixed_name;
 
+  if (muxer->widget_actions)
+    {
+      int i;
+
+      for (i = 0; i < muxer->widget_actions->len; i++)
+        {
+          GtkWidgetAction *action = g_ptr_array_index (muxer->widget_actions, i);
+          if (strcmp (action->name, action_name) == 0)
+            {
+              action->activate (muxer->widget, action->name, parameter);
+
+              return;
+            }
+        }
+    }
+
   group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
 
   if (group)
@@ -441,6 +522,23 @@ gtk_action_muxer_change_action_state (GActionGroup *action_group,
   Group *group;
   const gchar *unprefixed_name;
 
+  if (muxer->widget_actions)
+    {
+      int i;
+
+      for (i = 0; i < muxer->widget_actions->len; i++)
+        {
+          GtkWidgetAction *action = g_ptr_array_index (muxer->widget_actions, i);
+          if (strcmp (action->name, action_name) == 0)
+            {
+              if (action->change_state)
+                action->change_state (muxer->widget, action->name, state);
+
+              return;
+            }
+        }
+    }
+
   group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name);
 
   if (group)
@@ -600,6 +698,10 @@ gtk_action_muxer_get_property (GObject    *object,
       g_value_set_object (value, muxer->widget);
       break;
 
+    case PROP_WIDGET_ACTIONS:
+      g_value_set_boxed (value, muxer->widget_actions);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -623,6 +725,10 @@ gtk_action_muxer_set_property (GObject      *object,
       muxer->widget = g_value_get_object (value);
       break;
 
+    case PROP_WIDGET_ACTIONS:
+      muxer->widget_actions = g_value_get_boxed (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -683,6 +789,13 @@ gtk_action_muxer_class_init (GObjectClass *class)
                                                  G_PARAM_CONSTRUCT_ONLY |
                                                  G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_WIDGET_ACTIONS] = g_param_spec_boxed ("widget-actions", "Widget actions",
+                                                        "Widget actions",
+                                                        G_TYPE_PTR_ARRAY,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS);
+
   g_object_class_install_properties (class, NUM_PROPERTIES, properties);
 }
 
@@ -791,14 +904,17 @@ gtk_action_muxer_lookup (GtkActionMuxer *muxer,
 /*< private >
  * gtk_action_muxer_new:
  * @widget: the widget to which the muxer belongs
+ * @actions: widget actions
  *
  * Creates a new #GtkActionMuxer.
  */
 GtkActionMuxer *
-gtk_action_muxer_new (GtkWidget *widget)
+gtk_action_muxer_new (GtkWidget *widget,
+                      GPtrArray *actions)
 {
   return g_object_new (GTK_TYPE_ACTION_MUXER,
                        "widget", widget,
+                       "widget-actions", actions,
                        NULL);
 }
 
diff --git a/gtk/gtkactionmuxerprivate.h b/gtk/gtkactionmuxerprivate.h
index 190728fafd..33b96452d1 100644
--- a/gtk/gtkactionmuxerprivate.h
+++ b/gtk/gtkactionmuxerprivate.h
@@ -31,10 +31,20 @@ G_BEGIN_DECLS
 #define GTK_IS_ACTION_MUXER(inst)                           (G_TYPE_CHECK_INSTANCE_TYPE ((inst),             
        \
                                                              GTK_TYPE_ACTION_MUXER))
 
+typedef struct {
+  char *name;
+
+  GtkWidgetActionActivateFunc    activate;
+  GtkWidgetActionQueryFunc       query;
+  GtkWidgetActionChangeStateFunc change_state;
+  GtkWidgetActionQueryStateFunc  query_state;
+} GtkWidgetAction;
+
 typedef struct _GtkActionMuxer                              GtkActionMuxer;
 
 GType                   gtk_action_muxer_get_type                       (void);
-GtkActionMuxer *        gtk_action_muxer_new                            (GtkWidget      *widget);
+GtkActionMuxer *        gtk_action_muxer_new                            (GtkWidget      *widget,
+                                                                         GPtrArray      *actions);
 
 void                    gtk_action_muxer_insert                         (GtkActionMuxer *muxer,
                                                                          const gchar    *prefix,
@@ -59,6 +69,16 @@ void                    gtk_action_muxer_set_primary_accel              (GtkActi
 const gchar *           gtk_action_muxer_get_primary_accel              (GtkActionMuxer *muxer,
                                                                          const gchar    *action_and_target);
 
+void
+gtk_action_muxer_action_enabled_changed (GtkActionMuxer *muxer,
+                                         const char     *action_name,
+                                         gboolean        enabled);
+void
+gtk_action_muxer_action_state_changed (GtkActionMuxer *muxer,
+                                       const gchar    *action_name,
+                                       GVariant       *state);
+
+
 /* No better place for these... */
 gchar *                 gtk_print_action_and_target                     (const gchar    *action_namespace,
                                                                          const gchar    *action_name,
diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c
index f90fd827fc..3b3fe1e5ac 100644
--- a/gtk/gtkapplication.c
+++ b/gtk/gtkapplication.c
@@ -394,7 +394,7 @@ gtk_application_init (GtkApplication *application)
 {
   GtkApplicationPrivate *priv = gtk_application_get_instance_private (application);
 
-  priv->muxer = gtk_action_muxer_new (NULL);
+  priv->muxer = gtk_action_muxer_new (NULL, NULL);
 
   priv->accels = gtk_application_accels_new ();
 }
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 444ecbe4dd..69bc56e987 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -501,6 +501,7 @@ struct _GtkWidgetClassPrivate
   AtkRole accessible_role;
   const char *css_name;
   GType layout_manager_type;
+  GPtrArray *actions;
 };
 
 enum {
@@ -11897,14 +11898,16 @@ _gtk_widget_get_action_muxer (GtkWidget *widget,
                               gboolean   create)
 {
   GtkActionMuxer *muxer;
+  GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (widget);
+  GtkWidgetClassPrivate *priv = widget_class->priv;
 
   muxer = (GtkActionMuxer*)g_object_get_qdata (G_OBJECT (widget), quark_action_muxer);
   if (muxer)
     return muxer;
 
-  if (create)
+  if (create || priv->actions)
     {
-      muxer = gtk_action_muxer_new (widget);
+      muxer = gtk_action_muxer_new (widget, priv->actions);
       g_object_set_qdata_full (G_OBJECT (widget),
                                quark_action_muxer,
                                muxer,
@@ -13427,3 +13430,150 @@ gtk_widget_should_layout (GtkWidget *widget)
   return TRUE;
 }
 
+/*
+ * gtk_widget_class_install_action:
+ * @widget_class: a #GtkWidgetClass
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @activate: callback to use when the action is activated
+ * @query: (allow-none): callback to use when the action properties
+       are queried, or %NULL for always-enabled, parameterless actions
+ *
+ * This should be called at class initialization time to specify
+ * actions to be added for all instances of this class.
+ *
+ * Actions installed by this function are stateless. The only state
+ * they have is whether they are enabled or not. For more complicated
+ * stateful actions, see gtk_widget_class_install_stateful_action().
+ */
+void
+gtk_widget_class_install_action (GtkWidgetClass              *widget_class,
+                                 const char                  *action_name,
+                                 GtkWidgetActionActivateFunc  activate,
+                                 GtkWidgetActionQueryFunc     query)
+{
+  gtk_widget_class_install_stateful_action (widget_class, action_name,
+                                            activate, query,
+                                            NULL, NULL);
+}
+
+/*
+ * gtk_widget_class_install_stateful_action:
+ * @widget_class: a #GtkWidgetClass
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @activate: callback to use when the action is activated
+ * @query: (allow-none): callback to use when the action properties
+       are queried, or %NULL for always-enabled stateless actions
+ * @change: (allow-none): callback to use when the action state is
+ *     changed, or %NULL for stateless actions
+ * @query_state: (allow-none): callback to use when the action state
+       is queried, or %NULL for stateless actions
+ *
+ * This should be called at class initialization time to specify
+ * actions to be added for all instances of this class.
+ *
+ * Actions installed in this way can be simple or stateful.
+ * See the #GAction documentation for more information.
+ */
+void
+gtk_widget_class_install_stateful_action (GtkWidgetClass                 *widget_class,
+                                          const char                     *action_name,
+                                          GtkWidgetActionActivateFunc     activate,
+                                          GtkWidgetActionQueryFunc        query,
+                                          GtkWidgetActionChangeStateFunc  change_state,
+                                          GtkWidgetActionQueryStateFunc   query_state)
+{
+  GtkWidgetClassPrivate *priv = widget_class->priv;
+  GtkWidgetAction *action;
+
+  g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class));
+
+  if (priv->actions == NULL)
+    priv->actions = g_ptr_array_new ();
+  else if (GTK_IS_WIDGET_CLASS (&widget_class->parent_class))
+    {
+      GtkWidgetClass *parent_class = GTK_WIDGET_CLASS (&widget_class->parent_class);
+      GtkWidgetClassPrivate *parent_priv = parent_class->priv;
+      GPtrArray *parent_actions = parent_priv->actions;
+
+      if (priv->actions == parent_actions)
+        {
+          int i;
+
+          priv->actions = g_ptr_array_new ();
+          for (i = 0; i < parent_actions->len; i++)
+            g_ptr_array_add (priv->actions, g_ptr_array_index (parent_actions, i));
+        }
+    }
+
+  action = g_new0 (GtkWidgetAction, 1);
+  action->name = g_strdup (action_name);
+  action->activate = activate;
+  action->query = query;
+  action->change_state = change_state;
+  action->query_state = query_state;
+
+  GTK_NOTE(ACTIONS,
+           g_message ("%sClass: Adding %s action\n",
+                      g_type_name (G_TYPE_FROM_CLASS (widget_class)),
+                      action_name));
+
+  g_ptr_array_add (priv->actions, action);
+}
+
+/**
+ * gtk_widget_notify_class_action_enabled:
+ * @widget: a #GtkWidget
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @enabled: whether the action is now enabled
+ *
+ * Convenience API to notify when an action installed
+ * with gtk_widget_class_install_action() changes its
+ * enabled state. It must be called after the change
+ * has taken place (we expect the @query callback to
+ * already return the new state).
+ *
+ * This function is a more convenient alternative
+ * to calling g_action_group_action_enabled_changed()
+ * directly.
+ */
+void
+gtk_widget_notify_class_action_enabled (GtkWidget  *widget,
+                                        const char *action_name,
+                                        gboolean    enabled)
+{
+  GtkActionMuxer *muxer;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  muxer = _gtk_widget_get_action_muxer (widget, TRUE);
+  gtk_action_muxer_action_enabled_changed (muxer, action_name, enabled);
+}
+
+/**
+ * gtk_widget_notify_class_action_state:
+ * @widget: a #GtkWidget
+ * @action_name: a prefixed action name, such as "clipboard.paste"
+ * @state: the new state
+ *
+ * Convenience API to notify when an action installed
+ * with gtk_widget_class_install_action() changes its
+ * state. It must be called after the change has taken
+ * place (we expect the @query callback to already
+ * return the new state).
+ *
+ * This function is a more convenient alternative
+ * to calling g_action_group_action_state_changed()
+ * directly.
+ */
+void
+gtk_widget_notify_class_action_state (GtkWidget  *widget,
+                                      const char *action_name,
+                                      GVariant   *state)
+{
+  GtkActionMuxer *muxer;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  muxer = _gtk_widget_get_action_muxer (widget, TRUE);
+  gtk_action_muxer_action_state_changed (muxer, action_name, state);
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index ca9d768ea6..196cb96224 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -1023,6 +1023,106 @@ GDK_AVAILABLE_IN_ALL
 gboolean                gtk_widget_should_layout        (GtkWidget   *widget);
 
 
+/**
+ * GtkWidgetActionActivateFunc:
+ * @widget: the widget to which the action belongs
+ * @action_name: the (unprefixed) action name
+ * @parameter: parameter for activation
+ *
+ * The type of the callback functions used for activating
+ * actions installed with gtk_widget_class_install_action().
+ *
+ * The @parameter must match the @parameter_type of the action.
+ */
+typedef void (* GtkWidgetActionActivateFunc) (GtkWidget  *widget,
+                                              const char *action_name,
+                                              GVariant   *parameter);
+
+/**
+ * GtkWidgetActionQueryFunc:
+ * @widget: the widget to which the action belongs
+ * @action_name: the (unprefixed) action name
+ * @enabled: (out) (optional): return location for the enabled state
+ * @parameter_type: (out) (optional): return location for the parameter type
+ *
+ * The type of the callback functions used to query
+ * the enabledness and parameter type of actions installed with
+ * gtk_widget_class_install_action().
+ *
+ * See the #GAction documentation for more details about the
+ * meaning of these properties.
+ */
+typedef void (* GtkWidgetActionQueryFunc) (GtkWidget           *widget,
+                                           const char          *action_name,
+                                           gboolean            *enabled,
+                                           const GVariantType **parameter_type);
+
+/**
+ * GtkWidgetActionQueryStateFunc:
+ * @widget: the widget to which the action belongs
+ * @action_name: the (unprefixed) action name
+ * @state_type: (out) (optional): return location for the state type
+ * @state_hint: (out) (optional): return location for the state hint
+ * @state: (out) (optional): return location for the state
+ *
+ * The type of the callback functions used to query the state
+ * of stateful actions installed with gtk_widget_class_install_action().
+ *
+ * See the #GAction documentation for more details about the
+ * meaning of these properties.
+ */
+typedef void (* GtkWidgetActionQueryStateFunc) (GtkWidget           *widget,
+                                                const char          *action_name,
+                                                const GVariantType **state_type,
+                                                GVariant           **state_hint,
+                                                GVariant           **state);
+
+/**
+ * GtkWidgetActionChangeStateFunc:
+ * @widget: the widget to which the action belongs
+ * @action_name: the (unprefixed) action name
+ * @state: the new state
+ *
+ * The type of the callback functions used to change the
+ * state of actions installed with gtk_widget_class_install_action().
+ *
+ * The @state must match the @state_type of the action.
+ *
+ * Note that you can change the enabledness and state
+ * of widget actions by other means, as long as you
+ * emit the required #GActionGroup notification signals,
+ * which can be done with GtkWidget convenience API.
+ * This callback is used when the action state is
+ * changed via the #GActionGroup API.
+ */
+typedef void (*GtkWidgetActionChangeStateFunc) (GtkWidget  *widget,
+                                                const char *action_name,
+                                                GVariant   *state);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_class_install_action (GtkWidgetClass                *widget_class,
+                                                         const char                    *action_name,
+                                                         GtkWidgetActionActivateFunc    activate,
+                                                         GtkWidgetActionQueryFunc       query);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_class_install_stateful_action (GtkWidgetClass                
*widget_class,
+                                                                  const char                    *action_name,
+                                                                  GtkWidgetActionActivateFunc    activate,
+                                                                  GtkWidgetActionQueryFunc       query,
+                                                                  GtkWidgetActionChangeStateFunc 
change_state,
+                                                                  GtkWidgetActionQueryStateFunc  
query_state);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_notify_class_action_enabled (GtkWidget  *widget,
+                                                                const char *action_name,
+                                                                gboolean    enabled);
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_notify_class_action_state (GtkWidget  *widget,
+                                                              const char *action_name,
+                                                              GVariant   *state);
+
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWidget, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkRequisition, gtk_requisition_free)
 


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