Reactos
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
25static 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
35class 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
56public:
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 BOOL bHideClock = GetHideClock();
143
144 if (!bHideClock)
145 {
146 if (IsHorizontal)
147 {
148 clockSize.cy = pSize->cy;
149 if (clockSize.cy <= 0)
150 goto NoClock;
151 }
152 else
153 {
154 clockSize.cx = pSize->cx;
155 if (clockSize.cx <= 0)
156 goto NoClock;
157 }
158
159 ::SendMessage(m_hwndClock, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &clockSize);
160
161 trayClockMinSize = clockSize;
162 }
163 else
164 NoClock:
165 trayClockMinSize = clockSize;
166
167 if (IsHorizontal)
168 {
169 traySize.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y;
170 }
171 else
172 {
173 traySize.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X;
174 }
175
176 ::SendMessage(m_hwndPager, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &traySize);
177
178 trayNotifySize = traySize;
179
180 INT showDesktopButtonExtent = 0;
181 if (g_TaskbarSettings.bShowDesktopButton)
182 {
183 showDesktopButtonExtent = m_ShowDesktopButton.WidthOrHeight();
184 if (IsHorizontal)
185 {
186 showDesktopSize.cx = showDesktopButtonExtent;
187 showDesktopSize.cy = pSize->cy;
188 }
189 else
190 {
191 showDesktopSize.cx = pSize->cx;
192 showDesktopSize.cy = showDesktopButtonExtent;
193 }
194 }
195 trayShowDesktopSize = showDesktopSize;
196
197 if (IsHorizontal)
198 {
199 pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X;
200
201 if (!bHideClock)
202 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + trayClockMinSize.cx;
203
204 if (g_TaskbarSettings.bShowDesktopButton)
205 pSize->cx += showDesktopButtonExtent;
206
207 pSize->cx += traySize.cx;
208 pSize->cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth;
209 }
210 else
211 {
212 pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y;
213
214 if (!bHideClock)
215 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + trayClockMinSize.cy;
216
217 if (g_TaskbarSettings.bShowDesktopButton)
218 pSize->cy += showDesktopButtonExtent;
219
220 pSize->cy += traySize.cy;
221 pSize->cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight;
222 }
223
224 return TRUE;
225 }
226
227 VOID Size(IN OUT SIZE *pszClient)
228 {
229 RECT rcClient = {0, 0, pszClient->cx, pszClient->cy};
230 AlignControls(&rcClient);
231 pszClient->cx = rcClient.right - rcClient.left;
232 pszClient->cy = rcClient.bottom - rcClient.top;
233 }
234
235 VOID AlignControls(IN CONST PRECT prcClient OPTIONAL)
236 {
237 RECT rcClient;
238 if (prcClient != NULL)
239 rcClient = *prcClient;
240 else
241 GetClientRect(&rcClient);
242
243 rcClient.left += ContentMargin.cxLeftWidth;
244 rcClient.top += ContentMargin.cyTopHeight;
245 rcClient.right -= ContentMargin.cxRightWidth;
246 rcClient.bottom -= ContentMargin.cyBottomHeight;
247
248 CONST UINT swpFlags = SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOZORDER;
249
250 if (g_TaskbarSettings.bShowDesktopButton)
251 {
252 POINT ptShowDesktop =
253 {
254 rcClient.left,
255 rcClient.top
256 };
257 SIZE showDesktopSize =
258 {
259 rcClient.right - rcClient.left,
260 rcClient.bottom - rcClient.top
261 };
262
263 INT cxyShowDesktop = m_ShowDesktopButton.WidthOrHeight();
264 if (IsHorizontal)
265 {
266 if (!TrayTheme)
267 {
268 ptShowDesktop.y -= ContentMargin.cyTopHeight;
269 showDesktopSize.cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight;
270 }
271
272 rcClient.right -= (cxyShowDesktop - ContentMargin.cxRightWidth);
273
274 ptShowDesktop.x = rcClient.right;
275 showDesktopSize.cx = cxyShowDesktop;
276
277 // HACK: Clock has layout problems - remove this once addressed.
278 rcClient.right -= CLOCK_TEXT_HACK;
279 }
280 else
281 {
282 if (!TrayTheme)
283 {
284 ptShowDesktop.x -= ContentMargin.cxLeftWidth;
285 showDesktopSize.cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth;
286 }
287
288 rcClient.bottom -= (cxyShowDesktop - ContentMargin.cyBottomHeight);
289
290 ptShowDesktop.y = rcClient.bottom;
291 showDesktopSize.cy = cxyShowDesktop;
292
293 // HACK: Clock has layout problems - remove this once addressed.
294 rcClient.bottom -= CLOCK_TEXT_HACK;
295 }
296
297 /* Resize and reposition the button */
298 ::SetWindowPos(m_hwndShowDesktop,
299 NULL,
300 ptShowDesktop.x,
301 ptShowDesktop.y,
302 showDesktopSize.cx,
303 showDesktopSize.cy,
304 swpFlags);
305 }
306
307 if (!GetHideClock())
308 {
309 POINT ptClock = { rcClient.left, rcClient.top };
310 SIZE clockSize = { rcClient.right - rcClient.left, rcClient.bottom - rcClient.top };
311
312 if (IsHorizontal)
313 {
314 rcClient.right -= trayClockMinSize.cx;
315
316 ptClock.x = rcClient.right;
317 clockSize.cx = trayClockMinSize.cx;
318 }
319 else
320 {
321 rcClient.bottom -= trayClockMinSize.cy;
322
323 ptClock.y = rcClient.bottom;
324 clockSize.cy = trayClockMinSize.cy;
325 }
326
327 ::SetWindowPos(m_hwndClock,
328 NULL,
329 ptClock.x,
330 ptClock.y,
331 clockSize.cx,
332 clockSize.cy,
333 swpFlags);
334 }
335
336 POINT ptPager;
337 if (IsHorizontal)
338 {
339 ptPager.x = ContentMargin.cxLeftWidth;
340 ptPager.y = ((rcClient.bottom - rcClient.top) - trayNotifySize.cy) / 2;
341 if (g_TaskbarSettings.UseCompactTrayIcons())
342 ptPager.y += ContentMargin.cyTopHeight;
343 }
344 else
345 {
346 ptPager.x = ((rcClient.right - rcClient.left) - trayNotifySize.cx) / 2;
347 if (g_TaskbarSettings.UseCompactTrayIcons())
348 ptPager.x += ContentMargin.cxLeftWidth;
349 ptPager.y = ContentMargin.cyTopHeight;
350 }
351
352 ::SetWindowPos(m_hwndPager,
353 NULL,
354 ptPager.x,
355 ptPager.y,
356 trayNotifySize.cx,
357 trayNotifySize.cy,
358 swpFlags);
359
360 if (prcClient != NULL)
361 {
362 prcClient->left = rcClient.left - ContentMargin.cxLeftWidth;
363 prcClient->top = rcClient.top - ContentMargin.cyTopHeight;
364 prcClient->right = rcClient.right + ContentMargin.cxRightWidth;
365 prcClient->bottom = rcClient.bottom + ContentMargin.cyBottomHeight;
366 }
367 }
368
369 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
370 {
371 HDC hdc = (HDC) wParam;
372
373 if (!TrayTheme)
374 {
375 bHandled = FALSE;
376 return 0;
377 }
378
379 RECT rect;
380 GetClientRect(&rect);
381 if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0))
382 DrawThemeParentBackground(m_hWnd, hdc, &rect);
383
384 DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0);
385
386 return TRUE;
387 }
388
389 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
390 {
391 BOOL Horizontal = (BOOL) wParam;
392
393 if (Horizontal != IsHorizontal)
394 IsHorizontal = Horizontal;
395
396 SetWindowTheme(m_hWnd,
397 IsHorizontal ? L"TrayNotifyHoriz" : L"TrayNotifyVert",
398 NULL);
399 m_ShowDesktopButton.m_bHorizontal = Horizontal;
400
401 return (LRESULT)GetMinimumSize((PSIZE)lParam);
402 }
403
404 LRESULT OnGetShowDesktopButton(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
405 {
406 if (wParam == NULL)
407 return 0;
408
409 CTrayShowDesktopButton** ptr = (CTrayShowDesktopButton**)wParam;
410 if (!m_ShowDesktopButton)
411 {
412 *ptr = NULL;
413 return 0;
414 }
415
416 *ptr = &m_ShowDesktopButton;
417 bHandled = TRUE;
418 return 0;
419 }
420
421 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
422 {
423 SIZE clientSize;
424
425 clientSize.cx = LOWORD(lParam);
426 clientSize.cy = HIWORD(lParam);
427
428 Size(&clientSize);
429
430 return TRUE;
431 }
432
433 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
434 {
435 POINT pt;
436 pt.x = GET_X_LPARAM(lParam);
437 pt.y = GET_Y_LPARAM(lParam);
438
439 if (m_ShowDesktopButton && m_ShowDesktopButton.PtInButton(&pt))
440 return HTCLIENT;
441
442 return HTTRANSPARENT;
443 }
444
445 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
446 {
447 POINT pt;
448 ::GetCursorPos(&pt);
449
450 if (m_ShowDesktopButton && m_ShowDesktopButton.PtInButton(&pt))
451 m_ShowDesktopButton.StartHovering();
452
453 return TRUE;
454 }
455
456 LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
457 {
458 bHandled = TRUE;
459
460 if (reinterpret_cast<HWND>(wParam) == m_hwndClock)
461 return GetParent().SendMessage(uMsg, wParam, lParam);
462 else
463 return 0;
464 }
465
466 LRESULT OnClockMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
467 {
468 return SendMessageW(m_hwndClock, uMsg, wParam, lParam);
469 }
470
471 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
472 {
473 TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
474
475 /* Toggle show desktop button */
476 if (newSettings->bShowDesktopButton != g_TaskbarSettings.bShowDesktopButton)
477 {
478 g_TaskbarSettings.bShowDesktopButton = newSettings->bShowDesktopButton;
479 ::ShowWindow(m_hwndShowDesktop, g_TaskbarSettings.bShowDesktopButton ? SW_SHOW : SW_HIDE);
480
481 /* Ask the parent to resize */
482 NMHDR nmh = {m_hWnd, 0, NTNWM_REALIGN};
483 SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
484 }
485
486 g_TaskbarSettings.bHideInactiveIcons = newSettings->bHideInactiveIcons;
487
488 return OnClockMessage(uMsg, wParam, lParam, bHandled);
489 }
490
491 LRESULT OnPagerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
492 {
493 return SendMessageW(m_hwndPager, uMsg, wParam, lParam);
494 }
495
496 LRESULT OnRealign(INT uCode, LPNMHDR hdr, BOOL& bHandled)
497 {
498 hdr->hwndFrom = m_hWnd;
499 return GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM)hdr);
500 }
501
502 // *** IOleWindow methods ***
503
504 STDMETHODIMP
505 GetWindow(HWND* phwnd) override
506 {
507 if (!phwnd)
508 return E_INVALIDARG;
509 *phwnd = m_hWnd;
510 return S_OK;
511 }
512
513 STDMETHODIMP
514 ContextSensitiveHelp(BOOL fEnterMode) override
515 {
516 return E_NOTIMPL;
517 }
518
519 HRESULT Initialize(IN HWND hwndParent)
520 {
521 const DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
522 Create(hwndParent, 0, NULL, dwStyle, WS_EX_STATICEDGE);
523 return m_hWnd ? S_OK : E_FAIL;
524 }
525
526 DECLARE_NOT_AGGREGATABLE(CTrayNotifyWnd)
527
528 DECLARE_PROTECT_FINAL_CONSTRUCT()
529 BEGIN_COM_MAP(CTrayNotifyWnd)
530 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
531 END_COM_MAP()
532
533 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE)
534
535 BEGIN_MSG_MAP(CTrayNotifyWnd)
536 MESSAGE_HANDLER(WM_CREATE, OnCreate)
537 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
538 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
539 MESSAGE_HANDLER(WM_SIZE, OnSize)
540 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
541 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
542 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
543 MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu)
544 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnClockMessage)
545 MESSAGE_HANDLER(WM_SETFONT, OnClockMessage)
546 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnPagerMessage)
547 MESSAGE_HANDLER(WM_COPYDATA, OnPagerMessage)
548 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
549 NOTIFY_CODE_HANDLER(NTNWM_REALIGN, OnRealign)
550 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
551 MESSAGE_HANDLER(TNWM_GETSHOWDESKTOPBUTTON, OnGetShowDesktopButton)
552 END_MSG_MAP()
553};
554
555HRESULT CTrayNotifyWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
556{
557 return ShellObjectCreatorInit<CTrayNotifyWnd>(hwndParent, riid, ppv);
558}