struct _Widget_Data
{
- Evas_Object *scr, *box;
- Eina_List *items, *selected;
+ Evas_Object *scr, *box, *self;
+ Eina_List *items, *selected, *to_delete;
Elm_List_Mode mode;
Evas_Coord minw[2], minh[2];
+ int walking;
+ Eina_Bool fix_pending : 1;
Eina_Bool on_hold : 1;
Eina_Bool multi : 1;
Eina_Bool always_select : 1;
void (*del_cb) (void *data, Evas_Object *obj, void *event_info);
const void *data;
Ecore_Timer *long_timer;
+ Eina_Bool deleted : 1;
Eina_Bool even : 1;
Eina_Bool is_even : 1;
Eina_Bool fixed : 1;
static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
static void _sub_del(void *data, Evas_Object *obj, void *event_info);
static void _fix_items(Evas_Object *obj);
+static void _mouse_down(void *data, Evas *evas, Evas_Object *obj, void *event_info);
+static void _mouse_up(void *data, Evas *evas, Evas_Object *obj, void *event_info);
+static void _mouse_move(void *data, Evas *evas, Evas_Object *obj, void *event_info);
+
+#define ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, ...) \
+ if ((it) && ((it)->deleted)) \
+ { \
+ fprintf(stderr, "ERROR: %s:%d:%s() "#it" is NULL.\n", \
+ __FILE__, __LINE__, __FUNCTION__); \
+ return __VA_ARGS__; \
+ }
+
+
+
+static inline void
+_elm_list_item_call_del_cb(Elm_List_Item *it)
+{
+ if (it->del_cb) it->del_cb((void *)it->data, it->obj, it);
+}
+
+static inline void
+_elm_list_item_free(Elm_List_Item *it)
+{
+ evas_object_event_callback_del_full
+ (it->base, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down, it);
+ evas_object_event_callback_del_full
+ (it->base, EVAS_CALLBACK_MOUSE_UP, _mouse_up, it);
+ evas_object_event_callback_del_full
+ (it->base, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move, it);
+
+ if (it->icon)
+ evas_object_event_callback_del_full
+ (it->icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
+ _changed_size_hints, it->obj);
+
+ if (it->end)
+ evas_object_event_callback_del_full
+ (it->end, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
+ _changed_size_hints, it->obj);
+
+ eina_stringshare_del(it->label);
+
+ if (it->long_timer) ecore_timer_del(it->long_timer);
+ if (it->icon) evas_object_del(it->icon);
+ if (it->end) evas_object_del(it->end);
+ if (it->base) evas_object_del(it->base);
+ free(it);
+}
+
+static void
+_elm_list_process_deletions(Widget_Data *wd)
+{
+ Elm_List_Item *it;
+
+ wd->walking++; // avoid nested deletion and also _sub_del() fix_items
+
+ EINA_LIST_FREE(wd->to_delete, it)
+ {
+ _elm_list_item_call_del_cb(it);
+
+ wd->items = eina_list_remove_list(wd->items, it->node);
+ _elm_list_item_free(it);
+ }
+
+ wd->walking--;
+}
+
+static inline void
+_elm_list_walk(Widget_Data *wd)
+{
+ if (wd->walking < 0)
+ {
+ fprintf(stderr, "ERROR: walking was negative. fixed!\n");
+ wd->walking = 0;
+ }
+ wd->walking++;
+}
+
+static inline void
+_elm_list_unwalk(Widget_Data *wd)
+{
+ wd->walking--;
+ if (wd->walking < 0)
+ {
+ fprintf(stderr, "ERROR: walking became negative. fixed!\n");
+ wd->walking = 0;
+ }
+
+ if (wd->walking)
+ return;
+
+ if (wd->to_delete)
+ _elm_list_process_deletions(wd);
+
+ if (wd->fix_pending)
+ {
+ wd->fix_pending = EINA_FALSE;
+ _fix_items(wd->self);
+ _sizing_eval(wd->self);
+ }
+}
static void
_del_hook(Evas_Object *obj)
{
Widget_Data *wd = elm_widget_data_get(obj);
Elm_List_Item *it;
+ Eina_List *n;
+
+ if (wd->walking != 0)
+ fprintf(stderr, "ERROR: list deleted while walking.\n");
+
+ _elm_list_walk(wd);
+
+ EINA_LIST_FOREACH(wd->items, n, it)
+ _elm_list_item_call_del_cb(it);
+
+ _elm_list_unwalk(wd);
+ if (wd->to_delete)
+ fprintf(stderr, "ERROR: leaking nodes!\n");
EINA_LIST_FREE(wd->items, it)
- {
- if (it->del_cb) it->del_cb((void *)it->data, it->obj, it);
- if (it->long_timer) ecore_timer_del(it->long_timer);
- eina_stringshare_del(it->label);
- if (!it->fixed)
- {
- if (it->icon) evas_object_del(it->icon);
- if (it->end) evas_object_del(it->end);
- }
- if (it->base) evas_object_del(it->base);
- free(it);
- }
+ _elm_list_item_free(it);
+
eina_list_free(wd->selected);
+
free(wd);
}
evas_object_event_callback_del_full(sub,
EVAS_CALLBACK_CHANGED_SIZE_HINTS,
_changed_size_hints, obj);
- _fix_items(obj);
- _sizing_eval(obj);
+ if (!wd->walking)
+ {
+ _fix_items(obj);
+ _sizing_eval(obj);
+ }
+ else
+ wd->fix_pending = EINA_TRUE;
break;
}
}
Widget_Data *wd = elm_widget_data_get(it->obj);
const char *selectraise;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
if (it->hilighted) return;
+ _elm_list_walk(wd);
+
edje_object_signal_emit(it->base, "elm,state,selected", "elm");
selectraise = edje_object_data_get(it->base, "selectraise");
if ((selectraise) && (!strcmp(selectraise, "on")))
evas_object_raise(it->base);
it->hilighted = EINA_TRUE;
+
+ _elm_list_unwalk(wd);
}
static void
Widget_Data *wd = elm_widget_data_get(it->obj);
const char *selectraise;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
if (it->selected)
{
if (wd->always_select) goto call;
it->selected = EINA_TRUE;
wd->selected = eina_list_append(wd->selected, it);
call:
+ _elm_list_walk(wd);
+
if (it->func) it->func((void *)it->data, it->obj, it);
evas_object_smart_callback_call(it->obj, "selected", it);
+
+ _elm_list_unwalk(wd);
}
static void
Widget_Data *wd = elm_widget_data_get(it->obj);
const char *stacking, *selectraise;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
if (!it->hilighted) return;
+ _elm_list_walk(wd);
+
edje_object_signal_emit(it->base, "elm,state,unselected", "elm");
stacking = edje_object_data_get(it->base, "stacking");
selectraise = edje_object_data_get(it->base, "selectraise");
wd->selected = eina_list_remove(wd->selected, it);
evas_object_smart_callback_call(it->obj, "unselected", it);
}
+
+ _elm_list_unwalk(wd);
}
static void
Widget_Data *wd = elm_widget_data_get(it->obj);
Evas_Event_Mouse_Move *ev = event_info;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
{
if (!wd->on_hold)
Widget_Data *wd = elm_widget_data_get(it->obj);
it->long_timer = NULL;
+
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, 0);
+
wd->longpressed = EINA_TRUE;
evas_object_smart_callback_call(it->obj, "longpressed", it);
return 0;
Widget_Data *wd = elm_widget_data_get(it->obj);
Evas_Event_Mouse_Down *ev = event_info;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
if (ev->button != 1) return;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) wd->on_hold = EINA_TRUE;
else wd->on_hold = EINA_FALSE;
Widget_Data *wd = elm_widget_data_get(it->obj);
Evas_Event_Mouse_Up *ev = event_info;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
if (ev->button != 1) return;
if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) wd->on_hold = EINA_TRUE;
else wd->on_hold = EINA_FALSE;
wd->wasselected = 0;
return;
}
+
+ _elm_list_walk(wd); // watch out "return" before unwalk!
+
if (wd->multi)
{
if (!it->selected)
_item_select(it);
}
}
+
+ _elm_list_unwalk(wd);
}
static Elm_List_Item *
int i, redo = 0;
const char *style = elm_widget_style_get(obj);
+ if (wd->walking)
+ {
+ wd->fix_pending = EINA_TRUE;
+ return;
+ }
+
+ _elm_list_walk(wd); // watch out "return" before unwalk!
+
EINA_LIST_FOREACH(wd->items, l, it)
{
Evas_Coord mw, mh;
+ if (it->deleted) continue;
if (it->icon)
{
evas_object_size_hint_min_get(it->icon, &mw, &mh);
i = 0;
EINA_LIST_FOREACH(wd->items, l, it)
{
+ if (it->deleted) continue;
it->even = i & 0x1;
if ((it->even != it->is_even) || (!it->fixed) || (redo))
{
}
if (!it->fixed)
{
+ // this may call up user and it may modify the list item
+ // but we're safe as we're flagged as walking.
+ // just don't process further
edje_object_message_signal_process(it->base);
+ if (it->deleted)
+ continue;
mw = mh = -1;
elm_coords_finger_size_adjust(1, &mw, 1, &mh);
edje_object_size_min_restricted_calc(it->base, &mw, &mh, mw, mh);
{
const char *selectraise;
+ // this may call up user and it may modify the list item
+ // but we're safe as we're flagged as walking.
+ // just don't process further
edje_object_signal_emit(it->base, "elm,state,selected", "elm");
+ if (it->deleted)
+ continue;
+
selectraise = edje_object_data_get(it->base, "selectraise");
if ((selectraise) && (!strcmp(selectraise, "on")))
evas_object_raise(it->base);
}
i++;
}
+
+ _elm_list_unwalk(wd);
+
mw = 0; mh = 0;
evas_object_size_hint_min_get(wd->box, &mw, &mh);
if (wd->mode == ELM_LIST_LIMIT)
wd = ELM_NEW(Widget_Data);
e = evas_object_evas_get(parent);
- obj = elm_widget_add(e);
+ wd->self = obj = elm_widget_add(e);
elm_widget_type_set(obj, "list");
elm_widget_sub_object_add(parent, obj);
elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
Elm_List_Item *it;
if ((!before) || (!before->node)) return NULL;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(before, NULL);
wd = elm_widget_data_get(obj);
it = _item_new(obj, label, icon, end, func, data);
Elm_List_Item *it;
if ((!after) || (!after->node)) return NULL;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(after, NULL);
wd = elm_widget_data_get(obj);
it = _item_new(obj, label, icon, end, func, data);
elm_list_clear(Evas_Object *obj)
{
Widget_Data *wd = elm_widget_data_get(obj);
+ Elm_List_Item *it;
+
+ if (!wd->items)
+ return;
+
+ eina_list_free(wd->selected);
+ wd->selected = NULL;
+
+ if (wd->walking > 0)
+ {
+ Eina_List *n;
+ Elm_List_Item *it;
+ EINA_LIST_FOREACH(wd->items, n, it)
+ {
+ if (it->deleted)
+ continue;
+ it->deleted = EINA_TRUE;
+ wd->to_delete = eina_list_append(wd->to_delete, it);
+ }
+ return;
+ }
+
+ _elm_list_walk(wd);
+
+ EINA_LIST_FREE(wd->items, it)
+ {
+ _elm_list_item_call_del_cb(it);
+ _elm_list_item_free(it);
+ }
+
+ _elm_list_unwalk(wd);
- while (wd->items)
- elm_list_item_del(wd->items->data);
+ _fix_items(obj);
+ _sizing_eval(obj);
}
EAPI void
{
Widget_Data *wd = elm_widget_data_get(it->obj);
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
selected = !!selected;
if (it->selected == selected) return;
+ _elm_list_walk(wd);
+
if (selected)
{
if (!wd->multi)
}
else
_item_unselect(it);
+
+ _elm_list_unwalk(wd);
}
EAPI void
Evas_Coord bx, by, bw, bh;
Evas_Coord x, y, w, h;
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
evas_object_geometry_get(wd->box, &bx, &by, &bw, &bh);
evas_object_geometry_get(it->base, &x, &y, &w, &h);
x -= bx;
{
Widget_Data *wd = elm_widget_data_get(it->obj);
- if (it->del_cb) it->del_cb((void *)it->data, it->obj, it);
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
if (it->selected) _item_unselect(it);
+
+ if (wd->walking > 0)
+ {
+ if (it->deleted) return;
+ it->deleted = EINA_TRUE;
+ wd->to_delete = eina_list_append(wd->to_delete, it);
+ return;
+ }
+
wd->items = eina_list_remove_list(wd->items, it->node);
- eina_stringshare_del(it->label);
- if (it->long_timer) ecore_timer_del(it->long_timer);
- if (it->icon) evas_object_del(it->icon);
- if (it->end) evas_object_del(it->end);
- if (it->base) evas_object_del(it->base);
- free(it);
+
+ _elm_list_walk(wd);
+
+ _elm_list_item_call_del_cb(it);
+ _elm_list_item_free(it);
+
+ _elm_list_unwalk(wd);
}
/**
EAPI void
elm_list_item_del_cb_set(Elm_List_Item *it, void (*func)(void *data, Evas_Object *obj, void *event_info))
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
it->del_cb = func;
}
EAPI void *
elm_list_item_data_get(const Elm_List_Item *it)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
+
return (void *)it->data;
}
EAPI Evas_Object *
elm_list_item_icon_get(const Elm_List_Item *it)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
+
if (it->dummy_icon) return NULL;
return it->icon;
}
EAPI void
elm_list_item_icon_set(Elm_List_Item *it, Evas_Object *icon)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
if (it->icon == icon) return;
if (it->dummy_icon && !icon) return;
if (it->dummy_icon) evas_object_del(it->icon);
EAPI Evas_Object *
elm_list_item_end_get(const Elm_List_Item *it)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
+
if (it->dummy_end) return NULL;
return it->end;
}
EAPI void
elm_list_item_end_set(Elm_List_Item *it, Evas_Object *end)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
if (it->end == end) return;
if (it->dummy_end && !end) return;
if (it->dummy_end) evas_object_del(it->end);
EAPI Evas_Object *
elm_list_item_base_get(const Elm_List_Item *it)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
+
return it->base;
}
EAPI const char *
elm_list_item_label_get(const Elm_List_Item *it)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
+
return it->label;
}
EAPI void
elm_list_item_label_set(Elm_List_Item *it, const char *text)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
+
if (!eina_stringshare_replace(&it->label, text)) return;
if (it->base)
edje_object_part_text_set(it->base, "elm.text", it->label);
EAPI Elm_List_Item *
elm_list_item_prev(const Elm_List_Item *it)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
+
if (it->node->prev)
return it->node->prev->data;
else
EAPI Elm_List_Item *
elm_list_item_next(const Elm_List_Item *it)
{
+ ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
+
if (it->node->next)
return it->node->next->data;
else