Leaked source code of windows server 2003
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.

870 lines
24 KiB

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