xref: /reactos/base/shell/explorer/trayntfy.cpp (revision cdf90707)
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[] = L"TrayNotifyWnd";
29 
30 #define TRAY_NOTIFY_WND_SPACING_X   1
31 #define TRAY_NOTIFY_WND_SPACING_Y   1
32 
33 class CTrayNotifyWnd :
34     public CComCoClass<CTrayNotifyWnd>,
35     public CComObjectRootEx<CComMultiThreadModelNoCS>,
36     public CWindowImpl < CTrayNotifyWnd, CWindow, CControlWinTraits >,
37     public IOleWindow
38 {
39     CComPtr<IUnknown> m_clock;
40     CComPtr<IUnknown> m_pager;
41 
42     HWND m_hwndClock;
43     HWND m_hwndPager;
44 
45     HTHEME TrayTheme;
46     SIZE szTrayClockMin;
47     SIZE szTrayNotify;
48     MARGINS ContentMargin;
49     BOOL IsHorizontal;
50 
51 public:
52     CTrayNotifyWnd() :
53         m_hwndClock(NULL),
54         m_hwndPager(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         HRESULT hr;
107 
108         hr = CTrayClockWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_clock));
109         if (FAILED_UNEXPECTEDLY(hr))
110             return FALSE;
111 
112         hr = IUnknown_GetWindow(m_clock, &m_hwndClock);
113         if (FAILED_UNEXPECTEDLY(hr))
114             return FALSE;
115 
116         hr = CSysPagerWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_pager));
117         if (FAILED_UNEXPECTEDLY(hr))
118             return FALSE;
119 
120         hr = IUnknown_GetWindow(m_pager, &m_hwndPager);
121         if (FAILED_UNEXPECTEDLY(hr))
122             return FALSE;
123 
124         return TRUE;
125     }
126 
127     LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
128     {
129         return MA_NOACTIVATE;
130     }
131 
132     BOOL GetMinimumSize(IN OUT PSIZE pSize)
133     {
134         SIZE szClock = { 0, 0 };
135         SIZE szTray = { 0, 0 };
136 
137         if (!g_TaskbarSettings.sr.HideClock)
138         {
139             if (IsHorizontal)
140             {
141                 szClock.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
142                 if (szClock.cy <= 0)
143                     goto NoClock;
144             }
145             else
146             {
147                 szClock.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
148                 if (szClock.cx <= 0)
149                     goto NoClock;
150             }
151 
152             ::SendMessage(m_hwndClock, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &szClock);
153 
154             szTrayClockMin = szClock;
155         }
156         else
157         NoClock:
158         szTrayClockMin = szClock;
159 
160         if (IsHorizontal)
161         {
162             szTray.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
163         }
164         else
165         {
166             szTray.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
167         }
168 
169         ::SendMessage(m_hwndPager, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &szTray);
170 
171         szTrayNotify = szTray;
172 
173         if (IsHorizontal)
174         {
175             pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X;
176 
177             if (!g_TaskbarSettings.sr.HideClock)
178                 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + szTrayClockMin.cx;
179 
180             pSize->cx += szTray.cx;
181             pSize->cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth;
182         }
183         else
184         {
185             pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y;
186 
187             if (!g_TaskbarSettings.sr.HideClock)
188                 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + szTrayClockMin.cy;
189 
190             pSize->cy += szTray.cy;
191             pSize->cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight;
192         }
193 
194         return TRUE;
195     }
196 
197     VOID Size(IN const SIZE *pszClient)
198     {
199         if (!g_TaskbarSettings.sr.HideClock)
200         {
201             POINT ptClock;
202             SIZE szClock;
203 
204             if (IsHorizontal)
205             {
206                 ptClock.x = pszClient->cx - szTrayClockMin.cx - ContentMargin.cxRightWidth;
207                 ptClock.y = ContentMargin.cyTopHeight;
208                 szClock.cx = szTrayClockMin.cx;
209                 szClock.cy = pszClient->cy - ContentMargin.cyTopHeight - ContentMargin.cyBottomHeight;
210             }
211             else
212             {
213                 ptClock.x = ContentMargin.cxLeftWidth;
214                 ptClock.y = pszClient->cy - szTrayClockMin.cy;
215                 szClock.cx = pszClient->cx - ContentMargin.cxLeftWidth - ContentMargin.cxRightWidth;
216                 szClock.cy = szTrayClockMin.cy;
217             }
218 
219             ::SetWindowPos(m_hwndClock,
220                 NULL,
221                 ptClock.x,
222                 ptClock.y,
223                 szClock.cx,
224                 szClock.cy,
225                 SWP_NOZORDER);
226         }
227 
228         POINT ptPager;
229 
230         if (IsHorizontal)
231         {
232             ptPager.x = ContentMargin.cxLeftWidth;
233             ptPager.y = (pszClient->cy - szTrayNotify.cy)/2;
234         }
235         else
236         {
237             ptPager.x = (pszClient->cx - szTrayNotify.cx)/2;
238             ptPager.y = ContentMargin.cyTopHeight;
239         }
240 
241         ::SetWindowPos(m_hwndPager,
242             NULL,
243             ptPager.x,
244             ptPager.y,
245             szTrayNotify.cx,
246             szTrayNotify.cy,
247             SWP_NOZORDER);
248     }
249 
250     LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
251     {
252         HDC hdc = (HDC) wParam;
253 
254         if (!TrayTheme)
255         {
256             bHandled = FALSE;
257             return 0;
258         }
259 
260         RECT rect;
261         GetClientRect(&rect);
262         if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0))
263             DrawThemeParentBackground(m_hWnd, hdc, &rect);
264 
265         DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
266 
267         return TRUE;
268     }
269 
270     LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
271     {
272         BOOL Horizontal = (BOOL) wParam;
273 
274         if (Horizontal != IsHorizontal)
275         {
276             IsHorizontal = Horizontal;
277             if (IsHorizontal)
278                 SetWindowTheme(m_hWnd, L"TrayNotifyHoriz", NULL);
279             else
280                 SetWindowTheme(m_hWnd, L"TrayNotifyVert", NULL);
281         }
282 
283         return (LRESULT) GetMinimumSize((PSIZE) lParam);
284     }
285 
286     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
287     {
288         SIZE szClient;
289 
290         szClient.cx = LOWORD(lParam);
291         szClient.cy = HIWORD(lParam);
292 
293         Size(&szClient);
294 
295         return TRUE;
296     }
297 
298     LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
299     {
300         return HTTRANSPARENT;
301     }
302 
303     LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
304     {
305         bHandled = TRUE;
306 
307         if (reinterpret_cast<HWND>(wParam) == m_hwndClock)
308             return GetParent().SendMessage(uMsg, wParam, lParam);
309         else
310             return 0;
311     }
312 
313     LRESULT OnClockMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
314     {
315         return SendMessageW(m_hwndClock, uMsg, wParam, lParam);
316     }
317 
318     LRESULT OnPagerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
319     {
320         return SendMessageW(m_hwndPager, uMsg, wParam, lParam);
321     }
322 
323     LRESULT OnRealign(INT uCode, LPNMHDR hdr, BOOL& bHandled)
324     {
325         hdr->hwndFrom = m_hWnd;
326         return GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM)hdr);
327     }
328 
329     HRESULT WINAPI GetWindow(HWND* phwnd)
330     {
331         if (!phwnd)
332             return E_INVALIDARG;
333         *phwnd = m_hWnd;
334         return S_OK;
335     }
336 
337     HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
338     {
339         return E_NOTIMPL;
340     }
341 
342     DECLARE_NOT_AGGREGATABLE(CTrayNotifyWnd)
343 
344     DECLARE_PROTECT_FINAL_CONSTRUCT()
345     BEGIN_COM_MAP(CTrayNotifyWnd)
346         COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
347     END_COM_MAP()
348 
349     DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE)
350 
351     BEGIN_MSG_MAP(CTrayNotifyWnd)
352         MESSAGE_HANDLER(WM_CREATE, OnCreate)
353         MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
354         MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
355         MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
356         MESSAGE_HANDLER(WM_SIZE, OnSize)
357         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
358         MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu)
359         MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnClockMessage)
360         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnClockMessage)
361         MESSAGE_HANDLER(WM_SETFONT, OnClockMessage)
362         MESSAGE_HANDLER(WM_SETTINGCHANGE, OnPagerMessage)
363         MESSAGE_HANDLER(WM_COPYDATA, OnPagerMessage)
364         NOTIFY_CODE_HANDLER(NTNWM_REALIGN, OnRealign)
365         MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
366     END_MSG_MAP()
367 
368     HRESULT Initialize(IN HWND hwndParent)
369     {
370         DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
371         Create(hwndParent, 0, NULL, dwStyle, WS_EX_STATICEDGE);
372         if (!m_hWnd)
373             return E_FAIL;
374         return S_OK;
375     }
376 };
377 
378 HRESULT CTrayNotifyWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
379 {
380     return ShellObjectCreatorInit<CTrayNotifyWnd>(hwndParent, riid, ppv);
381 }
382