Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

843 lines
22 KiB

  1. #include "cabinet.h"
  2. #include "trayclok.h"
  3. #include "tray.h"
  4. #include "util.h"
  5. class CClockCtl : public CImpWndProc
  6. {
  7. public:
  8. STDMETHODIMP_(ULONG) AddRef();
  9. STDMETHODIMP_(ULONG) Release();
  10. CClockCtl() : _cRef(1) {}
  11. protected:
  12. void _UpdateLastHour();
  13. void _EnableTimer(DWORD dtNextTick);
  14. LRESULT _HandleCreate();
  15. LRESULT _HandleDestroy();
  16. DWORD _RecalcCurTime();
  17. void _EnsureFontsInitialized(BOOL fForce);
  18. void _GetTextExtent(HDC hdc, TCHAR* pszText, int cchText, LPRECT prcText);
  19. void _DrawText(HDC hdc, TCHAR* pszText, int cchText, LPRECT prcText);
  20. LRESULT _DoPaint(BOOL fPaint);
  21. void _Reset();
  22. LRESULT _HandleTimeChange();
  23. void _GetMaxTimeSize(HDC hdc, LPSIZE pszTime);
  24. void _GetMaxDateSize(HDC hdc, LPSIZE pszTime);
  25. void _GetMaxDaySize(HDC hdc, LPSIZE pszTime);
  26. LRESULT _CalcMinSize(int cxMax, int cyMax);
  27. LRESULT _HandleIniChange(WPARAM wParam, LPTSTR pszSection);
  28. LRESULT _OnNeedText(LPTOOLTIPTEXT lpttt);
  29. void _HandleThemeChanged(WPARAM wParam);
  30. LRESULT v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  31. TCHAR _szTimeFmt[40]; // The format string to pass to GetFormatTime
  32. TCHAR _szCurTime[40]; // The current string.
  33. int _cchCurTime;
  34. TCHAR _szDateFmt[40]; // The format string to pass to GetFormatTime
  35. TCHAR _szCurDate[40]; // The current string.
  36. int _cchCurDate;
  37. TCHAR _szCurDay[40]; // The current string.
  38. int _cchCurDay;
  39. WORD _wLastHour; // wHour from local time of last clock tick
  40. WORD _wLastMinute; // wMinute from local time of last clock tick
  41. BOOL _fClockRunning;
  42. BOOL _fClockClipped;
  43. BOOL _fHasFocus;
  44. HTHEME _hTheme;
  45. HFONT _hfontCapNormal;
  46. ULONG _cRef;
  47. friend BOOL ClockCtl_Class(HINSTANCE hinst);
  48. };
  49. ULONG CClockCtl::AddRef()
  50. {
  51. return ++_cRef;
  52. }
  53. ULONG CClockCtl::Release()
  54. {
  55. if (--_cRef == 0)
  56. {
  57. delete this;
  58. return 0;
  59. }
  60. return _cRef;
  61. }
  62. void CClockCtl::_UpdateLastHour()
  63. {
  64. SYSTEMTIME st;
  65. // Grab the time
  66. GetLocalTime(&st);
  67. _wLastHour = st.wHour;
  68. _wLastMinute = st.wMinute;
  69. }
  70. void CClockCtl::_EnableTimer(DWORD dtNextTick)
  71. {
  72. if (dtNextTick)
  73. {
  74. SetTimer(_hwnd, 0, dtNextTick, NULL);
  75. _fClockRunning = TRUE;
  76. }
  77. else if (_fClockRunning)
  78. {
  79. _fClockRunning = FALSE;
  80. KillTimer(_hwnd, 0);
  81. }
  82. }
  83. LRESULT CClockCtl::_HandleCreate()
  84. {
  85. AddRef();
  86. _EnsureFontsInitialized(FALSE);
  87. _hTheme = OpenThemeData(_hwnd, L"Clock");
  88. _UpdateLastHour();
  89. return 1;
  90. }
  91. LRESULT CClockCtl::_HandleDestroy()
  92. {
  93. Release(); // safe because cwndproc is holding a ref across call to v_wndproc
  94. if (_hTheme)
  95. {
  96. CloseThemeData(_hTheme);
  97. _hTheme = NULL;
  98. }
  99. if (_hfontCapNormal)
  100. {
  101. DeleteFont(_hfontCapNormal);
  102. _hfontCapNormal = NULL;
  103. }
  104. _EnableTimer(0);
  105. return 1;
  106. }
  107. DWORD CClockCtl::_RecalcCurTime()
  108. {
  109. SYSTEMTIME st;
  110. //
  111. // Current time.
  112. //
  113. GetLocalTime(&st);
  114. //
  115. // Don't recalc the text if the time hasn't changed yet.
  116. //
  117. if ((st.wMinute != _wLastMinute) || (st.wHour != _wLastHour) || !*_szCurTime)
  118. {
  119. _wLastMinute = st.wMinute;
  120. _wLastHour = st.wHour;
  121. //
  122. // Text for the current time.
  123. //
  124. _cchCurTime = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS,
  125. &st, _szTimeFmt, _szCurTime, ARRAYSIZE(_szCurTime));
  126. BOOL fRTL = IS_WINDOW_RTL_MIRRORED(_hwnd);
  127. _cchCurDate = GetDateFormat(LOCALE_USER_DEFAULT, fRTL ? DATE_RTLREADING : 0,
  128. &st, _szDateFmt, _szCurDate, ARRAYSIZE(_szCurDate));
  129. _cchCurDay = GetDateFormat(LOCALE_USER_DEFAULT, fRTL ? DATE_RTLREADING : 0,
  130. &st, TEXT("dddd"), _szCurDay, ARRAYSIZE(_szCurDay));
  131. // Don't count the NULL terminator.
  132. if (_cchCurTime > 0)
  133. _cchCurTime--;
  134. if (_cchCurDate > 0)
  135. _cchCurDate--;
  136. if (_cchCurDay > 0)
  137. _cchCurDay--;
  138. //
  139. // Update our window text so accessibility apps can see. Since we
  140. // don't have a caption USER won't try to paint us or anything, it
  141. // will just set the text and fire an event if any accessibility
  142. // clients are listening...
  143. //
  144. SetWindowText(_hwnd, _szCurTime);
  145. }
  146. //
  147. // Return number of milliseconds till we need to be called again.
  148. //
  149. return 1000UL * (60 - st.wSecond);
  150. }
  151. void CClockCtl::_EnsureFontsInitialized(BOOL fForce)
  152. {
  153. if (fForce || !_hfontCapNormal)
  154. {
  155. HFONT hfont;
  156. NONCLIENTMETRICS ncm;
  157. ncm.cbSize = sizeof(ncm);
  158. if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
  159. {
  160. // Create the normal font
  161. ncm.lfCaptionFont.lfWeight = FW_NORMAL;
  162. hfont = CreateFontIndirect(&ncm.lfCaptionFont);
  163. if (hfont)
  164. {
  165. if (_hfontCapNormal)
  166. DeleteFont(_hfontCapNormal);
  167. _hfontCapNormal = hfont;
  168. }
  169. }
  170. }
  171. }
  172. void CClockCtl::_GetTextExtent(HDC hdc, TCHAR* pszText, int cchText, LPRECT prcText)
  173. {
  174. if (_hTheme)
  175. {
  176. GetThemeTextExtent(_hTheme, hdc, CLP_TIME, 0, pszText, cchText, 0, prcText, prcText);
  177. }
  178. else
  179. {
  180. SIZE size;
  181. GetTextExtentPoint(hdc, pszText, cchText, &size);
  182. SetRect(prcText, 0, 0, size.cx, size.cy);
  183. }
  184. }
  185. void CClockCtl::_DrawText(HDC hdc, TCHAR* pszText, int cchText, LPRECT prcText)
  186. {
  187. if (_hTheme)
  188. {
  189. DrawThemeText(_hTheme, hdc, CLP_TIME, 0, pszText, cchText, 0, 0, prcText);
  190. }
  191. else
  192. {
  193. ExtTextOut(hdc, prcText->left, prcText->top, ETO_OPAQUE, NULL, pszText, cchText, NULL);
  194. }
  195. }
  196. LRESULT CClockCtl::_DoPaint(BOOL fPaint)
  197. {
  198. PAINTSTRUCT ps;
  199. RECT rcClient, rcClip = {0};
  200. DWORD dtNextTick = 0;
  201. BOOL fDoTimer;
  202. HDC hdc;
  203. HBITMAP hMemBm, hOldBm;
  204. //
  205. // If we are asked to paint and the clock is not running then start it.
  206. // Otherwise wait until we get a clock tick to recompute the time etc.
  207. //
  208. fDoTimer = !fPaint || !_fClockRunning;
  209. //
  210. // Get a DC to paint with.
  211. //
  212. if (fPaint)
  213. {
  214. BeginPaint(_hwnd, &ps);
  215. }
  216. else
  217. {
  218. ps.hdc = GetDC(_hwnd);
  219. GetClipBox(ps.hdc, &ps.rcPaint);
  220. }
  221. // Create memory surface and map rendering context if double buffering
  222. // Only make large enough for clipping region
  223. hdc = CreateCompatibleDC(ps.hdc);
  224. if (hdc)
  225. {
  226. hMemBm = CreateCompatibleBitmap(ps.hdc, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint));
  227. if (hMemBm)
  228. {
  229. hOldBm = (HBITMAP) SelectObject(hdc, hMemBm);
  230. // Offset painting to paint in region
  231. OffsetWindowOrgEx(hdc, ps.rcPaint.left, ps.rcPaint.top, NULL);
  232. }
  233. else
  234. {
  235. DeleteDC(hdc);
  236. hdc = NULL;
  237. }
  238. }
  239. if (hdc)
  240. {
  241. SHSendPrintRect(GetParent(_hwnd), _hwnd, hdc, &ps.rcPaint);
  242. _EnsureFontsInitialized(FALSE);
  243. //
  244. // Update the time if we need to.
  245. //
  246. if (fDoTimer || !*_szCurTime)
  247. {
  248. dtNextTick = _RecalcCurTime();
  249. ASSERT(dtNextTick);
  250. }
  251. //
  252. // Paint the clock face if we are not clipped or if we got a real
  253. // paint message for the window. We want to avoid turning off the
  254. // timer on paint messages (regardless of clip region) because this
  255. // implies the window is visible in some way. If we guessed wrong, we
  256. // will turn off the timer next timer tick anyway so no big deal.
  257. //
  258. if (GetClipBox(hdc, &rcClip) != NULLREGION || fPaint)
  259. {
  260. //
  261. // Draw the text centered in the window.
  262. //
  263. GetClientRect(_hwnd, &rcClient);
  264. HFONT hfontOld;
  265. if (_hfontCapNormal)
  266. hfontOld = SelectFont(hdc, _hfontCapNormal);
  267. SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
  268. SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
  269. BOOL fShowDate = FALSE;
  270. BOOL fShowDay = FALSE;
  271. RECT rcTime = {0};
  272. RECT rcDate = {0};
  273. RECT rcDay = {0};
  274. _GetTextExtent(hdc, _szCurTime, _cchCurTime, &rcTime);
  275. _GetTextExtent(hdc, _szCurDate, _cchCurDate, &rcDate);
  276. _GetTextExtent(hdc, _szCurDay, _cchCurDay, &rcDay);
  277. int cySpace = RECTHEIGHT(rcTime) / 2;
  278. int cy = RECTHEIGHT(rcTime) + cySpace;
  279. if ((cy + RECTHEIGHT(rcDay) < rcClient.bottom) && (RECTWIDTH(rcDay) < rcClient.right))
  280. {
  281. fShowDay = TRUE;
  282. cy += RECTHEIGHT(rcDay) + cySpace;
  283. if ((cy + RECTHEIGHT(rcDate) < rcClient.bottom) && (RECTWIDTH(rcDate) < rcClient.right))
  284. {
  285. fShowDate = TRUE;
  286. cy += RECTHEIGHT(rcDate) + cySpace;
  287. }
  288. }
  289. cy -= cySpace;
  290. int yOffset = max((rcClient.bottom - cy) / 2, 0);
  291. RECT rcDraw = rcTime;
  292. OffsetRect(&rcDraw, max((rcClient.right - RECTWIDTH(rcTime)) / 2, 0), yOffset);
  293. _DrawText(hdc, _szCurTime, _cchCurTime, &rcDraw);
  294. yOffset += RECTHEIGHT(rcTime) + cySpace;
  295. if (fShowDay)
  296. {
  297. rcDraw = rcDay;
  298. OffsetRect(&rcDraw, max((rcClient.right - RECTWIDTH(rcDay)) / 2, 0), yOffset);
  299. _DrawText(hdc, _szCurDay, _cchCurDay, &rcDraw);
  300. yOffset += RECTHEIGHT(rcDay) + cySpace;
  301. if (fShowDate)
  302. {
  303. rcDraw = rcDate;
  304. OffsetRect(&rcDraw, max((rcClient.right - RECTWIDTH(rcDate)) / 2, 0), yOffset);
  305. _DrawText(hdc, _szCurDate, _cchCurDate, &rcDraw);
  306. }
  307. }
  308. // figure out if the time is clipped
  309. _fClockClipped = (RECTWIDTH(rcTime) > rcClient.right || RECTHEIGHT(rcTime) > rcClient.bottom);
  310. if (_hfontCapNormal)
  311. SelectObject(hdc, hfontOld);
  312. if (_fHasFocus)
  313. {
  314. LRESULT lRes = SendMessage(_hwnd, WM_QUERYUISTATE, 0, 0);
  315. if (!(LOWORD(lRes) & UISF_HIDEFOCUS))
  316. {
  317. RECT rcFocus = rcClient;
  318. InflateRect(&rcFocus, -2, 0);
  319. DrawFocusRect(hdc, &rcFocus);
  320. }
  321. }
  322. }
  323. else
  324. {
  325. //
  326. // We are obscured so make sure we turn off the clock.
  327. //
  328. dtNextTick = 0;
  329. fDoTimer = TRUE;
  330. }
  331. BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint), hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
  332. SelectObject(hdc, hOldBm);
  333. DeleteObject(hMemBm);
  334. DeleteDC(hdc);
  335. //
  336. // Release our paint DC.
  337. //
  338. if (fPaint)
  339. EndPaint(_hwnd, &ps);
  340. else
  341. ReleaseDC(_hwnd, ps.hdc);
  342. }
  343. //
  344. // Reset/Kill the timer.
  345. //
  346. if (fDoTimer)
  347. {
  348. _EnableTimer(dtNextTick);
  349. //
  350. // If we just killed the timer becuase we were clipped when it arrived,
  351. // make sure that we are really clipped by invalidating ourselves once.
  352. //
  353. if (!dtNextTick && !fPaint)
  354. InvalidateRect(_hwnd, NULL, FALSE);
  355. else
  356. {
  357. InvalidateRect(_hwnd, NULL, TRUE);
  358. }
  359. }
  360. return 0;
  361. }
  362. void CClockCtl::_Reset()
  363. {
  364. //
  365. // Reset the clock by killing the timer and invalidating.
  366. // Everything will be updated when we try to paint.
  367. //
  368. _EnableTimer(0);
  369. InvalidateRect(_hwnd, NULL, FALSE);
  370. }
  371. LRESULT CClockCtl::_HandleTimeChange()
  372. {
  373. *_szCurTime = 0; // Force a text recalc.
  374. _UpdateLastHour();
  375. _Reset();
  376. return 1;
  377. }
  378. static const TCHAR c_szSlop[] = TEXT("00");
  379. void CClockCtl::_GetMaxTimeSize(HDC hdc, LPSIZE pszTime)
  380. {
  381. SYSTEMTIME st={0}; // Initialize to 0...
  382. RECT rcAM = {0};
  383. RECT rcPM = {0};
  384. TCHAR szTime[40];
  385. // We need to get the AM and the PM sizes...
  386. // We append Two 0s at end to add slop into size
  387. // first AM
  388. st.wHour=11;
  389. int cch = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st,
  390. _szTimeFmt, szTime, ARRAYSIZE(szTime) - ARRAYSIZE(c_szSlop));
  391. if (cch)
  392. cch--; // don't count the NULL
  393. lstrcat(szTime, c_szSlop);
  394. _GetTextExtent(hdc, szTime, cch+2, &rcAM);
  395. // then PM
  396. st.wHour=23;
  397. cch = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st,
  398. _szTimeFmt, szTime, ARRAYSIZE(szTime) - ARRAYSIZE(c_szSlop));
  399. if (cch)
  400. cch--; // don't count the NULL
  401. lstrcat(szTime, c_szSlop);
  402. _GetTextExtent(hdc, szTime, cch+2, &rcPM);
  403. pszTime->cx = max(rcAM.right, rcPM.right);
  404. pszTime->cy = max(rcAM.bottom, rcPM.bottom);
  405. }
  406. void CClockCtl::_GetMaxDateSize(HDC hdc, LPSIZE pszTime)
  407. {
  408. SYSTEMTIME st={0}; // Initialize to 0...
  409. TCHAR szDate[43];
  410. st.wYear = 2001;
  411. st.wMonth = 5;
  412. st.wDay = 5;
  413. BOOL fRTL = IS_WINDOW_RTL_MIRRORED(_hwnd);
  414. int cch = GetDateFormat(LOCALE_USER_DEFAULT, fRTL ? DATE_RTLREADING : 0,
  415. &st, _szDateFmt, szDate, ARRAYSIZE(szDate) - ARRAYSIZE(c_szSlop));
  416. if (cch > 0)
  417. cch--; // don't count the NULL
  418. lstrcat(szDate, c_szSlop);
  419. RECT rc = {0};
  420. _GetTextExtent(hdc, szDate, cch+2, &rc);
  421. pszTime->cx = rc.right;
  422. pszTime->cy = rc.bottom;
  423. }
  424. void CClockCtl::_GetMaxDaySize(HDC hdc, LPSIZE pszTime)
  425. {
  426. SYSTEMTIME st={0}; // Initialize to 0...
  427. TCHAR szDay[40];
  428. pszTime->cx = 0;
  429. pszTime->cy = 0;
  430. // Use a fake date, otherwise GetDateFormat complains about invalid args
  431. // BTW, the date is the day I fixed this bug for those of you reading this comment
  432. // in the year 2025.
  433. st.wYear = 2001;
  434. st.wMonth = 3;
  435. for (WORD wDay = 1; wDay <= 7; wDay++)
  436. {
  437. st.wDay = wDay;
  438. int cch = GetDateFormat(LOCALE_USER_DEFAULT, 0,
  439. &st, TEXT("dddd"), szDay, ARRAYSIZE(szDay) - ARRAYSIZE(c_szSlop));
  440. if (cch)
  441. cch--; // don't count the NULL
  442. lstrcat(szDay, c_szSlop);
  443. RECT rc = {0};
  444. _GetTextExtent(hdc, szDay, cch+2, &rc);
  445. pszTime->cx = max(pszTime->cx, rc.right);
  446. pszTime->cy = max(pszTime->cy, rc.bottom);
  447. }
  448. }
  449. LRESULT CClockCtl::_CalcMinSize(int cxMax, int cyMax)
  450. {
  451. RECT rc;
  452. HDC hdc;
  453. HFONT hfontOld;
  454. if (!(GetWindowLong(_hwnd, GWL_STYLE) & WS_VISIBLE))
  455. return 0L;
  456. if (_szTimeFmt[0] == TEXT('\0'))
  457. {
  458. if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIMEFORMAT, _szTimeFmt,
  459. ARRAYSIZE(_szTimeFmt)) == 0)
  460. {
  461. TraceMsg(TF_ERROR, "c.ccms: GetLocalInfo Failed %d.", GetLastError());
  462. }
  463. *_szCurTime = 0; // Force the text to be recomputed.
  464. }
  465. if (_szDateFmt[0] == TEXT('\0'))
  466. {
  467. if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, _szDateFmt,
  468. ARRAYSIZE(_szDateFmt)) == 0)
  469. {
  470. TraceMsg(TF_ERROR, "c.ccms: GetLocalInfo Failed %d.", GetLastError());
  471. }
  472. *_szCurDate = 0; // Force the text to be recomputed.
  473. }
  474. hdc = GetDC(_hwnd);
  475. if (!hdc)
  476. return(0L);
  477. _EnsureFontsInitialized(FALSE);
  478. if (_hfontCapNormal)
  479. hfontOld = SelectFont(hdc, _hfontCapNormal);
  480. SIZE size = {0};
  481. SIZE sizeTemp = {0};
  482. _GetMaxTimeSize(hdc, &sizeTemp);
  483. int cySpace = sizeTemp.cy / 2;
  484. size.cy += sizeTemp.cy;
  485. size.cx = max(sizeTemp.cx, size.cx);
  486. _GetMaxDaySize(hdc, &sizeTemp);
  487. if ((size.cy + sizeTemp.cy + cySpace < cyMax) && (sizeTemp.cx < cxMax))
  488. {
  489. size.cy += sizeTemp.cy + cySpace;
  490. size.cx = max(sizeTemp.cx, size.cx);
  491. _GetMaxDateSize(hdc, &sizeTemp);
  492. if ((size.cy + sizeTemp.cy + cySpace < cyMax) && (sizeTemp.cx < cxMax))
  493. {
  494. size.cy += sizeTemp.cy + cySpace;
  495. size.cx = max(sizeTemp.cx, size.cx);
  496. }
  497. }
  498. if (_hfontCapNormal)
  499. SelectObject(hdc, hfontOld);
  500. ReleaseDC(_hwnd, hdc);
  501. // Now lets set up our rectangle...
  502. // The width is 6 digits (a digit slop on both ends + size of
  503. // : or sep and max AM or PM string...)
  504. SetRect(&rc, 0, 0, size.cx,
  505. size.cy + 4 * g_cyBorder);
  506. AdjustWindowRectEx(&rc, GetWindowLong(_hwnd, GWL_STYLE), FALSE,
  507. GetWindowLong(_hwnd, GWL_EXSTYLE));
  508. // make sure we're at least the size of other buttons:
  509. if (rc.bottom - rc.top < g_cySize + g_cyEdge)
  510. rc.bottom = rc.top + g_cySize + g_cyEdge;
  511. return MAKELRESULT((rc.right - rc.left),
  512. (rc.bottom - rc.top));
  513. }
  514. LRESULT CClockCtl::_HandleIniChange(WPARAM wParam, LPTSTR pszSection)
  515. {
  516. if ((pszSection == NULL) || (lstrcmpi(pszSection, TEXT("WindowMetrics")) == 0) ||
  517. wParam == SPI_SETNONCLIENTMETRICS)
  518. {
  519. _EnsureFontsInitialized(TRUE);
  520. }
  521. // Only process certain sections...
  522. if ((pszSection == NULL) || (lstrcmpi(pszSection, TEXT("intl")) == 0) ||
  523. (wParam == SPI_SETICONTITLELOGFONT))
  524. {
  525. TOOLINFO ti;
  526. _szTimeFmt[0] = TEXT('\0'); // Go reread the format.
  527. _szDateFmt[0] = TEXT('\0'); // Go reread the format.
  528. // And make sure we have it recalc...
  529. RECT rc;
  530. GetClientRect(_hwnd, &rc);
  531. //
  532. // When the time/locale is changed, we get a WM_WININICHANGE.
  533. // But the WM_WININICHANGE comes *AFTER* the "sizing" messages. By the time
  534. // we are here, we have calculated the min. size of the clock window based
  535. // on the *PREVIOUS* time. The tray sets the clock window size based on
  536. // this "previous" size, but NOW we get the WININICHANGE, and can calculate
  537. // the new size of the clock. So we have to tell the tray to change our
  538. // size now, and then redraw ourselves.
  539. c_tray.SizeWindows();
  540. ti.cbSize = sizeof(ti);
  541. ti.uFlags = 0;
  542. ti.hwnd = v_hwndTray;
  543. ti.uId = (UINT_PTR)_hwnd;
  544. ti.lpszText = LPSTR_TEXTCALLBACK;
  545. SendMessage(c_tray.GetTrayTips(), TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  546. _Reset();
  547. }
  548. return 0;
  549. }
  550. LRESULT CClockCtl::_OnNeedText(LPTOOLTIPTEXT lpttt)
  551. {
  552. int iDateFormat = DATE_LONGDATE;
  553. //
  554. // This code is really squirly. We don't know if the time has been
  555. // clipped until we actually try to paint it, since the clip logic
  556. // is in the WM_PAINT handler... Go figure...
  557. //
  558. if (!*_szCurTime)
  559. {
  560. InvalidateRect(_hwnd, NULL, FALSE);
  561. UpdateWindow(_hwnd);
  562. }
  563. //
  564. // If the current user locale is any BiDi locale, then
  565. // Make the date reading order it RTL. SetBiDiDateFlags only adds
  566. // DATE_RTLREADING if the locale is BiDi. [samera]
  567. //
  568. SetBiDiDateFlags(&iDateFormat);
  569. if (_fClockClipped)
  570. {
  571. // we need to put the time in here too
  572. TCHAR sz[80];
  573. GetDateFormat(LOCALE_USER_DEFAULT, iDateFormat, NULL, NULL, sz, ARRAYSIZE(sz));
  574. wnsprintf(lpttt->szText, ARRAYSIZE(lpttt->szText), TEXT("%s %s"), _szCurTime, sz);
  575. }
  576. else
  577. {
  578. GetDateFormat(LOCALE_USER_DEFAULT, iDateFormat, NULL, NULL, lpttt->szText, ARRAYSIZE(lpttt->szText));
  579. }
  580. return TRUE;
  581. }
  582. void CClockCtl::_HandleThemeChanged(WPARAM wParam)
  583. {
  584. if (_hTheme)
  585. {
  586. CloseThemeData(_hTheme);
  587. _hTheme = NULL;
  588. }
  589. if (wParam)
  590. {
  591. _hTheme = OpenThemeData(_hwnd, L"Clock");
  592. }
  593. InvalidateRect(_hwnd, NULL, TRUE);
  594. }
  595. LRESULT CClockCtl::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  596. {
  597. switch (uMsg)
  598. {
  599. case WM_CALCMINSIZE:
  600. return _CalcMinSize((int)wParam, (int)lParam);
  601. case WM_NCCREATE:
  602. return _HandleCreate();
  603. case WM_NCDESTROY:
  604. return _HandleDestroy();
  605. case WM_ERASEBKGND:
  606. return 1;
  607. case WM_TIMER:
  608. case WM_PAINT:
  609. return _DoPaint((uMsg == WM_PAINT));
  610. case WM_GETTEXT:
  611. //
  612. // Update the text if we are not running and somebody wants it.
  613. //
  614. if (!_fClockRunning)
  615. _RecalcCurTime();
  616. goto L_default;
  617. case WM_WININICHANGE:
  618. return _HandleIniChange(wParam, (LPTSTR)lParam);
  619. case WM_POWER:
  620. //
  621. // a critical resume does not generate a WM_POWERBROADCAST
  622. // to windows for some reason, but it does generate a old
  623. // WM_POWER message.
  624. //
  625. if (wParam == PWR_CRITICALRESUME)
  626. goto TimeChanged;
  627. break;
  628. TimeChanged:
  629. case WM_TIMECHANGE:
  630. return _HandleTimeChange();
  631. case WM_NCHITTEST:
  632. return(HTTRANSPARENT);
  633. case WM_SHOWWINDOW:
  634. if (wParam)
  635. break;
  636. // fall through
  637. case TCM_RESET:
  638. _Reset();
  639. break;
  640. case WM_NOTIFY:
  641. {
  642. NMHDR *pnm = (NMHDR*)lParam;
  643. switch (pnm->code)
  644. {
  645. case TTN_NEEDTEXT:
  646. return _OnNeedText((LPTOOLTIPTEXT)lParam);
  647. break;
  648. }
  649. break;
  650. }
  651. case WM_THEMECHANGED:
  652. _HandleThemeChanged(wParam);
  653. break;
  654. case WM_SETFOCUS:
  655. case WM_KILLFOCUS:
  656. _fHasFocus = (uMsg == WM_SETFOCUS);
  657. InvalidateRect(_hwnd, NULL, TRUE);
  658. break;
  659. case WM_KEYDOWN:
  660. case WM_KEYUP:
  661. case WM_CHAR:
  662. case WM_SYSKEYDOWN:
  663. case WM_SYSKEYUP:
  664. case WM_SYSCHAR:
  665. //
  666. // forward all keyboard input to parent
  667. //
  668. if (SendMessage(GetParent(_hwnd), uMsg, wParam, lParam) != 0)
  669. {
  670. goto L_default;
  671. }
  672. break;
  673. default:
  674. L_default:
  675. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  676. }
  677. return 0;
  678. }
  679. // Register the clock class.
  680. BOOL ClockCtl_Class(HINSTANCE hinst)
  681. {
  682. WNDCLASS wc = {0};
  683. wc.lpszClassName = WC_TRAYCLOCK;
  684. wc.style = CS_HREDRAW | CS_VREDRAW;
  685. wc.lpfnWndProc = CClockCtl::s_WndProc;
  686. wc.hInstance = hinst;
  687. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  688. wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
  689. wc.cbWndExtra = sizeof(CClockCtl*);
  690. return RegisterClass(&wc);
  691. }
  692. HWND ClockCtl_Create(HWND hwndParent, UINT uID, HINSTANCE hInst)
  693. {
  694. HWND hwnd = NULL;
  695. CClockCtl* pcc = new CClockCtl();
  696. if (pcc)
  697. {
  698. hwnd = CreateWindowEx(0, WC_TRAYCLOCK,
  699. NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 0, 0, 0, 0,
  700. hwndParent, IntToPtr_(HMENU, uID), hInst, pcc);
  701. pcc->Release();
  702. }
  703. return hwnd;
  704. }