[EXPLORER] CTrayNotifyWnd: Only update the requested dimension when handling TNWM_GET...
[reactos.git] / base / shell / explorer / trayntfy.cpp
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 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 BOOL GetMinimumSize(IN OUT PSIZE pSize)
128 {
129 SIZE szClock = { 0, 0 };
130 SIZE szTray = { 0, 0 };
131
132 if (!g_TaskbarSettings.sr.HideClock)
133 {
134 if (IsHorizontal)
135 {
136 szClock.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
137 if (szClock.cy <= 0)
138 goto NoClock;
139 }
140 else
141 {
142 szClock.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
143 if (szClock.cx <= 0)
144 goto NoClock;
145 }
146
147 ::SendMessage(m_hwndClock, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &szClock);
148
149 szTrayClockMin = szClock;
150 }
151 else
152 NoClock:
153 szTrayClockMin = szClock;
154
155 if (IsHorizontal)
156 {
157 szTray.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
158 }
159 else
160 {
161 szTray.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
162 }
163
164 ::SendMessage(m_hwndPager, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &szTray);
165
166 szTrayNotify = szTray;
167
168 if (IsHorizontal)
169 {
170 pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X;
171
172 if (!g_TaskbarSettings.sr.HideClock)
173 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + szTrayClockMin.cx;
174
175 pSize->cx += szTray.cx;
176 pSize->cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth;
177 }
178 else
179 {
180 pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y;
181
182 if (!g_TaskbarSettings.sr.HideClock)
183 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + szTrayClockMin.cy;
184
185 pSize->cy += szTray.cy;
186 pSize->cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight;
187 }
188
189 return TRUE;
190 }
191
192 VOID Size(IN const SIZE *pszClient)
193 {
194 if (!g_TaskbarSettings.sr.HideClock)
195 {
196 POINT ptClock;
197 SIZE szClock;
198
199 if (IsHorizontal)
200 {
201 ptClock.x = pszClient->cx - szTrayClockMin.cx - ContentMargin.cxRightWidth;
202 ptClock.y = ContentMargin.cyTopHeight;
203 szClock.cx = szTrayClockMin.cx;
204 szClock.cy = pszClient->cy - ContentMargin.cyTopHeight - ContentMargin.cyBottomHeight;
205 }
206 else
207 {
208 ptClock.x = ContentMargin.cxLeftWidth;
209 ptClock.y = pszClient->cy - szTrayClockMin.cy;
210 szClock.cx = pszClient->cx - ContentMargin.cxLeftWidth - ContentMargin.cxRightWidth;
211 szClock.cy = szTrayClockMin.cy;
212 }
213
214 ::SetWindowPos(m_hwndClock,
215 NULL,
216 ptClock.x,
217 ptClock.y,
218 szClock.cx,
219 szClock.cy,
220 SWP_NOZORDER);
221
222 POINT ptPager;
223
224 if (IsHorizontal)
225 {
226 ptPager.x = ContentMargin.cxLeftWidth;
227 ptPager.y = (pszClient->cy - szTrayNotify.cy)/2;
228 }
229 else
230 {
231 ptPager.x = (pszClient->cx - szTrayNotify.cx)/2;
232 ptPager.y = ContentMargin.cyTopHeight;
233 }
234
235 ::SetWindowPos(m_hwndPager,
236 NULL,
237 ptPager.x,
238 ptPager.y,
239 szTrayNotify.cx,
240 szTrayNotify.cy,
241 SWP_NOZORDER);
242 }
243 }
244
245 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
246 {
247 HDC hdc = (HDC) wParam;
248
249 if (!TrayTheme)
250 {
251 bHandled = FALSE;
252 return 0;
253 }
254
255 RECT rect;
256 GetClientRect(&rect);
257 if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0))
258 DrawThemeParentBackground(m_hWnd, hdc, &rect);
259
260 DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
261
262 return TRUE;
263 }
264
265 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
266 {
267 BOOL Horizontal = (BOOL) wParam;
268
269 if (Horizontal != IsHorizontal)
270 {
271 IsHorizontal = Horizontal;
272 if (IsHorizontal)
273 SetWindowTheme(m_hWnd, L"TrayNotifyHoriz", NULL);
274 else
275 SetWindowTheme(m_hWnd, L"TrayNotifyVert", NULL);
276 }
277
278 return (LRESULT) GetMinimumSize((PSIZE) lParam);
279 }
280
281 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
282 {
283 SIZE szClient;
284
285 szClient.cx = LOWORD(lParam);
286 szClient.cy = HIWORD(lParam);
287
288 Size(&szClient);
289
290 return TRUE;
291 }
292
293 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
294 {
295 return HTTRANSPARENT;
296 }
297
298 LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
299 {
300 bHandled = TRUE;
301 return 0;
302 }
303
304 LRESULT OnClockMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
305 {
306 return SendMessageW(m_hwndClock, uMsg, wParam, lParam);
307 }
308
309 LRESULT OnPagerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
310 {
311 return SendMessageW(m_hwndPager, uMsg, wParam, lParam);
312 }
313
314 LRESULT OnRealign(INT uCode, LPNMHDR hdr, BOOL& bHandled)
315 {
316 hdr->hwndFrom = m_hWnd;
317 return GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM)hdr);
318 }
319
320 HRESULT WINAPI GetWindow(HWND* phwnd)
321 {
322 if (!phwnd)
323 return E_INVALIDARG;
324 *phwnd = m_hWnd;
325 return S_OK;
326 }
327
328 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
329 {
330 return E_NOTIMPL;
331 }
332
333 DECLARE_NOT_AGGREGATABLE(CTrayNotifyWnd)
334
335 DECLARE_PROTECT_FINAL_CONSTRUCT()
336 BEGIN_COM_MAP(CTrayNotifyWnd)
337 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
338 END_COM_MAP()
339
340 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE)
341
342 BEGIN_MSG_MAP(CTrayNotifyWnd)
343 MESSAGE_HANDLER(WM_CREATE, OnCreate)
344 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
345 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
346 MESSAGE_HANDLER(WM_SIZE, OnSize)
347 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
348 MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu) // FIXME: This handler is not necessary in Windows
349 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnClockMessage)
350 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnClockMessage)
351 MESSAGE_HANDLER(WM_SETFONT, OnClockMessage)
352 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnPagerMessage)
353 MESSAGE_HANDLER(WM_COPYDATA, OnPagerMessage)
354 NOTIFY_CODE_HANDLER(NTNWM_REALIGN, OnRealign)
355 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
356 END_MSG_MAP()
357
358 HRESULT Initialize(IN HWND hwndParent)
359 {
360 DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
361 Create(hwndParent, 0, NULL, dwStyle, WS_EX_STATICEDGE);
362 if (!m_hWnd)
363 return E_FAIL;
364 return S_OK;
365 }
366 };
367
368 HRESULT CTrayNotifyWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
369 {
370 return ShellObjectCreatorInit<CTrayNotifyWnd>(hwndParent, riid, ppv);
371 }