9b4a3c5af9faca4d3c7e69421e98e3cc46312a24
[framework/uifw/evas.git] / src / lib / canvas / evas_object_grid.c
1 #include <errno.h>
2 #include "evas_common.h"
3
4 typedef struct _Evas_Object_Grid_Data       Evas_Object_Grid_Data;
5 typedef struct _Evas_Object_Grid_Option     Evas_Object_Grid_Option;
6 typedef struct _Evas_Object_Grid_Iterator   Evas_Object_Grid_Iterator;
7 typedef struct _Evas_Object_Grid_Accessor   Evas_Object_Grid_Accessor;
8
9 struct _Evas_Object_Grid_Option
10 {
11    Evas_Object *obj;
12    Eina_List *l;
13    int x, y, w, h;
14 };
15
16 struct _Evas_Object_Grid_Data
17 {
18    Evas_Object_Smart_Clipped_Data base;
19    Eina_List *children;
20    struct {
21       int w, h;
22    } size;
23    Eina_Bool is_mirrored : 1;
24 };
25
26 struct _Evas_Object_Grid_Iterator
27 {
28    Eina_Iterator iterator;
29
30    Eina_Iterator *real_iterator;
31    const Evas_Object *grid;
32 };
33
34 struct _Evas_Object_Grid_Accessor
35 {
36    Eina_Accessor accessor;
37
38    Eina_Accessor *real_accessor;
39    const Evas_Object *grid;
40 };
41
42 #define EVAS_OBJECT_GRID_DATA_GET(o, ptr)                       \
43   Evas_Object_Grid_Data *ptr = evas_object_smart_data_get(o)
44
45 #define EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, ptr)                     \
46   EVAS_OBJECT_GRID_DATA_GET(o, ptr);                                    \
47   if (!ptr)                                                             \
48     {                                                                   \
49       CRIT("no widget data for object %p (%s)",                         \
50            o, evas_object_type_get(o));                                 \
51        abort();                                                         \
52        return;                                                          \
53     }
54
55 #define EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, ptr, val)            \
56   EVAS_OBJECT_GRID_DATA_GET(o, ptr);                                    \
57   if (!ptr)                                                             \
58     {                                                                   \
59        CRIT("No widget data for object %p (%s)",                        \
60                o, evas_object_type_get(o));                             \
61        abort();                                                         \
62        return val;                                                      \
63     }
64
65 static const char EVAS_OBJECT_GRID_OPTION_KEY[] = "|EvGd";
66
67 static Eina_Bool
68 _evas_object_grid_iterator_next(Evas_Object_Grid_Iterator *it, void **data)
69 {
70    Evas_Object_Grid_Option *opt;
71
72    if (!eina_iterator_next(it->real_iterator, (void **)&opt))
73      return EINA_FALSE;
74    if (data) *data = opt->obj;
75    return EINA_TRUE;
76 }
77
78 static Evas_Object *
79 _evas_object_grid_iterator_get_container(Evas_Object_Grid_Iterator *it)
80 {
81    return (Evas_Object *)it->grid;
82 }
83
84 static void
85 _evas_object_grid_iterator_free(Evas_Object_Grid_Iterator *it)
86 {
87    eina_iterator_free(it->real_iterator);
88    free(it);
89 }
90
91 static Eina_Bool
92 _evas_object_grid_accessor_get_at(Evas_Object_Grid_Accessor *it, unsigned int idx, void **data)
93 {
94    Evas_Object_Grid_Option *opt = NULL;
95
96    if (!eina_accessor_data_get(it->real_accessor, idx, (void **)&opt))
97      return EINA_FALSE;
98    if (data) *data = opt->obj;
99    return EINA_TRUE;
100 }
101
102 static Evas_Object *
103 _evas_object_grid_accessor_get_container(Evas_Object_Grid_Accessor *it)
104 {
105    return (Evas_Object *)it->grid;
106 }
107
108 static void
109 _evas_object_grid_accessor_free(Evas_Object_Grid_Accessor *it)
110 {
111    eina_accessor_free(it->real_accessor);
112    free(it);
113 }
114
115 static Evas_Object_Grid_Option *
116 _evas_object_grid_option_get(Evas_Object *o)
117 {
118    return evas_object_data_get(o, EVAS_OBJECT_GRID_OPTION_KEY);
119 }
120
121 static void
122 _evas_object_grid_option_set(Evas_Object *o, const Evas_Object_Grid_Option *opt)
123 {
124    evas_object_data_set(o, EVAS_OBJECT_GRID_OPTION_KEY, opt);
125 }
126
127 static Evas_Object_Grid_Option *
128 _evas_object_grid_option_del(Evas_Object *o)
129 {
130    return evas_object_data_del(o, EVAS_OBJECT_GRID_OPTION_KEY);
131 }
132
133 static void
134 _on_child_del(void *data, Evas *evas __UNUSED__, Evas_Object *child, void *einfo __UNUSED__)
135 {
136    Evas_Object *grid = data;
137    evas_object_grid_unpack(grid, child);
138 }
139
140 static void
141 _evas_object_grid_child_connect(Evas_Object *o, Evas_Object *child)
142 {
143    evas_object_event_callback_add
144      (child, EVAS_CALLBACK_DEL, _on_child_del, o);
145 }
146
147 static void
148 _evas_object_grid_child_disconnect(Evas_Object *o, Evas_Object *child)
149 {
150    evas_object_event_callback_del_full
151      (child, EVAS_CALLBACK_DEL, _on_child_del, o);
152 }
153
154 EVAS_SMART_SUBCLASS_NEW("Evas_Object_Grid", _evas_object_grid,
155                         Evas_Smart_Class, Evas_Smart_Class,
156                         evas_object_smart_clipped_class_get, NULL)
157
158 static void
159 _evas_object_grid_smart_add(Evas_Object *o)
160 {
161    EVAS_SMART_DATA_ALLOC(o, Evas_Object_Grid_Data)
162
163    priv->size.w = 100;
164    priv->size.h = 100;
165    
166    _evas_object_grid_parent_sc->add(o);
167 }
168
169 static void
170 _evas_object_grid_smart_del(Evas_Object *o)
171 {
172    EVAS_OBJECT_GRID_DATA_GET(o, priv);
173    Eina_List *l;
174
175    l = priv->children;
176    while (l)
177      {
178         Evas_Object_Grid_Option *opt = l->data;
179         _evas_object_grid_child_disconnect(o, opt->obj);
180         _evas_object_grid_option_del(opt->obj);
181         free(opt);
182         l = eina_list_remove_list(l, l);
183      }
184    _evas_object_grid_parent_sc->del(o);
185 }
186
187 static void
188 _evas_object_grid_smart_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h)
189 {
190    Evas_Coord ow, oh;
191    evas_object_geometry_get(o, NULL, NULL, &ow, &oh);
192    if ((ow == w) && (oh == h)) return;
193    evas_object_smart_changed(o);
194 }
195
196 static void
197 _evas_object_grid_smart_calculate(Evas_Object *o)
198 {
199    Eina_List *l;
200    Evas_Object_Grid_Option *opt;
201    Evas_Coord x, y, w, h, vw, vh, t;
202    Eina_Bool mirror;
203    
204    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, priv);
205    if (!priv) return;
206    if (!priv->children) return;
207    evas_object_geometry_get(o, &x, &y, &w, &h);
208    mirror = priv->is_mirrored;
209    vw = priv->size.w;
210    vh = priv->size.h;
211    EINA_LIST_FOREACH(priv->children, l, opt)
212      {
213         Evas_Coord x1, y1, x2, y2;
214         
215         x1 = x + ((w * opt->x) / vw);
216         y1 = y + ((h * opt->y) / vh);
217         x2 = x + ((w * (opt->x + opt->w)) / vw);
218         y2 = y + ((h * (opt->y + opt->h)) / vh);
219         if (mirror)
220           {
221              t = x1; x1 = x2; x2 = t;
222              t = y1; y1 = y2; y2 = t;
223           }
224         evas_object_move(opt->obj, x1, y1);
225         evas_object_resize(opt->obj, x2 - x1, y2 - y1);
226      }
227 }
228
229 static void
230 _evas_object_grid_smart_set_user(Evas_Smart_Class *sc)
231 {
232    sc->add = _evas_object_grid_smart_add;
233    sc->del = _evas_object_grid_smart_del;
234    sc->resize = _evas_object_grid_smart_resize;
235    sc->calculate = _evas_object_grid_smart_calculate;
236 }
237
238 EAPI Evas_Object *
239 evas_object_grid_add(Evas *evas)
240 {
241    return evas_object_smart_add(evas, _evas_object_grid_smart_class_new());
242 }
243
244 EAPI Evas_Object *
245 evas_object_grid_add_to(Evas_Object *parent)
246 {
247    Evas *evas;
248    Evas_Object *o;
249
250    evas = evas_object_evas_get(parent);
251    o = evas_object_grid_add(evas);
252    evas_object_smart_member_add(o, parent);
253    return o;
254 }
255
256 EAPI void
257 evas_object_grid_size_set(Evas_Object *o, int w, int h)
258 {
259    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, priv);
260    if ((priv->size.w == w) && (priv->size.h == h)) return;
261    priv->size.w = w;
262    priv->size.h = h;
263    evas_object_smart_changed(o);
264 }
265
266 EAPI void
267 evas_object_grid_size_get(const Evas_Object *o, int *w, int *h)
268 {
269    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, priv);
270    if (priv)
271      {
272         if (w) *w = priv->size.w;
273         if (h) *h = priv->size.h;
274      }
275    else
276      {
277         if (w) *w = 0;
278         if (h) *h = 0;
279      }
280 }
281
282 EAPI Eina_Bool
283 evas_object_grid_pack(Evas_Object *o, Evas_Object *child, int x, int y, int w, int h)
284 {
285    Evas_Object_Grid_Option *opt;
286    Eina_Bool newobj = EINA_FALSE;
287
288    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, 0);
289
290    opt = _evas_object_grid_option_get(child);
291    if (!opt)
292      {
293         opt = malloc(sizeof(*opt));
294         if (!opt)
295           {
296              ERR("could not allocate grid option data.");
297              return EINA_FALSE;
298           }
299         newobj = EINA_TRUE;
300      }
301
302    opt->x = x;
303    opt->y = y;
304    opt->w = w;
305    opt->h = h;
306
307    if (newobj)
308      {
309         opt->obj = child;
310         priv->children = eina_list_append(priv->children, opt);
311         opt->l = eina_list_last(priv->children);
312         _evas_object_grid_option_set(child, opt);
313         evas_object_smart_member_add(child, o);
314         _evas_object_grid_child_connect(o, child);
315      }
316    // FIXME: we could keep a changed list
317    evas_object_smart_changed(o);
318    return EINA_TRUE;
319 }
320
321 static void
322 _evas_object_grid_remove_opt(Evas_Object_Grid_Data *priv, Evas_Object_Grid_Option *opt)
323 {
324    priv->children = eina_list_remove_list(priv->children, opt->l);
325    opt->l = NULL;
326 }
327
328 EAPI Eina_Bool
329 evas_object_grid_unpack(Evas_Object *o, Evas_Object *child)
330 {
331    Evas_Object_Grid_Option *opt;
332
333    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, 0);
334
335    if (o != evas_object_smart_parent_get(child))
336      {
337         ERR("cannot unpack child from incorrect grid!");
338         return EINA_FALSE;
339      }
340
341    opt = _evas_object_grid_option_del(child);
342    if (!opt)
343      {
344         ERR("cannot unpack child with no packing option!");
345         return EINA_FALSE;
346      }
347
348    _evas_object_grid_child_disconnect(o, child);
349    _evas_object_grid_remove_opt(priv, opt);
350    evas_object_smart_member_del(child);
351    free(opt);
352    return EINA_TRUE;
353 }
354
355 EAPI void
356 evas_object_grid_clear(Evas_Object *o, Eina_Bool clear)
357 {
358    Evas_Object_Grid_Option *opt;
359
360    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, priv);
361
362    EINA_LIST_FREE(priv->children, opt)
363      {
364         _evas_object_grid_child_disconnect(o, opt->obj);
365         _evas_object_grid_option_del(opt->obj);
366         evas_object_smart_member_del(opt->obj);
367         if (clear)
368           evas_object_del(opt->obj);
369         free(opt);
370      }
371 }
372
373 EAPI Eina_Bool
374 evas_object_grid_pack_get(Evas_Object *o, Evas_Object *child, int *x, int *y, int *w, int *h)
375 {
376    Evas_Object_Grid_Option *opt;
377
378    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, 0);
379    opt = _evas_object_grid_option_get(child);
380    if (!opt) return 0;
381    if (x) *x = opt->x;
382    if (y) *y = opt->y;
383    if (w) *w = opt->w;
384    if (h) *h = opt->h;
385    return 1;
386 }
387
388 EAPI Eina_Iterator *
389 evas_object_grid_iterator_new(const Evas_Object *o)
390 {
391    Evas_Object_Grid_Iterator *it;
392
393    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
394
395    if (!priv->children) return NULL;
396
397    it = calloc(1, sizeof(Evas_Object_Grid_Iterator));
398    if (!it) return NULL;
399
400    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
401
402    it->real_iterator = eina_list_iterator_new(priv->children);
403    it->grid = o;
404
405    it->iterator.next = FUNC_ITERATOR_NEXT(_evas_object_grid_iterator_next);
406    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_evas_object_grid_iterator_get_container);
407    it->iterator.free = FUNC_ITERATOR_FREE(_evas_object_grid_iterator_free);
408
409    return &it->iterator;
410 }
411
412 EAPI Eina_Accessor *
413 evas_object_grid_accessor_new(const Evas_Object *o)
414 {
415    Evas_Object_Grid_Accessor *it;
416
417    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
418
419    if (!priv->children) return NULL;
420
421    it = calloc(1, sizeof(Evas_Object_Grid_Accessor));
422    if (!it) return NULL;
423
424    EINA_MAGIC_SET(&it->accessor, EINA_MAGIC_ACCESSOR);
425
426    it->real_accessor = eina_list_accessor_new(priv->children);
427    it->grid = o;
428
429    it->accessor.get_at = FUNC_ACCESSOR_GET_AT(_evas_object_grid_accessor_get_at);
430    it->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(_evas_object_grid_accessor_get_container);
431    it->accessor.free = FUNC_ACCESSOR_FREE(_evas_object_grid_accessor_free);
432
433    return &it->accessor;
434 }
435
436 EAPI Eina_List *
437 evas_object_grid_children_get(const Evas_Object *o)
438 {
439    Eina_List *new_list = NULL, *l;
440    Evas_Object_Grid_Option *opt;
441
442    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
443
444    EINA_LIST_FOREACH(priv->children, l, opt)
445       new_list = eina_list_append(new_list, opt->obj);
446
447    return new_list;
448 }
449
450 EAPI Eina_Bool
451 evas_object_grid_mirrored_get(const Evas_Object *obj)
452 {
453    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(obj, priv, EINA_FALSE);
454    return priv->is_mirrored;
455 }
456
457 EAPI void
458 evas_object_grid_mirrored_set(Evas_Object *obj, Eina_Bool mirrored)
459 {
460    EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(obj, priv);
461    mirrored = !!mirrored;
462    if (priv->is_mirrored != mirrored)
463      {
464         priv->is_mirrored = mirrored;
465         _evas_object_grid_smart_calculate(obj);
466      }
467 }