// ## MenuContainer.h // Classic Shell (c) 2009-2017, Ivo Beltchev // Open-Shell (c) 2017-2018, The Open-Shell Team // Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author // DragDrop.cpp - handles the drag and drop functionality of CMenuContainer #include "stdafx.h" #include "MenuContainer.h" #include "StartMenuDLL.h" #include "SettingsUI.h" #include "FNVHash.h" #include "Settings.h" #include "ResourceHelper.h" #include "Translations.h" #include "FileHelper.h" #include class CMetroDataObject: public IDataObject { public: CMetroDataObject( const wchar_t *path ); ~CMetroDataObject( void ); // from IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void **ppvObject ); virtual ULONG STDMETHODCALLTYPE AddRef( void ) { return ++m_RefCount; } virtual ULONG STDMETHODCALLTYPE Release( void ) { if (m_RefCount==1) { delete this; return 0; } return --m_RefCount; } // from IDataObject virtual HRESULT STDMETHODCALLTYPE GetData( FORMATETC *pformatetcIn, STGMEDIUM *pmedium ); virtual HRESULT STDMETHODCALLTYPE SetData( FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease ) { return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE GetDataHere( FORMATETC *pformatetc, STGMEDIUM *pmedium ) { return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE QueryGetData( FORMATETC *pformatetc ); virtual HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc( FORMATETC *pformatectIn, FORMATETC *pformatetcOut ); virtual HRESULT STDMETHODCALLTYPE EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc ); virtual HRESULT STDMETHODCALLTYPE DAdvise( FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection ) { return OLE_E_ADVISENOTSUPPORTED; } virtual HRESULT STDMETHODCALLTYPE DUnadvise( DWORD dwConnection ) { return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE EnumDAdvise( IEnumSTATDATA **ppenumAdvise ) { return OLE_E_ADVISENOTSUPPORTED; } private: int m_RefCount; std::map m_Data; bool m_bContents; void SetData( CLIPFORMAT format, HGLOBAL data ); }; static CLIPFORMAT g_PreferredEffectFormat; CMetroDataObject::CMetroDataObject( const wchar_t *path ) { m_RefCount=0; m_bContents=false; SetData(CMenuContainer::s_MetroLinkFormat,NULL); if (!path) return; HANDLE file=CreateFile(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if (file!=INVALID_HANDLE_VALUE) { DWORD size=GetFileSize(file,NULL); if (size>0) { HGLOBAL hContents=NULL, hDesc=NULL; hContents=GlobalAlloc(GMEM_MOVEABLE,size); if (hContents) hDesc=GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,sizeof(FILEGROUPDESCRIPTOR)); if (hDesc) { FILEGROUPDESCRIPTOR *desc=(FILEGROUPDESCRIPTOR*)GlobalLock(hDesc); desc->cItems=1; desc->fgd->dwFlags=FD_ATTRIBUTES|FD_FILESIZE|FD_LINKUI|FD_UNICODE; desc->fgd->dwFileAttributes=FILE_ATTRIBUTE_NORMAL; desc->fgd->nFileSizeLow=size; Strcpy(desc->fgd->cFileName,_countof(desc->fgd->cFileName),PathFindFileName(path)); GlobalUnlock(hDesc); SetData(CMenuContainer::s_DescriptorFormat,hDesc); hDesc=NULL; DWORD q; if (!ReadFile(file,GlobalLock(hContents),size,&q,NULL)) q=0; GlobalUnlock(hContents); if (q==size) { SetData(CMenuContainer::s_ContentsFormat,hContents); hContents=NULL; m_bContents=true; } } if (hContents) GlobalFree(hContents); if (hDesc) GlobalFree(hDesc); } CloseHandle(file); } } CMetroDataObject::~CMetroDataObject( void ) { for (std::map::iterator it=m_Data.begin();it!=m_Data.end();++it) if (it->second) GlobalFree(it->second); } HRESULT STDMETHODCALLTYPE CMetroDataObject::QueryInterface( REFIID riid, void **ppvObject ) { if (riid==IID_IDataObject || riid==IID_IUnknown) { AddRef(); *ppvObject=this; return S_OK; } *ppvObject=NULL; return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE CMetroDataObject::GetData( FORMATETC *pformatetcIn, STGMEDIUM *pmedium ) { if (!pformatetcIn || !pmedium) return E_INVALIDARG; pmedium->hGlobal=NULL; pmedium->pUnkForRelease=NULL; if (pformatetcIn->dwAspect!=DVASPECT_CONTENT) return DV_E_DVASPECT; if (!(pformatetcIn->tymed&TYMED_HGLOBAL)) return DV_E_TYMED; std::map::iterator it=m_Data.find(pformatetcIn->cfFormat); if (it==m_Data.end()) return DV_E_FORMATETC; wchar_t name[100]; GetClipboardFormatName(pformatetcIn->cfFormat,name,100); Trace(L"GetData: %s, %d",name,pformatetcIn->cfFormat); pmedium->tymed=TYMED_HGLOBAL; SIZE_T size=GlobalSize(it->second); pmedium->hGlobal=GlobalAlloc(GMEM_MOVEABLE,size); if (!pmedium->hGlobal) return E_OUTOFMEMORY; void *src=GlobalLock(it->second); void *dst=GlobalLock(pmedium->hGlobal); memcpy(dst,src,size); GlobalUnlock(pmedium->hGlobal); GlobalUnlock(it->second); return S_OK; } void CMetroDataObject::SetData( CLIPFORMAT format, HGLOBAL data ) { Assert(m_Data.find(format)==m_Data.end()); m_Data[format]=data; } HRESULT STDMETHODCALLTYPE CMetroDataObject::QueryGetData( FORMATETC *pformatetc ) { if (!pformatetc) return E_INVALIDARG; if (pformatetc->dwAspect!=DVASPECT_CONTENT) return DV_E_DVASPECT; if (!(pformatetc->tymed&TYMED_HGLOBAL)) return DV_E_TYMED; if (m_Data.find(pformatetc->cfFormat)==m_Data.end()) return DV_E_CLIPFORMAT; return S_OK; } HRESULT STDMETHODCALLTYPE CMetroDataObject::GetCanonicalFormatEtc( FORMATETC *pformatectIn, FORMATETC *pformatetcOut ) { return pformatetcOut?DATA_S_SAMEFORMATETC:E_INVALIDARG; } HRESULT STDMETHODCALLTYPE CMetroDataObject::EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc ) { if (!ppenumFormatEtc) return E_POINTER; *ppenumFormatEtc=NULL; if (dwDirection == DATADIR_GET) { FORMATETC formats[] = { {CMenuContainer::s_MetroLinkFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL}, {CMenuContainer::s_DescriptorFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL}, {CMenuContainer::s_ContentsFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL}, }; HRESULT hr = SHCreateStdEnumFmtEtc(m_bContents?3:1,formats,ppenumFormatEtc); return hr; } return E_NOTIMPL; } CComPtr CMenuContainer::CreateMetroDataObject( const CItemManager::ItemInfo *pInfo ) { CString path; if (!pInfo->PATH.IsEmpty()) { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); path=pInfo->GetPath(); } IDataObject *pDataObjectIn=new CMetroDataObject(path); CComPtr pDataObject; SHCreateDataObject(NULL,0,NULL,pDataObjectIn,IID_IDataObject,(void**)&pDataObject); if (pDataObject) { if (m_pDragSourceHelper) { g_ItemManager.UpdateItemInfo(pInfo,CItemManager::INFO_EXTRA_LARGE_ICON|CItemManager::INFO_REFRESH_NOW,false); int iconSize=CItemManager::EXTRA_LARGE_ICON_SIZE; SHDRAGIMAGE di={{iconSize,iconSize},{iconSize/2,iconSize},NULL,CLR_NONE}; di.hbmpDragImage=(HBITMAP)CopyImage(pInfo->extraLargeIcon->bitmap,IMAGE_BITMAP,0,0,0); m_pDragSourceHelper->SetFlags(DSH_ALLOWDROPDESCRIPTIONTEXT); if (di.hbmpDragImage) m_pDragSourceHelper->InitializeFromBitmap(&di,pDataObject); } } else { pDataObjectIn->Release(); } return pDataObject; } /////////////////////////////////////////////////////////////////////////////// bool CMenuContainer::DragOutApps( const CItemManager::ItemInfo *pInfo ) { // drag the Apps tree item for reordering CComPtr pDataObj=CreateMetroDataObject(pInfo); // do drag drop s_pDragSource=NULL; s_bDragFromTree=false; m_DragIndex=-1; s_bPreventClosing=true; m_DragTime=GetMessageTime(); SetTimer(TIMER_DRAG,100); s_bDragClosed=false; DWORD dwEffect=DROPEFFECT_MOVE; HRESULT res=SHDoDragDrop(NULL,pDataObj,NULL,dwEffect,&dwEffect); s_pDragSource=NULL; s_bDragFromTree=false; if (!m_bDestroyed) KillTimer(TIMER_DRAG); s_bPreventClosing=false; if (s_bDragClosed) { for (std::vector::iterator it=s_Menus.begin();it!=s_Menus.end();++it) if (!(*it)->m_bDestroyed) (*it)->PostMessage(WM_CLOSE); } return true; } bool CMenuContainer::DragOut( int index, bool bApp ) { if (!(m_Options&CONTAINER_DRAG) || s_bNoDragDrop) return false; const MenuItem &item=m_Items[index]; if (!item.pItem1 || (item.id!=MENU_NO && item.id!=MENU_RECENT)) return false; bool bLeft=(GetKeyState(VK_LBUTTON)<0); bool bRight=(GetKeyState(VK_RBUTTON)<0); if (!bLeft && !bRight) return false; CComPtr pFolder; PCUITEMID_CHILD child; // get IDataObject for the current item CComPtr pDataObj; bool bProtectedLink=false; if (bApp && GetWinVersion()GetUIObjectOf(NULL,1,&child,IID_IDataObject,NULL,(void**)&pDataObj))) return true; { CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); bProtectedLink=!m_bSubMenu || item.pItemInfo->IsProtectedLink(); bMetroLink=item.pItemInfo->IsMetroLink(); } if (m_pDragSourceHelper && bMetroLink) { g_ItemManager.UpdateItemInfo(item.pItemInfo,CItemManager::INFO_EXTRA_LARGE_ICON|CItemManager::INFO_REFRESH_NOW,false); int iconSize=CItemManager::EXTRA_LARGE_ICON_SIZE; SHDRAGIMAGE di={{iconSize,iconSize},{iconSize/2,iconSize},NULL,CLR_NONE}; di.hbmpDragImage=(HBITMAP)CopyImage(item.pItemInfo->extraLargeIcon->bitmap,IMAGE_BITMAP,0,0,0); m_pDragSourceHelper->SetFlags(DSH_ALLOWDROPDESCRIPTIONTEXT); if (di.hbmpDragImage) m_pDragSourceHelper->InitializeFromBitmap(&di,pDataObj); } } if (bProtectedLink) { // protected links default to DROPEFFECT_LINK HGLOBAL hGlobal=GlobalAlloc(GMEM_MOVEABLE,4); if (hGlobal) { *(DWORD*)GlobalLock(hGlobal)=DROPEFFECT_LINK; GlobalUnlock(hGlobal); FORMATETC format={s_PreferredEffectFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL}; STGMEDIUM medium={TYMED_HGLOBAL}; medium.hGlobal=hGlobal; if (FAILED(pDataObj->SetData(&format,&medium,TRUE))) GlobalFree(hGlobal); } } // force synchronous operation { CComQIPtr pAsync(pDataObj); if (pAsync) pAsync->SetAsyncMode(FALSE); } // do drag drop s_pDragSource=this; s_bDragFromTree=(index==m_ProgramTreeIndex); m_DragIndex=index; s_bDragMovable=(item.id==MENU_NO && index=0 && s_JumpList.groups[LOWORD(item.jumpIndex)].type==CJumpGroup::TYPE_PINNED); s_bPreventClosing=true; m_DragTime=GetMessageTime(); s_bDragClosed=false; SetTimer(TIMER_DRAG,100); DWORD dwEffect=DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK; HRESULT res=SHDoDragDrop(NULL,pDataObj,NULL,dwEffect,&dwEffect); s_pDragSource=NULL; s_bDragFromTree=false; if (!m_bDestroyed) KillTimer(TIMER_DRAG); s_bDragMovable=false; s_bPreventClosing=false; if (s_bDragClosed) { for (std::vector::iterator it=s_Menus.begin();it!=s_Menus.end();++it) if (!(*it)->m_bDestroyed) (*it)->PostMessage(WM_CLOSE); return true; } if (pFolder && res==DRAGDROP_S_DROP && !m_bDestroyed) { // check if the item still exists. refresh the menu if it doesn't SFGAOF flags=SFGAO_VALIDATE; if (FAILED(pFolder->GetAttributesOf(1,&child,&flags))) { SetActiveWindow(); // close all submenus for (int i=(int)s_Menus.size()-1;s_Menus[i]!=this;i--) if (!s_Menus[i]->m_bDestroyed) s_Menus[i]->DestroyWindow(); // update menu PostRefreshMessage(); } } // activate the top non-destroyed menu for (int i=(int)s_Menus.size()-1;i>=0;i--) if (!s_Menus[i]->m_bDestroyed) { SetForegroundWindow(s_Menus[i]->m_hWnd); s_Menus[i]->SetActiveWindow(); break; } return true; } void CMenuContainer::SetDropTip( IDataObject *pDataObj, bool bPin ) { DROPDESCRIPTION desc={bPin?DROPIMAGE_LINK:DROPIMAGE_INVALID}; Strcpy(desc.szMessage,_countof(desc.szMessage),bPin?FindTranslation(L"Menu.PinStart",L"Pin to Start menu"):L""); HGLOBAL hDesc=GlobalAlloc(GMEM_MOVEABLE,sizeof(desc)); if (hDesc) { memcpy(GlobalLock(hDesc),&desc,sizeof(desc)); GlobalUnlock(hDesc); FORMATETC fmte={CMenuContainer::s_DropDescriptionFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL}; STGMEDIUM medium={}; medium.tymed=TYMED_HGLOBAL; medium.hGlobal=hDesc; if (FAILED(pDataObj->SetData(&fmte,&medium,TRUE))) { GlobalFree(hDesc); } } } void CMenuContainer::GetDragEffect( DWORD &grfKeyState, DWORD *pdwEffect ) { grfKeyState&=MK_SHIFT|MK_CONTROL|MK_ALT; if (s_bNoDragDrop || !(m_Options&CONTAINER_DROP)) { *pdwEffect=DROPEFFECT_NONE; // can't drop here return; } if (!m_pDropFolder[0] && !(s_pDragSource==this && s_bDragMovable && !s_bDragFromTree)) { *pdwEffect=DROPEFFECT_NONE; // can't drop here return; } // only accept known data formats FORMATETC format1={s_ShellFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL}; FORMATETC format2={s_ShellUrlFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL}; FORMATETC format3={s_MetroLinkFormat,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL}; if (m_pDragObject->QueryGetData(&format1)!=S_OK && m_pDragObject->QueryGetData(&format2)!=S_OK && m_pDragObject->QueryGetData(&format3)!=S_OK) { *pdwEffect=DROPEFFECT_NONE; return; } if (s_pDragSource) { if (s_pDragSource->m_Items[s_pDragSource->m_DragIndex].id==MENU_RECENT) *pdwEffect&=DROPEFFECT_LINK; // dragging a recent item (allow only link) else if (grfKeyState==0 && !s_bRightDrag && s_pDragSource==this && s_bDragMovable && !s_bDragFromTree) *pdwEffect&=DROPEFFECT_MOVE; // dragging within the same menu - use move by default else if (grfKeyState==0 && !s_bRightDrag) *pdwEffect&=(s_bDragMovable && m_bSubMenu && (s_pDragSource->m_Options&CONTAINER_PROGRAMS))?DROPEFFECT_MOVE:DROPEFFECT_LINK; // dragging normal item to a different menu - default to move or link } if (m_pDragObject->QueryGetData(&format3)==S_OK) { if (m_Options&CONTAINER_APPS) *pdwEffect&=(s_pDragSource==this && !s_bDragFromTree)?DROPEFFECT_MOVE:DROPEFFECT_NONE; // dragging a metro link to Apps folder else *pdwEffect&=DROPEFFECT_LINK; // dragging a metro link to another folder } // handle keys if (!s_bRightDrag) { if (grfKeyState==MK_SHIFT) *pdwEffect&=DROPEFFECT_MOVE; if (grfKeyState==MK_CONTROL) *pdwEffect&=DROPEFFECT_COPY; if (grfKeyState==(MK_CONTROL|MK_SHIFT) || grfKeyState==MK_ALT) *pdwEffect&=DROPEFFECT_LINK; } else if (!m_bSubMenu && grfKeyState==0 && (*pdwEffect&DROPEFFECT_LINK)) { // when a file is dragged to the start menu he usually wants to make a shortcut // so when right-dragging, and linking is allowed, make it the default grfKeyState=MK_SHIFT|MK_CONTROL; } } HRESULT STDMETHODCALLTYPE CMenuContainer::DragEnter( IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect ) { s_bRightDrag=(grfKeyState&MK_RBUTTON)!=0; m_pDragObject=pDataObj; if (m_pDropTargetHelper) { POINT p={pt.x,pt.y}; m_pDropTargetHelper->DragEnter(m_hWnd,m_pDragObject,&p,*pdwEffect); } if (!m_bSubMenu && !s_bShowTopEmpty) { // when dragging over the main menu, show an (Empty) item at the top so the user can drop items there for (size_t i=0;i0 && m_FolderHash[0]) s_MenuScrolls[m_FolderHash[0]]=m_ScrollOffset; else s_MenuScrolls.erase(m_FolderHash[0]); InitWindow(); break; } } m_DragHoverTime=GetMessageTime()-10000; m_DragHoverItem=-1; return S_OK; } HRESULT STDMETHODCALLTYPE CMenuContainer::DragOver( DWORD grfKeyState, POINTL pt, DWORD *pdwEffect ) { POINT p0={pt.x,pt.y}; if (m_pDropTargetHelper) m_pDropTargetHelper->DragOver(&p0,*pdwEffect); POINT p=p0; ScreenToClient(&p); int index=HitTest(p,NULL,true); int mark=-1; bool bAfter=false; const CItemManager::ItemInfo *pInfo=NULL; RECT rcItem; if (index>=0) { GetItemRect(index,rcItem); if (!m_bSubMenu && index=rcItem.top+h && p.yDragLeave(); m_pDropTarget=NULL; } if (pInfo && pInfo->GetPidl()) { m_pDropTargetInfo=pInfo; CComPtr pItem; SHCreateItemFromIDList(pInfo->GetPidl(),IID_IShellItem,(void**)&pItem); if (pItem) { pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IDropTarget,(void**)&m_pDropTarget); if (m_pDropTarget) { m_pDropTarget->DragEnter(m_pDragObject,grfKeyState,pt,pdwEffect); if (*pdwEffect==0) m_pDropTargetInfo=NULL; } } } else m_pDropTargetInfo=NULL; } if (!m_pDropTargetInfo) { dropTargetIndex=-1; m_pDropTarget=NULL; } if (dropTargetIndex!=m_DropTargetIndex) { InvalidateItem(dropTargetIndex); InvalidateItem(m_DropTargetIndex); m_DropTargetIndex=dropTargetIndex; } if (m_pDropTarget) { SetDropTip(m_pDragObject,false); SetInsertMark(-1,false); return m_pDropTarget->DragOver(grfKeyState,pt,pdwEffect); } s_bRightDrag=(grfKeyState&MK_RBUTTON)!=0; GetDragEffect(grfKeyState,pdwEffect); if (index>=0 && index=0 && index>=0 && m_Items[index].jumpIndex>=0) { int groupIndex=LOWORD(m_Items[m_DragIndex].jumpIndex); if (s_JumpList.groups[groupIndex].type==CJumpGroup::TYPE_PINNED && LOWORD(m_Items[index].jumpIndex)==groupIndex && (*pdwEffect&DROPEFFECT_MOVE)) { // reorder jump list *pdwEffect=DROPEFFECT_MOVE; int y=(rcItem.top+rcItem.bottom)/2; bAfter=p.y>=y; SetHotItem(-1); } else index=-1; mark=index; } else { if (!m_bSubMenu && index=0 && index==m_DragHoverItem) { int hoverTime=(int)s_HoverTime; if (m_Items[index].id==MENU_PROGRAMS && GetSettingInt(L"ProgramsStyle")==PROGRAMS_INLINE) hoverTime=(int)s_ProgramsHoverTime; if ((GetMessageTime()-m_DragHoverTime)>hoverTime && m_Submenu!=m_DragHoverItem) { // expand m_DragHoverItem if (index=0 && m_bInsertAfter && (before!=0 || (m_Items[0].id!=MENU_EMPTY && m_Items[0].id!=MENU_EMPTY_TOP))) before++; if (before>=0 && !m_bSubMenu && (*pdwEffect&DROPEFFECT_LINK) && (s_pDragSource!=this || s_bDragFromTree || !s_bDragMovable)) { *pdwEffect=DROPEFFECT_LINK; SetDropTip(m_pDragObject,true); } else SetDropTip(m_pDragObject,false); return S_OK; } HRESULT STDMETHODCALLTYPE CMenuContainer::DragLeave( void ) { if (m_pDropTarget) { m_pDropTarget->DragLeave(); m_pDropTarget=NULL; } InvalidateItem(m_DropTargetIndex); m_DropTargetIndex=-1; m_pDropTargetInfo=NULL; if (m_pDropTargetHelper) m_pDropTargetHelper->DragLeave(); SetDropTip(m_pDragObject,false); SetInsertMark(-1,false); m_pDragObject=NULL; UpdateScroll(NULL,false); return S_OK; } HRESULT STDMETHODCALLTYPE CMenuContainer::Drop( IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect ) { if (s_pDragSource) { if (!s_pDragSource->m_bDestroyed) s_pDragSource->KillTimer(TIMER_DRAG); } if (m_pDropTargetHelper) { POINT p={pt.x,pt.y}; m_pDropTargetHelper->Drop(pDataObj,&p,*pdwEffect); } if (m_pDropTarget) { m_pDragObject=NULL; HRESULT res=m_pDropTarget->Drop(pDataObj,grfKeyState,pt,pdwEffect); m_pDropTarget=NULL; return res; } GetDragEffect(grfKeyState,pdwEffect); m_pDragObject=NULL; int before=m_InsertMark; if (before<0) return S_OK; if (before>=0 && m_bInsertAfter && (before!=0 || (m_Items[0].id!=MENU_EMPTY && m_Items[0].id!=MENU_EMPTY_TOP))) before++; // clear the insert mark SetInsertMark(-1,false); int folderIndex=0; if (before>=0) folderIndex=m_Items[min(before,(int)m_Items.size()-1)].priority>1?1:0; if (s_pDragSource==this && !s_bDragFromTree && s_bDragMovable && (*pdwEffect&DROPEFFECT_MOVE) && m_DragIndex!=m_ProgramTreeIndex && m_Items[m_DragIndex].priority==(m_Items[min(before,(int)m_Items.size()-1)].priority&2)) { if (before==m_DragIndex || before==m_DragIndex+1) return S_OK; // dropped in the same menu, just rearrange the items PlayMenuSound(SOUND_DROP); if (m_Items[m_DragIndex].jumpIndex>=0 && s_JumpList.groups[LOWORD(m_Items[m_DragIndex].jumpIndex)].type==CJumpGroup::TYPE_PINNED) { // reordering pinned item int groupIdx=LOWORD(m_Items[m_DragIndex].jumpIndex); int itemIdx=HIWORD(m_Items[m_DragIndex].jumpIndex); for (int i=0;i<=m_DragIndex;i++) { if (m_Items[i].jumpIndex>=0 && LOWORD(m_Items[i].jumpIndex)==groupIdx) { // found first pinned index PinJumpItem(s_JumpAppInfo,s_JumpList,groupIdx,itemIdx,true,before-i); PostRefreshMessage(); break; } } } else if (!(m_Options&CONTAINER_AUTOSORT)) { std::vector items; int skip1=0, skip2=0; for (int i=0;im_DragIndex-skip1) before--; items.insert(items.begin()+(before-skip2),drag); SaveItemOrder(items); if (m_bTwoColumns && s_MenuMode==MODE_JUMPLIST) SetMenuMode(MODE_NORMAL); PostRefreshMessage(); } } else if (m_pDropFolder[folderIndex]) { // simulate dropping the object into the original folder PlayMenuSound(SOUND_DROP); if (before>=0 && !m_bSubMenu && (*pdwEffect&DROPEFFECT_LINK) && (s_pDragSource!=this || s_bDragFromTree || !s_bDragMovable)) *pdwEffect=DROPEFFECT_LINK; bool bDropped=false; if (!m_bSubMenu && (*pdwEffect&DROPEFFECT_LINK) && !s_bRightDrag) { // if dropping a single folder onto the main menu, create a fake folder CComPtr pArray; if (SUCCEEDED(SHCreateShellItemArrayFromDataObject(pDataObj,IID_IShellItemArray,(void**)&pArray))) { DWORD count; CComPtr pItem; if (SUCCEEDED(pArray->GetCount(&count)) && count==1 && SUCCEEDED(pArray->GetItemAt(0,&pItem))) { CComString pPath; if (pItem->GetDisplayName(SIGDN_FILESYSPATH,&pPath)==S_OK && PathIsDirectory(pPath)) { wchar_t path[_MAX_PATH]; if (SUCCEEDED(SHGetPathFromIDList(m_Path1[0],path))) { wchar_t fname[_MAX_FNAME]; Strcpy(fname,_countof(fname),PathFindFileName(pPath)); int len=Strlen(fname); while (len>0 && (fname[len-1]=='\\' || fname[len-1]==':')) len--; fname[len]=0; PathAppend(path,fname); wchar_t finalPath[_MAX_PATH]; PathYetAnotherMakeUniqueName(finalPath,path,NULL,PathFindFileName(path)); if (CreateFakeFolder(pPath,finalPath)) { bDropped=true; wchar_t locName[_MAX_PATH]; int locIndex; if (SHGetLocalizedName(pPath,locName,_countof(locName),&locIndex)==S_OK) { SHSetLocalizedName(finalPath,locName,locIndex); } } } } } } } if (!bDropped) { // must use IShellFolder to get to the drop target because the BindToHandler doesn't support passing the parent window (easily) CComPtr pDesktop; SHGetDesktopFolder(&pDesktop); CComPtr pFolder; CComPtr pTarget; if (!pDesktop || FAILED(pDesktop->BindToObject(m_Path1[folderIndex],NULL,IID_IShellFolder,(void**)&pFolder)) || FAILED(pFolder->CreateViewObject(g_OwnerWindow,IID_IDropTarget,(void**)&pTarget))) return S_OK; DWORD dwEffect=*pdwEffect; if (s_bRightDrag) { if (FAILED(pTarget->DragEnter(pDataObj,MK_RBUTTON|grfKeyState,pt,&dwEffect))) return S_OK; dwEffect=*pdwEffect; pTarget->DragOver(MK_RBUTTON|grfKeyState,pt,&dwEffect); } else { if (FAILED(pTarget->DragEnter(pDataObj,MK_LBUTTON|grfKeyState,pt,&dwEffect))) return S_OK; dwEffect=*pdwEffect; pTarget->DragOver(MK_LBUTTON|grfKeyState,pt,pdwEffect); } CComQIPtr pAsync=pDataObj; if (pAsync) pAsync->SetAsyncMode(FALSE); for (auto& it : s_Menus) { if (!it->m_bDestroyed) { it->EnableWindow(FALSE); // disable all menus it->SetWindowPos(HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } } bool bAllPrograms=s_bAllPrograms; if (bAllPrograms) ::EnableWindow(g_TopWin7Menu,FALSE); bool bOld=s_bPreventClosing; s_bPreventClosing=true; AddRef(); pTarget->Drop(pDataObj,grfKeyState,pt,pdwEffect); s_bPreventClosing=bOld; for (auto& it : s_Menus) { if (!it->m_bDestroyed) { it->EnableWindow(TRUE); // enable all menus it->SetWindowPos(HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } } if (bAllPrograms) ::EnableWindow(g_TopWin7Menu,TRUE); } else { AddRef(); } if (!m_bDestroyed) { SetForegroundWindow(m_hWnd); SetActiveWindow(); SetFocus(); } if (before>=0 && !(m_Options&CONTAINER_AUTOSORT)) { std::vector items; int skip=0; for (int i=0;iDragEnter(pDataObj,grfKeyState,pt,pdwEffect); } HRESULT STDMETHODCALLTYPE CDropTargetProxy::DragOver( DWORD grfKeyState, POINTL pt, DWORD *pdwEffect ) { if (!m_pOwner) return E_FAIL; return m_pOwner->DragOver(grfKeyState,pt,pdwEffect); } HRESULT STDMETHODCALLTYPE CDropTargetProxy::DragLeave( void ) { if (!m_pOwner) return E_FAIL; return m_pOwner->DragLeave(); } HRESULT STDMETHODCALLTYPE CDropTargetProxy::Drop( IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect ) { if (!m_pOwner) return E_FAIL; return m_pOwner->Drop(pDataObj,grfKeyState,pt,pdwEffect); }