summaryrefslogtreecommitdiff
path: root/champlain
diff options
context:
space:
mode:
authorJiří Techet <techet@gmail.com>2016-08-14 10:45:33 +0100
committerJiří Techet <techet@gmail.com>2016-08-14 10:45:33 +0100
commit4d492a31bf845dc34cb27c19c1fab6971ce151e3 (patch)
treed7ec5375c26084823cc64d59e6cff60bf60daa01 /champlain
parente60d0fd405a93f517fd7478b862dfae5bfb87d4f (diff)
parent08624901af6b45004eb7d3ce94e8e2a196876c44 (diff)
downloadlibchamplain-4d492a31bf845dc34cb27c19c1fab6971ce151e3.tar.gz
Merge branch 'wrap2'
Diffstat (limited to 'champlain')
-rw-r--r--champlain/champlain-path-layer.c234
-rw-r--r--champlain/champlain-view.c619
-rw-r--r--champlain/champlain-view.h7
3 files changed, 787 insertions, 73 deletions
diff --git a/champlain/champlain-path-layer.c b/champlain/champlain-path-layer.c
index 4a8c0fc..47956ad 100644
--- a/champlain/champlain-path-layer.c
+++ b/champlain/champlain-path-layer.c
@@ -87,9 +87,35 @@ struct _ChamplainPathLayerPrivate
gdouble *dash;
guint num_dashes;
- ClutterContent *canvas;
cairo_surface_t *surface;
+
+ /* In order to correctly render paths in the horizontal wrap,
+ * the path_actor (a map-wide actor) contains two children that
+ * split the visible paths.
+ *
+ * The right_actor renders paths visible on the original map layer.
+ * (from viewport's x coordinate to the rightmost point on the map)
+
+ * The left_actor renders paths visible on the first cloned map layer.
+ * (a fixed size, from the leftmost point on the map)
+ *
+ * If horizontal wrap is disabled, the left_actor won't render
+ * anything.
+ */
ClutterActor *path_actor;
+
+ ClutterActor *right_actor;
+ ClutterActor *left_actor;
+
+ ClutterContent *right_canvas;
+ ClutterContent *left_canvas;
+
+ cairo_surface_t *right_surface;
+ cairo_surface_t *left_surface;
+
+ gboolean right_surface_updated;
+ gboolean left_surface_updated;
+
GList *nodes;
gboolean redraw_scheduled;
};
@@ -225,13 +251,17 @@ champlain_path_layer_dispose (GObject *object)
if (priv->view != NULL)
set_view (CHAMPLAIN_LAYER (self), NULL);
- if (priv->canvas)
+ if (priv->right_canvas)
{
- g_object_unref (priv->canvas);
- priv->canvas = NULL;
+ g_object_unref (priv->right_canvas);
+ g_object_unref (priv->left_canvas);
+ priv->right_canvas = NULL;
+ priv->left_canvas = NULL;
}
g_clear_pointer (&priv->surface, cairo_surface_destroy);
+ g_clear_pointer (&priv->right_surface, cairo_surface_destroy);
+ g_clear_pointer (&priv->left_surface, cairo_surface_destroy);
G_OBJECT_CLASS (champlain_path_layer_parent_class)->dispose (object);
}
@@ -379,6 +409,18 @@ champlain_path_layer_class_init (ChamplainPathLayerClass *klass)
"surface");
}
+static void
+initialize_child_actor (ChamplainPathLayer *self,
+ ClutterActor *child_actor,
+ ClutterContent *canvas)
+{
+ ChamplainPathLayerPrivate *priv = self->priv;;
+
+ clutter_actor_set_content (child_actor, canvas);
+ g_signal_connect (canvas, "draw", G_CALLBACK (redraw_path), self);
+ clutter_actor_set_size (child_actor, 255, 255);
+ clutter_actor_add_child (priv->path_actor, child_actor);
+}
static void
champlain_path_layer_init (ChamplainPathLayer *self)
@@ -401,14 +443,28 @@ champlain_path_layer_init (ChamplainPathLayer *self)
priv->fill_color = clutter_color_copy (&DEFAULT_FILL_COLOR);
priv->stroke_color = clutter_color_copy (&DEFAULT_STROKE_COLOR);
- priv->canvas = clutter_canvas_new ();
- clutter_canvas_set_size (CLUTTER_CANVAS (priv->canvas), 255, 255);
- g_signal_connect (priv->canvas, "draw", G_CALLBACK (redraw_path), self);
-
priv->path_actor = clutter_actor_new ();
- clutter_actor_set_size (priv->path_actor, 255, 255);
- clutter_actor_set_content (priv->path_actor, priv->canvas);
clutter_actor_add_child (CLUTTER_ACTOR (self), priv->path_actor);
+ clutter_actor_set_size (priv->path_actor, 255, 255);
+
+ priv->right_actor = clutter_actor_new ();
+ priv->left_actor = clutter_actor_new ();
+
+ priv->right_canvas = clutter_canvas_new ();
+ priv->left_canvas = clutter_canvas_new ();
+
+ priv->surface = NULL;
+ priv->right_surface = NULL;
+ priv->left_surface = NULL;
+
+ priv->right_surface_updated = FALSE;
+ priv->left_surface_updated = FALSE;
+
+ clutter_canvas_set_size (CLUTTER_CANVAS (priv->right_canvas), 255, 255);
+ clutter_canvas_set_size (CLUTTER_CANVAS (priv->left_canvas), 0, 0);
+
+ initialize_child_actor (self, priv->right_actor, priv->right_canvas);
+ initialize_child_actor (self, priv->left_actor, priv->left_canvas);
}
@@ -468,20 +524,89 @@ champlain_path_layer_new ()
}
+static void
+get_map_size (ChamplainView *view, gint *width, gint *height)
+{
+ gint size, rows, cols;
+ ChamplainMapSource *map_source = champlain_view_get_map_source (view);
+ gint zoom_level = champlain_view_get_zoom_level (view);
+ size = champlain_map_source_get_tile_size (map_source);
+ rows = champlain_map_source_get_row_count (map_source,
+ zoom_level);
+ cols = champlain_map_source_get_column_count (map_source,
+ zoom_level);
+ if (width)
+ *width = size * rows;
+
+ if (height)
+ *height = size * cols;
+}
+
+
static gboolean
invalidate_canvas (ChamplainPathLayer *layer)
{
ChamplainPathLayerPrivate *priv = layer->priv;
- gfloat width, height;
-
- width = 256;
- height = 256;
+ gfloat view_width, view_height;
+ gint map_width, map_height;
+ gint viewport_x, viewport_y;
+ gint anchor_x, anchor_y;
+ gfloat right_actor_width, right_actor_height;
+ gfloat left_actor_width, left_actor_height;
+
+ right_actor_width = 256;
+ right_actor_height = 256;
+ left_actor_width = 0;
+ left_actor_height = 0;
+ map_width = 256;
+ map_height = 256;
+
if (priv->view != NULL)
- clutter_actor_get_size (CLUTTER_ACTOR (priv->view), &width, &height);
+ {
+ get_map_size (priv->view, &map_width, &map_height);
+ clutter_actor_get_size (CLUTTER_ACTOR (priv->view), &view_width, &view_height);
+ champlain_view_get_viewport_origin (priv->view, &viewport_x, &viewport_y);
+ champlain_view_get_viewport_anchor (priv->view, &anchor_x, &anchor_y);
+
+ /* For efficiency in terms of clipping, the path actors must have a minimal size.
+ * The right_actor renders the paths on the visible side of the original map layer
+ * (from viewport offset to end of the map).
+ * The left_actor renders the paths on the visible side of the first cloned map layer
+ * (from the leftmost point on the map, clamped by the viewport width).
+ */
+ right_actor_width = MIN (map_width - (viewport_x + anchor_x), (gint)view_width);
+ right_actor_height = MIN (map_height - (viewport_y + anchor_y), (gint)view_height);
+ left_actor_width = MIN (view_width - right_actor_width, map_width - right_actor_width);
+ left_actor_height = right_actor_height;
+
+ /* Ensure sizes are positive */
+ right_actor_width = MAX (0, right_actor_width);
+ right_actor_height = MAX (0, right_actor_height);
+ left_actor_width = MAX (0, left_actor_width);
+ left_actor_height = MAX (0, left_actor_height);
+ }
+
+ clutter_actor_set_size (priv->path_actor, map_width, map_height);
+
+ clutter_actor_set_size (priv->right_actor, right_actor_width, right_actor_height);
+ clutter_canvas_set_size (CLUTTER_CANVAS (priv->right_canvas), right_actor_width, right_actor_height);
+ priv->right_surface_updated = FALSE;
+ clutter_content_invalidate (priv->right_canvas);
+
+ /* Since the left actor only renders paths visible on the clone, it should be hidden
+ * when no clone is visible.
+ */
+ if (left_actor_width != 0)
+ {
+ clutter_actor_set_size (priv->left_actor, left_actor_width, left_actor_height);
+ clutter_canvas_set_size (CLUTTER_CANVAS (priv->left_canvas), left_actor_width, left_actor_height);
+ priv->left_surface_updated = FALSE;
+ clutter_content_invalidate (priv->left_canvas);
+ clutter_actor_show (priv->left_actor);
+ }
+ else
+ clutter_actor_hide (priv->left_actor);
- clutter_canvas_set_size (CLUTTER_CANVAS (priv->canvas), width, height);
- clutter_actor_set_size (priv->path_actor, width, height);
- clutter_content_invalidate (priv->canvas);
priv->redraw_scheduled = FALSE;
return FALSE;
@@ -664,6 +789,60 @@ relocate_cb (G_GNUC_UNUSED GObject *gobject,
schedule_redraw (layer);
}
+static void
+update_surface (ChamplainPathLayer *layer,
+ ClutterCanvas *canvas,
+ cairo_surface_t *surface)
+{
+ ChamplainPathLayerPrivate *priv = layer->priv;
+
+ if (canvas == CLUTTER_CANVAS (priv->right_canvas))
+ {
+ cairo_surface_destroy (priv->right_surface);
+ priv->right_surface = cairo_surface_reference (surface);
+ priv->right_surface_updated = TRUE;
+ }
+ else if (canvas == CLUTTER_CANVAS (priv->left_canvas))
+ {
+ cairo_surface_destroy (priv->left_surface);
+ priv->left_surface = cairo_surface_reference (surface);
+ priv->left_surface_updated = TRUE;
+ }
+
+ /* Updating the exportable surface. Path layer has two surfaces (one for each canvas)
+ * which have to be merged into a single new one.
+ */
+ if (priv->left_surface_updated && priv->right_surface_updated)
+ {
+ gfloat view_width, view_height;
+ gint map_width, viewport_x, anchor_x;
+ cairo_surface_t *new_surface;
+ cairo_t *cr;
+
+ get_map_size (priv->view, &map_width, NULL);
+ clutter_actor_get_size (CLUTTER_ACTOR (priv->view), &view_width, &view_height);
+ champlain_view_get_viewport_origin (priv->view, &viewport_x, NULL);
+ champlain_view_get_viewport_anchor (priv->view, &anchor_x, NULL);
+
+ new_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, view_width, view_height);
+ cr = cairo_create (new_surface);
+
+ cairo_set_source_surface (cr,
+ priv->right_surface,
+ 0, 0);
+ cairo_paint (cr);
+
+ cairo_set_source_surface (cr,
+ priv->left_surface,
+ map_width - viewport_x - anchor_x, 0);
+ cairo_paint (cr);
+
+ set_surface (CHAMPLAIN_EXPORTABLE (layer), new_surface);
+
+ cairo_surface_destroy (new_surface);
+ cairo_destroy (cr);
+ }
+}
static gboolean
redraw_path (ClutterCanvas *canvas,
@@ -675,7 +854,8 @@ redraw_path (ClutterCanvas *canvas,
ChamplainPathLayerPrivate *priv = layer->priv;
GList *elem;
ChamplainView *view = priv->view;
- gint x, y;
+ gint viewport_x, viewport_y;
+ gint anchor_x, anchor_y;
/* layer not yet added to the view */
if (view == NULL)
@@ -684,8 +864,13 @@ redraw_path (ClutterCanvas *canvas,
if (!priv->visible || width == 0.0 || height == 0.0)
return FALSE;
- champlain_view_get_viewport_origin (priv->view, &x, &y);
- clutter_actor_set_position (priv->path_actor, x, y);
+ champlain_view_get_viewport_origin (priv->view, &viewport_x, &viewport_y);
+ champlain_view_get_viewport_anchor (priv->view, &anchor_x, &anchor_y);
+
+ if (canvas == CLUTTER_CANVAS (priv->right_canvas))
+ clutter_actor_set_position (priv->right_actor, viewport_x, viewport_y);
+ else
+ clutter_actor_set_position (priv->left_actor, -anchor_x, viewport_y);
/* Clear the drawing area */
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
@@ -702,7 +887,10 @@ redraw_path (ClutterCanvas *canvas,
x = champlain_view_longitude_to_x (view, champlain_location_get_longitude (location));
y = champlain_view_latitude_to_y (view, champlain_location_get_latitude (location));
- cairo_line_to (cr, x, y);
+ if (canvas == CLUTTER_CANVAS (priv->right_canvas))
+ cairo_line_to (cr, x, y);
+ else
+ cairo_line_to (cr, x + (viewport_x + anchor_x), y);
}
if (priv->closed_path)
@@ -729,7 +917,7 @@ redraw_path (ClutterCanvas *canvas,
if (priv->stroke)
cairo_stroke (cr);
- set_surface (CHAMPLAIN_EXPORTABLE (layer), cairo_get_target (cr));
+ update_surface (layer, canvas, cairo_get_target (cr));
return FALSE;
}
diff --git a/champlain/champlain-view.c b/champlain/champlain-view.c
index 49bb22b..63ac2bb 100644
--- a/champlain/champlain-view.c
+++ b/champlain/champlain-view.c
@@ -104,7 +104,8 @@ enum
PROP_BACKGROUND_PATTERN,
PROP_GOTO_ANIMATION_MODE,
PROP_GOTO_ANIMATION_DURATION,
- PROP_WORLD
+ PROP_WORLD,
+ PROP_HORIZONTAL_WRAP
};
#define PADDING 10
@@ -147,16 +148,27 @@ struct _ChamplainViewPrivate
/* ChamplainView */
ClutterActor *kinetic_scroll; /* kinetic_scroll */
ClutterActor *viewport; /* viewport */
- /* viewport_container */
+ ClutterActor *viewport_container; /* viewport_container */
ClutterActor *background_layer; /* background_layer */
ClutterActor *zoom_layer; /* zoom_layer */
ClutterActor *map_layer; /* map_layer */
- ClutterActor *user_layers; /* user_layers */
+ /* map_layer clones */
+ ClutterActor *user_layers; /* user_layers and clones */
ClutterActor *zoom_overlay_actor; /* zoom_overlay_actor */
ClutterActor *license_actor; /* license_actor */
ClutterContent *background_content;
+ gboolean hwrap;
+ gint num_clones;
+ GList *map_clones;
+ /* There are num_clones user layer slots, overlayed on the map clones.
+ * The first slot initially contains the real user_layers actor, and the
+ * rest contain clones. Whenever the cursor enters a clone slot, its content
+ * is swapped with the real one so as to ensure reactiveness to events.
+ */
+ GList *user_layer_slots;
+
gdouble viewport_x;
gdouble viewport_y;
gint viewport_width;
@@ -218,11 +230,14 @@ struct _ChamplainViewPrivate
gdouble accumulated_scroll_dy;
ChamplainBoundingBox *world_bbox;
+
+ GHashTable *visible_tiles;
};
G_DEFINE_TYPE (ChamplainView, champlain_view, CLUTTER_TYPE_ACTOR);
-
+static void exclusive_destroy_clone (ClutterActor *clone);
+static void update_clones (ChamplainView *view);
static gboolean scroll_event (ClutterActor *actor,
ClutterScrollEvent *event,
ChamplainView *view);
@@ -244,6 +259,18 @@ static void viewport_pos_changed_cb (GObject *gobject,
static gboolean kinetic_scroll_button_press_cb (ClutterActor *actor,
ClutterButtonEvent *event,
ChamplainView *view);
+static ClutterActor *sample_user_layer_at_pos (ChamplainView *view,
+ gfloat x,
+ gfloat y);
+static void swap_user_layer_slots (ChamplainView *view,
+ gint original_index,
+ gint clone_index);
+static gboolean viewport_motion_cb (ClutterActor *actor,
+ ClutterMotionEvent *event,
+ ChamplainView *view);
+static gboolean viewport_press_cb (ClutterActor *actor,
+ ClutterButtonEvent *event,
+ ChamplainView *view);
static void load_visible_tiles (ChamplainView *view,
gboolean relocate);
static gboolean view_set_zoom_level_at (ChamplainView *view,
@@ -277,10 +304,34 @@ static void get_tile_bounds (ChamplainView *view,
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,
@@ -361,10 +412,27 @@ view_relocated_cb (G_GNUC_UNUSED ChamplainViewport *viewport,
ChamplainView *view)
{
ChamplainViewPrivate *priv = view->priv;
-
+ gint anchor_x, anchor_y, new_width, new_height;
+ gint tile_size, column_count, row_count;
+
clutter_actor_destroy_all_children (priv->zoom_layer);
load_visible_tiles (view, TRUE);
g_signal_emit_by_name (view, "layer-relocated", NULL);
+
+ /* Clutter clones need their source actor to have an explicitly set size to display properly */
+ tile_size = champlain_map_source_get_tile_size (priv->map_source);
+ column_count = champlain_map_source_get_column_count (priv->map_source, priv->zoom_level);
+ row_count = champlain_map_source_get_row_count (priv->map_source, priv->zoom_level);
+ champlain_viewport_get_anchor (CHAMPLAIN_VIEWPORT (priv->viewport), &anchor_x, &anchor_y);
+
+ /* The area containing tiles in the map layer is actually column_count * tile_size wide (same
+ * for height), but the viewport anchor acts as an offset for the tile actors, causing the map
+ * layer to contain some empty space as well.
+ */
+ new_width = column_count * tile_size + anchor_x;
+ new_height = row_count * tile_size + anchor_y;
+
+ clutter_actor_set_size (priv->map_layer, new_width, new_height);
}
@@ -473,8 +541,11 @@ resize_viewport (ChamplainView *view)
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 (x_last - x_first + priv->viewport_width / 2, priv->viewport_width + (x_last - x_first) / 2);
+ else
+ 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 */
@@ -567,6 +638,10 @@ champlain_view_get_property (GObject *object,
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);
}
@@ -648,6 +723,10 @@ champlain_view_set_property (GObject *object,
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);
}
@@ -723,6 +802,11 @@ champlain_view_dispose (GObject *object)
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;
@@ -1080,6 +1164,20 @@ champlain_view_class_init (ChamplainViewClass *champlainViewClass)
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
@@ -1162,6 +1260,12 @@ _update_idle_cb (ChamplainView *view)
else
load_visible_tiles (view, FALSE);
+ if (priv->hwrap)
+ {
+ update_clones (view);
+ position_viewport (view, x_to_wrap_x (priv->viewport_x, get_map_width (view)), priv->viewport_y);
+ }
+
return FALSE;
}
@@ -1188,6 +1292,67 @@ view_size_changed_cb (ChamplainView *view,
priv->viewport_height = height;
}
+static void
+exclusive_destroy_clone (ClutterActor *clone)
+{
+ if (!CLUTTER_IS_CLONE (clone))
+ return;
+
+ clutter_actor_destroy (clone);
+}
+
+static void
+update_clones (ChamplainView *view)
+{
+ DEBUG_LOG ()
+
+ ChamplainViewPrivate *priv = view->priv;
+ gint map_size;
+ gfloat view_width;
+ gint i;
+
+ map_size = get_map_width (view);
+ clutter_actor_get_size (CLUTTER_ACTOR (view), &view_width, NULL);
+
+ priv->num_clones = ceil (view_width / map_size) + 1;
+
+ if (priv->map_clones != NULL)
+ {
+ /* Only destroy clones, skip the real user_layers actor */
+ g_list_free_full (priv->user_layer_slots, (GDestroyNotify) exclusive_destroy_clone);
+ g_list_free_full (priv->map_clones, (GDestroyNotify) clutter_actor_destroy);
+
+ priv->map_clones = NULL;
+ priv->user_layer_slots = NULL;
+ }
+
+ /* Inserting the real user layer in the first slot */
+ priv->user_layer_slots = g_list_append (priv->user_layer_slots, priv->user_layers);
+ clutter_actor_set_x (priv->user_layers, 0);
+
+ for (i = 0; i < priv->num_clones; i++)
+ {
+ ClutterActor *map_clone, *user_clone;
+
+ /* Map layer clones */
+ map_clone = clutter_clone_new (priv->map_layer);
+ clutter_actor_set_x (map_clone, (i + 1) * map_size);
+ clutter_actor_insert_child_below (priv->viewport_container, map_clone,
+ NULL);
+
+ priv->map_clones = g_list_prepend (priv->map_clones, map_clone);
+
+ /* User layer clones */
+ user_clone = clutter_clone_new (priv->user_layers);
+ clutter_actor_set_x (user_clone, (i + 1) * map_size);
+ clutter_actor_insert_child_below (priv->viewport_container, user_clone,
+ priv->user_layers);
+
+ /* Inserting the user layer clones in the following slots */
+ priv->user_layer_slots = g_list_append (priv->user_layer_slots, user_clone);
+ }
+}
+
static void
slice_free_gint64 (gpointer data)
@@ -1306,7 +1471,6 @@ champlain_view_init (ChamplainView *view)
ChamplainViewPrivate *priv = GET_PRIVATE (view);
ChamplainMapSourceFactory *factory;
ChamplainMapSource *source;
- ClutterActor *viewport_container;
ClutterLayoutManager *layout;
ClutterColor color = { 0xf1, 0xee, 0xe8, 0xff };
@@ -1345,6 +1509,7 @@ champlain_view_init (ChamplainView *view)
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 ();
@@ -1352,6 +1517,10 @@ champlain_view_init (ChamplainView *view)
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);
@@ -1370,21 +1539,22 @@ champlain_view_init (ChamplainView *view)
priv->map_layer = clutter_actor_new ();
priv->user_layers = clutter_actor_new ();
- viewport_container = clutter_actor_new ();
- clutter_actor_add_child (viewport_container, priv->background_layer);
- clutter_actor_add_child (viewport_container, priv->zoom_layer);
- clutter_actor_add_child (viewport_container, priv->map_layer);
- clutter_actor_add_child (viewport_container, priv->user_layers);
+ priv->viewport_container = clutter_actor_new ();
+ clutter_actor_add_child (priv->viewport_container, priv->background_layer);
+ clutter_actor_add_child (priv->viewport_container, priv->zoom_layer);
+ clutter_actor_add_child (priv->viewport_container, priv->map_layer);
+ clutter_actor_add_child (priv->viewport_container, priv->user_layers);
/* Setup viewport */
priv->viewport = champlain_viewport_new ();
- champlain_viewport_set_child (CHAMPLAIN_VIEWPORT (priv->viewport), viewport_container);
+ champlain_viewport_set_child (CHAMPLAIN_VIEWPORT (priv->viewport), priv->viewport_container);
g_signal_connect (priv->viewport, "relocated", G_CALLBACK (view_relocated_cb), view);
g_signal_connect (priv->viewport, "notify::x-origin",
G_CALLBACK (viewport_pos_changed_cb), view);
g_signal_connect (priv->viewport, "notify::y-origin",
G_CALLBACK (viewport_pos_changed_cb), view);
+ clutter_actor_set_reactive (priv->viewport, TRUE);
/* Setup kinetic scroll */
priv->kinetic_scroll = champlain_kinetic_scroll_view_new (FALSE, CHAMPLAIN_VIEWPORT (priv->viewport));
@@ -1464,6 +1634,18 @@ viewport_pos_changed_cb (G_GNUC_UNUSED GObject *gobject,
champlain_viewport_get_origin (CHAMPLAIN_VIEWPORT (priv->viewport), &x, &y);
+ if (priv->hwrap)
+ {
+ gint map_width;
+ map_width = get_map_width (view);
+
+ /* Faux wrapping, by positioning viewport to correct wrap point
+ * so the master map view is on the left edge of ChamplainView
+ * (possibly partially invisible) */
+ if (x < 0 || x >= map_width)
+ position_viewport (view, x_to_wrap_x (x, map_width), y);
+ }
+
if (ABS (x - priv->viewport_x) > 100 || ABS (y - priv->viewport_y) > 100)
{
update_coords (view, x, y, FALSE);
@@ -1473,6 +1655,126 @@ viewport_pos_changed_cb (G_GNUC_UNUSED GObject *gobject,
}
+static void
+swap_user_layer_slots (ChamplainView *view,
+ gint original_index,
+ gint clone_index)
+{
+ ChamplainViewPrivate *priv = view->priv;
+ gint map_width = get_map_width (view);
+
+ GList *original_slot = g_list_nth (priv->user_layer_slots, original_index);
+ GList *clone_slot = g_list_nth (priv->user_layer_slots, clone_index);
+
+ ClutterActor *clone = clone_slot->data;
+
+ original_slot->data = clone;
+ clone_slot->data = priv->user_layers;
+
+ clutter_actor_set_x (clone, original_index * map_width);
+ clutter_actor_set_x (priv->user_layers, clone_index * map_width);
+}
+
+
+static gboolean
+viewport_motion_cb (G_GNUC_UNUSED ClutterActor *actor,
+ ClutterMotionEvent *event,
+ ChamplainView *view)
+{
+ ChamplainViewPrivate *priv = view->priv;
+
+ gint map_width = get_map_width (view);
+
+ gint original_index = g_list_index (priv->user_layer_slots, priv->user_layers);
+ gint clone_index = (event->x + priv->viewport_x) / map_width;
+
+ if (clone_index != original_index && clone_index < priv->num_clones + 1)
+ swap_user_layer_slots (view, original_index, clone_index);
+
+ return TRUE;
+ }
+
+
+static ClutterActor *
+sample_user_layer_at_pos (ChamplainView *view,
+ gfloat x,
+ gfloat y)
+{
+ ChamplainViewPrivate *priv = view->priv;
+
+ ClutterStage *stage = CLUTTER_STAGE (clutter_actor_get_stage (CLUTTER_ACTOR (view)));
+ ClutterActor *retval = clutter_stage_get_actor_at_pos (stage,
+ CLUTTER_PICK_REACTIVE, x, y);
+
+ /* If no reactive actor is found on top of the clone, return NULL */
+ if (!clutter_actor_contains (priv->user_layers, retval))
+ return NULL;
+
+ return retval;
+}
+
+
+static gboolean
+viewport_press_cb (G_GNUC_UNUSED ClutterActor *actor,
+ ClutterButtonEvent *event,
+ ChamplainView *view)
+{
+ DEBUG_LOG ()
+
+ ChamplainViewPrivate *priv = view->priv;
+
+ if (!priv->hwrap)
+ return FALSE;
+
+ gint original_index = g_list_index (priv->user_layer_slots, priv->user_layers);
+ gint initial_original_index = original_index;
+ ClutterActor *sampled_actor = NULL;
+
+ /* Sampling neighbouring slots for children that are split by the slot border.
+ * (e.g. a marker that has one half in a slot #n and the other half in #n-1)
+ * Whenever a user clicks on the real user layer, it is swapped succesively with
+ * the right and left neighbors (if they exist) and the area at the event
+ * coordinates is inspected for a reactive child actor. If a child is found,
+ * a button press is synthesized over it.
+ */
+ gint right_neighbor_index = original_index + 1;
+ gint left_neighbor_index = original_index - 1;
+
+ /* Swapping and testing right neighbor */
+ if (right_neighbor_index < priv->num_clones)
+ {
+ swap_user_layer_slots (view, original_index, right_neighbor_index);
+ original_index = right_neighbor_index;
+ sampled_actor = sample_user_layer_at_pos (view, event->x, event->y);
+ }
+
+ /* Swapping and testing left neighbor */
+ if (left_neighbor_index >= 0 && sampled_actor == NULL)
+ {
+ swap_user_layer_slots (view, original_index, left_neighbor_index);
+ original_index = left_neighbor_index;
+ sampled_actor = sample_user_layer_at_pos (view, event->x, event->y);
+ }
+
+ /* If found, redirecting event to the sampled actor */
+ if (sampled_actor != NULL)
+ {
+ ClutterEvent *cloned_event = (ClutterEvent *)event;
+ clutter_event_set_source (cloned_event, sampled_actor);
+ clutter_event_put (cloned_event);
+ }
+ else
+ {
+ /* Swapping the real layer back to its initial slot */
+ if (original_index != initial_original_index)
+ swap_user_layer_slots (view, original_index, initial_original_index);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static gboolean
kinetic_scroll_button_press_cb (G_GNUC_UNUSED ClutterActor *actor,
ClutterButtonEvent *event,
@@ -1789,6 +2091,39 @@ champlain_view_zoom_out (ChamplainView *view)
champlain_view_set_zoom_level (view, view->priv->zoom_level - 1);
}
+static void
+paint_surface (ChamplainView *view,
+ cairo_t *cr,
+ cairo_surface_t *surface,
+ double x,
+ double y,
+ double opacity)
+{
+ ChamplainViewPrivate *priv = view->priv;
+
+ gint map_width = get_map_width (view);
+
+ cairo_set_source_surface (cr,
+ surface,
+ x, y);
+ cairo_paint_with_alpha (cr, opacity);
+
+ /* Paint each surface num_clones - 1 extra times (last clone is not
+ * actually visible) in order to horizontally wrap.
+ */
+ if (priv->hwrap)
+ {
+ gint i;
+
+ for (i = 1; i <= priv->num_clones - 1; i++)
+ {
+ cairo_set_source_surface (cr,
+ surface,
+ x + i * map_width, y);
+ cairo_paint_with_alpha (cr, opacity);
+ }
+ }
+}
static void
layers_to_surface (ChamplainView *view,
@@ -1809,8 +2144,8 @@ layers_to_surface (ChamplainView *view,
surface = champlain_exportable_get_surface (CHAMPLAIN_EXPORTABLE (layer));
if (!surface)
continue;
- cairo_set_source_surface (cr, surface, 0, 0);
- cairo_paint(cr);
+
+ paint_surface (view, cr, surface, 0, 0, 255);
}
}
@@ -1863,7 +2198,7 @@ champlain_view_to_surface (ChamplainView *view,
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;
@@ -1878,10 +2213,8 @@ champlain_view_to_surface (ChamplainView *view,
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);
+
+ paint_surface (view, cr, tile_surface, x, y, opacity);
}
}
@@ -2109,6 +2442,15 @@ champlain_view_x_to_longitude (ChamplainView *view,
g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), 0.0);
+ if (priv->hwrap)
+ {
+ gdouble width = get_map_width (view);
+ x = x_to_wrap_x (x, width);
+
+ if (x >= width - priv->viewport_x)
+ x -= width;
+ }
+
longitude = champlain_map_source_get_longitude (priv->map_source,
priv->zoom_level,
x + priv->viewport_x);
@@ -2202,6 +2544,28 @@ champlain_view_latitude_to_y (ChamplainView *view,
return y - priv->viewport_y;
}
+/**
+ * champlain_view_get_viewport_anchor:
+ * @view: a #ChamplainView
+ * @anchor_x: (out): the x coordinate of the viewport anchor
+ * @anchor_y: (out): the y coordinate of the viewport anchor
+ *
+ * Gets the x and y coordinate of the viewport anchor in respect to the layer origin.
+ *
+ * Since: 0.12.14
+ */
+void
+champlain_view_get_viewport_anchor (ChamplainView *view,
+ gint *anchor_x,
+ gint *anchor_y)
+{
+ DEBUG_LOG ()
+
+ g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
+ ChamplainViewPrivate *priv = view->priv;
+
+ champlain_viewport_get_anchor (CHAMPLAIN_VIEWPORT (priv->viewport), anchor_x, anchor_y);
+}
/**
* champlain_view_get_viewport_origin:
@@ -2285,29 +2649,36 @@ fill_background_tiles (ChamplainView *view)
}
static void
-tile_map_set (ChamplainView *view, gint tile_x, gint tile_y, gboolean value)
+tile_table_set (ChamplainView *view,
+ GHashTable *table,
+ gint tile_x,
+ gint tile_y,
+ gboolean value)
{
ChamplainViewPrivate *priv = view->priv;
gint64 count = champlain_map_source_get_column_count (priv->map_source, priv->zoom_level);
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 (priv->tile_map, key);
+ g_hash_table_remove (table, key);
g_slice_free (gint64, key);
}
}
static gboolean
-tile_in_tile_map (ChamplainView *view, gint tile_x, gint tile_y)
+tile_in_tile_table (ChamplainView *view,
+ GHashTable *table,
+ gint tile_x,
+ gint tile_y)
{
ChamplainViewPrivate *priv = view->priv;
gint64 count = champlain_map_source_get_column_count (priv->map_source, priv->zoom_level);
gint64 key = (gint64)tile_y * count + tile_x;
- return GPOINTER_TO_INT (g_hash_table_lookup (priv->tile_map, &key));
+ return GPOINTER_TO_INT (g_hash_table_lookup (table, &key));
}
@@ -2357,8 +2728,10 @@ fill_tile_cb (FillTileCallbackData *data)
gint size = data->size;
gint zoom_level = data->zoom_level;
- if (!tile_in_tile_map (view, x, y) && zoom_level == priv->zoom_level && data->map_source == priv->map_source &&
- y >= priv->tile_y_first && y < priv->tile_y_last && x >= priv->tile_x_first && x < priv->tile_x_last)
+ if (!tile_in_tile_table (view, priv->tile_map, x, y) &&
+ zoom_level == priv->zoom_level &&
+ data->map_source == priv->map_source &&
+ tile_in_tile_table (view, priv->visible_tiles, x, y))
{
GList *iter;
@@ -2369,7 +2742,7 @@ fill_tile_cb (FillTileCallbackData *data)
load_tile_for_source (view, iter->data, opacity, size, x, y);
}
- tile_map_set (view, x, y, TRUE);
+ tile_table_set (view, priv->tile_map, x, y, TRUE);
}
g_object_unref (view);
@@ -2378,7 +2751,6 @@ fill_tile_cb (FillTileCallbackData *data)
return FALSE;
}
-
static void
load_visible_tiles (ChamplainView *view,
gboolean relocate)
@@ -2389,7 +2761,7 @@ load_visible_tiles (ChamplainView *view,
ClutterActorIter iter;
gint size;
ClutterActor *child;
- gint x_count, y_count;
+ 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 };
@@ -2399,22 +2771,41 @@ load_visible_tiles (ChamplainView *view,
get_tile_bounds (view, &min_x, &min_y, &max_x, &max_y);
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);
+ if (priv->hwrap)
+ {
+ priv->tile_x_first = priv->viewport_x / size;
+ priv->tile_x_last = priv->tile_x_first + x_count;
+ }
+ else
+ {
+ 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_count = ceil ((gfloat) priv->viewport_height / size) + 1;
+ 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 = 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, 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);
@@ -2428,12 +2819,11 @@ load_visible_tiles (ChamplainView *view,
gint tile_x = champlain_tile_get_x (tile);
gint tile_y = champlain_tile_get_y (tile);
- if (tile_x < priv->tile_x_first || tile_x >= priv->tile_x_last ||
- tile_y < priv->tile_y_first || tile_y >= priv->tile_y_last)
+ if (!tile_in_tile_table (view, priv->visible_tiles, tile_x, tile_y))
{
champlain_tile_set_state (tile, CHAMPLAIN_STATE_DONE);
clutter_actor_iter_destroy (&iter);
- tile_map_set (view, tile_x, tile_y, FALSE);
+ tile_table_set (view, priv->tile_map, tile_x, tile_y, FALSE);
}
else if (relocate)
champlain_viewport_set_actor_position (CHAMPLAIN_VIEWPORT (priv->viewport), CLUTTER_ACTOR (tile), tile_x * size, tile_y * size);
@@ -2449,14 +2839,20 @@ load_visible_tiles (ChamplainView *view,
{
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, 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))
{
FillTileCallbackData *data;
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;
@@ -2886,6 +3282,80 @@ champlain_view_get_background_pattern (ChamplainView *view)
}
+/**
+ * champlain_view_set_horizontal_wrap:
+ * @view: a #ChamplainView
+ * @wrap: %TRUE to enable horizontal wrapping
+ *
+ * Sets the value of the #ChamplainView:horizontal-wrap property.
+ */
+void
+champlain_view_set_horizontal_wrap (ChamplainView *view,
+ gboolean wrap)
+{
+ DEBUG_LOG ()
+
+ g_return_if_fail (CHAMPLAIN_IS_VIEW (view));
+
+ ChamplainViewPrivate *priv = view->priv;
+
+ if (priv->hwrap == wrap)
+ return;
+
+ priv->hwrap = wrap;
+
+ if (priv->hwrap)
+ {
+ g_signal_connect (priv->viewport, "motion-event",
+ G_CALLBACK (viewport_motion_cb), view);
+ g_signal_connect (priv->viewport, "button-press-event",
+ G_CALLBACK (viewport_press_cb), view);
+ update_clones (view);
+ }
+ else
+ {
+ g_list_free_full (priv->map_clones, (GDestroyNotify) clutter_actor_destroy);
+ g_list_free_full (priv->user_layer_slots, (GDestroyNotify) exclusive_destroy_clone);
+ priv->map_clones = NULL;
+ priv->user_layer_slots = NULL;
+ g_signal_handlers_disconnect_by_func (priv->viewport, viewport_motion_cb, view);
+ g_signal_handlers_disconnect_by_func (priv->viewport, viewport_press_cb, view);
+ clutter_actor_set_x (priv->user_layers, 0);
+ }
+ resize_viewport (view);
+
+ gint map_width = get_map_width (view);
+ if (priv->hwrap)
+ position_viewport (view, x_to_wrap_x (priv->viewport_x, map_width), priv->viewport_y);
+ else
+ position_viewport (view, priv->viewport_x - ((gint)priv->viewport_width / map_width / 2) * map_width, priv->viewport_y);
+
+ load_visible_tiles (view, FALSE);
+}
+
+
+/**
+ * champlain_view_get_horizontal_wrap:
+ * @view: a #ChamplainView
+ *
+ * Returns the value of the #ChamplainView:horizontal-wrap property.
+ *
+ * Returns: (transfer none): %TRUE if #ChamplainView:horizontal-wrap is set.
+ *
+ */
+gboolean
+champlain_view_get_horizontal_wrap (ChamplainView *view)
+{
+ DEBUG_LOG ()
+
+ g_return_val_if_fail (CHAMPLAIN_IS_VIEW (view), FALSE);
+
+ ChamplainViewPrivate *priv = view->priv;
+
+ return priv->hwrap;
+}
+
+
static void
position_zoom_actor (ChamplainView *view)
{
@@ -2927,6 +3397,11 @@ zoom_animation_completed (ClutterActor *actor,
priv->animating_zoom = FALSE;
position_zoom_actor (view);
clutter_actor_show (priv->user_layers);
+ if (priv->hwrap)
+ update_clones (view);
+
+ if (priv->tiles_loading == 0)
+ clutter_actor_destroy_all_children (priv->zoom_layer);
g_signal_handlers_disconnect_by_func (actor, zoom_animation_completed, view);
g_signal_emit_by_name (view, "animation-completed::zoom", NULL);
@@ -2949,15 +3424,19 @@ show_zoom_actor (ChamplainView *view,
{
ClutterActorIter iter;
ClutterActor *child;
+ ClutterActor *tile_container;
gint size;
gint x_first, y_first;
gdouble zoom_actor_width, zoom_actor_height;
+ 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);
+ column_count = champlain_map_source_get_column_count (priv->map_source, priv->zoom_level);
+
x_first = CLAMP (priv->viewport_x / size, min_x, max_x);
y_first = CLAMP (priv->viewport_y / size, min_y, max_y);
@@ -2972,6 +3451,7 @@ show_zoom_actor (ChamplainView *view,
priv->zoom_actor_viewport_x = priv->viewport_x - deltax;
priv->zoom_actor_viewport_y = priv->viewport_y - deltay;
+ tile_container = clutter_actor_new ();
clutter_actor_iter_init (&iter, priv->map_layer);
while (clutter_actor_iter_next (&iter, &child))
{
@@ -2984,7 +3464,7 @@ show_zoom_actor (ChamplainView *view,
g_object_ref (CLUTTER_ACTOR (tile));
clutter_actor_iter_remove (&iter);
- clutter_actor_add_child (zoom_actor, CLUTTER_ACTOR (tile));
+ clutter_actor_add_child (tile_container, CLUTTER_ACTOR (tile));
g_object_unref (CLUTTER_ACTOR (tile));
/* We move overlay tiles to the zoom actor so they get properly reparented
@@ -2994,6 +3474,32 @@ show_zoom_actor (ChamplainView *view,
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);
+
+ /* The tile_container is cloned and its clones are also added to the zoom_actor
+ * in order to horizontally wrap. Moreover, the old clones are hidden while the zooming
+ * animation is runnning.
+ */
+ if (priv->hwrap)
+ {
+ GList *old_clone = priv->map_clones;
+ gint i;
+
+ for (i = 0; i < priv->num_clones; i++)
+ {
+ gfloat tiles_x;
+ ClutterActor *clone_right = clutter_clone_new (tile_container);
+
+ clutter_actor_hide (CLUTTER_ACTOR (old_clone->data));
+
+ clutter_actor_get_position (tile_container, &tiles_x, NULL);
+ clutter_actor_set_x (clone_right, tiles_x + (i * column_count * size));
+
+ clutter_actor_add_child (zoom_actor, clone_right);
+
+ old_clone = old_clone->next;
+ }
+ }
zoom_actor_width = clutter_actor_get_width (zoom_actor);
zoom_actor_height = clutter_actor_get_height (zoom_actor);
@@ -3026,14 +3532,26 @@ show_zoom_actor (ChamplainView *view,
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);
}
priv->animating_zoom = TRUE;
}
else
- clutter_actor_set_scale (zoom_actor, deltazoom, deltazoom);
+ {
+ clutter_actor_set_scale (zoom_actor, deltazoom, deltazoom);
+ if (priv->hwrap)
+ update_clones (view);
+ }
}
static void
@@ -3091,7 +3609,10 @@ view_set_zoom_level_at (ChamplainView *view,
{
resize_viewport (view);
remove_all_tiles (view);
- position_viewport (view, new_x, new_y);
+ if (priv->hwrap)
+ position_viewport (view, x_to_wrap_x (new_x, get_map_width (view)), new_y);
+ else
+ position_viewport (view, new_x, new_y);
load_visible_tiles (view, FALSE);
if (!priv->animate_zoom)
diff --git a/champlain/champlain-view.h b/champlain/champlain-view.h
index 43eca16..675606d 100644
--- a/champlain/champlain-view.h
+++ b/champlain/champlain-view.h
@@ -128,7 +128,8 @@ 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,
@@ -148,6 +149,7 @@ 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);
@@ -160,6 +162,9 @@ gdouble champlain_view_longitude_to_x (ChamplainView *view,
gdouble champlain_view_latitude_to_y (ChamplainView *view,
gdouble latitude);
+void champlain_view_get_viewport_anchor (ChamplainView *view,
+ gint *anchor_x,
+ gint *anchor_y);
void champlain_view_get_viewport_origin (ChamplainView *view,
gint *x,
gint *y);