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