Index: explorerband.cpp =================================================================== --- explorerband.cpp (revision 71510) +++ explorerband.cpp (working copy) @@ -1,87 +1,215 @@ -/* - * ReactOS Explorer - * - * Copyright 2016 Sylvain Deverre - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - #include "precomp.h" -#include -#include #if 1 #undef UNIMPLEMENTED -#define UNIMPLEMENTED DbgPrint("%s is UNIMPLEMENTED!\n", __FUNCTION__) +#define UNIMPLEMENTED DbgPrint("%s 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; +} + +static LPARAM TreeView_GetItemData(HWND hwndTreeView, HTREEITEM hItem); + + 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), dwBandID(0) + pSite(NULL), fVisible(FALSE), bNavigating(FALSE), dwBandID(0), pidlCurrent(NULL) { } CExplorerBand::~CExplorerBand() { + if(pidlCurrent) + { + ILFree(pidlCurrent); + } } void CExplorerBand::InitializeExplorerBand() { // Init the treeview here + TV_INSERTSTRUCT tvInsert; + TV_ITEM* tvItem; HRESULT hr; LPITEMIDLIST pidl; + SHFILEINFOW shfi; + NodeInfo *pNodeInfo; CComPtr browserService; SHChangeNotifyEntry shcne; - + + tvItem = &tvInsert.item; + tvInsert.hParent = 0; + tvInsert.hInsertAfter = TVI_LAST; hr = SHGetDesktopFolder(&pDesktop); - if (FAILED_UNEXPECTEDLY(hr)) + if (!SUCCEEDED(hr)) + { + ERR("Can't initialize desktop folder !\n"); return; - + } + pNodeInfo = new NodeInfo; + if (!pNodeInfo) + { + ERR("Can't allocate memory, aborting.\n"); + return; + } + // Initialize display name and our imagelist hr = SHGetFolderLocation(m_hWnd, CSIDL_DESKTOP, NULL, 0, &pidl); - if (FAILED_UNEXPECTEDLY(hr)) - return; + hImageList = (HIMAGELIST)SHGetFileInfoW((LPTSTR)pidl, NULL, &shfi, NULL, + SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME | SHGFI_SMALLICON); - IImageList * piml; - hr = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &piml)); - if (FAILED_UNEXPECTEDLY(hr)) - return; + // Set up our node + tvItem->mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN; + tvItem->cchTextMax = MAX_PATH; + tvItem->pszText = shfi.szDisplayName; + tvItem->iImage = tvItem->iSelectedImage = shfi.iIcon; + tvItem->cChildren = 1; - TreeView_SetImageList(m_hWnd, (HIMAGELIST)piml, TVSIL_NORMAL); + // Insert create node data + pNodeInfo->absolutePidl = ILClone(pidl); + pNodeInfo->relativePidl = ILClone(pidl); + pNodeInfo->expanded = FALSE; + tvItem->lParam = (LPARAM)pNodeInfo; + TreeView_SetImageList(m_hWnd, hImageList, TVSIL_NORMAL); - // Insert the root node - HTREEITEM hItem = InsertItem(0, pDesktop, pidl, pidl, FALSE); - if (!hItem) - { - ERR("Failed to create root item\n"); - return; - } + // Add root node + HTREEITEM hItem = TreeView_InsertItem(m_hWnd, &tvInsert); + hRoot = hItem; + TreeView_SelectItem(m_hWnd, hItem); - NodeInfo* pNodeInfo = GetNodeInfo(hItem); - // Insert child nodes InsertSubitems(hItem, pNodeInfo); TreeView_Expand(m_hWnd, hItem, TVE_EXPAND); // Navigate to current folder position - //NavigateToCurrentFolder(); + NavigateToCurrentFolder(); // Register shell notification shcne.pidl = pidl; @@ -98,14 +226,16 @@ ERR("Something went wrong, error %08x\n", GetLastError()); } // Register browser connection endpoint - hr = IUnknown_QueryService(pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService)); - if (FAILED_UNEXPECTEDLY(hr)) - return; - + hr = pServiceProvider->QueryService(SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService)); + if (!SUCCEEDED(hr)) + { + ERR("Can't get browser service\n"); + } hr = AtlAdvise(browserService, dynamic_cast(this), DIID_DWebBrowserEvents, &adviseCookie); - if (FAILED_UNEXPECTEDLY(hr)) - return; - + if (!SUCCEEDED(hr)) + { + ERR("Can't advise our browser service, error %08x\n"); + } ILFree(pidl); } @@ -114,12 +244,13 @@ HRESULT hr; CComPtr browserService; + TRACE("Cleaning up explorer band ...\n"); - - hr = IUnknown_QueryService(pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService)); - if (FAILED_UNEXPECTEDLY(hr)) - return; - + hr = pServiceProvider->QueryService(SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService)); + if (!SUCCEEDED(hr)) + { + ERR("Can't get browser service\n"); + } hr = AtlUnadvise(browserService, DIID_DWebBrowserEvents, adviseCookie); /* Remove all items of the treeview */ RevokeDragDrop(m_hWnd); @@ -129,17 +260,39 @@ TRACE("Cleanup done !\n"); } -CExplorerBand::NodeInfo* CExplorerBand::GetNodeInfo(HTREEITEM hItem) +HRESULT CExplorerBand::ExecuteCommand(CComPtr& menu, UINT nCmd) { - TVITEM tvItem; + CComPtr pBrowserOleWnd; + CMINVOKECOMMANDINFO cmi; + HWND browserWnd; + HRESULT hr; - tvItem.mask = TVIF_PARAM; - tvItem.hItem = hItem; + ZeroMemory(&cmi, sizeof(cmi)); + cmi.cbSize = sizeof(cmi); + cmi.lpVerb = MAKEINTRESOURCEA(nCmd); - if (!TreeView_GetItem(m_hWnd, &tvItem)) - return 0; + // Retrieve OLE window + hr = pServiceProvider->QueryService(SID_SShellBrowser, IID_PPV_ARG(IOleWindow, &pBrowserOleWnd)); + if (!SUCCEEDED(hr)) + { + ERR("Can't get IOleWindow interface for shell browser\n"); + return E_FAIL; + } + hr = pBrowserOleWnd->GetWindow(&browserWnd); + if (!SUCCEEDED(hr)) + { + ERR("Can't get browser hWnd\n"); + return E_FAIL; + } + cmi.hwnd = browserWnd; - return reinterpret_cast(tvItem.lParam); + if (GetKeyState(VK_SHIFT) & 0x8000) + cmi.fMask |= CMIC_MASK_SHIFT_DOWN; + + if (GetKeyState(VK_CONTROL) & 0x8000) + cmi.fMask |= CMIC_MASK_CONTROL_DOWN; + + return menu->InvokeCommand(&cmi); } HRESULT CExplorerBand::UpdateBrowser(LPITEMIDLIST pidlGoto) @@ -146,22 +299,32 @@ { CComPtr pBrowserService; HRESULT hr; - - hr = IUnknown_QueryService(pSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &pBrowserService)); - if (FAILED_UNEXPECTEDLY(hr)) + + hr = pServiceProvider->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &pBrowserService)); + if (!SUCCEEDED(hr)) + { + ERR("Can't get IBrowserService !\n"); return hr; + } hr = pBrowserService->BrowseObject(pidlGoto, SBSP_SAMEBROWSER | SBSP_ABSOLUTE); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; + if (!SUCCEEDED(hr)) + { + ERR("Cannot navigate to specified destination !\n"); + } + if(pidlCurrent) + { + ILFree(pidlCurrent); + pidlCurrent = ILClone(pidlGoto); + } + return hr; - return hr; } // *** notifications handling *** BOOL CExplorerBand::OnTreeItemExpanding(LPNMTREEVIEW pnmtv) { - NodeInfo *pNodeInfo; + NodeInfo *pNodeInfo; if (pnmtv->action == TVE_COLLAPSE) { if (pnmtv->itemNew.hItem == hRoot) @@ -176,7 +339,7 @@ } if (pnmtv->action == TVE_EXPAND) { // Grab our directory PIDL - pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem); + pNodeInfo = (NodeInfo*)TreeView_GetItemData(m_hWnd, pnmtv->itemNew.hItem); // We have it, let's try if (pNodeInfo && !pNodeInfo->expanded) if (!InsertSubitems(pnmtv->itemNew.hItem, pNodeInfo)) { @@ -193,70 +356,263 @@ return FALSE; } -void CExplorerBand::OnSelectionChanged(LPNMTREEVIEW pnmtv) +BOOL CExplorerBand::OnTreeItemDeleted(LPNMTREEVIEW pnmtv) { - NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem); + /* Destroy memory associated to our node */ + NodeInfo *ptr; - UpdateBrowser(pNodeInfo->absolutePidl); + ptr = (NodeInfo*)pnmtv->itemOld.lParam; + ILFree(ptr->relativePidl); + ILFree(ptr->absolutePidl); + delete ptr; + return TRUE; +} + +void CExplorerBand::OnTreeItemChanged(LPNMTREEVIEW pnmtv) +{ + NodeInfo *pInfo; + + /* Prevents navigation if selection is initiated inside the band */ + if (bNavigating) + return; + + pInfo = (NodeInfo*)TreeView_GetItemData(m_hWnd, pnmtv->itemNew.hItem); + UpdateBrowser(pInfo->absolutePidl); SetFocus(); // Expand the node //TreeView_Expand(m_hWnd, pnmtv->itemNew.hItem, TVE_EXPAND); } -// *** Helper functions *** -HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, IShellFolder *psfParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort) +void CExplorerBand::OnTreeItemDragging(LPNMTREEVIEW pnmtv, BOOL isRightClick) { - TV_INSERTSTRUCT tvInsert; - HTREEITEM htiCreated; + CComPtr pSrcFolder; + CComPtr pObj; + NodeInfo *pNodeInfo; + LPCITEMIDLIST pLast; + HRESULT hr; + DWORD dwEffect; + DWORD dwEffect2; - /* Get the attributes of the node */ - SFGAOF attrs = SFGAO_STREAM | SFGAO_HASSUBFOLDER; - HRESULT hr = psfParent->GetAttributesOf(1, &pEltRelative, &attrs); - if (FAILED_UNEXPECTEDLY(hr)) - return NULL; + dwEffect = DROPEFFECT_COPY | DROPEFFECT_MOVE; + if (!pnmtv->itemNew.lParam) + return; + pNodeInfo = (NodeInfo*)pnmtv->itemNew.lParam; + 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; +} - /* Ignore streams */ - if ((attrs & SFGAO_STREAM)) + +// *** ATL event handlers *** +LRESULT CExplorerBand::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + HTREEITEM item; + NodeInfo *info; + HMENU treeMenu; + WORD x; + WORD y; + CComPtr pFolder; + CComPtr contextMenu; + HRESULT hr; + UINT uCommand; + LPITEMIDLIST pidlChild; + + treeMenu = NULL; + item = TreeView_GetSelection(m_hWnd); + bHandled = TRUE; + if (!item) { - TRACE("Ignoring stream\n"); - return NULL; + goto Cleanup; } - /* Get the name of the node */ - WCHAR wszDisplayName[MAX_PATH]; - if (!ILGetDisplayNameEx(psfParent, pEltRelative, wszDisplayName, ILGDN_INFOLDER)) + x = LOWORD(lParam); + y = HIWORD(lParam); + if (x == -1 && y == -1) { - ERR("Failed to get node name\n"); - return NULL; + // TODO: grab position of tree item and position it correctly } - /* Get the icon of the node */ - INT iIcon = SHMapPIDLToSystemImageListIndex(psfParent, pEltRelative, NULL); + info = (NodeInfo*)TreeView_GetItemData(m_hWnd, item); + if (!info) + { + ERR("No node data, something has gone wrong !\n"); + goto Cleanup; + } + hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pFolder), + (LPCITEMIDLIST*)&pidlChild); + if (!SUCCEEDED(hr)) + { + ERR("Can't bind to folder!\n"); + goto Cleanup; + } + hr = pFolder->GetUIObjectOf(m_hWnd, 1, (LPCITEMIDLIST*)&pidlChild, IID_IContextMenu, + NULL, reinterpret_cast(&contextMenu)); + if (!SUCCEEDED(hr)) + { + ERR("Can't get IContextMenu interface\n"); + goto Cleanup; + } + treeMenu = CreatePopupMenu(); + hr = contextMenu->QueryContextMenu(treeMenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, + CMF_EXPLORE); + if (!SUCCEEDED(hr)) + { + WARN("Can't get context menu for item\n"); + DestroyMenu(treeMenu); + goto Cleanup; + } + uCommand = TrackPopupMenu(treeMenu, TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, + x, y, 0, m_hWnd, NULL); - NodeInfo* pChildInfo = new NodeInfo; - if (!pChildInfo) + ExecuteCommand(contextMenu, uCommand); +Cleanup: + if (treeMenu) + DestroyMenu(treeMenu); + bNavigating = TRUE; + TreeView_SelectItem(m_hWnd, oldSelected); + bNavigating = FALSE; + return TRUE; +} + +LRESULT CExplorerBand::ContextMenuHack(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + bHandled = FALSE; + if (uMsg == WM_RBUTTONDOWN) { - ERR("Failed to allocate NodeInfo\n"); - return FALSE; + TVHITTESTINFO info; + info.pt.x = LOWORD(lParam); + info.pt.y = HIWORD(lParam); + info.flags = TVHT_ONITEM; + info.hItem = NULL; + + // Save the current location + oldSelected = TreeView_GetSelection(m_hWnd); + + // Move to the item selected by the treeview (don't change right pane) + TreeView_HitTest(m_hWnd, &info); + bNavigating = TRUE; + TreeView_SelectItem(m_hWnd, info.hItem); + bNavigating = FALSE; } + return FALSE; /* let the wndproc process the message */ +} - // Store our node info - pChildInfo->absolutePidl = ILClone(pElt); - pChildInfo->relativePidl = ILClone(pEltRelative); - pChildInfo->expanded = FALSE; +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; + pInputSite->OnFocusChangeIS(reinterpret_cast(this), TRUE); + bHandled = FALSE; + return TRUE; +} + +LRESULT CExplorerBand::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + pInputSite->OnFocusChangeIS(reinterpret_cast(this), FALSE); + bHandled = FALSE; + return TRUE; +} + +// *** Helper functions *** +HTREEITEM CExplorerBand::InsertSubItem(HTREEITEM hParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort) +{ + WCHAR wszDisplayName[MAX_PATH]; + SHFILEINFOW shfi; + TV_INSERTSTRUCT tvInsert; + TV_ITEM* tvItem; + TVSORTCB sortCallback; + NodeInfo *pChildInfo; + HTREEITEM htiCreated; + // Set up our treeview template + tvItem = &tvInsert.item; tvInsert.hParent = hParent; tvInsert.hInsertAfter = TVI_LAST; - tvInsert.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN; - tvInsert.item.cchTextMax = MAX_PATH; - tvInsert.item.pszText = wszDisplayName; - tvInsert.item.iImage = tvInsert.item.iSelectedImage = iIcon; - tvInsert.item.cChildren = (attrs & SFGAO_HASSUBFOLDER) ? 1 : 0; - tvInsert.item.lParam = (LPARAM)pChildInfo; - htiCreated = TreeView_InsertItem(m_hWnd, &tvInsert); + SHGetFileInfoW((LPTSTR)pElt, NULL, &shfi, NULL, + SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_ATTRIBUTES | SHGFI_USEFILEATTRIBUTES); + GetDisplayName(pElt, wszDisplayName, MAX_PATH, SHGDN_INFOLDER); + + if (!(shfi.dwAttributes & SFGAO_STREAM)) + { + pChildInfo = new NodeInfo; + if (!pChildInfo) + { + return FALSE; + } + + tvItem->mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN; + tvItem->cchTextMax = MAX_PATH; + tvItem->pszText = wszDisplayName; + tvItem->iImage = tvItem->iSelectedImage = shfi.iIcon; + tvItem->cChildren = (shfi.dwAttributes & SFGAO_HASSUBFOLDER) ? 1 : 0; + + // Store our node info + pChildInfo->absolutePidl = ILClone(pElt); + pChildInfo->relativePidl = ILClone(pEltRelative); + pChildInfo->expanded = FALSE; + + tvItem->lParam = (LPARAM)pChildInfo; + htiCreated = TreeView_InsertItem(m_hWnd, &tvInsert); + } + else + { + TRACE("Is a stream\n"); + } + if (bSort) + { + sortCallback.hParent = hParent; + sortCallback.lpfnCompare = CompareTreeItems; + sortCallback.lParam = (LPARAM)this; + SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback); + } return htiCreated; } @@ -270,6 +626,7 @@ ULONG fetched; ULONG uItemCount; CComPtr pFolder; + TVSORTCB sortCallback; entry = pNodeInfo->absolutePidl; fetched = 1; @@ -321,12 +678,17 @@ LPITEMIDLIST pidlSubComplete; pidlSubComplete = ILCombine(entry, pidlSub); - if (InsertItem(hItem, pFolder, pidlSubComplete, pidlSub, FALSE)) + if (InsertSubItem(hItem, pidlSubComplete, pidlSub, FALSE)) uItemCount++; ILFree(pidlSubComplete); 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); @@ -334,6 +696,328 @@ return (uItemCount > 0) ? TRUE : FALSE; } +/** + * Navigate to a given PIDL in the treeview, and return matching tree item handle + * - dest: The absolute PIDL we should navigate in the treeview + * - item: Handle of the tree item matching the PIDL + * - bExpand: expand collapsed nodes in order to find the right element + * - bInsert: insert the element at the right place if we don't find it + * - bSelect: select the item after we found it + */ +BOOL CExplorerBand::NavigateToPIDL(LPITEMIDLIST dest, HTREEITEM *item, BOOL bExpand, BOOL bInsert, + BOOL bSelect) +{ + HTREEITEM current; + HTREEITEM tmp; + HTREEITEM parent; + BOOL found; + NodeInfo *nodeData; + LPITEMIDLIST relativeChild; + TVITEM tvItem; + + if (!item) + return FALSE; + + found = FALSE; + current = hRoot; + parent = NULL; + while(!found) + { + nodeData = (NodeInfo*)TreeView_GetItemData(m_hWnd, current); + if (!nodeData) + { + ERR("Something has gone wrong, no data associated to node !\n"); + *item = NULL; + return FALSE; + } + // If we found our node, give it back + if (!pDesktop->CompareIDs(0, nodeData->absolutePidl, dest)) + { + if (bSelect) + TreeView_SelectItem(m_hWnd, current); + *item = current; + return TRUE; + } + + // Check if we are a parent of the requested item + relativeChild = ILFindChild(nodeData->absolutePidl, dest); + if (relativeChild != 0) + { + // Notify treeview we have children + tvItem.mask = TVIF_CHILDREN; + tvItem.hItem = current; + tvItem.cChildren = 1; + TreeView_SetItem(m_hWnd, &tvItem); + + // If we can expand and the node isn't expanded yet, do it + if (bExpand) + { + if (!nodeData->expanded) + InsertSubitems(current, nodeData); + TreeView_Expand(m_hWnd, current, TVE_EXPAND); + } + + // Try to get a child + tmp = TreeView_GetChild(m_hWnd, current); + if (tmp) + { + // We have a child, let's continue with it + parent = current; + current = tmp; + continue; + } + + if (bInsert && nodeData->expanded) + { + // Happens when we have to create a subchild inside a child + current = InsertSubItem(current, dest, relativeChild, TRUE); + } + // We end up here, without any children, so we found nothing + // Tell the parent node it has children + ZeroMemory(&tvItem, sizeof(tvItem)); + *item = NULL; + return FALSE; + } + + // Find sibling + tmp = TreeView_GetNextSibling(m_hWnd, current); + if (tmp) + { + current = tmp; + continue; + } + if (bInsert) + { + current = InsertSubItem(parent, dest, ILFindLastID(dest), TRUE); + *item = current; + return TRUE; + } + *item = NULL; + return FALSE; + } + return FALSE; +} + +BOOL CExplorerBand::NavigateToCurrentFolder() +{ + LPITEMIDLIST explorerPidl; + CComPtr pBrowserService; + HRESULT hr; + HTREEITEM dummy; + BOOL result; + explorerPidl = NULL; + hr = pServiceProvider->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pBrowserService)); + if (!SUCCEEDED(hr)) + { + ERR("Can't get IBrowserService !\n"); + return FALSE; + } + + hr = pBrowserService->GetPidl(&explorerPidl); + if (!SUCCEEDED(hr) || !explorerPidl) + { + ERR("Unable to get browser PIDL !\n"); + return FALSE; + } + bNavigating = TRUE; + /* find PIDL into our explorer */ + result = NavigateToPIDL(explorerPidl, &dummy, TRUE, FALSE, TRUE); + bNavigating = FALSE; + 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 = (NodeInfo*)TreeView_GetItemData(m_hWnd, 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 = (NodeInfo*)TreeView_GetItemData(m_hWnd, 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); +} + +static LPARAM TreeView_GetItemData(HWND hwndTreeView, HTREEITEM hItem) +{ + TVITEM tvItem; + + tvItem.mask = TVIF_PARAM; + tvItem.hItem = hItem; + + if (!TreeView_GetItem(hwndTreeView, &tvItem)) + return 0; + + return tvItem.lParam; +} + + // *** IOleWindow methods *** HRESULT STDMETHODCALLTYPE CExplorerBand::GetWindow(HWND *lphwnd) { @@ -440,21 +1124,40 @@ if (pUnkSite != pSite) { + // Release all refs to the old site pSite = NULL; + pServiceProvider = NULL; + pBandOle = NULL; + pInputSite = NULL; } if(!pUnkSite) return S_OK; - - hr = IUnknown_GetWindow(pUnkSite, &parentWnd); + pSite = pUnkSite; + // Grab IServiceProvider for later use + hr = pSite->QueryInterface(IID_PPV_ARG(IServiceProvider, &pServiceProvider)); if (!SUCCEEDED(hr)) { + ERR("Query IServiceProvider interface for site failed with %08lx\n", hr); + return E_INVALIDARG; + } + hr = pSite->QueryInterface(IID_PPV_ARG(IOleWindow, &pBandOle)); + if (!SUCCEEDED(hr)) + { + ERR("Query IOleProvider interface for site failed with %08lx\n", hr); + return E_INVALIDARG; + } + hr = pBandOle->GetWindow(&parentWnd); + if (!SUCCEEDED(hr)) + { ERR("Could not get parent's window ! Status: %08lx\n", hr); return E_INVALIDARG; } - - pSite = pUnkSite; - + hr = pSite->QueryInterface(IID_PPV_ARG(IInputObjectSite, &pInputSite)); + if(!SUCCEEDED(hr)) + { + ERR("Can't get IInputObjectSite window, status: %08lx\n", hr); + } if (m_hWnd) { // Change its parent @@ -568,6 +1271,7 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::GetSizeMax(ULARGE_INTEGER *pcbSize) { + // TODO: calculate max size UNIMPLEMENTED; return E_NOTIMPL; } @@ -576,6 +1280,7 @@ // *** IWinEventHandler methods *** HRESULT STDMETHODCALLTYPE CExplorerBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) { + BOOL bHandled; if (uMsg == WM_NOTIFY) { NMHDR *pNotifyHeader = (NMHDR*)lParam; @@ -585,8 +1290,83 @@ *theResult = OnTreeItemExpanding((LPNMTREEVIEW)lParam); break; case TVN_SELCHANGED: - OnSelectionChanged((LPNMTREEVIEW)lParam); + OnTreeItemChanged((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) + DWORD dwAttr = SFGAO_CANRENAME; + LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam; + CComPtr pParent; + LPCITEMIDLIST pChild; + HRESULT hr; + + + *theResult = 1; + NodeInfo *info = (NodeInfo*)TreeView_GetItemData(m_hWnd, dispInfo->item.hItem); + if (!info) + return E_FAIL; + hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pChild); + if (!SUCCEEDED(hr) || !pParent.p) + return E_FAIL; + + hr = pParent->GetAttributesOf(1, &pChild, &dwAttr); + if (SUCCEEDED(hr) && (dwAttr & SFGAO_CANRENAME)) + *theResult = 0; + return S_OK; + } + case TVN_ENDLABELEDITW: + { + LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam; + NodeInfo *info = (NodeInfo*)TreeView_GetItemData(m_hWnd, dispInfo->item.hItem); + HRESULT hr; + + *theResult = 0; + if (dispInfo->item.pszText) + { + LPITEMIDLIST pidlNew; + CComPtr pParent; + LPCITEMIDLIST pidlChild; + + hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pidlChild); + if (!SUCCEEDED(hr) || !pParent.p) + return E_FAIL; + + hr = pParent->SetNameOf(0, pidlChild, dispInfo->item.pszText, SHGDN_INFOLDER, &pidlNew); + if(SUCCEEDED(hr) && pidlNew) + { + CComPtr pPersist; + LPITEMIDLIST pidlParent, pidlNewAbs; + + hr = pParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pPersist)); + if(!SUCCEEDED(hr)) + return E_FAIL; + + hr = pPersist->GetCurFolder(&pidlParent); + if(!SUCCEEDED(hr)) + return E_FAIL; + pidlNewAbs = ILCombine(pidlParent, pidlNew); + + // Navigate to our new location + UpdateBrowser(pidlNewAbs); + + ILFree(pidlNewAbs); + ILFree(pidlNew); + *theResult = 1; + } + return S_OK; + } + } default: break; } @@ -599,6 +1379,7 @@ return (hWnd == m_hWnd) ? S_OK : S_FALSE; } + // *** IBandNavigate methods *** HRESULT STDMETHODCALLTYPE CExplorerBand::Select(long paramC) { @@ -606,6 +1387,7 @@ return E_NOTIMPL; } + // *** INamespaceProxy *** HRESULT STDMETHODCALLTYPE CExplorerBand::GetNavigateTarget(long paramC, long param10, long param14) { @@ -637,6 +1419,7 @@ return E_NOTIMPL; } + // *** IDispatch methods *** HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfoCount(UINT *pctinfo) { @@ -658,44 +1441,135 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { - UNIMPLEMENTED; - return E_NOTIMPL; + switch (dispIdMember) + { + case DISPID_DOWNLOADCOMPLETE: + case DISPID_NAVIGATECOMPLETE2: + TRACE("DISPID_NAVIGATECOMPLETE2 received\n"); + NavigateToCurrentFolder(); + return S_OK; + } + TRACE("Unknown dispid requested: %08x\n", dispIdMember); + return E_INVALIDARG; } // *** IDropTarget methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) + +HRESULT 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 = (NodeInfo*)TreeView_GetItemData(m_hWnd, 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; } Index: explorerband.h =================================================================== --- explorerband.h (revision 71510) +++ explorerband.h (working copy) @@ -1,23 +1,3 @@ -/* - * ReactOS Explorer - * - * Copyright 2016 Sylvain Deverre - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - #pragma once #define WM_USER_SHELLEVENT WM_USER+88 @@ -49,33 +29,62 @@ LPITEMIDLIST relativePidl; BOOL expanded; }; - // *** BaseBarSite information *** CComPtr pSite; + CComPtr pServiceProvider; + CComPtr pInputSite; + CComPtr pBandOle; CComPtr pDesktop; - + // *** tree explorer band stuff *** + RECT hwndClient; BOOL fVisible; + BOOL bNavigating; BOOL bFocused; DWORD dwBandID; 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 *** + void OnTreeItemChanged(LPNMTREEVIEW pnmtv); 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); HRESULT UpdateBrowser(LPITEMIDLIST pidlGoto); - HTREEITEM InsertItem(HTREEITEM hParent, IShellFolder *psfParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort); + HTREEITEM InsertSubItem(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(); @@ -137,7 +146,7 @@ virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo); virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId); virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr); - + // *** IDropTarget methods *** virtual HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect); virtual HRESULT STDMETHODCALLTYPE DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect); @@ -170,8 +179,13 @@ END_COM_MAP() 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); \ No newline at end of file +HRESULT WINAPI CExplorerBand_Constructor(REFIID riid, LPVOID *ppv);