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