d050ea567e2d837e4ce58658d438d1a7acc4efe7
[clutter-gtk] / clutter-gtk / gtk-clutter-viewport.c
1 /**
2  * SECTION:gtk-clutter-viewport
3  * @short_description: A scrollable actor
4  *
5  * #GtkClutterViewport is a scrollable actor that can contain a single
6  * #ClutterActor. Using two #GtkAdjustment<!-- -->s it is possible to
7  * control the visible area of the child actor if the size of the viewport
8  * is smaller than the size of the child.
9  *
10  * The #GtkAdjustment<!-- -->s used to control the horizontal and
11  * vertical scrolling can be attached to a #GtkScrollbar subclass,
12  * like #GtkHScrollbar or #GtkVScrollbar.
13  *
14  * The #GtkClutterViewport can be used inside any #ClutterContainer
15  * implementation.
16  *
17  * #GtkClutterViewport is available since Clutter-GTK 1.0
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <cogl/cogl.h>
25
26 #include "gtk-clutter-scrollable.h"
27 #include "gtk-clutter-util.h"
28 #include "gtk-clutter-viewport.h"
29
30 /* XXX - GtkAdjustment accessors have been added with GTK+ 2.14,
31  * but I want Clutter-GTK to be future-proof, so let's do this
32  * little #define dance.
33  */
34 #if !GTK_CHECK_VERSION (2, 14, 0)
35 #define gtk_adjustment_set_page_size(a,v)       ((a)->page_size = (v))
36 #define gtk_adjustment_set_upper(a,v)           ((a)->upper = (v))
37 #define gtk_adjustment_set_page_increment(a,v)  ((a)->page_increment = (v))
38 #define gtk_adjustment_set_step_increment(a,v)  ((a)->step_increment = (v))
39 #define gtk_adjustment_set_lower(a,v)           ((a)->lower = (v))
40
41 #define gtk_adjustment_get_upper(a)             ((a)->upper)
42 #define gtk_adjustment_get_page_size(a)         ((a)->page_size)
43 #endif
44
45 #define GET_PRIVATE(obj)        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_CLUTTER_VIEWPORT, GtkClutterViewportPrivate))
46
47 #define I_(str) (g_intern_static_string ((str)))
48
49 static void clutter_container_iface_init      (gpointer g_iface);
50 static void gtk_clutter_scrollable_iface_init (gpointer g_iface);
51
52 G_DEFINE_TYPE_WITH_CODE (GtkClutterViewport,
53                          gtk_clutter_viewport,
54                          CLUTTER_TYPE_ACTOR,
55                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
56                                                 clutter_container_iface_init)
57                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CLUTTER_SCROLLABLE,
58                                                 gtk_clutter_scrollable_iface_init));
59
60 struct _GtkClutterViewportPrivate
61 {
62   ClutterVertex origin;
63
64   ClutterActor *child;
65
66   GtkAdjustment *h_adjustment;
67   GtkAdjustment *v_adjustment;
68 };
69
70 enum
71 {
72   PROP_0,
73
74   PROP_CHILD,
75   PROP_ORIGIN,
76   PROP_H_ADJUSTMENT,
77   PROP_V_ADJUSTMENT
78 };
79
80 static void
81 gtk_clutter_viewport_add (ClutterContainer *container,
82                           ClutterActor     *actor)
83 {
84   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (container)->priv;
85
86   if (priv->child)
87     clutter_actor_unparent (priv->child);
88
89   clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
90   priv->child = actor;
91
92   clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
93
94   g_signal_emit_by_name (container, "actor-added", actor);
95   g_object_notify (G_OBJECT (container), "child");
96 }
97
98 static void
99 gtk_clutter_viewport_remove (ClutterContainer *container,
100                              ClutterActor     *actor)
101 {
102   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (container)->priv;
103
104   if (G_LIKELY (priv->child == actor))
105     {
106       g_object_ref (actor);
107
108       clutter_actor_unparent (actor);
109       priv->child = NULL;
110
111       clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
112
113       g_signal_emit_by_name (container, "actor-removed", actor);
114
115       g_object_unref (actor);
116     }
117 }
118
119 static void
120 gtk_clutter_viewport_foreach (ClutterContainer *container,
121                               ClutterCallback   callback,
122                               gpointer          callback_data)
123 {
124   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (container)->priv;
125
126   if (G_LIKELY (priv->child))
127     callback (priv->child, callback_data);
128 }
129
130 static void
131 clutter_container_iface_init (gpointer g_iface)
132 {
133   ClutterContainerIface *iface = g_iface;
134
135   iface->add = gtk_clutter_viewport_add;
136   iface->remove = gtk_clutter_viewport_remove;
137   iface->foreach = gtk_clutter_viewport_foreach;
138 }
139
140 static void
141 viewport_adjustment_value_changed (GtkAdjustment      *adjustment,
142                                    GtkClutterViewport *viewport)
143 {
144   GtkClutterViewportPrivate *priv = viewport->priv;
145
146   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
147     {
148       GtkAdjustment *h_adjust = priv->h_adjustment;
149       GtkAdjustment *v_adjust = priv->v_adjustment;
150       ClutterUnit new_x, new_y;
151
152       new_x = CLUTTER_UNITS_FROM_FLOAT (gtk_adjustment_get_value (h_adjust));
153       new_y = CLUTTER_UNITS_FROM_FLOAT (gtk_adjustment_get_value (v_adjust));
154
155       /* change the origin and queue a relayout */
156       if (new_x != priv->origin.x || new_y != priv->origin.y)
157         {
158           priv->origin.x = new_x;
159           priv->origin.y = new_y;
160
161           clutter_actor_queue_relayout (CLUTTER_ACTOR (viewport));
162         }
163     }
164 }
165
166 static gboolean
167 viewport_reclamp_adjustment (GtkAdjustment *adjustment)
168 {
169   gdouble value = gtk_adjustment_get_value (adjustment);
170   gdouble limit;
171
172   limit = gtk_adjustment_get_upper (adjustment)
173         - gtk_adjustment_get_page_size (adjustment);
174
175   value = CLAMP (value, 0, limit);
176   if (value != gtk_adjustment_get_value (adjustment))
177     {
178       gtk_adjustment_set_value (adjustment, value);
179       return TRUE;
180     }
181   else
182     return FALSE;
183 }
184
185 static gboolean
186 viewport_set_hadjustment_values (GtkClutterViewport *viewport,
187                                  guint               width)
188 {
189   GtkClutterViewportPrivate *priv = viewport->priv;
190   GtkAdjustment *h_adjust = priv->h_adjustment;
191
192   gtk_adjustment_set_page_size (h_adjust, width);
193   gtk_adjustment_set_step_increment (h_adjust, width * 0.1);
194   gtk_adjustment_set_page_increment (h_adjust, width * 0.9);
195   gtk_adjustment_set_lower (h_adjust, 0);
196
197   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
198     {
199       ClutterUnit natural_width;
200
201       clutter_actor_get_preferred_size (priv->child,
202                                         NULL, NULL,
203                                         &natural_width, NULL);
204
205       gtk_adjustment_set_upper (h_adjust,
206                                 MAX (CLUTTER_UNITS_TO_DEVICE (natural_width),
207                                      width));
208     }
209   else
210     gtk_adjustment_set_upper (h_adjust, width);
211
212   return viewport_reclamp_adjustment (h_adjust);
213 }
214
215 static gboolean
216 viewport_set_vadjustment_values (GtkClutterViewport *viewport,
217                                  guint               height)
218 {
219   GtkClutterViewportPrivate *priv = viewport->priv;
220   GtkAdjustment *v_adjust = priv->v_adjustment;
221
222   height = clutter_actor_get_height (CLUTTER_ACTOR (viewport));
223
224   gtk_adjustment_set_page_size (v_adjust, height);
225   gtk_adjustment_set_step_increment (v_adjust, height * 0.1);
226   gtk_adjustment_set_page_increment (v_adjust, height * 0.9);
227   gtk_adjustment_set_lower (v_adjust, 0);
228
229   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
230     {
231       ClutterUnit natural_height;
232
233       clutter_actor_get_preferred_size (priv->child,
234                                         NULL, NULL,
235                                         NULL, &natural_height);
236
237       gtk_adjustment_set_upper (v_adjust,
238                                 MAX (CLUTTER_UNITS_TO_DEVICE (natural_height),
239                                      height));
240     }
241   else
242     gtk_adjustment_set_upper (v_adjust, height);
243
244   return viewport_reclamp_adjustment (v_adjust);
245 }
246
247 static inline void
248 disconnect_adjustment (GtkClutterViewport *viewport,
249                        GtkOrientation      orientation)
250 {
251   GtkClutterViewportPrivate *priv = viewport->priv;
252   GtkAdjustment **adj_p;
253
254   adj_p = (orientation == GTK_ORIENTATION_HORIZONTAL) ? &priv->h_adjustment
255                                                       : &priv->v_adjustment;
256
257   if (*adj_p)
258     {
259       g_signal_handlers_disconnect_by_func (*adj_p,
260                                             viewport_adjustment_value_changed,
261                                             viewport);
262       g_object_unref (*adj_p);
263       *adj_p = NULL;
264     }
265 }
266
267 static inline void
268 connect_adjustment (GtkClutterViewport *viewport,
269                     GtkOrientation      orientation,
270                     GtkAdjustment      *adjustment)
271 {
272   GtkClutterViewportPrivate *priv = viewport->priv;
273   GtkAdjustment **adj_p;
274   gboolean value_changed = FALSE;
275   guint width, height;
276
277   adj_p = (orientation == GTK_ORIENTATION_HORIZONTAL) ? &priv->h_adjustment
278                                                       : &priv->v_adjustment;
279
280   if (adjustment && adjustment == *adj_p)
281     return;
282
283   if (!adjustment)
284     adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 0, 0, 0, 0));
285
286   disconnect_adjustment (viewport, orientation);
287   *adj_p = g_object_ref_sink (adjustment);
288
289   clutter_actor_get_size (CLUTTER_ACTOR (viewport), &width, &height);
290
291   if (orientation == GTK_ORIENTATION_HORIZONTAL)
292     value_changed = viewport_set_hadjustment_values (viewport, width);
293   else
294     value_changed = viewport_set_vadjustment_values (viewport, height);
295
296   g_signal_connect (adjustment, "value-changed",
297                     G_CALLBACK (viewport_adjustment_value_changed),
298                     viewport);
299
300   gtk_adjustment_changed (adjustment);
301
302   if (value_changed)
303     gtk_adjustment_value_changed (adjustment);
304   else
305     viewport_adjustment_value_changed (adjustment, viewport);
306
307   if (orientation == GTK_ORIENTATION_HORIZONTAL)
308     g_object_notify (G_OBJECT (viewport), "hadjustment");
309   else
310     g_object_notify (G_OBJECT (viewport), "vadjustment");
311 }
312
313 static void
314 gtk_clutter_viewport_set_adjustments (GtkClutterScrollable *scrollable,
315                                       GtkAdjustment        *h_adjust,
316                                       GtkAdjustment        *v_adjust)
317 {
318   g_object_freeze_notify (G_OBJECT (scrollable));
319
320   connect_adjustment (GTK_CLUTTER_VIEWPORT (scrollable),
321                       GTK_ORIENTATION_HORIZONTAL,
322                       h_adjust);
323   connect_adjustment (GTK_CLUTTER_VIEWPORT (scrollable),
324                       GTK_ORIENTATION_VERTICAL,
325                       v_adjust);
326
327   g_object_thaw_notify (G_OBJECT (scrollable));
328 }
329
330 static void
331 gtk_clutter_viewport_get_adjustments (GtkClutterScrollable  *scrollable,
332                                       GtkAdjustment        **h_adjust,
333                                       GtkAdjustment        **v_adjust)
334 {
335   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (scrollable)->priv;
336
337   if (h_adjust)
338     *h_adjust = priv->h_adjustment;
339
340   if (v_adjust)
341     *v_adjust = priv->v_adjustment;
342 }
343
344 static void
345 gtk_clutter_scrollable_iface_init (gpointer g_iface)
346 {
347   GtkClutterScrollableIface *iface = g_iface;
348
349   iface->set_adjustments = gtk_clutter_viewport_set_adjustments;
350   iface->get_adjustments = gtk_clutter_viewport_get_adjustments;
351 }
352
353 static void
354 gtk_clutter_viewport_set_property (GObject      *gobject,
355                                    guint         prop_id,
356                                    const GValue *value,
357                                    GParamSpec   *pspec)
358 {
359   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (gobject)->priv;
360
361   switch (prop_id)
362     {
363     case PROP_CHILD:
364       clutter_container_add_actor (CLUTTER_CONTAINER (gobject),
365                                    g_value_get_object (value));
366       break;
367
368     case PROP_ORIGIN:
369       {
370         ClutterVertex *v = g_value_get_boxed (value);
371
372         priv->origin = *v;
373
374         if (CLUTTER_ACTOR_IS_VISIBLE (gobject))
375           clutter_actor_queue_redraw (CLUTTER_ACTOR (gobject));
376       }
377       break;
378
379     case PROP_H_ADJUSTMENT:
380       connect_adjustment (GTK_CLUTTER_VIEWPORT (gobject),
381                           GTK_ORIENTATION_HORIZONTAL,
382                           g_value_get_object (value));
383       break;
384
385     case PROP_V_ADJUSTMENT:
386       connect_adjustment (GTK_CLUTTER_VIEWPORT (gobject),
387                           GTK_ORIENTATION_VERTICAL,
388                           g_value_get_object (value));
389       break;
390
391     default:
392       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
393       break;
394     }
395 }
396
397 static void
398 gtk_clutter_viewport_get_property (GObject    *gobject,
399                                    guint       prop_id,
400                                    GValue     *value,
401                                    GParamSpec *pspec)
402 {
403   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (gobject)->priv;
404
405   switch (prop_id)
406     {
407     case PROP_CHILD:
408       g_value_set_object (value, priv->child);
409       break;
410
411     case PROP_ORIGIN:
412       g_value_set_boxed (value, &priv->origin);
413       break;
414
415     case PROP_H_ADJUSTMENT:
416       g_value_set_object (value, priv->h_adjustment);
417       break;
418
419     case PROP_V_ADJUSTMENT:
420       g_value_set_object (value, priv->v_adjustment);
421       break;
422
423     default:
424       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
425       break;
426     }
427 }
428
429 static void
430 gtk_clutter_viewport_dispose (GObject *gobject)
431 {
432   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (gobject)->priv;
433
434   if (priv->child)
435     {
436       clutter_actor_destroy (priv->child);
437       priv->child = NULL;
438     }
439
440   disconnect_adjustment (GTK_CLUTTER_VIEWPORT (gobject),
441                          GTK_ORIENTATION_HORIZONTAL);
442   disconnect_adjustment (GTK_CLUTTER_VIEWPORT (gobject),
443                          GTK_ORIENTATION_VERTICAL);
444
445   G_OBJECT_CLASS (gtk_clutter_viewport_parent_class)->dispose (gobject);
446 }
447
448 static void
449 gtk_clutter_viewport_get_preferred_width (ClutterActor *actor,
450                                           ClutterUnit   for_height,
451                                           ClutterUnit  *min_width_p,
452                                           ClutterUnit  *natural_width_p)
453 {
454   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (actor)->priv;
455
456   /* we don't have a minimum size */
457   if (min_width_p)
458     *min_width_p = 0;
459
460   /* if we have a child, we want to be as big as the child
461    * wishes to be; otherwise, we don't have a preferred width
462    */
463   if (priv->child)
464     clutter_actor_get_preferred_width (priv->child, for_height,
465                                        NULL,
466                                        natural_width_p);
467   else
468     {
469       if (natural_width_p)
470         *natural_width_p = 0;
471     }
472 }
473
474 static void
475 gtk_clutter_viewport_get_preferred_height (ClutterActor *actor,
476                                            ClutterUnit   for_width,
477                                            ClutterUnit  *min_height_p,
478                                            ClutterUnit  *natural_height_p)
479 {
480   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (actor)->priv;
481
482   /* we don't have a minimum size */
483   if (min_height_p)
484     *min_height_p = 0;
485
486   /* if we have a child, we want to be as big as the child
487    * wishes to be; otherwise, we don't have a preferred height
488    */
489   if (priv->child)
490     clutter_actor_get_preferred_height (priv->child, for_width,
491                                         NULL,
492                                         natural_height_p);
493   else
494     {
495       if (natural_height_p)
496         *natural_height_p = 0;
497     }
498 }
499
500 static void
501 gtk_clutter_viewport_allocate (ClutterActor          *actor,
502                                const ClutterActorBox *box,
503                                gboolean               origin_changed)
504 {
505   GtkClutterViewport *viewport = GTK_CLUTTER_VIEWPORT (actor);
506   GtkClutterViewportPrivate *priv = viewport->priv;
507   ClutterActorClass *parent_class;
508   gboolean h_adjustment_value_changed, v_adjustment_value_changed;
509   guint width, height;
510
511   parent_class = CLUTTER_ACTOR_CLASS (gtk_clutter_viewport_parent_class);
512   parent_class->allocate (actor, box, origin_changed);
513
514   width  = CLUTTER_UNITS_TO_DEVICE (box->x2 - box->x1);
515   height = CLUTTER_UNITS_TO_DEVICE (box->y2 - box->y1);
516
517   h_adjustment_value_changed =
518     viewport_set_hadjustment_values (viewport, width);
519   v_adjustment_value_changed =
520     viewport_set_vadjustment_values (viewport, height);
521
522   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
523     {
524       ClutterActorBox child_allocation = { 0, };
525       ClutterUnit alloc_width, alloc_height;
526
527       /* a viewport is a boundless actor which can contain a child
528        * without constraints; hence, we give any child exactly the
529        * wanted natural size, no matter how small the viewport
530        * actually is.
531        */
532       clutter_actor_get_preferred_size (priv->child,
533                                         NULL, NULL,
534                                         &alloc_width, &alloc_height);
535
536       child_allocation.x1 = clutter_actor_get_xu (priv->child);
537       child_allocation.y1 = clutter_actor_get_yu (priv->child);
538       child_allocation.x2 = child_allocation.x1 + alloc_width;
539       child_allocation.y2 = child_allocation.y1 + alloc_height;
540
541       clutter_actor_allocate (priv->child, &child_allocation, origin_changed);
542     }
543
544   gtk_adjustment_changed (priv->h_adjustment);
545   gtk_adjustment_changed (priv->v_adjustment);
546
547   if (h_adjustment_value_changed)
548     gtk_adjustment_value_changed (priv->h_adjustment);
549
550   if (v_adjustment_value_changed)
551     gtk_adjustment_value_changed (priv->v_adjustment);
552 }
553
554 static void
555 gtk_clutter_viewport_paint (ClutterActor *actor)
556 {
557   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (actor)->priv;
558
559   cogl_push_matrix ();
560
561   /* translate the paint environment by the same amount
562    * defined by the origin value
563    */
564   cogl_translatex (CLUTTER_UNITS_TO_FIXED (priv->origin.x) * -1,
565                    CLUTTER_UNITS_TO_FIXED (priv->origin.y) * -1,
566                    CLUTTER_UNITS_TO_FIXED (priv->origin.z) * -1);
567
568   /* the child will be painted in the new frame of reference */
569   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
570     clutter_actor_paint (priv->child);
571
572   cogl_pop_matrix ();
573 }
574
575 static void
576 gtk_clutter_viewport_pick (ClutterActor       *actor,
577                            const ClutterColor *pick_color)
578 {
579   /* chain up to get the default pick */
580   CLUTTER_ACTOR_CLASS (gtk_clutter_viewport_parent_class)->pick (actor, pick_color);
581
582   /* this will cause the child (if any) to be painted in pick mode */
583   gtk_clutter_viewport_paint (actor);
584 }
585
586 static void
587 gtk_clutter_viewport_class_init (GtkClutterViewportClass *klass)
588 {
589   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
590   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
591   GParamSpec *pspec;
592
593   g_type_class_add_private (klass, sizeof (GtkClutterViewportPrivate));
594
595   gobject_class->set_property = gtk_clutter_viewport_set_property;
596   gobject_class->get_property = gtk_clutter_viewport_get_property;
597   gobject_class->dispose = gtk_clutter_viewport_dispose;
598
599   actor_class->get_preferred_width = gtk_clutter_viewport_get_preferred_width;
600   actor_class->get_preferred_height = gtk_clutter_viewport_get_preferred_height;
601   actor_class->allocate = gtk_clutter_viewport_allocate;
602   actor_class->paint = gtk_clutter_viewport_paint;
603   actor_class->pick = gtk_clutter_viewport_pick;
604
605   /**
606    * GtkClutterViewport:child:
607    *
608    * The #ClutterActor inside the viewport.
609    *
610    * Since: 1.0
611    */
612   pspec = g_param_spec_object ("child",
613                                "Child",
614                                "The ClutterActor inside the viewport",
615                                CLUTTER_TYPE_ACTOR,
616                                G_PARAM_READWRITE);
617   g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
618
619   /**
620    * GtkClutterViewport:origin:
621    *
622    * The current origin of the viewport.
623    *
624    * Since: 1.0
625    */
626   pspec = g_param_spec_boxed ("origin",
627                               "Origin",
628                               "The current origin of the viewport",
629                               CLUTTER_TYPE_VERTEX,
630                               G_PARAM_READABLE);
631   g_object_class_install_property (gobject_class, PROP_ORIGIN, pspec);
632
633   /* GtkClutterScrollable properties */
634   g_object_class_override_property (gobject_class, PROP_H_ADJUSTMENT, "hadjustment");
635   g_object_class_override_property (gobject_class, PROP_V_ADJUSTMENT, "vadjustment");
636 }
637
638 static void
639 gtk_clutter_viewport_init (GtkClutterViewport *viewport)
640 {
641   GtkClutterViewportPrivate *priv;
642
643   viewport->priv = priv = GET_PRIVATE (viewport);
644 }
645
646 /**
647  * gtk_clutter_viewport_new:
648  * @h_adjust: horizontal adjustment, or %NULL
649  * @v_adjust: vertical adjustment, or %NULL
650  *
651  * Creates a new #GtkClutterViewport with the given adjustments.
652  *
653  * Return value: the newly created viewport actor
654  *
655  * Since: 1.0
656  */
657 ClutterActor *
658 gtk_clutter_viewport_new (GtkAdjustment *h_adjust,
659                           GtkAdjustment *v_adjust)
660 {
661   return g_object_new (GTK_TYPE_CLUTTER_VIEWPORT,
662                        "hadjustment", h_adjust,
663                        "vadjustment", v_adjust,
664                        NULL);
665 }
666
667 /**
668  * gtk_clutter_viewport_get_origin:
669  * @viewport: a #GtkClutterViewport
670  * @x: return location for the X origin in pixels, or %NULL
671  * @y: return location for the Y origin in pixels, or %NULL
672  * @z: return location for the Z origin in pixels, or %NULL
673  *
674  * Retrieves the current translation factor ("origin") used when
675  * displaying the child of @viewport.
676  *
677  * Since: 1.0.
678  */
679 void
680 gtk_clutter_viewport_get_origin (GtkClutterViewport *viewport,
681                                  gint               *x,
682                                  gint               *y,
683                                  gint               *z)
684 {
685   GtkClutterViewportPrivate *priv;
686
687   g_return_if_fail (GTK_IS_CLUTTER_VIEWPORT (viewport));
688
689   priv = viewport->priv;
690
691   if (x)
692     *x = CLUTTER_UNITS_TO_DEVICE (priv->origin.x);
693
694   if (y)
695     *y = CLUTTER_UNITS_TO_DEVICE (priv->origin.y);
696
697   if (z)
698     *z = CLUTTER_UNITS_TO_DEVICE (priv->origin.z);
699 }
700
701 /**
702  * gtk_clutter_viewport_get_originu:
703  * @viewport: a #GtkClutterViewport
704  * @origin: a #ClutterVertex
705  *
706  * Retrieves the current translation factor ("origin") used
707  * when displaying the child of @viewport.
708  *
709  * This function is the units-based version of
710  * gtk_clutter_viewport_get_origin().
711  *
712  * Since: 1.0
713  */
714 void
715 gtk_clutter_viewport_get_originu (GtkClutterViewport *viewport,
716                                   ClutterVertex      *origin)
717 {
718   g_return_if_fail (GTK_IS_CLUTTER_VIEWPORT (viewport));
719
720   *origin = viewport->priv->origin;
721 }