[BROWSEUI] -Fix a couple of cases where the tree view can cause a crash.
[reactos.git] / reactos / dll / win32 / browseui / explorerband.cpp
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2016 Sylvain Deverre <deverre dot sylv at gmail dot com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "precomp.h"
22 #include <commoncontrols.h>
23 #include <undocshell.h>
24
25 #if 1
26 #undef UNIMPLEMENTED
27
28 #define UNIMPLEMENTED DbgPrint("%s is UNIMPLEMENTED!\n", __FUNCTION__)
29 #endif
30
31 /*
32 * TODO:
33 * - Monitor correctly "external" shell interrupts (seems like we need to register/deregister them for each folder)
34 * - find and fix what cause explorer crashes sometimes (seems to be explorer that does more releases than addref)
35 * - TESTING
36 */
37
38 typedef struct _PIDLDATA
39 {
40 BYTE type;
41 BYTE data[1];
42 } PIDLDATA, *LPPIDLDATA;
43
44 #define PT_GUID 0x1F
45 #define PT_SHELLEXT 0x2E
46 #define PT_YAGUID 0x70
47
48 static BOOL _ILIsSpecialFolder (LPCITEMIDLIST pidl)
49 {
50 LPPIDLDATA lpPData = (LPPIDLDATA)&pidl->mkid.abID;
51
52 return (pidl &&
53 ((lpPData && (PT_GUID == lpPData->type || PT_SHELLEXT== lpPData->type ||
54 PT_YAGUID == lpPData->type)) || (pidl && pidl->mkid.cb == 0x00)));
55 }
56
57 static BOOL _ILIsDesktop (LPCITEMIDLIST pidl)
58 {
59 return (pidl && pidl->mkid.cb == 0x00);
60 }
61
62
63 HRESULT GetDisplayName(LPCITEMIDLIST pidlDirectory,TCHAR *szDisplayName,UINT cchMax,DWORD uFlags)
64 {
65 IShellFolder *pShellFolder = NULL;
66 LPCITEMIDLIST pidlRelative = NULL;
67 STRRET str;
68 HRESULT hr;
69
70 if (pidlDirectory == NULL || szDisplayName == NULL)
71 {
72 return E_FAIL;
73 }
74
75 hr = SHBindToParent(pidlDirectory, IID_PPV_ARG(IShellFolder, &pShellFolder), &pidlRelative);
76
77 if (SUCCEEDED(hr))
78 {
79 hr = pShellFolder->GetDisplayNameOf(pidlRelative,uFlags,&str);
80 if (SUCCEEDED(hr))
81 {
82 hr = StrRetToBuf(&str,pidlDirectory,szDisplayName,cchMax);
83 }
84 pShellFolder->Release();
85 }
86 return hr;
87 }
88
89 extern "C"
90 HRESULT WINAPI CExplorerBand_Constructor(REFIID riid, LPVOID *ppv)
91 {
92 #ifdef __REACTOS__
93 return ShellObjectCreator<CExplorerBand>(riid, ppv);
94 #else
95 return S_OK;
96 #endif
97 }
98
99 /*
100 This is a Windows hack, because shell event messages in Windows gives an
101 ill-formed PIDL stripped from useful data that parses incorrectly with SHGetFileInfo.
102 So we need to re-enumerate subfolders until we find one with the same name.
103 */
104 HRESULT _ReparsePIDL(LPITEMIDLIST buggyPidl, LPITEMIDLIST *cleanPidl)
105 {
106 HRESULT hr;
107 CComPtr<IShellFolder> folder;
108 CComPtr<IPersistFolder2> persist;
109 CComPtr<IEnumIDList> pEnumIDList;
110 LPITEMIDLIST childPidl;
111 LPITEMIDLIST correctChild;
112 LPITEMIDLIST correctParent;
113 ULONG fetched;
114 DWORD EnumFlags;
115
116
117 EnumFlags = SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN;
118 hr = SHBindToParent(buggyPidl, IID_PPV_ARG(IShellFolder, &folder), (LPCITEMIDLIST*)&childPidl);
119 *cleanPidl = NULL;
120 if (!SUCCEEDED(hr))
121 {
122 ERR("Can't bind to parent folder\n");
123 return hr;
124 }
125 hr = folder->QueryInterface(IID_PPV_ARG(IPersistFolder2, &persist));
126 if (!SUCCEEDED(hr))
127 {
128 ERR("PIDL doesn't belong to the shell namespace, aborting\n");
129 return hr;
130 }
131
132 hr = persist->GetCurFolder(&correctParent);
133 if (!SUCCEEDED(hr))
134 {
135 ERR("Unable to get current folder\n");
136 return hr;
137 }
138
139 hr = folder->EnumObjects(NULL,EnumFlags,&pEnumIDList);
140 // avoid broken IShellFolder implementations that return null pointer with success
141 if (!SUCCEEDED(hr) || !pEnumIDList)
142 {
143 ERR("Can't enum the folder !\n");
144 return hr;
145 }
146
147 while(SUCCEEDED(pEnumIDList->Next(1, &correctChild, &fetched)) && correctChild && fetched)
148 {
149 if (!folder->CompareIDs(0, childPidl, correctChild))
150 {
151 *cleanPidl = ILCombine(correctParent, correctChild);
152 ILFree(correctChild);
153 goto Cleanup;
154 }
155 ILFree(correctChild);
156 }
157 Cleanup:
158 ILFree(correctParent);
159 return hr;
160 }
161
162 CExplorerBand::CExplorerBand() :
163 pSite(NULL), fVisible(FALSE), bNavigating(FALSE), dwBandID(0), pidlCurrent(NULL)
164 {
165 }
166
167 CExplorerBand::~CExplorerBand()
168 {
169 if(pidlCurrent)
170 {
171 ILFree(pidlCurrent);
172 }
173 }
174
175 void CExplorerBand::InitializeExplorerBand()
176 {
177 // Init the treeview here
178 HRESULT hr;
179 LPITEMIDLIST pidl;
180 CComPtr<IWebBrowser2> browserService;
181 SHChangeNotifyEntry shcne;
182
183 hr = SHGetDesktopFolder(&pDesktop);
184 if (FAILED_UNEXPECTEDLY(hr))
185 return;
186
187 hr = SHGetFolderLocation(m_hWnd, CSIDL_DESKTOP, NULL, 0, &pidl);
188 if (FAILED_UNEXPECTEDLY(hr))
189 return;
190
191 IImageList * piml;
192 hr = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &piml));
193 if (FAILED_UNEXPECTEDLY(hr))
194 return;
195
196 TreeView_SetImageList(m_hWnd, (HIMAGELIST)piml, TVSIL_NORMAL);
197
198 // Insert the root node
199 hRoot = InsertItem(0, pDesktop, pidl, pidl, FALSE);
200 if (!hRoot)
201 {
202 ERR("Failed to create root item\n");
203 return;
204 }
205
206 NodeInfo* pNodeInfo = GetNodeInfo(hRoot);
207
208 // Insert child nodes
209 InsertSubitems(hRoot, pNodeInfo);
210 TreeView_Expand(m_hWnd, hRoot, TVE_EXPAND);
211
212 // Navigate to current folder position
213 NavigateToCurrentFolder();
214
215 // Register shell notification
216 shcne.pidl = pidl;
217 shcne.fRecursive = TRUE;
218 shellRegID = SHChangeNotifyRegister(
219 m_hWnd,
220 SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_RecursiveInterrupt,
221 SHCNE_DISKEVENTS | SHCNE_RENAMEFOLDER | SHCNE_RMDIR | SHCNE_MKDIR,
222 WM_USER_SHELLEVENT,
223 1,
224 &shcne);
225 if (!shellRegID)
226 {
227 ERR("Something went wrong, error %08x\n", GetLastError());
228 }
229 // Register browser connection endpoint
230 hr = IUnknown_QueryService(pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService));
231 if (FAILED_UNEXPECTEDLY(hr))
232 return;
233
234 hr = AtlAdvise(browserService, dynamic_cast<IDispatch*>(this), DIID_DWebBrowserEvents, &adviseCookie);
235 if (FAILED_UNEXPECTEDLY(hr))
236 return;
237
238 ILFree(pidl);
239 }
240
241 void CExplorerBand::DestroyExplorerBand()
242 {
243 HRESULT hr;
244 CComPtr <IWebBrowser2> browserService;
245
246 TRACE("Cleaning up explorer band ...\n");
247
248 hr = IUnknown_QueryService(pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService));
249 if (FAILED_UNEXPECTEDLY(hr))
250 return;
251
252 hr = AtlUnadvise(browserService, DIID_DWebBrowserEvents, adviseCookie);
253 /* Remove all items of the treeview */
254 RevokeDragDrop(m_hWnd);
255 TreeView_DeleteAllItems(m_hWnd);
256 pDesktop = NULL;
257 hRoot = NULL;
258 TRACE("Cleanup done !\n");
259 }
260
261 CExplorerBand::NodeInfo* CExplorerBand::GetNodeInfo(HTREEITEM hItem)
262 {
263 TVITEM tvItem;
264
265 tvItem.mask = TVIF_PARAM;
266 tvItem.hItem = hItem;
267
268 if (!TreeView_GetItem(m_hWnd, &tvItem))
269 return 0;
270
271 return reinterpret_cast<NodeInfo*>(tvItem.lParam);
272 }
273
274 HRESULT CExplorerBand::ExecuteCommand(CComPtr<IContextMenu>& menu, UINT nCmd)
275 {
276 CComPtr<IOleWindow> pBrowserOleWnd;
277 CMINVOKECOMMANDINFO cmi;
278 HWND browserWnd;
279 HRESULT hr;
280
281 hr = IUnknown_QueryService(pSite, SID_SShellBrowser, IID_PPV_ARG(IOleWindow, &pBrowserOleWnd));
282 if (FAILED_UNEXPECTEDLY(hr))
283 return hr;
284
285 hr = pBrowserOleWnd->GetWindow(&browserWnd);
286 if (FAILED_UNEXPECTEDLY(hr))
287 return hr;
288
289 ZeroMemory(&cmi, sizeof(cmi));
290 cmi.cbSize = sizeof(cmi);
291 cmi.lpVerb = MAKEINTRESOURCEA(nCmd);
292 cmi.hwnd = browserWnd;
293 if (GetKeyState(VK_SHIFT) & 0x8000)
294 cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
295 if (GetKeyState(VK_CONTROL) & 0x8000)
296 cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
297
298 return menu->InvokeCommand(&cmi);
299 }
300
301 HRESULT CExplorerBand::UpdateBrowser(LPITEMIDLIST pidlGoto)
302 {
303 CComPtr<IShellBrowser> pBrowserService;
304 HRESULT hr;
305
306 hr = IUnknown_QueryService(pSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &pBrowserService));
307 if (FAILED_UNEXPECTEDLY(hr))
308 return hr;
309
310 hr = pBrowserService->BrowseObject(pidlGoto, SBSP_SAMEBROWSER | SBSP_ABSOLUTE);
311 if (FAILED_UNEXPECTEDLY(hr))
312 return hr;
313
314 if(pidlCurrent)
315 {
316 ILFree(pidlCurrent);
317 pidlCurrent = ILClone(pidlGoto);
318 }
319 return hr;
320 }
321
322 // *** notifications handling ***
323 BOOL CExplorerBand::OnTreeItemExpanding(LPNMTREEVIEW pnmtv)
324 {
325 NodeInfo *pNodeInfo;
326
327 if (pnmtv->action == TVE_COLLAPSE) {
328 if (pnmtv->itemNew.hItem == hRoot)
329 {
330 // Prenvent root from collapsing
331 pnmtv->itemNew.mask |= TVIF_STATE;
332 pnmtv->itemNew.stateMask |= TVIS_EXPANDED;
333 pnmtv->itemNew.state &= ~TVIS_EXPANDED;
334 pnmtv->action = TVE_EXPAND;
335 return TRUE;
336 }
337 }
338 if (pnmtv->action == TVE_EXPAND) {
339 // Grab our directory PIDL
340 pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem);
341 // We have it, let's try
342 if (pNodeInfo && !pNodeInfo->expanded)
343 if (!InsertSubitems(pnmtv->itemNew.hItem, pNodeInfo)) {
344 // remove subitem "+" since we failed to add subitems
345 TV_ITEM tvItem;
346
347 tvItem.mask = TVIF_CHILDREN;
348 tvItem.hItem = pnmtv->itemNew.hItem;
349 tvItem.cChildren = 0;
350
351 TreeView_SetItem(m_hWnd, &tvItem);
352 }
353 }
354 return FALSE;
355 }
356
357 BOOL CExplorerBand::OnTreeItemDeleted(LPNMTREEVIEW pnmtv)
358 {
359 /* Destroy memory associated to our node */
360 NodeInfo* ptr = GetNodeInfo(pnmtv->itemNew.hItem);
361 if (ptr)
362 {
363 ILFree(ptr->relativePidl);
364 ILFree(ptr->absolutePidl);
365 delete ptr;
366 }
367 return TRUE;
368 }
369
370 void CExplorerBand::OnSelectionChanged(LPNMTREEVIEW pnmtv)
371 {
372 NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem);
373
374 /* Prevents navigation if selection is initiated inside the band */
375 if (bNavigating)
376 return;
377
378 UpdateBrowser(pNodeInfo->absolutePidl);
379
380 SetFocus();
381 // Expand the node
382 //TreeView_Expand(m_hWnd, pnmtv->itemNew.hItem, TVE_EXPAND);
383 }
384
385 void CExplorerBand::OnTreeItemDragging(LPNMTREEVIEW pnmtv, BOOL isRightClick)
386 {
387 CComPtr<IShellFolder> pSrcFolder;
388 CComPtr<IDataObject> pObj;
389 LPCITEMIDLIST pLast;
390 HRESULT hr;
391 DWORD dwEffect;
392 DWORD dwEffect2;
393
394 dwEffect = DROPEFFECT_COPY | DROPEFFECT_MOVE;
395 if (!pnmtv->itemNew.lParam)
396 return;
397 NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem);
398 hr = SHBindToParent(pNodeInfo->absolutePidl, IID_PPV_ARG(IShellFolder, &pSrcFolder), &pLast);
399 if (!SUCCEEDED(hr))
400 return;
401 hr = pSrcFolder->GetUIObjectOf(m_hWnd, 1, &pLast, IID_IDataObject, 0, reinterpret_cast<void**>(&pObj));
402 if (!SUCCEEDED(hr))
403 return;
404 DoDragDrop(pObj, this, dwEffect, &dwEffect2);
405 return;
406 }
407
408
409 // *** ATL event handlers ***
410 LRESULT CExplorerBand::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
411 {
412 HTREEITEM item;
413 NodeInfo *info;
414 HMENU treeMenu;
415 WORD x;
416 WORD y;
417 CComPtr<IShellFolder> pFolder;
418 CComPtr<IContextMenu> contextMenu;
419 HRESULT hr;
420 UINT uCommand;
421 LPITEMIDLIST pidlChild;
422
423 treeMenu = NULL;
424 item = TreeView_GetSelection(m_hWnd);
425 bHandled = TRUE;
426 if (!item)
427 {
428 goto Cleanup;
429 }
430
431 x = LOWORD(lParam);
432 y = HIWORD(lParam);
433 if (x == -1 && y == -1)
434 {
435 // TODO: grab position of tree item and position it correctly
436 }
437
438 info = GetNodeInfo(item);
439 if (!info)
440 {
441 ERR("No node data, something has gone wrong !\n");
442 goto Cleanup;
443 }
444 hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pFolder),
445 (LPCITEMIDLIST*)&pidlChild);
446 if (!SUCCEEDED(hr))
447 {
448 ERR("Can't bind to folder!\n");
449 goto Cleanup;
450 }
451 hr = pFolder->GetUIObjectOf(m_hWnd, 1, (LPCITEMIDLIST*)&pidlChild, IID_IContextMenu,
452 NULL, reinterpret_cast<void**>(&contextMenu));
453 if (!SUCCEEDED(hr))
454 {
455 ERR("Can't get IContextMenu interface\n");
456 goto Cleanup;
457 }
458 treeMenu = CreatePopupMenu();
459 hr = contextMenu->QueryContextMenu(treeMenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST,
460 CMF_EXPLORE);
461 if (!SUCCEEDED(hr))
462 {
463 WARN("Can't get context menu for item\n");
464 DestroyMenu(treeMenu);
465 goto Cleanup;
466 }
467 uCommand = TrackPopupMenu(treeMenu, TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
468 x, y, 0, m_hWnd, NULL);
469
470 ExecuteCommand(contextMenu, uCommand);
471 Cleanup:
472 if (treeMenu)
473 DestroyMenu(treeMenu);
474 bNavigating = TRUE;
475 TreeView_SelectItem(m_hWnd, oldSelected);
476 bNavigating = FALSE;
477 return TRUE;
478 }
479
480 LRESULT CExplorerBand::ContextMenuHack(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
481 {
482 bHandled = FALSE;
483 if (uMsg == WM_RBUTTONDOWN)
484 {
485 TVHITTESTINFO info;
486 info.pt.x = LOWORD(lParam);
487 info.pt.y = HIWORD(lParam);
488 info.flags = TVHT_ONITEM;
489 info.hItem = NULL;
490
491 // Save the current location
492 oldSelected = TreeView_GetSelection(m_hWnd);
493
494 // Move to the item selected by the treeview (don't change right pane)
495 TreeView_HitTest(m_hWnd, &info);
496 bNavigating = TRUE;
497 TreeView_SelectItem(m_hWnd, info.hItem);
498 bNavigating = FALSE;
499 }
500 return FALSE; /* let the wndproc process the message */
501 }
502
503 LRESULT CExplorerBand::OnShellEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
504 {
505 LPITEMIDLIST *dest;
506 LPITEMIDLIST clean;
507 HTREEITEM pItem;
508
509 dest = (LPITEMIDLIST*)wParam;
510 /* TODO: handle shell notifications */
511 switch(lParam & ~SHCNE_INTERRUPT)
512 {
513 case SHCNE_MKDIR:
514 if (!SUCCEEDED(_ReparsePIDL(dest[0], &clean)))
515 {
516 ERR("Can't reparse PIDL to a valid one\n");
517 return FALSE;
518 }
519 NavigateToPIDL(clean, &pItem, FALSE, TRUE, FALSE);
520 ILFree(clean);
521 break;
522 case SHCNE_RMDIR:
523 DeleteItem(dest[0]);
524 break;
525 case SHCNE_RENAMEFOLDER:
526 if (!SUCCEEDED(_ReparsePIDL(dest[1], &clean)))
527 {
528 ERR("Can't reparse PIDL to a valid one\n");
529 return FALSE;
530 }
531 if (NavigateToPIDL(dest[0], &pItem, FALSE, FALSE, FALSE))
532 RenameItem(pItem, clean);
533 ILFree(clean);
534 break;
535 case SHCNE_UPDATEDIR:
536 // We don't take care of this message
537 TRACE("Directory updated\n");
538 break;
539 default:
540 TRACE("Unhandled message\n");
541 }
542 return TRUE;
543 }
544
545 LRESULT CExplorerBand::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
546 {
547 bFocused = TRUE;
548 IUnknown_OnFocusChangeIS(pSite, reinterpret_cast<IUnknown*>(this), TRUE);
549 bHandled = FALSE;
550 return TRUE;
551 }
552
553 LRESULT CExplorerBand::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
554 {
555 IUnknown_OnFocusChangeIS(pSite, reinterpret_cast<IUnknown*>(this), FALSE);
556 bHandled = FALSE;
557 return TRUE;
558 }
559
560 // *** Helper functions ***
561 HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, IShellFolder *psfParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort)
562 {
563 TV_INSERTSTRUCT tvInsert;
564 HTREEITEM htiCreated;
565
566 /* Get the attributes of the node */
567 SFGAOF attrs = SFGAO_STREAM | SFGAO_HASSUBFOLDER;
568 HRESULT hr = psfParent->GetAttributesOf(1, &pEltRelative, &attrs);
569 if (FAILED_UNEXPECTEDLY(hr))
570 return NULL;
571
572 /* Ignore streams */
573 if ((attrs & SFGAO_STREAM))
574 {
575 TRACE("Ignoring stream\n");
576 return NULL;
577 }
578
579 /* Get the name of the node */
580 WCHAR wszDisplayName[MAX_PATH];
581 if (!ILGetDisplayNameEx(psfParent, pElt, wszDisplayName, ILGDN_INFOLDER))
582 {
583 ERR("Failed to get node name\n");
584 return NULL;
585 }
586
587 /* Get the icon of the node */
588 INT iIcon = SHMapPIDLToSystemImageListIndex(psfParent, pEltRelative, NULL);
589
590 NodeInfo* pChildInfo = new NodeInfo;
591 if (!pChildInfo)
592 {
593 ERR("Failed to allocate NodeInfo\n");
594 return FALSE;
595 }
596
597 // Store our node info
598 pChildInfo->absolutePidl = ILClone(pElt);
599 pChildInfo->relativePidl = ILClone(pEltRelative);
600 pChildInfo->expanded = FALSE;
601
602 // Set up our treeview template
603 tvInsert.hParent = hParent;
604 tvInsert.hInsertAfter = TVI_LAST;
605 tvInsert.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
606 tvInsert.item.cchTextMax = MAX_PATH;
607 tvInsert.item.pszText = wszDisplayName;
608 tvInsert.item.iImage = tvInsert.item.iSelectedImage = iIcon;
609 tvInsert.item.cChildren = (attrs & SFGAO_HASSUBFOLDER) ? 1 : 0;
610 tvInsert.item.lParam = (LPARAM)pChildInfo;
611
612 htiCreated = TreeView_InsertItem(m_hWnd, &tvInsert);
613
614 if (bSort)
615 {
616 TVSORTCB sortCallback;
617 sortCallback.hParent = hParent;
618 sortCallback.lpfnCompare = CompareTreeItems;
619 sortCallback.lParam = (LPARAM)this;
620 SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
621 }
622
623 return htiCreated;
624 }
625
626 /* This is the slow version of the above method */
627 HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort)
628 {
629 CComPtr<IShellFolder> psfFolder;
630 HRESULT hr = SHBindToParent(pElt, IID_PPV_ARG(IShellFolder, &psfFolder), NULL);
631 if (FAILED_UNEXPECTEDLY(hr))
632 return NULL;
633
634 return InsertItem(hParent, psfFolder, pElt, pEltRelative, bSort);
635 }
636
637 BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo)
638 {
639 CComPtr<IEnumIDList> pEnumIDList;
640 LPITEMIDLIST pidlSub;
641 LPITEMIDLIST entry;
642 SHCONTF EnumFlags;
643 HRESULT hr;
644 ULONG fetched;
645 ULONG uItemCount;
646 CComPtr<IShellFolder> pFolder;
647 TVSORTCB sortCallback;
648
649 entry = pNodeInfo->absolutePidl;
650 fetched = 1;
651 uItemCount = 0;
652 EnumFlags = SHCONTF_FOLDERS;
653
654 hr = SHGetFolderLocation(m_hWnd, CSIDL_DESKTOP, NULL, 0, &pidlSub);
655 if (!SUCCEEDED(hr))
656 {
657 ERR("Can't get desktop PIDL !\n");
658 return FALSE;
659 }
660
661 if (!pDesktop->CompareIDs(NULL, pidlSub, entry))
662 {
663 // We are the desktop, so use pDesktop as pFolder
664 pFolder = pDesktop;
665 }
666 else
667 {
668 // Get an IShellFolder of our pidl
669 hr = pDesktop->BindToObject(entry, NULL, IID_PPV_ARG(IShellFolder, &pFolder));
670 if (!SUCCEEDED(hr))
671 {
672 ILFree(pidlSub);
673 ERR("Can't bind folder to desktop !\n");
674 return FALSE;
675 }
676 }
677 ILFree(pidlSub);
678
679 // TODO: handle hidden folders according to settings !
680 EnumFlags |= SHCONTF_INCLUDEHIDDEN;
681
682 // Enum through objects
683 hr = pFolder->EnumObjects(NULL,EnumFlags,&pEnumIDList);
684
685 // avoid broken IShellFolder implementations that return null pointer with success
686 if (!SUCCEEDED(hr) || !pEnumIDList)
687 {
688 ERR("Can't enum the folder !\n");
689 return FALSE;
690 }
691
692 /* Don't redraw while we add stuff into the tree */
693 SendMessage(WM_SETREDRAW, FALSE, 0);
694 while(SUCCEEDED(pEnumIDList->Next(1, &pidlSub, &fetched)) && pidlSub && fetched)
695 {
696 LPITEMIDLIST pidlSubComplete;
697 pidlSubComplete = ILCombine(entry, pidlSub);
698
699 if (InsertItem(hItem, pFolder, pidlSubComplete, pidlSub, FALSE))
700 uItemCount++;
701 ILFree(pidlSubComplete);
702 ILFree(pidlSub);
703 }
704 pNodeInfo->expanded = TRUE;
705 /* Let's do sorting */
706 sortCallback.hParent = hItem;
707 sortCallback.lpfnCompare = CompareTreeItems;
708 sortCallback.lParam = (LPARAM)this;
709 SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
710
711 /* Now we can redraw */
712 SendMessage(WM_SETREDRAW, TRUE, 0);
713
714 return (uItemCount > 0) ? TRUE : FALSE;
715 }
716
717 /**
718 * Navigate to a given PIDL in the treeview, and return matching tree item handle
719 * - dest: The absolute PIDL we should navigate in the treeview
720 * - item: Handle of the tree item matching the PIDL
721 * - bExpand: expand collapsed nodes in order to find the right element
722 * - bInsert: insert the element at the right place if we don't find it
723 * - bSelect: select the item after we found it
724 */
725 BOOL CExplorerBand::NavigateToPIDL(LPITEMIDLIST dest, HTREEITEM *item, BOOL bExpand, BOOL bInsert,
726 BOOL bSelect)
727 {
728 HTREEITEM current;
729 HTREEITEM tmp;
730 HTREEITEM parent;
731 BOOL found;
732 NodeInfo *nodeData;
733 LPITEMIDLIST relativeChild;
734 TVITEM tvItem;
735
736 if (!item)
737 return FALSE;
738
739 found = FALSE;
740 current = hRoot;
741 parent = NULL;
742 while(!found)
743 {
744 nodeData = GetNodeInfo(current);
745 if (!nodeData)
746 {
747 ERR("Something has gone wrong, no data associated to node !\n");
748 *item = NULL;
749 return FALSE;
750 }
751 // If we found our node, give it back
752 if (!pDesktop->CompareIDs(0, nodeData->absolutePidl, dest))
753 {
754 if (bSelect)
755 TreeView_SelectItem(m_hWnd, current);
756 *item = current;
757 return TRUE;
758 }
759
760 // Check if we are a parent of the requested item
761 relativeChild = ILFindChild(nodeData->absolutePidl, dest);
762 if (relativeChild != 0)
763 {
764 // Notify treeview we have children
765 tvItem.mask = TVIF_CHILDREN;
766 tvItem.hItem = current;
767 tvItem.cChildren = 1;
768 TreeView_SetItem(m_hWnd, &tvItem);
769
770 // If we can expand and the node isn't expanded yet, do it
771 if (bExpand)
772 {
773 if (!nodeData->expanded)
774 InsertSubitems(current, nodeData);
775 TreeView_Expand(m_hWnd, current, TVE_EXPAND);
776 }
777
778 // Try to get a child
779 tmp = TreeView_GetChild(m_hWnd, current);
780 if (tmp)
781 {
782 // We have a child, let's continue with it
783 parent = current;
784 current = tmp;
785 continue;
786 }
787
788 if (bInsert && nodeData->expanded)
789 {
790 // Happens when we have to create a subchild inside a child
791 current = InsertItem(current, dest, relativeChild, TRUE);
792 }
793 // We end up here, without any children, so we found nothing
794 // Tell the parent node it has children
795 ZeroMemory(&tvItem, sizeof(tvItem));
796 *item = NULL;
797 return FALSE;
798 }
799
800 // Find sibling
801 tmp = TreeView_GetNextSibling(m_hWnd, current);
802 if (tmp)
803 {
804 current = tmp;
805 continue;
806 }
807 if (bInsert)
808 {
809 current = InsertItem(parent, dest, ILFindLastID(dest), TRUE);
810 *item = current;
811 return TRUE;
812 }
813 *item = NULL;
814 return FALSE;
815 }
816 return FALSE;
817 }
818
819 BOOL CExplorerBand::NavigateToCurrentFolder()
820 {
821 LPITEMIDLIST explorerPidl;
822 CComPtr<IBrowserService> pBrowserService;
823 HRESULT hr;
824 HTREEITEM dummy;
825 BOOL result;
826 explorerPidl = NULL;
827
828 hr = IUnknown_QueryService(pSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pBrowserService));
829 if (!SUCCEEDED(hr))
830 {
831 ERR("Can't get IBrowserService !\n");
832 return FALSE;
833 }
834
835 hr = pBrowserService->GetPidl(&explorerPidl);
836 if (!SUCCEEDED(hr) || !explorerPidl)
837 {
838 ERR("Unable to get browser PIDL !\n");
839 return FALSE;
840 }
841 bNavigating = TRUE;
842 /* find PIDL into our explorer */
843 result = NavigateToPIDL(explorerPidl, &dummy, TRUE, FALSE, TRUE);
844 bNavigating = FALSE;
845 return result;
846 }
847
848 BOOL CExplorerBand::DeleteItem(LPITEMIDLIST idl)
849 {
850 HTREEITEM toDelete;
851 TVITEM tvItem;
852 HTREEITEM parentNode;
853
854 if (!NavigateToPIDL(idl, &toDelete, FALSE, FALSE, FALSE))
855 return FALSE;
856
857 // TODO: check that the treeview item is really deleted
858
859 parentNode = TreeView_GetParent(m_hWnd, toDelete);
860 // Navigate to parent when deleting child item
861 if (!pDesktop->CompareIDs(0, idl, pidlCurrent))
862 {
863 TreeView_SelectItem(m_hWnd, parentNode);
864 }
865
866 // Remove the child item
867 TreeView_DeleteItem(m_hWnd, toDelete);
868 // Probe parent to see if it has children
869 if (!TreeView_GetChild(m_hWnd, parentNode))
870 {
871 // Decrement parent's child count
872 ZeroMemory(&tvItem, sizeof(tvItem));
873 tvItem.mask = TVIF_CHILDREN;
874 tvItem.hItem = parentNode;
875 tvItem.cChildren = 0;
876 TreeView_SetItem(m_hWnd, &tvItem);
877 }
878 return TRUE;
879 }
880
881 BOOL CExplorerBand::RenameItem(HTREEITEM toRename, LPITEMIDLIST newPidl)
882 {
883 WCHAR wszDisplayName[MAX_PATH];
884 TVITEM itemInfo;
885 LPCITEMIDLIST relPidl;
886 NodeInfo *treeInfo;
887 TVSORTCB sortCallback;
888 HTREEITEM child;
889
890 ZeroMemory(&itemInfo, sizeof(itemInfo));
891 itemInfo.mask = TVIF_PARAM;
892 itemInfo.hItem = toRename;
893
894 // Change PIDL associated to the item
895 relPidl = ILFindLastID(newPidl);
896 TreeView_GetItem(m_hWnd, &itemInfo);
897 if (!itemInfo.lParam)
898 {
899 ERR("Unable to fetch lParam\n");
900 return FALSE;
901 }
902 SendMessage(WM_SETREDRAW, FALSE, 0);
903 treeInfo = (NodeInfo*)itemInfo.lParam;
904 ILFree(treeInfo->absolutePidl);
905 ILFree(treeInfo->relativePidl);
906 treeInfo->absolutePidl = ILClone(newPidl);
907 treeInfo->relativePidl = ILClone(relPidl);
908
909 // Change the display name
910 GetDisplayName(newPidl, wszDisplayName, MAX_PATH, SHGDN_INFOLDER);
911 ZeroMemory(&itemInfo, sizeof(itemInfo));
912 itemInfo.hItem = toRename;
913 itemInfo.mask = TVIF_TEXT;
914 itemInfo.pszText = wszDisplayName;
915 TreeView_SetItem(m_hWnd, &itemInfo);
916
917 if((child = TreeView_GetChild(m_hWnd, toRename)) != NULL)
918 {
919 RefreshTreePidl(child, newPidl);
920 }
921
922 // Sorting
923 sortCallback.hParent = TreeView_GetParent(m_hWnd, toRename);
924 sortCallback.lpfnCompare = CompareTreeItems;
925 sortCallback.lParam = (LPARAM)this;
926 SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
927 SendMessage(WM_SETREDRAW, TRUE, 0);
928 return TRUE;
929 }
930
931 BOOL CExplorerBand::RefreshTreePidl(HTREEITEM tree, LPITEMIDLIST pidlParent)
932 {
933 HTREEITEM tmp;
934 NodeInfo *pInfo;
935
936 // Update our node data
937 pInfo = GetNodeInfo(tree);
938 if (!pInfo)
939 {
940 WARN("No tree info !\n");
941 return FALSE;
942 }
943 ILFree(pInfo->absolutePidl);
944 pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl);
945 if (!pInfo->absolutePidl)
946 {
947 WARN("PIDL allocation failed\n");
948 return FALSE;
949 }
950 // Recursively update children
951 if ((tmp = TreeView_GetChild(m_hWnd, tree)) != NULL)
952 {
953 RefreshTreePidl(tmp, pInfo->absolutePidl);
954 }
955
956 tmp = TreeView_GetNextSibling(m_hWnd, tree);
957 while(tmp != NULL)
958 {
959 pInfo = GetNodeInfo(tmp);
960 if(!pInfo)
961 {
962 WARN("No tree info !\n");
963 continue;
964 }
965 ILFree(pInfo->absolutePidl);
966 pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl);
967 tmp = TreeView_GetNextSibling(m_hWnd, tmp);
968 }
969 return TRUE;
970 }
971
972 // *** Tree item sorting callback ***
973 int CALLBACK CExplorerBand::CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3)
974 {
975 /*
976 * We first sort drive letters (Path root), then PIDLs and then regular folder
977 * display name.
978 * This is not how Windows sorts item, but it gives decent results.
979 */
980 NodeInfo *info1;
981 NodeInfo *info2;
982 CExplorerBand *pThis;
983 WCHAR wszFolder1[MAX_PATH];
984 WCHAR wszFolder2[MAX_PATH];
985
986 info1 = (NodeInfo*)p1;
987 info2 = (NodeInfo*)p2;
988 pThis = (CExplorerBand*)p3;
989
990 GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_FORPARSING);
991 GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_FORPARSING);
992 if (PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2))
993 {
994 return lstrcmpiW(wszFolder1,wszFolder2);
995 }
996 if (PathIsRoot(wszFolder1) && !PathIsRoot(wszFolder2))
997 {
998 return -1;
999 }
1000 if (!PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2))
1001 {
1002 return 1;
1003 }
1004 // Now, we compare non-root folders, grab display name
1005 GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_INFOLDER);
1006 GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_INFOLDER);
1007
1008 if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl))
1009 {
1010 return -1;
1011 }
1012 if (!_ILIsSpecialFolder(info1->relativePidl) && _ILIsSpecialFolder(info2->relativePidl))
1013 {
1014 return 1;
1015 }
1016 if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl))
1017 {
1018 HRESULT hr;
1019 hr = pThis->pDesktop->CompareIDs(0, info1->absolutePidl, info2->absolutePidl);
1020 if (!hr) return 0;
1021 return (hr > 0) ? -1 : 1;
1022 }
1023 return StrCmpLogicalW(wszFolder1, wszFolder2);
1024 }
1025
1026 // *** IOleWindow methods ***
1027 HRESULT STDMETHODCALLTYPE CExplorerBand::GetWindow(HWND *lphwnd)
1028 {
1029 if (!lphwnd)
1030 return E_INVALIDARG;
1031 *lphwnd = m_hWnd;
1032 return S_OK;
1033 }
1034
1035 HRESULT STDMETHODCALLTYPE CExplorerBand::ContextSensitiveHelp(BOOL fEnterMode)
1036 {
1037 UNIMPLEMENTED;
1038 return E_NOTIMPL;
1039 }
1040
1041
1042 // *** IDockingWindow methods ***
1043 HRESULT STDMETHODCALLTYPE CExplorerBand::CloseDW(DWORD dwReserved)
1044 {
1045 // We do nothing, we don't have anything to save yet
1046 TRACE("CloseDW called\n");
1047 return S_OK;
1048 }
1049
1050 HRESULT STDMETHODCALLTYPE CExplorerBand::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
1051 {
1052 /* Must return E_NOTIMPL according to MSDN */
1053 return E_NOTIMPL;
1054 }
1055
1056 HRESULT STDMETHODCALLTYPE CExplorerBand::ShowDW(BOOL fShow)
1057 {
1058 fVisible = fShow;
1059 ShowWindow(fShow);
1060 return S_OK;
1061 }
1062
1063
1064 // *** IDeskBand methods ***
1065 HRESULT STDMETHODCALLTYPE CExplorerBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
1066 {
1067 if (!pdbi)
1068 {
1069 return E_INVALIDARG;
1070 }
1071 this->dwBandID = dwBandID;
1072
1073 if (pdbi->dwMask & DBIM_MINSIZE)
1074 {
1075 pdbi->ptMinSize.x = 200;
1076 pdbi->ptMinSize.y = 30;
1077 }
1078
1079 if (pdbi->dwMask & DBIM_MAXSIZE)
1080 {
1081 pdbi->ptMaxSize.y = -1;
1082 }
1083
1084 if (pdbi->dwMask & DBIM_INTEGRAL)
1085 {
1086 pdbi->ptIntegral.y = 1;
1087 }
1088
1089 if (pdbi->dwMask & DBIM_ACTUAL)
1090 {
1091 pdbi->ptActual.x = 200;
1092 pdbi->ptActual.y = 30;
1093 }
1094
1095 if (pdbi->dwMask & DBIM_TITLE)
1096 {
1097 if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_FOLDERSLABEL, pdbi->wszTitle, _countof(pdbi->wszTitle)))
1098 return HRESULT_FROM_WIN32(GetLastError());
1099 }
1100
1101 if (pdbi->dwMask & DBIM_MODEFLAGS)
1102 {
1103 pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT;
1104 }
1105
1106 if (pdbi->dwMask & DBIM_BKCOLOR)
1107 {
1108 pdbi->dwMask &= ~DBIM_BKCOLOR;
1109 }
1110 return S_OK;
1111 }
1112
1113
1114 // *** IObjectWithSite methods ***
1115 HRESULT STDMETHODCALLTYPE CExplorerBand::SetSite(IUnknown *pUnkSite)
1116 {
1117 HRESULT hr;
1118 HWND parentWnd;
1119
1120 if (pUnkSite == pSite)
1121 return S_OK;
1122
1123 TRACE("SetSite called \n");
1124 if (!pUnkSite)
1125 {
1126 DestroyExplorerBand();
1127 DestroyWindow();
1128 m_hWnd = NULL;
1129 }
1130
1131 if (pUnkSite != pSite)
1132 {
1133 pSite = NULL;
1134 }
1135
1136 if(!pUnkSite)
1137 return S_OK;
1138
1139 hr = IUnknown_GetWindow(pUnkSite, &parentWnd);
1140 if (!SUCCEEDED(hr))
1141 {
1142 ERR("Could not get parent's window ! Status: %08lx\n", hr);
1143 return E_INVALIDARG;
1144 }
1145
1146 pSite = pUnkSite;
1147
1148 if (m_hWnd)
1149 {
1150 // Change its parent
1151 SetParent(parentWnd);
1152 }
1153 else
1154 {
1155 HWND wnd = CreateWindow(WC_TREEVIEW, NULL,
1156 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
1157 0, 0, 0, 0, parentWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL);
1158
1159 // Subclass the window
1160 SubclassWindow(wnd);
1161
1162 // Initialize our treeview now
1163 InitializeExplorerBand();
1164 RegisterDragDrop(m_hWnd, dynamic_cast<IDropTarget*>(this));
1165 }
1166 return S_OK;
1167 }
1168
1169 HRESULT STDMETHODCALLTYPE CExplorerBand::GetSite(REFIID riid, void **ppvSite)
1170 {
1171 if (!ppvSite)
1172 return E_POINTER;
1173 *ppvSite = pSite;
1174 return S_OK;
1175 }
1176
1177
1178 // *** IOleCommandTarget methods ***
1179 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
1180 {
1181 UNIMPLEMENTED;
1182 return E_NOTIMPL;
1183 }
1184
1185 HRESULT STDMETHODCALLTYPE CExplorerBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
1186 {
1187 UNIMPLEMENTED;
1188 return E_NOTIMPL;
1189 }
1190
1191
1192 // *** IServiceProvider methods ***
1193 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
1194 {
1195 UNIMPLEMENTED;
1196 return E_NOTIMPL;
1197 }
1198
1199
1200 // *** IInputObject methods ***
1201 HRESULT STDMETHODCALLTYPE CExplorerBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
1202 {
1203 if (fActivate)
1204 {
1205 //SetFocus();
1206 SetActiveWindow();
1207 }
1208 // TODO: handle message
1209 if(lpMsg)
1210 {
1211 TranslateMessage(lpMsg);
1212 DispatchMessage(lpMsg);
1213 }
1214 return S_OK;
1215 }
1216
1217 HRESULT STDMETHODCALLTYPE CExplorerBand::HasFocusIO()
1218 {
1219 return bFocused ? S_OK : S_FALSE;
1220 }
1221
1222 HRESULT STDMETHODCALLTYPE CExplorerBand::TranslateAcceleratorIO(LPMSG lpMsg)
1223 {
1224 if (lpMsg->hwnd == m_hWnd)
1225 {
1226 TranslateMessage(lpMsg);
1227 DispatchMessage(lpMsg);
1228 return S_OK;
1229 }
1230
1231 return S_FALSE;
1232 }
1233
1234 // *** IPersist methods ***
1235 HRESULT STDMETHODCALLTYPE CExplorerBand::GetClassID(CLSID *pClassID)
1236 {
1237 if (!pClassID)
1238 return E_POINTER;
1239 memcpy(pClassID, &CLSID_ExplorerBand, sizeof(CLSID));
1240 return S_OK;
1241 }
1242
1243
1244 // *** IPersistStream methods ***
1245 HRESULT STDMETHODCALLTYPE CExplorerBand::IsDirty()
1246 {
1247 UNIMPLEMENTED;
1248 return E_NOTIMPL;
1249 }
1250
1251 HRESULT STDMETHODCALLTYPE CExplorerBand::Load(IStream *pStm)
1252 {
1253 UNIMPLEMENTED;
1254 return E_NOTIMPL;
1255 }
1256
1257 HRESULT STDMETHODCALLTYPE CExplorerBand::Save(IStream *pStm, BOOL fClearDirty)
1258 {
1259 UNIMPLEMENTED;
1260 return E_NOTIMPL;
1261 }
1262
1263 HRESULT STDMETHODCALLTYPE CExplorerBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
1264 {
1265 // TODO: calculate max size
1266 UNIMPLEMENTED;
1267 return E_NOTIMPL;
1268 }
1269
1270
1271 // *** IWinEventHandler methods ***
1272 HRESULT STDMETHODCALLTYPE CExplorerBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
1273 {
1274 BOOL bHandled;
1275 if (uMsg == WM_NOTIFY)
1276 {
1277 NMHDR *pNotifyHeader = (NMHDR*)lParam;
1278 switch (pNotifyHeader->code)
1279 {
1280 case TVN_ITEMEXPANDING:
1281 *theResult = OnTreeItemExpanding((LPNMTREEVIEW)lParam);
1282 break;
1283 case TVN_SELCHANGED:
1284 OnSelectionChanged((LPNMTREEVIEW)lParam);
1285 break;
1286 case TVN_DELETEITEM:
1287 OnTreeItemDeleted((LPNMTREEVIEW)lParam);
1288 break;
1289 case NM_RCLICK:
1290 OnContextMenu(WM_CONTEXTMENU, (WPARAM)m_hWnd, GetMessagePos(), bHandled);
1291 *theResult = 1;
1292 break;
1293 case TVN_BEGINDRAG:
1294 case TVN_BEGINRDRAG:
1295 OnTreeItemDragging((LPNMTREEVIEW)lParam, pNotifyHeader->code == TVN_BEGINRDRAG);
1296 case TVN_BEGINLABELEDITW:
1297 {
1298 // TODO: put this in a function ? (mostly copypasta from CDefView)
1299 DWORD dwAttr = SFGAO_CANRENAME;
1300 LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam;
1301 CComPtr<IShellFolder> pParent;
1302 LPCITEMIDLIST pChild;
1303 HRESULT hr;
1304
1305 *theResult = 1;
1306 NodeInfo *info = GetNodeInfo(dispInfo->item.hItem);
1307 if (!info)
1308 return E_FAIL;
1309 hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pChild);
1310 if (!SUCCEEDED(hr) || !pParent.p)
1311 return E_FAIL;
1312
1313 hr = pParent->GetAttributesOf(1, &pChild, &dwAttr);
1314 if (SUCCEEDED(hr) && (dwAttr & SFGAO_CANRENAME))
1315 *theResult = 0;
1316 return S_OK;
1317 }
1318 case TVN_ENDLABELEDITW:
1319 {
1320 LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam;
1321 NodeInfo *info = GetNodeInfo(dispInfo->item.hItem);
1322 HRESULT hr;
1323
1324 *theResult = 0;
1325 if (dispInfo->item.pszText)
1326 {
1327 LPITEMIDLIST pidlNew;
1328 CComPtr<IShellFolder> pParent;
1329 LPCITEMIDLIST pidlChild;
1330
1331 hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pidlChild);
1332 if (!SUCCEEDED(hr) || !pParent.p)
1333 return E_FAIL;
1334
1335 hr = pParent->SetNameOf(0, pidlChild, dispInfo->item.pszText, SHGDN_INFOLDER, &pidlNew);
1336 if(SUCCEEDED(hr) && pidlNew)
1337 {
1338 CComPtr<IPersistFolder2> pPersist;
1339 LPITEMIDLIST pidlParent, pidlNewAbs;
1340
1341 hr = pParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pPersist));
1342 if(!SUCCEEDED(hr))
1343 return E_FAIL;
1344
1345 hr = pPersist->GetCurFolder(&pidlParent);
1346 if(!SUCCEEDED(hr))
1347 return E_FAIL;
1348 pidlNewAbs = ILCombine(pidlParent, pidlNew);
1349
1350 // Navigate to our new location
1351 UpdateBrowser(pidlNewAbs);
1352
1353 ILFree(pidlNewAbs);
1354 ILFree(pidlNew);
1355 *theResult = 1;
1356 }
1357 return S_OK;
1358 }
1359 }
1360 default:
1361 break;
1362 }
1363 }
1364 return S_OK;
1365 }
1366
1367 HRESULT STDMETHODCALLTYPE CExplorerBand::IsWindowOwner(HWND hWnd)
1368 {
1369 return (hWnd == m_hWnd) ? S_OK : S_FALSE;
1370 }
1371
1372 // *** IBandNavigate methods ***
1373 HRESULT STDMETHODCALLTYPE CExplorerBand::Select(long paramC)
1374 {
1375 UNIMPLEMENTED;
1376 return E_NOTIMPL;
1377 }
1378
1379 // *** INamespaceProxy ***
1380 HRESULT STDMETHODCALLTYPE CExplorerBand::GetNavigateTarget(long paramC, long param10, long param14)
1381 {
1382 UNIMPLEMENTED;
1383 return E_NOTIMPL;
1384 }
1385
1386 HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(long paramC)
1387 {
1388 UNIMPLEMENTED;
1389 return E_NOTIMPL;
1390 }
1391
1392 HRESULT STDMETHODCALLTYPE CExplorerBand::OnSelectionChanged(long paramC)
1393 {
1394 UNIMPLEMENTED;
1395 return E_NOTIMPL;
1396 }
1397
1398 HRESULT STDMETHODCALLTYPE CExplorerBand::RefreshFlags(long paramC, long param10, long param14)
1399 {
1400 UNIMPLEMENTED;
1401 return E_NOTIMPL;
1402 }
1403
1404 HRESULT STDMETHODCALLTYPE CExplorerBand::CacheItem(long paramC)
1405 {
1406 UNIMPLEMENTED;
1407 return E_NOTIMPL;
1408 }
1409
1410 // *** IDispatch methods ***
1411 HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfoCount(UINT *pctinfo)
1412 {
1413 UNIMPLEMENTED;
1414 return E_NOTIMPL;
1415 }
1416
1417 HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
1418 {
1419 UNIMPLEMENTED;
1420 return E_NOTIMPL;
1421 }
1422
1423 HRESULT STDMETHODCALLTYPE CExplorerBand::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
1424 {
1425 UNIMPLEMENTED;
1426 return E_NOTIMPL;
1427 }
1428
1429 HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1430 {
1431 switch (dispIdMember)
1432 {
1433 case DISPID_DOWNLOADCOMPLETE:
1434 case DISPID_NAVIGATECOMPLETE2:
1435 TRACE("DISPID_NAVIGATECOMPLETE2 received\n");
1436 NavigateToCurrentFolder();
1437 return S_OK;
1438 }
1439 TRACE("Unknown dispid requested: %08x\n", dispIdMember);
1440 return E_INVALIDARG;
1441 }
1442
1443 // *** IDropTarget methods ***
1444 HRESULT STDMETHODCALLTYPE CExplorerBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1445 {
1446 ERR("Entering drag\n");
1447 pCurObject = pObj;
1448 oldSelected = TreeView_GetSelection(m_hWnd);
1449 return DragOver(glfKeyState, pt, pdwEffect);
1450 }
1451
1452 HRESULT STDMETHODCALLTYPE CExplorerBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1453 {
1454 TVHITTESTINFO info;
1455 CComPtr<IShellFolder> pShellFldr;
1456 NodeInfo *nodeInfo;
1457 //LPCITEMIDLIST pChild;
1458 HRESULT hr;
1459
1460 info.pt.x = pt.x;
1461 info.pt.y = pt.y;
1462 info.flags = TVHT_ONITEM;
1463 info.hItem = NULL;
1464 ScreenToClient(&info.pt);
1465
1466 // Move to the item selected by the treeview (don't change right pane)
1467 TreeView_HitTest(m_hWnd, &info);
1468
1469 if (info.hItem)
1470 {
1471 bNavigating = TRUE;
1472 TreeView_SelectItem(m_hWnd, info.hItem);
1473 bNavigating = FALSE;
1474 // Delegate to shell folder
1475 if (pDropTarget && info.hItem != childTargetNode)
1476 {
1477 pDropTarget = NULL;
1478 }
1479 if (info.hItem != childTargetNode)
1480 {
1481 nodeInfo = GetNodeInfo(info.hItem);
1482 if (!nodeInfo)
1483 return E_FAIL;
1484 #if 0
1485 hr = SHBindToParent(nodeInfo->absolutePidl, IID_PPV_ARG(IShellFolder, &pShellFldr), &pChild);
1486 if (!SUCCEEDED(hr))
1487 return E_FAIL;
1488 hr = pShellFldr->GetUIObjectOf(m_hWnd, 1, &pChild, IID_IDropTarget, NULL, reinterpret_cast<void**>(&pDropTarget));
1489 if (!SUCCEEDED(hr))
1490 return E_FAIL;
1491 #endif
1492 if(_ILIsDesktop(nodeInfo->absolutePidl))
1493 pShellFldr = pDesktop;
1494 else
1495 {
1496 hr = pDesktop->BindToObject(nodeInfo->absolutePidl, 0, IID_PPV_ARG(IShellFolder, &pShellFldr));
1497 if (!SUCCEEDED(hr))
1498 {
1499 /* Don't allow dnd since we couldn't get our folder object */
1500 ERR("Can't bind to folder object\n");
1501 *pdwEffect = DROPEFFECT_NONE;
1502 return E_FAIL;
1503 }
1504 }
1505 hr = pShellFldr->CreateViewObject(m_hWnd, IID_PPV_ARG(IDropTarget, &pDropTarget));
1506 if (!SUCCEEDED(hr))
1507 {
1508 /* Don't allow dnd since we couldn't get our drop target */
1509 ERR("Can't get drop target for folder object\n");
1510 *pdwEffect = DROPEFFECT_NONE;
1511 return E_FAIL;
1512 }
1513 hr = pDropTarget->DragEnter(pCurObject, glfKeyState, pt, pdwEffect);
1514 childTargetNode = info.hItem;
1515 }
1516 if (pDropTarget)
1517 {
1518 hr = pDropTarget->DragOver(glfKeyState, pt, pdwEffect);
1519 }
1520 }
1521 else
1522 {
1523 childTargetNode = NULL;
1524 pDropTarget = NULL;
1525 *pdwEffect = DROPEFFECT_NONE;
1526 }
1527 return S_OK;
1528 }
1529
1530 HRESULT STDMETHODCALLTYPE CExplorerBand::DragLeave()
1531 {
1532 bNavigating = TRUE;
1533 TreeView_SelectItem(m_hWnd, oldSelected);
1534 bNavigating = FALSE;
1535 childTargetNode = NULL;
1536 if (pCurObject)
1537 {
1538 pCurObject = NULL;
1539 }
1540 return S_OK;
1541 }
1542
1543 HRESULT STDMETHODCALLTYPE CExplorerBand::Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1544 {
1545 if (!pDropTarget)
1546 return E_FAIL;
1547 pDropTarget->Drop(pObj, glfKeyState, pt, pdwEffect);
1548 DragLeave();
1549 return S_OK;
1550 }
1551
1552 // *** IDropSource methods ***
1553 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
1554 {
1555 if (fEscapePressed)
1556 return DRAGDROP_S_CANCEL;
1557 if ((grfKeyState & MK_LBUTTON) || (grfKeyState & MK_RBUTTON))
1558 return S_OK;
1559 return DRAGDROP_S_DROP;
1560 }
1561
1562 HRESULT STDMETHODCALLTYPE CExplorerBand::GiveFeedback(DWORD dwEffect)
1563 {
1564 return DRAGDROP_S_USEDEFAULTCURSORS;
1565 }