[BROWSEUI] -Fix a couple of cases where the tree view can cause a crash.
[reactos.git] / reactos / dll / win32 / browseui / explorerband.cpp
index 0e36405..3ad0758 100644 (file)
 #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()
@@ -142,6 +271,33 @@ CExplorerBand::NodeInfo* CExplorerBand::GetNodeInfo(HTREEITEM hItem)
     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;
@@ -155,6 +311,11 @@ HRESULT CExplorerBand::UpdateBrowser(LPITEMIDLIST pidlGoto)
     if (FAILED_UNEXPECTEDLY(hr))
         return hr;
 
+    if(pidlCurrent)
+    {
+        ILFree(pidlCurrent);
+        pidlCurrent = ILClone(pidlGoto);
+    }
     return hr;
 }
 
@@ -193,21 +354,209 @@ BOOL CExplorerBand::OnTreeItemExpanding(LPNMTREEVIEW pnmtv)
     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)
 {
@@ -229,7 +578,7 @@ HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, IShellFolder *psfParent,
 
     /* 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;
@@ -262,6 +611,15 @@ HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, IShellFolder *psfParent,
 
     htiCreated = TreeView_InsertItem(m_hWnd, &tvInsert);
 
+    if (bSort)
+    {
+        TVSORTCB sortCallback;
+        sortCallback.hParent = hParent;
+        sortCallback.lpfnCompare = CompareTreeItems;
+        sortCallback.lParam = (LPARAM)this;
+        SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
+    }
+
     return htiCreated;
 }
 
@@ -286,6 +644,7 @@ BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo)
     ULONG                               fetched;
     ULONG                               uItemCount;
     CComPtr<IShellFolder>               pFolder;
+    TVSORTCB                            sortCallback;
 
     entry = pNodeInfo->absolutePidl;
     fetched = 1;
@@ -343,6 +702,11 @@ BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo)
         ILFree(pidlSub);
     }
     pNodeInfo->expanded = TRUE;
+    /* Let's do sorting */
+    sortCallback.hParent = hItem;
+    sortCallback.lpfnCompare = CompareTreeItems;
+    sortCallback.lParam = (LPARAM)this;
+    SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
 
     /* Now we can redraw */
     SendMessage(WM_SETREDRAW, TRUE, 0);
@@ -481,6 +845,184 @@ BOOL CExplorerBand::NavigateToCurrentFolder()
     return result;
 }
 
+BOOL CExplorerBand::DeleteItem(LPITEMIDLIST idl)
+{
+    HTREEITEM                           toDelete;
+    TVITEM                              tvItem;
+    HTREEITEM                           parentNode;
+
+    if (!NavigateToPIDL(idl, &toDelete, FALSE, FALSE, FALSE))
+        return FALSE;
+
+    // TODO: check that the treeview item is really deleted
+
+    parentNode = TreeView_GetParent(m_hWnd, toDelete);
+    // Navigate to parent when deleting child item
+    if (!pDesktop->CompareIDs(0, idl, pidlCurrent))
+    {
+        TreeView_SelectItem(m_hWnd, parentNode);
+    }
+
+    // Remove the child item
+    TreeView_DeleteItem(m_hWnd, toDelete);
+    // Probe parent to see if it has children
+    if (!TreeView_GetChild(m_hWnd, parentNode))
+    {
+        // Decrement parent's child count
+        ZeroMemory(&tvItem, sizeof(tvItem));
+        tvItem.mask = TVIF_CHILDREN;
+        tvItem.hItem = parentNode;
+        tvItem.cChildren = 0;
+        TreeView_SetItem(m_hWnd, &tvItem);
+    }
+    return TRUE;
+}
+
+BOOL CExplorerBand::RenameItem(HTREEITEM toRename, LPITEMIDLIST newPidl)
+{
+    WCHAR                               wszDisplayName[MAX_PATH];
+    TVITEM                              itemInfo;
+    LPCITEMIDLIST                       relPidl;
+    NodeInfo                            *treeInfo;
+    TVSORTCB                            sortCallback;
+    HTREEITEM                           child;
+
+    ZeroMemory(&itemInfo, sizeof(itemInfo));
+    itemInfo.mask = TVIF_PARAM;
+    itemInfo.hItem = toRename;
+
+    // Change PIDL associated to the item
+    relPidl = ILFindLastID(newPidl);
+    TreeView_GetItem(m_hWnd, &itemInfo);
+    if (!itemInfo.lParam)
+    {
+        ERR("Unable to fetch lParam\n");
+        return FALSE;
+    }
+    SendMessage(WM_SETREDRAW, FALSE, 0);
+    treeInfo = (NodeInfo*)itemInfo.lParam;
+    ILFree(treeInfo->absolutePidl);
+    ILFree(treeInfo->relativePidl);
+    treeInfo->absolutePidl = ILClone(newPidl);
+    treeInfo->relativePidl = ILClone(relPidl);
+
+    // Change the display name
+    GetDisplayName(newPidl, wszDisplayName, MAX_PATH, SHGDN_INFOLDER);
+    ZeroMemory(&itemInfo, sizeof(itemInfo));
+    itemInfo.hItem = toRename;
+    itemInfo.mask = TVIF_TEXT;
+    itemInfo.pszText = wszDisplayName;
+    TreeView_SetItem(m_hWnd, &itemInfo);
+
+    if((child = TreeView_GetChild(m_hWnd, toRename)) != NULL)
+    {
+        RefreshTreePidl(child, newPidl);
+    }
+
+    // Sorting
+    sortCallback.hParent = TreeView_GetParent(m_hWnd, toRename);
+    sortCallback.lpfnCompare = CompareTreeItems;
+    sortCallback.lParam = (LPARAM)this;
+    SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
+    SendMessage(WM_SETREDRAW, TRUE, 0);
+    return TRUE;
+}
+
+BOOL CExplorerBand::RefreshTreePidl(HTREEITEM tree, LPITEMIDLIST pidlParent)
+{
+    HTREEITEM                           tmp;
+    NodeInfo                            *pInfo;
+
+    // Update our node data
+    pInfo = GetNodeInfo(tree);
+    if (!pInfo)
+    {
+        WARN("No tree info !\n");
+        return FALSE;
+    }
+    ILFree(pInfo->absolutePidl);
+    pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl);
+    if (!pInfo->absolutePidl)
+    {
+        WARN("PIDL allocation failed\n");
+        return FALSE;
+    }
+    // Recursively update children
+    if ((tmp = TreeView_GetChild(m_hWnd, tree)) != NULL)
+    {
+        RefreshTreePidl(tmp, pInfo->absolutePidl);
+    }
+
+    tmp = TreeView_GetNextSibling(m_hWnd, tree);
+    while(tmp != NULL)
+    {
+        pInfo = GetNodeInfo(tmp);
+        if(!pInfo)
+        {
+            WARN("No tree info !\n");
+            continue;
+        }
+        ILFree(pInfo->absolutePidl);
+        pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl);
+        tmp = TreeView_GetNextSibling(m_hWnd, tmp);
+    }
+    return TRUE;
+}
+
+// *** Tree item sorting callback ***
+int CALLBACK CExplorerBand::CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3)
+{
+    /*
+     * We first sort drive letters (Path root), then PIDLs and then regular folder
+     * display name.
+     * This is not how Windows sorts item, but it gives decent results.
+     */
+    NodeInfo                            *info1;
+    NodeInfo                            *info2;
+    CExplorerBand                       *pThis;
+    WCHAR                               wszFolder1[MAX_PATH];
+    WCHAR                               wszFolder2[MAX_PATH];
+
+    info1 = (NodeInfo*)p1;
+    info2 = (NodeInfo*)p2;
+    pThis = (CExplorerBand*)p3;
+
+    GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_FORPARSING);
+    GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_FORPARSING);
+    if (PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2))
+    {
+        return lstrcmpiW(wszFolder1,wszFolder2);
+    }
+    if (PathIsRoot(wszFolder1) && !PathIsRoot(wszFolder2))
+    {
+        return -1;
+    }
+    if (!PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2))
+    {
+        return 1;
+    }
+    // Now, we compare non-root folders, grab display name
+    GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_INFOLDER);
+    GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_INFOLDER);
+
+    if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl))
+    {
+        return -1;
+    }
+    if (!_ILIsSpecialFolder(info1->relativePidl) && _ILIsSpecialFolder(info2->relativePidl))
+    {
+        return 1;
+    }
+    if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl))
+    {
+        HRESULT hr;
+        hr = pThis->pDesktop->CompareIDs(0, info1->absolutePidl, info2->absolutePidl);
+        if (!hr) return 0;
+        return (hr > 0) ? -1 : 1;
+    }
+    return StrCmpLogicalW(wszFolder1, wszFolder2);
+}
+
 // *** IOleWindow methods ***
 HRESULT STDMETHODCALLTYPE CExplorerBand::GetWindow(HWND *lphwnd)
 {
@@ -552,7 +1094,8 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::GetBandInfo(DWORD dwBandID, DWORD dwVie
 
     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)
@@ -678,11 +1221,15 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::HasFocusIO()
 
 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)
@@ -715,6 +1262,7 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::Save(IStream *pStm, BOOL fClearDirty)
 
 HRESULT STDMETHODCALLTYPE CExplorerBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
 {
+    // TODO: calculate max size
     UNIMPLEMENTED;
     return E_NOTIMPL;
 }
@@ -723,6 +1271,7 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
 // *** 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;
@@ -734,6 +1283,80 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM
             case TVN_SELCHANGED:
                 OnSelectionChanged((LPNMTREEVIEW)lParam);
                 break;
+            case TVN_DELETEITEM:
+                OnTreeItemDeleted((LPNMTREEVIEW)lParam);
+                break;
+            case NM_RCLICK:
+                OnContextMenu(WM_CONTEXTMENU, (WPARAM)m_hWnd, GetMessagePos(), bHandled);
+                *theResult = 1;
+                break;
+            case TVN_BEGINDRAG:
+            case TVN_BEGINRDRAG:
+                OnTreeItemDragging((LPNMTREEVIEW)lParam, pNotifyHeader->code == TVN_BEGINRDRAG);
+            case TVN_BEGINLABELEDITW:
+            {
+                // TODO: put this in a function ? (mostly copypasta from CDefView)
+                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;
         }
@@ -820,37 +1443,123 @@ HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(DISPID dispIdMember, REFIID riid
 // *** IDropTarget methods ***
 HRESULT STDMETHODCALLTYPE CExplorerBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
 {
-    UNIMPLEMENTED;
-    return E_NOTIMPL;
+    ERR("Entering drag\n");
+    pCurObject = pObj;
+    oldSelected = TreeView_GetSelection(m_hWnd);
+    return DragOver(glfKeyState, pt, pdwEffect);
 }
 
 HRESULT STDMETHODCALLTYPE CExplorerBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
 {
-    UNIMPLEMENTED;
-    return E_NOTIMPL;
+    TVHITTESTINFO                           info;
+    CComPtr<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;
 }