📄 monthcal.c
字号:
/*
* PROJECT: ReactOS Timedate Control Panel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: lib/cpl/timedate/monthcal.c
* PURPOSE: Calander implementation
* COPYRIGHT: Copyright 2006 Thomas Weidenmueller <w3seek@reactos.com>
*
*/
#include <timedate.h>
static const WCHAR szMonthCalWndClass[] = L"MonthCalWnd";
#define MONTHCAL_HEADERBG COLOR_INACTIVECAPTION
#define MONTHCAL_HEADERFG COLOR_INACTIVECAPTIONTEXT
#define MONTHCAL_CTRLBG COLOR_WINDOW
#define MONTHCAL_CTRLFG COLOR_WINDOWTEXT
#define MONTHCAL_SELBG COLOR_ACTIVECAPTION
#define MONTHCAL_SELFG COLOR_CAPTIONTEXT
#define MONTHCAL_DISABLED_HEADERBG COLOR_INACTIVECAPTION
#define MONTHCAL_DISABLED_HEADERFG COLOR_INACTIVECAPTIONTEXT
#define MONTHCAL_DISABLED_CTRLBG COLOR_WINDOW
#define MONTHCAL_DISABLED_CTRLFG COLOR_WINDOWTEXT
#define MONTHCAL_DISABLED_SELBG COLOR_INACTIVECAPTION
#define MONTHCAL_DISABLED_SELFG COLOR_INACTIVECAPTIONTEXT
#define ID_DAYTIMER 1
typedef struct _MONTHCALWND
{
HWND hSelf;
HWND hNotify;
WORD Day;
WORD Month;
WORD Year;
WORD FirstDayOfWeek;
BYTE Days[6][7];
WCHAR Week[7];
SIZE CellSize;
SIZE ClientSize;
HFONT hFont;
HBRUSH hbHeader;
HBRUSH hbSelection;
DWORD UIState;
UINT Changed : 1;
UINT DayTimerSet : 1;
UINT Enabled : 1;
UINT HasFocus : 1;
} MONTHCALWND, *PMONTHCALWND;
static LRESULT
MonthCalNotifyControlParent(IN PMONTHCALWND infoPtr,
IN UINT code,
IN OUT PVOID data)
{
LRESULT Ret = 0;
if (infoPtr->hNotify != NULL)
{
LPNMHDR pnmh = (LPNMHDR)data;
pnmh->hwndFrom = infoPtr->hSelf;
pnmh->idFrom = GetWindowLongPtrW(infoPtr->hSelf,
GWLP_ID);
pnmh->code = code;
Ret = SendMessageW(infoPtr->hNotify,
WM_NOTIFY,
(WPARAM)pnmh->idFrom,
(LPARAM)pnmh);
}
return Ret;
}
static WORD
MonthCalMonthLength(IN WORD Month,
IN WORD Year)
{
const BYTE MonthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
if(Month == 2)
return MonthDays[Month - 1] + ((Year % 400 == 0) ? 1 : ((Year % 100 != 0) && (Year % 4 == 0)) ? 1 : 0);
else
return MonthDays[Month - 1];
}
static WORD
MonthCalWeekInMonth(IN WORD Day,
IN WORD DayOfWeek)
{
return (Day - DayOfWeek + 5) / 7;
}
static WORD
MonthCalDayOfWeek(IN PMONTHCALWND infoPtr,
IN WORD Day,
IN WORD Month,
IN WORD Year)
{
const BYTE DayOfWeek[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
WORD Ret;
Year -= (Month < 3);
Ret = (Year + (Year / 4) - (Year / 100) + (Year / 400) + DayOfWeek[Month - 1] + Day + 6) % 7;
return (7 + Ret - infoPtr->FirstDayOfWeek) % 7;
}
static WORD
MonthCalFirstDayOfWeek(VOID)
{
WCHAR szBuf[2] = {0};
WORD Ret = 0;
if (GetLocaleInfoW(LOCALE_USER_DEFAULT,
LOCALE_IFIRSTDAYOFWEEK,
szBuf,
sizeof(szBuf) / sizeof(szBuf[0])) != 0)
{
Ret = (WORD)(szBuf[0] - TEXT('0'));
}
return Ret;
}
static BOOL
MonthCalValidDate(IN WORD Day,
IN WORD Month,
IN WORD Year)
{
if (Month < 1 || Month > 12 ||
Day == 0 || Day > MonthCalMonthLength(Month,
Year) ||
Year < 1980 || Year > 2099)
{
return FALSE;
}
return TRUE;
}
static VOID
MonthCalUpdate(IN PMONTHCALWND infoPtr)
{
PBYTE pDay, pDayEnd;
WORD DayOfWeek, MonthLength, d = 0;
SIZE NewCellSize;
BOOL RepaintHeader = FALSE;
NewCellSize.cx = infoPtr->ClientSize.cx / 7;
NewCellSize.cy = infoPtr->ClientSize.cy / 7;
if (infoPtr->CellSize.cx != NewCellSize.cx ||
infoPtr->CellSize.cy != NewCellSize.cy)
{
infoPtr->CellSize = NewCellSize;
RepaintHeader = TRUE;
}
/* update the days layout of the current month */
ZeroMemory(infoPtr->Days,
sizeof(infoPtr->Days));
DayOfWeek = MonthCalDayOfWeek(infoPtr,
1,
infoPtr->Month,
infoPtr->Year);
MonthLength = MonthCalMonthLength(infoPtr->Month,
infoPtr->Year);
pDay = &infoPtr->Days[0][DayOfWeek];
pDayEnd = pDay + MonthLength;
while (pDay != pDayEnd)
{
*(pDay++) = (BYTE)++d;
}
/* repaint the control */
if (RepaintHeader)
{
InvalidateRect(infoPtr->hSelf,
NULL,
TRUE);
}
else
{
RECT rcClient;
rcClient.left = 0;
rcClient.top = infoPtr->CellSize.cy;
rcClient.right = infoPtr->ClientSize.cx;
rcClient.bottom = infoPtr->ClientSize.cy;
InvalidateRect(infoPtr->hSelf,
&rcClient,
TRUE);
}
}
static VOID
MonthCalSetupDayTimer(IN PMONTHCALWND infoPtr)
{
SYSTEMTIME LocalTime = {0};
UINT uElapse;
/* update the current date */
GetLocalTime(&LocalTime);
/* calculate the number of remaining milliseconds until midnight */
uElapse = 1000 - (UINT)LocalTime.wMilliseconds;
uElapse += (59 - (UINT)LocalTime.wSecond) * 1000;
uElapse += (59 - (UINT)LocalTime.wMinute) * 60 * 1000;
uElapse += (23 - (UINT)LocalTime.wHour) * 60 * 60 * 1000;
if (uElapse < USER_TIMER_MINIMUM || uElapse > USER_TIMER_MAXIMUM)
uElapse = 1000;
else
uElapse += 100; /* Add a delay of 0.1 seconds */
/* setup the new timer */
if (SetTimer(infoPtr->hSelf,
ID_DAYTIMER,
uElapse,
NULL) != 0)
{
infoPtr->DayTimerSet = TRUE;
}
}
static VOID
MonthCalReload(IN PMONTHCALWND infoPtr)
{
WCHAR szBuf[64];
UINT i;
infoPtr->UIState = (DWORD)SendMessageW(GetAncestor(infoPtr->hSelf,
GA_PARENT),
WM_QUERYUISTATE,
0,
0);
/* cache the configuration */
infoPtr->FirstDayOfWeek = MonthCalFirstDayOfWeek();
infoPtr->hbHeader = GetSysColorBrush(infoPtr->Enabled ? MONTHCAL_HEADERBG : MONTHCAL_DISABLED_HEADERBG);
infoPtr->hbSelection = GetSysColorBrush(infoPtr->Enabled ? MONTHCAL_SELBG : MONTHCAL_DISABLED_SELBG);
for (i = 0; i < 7; i++)
{
if (GetLocaleInfoW(LOCALE_USER_DEFAULT,
LOCALE_SABBREVDAYNAME1 +
((i + infoPtr->FirstDayOfWeek) % 7),
szBuf,
sizeof(szBuf) / sizeof(szBuf[0])) != 0)
{
infoPtr->Week[i] = szBuf[0];
}
}
/* update the control */
MonthCalUpdate(infoPtr);
}
static BOOL
MonthCalGetDayRect(IN PMONTHCALWND infoPtr,
IN WORD Day,
OUT RECT *rcCell)
{
if (Day >= 1 && Day <= MonthCalMonthLength(infoPtr->Month,
infoPtr->Year))
{
WORD DayOfWeek;
DayOfWeek = MonthCalDayOfWeek(infoPtr,
Day,
infoPtr->Month,
infoPtr->Year);
rcCell->left = DayOfWeek * infoPtr->CellSize.cx;
rcCell->top = (MonthCalWeekInMonth(Day,
DayOfWeek) + 1) * infoPtr->CellSize.cy;
rcCell->right = rcCell->left + infoPtr->CellSize.cx;
rcCell->bottom = rcCell->top + infoPtr->CellSize.cy;
return TRUE;
}
return FALSE;
}
static VOID
MonthCalChange(IN PMONTHCALWND infoPtr)
{
infoPtr->Changed = TRUE;
/* kill the day timer */
if (infoPtr->DayTimerSet)
{
KillTimer(infoPtr->hSelf,
ID_DAYTIMER);
infoPtr->DayTimerSet = FALSE;
}
}
static BOOL
MonthCalSetDate(IN PMONTHCALWND infoPtr,
IN WORD Day,
IN WORD Month,
IN WORD Year)
{
NMMCCSELCHANGE sc;
BOOL Ret = FALSE;
sc.OldDay = infoPtr->Day;
sc.OldMonth = infoPtr->Month;
sc.OldYear = infoPtr->Year;
sc.NewDay = Day;
sc.NewMonth = Month;
sc.NewYear = Year;
/* notify the parent */
if (!MonthCalNotifyControlParent(infoPtr,
MCCN_SELCHANGE,
&sc))
{
/* check if we actually need to update */
if (infoPtr->Month != sc.NewMonth ||
infoPtr->Year != sc.NewYear)
{
infoPtr->Day = sc.NewDay;
infoPtr->Month = sc.NewMonth;
infoPtr->Year = sc.NewYear;
MonthCalChange(infoPtr);
/* repaint the entire control */
MonthCalUpdate(infoPtr);
Ret = TRUE;
}
else if (infoPtr->Day != sc.NewDay)
{
RECT rcUpdate;
infoPtr->Day = sc.NewDay;
MonthCalChange(infoPtr);
if (MonthCalGetDayRect(infoPtr,
sc.OldDay,
&rcUpdate))
{
/* repaint the day cells that need to be updated */
InvalidateRect(infoPtr->hSelf,
&rcUpdate,
TRUE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -