|
|
/*++
Copyright (c) 1994-1998, Microsoft Corporation All rights reserved.
Module Name:
calendar.c
Abstract:
This module implements the calendar control for the Date/Time applet.
Revision History:
--*/
//
// Include Files.
//
#include "timedate.h"
#include "rc.h"
//
// Constant Declarations.
//
#define DEF_FIRST_WEEKDAY (6)
#define cBorderX 5
#define cBorderY 3
#define cBorderSelect 1
#define IS_FE_LANGUAGE(p) (((p) == LANG_CHINESE) || \
((p) == LANG_JAPANESE) || \ ((p) == LANG_KOREAN))
//
// Typedef Declarations.
//
//
// Struture for global data.
//
typedef struct _CALINFO { HWND hwnd; // the hwnd
HFONT hfontCal; // the font to use
BOOL fFocus; // do we have the focus
int cxBlank; // size of a blank
int cxChar; // the width of digits
int cyChar; // the height of digits
} CALINFO, *PCALINFO;
////////////////////////////////////////////////////////////////////////////
//
// GetFirstDayOfAnyWeek
//
// For this function ONLY:
// 0 = Monday
// 6 = Sunday
//
////////////////////////////////////////////////////////////////////////////
int GetFirstDayOfAnyWeek() { static int iDay = -1;
if (iDay < 0) { TCHAR ch[2] = { 0 };
*ch = TEXT('0') + DEF_FIRST_WEEKDAY;
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, ch, 2);
iDay = ( ((*ch >= TEXT('0')) && (*ch <= TEXT('6'))) ? ((int)*ch - TEXT('0')) : DEF_FIRST_WEEKDAY ); }
return (iDay); }
////////////////////////////////////////////////////////////////////////////
//
// GetLocalWeekday
//
////////////////////////////////////////////////////////////////////////////
int GetLocalWeekday() { //
// Convert local first day to 0==sunday and subtract from today.
//
return ((wDateTime[WEEKDAY] + 7 - ((GetFirstDayOfAnyWeek() + 1) % 7)) % 7); }
void DetermineDayOfWeek() { FILETIME FileTime; 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]; SystemTimeToFileTime(&SystemTime, &FileTime); FileTimeToSystemTime(&FileTime, &SystemTime); wDateTime[WEEKDAY] = SystemTime.wDayOfWeek; }
////////////////////////////////////////////////////////////////////////////
//
// GetFirstDayOfTheMonth
//
////////////////////////////////////////////////////////////////////////////
int GetFirstDayOfTheMonth() { DetermineDayOfWeek(); return ((GetLocalWeekday() + 8 - (wDateTime[DAY] % 7)) % 7); }
////////////////////////////////////////////////////////////////////////////
//
// GetDaysOfTheMonth
//
////////////////////////////////////////////////////////////////////////////
int GetDaysOfTheMonth( int iMonth) { int cDays; int nYear;
//
// Calculate the number of days in the current month -
// add one if this is a leap year and the month is February.
//
if (iMonth <= 7) { cDays = 30 + (iMonth % 2); } else { cDays = 31 - (iMonth % 2); }
if (iMonth == 2) { cDays = 28; nYear = wDateTime[YEAR]; if ((nYear % 4 == 0) && ((nYear % 100 != 0) || (nYear % 400 == 0))) { cDays++; } }
return (cDays); }
////////////////////////////////////////////////////////////////////////////
//
// AdjustDeltaDay
//
// Adjust the day part of the current date
//
////////////////////////////////////////////////////////////////////////////
void AdjustDeltaDay( HWND hwnd, int iDay) { GetTime();
if (wDateTime[DAY] != iDay) { wPrevDateTime[DAY] = wDateTime[DAY] = (WORD)iDay; fDateDirty = TRUE;
//
// Let our parent know that we changed.
//
FORWARD_WM_COMMAND( GetParent(hwnd), GetWindowLong(hwnd, GWL_ID), hwnd, CBN_SELCHANGE, SendMessage ); } }
int GetCalendarName(LPTSTR pszName, int cch) { TCHAR szDateString[100]; SYSTEMTIME SystemTime; int cchResult = 0;
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];
if (0 != GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &SystemTime, NULL, szDateString, ARRAYSIZE(szDateString))) { if ( NULL != pszName ) { StringCchCopy( pszName, cch, szDateString ); }
cchResult = lstrlen(szDateString); } return cchResult; }
////////////////////////////////////////////////////////////////////////////
//
// ChangeCurrentDate
//
// If we pass in iNewCol < 0, we simply want to invalidate todays date.
// This is used when we gain and lose focus.
//
////////////////////////////////////////////////////////////////////////////
void ChangeCurrentDate( PCALINFO pci, int iNewCol, int iNewRow) { int iFirstDay, iRow, iColumn; RECT rc, rcT;
GetClientRect(pci->hwnd, &rc); iFirstDay = GetFirstDayOfTheMonth(); iColumn = (wDateTime[DAY] - 1 + iFirstDay) % 7; iRow = 1 + ((wDateTime[DAY] - 1 + iFirstDay) / 7);
rcT.left = (((rc.right - rc.left) * iColumn) / 7) + cBorderX - cBorderSelect; rcT.right = rcT.left + (pci->cxChar * 2) + (2 * cBorderSelect); rcT.top = ((rc.bottom - rc.top) * iRow ) / 7 + cBorderY - cBorderSelect; rcT.bottom = rcT.top + pci->cyChar + (2 * cBorderSelect);
InvalidateRect(pci->hwnd, &rcT, FALSE);
if (iNewCol >= 0) { AdjustDeltaDay(pci->hwnd, ((iNewRow - 1) * 7) + iNewCol + 1 - iFirstDay); } }
////////////////////////////////////////////////////////////////////////////
//
// CalendarPaint
//
////////////////////////////////////////////////////////////////////////////
BOOL CalendarPaint( PCALINFO pci, HWND hwnd) { RECT rc, rcT; PAINTSTRUCT ps; HDC hdc; int iWeek, iWeekDay, iDay, iMaxDays; int iFirstDayOfWeek, iFirstDayOfMonth; TCHAR pszDate[3]; TCHAR szShortDay[25]; DWORD dwbkColor; COLORREF o_TextColor; LCID Locale; LANGID LangID = GetUserDefaultLangID(); BOOL IsFELang = IS_FE_LANGUAGE(PRIMARYLANGID(LangID));
iFirstDayOfMonth = GetFirstDayOfTheMonth(); iMaxDays = GetDaysOfTheMonth(wDateTime[MONTH]); iDay = 1; pszDate[0] = TEXT(' '); pszDate[1] = TEXT('0');
//
// Paint the background of the dates page.
//
hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rc); FillRect(hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
//
// The day specifier.
//
rcT.left = rc.left; rcT.right = rc.right; rcT.top = rc.top; rcT.bottom = rc.top + ((rc.bottom - rc.top) / 7); FillRect(hdc, &rcT, GetSysColorBrush(COLOR_INACTIVECAPTION));
//
// Fill the page.
//
SetBkColor(hdc, GetSysColor(COLOR_WINDOW)); SelectFont(hdc, pci->hfontCal); SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
//
// See if we need to calculate the size of characters.
//
if (pci->cxChar == 0) { DrawText(hdc, TEXT("0"), 1, &rcT, DT_CALCRECT); pci->cxChar = rcT.right - rcT.left; pci->cyChar = rcT.bottom - rcT.top;
DrawText(hdc, TEXT(" "), 1, &rcT, DT_CALCRECT); pci->cxBlank = rcT.right - rcT.left; }
for (iWeek = 1; (iWeek < 7); iWeek++) { for (iWeekDay = iFirstDayOfMonth; (iWeekDay < 7) && (iDay <= iMaxDays); iWeekDay++) { rcT.left = ((((rc.right - rc.left) * iWeekDay) / 7)) + cBorderX; rcT.top = (((rc.bottom - rc.top) * iWeek) / 7) + cBorderY; rcT.right = rcT.left + 20; rcT.bottom = rcT.top + 20;
if (pszDate[1] == TEXT('9')) { pszDate[1] = TEXT('0');
if (pszDate[0] == TEXT(' ')) { pszDate[0] = TEXT('1'); } else { pszDate[0] = pszDate[0] + 1; } } else { pszDate[1] = pszDate[1] + 1; }
if (wDateTime[DAY] == iDay) { dwbkColor = GetBkColor(hdc); SetBkColor(hdc, GetSysColor(COLOR_ACTIVECAPTION)); o_TextColor = GetTextColor(hdc); SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT)); }
ExtTextOut( hdc, rcT.left, rcT.top, 0, &rcT, (LPTSTR)pszDate, 2, NULL );
//
// If we drew it inverted - put it back.
//
if (wDateTime[DAY] == iDay) { //
// If we have the focus we also need to draw the focus
// rectangle for this item.
//
if (pci->fFocus) { rcT.bottom = rcT.top + pci->cyChar;
if (iDay <= 9) { rcT.right = rcT.left + pci->cxChar + pci->cxBlank; } else { rcT.right = rcT.left + 2 * pci->cxChar; }
DrawFocusRect(hdc, &rcT); }
SetBkColor(hdc, dwbkColor); SetTextColor(hdc, o_TextColor); }
iFirstDayOfMonth = 0; iDay++; } }
//
// Set the FONT color for the SMTWTFS line.
//
dwbkColor = SetBkColor(hdc, GetSysColor(COLOR_INACTIVECAPTION)); SetTextColor(hdc, GetSysColor(COLOR_INACTIVECAPTIONTEXT));
iFirstDayOfWeek = GetFirstDayOfAnyWeek();
if (!IsFELang) { //
// Not a FE locale.
//
// If it's Arabic or Syriac, then we want to use the US locale to get the
// first letter of the abbreviated day name to display in the calendar.
//
Locale = ((PRIMARYLANGID(LangID) == LANG_ARABIC) || (PRIMARYLANGID(LangID) == LANG_SYRIAC)) ? MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT) : LOCALE_USER_DEFAULT;
for (iWeekDay = 0; (iWeekDay < 7); iWeekDay++) { GetLocaleInfo( Locale, LOCALE_SABBREVDAYNAME1 + (iWeekDay + iFirstDayOfWeek) % 7, szShortDay, sizeof(szShortDay) / sizeof(TCHAR) );
if (*szShortDay) { *szShortDay = (TCHAR)CharUpper((LPTSTR)(DWORD_PTR)*szShortDay); }
TextOut( hdc, (((rc.right - rc.left) * iWeekDay) / 7) + cBorderX, cBorderY, szShortDay, 1 ); } } else { //
// FE Locale.
//
for (iWeekDay = 0; (iWeekDay < 7); iWeekDay++) { GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1 + (iWeekDay + iFirstDayOfWeek) % 7, szShortDay, sizeof(szShortDay) / sizeof(TCHAR) );
if (*szShortDay) { *szShortDay = (TCHAR)CharUpper((LPTSTR)(DWORD_PTR)*szShortDay); }
if ((PRIMARYLANGID(LangID) == LANG_CHINESE) && (lstrlen(szShortDay) == 3 )) { TextOut( hdc, (((rc.right - rc.left) * iWeekDay) / 7) + cBorderX, cBorderY, (LangID == MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG )) ? szShortDay : szShortDay + 2, 1 ); } else { TextOut( hdc, (((rc.right - rc.left) * iWeekDay) / 7) + cBorderX, cBorderY, szShortDay, lstrlen(szShortDay) ); } } }
SetBkColor(hdc, dwbkColor); EndPaint(hwnd, &ps); return (TRUE); }
////////////////////////////////////////////////////////////////////////////
//
// IsValidClick
//
////////////////////////////////////////////////////////////////////////////
BOOL IsValidClick( HWND hwnd, int x, int y) { int iT;
if (y == 0) { return (FALSE); }
iT = GetFirstDayOfTheMonth();
if ((y == 1) && (x < iT)) { return (FALSE); }
iT += GetDaysOfTheMonth(wDateTime[MONTH]) - 1;
if (y > ((iT / 7) + 1)) { return (FALSE); }
if ((y == ((iT / 7) + 1)) && (x > (iT % 7))) { return (FALSE); }
return (TRUE); }
////////////////////////////////////////////////////////////////////////////
//
// HandleDateChange
//
////////////////////////////////////////////////////////////////////////////
BOOL HandleDateChange( PCALINFO pci, int x, int y) { RECT rc, rcT; int ix, iy;
GetClientRect(pci->hwnd, &rc);
ix = (x * 7) / (rc.right - rc.left); iy = (y * 7) / (rc.bottom - rc.top);
if (IsValidClick(pci->hwnd, ix, iy)) { rcT.left = (((rc.right - rc.left) * ix)/ 7) + cBorderX - cBorderSelect; rcT.right = rcT.left + (2 * pci->cxChar) + (2 * cBorderSelect); rcT.top = ((rc.bottom - rc.top) * iy) / 7 + cBorderY - cBorderSelect; rcT.bottom = rcT.top + pci->cyChar + (2 * cBorderSelect);
InvalidateRect(pci->hwnd, &rcT, FALSE); ChangeCurrentDate( pci, (x * 7) / (rc.right - rc.left), (y * 7) / (rc.bottom - rc.top) );
NotifyWinEvent(EVENT_OBJECT_NAMECHANGE , pci->hwnd, OBJID_WINDOW, CHILDID_SELF); return (TRUE); } else { return (FALSE); } }
////////////////////////////////////////////////////////////////////////////
//
// HandleKeyDown
//
////////////////////////////////////////////////////////////////////////////
void HandleKeyDown( PCALINFO pci, int vk, LPARAM lParam) { RECT rcT;
//
// First thing, lets try to figure out what the current x and y is.
//
int ix = GetLocalWeekday(); int iy = (wDateTime[DAY] + GetFirstDayOfTheMonth() - 1) / 7;
switch (vk) { case ( VK_LEFT ) : { ix--; if (ix < 0) { ix = 6; iy--; } break; } case ( VK_RIGHT ) : { ix++; if (ix == 7) { ix = 0; iy++; } break; } case ( VK_UP ) : { iy--; break; } case ( VK_DOWN ) : { iy++; break; } default : { //
// Ignore the character.
//
return; } }
//
// The y's are offset for the days of the week.
//
iy++; if (!IsValidClick(pci->hwnd, ix, iy)) { return; }
GetClientRect(pci->hwnd, &rcT); rcT.left = ((rcT.right * ix) / 7) + cBorderX - cBorderSelect; rcT.right = rcT.left + (2 * pci->cxChar) + (2 * cBorderSelect); rcT.top = (rcT.bottom * iy) / 7 + cBorderY - cBorderSelect; rcT.bottom = rcT.top + pci->cyChar + (2 * cBorderSelect);
InvalidateRect(pci->hwnd, &rcT, FALSE);
//
// First try, simply call to change the date.
//
ChangeCurrentDate(pci, ix, iy); NotifyWinEvent(EVENT_OBJECT_NAMECHANGE , pci->hwnd, OBJID_WINDOW, CHILDID_SELF); }
////////////////////////////////////////////////////////////////////////////
//
// CalWndProc
//
////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK CalWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PCALINFO pci;
pci = (PCALINFO)GetWindowLongPtr(hwnd, 0);
switch (message) { case ( WM_CREATE ) : { pci = (PCALINFO)LocalAlloc(LPTR, sizeof(CALINFO)); if (pci == 0) { return (-1); }
pci->hwnd = hwnd; SetWindowLongPtr(hwnd, 0, (LONG_PTR)pci);
GetDate(); break; } case ( WM_NCDESTROY ) : { if (pci) { LocalFree((HLOCAL)pci); } break; } case ( WM_SETFONT ) : { if (wParam) { pci->hfontCal = (HFONT)wParam; pci->cxChar = 0; } break; } case ( WM_GETTEXT ) : { return GetCalendarName((LPTSTR)lParam, (int)wParam); } case ( WM_GETTEXTLENGTH ) : { return GetCalendarName(NULL, 0); } case ( WM_PAINT ) : { CalendarPaint(pci, hwnd); break; } case ( WM_LBUTTONDOWN ) : { SetFocus(hwnd); HandleDateChange(pci, LOWORD(lParam), HIWORD(lParam)); break; } case ( WM_SETFOCUS ) : { pci->fFocus = TRUE; ChangeCurrentDate(pci, -1, -1); break; } case ( WM_KILLFOCUS ) : { pci->fFocus = FALSE; ChangeCurrentDate(pci, -1, -1); break; } case ( WM_KEYDOWN ) : { HandleKeyDown(pci, (int)wParam, lParam); break; } case ( WM_GETDLGCODE ) : { return (DLGC_WANTARROWS); break; } default : { return ( DefWindowProc(hwnd, message, wParam, lParam) ); } } return (0); }
////////////////////////////////////////////////////////////////////////////
//
// CalendarInit
//
////////////////////////////////////////////////////////////////////////////
TCHAR const c_szCalClass[] = CALENDAR_CLASS;
BOOL CalendarInit( HANDLE hInstance) { WNDCLASS wc;
if (!GetClassInfo(hInstance, c_szCalClass, &wc)) { wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(PCALINFO); wc.hCursor = NULL; wc.hbrBackground = NULL; wc.hIcon = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = c_szCalClass; wc.hInstance = hInstance; wc.lpfnWndProc = CalWndProc;
return (RegisterClass(&wc)); } return (TRUE); }
|