* Sync up to trunk head (r65095).
[reactos.git] / dll / win32 / shell32 / desktop.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 CComPtr<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 CComPtr<IShellDesktopTray> ShellDesk;
163 CComObject<CDesktopBrowser> *pThis;
164 HRESULT hRet;
165
166 ShellDesk = (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, (IShellBrowser *)this, &rcClient, &hWndShellView);
207 if (!SUCCEEDED(hRet))
208 return FALSE;
209
210 SetShellWindowEx(hWnd, FindDesktopListView());
211
212 return TRUE;
213 }
214
215 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetWindow(HWND *phwnd)
216 {
217 if (hWnd != NULL)
218 {
219 *phwnd = hWnd;
220 return S_OK;
221 }
222
223 *phwnd = NULL;
224 return E_UNEXPECTED;
225 }
226
227 HRESULT STDMETHODCALLTYPE CDesktopBrowser::ContextSensitiveHelp(BOOL fEnterMode)
228 {
229 return E_NOTIMPL;
230 }
231
232 HRESULT STDMETHODCALLTYPE CDesktopBrowser::InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
233 {
234 return E_NOTIMPL;
235 }
236
237 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject)
238 {
239 return E_NOTIMPL;
240 }
241
242 HRESULT STDMETHODCALLTYPE CDesktopBrowser::RemoveMenusSB(HMENU hmenuShared)
243 {
244 return E_NOTIMPL;
245 }
246
247 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetStatusTextSB(LPCOLESTR lpszStatusText)
248 {
249 return E_NOTIMPL;
250 }
251
252 HRESULT STDMETHODCALLTYPE CDesktopBrowser::EnableModelessSB(BOOL fEnable)
253 {
254 return E_NOTIMPL;
255 }
256
257 HRESULT STDMETHODCALLTYPE CDesktopBrowser::TranslateAcceleratorSB(LPMSG lpmsg, WORD wID)
258 {
259 return S_FALSE;
260 }
261
262 HRESULT STDMETHODCALLTYPE CDesktopBrowser::BrowseObject(LPCITEMIDLIST pidl, UINT wFlags)
263 {
264 /*
265 * We should use IShellWindows interface here in order to attempt to
266 * find an open shell window that shows the requested pidl and activate it
267 */
268
269 return SHOpenNewFrame((LPITEMIDLIST)pidl, NULL, 0, 0);
270 }
271
272 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetViewStateStream(DWORD grfMode, IStream **ppStrm)
273 {
274 return E_NOTIMPL;
275 }
276
277 HWND CDesktopBrowser::DesktopGetWindowControl(IN UINT id)
278 {
279 switch (id)
280 {
281 case FCW_TOOLBAR:
282 case FCW_STATUS:
283 case FCW_TREE:
284 case FCW_PROGRESS:
285 return NULL;
286
287 default:
288 return NULL;
289 }
290
291 }
292
293 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetControlWindow(UINT id, HWND *lphwnd)
294 {
295 HWND hWnd;
296
297 hWnd = DesktopGetWindowControl(id);
298 if (hWnd != NULL)
299 {
300 *lphwnd = hWnd;
301 return S_OK;
302 }
303
304 *lphwnd = NULL;
305 return E_NOTIMPL;
306 }
307
308 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret)
309 {
310 HWND hWnd;
311
312 if (pret == NULL)
313 return E_POINTER;
314
315 hWnd = DesktopGetWindowControl(id);
316 if (hWnd != NULL)
317 {
318 *pret = SendMessageW(hWnd,
319 uMsg,
320 wParam,
321 lParam);
322 return S_OK;
323 }
324
325 return E_NOTIMPL;
326 }
327
328 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryActiveShellView(IShellView **ppshv)
329 {
330 *ppshv = DesktopView;
331 if (DesktopView != NULL)
332 DesktopView->AddRef();
333
334 return S_OK;
335 }
336
337 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnViewWindowActive(IShellView *ppshv)
338 {
339 return E_NOTIMPL;
340 }
341
342 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags)
343 {
344 return E_NOTIMPL;
345 }
346
347 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnDefaultCommand(IShellView *ppshv)
348 {
349 return E_NOTIMPL;
350 }
351
352 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnStateChange(IShellView *ppshv, ULONG uChange)
353 {
354 return S_OK;
355 }
356
357 HRESULT STDMETHODCALLTYPE CDesktopBrowser::IncludeObject(IShellView *ppshv, LPCITEMIDLIST pidl)
358 {
359 return S_OK;
360 }
361
362 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryService(REFGUID guidService, REFIID riid, PVOID *ppv)
363 {
364 /* FIXME - handle guidService */
365 return QueryInterface(riid, ppv);
366 }
367
368 BOOL CDesktopBrowser::MessageLoop()
369 {
370 MSG Msg;
371 BOOL bRet;
372
373 while ((bRet = GetMessageW(&Msg, NULL, 0, 0)) != 0)
374 {
375 if (bRet != -1)
376 {
377 TranslateMessage(&Msg);
378 DispatchMessageW(&Msg);
379 }
380 }
381
382 return TRUE;
383 }
384
385 LRESULT CALLBACK CDesktopBrowser::ProgmanWindowProc(IN HWND hwnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
386 {
387 CDesktopBrowser *pThis = NULL;
388 LRESULT Ret = FALSE;
389
390 if (uMsg != WM_NCCREATE)
391 {
392 pThis = reinterpret_cast<CDesktopBrowser *>(GetWindowLongPtrW(hwnd, 0));
393 if (pThis == NULL)
394 goto DefMsgHandler;
395 }
396
397 if (pThis != NULL || uMsg == WM_NCCREATE)
398 {
399 switch (uMsg)
400 {
401 case WM_ERASEBKGND:
402 return (LRESULT)PaintDesktop((HDC)wParam);
403
404 case WM_GETISHELLBROWSER:
405 Ret = (LRESULT)((IShellBrowser *)pThis);
406 break;
407
408 case WM_SIZE:
409 if (wParam == SIZE_MINIMIZED)
410 {
411 /* Hey, we're the desktop!!! */
412 ShowWindow(hwnd,
413 SW_RESTORE);
414 }
415 else
416 {
417 RECT rcDesktop;
418
419 rcDesktop.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
420 rcDesktop.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
421 rcDesktop.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
422 rcDesktop.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
423
424 /* FIXME: Update work area */
425 DBG_UNREFERENCED_LOCAL_VARIABLE(rcDesktop);
426 }
427 break;
428
429 case WM_SYSCOLORCHANGE:
430 case WM_SETTINGCHANGE:
431 {
432 if (uMsg == WM_SYSCOLORCHANGE || wParam == SPI_SETDESKWALLPAPER || wParam == 0)
433 {
434 if (pThis->hWndShellView != NULL)
435 {
436 /* Forward the message */
437 SendMessageW(pThis->hWndShellView,
438 uMsg,
439 wParam,
440 lParam);
441 }
442 }
443 break;
444 }
445
446 case WM_CREATE:
447 {
448 pThis->ShellDesk->RegisterDesktopWindow(pThis->hWnd);
449
450 if (!pThis->CreateDeskWnd())
451 WARN("Could not create the desktop view control!\n");
452 break;
453 }
454
455 case WM_NCCREATE:
456 {
457 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
458 pThis = SHDESK_Create(hwnd, CreateStruct);
459 if (pThis == NULL)
460 {
461 WARN("Failed to create desktop structure\n");
462 break;
463 }
464
465 SetWindowLongPtrW(hwnd,
466 0,
467 (LONG_PTR)pThis);
468 Ret = TRUE;
469 break;
470 }
471
472 case WM_NCDESTROY:
473 {
474 pThis->Release();
475 break;
476 }
477
478 case WM_EXPLORER_OPEN_NEW_WINDOW:
479 DbgPrint("Proxy Desktop message 1035 received.\n");
480 SHOnCWMCommandLine((HANDLE)lParam);
481 break;
482
483 default:
484 DefMsgHandler:
485 Ret = DefWindowProcW(hwnd, uMsg, wParam, lParam);
486 break;
487 }
488 }
489
490 return Ret;
491 }
492
493 static BOOL
494 RegisterProgmanWindowClass(VOID)
495 {
496 WNDCLASSW wcProgman;
497
498 wcProgman.style = CS_DBLCLKS;
499 wcProgman.lpfnWndProc = CDesktopBrowser::ProgmanWindowProc;
500 wcProgman.cbClsExtra = 0;
501 wcProgman.cbWndExtra = sizeof(CDesktopBrowser *);
502 wcProgman.hInstance = shell32_hInstance;
503 wcProgman.hIcon = NULL;
504 wcProgman.hCursor = LoadCursorW(NULL, IDC_ARROW);
505 wcProgman.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
506 wcProgman.lpszMenuName = NULL;
507 wcProgman.lpszClassName = szProgmanClassName;
508
509 return RegisterClassW(&wcProgman) != 0;
510 }
511
512
513 /*************************************************************************
514 * SHCreateDesktop [SHELL32.200]
515 *
516 */
517 HANDLE WINAPI SHCreateDesktop(IShellDesktopTray *ShellDesk)
518 {
519 HWND hWndDesk;
520 RECT rcDesk;
521
522 if (ShellDesk == NULL)
523 {
524 SetLastError(ERROR_INVALID_PARAMETER);
525 return NULL;
526 }
527
528 if (RegisterProgmanWindowClass() == 0)
529 {
530 WARN("Failed to register the Progman window class!\n");
531 return NULL;
532 }
533
534 rcDesk.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
535 rcDesk.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
536 rcDesk.right = rcDesk.left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
537 rcDesk.bottom = rcDesk.top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
538
539 if (IsRectEmpty(&rcDesk))
540 {
541 rcDesk.left = rcDesk.top = 0;
542 rcDesk.right = GetSystemMetrics(SM_CXSCREEN);
543 rcDesk.bottom = GetSystemMetrics(SM_CYSCREEN);
544 }
545
546 hWndDesk = CreateWindowExW(0, szProgmanClassName, szProgmanWindowName,
547 WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
548 rcDesk.left, rcDesk.top, rcDesk.right, rcDesk.bottom,
549 NULL, NULL, shell32_hInstance, (LPVOID)ShellDesk);
550 if (hWndDesk != NULL)
551 return (HANDLE)GetWindowLongPtrW(hWndDesk, 0);
552
553 return NULL;
554 }
555
556 /*************************************************************************
557 * SHCreateDesktop [SHELL32.201]
558 *
559 */
560 BOOL WINAPI SHDesktopMessageLoop(HANDLE hDesktop)
561 {
562 CDesktopBrowser *Desk = static_cast<CDesktopBrowser *>(hDesktop);
563
564 if (Desk == NULL || Desk->Tag != SHDESK_TAG)
565 {
566 SetLastError(ERROR_INVALID_PARAMETER);
567 return FALSE;
568 }
569
570 return Desk->MessageLoop();
571 }