Post-release version bump to 3.11.91
[cheese.git] / libcheese / totem-aspect-frame.c
blob7837ddfd5160c5ae0b23f6c34b4d6ccad5d6708f
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * A container that respects the aspect ratio of its child
5 * Copyright 2010, 2011 Intel Corporation.
6 * Copyright 2012, Red Hat, Inc.
8 * Based upon mx-aspect-frame.c
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU Lesser General Public License,
12 * version 2.1, as published by the Free Software Foundation.
14 * This program is distributed in the hope it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17 * more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <math.h>
26 #include "totem-aspect-frame.h"
28 typedef struct
30 guint expand : 1;
31 gdouble rotation;
32 } TotemAspectFramePrivate;
34 G_DEFINE_TYPE_WITH_PRIVATE (TotemAspectFrame, totem_aspect_frame, CLUTTER_TYPE_ACTOR)
36 enum
38 PROP_0,
40 PROP_EXPAND,
44 static void
45 totem_aspect_frame_get_property (GObject *object,
46 guint property_id,
47 GValue *value,
48 GParamSpec *pspec)
50 TotemAspectFrame *frame = TOTEM_ASPECT_FRAME (object);
52 switch (property_id)
54 case PROP_EXPAND:
55 g_value_set_boolean (value, totem_aspect_frame_get_expand (frame));
56 break;
58 default:
59 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
63 static void
64 totem_aspect_frame_set_property (GObject *object,
65 guint property_id,
66 const GValue *value,
67 GParamSpec *pspec)
69 switch (property_id)
71 case PROP_EXPAND:
72 totem_aspect_frame_set_expand (TOTEM_ASPECT_FRAME (object),
73 g_value_get_boolean (value));
74 break;
76 default:
77 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
81 static void
82 totem_aspect_frame_dispose (GObject *object)
84 G_OBJECT_CLASS (totem_aspect_frame_parent_class)->dispose (object);
87 static void
88 totem_aspect_frame_finalize (GObject *object)
90 G_OBJECT_CLASS (totem_aspect_frame_parent_class)->finalize (object);
93 static void
94 totem_aspect_frame_get_preferred_width (ClutterActor *actor,
95 gfloat for_height,
96 gfloat *min_width_p,
97 gfloat *nat_width_p)
99 gboolean override;
101 if (for_height >= 0)
102 override = FALSE;
103 else
104 g_object_get (G_OBJECT (actor), "natural-height-set", &override, NULL);
106 if (override)
107 g_object_get (G_OBJECT (actor), "natural-height", &for_height, NULL);
109 CLUTTER_ACTOR_CLASS (totem_aspect_frame_parent_class)->
110 get_preferred_width (actor, for_height, min_width_p, nat_width_p);
113 static void
114 totem_aspect_frame_get_preferred_height (ClutterActor *actor,
115 gfloat for_width,
116 gfloat *min_height_p,
117 gfloat *nat_height_p)
119 gboolean override;
121 if (for_width >= 0)
122 override = FALSE;
123 else
124 g_object_get (G_OBJECT (actor), "natural-width-set", &override, NULL);
126 if (override)
127 g_object_get (G_OBJECT (actor), "natural-width", &for_width, NULL);
129 CLUTTER_ACTOR_CLASS (totem_aspect_frame_parent_class)->
130 get_preferred_height (actor, for_width, min_height_p, nat_height_p);
133 static void
134 totem_aspect_frame_get_size (TotemAspectFrame *frame,
135 gdouble rotation,
136 gfloat *width,
137 gfloat *height)
139 ClutterActorBox box;
140 gfloat w, h;
142 clutter_actor_get_allocation_box (CLUTTER_ACTOR (frame), &box);
144 if (fmod (rotation, 180.0) == 90.0)
146 w = box.y2 - box.y1;
147 h = box.x2 - box.x1;
149 else
151 w = box.x2 - box.x1;
152 h = box.y2 - box.y1;
155 if (width)
156 *width = w;
157 if (height)
158 *height = h;
161 static void
162 _get_allocation (ClutterActor *actor,
163 gfloat *width,
164 gfloat *height)
166 ClutterActorBox box;
168 clutter_actor_get_allocation_box (actor, &box);
170 if (width)
171 *width = box.x2 - box.x1;
172 if (height)
173 *height = box.y2 - box.y1;
176 static void
177 totem_aspect_frame_set_rotation_internal (TotemAspectFrame *frame,
178 gdouble rotation,
179 gboolean animate)
181 TotemAspectFramePrivate *priv = totem_aspect_frame_get_instance_private (frame);
182 ClutterActor *actor;
183 gfloat frame_width, frame_height;
184 gfloat child_width, child_height;
185 gfloat child_dest_width, child_dest_height;
186 gdouble frame_aspect;
187 gdouble child_aspect;
189 actor = clutter_actor_get_child_at_index (CLUTTER_ACTOR (frame), 0);
190 if (!actor)
191 return;
193 totem_aspect_frame_get_size (frame, rotation,
194 &frame_width, &frame_height);
195 _get_allocation (actor, &child_width, &child_height);
197 if (child_width <= 0.0f || child_height <= 0.0f)
198 return;
200 frame_aspect = frame_width / frame_height;
201 child_aspect = child_width / child_height;
203 if ((frame_aspect < child_aspect) ^ priv->expand)
205 child_dest_width = frame_width;
206 child_dest_height = frame_width / child_aspect;
208 else
210 child_dest_height = frame_height;
211 child_dest_width = frame_height * child_aspect;
214 clutter_actor_set_pivot_point (actor, 0.5, 0.5);
216 if (animate)
218 clutter_actor_save_easing_state (actor);
219 clutter_actor_set_easing_duration (actor, 500);
222 clutter_actor_set_rotation_angle (actor, CLUTTER_Z_AXIS, rotation);
223 clutter_actor_set_scale (actor,
224 child_dest_width / child_width,
225 child_dest_height / child_height);
227 if (animate)
228 clutter_actor_restore_easing_state (actor);
231 static void
232 totem_aspect_frame_allocate (ClutterActor *actor,
233 const ClutterActorBox *box,
234 ClutterAllocationFlags flags)
236 ClutterActor *child;
237 ClutterActorBox child_box;
238 gfloat aspect, child_aspect, width, height, box_width, box_height;
240 TotemAspectFramePrivate *priv = totem_aspect_frame_get_instance_private (TOTEM_ASPECT_FRAME (actor));
242 CLUTTER_ACTOR_CLASS (totem_aspect_frame_parent_class)->
243 allocate (actor, box, flags);
245 child = clutter_actor_get_child_at_index (actor, 0);
246 if (!child)
247 return;
249 box_width = box->x2 - box->x1;
250 box_height = box->y2 - box->y1;
252 clutter_actor_get_preferred_size (child, NULL, NULL, &width, &height);
254 if (width <= 0.0f || height <= 0.0f)
255 return;
257 aspect = box_width / box_height;
258 child_aspect = width / height;
260 if ((aspect < child_aspect) ^ priv->expand)
262 width = box_width;
263 height = box_width / child_aspect;
265 else
267 height = box_height;
268 width = box_height * child_aspect;
271 child_box.x1 = (box_width - width) / 2;
272 child_box.y1 = (box_height - height) / 2;
273 child_box.x2 = child_box.x1 + width;
274 child_box.y2 = child_box.y1 + height;
276 clutter_actor_allocate (child, &child_box, flags);
278 totem_aspect_frame_set_rotation_internal (TOTEM_ASPECT_FRAME (actor),
279 priv->rotation, FALSE);
282 static void
283 totem_aspect_frame_paint (ClutterActor *actor)
285 ClutterActor *child;
286 TotemAspectFramePrivate *priv = totem_aspect_frame_get_instance_private (TOTEM_ASPECT_FRAME (actor));
288 child = clutter_actor_get_child_at_index (actor, 0);
290 if (!child)
291 return;
293 if (priv->expand)
295 gfloat width, height;
297 clutter_actor_get_size (actor, &width, &height);
299 cogl_clip_push_rectangle (0.0, 0.0, width, height);
300 clutter_actor_paint (child);
301 cogl_clip_pop ();
303 else
304 clutter_actor_paint (child);
307 static void
308 totem_aspect_frame_pick (ClutterActor *actor,
309 const ClutterColor *color)
311 ClutterActorBox box;
312 ClutterActor *child;
313 TotemAspectFramePrivate *priv = totem_aspect_frame_get_instance_private (TOTEM_ASPECT_FRAME (actor));
315 clutter_actor_get_allocation_box (actor, &box);
317 CLUTTER_ACTOR_CLASS (totem_aspect_frame_parent_class)->pick (actor, color);
319 child = clutter_actor_get_child_at_index (actor, 0);
321 if (!child)
322 return;
324 if (priv->expand)
326 cogl_clip_push_rectangle (0.0, 0.0, box.x2 - box.x1, box.y2 - box.y1);
327 clutter_actor_paint (child);
328 cogl_clip_pop ();
330 else
331 clutter_actor_paint (child);
334 static void
335 totem_aspect_frame_class_init (TotemAspectFrameClass *klass)
337 GParamSpec *pspec;
339 GObjectClass *object_class = G_OBJECT_CLASS (klass);
340 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
342 object_class->get_property = totem_aspect_frame_get_property;
343 object_class->set_property = totem_aspect_frame_set_property;
344 object_class->dispose = totem_aspect_frame_dispose;
345 object_class->finalize = totem_aspect_frame_finalize;
347 actor_class->get_preferred_width = totem_aspect_frame_get_preferred_width;
348 actor_class->get_preferred_height = totem_aspect_frame_get_preferred_height;
349 actor_class->allocate = totem_aspect_frame_allocate;
350 actor_class->paint = totem_aspect_frame_paint;
351 actor_class->pick = totem_aspect_frame_pick;
353 pspec = g_param_spec_boolean ("expand",
354 "Expand",
355 "Fill the allocated area with the child and "
356 "clip off the excess.",
357 FALSE,
358 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
359 g_object_class_install_property (object_class, PROP_EXPAND, pspec);
362 static void
363 totem_aspect_frame_init (TotemAspectFrame *self)
365 clutter_actor_set_pivot_point (CLUTTER_ACTOR (self), 0.5f, 0.5f);
368 ClutterActor *
369 totem_aspect_frame_new (void)
371 return g_object_new (TOTEM_TYPE_ASPECT_FRAME, NULL);
374 void
375 totem_aspect_frame_set_expand (TotemAspectFrame *frame, gboolean expand)
377 TotemAspectFramePrivate *priv;
379 g_return_if_fail (TOTEM_IS_ASPECT_FRAME (frame));
381 priv = totem_aspect_frame_get_instance_private (frame);
382 if (priv->expand != expand)
384 priv->expand = expand;
385 g_object_notify (G_OBJECT (frame), "expand");
387 totem_aspect_frame_set_rotation_internal (frame, priv->rotation, TRUE);
391 gboolean
392 totem_aspect_frame_get_expand (TotemAspectFrame *frame)
394 TotemAspectFramePrivate *priv;
396 g_return_val_if_fail (TOTEM_IS_ASPECT_FRAME (frame), FALSE);
397 priv = totem_aspect_frame_get_instance_private (frame);
398 return priv->expand;
401 void
402 totem_aspect_frame_set_child (TotemAspectFrame *frame,
403 ClutterActor *child)
405 g_return_if_fail (TOTEM_IS_ASPECT_FRAME (frame));
407 clutter_actor_add_child (CLUTTER_ACTOR (frame), child);
410 void
411 totem_aspect_frame_set_rotation (TotemAspectFrame *frame,
412 gdouble rotation)
414 TotemAspectFramePrivate *priv;
416 g_return_if_fail (TOTEM_IS_ASPECT_FRAME (frame));
417 g_return_if_fail (fmod (rotation, 90.0) == 0.0);
419 priv = totem_aspect_frame_get_instance_private (frame);
420 rotation = fmod (rotation, 360.0);
422 /* When animating, make sure that we go in the right direction,
423 * otherwise we'll spin in the wrong direction going back to 0 from 270 */
424 if (rotation == 0.0 && priv->rotation == 270.0)
425 rotation = 360.0;
426 else if (rotation == 90.0 && priv->rotation == 360.0)
427 totem_aspect_frame_set_rotation_internal (frame, 0.0, FALSE);
428 else if (rotation == 270.0 && fmod (priv->rotation, 360.0) == 0.0)
429 totem_aspect_frame_set_rotation_internal (frame, 360.0, FALSE);
431 g_debug ("Setting rotation to '%lf'", rotation);
433 priv->rotation = rotation;
434 totem_aspect_frame_set_rotation_internal (frame, rotation, TRUE);
437 gdouble
438 totem_aspect_frame_get_rotation (TotemAspectFrame *frame)
440 TotemAspectFramePrivate *priv;
441 gdouble rotation;
443 g_return_val_if_fail (TOTEM_IS_ASPECT_FRAME (frame), 0.0);
445 priv = totem_aspect_frame_get_instance_private (frame);
446 rotation = fmod (priv->rotation, 360.0);
447 g_debug ("Got rotation %lf", rotation);
449 return rotation;