1 /* champlain-viewport.c: Viewport actor
2 *
3 * Copyright (C) 2008 OpenedHand
4 * Copyright (C) 2011-2013 Jiri Techet <techet@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 *
21 * Written by: Chris Lord <chris@openedhand.com>
22 */
23
24 #include "config.h"
25
26 #include <clutter/clutter.h>
27
28 #include "champlain-viewport.h"
29 #include "champlain-private.h"
30
31 struct _ChamplainViewportPrivate
32 {
33 gdouble x;
34 gdouble y;
35
36 gint anchor_x;
37 gint anchor_y;
38
39 ChamplainAdjustment *hadjustment;
40 ChamplainAdjustment *vadjustment;
41
42 ClutterActor *child;
43 };
44
45 G_DEFINE_TYPE_WITH_PRIVATE (ChamplainViewport, champlain_viewport, CLUTTER_TYPE_ACTOR)
46
47 enum
48 {
49 PROP_0,
50
51 PROP_X_ORIGIN,
52 PROP_Y_ORIGIN,
53 PROP_HADJUST,
54 PROP_VADJUST,
55 };
56
57 enum
58 {
59 /* normal signals */
60 RELOCATED,
61 LAST_SIGNAL
62 };
63
64 static guint signals[LAST_SIGNAL] = { 0, };
65
66
67 static void
champlain_viewport_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)68 champlain_viewport_get_property (GObject *object,
69 guint prop_id,
70 GValue *value,
71 GParamSpec *pspec)
72 {
73 ChamplainAdjustment *adjustment;
74
75 ChamplainViewportPrivate *priv = CHAMPLAIN_VIEWPORT (object)->priv;
76
77 switch (prop_id)
78 {
79 case PROP_X_ORIGIN:
80 g_value_set_int (value, priv->x);
81 break;
82
83 case PROP_Y_ORIGIN:
84 g_value_set_int (value, priv->y);
85 break;
86
87 case PROP_HADJUST:
88 champlain_viewport_get_adjustments (CHAMPLAIN_VIEWPORT (object), &adjustment, NULL);
89 g_value_set_object (value, adjustment);
90 break;
91
92 case PROP_VADJUST:
93 champlain_viewport_get_adjustments (CHAMPLAIN_VIEWPORT (object), NULL, &adjustment);
94 g_value_set_object (value, adjustment);
95 break;
96
97 default:
98 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
99 break;
100 }
101 }
102
103
104 static void
champlain_viewport_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)105 champlain_viewport_set_property (GObject *object,
106 guint prop_id,
107 const GValue *value,
108 GParamSpec *pspec)
109 {
110 ChamplainViewport *viewport = CHAMPLAIN_VIEWPORT (object);
111 ChamplainViewportPrivate *priv = viewport->priv;
112
113 switch (prop_id)
114 {
115 case PROP_X_ORIGIN:
116 champlain_viewport_set_origin (viewport,
117 g_value_get_int (value),
118 priv->y);
119 break;
120
121 case PROP_Y_ORIGIN:
122 champlain_viewport_set_origin (viewport,
123 priv->x,
124 g_value_get_int (value));
125 break;
126
127 case PROP_HADJUST:
128 champlain_viewport_set_adjustments (CHAMPLAIN_VIEWPORT (object),
129 g_value_get_object (value),
130 priv->vadjustment);
131 break;
132
133 case PROP_VADJUST:
134 champlain_viewport_set_adjustments (CHAMPLAIN_VIEWPORT (object),
135 priv->hadjustment,
136 g_value_get_object (value));
137 break;
138
139 default:
140 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
141 break;
142 }
143 }
144
145
146 void
champlain_viewport_stop(ChamplainViewport * viewport)147 champlain_viewport_stop (ChamplainViewport *viewport)
148 {
149 ChamplainViewportPrivate *priv = CHAMPLAIN_VIEWPORT (viewport)->priv;
150
151 if (priv->hadjustment)
152 champlain_adjustment_interpolate_stop (priv->hadjustment);
153
154 if (priv->vadjustment)
155 champlain_adjustment_interpolate_stop (priv->vadjustment);
156 }
157
158
159 static void
champlain_viewport_dispose(GObject * gobject)160 champlain_viewport_dispose (GObject *gobject)
161 {
162 ChamplainViewportPrivate *priv = CHAMPLAIN_VIEWPORT (gobject)->priv;
163
164 if (priv->hadjustment)
165 {
166 champlain_adjustment_interpolate_stop (priv->hadjustment);
167 g_object_unref (priv->hadjustment);
168 priv->hadjustment = NULL;
169 }
170
171 if (priv->vadjustment)
172 {
173 champlain_adjustment_interpolate_stop (priv->vadjustment);
174 g_object_unref (priv->vadjustment);
175 priv->vadjustment = NULL;
176 }
177
178 G_OBJECT_CLASS (champlain_viewport_parent_class)->dispose (gobject);
179 }
180
181
182 static void
champlain_viewport_class_init(ChamplainViewportClass * klass)183 champlain_viewport_class_init (ChamplainViewportClass *klass)
184 {
185 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
186
187 gobject_class->get_property = champlain_viewport_get_property;
188 gobject_class->set_property = champlain_viewport_set_property;
189 gobject_class->dispose = champlain_viewport_dispose;
190
191 g_object_class_install_property (gobject_class,
192 PROP_X_ORIGIN,
193 g_param_spec_int ("x-origin",
194 "X Origin",
195 "Origin's X coordinate in pixels",
196 -G_MAXINT, G_MAXINT,
197 0,
198 G_PARAM_READWRITE));
199
200 g_object_class_install_property (gobject_class,
201 PROP_Y_ORIGIN,
202 g_param_spec_int ("y-origin",
203 "Y Origin",
204 "Origin's Y coordinate in pixels",
205 -G_MAXINT, G_MAXINT,
206 0,
207 G_PARAM_READWRITE));
208
209 g_object_class_install_property (gobject_class,
210 PROP_HADJUST,
211 g_param_spec_object ("hadjustment",
212 "ChamplainAdjustment",
213 "Horizontal adjustment",
214 CHAMPLAIN_TYPE_ADJUSTMENT,
215 G_PARAM_READWRITE));
216
217 g_object_class_install_property (gobject_class,
218 PROP_VADJUST,
219 g_param_spec_object ("vadjustment",
220 "ChamplainAdjustment",
221 "Vertical adjustment",
222 CHAMPLAIN_TYPE_ADJUSTMENT,
223 G_PARAM_READWRITE));
224
225 signals[RELOCATED] =
226 g_signal_new ("relocated",
227 G_OBJECT_CLASS_TYPE (gobject_class),
228 G_SIGNAL_RUN_LAST,
229 0, NULL, NULL,
230 NULL,
231 G_TYPE_NONE,
232 0);
233 }
234
235
236 static void
hadjustment_value_notify_cb(ChamplainAdjustment * adjustment,GParamSpec * pspec,ChamplainViewport * viewport)237 hadjustment_value_notify_cb (ChamplainAdjustment *adjustment,
238 GParamSpec *pspec,
239 ChamplainViewport *viewport)
240 {
241 ChamplainViewportPrivate *priv = viewport->priv;
242 gdouble value;
243
244 value = champlain_adjustment_get_value (adjustment);
245
246 if (priv->x != value)
247 champlain_viewport_set_origin (viewport, value, priv->y);
248 }
249
250
251 static void
vadjustment_value_notify_cb(ChamplainAdjustment * adjustment,GParamSpec * arg1,ChamplainViewport * viewport)252 vadjustment_value_notify_cb (ChamplainAdjustment *adjustment, GParamSpec *arg1,
253 ChamplainViewport *viewport)
254 {
255 ChamplainViewportPrivate *priv = viewport->priv;
256 gdouble value;
257
258 value = champlain_adjustment_get_value (adjustment);
259
260 if (priv->y != value)
261 champlain_viewport_set_origin (viewport, priv->x, value);
262 }
263
264
265 void
champlain_viewport_set_adjustments(ChamplainViewport * viewport,ChamplainAdjustment * hadjustment,ChamplainAdjustment * vadjustment)266 champlain_viewport_set_adjustments (ChamplainViewport *viewport,
267 ChamplainAdjustment *hadjustment,
268 ChamplainAdjustment *vadjustment)
269 {
270 ChamplainViewportPrivate *priv = CHAMPLAIN_VIEWPORT (viewport)->priv;
271
272 if (hadjustment != priv->hadjustment)
273 {
274 if (priv->hadjustment)
275 {
276 g_signal_handlers_disconnect_by_func (priv->hadjustment,
277 hadjustment_value_notify_cb,
278 viewport);
279 g_object_unref (priv->hadjustment);
280 }
281
282 if (hadjustment)
283 {
284 g_object_ref (hadjustment);
285 g_signal_connect (hadjustment, "notify::value",
286 G_CALLBACK (hadjustment_value_notify_cb),
287 viewport);
288 }
289
290 priv->hadjustment = hadjustment;
291 }
292
293 if (vadjustment != priv->vadjustment)
294 {
295 if (priv->vadjustment)
296 {
297 g_signal_handlers_disconnect_by_func (priv->vadjustment,
298 vadjustment_value_notify_cb,
299 viewport);
300 g_object_unref (priv->vadjustment);
301 }
302
303 if (vadjustment)
304 {
305 g_object_ref (vadjustment);
306 g_signal_connect (vadjustment, "notify::value",
307 G_CALLBACK (vadjustment_value_notify_cb),
308 viewport);
309 }
310
311 priv->vadjustment = vadjustment;
312 }
313 }
314
315
316 void
champlain_viewport_get_adjustments(ChamplainViewport * viewport,ChamplainAdjustment ** hadjustment,ChamplainAdjustment ** vadjustment)317 champlain_viewport_get_adjustments (ChamplainViewport *viewport,
318 ChamplainAdjustment **hadjustment,
319 ChamplainAdjustment **vadjustment)
320 {
321 ChamplainViewportPrivate *priv;
322
323 g_return_if_fail (CHAMPLAIN_IS_VIEWPORT (viewport));
324
325 priv = ((ChamplainViewport *) viewport)->priv;
326
327 if (hadjustment)
328 {
329 if (priv->hadjustment)
330 *hadjustment = priv->hadjustment;
331 else
332 {
333 ChamplainAdjustment *adjustment;
334 guint width;
335
336 width = clutter_actor_get_width (CLUTTER_ACTOR (viewport));
337
338 adjustment = champlain_adjustment_new (priv->x,
339 0,
340 width,
341 1);
342 champlain_viewport_set_adjustments (viewport,
343 adjustment,
344 priv->vadjustment);
345 *hadjustment = adjustment;
346 }
347 }
348
349 if (vadjustment)
350 {
351 if (priv->vadjustment)
352 *vadjustment = priv->vadjustment;
353 else
354 {
355 ChamplainAdjustment *adjustment;
356 guint height;
357
358 height = clutter_actor_get_height (CLUTTER_ACTOR (viewport));
359
360 adjustment = champlain_adjustment_new (priv->y,
361 0,
362 height,
363 1);
364 champlain_viewport_set_adjustments (viewport,
365 priv->hadjustment,
366 adjustment);
367 *vadjustment = adjustment;
368 }
369 }
370 }
371
372
373 static void
champlain_viewport_init(ChamplainViewport * self)374 champlain_viewport_init (ChamplainViewport *self)
375 {
376 self->priv = champlain_viewport_get_instance_private (self);
377
378 self->priv->anchor_x = 0;
379 self->priv->anchor_y = 0;
380 }
381
382
383 ClutterActor *
champlain_viewport_new(void)384 champlain_viewport_new (void)
385 {
386 return g_object_new (CHAMPLAIN_TYPE_VIEWPORT, NULL);
387 }
388
389
390 #define ANCHOR_LIMIT G_MAXINT16
391
392 void
champlain_viewport_set_origin(ChamplainViewport * viewport,gdouble x,gdouble y)393 champlain_viewport_set_origin (ChamplainViewport *viewport,
394 gdouble x,
395 gdouble y)
396 {
397 g_return_if_fail (CHAMPLAIN_IS_VIEWPORT (viewport));
398
399 ChamplainViewportPrivate *priv = viewport->priv;
400 gboolean relocated;
401
402 if (x == priv->x && y == priv->y)
403 return;
404
405 relocated = (ABS (priv->anchor_x - x) > ANCHOR_LIMIT || ABS (priv->anchor_y - y) > ANCHOR_LIMIT);
406 if (relocated)
407 {
408 priv->anchor_x = x - ANCHOR_LIMIT / 2;
409 priv->anchor_y = y - ANCHOR_LIMIT / 2;
410 }
411
412 if (priv->child)
413 clutter_actor_set_position (priv->child, -x + priv->anchor_x, -y + priv->anchor_y);
414
415 g_object_freeze_notify (G_OBJECT (viewport));
416
417 if (priv->hadjustment && priv->vadjustment)
418 {
419 g_object_freeze_notify (G_OBJECT (priv->hadjustment));
420 g_object_freeze_notify (G_OBJECT (priv->vadjustment));
421
422 if (x != priv->x)
423 {
424 priv->x = x;
425 g_object_notify (G_OBJECT (viewport), "x-origin");
426
427 champlain_adjustment_set_value (priv->hadjustment, x);
428 }
429
430 if (y != priv->y)
431 {
432 priv->y = y;
433 g_object_notify (G_OBJECT (viewport), "y-origin");
434
435 champlain_adjustment_set_value (priv->vadjustment, y);
436 }
437
438 g_object_thaw_notify (G_OBJECT (priv->hadjustment));
439 g_object_thaw_notify (G_OBJECT (priv->vadjustment));
440 }
441
442 g_object_thaw_notify (G_OBJECT (viewport));
443
444 if (relocated)
445 g_signal_emit_by_name (viewport, "relocated", NULL);
446 }
447
448
449 void
champlain_viewport_get_origin(ChamplainViewport * viewport,gdouble * x,gdouble * y)450 champlain_viewport_get_origin (ChamplainViewport *viewport,
451 gdouble *x,
452 gdouble *y)
453 {
454 g_return_if_fail (CHAMPLAIN_IS_VIEWPORT (viewport));
455
456 ChamplainViewportPrivate *priv = viewport->priv;
457
458 if (x)
459 *x = priv->x;
460
461 if (y)
462 *y = priv->y;
463 }
464
465
466 void
champlain_viewport_get_anchor(ChamplainViewport * viewport,gint * x,gint * y)467 champlain_viewport_get_anchor (ChamplainViewport *viewport,
468 gint *x,
469 gint *y)
470 {
471 g_return_if_fail (CHAMPLAIN_IS_VIEWPORT (viewport));
472
473 ChamplainViewportPrivate *priv = viewport->priv;
474
475 if (x)
476 *x = priv->anchor_x;
477
478 if (y)
479 *y = priv->anchor_y;
480 }
481
482
483 void
champlain_viewport_set_child(ChamplainViewport * viewport,ClutterActor * child)484 champlain_viewport_set_child (ChamplainViewport *viewport, ClutterActor *child)
485 {
486 g_return_if_fail (CHAMPLAIN_IS_VIEWPORT (viewport));
487
488 ChamplainViewportPrivate *priv = viewport->priv;
489
490 clutter_actor_remove_all_children (CLUTTER_ACTOR (viewport));
491 clutter_actor_add_child (CLUTTER_ACTOR (viewport), child);
492 priv->child = child;
493 }
494
495
496 void
champlain_viewport_set_actor_position(ChamplainViewport * viewport,ClutterActor * actor,gdouble x,gdouble y)497 champlain_viewport_set_actor_position (ChamplainViewport *viewport,
498 ClutterActor *actor,
499 gdouble x,
500 gdouble y)
501 {
502 ChamplainViewportPrivate *priv = viewport->priv;
503
504 clutter_actor_set_position (actor, x - priv->anchor_x, y - priv->anchor_y);
505 }
506