#define UNIMPLEMENTED DbgPrint("%s is UNIMPLEMENTED!\n", __FUNCTION__)
#endif
+/*
+ * TODO:
+ * - Monitor correctly "external" shell interrupts (seems like we need to register/deregister them for each folder)
+ * - find and fix what cause explorer crashes sometimes (seems to be explorer that does more releases than addref)
+ * - TESTING
+ */
+
+typedef struct _PIDLDATA
+{
+ BYTE type;
+ BYTE data[1];
+} PIDLDATA, *LPPIDLDATA;
+
+#define PT_GUID 0x1F
+#define PT_SHELLEXT 0x2E
+#define PT_YAGUID 0x70
+
+static BOOL _ILIsSpecialFolder (LPCITEMIDLIST pidl)
+{
+ LPPIDLDATA lpPData = (LPPIDLDATA)&pidl->mkid.abID;
+
+ return (pidl &&
+ ((lpPData && (PT_GUID == lpPData->type || PT_SHELLEXT== lpPData->type ||
+ PT_YAGUID == lpPData->type)) || (pidl && pidl->mkid.cb == 0x00)));
+}
+
+static BOOL _ILIsDesktop (LPCITEMIDLIST pidl)
+{
+ return (pidl && pidl->mkid.cb == 0x00);
+}
+
+
+HRESULT GetDisplayName(LPCITEMIDLIST pidlDirectory,TCHAR *szDisplayName,UINT cchMax,DWORD uFlags)
+{
+ IShellFolder *pShellFolder = NULL;
+ LPCITEMIDLIST pidlRelative = NULL;
+ STRRET str;
+ HRESULT hr;
+
+ if (pidlDirectory == NULL || szDisplayName == NULL)
+ {
+ return E_FAIL;
+ }
+
+ hr = SHBindToParent(pidlDirectory, IID_PPV_ARG(IShellFolder, &pShellFolder), &pidlRelative);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pShellFolder->GetDisplayNameOf(pidlRelative,uFlags,&str);
+ if (SUCCEEDED(hr))
+ {
+ hr = StrRetToBuf(&str,pidlDirectory,szDisplayName,cchMax);
+ }
+ pShellFolder->Release();
+ }
+ return hr;
+}
+
extern "C"
HRESULT WINAPI CExplorerBand_Constructor(REFIID riid, LPVOID *ppv)
{
+#ifdef __REACTOS__
return ShellObjectCreator<CExplorerBand>(riid, ppv);
+#else
+ return S_OK;
+#endif
+}
+
+/*
+ This is a Windows hack, because shell event messages in Windows gives an
+ ill-formed PIDL stripped from useful data that parses incorrectly with SHGetFileInfo.
+ So we need to re-enumerate subfolders until we find one with the same name.
+ */
+HRESULT _ReparsePIDL(LPITEMIDLIST buggyPidl, LPITEMIDLIST *cleanPidl)
+{
+ HRESULT hr;
+ CComPtr<IShellFolder> folder;
+ CComPtr<IPersistFolder2> persist;
+ CComPtr<IEnumIDList> pEnumIDList;
+ LPITEMIDLIST childPidl;
+ LPITEMIDLIST correctChild;
+ LPITEMIDLIST correctParent;
+ ULONG fetched;
+ DWORD EnumFlags;
+
+
+ EnumFlags = SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN;
+ hr = SHBindToParent(buggyPidl, IID_PPV_ARG(IShellFolder, &folder), (LPCITEMIDLIST*)&childPidl);
+ *cleanPidl = NULL;
+ if (!SUCCEEDED(hr))
+ {
+ ERR("Can't bind to parent folder\n");
+ return hr;
+ }
+ hr = folder->QueryInterface(IID_PPV_ARG(IPersistFolder2, &persist));
+ if (!SUCCEEDED(hr))
+ {
+ ERR("PIDL doesn't belong to the shell namespace, aborting\n");
+ return hr;
+ }
+
+ hr = persist->GetCurFolder(&correctParent);
+ if (!SUCCEEDED(hr))
+ {
+ ERR("Unable to get current folder\n");
+ return hr;
+ }
+
+ hr = folder->EnumObjects(NULL,EnumFlags,&pEnumIDList);
+ // avoid broken IShellFolder implementations that return null pointer with success
+ if (!SUCCEEDED(hr) || !pEnumIDList)
+ {
+ ERR("Can't enum the folder !\n");
+ return hr;
+ }
+
+ while(SUCCEEDED(pEnumIDList->Next(1, &correctChild, &fetched)) && correctChild && fetched)
+ {
+ if (!folder->CompareIDs(0, childPidl, correctChild))
+ {
+ *cleanPidl = ILCombine(correctParent, correctChild);
+ ILFree(correctChild);
+ goto Cleanup;
+ }
+ ILFree(correctChild);
+ }
+Cleanup:
+ ILFree(correctParent);
+ return hr;
}
CExplorerBand::CExplorerBand() :
- pSite(NULL), fVisible(FALSE), bNavigating(FALSE), dwBandID(0)
+ pSite(NULL), fVisible(FALSE), bNavigating(FALSE), dwBandID(0), pidlCurrent(NULL)
{
}
CExplorerBand::~CExplorerBand()
{
+ if(pidlCurrent)
+ {
+ ILFree(pidlCurrent);
+ }
}
void CExplorerBand::InitializeExplorerBand()
return reinterpret_cast<NodeInfo*>(tvItem.lParam);
}
+HRESULT CExplorerBand::ExecuteCommand(CComPtr<IContextMenu>& menu, UINT nCmd)
+{
+ CComPtr<IOleWindow> pBrowserOleWnd;
+ CMINVOKECOMMANDINFO cmi;
+ HWND browserWnd;
+ HRESULT hr;
+
+ hr = IUnknown_QueryService(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<IShellBrowser> pBrowserService;
if (FAILED_UNEXPECTEDLY(hr))
return hr;
+ if(pidlCurrent)
+ {
+ ILFree(pidlCurrent);
+ pidlCurrent = ILClone(pidlGoto);
+ }
return hr;
}
return FALSE;
}
+BOOL CExplorerBand::OnTreeItemDeleted(LPNMTREEVIEW pnmtv)
+{
+ /* Destroy memory associated to our node */
+ NodeInfo* ptr = GetNodeInfo(pnmtv->itemNew.hItem);
+ if (ptr)
+ {
+ ILFree(ptr->relativePidl);
+ ILFree(ptr->absolutePidl);
+ delete ptr;
+ }
+ return TRUE;
+}
+
void CExplorerBand::OnSelectionChanged(LPNMTREEVIEW pnmtv)
{
NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem);
- UpdateBrowser(pNodeInfo->absolutePidl);
-
/* Prevents navigation if selection is initiated inside the band */
if (bNavigating)
return;
+ UpdateBrowser(pNodeInfo->absolutePidl);
+
SetFocus();
// Expand the node
//TreeView_Expand(m_hWnd, pnmtv->itemNew.hItem, TVE_EXPAND);
}
+void CExplorerBand::OnTreeItemDragging(LPNMTREEVIEW pnmtv, BOOL isRightClick)
+{
+ CComPtr<IShellFolder> pSrcFolder;
+ CComPtr<IDataObject> pObj;
+ LPCITEMIDLIST pLast;
+ HRESULT hr;
+ DWORD dwEffect;
+ DWORD dwEffect2;
+
+ dwEffect = DROPEFFECT_COPY | DROPEFFECT_MOVE;
+ if (!pnmtv->itemNew.lParam)
+ return;
+ NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem);
+ hr = SHBindToParent(pNodeInfo->absolutePidl, IID_PPV_ARG(IShellFolder, &pSrcFolder), &pLast);
+ if (!SUCCEEDED(hr))
+ return;
+ hr = pSrcFolder->GetUIObjectOf(m_hWnd, 1, &pLast, IID_IDataObject, 0, reinterpret_cast<void**>(&pObj));
+ if (!SUCCEEDED(hr))
+ return;
+ DoDragDrop(pObj, this, dwEffect, &dwEffect2);
+ return;
+}
+
+
+// *** ATL event handlers ***
+LRESULT CExplorerBand::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+ HTREEITEM item;
+ NodeInfo *info;
+ HMENU treeMenu;
+ WORD x;
+ WORD y;
+ CComPtr<IShellFolder> pFolder;
+ CComPtr<IContextMenu> contextMenu;
+ HRESULT hr;
+ UINT uCommand;
+ LPITEMIDLIST pidlChild;
+
+ treeMenu = NULL;
+ item = TreeView_GetSelection(m_hWnd);
+ bHandled = TRUE;
+ if (!item)
+ {
+ goto Cleanup;
+ }
+
+ x = LOWORD(lParam);
+ y = HIWORD(lParam);
+ if (x == -1 && y == -1)
+ {
+ // TODO: grab position of tree item and position it correctly
+ }
+
+ 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<void**>(&contextMenu));
+ if (!SUCCEEDED(hr))
+ {
+ ERR("Can't get IContextMenu interface\n");
+ goto Cleanup;
+ }
+ treeMenu = CreatePopupMenu();
+ hr = contextMenu->QueryContextMenu(treeMenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST,
+ CMF_EXPLORE);
+ if (!SUCCEEDED(hr))
+ {
+ WARN("Can't get context menu for item\n");
+ DestroyMenu(treeMenu);
+ goto Cleanup;
+ }
+ uCommand = TrackPopupMenu(treeMenu, TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
+ x, y, 0, m_hWnd, NULL);
+
+ ExecuteCommand(contextMenu, uCommand);
+Cleanup:
+ if (treeMenu)
+ DestroyMenu(treeMenu);
+ bNavigating = TRUE;
+ TreeView_SelectItem(m_hWnd, oldSelected);
+ bNavigating = FALSE;
+ return TRUE;
+}
+
+LRESULT CExplorerBand::ContextMenuHack(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+ bHandled = FALSE;
+ if (uMsg == WM_RBUTTONDOWN)
+ {
+ TVHITTESTINFO info;
+ info.pt.x = LOWORD(lParam);
+ info.pt.y = HIWORD(lParam);
+ info.flags = TVHT_ONITEM;
+ info.hItem = NULL;
+
+ // Save the current location
+ oldSelected = TreeView_GetSelection(m_hWnd);
+
+ // Move to the item selected by the treeview (don't change right pane)
+ TreeView_HitTest(m_hWnd, &info);
+ bNavigating = TRUE;
+ TreeView_SelectItem(m_hWnd, info.hItem);
+ bNavigating = FALSE;
+ }
+ return FALSE; /* let the wndproc process the message */
+}
+
+LRESULT CExplorerBand::OnShellEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+ LPITEMIDLIST *dest;
+ LPITEMIDLIST clean;
+ HTREEITEM pItem;
+
+ dest = (LPITEMIDLIST*)wParam;
+ /* TODO: handle shell notifications */
+ switch(lParam & ~SHCNE_INTERRUPT)
+ {
+ case SHCNE_MKDIR:
+ if (!SUCCEEDED(_ReparsePIDL(dest[0], &clean)))
+ {
+ ERR("Can't reparse PIDL to a valid one\n");
+ return FALSE;
+ }
+ NavigateToPIDL(clean, &pItem, FALSE, TRUE, FALSE);
+ ILFree(clean);
+ break;
+ case SHCNE_RMDIR:
+ DeleteItem(dest[0]);
+ break;
+ case SHCNE_RENAMEFOLDER:
+ if (!SUCCEEDED(_ReparsePIDL(dest[1], &clean)))
+ {
+ ERR("Can't reparse PIDL to a valid one\n");
+ return FALSE;
+ }
+ if (NavigateToPIDL(dest[0], &pItem, FALSE, FALSE, FALSE))
+ RenameItem(pItem, clean);
+ ILFree(clean);
+ break;
+ case SHCNE_UPDATEDIR:
+ // We don't take care of this message
+ TRACE("Directory updated\n");
+ break;
+ default:
+ TRACE("Unhandled message\n");
+ }
+ return TRUE;
+}
+
+LRESULT CExplorerBand::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+ bFocused = TRUE;
+ IUnknown_OnFocusChangeIS(pSite, reinterpret_cast<IUnknown*>(this), TRUE);
+ bHandled = FALSE;
+ return TRUE;
+}
+
+LRESULT CExplorerBand::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
+{
+ IUnknown_OnFocusChangeIS(pSite, reinterpret_cast<IUnknown*>(this), FALSE);
+ bHandled = FALSE;
+ return TRUE;
+}
+
// *** Helper functions ***
HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, IShellFolder *psfParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort)
{
/* Get the name of the node */
WCHAR wszDisplayName[MAX_PATH];
- if (!ILGetDisplayNameEx(psfParent, pEltRelative, wszDisplayName, ILGDN_INFOLDER))
+ if (!ILGetDisplayNameEx(psfParent, pElt, wszDisplayName, ILGDN_INFOLDER))
{
ERR("Failed to get node name\n");
return NULL;
htiCreated = TreeView_InsertItem(m_hWnd, &tvInsert);
+ if (bSort)
+ {
+ TVSORTCB sortCallback;
+ sortCallback.hParent = hParent;
+ sortCallback.lpfnCompare = CompareTreeItems;
+ sortCallback.lParam = (LPARAM)this;
+ SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
+ }
+
return htiCreated;
}
ULONG fetched;
ULONG uItemCount;
CComPtr<IShellFolder> pFolder;
+ TVSORTCB sortCallback;
entry = pNodeInfo->absolutePidl;
fetched = 1;
ILFree(pidlSub);
}
pNodeInfo->expanded = TRUE;
+ /* Let's do sorting */
+ sortCallback.hParent = hItem;
+ sortCallback.lpfnCompare = CompareTreeItems;
+ sortCallback.lParam = (LPARAM)this;
+ SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
/* Now we can redraw */
SendMessage(WM_SETREDRAW, TRUE, 0);
return result;
}
+BOOL CExplorerBand::DeleteItem(LPITEMIDLIST idl)
+{
+ HTREEITEM toDelete;
+ TVITEM tvItem;
+ HTREEITEM parentNode;
+
+ if (!NavigateToPIDL(idl, &toDelete, FALSE, FALSE, FALSE))
+ return FALSE;
+
+ // TODO: check that the treeview item is really deleted
+
+ parentNode = TreeView_GetParent(m_hWnd, toDelete);
+ // Navigate to parent when deleting child item
+ if (!pDesktop->CompareIDs(0, idl, pidlCurrent))
+ {
+ TreeView_SelectItem(m_hWnd, parentNode);
+ }
+
+ // Remove the child item
+ TreeView_DeleteItem(m_hWnd, toDelete);
+ // Probe parent to see if it has children
+ if (!TreeView_GetChild(m_hWnd, parentNode))
+ {
+ // Decrement parent's child count
+ ZeroMemory(&tvItem, sizeof(tvItem));
+ tvItem.mask = TVIF_CHILDREN;
+ tvItem.hItem = parentNode;
+ tvItem.cChildren = 0;
+ TreeView_SetItem(m_hWnd, &tvItem);
+ }
+ return TRUE;
+}
+
+BOOL CExplorerBand::RenameItem(HTREEITEM toRename, LPITEMIDLIST newPidl)
+{
+ WCHAR wszDisplayName[MAX_PATH];
+ TVITEM itemInfo;
+ LPCITEMIDLIST relPidl;
+ NodeInfo *treeInfo;
+ TVSORTCB sortCallback;
+ HTREEITEM child;
+
+ ZeroMemory(&itemInfo, sizeof(itemInfo));
+ itemInfo.mask = TVIF_PARAM;
+ itemInfo.hItem = toRename;
+
+ // Change PIDL associated to the item
+ relPidl = ILFindLastID(newPidl);
+ TreeView_GetItem(m_hWnd, &itemInfo);
+ if (!itemInfo.lParam)
+ {
+ ERR("Unable to fetch lParam\n");
+ return FALSE;
+ }
+ SendMessage(WM_SETREDRAW, FALSE, 0);
+ treeInfo = (NodeInfo*)itemInfo.lParam;
+ ILFree(treeInfo->absolutePidl);
+ ILFree(treeInfo->relativePidl);
+ treeInfo->absolutePidl = ILClone(newPidl);
+ treeInfo->relativePidl = ILClone(relPidl);
+
+ // Change the display name
+ GetDisplayName(newPidl, wszDisplayName, MAX_PATH, SHGDN_INFOLDER);
+ ZeroMemory(&itemInfo, sizeof(itemInfo));
+ itemInfo.hItem = toRename;
+ itemInfo.mask = TVIF_TEXT;
+ itemInfo.pszText = wszDisplayName;
+ TreeView_SetItem(m_hWnd, &itemInfo);
+
+ if((child = TreeView_GetChild(m_hWnd, toRename)) != NULL)
+ {
+ RefreshTreePidl(child, newPidl);
+ }
+
+ // Sorting
+ sortCallback.hParent = TreeView_GetParent(m_hWnd, toRename);
+ sortCallback.lpfnCompare = CompareTreeItems;
+ sortCallback.lParam = (LPARAM)this;
+ SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
+ SendMessage(WM_SETREDRAW, TRUE, 0);
+ return TRUE;
+}
+
+BOOL CExplorerBand::RefreshTreePidl(HTREEITEM tree, LPITEMIDLIST pidlParent)
+{
+ HTREEITEM tmp;
+ NodeInfo *pInfo;
+
+ // Update our node data
+ pInfo = GetNodeInfo(tree);
+ if (!pInfo)
+ {
+ WARN("No tree info !\n");
+ return FALSE;
+ }
+ ILFree(pInfo->absolutePidl);
+ pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl);
+ if (!pInfo->absolutePidl)
+ {
+ WARN("PIDL allocation failed\n");
+ return FALSE;
+ }
+ // Recursively update children
+ if ((tmp = TreeView_GetChild(m_hWnd, tree)) != NULL)
+ {
+ RefreshTreePidl(tmp, pInfo->absolutePidl);
+ }
+
+ tmp = TreeView_GetNextSibling(m_hWnd, tree);
+ while(tmp != NULL)
+ {
+ pInfo = GetNodeInfo(tmp);
+ if(!pInfo)
+ {
+ WARN("No tree info !\n");
+ continue;
+ }
+ ILFree(pInfo->absolutePidl);
+ pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl);
+ tmp = TreeView_GetNextSibling(m_hWnd, tmp);
+ }
+ return TRUE;
+}
+
+// *** Tree item sorting callback ***
+int CALLBACK CExplorerBand::CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3)
+{
+ /*
+ * We first sort drive letters (Path root), then PIDLs and then regular folder
+ * display name.
+ * This is not how Windows sorts item, but it gives decent results.
+ */
+ NodeInfo *info1;
+ NodeInfo *info2;
+ CExplorerBand *pThis;
+ WCHAR wszFolder1[MAX_PATH];
+ WCHAR wszFolder2[MAX_PATH];
+
+ info1 = (NodeInfo*)p1;
+ info2 = (NodeInfo*)p2;
+ pThis = (CExplorerBand*)p3;
+
+ GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_FORPARSING);
+ GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_FORPARSING);
+ if (PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2))
+ {
+ return lstrcmpiW(wszFolder1,wszFolder2);
+ }
+ if (PathIsRoot(wszFolder1) && !PathIsRoot(wszFolder2))
+ {
+ return -1;
+ }
+ if (!PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2))
+ {
+ return 1;
+ }
+ // Now, we compare non-root folders, grab display name
+ GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_INFOLDER);
+ GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_INFOLDER);
+
+ if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl))
+ {
+ return -1;
+ }
+ if (!_ILIsSpecialFolder(info1->relativePidl) && _ILIsSpecialFolder(info2->relativePidl))
+ {
+ return 1;
+ }
+ if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl))
+ {
+ HRESULT hr;
+ hr = pThis->pDesktop->CompareIDs(0, info1->absolutePidl, info2->absolutePidl);
+ if (!hr) return 0;
+ return (hr > 0) ? -1 : 1;
+ }
+ return StrCmpLogicalW(wszFolder1, wszFolder2);
+}
+
// *** IOleWindow methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand::GetWindow(HWND *lphwnd)
{
if (pdbi->dwMask & DBIM_TITLE)
{
- lstrcpyW(pdbi->wszTitle, L"Explorer");
+ if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_FOLDERSLABEL, pdbi->wszTitle, _countof(pdbi->wszTitle)))
+ return HRESULT_FROM_WIN32(GetLastError());
}
if (pdbi->dwMask & DBIM_MODEFLAGS)
HRESULT STDMETHODCALLTYPE CExplorerBand::TranslateAcceleratorIO(LPMSG lpMsg)
{
- TranslateMessage(lpMsg);
- DispatchMessage(lpMsg);
- return S_OK;
-}
+ if (lpMsg->hwnd == m_hWnd)
+ {
+ TranslateMessage(lpMsg);
+ DispatchMessage(lpMsg);
+ return S_OK;
+ }
+ return S_FALSE;
+}
// *** IPersist methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand::GetClassID(CLSID *pClassID)
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;
if (uMsg == WM_NOTIFY)
{
NMHDR *pNotifyHeader = (NMHDR*)lParam;
case TVN_SELCHANGED:
OnSelectionChanged((LPNMTREEVIEW)lParam);
break;
+ case TVN_DELETEITEM:
+ OnTreeItemDeleted((LPNMTREEVIEW)lParam);
+ break;
+ case NM_RCLICK:
+ OnContextMenu(WM_CONTEXTMENU, (WPARAM)m_hWnd, GetMessagePos(), bHandled);
+ *theResult = 1;
+ break;
+ case TVN_BEGINDRAG:
+ case TVN_BEGINRDRAG:
+ OnTreeItemDragging((LPNMTREEVIEW)lParam, pNotifyHeader->code == TVN_BEGINRDRAG);
+ case TVN_BEGINLABELEDITW:
+ {
+ // TODO: put this in a function ? (mostly copypasta from CDefView)
+ DWORD dwAttr = SFGAO_CANRENAME;
+ LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam;
+ CComPtr<IShellFolder> pParent;
+ LPCITEMIDLIST pChild;
+ HRESULT hr;
+
+ *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))
+ *theResult = 0;
+ return S_OK;
+ }
+ case TVN_ENDLABELEDITW:
+ {
+ LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam;
+ NodeInfo *info = GetNodeInfo(dispInfo->item.hItem);
+ HRESULT hr;
+
+ *theResult = 0;
+ if (dispInfo->item.pszText)
+ {
+ LPITEMIDLIST pidlNew;
+ CComPtr<IShellFolder> pParent;
+ LPCITEMIDLIST pidlChild;
+
+ hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pidlChild);
+ if (!SUCCEEDED(hr) || !pParent.p)
+ return E_FAIL;
+
+ hr = pParent->SetNameOf(0, pidlChild, dispInfo->item.pszText, SHGDN_INFOLDER, &pidlNew);
+ if(SUCCEEDED(hr) && pidlNew)
+ {
+ CComPtr<IPersistFolder2> pPersist;
+ LPITEMIDLIST pidlParent, pidlNewAbs;
+
+ hr = pParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pPersist));
+ if(!SUCCEEDED(hr))
+ return E_FAIL;
+
+ hr = pPersist->GetCurFolder(&pidlParent);
+ if(!SUCCEEDED(hr))
+ return E_FAIL;
+ pidlNewAbs = ILCombine(pidlParent, pidlNew);
+
+ // Navigate to our new location
+ UpdateBrowser(pidlNewAbs);
+
+ ILFree(pidlNewAbs);
+ ILFree(pidlNew);
+ *theResult = 1;
+ }
+ return S_OK;
+ }
+ }
default:
break;
}
// *** IDropTarget methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
{
- UNIMPLEMENTED;
- return E_NOTIMPL;
+ ERR("Entering drag\n");
+ pCurObject = pObj;
+ oldSelected = TreeView_GetSelection(m_hWnd);
+ return DragOver(glfKeyState, pt, pdwEffect);
}
HRESULT STDMETHODCALLTYPE CExplorerBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
{
- UNIMPLEMENTED;
- return E_NOTIMPL;
+ TVHITTESTINFO info;
+ CComPtr<IShellFolder> pShellFldr;
+ NodeInfo *nodeInfo;
+ //LPCITEMIDLIST pChild;
+ HRESULT hr;
+
+ info.pt.x = pt.x;
+ info.pt.y = pt.y;
+ info.flags = TVHT_ONITEM;
+ info.hItem = NULL;
+ ScreenToClient(&info.pt);
+
+ // Move to the item selected by the treeview (don't change right pane)
+ TreeView_HitTest(m_hWnd, &info);
+
+ if (info.hItem)
+ {
+ bNavigating = TRUE;
+ TreeView_SelectItem(m_hWnd, info.hItem);
+ bNavigating = FALSE;
+ // Delegate to shell folder
+ if (pDropTarget && info.hItem != childTargetNode)
+ {
+ pDropTarget = NULL;
+ }
+ if (info.hItem != childTargetNode)
+ {
+ nodeInfo = GetNodeInfo(info.hItem);
+ if (!nodeInfo)
+ return E_FAIL;
+#if 0
+ hr = SHBindToParent(nodeInfo->absolutePidl, IID_PPV_ARG(IShellFolder, &pShellFldr), &pChild);
+ if (!SUCCEEDED(hr))
+ return E_FAIL;
+ hr = pShellFldr->GetUIObjectOf(m_hWnd, 1, &pChild, IID_IDropTarget, NULL, reinterpret_cast<void**>(&pDropTarget));
+ if (!SUCCEEDED(hr))
+ return E_FAIL;
+#endif
+ if(_ILIsDesktop(nodeInfo->absolutePidl))
+ pShellFldr = pDesktop;
+ else
+ {
+ hr = pDesktop->BindToObject(nodeInfo->absolutePidl, 0, IID_PPV_ARG(IShellFolder, &pShellFldr));
+ if (!SUCCEEDED(hr))
+ {
+ /* Don't allow dnd since we couldn't get our folder object */
+ ERR("Can't bind to folder object\n");
+ *pdwEffect = DROPEFFECT_NONE;
+ return E_FAIL;
+ }
+ }
+ hr = pShellFldr->CreateViewObject(m_hWnd, IID_PPV_ARG(IDropTarget, &pDropTarget));
+ if (!SUCCEEDED(hr))
+ {
+ /* Don't allow dnd since we couldn't get our drop target */
+ ERR("Can't get drop target for folder object\n");
+ *pdwEffect = DROPEFFECT_NONE;
+ return E_FAIL;
+ }
+ hr = pDropTarget->DragEnter(pCurObject, glfKeyState, pt, pdwEffect);
+ childTargetNode = info.hItem;
+ }
+ if (pDropTarget)
+ {
+ hr = pDropTarget->DragOver(glfKeyState, pt, pdwEffect);
+ }
+ }
+ else
+ {
+ childTargetNode = NULL;
+ pDropTarget = NULL;
+ *pdwEffect = DROPEFFECT_NONE;
+ }
+ return S_OK;
}
HRESULT STDMETHODCALLTYPE CExplorerBand::DragLeave()
{
- UNIMPLEMENTED;
- return E_NOTIMPL;
+ bNavigating = TRUE;
+ TreeView_SelectItem(m_hWnd, oldSelected);
+ bNavigating = FALSE;
+ childTargetNode = NULL;
+ if (pCurObject)
+ {
+ pCurObject = NULL;
+ }
+ return S_OK;
}
HRESULT STDMETHODCALLTYPE CExplorerBand::Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
{
- UNIMPLEMENTED;
- return E_NOTIMPL;
+ if (!pDropTarget)
+ return E_FAIL;
+ pDropTarget->Drop(pObj, glfKeyState, pt, pdwEffect);
+ DragLeave();
+ return S_OK;
}
// *** IDropSource methods ***
HRESULT STDMETHODCALLTYPE CExplorerBand::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
- UNIMPLEMENTED;
- return E_NOTIMPL;
+ if (fEscapePressed)
+ return DRAGDROP_S_CANCEL;
+ if ((grfKeyState & MK_LBUTTON) || (grfKeyState & MK_RBUTTON))
+ return S_OK;
+ return DRAGDROP_S_DROP;
}
HRESULT STDMETHODCALLTYPE CExplorerBand::GiveFeedback(DWORD dwEffect)
{
- UNIMPLEMENTED;
- return E_NOTIMPL;
+ return DRAGDROP_S_USEDEFAULTCURSORS;
}