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:
CTrayNotifyWnd()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 }
~CTrayNotifyWnd()68 ~CTrayNotifyWnd() { }
69
OnThemeChanged()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
OnThemeChanged(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)105 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
106 {
107 return OnThemeChanged();
108 }
109
OnCreate(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
GetMinimumSize(IN OUT PSIZE pSize)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
Size(IN OUT SIZE * pszClient)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
AlignControls(IN CONST PRECT prcClient OPTIONAL)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
OnEraseBackground(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnGetMinimumSize(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnGetShowDesktopButton(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnSize(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnNcHitTest(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnMouseMove(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnCtxMenu(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnClockMessage(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)465 LRESULT OnClockMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
466 {
467 return SendMessageW(m_hwndClock, uMsg, wParam, lParam);
468 }
469
OnTaskbarSettingsChanged(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnPagerMessage(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)488 LRESULT OnPagerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
489 {
490 return SendMessageW(m_hwndPager, uMsg, wParam, lParam);
491 }
492
OnRealign(INT uCode,LPNMHDR hdr,BOOL & bHandled)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 // *** IOleWindow methods ***
500
501 STDMETHODIMP
GetWindow(HWND * phwnd)502 GetWindow(HWND* phwnd) override
503 {
504 if (!phwnd)
505 return E_INVALIDARG;
506 *phwnd = m_hWnd;
507 return S_OK;
508 }
509
510 STDMETHODIMP
ContextSensitiveHelp(BOOL fEnterMode)511 ContextSensitiveHelp(BOOL fEnterMode) override
512 {
513 return E_NOTIMPL;
514 }
515
Initialize(IN HWND hwndParent)516 HRESULT Initialize(IN HWND hwndParent)
517 {
518 const DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
519 Create(hwndParent, 0, NULL, dwStyle, WS_EX_STATICEDGE);
520 return m_hWnd ? S_OK : E_FAIL;
521 }
522
523 DECLARE_NOT_AGGREGATABLE(CTrayNotifyWnd)
524
525 DECLARE_PROTECT_FINAL_CONSTRUCT()
526 BEGIN_COM_MAP(CTrayNotifyWnd)
527 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
528 END_COM_MAP()
529
530 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE)
531
532 BEGIN_MSG_MAP(CTrayNotifyWnd)
533 MESSAGE_HANDLER(WM_CREATE, OnCreate)
534 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
535 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
536 MESSAGE_HANDLER(WM_SIZE, OnSize)
537 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
538 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
539 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
540 MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu)
541 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnClockMessage)
542 MESSAGE_HANDLER(WM_SETFONT, OnClockMessage)
543 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnPagerMessage)
544 MESSAGE_HANDLER(WM_COPYDATA, OnPagerMessage)
545 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
546 NOTIFY_CODE_HANDLER(NTNWM_REALIGN, OnRealign)
547 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
548 MESSAGE_HANDLER(TNWM_GETSHOWDESKTOPBUTTON, OnGetShowDesktopButton)
549 END_MSG_MAP()
550 };
551
CTrayNotifyWnd_CreateInstance(HWND hwndParent,REFIID riid,void ** ppv)552 HRESULT CTrayNotifyWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
553 {
554 return ShellObjectCreatorInit<CTrayNotifyWnd>(hwndParent, riid, ppv);
555 }
556