xref: /reactos/base/shell/explorer/trayclock.cpp (revision 8a978a17)
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 OnContextMenu(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_CONTEXTMENU, OnContextMenu)
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     GetLocalTime(&LocalTime);
428     uiDueTime = 1000 - (UINT) LocalTime.wMilliseconds;
429     if (!g_TaskbarSettings.bShowSeconds)
430         uiDueTime += (59 - (UINT) LocalTime.wSecond) * 1000;
431 
432     if (uiDueTime < USER_TIMER_MINIMUM || uiDueTime > USER_TIMER_MAXIMUM)
433         uiDueTime = 1000;
434     else
435     {
436         /* Add an artificial delay of 0.05 seconds to make sure the timer
437             doesn't fire too early*/
438         uiDueTime += 50;
439     }
440 
441     return uiDueTime;
442 }
443 
444 BOOL CTrayClockWnd::ResetTime()
445 {
446     UINT uiDueTime;
447     BOOL Ret;
448 
449     /* Disable all timers */
450     if (IsTimerEnabled)
451     {
452         KillTimer(ID_TRAYCLOCK_TIMER);
453         IsTimerEnabled = FALSE;
454     }
455     else if (IsInitTimerEnabled)
456     {
457         KillTimer(ID_TRAYCLOCK_TIMER_INIT);
458     }
459 
460     uiDueTime = CalculateDueTime();
461 
462     /* Set the new timer */
463     Ret = SetTimer(ID_TRAYCLOCK_TIMER_INIT, uiDueTime, NULL) != 0;
464     IsInitTimerEnabled = Ret;
465 
466     return Ret;
467 }
468 
469 VOID CTrayClockWnd::CalibrateTimer()
470 {
471     UINT uiDueTime;
472     BOOL Ret;
473     UINT uiWait1, uiWait2;
474 
475     /* Kill the initialization timer */
476     KillTimer(ID_TRAYCLOCK_TIMER_INIT);
477     IsInitTimerEnabled = FALSE;
478 
479     uiDueTime = CalculateDueTime();
480 
481     if (g_TaskbarSettings.bShowSeconds)
482     {
483         uiWait1 = 1000 - 200;
484         uiWait2 = 1000;
485     }
486     else
487     {
488         uiWait1 = 60 * 1000 - 200;
489         uiWait2 = 60 * 1000;
490     }
491 
492     if (uiDueTime > uiWait1)
493     {
494         /* The update of the clock will be up to 200 ms late, but that's
495             acceptable. We're going to setup a timer that fires depending
496             uiWait2. */
497         Ret = SetTimer(ID_TRAYCLOCK_TIMER, uiWait2, NULL) != 0;
498         IsTimerEnabled = Ret;
499     }
500     else
501     {
502         /* Recalibrate the timer and recalculate again when the current
503             minute/second ends. */
504         ResetTime();
505     }
506 
507     /* Update the time */
508     Update();
509 }
510 
511 LRESULT CTrayClockWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
512 {
513     /* Disable all timers */
514     if (IsTimerEnabled)
515     {
516         KillTimer(ID_TRAYCLOCK_TIMER);
517     }
518     else if (IsInitTimerEnabled)
519     {
520         KillTimer(ID_TRAYCLOCK_TIMER_INIT);
521     }
522 
523     return TRUE;
524 }
525 
526 LRESULT CTrayClockWnd::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
527 {
528     RECT rcClient;
529     HFONT hPrevFont;
530     INT iPrevBkMode;
531     UINT i, line;
532 
533     PAINTSTRUCT ps;
534     HDC hDC = (HDC) wParam;
535 
536     if (wParam == 0)
537     {
538         hDC = BeginPaint(&ps);
539     }
540 
541     if (hDC == NULL)
542         return FALSE;
543 
544     if (LinesMeasured &&
545         GetClientRect(&rcClient))
546     {
547         iPrevBkMode = SetBkMode(hDC, TRANSPARENT);
548 
549         SetTextColor(hDC, textColor);
550 
551         hPrevFont = (HFONT) SelectObject(hDC, hFont);
552 
553         rcClient.top = (rcClient.bottom - CurrentSize.cy) / 2;
554         rcClient.bottom = rcClient.top + CurrentSize.cy;
555 
556         for (i = 0, line = 0;
557                 i < CLOCKWND_FORMAT_COUNT && line < VisibleLines;
558                 i++)
559         {
560             if (LineSizes[i].cx != 0)
561             {
562                 TextOut(hDC,
563                     (rcClient.right - LineSizes[i].cx) / 2,
564                     rcClient.top + TRAY_CLOCK_WND_SPACING_Y,
565                     szLines[i],
566                     wcslen(szLines[i]));
567 
568                 rcClient.top += LineSizes[i].cy + LineSpacing;
569                 line++;
570             }
571         }
572 
573         SelectObject(hDC, hPrevFont);
574 
575         SetBkMode(hDC, iPrevBkMode);
576     }
577 
578     if (wParam == 0)
579     {
580         EndPaint(&ps);
581     }
582 
583     return TRUE;
584 }
585 
586 VOID CTrayClockWnd::SetFont(IN HFONT hNewFont, IN BOOL bRedraw)
587 {
588     hFont = hNewFont;
589     LinesMeasured = MeasureLines();
590     if (bRedraw)
591     {
592         InvalidateRect(NULL, TRUE);
593     }
594 }
595 
596 LRESULT CTrayClockWnd::DrawBackground(HDC hdc)
597 {
598     RECT rect;
599 
600     GetClientRect(&rect);
601     DrawThemeParentBackground(m_hWnd, hdc, &rect);
602 
603     return TRUE;
604 }
605 
606 LRESULT CTrayClockWnd::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
607 {
608     HDC hdc = (HDC) wParam;
609 
610     if (!IsAppThemed())
611     {
612         bHandled = FALSE;
613         return 0;
614     }
615 
616     return DrawBackground(hdc);
617 }
618 
619 LRESULT CTrayClockWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
620 {
621     switch (wParam)
622     {
623     case ID_TRAYCLOCK_TIMER:
624         Update();
625         break;
626 
627     case ID_TRAYCLOCK_TIMER_INIT:
628         CalibrateTimer();
629         break;
630     }
631     return TRUE;
632 }
633 
634 LRESULT CTrayClockWnd::OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
635 {
636     IsHorizontal = (BOOL) wParam;
637 
638     return (LRESULT) GetMinimumSize((BOOL) wParam, (PSIZE) lParam) != 0;
639 }
640 
641 LRESULT CTrayClockWnd::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
642 {
643     return GetParent().SendMessage(uMsg, wParam, lParam);
644 }
645 
646 LRESULT CTrayClockWnd::OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
647 {
648     SetFont((HFONT) wParam, (BOOL) LOWORD(lParam));
649     return TRUE;
650 }
651 
652 LRESULT CTrayClockWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
653 {
654     m_tooltip.Create(m_hWnd, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP);
655 
656     TOOLINFOW ti = { 0 };
657     ti.cbSize = TTTOOLINFOW_V1_SIZE;
658     ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
659     ti.hwnd = m_hWnd;
660     ti.uId = reinterpret_cast<UINT_PTR>(m_hWnd);
661     ti.lpszText = NULL;
662     ti.lParam = NULL;
663 
664     m_tooltip.AddTool(&ti);
665 
666     if (!g_TaskbarSettings.sr.HideClock)
667     {
668         ResetTime();
669     }
670 
671     /* Update the time */
672     Update();
673 
674     return TRUE;
675 }
676 
677 LRESULT CTrayClockWnd::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
678 {
679     SIZE szClient;
680 
681     szClient.cx = LOWORD(lParam);
682     szClient.cy = HIWORD(lParam);
683 
684     VisibleLines = GetMinimumSize(IsHorizontal, &szClient);
685     CurrentSize = szClient;
686 
687     InvalidateRect(NULL, TRUE);
688     return TRUE;
689 }
690 
691 LRESULT CTrayClockWnd::OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
692 {
693     BOOL bRealign = FALSE;
694 
695     TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
696     if (newSettings->bShowSeconds != g_TaskbarSettings.bShowSeconds)
697     {
698         g_TaskbarSettings.bShowSeconds = newSettings->bShowSeconds;
699         if (!g_TaskbarSettings.sr.HideClock)
700         {
701             bRealign = TRUE;
702 
703             ResetTime();
704         }
705     }
706 
707     if (newSettings->sr.HideClock != g_TaskbarSettings.sr.HideClock)
708     {
709         g_TaskbarSettings.sr.HideClock = newSettings->sr.HideClock;
710         ShowWindow(g_TaskbarSettings.sr.HideClock ? SW_HIDE : SW_SHOW);
711         bRealign = TRUE;
712 
713         if (g_TaskbarSettings.sr.HideClock)
714         {
715             /* Disable all timers */
716             if (IsTimerEnabled)
717             {
718                 KillTimer(ID_TRAYCLOCK_TIMER);
719                 IsTimerEnabled = FALSE;
720             }
721             else if (IsInitTimerEnabled)
722             {
723                 KillTimer(ID_TRAYCLOCK_TIMER_INIT);
724                 IsInitTimerEnabled = FALSE;
725             }
726         }
727         else
728         {
729             ResetTime();
730         }
731     }
732 
733     if (bRealign)
734     {
735         /* Ask the parent to resize */
736         NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
737         GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
738         Update();
739     }
740     return 0;
741 }
742 
743 LRESULT CTrayClockWnd::OnLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
744 {
745     if (IsWindowVisible())
746     {
747         //FIXME: use SHRunControlPanel
748         ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
749     }
750     return TRUE;
751 }
752 
753 HRESULT CTrayClockWnd::Initialize(IN HWND hWndParent)
754 {
755     IsHorizontal = TRUE;
756 
757     /* Create the window. The tray window is going to move it to the correct
758         position and resize it as needed. */
759     DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
760     if (!g_TaskbarSettings.sr.HideClock)
761         dwStyle |= WS_VISIBLE;
762 
763     Create(hWndParent, 0, NULL, dwStyle);
764     if (!m_hWnd)
765         return E_FAIL;
766 
767     SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
768 
769     return S_OK;
770 
771 };
772 
773 HRESULT CTrayClockWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
774 {
775     return ShellObjectCreatorInit<CTrayClockWnd>(hwndParent, riid, ppv);
776 }
777