Sync with trunk r63343.
[reactos.git] / base / shell / rshell / CDesktopBrowser.cpp
1 /*
2 * Shell Desktop
3 *
4 * Copyright 2008 Thomas Bluemel
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "precomp.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(desktop);
24
25 BOOL WINAPI SetShellWindowEx(HWND, HWND);
26
27 #define SHDESK_TAG 0x4b534544
28
29 static const WCHAR szProgmanClassName [] = { 'P', 'r', 'o', 'g', 'm', 'a', 'n' };
30 static const WCHAR szProgmanWindowName [] = {
31 'P', 'r', 'o', 'g', 'r', 'a', 'm', ' ', 'M', 'a', 'n', 'a', 'g', 'e', 'r'
32 };
33
34 class CDesktopBrowser :
35 public CComObjectRootEx<CComMultiThreadModelNoCS>,
36 public IShellBrowser,
37 public ICommDlgBrowser,
38 public IServiceProvider
39 {
40 public:
41 DWORD Tag;
42 private:
43 HWND hWnd;
44 HWND hWndShellView;
45 HWND hWndDesktopListView;
46 CComPtr<IShellDesktopTray> ShellDesk;
47 CComPtr<IShellView> DesktopView;
48 IShellBrowser *DefaultShellBrowser;
49 LPITEMIDLIST pidlDesktopDirectory;
50 LPITEMIDLIST pidlDesktop;
51 public:
52 CDesktopBrowser();
53 ~CDesktopBrowser();
54 HRESULT Initialize(HWND hWndx, IShellDesktopTray *ShellDeskx);
55 HWND FindDesktopListView();
56 BOOL CreateDeskWnd();
57 HWND DesktopGetWindowControl(IN UINT id);
58 static LRESULT CALLBACK ProgmanWindowProc(IN HWND hwnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam);
59 static BOOL MessageLoop();
60
61 // *** IOleWindow methods ***
62 virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *lphwnd);
63 virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
64
65 // *** IShellBrowser methods ***
66 virtual HRESULT STDMETHODCALLTYPE InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
67 virtual HRESULT STDMETHODCALLTYPE SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject);
68 virtual HRESULT STDMETHODCALLTYPE RemoveMenusSB(HMENU hmenuShared);
69 virtual HRESULT STDMETHODCALLTYPE SetStatusTextSB(LPCOLESTR pszStatusText);
70 virtual HRESULT STDMETHODCALLTYPE EnableModelessSB(BOOL fEnable);
71 virtual HRESULT STDMETHODCALLTYPE TranslateAcceleratorSB(MSG *pmsg, WORD wID);
72 virtual HRESULT STDMETHODCALLTYPE BrowseObject(LPCITEMIDLIST pidl, UINT wFlags);
73 virtual HRESULT STDMETHODCALLTYPE GetViewStateStream(DWORD grfMode, IStream **ppStrm);
74 virtual HRESULT STDMETHODCALLTYPE GetControlWindow(UINT id, HWND *lphwnd);
75 virtual HRESULT STDMETHODCALLTYPE SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret);
76 virtual HRESULT STDMETHODCALLTYPE QueryActiveShellView(struct IShellView **ppshv);
77 virtual HRESULT STDMETHODCALLTYPE OnViewWindowActive(struct IShellView *ppshv);
78 virtual HRESULT STDMETHODCALLTYPE SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags);
79
80 // *** ICommDlgBrowser methods ***
81 virtual HRESULT STDMETHODCALLTYPE OnDefaultCommand(struct IShellView *ppshv);
82 virtual HRESULT STDMETHODCALLTYPE OnStateChange(struct IShellView *ppshv, ULONG uChange);
83 virtual HRESULT STDMETHODCALLTYPE IncludeObject(struct IShellView *ppshv, LPCITEMIDLIST pidl);
84
85 // *** IServiceProvider methods ***
86 virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppvObject);
87
88 BEGIN_COM_MAP(CDesktopBrowser)
89 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
90 COM_INTERFACE_ENTRY_IID(IID_IShellBrowser, IShellBrowser)
91 COM_INTERFACE_ENTRY_IID(IID_ICommDlgBrowser, ICommDlgBrowser)
92 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
93 END_COM_MAP()
94 };
95
96 CDesktopBrowser::CDesktopBrowser()
97 {
98 Tag = SHDESK_TAG;
99 hWnd = NULL;
100 hWndShellView = NULL;
101 hWndDesktopListView = NULL;
102 DefaultShellBrowser = NULL;
103 pidlDesktopDirectory = NULL;
104 pidlDesktop = NULL;
105 }
106
107 CDesktopBrowser::~CDesktopBrowser()
108 {
109 if (DesktopView.p != NULL)
110 {
111 if (hWndShellView != NULL)
112 DesktopView->DestroyViewWindow();
113
114 hWndShellView = NULL;
115 hWndDesktopListView = NULL;
116 }
117
118 if (pidlDesktopDirectory != NULL)
119 {
120 ILFree(pidlDesktopDirectory);
121 pidlDesktopDirectory = NULL;
122 }
123
124 if (pidlDesktop != NULL)
125 {
126 ILFree(pidlDesktop);
127 pidlDesktop = NULL;
128 }
129 }
130
131 HRESULT CDesktopBrowser::Initialize(HWND hWndx, IShellDesktopTray *ShellDeskx)
132 {
133 CComPtr<IShellFolder> psfDesktopFolder;
134 CSFV csfv;
135 HRESULT hRet;
136
137 hWnd = hWndx;
138 ShellDesk = ShellDeskx;
139 ShellDesk->AddRef();
140
141 pidlDesktopDirectory = SHCloneSpecialIDList(hWnd, CSIDL_DESKTOPDIRECTORY, FALSE);
142 hRet = SHGetSpecialFolderLocation(hWnd, CSIDL_DESKTOP, &pidlDesktop);
143 if (FAILED(hRet))
144 return hRet;
145
146 hRet = SHGetDesktopFolder(&psfDesktopFolder);
147 if (FAILED(hRet))
148 return hRet;
149
150 ZeroMemory(&csfv, sizeof(csfv));
151 csfv.cbSize = sizeof(csfv);
152 csfv.pshf = psfDesktopFolder;
153 csfv.psvOuter = NULL;
154
155 hRet = SHCreateShellFolderViewEx(&csfv, &DesktopView);
156
157 return hRet;
158 }
159
160 static CDesktopBrowser *SHDESK_Create(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
161 {
162 IShellDesktopTray *ShellDesk;
163 CComObject<CDesktopBrowser> *pThis;
164 HRESULT hRet;
165
166 ShellDesk = static_cast<IShellDesktopTray *>(lpCreateStruct->lpCreateParams);
167 if (ShellDesk == NULL)
168 {
169 WARN("No IShellDesk interface provided!");
170 return NULL;
171 }
172
173 pThis = new CComObject<CDesktopBrowser>;
174 if (pThis == NULL)
175 return NULL;
176 pThis->AddRef();
177
178 hRet = pThis->Initialize(hWnd, ShellDesk);
179 if (FAILED(hRet))
180 {
181 pThis->Release();
182 return NULL;
183 }
184
185 return pThis;
186 }
187
188 HWND CDesktopBrowser::FindDesktopListView()
189 {
190 return FindWindowExW(hWndShellView, NULL, WC_LISTVIEW, NULL);
191 }
192
193 BOOL CDesktopBrowser::CreateDeskWnd()
194 {
195 FOLDERSETTINGS fs;
196 RECT rcClient;
197 HRESULT hRet;
198
199 if (!GetClientRect(hWnd, &rcClient))
200 {
201 return FALSE;
202 }
203
204 fs.ViewMode = FVM_ICON;
205 fs.fFlags = FWF_DESKTOP | FWF_NOCLIENTEDGE | FWF_NOSCROLL | FWF_TRANSPARENT;
206 hRet = DesktopView->CreateViewWindow(NULL, &fs, static_cast<IShellBrowser *>(this), &rcClient, &hWndShellView);
207 if (!SUCCEEDED(hRet))
208 return FALSE;
209
210 SetShellWindowEx(hWnd, FindDesktopListView());
211
212 #if 1
213 /* A windows 8 specific hack */
214 ::ShowWindow(hWndShellView, SW_SHOW);
215 ::ShowWindow(FindDesktopListView(), SW_SHOW);
216 #endif
217
218 return TRUE;
219 }
220
221 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetWindow(HWND *phwnd)
222 {
223 if (hWnd != NULL)
224 {
225 *phwnd = hWnd;
226 return S_OK;
227 }
228
229 *phwnd = NULL;
230 return E_UNEXPECTED;
231 }
232
233 HRESULT STDMETHODCALLTYPE CDesktopBrowser::ContextSensitiveHelp(BOOL fEnterMode)
234 {
235 return E_NOTIMPL;
236 }
237
238 HRESULT STDMETHODCALLTYPE CDesktopBrowser::InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
239 {
240 return E_NOTIMPL;
241 }
242
243 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject)
244 {
245 return E_NOTIMPL;
246 }
247
248 HRESULT STDMETHODCALLTYPE CDesktopBrowser::RemoveMenusSB(HMENU hmenuShared)
249 {
250 return E_NOTIMPL;
251 }
252
253 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetStatusTextSB(LPCOLESTR lpszStatusText)
254 {
255 return E_NOTIMPL;
256 }
257
258 HRESULT STDMETHODCALLTYPE CDesktopBrowser::EnableModelessSB(BOOL fEnable)
259 {
260 return E_NOTIMPL;
261 }
262
263 HRESULT STDMETHODCALLTYPE CDesktopBrowser::TranslateAcceleratorSB(LPMSG lpmsg, WORD wID)
264 {
265 return S_FALSE;
266 }
267
268 typedef HRESULT(WINAPI *SH_OPEN_NEW_FRAME)(LPITEMIDLIST pidl, IUnknown *paramC, long param10, long param14);
269
270 HRESULT STDMETHODCALLTYPE CDesktopBrowser::BrowseObject(LPCITEMIDLIST pidl, UINT wFlags)
271 {
272 /* FIXME: Implement executing filebrowser.exe and somehow pass the pidl to it */
273
274 /* Returning failure here will make windows 7 and 8 to use the default file browser */
275 return E_FAIL;
276 }
277
278 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetViewStateStream(DWORD grfMode, IStream **ppStrm)
279 {
280 return E_NOTIMPL;
281 }
282
283 HWND CDesktopBrowser::DesktopGetWindowControl(IN UINT id)
284 {
285 switch (id)
286 {
287 case FCW_TOOLBAR:
288 case FCW_STATUS:
289 case FCW_TREE:
290 case FCW_PROGRESS:
291 return NULL;
292
293 default:
294 return NULL;
295 }
296
297 }
298
299 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetControlWindow(UINT id, HWND *lphwnd)
300 {
301 HWND hWnd;
302
303 hWnd = DesktopGetWindowControl(id);
304 if (hWnd != NULL)
305 {
306 *lphwnd = hWnd;
307 return S_OK;
308 }
309
310 *lphwnd = NULL;
311 return E_NOTIMPL;
312 }
313
314 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret)
315 {
316 HWND hWnd;
317
318 if (pret == NULL)
319 return E_POINTER;
320
321 hWnd = DesktopGetWindowControl(id);
322 if (hWnd != NULL)
323 {
324 *pret = SendMessageW(hWnd, uMsg, wParam, lParam);
325 return S_OK;
326 }
327
328 return E_NOTIMPL;
329 }
330
331 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryActiveShellView(IShellView **ppshv)
332 {
333 *ppshv = DesktopView;
334 if (DesktopView != NULL)
335 DesktopView->AddRef();
336
337 return S_OK;
338 }
339
340 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnViewWindowActive(IShellView *ppshv)
341 {
342 return E_NOTIMPL;
343 }
344
345 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags)
346 {
347 return E_NOTIMPL;
348 }
349
350 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnDefaultCommand(IShellView *ppshv)
351 {
352 return E_NOTIMPL;
353 }
354
355 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnStateChange(IShellView *ppshv, ULONG uChange)
356 {
357 return S_OK;
358 }
359
360 HRESULT STDMETHODCALLTYPE CDesktopBrowser::IncludeObject(IShellView *ppshv, LPCITEMIDLIST pidl)
361 {
362 return S_OK;
363 }
364
365 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryService(REFGUID guidService, REFIID riid, PVOID *ppv)
366 {
367 /* FIXME - handle guidService */
368 return QueryInterface(riid, ppv);
369 }
370
371 BOOL CDesktopBrowser::MessageLoop()
372 {
373 MSG Msg;
374 BOOL bRet;
375
376 while ((bRet = GetMessageW(&Msg, NULL, 0, 0)) != 0)
377 {
378 if (bRet != -1)
379 {
380 TranslateMessage(&Msg);
381 DispatchMessageW(&Msg);
382 }
383 }
384
385 return TRUE;
386 }
387
388 LRESULT CALLBACK CDesktopBrowser::ProgmanWindowProc(IN HWND hwnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
389 {
390 CDesktopBrowser *pThis = NULL;
391 LRESULT Ret = FALSE;
392
393 if (uMsg != WM_NCCREATE)
394 {
395 pThis = reinterpret_cast<CDesktopBrowser*>(GetWindowLongPtrW(hwnd, 0));
396 if (pThis == NULL)
397 goto DefMsgHandler;
398 }
399
400 if (pThis != NULL || uMsg == WM_NCCREATE)
401 {
402 switch (uMsg)
403 {
404 case WM_ERASEBKGND:
405 return (LRESULT) PaintDesktop(reinterpret_cast<HDC>(wParam));
406
407 case WM_GETISHELLBROWSER:
408 Ret = reinterpret_cast<LRESULT>(static_cast<IShellBrowser *>(pThis));
409 break;
410
411 case WM_SIZE:
412 if (wParam == SIZE_MINIMIZED)
413 {
414 /* Hey, we're the desktop!!! */
415 ShowWindow(hwnd,
416 SW_RESTORE);
417 }
418 else
419 {
420 RECT rcDesktop;
421
422 rcDesktop.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
423 rcDesktop.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
424 rcDesktop.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
425 rcDesktop.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
426
427 /* FIXME: Update work area */
428 }
429 break;
430
431 case WM_SYSCOLORCHANGE:
432 case WM_SETTINGCHANGE:
433 {
434 if (uMsg == WM_SYSCOLORCHANGE || wParam == SPI_SETDESKWALLPAPER || wParam == 0)
435 {
436 if (pThis->hWndShellView != NULL)
437 {
438 /* Forward the message */
439 SendMessageW(pThis->hWndShellView,
440 uMsg,
441 wParam,
442 lParam);
443 }
444 }
445 break;
446 }
447
448 case WM_CREATE:
449 {
450 pThis->ShellDesk->RegisterDesktopWindow(pThis->hWnd);
451
452 if (!pThis->CreateDeskWnd())
453 WARN("Could not create the desktop view control!\n");
454 break;
455 }
456
457 case WM_NCCREATE:
458 {
459 LPCREATESTRUCT CreateStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
460 pThis = SHDESK_Create(hwnd, CreateStruct);
461 if (pThis == NULL)
462 {
463 WARN("Failed to create desktop structure\n");
464 break;
465 }
466
467 SetWindowLongPtrW(hwnd,
468 0,
469 reinterpret_cast<LONG_PTR>(pThis));
470 Ret = TRUE;
471 break;
472 }
473
474 case WM_NCDESTROY:
475 {
476 pThis->Release();
477 break;
478 }
479
480 default:
481 DefMsgHandler :
482 Ret = DefWindowProcW(hwnd, uMsg, wParam, lParam);
483 break;
484 }
485 }
486
487 return Ret;
488 }
489
490 static BOOL
491 RegisterProgmanWindowClass(VOID)
492 {
493 WNDCLASSW wcProgman;
494
495 wcProgman.style = CS_DBLCLKS;
496 wcProgman.lpfnWndProc = CDesktopBrowser::ProgmanWindowProc;
497 wcProgman.cbClsExtra = 0;
498 wcProgman.cbWndExtra = sizeof(CDesktopBrowser *);
499 wcProgman.hInstance = shell32_hInstance;
500 wcProgman.hIcon = NULL;
501 wcProgman.hCursor = LoadCursorW(NULL, IDC_ARROW);
502 wcProgman.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);
503 wcProgman.lpszMenuName = NULL;
504 wcProgman.lpszClassName = szProgmanClassName;
505
506 return RegisterClassW(&wcProgman) != 0;
507 }
508
509
510 /*************************************************************************
511 * SHCreateDesktop [SHELL32.200]
512 *
513 */
514 HANDLE WINAPI SHCreateDesktop(IShellDesktopTray *ShellDesk)
515 {
516 HWND hWndDesk;
517 RECT rcDesk;
518
519 if (ShellDesk == NULL)
520 {
521 SetLastError(ERROR_INVALID_PARAMETER);
522 return NULL;
523 }
524
525 if (RegisterProgmanWindowClass() == 0)
526 {
527 WARN("Failed to register the Progman window class!\n");
528 return NULL;
529 }
530
531 rcDesk.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
532 rcDesk.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
533 rcDesk.right = rcDesk.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
534 rcDesk.bottom = rcDesk.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
535
536 if (IsRectEmpty(&rcDesk))
537 {
538 rcDesk.left = rcDesk.top = 0;
539 rcDesk.right = GetSystemMetrics(SM_CXSCREEN);
540 rcDesk.bottom = GetSystemMetrics(SM_CYSCREEN);
541 }
542
543 hWndDesk = CreateWindowExW(0, szProgmanClassName, szProgmanWindowName,
544 WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
545 rcDesk.left, rcDesk.top, rcDesk.right, rcDesk.bottom,
546 NULL, NULL, shell32_hInstance, reinterpret_cast<LPVOID>(ShellDesk));
547 if (hWndDesk != NULL)
548 return (HANDLE) GetWindowLongPtrW(hWndDesk, 0);
549
550 return NULL;
551 }
552
553 /*************************************************************************
554 * SHCreateDesktop [SHELL32.201]
555 *
556 */
557 BOOL WINAPI SHDesktopMessageLoop(HANDLE hDesktop)
558 {
559 CDesktopBrowser *Desk = reinterpret_cast<CDesktopBrowser *>(hDesktop);
560
561 if (Desk == NULL || Desk->Tag != SHDESK_TAG)
562 {
563 SetLastError(ERROR_INVALID_PARAMETER);
564 return FALSE;
565 }
566
567 return Desk->MessageLoop();
568 }