2 * Month calendar control
4 * Copyright 1998, 1999 Eric Kohl (ekohl@abo.rhein-zeitung.de)
5 * Copyright 1999 Alex Priem (alexp@sci.kun.nl)
11 * FIXME: refresh should ask for rect of required length. (?)
12 * FIXME: when pressing next/prev button, button should disappear
13 * until mouse is released. Should also set timer.
14 * FIXME: we refresh to often; especially in LButtonDown/MouseMove.
15 * FIXME: handle resources better (doesn't work now); also take care
16 of internationalization.
27 #include "debugtools.h"
29 DEFAULT_DEBUG_CHANNEL(monthcal)
31 /* take #days/month from ole/parsedt.c;
32 * we want full month-names, and abbreviated weekdays, so these are
36 char *monthtxt[] = {"January", "February", "March", "April", "May",
37 "June", "July", "August", "September", "October",
38 "November", "December"};
40 char *daytxt[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
41 int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
45 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongA (hwnd, 0))
48 * MONTHCAL_ValidateTime: is time a valid date/time combo?
52 /* CHECKME: all these validations OK? */
54 static int MONTHCAL_ValidateTime (SYSTEMTIME time)
57 if (time.wMonth > 12) return FALSE;
58 if (time.wDayOfWeek > 6) return FALSE;
59 if (time.wDay > mdays[time.wMonth]) return FALSE;
60 if (time.wMonth > 23) return FALSE;
61 if (time.wMinute > 60) return FALSE;
62 if (time.wSecond > 60) return FALSE;
63 if (time.wMilliseconds > 100) return FALSE;
67 static void MONTHCAL_CopyTime (const SYSTEMTIME *from, SYSTEMTIME *to)
70 to->wYear=from->wYear;
71 to->wMonth=from->wMonth;
72 to->wDayOfWeek=from->wDayOfWeek;
74 to->wHour=from->wHour;
75 to->wMinute=from->wMinute;
76 to->wSecond=from->wSecond;
77 to->wMilliseconds=from->wMilliseconds;
81 /* Note:Depending on DST, this may be offset by a day.
82 Need to find out if we're on a DST place & adjust the clock accordingly.
83 Above function assumes we have a valid data.
84 Valid for year>1752; d <= 1 <= 31, 1 <= m <= 12.
89 static int MONTHCAL_CalculateDayOfWeek (DWORD day, DWORD month, DWORD year)
93 return (year + year/4 - year/100 + year/400 +
94 DayOfWeekTable[month-1] + day - 1 ) % 7;
98 static int MONTHCAL_CalcDayFromPos (MONTHCAL_INFO *infoPtr, int x, int y)
101 int daypos,weekpos,retval,firstDay;
103 daypos=(x - infoPtr->prevmonth.left) / infoPtr->textWidth ;
104 weekpos=(y - infoPtr->days.bottom - infoPtr->rcClient.top) /
105 (infoPtr->textHeight*1.25);
106 firstDay=MONTHCAL_CalculateDayOfWeek (1,infoPtr->currentMonth,infoPtr->currentYear);
107 retval=daypos + 7*weekpos - firstDay;
108 TRACE ("%d %d %d\n",daypos,weekpos,retval);
113 static void MONTHCAL_CalcDayXY (MONTHCAL_INFO *infoPtr, int day, int month,
117 int firstDay,prevMonth;
119 firstDay=MONTHCAL_CalculateDayOfWeek (1,infoPtr->currentMonth,infoPtr->currentYear);
121 if (month==infoPtr->currentMonth) {
122 *x=(day+firstDay) & 7;
123 *y=(day+firstDay-*x) / 7;
126 if (month < infoPtr->currentMonth) {
128 if (prevMonth==0) prevMonth=11;
129 *x=(mdays[prevMonth]-firstDay) & 7;
134 *x=(day+firstDay+mdays[month]) & 7;
138 static void MONTHCAL_CalcDayRect (MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
140 r->left = infoPtr->prevmonth.left + x * infoPtr->textWidth;
141 r->right = r->left + infoPtr->textWidth;
142 r->top = infoPtr->rcClient.top + y * 1.25 * infoPtr->textHeight
143 + infoPtr->days.bottom;
144 r->bottom = r->top + infoPtr->textHeight;
148 static inline void MONTHCAL_CalcPosFromDay (MONTHCAL_INFO *infoPtr,
149 int day, int month, RECT *r)
154 MONTHCAL_CalcDayXY (infoPtr, day, month, &x, &y);
155 MONTHCAL_CalcDayRect (infoPtr, r, x, y);
161 static void MONTHCAL_CircleDay (HDC hdc, MONTHCAL_INFO *infoPtr, int i, int j)
164 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB (255,0,0) );
165 HPEN hOldPen2 = SelectObject( hdc, hRedPen );
169 /* use prevmonth to calculate position because it contains the extra width
170 * from MCS_WEEKNUMBERS
173 x=infoPtr->prevmonth.left + (i+0.5)*infoPtr->textWidth;
174 y=infoPtr->rcClient.top + 1.25*(j+0.5)*infoPtr->textHeight + infoPtr->days.bottom;
176 points[0].y = y-0.25*infoPtr->textHeight;
177 points[1].x = x-1.0*infoPtr->textWidth;
180 points[2].y = y+0.6*infoPtr->textHeight;
181 points[3].x = x+0.5*infoPtr->textWidth;
183 points[4].x = x+0.3*infoPtr->textWidth;
184 points[4].y = y-0.5*infoPtr->textHeight;
185 points[5].x = x-0.25*infoPtr->textWidth;
186 points[5].y = y-0.5*infoPtr->textHeight;
187 points[6].x = x-0.5*infoPtr->textWidth;
188 points[6].y = y-0.45*infoPtr->textHeight;
190 PolyBezier (hdc,points,4);
191 PolyBezier (hdc,points+3,4);
192 DeleteObject (hRedPen);
193 SelectObject (hdc, hOldPen2);
200 static void MONTHCAL_DrawDay (HDC hdc, MONTHCAL_INFO *infoPtr,
201 int day, int month, int x, int y, int bold)
206 static int haveBoldFont,haveSelectedDay=FALSE;
208 COLORREF oldCol,oldBk;
210 sprintf (buf,"%d",day);
214 /* No need to check styles: when selection is not valid, it is set to zero.
215 * 1<day<31, so evertyhing's OK.
218 MONTHCAL_CalcDayRect (infoPtr, &r, x, y);
220 if ((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
221 && (month==infoPtr->currentMonth)) {
225 TRACE ("%d %d %d\n",day,infoPtr->minSel.wDay,infoPtr->maxSel.wDay);
226 TRACE ("%d %d %d %d\n", r.left, r.top, r.right, r.bottom);
227 oldCol=SetTextColor (hdc, infoPtr->monthbk);
228 oldBk=SetBkColor (hdc,infoPtr->trailingtxt);
229 hbr= GetSysColorBrush (COLOR_GRAYTEXT);
230 hrgn=CreateEllipticRgn (r.left,r.top, r.right,r.bottom);
231 FillRgn (hdc,hrgn,hbr);
233 r2.left = r.left-0.25*infoPtr->textWidth;
235 r2.right = r.left+0.5*infoPtr->textWidth;
236 r2.bottom = r.bottom;
237 if (haveSelectedDay) FillRect (hdc,&r2,hbr);
238 haveSelectedDay=TRUE;
240 haveSelectedDay=FALSE;
245 /* need to add some code for multiple selections */
247 if ((bold) && (!haveBoldFont)) {
248 SelectObject (hdc, infoPtr->hBoldFont);
251 if ((!bold) && (haveBoldFont)) {
252 SelectObject (hdc, infoPtr->hFont);
258 DrawTextA ( hdc, buf, lstrlenA(buf), &r,
259 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
260 if (haveSelectedDay) {
261 SetTextColor(hdc, oldCol);
262 SetBkColor (hdc, oldBk);
265 if ((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth)) {
266 HPEN hNewPen, hOldPen;
268 hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
269 hbr= GetSysColorBrush (COLOR_WINDOWTEXT);
270 hOldPen = SelectObject( hdc, hNewPen );
275 FrameRect (hdc, &r, hbr);
276 SelectObject( hdc, hOldPen );
281 /* CHECKME: For `todays date', do we need to check the locale?*/
282 /* CHECKME: For `todays date', how do is Y2K handled?*/
283 /* FIXME: todays date circle */
285 static void MONTHCAL_Refresh (HWND hwnd, HDC hdc)
288 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
289 RECT *rcClient=&infoPtr->rcClient;
290 RECT *title=&infoPtr->title;
291 RECT *prev=&infoPtr->titlebtnprev;
292 RECT *next=&infoPtr->titlebtnnext;
293 RECT *titlemonth=&infoPtr->titlemonth;
294 RECT *titleyear=&infoPtr->titleyear;
295 RECT *prevmonth=&infoPtr->prevmonth;
296 RECT *nextmonth=&infoPtr->nextmonth;
297 RECT *days=&infoPtr->days;
298 RECT *weeknums=&infoPtr->weeknums;
299 RECT *rtoday=&infoPtr->today;
300 int i,j,m,mask,day,firstDay, weeknum,prevMonth;
301 int textHeight,textWidth;
307 char buf[20],*thisMonthtxt;
308 COLORREF oldTextColor,oldBkColor;
309 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
313 oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
315 currentFont = SelectObject (hdc, infoPtr->hFont);
317 /* FIXME: need a way to determine current font, without setting it */
319 if (infoPtr->hFont!=currentFont) {
320 SelectObject (hdc, currentFont);
321 infoPtr->hFont=currentFont;
322 GetObjectA (currentFont, sizeof (LOGFONTA), &logFont);
323 logFont.lfWeight=FW_BOLD;
324 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
328 GetTextMetricsA (hdc, &tm);
329 infoPtr->textHeight=textHeight=tm.tmHeight + tm.tmExternalLeading;
330 GetTextExtentPoint32A (hdc, "Sun",3, &size);
331 infoPtr->textWidth=textWidth=size.cx+2;
333 GetClientRect (hwnd, rcClient);
334 hbr = CreateSolidBrush (RGB(255,255,255));
335 DrawEdge (hdc, rcClient, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
336 FillRect (hdc, rcClient, hbr);
339 /* calculate whole client area & title area */
341 infoPtr->rcClient.right=7*infoPtr->textWidth;
342 if (dwStyle & MCS_WEEKNUMBERS)
343 infoPtr->rcClient.right+=infoPtr->textWidth;
345 title->top = rcClient->top + 1;
346 title->bottom = title->top + 2*textHeight + 4;
347 title->left = rcClient->left + 1;
348 title->right = rcClient->right - 1;
349 infoPtr->rcClient.bottom=title->bottom + 6*textHeight;
354 hbr = CreateSolidBrush (infoPtr->titlebk);
355 FillRect (hdc, title, hbr);
357 prev->top = next->top = title->top + 6;
358 prev->bottom = next->bottom = title->top + 2*textHeight - 3;
359 prev->right = title->left + 28;
360 prev->left = title->left + 4;
361 next->left = title->right - 28;
362 next->right = title->right - 4;
363 titlemonth->bottom= titleyear->bottom = prev->top + 2*textHeight - 3;
364 titlemonth->top = titleyear->top = title->top + 6;
365 titlemonth->left = title->left;
366 titlemonth->right = title->right;
369 DrawFrameControl(hdc, prev, DFC_SCROLL,
370 DFCS_SCROLLLEFT | (prssed ? DFCS_PUSHED : 0) |
371 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
374 DrawFrameControl(hdc, next, DFC_SCROLL,
375 DFCS_SCROLLRIGHT | (prssed ? DFCS_PUSHED : 0) |
376 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
378 oldBkColor=SetBkColor (hdc,infoPtr->titlebk);
379 SetTextColor(hdc, infoPtr->titletxt);
380 SelectObject (hdc, infoPtr->hBoldFont);
382 thisMonthtxt=monthtxt[infoPtr->currentMonth - 1];
383 sprintf (buf,"%s %ld",thisMonthtxt,infoPtr->currentYear);
384 DrawTextA ( hdc, buf, strlen(buf), titlemonth,
385 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
386 SelectObject (hdc, infoPtr->hFont);
388 /* titlemonth left/right contained rect for whole titletxt ('June 1999')
389 * MCM_HitTestInfo wants month & year rects, so prepare these now.
390 * (no, we can't draw them separately; the whole text is centered)
393 GetTextExtentPoint32A (hdc, buf,lstrlenA (buf), &size);
394 titlemonth->left = title->right/2 - size.cx/2;
395 titleyear->right = title->right/2 + size.cx/2;
396 GetTextExtentPoint32A (hdc, thisMonthtxt,lstrlenA (thisMonthtxt), &size);
397 titlemonth->right= titlemonth->left+size.cx;
398 titleyear->right = titlemonth->right;
400 /* draw line under day abbreviatons */
402 MoveToEx (hdc, rcClient->left+3, title->bottom + textHeight + 2, NULL);
403 LineTo (hdc, rcClient->right-3, title->bottom + textHeight + 2);
405 /* draw day abbreviations */
407 SetBkColor (hdc, infoPtr->monthbk);
408 SetTextColor(hdc, infoPtr->trailingtxt);
410 days->left = rcClient->left;
411 if (dwStyle & MCS_WEEKNUMBERS) days->left+=textWidth;
412 days->right = days->left + textWidth;
413 days->top = title->bottom + 2;
414 days->bottom = title->bottom + textHeight + 2;
417 for (j=0; j<7; j++) {
418 DrawTextA ( hdc, daytxt[i], strlen(daytxt[i]), days,
419 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
422 days->left+=textWidth;
423 days->right+=textWidth;
426 days->left = rcClient->left + j;
427 if (dwStyle & MCS_WEEKNUMBERS) days->left+=textWidth;
428 days->right = rcClient->left + (j+1)*textWidth-2;
430 /* draw day numbers; first, the previous month */
433 if (dwStyle & MCS_WEEKNUMBERS) prevmonth->left=textWidth;
435 firstDay=MONTHCAL_CalculateDayOfWeek (1,infoPtr->currentMonth,infoPtr->currentYear);
436 prevMonth=infoPtr->currentMonth-1;
437 if (prevMonth==0) prevMonth=11;
438 day=mdays[prevMonth]-firstDay;
443 while (day<=mdays[prevMonth]) {
444 MONTHCAL_DrawDay (hdc, infoPtr, day, prevMonth, i, 0,
445 infoPtr->monthdayState[m] & mask);
451 prevmonth->right = prevmonth->left+i*textWidth;
452 prevmonth->top = days->bottom;
453 prevmonth->bottom= prevmonth->top + textHeight;
455 /* draw `current' month */
458 infoPtr->firstDayplace=i;
459 SetTextColor(hdc, infoPtr->txt);
463 MONTHCAL_DrawDay (hdc, infoPtr, day, infoPtr->currentMonth, i, 0,
464 infoPtr->monthdayState[m] & mask);
465 if ((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
466 (day==infoPtr->todaysDate.wDay))
467 MONTHCAL_CircleDay (hdc, infoPtr, i,j);
475 while (day<=mdays[infoPtr->currentMonth]) {
476 MONTHCAL_DrawDay (hdc, infoPtr, day, infoPtr->currentMonth, i, j,
477 infoPtr->monthdayState[m] & mask);
478 if ((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
479 (day==infoPtr->todaysDate.wDay))
480 MONTHCAL_CircleDay (hdc, infoPtr, i,j);
490 /* draw `next' month */
492 /* note: the nextmonth rect only hints for the `half-week' that needs to be
493 * drawn to complete the current week. An eventual next week that needs to
494 * be drawn to complete the month calendar is not taken into account in
495 * this rect -- HitTest knows about this.*/
499 nextmonth->left = prevmonth->left+i*textWidth;
500 nextmonth->right = rcClient->right;
501 nextmonth->top = days->bottom+(j+1)*textHeight;
502 nextmonth->bottom = nextmonth->top + textHeight;
507 SetTextColor(hdc, infoPtr->trailingtxt);
508 while ((i<7) && (j<6)) {
509 MONTHCAL_DrawDay (hdc, infoPtr, day, infoPtr->currentMonth+1, i, j,
510 infoPtr->monthdayState[m] & mask);
519 SetTextColor(hdc, infoPtr->txt);
523 /* draw `today' date if style allows it, and draw a circle before today's
524 * date if necessairy */
526 if (!( dwStyle & MCS_NOTODAY)) {
528 if (!( dwStyle & MCS_NOTODAYCIRCLE)) {
529 MONTHCAL_CircleDay (hdc, infoPtr, 0, 6);
533 MONTHCAL_CalcDayRect (infoPtr, rtoday, offset==textWidth, 6);
534 sprintf (buf,"Today: %d/%d/%d",infoPtr->todaysDate.wMonth,
535 infoPtr->todaysDate.wDay, infoPtr->todaysDate.wYear-1900);
536 rtoday->right = rcClient->right;
537 SelectObject (hdc, infoPtr->hBoldFont);
538 DrawTextA ( hdc, buf, lstrlenA(buf), rtoday,
539 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
540 SelectObject (hdc, infoPtr->hFont);
543 if (dwStyle & MCS_WEEKNUMBERS) {
544 /* display weeknumbers*/
547 weeknums->right = textWidth;
548 weeknums->top = days->bottom;
549 weeknums->bottom = days->bottom + textHeight;
552 for (i=0; i<infoPtr->currentMonth; i++)
556 for (i=0; i<6; i++) {
557 sprintf (buf,"%d",weeknum);
558 DrawTextA ( hdc, buf, lstrlenA(buf), weeknums,
559 DT_CENTER | DT_BOTTOM | DT_SINGLELINE );
560 weeknums->bottom+=textHeight;
563 MoveToEx (hdc, weeknums->right+3, weeknums->top, NULL);
564 LineTo (hdc, weeknums->right-3, weeknums->bottom);
568 /* currentFont was font at entering Refresh */
570 SetBkColor (hdc, oldBkColor);
571 SelectObject (hdc, currentFont);
572 SetTextColor (hdc, oldTextColor);
577 MONTHCAL_GetMinReqRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
580 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
581 LPRECT lpRect=(LPRECT) lParam;
582 TRACE ("%x %lx\n",wParam,lParam);
584 /* validate parameters */
586 if ( (infoPtr==NULL) || (lpRect == NULL) ) return FALSE;
588 lpRect->left=infoPtr->rcClient.left;
589 lpRect->right=infoPtr->rcClient.right;
590 lpRect->top=infoPtr->rcClient.top;
591 lpRect->bottom=infoPtr->rcClient.bottom;
596 MONTHCAL_GetColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
599 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
601 TRACE ("%x %lx\n",wParam,lParam);
603 switch ((int)wParam) {
604 case MCSC_BACKGROUND:
609 return infoPtr->titlebk;
611 return infoPtr->titletxt;
613 return infoPtr->monthbk;
614 case MCSC_TRAILINGTEXT:
615 return infoPtr->trailingtxt;
622 MONTHCAL_SetColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
625 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
628 TRACE ("%x %lx\n",wParam,lParam);
630 switch ((int)wParam) {
631 case MCSC_BACKGROUND:
633 infoPtr->bk=(COLORREF) lParam;
637 infoPtr->txt=(COLORREF) lParam;
640 prev=infoPtr->titlebk;
641 infoPtr->titlebk=(COLORREF) lParam;
644 prev=infoPtr->titletxt;
645 infoPtr->titletxt=(COLORREF) lParam;
648 prev=infoPtr->monthbk;
649 infoPtr->monthbk=(COLORREF) lParam;
651 case MCSC_TRAILINGTEXT:
652 prev=infoPtr->trailingtxt;
653 infoPtr->trailingtxt=(COLORREF) lParam;
661 MONTHCAL_GetMonthDelta (HWND hwnd, WPARAM wParam, LPARAM lParam)
664 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
666 TRACE ("%x %lx\n",wParam,lParam);
668 if (infoPtr->delta) return infoPtr->delta;
669 else return infoPtr->visible;
673 MONTHCAL_SetMonthDelta (HWND hwnd, WPARAM wParam, LPARAM lParam)
676 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
677 int prev=infoPtr->delta;
679 TRACE ("%x %lx\n",wParam,lParam);
681 infoPtr->delta=(int) wParam;
688 MONTHCAL_GetFirstDayOfWeek (HWND hwnd, WPARAM wParam, LPARAM lParam)
690 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
692 return infoPtr->firstDay;
695 /* FIXME: we need more error checking here */
698 MONTHCAL_SetFirstDayOfWeek (HWND hwnd, WPARAM wParam, LPARAM lParam)
701 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
702 int prev=infoPtr->firstDay;
706 TRACE ("%x %lx\n",wParam,lParam);
708 if ((lParam>=0) && (lParam<7)) {
709 infoPtr->firstDay=(int) lParam;
710 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK,
712 TRACE ("%s %d\n",buf,strlen(buf));
713 if ((sscanf(buf,"%d",&day)==1) && (infoPtr->firstDay!=day))
714 infoPtr->firstDay|=HIWORD(TRUE);
722 /* FIXME: fill this in */
725 MONTHCAL_GetMonthRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
727 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
729 TRACE ("%x %lx\n",wParam,lParam);
731 return infoPtr->monthRange;
735 MONTHCAL_GetMaxTodayWidth (HWND hwnd)
738 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
740 return (infoPtr->today.right-infoPtr->today.left);
743 /* FIXME: are validated times taken from current date/time or simply
745 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
746 * adjusting range with MCM_SETRANGE
750 MONTHCAL_SetRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
752 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
753 SYSTEMTIME lprgSysTimeArray[1];
756 TRACE ("%x %lx\n",wParam,lParam);
758 if (wParam & GDTR_MAX) {
759 if (MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
760 MONTHCAL_CopyTime (&lprgSysTimeArray[1],&infoPtr->maxDate);
761 infoPtr->rangeValid|=GDTR_MAX;
763 GetSystemTime (&infoPtr->todaysDate);
764 MONTHCAL_CopyTime (&infoPtr->todaysDate,&infoPtr->maxDate);
767 if (wParam & GDTR_MIN) {
768 if (MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
769 MONTHCAL_CopyTime (&lprgSysTimeArray[0],&infoPtr->maxDate);
770 infoPtr->rangeValid|=GDTR_MIN;
772 GetSystemTime (&infoPtr->todaysDate);
773 MONTHCAL_CopyTime (&infoPtr->todaysDate,&infoPtr->maxDate);
777 prev=infoPtr->monthRange;
778 infoPtr->monthRange=infoPtr->maxDate.wMonth-infoPtr->minDate.wMonth;
779 if (infoPtr->monthRange!=prev)
780 COMCTL32_ReAlloc (infoPtr->monthdayState,
781 infoPtr->monthRange*sizeof(MONTHDAYSTATE));
787 /* CHECKME: At the moment, we copy ranges anyway,regardless of
788 * infoPtr->rangeValid; a invalid range is simply filled with zeros in
789 * SetRange. Is this the right behavior?
793 MONTHCAL_GetRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
795 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
796 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
798 /* validate parameters */
800 if ( (infoPtr==NULL) || (lprgSysTimeArray==NULL) ) return FALSE;
802 MONTHCAL_CopyTime (&infoPtr->maxDate,&lprgSysTimeArray[1]);
803 MONTHCAL_CopyTime (&infoPtr->minDate,&lprgSysTimeArray[0]);
805 return infoPtr->rangeValid;
809 MONTHCAL_SetDayState (HWND hwnd, WPARAM wParam, LPARAM lParam)
812 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
813 int i,iMonths=(int) wParam;
814 MONTHDAYSTATE *dayStates=(LPMONTHDAYSTATE) lParam;
816 TRACE ("%x %lx\n",wParam,lParam);
817 if (iMonths!=infoPtr->monthRange) return 0;
819 for (i=0; i<iMonths; i++)
820 infoPtr->monthdayState[i]=dayStates[i];
825 MONTHCAL_GetCurSel (HWND hwnd, WPARAM wParam, LPARAM lParam)
827 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
828 SYSTEMTIME *lpSel=(SYSTEMTIME *) lParam;
830 TRACE ("%x %lx\n",wParam,lParam);
831 if ( (infoPtr==NULL) || (lpSel==NULL) ) return FALSE;
832 if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
834 MONTHCAL_CopyTime (&infoPtr->minSel,lpSel);
839 /* FIXME: if the specified date is not visible, make it visible */
843 MONTHCAL_SetCurSel (HWND hwnd, WPARAM wParam, LPARAM lParam)
845 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
846 SYSTEMTIME *lpSel=(SYSTEMTIME *) lParam;
848 TRACE ("%x %lx\n",wParam,lParam);
849 if ( (infoPtr==NULL) || (lpSel==NULL) ) return FALSE;
850 if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
852 TRACE ("%d %d\n",lpSel->wMonth,lpSel->wDay);
854 MONTHCAL_CopyTime (lpSel,&infoPtr->minSel);
855 MONTHCAL_CopyTime (lpSel,&infoPtr->maxSel);
861 MONTHCAL_GetMaxSelCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
863 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
865 TRACE ("%x %lx\n",wParam,lParam);
866 return infoPtr->maxSelCount;
870 MONTHCAL_SetMaxSelCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
872 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
874 TRACE ("%x %lx\n",wParam,lParam);
875 if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT) {
876 infoPtr->maxSelCount=wParam;
884 MONTHCAL_GetSelRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
886 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
887 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
889 TRACE ("%x %lx\n",wParam,lParam);
891 /* validate parameters */
893 if ( (infoPtr==NULL) || (lprgSysTimeArray==NULL) ) return FALSE;
895 if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT) {
896 MONTHCAL_CopyTime (&infoPtr->maxSel,&lprgSysTimeArray[1]);
897 MONTHCAL_CopyTime (&infoPtr->minSel,&lprgSysTimeArray[0]);
898 TRACE ("[min,max]=[%d %d]\n",infoPtr->minSel.wDay,infoPtr->maxSel.wDay);
906 MONTHCAL_SetSelRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
908 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
909 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *) lParam;
911 TRACE ("%x %lx\n",wParam,lParam);
913 /* validate parameters */
915 if ( (infoPtr==NULL) || (lprgSysTimeArray==NULL) ) return FALSE;
917 if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT) {
918 infoPtr->selValid=TRUE;
919 MONTHCAL_CopyTime (&lprgSysTimeArray[1],&infoPtr->maxSel);
920 MONTHCAL_CopyTime (&lprgSysTimeArray[0],&infoPtr->minSel);
921 TRACE ("[min,max]=[%d %d]\n",infoPtr->minSel.wDay,infoPtr->maxSel.wDay);
932 MONTHCAL_GetToday (HWND hwnd, WPARAM wParam, LPARAM lParam)
934 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
935 SYSTEMTIME *lpToday=(SYSTEMTIME *) lParam;
937 TRACE ("%x %lx\n",wParam,lParam);
939 /* validate parameters */
941 if ( (infoPtr==NULL) || (lpToday==NULL) ) return FALSE;
942 MONTHCAL_CopyTime (&infoPtr->todaysDate,lpToday);
948 static int MONTHCAL_inbox (int x, int y, RECT r)
951 // TRACE ("%d %d [%d %d %d %d]\n",x,y,r.top,r.bottom,r.left,r.right);
953 if ((y>r.top) && (y<r.bottom) && (x>r.left) && (x<r.right)) return TRUE;
960 MONTHCAL_HitTest (HWND hwnd, LPARAM lParam)
962 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
963 PMCHITTESTINFO lpht=(PMCHITTESTINFO) lParam;
972 /* are we in the header? */
974 if (MONTHCAL_inbox (x,y,infoPtr->title)) {
976 if (MONTHCAL_inbox (x,y,infoPtr->titlebtnprev)) {
977 retval=MCHT_TITLEBTNPREV;
980 if (MONTHCAL_inbox (x,y,infoPtr->titlebtnnext)) {
981 retval=MCHT_TITLEBTNNEXT;
984 if (MONTHCAL_inbox (x,y,infoPtr->titlemonth)) {
985 retval=MCHT_TITLEMONTH;
988 if (MONTHCAL_inbox (x,y,infoPtr->titleyear)) {
989 retval=MCHT_TITLEYEAR;
996 if (MONTHCAL_inbox (x,y,infoPtr->days)) {
997 retval=MCHT_CALENDARDAY; /* FIXME: find out which day we're on */
1000 if (MONTHCAL_inbox (x,y,infoPtr->weeknums)) {
1001 retval=MCHT_CALENDARWEEKNUM;/* FIXME: find out which day we're on */
1004 if (MONTHCAL_inbox (x,y,infoPtr->prevmonth)) {
1005 retval=MCHT_CALENDARDATEPREV;
1008 if (MONTHCAL_inbox (x,y,infoPtr->nextmonth) ||
1009 ((x>infoPtr->nextmonth.left) && (x<infoPtr->nextmonth.right) &&
1010 (y>infoPtr->nextmonth.bottom) && (y<infoPtr->today.top))) {
1011 retval=MCHT_CALENDARDATENEXT;
1016 if (MONTHCAL_inbox (x,y,infoPtr->today)) {
1017 retval=MCHT_TODAYLINK;
1021 /* MCHT_CALENDARDATE determination: since the next & previous month have
1022 * been handled already (MCHT_CALENDARDATEPREV/NEXT), we only have to check
1023 * whether we're in the calendar area. infoPtr->prevMonth.left handles the
1024 * MCS_WEEKNUMBERS style nicely.
1028 TRACE ("%d %d [%d %d %d %d] [%d %d %d %d]\n",x,y,
1029 infoPtr->prevmonth.left, infoPtr->prevmonth.right,
1030 infoPtr->prevmonth.top, infoPtr->prevmonth.bottom,
1031 infoPtr->nextmonth.left, infoPtr->nextmonth.right,
1032 infoPtr->nextmonth.top, infoPtr->nextmonth.bottom);
1034 if ((x>infoPtr->prevmonth.left) && (x<infoPtr->nextmonth.right) &&
1035 (y>infoPtr->prevmonth.top) && (y<infoPtr->nextmonth.bottom)) {
1036 lpht->st.wYear=infoPtr->currentYear;
1037 lpht->st.wMonth=infoPtr->currentMonth;
1039 lpht->st.wDay=MONTHCAL_CalcDayFromPos (infoPtr,x,y);
1041 TRACE ("day hit: %d\n",lpht->st.wDay);
1042 retval=MCHT_CALENDARDATE;
1047 /* Hit nothing special? What's left must be background :-) */
1049 retval=MCHT_CALENDARBK;
1058 MONTHCAL_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1061 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr (hwnd);
1069 TRACE ("%x %lx\n",wParam,lParam);
1071 ht.pt.x = (INT)LOWORD(lParam);
1072 ht.pt.y = (INT)HIWORD(lParam);
1073 hit=MONTHCAL_HitTest (hwnd, (LPARAM) &ht);
1077 if (hit & MCHT_NEXT){
1078 infoPtr->currentMonth++;
1079 if (infoPtr->currentMonth>12) {
1080 infoPtr->currentYear++;
1081 infoPtr->currentMonth=1;
1084 if (hit & MCHT_PREV) {
1085 infoPtr->currentMonth--;
1086 if (infoPtr->currentMonth<1) {
1087 infoPtr->currentYear--;
1088 infoPtr->currentMonth=12;
1092 if (hit == MCHT_TITLEMONTH) {
1094 HRSRC hrsrc = FindResourceA( COMCTL32_hModule, MAKEINTRESOURCEA(IDD_MCMONTHMENU), RT_MENUA );
1096 TRACE ("returning zero\n");
1099 TRACE ("resource is:%x\n",hrsrc);
1100 hMenu=LoadMenuIndirectA( (LPCVOID)LoadResource( COMCTL32_hModule, hrsrc ));
1102 TRACE ("menu is:%x\n",hMenu);
1105 hMenu=CreateMenu ();
1106 AppendMenuA (hMenu,MF_STRING,IDM_JAN,"January");
1107 AppendMenuA (hMenu,MF_STRING,IDM_FEB,"February");
1108 AppendMenuA (hMenu,MF_STRING,IDM_MAR,"March");
1110 retval=CreateWindowA (POPUPMENU_CLASS_ATOM, NULL,
1111 WS_CHILD | WS_VISIBLE,
1113 hwnd, hMenu, GetWindowLongA (hwnd, GWL_HINSTANCE), NULL);
1114 TRACE ("hwnd returned:%x\n",retval);
1117 if (hit == MCHT_TITLEYEAR) {
1118 FIXME ("create updown for yearselection\n");
1120 if (hit == MCHT_TODAYLINK) {
1121 FIXME ("set currentday\n");
1123 if (hit == MCHT_CALENDARDATE) {
1124 SYSTEMTIME selArray[2];
1127 MONTHCAL_CopyTime (&ht.st, &selArray[0]);
1128 MONTHCAL_CopyTime (&ht.st, &selArray[1]);
1129 MONTHCAL_SetSelRange (hwnd,0,(LPARAM) &selArray);
1131 infoPtr->firstSelDay=ht.st.wDay;
1132 infoPtr->curSelDay=ht.st.wDay;
1133 infoPtr->selValid=MC_SEL_LBUTDOWN;
1136 MONTHCAL_Refresh (hwnd,hdc);
1137 ReleaseDC (hwnd,hdc);
1142 MONTHCAL_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1145 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr (hwnd);
1147 infoPtr->selValid=MC_SEL_LBUTUP;
1148 infoPtr->curSelDay=0;
1154 MONTHCAL_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
1156 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr (hwnd);
1162 if (infoPtr->selValid!=MC_SEL_LBUTDOWN) return 0;
1164 ht.pt.x=LOWORD(lParam);
1165 ht.pt.y=HIWORD(lParam);
1167 hit=MONTHCAL_HitTest (hwnd, (LPARAM) &ht);
1169 /* not on the calendar date numbers? bail out */
1170 TRACE ("hit:%x\n",hit);
1171 if ((hit & MCHT_CALENDARDATE) !=MCHT_CALENDARDATE) return 0;
1174 infoPtr->curSelDay=selday;
1175 MONTHCAL_CalcPosFromDay (infoPtr,selday,ht.st.wMonth,&r);
1177 if ( GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1178 SYSTEMTIME selArray[2];
1181 MONTHCAL_GetSelRange (hwnd,0,(LPARAM) &selArray);
1183 if (infoPtr->firstSelDay==selArray[0].wDay) i=1;
1184 TRACE ("oldRange:%d %d %d %d\n",infoPtr->firstSelDay,selArray[0].wDay,selArray[1].wDay,i);
1185 if (infoPtr->firstSelDay==selArray[1].wDay) {
1186 /* 1st time we get here: selArray[0]=selArray[1]) */
1187 /* if we're still at the first selected date, return */
1188 if (infoPtr->firstSelDay==selday) goto done;
1190 if (selday<infoPtr->firstSelDay) i=0;
1193 if (abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1194 if (selday>infoPtr->firstSelDay)
1195 selday=infoPtr->firstSelDay+infoPtr->maxSelCount;
1197 selday=infoPtr->firstSelDay-infoPtr->maxSelCount;
1200 if (selArray[i].wDay!=selday) {
1202 TRACE ("newRange:%d %d %d %d\n",infoPtr->firstSelDay,selArray[0].wDay,selArray[1].wDay,i);
1204 selArray[i].wDay=selday;
1207 if (selArray[0].wDay>selArray[1].wDay) {
1209 tempday=selArray[1].wDay;
1210 selArray[1].wDay=selArray[0].wDay;
1211 selArray[0].wDay=tempday;
1214 MONTHCAL_SetSelRange (hwnd,0,(LPARAM) &selArray);
1221 MONTHCAL_Refresh (hwnd, hdc);
1222 ReleaseDC (hwnd, hdc);
1228 MONTHCAL_Paint (HWND hwnd, WPARAM wParam)
1233 hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
1234 MONTHCAL_Refresh (hwnd, hdc);
1236 EndPaint (hwnd, &ps);
1241 MONTHCAL_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
1248 MONTHCAL_Refresh (hwnd, hdc);
1249 ReleaseDC (hwnd, hdc);
1250 InvalidateRect (hwnd, NULL, TRUE);
1257 MONTHCAL_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
1264 MONTHCAL_Refresh (hwnd, hdc);
1265 ReleaseDC (hwnd, hdc);
1271 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1275 MONTHCAL_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
1277 MONTHCAL_INFO *infoPtr;
1280 /* allocate memory for info structure */
1281 infoPtr = (MONTHCAL_INFO *)COMCTL32_Alloc (sizeof(MONTHCAL_INFO));
1282 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
1284 if (infoPtr == NULL) {
1285 ERR ( "could not allocate info memory!\n");
1288 if ((MONTHCAL_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
1289 ERR ( "pointer assignment error!\n");
1294 infoPtr->hFont=GetStockObject(DEFAULT_GUI_FONT);
1295 GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
1296 logFont.lfWeight=FW_BOLD;
1297 infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
1299 /* initialize info structure */
1300 /* FIXME: calculate systemtime ->> localtime (substract timezoneinfo) */
1302 GetSystemTime (&infoPtr->todaysDate);
1303 infoPtr->firstDay = 0;
1304 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1305 infoPtr->currentYear= infoPtr->todaysDate.wYear;
1306 MONTHCAL_CopyTime (&infoPtr->todaysDate,&infoPtr->minDate);
1307 MONTHCAL_CopyTime (&infoPtr->todaysDate,&infoPtr->maxDate);
1308 infoPtr->maxSelCount=6;
1309 infoPtr->monthRange=3;
1310 infoPtr->monthdayState=COMCTL32_Alloc
1311 (infoPtr->monthRange*sizeof(MONTHDAYSTATE));
1312 infoPtr->titlebk = GetSysColor (COLOR_GRAYTEXT);
1313 infoPtr->titletxt = GetSysColor (COLOR_WINDOW);
1314 infoPtr->monthbk = GetSysColor (COLOR_WINDOW);
1315 infoPtr->trailingtxt = GetSysColor (COLOR_GRAYTEXT);
1316 infoPtr->bk = GetSysColor (COLOR_WINDOW);
1317 infoPtr->txt = GetSysColor (COLOR_WINDOWTEXT);
1324 MONTHCAL_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
1326 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr (hwnd);
1328 /* free month calendar info data */
1329 COMCTL32_Free (infoPtr);
1339 MONTHCAL_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1345 return MONTHCAL_GetCurSel (hwnd, wParam, lParam);
1348 return MONTHCAL_SetCurSel (hwnd, wParam, lParam);
1350 case MCM_GETMAXSELCOUNT:
1351 return MONTHCAL_GetMaxSelCount (hwnd, wParam, lParam);
1353 case MCM_SETMAXSELCOUNT:
1354 return MONTHCAL_SetMaxSelCount (hwnd, wParam, lParam);
1356 case MCM_GETSELRANGE:
1357 return MONTHCAL_GetSelRange (hwnd, wParam, lParam);
1359 case MCM_SETSELRANGE:
1360 return MONTHCAL_SetSelRange (hwnd, wParam, lParam);
1362 case MCM_GETMONTHRANGE:
1363 return MONTHCAL_GetMonthRange (hwnd, wParam, lParam);
1365 case MCM_SETDAYSTATE:
1366 return MONTHCAL_SetDayState (hwnd, wParam, lParam);
1368 case MCM_GETMINREQRECT:
1369 return MONTHCAL_GetMinReqRect (hwnd, wParam, lParam);
1372 return MONTHCAL_GetColor (hwnd, wParam, lParam);
1375 return MONTHCAL_SetColor (hwnd, wParam, lParam);
1378 return MONTHCAL_GetToday (hwnd, wParam, lParam);
1381 FIXME ( "Unimplemented msg MCM_SETTODAY\n");
1385 return MONTHCAL_HitTest (hwnd,lParam);
1387 case MCM_GETFIRSTDAYOFWEEK:
1388 return MONTHCAL_GetFirstDayOfWeek (hwnd, wParam, lParam);
1390 case MCM_SETFIRSTDAYOFWEEK:
1391 return MONTHCAL_SetFirstDayOfWeek (hwnd, wParam, lParam);
1394 return MONTHCAL_GetRange (hwnd, wParam, lParam);
1397 return MONTHCAL_SetRange (hwnd, wParam, lParam);
1399 case MCM_GETMONTHDELTA:
1400 return MONTHCAL_GetMonthDelta (hwnd, wParam, lParam);
1402 case MCM_SETMONTHDELTA:
1403 return MONTHCAL_SetMonthDelta (hwnd, wParam, lParam);
1405 case MCM_GETMAXTODAYWIDTH:
1406 return MONTHCAL_GetMaxTodayWidth (hwnd);
1409 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1412 return MONTHCAL_KillFocus (hwnd, wParam, lParam);
1414 case WM_LBUTTONDOWN:
1415 return MONTHCAL_LButtonDown (hwnd, wParam, lParam);
1418 return MONTHCAL_MouseMove (hwnd, wParam, lParam);
1421 return MONTHCAL_LButtonUp (hwnd, wParam, lParam);
1424 return MONTHCAL_Paint (hwnd, wParam);
1427 return MONTHCAL_SetFocus (hwnd, wParam, lParam);
1430 return MONTHCAL_Create (hwnd, wParam, lParam);
1433 return MONTHCAL_Destroy (hwnd, wParam, lParam);
1436 if (uMsg >= WM_USER)
1437 ERR ( "unknown msg %04x wp=%08x lp=%08lx\n",
1438 uMsg, wParam, lParam);
1439 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1446 MONTHCAL_Register (void)
1450 if (GlobalFindAtomA (MONTHCAL_CLASSA)) return;
1452 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1453 wndClass.style = CS_GLOBALCLASS;
1454 wndClass.lpfnWndProc = (WNDPROC)MONTHCAL_WindowProc;
1455 wndClass.cbClsExtra = 0;
1456 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
1457 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1458 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1459 wndClass.lpszClassName = MONTHCAL_CLASSA;
1461 RegisterClassA (&wndClass);
1466 MONTHCAL_Unregister (void)
1468 if (GlobalFindAtomA (MONTHCAL_CLASSA))
1469 UnregisterClassA (MONTHCAL_CLASSA, (HINSTANCE)NULL);