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.

3825 lines
98 KiB

  1. /*++
  2. Copyright (c) 1994-1998, Microsoft Corporation All rights reserved.
  3. Module Name:
  4. date.c
  5. Abstract:
  6. This module implements the textual representations of
  7. Day Month Year : Hours Min Seconds for the Date/Time applet.
  8. Revision History:
  9. --*/
  10. //
  11. // Include Files.
  12. //
  13. #include "timedate.h"
  14. #include <commctrl.h>
  15. #include <prsht.h>
  16. #include <regstr.h>
  17. #include <imm.h>
  18. #include "clock.h"
  19. #include "mapctl.h"
  20. #include "inettime.h"
  21. #include "rc.h"
  22. #include <help.h>
  23. #include <shlwapi.h>
  24. #include <windows.h>
  25. #include <windowsx.h>
  26. //
  27. // Constant Declarations.
  28. //
  29. #define TZNAME_SIZE 128
  30. #define TZDISPLAYZ 128
  31. #define BIAS_ONE_HOUR (-60L)
  32. #define ZONE_IMAGE_SCALE (356)
  33. #define ZONE_BIAS_SCALE (-1440)
  34. #define ZONE_IMAGE_LEFT (120)
  35. #define ZONE_IMAGE_WIDTH (80)
  36. #define BIAS_PLUS_12 (12L * BIAS_ONE_HOUR)
  37. #define BIAS_MINUS_12 (- BIAS_PLUS_12)
  38. #define TZ_HIT_NONE (0)
  39. #define TZ_HIT_BASE (1)
  40. #define TZ_HIT_PARTIAL (2)
  41. #define TZ_HIT_EXACT (3)
  42. //
  43. // Global Variables.
  44. //
  45. TCHAR const szIntl[] = TEXT("intl");
  46. //
  47. // Default used if none could be found.
  48. //
  49. INTLSTRUCT IntlDef =
  50. {
  51. TEXT("Other Country"),
  52. 1, 0, 0, 0, 0, 2, 0, 1, 2, 1,
  53. TEXT("AM"),
  54. TEXT("PM"),
  55. TEXT("$"),
  56. TEXT(","),
  57. TEXT("."),
  58. TEXT("/"),
  59. TEXT(":"),
  60. TEXT(","),
  61. TEXT("dddd, MMMM dd, yyyy"),
  62. TEXT("M/d/yyyy"),
  63. TEXT("USA"),
  64. 1, 0, 1, 0, 0x0409,
  65. TEXT("hh:mm:ss tt"),
  66. 0, 1,
  67. TEXT(","),
  68. TEXT(".")
  69. };
  70. BOOL g_bFirstBoot = FALSE; // for first boot during setup
  71. int g_Time[3]; // time the user currently has set
  72. int g_LastTime[3]; // last displayed time - to stop flicker
  73. short wDateTime[7]; // values for first 7 date/time items
  74. short wPrevDateTime[7]; // only repaint fields if necessary
  75. BOOL fDateDirty;
  76. //
  77. // Formatting strings for AM and PM
  78. //
  79. TCHAR sz1159[12];
  80. TCHAR sz2359[12];
  81. //
  82. // Are we in 24 hour time. If not, is it AM or PM.
  83. //
  84. BOOL g_b24HR;
  85. BOOL g_bPM;
  86. //
  87. // This flag indicates if the user has tried to change the time.
  88. // If so, then we stop providing the system time and use the
  89. // time that we store internally. We send the clock control our
  90. // TimeProvider function.
  91. //
  92. WORD g_Modified = 0;
  93. WORD g_WasModified = 0;
  94. //
  95. // Which of the HMS MDY have leading zeros.
  96. //
  97. BOOL g_bLZero[6] = {FALSE, TRUE, TRUE, FALSE, FALSE, FALSE};
  98. //
  99. // Ranges of HMS MDY
  100. //
  101. struct
  102. {
  103. int nMax;
  104. int nMin;
  105. } g_sDateInfo[] =
  106. {
  107. 23, 0,
  108. 59, 0,
  109. 59, 0,
  110. 12, 1,
  111. 31, 1,
  112. 2099, 1980,
  113. };
  114. //
  115. // Time Zone info globals.
  116. //
  117. int g_nTimeZones = 0;
  118. TIME_ZONE_INFORMATION g_tziCurrent, *g_ptziCurrent = NULL;
  119. //
  120. // Registry location for Time Zone information.
  121. //
  122. #ifdef WINNT
  123. TCHAR c_szTimeZones[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
  124. #else
  125. TCHAR c_szTimeZones[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Time Zones");
  126. #endif
  127. //
  128. // Time Zone data value keys.
  129. //
  130. TCHAR c_szTZDisplayName[] = TEXT("Display");
  131. TCHAR c_szTZStandardName[] = TEXT("Std");
  132. TCHAR c_szTZDaylightName[] = TEXT("Dlt");
  133. TCHAR c_szTZI[] = TEXT("TZI");
  134. TCHAR c_szTZMapInfo[] = TEXT("MapID");
  135. //
  136. // IME globals.
  137. //
  138. HIMC g_PrevIMCForDateField;
  139. //
  140. // Context Help Ids.
  141. //
  142. const DWORD aDateTimeHelpIds[] =
  143. {
  144. IDD_GROUPBOX1, IDH_DATETIME_DATE_GROUP,
  145. IDD_GROUPBOX2, IDH_DATETIME_TIME,
  146. DATETIME_CURTZ, IDH_DATETIME_CURRENT_TIME_ZONE,
  147. DATETIME_CALENDAR, IDH_DATETIME_DATE,
  148. DATETIME_CLOCK, IDH_DATETIME_TIME,
  149. DATETIME_TBORDER, IDH_DATETIME_TIME,
  150. DATETIME_HOUR, IDH_DATETIME_TIME,
  151. DATETIME_TSEP1, IDH_DATETIME_TIME,
  152. DATETIME_MINUTE, IDH_DATETIME_TIME,
  153. DATETIME_TSEP2, IDH_DATETIME_TIME,
  154. DATETIME_SECOND, IDH_DATETIME_TIME,
  155. DATETIME_AMPM, IDH_DATETIME_TIME,
  156. DATETIME_TARROW, IDH_DATETIME_TIME,
  157. DATETIME_MONTHNAME, IDH_DATETIME_MONTH,
  158. DATETIME_YEAR, IDH_DATETIME_YEAR,
  159. DATETIME_YARROW, IDH_DATETIME_YEAR,
  160. IDD_TIMEZONES, IDH_DATETIME_TIMEZONE,
  161. // IDD_TIMEMAP, IDH_DATETIME_BITMAP,
  162. IDD_TIMEMAP, NO_HELP,
  163. IDD_AUTOMAGIC, IDH_DATETIME_DAYLIGHT_SAVE,
  164. 0, 0
  165. };
  166. //
  167. // Typedef Declarations.
  168. //
  169. //
  170. // Registry info goes in this structure.
  171. //
  172. typedef struct tagTZINFO
  173. {
  174. struct tagTZINFO *next;
  175. TCHAR szDisplayName[TZDISPLAYZ];
  176. TCHAR szStandardName[TZNAME_SIZE];
  177. TCHAR szDaylightName[TZNAME_SIZE];
  178. int ComboIndex;
  179. int SeaIndex;
  180. int LandIndex;
  181. int MapLeft;
  182. int MapWidth;
  183. LONG Bias;
  184. LONG StandardBias;
  185. LONG DaylightBias;
  186. SYSTEMTIME StandardDate;
  187. SYSTEMTIME DaylightDate;
  188. } TZINFO, *PTZINFO;
  189. //
  190. // State info for the time zone page.
  191. //
  192. typedef struct
  193. {
  194. PTZINFO zone;
  195. BOOL initializing;
  196. PTZINFO lookup[MAPCTL_MAX_INDICES];
  197. } TZPAGE_STATE;
  198. DWORD GetTextExtent(
  199. HDC hdc,
  200. LPCTSTR lpsz,
  201. int cb);
  202. #ifndef UNICODE
  203. ////////////////////////////////////////////////////////////////////////////
  204. //
  205. // WideStrToStr
  206. //
  207. ////////////////////////////////////////////////////////////////////////////
  208. int WINAPI WideStrToStr(
  209. LPSTR psz,
  210. LPWSTR pwsz)
  211. {
  212. return WideCharToMultiByte(CP_ACP, 0, pwsz, -1, psz, MAX_PATH, NULL, NULL);
  213. }
  214. ////////////////////////////////////////////////////////////////////////////
  215. //
  216. // StrToWideStr
  217. //
  218. ////////////////////////////////////////////////////////////////////////////
  219. int WINAPI StrToWideStr(
  220. LPWSTR pwsz,
  221. LPCSTR psz)
  222. {
  223. return MultiByteToWideChar(CP_ACP, 0, psz, -1, pwsz, MAX_PATH);
  224. }
  225. ////////////////////////////////////////////////////////////////////////////
  226. //
  227. // AnsiWideStrCmpI
  228. //
  229. ////////////////////////////////////////////////////////////////////////////
  230. int AnsiWideStrCmpI(
  231. LPCSTR pszAnsi,
  232. WCHAR *pwszWide)
  233. {
  234. char szAnsi[64];
  235. WideStrToStr(szAnsi, pwszWide);
  236. return (lstrcmpi(pszAnsi, szAnsi));
  237. }
  238. #endif
  239. ////////////////////////////////////////////////////////////////////////////
  240. //
  241. // ParseDateElement
  242. //
  243. // Assumes that the character pointed to by pszElement is a
  244. // 'M', 'd', or 'y', and checks if the string indicates a leading zero
  245. // or century. The return value is a pointer to the next character,
  246. // which should be a separator or NULL. A return value of NULL indicates
  247. // an error.
  248. //
  249. ////////////////////////////////////////////////////////////////////////////
  250. LPTSTR ParseDateElement(
  251. LPTSTR pszElement,
  252. BOOL *pbLZero)
  253. {
  254. //
  255. // Check for valid character.
  256. //
  257. switch (*pszElement)
  258. {
  259. case ( TEXT('y') ) :
  260. case ( TEXT('M') ) :
  261. case ( TEXT('d') ) :
  262. {
  263. break;
  264. }
  265. default:
  266. {
  267. return (NULL);
  268. }
  269. }
  270. ++pszElement;
  271. if (*pszElement != *(pszElement - 1))
  272. {
  273. *pbLZero = 0;
  274. }
  275. else
  276. {
  277. *pbLZero = 1;
  278. if (*pszElement++ == TEXT('y'))
  279. {
  280. if (!(*pszElement == TEXT('y')))
  281. {
  282. *pbLZero = 0;
  283. }
  284. else
  285. {
  286. if (!(*++pszElement == TEXT('y')))
  287. {
  288. //
  289. // Found 3 y's, invalid format.
  290. //
  291. return (NULL);
  292. }
  293. else
  294. {
  295. ++pszElement;
  296. }
  297. }
  298. }
  299. }
  300. return (pszElement);
  301. }
  302. int rgMoveTimeControls [] =
  303. {
  304. DATETIME_HOUR,
  305. DATETIME_MINUTE,
  306. DATETIME_SECOND,
  307. DATETIME_TSEP1,
  308. DATETIME_TSEP2
  309. };
  310. ////////////////////////////////////////////////////////////////////////////
  311. //
  312. // AdjustAMPMPosition
  313. //
  314. ////////////////////////////////////////////////////////////////////////////
  315. void AdjustAMPMPosition(HWND hwnd)
  316. {
  317. TCHAR szTimePrefix[5];
  318. static BOOL fMoved = FALSE;
  319. GetLocaleInfo(LOCALE_USER_DEFAULT,
  320. LOCALE_ITIMEMARKPOSN,
  321. szTimePrefix,
  322. ARRAYSIZE(szTimePrefix));
  323. if (!fMoved && szTimePrefix[0] == TEXT('1'))
  324. {
  325. RECT rLeftCtl, rAMPMCtl, rCurrCtl;
  326. HWND hwndAMPM, hwndCurr;
  327. int i, width;
  328. POINT pt;
  329. fMoved = TRUE;
  330. //Get rect of left most control (hours)
  331. GetWindowRect(GetDlgItem(hwnd, DATETIME_HOUR), &rLeftCtl);
  332. //Get rect of AM PM control
  333. hwndAMPM = GetDlgItem(hwnd, DATETIME_AMPM);
  334. GetWindowRect(hwndAMPM, &rAMPMCtl);
  335. width = rAMPMCtl.right - rAMPMCtl.left;
  336. //Shift all controls right by the AM PM control width
  337. for (i = 0; i < ARRAYSIZE(rgMoveTimeControls); i++)
  338. {
  339. hwndCurr = GetDlgItem(hwnd, rgMoveTimeControls[i]);
  340. GetWindowRect(hwndCurr, &rCurrCtl);
  341. pt.x = rCurrCtl.left;
  342. pt.y = rCurrCtl.top;
  343. ScreenToClient(hwnd, &pt);
  344. MoveWindow(hwndCurr, pt.x + width,
  345. pt.y,
  346. rCurrCtl.right - rCurrCtl.left,
  347. rCurrCtl.bottom - rCurrCtl.top,
  348. TRUE);
  349. }
  350. //Move AM PM control left to where the hours were.
  351. pt.x = rLeftCtl.left;
  352. pt.y = rAMPMCtl.top;
  353. ScreenToClient(hwnd, &pt);
  354. MoveWindow(hwndAMPM, pt.x,
  355. pt.y,
  356. rAMPMCtl.right - rAMPMCtl.left,
  357. rAMPMCtl.bottom - rAMPMCtl.top,
  358. TRUE);
  359. }
  360. }
  361. ////////////////////////////////////////////////////////////////////////////
  362. //
  363. // MonthUpperBound
  364. //
  365. ////////////////////////////////////////////////////////////////////////////
  366. int _fastcall MonthUpperBound(
  367. int nMonth,
  368. int nYear)
  369. {
  370. switch (nMonth)
  371. {
  372. case ( 2 ) :
  373. {
  374. //
  375. // A year is a leap year if it is divisible by 4 and is not
  376. // a century year (multiple of 100) or if it is divisible by
  377. // 400.
  378. //
  379. return ( ((nYear % 4 == 0) &&
  380. ((nYear % 100 != 0) || (nYear % 400 == 0))) ? 29 : 28 );
  381. }
  382. case ( 4 ) :
  383. case ( 6 ) :
  384. case ( 9 ) :
  385. case ( 11 ) :
  386. {
  387. return (30);
  388. }
  389. }
  390. return (31);
  391. }
  392. ////////////////////////////////////////////////////////////////////////////
  393. //
  394. // IsAMPM
  395. //
  396. // True if PM.
  397. //
  398. ////////////////////////////////////////////////////////////////////////////
  399. BOOL IsAMPM(
  400. int iHour)
  401. {
  402. return ((iHour >= 12) ? 1 : 0);
  403. }
  404. ////////////////////////////////////////////////////////////////////////////
  405. //
  406. // GetDateTime
  407. //
  408. ////////////////////////////////////////////////////////////////////////////
  409. void GetDateTime()
  410. {
  411. SYSTEMTIME SystemTime;
  412. GetLocalTime(&SystemTime);
  413. wDateTime[HOUR] = SystemTime.wHour;
  414. wDateTime[MINUTE] = SystemTime.wMinute;
  415. wDateTime[SECOND] = SystemTime.wSecond;
  416. wDateTime[MONTH] = SystemTime.wMonth;
  417. wDateTime[DAY] = SystemTime.wDay;
  418. wDateTime[YEAR] = SystemTime.wYear;
  419. wDateTime[WEEKDAY] = SystemTime.wDayOfWeek;
  420. }
  421. ////////////////////////////////////////////////////////////////////////////
  422. //
  423. // GetTime
  424. //
  425. ////////////////////////////////////////////////////////////////////////////
  426. void GetTime()
  427. {
  428. SYSTEMTIME SystemTime;
  429. GetLocalTime(&SystemTime);
  430. wDateTime[HOUR] = SystemTime.wHour;
  431. wDateTime[MINUTE] = SystemTime.wMinute;
  432. wDateTime[SECOND] = SystemTime.wSecond;
  433. }
  434. ////////////////////////////////////////////////////////////////////////////
  435. //
  436. // GetDate
  437. //
  438. ////////////////////////////////////////////////////////////////////////////
  439. void GetDate()
  440. {
  441. SYSTEMTIME SystemTime;
  442. GetLocalTime(&SystemTime);
  443. wDateTime[MONTH] = SystemTime.wMonth;
  444. wDateTime[DAY] = SystemTime.wDay;
  445. wDateTime[YEAR] = SystemTime.wYear;
  446. wDateTime[WEEKDAY] = SystemTime.wDayOfWeek;
  447. fDateDirty = FALSE;
  448. }
  449. ////////////////////////////////////////////////////////////////////////////
  450. //
  451. // SetTime
  452. //
  453. ////////////////////////////////////////////////////////////////////////////
  454. void SetTime()
  455. {
  456. SYSTEMTIME SystemTime;
  457. SystemTime.wHour = wDateTime[HOUR];
  458. SystemTime.wMinute = wDateTime[MINUTE];
  459. SystemTime.wSecond = wDateTime[SECOND];
  460. SystemTime.wMilliseconds = 0;
  461. SystemTime.wMonth = wDateTime[MONTH];
  462. SystemTime.wDay = wDateTime[DAY];
  463. SystemTime.wYear = wDateTime[YEAR];
  464. SetLocalTime(&SystemTime);
  465. SetLocalTime(&SystemTime);
  466. fDateDirty = FALSE;
  467. }
  468. ////////////////////////////////////////////////////////////////////////////
  469. //
  470. // SetDate
  471. //
  472. ////////////////////////////////////////////////////////////////////////////
  473. void SetDate()
  474. {
  475. SYSTEMTIME SystemTime;
  476. SystemTime.wHour = wDateTime[HOUR];
  477. SystemTime.wMinute = wDateTime[MINUTE];
  478. SystemTime.wSecond = wDateTime[SECOND];
  479. SystemTime.wMilliseconds = 0;
  480. SystemTime.wMonth = wDateTime[MONTH];
  481. SystemTime.wDay = wDateTime[DAY];
  482. SystemTime.wYear = wDateTime[YEAR];
  483. SetLocalTime(&SystemTime);
  484. SetLocalTime(&SystemTime);
  485. fDateDirty = FALSE;
  486. }
  487. ////////////////////////////////////////////////////////////////////////////
  488. //
  489. // AdjustDelta
  490. //
  491. // Alters the variables in wDeltaDateTime, allowing a CANCEL button
  492. // to perform its job by resetting the time as if it had never been
  493. // touched. GetTime() & GetDate() should already have been called.
  494. //
  495. ////////////////////////////////////////////////////////////////////////////
  496. void AdjustDelta(
  497. HWND hDlg,
  498. int nIndex)
  499. {
  500. int nDelta;
  501. //
  502. // We dont do time this way any more.
  503. //
  504. if (nIndex <= SECOND && nIndex >= HOUR)
  505. {
  506. return;
  507. }
  508. //
  509. // Get position of the buddy from either the date or the time.
  510. //
  511. nDelta = (int)SendDlgItemMessage( hDlg,
  512. nIndex <= SECOND
  513. ? DATETIME_TARROW
  514. : DATETIME_YARROW,
  515. UDM_GETPOS,
  516. 0,
  517. 0L );
  518. if ((nIndex == YEAR) && !g_bLZero[YEAR])
  519. {
  520. //
  521. // Years before 80 are 2080.
  522. // Range is 1980...2079.
  523. //
  524. if (nDelta < 80)
  525. {
  526. nDelta += 2000;
  527. }
  528. else
  529. {
  530. nDelta += 1900;
  531. }
  532. }
  533. //
  534. // If our current recording of the time/date is not what we have
  535. // now, do deltas.
  536. //
  537. if (wDateTime[nIndex] != nDelta)
  538. {
  539. //
  540. // Previous value is current user's settings.
  541. //
  542. wPrevDateTime[nIndex] = wDateTime[nIndex] = (WORD)nDelta;
  543. fDateDirty = TRUE;
  544. //
  545. // If we are changing HMS, update the time.
  546. //
  547. if (nIndex <= SECOND)
  548. {
  549. nIndex = 0;
  550. }
  551. }
  552. }
  553. ////////////////////////////////////////////////////////////////////////////
  554. //
  555. // AdjustDeltaMonth
  556. //
  557. // Change the month part of wDateTime
  558. //
  559. ////////////////////////////////////////////////////////////////////////////
  560. extern int GetDaysOfTheMonth(int iMonth);
  561. void AdjustDeltaMonth(
  562. int iMonth)
  563. {
  564. GetTime();
  565. if (wDateTime[MONTH] != iMonth)
  566. {
  567. //
  568. // Make sure the current day is valid in the new month.
  569. //
  570. if (wDateTime[DAY] > (WORD)GetDaysOfTheMonth(iMonth))
  571. {
  572. wDateTime[DAY] = (WORD)GetDaysOfTheMonth(iMonth);
  573. }
  574. wPrevDateTime[MONTH] = wDateTime[MONTH] = (WORD)iMonth;
  575. fDateDirty = TRUE;
  576. g_sDateInfo[DAY].nMax = MonthUpperBound( wDateTime[MONTH],
  577. wDateTime[YEAR] );
  578. }
  579. }
  580. ////////////////////////////////////////////////////////////////////////////
  581. //
  582. // ReadShortDate
  583. //
  584. // Verify that pszDate is one of MDY, DMY, or YMD.
  585. //
  586. ////////////////////////////////////////////////////////////////////////////
  587. int ReadShortDate(
  588. LPTSTR pszDate,
  589. BOOL *pbMonth,
  590. BOOL *pbDay,
  591. BOOL *pbYear)
  592. {
  593. int i, nOrder;
  594. BOOL *pbOrder[3];
  595. TCHAR cHope[3];
  596. //
  597. // nOrder : 0 = MDY
  598. // 1 = DMY
  599. // 2 = YMD
  600. //
  601. switch (cHope[0] = *pszDate)
  602. {
  603. case ( TEXT('M') ) :
  604. {
  605. nOrder = 0;
  606. pbOrder[0] = pbMonth;
  607. break;
  608. }
  609. case ( TEXT('d') ) :
  610. {
  611. nOrder = 1;
  612. pbOrder[0] = pbDay;
  613. break;
  614. }
  615. case ( TEXT('y') ) :
  616. {
  617. nOrder = 2;
  618. pbOrder[0] = pbYear;
  619. break;
  620. }
  621. default :
  622. {
  623. return (FALSE);
  624. }
  625. }
  626. //
  627. // Sets element 1.
  628. //
  629. if (nOrder) // 1 2
  630. {
  631. cHope[1] = TEXT('M');
  632. pbOrder[1] = pbMonth;
  633. }
  634. else // 0
  635. {
  636. cHope[1] = TEXT('d');
  637. pbOrder[1] = pbDay;
  638. }
  639. //
  640. // Sets element 2.
  641. //
  642. if (nOrder == 2) // 2
  643. {
  644. cHope[2] = TEXT('d');
  645. pbOrder[2] = pbDay;
  646. }
  647. else // 0 1
  648. {
  649. cHope[2] = TEXT('y');
  650. pbOrder[2] = pbYear;
  651. }
  652. //
  653. // Verifies that pszDate is of the form MDY DMY YMD.
  654. //
  655. for (i = 0; i < 3; i++, pszDate++)
  656. {
  657. if (*pszDate != cHope[i])
  658. {
  659. return (-1 - nOrder);
  660. }
  661. if (!(pszDate = ParseDateElement(pszDate, pbOrder[i])))
  662. {
  663. return (-1 - nOrder);
  664. }
  665. }
  666. //
  667. // Success. Return MDY, DMY or YMD index.
  668. //
  669. return (nOrder);
  670. }
  671. ////////////////////////////////////////////////////////////////////////////
  672. //
  673. // GetMaxCharWidth
  674. //
  675. // Determine the widest digit (safety against variable pitch fonts).
  676. //
  677. ////////////////////////////////////////////////////////////////////////////
  678. int GetMaxCharWidth(
  679. HDC hDC)
  680. {
  681. UINT nNumWidth[10];
  682. UINT i, nMaxNumWidth;
  683. GetCharWidth32(hDC, TEXT('0'), TEXT('9'), nNumWidth);
  684. for (nMaxNumWidth = 0, i = 0; i < 10; i++)
  685. {
  686. if (nNumWidth[i] > nMaxNumWidth)
  687. {
  688. nMaxNumWidth = nNumWidth[i];
  689. }
  690. }
  691. return (nMaxNumWidth);
  692. }
  693. ////////////////////////////////////////////////////////////////////////////
  694. //
  695. // GetMaxSubstitutedCharWidth
  696. //
  697. // Determine the widest digit (safety against variable pitch fonts), but
  698. // do it using strings so that if number substitution is on, we will get
  699. // the width of the number based on what will actually be displayed
  700. //
  701. ////////////////////////////////////////////////////////////////////////////
  702. int GetMaxSubstitutedCharWidth(
  703. HDC hDC)
  704. {
  705. char sz[2] = "0";
  706. TCHAR szAMPM[12];
  707. LONG i, nMaxNumWidth;
  708. DWORD dwWidth;
  709. SIZE size;
  710. for (nMaxNumWidth = 0, i = 0; i < 10; (*sz)++, i++)
  711. {
  712. if (GetTextExtentPoint32A(hDC, sz, 1, &size) && size.cx > nMaxNumWidth)
  713. nMaxNumWidth = size.cx;
  714. }
  715. if (nMaxNumWidth <= 8)
  716. {
  717. GetProfileString(szIntl, TEXT("s1159"), IntlDef.s1159, szAMPM, ARRAYSIZE(szAMPM));
  718. dwWidth = LOWORD(GetTextExtent(hDC, szAMPM, lstrlen(szAMPM)));
  719. if (dwWidth > 22)
  720. nMaxNumWidth = 10;
  721. GetProfileString(szIntl, TEXT("s2359"), IntlDef.s2359, szAMPM, ARRAYSIZE(szAMPM));
  722. dwWidth = LOWORD(GetTextExtent(hDC, szAMPM, lstrlen(szAMPM)));
  723. if (dwWidth > 22)
  724. nMaxNumWidth = 10;
  725. }
  726. return (nMaxNumWidth);
  727. }
  728. ////////////////////////////////////////////////////////////////////////////
  729. //
  730. // ReflectAMPM
  731. //
  732. // Sets the global g_bPM and updates the control to display AM or PM.
  733. //
  734. ////////////////////////////////////////////////////////////////////////////
  735. void ReflectAMPM(
  736. HWND hDlg,
  737. int nNum)
  738. {
  739. HWND hCtl = GetDlgItem(hDlg, DATETIME_AMPM);
  740. ListBox_SetTopIndex(hCtl, g_bPM);
  741. ListBox_SetCurSel(hCtl, (GetFocus() == hCtl) ? g_bPM : -1);
  742. }
  743. ////////////////////////////////////////////////////////////////////////////
  744. //
  745. // GetTextExtent
  746. //
  747. ////////////////////////////////////////////////////////////////////////////
  748. #ifdef WIN32
  749. DWORD GetTextExtent(
  750. HDC hdc,
  751. LPCTSTR lpsz,
  752. int cb)
  753. {
  754. SIZE sz;
  755. BOOL bSuccess = GetTextExtentPoint(hdc, lpsz, cb, &sz);
  756. if ( !bSuccess )
  757. {
  758. ZeroMemory( &sz, sizeof(sz) );
  759. }
  760. return ( MAKELONG((WORD)sz.cx, (WORD)sz.cy) );
  761. }
  762. #endif
  763. ////////////////////////////////////////////////////////////////////////////
  764. //
  765. // DateTimeInit
  766. //
  767. // Determine the widest digit (safety against variable pitch fonts).
  768. //
  769. ////////////////////////////////////////////////////////////////////////////
  770. void DateTimeInit(
  771. HWND hDlg,
  772. WORD nBaseID,
  773. WORD nSepID,
  774. LPTSTR pszSep,
  775. int nMaxDigitWidth,
  776. BOOL bDate)
  777. {
  778. HWND hAMPMList;
  779. HWND hDay, hMonth, hYear; // also used as hHour, hMinute, & hSecond
  780. HWND hOrder[5];
  781. HDC hDC;
  782. int nWidth, nHeight, X;
  783. DWORD dwSepExt;
  784. RECT Rect;
  785. int i;
  786. int nAMPMlength;
  787. hMonth = GetDlgItem(hDlg, nBaseID);
  788. hDay = GetDlgItem(hDlg, nBaseID + 1);
  789. hYear = GetDlgItem(hDlg, nBaseID + 2);
  790. hOrder[1] = GetDlgItem(hDlg, nSepID);
  791. hOrder[3] = GetDlgItem(hDlg, nSepID + 1);
  792. if (bDate)
  793. {
  794. i = GetProfileInt(szIntl, TEXT("iDate"), 0);
  795. }
  796. else
  797. {
  798. if (g_b24HR = (BOOL)GetProfileInt(szIntl, TEXT("iTime"), 0))
  799. {
  800. g_sDateInfo[HOUR].nMin = 0;
  801. g_sDateInfo[HOUR].nMax = 23;
  802. }
  803. else
  804. {
  805. g_sDateInfo[HOUR].nMin = 1;
  806. g_sDateInfo[HOUR].nMax = 12;
  807. GetProfileString(szIntl, TEXT("s1159"), IntlDef.s1159, sz1159, ARRAYSIZE(sz1159));
  808. GetProfileString(szIntl, TEXT("s2359"), IntlDef.s2359, sz2359, ARRAYSIZE(sz2359));
  809. }
  810. i = 0;
  811. }
  812. switch (i)
  813. {
  814. case ( 1 ) :
  815. {
  816. hOrder[0] = hDay;
  817. hOrder[2] = hMonth;
  818. hOrder[4] = hYear;
  819. break;
  820. }
  821. case ( 2 ) :
  822. {
  823. hOrder[0] = hYear;
  824. hOrder[2] = hMonth;
  825. hOrder[4] = hDay;
  826. break;
  827. }
  828. case ( 0 ) :
  829. default :
  830. {
  831. hOrder[0] = hMonth;
  832. hOrder[2] = hDay;
  833. hOrder[4] = hYear;
  834. break;
  835. }
  836. }
  837. hDC = GetDC(hDlg);
  838. if (!bDate)
  839. {
  840. dwSepExt = GetTextExtent(hDC, sz1159, lstrlen(sz1159));
  841. nAMPMlength = LOWORD(GetTextExtent(hDC, sz2359, lstrlen(sz2359)));
  842. if (nAMPMlength < (int)LOWORD(dwSepExt))
  843. {
  844. nAMPMlength = (int)LOWORD(dwSepExt);
  845. }
  846. }
  847. dwSepExt = GetTextExtent(hDC, pszSep, lstrlen(pszSep));
  848. ReleaseDC(hDlg, hDC);
  849. GetWindowRect(hYear, (LPRECT)&Rect);
  850. ScreenToClient(hDlg, (LPPOINT)&Rect.left);
  851. ScreenToClient(hDlg, (LPPOINT)&Rect.right);
  852. nHeight = Rect.bottom - Rect.top;
  853. nWidth = Rect.top;
  854. GetWindowRect( GetDlgItem( hDlg,
  855. bDate ? DATETIME_CALENDAR : DATETIME_CLOCK ),
  856. (LPRECT)&Rect );
  857. ScreenToClient(hDlg, (LPPOINT)&Rect.left);
  858. ScreenToClient(hDlg, (LPPOINT)&Rect.right);
  859. Rect.top = nWidth;
  860. X = (Rect.left + Rect.right - (6 * nMaxDigitWidth) - (2 * LOWORD(dwSepExt))) / 2;
  861. if (bDate)
  862. {
  863. if (g_bLZero[YEAR])
  864. {
  865. X -= nMaxDigitWidth;
  866. }
  867. }
  868. else if (!g_b24HR)
  869. {
  870. X -= nAMPMlength / 2;
  871. }
  872. for (i = 0; i < 5; i++)
  873. {
  874. nWidth = (i % 2) ? LOWORD(dwSepExt) : 2 * nMaxDigitWidth;
  875. if ((hOrder[i] == hYear) && bDate && g_bLZero[YEAR])
  876. {
  877. nWidth *= 2;
  878. }
  879. //
  880. // Allow for centering in edit control.
  881. //
  882. nWidth += 2;
  883. // MoveWindow(hOrder[i], X, Rect.top, nWidth, nHeight, FALSE);
  884. X += nWidth;
  885. }
  886. hAMPMList = GetDlgItem(hDlg, DATETIME_AMPM);
  887. ListBox_ResetContent(hAMPMList);
  888. if (!bDate && !g_b24HR)
  889. {
  890. ListBox_InsertString(hAMPMList, 0, sz1159);
  891. ListBox_InsertString(hAMPMList, 1, sz2359);
  892. }
  893. EnableWindow(hAMPMList, !g_b24HR);
  894. Edit_LimitText(hYear, (bDate && g_bLZero[YEAR]) ? 4 : 2);
  895. Edit_LimitText(hMonth, 2);
  896. Edit_LimitText(hDay, 2);
  897. SetDlgItemText(hDlg, nSepID, pszSep);
  898. SetDlgItemText(hDlg, nSepID + 1, pszSep);
  899. }
  900. ////////////////////////////////////////////////////////////////////////////
  901. //
  902. // myitoa
  903. //
  904. ////////////////////////////////////////////////////////////////////////////
  905. void myitoa(
  906. int intValue,
  907. LPTSTR lpStr)
  908. {
  909. LPTSTR lpString;
  910. TCHAR c;
  911. //
  912. // lpString points to 1st char.
  913. //
  914. lpString = lpStr;
  915. do
  916. {
  917. *lpStr++ = (TCHAR)(intValue % 10 + TEXT('0'));
  918. } while ((intValue /= 10) > 0);
  919. //
  920. // lpStr points to last char.
  921. //
  922. *lpStr-- = TEXT('\0');
  923. //
  924. // Now reverse the string.
  925. //
  926. while (lpString < lpStr)
  927. {
  928. c = *lpString;
  929. *(lpString++) = *lpStr;
  930. *(lpStr--) = c;
  931. }
  932. }
  933. ////////////////////////////////////////////////////////////////////////////
  934. //
  935. // UpdateItem
  936. //
  937. // This displays the information in the control from the array
  938. // of global values. Also selects the control. Also adds leading 0's
  939. // as well as rounding years to 2 digits and 24 or AM/PM hours.
  940. //
  941. ////////////////////////////////////////////////////////////////////////////
  942. void UpdateItem(
  943. HWND hDlg,
  944. int i)
  945. {
  946. TCHAR szNum[5];
  947. int nNum = g_Modified ? wPrevDateTime[i] : wDateTime[i];
  948. //
  949. // Use internal time.
  950. //
  951. if (i <= SECOND && i >= HOUR)
  952. {
  953. nNum = g_Time[i];
  954. //
  955. // Do not paint un-necessarily.
  956. //
  957. if ((nNum == g_LastTime[i]) && (nNum >= 10))
  958. {
  959. return;
  960. }
  961. g_LastTime[i] = nNum;
  962. if (i == HOUR)
  963. {
  964. if (IsAMPM(nNum))
  965. {
  966. g_bPM = TRUE;
  967. }
  968. ReflectAMPM(hDlg, nNum);
  969. }
  970. }
  971. if (i == YEAR)
  972. {
  973. //
  974. // Round the years to last 2 digits.
  975. //
  976. if (!g_bLZero[i])
  977. {
  978. nNum %= 100;
  979. }
  980. }
  981. else if ((i == HOUR) && !g_b24HR)
  982. {
  983. //
  984. // nNum came from our internal date time.
  985. // Remove 12 hours if not 24hour.
  986. //
  987. if (g_bPM)
  988. {
  989. nNum %= 12;
  990. }
  991. //
  992. // 00 hours is actually 12AM.
  993. //
  994. if (!nNum)
  995. {
  996. nNum = 12;
  997. }
  998. }
  999. //
  1000. // See if we need leading zeros.
  1001. // We only deal with 2 character numbers MAX.
  1002. //
  1003. if ((nNum < 10) && (g_bLZero[i] || (i == YEAR)))
  1004. {
  1005. szNum[0] = TEXT('0');
  1006. szNum[1] = (TCHAR)(TEXT('0') + nNum);
  1007. szNum[2] = TEXT('\0');
  1008. }
  1009. else
  1010. {
  1011. myitoa(nNum, szNum);
  1012. }
  1013. //
  1014. // Reflect the value in the appropriate control.
  1015. //
  1016. SetDlgItemText(hDlg, DATETIME_HOUR + i, szNum);
  1017. //
  1018. // Select the field too.
  1019. //
  1020. SendDlgItemMessage(hDlg, DATETIME_HOUR + i, EM_SETSEL, 0, MAKELONG(0, 32767));
  1021. //
  1022. // If we changed year or month, then we may have altered the leap year
  1023. // state.
  1024. //
  1025. if (i == MONTH || i == YEAR)
  1026. {
  1027. g_sDateInfo[DAY].nMax = MonthUpperBound( wDateTime[MONTH],
  1028. wDateTime[YEAR] );
  1029. }
  1030. }
  1031. ////////////////////////////////////////////////////////////////////////////
  1032. //
  1033. // _ShowTZ
  1034. //
  1035. ////////////////////////////////////////////////////////////////////////////
  1036. TCHAR c_szFirstBootTZ[] = TEXT("!!!First Boot!!!");
  1037. void _ShowTZ(
  1038. HWND hDlg)
  1039. {
  1040. HWND ctl = GetDlgItem(hDlg, DATETIME_CURTZ);
  1041. TIME_ZONE_INFORMATION info;
  1042. TCHAR final[64 + TZNAME_SIZE];
  1043. TCHAR name[TZNAME_SIZE];
  1044. DWORD TimeZoneId;
  1045. if (g_bFirstBoot)
  1046. {
  1047. ShowWindow(ctl, SW_HIDE);
  1048. }
  1049. else
  1050. {
  1051. TimeZoneId = GetTimeZoneInformation(&info);
  1052. #ifdef UNICODE
  1053. lstrcpy( name,
  1054. (TimeZoneId == TIME_ZONE_ID_STANDARD)
  1055. ? info.StandardName
  1056. : info.DaylightName );
  1057. #else
  1058. WideStrToStr(name, (TimeZoneId == TIME_ZONE_ID_STANDARD)
  1059. ? info.StandardName
  1060. : info.DaylightName );
  1061. #endif //UNICODE
  1062. //
  1063. // Display nothing if it is our special 1st boot marker.
  1064. //
  1065. if (*name && (lstrcmpi(name, c_szFirstBootTZ) != 0))
  1066. {
  1067. static TCHAR format[128] = TEXT("");
  1068. if (!*format)
  1069. {
  1070. GetWindowText( ctl,
  1071. format,
  1072. ARRAYSIZE(format) );
  1073. }
  1074. wsprintf(final, format, name);
  1075. }
  1076. else
  1077. {
  1078. *final = 0;
  1079. }
  1080. SetWindowText(ctl, final);
  1081. }
  1082. }
  1083. ////////////////////////////////////////////////////////////////////////////
  1084. //
  1085. // UnhookTime
  1086. //
  1087. // To stop the clock calling us back all the time (around exit).
  1088. //
  1089. ////////////////////////////////////////////////////////////////////////////
  1090. void UnhookTimer(
  1091. HWND hDlg)
  1092. {
  1093. SendDlgItemMessage(hDlg, DATETIME_CLOCK, CLM_TIMEHWND, CLF_SETHWND, 0);
  1094. }
  1095. ////////////////////////////////////////////////////////////////////////////
  1096. //
  1097. // TimeProvider
  1098. //
  1099. // Called by the clock to find out what time it is.
  1100. //
  1101. ////////////////////////////////////////////////////////////////////////////
  1102. void TimeProvider(
  1103. LPSYSTEMTIME lpSystemTime,
  1104. HWND hDlg)
  1105. {
  1106. short wTemp[7];
  1107. //
  1108. // If the user has modified the time, the clock should
  1109. // display the edit controls, otherwise its just the SystemTime.
  1110. //
  1111. if (g_Modified)
  1112. {
  1113. lpSystemTime->wHour = (WORD)g_Time[HOUR];
  1114. lpSystemTime->wMinute = (WORD)g_Time[MINUTE];
  1115. lpSystemTime->wSecond = (WORD)g_Time[SECOND];
  1116. }
  1117. else
  1118. {
  1119. #ifdef WIN32
  1120. GetLocalTime(lpSystemTime);
  1121. #else
  1122. GetTime();
  1123. if (wDateTime[HOUR] >= 0 && wDateTime[HOUR] <= 24)
  1124. {
  1125. lpSystemTime->wHour = wDateTime[HOUR];
  1126. }
  1127. lpSystemTime->wMinute = wDateTime[MINUTE];
  1128. lpSystemTime->wSecond = wDateTime[SECOND];
  1129. #endif
  1130. //
  1131. // Copy the time and display it for us too.
  1132. //
  1133. g_bPM = IsAMPM(lpSystemTime->wHour);
  1134. g_Time[HOUR] = lpSystemTime->wHour;
  1135. g_Time[MINUTE] = lpSystemTime->wMinute;
  1136. g_Time[SECOND] = lpSystemTime->wSecond;
  1137. //
  1138. // Check for date rollover.
  1139. //
  1140. if (!fDateDirty)
  1141. {
  1142. wTemp[DAY] = wDateTime[DAY];
  1143. wTemp[MONTH] = wDateTime[MONTH];
  1144. wTemp[YEAR] = wDateTime[YEAR];
  1145. GetDate();
  1146. if ((wDateTime[DAY] != wTemp[DAY]) ||
  1147. (wDateTime[MONTH] != wTemp[MONTH]) ||
  1148. (wDateTime[YEAR] != wTemp[YEAR]))
  1149. {
  1150. InvalidateRect(GetDlgItem(hDlg, DATETIME_CALENDAR), NULL, TRUE);
  1151. if (wDateTime[MONTH] != wTemp[MONTH])
  1152. {
  1153. ComboBox_SetCurSel( GetDlgItem(hDlg, DATETIME_MONTHNAME),
  1154. wDateTime[MONTH] - 1 );
  1155. }
  1156. if (wDateTime[YEAR] != wTemp[YEAR])
  1157. {
  1158. UpdateItem(hDlg, YEAR);
  1159. }
  1160. _ShowTZ(hDlg);
  1161. }
  1162. }
  1163. UpdateItem(hDlg, HOUR);
  1164. UpdateItem(hDlg, MINUTE);
  1165. UpdateItem(hDlg, SECOND);
  1166. ReflectAMPM(hDlg, g_Time[HOUR]);
  1167. }
  1168. }
  1169. ////////////////////////////////////////////////////////////////////////////
  1170. //
  1171. // bSupportedCalendar
  1172. //
  1173. // Returns True if the current calendar is not Hijri nor Hebrew
  1174. //
  1175. // Otherwise it returns FALSE.
  1176. //
  1177. ////////////////////////////////////////////////////////////////////////////
  1178. BOOL bSupportedCalendar()
  1179. {
  1180. TCHAR tchCalendar[32];
  1181. CALTYPE defCalendar = CAL_GREGORIAN;
  1182. if (GetLocaleInfo(LOCALE_USER_DEFAULT,
  1183. LOCALE_ICALENDARTYPE,
  1184. tchCalendar,
  1185. ARRAYSIZE(tchCalendar)))
  1186. {
  1187. defCalendar = StrToInt(tchCalendar);
  1188. }
  1189. return (!(defCalendar == CAL_HIJRI || defCalendar == CAL_HEBREW));
  1190. }
  1191. ////////////////////////////////////////////////////////////////////////////
  1192. //
  1193. // InitDateTimeDlg
  1194. //
  1195. // Called to init the dialog.
  1196. //
  1197. ////////////////////////////////////////////////////////////////////////////
  1198. void InitDateTimeDlg(
  1199. HWND hDlg,
  1200. UINT message,
  1201. WPARAM wParam,
  1202. LPARAM lParam)
  1203. {
  1204. int nMaxDigitWidth;
  1205. int i;
  1206. TCHAR szNum[5];
  1207. TCHAR szMonth[64];
  1208. TCHAR szShortDate[12];
  1209. HDC hDC;
  1210. HFONT hFont;
  1211. HWND hwndCB;
  1212. CALID calId;
  1213. static int nInc[] = { 1, 5, 5, 1, 1, 5 };
  1214. HWND hwndScroll;
  1215. UDACCEL udAccel[2];
  1216. HWND hwndTBorder;
  1217. HCURSOR oldcursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1218. LCID lcid = LOCALE_USER_DEFAULT;
  1219. InitCommonControls();
  1220. //
  1221. // Sets the Leading zero status of the 6 controls.
  1222. //
  1223. g_bLZero[HOUR] = g_bLZero[MONTH] = g_bLZero[DAY] = FALSE;
  1224. g_bLZero[MINUTE] = g_bLZero[SECOND] = g_bLZero[YEAR] = TRUE;
  1225. hDC = GetDC(hDlg);
  1226. if (hFont = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0L))
  1227. {
  1228. hFont = SelectObject( hDC, hFont );
  1229. }
  1230. if (hFont)
  1231. {
  1232. SelectObject(hDC, hFont);
  1233. }
  1234. AdjustAMPMPosition(hDlg);
  1235. nMaxDigitWidth = GetMaxCharWidth(hDC);
  1236. ReleaseDC(hDlg, hDC);
  1237. g_bLZero[HOUR] = GetProfileInt(szIntl, TEXT("iTLZero"), 0);
  1238. //
  1239. // Initialize szShortDate in case GetProfileString fails.
  1240. //
  1241. lstrcpyn(szShortDate, IntlDef.sShortDate, ARRAYSIZE(szShortDate));
  1242. GetProfileString(szIntl, TEXT("sShortDate"), IntlDef.sShortDate, szShortDate, ARRAYSIZE(szShortDate));
  1243. ReadShortDate(szShortDate, g_bLZero + MONTH, g_bLZero + DAY, g_bLZero + YEAR);
  1244. g_bLZero[YEAR] = TRUE; //we always want the year to be 4 digits (this will be bad in late 9999)
  1245. //
  1246. // Setup the TIME stuff.
  1247. //
  1248. GetTime();
  1249. g_Time[HOUR] = wDateTime[HOUR];
  1250. g_Time[MINUTE] = wDateTime[MINUTE];
  1251. g_Time[SECOND] = wDateTime[SECOND];
  1252. GetProfileString(szIntl, TEXT("sTime"), IntlDef.sTime, szNum, 3);
  1253. DateTimeInit(hDlg, DATETIME_HOUR, DATETIME_TSEP1, szNum, nMaxDigitWidth, FALSE);
  1254. //
  1255. // Force all entries to be re-drawn,
  1256. //
  1257. g_LastTime[HOUR] = g_LastTime[MINUTE] = g_LastTime[SECOND] = -1;
  1258. UpdateItem(hDlg, HOUR);
  1259. UpdateItem(hDlg, MINUTE);
  1260. UpdateItem(hDlg, SECOND);
  1261. ReflectAMPM(hDlg, wDateTime[HOUR]);
  1262. //
  1263. // Setup the Date stuff.
  1264. //
  1265. GetDate();
  1266. g_sDateInfo[DAY].nMax = MonthUpperBound(wDateTime[MONTH], wDateTime[YEAR]);
  1267. if (!g_bLZero[YEAR])
  1268. {
  1269. wDateTime[YEAR] %= 100;
  1270. g_sDateInfo[YEAR].nMax = 99;
  1271. g_sDateInfo[YEAR].nMin = 0;
  1272. }
  1273. else
  1274. {
  1275. g_sDateInfo[YEAR].nMax = 2099;
  1276. g_sDateInfo[YEAR].nMin = 1980;
  1277. }
  1278. for (i = MONTH; i <= YEAR; i++)
  1279. {
  1280. wPrevDateTime[i] = -1;
  1281. }
  1282. //
  1283. // Get the month names. And select this month.
  1284. //
  1285. hwndCB = GetDlgItem(hDlg, DATETIME_MONTHNAME);
  1286. ComboBox_ResetContent(hwndCB);
  1287. //
  1288. // If the current calendar is Hijri or Hebrew then use the Gregorian one.
  1289. //
  1290. if (!bSupportedCalendar())
  1291. lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
  1292. GetLocaleInfo(lcid, LOCALE_ICALENDARTYPE, szMonth, ARRAYSIZE(szMonth));
  1293. calId = (CALID)StrToInt(szMonth);
  1294. for (i = 0; i < 12; i++)
  1295. {
  1296. #ifdef WINNT
  1297. GetCalendarInfo(lcid, calId, CAL_SMONTHNAME1 + i, szMonth, ARRAYSIZE(szMonth), NULL);
  1298. #else
  1299. GetLocaleInfo( LOCALE_USER_DEFAULT,
  1300. LOCALE_SMONTHNAME1 + i,
  1301. szMonth,
  1302. sizeof(szMonth) );
  1303. if (*szMonth && !IsDBCSLeadByte(*szMonth))
  1304. {
  1305. *szMonth = (TCHAR)CharUpper((LPTSTR)(DWORD)(TBYTE)*szMonth);
  1306. }
  1307. #endif
  1308. ComboBox_AddString(hwndCB, szMonth);
  1309. }
  1310. ComboBox_SetCurSel(hwndCB, wDateTime[MONTH] - 1);
  1311. //
  1312. // Set the default modifier for the Year Updown arrows.
  1313. //
  1314. wParam -= DATETIME_HOUR;
  1315. hwndScroll = GetDlgItem(hDlg, DATETIME_YARROW);
  1316. SendMessage( hwndScroll,
  1317. UDM_SETRANGE,
  1318. 0,
  1319. MAKELPARAM(g_sDateInfo[YEAR].nMax, g_sDateInfo[YEAR].nMin) );
  1320. udAccel[0].nSec = 0;
  1321. udAccel[0].nInc = 1;
  1322. udAccel[1].nSec = 2;
  1323. udAccel[1].nInc = nInc[YEAR];
  1324. SendMessage(hwndScroll, UDM_SETACCEL, 2, (LPARAM)(LPUDACCEL)udAccel);
  1325. SendMessage(hwndScroll, UDM_SETBUDDY, (WPARAM)GetDlgItem(hDlg, DATETIME_YEAR), 0L);
  1326. //
  1327. // Set the default modifier for the time arrows.
  1328. // It should control the HOURS by default as per joelgros
  1329. //
  1330. hwndScroll = GetDlgItem(hDlg, DATETIME_TARROW);
  1331. SendMessage( hwndScroll,
  1332. UDM_SETRANGE,
  1333. 0,
  1334. MAKELPARAM( g_sDateInfo[HOUR].nMax,
  1335. g_sDateInfo[HOUR].nMin) );
  1336. udAccel[0].nSec = 0;
  1337. udAccel[0].nInc = 1;
  1338. udAccel[1].nSec = 2;
  1339. udAccel[1].nInc = nInc[HOUR];
  1340. SendMessage( hwndScroll, UDM_SETACCEL, 2, (LPARAM)(LPUDACCEL)udAccel );
  1341. SendMessage( hwndScroll, UDM_SETBUDDY, (WPARAM)GetDlgItem(hDlg, DATETIME_HOUR), 0L );
  1342. //
  1343. // Make the 'well' for the digits appear.
  1344. //
  1345. hwndTBorder = GetDlgItem(hDlg, DATETIME_TBORDER);
  1346. SetWindowLong( hwndTBorder,
  1347. GWL_EXSTYLE,
  1348. GetWindowLong(hwndTBorder, GWL_EXSTYLE) | WS_EX_CLIENTEDGE );
  1349. //
  1350. // Display the border right now.
  1351. //
  1352. SetWindowPos( hwndTBorder,
  1353. NULL,
  1354. 0, 0, 0, 0,
  1355. SWP_NOMOVE | SWP_NOSIZE | SWP_DRAWFRAME | SWP_SHOWWINDOW );
  1356. //
  1357. // Display month->year.
  1358. //
  1359. for (i = MONTH; i <= YEAR; i++)
  1360. {
  1361. if ((wDateTime[i] != wPrevDateTime[i]) &&
  1362. (GetFocus() != GetDlgItem(hDlg, DATETIME_HOUR + i)))
  1363. {
  1364. //
  1365. // Update previous date-time.
  1366. //
  1367. wPrevDateTime[i] = wDateTime[i];
  1368. if (i == YEAR)
  1369. {
  1370. UpdateItem(hDlg, i);
  1371. }
  1372. }
  1373. }
  1374. g_Modified = FALSE;
  1375. //
  1376. // Tell the clock that we have a time provider - must be done last.
  1377. //
  1378. SendDlgItemMessage( hDlg,
  1379. DATETIME_CLOCK,
  1380. CLM_TIMEHWND,
  1381. CLF_SETHWND,
  1382. (LPARAM)(LPINT)hDlg );
  1383. SetCursor(oldcursor);
  1384. }
  1385. ////////////////////////////////////////////////////////////////////////////
  1386. //
  1387. // CheckNum
  1388. //
  1389. ////////////////////////////////////////////////////////////////////////////
  1390. LRESULT CheckNum(
  1391. HWND hDlg,
  1392. UINT nScrollID,
  1393. HWND hCtl)
  1394. {
  1395. static int cReenter = 0;
  1396. LRESULT lRet;
  1397. //
  1398. // If this is an illegal value, (but not blank), then kill the last char
  1399. // that was entered.
  1400. //
  1401. lRet = SendDlgItemMessage(hDlg, nScrollID, UDM_GETPOS, 0, 0L);
  1402. //
  1403. // Guard against re-entrance.
  1404. //
  1405. ++cReenter;
  1406. if (cReenter <= 4)
  1407. {
  1408. SendMessage( hCtl,
  1409. HIWORD(lRet) && GetWindowTextLength(hCtl)
  1410. ? EM_UNDO
  1411. : EM_EMPTYUNDOBUFFER,
  1412. 0,
  1413. 0L );
  1414. }
  1415. --cReenter;
  1416. return (lRet);
  1417. }
  1418. ////////////////////////////////////////////////////////////////////////////
  1419. //
  1420. // DateTimeDlgProc
  1421. //
  1422. // Main dialog proc.
  1423. //
  1424. ////////////////////////////////////////////////////////////////////////////
  1425. INT_PTR CALLBACK DateTimeDlgProc(
  1426. HWND hDlg,
  1427. UINT uMsg,
  1428. WPARAM wParam,
  1429. LPARAM lParam)
  1430. {
  1431. int i;
  1432. switch (uMsg)
  1433. {
  1434. case (WM_INITDIALOG):
  1435. {
  1436. AddInternetPageAsync(GetParent(hDlg), hDlg);
  1437. InitDateTimeDlg(hDlg, uMsg, wParam, lParam);
  1438. g_PrevIMCForDateField = ImmAssociateContext(GetDlgItem(hDlg, DATETIME_YEAR), 0);
  1439. break;
  1440. }
  1441. case ( WM_DESTROY ) :
  1442. {
  1443. if (g_PrevIMCForDateField)
  1444. {
  1445. ImmAssociateContext( GetDlgItem(hDlg, DATETIME_YEAR),
  1446. g_PrevIMCForDateField );
  1447. }
  1448. UnhookTimer(hDlg);
  1449. break;
  1450. }
  1451. #ifdef WIN32
  1452. case ( WM_CTLCOLORSTATIC ) :
  1453. #endif
  1454. case ( WM_CTLCOLOR ) :
  1455. {
  1456. //
  1457. // Set the background color of the time controls to the the
  1458. // color of the edit controls.
  1459. //
  1460. if ((GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg) ==
  1461. GetDlgItem(hDlg, DATETIME_TSEP1)) ||
  1462. (GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg) ==
  1463. GetDlgItem(hDlg, DATETIME_TSEP2)) ||
  1464. (GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg) ==
  1465. GetDlgItem(hDlg, DATETIME_TBORDER)))
  1466. {
  1467. #ifndef WIN32
  1468. //
  1469. // Make the statics the color of the edits.
  1470. //
  1471. lParam = GET_WM_CTLCOLOR_MPS(
  1472. GET_WM_CTLCOLOR_HDC(wParam, lParam, uMsg),
  1473. GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg),
  1474. CTLCOLOR_EDIT );
  1475. return ((INT_PTR)DefWindowProc(hDlg, uMsg, wParam, lParam));
  1476. #else
  1477. return ((INT_PTR)DefWindowProc(hDlg, WM_CTLCOLOREDIT, wParam, lParam));
  1478. #endif
  1479. }
  1480. return (0);
  1481. break;
  1482. }
  1483. case ( WM_NOTIFY ) :
  1484. {
  1485. //
  1486. // Property sheet handler stuff.
  1487. //
  1488. switch (((NMHDR *)lParam)->code)
  1489. {
  1490. case ( PSN_SETACTIVE ) :
  1491. {
  1492. _ShowTZ(hDlg);
  1493. break;
  1494. }
  1495. case ( PSN_RESET ) :
  1496. {
  1497. UnhookTimer(hDlg);
  1498. SetFocus(GetDlgItem(hDlg, (int)wParam));
  1499. GetDate();
  1500. GetTime();
  1501. break;
  1502. }
  1503. case ( PSN_APPLY ) :
  1504. {
  1505. wDateTime[MINUTE] = (WORD)g_Time[MINUTE];
  1506. wDateTime[SECOND] = (WORD)g_Time[SECOND];
  1507. if (g_b24HR)
  1508. {
  1509. wDateTime[HOUR] = (WORD)g_Time[HOUR];
  1510. }
  1511. else
  1512. {
  1513. wDateTime[HOUR] = g_Time[HOUR] % 12;
  1514. if (g_bPM)
  1515. {
  1516. wDateTime[HOUR] += 12;
  1517. }
  1518. }
  1519. g_WasModified = g_Modified;
  1520. SetTime();
  1521. g_LastTime[HOUR] = g_LastTime[MINUTE] = g_LastTime[SECOND] = -1;
  1522. for (i = MONTH; i <= YEAR; i++)
  1523. {
  1524. wPrevDateTime[i] = -1;
  1525. }
  1526. g_Modified = FALSE;
  1527. wPrevDateTime[HOUR] = wDateTime[HOUR];
  1528. wPrevDateTime[MINUTE] = wDateTime[MINUTE];
  1529. wPrevDateTime[SECOND] = wDateTime[SECOND];
  1530. wPrevDateTime[MONTH] = wDateTime[MONTH];
  1531. wPrevDateTime[DAY] = wDateTime[DAY];
  1532. wPrevDateTime[YEAR] = wDateTime[YEAR];
  1533. wPrevDateTime[WEEKDAY] = wDateTime[WEEKDAY];
  1534. //
  1535. // We handled it - no repaint.
  1536. //
  1537. return (TRUE);
  1538. }
  1539. }
  1540. break;
  1541. }
  1542. case ( WM_VSCROLL ) :
  1543. {
  1544. switch (GET_WM_VSCROLL_CODE(wParam, lParam))
  1545. {
  1546. case ( SB_THUMBPOSITION ) :
  1547. {
  1548. SYSTEMTIME SystemTime;
  1549. HWND hBuddy = (HWND)SendMessage(
  1550. GET_WM_VSCROLL_HWND(wParam, lParam),
  1551. UDM_GETBUDDY,
  1552. 0,
  1553. 0L );
  1554. if (hBuddy == GetDlgItem(hDlg, DATETIME_HOUR))
  1555. {
  1556. g_Time[HOUR] = GET_WM_VSCROLL_POS(wParam, lParam);
  1557. }
  1558. else if (hBuddy == GetDlgItem(hDlg, DATETIME_MINUTE))
  1559. {
  1560. g_Time[MINUTE] = GET_WM_VSCROLL_POS(wParam, lParam);
  1561. }
  1562. else if (hBuddy == GetDlgItem(hDlg, DATETIME_SECOND))
  1563. {
  1564. g_Time[SECOND] = GET_WM_VSCROLL_POS(wParam, lParam);
  1565. }
  1566. // else if (hBuddy == GetDlgItem(hDlg, DATETIME_AMPM))
  1567. if (hBuddy != GetDlgItem(hDlg, DATETIME_YEAR))
  1568. g_Modified = TRUE;
  1569. //
  1570. // Light the apply now button.
  1571. //
  1572. PropSheet_Changed(GetParent(hDlg), hDlg);
  1573. //
  1574. // Force the clock to reflect this setting.
  1575. //
  1576. TimeProvider(&SystemTime, hDlg);
  1577. SendDlgItemMessage( hDlg,
  1578. DATETIME_CLOCK,
  1579. CLM_UPDATETIME,
  1580. CLF_SETTIME,
  1581. (LPARAM)(LPSYSTEMTIME)&SystemTime );
  1582. //
  1583. // Fall thru to update the year...
  1584. //
  1585. }
  1586. case ( SB_ENDSCROLL ) :
  1587. {
  1588. //
  1589. // If this is the year, have the calendar repaint.
  1590. //
  1591. if ((HWND)SendMessage( GET_WM_VSCROLL_HWND(wParam, lParam),
  1592. UDM_GETBUDDY,
  1593. 0,
  1594. 0L ) == GetDlgItem(hDlg, DATETIME_YEAR))
  1595. {
  1596. //
  1597. // Have it update the information.
  1598. //
  1599. GetTime();
  1600. AdjustDelta(hDlg, YEAR);
  1601. UpdateItem(hDlg, YEAR);
  1602. InvalidateRect( GetDlgItem(hDlg, DATETIME_CALENDAR),
  1603. NULL,
  1604. TRUE );
  1605. }
  1606. break;
  1607. }
  1608. }
  1609. break;
  1610. }
  1611. case ( CLM_UPDATETIME ) :
  1612. {
  1613. //
  1614. // The clock updating/reflecting the time.
  1615. //
  1616. switch (wParam)
  1617. {
  1618. case ( CLF_SETTIME ) :
  1619. {
  1620. //
  1621. // Clock telling us what the time is.
  1622. //
  1623. g_Modified = TRUE;
  1624. g_Time[HOUR] = ((LPSYSTEMTIME)lParam)->wHour;
  1625. g_Time[MINUTE] = ((LPSYSTEMTIME)lParam)->wMinute;
  1626. g_Time[SECOND] = ((LPSYSTEMTIME)lParam)->wSecond;
  1627. g_bPM = IsAMPM(g_Time[HOUR]);
  1628. break;
  1629. }
  1630. case ( CLF_GETTIME ) :
  1631. {
  1632. //
  1633. // We tell the clock what time it is.
  1634. //
  1635. TimeProvider((LPSYSTEMTIME)lParam, hDlg);
  1636. break;
  1637. }
  1638. }
  1639. break;
  1640. }
  1641. case ( WM_COMMAND ) :
  1642. {
  1643. //
  1644. // Command processing.
  1645. //
  1646. switch (GET_WM_COMMAND_ID(wParam, lParam))
  1647. {
  1648. case ( DATETIME_AMPM ) :
  1649. {
  1650. //
  1651. // Deals with the AMPM control.
  1652. //
  1653. UDACCEL udAccel;
  1654. HWND hwndScroll = GetDlgItem(hDlg, DATETIME_TARROW);
  1655. HWND hwndThisCtl = GET_WM_COMMAND_HWND(wParam, lParam);
  1656. //
  1657. // We only care if we get/loose the focus.
  1658. //
  1659. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  1660. {
  1661. case ( LBN_SETFOCUS ) :
  1662. {
  1663. //
  1664. // If we get the focus, then the UD control
  1665. // should deal with the AMPM.
  1666. //
  1667. // Select the visible entry.
  1668. //
  1669. ReflectAMPM(hDlg, wDateTime[HOUR]);
  1670. // if it has a buddy, remove it...
  1671. if ((HWND)SendMessage( hwndScroll,
  1672. UDM_GETBUDDY,
  1673. 0,
  1674. 0 ) != NULL)
  1675. {
  1676. SendMessage(hwndScroll, UDM_SETBUDDY, 0, 0);
  1677. }
  1678. //
  1679. // Tell the UD control how to manipulate AM/PM.
  1680. //
  1681. SendMessage( hwndScroll,
  1682. UDM_SETRANGE,
  1683. 0,
  1684. MAKELPARAM(1, 0) );
  1685. udAccel.nSec = 0;
  1686. udAccel.nInc = 1;
  1687. SendMessage( hwndScroll,
  1688. UDM_SETACCEL,
  1689. 1,
  1690. (LPARAM)(LPUDACCEL)&udAccel );
  1691. SendMessage( hwndScroll,
  1692. UDM_SETBUDDY,
  1693. (WPARAM)hwndThisCtl,
  1694. 0 );
  1695. break;
  1696. }
  1697. case ( LBN_KILLFOCUS ) :
  1698. {
  1699. //
  1700. // When we loose focus, the g_bPM flag is updated.
  1701. //
  1702. // Remove selection from the AM/PM.
  1703. //
  1704. ListBox_SetCurSel(hwndThisCtl, -1);
  1705. if ((HWND)SendMessage( hwndScroll,
  1706. UDM_GETBUDDY,
  1707. 0,
  1708. 0 ) == hwndThisCtl)
  1709. {
  1710. SendMessage(hwndScroll, UDM_SETBUDDY, 0, 0);
  1711. }
  1712. break;
  1713. }
  1714. case ( LBN_SELCHANGE ) :
  1715. {
  1716. if ((g_Modified == FALSE) &&
  1717. (g_bPM == (BOOL)ListBox_GetTopIndex(hwndThisCtl)))
  1718. {
  1719. break;
  1720. }
  1721. //
  1722. // Find the visible entry.
  1723. //
  1724. g_Modified = TRUE;
  1725. //
  1726. // Light the apply now button.
  1727. //
  1728. PropSheet_Changed(GetParent(hDlg), hDlg);
  1729. g_bPM = (BOOL)ListBox_GetTopIndex(hwndThisCtl);
  1730. break;
  1731. }
  1732. }
  1733. break;
  1734. }
  1735. case ( DATETIME_HOUR ) :
  1736. case ( DATETIME_MINUTE ) :
  1737. case ( DATETIME_SECOND ) :
  1738. {
  1739. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  1740. {
  1741. case ( EN_CHANGE ) :
  1742. {
  1743. SYSTEMTIME SystemTime;
  1744. g_Modified = TRUE;
  1745. //
  1746. // Light the apply now button.
  1747. //
  1748. PropSheet_Changed(GetParent(hDlg), hDlg);
  1749. //
  1750. // Work out what the change was too.
  1751. //
  1752. g_Time[GET_WM_COMMAND_ID(wParam, lParam) -
  1753. DATETIME_HOUR] =
  1754. (int)SendDlgItemMessage( hDlg,
  1755. DATETIME_TARROW,
  1756. UDM_GETPOS,
  1757. 0,
  1758. 0 );
  1759. //
  1760. // Force the clock to reflect this setting.
  1761. //
  1762. TimeProvider(&SystemTime, hDlg);
  1763. SendDlgItemMessage( hDlg,
  1764. DATETIME_CLOCK,
  1765. CLM_UPDATETIME,
  1766. 0,
  1767. (LPARAM)(LPSYSTEMTIME)&SystemTime );
  1768. break;
  1769. }
  1770. }
  1771. // fall thru...
  1772. }
  1773. case ( DATETIME_MONTH ) :
  1774. case ( DATETIME_YEAR ) :
  1775. case ( DATETIME_DAY ) :
  1776. {
  1777. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  1778. {
  1779. case ( EN_CHANGE ) :
  1780. {
  1781. CheckNum( hDlg,
  1782. GET_WM_COMMAND_ID(wParam, lParam) <= DATETIME_SECOND
  1783. ? DATETIME_TARROW
  1784. : DATETIME_YARROW,
  1785. GET_WM_COMMAND_HWND(wParam, lParam) );
  1786. // Changing the year may alter the number of days in February.
  1787. // Yes this is a hack, but this entire applet is a giant
  1788. // broken hack and I want to change it as little as possible.
  1789. if (GET_WM_COMMAND_ID(wParam, lParam) == DATETIME_YEAR && wDateTime[MONTH] == 2)
  1790. {
  1791. g_sDateInfo[DAY].nMax = MonthUpperBound( wDateTime[MONTH],
  1792. wDateTime[YEAR] );
  1793. if (wDateTime[DAY] > g_sDateInfo[DAY].nMax)
  1794. {
  1795. wDateTime[DAY] = (WORD)g_sDateInfo[DAY].nMax;
  1796. fDateDirty = TRUE;
  1797. }
  1798. InvalidateRect( GetDlgItem(hDlg, DATETIME_CALENDAR),
  1799. NULL,
  1800. TRUE );
  1801. }
  1802. break;
  1803. }
  1804. case ( EN_SETFOCUS ) :
  1805. {
  1806. UINT id = GET_WM_COMMAND_ID(wParam, lParam) - DATETIME_HOUR;
  1807. if (id <= SECOND)
  1808. {
  1809. UDACCEL udAccel[2];
  1810. static int nInc[] = { 1, 5, 5, 1, 1, 5 };
  1811. HWND hwndScroll = GetDlgItem(hDlg, DATETIME_TARROW);
  1812. // if it has a buddy, remove it...
  1813. if ((HWND)SendMessage( hwndScroll,
  1814. UDM_GETBUDDY,
  1815. 0,
  1816. 0 ) != NULL)
  1817. {
  1818. SendMessage(hwndScroll, UDM_SETBUDDY, 0, 0);
  1819. }
  1820. //
  1821. // now set the new one
  1822. //
  1823. SendMessage( hwndScroll,
  1824. UDM_SETRANGE,
  1825. 0,
  1826. MAKELPARAM( g_sDateInfo[id].nMax,
  1827. g_sDateInfo[id].nMin) );
  1828. udAccel[0].nSec = 0;
  1829. udAccel[0].nInc = 1;
  1830. udAccel[1].nSec = 2;
  1831. udAccel[1].nInc = nInc[id];
  1832. SendMessage( hwndScroll,
  1833. UDM_SETACCEL,
  1834. 2,
  1835. (LPARAM)(LPUDACCEL)udAccel );
  1836. //
  1837. // Set the UD to update this control.
  1838. //
  1839. SendMessage( hwndScroll,
  1840. UDM_SETBUDDY,
  1841. (WPARAM)GET_WM_COMMAND_HWND(wParam,
  1842. lParam),
  1843. 0 );
  1844. }
  1845. break;
  1846. }
  1847. case ( EN_KILLFOCUS ) :
  1848. {
  1849. //
  1850. // Gets in range HMS MDY.
  1851. //
  1852. UINT id = GET_WM_COMMAND_ID(wParam, lParam) - DATETIME_HOUR;
  1853. AdjustDelta(hDlg, id);
  1854. UpdateItem(hDlg, id);
  1855. //
  1856. // If control is YEAR.
  1857. //
  1858. if (id == (DATETIME_YEAR - DATETIME_HOUR))
  1859. {
  1860. InvalidateRect( GetDlgItem(hDlg, DATETIME_CALENDAR),
  1861. NULL,
  1862. TRUE );
  1863. }
  1864. break;
  1865. }
  1866. default :
  1867. {
  1868. break;
  1869. }
  1870. }
  1871. break;
  1872. }
  1873. case ( DATETIME_MONTHNAME ) :
  1874. {
  1875. if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
  1876. {
  1877. int nIndex = 1 + (int)ComboBox_GetCurSel(
  1878. GetDlgItem( hDlg,
  1879. DATETIME_MONTHNAME ));
  1880. if (wDateTime[MONTH] != nIndex)
  1881. {
  1882. AdjustDeltaMonth(nIndex);
  1883. InvalidateRect( GetDlgItem(hDlg, DATETIME_CALENDAR),
  1884. NULL,
  1885. TRUE );
  1886. PropSheet_Changed(GetParent(hDlg), hDlg);
  1887. }
  1888. }
  1889. break;
  1890. }
  1891. case ( DATETIME_CALENDAR ) :
  1892. {
  1893. //
  1894. // If the calendar sent us a change, we will assume
  1895. // that it is to allow the apply now to work.
  1896. //
  1897. PropSheet_Changed(GetParent(hDlg), hDlg);
  1898. break;
  1899. }
  1900. }
  1901. break;
  1902. }
  1903. case ( WM_WININICHANGE ) :
  1904. {
  1905. //
  1906. // Reinitialize if there is a time format change.
  1907. //
  1908. InitDateTimeDlg(hDlg, uMsg, wParam, lParam);
  1909. InvalidateRect(GetDlgItem(hDlg, DATETIME_CALENDAR), NULL, TRUE);
  1910. break;
  1911. }
  1912. case ( WM_TIMECHANGE ) :
  1913. {
  1914. //
  1915. // Forward time change messages to the clock control.
  1916. //
  1917. SendDlgItemMessage( hDlg,
  1918. DATETIME_CLOCK,
  1919. WM_TIMECHANGE,
  1920. wParam,
  1921. lParam );
  1922. break;
  1923. }
  1924. case ( WMUSER_ADDINTERNETTAB ) :
  1925. {
  1926. AddInternetTab(hDlg);
  1927. break;
  1928. }
  1929. case ( WM_HELP ) : // F1
  1930. {
  1931. WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
  1932. NULL,
  1933. HELP_WM_HELP,
  1934. (DWORD_PTR)(LPTSTR)aDateTimeHelpIds );
  1935. break;
  1936. }
  1937. case ( WM_CONTEXTMENU ) : // right mouse click
  1938. {
  1939. WinHelp( (HWND)wParam,
  1940. NULL,
  1941. HELP_CONTEXTMENU,
  1942. (DWORD_PTR)(LPTSTR)aDateTimeHelpIds );
  1943. break;
  1944. }
  1945. default :
  1946. {
  1947. return (FALSE);
  1948. }
  1949. }
  1950. return (TRUE);
  1951. }
  1952. ////////////////////////////////////////////////////////////////////////////
  1953. //
  1954. // SetZoneState
  1955. //
  1956. // Sets the display state of a time zone in the map control.
  1957. //
  1958. ////////////////////////////////////////////////////////////////////////////
  1959. void SetZoneState(
  1960. HWND map,
  1961. PTZINFO zone,
  1962. BOOL highlight)
  1963. {
  1964. if (zone)
  1965. {
  1966. if (zone->SeaIndex >= 0)
  1967. {
  1968. MapControlSetSeaRegionHighlight( map,
  1969. zone->SeaIndex,
  1970. highlight,
  1971. zone->MapLeft,
  1972. zone->MapWidth );
  1973. }
  1974. if (zone->LandIndex >= 0)
  1975. {
  1976. MapControlSetLandRegionHighlight( map,
  1977. zone->LandIndex,
  1978. highlight,
  1979. zone->MapLeft,
  1980. zone->MapWidth );
  1981. }
  1982. }
  1983. }
  1984. ////////////////////////////////////////////////////////////////////////////
  1985. //
  1986. // SetZoneFamilyState
  1987. //
  1988. // Sets the display state of a time zone family in the map control.
  1989. //
  1990. ////////////////////////////////////////////////////////////////////////////
  1991. void SetZoneFamilyState(
  1992. HWND map,
  1993. PTZINFO family,
  1994. BOOL highlight)
  1995. {
  1996. if (family)
  1997. {
  1998. PTZINFO zone = family;
  1999. do
  2000. {
  2001. SetZoneState(map, zone, highlight);
  2002. zone = zone->next;
  2003. }
  2004. while(zone && (zone != family));
  2005. }
  2006. }
  2007. ////////////////////////////////////////////////////////////////////////////
  2008. //
  2009. // ParseMapInfo
  2010. //
  2011. // Parses the color table information about the world bitmap we display.
  2012. //
  2013. // Expected format: "sea,land"
  2014. // where sea and land are color table indices or -1.
  2015. //
  2016. ////////////////////////////////////////////////////////////////////////////
  2017. void ParseMapInfo(
  2018. PTZINFO zone,
  2019. const TCHAR *text)
  2020. {
  2021. const TCHAR *p = text;
  2022. zone->SeaIndex = zone->LandIndex = -1;
  2023. if (*p)
  2024. {
  2025. if (*p != TEXT('-'))
  2026. {
  2027. zone->SeaIndex = 0;
  2028. while (*p && (*p != TEXT(',')))
  2029. {
  2030. zone->SeaIndex = (10 * zone->SeaIndex) + (*p - TEXT('0'));
  2031. p++;
  2032. }
  2033. }
  2034. else
  2035. {
  2036. do
  2037. {
  2038. p++;
  2039. } while (*p && (*p != TEXT(',')));
  2040. }
  2041. if (*p == TEXT(','))
  2042. {
  2043. p++;
  2044. }
  2045. if (*p)
  2046. {
  2047. if (*p != TEXT('-'))
  2048. {
  2049. zone->LandIndex = 0;
  2050. while (*p)
  2051. {
  2052. zone->LandIndex = (10 * zone->LandIndex) + (*p - TEXT('0'));
  2053. p++;
  2054. }
  2055. }
  2056. }
  2057. }
  2058. }
  2059. ////////////////////////////////////////////////////////////////////////////
  2060. //
  2061. // ReadZoneData
  2062. //
  2063. // Reads the data for a time zone from the registry.
  2064. //
  2065. ////////////////////////////////////////////////////////////////////////////
  2066. BOOL ReadZoneData(
  2067. PTZINFO zone,
  2068. HKEY key,
  2069. LPCTSTR keyname)
  2070. {
  2071. TCHAR mapinfo[16];
  2072. DWORD len;
  2073. len = sizeof(zone->szDisplayName);
  2074. if (RegQueryValueEx( key,
  2075. c_szTZDisplayName,
  2076. 0,
  2077. NULL,
  2078. (LPBYTE)zone->szDisplayName,
  2079. &len ) != ERROR_SUCCESS)
  2080. {
  2081. return (FALSE);
  2082. }
  2083. //
  2084. // Under NT, the keyname is the "Standard" name. Values stored
  2085. // under the keyname contain the other strings and binary info
  2086. // related to the time zone. Every time zone must have a standard
  2087. // name, therefore, we save registry space by using the Standard
  2088. // name as the subkey name under the "Time Zones" key.
  2089. //
  2090. len = sizeof(zone->szStandardName);
  2091. if (RegQueryValueEx( key,
  2092. c_szTZStandardName,
  2093. 0,
  2094. NULL,
  2095. (LPBYTE)zone->szStandardName,
  2096. &len ) != ERROR_SUCCESS)
  2097. {
  2098. //
  2099. // Use keyname if can't get StandardName value.
  2100. //
  2101. lstrcpyn( zone->szStandardName,
  2102. keyname,
  2103. sizeof(zone->szStandardName) );
  2104. }
  2105. len = sizeof(zone->szDaylightName);
  2106. if (RegQueryValueEx( key,
  2107. c_szTZDaylightName,
  2108. 0,
  2109. NULL,
  2110. (LPBYTE)zone->szDaylightName,
  2111. &len ) != ERROR_SUCCESS)
  2112. {
  2113. return (FALSE);
  2114. }
  2115. len = sizeof(zone->Bias) +
  2116. sizeof(zone->StandardBias) +
  2117. sizeof(zone->DaylightBias) +
  2118. sizeof(zone->StandardDate) +
  2119. sizeof(zone->DaylightDate);
  2120. if (RegQueryValueEx( key,
  2121. c_szTZI,
  2122. 0,
  2123. NULL,
  2124. (LPBYTE)&zone->Bias,
  2125. &len ) != ERROR_SUCCESS)
  2126. {
  2127. return (FALSE);
  2128. }
  2129. len = sizeof(mapinfo);
  2130. if (RegQueryValueEx( key,
  2131. c_szTZMapInfo,
  2132. 0,
  2133. NULL,
  2134. (LPBYTE)mapinfo,
  2135. &len ) != ERROR_SUCCESS)
  2136. {
  2137. *mapinfo = TEXT('\0');
  2138. }
  2139. ParseMapInfo(zone, mapinfo);
  2140. //
  2141. // Generate phony MapLeft and MapRight until they show up in the
  2142. // registry.
  2143. //
  2144. zone->MapLeft = ((zone->Bias * ZONE_IMAGE_SCALE) / ZONE_BIAS_SCALE) +
  2145. ZONE_IMAGE_LEFT;
  2146. zone->MapWidth = ZONE_IMAGE_WIDTH;
  2147. return (TRUE);
  2148. }
  2149. ////////////////////////////////////////////////////////////////////////////
  2150. //
  2151. // AddZoneToList
  2152. //
  2153. // Inserts a new time zone into a list, sorted by bias and then name.
  2154. //
  2155. ////////////////////////////////////////////////////////////////////////////
  2156. void AddZoneToList(
  2157. PTZINFO *list,
  2158. PTZINFO zone)
  2159. {
  2160. if (*list)
  2161. {
  2162. PTZINFO curr = NULL;
  2163. PTZINFO next = *list;
  2164. while (next && zone->Bias <= next->Bias)
  2165. {
  2166. if (zone->Bias == next->Bias)
  2167. {
  2168. if (CompareString( GetUserDefaultLCID(),
  2169. 0,
  2170. zone->szDisplayName,
  2171. -1,
  2172. next->szDisplayName,
  2173. -1 ) == CSTR_LESS_THAN)
  2174. {
  2175. break;
  2176. }
  2177. }
  2178. curr = next;
  2179. next = curr->next;
  2180. }
  2181. zone->next = next;
  2182. if (curr)
  2183. {
  2184. curr->next = zone;
  2185. }
  2186. else
  2187. {
  2188. *list = zone;
  2189. }
  2190. }
  2191. else
  2192. {
  2193. *list = zone;
  2194. }
  2195. }
  2196. ////////////////////////////////////////////////////////////////////////////
  2197. //
  2198. // FreeTimezoneList
  2199. //
  2200. // Frees all time zones in the passed list, setting the head to NULL.
  2201. //
  2202. ////////////////////////////////////////////////////////////////////////////
  2203. void FreeTimezoneList(
  2204. PTZINFO *list)
  2205. {
  2206. while (*list)
  2207. {
  2208. PTZINFO next = (*list)->next;
  2209. LocalFree((HANDLE)*list);
  2210. *list = next;
  2211. }
  2212. }
  2213. ////////////////////////////////////////////////////////////////////////////
  2214. //
  2215. // ReadTimezones
  2216. //
  2217. // Reads the time zone information from the registry.
  2218. // Returns num read, -1 on failure.
  2219. //
  2220. ////////////////////////////////////////////////////////////////////////////
  2221. int ReadTimezones(
  2222. PTZINFO *list)
  2223. {
  2224. HKEY key = NULL;
  2225. int count = -1;
  2226. *list = NULL;
  2227. if (RegOpenKey( HKEY_LOCAL_MACHINE,
  2228. c_szTimeZones,
  2229. &key ) == ERROR_SUCCESS)
  2230. {
  2231. TCHAR name[TZNAME_SIZE];
  2232. PTZINFO zone = NULL;
  2233. int i;
  2234. count = 0;
  2235. for (i = 0;
  2236. RegEnumKey(key, i, name, TZNAME_SIZE) == ERROR_SUCCESS;
  2237. i++)
  2238. {
  2239. HKEY subkey = NULL;
  2240. if (!zone &&
  2241. ((zone = (PTZINFO)LocalAlloc(LPTR, sizeof(TZINFO))) == NULL))
  2242. {
  2243. zone = *list;
  2244. *list = NULL;
  2245. count = -1;
  2246. break;
  2247. }
  2248. zone->next = NULL;
  2249. if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS)
  2250. {
  2251. //
  2252. // Each sub key name under the Time Zones key is the
  2253. // "Standard" name for the Time Zone.
  2254. //
  2255. lstrcpyn(zone->szStandardName, name, TZNAME_SIZE);
  2256. if (ReadZoneData(zone, subkey, name))
  2257. {
  2258. AddZoneToList(list, zone);
  2259. zone = NULL;
  2260. count++;
  2261. }
  2262. RegCloseKey(subkey);
  2263. }
  2264. }
  2265. FreeTimezoneList(&zone);
  2266. RegCloseKey(key);
  2267. }
  2268. return (count);
  2269. }
  2270. ////////////////////////////////////////////////////////////////////////////
  2271. //
  2272. // InitZoneMapping
  2273. //
  2274. // Initializes map and map lookup for a specific time zone.
  2275. //
  2276. ////////////////////////////////////////////////////////////////////////////
  2277. void InitZoneMapping(
  2278. PTZINFO *lookup,
  2279. PTZINFO list,
  2280. HWND map)
  2281. {
  2282. PTZINFO zone = list; // not needed but more readable
  2283. while (zone)
  2284. {
  2285. if (zone->SeaIndex >= 0)
  2286. {
  2287. lookup[zone->SeaIndex] = zone;
  2288. }
  2289. if (zone->LandIndex >= 0)
  2290. {
  2291. lookup[zone->LandIndex] = zone;
  2292. }
  2293. SetZoneState(map, zone, FALSE);
  2294. zone = zone->next;
  2295. }
  2296. }
  2297. ////////////////////////////////////////////////////////////////////////////
  2298. //
  2299. // BreakZonesIntoFamilies
  2300. //
  2301. // Breaks the passed list into many circular lists.
  2302. // Each list consists of all time zones with a particular bias.
  2303. // Assumes the passed list is sorted by bias.
  2304. //
  2305. ////////////////////////////////////////////////////////////////////////////
  2306. void BreakZonesIntoFamilies(
  2307. PTZINFO head)
  2308. {
  2309. PTZINFO subhead = NULL;
  2310. PTZINFO last = NULL;
  2311. PTZINFO zone = head;
  2312. while (zone)
  2313. {
  2314. subhead = zone;
  2315. do
  2316. {
  2317. last = zone;
  2318. zone = zone->next;
  2319. }
  2320. while (zone && (zone->Bias == subhead->Bias));
  2321. last->next = subhead;
  2322. }
  2323. //
  2324. // Merge -12 and +12 zones into a single group.
  2325. // Assumes populated registry and depends on sort order.
  2326. //
  2327. if ((subhead) &&
  2328. (subhead->Bias == BIAS_PLUS_12) &&
  2329. (head->Bias == BIAS_MINUS_12))
  2330. {
  2331. PTZINFO next = head;
  2332. do
  2333. {
  2334. zone = next;
  2335. next = zone->next;
  2336. }
  2337. while (next != head);
  2338. zone->next = subhead;
  2339. last->next = head;
  2340. }
  2341. }
  2342. ////////////////////////////////////////////////////////////////////////////
  2343. //
  2344. // InitTimezones
  2345. //
  2346. // Initializes time zone stuff, UI and otherwise.
  2347. //
  2348. ////////////////////////////////////////////////////////////////////////////
  2349. BOOL InitTimezones(
  2350. HWND page,
  2351. PTZINFO *lookup)
  2352. {
  2353. PTZINFO list = NULL;
  2354. if ((g_nTimeZones = ReadTimezones(&list)) >= 0)
  2355. {
  2356. HWND combo = GetDlgItem(page, IDD_TIMEZONES);
  2357. PTZINFO zone = list;
  2358. SetWindowRedraw(combo, FALSE);
  2359. while (zone)
  2360. {
  2361. int index = ComboBox_AddString(combo, zone->szDisplayName);
  2362. if (index < 0)
  2363. {
  2364. break;
  2365. }
  2366. zone->ComboIndex = index;
  2367. ComboBox_SetItemData(combo, index, (LPARAM)zone);
  2368. zone = zone->next;
  2369. }
  2370. SetWindowRedraw(combo, TRUE);
  2371. if (!zone)
  2372. {
  2373. InitZoneMapping(lookup, list, GetDlgItem(page, IDD_TIMEMAP));
  2374. BreakZonesIntoFamilies(list);
  2375. return (TRUE);
  2376. }
  2377. FreeTimezoneList(&list);
  2378. ComboBox_ResetContent(combo);
  2379. }
  2380. return (FALSE);
  2381. }
  2382. ////////////////////////////////////////////////////////////////////////////
  2383. //
  2384. // ChangeZone
  2385. //
  2386. // Updates the current zone, making sure new zone's family is highlighted.
  2387. //
  2388. ////////////////////////////////////////////////////////////////////////////
  2389. void ChangeZone(
  2390. HWND page,
  2391. TZPAGE_STATE *state,
  2392. PTZINFO zone)
  2393. {
  2394. if (zone || state->zone)
  2395. {
  2396. BOOL newfamily = (!zone || !state->zone ||
  2397. (zone->Bias != state->zone->Bias));
  2398. HWND map = GetDlgItem(page, IDD_TIMEMAP);
  2399. BOOL dayval = (zone && (zone->StandardDate.wMonth != 0));
  2400. if (newfamily && state->zone)
  2401. {
  2402. SetZoneFamilyState(map, state->zone, FALSE);
  2403. }
  2404. state->zone = zone;
  2405. if (newfamily && state->zone)
  2406. {
  2407. SetZoneFamilyState(map, state->zone, TRUE);
  2408. }
  2409. if (newfamily)
  2410. {
  2411. MapControlInvalidateDirtyRegions(map);
  2412. }
  2413. ShowWindow(GetDlgItem(page, IDD_AUTOMAGIC), (dayval != 0 ? SW_SHOW : SW_HIDE));
  2414. if (!state->initializing)
  2415. {
  2416. PropSheet_Changed(GetParent(page), page);
  2417. }
  2418. }
  2419. }
  2420. ////////////////////////////////////////////////////////////////////////////
  2421. //
  2422. // HotTrackZone
  2423. //
  2424. // Updates the map highlighting and combo selection for a given map index.
  2425. // Expects to be called with dups.
  2426. //
  2427. ////////////////////////////////////////////////////////////////////////////
  2428. void HotTrackZone(
  2429. HWND page,
  2430. TZPAGE_STATE *state,
  2431. int index)
  2432. {
  2433. PTZINFO zone = state->lookup[index];
  2434. if (zone && (zone != state->zone))
  2435. {
  2436. ComboBox_SetCurSel( GetDlgItem(page, IDD_TIMEZONES),
  2437. (zone ? zone->ComboIndex : -1) );
  2438. ChangeZone(page, state, zone);
  2439. }
  2440. }
  2441. ////////////////////////////////////////////////////////////////////////////
  2442. //
  2443. // CenterZone
  2444. //
  2445. // Updates the map highlighting and combo selection for a given map index.
  2446. // Expects to be called with dups.
  2447. //
  2448. ////////////////////////////////////////////////////////////////////////////
  2449. void CenterZone(
  2450. HWND page,
  2451. TZPAGE_STATE *state,
  2452. BOOL animate)
  2453. {
  2454. PTZINFO zone = state->zone;
  2455. if (zone)
  2456. {
  2457. HWND map = GetDlgItem(page, IDD_TIMEMAP);
  2458. MapControlRotateTo(map, zone->MapLeft + zone->MapWidth / 2, animate);
  2459. }
  2460. }
  2461. ////////////////////////////////////////////////////////////////////////////
  2462. //
  2463. // GetPTZ
  2464. //
  2465. // Returns the pointer for the iItem time zone.
  2466. // If iItem is -1 on entry, use the currently selected time zone.
  2467. //
  2468. ////////////////////////////////////////////////////////////////////////////
  2469. PTZINFO GetPTZ(
  2470. HWND hDlg,
  2471. int iItem)
  2472. {
  2473. HWND hCtl = GetDlgItem(hDlg, IDD_TIMEZONES);
  2474. if (iItem == -1)
  2475. {
  2476. iItem = (int)ComboBox_GetCurSel(hCtl);
  2477. }
  2478. if (iItem < 0)
  2479. {
  2480. return (NULL);
  2481. }
  2482. return ((PTZINFO)ComboBox_GetItemData(hCtl, iItem));
  2483. }
  2484. ////////////////////////////////////////////////////////////////////////////
  2485. //
  2486. // GetAllowLocalTimeChange
  2487. //
  2488. ////////////////////////////////////////////////////////////////////////////
  2489. TCHAR c_szRegPathTZControl[] = REGSTR_PATH_TIMEZONE;
  2490. TCHAR c_szRegValDisableTZUpdate[] = REGSTR_VAL_TZNOAUTOTIME;
  2491. BOOL GetAllowLocalTimeChange()
  2492. {
  2493. //
  2494. // Assume allowed until we see a disallow flag.
  2495. //
  2496. BOOL result = TRUE;
  2497. HKEY key;
  2498. if (RegOpenKey( HKEY_LOCAL_MACHINE,
  2499. c_szRegPathTZControl,
  2500. &key ) == ERROR_SUCCESS)
  2501. {
  2502. //
  2503. // Assume no disallow flag until we see one.
  2504. //
  2505. DWORD value = 0;
  2506. long len = sizeof(value);
  2507. DWORD type;
  2508. if ((RegQueryValueEx( key,
  2509. c_szRegValDisableTZUpdate,
  2510. NULL,
  2511. &type,
  2512. (LPBYTE)&value,
  2513. &len ) == ERROR_SUCCESS) &&
  2514. ((type == REG_DWORD) || (type == REG_BINARY)) &&
  2515. (len == sizeof(value)) && value)
  2516. {
  2517. //
  2518. // Okay, we have a nonzero value, it is either:
  2519. //
  2520. // 1) 0xFFFFFFFF
  2521. // this is set in an inf file for first boot to prevent
  2522. // the base from performing any cutovers during setup.
  2523. //
  2524. // 2) some other value
  2525. // this signifies that the user actualy disabled cutovers
  2526. // *return that local time changes are disabled
  2527. //
  2528. if (value != 0xFFFFFFFF)
  2529. {
  2530. result = FALSE;
  2531. }
  2532. }
  2533. RegCloseKey(key);
  2534. }
  2535. return (result);
  2536. }
  2537. ////////////////////////////////////////////////////////////////////////////
  2538. //
  2539. // SetAllowLocalTimeChange
  2540. //
  2541. ////////////////////////////////////////////////////////////////////////////
  2542. void SetAllowLocalTimeChange(
  2543. BOOL fAllow)
  2544. {
  2545. HKEY key = NULL;
  2546. if (fAllow)
  2547. {
  2548. //
  2549. // Remove the disallow flag from the registry if it exists.
  2550. //
  2551. if (RegOpenKey( HKEY_LOCAL_MACHINE,
  2552. c_szRegPathTZControl,
  2553. &key ) == ERROR_SUCCESS)
  2554. {
  2555. RegDeleteValue(key, c_szRegValDisableTZUpdate);
  2556. }
  2557. }
  2558. else
  2559. {
  2560. //
  2561. // Add/set the nonzero disallow flag.
  2562. //
  2563. if (RegCreateKey( HKEY_LOCAL_MACHINE,
  2564. c_szRegPathTZControl,
  2565. &key ) == ERROR_SUCCESS)
  2566. {
  2567. DWORD value = 1;
  2568. RegSetValueEx( key,
  2569. (LPCTSTR)c_szRegValDisableTZUpdate,
  2570. 0UL,
  2571. REG_DWORD,
  2572. (LPBYTE)&value,
  2573. sizeof(value) );
  2574. }
  2575. }
  2576. if (key)
  2577. {
  2578. RegCloseKey(key);
  2579. }
  2580. }
  2581. ////////////////////////////////////////////////////////////////////////////
  2582. //
  2583. // InitTimeZonePage
  2584. //
  2585. // This function initializes everything to do with the Time Zones.
  2586. //
  2587. ////////////////////////////////////////////////////////////////////////////
  2588. BOOL InitTimeZonePage(
  2589. HWND hDlg,
  2590. TZPAGE_STATE *state)
  2591. {
  2592. TIME_ZONE_INFORMATION tziCurrent;
  2593. DWORD dwTZID;
  2594. PTZINFO ptzi;
  2595. int j ,iCurrentTZ;
  2596. BOOL fForceSelection = FALSE;
  2597. TCHAR temp[TZNAME_SIZE];
  2598. TCHAR oldTzMapName[TZNAME_SIZE], newTzMapName[TZNAME_SIZE];
  2599. //
  2600. // Get the current time zone information.
  2601. //
  2602. dwTZID = GetTimeZoneInformation(&tziCurrent);
  2603. LoadString(g_hInst, IDS_ISRAELTIMEZONE, oldTzMapName, TZNAME_SIZE);
  2604. LoadString(g_hInst, IDS_JERUSALEMTIMEZONE, newTzMapName, TZNAME_SIZE);
  2605. // this is a hack for Win95 or WinNT 4 to Win98/Win2k migration. "Israel" became "Jerusalem"
  2606. #ifdef UNICODE
  2607. if (!lstrcmpi(oldTzMapName, tziCurrent.StandardName))
  2608. {
  2609. lstrcpy(tziCurrent.StandardName, newTzMapName);
  2610. fForceSelection = TRUE;
  2611. }
  2612. #else
  2613. if (!AnsiWideStrCmpI(oldTzMapName, tziCurrent.StandardName))
  2614. {
  2615. StrToWideStr(tziCurrent.StandardName, newTzMapName);
  2616. fForceSelection = TRUE;
  2617. }
  2618. #endif
  2619. //
  2620. // Check for bogus time zone info.
  2621. //
  2622. if (dwTZID != TIME_ZONE_ID_INVALID)
  2623. {
  2624. //
  2625. // Copy the name out so we can check for first boot.
  2626. //
  2627. #ifdef UNICODE
  2628. lstrcpy(temp, tziCurrent.StandardName);
  2629. #else
  2630. WideStrToStr(temp, tziCurrent.StandardName);
  2631. #endif
  2632. }
  2633. else
  2634. {
  2635. //
  2636. // Treat bogus time zones like first boot.
  2637. //
  2638. lstrcpy(temp, c_szFirstBootTZ);
  2639. }
  2640. if (lstrcmpi(temp, c_szFirstBootTZ) == 0)
  2641. {
  2642. //
  2643. // The 'default' value of the time zone key specifies the
  2644. // default zone.
  2645. //
  2646. TCHAR szDefaultName[TZNAME_SIZE];
  2647. LONG len = sizeof(szDefaultName);
  2648. if (RegQueryValue( HKEY_LOCAL_MACHINE,
  2649. c_szTimeZones,
  2650. szDefaultName,
  2651. &len ) == ERROR_SUCCESS)
  2652. {
  2653. #ifdef UNICODE
  2654. lstrcpy(tziCurrent.StandardName, szDefaultName);
  2655. #else
  2656. StrToWideStr(tziCurrent.StandardName, szDefaultName);
  2657. #endif
  2658. }
  2659. else
  2660. {
  2661. tziCurrent.StandardName[0] = 0;
  2662. }
  2663. //
  2664. // If we can't find it by name, use GMT.
  2665. //
  2666. tziCurrent.StandardBias = tziCurrent.DaylightBias = tziCurrent.Bias = 0;
  2667. //
  2668. // Force the user to make a valid choice before quitting.
  2669. //
  2670. fForceSelection = TRUE;
  2671. }
  2672. //
  2673. // Get the Time Zones from the registry.
  2674. //
  2675. InitTimezones(hDlg, state->lookup);
  2676. //
  2677. // Try to select the 'current' one or some equivalent.
  2678. //
  2679. //
  2680. // Start with an invalid index.
  2681. //
  2682. iCurrentTZ = g_nTimeZones;
  2683. //
  2684. // Try to find by name.
  2685. //
  2686. for (j = 0; j < g_nTimeZones; j++)
  2687. {
  2688. ptzi = GetPTZ(hDlg, j);
  2689. #ifdef UNICODE
  2690. if (!lstrcmpi(ptzi->szStandardName, tziCurrent.StandardName))
  2691. #else
  2692. if (!AnsiWideStrCmpI(ptzi->szStandardName, tziCurrent.StandardName))
  2693. #endif
  2694. {
  2695. iCurrentTZ = j;
  2696. break;
  2697. }
  2698. }
  2699. //
  2700. // If it hasn't been found yet, try to find a nearby zone using biases.
  2701. //
  2702. if (iCurrentTZ == g_nTimeZones)
  2703. {
  2704. int nBestHitCount = TZ_HIT_NONE;
  2705. for (j = 0; j < g_nTimeZones; j++)
  2706. {
  2707. ptzi = GetPTZ(hDlg, j);
  2708. if (ptzi->Bias == tziCurrent.Bias)
  2709. {
  2710. int nHitCount = TZ_HIT_BASE +
  2711. ((ptzi->StandardBias == tziCurrent.StandardBias) +
  2712. (ptzi->DaylightBias == tziCurrent.DaylightBias));
  2713. if (nHitCount > nBestHitCount)
  2714. {
  2715. nBestHitCount = nHitCount;
  2716. iCurrentTZ = j;
  2717. if (nHitCount >= TZ_HIT_EXACT)
  2718. {
  2719. break;
  2720. }
  2721. }
  2722. }
  2723. }
  2724. }
  2725. //
  2726. // Still didn't find it?
  2727. //
  2728. if (iCurrentTZ == g_nTimeZones)
  2729. {
  2730. //
  2731. // Punt.
  2732. //
  2733. iCurrentTZ = 0;
  2734. fForceSelection = TRUE;
  2735. }
  2736. //
  2737. // Set up the dialog using this time zone's info.
  2738. //
  2739. //
  2740. // Always use our rules for the allow-daylight muck.
  2741. //
  2742. #ifndef WINNT
  2743. if ((ptzi = GetPTZ(hDlg, iCurrentTZ)) != NULL)
  2744. {
  2745. tziCurrent.StandardDate = ptzi->StandardDate;
  2746. tziCurrent.DaylightDate = ptzi->DaylightDate;
  2747. }
  2748. #endif
  2749. //
  2750. // If wMonth is 0, then this Time Zone does not support DST.
  2751. //
  2752. if ((tziCurrent.StandardDate.wMonth == 0) ||
  2753. (tziCurrent.DaylightDate.wMonth == 0))
  2754. {
  2755. ShowWindow(GetDlgItem(hDlg, IDD_AUTOMAGIC), SW_HIDE);
  2756. }
  2757. //
  2758. // Always get "allow DLT" flag even if this zone is disabled.
  2759. //
  2760. CheckDlgButton(hDlg, IDD_AUTOMAGIC, GetAllowLocalTimeChange());
  2761. ComboBox_SetCurSel(GetDlgItem(hDlg, IDD_TIMEZONES), iCurrentTZ);
  2762. ChangeZone(hDlg, state, GetPTZ(hDlg, -1));
  2763. CenterZone(hDlg, state, FALSE);
  2764. if (fForceSelection || g_bFirstBoot)
  2765. {
  2766. PropSheet_Changed(GetParent(hDlg), hDlg);
  2767. PropSheet_CancelToClose(GetParent(hDlg));
  2768. }
  2769. return (TRUE);
  2770. }
  2771. ////////////////////////////////////////////////////////////////////////////
  2772. //
  2773. // SetTheTimezone
  2774. //
  2775. // Apply the User's time zone selection.
  2776. //
  2777. ////////////////////////////////////////////////////////////////////////////
  2778. void SetTheTimezone(
  2779. BOOL bAutoMagicTimeChange,
  2780. BOOL bAutoMagicEnabled,
  2781. PTZINFO ptzi)
  2782. {
  2783. TIME_ZONE_INFORMATION tzi;
  2784. HCURSOR hCurOld;
  2785. if (!ptzi)
  2786. {
  2787. return;
  2788. }
  2789. tzi.Bias = ptzi->Bias;
  2790. #ifdef WINNT
  2791. if ((bAutoMagicTimeChange == 0) ||
  2792. (ptzi->StandardDate.wMonth == 0))
  2793. {
  2794. //
  2795. // Standard Only.
  2796. //
  2797. tzi.StandardBias = ptzi->StandardBias;
  2798. tzi.DaylightBias = ptzi->StandardBias;
  2799. tzi.StandardDate = ptzi->StandardDate;
  2800. tzi.DaylightDate = ptzi->StandardDate;
  2801. #ifdef UNICODE
  2802. lstrcpy(tzi.StandardName, ptzi->szStandardName);
  2803. lstrcpy(tzi.DaylightName, ptzi->szStandardName);
  2804. #else
  2805. StrToWideStr(tzi.StandardName, ptzi->szStandardName);
  2806. StrToWideStr(tzi.DaylightName, ptzi->szStandardName);
  2807. #endif
  2808. }
  2809. else
  2810. #endif
  2811. {
  2812. //
  2813. // Automatically adjust for Daylight Saving Time.
  2814. //
  2815. tzi.StandardBias = ptzi->StandardBias;
  2816. tzi.DaylightBias = ptzi->DaylightBias;
  2817. tzi.StandardDate = ptzi->StandardDate;
  2818. tzi.DaylightDate = ptzi->DaylightDate;
  2819. #ifdef UNICODE
  2820. lstrcpy(tzi.StandardName, ptzi->szStandardName);
  2821. lstrcpy(tzi.DaylightName, ptzi->szDaylightName);
  2822. #else
  2823. StrToWideStr(tzi.StandardName, ptzi->szStandardName);
  2824. StrToWideStr(tzi.DaylightName, ptzi->szDaylightName);
  2825. #endif
  2826. }
  2827. SetAllowLocalTimeChange(bAutoMagicTimeChange);
  2828. SetTimeZoneInformation(&tzi);
  2829. hCurOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  2830. SetCursor(hCurOld);
  2831. }
  2832. ////////////////////////////////////////////////////////////////////////////
  2833. //
  2834. // TimeZoneDlgProc
  2835. //
  2836. ////////////////////////////////////////////////////////////////////////////
  2837. INT_PTR CALLBACK TimeZoneDlgProc(
  2838. HWND hDlg,
  2839. UINT uMsg,
  2840. WPARAM wParam,
  2841. LPARAM lParam)
  2842. {
  2843. TZPAGE_STATE *state = (TZPAGE_STATE *)GetWindowLongPtr(hDlg, DWLP_USER);
  2844. int i;
  2845. switch (uMsg)
  2846. {
  2847. case ( WM_INITDIALOG ) :
  2848. {
  2849. state = (TZPAGE_STATE *)LocalAlloc(LPTR, sizeof(TZPAGE_STATE));
  2850. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)state);
  2851. if (!state)
  2852. {
  2853. EndDialog(hDlg, -1);
  2854. break;
  2855. }
  2856. state->initializing = TRUE;
  2857. InitTimeZonePage(hDlg, state);
  2858. state->initializing = FALSE;
  2859. break;
  2860. }
  2861. case ( WM_DESTROY ) :
  2862. {
  2863. for (i = 0; i < g_nTimeZones; i++)
  2864. {
  2865. LocalFree((HLOCAL)GetPTZ(hDlg, i));
  2866. }
  2867. if (state)
  2868. {
  2869. LocalFree((HANDLE)state);
  2870. SetWindowLongPtr(hDlg, DWLP_USER, 0L);
  2871. }
  2872. break;
  2873. }
  2874. case ( WM_NOTIFY ) :
  2875. {
  2876. switch (((NMHDR *)lParam)->idFrom)
  2877. {
  2878. case ( 0 ) :
  2879. {
  2880. switch (((NMHDR *)lParam)->code)
  2881. {
  2882. case ( PSN_APPLY ) :
  2883. {
  2884. g_ptziCurrent = NULL;
  2885. //
  2886. // Find out which listbox item was selected.
  2887. //
  2888. SetTheTimezone(
  2889. IsDlgButtonChecked(hDlg, IDD_AUTOMAGIC),
  2890. IsWindowVisible(GetDlgItem(hDlg, IDD_AUTOMAGIC)),
  2891. GetPTZ(hDlg, -1) );
  2892. //
  2893. // if the user had modified the time as well as the timezone,
  2894. // then we should honor the time that they gave us since they
  2895. // explicitly said this was the time. If we don't then the
  2896. // time they entered will be offset by the timezone change
  2897. //
  2898. if (g_WasModified)
  2899. {
  2900. g_WasModified = FALSE;
  2901. SetTime();
  2902. }
  2903. break;
  2904. }
  2905. }
  2906. break;
  2907. }
  2908. case ( IDD_TIMEMAP ) :
  2909. {
  2910. NFYMAPEVENT *event = (NFYMAPEVENT *)lParam;
  2911. switch (event->hdr.code)
  2912. {
  2913. case ( MAPN_TOUCH ) :
  2914. {
  2915. HotTrackZone(hDlg, state, event->index);
  2916. break;
  2917. }
  2918. case ( MAPN_SELECT ) :
  2919. {
  2920. CenterZone(hDlg, state, TRUE);
  2921. break;
  2922. }
  2923. }
  2924. break;
  2925. }
  2926. }
  2927. break;
  2928. }
  2929. case ( WM_COMMAND ) :
  2930. {
  2931. switch (GET_WM_COMMAND_ID(wParam, lParam))
  2932. {
  2933. case ( IDD_TIMEZONES ) : // combo box
  2934. {
  2935. if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
  2936. {
  2937. ChangeZone(hDlg, state, GetPTZ(hDlg, -1));
  2938. CenterZone(hDlg, state, TRUE);
  2939. }
  2940. break;
  2941. }
  2942. case ( IDD_AUTOMAGIC ) : // check box
  2943. {
  2944. if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
  2945. {
  2946. PropSheet_Changed(GetParent(hDlg), hDlg);
  2947. }
  2948. break;
  2949. }
  2950. }
  2951. break;
  2952. }
  2953. case ( WM_HELP ) : // F1
  2954. {
  2955. WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
  2956. NULL,
  2957. HELP_WM_HELP,
  2958. (DWORD_PTR)(LPTSTR)aDateTimeHelpIds );
  2959. break;
  2960. }
  2961. case ( WM_CONTEXTMENU ) : // right mouse click
  2962. {
  2963. WinHelp( (HWND)wParam,
  2964. NULL,
  2965. HELP_CONTEXTMENU,
  2966. (DWORD_PTR)(LPTSTR)aDateTimeHelpIds );
  2967. break;
  2968. }
  2969. default :
  2970. {
  2971. return (FALSE);
  2972. }
  2973. }
  2974. return (TRUE);
  2975. }
  2976. ////////////////////////////////////////////////////////////////////////////
  2977. //
  2978. // GetClInt
  2979. //
  2980. // Steal an int from the command line.
  2981. //
  2982. ////////////////////////////////////////////////////////////////////////////
  2983. static int GetClInt(
  2984. const TCHAR *p)
  2985. {
  2986. BOOL neg = FALSE;
  2987. int v = 0;
  2988. //
  2989. // Skip spaces.
  2990. //
  2991. while (*p == TEXT(' '))
  2992. {
  2993. p++;
  2994. }
  2995. //
  2996. // See if it's negative.
  2997. //
  2998. if (*p == TEXT('-'))
  2999. {
  3000. //
  3001. // It's negative. Remember that it's negative and skip the
  3002. // '-' char.
  3003. //
  3004. neg = TRUE;
  3005. p++;
  3006. }
  3007. //
  3008. // Parse the absolute portion. Digits only.
  3009. //
  3010. while ((*p >= TEXT('0')) && (*p <= TEXT('9')))
  3011. {
  3012. //
  3013. // Accumulate the value.
  3014. //
  3015. v = v * 10 + *p++ - TEXT('0');
  3016. }
  3017. //
  3018. // Return the result.
  3019. //
  3020. return (neg ? -v : v);
  3021. }
  3022. ////////////////////////////////////////////////////////////////////////////
  3023. //
  3024. // SelectZoneByName
  3025. //
  3026. ////////////////////////////////////////////////////////////////////////////
  3027. BOOL SelectZoneByName(
  3028. LPCTSTR cmdline)
  3029. {
  3030. BOOL result = FALSE;
  3031. HKEY key = NULL;
  3032. while (*cmdline == TEXT(' '))
  3033. {
  3034. cmdline++;
  3035. }
  3036. if (!*cmdline)
  3037. {
  3038. return (FALSE);
  3039. }
  3040. if (RegOpenKey( HKEY_LOCAL_MACHINE,
  3041. c_szTimeZones,
  3042. &key ) == ERROR_SUCCESS)
  3043. {
  3044. TCHAR name[TZNAME_SIZE];
  3045. HKEY subkey = NULL;
  3046. TZINFO zone;
  3047. //
  3048. // User can pass key name.
  3049. //
  3050. if (RegOpenKey(key, cmdline, &subkey) == ERROR_SUCCESS)
  3051. {
  3052. if (ReadZoneData(&zone, subkey, cmdline))
  3053. {
  3054. result = TRUE;
  3055. }
  3056. }
  3057. else
  3058. {
  3059. //
  3060. // User can also pass display name.
  3061. //
  3062. int i;
  3063. int CmdLen = lstrlen(cmdline);
  3064. for (i = 0;
  3065. RegEnumKey(key, i, name, TZNAME_SIZE) == ERROR_SUCCESS;
  3066. i++)
  3067. {
  3068. if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS)
  3069. {
  3070. LONG len = sizeof(zone.szDisplayName);
  3071. if ((RegQueryValueEx( subkey,
  3072. c_szTZDisplayName,
  3073. 0,
  3074. NULL,
  3075. (LPBYTE)&zone.szDisplayName,
  3076. &len ) == ERROR_SUCCESS) &&
  3077. (CompareString( GetUserDefaultLCID(),
  3078. NORM_IGNORECASE | NORM_IGNOREKANATYPE |
  3079. NORM_IGNOREWIDTH | NORM_IGNORENONSPACE,
  3080. zone.szDisplayName,
  3081. (CmdLen < 15)
  3082. ? -1
  3083. : min(lstrlen(zone.szDisplayName),
  3084. CmdLen),
  3085. cmdline,
  3086. -1 ) == CSTR_EQUAL))
  3087. {
  3088. if (ReadZoneData(&zone, subkey, name))
  3089. {
  3090. result = TRUE;
  3091. }
  3092. }
  3093. RegCloseKey(subkey);
  3094. }
  3095. if (result)
  3096. {
  3097. break;
  3098. }
  3099. }
  3100. }
  3101. RegCloseKey(key);
  3102. if (result)
  3103. {
  3104. SetTheTimezone(1, 1, &zone);
  3105. }
  3106. }
  3107. return (result);
  3108. }
  3109. ////////////////////////////////////////////////////////////////////////////
  3110. //
  3111. // OpenDateTimePropertySheet
  3112. //
  3113. // Opens a DateTime property sheet.
  3114. // Set the page for the property sheet.
  3115. //
  3116. ////////////////////////////////////////////////////////////////////////////
  3117. BOOL OpenDateTimePropertySheet(
  3118. HWND hwnd,
  3119. LPCTSTR cmdline)
  3120. {
  3121. // Make this an array for multiple pages.
  3122. PROPSHEETPAGE apsp[3];
  3123. PROPSHEETHEADER psh;
  3124. HDC hDC;
  3125. HFONT hFont;
  3126. int wMaxDigitWidth;
  3127. BOOL fReturn;
  3128. HRESULT hrOle;
  3129. hDC = GetDC(hwnd);
  3130. wMaxDigitWidth = GetMaxSubstitutedCharWidth(hDC);
  3131. ReleaseDC(hwnd, hDC);
  3132. psh.nStartPage = (UINT)-1;
  3133. if (cmdline && *cmdline)
  3134. {
  3135. if (*cmdline == TEXT('/'))
  3136. {
  3137. BOOL fAutoSet = FALSE;
  3138. //
  3139. // Legend:
  3140. // zZ: first boot batch mode setup "/z pacific" etc
  3141. // fF: regular first boot
  3142. // mM: time zone change forced local time change message
  3143. //
  3144. switch (*++cmdline)
  3145. {
  3146. case ( TEXT('z') ) :
  3147. case ( TEXT('Z') ) :
  3148. {
  3149. fAutoSet = TRUE;
  3150. //
  3151. // Fall thru...
  3152. //
  3153. }
  3154. case ( TEXT('f') ) :
  3155. case ( TEXT('F') ) :
  3156. {
  3157. g_bFirstBoot = TRUE;
  3158. if (fAutoSet && SelectZoneByName(cmdline + 1))
  3159. {
  3160. return (TRUE);
  3161. }
  3162. //
  3163. // Start on time zone page.
  3164. //
  3165. psh.nStartPage = 1;
  3166. break;
  3167. }
  3168. case ( TEXT('m') ) :
  3169. case ( TEXT('M') ) :
  3170. {
  3171. MSGBOXPARAMS params =
  3172. {
  3173. sizeof(params),
  3174. hwnd,
  3175. g_hInst,
  3176. MAKEINTRESOURCE(IDS_WARNAUTOTIMECHANGE),
  3177. MAKEINTRESOURCE(IDS_WATC_CAPTION),
  3178. MB_OK | MB_USERICON,
  3179. MAKEINTRESOURCE(IDI_TIMEDATE),
  3180. 0,
  3181. NULL,
  3182. 0
  3183. };
  3184. MessageBoxIndirect(&params);
  3185. //
  3186. // Show time/date page for user to verify.
  3187. //
  3188. psh.nStartPage = 0;
  3189. break;
  3190. }
  3191. default :
  3192. {
  3193. //
  3194. // Fall out, maybe it's a number...
  3195. //
  3196. break;
  3197. }
  3198. }
  3199. }
  3200. }
  3201. if (psh.nStartPage == (UINT)-1)
  3202. {
  3203. if (cmdline && (*cmdline >= TEXT('0')) && (*cmdline <= TEXT('9')))
  3204. {
  3205. psh.nStartPage = GetClInt(cmdline);
  3206. }
  3207. else
  3208. {
  3209. psh.nStartPage = 0;
  3210. }
  3211. }
  3212. //
  3213. // Register our classes.
  3214. //
  3215. ClockInit(g_hInst);
  3216. CalendarInit(g_hInst);
  3217. RegisterMapControlStuff(g_hInst);
  3218. psh.dwSize = sizeof(psh);
  3219. if (g_bFirstBoot)
  3220. {
  3221. //
  3222. // Disable Apply button for first boot.
  3223. //
  3224. psh.dwFlags = PSH_PROPTITLE | PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW;
  3225. }
  3226. else
  3227. {
  3228. psh.dwFlags = PSH_PROPTITLE | PSH_PROPSHEETPAGE;
  3229. }
  3230. psh.hwndParent = hwnd;
  3231. psh.hInstance = g_hInst;
  3232. psh.pszIcon = NULL;
  3233. //
  3234. // psh.nStartPage is set above.
  3235. //
  3236. psh.pszCaption = MAKEINTRESOURCE(IDS_TIMEDATE);
  3237. psh.nPages = 2;
  3238. psh.ppsp = apsp;
  3239. apsp[0].dwSize = sizeof(PROPSHEETPAGE);
  3240. apsp[0].dwFlags = PSP_DEFAULT;
  3241. apsp[0].hInstance = g_hInst;
  3242. apsp[0].pszTemplate = wMaxDigitWidth > 8 ? MAKEINTRESOURCE(DLG_DATETIMEWIDE) : MAKEINTRESOURCE(DLG_DATETIME);
  3243. apsp[0].pfnDlgProc = DateTimeDlgProc;
  3244. apsp[0].lParam = 0;
  3245. apsp[1].dwSize = sizeof(PROPSHEETPAGE);
  3246. apsp[1].dwFlags = PSP_DEFAULT;
  3247. apsp[1].hInstance = g_hInst;
  3248. apsp[1].pszTemplate = MAKEINTRESOURCE(DLG_TIMEZONE);
  3249. apsp[1].pfnDlgProc = TimeZoneDlgProc;
  3250. apsp[1].lParam = 0;
  3251. if (psh.nStartPage >= psh.nPages)
  3252. {
  3253. psh.nStartPage = 0;
  3254. }
  3255. // We use the HyperLink control and that requires OLE (for IAccessible)
  3256. hrOle = CoInitialize(0);
  3257. fReturn = (BOOL)PropertySheet(&psh);
  3258. if (SUCCEEDED(hrOle))
  3259. {
  3260. CoUninitialize();
  3261. }
  3262. return fReturn;
  3263. }