I am implementing drag&drop support for LXPM, the editor that comes with
VAC 3. One the one hand, I am supporting the drop of files that can
directly be handled by the drop handler (without going through the
DM_RENDER/DM_RENDERCOMPLETE cycle). On the other hand, I am supporting
the drop of marked text fragments from EPM which have to go through the
rendering process (DM_RENDER / DM_RENDERCOMPLETE).
I have mostly followed this:
http://groups.google.de/group/comp.os.os2.programmer.misc/browse_thread/thread/b2304feeeea36a8d/9641d096b9921c93?lnk=gst&q=DM_RENDER&rnum=7#9641d096b9921c93
In principle, everything works just fine. In particular, dropping files
(from WPS or elsewhere where <DRM_OS2FILE,DRF_TEXT> is supported and I
have a valid container and source name) is not a problem at all.
However, for text fragments dropped from EPM, whenever the
DM_RENDERCOMPLETE handler is entered, the calls to:
DrgQueryDraginfoPtr
DrgQueryDraginfoPtrFromDragitem
DrgQueryDraginfoPtrFromHwnd
all return a NULL pointer which makes it impossible to query and
therefore properly free the Draginfo structure. This eventually would
lead to a crash.
Then I implemented the really ugly hack as described in:
http://groups.google.de/group/comp.os.os2.programmer.misc/browse_thread/thread/9684e240d3792f1e/d17414e8b0f167a7?hl=de&q=In+DM_DROP,+do+I#d17414e8b0f167a7
in order to compute the Draginfo pointer directly from the Dragitem pointer:
PDRAGINFO pDragInfo=NULL;
PDRAGITEM pDragItem=NULL;
pDragItem = pDragTransfer->pditem;
pDragInfo = (PDRAGINFO)(((ULONG)pDragItem & 0xFFFF0000) + 0x2C);
That indeed gives me a valid pointer that I can pass to DrgFreeDragInfo
and DrgDeleteDraginfoStrHandles without any problems.
Questions:
1.) do I really have to call DrgFreeDragInfo from within DM_DRAGOVER ?
What's the logic between
DrgAllocDraginfo,DrgAccessDraginfo,DrgFreeDraginfo ? I am not complete
sure about the "matching" logic ...
2.) if DM_RENDER fails (returns error to drop target) , will
DM_RENDERCOMPLETE be invoked by the drag source anyway ? In other words:
can I do the clean up for every Dragitem that has undergone the
rendering process in DM_RENDERCOMPLETE or do I have to do the cleanup in
DM_DROP if rendering fails (as I did in sample code below) ?
3.) is the DragInfo offset of 0x002C an offset into a 64-kByte segment
(as above) or only into a 4-kByte page like so:
pDragInfo = (PDRAGINFO)(((ULONG)pDragItem & 0xFFFFF000) + 0x2C); Is
there a potential possibility that multiple DragInfo structures are
allocated by one process so that this hack could potentially fail ?
4.) Why do DragQueryDragitem... fail ? What could be the reason ?
Thanks for any hint,
Lars
Here is the current code (relevant drag&drop part):
MRESULT DragOverHandler(HWND hwnd,PDRAGINFO pDragInfo)
{
PDRAGITEM pDragItem;
ULONG i,ulItems;
USHORT usIndicator=DOR_NEVERDROP,usOp=0;
if (DrgAccessDraginfo(pDragInfo))
{
usIndicator = DOR_DROP;
usOp = DO_MOVE;
if (pDragInfo->usOperation != DO_DEFAULT)
{
usIndicator = DOR_NODROPOP;
usOp = 0;
}
else
{
ulItems = DrgQueryDragitemCount(pDragInfo);
for (i=0;i<ulItems;i++)
{
pDragItem = DrgQueryDragitemPtr(pDragInfo,i);
#ifdef DEBUG
DrgQueryStrName(pDragItem->hstrType,sizeof(testBuf1),testBuf1);
DrgQueryStrName(pDragItem->hstrRMF,sizeof(testBuf2),testBuf2);
DrgQueryStrName(pDragItem->hstrContainerName,sizeof(testBuf3),testBuf3);
DrgQueryStrName(pDragItem->hstrSourceName,sizeof(testBuf4),testBuf4);
DrgQueryStrName(pDragItem->hstrTargetName,sizeof(testBuf5),testBuf5);
#endif
if (!pDragItem->hstrContainerName ||
!DrgVerifyRMF(pDragItem,"DRM_OS2FILE","DRF_TEXT")
)
{
usIndicator = DOR_NEVERDROP;
usOp = 0;
break;
}
}
}
DrgFreeDraginfo(pDragInfo);
}
return MRFROM2SHORT(usIndicator,usOp);
}
MRESULT DropHandler(HWND hwnd,PDRAGINFO pDragInfo)
{
PDRAGTRANSFER pDragTransfer,pTemp;
PDRAGITEM pDragItem;
ULONG ulLen;
ULONG i,j,ulItems,ulItemsDelayed;
int ret;
if (DrgAccessDraginfo(pDragInfo))
{
ulItemsDelayed = 0;
ulItems = DrgQueryDragitemCount(pDragInfo);
for (i=0;i<ulItems;i++)
{
pDragItem = DrgQueryDragitemPtr(pDragInfo,i);
if (pDragItem->hstrSourceName)
{
memset(pCmdString,0,CCHMAXPATH);
pCmdString[0]='"';
ulLen =
DrgQueryStrName(pDragItem->hstrContainerName,CCHMAXPATH,pCmdString+1);
ulLen = ulLen +
DrgQueryStrName(pDragItem->hstrSourceName,CCHMAXPATH-ulLen,pCmdString+ulLen+1);
pCmdString[ulLen+1]='"';
if (strlen(pParamString))
{
strcat(pCmdString," ");
strcat(pCmdString,pParamString);
}
ret = lxcall("lx",pCmdString);
DrgSendTransferMsg(pDragItem->hwndItem,
DM_ENDCONVERSATION,
MPFROMP(pDragItem->ulItemID),
ret >= 0 ?
MPFROMLONG(DMFL_TARGETSUCCESSFUL):MPFROMLONG(DMFL_TARGETFAIL));
}
else
{
ulItemsDelayed++;
}
}
if (!ulItemsDelayed || (pDragTransfer =
DrgAllocDragtransfer(ulItemsDelayed)) == NULL)
{
DrgDeleteDraginfoStrHandles(pDragInfo);
DrgFreeDraginfo(pDragInfo);
}
else
{
for (i=0,j=ulItemsDelayed,pTemp = pDragTransfer;i<ulItems;i++,j--)
{
pDragItem = DrgQueryDragitemPtr(pDragInfo,i);
if (!pDragItem->hstrSourceName)
{
pTemp->cb = sizeof(DRAGTRANSFER);
pTemp->hwndClient = hwnd;
pTemp->pditem = pDragItem;
pTemp->hstrSelectedRMF =
DrgAddStrHandle("<DRM_OS2FILE,DRF_TEXT>");
pTemp->hstrRenderToName = 0;
pTemp->ulTargetInfo = j;
pTemp->fsReply = 0;
pTemp->usOperation = DO_DEFAULT;
if (pDragItem->fsControl & DC_PREPAREITEM)
{
DrgSendTransferMsg(pDragItem->hwndItem,DM_RENDERPREPARE,(MPARAM)pTemp,0);
}
memset(pCmdString,0,CCHMAXPATH);
ulLen =
DrgQueryStrName(pDragItem->hstrContainerName,CCHMAXPATH,pCmdString);
ulLen = ulLen +
DrgQueryStrName(pDragItem->hstrTargetName,CCHMAXPATH-ulLen,pCmdString+ulLen);
pTemp->hstrRenderToName = DrgAddStrHandle(pCmdString);
if ((pDragItem->fsControl & (DC_PREPARE|DC_PREPAREITEM))
== DC_PREPARE)
{
DrgSendTransferMsg(pDragItem->hwndItem,DM_RENDERPREPARE,(MPARAM)pTemp,0);
}
if
(!DrgSendTransferMsg(pDragItem->hwndItem,DM_RENDER,(MPARAM)pTemp,0))
{
DrgSendTransferMsg(pDragItem->hwndItem,DM_ENDCONVERSATION,(MPARAM)pDragItem->ulItemID,(MPARAM)DMFL_TARGETFAIL);
DrgDeleteStrHandle(pTemp->hstrSelectedRMF);
DrgDeleteStrHandle(pTemp->hstrRenderToName);
DrgFreeDragtransfer(pTemp);
}
else
{
pTemp++;
}
}
}
if (pTemp == pDragTransfer)
{
DrgDeleteDraginfoStrHandles(pDragInfo);
DrgFreeDraginfo(pDragInfo);
}
}
}
return MRFROMLONG(0);
}
MRESULT RenderCompleteHandler(PDRAGTRANSFER pDragTransfer,USHORT usResults)
{
PDRAGINFO pDragInfo=NULL;
PDRAGITEM pDragItem=NULL;
ULONG ulLen,ulTargetInfo;
int ret;
pDragItem = pDragTransfer->pditem;
pDragInfo = (PDRAGINFO)(((ULONG)pDragItem & 0xFFFF0000) + 0x2C);
ulTargetInfo = pDragTransfer->ulTargetInfo;
if (usResults & DMFL_RENDEROK)
{
memset(pCmdString,0,CCHMAXPATH);
pCmdString[0]='"';
ulLen =
DrgQueryStrName(pDragTransfer->hstrRenderToName,sizeof(pCmdString),pCmdString+1);
pCmdString[ulLen+1]='"';
ret = lxcall("get",pCmdString);
lxcmd("LP_SELALL");
memmove(pCmdString,pCmdString+1,ulLen);
pCmdString[ulLen]='\0';
DosForceDelete(pCmdString);
}
DrgSendTransferMsg(pDragItem->hwndItem,
DM_ENDCONVERSATION,
(MPARAM)pDragItem->ulItemID,
ret >= 0 ? (MPARAM)DMFL_TARGETSUCCESSFUL:
(MPARAM)DMFL_TARGETFAIL);
DrgDeleteStrHandle(pDragTransfer->hstrSelectedRMF);
DrgDeleteStrHandle(pDragTransfer->hstrRenderToName);
DrgFreeDragtransfer(pDragTransfer);
if (ulTargetInfo == 1)
{
#ifdef DEBUG
DosBeep(400,100);
#endif
DrgDeleteDraginfoStrHandles(pDragInfo);
DrgFreeDraginfo(pDragInfo);
}
return (MRESULT)0;
}
MRESULT ShowHelp(HWND hwnd,PDRAGINFO pDragInfo)
{
MB2EXTINFO mb ={0};
mb.cb = sizeof(mb);
mb.cButtons = 2;
mb.flStyle = MB_INFORMATION | MB_NONMODAL | MB_MOVEABLE;
mb.hwndNotify = hwnd;
strcpy(mb.mb2d[0].achText,"Cancel");
mb.mb2d[0].idButton = 10;
mb.mb2d[0].flStyle = BS_PUSHBUTTON;
strcpy(mb.mb2d[1].achText,"Help");
mb.mb2d[1].idButton = 20;
mb.mb2d[1].flStyle = BS_PUSHBUTTON;
if (DrgAccessDraginfo(pDragInfo))
{
WinMessageBox2(HWND_DESKTOP,
NULLHANDLE,
"DRGDRP can take the following parameters when
invoked through the LPEX command execution line:\n"\
"/settings\n"\
"\tto query the current settings\n"\
"/nopro,/np\n"\
"/nosys,/ns\n"\
"/asis,/as\n"\
"/doctype DocType,/dt DocType\n"\
"\tsame meaning as parameters to \"lx\" command\n"\
"\tpress \"Help\" to see Editor Reference\n\n"\
"Note: DRGDRP will only correctly handle
parameters\n"\
"if it is initially loaded through the
\"profinit.lx\" profile !",
"Info",
0,
(PMB2INFO)&mb);
DrgDeleteDraginfoStrHandles(pDragInfo);
DrgFreeDraginfo(pDragInfo);
}
return MPFROMLONG(0);
}
VOID InvokeHelp(HWND hwnd)
{
PROGDETAILS d={0};
d.Length = sizeof(d);
d.pszExecutable = "VIEW.EXE";
WinStartApp(hwnd,&d,"LPXCREF.INF \"lx command\"",NULL,0UL);
return;
}
MRESULT EXPENTRY ClientProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2)
{
switch(msg)
{
case DM_DRAGOVER:
return DragOverHandler(hwnd,(PDRAGINFO)MPFROMP(mp1));
case DM_DROP:
return DropHandler(hwnd,(PDRAGINFO)MPFROMP(mp1));
case DM_DROPHELP:
return ShowHelp(hwnd,(PDRAGINFO)MPFROMP(mp1));
case DM_RENDERCOMPLETE:
return
RenderCompleteHandler((PDRAGTRANSFER)MPFROMP(mp1),SHORT1FROMMP(mp2));
case WM_MSGBOXDISMISS:
{
BOOL rc = FALSE;
HWND hwndMsgBox=HWNDFROMMP(mp1);
if (LONGFROMMP(mp2) == 20)
{
InvokeHelp(hwnd);
}
rc = WinDestroyWindow(hwndMsgBox);
return (MRESULT)0;
}
default:
break;
}
return gOldClientProc(hwnd,msg,mp1,mp2);
}