This huge commit will add e_fm2_op_registry, a central place that will store all on-going operations. You will find that it will report when operations are added, remove and when they change, for example when they change progress. Please notice that the recommended way to present information is to add listener to specific entries, as opposed to use E_EVENT_FM_OP_REGISTRY_CHANGED since it will be called immediately and you do not have to filter which entry is being changed. Entries will be associated with the e_fm object that originated it. With that one can get the Ecore_X_Window and request the window to be raised from somewhere else (ie: gadman/gadget). If object is deleted (ie: window is closed), the pointer will be made NULL and it will be a "windowless operation". TO DO (I need someone to do those): - e_fwin: windows should present on-going operations as an overlay, Dave Andreolli already started such work, it's just a matter of using the new infrastructure. - create a new gadget that present all on-going operations and maybe a history. It would be nice to show the e_fwin of if the entry is clicked, as well as a cancel button and a progress bar. See "places" module, make them similar. SVN revision: 39541devs/princeamd/enlightenment-0.17-elive
parent
4598a292ed
commit
92cab40cda
7 changed files with 911 additions and 130 deletions
@ -0,0 +1,498 @@ |
||||
/*
|
||||
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 |
||||
*/ |
||||
#include "e.h" |
||||
|
||||
EAPI int E_EVENT_FM_OP_REGISTRY_ADD = 0; |
||||
EAPI int E_EVENT_FM_OP_REGISTRY_DEL = 0; |
||||
EAPI int E_EVENT_FM_OP_REGISTRY_CHANGED = 0; |
||||
|
||||
static Eina_Hash *_e_fm2_op_registry = NULL; |
||||
static unsigned int _e_fm2_init_count = 0; |
||||
|
||||
typedef struct _E_Fm2_Op_Registry_Entry_Listener E_Fm2_Op_Registry_Entry_Listener; |
||||
typedef struct _E_Fm2_Op_Registry_Entry_Internal E_Fm2_Op_Registry_Entry_Internal; |
||||
|
||||
struct _E_Fm2_Op_Registry_Entry_Listener |
||||
{ |
||||
EINA_INLIST; |
||||
void (*cb)(void *data, const E_Fm2_Op_Registry_Entry *entry); |
||||
void *data; |
||||
void (*free_data)(void *data); |
||||
}; |
||||
|
||||
struct _E_Fm2_Op_Registry_Entry_Internal |
||||
{ |
||||
E_Fm2_Op_Registry_Entry entry; |
||||
Eina_Inlist *listeners; |
||||
int references; |
||||
Ecore_Event *changed_event; |
||||
}; |
||||
|
||||
static void |
||||
_e_fm2_op_registry_entry_e_fm_deleted(void *data, Evas *evas, Evas_Object *e_fm, void *event) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry *entry = data; |
||||
|
||||
entry->e_fm = NULL; |
||||
e_fm2_op_registry_entry_changed(entry); |
||||
} |
||||
|
||||
static void |
||||
_e_fm2_op_registry_entry_e_fm_monitor_start(const E_Fm2_Op_Registry_Entry *entry) |
||||
{ |
||||
if (!entry->e_fm) return; |
||||
evas_object_event_callback_add |
||||
(entry->e_fm, EVAS_CALLBACK_DEL, |
||||
_e_fm2_op_registry_entry_e_fm_deleted, entry); |
||||
} |
||||
|
||||
static void |
||||
_e_fm2_op_registry_entry_e_fm_monitor_stop(const E_Fm2_Op_Registry_Entry *entry) |
||||
{ |
||||
if (!entry->e_fm) return; |
||||
evas_object_event_callback_del_full |
||||
(entry->e_fm, EVAS_CALLBACK_DEL, |
||||
_e_fm2_op_registry_entry_e_fm_deleted, entry); |
||||
} |
||||
|
||||
|
||||
static inline E_Fm2_Op_Registry_Entry_Internal * |
||||
_e_fm2_op_registry_entry_internal_get(const E_Fm2_Op_Registry_Entry *entry) |
||||
{ |
||||
return (E_Fm2_Op_Registry_Entry_Internal *)entry; |
||||
} |
||||
|
||||
static void |
||||
_e_fm2_op_registry_entry_internal_free(E_Fm2_Op_Registry_Entry_Internal *e) |
||||
{ |
||||
_e_fm2_op_registry_entry_e_fm_monitor_stop(&(e->entry)); |
||||
|
||||
while (e->listeners) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Listener *listener = (void *)e->listeners; |
||||
e->listeners = eina_inlist_remove(e->listeners, e->listeners); |
||||
|
||||
if (listener->free_data) listener->free_data(listener->data); |
||||
free(listener); |
||||
} |
||||
|
||||
eina_stringshare_del(e->entry.src); |
||||
eina_stringshare_del(e->entry.dst); |
||||
free(e); |
||||
} |
||||
|
||||
static inline int |
||||
_e_fm2_op_registry_entry_internal_unref(E_Fm2_Op_Registry_Entry_Internal *e) |
||||
{ |
||||
if (e->references < 1) |
||||
return 0; |
||||
|
||||
e->references--; |
||||
if (e->references > 0) |
||||
return e->references; |
||||
|
||||
_e_fm2_op_registry_entry_internal_free(e); |
||||
return 0; |
||||
} |
||||
|
||||
static inline int |
||||
_e_fm2_op_registry_entry_internal_ref(E_Fm2_Op_Registry_Entry_Internal *e) |
||||
{ |
||||
e->references++; |
||||
return e->references; |
||||
} |
||||
|
||||
static void |
||||
_e_fm2_op_registry_entry_listeners_call(const E_Fm2_Op_Registry_Entry_Internal *e) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Listener *l, **shadow; |
||||
const E_Fm2_Op_Registry_Entry *entry; |
||||
unsigned int i, count; |
||||
|
||||
/* NB: iterate on a copy in order to allow listeners to be deleted
|
||||
* from callbacks. number of listeners should be small, so the |
||||
* following should do fine. |
||||
*/ |
||||
count = eina_inlist_count(e->listeners); |
||||
if (count < 1) return; |
||||
|
||||
shadow = alloca(sizeof(*shadow) * count); |
||||
if (!shadow) return; |
||||
|
||||
i = 0; |
||||
EINA_INLIST_FOREACH(e->listeners, l) |
||||
shadow[i++] = l; |
||||
|
||||
entry = &(e->entry); |
||||
for (i = 0; i < count; i++) |
||||
shadow[i]->cb(shadow[i]->data, entry); |
||||
} |
||||
|
||||
static void |
||||
_e_fm2_op_registry_entry_internal_unref_on_event(void *data, void *event __UNUSED__) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Internal *e = data; |
||||
_e_fm2_op_registry_entry_internal_unref(e); |
||||
} |
||||
|
||||
static void |
||||
_e_fm2_op_registry_entry_internal_event(E_Fm2_Op_Registry_Entry_Internal *e, int event_type) |
||||
{ |
||||
_e_fm2_op_registry_entry_internal_ref(e); |
||||
ecore_event_add(event_type, &(e->entry), |
||||
_e_fm2_op_registry_entry_internal_unref_on_event, e); |
||||
} |
||||
|
||||
Eina_Bool |
||||
e_fm2_op_registry_entry_add(int id, Evas_Object *e_fm, E_Fm_Op_Type op) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Internal *e; |
||||
|
||||
e = E_NEW(E_Fm2_Op_Registry_Entry_Internal, 1); |
||||
if (!e) return 0; |
||||
|
||||
e->entry.id = id; |
||||
e->entry.e_fm = e_fm; |
||||
e->entry.start_time = ecore_loop_time_get(); |
||||
e->entry.op = op; |
||||
e->entry.status = E_FM2_OP_STATUS_IN_PROGRESS; |
||||
e->references = 1; |
||||
|
||||
if (!eina_hash_add(_e_fm2_op_registry, &id, e)) |
||||
{ |
||||
free(e); |
||||
return 0; |
||||
} |
||||
|
||||
_e_fm2_op_registry_entry_e_fm_monitor_start(&(e->entry)); |
||||
_e_fm2_op_registry_entry_internal_event(e, E_EVENT_FM_OP_REGISTRY_ADD); |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
Eina_Bool |
||||
e_fm2_op_registry_entry_del(int id) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Internal *e; |
||||
|
||||
e = eina_hash_find(_e_fm2_op_registry, &id); |
||||
if (!e) return 0; |
||||
eina_hash_del_by_key(_e_fm2_op_registry, &id); |
||||
|
||||
_e_fm2_op_registry_entry_internal_event(e, E_EVENT_FM_OP_REGISTRY_DEL); |
||||
_e_fm2_op_registry_entry_internal_unref(e); |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
static void |
||||
_e_fm2_op_registry_entry_internal_unref_on_changed_event(void *data, void *event __UNUSED__) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Internal *e = data; |
||||
e->changed_event = NULL; |
||||
_e_fm2_op_registry_entry_internal_unref(e); |
||||
} |
||||
|
||||
void |
||||
e_fm2_op_registry_entry_changed(const E_Fm2_Op_Registry_Entry *entry) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Internal *e; |
||||
|
||||
if (!entry) return; |
||||
e = _e_fm2_op_registry_entry_internal_get(entry); |
||||
|
||||
_e_fm2_op_registry_entry_listeners_call(e); |
||||
|
||||
if (e->changed_event) return; |
||||
_e_fm2_op_registry_entry_internal_ref(e); |
||||
e->changed_event = ecore_event_add |
||||
(E_EVENT_FM_OP_REGISTRY_CHANGED, &(e->entry), |
||||
_e_fm2_op_registry_entry_internal_unref_on_changed_event, e); |
||||
} |
||||
|
||||
/**
|
||||
* Set the new e_fm for this operation. |
||||
* |
||||
* Use this call instead of directly setting in order to have the |
||||
* object to be monitored, when it is gone, the pointer will be made |
||||
* NULL. |
||||
* |
||||
* @note: it will not call any listener or add any event, please use |
||||
* e_fm2_op_registry_entry_changed(). |
||||
*/ |
||||
void |
||||
e_fm2_op_registry_entry_e_fm_set(E_Fm2_Op_Registry_Entry *entry, Evas_Object *e_fm) |
||||
{ |
||||
if (!entry) return; |
||||
_e_fm2_op_registry_entry_e_fm_monitor_stop(entry); |
||||
entry->e_fm = e_fm; |
||||
_e_fm2_op_registry_entry_e_fm_monitor_start(entry); |
||||
} |
||||
|
||||
/**
|
||||
* Set the new files for this operation. |
||||
* |
||||
* Use this call instead of directly setting in order to have |
||||
* stringshare references right. |
||||
* |
||||
* @note: it will not call any listener or add any event, please use |
||||
* e_fm2_op_registry_entry_changed(). |
||||
*/ |
||||
void |
||||
e_fm2_op_registry_entry_files_set(E_Fm2_Op_Registry_Entry *entry, const char *src, const char *dst) |
||||
{ |
||||
if (!entry) return; |
||||
|
||||
src = eina_stringshare_add(src); |
||||
dst = eina_stringshare_add(dst); |
||||
|
||||
eina_stringshare_del(entry->src); |
||||
eina_stringshare_del(entry->dst); |
||||
|
||||
entry->src = src; |
||||
entry->dst = dst; |
||||
} |
||||
|
||||
/**
|
||||
* Adds a reference to given entry. |
||||
* |
||||
* @return: new number of references after operation or -1 on error. |
||||
*/ |
||||
EAPI int |
||||
e_fm2_op_registry_entry_ref(E_Fm2_Op_Registry_Entry *entry) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Internal *e; |
||||
|
||||
if (!entry) return -1; |
||||
|
||||
e = _e_fm2_op_registry_entry_internal_get(entry); |
||||
return _e_fm2_op_registry_entry_internal_ref(e); |
||||
} |
||||
|
||||
/**
|
||||
* Releases a reference to given entry. |
||||
* |
||||
* @return: new number of references after operation or -1 on error, |
||||
* if 0 the entry was freed and pointer is then invalid. |
||||
*/ |
||||
EAPI int |
||||
e_fm2_op_registry_entry_unref(E_Fm2_Op_Registry_Entry *entry) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Internal *e; |
||||
|
||||
if (!entry) return -1; |
||||
|
||||
e = _e_fm2_op_registry_entry_internal_get(entry); |
||||
return _e_fm2_op_registry_entry_internal_unref(e); |
||||
} |
||||
|
||||
/**
|
||||
* Returns the X window associated to this operation. |
||||
* |
||||
* This will handle all bureaucracy to get X window based on e_fm evas |
||||
* object. |
||||
* |
||||
* @return: 0 if no window, window identifier otherwise. |
||||
*/ |
||||
EAPI Ecore_X_Window |
||||
e_fm2_op_registry_entry_xwin_get(const E_Fm2_Op_Registry_Entry *entry) |
||||
{ |
||||
Evas *e; |
||||
Ecore_Evas *ee; |
||||
|
||||
if (!entry) return 0; |
||||
if (!entry->e_fm) return 0; |
||||
|
||||
e = evas_object_evas_get(entry->e_fm); |
||||
if (!e) return 0; |
||||
|
||||
ee = evas_data_attach_get(e); |
||||
if (!ee) return 0; |
||||
|
||||
return (Ecore_X_Window)(long)ecore_evas_window_get(ee); |
||||
} |
||||
|
||||
/**
|
||||
* Discover entry based on its identifier. |
||||
* |
||||
* @note: does not increment reference. |
||||
*/ |
||||
EAPI E_Fm2_Op_Registry_Entry * |
||||
e_fm2_op_registry_entry_get(int id) |
||||
{ |
||||
return eina_hash_find(_e_fm2_op_registry, &id); |
||||
} |
||||
|
||||
/**
|
||||
* Adds a function to be called when entry changes. |
||||
* |
||||
* When entry changes any attribute this function will be called. |
||||
* |
||||
* @param: entry entry to operate on. |
||||
* @param: cb function to callback on changes. |
||||
* @param: data extra data to give to @p cb |
||||
* @param: free_data function to call when listener is removed, entry |
||||
* is deleted or any error occur in this function and listener |
||||
* cannot be added. |
||||
* |
||||
* @note: does not increment reference. |
||||
* @note: on errors, @p free_data will be called. |
||||
*/ |
||||
EAPI void |
||||
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)) |
||||
{ |
||||
E_Fm2_Op_Registry_Entry_Internal *e; |
||||
E_Fm2_Op_Registry_Entry_Listener *listener; |
||||
Eina_Error err; |
||||
|
||||
if ((!entry) || (!cb)) |
||||
{ |
||||
if (free_data) free_data((void *)data); |
||||
return; |
||||
} |
||||
|
||||
listener = malloc(sizeof(*listener)); |
||||
if (!listener) |
||||
{ |
||||
if (free_data) free_data((void *)data); |
||||
return; |
||||
} |
||||
listener->cb = cb; |
||||
listener->data = (void *)data; |
||||
listener->free_data = free_data; |
||||
|
||||
e = _e_fm2_op_registry_entry_internal_get(entry); |
||||
e->listeners = eina_inlist_append(e->listeners, EINA_INLIST_GET(listener)); |
||||
err = eina_error_get(); |
||||
if (err) |
||||
{ |
||||
EINA_ERROR_PERR("could not add listener: %s\n", |
||||
eina_error_msg_get(err)); |
||||
|
||||
if (free_data) free_data((void *)data); |
||||
free(listener); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
|