[Elm_Dnd] Fix type matching of drag and drop target objects in X11.

Summary:
Type matching for drag and drop targets does not consider drop target objects' types.

For example, we have drag object which provides image type and drop target object which only accepts text type.
For current code, in _x11_dnd_drop function, we only check savedtypes.types with _x11_atoms.
As result, we allows the image to be dropped into text. You can refer to the test in D617.

This path fixes this issue by matching drag object's type with drop targets' types to find suitable one.
@fix

Reviewers: raster, JackDanielZ, seoz, woohyun

Differential Revision: https://phab.enlightenment.org/D628
This commit is contained in:
Thiep Ha 2014-03-19 15:59:13 +09:00 committed by Carsten Haitzler (Rasterman)
parent b518a649bb
commit a71fc8cbb8
1 changed files with 219 additions and 124 deletions

View File

@ -95,8 +95,10 @@ struct _Dropable
/* FIXME: Cache window */
Eina_Inlist *cbs_list; /* List of Dropable_Cbs * */
struct {
Evas_Coord x, y;
Eina_Bool in : 1;
Evas_Coord x, y;
Eina_Bool in : 1;
const char *type;
Elm_Sel_Format format;
} last;
};
@ -1171,10 +1173,10 @@ _x11_dropable_find(Ecore_X_Window win)
return NULL;
}
static Dropable *
_x11_dropable_geom_find(Ecore_X_Window win, Evas_Coord px, Evas_Coord py)
static Eina_List *
_x11_dropable_list_geom_find(Ecore_X_Window win, Evas_Coord px, Evas_Coord py)
{
Eina_List *itr, *top_objects_list = NULL;
Eina_List *itr, *top_objects_list = NULL, *dropable_list = NULL;
Evas *evas = NULL;
Evas_Object *top_obj;
Dropable *dropable = NULL;
@ -1211,15 +1213,31 @@ _x11_dropable_geom_find(Ecore_X_Window win, Evas_Coord px, Evas_Coord py)
{
eo_do(object, eo_base_data_get("__elm_dropable", (void **)&dropable));
if (dropable)
goto end;
{
Eina_Bool exist = EINA_FALSE;
Eina_List *l;
Dropable *d = NULL;
EINA_LIST_FOREACH(dropable_list, l, d)
{
if (d == dropable)
{
exist = EINA_TRUE;
break;
}
}
if (!exist)
dropable_list = eina_list_append(dropable_list, dropable);
object = evas_object_smart_parent_get(object);
if (dropable)
cnp_debug("Drop target %p of type %s found\n",
dropable->obj, eo_class_name_get(eo_class_get(dropable->obj)));
}
else
object = evas_object_smart_parent_get(object);
}
}
end:
eina_list_free(top_objects_list);
if (dropable) cnp_debug("Drop target %p of type %s found\n", dropable->obj, eo_class_name_get(eo_class_get(dropable->obj)));
return dropable;
return dropable_list;
}
static void
@ -1234,22 +1252,6 @@ _x11_dropable_coords_adjust(Dropable *dropable, Evas_Coord *x, Evas_Coord *y)
*y = *y - ey;
}
static void
_x11_dropable_all_set(Ecore_X_Window win, Evas_Coord x, Evas_Coord y, Eina_Bool set)
{
Eina_List *l;
Dropable *dropable;
EINA_LIST_FOREACH(drops, l, dropable)
{
if (_x11_elm_widget_xwin_get(dropable->obj) == win)
{
dropable->last.x = x;
dropable->last.y = y;
dropable->last.in = set;
}
}
}
static Eina_Bool
_x11_dnd_enter(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
{
@ -1262,7 +1264,6 @@ _x11_dnd_enter(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
if (dropable)
{
cnp_debug("Enter %x\n", enter->win);
_x11_dropable_all_set(enter->win, 0, 0, EINA_FALSE);
}
/* Skip it */
cnp_debug("enter types=%p (%d)\n", enter->types, enter->num_types);
@ -1294,53 +1295,74 @@ _x11_dnd_enter(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
}
static void
_x11_dnd_dropable_handle(Dropable *dropable, Evas_Coord x, Evas_Coord y, Eina_Bool have_obj, Elm_Xdnd_Action action)
_x11_dnd_dropable_handle(Dropable *dropable, Evas_Coord x, Evas_Coord y, Elm_Xdnd_Action action)
{
Dropable *dropable_last = NULL;
Dropable *d, *last_dropable = NULL;
Eina_List *l;
Dropable_Cbs *cbs;
Eina_Inlist *itr;
if (dropable->last.in)
dropable_last = _x11_dropable_geom_find
(_x11_elm_widget_xwin_get(dropable->obj),
dropable->last.x, dropable->last.y);
if ((have_obj) && (dropable_last == dropable)) // same
EINA_LIST_FOREACH(drops, l, d)
{
cnp_debug("same obj dropable %p\n", dropable);
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
if (cbs->poscb)
cbs->poscb(cbs->posdata, dropable->obj, x, y, action);
}
else if ((have_obj) && (!dropable_last)) // enter new obj
{
cnp_debug("enter %p\n", dropable->obj);
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
if (cbs->entercb)
cbs->entercb(cbs->enterdata, dropable->obj);
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
if (cbs->poscb)
cbs->poscb(cbs->posdata, dropable->obj, x, y, action);
}
else if ((!have_obj) && (dropable_last)) // leave last obj
{
cnp_debug("leave %p\n", dropable_last->obj);
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
if (cbs->leavecb)
cbs->leavecb(cbs->leavedata, dropable->obj);
}
else if (have_obj) // leave last obj and enter new one
{
cnp_debug("enter %p\n", dropable->obj);
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
if (cbs->entercb)
cbs->entercb(cbs->enterdata, dropable->obj);
if (dropable_last)
if (d->last.in)
{
dropable = dropable_last;
last_dropable = d;
break;
}
}
if (last_dropable)
{
if (last_dropable == dropable) // same
{
cnp_debug("same obj dropable %p\n", dropable->obj);
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
if (cbs->leavecb)
cbs->leavecb(cbs->leavedata, dropable->obj);
cnp_debug("leave %p\n", dropable->obj);
if (cbs->poscb)
cbs->poscb(cbs->posdata, dropable->obj, x, y, action);
}
else
{
if (dropable) // leave last obj and enter new one
{
cnp_debug("leave %p\n", last_dropable->obj);
cnp_debug("enter %p\n", dropable->obj);
last_dropable->last.in = EINA_FALSE;
last_dropable->last.type = NULL;
dropable->last.in = EINA_TRUE;
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
if (cbs->entercb)
cbs->entercb(cbs->enterdata, dropable->obj);
EINA_INLIST_FOREACH_SAFE(last_dropable->cbs_list, itr, cbs)
if (cbs->leavecb)
cbs->leavecb(cbs->leavedata, last_dropable->obj);
}
else // leave last obj
{
cnp_debug("leave %p\n", last_dropable->obj);
last_dropable->last.in = EINA_FALSE;
last_dropable->last.type = NULL;
EINA_INLIST_FOREACH_SAFE(last_dropable->cbs_list, itr, cbs)
if (cbs->leavecb)
cbs->leavecb(cbs->leavedata, last_dropable->obj);
}
}
}
else
{
if (dropable) // enter new obj
{
cnp_debug("enter %p\n", dropable->obj);
dropable->last.in = EINA_TRUE;
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
{
if (cbs->entercb)
cbs->entercb(cbs->enterdata, dropable->obj);
if (cbs->poscb)
cbs->poscb(cbs->posdata, dropable->obj, x, y, action);
}
}
else
{
cnp_debug("both dropable & last_dropable are null\n");
}
}
}
@ -1389,50 +1411,129 @@ _x11_dnd_action_rev_map(Elm_Xdnd_Action action)
return act;
}
static int
_x11_dnd_types_get(Elm_Sel_Format format, const char **types)
{
int i;
int types_no = 0;
for (i = 0; i < CNP_N_ATOMS; i++)
{
if (_x11_atoms[i].formats == ELM_SEL_FORMAT_TARGETS)
{
if (format == ELM_SEL_FORMAT_TARGETS)
if (types)
types[types_no++] = _x11_atoms[i].name;
}
else if (_x11_atoms[i].formats & format)
{
if (types)
types[types_no++] = _x11_atoms[i].name;
}
}
return types_no;
}
static Eina_Bool
_x11_dnd_position(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
{
Ecore_X_Event_Xdnd_Position *pos = ev;
Ecore_X_Rectangle rect = { 0, 0, 0, 0 };
Dropable *dropable, *dropable_old;
Dropable *dropable;
Elm_Xdnd_Action act;
/* Need to send a status back */
/* FIXME: Should check I can drop here */
/* FIXME: Should highlight widget */
dropable_old = dropable = _x11_dropable_find(pos->win);
dropable = _x11_dropable_find(pos->win);
if (dropable)
{
Evas_Coord x, y, ox = 0, oy = 0, ow = 0, oh = 0;
Eina_List *dropable_list;
Evas_Coord x, y, ox = 0, oy = 0;
act = _x11_dnd_action_map(pos->action);
x = pos->position.x;
y = pos->position.y;
_x11_dropable_coords_adjust(dropable, &x, &y);
dropable = _x11_dropable_geom_find(pos->win, x, y);
act = _x11_dnd_action_map(pos->action);
if (dropable)
dropable_list = _x11_dropable_list_geom_find(pos->win, x, y);
/* check if there is dropable (obj) can accept this drop */
if (dropable_list)
{
evas_object_geometry_get(dropable->obj, &ox, &oy, &ow, &oh);
rect.x = pos->position.x - x + ox;
rect.y = pos->position.y - y + oy;
rect.width = ow;
rect.height = oh;
ecore_x_dnd_send_status(EINA_TRUE, EINA_FALSE, rect, pos->action);
cnp_debug("dnd position %i %i %p\n", x - ox, y - oy, dropable);
_x11_dnd_dropable_handle(dropable, x - ox, y - oy, EINA_TRUE,
act);
// CCCCCCC: call dnd exit on last obj if obj != last
// CCCCCCC: call drop position on obj
_x11_dropable_all_set(pos->win, x, y, EINA_TRUE);
Eina_List *l;
Eina_Bool found = EINA_FALSE;
int i, j;
EINA_LIST_FOREACH(dropable_list, l, dropable)
{
Dropable_Cbs *cbs;
Eina_Inlist *itr;
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
{
int types_no;
const char *types[CNP_N_ATOMS];
types_no = _x11_dnd_types_get(cbs->types, types);
for (j = 0; j < types_no; j++)
{
for (i = 0; i < savedtypes.ntypes; i++)
{
if (!strcmp(types[j], savedtypes.types[i]))
{
found = EINA_TRUE;
dropable->last.type = savedtypes.types[i];
dropable->last.format = cbs->types;
break;
}
}
if (found) break;
}
if (found) break;
}
if (found) break;
}
if (found)
{
Dropable *d = NULL;
Eina_Rectangle inter_rect = {0, 0, 0, 0};
int idx = 0;
EINA_LIST_FOREACH(dropable_list, l, d)
{
if (idx == 0)
{
evas_object_geometry_get(d->obj, &inter_rect.x, &inter_rect.y,
&inter_rect.w, &inter_rect.h);
}
else
{
Eina_Rectangle cur_rect;
evas_object_geometry_get(d->obj, &cur_rect.x, &cur_rect.y,
&cur_rect.w, &cur_rect.h);
if (!eina_rectangle_intersection(&inter_rect, &cur_rect)) continue;
}
idx++;
}
rect.x = inter_rect.x;
rect.y = inter_rect.y;
rect.width = inter_rect.w;
rect.height = inter_rect.h;
ecore_x_dnd_send_status(EINA_TRUE, EINA_FALSE, rect, pos->action);
cnp_debug("dnd position %i %i %p\n", x - ox, y - oy, dropable);
_x11_dnd_dropable_handle(dropable, x - ox, y - oy, act);
// CCCCCCC: call dnd exit on last obj if obj != last
// CCCCCCC: call drop position on obj
}
else
{
//if not: send false status
ecore_x_dnd_send_status(EINA_FALSE, EINA_FALSE, rect, pos->action);
cnp_debug("dnd position (%d, %d) not in obj\n", x, y);
_x11_dnd_dropable_handle(NULL, 0, 0, act);
// CCCCCCC: call dnd exit on last obj
}
}
else
{
ecore_x_dnd_send_status(EINA_FALSE, EINA_FALSE, rect, pos->action);
cnp_debug("dnd position (%d, %d) not in obj\n", x, y);
_x11_dnd_dropable_handle(dropable_old, 0, 0, EINA_FALSE,
act);
// CCCCCCC: call dnd exit on last obj
_x11_dropable_all_set(pos->win, x, y, EINA_TRUE);
cnp_debug("dnd position (%d, %d) has no drop\n", x, y);
_x11_dnd_dropable_handle(NULL, 0, 0, act);
}
}
else
@ -1445,17 +1546,9 @@ _x11_dnd_position(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
static Eina_Bool
_x11_dnd_leave(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
{
Ecore_X_Event_Xdnd_Leave *leave = ev;
Dropable *dropable;
dropable = _x11_dropable_find(leave->win);
if (dropable)
{
cnp_debug("Leave %x\n", leave->win);
_x11_dnd_dropable_handle(dropable, 0, 0, EINA_FALSE, ELM_XDND_ACTION_UNKNOWN);
_x11_dropable_all_set(leave->win, 0, 0, EINA_FALSE);
// CCCCCCC: call dnd exit on last obj if there was one
}
cnp_debug("Leave %x\n", ((Ecore_X_Event_Xdnd_Leave *)ev)->win);
_x11_dnd_dropable_handle(NULL, 0, 0, ELM_XDND_ACTION_UNKNOWN);
// CCCCCCC: call dnd exit on last obj if there was one
// leave->win leave->source
return EINA_TRUE;
}
@ -1464,11 +1557,13 @@ static Eina_Bool
_x11_dnd_drop(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
{
Ecore_X_Event_Xdnd_Drop *drop;
Dropable *dropable;
Dropable *dropable = NULL;
Elm_Selection_Data ddata;
Evas_Coord x = 0, y = 0;
Elm_Xdnd_Action act = ELM_XDND_ACTION_UNKNOWN;
int i, j;
Eina_List *l;
Dropable_Cbs *cbs;
Eina_Inlist *itr;
drop = ev;
@ -1484,19 +1579,14 @@ _x11_dnd_drop(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
cnp_debug("Drop position is %d,%d\n", savedtypes.x, savedtypes.y);
dropable = _x11_dropable_geom_find(drop->win, savedtypes.x, savedtypes.y);
if (!dropable) return EINA_TRUE; /* didn't find one */
evas_object_geometry_get(dropable->obj, &x, &y, NULL, NULL);
savedtypes.x -= x;
savedtypes.y -= y;
/* Find our type from the previous list */
for (i = 0; i < CNP_N_ATOMS; i++)
EINA_LIST_FOREACH(drops, l, dropable)
{
for (j = 0; j < savedtypes.ntypes; j++)
if (dropable->last.in)
{
if (!strcmp(savedtypes.types[j], _x11_atoms[i].name)) goto found;
evas_object_geometry_get(dropable->obj, &x, &y, NULL, NULL);
savedtypes.x -= x;
savedtypes.y -= y;
goto found;
}
}
@ -1504,12 +1594,11 @@ _x11_dnd_drop(void *data EINA_UNUSED, int etype EINA_UNUSED, void *ev)
return EINA_TRUE;
found:
cnp_debug("Found a target we'd like: %s\n", _x11_atoms[i].name);
cnp_debug("0x%x\n", drop->win);
act = _x11_dnd_action_map(drop->action);
if (i == CNP_ATOM_text_urilist)
if ((!strcmp(dropable->last.type, text_uri)))
{
cnp_debug("We found a URI... (%scached) %s\n",
savedtypes.imgfile ? "" : "not ",
@ -1523,8 +1612,6 @@ found:
ddata.y = savedtypes.y;
ddata.action = act;
Dropable_Cbs *cbs;
Eina_Inlist *itr;
EINA_INLIST_FOREACH_SAFE(dropable->cbs_list, itr, cbs)
{
/* If it's markup that also supports images */
@ -1569,14 +1656,21 @@ found:
}
}
cnp_debug("doing a request then\n");
_x11_selections[ELM_SEL_TYPE_XDND].xwin = drop->win;
_x11_selections[ELM_SEL_TYPE_XDND].requestwidget = dropable->obj;
_x11_selections[ELM_SEL_TYPE_XDND].requestformat = ELM_SEL_FORMAT_MARKUP;
_x11_selections[ELM_SEL_TYPE_XDND].active = EINA_TRUE;
_x11_selections[ELM_SEL_TYPE_XDND].action = act;
if (dropable->last.type)
{
cnp_debug("doing a request then: %s\n", dropable->last.type);
_x11_selections[ELM_SEL_TYPE_XDND].xwin = drop->win;
_x11_selections[ELM_SEL_TYPE_XDND].requestwidget = dropable->obj;
_x11_selections[ELM_SEL_TYPE_XDND].requestformat = dropable->last.format;
_x11_selections[ELM_SEL_TYPE_XDND].active = EINA_TRUE;
_x11_selections[ELM_SEL_TYPE_XDND].action = act;
ecore_x_selection_xdnd_request(drop->win, _x11_atoms[i].name);
ecore_x_selection_xdnd_request(drop->win, dropable->last.type);
}
else
{
cnp_debug("cannot match format\n");
}
return EINA_TRUE;
}
@ -1890,6 +1984,7 @@ _x11_elm_drop_target_add(Evas_Object *obj, Elm_Sel_Format format,
/* Create new drop */
dropable = calloc(1, sizeof(Dropable));
if (!dropable) goto error;
dropable->last.in = EINA_FALSE;
drops = eina_list_append(drops, dropable);
if (!drops) goto error;
dropable->obj = obj;