diff --git a/reactos/dll/win32/browseui/explorerband.cpp b/reactos/dll/win32/browseui/explorerband.cpp index ec529e8b181..1e9769d7dc0 100644 --- a/reactos/dll/win32/browseui/explorerband.cpp +++ b/reactos/dll/win32/browseui/explorerband.cpp @@ -28,13 +28,148 @@ #define UNIMPLEMENTED DbgPrint("%s is UNIMPLEMENTED!\n", __FUNCTION__) #endif +/* + * TODO: + * - Monitor correctly "external" shell interrupts (seems like we need to register/deregister them for each folder) + * - find and fix what cause explorer crashes sometimes (seems to be explorer that does more releases than addref) + * - TESTING + */ + +typedef struct _PIDLDATA +{ + BYTE type; + BYTE data[1]; +} PIDLDATA, *LPPIDLDATA; + +#define PT_GUID 0x1F +#define PT_SHELLEXT 0x2E +#define PT_YAGUID 0x70 + +static BOOL _ILIsSpecialFolder (LPCITEMIDLIST pidl) +{ + LPPIDLDATA lpPData = (LPPIDLDATA)&pidl->mkid.abID; + + return (pidl && + ((lpPData && (PT_GUID == lpPData->type || PT_SHELLEXT== lpPData->type || + PT_YAGUID == lpPData->type)) || (pidl && pidl->mkid.cb == 0x00))); +} + +static BOOL _ILIsDesktop (LPCITEMIDLIST pidl) +{ + return (pidl && pidl->mkid.cb == 0x00); +} + + +HRESULT GetDisplayName(LPCITEMIDLIST pidlDirectory,TCHAR *szDisplayName,UINT cchMax,DWORD uFlags) +{ + IShellFolder *pShellFolder = NULL; + LPCITEMIDLIST pidlRelative = NULL; + STRRET str; + HRESULT hr; + + if (pidlDirectory == NULL || szDisplayName == NULL) + { + return E_FAIL; + } + + hr = SHBindToParent(pidlDirectory, IID_PPV_ARG(IShellFolder, &pShellFolder), &pidlRelative); + + if (SUCCEEDED(hr)) + { + hr = pShellFolder->GetDisplayNameOf(pidlRelative,uFlags,&str); + if (SUCCEEDED(hr)) + { + hr = StrRetToBuf(&str,pidlDirectory,szDisplayName,cchMax); + } + pShellFolder->Release(); + } + return hr; +} + +extern "C" +HRESULT WINAPI CExplorerBand_Constructor(REFIID riid, LPVOID *ppv) +{ +#ifdef __REACTOS__ + return ShellObjectCreator(riid, ppv); +#else + return S_OK; +#endif +} + +/* + This is a Windows hack, because shell event messages in Windows gives an + ill-formed PIDL stripped from useful data that parses incorrectly with SHGetFileInfo. + So we need to re-enumerate subfolders until we find one with the same name. + */ +HRESULT _ReparsePIDL(LPITEMIDLIST buggyPidl, LPITEMIDLIST *cleanPidl) +{ + HRESULT hr; + CComPtr folder; + CComPtr persist; + CComPtr pEnumIDList; + LPITEMIDLIST childPidl; + LPITEMIDLIST correctChild; + LPITEMIDLIST correctParent; + ULONG fetched; + DWORD EnumFlags; + + + EnumFlags = SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN; + hr = SHBindToParent(buggyPidl, IID_PPV_ARG(IShellFolder, &folder), (LPCITEMIDLIST*)&childPidl); + *cleanPidl = NULL; + if (!SUCCEEDED(hr)) + { + ERR("Can't bind to parent folder\n"); + return hr; + } + hr = folder->QueryInterface(IID_PPV_ARG(IPersistFolder2, &persist)); + if (!SUCCEEDED(hr)) + { + ERR("PIDL doesn't belong to the shell namespace, aborting\n"); + return hr; + } + + hr = persist->GetCurFolder(&correctParent); + if (!SUCCEEDED(hr)) + { + ERR("Unable to get current folder\n"); + return hr; + } + + hr = folder->EnumObjects(NULL,EnumFlags,&pEnumIDList); + // avoid broken IShellFolder implementations that return null pointer with success + if (!SUCCEEDED(hr) || !pEnumIDList) + { + ERR("Can't enum the folder !\n"); + return hr; + } + + while(SUCCEEDED(pEnumIDList->Next(1, &correctChild, &fetched)) && correctChild && fetched) + { + if (!folder->CompareIDs(0, childPidl, correctChild)) + { + *cleanPidl = ILCombine(correctParent, correctChild); + ILFree(correctChild); + goto Cleanup; + } + ILFree(correctChild); + } +Cleanup: + ILFree(correctParent); + return hr; +} + CExplorerBand::CExplorerBand() : - pSite(NULL), fVisible(FALSE), bNavigating(FALSE), dwBandID(0) + pSite(NULL), fVisible(FALSE), bNavigating(FALSE), dwBandID(0), pidlCurrent(NULL) { } CExplorerBand::~CExplorerBand() { + if(pidlCurrent) + { + ILFree(pidlCurrent); + } } void CExplorerBand::InitializeExplorerBand() @@ -176,6 +311,11 @@ HRESULT CExplorerBand::UpdateBrowser(LPITEMIDLIST pidlGoto) if (FAILED_UNEXPECTEDLY(hr)) return hr; + if(pidlCurrent) + { + ILFree(pidlCurrent); + pidlCurrent = ILClone(pidlGoto); + } return hr; } @@ -214,6 +354,17 @@ BOOL CExplorerBand::OnTreeItemExpanding(LPNMTREEVIEW pnmtv) return FALSE; } +BOOL CExplorerBand::OnTreeItemDeleted(LPNMTREEVIEW pnmtv) +{ + /* Destroy memory associated to our node */ + NodeInfo* ptr = GetNodeInfo(pnmtv->itemNew.hItem); + + ILFree(ptr->relativePidl); + ILFree(ptr->absolutePidl); + delete ptr; + return TRUE; +} + void CExplorerBand::OnSelectionChanged(LPNMTREEVIEW pnmtv) { NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem); @@ -229,6 +380,29 @@ void CExplorerBand::OnSelectionChanged(LPNMTREEVIEW pnmtv) //TreeView_Expand(m_hWnd, pnmtv->itemNew.hItem, TVE_EXPAND); } +void CExplorerBand::OnTreeItemDragging(LPNMTREEVIEW pnmtv, BOOL isRightClick) +{ + CComPtr pSrcFolder; + CComPtr pObj; + LPCITEMIDLIST pLast; + HRESULT hr; + DWORD dwEffect; + DWORD dwEffect2; + + dwEffect = DROPEFFECT_COPY | DROPEFFECT_MOVE; + if (!pnmtv->itemNew.lParam) + return; + NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem); + hr = SHBindToParent(pNodeInfo->absolutePidl, IID_PPV_ARG(IShellFolder, &pSrcFolder), &pLast); + if (!SUCCEEDED(hr)) + return; + hr = pSrcFolder->GetUIObjectOf(m_hWnd, 1, &pLast, IID_IDataObject, 0, reinterpret_cast(&pObj)); + if (!SUCCEEDED(hr)) + return; + DoDragDrop(pObj, this, dwEffect, &dwEffect2); + return; +} + // *** ATL event handlers *** LRESULT CExplorerBand::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) @@ -323,6 +497,64 @@ LRESULT CExplorerBand::ContextMenuHack(UINT uMsg, WPARAM wParam, LPARAM lParam, } return FALSE; /* let the wndproc process the message */ } + +LRESULT CExplorerBand::OnShellEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + LPITEMIDLIST *dest; + LPITEMIDLIST clean; + HTREEITEM pItem; + + dest = (LPITEMIDLIST*)wParam; + /* TODO: handle shell notifications */ + switch(lParam & ~SHCNE_INTERRUPT) + { + case SHCNE_MKDIR: + if (!SUCCEEDED(_ReparsePIDL(dest[0], &clean))) + { + ERR("Can't reparse PIDL to a valid one\n"); + return FALSE; + } + NavigateToPIDL(clean, &pItem, FALSE, TRUE, FALSE); + ILFree(clean); + break; + case SHCNE_RMDIR: + DeleteItem(dest[0]); + break; + case SHCNE_RENAMEFOLDER: + if (!SUCCEEDED(_ReparsePIDL(dest[1], &clean))) + { + ERR("Can't reparse PIDL to a valid one\n"); + return FALSE; + } + if (NavigateToPIDL(dest[0], &pItem, FALSE, FALSE, FALSE)) + RenameItem(pItem, clean); + ILFree(clean); + break; + case SHCNE_UPDATEDIR: + // We don't take care of this message + TRACE("Directory updated\n"); + break; + default: + TRACE("Unhandled message\n"); + } + return TRUE; +} + +LRESULT CExplorerBand::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + bFocused = TRUE; + IUnknown_OnFocusChangeIS(pSite, reinterpret_cast(this), TRUE); + bHandled = FALSE; + return TRUE; +} + +LRESULT CExplorerBand::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + IUnknown_OnFocusChangeIS(pSite, reinterpret_cast(this), FALSE); + bHandled = FALSE; + return TRUE; +} + // *** Helper functions *** HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, IShellFolder *psfParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort) { @@ -377,6 +609,15 @@ HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, IShellFolder *psfParent, htiCreated = TreeView_InsertItem(m_hWnd, &tvInsert); + if (bSort) + { + TVSORTCB sortCallback; + sortCallback.hParent = hParent; + sortCallback.lpfnCompare = CompareTreeItems; + sortCallback.lParam = (LPARAM)this; + SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback); + } + return htiCreated; } @@ -401,6 +642,7 @@ BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo) ULONG fetched; ULONG uItemCount; CComPtr pFolder; + TVSORTCB sortCallback; entry = pNodeInfo->absolutePidl; fetched = 1; @@ -458,6 +700,11 @@ BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo) ILFree(pidlSub); } pNodeInfo->expanded = TRUE; + /* Let's do sorting */ + sortCallback.hParent = hItem; + sortCallback.lpfnCompare = CompareTreeItems; + sortCallback.lParam = (LPARAM)this; + SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback); /* Now we can redraw */ SendMessage(WM_SETREDRAW, TRUE, 0); @@ -596,6 +843,184 @@ BOOL CExplorerBand::NavigateToCurrentFolder() return result; } +BOOL CExplorerBand::DeleteItem(LPITEMIDLIST idl) +{ + HTREEITEM toDelete; + TVITEM tvItem; + HTREEITEM parentNode; + + if (!NavigateToPIDL(idl, &toDelete, FALSE, FALSE, FALSE)) + return FALSE; + + // TODO: check that the treeview item is really deleted + + parentNode = TreeView_GetParent(m_hWnd, toDelete); + // Navigate to parent when deleting child item + if (!pDesktop->CompareIDs(0, idl, pidlCurrent)) + { + TreeView_SelectItem(m_hWnd, parentNode); + } + + // Remove the child item + TreeView_DeleteItem(m_hWnd, toDelete); + // Probe parent to see if it has children + if (!TreeView_GetChild(m_hWnd, parentNode)) + { + // Decrement parent's child count + ZeroMemory(&tvItem, sizeof(tvItem)); + tvItem.mask = TVIF_CHILDREN; + tvItem.hItem = parentNode; + tvItem.cChildren = 0; + TreeView_SetItem(m_hWnd, &tvItem); + } + return TRUE; +} + +BOOL CExplorerBand::RenameItem(HTREEITEM toRename, LPITEMIDLIST newPidl) +{ + WCHAR wszDisplayName[MAX_PATH]; + TVITEM itemInfo; + LPCITEMIDLIST relPidl; + NodeInfo *treeInfo; + TVSORTCB sortCallback; + HTREEITEM child; + + ZeroMemory(&itemInfo, sizeof(itemInfo)); + itemInfo.mask = TVIF_PARAM; + itemInfo.hItem = toRename; + + // Change PIDL associated to the item + relPidl = ILFindLastID(newPidl); + TreeView_GetItem(m_hWnd, &itemInfo); + if (!itemInfo.lParam) + { + ERR("Unable to fetch lParam\n"); + return FALSE; + } + SendMessage(WM_SETREDRAW, FALSE, 0); + treeInfo = (NodeInfo*)itemInfo.lParam; + ILFree(treeInfo->absolutePidl); + ILFree(treeInfo->relativePidl); + treeInfo->absolutePidl = ILClone(newPidl); + treeInfo->relativePidl = ILClone(relPidl); + + // Change the display name + GetDisplayName(newPidl, wszDisplayName, MAX_PATH, SHGDN_INFOLDER); + ZeroMemory(&itemInfo, sizeof(itemInfo)); + itemInfo.hItem = toRename; + itemInfo.mask = TVIF_TEXT; + itemInfo.pszText = wszDisplayName; + TreeView_SetItem(m_hWnd, &itemInfo); + + if((child = TreeView_GetChild(m_hWnd, toRename)) != NULL) + { + RefreshTreePidl(child, newPidl); + } + + // Sorting + sortCallback.hParent = TreeView_GetParent(m_hWnd, toRename); + sortCallback.lpfnCompare = CompareTreeItems; + sortCallback.lParam = (LPARAM)this; + SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback); + SendMessage(WM_SETREDRAW, TRUE, 0); + return TRUE; +} + +BOOL CExplorerBand::RefreshTreePidl(HTREEITEM tree, LPITEMIDLIST pidlParent) +{ + HTREEITEM tmp; + NodeInfo *pInfo; + + // Update our node data + pInfo = GetNodeInfo(tree); + if (!pInfo) + { + WARN("No tree info !\n"); + return FALSE; + } + ILFree(pInfo->absolutePidl); + pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl); + if (!pInfo->absolutePidl) + { + WARN("PIDL allocation failed\n"); + return FALSE; + } + // Recursively update children + if ((tmp = TreeView_GetChild(m_hWnd, tree)) != NULL) + { + RefreshTreePidl(tmp, pInfo->absolutePidl); + } + + tmp = TreeView_GetNextSibling(m_hWnd, tree); + while(tmp != NULL) + { + pInfo = GetNodeInfo(tmp); + if(!pInfo) + { + WARN("No tree info !\n"); + continue; + } + ILFree(pInfo->absolutePidl); + pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl); + tmp = TreeView_GetNextSibling(m_hWnd, tmp); + } + return TRUE; +} + +// *** Tree item sorting callback *** +int CALLBACK CExplorerBand::CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3) +{ + /* + * We first sort drive letters (Path root), then PIDLs and then regular folder + * display name. + * This is not how Windows sorts item, but it gives decent results. + */ + NodeInfo *info1; + NodeInfo *info2; + CExplorerBand *pThis; + WCHAR wszFolder1[MAX_PATH]; + WCHAR wszFolder2[MAX_PATH]; + + info1 = (NodeInfo*)p1; + info2 = (NodeInfo*)p2; + pThis = (CExplorerBand*)p3; + + GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_FORPARSING); + GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_FORPARSING); + if (PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2)) + { + return lstrcmpiW(wszFolder1,wszFolder2); + } + if (PathIsRoot(wszFolder1) && !PathIsRoot(wszFolder2)) + { + return -1; + } + if (!PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2)) + { + return 1; + } + // Now, we compare non-root folders, grab display name + GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_INFOLDER); + GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_INFOLDER); + + if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl)) + { + return -1; + } + if (!_ILIsSpecialFolder(info1->relativePidl) && _ILIsSpecialFolder(info2->relativePidl)) + { + return 1; + } + if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl)) + { + HRESULT hr; + hr = pThis->pDesktop->CompareIDs(0, info1->absolutePidl, info2->absolutePidl); + if (!hr) return 0; + return (hr > 0) ? -1 : 1; + } + return StrCmpLogicalW(wszFolder1, wszFolder2); +} + // *** IOleWindow methods *** HRESULT STDMETHODCALLTYPE CExplorerBand::GetWindow(HWND *lphwnd) { @@ -835,6 +1260,7 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::Save(IStream *pStm, BOOL fClearDirty) HRESULT STDMETHODCALLTYPE CExplorerBand::GetSizeMax(ULARGE_INTEGER *pcbSize) { + // TODO: calculate max size UNIMPLEMENTED; return E_NOTIMPL; } @@ -855,10 +1281,16 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM case TVN_SELCHANGED: OnSelectionChanged((LPNMTREEVIEW)lParam); break; + case TVN_DELETEITEM: + OnTreeItemDeleted((LPNMTREEVIEW)lParam); + break; case NM_RCLICK: OnContextMenu(WM_CONTEXTMENU, (WPARAM)m_hWnd, GetMessagePos(), bHandled); *theResult = 1; break; + case TVN_BEGINDRAG: + case TVN_BEGINRDRAG: + OnTreeItemDragging((LPNMTREEVIEW)lParam, pNotifyHeader->code == TVN_BEGINRDRAG); case TVN_BEGINLABELEDITW: { // TODO: put this in a function ? (mostly copypasta from CDefView) @@ -1009,37 +1441,120 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(DISPID dispIdMember, REFIID riid // *** IDropTarget methods *** HRESULT STDMETHODCALLTYPE CExplorerBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) { - UNIMPLEMENTED; - return E_NOTIMPL; + ERR("Entering drag\n"); + pCurObject = pObj; + oldSelected = TreeView_GetSelection(m_hWnd); + return DragOver(glfKeyState, pt, pdwEffect); } HRESULT STDMETHODCALLTYPE CExplorerBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) { - UNIMPLEMENTED; - return E_NOTIMPL; + TVHITTESTINFO info; + CComPtr pShellFldr; + NodeInfo *nodeInfo; + //LPCITEMIDLIST pChild; + HRESULT hr; + + info.pt.x = pt.x; + info.pt.y = pt.y; + info.flags = TVHT_ONITEM; + info.hItem = NULL; + ScreenToClient(&info.pt); + + // Move to the item selected by the treeview (don't change right pane) + TreeView_HitTest(m_hWnd, &info); + + if (info.hItem) + { + bNavigating = TRUE; + TreeView_SelectItem(m_hWnd, info.hItem); + bNavigating = FALSE; + // Delegate to shell folder + if (pDropTarget && info.hItem != childTargetNode) + { + pDropTarget = NULL; + } + if (info.hItem != childTargetNode) + { + nodeInfo = GetNodeInfo(info.hItem); + if (!nodeInfo) + return E_FAIL; +#if 0 + hr = SHBindToParent(nodeInfo->absolutePidl, IID_PPV_ARG(IShellFolder, &pShellFldr), &pChild); + if (!SUCCEEDED(hr)) + return E_FAIL; + hr = pShellFldr->GetUIObjectOf(m_hWnd, 1, &pChild, IID_IDropTarget, NULL, reinterpret_cast(&pDropTarget)); + if (!SUCCEEDED(hr)) + return E_FAIL; +#endif + if(_ILIsDesktop(nodeInfo->absolutePidl)) + pShellFldr = pDesktop; + else + { + hr = pDesktop->BindToObject(nodeInfo->absolutePidl, 0, IID_PPV_ARG(IShellFolder, &pShellFldr)); + if (!SUCCEEDED(hr)) + { + /* Don't allow dnd since we couldn't get our folder object */ + ERR("Can't bind to folder object\n"); + *pdwEffect = DROPEFFECT_NONE; + return E_FAIL; + } + } + hr = pShellFldr->CreateViewObject(m_hWnd, IID_PPV_ARG(IDropTarget, &pDropTarget)); + if (!SUCCEEDED(hr)) + { + /* Don't allow dnd since we couldn't get our drop target */ + ERR("Can't get drop target for folder object\n"); + *pdwEffect = DROPEFFECT_NONE; + return E_FAIL; + } + hr = pDropTarget->DragEnter(pCurObject, glfKeyState, pt, pdwEffect); + childTargetNode = info.hItem; + } + hr = pDropTarget->DragOver(glfKeyState, pt, pdwEffect); + } + else + { + childTargetNode = NULL; + pDropTarget = NULL; + *pdwEffect = DROPEFFECT_NONE; + } + return S_OK; } HRESULT STDMETHODCALLTYPE CExplorerBand::DragLeave() { - UNIMPLEMENTED; - return E_NOTIMPL; + bNavigating = TRUE; + TreeView_SelectItem(m_hWnd, oldSelected); + bNavigating = FALSE; + childTargetNode = NULL; + if (pCurObject) + { + pCurObject = NULL; + } + return S_OK; } HRESULT STDMETHODCALLTYPE CExplorerBand::Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) { - UNIMPLEMENTED; - return E_NOTIMPL; + if (!pDropTarget) + return E_FAIL; + pDropTarget->Drop(pObj, glfKeyState, pt, pdwEffect); + DragLeave(); + return S_OK; } // *** IDropSource methods *** HRESULT STDMETHODCALLTYPE CExplorerBand::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) { - UNIMPLEMENTED; - return E_NOTIMPL; + if (fEscapePressed) + return DRAGDROP_S_CANCEL; + if ((grfKeyState & MK_LBUTTON) || (grfKeyState & MK_RBUTTON)) + return S_OK; + return DRAGDROP_S_DROP; } HRESULT STDMETHODCALLTYPE CExplorerBand::GiveFeedback(DWORD dwEffect) { - UNIMPLEMENTED; - return E_NOTIMPL; + return DRAGDROP_S_USEDEFAULTCURSORS; } diff --git a/reactos/dll/win32/browseui/explorerband.h b/reactos/dll/win32/browseui/explorerband.h index f73fe9af314..3c2a1af5488 100644 --- a/reactos/dll/win32/browseui/explorerband.h +++ b/reactos/dll/win32/browseui/explorerband.h @@ -53,7 +53,7 @@ private: // *** BaseBarSite information *** CComPtr pSite; CComPtr pDesktop; - + // *** tree explorer band stuff *** BOOL fVisible; BOOL bNavigating; @@ -62,21 +62,33 @@ private: HIMAGELIST hImageList; HTREEITEM hRoot; HTREEITEM oldSelected; - + LPITEMIDLIST pidlCurrent; + // *** notification cookies *** DWORD adviseCookie; ULONG shellRegID; - + + // *** Drop target information *** + CComPtr pDropTarget; + HTREEITEM childTargetNode; + CComPtr pCurObject; + void InitializeExplorerBand(); void DestroyExplorerBand(); HRESULT ExecuteCommand(CComPtr& menu, UINT nCmd); + // *** notifications handling *** BOOL OnTreeItemExpanding(LPNMTREEVIEW pnmtv); void OnSelectionChanged(LPNMTREEVIEW pnmtv); + BOOL OnTreeItemDeleted(LPNMTREEVIEW pnmtv); + void OnTreeItemDragging(LPNMTREEVIEW pnmtv, BOOL isRightClick); // *** ATL event handlers *** LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); LRESULT ContextMenuHack(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + LRESULT OnShellEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); // *** Helper functions *** NodeInfo* GetNodeInfo(HTREEITEM hItem); @@ -85,8 +97,14 @@ private: HTREEITEM InsertItem(HTREEITEM hParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort); BOOL InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo); BOOL NavigateToPIDL(LPITEMIDLIST dest, HTREEITEM *item, BOOL bExpand, BOOL bInsert, BOOL bSelect); + BOOL DeleteItem(LPITEMIDLIST toDelete); + BOOL RenameItem(HTREEITEM toRename, LPITEMIDLIST newPidl); + BOOL RefreshTreePidl(HTREEITEM tree, LPITEMIDLIST pidlParent); BOOL NavigateToCurrentFolder(); + // *** Tree item sorting callback *** + static int CALLBACK CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3); + public: CExplorerBand(); virtual ~CExplorerBand(); @@ -181,6 +199,12 @@ public: BEGIN_MSG_MAP(CExplorerBand) MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) + MESSAGE_HANDLER(WM_USER_SHELLEVENT, OnShellEvent) MESSAGE_HANDLER(WM_RBUTTONDOWN, ContextMenuHack) + MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) + // MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) END_MSG_MAP() }; + +extern "C" +HRESULT WINAPI CExplorerBand_Constructor(REFIID riid, LPVOID *ppv);