f21ba2c110679817338a7c5e2e0fe7c8f646959f
[reactos.git] / base / shell / explorer / trayclock.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 * TrayClockWnd
26 */
27
28 const struct
29 {
30 BOOL IsTime;
31 DWORD dwFormatFlags;
32 LPCWSTR lpFormat;
33 } ClockWndFormats[] = {
34 { TRUE, 0, NULL },
35 { FALSE, 0, L"dddd" },
36 { FALSE, DATE_SHORTDATE, NULL }
37 };
38 const UINT ClockWndFormatsCount = _ARRAYSIZE(ClockWndFormats);
39
40 #define CLOCKWND_FORMAT_COUNT ClockWndFormatsCount
41
42 static const WCHAR szTrayClockWndClass[] = L"TrayClockWClass";
43
44 class CTrayClockWnd :
45 public CComCoClass<CTrayClockWnd>,
46 public CComObjectRootEx<CComMultiThreadModelNoCS>,
47 public CWindowImpl < CTrayClockWnd, CWindow, CControlWinTraits >,
48 public IOleWindow
49 {
50 HFONT hFont;
51 COLORREF textColor;
52 RECT rcText;
53 SYSTEMTIME LocalTime;
54 CTooltips m_tooltip;
55
56 union
57 {
58 DWORD dwFlags;
59 struct
60 {
61 DWORD IsTimerEnabled : 1;
62 DWORD IsInitTimerEnabled : 1;
63 DWORD LinesMeasured : 1;
64 DWORD IsHorizontal : 1;
65 };
66 };
67 DWORD LineSpacing;
68 SIZE CurrentSize;
69 WORD VisibleLines;
70 SIZE LineSizes[CLOCKWND_FORMAT_COUNT];
71 WCHAR szLines[CLOCKWND_FORMAT_COUNT][48];
72
73 public:
74 CTrayClockWnd();
75 virtual ~CTrayClockWnd();
76
77 private:
78 LRESULT OnThemeChanged();
79 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
80
81 BOOL MeasureLines();
82 WORD GetMinimumSize(IN BOOL Horizontal, IN OUT PSIZE pSize);
83 VOID UpdateWnd();
84 VOID Update();
85 UINT CalculateDueTime();
86 BOOL ResetTime();
87 VOID CalibrateTimer();
88 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
89 LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
90 VOID SetFont(IN HFONT hNewFont, IN BOOL bRedraw);
91 LRESULT DrawBackground(HDC hdc);
92 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
93 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
94 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
95 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
96 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
97 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
98 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
99 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
100 LRESULT OnLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
101
102 public:
103
104 HRESULT WINAPI GetWindow(HWND* phwnd)
105 {
106 if (!phwnd)
107 return E_INVALIDARG;
108 *phwnd = m_hWnd;
109 return S_OK;
110 }
111
112 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
113 {
114 return E_NOTIMPL;
115 }
116
117 DECLARE_NOT_AGGREGATABLE(CTrayClockWnd)
118
119 DECLARE_PROTECT_FINAL_CONSTRUCT()
120 BEGIN_COM_MAP(CTrayClockWnd)
121 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
122 END_COM_MAP()
123
124 DECLARE_WND_CLASS_EX(szTrayClockWndClass, CS_DBLCLKS, COLOR_3DFACE)
125
126 BEGIN_MSG_MAP(CTrayClockWnd)
127 MESSAGE_HANDLER(WM_CREATE, OnCreate)
128 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
129 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
130 MESSAGE_HANDLER(WM_SIZE, OnSize)
131 MESSAGE_HANDLER(WM_PAINT, OnPaint)
132 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
133 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
134 MESSAGE_HANDLER(WM_TIMER, OnTimer)
135 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
136 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
137 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
138 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
139 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClick)
140 END_MSG_MAP()
141
142 HRESULT Initialize(IN HWND hWndParent);
143 };
144
145 #define ID_TRAYCLOCK_TIMER 0
146 #define ID_TRAYCLOCK_TIMER_INIT 1
147
148 #define TRAY_CLOCK_WND_SPACING_X 5
149 #define TRAY_CLOCK_WND_SPACING_Y 0
150
151 CTrayClockWnd::CTrayClockWnd() :
152 hFont(NULL),
153 dwFlags(0),
154 LineSpacing(0),
155 VisibleLines(0)
156 {
157 ZeroMemory(&textColor, sizeof(textColor));
158 ZeroMemory(&rcText, sizeof(rcText));
159 ZeroMemory(&LocalTime, sizeof(LocalTime));
160 ZeroMemory(&CurrentSize, sizeof(CurrentSize));
161 ZeroMemory(LineSizes, sizeof(LineSizes));
162 ZeroMemory(szLines, sizeof(szLines));
163 }
164 CTrayClockWnd::~CTrayClockWnd() { }
165
166 LRESULT CTrayClockWnd::OnThemeChanged()
167 {
168 LOGFONTW clockFont;
169 HTHEME clockTheme;
170 HFONT hFont;
171
172 clockTheme = OpenThemeData(m_hWnd, L"Clock");
173
174 if (clockTheme)
175 {
176 GetThemeFont(clockTheme,
177 NULL,
178 CLP_TIME,
179 0,
180 TMT_FONT,
181 &clockFont);
182
183 hFont = CreateFontIndirectW(&clockFont);
184
185 GetThemeColor(clockTheme,
186 CLP_TIME,
187 0,
188 TMT_TEXTCOLOR,
189 &textColor);
190
191 if (this->hFont != NULL)
192 DeleteObject(this->hFont);
193
194 SetFont(hFont, FALSE);
195 }
196 else
197 {
198 /* We don't need to set a font here, our parent will use
199 * WM_SETFONT to set the right one when themes are not enabled. */
200 textColor = RGB(0, 0, 0);
201 }
202
203 CloseThemeData(clockTheme);
204
205 return TRUE;
206 }
207
208 LRESULT CTrayClockWnd::OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
209 {
210 return OnThemeChanged();
211 }
212
213 BOOL CTrayClockWnd::MeasureLines()
214 {
215 HDC hDC;
216 HFONT hPrevFont;
217 UINT c, i;
218 BOOL bRet = TRUE;
219
220 hDC = GetDC();
221 if (hDC != NULL)
222 {
223 if (hFont)
224 hPrevFont = (HFONT) SelectObject(hDC, hFont);
225
226 for (i = 0; i < CLOCKWND_FORMAT_COUNT && bRet; i++)
227 {
228 if (szLines[i][0] != L'\0' &&
229 !GetTextExtentPointW(hDC, szLines[i], wcslen(szLines[i]),
230 &LineSizes[i]))
231 {
232 bRet = FALSE;
233 break;
234 }
235 }
236
237 if (hFont)
238 SelectObject(hDC, hPrevFont);
239
240 ReleaseDC(hDC);
241
242 if (bRet)
243 {
244 LineSpacing = 0;
245
246 /* calculate the line spacing */
247 for (i = 0, c = 0; i < CLOCKWND_FORMAT_COUNT; i++)
248 {
249 if (LineSizes[i].cx > 0)
250 {
251 LineSpacing += LineSizes[i].cy;
252 c++;
253 }
254 }
255
256 if (c > 0)
257 {
258 /* We want a spacing of 1/2 line */
259 LineSpacing = (LineSpacing / c) / 2;
260 }
261
262 return TRUE;
263 }
264 }
265
266 return FALSE;
267 }
268
269 WORD CTrayClockWnd::GetMinimumSize(IN BOOL Horizontal, IN OUT PSIZE pSize)
270 {
271 WORD iLinesVisible = 0;
272 UINT i;
273 SIZE szMax = { 0, 0 };
274
275 if (!LinesMeasured)
276 LinesMeasured = MeasureLines();
277
278 if (!LinesMeasured)
279 return 0;
280
281 for (i = 0; i < CLOCKWND_FORMAT_COUNT; i++)
282 {
283 if (LineSizes[i].cx != 0)
284 {
285 if (iLinesVisible > 0)
286 {
287 if (Horizontal)
288 {
289 if (szMax.cy + LineSizes[i].cy + (LONG) LineSpacing >
290 pSize->cy - (2 * TRAY_CLOCK_WND_SPACING_Y))
291 {
292 break;
293 }
294 }
295 else
296 {
297 if (LineSizes[i].cx > pSize->cx - (2 * TRAY_CLOCK_WND_SPACING_X))
298 break;
299 }
300
301 /* Add line spacing */
302 szMax.cy += LineSpacing;
303 }
304
305 iLinesVisible++;
306
307 /* Increase maximum rectangle */
308 szMax.cy += LineSizes[i].cy;
309 if (LineSizes[i].cx > szMax.cx)
310 szMax.cx = LineSizes[i].cx;
311 }
312 }
313
314 szMax.cx += 2 * TRAY_CLOCK_WND_SPACING_X;
315 szMax.cy += 2 * TRAY_CLOCK_WND_SPACING_Y;
316
317 *pSize = szMax;
318
319 return iLinesVisible;
320 }
321
322 VOID CTrayClockWnd::UpdateWnd()
323 {
324 SIZE szPrevCurrent;
325 UINT BufSize, i;
326 INT iRet;
327 RECT rcClient;
328
329 ZeroMemory(LineSizes, sizeof(LineSizes));
330
331 szPrevCurrent = CurrentSize;
332
333 for (i = 0; i < CLOCKWND_FORMAT_COUNT; i++)
334 {
335 szLines[i][0] = L'\0';
336 BufSize = _countof(szLines[0]);
337
338 if (ClockWndFormats[i].IsTime)
339 {
340 iRet = GetTimeFormat(LOCALE_USER_DEFAULT,
341 g_TaskbarSettings.bShowSeconds ? ClockWndFormats[i].dwFormatFlags : TIME_NOSECONDS,
342 &LocalTime,
343 ClockWndFormats[i].lpFormat,
344 szLines[i],
345 BufSize);
346 }
347 else
348 {
349 iRet = GetDateFormat(LOCALE_USER_DEFAULT,
350 ClockWndFormats[i].dwFormatFlags,
351 &LocalTime,
352 ClockWndFormats[i].lpFormat,
353 szLines[i],
354 BufSize);
355 }
356
357 if (iRet != 0 && i == 0)
358 {
359 /* Set the window text to the time only */
360 SetWindowText(szLines[i]);
361 }
362 }
363
364 LinesMeasured = MeasureLines();
365
366 if (LinesMeasured &&
367 GetClientRect(&rcClient))
368 {
369 SIZE szWnd;
370
371 szWnd.cx = rcClient.right;
372 szWnd.cy = rcClient.bottom;
373
374 VisibleLines = GetMinimumSize(IsHorizontal, &szWnd);
375 CurrentSize = szWnd;
376 }
377
378 if (IsWindowVisible())
379 {
380 InvalidateRect(NULL, TRUE);
381
382 if (szPrevCurrent.cx != CurrentSize.cx ||
383 szPrevCurrent.cy != CurrentSize.cy)
384 {
385 /* Ask the parent to resize */
386 NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
387 GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
388 }
389 }
390
391 int iDateLength = GetDateFormat(LOCALE_USER_DEFAULT,
392 DATE_LONGDATE,
393 &LocalTime,
394 NULL,
395 NULL,
396 0);
397 if (iDateLength <= 0)
398 {
399 return;
400 }
401
402 WCHAR* szDate = new WCHAR[iDateLength];
403 if (GetDateFormat(LOCALE_USER_DEFAULT,
404 DATE_LONGDATE,
405 &LocalTime,
406 NULL,
407 szDate,
408 iDateLength) > 0)
409 {
410 m_tooltip.UpdateTipText(m_hWnd,
411 reinterpret_cast<UINT_PTR>(m_hWnd),
412 szDate);
413 }
414 delete[] szDate;
415 }
416
417 VOID CTrayClockWnd::Update()
418 {
419 GetLocalTime(&LocalTime);
420 UpdateWnd();
421 }
422
423 UINT CTrayClockWnd::CalculateDueTime()
424 {
425 UINT uiDueTime;
426
427 /* Calculate the due time */
428 GetLocalTime(&LocalTime);
429 uiDueTime = 1000 - (UINT) LocalTime.wMilliseconds;
430 if (g_TaskbarSettings.bShowSeconds)
431 uiDueTime += (UINT) LocalTime.wSecond * 100;
432 else
433 uiDueTime += (59 - (UINT) LocalTime.wSecond) * 1000;
434
435 if (uiDueTime < USER_TIMER_MINIMUM || uiDueTime > USER_TIMER_MAXIMUM)
436 uiDueTime = 1000;
437 else
438 {
439 /* Add an artificial delay of 0.05 seconds to make sure the timer
440 doesn't fire too early*/
441 uiDueTime += 50;
442 }
443
444 return uiDueTime;
445 }
446
447 BOOL CTrayClockWnd::ResetTime()
448 {
449 UINT uiDueTime;
450 BOOL Ret;
451
452 /* Disable all timers */
453 if (IsTimerEnabled)
454 {
455 KillTimer(ID_TRAYCLOCK_TIMER);
456 IsTimerEnabled = FALSE;
457 }
458
459 if (IsInitTimerEnabled)
460 {
461 KillTimer(ID_TRAYCLOCK_TIMER_INIT);
462 }
463
464 uiDueTime = CalculateDueTime();
465
466 /* Set the new timer */
467 Ret = SetTimer(ID_TRAYCLOCK_TIMER_INIT, uiDueTime, NULL) != 0;
468 IsInitTimerEnabled = Ret;
469
470 /* Update the time */
471 Update();
472
473 return Ret;
474 }
475
476 VOID CTrayClockWnd::CalibrateTimer()
477 {
478 UINT uiDueTime;
479 BOOL Ret;
480 UINT uiWait1, uiWait2;
481
482 /* Kill the initialization timer */
483 KillTimer(ID_TRAYCLOCK_TIMER_INIT);
484 IsInitTimerEnabled = FALSE;
485
486 uiDueTime = CalculateDueTime();
487
488 if (g_TaskbarSettings.bShowSeconds)
489 {
490 uiWait1 = 1000 - 200;
491 uiWait2 = 1000;
492 }
493 else
494 {
495 uiWait1 = 60 * 1000 - 200;
496 uiWait2 = 60 * 1000;
497 }
498
499 if (uiDueTime > uiWait1)
500 {
501 /* The update of the clock will be up to 200 ms late, but that's
502 acceptable. We're going to setup a timer that fires depending
503 uiWait2. */
504 Ret = SetTimer(ID_TRAYCLOCK_TIMER, uiWait2, NULL) != 0;
505 IsTimerEnabled = Ret;
506
507 /* Update the time */
508 Update();
509 }
510 else
511 {
512 /* Recalibrate the timer and recalculate again when the current
513 minute/second ends. */
514 ResetTime();
515 }
516 }
517
518 LRESULT CTrayClockWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
519 {
520 /* Disable all timers */
521 if (IsTimerEnabled)
522 {
523 KillTimer(ID_TRAYCLOCK_TIMER);
524 }
525
526 if (IsInitTimerEnabled)
527 {
528 KillTimer(ID_TRAYCLOCK_TIMER_INIT);
529 }
530
531 return TRUE;
532 }
533
534 LRESULT CTrayClockWnd::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
535 {
536 RECT rcClient;
537 HFONT hPrevFont;
538 INT iPrevBkMode;
539 UINT i, line;
540
541 PAINTSTRUCT ps;
542 HDC hDC = (HDC) wParam;
543
544 if (wParam == 0)
545 {
546 hDC = BeginPaint(&ps);
547 }
548
549 if (hDC == NULL)
550 return FALSE;
551
552 if (LinesMeasured &&
553 GetClientRect(&rcClient))
554 {
555 iPrevBkMode = SetBkMode(hDC, TRANSPARENT);
556
557 SetTextColor(hDC, textColor);
558
559 hPrevFont = (HFONT) SelectObject(hDC, hFont);
560
561 rcClient.top = (rcClient.bottom - CurrentSize.cy) / 2;
562 rcClient.bottom = rcClient.top + CurrentSize.cy;
563
564 for (i = 0, line = 0;
565 i < CLOCKWND_FORMAT_COUNT && line < VisibleLines;
566 i++)
567 {
568 if (LineSizes[i].cx != 0)
569 {
570 TextOut(hDC,
571 (rcClient.right - LineSizes[i].cx) / 2,
572 rcClient.top + TRAY_CLOCK_WND_SPACING_Y,
573 szLines[i],
574 wcslen(szLines[i]));
575
576 rcClient.top += LineSizes[i].cy + LineSpacing;
577 line++;
578 }
579 }
580
581 SelectObject(hDC, hPrevFont);
582
583 SetBkMode(hDC, iPrevBkMode);
584 }
585
586 if (wParam == 0)
587 {
588 EndPaint(&ps);
589 }
590
591 return TRUE;
592 }
593
594 VOID CTrayClockWnd::SetFont(IN HFONT hNewFont, IN BOOL bRedraw)
595 {
596 hFont = hNewFont;
597 LinesMeasured = MeasureLines();
598 if (bRedraw)
599 {
600 InvalidateRect(NULL, TRUE);
601 }
602 }
603
604 LRESULT CTrayClockWnd::DrawBackground(HDC hdc)
605 {
606 RECT rect;
607
608 GetClientRect(&rect);
609 DrawThemeParentBackground(m_hWnd, hdc, &rect);
610
611 return TRUE;
612 }
613
614 LRESULT CTrayClockWnd::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
615 {
616 HDC hdc = (HDC) wParam;
617
618 if (!IsAppThemed())
619 {
620 bHandled = FALSE;
621 return 0;
622 }
623
624 return DrawBackground(hdc);
625 }
626
627 LRESULT CTrayClockWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
628 {
629 switch (wParam)
630 {
631 case ID_TRAYCLOCK_TIMER:
632 Update();
633 break;
634
635 case ID_TRAYCLOCK_TIMER_INIT:
636 CalibrateTimer();
637 break;
638 }
639 return TRUE;
640 }
641
642 LRESULT CTrayClockWnd::OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
643 {
644 IsHorizontal = (BOOL) wParam;
645
646 return (LRESULT) GetMinimumSize((BOOL) wParam, (PSIZE) lParam) != 0;
647 }
648
649 LRESULT CTrayClockWnd::OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
650 {
651 // HTCLIENT is returned to receive WM_MOUSEMOVE messages for the tooltip
652 return HTCLIENT;
653 }
654
655 LRESULT CTrayClockWnd::OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
656 {
657 SetFont((HFONT) wParam, (BOOL) LOWORD(lParam));
658 return TRUE;
659 }
660
661 LRESULT CTrayClockWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
662 {
663 m_tooltip.Create(m_hWnd, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP);
664
665 TOOLINFOW ti = { 0 };
666 ti.cbSize = TTTOOLINFOW_V1_SIZE;
667 ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
668 ti.hwnd = m_hWnd;
669 ti.uId = reinterpret_cast<UINT_PTR>(m_hWnd);
670 ti.lpszText = NULL;
671 ti.lParam = NULL;
672
673 m_tooltip.AddTool(&ti);
674
675 ResetTime();
676 return TRUE;
677 }
678
679 LRESULT CTrayClockWnd::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
680 {
681 SIZE szClient;
682
683 szClient.cx = LOWORD(lParam);
684 szClient.cy = HIWORD(lParam);
685
686 VisibleLines = GetMinimumSize(IsHorizontal, &szClient);
687 CurrentSize = szClient;
688
689 InvalidateRect(NULL, TRUE);
690 return TRUE;
691 }
692
693 LRESULT CTrayClockWnd::OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
694 {
695 BOOL bRealign = FALSE;
696
697 TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
698 if (newSettings->bShowSeconds != g_TaskbarSettings.bShowSeconds)
699 {
700 g_TaskbarSettings.bShowSeconds = newSettings->bShowSeconds;
701 bRealign = TRUE;
702 }
703
704 if (newSettings->sr.HideClock != g_TaskbarSettings.sr.HideClock)
705 {
706 g_TaskbarSettings.sr.HideClock = newSettings->sr.HideClock;
707 ShowWindow(g_TaskbarSettings.sr.HideClock ? SW_HIDE : SW_SHOW);
708 bRealign = TRUE;
709 }
710
711 if (bRealign)
712 {
713 /* Ask the parent to resize */
714 NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
715 GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
716 Update();
717 }
718 return 0;
719 }
720
721 LRESULT CTrayClockWnd::OnLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
722 {
723 if (IsWindowVisible())
724 {
725 //FIXME: use SHRunControlPanel
726 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
727 }
728 return TRUE;
729 }
730
731 HRESULT CTrayClockWnd::Initialize(IN HWND hWndParent)
732 {
733 IsHorizontal = TRUE;
734
735 /* Create the window. The tray window is going to move it to the correct
736 position and resize it as needed. */
737 DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
738 if (!g_TaskbarSettings.sr.HideClock)
739 dwStyle |= WS_VISIBLE;
740
741 Create(hWndParent, 0, NULL, dwStyle);
742 if (!m_hWnd)
743 return E_FAIL;
744
745 SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
746
747 return S_OK;
748
749 };
750
751 HRESULT CTrayClockWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
752 {
753 return ShellObjectCreatorInit<CTrayClockWnd>(hwndParent, riid, ppv);
754 }