mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
1123 lines
30 KiB
C
1123 lines
30 KiB
C
/*
|
|
* PROJECT: ReactOS Timedate Control Panel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: dll/cpl/timedate/monthcal.c
|
|
* PURPOSE: Calander implementation
|
|
* COPYRIGHT: Copyright 2006 Thomas Weidenmueller <w3seek@reactos.com>
|
|
*
|
|
*/
|
|
|
|
#include "timedate.h"
|
|
|
|
#include <windowsx.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;
|
|
}
|
|
|
|
/*
|
|
* For the year range 1..9999
|
|
* return 1 if is leap year otherwise 0
|
|
*/
|
|
static WORD LeapYear(IN WORD Year)
|
|
{
|
|
return
|
|
#ifdef WITH_1752
|
|
(Year <= 1752) ? !(Year % 4) :
|
|
#endif
|
|
!(Year % 4) && ((Year % 100) || !(Year % 400));
|
|
}
|
|
|
|
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] + LeapYear(Year);
|
|
else
|
|
{
|
|
#ifdef WITH_1752
|
|
if ((Year == 1752) && (Month == 9))
|
|
return 19; // Special case: September 1752 has no 3rd-13th
|
|
else
|
|
#endif
|
|
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 < 1899 || Year > 9999)
|
|
{
|
|
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);
|
|
if (MonthCalGetDayRect(infoPtr,
|
|
sc.NewDay,
|
|
&rcUpdate))
|
|
{
|
|
InvalidateRect(infoPtr->hSelf,
|
|
&rcUpdate,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
Ret = TRUE;
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static VOID
|
|
MonthCalSetLocalTime(IN PMONTHCALWND infoPtr,
|
|
OUT SYSTEMTIME *Time)
|
|
{
|
|
NMMCCAUTOUPDATE au;
|
|
SYSTEMTIME LocalTime = {0};
|
|
|
|
GetLocalTime(&LocalTime);
|
|
|
|
au.SystemTime = LocalTime;
|
|
if (!MonthCalNotifyControlParent(infoPtr,
|
|
MCCN_AUTOUPDATE,
|
|
&au))
|
|
{
|
|
if (MonthCalSetDate(infoPtr,
|
|
LocalTime.wDay,
|
|
LocalTime.wMonth,
|
|
LocalTime.wYear))
|
|
{
|
|
infoPtr->Changed = FALSE;
|
|
}
|
|
}
|
|
|
|
/* Kill the day timer */
|
|
if (infoPtr->DayTimerSet)
|
|
{
|
|
KillTimer(infoPtr->hSelf,
|
|
ID_DAYTIMER);
|
|
infoPtr->DayTimerSet = FALSE;
|
|
}
|
|
|
|
/* Setup the new day timer */
|
|
MonthCalSetupDayTimer(infoPtr);
|
|
|
|
if (Time != NULL)
|
|
{
|
|
*Time = LocalTime;
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
MonthCalRepaintDay(IN PMONTHCALWND infoPtr,
|
|
IN WORD Day)
|
|
{
|
|
RECT rcCell;
|
|
|
|
if (MonthCalGetDayRect(infoPtr,
|
|
Day,
|
|
&rcCell))
|
|
{
|
|
InvalidateRect(infoPtr->hSelf,
|
|
&rcCell,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
MonthCalPaint(IN PMONTHCALWND infoPtr,
|
|
IN HDC hDC,
|
|
IN LPRECT prcUpdate)
|
|
{
|
|
LONG x, y;
|
|
RECT rcCell;
|
|
COLORREF crOldText, crOldCtrlText = CLR_INVALID;
|
|
HFONT hOldFont;
|
|
INT iOldBkMode;
|
|
|
|
#if MONTHCAL_CTRLBG != MONTHCAL_DISABLED_CTRLBG
|
|
if (!infoPtr->Enabled)
|
|
{
|
|
FillRect(hDC,
|
|
prcUpdate,
|
|
GetSysColorBrush(MONTHCAL_DISABLED_CTRLBG));
|
|
}
|
|
#endif
|
|
|
|
iOldBkMode = SetBkMode(hDC,
|
|
TRANSPARENT);
|
|
hOldFont = (HFONT)SelectObject(hDC,
|
|
infoPtr->hFont);
|
|
|
|
for (y = prcUpdate->top / infoPtr->CellSize.cy;
|
|
y <= prcUpdate->bottom / infoPtr->CellSize.cy && y < 7;
|
|
y++)
|
|
{
|
|
rcCell.top = y * infoPtr->CellSize.cy;
|
|
rcCell.bottom = rcCell.top + infoPtr->CellSize.cy;
|
|
|
|
if (y == 0)
|
|
{
|
|
RECT rcHeader;
|
|
|
|
/* Paint the header */
|
|
rcHeader.left = prcUpdate->left;
|
|
rcHeader.top = rcCell.top;
|
|
rcHeader.right = prcUpdate->right;
|
|
rcHeader.bottom = rcCell.bottom;
|
|
|
|
FillRect(hDC,
|
|
&rcHeader,
|
|
infoPtr->hbHeader);
|
|
|
|
crOldText = SetTextColor(hDC,
|
|
GetSysColor(infoPtr->Enabled ? MONTHCAL_HEADERFG : MONTHCAL_DISABLED_HEADERFG));
|
|
|
|
for (x = prcUpdate->left / infoPtr->CellSize.cx;
|
|
x <= prcUpdate->right / infoPtr->CellSize.cx && x < 7;
|
|
x++)
|
|
{
|
|
rcCell.left = x * infoPtr->CellSize.cx;
|
|
rcCell.right = rcCell.left + infoPtr->CellSize.cx;
|
|
|
|
/* Write the first letter of each weekday */
|
|
DrawTextW(hDC,
|
|
&infoPtr->Week[x],
|
|
1,
|
|
&rcCell,
|
|
DT_SINGLELINE | DT_NOPREFIX | DT_CENTER | DT_VCENTER);
|
|
}
|
|
|
|
SetTextColor(hDC,
|
|
crOldText);
|
|
}
|
|
else
|
|
{
|
|
if (crOldCtrlText == CLR_INVALID)
|
|
{
|
|
crOldCtrlText = SetTextColor(hDC,
|
|
GetSysColor(infoPtr->Enabled ? MONTHCAL_CTRLFG : MONTHCAL_DISABLED_CTRLFG));
|
|
}
|
|
|
|
for (x = prcUpdate->left / infoPtr->CellSize.cx;
|
|
x <= prcUpdate->right / infoPtr->CellSize.cx && x < 7;
|
|
x++)
|
|
{
|
|
UINT Day = infoPtr->Days[y - 1][x];
|
|
|
|
rcCell.left = x * infoPtr->CellSize.cx;
|
|
rcCell.right = rcCell.left + infoPtr->CellSize.cx;
|
|
|
|
/* Write the day number */
|
|
if (Day != 0 && Day < 100)
|
|
{
|
|
WCHAR szDay[3];
|
|
INT szDayLen;
|
|
RECT rcText;
|
|
SIZE TextSize;
|
|
|
|
szDayLen = swprintf(szDay,
|
|
L"%lu",
|
|
Day);
|
|
|
|
if (GetTextExtentPoint32W(hDC,
|
|
szDay,
|
|
szDayLen,
|
|
&TextSize))
|
|
{
|
|
RECT rcHighlight = { 0, 0, 0, 0 };
|
|
|
|
rcText.left = rcCell.left + (infoPtr->CellSize.cx / 2) - (TextSize.cx / 2);
|
|
rcText.top = rcCell.top + (infoPtr->CellSize.cy / 2) - (TextSize.cy / 2);
|
|
rcText.right = rcText.left + TextSize.cx;
|
|
rcText.bottom = rcText.top + TextSize.cy;
|
|
|
|
if (Day == infoPtr->Day)
|
|
{
|
|
SIZE TextSel;
|
|
|
|
TextSel.cx = (infoPtr->CellSize.cx * 2) / 3;
|
|
TextSel.cy = (infoPtr->CellSize.cy * 3) / 4;
|
|
|
|
if (TextSel.cx < rcText.right - rcText.left)
|
|
TextSel.cx = rcText.right - rcText.left;
|
|
if (TextSel.cy < rcText.bottom - rcText.top)
|
|
TextSel.cy = rcText.bottom - rcText.top;
|
|
|
|
rcHighlight.left = rcCell.left + (infoPtr->CellSize.cx / 2) - (TextSel.cx / 2);
|
|
rcHighlight.right = rcHighlight.left + TextSel.cx;
|
|
rcHighlight.top = rcCell.top + (infoPtr->CellSize.cy / 2) - (TextSel.cy / 2);
|
|
rcHighlight.bottom = rcHighlight.top + TextSel.cy;
|
|
|
|
InflateRect(&rcHighlight,
|
|
GetSystemMetrics(SM_CXFOCUSBORDER),
|
|
GetSystemMetrics(SM_CYFOCUSBORDER));
|
|
|
|
if (!FillRect(hDC,
|
|
&rcHighlight,
|
|
infoPtr->hbSelection))
|
|
{
|
|
goto FailNoHighlight;
|
|
}
|
|
|
|
/* Highlight the selected day */
|
|
crOldText = SetTextColor(hDC,
|
|
GetSysColor(infoPtr->Enabled ? MONTHCAL_SELFG : MONTHCAL_DISABLED_SELFG));
|
|
}
|
|
else
|
|
{
|
|
FailNoHighlight:
|
|
/* Don't change the text color, we're not highlighting it... */
|
|
crOldText = CLR_INVALID;
|
|
}
|
|
|
|
TextOutW(hDC,
|
|
rcText.left,
|
|
rcText.top,
|
|
szDay,
|
|
szDayLen);
|
|
|
|
if (Day == infoPtr->Day && crOldText != CLR_INVALID)
|
|
{
|
|
if (infoPtr->HasFocus && infoPtr->Enabled && !(infoPtr->UIState & UISF_HIDEFOCUS))
|
|
{
|
|
COLORREF crOldBk;
|
|
|
|
crOldBk = SetBkColor(hDC,
|
|
GetSysColor(infoPtr->Enabled ? MONTHCAL_SELBG : MONTHCAL_DISABLED_SELBG));
|
|
|
|
DrawFocusRect(hDC,
|
|
&rcHighlight);
|
|
|
|
SetBkColor(hDC,
|
|
crOldBk);
|
|
}
|
|
|
|
SetTextColor(hDC,
|
|
crOldText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (crOldCtrlText != CLR_INVALID)
|
|
{
|
|
SetTextColor(hDC,
|
|
crOldCtrlText);
|
|
}
|
|
|
|
SetBkMode(hDC,
|
|
iOldBkMode);
|
|
SelectObject(hDC,
|
|
(HGDIOBJ)hOldFont);
|
|
}
|
|
|
|
static HFONT
|
|
MonthCalChangeFont(IN PMONTHCALWND infoPtr,
|
|
IN HFONT hFont,
|
|
IN BOOL Redraw)
|
|
{
|
|
HFONT hOldFont = infoPtr->hFont;
|
|
infoPtr->hFont = hFont;
|
|
|
|
if (Redraw)
|
|
{
|
|
InvalidateRect(infoPtr->hSelf,
|
|
NULL,
|
|
TRUE);
|
|
}
|
|
|
|
return hOldFont;
|
|
}
|
|
|
|
static WORD
|
|
MonthCalPtToDay(IN PMONTHCALWND infoPtr,
|
|
IN INT x,
|
|
IN INT y)
|
|
{
|
|
WORD Ret = 0;
|
|
|
|
if (infoPtr->CellSize.cx != 0 && infoPtr->CellSize.cy != 0 &&
|
|
x >= 0 && y >= 0)
|
|
{
|
|
x /= infoPtr->CellSize.cx;
|
|
y /= infoPtr->CellSize.cy;
|
|
|
|
if (x < 7 && y != 0 && y < 7)
|
|
{
|
|
Ret = (WORD)infoPtr->Days[y - 1][x];
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static LRESULT CALLBACK
|
|
MonthCalWndProc(IN HWND hwnd,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam)
|
|
{
|
|
PMONTHCALWND infoPtr;
|
|
LRESULT Ret = 0;
|
|
|
|
infoPtr = (PMONTHCALWND)GetWindowLongPtrW(hwnd,
|
|
0);
|
|
|
|
if (infoPtr == NULL && uMsg != WM_CREATE)
|
|
{
|
|
goto HandleDefaultMessage;
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
#if MONTHCAL_CTRLBG != MONTHCAL_DISABLED_CTRLBG
|
|
case WM_ERASEBKGND:
|
|
Ret = !infoPtr->Enabled;
|
|
break;
|
|
#endif
|
|
|
|
case WM_PAINT:
|
|
case WM_PRINTCLIENT:
|
|
{
|
|
if (infoPtr->CellSize.cx != 0 && infoPtr->CellSize.cy != 0)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hDC;
|
|
|
|
if (wParam != 0)
|
|
{
|
|
if (!GetUpdateRect(hwnd,
|
|
&ps.rcPaint,
|
|
TRUE))
|
|
{
|
|
break;
|
|
}
|
|
hDC = (HDC)wParam;
|
|
}
|
|
else
|
|
{
|
|
hDC = BeginPaint(hwnd,
|
|
&ps);
|
|
if (hDC == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
MonthCalPaint(infoPtr,
|
|
hDC,
|
|
&ps.rcPaint);
|
|
|
|
if (wParam == 0)
|
|
{
|
|
EndPaint(hwnd,
|
|
&ps);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
WORD SelDay;
|
|
|
|
SelDay = MonthCalPtToDay(infoPtr,
|
|
GET_X_LPARAM(lParam),
|
|
GET_Y_LPARAM(lParam));
|
|
if (SelDay != 0 && SelDay != infoPtr->Day)
|
|
{
|
|
MonthCalSetDate(infoPtr,
|
|
SelDay,
|
|
infoPtr->Month,
|
|
infoPtr->Year);
|
|
}
|
|
|
|
/* Fall through */
|
|
}
|
|
|
|
case WM_MBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
{
|
|
if (!infoPtr->HasFocus)
|
|
{
|
|
SetFocus(hwnd);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_KEYDOWN:
|
|
{
|
|
WORD NewDay = 0;
|
|
|
|
switch (wParam)
|
|
{
|
|
case VK_UP:
|
|
{
|
|
if (infoPtr->Day > 7)
|
|
{
|
|
NewDay = infoPtr->Day - 7;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case VK_DOWN:
|
|
{
|
|
if (infoPtr->Day + 7 <= MonthCalMonthLength(infoPtr->Month,
|
|
infoPtr->Year))
|
|
{
|
|
NewDay = infoPtr->Day + 7;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case VK_LEFT:
|
|
{
|
|
if (infoPtr->Day > 1)
|
|
{
|
|
NewDay = infoPtr->Day - 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case VK_RIGHT:
|
|
{
|
|
if (infoPtr->Day < MonthCalMonthLength(infoPtr->Month,
|
|
infoPtr->Year))
|
|
{
|
|
NewDay = infoPtr->Day + 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Update the selection */
|
|
if (NewDay != 0)
|
|
{
|
|
MonthCalSetDate(infoPtr,
|
|
NewDay,
|
|
infoPtr->Month,
|
|
infoPtr->Year);
|
|
}
|
|
|
|
goto HandleDefaultMessage;
|
|
}
|
|
|
|
case WM_GETDLGCODE:
|
|
{
|
|
INT virtKey;
|
|
|
|
virtKey = (lParam != 0 ? (INT)((LPMSG)lParam)->wParam : 0);
|
|
switch (virtKey)
|
|
{
|
|
case VK_TAB:
|
|
{
|
|
/* Change the UI status */
|
|
SendMessageW(GetAncestor(hwnd,
|
|
GA_PARENT),
|
|
WM_CHANGEUISTATE,
|
|
MAKEWPARAM(UIS_INITIALIZE,
|
|
0),
|
|
0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ret |= DLGC_WANTARROWS;
|
|
break;
|
|
}
|
|
|
|
case WM_SETFOCUS:
|
|
{
|
|
infoPtr->HasFocus = TRUE;
|
|
MonthCalRepaintDay(infoPtr,
|
|
infoPtr->Day);
|
|
break;
|
|
}
|
|
|
|
case WM_KILLFOCUS:
|
|
{
|
|
infoPtr->HasFocus = FALSE;
|
|
MonthCalRepaintDay(infoPtr,
|
|
infoPtr->Day);
|
|
break;
|
|
}
|
|
|
|
case WM_UPDATEUISTATE:
|
|
{
|
|
DWORD OldUIState;
|
|
|
|
Ret = DefWindowProcW(hwnd,
|
|
uMsg,
|
|
wParam,
|
|
lParam);
|
|
|
|
OldUIState = infoPtr->UIState;
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case UIS_SET:
|
|
infoPtr->UIState |= HIWORD(wParam);
|
|
break;
|
|
|
|
case UIS_CLEAR:
|
|
infoPtr->UIState &= ~HIWORD(wParam);
|
|
break;
|
|
}
|
|
|
|
if (infoPtr->UIState != OldUIState)
|
|
{
|
|
MonthCalRepaintDay(infoPtr,
|
|
infoPtr->Day);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MCCM_SETDATE:
|
|
{
|
|
WORD Day, Month, Year, DaysCount;
|
|
|
|
Day = LOWORD(wParam);
|
|
Month = HIWORD(wParam);
|
|
Year = LOWORD(lParam);
|
|
|
|
if (Day == (WORD)-1)
|
|
Day = infoPtr->Day;
|
|
if (Month == (WORD)-1)
|
|
Month = infoPtr->Month;
|
|
if (Year == (WORD)-1)
|
|
Year = infoPtr->Year;
|
|
|
|
DaysCount = MonthCalMonthLength(Month,
|
|
Year);
|
|
if (Day > DaysCount)
|
|
Day = DaysCount;
|
|
|
|
if (MonthCalValidDate(Day,
|
|
Month,
|
|
Year))
|
|
{
|
|
if (Day != infoPtr->Day ||
|
|
Month != infoPtr->Month ||
|
|
Year != infoPtr->Year)
|
|
{
|
|
Ret = MonthCalSetDate(infoPtr,
|
|
Day,
|
|
Month,
|
|
Year);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MCCM_GETDATE:
|
|
{
|
|
LPSYSTEMTIME lpSystemTime = (LPSYSTEMTIME)wParam;
|
|
|
|
lpSystemTime->wYear = infoPtr->Year;
|
|
lpSystemTime->wMonth = infoPtr->Month;
|
|
lpSystemTime->wDay = infoPtr->Day;
|
|
|
|
Ret = TRUE;
|
|
break;
|
|
}
|
|
|
|
case MCCM_RESET:
|
|
{
|
|
MonthCalSetLocalTime(infoPtr,
|
|
NULL);
|
|
Ret = TRUE;
|
|
break;
|
|
}
|
|
|
|
case MCCM_CHANGED:
|
|
{
|
|
Ret = infoPtr->Changed;
|
|
break;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case ID_DAYTIMER:
|
|
{
|
|
/* Kill the timer */
|
|
KillTimer(hwnd,
|
|
ID_DAYTIMER);
|
|
infoPtr->DayTimerSet = FALSE;
|
|
|
|
if (!infoPtr->Changed)
|
|
{
|
|
/* Update the system time and setup the new day timer */
|
|
MonthCalSetLocalTime(infoPtr,
|
|
NULL);
|
|
|
|
/* Update the control */
|
|
MonthCalUpdate(infoPtr);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_SETFONT:
|
|
{
|
|
Ret = (LRESULT)MonthCalChangeFont(infoPtr,
|
|
(HFONT)wParam,
|
|
(BOOL)LOWORD(lParam));
|
|
break;
|
|
}
|
|
|
|
case WM_SIZE:
|
|
{
|
|
infoPtr->ClientSize.cx = LOWORD(lParam);
|
|
infoPtr->ClientSize.cy = HIWORD(lParam);
|
|
infoPtr->CellSize.cx = infoPtr->ClientSize.cx / 7;
|
|
infoPtr->CellSize.cy = infoPtr->ClientSize.cy / 7;
|
|
|
|
/* Repaint the control */
|
|
InvalidateRect(hwnd,
|
|
NULL,
|
|
TRUE);
|
|
break;
|
|
}
|
|
|
|
case WM_GETFONT:
|
|
{
|
|
Ret = (LRESULT)infoPtr->hFont;
|
|
break;
|
|
}
|
|
|
|
case WM_ENABLE:
|
|
{
|
|
infoPtr->Enabled = ((BOOL)wParam != FALSE);
|
|
MonthCalReload(infoPtr);
|
|
break;
|
|
}
|
|
|
|
case WM_STYLECHANGED:
|
|
{
|
|
if (wParam == GWL_STYLE)
|
|
{
|
|
unsigned int OldEnabled = infoPtr->Enabled;
|
|
infoPtr->Enabled = !(((LPSTYLESTRUCT)lParam)->styleNew & WS_DISABLED);
|
|
|
|
if (OldEnabled != infoPtr->Enabled)
|
|
{
|
|
MonthCalReload(infoPtr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_CREATE:
|
|
{
|
|
infoPtr = (MONTHCALWND*) HeapAlloc(GetProcessHeap(),
|
|
0,
|
|
sizeof(MONTHCALWND));
|
|
if (infoPtr == NULL)
|
|
{
|
|
Ret = (LRESULT)-1;
|
|
break;
|
|
}
|
|
|
|
SetWindowLongPtrW(hwnd,
|
|
0,
|
|
(LONG_PTR)infoPtr);
|
|
|
|
ZeroMemory(infoPtr,
|
|
sizeof(MONTHCALWND));
|
|
|
|
infoPtr->hSelf = hwnd;
|
|
infoPtr->hNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
|
|
infoPtr->Enabled = !(((LPCREATESTRUCTW)lParam)->style & WS_DISABLED);
|
|
|
|
MonthCalSetLocalTime(infoPtr,
|
|
NULL);
|
|
|
|
MonthCalReload(infoPtr);
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
HeapFree(GetProcessHeap(),
|
|
0,
|
|
infoPtr);
|
|
SetWindowLongPtrW(hwnd,
|
|
0,
|
|
(DWORD_PTR)NULL);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
HandleDefaultMessage:
|
|
Ret = DefWindowProcW(hwnd,
|
|
uMsg,
|
|
wParam,
|
|
lParam);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
BOOL
|
|
RegisterMonthCalControl(IN HINSTANCE hInstance)
|
|
{
|
|
WNDCLASSW wc = {0};
|
|
|
|
wc.style = CS_DBLCLKS;
|
|
wc.lpfnWndProc = MonthCalWndProc;
|
|
wc.cbWndExtra = sizeof(PMONTHCALWND);
|
|
wc.hInstance = hInstance;
|
|
wc.hCursor = LoadCursorW(NULL,
|
|
(LPWSTR)IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(MONTHCAL_CTRLBG + 1);
|
|
wc.lpszClassName = szMonthCalWndClass;
|
|
|
|
return RegisterClassW(&wc) != 0;
|
|
}
|
|
|
|
VOID
|
|
UnregisterMonthCalControl(IN HINSTANCE hInstance)
|
|
{
|
|
UnregisterClassW(szMonthCalWndClass,
|
|
hInstance);
|
|
}
|