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