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