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
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]))
92 int firstDayplace
; /* place of the first day of the current month */
93 int delta
; /* scroll rate; # of months that the */
94 /* control moves when user clicks a scroll button */
95 int visible
; /* # of months visible */
96 int firstDay
; /* Start month calendar with firstDay's day */
98 MONTHDAYSTATE
*monthdayState
;
99 SYSTEMTIME todaysDate
;
102 int status
; /* See MC_SEL flags */
103 int curSelDay
; /* current selected day */
104 int firstSelDay
; /* first selected day */
112 RECT title
; /* rect for the header above the calendar */
113 RECT titlebtnnext
; /* the `next month' button in the header */
114 RECT titlebtnprev
; /* the `prev month' button in the header */
115 RECT titlemonth
; /* the `month name' txt in the header */
116 RECT titleyear
; /* the `year number' txt in the header */
117 RECT wdays
; /* week days at top */
118 RECT days
; /* calendar area */
119 RECT weeknums
; /* week numbers at left side */
120 RECT todayrect
; /* `today: xx/xx/xx' text rect */
121 HWND hwndNotify
; /* Window to receive the notifications */
122 HWND hWndYearEdit
; /* Window Handle of edit box to handle years */
123 HWND hWndYearUpDown
;/* Window Handle of updown box to handle years */
124 } MONTHCAL_INFO
, *LPMONTHCAL_INFO
;
127 /* Offsets of days in the week to the weekday of january 1 in a leap year */
128 static const int DayOfWeekTable
[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
130 static const WCHAR themeClass
[] = { 'S','c','r','o','l','l','b','a','r',0 };
132 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0))
134 /* helper functions */
136 /* returns the number of days in any given month, checking for leap days */
137 /* january is 1, december is 12 */
138 int MONTHCAL_MonthLength(int month
, int year
)
140 const int mdays
[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
141 /*Wrap around, this eases handling*/
147 /* if we have a leap year add 1 day to February */
148 /* a leap year is a year either divisible by 400 */
149 /* or divisible by 4 and not by 100 */
150 if(month
== 2) { /* February */
151 return mdays
[month
- 1] + ((year
%400 == 0) ? 1 : ((year
%100 != 0) &&
152 (year
%4 == 0)) ? 1 : 0);
155 return mdays
[month
- 1];
160 /* make sure that time is valid */
161 static int MONTHCAL_ValidateTime(SYSTEMTIME time
)
163 if(time
.wMonth
> 12) return FALSE
;
164 if(time
.wDayOfWeek
> 6) return FALSE
;
165 if(time
.wDay
> MONTHCAL_MonthLength(time
.wMonth
, time
.wYear
))
167 if(time
.wHour
> 23) return FALSE
;
168 if(time
.wMinute
> 59) return FALSE
;
169 if(time
.wSecond
> 59) return FALSE
;
170 if(time
.wMilliseconds
> 999) return FALSE
;
176 /* Note:Depending on DST, this may be offset by a day.
177 Need to find out if we're on a DST place & adjust the clock accordingly.
178 Above function assumes we have a valid data.
179 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
183 /* returns the day in the week(0 == sunday, 6 == saturday) */
184 /* day(1 == 1st, 2 == 2nd... etc), year is the year value */
185 static int MONTHCAL_CalculateDayOfWeek(DWORD day
, DWORD month
, DWORD year
)
189 return((year
+ year
/4 - year
/100 + year
/400 +
190 DayOfWeekTable
[month
-1] + day
) % 7);
193 /* From a given point, calculate the row (weekpos), column(daypos)
194 and day in the calendar. day== 0 mean the last day of tha last month
196 static int MONTHCAL_CalcDayFromPos(MONTHCAL_INFO
*infoPtr
, int x
, int y
,
197 int *daypos
,int *weekpos
)
199 int retval
, firstDay
;
202 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
204 /* if the point is outside the x bounds of the window put
205 it at the boundary */
206 if (x
> rcClient
.right
)
210 *daypos
= (x
- infoPtr
->days
.left
) / infoPtr
->width_increment
;
211 *weekpos
= (y
- infoPtr
->days
.top
) / infoPtr
->height_increment
;
213 firstDay
= (MONTHCAL_CalculateDayOfWeek(1, infoPtr
->currentMonth
, infoPtr
->currentYear
)+6 - infoPtr
->firstDay
)%7;
214 retval
= *daypos
+ (7 * *weekpos
) - firstDay
;
218 /* day is the day of the month, 1 == 1st day of the month */
219 /* sets x and y to be the position of the day */
220 /* x == day, y == week where(0,0) == firstDay, 1st week */
221 static void MONTHCAL_CalcDayXY(MONTHCAL_INFO
*infoPtr
, int day
, int month
,
224 int firstDay
, prevMonth
;
226 firstDay
= (MONTHCAL_CalculateDayOfWeek(1, infoPtr
->currentMonth
, infoPtr
->currentYear
) +6 - infoPtr
->firstDay
)%7;
228 if(month
==infoPtr
->currentMonth
) {
229 *x
= (day
+ firstDay
) % 7;
230 *y
= (day
+ firstDay
- *x
) / 7;
233 if(month
< infoPtr
->currentMonth
) {
234 prevMonth
= month
- 1;
238 *x
= (MONTHCAL_MonthLength(prevMonth
, infoPtr
->currentYear
) - firstDay
) % 7;
243 *y
= MONTHCAL_MonthLength(month
, infoPtr
->currentYear
- 1) / 7;
244 *x
= (day
+ firstDay
+ MONTHCAL_MonthLength(month
,
245 infoPtr
->currentYear
)) % 7;
249 /* x: column(day), y: row(week) */
250 static void MONTHCAL_CalcDayRect(MONTHCAL_INFO
*infoPtr
, RECT
*r
, int x
, int y
)
252 r
->left
= infoPtr
->days
.left
+ x
* infoPtr
->width_increment
;
253 r
->right
= r
->left
+ infoPtr
->width_increment
;
254 r
->top
= infoPtr
->days
.top
+ y
* infoPtr
->height_increment
;
255 r
->bottom
= r
->top
+ infoPtr
->textHeight
;
259 /* sets the RECT struct r to the rectangle around the day and month */
260 /* day is the day value of the month(1 == 1st), month is the month */
261 /* value(january == 1, december == 12) */
262 static inline void MONTHCAL_CalcPosFromDay(MONTHCAL_INFO
*infoPtr
,
263 int day
, int month
, RECT
*r
)
267 MONTHCAL_CalcDayXY(infoPtr
, day
, month
, &x
, &y
);
268 MONTHCAL_CalcDayRect(infoPtr
, r
, x
, y
);
272 /* day is the day in the month(1 == 1st of the month) */
273 /* month is the month value(1 == january, 12 == december) */
274 static void MONTHCAL_CircleDay(MONTHCAL_INFO
*infoPtr
, HDC hdc
, int day
, int month
)
276 HPEN hRedPen
= CreatePen(PS_SOLID
, 2, RGB(255, 0, 0));
277 HPEN hOldPen2
= SelectObject(hdc
, hRedPen
);
283 MONTHCAL_CalcPosFromDay(infoPtr
, day
, month
, &day_rect
);
290 points
[1].x
= x
+ 0.8 * infoPtr
->width_increment
;
292 points
[2].x
= x
+ 0.9 * infoPtr
->width_increment
;
294 points
[3].x
= x
+ infoPtr
->width_increment
;
295 points
[3].y
= y
+ 0.5 * infoPtr
->height_increment
;
297 points
[4].x
= x
+ infoPtr
->width_increment
;
298 points
[4].y
= y
+ 0.9 * infoPtr
->height_increment
;
299 points
[5].x
= x
+ 0.6 * infoPtr
->width_increment
;
300 points
[5].y
= y
+ 0.9 * infoPtr
->height_increment
;
301 points
[6].x
= x
+ 0.5 * infoPtr
->width_increment
;
302 points
[6].y
= y
+ 0.9 * infoPtr
->height_increment
; /* bring the bottom up just
303 a hair to fit inside the day rectangle */
305 points
[7].x
= x
+ 0.2 * infoPtr
->width_increment
;
306 points
[7].y
= y
+ 0.8 * infoPtr
->height_increment
;
307 points
[8].x
= x
+ 0.1 * infoPtr
->width_increment
;
308 points
[8].y
= y
+ 0.8 * infoPtr
->height_increment
;
310 points
[9].y
= y
+ 0.5 * infoPtr
->height_increment
;
312 points
[10].x
= x
+ 0.1 * infoPtr
->width_increment
;
313 points
[10].y
= y
+ 0.2 * infoPtr
->height_increment
;
314 points
[11].x
= x
+ 0.2 * infoPtr
->width_increment
;
315 points
[11].y
= y
+ 0.3 * infoPtr
->height_increment
;
316 points
[12].x
= x
+ 0.4 * infoPtr
->width_increment
;
317 points
[12].y
= y
+ 0.2 * infoPtr
->height_increment
;
319 PolyBezier(hdc
, points
, 13);
320 DeleteObject(hRedPen
);
321 SelectObject(hdc
, hOldPen2
);
325 static void MONTHCAL_DrawDay(MONTHCAL_INFO
*infoPtr
, HDC hdc
, int day
, int month
,
326 int x
, int y
, int bold
)
328 static const WCHAR fmtW
[] = { '%','d',0 };
331 static int haveBoldFont
, haveSelectedDay
= FALSE
;
336 wsprintfW(buf
, fmtW
, day
);
338 /* No need to check styles: when selection is not valid, it is set to zero.
339 * 1<day<31, so evertyhing's OK.
342 MONTHCAL_CalcDayRect(infoPtr
, &r
, x
, y
);
344 if((day
>=infoPtr
->minSel
.wDay
) && (day
<=infoPtr
->maxSel
.wDay
)
345 && (month
==infoPtr
->currentMonth
)) {
349 TRACE("%d %d %d\n",day
, infoPtr
->minSel
.wDay
, infoPtr
->maxSel
.wDay
);
350 TRACE("%ld %ld %ld %ld\n", r
.left
, r
.top
, r
.right
, r
.bottom
);
351 oldCol
= SetTextColor(hdc
, infoPtr
->monthbk
);
352 oldBk
= SetBkColor(hdc
, infoPtr
->trailingtxt
);
353 hbr
= GetSysColorBrush(COLOR_GRAYTEXT
);
354 hrgn
= CreateEllipticRgn(r
.left
, r
.top
, r
.right
, r
.bottom
);
355 FillRgn(hdc
, hrgn
, hbr
);
357 /* FIXME: this may need to be changed now b/c of the other
358 drawing changes 11/3/99 CMM */
359 r2
.left
= r
.left
- 0.25 * infoPtr
->textWidth
;
361 r2
.right
= r
.left
+ 0.5 * infoPtr
->textWidth
;
362 r2
.bottom
= r
.bottom
;
363 if(haveSelectedDay
) FillRect(hdc
, &r2
, hbr
);
364 haveSelectedDay
= TRUE
;
366 haveSelectedDay
= FALSE
;
369 /* need to add some code for multiple selections */
371 if((bold
) &&(!haveBoldFont
)) {
372 SelectObject(hdc
, infoPtr
->hBoldFont
);
375 if((!bold
) &&(haveBoldFont
)) {
376 SelectObject(hdc
, infoPtr
->hFont
);
377 haveBoldFont
= FALSE
;
380 if(haveSelectedDay
) {
381 SetTextColor(hdc
, oldCol
);
382 SetBkColor(hdc
, oldBk
);
385 SetBkMode(hdc
,TRANSPARENT
);
386 DrawTextW(hdc
, buf
, -1, &r
, DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
388 /* draw a rectangle around the currently selected days text */
389 if((day
==infoPtr
->curSelDay
) && (month
==infoPtr
->currentMonth
))
390 DrawFocusRect(hdc
, &r
);
394 static void paint_button (MONTHCAL_INFO
*infoPtr
, HDC hdc
, BOOL btnNext
,
395 BOOL pressed
, RECT
* r
)
397 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
401 static const int states
[] = {
403 ABS_LEFTNORMAL
, ABS_LEFTPRESSED
, ABS_LEFTDISABLED
,
405 ABS_RIGHTNORMAL
, ABS_RIGHTPRESSED
, ABS_RIGHTDISABLED
407 int stateNum
= btnNext
? 3 : 0;
412 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
413 if (dwStyle
& WS_DISABLED
) stateNum
+= 2;
415 DrawThemeBackground (theme
, hdc
, SBP_ARROWBTN
, states
[stateNum
], r
, NULL
);
419 int style
= btnNext
? DFCS_SCROLLRIGHT
: DFCS_SCROLLLEFT
;
421 style
|= DFCS_PUSHED
;
424 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
425 if (dwStyle
& WS_DISABLED
) style
|= DFCS_INACTIVE
;
428 DrawFrameControl(hdc
, r
, DFC_SCROLL
, style
);
433 static void MONTHCAL_Refresh(MONTHCAL_INFO
*infoPtr
, HDC hdc
, PAINTSTRUCT
* ps
)
435 static const WCHAR todayW
[] = { 'T','o','d','a','y',':',0 };
436 static const WCHAR fmt1W
[] = { '%','s',' ','%','l','d',0 };
437 static const WCHAR fmt2W
[] = { '%','s',' ','%','s',0 };
438 static const WCHAR fmt3W
[] = { '%','d',0 };
439 RECT
*title
=&infoPtr
->title
;
440 RECT
*prev
=&infoPtr
->titlebtnprev
;
441 RECT
*next
=&infoPtr
->titlebtnnext
;
442 RECT
*titlemonth
=&infoPtr
->titlemonth
;
443 RECT
*titleyear
=&infoPtr
->titleyear
;
447 int i
, j
, m
, mask
, day
, firstDay
, weeknum
, weeknum1
,prevMonth
;
448 int textHeight
= infoPtr
->textHeight
, textWidth
= infoPtr
->textWidth
;
455 COLORREF oldTextColor
, oldBkColor
;
456 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
458 RECT rcDay
; /* used in MONTHCAL_CalcDayRect() */
459 SYSTEMTIME localtime
;
462 oldTextColor
= SetTextColor(hdc
, GetSysColor(COLOR_WINDOWTEXT
));
464 /* fill background */
465 hbr
= CreateSolidBrush (infoPtr
->bk
);
466 FillRect(hdc
, &ps
->rcPaint
, hbr
);
470 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), title
))
472 hbr
= CreateSolidBrush(infoPtr
->titlebk
);
473 FillRect(hdc
, title
, hbr
);
477 /* if the previous button is pressed draw it depressed */
478 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), prev
))
479 paint_button (infoPtr
, hdc
, FALSE
, infoPtr
->status
& MC_PREVPRESSED
, prev
);
481 /* if next button is depressed draw it depressed */
482 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), next
))
483 paint_button (infoPtr
, hdc
, TRUE
, infoPtr
->status
& MC_NEXTPRESSED
, next
);
485 oldBkColor
= SetBkColor(hdc
, infoPtr
->titlebk
);
486 SetTextColor(hdc
, infoPtr
->titletxt
);
487 currentFont
= SelectObject(hdc
, infoPtr
->hBoldFont
);
489 GetLocaleInfoW( LOCALE_USER_DEFAULT
,LOCALE_SMONTHNAME1
+infoPtr
->currentMonth
-1,
491 wsprintfW(buf
, fmt1W
, buf1
, infoPtr
->currentYear
);
493 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), title
))
495 DrawTextW(hdc
, buf
, strlenW(buf
), title
,
496 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
499 /* titlemonth left/right contained rect for whole titletxt('June 1999')
500 * MCM_HitTestInfo wants month & year rects, so prepare these now.
501 *(no, we can't draw them separately; the whole text is centered)
503 GetTextExtentPoint32W(hdc
, buf
, strlenW(buf
), &size
);
504 titlemonth
->left
= title
->right
/ 2 + title
->left
/ 2 - size
.cx
/ 2;
505 titleyear
->right
= title
->right
/ 2 + title
->left
/ 2 + size
.cx
/ 2;
506 GetTextExtentPoint32W(hdc
, buf1
, strlenW(buf1
), &size
);
507 titlemonth
->right
= titlemonth
->left
+ size
.cx
;
508 titleyear
->left
= titlemonth
->right
;
510 /* draw month area */
511 rcTemp
.top
=infoPtr
->wdays
.top
;
512 rcTemp
.left
=infoPtr
->wdays
.left
;
513 rcTemp
.bottom
=infoPtr
->todayrect
.bottom
;
514 rcTemp
.right
=infoPtr
->todayrect
.right
;
515 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcTemp
))
517 hbr
= CreateSolidBrush(infoPtr
->monthbk
);
518 FillRect(hdc
, &rcTemp
, hbr
);
522 /* draw line under day abbreviatons */
524 MoveToEx(hdc
, infoPtr
->days
.left
+ 3, title
->bottom
+ textHeight
+ 1, NULL
);
525 LineTo(hdc
, infoPtr
->days
.right
- 3, title
->bottom
+ textHeight
+ 1);
527 prevMonth
= infoPtr
->currentMonth
- 1;
528 if(prevMonth
== 0) /* if currentMonth is january(1) prevMonth is */
529 prevMonth
= 12; /* december(12) of the previous year */
531 infoPtr
->wdays
.left
= infoPtr
->days
.left
= infoPtr
->weeknums
.right
;
532 /* draw day abbreviations */
534 SelectObject(hdc
, infoPtr
->hFont
);
535 SetBkColor(hdc
, infoPtr
->monthbk
);
536 SetTextColor(hdc
, infoPtr
->trailingtxt
);
538 /* copy this rect so we can change the values without changing */
539 /* the original version */
540 days
->left
= infoPtr
->wdays
.left
;
541 days
->right
= days
->left
+ infoPtr
->width_increment
;
542 days
->top
= infoPtr
->wdays
.top
;
543 days
->bottom
= infoPtr
->wdays
.bottom
;
545 i
= infoPtr
->firstDay
;
548 GetLocaleInfoW( LOCALE_USER_DEFAULT
,LOCALE_SABBREVDAYNAME1
+ (i
+j
+6)%7, buf
, countof(buf
));
549 DrawTextW(hdc
, buf
, strlenW(buf
), days
, DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
550 days
->left
+=infoPtr
->width_increment
;
551 days
->right
+=infoPtr
->width_increment
;
554 /* draw day numbers; first, the previous month */
556 firstDay
= MONTHCAL_CalculateDayOfWeek(1, infoPtr
->currentMonth
, infoPtr
->currentYear
);
558 day
= MONTHCAL_MonthLength(prevMonth
, infoPtr
->currentYear
) +
559 (infoPtr
->firstDay
+ 7 - firstDay
)%7 + 1;
560 if (day
> MONTHCAL_MonthLength(prevMonth
, infoPtr
->currentYear
))
562 startofprescal
= day
;
567 while(day
<= MONTHCAL_MonthLength(prevMonth
, infoPtr
->currentYear
)) {
568 MONTHCAL_CalcDayRect(infoPtr
, &rcDay
, i
, 0);
569 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcDay
))
571 MONTHCAL_DrawDay(infoPtr
, hdc
, day
, prevMonth
, i
, 0,
572 infoPtr
->monthdayState
[m
] & mask
);
580 /* draw `current' month */
582 day
= 1; /* start at the beginning of the current month */
584 infoPtr
->firstDayplace
= i
;
585 SetTextColor(hdc
, infoPtr
->txt
);
589 /* draw the first week of the current month */
591 MONTHCAL_CalcDayRect(infoPtr
, &rcDay
, i
, 0);
592 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcDay
))
595 MONTHCAL_DrawDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
, i
, 0,
596 infoPtr
->monthdayState
[m
] & mask
);
598 if((infoPtr
->currentMonth
==infoPtr
->todaysDate
.wMonth
) &&
599 (day
==infoPtr
->todaysDate
.wDay
) &&
600 (infoPtr
->currentYear
== infoPtr
->todaysDate
.wYear
)) {
601 if(!(dwStyle
& MCS_NOTODAYCIRCLE
))
602 MONTHCAL_CircleDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
);
611 j
= 1; /* move to the 2nd week of the current month */
612 i
= 0; /* move back to sunday */
613 while(day
<= MONTHCAL_MonthLength(infoPtr
->currentMonth
, infoPtr
->currentYear
)) {
614 MONTHCAL_CalcDayRect(infoPtr
, &rcDay
, i
, j
);
615 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcDay
))
617 MONTHCAL_DrawDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
, i
, j
,
618 infoPtr
->monthdayState
[m
] & mask
);
620 if((infoPtr
->currentMonth
==infoPtr
->todaysDate
.wMonth
) &&
621 (day
==infoPtr
->todaysDate
.wDay
) &&
622 (infoPtr
->currentYear
== infoPtr
->todaysDate
.wYear
))
623 if(!(dwStyle
& MCS_NOTODAYCIRCLE
))
624 MONTHCAL_CircleDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
);
629 if(i
>6) { /* past saturday, goto the next weeks sunday */
635 /* draw `next' month */
637 day
= 1; /* start at the first day of the next month */
641 SetTextColor(hdc
, infoPtr
->trailingtxt
);
642 while((i
<7) &&(j
<6)) {
643 MONTHCAL_CalcDayRect(infoPtr
, &rcDay
, i
, j
);
644 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcDay
))
646 MONTHCAL_DrawDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
+ 1, i
, j
,
647 infoPtr
->monthdayState
[m
] & mask
);
653 if(i
==7) { /* past saturday, go to next week's sunday */
658 SetTextColor(hdc
, infoPtr
->txt
);
661 /* draw `today' date if style allows it, and draw a circle before today's
662 * date if necessary */
664 if(!(dwStyle
& MCS_NOTODAY
)) {
666 if(!(dwStyle
& MCS_NOTODAYCIRCLE
)) {
667 /*day is the number of days from nextmonth we put on the calendar */
668 MONTHCAL_CircleDay(infoPtr
, hdc
,
669 day
+MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
),
670 infoPtr
->currentMonth
);
673 if (!LoadStringW(COMCTL32_hModule
,IDM_TODAY
,buf1
,countof(buf1
)))
675 WARN("Can't load resource\n");
676 strcpyW(buf1
, todayW
);
678 MONTHCAL_CalcDayRect(infoPtr
, &rtoday
, 1, 6);
679 MONTHCAL_CopyTime(&infoPtr
->todaysDate
,&localtime
);
680 GetDateFormatW(LOCALE_USER_DEFAULT
,DATE_SHORTDATE
,&localtime
,NULL
,buf2
,countof(buf2
));
681 wsprintfW(buf
, fmt2W
, buf1
, buf2
);
682 SelectObject(hdc
, infoPtr
->hBoldFont
);
684 DrawTextW(hdc
, buf
, -1, &rtoday
, DT_CALCRECT
| DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
);
685 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rtoday
))
687 DrawTextW(hdc
, buf
, -1, &rtoday
, DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
);
689 SelectObject(hdc
, infoPtr
->hFont
);
692 /*eventually draw week numbers*/
693 if(dwStyle
& MCS_WEEKNUMBERS
) {
694 /* display weeknumbers*/
697 /* Rules what week to call the first week of a new year:
698 LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?):
699 The week containing Jan 1 is the first week of year
700 LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany):
701 First week of year must contain 4 days of the new year
702 LOCALE_IFIRSTWEEKOFYEAR == 1 (what contries?)
703 The first week of the year must contain only days of the new year
705 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_IFIRSTWEEKOFYEAR
, buf
, countof(buf
));
706 weeknum
= atoiW(buf
);
717 if (infoPtr
->currentMonth
< 2)
719 /* calculate all those exceptions for january */
720 weeknum1
=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr
->currentYear
);
721 if ((infoPtr
->firstDay
+7 - weeknum1
)%7 > mindays
)
727 weeknum
+=MONTHCAL_MonthLength(i
+1, infoPtr
->currentYear
-1);
728 weeknum
+=startofprescal
+ 7;
730 weeknum1
=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr
->currentYear
-1);
731 if ((infoPtr
->firstDay
+ 7 - weeknum1
)%7 > mindays
)
738 for(i
=0; i
<prevMonth
-1; i
++)
739 weeknum
+=MONTHCAL_MonthLength(i
+1, infoPtr
->currentYear
);
740 weeknum
+=startofprescal
+ 7;
742 weeknum1
=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr
->currentYear
);
743 if ((infoPtr
->firstDay
+ 7 - weeknum1
)%7 > mindays
)
746 days
->left
= infoPtr
->weeknums
.left
;
747 days
->right
= infoPtr
->weeknums
.right
;
748 days
->top
= infoPtr
->weeknums
.top
;
749 days
->bottom
= days
->top
+infoPtr
->height_increment
;
751 if((i
==0)&&(weeknum
>50))
753 wsprintfW(buf
, fmt3W
, weeknum
);
756 else if((i
==5)&&(weeknum
>47))
758 wsprintfW(buf
, fmt3W
, 1);
761 wsprintfW(buf
, fmt3W
, weeknum
+ i
);
762 DrawTextW(hdc
, buf
, -1, days
, DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
763 days
->top
+=infoPtr
->height_increment
;
764 days
->bottom
+=infoPtr
->height_increment
;
767 MoveToEx(hdc
, infoPtr
->weeknums
.right
, infoPtr
->weeknums
.top
+ 3 , NULL
);
768 LineTo(hdc
, infoPtr
->weeknums
.right
, infoPtr
->weeknums
.bottom
);
771 /* currentFont was font at entering Refresh */
773 SetBkColor(hdc
, oldBkColor
);
774 SelectObject(hdc
, currentFont
);
775 SetTextColor(hdc
, oldTextColor
);
780 MONTHCAL_GetMinReqRect(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
782 LPRECT lpRect
= (LPRECT
) lParam
;
784 TRACE("rect %p\n", lpRect
);
786 /* validate parameters */
788 if((infoPtr
==NULL
) ||(lpRect
== NULL
) ) return FALSE
;
790 lpRect
->left
= infoPtr
->title
.left
;
791 lpRect
->top
= infoPtr
->title
.top
;
792 lpRect
->right
= infoPtr
->title
.right
;
793 lpRect
->bottom
= infoPtr
->todayrect
.bottom
;
794 AdjustWindowRect(lpRect
, GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
), FALSE
);
796 TRACE("%s\n", wine_dbgstr_rect(lpRect
));
803 MONTHCAL_GetColor(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
)
807 switch((int)wParam
) {
808 case MCSC_BACKGROUND
:
813 return infoPtr
->titlebk
;
815 return infoPtr
->titletxt
;
817 return infoPtr
->monthbk
;
818 case MCSC_TRAILINGTEXT
:
819 return infoPtr
->trailingtxt
;
827 MONTHCAL_SetColor(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
831 TRACE("%d: color %08lx\n", wParam
, lParam
);
833 switch((int)wParam
) {
834 case MCSC_BACKGROUND
:
836 infoPtr
->bk
= (COLORREF
)lParam
;
840 infoPtr
->txt
= (COLORREF
)lParam
;
843 prev
= infoPtr
->titlebk
;
844 infoPtr
->titlebk
= (COLORREF
)lParam
;
847 prev
=infoPtr
->titletxt
;
848 infoPtr
->titletxt
= (COLORREF
)lParam
;
851 prev
= infoPtr
->monthbk
;
852 infoPtr
->monthbk
= (COLORREF
)lParam
;
854 case MCSC_TRAILINGTEXT
:
855 prev
= infoPtr
->trailingtxt
;
856 infoPtr
->trailingtxt
= (COLORREF
)lParam
;
860 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
866 MONTHCAL_GetMonthDelta(MONTHCAL_INFO
*infoPtr
)
871 return infoPtr
->delta
;
873 return infoPtr
->visible
;
878 MONTHCAL_SetMonthDelta(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
)
880 int prev
= infoPtr
->delta
;
882 TRACE("delta %d\n", wParam
);
884 infoPtr
->delta
= (int)wParam
;
890 MONTHCAL_GetFirstDayOfWeek(MONTHCAL_INFO
*infoPtr
)
892 return infoPtr
->firstDay
;
896 /* sets the first day of the week that will appear in the control */
897 /* 0 == Sunday, 6 == Saturday */
898 /* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
899 /* FIXME: we need more error checking here */
901 MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
903 int prev
= infoPtr
->firstDay
;
906 TRACE("day %ld\n", lParam
);
908 if((lParam
>= 0) && (lParam
< 7)) {
909 infoPtr
->firstDay
= (int)lParam
;
913 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_IFIRSTDAYOFWEEK
, buf
, countof(buf
));
914 TRACE("%s %d\n", debugstr_w(buf
), strlenW(buf
));
915 infoPtr
->firstDay
= (atoiW(buf
)+1)%7;
922 MONTHCAL_GetMonthRange(MONTHCAL_INFO
*infoPtr
)
926 return infoPtr
->monthRange
;
931 MONTHCAL_GetMaxTodayWidth(MONTHCAL_INFO
*infoPtr
)
933 return(infoPtr
->todayrect
.right
- infoPtr
->todayrect
.left
);
937 /* FIXME: are validated times taken from current date/time or simply
939 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
940 * adjusting range with MCM_SETRANGE
944 MONTHCAL_SetRange(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
946 SYSTEMTIME
*lprgSysTimeArray
=(SYSTEMTIME
*)lParam
;
949 TRACE("%x %lx\n", wParam
, lParam
);
951 if(wParam
& GDTR_MAX
) {
952 if(MONTHCAL_ValidateTime(lprgSysTimeArray
[1])){
953 MONTHCAL_CopyTime(&lprgSysTimeArray
[1], &infoPtr
->maxDate
);
954 infoPtr
->rangeValid
|=GDTR_MAX
;
956 GetSystemTime(&infoPtr
->todaysDate
);
957 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, &infoPtr
->maxDate
);
960 if(wParam
& GDTR_MIN
) {
961 if(MONTHCAL_ValidateTime(lprgSysTimeArray
[0])) {
962 MONTHCAL_CopyTime(&lprgSysTimeArray
[0], &infoPtr
->minDate
);
963 infoPtr
->rangeValid
|=GDTR_MIN
;
965 GetSystemTime(&infoPtr
->todaysDate
);
966 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, &infoPtr
->minDate
);
970 prev
= infoPtr
->monthRange
;
971 infoPtr
->monthRange
= infoPtr
->maxDate
.wMonth
- infoPtr
->minDate
.wMonth
;
973 if(infoPtr
->monthRange
!=prev
) {
974 infoPtr
->monthdayState
= ReAlloc(infoPtr
->monthdayState
,
975 infoPtr
->monthRange
* sizeof(MONTHDAYSTATE
));
983 MONTHCAL_GetRange(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
985 MONTHCAL_INFO
*infoPtr
= MONTHCAL_GetInfoPtr(hwnd
);
986 SYSTEMTIME
*lprgSysTimeArray
= (SYSTEMTIME
*)lParam
;
988 /* validate parameters */
990 if((infoPtr
==NULL
) || (lprgSysTimeArray
==NULL
)) return FALSE
;
992 MONTHCAL_CopyTime(&infoPtr
->maxDate
, &lprgSysTimeArray
[1]);
993 MONTHCAL_CopyTime(&infoPtr
->minDate
, &lprgSysTimeArray
[0]);
995 return infoPtr
->rangeValid
;
1000 MONTHCAL_SetDayState(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
1003 int i
, iMonths
= (int)wParam
;
1004 MONTHDAYSTATE
*dayStates
= (LPMONTHDAYSTATE
)lParam
;
1006 TRACE("%x %lx\n", wParam
, lParam
);
1007 if(iMonths
!=infoPtr
->monthRange
) return 0;
1009 for(i
=0; i
<iMonths
; i
++)
1010 infoPtr
->monthdayState
[i
] = dayStates
[i
];
1015 MONTHCAL_GetCurSel(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1017 SYSTEMTIME
*lpSel
= (SYSTEMTIME
*) lParam
;
1019 TRACE("%lx\n", lParam
);
1020 if((infoPtr
==NULL
) ||(lpSel
==NULL
)) return FALSE
;
1021 if(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & MCS_MULTISELECT
) return FALSE
;
1023 MONTHCAL_CopyTime(&infoPtr
->minSel
, lpSel
);
1024 TRACE("%d/%d/%d\n", lpSel
->wYear
, lpSel
->wMonth
, lpSel
->wDay
);
1028 /* FIXME: if the specified date is not visible, make it visible */
1029 /* FIXME: redraw? */
1031 MONTHCAL_SetCurSel(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1033 SYSTEMTIME
*lpSel
= (SYSTEMTIME
*)lParam
;
1035 TRACE("%lx\n", lParam
);
1036 if((infoPtr
==NULL
) ||(lpSel
==NULL
)) return FALSE
;
1037 if(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & MCS_MULTISELECT
) return FALSE
;
1039 infoPtr
->currentMonth
=lpSel
->wMonth
;
1040 infoPtr
->currentYear
=lpSel
->wYear
;
1042 MONTHCAL_CopyTime(lpSel
, &infoPtr
->minSel
);
1043 MONTHCAL_CopyTime(lpSel
, &infoPtr
->maxSel
);
1045 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1052 MONTHCAL_GetMaxSelCount(MONTHCAL_INFO
*infoPtr
)
1054 return infoPtr
->maxSelCount
;
1059 MONTHCAL_SetMaxSelCount(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
)
1061 TRACE("%x\n", wParam
);
1063 if(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & MCS_MULTISELECT
) {
1064 infoPtr
->maxSelCount
= wParam
;
1072 MONTHCAL_GetSelRange(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1074 SYSTEMTIME
*lprgSysTimeArray
= (SYSTEMTIME
*) lParam
;
1076 TRACE("%lx\n", lParam
);
1078 /* validate parameters */
1080 if((infoPtr
==NULL
) ||(lprgSysTimeArray
==NULL
)) return FALSE
;
1082 if(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & MCS_MULTISELECT
)
1084 MONTHCAL_CopyTime(&infoPtr
->maxSel
, &lprgSysTimeArray
[1]);
1085 MONTHCAL_CopyTime(&infoPtr
->minSel
, &lprgSysTimeArray
[0]);
1086 TRACE("[min,max]=[%d %d]\n", infoPtr
->minSel
.wDay
, infoPtr
->maxSel
.wDay
);
1095 MONTHCAL_SetSelRange(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1097 SYSTEMTIME
*lprgSysTimeArray
= (SYSTEMTIME
*) lParam
;
1099 TRACE("%lx\n", lParam
);
1101 /* validate parameters */
1103 if((infoPtr
==NULL
) ||(lprgSysTimeArray
==NULL
)) return FALSE
;
1105 if(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & MCS_MULTISELECT
)
1107 MONTHCAL_CopyTime(&lprgSysTimeArray
[1], &infoPtr
->maxSel
);
1108 MONTHCAL_CopyTime(&lprgSysTimeArray
[0], &infoPtr
->minSel
);
1109 TRACE("[min,max]=[%d %d]\n", infoPtr
->minSel
.wDay
, infoPtr
->maxSel
.wDay
);
1118 MONTHCAL_GetToday(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1120 SYSTEMTIME
*lpToday
= (SYSTEMTIME
*) lParam
;
1122 TRACE("%lx\n", lParam
);
1124 /* validate parameters */
1126 if((infoPtr
==NULL
) || (lpToday
==NULL
)) return FALSE
;
1127 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, lpToday
);
1133 MONTHCAL_SetToday(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1135 SYSTEMTIME
*lpToday
= (SYSTEMTIME
*) lParam
;
1137 TRACE("%lx\n", lParam
);
1139 /* validate parameters */
1141 if((infoPtr
==NULL
) ||(lpToday
==NULL
)) return FALSE
;
1142 MONTHCAL_CopyTime(lpToday
, &infoPtr
->todaysDate
);
1143 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1149 MONTHCAL_HitTest(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1151 PMCHITTESTINFO lpht
= (PMCHITTESTINFO
)lParam
;
1159 retval
= MCHT_NOWHERE
;
1161 ZeroMemory(&lpht
->st
, sizeof(lpht
->st
));
1163 /* Comment in for debugging...
1164 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,
1165 infoPtr->wdays.left, infoPtr->wdays.right,
1166 infoPtr->wdays.top, infoPtr->wdays.bottom,
1167 infoPtr->days.left, infoPtr->days.right,
1168 infoPtr->days.top, infoPtr->days.bottom,
1169 infoPtr->todayrect.left, infoPtr->todayrect.right,
1170 infoPtr->todayrect.top, infoPtr->todayrect.bottom,
1171 infoPtr->weeknums.left, infoPtr->weeknums.right,
1172 infoPtr->weeknums.top, infoPtr->weeknums.bottom);
1175 /* are we in the header? */
1177 if(PtInRect(&infoPtr
->title
, lpht
->pt
)) {
1178 if(PtInRect(&infoPtr
->titlebtnprev
, lpht
->pt
)) {
1179 retval
= MCHT_TITLEBTNPREV
;
1182 if(PtInRect(&infoPtr
->titlebtnnext
, lpht
->pt
)) {
1183 retval
= MCHT_TITLEBTNNEXT
;
1186 if(PtInRect(&infoPtr
->titlemonth
, lpht
->pt
)) {
1187 retval
= MCHT_TITLEMONTH
;
1190 if(PtInRect(&infoPtr
->titleyear
, lpht
->pt
)) {
1191 retval
= MCHT_TITLEYEAR
;
1195 retval
= MCHT_TITLE
;
1199 day
= MONTHCAL_CalcDayFromPos(infoPtr
,x
,y
,&wday
,&wnum
);
1200 if(PtInRect(&infoPtr
->wdays
, lpht
->pt
)) {
1201 retval
= MCHT_CALENDARDAY
;
1202 lpht
->st
.wYear
= infoPtr
->currentYear
;
1203 lpht
->st
.wMonth
= (day
< 1)? infoPtr
->currentMonth
-1 : infoPtr
->currentMonth
;
1204 lpht
->st
.wDay
= (day
< 1)?
1205 MONTHCAL_MonthLength(infoPtr
->currentMonth
-1,infoPtr
->currentYear
) -day
: day
;
1208 if(PtInRect(&infoPtr
->weeknums
, lpht
->pt
)) {
1209 retval
= MCHT_CALENDARWEEKNUM
;
1210 lpht
->st
.wYear
= infoPtr
->currentYear
;
1211 lpht
->st
.wMonth
= (day
< 1) ? infoPtr
->currentMonth
-1 :
1212 (day
> MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
)) ?
1213 infoPtr
->currentMonth
+1 :infoPtr
->currentMonth
;
1214 lpht
->st
.wDay
= (day
< 1 ) ?
1215 MONTHCAL_MonthLength(infoPtr
->currentMonth
-1,infoPtr
->currentYear
) -day
:
1216 (day
> MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
)) ?
1217 day
- MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
) : day
;
1220 if(PtInRect(&infoPtr
->days
, lpht
->pt
))
1222 lpht
->st
.wYear
= infoPtr
->currentYear
;
1225 retval
= MCHT_CALENDARDATEPREV
;
1226 lpht
->st
.wMonth
= infoPtr
->currentMonth
- 1;
1227 if (lpht
->st
.wMonth
<1)
1229 lpht
->st
.wMonth
= 12;
1232 lpht
->st
.wDay
= MONTHCAL_MonthLength(lpht
->st
.wMonth
,lpht
->st
.wYear
) -day
;
1234 else if (day
> MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
))
1236 retval
= MCHT_CALENDARDATENEXT
;
1237 lpht
->st
.wMonth
= infoPtr
->currentMonth
+ 1;
1238 if (lpht
->st
.wMonth
<12)
1240 lpht
->st
.wMonth
= 1;
1243 lpht
->st
.wDay
= day
- MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
) ;
1246 retval
= MCHT_CALENDARDATE
;
1247 lpht
->st
.wMonth
= infoPtr
->currentMonth
;
1248 lpht
->st
.wDay
= day
;
1249 lpht
->st
.wDayOfWeek
= MONTHCAL_CalculateDayOfWeek(day
,lpht
->st
.wMonth
,lpht
->st
.wYear
);
1253 if(PtInRect(&infoPtr
->todayrect
, lpht
->pt
)) {
1254 retval
= MCHT_TODAYLINK
;
1259 /* Hit nothing special? What's left must be background :-) */
1261 retval
= MCHT_CALENDARBK
;
1263 lpht
->uHit
= retval
;
1268 static void MONTHCAL_GoToNextMonth(MONTHCAL_INFO
*infoPtr
)
1270 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
1272 TRACE("MONTHCAL_GoToNextMonth\n");
1274 infoPtr
->currentMonth
++;
1275 if(infoPtr
->currentMonth
> 12) {
1276 infoPtr
->currentYear
++;
1277 infoPtr
->currentMonth
= 1;
1280 if(dwStyle
& MCS_DAYSTATE
) {
1284 nmds
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1285 nmds
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1286 nmds
.nmhdr
.code
= MCN_GETDAYSTATE
;
1287 nmds
.cDayState
= infoPtr
->monthRange
;
1288 nmds
.prgDayState
= Alloc(infoPtr
->monthRange
* sizeof(MONTHDAYSTATE
));
1290 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
1291 (WPARAM
)nmds
.nmhdr
.idFrom
, (LPARAM
)&nmds
);
1292 for(i
=0; i
<infoPtr
->monthRange
; i
++)
1293 infoPtr
->monthdayState
[i
] = nmds
.prgDayState
[i
];
1298 static void MONTHCAL_GoToPrevMonth(MONTHCAL_INFO
*infoPtr
)
1300 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
1304 infoPtr
->currentMonth
--;
1305 if(infoPtr
->currentMonth
< 1) {
1306 infoPtr
->currentYear
--;
1307 infoPtr
->currentMonth
= 12;
1310 if(dwStyle
& MCS_DAYSTATE
) {
1314 nmds
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1315 nmds
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1316 nmds
.nmhdr
.code
= MCN_GETDAYSTATE
;
1317 nmds
.cDayState
= infoPtr
->monthRange
;
1318 nmds
.prgDayState
= Alloc
1319 (infoPtr
->monthRange
* sizeof(MONTHDAYSTATE
));
1321 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
1322 (WPARAM
)nmds
.nmhdr
.idFrom
, (LPARAM
)&nmds
);
1323 for(i
=0; i
<infoPtr
->monthRange
; i
++)
1324 infoPtr
->monthdayState
[i
] = nmds
.prgDayState
[i
];
1329 MONTHCAL_RButtonDown(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1331 static const WCHAR todayW
[] = { 'G','o',' ','t','o',' ','T','o','d','a','y',':',0 };
1336 hMenu
= CreatePopupMenu();
1337 if (!LoadStringW(COMCTL32_hModule
,IDM_GOTODAY
,buf
,countof(buf
)))
1339 WARN("Can't load resource\n");
1340 strcpyW(buf
, todayW
);
1342 AppendMenuW(hMenu
, MF_STRING
|MF_ENABLED
,1, buf
);
1343 menupoint
.x
=(INT
)LOWORD(lParam
);
1344 menupoint
.y
=(INT
)HIWORD(lParam
);
1345 ClientToScreen(infoPtr
->hwndSelf
, &menupoint
);
1346 if( TrackPopupMenu(hMenu
,TPM_RIGHTBUTTON
| TPM_NONOTIFY
|TPM_RETURNCMD
,
1347 menupoint
.x
, menupoint
.y
, 0, infoPtr
->hwndSelf
, NULL
))
1349 infoPtr
->currentMonth
=infoPtr
->todaysDate
.wMonth
;
1350 infoPtr
->currentYear
=infoPtr
->todaysDate
.wYear
;
1351 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1357 MONTHCAL_LButtonDown(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1359 static const WCHAR EditW
[] = { 'E','D','I','T',0 };
1363 RECT rcDay
; /* used in determining area to invalidate */
1368 TRACE("%lx\n", lParam
);
1370 if (infoPtr
->hWndYearUpDown
)
1372 infoPtr
->currentYear
=SendMessageW( infoPtr
->hWndYearUpDown
, UDM_SETPOS
, (WPARAM
) 0,(LPARAM
)0);
1373 if(!DestroyWindow(infoPtr
->hWndYearUpDown
))
1375 FIXME("Can't destroy Updown Control\n");
1378 infoPtr
->hWndYearUpDown
=0;
1379 if(!DestroyWindow(infoPtr
->hWndYearEdit
))
1381 FIXME("Can't destroy Updown Control\n");
1384 infoPtr
->hWndYearEdit
=0;
1385 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1388 ht
.pt
.x
= (INT
)LOWORD(lParam
);
1389 ht
.pt
.y
= (INT
)HIWORD(lParam
);
1390 hit
= MONTHCAL_HitTest(infoPtr
, (LPARAM
)&ht
);
1392 /* FIXME: these flags should be checked by */
1393 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1395 if(hit
==MCHT_TITLEBTNNEXT
) {
1396 MONTHCAL_GoToNextMonth(infoPtr
);
1397 infoPtr
->status
= MC_NEXTPRESSED
;
1398 SetTimer(infoPtr
->hwndSelf
, MC_NEXTMONTHTIMER
, MC_NEXTMONTHDELAY
, 0);
1399 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1402 if(hit
== MCHT_TITLEBTNPREV
){
1403 MONTHCAL_GoToPrevMonth(infoPtr
);
1404 infoPtr
->status
= MC_PREVPRESSED
;
1405 SetTimer(infoPtr
->hwndSelf
, MC_PREVMONTHTIMER
, MC_NEXTMONTHDELAY
, 0);
1406 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1410 if(hit
== MCHT_TITLEMONTH
) {
1411 hMenu
= CreatePopupMenu();
1415 GetLocaleInfoW(LOCALE_USER_DEFAULT
,LOCALE_SMONTHNAME1
+i
, buf
,countof(buf
));
1416 AppendMenuW(hMenu
, MF_STRING
|MF_ENABLED
,i
+1, buf
);
1418 menupoint
.x
=infoPtr
->titlemonth
.right
;
1419 menupoint
.y
=infoPtr
->titlemonth
.bottom
;
1420 ClientToScreen(infoPtr
->hwndSelf
, &menupoint
);
1421 i
= TrackPopupMenu(hMenu
,TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
| TPM_RETURNCMD
,
1422 menupoint
.x
, menupoint
.y
, 0, infoPtr
->hwndSelf
, NULL
);
1423 if ((i
>0) && (i
<13))
1425 infoPtr
->currentMonth
=i
;
1426 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1429 if(hit
== MCHT_TITLEYEAR
) {
1430 infoPtr
->hWndYearEdit
=CreateWindowExW(0,
1433 WS_VISIBLE
| WS_CHILD
|UDS_SETBUDDYINT
,
1434 infoPtr
->titleyear
.left
+3,infoPtr
->titlebtnnext
.top
,
1435 infoPtr
->titleyear
.right
-infoPtr
->titleyear
.left
+4,
1436 infoPtr
->textHeight
,
1441 SendMessageW( infoPtr
->hWndYearEdit
, WM_SETFONT
, (WPARAM
) infoPtr
->hBoldFont
, (LPARAM
)TRUE
);
1442 infoPtr
->hWndYearUpDown
=CreateWindowExW(0,
1445 WS_VISIBLE
| WS_CHILD
|UDS_SETBUDDYINT
|UDS_NOTHOUSANDS
|UDS_ARROWKEYS
,
1446 infoPtr
->titleyear
.right
+7,infoPtr
->titlebtnnext
.top
,
1448 infoPtr
->textHeight
,
1453 SendMessageW( infoPtr
->hWndYearUpDown
, UDM_SETRANGE
, (WPARAM
) 0, MAKELONG (9999, 1753));
1454 SendMessageW( infoPtr
->hWndYearUpDown
, UDM_SETBUDDY
, (WPARAM
) infoPtr
->hWndYearEdit
, (LPARAM
)0 );
1455 SendMessageW( infoPtr
->hWndYearUpDown
, UDM_SETPOS
, (WPARAM
) 0,(LPARAM
)infoPtr
->currentYear
);
1459 if(hit
== MCHT_TODAYLINK
) {
1460 infoPtr
->currentMonth
=infoPtr
->todaysDate
.wMonth
;
1461 infoPtr
->currentYear
=infoPtr
->todaysDate
.wYear
;
1462 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1465 if(hit
== MCHT_CALENDARDATE
) {
1466 SYSTEMTIME selArray
[2];
1469 MONTHCAL_CopyTime(&ht
.st
, &selArray
[0]);
1470 MONTHCAL_CopyTime(&ht
.st
, &selArray
[1]);
1471 MONTHCAL_SetSelRange(infoPtr
, (LPARAM
)&selArray
);
1472 MONTHCAL_SetCurSel(infoPtr
, (LPARAM
)&selArray
);
1473 TRACE("MCHT_CALENDARDATE\n");
1474 nmsc
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1475 nmsc
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1476 nmsc
.nmhdr
.code
= MCN_SELCHANGE
;
1477 MONTHCAL_CopyTime(&infoPtr
->minSel
,&nmsc
.stSelStart
);
1478 MONTHCAL_CopyTime(&infoPtr
->maxSel
,&nmsc
.stSelEnd
);
1480 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
1481 (WPARAM
)nmsc
.nmhdr
.idFrom
,(LPARAM
)&nmsc
);
1484 /* redraw both old and new days if the selected day changed */
1485 if(infoPtr
->curSelDay
!= ht
.st
.wDay
) {
1486 MONTHCAL_CalcPosFromDay(infoPtr
, ht
.st
.wDay
, ht
.st
.wMonth
, &rcDay
);
1487 InvalidateRect(infoPtr
->hwndSelf
, &rcDay
, TRUE
);
1489 MONTHCAL_CalcPosFromDay(infoPtr
, infoPtr
->curSelDay
, infoPtr
->currentMonth
, &rcDay
);
1490 InvalidateRect(infoPtr
->hwndSelf
, &rcDay
, TRUE
);
1493 infoPtr
->firstSelDay
= ht
.st
.wDay
;
1494 infoPtr
->curSelDay
= ht
.st
.wDay
;
1495 infoPtr
->status
= MC_SEL_LBUTDOWN
;
1504 MONTHCAL_LButtonUp(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1508 BOOL redraw
= FALSE
;
1514 if(infoPtr
->status
& MC_NEXTPRESSED
) {
1515 KillTimer(infoPtr
->hwndSelf
, MC_NEXTMONTHTIMER
);
1516 infoPtr
->status
&= ~MC_NEXTPRESSED
;
1519 if(infoPtr
->status
& MC_PREVPRESSED
) {
1520 KillTimer(infoPtr
->hwndSelf
, MC_PREVMONTHTIMER
);
1521 infoPtr
->status
&= ~MC_PREVPRESSED
;
1525 ht
.pt
.x
= (INT
)LOWORD(lParam
);
1526 ht
.pt
.y
= (INT
)HIWORD(lParam
);
1527 hit
= MONTHCAL_HitTest(infoPtr
, (LPARAM
)&ht
);
1529 infoPtr
->status
= MC_SEL_LBUTUP
;
1531 if(hit
==MCHT_CALENDARDATENEXT
) {
1532 MONTHCAL_GoToNextMonth(infoPtr
);
1533 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1536 if(hit
== MCHT_CALENDARDATEPREV
){
1537 MONTHCAL_GoToPrevMonth(infoPtr
);
1538 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1541 nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1542 nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1543 nmhdr
.code
= NM_RELEASEDCAPTURE
;
1544 TRACE("Sent notification from %p to %p\n", infoPtr
->hwndSelf
, infoPtr
->hwndNotify
);
1546 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, (WPARAM
)nmhdr
.idFrom
, (LPARAM
)&nmhdr
);
1547 /* redraw if necessary */
1549 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1550 /* only send MCN_SELECT if currently displayed month's day was selected */
1551 if(hit
== MCHT_CALENDARDATE
) {
1552 nmsc
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1553 nmsc
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1554 nmsc
.nmhdr
.code
= MCN_SELECT
;
1555 MONTHCAL_CopyTime(&infoPtr
->minSel
, &nmsc
.stSelStart
);
1556 MONTHCAL_CopyTime(&infoPtr
->maxSel
, &nmsc
.stSelEnd
);
1558 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, (WPARAM
)nmsc
.nmhdr
.idFrom
, (LPARAM
)&nmsc
);
1566 MONTHCAL_Timer(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
)
1568 BOOL redraw
= FALSE
;
1570 TRACE("%d\n", wParam
);
1573 case MC_NEXTMONTHTIMER
:
1575 MONTHCAL_GoToNextMonth(infoPtr
);
1577 case MC_PREVMONTHTIMER
:
1579 MONTHCAL_GoToPrevMonth(infoPtr
);
1582 ERR("got unknown timer\n");
1586 /* redraw only if necessary */
1588 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1595 MONTHCAL_MouseMove(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
1598 int oldselday
, selday
, hit
;
1601 if(!(infoPtr
->status
& MC_SEL_LBUTDOWN
)) return 0;
1603 ht
.pt
.x
= LOWORD(lParam
);
1604 ht
.pt
.y
= HIWORD(lParam
);
1606 hit
= MONTHCAL_HitTest(infoPtr
, (LPARAM
)&ht
);
1608 /* not on the calendar date numbers? bail out */
1609 TRACE("hit:%x\n",hit
);
1610 if((hit
& MCHT_CALENDARDATE
) != MCHT_CALENDARDATE
) return 0;
1612 selday
= ht
.st
.wDay
;
1613 oldselday
= infoPtr
->curSelDay
;
1614 infoPtr
->curSelDay
= selday
;
1615 MONTHCAL_CalcPosFromDay(infoPtr
, selday
, ht
.st
. wMonth
, &r
);
1617 if(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & MCS_MULTISELECT
) {
1618 SYSTEMTIME selArray
[2];
1621 MONTHCAL_GetSelRange(infoPtr
, (LPARAM
)&selArray
);
1623 if(infoPtr
->firstSelDay
==selArray
[0].wDay
) i
=1;
1624 TRACE("oldRange:%d %d %d %d\n", infoPtr
->firstSelDay
, selArray
[0].wDay
, selArray
[1].wDay
, i
);
1625 if(infoPtr
->firstSelDay
==selArray
[1].wDay
) {
1626 /* 1st time we get here: selArray[0]=selArray[1]) */
1627 /* if we're still at the first selected date, return */
1628 if(infoPtr
->firstSelDay
==selday
) goto done
;
1629 if(selday
<infoPtr
->firstSelDay
) i
= 0;
1632 if(abs(infoPtr
->firstSelDay
- selday
) >= infoPtr
->maxSelCount
) {
1633 if(selday
>infoPtr
->firstSelDay
)
1634 selday
= infoPtr
->firstSelDay
+ infoPtr
->maxSelCount
;
1636 selday
= infoPtr
->firstSelDay
- infoPtr
->maxSelCount
;
1639 if(selArray
[i
].wDay
!=selday
) {
1640 TRACE("newRange:%d %d %d %d\n", infoPtr
->firstSelDay
, selArray
[0].wDay
, selArray
[1].wDay
, i
);
1642 selArray
[i
].wDay
= selday
;
1644 if(selArray
[0].wDay
>selArray
[1].wDay
) {
1646 tempday
= selArray
[1].wDay
;
1647 selArray
[1].wDay
= selArray
[0].wDay
;
1648 selArray
[0].wDay
= tempday
;
1651 MONTHCAL_SetSelRange(infoPtr
, (LPARAM
)&selArray
);
1657 /* only redraw if the currently selected day changed */
1658 /* FIXME: this should specify a rectangle containing only the days that changed */
1659 /* using InvalidateRect */
1660 if(oldselday
!= infoPtr
->curSelDay
)
1661 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1668 MONTHCAL_Paint(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
)
1675 GetClientRect(infoPtr
->hwndSelf
, &ps
.rcPaint
);
1679 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
1681 MONTHCAL_Refresh(infoPtr
, hdc
, &ps
);
1682 if (!wParam
) EndPaint(infoPtr
->hwndSelf
, &ps
);
1688 MONTHCAL_KillFocus(MONTHCAL_INFO
*infoPtr
)
1692 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
1699 MONTHCAL_SetFocus(MONTHCAL_INFO
*infoPtr
)
1703 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1708 /* sets the size information */
1709 static void MONTHCAL_UpdateSize(MONTHCAL_INFO
*infoPtr
)
1711 static const WCHAR SunW
[] = { 'S','u','n',0 };
1712 static const WCHAR O0W
[] = { '0','0',0 };
1713 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
1714 RECT
*title
=&infoPtr
->title
;
1715 RECT
*prev
=&infoPtr
->titlebtnprev
;
1716 RECT
*next
=&infoPtr
->titlebtnnext
;
1717 RECT
*titlemonth
=&infoPtr
->titlemonth
;
1718 RECT
*titleyear
=&infoPtr
->titleyear
;
1719 RECT
*wdays
=&infoPtr
->wdays
;
1720 RECT
*weeknumrect
=&infoPtr
->weeknums
;
1721 RECT
*days
=&infoPtr
->days
;
1722 RECT
*todayrect
=&infoPtr
->todayrect
;
1725 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
);
1727 int xdiv
, left_offset
;
1730 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
1732 currentFont
= SelectObject(hdc
, infoPtr
->hFont
);
1734 /* get the height and width of each day's text */
1735 GetTextMetricsW(hdc
, &tm
);
1736 infoPtr
->textHeight
= tm
.tmHeight
+ tm
.tmExternalLeading
+ tm
.tmInternalLeading
;
1737 GetTextExtentPoint32W(hdc
, SunW
, 3, &size
);
1738 infoPtr
->textWidth
= size
.cx
+ 2;
1740 /* recalculate the height and width increments and offsets */
1741 GetTextExtentPoint32W(hdc
, O0W
, 2, &size
);
1743 xdiv
= (dwStyle
& MCS_WEEKNUMBERS
) ? 8 : 7;
1745 infoPtr
->width_increment
= size
.cx
* 2 + 4;
1746 infoPtr
->height_increment
= infoPtr
->textHeight
;
1747 left_offset
= (rcClient
.right
- rcClient
.left
) - (infoPtr
->width_increment
* xdiv
);
1749 /* calculate title area */
1750 title
->top
= rcClient
.top
;
1751 title
->bottom
= title
->top
+ 3 * infoPtr
->height_increment
/ 2;
1752 title
->left
= left_offset
;
1753 title
->right
= rcClient
.right
;
1755 /* set the dimensions of the next and previous buttons and center */
1756 /* the month text vertically */
1757 prev
->top
= next
->top
= title
->top
+ 4;
1758 prev
->bottom
= next
->bottom
= title
->bottom
- 4;
1759 prev
->left
= title
->left
+ 4;
1760 prev
->right
= prev
->left
+ (title
->bottom
- title
->top
) ;
1761 next
->right
= title
->right
- 4;
1762 next
->left
= next
->right
- (title
->bottom
- title
->top
);
1764 /* titlemonth->left and right change based upon the current month */
1765 /* and are recalculated in refresh as the current month may change */
1766 /* without the control being resized */
1767 titlemonth
->top
= titleyear
->top
= title
->top
+ (infoPtr
->height_increment
)/2;
1768 titlemonth
->bottom
= titleyear
->bottom
= title
->bottom
- (infoPtr
->height_increment
)/2;
1770 /* setup the dimensions of the rectangle we draw the names of the */
1771 /* days of the week in */
1772 weeknumrect
->left
= left_offset
;
1773 if(dwStyle
& MCS_WEEKNUMBERS
)
1774 weeknumrect
->right
=prev
->right
;
1776 weeknumrect
->right
=weeknumrect
->left
;
1777 wdays
->left
= days
->left
= weeknumrect
->right
;
1778 wdays
->right
= days
->right
= wdays
->left
+ 7 * infoPtr
->width_increment
;
1779 wdays
->top
= title
->bottom
;
1780 wdays
->bottom
= wdays
->top
+ infoPtr
->height_increment
;
1782 days
->top
= weeknumrect
->top
= wdays
->bottom
;
1783 days
->bottom
= weeknumrect
->bottom
= days
->top
+ 6 * infoPtr
->height_increment
;
1785 todayrect
->left
= rcClient
.left
;
1786 todayrect
->right
= rcClient
.right
;
1787 todayrect
->top
= days
->bottom
;
1788 todayrect
->bottom
= days
->bottom
+ infoPtr
->height_increment
;
1790 TRACE("dx=%d dy=%d client[%s] title[%s] wdays[%s] days[%s] today[%s]\n",
1791 infoPtr
->width_increment
,infoPtr
->height_increment
,
1792 wine_dbgstr_rect(&rcClient
),
1793 wine_dbgstr_rect(title
),
1794 wine_dbgstr_rect(wdays
),
1795 wine_dbgstr_rect(days
),
1796 wine_dbgstr_rect(todayrect
));
1798 /* restore the originally selected font */
1799 SelectObject(hdc
, currentFont
);
1801 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1804 static LRESULT
MONTHCAL_Size(MONTHCAL_INFO
*infoPtr
, int Width
, int Height
)
1806 TRACE("(width=%d, height=%d)\n", Width
, Height
);
1808 MONTHCAL_UpdateSize(infoPtr
);
1810 /* invalidate client area and erase background */
1811 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
1816 static LRESULT
MONTHCAL_GetFont(MONTHCAL_INFO
*infoPtr
)
1818 return (LRESULT
)infoPtr
->hFont
;
1821 static LRESULT
MONTHCAL_SetFont(MONTHCAL_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
)
1826 if (!hFont
) return 0;
1828 hOldFont
= infoPtr
->hFont
;
1829 infoPtr
->hFont
= hFont
;
1831 GetObjectW(infoPtr
->hFont
, sizeof(lf
), &lf
);
1832 lf
.lfWeight
= FW_BOLD
;
1833 infoPtr
->hBoldFont
= CreateFontIndirectW(&lf
);
1836 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1838 return (LRESULT
)hOldFont
;
1841 /* update theme after a WM_THEMECHANGED message */
1842 static LRESULT
theme_changed (MONTHCAL_INFO
* infoPtr
)
1844 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
1845 CloseThemeData (theme
);
1846 theme
= OpenThemeData (infoPtr
->hwndSelf
, themeClass
);
1850 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1852 MONTHCAL_Create(HWND hwnd
, WPARAM wParam
, LPARAM lParam
)
1854 MONTHCAL_INFO
*infoPtr
;
1856 /* allocate memory for info structure */
1857 infoPtr
=(MONTHCAL_INFO
*)Alloc(sizeof(MONTHCAL_INFO
));
1858 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
1860 if(infoPtr
== NULL
) {
1861 ERR( "could not allocate info memory!\n");
1865 infoPtr
->hwndSelf
= hwnd
;
1866 infoPtr
->hwndNotify
= ((LPCREATESTRUCTW
)lParam
)->hwndParent
;
1868 MONTHCAL_SetFont(infoPtr
, GetStockObject(DEFAULT_GUI_FONT
), FALSE
);
1870 /* initialize info structure */
1871 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1873 GetLocalTime(&infoPtr
->todaysDate
);
1874 MONTHCAL_SetFirstDayOfWeek(infoPtr
, (LPARAM
)-1);
1875 infoPtr
->currentMonth
= infoPtr
->todaysDate
.wMonth
;
1876 infoPtr
->currentYear
= infoPtr
->todaysDate
.wYear
;
1877 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, &infoPtr
->minDate
);
1878 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, &infoPtr
->maxDate
);
1879 infoPtr
->maxDate
.wYear
=2050;
1880 infoPtr
->minDate
.wYear
=1950;
1881 infoPtr
->maxSelCount
= 7;
1882 infoPtr
->monthRange
= 3;
1883 infoPtr
->monthdayState
= Alloc
1884 (infoPtr
->monthRange
* sizeof(MONTHDAYSTATE
));
1885 infoPtr
->titlebk
= GetSysColor(COLOR_ACTIVECAPTION
);
1886 infoPtr
->titletxt
= GetSysColor(COLOR_WINDOW
);
1887 infoPtr
->monthbk
= GetSysColor(COLOR_WINDOW
);
1888 infoPtr
->trailingtxt
= GetSysColor(COLOR_GRAYTEXT
);
1889 infoPtr
->bk
= GetSysColor(COLOR_WINDOW
);
1890 infoPtr
->txt
= GetSysColor(COLOR_WINDOWTEXT
);
1892 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1893 /* of the control */
1894 MONTHCAL_UpdateSize(infoPtr
);
1896 OpenThemeData (infoPtr
->hwndSelf
, themeClass
);
1903 MONTHCAL_Destroy(MONTHCAL_INFO
*infoPtr
)
1905 /* free month calendar info data */
1906 if(infoPtr
->monthdayState
)
1907 Free(infoPtr
->monthdayState
);
1908 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
1910 CloseThemeData (GetWindowTheme (infoPtr
->hwndSelf
));
1917 static LRESULT WINAPI
1918 MONTHCAL_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1920 MONTHCAL_INFO
*infoPtr
;
1922 TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
1924 infoPtr
= MONTHCAL_GetInfoPtr(hwnd
);
1925 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
1926 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
1930 return MONTHCAL_GetCurSel(infoPtr
, lParam
);
1933 return MONTHCAL_SetCurSel(infoPtr
, lParam
);
1935 case MCM_GETMAXSELCOUNT
:
1936 return MONTHCAL_GetMaxSelCount(infoPtr
);
1938 case MCM_SETMAXSELCOUNT
:
1939 return MONTHCAL_SetMaxSelCount(infoPtr
, wParam
);
1941 case MCM_GETSELRANGE
:
1942 return MONTHCAL_GetSelRange(infoPtr
, lParam
);
1944 case MCM_SETSELRANGE
:
1945 return MONTHCAL_SetSelRange(infoPtr
, lParam
);
1947 case MCM_GETMONTHRANGE
:
1948 return MONTHCAL_GetMonthRange(infoPtr
);
1950 case MCM_SETDAYSTATE
:
1951 return MONTHCAL_SetDayState(infoPtr
, wParam
, lParam
);
1953 case MCM_GETMINREQRECT
:
1954 return MONTHCAL_GetMinReqRect(infoPtr
, lParam
);
1957 return MONTHCAL_GetColor(infoPtr
, wParam
);
1960 return MONTHCAL_SetColor(infoPtr
, wParam
, lParam
);
1963 return MONTHCAL_GetToday(infoPtr
, lParam
);
1966 return MONTHCAL_SetToday(infoPtr
, lParam
);
1969 return MONTHCAL_HitTest(infoPtr
, lParam
);
1971 case MCM_GETFIRSTDAYOFWEEK
:
1972 return MONTHCAL_GetFirstDayOfWeek(infoPtr
);
1974 case MCM_SETFIRSTDAYOFWEEK
:
1975 return MONTHCAL_SetFirstDayOfWeek(infoPtr
, lParam
);
1978 return MONTHCAL_GetRange(hwnd
, wParam
, lParam
);
1981 return MONTHCAL_SetRange(infoPtr
, wParam
, lParam
);
1983 case MCM_GETMONTHDELTA
:
1984 return MONTHCAL_GetMonthDelta(infoPtr
);
1986 case MCM_SETMONTHDELTA
:
1987 return MONTHCAL_SetMonthDelta(infoPtr
, wParam
);
1989 case MCM_GETMAXTODAYWIDTH
:
1990 return MONTHCAL_GetMaxTodayWidth(infoPtr
);
1993 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1996 return MONTHCAL_KillFocus(infoPtr
);
1998 case WM_RBUTTONDOWN
:
1999 return MONTHCAL_RButtonDown(infoPtr
, lParam
);
2001 case WM_LBUTTONDOWN
:
2002 return MONTHCAL_LButtonDown(infoPtr
, lParam
);
2005 return MONTHCAL_MouseMove(infoPtr
, wParam
, lParam
);
2008 return MONTHCAL_LButtonUp(infoPtr
, lParam
);
2010 case WM_PRINTCLIENT
:
2012 return MONTHCAL_Paint(infoPtr
, wParam
);
2015 return MONTHCAL_SetFocus(infoPtr
);
2018 return MONTHCAL_Size(infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
2021 return MONTHCAL_Create(hwnd
, wParam
, lParam
);
2024 return MONTHCAL_SetFont(infoPtr
, (HFONT
)wParam
, (BOOL
)lParam
);
2027 return MONTHCAL_GetFont(infoPtr
);
2030 return MONTHCAL_Timer(infoPtr
, wParam
);
2032 case WM_THEMECHANGED
:
2033 return theme_changed (infoPtr
);
2036 return MONTHCAL_Destroy(infoPtr
);
2039 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
))
2040 ERR( "unknown msg %04x wp=%08x lp=%08lx\n", uMsg
, wParam
, lParam
);
2041 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
2047 MONTHCAL_Register(void)
2051 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
2052 wndClass
.style
= CS_GLOBALCLASS
;
2053 wndClass
.lpfnWndProc
= MONTHCAL_WindowProc
;
2054 wndClass
.cbClsExtra
= 0;
2055 wndClass
.cbWndExtra
= sizeof(MONTHCAL_INFO
*);
2056 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
2057 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
2058 wndClass
.lpszClassName
= MONTHCAL_CLASSW
;
2060 RegisterClassW(&wndClass
);
2065 MONTHCAL_Unregister(void)
2067 UnregisterClassW(MONTHCAL_CLASSW
, NULL
);