Code/Resource
Windows Develop
Linux-Unix program
Internet-Socket-Network
Web Server
Browser Client
Ftp Server
Ftp Client
Browser Plugins
Proxy Server
Email Server
Email Client
WEB Mail
Firewall-Security
Telnet Server
Telnet Client
ICQ-IM-Chat
Search Engine
Sniffer Package capture
Remote Control
xml-soap-webservice
P2P
WEB(ASP,PHP,...)
TCP/IP Stack
SNMP
Grid Computing
SilverLight
DNS
Cluster Service
Network Security
Communication-Mobile
Game Program
Editor
Multimedia program
Graph program
Compiler program
Compress-Decompress algrithms
Crypt_Decrypt algrithms
Mathimatics-Numerical algorithms
MultiLanguage
Disk/Storage
Java Develop
assembly language
Applications
Other systems
Database system
Embeded-SCM Develop
FlashMX/Flex
source in ebook
Delphi VCL
OS Develop
MiddleWare
MPI
MacOS develop
LabView
ELanguage
Software/Tools
E-Books
Artical/Document
monthcal.c
Package: shell.rar [view]
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 223k
Category:
Windows Kernel
Development Platform:
Visual C++
- #include "ctlspriv.h"
- #include "scdttime.h"
- #include "monthcal.h"
- #include "prshti.h" // for StrDup_AtoW
- // TODO
- //
- // #6329: When Min/Max range is set, then dates before the min
- // or after the max are painted in the normal date color. They
- // should be painted with MCSC_TRAILINGTEXT color. (Or we should
- // add a new color to cover this case.) Feature requested by Jobi George
- //
- // 9577: We want a DAYSTATE like structure for the background
- // color of dates. For highlighting. Perhaps a COLORSTATE per
- // registered background color.
- //
- // private message
- #define MCMP_WINDOWPOSCHANGED (MCM_FIRST - 1) // MCM_FIRST is way over WM_USER
- #define DTMP_WINDOWPOSCHANGED (DTM_FIRST - 1) // DTM_FIRST is way over WM_USER
- // MONTHCAL
- LRESULT CALLBACK MonthCalWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
- LRESULT MCNcCreateHandler(HWND hwnd);
- LRESULT MCCreateHandler(MONTHCAL *pmc, HWND hwnd, LPCREATESTRUCT lpcs);
- LRESULT MCOnStyleChanging(MONTHCAL *pmc, UINT gwl, LPSTYLESTRUCT pinfo);
- LRESULT MCOnStyleChanged(MONTHCAL *pmc, UINT gwl, LPSTYLESTRUCT pinfo);
- void MCCalcSizes(MONTHCAL *pmc);
- void MCHandleSetFont(MONTHCAL *pmc, HFONT hfont, BOOL fRedraw);
- void MCPaint(MONTHCAL *pmc, HDC hdc);
- void MCPaintMonth(MONTHCAL *pmc, HDC hdc, RECT *prc, int iMonth, int iYear, int iIndex,
- BOOL fDrawPrev, BOOL fDrawNext, HBRUSH hbrSelect);
- void MCNcDestroyHandler(HWND hwnd, MONTHCAL *pmc, WPARAM wParam, LPARAM lParam);
- void MCRecomputeSizing(MONTHCAL *pmc, RECT *prect);
- LRESULT MCSizeHandler(MONTHCAL *pmc, RECT *prc);
- void MCUpdateMonthNamePos(MONTHCAL *pmc);
- void MCUpdateStartEndDates(MONTHCAL *pmc, SYSTEMTIME *pstStart);
- void MCGetRcForDay(MONTHCAL *pmc, int iMonth, int iDay, RECT *prc);
- void MCGetRcForMonth(MONTHCAL *pmc, int iMonth, RECT *prc);
- void MCUpdateToday(MONTHCAL *pmc);
- void MCUpdateRcDayCur(MONTHCAL *pmc, SYSTEMTIME *pst);
- void MCUpdateDayState(MONTHCAL *pmc);
- int MCGetOffsetForYrMo(MONTHCAL *pmc, int iYear, int iMonth);
- int MCIsSelectedDayMoYr(MONTHCAL *pmc, int iDay, int iMonth, int iYear);
- BOOL MCIsBoldOffsetDay(MONTHCAL *pmc, int nDay, int iIndex);
- BOOL FGetOffsetForPt(MONTHCAL *pmc, POINT pt, int *piOffset);
- BOOL FGetRowColForRelPt(MONTHCAL *pmc, POINT ptRel, int *piRow, int *piCol);
- BOOL FGetDateForPt(MONTHCAL *pmc, POINT pt, SYSTEMTIME *pst,
- int* piDay, int* piCol, int* piRow, LPRECT prcMonth);
- LRESULT MCContextMenu(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam);
- LRESULT MCLButtonDown(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam);
- LRESULT MCLButtonUp(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam);
- LRESULT MCMouseMove(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam);
- LRESULT MCHandleTimer(MONTHCAL *pmc, WPARAM wParam);
- LRESULT MCHandleKeydown(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam);
- LRESULT MCHandleChar(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam);
- int MCIncrStartMonth(MONTHCAL *pmc, int nDelta, BOOL fDelayDayChange);
- void MCGetTitleRcsForOffset(MONTHCAL* pmc, int iOffset, LPRECT prcMonth, LPRECT prcYear);
- BOOL MCSetDate(MONTHCAL *pmc, SYSTEMTIME *pst);
- void MCNotifySelChange(MONTHCAL *pmc, UINT uMsg);
- void MCInvalidateDates(MONTHCAL *pmc, SYSTEMTIME *pst1, SYSTEMTIME *pst2);
- void MCInvalidateMonthDays(MONTHCAL *pmc);
- void MCSetToday(MONTHCAL* pmc, SYSTEMTIME* pst);
- void MCGetTodayBtnRect(MONTHCAL *pmc, RECT *prc);
- void GetYrMoForOffset(MONTHCAL *pmc, int iOffset, int *piYear, int *piMonth);
- BOOL FScrollIntoView(MONTHCAL *pmc);
- void MCFreeCalendarInfo(PCALENDARTYPE pct);
- void MCGetCalendarInfo(PCALENDARTYPE pct);
- BOOL MCIsDateStringRTL(TCHAR tch);
- // DATEPICK
- LRESULT CALLBACK DatePickWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
- LRESULT DPNcCreateHandler(HWND hwnd);
- LRESULT DPCreateHandler(DATEPICK *pdp, HWND hwnd, LPCREATESTRUCT lpcs);
- LRESULT DPOnStyleChanging(DATEPICK *pdp, UINT gwl, LPSTYLESTRUCT pinfo);
- LRESULT DPOnStyleChanged(DATEPICK *pdp, UINT gwl, LPSTYLESTRUCT pinfo);
- void DPHandleLocaleChange(DATEPICK *pdp);
- void DPDestroyHandler(HWND hwnd, DATEPICK *pdp, WPARAM wParam, LPARAM lParam);
- void DPHandleSetFont(DATEPICK *pdp, HFONT hfont, BOOL fRedraw);
- void DPPaint(DATEPICK *pdp, HDC hdc);
- void DPLBD_MonthCal(DATEPICK *pdp, BOOL fLButtonDown);
- LRESULT DPLButtonDown(DATEPICK *pdp, WPARAM wParam, LPARAM lParam);
- LRESULT DPLButtonUp(DATEPICK *pdp, WPARAM wParam, LPARAM lParam);
- void DPRecomputeSizing(DATEPICK *pdp, RECT *prect);
- LRESULT DPHandleKeydown(DATEPICK *pdp, WPARAM wParam, LPARAM lParam);
- LRESULT DPHandleChar(DATEPICK *pdp, WPARAM wParam, LPARAM lParam);
- void DPNotifyDateChange(DATEPICK *pdp);
- BOOL DPSetDate(DATEPICK *pdp, SYSTEMTIME *pst, BOOL fMungeDate);
- void DPDrawDropdownButton(DATEPICK *pdp, HDC hdc, BOOL fPressed);
- void SECGetSystemtime(LPSUBEDITCONTROL psec, LPSYSTEMTIME pst);
- static TCHAR const g_rgchMCName[] = MONTHCAL_CLASS;
- static TCHAR const g_rgchDTPName[] = DATETIMEPICK_CLASS;
- // MONTHCAL globals
- #define g_szTextExtentDef TEXT("0000")
- #define g_szNumFmt TEXT("%d")
- //
- // Epoch = the beginning of the universe (the earliest date we support)
- // Armageddon = the end of the universe (the latest date we support)
- //
- // Epoch is 14-sep-1752 because that's when the Gregorian calendar
- // kicked in. The day before 14-sep-1752 was 2-sep-1752 (in British
- // and US history; other countries switched at other times).
- //
- // Armageddon is 31-dec-9999 because we assume four digits for years
- // is enough. (Oh no, the Y10K problem...)
- //
- const SYSTEMTIME c_stEpoch = { 1752, 9, 0, 14, 0, 0, 0, 0 };
- const SYSTEMTIME c_stArmageddon = { 9999, 12, 0, 31, 23, 59, 59, 999 };
- void FillRectClr(HDC hdc, LPRECT prc, COLORREF clr)
- {
- COLORREF clrSave = SetBkColor(hdc, clr);
- ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL);
- SetBkColor(hdc, clrSave);
- }
- BOOL InitDateClasses(HINSTANCE hinst)
- {
- WNDCLASS wndclass;
- if (GetClassInfo(hinst, g_rgchMCName, &wndclass))
- {
- // we're already registered
- DebugMsg(TF_MONTHCAL, TEXT("mc: Date Classes already initialized."));
- return(TRUE);
- }
- wndclass.style = CS_GLOBALCLASS;
- wndclass.lpfnWndProc = (WNDPROC)MonthCalWndProc;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = sizeof(LPVOID);
- wndclass.hInstance = hinst;
- wndclass.hIcon = NULL;
- wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
- wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wndclass.lpszMenuName = NULL;
- wndclass.lpszClassName = g_rgchMCName;
- if (!RegisterClass(&wndclass))
- {
- DebugMsg(DM_WARNING, TEXT("mc: MonthCalClass failed to initialize"));
- return(FALSE);
- }
- wndclass.lpfnWndProc = (WNDPROC)DatePickWndProc;
- wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wndclass.lpszClassName = g_rgchDTPName;
- if (!RegisterClass(&wndclass))
- {
- DebugMsg(DM_WARNING, TEXT("mc: DatePickClass failed to initialize"));
- return(FALSE);
- }
- DebugMsg(TF_MONTHCAL, TEXT("mc: Date Classes initialized successfully."));
- return(TRUE);
- }
- ////////////////////////////////////////////////////////////////////////////
- //
- // MonthCal stuff
- //
- ////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////
- //
- // MCInsert/RemoveMarkers
- //
- // QuickSummary: Convert the string "MMMM yyyy" into "1MMMM3 2yyyy4".
- //
- // In order to lay out the month/year info in the header, we have to be
- // able to extract the month and year out of the formatted string so we
- // know what their rectangles are. We do this by wrapping the month and
- // year inserts with markers so we can extract them after formatting.
- //
- // Since 1 through 4 are control characters, they won't conflict with
- // displayable characters in the actual format string. And just to play it
- // safe, if we actually see a format character, we erase it from the string.
- //
- // MCInsertMarkers inserts the markers into the output string so we can
- // extract the substrings later. Quotation marks are funky since you can
- // write a format of "'The' mm'''th month of' yyyy". Note that a simple
- // even-odd test works for detecting whether we are inside or outside
- // quotation marks, even in the nested quotation mark case.
- //
- void MCInsertMarkers(LPTSTR pszOut, LPCTSTR pszIn)
- {
- BOOL fInQuote = FALSE;
- UINT flSeen = 0;
- UINT flThis;
- for (;;)
- {
- TCHAR ch = *pszIn;
- switch (ch) {
- // At end of string, terminate the output buffer and go home
- case TEXT(''):
- *pszOut = TEXT('');
- return;
- case TEXT('m'):
- case TEXT('M'):
- flThis = IMM_MONTHSTART;
- goto CheckMarker;
- case TEXT('y'):
- flThis = IMM_YEARSTART;
- goto CheckMarker;
- CheckMarker:
- // If inside a quotation mark or we've already done this guy,
- // then just treat it as a regular character.
- if (fInQuote || (flSeen & flThis))
- goto CopyChar;
- flSeen |= flThis;
- *pszOut++ = (TCHAR)flThis;
- // Don't need to use CharNext because we know *pszIn is "m" "M" or "y"
- for ( ; *pszIn == ch; pszIn++)
- {
- *pszOut++ = ch;
- }
- *pszOut++ = (TCHAR)(flThis + DMM_STARTEND);
- // Restart the loop so we re-parse the character at *pszIn
- continue;
- // Toggle the quotation mark gizmo if we see one, and then just
- // copy it.
- case ''':
- fInQuote ^= TRUE;
- goto CopyChar;
- //
- // Don't let these sneak into the output format or it
- // will confuse us.
- //
- case IMM_MONTHSTART:
- case IMM_YEARSTART:
- case IMM_MONTHEND:
- case IMM_YEAREND:
- break;
- default:
- CopyChar:
- *pszOut++ = ch;
- #ifndef UNICODE
- if (IsDBCSLeadByte(ch) && pszIn[1]) {
- *pszOut++ = *++pszIn;
- }
- #endif
- break;
- }
- pszIn++; // We handled the DBCS case already
- }
- // NOTREACHED
- }
- //
- // MCRemoveMarkers hunts down the marker characters and strips them out,
- // recording their locations in the optional MONTHMETRICS (as character
- // indices).
- //
- void MCRemoveMarkers(LPTSTR pszBuf, PMONTHMETRICS pmm)
- {
- int iWrite, iRead;
- //
- // If by some horrid error we can't find our markers, just pretend
- // they were at the start of the string.
- //
- if (pmm) {
- pmm->rgi[IMM_MONTHSTART] = 0;
- pmm->rgi[IMM_YEARSTART ] = 0;
- pmm->rgi[IMM_MONTHEND ] = 0;
- pmm->rgi[IMM_YEAREND ] = 0;
- }
- iWrite = iRead = 0;
- for (;;)
- {
- TCHAR ch = pszBuf[iRead];
- switch (ch)
- {
- // At end of string, terminate the output buffer and go home
- case TEXT(''):
- pszBuf[iWrite] = TEXT('');
- return;
- // If we find a marker, eat it and remember its location
- case IMM_MONTHSTART:
- case IMM_YEARSTART:
- case IMM_MONTHEND:
- case IMM_YEAREND:
- if (pmm)
- pmm->rgi[ch] = iWrite;
- break;
- // Otherwise, just copy it to the output
- default:
- pszBuf[iWrite++] = ch;
- #ifndef UNICODE
- if (IsDBCSLeadByte(ch) && pszBuf[iRead+1]) {
- pszBuf[iWrite++] = pszBuf[++iRead];
- }
- #endif
- break;
- }
- iRead++;
- }
- // NOTREACHED
- }
- ////////////////////////////////////
- //
- // Like LocalizedLoadString, except that we get the string from
- // LOCAL_USER_DEFAULT instead of GetUserDefaultUILanguage().
- //
- // LOCALE_USER_DEFAULT is the same as GetUserDefaultLCID(), and
- // LANGIDFROMLCID(GetUserDefaultLCID()) is the same as GetUserDefaultLangID().
- //
- // So we pass GetUserDefaultLangID() as the language.
- //
- int MCLoadString(UINT uID, LPWSTR lpBuffer, int nBufferMax)
- {
- return CCLoadStringEx(uID, lpBuffer, nBufferMax, GetUserDefaultLangID());
- }
- ////////////////////////////////////
- //
- // Get the localized calendar info
- //
- BOOL UpdateLocaleInfo(MONTHCAL* pmc, LPLOCALEINFO pli)
- {
- int i;
- TCHAR szBuf[64];
- int cch;
- LPTSTR pc = szBuf;
- //
- // Get information about the calendar (e.g., is it supported?)
- //
- MCGetCalendarInfo(&pmc->ct);
- //
- // Check if the calendar title is an RTL string
- //
- GetDateFormat(pmc->ct.lcid, 0, NULL, TEXT("MMMM"), szBuf, ARRAYSIZE(szBuf));
- pmc->fHeaderRTL = (WORD) MCIsDateStringRTL(szBuf[0]);
- //
- // get the short date format and sniff it to see if it displays the year
- // or month first
- //
- MCLoadString(IDS_MONTHFMT, pli->szMonthFmt, ARRAYSIZE(pli->szMonthFmt));
- //
- // Try to get the MONTHYEAR format from NLS. If not supported by NLS,
- // then use the hard-coded value in our resources. Note that we
- // subtract 4 from the buffer size because we may insert up to four
- // marker characters.
- //
- COMPILETIME_ASSERT(ARRAYSIZE(szBuf) >= ARRAYSIZE(pli->szMonthYearFmt));
- szBuf[0] = TEXT('');
- GetLocaleInfo(pmc->ct.lcid, LOCALE_SYEARMONTH,
- szBuf, ARRAYSIZE(pli->szMonthYearFmt) - CCH_MARKERS);
- if (!szBuf[0]) {
- MCLoadString(IDS_MONTHYEARFMT, szBuf, ARRAYSIZE(pli->szMonthYearFmt) - CCH_MARKERS);
- }
- MCInsertMarkers(pli->szMonthYearFmt, szBuf);
- //
- // BUGBUG - this code needs to change to use CAL_ values when we
- // want to support multiple calendars.
- //
- //
- // Get the month names
- //
- for (i = 0; i < 12; i++)
- {
- cch = GetLocaleInfo(pmc->ct.lcid, LOCALE_SMONTHNAME1 + i,
- pli->rgszMonth[i], CCHMAXMONTH);
- if (cch == 0)
- // the calendar is pretty useless without month names...
- return(FALSE);
- }
- //
- // Get the days of the week
- //
- for (i = 0; i < 7; i++)
- {
- cch = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVDAYNAME1 + i,
- pli->rgszDay[i], CCHMAXABBREVDAY);
- if (cch == 0)
- // the calendar is pretty useless without day names...
- return(FALSE);
- }
- //
- // If we haven't already set what the first day of the week is, get the
- // localized setting.
- //
- if (!pmc->fFirstDowSet)
- {
- cch = GetLocaleInfo(pmc->ct.lcid, LOCALE_IFIRSTDAYOFWEEK, szBuf, ARRAYSIZE(szBuf));
- if (cch > 0)
- pli->dowStartWeek = szBuf[0] - TEXT('0');
- }
- //
- // Get the first week of the year
- //
- cch = GetLocaleInfo(pmc->ct.lcid, LOCALE_IFIRSTWEEKOFYEAR, szBuf, ARRAYSIZE(szBuf));
- if (cch > 0)
- pli->firstWeek = szBuf[0] - TEXT('0');
- // Set up pointers
- for (i = 0; i < 12; i++)
- pli->rgpszMonth[i] = pli->rgszMonth[i];
- for (i = 0; i < 7; i++)
- pli->rgpszDay[i] = pli->rgszDay[i];
- // Get static strings
- MCLoadString(IDS_TODAY, pli->szToday, ARRAYSIZE(pli->szToday));
- MCLoadString(IDS_GOTOTODAY, pli->szGoToToday, ARRAYSIZE(pli->szGoToToday));
- // if we've been initialized
- if (pmc->hinstance)
- {
- SYSTEMTIME st;
- CopyDate(pmc->stMonthFirst, st);
- MCUpdateStartEndDates(pmc, &st);
- }
- return(TRUE);
- }
- void MCReloadMenus(MONTHCAL *pmc)
- {
- int i;
- if (pmc->hmenuCtxt)
- DestroyMenu(pmc->hmenuCtxt);
- if (pmc->hmenuMonth)
- DestroyMenu(pmc->hmenuMonth);
- pmc->hmenuCtxt = CreatePopupMenu();
- if (pmc->hmenuCtxt)
- AppendMenu(pmc->hmenuCtxt, MF_STRING, 1, pmc->li.szGoToToday);
- pmc->hmenuMonth = CreatePopupMenu();
- if (pmc->hmenuMonth)
- {
- for (i = 0; i < 12; i++)
- AppendMenu(pmc->hmenuMonth, MF_STRING, i + 1, pmc->li.rgszMonth[i]);
- }
- }
- BOOL MCHandleEraseBkgnd(MONTHCAL* pmc, HDC hdc)
- {
- RECT rc;
- GetClipBox(hdc, &rc);
- FillRectClr(hdc, &rc, pmc->clr[MCSC_BACKGROUND]);
- return TRUE;
- }
- LRESULT MCHandleHitTest(MONTHCAL* pmc, PMCHITTESTINFO phti)
- {
- int iMonth;
- RECT rc;
- if (!phti || phti->cbSize != sizeof(MCHITTESTINFO))
- return -1;
- phti->uHit = MCHT_NOWHERE;
- MCGetTodayBtnRect(pmc, &rc);
- if (PtInRect(&rc, phti->pt) && MonthCal_ShowToday(pmc))
- {
- phti->uHit = MCHT_TODAYLINK;
- }
- else if (pmc->fSpinPrev = (WORD) PtInRect(&pmc->rcPrev, phti->pt))
- {
- phti->uHit = MCHT_TITLEBTNPREV;
- }
- else if (PtInRect(&pmc->rcNext, phti->pt))
- {
- phti->uHit = MCHT_TITLEBTNNEXT;
- }
- else if (FGetOffsetForPt(pmc, phti->pt, &iMonth))
- {
- RECT rcMonth; // bounding rect for month containg phti->pt
- POINT ptRel; // relative point in a month
- int month;
- int year;
- MCGetRcForMonth(pmc, iMonth, &rcMonth);
- ptRel.x = phti->pt.x - rcMonth.left;
- ptRel.y = phti->pt.y - rcMonth.top;
- GetYrMoForOffset(pmc, iMonth, &year, &month);
- phti->st.wMonth = (WORD) month;
- phti->st.wYear = (WORD) year;
- //
- // if calendar is showing week numbers and the point lies in the
- // the week numbers, get the date for day immediately to the right
- // of the week number containing the point
- //
- if (MonthCal_ShowWeekNumbers(pmc) && PtInRect(&pmc->rcWeekNum, ptRel))
- {
- phti->uHit |= MCHT_CALENDARWEEKNUM;
- phti->pt.x += pmc->rcDayNum.left;
- FGetDateForPt(pmc, phti->pt, &phti->st, NULL, NULL, NULL, NULL);
- }
- //
- // if the point lies in the days of the week header, then return
- // the day of the week containing the point
- //
- else if (PtInRect(&pmc->rcDow, ptRel))
- {
- int iRow;
- int iCol;
- phti->uHit |= MCHT_CALENDARDAY;
- ptRel.y = pmc->rcDayNum.top;
- FGetRowColForRelPt(pmc, ptRel, &iRow, &iCol);
- phti->st.wDayOfWeek = (WORD) iCol;
- }
- //
- // if the point lies in the actually calendar part, then return the
- // date containg the point
- //
- else if (PtInRect(&pmc->rcDayNum, ptRel))
- {
- int iDay;
- // we're in the calendar part!
- phti->uHit |= MCHT_CALENDAR;
- if (FGetDateForPt(pmc, phti->pt, &phti->st, &iDay, NULL, NULL, NULL))
- {
- phti->uHit |= MCHT_CALENDARDATE;
- // if it was beyond the bounds of the days we're showing
- // and also FGetDateForPt returns TRUE, then we're on the boundary
- // of the displayed months
- if (iDay <= 0)
- {
- phti->uHit |= MCHT_PREV;
- }
- else if (iDay > pmc->rgcDay[iMonth + 1])
- {
- phti->uHit |= MCHT_NEXT;
- }
- }
- }
- else
- {
- RECT rcMonthTitle;
- RECT rcYearTitle;
- // otherwise we're in the title
- phti->uHit |= MCHT_TITLE;
- MCGetTitleRcsForOffset(pmc, iMonth, &rcMonthTitle, &rcYearTitle);
- if (PtInRect(&rcMonthTitle, phti->pt))
- {
- phti->uHit |= MCHT_TITLEMONTH;
- }
- else if (PtInRect(&rcYearTitle, phti->pt))
- {
- phti->uHit |= MCHT_TITLEYEAR;
- }
- }
- }
- DebugMsg(TF_MONTHCAL, TEXT("mc: Hittest returns : %d %d %d %d)"),
- (int)phti->st.wDay,
- (int)phti->st.wMonth,
- (int)phti->st.wYear,
- (int)phti->st.wDayOfWeek
- );
- return phti->uHit;
- }
- void MonthCal_OnPaint(MONTHCAL *pmc, HDC hdc)
- {
- if (hdc)
- {
- MCPaint(pmc, hdc);
- }
- else
- {
- PAINTSTRUCT ps;
- hdc = BeginPaint(pmc->ci.hwnd, &ps);
- MCPaint(pmc, hdc);
- EndPaint(pmc->ci.hwnd, &ps);
- }
- }
- BOOL MCGetDateFormatWithTempYear(PCALENDARTYPE pct, SYSTEMTIME *pst, LPCTSTR pszFormat, UINT uYear, LPTSTR pszBuf, UINT cchBuf)
- {
- BOOL fRc;
- WORD wYear = pst->wYear;
- pst->wYear = (WORD)uYear;
- fRc = GetDateFormat(pct->lcid, 0, pst, pszFormat, pszBuf, cchBuf);
- if (!fRc)
- {
- // AIGH! I hate Feburary 29. In case we are Feb 29 1996 and the
- // user changes to a non-leap year, force the day to something valid
- // in February 1997 (or whatever year the user finally picked).
- //
- // We can't blindly smash the day to 1 because the era might change
- // in the middle of the month.
- WORD wDay = pst->wDay;
- ASSERT(pst->wDay == 29);
- pst->wDay = 28;
- fRc = GetDateFormat(pct->lcid, 0, pst, pszFormat, pszBuf, cchBuf);
- pst->wDay = wDay;
- }
- pst->wYear = wYear;
- return fRc;
- }
- void MCUpdateEditYear(MONTHCAL *pmc)
- {
- TCHAR rgch[64];
- ASSERT(pmc->hwndEdit);
- EVAL(MCGetDateFormatWithTempYear(&pmc->ct, &pmc->st, TEXT("yyyy"), pmc->st.wYear, rgch, ARRAYSIZE(rgch)));
- SendMessage(pmc->hwndEdit, WM_SETTEXT, 0, (LPARAM)rgch);
- }
- LRESULT CALLBACK MonthCalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- MONTHCAL *pmc;
- LRESULT lres = 0;
- if (uMsg == WM_NCCREATE)
- return(MCNcCreateHandler(hwnd));
- pmc = MonthCal_GetPtr(hwnd);
- if (pmc == NULL)
- return(DefWindowProc(hwnd, uMsg, wParam, lParam));
- // Dispatch the various messages we can receive
- switch (uMsg)
- {
- case WM_CREATE:
- lres = MCCreateHandler(pmc, hwnd, (LPCREATESTRUCT)lParam);
- break;
- HANDLE_MSG(pmc, WM_ERASEBKGND, MCHandleEraseBkgnd);
- case WM_PRINTCLIENT:
- case WM_PAINT:
- MonthCal_OnPaint(pmc, (HDC)wParam);
- return(0);
- case WM_KEYDOWN:
- MCHandleKeydown(pmc, wParam, lParam);
- break;
- case WM_KEYUP:
- switch (wParam)
- {
- case VK_CONTROL:
- pmc->fControl = FALSE;
- break;
- case VK_SHIFT:
- pmc->fShift = FALSE;
- break;
- }
- break;
- #if 0
- case WM_CHAR:
- MCHandleChar(pmc, wParam, lParam);
- break;
- #endif
- case WM_CONTEXTMENU:
- MCContextMenu(pmc, wParam, lParam);
- break;
- case WM_LBUTTONDOWN:
- MCLButtonDown(pmc, wParam, lParam);
- break;
- case WM_LBUTTONUP:
- MCLButtonUp(pmc, wParam, lParam);
- break;
- case WM_MOUSEMOVE:
- MCMouseMove(pmc, wParam, lParam);
- break;
- case WM_GETFONT:
- lres = (LRESULT)pmc->hfont;
- break;
- case WM_SETFONT:
- MCHandleSetFont(pmc, (HFONT)wParam, (BOOL)LOWORD(lParam));
- MCSizeHandler(pmc, &pmc->rc);
- MCUpdateMonthNamePos(pmc);
- break;
- case WM_TIMER:
- MCHandleTimer(pmc, wParam);
- break;
- case WM_NCDESTROY:
- MCNcDestroyHandler(hwnd, pmc, wParam, lParam);
- break;
- case WM_ENABLE:
- {
- BOOL fEnable = wParam ? TRUE:FALSE;
- if (pmc->fEnabled != fEnable)
- {
- pmc->fEnabled = (WORD) fEnable;
- InvalidateRect(pmc->ci.hwnd, NULL, TRUE);
- }
- break;
- }
- case MCMP_WINDOWPOSCHANGED:
- case WM_SIZE:
- {
- RECT rc;
- if (uMsg==MCMP_WINDOWPOSCHANGED)
- {
- GetClientRect(pmc->ci.hwnd, &rc);
- }
- else
- {
- rc.left = 0;
- rc.top = 0;
- rc.right = GET_X_LPARAM(lParam);
- rc.bottom = GET_Y_LPARAM(lParam);
- }
- lres = MCSizeHandler(pmc, &rc);
- break;
- }
- case WM_CANCELMODE:
- PostMessage(pmc->ci.hwnd, WM_LBUTTONUP, 0, 0xFFFFFFFF);
- break;
- case WM_SYSCOLORCHANGE:
- InitGlobalColors();
- break;
- case WM_WININICHANGE:
- InitGlobalMetrics(wParam);
- if (lParam == 0 ||
- #ifdef UNICODE_WIN9x
- !lstrcmpiA((LPSTR)lParam, "Intl")
- #else
- !lstrcmpi((LPTSTR)lParam, TEXT("Intl"))
- #endif
- )
- {
- UpdateLocaleInfo(pmc, &pmc->li);
- MCReloadMenus(pmc);
- InvalidateRect(hwnd, NULL, TRUE);
- wParam = 0; // force MCCalcSizes to happen
- }
- if (wParam == 0 || wParam == SPI_SETNONCLIENTMETRICS)
- {
- MCCalcSizes(pmc);
- PostMessage(pmc->ci.hwnd, MCMP_WINDOWPOSCHANGED, 0, 0);
- }
- break;
- case WM_NOTIFYFORMAT:
- return CIHandleNotifyFormat(&pmc->ci, lParam);
- break;
- case WM_STYLECHANGING:
- lres = MCOnStyleChanging(pmc, (UINT) wParam, (LPSTYLESTRUCT)lParam);
- break;
- case WM_STYLECHANGED:
- lres = MCOnStyleChanged(pmc, (UINT) wParam, (LPSTYLESTRUCT)lParam);
- break;
- case WM_NOTIFY: {
- LPNMHDR pnm = (LPNMHDR)lParam;
- switch (pnm->code)
- {
- case UDN_DELTAPOS:
- if (pnm->hwndFrom == pmc->hwndUD)
- {
- // A notification from the UpDown control buddied
- // with the currently popped up monthcal, adjust the
- // edit box appropriately. We use UDN_DELTAPOS instad
- // of WM_VSCROLL because we care only about the delta and
- // not the absolute number. The absolute number causes us
- // problems in localized calendars.
- LPNM_UPDOWN pnmdp = (LPNM_UPDOWN)lParam;
- UINT yr = pmc->st.wYear + pnmdp->iDelta;
- UINT yrMin, yrMax;
- int delta;
- yrMin = pmc->stMin.wYear;
- if (yr < yrMin)
- yr = yrMin;
- yrMax = pmc->stMax.wYear;
- if (yr > yrMax)
- yr = yrMax;
- delta = yr - pmc->st.wYear;
- pmc->st.wYear = (WORD)yr;
- if (delta) {
- MCIncrStartMonth(pmc, delta * 12, FALSE);
- MCNotifySelChange(pmc,MCN_SELCHANGE);
- }
- }
- break;
- }
- } // WM_NOTIFY switch
- break;
- case WM_VSCROLL:
- // this must be coming from our UpDown control buddied
- // with the currently popped up monthcal, adjust the
- // edit box appropriately
- // We must do this on WM_VSCROLL rather than UDN_DELTAPOS
- // since we need to fix the selection after the updown mangled it
- MCUpdateEditYear(pmc);
- break;
- //
- // MONTHCAL specific messages
- //
- // MCM_GETCURSEL wParam=void lParam=LPSYSTEMTIME
- // sets *lParam to the currently selected SYSTEMTIME
- // returns TRUE on success, FALSE on error (such as multi-select MONTHCAL)
- case MCM_GETCURSEL:
- if (!MonthCal_IsMultiSelect(pmc))
- {
- LPSYSTEMTIME pst = (LPSYSTEMTIME)lParam;
- if (pst)
- {
- ZeroMemory(pst, sizeof(SYSTEMTIME));
- // BUGBUG raymondc v6. Need to zero out the time fields instead of
- // setting them to garbage. This confuses MFC.
- *pst = pmc->st;
- pst->wDayOfWeek = (DowFromDate(pst)+1) % 7; // this returns 0==sun
- lres = 1;
- }
- }
- break;
- // MCM_SETCURSEL wParam=void lParam=LPSYSTEMTIME
- // sets the currently selected SYSTEMTIME to *lParam
- // returns TRUE on success, FALSE on error (such as multi-select MONTHCAL or bad parameters)
- case MCM_SETCURSEL:
- {
- LPSYSTEMTIME pst = (LPSYSTEMTIME)lParam;
- if (MonthCal_IsMultiSelect(pmc) ||
- !IsValidDate(pst))
- {
- break;
- }
- if (0 == CmpDate(pst, &pmc->st))
- {
- // if no change, just return
- lres = 1;
- break;
- }
- pmc->rcDayOld = pmc->rcDayCur;
- pmc->fNoNotify = TRUE;
- lres = MCSetDate(pmc, pst);
- pmc->fNoNotify = FALSE;
- if (lres)
- {
- InvalidateRect(pmc->ci.hwnd, &pmc->rcDayOld, FALSE); // erase old highlight
- InvalidateRect(pmc->ci.hwnd, &pmc->rcDayCur, FALSE); // draw new highlight
- }
- UpdateWindow(pmc->ci.hwnd);
- break;
- }
- // MCM_GETMAXSELCOUNT wParam=void lParam=void
- // returns the max number of selected days allowed
- case MCM_GETMAXSELCOUNT:
- lres = (LRESULT)(MonthCal_IsMultiSelect(pmc) ? pmc->cSelMax : 1);
- break;
- // MCM_SETMAXSELCOUNT wParam=int lParam=void
- // sets the maximum selectable date range to wParam days
- // returns TRUE on success, FALSE on error (such as single-select MONTHCAL)
- case MCM_SETMAXSELCOUNT:
- if (!MonthCal_IsMultiSelect(pmc) || (int)wParam < 1)
- break;
- pmc->cSelMax = (int)wParam;
- lres = 1;
- break;
- // MCM_GETSELRANGE wParam=void lParam=LPSYSTEMTIME[2]
- // sets *lParam to the first date of the range, *(lParam+1) to the second date
- // returns TRUE on success, FALSE otherwise (such as single-select MONTHCAL)
- case MCM_GETSELRANGE:
- {
- LPSYSTEMTIME pst;
- pst = (LPSYSTEMTIME)lParam;
- if (!pst)
- break;
- ZeroMemory(pst, sizeof(SYSTEMTIME) * 2);
- if (!MonthCal_IsMultiSelect(pmc))
- break;
- *pst = pmc->st;
- pst->wDayOfWeek = (DowFromDate(pst)+1) % 7; // this returns 0==sun
- pst++;
- *pst = pmc->stEndSel;
- pst->wDayOfWeek = (DowFromDate(pst)+1) % 7; // this returns 0==sun
- lres = 1;
- break;
- }
- // MCM_SETSELRANGE wParam=void lParam=LPSYSTEMTIME[2]
- // sets the currently selected day range to *lparam to *(lParam+1)
- // returns TRUE on success, FALSE otherwise (such as single-select MONTHCAL or bad params)
- case MCM_SETSELRANGE:
- {
- LPSYSTEMTIME pstStart = (LPSYSTEMTIME)lParam;
- LPSYSTEMTIME pstEnd = &pstStart[1];
- SYSTEMTIME stStart;
- SYSTEMTIME stEnd;
- if (!MonthCal_IsMultiSelect(pmc) ||
- !IsValidDate(pstStart) ||
- !IsValidDate(pstEnd))
- break;
- // IE3 shipped without validating the time portion of this message.
- // Make sure our stored systemtimes are always valid (so we will
- // always give out valid systemtime structs).
- //
- if (!IsValidTime(pstStart))
- CopyTime(pmc->st, *pstStart);
- if (!IsValidTime(pstEnd))
- CopyTime(pmc->stEndSel, *pstEnd);
- if (CmpDate(pstStart, pstEnd) > 0)
- {
- stEnd = *pstStart;
- stStart = *pstEnd;
- pstStart = &stStart;
- pstEnd = &stEnd;
- }
- if (CmpDate(pstStart, &pmc->stMin) < 0)
- break;
- if (CmpDate(pstEnd, &pmc->stMax) > 0)
- break;
- if (DaysBetweenDates(pstStart, pstEnd) >= pmc->cSelMax)
- break;
- if (0 == CmpDate(pstStart, &pmc->st) &&
- 0 == CmpDate(pstEnd, &pmc->stEndSel))
- {
- // if no change, just return
- lres = 1;
- break;
- }
- pmc->stStartPrev = pmc->st;
- pmc->stEndPrev = pmc->stEndSel;
- pmc->fNoNotify = TRUE;
- lres = MCSetDate(pmc, pstEnd);
- if (lres)
- {
- pmc->st = *pstStart;
- pmc->stEndSel = *pstEnd;
- MCInvalidateDates(pmc, &pmc->stStartPrev, &pmc->stEndPrev);
- MCInvalidateDates(pmc, &pmc->st, &pmc->stEndSel);
- UpdateWindow(pmc->ci.hwnd);
- }
- pmc->fNoNotify = FALSE;
- break;
- }
- // MCM_GETMONTHRANGE wParam=GMR_flags lParam=LPSYSTEMTIME[2]
- // if GMR_VISIBLE, returns the range of selectable (non-grayed) displayed
- // days. if GMR_DAYSTATE, returns the range of every (incl grayed) days.
- // returns the number of months the above range spans.
- case MCM_GETMONTHRANGE:
- {
- LPSYSTEMTIME pst = (LPSYSTEMTIME)lParam;
- if (pst)
- {
- ZeroMemory(pst, 2 * sizeof(SYSTEMTIME));
- if (wParam == GMR_VISIBLE)
- {
- pst[0] = pmc->stMonthFirst;
- pst[1] = pmc->stMonthLast;
- }
- else if (wParam == GMR_DAYSTATE)
- {
- pst[0] = pmc->stViewFirst;
- pst[1] = pmc->stViewLast;
- }
- }
- lres = (LRESULT)pmc->nMonths;
- if (wParam == GMR_DAYSTATE)
- lres += 2;
- break;
- }
- // MCM_SETDAYSTATE wParam=int lParam=LPDAYSTATE
- // updates the MONTHCAL's DAYSTATE, only for MONTHCALs with DAYSTATE enabled
- // the range of months represented in the DAYSTATE array passed in lParam
- // should match that of the MONTHCAL
- // wParam count of items in DAYSTATE array
- // lParam pointer to array of DAYSTATE items
- // returns FALSE if not DAYSTATE enabled or if an error occurs, TRUE otherwise
- case MCM_SETDAYSTATE:
- {
- MONTHDAYSTATE *pmds = (MONTHDAYSTATE *)lParam;
- int i;
- if (!MonthCal_IsDayState(pmc) ||
- (int)wParam != (pmc->nMonths + 2))
- break;
- for (i = 0; i < (int)wParam; i++)
- {
- pmc->rgdayState[i] = *pmds;
- pmds++;
- }
- MCInvalidateMonthDays(pmc);
- lres = 1;
- break;
- }
- // MCM_GETMINREQRECT wParam=void lParam=LPRECT
- // sets *lParam to the minimum size required to display one month in full.
- // Note: this is dependent upon the currently selected font.
- // Apps can take the returned size and double the width to get two calendars
- // displayed.
- case MCM_GETMINREQRECT:
- {
- LPRECT prc = (LPRECT)lParam;
- prc->left = 0;
- prc->top = 0;
- prc->right = pmc->dxMonth;
- prc->bottom = pmc->dyMonth;
- if (MonthCal_ShowToday(pmc))
- {
- prc->bottom += pmc->dyToday;
- }
- AdjustWindowRect(prc, pmc->ci.style, FALSE);
- // This is a bogus message, lParam should really be LPSIZE.
- // Make sure left and top are 0 (AdjustWindowRect will make these negative).
- prc->right -= prc->left;
- prc->bottom -= prc->top;
- prc->left = 0;
- prc->top = 0;
- lres = 1;
- break;
- }
- // MCM_GETMAXTODAYWIDTH wParam=void lParam=LPDWORD
- // sets *lParam to the width of the "today" string, so apps
- // can figure out how big to make the calendar (max of MCM_GETMINREQRECT
- // and MCM_GETMAXTODAYWIDTH).
- case MCM_GETMAXTODAYWIDTH:
- {
- RECT rc;
- rc.left = 0;
- rc.top = 0;
- rc.right = pmc->dxToday;
- rc.bottom = pmc->dyToday;
- AdjustWindowRect(&rc, pmc->ci.style, FALSE);
- lres = rc.right - rc.left;
- break;
- }
- case MCM_HITTEST:
- return MCHandleHitTest(pmc, (PMCHITTESTINFO)lParam);
- case MCM_SETCOLOR:
- if (wParam >= 0 && wParam < MCSC_COLORCOUNT)
- {
- COLORREF clr = pmc->clr[wParam];
- pmc->clr[wParam] = (COLORREF)lParam;
- InvalidateRect(hwnd, NULL, wParam == MCSC_BACKGROUND);
- return clr;
- }
- return -1;
- case MCM_GETCOLOR:
- if (wParam >= 0 && wParam < MCSC_COLORCOUNT)
- return pmc->clr[wParam];
- return -1;
- case MCM_SETFIRSTDAYOFWEEK:
- {
- lres = MAKELONG(pmc->li.dowStartWeek, (BOOL)pmc->fFirstDowSet);
- if (lParam == (LPARAM)-1) {
- pmc->fFirstDowSet = FALSE;
- } else if (lParam < 7) {
- pmc->fFirstDowSet = TRUE;
- pmc->li.dowStartWeek = (TCHAR)lParam;
- }
- UpdateLocaleInfo(pmc, &pmc->li);
- InvalidateRect(hwnd, NULL, FALSE);
- return lres;
- }
- case MCM_GETFIRSTDAYOFWEEK:
- return MAKELONG(pmc->li.dowStartWeek, (BOOL)pmc->fFirstDowSet);
- case MCM_SETTODAY:
- MCSetToday(pmc, (SYSTEMTIME*)lParam);
- break;
- case MCM_GETTODAY:
- if (lParam) {
- *((SYSTEMTIME*)lParam) = pmc->stToday;
- return TRUE;
- }
- return FALSE;
- case MCM_GETRANGE:
- if (lParam)
- {
- LPSYSTEMTIME pst = (LPSYSTEMTIME)lParam;
- ZeroMemory(pst, sizeof(SYSTEMTIME)*2);
- ASSERT(lres == 0);
- if (pmc->fMinYrSet)
- {
- pst[0] = pmc->stMin;
- lres = GDTR_MIN;
- }
- if (pmc->fMaxYrSet)
- {
- pst[1] = pmc->stMax;
- lres |= GDTR_MAX;
- }
- }
- break;
- case MCM_SETRANGE:
- if (lParam)
- {
- LPSYSTEMTIME pst = (LPSYSTEMTIME)lParam;
- if (((wParam & GDTR_MIN) && !IsValidDate(pst)) ||
- ((wParam & GDTR_MAX) && !IsValidDate(&pst[1])))
- break;
- // IE3 did not validate the time portion of this struct
- // use stToday time fields cuz pmc->stMin/Max may be zero
- if ((wParam & GDTR_MIN) && !IsValidTime(pst))
- CopyTime(pmc->stToday, pst[0]);
- if ((wParam & GDTR_MAX) && !IsValidTime(&pst[1]))
- CopyTime(pmc->stToday, pst[1]);
- if (wParam & GDTR_MIN)
- {
- pmc->stMin = *pst;
- pmc->fMinYrSet = TRUE;
- }
- else
- {
- pmc->stMin = c_stEpoch;
- pmc->fMinYrSet = FALSE;
- }
- pst++;
- if (wParam & GDTR_MAX)
- {
- pmc->stMax = *pst;
- pmc->fMaxYrSet = TRUE;
- }
- else
- {
- pmc->stMax = c_stArmageddon;
- pmc->fMaxYrSet = FALSE;
- }
- if (pmc->fMaxYrSet && pmc->fMinYrSet && CmpDate(&pmc->stMin, &pmc->stMax) > 0)
- {
- SYSTEMTIME stTemp = pmc->stMin;
- pmc->stMin = pmc->stMax;
- pmc->stMax = stTemp;
- }
- lres = TRUE;
- }
- break;
- case MCM_GETMONTHDELTA:
- if (pmc->fMonthDelta)
- lres = pmc->nMonthDelta;
- else
- lres = pmc->nMonths;
- break;
- case MCM_SETMONTHDELTA:
- if (pmc->fMonthDelta)
- lres = pmc->nMonthDelta;
- else
- lres = 0;
- if ((int)wParam==0)
- pmc->fMonthDelta = FALSE;
- else
- {
- pmc->fMonthDelta = TRUE;
- pmc->nMonthDelta = (int)wParam;
- }
- break;
- default:
- if (CCWndProc(&pmc->ci, uMsg, wParam, lParam, &lres))
- return lres;
- lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
- break;
- } /* switch (uMsg) */
- return(lres);
- }
- LRESULT MCNcCreateHandler(HWND hwnd)
- {
- MONTHCAL *pmc;
- // Allocate storage for the dtpick structure
- pmc = (MONTHCAL *)NearAlloc(sizeof(MONTHCAL));
- if (!pmc)
- {
- DebugMsg(DM_WARNING, TEXT("mc: Out Of Near Memory"));
- return(0L);
- }
- MonthCal_SetPtr(hwnd, pmc);
- return(1L);
- }
- void MCInitColorArray(COLORREF* pclr)
- {
- pclr[MCSC_BACKGROUND] = g_clrWindow;
- pclr[MCSC_MONTHBK] = g_clrWindow;
- pclr[MCSC_TEXT] = g_clrWindowText;
- pclr[MCSC_TITLEBK] = GetSysColor(COLOR_ACTIVECAPTION);
- pclr[MCSC_TITLETEXT] = GetSysColor(COLOR_CAPTIONTEXT);
- pclr[MCSC_TRAILINGTEXT] = g_clrGrayText;
- }
- LRESULT MCCreateHandler(MONTHCAL *pmc, HWND hwnd, LPCREATESTRUCT lpcs)
- {
- HFONT hfont;
- SYSTEMTIME st;
- // Validate data
- //
- if (lpcs->style & MCS_INVALIDBITS)
- return(-1);
- CIInitialize(&pmc->ci, hwnd, lpcs);
- UpdateLocaleInfo(pmc, &pmc->li);
- // Initialize our data.
- //
- pmc->hinstance = lpcs->hInstance;
- pmc->fEnabled = !(pmc->ci.style & WS_DISABLED);
- pmc->hpenToday = CreatePen(PS_SOLID, 2, CAL_COLOR_TODAY);
- MCReloadMenus(pmc);
- // Default minimum date is the epoch
- pmc->stMin = c_stEpoch;
- // Default maximum date is armageddon
- pmc->stMax = c_stArmageddon;
- GetLocalTime(&pmc->stToday);
- pmc->st = pmc->stToday;
- if (MonthCal_IsMultiSelect(pmc))
- pmc->stEndSel = pmc->st;
- // make sure the time portions of these are valid. they are never
- // touched after this point
- pmc->stMonthFirst = pmc->st;
- pmc->stMonthLast = pmc->st;
- pmc->stViewFirst = pmc->st;
- pmc->stViewLast = pmc->st;
- pmc->cSelMax = CAL_DEF_SELMAX;
- hfont = NULL;
- if (lpcs->hwndParent)
- hfont = (HFONT)SendMessage(lpcs->hwndParent, WM_GETFONT, 0, 0);
- if (hfont == NULL)
- hfont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
- MCHandleSetFont(pmc, hfont, FALSE);
- CopyDate(pmc->st, st);
- // Can we start at January?
- if (st.wMonth <= (pmc->nViewRows * pmc->nViewCols))
- st.wMonth = 1;
- MCUpdateStartEndDates(pmc, &st);
- pmc->idTimerToday = SetTimer(pmc->ci.hwnd, CAL_TODAYTIMER, CAL_SECTODAYTIMER * 1000, NULL);
- MCInitColorArray(pmc->clr);
- return(0);
- }
- LRESULT MCOnStyleChanging(MONTHCAL *pmc, UINT gwl, LPSTYLESTRUCT pinfo)
- {
- if (gwl == GWL_STYLE)
- {
- DWORD changeFlags = pmc->ci.style ^ pinfo->styleNew;
- // Don't allow these bits to change
- changeFlags &= MCS_MULTISELECT | MCS_DAYSTATE | MCS_INVALIDBITS;
- pinfo->styleNew ^= changeFlags;
- }
- return(0);
- }
- LRESULT MCOnStyleChanged(MONTHCAL *pmc, UINT gwl, LPSTYLESTRUCT pinfo)
- {
- if (gwl == GWL_STYLE)
- {
- DWORD changeFlags = pmc->ci.style ^ pinfo->styleNew;
- ASSERT(!(changeFlags & (MCS_MULTISELECT|MCS_DAYSTATE|MCS_INVALIDBITS)));
- pmc->ci.style = pinfo->styleNew;
- if (changeFlags & MCS_WEEKNUMBERS)
- {
- MCCalcSizes(pmc);
- MCUpdateRcDayCur(pmc, &pmc->st);
- //MCUpdateToday(pmc);
- }
- // save a touch of code and share the MCUpdateToday
- // call with MCS_WEEKNUMBERS above
- if (changeFlags & MCS_NOTODAY|MCS_NOTODAYCIRCLE|MCS_WEEKNUMBERS)
- {
- MCUpdateToday(pmc);
- }
- if (changeFlags & (WS_BORDER | WS_CAPTION | WS_THICKFRAME)) {
- // the changing of these bits affect the size of the window
- // but not until after this message is handled
- // so post ourself a message.
- PostMessage(pmc->ci.hwnd, MCMP_WINDOWPOSCHANGED, 0, 0);
- }
- if (changeFlags)
- InvalidateRect(pmc->ci.hwnd, NULL, TRUE);
- }
- else if (gwl == GWL_EXSTYLE)
- {
- if ((pinfo->styleOld ^ pinfo->styleNew) & RTL_MIRRORED_WINDOW)
- {
- MCUpdateMonthNamePos(pmc);
- }
- }
- return(0);
- }
- void MCCalcSizes(MONTHCAL *pmc)
- {
- HDC hdc;
- HFONT hfontOrig;
- int i, dxMax, dyMax, dxExtra;
- RECT rect;
- TCHAR szBuf[128];
- TCHAR szDateFmt[64];
- // get sizing info for bold font...
- hdc = GetDC(pmc->ci.hwnd);
- hfontOrig = SelectObject(hdc, (HGDIOBJ)pmc->hfontBold);
- MGetTextExtent(hdc, g_szTextExtentDef, 2, &dxMax, &dyMax);
- MGetTextExtent(hdc, g_szTextExtentDef, 4, &pmc->dxYearMax, NULL);
- GetDateFormat(pmc->ct.lcid, DATE_SHORTDATE, &pmc->stToday,
- NULL, szDateFmt, sizeof(szDateFmt));
- wsprintf(szBuf,TEXT("%s %s"),pmc->li.szToday,szDateFmt);
- MGetTextExtent(hdc, szBuf, -1, &pmc->dxToday, &pmc->dyToday);
- // BUGBUG raymondc - hard-coded numbers are accessibility-incompatible
- pmc->dyToday += 4;
- //
- // Cache these values so we don't go wacko if the app fails to
- // forward WM_WININCHANGE messages into us and the user changes
- // scrollbar widths. We'll draw with the wrong width, but at
- // least they will be consistently wrong.
- //
- pmc->dxArrowMargin = DX_ARROWMARGIN;
- pmc->dxCalArrow = DX_CALARROW;
- pmc->dyCalArrow = DY_CALARROW;
- //
- // The banner bar consists of
- //
- // margin + scrollbutton + spacer +
- // MonthName yyyy +
- // + spacer + scrollbutton + margin
- //
- // Margin is dxArrowMargin
- //
- // Scrollbutton = dxCalArrow
- //
- // Spacer = border + CXVSCROLL + border
- //
- // The spacer needs to be large enough for us to insert an updown
- // control when it comes time to spin the year. We don't need to
- // cache the spacer anywhere - its value is implicit from the others.
- //
- // The actual width is divided by the number of columns we need
- // (typically 7, but perhaps 8 if we are also displaying week numbers).
- //
- // We round the division down - later, we'll add some random futz
- // to compensate.
- //
- dxExtra = pmc->dxArrowMargin + pmc->dxCalArrow +
- (g_cxBorder + g_cxVScroll + g_cxBorder);
- dxExtra = dxExtra + dxExtra; // left + right
- for (i = 0; i < 12; i++)
- {
- int dxTemp;
- // BUGBUG raymondc - not localization safe for languages which change
- // month forms based on context
- wsprintf(szBuf,TEXT("%s %s"),pmc->li.rgszMonth[i],g_szTextExtentDef);
- MGetTextExtent(hdc, szBuf, -1, &dxTemp, NULL);
- dxTemp += dxExtra;
- dxTemp = dxTemp / (CALCOLMAX + (MonthCal_ShowWeekNumbers(pmc) ? 1:0));
- if (dxTemp > dxMax)
- dxMax = dxTemp;
- }
- SelectObject(hdc, (HGDIOBJ)pmc->hfont);
- for (i = 0; i < 7; i++)
- {
- SIZE size;
- MGetTextExtent(hdc, pmc->li.rgszDay[i], -1, (LPINT)&size.cx, (LPINT)&size.cy);
- if (size.cx > dxMax)
- dxMax = size.cx;
- if (size.cy > dyMax)
- dyMax = size.cy;
- }
- if (dyMax < pmc->dyCalArrow / 2)
- dyMax = pmc->dyCalArrow / 2;
- SelectObject(hdc, (HGDIOBJ)hfontOrig);
- ReleaseDC(pmc->ci.hwnd, hdc);
- pmc->dxCol = dxMax + 2;
- pmc->dyRow = dyMax + 2;
- pmc->dxMonth = pmc->dxCol * (CALCOLMAX + (MonthCal_ShowWeekNumbers(pmc) ? 1:0)) + 1;
- pmc->dyMonth = pmc->dyRow * (CALROWMAX + 3) + 1; // we add 2 for the month name and day names
- pmc->dxToday += pmc->dxCol+6+CALBORDER; // +2 for -1 at ends and 4 for shift of circle
- if (pmc->dxMonth > pmc->dxToday)
- pmc->dxToday = pmc->dxMonth;
- // Space for month name (tile bar area of each month)
- pmc->rcMonthName.left = 0;
- pmc->rcMonthName.top = 0;
- pmc->rcMonthName.right = pmc->dxMonth;
- pmc->rcMonthName.bottom = pmc->rcMonthName.top + (pmc->dyRow * 2);
- // Space for day-of-week
- pmc->rcDow.left = 0;
- pmc->rcDow.top = pmc->rcMonthName.bottom;
- pmc->rcDow.right = pmc->dxMonth;
- pmc->rcDow.bottom = pmc->rcDow.top + pmc->dyRow;
- // Space for week numbers
- if (MonthCal_ShowWeekNumbers(pmc))
- {
- pmc->rcWeekNum.left = pmc->rcDow.left;
- pmc->rcWeekNum.top = pmc->rcDow.bottom;
- pmc->rcWeekNum.right = pmc->rcWeekNum.left + pmc->dxCol;
- pmc->rcWeekNum.bottom = pmc->dyMonth;
- pmc->rcDow.left += pmc->dxCol; // shift days of week
- }
- // Space for the day numbers
- pmc->rcDayNum.left = pmc->rcDow.left;
- pmc->rcDayNum.top = pmc->rcDow.bottom;
- pmc->rcDayNum.right = pmc->rcDayNum.left + (CALCOLMAX * pmc->dxCol);
- pmc->rcDayNum.bottom = pmc->dyMonth;
- GetClientRect(pmc->ci.hwnd, &rect);
- MCRecomputeSizing(pmc, &rect);
- }
- void MCHandleSetFont(MONTHCAL *pmc, HFONT hfont, BOOL fRedraw)
- {
- LOGFONT lf;
- HFONT hfontBold;
- if (hfont == NULL)
- hfont = (HFONT)GetStockObject(SYSTEM_FONT);
- GetObject(hfont, sizeof(LOGFONT), (LPVOID)&lf);
- // we want to make sure that the bold days are obviously different
- // from the non-bold days...
- lf.lfWeight = (lf.lfWeight >= 700 ? 1000 : 800);
- hfontBold = CreateFontIndirect(&lf);
- if (hfontBold == NULL)
- return;
- if (pmc->hfontBold)
- DeleteObject((HGDIOBJ)pmc->hfontBold);
- pmc->hfont = hfont;
- pmc->hfontBold = hfontBold;
- pmc->ci.uiCodePage = GetCodePageForFont(hfont);
- // calculate the new row and column sizes
- MCCalcSizes(pmc);
- if (fRedraw)
- {
- InvalidateRect(pmc->ci.hwnd, NULL, TRUE);
- UpdateWindow(pmc->ci.hwnd);
- }
- }
- #if 0 // why is this here? it's not called anywhere??
- // Stolen from the windows tips help file
- void DrawTransparentBitmap(HDC hdc, HBITMAP hbmp, RECT *prc, COLORREF cTransparentColor)
- {
- COLORREF cColor;
- BITMAP bm;
- HBITMAP hbmAndBack, hbmAndObject, hbmAndMem, hbmSave;
- HGDIOBJ hbmBackOld, hbmObjectOld, hbmMemOld, hbmSaveOld;
- HDC hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave;
- POINT ptSize;
- int d;
- hdcTemp = CreateCompatibleDC(hdc);
- SelectObject(hdcTemp, (HGDIOBJ)hbmp); // Select the bitmap
- GetObject(hbmp, sizeof(BITMAP), &bm);
- ptSize.x = bm.bmWidth; // Get width of bitmap
- ptSize.y = bm.bmHeight; // Get height of bitmap
- DPtoLP(hdcTemp, &ptSize, 1); // Convert from device to logical points
- d = prc->right - prc->left;
- if (d < ptSize.x)
- ptSize.x = d;
- d = prc->bottom - prc->top;
- if (d < ptSize.y)
- ptSize.y = d;
- // Create some DCs to hold temporary data.
- hdcBack = CreateCompatibleDC(hdc);
- hdcObject = CreateCompatibleDC(hdc);
- hdcMem = CreateCompatibleDC(hdc);
- hdcSave = CreateCompatibleDC(hdc);
- // Create a bitmap for each DC. DCs are required for a number of
- // GDI functions.
- // Monochrome DC
- hbmAndBack = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
- // Monochrome DC
- hbmAndObject = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
- hbmAndMem = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);
- hbmSave = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);
- // Each DC must select a bitmap object to store pixel data.
- hbmBackOld = SelectObject(hdcBack, (HGDIOBJ)hbmAndBack);
- hbmObjectOld = SelectObject(hdcObject, (HGDIOBJ)hbmAndObject);
- hbmMemOld = SelectObject(hdcMem, (HGDIOBJ)hbmAndMem);
- hbmSaveOld = SelectObject(hdcSave, (HGDIOBJ)hbmSave);
- // Set proper mapping mode.
- SetMapMode(hdcTemp, GetMapMode(hdc));
- // Save the bitmap sent here, because it will be overwritten.
- BitBlt(hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);
- // Set the background color of the source DC to the color.
- // contained in the parts of the bitmap that should be transparent
- cColor = SetBkColor(hdcTemp, cTransparentColor);
- // Create the object mask for the bitmap by performing a BitBlt
- // from the source bitmap to a monochrome bitmap.
- BitBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);
- // Set the background color of the source DC back to the original
- // color.
- SetBkColor(hdcTemp, cColor);
- // Create the inverse of the object mask.
- BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, NOTSRCCOPY);
- // Copy the background of the main DC to the destination.
- BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdc, prc->left, prc->top, SRCCOPY);
- // Mask out the places where the bitmap will be placed.
- BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);
- // Mask out the transparent colored pixels on the bitmap.
- BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);
- // XOR the bitmap with the background on the destination DC.
- BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);
- // Copy the destination to the screen.
- BitBlt(hdc, prc->left, prc->top, ptSize.x, ptSize.y, hdcMem, 0, 0, SRCCOPY);
- // Place the original bitmap back into the bitmap sent here.
- BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY);
- // Delete the memory bitmaps.
- SelectObject(hdcBack, hbmBackOld);
- DeleteObject(hbmAndBack);
- SelectObject(hdcObject, hbmObjectOld);
- DeleteObject(hbmAndObject);
- SelectObject(hdcMem, hbmMemOld);
- DeleteObject(hbmAndMem);
- SelectObject(hdcSave, hbmSaveOld);
- DeleteObject(hbmSave);
- // Delete the memory DCs.
- DeleteDC(hdcMem);
- DeleteDC(hdcBack);
- DeleteDC(hdcObject);
- DeleteDC(hdcSave);
- DeleteDC(hdcTemp);
- }
- #endif // DEAD CODE
- void MCDrawTodayCircle(MONTHCAL *pmc, HDC hdc, RECT *prc)
- {
- HGDIOBJ hpenOld;
- int xBegin, yBegin, yEnd;
- xBegin = (prc->right - prc->left) / 2 + prc->left;
- yBegin = prc->top + 4;
- yEnd = (prc->bottom - prc->top) / 2 + prc->top;
- hpenOld = SelectObject(hdc, (HGDIOBJ)pmc->hpenToday);
- Arc(hdc, prc->left + 1, yBegin, prc->right, prc->bottom,
- xBegin, yBegin, prc->right, yEnd);
- Arc(hdc, prc->left - 10, prc->top + 1, prc->right, prc->bottom,
- prc->right, yEnd, prc->left + 3, yBegin);
- SelectObject(hdc, hpenOld);
- }
- void MCInvalidateMonthDays(MONTHCAL *pmc)
- {
- InvalidateRect(pmc->ci.hwnd, &pmc->rcCentered, FALSE);
- }
- void MCGetTodayBtnRect(MONTHCAL *pmc, RECT *prc)
- {
- if (pmc->dxToday > pmc->rcCentered.right - pmc->rcCentered.left)
- {
- prc->left = pmc->rc.left + 1;
- prc->right = pmc->rc.right - 1;
- }
- else
- {
- prc->left = pmc->rcCentered.left + 1;
- prc->right = pmc->rcCentered.right - 1;
- }
- prc->top = pmc->rcCentered.bottom - pmc->dyToday;
- prc->bottom = pmc->rcCentered.bottom;
- // center the today rect when we only have 1 col and it will fit in window
- if ((pmc->nViewCols == 1) && (pmc->dxToday <= pmc->rc.right - pmc->rc.left))
- {
- int dx = ((pmc->rcCentered.right - pmc->rcCentered.left) - pmc->dxToday) / 2 - 1;
- prc->left += dx;
- prc->right -= dx;
- }
- }
- void MCPaintArrowBtn(MONTHCAL *pmc, HDC hdc, BOOL fPrev, BOOL fPressed)
- {
- LPRECT prc;
- UINT dfcs;
- BOOL bMirrored = FALSE;
- // This is to work around DrawFrameControl() mirroring bug fixed on W2k (bld 2042 or higher)
- #ifndef WINNT
- bMirrored = IS_DC_RTL_MIRRORED(hdc);
- #endif // WINNT
- if (fPrev)
- {
- if(bMirrored)
- {
- dfcs = DFCS_SCROLLRIGHT;
- }
- else
- {
- dfcs = DFCS_SCROLLLEFT;
- }
- prc = &pmc->rcPrev;
- }
- else
- {
- if(bMirrored)
- {
- dfcs = DFCS_SCROLLLEFT;
- }
- else
- {
- dfcs = DFCS_SCROLLRIGHT;
- }
- prc = &pmc->rcNext;
- }
- if (pmc->fEnabled)
- {
- if (fPressed)
- {
- dfcs |= DFCS_PUSHED | DFCS_FLAT;
- }
- }
- else
- {
- dfcs |= DFCS_INACTIVE;
- }
- DrawFrameControl(hdc, prc, DFC_SCROLL, dfcs);
- }
- void MCPaint(MONTHCAL *pmc, HDC hdc)
- {
- RECT rc, rcT;
- int irow, icol, iMonth, iYear, iIndex, dx, dy;
- HBRUSH hbrSelect;
- HGDIOBJ hgdiOrig, hpenOrig;
- pmc->hpen = CreatePen(PS_SOLID, 0, pmc->clr[MCSC_TEXT]);
- hbrSelect = CreateSolidBrush(pmc->clr[MCSC_TITLEBK]);
- SetBkMode(hdc, TRANSPARENT);
- SetTextColor(hdc, pmc->clr[MCSC_TEXT]);
- hpenOrig = SelectObject(hdc, GetStockObject(BLACK_PEN));
- rc = pmc->rcCentered;
- FillRectClr(hdc, &rc, pmc->clr[MCSC_MONTHBK]);
- SelectObject(hdc, (HGDIOBJ)pmc->hpen);
- // get the place for top left month
- rc.left = pmc->rcCentered.left;
- rc.right = rc.left + pmc->dxMonth;
- rc.top = pmc->rcCentered.top;
- rc.bottom = rc.top + pmc->dyMonth;
- iMonth = pmc->stMonthFirst.wMonth;
- iYear = pmc->stMonthFirst.wYear;
- dx = pmc->dxMonth + CALBORDER;
- dy = pmc->dyMonth + CALBORDER;
- iIndex = 0;
- for (irow = 0; irow < pmc->nViewRows; irow++)
- {
- rcT = rc;
- for (icol = 0; icol < pmc->nViewCols; icol++)
- {
- if (RectVisible(hdc, &rcT))
- {
- MCPaintMonth(pmc, hdc, &rcT, iMonth, iYear, iIndex,
- iIndex == 0,
- iIndex == (pmc->nMonths - 1), hbrSelect);
- }
- rcT.left += dx;
- rcT.right += dx;
- if (++iMonth > 12)
- {
- iMonth = 1;
- iYear++;
- }
- iIndex++;
- }
- rc.top += dy;
- rc.bottom += dy;
- }
- // draw the today stuff
- if (MonthCal_ShowToday(pmc))
- {
- MCGetTodayBtnRect(pmc, &rc);
- if (RectVisible(hdc, &rc))
- {
- TCHAR szDateFmt[32];
- TCHAR szBuf[64];
- rcT.right = rc.left + 2; // a bit extra border space
- if (MonthCal_ShowTodayCircle(pmc)) // this turns on/off the red circle
- {
- rcT.left = rcT.right + 2;
- rcT.right = rcT.left + pmc->dxCol - 2;
- rcT.top = rc.top + 2;
- rcT.bottom = rc.bottom - 2;
- MCDrawTodayCircle(pmc, hdc, &rcT);
- }
- rcT.left = rcT.right + 2;
- rcT.right = rc.right - 2;
- rcT.top = rc.top;
- rcT.bottom = rc.bottom;
- hgdiOrig = SelectObject(hdc, (HGDIOBJ)pmc->hfontBold);
- SetTextColor(hdc, pmc->clr[MCSC_TEXT]);
- GetDateFormat(pmc->ct.lcid, DATE_SHORTDATE, &pmc->stToday,
- NULL, szDateFmt, sizeof(szDateFmt));
- wsprintf(szBuf, TEXT("%s %s"), pmc->li.szToday, szDateFmt);
- DrawText(hdc, szBuf, lstrlen(szBuf), &rcT,
- DT_LEFT | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
- SelectObject(hdc, hgdiOrig);
- }
- }
- // Draw the spin buttons
- if (RectVisible(hdc, &pmc->rcPrev))
- MCPaintArrowBtn(pmc, hdc, TRUE, (pmc->idTimer && pmc->fSpinPrev));
- if (RectVisible(hdc, &pmc->rcNext))
- MCPaintArrowBtn(pmc, hdc, FALSE, (pmc->idTimer && !pmc->fSpinPrev));
- SelectObject(hdc, hpenOrig);
- DeleteObject((HGDIOBJ)hbrSelect);
- DeleteObject((HGDIOBJ)pmc->hpen);
- }
- //
- // MCGetMonthFormat gets the string to display for the month/year
- // in the passed-in SYSTEMTIME. This is tricky because of eras.
- // If pmm is non-NULL, it receives the metrics of the
- // formatted month/year string.
- //
- void MCGetMonthFormat(MONTHCAL *pmc, SYSTEMTIME *pst, LPTSTR rgch, UINT cch, PMONTHMETRICS pmm)
- {
- // For all months, we display the name appropriate to the first
- // day of the month. Note that this means that the title of the
- // month in which the era changes may be confusing. If the era
- // changes in the middle of a month, we name the month after the
- // previous era, even if the current selection belongs to the next
- // era. I hope nobody will mind.
- #if 0 // code that tried to track the era based on where the selection is
- // but before you can turn this on, you have to find everybody who
- // changes the selection, and that's hard because the monthcal control
- // doesn't have a centralizesd selection changer; people just party on
- // the selection directly
- if (pst->wMonth == pmc->st.wMonth &&
- pst->wYear == pmc->st.wYear) {
- pst->wDay = pmc->st.wDay;
- } else {
- pst->wDay = 1;
- }
- #else
- pst->wDay = 1;
- #endif
- //
- // Get the string (all marked up), then extract the markers
- // to locate the month and year substrings.
- //
- rgch[0] = TEXT(''); // In case something horrible happens
- GetDateFormat(pmc->ct.lcid, 0, pst,
- pmc->li.szMonthYearFmt,
- rgch, cch);
- MCRemoveMarkers(rgch, pmm);
- }
- void MCPaintMonth(MONTHCAL *pmc, HDC hdc, RECT *prc, int iMonth, int iYear, int iIndex,
- BOOL fDrawPrev, BOOL fDrawNext, HBRUSH hbrSelect)
- {
- BOOL fBold, fView, fReset;
- RECT rc, rcT;
- int nDay, cdy, irow, icol, crowShow, nweek, isel;
- TCHAR rgch[64];
- LPTSTR psz;
- HGDIOBJ hfontOrig, hbrushOld;
- COLORREF clrGrayText, clrHiliteText, clrOld, clrText;
- SYSTEMTIME st = {0};
- int iIndexSave = iIndex;
- clrText = pmc->clr[MCSC_TEXT];
- clrGrayText = pmc->clr[MCSC_TRAILINGTEXT];
- clrHiliteText = pmc->clr[MCSC_TITLETEXT];
- hfontOrig = SelectObject(hdc, (HGDIOBJ)pmc->hfont);
- SelectObject(hdc, (HGDIOBJ)pmc->hpen);
- //
- // Draw the Month and Year
- //
- // translate the relative coords to window coords
- rc = pmc->rcMonthName;
- rc.left += prc->left;
- rc.right += prc->left;
- rc.top += prc->top;
- rc.bottom += prc->top;
- if (RectVisible(hdc, &rc))
- {
- FillRectClr(hdc, &rc, pmc->clr[MCSC_TITLEBK]);
- SetTextColor(hdc, pmc->clr[MCSC_TITLETEXT]);
- SelectObject(hdc, (HGDIOBJ)pmc->hfontBold);
- st.wYear = (WORD) iYear;
- st.wMonth = (WORD) iMonth;
- MCGetMonthFormat(pmc, &st, rgch, ARRAYSIZE(rgch), NULL);
- DrawText(hdc, rgch, lstrlen(rgch), &rc, DT_CENTER | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
- #ifdef MARKER_DEBUG
- //
- // When debugging MCInsertMarker and MCRemoveMarker, draw colored
- // bars where we think the markers were.
- //
- { RECT rcT = rc;
- rcT.top = rcT.bottom - 2;
- rcT.left = rc.left + pmc->rgmm[iIndex].rgi[IMM_MONTHSTART];
- rcT.right = rc.left + pmc->rgmm[iIndex].rgi[IMM_MONTHEND];
- FillRectClr(hdc, &rcT, RGB(0xFF, 0, 0));
- rcT.left = rc.left + pmc->rgmm[iIndex].rgi[IMM_YEARSTART];
- rcT.right = rc.left + pmc->rgmm[iIndex].rgi[IMM_YEAREND];
- FillRectClr(hdc, &rcT, RGB(0, 0xFF, 0));
- }
- #endif
- SelectObject(hdc, (HGDIOBJ)pmc->hfont);
- }
- SetTextColor(hdc, pmc->clr[MCSC_TITLEBK]);
- //
- // Draw the days of the month
- //
- // translate the relative coords to window coords
- rc = pmc->rcDow;
- rc.left += prc->left;
- rc.right += prc->left;
- rc.top += prc->top;
- rc.bottom += prc->top;
- if (RectVisible(hdc, &rc))
- {
- MoveToEx(hdc, rc.left + 4, rc.bottom - 1, NULL);
- LineTo(hdc, rc.right - 4, rc.bottom - 1);
- rc.right = rc.left + pmc->dxCol;
- for (icol = 0; icol < CALCOLMAX; icol++)
- {
- psz = pmc->li.rgszDay[(icol + pmc->li.dowStartWeek) % 7];
- DrawText(hdc, psz, lstrlen(psz), &rc, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
- rc.left += pmc->dxCol;
- rc.right += pmc->dxCol;
- }
- }
- // Check to see how many days from the previous month exist in this months calendar
- nDay = pmc->rgnDayUL[iIndex]; // last day in prev month that won't be shown in this month
- cdy = pmc->rgcDay[iIndex]; // # of days in prev month
- // Calculate the number of weeks to display
- if (fDrawNext)
- crowShow = CALROWMAX;
- else
- crowShow = ((cdy - nDay) + pmc->rgcDay[iIndex + 1] + 6/* round up */) / 7;
- if (nDay != cdy)
- {
- // start at previous month
- iMonth--;
- if(iMonth <= 0)
- {
- iMonth = 12;
- iYear--;
- }
- nDay++;
- fView = FALSE;
- }
- else
- {
- // start at this month
- iIndex++; // this month
- nDay = 1;
- cdy = pmc->rgcDay[iIndex];
- fView = TRUE;
- }
- //
- // Draw the week numbers
- //
- if (MonthCal_ShowWeekNumbers(pmc))
- {
- // translate the relative coords to window coords
- rc = pmc->rcWeekNum;
- rc.left += prc->left;
- rc.top += prc->top;
- rc.right += prc->left;
- rc.bottom = rc.top + (pmc->dyRow * crowShow);
- // draw the week numbers
- if (RectVisible(hdc, &rc))
- {
- MoveToEx(hdc, rc.right - 1, rc.top + 4, NULL);
- LineTo(hdc, rc.right - 1, rc.bottom - 4);
- st.wYear = (WORD) iYear;
- st.wMonth = (WORD) iMonth;
- st.wDay = (WORD) nDay;
- nweek = GetWeekNumber(&st, pmc->li.dowStartWeek, pmc->li.firstWeek);
- rc.bottom = rc.top + pmc->dyRow;
- for (irow = 0; irow < crowShow; irow++)
- {
- wsprintf(rgch, g_szNumFmt, nweek);
- DrawText(hdc, rgch, (nweek > 9 ? 2 : 1), &rc,
- DT_CENTER | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
- rc.top += pmc->dyRow;
- rc.bottom += pmc->dyRow;
- IncrSystemTime(&st, &st, 1, INCRSYS_WEEK);
- nweek = GetWeekNumber(&st, pmc->li.dowStartWeek, pmc->li.firstWeek);
- }
- }
- }
- if (!fView)
- SetTextColor(hdc, clrGrayText);
- else
- SetTextColor(hdc, clrText);
- rc = pmc->rcDayNum;
- rc.left += prc->left;
- rc.top += prc->top;
- rc.right = rc.left + pmc->dxCol;
- rc.bottom = rc.top + pmc->dyRow;
- fReset = FALSE;
- fBold = FALSE;
- for (irow = 0; irow < crowShow; irow++)
- {
- rcT = rc;
- for (icol = 0; icol < CALCOLMAX; icol++)
- {
- if ((fView || fDrawPrev) && RectVisible(hdc, &rcT))
- {
- wsprintf(rgch, g_szNumFmt, nDay);
- if (MonthCal_IsDayState(pmc))
- {
- // if we're in a dropdown we don't display
- if (MCIsBoldOffsetDay(pmc, nDay, iIndex))
- {
- if (!fBold)
- {
- SelectObject(hdc, (HGDIOBJ)pmc->hfontBold);
- fBold = TRUE;
- }
- }
- else
- {
- if (fBold)
- {
- SelectObject(hdc, (HGDIOBJ)pmc->hfont);
- fBold = FALSE;
- }
- }
- }
- if (isel = MCIsSelectedDayMoYr(pmc, nDay, iMonth, iYear))
- {
- int x1, x2;
- clrOld = SetTextColor(hdc, clrHiliteText);
- hbrushOld = SelectObject(hdc, (HGDIOBJ)hbrSelect);
- fReset = TRUE;
- SelectObject(hdc, GetStockObject(NULL_PEN));
- x1 = 0;
- x2 = 0;
- if (isel & SEL_DOT)
- {
- Ellipse(hdc, rcT.left + 2, rcT.top + 2, rcT.right - 1, rcT.bottom - 1);
- if (isel == SEL_BEGIN)
- {
- x1 = rcT.left + (rcT.right - rcT.left) / 2;
- x2 = rcT.right;
- }
- else if (isel == SEL_END)
- {
- x1 = rcT.left;
- x2 = rcT.left + (rcT.right - rcT.left) / 2;
- }
- }
- else
- {
- x1 = rcT.left;
- x2 = rcT.right;
- }
- if (x1 && x2)
- {
- Rectangle(hdc, x1, rcT.top + 2, x2 + 1, rcT.bottom - 1);
- }
- }
- DrawText(hdc, rgch, (nDay > 9 ? 2 : 1), &rcT,
- DT_CENTER | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
- if (MonthCal_ShowTodayCircle(pmc) && pmc->fToday && iIndexSave == pmc->iMonthToday &&
- icol == pmc->iColToday && irow == pmc->iRowToday)
- {
- MCDrawTodayCircle(pmc, hdc, &rcT);
- }
- if (fReset)
- {
- SetTextColor(hdc, clrOld);
- SelectObject(hdc, (HGDIOBJ)hbrushOld);
- fReset = FALSE;
- }
- }
- rcT.left += pmc->dxCol;
- rcT.right += pmc->dxCol;
- nDay++;
- if (nDay > cdy)
- {
- if (!fDrawNext && iIndex > iIndexSave)
- goto doneMonth;
- nDay = 1;
- iIndex++;
- cdy = pmc->rgcDay[iIndex];
- iMonth++;
- if (iMonth > 12)
- {
- iMonth = 1;
- iYear++;
- }
- fView = !fView;
- SetTextColor(hdc, fView ? clrText : clrGrayText);
- fDrawPrev = fDrawNext;
- }
- }
- rc.top += pmc->dyRow;
- rc.bottom += pmc->dyRow;
- }
- doneMonth:
- SelectObject(hdc, hfontOrig);
- return;
- }
- int MCIsSelectedDayMoYr(MONTHCAL *pmc, int iDay, int iMonth, int iYear)
- {
- SYSTEMTIME st;
- int iBegin, iEnd;
- int iret = 0;
- st.wYear = (WORD) iYear;
- st.wMonth = (WORD) iMonth;
- st.wDay = (WORD) iDay;
- iBegin = CmpDate(&st, &pmc->st);
- if (MonthCal_IsMultiSelect(pmc))
- {
- iEnd = CmpDate(&st, &pmc->stEndSel);
- if (iBegin > 0 && iEnd< 0)
- iret = SEL_MID;
- else
- {
- if (iBegin == 0)
- iret |= SEL_BEGIN;
- if (iEnd == 0)
- iret |= SEL_END;
- }
- }
- else if (iBegin == 0)
- {
- iret = SEL_DOT;
- }
- return(iret);
- }
- BOOL MCIsBoldOffsetDay(MONTHCAL *pmc, int nDay, int iIndex)
- {
- return(pmc->rgdayState && (pmc->rgdayState[iIndex] & (1L << (nDay - 1))) != 0);
- }
- void MCNcDestroyHandler(HWND hwnd, MONTHCAL *pmc, WPARAM wParam, LPARAM lParam)
- {
- if (pmc)
- {
- if (pmc->hpenToday)
- DeleteObject((HGDIOBJ)pmc->hpenToday);
- if (pmc->hfontBold)
- DeleteObject((HGDIOBJ)pmc->hfontBold);
- if (pmc->hmenuCtxt)
- DestroyMenu(pmc->hmenuCtxt);
- if (pmc->hmenuMonth)
- DestroyMenu(pmc->hmenuMonth);
- if (pmc->idTimer)
- KillTimer(pmc->ci.hwnd, pmc->idTimer);
- if (pmc->idTimerToday)
- KillTimer(pmc->ci.hwnd, pmc->idTimerToday);
- MCFreeCalendarInfo(&pmc->ct);
- GlobalFreePtr(pmc);
- }
- // In case rogue messages float through after we have freed the pdtpick, set
- // the handle in the window structure to FFFF and test for this value at
- // the top of the WndProc
- MonthCal_SetPtr(hwnd, NULL);
- // Call DefWindowProc32 to free all little chunks of memory such as szName
- // and rgwScroll.
- DefWindowProc(hwnd, WM_NCDESTROY, wParam, lParam);
- }
- /* Computes the following:
- * nViewCols
- * nViewRows
- * rcCentered
- * rcPrev
- * rcNext
- */
- void MCRecomputeSizing(MONTHCAL *pmc, RECT *prect)
- {
- RECT rc;
- int dx, dy, dCal;
- // Space for entire calendar
- pmc->rc = *prect;
- dx = prect->right - prect->left;
- dy = prect->bottom - prect->top;
- pmc->nViewCols = 1 + (dx - pmc->dxMonth) / (pmc->dxMonth + CALBORDER);
- pmc->nViewRows = 1 + (dy - pmc->dyMonth - pmc->dyToday) / (pmc->dyMonth + CALBORDER);
- // if dx < dxMonth or dy < dyMonth, these can be zero. That's bad...
- if (pmc->nViewCols < 1)
- pmc->nViewCols = 1;
- if (pmc->nViewRows < 1)
- pmc->nViewRows = 1;
- // Make sure we don't display more than CALMONTHMAX months
- while ((pmc->nViewRows * pmc->nViewCols) > CALMONTHMAX)
- {
- if (pmc->nViewRows > pmc->nViewCols)
- pmc->nViewRows--;
- else
- pmc->nViewCols--;
- }
- // RC for the months, centered within the client window
- dCal = pmc->nViewCols * (pmc->dxMonth + CALBORDER) - CALBORDER;
- pmc->rcCentered.left = (dx - dCal) / 2;
- if (pmc->rcCentered.left < 0)
- pmc->rcCentered.left = 0;
- pmc->rcCentered.right = pmc->rcCentered.left + dCal;
- dCal = pmc->nViewRows * (pmc->dyMonth + CALBORDER) - CALBORDER + pmc->dyToday;
- pmc->rcCentered.top = (dy - dCal) / 2;
- if (pmc->rcCentered.top < 0)
- pmc->rcCentered.top = 0;
- pmc->rcCentered.bottom = pmc->rcCentered.top + dCal;
- // Calculate and set RCs for the spin buttons
- rc.top = pmc->rcCentered.top + (pmc->dyRow * 2 - pmc->dyCalArrow) /2;
- rc.bottom = rc.top + pmc->dyCalArrow;
- rc.left = pmc->rcCentered.left + pmc->dxArrowMargin;
- rc.right = rc.left + pmc->dxCalArrow;
- pmc->rcPrev = rc;
- rc.right = pmc->rcCentered.right - pmc->dxArrowMargin;
- rc.left = rc.right - pmc->dxCalArrow;
- pmc->rcNext = rc;
- }
- LRESULT MCSizeHandler(MONTHCAL *pmc, RECT *prc)
- {
- int nMax;
- SYSTEMTIME st;
- int cmo, dmo;
- MCRecomputeSizing(pmc, prc);
- nMax = pmc->nViewRows * pmc->nViewCols;
- // Compute new start date
- CopyDate(pmc->stMonthFirst, st);
- // BUGBUG: this doesn't consider stEndSel
- cmo = (pmc->stMonthLast.wYear - (int)pmc->st.wYear) * 12 +
- (pmc->stMonthLast.wMonth - (int)pmc->st.wMonth);
- dmo = nMax - pmc->nMonths;
- if (-dmo > cmo)
- {
- // Selected mon/yr not in view
- IncrSystemTime(&st, &st, -(cmo + dmo), INCRSYS_MONTH);
- cmo = 0;
- }
- // If the # of months being displayed has changed, then lets try to
- // start the calendar from January.
- if ((dmo != 0) && (cmo + dmo >= pmc->stMonthFirst.wMonth - 1))
- st.wMonth = 1;
- MCUpdateStartEndDates(pmc, &st);
- InvalidateRect(pmc->ci.hwnd, NULL, TRUE);
- UpdateWindow(pmc->ci.hwnd);
- return(0);
- }
- //
- // For each month being displayed, compute the precise locations of all
- // the gizmos we draw into the month header area.
- //
- void MCUpdateMonthNamePos(MONTHCAL *pmc)
- {
- HDC hdc;
- int iCount;
- SYSTEMTIME st;
- TCHAR rgch[64];
- SIZE size;
- HGDIOBJ hfontOrig;
- hdc = GetDC(pmc->ci.hwnd);
- hfontOrig = SelectObject(hdc, (HGDIOBJ)pmc->hfontBold);
- st = pmc->stMonthFirst;
- for (iCount = 0; iCount < pmc->nMonths; iCount++)
- {
- PMONTHMETRICS pmm = &pmc->rgmm[iCount];
- int i;
- MCGetMonthFormat(pmc, &st, rgch, ARRAYSIZE(rgch), pmm);
- GetTextExtentPoint32(hdc, rgch, lstrlen(rgch), &size);
- pmm->rgi[IMM_START] = (pmc->dxMonth - size.cx) / 2;
- //
- // Now convert the indices into pixels so we can figure out where
- // all the strings ended up.
- //
- for (i = IMM_DATEFIRST; i <= IMM_DATELAST; i++) {
- SIZE sizeT;
- // In case of horrible error, pretend the marker was at the
- // beginning of the string.
- sizeT.cx = 0;
- GetTextExtentPoint32(hdc, rgch, pmm->rgi[i], &sizeT);
- pmm->rgi[i] = pmm->rgi[IMM_START] + sizeT.cx;
- }
- //
- // Now flip the coordinates for RTL.
- //
- if (pmc->fHeaderRTL || IS_WINDOW_RTL_MIRRORED(pmc->ci.hwnd))
- {
- int dxStart, dxEnd;
- // Flip the month...
- dxStart = pmm->rgi[IMM_MONTHSTART] - pmm->rgi[IMM_START];
- dxEnd = pmm->rgi[IMM_MONTHEND ] - pmm->rgi[IMM_START];
- pmm->rgi[IMM_MONTHSTART] = pmm->rgi[IMM_START] + size.cx - dxEnd;
- pmm->rgi[IMM_MONTHEND ] = pmm->rgi[IMM_START] + size.cx - dxStart;
- // Flip the year...
- dxStart = pmm->rgi[IMM_YEARSTART] - pmm->rgi[IMM_START];
- dxEnd = pmm->rgi[IMM_YEAREND ] - pmm->rgi[IMM_START];
- pmm->rgi[IMM_YEARSTART] = pmm->rgi[IMM_START] + size.cx - dxEnd;
- pmm->rgi[IMM_YEAREND ] = pmm->rgi[IMM_START] + size.cx - dxStart;
- }
- // On to the next month
- if(++st.wMonth > 12)
- {
- st.wMonth = 1;
- st.wYear++;
- }
- }
- SelectObject(hdc, hfontOrig);
- ReleaseDC(pmc->ci.hwnd, hdc);
- }
- /*
- * Computes the following, given the number of rows & columns available:
- * stMonthFirst.wMonth
- * stMonthFirst.wYear
- * stMonthLast.wMonth
- * stMonthLast.wYear
- * nMonths
- *
- * Trashes *pstStart
- */
- void MCUpdateStartEndDates(MONTHCAL *pmc, SYSTEMTIME *pstStart)
- {
- int iCount, iMonth, iYear;
- int nMonthsToEdge;
- pmc->nMonths = pmc->nViewRows * pmc->nViewCols;
- // make sure pstStart to pstStart+nMonths is within range
- nMonthsToEdge = ((int)pmc->stMax.wYear - (int)pstStart->wYear) * 12 +
- ((int)pmc->stMax.wMonth - (int)pstStart->wMonth) + 1;
- if (nMonthsToEdge < pmc->nMonths)
- IncrSystemTime(pstStart, pstStart, nMonthsToEdge - pmc->nMonths, INCRSYS_MONTH);
- if (CmpDate(pstStart, &pmc->stMin) < 0)
- {
- CopyDate(pmc->stMin, *pstStart);
- }
- nMonthsToEdge = ((int)pmc->stMax.wYear - (int)pstStart->wYear) * 12 +
- ((int)pmc->stMax.wMonth - (int)pstStart->wMonth) + 1;
- if (nMonthsToEdge < pmc->nMonths)
- pmc->nMonths = nMonthsToEdge;
- pmc->stMonthFirst.wYear = pstStart->wYear;
- pmc->stMonthFirst.wMonth = pstStart->wMonth;
- pmc->stMonthFirst.wDay = 1;
- if (CmpDate(&pmc->stMonthFirst, &pmc->stMin) < 0)
- {
- pmc->stMonthFirst.wDay = pmc->stMin.wDay;
- ASSERT(0==CmpDate(&pmc->stMonthFirst, &pmc->stMin));
- }
- // these ranges are CALMONTHMAX+2 and nMonths <= CALMONTHMAX, so we are safe
- // index 0 corresponds to stViewFirst (DAYSTATE) info
- // index 1..nMonths correspond to stMonthFirst..stMonthLast info
- // index nMonths+1 corresponds to stViewLast (DAYSTATE) info
- //
- iYear = pmc->stMonthFirst.wYear;
- iMonth = pmc->stMonthFirst.wMonth - 1;
- if(iMonth == 0)
- {
- iMonth = 12;
- iYear--;
- }
- for (iCount = 0; iCount <= pmc->nMonths+1; iCount++)
- {
- int cdy, dow, ddow;
- // number of days in this month
- cdy = GetDaysForMonth(iYear, iMonth);
- pmc->rgcDay[iCount] = cdy;
- // move to "this" month
- if(++iMonth > 12)
- {
- iMonth = 1;
- iYear++;
- }
- // last day of this month NOT visible when viewing NEXT month
- dow = GetStartDowForMonth(iYear, iMonth);
- ddow = dow - pmc->li.dowStartWeek;
- if(ddow < 0)
- ddow += CALCOLMAX;
- pmc->rgnDayUL[iCount] = cdy - ddow;
- }
- // we want to always have days visible on the previous month
- if (pmc->rgnDayUL[0] == pmc->rgcDay[0])
- pmc->rgnDayUL[0] -= CALCOLMAX;
- IncrSystemTime(&pmc->stMonthFirst, &pmc->stMonthLast, pmc->nMonths - 1, INCRSYS_MONTH);
- pmc->stMonthLast.wDay = (WORD) pmc->rgcDay[pmc->nMonths];
- if (pmc->fMaxYrSet && CmpDate(&pmc->stMonthLast, &pmc->stMax) > 0)
- {
- pmc->stMonthLast.wDay = pmc->stMax.wDay;
- ASSERT(0==CmpDate(&pmc->stMonthLast, &pmc->stMax));
- }
- pmc->stViewFirst.wYear = pmc->stMonthFirst.wYear;
- pmc->stViewFirst.wMonth = pmc->stMonthFirst.wMonth - 1;
- if (pmc->stViewFirst.wMonth == 0)
- {
- pmc->stViewFirst.wMonth = 12;
- pmc->stViewFirst.wYear--;
- }
- pmc->stViewFirst.wDay = pmc->rgnDayUL[0] + 1;
- pmc->stViewLast.wYear = pmc->stMonthLast.wYear;
- pmc->stViewLast.wMonth = pmc->stMonthLast.wMonth + 1;
- if (pmc->stViewLast.wMonth == 13)
- {
- pmc->stViewLast.wMonth = 1;
- pmc->stViewLast.wYear++;
- }
- // total days - (days in last month + remaining days in previous month)
- pmc->stViewLast.wDay = CALROWMAX * CALCOLMAX -
- (pmc->rgcDay[pmc->nMonths] +
- pmc->rgcDay[pmc->nMonths-1] - pmc->rgnDayUL[pmc->nMonths-1]);
- MCUpdateDayState(pmc);
- MCUpdateRcDayCur(pmc, &pmc->st);
- MCUpdateToday(pmc);
- MCUpdateMonthNamePos(pmc);
- }
- void MCUpdateToday(MONTHCAL *pmc)
- {
- if (MonthCal_ShowTodayCircle(pmc))
- {
- int iMonth;
- iMonth = MCGetOffsetForYrMo(pmc, pmc->stToday.wYear, pmc->stToday.wMonth);
- if (iMonth < 0)
- {
- // today is not visible in the displayed months
- pmc->fToday = FALSE;
- }
- else
- {
- int iDay;
- // today is visible in the displayed months
- pmc->fToday = TRUE;
- iDay = pmc->rgcDay[iMonth] - pmc->rgnDayUL[iMonth] + pmc->stToday.wDay - 1;
- pmc->iMonthToday = iMonth;
- pmc->iRowToday = iDay / CALCOLMAX;
- pmc->iColToday = iDay % CALCOLMAX;
- }
- }
- }
- BOOL FUpdateRcDayCur(MONTHCAL *pmc, POINT pt)
- {
- int iRow, iCol;
- RECT rc;
- SYSTEMTIME st;
- if (!FGetDateForPt(pmc, pt, &st, NULL, &iCol, &iRow, &rc))
- return FALSE;
- if (CmpDate(&st, &pmc->stMin) < 0)
- return FALSE;
- if (CmpDate(&st, &pmc->stMax) > 0)
- return FALSE;
- // calculate the day rc
- pmc->rcDayCur.left = rc.left + pmc->rcDayNum.left + iCol * pmc->dxCol;
- pmc->rcDayCur.top = rc.top + pmc->rcDayNum.top + iRow * pmc->dyRow;
- pmc->rcDayCur.right = pmc->rcDayCur.left + pmc->dxCol;
- pmc->rcDayCur.bottom = pmc->rcDayCur.top + pmc->dyRow;
- return(TRUE);
- }
- void MCUpdateDayState(MONTHCAL *pmc)
- {
- HWND hwndParent;
- if (!MonthCal_IsDayState(pmc))
- return;
- hwndParent = GetParent(pmc->ci.hwnd);
- if (hwndParent)
- {
- int i, mon, yr, cmonths;
- yr = pmc->stViewFirst.wYear;
- mon = pmc->stViewFirst.wMonth;
- cmonths = pmc->nMonths + 2;
- // don't do anything unless we need to
- if (cmonths != pmc->cds || mon != pmc->dsMonth || yr != pmc->dsYear)
- {
- // this is a small enough to not deal with allocating it
- NMDAYSTATE nmds;
- MONTHDAYSTATE buffer[CALMONTHMAX+2];
- ZeroMemory(&nmds, sizeof(nmds));
- nmds.stStart.wYear = (WORD) yr;
- nmds.stStart.wMonth = (WORD) mon;
- nmds.stStart.wDay = 1;
- nmds.cDayState = cmonths;
- nmds.prgDayState = buffer;
- CCSendNotify(&pmc->ci, MCN_GETDAYSTATE, &nmds.nmhdr);
- for (i = 0; i < cmonths; i++)
- pmc->rgdayState[i] = nmds.prgDayState[i];
- pmc->cds = cmonths;
- pmc->dsMonth = mon;
- pmc->dsYear = yr;
- }
- }
- }
- void MCNotifySelChange(MONTHCAL *pmc, UINT uMsg)
- {
- HWND hwndParent;
- if (pmc->fNoNotify)
- return;
- hwndParent = GetParent(pmc->ci.hwnd);
- if (hwndParent)
- {
- NMSELCHANGE nmsc;
- ZeroMemory(&nmsc, sizeof(nmsc));
- CopyDate(pmc->st, nmsc.stSelStart);
- if (MonthCal_IsMultiSelect(pmc))
- CopyDate(pmc->stEndSel, nmsc.stSelEnd);
- CCSendNotify(&pmc->ci, uMsg, &nmsc.nmhdr);
- }
- }
- void MCUpdateRcDayCur(MONTHCAL *pmc, SYSTEMTIME *pst)
- {
- int iOff;
- iOff = MCGetOffsetForYrMo(pmc, pst->wYear, pst->wMonth);
- if (iOff >= 0)
- MCGetRcForDay(pmc, iOff, pst->wDay, &pmc->rcDayCur);
- }
- // returns zero-based index into DISPLAYED months for month
- // if month is not in DISPLAYED months, then -1 is returned...
- int MCGetOffsetForYrMo(MONTHCAL *pmc, int iYear, int iMonth)
- {
- int iOff;
- iOff = ((int)iYear - pmc->stMonthFirst.wYear) * 12 + (int)iMonth - pmc->stMonthFirst.wMonth;
- if (iOff < 0 || iOff >= pmc->nMonths)
- return(-1);
- return(iOff);
- }
- // iMonth is a zero-based index relative to the DISPLAYED months.
- // iDay is a 1-based index of the day of the month,
- void MCGetRcForDay(MONTHCAL *pmc, int iMonth, int iDay, RECT *prc)
- {
- RECT rc;
- int iPlace, iRow, iCol;
- MCGetRcForMonth(pmc, iMonth, &rc);
- iPlace = pmc->rgcDay[iMonth] - pmc->rgnDayUL[iMonth] + iDay - 1;
- iRow = iPlace / CALCOLMAX;
- iCol = iPlace % CALCOLMAX;
- prc->left = rc.left + pmc->rcDayNum.left + (pmc->dxCol * iCol);
- prc->top = rc.top + pmc->rcDayNum.top + (pmc->dyRow * iRow);
- prc->right = prc->left + pmc->dxCol;
- prc->bottom = prc->top + pmc->dyRow;
- }
- //
- // This routine gets the bounding rect for the iMonth of the displayed months.
- // NOTE: iMonth is a zero-based index relative to the DISPLAYED months,
- // counting along the rows.
- //
- void MCGetRcForMonth(MONTHCAL *pmc, int iMonth, RECT *prc)
- {
- int iRow, iCol, d;
- iRow = iMonth / pmc->nViewCols;
- iCol = iMonth % pmc->nViewCols;
- // intialize the rect to be the bounding rect for the month in the
- // top left corner
- prc->left = pmc->rcCentered.left;
- prc->right = prc->left + pmc->dxMonth;
- prc->top = pmc->rcCentered.top;
- prc->bottom = prc->top + pmc->dyMonth;
- if (iCol) // slide the rect across to the correct column
- {
- d = (pmc->dxMonth + CALBORDER) * iCol;
- prc->left += d;
- prc->right += d;
- }
- if (iRow) // slide the rect down to the correct row
- {
- d = (pmc->dyMonth + CALBORDER) * iRow;
- prc->top += d;
- prc->bottom += d;
- }
- }
- // Changes starting month by nDelta
- // returns number of months actually changed
- int FIncrStartMonth(MONTHCAL *pmc, int nDelta, BOOL fNoCurDayChange)
- {
- SYSTEMTIME stStart;
- int nOldStartYear = pmc->stMonthFirst.wYear;
- int nOldStartMonth = pmc->stMonthFirst.wMonth;
- IncrSystemTime(&pmc->stMonthFirst, &stStart, nDelta, INCRSYS_MONTH);
- // MCUpdateStartEndDates takes stMin/stMax into account
- MCUpdateStartEndDates(pmc, &stStart);
- if (!fNoCurDayChange)
- {
- int cday;
- // BUGBUG: we arbitrarily set the currently selected day
- // to be in the new stMonthFirst, but given the way the
- // control works, I doubt we ever hit this code. what's it for??
- if (MonthCal_IsMultiSelect(pmc))
- cday = DaysBetweenDates(&pmc->st, &pmc->stEndSel);
- // need to set date for focus here
- pmc->st.wMonth = pmc->stMonthFirst.wMonth;
- pmc->st.wYear = pmc->stMonthFirst.wYear;
- // Check to see if the day is in range, eg, Jan 31 -> Feb 28
- if (pmc->st.wDay > pmc->rgcDay[1])
- pmc->st.wDay = (WORD) pmc->rgcDay[1];
- if (MonthCal_IsMultiSelect(pmc))
- IncrSystemTime(&pmc->st, &pmc->stEndSel, cday, INCRSYS_DAY);
- MCNotifySelChange(pmc, MCN_SELCHANGE);
- MCUpdateRcDayCur(pmc, &pmc->st);
- }
- MCInvalidateMonthDays(pmc);
- return((pmc->stMonthFirst.wYear-nOldStartYear)*12 + (pmc->stMonthFirst.wMonth-nOldStartMonth));
- }
- // FIncrStartMonth with a beep when it doesn't change.
- int MCIncrStartMonth(MONTHCAL *pmc, int nDelta, BOOL fDelayDayChange)
- {
- int cmoSpun;
- // FIncrStartMonth takes stMin/stMax into account
- cmoSpun = FIncrStartMonth(pmc, nDelta, fDelayDayChange);
- if (cmoSpun==0)
- MessageBeep(0);
- return(cmoSpun);
- }
- //
- // Determines in which month the given point lies. In other words, if the
- // calendar control is currently sized to show six months, this routine
- // determines in which which of those six months the point lies. It returns
- // the zero based index of the month, counting along the rows.
- //
- BOOL FGetOffsetForPt(MONTHCAL *pmc, POINT pt, int *piOffset)
- {
- int iRow, iCol, i;
- // check to see if point is within the centered months
- if (!PtInRect(&pmc->rcCentered, pt))
- return(FALSE);
- // calculate the month row and column
- // (we're really fudging a little here, since the point could
- // actually be within the space between months...)
- iCol = (pt.x - pmc->rcCentered.left) / (pmc->dxMonth + CALBORDER);
- iRow = (pt.y - pmc->rcCentered.top) / (pmc->dyMonth + CALBORDER);
- i = iRow * pmc->nViewCols + iCol;
- if (i >= pmc->nMonths)
- return(FALSE);
- *piOffset = i;
- return(TRUE);
- }
- //
- // This routine returns the row and column of day containing the given point
- //
- BOOL FGetRowColForRelPt(MONTHCAL *pmc, POINT ptRel, int *piRow, int *piCol)
- {
- if (!PtInRect(&pmc->rcDayNum, ptRel))
- return(FALSE);
- ptRel.x -= pmc->rcDayNum.left;
- ptRel.y -= pmc->rcDayNum.top;
- *piCol = ptRel.x / pmc->dxCol;
- *piRow = ptRel.y / pmc->dyRow;
- return(TRUE);
- }
- //
- // This routine returns the month and year of the iMonth in the displayed
- // months. NOTE: iMonth is a zero-based index of the displayed months
- //
- void GetYrMoForOffset(MONTHCAL *pmc, int iMonth, int *piYear, int *piMonth)
- {
- SYSTEMTIME st;
- st.wDay = 1;
- st.wMonth = pmc->stMonthFirst.wMonth;
- st.wYear = pmc->stMonthFirst.wYear;
- IncrSystemTime(&st, &st, iMonth, INCRSYS_MONTH);
- *piYear = st.wYear;
- *piMonth = st.wMonth;
- }
- //
- // This routine returns, the day, month, and year of day containing the
- // given point. It will optionally return the day of the month, the row and
- // column in the month, and the bounding rect of the month containing the point.
- // NOTE: the day returned in piDay can be less than 1 (to indicate a day in the
- // previous month) or greater than the number of days in the month (to indicate
- // a day in the next month).
- //
- BOOL FGetDateForPt(MONTHCAL *pmc, POINT pt, SYSTEMTIME *pst, int *piDay,
- int* piCol, int* piRow, LPRECT prcMonth)
- {
- int iOff, iRow, iCol, iDay, iMon, iYear;
- RECT rcMonth;
- if (!FGetOffsetForPt(pmc, pt, &iOff))
- return(FALSE);
- MCGetRcForMonth(pmc, iOff, &rcMonth);
- pt.x -= rcMonth.left;
- pt.y -= rcMonth.top;
- if (!FGetRowColForRelPt(pmc, pt, &iRow, &iCol))
- return(FALSE);
- // get the day containing the point by subtracting the number of days
- // that are visible from the previous month, and then add one, since
- // we are zero-based and the days of the month are 1-based.
- //
- iDay = iRow * CALCOLMAX + iCol - (pmc->rgcDay[iOff] - pmc->rgnDayUL[iOff]) + 1;
- if (piDay)
- *piDay = iDay;
- if (iDay <= 0)
- {
- if (iOff)
- return(FALSE); // dont accept days in prev month unless
- // this happens to be the first month
- iDay += pmc->rgcDay[iOff]; // add the cnt of days in the prev month,
- --iOff; // then incr the month to get day in new month
- }
- else if (iDay > pmc->rgcDay[iOff+1])
- {
- if (iOff < (pmc->nMonths - 1)) // dont accept days in next month unless
- return(FALSE); // this happens to be the last month
- ++iOff; // increment the month, and then sub the
- iDay -= pmc->rgcDay[iOff]; // count of days to get day in new month
- }
- GetYrMoForOffset(pmc, iOff, &iYear, &iMon);
- pst->wDay = (WORD) iDay;
- pst->wMonth = (WORD) iMon;
- pst->wYear = (WORD) iYear;
- if (piCol)
- *piCol = iCol;
- if (piRow)
- *piRow = iRow;
- if (prcMonth)
- *prcMonth = rcMonth;
- return(TRUE);
- }
- BOOL MCSetDate(MONTHCAL *pmc, SYSTEMTIME *pst)
- {
- int nDelta = 0;
- //
- // Can't set date outside of min/max range
- //
- if (CmpDate(pst, &pmc->stMin) < 0)
- return FALSE;
- if (CmpDate(pst, &pmc->stMax) > 0)
- return FALSE;
- //
- // Set new day
- //
- pmc->st = *pst;
- if (MonthCal_IsMultiSelect(pmc))
- pmc->stEndSel = *pst;
- FScrollIntoView(pmc);
- MCNotifySelChange(pmc, MCN_SELCHANGE);
- MCUpdateRcDayCur(pmc, pst);
- return(TRUE);
- }
- void MCSetToday(MONTHCAL* pmc, SYSTEMTIME* pst)
- {
- SYSTEMTIME st;
- RECT rc;
- if (!pst)
- {
- GetLocalTime(&st);
- pmc->fTodaySet = FALSE;
- }
- else
- {
- st = *pst;
- pmc->fTodaySet = TRUE;
- }
- if (CmpDate(&st, &pmc->stToday) != 0)
- {
- MCGetRcForDay(pmc, pmc->iMonthToday, pmc->stToday.wDay, &rc);
- InvalidateRect(pmc->ci.hwnd, &rc, FALSE);
- pmc->stToday = st;
- MCUpdateToday(pmc);
- MCGetRcForDay(pmc, pmc->iMonthToday, pmc->stToday.wDay, &rc);
- InvalidateRect(pmc->ci.hwnd, &rc, FALSE);
- if (MonthCal_ShowToday(pmc))
- {
- MCGetTodayBtnRect(pmc, &rc);
- InvalidateRect(pmc->ci.hwnd, &rc, FALSE);
- }
- UpdateWindow(pmc->ci.hwnd);
- }
- }
- LRESULT MCHandleTimer(MONTHCAL *pmc, WPARAM wParam)
- {
- if (wParam == CAL_IDAUTOSPIN)
- {
- int nDelta = pmc->fMonthDelta ? pmc->nMonthDelta : pmc->nMonths;
- // BUGBUG pass last parameter TRUE if multiselect! else you
- // can't multiselect across months
- MCIncrStartMonth(pmc, (pmc->fSpinPrev ? -nDelta : nDelta), FALSE);
- if (pmc->idTimer == 0)
- pmc->idTimer = SetTimer(pmc->ci.hwnd, CAL_IDAUTOSPIN, CAL_MSECAUTOSPIN, NULL);
- pmc->rcDayOld = pmc->rcDayCur;
- UpdateWindow(pmc->ci.hwnd);
- }
- else if (wParam == CAL_TODAYTIMER)
- {
- if (!pmc->fTodaySet)
- MCSetToday(pmc, NULL);
- }
- MCNotifySelChange(pmc, MCN_SELCHANGE); // our date has changed
- return((LRESULT)TRUE);
- }
- void MCInvalidateDates(MONTHCAL *pmc, SYSTEMTIME *pst1, SYSTEMTIME *pst2)
- {
- int iMonth, ioff, icol, irow;
- RECT rc, rcMonth;
- SYSTEMTIME st, stEnd;
- if (CmpDate(pst1, &pmc->stViewLast) > 0 ||
- CmpDate(pst2, &pmc->stViewFirst) < 0)
- return;
- if (CmpDate(pst1, &pmc->stViewFirst) < 0)
- CopyDate(pmc->stViewFirst, st);
- else
- CopyDate(*pst1, st);
- if (CmpDate(pst2, &pmc->stViewLast) > 0)
- CopyDate(pmc->stViewLast, stEnd);
- else
- CopyDate(*pst2, stEnd);
- iMonth = MCGetOffsetForYrMo(pmc, st.wYear, st.wMonth);
- if (iMonth == -1)
- {
- if (st.wMonth == pmc->stViewFirst.wMonth)
- {
- iMonth = 0;
- ioff = st.wDay - pmc->rgnDayUL[0] - 1;
- }
- else
- {
- iMonth = pmc->nMonths - 1;
- ioff = st.wDay + pmc->rgcDay[pmc->nMonths] +
- pmc->rgcDay[iMonth] - pmc->rgnDayUL[iMonth] - 1;
- }
- }
- else
- {
- ioff = st.wDay + (pmc->rgcDay[iMonth] - pmc->rgnDayUL[iMonth]) - 1;
- }
- MCGetRcForMonth(pmc, iMonth, &rcMonth);
- // TODO: this is bullshit. make it more efficient...
- while (CmpDate(&st, &stEnd) <= 0)
- {
- irow = ioff / CALCOLMAX;
- icol = ioff % CALCOLMAX;
- rc.left = rcMonth.left + pmc->rcDayNum.left + (pmc->dxCol * icol);
- rc.top = rcMonth.top + pmc->rcDayNum.top + (pmc->dyRow * irow);
- rc.right = rc.left + pmc->dxCol;
- rc.bottom = rc.top + pmc->dyRow;
- InvalidateRect(pmc->ci.hwnd, &rc, FALSE);
- IncrSystemTime(&st, &st, 1, INCRSYS_DAY);
- ioff++;
- if (st.wDay == 1)
- {
- if (st.wMonth != pmc->stMonthFirst.wMonth &&
- st.wMonth != pmc->stViewLast.wMonth)
- {
- iMonth++;
- MCGetRcForMonth(pmc, iMonth, &rcMonth);
- ioff = ioff % CALCOLMAX;
- }
- }
- }
- }
- void MCHandleMultiSelect(MONTHCAL *pmc, SYSTEMTIME *pst)
- {
- int i;
- DWORD cday;
- SYSTEMTIME stStart, stEnd;
- if (!pmc->fMultiSelecting)
- {
- CopyDate(*pst, stStart);
- CopyDate(*pst, stEnd);
- pmc->fMultiSelecting = TRUE;
- pmc->fForwardSelect = TRUE;
- CopyDate(pmc->st, pmc->stStartPrev);
- CopyDate(pmc->stEndSel, pmc->stEndPrev);
- }
- else
- {
- if (pmc->fForwardSelect)
- {
- i = CmpDate(pst, &pmc->st);
- if (i >= 0)
- {
- CopyDate(pmc->st, stStart);
- CopyDate(*pst, stEnd);
- }
- else
- {
- CopyDate(*pst, stStart);
- CopyDate(pmc->st, stEnd);
- pmc->fForwardSelect = FALSE;
- }
- }
- else
- {
- i = CmpDate(pst, &pmc->stEndSel);
- if (i < 0)
- {
- CopyDate(*pst, stStart);
- CopyDate(pmc->stEndSel, stEnd);
- }
- else
- {
- CopyDate(pmc->stEndSel, stStart);
- CopyDate(*pst, stEnd);
- pmc->fForwardSelect = TRUE;
- }
- }
- }
- // check to make sure not exceeding cSelMax
- cday = DaysBetweenDates(&stStart, &stEnd) + 1;
- if (cday > pmc->cSelMax)
- {
- if (pmc->fForwardSelect)
- IncrSystemTime(&stStart, &stEnd, pmc->cSelMax - 1, INCRSYS_DAY);
- else
- IncrSystemTime(&stEnd, &stStart, 1 - pmc->cSelMax, INCRSYS_DAY);
- }
- if (0 == CmpDate(&stStart, &pmc->st) &&
- 0 == CmpDate(&stEnd, &pmc->stEndSel))
- return;
- // TODO: do this more effeciently..
- MCInvalidateDates(pmc, &pmc->st, &pmc->stEndSel);
- MCInvalidateDates(pmc, &stStart, &stEnd);
- CopyDate(stStart, pmc->st);
- CopyDate(stEnd, pmc->stEndSel);
- MCNotifySelChange(pmc, MCN_SELCHANGE);
- UpdateWindow(pmc->ci.hwnd);
- }
- void MCGotoToday(MONTHCAL *pmc)
- {
- pmc->rcDayOld = pmc->rcDayCur;
- // force old selection to get repainted
- if (MonthCal_IsMultiSelect(pmc))
- MCInvalidateDates(pmc, &pmc->st, &pmc->stEndSel);
- else
- InvalidateRect(pmc->ci.hwnd, &pmc->rcDayOld, FALSE);
- MCSetDate(pmc, &pmc->stToday);
- MCNotifySelChange(pmc, MCN_SELECT);
- // force new selection to get repainted
- InvalidateRect(pmc->ci.hwnd, &pmc->rcDayCur, FALSE);
- UpdateWindow(pmc->ci.hwnd);
- }
- LRESULT MCContextMenu(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam)
- {
- POINT pt;
- int click;
- if (!pmc->fEnabled || !MonthCal_ShowToday(pmc))
- return(0);
- // ignore double click since this makes us advance twice
- // since we already had a leftdown before the leftdblclk
- if (!pmc->fCapture)
- {
- pt.x = GET_X_LPARAM(lParam);
- pt.y = GET_Y_LPARAM(lParam);
- //
- // If the context menu was generated from the keyboard,
- // then put it at the focus rectangle.
- //
- if (pt.x == -1 && pt.y == -1)
- {
- pt.x = (pmc->rcDayCur.left + pmc->rcDayCur.right ) / 2;
- pt.y = (pmc->rcDayCur.top + pmc->rcDayCur.bottom) / 2;
- ClientToScreen(pmc->ci.hwnd, &pt);
- }
- click = TrackPopupMenu(pmc->hmenuCtxt,
- TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
- pt.x, pt.y, 0, pmc->ci.hwnd, NULL);
- if (click >= 1)
- MCGotoToday(pmc);
- }
- return(0);
- }
- //
- // Computes the bounding rects for the month and the year in the title area of
- // the month.
- //
- void MCGetTitleRcsForOffset(MONTHCAL* pmc, int iOffset, LPRECT prcMonth, LPRECT prcYear)
- {
- RECT rcT;
- RECT rc;
- MCGetRcForMonth(pmc, iOffset, &rc);
- rcT.top = rc.top + (pmc->dyRow / 2);
- rcT.bottom = rcT.top + pmc->dyRow;
- rcT.left = rc.left + pmc->rcMonthName.left + pmc->rgmm[iOffset].rgi[IMM_MONTHSTART];
- rcT.right = rc.left + pmc->rcMonthName.left + pmc->rgmm[iOffset].rgi[IMM_MONTHEND];
- *prcMonth = rcT;
- rcT.left = rc.left + pmc->rcMonthName.left + pmc->rgmm[iOffset].rgi[IMM_YEARSTART];
- rcT.right = rc.left + pmc->rcMonthName.left + pmc->rgmm[iOffset].rgi[IMM_YEAREND];
- *prcYear = rcT;
- }
- LRESULT MCLButtonDown(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam)
- {
- HDC hdc;
- POINT pt;
- SYSTEMTIME st;
- RECT rc, rcCal;
- BOOL fShow;
- MSG msg;
- int offset, imonth, iyear;
- if (!pmc->fEnabled)
- return(0);
- pt.x = GET_X_LPARAM(lParam);
- pt.y = GET_Y_LPARAM(lParam);
- // treat a shift click like an LMouseDown at the prev location and
- // a MouseMove to the new location
- if (MonthCal_IsMultiSelect(pmc) && ((wParam & MK_SHIFT) == MK_SHIFT) && (!PtInRect(&pmc->rcDayCur, pt)))
- {
- SetCapture(pmc->ci.hwnd);
- pmc->fCapture = TRUE;
- pmc->fForwardSelect = (CmpDate(&pmc->stAnchor, &pmc->st) != 0) ? FALSE : TRUE;
- pmc->fMultiSelecting = TRUE;
- hdc = GetDC(pmc->ci.hwnd);
- DrawFocusRect(hdc, &pmc->rcDayCur); // draw focus rect
- pmc->fFocusDrawn = TRUE;
- ReleaseDC(pmc->ci.hwnd, hdc);
- MCMouseMove(pmc, wParam, lParam); // draw the highlight to new date
- return 0;
- }
- // ignore double click since this makes us advance twice
- // since we already had a leftdown before the leftdblclk
- if (!pmc->fCapture)
- {
- SetCapture(pmc->ci.hwnd);
- pmc->fCapture = TRUE;
- // check for spin buttons
- if ((pmc->fSpinPrev = (WORD) PtInRect(&pmc->rcPrev, pt)) || PtInRect(&pmc->rcNext, pt))
- {
- MCHandleTimer(pmc, CAL_IDAUTOSPIN);
- return(0);
- }
- // check for valid day
- pmc->rcDayOld = pmc->rcDayCur; // rcDayCur should always be valid now
- if (MonthCal_IsMultiSelect(pmc))
- {
- // need to cache these values because these are how
- // we determine if the selection has changed and we
- // need to notify the parent
- CopyDate(pmc->st, pmc->stStartPrev);
- CopyDate(pmc->stEndSel, pmc->stEndPrev);
- }
- if (FUpdateRcDayCur(pmc, pt))
- {
- if (MonthCal_IsMultiSelect(pmc))
- {
- if (FGetDateForPt(pmc, pt, &st, NULL, NULL, NULL, NULL))
- MCHandleMultiSelect(pmc, &st);
- }
- hdc = GetDC(pmc->ci.hwnd);
- DrawFocusRect(hdc, &pmc->rcDayCur); // draw focus rect
- pmc->fFocusDrawn = TRUE;
- ReleaseDC(pmc->ci.hwnd, hdc);
- CopyDate(st, pmc->stAnchor); // new Anchor point
- }
- else
- {
- RECT rcMonth, rcYear;
- int delta, year, month;
- // is this a click in the today area...
- if (MonthCal_ShowToday(pmc))
- {
- MCGetTodayBtnRect(pmc, &rc);
- if (PtInRect(&rc, pt))
- {
- CCReleaseCapture(&pmc->ci);
- pmc->fCapture = FALSE;
- MCGotoToday(pmc);
- return(0);
- }
- }
- // figure out if the click was in a month name or a year
- if (!FGetOffsetForPt(pmc, pt, &offset))
- return(0);
- GetYrMoForOffset(pmc, offset, &year, &month);
- // calculate where the month name and year are,
- // so we can figure out if they clicked in them...
- MCGetTitleRcsForOffset(pmc, offset, &rcMonth, &rcYear);
- delta = 0;
- if (PtInRect(&rcMonth, pt))
- {
- CCReleaseCapture(&pmc->ci);
- pmc->fCapture = FALSE;
- ClientToScreen(pmc->ci.hwnd, &pt);
- imonth = TrackPopupMenu(pmc->hmenuMonth,
- TPM_LEFTALIGN | TPM_TOPALIGN |
- TPM_NONOTIFY | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
- pt.x, pt.y, 0, pmc->ci.hwnd, NULL);
- if (imonth >= 1)
- delta = imonth - month;
- goto ChangeMonth;
- }
- if (PtInRect(&rcYear, pt))
- {
- HWND hwndEdit, hwndUD, hwndFocus;
- int yrMin, yrMax;
- DWORD dwExStyle = 0L;
- CCReleaseCapture(&pmc->ci);
- pmc->fCapture = FALSE;
- //
- // If the year is in a RTL string, then numeric control
- // is to the left.
- //
- if (pmc->fHeaderRTL)
- {
- rcYear.left = (rcYear.right - (pmc->dxYearMax + 6));
- }
- else
- {
- rcYear.right = rcYear.left + pmc->dxYearMax + 6;
- }
- rcYear.top--;
- rcYear.bottom++;
- if(((pmc->fHeaderRTL) && !(IS_WINDOW_RTL_MIRRORED(pmc->ci.hwnd))) ||
- (!(pmc->fHeaderRTL) && (IS_WINDOW_RTL_MIRRORED(pmc->ci.hwnd))))
- {
- // not mirrored force RTL, mirrored force LTR (for mirroring RTLis LTR!!)
- dwExStyle|= WS_EX_RTLREADING;
- }
- hwndEdit = CreateWindowEx(dwExStyle, TEXT("EDIT"), NULL,
- WS_CHILD | WS_VISIBLE | WS_BORDER | ES_READONLY | ES_LEFT | ES_AUTOHSCROLL,
- rcYear.left, rcYear.top, rcYear.right - rcYear.left, rcYear.bottom - rcYear.top,
- pmc->ci.hwnd, (HMENU)0, pmc->hinstance, NULL);
- if (hwndEdit == NULL)
- return(0);
- pmc->hwndEdit = hwndEdit;
- SendMessage(hwndEdit, WM_SETFONT, (WPARAM)pmc->hfontBold, (LPARAM)FALSE);
- SendMessage(hwndEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN,
- (LPARAM)MAKELONG(1, 1));
- MCUpdateEditYear(pmc);
- //
- // Convert from Gregorian to display years.
- //
- year = GregorianToOther(&pmc->ct, year);
- yrMin = GregorianToOther(&pmc->ct, pmc->stMin.wYear);
- yrMax = 9999;
- if (pmc->fMaxYrSet)
- yrMax = GregorianToOther(&pmc->ct, pmc->stMax.wYear);
- hwndUD = CreateUpDownControl(
- WS_CHILD | WS_VISIBLE | WS_BORDER |
- UDS_NOTHOUSANDS | UDS_ARROWKEYS,// | UDS_SETBUDDYINT,
- pmc->fHeaderRTL ? (rcYear.left - 1 - (rcYear.bottom-rcYear.top)): (rcYear.right + 1),
- rcYear.top,
- rcYear.bottom - rcYear.top, rcYear.bottom - rcYear.top, pmc->ci.hwnd,
- 1, pmc->hinstance, hwndEdit, yrMax, yrMin, year);
- if (hwndUD == NULL)
- {
- DestroyWindow(hwndEdit);
- return(0);
- }
- pmc->hwndUD = hwndUD;
- hwndFocus = SetFocus(hwndEdit);
- //
- // Widen the area depending on the string direction.
- //
- if (pmc->fHeaderRTL)
- rcYear.left -= (1 + rcYear.bottom - rcYear.top);
- else
- rcYear.right += 1 + rcYear.bottom - rcYear.top;
- // Use MapWindowRect, It works in a mirrored and unmirrored windows.
- MapWindowRect(pmc->ci.hwnd, NULL, (LPPOINT)&rcYear);
- rcCal = pmc->rc;
- MapWindowRect(pmc->ci.hwnd, NULL, (LPPOINT)&rcCal);
- fShow = TRUE;
- while (fShow && GetFocus() == hwndEdit)
- {
- if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
- {
- // Check for events that cause the calendar to go away
- if (msg.message == WM_KILLFOCUS ||
- (msg.message >= WM_SYSKEYDOWN &&
- msg.message <= WM_SYSDEADCHAR))
- {
- fShow = FALSE;
- }
- else if ((msg.message == WM_LBUTTONDOWN ||
- msg.message == WM_NCLBUTTONDOWN ||
- msg.message == WM_RBUTTONDOWN ||
- msg.message == WM_NCRBUTTONDOWN ||
- msg.message == WM_MBUTTONDOWN ||
- msg.message == WM_NCMBUTTONDOWN) &&
- !PtInRect(&rcYear, msg.pt))
- {
- fShow = FALSE;
- // if its a button down inside the calendar, eat it
- // so the calendar doesn't do anything strange when
- // the user is just trying to get rid of the year edit
- if (PtInRect(&rcCal, msg.pt))
- GetMessage(&msg, NULL, 0, 0);
- break; // do not dispatch
- }
- else if (msg.message == WM_QUIT)
- { // Don't dispatch a WM_QUIT; leave it in the queue
- break; // do not dispatch
- }
- else if (msg.message == WM_CHAR)
- {
- if (msg.wParam == VK_ESCAPE)
- {
- goto NoYearChange;
- }
- else if (msg.wParam == VK_RETURN)
- {
- fShow = FALSE;
- }
- }
- GetMessage(&msg, NULL, 0, 0);
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- else
- WaitMessage();
- }
- iyear = (int) SendMessage(hwndUD, UDM_GETPOS, 0, 0);
- if (HIWORD(iyear) == 0)
- delta = (iyear - year) * 12;
- NoYearChange:
- DestroyWindow(hwndUD);
- DestroyWindow(hwndEdit);
- pmc->hwndUD = NULL;
- pmc->hwndEdit = NULL;
- UpdateWindow(pmc->ci.hwnd);
- if (hwndFocus != NULL)
- SetFocus(hwndFocus);
- }
- ChangeMonth:
- if (delta != 0)
- {
- MCIncrStartMonth(pmc, delta, FALSE);
- MCNotifySelChange(pmc,MCN_SELCHANGE);
- }
- }
- }
- return(0);
- }
- LRESULT MCLButtonUp(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam)
- {
- HDC hdc;
- SYSTEMTIME st;
- POINT pt;
- if (pmc->fCapture)
- {
- CCReleaseCapture(&pmc->ci);
- pmc->fCapture = FALSE;
- if (pmc->idTimer)
- {
- KillTimer(pmc->ci.hwnd, pmc->idTimer);
- pmc->idTimer = 0;
- hdc = GetDC(pmc->ci.hwnd);
- MCPaintArrowBtn(pmc, hdc, pmc->fSpinPrev, FALSE);
- ReleaseDC(pmc->ci.hwnd, hdc);
- return(0);
- }
- if (pmc->fFocusDrawn)
- {
- hdc = GetDC(pmc->ci.hwnd);
- DrawFocusRect(hdc, &pmc->rcDayCur); // erase old focus rect
- pmc->fFocusDrawn = FALSE;
- ReleaseDC(pmc->ci.hwnd, hdc);
- }
- pt.x = GET_X_LPARAM(lParam);
- pt.y = GET_Y_LPARAM(lParam);
- if (MonthCal_IsMultiSelect(pmc))
- {
- FUpdateRcDayCur(pmc, pt);
- if (!EqualRect(&pmc->rcDayOld, &pmc->rcDayCur))
- {
- if (FGetDateForPt(pmc, pt, &st, NULL, NULL, NULL, NULL))
- MCHandleMultiSelect(pmc, &st);
- }
- pmc->fMultiSelecting = FALSE;
- if (0 != CmpDate(&pmc->stStartPrev, &pmc->st) ||
- 0 != CmpDate(&pmc->stEndPrev, &pmc->stEndSel))
- {
- FScrollIntoView(pmc);
- }
- MCNotifySelChange(pmc, MCN_SELECT);
- }
- else
- {
- if (FUpdateRcDayCur(pmc, pt))
- {
- if (!EqualRect(&pmc->rcDayOld, &pmc->rcDayCur) && (FGetDateForPt(pmc, pt, &st, NULL, NULL, NULL, NULL)))
- {
- InvalidateRect(pmc->ci.hwnd, &pmc->rcDayOld, FALSE);
- InvalidateRect(pmc->ci.hwnd, &pmc->rcDayCur, FALSE);
- MCSetDate(pmc, &st);
- }
- MCNotifySelChange(pmc, MCN_SELECT);
- }
- }
- }
- return(0);
- }
- LRESULT MCMouseMove(MONTHCAL *pmc, WPARAM wParam, LPARAM lParam)
- {
- BOOL fPrev;
- HDC hdc;
- POINT pt;
- SYSTEMTIME st;
- if (pmc->fCapture)
- {
- pt.x = GET_X_LPARAM(lParam);
- pt.y = GET_Y_LPARAM(lParam);
- // check spin buttons
- if ((fPrev = PtInRect(&pmc->rcPrev, pt)) || PtInRect(&pmc->rcNext, pt))
- {
- if (pmc->idTimer == 0)
- {
- pmc->fSpinPrev = (WORD) fPrev;
- MCHandleTimer(pmc, CAL_IDAUTOSPIN);
- }
- return(0);
- }