Eo: Removed "type" property from event/op descriptions.
[profile/ivi/eobj.git] / lib / eo_base_class.c
1 #include <Eina.h>
2
3 #include "Eo.h"
4 #include "eo_private.h"
5
6 #include "config.h"
7
8 static int event_freeze_count = 0;
9
10 typedef struct
11 {
12    Eina_Inlist *generic_data;
13    Eo ***wrefs;
14
15    Eina_Inlist *callbacks;
16    int walking_list;
17    int event_freeze_count;
18    Eina_Bool deletions_waiting : 1;
19 } Private_Data;
20
21 typedef struct
22 {
23    EINA_INLIST;
24    Eina_Stringshare *key;
25    void *data;
26    eo_base_data_free_func free_func;
27 } Eo_Generic_Data_Node;
28
29 static void
30 _eo_generic_data_node_free(Eo_Generic_Data_Node *node)
31 {
32    eina_stringshare_del(node->key);
33    if (node->free_func)
34       node->free_func(node->data);
35    free(node);
36 }
37
38 static void
39 _eo_generic_data_del_all(Private_Data *pd)
40 {
41    Eina_Inlist *nnode;
42    Eo_Generic_Data_Node *node = NULL;
43
44    EINA_INLIST_FOREACH_SAFE(pd->generic_data, nnode, node)
45      {
46         pd->generic_data = eina_inlist_remove(pd->generic_data,
47               EINA_INLIST_GET(node));
48
49         _eo_generic_data_node_free(node);
50      }
51 }
52
53 static void
54 _data_set(Eo *obj, void *class_data, va_list *list)
55 {
56    Private_Data *pd = class_data;
57    const char *key = va_arg(*list, const char *);
58    const void *data = va_arg(*list, const void *);
59    eo_base_data_free_func free_func = va_arg(*list, eo_base_data_free_func);
60
61    Eo_Generic_Data_Node *node;
62
63    if (!key) return;
64
65    eo_do(obj, eo_base_data_del(key));
66
67    node = malloc(sizeof(Eo_Generic_Data_Node));
68    node->key = eina_stringshare_add(key);
69    node->data = (void *) data;
70    node->free_func = free_func;
71    pd->generic_data = eina_inlist_prepend(pd->generic_data,
72          EINA_INLIST_GET(node));
73 }
74
75 static void
76 _data_get(const Eo *obj EINA_UNUSED, const void *class_data, va_list *list)
77 {
78    /* We don't really change it... */
79    Private_Data *pd = (Private_Data *) class_data;
80    const char *key = va_arg(*list, const char *);
81    void **data = va_arg(*list, void **);
82    Eo_Generic_Data_Node *node;
83
84    if (!data) return;
85    *data = NULL;
86
87    if (!key) return;
88
89    EINA_INLIST_FOREACH(pd->generic_data, node)
90      {
91         if (!strcmp(node->key, key))
92           {
93              pd->generic_data =
94                 eina_inlist_promote(pd->generic_data, EINA_INLIST_GET(node));
95              *data = node->data;
96              return;
97           }
98      }
99 }
100
101 static void
102 _data_del(Eo *obj EINA_UNUSED, void *class_data, va_list *list)
103 {
104    Private_Data *pd = class_data;
105    const char *key = va_arg(*list, const char *);
106
107    Eo_Generic_Data_Node *node;
108
109    if (!key) return;
110
111    EINA_INLIST_FOREACH(pd->generic_data, node)
112      {
113         if (!strcmp(node->key, key))
114           {
115              pd->generic_data = eina_inlist_remove(pd->generic_data,
116                    EINA_INLIST_GET(node));
117              _eo_generic_data_node_free(node);
118              return;
119           }
120      }
121 }
122
123 /* Weak reference. */
124
125 static inline size_t
126 _wref_count(Private_Data *pd)
127 {
128    size_t count = 0;
129    if (!pd->wrefs)
130       return 0;
131
132    Eo ***itr;
133    for (itr = pd->wrefs ; *itr ; itr++)
134       count++;
135
136    return count;
137 }
138
139 static void
140 _wref_add(const Eo *obj, const void *class_data, va_list *list)
141 {
142    Private_Data *pd = (Private_Data *) class_data;
143    size_t count;
144    Eo **wref = va_arg(*list, Eo **);
145
146    count = _wref_count(pd);
147    count += 1; /* New wref. */
148
149    pd->wrefs= realloc(pd->wrefs, sizeof(*pd->wrefs) * (count + 1));
150
151    pd->wrefs[count - 1] = wref;
152    pd->wrefs[count] = NULL;
153    *wref = (Eo *) obj;
154 }
155
156 static void
157 _wref_del(const Eo *obj, const void *class_data, va_list *list)
158 {
159    Private_Data *pd = (Private_Data *) class_data;
160    size_t count;
161    Eo **wref = va_arg(*list, Eo **);
162    if (*wref != obj)
163      {
164         ERR("Wref is a weak ref to %p, while this function was called on %p.",
165               *wref, obj);
166         return;
167      }
168
169    if (!pd->wrefs)
170      {
171         ERR("There are no weak refs for object %p", obj);
172         *wref = NULL;
173         return;
174      }
175
176    /* Move the last item in the array instead of the current wref. */
177    count = _wref_count(pd);
178
179      {
180         Eo ***itr;
181         for (itr = pd->wrefs ; *itr ; itr++)
182           {
183              if (*itr == wref)
184                {
185                   *itr = pd->wrefs[count - 1];
186                   break;
187                }
188           }
189
190         if (!*itr)
191           {
192              ERR("Wref %p is not associated with object %p", wref, obj);
193              *wref = NULL;
194              return;
195           }
196      }
197
198    if (count > 1)
199      {
200         // No count--; because of the NULL that is not included in the count. */
201         pd->wrefs = realloc(pd->wrefs, sizeof(*pd->wrefs) * count);
202         pd->wrefs[count - 1] = NULL;
203      }
204    else
205      {
206         free(pd->wrefs);
207         pd->wrefs = NULL;
208      }
209
210    *wref = NULL;
211 }
212
213 static inline void
214 _wref_destruct(Private_Data *pd)
215 {
216    Eo ***itr;
217    if (!pd->wrefs)
218       return;
219
220    for (itr = pd->wrefs ; *itr ; itr++)
221      {
222         **itr = NULL;
223      }
224
225    free(pd->wrefs);
226 }
227
228 /* EOF Weak reference. */
229
230 /* Event callbacks */
231
232 /* Callbacks */
233
234 typedef struct
235 {
236    EINA_INLIST;
237    const Eo_Event_Description *event;
238    Eo_Event_Cb func;
239    void *func_data;
240    Eo_Callback_Priority priority;
241    Eina_Bool delete_me : 1;
242 } Eo_Callback_Description;
243
244 /* Actually remove, doesn't care about walking list, or delete_me */
245 static void
246 _eo_callback_remove(Private_Data *pd, Eo_Callback_Description *cb)
247 {
248    pd->callbacks = eina_inlist_remove(pd->callbacks,
249          EINA_INLIST_GET(cb));
250    free(cb);
251 }
252
253 /* Actually remove, doesn't care about walking list, or delete_me */
254 static void
255 _eo_callback_remove_all(Private_Data *pd)
256 {
257    Eina_Inlist *initr;
258    Eo_Callback_Description *cb = NULL;
259    EINA_INLIST_FOREACH_SAFE(pd->callbacks, initr, cb)
260      {
261         _eo_callback_remove(pd, cb);
262      }
263 }
264
265 static void
266 _eo_callbacks_clear(Private_Data *pd)
267 {
268    Eina_Inlist *itn;
269    Eo_Callback_Description *cb = NULL;
270
271    /* Abort if we are currently walking the list. */
272    if (pd->walking_list > 0)
273       return;
274
275    /* If there are no deletions waiting. */
276    if (!pd->deletions_waiting)
277       return;
278
279    pd->deletions_waiting = EINA_FALSE;
280
281    EINA_INLIST_FOREACH_SAFE(pd->callbacks, itn, cb)
282      {
283         if (cb->delete_me)
284           {
285              _eo_callback_remove(pd, cb);
286           }
287      }
288 }
289
290 static int
291 _callback_priority_cmp(const void *_a, const void *_b)
292 {
293    const Eo_Callback_Description *a, *b;
294    a = (const Eo_Callback_Description *) _a;
295    b = (const Eo_Callback_Description *) _b;
296    if (a->priority < b->priority)
297       return -1;
298    else
299       return 1;
300 }
301
302 static void
303 _ev_cb_priority_add(Eo *obj, void *class_data, va_list *list)
304 {
305    Private_Data *pd = (Private_Data *) class_data;
306    const Eo_Event_Description *desc = va_arg(*list, const Eo_Event_Description *);
307    Eo_Callback_Priority priority = va_arg(*list, int);
308    Eo_Event_Cb func = va_arg(*list, Eo_Event_Cb);
309    const void *data = va_arg(*list, const void *);
310
311    Eo_Callback_Description *cb = calloc(1, sizeof(*cb));
312    cb->event = desc;
313    cb->func = func;
314    cb->func_data = (void *) data;
315    cb->priority = priority;
316    pd->callbacks = eina_inlist_sorted_insert(pd->callbacks,
317          EINA_INLIST_GET(cb), _callback_priority_cmp);
318
319    eo_do(obj, eo_event_callback_call(EO_EV_CALLBACK_ADD, desc, NULL));
320 }
321
322 static void
323 _ev_cb_del(Eo *obj, void *class_data, va_list *list)
324 {
325    Private_Data *pd = (Private_Data *) class_data;
326    const Eo_Event_Description *desc = va_arg(*list, const Eo_Event_Description *);
327    Eo_Event_Cb func = va_arg(*list, Eo_Event_Cb);
328    void *user_data = va_arg(*list, void *);
329
330    Eo_Callback_Description *cb;
331    EINA_INLIST_FOREACH(pd->callbacks, cb)
332      {
333         if ((cb->event == desc) && (cb->func == func) &&
334               (cb->func_data == user_data))
335           {
336              cb->delete_me = EINA_TRUE;
337              pd->deletions_waiting = EINA_TRUE;
338              _eo_callbacks_clear(pd);
339              eo_do(obj, eo_event_callback_call(EO_EV_CALLBACK_DEL, desc, NULL));
340              return;
341           }
342      }
343
344    ERR("Callback of object %p with function %p and data %p not found.", obj, func, user_data);
345 }
346
347 static void
348 _ev_cb_call(const Eo *_obj, const void *class_data, va_list *list)
349 {
350    Private_Data *pd = (Private_Data *) class_data;
351    Eo *obj = (Eo *) _obj;
352    const Eo_Event_Description *desc = va_arg(*list, const Eo_Event_Description *);
353    void *event_info = va_arg(*list, void *);
354    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
355
356    Eo_Callback_Description *cb;
357
358    if (ret) *ret = EINA_TRUE;
359
360    if (event_freeze_count || pd->event_freeze_count)
361       return;
362
363    /* FIXME: Change eo_ref to _eo_ref and unref. */
364    eo_ref(obj);
365    pd->walking_list++;
366
367    EINA_INLIST_FOREACH(pd->callbacks, cb)
368      {
369         if (!cb->delete_me && (cb->event == desc))
370           {
371              /* Abort callback calling if the func says so. */
372              if (!cb->func((void *) cb->func_data, obj, desc,
373                       (void *) event_info))
374                {
375                   if (ret) *ret = EINA_FALSE;
376                   break;
377                }
378           }
379      }
380    pd->walking_list--;
381    _eo_callbacks_clear(pd);
382    eo_unref(obj);
383 }
384
385 static Eina_Bool
386 _eo_event_forwarder_callback(void *data, Eo *obj, const Eo_Event_Description *desc, void *event_info)
387 {
388    (void) obj;
389    Eo *new_obj = (Eo *) data;
390    Eina_Bool ret;
391    eo_do(new_obj, eo_event_callback_call(desc, event_info, &ret));
392    return ret;
393 }
394
395 /* FIXME: Change default priority? Maybe call later? */
396 static void
397 _ev_cb_forwarder_add(Eo *obj, void *class_data EINA_UNUSED, va_list *list)
398 {
399    const Eo_Event_Description *desc = va_arg(*list, const Eo_Event_Description *);
400    Eo *new_obj = va_arg(*list, Eo *);
401    /* FIXME: Add it EO_MAGIC_RETURN(new_obj, EO_EINA_MAGIC); */
402
403    eo_do(obj, eo_event_callback_add(desc, _eo_event_forwarder_callback, new_obj));
404 }
405
406 static void
407 _ev_cb_forwarder_del(Eo *obj, void *class_data EINA_UNUSED, va_list *list)
408 {
409    const Eo_Event_Description *desc = va_arg(*list, const Eo_Event_Description *);
410    Eo *new_obj = va_arg(*list, Eo *);
411    /* FIXME: Add it EO_MAGIC_RETURN(new_obj, EO_EINA_MAGIC); */
412
413    eo_do(obj, eo_event_callback_del(desc, _eo_event_forwarder_callback, new_obj));
414 }
415
416 static void
417 _ev_freeze(Eo *obj EINA_UNUSED, void *class_data, va_list *list EINA_UNUSED)
418 {
419    Private_Data *pd = (Private_Data *) class_data;
420    pd->event_freeze_count++;
421 }
422
423 static void
424 _ev_thaw(Eo *obj, void *class_data, va_list *list EINA_UNUSED)
425 {
426    Private_Data *pd = (Private_Data *) class_data;
427    if (pd->event_freeze_count > 0)
428      {
429         pd->event_freeze_count--;
430      }
431    else
432      {
433         ERR("Events for object %p have already been thawed.", obj);
434      }
435 }
436
437 static void
438 _ev_freeze_get(const Eo *obj EINA_UNUSED, const void *class_data, va_list *list)
439 {
440    Private_Data *pd = (Private_Data *) class_data;
441    int *ret = va_arg(*list, int *);
442    *ret = pd->event_freeze_count;
443 }
444
445 static void
446 _ev_global_freeze(const Eo_Class *klass EINA_UNUSED, va_list *list EINA_UNUSED)
447 {
448    event_freeze_count++;
449 }
450
451 static void
452 _ev_global_thaw(const Eo_Class *klass EINA_UNUSED, va_list *list EINA_UNUSED)
453 {
454    if (event_freeze_count > 0)
455      {
456         event_freeze_count--;
457      }
458    else
459      {
460         ERR("Global events have already been thawed.");
461      }
462 }
463
464 static void
465 _ev_global_freeze_get(const Eo_Class *klass EINA_UNUSED, va_list *list)
466 {
467    int *ret = va_arg(*list, int *);
468    *ret = event_freeze_count;
469 }
470
471
472 /* EOF event callbacks */
473
474
475 /* EO_BASE_CLASS stuff */
476 #define MY_CLASS EO_BASE_CLASS
477
478 /* FIXME: Set proper type descriptions. */
479 EAPI const Eo_Event_Description _EO_EV_CALLBACK_ADD =
480    EO_EVENT_DESCRIPTION("callback,add", "A callback was added.");
481 EAPI const Eo_Event_Description _EO_EV_CALLBACK_DEL =
482    EO_EVENT_DESCRIPTION("callback,del", "A callback was deleted.");
483 EAPI const Eo_Event_Description _EO_EV_DEL =
484    EO_EVENT_DESCRIPTION("del", "Obj is being deleted.");
485
486 static void
487 _constructor(Eo *obj, void *class_data EINA_UNUSED)
488 {
489    DBG("%p - %s.", obj, eo_class_name_get(MY_CLASS));
490 }
491
492 static void
493 _destructor(Eo *obj, void *class_data)
494 {
495    DBG("%p - %s.", obj, eo_class_name_get(MY_CLASS));
496
497    _eo_generic_data_del_all(class_data);
498    _wref_destruct(class_data);
499    _eo_callback_remove_all(class_data);
500 }
501
502 static void
503 _class_constructor(Eo_Class *klass)
504 {
505    event_freeze_count = 0;
506
507    const Eo_Op_Func_Description func_desc[] = {
508         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DATA_SET), _data_set),
509         EO_OP_FUNC_CONST(EO_BASE_ID(EO_BASE_SUB_ID_DATA_GET), _data_get),
510         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DATA_DEL), _data_del),
511         EO_OP_FUNC_CONST(EO_BASE_ID(EO_BASE_SUB_ID_WREF_ADD), _wref_add),
512         EO_OP_FUNC_CONST(EO_BASE_ID(EO_BASE_SUB_ID_WREF_DEL), _wref_del),
513         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_CALLBACK_PRIORITY_ADD), _ev_cb_priority_add),
514         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_CALLBACK_DEL), _ev_cb_del),
515         EO_OP_FUNC_CONST(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_CALLBACK_CALL), _ev_cb_call),
516         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_CALLBACK_FORWARDER_ADD), _ev_cb_forwarder_add),
517         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_CALLBACK_FORWARDER_DEL), _ev_cb_forwarder_del),
518         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_FREEZE), _ev_freeze),
519         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_THAW), _ev_thaw),
520         EO_OP_FUNC_CONST(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_FREEZE_GET), _ev_freeze_get),
521         EO_OP_FUNC_CLASS(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_GLOBAL_FREEZE), _ev_global_freeze),
522         EO_OP_FUNC_CLASS(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_GLOBAL_THAW), _ev_global_thaw),
523         EO_OP_FUNC_CLASS(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_GLOBAL_FREEZE_GET), _ev_global_freeze_get),
524         EO_OP_FUNC_SENTINEL
525    };
526
527    eo_class_funcs_set(klass, func_desc);
528 }
529
530 static const Eo_Op_Description op_desc[] = {
531      EO_OP_DESCRIPTION(EO_BASE_SUB_ID_DATA_SET, "Set data for key."),
532      EO_OP_DESCRIPTION_CONST(EO_BASE_SUB_ID_DATA_GET, "Get data for key."),
533      EO_OP_DESCRIPTION(EO_BASE_SUB_ID_DATA_DEL, "Del key."),
534      EO_OP_DESCRIPTION_CONST(EO_BASE_SUB_ID_WREF_ADD, "Add a weak ref to the object."),
535      EO_OP_DESCRIPTION_CONST(EO_BASE_SUB_ID_WREF_DEL, "Delete the weak ref."),
536      EO_OP_DESCRIPTION(EO_BASE_SUB_ID_EVENT_CALLBACK_PRIORITY_ADD, "Add an event callback with a priority."),
537      EO_OP_DESCRIPTION(EO_BASE_SUB_ID_EVENT_CALLBACK_DEL, "Delete an event callback"),
538      EO_OP_DESCRIPTION_CONST(EO_BASE_SUB_ID_EVENT_CALLBACK_CALL, "Call the event callbacks for an event."),
539      EO_OP_DESCRIPTION(EO_BASE_SUB_ID_EVENT_CALLBACK_FORWARDER_ADD, "Add an event forwarder."),
540      EO_OP_DESCRIPTION(EO_BASE_SUB_ID_EVENT_CALLBACK_FORWARDER_DEL, "Delete an event forwarder."),
541      EO_OP_DESCRIPTION(EO_BASE_SUB_ID_EVENT_FREEZE, "Freezes events."),
542      EO_OP_DESCRIPTION(EO_BASE_SUB_ID_EVENT_THAW, "Thaws events."),
543      EO_OP_DESCRIPTION_CONST(EO_BASE_SUB_ID_EVENT_FREEZE_GET, "Get event freeze counter."),
544      EO_OP_DESCRIPTION_CLASS(EO_BASE_SUB_ID_EVENT_GLOBAL_FREEZE, "Freezes events globally."),
545      EO_OP_DESCRIPTION_CLASS(EO_BASE_SUB_ID_EVENT_GLOBAL_THAW, "Thaws events globally."),
546      EO_OP_DESCRIPTION_CLASS(EO_BASE_SUB_ID_EVENT_GLOBAL_FREEZE_GET, "Get global event freeze counter."),
547      EO_OP_DESCRIPTION_SENTINEL
548 };
549
550 static const Eo_Event_Description *event_desc[] = {
551      EO_EV_CALLBACK_ADD,
552      EO_EV_CALLBACK_DEL,
553      EO_EV_DEL,
554      NULL
555 };
556
557 static const Eo_Class_Description class_desc = {
558      "Eo Base",
559      EO_CLASS_TYPE_REGULAR_NO_INSTANT,
560      EO_CLASS_DESCRIPTION_OPS(NULL, op_desc, EO_BASE_SUB_ID_LAST),
561      event_desc,
562      sizeof(Private_Data),
563      _constructor,
564      _destructor,
565      _class_constructor,
566      NULL
567 };
568
569 EO_DEFINE_CLASS_STATIC(eo_base_class_get, EO_BASE_CLASS_ID, &class_desc, NULL, NULL)
570