xref: /reactos/base/shell/explorer/trayntfy.cpp (revision 84344399)
1 /*
2  * ReactOS Explorer
3  *
4  * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5  * Copyright 2018 Ged Murphy <gedmurphy@reactos.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "precomp.h"
23 #include <commoncontrols.h>
24 
25 static const WCHAR szTrayNotifyWndClass[] = L"TrayNotifyWnd";
26 
27 #define TRAY_NOTIFY_WND_SPACING_X   1
28 #define TRAY_NOTIFY_WND_SPACING_Y   1
29 #define CLOCK_TEXT_HACK             4
30 
31 /*
32  * TrayNotifyWnd
33  */
34 
35 class CTrayNotifyWnd :
36     public CComCoClass<CTrayNotifyWnd>,
37     public CComObjectRootEx<CComMultiThreadModelNoCS>,
38     public CWindowImpl < CTrayNotifyWnd, CWindow, CControlWinTraits >,
39     public IOleWindow
40 {
41     CComPtr<IUnknown> m_clock;
42     CTrayShowDesktopButton m_ShowDesktopButton;
43     CComPtr<IUnknown> m_pager;
44 
45     HWND m_hwndClock;
46     HWND m_hwndShowDesktop;
47     HWND m_hwndPager;
48 
49     HTHEME TrayTheme;
50     SIZE trayClockMinSize;
51     SIZE trayShowDesktopSize;
52     SIZE trayNotifySize;
53     MARGINS ContentMargin;
54     BOOL IsHorizontal;
55 
56 public:
57     CTrayNotifyWnd() :
58         m_hwndClock(NULL),
59         m_hwndPager(NULL),
60         TrayTheme(NULL),
61         IsHorizontal(FALSE)
62     {
63         ZeroMemory(&trayClockMinSize, sizeof(trayClockMinSize));
64         ZeroMemory(&trayShowDesktopSize, sizeof(trayShowDesktopSize));
65         ZeroMemory(&trayNotifySize, sizeof(trayNotifySize));
66         ZeroMemory(&ContentMargin, sizeof(ContentMargin));
67     }
68     ~CTrayNotifyWnd() { }
69 
70     LRESULT OnThemeChanged()
71     {
72         if (TrayTheme)
73             CloseThemeData(TrayTheme);
74 
75         if (IsThemeActive())
76             TrayTheme = OpenThemeData(m_hWnd, L"TrayNotify");
77         else
78             TrayTheme = NULL;
79 
80         if (TrayTheme)
81         {
82             SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, 0);
83 
84             GetThemeMargins(TrayTheme,
85                 NULL,
86                 TNP_BACKGROUND,
87                 0,
88                 TMT_CONTENTMARGINS,
89                 NULL,
90                 &ContentMargin);
91         }
92         else
93         {
94             SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, WS_EX_STATICEDGE);
95 
96             ContentMargin.cxLeftWidth = 2;
97             ContentMargin.cxRightWidth = 2;
98             ContentMargin.cyTopHeight = 2;
99             ContentMargin.cyBottomHeight = 2;
100         }
101 
102         return TRUE;
103     }
104 
105     LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
106     {
107         return OnThemeChanged();
108     }
109 
110     LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
111     {
112         HRESULT hr;
113 
114         hr = CTrayClockWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_clock));
115         if (FAILED_UNEXPECTEDLY(hr))
116             return FALSE;
117 
118         hr = IUnknown_GetWindow(m_clock, &m_hwndClock);
119         if (FAILED_UNEXPECTEDLY(hr))
120             return FALSE;
121 
122         hr = CSysPagerWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_pager));
123         if (FAILED_UNEXPECTEDLY(hr))
124             return FALSE;
125 
126         hr = IUnknown_GetWindow(m_pager, &m_hwndPager);
127         if (FAILED_UNEXPECTEDLY(hr))
128             return FALSE;
129 
130         /* Create the 'Show Desktop' button */
131         m_ShowDesktopButton.DoCreate(m_hWnd);
132         m_hwndShowDesktop = m_ShowDesktopButton.m_hWnd;
133 
134         return TRUE;
135     }
136 
137     BOOL GetMinimumSize(IN OUT PSIZE pSize)
138     {
139         SIZE clockSize = { 0, 0 };
140         SIZE traySize = { 0, 0 };
141         SIZE showDesktopSize = { 0, 0 };
142 
143         if (!g_TaskbarSettings.sr.HideClock)
144         {
145             if (IsHorizontal)
146             {
147                 clockSize.cy = pSize->cy;
148                 if (clockSize.cy <= 0)
149                     goto NoClock;
150             }
151             else
152             {
153                 clockSize.cx = pSize->cx;
154                 if (clockSize.cx <= 0)
155                     goto NoClock;
156             }
157 
158             ::SendMessage(m_hwndClock, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &clockSize);
159 
160             trayClockMinSize = clockSize;
161         }
162         else
163         NoClock:
164         trayClockMinSize = clockSize;
165 
166         if (IsHorizontal)
167         {
168             traySize.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
169         }
170         else
171         {
172             traySize.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
173         }
174 
175         ::SendMessage(m_hwndPager, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &traySize);
176 
177         trayNotifySize = traySize;
178 
179         INT showDesktopButtonExtent = 0;
180         if (g_TaskbarSettings.bShowDesktopButton)
181         {
182             showDesktopButtonExtent = m_ShowDesktopButton.WidthOrHeight();
183             if (IsHorizontal)
184             {
185                 showDesktopSize.cx = showDesktopButtonExtent;
186                 showDesktopSize.cy = pSize->cy;
187             }
188             else
189             {
190                 showDesktopSize.cx = pSize->cx;
191                 showDesktopSize.cy = showDesktopButtonExtent;
192             }
193         }
194         trayShowDesktopSize = showDesktopSize;
195 
196         if (IsHorizontal)
197         {
198             pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X;
199 
200             if (!g_TaskbarSettings.sr.HideClock)
201                 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + trayClockMinSize.cx;
202 
203             if (g_TaskbarSettings.bShowDesktopButton)
204                 pSize->cx += showDesktopButtonExtent;
205 
206             pSize->cx += traySize.cx;
207             pSize->cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth;
208         }
209         else
210         {
211             pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y;
212 
213             if (!g_TaskbarSettings.sr.HideClock)
214                 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + trayClockMinSize.cy;
215 
216             if (g_TaskbarSettings.bShowDesktopButton)
217                 pSize->cy += showDesktopButtonExtent;
218 
219             pSize->cy += traySize.cy;
220             pSize->cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight;
221         }
222 
223         return TRUE;
224     }
225 
226     VOID Size(IN OUT SIZE *pszClient)
227     {
228         RECT rcClient = {0, 0, pszClient->cx, pszClient->cy};
229         AlignControls(&rcClient);
230         pszClient->cx = rcClient.right - rcClient.left;
231         pszClient->cy = rcClient.bottom - rcClient.top;
232     }
233 
234     VOID AlignControls(IN CONST PRECT prcClient OPTIONAL)
235     {
236         RECT rcClient;
237         if (prcClient != NULL)
238             rcClient = *prcClient;
239         else
240             GetClientRect(&rcClient);
241 
242         rcClient.left += ContentMargin.cxLeftWidth;
243         rcClient.top += ContentMargin.cyTopHeight;
244         rcClient.right -= ContentMargin.cxRightWidth;
245         rcClient.bottom -= ContentMargin.cyBottomHeight;
246 
247         CONST UINT swpFlags = SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOZORDER;
248 
249         if (g_TaskbarSettings.bShowDesktopButton)
250         {
251             POINT ptShowDesktop =
252             {
253                 rcClient.left,
254                 rcClient.top
255             };
256             SIZE showDesktopSize =
257             {
258                 rcClient.right - rcClient.left,
259                 rcClient.bottom - rcClient.top
260             };
261 
262             INT cxyShowDesktop = m_ShowDesktopButton.WidthOrHeight();
263             if (IsHorizontal)
264             {
265                 if (!TrayTheme)
266                 {
267                     ptShowDesktop.y -= ContentMargin.cyTopHeight;
268                     showDesktopSize.cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight;
269                 }
270 
271                 rcClient.right -= (cxyShowDesktop - ContentMargin.cxRightWidth);
272 
273                 ptShowDesktop.x = rcClient.right;
274                 showDesktopSize.cx = cxyShowDesktop;
275 
276                 // HACK: Clock has layout problems - remove this once addressed.
277                 rcClient.right -= CLOCK_TEXT_HACK;
278             }
279             else
280             {
281                 if (!TrayTheme)
282                 {
283                     ptShowDesktop.x -= ContentMargin.cxLeftWidth;
284                     showDesktopSize.cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth;
285                 }
286 
287                 rcClient.bottom -= (cxyShowDesktop - ContentMargin.cyBottomHeight);
288 
289                 ptShowDesktop.y = rcClient.bottom;
290                 showDesktopSize.cy = cxyShowDesktop;
291 
292                 // HACK: Clock has layout problems - remove this once addressed.
293                 rcClient.bottom -= CLOCK_TEXT_HACK;
294             }
295 
296             /* Resize and reposition the button */
297             ::SetWindowPos(m_hwndShowDesktop,
298                 NULL,
299                 ptShowDesktop.x,
300                 ptShowDesktop.y,
301                 showDesktopSize.cx,
302                 showDesktopSize.cy,
303                 swpFlags);
304         }
305 
306         if (!g_TaskbarSettings.sr.HideClock)
307         {
308             POINT ptClock = { rcClient.left, rcClient.top };
309             SIZE clockSize = { rcClient.right - rcClient.left, rcClient.bottom - rcClient.top };
310 
311             if (IsHorizontal)
312             {
313                 rcClient.right -= trayClockMinSize.cx;
314 
315                 ptClock.x = rcClient.right;
316                 clockSize.cx = trayClockMinSize.cx;
317             }
318             else
319             {
320                 rcClient.bottom -= trayClockMinSize.cy;
321 
322                 ptClock.y = rcClient.bottom;
323                 clockSize.cy = trayClockMinSize.cy;
324             }
325 
326             ::SetWindowPos(m_hwndClock,
327                 NULL,
328                 ptClock.x,
329                 ptClock.y,
330                 clockSize.cx,
331                 clockSize.cy,
332                 swpFlags);
333         }
334 
335         POINT ptPager;
336         if (IsHorizontal)
337         {
338             ptPager.x = ContentMargin.cxLeftWidth;
339             ptPager.y = ((rcClient.bottom - rcClient.top) - trayNotifySize.cy) / 2;
340             if (g_TaskbarSettings.UseCompactTrayIcons())
341                 ptPager.y += ContentMargin.cyTopHeight;
342         }
343         else
344         {
345             ptPager.x = ((rcClient.right - rcClient.left) - trayNotifySize.cx) / 2;
346             if (g_TaskbarSettings.UseCompactTrayIcons())
347                 ptPager.x += ContentMargin.cxLeftWidth;
348             ptPager.y = ContentMargin.cyTopHeight;
349         }
350 
351         ::SetWindowPos(m_hwndPager,
352             NULL,
353             ptPager.x,
354             ptPager.y,
355             trayNotifySize.cx,
356             trayNotifySize.cy,
357             swpFlags);
358 
359         if (prcClient != NULL)
360         {
361             prcClient->left = rcClient.left - ContentMargin.cxLeftWidth;
362             prcClient->top = rcClient.top - ContentMargin.cyTopHeight;
363             prcClient->right = rcClient.right + ContentMargin.cxRightWidth;
364             prcClient->bottom = rcClient.bottom + ContentMargin.cyBottomHeight;
365         }
366     }
367 
368     LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
369     {
370         HDC hdc = (HDC) wParam;
371 
372         if (!TrayTheme)
373         {
374             bHandled = FALSE;
375             return 0;
376         }
377 
378         RECT rect;
379         GetClientRect(&rect);
380         if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0))
381             DrawThemeParentBackground(m_hWnd, hdc, &rect);
382 
383         DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
384 
385         return TRUE;
386     }
387 
388     LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
389     {
390         BOOL Horizontal = (BOOL) wParam;
391 
392         if (Horizontal != IsHorizontal)
393             IsHorizontal = Horizontal;
394 
395         SetWindowTheme(m_hWnd,
396                        IsHorizontal ? L"TrayNotifyHoriz" : L"TrayNotifyVert",
397                        NULL);
398         m_ShowDesktopButton.m_bHorizontal = Horizontal;
399 
400         return (LRESULT)GetMinimumSize((PSIZE)lParam);
401     }
402 
403     LRESULT OnGetShowDesktopButton(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
404     {
405         if (wParam == NULL)
406             return 0;
407 
408         CTrayShowDesktopButton** ptr = (CTrayShowDesktopButton**)wParam;
409         if (!m_ShowDesktopButton)
410         {
411             *ptr = NULL;
412             return 0;
413         }
414 
415         *ptr = &m_ShowDesktopButton;
416         bHandled = TRUE;
417         return 0;
418     }
419 
420     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
421     {
422         SIZE clientSize;
423 
424         clientSize.cx = LOWORD(lParam);
425         clientSize.cy = HIWORD(lParam);
426 
427         Size(&clientSize);
428 
429         return TRUE;
430     }
431 
432     LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
433     {
434         POINT pt;
435         pt.x = GET_X_LPARAM(lParam);
436         pt.y = GET_Y_LPARAM(lParam);
437 
438         if (m_ShowDesktopButton && m_ShowDesktopButton.PtInButton(&pt))
439             return HTCLIENT;
440 
441         return HTTRANSPARENT;
442     }
443 
444     LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
445     {
446         POINT pt;
447         ::GetCursorPos(&pt);
448 
449         if (m_ShowDesktopButton && m_ShowDesktopButton.PtInButton(&pt))
450             m_ShowDesktopButton.StartHovering();
451 
452         return TRUE;
453     }
454 
455     LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
456     {
457         bHandled = TRUE;
458 
459         if (reinterpret_cast<HWND>(wParam) == m_hwndClock)
460             return GetParent().SendMessage(uMsg, wParam, lParam);
461         else
462             return 0;
463     }
464 
465     LRESULT OnClockMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
466     {
467         return SendMessageW(m_hwndClock, uMsg, wParam, lParam);
468     }
469 
470     LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
471     {
472         TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
473 
474         /* Toggle show desktop button */
475         if (newSettings->bShowDesktopButton != g_TaskbarSettings.bShowDesktopButton)
476         {
477             g_TaskbarSettings.bShowDesktopButton = newSettings->bShowDesktopButton;
478             ::ShowWindow(m_hwndShowDesktop, g_TaskbarSettings.bShowDesktopButton ? SW_SHOW : SW_HIDE);
479 
480             /* Ask the parent to resize */
481             NMHDR nmh = {m_hWnd, 0, NTNWM_REALIGN};
482             SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
483         }
484 
485         return OnClockMessage(uMsg, wParam, lParam, bHandled);
486     }
487 
488     LRESULT OnPagerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
489     {
490         return SendMessageW(m_hwndPager, uMsg, wParam, lParam);
491     }
492 
493     LRESULT OnRealign(INT uCode, LPNMHDR hdr, BOOL& bHandled)
494     {
495         hdr->hwndFrom = m_hWnd;
496         return GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM)hdr);
497     }
498 
499     HRESULT WINAPI GetWindow(HWND* phwnd)
500     {
501         if (!phwnd)
502             return E_INVALIDARG;
503         *phwnd = m_hWnd;
504         return S_OK;
505     }
506 
507     HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
508     {
509         return E_NOTIMPL;
510     }
511 
512     HRESULT Initialize(IN HWND hwndParent)
513     {
514         const DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
515         Create(hwndParent, 0, NULL, dwStyle, WS_EX_STATICEDGE);
516         return m_hWnd ? S_OK : E_FAIL;
517     }
518 
519     DECLARE_NOT_AGGREGATABLE(CTrayNotifyWnd)
520 
521     DECLARE_PROTECT_FINAL_CONSTRUCT()
522     BEGIN_COM_MAP(CTrayNotifyWnd)
523         COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
524     END_COM_MAP()
525 
526     DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE)
527 
528     BEGIN_MSG_MAP(CTrayNotifyWnd)
529         MESSAGE_HANDLER(WM_CREATE, OnCreate)
530         MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
531         MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
532         MESSAGE_HANDLER(WM_SIZE, OnSize)
533         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
534         MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
535         MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
536         MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu)
537         MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnClockMessage)
538         MESSAGE_HANDLER(WM_SETFONT, OnClockMessage)
539         MESSAGE_HANDLER(WM_SETTINGCHANGE, OnPagerMessage)
540         MESSAGE_HANDLER(WM_COPYDATA, OnPagerMessage)
541         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
542         NOTIFY_CODE_HANDLER(NTNWM_REALIGN, OnRealign)
543         MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
544         MESSAGE_HANDLER(TNWM_GETSHOWDESKTOPBUTTON, OnGetShowDesktopButton)
545     END_MSG_MAP()
546 };
547 
548 HRESULT CTrayNotifyWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
549 {
550     return ShellObjectCreatorInit<CTrayNotifyWnd>(hwndParent, riid, ppv);
551 }
552