[SHELL32]
[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 extern "C"
32 HRESULT WINAPI CExplorerBand_Constructor(REFIID riid, LPVOID *ppv)
33 {
34 return ShellObjectCreator<CExplorerBand>(riid, ppv);
35 }
36
37 CExplorerBand::CExplorerBand() :
38 pSite(NULL), fVisible(FALSE), bNavigating(FALSE), dwBandID(0)
39 {
40 }
41
42 CExplorerBand::~CExplorerBand()
43 {
44 }
45
46 void CExplorerBand::InitializeExplorerBand()
47 {
48 // Init the treeview here
49 HRESULT hr;
50 LPITEMIDLIST pidl;
51 CComPtr<IWebBrowser2> browserService;
52 SHChangeNotifyEntry shcne;
53
54 hr = SHGetDesktopFolder(&pDesktop);
55 if (FAILED_UNEXPECTEDLY(hr))
56 return;
57
58 hr = SHGetFolderLocation(m_hWnd, CSIDL_DESKTOP, NULL, 0, &pidl);
59 if (FAILED_UNEXPECTEDLY(hr))
60 return;
61
62 IImageList * piml;
63 hr = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &piml));
64 if (FAILED_UNEXPECTEDLY(hr))
65 return;
66
67 TreeView_SetImageList(m_hWnd, (HIMAGELIST)piml, TVSIL_NORMAL);
68
69 // Insert the root node
70 hRoot = InsertItem(0, pDesktop, pidl, pidl, FALSE);
71 if (!hRoot)
72 {
73 ERR("Failed to create root item\n");
74 return;
75 }
76
77 NodeInfo* pNodeInfo = GetNodeInfo(hRoot);
78
79 // Insert child nodes
80 InsertSubitems(hRoot, pNodeInfo);
81 TreeView_Expand(m_hWnd, hRoot, TVE_EXPAND);
82
83 // Navigate to current folder position
84 NavigateToCurrentFolder();
85
86 // Register shell notification
87 shcne.pidl = pidl;
88 shcne.fRecursive = TRUE;
89 shellRegID = SHChangeNotifyRegister(
90 m_hWnd,
91 SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_RecursiveInterrupt,
92 SHCNE_DISKEVENTS | SHCNE_RENAMEFOLDER | SHCNE_RMDIR | SHCNE_MKDIR,
93 WM_USER_SHELLEVENT,
94 1,
95 &shcne);
96 if (!shellRegID)
97 {
98 ERR("Something went wrong, error %08x\n", GetLastError());
99 }
100 // Register browser connection endpoint
101 hr = IUnknown_QueryService(pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService));
102 if (FAILED_UNEXPECTEDLY(hr))
103 return;
104
105 hr = AtlAdvise(browserService, dynamic_cast<IDispatch*>(this), DIID_DWebBrowserEvents, &adviseCookie);
106 if (FAILED_UNEXPECTEDLY(hr))
107 return;
108
109 ILFree(pidl);
110 }
111
112 void CExplorerBand::DestroyExplorerBand()
113 {
114 HRESULT hr;
115 CComPtr <IWebBrowser2> browserService;
116
117 TRACE("Cleaning up explorer band ...\n");
118
119 hr = IUnknown_QueryService(pSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &browserService));
120 if (FAILED_UNEXPECTEDLY(hr))
121 return;
122
123 hr = AtlUnadvise(browserService, DIID_DWebBrowserEvents, adviseCookie);
124 /* Remove all items of the treeview */
125 RevokeDragDrop(m_hWnd);
126 TreeView_DeleteAllItems(m_hWnd);
127 pDesktop = NULL;
128 hRoot = NULL;
129 TRACE("Cleanup done !\n");
130 }
131
132 CExplorerBand::NodeInfo* CExplorerBand::GetNodeInfo(HTREEITEM hItem)
133 {
134 TVITEM tvItem;
135
136 tvItem.mask = TVIF_PARAM;
137 tvItem.hItem = hItem;
138
139 if (!TreeView_GetItem(m_hWnd, &tvItem))
140 return 0;
141
142 return reinterpret_cast<NodeInfo*>(tvItem.lParam);
143 }
144
145 HRESULT CExplorerBand::ExecuteCommand(CComPtr<IContextMenu>& menu, UINT nCmd)
146 {
147 CComPtr<IOleWindow> pBrowserOleWnd;
148 CMINVOKECOMMANDINFO cmi;
149 HWND browserWnd;
150 HRESULT hr;
151
152 hr = IUnknown_QueryService(pSite, SID_SShellBrowser, IID_PPV_ARG(IOleWindow, &pBrowserOleWnd));
153 if (FAILED_UNEXPECTEDLY(hr))
154 return hr;
155
156 hr = pBrowserOleWnd->GetWindow(&browserWnd);
157 if (FAILED_UNEXPECTEDLY(hr))
158 return hr;
159
160 ZeroMemory(&cmi, sizeof(cmi));
161 cmi.cbSize = sizeof(cmi);
162 cmi.lpVerb = MAKEINTRESOURCEA(nCmd);
163 cmi.hwnd = browserWnd;
164 if (GetKeyState(VK_SHIFT) & 0x8000)
165 cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
166 if (GetKeyState(VK_CONTROL) & 0x8000)
167 cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
168
169 return menu->InvokeCommand(&cmi);
170 }
171
172 HRESULT CExplorerBand::UpdateBrowser(LPITEMIDLIST pidlGoto)
173 {
174 CComPtr<IShellBrowser> pBrowserService;
175 HRESULT hr;
176
177 hr = IUnknown_QueryService(pSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &pBrowserService));
178 if (FAILED_UNEXPECTEDLY(hr))
179 return hr;
180
181 hr = pBrowserService->BrowseObject(pidlGoto, SBSP_SAMEBROWSER | SBSP_ABSOLUTE);
182 if (FAILED_UNEXPECTEDLY(hr))
183 return hr;
184
185 return hr;
186 }
187
188 // *** notifications handling ***
189 BOOL CExplorerBand::OnTreeItemExpanding(LPNMTREEVIEW pnmtv)
190 {
191 NodeInfo *pNodeInfo;
192
193 if (pnmtv->action == TVE_COLLAPSE) {
194 if (pnmtv->itemNew.hItem == hRoot)
195 {
196 // Prenvent root from collapsing
197 pnmtv->itemNew.mask |= TVIF_STATE;
198 pnmtv->itemNew.stateMask |= TVIS_EXPANDED;
199 pnmtv->itemNew.state &= ~TVIS_EXPANDED;
200 pnmtv->action = TVE_EXPAND;
201 return TRUE;
202 }
203 }
204 if (pnmtv->action == TVE_EXPAND) {
205 // Grab our directory PIDL
206 pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem);
207 // We have it, let's try
208 if (pNodeInfo && !pNodeInfo->expanded)
209 if (!InsertSubitems(pnmtv->itemNew.hItem, pNodeInfo)) {
210 // remove subitem "+" since we failed to add subitems
211 TV_ITEM tvItem;
212
213 tvItem.mask = TVIF_CHILDREN;
214 tvItem.hItem = pnmtv->itemNew.hItem;
215 tvItem.cChildren = 0;
216
217 TreeView_SetItem(m_hWnd, &tvItem);
218 }
219 }
220 return FALSE;
221 }
222
223 void CExplorerBand::OnSelectionChanged(LPNMTREEVIEW pnmtv)
224 {
225 NodeInfo* pNodeInfo = GetNodeInfo(pnmtv->itemNew.hItem);
226
227 /* Prevents navigation if selection is initiated inside the band */
228 if (bNavigating)
229 return;
230
231 UpdateBrowser(pNodeInfo->absolutePidl);
232
233 SetFocus();
234 // Expand the node
235 //TreeView_Expand(m_hWnd, pnmtv->itemNew.hItem, TVE_EXPAND);
236 }
237
238
239 // *** ATL event handlers ***
240 LRESULT CExplorerBand::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
241 {
242 HTREEITEM item;
243 NodeInfo *info;
244 HMENU treeMenu;
245 WORD x;
246 WORD y;
247 CComPtr<IShellFolder> pFolder;
248 CComPtr<IContextMenu> contextMenu;
249 HRESULT hr;
250 UINT uCommand;
251 LPITEMIDLIST pidlChild;
252
253 treeMenu = NULL;
254 item = TreeView_GetSelection(m_hWnd);
255 bHandled = TRUE;
256 if (!item)
257 {
258 goto Cleanup;
259 }
260
261 x = LOWORD(lParam);
262 y = HIWORD(lParam);
263 if (x == -1 && y == -1)
264 {
265 // TODO: grab position of tree item and position it correctly
266 }
267
268 info = GetNodeInfo(item);
269 if (!info)
270 {
271 ERR("No node data, something has gone wrong !\n");
272 goto Cleanup;
273 }
274 hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pFolder),
275 (LPCITEMIDLIST*)&pidlChild);
276 if (!SUCCEEDED(hr))
277 {
278 ERR("Can't bind to folder!\n");
279 goto Cleanup;
280 }
281 hr = pFolder->GetUIObjectOf(m_hWnd, 1, (LPCITEMIDLIST*)&pidlChild, IID_IContextMenu,
282 NULL, reinterpret_cast<void**>(&contextMenu));
283 if (!SUCCEEDED(hr))
284 {
285 ERR("Can't get IContextMenu interface\n");
286 goto Cleanup;
287 }
288 treeMenu = CreatePopupMenu();
289 hr = contextMenu->QueryContextMenu(treeMenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST,
290 CMF_EXPLORE);
291 if (!SUCCEEDED(hr))
292 {
293 WARN("Can't get context menu for item\n");
294 DestroyMenu(treeMenu);
295 goto Cleanup;
296 }
297 uCommand = TrackPopupMenu(treeMenu, TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
298 x, y, 0, m_hWnd, NULL);
299
300 ExecuteCommand(contextMenu, uCommand);
301 Cleanup:
302 if (treeMenu)
303 DestroyMenu(treeMenu);
304 bNavigating = TRUE;
305 TreeView_SelectItem(m_hWnd, oldSelected);
306 bNavigating = FALSE;
307 return TRUE;
308 }
309
310 LRESULT CExplorerBand::ContextMenuHack(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
311 {
312 bHandled = FALSE;
313 if (uMsg == WM_RBUTTONDOWN)
314 {
315 TVHITTESTINFO info;
316 info.pt.x = LOWORD(lParam);
317 info.pt.y = HIWORD(lParam);
318 info.flags = TVHT_ONITEM;
319 info.hItem = NULL;
320
321 // Save the current location
322 oldSelected = TreeView_GetSelection(m_hWnd);
323
324 // Move to the item selected by the treeview (don't change right pane)
325 TreeView_HitTest(m_hWnd, &info);
326 bNavigating = TRUE;
327 TreeView_SelectItem(m_hWnd, info.hItem);
328 bNavigating = FALSE;
329 }
330 return FALSE; /* let the wndproc process the message */
331 }
332 // *** Helper functions ***
333 HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, IShellFolder *psfParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort)
334 {
335 TV_INSERTSTRUCT tvInsert;
336 HTREEITEM htiCreated;
337
338 /* Get the attributes of the node */
339 SFGAOF attrs = SFGAO_STREAM | SFGAO_HASSUBFOLDER;
340 HRESULT hr = psfParent->GetAttributesOf(1, &pEltRelative, &attrs);
341 if (FAILED_UNEXPECTEDLY(hr))
342 return NULL;
343
344 /* Ignore streams */
345 if ((attrs & SFGAO_STREAM))
346 {
347 TRACE("Ignoring stream\n");
348 return NULL;
349 }
350
351 /* Get the name of the node */
352 WCHAR wszDisplayName[MAX_PATH];
353 if (!ILGetDisplayNameEx(psfParent, pEltRelative, wszDisplayName, ILGDN_INFOLDER))
354 {
355 ERR("Failed to get node name\n");
356 return NULL;
357 }
358
359 /* Get the icon of the node */
360 INT iIcon = SHMapPIDLToSystemImageListIndex(psfParent, pEltRelative, NULL);
361
362 NodeInfo* pChildInfo = new NodeInfo;
363 if (!pChildInfo)
364 {
365 ERR("Failed to allocate NodeInfo\n");
366 return FALSE;
367 }
368
369 // Store our node info
370 pChildInfo->absolutePidl = ILClone(pElt);
371 pChildInfo->relativePidl = ILClone(pEltRelative);
372 pChildInfo->expanded = FALSE;
373
374 // Set up our treeview template
375 tvInsert.hParent = hParent;
376 tvInsert.hInsertAfter = TVI_LAST;
377 tvInsert.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
378 tvInsert.item.cchTextMax = MAX_PATH;
379 tvInsert.item.pszText = wszDisplayName;
380 tvInsert.item.iImage = tvInsert.item.iSelectedImage = iIcon;
381 tvInsert.item.cChildren = (attrs & SFGAO_HASSUBFOLDER) ? 1 : 0;
382 tvInsert.item.lParam = (LPARAM)pChildInfo;
383
384 htiCreated = TreeView_InsertItem(m_hWnd, &tvInsert);
385
386 return htiCreated;
387 }
388
389 /* This is the slow version of the above method */
390 HTREEITEM CExplorerBand::InsertItem(HTREEITEM hParent, LPITEMIDLIST pElt, LPITEMIDLIST pEltRelative, BOOL bSort)
391 {
392 CComPtr<IShellFolder> psfFolder;
393 HRESULT hr = SHBindToParent(pElt, IID_PPV_ARG(IShellFolder, &psfFolder), NULL);
394 if (FAILED_UNEXPECTEDLY(hr))
395 return NULL;
396
397 return InsertItem(hParent, psfFolder, pElt, pEltRelative, bSort);
398 }
399
400 BOOL CExplorerBand::InsertSubitems(HTREEITEM hItem, NodeInfo *pNodeInfo)
401 {
402 CComPtr<IEnumIDList> pEnumIDList;
403 LPITEMIDLIST pidlSub;
404 LPITEMIDLIST entry;
405 SHCONTF EnumFlags;
406 HRESULT hr;
407 ULONG fetched;
408 ULONG uItemCount;
409 CComPtr<IShellFolder> pFolder;
410
411 entry = pNodeInfo->absolutePidl;
412 fetched = 1;
413 uItemCount = 0;
414 EnumFlags = SHCONTF_FOLDERS;
415
416 hr = SHGetFolderLocation(m_hWnd, CSIDL_DESKTOP, NULL, 0, &pidlSub);
417 if (!SUCCEEDED(hr))
418 {
419 ERR("Can't get desktop PIDL !\n");
420 return FALSE;
421 }
422
423 if (!pDesktop->CompareIDs(NULL, pidlSub, entry))
424 {
425 // We are the desktop, so use pDesktop as pFolder
426 pFolder = pDesktop;
427 }
428 else
429 {
430 // Get an IShellFolder of our pidl
431 hr = pDesktop->BindToObject(entry, NULL, IID_PPV_ARG(IShellFolder, &pFolder));
432 if (!SUCCEEDED(hr))
433 {
434 ILFree(pidlSub);
435 ERR("Can't bind folder to desktop !\n");
436 return FALSE;
437 }
438 }
439 ILFree(pidlSub);
440
441 // TODO: handle hidden folders according to settings !
442 EnumFlags |= SHCONTF_INCLUDEHIDDEN;
443
444 // Enum through objects
445 hr = pFolder->EnumObjects(NULL,EnumFlags,&pEnumIDList);
446
447 // avoid broken IShellFolder implementations that return null pointer with success
448 if (!SUCCEEDED(hr) || !pEnumIDList)
449 {
450 ERR("Can't enum the folder !\n");
451 return FALSE;
452 }
453
454 /* Don't redraw while we add stuff into the tree */
455 SendMessage(WM_SETREDRAW, FALSE, 0);
456 while(SUCCEEDED(pEnumIDList->Next(1, &pidlSub, &fetched)) && pidlSub && fetched)
457 {
458 LPITEMIDLIST pidlSubComplete;
459 pidlSubComplete = ILCombine(entry, pidlSub);
460
461 if (InsertItem(hItem, pFolder, pidlSubComplete, pidlSub, FALSE))
462 uItemCount++;
463 ILFree(pidlSubComplete);
464 ILFree(pidlSub);
465 }
466 pNodeInfo->expanded = TRUE;
467
468 /* Now we can redraw */
469 SendMessage(WM_SETREDRAW, TRUE, 0);
470
471 return (uItemCount > 0) ? TRUE : FALSE;
472 }
473
474 /**
475 * Navigate to a given PIDL in the treeview, and return matching tree item handle
476 * - dest: The absolute PIDL we should navigate in the treeview
477 * - item: Handle of the tree item matching the PIDL
478 * - bExpand: expand collapsed nodes in order to find the right element
479 * - bInsert: insert the element at the right place if we don't find it
480 * - bSelect: select the item after we found it
481 */
482 BOOL CExplorerBand::NavigateToPIDL(LPITEMIDLIST dest, HTREEITEM *item, BOOL bExpand, BOOL bInsert,
483 BOOL bSelect)
484 {
485 HTREEITEM current;
486 HTREEITEM tmp;
487 HTREEITEM parent;
488 BOOL found;
489 NodeInfo *nodeData;
490 LPITEMIDLIST relativeChild;
491 TVITEM tvItem;
492
493 if (!item)
494 return FALSE;
495
496 found = FALSE;
497 current = hRoot;
498 parent = NULL;
499 while(!found)
500 {
501 nodeData = GetNodeInfo(current);
502 if (!nodeData)
503 {
504 ERR("Something has gone wrong, no data associated to node !\n");
505 *item = NULL;
506 return FALSE;
507 }
508 // If we found our node, give it back
509 if (!pDesktop->CompareIDs(0, nodeData->absolutePidl, dest))
510 {
511 if (bSelect)
512 TreeView_SelectItem(m_hWnd, current);
513 *item = current;
514 return TRUE;
515 }
516
517 // Check if we are a parent of the requested item
518 relativeChild = ILFindChild(nodeData->absolutePidl, dest);
519 if (relativeChild != 0)
520 {
521 // Notify treeview we have children
522 tvItem.mask = TVIF_CHILDREN;
523 tvItem.hItem = current;
524 tvItem.cChildren = 1;
525 TreeView_SetItem(m_hWnd, &tvItem);
526
527 // If we can expand and the node isn't expanded yet, do it
528 if (bExpand)
529 {
530 if (!nodeData->expanded)
531 InsertSubitems(current, nodeData);
532 TreeView_Expand(m_hWnd, current, TVE_EXPAND);
533 }
534
535 // Try to get a child
536 tmp = TreeView_GetChild(m_hWnd, current);
537 if (tmp)
538 {
539 // We have a child, let's continue with it
540 parent = current;
541 current = tmp;
542 continue;
543 }
544
545 if (bInsert && nodeData->expanded)
546 {
547 // Happens when we have to create a subchild inside a child
548 current = InsertItem(current, dest, relativeChild, TRUE);
549 }
550 // We end up here, without any children, so we found nothing
551 // Tell the parent node it has children
552 ZeroMemory(&tvItem, sizeof(tvItem));
553 *item = NULL;
554 return FALSE;
555 }
556
557 // Find sibling
558 tmp = TreeView_GetNextSibling(m_hWnd, current);
559 if (tmp)
560 {
561 current = tmp;
562 continue;
563 }
564 if (bInsert)
565 {
566 current = InsertItem(parent, dest, ILFindLastID(dest), TRUE);
567 *item = current;
568 return TRUE;
569 }
570 *item = NULL;
571 return FALSE;
572 }
573 return FALSE;
574 }
575
576 BOOL CExplorerBand::NavigateToCurrentFolder()
577 {
578 LPITEMIDLIST explorerPidl;
579 CComPtr<IBrowserService> pBrowserService;
580 HRESULT hr;
581 HTREEITEM dummy;
582 BOOL result;
583 explorerPidl = NULL;
584
585 hr = IUnknown_QueryService(pSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pBrowserService));
586 if (!SUCCEEDED(hr))
587 {
588 ERR("Can't get IBrowserService !\n");
589 return FALSE;
590 }
591
592 hr = pBrowserService->GetPidl(&explorerPidl);
593 if (!SUCCEEDED(hr) || !explorerPidl)
594 {
595 ERR("Unable to get browser PIDL !\n");
596 return FALSE;
597 }
598 bNavigating = TRUE;
599 /* find PIDL into our explorer */
600 result = NavigateToPIDL(explorerPidl, &dummy, TRUE, FALSE, TRUE);
601 bNavigating = FALSE;
602 return result;
603 }
604
605 // *** IOleWindow methods ***
606 HRESULT STDMETHODCALLTYPE CExplorerBand::GetWindow(HWND *lphwnd)
607 {
608 if (!lphwnd)
609 return E_INVALIDARG;
610 *lphwnd = m_hWnd;
611 return S_OK;
612 }
613
614 HRESULT STDMETHODCALLTYPE CExplorerBand::ContextSensitiveHelp(BOOL fEnterMode)
615 {
616 UNIMPLEMENTED;
617 return E_NOTIMPL;
618 }
619
620
621 // *** IDockingWindow methods ***
622 HRESULT STDMETHODCALLTYPE CExplorerBand::CloseDW(DWORD dwReserved)
623 {
624 // We do nothing, we don't have anything to save yet
625 TRACE("CloseDW called\n");
626 return S_OK;
627 }
628
629 HRESULT STDMETHODCALLTYPE CExplorerBand::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
630 {
631 /* Must return E_NOTIMPL according to MSDN */
632 return E_NOTIMPL;
633 }
634
635 HRESULT STDMETHODCALLTYPE CExplorerBand::ShowDW(BOOL fShow)
636 {
637 fVisible = fShow;
638 ShowWindow(fShow);
639 return S_OK;
640 }
641
642
643 // *** IDeskBand methods ***
644 HRESULT STDMETHODCALLTYPE CExplorerBand::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
645 {
646 if (!pdbi)
647 {
648 return E_INVALIDARG;
649 }
650 this->dwBandID = dwBandID;
651
652 if (pdbi->dwMask & DBIM_MINSIZE)
653 {
654 pdbi->ptMinSize.x = 200;
655 pdbi->ptMinSize.y = 30;
656 }
657
658 if (pdbi->dwMask & DBIM_MAXSIZE)
659 {
660 pdbi->ptMaxSize.y = -1;
661 }
662
663 if (pdbi->dwMask & DBIM_INTEGRAL)
664 {
665 pdbi->ptIntegral.y = 1;
666 }
667
668 if (pdbi->dwMask & DBIM_ACTUAL)
669 {
670 pdbi->ptActual.x = 200;
671 pdbi->ptActual.y = 30;
672 }
673
674 if (pdbi->dwMask & DBIM_TITLE)
675 {
676 lstrcpyW(pdbi->wszTitle, L"Explorer");
677 }
678
679 if (pdbi->dwMask & DBIM_MODEFLAGS)
680 {
681 pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT;
682 }
683
684 if (pdbi->dwMask & DBIM_BKCOLOR)
685 {
686 pdbi->dwMask &= ~DBIM_BKCOLOR;
687 }
688 return S_OK;
689 }
690
691
692 // *** IObjectWithSite methods ***
693 HRESULT STDMETHODCALLTYPE CExplorerBand::SetSite(IUnknown *pUnkSite)
694 {
695 HRESULT hr;
696 HWND parentWnd;
697
698 if (pUnkSite == pSite)
699 return S_OK;
700
701 TRACE("SetSite called \n");
702 if (!pUnkSite)
703 {
704 DestroyExplorerBand();
705 DestroyWindow();
706 m_hWnd = NULL;
707 }
708
709 if (pUnkSite != pSite)
710 {
711 pSite = NULL;
712 }
713
714 if(!pUnkSite)
715 return S_OK;
716
717 hr = IUnknown_GetWindow(pUnkSite, &parentWnd);
718 if (!SUCCEEDED(hr))
719 {
720 ERR("Could not get parent's window ! Status: %08lx\n", hr);
721 return E_INVALIDARG;
722 }
723
724 pSite = pUnkSite;
725
726 if (m_hWnd)
727 {
728 // Change its parent
729 SetParent(parentWnd);
730 }
731 else
732 {
733 HWND wnd = CreateWindow(WC_TREEVIEW, NULL,
734 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
735 0, 0, 0, 0, parentWnd, NULL, _AtlBaseModule.GetModuleInstance(), NULL);
736
737 // Subclass the window
738 SubclassWindow(wnd);
739
740 // Initialize our treeview now
741 InitializeExplorerBand();
742 RegisterDragDrop(m_hWnd, dynamic_cast<IDropTarget*>(this));
743 }
744 return S_OK;
745 }
746
747 HRESULT STDMETHODCALLTYPE CExplorerBand::GetSite(REFIID riid, void **ppvSite)
748 {
749 if (!ppvSite)
750 return E_POINTER;
751 *ppvSite = pSite;
752 return S_OK;
753 }
754
755
756 // *** IOleCommandTarget methods ***
757 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
758 {
759 UNIMPLEMENTED;
760 return E_NOTIMPL;
761 }
762
763 HRESULT STDMETHODCALLTYPE CExplorerBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
764 {
765 UNIMPLEMENTED;
766 return E_NOTIMPL;
767 }
768
769
770 // *** IServiceProvider methods ***
771 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
772 {
773 UNIMPLEMENTED;
774 return E_NOTIMPL;
775 }
776
777
778 // *** IInputObject methods ***
779 HRESULT STDMETHODCALLTYPE CExplorerBand::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
780 {
781 if (fActivate)
782 {
783 //SetFocus();
784 SetActiveWindow();
785 }
786 // TODO: handle message
787 if(lpMsg)
788 {
789 TranslateMessage(lpMsg);
790 DispatchMessage(lpMsg);
791 }
792 return S_OK;
793 }
794
795 HRESULT STDMETHODCALLTYPE CExplorerBand::HasFocusIO()
796 {
797 return bFocused ? S_OK : S_FALSE;
798 }
799
800 HRESULT STDMETHODCALLTYPE CExplorerBand::TranslateAcceleratorIO(LPMSG lpMsg)
801 {
802 if (lpMsg->hwnd == m_hWnd)
803 {
804 TranslateMessage(lpMsg);
805 DispatchMessage(lpMsg);
806 return S_OK;
807 }
808
809 return S_FALSE;
810 }
811
812 // *** IPersist methods ***
813 HRESULT STDMETHODCALLTYPE CExplorerBand::GetClassID(CLSID *pClassID)
814 {
815 if (!pClassID)
816 return E_POINTER;
817 memcpy(pClassID, &CLSID_ExplorerBand, sizeof(CLSID));
818 return S_OK;
819 }
820
821
822 // *** IPersistStream methods ***
823 HRESULT STDMETHODCALLTYPE CExplorerBand::IsDirty()
824 {
825 UNIMPLEMENTED;
826 return E_NOTIMPL;
827 }
828
829 HRESULT STDMETHODCALLTYPE CExplorerBand::Load(IStream *pStm)
830 {
831 UNIMPLEMENTED;
832 return E_NOTIMPL;
833 }
834
835 HRESULT STDMETHODCALLTYPE CExplorerBand::Save(IStream *pStm, BOOL fClearDirty)
836 {
837 UNIMPLEMENTED;
838 return E_NOTIMPL;
839 }
840
841 HRESULT STDMETHODCALLTYPE CExplorerBand::GetSizeMax(ULARGE_INTEGER *pcbSize)
842 {
843 UNIMPLEMENTED;
844 return E_NOTIMPL;
845 }
846
847
848 // *** IWinEventHandler methods ***
849 HRESULT STDMETHODCALLTYPE CExplorerBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
850 {
851 BOOL bHandled;
852 if (uMsg == WM_NOTIFY)
853 {
854 NMHDR *pNotifyHeader = (NMHDR*)lParam;
855 switch (pNotifyHeader->code)
856 {
857 case TVN_ITEMEXPANDING:
858 *theResult = OnTreeItemExpanding((LPNMTREEVIEW)lParam);
859 break;
860 case TVN_SELCHANGED:
861 OnSelectionChanged((LPNMTREEVIEW)lParam);
862 break;
863 case NM_RCLICK:
864 OnContextMenu(WM_CONTEXTMENU, (WPARAM)m_hWnd, GetMessagePos(), bHandled);
865 *theResult = 1;
866 break;
867 case TVN_BEGINLABELEDITW:
868 {
869 // TODO: put this in a function ? (mostly copypasta from CDefView)
870 DWORD dwAttr = SFGAO_CANRENAME;
871 LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam;
872 CComPtr<IShellFolder> pParent;
873 LPCITEMIDLIST pChild;
874 HRESULT hr;
875
876 *theResult = 1;
877 NodeInfo *info = GetNodeInfo(dispInfo->item.hItem);
878 if (!info)
879 return E_FAIL;
880 hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pChild);
881 if (!SUCCEEDED(hr) || !pParent.p)
882 return E_FAIL;
883
884 hr = pParent->GetAttributesOf(1, &pChild, &dwAttr);
885 if (SUCCEEDED(hr) && (dwAttr & SFGAO_CANRENAME))
886 *theResult = 0;
887 return S_OK;
888 }
889 case TVN_ENDLABELEDITW:
890 {
891 LPNMTVDISPINFO dispInfo = (LPNMTVDISPINFO)lParam;
892 NodeInfo *info = GetNodeInfo(dispInfo->item.hItem);
893 HRESULT hr;
894
895 *theResult = 0;
896 if (dispInfo->item.pszText)
897 {
898 LPITEMIDLIST pidlNew;
899 CComPtr<IShellFolder> pParent;
900 LPCITEMIDLIST pidlChild;
901
902 hr = SHBindToParent(info->absolutePidl, IID_PPV_ARG(IShellFolder, &pParent), &pidlChild);
903 if (!SUCCEEDED(hr) || !pParent.p)
904 return E_FAIL;
905
906 hr = pParent->SetNameOf(0, pidlChild, dispInfo->item.pszText, SHGDN_INFOLDER, &pidlNew);
907 if(SUCCEEDED(hr) && pidlNew)
908 {
909 CComPtr<IPersistFolder2> pPersist;
910 LPITEMIDLIST pidlParent, pidlNewAbs;
911
912 hr = pParent->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pPersist));
913 if(!SUCCEEDED(hr))
914 return E_FAIL;
915
916 hr = pPersist->GetCurFolder(&pidlParent);
917 if(!SUCCEEDED(hr))
918 return E_FAIL;
919 pidlNewAbs = ILCombine(pidlParent, pidlNew);
920
921 // Navigate to our new location
922 UpdateBrowser(pidlNewAbs);
923
924 ILFree(pidlNewAbs);
925 ILFree(pidlNew);
926 *theResult = 1;
927 }
928 return S_OK;
929 }
930 }
931 default:
932 break;
933 }
934 }
935 return S_OK;
936 }
937
938 HRESULT STDMETHODCALLTYPE CExplorerBand::IsWindowOwner(HWND hWnd)
939 {
940 return (hWnd == m_hWnd) ? S_OK : S_FALSE;
941 }
942
943 // *** IBandNavigate methods ***
944 HRESULT STDMETHODCALLTYPE CExplorerBand::Select(long paramC)
945 {
946 UNIMPLEMENTED;
947 return E_NOTIMPL;
948 }
949
950 // *** INamespaceProxy ***
951 HRESULT STDMETHODCALLTYPE CExplorerBand::GetNavigateTarget(long paramC, long param10, long param14)
952 {
953 UNIMPLEMENTED;
954 return E_NOTIMPL;
955 }
956
957 HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(long paramC)
958 {
959 UNIMPLEMENTED;
960 return E_NOTIMPL;
961 }
962
963 HRESULT STDMETHODCALLTYPE CExplorerBand::OnSelectionChanged(long paramC)
964 {
965 UNIMPLEMENTED;
966 return E_NOTIMPL;
967 }
968
969 HRESULT STDMETHODCALLTYPE CExplorerBand::RefreshFlags(long paramC, long param10, long param14)
970 {
971 UNIMPLEMENTED;
972 return E_NOTIMPL;
973 }
974
975 HRESULT STDMETHODCALLTYPE CExplorerBand::CacheItem(long paramC)
976 {
977 UNIMPLEMENTED;
978 return E_NOTIMPL;
979 }
980
981 // *** IDispatch methods ***
982 HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfoCount(UINT *pctinfo)
983 {
984 UNIMPLEMENTED;
985 return E_NOTIMPL;
986 }
987
988 HRESULT STDMETHODCALLTYPE CExplorerBand::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
989 {
990 UNIMPLEMENTED;
991 return E_NOTIMPL;
992 }
993
994 HRESULT STDMETHODCALLTYPE CExplorerBand::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
995 {
996 UNIMPLEMENTED;
997 return E_NOTIMPL;
998 }
999
1000 HRESULT STDMETHODCALLTYPE CExplorerBand::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1001 {
1002 switch (dispIdMember)
1003 {
1004 case DISPID_DOWNLOADCOMPLETE:
1005 case DISPID_NAVIGATECOMPLETE2:
1006 TRACE("DISPID_NAVIGATECOMPLETE2 received\n");
1007 NavigateToCurrentFolder();
1008 return S_OK;
1009 }
1010 TRACE("Unknown dispid requested: %08x\n", dispIdMember);
1011 return E_INVALIDARG;
1012 }
1013
1014 // *** IDropTarget methods ***
1015 HRESULT STDMETHODCALLTYPE CExplorerBand::DragEnter(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1016 {
1017 UNIMPLEMENTED;
1018 return E_NOTIMPL;
1019 }
1020
1021 HRESULT STDMETHODCALLTYPE CExplorerBand::DragOver(DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1022 {
1023 UNIMPLEMENTED;
1024 return E_NOTIMPL;
1025 }
1026
1027 HRESULT STDMETHODCALLTYPE CExplorerBand::DragLeave()
1028 {
1029 UNIMPLEMENTED;
1030 return E_NOTIMPL;
1031 }
1032
1033 HRESULT STDMETHODCALLTYPE CExplorerBand::Drop(IDataObject *pObj, DWORD glfKeyState, POINTL pt, DWORD *pdwEffect)
1034 {
1035 UNIMPLEMENTED;
1036 return E_NOTIMPL;
1037 }
1038
1039 // *** IDropSource methods ***
1040 HRESULT STDMETHODCALLTYPE CExplorerBand::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
1041 {
1042 UNIMPLEMENTED;
1043 return E_NOTIMPL;
1044 }
1045
1046 HRESULT STDMETHODCALLTYPE CExplorerBand::GiveFeedback(DWORD dwEffect)
1047 {
1048 UNIMPLEMENTED;
1049 return E_NOTIMPL;
1050 }