7a49c7025bf15298e6ed508325a3c27b0e8e86c2
1 /* Month calendar control
4 * Copyright 1998, 1999 Eric Kohl (ekohl@abo.rhein-zeitung.de)
5 * Copyright 1999 Alex Priem (alexp@sci.kun.nl)
6 * Copyright 1999 Chris Morgan <cmorgan@wpi.edu> and
7 * James Abbatiello <abbeyj@wpi.edu>
8 * Copyright 2000 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on Oct. 20, 2004, by Dimitrie O. Paun.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
34 * -- MCM_[GS]ETUNICODEFORMAT
35 * -- MONTHCAL_GetMonthRange
36 * -- handle resources better (doesn't work now);
37 * -- take care of internationalization.
38 * -- keyboard handling.
39 * -- GetRange: At the moment, we copy ranges anyway, regardless of
40 * infoPtr->rangeValid; an invalid range is simply filled
41 * with zeros in SetRange. Is this the right behavior?
60 #include "wine/unicode.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(monthcal
);
65 #define MC_SEL_LBUTUP 1 /* Left button released */
66 #define MC_SEL_LBUTDOWN 2 /* Left button pressed in calendar */
67 #define MC_PREVPRESSED 4 /* Prev month button pressed */
68 #define MC_NEXTPRESSED 8 /* Next month button pressed */
69 #define MC_NEXTMONTHDELAY 350 /* when continuously pressing `next */
70 /* month', wait 500 ms before going */
71 /* to the next month */
72 #define MC_NEXTMONTHTIMER 1 /* Timer ID's */
73 #define MC_PREVMONTHTIMER 2
75 #define countof(arr) (sizeof(arr)/sizeof(arr[0]))
80 DWORD dwStyle
; /* cached GWL_STYLE */
93 int firstDayplace
; /* place of the first day of the current month */
94 INT delta
; /* scroll rate; # of months that the */
95 /* control moves when user clicks a scroll button */
96 int visible
; /* # of months visible */
97 int firstDay
; /* Start month calendar with firstDay's day */
98 int firstDayHighWord
; /* High word only used externally */
100 MONTHDAYSTATE
*monthdayState
;
101 SYSTEMTIME todaysDate
;
104 int status
; /* See MC_SEL flags */
105 int curSelDay
; /* current selected day */
106 int firstSelDay
; /* first selected day */
114 RECT title
; /* rect for the header above the calendar */
115 RECT titlebtnnext
; /* the `next month' button in the header */
116 RECT titlebtnprev
; /* the `prev month' button in the header */
117 RECT titlemonth
; /* the `month name' txt in the header */
118 RECT titleyear
; /* the `year number' txt in the header */
119 RECT wdays
; /* week days at top */
120 RECT days
; /* calendar area */
121 RECT weeknums
; /* week numbers at left side */
122 RECT todayrect
; /* `today: xx/xx/xx' text rect */
123 HWND hwndNotify
; /* Window to receive the notifications */
124 HWND hWndYearEdit
; /* Window Handle of edit box to handle years */
125 HWND hWndYearUpDown
;/* Window Handle of updown box to handle years */
126 } MONTHCAL_INFO
, *LPMONTHCAL_INFO
;
129 /* Offsets of days in the week to the weekday of january 1 in a leap year */
130 static const int DayOfWeekTable
[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
132 static const WCHAR themeClass
[] = { 'S','c','r','o','l','l','b','a','r',0 };
134 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0))
136 /* helper functions */
138 /* returns the number of days in any given month, checking for leap days */
139 /* january is 1, december is 12 */
140 int MONTHCAL_MonthLength(int month
, int year
)
142 const int mdays
[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
143 /*Wrap around, this eases handling*/
149 /* if we have a leap year add 1 day to February */
150 /* a leap year is a year either divisible by 400 */
151 /* or divisible by 4 and not by 100 */
152 if(month
== 2) { /* February */
153 return mdays
[month
- 1] + ((year
%400 == 0) ? 1 : ((year
%100 != 0) &&
154 (year
%4 == 0)) ? 1 : 0);
157 return mdays
[month
- 1];
161 /* compares timestamps using date part only */
162 static inline BOOL
MONTHCAL_IsDateEqual(const SYSTEMTIME
*first
, const SYSTEMTIME
*second
)
164 return (first
->wYear
== second
->wYear
) && (first
->wMonth
== second
->wMonth
) &&
165 (first
->wDay
== second
->wDay
);
168 /* make sure that time is valid */
169 static BOOL
MONTHCAL_ValidateTime(SYSTEMTIME time
)
171 if(time
.wMonth
< 1 || time
.wMonth
> 12 ) return FALSE
;
172 if(time
.wDayOfWeek
> 6) return FALSE
;
173 if(time
.wDay
> MONTHCAL_MonthLength(time
.wMonth
, time
.wYear
))
180 /* Note:Depending on DST, this may be offset by a day.
181 Need to find out if we're on a DST place & adjust the clock accordingly.
182 Above function assumes we have a valid data.
183 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
187 /* returns the day in the week(0 == sunday, 6 == saturday) */
188 /* day(1 == 1st, 2 == 2nd... etc), year is the year value */
189 static int MONTHCAL_CalculateDayOfWeek(DWORD day
, DWORD month
, DWORD year
)
193 return((year
+ year
/4 - year
/100 + year
/400 +
194 DayOfWeekTable
[month
-1] + day
) % 7);
197 /* From a given point, calculate the row (weekpos), column(daypos)
198 and day in the calendar. day== 0 mean the last day of tha last month
200 static int MONTHCAL_CalcDayFromPos(const MONTHCAL_INFO
*infoPtr
, int x
, int y
,
201 int *daypos
,int *weekpos
)
203 int retval
, firstDay
;
206 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
208 /* if the point is outside the x bounds of the window put
209 it at the boundary */
210 if (x
> rcClient
.right
)
214 *daypos
= (x
- infoPtr
->days
.left
) / infoPtr
->width_increment
;
215 *weekpos
= (y
- infoPtr
->days
.top
) / infoPtr
->height_increment
;
217 firstDay
= (MONTHCAL_CalculateDayOfWeek(1, infoPtr
->currentMonth
, infoPtr
->currentYear
)+6 - infoPtr
->firstDay
)%7;
218 retval
= *daypos
+ (7 * *weekpos
) - firstDay
;
222 /* day is the day of the month, 1 == 1st day of the month */
223 /* sets x and y to be the position of the day */
224 /* x == day, y == week where(0,0) == firstDay, 1st week */
225 static void MONTHCAL_CalcDayXY(const MONTHCAL_INFO
*infoPtr
, int day
, int month
,
228 int firstDay
, prevMonth
;
230 firstDay
= (MONTHCAL_CalculateDayOfWeek(1, infoPtr
->currentMonth
, infoPtr
->currentYear
) +6 - infoPtr
->firstDay
)%7;
232 if(month
==infoPtr
->currentMonth
) {
233 *x
= (day
+ firstDay
) % 7;
234 *y
= (day
+ firstDay
- *x
) / 7;
237 if(month
< infoPtr
->currentMonth
) {
238 prevMonth
= month
- 1;
242 *x
= (MONTHCAL_MonthLength(prevMonth
, infoPtr
->currentYear
) - firstDay
) % 7;
247 *y
= MONTHCAL_MonthLength(month
, infoPtr
->currentYear
- 1) / 7;
248 *x
= (day
+ firstDay
+ MONTHCAL_MonthLength(month
,
249 infoPtr
->currentYear
)) % 7;
253 /* x: column(day), y: row(week) */
254 static void MONTHCAL_CalcDayRect(const MONTHCAL_INFO
*infoPtr
, RECT
*r
, int x
, int y
)
256 r
->left
= infoPtr
->days
.left
+ x
* infoPtr
->width_increment
;
257 r
->right
= r
->left
+ infoPtr
->width_increment
;
258 r
->top
= infoPtr
->days
.top
+ y
* infoPtr
->height_increment
;
259 r
->bottom
= r
->top
+ infoPtr
->textHeight
;
263 /* sets the RECT struct r to the rectangle around the day and month */
264 /* day is the day value of the month(1 == 1st), month is the month */
265 /* value(january == 1, december == 12) */
266 static inline void MONTHCAL_CalcPosFromDay(const MONTHCAL_INFO
*infoPtr
,
267 int day
, int month
, RECT
*r
)
271 MONTHCAL_CalcDayXY(infoPtr
, day
, month
, &x
, &y
);
272 MONTHCAL_CalcDayRect(infoPtr
, r
, x
, y
);
276 /* day is the day in the month(1 == 1st of the month) */
277 /* month is the month value(1 == january, 12 == december) */
278 static void MONTHCAL_CircleDay(const MONTHCAL_INFO
*infoPtr
, HDC hdc
, int day
, int month
)
280 HPEN hRedPen
= CreatePen(PS_SOLID
, 1, RGB(255, 0, 0));
281 HPEN hOldPen2
= SelectObject(hdc
, hRedPen
);
285 MONTHCAL_CalcPosFromDay(infoPtr
, day
, month
, &day_rect
);
287 hOldBrush
= SelectObject(hdc
, GetStockObject(NULL_BRUSH
));
288 Rectangle(hdc
, day_rect
.left
, day_rect
.top
, day_rect
.right
, day_rect
.bottom
);
290 SelectObject(hdc
, hOldBrush
);
291 DeleteObject(hRedPen
);
292 SelectObject(hdc
, hOldPen2
);
295 static void MONTHCAL_DrawDay(const MONTHCAL_INFO
*infoPtr
, HDC hdc
, int day
, int month
,
296 int x
, int y
, int bold
)
298 static const WCHAR fmtW
[] = { '%','d',0 };
301 static BOOL haveBoldFont
, haveSelectedDay
= FALSE
;
306 wsprintfW(buf
, fmtW
, day
);
308 /* No need to check styles: when selection is not valid, it is set to zero.
309 * 1<day<31, so everything is OK.
312 MONTHCAL_CalcDayRect(infoPtr
, &r
, x
, y
);
314 if((day
>=infoPtr
->minSel
.wDay
) && (day
<=infoPtr
->maxSel
.wDay
)
315 && (month
==infoPtr
->currentMonth
)) {
318 TRACE("%d %d %d\n",day
, infoPtr
->minSel
.wDay
, infoPtr
->maxSel
.wDay
);
319 TRACE("%s\n", wine_dbgstr_rect(&r
));
320 oldCol
= SetTextColor(hdc
, infoPtr
->monthbk
);
321 oldBk
= SetBkColor(hdc
, infoPtr
->trailingtxt
);
322 hbr
= GetSysColorBrush(COLOR_HIGHLIGHT
);
323 FillRect(hdc
, &r
, hbr
);
325 /* FIXME: this may need to be changed now b/c of the other
326 drawing changes 11/3/99 CMM */
327 r2
.left
= r
.left
- 0.25 * infoPtr
->textWidth
;
329 r2
.right
= r
.left
+ 0.5 * infoPtr
->textWidth
;
330 r2
.bottom
= r
.bottom
;
331 if(haveSelectedDay
) FillRect(hdc
, &r2
, hbr
);
332 haveSelectedDay
= TRUE
;
334 haveSelectedDay
= FALSE
;
337 /* need to add some code for multiple selections */
339 if((bold
) &&(!haveBoldFont
)) {
340 SelectObject(hdc
, infoPtr
->hBoldFont
);
343 if((!bold
) &&(haveBoldFont
)) {
344 SelectObject(hdc
, infoPtr
->hFont
);
345 haveBoldFont
= FALSE
;
348 SetBkMode(hdc
,TRANSPARENT
);
349 DrawTextW(hdc
, buf
, -1, &r
, DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
351 if(haveSelectedDay
) {
352 SetTextColor(hdc
, oldCol
);
353 SetBkColor(hdc
, oldBk
);
356 /* draw a rectangle around the currently selected days text */
357 if((day
==infoPtr
->curSelDay
) && (month
==infoPtr
->currentMonth
))
358 DrawFocusRect(hdc
, &r
);
362 static void paint_button (const MONTHCAL_INFO
*infoPtr
, HDC hdc
, BOOL btnNext
,
363 BOOL pressed
, RECT
* r
)
365 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
369 static const int states
[] = {
371 ABS_LEFTNORMAL
, ABS_LEFTPRESSED
, ABS_LEFTDISABLED
,
373 ABS_RIGHTNORMAL
, ABS_RIGHTPRESSED
, ABS_RIGHTDISABLED
375 int stateNum
= btnNext
? 3 : 0;
380 if (infoPtr
->dwStyle
& WS_DISABLED
) stateNum
+= 2;
382 DrawThemeBackground (theme
, hdc
, SBP_ARROWBTN
, states
[stateNum
], r
, NULL
);
386 int style
= btnNext
? DFCS_SCROLLRIGHT
: DFCS_SCROLLLEFT
;
388 style
|= DFCS_PUSHED
;
391 if (infoPtr
->dwStyle
& WS_DISABLED
) style
|= DFCS_INACTIVE
;
394 DrawFrameControl(hdc
, r
, DFC_SCROLL
, style
);
399 static void MONTHCAL_Refresh(MONTHCAL_INFO
*infoPtr
, HDC hdc
, const PAINTSTRUCT
*ps
)
401 static const WCHAR todayW
[] = { 'T','o','d','a','y',':',0 };
402 static const WCHAR fmt1W
[] = { '%','s',' ','%','l','d',0 };
403 static const WCHAR fmt2W
[] = { '%','s',' ','%','s',0 };
404 static const WCHAR fmt3W
[] = { '%','d',0 };
405 RECT
*title
=&infoPtr
->title
;
406 RECT
*prev
=&infoPtr
->titlebtnprev
;
407 RECT
*next
=&infoPtr
->titlebtnnext
;
408 RECT
*titlemonth
=&infoPtr
->titlemonth
;
409 RECT
*titleyear
=&infoPtr
->titleyear
;
413 int i
, j
, m
, mask
, day
, firstDay
, weeknum
, weeknum1
,prevMonth
;
414 int textHeight
= infoPtr
->textHeight
;
421 COLORREF oldTextColor
, oldBkColor
;
423 RECT rcDay
; /* used in MONTHCAL_CalcDayRect() */
424 SYSTEMTIME localtime
;
427 oldTextColor
= SetTextColor(hdc
, comctl32_color
.clrWindowText
);
429 /* fill background */
430 hbr
= CreateSolidBrush (infoPtr
->bk
);
431 FillRect(hdc
, &ps
->rcPaint
, hbr
);
435 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), title
))
437 hbr
= CreateSolidBrush(infoPtr
->titlebk
);
438 FillRect(hdc
, title
, hbr
);
442 /* if the previous button is pressed draw it depressed */
443 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), prev
))
444 paint_button (infoPtr
, hdc
, FALSE
, infoPtr
->status
& MC_PREVPRESSED
, prev
);
446 /* if next button is depressed draw it depressed */
447 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), next
))
448 paint_button (infoPtr
, hdc
, TRUE
, infoPtr
->status
& MC_NEXTPRESSED
, next
);
450 oldBkColor
= SetBkColor(hdc
, infoPtr
->titlebk
);
451 SetTextColor(hdc
, infoPtr
->titletxt
);
452 currentFont
= SelectObject(hdc
, infoPtr
->hBoldFont
);
454 GetLocaleInfoW( LOCALE_USER_DEFAULT
,LOCALE_SMONTHNAME1
+infoPtr
->currentMonth
-1,
456 wsprintfW(buf
, fmt1W
, buf1
, infoPtr
->currentYear
);
458 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), title
))
460 DrawTextW(hdc
, buf
, strlenW(buf
), title
,
461 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
464 /* titlemonth left/right contained rect for whole titletxt('June 1999')
465 * MCM_HitTestInfo wants month & year rects, so prepare these now.
466 *(no, we can't draw them separately; the whole text is centered)
468 GetTextExtentPoint32W(hdc
, buf
, strlenW(buf
), &size
);
469 titlemonth
->left
= title
->right
/ 2 + title
->left
/ 2 - size
.cx
/ 2;
470 titleyear
->right
= title
->right
/ 2 + title
->left
/ 2 + size
.cx
/ 2;
471 GetTextExtentPoint32W(hdc
, buf1
, strlenW(buf1
), &size
);
472 titlemonth
->right
= titlemonth
->left
+ size
.cx
;
473 titleyear
->left
= titlemonth
->right
;
475 /* draw month area */
476 rcTemp
.top
=infoPtr
->wdays
.top
;
477 rcTemp
.left
=infoPtr
->wdays
.left
;
478 rcTemp
.bottom
=infoPtr
->todayrect
.bottom
;
479 rcTemp
.right
=infoPtr
->todayrect
.right
;
480 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcTemp
))
482 hbr
= CreateSolidBrush(infoPtr
->monthbk
);
483 FillRect(hdc
, &rcTemp
, hbr
);
487 /* draw line under day abbreviations */
489 MoveToEx(hdc
, infoPtr
->days
.left
+ 3, title
->bottom
+ textHeight
+ 1, NULL
);
490 LineTo(hdc
, infoPtr
->days
.right
- 3, title
->bottom
+ textHeight
+ 1);
492 prevMonth
= infoPtr
->currentMonth
- 1;
493 if(prevMonth
== 0) /* if currentMonth is january(1) prevMonth is */
494 prevMonth
= 12; /* december(12) of the previous year */
496 infoPtr
->wdays
.left
= infoPtr
->days
.left
= infoPtr
->weeknums
.right
;
497 /* draw day abbreviations */
499 SelectObject(hdc
, infoPtr
->hFont
);
500 SetBkColor(hdc
, infoPtr
->monthbk
);
501 SetTextColor(hdc
, infoPtr
->trailingtxt
);
503 /* copy this rect so we can change the values without changing */
504 /* the original version */
505 days
->left
= infoPtr
->wdays
.left
;
506 days
->right
= days
->left
+ infoPtr
->width_increment
;
507 days
->top
= infoPtr
->wdays
.top
;
508 days
->bottom
= infoPtr
->wdays
.bottom
;
510 i
= infoPtr
->firstDay
;
513 GetLocaleInfoW( LOCALE_USER_DEFAULT
,LOCALE_SABBREVDAYNAME1
+ (i
+j
+6)%7, buf
, countof(buf
));
514 DrawTextW(hdc
, buf
, strlenW(buf
), days
, DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
515 days
->left
+=infoPtr
->width_increment
;
516 days
->right
+=infoPtr
->width_increment
;
519 /* draw day numbers; first, the previous month */
521 firstDay
= MONTHCAL_CalculateDayOfWeek(1, infoPtr
->currentMonth
, infoPtr
->currentYear
);
523 day
= MONTHCAL_MonthLength(prevMonth
, infoPtr
->currentYear
) +
524 (infoPtr
->firstDay
+ 7 - firstDay
)%7 + 1;
525 if (day
> MONTHCAL_MonthLength(prevMonth
, infoPtr
->currentYear
))
527 startofprescal
= day
;
532 while(day
<= MONTHCAL_MonthLength(prevMonth
, infoPtr
->currentYear
)) {
533 MONTHCAL_CalcDayRect(infoPtr
, &rcDay
, i
, 0);
534 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcDay
))
536 MONTHCAL_DrawDay(infoPtr
, hdc
, day
, prevMonth
, i
, 0,
537 infoPtr
->monthdayState
[m
] & mask
);
545 /* draw `current' month */
547 day
= 1; /* start at the beginning of the current month */
549 infoPtr
->firstDayplace
= i
;
550 SetTextColor(hdc
, infoPtr
->txt
);
554 /* draw the first week of the current month */
556 MONTHCAL_CalcDayRect(infoPtr
, &rcDay
, i
, 0);
557 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcDay
))
560 MONTHCAL_DrawDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
, i
, 0,
561 infoPtr
->monthdayState
[m
] & mask
);
563 if((infoPtr
->currentMonth
==infoPtr
->todaysDate
.wMonth
) &&
564 (day
==infoPtr
->todaysDate
.wDay
) &&
565 (infoPtr
->currentYear
== infoPtr
->todaysDate
.wYear
)) {
566 if(!(infoPtr
->dwStyle
& MCS_NOTODAYCIRCLE
))
567 MONTHCAL_CircleDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
);
576 j
= 1; /* move to the 2nd week of the current month */
577 i
= 0; /* move back to sunday */
578 while(day
<= MONTHCAL_MonthLength(infoPtr
->currentMonth
, infoPtr
->currentYear
)) {
579 MONTHCAL_CalcDayRect(infoPtr
, &rcDay
, i
, j
);
580 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcDay
))
582 MONTHCAL_DrawDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
, i
, j
,
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(!(infoPtr
->dwStyle
& MCS_NOTODAYCIRCLE
))
589 MONTHCAL_CircleDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
);
594 if(i
>6) { /* past saturday, goto the next weeks sunday */
600 /* draw `next' month */
602 day
= 1; /* start at the first day of the next month */
606 SetTextColor(hdc
, infoPtr
->trailingtxt
);
607 while((i
<7) &&(j
<6)) {
608 MONTHCAL_CalcDayRect(infoPtr
, &rcDay
, i
, j
);
609 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rcDay
))
611 MONTHCAL_DrawDay(infoPtr
, hdc
, day
, infoPtr
->currentMonth
+ 1, i
, j
,
612 infoPtr
->monthdayState
[m
] & mask
);
618 if(i
==7) { /* past saturday, go to next week's sunday */
623 SetTextColor(hdc
, infoPtr
->txt
);
626 /* draw `today' date if style allows it, and draw a circle before today's
627 * date if necessary */
629 if(!(infoPtr
->dwStyle
& MCS_NOTODAY
)) {
630 if(!(infoPtr
->dwStyle
& MCS_NOTODAYCIRCLE
)) {
631 /*day is the number of days from nextmonth we put on the calendar */
632 MONTHCAL_CircleDay(infoPtr
, hdc
,
633 day
+MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
),
634 infoPtr
->currentMonth
);
636 if (!LoadStringW(COMCTL32_hModule
,IDM_TODAY
,buf1
,countof(buf1
)))
638 WARN("Can't load resource\n");
639 strcpyW(buf1
, todayW
);
641 MONTHCAL_CalcDayRect(infoPtr
, &rtoday
, 1, 6);
642 MONTHCAL_CopyTime(&infoPtr
->todaysDate
,&localtime
);
643 GetDateFormatW(LOCALE_USER_DEFAULT
,DATE_SHORTDATE
,&localtime
,NULL
,buf2
,countof(buf2
));
644 wsprintfW(buf
, fmt2W
, buf1
, buf2
);
645 SelectObject(hdc
, infoPtr
->hBoldFont
);
647 DrawTextW(hdc
, buf
, -1, &rtoday
, DT_CALCRECT
| DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
);
648 if(IntersectRect(&rcTemp
, &(ps
->rcPaint
), &rtoday
))
650 DrawTextW(hdc
, buf
, -1, &rtoday
, DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
);
652 SelectObject(hdc
, infoPtr
->hFont
);
655 /*eventually draw week numbers*/
656 if(infoPtr
->dwStyle
& MCS_WEEKNUMBERS
) {
657 /* display weeknumbers*/
660 /* Rules what week to call the first week of a new year:
661 LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?):
662 The week containing Jan 1 is the first week of year
663 LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany):
664 First week of year must contain 4 days of the new year
665 LOCALE_IFIRSTWEEKOFYEAR == 1 (what contries?)
666 The first week of the year must contain only days of the new year
668 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_IFIRSTWEEKOFYEAR
, buf
, countof(buf
));
669 weeknum
= atoiW(buf
);
680 if (infoPtr
->currentMonth
< 2)
682 /* calculate all those exceptions for january */
683 weeknum1
=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr
->currentYear
);
684 if ((infoPtr
->firstDay
+7 - weeknum1
)%7 > mindays
)
690 weeknum
+=MONTHCAL_MonthLength(i
+1, infoPtr
->currentYear
-1);
691 weeknum
+=startofprescal
+ 7;
693 weeknum1
=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr
->currentYear
-1);
694 if ((infoPtr
->firstDay
+ 7 - weeknum1
)%7 > mindays
)
701 for(i
=0; i
<prevMonth
-1; i
++)
702 weeknum
+=MONTHCAL_MonthLength(i
+1, infoPtr
->currentYear
);
703 weeknum
+=startofprescal
+ 7;
705 weeknum1
=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr
->currentYear
);
706 if ((infoPtr
->firstDay
+ 7 - weeknum1
)%7 > mindays
)
709 days
->left
= infoPtr
->weeknums
.left
;
710 days
->right
= infoPtr
->weeknums
.right
;
711 days
->top
= infoPtr
->weeknums
.top
;
712 days
->bottom
= days
->top
+infoPtr
->height_increment
;
714 if((i
==0)&&(weeknum
>50))
716 wsprintfW(buf
, fmt3W
, weeknum
);
719 else if((i
==5)&&(weeknum
>47))
721 wsprintfW(buf
, fmt3W
, 1);
724 wsprintfW(buf
, fmt3W
, weeknum
+ i
);
725 DrawTextW(hdc
, buf
, -1, days
, DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
);
726 days
->top
+=infoPtr
->height_increment
;
727 days
->bottom
+=infoPtr
->height_increment
;
730 MoveToEx(hdc
, infoPtr
->weeknums
.right
, infoPtr
->weeknums
.top
+ 3 , NULL
);
731 LineTo(hdc
, infoPtr
->weeknums
.right
, infoPtr
->weeknums
.bottom
);
734 /* currentFont was font at entering Refresh */
736 SetBkColor(hdc
, oldBkColor
);
737 SelectObject(hdc
, currentFont
);
738 SetTextColor(hdc
, oldTextColor
);
743 MONTHCAL_GetMinReqRect(const MONTHCAL_INFO
*infoPtr
, LPRECT lpRect
)
745 TRACE("rect %p\n", lpRect
);
747 if(!lpRect
) return FALSE
;
749 lpRect
->left
= infoPtr
->title
.left
;
750 lpRect
->top
= infoPtr
->title
.top
;
751 lpRect
->right
= infoPtr
->title
.right
;
752 lpRect
->bottom
= infoPtr
->todayrect
.bottom
;
753 AdjustWindowRect(lpRect
, infoPtr
->dwStyle
, FALSE
);
755 TRACE("%s\n", wine_dbgstr_rect(lpRect
));
762 MONTHCAL_GetColor(const MONTHCAL_INFO
*infoPtr
, INT index
)
767 case MCSC_BACKGROUND
:
772 return infoPtr
->titlebk
;
774 return infoPtr
->titletxt
;
776 return infoPtr
->monthbk
;
777 case MCSC_TRAILINGTEXT
:
778 return infoPtr
->trailingtxt
;
786 MONTHCAL_SetColor(MONTHCAL_INFO
*infoPtr
, INT index
, COLORREF color
)
790 TRACE("%d: color %08x\n", index
, color
);
793 case MCSC_BACKGROUND
:
799 infoPtr
->txt
= color
;
802 prev
= infoPtr
->titlebk
;
803 infoPtr
->titlebk
= color
;
806 prev
=infoPtr
->titletxt
;
807 infoPtr
->titletxt
= color
;
810 prev
= infoPtr
->monthbk
;
811 infoPtr
->monthbk
= color
;
813 case MCSC_TRAILINGTEXT
:
814 prev
= infoPtr
->trailingtxt
;
815 infoPtr
->trailingtxt
= color
;
819 InvalidateRect(infoPtr
->hwndSelf
, NULL
, index
== MCSC_BACKGROUND
? TRUE
: FALSE
);
825 MONTHCAL_GetMonthDelta(const MONTHCAL_INFO
*infoPtr
)
830 return infoPtr
->delta
;
832 return infoPtr
->visible
;
837 MONTHCAL_SetMonthDelta(MONTHCAL_INFO
*infoPtr
, INT delta
)
839 INT prev
= infoPtr
->delta
;
841 TRACE("delta %d\n", delta
);
843 infoPtr
->delta
= delta
;
849 MONTHCAL_GetFirstDayOfWeek(const MONTHCAL_INFO
*infoPtr
)
851 return MAKELONG(infoPtr
->firstDay
, infoPtr
->firstDayHighWord
);
855 /* sets the first day of the week that will appear in the control */
856 /* 0 == Sunday, 6 == Saturday */
857 /* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
858 /* FIXME: we need more error checking here */
860 MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO
*infoPtr
, INT day
)
862 int prev
= MAKELONG(infoPtr
->firstDay
, infoPtr
->firstDayHighWord
);
866 TRACE("day %d\n", day
);
868 GetLocaleInfoW(LOCALE_USER_DEFAULT
, LOCALE_IFIRSTDAYOFWEEK
, buf
, countof(buf
));
869 TRACE("%s %d\n", debugstr_w(buf
), strlenW(buf
));
871 localFirstDay
= atoiW(buf
);
875 infoPtr
->firstDay
= localFirstDay
;
876 infoPtr
->firstDayHighWord
= FALSE
;
880 infoPtr
->firstDay
= 6; /* max first day allowed */
881 infoPtr
->firstDayHighWord
= TRUE
;
885 infoPtr
->firstDay
= day
;
886 infoPtr
->firstDayHighWord
= TRUE
;
894 MONTHCAL_GetMonthRange(const MONTHCAL_INFO
*infoPtr
)
898 return infoPtr
->monthRange
;
903 MONTHCAL_GetMaxTodayWidth(const MONTHCAL_INFO
*infoPtr
)
905 return(infoPtr
->todayrect
.right
- infoPtr
->todayrect
.left
);
910 MONTHCAL_SetRange(MONTHCAL_INFO
*infoPtr
, SHORT limits
, SYSTEMTIME
*range
)
912 FILETIME ft_min
, ft_max
;
914 TRACE("%x %p\n", limits
, range
);
916 if ((limits
& GDTR_MIN
&& !MONTHCAL_ValidateTime(range
[0])) ||
917 (limits
& GDTR_MAX
&& !MONTHCAL_ValidateTime(range
[1])))
920 if (limits
& GDTR_MIN
)
922 MONTHCAL_CopyTime(&range
[0], &infoPtr
->minDate
);
923 infoPtr
->rangeValid
|= GDTR_MIN
;
925 if (limits
& GDTR_MAX
)
927 MONTHCAL_CopyTime(&range
[1], &infoPtr
->maxDate
);
928 infoPtr
->rangeValid
|= GDTR_MAX
;
931 /* Only one limit set - we are done */
932 if ((infoPtr
->rangeValid
& (GDTR_MIN
| GDTR_MAX
)) != (GDTR_MIN
| GDTR_MAX
))
935 SystemTimeToFileTime(&infoPtr
->maxDate
, &ft_max
);
936 SystemTimeToFileTime(&infoPtr
->minDate
, &ft_min
);
938 if (CompareFileTime(&ft_min
, &ft_max
) > 0)
940 if ((limits
& (GDTR_MIN
| GDTR_MAX
)) == (GDTR_MIN
| GDTR_MAX
))
942 /* Native swaps limits only when both limits are being set. */
943 SYSTEMTIME st_tmp
= infoPtr
->minDate
;
944 infoPtr
->minDate
= infoPtr
->maxDate
;
945 infoPtr
->maxDate
= st_tmp
;
949 /* Reset the other limit. */
950 /* FIXME: native sets date&time to 0. Should we do this too? */
951 infoPtr
->rangeValid
&= limits
& GDTR_MIN
? ~GDTR_MAX
: ~GDTR_MIN
;
960 MONTHCAL_GetRange(const MONTHCAL_INFO
*infoPtr
, SYSTEMTIME
*range
)
962 TRACE("%p\n", range
);
964 if(!range
) return FALSE
;
966 MONTHCAL_CopyTime(&infoPtr
->maxDate
, &range
[1]);
967 MONTHCAL_CopyTime(&infoPtr
->minDate
, &range
[0]);
969 return infoPtr
->rangeValid
;
974 MONTHCAL_SetDayState(const MONTHCAL_INFO
*infoPtr
, INT months
, MONTHDAYSTATE
*states
)
978 TRACE("%d %p\n", months
, states
);
979 if(months
!= infoPtr
->monthRange
) return 0;
981 for(i
= 0; i
< months
; i
++)
982 infoPtr
->monthdayState
[i
] = states
[i
];
988 MONTHCAL_GetCurSel(const MONTHCAL_INFO
*infoPtr
, SYSTEMTIME
*curSel
)
990 TRACE("%p\n", curSel
);
991 if(!curSel
) return FALSE
;
992 if(infoPtr
->dwStyle
& MCS_MULTISELECT
) return FALSE
;
994 MONTHCAL_CopyTime(&infoPtr
->minSel
, curSel
);
995 TRACE("%d/%d/%d\n", curSel
->wYear
, curSel
->wMonth
, curSel
->wDay
);
999 /* FIXME: if the specified date is not visible, make it visible */
1000 /* FIXME: redraw? */
1002 MONTHCAL_SetCurSel(MONTHCAL_INFO
*infoPtr
, SYSTEMTIME
*curSel
)
1004 TRACE("%p\n", curSel
);
1005 if(!curSel
) return FALSE
;
1006 if(infoPtr
->dwStyle
& MCS_MULTISELECT
) return FALSE
;
1008 if(!MONTHCAL_ValidateTime(*curSel
)) return FALSE
;
1010 MONTHCAL_CopyTime(curSel
, &infoPtr
->minSel
);
1011 MONTHCAL_CopyTime(curSel
, &infoPtr
->maxSel
);
1013 /* exit earlier if selection equals current */
1014 if (infoPtr
->currentMonth
== curSel
->wMonth
&&
1015 infoPtr
->currentYear
== curSel
->wYear
&&
1016 infoPtr
->curSelDay
== curSel
->wDay
) return TRUE
;
1018 infoPtr
->currentMonth
= curSel
->wMonth
;
1019 infoPtr
->currentYear
= curSel
->wYear
;
1021 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1028 MONTHCAL_GetMaxSelCount(const MONTHCAL_INFO
*infoPtr
)
1030 return infoPtr
->maxSelCount
;
1035 MONTHCAL_SetMaxSelCount(MONTHCAL_INFO
*infoPtr
, INT max
)
1039 if(infoPtr
->dwStyle
& MCS_MULTISELECT
) {
1040 infoPtr
->maxSelCount
= max
;
1048 MONTHCAL_GetSelRange(const MONTHCAL_INFO
*infoPtr
, SYSTEMTIME
*range
)
1050 TRACE("%p\n", range
);
1052 if(!range
) return FALSE
;
1054 if(infoPtr
->dwStyle
& MCS_MULTISELECT
)
1056 MONTHCAL_CopyTime(&infoPtr
->maxSel
, &range
[1]);
1057 MONTHCAL_CopyTime(&infoPtr
->minSel
, &range
[0]);
1058 TRACE("[min,max]=[%d %d]\n", infoPtr
->minSel
.wDay
, infoPtr
->maxSel
.wDay
);
1067 MONTHCAL_SetSelRange(MONTHCAL_INFO
*infoPtr
, SYSTEMTIME
*range
)
1069 TRACE("%p\n", range
);
1071 if(!range
) return FALSE
;
1073 if(infoPtr
->dwStyle
& MCS_MULTISELECT
)
1075 MONTHCAL_CopyTime(&range
[1], &infoPtr
->maxSel
);
1076 MONTHCAL_CopyTime(&range
[0], &infoPtr
->minSel
);
1077 TRACE("[min,max]=[%d %d]\n", infoPtr
->minSel
.wDay
, infoPtr
->maxSel
.wDay
);
1086 MONTHCAL_GetToday(const MONTHCAL_INFO
*infoPtr
, SYSTEMTIME
*today
)
1088 TRACE("%p\n", today
);
1090 if(!today
) return FALSE
;
1091 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, today
);
1097 MONTHCAL_SetToday(MONTHCAL_INFO
*infoPtr
, SYSTEMTIME
*today
)
1099 TRACE("%p\n", today
);
1101 if(!today
) return FALSE
;
1103 if(MONTHCAL_IsDateEqual(today
, &infoPtr
->todaysDate
)) return TRUE
;
1105 MONTHCAL_CopyTime(today
, &infoPtr
->todaysDate
);
1106 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1112 MONTHCAL_HitTest(const MONTHCAL_INFO
*infoPtr
, MCHITTESTINFO
*lpht
)
1122 ZeroMemory(&lpht
->st
, sizeof(lpht
->st
));
1124 /* Comment in for debugging...
1125 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,
1126 infoPtr->wdays.left, infoPtr->wdays.right,
1127 infoPtr->wdays.top, infoPtr->wdays.bottom,
1128 infoPtr->days.left, infoPtr->days.right,
1129 infoPtr->days.top, infoPtr->days.bottom,
1130 infoPtr->todayrect.left, infoPtr->todayrect.right,
1131 infoPtr->todayrect.top, infoPtr->todayrect.bottom,
1132 infoPtr->weeknums.left, infoPtr->weeknums.right,
1133 infoPtr->weeknums.top, infoPtr->weeknums.bottom);
1136 /* are we in the header? */
1138 if(PtInRect(&infoPtr
->title
, lpht
->pt
)) {
1139 if(PtInRect(&infoPtr
->titlebtnprev
, lpht
->pt
)) {
1140 retval
= MCHT_TITLEBTNPREV
;
1143 if(PtInRect(&infoPtr
->titlebtnnext
, lpht
->pt
)) {
1144 retval
= MCHT_TITLEBTNNEXT
;
1147 if(PtInRect(&infoPtr
->titlemonth
, lpht
->pt
)) {
1148 retval
= MCHT_TITLEMONTH
;
1151 if(PtInRect(&infoPtr
->titleyear
, lpht
->pt
)) {
1152 retval
= MCHT_TITLEYEAR
;
1156 retval
= MCHT_TITLE
;
1160 day
= MONTHCAL_CalcDayFromPos(infoPtr
,x
,y
,&wday
,&wnum
);
1161 if(PtInRect(&infoPtr
->wdays
, lpht
->pt
)) {
1162 retval
= MCHT_CALENDARDAY
;
1163 lpht
->st
.wYear
= infoPtr
->currentYear
;
1164 lpht
->st
.wMonth
= (day
< 1)? infoPtr
->currentMonth
-1 : infoPtr
->currentMonth
;
1165 lpht
->st
.wDay
= (day
< 1)?
1166 MONTHCAL_MonthLength(infoPtr
->currentMonth
-1,infoPtr
->currentYear
) -day
: day
;
1169 if(PtInRect(&infoPtr
->weeknums
, lpht
->pt
)) {
1170 retval
= MCHT_CALENDARWEEKNUM
;
1171 lpht
->st
.wYear
= infoPtr
->currentYear
;
1172 lpht
->st
.wMonth
= (day
< 1) ? infoPtr
->currentMonth
-1 :
1173 (day
> MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
)) ?
1174 infoPtr
->currentMonth
+1 :infoPtr
->currentMonth
;
1175 lpht
->st
.wDay
= (day
< 1 ) ?
1176 MONTHCAL_MonthLength(infoPtr
->currentMonth
-1,infoPtr
->currentYear
) -day
:
1177 (day
> MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
)) ?
1178 day
- MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
) : day
;
1181 if(PtInRect(&infoPtr
->days
, lpht
->pt
))
1183 lpht
->st
.wYear
= infoPtr
->currentYear
;
1186 retval
= MCHT_CALENDARDATEPREV
;
1187 lpht
->st
.wMonth
= infoPtr
->currentMonth
- 1;
1188 if (lpht
->st
.wMonth
<1)
1190 lpht
->st
.wMonth
= 12;
1193 lpht
->st
.wDay
= MONTHCAL_MonthLength(lpht
->st
.wMonth
,lpht
->st
.wYear
) -day
;
1195 else if (day
> MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
))
1197 retval
= MCHT_CALENDARDATENEXT
;
1198 lpht
->st
.wMonth
= infoPtr
->currentMonth
+ 1;
1199 if (lpht
->st
.wMonth
<12)
1201 lpht
->st
.wMonth
= 1;
1204 lpht
->st
.wDay
= day
- MONTHCAL_MonthLength(infoPtr
->currentMonth
,infoPtr
->currentYear
) ;
1207 retval
= MCHT_CALENDARDATE
;
1208 lpht
->st
.wMonth
= infoPtr
->currentMonth
;
1209 lpht
->st
.wDay
= day
;
1210 lpht
->st
.wDayOfWeek
= MONTHCAL_CalculateDayOfWeek(day
,lpht
->st
.wMonth
,lpht
->st
.wYear
);
1214 if(PtInRect(&infoPtr
->todayrect
, lpht
->pt
)) {
1215 retval
= MCHT_TODAYLINK
;
1220 /* Hit nothing special? What's left must be background :-) */
1222 retval
= MCHT_CALENDARBK
;
1224 lpht
->uHit
= retval
;
1229 static void MONTHCAL_GoToNextMonth(MONTHCAL_INFO
*infoPtr
)
1231 TRACE("MONTHCAL_GoToNextMonth\n");
1233 infoPtr
->currentMonth
++;
1234 if(infoPtr
->currentMonth
> 12) {
1235 infoPtr
->currentYear
++;
1236 infoPtr
->currentMonth
= 1;
1239 if(infoPtr
->dwStyle
& MCS_DAYSTATE
) {
1243 nmds
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1244 nmds
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1245 nmds
.nmhdr
.code
= MCN_GETDAYSTATE
;
1246 nmds
.cDayState
= infoPtr
->monthRange
;
1247 nmds
.prgDayState
= Alloc(infoPtr
->monthRange
* sizeof(MONTHDAYSTATE
));
1249 nmds
.stStart
= infoPtr
->todaysDate
;
1250 nmds
.stStart
.wYear
= infoPtr
->currentYear
;
1251 nmds
.stStart
.wMonth
= infoPtr
->currentMonth
;
1252 nmds
.stStart
.wDay
= 1;
1254 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmds
.nmhdr
.idFrom
, (LPARAM
)&nmds
);
1255 for(i
=0; i
<infoPtr
->monthRange
; i
++)
1256 infoPtr
->monthdayState
[i
] = nmds
.prgDayState
[i
];
1261 static void MONTHCAL_GoToPrevMonth(MONTHCAL_INFO
*infoPtr
)
1265 infoPtr
->currentMonth
--;
1266 if(infoPtr
->currentMonth
< 1) {
1267 infoPtr
->currentYear
--;
1268 infoPtr
->currentMonth
= 12;
1271 if(infoPtr
->dwStyle
& MCS_DAYSTATE
) {
1275 nmds
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1276 nmds
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1277 nmds
.nmhdr
.code
= MCN_GETDAYSTATE
;
1278 nmds
.cDayState
= infoPtr
->monthRange
;
1279 nmds
.prgDayState
= Alloc
1280 (infoPtr
->monthRange
* sizeof(MONTHDAYSTATE
));
1282 nmds
.stStart
= infoPtr
->todaysDate
;
1283 nmds
.stStart
.wYear
= infoPtr
->currentYear
;
1284 nmds
.stStart
.wMonth
= infoPtr
->currentMonth
;
1285 nmds
.stStart
.wDay
= 1;
1287 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmds
.nmhdr
.idFrom
, (LPARAM
)&nmds
);
1288 for(i
=0; i
<infoPtr
->monthRange
; i
++)
1289 infoPtr
->monthdayState
[i
] = nmds
.prgDayState
[i
];
1294 MONTHCAL_RButtonDown(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1296 static const WCHAR todayW
[] = { 'G','o',' ','t','o',' ','T','o','d','a','y',':',0 };
1301 hMenu
= CreatePopupMenu();
1302 if (!LoadStringW(COMCTL32_hModule
,IDM_GOTODAY
,buf
,countof(buf
)))
1304 WARN("Can't load resource\n");
1305 strcpyW(buf
, todayW
);
1307 AppendMenuW(hMenu
, MF_STRING
|MF_ENABLED
,1, buf
);
1308 menupoint
.x
=(short)LOWORD(lParam
);
1309 menupoint
.y
=(short)HIWORD(lParam
);
1310 ClientToScreen(infoPtr
->hwndSelf
, &menupoint
);
1311 if( TrackPopupMenu(hMenu
,TPM_RIGHTBUTTON
| TPM_NONOTIFY
|TPM_RETURNCMD
,
1312 menupoint
.x
, menupoint
.y
, 0, infoPtr
->hwndSelf
, NULL
))
1314 infoPtr
->currentMonth
=infoPtr
->todaysDate
.wMonth
;
1315 infoPtr
->currentYear
=infoPtr
->todaysDate
.wYear
;
1316 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1322 MONTHCAL_LButtonDown(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1324 static const WCHAR EditW
[] = { 'E','D','I','T',0 };
1328 RECT rcDay
; /* used in determining area to invalidate */
1333 TRACE("%lx\n", lParam
);
1335 if (infoPtr
->hWndYearUpDown
)
1337 infoPtr
->currentYear
=SendMessageW(infoPtr
->hWndYearUpDown
, UDM_SETPOS
, 0, 0);
1338 if(!DestroyWindow(infoPtr
->hWndYearUpDown
))
1340 FIXME("Can't destroy Updown Control\n");
1343 infoPtr
->hWndYearUpDown
=0;
1344 if(!DestroyWindow(infoPtr
->hWndYearEdit
))
1346 FIXME("Can't destroy Updown Control\n");
1349 infoPtr
->hWndYearEdit
=0;
1350 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1353 ht
.pt
.x
= (short)LOWORD(lParam
);
1354 ht
.pt
.y
= (short)HIWORD(lParam
);
1355 hit
= MONTHCAL_HitTest(infoPtr
, &ht
);
1357 /* FIXME: these flags should be checked by */
1358 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1360 if(hit
==MCHT_TITLEBTNNEXT
) {
1361 MONTHCAL_GoToNextMonth(infoPtr
);
1362 infoPtr
->status
= MC_NEXTPRESSED
;
1363 SetTimer(infoPtr
->hwndSelf
, MC_NEXTMONTHTIMER
, MC_NEXTMONTHDELAY
, 0);
1364 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1367 if(hit
== MCHT_TITLEBTNPREV
){
1368 MONTHCAL_GoToPrevMonth(infoPtr
);
1369 infoPtr
->status
= MC_PREVPRESSED
;
1370 SetTimer(infoPtr
->hwndSelf
, MC_PREVMONTHTIMER
, MC_NEXTMONTHDELAY
, 0);
1371 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1375 if(hit
== MCHT_TITLEMONTH
) {
1376 hMenu
= CreatePopupMenu();
1380 GetLocaleInfoW(LOCALE_USER_DEFAULT
,LOCALE_SMONTHNAME1
+i
, buf
,countof(buf
));
1381 AppendMenuW(hMenu
, MF_STRING
|MF_ENABLED
,i
+1, buf
);
1383 menupoint
.x
=infoPtr
->titlemonth
.right
;
1384 menupoint
.y
=infoPtr
->titlemonth
.bottom
;
1385 ClientToScreen(infoPtr
->hwndSelf
, &menupoint
);
1386 i
= TrackPopupMenu(hMenu
,TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
| TPM_RETURNCMD
,
1387 menupoint
.x
, menupoint
.y
, 0, infoPtr
->hwndSelf
, NULL
);
1388 if ((i
>0) && (i
<13))
1390 infoPtr
->currentMonth
=i
;
1391 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1394 if(hit
== MCHT_TITLEYEAR
) {
1395 infoPtr
->hWndYearEdit
=CreateWindowExW(0,
1398 WS_VISIBLE
| WS_CHILD
|UDS_SETBUDDYINT
,
1399 infoPtr
->titleyear
.left
+3,infoPtr
->titlebtnnext
.top
,
1400 infoPtr
->titleyear
.right
-infoPtr
->titleyear
.left
+4,
1401 infoPtr
->textHeight
,
1406 SendMessageW( infoPtr
->hWndYearEdit
, WM_SETFONT
, (WPARAM
) infoPtr
->hBoldFont
, (LPARAM
)TRUE
);
1407 infoPtr
->hWndYearUpDown
=CreateWindowExW(0,
1410 WS_VISIBLE
| WS_CHILD
|UDS_SETBUDDYINT
|UDS_NOTHOUSANDS
|UDS_ARROWKEYS
,
1411 infoPtr
->titleyear
.right
+7,infoPtr
->titlebtnnext
.top
,
1413 infoPtr
->textHeight
,
1418 SendMessageW(infoPtr
->hWndYearUpDown
, UDM_SETRANGE
, 0, MAKELONG (9999, 1753));
1419 SendMessageW(infoPtr
->hWndYearUpDown
, UDM_SETBUDDY
, (WPARAM
) infoPtr
->hWndYearEdit
, 0);
1420 SendMessageW(infoPtr
->hWndYearUpDown
, UDM_SETPOS
, 0, infoPtr
->currentYear
);
1424 if(hit
== MCHT_TODAYLINK
) {
1427 infoPtr
->curSelDay
= infoPtr
->todaysDate
.wDay
;
1428 infoPtr
->firstSelDay
= infoPtr
->todaysDate
.wDay
;
1429 infoPtr
->currentMonth
=infoPtr
->todaysDate
.wMonth
;
1430 infoPtr
->currentYear
=infoPtr
->todaysDate
.wYear
;
1431 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, &infoPtr
->minSel
);
1432 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, &infoPtr
->maxSel
);
1433 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1435 nmsc
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1436 nmsc
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1437 nmsc
.nmhdr
.code
= MCN_SELCHANGE
;
1438 MONTHCAL_CopyTime(&infoPtr
->minSel
, &nmsc
.stSelStart
);
1439 MONTHCAL_CopyTime(&infoPtr
->maxSel
, &nmsc
.stSelEnd
);
1440 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmsc
.nmhdr
.idFrom
, (LPARAM
)&nmsc
);
1442 nmsc
.nmhdr
.code
= MCN_SELECT
;
1443 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmsc
.nmhdr
.idFrom
, (LPARAM
)&nmsc
);
1446 if(hit
== MCHT_CALENDARDATE
) {
1447 SYSTEMTIME selArray
[2];
1450 MONTHCAL_CopyTime(&ht
.st
, &selArray
[0]);
1451 MONTHCAL_CopyTime(&ht
.st
, &selArray
[1]);
1452 MONTHCAL_SetSelRange(infoPtr
, selArray
);
1453 MONTHCAL_SetCurSel(infoPtr
, &selArray
[0]);
1454 TRACE("MCHT_CALENDARDATE\n");
1455 nmsc
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1456 nmsc
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1457 nmsc
.nmhdr
.code
= MCN_SELCHANGE
;
1458 MONTHCAL_CopyTime(&infoPtr
->minSel
,&nmsc
.stSelStart
);
1459 MONTHCAL_CopyTime(&infoPtr
->maxSel
,&nmsc
.stSelEnd
);
1461 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmsc
.nmhdr
.idFrom
, (LPARAM
)&nmsc
);
1464 /* redraw both old and new days if the selected day changed */
1465 if(infoPtr
->curSelDay
!= ht
.st
.wDay
) {
1466 MONTHCAL_CalcPosFromDay(infoPtr
, ht
.st
.wDay
, ht
.st
.wMonth
, &rcDay
);
1467 InvalidateRect(infoPtr
->hwndSelf
, &rcDay
, TRUE
);
1469 MONTHCAL_CalcPosFromDay(infoPtr
, infoPtr
->curSelDay
, infoPtr
->currentMonth
, &rcDay
);
1470 InvalidateRect(infoPtr
->hwndSelf
, &rcDay
, TRUE
);
1473 infoPtr
->firstSelDay
= ht
.st
.wDay
;
1474 infoPtr
->curSelDay
= ht
.st
.wDay
;
1475 infoPtr
->status
= MC_SEL_LBUTDOWN
;
1484 MONTHCAL_LButtonUp(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1488 BOOL redraw
= FALSE
;
1494 if(infoPtr
->status
& MC_NEXTPRESSED
) {
1495 KillTimer(infoPtr
->hwndSelf
, MC_NEXTMONTHTIMER
);
1496 infoPtr
->status
&= ~MC_NEXTPRESSED
;
1499 if(infoPtr
->status
& MC_PREVPRESSED
) {
1500 KillTimer(infoPtr
->hwndSelf
, MC_PREVMONTHTIMER
);
1501 infoPtr
->status
&= ~MC_PREVPRESSED
;
1505 ht
.pt
.x
= (short)LOWORD(lParam
);
1506 ht
.pt
.y
= (short)HIWORD(lParam
);
1507 hit
= MONTHCAL_HitTest(infoPtr
, &ht
);
1509 infoPtr
->status
= MC_SEL_LBUTUP
;
1511 if(hit
==MCHT_CALENDARDATENEXT
) {
1512 MONTHCAL_GoToNextMonth(infoPtr
);
1513 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1516 if(hit
== MCHT_CALENDARDATEPREV
){
1517 MONTHCAL_GoToPrevMonth(infoPtr
);
1518 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1521 nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1522 nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1523 nmhdr
.code
= NM_RELEASEDCAPTURE
;
1524 TRACE("Sent notification from %p to %p\n", infoPtr
->hwndSelf
, infoPtr
->hwndNotify
);
1526 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmhdr
.idFrom
, (LPARAM
)&nmhdr
);
1527 /* redraw if necessary */
1529 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1530 /* only send MCN_SELECT if currently displayed month's day was selected */
1531 if(hit
== MCHT_CALENDARDATE
) {
1532 nmsc
.nmhdr
.hwndFrom
= infoPtr
->hwndSelf
;
1533 nmsc
.nmhdr
.idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1534 nmsc
.nmhdr
.code
= MCN_SELECT
;
1535 MONTHCAL_CopyTime(&infoPtr
->minSel
, &nmsc
.stSelStart
);
1536 MONTHCAL_CopyTime(&infoPtr
->maxSel
, &nmsc
.stSelEnd
);
1538 SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, nmsc
.nmhdr
.idFrom
, (LPARAM
)&nmsc
);
1546 MONTHCAL_Timer(MONTHCAL_INFO
*infoPtr
, WPARAM wParam
)
1548 BOOL redraw
= FALSE
;
1550 TRACE("%ld\n", wParam
);
1553 case MC_NEXTMONTHTIMER
:
1555 MONTHCAL_GoToNextMonth(infoPtr
);
1557 case MC_PREVMONTHTIMER
:
1559 MONTHCAL_GoToPrevMonth(infoPtr
);
1562 ERR("got unknown timer\n");
1566 /* redraw only if necessary */
1568 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1575 MONTHCAL_MouseMove(MONTHCAL_INFO
*infoPtr
, LPARAM lParam
)
1578 int oldselday
, selday
, hit
;
1581 if(!(infoPtr
->status
& MC_SEL_LBUTDOWN
)) return 0;
1583 ht
.pt
.x
= (short)LOWORD(lParam
);
1584 ht
.pt
.y
= (short)HIWORD(lParam
);
1586 hit
= MONTHCAL_HitTest(infoPtr
, &ht
);
1588 /* not on the calendar date numbers? bail out */
1589 TRACE("hit:%x\n",hit
);
1590 if((hit
& MCHT_CALENDARDATE
) != MCHT_CALENDARDATE
) return 0;
1592 selday
= ht
.st
.wDay
;
1593 oldselday
= infoPtr
->curSelDay
;
1594 infoPtr
->curSelDay
= selday
;
1595 MONTHCAL_CalcPosFromDay(infoPtr
, selday
, ht
.st
. wMonth
, &r
);
1597 if(infoPtr
->dwStyle
& MCS_MULTISELECT
) {
1598 SYSTEMTIME selArray
[2];
1601 MONTHCAL_GetSelRange(infoPtr
, selArray
);
1603 if(infoPtr
->firstSelDay
==selArray
[0].wDay
) i
=1;
1604 TRACE("oldRange:%d %d %d %d\n", infoPtr
->firstSelDay
, selArray
[0].wDay
, selArray
[1].wDay
, i
);
1605 if(infoPtr
->firstSelDay
==selArray
[1].wDay
) {
1606 /* 1st time we get here: selArray[0]=selArray[1]) */
1607 /* if we're still at the first selected date, return */
1608 if(infoPtr
->firstSelDay
==selday
) goto done
;
1609 if(selday
<infoPtr
->firstSelDay
) i
= 0;
1612 if(abs(infoPtr
->firstSelDay
- selday
) >= infoPtr
->maxSelCount
) {
1613 if(selday
>infoPtr
->firstSelDay
)
1614 selday
= infoPtr
->firstSelDay
+ infoPtr
->maxSelCount
;
1616 selday
= infoPtr
->firstSelDay
- infoPtr
->maxSelCount
;
1619 if(selArray
[i
].wDay
!=selday
) {
1620 TRACE("newRange:%d %d %d %d\n", infoPtr
->firstSelDay
, selArray
[0].wDay
, selArray
[1].wDay
, i
);
1622 selArray
[i
].wDay
= selday
;
1624 if(selArray
[0].wDay
>selArray
[1].wDay
) {
1626 tempday
= selArray
[1].wDay
;
1627 selArray
[1].wDay
= selArray
[0].wDay
;
1628 selArray
[0].wDay
= tempday
;
1631 MONTHCAL_SetSelRange(infoPtr
, selArray
);
1637 /* only redraw if the currently selected day changed */
1638 /* FIXME: this should specify a rectangle containing only the days that changed */
1639 /* using InvalidateRect */
1640 if(oldselday
!= infoPtr
->curSelDay
)
1641 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1648 MONTHCAL_Paint(MONTHCAL_INFO
*infoPtr
, HDC hdc_paint
)
1655 GetClientRect(infoPtr
->hwndSelf
, &ps
.rcPaint
);
1659 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
1661 MONTHCAL_Refresh(infoPtr
, hdc
, &ps
);
1662 if (!hdc_paint
) EndPaint(infoPtr
->hwndSelf
, &ps
);
1668 MONTHCAL_KillFocus(const MONTHCAL_INFO
*infoPtr
, HWND hFocusWnd
)
1672 if (infoPtr
->hwndNotify
!= hFocusWnd
)
1673 ShowWindow(infoPtr
->hwndSelf
, SW_HIDE
);
1675 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
1682 MONTHCAL_SetFocus(const MONTHCAL_INFO
*infoPtr
)
1686 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1691 /* sets the size information */
1692 static void MONTHCAL_UpdateSize(MONTHCAL_INFO
*infoPtr
)
1694 static const WCHAR SunW
[] = { 'S','u','n',0 };
1695 static const WCHAR O0W
[] = { '0','0',0 };
1696 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
1697 RECT
*title
=&infoPtr
->title
;
1698 RECT
*prev
=&infoPtr
->titlebtnprev
;
1699 RECT
*next
=&infoPtr
->titlebtnnext
;
1700 RECT
*titlemonth
=&infoPtr
->titlemonth
;
1701 RECT
*titleyear
=&infoPtr
->titleyear
;
1702 RECT
*wdays
=&infoPtr
->wdays
;
1703 RECT
*weeknumrect
=&infoPtr
->weeknums
;
1704 RECT
*days
=&infoPtr
->days
;
1705 RECT
*todayrect
=&infoPtr
->todayrect
;
1709 int xdiv
, left_offset
;
1712 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
1714 currentFont
= SelectObject(hdc
, infoPtr
->hFont
);
1716 /* get the height and width of each day's text */
1717 GetTextMetricsW(hdc
, &tm
);
1718 infoPtr
->textHeight
= tm
.tmHeight
+ tm
.tmExternalLeading
+ tm
.tmInternalLeading
;
1719 GetTextExtentPoint32W(hdc
, SunW
, 3, &size
);
1720 infoPtr
->textWidth
= size
.cx
+ 2;
1722 /* recalculate the height and width increments and offsets */
1723 GetTextExtentPoint32W(hdc
, O0W
, 2, &size
);
1725 xdiv
= (infoPtr
->dwStyle
& MCS_WEEKNUMBERS
) ? 8 : 7;
1727 infoPtr
->width_increment
= size
.cx
* 2 + 4;
1728 infoPtr
->height_increment
= infoPtr
->textHeight
;
1729 left_offset
= (rcClient
.right
- rcClient
.left
) - (infoPtr
->width_increment
* xdiv
);
1731 /* calculate title area */
1732 title
->top
= rcClient
.top
;
1733 title
->bottom
= title
->top
+ 3 * infoPtr
->height_increment
/ 2;
1734 title
->left
= left_offset
;
1735 title
->right
= rcClient
.right
;
1737 /* set the dimensions of the next and previous buttons and center */
1738 /* the month text vertically */
1739 prev
->top
= next
->top
= title
->top
+ 4;
1740 prev
->bottom
= next
->bottom
= title
->bottom
- 4;
1741 prev
->left
= title
->left
+ 4;
1742 prev
->right
= prev
->left
+ (title
->bottom
- title
->top
) ;
1743 next
->right
= title
->right
- 4;
1744 next
->left
= next
->right
- (title
->bottom
- title
->top
);
1746 /* titlemonth->left and right change based upon the current month */
1747 /* and are recalculated in refresh as the current month may change */
1748 /* without the control being resized */
1749 titlemonth
->top
= titleyear
->top
= title
->top
+ (infoPtr
->height_increment
)/2;
1750 titlemonth
->bottom
= titleyear
->bottom
= title
->bottom
- (infoPtr
->height_increment
)/2;
1752 /* setup the dimensions of the rectangle we draw the names of the */
1753 /* days of the week in */
1754 weeknumrect
->left
= left_offset
;
1755 if(infoPtr
->dwStyle
& MCS_WEEKNUMBERS
)
1756 weeknumrect
->right
=prev
->right
;
1758 weeknumrect
->right
=weeknumrect
->left
;
1759 wdays
->left
= days
->left
= weeknumrect
->right
;
1760 wdays
->right
= days
->right
= wdays
->left
+ 7 * infoPtr
->width_increment
;
1761 wdays
->top
= title
->bottom
;
1762 wdays
->bottom
= wdays
->top
+ infoPtr
->height_increment
;
1764 days
->top
= weeknumrect
->top
= wdays
->bottom
;
1765 days
->bottom
= weeknumrect
->bottom
= days
->top
+ 6 * infoPtr
->height_increment
;
1767 todayrect
->left
= rcClient
.left
;
1768 todayrect
->right
= rcClient
.right
;
1769 todayrect
->top
= days
->bottom
;
1770 todayrect
->bottom
= days
->bottom
+ infoPtr
->height_increment
;
1772 TRACE("dx=%d dy=%d client[%s] title[%s] wdays[%s] days[%s] today[%s]\n",
1773 infoPtr
->width_increment
,infoPtr
->height_increment
,
1774 wine_dbgstr_rect(&rcClient
),
1775 wine_dbgstr_rect(title
),
1776 wine_dbgstr_rect(wdays
),
1777 wine_dbgstr_rect(days
),
1778 wine_dbgstr_rect(todayrect
));
1780 /* restore the originally selected font */
1781 SelectObject(hdc
, currentFont
);
1783 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1786 static LRESULT
MONTHCAL_Size(MONTHCAL_INFO
*infoPtr
, int Width
, int Height
)
1788 TRACE("(width=%d, height=%d)\n", Width
, Height
);
1790 MONTHCAL_UpdateSize(infoPtr
);
1792 /* invalidate client area and erase background */
1793 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
1798 static LRESULT
MONTHCAL_GetFont(const MONTHCAL_INFO
*infoPtr
)
1800 return (LRESULT
)infoPtr
->hFont
;
1803 static LRESULT
MONTHCAL_SetFont(MONTHCAL_INFO
*infoPtr
, HFONT hFont
, BOOL redraw
)
1808 if (!hFont
) return 0;
1810 hOldFont
= infoPtr
->hFont
;
1811 infoPtr
->hFont
= hFont
;
1813 GetObjectW(infoPtr
->hFont
, sizeof(lf
), &lf
);
1814 lf
.lfWeight
= FW_BOLD
;
1815 infoPtr
->hBoldFont
= CreateFontIndirectW(&lf
);
1817 MONTHCAL_UpdateSize(infoPtr
);
1820 InvalidateRect(infoPtr
->hwndSelf
, NULL
, FALSE
);
1822 return (LRESULT
)hOldFont
;
1825 /* update theme after a WM_THEMECHANGED message */
1826 static LRESULT
theme_changed (const MONTHCAL_INFO
* infoPtr
)
1828 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
1829 CloseThemeData (theme
);
1830 OpenThemeData (infoPtr
->hwndSelf
, themeClass
);
1834 static INT
MONTHCAL_StyleChanged(MONTHCAL_INFO
*infoPtr
, WPARAM wStyleType
,
1835 const STYLESTRUCT
*lpss
)
1837 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
1838 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
1840 if (wStyleType
!= GWL_STYLE
) return 0;
1842 infoPtr
->dwStyle
= lpss
->styleNew
;
1847 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1849 MONTHCAL_Create(HWND hwnd
, LPCREATESTRUCTW lpcs
)
1851 MONTHCAL_INFO
*infoPtr
;
1853 /* allocate memory for info structure */
1854 infoPtr
= Alloc(sizeof(MONTHCAL_INFO
));
1855 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
1857 if(infoPtr
== NULL
) {
1858 ERR( "could not allocate info memory!\n");
1862 infoPtr
->hwndSelf
= hwnd
;
1863 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
1864 infoPtr
->dwStyle
= GetWindowLongW(hwnd
, GWL_STYLE
);
1866 MONTHCAL_SetFont(infoPtr
, GetStockObject(DEFAULT_GUI_FONT
), FALSE
);
1868 /* initialize info structure */
1869 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1871 GetLocalTime(&infoPtr
->todaysDate
);
1872 infoPtr
->firstDayHighWord
= FALSE
;
1873 MONTHCAL_SetFirstDayOfWeek(infoPtr
, -1);
1874 infoPtr
->currentMonth
= infoPtr
->todaysDate
.wMonth
;
1875 infoPtr
->currentYear
= infoPtr
->todaysDate
.wYear
;
1876 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, &infoPtr
->minDate
);
1877 MONTHCAL_CopyTime(&infoPtr
->todaysDate
, &infoPtr
->maxDate
);
1878 infoPtr
->maxDate
.wYear
=2050;
1879 infoPtr
->minDate
.wYear
=1950;
1880 infoPtr
->maxSelCount
= 7;
1881 infoPtr
->monthRange
= 3;
1882 infoPtr
->monthdayState
= Alloc
1883 (infoPtr
->monthRange
* sizeof(MONTHDAYSTATE
));
1884 infoPtr
->titlebk
= comctl32_color
.clrActiveCaption
;
1885 infoPtr
->titletxt
= comctl32_color
.clrWindow
;
1886 infoPtr
->monthbk
= comctl32_color
.clrWindow
;
1887 infoPtr
->trailingtxt
= comctl32_color
.clrGrayText
;
1888 infoPtr
->bk
= comctl32_color
.clrWindow
;
1889 infoPtr
->txt
= comctl32_color
.clrWindowText
;
1891 /* set the current day for highlighing */
1892 infoPtr
->minSel
.wDay
= infoPtr
->todaysDate
.wDay
;
1893 infoPtr
->maxSel
.wDay
= infoPtr
->todaysDate
.wDay
;
1895 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1896 /* of the control */
1897 MONTHCAL_UpdateSize(infoPtr
);
1899 OpenThemeData (infoPtr
->hwndSelf
, themeClass
);
1906 MONTHCAL_Destroy(MONTHCAL_INFO
*infoPtr
)
1908 /* free month calendar info data */
1909 Free(infoPtr
->monthdayState
);
1910 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
1912 CloseThemeData (GetWindowTheme (infoPtr
->hwndSelf
));
1919 static LRESULT WINAPI
1920 MONTHCAL_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1922 MONTHCAL_INFO
*infoPtr
;
1924 TRACE("hwnd=%p msg=%x wparam=%lx lparam=%lx\n", hwnd
, uMsg
, wParam
, lParam
);
1926 infoPtr
= MONTHCAL_GetInfoPtr(hwnd
);
1927 if (!infoPtr
&& (uMsg
!= WM_CREATE
))
1928 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
1932 return MONTHCAL_GetCurSel(infoPtr
, (LPSYSTEMTIME
)lParam
);
1935 return MONTHCAL_SetCurSel(infoPtr
, (LPSYSTEMTIME
)lParam
);
1937 case MCM_GETMAXSELCOUNT
:
1938 return MONTHCAL_GetMaxSelCount(infoPtr
);
1940 case MCM_SETMAXSELCOUNT
:
1941 return MONTHCAL_SetMaxSelCount(infoPtr
, wParam
);
1943 case MCM_GETSELRANGE
:
1944 return MONTHCAL_GetSelRange(infoPtr
, (LPSYSTEMTIME
)lParam
);
1946 case MCM_SETSELRANGE
:
1947 return MONTHCAL_SetSelRange(infoPtr
, (LPSYSTEMTIME
)lParam
);
1949 case MCM_GETMONTHRANGE
:
1950 return MONTHCAL_GetMonthRange(infoPtr
);
1952 case MCM_SETDAYSTATE
:
1953 return MONTHCAL_SetDayState(infoPtr
, (INT
)wParam
, (LPMONTHDAYSTATE
)lParam
);
1955 case MCM_GETMINREQRECT
:
1956 return MONTHCAL_GetMinReqRect(infoPtr
, (LPRECT
)lParam
);
1959 return MONTHCAL_GetColor(infoPtr
, wParam
);
1962 return MONTHCAL_SetColor(infoPtr
, wParam
, (COLORREF
)lParam
);
1965 return MONTHCAL_GetToday(infoPtr
, (LPSYSTEMTIME
)lParam
);
1968 return MONTHCAL_SetToday(infoPtr
, (LPSYSTEMTIME
)lParam
);
1971 return MONTHCAL_HitTest(infoPtr
, (PMCHITTESTINFO
)lParam
);
1973 case MCM_GETFIRSTDAYOFWEEK
:
1974 return MONTHCAL_GetFirstDayOfWeek(infoPtr
);
1976 case MCM_SETFIRSTDAYOFWEEK
:
1977 return MONTHCAL_SetFirstDayOfWeek(infoPtr
, (INT
)lParam
);
1980 return MONTHCAL_GetRange(infoPtr
, (LPSYSTEMTIME
)lParam
);
1983 return MONTHCAL_SetRange(infoPtr
, (SHORT
)wParam
, (LPSYSTEMTIME
)lParam
);
1985 case MCM_GETMONTHDELTA
:
1986 return MONTHCAL_GetMonthDelta(infoPtr
);
1988 case MCM_SETMONTHDELTA
:
1989 return MONTHCAL_SetMonthDelta(infoPtr
, wParam
);
1991 case MCM_GETMAXTODAYWIDTH
:
1992 return MONTHCAL_GetMaxTodayWidth(infoPtr
);
1995 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
1998 return MONTHCAL_KillFocus(infoPtr
, (HWND
)wParam
);
2000 case WM_RBUTTONDOWN
:
2001 return MONTHCAL_RButtonDown(infoPtr
, lParam
);
2003 case WM_LBUTTONDOWN
:
2004 return MONTHCAL_LButtonDown(infoPtr
, lParam
);
2007 return MONTHCAL_MouseMove(infoPtr
, lParam
);
2010 return MONTHCAL_LButtonUp(infoPtr
, lParam
);
2012 case WM_PRINTCLIENT
:
2014 return MONTHCAL_Paint(infoPtr
, (HDC
)wParam
);
2017 return MONTHCAL_SetFocus(infoPtr
);
2020 return MONTHCAL_Size(infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
2023 return MONTHCAL_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
2026 return MONTHCAL_SetFont(infoPtr
, (HFONT
)wParam
, (BOOL
)lParam
);
2029 return MONTHCAL_GetFont(infoPtr
);
2032 return MONTHCAL_Timer(infoPtr
, wParam
);
2034 case WM_THEMECHANGED
:
2035 return theme_changed (infoPtr
);
2038 return MONTHCAL_Destroy(infoPtr
);
2040 case WM_SYSCOLORCHANGE
:
2041 COMCTL32_RefreshSysColors();
2044 case WM_STYLECHANGED
:
2045 return MONTHCAL_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
2048 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
2049 ERR( "unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
2050 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
2056 MONTHCAL_Register(void)
2060 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
2061 wndClass
.style
= CS_GLOBALCLASS
;
2062 wndClass
.lpfnWndProc
= MONTHCAL_WindowProc
;
2063 wndClass
.cbClsExtra
= 0;
2064 wndClass
.cbWndExtra
= sizeof(MONTHCAL_INFO
*);
2065 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
2066 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
2067 wndClass
.lpszClassName
= MONTHCAL_CLASSW
;
2069 RegisterClassW(&wndClass
);
2074 MONTHCAL_Unregister(void)
2076 UnregisterClassW(MONTHCAL_CLASSW
, NULL
);