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

/*++
Copyright (c) 1994-1998, Microsoft Corporation All rights reserved.
Module Name:
date.c
Abstract:
This module implements the textual representations of
Day Month Year : Hours Min Seconds for the Date/Time applet.
Revision History:
--*/
//
// Include Files.
//
#include "timedate.h"
#include <commctrl.h>
#include <prsht.h>
#include <regstr.h>
#include <imm.h>
#include "clock.h"
#include "mapctl.h"
#include "inettime.h"
#include "rc.h"
#include <help.h>
#include <shlwapi.h>
#include <windows.h>
#include <windowsx.h>
//
// Constant Declarations.
//
#define TZNAME_SIZE 128
#define TZDISPLAYZ 128
#define BIAS_ONE_HOUR (-60L)
#define ZONE_IMAGE_SCALE (356)
#define ZONE_BIAS_SCALE (-1440)
#define ZONE_IMAGE_LEFT (120)
#define ZONE_IMAGE_WIDTH (80)
#define BIAS_PLUS_12 (12L * BIAS_ONE_HOUR)
#define BIAS_MINUS_12 (- BIAS_PLUS_12)
#define TZ_HIT_NONE (0)
#define TZ_HIT_BASE (1)
#define TZ_HIT_PARTIAL (2)
#define TZ_HIT_EXACT (3)
//
// Global Variables.
//
TCHAR const szIntl[] = TEXT("intl");
//
// Default used if none could be found.
//
INTLSTRUCT IntlDef =
{
TEXT("Other Country"),
1, 0, 0, 0, 0, 2, 0, 1, 2, 1,
TEXT("AM"),
TEXT("PM"),
TEXT("$"),
TEXT(","),
TEXT("."),
TEXT("/"),
TEXT(":"),
TEXT(","),
TEXT("dddd, MMMM dd, yyyy"),
TEXT("M/d/yyyy"),
TEXT("USA"),
1, 0, 1, 0, 0x0409,
TEXT("hh:mm:ss tt"),
0, 1,
TEXT(","),
TEXT(".")
};
BOOL g_bFirstBoot = FALSE; // for first boot during setup
int g_Time[3]; // time the user currently has set
int g_LastTime[3]; // last displayed time - to stop flicker
short wDateTime[7]; // values for first 7 date/time items
short wPrevDateTime[7]; // only repaint fields if necessary
BOOL fDateDirty;
//
// Formatting strings for AM and PM
//
TCHAR sz1159[12];
TCHAR sz2359[12];
//
// Are we in 24 hour time. If not, is it AM or PM.
//
BOOL g_b24HR;
BOOL g_bPM;
//
// This flag indicates if the user has tried to change the time.
// If so, then we stop providing the system time and use the
// time that we store internally. We send the clock control our
// TimeProvider function.
//
WORD g_Modified = 0;
WORD g_WasModified = 0;
//
// Which of the HMS MDY have leading zeros.
//
BOOL g_bLZero[6] = {FALSE, TRUE, TRUE, FALSE, FALSE, FALSE};
//
// Ranges of HMS MDY
//
struct
{
int nMax;
int nMin;
} g_sDateInfo[] =
{
23, 0,
59, 0,
59, 0,
12, 1,
31, 1,
2099, 1980,
};
//
// Time Zone info globals.
//
int g_nTimeZones = 0;
TIME_ZONE_INFORMATION g_tziCurrent, *g_ptziCurrent = NULL;
//
// Registry location for Time Zone information.
//
#ifdef WINNT
TCHAR c_szTimeZones[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
#else
TCHAR c_szTimeZones[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Time Zones");
#endif
//
// Time Zone data value keys.
//
TCHAR c_szTZDisplayName[] = TEXT("Display");
TCHAR c_szTZStandardName[] = TEXT("Std");
TCHAR c_szTZDaylightName[] = TEXT("Dlt");
TCHAR c_szTZI[] = TEXT("TZI");
TCHAR c_szTZMapInfo[] = TEXT("MapID");
//
// IME globals.
//
HIMC g_PrevIMCForDateField;
//
// Context Help Ids.
//
const DWORD aDateTimeHelpIds[] =
{
IDD_GROUPBOX1, IDH_DATETIME_DATE_GROUP,
IDD_GROUPBOX2, IDH_DATETIME_TIME,
DATETIME_CURTZ, IDH_DATETIME_CURRENT_TIME_ZONE,
DATETIME_CALENDAR, IDH_DATETIME_DATE,
DATETIME_CLOCK, IDH_DATETIME_TIME,
DATETIME_TBORDER, IDH_DATETIME_TIME,
DATETIME_HOUR, IDH_DATETIME_TIME,
DATETIME_TSEP1, IDH_DATETIME_TIME,
DATETIME_MINUTE, IDH_DATETIME_TIME,
DATETIME_TSEP2, IDH_DATETIME_TIME,
DATETIME_SECOND, IDH_DATETIME_TIME,
DATETIME_AMPM, IDH_DATETIME_TIME,
DATETIME_TARROW, IDH_DATETIME_TIME,
DATETIME_MONTHNAME, IDH_DATETIME_MONTH,
DATETIME_YEAR, IDH_DATETIME_YEAR,
DATETIME_YARROW, IDH_DATETIME_YEAR,
IDD_TIMEZONES, IDH_DATETIME_TIMEZONE,
// IDD_TIMEMAP, IDH_DATETIME_BITMAP,
IDD_TIMEMAP, NO_HELP,
IDD_AUTOMAGIC, IDH_DATETIME_DAYLIGHT_SAVE,
0, 0
};
//
// Typedef Declarations.
//
//
// Registry info goes in this structure.
//
typedef struct tagTZINFO
{
struct tagTZINFO *next;
TCHAR szDisplayName[TZDISPLAYZ];
TCHAR szStandardName[TZNAME_SIZE];
TCHAR szDaylightName[TZNAME_SIZE];
int ComboIndex;
int SeaIndex;
int LandIndex;
int MapLeft;
int MapWidth;
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
} TZINFO, *PTZINFO;
//
// State info for the time zone page.
//
typedef struct
{
PTZINFO zone;
BOOL initializing;
PTZINFO lookup[MAPCTL_MAX_INDICES];
} TZPAGE_STATE;
DWORD GetTextExtent(
HDC hdc,
LPCTSTR lpsz,
int cb);
#ifndef UNICODE
////////////////////////////////////////////////////////////////////////////
//
// WideStrToStr
//
////////////////////////////////////////////////////////////////////////////
int WINAPI WideStrToStr(
LPSTR psz,
LPWSTR pwsz)
{
return WideCharToMultiByte(CP_ACP, 0, pwsz, -1, psz, MAX_PATH, NULL, NULL);
}
////////////////////////////////////////////////////////////////////////////
//
// StrToWideStr
//
////////////////////////////////////////////////////////////////////////////
int WINAPI StrToWideStr(
LPWSTR pwsz,
LPCSTR psz)
{
return MultiByteToWideChar(CP_ACP, 0, psz, -1, pwsz, MAX_PATH);
}
////////////////////////////////////////////////////////////////////////////
//
// AnsiWideStrCmpI
//
////////////////////////////////////////////////////////////////////////////
int AnsiWideStrCmpI(
LPCSTR pszAnsi,
WCHAR *pwszWide)
{
char szAnsi[64];
WideStrToStr(szAnsi, pwszWide);
return (lstrcmpi(pszAnsi, szAnsi));
}
#endif
////////////////////////////////////////////////////////////////////////////
//
// ParseDateElement
//
// Assumes that the character pointed to by pszElement is a
// 'M', 'd', or 'y', and checks if the string indicates a leading zero
// or century. The return value is a pointer to the next character,
// which should be a separator or NULL. A return value of NULL indicates
// an error.
//
////////////////////////////////////////////////////////////////////////////
LPTSTR ParseDateElement(
LPTSTR pszElement,
BOOL *pbLZero)
{
//
// Check for valid character.
//
switch (*pszElement)
{
case ( TEXT('y') ) :
case ( TEXT('M') ) :
case ( TEXT('d') ) :
{
break;
}
default:
{
return (NULL);
}
}
++pszElement;
if (*pszElement != *(pszElement - 1))
{
*pbLZero = 0;
}
else
{
*pbLZero = 1;
if (*pszElement++ == TEXT('y'))
{
if (!(*pszElement == TEXT('y')))
{
*pbLZero = 0;
}
else
{
if (!(*++pszElement == TEXT('y')))
{
//
// Found 3 y's, invalid format.
//
return (NULL);
}
else
{
++pszElement;
}
}
}
}
return (pszElement);
}
int rgMoveTimeControls [] =
{
DATETIME_HOUR,
DATETIME_MINUTE,
DATETIME_SECOND,
DATETIME_TSEP1,
DATETIME_TSEP2
};
////////////////////////////////////////////////////////////////////////////
//
// AdjustAMPMPosition
//
////////////////////////////////////////////////////////////////////////////
void AdjustAMPMPosition(HWND hwnd)
{
TCHAR szTimePrefix[5];
static BOOL fMoved = FALSE;
GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_ITIMEMARKPOSN,
szTimePrefix,
ARRAYSIZE(szTimePrefix));
if (!fMoved && szTimePrefix[0] == TEXT('1'))
{
RECT rLeftCtl, rAMPMCtl, rCurrCtl;
HWND hwndAMPM, hwndCurr;
int i, width;
POINT pt;
fMoved = TRUE;
//Get rect of left most control (hours)
GetWindowRect(GetDlgItem(hwnd, DATETIME_HOUR), &rLeftCtl);
//Get rect of AM PM control
hwndAMPM = GetDlgItem(hwnd, DATETIME_AMPM);
GetWindowRect(hwndAMPM, &rAMPMCtl);
width = rAMPMCtl.right - rAMPMCtl.left;
//Shift all controls right by the AM PM control width
for (i = 0; i < ARRAYSIZE(rgMoveTimeControls); i++)
{
hwndCurr = GetDlgItem(hwnd, rgMoveTimeControls[i]);
GetWindowRect(hwndCurr, &rCurrCtl);
pt.x = rCurrCtl.left;
pt.y = rCurrCtl.top;
ScreenToClient(hwnd, &pt);
MoveWindow(hwndCurr, pt.x + width,
pt.y,
rCurrCtl.right - rCurrCtl.left,
rCurrCtl.bottom - rCurrCtl.top,
TRUE);
}
//Move AM PM control left to where the hours were.
pt.x = rLeftCtl.left;
pt.y = rAMPMCtl.top;
ScreenToClient(hwnd, &pt);
MoveWindow(hwndAMPM, pt.x,
pt.y,
rAMPMCtl.right - rAMPMCtl.left,
rAMPMCtl.bottom - rAMPMCtl.top,
TRUE);
}
}
////////////////////////////////////////////////////////////////////////////
//
// MonthUpperBound
//
////////////////////////////////////////////////////////////////////////////
int _fastcall MonthUpperBound(
int nMonth,
int nYear)
{
switch (nMonth)
{
case ( 2 ) :
{
//
// A year is a leap year if it is divisible by 4 and is not
// a century year (multiple of 100) or if it is divisible by
// 400.
//
return ( ((nYear % 4 == 0) &&
((nYear % 100 != 0) || (nYear % 400 == 0))) ? 29 : 28 );
}
case ( 4 ) :
case ( 6 ) :
case ( 9 ) :
case ( 11 ) :
{
return (30);
}
}
return (31);
}
////////////////////////////////////////////////////////////////////////////
//
// IsAMPM
//
// True if PM.
//
////////////////////////////////////////////////////////////////////////////
BOOL IsAMPM(
int iHour)
{
return ((iHour >= 12) ? 1 : 0);
}
////////////////////////////////////////////////////////////////////////////
//
// GetDateTime
//
////////////////////////////////////////////////////////////////////////////
void GetDateTime()
{
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
wDateTime[HOUR] = SystemTime.wHour;
wDateTime[MINUTE] = SystemTime.wMinute;
wDateTime[SECOND] = SystemTime.wSecond;
wDateTime[MONTH] = SystemTime.wMonth;
wDateTime[DAY] = SystemTime.wDay;
wDateTime[YEAR] = SystemTime.wYear;
wDateTime[WEEKDAY] = SystemTime.wDayOfWeek;
}
////////////////////////////////////////////////////////////////////////////
//
// GetTime
//
////////////////////////////////////////////////////////////////////////////
void GetTime()
{
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
wDateTime[HOUR] = SystemTime.wHour;
wDateTime[MINUTE] = SystemTime.wMinute;
wDateTime[SECOND] = SystemTime.wSecond;
}
////////////////////////////////////////////////////////////////////////////
//
// GetDate
//
////////////////////////////////////////////////////////////////////////////
void GetDate()
{
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
wDateTime[MONTH] = SystemTime.wMonth;
wDateTime[DAY] = SystemTime.wDay;
wDateTime[YEAR] = SystemTime.wYear;
wDateTime[WEEKDAY] = SystemTime.wDayOfWeek;
fDateDirty = FALSE;
}
////////////////////////////////////////////////////////////////////////////
//
// SetTime
//
////////////////////////////////////////////////////////////////////////////
void SetTime()
{
SYSTEMTIME SystemTime;
SystemTime.wHour = wDateTime[HOUR];
SystemTime.wMinute = wDateTime[MINUTE];
SystemTime.wSecond = wDateTime[SECOND];
SystemTime.wMilliseconds = 0;
SystemTime.wMonth = wDateTime[MONTH];
SystemTime.wDay = wDateTime[DAY];
SystemTime.wYear = wDateTime[YEAR];
SetLocalTime(&SystemTime);
SetLocalTime(&SystemTime);
fDateDirty = FALSE;
}
////////////////////////////////////////////////////////////////////////////
//
// SetDate
//
////////////////////////////////////////////////////////////////////////////
void SetDate()
{
SYSTEMTIME SystemTime;
SystemTime.wHour = wDateTime[HOUR];
SystemTime.wMinute = wDateTime[MINUTE];
SystemTime.wSecond = wDateTime[SECOND];
SystemTime.wMilliseconds = 0;
SystemTime.wMonth = wDateTime[MONTH];
SystemTime.wDay = wDateTime[DAY];
SystemTime.wYear = wDateTime[YEAR];
SetLocalTime(&SystemTime);
SetLocalTime(&SystemTime);
fDateDirty = FALSE;
}
////////////////////////////////////////////////////////////////////////////
//
// AdjustDelta
//
// Alters the variables in wDeltaDateTime, allowing a CANCEL button
// to perform its job by resetting the time as if it had never been
// touched. GetTime() & GetDate() should already have been called.
//
////////////////////////////////////////////////////////////////////////////
void AdjustDelta(
HWND hDlg,
int nIndex)
{
int nDelta;
//
// We dont do time this way any more.
//
if (nIndex <= SECOND && nIndex >= HOUR)
{
return;
}
//
// Get position of the buddy from either the date or the time.
//
nDelta = (int)SendDlgItemMessage( hDlg,
nIndex <= SECOND
? DATETIME_TARROW
: DATETIME_YARROW,
UDM_GETPOS,
0,
0L );
if ((nIndex == YEAR) && !g_bLZero[YEAR])
{
//
// Years before 80 are 2080.
// Range is 1980...2079.
//
if (nDelta < 80)
{
nDelta += 2000;
}
else
{
nDelta += 1900;
}
}
//
// If our current recording of the time/date is not what we have
// now, do deltas.
//
if (wDateTime[nIndex] != nDelta)
{
//
// Previous value is current user's settings.
//
wPrevDateTime[nIndex] = wDateTime[nIndex] = (WORD)nDelta;
fDateDirty = TRUE;
//
// If we are changing HMS, update the time.
//
if (nIndex <= SECOND)
{
nIndex = 0;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// AdjustDeltaMonth
//
// Change the month part of wDateTime
//
////////////////////////////////////////////////////////////////////////////
extern int GetDaysOfTheMonth(int iMonth);
void AdjustDeltaMonth(
int iMonth)
{
GetTime();
if (wDateTime[MONTH] != iMonth)
{
//
// Make sure the current day is valid in the new month.
//
if (wDateTime[DAY] > (WORD)GetDaysOfTheMonth(iMonth))
{
wDateTime[DAY] = (WORD)GetDaysOfTheMonth(iMonth);
}
wPrevDateTime[MONTH] = wDateTime[MONTH] = (WORD)iMonth;
fDateDirty = TRUE;
g_sDateInfo[DAY].nMax = MonthUpperBound( wDateTime[MONTH],
wDateTime[YEAR] );
}
}
////////////////////////////////////////////////////////////////////////////
//
// ReadShortDate
//
// Verify that pszDate is one of MDY, DMY, or YMD.
//
////////////////////////////////////////////////////////////////////////////
int ReadShortDate(
LPTSTR pszDate,
BOOL *pbMonth,
BOOL *pbDay,
BOOL *pbYear)
{
int i, nOrder;
BOOL *pbOrder[3];
TCHAR cHope[3];
//
// nOrder : 0 = MDY
// 1 = DMY
// 2 = YMD
//
switch (cHope[0] = *pszDate)
{
case ( TEXT('M') ) :
{
nOrder = 0;
pbOrder[0] = pbMonth;
break;
}
case ( TEXT('d') ) :
{
nOrder = 1;
pbOrder[0] = pbDay;
break;
}
case ( TEXT('y') ) :
{
nOrder = 2;
pbOrder[0] = pbYear;
break;
}
default :
{
return (FALSE);
}
}
//
// Sets element 1.
//
if (nOrder) // 1 2
{
cHope[1] = TEXT('M');
pbOrder[1] = pbMonth;
}
else // 0
{
cHope[1] = TEXT('d');
pbOrder[1] = pbDay;
}
//
// Sets element 2.
//
if (nOrder == 2) // 2
{
cHope[2] = TEXT('d');
pbOrder[2] = pbDay;
}
else // 0 1
{
cHope[2] = TEXT('y');
pbOrder[2] = pbYear;
}
//
// Verifies that pszDate is of the form MDY DMY YMD.
//
for (i = 0; i < 3; i++, pszDate++)
{
if (*pszDate != cHope[i])
{
return (-1 - nOrder);
}
if (!(pszDate = ParseDateElement(pszDate, pbOrder[i])))
{
return (-1 - nOrder);
}
}
//
// Success. Return MDY, DMY or YMD index.
//
return (nOrder);
}
////////////////////////////////////////////////////////////////////////////
//
// GetMaxCharWidth
//
// Determine the widest digit (safety against variable pitch fonts).
//
////////////////////////////////////////////////////////////////////////////
int GetMaxCharWidth(
HDC hDC)
{
UINT nNumWidth[10];
UINT i, nMaxNumWidth;
GetCharWidth32(hDC, TEXT('0'), TEXT('9'), nNumWidth);
for (nMaxNumWidth = 0, i = 0; i < 10; i++)
{
if (nNumWidth[i] > nMaxNumWidth)
{
nMaxNumWidth = nNumWidth[i];
}
}
return (nMaxNumWidth);
}
////////////////////////////////////////////////////////////////////////////
//
// GetMaxSubstitutedCharWidth
//
// Determine the widest digit (safety against variable pitch fonts), but
// do it using strings so that if number substitution is on, we will get
// the width of the number based on what will actually be displayed
//
////////////////////////////////////////////////////////////////////////////
int GetMaxSubstitutedCharWidth(
HDC hDC)
{
char sz[2] = "0";
TCHAR szAMPM[12];
LONG i, nMaxNumWidth;
DWORD dwWidth;
SIZE size;
for (nMaxNumWidth = 0, i = 0; i < 10; (*sz)++, i++)
{
if (GetTextExtentPoint32A(hDC, sz, 1, &size) && size.cx > nMaxNumWidth)
nMaxNumWidth = size.cx;
}
if (nMaxNumWidth <= 8)
{
GetProfileString(szIntl, TEXT("s1159"), IntlDef.s1159, szAMPM, ARRAYSIZE(szAMPM));
dwWidth = LOWORD(GetTextExtent(hDC, szAMPM, lstrlen(szAMPM)));
if (dwWidth > 22)
nMaxNumWidth = 10;
GetProfileString(szIntl, TEXT("s2359"), IntlDef.s2359, szAMPM, ARRAYSIZE(szAMPM));
dwWidth = LOWORD(GetTextExtent(hDC, szAMPM, lstrlen(szAMPM)));
if (dwWidth > 22)
nMaxNumWidth = 10;
}
return (nMaxNumWidth);
}
////////////////////////////////////////////////////////////////////////////
//
// ReflectAMPM
//
// Sets the global g_bPM and updates the control to display AM or PM.
//
////////////////////////////////////////////////////////////////////////////
void ReflectAMPM(
HWND hDlg,
int nNum)
{
HWND hCtl = GetDlgItem(hDlg, DATETIME_AMPM);
ListBox_SetTopIndex(hCtl, g_bPM);
ListBox_SetCurSel(hCtl, (GetFocus() == hCtl) ? g_bPM : -1);
}
////////////////////////////////////////////////////////////////////////////
//
// GetTextExtent
//
////////////////////////////////////////////////////////////////////////////
#ifdef WIN32
DWORD GetTextExtent(
HDC hdc,
LPCTSTR lpsz,
int cb)
{
SIZE sz;
BOOL bSuccess = GetTextExtentPoint(hdc, lpsz, cb, &sz);
if ( !bSuccess )
{
ZeroMemory( &sz, sizeof(sz) );
}
return ( MAKELONG((WORD)sz.cx, (WORD)sz.cy) );
}
#endif
////////////////////////////////////////////////////////////////////////////
//
// DateTimeInit
//
// Determine the widest digit (safety against variable pitch fonts).
//
////////////////////////////////////////////////////////////////////////////
void DateTimeInit(
HWND hDlg,
WORD nBaseID,
WORD nSepID,
LPTSTR pszSep,
int nMaxDigitWidth,
BOOL bDate)
{
HWND hAMPMList;
HWND hDay, hMonth, hYear; // also used as hHour, hMinute, & hSecond
HWND hOrder[5];
HDC hDC;
int nWidth, nHeight, X;
DWORD dwSepExt;
RECT Rect;
int i;
int nAMPMlength;
hMonth = GetDlgItem(hDlg, nBaseID);
hDay = GetDlgItem(hDlg, nBaseID + 1);
hYear = GetDlgItem(hDlg, nBaseID + 2);
hOrder[1] = GetDlgItem(hDlg, nSepID);
hOrder[3] = GetDlgItem(hDlg, nSepID + 1);
if (bDate)
{
i = GetProfileInt(szIntl, TEXT("iDate"), 0);
}
else
{
if (g_b24HR = (BOOL)GetProfileInt(szIntl, TEXT("iTime"), 0))
{
g_sDateInfo[HOUR].nMin = 0;
g_sDateInfo[HOUR].nMax = 23;
}
else
{
g_sDateInfo[HOUR].nMin = 1;
g_sDateInfo[HOUR].nMax = 12;
GetProfileString(szIntl, TEXT("s1159"), IntlDef.s1159, sz1159, ARRAYSIZE(sz1159));
GetProfileString(szIntl, TEXT("s2359"), IntlDef.s2359, sz2359, ARRAYSIZE(sz2359));
}
i = 0;
}
switch (i)
{
case ( 1 ) :
{
hOrder[0] = hDay;
hOrder[2] = hMonth;
hOrder[4] = hYear;
break;
}
case ( 2 ) :
{
hOrder[0] = hYear;
hOrder[2] = hMonth;
hOrder[4] = hDay;
break;
}
case ( 0 ) :
default :
{
hOrder[0] = hMonth;
hOrder[2] = hDay;
hOrder[4] = hYear;
break;
}
}
hDC = GetDC(hDlg);
if (!bDate)
{
dwSepExt = GetTextExtent(hDC, sz1159, lstrlen(sz1159));
nAMPMlength = LOWORD(GetTextExtent(hDC, sz2359, lstrlen(sz2359)));
if (nAMPMlength < (int)LOWORD(dwSepExt))
{
nAMPMlength = (int)LOWORD(dwSepExt);
}
}
dwSepExt = GetTextExtent(hDC, pszSep, lstrlen(pszSep));
ReleaseDC(hDlg, hDC);
GetWindowRect(hYear, (LPRECT)&Rect);
ScreenToClient(hDlg, (LPPOINT)&Rect.left);
ScreenToClient(hDlg, (LPPOINT)&Rect.right);
nHeight = Rect.bottom - Rect.top;
nWidth = Rect.top;
GetWindowRect( GetDlgItem( hDlg,
bDate ? DATETIME_CALENDAR : DATETIME_CLOCK ),
(LPRECT)&Rect );
ScreenToClient(hDlg, (LPPOINT)&Rect.left);
ScreenToClient(hDlg, (LPPOINT)&Rect.right);
Rect.top = nWidth;
X = (Rect.left + Rect.right - (6 * nMaxDigitWidth) - (2 * LOWORD(dwSepExt))) / 2;
if (bDate)
{
if (g_bLZero[YEAR])
{
X -= nMaxDigitWidth;
}
}
else if (!g_b24HR)
{
X -= nAMPMlength / 2;
}
for (i = 0; i < 5; i++)
{
nWidth = (i % 2) ? LOWORD(dwSepExt) : 2 * nMaxDigitWidth;
if ((hOrder[i] == hYear) && bDate && g_bLZero[YEAR])
{
nWidth *= 2;
}
//
// Allow for centering in edit control.
//
nWidth += 2;
// MoveWindow(hOrder[i], X, Rect.top, nWidth, nHeight, FALSE);
X += nWidth;
}
hAMPMList = GetDlgItem(hDlg, DATETIME_AMPM);
ListBox_ResetContent(hAMPMList);
if (!bDate && !g_b24HR)
{
ListBox_InsertString(hAMPMList, 0, sz1159);
ListBox_InsertString(hAMPMList, 1, sz2359);
}
EnableWindow(hAMPMList, !g_b24HR);
Edit_LimitText(hYear, (bDate && g_bLZero[YEAR]) ? 4 : 2);
Edit_LimitText(hMonth, 2);
Edit_LimitText(hDay, 2);
SetDlgItemText(hDlg, nSepID, pszSep);
SetDlgItemText(hDlg, nSepID + 1, pszSep);
}
////////////////////////////////////////////////////////////////////////////
//
// myitoa
//
////////////////////////////////////////////////////////////////////////////
void myitoa(
int intValue,
LPTSTR lpStr)
{
LPTSTR lpString;
TCHAR c;
//
// lpString points to 1st char.
//
lpString = lpStr;
do
{
*lpStr++ = (TCHAR)(intValue % 10 + TEXT('0'));
} while ((intValue /= 10) > 0);
//
// lpStr points to last char.
//
*lpStr-- = TEXT('\0');
//
// Now reverse the string.
//
while (lpString < lpStr)
{
c = *lpString;
*(lpString++) = *lpStr;
*(lpStr--) = c;
}
}
////////////////////////////////////////////////////////////////////////////
//
// UpdateItem
//
// This displays the information in the control from the array
// of global values. Also selects the control. Also adds leading 0's
// as well as rounding years to 2 digits and 24 or AM/PM hours.
//
////////////////////////////////////////////////////////////////////////////
void UpdateItem(
HWND hDlg,
int i)
{
TCHAR szNum[5];
int nNum = g_Modified ? wPrevDateTime[i] : wDateTime[i];
//
// Use internal time.
//
if (i <= SECOND && i >= HOUR)
{
nNum = g_Time[i];
//
// Do not paint un-necessarily.
//
if ((nNum == g_LastTime[i]) && (nNum >= 10))
{
return;
}
g_LastTime[i] = nNum;
if (i == HOUR)
{
if (IsAMPM(nNum))
{
g_bPM = TRUE;
}
ReflectAMPM(hDlg, nNum);
}
}
if (i == YEAR)
{
//
// Round the years to last 2 digits.
//
if (!g_bLZero[i])
{
nNum %= 100;
}
}
else if ((i == HOUR) && !g_b24HR)
{
//
// nNum came from our internal date time.
// Remove 12 hours if not 24hour.
//
if (g_bPM)
{
nNum %= 12;
}
//
// 00 hours is actually 12AM.
//
if (!nNum)
{
nNum = 12;
}
}
//
// See if we need leading zeros.
// We only deal with 2 character numbers MAX.
//
if ((nNum < 10) && (g_bLZero[i] || (i == YEAR)))
{
szNum[0] = TEXT('0');
szNum[1] = (TCHAR)(TEXT('0') + nNum);
szNum[2] = TEXT('\0');
}
else
{
myitoa(nNum, szNum);
}
//
// Reflect the value in the appropriate control.
//
SetDlgItemText(hDlg, DATETIME_HOUR + i, szNum);
//
// Select the field too.
//
SendDlgItemMessage(hDlg, DATETIME_HOUR + i, EM_SETSEL, 0, MAKELONG(0, 32767));
//
// If we changed year or month, then we may have altered the leap year
// state.
//
if (i == MONTH || i == YEAR)
{
g_sDateInfo[DAY].nMax = MonthUpperBound( wDateTime[MONTH],
wDateTime[YEAR] );
}
}
////////////////////////////////////////////////////////////////////////////
//
// _ShowTZ
//
////////////////////////////////////////////////////////////////////////////
TCHAR c_szFirstBootTZ[] = TEXT("!!!First Boot!!!");
void _ShowTZ(
HWND hDlg)
{
HWND ctl = GetDlgItem(hDlg, DATETIME_CURTZ);
TIME_ZONE_INFORMATION info;
TCHAR final[64 + TZNAME_SIZE];
TCHAR name[TZNAME_SIZE];
DWORD TimeZoneId;
if (g_bFirstBoot)
{
ShowWindow(ctl, SW_HIDE);
}
else
{
TimeZoneId = GetTimeZoneInformation(&info);
#ifdef UNICODE
lstrcpy( name,
(TimeZoneId == TIME_ZONE_ID_STANDARD)
? info.StandardName
: info.DaylightName );
#else
WideStrToStr(name, (TimeZoneId == TIME_ZONE_ID_STANDARD)
? info.StandardName
: info.DaylightName );
#endif //UNICODE
//
// Display nothing if it is our special 1st boot marker.
//
if (*name && (lstrcmpi(name, c_szFirstBootTZ) != 0))
{
static TCHAR format[128] = TEXT("");
if (!*format)
{
GetWindowText( ctl,
format,
ARRAYSIZE(format) );
}
wsprintf(final, format, name);
}
else
{
*final = 0;
}
SetWindowText(ctl, final);
}
}
////////////////////////////////////////////////////////////////////////////
//
// UnhookTime
//
// To stop the clock calling us back all the time (around exit).
//
////////////////////////////////////////////////////////////////////////////
void UnhookTimer(
HWND hDlg)
{
SendDlgItemMessage(hDlg, DATETIME_CLOCK, CLM_TIMEHWND, CLF_SETHWND, 0);
}
////////////////////////////////////////////////////////////////////////////
//
// TimeProvider
//
// Called by the clock to find out what time it is.
//
////////////////////////////////////////////////////////////////////////////
void TimeProvider(
LPSYSTEMTIME lpSystemTime,
HWND hDlg)
{
short wTemp[7];
//
// If the user has modified the time, the clock should
// display the edit controls, otherwise its just the SystemTime.
//
if (g_Modified)
{
lpSystemTime->wHour = (WORD)g_Time[HOUR];
lpSystemTime->wMinute = (WORD)g_Time[MINUTE];
lpSystemTime->wSecond = (WORD)g_Time[SECOND];
}
else
{
#ifdef WIN32
GetLocalTime(lpSystemTime);
#else
GetTime();
if (wDateTime[HOUR] >= 0 && wDateTime[HOUR] <= 24)
{
lpSystemTime->wHour = wDateTime[HOUR];
}
lpSystemTime->wMinute = wDateTime[MINUTE];
lpSystemTime->wSecond = wDateTime[SECOND];
#endif
//
// Copy the time and display it for us too.
//
g_bPM = IsAMPM(lpSystemTime->wHour);
g_Time[HOUR] = lpSystemTime->wHour;
g_Time[MINUTE] = lpSystemTime->wMinute;
g_Time[SECOND] = lpSystemTime->wSecond;
//
// Check for date rollover.
//
if (!fDateDirty)
{
wTemp[DAY] = wDateTime[DAY];
wTemp[MONTH] = wDateTime[MONTH];
wTemp[YEAR] = wDateTime[YEAR];
GetDate();
if ((wDateTime[DAY] != wTemp[DAY]) ||
(wDateTime[MONTH] != wTemp[MONTH]) ||
(wDateTime[YEAR] != wTemp[YEAR]))
{
InvalidateRect(GetDlgItem(hDlg, DATETIME_CALENDAR), NULL, TRUE);
if (wDateTime[MONTH] != wTemp[MONTH])
{
ComboBox_SetCurSel( GetDlgItem(hDlg, DATETIME_MONTHNAME),
wDateTime[MONTH] - 1 );
}
if (wDateTime[YEAR] != wTemp[YEAR])
{
UpdateItem(hDlg, YEAR);
}
_ShowTZ(hDlg);
}
}
UpdateItem(hDlg, HOUR);
UpdateItem(hDlg, MINUTE);
UpdateItem(hDlg, SECOND);
ReflectAMPM(hDlg, g_Time[HOUR]);
}
}
////////////////////////////////////////////////////////////////////////////
//
// bSupportedCalendar
//
// Returns True if the current calendar is not Hijri nor Hebrew
//
// Otherwise it returns FALSE.
//
////////////////////////////////////////////////////////////////////////////
BOOL bSupportedCalendar()
{
TCHAR tchCalendar[32];
CALTYPE defCalendar = CAL_GREGORIAN;
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_ICALENDARTYPE,
tchCalendar,
ARRAYSIZE(tchCalendar)))
{
defCalendar = StrToInt(tchCalendar);
}
return (!(defCalendar == CAL_HIJRI || defCalendar == CAL_HEBREW));
}
////////////////////////////////////////////////////////////////////////////
//
// InitDateTimeDlg
//
// Called to init the dialog.
//
////////////////////////////////////////////////////////////////////////////
void InitDateTimeDlg(
HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
int nMaxDigitWidth;
int i;
TCHAR szNum[5];
TCHAR szMonth[64];
TCHAR szShortDate[12];
HDC hDC;
HFONT hFont;
HWND hwndCB;
CALID calId;
static int nInc[] = { 1, 5, 5, 1, 1, 5 };
HWND hwndScroll;
UDACCEL udAccel[2];
HWND hwndTBorder;
HCURSOR oldcursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
LCID lcid = LOCALE_USER_DEFAULT;
InitCommonControls();
//
// Sets the Leading zero status of the 6 controls.
//
g_bLZero[HOUR] = g_bLZero[MONTH] = g_bLZero[DAY] = FALSE;
g_bLZero[MINUTE] = g_bLZero[SECOND] = g_bLZero[YEAR] = TRUE;
hDC = GetDC(hDlg);
if (hFont = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0L))
{
hFont = SelectObject( hDC, hFont );
}
if (hFont)
{
SelectObject(hDC, hFont);
}
AdjustAMPMPosition(hDlg);
nMaxDigitWidth = GetMaxCharWidth(hDC);
ReleaseDC(hDlg, hDC);
g_bLZero[HOUR] = GetProfileInt(szIntl, TEXT("iTLZero"), 0);
//
// Initialize szShortDate in case GetProfileString fails.
//
lstrcpyn(szShortDate, IntlDef.sShortDate, ARRAYSIZE(szShortDate));
GetProfileString(szIntl, TEXT("sShortDate"), IntlDef.sShortDate, szShortDate, ARRAYSIZE(szShortDate));
ReadShortDate(szShortDate, g_bLZero + MONTH, g_bLZero + DAY, g_bLZero + YEAR);
g_bLZero[YEAR] = TRUE; //we always want the year to be 4 digits (this will be bad in late 9999)
//
// Setup the TIME stuff.
//
GetTime();
g_Time[HOUR] = wDateTime[HOUR];
g_Time[MINUTE] = wDateTime[MINUTE];
g_Time[SECOND] = wDateTime[SECOND];
GetProfileString(szIntl, TEXT("sTime"), IntlDef.sTime, szNum, 3);
DateTimeInit(hDlg, DATETIME_HOUR, DATETIME_TSEP1, szNum, nMaxDigitWidth, FALSE);
//
// Force all entries to be re-drawn,
//
g_LastTime[HOUR] = g_LastTime[MINUTE] = g_LastTime[SECOND] = -1;
UpdateItem(hDlg, HOUR);
UpdateItem(hDlg, MINUTE);
UpdateItem(hDlg, SECOND);
ReflectAMPM(hDlg, wDateTime[HOUR]);
//
// Setup the Date stuff.
//
GetDate();
g_sDateInfo[DAY].nMax = MonthUpperBound(wDateTime[MONTH], wDateTime[YEAR]);
if (!g_bLZero[YEAR])
{
wDateTime[YEAR] %= 100;
g_sDateInfo[YEAR].nMax = 99;
g_sDateInfo[YEAR].nMin = 0;
}
else
{
g_sDateInfo[YEAR].nMax = 2099;
g_sDateInfo[YEAR].nMin = 1980;
}
for (i = MONTH; i <= YEAR; i++)
{
wPrevDateTime[i] = -1;
}
//
// Get the month names. And select this month.
//
hwndCB = GetDlgItem(hDlg, DATETIME_MONTHNAME);
ComboBox_ResetContent(hwndCB);
//
// If the current calendar is Hijri or Hebrew then use the Gregorian one.
//
if (!bSupportedCalendar())
lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
GetLocaleInfo(lcid, LOCALE_ICALENDARTYPE, szMonth, ARRAYSIZE(szMonth));
calId = (CALID)StrToInt(szMonth);
for (i = 0; i < 12; i++)
{
#ifdef WINNT
GetCalendarInfo(lcid, calId, CAL_SMONTHNAME1 + i, szMonth, ARRAYSIZE(szMonth), NULL);
#else
GetLocaleInfo( LOCALE_USER_DEFAULT,
LOCALE_SMONTHNAME1 + i,
szMonth,
sizeof(szMonth) );
if (*szMonth && !IsDBCSLeadByte(*szMonth))
{
*szMonth = (TCHAR)CharUpper((LPTSTR)(DWORD)(TBYTE)*szMonth);
}
#endif
ComboBox_AddString(hwndCB, szMonth);
}
ComboBox_SetCurSel(hwndCB, wDateTime[MONTH] - 1);
//
// Set the default modifier for the Year Updown arrows.
//
wParam -= DATETIME_HOUR;
hwndScroll = GetDlgItem(hDlg, DATETIME_YARROW);
SendMessage( hwndScroll,
UDM_SETRANGE,
0,
MAKELPARAM(g_sDateInfo[YEAR].nMax, g_sDateInfo[YEAR].nMin) );
udAccel[0].nSec = 0;
udAccel[0].nInc = 1;
udAccel[1].nSec = 2;
udAccel[1].nInc = nInc[YEAR];
SendMessage(hwndScroll, UDM_SETACCEL, 2, (LPARAM)(LPUDACCEL)udAccel);
SendMessage(hwndScroll, UDM_SETBUDDY, (WPARAM)GetDlgItem(hDlg, DATETIME_YEAR), 0L);
//
// Set the default modifier for the time arrows.
// It should control the HOURS by default as per joelgros
//
hwndScroll = GetDlgItem(hDlg, DATETIME_TARROW);
SendMessage( hwndScroll,
UDM_SETRANGE,
0,
MAKELPARAM( g_sDateInfo[HOUR].nMax,
g_sDateInfo[HOUR].nMin) );
udAccel[0].nSec = 0;
udAccel[0].nInc = 1;
udAccel[1].nSec = 2;
udAccel[1].nInc = nInc[HOUR];
SendMessage( hwndScroll, UDM_SETACCEL, 2, (LPARAM)(LPUDACCEL)udAccel );
SendMessage( hwndScroll, UDM_SETBUDDY, (WPARAM)GetDlgItem(hDlg, DATETIME_HOUR), 0L );
//
// Make the 'well' for the digits appear.
//
hwndTBorder = GetDlgItem(hDlg, DATETIME_TBORDER);
SetWindowLong( hwndTBorder,
GWL_EXSTYLE,
GetWindowLong(hwndTBorder, GWL_EXSTYLE) | WS_EX_CLIENTEDGE );
//
// Display the border right now.
//
SetWindowPos( hwndTBorder,
NULL,
0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_DRAWFRAME | SWP_SHOWWINDOW );
//
// Display month->year.
//
for (i = MONTH; i <= YEAR; i++)
{
if ((wDateTime[i] != wPrevDateTime[i]) &&
(GetFocus() != GetDlgItem(hDlg, DATETIME_HOUR + i)))
{
//
// Update previous date-time.
//
wPrevDateTime[i] = wDateTime[i];
if (i == YEAR)
{
UpdateItem(hDlg, i);
}
}
}
g_Modified = FALSE;
//
// Tell the clock that we have a time provider - must be done last.
//
SendDlgItemMessage( hDlg,
DATETIME_CLOCK,
CLM_TIMEHWND,
CLF_SETHWND,
(LPARAM)(LPINT)hDlg );
SetCursor(oldcursor);
}
////////////////////////////////////////////////////////////////////////////
//
// CheckNum
//
////////////////////////////////////////////////////////////////////////////
LRESULT CheckNum(
HWND hDlg,
UINT nScrollID,
HWND hCtl)
{
static int cReenter = 0;
LRESULT lRet;
//
// If this is an illegal value, (but not blank), then kill the last char
// that was entered.
//
lRet = SendDlgItemMessage(hDlg, nScrollID, UDM_GETPOS, 0, 0L);
//
// Guard against re-entrance.
//
++cReenter;
if (cReenter <= 4)
{
SendMessage( hCtl,
HIWORD(lRet) && GetWindowTextLength(hCtl)
? EM_UNDO
: EM_EMPTYUNDOBUFFER,
0,
0L );
}
--cReenter;
return (lRet);
}
////////////////////////////////////////////////////////////////////////////
//
// DateTimeDlgProc
//
// Main dialog proc.
//
////////////////////////////////////////////////////////////////////////////
INT_PTR CALLBACK DateTimeDlgProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
int i;
switch (uMsg)
{
case (WM_INITDIALOG):
{
AddInternetPageAsync(GetParent(hDlg), hDlg);
InitDateTimeDlg(hDlg, uMsg, wParam, lParam);
g_PrevIMCForDateField = ImmAssociateContext(GetDlgItem(hDlg, DATETIME_YEAR), 0);
break;
}
case ( WM_DESTROY ) :
{
if (g_PrevIMCForDateField)
{
ImmAssociateContext( GetDlgItem(hDlg, DATETIME_YEAR),
g_PrevIMCForDateField );
}
UnhookTimer(hDlg);
break;
}
#ifdef WIN32
case ( WM_CTLCOLORSTATIC ) :
#endif
case ( WM_CTLCOLOR ) :
{
//
// Set the background color of the time controls to the the
// color of the edit controls.
//
if ((GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg) ==
GetDlgItem(hDlg, DATETIME_TSEP1)) ||
(GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg) ==
GetDlgItem(hDlg, DATETIME_TSEP2)) ||
(GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg) ==
GetDlgItem(hDlg, DATETIME_TBORDER)))
{
#ifndef WIN32
//
// Make the statics the color of the edits.
//
lParam = GET_WM_CTLCOLOR_MPS(
GET_WM_CTLCOLOR_HDC(wParam, lParam, uMsg),
GET_WM_CTLCOLOR_HWND(wParam, lParam, uMsg),
CTLCOLOR_EDIT );
return ((INT_PTR)DefWindowProc(hDlg, uMsg, wParam, lParam));
#else
return ((INT_PTR)DefWindowProc(hDlg, WM_CTLCOLOREDIT, wParam, lParam));
#endif
}
return (0);
break;
}
case ( WM_NOTIFY ) :
{
//
// Property sheet handler stuff.
//
switch (((NMHDR *)lParam)->code)
{
case ( PSN_SETACTIVE ) :
{
_ShowTZ(hDlg);
break;
}
case ( PSN_RESET ) :
{
UnhookTimer(hDlg);
SetFocus(GetDlgItem(hDlg, (int)wParam));
GetDate();
GetTime();
break;
}
case ( PSN_APPLY ) :
{
wDateTime[MINUTE] = (WORD)g_Time[MINUTE];
wDateTime[SECOND] = (WORD)g_Time[SECOND];
if (g_b24HR)
{
wDateTime[HOUR] = (WORD)g_Time[HOUR];
}
else
{
wDateTime[HOUR] = g_Time[HOUR] % 12;
if (g_bPM)
{
wDateTime[HOUR] += 12;
}
}
g_WasModified = g_Modified;
SetTime();
g_LastTime[HOUR] = g_LastTime[MINUTE] = g_LastTime[SECOND] = -1;
for (i = MONTH; i <= YEAR; i++)
{
wPrevDateTime[i] = -1;
}
g_Modified = FALSE;
wPrevDateTime[HOUR] = wDateTime[HOUR];
wPrevDateTime[MINUTE] = wDateTime[MINUTE];
wPrevDateTime[SECOND] = wDateTime[SECOND];
wPrevDateTime[MONTH] = wDateTime[MONTH];
wPrevDateTime[DAY] = wDateTime[DAY];
wPrevDateTime[YEAR] = wDateTime[YEAR];
wPrevDateTime[WEEKDAY] = wDateTime[WEEKDAY];
//
// We handled it - no repaint.
//
return (TRUE);
}
}
break;
}
case ( WM_VSCROLL ) :
{
switch (GET_WM_VSCROLL_CODE(wParam, lParam))
{
case ( SB_THUMBPOSITION ) :
{
SYSTEMTIME SystemTime;
HWND hBuddy = (HWND)SendMessage(
GET_WM_VSCROLL_HWND(wParam, lParam),
UDM_GETBUDDY,
0,
0L );
if (hBuddy == GetDlgItem(hDlg, DATETIME_HOUR))
{
g_Time[HOUR] = GET_WM_VSCROLL_POS(wParam, lParam);
}
else if (hBuddy == GetDlgItem(hDlg, DATETIME_MINUTE))
{
g_Time[MINUTE] = GET_WM_VSCROLL_POS(wParam, lParam);
}
else if (hBuddy == GetDlgItem(hDlg, DATETIME_SECOND))
{
g_Time[SECOND] = GET_WM_VSCROLL_POS(wParam, lParam);
}
// else if (hBuddy == GetDlgItem(hDlg, DATETIME_AMPM))
if (hBuddy != GetDlgItem(hDlg, DATETIME_YEAR))
g_Modified = TRUE;
//
// Light the apply now button.
//
PropSheet_Changed(GetParent(hDlg), hDlg);
//
// Force the clock to reflect this setting.
//
TimeProvider(&SystemTime, hDlg);
SendDlgItemMessage( hDlg,
DATETIME_CLOCK,
CLM_UPDATETIME,
CLF_SETTIME,
(LPARAM)(LPSYSTEMTIME)&SystemTime );
//
// Fall thru to update the year...
//
}
case ( SB_ENDSCROLL ) :
{
//
// If this is the year, have the calendar repaint.
//
if ((HWND)SendMessage( GET_WM_VSCROLL_HWND(wParam, lParam),
UDM_GETBUDDY,
0,
0L ) == GetDlgItem(hDlg, DATETIME_YEAR))
{
//
// Have it update the information.
//
GetTime();
AdjustDelta(hDlg, YEAR);
UpdateItem(hDlg, YEAR);
InvalidateRect( GetDlgItem(hDlg, DATETIME_CALENDAR),
NULL,
TRUE );
}
break;
}
}
break;
}
case ( CLM_UPDATETIME ) :
{
//
// The clock updating/reflecting the time.
//
switch (wParam)
{
case ( CLF_SETTIME ) :
{
//
// Clock telling us what the time is.
//
g_Modified = TRUE;
g_Time[HOUR] = ((LPSYSTEMTIME)lParam)->wHour;
g_Time[MINUTE] = ((LPSYSTEMTIME)lParam)->wMinute;
g_Time[SECOND] = ((LPSYSTEMTIME)lParam)->wSecond;
g_bPM = IsAMPM(g_Time[HOUR]);
break;
}
case ( CLF_GETTIME ) :
{
//
// We tell the clock what time it is.
//
TimeProvider((LPSYSTEMTIME)lParam, hDlg);
break;
}
}
break;
}
case ( WM_COMMAND ) :
{
//
// Command processing.
//
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case ( DATETIME_AMPM ) :
{
//
// Deals with the AMPM control.
//
UDACCEL udAccel;
HWND hwndScroll = GetDlgItem(hDlg, DATETIME_TARROW);
HWND hwndThisCtl = GET_WM_COMMAND_HWND(wParam, lParam);
//
// We only care if we get/loose the focus.
//
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case ( LBN_SETFOCUS ) :
{
//
// If we get the focus, then the UD control
// should deal with the AMPM.
//
// Select the visible entry.
//
ReflectAMPM(hDlg, wDateTime[HOUR]);
// if it has a buddy, remove it...
if ((HWND)SendMessage( hwndScroll,
UDM_GETBUDDY,
0,
0 ) != NULL)
{
SendMessage(hwndScroll, UDM_SETBUDDY, 0, 0);
}
//
// Tell the UD control how to manipulate AM/PM.
//
SendMessage( hwndScroll,
UDM_SETRANGE,
0,
MAKELPARAM(1, 0) );
udAccel.nSec = 0;
udAccel.nInc = 1;
SendMessage( hwndScroll,
UDM_SETACCEL,
1,
(LPARAM)(LPUDACCEL)&udAccel );
SendMessage( hwndScroll,
UDM_SETBUDDY,
(WPARAM)hwndThisCtl,
0 );
break;
}
case ( LBN_KILLFOCUS ) :
{
//
// When we loose focus, the g_bPM flag is updated.
//
// Remove selection from the AM/PM.
//
ListBox_SetCurSel(hwndThisCtl, -1);
if ((HWND)SendMessage( hwndScroll,
UDM_GETBUDDY,
0,
0 ) == hwndThisCtl)
{
SendMessage(hwndScroll, UDM_SETBUDDY, 0, 0);
}
break;
}
case ( LBN_SELCHANGE ) :
{
if ((g_Modified == FALSE) &&
(g_bPM == (BOOL)ListBox_GetTopIndex(hwndThisCtl)))
{
break;
}
//
// Find the visible entry.
//
g_Modified = TRUE;
//
// Light the apply now button.
//
PropSheet_Changed(GetParent(hDlg), hDlg);
g_bPM = (BOOL)ListBox_GetTopIndex(hwndThisCtl);
break;
}
}
break;
}
case ( DATETIME_HOUR ) :
case ( DATETIME_MINUTE ) :
case ( DATETIME_SECOND ) :
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case ( EN_CHANGE ) :
{
SYSTEMTIME SystemTime;
g_Modified = TRUE;
//
// Light the apply now button.
//
PropSheet_Changed(GetParent(hDlg), hDlg);
//
// Work out what the change was too.
//
g_Time[GET_WM_COMMAND_ID(wParam, lParam) -
DATETIME_HOUR] =
(int)SendDlgItemMessage( hDlg,
DATETIME_TARROW,
UDM_GETPOS,
0,
0 );
//
// Force the clock to reflect this setting.
//
TimeProvider(&SystemTime, hDlg);
SendDlgItemMessage( hDlg,
DATETIME_CLOCK,
CLM_UPDATETIME,
0,
(LPARAM)(LPSYSTEMTIME)&SystemTime );
break;
}
}
// fall thru...
}
case ( DATETIME_MONTH ) :
case ( DATETIME_YEAR ) :
case ( DATETIME_DAY ) :
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case ( EN_CHANGE ) :
{
CheckNum( hDlg,
GET_WM_COMMAND_ID(wParam, lParam) <= DATETIME_SECOND
? DATETIME_TARROW
: DATETIME_YARROW,
GET_WM_COMMAND_HWND(wParam, lParam) );
// Changing the year may alter the number of days in February.
// Yes this is a hack, but this entire applet is a giant
// broken hack and I want to change it as little as possible.
if (GET_WM_COMMAND_ID(wParam, lParam) == DATETIME_YEAR && wDateTime[MONTH] == 2)
{
g_sDateInfo[DAY].nMax = MonthUpperBound( wDateTime[MONTH],
wDateTime[YEAR] );
if (wDateTime[DAY] > g_sDateInfo[DAY].nMax)
{
wDateTime[DAY] = (WORD)g_sDateInfo[DAY].nMax;
fDateDirty = TRUE;
}
InvalidateRect( GetDlgItem(hDlg, DATETIME_CALENDAR),
NULL,
TRUE );
}
break;
}
case ( EN_SETFOCUS ) :
{
UINT id = GET_WM_COMMAND_ID(wParam, lParam) - DATETIME_HOUR;
if (id <= SECOND)
{
UDACCEL udAccel[2];
static int nInc[] = { 1, 5, 5, 1, 1, 5 };
HWND hwndScroll = GetDlgItem(hDlg, DATETIME_TARROW);
// if it has a buddy, remove it...
if ((HWND)SendMessage( hwndScroll,
UDM_GETBUDDY,
0,
0 ) != NULL)
{
SendMessage(hwndScroll, UDM_SETBUDDY, 0, 0);
}
//
// now set the new one
//
SendMessage( hwndScroll,
UDM_SETRANGE,
0,
MAKELPARAM( g_sDateInfo[id].nMax,
g_sDateInfo[id].nMin) );
udAccel[0].nSec = 0;
udAccel[0].nInc = 1;
udAccel[1].nSec = 2;
udAccel[1].nInc = nInc[id];
SendMessage( hwndScroll,
UDM_SETACCEL,
2,
(LPARAM)(LPUDACCEL)udAccel );
//
// Set the UD to update this control.
//
SendMessage( hwndScroll,
UDM_SETBUDDY,
(WPARAM)GET_WM_COMMAND_HWND(wParam,
lParam),
0 );
}
break;
}
case ( EN_KILLFOCUS ) :
{
//
// Gets in range HMS MDY.
//
UINT id = GET_WM_COMMAND_ID(wParam, lParam) - DATETIME_HOUR;
AdjustDelta(hDlg, id);
UpdateItem(hDlg, id);
//
// If control is YEAR.
//
if (id == (DATETIME_YEAR - DATETIME_HOUR))
{
InvalidateRect( GetDlgItem(hDlg, DATETIME_CALENDAR),
NULL,
TRUE );
}
break;
}
default :
{
break;
}
}
break;
}
case ( DATETIME_MONTHNAME ) :
{
if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
{
int nIndex = 1 + (int)ComboBox_GetCurSel(
GetDlgItem( hDlg,
DATETIME_MONTHNAME ));
if (wDateTime[MONTH] != nIndex)
{
AdjustDeltaMonth(nIndex);
InvalidateRect( GetDlgItem(hDlg, DATETIME_CALENDAR),
NULL,
TRUE );
PropSheet_Changed(GetParent(hDlg), hDlg);
}
}
break;
}
case ( DATETIME_CALENDAR ) :
{
//
// If the calendar sent us a change, we will assume
// that it is to allow the apply now to work.
//
PropSheet_Changed(GetParent(hDlg), hDlg);
break;
}
}
break;
}
case ( WM_WININICHANGE ) :
{
//
// Reinitialize if there is a time format change.
//
InitDateTimeDlg(hDlg, uMsg, wParam, lParam);
InvalidateRect(GetDlgItem(hDlg, DATETIME_CALENDAR), NULL, TRUE);
break;
}
case ( WM_TIMECHANGE ) :
{
//
// Forward time change messages to the clock control.
//
SendDlgItemMessage( hDlg,
DATETIME_CLOCK,
WM_TIMECHANGE,
wParam,
lParam );
break;
}
case ( WMUSER_ADDINTERNETTAB ) :
{
AddInternetTab(hDlg);
break;
}
case ( WM_HELP ) : // F1
{
WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
NULL,
HELP_WM_HELP,
(DWORD_PTR)(LPTSTR)aDateTimeHelpIds );
break;
}
case ( WM_CONTEXTMENU ) : // right mouse click
{
WinHelp( (HWND)wParam,
NULL,
HELP_CONTEXTMENU,
(DWORD_PTR)(LPTSTR)aDateTimeHelpIds );
break;
}
default :
{
return (FALSE);
}
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// SetZoneState
//
// Sets the display state of a time zone in the map control.
//
////////////////////////////////////////////////////////////////////////////
void SetZoneState(
HWND map,
PTZINFO zone,
BOOL highlight)
{
if (zone)
{
if (zone->SeaIndex >= 0)
{
MapControlSetSeaRegionHighlight( map,
zone->SeaIndex,
highlight,
zone->MapLeft,
zone->MapWidth );
}
if (zone->LandIndex >= 0)
{
MapControlSetLandRegionHighlight( map,
zone->LandIndex,
highlight,
zone->MapLeft,
zone->MapWidth );
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// SetZoneFamilyState
//
// Sets the display state of a time zone family in the map control.
//
////////////////////////////////////////////////////////////////////////////
void SetZoneFamilyState(
HWND map,
PTZINFO family,
BOOL highlight)
{
if (family)
{
PTZINFO zone = family;
do
{
SetZoneState(map, zone, highlight);
zone = zone->next;
}
while(zone && (zone != family));
}
}
////////////////////////////////////////////////////////////////////////////
//
// ParseMapInfo
//
// Parses the color table information about the world bitmap we display.
//
// Expected format: "sea,land"
// where sea and land are color table indices or -1.
//
////////////////////////////////////////////////////////////////////////////
void ParseMapInfo(
PTZINFO zone,
const TCHAR *text)
{
const TCHAR *p = text;
zone->SeaIndex = zone->LandIndex = -1;
if (*p)
{
if (*p != TEXT('-'))
{
zone->SeaIndex = 0;
while (*p && (*p != TEXT(',')))
{
zone->SeaIndex = (10 * zone->SeaIndex) + (*p - TEXT('0'));
p++;
}
}
else
{
do
{
p++;
} while (*p && (*p != TEXT(',')));
}
if (*p == TEXT(','))
{
p++;
}
if (*p)
{
if (*p != TEXT('-'))
{
zone->LandIndex = 0;
while (*p)
{
zone->LandIndex = (10 * zone->LandIndex) + (*p - TEXT('0'));
p++;
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// ReadZoneData
//
// Reads the data for a time zone from the registry.
//
////////////////////////////////////////////////////////////////////////////
BOOL ReadZoneData(
PTZINFO zone,
HKEY key,
LPCTSTR keyname)
{
TCHAR mapinfo[16];
DWORD len;
len = sizeof(zone->szDisplayName);
if (RegQueryValueEx( key,
c_szTZDisplayName,
0,
NULL,
(LPBYTE)zone->szDisplayName,
&len ) != ERROR_SUCCESS)
{
return (FALSE);
}
//
// Under NT, the keyname is the "Standard" name. Values stored
// under the keyname contain the other strings and binary info
// related to the time zone. Every time zone must have a standard
// name, therefore, we save registry space by using the Standard
// name as the subkey name under the "Time Zones" key.
//
len = sizeof(zone->szStandardName);
if (RegQueryValueEx( key,
c_szTZStandardName,
0,
NULL,
(LPBYTE)zone->szStandardName,
&len ) != ERROR_SUCCESS)
{
//
// Use keyname if can't get StandardName value.
//
lstrcpyn( zone->szStandardName,
keyname,
sizeof(zone->szStandardName) );
}
len = sizeof(zone->szDaylightName);
if (RegQueryValueEx( key,
c_szTZDaylightName,
0,
NULL,
(LPBYTE)zone->szDaylightName,
&len ) != ERROR_SUCCESS)
{
return (FALSE);
}
len = sizeof(zone->Bias) +
sizeof(zone->StandardBias) +
sizeof(zone->DaylightBias) +
sizeof(zone->StandardDate) +
sizeof(zone->DaylightDate);
if (RegQueryValueEx( key,
c_szTZI,
0,
NULL,
(LPBYTE)&zone->Bias,
&len ) != ERROR_SUCCESS)
{
return (FALSE);
}
len = sizeof(mapinfo);
if (RegQueryValueEx( key,
c_szTZMapInfo,
0,
NULL,
(LPBYTE)mapinfo,
&len ) != ERROR_SUCCESS)
{
*mapinfo = TEXT('\0');
}
ParseMapInfo(zone, mapinfo);
//
// Generate phony MapLeft and MapRight until they show up in the
// registry.
//
zone->MapLeft = ((zone->Bias * ZONE_IMAGE_SCALE) / ZONE_BIAS_SCALE) +
ZONE_IMAGE_LEFT;
zone->MapWidth = ZONE_IMAGE_WIDTH;
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// AddZoneToList
//
// Inserts a new time zone into a list, sorted by bias and then name.
//
////////////////////////////////////////////////////////////////////////////
void AddZoneToList(
PTZINFO *list,
PTZINFO zone)
{
if (*list)
{
PTZINFO curr = NULL;
PTZINFO next = *list;
while (next && zone->Bias <= next->Bias)
{
if (zone->Bias == next->Bias)
{
if (CompareString( GetUserDefaultLCID(),
0,
zone->szDisplayName,
-1,
next->szDisplayName,
-1 ) == CSTR_LESS_THAN)
{
break;
}
}
curr = next;
next = curr->next;
}
zone->next = next;
if (curr)
{
curr->next = zone;
}
else
{
*list = zone;
}
}
else
{
*list = zone;
}
}
////////////////////////////////////////////////////////////////////////////
//
// FreeTimezoneList
//
// Frees all time zones in the passed list, setting the head to NULL.
//
////////////////////////////////////////////////////////////////////////////
void FreeTimezoneList(
PTZINFO *list)
{
while (*list)
{
PTZINFO next = (*list)->next;
LocalFree((HANDLE)*list);
*list = next;
}
}
////////////////////////////////////////////////////////////////////////////
//
// ReadTimezones
//
// Reads the time zone information from the registry.
// Returns num read, -1 on failure.
//
////////////////////////////////////////////////////////////////////////////
int ReadTimezones(
PTZINFO *list)
{
HKEY key = NULL;
int count = -1;
*list = NULL;
if (RegOpenKey( HKEY_LOCAL_MACHINE,
c_szTimeZones,
&key ) == ERROR_SUCCESS)
{
TCHAR name[TZNAME_SIZE];
PTZINFO zone = NULL;
int i;
count = 0;
for (i = 0;
RegEnumKey(key, i, name, TZNAME_SIZE) == ERROR_SUCCESS;
i++)
{
HKEY subkey = NULL;
if (!zone &&
((zone = (PTZINFO)LocalAlloc(LPTR, sizeof(TZINFO))) == NULL))
{
zone = *list;
*list = NULL;
count = -1;
break;
}
zone->next = NULL;
if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS)
{
//
// Each sub key name under the Time Zones key is the
// "Standard" name for the Time Zone.
//
lstrcpyn(zone->szStandardName, name, TZNAME_SIZE);
if (ReadZoneData(zone, subkey, name))
{
AddZoneToList(list, zone);
zone = NULL;
count++;
}
RegCloseKey(subkey);
}
}
FreeTimezoneList(&zone);
RegCloseKey(key);
}
return (count);
}
////////////////////////////////////////////////////////////////////////////
//
// InitZoneMapping
//
// Initializes map and map lookup for a specific time zone.
//
////////////////////////////////////////////////////////////////////////////
void InitZoneMapping(
PTZINFO *lookup,
PTZINFO list,
HWND map)
{
PTZINFO zone = list; // not needed but more readable
while (zone)
{
if (zone->SeaIndex >= 0)
{
lookup[zone->SeaIndex] = zone;
}
if (zone->LandIndex >= 0)
{
lookup[zone->LandIndex] = zone;
}
SetZoneState(map, zone, FALSE);
zone = zone->next;
}
}
////////////////////////////////////////////////////////////////////////////
//
// BreakZonesIntoFamilies
//
// Breaks the passed list into many circular lists.
// Each list consists of all time zones with a particular bias.
// Assumes the passed list is sorted by bias.
//
////////////////////////////////////////////////////////////////////////////
void BreakZonesIntoFamilies(
PTZINFO head)
{
PTZINFO subhead = NULL;
PTZINFO last = NULL;
PTZINFO zone = head;
while (zone)
{
subhead = zone;
do
{
last = zone;
zone = zone->next;
}
while (zone && (zone->Bias == subhead->Bias));
last->next = subhead;
}
//
// Merge -12 and +12 zones into a single group.
// Assumes populated registry and depends on sort order.
//
if ((subhead) &&
(subhead->Bias == BIAS_PLUS_12) &&
(head->Bias == BIAS_MINUS_12))
{
PTZINFO next = head;
do
{
zone = next;
next = zone->next;
}
while (next != head);
zone->next = subhead;
last->next = head;
}
}
////////////////////////////////////////////////////////////////////////////
//
// InitTimezones
//
// Initializes time zone stuff, UI and otherwise.
//
////////////////////////////////////////////////////////////////////////////
BOOL InitTimezones(
HWND page,
PTZINFO *lookup)
{
PTZINFO list = NULL;
if ((g_nTimeZones = ReadTimezones(&list)) >= 0)
{
HWND combo = GetDlgItem(page, IDD_TIMEZONES);
PTZINFO zone = list;
SetWindowRedraw(combo, FALSE);
while (zone)
{
int index = ComboBox_AddString(combo, zone->szDisplayName);
if (index < 0)
{
break;
}
zone->ComboIndex = index;
ComboBox_SetItemData(combo, index, (LPARAM)zone);
zone = zone->next;
}
SetWindowRedraw(combo, TRUE);
if (!zone)
{
InitZoneMapping(lookup, list, GetDlgItem(page, IDD_TIMEMAP));
BreakZonesIntoFamilies(list);
return (TRUE);
}
FreeTimezoneList(&list);
ComboBox_ResetContent(combo);
}
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// ChangeZone
//
// Updates the current zone, making sure new zone's family is highlighted.
//
////////////////////////////////////////////////////////////////////////////
void ChangeZone(
HWND page,
TZPAGE_STATE *state,
PTZINFO zone)
{
if (zone || state->zone)
{
BOOL newfamily = (!zone || !state->zone ||
(zone->Bias != state->zone->Bias));
HWND map = GetDlgItem(page, IDD_TIMEMAP);
BOOL dayval = (zone && (zone->StandardDate.wMonth != 0));
if (newfamily && state->zone)
{
SetZoneFamilyState(map, state->zone, FALSE);
}
state->zone = zone;
if (newfamily && state->zone)
{
SetZoneFamilyState(map, state->zone, TRUE);
}
if (newfamily)
{
MapControlInvalidateDirtyRegions(map);
}
ShowWindow(GetDlgItem(page, IDD_AUTOMAGIC), (dayval != 0 ? SW_SHOW : SW_HIDE));
if (!state->initializing)
{
PropSheet_Changed(GetParent(page), page);
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// HotTrackZone
//
// Updates the map highlighting and combo selection for a given map index.
// Expects to be called with dups.
//
////////////////////////////////////////////////////////////////////////////
void HotTrackZone(
HWND page,
TZPAGE_STATE *state,
int index)
{
PTZINFO zone = state->lookup[index];
if (zone && (zone != state->zone))
{
ComboBox_SetCurSel( GetDlgItem(page, IDD_TIMEZONES),
(zone ? zone->ComboIndex : -1) );
ChangeZone(page, state, zone);
}
}
////////////////////////////////////////////////////////////////////////////
//
// CenterZone
//
// Updates the map highlighting and combo selection for a given map index.
// Expects to be called with dups.
//
////////////////////////////////////////////////////////////////////////////
void CenterZone(
HWND page,
TZPAGE_STATE *state,
BOOL animate)
{
PTZINFO zone = state->zone;
if (zone)
{
HWND map = GetDlgItem(page, IDD_TIMEMAP);
MapControlRotateTo(map, zone->MapLeft + zone->MapWidth / 2, animate);
}
}
////////////////////////////////////////////////////////////////////////////
//
// GetPTZ
//
// Returns the pointer for the iItem time zone.
// If iItem is -1 on entry, use the currently selected time zone.
//
////////////////////////////////////////////////////////////////////////////
PTZINFO GetPTZ(
HWND hDlg,
int iItem)
{
HWND hCtl = GetDlgItem(hDlg, IDD_TIMEZONES);
if (iItem == -1)
{
iItem = (int)ComboBox_GetCurSel(hCtl);
}
if (iItem < 0)
{
return (NULL);
}
return ((PTZINFO)ComboBox_GetItemData(hCtl, iItem));
}
////////////////////////////////////////////////////////////////////////////
//
// GetAllowLocalTimeChange
//
////////////////////////////////////////////////////////////////////////////
TCHAR c_szRegPathTZControl[] = REGSTR_PATH_TIMEZONE;
TCHAR c_szRegValDisableTZUpdate[] = REGSTR_VAL_TZNOAUTOTIME;
BOOL GetAllowLocalTimeChange()
{
//
// Assume allowed until we see a disallow flag.
//
BOOL result = TRUE;
HKEY key;
if (RegOpenKey( HKEY_LOCAL_MACHINE,
c_szRegPathTZControl,
&key ) == ERROR_SUCCESS)
{
//
// Assume no disallow flag until we see one.
//
DWORD value = 0;
long len = sizeof(value);
DWORD type;
if ((RegQueryValueEx( key,
c_szRegValDisableTZUpdate,
NULL,
&type,
(LPBYTE)&value,
&len ) == ERROR_SUCCESS) &&
((type == REG_DWORD) || (type == REG_BINARY)) &&
(len == sizeof(value)) && value)
{
//
// Okay, we have a nonzero value, it is either:
//
// 1) 0xFFFFFFFF
// this is set in an inf file for first boot to prevent
// the base from performing any cutovers during setup.
//
// 2) some other value
// this signifies that the user actualy disabled cutovers
// *return that local time changes are disabled
//
if (value != 0xFFFFFFFF)
{
result = FALSE;
}
}
RegCloseKey(key);
}
return (result);
}
////////////////////////////////////////////////////////////////////////////
//
// SetAllowLocalTimeChange
//
////////////////////////////////////////////////////////////////////////////
void SetAllowLocalTimeChange(
BOOL fAllow)
{
HKEY key = NULL;
if (fAllow)
{
//
// Remove the disallow flag from the registry if it exists.
//
if (RegOpenKey( HKEY_LOCAL_MACHINE,
c_szRegPathTZControl,
&key ) == ERROR_SUCCESS)
{
RegDeleteValue(key, c_szRegValDisableTZUpdate);
}
}
else
{
//
// Add/set the nonzero disallow flag.
//
if (RegCreateKey( HKEY_LOCAL_MACHINE,
c_szRegPathTZControl,
&key ) == ERROR_SUCCESS)
{
DWORD value = 1;
RegSetValueEx( key,
(LPCTSTR)c_szRegValDisableTZUpdate,
0UL,
REG_DWORD,
(LPBYTE)&value,
sizeof(value) );
}
}
if (key)
{
RegCloseKey(key);
}
}
////////////////////////////////////////////////////////////////////////////
//
// InitTimeZonePage
//
// This function initializes everything to do with the Time Zones.
//
////////////////////////////////////////////////////////////////////////////
BOOL InitTimeZonePage(
HWND hDlg,
TZPAGE_STATE *state)
{
TIME_ZONE_INFORMATION tziCurrent;
DWORD dwTZID;
PTZINFO ptzi;
int j ,iCurrentTZ;
BOOL fForceSelection = FALSE;
TCHAR temp[TZNAME_SIZE];
TCHAR oldTzMapName[TZNAME_SIZE], newTzMapName[TZNAME_SIZE];
//
// Get the current time zone information.
//
dwTZID = GetTimeZoneInformation(&tziCurrent);
LoadString(g_hInst, IDS_ISRAELTIMEZONE, oldTzMapName, TZNAME_SIZE);
LoadString(g_hInst, IDS_JERUSALEMTIMEZONE, newTzMapName, TZNAME_SIZE);
// this is a hack for Win95 or WinNT 4 to Win98/Win2k migration. "Israel" became "Jerusalem"
#ifdef UNICODE
if (!lstrcmpi(oldTzMapName, tziCurrent.StandardName))
{
lstrcpy(tziCurrent.StandardName, newTzMapName);
fForceSelection = TRUE;
}
#else
if (!AnsiWideStrCmpI(oldTzMapName, tziCurrent.StandardName))
{
StrToWideStr(tziCurrent.StandardName, newTzMapName);
fForceSelection = TRUE;
}
#endif
//
// Check for bogus time zone info.
//
if (dwTZID != TIME_ZONE_ID_INVALID)
{
//
// Copy the name out so we can check for first boot.
//
#ifdef UNICODE
lstrcpy(temp, tziCurrent.StandardName);
#else
WideStrToStr(temp, tziCurrent.StandardName);
#endif
}
else
{
//
// Treat bogus time zones like first boot.
//
lstrcpy(temp, c_szFirstBootTZ);
}
if (lstrcmpi(temp, c_szFirstBootTZ) == 0)
{
//
// The 'default' value of the time zone key specifies the
// default zone.
//
TCHAR szDefaultName[TZNAME_SIZE];
LONG len = sizeof(szDefaultName);
if (RegQueryValue( HKEY_LOCAL_MACHINE,
c_szTimeZones,
szDefaultName,
&len ) == ERROR_SUCCESS)
{
#ifdef UNICODE
lstrcpy(tziCurrent.StandardName, szDefaultName);
#else
StrToWideStr(tziCurrent.StandardName, szDefaultName);
#endif
}
else
{
tziCurrent.StandardName[0] = 0;
}
//
// If we can't find it by name, use GMT.
//
tziCurrent.StandardBias = tziCurrent.DaylightBias = tziCurrent.Bias = 0;
//
// Force the user to make a valid choice before quitting.
//
fForceSelection = TRUE;
}
//
// Get the Time Zones from the registry.
//
InitTimezones(hDlg, state->lookup);
//
// Try to select the 'current' one or some equivalent.
//
//
// Start with an invalid index.
//
iCurrentTZ = g_nTimeZones;
//
// Try to find by name.
//
for (j = 0; j < g_nTimeZones; j++)
{
ptzi = GetPTZ(hDlg, j);
#ifdef UNICODE
if (!lstrcmpi(ptzi->szStandardName, tziCurrent.StandardName))
#else
if (!AnsiWideStrCmpI(ptzi->szStandardName, tziCurrent.StandardName))
#endif
{
iCurrentTZ = j;
break;
}
}
//
// If it hasn't been found yet, try to find a nearby zone using biases.
//
if (iCurrentTZ == g_nTimeZones)
{
int nBestHitCount = TZ_HIT_NONE;
for (j = 0; j < g_nTimeZones; j++)
{
ptzi = GetPTZ(hDlg, j);
if (ptzi->Bias == tziCurrent.Bias)
{
int nHitCount = TZ_HIT_BASE +
((ptzi->StandardBias == tziCurrent.StandardBias) +
(ptzi->DaylightBias == tziCurrent.DaylightBias));
if (nHitCount > nBestHitCount)
{
nBestHitCount = nHitCount;
iCurrentTZ = j;
if (nHitCount >= TZ_HIT_EXACT)
{
break;
}
}
}
}
}
//
// Still didn't find it?
//
if (iCurrentTZ == g_nTimeZones)
{
//
// Punt.
//
iCurrentTZ = 0;
fForceSelection = TRUE;
}
//
// Set up the dialog using this time zone's info.
//
//
// Always use our rules for the allow-daylight muck.
//
#ifndef WINNT
if ((ptzi = GetPTZ(hDlg, iCurrentTZ)) != NULL)
{
tziCurrent.StandardDate = ptzi->StandardDate;
tziCurrent.DaylightDate = ptzi->DaylightDate;
}
#endif
//
// If wMonth is 0, then this Time Zone does not support DST.
//
if ((tziCurrent.StandardDate.wMonth == 0) ||
(tziCurrent.DaylightDate.wMonth == 0))
{
ShowWindow(GetDlgItem(hDlg, IDD_AUTOMAGIC), SW_HIDE);
}
//
// Always get "allow DLT" flag even if this zone is disabled.
//
CheckDlgButton(hDlg, IDD_AUTOMAGIC, GetAllowLocalTimeChange());
ComboBox_SetCurSel(GetDlgItem(hDlg, IDD_TIMEZONES), iCurrentTZ);
ChangeZone(hDlg, state, GetPTZ(hDlg, -1));
CenterZone(hDlg, state, FALSE);
if (fForceSelection || g_bFirstBoot)
{
PropSheet_Changed(GetParent(hDlg), hDlg);
PropSheet_CancelToClose(GetParent(hDlg));
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// SetTheTimezone
//
// Apply the User's time zone selection.
//
////////////////////////////////////////////////////////////////////////////
void SetTheTimezone(
BOOL bAutoMagicTimeChange,
BOOL bAutoMagicEnabled,
PTZINFO ptzi)
{
TIME_ZONE_INFORMATION tzi;
HCURSOR hCurOld;
if (!ptzi)
{
return;
}
tzi.Bias = ptzi->Bias;
#ifdef WINNT
if ((bAutoMagicTimeChange == 0) ||
(ptzi->StandardDate.wMonth == 0))
{
//
// Standard Only.
//
tzi.StandardBias = ptzi->StandardBias;
tzi.DaylightBias = ptzi->StandardBias;
tzi.StandardDate = ptzi->StandardDate;
tzi.DaylightDate = ptzi->StandardDate;
#ifdef UNICODE
lstrcpy(tzi.StandardName, ptzi->szStandardName);
lstrcpy(tzi.DaylightName, ptzi->szStandardName);
#else
StrToWideStr(tzi.StandardName, ptzi->szStandardName);
StrToWideStr(tzi.DaylightName, ptzi->szStandardName);
#endif
}
else
#endif
{
//
// Automatically adjust for Daylight Saving Time.
//
tzi.StandardBias = ptzi->StandardBias;
tzi.DaylightBias = ptzi->DaylightBias;
tzi.StandardDate = ptzi->StandardDate;
tzi.DaylightDate = ptzi->DaylightDate;
#ifdef UNICODE
lstrcpy(tzi.StandardName, ptzi->szStandardName);
lstrcpy(tzi.DaylightName, ptzi->szDaylightName);
#else
StrToWideStr(tzi.StandardName, ptzi->szStandardName);
StrToWideStr(tzi.DaylightName, ptzi->szDaylightName);
#endif
}
SetAllowLocalTimeChange(bAutoMagicTimeChange);
SetTimeZoneInformation(&tzi);
hCurOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
SetCursor(hCurOld);
}
////////////////////////////////////////////////////////////////////////////
//
// TimeZoneDlgProc
//
////////////////////////////////////////////////////////////////////////////
INT_PTR CALLBACK TimeZoneDlgProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
TZPAGE_STATE *state = (TZPAGE_STATE *)GetWindowLongPtr(hDlg, DWLP_USER);
int i;
switch (uMsg)
{
case ( WM_INITDIALOG ) :
{
state = (TZPAGE_STATE *)LocalAlloc(LPTR, sizeof(TZPAGE_STATE));
SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)state);
if (!state)
{
EndDialog(hDlg, -1);
break;
}
state->initializing = TRUE;
InitTimeZonePage(hDlg, state);
state->initializing = FALSE;
break;
}
case ( WM_DESTROY ) :
{
for (i = 0; i < g_nTimeZones; i++)
{
LocalFree((HLOCAL)GetPTZ(hDlg, i));
}
if (state)
{
LocalFree((HANDLE)state);
SetWindowLongPtr(hDlg, DWLP_USER, 0L);
}
break;
}
case ( WM_NOTIFY ) :
{
switch (((NMHDR *)lParam)->idFrom)
{
case ( 0 ) :
{
switch (((NMHDR *)lParam)->code)
{
case ( PSN_APPLY ) :
{
g_ptziCurrent = NULL;
//
// Find out which listbox item was selected.
//
SetTheTimezone(
IsDlgButtonChecked(hDlg, IDD_AUTOMAGIC),
IsWindowVisible(GetDlgItem(hDlg, IDD_AUTOMAGIC)),
GetPTZ(hDlg, -1) );
//
// if the user had modified the time as well as the timezone,
// then we should honor the time that they gave us since they
// explicitly said this was the time. If we don't then the
// time they entered will be offset by the timezone change
//
if (g_WasModified)
{
g_WasModified = FALSE;
SetTime();
}
break;
}
}
break;
}
case ( IDD_TIMEMAP ) :
{
NFYMAPEVENT *event = (NFYMAPEVENT *)lParam;
switch (event->hdr.code)
{
case ( MAPN_TOUCH ) :
{
HotTrackZone(hDlg, state, event->index);
break;
}
case ( MAPN_SELECT ) :
{
CenterZone(hDlg, state, TRUE);
break;
}
}
break;
}
}
break;
}
case ( WM_COMMAND ) :
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case ( IDD_TIMEZONES ) : // combo box
{
if (GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE)
{
ChangeZone(hDlg, state, GetPTZ(hDlg, -1));
CenterZone(hDlg, state, TRUE);
}
break;
}
case ( IDD_AUTOMAGIC ) : // check box
{
if (GET_WM_COMMAND_CMD(wParam, lParam) == BN_CLICKED)
{
PropSheet_Changed(GetParent(hDlg), hDlg);
}
break;
}
}
break;
}
case ( WM_HELP ) : // F1
{
WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
NULL,
HELP_WM_HELP,
(DWORD_PTR)(LPTSTR)aDateTimeHelpIds );
break;
}
case ( WM_CONTEXTMENU ) : // right mouse click
{
WinHelp( (HWND)wParam,
NULL,
HELP_CONTEXTMENU,
(DWORD_PTR)(LPTSTR)aDateTimeHelpIds );
break;
}
default :
{
return (FALSE);
}
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// GetClInt
//
// Steal an int from the command line.
//
////////////////////////////////////////////////////////////////////////////
static int GetClInt(
const TCHAR *p)
{
BOOL neg = FALSE;
int v = 0;
//
// Skip spaces.
//
while (*p == TEXT(' '))
{
p++;
}
//
// See if it's negative.
//
if (*p == TEXT('-'))
{
//
// It's negative. Remember that it's negative and skip the
// '-' char.
//
neg = TRUE;
p++;
}
//
// Parse the absolute portion. Digits only.
//
while ((*p >= TEXT('0')) && (*p <= TEXT('9')))
{
//
// Accumulate the value.
//
v = v * 10 + *p++ - TEXT('0');
}
//
// Return the result.
//
return (neg ? -v : v);
}
////////////////////////////////////////////////////////////////////////////
//
// SelectZoneByName
//
////////////////////////////////////////////////////////////////////////////
BOOL SelectZoneByName(
LPCTSTR cmdline)
{
BOOL result = FALSE;
HKEY key = NULL;
while (*cmdline == TEXT(' '))
{
cmdline++;
}
if (!*cmdline)
{
return (FALSE);
}
if (RegOpenKey( HKEY_LOCAL_MACHINE,
c_szTimeZones,
&key ) == ERROR_SUCCESS)
{
TCHAR name[TZNAME_SIZE];
HKEY subkey = NULL;
TZINFO zone;
//
// User can pass key name.
//
if (RegOpenKey(key, cmdline, &subkey) == ERROR_SUCCESS)
{
if (ReadZoneData(&zone, subkey, cmdline))
{
result = TRUE;
}
}
else
{
//
// User can also pass display name.
//
int i;
int CmdLen = lstrlen(cmdline);
for (i = 0;
RegEnumKey(key, i, name, TZNAME_SIZE) == ERROR_SUCCESS;
i++)
{
if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS)
{
LONG len = sizeof(zone.szDisplayName);
if ((RegQueryValueEx( subkey,
c_szTZDisplayName,
0,
NULL,
(LPBYTE)&zone.szDisplayName,
&len ) == ERROR_SUCCESS) &&
(CompareString( GetUserDefaultLCID(),
NORM_IGNORECASE | NORM_IGNOREKANATYPE |
NORM_IGNOREWIDTH | NORM_IGNORENONSPACE,
zone.szDisplayName,
(CmdLen < 15)
? -1
: min(lstrlen(zone.szDisplayName),
CmdLen),
cmdline,
-1 ) == CSTR_EQUAL))
{
if (ReadZoneData(&zone, subkey, name))
{
result = TRUE;
}
}
RegCloseKey(subkey);
}
if (result)
{
break;
}
}
}
RegCloseKey(key);
if (result)
{
SetTheTimezone(1, 1, &zone);
}
}
return (result);
}
////////////////////////////////////////////////////////////////////////////
//
// OpenDateTimePropertySheet
//
// Opens a DateTime property sheet.
// Set the page for the property sheet.
//
////////////////////////////////////////////////////////////////////////////
BOOL OpenDateTimePropertySheet(
HWND hwnd,
LPCTSTR cmdline)
{
// Make this an array for multiple pages.
PROPSHEETPAGE apsp[3];
PROPSHEETHEADER psh;
HDC hDC;
HFONT hFont;
int wMaxDigitWidth;
BOOL fReturn;
HRESULT hrOle;
hDC = GetDC(hwnd);
wMaxDigitWidth = GetMaxSubstitutedCharWidth(hDC);
ReleaseDC(hwnd, hDC);
psh.nStartPage = (UINT)-1;
if (cmdline && *cmdline)
{
if (*cmdline == TEXT('/'))
{
BOOL fAutoSet = FALSE;
//
// Legend:
// zZ: first boot batch mode setup "/z pacific" etc
// fF: regular first boot
// mM: time zone change forced local time change message
//
switch (*++cmdline)
{
case ( TEXT('z') ) :
case ( TEXT('Z') ) :
{
fAutoSet = TRUE;
//
// Fall thru...
//
}
case ( TEXT('f') ) :
case ( TEXT('F') ) :
{
g_bFirstBoot = TRUE;
if (fAutoSet && SelectZoneByName(cmdline + 1))
{
return (TRUE);
}
//
// Start on time zone page.
//
psh.nStartPage = 1;
break;
}
case ( TEXT('m') ) :
case ( TEXT('M') ) :
{
MSGBOXPARAMS params =
{
sizeof(params),
hwnd,
g_hInst,
MAKEINTRESOURCE(IDS_WARNAUTOTIMECHANGE),
MAKEINTRESOURCE(IDS_WATC_CAPTION),
MB_OK | MB_USERICON,
MAKEINTRESOURCE(IDI_TIMEDATE),
0,
NULL,
0
};
MessageBoxIndirect(&params);
//
// Show time/date page for user to verify.
//
psh.nStartPage = 0;
break;
}
default :
{
//
// Fall out, maybe it's a number...
//
break;
}
}
}
}
if (psh.nStartPage == (UINT)-1)
{
if (cmdline && (*cmdline >= TEXT('0')) && (*cmdline <= TEXT('9')))
{
psh.nStartPage = GetClInt(cmdline);
}
else
{
psh.nStartPage = 0;
}
}
//
// Register our classes.
//
ClockInit(g_hInst);
CalendarInit(g_hInst);
RegisterMapControlStuff(g_hInst);
psh.dwSize = sizeof(psh);
if (g_bFirstBoot)
{
//
// Disable Apply button for first boot.
//
psh.dwFlags = PSH_PROPTITLE | PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW;
}
else
{
psh.dwFlags = PSH_PROPTITLE | PSH_PROPSHEETPAGE;
}
psh.hwndParent = hwnd;
psh.hInstance = g_hInst;
psh.pszIcon = NULL;
//
// psh.nStartPage is set above.
//
psh.pszCaption = MAKEINTRESOURCE(IDS_TIMEDATE);
psh.nPages = 2;
psh.ppsp = apsp;
apsp[0].dwSize = sizeof(PROPSHEETPAGE);
apsp[0].dwFlags = PSP_DEFAULT;
apsp[0].hInstance = g_hInst;
apsp[0].pszTemplate = wMaxDigitWidth > 8 ? MAKEINTRESOURCE(DLG_DATETIMEWIDE) : MAKEINTRESOURCE(DLG_DATETIME);
apsp[0].pfnDlgProc = DateTimeDlgProc;
apsp[0].lParam = 0;
apsp[1].dwSize = sizeof(PROPSHEETPAGE);
apsp[1].dwFlags = PSP_DEFAULT;
apsp[1].hInstance = g_hInst;
apsp[1].pszTemplate = MAKEINTRESOURCE(DLG_TIMEZONE);
apsp[1].pfnDlgProc = TimeZoneDlgProc;
apsp[1].lParam = 0;
if (psh.nStartPage >= psh.nPages)
{
psh.nStartPage = 0;
}
// We use the HyperLink control and that requires OLE (for IAccessible)
hrOle = CoInitialize(0);
fReturn = (BOOL)PropertySheet(&psh);
if (SUCCEEDED(hrOle))
{
CoUninitialize();
}
return fReturn;
}