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