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