//--------------------------------------------------------------------------- // // 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; }