Added YUV routines needed for v4l driver, and in the future possibly
[wine/gsoc-2012-control.git] / dlls / comctl32 / monthcal.c
blob0b8fda7a60f1d34e20ddb7d2cbc6eb45aed3cc78
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * NOTE
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.
33 * TODO:
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?
42 * -- search for FIXME
45 #include <math.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
51 #include "windef.h"
52 #include "winbase.h"
53 #include "wingdi.h"
54 #include "winuser.h"
55 #include "winnls.h"
56 #include "commctrl.h"
57 #include "comctl32.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(monthcal);
63 #define MC_SEL_LBUTUP 1 /* Left button released */
64 #define MC_SEL_LBUTDOWN 2 /* Left button pressed in calendar */
65 #define MC_PREVPRESSED 4 /* Prev month button pressed */
66 #define MC_NEXTPRESSED 8 /* Next month button pressed */
67 #define MC_NEXTMONTHDELAY 350 /* when continuously pressing `next */
68 /* month', wait 500 ms before going */
69 /* to the next month */
70 #define MC_NEXTMONTHTIMER 1 /* Timer ID's */
71 #define MC_PREVMONTHTIMER 2
73 #define countof(arr) (sizeof(arr)/sizeof(arr[0]))
75 typedef struct
77 HWND hwndSelf;
78 COLORREF bk;
79 COLORREF txt;
80 COLORREF titlebk;
81 COLORREF titletxt;
82 COLORREF monthbk;
83 COLORREF trailingtxt;
84 HFONT hFont;
85 HFONT hBoldFont;
86 int textHeight;
87 int textWidth;
88 int height_increment;
89 int width_increment;
90 int firstDayplace; /* place of the first day of the current month */
91 int delta; /* scroll rate; # of months that the */
92 /* control moves when user clicks a scroll button */
93 int visible; /* # of months visible */
94 int firstDay; /* Start month calendar with firstDay's day */
95 int monthRange;
96 MONTHDAYSTATE *monthdayState;
97 SYSTEMTIME todaysDate;
98 DWORD currentMonth;
99 DWORD currentYear;
100 int status; /* See MC_SEL flags */
101 int curSelDay; /* current selected day */
102 int firstSelDay; /* first selected day */
103 int maxSelCount;
104 SYSTEMTIME minSel;
105 SYSTEMTIME maxSel;
106 DWORD rangeValid;
107 SYSTEMTIME minDate;
108 SYSTEMTIME maxDate;
110 RECT title; /* rect for the header above the calendar */
111 RECT titlebtnnext; /* the `next month' button in the header */
112 RECT titlebtnprev; /* the `prev month' button in the header */
113 RECT titlemonth; /* the `month name' txt in the header */
114 RECT titleyear; /* the `year number' txt in the header */
115 RECT wdays; /* week days at top */
116 RECT days; /* calendar area */
117 RECT weeknums; /* week numbers at left side */
118 RECT todayrect; /* `today: xx/xx/xx' text rect */
119 HWND hwndNotify; /* Window to receive the notifications */
120 HWND hWndYearEdit; /* Window Handle of edit box to handle years */
121 HWND hWndYearUpDown;/* Window Handle of updown box to handle years */
122 } MONTHCAL_INFO, *LPMONTHCAL_INFO;
125 /* Offsets of days in the week to the weekday of january 1 in a leap year */
126 static const int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
129 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0))
131 /* helper functions */
133 /* returns the number of days in any given month, checking for leap days */
134 /* january is 1, december is 12 */
135 int MONTHCAL_MonthLength(int month, int year)
137 const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
138 /*Wrap around, this eases handling*/
139 if(month == 0)
140 month = 12;
141 if(month == 13)
142 month = 1;
144 /* if we have a leap year add 1 day to February */
145 /* a leap year is a year either divisible by 400 */
146 /* or divisible by 4 and not by 100 */
147 if(month == 2) { /* February */
148 return mdays[month - 1] + ((year%400 == 0) ? 1 : ((year%100 != 0) &&
149 (year%4 == 0)) ? 1 : 0);
151 else {
152 return mdays[month - 1];
157 /* make sure that time is valid */
158 static int MONTHCAL_ValidateTime(SYSTEMTIME time)
160 if(time.wMonth > 12) return FALSE;
161 if(time.wDayOfWeek > 6) return FALSE;
162 if(time.wDay > MONTHCAL_MonthLength(time.wMonth, time.wYear))
163 return FALSE;
164 if(time.wHour > 23) return FALSE;
165 if(time.wMinute > 59) return FALSE;
166 if(time.wSecond > 59) return FALSE;
167 if(time.wMilliseconds > 999) return FALSE;
169 return TRUE;
173 void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to)
175 to->wYear = from->wYear;
176 to->wMonth = from->wMonth;
177 to->wDayOfWeek = from->wDayOfWeek;
178 to->wDay = from->wDay;
179 to->wHour = from->wHour;
180 to->wMinute = from->wMinute;
181 to->wSecond = from->wSecond;
182 to->wMilliseconds = from->wMilliseconds;
186 /* Note:Depending on DST, this may be offset by a day.
187 Need to find out if we're on a DST place & adjust the clock accordingly.
188 Above function assumes we have a valid data.
189 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
190 0 = Sunday.
193 /* returns the day in the week(0 == sunday, 6 == saturday) */
194 /* day(1 == 1st, 2 == 2nd... etc), year is the year value */
195 static int MONTHCAL_CalculateDayOfWeek(DWORD day, DWORD month, DWORD year)
197 year-=(month < 3);
199 return((year + year/4 - year/100 + year/400 +
200 DayOfWeekTable[month-1] + day ) % 7);
203 /* From a given point, calculate the row (weekpos), column(daypos)
204 and day in the calendar. day== 0 mean the last day of tha last month
206 static int MONTHCAL_CalcDayFromPos(MONTHCAL_INFO *infoPtr, int x, int y,
207 int *daypos,int *weekpos)
209 int retval, firstDay;
210 RECT rcClient;
212 GetClientRect(infoPtr->hwndSelf, &rcClient);
214 /* if the point is outside the x bounds of the window put
215 it at the boundry */
216 if (x > rcClient.right)
217 x = rcClient.right;
220 *daypos = (x - infoPtr->days.left ) / infoPtr->width_increment;
221 *weekpos = (y - infoPtr->days.top ) / infoPtr->height_increment;
223 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear)+6 - infoPtr->firstDay)%7;
224 retval = *daypos + (7 * *weekpos) - firstDay;
225 return retval;
228 /* day is the day of the month, 1 == 1st day of the month */
229 /* sets x and y to be the position of the day */
230 /* x == day, y == week where(0,0) == firstDay, 1st week */
231 static void MONTHCAL_CalcDayXY(MONTHCAL_INFO *infoPtr, int day, int month,
232 int *x, int *y)
234 int firstDay, prevMonth;
236 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear) +6 - infoPtr->firstDay)%7;
238 if(month==infoPtr->currentMonth) {
239 *x = (day + firstDay) % 7;
240 *y = (day + firstDay - *x) / 7;
241 return;
243 if(month < infoPtr->currentMonth) {
244 prevMonth = month - 1;
245 if(prevMonth==0)
246 prevMonth = 12;
248 *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay) % 7;
249 *y = 0;
250 return;
253 *y = MONTHCAL_MonthLength(month, infoPtr->currentYear - 1) / 7;
254 *x = (day + firstDay + MONTHCAL_MonthLength(month,
255 infoPtr->currentYear)) % 7;
259 /* x: column(day), y: row(week) */
260 static void MONTHCAL_CalcDayRect(MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
262 r->left = infoPtr->days.left + x * infoPtr->width_increment;
263 r->right = r->left + infoPtr->width_increment;
264 r->top = infoPtr->days.top + y * infoPtr->height_increment;
265 r->bottom = r->top + infoPtr->textHeight;
269 /* sets the RECT struct r to the rectangle around the day and month */
270 /* day is the day value of the month(1 == 1st), month is the month */
271 /* value(january == 1, december == 12) */
272 static inline void MONTHCAL_CalcPosFromDay(MONTHCAL_INFO *infoPtr,
273 int day, int month, RECT *r)
275 int x, y;
277 MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
278 MONTHCAL_CalcDayRect(infoPtr, r, x, y);
282 /* day is the day in the month(1 == 1st of the month) */
283 /* month is the month value(1 == january, 12 == december) */
284 static void MONTHCAL_CircleDay(MONTHCAL_INFO *infoPtr, HDC hdc, int day, int month)
286 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
287 HPEN hOldPen2 = SelectObject(hdc, hRedPen);
288 POINT points[13];
289 int x, y;
290 RECT day_rect;
293 MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
295 x = day_rect.left;
296 y = day_rect.top;
298 points[0].x = x;
299 points[0].y = y - 1;
300 points[1].x = x + 0.8 * infoPtr->width_increment;
301 points[1].y = y - 1;
302 points[2].x = x + 0.9 * infoPtr->width_increment;
303 points[2].y = y;
304 points[3].x = x + infoPtr->width_increment;
305 points[3].y = y + 0.5 * infoPtr->height_increment;
307 points[4].x = x + infoPtr->width_increment;
308 points[4].y = y + 0.9 * infoPtr->height_increment;
309 points[5].x = x + 0.6 * infoPtr->width_increment;
310 points[5].y = y + 0.9 * infoPtr->height_increment;
311 points[6].x = x + 0.5 * infoPtr->width_increment;
312 points[6].y = y + 0.9 * infoPtr->height_increment; /* bring the bottom up just
313 a hair to fit inside the day rectangle */
315 points[7].x = x + 0.2 * infoPtr->width_increment;
316 points[7].y = y + 0.8 * infoPtr->height_increment;
317 points[8].x = x + 0.1 * infoPtr->width_increment;
318 points[8].y = y + 0.8 * infoPtr->height_increment;
319 points[9].x = x;
320 points[9].y = y + 0.5 * infoPtr->height_increment;
322 points[10].x = x + 0.1 * infoPtr->width_increment;
323 points[10].y = y + 0.2 * infoPtr->height_increment;
324 points[11].x = x + 0.2 * infoPtr->width_increment;
325 points[11].y = y + 0.3 * infoPtr->height_increment;
326 points[12].x = x + 0.4 * infoPtr->width_increment;
327 points[12].y = y + 0.2 * infoPtr->height_increment;
329 PolyBezier(hdc, points, 13);
330 DeleteObject(hRedPen);
331 SelectObject(hdc, hOldPen2);
335 static void MONTHCAL_DrawDay(MONTHCAL_INFO *infoPtr, HDC hdc, int day, int month,
336 int x, int y, int bold)
338 static const WCHAR fmtW[] = { '%','d',0 };
339 WCHAR buf[10];
340 RECT r;
341 static int haveBoldFont, haveSelectedDay = FALSE;
342 HBRUSH hbr;
343 COLORREF oldCol = 0;
344 COLORREF oldBk = 0;
346 wsprintfW(buf, fmtW, day);
348 /* No need to check styles: when selection is not valid, it is set to zero.
349 * 1<day<31, so evertyhing's OK.
352 MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
354 if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
355 && (month==infoPtr->currentMonth)) {
356 HRGN hrgn;
357 RECT r2;
359 TRACE("%d %d %d\n",day, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
360 TRACE("%ld %ld %ld %ld\n", r.left, r.top, r.right, r.bottom);
361 oldCol = SetTextColor(hdc, infoPtr->monthbk);
362 oldBk = SetBkColor(hdc, infoPtr->trailingtxt);
363 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
364 hrgn = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
365 FillRgn(hdc, hrgn, hbr);
367 /* FIXME: this may need to be changed now b/c of the other
368 drawing changes 11/3/99 CMM */
369 r2.left = r.left - 0.25 * infoPtr->textWidth;
370 r2.top = r.top;
371 r2.right = r.left + 0.5 * infoPtr->textWidth;
372 r2.bottom = r.bottom;
373 if(haveSelectedDay) FillRect(hdc, &r2, hbr);
374 haveSelectedDay = TRUE;
375 } else {
376 haveSelectedDay = FALSE;
379 /* need to add some code for multiple selections */
381 if((bold) &&(!haveBoldFont)) {
382 SelectObject(hdc, infoPtr->hBoldFont);
383 haveBoldFont = TRUE;
385 if((!bold) &&(haveBoldFont)) {
386 SelectObject(hdc, infoPtr->hFont);
387 haveBoldFont = FALSE;
390 if(haveSelectedDay) {
391 SetTextColor(hdc, oldCol);
392 SetBkColor(hdc, oldBk);
395 SetBkMode(hdc,TRANSPARENT);
396 DrawTextW(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
398 /* draw a rectangle around the currently selected days text */
399 if((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth))
400 DrawFocusRect(hdc, &r);
404 static void MONTHCAL_Refresh(MONTHCAL_INFO *infoPtr, HDC hdc, PAINTSTRUCT* ps)
406 static const WCHAR todayW[] = { 'T','o','d','a','y',':',0 };
407 static const WCHAR fmt1W[] = { '%','s',' ','%','l','d',0 };
408 static const WCHAR fmt2W[] = { '%','s',' ','%','s',0 };
409 static const WCHAR fmt3W[] = { '%','d',0 };
410 RECT *title=&infoPtr->title;
411 RECT *prev=&infoPtr->titlebtnprev;
412 RECT *next=&infoPtr->titlebtnnext;
413 RECT *titlemonth=&infoPtr->titlemonth;
414 RECT *titleyear=&infoPtr->titleyear;
415 RECT dayrect;
416 RECT *days=&dayrect;
417 RECT rtoday;
418 int i, j, m, mask, day, firstDay, weeknum, weeknum1,prevMonth;
419 int textHeight = infoPtr->textHeight, textWidth = infoPtr->textWidth;
420 SIZE size;
421 HBRUSH hbr;
422 HFONT currentFont;
423 WCHAR buf[20];
424 WCHAR buf1[20];
425 WCHAR buf2[32];
426 COLORREF oldTextColor, oldBkColor;
427 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
428 RECT rcTemp;
429 RECT rcDay; /* used in MONTHCAL_CalcDayRect() */
430 SYSTEMTIME localtime;
431 int startofprescal;
433 oldTextColor = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
435 /* fill background */
436 hbr = CreateSolidBrush (infoPtr->bk);
437 FillRect(hdc, &ps->rcPaint, hbr);
438 DeleteObject(hbr);
440 /* draw header */
441 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
443 hbr = CreateSolidBrush(infoPtr->titlebk);
444 FillRect(hdc, title, hbr);
445 DeleteObject(hbr);
448 /* if the previous button is pressed draw it depressed */
449 if(IntersectRect(&rcTemp, &(ps->rcPaint), prev))
451 if((infoPtr->status & MC_PREVPRESSED))
452 DrawFrameControl(hdc, prev, DFC_SCROLL,
453 DFCS_SCROLLLEFT | DFCS_PUSHED |
454 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
455 else /* if the previous button is pressed draw it depressed */
456 DrawFrameControl(hdc, prev, DFC_SCROLL,
457 DFCS_SCROLLLEFT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
460 /* if next button is depressed draw it depressed */
461 if(IntersectRect(&rcTemp, &(ps->rcPaint), next))
463 if((infoPtr->status & MC_NEXTPRESSED))
464 DrawFrameControl(hdc, next, DFC_SCROLL,
465 DFCS_SCROLLRIGHT | DFCS_PUSHED |
466 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
467 else /* if the next button is pressed draw it depressed */
468 DrawFrameControl(hdc, next, DFC_SCROLL,
469 DFCS_SCROLLRIGHT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
472 oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
473 SetTextColor(hdc, infoPtr->titletxt);
474 currentFont = SelectObject(hdc, infoPtr->hBoldFont);
476 GetLocaleInfoW( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+infoPtr->currentMonth -1,
477 buf1,countof(buf1));
478 wsprintfW(buf, fmt1W, buf1, infoPtr->currentYear);
480 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
482 DrawTextW(hdc, buf, strlenW(buf), title,
483 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
486 /* titlemonth left/right contained rect for whole titletxt('June 1999')
487 * MCM_HitTestInfo wants month & year rects, so prepare these now.
488 *(no, we can't draw them separately; the whole text is centered)
490 GetTextExtentPoint32W(hdc, buf, strlenW(buf), &size);
491 titlemonth->left = title->right / 2 + title->left / 2 - size.cx / 2;
492 titleyear->right = title->right / 2 + title->left / 2 + size.cx / 2;
493 GetTextExtentPoint32W(hdc, buf1, strlenW(buf1), &size);
494 titlemonth->right = titlemonth->left + size.cx;
495 titleyear->left = titlemonth->right;
497 /* draw month area */
498 rcTemp.top=infoPtr->wdays.top;
499 rcTemp.left=infoPtr->wdays.left;
500 rcTemp.bottom=infoPtr->todayrect.bottom;
501 rcTemp.right =infoPtr->todayrect.right;
502 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcTemp))
504 hbr = CreateSolidBrush(infoPtr->monthbk);
505 FillRect(hdc, &rcTemp, hbr);
506 DeleteObject(hbr);
509 /* draw line under day abbreviatons */
511 MoveToEx(hdc, infoPtr->days.left + 3, title->bottom + textHeight + 1, NULL);
512 LineTo(hdc, infoPtr->days.right - 3, title->bottom + textHeight + 1);
514 prevMonth = infoPtr->currentMonth - 1;
515 if(prevMonth == 0) /* if currentMonth is january(1) prevMonth is */
516 prevMonth = 12; /* december(12) of the previous year */
518 infoPtr->wdays.left = infoPtr->days.left = infoPtr->weeknums.right;
519 /* draw day abbreviations */
521 SelectObject(hdc, infoPtr->hFont);
522 SetBkColor(hdc, infoPtr->monthbk);
523 SetTextColor(hdc, infoPtr->trailingtxt);
525 /* copy this rect so we can change the values without changing */
526 /* the original version */
527 days->left = infoPtr->wdays.left;
528 days->right = days->left + infoPtr->width_increment;
529 days->top = infoPtr->wdays.top;
530 days->bottom = infoPtr->wdays.bottom;
532 i = infoPtr->firstDay;
534 for(j=0; j<7; j++) {
535 GetLocaleInfoW( LOCALE_USER_DEFAULT,LOCALE_SABBREVDAYNAME1 + (i+j+6)%7, buf, countof(buf));
536 DrawTextW(hdc, buf, strlenW(buf), days, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
537 days->left+=infoPtr->width_increment;
538 days->right+=infoPtr->width_increment;
541 /* draw day numbers; first, the previous month */
543 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
545 day = MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) +
546 (infoPtr->firstDay + 7 - firstDay)%7 + 1;
547 if (day > MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear))
548 day -=7;
549 startofprescal = day;
550 mask = 1<<(day-1);
552 i = 0;
553 m = 0;
554 while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear)) {
555 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
556 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
558 MONTHCAL_DrawDay(infoPtr, hdc, day, prevMonth, i, 0,
559 infoPtr->monthdayState[m] & mask);
562 mask<<=1;
563 day++;
564 i++;
567 /* draw `current' month */
569 day = 1; /* start at the beginning of the current month */
571 infoPtr->firstDayplace = i;
572 SetTextColor(hdc, infoPtr->txt);
573 m++;
574 mask = 1;
576 /* draw the first week of the current month */
577 while(i<7) {
578 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
579 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
582 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth, i, 0,
583 infoPtr->monthdayState[m] & mask);
585 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
586 (day==infoPtr->todaysDate.wDay) &&
587 (infoPtr->currentYear == infoPtr->todaysDate.wYear)) {
588 if(!(dwStyle & MCS_NOTODAYCIRCLE))
589 MONTHCAL_CircleDay(infoPtr, hdc, day, infoPtr->currentMonth);
593 mask<<=1;
594 day++;
595 i++;
598 j = 1; /* move to the 2nd week of the current month */
599 i = 0; /* move back to sunday */
600 while(day <= MONTHCAL_MonthLength(infoPtr->currentMonth, infoPtr->currentYear)) {
601 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
602 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
604 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth, i, j,
605 infoPtr->monthdayState[m] & mask);
607 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
608 (day==infoPtr->todaysDate.wDay) &&
609 (infoPtr->currentYear == infoPtr->todaysDate.wYear))
610 if(!(dwStyle & MCS_NOTODAYCIRCLE))
611 MONTHCAL_CircleDay(infoPtr, hdc, day, infoPtr->currentMonth);
613 mask<<=1;
614 day++;
615 i++;
616 if(i>6) { /* past saturday, goto the next weeks sunday */
617 i = 0;
618 j++;
622 /* draw `next' month */
624 day = 1; /* start at the first day of the next month */
625 m++;
626 mask = 1;
628 SetTextColor(hdc, infoPtr->trailingtxt);
629 while((i<7) &&(j<6)) {
630 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
631 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
633 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth + 1, i, j,
634 infoPtr->monthdayState[m] & mask);
637 mask<<=1;
638 day++;
639 i++;
640 if(i==7) { /* past saturday, go to next week's sunday */
641 i = 0;
642 j++;
645 SetTextColor(hdc, infoPtr->txt);
648 /* draw `today' date if style allows it, and draw a circle before today's
649 * date if necessary */
651 if(!(dwStyle & MCS_NOTODAY)) {
652 int offset = 0;
653 if(!(dwStyle & MCS_NOTODAYCIRCLE)) {
654 /*day is the number of days from nextmonth we put on the calendar */
655 MONTHCAL_CircleDay(infoPtr, hdc,
656 day+MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear),
657 infoPtr->currentMonth);
658 offset+=textWidth;
660 if (!LoadStringW(COMCTL32_hModule,IDM_TODAY,buf1,countof(buf1)))
662 WARN("Can't load resource\n");
663 strcpyW(buf1, todayW);
665 MONTHCAL_CalcDayRect(infoPtr, &rtoday, 1, 6);
666 MONTHCAL_CopyTime(&infoPtr->todaysDate,&localtime);
667 GetDateFormatW(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&localtime,NULL,buf2,countof(buf2));
668 wsprintfW(buf, fmt2W, buf1, buf2);
669 SelectObject(hdc, infoPtr->hBoldFont);
671 DrawTextW(hdc, buf, -1, &rtoday, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE);
672 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rtoday))
674 DrawTextW(hdc, buf, -1, &rtoday, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
676 SelectObject(hdc, infoPtr->hFont);
679 /*eventually draw week numbers*/
680 if(dwStyle & MCS_WEEKNUMBERS) {
681 /* display weeknumbers*/
682 int mindays;
684 /* Rules what week to call the first week of a new year:
685 LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?):
686 The week containing Jan 1 is the first week of year
687 LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany):
688 First week of year must contain 4 days of the new year
689 LOCALE_IFIRSTWEEKOFYEAR == 1 (what contries?)
690 The first week of the year must contain only days of the new year
692 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, countof(buf));
693 weeknum = atoiW(buf);
694 switch (weeknum)
696 case 1: mindays = 6;
697 break;
698 case 2: mindays = 3;
699 break;
700 case 0:
701 default:
702 mindays = 0;
704 if (infoPtr->currentMonth < 2)
706 /* calculate all those exceptions for january */
707 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
708 if ((infoPtr->firstDay +7 - weeknum1)%7 > mindays)
709 weeknum =1;
710 else
712 weeknum = 0;
713 for(i=0; i<11; i++)
714 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear-1);
715 weeknum +=startofprescal+ 7;
716 weeknum /=7;
717 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear-1);
718 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
719 weeknum++;
722 else
724 weeknum = 0;
725 for(i=0; i<prevMonth-1; i++)
726 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear);
727 weeknum +=startofprescal+ 7;
728 weeknum /=7;
729 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
730 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
731 weeknum++;
733 days->left = infoPtr->weeknums.left;
734 days->right = infoPtr->weeknums.right;
735 days->top = infoPtr->weeknums.top;
736 days->bottom = days->top +infoPtr->height_increment;
737 for(i=0; i<6; i++) {
738 if((i==0)&&(weeknum>50))
740 wsprintfW(buf, fmt3W, weeknum);
741 weeknum=0;
743 else if((i==5)&&(weeknum>47))
745 wsprintfW(buf, fmt3W, 1);
747 else
748 wsprintfW(buf, fmt3W, weeknum + i);
749 DrawTextW(hdc, buf, -1, days, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
750 days->top+=infoPtr->height_increment;
751 days->bottom+=infoPtr->height_increment;
754 MoveToEx(hdc, infoPtr->weeknums.right, infoPtr->weeknums.top + 3 , NULL);
755 LineTo(hdc, infoPtr->weeknums.right, infoPtr->weeknums.bottom );
758 /* currentFont was font at entering Refresh */
760 SetBkColor(hdc, oldBkColor);
761 SelectObject(hdc, currentFont);
762 SetTextColor(hdc, oldTextColor);
766 static LRESULT
767 MONTHCAL_GetMinReqRect(MONTHCAL_INFO *infoPtr, LPARAM lParam)
769 LPRECT lpRect = (LPRECT) lParam;
771 TRACE("rect %p\n", lpRect);
773 /* validate parameters */
775 if((infoPtr==NULL) ||(lpRect == NULL) ) return FALSE;
777 lpRect->left = infoPtr->title.left;
778 lpRect->top = infoPtr->title.top;
779 lpRect->right = infoPtr->title.right;
780 lpRect->bottom = infoPtr->todayrect.bottom;
781 AdjustWindowRect(lpRect, GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE), FALSE);
783 TRACE("%s\n", wine_dbgstr_rect(lpRect));
785 return TRUE;
789 static LRESULT
790 MONTHCAL_GetColor(MONTHCAL_INFO *infoPtr, WPARAM wParam)
792 TRACE("\n");
794 switch((int)wParam) {
795 case MCSC_BACKGROUND:
796 return infoPtr->bk;
797 case MCSC_TEXT:
798 return infoPtr->txt;
799 case MCSC_TITLEBK:
800 return infoPtr->titlebk;
801 case MCSC_TITLETEXT:
802 return infoPtr->titletxt;
803 case MCSC_MONTHBK:
804 return infoPtr->monthbk;
805 case MCSC_TRAILINGTEXT:
806 return infoPtr->trailingtxt;
809 return -1;
813 static LRESULT
814 MONTHCAL_SetColor(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
816 int prev = -1;
818 TRACE("%d: color %08lx\n", wParam, lParam);
820 switch((int)wParam) {
821 case MCSC_BACKGROUND:
822 prev = infoPtr->bk;
823 infoPtr->bk = (COLORREF)lParam;
824 break;
825 case MCSC_TEXT:
826 prev = infoPtr->txt;
827 infoPtr->txt = (COLORREF)lParam;
828 break;
829 case MCSC_TITLEBK:
830 prev = infoPtr->titlebk;
831 infoPtr->titlebk = (COLORREF)lParam;
832 break;
833 case MCSC_TITLETEXT:
834 prev=infoPtr->titletxt;
835 infoPtr->titletxt = (COLORREF)lParam;
836 break;
837 case MCSC_MONTHBK:
838 prev = infoPtr->monthbk;
839 infoPtr->monthbk = (COLORREF)lParam;
840 break;
841 case MCSC_TRAILINGTEXT:
842 prev = infoPtr->trailingtxt;
843 infoPtr->trailingtxt = (COLORREF)lParam;
844 break;
847 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
848 return prev;
852 static LRESULT
853 MONTHCAL_GetMonthDelta(MONTHCAL_INFO *infoPtr)
855 TRACE("\n");
857 if(infoPtr->delta)
858 return infoPtr->delta;
859 else
860 return infoPtr->visible;
864 static LRESULT
865 MONTHCAL_SetMonthDelta(MONTHCAL_INFO *infoPtr, WPARAM wParam)
867 int prev = infoPtr->delta;
869 TRACE("delta %d\n", wParam);
871 infoPtr->delta = (int)wParam;
872 return prev;
876 static LRESULT
877 MONTHCAL_GetFirstDayOfWeek(MONTHCAL_INFO *infoPtr)
879 return infoPtr->firstDay;
883 /* sets the first day of the week that will appear in the control */
884 /* 0 == Sunday, 6 == Saturday */
885 /* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
886 /* FIXME: we need more error checking here */
887 static LRESULT
888 MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO *infoPtr, LPARAM lParam)
890 int prev = infoPtr->firstDay;
891 WCHAR buf[40];
893 TRACE("day %ld\n", lParam);
895 if((lParam >= 0) && (lParam < 7)) {
896 infoPtr->firstDay = (int)lParam;
898 else
900 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, buf, countof(buf));
901 TRACE("%s %d\n", debugstr_w(buf), strlenW(buf));
902 infoPtr->firstDay = (atoiW(buf)+1)%7;
904 return prev;
908 static LRESULT
909 MONTHCAL_GetMonthRange(MONTHCAL_INFO *infoPtr)
911 TRACE("\n");
913 return infoPtr->monthRange;
917 static LRESULT
918 MONTHCAL_GetMaxTodayWidth(MONTHCAL_INFO *infoPtr)
920 return(infoPtr->todayrect.right - infoPtr->todayrect.left);
924 /* FIXME: are validated times taken from current date/time or simply
925 * copied?
926 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
927 * adjusting range with MCM_SETRANGE
930 static LRESULT
931 MONTHCAL_SetRange(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
933 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *)lParam;
934 int prev;
936 TRACE("%x %lx\n", wParam, lParam);
938 if(wParam & GDTR_MAX) {
939 if(MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
940 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxDate);
941 infoPtr->rangeValid|=GDTR_MAX;
942 } else {
943 GetSystemTime(&infoPtr->todaysDate);
944 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
947 if(wParam & GDTR_MIN) {
948 if(MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
949 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minDate);
950 infoPtr->rangeValid|=GDTR_MIN;
951 } else {
952 GetSystemTime(&infoPtr->todaysDate);
953 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
957 prev = infoPtr->monthRange;
958 infoPtr->monthRange = infoPtr->maxDate.wMonth - infoPtr->minDate.wMonth;
960 if(infoPtr->monthRange!=prev) {
961 infoPtr->monthdayState = ReAlloc(infoPtr->monthdayState,
962 infoPtr->monthRange * sizeof(MONTHDAYSTATE));
965 return 1;
969 static LRESULT
970 MONTHCAL_GetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
972 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
973 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *)lParam;
975 /* validate parameters */
977 if((infoPtr==NULL) || (lprgSysTimeArray==NULL)) return FALSE;
979 MONTHCAL_CopyTime(&infoPtr->maxDate, &lprgSysTimeArray[1]);
980 MONTHCAL_CopyTime(&infoPtr->minDate, &lprgSysTimeArray[0]);
982 return infoPtr->rangeValid;
986 static LRESULT
987 MONTHCAL_SetDayState(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
990 int i, iMonths = (int)wParam;
991 MONTHDAYSTATE *dayStates = (LPMONTHDAYSTATE)lParam;
993 TRACE("%x %lx\n", wParam, lParam);
994 if(iMonths!=infoPtr->monthRange) return 0;
996 for(i=0; i<iMonths; i++)
997 infoPtr->monthdayState[i] = dayStates[i];
998 return 1;
1001 static LRESULT
1002 MONTHCAL_GetCurSel(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1004 SYSTEMTIME *lpSel = (SYSTEMTIME *) lParam;
1006 TRACE("%lx\n", lParam);
1007 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1008 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1010 MONTHCAL_CopyTime(&infoPtr->minSel, lpSel);
1011 TRACE("%d/%d/%d\n", lpSel->wYear, lpSel->wMonth, lpSel->wDay);
1012 return TRUE;
1015 /* FIXME: if the specified date is not visible, make it visible */
1016 /* FIXME: redraw? */
1017 static LRESULT
1018 MONTHCAL_SetCurSel(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1020 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
1022 TRACE("%lx\n", lParam);
1023 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1024 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1026 infoPtr->currentMonth=lpSel->wMonth;
1027 infoPtr->currentYear=lpSel->wYear;
1029 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
1030 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
1032 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1034 return TRUE;
1038 static LRESULT
1039 MONTHCAL_GetMaxSelCount(MONTHCAL_INFO *infoPtr)
1041 return infoPtr->maxSelCount;
1045 static LRESULT
1046 MONTHCAL_SetMaxSelCount(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1048 TRACE("%x\n", wParam);
1050 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT) {
1051 infoPtr->maxSelCount = wParam;
1054 return TRUE;
1058 static LRESULT
1059 MONTHCAL_GetSelRange(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1061 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1063 TRACE("%lx\n", lParam);
1065 /* validate parameters */
1067 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1069 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT)
1071 MONTHCAL_CopyTime(&infoPtr->maxSel, &lprgSysTimeArray[1]);
1072 MONTHCAL_CopyTime(&infoPtr->minSel, &lprgSysTimeArray[0]);
1073 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1074 return TRUE;
1077 return FALSE;
1081 static LRESULT
1082 MONTHCAL_SetSelRange(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1084 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1086 TRACE("%lx\n", lParam);
1088 /* validate parameters */
1090 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1092 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT)
1094 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxSel);
1095 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minSel);
1096 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1097 return TRUE;
1100 return FALSE;
1104 static LRESULT
1105 MONTHCAL_GetToday(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1107 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1109 TRACE("%lx\n", lParam);
1111 /* validate parameters */
1113 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
1114 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
1115 return TRUE;
1119 static LRESULT
1120 MONTHCAL_SetToday(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1122 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1124 TRACE("%lx\n", lParam);
1126 /* validate parameters */
1128 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
1129 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
1130 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1131 return TRUE;
1135 static LRESULT
1136 MONTHCAL_HitTest(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1138 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
1139 UINT x,y;
1140 DWORD retval;
1141 int day,wday,wnum;
1144 x = lpht->pt.x;
1145 y = lpht->pt.y;
1146 retval = MCHT_NOWHERE;
1148 ZeroMemory(&lpht->st, sizeof(lpht->st));
1150 /* Comment in for debugging...
1151 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,
1152 infoPtr->wdays.left, infoPtr->wdays.right,
1153 infoPtr->wdays.top, infoPtr->wdays.bottom,
1154 infoPtr->days.left, infoPtr->days.right,
1155 infoPtr->days.top, infoPtr->days.bottom,
1156 infoPtr->todayrect.left, infoPtr->todayrect.right,
1157 infoPtr->todayrect.top, infoPtr->todayrect.bottom,
1158 infoPtr->weeknums.left, infoPtr->weeknums.right,
1159 infoPtr->weeknums.top, infoPtr->weeknums.bottom);
1162 /* are we in the header? */
1164 if(PtInRect(&infoPtr->title, lpht->pt)) {
1165 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1166 retval = MCHT_TITLEBTNPREV;
1167 goto done;
1169 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1170 retval = MCHT_TITLEBTNNEXT;
1171 goto done;
1173 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1174 retval = MCHT_TITLEMONTH;
1175 goto done;
1177 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1178 retval = MCHT_TITLEYEAR;
1179 goto done;
1182 retval = MCHT_TITLE;
1183 goto done;
1186 day = MONTHCAL_CalcDayFromPos(infoPtr,x,y,&wday,&wnum);
1187 if(PtInRect(&infoPtr->wdays, lpht->pt)) {
1188 retval = MCHT_CALENDARDAY;
1189 lpht->st.wYear = infoPtr->currentYear;
1190 lpht->st.wMonth = (day < 1)? infoPtr->currentMonth -1 : infoPtr->currentMonth;
1191 lpht->st.wDay = (day < 1)?
1192 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day : day;
1193 goto done;
1195 if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
1196 retval = MCHT_CALENDARWEEKNUM;
1197 lpht->st.wYear = infoPtr->currentYear;
1198 lpht->st.wMonth = (day < 1) ? infoPtr->currentMonth -1 :
1199 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1200 infoPtr->currentMonth +1 :infoPtr->currentMonth;
1201 lpht->st.wDay = (day < 1 ) ?
1202 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day :
1203 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1204 day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) : day;
1205 goto done;
1207 if(PtInRect(&infoPtr->days, lpht->pt))
1209 lpht->st.wYear = infoPtr->currentYear;
1210 if ( day < 1)
1212 retval = MCHT_CALENDARDATEPREV;
1213 lpht->st.wMonth = infoPtr->currentMonth - 1;
1214 if (lpht->st.wMonth <1)
1216 lpht->st.wMonth = 12;
1217 lpht->st.wYear--;
1219 lpht->st.wDay = MONTHCAL_MonthLength(lpht->st.wMonth,lpht->st.wYear) -day;
1221 else if (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear))
1223 retval = MCHT_CALENDARDATENEXT;
1224 lpht->st.wMonth = infoPtr->currentMonth + 1;
1225 if (lpht->st.wMonth <12)
1227 lpht->st.wMonth = 1;
1228 lpht->st.wYear++;
1230 lpht->st.wDay = day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) ;
1232 else {
1233 retval = MCHT_CALENDARDATE;
1234 lpht->st.wMonth = infoPtr->currentMonth;
1235 lpht->st.wDay = day;
1236 lpht->st.wDayOfWeek = MONTHCAL_CalculateDayOfWeek(day,lpht->st.wMonth,lpht->st.wYear);
1238 goto done;
1240 if(PtInRect(&infoPtr->todayrect, lpht->pt)) {
1241 retval = MCHT_TODAYLINK;
1242 goto done;
1246 /* Hit nothing special? What's left must be background :-) */
1248 retval = MCHT_CALENDARBK;
1249 done:
1250 lpht->uHit = retval;
1251 return retval;
1255 static void MONTHCAL_GoToNextMonth(MONTHCAL_INFO *infoPtr)
1257 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
1259 TRACE("MONTHCAL_GoToNextMonth\n");
1261 infoPtr->currentMonth++;
1262 if(infoPtr->currentMonth > 12) {
1263 infoPtr->currentYear++;
1264 infoPtr->currentMonth = 1;
1267 if(dwStyle & MCS_DAYSTATE) {
1268 NMDAYSTATE nmds;
1269 int i;
1271 nmds.nmhdr.hwndFrom = infoPtr->hwndSelf;
1272 nmds.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1273 nmds.nmhdr.code = MCN_GETDAYSTATE;
1274 nmds.cDayState = infoPtr->monthRange;
1275 nmds.prgDayState = Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1277 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
1278 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1279 for(i=0; i<infoPtr->monthRange; i++)
1280 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1285 static void MONTHCAL_GoToPrevMonth(MONTHCAL_INFO *infoPtr)
1287 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
1289 TRACE("\n");
1291 infoPtr->currentMonth--;
1292 if(infoPtr->currentMonth < 1) {
1293 infoPtr->currentYear--;
1294 infoPtr->currentMonth = 12;
1297 if(dwStyle & MCS_DAYSTATE) {
1298 NMDAYSTATE nmds;
1299 int i;
1301 nmds.nmhdr.hwndFrom = infoPtr->hwndSelf;
1302 nmds.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1303 nmds.nmhdr.code = MCN_GETDAYSTATE;
1304 nmds.cDayState = infoPtr->monthRange;
1305 nmds.prgDayState = Alloc
1306 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1308 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
1309 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1310 for(i=0; i<infoPtr->monthRange; i++)
1311 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1315 static LRESULT
1316 MONTHCAL_RButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1318 static const WCHAR todayW[] = { 'G','o',' ','t','o',' ','T','o','d','a','y',':',0 };
1319 HMENU hMenu;
1320 POINT menupoint;
1321 WCHAR buf[32];
1323 hMenu = CreatePopupMenu();
1324 if (!LoadStringW(COMCTL32_hModule,IDM_GOTODAY,buf,countof(buf)))
1326 WARN("Can't load resource\n");
1327 strcpyW(buf, todayW);
1329 AppendMenuW(hMenu, MF_STRING|MF_ENABLED,1, buf);
1330 menupoint.x=(INT)LOWORD(lParam);
1331 menupoint.y=(INT)HIWORD(lParam);
1332 ClientToScreen(infoPtr->hwndSelf, &menupoint);
1333 if( TrackPopupMenu(hMenu,TPM_RIGHTBUTTON| TPM_NONOTIFY|TPM_RETURNCMD,
1334 menupoint.x, menupoint.y, 0, infoPtr->hwndSelf, NULL))
1336 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1337 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1338 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1340 return 0;
1343 static LRESULT
1344 MONTHCAL_LButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1346 static const WCHAR EditW[] = { 'E','D','I','T',0 };
1347 MCHITTESTINFO ht;
1348 DWORD hit;
1349 HMENU hMenu;
1350 RECT rcDay; /* used in determining area to invalidate */
1351 WCHAR buf[32];
1352 int i;
1353 POINT menupoint;
1355 TRACE("%lx\n", lParam);
1357 if (infoPtr->hWndYearUpDown)
1359 infoPtr->currentYear=SendMessageW( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)0);
1360 if(!DestroyWindow(infoPtr->hWndYearUpDown))
1362 FIXME("Can't destroy Updown Control\n");
1364 else
1365 infoPtr->hWndYearUpDown=0;
1366 if(!DestroyWindow(infoPtr->hWndYearEdit))
1368 FIXME("Can't destroy Updown Control\n");
1370 else
1371 infoPtr->hWndYearEdit=0;
1372 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1375 ht.pt.x = (INT)LOWORD(lParam);
1376 ht.pt.y = (INT)HIWORD(lParam);
1377 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1379 /* FIXME: these flags should be checked by */
1380 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1381 /* multi-bit */
1382 if(hit ==MCHT_TITLEBTNNEXT) {
1383 MONTHCAL_GoToNextMonth(infoPtr);
1384 infoPtr->status = MC_NEXTPRESSED;
1385 SetTimer(infoPtr->hwndSelf, MC_NEXTMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1386 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1387 return TRUE;
1389 if(hit == MCHT_TITLEBTNPREV){
1390 MONTHCAL_GoToPrevMonth(infoPtr);
1391 infoPtr->status = MC_PREVPRESSED;
1392 SetTimer(infoPtr->hwndSelf, MC_PREVMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1393 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1394 return TRUE;
1397 if(hit == MCHT_TITLEMONTH) {
1398 hMenu = CreatePopupMenu();
1400 for (i=0; i<12;i++)
1402 GetLocaleInfoW(LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+i, buf,countof(buf));
1403 AppendMenuW(hMenu, MF_STRING|MF_ENABLED,i+1, buf);
1405 menupoint.x=infoPtr->titlemonth.right;
1406 menupoint.y=infoPtr->titlemonth.bottom;
1407 ClientToScreen(infoPtr->hwndSelf, &menupoint);
1408 i= TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD,
1409 menupoint.x, menupoint.y, 0, infoPtr->hwndSelf, NULL);
1410 if ((i>0) && (i<13))
1412 infoPtr->currentMonth=i;
1413 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1416 if(hit == MCHT_TITLEYEAR) {
1417 infoPtr->hWndYearEdit=CreateWindowExW(0,
1418 EditW,
1420 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT,
1421 infoPtr->titleyear.left+3,infoPtr->titlebtnnext.top,
1422 infoPtr->titleyear.right-infoPtr->titleyear.left+4,
1423 infoPtr->textHeight,
1424 infoPtr->hwndSelf,
1425 NULL,
1426 NULL,
1427 NULL);
1428 SendMessageW( infoPtr->hWndYearEdit, WM_SETFONT, (WPARAM) infoPtr->hBoldFont, (LPARAM)TRUE);
1429 infoPtr->hWndYearUpDown=CreateWindowExW(0,
1430 UPDOWN_CLASSW,
1432 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT|UDS_NOTHOUSANDS|UDS_ARROWKEYS,
1433 infoPtr->titleyear.right+7,infoPtr->titlebtnnext.top,
1435 infoPtr->textHeight,
1436 infoPtr->hwndSelf,
1437 NULL,
1438 NULL,
1439 NULL);
1440 SendMessageW( infoPtr->hWndYearUpDown, UDM_SETRANGE, (WPARAM) 0, MAKELONG (9999, 1753));
1441 SendMessageW( infoPtr->hWndYearUpDown, UDM_SETBUDDY, (WPARAM) infoPtr->hWndYearEdit, (LPARAM)0 );
1442 SendMessageW( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)infoPtr->currentYear );
1443 return TRUE;
1446 if(hit == MCHT_TODAYLINK) {
1447 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1448 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1449 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1450 return TRUE;
1452 if(hit == MCHT_CALENDARDATE) {
1453 SYSTEMTIME selArray[2];
1454 NMSELCHANGE nmsc;
1456 MONTHCAL_CopyTime(&ht.st, &selArray[0]);
1457 MONTHCAL_CopyTime(&ht.st, &selArray[1]);
1458 MONTHCAL_SetSelRange(infoPtr, (LPARAM)&selArray);
1459 MONTHCAL_SetCurSel(infoPtr, (LPARAM)&selArray);
1460 TRACE("MCHT_CALENDARDATE\n");
1461 nmsc.nmhdr.hwndFrom = infoPtr->hwndSelf;
1462 nmsc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1463 nmsc.nmhdr.code = MCN_SELCHANGE;
1464 MONTHCAL_CopyTime(&infoPtr->minSel,&nmsc.stSelStart);
1465 MONTHCAL_CopyTime(&infoPtr->maxSel,&nmsc.stSelEnd);
1467 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
1468 (WPARAM)nmsc.nmhdr.idFrom,(LPARAM)&nmsc);
1471 /* redraw both old and new days if the selected day changed */
1472 if(infoPtr->curSelDay != ht.st.wDay) {
1473 MONTHCAL_CalcPosFromDay(infoPtr, ht.st.wDay, ht.st.wMonth, &rcDay);
1474 InvalidateRect(infoPtr->hwndSelf, &rcDay, TRUE);
1476 MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->curSelDay, infoPtr->currentMonth, &rcDay);
1477 InvalidateRect(infoPtr->hwndSelf, &rcDay, TRUE);
1480 infoPtr->firstSelDay = ht.st.wDay;
1481 infoPtr->curSelDay = ht.st.wDay;
1482 infoPtr->status = MC_SEL_LBUTDOWN;
1483 return TRUE;
1486 return 0;
1490 static LRESULT
1491 MONTHCAL_LButtonUp(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1493 NMSELCHANGE nmsc;
1494 NMHDR nmhdr;
1495 BOOL redraw = FALSE;
1496 MCHITTESTINFO ht;
1497 DWORD hit;
1499 TRACE("\n");
1501 if(infoPtr->status & MC_NEXTPRESSED) {
1502 KillTimer(infoPtr->hwndSelf, MC_NEXTMONTHTIMER);
1503 infoPtr->status &= ~MC_NEXTPRESSED;
1504 redraw = TRUE;
1506 if(infoPtr->status & MC_PREVPRESSED) {
1507 KillTimer(infoPtr->hwndSelf, MC_PREVMONTHTIMER);
1508 infoPtr->status &= ~MC_PREVPRESSED;
1509 redraw = TRUE;
1512 ht.pt.x = (INT)LOWORD(lParam);
1513 ht.pt.y = (INT)HIWORD(lParam);
1514 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1516 infoPtr->status = MC_SEL_LBUTUP;
1518 if(hit ==MCHT_CALENDARDATENEXT) {
1519 MONTHCAL_GoToNextMonth(infoPtr);
1520 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1521 return TRUE;
1523 if(hit == MCHT_CALENDARDATEPREV){
1524 MONTHCAL_GoToPrevMonth(infoPtr);
1525 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1526 return TRUE;
1528 nmhdr.hwndFrom = infoPtr->hwndSelf;
1529 nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1530 nmhdr.code = NM_RELEASEDCAPTURE;
1531 TRACE("Sent notification from %p to %p\n", infoPtr->hwndSelf, infoPtr->hwndNotify);
1533 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1534 /* redraw if necessary */
1535 if(redraw)
1536 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1537 /* only send MCN_SELECT if currently displayed month's day was selected */
1538 if(hit == MCHT_CALENDARDATE) {
1539 nmsc.nmhdr.hwndFrom = infoPtr->hwndSelf;
1540 nmsc.nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1541 nmsc.nmhdr.code = MCN_SELECT;
1542 MONTHCAL_CopyTime(&infoPtr->minSel, &nmsc.stSelStart);
1543 MONTHCAL_CopyTime(&infoPtr->maxSel, &nmsc.stSelEnd);
1545 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, (WPARAM)nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1548 return 0;
1552 static LRESULT
1553 MONTHCAL_Timer(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1555 BOOL redraw = FALSE;
1557 TRACE("%d\n", wParam);
1559 switch(wParam) {
1560 case MC_NEXTMONTHTIMER:
1561 redraw = TRUE;
1562 MONTHCAL_GoToNextMonth(infoPtr);
1563 break;
1564 case MC_PREVMONTHTIMER:
1565 redraw = TRUE;
1566 MONTHCAL_GoToPrevMonth(infoPtr);
1567 break;
1568 default:
1569 ERR("got unknown timer\n");
1570 break;
1573 /* redraw only if necessary */
1574 if(redraw)
1575 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1577 return 0;
1581 static LRESULT
1582 MONTHCAL_MouseMove(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
1584 MCHITTESTINFO ht;
1585 int oldselday, selday, hit;
1586 RECT r;
1588 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1590 ht.pt.x = LOWORD(lParam);
1591 ht.pt.y = HIWORD(lParam);
1593 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1595 /* not on the calendar date numbers? bail out */
1596 TRACE("hit:%x\n",hit);
1597 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1599 selday = ht.st.wDay;
1600 oldselday = infoPtr->curSelDay;
1601 infoPtr->curSelDay = selday;
1602 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1604 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT) {
1605 SYSTEMTIME selArray[2];
1606 int i;
1608 MONTHCAL_GetSelRange(infoPtr, (LPARAM)&selArray);
1609 i = 0;
1610 if(infoPtr->firstSelDay==selArray[0].wDay) i=1;
1611 TRACE("oldRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1612 if(infoPtr->firstSelDay==selArray[1].wDay) {
1613 /* 1st time we get here: selArray[0]=selArray[1]) */
1614 /* if we're still at the first selected date, return */
1615 if(infoPtr->firstSelDay==selday) goto done;
1616 if(selday<infoPtr->firstSelDay) i = 0;
1619 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1620 if(selday>infoPtr->firstSelDay)
1621 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1622 else
1623 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1626 if(selArray[i].wDay!=selday) {
1627 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1629 selArray[i].wDay = selday;
1631 if(selArray[0].wDay>selArray[1].wDay) {
1632 DWORD tempday;
1633 tempday = selArray[1].wDay;
1634 selArray[1].wDay = selArray[0].wDay;
1635 selArray[0].wDay = tempday;
1638 MONTHCAL_SetSelRange(infoPtr, (LPARAM)&selArray);
1642 done:
1644 /* only redraw if the currently selected day changed */
1645 /* FIXME: this should specify a rectangle containing only the days that changed */
1646 /* using InvalidateRect */
1647 if(oldselday != infoPtr->curSelDay)
1648 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1650 return 0;
1654 static LRESULT
1655 MONTHCAL_Paint(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1657 HDC hdc;
1658 PAINTSTRUCT ps;
1660 if (wParam)
1662 GetClientRect(infoPtr->hwndSelf, &ps.rcPaint);
1663 hdc = (HDC)wParam;
1665 else
1666 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
1668 MONTHCAL_Refresh(infoPtr, hdc, &ps);
1669 if (!wParam) EndPaint(infoPtr->hwndSelf, &ps);
1670 return 0;
1674 static LRESULT
1675 MONTHCAL_KillFocus(MONTHCAL_INFO *infoPtr)
1677 TRACE("\n");
1679 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1681 return 0;
1685 static LRESULT
1686 MONTHCAL_SetFocus(MONTHCAL_INFO *infoPtr)
1688 TRACE("\n");
1690 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1692 return 0;
1695 /* sets the size information */
1696 static void MONTHCAL_UpdateSize(MONTHCAL_INFO *infoPtr)
1698 static const WCHAR SunW[] = { 'S','u','n',0 };
1699 static const WCHAR O0W[] = { '0','0',0 };
1700 HDC hdc = GetDC(infoPtr->hwndSelf);
1701 RECT *title=&infoPtr->title;
1702 RECT *prev=&infoPtr->titlebtnprev;
1703 RECT *next=&infoPtr->titlebtnnext;
1704 RECT *titlemonth=&infoPtr->titlemonth;
1705 RECT *titleyear=&infoPtr->titleyear;
1706 RECT *wdays=&infoPtr->wdays;
1707 RECT *weeknumrect=&infoPtr->weeknums;
1708 RECT *days=&infoPtr->days;
1709 RECT *todayrect=&infoPtr->todayrect;
1710 SIZE size;
1711 TEXTMETRICW tm;
1712 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
1713 HFONT currentFont;
1714 int xdiv, left_offset;
1715 RECT rcClient;
1717 GetClientRect(infoPtr->hwndSelf, &rcClient);
1719 currentFont = SelectObject(hdc, infoPtr->hFont);
1721 /* get the height and width of each day's text */
1722 GetTextMetricsW(hdc, &tm);
1723 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading + tm.tmInternalLeading;
1724 GetTextExtentPoint32W(hdc, SunW, 3, &size);
1725 infoPtr->textWidth = size.cx + 2;
1727 /* recalculate the height and width increments and offsets */
1728 GetTextExtentPoint32W(hdc, O0W, 2, &size);
1730 xdiv = (dwStyle & MCS_WEEKNUMBERS) ? 8 : 7;
1732 infoPtr->width_increment = size.cx * 2 + 4;
1733 infoPtr->height_increment = infoPtr->textHeight;
1734 left_offset = (rcClient.right - rcClient.left) - (infoPtr->width_increment * xdiv);
1736 /* calculate title area */
1737 title->top = rcClient.top;
1738 title->bottom = title->top + 3 * infoPtr->height_increment / 2;
1739 title->left = left_offset;
1740 title->right = rcClient.right;
1742 /* set the dimensions of the next and previous buttons and center */
1743 /* the month text vertically */
1744 prev->top = next->top = title->top + 4;
1745 prev->bottom = next->bottom = title->bottom - 4;
1746 prev->left = title->left + 4;
1747 prev->right = prev->left + (title->bottom - title->top) ;
1748 next->right = title->right - 4;
1749 next->left = next->right - (title->bottom - title->top);
1751 /* titlemonth->left and right change based upon the current month */
1752 /* and are recalculated in refresh as the current month may change */
1753 /* without the control being resized */
1754 titlemonth->top = titleyear->top = title->top + (infoPtr->height_increment)/2;
1755 titlemonth->bottom = titleyear->bottom = title->bottom - (infoPtr->height_increment)/2;
1757 /* setup the dimensions of the rectangle we draw the names of the */
1758 /* days of the week in */
1759 weeknumrect->left = left_offset;
1760 if(dwStyle & MCS_WEEKNUMBERS)
1761 weeknumrect->right=prev->right;
1762 else
1763 weeknumrect->right=weeknumrect->left;
1764 wdays->left = days->left = weeknumrect->right;
1765 wdays->right = days->right = wdays->left + 7 * infoPtr->width_increment;
1766 wdays->top = title->bottom ;
1767 wdays->bottom = wdays->top + infoPtr->height_increment;
1769 days->top = weeknumrect->top = wdays->bottom ;
1770 days->bottom = weeknumrect->bottom = days->top + 6 * infoPtr->height_increment;
1772 todayrect->left = rcClient.left;
1773 todayrect->right = rcClient.right;
1774 todayrect->top = days->bottom;
1775 todayrect->bottom = days->bottom + infoPtr->height_increment;
1777 TRACE("dx=%d dy=%d client[%s] title[%s] wdays[%s] days[%s] today[%s]\n",
1778 infoPtr->width_increment,infoPtr->height_increment,
1779 wine_dbgstr_rect(&rcClient),
1780 wine_dbgstr_rect(title),
1781 wine_dbgstr_rect(wdays),
1782 wine_dbgstr_rect(days),
1783 wine_dbgstr_rect(todayrect));
1785 /* restore the originally selected font */
1786 SelectObject(hdc, currentFont);
1788 ReleaseDC(infoPtr->hwndSelf, hdc);
1791 static LRESULT MONTHCAL_Size(MONTHCAL_INFO *infoPtr, int Width, int Height)
1793 TRACE("(width=%d, height=%d)\n", Width, Height);
1795 MONTHCAL_UpdateSize(infoPtr);
1797 /* invalidate client area and erase background */
1798 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1800 return 0;
1803 static LRESULT MONTHCAL_GetFont(MONTHCAL_INFO *infoPtr)
1805 return (LRESULT)infoPtr->hFont;
1808 static LRESULT MONTHCAL_SetFont(MONTHCAL_INFO *infoPtr, HFONT hFont, BOOL redraw)
1810 HFONT hOldFont;
1811 LOGFONTW lf;
1813 if (!hFont) return 0;
1815 hOldFont = infoPtr->hFont;
1816 infoPtr->hFont = hFont;
1818 GetObjectW(infoPtr->hFont, sizeof(lf), &lf);
1819 lf.lfWeight = FW_BOLD;
1820 infoPtr->hBoldFont = CreateFontIndirectW(&lf);
1822 if (redraw)
1823 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1825 return (LRESULT)hOldFont;
1828 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1829 static LRESULT
1830 MONTHCAL_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
1832 MONTHCAL_INFO *infoPtr;
1834 /* allocate memory for info structure */
1835 infoPtr =(MONTHCAL_INFO*)Alloc(sizeof(MONTHCAL_INFO));
1836 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
1838 if(infoPtr == NULL) {
1839 ERR( "could not allocate info memory!\n");
1840 return 0;
1843 infoPtr->hwndSelf = hwnd;
1844 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1846 MONTHCAL_SetFont(infoPtr, GetStockObject(DEFAULT_GUI_FONT), FALSE);
1848 /* initialize info structure */
1849 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1851 GetLocalTime(&infoPtr->todaysDate);
1852 MONTHCAL_SetFirstDayOfWeek(infoPtr, (LPARAM)-1);
1853 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1854 infoPtr->currentYear = infoPtr->todaysDate.wYear;
1855 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
1856 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
1857 infoPtr->maxDate.wYear=2050;
1858 infoPtr->minDate.wYear=1950;
1859 infoPtr->maxSelCount = 7;
1860 infoPtr->monthRange = 3;
1861 infoPtr->monthdayState = Alloc
1862 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1863 infoPtr->titlebk = GetSysColor(COLOR_ACTIVECAPTION);
1864 infoPtr->titletxt = GetSysColor(COLOR_WINDOW);
1865 infoPtr->monthbk = GetSysColor(COLOR_WINDOW);
1866 infoPtr->trailingtxt = GetSysColor(COLOR_GRAYTEXT);
1867 infoPtr->bk = GetSysColor(COLOR_WINDOW);
1868 infoPtr->txt = GetSysColor(COLOR_WINDOWTEXT);
1870 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1871 /* of the control */
1872 MONTHCAL_UpdateSize(infoPtr);
1874 return 0;
1878 static LRESULT
1879 MONTHCAL_Destroy(MONTHCAL_INFO *infoPtr)
1881 /* free month calendar info data */
1882 if(infoPtr->monthdayState)
1883 Free(infoPtr->monthdayState);
1884 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
1885 Free(infoPtr);
1886 return 0;
1890 static LRESULT WINAPI
1891 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1893 MONTHCAL_INFO *infoPtr;
1895 TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1897 infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1898 if (!infoPtr && (uMsg != WM_CREATE))
1899 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
1900 switch(uMsg)
1902 case MCM_GETCURSEL:
1903 return MONTHCAL_GetCurSel(infoPtr, lParam);
1905 case MCM_SETCURSEL:
1906 return MONTHCAL_SetCurSel(infoPtr, lParam);
1908 case MCM_GETMAXSELCOUNT:
1909 return MONTHCAL_GetMaxSelCount(infoPtr);
1911 case MCM_SETMAXSELCOUNT:
1912 return MONTHCAL_SetMaxSelCount(infoPtr, wParam);
1914 case MCM_GETSELRANGE:
1915 return MONTHCAL_GetSelRange(infoPtr, lParam);
1917 case MCM_SETSELRANGE:
1918 return MONTHCAL_SetSelRange(infoPtr, lParam);
1920 case MCM_GETMONTHRANGE:
1921 return MONTHCAL_GetMonthRange(infoPtr);
1923 case MCM_SETDAYSTATE:
1924 return MONTHCAL_SetDayState(infoPtr, wParam, lParam);
1926 case MCM_GETMINREQRECT:
1927 return MONTHCAL_GetMinReqRect(infoPtr, lParam);
1929 case MCM_GETCOLOR:
1930 return MONTHCAL_GetColor(infoPtr, wParam);
1932 case MCM_SETCOLOR:
1933 return MONTHCAL_SetColor(infoPtr, wParam, lParam);
1935 case MCM_GETTODAY:
1936 return MONTHCAL_GetToday(infoPtr, lParam);
1938 case MCM_SETTODAY:
1939 return MONTHCAL_SetToday(infoPtr, lParam);
1941 case MCM_HITTEST:
1942 return MONTHCAL_HitTest(infoPtr, lParam);
1944 case MCM_GETFIRSTDAYOFWEEK:
1945 return MONTHCAL_GetFirstDayOfWeek(infoPtr);
1947 case MCM_SETFIRSTDAYOFWEEK:
1948 return MONTHCAL_SetFirstDayOfWeek(infoPtr, lParam);
1950 case MCM_GETRANGE:
1951 return MONTHCAL_GetRange(hwnd, wParam, lParam);
1953 case MCM_SETRANGE:
1954 return MONTHCAL_SetRange(infoPtr, wParam, lParam);
1956 case MCM_GETMONTHDELTA:
1957 return MONTHCAL_GetMonthDelta(infoPtr);
1959 case MCM_SETMONTHDELTA:
1960 return MONTHCAL_SetMonthDelta(infoPtr, wParam);
1962 case MCM_GETMAXTODAYWIDTH:
1963 return MONTHCAL_GetMaxTodayWidth(infoPtr);
1965 case WM_GETDLGCODE:
1966 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1968 case WM_KILLFOCUS:
1969 return MONTHCAL_KillFocus(infoPtr);
1971 case WM_RBUTTONDOWN:
1972 return MONTHCAL_RButtonDown(infoPtr, lParam);
1974 case WM_LBUTTONDOWN:
1975 return MONTHCAL_LButtonDown(infoPtr, lParam);
1977 case WM_MOUSEMOVE:
1978 return MONTHCAL_MouseMove(infoPtr, wParam, lParam);
1980 case WM_LBUTTONUP:
1981 return MONTHCAL_LButtonUp(infoPtr, lParam);
1983 case WM_PAINT:
1984 return MONTHCAL_Paint(infoPtr, wParam);
1986 case WM_SETFOCUS:
1987 return MONTHCAL_SetFocus(infoPtr);
1989 case WM_SIZE:
1990 return MONTHCAL_Size(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
1992 case WM_CREATE:
1993 return MONTHCAL_Create(hwnd, wParam, lParam);
1995 case WM_SETFONT:
1996 return MONTHCAL_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
1998 case WM_GETFONT:
1999 return MONTHCAL_GetFont(infoPtr);
2001 case WM_TIMER:
2002 return MONTHCAL_Timer(infoPtr, wParam);
2004 case WM_DESTROY:
2005 return MONTHCAL_Destroy(infoPtr);
2007 default:
2008 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
2009 ERR( "unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
2010 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
2015 void
2016 MONTHCAL_Register(void)
2018 WNDCLASSW wndClass;
2020 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
2021 wndClass.style = CS_GLOBALCLASS;
2022 wndClass.lpfnWndProc = MONTHCAL_WindowProc;
2023 wndClass.cbClsExtra = 0;
2024 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
2025 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
2026 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2027 wndClass.lpszClassName = MONTHCAL_CLASSW;
2029 RegisterClassW(&wndClass);
2033 void
2034 MONTHCAL_Unregister(void)
2036 UnregisterClassW(MONTHCAL_CLASSW, NULL);