0459faa028f1d02d90ecd02b7278edbb7c6f5ad5
[framework/uifw/e17.git] / src / bin / e_fm_op_registry.c
1 #include "e.h"
2
3 EAPI int E_EVENT_FM_OP_REGISTRY_ADD = 0;
4 EAPI int E_EVENT_FM_OP_REGISTRY_DEL = 0;
5 EAPI int E_EVENT_FM_OP_REGISTRY_CHANGED = 0;
6
7 static Eina_Hash *_e_fm2_op_registry = NULL;
8 static unsigned int _e_fm2_init_count = 0;
9
10 typedef struct _E_Fm2_Op_Registry_Entry_Listener E_Fm2_Op_Registry_Entry_Listener;
11 typedef struct _E_Fm2_Op_Registry_Entry_Internal E_Fm2_Op_Registry_Entry_Internal;
12
13 struct _E_Fm2_Op_Registry_Entry_Listener
14 {
15    EINA_INLIST;
16    void (*cb)(void *data, const E_Fm2_Op_Registry_Entry *entry);
17    void *data;
18    void (*free_data)(void *data);
19 };
20
21 struct _E_Fm2_Op_Registry_Entry_Internal
22 {
23    E_Fm2_Op_Registry_Entry entry;
24    Eina_Inlist *listeners;
25    int references;
26    Ecore_Event *changed_event;
27 };
28
29 static void
30 _e_fm2_op_registry_entry_e_fm_deleted(void *data, Evas *evas __UNUSED__, Evas_Object *e_fm __UNUSED__, void *event __UNUSED__)
31 {
32    E_Fm2_Op_Registry_Entry *entry = data;
33
34    entry->e_fm = NULL;
35    e_fm2_op_registry_entry_changed(entry);
36 }
37
38 static void
39 _e_fm2_op_registry_entry_e_fm_monitor_start(const E_Fm2_Op_Registry_Entry *entry)
40 {
41    if (!entry->e_fm) return;
42    evas_object_event_callback_add
43      (entry->e_fm, EVAS_CALLBACK_DEL,
44       _e_fm2_op_registry_entry_e_fm_deleted, entry);
45 }
46
47 static void
48 _e_fm2_op_registry_entry_e_fm_monitor_stop(const E_Fm2_Op_Registry_Entry *entry)
49 {
50    if (!entry->e_fm) return;
51    evas_object_event_callback_del_full
52      (entry->e_fm, EVAS_CALLBACK_DEL,
53       _e_fm2_op_registry_entry_e_fm_deleted, entry);
54 }
55
56
57 static inline E_Fm2_Op_Registry_Entry_Internal *
58 _e_fm2_op_registry_entry_internal_get(const E_Fm2_Op_Registry_Entry *entry)
59 {
60    return (E_Fm2_Op_Registry_Entry_Internal *)entry;
61 }
62
63 static void
64 _e_fm2_op_registry_entry_internal_free(E_Fm2_Op_Registry_Entry_Internal *e)
65 {
66    _e_fm2_op_registry_entry_e_fm_monitor_stop(&(e->entry));
67
68    while (e->listeners)
69      {
70         E_Fm2_Op_Registry_Entry_Listener *listener = (void *)e->listeners;
71         e->listeners = eina_inlist_remove(e->listeners, e->listeners);
72
73         if (listener->free_data) listener->free_data(listener->data);
74         free(listener);
75      }
76
77    eina_stringshare_del(e->entry.src);
78    eina_stringshare_del(e->entry.dst);
79    free(e);
80 }
81
82 static inline int
83 _e_fm2_op_registry_entry_internal_unref(E_Fm2_Op_Registry_Entry_Internal *e)
84 {
85    if (e->references < 1)
86      return 0;
87
88    e->references--;
89    if (e->references > 0)
90      return e->references;
91
92    _e_fm2_op_registry_entry_internal_free(e);
93    return 0;
94 }
95
96 static inline int
97 _e_fm2_op_registry_entry_internal_ref(E_Fm2_Op_Registry_Entry_Internal *e)
98 {
99    e->references++;
100    return e->references;
101 }
102
103 static void
104 _e_fm2_op_registry_entry_listeners_call(const E_Fm2_Op_Registry_Entry_Internal *e)
105 {
106    E_Fm2_Op_Registry_Entry_Listener *l, **shadow;
107    const E_Fm2_Op_Registry_Entry *entry;
108    unsigned int i, count;
109
110    /* NB: iterate on a copy in order to allow listeners to be deleted
111     * from callbacks.  number of listeners should be small, so the
112     * following should do fine.
113     */
114    count = eina_inlist_count(e->listeners);
115    if (count < 1) return;
116
117    shadow = alloca(sizeof(*shadow) * count);
118    if (!shadow) return;
119
120    i = 0;
121    EINA_INLIST_FOREACH(e->listeners, l)
122      shadow[i++] = l;
123
124    entry = &(e->entry);
125    for (i = 0; i < count; i++)
126      shadow[i]->cb(shadow[i]->data, entry);
127 }
128
129 static void
130 _e_fm2_op_registry_entry_internal_unref_on_event(void *data, void *event __UNUSED__)
131 {
132    E_Fm2_Op_Registry_Entry_Internal *e = data;
133    _e_fm2_op_registry_entry_internal_unref(e);
134 }
135
136 static void
137 _e_fm2_op_registry_entry_internal_event(E_Fm2_Op_Registry_Entry_Internal *e, int event_type)
138 {
139    _e_fm2_op_registry_entry_internal_ref(e);
140    ecore_event_add(event_type, &(e->entry),
141                    _e_fm2_op_registry_entry_internal_unref_on_event, e);
142 }
143
144 Eina_Bool
145 e_fm2_op_registry_entry_add(int id, Evas_Object *e_fm, E_Fm_Op_Type op, E_Fm2_Op_Registry_Abort_Func abort)
146 {
147    E_Fm2_Op_Registry_Entry_Internal *e;
148
149    e = E_NEW(E_Fm2_Op_Registry_Entry_Internal, 1);
150    if (!e) return 0;
151
152    e->entry.id = id;
153    e->entry.e_fm = e_fm;
154    e->entry.start_time = ecore_loop_time_get();
155    e->entry.op = op;
156    e->entry.status = E_FM2_OP_STATUS_IN_PROGRESS;
157    e->entry.func.abort = abort;
158    e->references = 1;
159
160    if (!eina_hash_add(_e_fm2_op_registry, &id, e))
161      {
162         free(e);
163         return 0;
164      }
165
166    _e_fm2_op_registry_entry_e_fm_monitor_start(&(e->entry));
167    _e_fm2_op_registry_entry_internal_event(e, E_EVENT_FM_OP_REGISTRY_ADD);
168
169    return 1;
170 }
171
172 Eina_Bool
173 e_fm2_op_registry_entry_del(int id)
174 {
175    E_Fm2_Op_Registry_Entry_Internal *e;
176
177    e = eina_hash_find(_e_fm2_op_registry, &id);
178    if (!e) return 0;
179    eina_hash_del_by_key(_e_fm2_op_registry, &id);
180
181    _e_fm2_op_registry_entry_internal_event(e, E_EVENT_FM_OP_REGISTRY_DEL);
182    _e_fm2_op_registry_entry_internal_unref(e);
183
184    return 1;
185 }
186
187 static void
188 _e_fm2_op_registry_entry_internal_unref_on_changed_event(void *data, void *event __UNUSED__)
189 {
190    E_Fm2_Op_Registry_Entry_Internal *e = data;
191    e->changed_event = NULL;
192    _e_fm2_op_registry_entry_internal_unref(e);
193 }
194
195 void
196 e_fm2_op_registry_entry_changed(const E_Fm2_Op_Registry_Entry *entry)
197 {
198    E_Fm2_Op_Registry_Entry_Internal *e;
199
200    if (!entry) return;
201    e = _e_fm2_op_registry_entry_internal_get(entry);
202
203    _e_fm2_op_registry_entry_listeners_call(e);
204
205    if (e->changed_event) return;
206    _e_fm2_op_registry_entry_internal_ref(e);
207    e->changed_event = ecore_event_add
208      (E_EVENT_FM_OP_REGISTRY_CHANGED, &(e->entry),
209       _e_fm2_op_registry_entry_internal_unref_on_changed_event, e);
210 }
211
212 /**
213  * Set the new e_fm for this operation.
214  *
215  * Use this call instead of directly setting in order to have the
216  * object to be monitored, when it is gone, the pointer will be made
217  * NULL.
218  *
219  * @note: it will not call any listener or add any event, please use
220  * e_fm2_op_registry_entry_changed().
221  */
222 void
223 e_fm2_op_registry_entry_e_fm_set(E_Fm2_Op_Registry_Entry *entry, Evas_Object *e_fm)
224 {
225    if (!entry) return;
226    _e_fm2_op_registry_entry_e_fm_monitor_stop(entry);
227    entry->e_fm = e_fm;
228    _e_fm2_op_registry_entry_e_fm_monitor_start(entry);
229 }
230
231 /**
232  * Set the new files for this operation.
233  *
234  * Use this call instead of directly setting in order to have
235  * stringshare references right.
236  *
237  * @note: it will not call any listener or add any event, please use
238  * e_fm2_op_registry_entry_changed().
239  */
240 void
241 e_fm2_op_registry_entry_files_set(E_Fm2_Op_Registry_Entry *entry, const char *src, const char *dst)
242 {
243    if (!entry) return;
244
245    eina_stringshare_replace(&entry->src, src);
246    eina_stringshare_replace(&entry->dst, dst);
247 }
248
249 /**
250  * Adds a reference to given entry.
251  *
252  * @return: new number of references after operation or -1 on error.
253  */
254 EAPI int
255 e_fm2_op_registry_entry_ref(E_Fm2_Op_Registry_Entry *entry)
256 {
257    E_Fm2_Op_Registry_Entry_Internal *e;
258
259    if (!entry) return -1;
260
261    e = _e_fm2_op_registry_entry_internal_get(entry);
262    return _e_fm2_op_registry_entry_internal_ref(e);
263 }
264
265 /**
266  * Releases a reference to given entry.
267  *
268  * @return: new number of references after operation or -1 on error,
269  *    if 0 the entry was freed and pointer is then invalid.
270  */
271 EAPI int
272 e_fm2_op_registry_entry_unref(E_Fm2_Op_Registry_Entry *entry)
273 {
274    E_Fm2_Op_Registry_Entry_Internal *e;
275
276    if (!entry) return -1;
277
278    e = _e_fm2_op_registry_entry_internal_get(entry);
279    return _e_fm2_op_registry_entry_internal_unref(e);
280 }
281
282 /**
283  * Returns the X window associated to this operation.
284  *
285  * This will handle all bureaucracy to get X window based on e_fm evas
286  * object.
287  *
288  * @return: 0 if no window, window identifier otherwise.
289  */
290 EAPI Ecore_X_Window
291 e_fm2_op_registry_entry_xwin_get(const E_Fm2_Op_Registry_Entry *entry)
292 {
293    Evas *e;
294    Ecore_Evas *ee;
295
296    if (!entry) return 0;
297    if (!entry->e_fm) return 0;
298
299    e = evas_object_evas_get(entry->e_fm);
300    if (!e) return 0;
301
302    ee = evas_data_attach_get(e);
303    if (!ee) return 0;
304
305    return (Ecore_X_Window)(long)ecore_evas_window_get(ee);
306 }
307
308 /**
309  * Discover entry based on its identifier.
310  *
311  * @note: does not increment reference.
312  */
313 EAPI E_Fm2_Op_Registry_Entry *
314 e_fm2_op_registry_entry_get(int id)
315 {
316    return eina_hash_find(_e_fm2_op_registry, &id);
317 }
318
319 /**
320  * Adds a function to be called when entry changes.
321  *
322  * When entry changes any attribute this function will be called.
323  *
324  * @param: entry entry to operate on.
325  * @param: cb function to callback on changes.
326  * @param: data extra data to give to @p cb
327  * @param: free_data function to call when listener is removed, entry
328  *    is deleted or any error occur in this function and listener
329  *    cannot be added.
330  *
331  * @note: does not increment reference.
332  * @note: on errors, @p free_data will be called.
333  */
334 EAPI void
335 e_fm2_op_registry_entry_listener_add(E_Fm2_Op_Registry_Entry *entry, void (*cb)(void *data, const E_Fm2_Op_Registry_Entry *entry), const void *data, void (*free_data)(void *data))
336 {
337    E_Fm2_Op_Registry_Entry_Internal *e;
338    E_Fm2_Op_Registry_Entry_Listener *listener;
339    Eina_Error err;
340
341    if ((!entry) || (!cb))
342      {
343         if (free_data) free_data((void *)data);
344         return;
345      }
346
347    listener = malloc(sizeof(*listener));
348    if (!listener)
349      {
350         if (free_data) free_data((void *)data);
351         return;
352      }
353    listener->cb = cb;
354    listener->data = (void *)data;
355    listener->free_data = free_data;
356
357    e = _e_fm2_op_registry_entry_internal_get(entry);
358    e->listeners = eina_inlist_append(e->listeners, EINA_INLIST_GET(listener));
359    err = eina_error_get();
360    if (err)
361      {
362         printf("could not add listener: %s\n", eina_error_msg_get(err));
363         if (free_data) free_data((void *)data);
364         free(listener);
365         return;
366      }
367 }
368
369 /**
370  * Removes the function to be called when entry changes.
371  *
372  * @param: entry entry to operate on.
373  * @param: cb function to callback on changes.
374  * @param: data extra data to give to @p cb
375  *
376  * @note: does not decrement reference.
377  * @see: e_fm2_op_registry_entry_listener_add()
378  */
379 EAPI void
380 e_fm2_op_registry_entry_listener_del(E_Fm2_Op_Registry_Entry *entry, void (*cb)(void *data, const E_Fm2_Op_Registry_Entry *entry), const void *data)
381 {
382    E_Fm2_Op_Registry_Entry_Internal *e;
383    E_Fm2_Op_Registry_Entry_Listener *l;
384
385    if ((!entry) || (!cb)) return;
386    e = _e_fm2_op_registry_entry_internal_get(entry);
387
388    EINA_INLIST_FOREACH(e->listeners, l)
389      if ((l->cb == cb) && (l->data == data))
390        {
391           e->listeners = eina_inlist_remove(e->listeners, EINA_INLIST_GET(l));
392           if (l->free_data) l->free_data(l->data);
393           free(l);
394           return;
395        }
396 }
397
398 /**
399  * Returns an iterator over all the entries in the fm operations registry.
400  *
401  * @warning: this iterator is just valid until new entries are added
402  *    or removed (usually happens from main loop). This is because
403  *    when system is back to main loop it can report new events and
404  *    operations can be added or removed from this registry. In other
405  *    words, it is fine to call this function, immediately walk the
406  *    iterator and do something, then free the iterator. You can use
407  *    it to create a shadow list if you wish.
408  *
409  * @see e_fm2_op_registry_get_all()
410  */
411 EAPI Eina_Iterator *
412 e_fm2_op_registry_iterator_new(void)
413 {
414    return eina_hash_iterator_data_new(_e_fm2_op_registry);
415 }
416
417 /**
418  * Returns a shadow list with all entries in the registry.
419  *
420  * All entries will have references incremented, so you must free the
421  * list with e_fm2_op_registry_get_all_free() to free the list and
422  * release these references.
423  *
424  * @note: List is unsorted!
425  * @note: if you need a simple, immediate walk, use
426  *    e_fm2_op_registry_iterator_new()
427  */
428 EAPI Eina_List *
429 e_fm2_op_registry_get_all(void)
430 {
431    Eina_List *list;
432    Eina_Iterator *it;
433    E_Fm2_Op_Registry_Entry_Internal *e;
434
435    list = NULL;
436    it = eina_hash_iterator_data_new(_e_fm2_op_registry);
437    EINA_ITERATOR_FOREACH(it, e)
438      {
439         _e_fm2_op_registry_entry_internal_ref(e);
440         list = eina_list_append(list, &(e->entry));
441      }
442    eina_iterator_free(it);
443
444    return list;
445 }
446
447 EAPI void
448 e_fm2_op_registry_get_all_free(Eina_List *list)
449 {
450    E_Fm2_Op_Registry_Entry *entry;
451    EINA_LIST_FREE(list, entry)
452      e_fm2_op_registry_entry_unref(entry);
453 }
454
455 EAPI Eina_Bool
456 e_fm2_op_registry_is_empty(void)
457 {
458    return eina_hash_population(_e_fm2_op_registry) == 0;
459 }
460
461 EAPI int
462 e_fm2_op_registry_count(void)
463 {
464    return eina_hash_population(_e_fm2_op_registry);
465 }
466
467
468 EINTERN unsigned int
469 e_fm2_op_registry_init(void)
470 {
471    _e_fm2_init_count++;
472    if (_e_fm2_init_count > 1) return _e_fm2_init_count;
473
474    _e_fm2_op_registry = eina_hash_int32_new(NULL);
475    if (!_e_fm2_op_registry)
476      {
477         _e_fm2_init_count = 0;
478         return 0;
479      }
480
481    if (E_EVENT_FM_OP_REGISTRY_ADD == 0)
482      E_EVENT_FM_OP_REGISTRY_ADD = ecore_event_type_new();
483    if (E_EVENT_FM_OP_REGISTRY_DEL == 0)
484      E_EVENT_FM_OP_REGISTRY_DEL = ecore_event_type_new();
485    if (E_EVENT_FM_OP_REGISTRY_CHANGED == 0)
486      E_EVENT_FM_OP_REGISTRY_CHANGED = ecore_event_type_new();
487
488    return 1;
489 }
490
491 EINTERN unsigned int
492 e_fm2_op_registry_shutdown(void)
493 {
494    if (_e_fm2_init_count == 0) return 0;
495    _e_fm2_init_count--;
496    if (_e_fm2_init_count > 0) return _e_fm2_init_count;
497
498    eina_hash_free(_e_fm2_op_registry);
499    _e_fm2_op_registry = NULL;
500
501    return 0;
502 }
503
504 EAPI void
505 e_fm2_op_registry_entry_abort(E_Fm2_Op_Registry_Entry *entry)
506 {
507    if (!entry) return;
508
509    if (entry->func.abort) 
510       entry->func.abort(entry);
511 }