Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[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 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 STRRET strret;
578 hr = psfParent->GetDisplayNameOf(pEltRelative, SHGDN_INFOLDER, &strret);
579 if (FAILED_UNEXPECTEDLY(hr))
580 return NULL;
581
582 hr = StrRetToBufW(&strret, pEltRelative, wszDisplayName, MAX_PATH);
583 if (FAILED_UNEXPECTEDLY(hr))
584 return NULL;
585
586 /* Get the icon of the node */
587 INT iIcon = SHMapPIDLToSystemImageListIndex(psfParent, pEltRelative, NULL);
588
589 NodeInfo* pChildInfo = new NodeInfo;
590 if (!pChildInfo)
591 {
592 ERR("Failed to allocate NodeInfo\n");
593 return FALSE;
594 }
595
596 // Store our node info
597 pChildInfo->absolutePidl = ILClone(pElt);
598 pChildInfo->relativePidl = ILClone(pEltRelative);
599 pChildInfo->expanded = FALSE;
600
601 // Set up our treeview template
602 tvInsert.hParent = hParent;
603 tvInsert.hInsertAfter = TVI_LAST;
604 tvInsert.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
605 tvInsert.item.cchTextMax = MAX_PATH;
606 tvInsert.item.pszText = wszDisplayName;
607 tvInsert.item.iImage = tvInsert.item.iSelectedImage = iIcon;
608 tvInsert.item.cChildren = (attrs & SFGAO_HASSUBFOLDER) ? 1 : 0;
609 tvInsert.item.lParam = (LPARAM)pChildInfo;
610
611 htiCreated = TreeView_InsertItem(m_hWnd, &tvInsert);
612
613 if (bSort)
614 {
615 TVSORTCB sortCallback;
616 sortCallback.hParent = hParent;
617 sortCallback.lpfnCompare = CompareTreeItems;
618 sortCallback.lParam = (LPARAM)this;
619 SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
620 }
621
622 return htiCreated;
623 }
624
625 /* This is the slow version of the above method */
626 HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort)
627 {
628 CComPtr<IShellFolder> psfFolder;
629 HRESULT hr = SHBindToParent(pElt, IID_PPV_ARG(IShellFolder, &psfFolder), NULL);
630 if (FAILED_UNEXPECTEDLY(hr))
631 return NULL;
632
633 return InsertItem(hParent, psfFolder, pElt, pEltRelative, bSort);
634 }
635
636 BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo)
637 {
638 CComPtr<IEnumIDList> pEnumIDList;
639 LPITEMIDLIST pidlSub;
640 LPITEMIDLIST entry;
641 SHCONTF EnumFlags;
642 HRESULT hr;
643 ULONG fetched;
644 ULONG uItemCount;
645 CComPtr<IShellFolder> pFolder;
646 TVSORTCB sortCallback;
647
648 entry = pNodeInfo->absolutePidl;
649 fetched = 1;
650 uItemCount = 0;
651 EnumFlags = SHCONTF_FOLDERS;
652
653 hr = SHGetFolderLocation(m_hWnd, CSIDL_DESKTOP, NULL, 0, &pidlSub);
654 if (!SUCCEEDED(hr))
655 {
656 ERR("Can't get desktop PIDL !\n");
657 return FALSE;
658 }
659
660 if (!pDesktop->CompareIDs(NULL, pidlSub, entry))
661 {
662 // We are the desktop, so use pDesktop as pFolder
663 pFolder = pDesktop;
664 }
665 else
666 {
667 // Get an IShellFolder of our pidl
668 hr = pDesktop->BindToObject(entry, NULL, IID_PPV_ARG(IShellFolder, &pFolder));
669 if (!SUCCEEDED(hr))
670 {
671 ILFree(pidlSub);
672 ERR("Can't bind folder to desktop !\n");
673 return FALSE;
674 }
675 }
676 ILFree(pidlSub);
677
678 // TODO: handle hidden folders according to settings !
679 EnumFlags |= SHCONTF_INCLUDEHIDDEN;
680
681 // Enum through objects
682 hr = pFolder->EnumObjects(NULL,EnumFlags,&pEnumIDList);
683
684 // avoid broken IShellFolder implementations that return null pointer with success
685 if (!SUCCEEDED(hr) || !pEnumIDList)
686 {
687 ERR("Can't enum the folder !\n");
688 return FALSE;
689 }
690
691 /* Don't redraw while we add stuff into the tree */
692 SendMessage(WM_SETREDRAW, FALSE, 0);
693 while(SUCCEEDED(pEnumIDList->Next(1, &pidlSub, &fetched)) && pidlSub && fetched)
694 {
695 LPITEMIDLIST pidlSubComplete;
696 pidlSubComplete = ILCombine(entry, pidlSub);
697
698 if (InsertItem(hItem, pFolder, pidlSubComplete, pidlSub, FALSE))
699 uItemCount++;
700 ILFree(pidlSubComplete);
701 ILFree(pidlSub);
702 }
703 pNodeInfo->expanded = TRUE;
704 /* Let's do sorting */
705 sortCallback.hParent = hItem;
706 sortCallback.lpfnCompare = CompareTreeItems;
707 sortCallback.lParam = (LPARAM)this;
708 SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
709
710 /* Now we can redraw */
711 SendMessage(WM_SETREDRAW, TRUE, 0);
712
713 return (uItemCount > 0) ? TRUE : FALSE;
714 }
715
716 /**
717 * Navigate to a given PIDL in the treeview, and return matching tree item handle
718 * - dest: The absolute PIDL we should navigate in the treeview
719 * - item: Handle of the tree item matching the PIDL
720 * - bExpand: expand collapsed nodes in order to find the right element
721 * - bInsert: insert the element at the right place if we don't find it
722 * - bSelect: select the item after we found it
723 */
724 BOOL CExplorerBand::NavigateToPIDL(LPITEMIDLIST dest, HTREEITEM *item, BOOL bExpand, BOOL bInsert,
725 BOOL bSelect)
726 {
727 HTREEITEM current;
728 HTREEITEM tmp;
729 HTREEITEM parent;
730 BOOL found;
731 NodeInfo *nodeData;
732 LPITEMIDLIST relativeChild;
733 TVITEM tvItem;
734
735 if (!item)
736 return FALSE;
737
738 found = FALSE;
739 current = hRoot;
740 parent = NULL;
741 while(!found)
742 {
743 nodeData = GetNodeInfo(current);
744 if (!nodeData)
745 {
746 ERR("Something has gone wrong, no data associated to node !\n");
747 *item = NULL;
748 return FALSE;
749 }
750 // If we found our node, give it back
751 if (!pDesktop->CompareIDs(0, nodeData->absolutePidl, dest))
752 {
753 if (bSelect)
754 TreeView_SelectItem(m_hWnd, current);
755 *item = current;
756 return TRUE;
757 }
758
759 // Check if we are a parent of the requested item
760 relativeChild = ILFindChild(nodeData->absolutePidl, dest);
761 if (relativeChild != 0)
762 {
763 // Notify treeview we have children
764 tvItem.mask = TVIF_CHILDREN;
765 tvItem.hItem = current;
766 tvItem.cChildren = 1;
767 TreeView_SetItem(m_hWnd, &tvItem);
768
769 // If we can expand and the node isn't expanded yet, do it
770 if (bExpand)
771 {
772 if (!nodeData->expanded)
773 InsertSubitems(current, nodeData);
774 TreeView_Expand(m_hWnd, current, TVE_EXPAND);
775 }
776
777 // Try to get a child
778 tmp = TreeView_GetChild(m_hWnd, current);
779 if (tmp)
780 {
781 // We have a child, let's continue with it
782 parent = current;
783 current = tmp;
784 continue;
785 }
786
787 if (bInsert && nodeData->expanded)
788 {
789 // Happens when we have to create a subchild inside a child
790 current = InsertItem(current, dest, relativeChild, TRUE);
791 }
792 // We end up here, without any children, so we found nothing
793 // Tell the parent node it has children
794 ZeroMemory(&tvItem, sizeof(tvItem));
795 *item = NULL;
796 return FALSE;
797 }
798
799 // Find sibling
800 tmp = TreeView_GetNextSibling(m_hWnd, current);
801 if (tmp)
802 {
803 current = tmp;
804 continue;
805 }
806 if (bInsert)
807 {
808 current = InsertItem(parent, dest, ILFindLastID(dest), TRUE);
809 *item = current;
810 return TRUE;
811 }
812 *item = NULL;
813 return FALSE;
814 }
815 return FALSE;
816 }
817
818 BOOL CExplorerBand::NavigateToCurrentFolder()
819 {
820 LPITEMIDLIST explorerPidl;
821 CComPtr<IBrowserService> pBrowserService;
822 HRESULT hr;
823 HTREEITEM dummy;
824 BOOL result;
825 explorerPidl = NULL;
826
827 hr = IUnknown_QueryService(pSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pBrowserService));
828 if (!SUCCEEDED(hr))
829 {
830 ERR("Can't get IBrowserService !\n");
831 return FALSE;
832 }
833
834 hr = pBrowserService->GetPidl(&explorerPidl);
835 if (!SUCCEEDED(hr) || !explorerPidl)
836 {
837 ERR("Unable to get browser PIDL !\n");
838 return FALSE;
839 }
840 bNavigating = TRUE;
841 /* find PIDL into our explorer */
842 result = NavigateToPIDL(explorerPidl, &dummy, TRUE, FALSE, TRUE);
843 bNavigating = FALSE;
844 return result;
845 }
846
847 BOOL CExplorerBand::DeleteItem(LPITEMIDLIST idl)
848 {
849 HTREEITEM toDelete;
850 TVITEM tvItem;
851 HTREEITEM parentNode;
852
853 if (!NavigateToPIDL(idl, &toDelete, FALSE, FALSE, FALSE))
854 return FALSE;
855
856 // TODO: check that the treeview item is really deleted
857
858 parentNode = TreeView_GetParent(m_hWnd, toDelete);
859 // Navigate to parent when deleting child item
860 if (!pDesktop->CompareIDs(0, idl, pidlCurrent))
861 {
862 TreeView_SelectItem(m_hWnd, parentNode);
863 }
864
865 // Remove the child item
866 TreeView_DeleteItem(m_hWnd, toDelete);
867 // Probe parent to see if it has children
868 if (!TreeView_GetChild(m_hWnd, parentNode))
869 {
870 // Decrement parent's child count
871 ZeroMemory(&tvItem, sizeof(tvItem));
872 tvItem.mask = TVIF_CHILDREN;
873 tvItem.hItem = parentNode;
874 tvItem.cChildren = 0;
875 TreeView_SetItem(m_hWnd, &tvItem);
876 }
877 return TRUE;
878 }
879
880 BOOL CExplorerBand::RenameItem(HTREEITEM toRename, LPITEMIDLIST newPidl)
881 {
882 WCHAR wszDisplayName[MAX_PATH];
883 TVITEM itemInfo;
884 LPCITEMIDLIST relPidl;
885 NodeInfo *treeInfo;
886 TVSORTCB sortCallback;
887 HTREEITEM child;
888
889 ZeroMemory(&itemInfo, sizeof(itemInfo));
890 itemInfo.mask = TVIF_PARAM;
891 itemInfo.hItem = toRename;
892
893 // Change PIDL associated to the item
894 relPidl = ILFindLastID(newPidl);
895 TreeView_GetItem(m_hWnd, &itemInfo);
896 if (!itemInfo.lParam)
897 {
898 ERR("Unable to fetch lParam\n");
899 return FALSE;
900 }
901 SendMessage(WM_SETREDRAW, FALSE, 0);
902 treeInfo = (NodeInfo*)itemInfo.lParam;
903 ILFree(treeInfo->absolutePidl);
904 ILFree(treeInfo->relativePidl);
905 treeInfo->absolutePidl = ILClone(newPidl);
906 treeInfo->relativePidl = ILClone(relPidl);
907
908 // Change the display name
909 GetDisplayName(newPidl, wszDisplayName, MAX_PATH, SHGDN_INFOLDER);
910 ZeroMemory(&itemInfo, sizeof(itemInfo));
911 itemInfo.hItem = toRename;
912 itemInfo.mask = TVIF_TEXT;
913 itemInfo.pszText = wszDisplayName;
914 TreeView_SetItem(m_hWnd, &itemInfo);
915
916 if((child = TreeView_GetChild(m_hWnd, toRename)) != NULL)
917 {
918 RefreshTreePidl(child, newPidl);
919 }
920
921 // Sorting
922 sortCallback.hParent = TreeView_GetParent(m_hWnd, toRename);
923 sortCallback.lpfnCompare = CompareTreeItems;
924 sortCallback.lParam = (LPARAM)this;
925 SendMessage(TVM_SORTCHILDRENCB, 0, (LPARAM)&sortCallback);
926 SendMessage(WM_SETREDRAW, TRUE, 0);
927 return TRUE;
928 }
929
930 BOOL CExplorerBand::RefreshTreePidl(HTREEITEM tree, LPITEMIDLIST pidlParent)
931 {
932 HTREEITEM tmp;
933 NodeInfo *pInfo;
934
935 // Update our node data
936 pInfo = GetNodeInfo(tree);
937 if (!pInfo)
938 {
939 WARN("No tree info !\n");
940 return FALSE;
941 }
942 ILFree(pInfo->absolutePidl);
943 pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl);
944 if (!pInfo->absolutePidl)
945 {
946 WARN("PIDL allocation failed\n");
947 return FALSE;
948 }
949 // Recursively update children
950 if ((tmp = TreeView_GetChild(m_hWnd, tree)) != NULL)
951 {
952 RefreshTreePidl(tmp, pInfo->absolutePidl);
953 }
954
955 tmp = TreeView_GetNextSibling(m_hWnd, tree);
956 while(tmp != NULL)
957 {
958 pInfo = GetNodeInfo(tmp);
959 if(!pInfo)
960 {
961 WARN("No tree info !\n");
962 continue;
963 }
964 ILFree(pInfo->absolutePidl);
965 pInfo->absolutePidl = ILCombine(pidlParent, pInfo->relativePidl);
966 tmp = TreeView_GetNextSibling(m_hWnd, tmp);
967 }
968 return TRUE;
969 }
970
971 // *** Tree item sorting callback ***
972 int CALLBACK CExplorerBand::CompareTreeItems(LPARAM p1, LPARAM p2, LPARAM p3)
973 {
974 /*
975 * We first sort drive letters (Path root), then PIDLs and then regular folder
976 * display name.
977 * This is not how Windows sorts item, but it gives decent results.
978 */
979 NodeInfo *info1;
980 NodeInfo *info2;
981 CExplorerBand *pThis;
982 WCHAR wszFolder1[MAX_PATH];
983 WCHAR wszFolder2[MAX_PATH];
984
985 info1 = (NodeInfo*)p1;
986 info2 = (NodeInfo*)p2;
987 pThis = (CExplorerBand*)p3;
988
989 GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_FORPARSING);
990 GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_FORPARSING);
991 if (PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2))
992 {
993 return lstrcmpiW(wszFolder1,wszFolder2);
994 }
995 if (PathIsRoot(wszFolder1) && !PathIsRoot(wszFolder2))
996 {
997 return -1;
998 }
999 if (!PathIsRoot(wszFolder1) && PathIsRoot(wszFolder2))
1000 {
1001 return 1;
1002 }
1003 // Now, we compare non-root folders, grab display name
1004 GetDisplayName(info1->absolutePidl, wszFolder1, MAX_PATH, SHGDN_INFOLDER);
1005 GetDisplayName(info2->absolutePidl, wszFolder2, MAX_PATH, SHGDN_INFOLDER);
1006
1007 if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl))
1008 {
1009 return -1;
1010 }
1011 if (!_ILIsSpecialFolder(info1->relativePidl) && _ILIsSpecialFolder(info2->relativePidl))
1012 {
1013 return 1;
1014 }
1015 if (_ILIsSpecialFolder(info1->relativePidl) && !_ILIsSpecialFolder(info2->relativePidl))
1016 {
1017 HRESULT hr;
1018 hr = pThis->pDesktop->CompareIDs(0, info1->absolutePidl, info2->absolutePidl);
1019 if (!hr) return 0;
1020 return (hr > 0) ? -1 : 1;
1021 }
1022 return StrCmpLogicalW(wszFolder1, wszFolder2);
1023 }
1024
1025 // *** IOleWindow methods ***
1026 HRESULT STDMETHODCALLTYPE CExplorerBand::GetWindow(HWND *lphwnd)
1027 {
1028 if (!lphwnd)
1029 return E_INVALIDARG;
1030 *lphwnd = m_hWnd;
1031 return S_OK;
1032 }
1033
1034 HRESULT STDMETHODCALLTYPE CExplorerBand::ContextSensitiveHelp(BOOL fEnterMode)
1035 {
1036 UNIMPLEMENTED;
1037 return E_NOTIMPL;
1038 }
1039
1040
1041 // *** IDockingWindow methods ***
1042 HRESULT STDMETHODCALLTYPE CExplorerBand::CloseDW(DWORD dwReserved)
1043 {
1044 // We do nothing, we don't have anything to save yet
1045 TRACE("CloseDW called\n");
1046 return S_OK;
1047 }
1048
1049 HRESULT STDMETHODCALLTYPE CExplorerBand::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
1050 {
1051 /* Must return E_NOTIMPL according to MSDN */
1052 return E_NOTIMPL;
1053 }
1054
1055 HRESULT STDMETHODCALLTYPE CExplorerBand::ShowDW(BOOL fShow)
1056 {
1057 fVisible = fShow;
1058 ShowWindow(fShow);
1059 return S_OK;
1060 }
1061
1062
1063 // *** IDeskBand methods ***
1064 HRESULT STDMETHODCALLTYPE CExplorerBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
1065 {
1066 if (!pdbi)
1067 {
1068 return E_INVALIDARG;
1069 }
1070 this->dwBandID = dwBandID;
1071
1072 if (pdbi->dwMask & DBIM_MINSIZE)
1073 {
1074 pdbi->ptMinSize.x = 200;
1075 pdbi->ptMinSize.y = 30;
1076 }
1077
1078 if (pdbi->dwMask & DBIM_MAXSIZE)
1079 {
1080 pdbi->ptMaxSize.y = -1;
1081 }
1082
1083 if (pdbi->dwMask & DBIM_INTEGRAL)
1084 {
1085 pdbi->ptIntegral.y = 1;
1086 }
1087
1088 if (pdbi->dwMask & DBIM_ACTUAL)
1089 {
1090 pdbi->ptActual.x = 200;
1091 pdbi->ptActual.y = 30;
1092 }
1093
1094 if (pdbi->dwMask & DBIM_TITLE)
1095 {
1096 if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_FOLDERSLABEL, pdbi->wszTitle, _countof(pdbi->wszTitle)))
1097 return HRESULT_FROM_WIN32(GetLastError());
1098 }
1099
1100 if (pdbi->dwMask & DBIM_MODEFLAGS)
1101 {
1102 pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT;
1103 }
1104
1105 if (pdbi->dwMask & DBIM_BKCOLOR)
1106 {
1107 pdbi->dwMask &= ~DBIM_BKCOLOR;
1108 }
1109 return S_OK;
1110 }
1111
1112
1113 // *** IObjectWithSite methods ***
1114 HRESULT STDMETHODCALLTYPE CExplorerBand::SetSite(IUnknown *pUnkSite)
1115 {
1116 HRESULT hr;
1117 HWND parentWnd;
1118
1119 if (pUnkSite == pSite)
1120 return S_OK;
1121
1122 TRACE("SetSite called \n");
1123 if (!pUnkSite)
1124 {
1125 DestroyExplorerBand();
1126 DestroyWindow();
1127 m_hWnd = NULL;
1128 }
1129
1130 if (pUnkSite != pSite)
1131 {
1132 pSite = NULL;
1133 }
1134
1135 if(!pUnkSite)
1136 return S_OK;
1137
1138 hr = IUnknown_GetWindow(pUnkSite, &parentWnd);
1139 if (!SUCCEEDED(hr))
1140 {
1141 ERR("Could not get parent's window ! Status: %08lx\n", hr);
1142 return E_INVALIDARG;
1143 }
1144
1145 pSite = pUnkSite;
1146
1147 if (m_hWnd)
1148 {
1149 // Change its parent
1150 SetParent(parentWnd);
1151 }
1152 else
1153 {
1154 HWND wnd = CreateWindow(WC_TREEVIEW, NULL,
1155 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
1156 0, 0, 0, 0, parentWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL);
1157
1158 // Subclass the window
1159 SubclassWindow(wnd);
1160
1161 // Initialize our treeview now
1162 InitializeExplorerBand();
1163 RegisterDragDrop(m_hWnd, dynamic_cast<IDropTarget*>(this));
1164 }
1165 return S_OK;
1166 }
1167
1168 HRESULT STDMETHODCALLTYPE CExplorerBand::GetSite(REFIID riid, void **ppvSite)
1169 {
1170 if (!ppvSite)
1171 return E_POINTER;
1172 *ppvSite = pSite;
1173 return S_OK;
1174 }
1175
1176
1177 // *** IOleCommandTarget methods ***
1178 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
1179 {
1180 UNIMPLEMENTED;
1181 return E_NOTIMPL;
1182 }
1183
1184 HRESULT STDMETHODCALLTYPE CExplorerBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
1185 {
1186 UNIMPLEMENTED;
1187 return E_NOTIMPL;
1188 }
1189
1190
1191 // *** IServiceProvider methods ***
1192 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
1193 {
1194 /* FIXME: we probably want to handle more services here */
1195 return IUnknown_QueryService(pSite, SID_SShellBrowser, riid, ppvObject);
1196 }
1197
1198
1199 // *** IInputObject methods ***
1200 HRESULT STDMETHODCALLTYPE CExplorerBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
1201 {
1202 if (fActivate)
1203 {
1204 //SetFocus();
1205 SetActiveWindow();
1206 }
1207 // TODO: handle message
1208 if(lpMsg)
1209 {
1210 TranslateMessage(lpMsg);
1211 DispatchMessage(lpMsg);
1212 }
1213 return S_OK;
1214 }
1215
1216 HRESULT STDMETHODCALLTYPE CExplorerBand::HasFocusIO()
1217 {
1218 return bFocused ? S_OK : S_FALSE;
1219 }
1220
1221 HRESULT STDMETHODCALLTYPE CExplorerBand::TranslateAcceleratorIO(LPMSG lpMsg)
1222 {
1223 if (lpMsg->hwnd == m_hWnd)
1224 {
1225 TranslateMessage(lpMsg);
1226 DispatchMessage(lpMsg);
1227 return S_OK;
1228 }
1229
1230 return S_FALSE;
1231 }
1232
1233 // *** IPersist methods ***
1234 HRESULT STDMETHODCALLTYPE CExplorerBand::GetClassID(CLSID *pClassID)
1235 {
1236 if (!pClassID)
1237 return E_POINTER;
1238 memcpy(pClassID, &CLSID_ExplorerBand, sizeof(CLSID));
1239 return S_OK;
1240 }
1241
1242
1243 // *** IPersistStream methods ***
1244 HRESULT STDMETHODCALLTYPE CExplorerBand::IsDirty()
1245 {
1246 UNIMPLEMENTED;
1247 return E_NOTIMPL;
1248 }
1249
1250 HRESULT STDMETHODCALLTYPE CExplorerBand::Load(IStream *pStm)
1251 {
1252 UNIMPLEMENTED;
1253 return E_NOTIMPL;
1254 }
1255
1256 HRESULT STDMETHODCALLTYPE CExplorerBand::Save(IStream *pStm, BOOL fClearDirty)
1257 {
1258 UNIMPLEMENTED;
1259 return E_NOTIMPL;
1260 }
1261
1262 HRESULT STDMETHODCALLTYPE CExplorerBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
1263 {
1264 // TODO: calculate max size
1265 UNIMPLEMENTED;
1266 return E_NOTIMPL;
1267 }
1268
1269
1270 // *** IWinEventHandler methods ***
1271 HRESULT STDMETHODCALLTYPE CExplorerBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
1272 {
1273 BOOL bHandled;
1274 if (uMsg == WM_NOTIFY)
1275 {
1276 NMHDR *pNotifyHeader = (NMHDR*)lParam;
1277 switch (pNotifyHeader->code)
1278 {
1279 case TVN_ITEMEXPANDING:
1280 *theResult = OnTreeItemExpanding((LPNMTREEVIEW)lParam);
1281 break;
1282 case TVN_SELCHANGED:
1283 OnSelectionChanged((LPNMTREEVIEW)lParam);
1284 break;
1285 case TVN_DELETEITEM:
1286 OnTreeItemDeleted((LPNMTREEVIEW)lParam);
1287 break;
1288 case NM_RCLICK:
1289 OnContextMenu(WM_CONTEXTMENU, (WPARAM)m_hWnd, GetMessagePos(), bHandled);
1290 *theResult = 1;
1291 break;
1292 case TVN_BEGINDRAG:
1293 case TVN_BEGINRDRAG:
1294 OnTreeItemDragging((LPNMTREEVIEW)lParam, pNotifyHeader->code == TVN_BEGINRDRAG);
1295 case TVN_BEGINLABELEDITW:
1296 {
1297 // TODO: put this in a function ? (mostly copypasta from CDefView)
1298 DWORD dwAttr = SFGAO_CANRENAME;
1299 LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam;
1300 CComPtr<IShellFolder> pParent;
1301 LPCITEMIDLIST pChild;
1302 HRESULT hr;
1303
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))
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 *theResult = 0;
1324 if (dispInfo->item.pszText)
1325 {
1326 LPITEMIDLIST pidlNew;
1327 CComPtr<IShellFolder> pParent;
1328 LPCITEMIDLIST pidlChild;
1329
1330 hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pidlChild);
1331 if (!SUCCEEDED(hr) || !pParent.p)
1332 return E_FAIL;
1333
1334 hr = pParent->SetNameOf(0, pidlChild, dispInfo->item.pszText, SHGDN_INFOLDER, &pidlNew);
1335 if(SUCCEEDED(hr) && pidlNew)
1336 {
1337 CComPtr<IPersistFolder2> pPersist;
1338 LPITEMIDLIST pidlParent, pidlNewAbs;
1339
1340 hr = pParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pPersist));
1341 if(!SUCCEEDED(hr))
1342 return E_FAIL;
1343
1344 hr = pPersist->GetCurFolder(&pidlParent);
1345 if(!SUCCEEDED(hr))
1346 return E_FAIL;
1347 pidlNewAbs = ILCombine(pidlParent, pidlNew);
1348
1349 // Navigate to our new location
1350 UpdateBrowser(pidlNewAbs);
1351
1352 ILFree(pidlNewAbs);
1353 ILFree(pidlNew);
1354 *theResult = 1;
1355 }
1356 return S_OK;
1357 }
1358 }
1359 default:
1360 break;
1361 }
1362 }
1363 return S_OK;
1364 }
1365
1366 HRESULT STDMETHODCALLTYPE CExplorerBand::IsWindowOwner(HWND hWnd)
1367 {
1368 return (hWnd == m_hWnd) ? S_OK : S_FALSE;
1369 }
1370
1371 // *** IBandNavigate methods ***
1372 HRESULT STDMETHODCALLTYPE CExplorerBand::Select(long paramC)
1373 {
1374 UNIMPLEMENTED;
1375 return E_NOTIMPL;
1376 }
1377
1378 // *** INamespaceProxy ***
1379 HRESULT STDMETHODCALLTYPE CExplorerBand::GetNavigateTarget(long paramC, long param10, long param14)
1380 {
1381 UNIMPLEMENTED;
1382 return E_NOTIMPL;
1383 }
1384
1385 HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(long paramC)
1386 {
1387 UNIMPLEMENTED;
1388 return E_NOTIMPL;
1389 }
1390
1391 HRESULT STDMETHODCALLTYPE CExplorerBand::OnSelectionChanged(long paramC)
1392 {
1393 UNIMPLEMENTED;
1394 return E_NOTIMPL;
1395 }
1396
1397 HRESULT STDMETHODCALLTYPE CExplorerBand::RefreshFlags(long paramC, long param10, long param14)
1398 {
1399 UNIMPLEMENTED;
1400 return E_NOTIMPL;
1401 }
1402
1403 HRESULT STDMETHODCALLTYPE CExplorerBand::CacheItem(long paramC)
1404 {
1405 UNIMPLEMENTED;
1406 return E_NOTIMPL;
1407 }
1408
1409 // *** IDispatch methods ***
1410 HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfoCount(UINT *pctinfo)
1411 {
1412 UNIMPLEMENTED;
1413 return E_NOTIMPL;
1414 }
1415
1416 HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
1417 {
1418 UNIMPLEMENTED;
1419 return E_NOTIMPL;
1420 }
1421
1422 HRESULT STDMETHODCALLTYPE CExplorerBand::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
1423 {
1424 UNIMPLEMENTED;
1425 return E_NOTIMPL;
1426 }
1427
1428 HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1429 {
1430 switch (dispIdMember)
1431 {
1432 case DISPID_DOWNLOADCOMPLETE:
1433 case DISPID_NAVIGATECOMPLETE2:
1434 TRACE("DISPID_NAVIGATECOMPLETE2 received\n");
1435 NavigateToCurrentFolder();
1436 return S_OK;
1437 }
1438 TRACE("Unknown dispid requested: %08x\n", dispIdMember);
1439 return E_INVALIDARG;
1440 }
1441
1442 // *** IDropTarget methods ***
1443 HRESULT STDMETHODCALLTYPE CExplorerBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1444 {
1445 ERR("Entering drag\n");
1446 pCurObject = pObj;
1447 oldSelected = TreeView_GetSelection(m_hWnd);
1448 return DragOver(glfKeyState, pt, pdwEffect);
1449 }
1450
1451 HRESULT STDMETHODCALLTYPE CExplorerBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1452 {
1453 TVHITTESTINFO info;
1454 CComPtr<IShellFolder> pShellFldr;
1455 NodeInfo *nodeInfo;
1456 //LPCITEMIDLIST pChild;
1457 HRESULT hr;
1458
1459 info.pt.x = pt.x;
1460 info.pt.y = pt.y;
1461 info.flags = TVHT_ONITEM;
1462 info.hItem = NULL;
1463 ScreenToClient(&info.pt);
1464
1465 // Move to the item selected by the treeview (don't change right pane)
1466 TreeView_HitTest(m_hWnd, &info);
1467
1468 if (info.hItem)
1469 {
1470 bNavigating = TRUE;
1471 TreeView_SelectItem(m_hWnd, info.hItem);
1472 bNavigating = FALSE;
1473 // Delegate to shell folder
1474 if (pDropTarget && info.hItem != childTargetNode)
1475 {
1476 pDropTarget = NULL;
1477 }
1478 if (info.hItem != childTargetNode)
1479 {
1480 nodeInfo = GetNodeInfo(info.hItem);
1481 if (!nodeInfo)
1482 return E_FAIL;
1483 #if 0
1484 hr = SHBindToParent(nodeInfo->absolutePidl, IID_PPV_ARG(IShellFolder, &pShellFldr), &pChild);
1485 if (!SUCCEEDED(hr))
1486 return E_FAIL;
1487 hr = pShellFldr->GetUIObjectOf(m_hWnd, 1, &pChild, IID_IDropTarget, NULL, reinterpret_cast<void**>(&pDropTarget));
1488 if (!SUCCEEDED(hr))
1489 return E_FAIL;
1490 #endif
1491 if(_ILIsDesktop(nodeInfo->absolutePidl))
1492 pShellFldr = pDesktop;
1493 else
1494 {
1495 hr = pDesktop->BindToObject(nodeInfo->absolutePidl, 0, IID_PPV_ARG(IShellFolder, &pShellFldr));
1496 if (!SUCCEEDED(hr))
1497 {
1498 /* Don't allow dnd since we couldn't get our folder object */
1499 ERR("Can't bind to folder object\n");
1500 *pdwEffect = DROPEFFECT_NONE;
1501 return E_FAIL;
1502 }
1503 }
1504 hr = pShellFldr->CreateViewObject(m_hWnd, IID_PPV_ARG(IDropTarget, &pDropTarget));
1505 if (!SUCCEEDED(hr))
1506 {
1507 /* Don't allow dnd since we couldn't get our drop target */
1508 ERR("Can't get drop target for folder object\n");
1509 *pdwEffect = DROPEFFECT_NONE;
1510 return E_FAIL;
1511 }
1512 hr = pDropTarget->DragEnter(pCurObject, glfKeyState, pt, pdwEffect);
1513 childTargetNode = info.hItem;
1514 }
1515 if (pDropTarget)
1516 {
1517 hr = pDropTarget->DragOver(glfKeyState, pt, pdwEffect);
1518 }
1519 }
1520 else
1521 {
1522 childTargetNode = NULL;
1523 pDropTarget = NULL;
1524 *pdwEffect = DROPEFFECT_NONE;
1525 }
1526 return S_OK;
1527 }
1528
1529 HRESULT STDMETHODCALLTYPE CExplorerBand::DragLeave()
1530 {
1531 bNavigating = TRUE;
1532 TreeView_SelectItem(m_hWnd, oldSelected);
1533 bNavigating = FALSE;
1534 childTargetNode = NULL;
1535 if (pCurObject)
1536 {
1537 pCurObject = NULL;
1538 }
1539 return S_OK;
1540 }
1541
1542 HRESULT STDMETHODCALLTYPE CExplorerBand::Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1543 {
1544 if (!pDropTarget)
1545 return E_FAIL;
1546 pDropTarget->Drop(pObj, glfKeyState, pt, pdwEffect);
1547 DragLeave();
1548 return S_OK;
1549 }
1550
1551 // *** IDropSource methods ***
1552 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
1553 {
1554 if (fEscapePressed)
1555 return DRAGDROP_S_CANCEL;
1556 if ((grfKeyState & MK_LBUTTON) || (grfKeyState & MK_RBUTTON))
1557 return S_OK;
1558 return DRAGDROP_S_DROP;
1559 }
1560
1561 HRESULT STDMETHODCALLTYPE CExplorerBand::GiveFeedback(DWORD dwEffect)
1562 {
1563 return DRAGDROP_S_USEDEFAULTCURSORS;
1564 }