[libchamplain: 20/45] Merge commit 'origin/wrap~10'
- From: Jiří Techet <jiritechet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libchamplain: 20/45] Merge commit 'origin/wrap~10'
- Date: Sun, 14 Aug 2016 12:30:00 +0000 (UTC)
commit acaf0c146ed9705d6f8bf8cbe215950ed9b23684
Merge: 7cc3ec9 e731df3
Author: Marius Stanciu <stanciumarius94 gmail com>
Date: Wed Jul 27 15:27:21 2016 +0300
Merge commit 'origin/wrap~10'
champlain/champlain-view.c | 528 +++++++++++++++++++++++++++---
champlain/champlain-view.h | 4 +-
demos/launcher-gtk.c | 17 +
docs/reference/libchamplain-sections.txt | 2 +
4 files changed, 507 insertions(+), 44 deletions(-)
---
diff --cc champlain/champlain-view.c
index 49bb22b,3ed2432..c7c93b0
--- a/champlain/champlain-view.c
+++ b/champlain/champlain-view.c
@@@ -104,7 -104,8 +104,8 @@@ enu
PROP_BACKGROUND_PATTERN,
PROP_GOTO_ANIMATION_MODE,
PROP_GOTO_ANIMATION_DURATION,
- PROP_WORLD
++ PROP_WORLD,
+ PROP_HORIZONTAL_WRAP
-
};
#define PADDING 10
@@@ -203,21 -214,7 +215,23 @@@ struct _ChamplainViewPrivat
guint zoom_actor_timeout;
GHashTable *tile_map;
+
+ gint tile_x_first;
+ gint tile_y_first;
+ gint tile_x_last;
+ gint tile_y_last;
+
+ /* Zoom gesture */
+ ClutterGestureAction *zoom_gesture;
+ guint initial_gesture_zoom;
+ gdouble focus_lat;
+ gdouble focus_lon;
+ gboolean zoom_started;
+ gdouble accumulated_scroll_dy;
+
+ ChamplainBoundingBox *world_bbox;
++
+ GHashTable *visible_tiles;
};
G_DEFINE_TYPE (ChamplainView, champlain_view, CLUTTER_TYPE_ACTOR);
@@@ -272,15 -283,31 +299,39 @@@ static ChamplainBoundingBox *get_boundi
guint zoom_level,
gdouble x,
gdouble y);
-
+static void get_tile_bounds (ChamplainView *view,
+ guint *min_x,
+ guint *min_y,
+ guint *max_x,
+ guint *max_y);
- static gboolean tile_in_tile_map (ChamplainView *view,
++static gboolean tile_in_tile_table (ChamplainView *view,
++ GHashTable *table,
+ gint tile_x,
+ gint tile_y);
+ static gdouble
+ x_to_wrap_x (gdouble x, gdouble width)
+ {
+ if (x < 0)
+ x += ((gint)-x / (gint)width + 1) * width;
+
+ return fmod (x, width);
+ }
+
+
+ static gint
+ get_map_width (ChamplainView *view)
+ {
+ gint size, cols;
+ ChamplainViewPrivate *priv = view->priv;
+
+ size = champlain_map_source_get_tile_size (priv->map_source);
+ cols = champlain_map_source_get_column_count (priv->map_source,
+ priv->zoom_level);
+ return size * cols;
+ }
+
+
static void
update_coords (ChamplainView *view,
gdouble x,
@@@ -462,20 -453,19 +513,23 @@@ resize_viewport (ChamplainView *view
champlain_viewport_get_adjustments (CHAMPLAIN_VIEWPORT (priv->viewport), &hadjust,
&vadjust);
- gint map_width = get_map_width (view);
- gint map_height = champlain_map_source_get_row_count (priv->map_source, priv->zoom_level) *
- champlain_map_source_get_tile_size (priv->map_source);
-
- lower_x = MIN (-priv->viewport_width / 2, -priv->viewport_width + map_width / 2);
- lower_y = MIN (-priv->viewport_height / 2, -priv->viewport_height + map_height / 2);
+ get_tile_bounds (view, &min_x, &min_y, &max_x, &max_y);
+ gint x_last = max_x * champlain_map_source_get_tile_size (priv->map_source);
+ gint y_last = max_y * champlain_map_source_get_tile_size (priv->map_source);
+ gint x_first = min_x * champlain_map_source_get_tile_size (priv->map_source);
+ gint y_first = min_y * champlain_map_source_get_tile_size (priv->map_source);
+
+ lower_x = MIN (x_first - priv->viewport_width / 2,
+ (x_first - priv->viewport_width) + (x_last - x_first) / 2);
+ lower_y = MIN (y_first - priv->viewport_height / 2,
+ (y_first - priv->viewport_height) + (y_last - y_first) / 2);
-
- upper_x = MAX (x_last - priv->viewport_width / 2, (x_last - x_first) / 2);
++
+ if (priv->hwrap)
- upper_x = MAX (map_width + priv->viewport_width / 2, priv->viewport_width + map_width / 2);
++ upper_x = MAX (x_last - x_first + priv->viewport_width / 2, priv->viewport_width + (x_last - x_first) /
2);
+ else
- upper_x = MAX (map_width - priv->viewport_width / 2, map_width / 2);
-
- upper_y = MAX (map_height - priv->viewport_height / 2, map_height / 2);
++ upper_x = MAX (x_last - priv->viewport_width / 2, (x_last - x_first) / 2);
+ upper_y = MAX (y_last - priv->viewport_height / 2, (y_last - y_first)/ 2);
/* we don't want to get notified about the position change now */
g_signal_handlers_block_by_func (priv->viewport, G_CALLBACK (viewport_pos_changed_cb), view);
@@@ -563,10 -554,10 +617,14 @@@ champlain_view_get_property (GObject *o
g_value_set_uint (value, priv->goto_duration);
break;
+ case PROP_WORLD:
+ g_value_set_boxed (value, priv->world_bbox);
+ break;
+
+ case PROP_HORIZONTAL_WRAP:
+ g_value_set_boolean (value, champlain_view_get_horizontal_wrap (view));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@@ -644,10 -635,10 +702,14 @@@ champlain_view_set_property (GObject *o
priv->goto_duration = g_value_get_uint (value);
break;
+ case PROP_WORLD:
+ champlain_view_set_world (view, g_value_get_boxed (value));
+ break;
+
+ case PROP_HORIZONTAL_WRAP:
+ champlain_view_set_horizontal_wrap (view, g_value_get_boolean (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@@ -716,13 -701,11 +778,18 @@@ champlain_view_dispose (GObject *object
priv->tile_map = NULL;
}
+ if (priv->zoom_gesture)
+ {
+ clutter_actor_remove_action (CLUTTER_ACTOR (view),
+ CLUTTER_ACTION (priv->zoom_gesture));
+ priv->zoom_gesture = NULL;
+ }
+
+ if (priv->visible_tiles != NULL)
+ {
+ g_hash_table_destroy (priv->visible_tiles);
+ priv->visible_tiles = NULL;
+ }
priv->map_layer = NULL;
priv->license_actor = NULL;
@@@ -1061,25 -1034,20 +1128,39 @@@ champlain_view_class_init (ChamplainVie
G_PARAM_READWRITE));
/**
+ * ChamplainView:world:
+ *
+ * Set a bounding box to limit the world to. No tiles will be loaded
+ * outside of this bounding box. It will not be possible to scroll outside
+ * of this bounding box.
+ *
+ * Default world is the actual world.
+ *
+ * Since: 0.12.11
+ */
+ g_object_class_install_property (object_class,
+ PROP_WORLD,
+ g_param_spec_boxed ("world",
+ "The world",
+ "The bounding box to limit the #ChamplainView to",
+ CHAMPLAIN_TYPE_BOUNDING_BOX,
+ G_PARAM_READWRITE));
+
+ /**
+ * ChamplainView:horizontal-wrap:
+ *
+ * Determines whether the view should wrap horizontally.
+ *
+ */
+ g_object_class_install_property (object_class,
+ PROP_HORIZONTAL_WRAP,
+ g_param_spec_boolean ("horizontal-wrap",
+ "Horizontal wrap",
+ "Determines whether the view should wrap horizontally.",
+ FALSE,
+ CHAMPLAIN_PARAM_READWRITE));
+
+ /**
* ChamplainView::animation-completed:
*
* The #ChamplainView::animation-completed signal is emitted when any animation in the view
@@@ -1345,13 -1283,13 +1498,18 @@@ champlain_view_init (ChamplainView *vie
priv->redraw_timeout = 0;
priv->zoom_actor_timeout = 0;
priv->tile_map = g_hash_table_new_full (g_int64_hash, g_int64_equal, slice_free_gint64, NULL);
+ priv->visible_tiles = g_hash_table_new_full (g_int64_hash, g_int64_equal, slice_free_gint64, NULL);
priv->goto_duration = 0;
priv->goto_mode = CLUTTER_EASE_IN_OUT_CIRC;
+ priv->world_bbox = champlain_bounding_box_new ();
+ priv->world_bbox->left = CHAMPLAIN_MIN_LONGITUDE;
+ priv->world_bbox->bottom = CHAMPLAIN_MIN_LATITUDE;
+ priv->world_bbox->right = CHAMPLAIN_MAX_LONGITUDE;
+ priv->world_bbox->top = CHAMPLAIN_MAX_LATITUDE;
+ priv->num_clones = 0;
+ priv->map_clones = NULL;
+ priv->user_layer_slots = NULL;
+ priv->hwrap = FALSE;
clutter_actor_set_background_color (CLUTTER_ACTOR (view), &color);
@@@ -1790,109 -1849,6 +2078,109 @@@ champlain_view_zoom_out (ChamplainView
}
+static void
+layers_to_surface (ChamplainView *view,
+ cairo_t *cr)
+{
+ ClutterActorIter iter;
+ ClutterActor *child;
+
+ clutter_actor_iter_init (&iter, view->priv->user_layers);
+ while (clutter_actor_iter_next (&iter, &child))
+ {
+ ChamplainLayer *layer = CHAMPLAIN_LAYER (child);
+ cairo_surface_t *surface;
+
+ if (!CHAMPLAIN_IS_EXPORTABLE (layer))
+ continue;
+
+ surface = champlain_exportable_get_surface (CHAMPLAIN_EXPORTABLE (layer));
+ if (!surface)
+ continue;
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_paint(cr);
+ }
+}
+
+
+/**
+ * champlain_view_to_surface:
+ * @view: a #ChamplainView
+ * @include_layers: Set to %TRUE if you want to include layers
+ *
+ * Will generate a #cairo_surface_t that represents the current view
+ * of the map. Without any markers or layers. If the current #ChamplainRenderer
+ * used does not support this, this function will return %NULL.
+ *
+ * If @include_layers is set to %TRUE all layers that implement
+ * #ChamplainExportable will be included in the surface.
+ *
+ * The #ChamplainView also need to be in #CHAMPLAIN_STATE_DONE state.
+ *
+ * Returns: (transfer full): a #cairo_surface_t or %NULL on failure. Free with
+ * cairo_surface_destroy() when done.
+ */
+cairo_surface_t *
+champlain_view_to_surface (ChamplainView *view,
+ gboolean include_layers)
+{
+ DEBUG_LOG ()
+
+ g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), NULL);
+
+ ChamplainViewPrivate *priv = view->priv;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ ClutterActorIter iter;
+ ClutterActor *child;
+ gdouble width, height;
+
+ if (priv->state != CHAMPLAIN_STATE_DONE)
+ return NULL;
+
+ width = clutter_actor_get_width (CLUTTER_ACTOR (view));
+ height = clutter_actor_get_height (CLUTTER_ACTOR (view));
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create (surface);
+
+ clutter_actor_iter_init (&iter, priv->map_layer);
+ while (clutter_actor_iter_next (&iter, &child))
+ {
+ ChamplainTile *tile = CHAMPLAIN_TILE (child);
+ guint tile_x = champlain_tile_get_x (tile);
+ guint tile_y = champlain_tile_get_y (tile);
+ guint tile_size = champlain_tile_get_size (tile);
+
- if (tile_in_tile_map (view, tile_x, tile_y))
++ if (tile_in_tile_table (view, priv->tile_map, tile_x, tile_y))
+ {
+ cairo_surface_t *tile_surface;
+ double x, y, opacity;
+
+ tile_surface = champlain_exportable_get_surface (CHAMPLAIN_EXPORTABLE (tile));
+ if (!tile_surface)
+ {
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ return NULL;
+ }
+ opacity = ((double) clutter_actor_get_opacity (CLUTTER_ACTOR (tile))) / 255.0;
+ x = ((double) tile_x * tile_size) - priv->viewport_x;
+ y = ((double) tile_y * tile_size) - priv->viewport_y;
+ cairo_set_source_surface (cr,
+ tile_surface,
+ x, y);
+ cairo_paint_with_alpha(cr, opacity);
+ }
+ }
+
+ if (include_layers)
+ layers_to_surface (view, cr);
+
+ cairo_destroy (cr);
+ return surface;
+}
+
+
/**
* champlain_view_set_zoom_level:
* @view: a #ChamplainView
@@@ -2292,12 -2200,9 +2593,12 @@@ tile_table_set (ChamplainView *view
gint64 *key = g_slice_alloc (sizeof(gint64));
*key = (gint64)tile_y * count + tile_x;
if (value)
- g_hash_table_insert (priv->tile_map, key, GINT_TO_POINTER (TRUE));
+ g_hash_table_insert (table, key, GINT_TO_POINTER (TRUE));
else
- g_hash_table_remove (table, key);
+ {
- g_hash_table_remove (priv->tile_map, key);
++ g_hash_table_remove (table, key);
+ g_slice_free (gint64, key);
+ }
}
@@@ -2389,32 -2298,47 +2694,52 @@@ load_visible_tiles (ChamplainView *view
ClutterActorIter iter;
gint size;
ClutterActor *child;
- gint x_count, y_count;
- gint x_count, y_count, max_x_end, max_y_end;
- gint x_start, y_start, x_end, y_end;
++ gint x_count, y_count, column_count;
+ guint min_x, min_y, max_x, max_y;
gint arm_size, arm_max, turn;
gint dirs[5] = { 0, 1, 0, -1, 0 };
gint i, x, y;
size = champlain_map_source_get_tile_size (priv->map_source);
+ get_tile_bounds (view, &min_x, &min_y, &max_x, &max_y);
- max_x_end = champlain_map_source_get_column_count (priv->map_source, priv->zoom_level);
- max_y_end = champlain_map_source_get_row_count (priv->map_source, priv->zoom_level);
+ x_count = ceil ((gfloat) priv->viewport_width / size) + 1;
- y_count = ceil ((gfloat) priv->viewport_height / size) + 1;
++ column_count = champlain_map_source_get_column_count (priv->map_source, priv->zoom_level);
- priv->tile_x_first = CLAMP (priv->viewport_x / size, min_x, max_x);
- priv->tile_y_first = CLAMP (priv->viewport_y / size, min_y, max_y);
- x_count = ceil ((gfloat) (priv->viewport_width) / size) + 1;
- if (priv->hwrap)
++ if (priv->hwrap)
+ {
- x_start = priv->viewport_x / size;
- x_end = x_start + x_count;
- }
- else
++ priv->tile_x_first = priv->viewport_x / size;
++ priv->tile_x_last = priv->tile_x_first + x_count;
++ }
++ else
+ {
- x_start = CLAMP (priv->viewport_x / size, 0, max_x_end);
- x_end = MIN (x_start + x_count, max_x_end);
- x_count = x_end - x_start;
++ priv->tile_x_first = CLAMP (priv->viewport_x / size, min_x, max_x);
++ priv->tile_x_last = priv->tile_x_first + x_count;
++ priv->tile_x_last = CLAMP (priv->tile_x_last, priv->tile_x_first, max_x);
++ x_count = priv->tile_x_last - priv->tile_x_first;
+ }
- priv->tile_x_last = priv->tile_x_first + x_count;
- y_start = CLAMP (priv->viewport_y / size, 0, max_y_end);
+ y_count = ceil ((gfloat) priv->viewport_height / size) + 1;
- y_end = MIN (y_start + y_count, max_y_end);
- y_count = y_end - y_start;
++ priv->tile_y_first = CLAMP (priv->viewport_y / size, min_y, max_y);
+ priv->tile_y_last = priv->tile_y_first + y_count;
-
- priv->tile_x_last = CLAMP (priv->tile_x_last, priv->tile_x_first, max_x);
+ priv->tile_y_last = CLAMP (priv->tile_y_last, priv->tile_y_first, max_y);
-
- x_count = priv->tile_x_last - priv->tile_x_first;
+ y_count = priv->tile_y_last - priv->tile_y_first;
+
+ DEBUG ("Range %d, %d to %d, %d", priv->tile_x_first, priv->tile_y_first, priv->tile_x_last,
priv->tile_y_last);
+
+
+ g_hash_table_remove_all (priv->visible_tiles);
- for (x = x_start; x < x_end; x++)
- for (y = y_start; y < y_end; y++)
++ for (x = priv->tile_x_first; x < priv->tile_x_last; x++)
++ for (y = priv->tile_y_first; y < priv->tile_y_last; y++)
+ {
+ gint tile_x = x;
+
+ if (priv->hwrap)
- tile_x = x_to_wrap_x (tile_x, max_x_end);
++ tile_x = x_to_wrap_x (tile_x, column_count);
+
+ tile_table_set (view, priv->visible_tiles, tile_x, y, TRUE);
+ }
+
/* fill background tiles */
if (priv->background_content != NULL)
fill_background_tiles (view);
@@@ -2449,14 -2372,21 +2773,22 @@@
{
for (i = 0; i < arm_size; i++)
{
- if (!tile_in_tile_map (view, x, y) && y >= priv->tile_y_first && y < priv->tile_y_last && x >=
priv->tile_x_first && x < priv->tile_x_last)
+ gint tile_x = x;
+
+ if (priv->hwrap)
- tile_x = x_to_wrap_x (tile_x, max_x_end);
++ tile_x = x_to_wrap_x (tile_x, column_count);
+
+ if (!tile_in_tile_table (view, priv->tile_map, tile_x, y) &&
+ tile_in_tile_table (view, priv->visible_tiles, tile_x, y) &&
- y >= y_start && y < y_end)
++ y >= priv->tile_y_first &&
++ y < priv->tile_y_last)
{
FillTileCallbackData *data;
- DEBUG ("Loading tile %d, %d, %d", priv->zoom_level, tile_x, y);
+ DEBUG ("Loading tile %d, %d, %d", priv->zoom_level, x, y);
data = g_slice_new (FillTileCallbackData);
- data->x = x;
+ data->x = tile_x;
data->y = y;
data->size = size;
data->zoom_level = priv->zoom_level;
@@@ -2927,12 -2924,13 +3326,16 @@@ zoom_animation_completed (ClutterActor
priv->animating_zoom = FALSE;
position_zoom_actor (view);
clutter_actor_show (priv->user_layers);
+ if (priv->hwrap)
+ update_clones (view);
g_signal_handlers_disconnect_by_func (actor, zoom_animation_completed, view);
+ g_signal_emit_by_name (view, "animation-completed::zoom", NULL);
}
++
++
static void
show_zoom_actor (ChamplainView *view,
guint zoom_level,
@@@ -2949,17 -2947,20 +3352,21 @@@
{
ClutterActorIter iter;
ClutterActor *child;
- gint size;
+ ClutterActor *tile_container;
+ gint size, i;
gint x_first, y_first;
gdouble zoom_actor_width, zoom_actor_height;
- gdouble max_x_end, max_y_end;
++ gint column_count;
gdouble deltax, deltay;
+ guint min_x, min_y, max_x, max_y;
+ get_tile_bounds (view, &min_x, &min_y, &max_x, &max_y);
size = champlain_map_source_get_tile_size (priv->map_source);
- max_x_end = champlain_map_source_get_column_count (priv->map_source, priv->zoom_level);
- max_y_end = champlain_map_source_get_row_count (priv->map_source, priv->zoom_level);
++ column_count = champlain_map_source_get_column_count (priv->map_source, priv->zoom_level);
+
- x_first = CLAMP (priv->viewport_x / size, 0, max_x_end);
- y_first = CLAMP (priv->viewport_y / size, 0, max_y_end);
+ x_first = CLAMP (priv->viewport_x / size, min_x, max_x);
+ y_first = CLAMP (priv->viewport_y / size, min_y, max_y);
clutter_actor_destroy_all_children (priv->zoom_overlay_actor);
zoom_actor = clutter_actor_new ();
@@@ -2994,6 -2996,25 +3402,25 @@@
clutter_actor_set_position (CLUTTER_ACTOR (tile), (tile_x - x_first) * size, (tile_y - y_first) *
size);
}
+ clutter_actor_add_child (zoom_actor, tile_container);
+
+ if (priv->hwrap)
+ {
+ GList *old_clone = priv->map_clones;
+ for (i = 0; i < priv->num_clones; i++)
+ {
+ ClutterActor *clone_right = clutter_clone_new (tile_container);
+ clutter_actor_hide (CLUTTER_ACTOR (old_clone->data));
+ clutter_actor_set_reactive (CLUTTER_ACTOR (old_clone->data), FALSE);
+ gfloat tiles_x;
+
+ clutter_actor_get_position (tile_container, &tiles_x, NULL);
- clutter_actor_set_x (clone_right, tiles_x + (i * max_x_end * size));
++ clutter_actor_set_x (clone_right, tiles_x + (i * column_count * size));
+
+ old_clone = old_clone->next;
+ clutter_actor_add_child (zoom_actor, clone_right);
+ }
+ }
zoom_actor_width = clutter_actor_get_width (zoom_actor);
zoom_actor_height = clutter_actor_get_height (zoom_actor);
@@@ -3026,7 -3047,7 +3453,15 @@@
if (!priv->animating_zoom)
{
-- clutter_actor_hide (priv->user_layers);
++ if (priv->hwrap)
++ {
++ GList *slot;
++ for (slot = priv->user_layer_slots; slot != NULL; slot = slot->next)
++ clutter_actor_hide (CLUTTER_ACTOR (slot->data));
++ }
++ else
++ clutter_actor_hide (priv->user_layers);
++
g_signal_connect (zoom_actor, "transition-stopped::scale-x", G_CALLBACK
(zoom_animation_completed), view);
}
diff --cc champlain/champlain-view.h
index 43eca16,494f297..9c8c057
--- a/champlain/champlain-view.h
+++ b/champlain/champlain-view.h
@@@ -126,9 -126,9 +126,10 @@@ void champlain_view_set_animate_zoom (C
gboolean value);
void champlain_view_set_background_pattern (ChamplainView *view,
ClutterContent *background);
+void champlain_view_set_world (ChamplainView *view,
+ ChamplainBoundingBox *bbox);
-
+ void champlain_view_set_horizontal_wrap (ChamplainView *view,
+ gboolean wrap);
-
void champlain_view_add_layer (ChamplainView *view,
ChamplainLayer *layer);
void champlain_view_remove_layer (ChamplainView *view,
@@@ -147,7 -145,7 +148,8 @@@ gboolean champlain_view_get_zoom_on_dou
gboolean champlain_view_get_animate_zoom (ChamplainView *view);
ChamplainState champlain_view_get_state (ChamplainView *view);
ClutterContent *champlain_view_get_background_pattern (ChamplainView *view);
+ChamplainBoundingBox *champlain_view_get_world (ChamplainView *view);
+ gboolean champlain_view_get_horizontal_wrap (ChamplainView *view);
void champlain_view_reload_tiles (ChamplainView *view);
diff --cc docs/reference/libchamplain-sections.txt
index 970d819,097a21a..cf51b41
--- a/docs/reference/libchamplain-sections.txt
+++ b/docs/reference/libchamplain-sections.txt
@@@ -157,8 -154,8 +158,9 @@@ champlain_view_get_keep_center_on_resiz
champlain_view_get_zoom_on_double_click
champlain_view_get_animate_zoom
champlain_view_get_background_pattern
+ champlain_view_get_horizontal_wrap
champlain_view_reload_tiles
+champlain_view_to_surface
champlain_view_x_to_longitude
champlain_view_y_to_latitude
champlain_view_longitude_to_x
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]