xref: /reactos/base/shell/explorer/trayntfy.cpp (revision b917d826)
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 
24 /*
25  * TrayNotifyWnd
26  */
27 
28 static const WCHAR szTrayNotifyWndClass [] = TEXT("TrayNotifyWnd");
29 
30 #define TRAY_NOTIFY_WND_SPACING_X   1
31 #define TRAY_NOTIFY_WND_SPACING_Y   1
32 
33 class CTrayNotifyWnd :
34     public CComObjectRootEx<CComMultiThreadModelNoCS>,
35     public CWindowImpl < CTrayNotifyWnd, CWindow, CControlWinTraits >
36 {
37     HWND hWndNotify;
38 
39     CSysPagerWnd * m_pager;
40     CTrayClockWnd * m_clock;
41 
42     CComPtr<ITrayWindow> TrayWindow;
43 
44     HTHEME TrayTheme;
45     SIZE szTrayClockMin;
46     SIZE szTrayNotify;
47     MARGINS ContentMargin;
48     BOOL IsHorizontal;
49 
50 public:
51     CTrayNotifyWnd() :
52         hWndNotify(NULL),
53         m_pager(NULL),
54         m_clock(NULL),
55         TrayTheme(NULL),
56         IsHorizontal(FALSE)
57     {
58         ZeroMemory(&szTrayClockMin, sizeof(szTrayClockMin));
59         ZeroMemory(&szTrayNotify, sizeof(szTrayNotify));
60         ZeroMemory(&ContentMargin, sizeof(ContentMargin));
61     }
62     virtual ~CTrayNotifyWnd() { }
63 
64     LRESULT OnThemeChanged()
65     {
66         if (TrayTheme)
67             CloseThemeData(TrayTheme);
68 
69         if (IsThemeActive())
70             TrayTheme = OpenThemeData(m_hWnd, L"TrayNotify");
71         else
72             TrayTheme = NULL;
73 
74         if (TrayTheme)
75         {
76             SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, 0);
77 
78             GetThemeMargins(TrayTheme,
79                 NULL,
80                 TNP_BACKGROUND,
81                 0,
82                 TMT_CONTENTMARGINS,
83                 NULL,
84                 &ContentMargin);
85         }
86         else
87         {
88             SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, WS_EX_STATICEDGE);
89 
90             ContentMargin.cxLeftWidth = 2;
91             ContentMargin.cxRightWidth = 2;
92             ContentMargin.cyTopHeight = 2;
93             ContentMargin.cyBottomHeight = 2;
94         }
95 
96         return TRUE;
97     }
98 
99     LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
100     {
101         return OnThemeChanged();
102     }
103 
104     LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
105     {
106         m_clock = new CTrayClockWnd();
107         m_clock->_Init(m_hWnd, !g_TaskbarSettings.sr.HideClock);
108 
109         m_pager = new CSysPagerWnd();
110         m_pager->_Init(m_hWnd, !g_TaskbarSettings.sr.HideClock);
111 
112         return TRUE;
113     }
114 
115     BOOL GetMinimumSize(IN OUT PSIZE pSize)
116     {
117         SIZE szClock = { 0, 0 };
118         SIZE szTray = { 0, 0 };
119 
120         if (!g_TaskbarSettings.sr.HideClock)
121         {
122             if (IsHorizontal)
123             {
124                 szClock.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
125                 if (szClock.cy <= 0)
126                     goto NoClock;
127             }
128             else
129             {
130                 szClock.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
131                 if (szClock.cx <= 0)
132                     goto NoClock;
133             }
134 
135             m_clock->SendMessage(TCWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &szClock);
136 
137             szTrayClockMin = szClock;
138         }
139         else
140         NoClock:
141         szTrayClockMin = szClock;
142 
143         if (IsHorizontal)
144         {
145             szTray.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
146         }
147         else
148         {
149             szTray.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
150         }
151 
152         m_pager->GetSize(IsHorizontal, &szTray);
153 
154         szTrayNotify = szTray;
155 
156         if (IsHorizontal)
157         {
158             pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X;
159 
160             if (!g_TaskbarSettings.sr.HideClock)
161                 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + szTrayClockMin.cx;
162 
163             pSize->cx += szTray.cx;
164         }
165         else
166         {
167             pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y;
168 
169             if (!g_TaskbarSettings.sr.HideClock)
170                 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + szTrayClockMin.cy;
171 
172             pSize->cy += szTray.cy;
173         }
174 
175         pSize->cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight;
176         pSize->cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth;
177 
178         return TRUE;
179     }
180 
181     VOID Size(IN const SIZE *pszClient)
182     {
183         if (!g_TaskbarSettings.sr.HideClock)
184         {
185             POINT ptClock;
186             SIZE szClock;
187 
188             if (IsHorizontal)
189             {
190                 ptClock.x = pszClient->cx - szTrayClockMin.cx - ContentMargin.cxRightWidth;
191                 ptClock.y = ContentMargin.cyTopHeight;
192                 szClock.cx = szTrayClockMin.cx;
193                 szClock.cy = pszClient->cy - ContentMargin.cyTopHeight - ContentMargin.cyBottomHeight;
194             }
195             else
196             {
197                 ptClock.x = ContentMargin.cxLeftWidth;
198                 ptClock.y = pszClient->cy - szTrayClockMin.cy;
199                 szClock.cx = pszClient->cx - ContentMargin.cxLeftWidth - ContentMargin.cxRightWidth;
200                 szClock.cy = szTrayClockMin.cy;
201             }
202 
203             m_clock->SetWindowPos(
204                 NULL,
205                 ptClock.x,
206                 ptClock.y,
207                 szClock.cx,
208                 szClock.cy,
209                 SWP_NOZORDER);
210 
211             POINT ptPager;
212 
213             if (IsHorizontal)
214             {
215                 ptPager.x = ContentMargin.cxLeftWidth;
216                 ptPager.y = (pszClient->cy - szTrayNotify.cy)/2;
217             }
218             else
219             {
220                 ptPager.x = (pszClient->cx - szTrayNotify.cx)/2;
221                 ptPager.y = ContentMargin.cyTopHeight;
222             }
223 
224             m_pager->SetWindowPos(
225                 NULL,
226                 ptPager.x,
227                 ptPager.y,
228                 szTrayNotify.cx,
229                 szTrayNotify.cy,
230                 SWP_NOZORDER);
231         }
232     }
233 
234     LRESULT DrawBackground(HDC hdc)
235     {
236         HRESULT res;
237         RECT rect;
238 
239         GetClientRect(&rect);
240 
241         if (TrayTheme)
242         {
243             if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0))
244             {
245                 DrawThemeParentBackground(m_hWnd, hdc, &rect);
246             }
247 
248             res = DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
249         }
250 
251         return res;
252     }
253 
254     LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
255     {
256         HDC hdc = (HDC) wParam;
257 
258         if (!TrayTheme)
259         {
260             bHandled = FALSE;
261             return 0;
262         }
263 
264         return DrawBackground(hdc);
265     }
266 
267     BOOL NotifyIconCmd(WPARAM wParam, LPARAM lParam)
268     {
269         if (m_pager)
270         {
271             return m_pager->NotifyIconCmd(wParam, lParam);
272         }
273 
274         return TRUE;
275     }
276 
277     BOOL GetClockRect(OUT PRECT rcClock)
278     {
279         if (!m_clock->IsWindowVisible())
280             return FALSE;
281 
282         return m_clock->GetWindowRect(rcClock);
283     }
284 
285     LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
286     {
287         BOOL Horizontal = (BOOL) wParam;
288 
289         if (Horizontal != IsHorizontal)
290         {
291             IsHorizontal = Horizontal;
292             if (IsHorizontal)
293                 SetWindowTheme(m_hWnd, L"TrayNotifyHoriz", NULL);
294             else
295                 SetWindowTheme(m_hWnd, L"TrayNotifyVert", NULL);
296         }
297 
298         return (LRESULT) GetMinimumSize((PSIZE) lParam);
299     }
300 
301     LRESULT OnUpdateTime(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
302     {
303         if (m_clock != NULL)
304         {
305             /* Forward the message to the tray clock window procedure */
306             return m_clock->OnUpdateTime(uMsg, wParam, lParam, bHandled);
307         }
308         return FALSE;
309     }
310 
311     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
312     {
313         SIZE szClient;
314 
315         szClient.cx = LOWORD(lParam);
316         szClient.cy = HIWORD(lParam);
317 
318         Size(&szClient);
319 
320         return TRUE;
321     }
322 
323     LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
324     {
325         return HTTRANSPARENT;
326     }
327 
328     LRESULT OnShowClock(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
329     {
330         BOOL PrevHidden = g_TaskbarSettings.sr.HideClock;
331         g_TaskbarSettings.sr.HideClock = (wParam == 0);
332 
333         if (m_clock != NULL && PrevHidden != g_TaskbarSettings.sr.HideClock)
334         {
335             m_clock->ShowWindow(g_TaskbarSettings.sr.HideClock ? SW_HIDE : SW_SHOW);
336         }
337 
338         return (LRESULT) (!PrevHidden);
339     }
340 
341     LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
342     {
343         TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
344         if (newSettings->bShowSeconds != g_TaskbarSettings.bShowSeconds)
345         {
346             g_TaskbarSettings.bShowSeconds = newSettings->bShowSeconds;
347             /* TODO: Toggle showing seconds */
348         }
349 
350         if (newSettings->sr.HideClock != g_TaskbarSettings.sr.HideClock)
351         {
352             g_TaskbarSettings.sr.HideClock = newSettings->sr.HideClock;
353             /* TODO: Toggle hiding the clock */
354         }
355 
356         return 0;
357     }
358 
359     LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
360     {
361         const NMHDR *nmh = (const NMHDR *) lParam;
362 
363         if (nmh->hwndFrom == m_clock->m_hWnd)
364         {
365             /* Pass down notifications */
366             return m_clock->SendMessage(WM_NOTIFY, wParam, lParam);
367         }
368 
369         return FALSE;
370     }
371 
372     LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
373     {
374         if (m_clock != NULL)
375         {
376             m_clock->SendMessageW(WM_SETFONT, wParam, lParam);
377         }
378 
379         bHandled = FALSE;
380         return FALSE;
381     }
382 
383     LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
384     {
385         bHandled = TRUE;
386         return 0;
387     }
388 
389     LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
390     {
391         if (wParam == SPI_SETNONCLIENTMETRICS)
392         {
393             m_pager->ResizeImagelist();
394         }
395         return 0;
396     }
397 
398     DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE)
399 
400     BEGIN_MSG_MAP(CTrayNotifyWnd)
401         MESSAGE_HANDLER(WM_CREATE, OnCreate)
402         MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
403         MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
404         MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
405         MESSAGE_HANDLER(WM_SIZE, OnSize)
406         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
407         MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
408         MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
409         MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu) // FIXME: This handler is not necessary in Windows
410         MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
411         MESSAGE_HANDLER(TNWM_UPDATETIME, OnUpdateTime)
412         MESSAGE_HANDLER(TNWM_SHOWCLOCK, OnShowClock)
413         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
414     END_MSG_MAP()
415 
416     HWND _Init(IN OUT ITrayWindow *TrayWindow)
417     {
418         HWND hWndTrayWindow;
419 
420         hWndTrayWindow = TrayWindow->GetHWND();
421         if (hWndTrayWindow == NULL)
422             return NULL;
423 
424         this->TrayWindow = TrayWindow;
425         this->hWndNotify = hWndTrayWindow;
426 
427         DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
428         return Create(hWndTrayWindow, 0, NULL, dwStyle, WS_EX_STATICEDGE);
429     }
430 };
431 
432 HWND CreateTrayNotifyWnd(IN OUT ITrayWindow *Tray, CTrayNotifyWnd** ppinstance)
433 {
434     CTrayNotifyWnd * pTrayNotify = new CTrayNotifyWnd();
435     // TODO: Destroy after the window is destroyed
436     *ppinstance = pTrayNotify;
437 
438     return pTrayNotify->_Init(Tray);
439 }
440 
441 BOOL
442 TrayNotify_NotifyIconCmd(CTrayNotifyWnd* pTrayNotify, WPARAM wParam, LPARAM lParam)
443 {
444     return pTrayNotify->NotifyIconCmd(wParam, lParam);
445 }
446 
447 BOOL
448 TrayNotify_GetClockRect(CTrayNotifyWnd* pTrayNotify, OUT PRECT rcClock)
449 {
450     return pTrayNotify->GetClockRect(rcClock);
451 }
452