diff --git a/dll/win32/shdocvw/CExplorerBand.cpp b/dll/win32/shdocvw/CExplorerBand.cpp index 8a60321a706..aa70c06c5c2 100644 --- a/dll/win32/shdocvw/CExplorerBand.cpp +++ b/dll/win32/shdocvw/CExplorerBand.cpp @@ -7,8 +7,6 @@ */ #include "objects.h" -#include -#include #include WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); @@ -21,829 +19,195 @@ WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); */ CExplorerBand::CExplorerBand() - : m_pSite(NULL) - , m_fVisible(FALSE) - , m_mtxBlockNavigate(0) - , m_dwBandID(0) - , m_isEditing(FALSE) - , m_pidlCurrent(NULL) { } CExplorerBand::~CExplorerBand() { - if (m_pidlCurrent) - { - ILFree(m_pidlCurrent); - } } -void CExplorerBand::InitializeExplorerBand() +STDMETHODIMP CExplorerBand::GetClassID(CLSID *pClassID) { - // Init the treeview here - HRESULT hr = SHGetDesktopFolder(&m_pDesktop); - if (FAILED_UNEXPECTEDLY(hr)) - return; + if (!pClassID) + return E_POINTER; + *pClassID = CLSID_ExplorerBand; + return S_OK; +} - LPITEMIDLIST pidl; - hr = SHGetFolderLocation(m_hWnd, CSIDL_DESKTOP, NULL, 0, &pidl); - if (FAILED_UNEXPECTEDLY(hr)) - return; +INT CExplorerBand::_GetRootCsidl() +{ + return CSIDL_DESKTOP; +} - IImageList * piml; - hr = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &piml)); - if (FAILED_UNEXPECTEDLY(hr)) - return; +DWORD CExplorerBand::_GetTVStyle() +{ + // Remove TVS_SINGLEEXPAND for now since it has strange behaviour + return WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TVS_HASLINES | + TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_EDITLABELS /* | TVS_SINGLEEXPAND*/; +} - TreeView_SetImageList(m_hWnd, (HIMAGELIST)piml, TVSIL_NORMAL); +DWORD CExplorerBand::_GetTVExStyle() +{ + return 0; +} + +DWORD CExplorerBand::_GetEnumFlags() +{ + return SHCONTF_FOLDERS; +} + +BOOL CExplorerBand::_GetTitle(LPWSTR pszTitle, INT cchTitle) +{ + return ::LoadStringW(instance, IDS_FOLDERSLABEL, pszTitle, cchTitle); +} + +BOOL CExplorerBand::_WantsRootItem() +{ + return TRUE; +} + +// Called when the user has selected an item. +STDMETHODIMP CExplorerBand::OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) +{ + return Invoke(pidl); +} + +// Handles a user action on an item. +STDMETHODIMP CExplorerBand::Invoke(_In_ PCIDLIST_ABSOLUTE pidl) +{ + /* Prevents navigation if selection is initiated inside the band */ + if (m_mtxBlockNavigate) + return S_OK; + + _UpdateBrowser(pidl); + m_hwndTreeView.SetFocus(); + return S_OK; +} + +void CExplorerBand::_SortItems(HTREEITEM hParent) +{ + TVSORTCB sortCallback; + sortCallback.hParent = hParent; + sortCallback.lpfnCompare = _CompareTreeItems; + sortCallback.lParam = (LPARAM)(PVOID)m_pDesktop; // m_pDesktop is not a pointer + TreeView_SortChildrenCB(m_hwndTreeView, &sortCallback, 0); +} + +INT CALLBACK CExplorerBand::_CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3) +{ + CItemData *info1 = (CItemData*)p1; + CItemData *info2 = (CItemData*)p2; + IShellFolder *pDesktop = (IShellFolder *)p3; + HRESULT hr = pDesktop->CompareIDs(0, info1->absolutePidl, info2->absolutePidl); + if (FAILED(hr)) + return 0; + return (SHORT)HRESULT_CODE(hr); +} + +HRESULT CExplorerBand::_CreateTreeView(HWND hwndParent) +{ + HRESULT hr = CNSCBand::_CreateTreeView(hwndParent); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; // Insert the root node - m_hRoot = InsertItem(NULL, m_pDesktop, pidl, pidl, FALSE); + m_hRoot = _InsertItem(NULL, m_pDesktop, m_pidlRoot, m_pidlRoot, FALSE); if (!m_hRoot) { ERR("Failed to create root item\n"); - return; + return E_FAIL; } - - NodeInfo* pNodeInfo = GetNodeInfo(m_hRoot); - - // Insert child nodes - InsertSubitems(m_hRoot, pNodeInfo); - TreeView_Expand(m_hWnd, m_hRoot, TVE_EXPAND); + TreeView_Expand(m_hwndTreeView, m_hRoot, TVE_EXPAND); // Navigate to current folder position - NavigateToCurrentFolder(); - -#define TARGET_EVENTS ( \ - SHCNE_DRIVEADD | SHCNE_MKDIR | SHCNE_CREATE | SHCNE_DRIVEREMOVED | SHCNE_RMDIR | \ - SHCNE_DELETE | SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEDIR | \ - SHCNE_UPDATEITEM | SHCNE_ASSOCCHANGED \ -) - // Register shell notification - SHChangeNotifyEntry shcne = { pidl, TRUE }; - m_shellRegID = SHChangeNotifyRegister(m_hWnd, - SHCNRF_NewDelivery | SHCNRF_ShellLevel, - TARGET_EVENTS, - WM_USER_SHELLEVENT, - 1, &shcne); - if (!m_shellRegID) - { - ERR("Something went wrong, error %08x\n", GetLastError()); - } + _NavigateToCurrentFolder(); // Register browser connection endpoint CComPtr browserService; hr = IUnknown_QueryService(m_pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService)); if (FAILED_UNEXPECTEDLY(hr)) - return; + return hr; + // Communicate via IDispatch hr = AtlAdvise(browserService, dynamic_cast(this), DIID_DWebBrowserEvents, &m_adviseCookie); if (FAILED_UNEXPECTEDLY(hr)) - return; + return hr; - ILFree(pidl); -} - -void CExplorerBand::DestroyExplorerBand() -{ - HRESULT hr; - CComPtr browserService; - - TRACE("Cleaning up explorer band ...\n"); - - hr = IUnknown_QueryService(m_pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService)); - if (FAILED_UNEXPECTEDLY(hr)) - return; - - hr = AtlUnadvise(browserService, DIID_DWebBrowserEvents, m_adviseCookie); - /* Remove all items of the treeview */ - RevokeDragDrop(m_hWnd); - TreeView_DeleteAllItems(m_hWnd); - m_pDesktop = NULL; - m_hRoot = NULL; - TRACE("Cleanup done !\n"); -} - -CExplorerBand::NodeInfo* CExplorerBand::GetNodeInfo(HTREEITEM hItem) -{ - TVITEM tvItem; - - tvItem.mask = TVIF_PARAM; - tvItem.hItem = hItem; - - if (!TreeView_GetItem(m_hWnd, &tvItem)) - return 0; - - return reinterpret_cast(tvItem.lParam); -} - -static HRESULT GetCurrentLocationFromView(IShellView &View, PIDLIST_ABSOLUTE &pidl) -{ - CComPtr pfv; - CComPtr psf; - HRESULT hr = View.QueryInterface(IID_PPV_ARG(IFolderView, &pfv)); - if (SUCCEEDED(hr) && SUCCEEDED(hr = pfv->GetFolder(IID_PPV_ARG(IShellFolder, &psf)))) - hr = SHELL_GetIDListFromObject(psf, &pidl); return hr; } -HRESULT CExplorerBand::GetCurrentLocation(PIDLIST_ABSOLUTE &pidl) +void CExplorerBand::_DestroyTreeView() { - pidl = NULL; - CComPtr psb; - HRESULT hr = IUnknown_QueryService(m_pSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb)); + CComPtr browserService; + HRESULT hr = IUnknown_QueryService(m_pSite, SID_SWebBrowserApp, + IID_PPV_ARG(IWebBrowser2, &browserService)); if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - CComPtr pbs; - if (SUCCEEDED(hr = psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) - if (SUCCEEDED(hr = pbs->GetPidl(&pidl)) && pidl) - return hr; - - CComPtr psv; - if (!FAILED_UNEXPECTEDLY(hr = psb->QueryActiveShellView(&psv))) - if (SUCCEEDED(hr = psv.p ? GetCurrentLocationFromView(*psv.p, pidl) : E_FAIL)) - return hr; - return hr; -} - -HRESULT CExplorerBand::IsCurrentLocation(PCIDLIST_ABSOLUTE pidl) -{ - if (!pidl) - return E_INVALIDARG; - HRESULT hr = E_FAIL; - PIDLIST_ABSOLUTE location = m_pidlCurrent; - if (location || SUCCEEDED(hr = GetCurrentLocation(location))) - hr = SHELL_IsEqualAbsoluteID(location, pidl) ? S_OK : S_FALSE; - if (location != m_pidlCurrent) - ILFree(location); - return hr; -} - -HRESULT CExplorerBand::ExecuteCommand(CComPtr& menu, UINT nCmd) -{ - CComPtr pBrowserOleWnd; - CMINVOKECOMMANDINFO cmi; - HWND browserWnd; - HRESULT hr; - - hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IOleWindow, &pBrowserOleWnd)); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - hr = pBrowserOleWnd->GetWindow(&browserWnd); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - ZeroMemory(&cmi, sizeof(cmi)); - cmi.cbSize = sizeof(cmi); - cmi.lpVerb = MAKEINTRESOURCEA(nCmd); - cmi.hwnd = browserWnd; - 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) -{ - CComPtr pBrowserService; - HRESULT hr; - - hr = IUnknown_QueryService(m_pSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &pBrowserService)); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - hr = pBrowserService->BrowseObject(pidlGoto, SBSP_SAMEBROWSER | SBSP_ABSOLUTE); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - ILFree(m_pidlCurrent); - return SHILClone(pidlGoto, &m_pidlCurrent); -} - -// *** notifications handling *** -BOOL CExplorerBand::OnTreeItemExpanding(LPNMTREEVIEW pnmtv) -{ - NodeInfo *pNodeInfo; - - if (pnmtv->action == TVE_COLLAPSE) { - if (pnmtv->itemNew.hItem == m_hRoot) - { - // Prenvent root from collapsing - pnmtv->itemNew.mask |= TVIF_STATE; - pnmtv->itemNew.stateMask |= TVIS_EXPANDED; - pnmtv->itemNew.state &= ~TVIS_EXPANDED; - pnmtv->action = TVE_EXPAND; - return TRUE; - } - } - if (pnmtv->action == TVE_EXPAND) { - // Grab our directory PIDL - pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem); - // We have it, let's try - if (pNodeInfo && !pNodeInfo->expanded) - if (!InsertSubitems(pnmtv->itemNew.hItem, pNodeInfo)) { - // remove subitem "+" since we failed to add subitems - TV_ITEM tvItem; - - tvItem.mask = TVIF_CHILDREN; - tvItem.hItem = pnmtv->itemNew.hItem; - tvItem.cChildren = 0; - - TreeView_SetItem(m_hWnd, &tvItem); - } - } - return FALSE; -} - -BOOL CExplorerBand::OnTreeItemDeleted(LPNMTREEVIEW pnmtv) -{ - // Navigate to parent when deleting selected item - HTREEITEM hItem = pnmtv->itemOld.hItem; - HTREEITEM hParent = TreeView_GetParent(m_hWnd, hItem); - if (hParent && TreeView_GetSelection(m_hWnd) == hItem) - TreeView_SelectItem(m_hWnd, hParent); - - /* Destroy memory associated to our node */ - NodeInfo* pNode = GetNodeInfo(hItem); - if (!pNode) - return FALSE; - - ILFree(pNode->relativePidl); - ILFree(pNode->absolutePidl); - delete pNode; - - return TRUE; -} - -void CExplorerBand::OnSelectionChanged(LPNMTREEVIEW pnmtv) -{ - NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem); - - /* Prevents navigation if selection is initiated inside the band */ - if (m_mtxBlockNavigate) return; - UpdateBrowser(pNodeInfo->absolutePidl); + AtlUnadvise(browserService, DIID_DWebBrowserEvents, m_adviseCookie); - SetFocus(); - // Expand the node - //TreeView_Expand(m_hWnd, pnmtv->itemNew.hItem, TVE_EXPAND); + CNSCBand::_DestroyTreeView(); } -void CExplorerBand::OnTreeItemDragging(LPNMTREEVIEW pnmtv, BOOL isRightClick) +// *** IDispatch methods *** + +STDMETHODIMP CExplorerBand::GetTypeInfoCount(UINT *pctinfo) { - if (!pnmtv->itemNew.lParam) - return; - - NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem); - - HRESULT hr; - CComPtr pSrcFolder; - LPCITEMIDLIST pLast; - hr = SHBindToParent(pNodeInfo->absolutePidl, IID_PPV_ARG(IShellFolder, &pSrcFolder), &pLast); - if (!SUCCEEDED(hr)) - return; - - SFGAOF attrs = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK; - pSrcFolder->GetAttributesOf(1, &pLast, &attrs); - - DWORD dwEffect = 0; - if (attrs & SFGAO_CANCOPY) - dwEffect |= DROPEFFECT_COPY; - if (attrs & SFGAO_CANMOVE) - dwEffect |= DROPEFFECT_MOVE; - if (attrs & SFGAO_CANLINK) - dwEffect |= DROPEFFECT_LINK; - - CComPtr pObj; - hr = pSrcFolder->GetUIObjectOf(m_hWnd, 1, &pLast, IID_IDataObject, 0, (LPVOID*)&pObj); - if (!SUCCEEDED(hr)) - return; - - DoDragDrop(pObj, this, dwEffect, &dwEffect); + UNIMPLEMENTED; + return E_NOTIMPL; } -// *** ATL event handlers *** -LRESULT CExplorerBand::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +STDMETHODIMP CExplorerBand::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { - HTREEITEM item; - NodeInfo *info; - HMENU treeMenu; - POINT pt; - CComPtr pFolder; - CComPtr contextMenu; - HRESULT hr; - UINT uCommand; - LPITEMIDLIST pidlChild; - UINT cmdBase = max(FCIDM_SHVIEWFIRST, 1); - UINT cmf = CMF_EXPLORE; - SFGAOF attr = SFGAO_CANRENAME; - BOOL startedRename = FALSE; + UNIMPLEMENTED; + return E_NOTIMPL; +} - treeMenu = NULL; - item = TreeView_GetSelection(m_hWnd); - bHandled = TRUE; - if (!item) - { - goto Cleanup; - } +STDMETHODIMP CExplorerBand::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} - pt.x = LOWORD(lParam); - pt.y = HIWORD(lParam); - if ((UINT)lParam == (UINT)-1) +STDMETHODIMP +CExplorerBand::Invoke( + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + switch (dispIdMember) { - RECT r; - if (TreeView_GetItemRect(m_hWnd, item, &r, TRUE)) + case DISPID_DOWNLOADCOMPLETE: + case DISPID_NAVIGATECOMPLETE2: { - pt.x = (r.left + r.right) / 2; // Center of - pt.y = (r.top + r.bottom) / 2; // item rectangle - } - ClientToScreen(&pt); - } - - info = GetNodeInfo(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; - } - - IUnknown_SetSite(contextMenu, (IDeskBand *)this); - - if (SUCCEEDED(pFolder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlChild, &attr)) && (attr & SFGAO_CANRENAME)) - cmf |= CMF_CANRENAME; - - treeMenu = CreatePopupMenu(); - hr = contextMenu->QueryContextMenu(treeMenu, 0, cmdBase, FCIDM_SHVIEWLAST, cmf); - 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, - pt.x, pt.y, 0, m_hWnd, NULL); - if (uCommand) - { - uCommand -= cmdBase; - - // Do DFM_CMD_RENAME in the treeview - if ((cmf & CMF_CANRENAME) && SHELL_IsVerb(contextMenu, uCommand, L"rename")) - { - HTREEITEM oldSelected = m_oldSelected; - SetFocus(); - startedRename = TreeView_EditLabel(m_hWnd, item) != NULL; - m_oldSelected = oldSelected; // Restore after TVN_BEGINLABELEDIT - goto Cleanup; - } - - hr = ExecuteCommand(contextMenu, uCommand); - } - -Cleanup: - if (contextMenu) - IUnknown_SetSite(contextMenu, NULL); - if (treeMenu) - DestroyMenu(treeMenu); - if (startedRename) - { - // The treeview disables drawing of the edited item so we must make sure - // the correct item is selected (on right-click -> rename on not-current folder). - // TVN_ENDLABELEDIT becomes responsible for restoring the selection. - } - else - { - ++m_mtxBlockNavigate; - TreeView_SelectItem(m_hWnd, m_oldSelected); - --m_mtxBlockNavigate; - } - return TRUE; -} - -LRESULT CExplorerBand::ContextMenuHack(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) -{ - bHandled = FALSE; - if (uMsg == WM_RBUTTONDOWN) - { - TVHITTESTINFO info; - info.pt.x = LOWORD(lParam); - info.pt.y = HIWORD(lParam); - info.flags = TVHT_ONITEM; - info.hItem = NULL; - - // Save the current location - m_oldSelected = TreeView_GetSelection(m_hWnd); - - // Move to the item selected by the treeview (don't change right pane) - TreeView_HitTest(m_hWnd, &info); - ++m_mtxBlockNavigate; - TreeView_SelectItem(m_hWnd, info.hItem); - --m_mtxBlockNavigate; - } - return FALSE; /* let the wndproc process the message */ -} - -// WM_USER_SHELLEVENT -LRESULT CExplorerBand::OnShellEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) -{ - // We use SHCNRF_NewDelivery method - HANDLE hChange = (HANDLE)wParam; - DWORD dwProcID = (DWORD)lParam; - - PIDLIST_ABSOLUTE *ppidl = NULL; - LONG lEvent; - HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &ppidl, &lEvent); - if (hLock == NULL) - { - ERR("hLock == NULL\n"); - return 0; - } - - OnChangeNotify(ppidl[0], ppidl[1], (lEvent & ~SHCNE_INTERRUPT)); - - SHChangeNotification_Unlock(hLock); - return 0; -} - -BOOL -CExplorerBand::IsTreeItemInEnum( - _In_ HTREEITEM hItem, - _In_ IEnumIDList *pEnum) -{ - NodeInfo* pNodeInfo = GetNodeInfo(hItem); - if (!pNodeInfo) - return FALSE; - - pEnum->Reset(); - - CComHeapPtr pidlTemp; - while (pEnum->Next(1, &pidlTemp, NULL) == S_OK) - { - if (ILIsEqual(pidlTemp, pNodeInfo->relativePidl)) - return TRUE; - - pidlTemp.Free(); - } - - return FALSE; -} - -BOOL -CExplorerBand::TreeItemHasThisChild( - _In_ HTREEITEM hItem, - _In_ PCITEMID_CHILD pidlChild) -{ - for (hItem = TreeView_GetChild(m_hWnd, hItem); hItem; - hItem = TreeView_GetNextSibling(m_hWnd, hItem)) - { - NodeInfo* pNodeInfo = GetNodeInfo(hItem); - if (ILIsEqual(pNodeInfo->relativePidl, pidlChild)) - return TRUE; - } - - return FALSE; -} - -HRESULT -CExplorerBand::GetItemEnum( - _Out_ CComPtr& pEnum, - _In_ HTREEITEM hItem) -{ - NodeInfo* pNodeInfo = GetNodeInfo(hItem); - - CComPtr psfDesktop; - HRESULT hr = SHGetDesktopFolder(&psfDesktop); - if (FAILED(hr)) - return hr; - - CComPtr pFolder; - hr = psfDesktop->BindToObject(pNodeInfo->absolutePidl, NULL, IID_PPV_ARG(IShellFolder, &pFolder)); - if (FAILED(hr)) - return hr; - - return pFolder->EnumObjects(NULL, SHCONTF_FOLDERS, &pEnum); -} - -BOOL CExplorerBand::ItemHasAnyChild(_In_ HTREEITEM hItem) -{ - CComPtr pEnum; - HRESULT hr = GetItemEnum(pEnum, hItem); - if (FAILED(hr)) - return FALSE; - - CComHeapPtr pidlTemp; - hr = pEnum->Next(1, &pidlTemp, NULL); - return SUCCEEDED(hr); -} - -void CExplorerBand::RefreshRecurse(_In_ HTREEITEM hTarget) -{ - NodeInfo* pNodeInfo = GetNodeInfo(hTarget); - - CComPtr pEnum; - HRESULT hrEnum = GetItemEnum(pEnum, hTarget); - - // Delete zombie items - HTREEITEM hItem, hNextItem; - for (hItem = TreeView_GetChild(m_hWnd, hTarget); hItem; hItem = hNextItem) - { - hNextItem = TreeView_GetNextSibling(m_hWnd, hItem); - - if (SUCCEEDED(hrEnum) && !IsTreeItemInEnum(hItem, pEnum)) - TreeView_DeleteItem(m_hWnd, hItem); - } - - pEnum = NULL; - hrEnum = GetItemEnum(pEnum, hTarget); - - // Insert new items and update items - if (SUCCEEDED(hrEnum)) - { - CComHeapPtr pidlTemp; - while (pEnum->Next(1, &pidlTemp, NULL) == S_OK) - { - if (!TreeItemHasThisChild(hTarget, pidlTemp)) - { - CComHeapPtr pidlAbsolute(ILCombine(pNodeInfo->absolutePidl, pidlTemp)); - InsertItem(hTarget, pidlAbsolute, pidlTemp, TRUE); - } - pidlTemp.Free(); + TRACE("dispId %d received\n", dispIdMember); + _NavigateToCurrentFolder(); + return S_OK; } } - - // Update children and recurse - for (hItem = TreeView_GetChild(m_hWnd, hTarget); hItem; hItem = hNextItem) - { - hNextItem = TreeView_GetNextSibling(m_hWnd, hItem); - - TV_ITEMW item = { TVIF_HANDLE | TVIF_CHILDREN }; - item.hItem = hItem; - item.cChildren = ItemHasAnyChild(hItem); - TreeView_SetItem(m_hWnd, &item); - - if (TreeView_GetItemState(m_hWnd, hItem, TVIS_EXPANDEDONCE) & TVIS_EXPANDEDONCE) - RefreshRecurse(hItem); - } + TRACE("Unknown dispid requested: %08x\n", dispIdMember); + return E_INVALIDARG; } -void CExplorerBand::Refresh() +BOOL CExplorerBand::_NavigateToCurrentFolder() { - SendMessage(WM_SETREDRAW, FALSE, 0); - RefreshRecurse(m_hRoot); - SendMessage(WM_SETREDRAW, TRUE, 0); - InvalidateRect(NULL, TRUE); -} - -#define TIMER_ID_REFRESH 9999 - -LRESULT CExplorerBand::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) -{ - if (wParam != TIMER_ID_REFRESH) - return 0; - - KillTimer(TIMER_ID_REFRESH); - - // FIXME: Avoid full refresh and optimize for speed - Refresh(); - - return 0; -} - -void -CExplorerBand::OnChangeNotify( - _In_opt_ LPCITEMIDLIST pidl0, - _In_opt_ LPCITEMIDLIST pidl1, - _In_ LONG lEvent) -{ - switch (lEvent) - { - case SHCNE_DRIVEADD: - case SHCNE_MKDIR: - case SHCNE_CREATE: - case SHCNE_DRIVEREMOVED: - case SHCNE_RMDIR: - case SHCNE_DELETE: - case SHCNE_RENAMEFOLDER: - case SHCNE_RENAMEITEM: - case SHCNE_UPDATEDIR: - case SHCNE_UPDATEITEM: - case SHCNE_ASSOCCHANGED: - { - KillTimer(TIMER_ID_REFRESH); - SetTimer(TIMER_ID_REFRESH, 500, NULL); - break; - } - default: - { - TRACE("lEvent: 0x%08lX\n", lEvent); - break; - } - } -} - -LRESULT CExplorerBand::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) -{ - m_bFocused = TRUE; - IUnknown_OnFocusChangeIS(m_pSite, reinterpret_cast(this), TRUE); - bHandled = FALSE; - return TRUE; -} - -LRESULT CExplorerBand::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) -{ - IUnknown_OnFocusChangeIS(m_pSite, reinterpret_cast(this), FALSE); - bHandled = FALSE; - return TRUE; -} - -// *** Helper functions *** -HTREEITEM -CExplorerBand::InsertItem( - _In_opt_ HTREEITEM hParent, - _Inout_ IShellFolder *psfParent, - _In_ LPCITEMIDLIST pElt, - _In_ LPCITEMIDLIST pEltRelative, - _In_ BOOL bSort) -{ - TV_INSERTSTRUCT tvInsert; - HTREEITEM htiCreated; - - /* Get the attributes of the node */ - SFGAOF attrs = SFGAO_STREAM | SFGAO_HASSUBFOLDER; - HRESULT hr = psfParent->GetAttributesOf(1, &pEltRelative, &attrs); + CComHeapPtr pidl; + HRESULT hr = _GetCurrentLocation(&pidl); if (FAILED_UNEXPECTEDLY(hr)) - return NULL; - - /* Ignore streams */ - if (attrs & SFGAO_STREAM) - { - TRACE("Ignoring stream\n"); - return NULL; - } - - /* Get the name of the node */ - WCHAR wszDisplayName[MAX_PATH]; - STRRET strret; - hr = psfParent->GetDisplayNameOf(pEltRelative, SHGDN_INFOLDER, &strret); - if (FAILED_UNEXPECTEDLY(hr)) - return NULL; - - hr = StrRetToBufW(&strret, pEltRelative, wszDisplayName, MAX_PATH); - if (FAILED_UNEXPECTEDLY(hr)) - return NULL; - - /* Get the icon of the node */ - INT iIcon = SHMapPIDLToSystemImageListIndex(psfParent, pEltRelative, NULL); - - NodeInfo* pChildInfo = new NodeInfo; - if (!pChildInfo) - { - ERR("Failed to allocate NodeInfo\n"); return FALSE; - } - // Store our node info - pChildInfo->absolutePidl = ILClone(pElt); - pChildInfo->relativePidl = ILClone(pEltRelative); - pChildInfo->expanded = FALSE; + // Find PIDL into our explorer + ++m_mtxBlockNavigate; + HTREEITEM hItem; + BOOL result = _NavigateToPIDL(pidl, &hItem, TRUE, FALSE, TRUE); + --m_mtxBlockNavigate; - // Set up our treeview template - 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); - - if (bSort) - { - TVSORTCB sortCallback; - sortCallback.hParent = hParent; - sortCallback.lpfnCompare = CompareTreeItems; - sortCallback.lParam = (LPARAM)(PVOID)m_pDesktop; - SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback); - } - - return htiCreated; -} - -/* This is the slow version of the above method */ -HTREEITEM -CExplorerBand::InsertItem( - _In_opt_ HTREEITEM hParent, - _In_ LPCITEMIDLIST pElt, - _In_ LPCITEMIDLIST pEltRelative, - _In_ BOOL bSort) -{ - CComPtr psfFolder; - HRESULT hr = SHBindToParent(pElt, IID_PPV_ARG(IShellFolder, &psfFolder), NULL); - if (FAILED_UNEXPECTEDLY(hr)) - return NULL; - - return InsertItem(hParent, psfFolder, pElt, pEltRelative, bSort); -} - -BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo) -{ - CComPtr pEnumIDList; - LPITEMIDLIST pidlSub; - LPITEMIDLIST entry; - SHCONTF EnumFlags; - HRESULT hr; - ULONG fetched; - ULONG uItemCount; - CComPtr pFolder; - TVSORTCB sortCallback; - - entry = pNodeInfo->absolutePidl; - fetched = 1; - uItemCount = 0; - EnumFlags = SHCONTF_FOLDERS; - - hr = SHGetFolderLocation(m_hWnd, CSIDL_DESKTOP, NULL, 0, &pidlSub); - if (!SUCCEEDED(hr)) - { - ERR("Can't get desktop PIDL !\n"); - return FALSE; - } - - if (!m_pDesktop->CompareIDs(NULL, pidlSub, entry)) - { - // We are the desktop, so use pDesktop as pFolder - pFolder = m_pDesktop; - } - else - { - // Get an IShellFolder of our pidl - hr = m_pDesktop->BindToObject(entry, NULL, IID_PPV_ARG(IShellFolder, &pFolder)); - if (!SUCCEEDED(hr)) - { - ILFree(pidlSub); - ERR("Can't bind folder to desktop !\n"); - return FALSE; - } - } - ILFree(pidlSub); - - // TODO: handle hidden folders according to settings ! - EnumFlags |= SHCONTF_INCLUDEHIDDEN; - - // Enum through objects - hr = pFolder->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 FALSE; - } - - /* Don't redraw while we add stuff into the tree */ - SendMessage(WM_SETREDRAW, FALSE, 0); - while(SUCCEEDED(pEnumIDList->Next(1, &pidlSub, &fetched)) && pidlSub && fetched) - { - LPITEMIDLIST pidlSubComplete; - pidlSubComplete = ILCombine(entry, pidlSub); - - if (InsertItem(hItem, pFolder, pidlSubComplete, pidlSub, FALSE)) - uItemCount++; - ILFree(pidlSubComplete); - ILFree(pidlSub); - } - pNodeInfo->expanded = TRUE; - /* Let's do sorting */ - sortCallback.hParent = hItem; - sortCallback.lpfnCompare = CompareTreeItems; - sortCallback.lParam = (LPARAM)(PVOID)m_pDesktop; - SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback); - - /* Now we can redraw */ - SendMessage(WM_SETREDRAW, TRUE, 0); - - return (uItemCount > 0) ? TRUE : FALSE; + return result; } /** @@ -854,712 +218,99 @@ BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo) * - 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(LPCITEMIDLIST dest, HTREEITEM *item, BOOL bExpand, BOOL bInsert, - BOOL bSelect) +BOOL +CExplorerBand::_NavigateToPIDL( + _In_ LPCITEMIDLIST dest, + _Out_ HTREEITEM *phItem, + _In_ BOOL bExpand, + _In_ BOOL bInsert, + _In_ BOOL bSelect) { - HTREEITEM current; - HTREEITEM tmp; - HTREEITEM parent; - NodeInfo *nodeData; - LPITEMIDLIST relativeChild; - TVITEM tvItem; - - if (!item) + if (!phItem) return FALSE; - current = m_hRoot; - parent = NULL; + *phItem = NULL; + + HTREEITEM hItem = TreeView_GetFirstVisible(m_hwndTreeView); + HTREEITEM hParent = NULL, tmp; while (TRUE) { - nodeData = GetNodeInfo(current); - if (!nodeData) + CItemData *pItemData = GetItemData(hItem); + if (!pItemData) { - ERR("Something has gone wrong, no data associated to node !\n"); - *item = NULL; + ERR("Something has gone wrong, no data associated to node\n"); return FALSE; } + // If we found our node, give it back - if (!m_pDesktop->CompareIDs(0, nodeData->absolutePidl, dest)) + if (!m_pDesktop->CompareIDs(0, pItemData->absolutePidl, dest)) { if (bSelect) - TreeView_SelectItem(m_hWnd, current); - *item = current; + TreeView_SelectItem(m_hwndTreeView, hItem); + *phItem = hItem; return TRUE; } // Check if we are a parent of the requested item - relativeChild = ILFindChild(nodeData->absolutePidl, dest); - if (relativeChild != 0) + TVITEMW tvItem; + LPITEMIDLIST relativeChild = ILFindChild(pItemData->absolutePidl, dest); + if (relativeChild) { // Notify treeview we have children tvItem.mask = TVIF_CHILDREN; - tvItem.hItem = current; + tvItem.hItem = hItem; tvItem.cChildren = 1; - TreeView_SetItem(m_hWnd, &tvItem); + TreeView_SetItem(m_hwndTreeView, &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); + if (!pItemData->expanded) + { + _InsertSubitems(hItem, pItemData->absolutePidl); + pItemData->expanded = TRUE; + } + TreeView_Expand(m_hwndTreeView, hItem, TVE_EXPAND); } // Try to get a child - tmp = TreeView_GetChild(m_hWnd, current); + tmp = TreeView_GetChild(m_hwndTreeView, hItem); if (tmp) { // We have a child, let's continue with it - parent = current; - current = tmp; + hParent = hItem; + hItem = tmp; continue; } - if (bInsert && nodeData->expanded) + if (bInsert && pItemData->expanded) { // Happens when we have to create a subchild inside a child - current = InsertItem(current, dest, relativeChild, TRUE); + hItem = _InsertItem(hItem, 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); + tmp = TreeView_GetNextSibling(m_hwndTreeView, hItem); if (tmp) { - current = tmp; + hItem = tmp; continue; } + if (bInsert) { - current = InsertItem(parent, dest, ILFindLastID(dest), TRUE); - *item = current; + *phItem = hItem = _InsertItem(hParent, dest, ILFindLastID(dest), TRUE); return TRUE; } - *item = NULL; + return FALSE; } + UNREACHABLE; } - -BOOL CExplorerBand::NavigateToCurrentFolder() -{ - LPITEMIDLIST explorerPidl; - HTREEITEM dummy; - BOOL result; - - HRESULT hr = GetCurrentLocation(explorerPidl); - if (FAILED_UNEXPECTEDLY(hr)) - { - ERR("Unable to get browser PIDL !\n"); - return FALSE; - } - ++m_mtxBlockNavigate; - /* find PIDL into our explorer */ - result = NavigateToPIDL(explorerPidl, &dummy, TRUE, FALSE, TRUE); - --m_mtxBlockNavigate; - ILFree(explorerPidl); - return result; -} - -// *** Tree item sorting callback *** -int CALLBACK CExplorerBand::CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3) -{ - NodeInfo *info1 = (NodeInfo*)p1; - NodeInfo *info2 = (NodeInfo*)p2; - IShellFolder *pDesktop = (IShellFolder *)p3; - - HRESULT hr = pDesktop->CompareIDs(0, info1->absolutePidl, info2->absolutePidl); - if (FAILED(hr)) - return 0; - - return (SHORT)HRESULT_CODE(hr); -} - -// *** IOleWindow methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::GetWindow(HWND *lphwnd) -{ - if (!lphwnd) - return E_INVALIDARG; - *lphwnd = m_hWnd; - return S_OK; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::ContextSensitiveHelp(BOOL fEnterMode) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - - -// *** IDockingWindow methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::CloseDW(DWORD dwReserved) -{ - // We do nothing, we don't have anything to save yet - TRACE("CloseDW called\n"); - return S_OK; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved) -{ - /* Must return E_NOTIMPL according to MSDN */ - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::ShowDW(BOOL fShow) -{ - m_fVisible = fShow; - ShowWindow(fShow); - return S_OK; -} - - -// *** IDeskBand methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi) -{ - if (!pdbi) - { - return E_INVALIDARG; - } - this->m_dwBandID = dwBandID; - - if (pdbi->dwMask & DBIM_MINSIZE) - { - pdbi->ptMinSize.x = 200; - pdbi->ptMinSize.y = 30; - } - - if (pdbi->dwMask & DBIM_MAXSIZE) - { - pdbi->ptMaxSize.y = -1; - } - - if (pdbi->dwMask & DBIM_INTEGRAL) - { - pdbi->ptIntegral.y = 1; - } - - if (pdbi->dwMask & DBIM_ACTUAL) - { - pdbi->ptActual.x = 200; - pdbi->ptActual.y = 30; - } - - if (pdbi->dwMask & DBIM_TITLE) - { - if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_FOLDERSLABEL, pdbi->wszTitle, _countof(pdbi->wszTitle))) - return HRESULT_FROM_WIN32(GetLastError()); - } - - if (pdbi->dwMask & DBIM_MODEFLAGS) - { - pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT; - } - - if (pdbi->dwMask & DBIM_BKCOLOR) - { - pdbi->dwMask &= ~DBIM_BKCOLOR; - } - return S_OK; -} - - -// *** IObjectWithSite methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::SetSite(IUnknown *pUnkSite) -{ - HRESULT hr; - HWND parentWnd; - - if (pUnkSite == m_pSite) - return S_OK; - - TRACE("SetSite called \n"); - if (!pUnkSite) - { - DestroyExplorerBand(); - DestroyWindow(); - m_hWnd = NULL; - } - - if (pUnkSite != m_pSite) - { - m_pSite = NULL; - } - - if(!pUnkSite) - return S_OK; - - hr = IUnknown_GetWindow(pUnkSite, &parentWnd); - if (!SUCCEEDED(hr)) - { - ERR("Could not get parent's window ! Status: %08lx\n", hr); - return E_INVALIDARG; - } - - m_pSite = pUnkSite; - - if (m_hWnd) - { - // Change its parent - SetParent(parentWnd); - } - else - { - HWND wnd = CreateWindow(WC_TREEVIEW, NULL, - WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TVS_HASLINES | TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_EDITLABELS /* | TVS_SINGLEEXPAND*/ , // remove TVS_SINGLEEXPAND for now since it has strange behaviour - 0, 0, 0, 0, parentWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL); - - // Subclass the window - SubclassWindow(wnd); - - // Initialize our treeview now - InitializeExplorerBand(); - RegisterDragDrop(m_hWnd, dynamic_cast(this)); - } - return S_OK; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::GetSite(REFIID riid, void **ppvSite) -{ - if (!ppvSite) - return E_POINTER; - *ppvSite = m_pSite; - return S_OK; -} - - -// *** IOleCommandTarget methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - - -// *** IServiceProvider methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject) -{ - /* FIXME: we probably want to handle more services here */ - return IUnknown_QueryService(m_pSite, SID_SShellBrowser, riid, ppvObject); -} - - -// *** IInputObject methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg) -{ - if (fActivate) - { - //SetFocus(); - SetActiveWindow(); - } - // TODO: handle message - if(lpMsg) - { - TranslateMessage(lpMsg); - DispatchMessage(lpMsg); - } - return S_OK; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::HasFocusIO() -{ - return m_bFocused ? S_OK : S_FALSE; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::TranslateAcceleratorIO(LPMSG lpMsg) -{ - if (lpMsg->hwnd == m_hWnd || - (m_isEditing && IsChild(lpMsg->hwnd))) - { - TranslateMessage(lpMsg); - DispatchMessage(lpMsg); - return S_OK; - } - - return S_FALSE; -} - -// *** IPersist methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::GetClassID(CLSID *pClassID) -{ - if (!pClassID) - return E_POINTER; - memcpy(pClassID, &CLSID_ExplorerBand, sizeof(CLSID)); - return S_OK; -} - - -// *** IPersistStream methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::IsDirty() -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::Load(IStream *pStm) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::Save(IStream *pStm, BOOL fClearDirty) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::GetSizeMax(ULARGE_INTEGER *pcbSize) -{ - // TODO: calculate max size - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** IWinEventHandler methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) -{ - BOOL bHandled; - LRESULT result; - - if (uMsg == WM_NOTIFY) - { - NMHDR *pNotifyHeader = (NMHDR*)lParam; - switch (pNotifyHeader->code) - { - case TVN_ITEMEXPANDING: - result = OnTreeItemExpanding((LPNMTREEVIEW)lParam); - if (theResult) - *theResult = result; - break; - 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); - if (theResult) - *theResult = 1; - break; - case TVN_BEGINDRAG: - case TVN_BEGINRDRAG: - OnTreeItemDragging((LPNMTREEVIEW)lParam, pNotifyHeader->code == TVN_BEGINRDRAG); - break; - 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; - - if (theResult) - *theResult = 1; - NodeInfo *info = GetNodeInfo(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)) - { - if (theResult) - *theResult = 0; - m_isEditing = TRUE; - m_oldSelected = NULL; - } - return S_OK; - } - case TVN_ENDLABELEDITW: - { - LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam; - NodeInfo *info = GetNodeInfo(dispInfo->item.hItem); - HRESULT hr; - - m_isEditing = FALSE; - if (m_oldSelected) - { - ++m_mtxBlockNavigate; - TreeView_SelectItem(m_hWnd, m_oldSelected); - --m_mtxBlockNavigate; - } - - if (theResult) - *theResult = 0; - if (dispInfo->item.pszText) - { - LPITEMIDLIST pidlNew; - CComPtr pParent; - LPCITEMIDLIST pidlChild; - BOOL RenamedCurrent = IsCurrentLocation(info->absolutePidl) == S_OK; - - hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pidlChild); - if (!SUCCEEDED(hr) || !pParent.p) - return E_FAIL; - - hr = pParent->SetNameOf(m_hWnd, 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); - - if (RenamedCurrent) - { - // Navigate to our new location - UpdateBrowser(pidlNewAbs); - } - else - { - // Tell everyone in case SetNameOf forgot, this causes IShellView to update itself when we renamed a child - SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, info->absolutePidl, pidlNewAbs); - } - - ILFree(pidlParent); - ILFree(pidlNewAbs); - ILFree(pidlNew); - if (theResult) - *theResult = 1; - } - return S_OK; - } - } - default: - break; - } - } - return S_OK; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::IsWindowOwner(HWND hWnd) -{ - return (hWnd == m_hWnd) ? S_OK : S_FALSE; -} - -// *** IBandNavigate methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::Select(long paramC) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** INamespaceProxy *** - -/// Returns the ITEMIDLIST that should be navigated when an item is invoked. -STDMETHODIMP CExplorerBand::GetNavigateTarget( - _In_ PCIDLIST_ABSOLUTE pidl, - _Out_ PIDLIST_ABSOLUTE ppidlTarget, - _Out_ ULONG *pulAttrib) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -/// Handles a user action on an item. -STDMETHODIMP CExplorerBand::Invoke(_In_ PCIDLIST_ABSOLUTE pidl) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -/// Called when the user has selected an item. -STDMETHODIMP CExplorerBand::OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -/// Returns flags used to update the tree control. -STDMETHODIMP CExplorerBand::RefreshFlags( - _Out_ DWORD *pdwStyle, - _Out_ DWORD *pdwExStyle, - _Out_ DWORD *dwEnum) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CExplorerBand::CacheItem(_In_ PCIDLIST_ABSOLUTE pidl) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** IDispatch methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfoCount(UINT *pctinfo) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) -{ - 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) -{ - ERR("Entering drag\n"); - m_pCurObject = pObj; - m_oldSelected = TreeView_GetSelection(m_hWnd); - return DragOver(glfKeyState, pt, pdwEffect); -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) -{ - 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) - { - ++m_mtxBlockNavigate; - TreeView_SelectItem(m_hWnd, info.hItem); - --m_mtxBlockNavigate; - // Delegate to shell folder - if (m_pDropTarget && info.hItem != m_childTargetNode) - { - m_pDropTarget = NULL; - } - if (info.hItem != m_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 = m_pDesktop; - else - { - hr = m_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, &m_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 = m_pDropTarget->DragEnter(m_pCurObject, glfKeyState, pt, pdwEffect); - m_childTargetNode = info.hItem; - } - if (m_pDropTarget) - { - hr = m_pDropTarget->DragOver(glfKeyState, pt, pdwEffect); - } - } - else - { - m_childTargetNode = NULL; - m_pDropTarget = NULL; - *pdwEffect = DROPEFFECT_NONE; - } - return S_OK; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::DragLeave() -{ - ++m_mtxBlockNavigate; - TreeView_SelectItem(m_hWnd, m_oldSelected); - --m_mtxBlockNavigate; - m_childTargetNode = NULL; - if (m_pCurObject) - { - m_pCurObject = NULL; - } - return S_OK; -} - -HRESULT STDMETHODCALLTYPE CExplorerBand::Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) -{ - if (!m_pDropTarget) - return E_FAIL; - m_pDropTarget->Drop(pObj, glfKeyState, pt, pdwEffect); - DragLeave(); - return S_OK; -} - -// *** IDropSource methods *** -HRESULT STDMETHODCALLTYPE CExplorerBand::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) -{ - 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) -{ - return DRAGDROP_S_USEDEFAULTCURSORS; -} diff --git a/dll/win32/shdocvw/CExplorerBand.h b/dll/win32/shdocvw/CExplorerBand.h index 5c307deacce..6a2597605f8 100644 --- a/dll/win32/shdocvw/CExplorerBand.h +++ b/dll/win32/shdocvw/CExplorerBand.h @@ -8,185 +8,27 @@ #pragma once -#define WM_USER_SHELLEVENT (WM_USER + 88) +#include "CNSCBand.h" -class CExplorerBand : - public CComCoClass, - public CComObjectRootEx, - public IDeskBand, - public IObjectWithSite, - public IInputObject, - public IPersistStream, - public IOleCommandTarget, - public IServiceProvider, - public IBandNavigate, - public IWinEventHandler, - public INamespaceProxy, - public IDispatch, - public IDropSource, - public IDropTarget, - public CWindowImpl +class CExplorerBand + : public CNSCBand + , public CComCoClass + , public CComObjectRootEx + , public IDispatch { -private: - class NodeInfo - { - public: - LPITEMIDLIST absolutePidl; - LPITEMIDLIST relativePidl; - BOOL expanded; - }; - - // *** BaseBarSite information *** - CComPtr m_pSite; - CComPtr m_pDesktop; - - // *** tree explorer band stuff *** - BOOL m_fVisible; - BYTE m_mtxBlockNavigate; // A "lock" that prevents internal selection changes to initiate a navigation to the newly selected item. - BOOL m_bFocused; - DWORD m_dwBandID; - BOOL m_isEditing; - HIMAGELIST m_hImageList; - HTREEITEM m_hRoot; - HTREEITEM m_oldSelected; - LPITEMIDLIST m_pidlCurrent; // Note: This is NULL until the first user navigation! - - // *** notification cookies *** - DWORD m_adviseCookie; - ULONG m_shellRegID; - - // *** Drop target information *** - CComPtr m_pDropTarget; - HTREEITEM m_childTargetNode; - CComPtr m_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); - LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); - - // *** Helper functions *** - NodeInfo* GetNodeInfo(HTREEITEM hItem); - HRESULT UpdateBrowser(LPITEMIDLIST pidlGoto); - HTREEITEM InsertItem( - _In_opt_ HTREEITEM hParent, - _Inout_ IShellFolder *psfParent, - _In_ LPCITEMIDLIST pElt, - _In_ LPCITEMIDLIST pEltRelative, - _In_ BOOL bSort); - HTREEITEM InsertItem( - _In_opt_ HTREEITEM hParent, - _In_ LPCITEMIDLIST pElt, - _In_ LPCITEMIDLIST pEltRelative, - _In_ BOOL bSort); - BOOL InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo); - BOOL NavigateToPIDL(LPCITEMIDLIST dest, HTREEITEM *item, BOOL bExpand, BOOL bInsert, BOOL bSelect); - BOOL NavigateToCurrentFolder(); - HRESULT GetCurrentLocation(PIDLIST_ABSOLUTE &pidl); - HRESULT IsCurrentLocation(PCIDLIST_ABSOLUTE pidl); - void OnChangeNotify( - _In_opt_ LPCITEMIDLIST pidl0, - _In_opt_ LPCITEMIDLIST pidl1, - _In_ LONG lEvent); - void Refresh(); - void RefreshRecurse(_In_ HTREEITEM hItem); - BOOL IsTreeItemInEnum(_In_ HTREEITEM hItem, _In_ IEnumIDList *pEnum); - BOOL TreeItemHasThisChild(_In_ HTREEITEM hItem, _In_ PCITEMID_CHILD pidlChild); - HRESULT GetItemEnum(_Out_ CComPtr& pEnum, _In_ HTREEITEM hItem); - BOOL ItemHasAnyChild(_In_ HTREEITEM hItem); - - // *** Tree item sorting callback *** - static int CALLBACK CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3); - public: CExplorerBand(); virtual ~CExplorerBand(); - // *** IOleWindow methods *** - STDMETHOD(GetWindow)(HWND *lphwnd) override; - STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override; - - // *** IDockingWindow methods *** - STDMETHOD(CloseDW)(DWORD dwReserved) override; - STDMETHOD(ResizeBorderDW)(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved) override; - STDMETHOD(ShowDW)(BOOL fShow) override; - - // *** IDeskBand methods *** - STDMETHOD(GetBandInfo)(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi) override; - - // *** IObjectWithSite methods *** - STDMETHOD(SetSite)(IUnknown *pUnkSite) override; - STDMETHOD(GetSite)(REFIID riid, void **ppvSite) override; - - // *** IOleCommandTarget methods *** - STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) override; - STDMETHOD(Exec)(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) override; - - // *** IServiceProvider methods *** - STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppvObject) override; - - // *** IInputObject methods *** - STDMETHOD(UIActivateIO)(BOOL fActivate, LPMSG lpMsg) override; - STDMETHOD(HasFocusIO)() override; - STDMETHOD(TranslateAcceleratorIO)(LPMSG lpMsg) override; - - // *** IPersist methods *** - STDMETHOD(GetClassID)(CLSID *pClassID) override; - - // *** IPersistStream methods *** - STDMETHOD(IsDirty)() override; - STDMETHOD(Load)(IStream *pStm) override; - STDMETHOD(Save)(IStream *pStm, BOOL fClearDirty) override; - STDMETHOD(GetSizeMax)(ULARGE_INTEGER *pcbSize) override; - - // *** IWinEventHandler methods *** - STDMETHOD(OnWinEvent)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) override; - STDMETHOD(IsWindowOwner)(HWND hWnd) override; - - // *** IBandNavigate methods *** - STDMETHOD(Select)(long paramC) override; - - // *** INamespaceProxy *** - STDMETHOD(GetNavigateTarget)( - _In_ PCIDLIST_ABSOLUTE pidl, - _Out_ PIDLIST_ABSOLUTE ppidlTarget, - _Out_ ULONG *pulAttrib) override; - STDMETHOD(Invoke)(_In_ PCIDLIST_ABSOLUTE pidl) override; - STDMETHOD(OnSelectionChanged)(_In_ PCIDLIST_ABSOLUTE pidl) override; - STDMETHOD(RefreshFlags)( - _Out_ DWORD *pdwStyle, - _Out_ DWORD *pdwExStyle, - _Out_ DWORD *dwEnum) override; - STDMETHOD(CacheItem)(_In_ PCIDLIST_ABSOLUTE pidl) override; + STDMETHODIMP GetClassID(CLSID *pClassID) override; + STDMETHODIMP OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) override; + STDMETHODIMP Invoke(_In_ PCIDLIST_ABSOLUTE pidl) override; // *** IDispatch methods *** - STDMETHOD(GetTypeInfoCount)(UINT *pctinfo) override; - STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) override; - STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) override; - STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) override; - - // *** IDropTarget methods *** - STDMETHOD(DragEnter)(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) override; - STDMETHOD(DragOver)(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) override; - STDMETHOD(DragLeave)() override; - STDMETHOD(Drop)(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) override; - - // *** IDropSource methods *** - STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState) override; - STDMETHOD(GiveFeedback)(DWORD dwEffect) override; + STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) override; + STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) override; + STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) override; + STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) override; DECLARE_REGISTRY_RESOURCEID(IDR_EXPLORERBAND) DECLARE_NOT_AGGREGATABLE(CExplorerBand) @@ -194,27 +36,39 @@ public: DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CExplorerBand) - COM_INTERFACE_ENTRY_IID(IID_IDispatch, IDispatch) - COM_INTERFACE_ENTRY_IID(IID_IWinEventHandler, IWinEventHandler) - COM_INTERFACE_ENTRY_IID(IID_IBandNavigate, IBandNavigate) - COM_INTERFACE_ENTRY_IID(IID_INamespaceProxy, INamespaceProxy) - COM_INTERFACE_ENTRY2_IID(IID_IOleWindow, IOleWindow, IDeskBand) - COM_INTERFACE_ENTRY2_IID(IID_IDockingWindow, IDockingWindow, IDeskBand) COM_INTERFACE_ENTRY_IID(IID_IDeskBand, IDeskBand) + COM_INTERFACE_ENTRY2_IID(IID_IDockingWindow, IDockingWindow, IDeskBand) + COM_INTERFACE_ENTRY2_IID(IID_IOleWindow, IOleWindow, IDeskBand) COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite) + COM_INTERFACE_ENTRY_IID(IID_IInputObject, IInputObject) + COM_INTERFACE_ENTRY_IID(IID_IPersistStream, IPersistStream) + COM_INTERFACE_ENTRY2_IID(IID_IPersist, IPersist, IPersistStream) COM_INTERFACE_ENTRY_IID(IID_IOleCommandTarget, IOleCommandTarget) COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider) - COM_INTERFACE_ENTRY_IID(IID_IInputObject, IInputObject) - COM_INTERFACE_ENTRY2_IID(IID_IPersist, IPersist, IPersistStream) - COM_INTERFACE_ENTRY_IID(IID_IPersistStream, IPersistStream) + COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) + COM_INTERFACE_ENTRY_IID(IID_IBandNavigate, IBandNavigate) + COM_INTERFACE_ENTRY_IID(IID_IWinEventHandler, IWinEventHandler) + COM_INTERFACE_ENTRY_IID(IID_INamespaceProxy, INamespaceProxy) + COM_INTERFACE_ENTRY_IID(IID_IDispatch, IDispatch) + COM_INTERFACE_ENTRY2_IID(IID_IUnknown, IUnknown, IDispatch) 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_TIMER, OnTimer) - // MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) - END_MSG_MAP() +protected: + INT _GetRootCsidl() override; + DWORD _GetTVStyle() override; + DWORD _GetTVExStyle() override; + DWORD _GetEnumFlags() override; + HRESULT _CreateTreeView(HWND hwndParent) override; + void _DestroyTreeView() override; + BOOL _WantsRootItem() override; + BOOL _GetTitle(LPWSTR pszTitle, INT cchTitle) override; + void _SortItems(HTREEITEM hParent) override; + BOOL _NavigateToCurrentFolder(); + BOOL _NavigateToPIDL( + _In_ LPCITEMIDLIST dest, + _Out_ HTREEITEM *phItem, + _In_ BOOL bExpand, + _In_ BOOL bInsert, + _In_ BOOL bSelect); + static INT CALLBACK _CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3); // Used by _SortItems }; diff --git a/dll/win32/shdocvw/CFavBand.cpp b/dll/win32/shdocvw/CFavBand.cpp index bc00c3943bb..b79f2e27be2 100644 --- a/dll/win32/shdocvw/CFavBand.cpp +++ b/dll/win32/shdocvw/CFavBand.cpp @@ -11,40 +11,64 @@ WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); CFavBand::CFavBand() - : m_fVisible(FALSE) - , m_bFocused(FALSE) - , m_dwBandID(0) - , m_hToolbarImageList(NULL) - , m_hTreeViewImageList(NULL) { - SHDOCVW_LockModule(); - SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &m_pidlFav); } CFavBand::~CFavBand() { - if (m_hToolbarImageList) - { - ImageList_Destroy(m_hToolbarImageList); - m_hToolbarImageList = NULL; - } - if (m_hTreeViewImageList) - { - ImageList_Destroy(m_hTreeViewImageList); - m_hTreeViewImageList = NULL; - } - SHDOCVW_UnlockModule(); } -VOID CFavBand::OnFinalMessage(HWND) +STDMETHODIMP CFavBand::GetClassID(CLSID *pClassID) { - // The message loop is finished, now we can safely destruct! - Release(); + if (!pClassID) + return E_POINTER; + *pClassID = CLSID_SH_FavBand; + return S_OK; } -// *** helper methods *** +INT CFavBand::_GetRootCsidl() +{ + return CSIDL_FAVORITES; +} -BOOL CFavBand::CreateToolbar() +DWORD CFavBand::_GetTVStyle() +{ + // Remove TVS_SINGLEEXPAND for now since it has strange behaviour + return TVS_NOHSCROLL | TVS_NONEVENHEIGHT | TVS_FULLROWSELECT | TVS_INFOTIP | + /*TVS_SINGLEEXPAND | TVS_TRACKSELECT |*/ TVS_SHOWSELALWAYS | TVS_EDITLABELS | + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP; +} + +DWORD CFavBand::_GetTVExStyle() +{ + return WS_EX_CLIENTEDGE; +} + +DWORD CFavBand::_GetEnumFlags() +{ + return SHCONTF_FOLDERS | SHCONTF_NONFOLDERS; +} + +BOOL CFavBand::_GetTitle(LPWSTR pszTitle, INT cchTitle) +{ +#define IDS_FAVORITES 47 // Borrowed from shell32.dll + HINSTANCE hShell32 = ::LoadLibraryExW(L"shell32.dll", NULL, LOAD_LIBRARY_AS_DATAFILE); + if (hShell32) + { + ::LoadStringW(hShell32, IDS_FAVORITES, pszTitle, cchTitle); + ::FreeLibrary(hShell32); + return TRUE; + } + return FALSE; +#undef IDS_FAVORITES +} + +BOOL CFavBand::_WantsRootItem() +{ + return FALSE; +} + +HRESULT CFavBand::_CreateToolbar(HWND hwndParent) { #define IDB_SHELL_EXPLORER_SM 216 // Borrowed from browseui.dll HINSTANCE hinstBrowseUI = LoadLibraryExW(L"browseui.dll", NULL, LOAD_LIBRARY_AS_DATAFILE); @@ -58,23 +82,23 @@ BOOL CFavBand::CreateToolbar() #undef IDB_SHELL_EXPLORER_SM ATLASSERT(hbmToolbar); if (!hbmToolbar) - return FALSE; + return E_FAIL; m_hToolbarImageList = ImageList_Create(16, 16, ILC_COLOR32, 0, 8); ATLASSERT(m_hToolbarImageList); if (!m_hToolbarImageList) - return FALSE; + return E_FAIL; ImageList_Add(m_hToolbarImageList, hbmToolbar, NULL); DeleteObject(hbmToolbar); DWORD style = WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_LIST | CCS_NODIVIDER | TBSTYLE_WRAPABLE; - HWND hwndTB = ::CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, style, 0, 0, 0, 0, m_hWnd, - (HMENU)(LONG_PTR)IDW_TOOLBAR, instance, NULL); + HWND hwndTB = ::CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, style, 0, 0, 0, 0, hwndParent, + (HMENU)UlongToHandle(IDW_TOOLBAR), instance, NULL); ATLASSERT(hwndTB); if (!hwndTB) - return FALSE; + return E_FAIL; m_hwndToolbar.Attach(hwndTB); m_hwndToolbar.SendMessage(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); @@ -102,480 +126,57 @@ BOOL CFavBand::CreateToolbar() tbb[iButton].iString = (INT)m_hwndToolbar.SendMessage(TB_ADDSTRING, 0, (LPARAM)szzOrganize); ++iButton; ATLASSERT(iButton == _countof(tbb)); - - LRESULT ret = m_hwndToolbar.SendMessage(TB_ADDBUTTONS, iButton, (LPARAM)&tbb); - ATLASSERT(ret); - - return ret; -} - -BOOL CFavBand::CreateTreeView() -{ - m_hTreeViewImageList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 64, 0); - ATLASSERT(m_hTreeViewImageList); - if (!m_hTreeViewImageList) - return FALSE; - - DWORD style = TVS_NOHSCROLL | TVS_NONEVENHEIGHT | TVS_FULLROWSELECT | TVS_INFOTIP | - TVS_SINGLEEXPAND | TVS_TRACKSELECT | TVS_SHOWSELALWAYS | TVS_EDITLABELS | - WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP; - HWND hwndTV = ::CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEWW, NULL, style, 0, 0, 0, 0, - m_hWnd, (HMENU)(ULONG_PTR)IDW_TREEVIEW, instance, NULL); - ATLASSERT(hwndTV); - if (!hwndTV) - return FALSE; - - m_hwndTreeView.Attach(hwndTV); - TreeView_SetImageList(m_hwndTreeView, m_hTreeViewImageList, TVSIL_NORMAL); - - return TRUE; -} - -// *** message handlers *** - -LRESULT CFavBand::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) -{ - INITCOMMONCONTROLSEX iccx = { sizeof(iccx), ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES }; - if (!::InitCommonControlsEx(&iccx) || !CreateToolbar() || !CreateTreeView()) - return -1; - - return 0; -} - -LRESULT CFavBand::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) -{ - m_hwndTreeView.DestroyWindow(); - m_hwndToolbar.DestroyWindow(); - return 0; -} - -LRESULT CFavBand::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) -{ - if (!m_hwndTreeView) - return 0; - - RECT rc; - GetClientRect(&rc); - LONG cx = rc.right, cy = rc.bottom; - - RECT rcTB; - m_hwndToolbar.SendMessage(TB_AUTOSIZE, 0, 0); - m_hwndToolbar.GetWindowRect(&rcTB); - - LONG cyTB = rcTB.bottom - rcTB.top; - m_hwndTreeView.MoveWindow(0, cyTB, cx, cy - cyTB); - - return 0; -} - -LRESULT CFavBand::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) -{ - m_bFocused = TRUE; - IUnknown_OnFocusChangeIS(m_pSite, reinterpret_cast(this), TRUE); - return 0; -} - -LRESULT CFavBand::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) -{ - IUnknown_OnFocusChangeIS(m_pSite, reinterpret_cast(this), FALSE); - m_bFocused = FALSE; - return 0; -} - -LRESULT CFavBand::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) -{ - switch (LOWORD(wParam)) - { - case ID_ADD: - { - UNIMPLEMENTED; - SHELL_ErrorBox(m_hWnd, ERROR_NOT_SUPPORTED); - break; - } - case ID_ORGANIZE: - { - SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_IDLIST }; - sei.hwnd = m_hWnd; - sei.nShow = SW_SHOWNORMAL; - sei.lpIDList = m_pidlFav; - ::ShellExecuteExW(&sei); - break; - } - } - return 0; -} - -// *** IOleWindow *** - -STDMETHODIMP CFavBand::GetWindow(HWND *lphwnd) -{ - if (!lphwnd) - return E_INVALIDARG; - *lphwnd = m_hWnd; - return S_OK; -} - -STDMETHODIMP CFavBand::ContextSensitiveHelp(BOOL fEnterMode) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** IDockingWindow *** - -STDMETHODIMP CFavBand::CloseDW(DWORD dwReserved) -{ - // We do nothing, we don't have anything to save yet - TRACE("CloseDW called\n"); - return S_OK; -} - -STDMETHODIMP CFavBand::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved) -{ - /* Must return E_NOTIMPL according to MSDN */ - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::ShowDW(BOOL fShow) -{ - m_fVisible = fShow; - ShowWindow(fShow ? SW_SHOW : SW_HIDE); - return S_OK; -} - -// *** IDeskBand *** - -STDMETHODIMP CFavBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi) -{ - if (!pdbi) - return E_INVALIDARG; - - m_dwBandID = dwBandID; - - if (pdbi->dwMask & DBIM_MINSIZE) - { - pdbi->ptMinSize.x = 200; - pdbi->ptMinSize.y = 30; - } - - if (pdbi->dwMask & DBIM_MAXSIZE) - pdbi->ptMaxSize.y = -1; - - if (pdbi->dwMask & DBIM_INTEGRAL) - pdbi->ptIntegral.y = 1; - - if (pdbi->dwMask & DBIM_ACTUAL) - { - pdbi->ptActual.x = 200; - pdbi->ptActual.y = 30; - } - - if (pdbi->dwMask & DBIM_TITLE) - { -#define IDS_FAVORITES 47 // Borrowed from shell32.dll - HINSTANCE hShell32 = LoadLibraryExW(L"shell32.dll", NULL, LOAD_LIBRARY_AS_DATAFILE); - if (hShell32) - { - LoadStringW(hShell32, IDS_FAVORITES, pdbi->wszTitle, _countof(pdbi->wszTitle)); - FreeLibrary(hShell32); - } -#undef IDS_FAVORITES - } - - if (pdbi->dwMask & DBIM_MODEFLAGS) - pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT; - - if (pdbi->dwMask & DBIM_BKCOLOR) - pdbi->dwMask &= ~DBIM_BKCOLOR; + m_hwndToolbar.SendMessage(TB_ADDBUTTONS, iButton, (LPARAM)&tbb); return S_OK; } -// *** IObjectWithSite *** - -STDMETHODIMP CFavBand::SetSite(IUnknown *pUnkSite) -{ - HRESULT hr; - - if (pUnkSite == m_pSite) - return S_OK; - - TRACE("SetSite called\n"); - - if (!pUnkSite) - { - DestroyWindow(); - m_hWnd = NULL; - } - - if (pUnkSite != m_pSite) - m_pSite = NULL; - - if (!pUnkSite) - return S_OK; - - HWND hwndParent; - hr = IUnknown_GetWindow(pUnkSite, &hwndParent); - if (!SUCCEEDED(hr)) - { - ERR("Could not get parent's window! 0x%08lX\n", hr); - return E_INVALIDARG; - } - - m_pSite = pUnkSite; - - if (m_hWnd) - { - SetParent(hwndParent); // Change its parent - } - else - { - this->Create(hwndParent, NULL, NULL, WS_CHILD | WS_VISIBLE, 0, (UINT)0, NULL); - } - - return S_OK; -} - -STDMETHODIMP CFavBand::GetSite(REFIID riid, void **ppvSite) -{ - if (!ppvSite) - return E_POINTER; - *ppvSite = m_pSite; - return S_OK; -} - -// *** IOleCommandTarget *** - -STDMETHODIMP CFavBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** IServiceProvider *** - -STDMETHODIMP CFavBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** IServiceProvider *** - -STDMETHODIMP CFavBand::QueryContextMenu( - HMENU hmenu, - UINT indexMenu, - UINT idCmdFirst, - UINT idCmdLast, - UINT uFlags) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::InvokeCommand( - LPCMINVOKECOMMANDINFO lpici) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::GetCommandString( - UINT_PTR idCmd, - UINT uType, - UINT *pwReserved, - LPSTR pszName, - UINT cchMax) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** IInputObject *** - -STDMETHODIMP CFavBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg) -{ - if (fActivate) - { - //SetFocus(); - SetActiveWindow(); - } - - if (lpMsg) - { - TranslateMessage(lpMsg); - DispatchMessage(lpMsg); - } - - return S_OK; -} - -STDMETHODIMP CFavBand::HasFocusIO() -{ - return m_bFocused ? S_OK : S_FALSE; -} - -STDMETHODIMP CFavBand::TranslateAcceleratorIO(LPMSG lpMsg) -{ - if (lpMsg->hwnd == m_hWnd) - { - TranslateMessage(lpMsg); - DispatchMessage(lpMsg); - return S_OK; - } - - return S_FALSE; -} - -// *** IPersist *** - -STDMETHODIMP CFavBand::GetClassID(CLSID *pClassID) -{ - if (!pClassID) - return E_POINTER; - *pClassID = CLSID_SH_FavBand; - return S_OK; -} - - -// *** IPersistStream *** - -STDMETHODIMP CFavBand::IsDirty() -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::Load(IStream *pStm) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::Save(IStream *pStm, BOOL fClearDirty) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::GetSizeMax(ULARGE_INTEGER *pcbSize) -{ - // TODO: calculate max size - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** IWinEventHandler *** - -STDMETHODIMP CFavBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::IsWindowOwner(HWND hWnd) -{ - return (hWnd == m_hWnd) ? S_OK : S_FALSE; -} - -// *** IBandNavigate *** - -STDMETHODIMP CFavBand::Select(long paramC) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** INamespaceProxy *** - -/// Returns the ITEMIDLIST that should be navigated when an item is invoked. -STDMETHODIMP CFavBand::GetNavigateTarget( - _In_ PCIDLIST_ABSOLUTE pidl, - _Out_ PIDLIST_ABSOLUTE ppidlTarget, - _Out_ ULONG *pulAttrib) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -/// Handles a user action on an item. -STDMETHODIMP CFavBand::Invoke(_In_ PCIDLIST_ABSOLUTE pidl) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -/// Called when the user has selected an item. +// Called when the user has selected an item. STDMETHODIMP CFavBand::OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) { - UNIMPLEMENTED; - return E_NOTIMPL; -} + CComHeapPtr pidlTarget; + DWORD attrs = SFGAO_FOLDER | SFGAO_LINK; + HRESULT hr = GetNavigateTarget(pidl, &pidlTarget, &attrs); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; -/// Returns flags used to update the tree control. -STDMETHODIMP CFavBand::RefreshFlags( - _Out_ DWORD *pdwStyle, - _Out_ DWORD *pdwExStyle, - _Out_ DWORD *dwEnum) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} + if ((attrs & (SFGAO_FOLDER | SFGAO_LINK)) == (SFGAO_FOLDER | SFGAO_LINK)) + return _UpdateBrowser(pidlTarget); -STDMETHODIMP CFavBand::CacheItem( - _In_ PCIDLIST_ABSOLUTE pidl) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -// *** IDispatch *** - -STDMETHODIMP CFavBand::GetTypeInfoCount(UINT *pctinfo) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::GetIDsOfNames( - REFIID riid, - LPOLESTR *rgszNames, - UINT cNames, - LCID lcid, - DISPID *rgDispId) -{ - UNIMPLEMENTED; - return E_NOTIMPL; -} - -STDMETHODIMP CFavBand::Invoke( - DISPID dispIdMember, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS *pDispParams, - VARIANT *pVarResult, - EXCEPINFO *pExcepInfo, - UINT *puArgErr) -{ - switch (dispIdMember) + if (attrs & SFGAO_FOLDER) { - case DISPID_DOWNLOADCOMPLETE: - case DISPID_NAVIGATECOMPLETE2: - // FIXME: Update current location - return S_OK; + HTREEITEM hItem = TreeView_GetSelection(m_hwndTreeView); + CItemData *pItemData = GetItemData(hItem); + if (pItemData && !pItemData->expanded) + { + _InsertSubitems(hItem, pItemData->absolutePidl); + pItemData->expanded = TRUE; + } + TreeView_Expand(m_hwndTreeView, hItem, TVE_EXPAND); + return S_OK; } - return E_INVALIDARG; + + SHELLEXECUTEINFOW info = { sizeof(info) }; + info.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_IDLIST; + info.hwnd = m_hWnd; + info.nShow = SW_SHOWNORMAL; + info.lpIDList = pidlTarget; + ShellExecuteExW(&info); + return hr; +} + +void CFavBand::_SortItems(HTREEITEM hParent) +{ + TreeView_SortChildren(m_hwndTreeView, hParent, 0); // Sort by name +} + +HRESULT CFavBand::_CreateTreeView(HWND hwndParent) +{ + HRESULT hr = CNSCBand::_CreateTreeView(hwndParent); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + TreeView_SetItemHeight(m_hwndTreeView, 24); + _InsertSubitems(TVI_ROOT, m_pidlRoot); + return hr; } diff --git a/dll/win32/shdocvw/CFavBand.h b/dll/win32/shdocvw/CFavBand.h index 1b91e04779c..62619096491 100644 --- a/dll/win32/shdocvw/CFavBand.h +++ b/dll/win32/shdocvw/CFavBand.h @@ -7,129 +7,27 @@ #pragma once -#define FAVBANDCLASSNAME L"ReactOS Favorites Band" - #ifdef __cplusplus + +#include "CNSCBand.h" + class CFavBand - : public CComCoClass + : public CNSCBand + , public CComCoClass , public CComObjectRootEx - , public CWindowImpl - , public IDispatch - , public IDeskBand - , public IObjectWithSite - , public IInputObject - , public IPersistStream - , public IOleCommandTarget - , public IServiceProvider - , public IContextMenu - , public IBandNavigate - , public IWinEventHandler - , public INamespaceProxy { public: - DECLARE_WND_CLASS_EX(FAVBANDCLASSNAME, 0, COLOR_3DFACE) - static LPCWSTR GetWndClassName() { return FAVBANDCLASSNAME; } - CFavBand(); virtual ~CFavBand(); - // *** IOleWindow methods *** - STDMETHODIMP GetWindow(HWND *lphwnd) override; - STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override; - - // *** IDockingWindow methods *** - STDMETHODIMP CloseDW(DWORD dwReserved) override; - STDMETHODIMP ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved) override; - STDMETHODIMP ShowDW(BOOL fShow) override; - - // *** IDeskBand methods *** - STDMETHODIMP GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi) override; - - // *** IObjectWithSite methods *** - STDMETHODIMP SetSite(IUnknown *pUnkSite) override; - STDMETHODIMP GetSite(REFIID riid, void **ppvSite) override; - - // *** IOleCommandTarget methods *** - STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) override; - STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) override; - - // *** IServiceProvider methods *** - STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObject) override; - - // *** IContextMenu methods *** - STDMETHODIMP QueryContextMenu( - HMENU hmenu, - UINT indexMenu, - UINT idCmdFirst, - UINT idCmdLast, - UINT uFlags) override; - STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici) override; - STDMETHODIMP GetCommandString( - UINT_PTR idCmd, - UINT uType, - UINT *pwReserved, - LPSTR pszName, - UINT cchMax) override; - - // *** IInputObject methods *** - STDMETHODIMP UIActivateIO(BOOL fActivate, LPMSG lpMsg) override; - STDMETHODIMP HasFocusIO() override; - STDMETHODIMP TranslateAcceleratorIO(LPMSG lpMsg) override; - - // *** IPersist methods *** STDMETHODIMP GetClassID(CLSID *pClassID) override; - - // *** IPersistStream methods *** - STDMETHODIMP IsDirty() override; - STDMETHODIMP Load(IStream *pStm) override; - STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty) override; - STDMETHODIMP GetSizeMax(ULARGE_INTEGER *pcbSize) override; - - // *** IWinEventHandler methods *** - STDMETHODIMP OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) override; - STDMETHODIMP IsWindowOwner(HWND hWnd) override; - - // *** IBandNavigate methods *** - STDMETHODIMP Select(long paramC) override; - - // *** INamespaceProxy methods *** - STDMETHODIMP GetNavigateTarget( - _In_ PCIDLIST_ABSOLUTE pidl, - _Out_ PIDLIST_ABSOLUTE ppidlTarget, - _Out_ ULONG *pulAttrib) override; - STDMETHODIMP Invoke(_In_ PCIDLIST_ABSOLUTE pidl) override; - STDMETHODIMP OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) override; - STDMETHODIMP RefreshFlags( - _Out_ DWORD *pdwStyle, - _Out_ DWORD *pdwExStyle, - _Out_ DWORD *dwEnum) override; - STDMETHODIMP CacheItem(_In_ PCIDLIST_ABSOLUTE pidl) override; - - // *** IDispatch methods *** - STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) override; - STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) override; - STDMETHODIMP GetIDsOfNames( - REFIID riid, - LPOLESTR *rgszNames, - UINT cNames, - LCID lcid, - DISPID *rgDispId) override; - STDMETHODIMP Invoke( - DISPID dispIdMember, - REFIID riid, - LCID lcid, - WORD wFlags, - DISPPARAMS *pDispParams, - VARIANT *pVarResult, - EXCEPINFO *pExcepInfo, - UINT *puArgErr) override; + STDMETHODIMP OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl); DECLARE_REGISTRY_RESOURCEID(IDR_FAVBAND) DECLARE_NOT_AGGREGATABLE(CFavBand) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CFavBand) - COM_INTERFACE_ENTRY_IID(IID_IDispatch, IDispatch) COM_INTERFACE_ENTRY_IID(IID_IDeskBand, IDeskBand) COM_INTERFACE_ENTRY2_IID(IID_IDockingWindow, IDockingWindow, IDeskBand) COM_INTERFACE_ENTRY2_IID(IID_IOleWindow, IOleWindow, IDeskBand) @@ -146,38 +44,16 @@ public: COM_INTERFACE_ENTRY_IID(IID_INamespaceProxy, INamespaceProxy) END_COM_MAP() - BEGIN_MSG_MAP(CFavBand) - MESSAGE_HANDLER(WM_CREATE, OnCreate) - MESSAGE_HANDLER(WM_DESTROY, OnDestroy) - MESSAGE_HANDLER(WM_SIZE, OnSize) - MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) - MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) - MESSAGE_HANDLER(WM_COMMAND, OnCommand) - END_MSG_MAP() - protected: - BOOL m_fVisible; - BOOL m_bFocused; - DWORD m_dwBandID; - CComPtr m_pSite; - CComHeapPtr m_pidlFav; - HIMAGELIST m_hToolbarImageList; - HIMAGELIST m_hTreeViewImageList; - CToolbar<> m_hwndToolbar; - CTreeView m_hwndTreeView; - - VOID OnFinalMessage(HWND) override; - - // *** helper methods *** - BOOL CreateToolbar(); - BOOL CreateTreeView(); - - // *** message handlers *** - LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); - LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); - LRESULT OnSize(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); - LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + INT _GetRootCsidl() override; + DWORD _GetTVStyle() override; + DWORD _GetTVExStyle() override; + DWORD _GetEnumFlags() override; + BOOL _GetTitle(LPWSTR pszTitle, INT cchTitle) override; + HRESULT _CreateTreeView(HWND hwndParent) override; + HRESULT _CreateToolbar(HWND hwndParent) override; + BOOL _WantsRootItem() override; + void _SortItems(HTREEITEM hParent) override; }; + #endif // def __cplusplus diff --git a/dll/win32/shdocvw/CMakeLists.txt b/dll/win32/shdocvw/CMakeLists.txt index 71ab7e8ddb3..764663a42ed 100644 --- a/dll/win32/shdocvw/CMakeLists.txt +++ b/dll/win32/shdocvw/CMakeLists.txt @@ -34,6 +34,7 @@ add_cd_file(TARGET shdocvw DESTINATION reactos/system32 FOR all) add_library(shdocvw_sublib OBJECT CExplorerBand.cpp CFavBand.cpp + CNSCBand.cpp mrulist.cpp objects.cpp utility.cpp) diff --git a/dll/win32/shdocvw/CNSCBand.cpp b/dll/win32/shdocvw/CNSCBand.cpp new file mode 100644 index 00000000000..976acb50a7d --- /dev/null +++ b/dll/win32/shdocvw/CNSCBand.cpp @@ -0,0 +1,1487 @@ +/* + * PROJECT: ReactOS shdocvw + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: NameSpace Control Band + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "objects.h" +#include +#include +#include + +#define TIMER_ID_REFRESH 9999 + +#include +WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); + +HRESULT +SHDOCVW_GetPathOfShortcut( + _In_opt_ HWND hWnd, + _In_ LPCWSTR pszLnkFile, + _Out_ LPWSTR pszPath) +{ + *pszPath = UNICODE_NULL; + CComPtr pShellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARG(IShellLink, &pShellLink)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + CComPtr pPersistFile; + hr = pShellLink->QueryInterface(IID_PPV_ARG(IPersistFile, &pPersistFile)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + hr = pPersistFile->Load(pszLnkFile, STGM_READ); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + WIN32_FIND_DATA find; + hr = pShellLink->GetPath(pszPath, MAX_PATH, &find, 0); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + return S_OK; +} + +HRESULT +SHDOCVW_CreateShortcut( + _In_ LPCWSTR pszLnkFileName, + _In_ PCIDLIST_ABSOLUTE pidlTarget, + _In_opt_ LPCWSTR pszDescription) +{ + HRESULT hr; + + CComPtr psl; + hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARG(IShellLink, &psl)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + psl->SetIDList(pidlTarget); + + if (pszDescription) + psl->SetDescription(pszDescription); + + CComPtr ppf; + hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + return ppf->Save(pszLnkFileName, TRUE); +} + +CNSCBand::CNSCBand() +{ + SHDOCVW_LockModule(); + + INITCOMMONCONTROLSEX iccx = { sizeof(iccx), ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES }; + ::InitCommonControlsEx(&iccx); +} + +CNSCBand::~CNSCBand() +{ + if (m_hToolbarImageList) + { + ImageList_Destroy(m_hToolbarImageList); + m_hToolbarImageList = NULL; + } + SHDOCVW_UnlockModule(); +} + +VOID CNSCBand::OnFinalMessage(HWND) +{ + // The message loop is finished, now we can safely destruct! + static_cast(this)->Release(); +} + +// *** helper methods *** + +CNSCBand::CItemData* CNSCBand::GetItemData(_In_ HTREEITEM hItem) +{ + if (hItem == TVI_ROOT) + return NULL; + + TVITEMW tvItem = { TVIF_PARAM, hItem }; + if (!TreeView_GetItem(m_hwndTreeView, &tvItem)) + return NULL; + + return reinterpret_cast(tvItem.lParam); +} + +static HRESULT +SHDOCVW_GetCurrentLocationFromView(_In_ IShellView& View, _In_ PIDLIST_ABSOLUTE *ppidl) +{ + CComPtr pfv; + CComPtr psf; + HRESULT hr = View.QueryInterface(IID_PPV_ARG(IFolderView, &pfv)); + if (SUCCEEDED(hr) && SUCCEEDED(hr = pfv->GetFolder(IID_PPV_ARG(IShellFolder, &psf)))) + hr = SHELL_GetIDListFromObject(psf, ppidl); + return hr; +} + +HRESULT CNSCBand::_GetCurrentLocation(_Out_ PIDLIST_ABSOLUTE *ppidl) +{ + *ppidl = NULL; + CComPtr psb; + HRESULT hr = IUnknown_QueryService(m_pSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + CComPtr pbs; + if (SUCCEEDED(hr = psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) + if (SUCCEEDED(hr = pbs->GetPidl(ppidl)) && *ppidl) + return hr; + + CComPtr psv; + if (!FAILED_UNEXPECTEDLY(hr = psb->QueryActiveShellView(&psv))) + if (SUCCEEDED(hr = psv.p ? SHDOCVW_GetCurrentLocationFromView(*psv.p, ppidl) : E_FAIL)) + return hr; + return hr; +} + +HRESULT CNSCBand::_IsCurrentLocation(_In_ PCIDLIST_ABSOLUTE pidl) +{ + if (!pidl) + return E_INVALIDARG; + HRESULT hr = E_FAIL; + PIDLIST_ABSOLUTE location = NULL; + hr = _GetCurrentLocation(&location); + if (SUCCEEDED(hr)) + hr = SHELL_IsEqualAbsoluteID(location, pidl) ? S_OK : S_FALSE; + ILFree(location); + return hr; +} + +HRESULT CNSCBand::_ExecuteCommand(_In_ CComPtr& menu, _In_ UINT nCmd) +{ + CComPtr pBrowserOleWnd; + HRESULT hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, + IID_PPV_ARG(IOleWindow, &pBrowserOleWnd)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + HWND browserWnd; + hr = pBrowserOleWnd->GetWindow(&browserWnd); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + CMINVOKECOMMANDINFO cmi = { sizeof(cmi) }; + cmi.lpVerb = MAKEINTRESOURCEA(nCmd); + cmi.hwnd = browserWnd; + if (::GetKeyState(VK_SHIFT) < 0) + cmi.fMask |= CMIC_MASK_SHIFT_DOWN; + if (::GetKeyState(VK_CONTROL) < 0) + cmi.fMask |= CMIC_MASK_CONTROL_DOWN; + + return menu->InvokeCommand(&cmi); +} + +void CNSCBand::_RegisterChangeNotify() +{ +#define TARGET_EVENTS ( \ + SHCNE_DRIVEADD | SHCNE_MKDIR | SHCNE_CREATE | SHCNE_DRIVEREMOVED | SHCNE_RMDIR | \ + SHCNE_DELETE | SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEDIR | \ + SHCNE_UPDATEITEM | SHCNE_ASSOCCHANGED \ +) + // Register shell notification + SHChangeNotifyEntry shcne = { m_pidlRoot, TRUE }; + m_shellRegID = SHChangeNotifyRegister(m_hWnd, + SHCNRF_NewDelivery | SHCNRF_ShellLevel, + TARGET_EVENTS, + WM_USER_SHELLEVENT, + 1, &shcne); + if (!m_shellRegID) + { + ERR("Something went wrong, error %08x\n", GetLastError()); + } +} + +void CNSCBand::_UnregisterChangeNotify() +{ + SHChangeNotifyDeregister(m_shellRegID); + m_shellRegID = 0; +} + +void CNSCBand::_DestroyTreeView() +{ + TRACE("Cleaning up treeview...\n"); + /* Remove all items of the treeview */ + ::RevokeDragDrop(m_hwndTreeView); + TreeView_DeleteAllItems(m_hwndTreeView); + m_hwndTreeView.DestroyWindow(); + m_pDesktop = NULL; + m_hRoot = NULL; + TRACE("Cleanup ok\n"); +} + +void CNSCBand::_DestroyToolbar() +{ + m_hwndToolbar.DestroyWindow(); +} + +HRESULT CNSCBand::_CreateTreeView(HWND hwndParent) +{ + RefreshFlags(&m_dwTVStyle, &m_dwTVExStyle, &m_dwEnumFlags); + HWND hwndTV = ::CreateWindowExW(m_dwTVExStyle, WC_TREEVIEWW, NULL, m_dwTVStyle, 0, 0, 0, 0, + hwndParent, (HMENU)UlongToHandle(IDW_TREEVIEW), instance, NULL); + ATLASSERT(hwndTV); + if (!hwndTV) + return E_FAIL; + + m_hwndTreeView.Attach(hwndTV); + ::RegisterDragDrop(m_hwndTreeView, dynamic_cast(this)); + + // Init the treeview here + HRESULT hr = SHGetDesktopFolder(&m_pDesktop); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + m_pidlRoot.Free(); + hr = SHGetFolderLocation(m_hWnd, _GetRootCsidl(), NULL, 0, &m_pidlRoot); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + // Create image list and set + IImageList *piml; + hr = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &piml)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + TreeView_SetImageList(m_hwndTreeView, (HIMAGELIST)piml, TVSIL_NORMAL); + return S_OK; +} + +BOOL +CNSCBand::_IsTreeItemInEnum( + _In_ HTREEITEM hItem, + _In_ IEnumIDList *pEnum) +{ + CItemData* pItemData = GetItemData(hItem); + if (!pItemData) + return FALSE; + + pEnum->Reset(); + + CComHeapPtr pidlTemp; + while (pEnum->Next(1, &pidlTemp, NULL) == S_OK) + { + if (ILIsEqual(pidlTemp, pItemData->relativePidl)) + return TRUE; + + pidlTemp.Free(); + } + + return FALSE; +} + +BOOL +CNSCBand::_TreeItemHasThisChild( + _In_ HTREEITEM hItem, + _In_ PCITEMID_CHILD pidlChild) +{ + for (hItem = TreeView_GetChild(m_hwndTreeView, hItem); hItem; + hItem = TreeView_GetNextSibling(m_hwndTreeView, hItem)) + { + CItemData* pItemData = GetItemData(hItem); + if (ILIsEqual(pItemData->relativePidl, pidlChild)) + return TRUE; + } + + return FALSE; +} + +HRESULT +CNSCBand::_GetItemEnum( + _Out_ CComPtr& pEnum, + _In_ HTREEITEM hItem, + _Out_opt_ IShellFolder **ppFolder) +{ + CComPtr psfDesktop; + HRESULT hr = SHGetDesktopFolder(&psfDesktop); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + CComPtr pFolder; + if (!ppFolder) + ppFolder = &pFolder; + + if (hItem == m_hRoot && hItem) + { + *ppFolder = psfDesktop; + (*ppFolder)->AddRef(); + } + else + { + CItemData* pItemData = GetItemData(hItem); + if (!pItemData && hItem == TVI_ROOT && !_WantsRootItem()) + hr = psfDesktop->BindToObject(m_pidlRoot, NULL, IID_PPV_ARG(IShellFolder, ppFolder)); + else + hr = psfDesktop->BindToObject(pItemData->absolutePidl, NULL, IID_PPV_ARG(IShellFolder, ppFolder)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + } + + return (*ppFolder)->EnumObjects(NULL, _GetEnumFlags(), &pEnum); +} + +BOOL CNSCBand::_ItemHasAnyChild(_In_ HTREEITEM hItem) +{ + CComPtr pEnum; + HRESULT hr = _GetItemEnum(pEnum, hItem); + if (FAILED(hr)) + return FALSE; + + CComHeapPtr pidlTemp; + hr = pEnum->Next(1, &pidlTemp, NULL); + return SUCCEEDED(hr); +} + +void CNSCBand::_RefreshRecurse(_In_ HTREEITEM hTarget) +{ + CComPtr pEnum; + HRESULT hrEnum = _GetItemEnum(pEnum, hTarget); + + // Delete zombie items + HTREEITEM hItem, hNextItem; + for (hItem = TreeView_GetChild(m_hwndTreeView, hTarget); hItem; hItem = hNextItem) + { + hNextItem = TreeView_GetNextSibling(m_hwndTreeView, hItem); + + if (SUCCEEDED(hrEnum) && !_IsTreeItemInEnum(hItem, pEnum)) + TreeView_DeleteItem(m_hwndTreeView, hItem); + } + + pEnum = NULL; + hrEnum = _GetItemEnum(pEnum, hTarget); + + CItemData* pItemData = ((hTarget == TVI_ROOT) ? NULL : GetItemData(hTarget)); + + // Insert new items and update items + if (SUCCEEDED(hrEnum)) + { + CComHeapPtr pidlTemp; + while (pEnum->Next(1, &pidlTemp, NULL) == S_OK) + { + if (!_TreeItemHasThisChild(hTarget, pidlTemp)) + { + if (pItemData) + { + CComHeapPtr pidlAbsolute(ILCombine(pItemData->absolutePidl, pidlTemp)); + _InsertItem(hTarget, pidlAbsolute, pidlTemp, TRUE); + } + else + { + CComHeapPtr pidlAbsolute(ILCombine(m_pidlRoot, pidlTemp)); + _InsertItem(hTarget, pidlAbsolute, pidlTemp, TRUE); + } + } + pidlTemp.Free(); + } + } + + // Update children and recurse + for (hItem = TreeView_GetChild(m_hwndTreeView, hTarget); hItem; hItem = hNextItem) + { + hNextItem = TreeView_GetNextSibling(m_hwndTreeView, hItem); + + TV_ITEMW item = { TVIF_HANDLE | TVIF_CHILDREN }; + item.hItem = hItem; + item.cChildren = _ItemHasAnyChild(hItem); + TreeView_SetItem(m_hwndTreeView, &item); + + if (TreeView_GetItemState(m_hwndTreeView, hItem, TVIS_EXPANDEDONCE) & TVIS_EXPANDEDONCE) + _RefreshRecurse(hItem); + } +} + +void CNSCBand::_Refresh() +{ + m_hwndTreeView.SendMessage(WM_SETREDRAW, FALSE, 0); + _RefreshRecurse(_WantsRootItem() ? m_hRoot : TVI_ROOT); + m_hwndTreeView.SendMessage(WM_SETREDRAW, TRUE, 0); +} + +LRESULT CNSCBand::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + KillTimer(wParam); + + if (wParam == TIMER_ID_REFRESH) + _Refresh(); + + return 0; +} + +void +CNSCBand::OnChangeNotify( + _In_opt_ LPCITEMIDLIST pidl0, + _In_opt_ LPCITEMIDLIST pidl1, + _In_ LONG lEvent) +{ + switch (lEvent) + { + case SHCNE_DRIVEADD: + case SHCNE_MKDIR: + case SHCNE_CREATE: + case SHCNE_DRIVEREMOVED: + case SHCNE_RMDIR: + case SHCNE_DELETE: + case SHCNE_RENAMEFOLDER: + case SHCNE_RENAMEITEM: + case SHCNE_UPDATEDIR: + case SHCNE_UPDATEITEM: + case SHCNE_ASSOCCHANGED: + { + KillTimer(TIMER_ID_REFRESH); + SetTimer(TIMER_ID_REFRESH, 500, NULL); + break; + } + default: + { + TRACE("lEvent: 0x%08lX\n", lEvent); + break; + } + } +} + +HTREEITEM +CNSCBand::_InsertItem( + _In_opt_ HTREEITEM hParent, + _Inout_ IShellFolder *psfParent, + _In_ LPCITEMIDLIST pElt, + _In_ LPCITEMIDLIST pEltRelative, + _In_ BOOL bSort) +{ + /* 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; + + /* Get the name of the node */ + WCHAR wszDisplayName[MAX_PATH]; + STRRET strret; + hr = psfParent->GetDisplayNameOf(pEltRelative, SHGDN_INFOLDER, &strret); + if (FAILED_UNEXPECTEDLY(hr)) + return NULL; + + hr = StrRetToBufW(&strret, pEltRelative, wszDisplayName, MAX_PATH); + if (FAILED_UNEXPECTEDLY(hr)) + return NULL; + + /* Get the icon of the node */ + INT iIcon = SHMapPIDLToSystemImageListIndex(psfParent, pEltRelative, NULL); + + CItemData* pChildInfo = new CItemData; + if (!pChildInfo) + { + ERR("Failed to allocate CItemData\n"); + return NULL; + } + pChildInfo->absolutePidl.Attach(ILClone(pElt)); + pChildInfo->relativePidl.Attach(ILClone(pEltRelative)); + + // Set up our treeview template + TV_INSERTSTRUCT tvInsert = { hParent, 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.lParam = (LPARAM)pChildInfo; + + if (!(attrs & SFGAO_STREAM) && (attrs & SFGAO_HASSUBFOLDER)) + tvInsert.item.cChildren = 1; + + HTREEITEM htiCreated = TreeView_InsertItem(m_hwndTreeView, &tvInsert); + + if (bSort) + _SortItems(hParent); + + return htiCreated; +} + +/* This is the slow version of the above method */ +HTREEITEM +CNSCBand::_InsertItem( + _In_opt_ HTREEITEM hParent, + _In_ LPCITEMIDLIST pElt, + _In_ LPCITEMIDLIST pEltRelative, + _In_ BOOL bSort) +{ + CComPtr psfFolder; + HRESULT hr = SHBindToParent(pElt, IID_PPV_ARG(IShellFolder, &psfFolder), NULL); + if (FAILED_UNEXPECTEDLY(hr)) + return NULL; + + return _InsertItem(hParent, psfFolder, pElt, pEltRelative, bSort); +} + +BOOL CNSCBand::_InsertSubitems(HTREEITEM hItem, LPCITEMIDLIST entry) +{ + ULONG fetched = 1, uItemCount = 0; + + CComPtr pEnum; + CComPtr pFolder; + HRESULT hr = _GetItemEnum(pEnum, hItem, &pFolder); + if (FAILED_UNEXPECTEDLY(hr)) + return FALSE; + + /* Don't redraw while we add stuff into the tree */ + m_hwndTreeView.SendMessage(WM_SETREDRAW, FALSE, 0); + + LPITEMIDLIST pidlSub; + while (SUCCEEDED(pEnum->Next(1, &pidlSub, &fetched)) && pidlSub && fetched) + { + LPITEMIDLIST pidlSubComplete; + pidlSubComplete = ILCombine(entry, pidlSub); + + if (_InsertItem(hItem, pFolder, pidlSubComplete, pidlSub, FALSE)) + ++uItemCount; + + ILFree(pidlSubComplete); + ILFree(pidlSub); + } + + /* Let's do sorting */ + _SortItems(hItem); + + /* Now we can redraw */ + m_hwndTreeView.SendMessage(WM_SETREDRAW, TRUE, 0); + + return (uItemCount > 0); +} + +// *** message handlers *** + +LRESULT CNSCBand::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + if (FAILED_UNEXPECTEDLY(_CreateToolbar(m_hWnd))) + return -1; + if (FAILED_UNEXPECTEDLY(_CreateTreeView(m_hWnd))) + return -1; + return 0; +} + +LRESULT CNSCBand::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + _DestroyTreeView(); + _DestroyToolbar(); + _UnregisterChangeNotify(); + return 0; +} + +LRESULT CNSCBand::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) +{ + if (!m_hwndTreeView) + return 0; + + RECT rc; + GetClientRect(&rc); + LONG cx = rc.right, cy = rc.bottom; + + RECT rcTB; + LONG cyTB = 0; + if (m_hwndToolbar) + { + m_hwndToolbar.SendMessage(TB_AUTOSIZE, 0, 0); + m_hwndToolbar.GetWindowRect(&rcTB); + cyTB = rcTB.bottom - rcTB.top; + } + + m_hwndTreeView.MoveWindow(0, cyTB, cx, cy - cyTB); + return 0; +} + +LRESULT CNSCBand::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + m_bFocused = TRUE; + IUnknown_OnFocusChangeIS(m_pSite, reinterpret_cast(this), TRUE); + bHandled = FALSE; + return 0; +} + +LRESULT CNSCBand::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + IUnknown_OnFocusChangeIS(m_pSite, reinterpret_cast(this), FALSE); + m_bFocused = FALSE; + return 0; +} + +HRESULT CNSCBand::_AddFavorite() +{ + CComHeapPtr pidlCurrent; + _GetCurrentLocation(&pidlCurrent); + + WCHAR szCurDir[MAX_PATH]; + if (!ILGetDisplayName(pidlCurrent, szCurDir)) + { + FIXME("\n"); + return E_FAIL; + } + + WCHAR szPath[MAX_PATH], szSuffix[32]; + SHGetSpecialFolderPathW(m_hWnd, szPath, CSIDL_FAVORITES, TRUE); + PathAppendW(szPath, PathFindFileNameW(szCurDir)); + + const INT ich = lstrlenW(szPath); + for (INT iTry = 2; iTry <= 9999; ++iTry) + { + PathAddExtensionW(szPath, L".lnk"); + if (!PathFileExistsW(szPath)) + break; + szPath[ich] = UNICODE_NULL; + wsprintfW(szSuffix, L" (%d)", iTry); + lstrcatW(szPath, szSuffix); + } + + TRACE("%S, %S\n", szCurDir, szPath); + + return SHDOCVW_CreateShortcut(szPath, pidlCurrent, NULL); +} + +LRESULT CNSCBand::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + switch (LOWORD(wParam)) + { + case ID_ADD: + { + _AddFavorite(); + break; + } + case ID_ORGANIZE: + { + SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_INVOKEIDLIST }; + sei.hwnd = m_hWnd; + sei.nShow = SW_SHOWNORMAL; + sei.lpIDList = m_pidlRoot; + ::ShellExecuteExW(&sei); + break; + } + } + return 0; +} + +BOOL CNSCBand::OnTreeItemExpanding(_In_ LPNMTREEVIEW pnmtv) +{ + CItemData *pItemData; + + if (pnmtv->action == TVE_COLLAPSE) + { + if (pnmtv->itemNew.hItem == m_hRoot) + { + // Prenvent root from collapsing + pnmtv->itemNew.mask |= TVIF_STATE; + pnmtv->itemNew.stateMask |= TVIS_EXPANDED; + pnmtv->itemNew.state &= ~TVIS_EXPANDED; + pnmtv->action = TVE_EXPAND; + return TRUE; + } + } + + if (pnmtv->action == TVE_EXPAND) + { + // Grab our directory PIDL + pItemData = GetItemData(pnmtv->itemNew.hItem); + // We have it, let's try + if (pItemData && !pItemData->expanded) + { + if (_InsertSubitems(pnmtv->itemNew.hItem, pItemData->absolutePidl)) + { + pItemData->expanded = TRUE; + } + else + { + // remove subitem "+" since we failed to add subitems + TVITEMW tvItem = { TVIF_CHILDREN, pnmtv->itemNew.hItem }; + tvItem.cChildren = 0; + TreeView_SetItem(m_hwndTreeView, &tvItem); + } + } + } + return FALSE; +} + +BOOL CNSCBand::OnTreeItemDeleted(_In_ LPNMTREEVIEW pnmtv) +{ + // Navigate to parent when deleting selected item + HTREEITEM hItem = pnmtv->itemOld.hItem; + HTREEITEM hParent = TreeView_GetParent(m_hwndTreeView, hItem); + if (hParent && TreeView_GetSelection(m_hwndTreeView) == hItem) + TreeView_SelectItem(m_hwndTreeView, hParent); + + /* Destroy memory associated to our node */ + CItemData* pItemData = GetItemData(hItem); + if (!pItemData) + return FALSE; + + delete pItemData; + + return TRUE; +} + +void CNSCBand::_OnSelectionChanged(_In_ LPNMTREEVIEW pnmtv) +{ + HTREEITEM hItem = pnmtv->itemNew.hItem; + if (!hItem) + return; + CItemData* pItemData = GetItemData(hItem); + if (pItemData) + OnSelectionChanged(pItemData->absolutePidl); +} + +void CNSCBand::OnTreeItemDragging(_In_ LPNMTREEVIEW pnmtv, _In_ BOOL isRightClick) +{ + CItemData* pItemData = GetItemData(pnmtv->itemNew.hItem); + if (!pItemData) + return; + + HRESULT hr; + CComPtr pSrcFolder; + LPCITEMIDLIST pLast; + hr = SHBindToParent(pItemData->absolutePidl, IID_PPV_ARG(IShellFolder, &pSrcFolder), &pLast); + if (FAILED_UNEXPECTEDLY(hr)) + return; + + SFGAOF attrs = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK; + pSrcFolder->GetAttributesOf(1, &pLast, &attrs); + + DWORD dwEffect = 0; + if (attrs & SFGAO_CANCOPY) + dwEffect |= DROPEFFECT_COPY; + if (attrs & SFGAO_CANMOVE) + dwEffect |= DROPEFFECT_MOVE; + if (attrs & SFGAO_CANLINK) + dwEffect |= DROPEFFECT_LINK; + + CComPtr pObj; + hr = pSrcFolder->GetUIObjectOf(m_hWnd, 1, &pLast, IID_IDataObject, 0, (LPVOID*)&pObj); + if (FAILED_UNEXPECTEDLY(hr)) + return; + + DoDragDrop(pObj, this, dwEffect, &dwEffect); +} + +LRESULT CNSCBand::OnBeginLabelEdit(_In_ LPNMTVDISPINFO dispInfo) +{ + // TODO: put this in a function ? (mostly copypasta from CDefView) + DWORD dwAttr = SFGAO_CANRENAME; + CComPtr pParent; + LPCITEMIDLIST pChild; + HRESULT hr; + + CItemData *info = GetItemData(dispInfo->item.hItem); + if (!info) + return FALSE; + + hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pChild); + if (FAILED_UNEXPECTEDLY(hr)) + return FALSE; + + hr = pParent->GetAttributesOf(1, &pChild, &dwAttr); + if (SUCCEEDED(hr) && (dwAttr & SFGAO_CANRENAME)) + { + m_isEditing = TRUE; + m_oldSelected = NULL; + return FALSE; + } + + return TRUE; +} + +HRESULT CNSCBand::_UpdateBrowser(LPCITEMIDLIST pidlGoto) +{ + CComPtr pBrowserService; + HRESULT hr = IUnknown_QueryService(m_pSite, SID_STopLevelBrowser, + IID_PPV_ARG(IShellBrowser, &pBrowserService)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + hr = pBrowserService->BrowseObject(pidlGoto, SBSP_SAMEBROWSER | SBSP_ABSOLUTE); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + return S_OK; +} + +LRESULT CNSCBand::OnEndLabelEdit(_In_ LPNMTVDISPINFO dispInfo) +{ + CItemData *info = GetItemData(dispInfo->item.hItem); + HRESULT hr; + + m_isEditing = FALSE; + if (m_oldSelected) + { + ++m_mtxBlockNavigate; + TreeView_SelectItem(m_hwndTreeView, m_oldSelected); + --m_mtxBlockNavigate; + } + + if (!dispInfo->item.pszText) + return FALSE; + + CComPtr pParent; + LPCITEMIDLIST pidlChild; + BOOL RenamedCurrent = _IsCurrentLocation(info->absolutePidl) == S_OK; + + hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pidlChild); + if (FAILED_UNEXPECTEDLY(hr)) + return FALSE; + + CComHeapPtr pidlNew; + hr = pParent->SetNameOf(m_hWnd, pidlChild, dispInfo->item.pszText, SHGDN_INFOLDER, &pidlNew); + if (SUCCEEDED(hr) && pidlNew) + { + CComPtr pPersist; + hr = pParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pPersist)); + if (FAILED_UNEXPECTEDLY(hr)) + return FALSE; + + CComHeapPtr pidlParent; + hr = pPersist->GetCurFolder(&pidlParent); + if (FAILED_UNEXPECTEDLY(hr)) + return FALSE; + + CComHeapPtr pidlNewAbs(ILCombine(pidlParent, pidlNew)); + if (RenamedCurrent) + { + _UpdateBrowser(pidlNewAbs); + } + else + { + // Tell everyone if SetNameOf forgot, this causes IShellView to update itself when we rename a child + SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, info->absolutePidl, pidlNewAbs); + } + + return TRUE; + } + + return FALSE; +} + +LRESULT CNSCBand::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + NMHDR *pnmhdr = (NMHDR*)lParam; + switch (pnmhdr->code) + { + case TVN_ITEMEXPANDING: + return OnTreeItemExpanding((LPNMTREEVIEW)lParam); + //case TVN_SINGLEEXPAND: + case TVN_SELCHANGED: + if (pnmhdr->hwndFrom == m_hwndTreeView) + _OnSelectionChanged((LPNMTREEVIEW)lParam); + break; + case TVN_DELETEITEM: + OnTreeItemDeleted((LPNMTREEVIEW)lParam); + break; + case NM_CLICK: + case NM_RCLICK: + if (pnmhdr->hwndFrom == m_hwndTreeView) + { + TVHITTESTINFO HitTest; + ::GetCursorPos(&HitTest.pt); + ::ScreenToClient(m_hwndTreeView, &HitTest.pt); + TreeView_HitTest(m_hwndTreeView, &HitTest); + + if (HitTest.flags & (TVHT_ABOVE | TVHT_BELOW | TVHT_NOWHERE)) + return TRUE; // Prevents click processing + + if (HitTest.flags & TVHT_ONITEMBUTTON) // [+] / [-] + break; // Do default processing + + // Generate selection notification even if same item + m_hwndTreeView.SendMessage(WM_SETREDRAW, FALSE, 0); + TreeView_SelectItem(m_hwndTreeView, NULL); + TreeView_SelectItem(m_hwndTreeView, HitTest.hItem); + m_hwndTreeView.SendMessage(WM_SETREDRAW, TRUE, 0); + + if (pnmhdr->code == NM_CLICK) + return TRUE; // Prevents click processing + } + break; + case TVN_BEGINDRAG: + case TVN_BEGINRDRAG: + OnTreeItemDragging((LPNMTREEVIEW)lParam, pnmhdr->code == TVN_BEGINRDRAG); + break; + case TVN_BEGINLABELEDITW: + return OnBeginLabelEdit((LPNMTVDISPINFO)lParam); + case TVN_ENDLABELEDITW: + return OnEndLabelEdit((LPNMTVDISPINFO)lParam); + default: + break; + } + + return 0; +} + +// Temporary menu +struct CMenuTemp +{ + HMENU m_hMenu = NULL; + CMenuTemp(HMENU hMenu) : m_hMenu(hMenu) + { + } + ~CMenuTemp() + { + if (m_hMenu) + ::DestroyMenu(m_hMenu); + } + operator HMENU() const + { + return m_hMenu; + } +}; + +// *** ATL event handlers *** +LRESULT CNSCBand::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + HWND hwndTarget = reinterpret_cast(wParam); + if (hwndTarget && (hwndTarget == m_hwndToolbar || hwndTarget == m_hWnd)) + { + FIXME("Show 'Close Toolbar' menu\n"); + return 0; + } + + HTREEITEM hItem = TreeView_GetSelection(m_hwndTreeView); + if (!hItem) + return 0; + + POINT pt = { (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam) }; + if ((UINT)lParam == (UINT)-1) + { + RECT rc; + if (TreeView_GetItemRect(m_hwndTreeView, hItem, &rc, TRUE)) + { + // Center of item rectangle + pt.x = (rc.left + rc.right) / 2; + pt.y = (rc.top + rc.bottom) / 2; + } + ClientToScreen(&pt); + } + + CItemData *info = GetItemData(hItem); + if (!info) + { + ERR("No node data, something has gone wrong\n"); + return 0; + } + + CComPtr pFolder; + LPCITEMIDLIST pidlChild; + HRESULT hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pFolder), &pidlChild); + if (FAILED_UNEXPECTEDLY(hr)) + return 0; + + CComPtr contextMenu; + hr = pFolder->GetUIObjectOf(m_hWnd, 1, &pidlChild, IID_NULL_PPV_ARG(IContextMenu, &contextMenu)); + if (FAILED_UNEXPECTEDLY(hr)) + return 0; + + IUnknown_SetSite(contextMenu, (IDeskBand *)this); + + UINT cmf = CMF_EXPLORE; + SFGAOF attr = SFGAO_CANRENAME; + hr = pFolder->GetAttributesOf(1, &pidlChild, &attr); + if (SUCCEEDED(hr) && (attr & SFGAO_CANRENAME)) + cmf |= CMF_CANRENAME; + + CMenuTemp menuTemp(::CreatePopupMenu()); + UINT idCmdFirst = max(FCIDM_SHVIEWFIRST, 1); + hr = contextMenu->QueryContextMenu(menuTemp, 0, idCmdFirst, FCIDM_SHVIEWLAST, cmf); + if (FAILED_UNEXPECTEDLY(hr)) + return 0; + + enum { flags = TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON }; + UINT uCommand = ::TrackPopupMenu(menuTemp, flags, pt.x, pt.y, 0, m_hWnd, NULL); + if (uCommand) + { + uCommand -= idCmdFirst; + + // Do DFM_CMD_RENAME in the treeview + if ((cmf & CMF_CANRENAME) && SHELL_IsVerb(contextMenu, uCommand, L"rename")) + { + m_hwndTreeView.SetFocus(); + if (TreeView_EditLabel(m_hwndTreeView, hItem)) + m_oldSelected = hItem; + return 0; + } + + hr = _ExecuteCommand(contextMenu, uCommand); + } + + return TRUE; +} + +// WM_USER_SHELLEVENT +LRESULT CNSCBand::OnShellEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) +{ + // We use SHCNRF_NewDelivery method + HANDLE hChange = (HANDLE)wParam; + DWORD dwProcID = (DWORD)lParam; + + PIDLIST_ABSOLUTE *ppidl = NULL; + LONG lEvent; + HANDLE hLock = SHChangeNotification_Lock(hChange, dwProcID, &ppidl, &lEvent); + if (!hLock) + { + ERR("!hLock\n"); + return 0; + } + + OnChangeNotify(ppidl[0], ppidl[1], (lEvent & ~SHCNE_INTERRUPT)); + + SHChangeNotification_Unlock(hLock); + return 0; +} + +// *** IOleWindow *** + +STDMETHODIMP CNSCBand::GetWindow(HWND *lphwnd) +{ + if (!lphwnd) + return E_INVALIDARG; + *lphwnd = m_hWnd; + return S_OK; +} + +STDMETHODIMP CNSCBand::ContextSensitiveHelp(BOOL fEnterMode) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +// *** IDockingWindow *** + +STDMETHODIMP CNSCBand::CloseDW(DWORD dwReserved) +{ + // We do nothing, we don't have anything to save yet + TRACE("CloseDW called\n"); + return S_OK; +} + +STDMETHODIMP CNSCBand::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved) +{ + /* Must return E_NOTIMPL according to MSDN */ + return E_NOTIMPL; +} + +STDMETHODIMP CNSCBand::ShowDW(BOOL fShow) +{ + m_fVisible = fShow; + ShowWindow(fShow ? SW_SHOW : SW_HIDE); + return S_OK; +} + +// *** IDeskBand *** + +STDMETHODIMP CNSCBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi) +{ + if (!pdbi) + return E_INVALIDARG; + + m_dwBandID = dwBandID; + + if (pdbi->dwMask & DBIM_MINSIZE) + { + pdbi->ptMinSize.x = 200; + pdbi->ptMinSize.y = 30; + } + + if (pdbi->dwMask & DBIM_MAXSIZE) + pdbi->ptMaxSize.y = -1; + + if (pdbi->dwMask & DBIM_INTEGRAL) + pdbi->ptIntegral.y = 1; + + if (pdbi->dwMask & DBIM_ACTUAL) + { + pdbi->ptActual.x = 200; + pdbi->ptActual.y = 30; + } + + if (pdbi->dwMask & DBIM_TITLE) + { + _GetTitle(pdbi->wszTitle, _countof(pdbi->wszTitle)); + } + + if (pdbi->dwMask & DBIM_MODEFLAGS) + pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT; + + if (pdbi->dwMask & DBIM_BKCOLOR) + pdbi->dwMask &= ~DBIM_BKCOLOR; + + return S_OK; +} + +// *** IObjectWithSite *** + +STDMETHODIMP CNSCBand::SetSite(IUnknown *pUnkSite) +{ + HRESULT hr; + + if (pUnkSite == m_pSite) + return S_OK; + + TRACE("SetSite called\n"); + + if (!pUnkSite) + { + DestroyWindow(); + m_hWnd = NULL; + } + + if (pUnkSite != m_pSite) + m_pSite = NULL; + + if (!pUnkSite) + return S_OK; + + HWND hwndParent; + hr = IUnknown_GetWindow(pUnkSite, &hwndParent); + if (FAILED_UNEXPECTEDLY(hr)) + return E_INVALIDARG; + + m_pSite = pUnkSite; + + if (m_hWnd) + { + SetParent(hwndParent); // Change its parent + } + else + { + enum { style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN }; + this->Create(hwndParent, NULL, NULL, style, 0, 0U, NULL); + } + + _RegisterChangeNotify(); + + return S_OK; +} + +STDMETHODIMP CNSCBand::GetSite(REFIID riid, void **ppvSite) +{ + if (!ppvSite) + return E_POINTER; + *ppvSite = m_pSite; + return S_OK; +} + +// *** IOleCommandTarget *** + +STDMETHODIMP CNSCBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +STDMETHODIMP CNSCBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +// *** IServiceProvider *** + +STDMETHODIMP CNSCBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject) +{ + return IUnknown_QueryService(m_pSite, guidService, riid, ppvObject); +} + +// *** IContextMenu *** + +STDMETHODIMP CNSCBand::QueryContextMenu( + HMENU hmenu, + UINT indexMenu, + UINT idCmdFirst, + UINT idCmdLast, + UINT uFlags) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +STDMETHODIMP CNSCBand::InvokeCommand( + LPCMINVOKECOMMANDINFO lpici) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +STDMETHODIMP CNSCBand::GetCommandString( + UINT_PTR idCmd, + UINT uType, + UINT *pwReserved, + LPSTR pszName, + UINT cchMax) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +// *** IInputObject *** + +STDMETHODIMP CNSCBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg) +{ + if (fActivate) + { + m_hwndTreeView.SetFocus(); + } + + if (lpMsg) + { + TranslateMessage(lpMsg); + DispatchMessage(lpMsg); + } + + return S_OK; +} + +STDMETHODIMP CNSCBand::HasFocusIO() +{ + return m_bFocused ? S_OK : S_FALSE; +} + +STDMETHODIMP CNSCBand::TranslateAcceleratorIO(LPMSG lpMsg) +{ + if (lpMsg->hwnd == m_hWnd || + (m_isEditing && IsChild(lpMsg->hwnd))) + { + TranslateMessage(lpMsg); + DispatchMessage(lpMsg); + return S_OK; + } + + return S_FALSE; +} + +// *** IPersist *** + +STDMETHODIMP CNSCBand::GetClassID(CLSID *pClassID) +{ + return E_NOTIMPL; +} + +// *** IPersistStream *** + +STDMETHODIMP CNSCBand::IsDirty() +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +STDMETHODIMP CNSCBand::Load(IStream *pStm) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +STDMETHODIMP CNSCBand::Save(IStream *pStm, BOOL fClearDirty) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +STDMETHODIMP CNSCBand::GetSizeMax(ULARGE_INTEGER *pcbSize) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +// *** IWinEventHandler *** + +STDMETHODIMP CNSCBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) +{ + return S_OK; +} + +STDMETHODIMP CNSCBand::IsWindowOwner(HWND hWnd) +{ + return SHIsChildOrSelf(m_hWnd, hWnd); +} + +// *** IBandNavigate *** + +STDMETHODIMP CNSCBand::Select(LPCITEMIDLIST pidl) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +// *** INamespaceProxy *** + +// Returns the ITEMIDLIST that should be navigated when an item is invoked. +STDMETHODIMP CNSCBand::GetNavigateTarget( + _In_ PCIDLIST_ABSOLUTE pidl, + _Out_ PIDLIST_ABSOLUTE *ppidlTarget, + _Out_ ULONG *pulAttrib) +{ + *pulAttrib = 0; + WCHAR szPath[MAX_PATH]; + if (!SHGetPathFromIDListW(pidl, szPath)) + return E_FAIL; + + if (lstrcmpiW(PathFindExtensionW(szPath), L".lnk") == 0) // shortcut file? + { + WCHAR szTarget[MAX_PATH]; + HRESULT hr = SHDOCVW_GetPathOfShortcut(m_hWnd, szPath, szTarget); + if (SUCCEEDED(hr)) + { + lstrcpynW(szPath, szTarget, _countof(szPath)); + *pulAttrib |= SFGAO_LINK; + } + } + + if (PathIsDirectoryW(szPath) || PathIsRootW(szPath)) + *pulAttrib |= SFGAO_FOLDER; + + *ppidlTarget = ILCreateFromPathW(szPath); + return S_OK; +} + +// Handles a user action on an item. +STDMETHODIMP CNSCBand::Invoke(_In_ PCIDLIST_ABSOLUTE pidl) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +// Called when the user has selected an item. +STDMETHODIMP CNSCBand::OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) +{ + return S_OK; +} + +// Returns flags used to update the tree control. +STDMETHODIMP CNSCBand::RefreshFlags( + _Out_ DWORD *pdwStyle, + _Out_ DWORD *pdwExStyle, + _Out_ DWORD *dwEnum) +{ + *pdwStyle = _GetTVStyle(); + *pdwExStyle = _GetTVExStyle(); + *dwEnum = _GetEnumFlags(); + return S_OK; +} + +STDMETHODIMP CNSCBand::CacheItem( + _In_ PCIDLIST_ABSOLUTE pidl) +{ + UNIMPLEMENTED; + return E_NOTIMPL; +} + +// *** IDropTarget methods *** +STDMETHODIMP CNSCBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) +{ + ERR("Entering drag\n"); + m_pCurObject = pObj; + m_oldSelected = TreeView_GetSelection(m_hwndTreeView); + return DragOver(glfKeyState, pt, pdwEffect); +} + +STDMETHODIMP CNSCBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) +{ + TVHITTESTINFO info; + 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_hwndTreeView, &info); + + HRESULT hr; + if (!info.hItem) + { + m_childTargetNode = NULL; + m_pDropTarget = NULL; + *pdwEffect = DROPEFFECT_NONE; + return S_OK; + } + + ++m_mtxBlockNavigate; + TreeView_SelectItem(m_hwndTreeView, info.hItem); + --m_mtxBlockNavigate; + + // Delegate to shell folder + if (m_pDropTarget && info.hItem != m_childTargetNode) + m_pDropTarget = NULL; + + if (info.hItem != m_childTargetNode) + { + CItemData *pItemData = GetItemData(info.hItem); + if (!pItemData) + return E_FAIL; + + CComPtr pFolder; + if (_ILIsDesktop(pItemData->absolutePidl)) + { + pFolder = m_pDesktop; + } + else + { + hr = m_pDesktop->BindToObject(pItemData->absolutePidl, 0, IID_PPV_ARG(IShellFolder, &pFolder)); + 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 = pFolder->CreateViewObject(m_hWnd, IID_PPV_ARG(IDropTarget, &m_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 = m_pDropTarget->DragEnter(m_pCurObject, glfKeyState, pt, pdwEffect); + m_childTargetNode = info.hItem; + } + + if (m_pDropTarget) + hr = m_pDropTarget->DragOver(glfKeyState, pt, pdwEffect); + + return S_OK; +} + +STDMETHODIMP CNSCBand::DragLeave() +{ + ++m_mtxBlockNavigate; + TreeView_SelectItem(m_hwndTreeView, m_oldSelected); + --m_mtxBlockNavigate; + m_childTargetNode = NULL; + if (m_pCurObject) + m_pCurObject = NULL; + return S_OK; +} + +STDMETHODIMP CNSCBand::Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) +{ + if (!m_pDropTarget) + return E_FAIL; + m_pDropTarget->Drop(pObj, glfKeyState, pt, pdwEffect); + DragLeave(); + return S_OK; +} + +// *** IDropSource methods *** + +STDMETHODIMP CNSCBand::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) +{ + if (fEscapePressed) + return DRAGDROP_S_CANCEL; + if ((grfKeyState & MK_LBUTTON) || (grfKeyState & MK_RBUTTON)) + return S_OK; + return DRAGDROP_S_DROP; +} + +STDMETHODIMP CNSCBand::GiveFeedback(DWORD dwEffect) +{ + return DRAGDROP_S_USEDEFAULTCURSORS; +} diff --git a/dll/win32/shdocvw/CNSCBand.h b/dll/win32/shdocvw/CNSCBand.h new file mode 100644 index 00000000000..f6c06afeb98 --- /dev/null +++ b/dll/win32/shdocvw/CNSCBand.h @@ -0,0 +1,231 @@ +/* + * PROJECT: ReactOS shdocvw + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: NameSpace Control Band + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#pragma once + +#define NSCBANDCLASSNAME L"ReactOS NameSpace Control Band" + +#define WM_USER_SHELLEVENT (WM_USER + 88) + +#ifdef __cplusplus +class CNSCBand + : public CWindowImpl + , public IDeskBand + , public IObjectWithSite + , public IInputObject + , public IPersistStream + , public IOleCommandTarget + , public IServiceProvider + , public IContextMenu + , public IBandNavigate + , public IWinEventHandler + , public INamespaceProxy + , public IDropTarget + , public IDropSource +{ +public: + DECLARE_WND_CLASS_EX(NSCBANDCLASSNAME, 0, COLOR_3DFACE) + static LPCWSTR GetWndClassName() { return NSCBANDCLASSNAME; } + + CNSCBand(); + virtual ~CNSCBand(); + + // The node of TreeView + struct CItemData + { + CComHeapPtr absolutePidl; + CComHeapPtr relativePidl; + BOOL expanded = FALSE; + }; + CItemData* GetItemData(_In_ HTREEITEM hItem); + + // *** IOleWindow methods *** + STDMETHODIMP GetWindow(HWND *lphwnd) override; + STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) override; + + // *** IDockingWindow methods *** + STDMETHODIMP CloseDW(DWORD dwReserved) override; + STDMETHODIMP ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved) override; + STDMETHODIMP ShowDW(BOOL fShow) override; + + // *** IDeskBand methods *** + STDMETHODIMP GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi) override; + + // *** IObjectWithSite methods *** + STDMETHODIMP SetSite(IUnknown *pUnkSite) override; + STDMETHODIMP GetSite(REFIID riid, void **ppvSite) override; + + // *** IOleCommandTarget methods *** + STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) override; + STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) override; + + // *** IServiceProvider methods *** + STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppvObject) override; + + // *** IContextMenu methods *** + STDMETHODIMP QueryContextMenu( + HMENU hmenu, + UINT indexMenu, + UINT idCmdFirst, + UINT idCmdLast, + UINT uFlags) override; + STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici) override; + STDMETHODIMP GetCommandString( + UINT_PTR idCmd, + UINT uType, + UINT *pwReserved, + LPSTR pszName, + UINT cchMax) override; + + // *** IInputObject methods *** + STDMETHODIMP UIActivateIO(BOOL fActivate, LPMSG lpMsg) override; + STDMETHODIMP HasFocusIO() override; + STDMETHODIMP TranslateAcceleratorIO(LPMSG lpMsg) override; + + // *** IPersist methods *** + STDMETHODIMP GetClassID(CLSID *pClassID) override; + + // *** IPersistStream methods *** + STDMETHODIMP IsDirty() override; + STDMETHODIMP Load(IStream *pStm) override; + STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty) override; + STDMETHODIMP GetSizeMax(ULARGE_INTEGER *pcbSize) override; + + // *** IWinEventHandler methods *** + STDMETHODIMP OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult) override; + STDMETHODIMP IsWindowOwner(HWND hWnd) override; + + // *** IBandNavigate methods *** + STDMETHODIMP Select(LPCITEMIDLIST pidl) override; + + // *** INamespaceProxy methods *** + STDMETHODIMP GetNavigateTarget( + _In_ PCIDLIST_ABSOLUTE pidl, + _Out_ PIDLIST_ABSOLUTE *ppidlTarget, + _Out_ ULONG *pulAttrib) override; + STDMETHODIMP Invoke(_In_ PCIDLIST_ABSOLUTE pidl) override; + STDMETHODIMP OnSelectionChanged(_In_ PCIDLIST_ABSOLUTE pidl) override; + STDMETHODIMP RefreshFlags( + _Out_ DWORD *pdwStyle, + _Out_ DWORD *pdwExStyle, + _Out_ DWORD *dwEnum) override; + STDMETHODIMP CacheItem(_In_ PCIDLIST_ABSOLUTE pidl) override; + + // *** IDropTarget methods *** + STDMETHODIMP DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) override; + STDMETHODIMP DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) override; + STDMETHODIMP DragLeave() override; + STDMETHODIMP Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect) override; + + // *** IDropSource methods *** + STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) override; + STDMETHODIMP GiveFeedback(DWORD dwEffect) override; + +protected: + DWORD m_dwTVStyle = 0; + DWORD m_dwTVExStyle = 0; + DWORD m_dwEnumFlags = 0; + BOOL m_fVisible = FALSE; + BOOL m_bFocused = FALSE; + DWORD m_dwBandID = 0; + CComPtr m_pSite; + CComPtr m_pDesktop; + CComHeapPtr m_pidlRoot; + HIMAGELIST m_hToolbarImageList = NULL; + CToolbar<> m_hwndToolbar; + CTreeView m_hwndTreeView; + LONG m_mtxBlockNavigate = 0; // A "lock" that prevents internal selection changes to initiate a navigation to the newly selected item. + BOOL m_isEditing = FALSE; + HTREEITEM m_hRoot = NULL; + HTREEITEM m_oldSelected = NULL; + DWORD m_adviseCookie = 0; + ULONG m_shellRegID = 0; + + // *** Drop target information *** + CComPtr m_pDropTarget; + HTREEITEM m_childTargetNode = NULL; + CComPtr m_pCurObject; + + VOID OnFinalMessage(HWND) override; + + // *** helper methods *** + virtual INT _GetRootCsidl() = 0; + virtual HRESULT _CreateTreeView(HWND hwndParent); + virtual HRESULT _CreateToolbar(HWND hwndParent) { return S_OK; } + virtual void _DestroyTreeView(); + virtual void _DestroyToolbar(); + virtual DWORD _GetTVStyle() = 0; + virtual DWORD _GetTVExStyle() = 0; + virtual DWORD _GetEnumFlags() = 0; + virtual BOOL _GetTitle(LPWSTR pszTitle, INT cchTitle) = 0; + virtual BOOL _WantsRootItem() = 0; + virtual void _SortItems(HTREEITEM hParent) = 0; + void _RegisterChangeNotify(); + void _UnregisterChangeNotify(); + BOOL OnTreeItemExpanding(_In_ LPNMTREEVIEW pnmtv); + BOOL OnTreeItemDeleted(_In_ LPNMTREEVIEW pnmtv); + void _OnSelectionChanged(_In_ LPNMTREEVIEW pnmtv); + void OnTreeItemDragging(_In_ LPNMTREEVIEW pnmtv, _In_ BOOL isRightClick); + LRESULT OnBeginLabelEdit(_In_ LPNMTVDISPINFO dispInfo); + LRESULT OnEndLabelEdit(_In_ LPNMTVDISPINFO dispInfo); + void OnChangeNotify( + _In_opt_ LPCITEMIDLIST pidl0, + _In_opt_ LPCITEMIDLIST pidl1, + _In_ LONG lEvent); + HRESULT _ExecuteCommand(_In_ CComPtr& menu, _In_ UINT nCmd); + HTREEITEM _InsertItem( + _In_opt_ HTREEITEM hParent, + _Inout_ IShellFolder *psfParent, + _In_ LPCITEMIDLIST pElt, + _In_ LPCITEMIDLIST pEltRelative, + _In_ BOOL bSort); + HTREEITEM _InsertItem( + _In_opt_ HTREEITEM hParent, + _In_ LPCITEMIDLIST pElt, + _In_ LPCITEMIDLIST pEltRelative, + _In_ BOOL bSort); + BOOL _InsertSubitems(HTREEITEM hItem, LPCITEMIDLIST entry); + HRESULT _UpdateBrowser(LPCITEMIDLIST pidlGoto); + HRESULT _GetCurrentLocation(_Out_ PIDLIST_ABSOLUTE *ppidl); + HRESULT _IsCurrentLocation(_In_ PCIDLIST_ABSOLUTE pidl); + void _Refresh(); + void _RefreshRecurse(_In_ HTREEITEM hItem); + BOOL _IsTreeItemInEnum(_In_ HTREEITEM hItem, _In_ IEnumIDList *pEnum); + BOOL _TreeItemHasThisChild(_In_ HTREEITEM hItem, _In_ PCITEMID_CHILD pidlChild); + HRESULT _GetItemEnum( + _Out_ CComPtr& pEnum, + _In_ HTREEITEM hItem, + _Out_opt_ IShellFolder **ppFolder = NULL); + BOOL _ItemHasAnyChild(_In_ HTREEITEM hItem); + HRESULT _AddFavorite(); + + // *** ATL message handlers *** + LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); + LRESULT OnSize(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); + LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + LRESULT OnShellEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled); + + BEGIN_MSG_MAP(CNSCBand) + MESSAGE_HANDLER(WM_CREATE, OnCreate) + MESSAGE_HANDLER(WM_DESTROY, OnDestroy) + MESSAGE_HANDLER(WM_SIZE, OnSize) + MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) + MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) + MESSAGE_HANDLER(WM_COMMAND, OnCommand) + MESSAGE_HANDLER(WM_NOTIFY, OnNotify) + MESSAGE_HANDLER(WM_TIMER, OnTimer) + MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) + MESSAGE_HANDLER(WM_USER_SHELLEVENT, OnShellEvent) + END_MSG_MAP() +}; +#endif // def __cplusplus diff --git a/sdk/include/reactos/shlobj_undoc.h b/sdk/include/reactos/shlobj_undoc.h index c572dc36930..c9796909aa1 100644 --- a/sdk/include/reactos/shlobj_undoc.h +++ b/sdk/include/reactos/shlobj_undoc.h @@ -506,7 +506,7 @@ DECLARE_INTERFACE_(IBandNavigate, IUnknown) STDMETHOD_(ULONG,AddRef)(THIS) PURE; STDMETHOD_(ULONG,Release)(THIS) PURE; /*** IBandNavigate ***/ - STDMETHOD(Select)(THIS_ long paramC) PURE; + STDMETHOD(Select)(THIS_ LPCITEMIDLIST pidl) PURE; }; #undef INTERFACE @@ -528,7 +528,7 @@ DECLARE_INTERFACE_(INamespaceProxy, IUnknown) STDMETHOD_(ULONG,AddRef)(THIS) PURE; STDMETHOD_(ULONG,Release)(THIS) PURE; /*** INamespaceProxy ***/ - STDMETHOD(GetNavigateTarget)(THIS_ _In_ PCIDLIST_ABSOLUTE pidl, _Out_ PIDLIST_ABSOLUTE ppidlTarget, _Out_ ULONG *pulAttrib) PURE; + STDMETHOD(GetNavigateTarget)(THIS_ _In_ PCIDLIST_ABSOLUTE pidl, _Out_ PIDLIST_ABSOLUTE *ppidlTarget, _Out_ ULONG *pulAttrib) PURE; STDMETHOD(Invoke)(THIS_ _In_ PCIDLIST_ABSOLUTE pidl) PURE; STDMETHOD(OnSelectionChanged)(THIS_ _In_ PCIDLIST_ABSOLUTE pidl) PURE; STDMETHOD(RefreshFlags)(THIS_ _Out_ DWORD *pdwStyle, _Out_ DWORD *pdwExStyle, _Out_ DWORD *dwEnum) PURE;