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