[evolution] Add ETableColumnSelector.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Add ETableColumnSelector.
- Date: Tue, 2 Jul 2013 15:27:57 +0000 (UTC)
commit c4a37c155a57c99d736dc8ae25ae4b77d0f82024
Author: Matthew Barnes <mbarnes redhat com>
Date: Mon Jul 1 09:08:12 2013 -0400
Add ETableColumnSelector.
A new ETable column selection and ordering widget that looks like
it was written in the 21st century. Derives from ETreeViewFrame.
.../evolution-util/evolution-util-docs.sgml | 1 +
.../evolution-util/evolution-util-sections.txt | 20 +
doc/reference/evolution-util/evolution-util.types | 1 +
e-util/Makefile.am | 2 +
e-util/e-table-column-selector.c | 463 ++++++++++++++++++++
e-util/e-table-column-selector.h | 81 ++++
e-util/e-util.h | 1 +
7 files changed, 569 insertions(+), 0 deletions(-)
---
diff --git a/doc/reference/evolution-util/evolution-util-docs.sgml
b/doc/reference/evolution-util/evolution-util-docs.sgml
index 57917bd..a9a889e 100644
--- a/doc/reference/evolution-util/evolution-util-docs.sgml
+++ b/doc/reference/evolution-util/evolution-util-docs.sgml
@@ -114,6 +114,7 @@
<xi:include href="xml/e-table.xml"/>
<xi:include href="xml/e-table-click-to-add.xml"/>
<xi:include href="xml/e-table-col.xml"/>
+ <xi:include href="xml/e-table-column-selector.xml"/>
<xi:include href="xml/e-table-column-specification.xml"/>
<xi:include href="xml/e-table-config.xml"/>
<xi:include href="xml/e-table-extras.xml"/>
diff --git a/doc/reference/evolution-util/evolution-util-sections.txt
b/doc/reference/evolution-util/evolution-util-sections.txt
index 4337854..79204d8 100644
--- a/doc/reference/evolution-util/evolution-util-sections.txt
+++ b/doc/reference/evolution-util/evolution-util-sections.txt
@@ -3393,6 +3393,26 @@ e_table_col_get_type
</SECTION>
<SECTION>
+<FILE>e-table-column-selector</FILE>
+<TITLE>ETableColumnSelector</TITLE>
+ETableColumnSelector
+e_table_column_selector_new
+e_table_column_selector_get_state
+e_table_column_selector_apply
+<SUBSECTION Standard>
+E_TABLE_COLUMN_SELECTOR
+E_IS_TABLE_COLUMN_SELECTOR
+E_TYPE_TABLE_COLUMN_SELECTOR
+E_TABLE_COLUMN_SELECTOR_CLASS
+E_IS_TABLE_COLUMN_SELECTOR_CLASS
+E_TABLE_COLUMN_SELECTOR_GET_CLASS
+ETableColumnSelectorClass
+e_table_column_selector_get_type
+<SUBSECTION Private>
+ETableColumnSelectorPrivate
+</SECTION>
+
+<SECTION>
<FILE>e-table-column-specification</FILE>
<TITLE>ETableColumnSpecification</TITLE>
ETableColumnSpecification
diff --git a/doc/reference/evolution-util/evolution-util.types
b/doc/reference/evolution-util/evolution-util.types
index 764fd3c..576dfcb 100644
--- a/doc/reference/evolution-util/evolution-util.types
+++ b/doc/reference/evolution-util/evolution-util.types
@@ -123,6 +123,7 @@ e_spell_entry_get_type
e_stock_request_get_type
e_table_click_to_add_get_type
e_table_col_get_type
+e_table_column_selector_get_type
e_table_column_specification_get_type
e_table_config_get_type
e_table_extras_get_type
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index a7123bb..894392a 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -254,6 +254,7 @@ evolution_util_include_HEADERS = \
e-table-click-to-add.h \
e-table-col-dnd.h \
e-table-col.h \
+ e-table-column-selector.h \
e-table-column-specification.h \
e-table-config.h \
e-table-defines.h \
@@ -499,6 +500,7 @@ libevolution_util_la_SOURCES = \
e-stock-request.c \
e-table-click-to-add.c \
e-table-col.c \
+ e-table-column-selector.c \
e-table-column-specification.c \
e-table-config.c \
e-table-extras.c \
diff --git a/e-util/e-table-column-selector.c b/e-util/e-table-column-selector.c
new file mode 100644
index 0000000..80cceef
--- /dev/null
+++ b/e-util/e-table-column-selector.c
@@ -0,0 +1,463 @@
+/*
+ * e-table-column-selector.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/**
+ * SECTION: e-table-column-selector
+ * @include: e-util/e-util.h
+ * @short_description: Select columns for an #ETable or #ETree
+ *
+ * #ETableColumnSelector is a widget for choosing and ordering the
+ * available columns of an #ETable or #ETree.
+ **/
+
+#include "e-table-column-selector.h"
+
+#include <config.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-util/e-table-specification.h"
+
+#define E_TABLE_COLUMN_SELECTOR_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_TABLE_COLUMN_SELECTOR, ETableColumnSelectorPrivate))
+
+struct _ETableColumnSelectorPrivate {
+ ETableState *state;
+};
+
+enum {
+ PROP_0,
+ PROP_STATE
+};
+
+enum {
+ COLUMN_ACTIVE,
+ COLUMN_TITLE,
+ COLUMN_SPECIFICATION,
+ COLUMN_EXPANSION,
+ NUM_COLUMNS
+};
+
+G_DEFINE_TYPE (
+ ETableColumnSelector,
+ e_table_column_selector,
+ E_TYPE_TREE_VIEW_FRAME)
+
+static void
+table_column_selector_toggled_cb (GtkCellRendererToggle *renderer,
+ const gchar *path_string,
+ GtkTreeView *tree_view)
+{
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter;
+ gboolean active;
+
+ tree_model = gtk_tree_view_get_model (tree_view);
+ gtk_tree_model_get_iter_from_string (tree_model, &iter, path_string);
+
+ gtk_tree_model_get (tree_model, &iter, COLUMN_ACTIVE, &active, -1);
+
+ gtk_list_store_set (
+ GTK_LIST_STORE (tree_model),
+ &iter, COLUMN_ACTIVE, !active, -1);
+}
+
+static GtkTreeModel *
+table_column_selector_build_model (ETableColumnSelector *selector)
+{
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ ETableState *state;
+ ETableSpecification *specification;
+ GPtrArray *columns;
+ GHashTable *columns_added;
+ guint ii;
+
+ state = e_table_column_selector_get_state (selector);
+ specification = e_table_state_ref_specification (state);
+ columns = e_table_specification_ref_columns (specification);
+
+ /* Set of ETableColumnSpecifications to help keep track
+ * of which ones are already added to the list store. */
+ columns_added = g_hash_table_new (NULL, NULL);
+
+ list_store = gtk_list_store_new (
+ NUM_COLUMNS,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING,
+ E_TYPE_TABLE_COLUMN_SPECIFICATION,
+ G_TYPE_DOUBLE);
+
+ /* Add selected columns from ETableState first. */
+
+ for (ii = 0; ii < state->col_count; ii++) {
+ ETableColumnSpecification *column_spec;
+ gdouble expansion;
+
+ column_spec = state->column_specs[ii];
+ expansion = state->expansions[ii];
+
+ gtk_list_store_append (list_store, &iter);
+
+ gtk_list_store_set (
+ list_store, &iter,
+ COLUMN_ACTIVE, TRUE,
+ COLUMN_TITLE, column_spec->title,
+ COLUMN_SPECIFICATION, column_spec,
+ COLUMN_EXPANSION, expansion,
+ -1);
+
+ g_hash_table_add (columns_added, column_spec);
+ }
+
+ /* Add the rest of the columns from ETableSpecification. */
+
+ for (ii = 0; ii < columns->len; ii++) {
+ ETableColumnSpecification *column_spec;
+
+ column_spec = g_ptr_array_index (columns, ii);
+
+ if (g_hash_table_contains (columns_added, column_spec))
+ continue;
+
+ /* XXX We have this unfortunate "disabled" flag because
+ * past developers made the mistake of having table
+ * config files reference columns by number instead
+ * of name so removing a column would break all the
+ * table config files out in the wild. */
+ if (column_spec->disabled)
+ continue;
+
+ gtk_list_store_append (list_store, &iter);
+
+ gtk_list_store_set (
+ list_store, &iter,
+ COLUMN_ACTIVE, FALSE,
+ COLUMN_TITLE, column_spec->title,
+ COLUMN_SPECIFICATION, column_spec,
+ COLUMN_EXPANSION, 1.0,
+ -1);
+
+ g_hash_table_add (columns_added, column_spec);
+ }
+
+ g_hash_table_destroy (columns_added);
+
+ g_object_unref (specification);
+ g_ptr_array_unref (columns);
+
+ return GTK_TREE_MODEL (list_store);
+}
+
+static void
+table_column_selector_set_state (ETableColumnSelector *selector,
+ ETableState *state)
+{
+ g_return_if_fail (E_IS_TABLE_STATE (state));
+ g_return_if_fail (selector->priv->state == NULL);
+
+ selector->priv->state = g_object_ref (state);
+}
+
+static void
+table_column_selector_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_STATE:
+ table_column_selector_set_state (
+ E_TABLE_COLUMN_SELECTOR (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+table_column_selector_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_STATE:
+ g_value_set_object (
+ value,
+ e_table_column_selector_get_state (
+ E_TABLE_COLUMN_SELECTOR (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+table_column_selector_dispose (GObject *object)
+{
+ ETableColumnSelectorPrivate *priv;
+
+ priv = E_TABLE_COLUMN_SELECTOR_GET_PRIVATE (object);
+
+ g_clear_object (&priv->state);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_table_column_selector_parent_class)->
+ dispose (object);
+}
+
+static void
+table_column_selector_constructed (GObject *object)
+{
+ ETableColumnSelector *selector;
+ ETreeViewFrame *tree_view_frame;
+ GtkAction *action;
+ GtkTreeView *tree_view;
+ GtkTreeModel *tree_model;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ const gchar *tooltip;
+
+ selector = E_TABLE_COLUMN_SELECTOR (object);
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_table_column_selector_parent_class)->
+ constructed (object);
+
+ tree_view_frame = E_TREE_VIEW_FRAME (object);
+ tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);
+
+ gtk_tree_view_set_reorderable (tree_view, TRUE);
+ gtk_tree_view_set_headers_visible (tree_view, FALSE);
+
+ selection = gtk_tree_view_get_selection (tree_view);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+ /* Configure the toolbar actions. */
+
+ action = e_tree_view_frame_lookup_toolbar_action (
+ tree_view_frame, E_TREE_VIEW_FRAME_ACTION_ADD);
+ gtk_action_set_visible (action, FALSE);
+
+ action = e_tree_view_frame_lookup_toolbar_action (
+ tree_view_frame, E_TREE_VIEW_FRAME_ACTION_REMOVE);
+ gtk_action_set_visible (action, FALSE);
+
+ action = e_tree_view_frame_lookup_toolbar_action (
+ tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_TOP);
+ tooltip = _("Move selected column names to top");
+ gtk_action_set_tooltip (action, tooltip);
+
+ action = e_tree_view_frame_lookup_toolbar_action (
+ tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_UP);
+ tooltip = _("Move selected column names up one row");
+ gtk_action_set_tooltip (action, tooltip);
+
+ action = e_tree_view_frame_lookup_toolbar_action (
+ tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_DOWN);
+ tooltip = _("Move selected column names down one row");
+ gtk_action_set_tooltip (action, tooltip);
+
+ action = e_tree_view_frame_lookup_toolbar_action (
+ tree_view_frame, E_TREE_VIEW_FRAME_ACTION_MOVE_BOTTOM);
+ tooltip = _("Move selected column names to bottom");
+ gtk_action_set_tooltip (action, tooltip);
+
+ action = e_tree_view_frame_lookup_toolbar_action (
+ tree_view_frame, E_TREE_VIEW_FRAME_ACTION_SELECT_ALL);
+ tooltip = _("Select all column names");
+ gtk_action_set_tooltip (action, tooltip);
+
+ /* Configure the tree view columns. */
+
+ column = gtk_tree_view_column_new ();
+ renderer = gtk_cell_renderer_toggle_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "active", COLUMN_ACTIVE);
+ gtk_tree_view_append_column (tree_view, column);
+
+ g_signal_connect (
+ renderer, "toggled",
+ G_CALLBACK (table_column_selector_toggled_cb),
+ tree_view);
+
+ column = gtk_tree_view_column_new ();
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (
+ column, renderer, "text", COLUMN_TITLE);
+ gtk_tree_view_append_column (tree_view, column);
+
+ /* Create and populate the tree model. */
+
+ tree_model = table_column_selector_build_model (selector);
+ gtk_tree_view_set_model (tree_view, tree_model);
+ g_object_unref (tree_model);
+}
+
+static void
+e_table_column_selector_class_init (ETableColumnSelectorClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ETableColumnSelectorPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = table_column_selector_set_property;
+ object_class->get_property = table_column_selector_get_property;
+ object_class->dispose = table_column_selector_dispose;
+ object_class->constructed = table_column_selector_constructed;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_STATE,
+ g_param_spec_object (
+ "state",
+ "Table State",
+ "Column state of the source table",
+ E_TYPE_TABLE_STATE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_table_column_selector_init (ETableColumnSelector *selector)
+{
+ selector->priv = E_TABLE_COLUMN_SELECTOR_GET_PRIVATE (selector);
+}
+
+/**
+ * e_table_column_selector_new:
+ * @state: an #ETableState
+ *
+ * Creates a new #ETableColumnSelector, obtaining the initial column
+ * selection content from @state.
+ *
+ * Note that @state remains unmodified until e_table_column_selector_apply()
+ * is called.
+ *
+ * Returns: an #ETableColumnSelector
+ **/
+GtkWidget *
+e_table_column_selector_new (ETableState *state)
+{
+ g_return_val_if_fail (E_IS_TABLE_STATE (state), NULL);
+
+ return g_object_new (
+ E_TYPE_TABLE_COLUMN_SELECTOR,
+ "state", state, NULL);
+}
+
+/**
+ * e_table_column_selector_get_state:
+ * @selector: an #ETableColumnSelector
+ *
+ * Returns the #ETableState passed to e_table_column_selector_new().
+ *
+ * Returns: an #ETableState
+ **/
+ETableState *
+e_table_column_selector_get_state (ETableColumnSelector *selector)
+{
+ g_return_val_if_fail (E_IS_TABLE_COLUMN_SELECTOR (selector), NULL);
+
+ return selector->priv->state;
+}
+
+/**
+ * e_table_column_selector_apply:
+ * @selector: an #ETableColumnSelector
+ *
+ * Applies the user's column preferences to the @selector's
+ * #ETableColumnSelector:state instance.
+ **/
+void
+e_table_column_selector_apply (ETableColumnSelector *selector)
+{
+ ETableState *state;
+ ETreeViewFrame *tree_view_frame;
+ GtkTreeView *tree_view;
+ GtkTreeModel *tree_model;
+ GArray *active_iters;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+ guint ii;
+
+ g_return_if_fail (E_IS_TABLE_COLUMN_SELECTOR (selector));
+
+ tree_view_frame = E_TREE_VIEW_FRAME (selector);
+ tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);
+ tree_model = gtk_tree_view_get_model (tree_view);
+
+ /* Collect all the "active" rows into an array of iterators. */
+
+ active_iters = g_array_new (FALSE, TRUE, sizeof (GtkTreeIter));
+
+ iter_valid = gtk_tree_model_get_iter_first (tree_model, &iter);
+
+ while (iter_valid) {
+ gboolean active;
+
+ gtk_tree_model_get (
+ tree_model, &iter, COLUMN_ACTIVE, &active, -1);
+
+ if (active)
+ g_array_append_val (active_iters, iter);
+
+ iter_valid = gtk_tree_model_iter_next (tree_model, &iter);
+ }
+
+ /* Reconstruct the ETableState from the array of iterators. */
+
+ state = e_table_column_selector_get_state (selector);
+
+ for (ii = 0; ii < state->col_count; ii++)
+ g_object_unref (state->column_specs[ii]);
+ g_free (state->column_specs);
+ g_free (state->expansions);
+
+ state->col_count = active_iters->len;
+ state->column_specs = g_new0 (
+ ETableColumnSpecification *, active_iters->len);
+ state->expansions = g_new0 (gdouble, active_iters->len);
+
+ for (ii = 0; ii < active_iters->len; ii++) {
+ ETableColumnSpecification *column_spec;
+ gdouble expansion;
+
+ iter = g_array_index (active_iters, GtkTreeIter, ii);
+
+ gtk_tree_model_get (
+ tree_model, &iter,
+ COLUMN_SPECIFICATION, &column_spec,
+ COLUMN_EXPANSION, &expansion,
+ -1);
+
+ state->column_specs[ii] = g_object_ref (column_spec);
+ state->expansions[ii] = expansion;
+
+ g_object_unref (column_spec);
+ }
+
+ g_array_free (active_iters, TRUE);
+}
diff --git a/e-util/e-table-column-selector.h b/e-util/e-table-column-selector.h
new file mode 100644
index 0000000..810b370
--- /dev/null
+++ b/e-util/e-table-column-selector.h
@@ -0,0 +1,81 @@
+/*
+ * e-table-column-selector.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_TABLE_COLUMN_SELECTOR_H
+#define E_TABLE_COLUMN_SELECTOR_H
+
+#include <e-util/e-table-state.h>
+#include <e-util/e-tree-view-frame.h>
+
+/* Standard GObject macros */
+#define E_TYPE_TABLE_COLUMN_SELECTOR \
+ (e_table_column_selector_get_type ())
+#define E_TABLE_COLUMN_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_TABLE_COLUMN_SELECTOR, ETableColumnSelector))
+#define E_TABLE_COLUMN_SELECTOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_TABLE_COLUMN_SELECTOR, ETableColumnSelectorClass))
+#define E_IS_TABLE_COLUMN_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_TABLE_COLUMN_SELECTOR))
+#define E_IS_TABLE_COLUMN_SELECTOR_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_TABLE_COLUMN_SELECTOR))
+#define E_TABLE_COLUMN_SELECTOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_TABLE_COLUMN_SELECTOR, ETableColumnSelectorClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ETableColumnSelector ETableColumnSelector;
+typedef struct _ETableColumnSelectorClass ETableColumnSelectorClass;
+typedef struct _ETableColumnSelectorPrivate ETableColumnSelectorPrivate;
+
+/**
+ * ETableColumnSelector:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _ETableColumnSelector {
+ ETreeViewFrame parent;
+ ETableColumnSelectorPrivate *priv;
+};
+
+struct _ETableColumnSelectorClass {
+ ETreeViewFrameClass parent_class;
+};
+
+GType e_table_column_selector_get_type
+ (void) G_GNUC_CONST;
+GtkWidget * e_table_column_selector_new
+ (ETableState *state);
+ETableState * e_table_column_selector_get_state
+ (ETableColumnSelector *selector);
+void e_table_column_selector_apply
+ (ETableColumnSelector *selector);
+
+G_END_DECLS
+
+#endif /* E_TABLE_COLUMN_SELECTOR_H */
+
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 980c279..eef8830 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -169,6 +169,7 @@
#include <e-util/e-table-click-to-add.h>
#include <e-util/e-table-col-dnd.h>
#include <e-util/e-table-col.h>
+#include <e-util/e-table-column-selector.h>
#include <e-util/e-table-column-specification.h>
#include <e-util/e-table-config.h>
#include <e-util/e-table-defines.h>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]