diff options
-rw-r--r-- | Changelog | 4 | ||||
-rw-r--r-- | win/OleDND.h | 358 |
2 files changed, 203 insertions, 159 deletions
@@ -1,3 +1,7 @@ +2014-08-19 Petasis George <petasis@iit.demokritos.gr> + * win/OleDND.h: Corrected behaviour when mouse drags over overlapping + windows. Bug reported by Csaba Nemethi. + 2014-08-13 Petasis George <petasis@iit.demokritos.gr> * library/tkdnd_windows.tcl: * library/tkdnd.tcl: diff --git a/win/OleDND.h b/win/OleDND.h index f1a01c4..807a8d5 100644 --- a/win/OleDND.h +++ b/win/OleDND.h @@ -469,6 +469,9 @@ class TkDND_DropTarget: public IDropTarget { Tcl_Interp *interp; Tk_Window tkwin; TCHAR szTempStr[MAX_PATH+2]; + + Tcl_Obj *typelist, *actionlist, *codelist; + bool drop_active; const TCHAR * FormatName(UINT cfFormat) { for (int i = 0; ClipboardFormatBook[i].name != 0; i++) { @@ -481,7 +484,8 @@ class TkDND_DropTarget: public IDropTarget { public: TkDND_DropTarget(Tcl_Interp *_interp, Tk_Window _tkwin) : - interp(_interp), tkwin(_tkwin), m_lRefCount(1) { + interp(_interp), tkwin(_tkwin), m_lRefCount(1), drop_active(false), + typelist(NULL), actionlist(NULL), codelist(NULL) { }; /* TkDND_DropTarget */ ~TkDND_DropTarget(void) { @@ -515,51 +519,11 @@ class TkDND_DropTarget: public IDropTarget { } }; /* Release */ - /* IDropTarget interface members */ - - STDMETHODIMP DragEnter(IDataObject *pDataObject, DWORD grfKeyState, - POINTL pt, DWORD *pdwEffect) { - // We want to get: - // a) The types supported by the drag source. - // b) The actions supported by the drag source. - // c) The state of the keyboard modifier keys. - // And we must return: - // a) The prefered action. - - IEnumFORMATETC *pEF; - FORMATETC fetc; - char tmp[64]; - Tcl_Obj *typelist = Tcl_NewListObj(0, NULL), *element, - *actionlist = Tcl_NewListObj(0, NULL), *objv[8], - *pressedkeys = Tcl_NewListObj(0, NULL), *result, - *codelist = Tcl_NewListObj(0, NULL); - int i, status, index; - static const char *DropActions[] = { - "copy", "move", "link", "ask", "private", "refuse_drop", - "default", - (char *) NULL - }; - enum dropactions { - ActionCopy, ActionMove, ActionLink, ActionAsk, ActionPrivate, - refuse_drop, ActionDefault - }; - - // Get the types supported by the drag source. - if (pDataObject->EnumFormatEtc(DATADIR_GET, &pEF) == S_OK) { - while (pEF->Next(1, &fetc, NULL) == S_OK) { - if (pDataObject->QueryGetData(&fetc) == S_OK) { - /* Get the format name from windows */ - element = TCL_NEWSTRING(FormatName(fetc.cfFormat), -1); - Tcl_ListObjAppendElement(NULL, typelist, element); - /* Store the numeric code of the format */ - sprintf(tmp, "0x%08x", fetc.cfFormat); - element = Tcl_NewStringObj(tmp, -1); - Tcl_ListObjAppendElement(NULL, codelist, element); - }; // if (pIDataSource->QueryGetData(&fetc) == S_OK) - }; // while (pEF->Next(1, &fetc, NULL) == S_OK) - }; // if (pIDataSource->EnumFormatEtc(DATADIR_GET, &pEF) == S_OK) - - // Get the state of the keyboard modifier keys. + /* + * Helper functions... + */ + Tcl_Obj *GetPressedKeys(DWORD grfKeyState) { + Tcl_Obj *pressedkeys = Tcl_NewListObj(0, NULL); // MK_CONTROL, MK_SHIFT, MK_ALT, MK_RBUTTON, MK_LBUTTON if (grfKeyState & MK_CONTROL) Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("ctrl", -1)); @@ -573,28 +537,35 @@ class TkDND_DropTarget: public IDropTarget { Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("2", -1)); if (grfKeyState & MK_LBUTTON) Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("1", -1)); - - // Get the actions supported by the drag source. - // DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK - if (*pdwEffect & DROPEFFECT_COPY) - Tcl_ListObjAppendElement(NULL,actionlist,Tcl_NewStringObj("copy", -1)); - if (*pdwEffect & DROPEFFECT_MOVE) - Tcl_ListObjAppendElement(NULL,actionlist,Tcl_NewStringObj("move", -1)); - if (*pdwEffect & DROPEFFECT_LINK) - Tcl_ListObjAppendElement(NULL,actionlist,Tcl_NewStringObj("link", -1)); + return pressedkeys; + }; /* GetPressedKeys */ + + DWORD SendDragEnter(POINTL pt, DWORD grfKeyState) { + Tcl_Obj *objv[8], *result; + int i, status, index; + DWORD effect = DROPEFFECT_NONE; + static const char *DropActions[] = { + "copy", "move", "link", "ask", "private", "refuse_drop", + "default", + (char *) NULL + }; + enum dropactions { + ActionCopy, ActionMove, ActionLink, ActionAsk, ActionPrivate, + refuse_drop, ActionDefault + }; + if (drop_active) { + return DROPEFFECT_COPY; + } - // We are ready to pass the info to the Tcl level, and get the desired - // action. objv[0] = Tcl_NewStringObj("tkdnd::olednd::_HandleDragEnter", -1); objv[1] = Tcl_NewStringObj(Tk_PathName(tkwin), -1); objv[2] = typelist; objv[3] = actionlist; - objv[4] = pressedkeys; + objv[4] = GetPressedKeys(grfKeyState); objv[5] = Tcl_NewLongObj(pt.x); objv[6] = Tcl_NewLongObj(pt.y); objv[7] = codelist; TkDND_Status_Eval(8); - *pdwEffect = DROPEFFECT_NONE; if (status == TCL_OK) { /* Get the returned action... */ result = Tcl_GetObjResult(interp); Tcl_IncrRefCount(result); @@ -604,54 +575,40 @@ class TkDND_DropTarget: public IDropTarget { if (status != TCL_OK) index = ActionDefault; } switch ((enum dropactions) index) { - case ActionCopy: *pdwEffect = DROPEFFECT_COPY; break; - case ActionMove: *pdwEffect = DROPEFFECT_MOVE; break; - case ActionLink: *pdwEffect = DROPEFFECT_LINK; break; - case ActionAsk: *pdwEffect = DROPEFFECT_NONE; break; - case ActionPrivate: *pdwEffect = DROPEFFECT_NONE; break; - case ActionDefault: *pdwEffect = DROPEFFECT_COPY; break; - case refuse_drop: *pdwEffect = DROPEFFECT_NONE; /* Refuse drop. */ + case ActionCopy: effect = DROPEFFECT_COPY; break; + case ActionMove: effect = DROPEFFECT_MOVE; break; + case ActionLink: effect = DROPEFFECT_LINK; break; + case ActionAsk: effect = DROPEFFECT_NONE; break; + case ActionPrivate: effect = DROPEFFECT_NONE; break; + case ActionDefault: effect = DROPEFFECT_COPY; break; + case refuse_drop: effect = DROPEFFECT_NONE; /* Refuse drop. */ } - return S_OK; - }; /* DragEnter */ - - STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { - Tcl_Obj *objv[5], *pressedkeys = Tcl_NewListObj(0, NULL), *result; + drop_active = true; + return effect; + }; /* SendDragEnter */ + + DWORD SendDragOver(POINTL pt, DWORD grfKeyState) { + Tcl_Obj *objv[8], *result; int i, status, index; + DWORD effect = DROPEFFECT_NONE; static const char *DropActions[] = { "copy", "move", "link", "ask", "private", "refuse_drop", - "default", + "default", (char *) NULL }; enum dropactions { ActionCopy, ActionMove, ActionLink, ActionAsk, ActionPrivate, refuse_drop, ActionDefault }; - - // Get the state of the keyboard modifier keys. - // MK_CONTROL, MK_SHIFT, MK_ALT, MK_RBUTTON, MK_LBUTTON - if (grfKeyState & MK_CONTROL) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("ctrl", -1)); - if (grfKeyState & MK_SHIFT) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("shift",-1)); - if (grfKeyState & MK_ALT) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("alt", -1)); - if (grfKeyState & MK_RBUTTON) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("3", -1)); - if (grfKeyState & MK_MBUTTON) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("2", -1)); - if (grfKeyState & MK_LBUTTON) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("1", -1)); - - // We are ready to pass the info to the Tcl level, and get the desired - // action. + if (!drop_active) { + return effect; + } objv[0] = Tcl_NewStringObj("tkdnd::olednd::_HandleDragOver", -1); objv[1] = Tcl_NewStringObj(Tk_PathName(tkwin), -1); - objv[2] = pressedkeys; + objv[2] = GetPressedKeys(grfKeyState); objv[3] = Tcl_NewLongObj(pt.x); objv[4] = Tcl_NewLongObj(pt.y); TkDND_Status_Eval(5); - *pdwEffect = DROPEFFECT_NONE; if (status == TCL_OK) { /* Get the returned action... */ result = Tcl_GetObjResult(interp); Tcl_IncrRefCount(result); @@ -661,30 +618,163 @@ class TkDND_DropTarget: public IDropTarget { if (status != TCL_OK) index = ActionDefault; } switch ((enum dropactions) index) { - case ActionCopy: *pdwEffect = DROPEFFECT_COPY; break; - case ActionMove: *pdwEffect = DROPEFFECT_MOVE; break; - case ActionLink: *pdwEffect = DROPEFFECT_LINK; break; - case ActionAsk: *pdwEffect = DROPEFFECT_NONE; break; - case ActionPrivate: *pdwEffect = DROPEFFECT_NONE; break; - case ActionDefault: *pdwEffect = DROPEFFECT_COPY; break; - case refuse_drop: *pdwEffect = DROPEFFECT_NONE; /* Refuse drop. */ + case ActionCopy: effect = DROPEFFECT_COPY; break; + case ActionMove: effect = DROPEFFECT_MOVE; break; + case ActionLink: effect = DROPEFFECT_LINK; break; + case ActionAsk: effect = DROPEFFECT_NONE; break; + case ActionPrivate: effect = DROPEFFECT_NONE; break; + case ActionDefault: effect = DROPEFFECT_COPY; break; + case refuse_drop: effect = DROPEFFECT_NONE; /* Refuse drop. */ + } + return effect; + }; /* SendDragOver */ + + DWORD SendDrop(POINTL pt, DWORD grfKeyState, Tcl_Obj *type, Tcl_Obj *data) { + Tcl_Obj *objv[8], *result; + int i, status, index; + DWORD effect = DROPEFFECT_NONE; + static const char *DropActions[] = { + "copy", "move", "link", "ask", "private", "refuse_drop", + "default", + (char *) NULL + }; + enum dropactions { + ActionCopy, ActionMove, ActionLink, ActionAsk, ActionPrivate, + refuse_drop, ActionDefault + }; + if (!drop_active) { + return effect; + } + drop_active = false; + objv[0] = Tcl_NewStringObj("tkdnd::olednd::_HandleDrop", -1); + objv[1] = Tcl_NewStringObj(Tk_PathName(tkwin), -1); + objv[2] = GetPressedKeys(grfKeyState); + objv[3] = Tcl_NewLongObj(pt.x); + objv[4] = Tcl_NewLongObj(pt.y); + objv[5] = type; + objv[6] = data; + TkDND_Status_Eval(7); + if (status == TCL_OK) { + /* Get the returned action... */ + result = Tcl_GetObjResult(interp); Tcl_IncrRefCount(result); + status = Tcl_GetIndexFromObj(interp, result, (const char **)DropActions, + "dropactions", 0, &index); + Tcl_DecrRefCount(result); + if (status != TCL_OK) index = ActionDefault; + } + switch ((enum dropactions) index) { + case ActionCopy: effect = DROPEFFECT_COPY; break; + case ActionMove: effect = DROPEFFECT_MOVE; break; + case ActionLink: effect = DROPEFFECT_LINK; break; + case ActionAsk: effect = DROPEFFECT_NONE; break; + case ActionPrivate: effect = DROPEFFECT_NONE; break; + case ActionDefault: effect = DROPEFFECT_COPY; break; + case refuse_drop: effect = DROPEFFECT_NONE; /* Refuse drop. */ + } + return effect; + }; /* SendDrop */ + + void SendDragLeave(void) { + Tcl_Obj *objv[2]; + int i; + if (drop_active) { + objv[0] = Tcl_NewStringObj("tkdnd::olednd::_HandleDragLeave", -1); + objv[1] = Tcl_NewStringObj(Tk_PathName(tkwin), -1); + TkDND_Eval(2); + } + drop_active = false; + }; /* SendDragLeave */ + + /* + * IDropTarget interface members. + */ + + STDMETHODIMP DragEnter(IDataObject *pDataObject, DWORD grfKeyState, + POINTL pt, DWORD *pdwEffect) { + // We want to get: + // a) The types supported by the drag source. + // b) The actions supported by the drag source. + // c) The state of the keyboard modifier keys. + // And we must return: + // a) The prefered action. + + IEnumFORMATETC *pEF; + FORMATETC fetc; + char tmp[64]; + Tcl_Obj *element; + + /* + * Windows will send DragOver events even for coordinates that are + * hidden by other windows. So we have to simulate Enter/Leave events, + * thus we must remember the drag source details, like types and + * actions... + */ + if (typelist != NULL) Tcl_DecrRefCount(typelist); + if (actionlist != NULL) Tcl_DecrRefCount(actionlist); + if (codelist != NULL) Tcl_DecrRefCount(codelist); + + typelist = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(typelist); + actionlist = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(actionlist); + codelist = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(codelist); + + /* + * Get the types supported by the drag source. + */ + if (pDataObject->EnumFormatEtc(DATADIR_GET, &pEF) == S_OK) { + while (pEF->Next(1, &fetc, NULL) == S_OK) { + if (pDataObject->QueryGetData(&fetc) == S_OK) { + /* Get the format name from windows */ + element = TCL_NEWSTRING(FormatName(fetc.cfFormat), -1); + Tcl_ListObjAppendElement(NULL, typelist, element); + /* Store the numeric code of the format */ + sprintf(tmp, "0x%08x", fetc.cfFormat); + element = Tcl_NewStringObj(tmp, -1); + Tcl_ListObjAppendElement(NULL, codelist, element); + }; // if (pIDataSource->QueryGetData(&fetc) == S_OK) + }; // while (pEF->Next(1, &fetc, NULL) == S_OK) + }; // if (pIDataSource->EnumFormatEtc(DATADIR_GET, &pEF) == S_OK) + + // Get the actions supported by the drag source. + // DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK + if (*pdwEffect & DROPEFFECT_COPY) + Tcl_ListObjAppendElement(NULL,actionlist,Tcl_NewStringObj("copy", -1)); + if (*pdwEffect & DROPEFFECT_MOVE) + Tcl_ListObjAppendElement(NULL,actionlist,Tcl_NewStringObj("move", -1)); + if (*pdwEffect & DROPEFFECT_LINK) + Tcl_ListObjAppendElement(NULL,actionlist,Tcl_NewStringObj("link", -1)); + + // We are ready to pass the info to the Tcl level, and get the desired + // action. + *pdwEffect = SendDragEnter(pt, grfKeyState); + return S_OK; + }; /* DragEnter */ + + STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { + /* + * This event will be delivered when the mouse is over tkwin. Ensure that + * the part the mouse is in, is not overlapped by another window... + */ + if (Tk_CoordsToWindow(pt.x, pt.y, tkwin) != tkwin) { + SendDragLeave(); + *pdwEffect = DROPEFFECT_NONE; + } else { + SendDragEnter(pt, grfKeyState); + *pdwEffect = SendDragOver(pt, grfKeyState); } return S_OK; }; /* DragOver */ STDMETHODIMP DragLeave(void) { - Tcl_Obj *objv[2]; - int i; - objv[0] = Tcl_NewStringObj("tkdnd::olednd::_HandleDragLeave", -1); - objv[1] = Tcl_NewStringObj(Tk_PathName(tkwin), -1); - TkDND_Eval(2); + SendDragLeave(); + if (typelist != NULL) {Tcl_DecrRefCount(typelist); typelist = NULL;} + if (actionlist != NULL) {Tcl_DecrRefCount(actionlist); actionlist = NULL;} + if (codelist != NULL) {Tcl_DecrRefCount(codelist); codelist = NULL;} return S_OK; }; /* DragLeave */ STDMETHODIMP Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { - Tcl_Obj *objv[7], *result, **typeObj, *data = NULL, *type, - *pressedkeys = NULL; + Tcl_Obj *objv[7], *result, **typeObj, *data = NULL, *type = NULL; int i, type_index, status, index, typeObjc; static const char *DropTypes[] = { "CF_UNICODETEXT", "CF_TEXT", "CF_HDROP", @@ -699,16 +789,9 @@ class TkDND_DropTarget: public IDropTarget { TYPE_CF_RTF, TYPE_CF_RTFTEXT, TYPE_CF_RICHTEXTFORMAT, TYPE_FILEGROUPDESCRIPTORW, TYPE_FILEGROUPDESCRIPTOR }; - static const char *DropActions[] = { - "copy", "move", "link", "ask", "private", "refuse_drop", - "default", - (char *) NULL - }; - enum dropactions { - ActionCopy, ActionMove, ActionLink, ActionAsk, ActionPrivate, - refuse_drop, ActionDefault - }; *pdwEffect = DROPEFFECT_NONE; + if (!drop_active) return S_OK; + drop_active = false; // Get the drop format list. objv[0] = Tcl_NewStringObj("tkdnd::olednd::_GetDropTypes", -1); objv[1] = Tcl_NewStringObj(Tk_PathName(tkwin), -1); @@ -729,7 +812,6 @@ class TkDND_DropTarget: public IDropTarget { case TYPE_CF_RTF: case TYPE_CF_RTFTEXT: case TYPE_CF_RICHTEXTFORMAT: - type = typeObj[type_index]; Tcl_IncrRefCount(type); data = GetData_Bytearray(pDataObject, type); break; case TYPE_CF_TEXT: @@ -769,52 +851,10 @@ class TkDND_DropTarget: public IDropTarget { } Tcl_DecrRefCount(result); - // Get the state of the keyboard modifier keys. - // MK_CONTROL, MK_SHIFT, MK_ALT, MK_RBUTTON, MK_LBUTTON - pressedkeys = Tcl_NewListObj(0, NULL); - if (grfKeyState & MK_CONTROL) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("ctrl", -1)); - if (grfKeyState & MK_SHIFT) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("shift",-1)); - if (grfKeyState & MK_ALT) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("alt", -1)); - if (grfKeyState & MK_RBUTTON) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("3", -1)); - if (grfKeyState & MK_MBUTTON) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("2", -1)); - if (grfKeyState & MK_LBUTTON) - Tcl_ListObjAppendElement(NULL,pressedkeys,Tcl_NewStringObj("1", -1)); - // We are ready to pass the info to the Tcl level, and get the desired // action. - objv[0] = Tcl_NewStringObj("tkdnd::olednd::_HandleDrop", -1); - objv[1] = Tcl_NewStringObj(Tk_PathName(tkwin), -1); - objv[2] = pressedkeys; - objv[3] = Tcl_NewLongObj(pt.x); - objv[4] = Tcl_NewLongObj(pt.y); - objv[5] = type; - objv[6] = data; - TkDND_Status_Eval(7); + *pdwEffect = SendDrop(pt, grfKeyState, type, data); Tcl_DecrRefCount(type); - *pdwEffect = DROPEFFECT_NONE; - if (status == TCL_OK) { - /* Get the returned action... */ - result = Tcl_GetObjResult(interp); Tcl_IncrRefCount(result); - status = Tcl_GetIndexFromObj(interp, result, (const char **)DropActions, - "dropactions", 0, &index); - Tcl_DecrRefCount(result); - if (status != TCL_OK) index = ActionDefault; - } - switch ((enum dropactions) index) { - case ActionCopy: *pdwEffect = DROPEFFECT_COPY; break; - case ActionMove: *pdwEffect = DROPEFFECT_MOVE; break; - case ActionLink: *pdwEffect = DROPEFFECT_LINK; break; - case ActionAsk: *pdwEffect = DROPEFFECT_NONE; break; - case ActionPrivate: *pdwEffect = DROPEFFECT_NONE; break; - case ActionDefault: *pdwEffect = DROPEFFECT_COPY; break; - case refuse_drop: *pdwEffect = DROPEFFECT_NONE; /* Refuse drop. */ - } - return S_OK; }; /* Drop */ |