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 "shelldesktop.h"
22 
23 // Support for multiple monitors is disabled till LVM_SETWORKAREAS gets implemented
24 #ifdef MULTIMONITOR_SUPPORT
25 #include <atlcoll.h>
26 #endif
27 
28 
29 
30 WINE_DEFAULT_DEBUG_CHANNEL(desktop);
31 
32 static const WCHAR szProgmanClassName[]  = L"Progman";
33 static const WCHAR szProgmanWindowName[] = L"Program Manager";
34 
35 class CDesktopBrowser :
36     public CWindowImpl<CDesktopBrowser, CWindow, CFrameWinTraits>,
37     public CComObjectRootEx<CComMultiThreadModelNoCS>,
38     public IShellBrowser,
39     public IServiceProvider
40 {
41 private:
42     HACCEL m_hAccel;
43     HWND m_hWndShellView;
44     CComPtr<IShellDesktopTray> m_Tray;
45     CComPtr<IShellView>        m_ShellView;
46 
47     LRESULT _NotifyTray(UINT uMsg, WPARAM wParam, LPARAM lParam);
48     HRESULT _Resize();
49 
50 public:
51     CDesktopBrowser();
52     ~CDesktopBrowser();
53     HRESULT Initialize(IShellDesktopTray *ShellDeskx);
54 
55     // *** IOleWindow methods ***
56     virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *lphwnd);
57     virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
58 
59     // *** IShellBrowser methods ***
60     virtual HRESULT STDMETHODCALLTYPE InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
61     virtual HRESULT STDMETHODCALLTYPE SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject);
62     virtual HRESULT STDMETHODCALLTYPE RemoveMenusSB(HMENU hmenuShared);
63     virtual HRESULT STDMETHODCALLTYPE SetStatusTextSB(LPCOLESTR pszStatusText);
64     virtual HRESULT STDMETHODCALLTYPE EnableModelessSB(BOOL fEnable);
65     virtual HRESULT STDMETHODCALLTYPE TranslateAcceleratorSB(MSG *pmsg, WORD wID);
66     virtual HRESULT STDMETHODCALLTYPE BrowseObject(LPCITEMIDLIST pidl, UINT wFlags);
67     virtual HRESULT STDMETHODCALLTYPE GetViewStateStream(DWORD grfMode, IStream **ppStrm);
68     virtual HRESULT STDMETHODCALLTYPE GetControlWindow(UINT id, HWND *lphwnd);
69     virtual HRESULT STDMETHODCALLTYPE SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret);
70     virtual HRESULT STDMETHODCALLTYPE QueryActiveShellView(struct IShellView **ppshv);
71     virtual HRESULT STDMETHODCALLTYPE OnViewWindowActive(struct IShellView *ppshv);
72     virtual HRESULT STDMETHODCALLTYPE SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags);
73 
74     // *** IServiceProvider methods ***
75     virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppvObject);
76 
77     // message handlers
78     LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
79     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
80     LRESULT OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
81     LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
82     LRESULT OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
83     LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
84     LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
85 
86 DECLARE_WND_CLASS_EX(szProgmanClassName, CS_DBLCLKS, COLOR_DESKTOP)
87 
88 BEGIN_MSG_MAP(CBaseBar)
89     MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
90     MESSAGE_HANDLER(WM_SIZE, OnSize)
91     MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSettingChange)
92     MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
93     MESSAGE_HANDLER(WM_CLOSE, OnClose)
94     MESSAGE_HANDLER(WM_EXPLORER_OPEN_NEW_WINDOW, OnOpenNewWindow)
95     MESSAGE_HANDLER(WM_COMMAND, OnCommand)
96     MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
97 END_MSG_MAP()
98 
99 BEGIN_COM_MAP(CDesktopBrowser)
100     COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
101     COM_INTERFACE_ENTRY_IID(IID_IShellBrowser, IShellBrowser)
102     COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
103 END_COM_MAP()
104 };
105 
106 CDesktopBrowser::CDesktopBrowser():
107     m_hAccel(NULL),
108     m_hWndShellView(NULL)
109 {
110 }
111 
112 CDesktopBrowser::~CDesktopBrowser()
113 {
114     if (m_ShellView.p != NULL && m_hWndShellView != NULL)
115     {
116         m_ShellView->DestroyViewWindow();
117     }
118 }
119 
120 #ifdef MULTIMONITOR_SUPPORT
121 BOOL CALLBACK MonitorEnumProc(
122   _In_ HMONITOR hMonitor,
123   _In_ HDC      hdcMonitor,
124   _In_ LPRECT   lprcMonitor,
125   _In_ LPARAM   dwData
126 )
127 {
128     CAtlList<RECT> *list = (CAtlList<RECT>*)dwData;
129     MONITORINFO MonitorInfo;
130     MonitorInfo.cbSize = sizeof(MonitorInfo);
131     if (::GetMonitorInfoW(hMonitor, &MonitorInfo))
132     {
133         list->AddTail(MonitorInfo.rcWork);
134     }
135 
136     return TRUE;
137 }
138 #endif
139 
140 HRESULT CDesktopBrowser::_Resize()
141 {
142     RECT rcNewSize;
143 
144 #ifdef MULTIMONITOR_SUPPORT
145 
146     UINT cMonitors = GetSystemMetrics(SM_CMONITORS);
147     if (cMonitors == 1)
148     {
149         SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcNewSize, 0);
150     }
151     else
152     {
153         SetRect(&rcNewSize,
154                 GetSystemMetrics(SM_XVIRTUALSCREEN),
155                 GetSystemMetrics(SM_YVIRTUALSCREEN),
156                 GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN),
157                 GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN));
158     }
159 
160     ::MoveWindow(m_hWnd, rcNewSize.left, rcNewSize.top, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE);
161     ::MoveWindow(m_hWndShellView, 0, 0, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE);
162 
163     if (cMonitors != 1)
164     {
165         CAtlList<RECT> list;
166         EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&list);
167         RECT* prcWorkAreas = new RECT[list.GetCount()];
168         int i = 0;
169         for (POSITION it = list.GetHeadPosition(); it; list.GetNext(it))
170             prcWorkAreas[i++] = list.GetAt(it);
171 
172         HWND hwndListView = FindWindowExW(m_hWndShellView, NULL, WC_LISTVIEW, NULL);
173 
174         ::SendMessageW(hwndListView, LVM_SETWORKAREAS , i, (LPARAM)prcWorkAreas);
175     }
176 
177 #else
178      SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcNewSize, 0);
179     ::MoveWindow(m_hWnd, rcNewSize.left, rcNewSize.top, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE);
180     ::MoveWindow(m_hWndShellView, 0, 0, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE);
181 
182 #endif
183     return S_OK;
184 }
185 
186 HRESULT CDesktopBrowser::Initialize(IShellDesktopTray *ShellDesk)
187 {
188     CComPtr<IShellFolder> psfDesktop;
189     HRESULT hRet;
190     hRet = SHGetDesktopFolder(&psfDesktop);
191     if (FAILED_UNEXPECTEDLY(hRet))
192         return hRet;
193 
194     m_Tray = ShellDesk;
195 
196     Create(NULL, NULL, szProgmanWindowName, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_TOOLWINDOW);
197     if (!m_hWnd)
198         return E_FAIL;
199 
200     CSFV csfv = {sizeof(CSFV), psfDesktop};
201     hRet = SHCreateShellFolderViewEx(&csfv, &m_ShellView);
202     if (FAILED_UNEXPECTEDLY(hRet))
203         return hRet;
204 
205     m_Tray->RegisterDesktopWindow(m_hWnd);
206     if (FAILED_UNEXPECTEDLY(hRet))
207         return hRet;
208 
209     FOLDERSETTINGS fs;
210     RECT rcShellView = {0,0,0,0};
211     fs.ViewMode = FVM_ICON;
212     fs.fFlags = FWF_DESKTOP | FWF_NOCLIENTEDGE | FWF_NOSCROLL | FWF_TRANSPARENT;
213     hRet = m_ShellView->CreateViewWindow(NULL, &fs, (IShellBrowser *)this, &rcShellView, &m_hWndShellView);
214     if (FAILED_UNEXPECTEDLY(hRet))
215         return hRet;
216 
217     _Resize();
218 
219     HWND hwndListView = FindWindowExW(m_hWndShellView, NULL, WC_LISTVIEW, NULL);
220     SetShellWindowEx(m_hWnd, hwndListView);
221 
222     m_hAccel = LoadAcceleratorsW(shell32_hInstance, MAKEINTRESOURCEW(IDA_DESKBROWSER));
223 
224 #if 1
225     /* A Windows8+ specific hack */
226     ::ShowWindow(m_hWndShellView, SW_SHOW);
227     ::ShowWindow(hwndListView, SW_SHOW);
228 #endif
229     ShowWindow(SW_SHOW);
230     UpdateWindow();
231 
232     return hRet;
233 }
234 
235 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetWindow(HWND *lphwnd)
236 {
237     if (lphwnd == NULL)
238         return E_POINTER;
239     *lphwnd = m_hWnd;
240     return S_OK;
241 }
242 
243 HRESULT STDMETHODCALLTYPE CDesktopBrowser::ContextSensitiveHelp(BOOL fEnterMode)
244 {
245     return E_NOTIMPL;
246 }
247 
248 HRESULT STDMETHODCALLTYPE CDesktopBrowser::InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
249 {
250     return E_NOTIMPL;
251 }
252 
253 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject)
254 {
255     return E_NOTIMPL;
256 }
257 
258 HRESULT STDMETHODCALLTYPE CDesktopBrowser::RemoveMenusSB(HMENU hmenuShared)
259 {
260     return E_NOTIMPL;
261 }
262 
263 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetStatusTextSB(LPCOLESTR lpszStatusText)
264 {
265     return E_NOTIMPL;
266 }
267 
268 HRESULT STDMETHODCALLTYPE CDesktopBrowser::EnableModelessSB(BOOL fEnable)
269 {
270     return E_NOTIMPL;
271 }
272 
273 HRESULT STDMETHODCALLTYPE CDesktopBrowser::TranslateAcceleratorSB(LPMSG lpmsg, WORD wID)
274 {
275     if (!::TranslateAcceleratorW(m_hWnd, m_hAccel, lpmsg))
276         return S_FALSE;
277     return S_OK;
278 }
279 
280 HRESULT STDMETHODCALLTYPE CDesktopBrowser::BrowseObject(LPCITEMIDLIST pidl, UINT wFlags)
281 {
282     /*
283      * We should use IShellWindows interface here in order to attempt to
284      * find an open shell window that shows the requested pidl and activate it
285      */
286 
287     DWORD dwFlags = ((wFlags & SBSP_EXPLOREMODE) != 0) ? SH_EXPLORER_CMDLINE_FLAG_E : 0;
288     return SHOpenNewFrame(ILClone(pidl), NULL, 0, dwFlags);
289 }
290 
291 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetViewStateStream(DWORD grfMode, IStream **ppStrm)
292 {
293     return E_NOTIMPL;
294 }
295 
296 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetControlWindow(UINT id, HWND *lphwnd)
297 {
298     if (lphwnd == NULL)
299         return E_POINTER;
300     return E_NOTIMPL;
301 }
302 
303 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret)
304 {
305     if (pret == NULL)
306         return E_POINTER;
307     return E_NOTIMPL;
308 }
309 
310 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryActiveShellView(IShellView **ppshv)
311 {
312     if (ppshv == NULL)
313         return E_POINTER;
314     *ppshv = m_ShellView;
315     if (m_ShellView != NULL)
316         m_ShellView->AddRef();
317 
318     return S_OK;
319 }
320 
321 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnViewWindowActive(IShellView *ppshv)
322 {
323     return E_NOTIMPL;
324 }
325 
326 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags)
327 {
328     return E_NOTIMPL;
329 }
330 
331 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryService(REFGUID guidService, REFIID riid, PVOID *ppv)
332 {
333     /* FIXME - handle guidService */
334     return QueryInterface(riid, ppv);
335 }
336 
337 LRESULT CDesktopBrowser::_NotifyTray(UINT uMsg, WPARAM wParam, LPARAM lParam)
338 {
339     HWND hWndTray;
340     HRESULT hRet;
341 
342     hRet = m_Tray->GetTrayWindow(&hWndTray);
343     if (SUCCEEDED(hRet))
344         ::PostMessageW(hWndTray, uMsg, wParam, lParam);
345 
346     return 0;
347 }
348 
349 LRESULT CDesktopBrowser::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
350 {
351     switch (LOWORD(wParam))
352     {
353         case FCIDM_DESKBROWSER_CLOSE:
354             return _NotifyTray(TWM_DOEXITWINDOWS, 0, 0);
355         case FCIDM_DESKBROWSER_FOCUS:
356             if (GetKeyState(VK_SHIFT))
357                 return _NotifyTray(TWM_CYCLEFOCUS, 1, 0xFFFFFFFF);
358             else
359                 return _NotifyTray(TWM_CYCLEFOCUS, 1, 1);
360         case FCIDM_DESKBROWSER_SEARCH:
361             SHFindFiles(NULL, NULL);
362             break;
363         case FCIDM_DESKBROWSER_REFRESH:
364             if (m_ShellView)
365                 m_ShellView->Refresh();
366             break;
367     }
368 
369     return 0;
370 }
371 
372 
373 LRESULT CDesktopBrowser::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
374 {
375     return (LRESULT)PaintDesktop((HDC)wParam);
376 }
377 
378 LRESULT CDesktopBrowser::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
379 {
380     if (wParam == SIZE_MINIMIZED)
381     {
382         /* Hey, we're the desktop!!! */
383         ::ShowWindow(m_hWnd, SW_RESTORE);
384     }
385 
386     ::InvalidateRect(m_hWndShellView, NULL, TRUE);
387 
388     return 0;
389 }
390 
391 LRESULT CDesktopBrowser::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
392 {
393     LPVOID lpEnvironment;
394     RegenerateUserEnvironment(&lpEnvironment, TRUE);
395 
396     if (m_hWndShellView)
397     {
398         /* Forward the message */
399         SendMessageW(m_hWndShellView, uMsg, wParam, lParam);
400     }
401 
402     if (uMsg == WM_SETTINGCHANGE && wParam == SPI_SETWORKAREA && m_hWndShellView != NULL)
403     {
404         _Resize();
405     }
406 
407     return 0;
408 }
409 
410 LRESULT CDesktopBrowser::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
411 {
412     return _NotifyTray(TWM_DOEXITWINDOWS, 0, 0);
413 }
414 
415 LRESULT CDesktopBrowser::OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
416 {
417     TRACE("Proxy Desktop message 1035 received.\n");
418     SHOnCWMCommandLine((HANDLE)lParam);
419     return 0;
420 }
421 
422 LRESULT CDesktopBrowser::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
423 {
424     ::SetFocus(m_hWndShellView);
425     return 0;
426 }
427 
428 HRESULT CDesktopBrowser_CreateInstance(IShellDesktopTray *Tray, REFIID riid, void **ppv)
429 {
430     return ShellObjectCreatorInit<CDesktopBrowser, IShellDesktopTray*>(Tray, riid, ppv);
431 }
432 
433 /*************************************************************************
434  * SHCreateDesktop            [SHELL32.200]
435  *
436  */
437 HANDLE WINAPI SHCreateDesktop(IShellDesktopTray *Tray)
438 {
439     if (Tray == NULL)
440     {
441         SetLastError(ERROR_INVALID_PARAMETER);
442         return NULL;
443     }
444 
445     CComPtr<IShellBrowser> Browser;
446     HRESULT hr = CDesktopBrowser_CreateInstance(Tray, IID_PPV_ARG(IShellBrowser, &Browser));
447     if (FAILED_UNEXPECTEDLY(hr))
448         return NULL;
449 
450     return static_cast<HANDLE>(Browser.Detach());
451 }
452 
453 /*************************************************************************
454  * SHCreateDesktop            [SHELL32.201]
455  *
456  */
457 BOOL WINAPI SHDesktopMessageLoop(HANDLE hDesktop)
458 {
459     if (hDesktop == NULL)
460     {
461         SetLastError(ERROR_INVALID_PARAMETER);
462         return FALSE;
463     }
464 
465     MSG Msg;
466     BOOL bRet;
467 
468     CComPtr<IShellBrowser> browser;
469     CComPtr<IShellView> shellView;
470 
471     browser.Attach(static_cast<IShellBrowser*>(hDesktop));
472     HRESULT hr = browser->QueryActiveShellView(&shellView);
473     if (FAILED_UNEXPECTEDLY(hr))
474         return FALSE;
475 
476     while ((bRet = GetMessageW(&Msg, NULL, 0, 0)) != 0)
477     {
478         if (bRet != -1)
479         {
480             if (shellView->TranslateAcceleratorW(&Msg) != S_OK)
481             {
482                 TranslateMessage(&Msg);
483                 DispatchMessage(&Msg);
484             }
485         }
486     }
487 
488     return TRUE;
489 }
490