1 /* Month calendar control
4 * Copyright 1998, 1999 Eric Kohl (ekohl@abo.rhein-zeitung.de)
5 * Copyright 1999 Alex Priem (alexp@sci.kun.nl)
6 * Copyright 1999 Chris Morgan <cmorgan@wpi.edu> and
7 * James Abbatiello <abbeyj@wpi.edu>
13 * FIXME: refresh should ask for rect of required length. (?)
14 * FIXME: handle resources better (doesn't work now); also take care
15 of internationalization.
16 * FIXME: keyboard handling.
29 #include "debugtools.h"
31 DEFAULT_DEBUG_CHANNEL(monthcal)
33 /* take #days/month from ole/parsedt.c;
34 * we want full month-names, and abbreviated weekdays, so these are
37 static const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
39 const char * const monthtxt[] = {"January", "February", "March", "April", "May",
40 "June", "July", "August", "September", "October",
41 "November", "December"};
42 const char * const daytxt[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
43 static const int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
46 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongA(hwnd, 0))
48 /* helper functions */
50 /* returns the number of days in any given month */
51 /* january is 1, december is 12 */
52 static int MONTHCAL_MonthLength(int month, int year)
54 /* if we have a leap year add 1 day to February */
55 /* a leap year is a year either divisible by 400 */
56 /* or divisible by 4 and not by 100 */
57 if(month == 2) { /* February */
58 return mdays[month - 1] + ((year%400 == 0) ? 1 : ((year%100 != 0) &&
59 (year%4 == 0)) ? 1 : 0);
62 return mdays[month - 1];
67 /* make sure that time is valid */
68 static int MONTHCAL_ValidateTime(SYSTEMTIME time)
70 if(time.wMonth > 12) return FALSE;
71 if(time.wDayOfWeek > 6) return FALSE;
72 if(time.wDay > MONTHCAL_MonthLength(time.wMonth, time.wYear))
74 if(time.wHour > 23) return FALSE;
75 if(time.wMinute > 59) return FALSE;
76 if(time.wSecond > 59) return FALSE;
77 if(time.wMilliseconds > 999) return FALSE;
83 void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to)
85 to->wYear = from->wYear;
86 to->wMonth = from->wMonth;
87 to->wDayOfWeek = from->wDayOfWeek;
88 to->wDay = from->wDay;
89 to->wHour = from->wHour;
90 to->wMinute = from->wMinute;
91 to->wSecond = from->wSecond;
92 to->wMilliseconds = from->wMilliseconds;
96 /* Note:Depending on DST, this may be offset by a day.
97 Need to find out if we're on a DST place & adjust the clock accordingly.
98 Above function assumes we have a valid data.
99 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
103 /* returns the day in the week(0 == sunday, 6 == saturday) */
104 /* day(1 == 1st, 2 == 2nd... etc), year is the year value */
105 int MONTHCAL_CalculateDayOfWeek(DWORD day, DWORD month, DWORD year)
109 return((year + year/4 - year/100 + year/400 +
110 DayOfWeekTable[month-1] + day - 1 ) % 7);
114 static int MONTHCAL_CalcDayFromPos(MONTHCAL_INFO *infoPtr, int x, int y)
116 int daypos, weekpos, retval, firstDay;
118 /* if the point is outside the x bounds of the window put
120 if(x > (infoPtr->width_increment * 7.0)) {
121 x = infoPtr->rcClient.right - infoPtr->rcClient.left - infoPtr->left_offset;
124 daypos = (x -(infoPtr->prevmonth.left + infoPtr->left_offset)) / infoPtr->width_increment;
125 weekpos = (y - infoPtr->days.bottom - infoPtr->rcClient.top) / infoPtr->height_increment;
127 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
128 retval = daypos + (7 * weekpos) - firstDay;
129 TRACE("%d %d %d\n", daypos, weekpos, retval);
133 /* day is the day of the month, 1 == 1st day of the month */
134 /* sets x and y to be the position of the day */
135 /* x == day, y == week where(0,0) == sunday, 1st week */
136 static void MONTHCAL_CalcDayXY(MONTHCAL_INFO *infoPtr, int day, int month,
139 int firstDay, prevMonth;
141 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
143 if(month==infoPtr->currentMonth) {
144 *x = (day + firstDay) % 7;
145 *y = (day + firstDay - *x) / 7;
148 if(month < infoPtr->currentMonth) {
149 prevMonth = month - 1;
153 *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay) % 7;
158 *y = MONTHCAL_MonthLength(month, infoPtr->currentYear - 1) / 7;
159 *x = (day + firstDay + MONTHCAL_MonthLength(month,
160 infoPtr->currentYear)) % 7;
164 /* x: column(day), y: row(week) */
165 static void MONTHCAL_CalcDayRect(MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
167 r->left = infoPtr->prevmonth.left + x * infoPtr->width_increment + infoPtr->left_offset;
168 r->right = r->left + infoPtr->width_increment;
169 r->top = infoPtr->height_increment * y + infoPtr->days.bottom + infoPtr->top_offset;
170 r->bottom = r->top + infoPtr->textHeight;
174 /* sets the RECT struct r to the rectangle around the day and month */
175 /* day is the day value of the month(1 == 1st), month is the month */
176 /* value(january == 1, december == 12) */
177 static inline void MONTHCAL_CalcPosFromDay(MONTHCAL_INFO *infoPtr,
178 int day, int month, RECT *r)
182 MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
183 MONTHCAL_CalcDayRect(infoPtr, r, x, y);
187 /* day is the day in the month(1 == 1st of the month) */
188 /* month is the month value(1 == january, 12 == december) */
189 static void MONTHCAL_CircleDay(HDC hdc, MONTHCAL_INFO *infoPtr, int day,
192 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
193 HPEN hOldPen2 = SelectObject(hdc, hRedPen);
198 /* use prevmonth to calculate position because it contains the extra width
199 * from MCS_WEEKNUMBERS
202 MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
209 points[1].x = x + 0.8 * infoPtr->width_increment;
211 points[2].x = x + 0.9 * infoPtr->width_increment;
213 points[3].x = x + infoPtr->width_increment;
214 points[3].y = y + 0.5 * infoPtr->textHeight;
216 points[4].x = x + infoPtr->width_increment;
217 points[4].y = y + 0.9 * infoPtr->textHeight;
218 points[5].x = x + 0.6 * infoPtr->width_increment;
219 points[5].y = y + 0.9 * infoPtr->textHeight;
220 points[6].x = x + 0.5 * infoPtr->width_increment;
221 points[6].y = y + 0.9 * infoPtr->textHeight; /* bring the bottom up just
222 a hair to fit inside the day rectangle */
224 points[7].x = x + 0.2 * infoPtr->width_increment;
225 points[7].y = y + 0.8 * infoPtr->textHeight;
226 points[8].x = x + 0.1 * infoPtr->width_increment;
227 points[8].y = y + 0.8 * infoPtr->textHeight;
229 points[9].y = y + 0.5 * infoPtr->textHeight;
231 points[10].x = x + 0.1 * infoPtr->width_increment;
232 points[10].y = y + 0.2 * infoPtr->textHeight;
233 points[11].x = x + 0.2 * infoPtr->width_increment;
234 points[11].y = y + 0.3 * infoPtr->textHeight;
235 points[12].x = x + 0.5 * infoPtr->width_increment;
236 points[12].y = y + 0.3 * infoPtr->textHeight;
238 PolyBezier(hdc, points, 13);
239 DeleteObject(hRedPen);
240 SelectObject(hdc, hOldPen2);
244 static void MONTHCAL_DrawDay(HDC hdc, MONTHCAL_INFO *infoPtr,
245 int day, int month, int x, int y, int bold)
249 static int haveBoldFont, haveSelectedDay = FALSE;
251 HPEN hNewPen, hOldPen = 0;
255 sprintf(buf, "%d", day);
257 /* No need to check styles: when selection is not valid, it is set to zero.
258 * 1<day<31, so evertyhing's OK.
261 MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
263 if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
264 && (month==infoPtr->currentMonth)) {
268 TRACE("%d %d %d\n",day, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
269 TRACE("%d %d %d %d\n", r.left, r.top, r.right, r.bottom);
270 oldCol = SetTextColor(hdc, infoPtr->monthbk);
271 oldBk = SetBkColor(hdc, infoPtr->trailingtxt);
272 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
273 hrgn = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
274 FillRgn(hdc, hrgn, hbr);
276 /* FIXME: this may need to be changed now b/c of the other
277 drawing changes 11/3/99 CMM */
278 r2.left = r.left - 0.25 * infoPtr->textWidth;
280 r2.right = r.left + 0.5 * infoPtr->textWidth;
281 r2.bottom = r.bottom;
282 if(haveSelectedDay) FillRect(hdc, &r2, hbr);
283 haveSelectedDay = TRUE;
285 haveSelectedDay = FALSE;
288 /* need to add some code for multiple selections */
290 if((bold) &&(!haveBoldFont)) {
291 SelectObject(hdc, infoPtr->hBoldFont);
294 if((!bold) &&(haveBoldFont)) {
295 SelectObject(hdc, infoPtr->hFont);
296 haveBoldFont = FALSE;
299 if(haveSelectedDay) {
300 SetTextColor(hdc, oldCol);
301 SetBkColor(hdc, oldBk);
304 DrawTextA(hdc, buf, lstrlenA(buf), &r,
305 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
307 /* draw a rectangle around the currently selected days text */
308 if((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth)) {
309 hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
310 hbr = GetSysColorBrush(COLOR_WINDOWTEXT);
311 FrameRect(hdc, &r, hbr);
312 SelectObject(hdc, hOldPen);
317 /* CHECKME: For `todays date', do we need to check the locale?*/
318 static void MONTHCAL_Refresh(HWND hwnd, HDC hdc)
320 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
321 RECT *rcClient=&infoPtr->rcClient;
322 RECT *rcDraw=&infoPtr->rcDraw;
323 RECT *title=&infoPtr->title;
324 RECT *prev=&infoPtr->titlebtnprev;
325 RECT *next=&infoPtr->titlebtnnext;
326 RECT *titlemonth=&infoPtr->titlemonth;
327 RECT *titleyear=&infoPtr->titleyear;
328 RECT *prevmonth=&infoPtr->prevmonth;
329 RECT *nextmonth=&infoPtr->nextmonth;
332 RECT *weeknums=&infoPtr->weeknums;
333 RECT *rtoday=&infoPtr->today;
334 int i, j, m, mask, day, firstDay, weeknum, prevMonth;
335 int textHeight = infoPtr->textHeight, textWidth = infoPtr->textWidth;
339 /* LOGFONTA logFont; */
341 const char *thisMonthtxt;
342 COLORREF oldTextColor, oldBkColor;
343 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
346 oldTextColor = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
348 /* draw control edge */
349 hbr = CreateSolidBrush(RGB(255, 255, 255));
350 FillRect(hdc, rcClient, hbr);
351 DrawEdge(hdc, rcClient, EDGE_SUNKEN, BF_RECT);
356 hbr = CreateSolidBrush(infoPtr->titlebk);
357 FillRect(hdc, title, hbr);
359 /* if the previous button is pressed draw it depressed */
360 if((infoPtr->status & MC_PREVPRESSED))
361 DrawFrameControl(hdc, prev, DFC_SCROLL,
362 DFCS_SCROLLLEFT | DFCS_PUSHED |
363 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
364 else /* if the previous button is pressed draw it depressed */
365 DrawFrameControl(hdc, prev, DFC_SCROLL,
366 DFCS_SCROLLLEFT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
368 /* if next button is depressed draw it depressed */
369 if((infoPtr->status & MC_NEXTPRESSED))
370 DrawFrameControl(hdc, next, DFC_SCROLL,
371 DFCS_SCROLLRIGHT | DFCS_PUSHED |
372 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
373 else /* if the next button is pressed draw it depressed */
374 DrawFrameControl(hdc, next, DFC_SCROLL,
375 DFCS_SCROLLRIGHT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
377 oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
378 SetTextColor(hdc, infoPtr->titletxt);
379 currentFont = SelectObject(hdc, infoPtr->hBoldFont);
381 /* titlemonth->left and right are set in MONTHCAL_UpdateSize */
382 titlemonth->left = title->left;
383 titlemonth->right = title->right;
385 thisMonthtxt = monthtxt[infoPtr->currentMonth - 1];
386 sprintf(buf, "%s %ld", thisMonthtxt, infoPtr->currentYear);
387 DrawTextA(hdc, buf, strlen(buf), titlemonth,
388 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
389 SelectObject(hdc, infoPtr->hFont);
391 /* titlemonth left/right contained rect for whole titletxt('June 1999')
392 * MCM_HitTestInfo wants month & year rects, so prepare these now.
393 *(no, we can't draw them separately; the whole text is centered)
395 GetTextExtentPoint32A(hdc, buf, lstrlenA(buf), &size);
396 titlemonth->left = title->right / 2 - size.cx / 2;
397 titleyear->right = title->right / 2 + size.cx / 2;
398 GetTextExtentPoint32A(hdc, thisMonthtxt, lstrlenA(thisMonthtxt), &size);
399 titlemonth->right = titlemonth->left + size.cx;
400 titleyear->right = titlemonth->right;
403 /* draw line under day abbreviatons */
405 if(dwStyle & MCS_WEEKNUMBERS)
406 MoveToEx(hdc, rcDraw->left + textWidth + 3, title->bottom + textHeight + 2, NULL);
408 MoveToEx(hdc, rcDraw->left + 3, title->bottom + textHeight + 2, NULL);
410 LineTo(hdc, rcDraw->right - 3, title->bottom + textHeight + 2);
412 /* draw day abbreviations */
414 SetBkColor(hdc, infoPtr->monthbk);
415 SetTextColor(hdc, infoPtr->trailingtxt);
417 /* copy this rect so we can change the values without changing */
418 /* the original version */
419 days->left = infoPtr->days.left;
420 days->right = infoPtr->days.right;
421 days->top = infoPtr->days.top;
422 days->bottom = infoPtr->days.bottom;
424 i = infoPtr->firstDay;
427 DrawTextA(hdc, daytxt[i], strlen(daytxt[i]), days,
428 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
430 days->left+=infoPtr->width_increment;
431 days->right+=infoPtr->width_increment;
434 days->left = rcDraw->left + j;
435 if(dwStyle & MCS_WEEKNUMBERS) days->left+=textWidth;
436 /* FIXME: this may need to be changed now 11/10/99 CMM */
437 days->right = rcDraw->left + (j+1) * textWidth - 2;
439 /* draw day numbers; first, the previous month */
441 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
443 prevMonth = infoPtr->currentMonth - 1;
444 if(prevMonth == 0) /* if currentMonth is january(1) prevMonth is */
445 prevMonth = 12; /* december(12) of the previous year */
447 day = MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay;
452 while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear)) {
453 MONTHCAL_DrawDay(hdc, infoPtr, day, prevMonth, i, 0,
454 infoPtr->monthdayState[m] & mask);
461 if(dwStyle & MCS_WEEKNUMBERS) prevmonth->left = textWidth;
462 prevmonth->right = prevmonth->left + i * textWidth;
463 prevmonth->top = days->bottom;
464 prevmonth->bottom = prevmonth->top + textHeight;
466 /* draw `current' month */
468 day = 1; /* start at the beginning of the current month */
470 infoPtr->firstDayplace = i;
471 SetTextColor(hdc, infoPtr->txt);
475 /* draw the first week of the current month */
477 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, 0,
478 infoPtr->monthdayState[m] & mask);
480 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
481 (day==infoPtr->todaysDate.wDay) &&
482 (infoPtr->currentYear == infoPtr->todaysDate.wYear)) {
483 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
491 j = 1; /* move to the 2nd week of the current month */
492 i = 0; /* move back to sunday */
493 while(day <= MONTHCAL_MonthLength(infoPtr->currentMonth, infoPtr->currentYear)) {
494 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, j,
495 infoPtr->monthdayState[m] & mask);
497 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
498 (day==infoPtr->todaysDate.wDay) &&
499 (infoPtr->currentYear == infoPtr->todaysDate.wYear))
500 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
505 if(i>6) { /* past saturday, goto the next weeks sunday */
511 /* draw `next' month */
513 /* note: the nextmonth rect only hints for the `half-week' that needs to be
514 * drawn to complete the current week. An eventual next week that needs to
515 * be drawn to complete the month calendar is not taken into account in
516 * this rect -- HitTest knows about this.*/
518 nextmonth->left = prevmonth->left + i * textWidth;
519 nextmonth->right = rcDraw->right;
520 nextmonth->top = days->bottom + (j+1) * textHeight;
521 nextmonth->bottom = nextmonth->top + textHeight;
523 day = 1; /* start at the first day of the next month */
527 SetTextColor(hdc, infoPtr->trailingtxt);
528 while((i<7) &&(j<6)) {
529 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth + 1, i, j,
530 infoPtr->monthdayState[m] & mask);
535 if(i==7) { /* past saturday, go to next week's sunday */
540 SetTextColor(hdc, infoPtr->txt);
543 /* draw `today' date if style allows it, and draw a circle before today's
544 * date if necessary */
546 if(!(dwStyle & MCS_NOTODAY)) {
548 if(!(dwStyle & MCS_NOTODAYCIRCLE)) {
549 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth + 1);
552 MONTHCAL_CalcDayRect(infoPtr, rtoday, offset==textWidth, 6);
553 sprintf(buf, "Today: %d/%d/%d", infoPtr->todaysDate.wMonth,
554 infoPtr->todaysDate.wDay, infoPtr->todaysDate.wYear % 100);
555 rtoday->right = rcDraw->right;
556 SelectObject(hdc, infoPtr->hBoldFont);
557 DrawTextA(hdc, buf, lstrlenA(buf), rtoday,
558 DT_LEFT | DT_VCENTER | DT_SINGLELINE);
559 SelectObject(hdc, infoPtr->hFont);
562 if(dwStyle & MCS_WEEKNUMBERS) {
563 /* display weeknumbers*/
565 weeknums->right = textWidth;
566 weeknums->top = days->bottom + 2;
567 weeknums->bottom = days->bottom + 2 + textHeight;
570 for(i=0; i<infoPtr->currentMonth-1; i++)
571 weeknum+=MONTHCAL_MonthLength(i, infoPtr->currentYear);
575 sprintf(buf, "%d", weeknum + i);
576 DrawTextA(hdc, buf, lstrlenA(buf), weeknums,
577 DT_CENTER | DT_BOTTOM | DT_SINGLELINE );
578 weeknums->top+=textHeight * 1.25;
579 weeknums->bottom+=textHeight * 1.25;
582 MoveToEx(hdc, weeknums->right, days->bottom + 5 , NULL);
583 LineTo(hdc, weeknums->right, weeknums->bottom - 1.25 * textHeight - 5);
587 /* currentFont was font at entering Refresh */
589 SetBkColor(hdc, oldBkColor);
590 SelectObject(hdc, currentFont);
591 SetTextColor(hdc, oldTextColor);
596 MONTHCAL_GetMinReqRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
598 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
599 LPRECT lpRect = (LPRECT) lParam;
600 TRACE("%x %lx\n", wParam, lParam);
602 /* validate parameters */
604 if((infoPtr==NULL) ||(lpRect == NULL) ) return FALSE;
606 lpRect->left = infoPtr->rcClient.left;
607 lpRect->right = infoPtr->rcClient.right;
608 lpRect->top = infoPtr->rcClient.top;
609 lpRect->bottom = infoPtr->rcClient.bottom;
614 MONTHCAL_GetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
616 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
618 TRACE("%x %lx\n", wParam, lParam);
620 switch((int)wParam) {
621 case MCSC_BACKGROUND:
626 return infoPtr->titlebk;
628 return infoPtr->titletxt;
630 return infoPtr->monthbk;
631 case MCSC_TRAILINGTEXT:
632 return infoPtr->trailingtxt;
639 MONTHCAL_SetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
641 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
644 TRACE("%x %lx\n", wParam, lParam);
646 switch((int)wParam) {
647 case MCSC_BACKGROUND:
649 infoPtr->bk = (COLORREF)lParam;
653 infoPtr->txt = (COLORREF)lParam;
656 prev = infoPtr->titlebk;
657 infoPtr->titlebk = (COLORREF)lParam;
660 prev=infoPtr->titletxt;
661 infoPtr->titletxt = (COLORREF)lParam;
664 prev = infoPtr->monthbk;
665 infoPtr->monthbk = (COLORREF)lParam;
667 case MCSC_TRAILINGTEXT:
668 prev = infoPtr->trailingtxt;
669 infoPtr->trailingtxt = (COLORREF)lParam;
677 MONTHCAL_GetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
679 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
681 TRACE("%x %lx\n", wParam, lParam);
684 return infoPtr->delta;
686 return infoPtr->visible;
690 MONTHCAL_SetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
692 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
693 int prev = infoPtr->delta;
695 TRACE("%x %lx\n", wParam, lParam);
697 infoPtr->delta = (int)wParam;
703 MONTHCAL_GetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
705 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
707 return infoPtr->firstDay;
711 /* sets the first day of the week that will appear in the control */
712 /* 0 == Monday, 6 == Sunday */
713 /* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
714 /* FIXME: we need more error checking here */
716 MONTHCAL_SetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
718 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
719 int prev = infoPtr->firstDay;
723 TRACE("%x %lx\n", wParam, lParam);
725 if((lParam >= 0) && (lParam < 7)) {
726 infoPtr->firstDay = (int)lParam;
727 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK,
729 TRACE("%s %d\n", buf, strlen(buf));
730 if((sscanf(buf, "%d", &day) == 1) &&(infoPtr->firstDay != day))
731 infoPtr->firstDay = day;
737 /* FIXME: fill this in */
739 MONTHCAL_GetMonthRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
741 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
743 TRACE("%x %lx\n", wParam, lParam);
746 return infoPtr->monthRange;
751 MONTHCAL_GetMaxTodayWidth(HWND hwnd)
753 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
755 return(infoPtr->today.right - infoPtr->today.left);
759 /* FIXME: are validated times taken from current date/time or simply
761 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
762 * adjusting range with MCM_SETRANGE
766 MONTHCAL_SetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
768 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
769 SYSTEMTIME lprgSysTimeArray[1];
772 TRACE("%x %lx\n", wParam, lParam);
774 if(wParam & GDTR_MAX) {
775 if(MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
776 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxDate);
777 infoPtr->rangeValid|=GDTR_MAX;
779 GetSystemTime(&infoPtr->todaysDate);
780 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
783 if(wParam & GDTR_MIN) {
784 if(MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
785 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->maxDate);
786 infoPtr->rangeValid|=GDTR_MIN;
788 GetSystemTime(&infoPtr->todaysDate);
789 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
793 prev = infoPtr->monthRange;
794 infoPtr->monthRange = infoPtr->maxDate.wMonth - infoPtr->minDate.wMonth;
796 if(infoPtr->monthRange!=prev) {
797 COMCTL32_ReAlloc(infoPtr->monthdayState,
798 infoPtr->monthRange * sizeof(MONTHDAYSTATE));
805 /* CHECKME: At the moment, we copy ranges anyway,regardless of
806 * infoPtr->rangeValid; a invalid range is simply filled with zeros in
807 * SetRange. Is this the right behavior?
811 MONTHCAL_GetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
813 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
814 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *)lParam;
816 /* validate parameters */
818 if((infoPtr==NULL) || (lprgSysTimeArray==NULL)) return FALSE;
820 MONTHCAL_CopyTime(&infoPtr->maxDate, &lprgSysTimeArray[1]);
821 MONTHCAL_CopyTime(&infoPtr->minDate, &lprgSysTimeArray[0]);
823 return infoPtr->rangeValid;
828 MONTHCAL_SetDayState(HWND hwnd, WPARAM wParam, LPARAM lParam)
831 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
832 int i, iMonths = (int)wParam;
833 MONTHDAYSTATE *dayStates = (LPMONTHDAYSTATE)lParam;
835 TRACE("%x %lx\n", wParam, lParam);
836 if(iMonths!=infoPtr->monthRange) return 0;
838 for(i=0; i<iMonths; i++)
839 infoPtr->monthdayState[i] = dayStates[i];
845 MONTHCAL_GetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
847 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
848 SYSTEMTIME *lpSel = (SYSTEMTIME *) lParam;
850 TRACE("%x %lx\n", wParam, lParam);
851 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
852 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
854 MONTHCAL_CopyTime(&infoPtr->minSel, lpSel);
859 /* FIXME: if the specified date is not visible, make it visible */
862 MONTHCAL_SetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
864 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
865 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
867 TRACE("%x %lx\n", wParam, lParam);
868 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
869 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
871 TRACE("%d %d\n", lpSel->wMonth, lpSel->wDay);
873 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
874 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
881 MONTHCAL_GetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
883 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
885 TRACE("%x %lx\n", wParam, lParam);
886 return infoPtr->maxSelCount;
891 MONTHCAL_SetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
893 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
895 TRACE("%x %lx\n", wParam, lParam);
896 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
897 infoPtr->maxSelCount = wParam;
905 MONTHCAL_GetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
907 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
908 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
910 TRACE("%x %lx\n", wParam, lParam);
912 /* validate parameters */
914 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
916 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT)
918 MONTHCAL_CopyTime(&infoPtr->maxSel, &lprgSysTimeArray[1]);
919 MONTHCAL_CopyTime(&infoPtr->minSel, &lprgSysTimeArray[0]);
920 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
929 MONTHCAL_SetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
931 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
932 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
934 TRACE("%x %lx\n", wParam, lParam);
936 /* validate parameters */
938 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
940 if(GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)
942 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxSel);
943 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minSel);
944 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
953 MONTHCAL_GetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
955 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
956 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
958 TRACE("%x %lx\n", wParam, lParam);
960 /* validate parameters */
962 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
963 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
969 MONTHCAL_SetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
971 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
972 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
974 TRACE("%x %lx\n", wParam, lParam);
976 /* validate parameters */
978 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
979 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
985 MONTHCAL_HitTest(HWND hwnd, LPARAM lParam)
987 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
988 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
994 retval = MCHT_NOWHERE;
997 /* are we in the header? */
999 if(PtInRect(&infoPtr->title, lpht->pt)) {
1000 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1001 retval = MCHT_TITLEBTNPREV;
1004 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1005 retval = MCHT_TITLEBTNNEXT;
1008 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1009 retval = MCHT_TITLEMONTH;
1012 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1013 retval = MCHT_TITLEYEAR;
1017 retval = MCHT_TITLE;
1021 if(PtInRect(&infoPtr->days, lpht->pt)) {
1022 retval = MCHT_CALENDARDAY; /* FIXME: find out which day we're on */
1025 if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
1026 retval = MCHT_CALENDARWEEKNUM; /* FIXME: find out which day we're on */
1029 if(PtInRect(&infoPtr->prevmonth, lpht->pt)) {
1030 retval = MCHT_CALENDARDATEPREV;
1034 if(PtInRect(&infoPtr->nextmonth, lpht->pt) ||
1035 ((x>infoPtr->nextmonth.left) &&(x<infoPtr->nextmonth.right) &&
1036 (y>infoPtr->nextmonth.bottom) &&(y<infoPtr->today.top ))) {
1037 retval = MCHT_CALENDARDATENEXT;
1041 if(PtInRect(&infoPtr->today, lpht->pt)) {
1042 retval = MCHT_TODAYLINK;
1046 /* MCHT_CALENDARDATE determination: since the next & previous month have
1047 * been handled already(MCHT_CALENDARDATEPREV/NEXT), we only have to check
1048 * whether we're in the calendar area. infoPtr->prevMonth.left handles the
1049 * MCS_WEEKNUMBERS style nicely.
1053 TRACE("%d %d [%d %d %d %d] [%d %d %d %d]\n", x, y,
1054 infoPtr->prevmonth.left, infoPtr->prevmonth.right,
1055 infoPtr->prevmonth.top, infoPtr->prevmonth.bottom,
1056 infoPtr->nextmonth.left, infoPtr->nextmonth.right,
1057 infoPtr->nextmonth.top, infoPtr->nextmonth.bottom);
1058 if((x>infoPtr->prevmonth.left) &&(x<infoPtr->nextmonth.right) &&
1059 (y>infoPtr->prevmonth.top) &&(y<infoPtr->nextmonth.bottom)) {
1060 lpht->st.wYear = infoPtr->currentYear;
1061 lpht->st.wMonth = infoPtr->currentMonth;
1063 lpht->st.wDay = MONTHCAL_CalcDayFromPos(infoPtr, x, y);
1065 TRACE("day hit: %d\n", lpht->st.wDay);
1066 retval = MCHT_CALENDARDATE;
1071 /* Hit nothing special? What's left must be background :-) */
1073 retval = MCHT_CALENDARBK;
1075 lpht->uHit = retval;
1080 static void MONTHCAL_GoToNextMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1082 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1084 TRACE("MONTHCAL_GoToNextMonth\n");
1086 infoPtr->currentMonth++;
1087 if(infoPtr->currentMonth > 12) {
1088 infoPtr->currentYear++;
1089 infoPtr->currentMonth = 1;
1092 if(dwStyle & MCS_DAYSTATE) {
1096 nmds.nmhdr.hwndFrom = hwnd;
1097 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1098 nmds.nmhdr.code = MCN_GETDAYSTATE;
1099 nmds.cDayState = infoPtr->monthRange;
1100 nmds.prgDayState = COMCTL32_Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1102 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1103 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1104 for(i=0; i<infoPtr->monthRange; i++)
1105 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1110 static void MONTHCAL_GoToPrevMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1112 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1114 TRACE("MONTHCAL_GoToPrevMonth\n");
1116 infoPtr->currentMonth--;
1117 if(infoPtr->currentMonth < 1) {
1118 infoPtr->currentYear--;
1119 infoPtr->currentMonth = 12;
1122 if(dwStyle & MCS_DAYSTATE) {
1126 nmds.nmhdr.hwndFrom = hwnd;
1127 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1128 nmds.nmhdr.code = MCN_GETDAYSTATE;
1129 nmds.cDayState = infoPtr->monthRange;
1130 nmds.prgDayState = COMCTL32_Alloc
1131 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1133 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1134 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1135 for(i=0; i<infoPtr->monthRange; i++)
1136 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1142 MONTHCAL_LButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1144 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1150 BOOL redraw = FALSE;
1153 TRACE("%x %lx\n", wParam, lParam);
1155 ht.pt.x = (INT)LOWORD(lParam);
1156 ht.pt.y = (INT)HIWORD(lParam);
1157 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1159 /* FIXME: these flags should be checked by */
1160 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1162 if(hit & MCHT_NEXT) {
1164 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1165 infoPtr->status = MC_NEXTPRESSED;
1166 SetTimer(hwnd, MC_NEXTMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1168 if(hit & MCHT_PREV) {
1170 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1171 infoPtr->status = MC_PREVPRESSED;
1172 SetTimer(hwnd, MC_PREVMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1175 if(hit == MCHT_TITLEMONTH) {
1177 HRSRC hrsrc = FindResourceA( COMCTL32_hModule, MAKEINTRESOURCEA(IDD_MCMONTHMENU), RT_MENUA );
1179 TRACE("returning zero\n");
1182 TRACE("resource is:%x\n",hrsrc);
1183 hMenu = LoadMenuIndirectA((LPCVOID)LoadResource( COMCTL32_hModule, hrsrc ));
1185 TRACE("menu is:%x\n",hMenu);
1188 hMenu = CreateMenu();
1189 AppendMenuA(hMenu, MF_STRING,IDM_JAN, "January");
1190 AppendMenuA(hMenu, MF_STRING,IDM_FEB, "February");
1191 AppendMenuA(hMenu, MF_STRING,IDM_MAR, "March");
1193 retval = CreateWindowA(POPUPMENU_CLASS_ATOM, NULL,
1194 WS_CHILD | WS_VISIBLE, 0, 0 ,100 , 220,
1195 hwnd, hMenu, GetWindowLongA(hwnd, GWL_HINSTANCE), NULL);
1196 TRACE("hwnd returned:%x\n", retval);
1199 if(hit == MCHT_TITLEYEAR) {
1200 FIXME("create updown for yearselection\n");
1202 if(hit == MCHT_TODAYLINK) {
1203 FIXME("set currentday\n");
1205 if(hit == MCHT_CALENDARDATE) {
1206 SYSTEMTIME selArray[2];
1210 nmsc.nmhdr.hwndFrom = hwnd;
1211 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1212 nmsc.nmhdr.code = MCN_SELCHANGE;
1213 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1214 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1216 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1217 (WPARAM)nmsc.nmhdr.idFrom,(LPARAM)&nmsc);
1219 MONTHCAL_CopyTime(&ht.st, &selArray[0]);
1220 MONTHCAL_CopyTime(&ht.st, &selArray[1]);
1221 MONTHCAL_SetSelRange(hwnd,0,(LPARAM) &selArray);
1223 /* redraw if the selected day changed */
1224 if(infoPtr->curSelDay != ht.st.wDay) {
1228 infoPtr->firstSelDay = ht.st.wDay;
1229 infoPtr->curSelDay = ht.st.wDay;
1230 infoPtr->status = MC_SEL_LBUTDOWN;
1233 /* redraw only if the control changed */
1236 MONTHCAL_Refresh(hwnd, hdc);
1237 ReleaseDC(hwnd, hdc);
1245 MONTHCAL_LButtonUp(HWND hwnd, WPARAM wParam, LPARAM lParam)
1247 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1251 BOOL redraw = FALSE;
1255 if(infoPtr->status & MC_NEXTPRESSED) {
1256 KillTimer(hwnd, MC_NEXTMONTHTIMER);
1259 if(infoPtr->status & MC_PREVPRESSED) {
1260 KillTimer(hwnd, MC_PREVMONTHTIMER);
1264 infoPtr->status = MC_SEL_LBUTUP;
1266 nmhdr.hwndFrom = hwnd;
1267 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
1268 nmhdr.code = NM_RELEASEDCAPTURE;
1269 TRACE("Sent notification from %x to %x\n", hwnd, GetParent(hwnd));
1271 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1272 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1274 nmsc.nmhdr.hwndFrom = hwnd;
1275 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1276 nmsc.nmhdr.code = MCN_SELECT;
1277 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1278 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1280 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1281 (WPARAM)nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1283 /* redraw if necessary */
1286 MONTHCAL_Refresh(hwnd, hdc);
1287 ReleaseDC(hwnd, hdc);
1295 MONTHCAL_Timer(HWND hwnd, WPARAM wParam, LPARAM lParam)
1297 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1299 BOOL redraw = FALSE;
1301 TRACE(" %d\n", wParam);
1302 if(!infoPtr) return 0;
1305 case MC_NEXTMONTHTIMER:
1307 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1309 case MC_PREVMONTHTIMER:
1311 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1314 ERR("got unknown timer\n");
1317 /* redraw only if necessary */
1320 MONTHCAL_Refresh(hwnd, hdc);
1321 ReleaseDC(hwnd, hdc);
1329 MONTHCAL_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1331 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1334 int oldselday, selday, hit;
1337 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1339 ht.pt.x = LOWORD(lParam);
1340 ht.pt.y = HIWORD(lParam);
1342 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1344 /* not on the calendar date numbers? bail out */
1345 TRACE("hit:%x\n",hit);
1346 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1348 selday = ht.st.wDay;
1349 oldselday = infoPtr->curSelDay;
1350 infoPtr->curSelDay = selday;
1351 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1353 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1354 SYSTEMTIME selArray[2];
1357 MONTHCAL_GetSelRange(hwnd, 0, (LPARAM)&selArray);
1359 if(infoPtr->firstSelDay==selArray[0].wDay) i=1;
1360 TRACE("oldRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1361 if(infoPtr->firstSelDay==selArray[1].wDay) {
1362 /* 1st time we get here: selArray[0]=selArray[1]) */
1363 /* if we're still at the first selected date, return */
1364 if(infoPtr->firstSelDay==selday) goto done;
1365 if(selday<infoPtr->firstSelDay) i = 0;
1368 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1369 if(selday>infoPtr->firstSelDay)
1370 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1372 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1375 if(selArray[i].wDay!=selday) {
1376 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1378 selArray[i].wDay = selday;
1380 if(selArray[0].wDay>selArray[1].wDay) {
1382 tempday = selArray[1].wDay;
1383 selArray[1].wDay = selArray[0].wDay;
1384 selArray[0].wDay = tempday;
1387 MONTHCAL_SetSelRange(hwnd, 0, (LPARAM)&selArray);
1393 /* only redraw if the currently selected day changed */
1394 if(oldselday != infoPtr->curSelDay) {
1396 MONTHCAL_Refresh(hwnd, hdc);
1397 ReleaseDC(hwnd, hdc);
1405 MONTHCAL_Paint(HWND hwnd, WPARAM wParam)
1410 hdc = (wParam==0 ? BeginPaint(hwnd, &ps) : (HDC)wParam);
1411 MONTHCAL_Refresh(hwnd, hdc);
1412 if(!wParam) EndPaint(hwnd, &ps);
1418 MONTHCAL_KillFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1425 MONTHCAL_Refresh(hwnd, hdc);
1426 ReleaseDC(hwnd, hdc);
1427 InvalidateRect(hwnd, NULL, TRUE);
1434 MONTHCAL_SetFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1441 MONTHCAL_Refresh(hwnd, hdc);
1442 ReleaseDC(hwnd, hdc);
1447 /* sets the size information */
1448 static void MONTHCAL_UpdateSize(HWND hwnd)
1450 HDC hdc = GetDC(hwnd);
1451 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1452 RECT *rcClient=&infoPtr->rcClient;
1453 RECT *rcDraw=&infoPtr->rcDraw;
1454 RECT *title=&infoPtr->title;
1455 RECT *prev=&infoPtr->titlebtnprev;
1456 RECT *next=&infoPtr->titlebtnnext;
1457 RECT *titlemonth=&infoPtr->titlemonth;
1458 RECT *titleyear=&infoPtr->titleyear;
1459 RECT *days=&infoPtr->days;
1462 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1465 currentFont = SelectObject(hdc, infoPtr->hFont);
1467 /* FIXME: need a way to determine current font, without setting it */
1469 if(infoPtr->hFont!=currentFont) {
1470 SelectObject(hdc, currentFont);
1471 infoPtr->hFont=currentFont;
1472 GetObjectA(currentFont, sizeof(LOGFONTA), &logFont);
1473 logFont.lfWeight=FW_BOLD;
1474 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1478 /* get the height and width of each day's text */
1479 GetTextMetricsA(hdc, &tm);
1480 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading;
1481 GetTextExtentPoint32A(hdc, "Sun", 3, &size);
1482 infoPtr->textWidth = size.cx + 2;
1484 /* retrieve the controls client rectangle info infoPtr->rcClient */
1485 GetClientRect(hwnd, rcClient);
1487 if(dwStyle & MCS_WEEKNUMBERS)
1488 infoPtr->rcClient.right+=infoPtr->textWidth;
1490 /* rcDraw is the rectangle the control is drawn in */
1491 rcDraw->left = rcClient->left;
1492 rcDraw->right = rcClient->right;
1493 rcDraw->top = rcClient->top;
1494 rcDraw->bottom = rcClient->bottom;
1496 /* use DrawEdge to adjust the size of rcClient such that we */
1497 /* do not overwrite the border when drawing the control */
1498 DrawEdge((HDC)NULL, rcDraw, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1501 /* this is correct, the control does NOT expand vertically */
1502 /* like it does horizontally */
1503 /* make sure we don't move the controls bottom out of the client */
1505 if((rcDraw->top + 8 * infoPtr->textHeight + 5) < rcDraw->bottom) {
1506 rcDraw->bottom = rcDraw->top + 8 * infoPtr->textHeight + 5;
1509 /* calculate title area */
1510 title->top = rcClient->top + 1;
1511 title->bottom = title->top + 2 * infoPtr->textHeight + 4;
1512 title->left = rcClient->left + 1;
1513 title->right = rcClient->right - 1;
1515 /* recalculate the height and width increments and offsets */
1516 infoPtr->width_increment = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) / 7.0;
1517 infoPtr->height_increment = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) / 7.0;
1518 infoPtr->left_offset = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) - (infoPtr->width_increment * 7.0);
1519 infoPtr->top_offset = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) - (infoPtr->height_increment * 7.0);
1521 /* set the dimensions of the next and previous buttons and center */
1522 /* the month text vertically */
1523 prev->top = next->top = title->top + 6;
1524 prev->bottom = next->bottom = title->top + 2 * infoPtr->textHeight - 3;
1525 prev->right = title->left + 28;
1526 prev->left = title->left + 4;
1527 next->left = title->right - 28;
1528 next->right = title->right - 4;
1530 /* titlemonth->left and right change based upon the current month */
1531 /* and are recalculated in refresh as the current month may change */
1532 /* without the control being resized */
1533 titlemonth->bottom = titleyear->bottom = prev->top + 2 * infoPtr->textHeight - 3;
1534 titlemonth->top = titleyear->top = title->top;
1536 /* setup the dimensions of the rectangle we draw the names of the */
1537 /* days of the week in */
1538 days->left = infoPtr->left_offset;
1539 if(dwStyle & MCS_WEEKNUMBERS) days->left+=infoPtr->textWidth;
1540 days->right = days->left + infoPtr->width_increment;
1541 days->top = title->bottom + 2;
1542 days->bottom = title->bottom + infoPtr->textHeight + 2;
1544 /* restore the originally selected font */
1545 SelectObject(hdc, currentFont);
1547 ReleaseDC(hwnd, hdc);
1550 static LRESULT MONTHCAL_Size(HWND hwnd, int Width, int Height)
1552 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
1554 MONTHCAL_UpdateSize(hwnd);
1556 /* invalidate client area and erase background */
1557 InvalidateRect(hwnd, NULL, TRUE);
1562 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1564 MONTHCAL_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
1566 MONTHCAL_INFO *infoPtr;
1569 /* allocate memory for info structure */
1570 infoPtr =(MONTHCAL_INFO*)COMCTL32_Alloc(sizeof(MONTHCAL_INFO));
1571 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1573 if(infoPtr == NULL) {
1574 ERR( "could not allocate info memory!\n");
1577 if((MONTHCAL_INFO*)GetWindowLongA(hwnd, 0) != infoPtr) {
1578 ERR( "pointer assignment error!\n");
1582 infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1583 GetObjectA(infoPtr->hFont, sizeof(LOGFONTA), &logFont);
1584 logFont.lfWeight = FW_BOLD;
1585 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1587 /* initialize info structure */
1588 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1590 GetSystemTime(&infoPtr->todaysDate);
1591 infoPtr->firstDay = 0;
1592 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1593 infoPtr->currentYear = infoPtr->todaysDate.wYear;
1594 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
1595 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
1596 infoPtr->maxSelCount = 6;
1597 infoPtr->monthRange = 3;
1598 infoPtr->monthdayState = COMCTL32_Alloc
1599 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1600 infoPtr->titlebk = GetSysColor(COLOR_ACTIVECAPTION);
1601 infoPtr->titletxt = GetSysColor(COLOR_WINDOW);
1602 infoPtr->monthbk = GetSysColor(COLOR_WINDOW);
1603 infoPtr->trailingtxt = GetSysColor(COLOR_GRAYTEXT);
1604 infoPtr->bk = GetSysColor(COLOR_WINDOW);
1605 infoPtr->txt = GetSysColor(COLOR_WINDOWTEXT);
1607 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1608 /* of the control */
1609 MONTHCAL_UpdateSize(hwnd);
1616 MONTHCAL_Destroy(HWND hwnd, WPARAM wParam, LPARAM lParam)
1618 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1620 /* free month calendar info data */
1621 COMCTL32_Free(infoPtr);
1627 static LRESULT WINAPI
1628 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1633 return MONTHCAL_GetCurSel(hwnd, wParam, lParam);
1636 return MONTHCAL_SetCurSel(hwnd, wParam, lParam);
1638 case MCM_GETMAXSELCOUNT:
1639 return MONTHCAL_GetMaxSelCount(hwnd, wParam, lParam);
1641 case MCM_SETMAXSELCOUNT:
1642 return MONTHCAL_SetMaxSelCount(hwnd, wParam, lParam);
1644 case MCM_GETSELRANGE:
1645 return MONTHCAL_GetSelRange(hwnd, wParam, lParam);
1647 case MCM_SETSELRANGE:
1648 return MONTHCAL_SetSelRange(hwnd, wParam, lParam);
1650 case MCM_GETMONTHRANGE:
1651 return MONTHCAL_GetMonthRange(hwnd, wParam, lParam);
1653 case MCM_SETDAYSTATE:
1654 return MONTHCAL_SetDayState(hwnd, wParam, lParam);
1656 case MCM_GETMINREQRECT:
1657 return MONTHCAL_GetMinReqRect(hwnd, wParam, lParam);
1660 return MONTHCAL_GetColor(hwnd, wParam, lParam);
1663 return MONTHCAL_SetColor(hwnd, wParam, lParam);
1666 return MONTHCAL_GetToday(hwnd, wParam, lParam);
1669 return MONTHCAL_SetToday(hwnd, wParam, lParam);
1672 return MONTHCAL_HitTest(hwnd,lParam);
1674 case MCM_GETFIRSTDAYOFWEEK:
1675 return MONTHCAL_GetFirstDayOfWeek(hwnd, wParam, lParam);
1677 case MCM_SETFIRSTDAYOFWEEK:
1678 return MONTHCAL_SetFirstDayOfWeek(hwnd, wParam, lParam);
1681 return MONTHCAL_GetRange(hwnd, wParam, lParam);
1684 return MONTHCAL_SetRange(hwnd, wParam, lParam);
1686 case MCM_GETMONTHDELTA:
1687 return MONTHCAL_GetMonthDelta(hwnd, wParam, lParam);
1689 case MCM_SETMONTHDELTA:
1690 return MONTHCAL_SetMonthDelta(hwnd, wParam, lParam);
1692 case MCM_GETMAXTODAYWIDTH:
1693 return MONTHCAL_GetMaxTodayWidth(hwnd);
1696 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1699 return MONTHCAL_KillFocus(hwnd, wParam, lParam);
1701 case WM_LBUTTONDOWN:
1702 return MONTHCAL_LButtonDown(hwnd, wParam, lParam);
1705 return MONTHCAL_MouseMove(hwnd, wParam, lParam);
1708 return MONTHCAL_LButtonUp(hwnd, wParam, lParam);
1711 return MONTHCAL_Paint(hwnd, wParam);
1714 return MONTHCAL_SetFocus(hwnd, wParam, lParam);
1717 return MONTHCAL_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
1720 return MONTHCAL_Create(hwnd, wParam, lParam);
1723 return MONTHCAL_Timer(hwnd, wParam, lParam);
1726 return MONTHCAL_Destroy(hwnd, wParam, lParam);
1730 ERR( "unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
1731 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
1738 MONTHCAL_Register(void)
1742 if(GlobalFindAtomA(MONTHCAL_CLASSA)) return;
1744 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
1745 wndClass.style = CS_GLOBALCLASS;
1746 wndClass.lpfnWndProc = (WNDPROC)MONTHCAL_WindowProc;
1747 wndClass.cbClsExtra = 0;
1748 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
1749 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
1750 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1751 wndClass.lpszClassName = MONTHCAL_CLASSA;
1753 RegisterClassA(&wndClass);
1758 MONTHCAL_Unregister(void)
1760 if(GlobalFindAtomA(MONTHCAL_CLASSA))
1761 UnregisterClassA(MONTHCAL_CLASSA, (HINSTANCE)NULL);