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