xref: /reactos/base/shell/explorer/trayclock.cpp (revision cce399e7)
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 #define CLOCKWND_FORMAT_TIME 0
42 #define CLOCKWND_FORMAT_DAY  1
43 #define CLOCKWND_FORMAT_DATE 2
44 
45 static const WCHAR szTrayClockWndClass[] = L"TrayClockWClass";
46 
47 class CTrayClockWnd :
48     public CComCoClass<CTrayClockWnd>,
49     public CComObjectRootEx<CComMultiThreadModelNoCS>,
50     public CWindowImpl < CTrayClockWnd, CWindow, CControlWinTraits >,
51     public IOleWindow
52 {
53     HFONT hFont;
54     COLORREF textColor;
55     RECT rcText;
56     SYSTEMTIME LocalTime;
57     CTooltips m_tooltip;
58 
59     union
60     {
61         DWORD dwFlags;
62         struct
63         {
64             DWORD IsTimerEnabled : 1;
65             DWORD IsInitTimerEnabled : 1;
66             DWORD LinesMeasured : 1;
67             DWORD IsHorizontal : 1;
68         };
69     };
70     DWORD LineSpacing;
71     SIZE CurrentSize;
72     WORD VisibleLines;
73     SIZE LineSizes[CLOCKWND_FORMAT_COUNT];
74     WCHAR szLines[CLOCKWND_FORMAT_COUNT][48];
75 
76 public:
77     CTrayClockWnd();
78     virtual ~CTrayClockWnd();
79 
80 private:
81     LRESULT OnThemeChanged();
82     LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
83 
84     BOOL MeasureLines();
85     WORD GetMinimumSize(IN BOOL Horizontal, IN OUT PSIZE pSize);
86     VOID UpdateWnd();
87     VOID Update();
88     UINT CalculateDueTime();
89     BOOL ResetTime();
90     VOID CalibrateTimer();
91     LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
92     LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
93     VOID SetFont(IN HFONT hNewFont, IN BOOL bRedraw);
94     LRESULT DrawBackground(HDC hdc);
95     LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
96     LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
97     LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
98     LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
99     LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
100     LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
101     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
102     LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
103     LRESULT OnLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
104     VOID PaintLine(IN HDC hDC, IN OUT RECT *rcClient, IN UINT LineNumber, IN UINT szLinesIndex);
105 
106 public:
107 
108     HRESULT WINAPI GetWindow(HWND* phwnd)
109     {
110         if (!phwnd)
111             return E_INVALIDARG;
112         *phwnd = m_hWnd;
113         return S_OK;
114     }
115 
116     HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
117     {
118         return E_NOTIMPL;
119     }
120 
121     DECLARE_NOT_AGGREGATABLE(CTrayClockWnd)
122 
123     DECLARE_PROTECT_FINAL_CONSTRUCT()
124     BEGIN_COM_MAP(CTrayClockWnd)
125         COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
126     END_COM_MAP()
127 
128     DECLARE_WND_CLASS_EX(szTrayClockWndClass, CS_DBLCLKS, COLOR_3DFACE)
129 
130     BEGIN_MSG_MAP(CTrayClockWnd)
131         MESSAGE_HANDLER(WM_CREATE, OnCreate)
132         MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
133         MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
134         MESSAGE_HANDLER(WM_SIZE, OnSize)
135         MESSAGE_HANDLER(WM_PAINT, OnPaint)
136         MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
137         MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
138         MESSAGE_HANDLER(WM_TIMER, OnTimer)
139         MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
140         MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
141         MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
142         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
143         MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClick)
144     END_MSG_MAP()
145 
146     HRESULT Initialize(IN HWND hWndParent);
147 };
148 
149 #define ID_TRAYCLOCK_TIMER  0
150 #define ID_TRAYCLOCK_TIMER_INIT 1
151 
152 #define TRAY_CLOCK_WND_SPACING_X    5
153 #define TRAY_CLOCK_WND_SPACING_Y    0
154 
155 CTrayClockWnd::CTrayClockWnd() :
156         hFont(NULL),
157         textColor(0),
158         dwFlags(0),
159         LineSpacing(0),
160         VisibleLines(0)
161 {
162     ZeroMemory(&rcText, sizeof(rcText));
163     ZeroMemory(&LocalTime, sizeof(LocalTime));
164     ZeroMemory(&CurrentSize, sizeof(CurrentSize));
165     ZeroMemory(LineSizes, sizeof(LineSizes));
166     ZeroMemory(szLines, sizeof(szLines));
167 }
168 CTrayClockWnd::~CTrayClockWnd() { }
169 
170 LRESULT CTrayClockWnd::OnThemeChanged()
171 {
172     LOGFONTW clockFont;
173     HTHEME clockTheme;
174     HFONT hFont;
175 
176     clockTheme = OpenThemeData(m_hWnd, L"Clock");
177 
178     if (clockTheme)
179     {
180         GetThemeFont(clockTheme, NULL, CLP_TIME, 0, TMT_FONT, &clockFont);
181 
182         hFont = CreateFontIndirectW(&clockFont);
183 
184         GetThemeColor(clockTheme, CLP_TIME, 0, TMT_TEXTCOLOR, &textColor);
185 
186         if (this->hFont != NULL)
187             DeleteObject(this->hFont);
188 
189         SetFont(hFont, FALSE);
190 
191         CloseThemeData(clockTheme);
192     }
193 
194     return TRUE;
195 }
196 
197 LRESULT CTrayClockWnd::OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
198 {
199     return OnThemeChanged();
200 }
201 
202 BOOL CTrayClockWnd::MeasureLines()
203 {
204     HDC hDC;
205     HFONT hPrevFont;
206     UINT c, i;
207     BOOL bRet = TRUE;
208 
209     hDC = GetDC();
210     if (hDC != NULL)
211     {
212         if (hFont)
213             hPrevFont = (HFONT) SelectObject(hDC, hFont);
214 
215         for (i = 0; i < CLOCKWND_FORMAT_COUNT && bRet; i++)
216         {
217             if (szLines[i][0] != L'\0' &&
218                 !GetTextExtentPointW(hDC, szLines[i], wcslen(szLines[i]),
219                                         &LineSizes[i]))
220             {
221                 bRet = FALSE;
222                 break;
223             }
224         }
225 
226         if (hFont)
227             SelectObject(hDC, hPrevFont);
228 
229         ReleaseDC(hDC);
230 
231         if (bRet)
232         {
233             LineSpacing = 0;
234 
235             /* calculate the line spacing */
236             for (i = 0, c = 0; i < CLOCKWND_FORMAT_COUNT; i++)
237             {
238                 if (LineSizes[i].cx > 0)
239                 {
240                     LineSpacing += LineSizes[i].cy;
241                     c++;
242                 }
243             }
244 
245             if (c > 0)
246             {
247                 /* We want a spacing of 1/2 line */
248                 LineSpacing = (LineSpacing / c) / 2;
249             }
250 
251             return TRUE;
252         }
253     }
254 
255     return FALSE;
256 }
257 
258 WORD CTrayClockWnd::GetMinimumSize(IN BOOL Horizontal, IN OUT PSIZE pSize)
259 {
260     WORD iLinesVisible = 0;
261     UINT i;
262     SIZE szMax = { 0, 0 };
263 
264     if (!LinesMeasured)
265         LinesMeasured = MeasureLines();
266 
267     if (!LinesMeasured)
268         return 0;
269 
270     /* Prevents the date from being cut off when the day of the week is shorter than the date. */
271     if (VisibleLines > 1 && g_TaskbarSettings.bPreferDate)
272         szMax.cx = LineSizes[CLOCKWND_FORMAT_DATE].cx;
273 
274     for (i = 0; i < CLOCKWND_FORMAT_COUNT; i++)
275     {
276         if (LineSizes[i].cx != 0)
277         {
278             if (iLinesVisible > 0)
279             {
280                 if (Horizontal)
281                 {
282                     if (szMax.cy + LineSizes[i].cy + (LONG) LineSpacing >
283                         pSize->cy - (2 * TRAY_CLOCK_WND_SPACING_Y))
284                     {
285                         break;
286                     }
287                 }
288                 else
289                 {
290                     if (LineSizes[i].cx > pSize->cx - (2 * TRAY_CLOCK_WND_SPACING_X))
291                         break;
292                 }
293 
294                 /* Add line spacing */
295                 szMax.cy += LineSpacing;
296             }
297 
298             iLinesVisible++;
299 
300             /* Increase maximum rectangle */
301             szMax.cy += LineSizes[i].cy;
302             if (LineSizes[i].cx > szMax.cx)
303                 szMax.cx = LineSizes[i].cx;
304         }
305     }
306 
307     szMax.cx += 2 * TRAY_CLOCK_WND_SPACING_X;
308     szMax.cy += 2 * TRAY_CLOCK_WND_SPACING_Y;
309 
310     *pSize = szMax;
311 
312     return iLinesVisible;
313 }
314 
315 VOID CTrayClockWnd::UpdateWnd()
316 {
317     SIZE szPrevCurrent;
318     UINT BufSize, i;
319     INT iRet;
320     RECT rcClient;
321 
322     ZeroMemory(LineSizes, sizeof(LineSizes));
323 
324     szPrevCurrent = CurrentSize;
325 
326     for (i = 0; i < CLOCKWND_FORMAT_COUNT; i++)
327     {
328         szLines[i][0] = L'\0';
329         BufSize = _countof(szLines[0]);
330 
331         if (ClockWndFormats[i].IsTime)
332         {
333             iRet = GetTimeFormat(LOCALE_USER_DEFAULT,
334                 g_TaskbarSettings.bShowSeconds ? ClockWndFormats[i].dwFormatFlags : TIME_NOSECONDS,
335                 &LocalTime,
336                 ClockWndFormats[i].lpFormat,
337                 szLines[i],
338                 BufSize);
339         }
340         else
341         {
342             iRet = GetDateFormat(LOCALE_USER_DEFAULT,
343                 ClockWndFormats[i].dwFormatFlags,
344                 &LocalTime,
345                 ClockWndFormats[i].lpFormat,
346                 szLines[i],
347                 BufSize);
348         }
349 
350         if (iRet != 0 && i == 0)
351         {
352             /* Set the window text to the time only */
353             SetWindowText(szLines[i]);
354         }
355     }
356 
357     LinesMeasured = MeasureLines();
358 
359     if (LinesMeasured &&
360         GetClientRect(&rcClient))
361     {
362         SIZE szWnd;
363 
364         szWnd.cx = rcClient.right;
365         szWnd.cy = rcClient.bottom;
366 
367         VisibleLines = GetMinimumSize(IsHorizontal, &szWnd);
368         CurrentSize = szWnd;
369     }
370 
371     if (IsWindowVisible())
372     {
373         InvalidateRect(NULL, TRUE);
374 
375         if (szPrevCurrent.cx != CurrentSize.cx ||
376             szPrevCurrent.cy != CurrentSize.cy)
377         {
378             /* Ask the parent to resize */
379             NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
380             GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
381         }
382     }
383 
384     int iDateLength = GetDateFormat(LOCALE_USER_DEFAULT,
385                                           DATE_LONGDATE,
386                                           &LocalTime,
387                                           NULL,
388                                           NULL,
389                                           0);
390     if (iDateLength <= 0)
391     {
392         return;
393     }
394 
395     WCHAR* szDate = new WCHAR[iDateLength];
396     if (GetDateFormat(LOCALE_USER_DEFAULT,
397                       DATE_LONGDATE,
398                       &LocalTime,
399                       NULL,
400                       szDate,
401                       iDateLength) > 0)
402     {
403         m_tooltip.UpdateTipText(m_hWnd,
404                                 reinterpret_cast<UINT_PTR>(m_hWnd),
405                                 szDate);
406     }
407     delete[] szDate;
408 }
409 
410 VOID CTrayClockWnd::Update()
411 {
412     GetLocalTime(&LocalTime);
413     UpdateWnd();
414 }
415 
416 UINT CTrayClockWnd::CalculateDueTime()
417 {
418     UINT uiDueTime;
419 
420     GetLocalTime(&LocalTime);
421     uiDueTime = 1000 - (UINT) LocalTime.wMilliseconds;
422     if (!g_TaskbarSettings.bShowSeconds)
423         uiDueTime += (59 - (UINT) LocalTime.wSecond) * 1000;
424 
425     return uiDueTime;
426 }
427 
428 BOOL CTrayClockWnd::ResetTime()
429 {
430     UINT uiDueTime;
431     BOOL Ret;
432 
433     /* Disable all timers */
434     if (IsTimerEnabled)
435     {
436         KillTimer(ID_TRAYCLOCK_TIMER);
437         IsTimerEnabled = FALSE;
438     }
439     else if (IsInitTimerEnabled)
440     {
441         KillTimer(ID_TRAYCLOCK_TIMER_INIT);
442     }
443 
444     uiDueTime = CalculateDueTime();
445 
446     /* Set the new timer */
447     Ret = SetTimer(ID_TRAYCLOCK_TIMER_INIT, uiDueTime, NULL) != 0;
448     IsInitTimerEnabled = Ret;
449 
450     return Ret;
451 }
452 
453 VOID CTrayClockWnd::CalibrateTimer()
454 {
455     UINT uiDueTime;
456     BOOL Ret;
457     UINT uiWait1, uiWait2;
458 
459     /* Kill the initialization timer */
460     KillTimer(ID_TRAYCLOCK_TIMER_INIT);
461     IsInitTimerEnabled = FALSE;
462 
463     uiDueTime = CalculateDueTime();
464 
465     if (g_TaskbarSettings.bShowSeconds)
466     {
467         uiWait1 = 1000 - 200;
468         uiWait2 = 1000;
469     }
470     else
471     {
472         uiWait1 = 60 * 1000 - 200;
473         uiWait2 = 60 * 1000;
474     }
475 
476     if (uiDueTime > uiWait1)
477     {
478         /* The update of the clock will be up to 200 ms late, but that's
479             acceptable. We're going to setup a timer that fires depending
480             uiWait2. */
481         Ret = SetTimer(ID_TRAYCLOCK_TIMER, uiWait2, NULL) != 0;
482         IsTimerEnabled = Ret;
483     }
484     else
485     {
486         /* Recalibrate the timer and recalculate again when the current
487             minute/second ends. */
488         ResetTime();
489     }
490 
491     /* Update the time */
492     Update();
493 }
494 
495 LRESULT CTrayClockWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
496 {
497     /* Disable all timers */
498     if (IsTimerEnabled)
499     {
500         KillTimer(ID_TRAYCLOCK_TIMER);
501     }
502     else if (IsInitTimerEnabled)
503     {
504         KillTimer(ID_TRAYCLOCK_TIMER_INIT);
505     }
506 
507     return TRUE;
508 }
509 
510 LRESULT CTrayClockWnd::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
511 {
512     RECT rcClient;
513     HFONT hPrevFont;
514     INT iPrevBkMode;
515     UINT i, line;
516     PAINTSTRUCT ps;
517     HDC hDC = (HDC) wParam;
518 
519     if (wParam == 0)
520         hDC = BeginPaint(&ps);
521 
522     if (hDC == NULL)
523         return FALSE;
524 
525     if (LinesMeasured &&
526         GetClientRect(&rcClient))
527     {
528         iPrevBkMode = SetBkMode(hDC, TRANSPARENT);
529 
530         if (!IsAppThemed())
531             textColor = ::GetSysColor(COLOR_BTNTEXT);
532 
533         ::SetTextColor(hDC, textColor);
534 
535         hPrevFont = (HFONT) SelectObject(hDC, hFont);
536 
537         rcClient.top = (rcClient.bottom - CurrentSize.cy) / 2;
538         rcClient.bottom = rcClient.top + CurrentSize.cy;
539 
540         if (VisibleLines == 2)
541         {
542             /* Display either time and weekday (by default), or time and date (opt-in) */
543             PaintLine(hDC, &rcClient, 0, CLOCKWND_FORMAT_TIME);
544             PaintLine(hDC, &rcClient, 1,
545                       g_TaskbarSettings.bPreferDate ? CLOCKWND_FORMAT_DATE : CLOCKWND_FORMAT_DAY);
546         }
547         else
548         {
549             for (i = 0, line = 0;
550                  i < CLOCKWND_FORMAT_COUNT && line < VisibleLines;
551                  i++)
552             {
553                 PaintLine(hDC, &rcClient, i, i);
554                 line++;
555             }
556         }
557 
558         SelectObject(hDC, hPrevFont);
559 
560         SetBkMode(hDC, iPrevBkMode);
561     }
562 
563     if (wParam == 0)
564         EndPaint(&ps);
565 
566     return TRUE;
567 }
568 
569 VOID CTrayClockWnd::PaintLine(IN HDC hDC, IN OUT RECT *rcClient, IN UINT LineNumber, IN UINT szLinesIndex)
570 {
571     if (LineSizes[LineNumber].cx == 0)
572         return;
573 
574     INT HShift = ((IsHorizontal && (VisibleLines <= 1 ||
575                    g_TaskbarSettings.bCompactTrayIcons)) ? 0 : TRAY_CLOCK_WND_SPACING_X);
576 
577     TextOut(hDC,
578             ((rcClient->right - LineSizes[szLinesIndex].cx) / 2) + HShift,
579             rcClient->top + TRAY_CLOCK_WND_SPACING_Y,
580             szLines[szLinesIndex],
581             wcslen(szLines[szLinesIndex]));
582 
583     rcClient->top += LineSizes[LineNumber].cy + LineSpacing;
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 (newSettings->bPreferDate != g_TaskbarSettings.bPreferDate)
734     {
735         g_TaskbarSettings.bPreferDate = newSettings->bPreferDate;
736         bRealign = TRUE;
737     }
738 
739     if (bRealign)
740     {
741         /* Ask the parent to resize */
742         NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
743         GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
744         Update();
745     }
746     return 0;
747 }
748 
749 LRESULT CTrayClockWnd::OnLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
750 {
751     if (IsWindowVisible())
752     {
753         //FIXME: use SHRunControlPanel
754         ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
755     }
756     return TRUE;
757 }
758 
759 HRESULT CTrayClockWnd::Initialize(IN HWND hWndParent)
760 {
761     IsHorizontal = TRUE;
762 
763     /* Create the window. The tray window is going to move it to the correct
764         position and resize it as needed. */
765     DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
766     if (!g_TaskbarSettings.sr.HideClock)
767         dwStyle |= WS_VISIBLE;
768 
769     Create(hWndParent, 0, NULL, dwStyle);
770     if (!m_hWnd)
771         return E_FAIL;
772 
773     SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
774 
775     return S_OK;
776 
777 };
778 
779 HRESULT CTrayClockWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
780 {
781     return ShellObjectCreatorInit<CTrayClockWnd>(hwndParent, riid, ppv);
782 }
783