Build the timings tree. Remove next from segments and make sub_segments GSList values
[laugh.git] / src / laugh-layout.c
blob8c7835d0ecad1b909d47cd71da529b1b48a0270e
1 /*
2 * Laugh.
4 * An glib SMIL library.
6 * Authored By Koos Vriezen <koos.vriezen@gmail.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 /**
25 * SECTION:laugh-layout
26 * @short_description: DOM layout classes.
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
33 #include "laugh-layout.h"
34 #include "laugh-io.h"
36 #include <string.h>
37 #include <stdlib.h>
38 #include <glib/gprintf.h>
39 #include <clutter/clutter-group.h>
40 #include <clutter/clutter-rectangle.h>
42 #define LAUGH_LAYOUT_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_LAYOUT, LaughLayoutPrivate))
46 struct _LaughLayoutPrivate
48 ClutterActor *region_actor;
49 ClutterActor *background_actor;
50 ClutterColor bg_color;
51 LaughIO *io;
54 static gpointer laugh_layout_parent_class = ((void *)0);
56 static void laugh_layout_finalize (GObject *object)
58 G_OBJECT_CLASS (laugh_layout_parent_class)->finalize (object);
61 static void laugh_layout_dispose (GObject *object)
63 LaughLayout *layout = LAUGH_LAYOUT(object);
64 LaughLayoutPrivate *priv = layout->priv;
66 if (priv->background_actor)
67 clutter_actor_destroy (priv->background_actor);
68 if (priv->region_actor)
69 clutter_actor_destroy (priv->region_actor);
71 G_OBJECT_CLASS (laugh_layout_parent_class)->dispose (object);
74 void _lauch_layout_mime_type (LaughIO *io, const gchar *mime, gpointer d)
76 g_printf ("lauch_layout_mime_type: %s\n", mime);
77 if (strncmp (mime, "image/", 6)) {
78 LaughNode *node = LAUGH_NODE (d);
80 g_printerr ("region background not an image\n");
81 laugh_io_cancel (io);
83 g_object_unref (G_OBJECT (io));
85 laugh_node_base_emit_initialized (node);
89 static void
90 _lauch_layout_completed (LaughIO *io, gsize sz, gpointer data, gpointer d)
92 LaughNode *node = LAUGH_NODE (d);
94 g_printf ("_lauch_layout_completed: %u\n", sz);
95 if (data) {
96 /* create Pixbuf and ClutterActor*/
98 g_free (data);
100 g_object_unref (G_OBJECT (io));
102 laugh_node_base_emit_initialized (node);
105 static void _lauch_layout_actor_destroyed (ClutterActor *actor, gpointer data)
107 LaughLayoutPrivate *priv = (LaughLayoutPrivate *)data;
109 if (actor == priv->background_actor)
110 priv->background_actor = NULL;
111 else if (actor == priv->region_actor)
112 priv->region_actor = NULL;
115 static void
116 _laugh_layout_create_background (LaughLayoutPrivate *priv, guint w, guint h)
118 if (priv->background_actor)
119 clutter_actor_destroy (priv->background_actor);
121 if (priv->bg_color.alpha > 0) {
122 priv->background_actor = clutter_rectangle_new ();
123 clutter_rectangle_set_color (
124 CLUTTER_RECTANGLE (priv->background_actor),
125 &priv->bg_color);
126 clutter_actor_set_size (priv->background_actor, w, h);
127 clutter_actor_set_position (priv->background_actor, 0, 0);
128 clutter_container_add_actor (CLUTTER_CONTAINER (priv->region_actor),
129 priv->background_actor);
130 clutter_actor_show (priv->background_actor);
131 g_signal_connect (G_OBJECT (priv->background_actor), "destroy",
132 (GCallback) _lauch_layout_actor_destroyed, (gpointer) priv);
133 } else {
134 priv->background_actor = NULL;
138 static void _laugh_layout_init (LaughNode *node, LaughInitializer *initializer)
140 LaughNode *child;
141 ClutterActor *region_actor;
142 ClutterContainer *parent_actor = initializer->parent_region;
143 guint pw = initializer->parent_width;
144 guint ph = initializer->parent_height;
146 if (node->attributes)
147 g_hash_table_foreach (node->attributes, laugh_attributes_set, node);
149 if (parent_actor) {
150 LaughLayout *self = LAUGH_LAYOUT (node);
151 LaughLayoutPrivate *priv = self->priv;
152 float x, y, w, h;
154 region_actor = clutter_group_new ();
155 priv->region_actor = region_actor;
156 g_signal_connect (G_OBJECT (region_actor), "destroy",
157 (GCallback) _lauch_layout_actor_destroyed, (gpointer) priv);
159 if (LaughTagIdLayout != node->id) {
160 laugh_size_setting_get(&self->size_setting, pw, ph, &x, &y, &w, &h);
161 initializer->parent_width = w;
162 initializer->parent_height = h;
163 clutter_actor_set_size (region_actor, w, h);
164 clutter_actor_set_position (region_actor, x, y);
165 clutter_actor_set_clip (region_actor, 0, 0, w, h);
168 _laugh_layout_create_background (priv, w, h);
170 switch (node->id) {
171 case LaughTagIdRootLayout:
172 if (node->parent_node &&
173 LaughTagIdLayout == node->parent_node->id) {
174 LaughLayout *layout = LAUGH_LAYOUT (node->parent_node);
175 ClutterActor *layout_actor = layout->priv->region_actor;
176 float xscale = pw / w;
177 float yscale = ph / h;
178 clutter_actor_set_clip (layout_actor, 0, 0, w, h);
179 if (xscale > yscale) {
180 clutter_actor_set_scale (layout_actor, yscale, yscale);
181 } else {
182 clutter_actor_set_scale (layout_actor, xscale, xscale);
184 break;
186 case LaughTagIdRegion:
187 default:
188 break;
190 clutter_container_add_actor (parent_actor, region_actor);
191 clutter_actor_show (region_actor);
193 initializer->parent_region = CLUTTER_CONTAINER (region_actor);
196 for (child = node->first_child; child; child = child->next_sibling)
197 laugh_node_init (child, initializer);
199 initializer->parent_region = parent_actor;
200 initializer->parent_width = pw;
201 initializer->parent_height = ph;
204 static void _laugh_layout_set_attribute (LaughNode *node,
205 LaughNodeAttributeId att, const gchar *val, gpointer *undo)
207 const gchar *value = val;
208 gboolean need_sizing = FALSE;
209 LaughLayout *self = (LaughLayout *)node;
211 laugh_node_base_set_attribute (node, att, val, undo);
212 g_printf ("_laugh_layout_set_attribute %s=%s\n", laugh_attribute_from_id (att), value);
214 if (!val && undo)
215 val = *(const gchar **)undo;
217 switch (att) {
218 case LaughAttrIdLeft:
219 laugh_size_set_string (&self->size_setting.left, value);
220 need_sizing = TRUE;
221 break;
222 case LaughAttrIdTop:
223 laugh_size_set_string (&self->size_setting.top, value);
224 need_sizing = TRUE;
225 break;
226 case LaughAttrIdWidth:
227 laugh_size_set_string (&self->size_setting.width, value);
228 need_sizing = TRUE;
229 break;
230 case LaughAttrIdHeight:
231 laugh_size_set_string (&self->size_setting.height, value);
232 need_sizing = TRUE;
233 break;
234 case LaughAttrIdRight:
235 laugh_size_set_string (&self->size_setting.right, value);
236 need_sizing = TRUE;
237 break;
238 case LaughAttrIdBottom:
239 laugh_size_set_string (&self->size_setting.bottom, value);
240 need_sizing = TRUE;
241 break;
242 case LaughAttrIdBgColor:
243 case LaughAttrIdBgColor1:
244 /*TODO destory existing CluttorActor */
245 /*TODO create CluttorActor */
246 if (!clutter_color_parse (value, &self->priv->bg_color)) {
247 g_printerr ("parse color %s failed\n", value);
248 self->priv->bg_color.alpha = 0;
250 break;
251 case LaughAttrIdBgImage:
252 /*TODO destory existing CluttorActor */
253 if (value) {
254 self->priv->io = laugh_io_new (value);
256 g_signal_connect (G_OBJECT (self->priv->io), "mime-type",
257 (GCallback) _lauch_layout_mime_type, (gpointer) node);
258 g_signal_connect (G_OBJECT (self->priv->io), "completed",
259 (GCallback) _lauch_layout_completed, (gpointer) node);
261 laugh_io_open (self->priv->io);
263 break;
264 /*TODO showBackground */
265 default:
266 break; /* kill warning: enumeration value not handled in switch*/
268 /*TODO if (node->state > LaughStateInit && need_sizing)*/
271 static void laugh_layout_class_init (LaughLayoutClass *klass)
273 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
274 LaughNodeClass *node_class = (LaughNodeClass *) klass;
276 laugh_layout_parent_class = g_type_class_peek_parent (klass);
278 gobject_class->finalize = laugh_layout_finalize;
279 gobject_class->dispose = laugh_layout_dispose;
280 node_class->init = _laugh_layout_init;
281 node_class->set_attribute = _laugh_layout_set_attribute;
283 g_type_class_add_private (gobject_class, sizeof (LaughLayoutPrivate));
286 static
287 void laugh_layout_instance_init (GTypeInstance *instance, gpointer g_class)
289 LaughLayout *self = (LaughLayout *)instance;
291 self->priv = LAUGH_LAYOUT_GET_PRIVATE (self);
293 self->priv->bg_color.alpha = 0;
296 GType laugh_layout_get_type (void)
298 static GType type = 0;
299 if (type == 0) {
300 static const GTypeInfo info = {
301 sizeof (LaughLayoutClass),
302 NULL, /* base_init */
303 NULL, /* base_finalize */
304 (GClassInitFunc) laugh_layout_class_init, /* class_init */
305 NULL, /* class_finalize */
306 NULL, /* class_data */
307 sizeof (LaughLayout),
308 0, /* n_preallocs */
309 laugh_layout_instance_init /* instance_init */
311 type = g_type_register_static (LAUGH_TYPE_NODE,
312 "LaughLayoutType",
313 &info, 0);
315 return type;
318 void laugh_size_set_string (LaughSize *size, const gchar *value)
320 char *ep;
321 char *p;
323 if (value) {
324 p = strchr (value, '%');
325 if (p) {
326 size->abs_size = 0.0;
327 size->perc_size = strtod (value, &ep);
328 size->is_set = ep != value;
329 } else {
330 size->abs_size = strtod (value, &ep);
331 size->perc_size = 0.0;
332 size->is_set = ep != value;
334 } else {
335 size->is_set = FALSE;
339 float laugh_size_get (LaughSize *size, float relative_to)
341 return size->abs_size + size->perc_size * relative_to / 100;
344 void laugh_size_setting_get (LaughSizeSetting *sizes, float pw, float ph,
345 float *x, float *y, float *w, float *h) {
346 if (sizes->left.is_set) {
347 *x = laugh_size_get (&sizes->left, pw);
348 } else if (sizes->width.is_set) {
349 if (sizes->right.is_set)
350 *x = pw -
351 laugh_size_get (&sizes->width, pw) -
352 laugh_size_get (&sizes->right, pw);
353 else
354 *x = 0;
355 } else {
356 *x = 0;
359 if (sizes->top.is_set) {
360 *y = laugh_size_get (&sizes->top, ph);
361 } else if (sizes->height.is_set) {
362 if (sizes->bottom.is_set)
363 *y = ph -
364 laugh_size_get (&sizes->height, ph) -
365 laugh_size_get (&sizes->bottom, ph);
366 else
367 *y = 0;
368 } else {
369 *y = 0;
372 if (sizes->width.is_set)
373 *w = laugh_size_get (&sizes->width, pw);
374 else if (sizes->right.is_set)
375 *w = pw - *x -laugh_size_get (&sizes->right, pw);
376 else
377 *w = pw - *x;
378 if (*w < 0)
379 *w = 0;
381 if (sizes->height.is_set)
382 *h = laugh_size_get (&sizes->height, ph);
383 else if (sizes->bottom.is_set)
384 *h = ph - *y - laugh_size_get (&sizes->bottom, ph);
385 else
386 *h = ph - *y;
387 if (*h < 0)
388 *h = 0;
391 LaughNode *laugh_layout_new (LaughDocument *doc, LaughNodeTagId id,
392 GHashTable *attributes)
394 LaughNode *node = LAUGH_NODE(g_object_new (LAUGH_TYPE_LAYOUT, NULL));
396 laugh_node_base_construct (doc, node, id, attributes);
398 return node;
401 #ifdef LAIGH_TEST_SIZES
402 /* gcc laugh-dom.c laugh-io.c laugh-layout.c -o sizestest `pkg-config --libs --cflags glib-2.0 gio-2.0 gthread-2.0` -lexpat -DLAIGH_TEST_SIZES */
403 void test_sizes (float pw, float ph,
404 const gchar *left, const gchar *top,
405 const gchar *width, const gchar *height,
406 const gchar *right, const gchar *bottom)
408 LaughSizeSetting sizes;
409 float x, y, w, h;
410 memset (&sizes, 0, sizeof (LaughSizeSetting));
411 if (left)
412 laugh_size_set_string (&sizes.left, left);
413 if (top)
414 laugh_size_set_string (&sizes.top, top);
415 if (width)
416 laugh_size_set_string (&sizes.width, width);
417 if (height)
418 laugh_size_set_string (&sizes.height, height);
419 if (right)
420 laugh_size_set_string (&sizes.right, right);
421 if (bottom)
422 laugh_size_set_string (&sizes.bottom, bottom);
423 laugh_size_setting_get (&sizes, pw, ph, &x, &y, &w, &h);
424 g_printf ("left %s top %s width %s height %s right %s bottom %s\n"
425 "\t(%.1fx%.1f) => [%.1f, %.1f %.1fx%.1f]\n",
426 pw, ph,
427 left, top, width, height, right, bottom,
428 x, y, w, h);
431 int main()
433 test_sizes (320, 240, "50", "30", "200", "180", NULL, NULL);
434 test_sizes (320, 240, "50", "30", NULL, NULL, "40", "20");
435 test_sizes (320, 240, "50", "30", "50%", "60%", NULL, NULL);
436 test_sizes (320, 240, "50", "30", NULL, NULL, "10%", "20%");
438 return 0;
441 #endif