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>
8 * Copyright 2000 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on Oct. 20, 2004, by Dimitrie O. Paun.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
34 * -- MCM_[GS]ETUNICODEFORMAT
35 * -- MONTHCAL_GetMonthRange
36 * -- handle resources better (doesn't work now);
37 * -- take care of internationalization.
38 * -- keyboard handling.
39 * -- GetRange: At the moment, we copy ranges anyway, regardless of
40 * infoPtr->rangeValid; an invalid range is simply filled
41 * with zeros in SetRange. Is this the right behavior?
60 #include "wine/unicode.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(monthcal);
65 #define MC_SEL_LBUTUP 1 /* Left button released */
66 #define MC_SEL_LBUTDOWN 2 /* Left button pressed in calendar */
67 #define MC_PREVPRESSED 4 /* Prev month button pressed */
68 #define MC_NEXTPRESSED 8 /* Next month button pressed */
69 #define MC_NEXTMONTHDELAY 350 /* when continuously pressing `next */
70 /* month', wait 500 ms before going */
71 /* to the next month */
72 #define MC_NEXTMONTHTIMER 1 /* Timer ID's */
73 #define MC_PREVMONTHTIMER 2
75 #define countof(arr) (sizeof(arr)/sizeof(arr[0]))
80 DWORD dwStyle; /* cached GWL_STYLE */
93 int firstDayplace; /* place of the first day of the current month */
94 int delta; /* scroll rate; # of months that the */
95 /* control moves when user clicks a scroll button */
96 int visible; /* # of months visible */
97 int firstDay; /* Start month calendar with firstDay's day */
98 int firstDayHighWord; /* High word only used externally */
100 MONTHDAYSTATE *monthdayState;
101 SYSTEMTIME todaysDate;
104 int status; /* See MC_SEL flags */
105 int curSelDay; /* current selected day */
106 int firstSelDay; /* first selected day */
114 RECT title; /* rect for the header above the calendar */
115 RECT titlebtnnext; /* the `next month' button in the header */
116 RECT titlebtnprev; /* the `prev month' button in the header */
117 RECT titlemonth; /* the `month name' txt in the header */
118 RECT titleyear; /* the `year number' txt in the header */
119 RECT wdays; /* week days at top */
120 RECT days; /* calendar area */
121 RECT weeknums; /* week numbers at left side */
122 RECT todayrect; /* `today: xx/xx/xx' text rect */
123 HWND hwndNotify; /* Window to receive the notifications */
124 HWND hWndYearEdit; /* Window Handle of edit box to handle years */
125 HWND hWndYearUpDown;/* Window Handle of updown box to handle years */
126 } MONTHCAL_INFO, *LPMONTHCAL_INFO;
129 /* Offsets of days in the week to the weekday of january 1 in a leap year */
130 static const int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
132 static const WCHAR themeClass[] = { 'S','c','r','o','l','l','b','a','r',0 };
134 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0))
136 /* helper functions */
138 /* returns the number of days in any given month, checking for leap days */
139 /* january is 1, december is 12 */
140 int MONTHCAL_MonthLength(int month, int year)
142 const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
143 /*Wrap around, this eases handling*/
149 /* if we have a leap year add 1 day to February */
150 /* a leap year is a year either divisible by 400 */
151 /* or divisible by 4 and not by 100 */
152 if(month == 2) { /* February */
153 return mdays[month - 1] + ((year%400 == 0) ? 1 : ((year%100 != 0) &&
154 (year%4 == 0)) ? 1 : 0);
157 return mdays[month - 1];
162 /* make sure that time is valid */
163 static int MONTHCAL_ValidateTime(SYSTEMTIME time)
165 if(time.wMonth < 1 || time.wMonth > 12 ) return FALSE;
166 if(time.wDayOfWeek > 6) return FALSE;
167 if(time.wDay > MONTHCAL_MonthLength(time.wMonth, time.wYear))
174 /* Note:Depending on DST, this may be offset by a day.
175 Need to find out if we're on a DST place & adjust the clock accordingly.
176 Above function assumes we have a valid data.
177 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
181 /* returns the day in the week(0 == sunday, 6 == saturday) */
182 /* day(1 == 1st, 2 == 2nd... etc), year is the year value */
183 static int MONTHCAL_CalculateDayOfWeek(DWORD day, DWORD month, DWORD year)
187 return((year + year/4 - year/100 + year/400 +
188 DayOfWeekTable[month-1] + day ) % 7);
191 /* From a given point, calculate the row (weekpos), column(daypos)
192 and day in the calendar. day== 0 mean the last day of tha last month
194 static int MONTHCAL_CalcDayFromPos(const MONTHCAL_INFO *infoPtr, int x, int y,
195 int *daypos,int *weekpos)
197 int retval, firstDay;
200 GetClientRect(infoPtr->hwndSelf, &rcClient);
202 /* if the point is outside the x bounds of the window put
203 it at the boundary */
204 if (x > rcClient.right)
208 *daypos = (x - infoPtr->days.left ) / infoPtr->width_increment;
209 *weekpos = (y - infoPtr->days.top ) / infoPtr->height_increment;
211 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear)+6 - infoPtr->firstDay)%7;
212 retval = *daypos + (7 * *weekpos) - firstDay;
216 /* day is the day of the month, 1 == 1st day of the month */
217 /* sets x and y to be the position of the day */
218 /* x == day, y == week where(0,0) == firstDay, 1st week */
219 static void MONTHCAL_CalcDayXY(const MONTHCAL_INFO *infoPtr, int day, int month,
222 int firstDay, prevMonth;
224 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear) +6 - infoPtr->firstDay)%7;
226 if(month==infoPtr->currentMonth) {
227 *x = (day + firstDay) % 7;
228 *y = (day + firstDay - *x) / 7;
231 if(month < infoPtr->currentMonth) {
232 prevMonth = month - 1;
236 *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay) % 7;
241 *y = MONTHCAL_MonthLength(month, infoPtr->currentYear - 1) / 7;
242 *x = (day + firstDay + MONTHCAL_MonthLength(month,
243 infoPtr->currentYear)) % 7;
247 /* x: column(day), y: row(week) */
248 static void MONTHCAL_CalcDayRect(const MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
250 r->left = infoPtr->days.left + x * infoPtr->width_increment;
251 r->right = r->left + infoPtr->width_increment;
252 r->top = infoPtr->days.top + y * infoPtr->height_increment;
253 r->bottom = r->top + infoPtr->textHeight;
257 /* sets the RECT struct r to the rectangle around the day and month */
258 /* day is the day value of the month(1 == 1st), month is the month */
259 /* value(january == 1, december == 12) */
260 static inline void MONTHCAL_CalcPosFromDay(const MONTHCAL_INFO *infoPtr,
261 int day, int month, RECT *r)
265 MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
266 MONTHCAL_CalcDayRect(infoPtr, r, x, y);
270 /* day is the day in the month(1 == 1st of the month) */
271 /* month is the month value(1 == january, 12 == december) */
272 static void MONTHCAL_CircleDay(const MONTHCAL_INFO *infoPtr, HDC hdc, int day, int month)
274 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
275 HPEN hOldPen2 = SelectObject(hdc, hRedPen);
281 MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
288 points[1].x = x + 0.8 * infoPtr->width_increment;
290 points[2].x = x + 0.9 * infoPtr->width_increment;
292 points[3].x = x + infoPtr->width_increment;
293 points[3].y = y + 0.5 * infoPtr->height_increment;
295 points[4].x = x + infoPtr->width_increment;
296 points[4].y = y + 0.9 * infoPtr->height_increment;
297 points[5].x = x + 0.6 * infoPtr->width_increment;
298 points[5].y = y + 0.9 * infoPtr->height_increment;
299 points[6].x = x + 0.5 * infoPtr->width_increment;
300 points[6].y = y + 0.9 * infoPtr->height_increment; /* bring the bottom up just
301 a hair to fit inside the day rectangle */
303 points[7].x = x + 0.2 * infoPtr->width_increment;
304 points[7].y = y + 0.8 * infoPtr->height_increment;
305 points[8].x = x + 0.1 * infoPtr->width_increment;
306 points[8].y = y + 0.8 * infoPtr->height_increment;
308 points[9].y = y + 0.5 * infoPtr->height_increment;
310 points[10].x = x + 0.1 * infoPtr->width_increment;
311 points[10].y = y + 0.2 * infoPtr->height_increment;
312 points[11].x = x + 0.2 * infoPtr->width_increment;
313 points[11].y = y + 0.3 * infoPtr->height_increment;
314 points[12].x = x + 0.4 * infoPtr->width_increment;
315 points[12].y = y + 0.2 * infoPtr->height_increment;
317 PolyBezier(hdc, points, 13);
318 DeleteObject(hRedPen);
319 SelectObject(hdc, hOldPen2);
323 static void MONTHCAL_DrawDay(const MONTHCAL_INFO *infoPtr, HDC hdc, int day, int month,
324 int x, int y, int bold)
326 static const WCHAR fmtW[] = { '%','d',0 };
329 static int haveBoldFont, haveSelectedDay = FALSE;
334 wsprintfW(buf, fmtW, day);
336 /* No need to check styles: when selection is not valid, it is set to zero.
337 * 1<day<31, so everything is OK.
340 MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
342 if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
343 && (month==infoPtr->currentMonth)) {
347 TRACE("%d %d %d\n",day, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
348 TRACE("%s\n", wine_dbgstr_rect(&r));
349 oldCol = SetTextColor(hdc, infoPtr->monthbk);
350 oldBk = SetBkColor(hdc, infoPtr->trailingtxt);
351 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
352 hrgn = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
353 FillRgn(hdc, hrgn, hbr);
355 /* FIXME: this may need to be changed now b/c of the other
356 drawing changes 11/3/99 CMM */
357 r2.left = r.left - 0.25 * infoPtr->textWidth;
359 r2.right = r.left + 0.5 * infoPtr->textWidth;
360 r2.bottom = r.bottom;
361 if(haveSelectedDay) FillRect(hdc, &r2, hbr);
362 haveSelectedDay = TRUE;
364 haveSelectedDay = FALSE;
367 /* need to add some code for multiple selections */
369 if((bold) &&(!haveBoldFont)) {
370 SelectObject(hdc, infoPtr->hBoldFont);
373 if((!bold) &&(haveBoldFont)) {
374 SelectObject(hdc, infoPtr->hFont);
375 haveBoldFont = FALSE;
378 if(haveSelectedDay) {
379 SetTextColor(hdc, oldCol);
380 SetBkColor(hdc, oldBk);
383 SetBkMode(hdc,TRANSPARENT);
384 DrawTextW(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
386 /* draw a rectangle around the currently selected days text */
387 if((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth))
388 DrawFocusRect(hdc, &r);
392 static void paint_button (const MONTHCAL_INFO *infoPtr, HDC hdc, BOOL btnNext,
393 BOOL pressed, RECT* r)
395 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
399 static const int states[] = {
401 ABS_LEFTNORMAL, ABS_LEFTPRESSED, ABS_LEFTDISABLED,
403 ABS_RIGHTNORMAL, ABS_RIGHTPRESSED, ABS_RIGHTDISABLED
405 int stateNum = btnNext ? 3 : 0;
410 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
411 if (dwStyle & WS_DISABLED) stateNum += 2;
413 DrawThemeBackground (theme, hdc, SBP_ARROWBTN, states[stateNum], r, NULL);
417 int style = btnNext ? DFCS_SCROLLRIGHT : DFCS_SCROLLLEFT;
419 style |= DFCS_PUSHED;
422 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
423 if (dwStyle & WS_DISABLED) style |= DFCS_INACTIVE;
426 DrawFrameControl(hdc, r, DFC_SCROLL, style);
431 static void MONTHCAL_Refresh(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRUCT *ps)
433 static const WCHAR todayW[] = { 'T','o','d','a','y',':',0 };
434 static const WCHAR fmt1W[] = { '%','s',' ','%','l','d',0 };
435 static const WCHAR fmt2W[] = { '%','s',' ','%','s',0 };
436 static const WCHAR fmt3W[] = { '%','d',0 };
437 RECT *title=&infoPtr->title;
438 RECT *prev=&infoPtr->titlebtnprev;
439 RECT *next=&infoPtr->titlebtnnext;
440 RECT *titlemonth=&infoPtr->titlemonth;
441 RECT *titleyear=&infoPtr->titleyear;
445 int i, j, m, mask, day, firstDay, weeknum, weeknum1,prevMonth;
446 int textHeight = infoPtr->textHeight;
453 COLORREF oldTextColor, oldBkColor;
454 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
456 RECT rcDay; /* used in MONTHCAL_CalcDayRect() */
457 SYSTEMTIME localtime;
460 oldTextColor = SetTextColor(hdc, comctl32_color.clrWindowText);
462 /* fill background */
463 hbr = CreateSolidBrush (infoPtr->bk);
464 FillRect(hdc, &ps->rcPaint, hbr);
468 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
470 hbr = CreateSolidBrush(infoPtr->titlebk);
471 FillRect(hdc, title, hbr);
475 /* if the previous button is pressed draw it depressed */
476 if(IntersectRect(&rcTemp, &(ps->rcPaint), prev))
477 paint_button (infoPtr, hdc, FALSE, infoPtr->status & MC_PREVPRESSED, prev);
479 /* if next button is depressed draw it depressed */
480 if(IntersectRect(&rcTemp, &(ps->rcPaint), next))
481 paint_button (infoPtr, hdc, TRUE, infoPtr->status & MC_NEXTPRESSED, next);
483 oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
484 SetTextColor(hdc, infoPtr->titletxt);
485 currentFont = SelectObject(hdc, infoPtr->hBoldFont);
487 GetLocaleInfoW( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+infoPtr->currentMonth -1,
489 wsprintfW(buf, fmt1W, buf1, infoPtr->currentYear);
491 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
493 DrawTextW(hdc, buf, strlenW(buf), title,
494 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
497 /* titlemonth left/right contained rect for whole titletxt('June 1999')
498 * MCM_HitTestInfo wants month & year rects, so prepare these now.
499 *(no, we can't draw them separately; the whole text is centered)
501 GetTextExtentPoint32W(hdc, buf, strlenW(buf), &size);
502 titlemonth->left = title->right / 2 + title->left / 2 - size.cx / 2;
503 titleyear->right = title->right / 2 + title->left / 2 + size.cx / 2;
504 GetTextExtentPoint32W(hdc, buf1, strlenW(buf1), &size);
505 titlemonth->right = titlemonth->left + size.cx;
506 titleyear->left = titlemonth->right;
508 /* draw month area */
509 rcTemp.top=infoPtr->wdays.top;
510 rcTemp.left=infoPtr->wdays.left;
511 rcTemp.bottom=infoPtr->todayrect.bottom;
512 rcTemp.right =infoPtr->todayrect.right;
513 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcTemp))
515 hbr = CreateSolidBrush(infoPtr->monthbk);
516 FillRect(hdc, &rcTemp, hbr);
520 /* draw line under day abbreviations */
522 MoveToEx(hdc, infoPtr->days.left + 3, title->bottom + textHeight + 1, NULL);
523 LineTo(hdc, infoPtr->days.right - 3, title->bottom + textHeight + 1);
525 prevMonth = infoPtr->currentMonth - 1;
526 if(prevMonth == 0) /* if currentMonth is january(1) prevMonth is */
527 prevMonth = 12; /* december(12) of the previous year */
529 infoPtr->wdays.left = infoPtr->days.left = infoPtr->weeknums.right;
530 /* draw day abbreviations */
532 SelectObject(hdc, infoPtr->hFont);
533 SetBkColor(hdc, infoPtr->monthbk);
534 SetTextColor(hdc, infoPtr->trailingtxt);
536 /* copy this rect so we can change the values without changing */
537 /* the original version */
538 days->left = infoPtr->wdays.left;
539 days->right = days->left + infoPtr->width_increment;
540 days->top = infoPtr->wdays.top;
541 days->bottom = infoPtr->wdays.bottom;
543 i = infoPtr->firstDay;
546 GetLocaleInfoW( LOCALE_USER_DEFAULT,LOCALE_SABBREVDAYNAME1 + (i+j+6)%7, buf, countof(buf));
547 DrawTextW(hdc, buf, strlenW(buf), days, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
548 days->left+=infoPtr->width_increment;
549 days->right+=infoPtr->width_increment;
552 /* draw day numbers; first, the previous month */
554 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
556 day = MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) +
557 (infoPtr->firstDay + 7 - firstDay)%7 + 1;
558 if (day > MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear))
560 startofprescal = day;
565 while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear)) {
566 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
567 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
569 MONTHCAL_DrawDay(infoPtr, hdc, day, prevMonth, i, 0,
570 infoPtr->monthdayState[m] & mask);
578 /* draw `current' month */
580 day = 1; /* start at the beginning of the current month */
582 infoPtr->firstDayplace = i;
583 SetTextColor(hdc, infoPtr->txt);
587 /* draw the first week of the current month */
589 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
590 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
593 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth, i, 0,
594 infoPtr->monthdayState[m] & mask);
596 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
597 (day==infoPtr->todaysDate.wDay) &&
598 (infoPtr->currentYear == infoPtr->todaysDate.wYear)) {
599 if(!(dwStyle & MCS_NOTODAYCIRCLE))
600 MONTHCAL_CircleDay(infoPtr, hdc, day, infoPtr->currentMonth);
609 j = 1; /* move to the 2nd week of the current month */
610 i = 0; /* move back to sunday */
611 while(day <= MONTHCAL_MonthLength(infoPtr->currentMonth, infoPtr->currentYear)) {
612 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
613 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
615 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth, i, j,
616 infoPtr->monthdayState[m] & mask);
618 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
619 (day==infoPtr->todaysDate.wDay) &&
620 (infoPtr->currentYear == infoPtr->todaysDate.wYear))
621 if(!(dwStyle & MCS_NOTODAYCIRCLE))
622 MONTHCAL_CircleDay(infoPtr, hdc, day, infoPtr->currentMonth);
627 if(i>6) { /* past saturday, goto the next weeks sunday */
633 /* draw `next' month */
635 day = 1; /* start at the first day of the next month */
639 SetTextColor(hdc, infoPtr->trailingtxt);
640 while((i<7) &&(j<6)) {
641 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
642 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
644 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth + 1, i, j,
645 infoPtr->monthdayState[m] & mask);
651 if(i==7) { /* past saturday, go to next week's sunday */
656 SetTextColor(hdc, infoPtr->txt);
659 /* draw `today' date if style allows it, and draw a circle before today's
660 * date if necessary */
662 if(!(dwStyle & MCS_NOTODAY)) {
663 if(!(dwStyle & MCS_NOTODAYCIRCLE)) {
664 /*day is the number of days from nextmonth we put on the calendar */
665 MONTHCAL_CircleDay(infoPtr, hdc,
666 day+MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear),
667 infoPtr->currentMonth);
669 if (!LoadStringW(COMCTL32_hModule,IDM_TODAY,buf1,countof(buf1)))
671 WARN("Can't load resource\n");
672 strcpyW(buf1, todayW);
674 MONTHCAL_CalcDayRect(infoPtr, &rtoday, 1, 6);
675 MONTHCAL_CopyTime(&infoPtr->todaysDate,&localtime);
676 GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&localtime,NULL,buf2,countof(buf2));
677 wsprintfW(buf, fmt2W, buf1, buf2);
678 SelectObject(hdc, infoPtr->hBoldFont);
680 DrawTextW(hdc, buf, -1, &rtoday, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE);
681 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rtoday))
683 DrawTextW(hdc, buf, -1, &rtoday, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
685 SelectObject(hdc, infoPtr->hFont);
688 /*eventually draw week numbers*/
689 if(dwStyle & MCS_WEEKNUMBERS) {
690 /* display weeknumbers*/
693 /* Rules what week to call the first week of a new year:
694 LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?):
695 The week containing Jan 1 is the first week of year
696 LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany):
697 First week of year must contain 4 days of the new year
698 LOCALE_IFIRSTWEEKOFYEAR == 1 (what contries?)
699 The first week of the year must contain only days of the new year
701 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, countof(buf));
702 weeknum = atoiW(buf);
713 if (infoPtr->currentMonth < 2)
715 /* calculate all those exceptions for january */
716 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
717 if ((infoPtr->firstDay +7 - weeknum1)%7 > mindays)
723 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear-1);
724 weeknum +=startofprescal+ 7;
726 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear-1);
727 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
734 for(i=0; i<prevMonth-1; i++)
735 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear);
736 weeknum +=startofprescal+ 7;
738 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
739 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
742 days->left = infoPtr->weeknums.left;
743 days->right = infoPtr->weeknums.right;
744 days->top = infoPtr->weeknums.top;
745 days->bottom = days->top +infoPtr->height_increment;
747 if((i==0)&&(weeknum>50))
749 wsprintfW(buf, fmt3W, weeknum);
752 else if((i==5)&&(weeknum>47))
754 wsprintfW(buf, fmt3W, 1);
757 wsprintfW(buf, fmt3W, weeknum + i);
758 DrawTextW(hdc, buf, -1, days, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
759 days->top+=infoPtr->height_increment;
760 days->bottom+=infoPtr->height_increment;
763 MoveToEx(hdc, infoPtr->weeknums.right, infoPtr->weeknums.top + 3 , NULL);
764 LineTo(hdc, infoPtr->weeknums.right, infoPtr->weeknums.bottom );
767 /* currentFont was font at entering Refresh */
769 SetBkColor(hdc, oldBkColor);
770 SelectObject(hdc, currentFont);
771 SetTextColor(hdc, oldTextColor);
776 MONTHCAL_GetMinReqRect(const MONTHCAL_INFO *infoPtr, LPARAM lParam)
778 LPRECT lpRect = (LPRECT) lParam;
780 TRACE("rect %p\n", lpRect);
782 /* validate parameters */
784 if((infoPtr==NULL) ||(lpRect == NULL) ) return FALSE;
786 lpRect->left = infoPtr->title.left;
787 lpRect->top = infoPtr->title.top;
788 lpRect->right = infoPtr->title.right;
789 lpRect->bottom = infoPtr->todayrect.bottom;
790 AdjustWindowRect(lpRect, GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE), FALSE);
792 TRACE("%s\n", wine_dbgstr_rect(lpRect));
799 MONTHCAL_GetColor(const MONTHCAL_INFO *infoPtr, WPARAM wParam)
803 switch((int)wParam) {
804 case MCSC_BACKGROUND:
809 return infoPtr->titlebk;
811 return infoPtr->titletxt;
813 return infoPtr->monthbk;
814 case MCSC_TRAILINGTEXT:
815 return infoPtr->trailingtxt;
823 MONTHCAL_SetColor(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
827 TRACE("%ld: color %08lx\n", wParam, lParam);
829 switch((int)wParam) {
830 case MCSC_BACKGROUND:
832 infoPtr->bk = (COLORREF)lParam;
836 infoPtr->txt = (COLORREF)lParam;
839 prev = infoPtr->titlebk;
840 infoPtr->titlebk = (COLORREF)lParam;
843 prev=infoPtr->titletxt;
844 infoPtr->titletxt = (COLORREF)lParam;
847 prev = infoPtr->monthbk;
848 infoPtr->monthbk = (COLORREF)lParam;
850 case MCSC_TRAILINGTEXT:
851 prev = infoPtr->trailingtxt;
852 infoPtr->trailingtxt = (COLORREF)lParam;
856 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
862 MONTHCAL_GetMonthDelta(const MONTHCAL_INFO *infoPtr)
867 return infoPtr->delta;
869 return infoPtr->visible;
874 MONTHCAL_SetMonthDelta(MONTHCAL_INFO *infoPtr, WPARAM wParam)
876 int prev = infoPtr->delta;
878 TRACE("delta %ld\n", wParam);
880 infoPtr->delta = (int)wParam;
886 MONTHCAL_GetFirstDayOfWeek(const MONTHCAL_INFO *infoPtr)
888 return MAKELONG(infoPtr->firstDay, infoPtr->firstDayHighWord);
892 /* sets the first day of the week that will appear in the control */
893 /* 0 == Sunday, 6 == Saturday */
894 /* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
895 /* FIXME: we need more error checking here */
897 MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO *infoPtr, LPARAM lParam)
899 int prev = MAKELONG(infoPtr->firstDay, infoPtr->firstDayHighWord);
903 TRACE("day %ld\n", lParam);
905 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, buf, countof(buf));
906 TRACE("%s %d\n", debugstr_w(buf), strlenW(buf));
908 localFirstDay = atoiW(buf);
912 infoPtr->firstDay = localFirstDay;
913 infoPtr->firstDayHighWord = FALSE;
917 infoPtr->firstDay = 6; /* max first day allowed */
918 infoPtr->firstDayHighWord = TRUE;
922 infoPtr->firstDay = lParam;
923 infoPtr->firstDayHighWord = TRUE;
931 MONTHCAL_GetMonthRange(const MONTHCAL_INFO *infoPtr)
935 return infoPtr->monthRange;
940 MONTHCAL_GetMaxTodayWidth(const MONTHCAL_INFO *infoPtr)
942 return(infoPtr->todayrect.right - infoPtr->todayrect.left);
947 MONTHCAL_SetRange(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
949 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *)lParam;
950 FILETIME ft_min, ft_max;
952 TRACE("%lx %lx\n", wParam, lParam);
954 if ((wParam & GDTR_MIN && !MONTHCAL_ValidateTime(lprgSysTimeArray[0])) ||
955 (wParam & GDTR_MAX && !MONTHCAL_ValidateTime(lprgSysTimeArray[1])))
958 if (wParam & GDTR_MIN)
960 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minDate);
961 infoPtr->rangeValid |= GDTR_MIN;
963 if (wParam & GDTR_MAX)
965 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxDate);
966 infoPtr->rangeValid |= GDTR_MAX;
969 /* Only one limit set - we are done */
970 if ((infoPtr->rangeValid & (GDTR_MIN | GDTR_MAX)) != (GDTR_MIN | GDTR_MAX))
973 SystemTimeToFileTime(&infoPtr->maxDate, &ft_max);
974 SystemTimeToFileTime(&infoPtr->minDate, &ft_min);
976 if (CompareFileTime(&ft_min, &ft_max) > 0)
978 if ((wParam & (GDTR_MIN | GDTR_MAX)) == (GDTR_MIN | GDTR_MAX))
980 /* Native swaps limits only when both limits are being set. */
981 SYSTEMTIME st_tmp = infoPtr->minDate;
982 infoPtr->minDate = infoPtr->maxDate;
983 infoPtr->maxDate = st_tmp;
987 /* Reset the other limit. */
988 /* FIXME: native sets date&time to 0. Should we do this too? */
989 infoPtr->rangeValid &= wParam & GDTR_MIN ? ~GDTR_MAX : ~GDTR_MIN ;
998 MONTHCAL_GetRange(HWND hwnd, LPARAM lParam)
1000 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1001 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *)lParam;
1003 /* validate parameters */
1005 if((infoPtr==NULL) || (lprgSysTimeArray==NULL)) return FALSE;
1007 MONTHCAL_CopyTime(&infoPtr->maxDate, &lprgSysTimeArray[1]);
1008 MONTHCAL_CopyTime(&infoPtr->minDate, &lprgSysTimeArray[0]);
1010 return infoPtr->rangeValid;
1015 MONTHCAL_SetDayState(const MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
1018 int i, iMonths = (int)wParam;
1019 MONTHDAYSTATE *dayStates = (LPMONTHDAYSTATE)lParam;
1021 TRACE("%lx %lx\n", wParam, lParam);
1022 if(iMonths!=infoPtr->monthRange) return 0;
1024 for(i=0; i<iMonths; i++)
1025 infoPtr->monthdayState[i] = dayStates[i];
1030 MONTHCAL_GetCurSel(const MONTHCAL_INFO *infoPtr, LPARAM lParam)
1032 SYSTEMTIME *lpSel = (SYSTEMTIME *) lParam;
1034 TRACE("%lx\n", lParam);
1035 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1036 if(infoPtr->dwStyle & MCS_MULTISELECT) return FALSE;
1038 MONTHCAL_CopyTime(&infoPtr->minSel, lpSel);
1039 TRACE("%d/%d/%d\n", lpSel->wYear, lpSel->wMonth, lpSel->wDay);
1043 /* FIXME: if the specified date is not visible, make it visible */
1044 /* FIXME: redraw? */
1046 MONTHCAL_SetCurSel(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1048 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
1050 TRACE("%lx\n", lParam);
1051 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1052 if(infoPtr->dwStyle & MCS_MULTISELECT) return FALSE;
1054 if(!MONTHCAL_ValidateTime(*lpSel)) return FALSE;
1056 infoPtr->currentMonth=lpSel->wMonth;
1057 infoPtr->currentYear=lpSel->wYear;
1059 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
1060 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
1062 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1069 MONTHCAL_GetMaxSelCount(const MONTHCAL_INFO *infoPtr)
1071 return infoPtr->maxSelCount;
1076 MONTHCAL_SetMaxSelCount(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1078 TRACE("%lx\n", wParam);
1080 if(infoPtr->dwStyle & MCS_MULTISELECT) {
1081 infoPtr->maxSelCount = wParam;
1089 MONTHCAL_GetSelRange(const MONTHCAL_INFO *infoPtr, LPARAM lParam)
1091 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1093 TRACE("%lx\n", lParam);
1095 /* validate parameters */
1097 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1099 if(infoPtr->dwStyle & MCS_MULTISELECT)
1101 MONTHCAL_CopyTime(&infoPtr->maxSel, &lprgSysTimeArray[1]);
1102 MONTHCAL_CopyTime(&infoPtr->minSel, &lprgSysTimeArray[0]);
1103 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1112 MONTHCAL_SetSelRange(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1114 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1116 TRACE("%lx\n", lParam);
1118 /* validate parameters */
1120 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1122 if(infoPtr->dwStyle & MCS_MULTISELECT)
1124 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxSel);
1125 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minSel);
1126 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1135 MONTHCAL_GetToday(const MONTHCAL_INFO *infoPtr, LPARAM lParam)
1137 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1139 TRACE("%lx\n", lParam);
1141 /* validate parameters */
1143 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
1144 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
1150 MONTHCAL_SetToday(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1152 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1154 TRACE("%lx\n", lParam);
1156 /* validate parameters */
1158 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
1159 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
1160 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1166 MONTHCAL_HitTest(const MONTHCAL_INFO *infoPtr, LPARAM lParam)
1168 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
1177 ZeroMemory(&lpht->st, sizeof(lpht->st));
1179 /* Comment in for debugging...
1180 TRACE("%d %d wd[%d %d %d %d] d[%d %d %d %d] t[%d %d %d %d] wn[%d %d %d %d]\n", x, y,
1181 infoPtr->wdays.left, infoPtr->wdays.right,
1182 infoPtr->wdays.top, infoPtr->wdays.bottom,
1183 infoPtr->days.left, infoPtr->days.right,
1184 infoPtr->days.top, infoPtr->days.bottom,
1185 infoPtr->todayrect.left, infoPtr->todayrect.right,
1186 infoPtr->todayrect.top, infoPtr->todayrect.bottom,
1187 infoPtr->weeknums.left, infoPtr->weeknums.right,
1188 infoPtr->weeknums.top, infoPtr->weeknums.bottom);
1191 /* are we in the header? */
1193 if(PtInRect(&infoPtr->title, lpht->pt)) {
1194 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1195 retval = MCHT_TITLEBTNPREV;
1198 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1199 retval = MCHT_TITLEBTNNEXT;
1202 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1203 retval = MCHT_TITLEMONTH;
1206 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1207 retval = MCHT_TITLEYEAR;
1211 retval = MCHT_TITLE;
1215 day = MONTHCAL_CalcDayFromPos(infoPtr,x,y,&wday,&wnum);
1216 if(PtInRect(&infoPtr->wdays, lpht->pt)) {
1217 retval = MCHT_CALENDARDAY;
1218 lpht->st.wYear = infoPtr->currentYear;
1219 lpht->st.wMonth = (day < 1)? infoPtr->currentMonth -1 : infoPtr->currentMonth;
1220 lpht->st.wDay = (day < 1)?
1221 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day : day;
1224 if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
1225 retval = MCHT_CALENDARWEEKNUM;
1226 lpht->st.wYear = infoPtr->currentYear;
1227 lpht->st.wMonth = (day < 1) ? infoPtr->currentMonth -1 :
1228 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1229 infoPtr->currentMonth +1 :infoPtr->currentMonth;
1230 lpht->st.wDay = (day < 1 ) ?
1231 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day :
1232 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1233 day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) : day;
1236 if(PtInRect(&infoPtr->days, lpht->pt))
1238 lpht->st.wYear = infoPtr->currentYear;
1241 retval = MCHT_CALENDARDATEPREV;
1242 lpht->st.wMonth = infoPtr->currentMonth - 1;
1243 if (lpht->st.wMonth <1)
1245 lpht->st.wMonth = 12;
1248 lpht->st.wDay = MONTHCAL_MonthLength(lpht->st.wMonth,lpht->st.wYear) -day;
1250 else if (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear))
1252 retval = MCHT_CALENDARDATENEXT;
1253 lpht->st.wMonth = infoPtr->currentMonth + 1;
1254 if (lpht->st.wMonth <12)
1256 lpht->st.wMonth = 1;
1259 lpht->st.wDay = day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) ;
1262 retval = MCHT_CALENDARDATE;
1263 lpht->st.wMonth = infoPtr->currentMonth;
1264 lpht->st.wDay = day;
1265 lpht->st.wDayOfWeek = MONTHCAL_CalculateDayOfWeek(day,lpht->st.wMonth,lpht->st.wYear);
1269 if(PtInRect(&infoPtr->todayrect, lpht->pt)) {
1270 retval = MCHT_TODAYLINK;
1275 /* Hit nothing special? What's left must be background :-) */
1277 retval = MCHT_CALENDARBK;
1279 lpht->uHit = retval;
1284 static void MONTHCAL_GoToNextMonth(MONTHCAL_INFO *infoPtr)
1286 TRACE("MONTHCAL_GoToNextMonth\n");
1288 infoPtr->currentMonth++;
1289 if(infoPtr->currentMonth > 12) {
1290 infoPtr->currentYear++;
1291 infoPtr->currentMonth = 1;
1294 if(infoPtr->dwStyle & MCS_DAYSTATE) {
1298 nmds.nmhdr.hwndFrom = infoPtr->hwndSelf;
1299 nmds.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1300 nmds.nmhdr.code = MCN_GETDAYSTATE;
1301 nmds.cDayState = infoPtr->monthRange;
1302 nmds.prgDayState = Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1304 nmds.stStart = infoPtr->todaysDate;
1305 nmds.stStart.wYear = infoPtr->currentYear;
1306 nmds.stStart.wMonth = infoPtr->currentMonth;
1307 nmds.stStart.wDay = 1;
1309 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmds.nmhdr.idFrom, (LPARAM)&nmds);
1310 for(i=0; i<infoPtr->monthRange; i++)
1311 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1316 static void MONTHCAL_GoToPrevMonth(MONTHCAL_INFO *infoPtr)
1320 infoPtr->currentMonth--;
1321 if(infoPtr->currentMonth < 1) {
1322 infoPtr->currentYear--;
1323 infoPtr->currentMonth = 12;
1326 if(infoPtr->dwStyle & MCS_DAYSTATE) {
1330 nmds.nmhdr.hwndFrom = infoPtr->hwndSelf;
1331 nmds.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1332 nmds.nmhdr.code = MCN_GETDAYSTATE;
1333 nmds.cDayState = infoPtr->monthRange;
1334 nmds.prgDayState = Alloc
1335 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1337 nmds.stStart = infoPtr->todaysDate;
1338 nmds.stStart.wYear = infoPtr->currentYear;
1339 nmds.stStart.wMonth = infoPtr->currentMonth;
1340 nmds.stStart.wDay = 1;
1342 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmds.nmhdr.idFrom, (LPARAM)&nmds);
1343 for(i=0; i<infoPtr->monthRange; i++)
1344 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1349 MONTHCAL_RButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1351 static const WCHAR todayW[] = { 'G','o',' ','t','o',' ','T','o','d','a','y',':',0 };
1356 hMenu = CreatePopupMenu();
1357 if (!LoadStringW(COMCTL32_hModule,IDM_GOTODAY,buf,countof(buf)))
1359 WARN("Can't load resource\n");
1360 strcpyW(buf, todayW);
1362 AppendMenuW(hMenu, MF_STRING|MF_ENABLED,1, buf);
1363 menupoint.x=(short)LOWORD(lParam);
1364 menupoint.y=(short)HIWORD(lParam);
1365 ClientToScreen(infoPtr->hwndSelf, &menupoint);
1366 if( TrackPopupMenu(hMenu,TPM_RIGHTBUTTON| TPM_NONOTIFY|TPM_RETURNCMD,
1367 menupoint.x, menupoint.y, 0, infoPtr->hwndSelf, NULL))
1369 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1370 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1371 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1377 MONTHCAL_LButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1379 static const WCHAR EditW[] = { 'E','D','I','T',0 };
1383 RECT rcDay; /* used in determining area to invalidate */
1388 TRACE("%lx\n", lParam);
1390 if (infoPtr->hWndYearUpDown)
1392 infoPtr->currentYear=SendMessageW(infoPtr->hWndYearUpDown, UDM_SETPOS, 0, 0);
1393 if(!DestroyWindow(infoPtr->hWndYearUpDown))
1395 FIXME("Can't destroy Updown Control\n");
1398 infoPtr->hWndYearUpDown=0;
1399 if(!DestroyWindow(infoPtr->hWndYearEdit))
1401 FIXME("Can't destroy Updown Control\n");
1404 infoPtr->hWndYearEdit=0;
1405 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1408 ht.pt.x = (short)LOWORD(lParam);
1409 ht.pt.y = (short)HIWORD(lParam);
1410 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1412 /* FIXME: these flags should be checked by */
1413 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1415 if(hit ==MCHT_TITLEBTNNEXT) {
1416 MONTHCAL_GoToNextMonth(infoPtr);
1417 infoPtr->status = MC_NEXTPRESSED;
1418 SetTimer(infoPtr->hwndSelf, MC_NEXTMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1419 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1422 if(hit == MCHT_TITLEBTNPREV){
1423 MONTHCAL_GoToPrevMonth(infoPtr);
1424 infoPtr->status = MC_PREVPRESSED;
1425 SetTimer(infoPtr->hwndSelf, MC_PREVMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1426 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1430 if(hit == MCHT_TITLEMONTH) {
1431 hMenu = CreatePopupMenu();
1435 GetLocaleInfoW(LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+i, buf,countof(buf));
1436 AppendMenuW(hMenu, MF_STRING|MF_ENABLED,i+1, buf);
1438 menupoint.x=infoPtr->titlemonth.right;
1439 menupoint.y=infoPtr->titlemonth.bottom;
1440 ClientToScreen(infoPtr->hwndSelf, &menupoint);
1441 i= TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD,
1442 menupoint.x, menupoint.y, 0, infoPtr->hwndSelf, NULL);
1443 if ((i>0) && (i<13))
1445 infoPtr->currentMonth=i;
1446 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1449 if(hit == MCHT_TITLEYEAR) {
1450 infoPtr->hWndYearEdit=CreateWindowExW(0,
1453 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT,
1454 infoPtr->titleyear.left+3,infoPtr->titlebtnnext.top,
1455 infoPtr->titleyear.right-infoPtr->titleyear.left+4,
1456 infoPtr->textHeight,
1461 SendMessageW( infoPtr->hWndYearEdit, WM_SETFONT, (WPARAM) infoPtr->hBoldFont, (LPARAM)TRUE);
1462 infoPtr->hWndYearUpDown=CreateWindowExW(0,
1465 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT|UDS_NOTHOUSANDS|UDS_ARROWKEYS,
1466 infoPtr->titleyear.right+7,infoPtr->titlebtnnext.top,
1468 infoPtr->textHeight,
1473 SendMessageW(infoPtr->hWndYearUpDown, UDM_SETRANGE, 0, MAKELONG (9999, 1753));
1474 SendMessageW(infoPtr->hWndYearUpDown, UDM_SETBUDDY, (WPARAM) infoPtr->hWndYearEdit, 0);
1475 SendMessageW(infoPtr->hWndYearUpDown, UDM_SETPOS, 0, infoPtr->currentYear);
1479 if(hit == MCHT_TODAYLINK) {
1482 infoPtr->curSelDay = infoPtr->todaysDate.wDay;
1483 infoPtr->firstSelDay = infoPtr->todaysDate.wDay;
1484 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1485 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1486 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minSel);
1487 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxSel);
1488 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1490 nmsc.nmhdr.hwndFrom = infoPtr->hwndSelf;
1491 nmsc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1492 nmsc.nmhdr.code = MCN_SELCHANGE;
1493 MONTHCAL_CopyTime(&infoPtr->minSel, &nmsc.stSelStart);
1494 MONTHCAL_CopyTime(&infoPtr->maxSel, &nmsc.stSelEnd);
1495 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1497 nmsc.nmhdr.code = MCN_SELECT;
1498 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1501 if(hit == MCHT_CALENDARDATE) {
1502 SYSTEMTIME selArray[2];
1505 MONTHCAL_CopyTime(&ht.st, &selArray[0]);
1506 MONTHCAL_CopyTime(&ht.st, &selArray[1]);
1507 MONTHCAL_SetSelRange(infoPtr, (LPARAM)selArray);
1508 MONTHCAL_SetCurSel(infoPtr, (LPARAM)selArray);
1509 TRACE("MCHT_CALENDARDATE\n");
1510 nmsc.nmhdr.hwndFrom = infoPtr->hwndSelf;
1511 nmsc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1512 nmsc.nmhdr.code = MCN_SELCHANGE;
1513 MONTHCAL_CopyTime(&infoPtr->minSel,&nmsc.stSelStart);
1514 MONTHCAL_CopyTime(&infoPtr->maxSel,&nmsc.stSelEnd);
1516 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1519 /* redraw both old and new days if the selected day changed */
1520 if(infoPtr->curSelDay != ht.st.wDay) {
1521 MONTHCAL_CalcPosFromDay(infoPtr, ht.st.wDay, ht.st.wMonth, &rcDay);
1522 InvalidateRect(infoPtr->hwndSelf, &rcDay, TRUE);
1524 MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->curSelDay, infoPtr->currentMonth, &rcDay);
1525 InvalidateRect(infoPtr->hwndSelf, &rcDay, TRUE);
1528 infoPtr->firstSelDay = ht.st.wDay;
1529 infoPtr->curSelDay = ht.st.wDay;
1530 infoPtr->status = MC_SEL_LBUTDOWN;
1539 MONTHCAL_LButtonUp(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1543 BOOL redraw = FALSE;
1549 if(infoPtr->status & MC_NEXTPRESSED) {
1550 KillTimer(infoPtr->hwndSelf, MC_NEXTMONTHTIMER);
1551 infoPtr->status &= ~MC_NEXTPRESSED;
1554 if(infoPtr->status & MC_PREVPRESSED) {
1555 KillTimer(infoPtr->hwndSelf, MC_PREVMONTHTIMER);
1556 infoPtr->status &= ~MC_PREVPRESSED;
1560 ht.pt.x = (short)LOWORD(lParam);
1561 ht.pt.y = (short)HIWORD(lParam);
1562 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1564 infoPtr->status = MC_SEL_LBUTUP;
1566 if(hit ==MCHT_CALENDARDATENEXT) {
1567 MONTHCAL_GoToNextMonth(infoPtr);
1568 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1571 if(hit == MCHT_CALENDARDATEPREV){
1572 MONTHCAL_GoToPrevMonth(infoPtr);
1573 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1576 nmhdr.hwndFrom = infoPtr->hwndSelf;
1577 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1578 nmhdr.code = NM_RELEASEDCAPTURE;
1579 TRACE("Sent notification from %p to %p\n", infoPtr->hwndSelf, infoPtr->hwndNotify);
1581 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
1582 /* redraw if necessary */
1584 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1585 /* only send MCN_SELECT if currently displayed month's day was selected */
1586 if(hit == MCHT_CALENDARDATE) {
1587 nmsc.nmhdr.hwndFrom = infoPtr->hwndSelf;
1588 nmsc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1589 nmsc.nmhdr.code = MCN_SELECT;
1590 MONTHCAL_CopyTime(&infoPtr->minSel, &nmsc.stSelStart);
1591 MONTHCAL_CopyTime(&infoPtr->maxSel, &nmsc.stSelEnd);
1593 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1601 MONTHCAL_Timer(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1603 BOOL redraw = FALSE;
1605 TRACE("%ld\n", wParam);
1608 case MC_NEXTMONTHTIMER:
1610 MONTHCAL_GoToNextMonth(infoPtr);
1612 case MC_PREVMONTHTIMER:
1614 MONTHCAL_GoToPrevMonth(infoPtr);
1617 ERR("got unknown timer\n");
1621 /* redraw only if necessary */
1623 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1630 MONTHCAL_MouseMove(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1633 int oldselday, selday, hit;
1636 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1638 ht.pt.x = (short)LOWORD(lParam);
1639 ht.pt.y = (short)HIWORD(lParam);
1641 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1643 /* not on the calendar date numbers? bail out */
1644 TRACE("hit:%x\n",hit);
1645 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1647 selday = ht.st.wDay;
1648 oldselday = infoPtr->curSelDay;
1649 infoPtr->curSelDay = selday;
1650 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1652 if(infoPtr->dwStyle & MCS_MULTISELECT) {
1653 SYSTEMTIME selArray[2];
1656 MONTHCAL_GetSelRange(infoPtr, (LPARAM)selArray);
1658 if(infoPtr->firstSelDay==selArray[0].wDay) i=1;
1659 TRACE("oldRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1660 if(infoPtr->firstSelDay==selArray[1].wDay) {
1661 /* 1st time we get here: selArray[0]=selArray[1]) */
1662 /* if we're still at the first selected date, return */
1663 if(infoPtr->firstSelDay==selday) goto done;
1664 if(selday<infoPtr->firstSelDay) i = 0;
1667 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1668 if(selday>infoPtr->firstSelDay)
1669 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1671 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1674 if(selArray[i].wDay!=selday) {
1675 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1677 selArray[i].wDay = selday;
1679 if(selArray[0].wDay>selArray[1].wDay) {
1681 tempday = selArray[1].wDay;
1682 selArray[1].wDay = selArray[0].wDay;
1683 selArray[0].wDay = tempday;
1686 MONTHCAL_SetSelRange(infoPtr, (LPARAM)selArray);
1692 /* only redraw if the currently selected day changed */
1693 /* FIXME: this should specify a rectangle containing only the days that changed */
1694 /* using InvalidateRect */
1695 if(oldselday != infoPtr->curSelDay)
1696 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1703 MONTHCAL_Paint(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1710 GetClientRect(infoPtr->hwndSelf, &ps.rcPaint);
1714 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
1716 MONTHCAL_Refresh(infoPtr, hdc, &ps);
1717 if (!wParam) EndPaint(infoPtr->hwndSelf, &ps);
1723 MONTHCAL_KillFocus(const MONTHCAL_INFO *infoPtr, HWND hFocusWnd)
1727 if (infoPtr->hwndNotify != hFocusWnd)
1728 ShowWindow(infoPtr->hwndSelf, SW_HIDE);
1730 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1737 MONTHCAL_SetFocus(const MONTHCAL_INFO *infoPtr)
1741 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1746 /* sets the size information */
1747 static void MONTHCAL_UpdateSize(MONTHCAL_INFO *infoPtr)
1749 static const WCHAR SunW[] = { 'S','u','n',0 };
1750 static const WCHAR O0W[] = { '0','0',0 };
1751 HDC hdc = GetDC(infoPtr->hwndSelf);
1752 RECT *title=&infoPtr->title;
1753 RECT *prev=&infoPtr->titlebtnprev;
1754 RECT *next=&infoPtr->titlebtnnext;
1755 RECT *titlemonth=&infoPtr->titlemonth;
1756 RECT *titleyear=&infoPtr->titleyear;
1757 RECT *wdays=&infoPtr->wdays;
1758 RECT *weeknumrect=&infoPtr->weeknums;
1759 RECT *days=&infoPtr->days;
1760 RECT *todayrect=&infoPtr->todayrect;
1764 int xdiv, left_offset;
1767 GetClientRect(infoPtr->hwndSelf, &rcClient);
1769 currentFont = SelectObject(hdc, infoPtr->hFont);
1771 /* get the height and width of each day's text */
1772 GetTextMetricsW(hdc, &tm);
1773 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading + tm.tmInternalLeading;
1774 GetTextExtentPoint32W(hdc, SunW, 3, &size);
1775 infoPtr->textWidth = size.cx + 2;
1777 /* recalculate the height and width increments and offsets */
1778 GetTextExtentPoint32W(hdc, O0W, 2, &size);
1780 xdiv = (infoPtr->dwStyle & MCS_WEEKNUMBERS) ? 8 : 7;
1782 infoPtr->width_increment = size.cx * 2 + 4;
1783 infoPtr->height_increment = infoPtr->textHeight;
1784 left_offset = (rcClient.right - rcClient.left) - (infoPtr->width_increment * xdiv);
1786 /* calculate title area */
1787 title->top = rcClient.top;
1788 title->bottom = title->top + 3 * infoPtr->height_increment / 2;
1789 title->left = left_offset;
1790 title->right = rcClient.right;
1792 /* set the dimensions of the next and previous buttons and center */
1793 /* the month text vertically */
1794 prev->top = next->top = title->top + 4;
1795 prev->bottom = next->bottom = title->bottom - 4;
1796 prev->left = title->left + 4;
1797 prev->right = prev->left + (title->bottom - title->top) ;
1798 next->right = title->right - 4;
1799 next->left = next->right - (title->bottom - title->top);
1801 /* titlemonth->left and right change based upon the current month */
1802 /* and are recalculated in refresh as the current month may change */
1803 /* without the control being resized */
1804 titlemonth->top = titleyear->top = title->top + (infoPtr->height_increment)/2;
1805 titlemonth->bottom = titleyear->bottom = title->bottom - (infoPtr->height_increment)/2;
1807 /* setup the dimensions of the rectangle we draw the names of the */
1808 /* days of the week in */
1809 weeknumrect->left = left_offset;
1810 if(infoPtr->dwStyle & MCS_WEEKNUMBERS)
1811 weeknumrect->right=prev->right;
1813 weeknumrect->right=weeknumrect->left;
1814 wdays->left = days->left = weeknumrect->right;
1815 wdays->right = days->right = wdays->left + 7 * infoPtr->width_increment;
1816 wdays->top = title->bottom ;
1817 wdays->bottom = wdays->top + infoPtr->height_increment;
1819 days->top = weeknumrect->top = wdays->bottom ;
1820 days->bottom = weeknumrect->bottom = days->top + 6 * infoPtr->height_increment;
1822 todayrect->left = rcClient.left;
1823 todayrect->right = rcClient.right;
1824 todayrect->top = days->bottom;
1825 todayrect->bottom = days->bottom + infoPtr->height_increment;
1827 TRACE("dx=%d dy=%d client[%s] title[%s] wdays[%s] days[%s] today[%s]\n",
1828 infoPtr->width_increment,infoPtr->height_increment,
1829 wine_dbgstr_rect(&rcClient),
1830 wine_dbgstr_rect(title),
1831 wine_dbgstr_rect(wdays),
1832 wine_dbgstr_rect(days),
1833 wine_dbgstr_rect(todayrect));
1835 /* restore the originally selected font */
1836 SelectObject(hdc, currentFont);
1838 ReleaseDC(infoPtr->hwndSelf, hdc);
1841 static LRESULT MONTHCAL_Size(MONTHCAL_INFO *infoPtr, int Width, int Height)
1843 TRACE("(width=%d, height=%d)\n", Width, Height);
1845 MONTHCAL_UpdateSize(infoPtr);
1847 /* invalidate client area and erase background */
1848 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1853 static LRESULT MONTHCAL_GetFont(const MONTHCAL_INFO *infoPtr)
1855 return (LRESULT)infoPtr->hFont;
1858 static LRESULT MONTHCAL_SetFont(MONTHCAL_INFO *infoPtr, HFONT hFont, BOOL redraw)
1863 if (!hFont) return 0;
1865 hOldFont = infoPtr->hFont;
1866 infoPtr->hFont = hFont;
1868 GetObjectW(infoPtr->hFont, sizeof(lf), &lf);
1869 lf.lfWeight = FW_BOLD;
1870 infoPtr->hBoldFont = CreateFontIndirectW(&lf);
1872 MONTHCAL_UpdateSize(infoPtr);
1875 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1877 return (LRESULT)hOldFont;
1880 /* update theme after a WM_THEMECHANGED message */
1881 static LRESULT theme_changed (const MONTHCAL_INFO* infoPtr)
1883 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
1884 CloseThemeData (theme);
1885 OpenThemeData (infoPtr->hwndSelf, themeClass);
1889 static INT MONTHCAL_StyleChanged(MONTHCAL_INFO *infoPtr, WPARAM wStyleType,
1890 const STYLESTRUCT *lpss)
1892 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
1893 wStyleType, lpss->styleOld, lpss->styleNew);
1895 if (wStyleType != GWL_STYLE) return 0;
1897 infoPtr->dwStyle = lpss->styleNew;
1902 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1904 MONTHCAL_Create(HWND hwnd, LPARAM lParam)
1906 MONTHCAL_INFO *infoPtr;
1908 /* allocate memory for info structure */
1909 infoPtr = Alloc(sizeof(MONTHCAL_INFO));
1910 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
1912 if(infoPtr == NULL) {
1913 ERR( "could not allocate info memory!\n");
1917 infoPtr->hwndSelf = hwnd;
1918 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1919 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
1921 MONTHCAL_SetFont(infoPtr, GetStockObject(DEFAULT_GUI_FONT), FALSE);
1923 /* initialize info structure */
1924 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1926 GetLocalTime(&infoPtr->todaysDate);
1927 infoPtr->firstDayHighWord = FALSE;
1928 MONTHCAL_SetFirstDayOfWeek(infoPtr, (LPARAM)-1);
1929 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1930 infoPtr->currentYear = infoPtr->todaysDate.wYear;
1931 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
1932 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
1933 infoPtr->maxDate.wYear=2050;
1934 infoPtr->minDate.wYear=1950;
1935 infoPtr->maxSelCount = 7;
1936 infoPtr->monthRange = 3;
1937 infoPtr->monthdayState = Alloc
1938 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1939 infoPtr->titlebk = comctl32_color.clrActiveCaption;
1940 infoPtr->titletxt = comctl32_color.clrWindow;
1941 infoPtr->monthbk = comctl32_color.clrWindow;
1942 infoPtr->trailingtxt = comctl32_color.clrGrayText;
1943 infoPtr->bk = comctl32_color.clrWindow;
1944 infoPtr->txt = comctl32_color.clrWindowText;
1946 /* set the current day for highlighing */
1947 infoPtr->minSel.wDay = infoPtr->todaysDate.wDay;
1948 infoPtr->maxSel.wDay = infoPtr->todaysDate.wDay;
1950 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1951 /* of the control */
1952 MONTHCAL_UpdateSize(infoPtr);
1954 OpenThemeData (infoPtr->hwndSelf, themeClass);
1961 MONTHCAL_Destroy(MONTHCAL_INFO *infoPtr)
1963 /* free month calendar info data */
1964 Free(infoPtr->monthdayState);
1965 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
1967 CloseThemeData (GetWindowTheme (infoPtr->hwndSelf));
1974 static LRESULT WINAPI
1975 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1977 MONTHCAL_INFO *infoPtr;
1979 TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1981 infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1982 if (!infoPtr && (uMsg != WM_CREATE))
1983 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
1987 return MONTHCAL_GetCurSel(infoPtr, lParam);
1990 return MONTHCAL_SetCurSel(infoPtr, lParam);
1992 case MCM_GETMAXSELCOUNT:
1993 return MONTHCAL_GetMaxSelCount(infoPtr);
1995 case MCM_SETMAXSELCOUNT:
1996 return MONTHCAL_SetMaxSelCount(infoPtr, wParam);
1998 case MCM_GETSELRANGE:
1999 return MONTHCAL_GetSelRange(infoPtr, lParam);
2001 case MCM_SETSELRANGE:
2002 return MONTHCAL_SetSelRange(infoPtr, lParam);
2004 case MCM_GETMONTHRANGE:
2005 return MONTHCAL_GetMonthRange(infoPtr);
2007 case MCM_SETDAYSTATE:
2008 return MONTHCAL_SetDayState(infoPtr, wParam, lParam);
2010 case MCM_GETMINREQRECT:
2011 return MONTHCAL_GetMinReqRect(infoPtr, lParam);
2014 return MONTHCAL_GetColor(infoPtr, wParam);
2017 return MONTHCAL_SetColor(infoPtr, wParam, lParam);
2020 return MONTHCAL_GetToday(infoPtr, lParam);
2023 return MONTHCAL_SetToday(infoPtr, lParam);
2026 return MONTHCAL_HitTest(infoPtr, lParam);
2028 case MCM_GETFIRSTDAYOFWEEK:
2029 return MONTHCAL_GetFirstDayOfWeek(infoPtr);
2031 case MCM_SETFIRSTDAYOFWEEK:
2032 return MONTHCAL_SetFirstDayOfWeek(infoPtr, lParam);
2035 return MONTHCAL_GetRange(hwnd, lParam);
2038 return MONTHCAL_SetRange(infoPtr, wParam, lParam);
2040 case MCM_GETMONTHDELTA:
2041 return MONTHCAL_GetMonthDelta(infoPtr);
2043 case MCM_SETMONTHDELTA:
2044 return MONTHCAL_SetMonthDelta(infoPtr, wParam);
2046 case MCM_GETMAXTODAYWIDTH:
2047 return MONTHCAL_GetMaxTodayWidth(infoPtr);
2050 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2053 return MONTHCAL_KillFocus(infoPtr, (HWND)wParam);
2055 case WM_RBUTTONDOWN:
2056 return MONTHCAL_RButtonDown(infoPtr, lParam);
2058 case WM_LBUTTONDOWN:
2059 return MONTHCAL_LButtonDown(infoPtr, lParam);
2062 return MONTHCAL_MouseMove(infoPtr, lParam);
2065 return MONTHCAL_LButtonUp(infoPtr, lParam);
2067 case WM_PRINTCLIENT:
2069 return MONTHCAL_Paint(infoPtr, wParam);
2072 return MONTHCAL_SetFocus(infoPtr);
2075 return MONTHCAL_Size(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
2078 return MONTHCAL_Create(hwnd, lParam);
2081 return MONTHCAL_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
2084 return MONTHCAL_GetFont(infoPtr);
2087 return MONTHCAL_Timer(infoPtr, wParam);
2089 case WM_THEMECHANGED:
2090 return theme_changed (infoPtr);
2093 return MONTHCAL_Destroy(infoPtr);
2095 case WM_SYSCOLORCHANGE:
2096 COMCTL32_RefreshSysColors();
2099 case WM_STYLECHANGED:
2100 return MONTHCAL_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
2103 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
2104 ERR( "unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
2105 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
2111 MONTHCAL_Register(void)
2115 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
2116 wndClass.style = CS_GLOBALCLASS;
2117 wndClass.lpfnWndProc = MONTHCAL_WindowProc;
2118 wndClass.cbClsExtra = 0;
2119 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
2120 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
2121 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2122 wndClass.lpszClassName = MONTHCAL_CLASSW;
2124 RegisterClassW(&wndClass);
2129 MONTHCAL_Unregister(void)
2131 UnregisterClassW(MONTHCAL_CLASSW, NULL);