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