mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
506 lines
16 KiB
506 lines
16 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1991-1993
|
|
//
|
|
// File: idldrop.c
|
|
//
|
|
// History:
|
|
// 12-16-93 SatoNa Created.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#include "iid.h"
|
|
|
|
TCHAR const c_szDDHandler[] = STRREG_SHEX_DDHANDLER;
|
|
|
|
extern void WINAPI IDLData_InitializeClipboardFormats(void);
|
|
|
|
// Stub needed for C++ code
|
|
BOOL IsIDLDropTarget(IDropTarget *pdtgt)
|
|
{
|
|
return(ISIDLDROPTARGET(pdtgt));
|
|
}
|
|
|
|
//===========================================================================
|
|
// CIDLDropTarget: Vtable (sample)
|
|
//===========================================================================
|
|
#ifdef DEBUG // This is just a type checking
|
|
const IDropTargetVtbl c_IDLDropTargetVtbl =
|
|
{
|
|
CIDLDropTarget_QueryInterface,
|
|
CIDLDropTarget_AddRef,
|
|
CIDLDropTarget_Release,
|
|
CIDLDropTarget_DragEnter,
|
|
CIDLDropTarget_DragOver,
|
|
CIDLDropTarget_DragLeave,
|
|
CIDLDropTarget_Drop,
|
|
};
|
|
#endif // DEBUG
|
|
|
|
//===========================================================================
|
|
// CIDLDropTarget: constructors
|
|
//===========================================================================
|
|
|
|
HRESULT CIDLDropTarget_Create(HWND hwndOwner, const IDropTargetVtbl *lpVtbl,
|
|
LPCITEMIDLIST pidl, LPDROPTARGET *ppdropt)
|
|
{
|
|
LPIDLDROPTARGET pidldt = (void*)LocalAlloc(LPTR, SIZEOF(CIDLDropTarget));
|
|
if (pidldt)
|
|
{
|
|
pidldt->pidl = ILClone(pidl);
|
|
if (pidldt->pidl)
|
|
{
|
|
pidldt->dropt.lpVtbl = lpVtbl;
|
|
pidldt->cRef = 1;
|
|
pidldt->hwndOwner = hwndOwner;
|
|
|
|
Assert(pidldt->pdtgAgr == NULL);
|
|
|
|
*ppdropt = &pidldt->dropt;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
LocalFree((HLOCAL)pidldt);
|
|
}
|
|
*ppdropt = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//===========================================================================
|
|
// CIDLDropTarget: member function
|
|
//===========================================================================
|
|
|
|
STDMETHODIMP CIDLDropTarget_QueryInterface(LPDROPTARGET pdropt, REFIID riid, LPVOID *ppvObj)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDropTarget))
|
|
{
|
|
*ppvObj = pdropt;
|
|
pdropt->lpVtbl->AddRef(pdropt);
|
|
return S_OK;
|
|
}
|
|
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CIDLDropTarget_AddRef(LPDROPTARGET pdropt)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
this->cRef++;
|
|
return this->cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CIDLDropTarget_Release(LPDROPTARGET pdropt)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
|
|
this->cRef--;
|
|
if (this->cRef > 0)
|
|
return this->cRef;
|
|
|
|
if (this->pdtgAgr)
|
|
this->pdtgAgr->lpVtbl->Release(this->pdtgAgr);
|
|
|
|
if (this->pidl)
|
|
ILFree(this->pidl);
|
|
|
|
// if we hit this a lot maybe we should just release it
|
|
AssertMsg(this->pdtobj == NULL, TEXT("didn't get matching DragLeave"));
|
|
|
|
LocalFree((HLOCAL)this);
|
|
return 0;
|
|
}
|
|
|
|
extern void WINAPI IDLData_InitializeClipboardFormats(void);
|
|
|
|
STDMETHODIMP CIDLDropTarget_DragEnter(LPDROPTARGET pdropt, LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
|
|
Assert(this->pdtobj == NULL); // avoid a leak
|
|
|
|
// init our registerd data formats
|
|
IDLData_InitializeClipboardFormats();
|
|
|
|
this->grfKeyStateLast = grfKeyState;
|
|
this->pdtobj = pDataObj;
|
|
this->dwData = 0;
|
|
|
|
if (pDataObj)
|
|
{
|
|
LPENUMFORMATETC penum;
|
|
FORMATETC fmte;
|
|
HRESULT hres;
|
|
pDataObj->lpVtbl->AddRef(pDataObj);
|
|
hres = pDataObj->lpVtbl->EnumFormatEtc(pDataObj, DATADIR_GET, &penum);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
LONG celt;
|
|
while (penum->lpVtbl->Next(penum,1,&fmte,&celt)==S_OK)
|
|
{
|
|
if (fmte.cfFormat==CF_HDROP && (fmte.tymed&TYMED_HGLOBAL)) {
|
|
this->dwData |= DTID_HDROP;
|
|
}
|
|
if (fmte.cfFormat==g_cfHIDA && (fmte.tymed&TYMED_HGLOBAL)) {
|
|
this->dwData |= DTID_HIDA;
|
|
}
|
|
if (fmte.cfFormat==g_cfNetResource && (fmte.tymed&TYMED_HGLOBAL)) {
|
|
this->dwData |= DTID_NETRES;
|
|
}
|
|
if (fmte.cfFormat==g_cfFileContents && (fmte.tymed&(TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE))) {
|
|
this->dwData |= DTID_CONTENTS;
|
|
}
|
|
|
|
if (fmte.cfFormat==g_cfFileGroupDescriptorA && (fmte.tymed&TYMED_HGLOBAL)) {
|
|
this->dwData |= DTID_FDESCA;
|
|
}
|
|
|
|
if (fmte.cfFormat==g_cfFileGroupDescriptorW && (fmte.tymed&TYMED_HGLOBAL)) {
|
|
this->dwData |= DTID_FDESCW;
|
|
}
|
|
|
|
if (fmte.cfFormat==g_cfPreferredDropEffect &&
|
|
(fmte.tymed&TYMED_HGLOBAL) && ((this->dwEffectPreferred =
|
|
DataObj_GetPreferredEffect(pDataObj, 0)) != 0))
|
|
{
|
|
this->dwData |= DTID_PREFERREDEFFECT;
|
|
}
|
|
}
|
|
penum->lpVtbl->Release(penum);
|
|
}
|
|
|
|
//
|
|
// HACK:
|
|
// Win95 always did the GetData below which can be quite expensive if
|
|
// the data is a directory structure on an ftp server etc.
|
|
// dont check for FD_LINKUI if the data object has a preferred effect
|
|
//
|
|
if ((this->dwData & (DTID_PREFERREDEFFECT | DTID_CONTENTS)) == DTID_CONTENTS)
|
|
{
|
|
if (this->dwData & DTID_FDESCA)
|
|
{
|
|
FORMATETC fmteRead = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
if (pDataObj->lpVtbl->GetData(pDataObj, &fmteRead, &medium)==S_OK)
|
|
{
|
|
FILEGROUPDESCRIPTORA * pfgd = (FILEGROUPDESCRIPTORA *)GlobalLock(medium.hGlobal);
|
|
if (pfgd)
|
|
{
|
|
if (pfgd->cItems >= 1)
|
|
{
|
|
if (pfgd->fgd[0].dwFlags & FD_LINKUI) {
|
|
this->dwData |= DTID_FD_LINKUI;
|
|
}
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
SHReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
else if (this->dwData & DTID_FDESCW)
|
|
{
|
|
FORMATETC fmteRead = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium;
|
|
if (pDataObj->lpVtbl->GetData(pDataObj, &fmteRead, &medium)==S_OK)
|
|
{
|
|
FILEGROUPDESCRIPTORW * pfgd = (FILEGROUPDESCRIPTORW *)GlobalLock(medium.hGlobal);
|
|
if (pfgd)
|
|
{
|
|
if (pfgd->cItems >= 1)
|
|
{
|
|
if (pfgd->fgd[0].dwFlags & FD_LINKUI) {
|
|
this->dwData |= DTID_FD_LINKUI;
|
|
}
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
SHReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SHXOleQueryCreateFromData(pDataObj) == S_OK) {
|
|
this->dwData |= DTID_OLEOBJ;
|
|
}
|
|
if (SHXOleQueryLinkFromData(pDataObj) == S_OK) {
|
|
this->dwData |= DTID_OLELINK;
|
|
}
|
|
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - CIDL::DragEnter this->dwData = %x"), this->dwData);
|
|
}
|
|
|
|
// stash this away
|
|
if (pdwEffect)
|
|
this->dwEffectLastReturned = *pdwEffect;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// subclasses can prevetn us from assigning in the dwEffect by not passing in pdwEffect
|
|
STDMETHODIMP CIDLDropTarget_DragOver(LPDROPTARGET pdropt, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
this->grfKeyStateLast = grfKeyState;
|
|
if (pdwEffect)
|
|
*pdwEffect = this->dwEffectLastReturned;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CIDLDropTarget_DragLeave(LPDROPTARGET pdropt)
|
|
{
|
|
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdropt);
|
|
if (this->pdtobj)
|
|
{
|
|
DebugMsg(DM_TRACE, TEXT("sh TR - CIDL::DragLeave called when pdtobj!=NULL (%x)"), this->pdtobj);
|
|
this->pdtobj->lpVtbl->Release(this->pdtobj);
|
|
this->pdtobj = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
STDMETHODIMP CIDLDropTarget_Drop(LPDROPTARGET pdropt, LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// BUGBUG: The TrackPopupMenu does not work, if the hwnd does not have
|
|
// the input focus. We believe this is a bug in USER, but ...
|
|
//
|
|
BOOL _TrackPopupMenu(HMENU hmenu, UINT wFlags, int x, int y,
|
|
int wReserved, HWND hwndOwner, LPCRECT lprc)
|
|
{
|
|
int iRet = FALSE;
|
|
HWND hwndDummy = CreateWindow(c_szStatic, NULL,
|
|
0, x, y, 1, 1, HWND_DESKTOP,
|
|
NULL, HINST_THISDLL, NULL);
|
|
if (hwndDummy)
|
|
{
|
|
HWND hwndPrev = GetForegroundWindow(); // to restore
|
|
|
|
SetForegroundWindow(hwndDummy);
|
|
SetFocus(hwndDummy);
|
|
iRet = TrackPopupMenu(hmenu, wFlags, x, y, wReserved, hwndDummy, lprc);
|
|
|
|
//
|
|
// We MUST unlock the destination window before changing its Z-order.
|
|
//
|
|
CDefView_UnlockWindow();
|
|
|
|
if (iRet)
|
|
{
|
|
// non-cancel item is selected. Make the hwndOwner foreground.
|
|
SetForegroundWindow(hwndOwner);
|
|
SetFocus(hwndOwner);
|
|
}
|
|
else
|
|
{
|
|
// The user canceled the menu.
|
|
// Restore the previous foreground window (before destroying hwndDummy).
|
|
if (hwndPrev) {
|
|
SetForegroundWindow(hwndPrev);
|
|
}
|
|
}
|
|
|
|
DestroyWindow(hwndDummy);
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
struct {
|
|
UINT uID;
|
|
DWORD dwEffect;
|
|
} const c_IDEffects[] = {
|
|
DDIDM_COPY, DROPEFFECT_COPY,
|
|
DDIDM_MOVE, DROPEFFECT_MOVE,
|
|
DDIDM_LINK, DROPEFFECT_LINK,
|
|
DDIDM_SCRAP_COPY, DROPEFFECT_COPY,
|
|
DDIDM_SCRAP_MOVE, DROPEFFECT_MOVE,
|
|
DDIDM_DOCLINK, DROPEFFECT_LINK,
|
|
DDIDM_CONTENTS_COPY, DROPEFFECT_COPY,
|
|
DDIDM_CONTENTS_MOVE, DROPEFFECT_MOVE,
|
|
DDIDM_CONTENTS_LINK, DROPEFFECT_LINK,
|
|
DDIDM_SYNCCOPYTYPE, DROPEFFECT_COPY, // (order is important)
|
|
DDIDM_SYNCCOPY, DROPEFFECT_COPY,
|
|
};
|
|
|
|
//
|
|
// Pops up the "Copy, Link, Move" context menu, so that the user can
|
|
// choose one of them.
|
|
//
|
|
// in:
|
|
// pdwEffect drop effects allowed
|
|
// dwDefaultEffect default drop effect
|
|
// hkeyBase/hkeyProgID extension hkeys
|
|
// hmenuReplace replaces POPUP_NONDEFAULTDD. Can only contain:
|
|
// DDIDM_MOVE, DDIDM_COPY, DDIDM_LINK menu ids
|
|
// pt in screen
|
|
// Returns:
|
|
// S_OK -- Menu item is processed by one of extensions or canceled
|
|
// S_FALSE -- Menu item is selected
|
|
//
|
|
HRESULT CIDLDropTarget_DragDropMenu(LPIDLDROPTARGET this,
|
|
DWORD dwDefaultEffect,
|
|
IDataObject *pdtobj,
|
|
POINTL pt, DWORD *pdwEffect,
|
|
HKEY hkeyProgID, HKEY hkeyBase,
|
|
UINT idMenu, DWORD grfKeyState)
|
|
{
|
|
DRAGDROPMENUPARAM ddm = { dwDefaultEffect, pdtobj, { pt.x, pt.y},
|
|
pdwEffect,
|
|
hkeyProgID, hkeyBase, idMenu, 0, grfKeyState };
|
|
return CIDLDropTarget_DragDropMenuEx(this, &ddm);
|
|
}
|
|
|
|
HRESULT CIDLDropTarget_DragDropMenuEx(LPIDLDROPTARGET this,
|
|
LPDRAGDROPMENUPARAM pddm)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY; // assume error
|
|
DWORD dwEffectOut = 0; // assume no-ope.
|
|
HMENU hmenu = _LoadPopupMenu(pddm->idMenu);
|
|
if (hmenu)
|
|
{
|
|
int nItem;
|
|
UINT idCmd;
|
|
UINT idCmdFirst = DDIDM_EXTFIRST;
|
|
HDXA hdxa = HDXA_Create();
|
|
HDCA hdca = DCA_Create();
|
|
if (hdxa && hdca)
|
|
{
|
|
//
|
|
// Add extended menu for "Base" class.
|
|
//
|
|
if (pddm->hkeyBase && pddm->hkeyBase != pddm->hkeyProgID)
|
|
DCA_AddItemsFromKey(hdca, pddm->hkeyBase, c_szDDHandler);
|
|
|
|
//
|
|
// Enumerate the DD handlers and let them append menu items.
|
|
//
|
|
if (pddm->hkeyProgID)
|
|
DCA_AddItemsFromKey(hdca, pddm->hkeyProgID, c_szDDHandler);
|
|
|
|
idCmdFirst = HDXA_AppendMenuItems(hdxa, pddm->pdtobj, 1,
|
|
&pddm->hkeyProgID, this->pidl, hmenu, 0,
|
|
DDIDM_EXTFIRST, DDIDM_EXTLAST, 0, hdca);
|
|
}
|
|
|
|
// eliminate menu options that are not allowed by dwEffect
|
|
|
|
for (nItem = 0; nItem < ARRAYSIZE(c_IDEffects); ++nItem)
|
|
{
|
|
if (GetMenuState(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND)!=(UINT)-1)
|
|
{
|
|
if (!(c_IDEffects[nItem].dwEffect & *(pddm->pdwEffect)))
|
|
{
|
|
RemoveMenu(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND);
|
|
}
|
|
else if (c_IDEffects[nItem].dwEffect == pddm->dwDefEffect)
|
|
{
|
|
SetMenuDefaultItem(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this dragging is caused by the left button, simply choose
|
|
// the default one, otherwise, pop up the context menu. If there
|
|
// is no key state info and the original effect is the same as the
|
|
// current effect, choose the default one, otherwise pop up the
|
|
// context menu. If the CTRL and SHIFT keys are being held down
|
|
// and this isn't a fake "pasted-link" option, then pop up
|
|
// the context menu, otherwise choose the default.
|
|
//
|
|
if (((this->grfKeyStateLast & MK_LBUTTON) ||
|
|
(!this->grfKeyStateLast && (*(pddm->pdwEffect) == pddm->dwDefEffect)))
|
|
&& !( (pddm->grfKeyState & MK_SHIFT) && (pddm->grfKeyState & MK_CONTROL) && !(pddm->grfKeyState & MK_FAKEDROP)))
|
|
{
|
|
idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
|
|
|
|
//
|
|
// This one MUST be called here. Please read its comment block.
|
|
//
|
|
CDefView_UnlockWindow();
|
|
|
|
if (this->hwndOwner)
|
|
SetForegroundWindow(this->hwndOwner);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Note that _TrackPopupMenu calls CDefView_UnlockWindow().
|
|
//
|
|
idCmd = _TrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
|
|
pddm->pt.x, pddm->pt.y, 0, this->hwndOwner, NULL);
|
|
}
|
|
|
|
//
|
|
// We also need to call this here to release the dragged image.
|
|
//
|
|
DAD_SetDragImage(NULL, NULL);
|
|
|
|
//
|
|
// Check if the user selected one of add-in menu items.
|
|
//
|
|
if (idCmd == 0)
|
|
{
|
|
hres = S_OK; // Canceled by the user, return S_OK
|
|
}
|
|
else if (InRange(idCmd, DDIDM_EXTFIRST, DDIDM_EXTLAST))
|
|
{
|
|
//
|
|
// Yes. Let the context menu handler process it.
|
|
//
|
|
CMINVOKECOMMANDINFOEX ici = {
|
|
SIZEOF(CMINVOKECOMMANDINFOEX),
|
|
0L,
|
|
this->hwndOwner,
|
|
(LPSTR)MAKEINTRESOURCE(idCmd - DDIDM_EXTFIRST),
|
|
NULL, NULL,
|
|
SW_NORMAL,
|
|
};
|
|
|
|
HDXA_LetHandlerProcessCommand(hdxa, &ici);
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
for (nItem = 0; nItem < ARRAYSIZE(c_IDEffects); ++nItem)
|
|
{
|
|
if (idCmd == c_IDEffects[nItem].uID)
|
|
{
|
|
dwEffectOut = c_IDEffects[nItem].dwEffect;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if hmenuReplace had menu commands other than DDIDM_COPY,
|
|
// DDIDM_MOVE, DDIDM_LINK, and that item was selected,
|
|
// this assert will catch it. (dwEffectOut is 0 in this case)
|
|
Assert(nItem < ARRAYSIZE(c_IDEffects));
|
|
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
if (hdca)
|
|
DCA_Destroy(hdca);
|
|
|
|
if (hdxa)
|
|
HDXA_Destroy(hdxa);
|
|
|
|
DestroyMenu(hmenu);
|
|
pddm->idCmd = idCmd;
|
|
}
|
|
|
|
*(pddm->pdwEffect) = dwEffectOut;
|
|
|
|
return hres;
|
|
}
|