reshuffling of dlls
[reactos.git] / reactos / dll / comctl32 / monthcal.c
1 /* Month calendar control
2
3 *
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>
9 *
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.
14 *
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.
19 *
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
23 *
24 * NOTE
25 *
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.
28 *
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.
32 *
33 * TODO:
34 * -- MCM_[GS]ETUNICODEFORMAT
35 * -- MONTHCAL_GetMonthRange
36 * -- handle resources better (doesn't work now);
37 * -- take care of internationalization.
38 * -- keyboard handling.
39 * -- GetRange: At the moment, we copy ranges anyway, regardless of
40 * infoPtr->rangeValid; an invalid range is simply filled
41 * with zeros in SetRange. Is this the right behavior?
42 * -- search for FIXME
43 */
44
45 #include <math.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #include "windef.h"
52 #include "winbase.h"
53 #include "wingdi.h"
54 #include "winuser.h"
55 #include "winnls.h"
56 #include "commctrl.h"
57 #include "comctl32.h"
58 #include "uxtheme.h"
59 #include "tmschema.h"
60 #include "wine/unicode.h"
61 #include "wine/debug.h"
62
63 WINE_DEFAULT_DEBUG_CHANNEL(monthcal);
64
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
74
75 #define countof(arr) (sizeof(arr)/sizeof(arr[0]))
76
77 typedef struct
78 {
79 HWND hwndSelf;
80 COLORREF bk;
81 COLORREF txt;
82 COLORREF titlebk;
83 COLORREF titletxt;
84 COLORREF monthbk;
85 COLORREF trailingtxt;
86 HFONT hFont;
87 HFONT hBoldFont;
88 int textHeight;
89 int textWidth;
90 int height_increment;
91 int width_increment;
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 */
97 int monthRange;
98 MONTHDAYSTATE *monthdayState;
99 SYSTEMTIME todaysDate;
100 DWORD currentMonth;
101 DWORD currentYear;
102 int status; /* See MC_SEL flags */
103 int curSelDay; /* current selected day */
104 int firstSelDay; /* first selected day */
105 int maxSelCount;
106 SYSTEMTIME minSel;
107 SYSTEMTIME maxSel;
108 DWORD rangeValid;
109 SYSTEMTIME minDate;
110 SYSTEMTIME maxDate;
111
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;
125
126
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};
129
130 static const WCHAR themeClass[] = { 'S','c','r','o','l','l','b','a','r',0 };
131
132 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0))
133
134 /* helper functions */
135
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)
139 {
140 const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
141 /*Wrap around, this eases handling*/
142 if(month == 0)
143 month = 12;
144 if(month == 13)
145 month = 1;
146
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);
153 }
154 else {
155 return mdays[month - 1];
156 }
157 }
158
159
160 /* make sure that time is valid */
161 static int MONTHCAL_ValidateTime(SYSTEMTIME time)
162 {
163 if(time.wMonth > 12) return FALSE;
164 if(time.wDayOfWeek > 6) return FALSE;
165 if(time.wDay > MONTHCAL_MonthLength(time.wMonth, time.wYear))
166 return FALSE;
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;
171
172 return TRUE;
173 }
174
175
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.
180 0 = Sunday.
181 */
182
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)
186 {
187 year-=(month < 3);
188
189 return((year + year/4 - year/100 + year/400 +
190 DayOfWeekTable[month-1] + day ) % 7);
191 }
192
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
195 */
196 static int MONTHCAL_CalcDayFromPos(MONTHCAL_INFO *infoPtr, int x, int y,
197 int *daypos,int *weekpos)
198 {
199 int retval, firstDay;
200 RECT rcClient;
201
202 GetClientRect(infoPtr->hwndSelf, &rcClient);
203
204 /* if the point is outside the x bounds of the window put
205 it at the boundary */
206 if (x > rcClient.right)
207 x = rcClient.right;
208
209
210 *daypos = (x - infoPtr->days.left ) / infoPtr->width_increment;
211 *weekpos = (y - infoPtr->days.top ) / infoPtr->height_increment;
212
213 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear)+6 - infoPtr->firstDay)%7;
214 retval = *daypos + (7 * *weekpos) - firstDay;
215 return retval;
216 }
217
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,
222 int *x, int *y)
223 {
224 int firstDay, prevMonth;
225
226 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear) +6 - infoPtr->firstDay)%7;
227
228 if(month==infoPtr->currentMonth) {
229 *x = (day + firstDay) % 7;
230 *y = (day + firstDay - *x) / 7;
231 return;
232 }
233 if(month < infoPtr->currentMonth) {
234 prevMonth = month - 1;
235 if(prevMonth==0)
236 prevMonth = 12;
237
238 *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay) % 7;
239 *y = 0;
240 return;
241 }
242
243 *y = MONTHCAL_MonthLength(month, infoPtr->currentYear - 1) / 7;
244 *x = (day + firstDay + MONTHCAL_MonthLength(month,
245 infoPtr->currentYear)) % 7;
246 }
247
248
249 /* x: column(day), y: row(week) */
250 static void MONTHCAL_CalcDayRect(MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
251 {
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;
256 }
257
258
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)
264 {
265 int x, y;
266
267 MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
268 MONTHCAL_CalcDayRect(infoPtr, r, x, y);
269 }
270
271
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)
275 {
276 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
277 HPEN hOldPen2 = SelectObject(hdc, hRedPen);
278 POINT points[13];
279 int x, y;
280 RECT day_rect;
281
282
283 MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
284
285 x = day_rect.left;
286 y = day_rect.top;
287
288 points[0].x = x;
289 points[0].y = y - 1;
290 points[1].x = x + 0.8 * infoPtr->width_increment;
291 points[1].y = y - 1;
292 points[2].x = x + 0.9 * infoPtr->width_increment;
293 points[2].y = y;
294 points[3].x = x + infoPtr->width_increment;
295 points[3].y = y + 0.5 * infoPtr->height_increment;
296
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 */
304
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;
309 points[9].x = x;
310 points[9].y = y + 0.5 * infoPtr->height_increment;
311
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;
318
319 PolyBezier(hdc, points, 13);
320 DeleteObject(hRedPen);
321 SelectObject(hdc, hOldPen2);
322 }
323
324
325 static void MONTHCAL_DrawDay(MONTHCAL_INFO *infoPtr, HDC hdc, int day, int month,
326 int x, int y, int bold)
327 {
328 static const WCHAR fmtW[] = { '%','d',0 };
329 WCHAR buf[10];
330 RECT r;
331 static int haveBoldFont, haveSelectedDay = FALSE;
332 HBRUSH hbr;
333 COLORREF oldCol = 0;
334 COLORREF oldBk = 0;
335
336 wsprintfW(buf, fmtW, day);
337
338 /* No need to check styles: when selection is not valid, it is set to zero.
339 * 1<day<31, so evertyhing's OK.
340 */
341
342 MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
343
344 if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
345 && (month==infoPtr->currentMonth)) {
346 HRGN hrgn;
347 RECT r2;
348
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);
356
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;
360 r2.top = r.top;
361 r2.right = r.left + 0.5 * infoPtr->textWidth;
362 r2.bottom = r.bottom;
363 if(haveSelectedDay) FillRect(hdc, &r2, hbr);
364 haveSelectedDay = TRUE;
365 } else {
366 haveSelectedDay = FALSE;
367 }
368
369 /* need to add some code for multiple selections */
370
371 if((bold) &&(!haveBoldFont)) {
372 SelectObject(hdc, infoPtr->hBoldFont);
373 haveBoldFont = TRUE;
374 }
375 if((!bold) &&(haveBoldFont)) {
376 SelectObject(hdc, infoPtr->hFont);
377 haveBoldFont = FALSE;
378 }
379
380 if(haveSelectedDay) {
381 SetTextColor(hdc, oldCol);
382 SetBkColor(hdc, oldBk);
383 }
384
385 SetBkMode(hdc,TRANSPARENT);
386 DrawTextW(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
387
388 /* draw a rectangle around the currently selected days text */
389 if((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth))
390 DrawFocusRect(hdc, &r);
391 }
392
393
394 static void paint_button (MONTHCAL_INFO *infoPtr, HDC hdc, BOOL btnNext,
395 BOOL pressed, RECT* r)
396 {
397 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
398
399 if (theme)
400 {
401 static const int states[] = {
402 /* Prev button */
403 ABS_LEFTNORMAL, ABS_LEFTPRESSED, ABS_LEFTDISABLED,
404 /* Next button */
405 ABS_RIGHTNORMAL, ABS_RIGHTPRESSED, ABS_RIGHTDISABLED
406 };
407 int stateNum = btnNext ? 3 : 0;
408 if (pressed)
409 stateNum += 1;
410 else
411 {
412 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
413 if (dwStyle & WS_DISABLED) stateNum += 2;
414 }
415 DrawThemeBackground (theme, hdc, SBP_ARROWBTN, states[stateNum], r, NULL);
416 }
417 else
418 {
419 int style = btnNext ? DFCS_SCROLLRIGHT : DFCS_SCROLLLEFT;
420 if (pressed)
421 style |= DFCS_PUSHED;
422 else
423 {
424 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
425 if (dwStyle & WS_DISABLED) style |= DFCS_INACTIVE;
426 }
427
428 DrawFrameControl(hdc, r, DFC_SCROLL, style);
429 }
430 }
431
432
433 static void MONTHCAL_Refresh(MONTHCAL_INFO *infoPtr, HDC hdc, PAINTSTRUCT* ps)
434 {
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;
444 RECT dayrect;
445 RECT *days=&dayrect;
446 RECT rtoday;
447 int i, j, m, mask, day, firstDay, weeknum, weeknum1,prevMonth;
448 int textHeight = infoPtr->textHeight, textWidth = infoPtr->textWidth;
449 SIZE size;
450 HBRUSH hbr;
451 HFONT currentFont;
452 WCHAR buf[20];
453 WCHAR buf1[20];
454 WCHAR buf2[32];
455 COLORREF oldTextColor, oldBkColor;
456 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
457 RECT rcTemp;
458 RECT rcDay; /* used in MONTHCAL_CalcDayRect() */
459 SYSTEMTIME localtime;
460 int startofprescal;
461
462 oldTextColor = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
463
464 /* fill background */
465 hbr = CreateSolidBrush (infoPtr->bk);
466 FillRect(hdc, &ps->rcPaint, hbr);
467 DeleteObject(hbr);
468
469 /* draw header */
470 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
471 {
472 hbr = CreateSolidBrush(infoPtr->titlebk);
473 FillRect(hdc, title, hbr);
474 DeleteObject(hbr);
475 }
476
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);
480
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);
484
485 oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
486 SetTextColor(hdc, infoPtr->titletxt);
487 currentFont = SelectObject(hdc, infoPtr->hBoldFont);
488
489 GetLocaleInfoW( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+infoPtr->currentMonth -1,
490 buf1,countof(buf1));
491 wsprintfW(buf, fmt1W, buf1, infoPtr->currentYear);
492
493 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
494 {
495 DrawTextW(hdc, buf, strlenW(buf), title,
496 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
497 }
498
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)
502 */
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;
509
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))
516 {
517 hbr = CreateSolidBrush(infoPtr->monthbk);
518 FillRect(hdc, &rcTemp, hbr);
519 DeleteObject(hbr);
520 }
521
522 /* draw line under day abbreviatons */
523
524 MoveToEx(hdc, infoPtr->days.left + 3, title->bottom + textHeight + 1, NULL);
525 LineTo(hdc, infoPtr->days.right - 3, title->bottom + textHeight + 1);
526
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 */
530
531 infoPtr->wdays.left = infoPtr->days.left = infoPtr->weeknums.right;
532 /* draw day abbreviations */
533
534 SelectObject(hdc, infoPtr->hFont);
535 SetBkColor(hdc, infoPtr->monthbk);
536 SetTextColor(hdc, infoPtr->trailingtxt);
537
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;
544
545 i = infoPtr->firstDay;
546
547 for(j=0; j<7; j++) {
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;
552 }
553
554 /* draw day numbers; first, the previous month */
555
556 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
557
558 day = MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) +
559 (infoPtr->firstDay + 7 - firstDay)%7 + 1;
560 if (day > MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear))
561 day -=7;
562 startofprescal = day;
563 mask = 1<<(day-1);
564
565 i = 0;
566 m = 0;
567 while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear)) {
568 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
569 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
570 {
571 MONTHCAL_DrawDay(infoPtr, hdc, day, prevMonth, i, 0,
572 infoPtr->monthdayState[m] & mask);
573 }
574
575 mask<<=1;
576 day++;
577 i++;
578 }
579
580 /* draw `current' month */
581
582 day = 1; /* start at the beginning of the current month */
583
584 infoPtr->firstDayplace = i;
585 SetTextColor(hdc, infoPtr->txt);
586 m++;
587 mask = 1;
588
589 /* draw the first week of the current month */
590 while(i<7) {
591 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
592 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
593 {
594
595 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth, i, 0,
596 infoPtr->monthdayState[m] & mask);
597
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);
603 }
604 }
605
606 mask<<=1;
607 day++;
608 i++;
609 }
610
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))
616 {
617 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth, i, j,
618 infoPtr->monthdayState[m] & mask);
619
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);
625 }
626 mask<<=1;
627 day++;
628 i++;
629 if(i>6) { /* past saturday, goto the next weeks sunday */
630 i = 0;
631 j++;
632 }
633 }
634
635 /* draw `next' month */
636
637 day = 1; /* start at the first day of the next month */
638 m++;
639 mask = 1;
640
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))
645 {
646 MONTHCAL_DrawDay(infoPtr, hdc, day, infoPtr->currentMonth + 1, i, j,
647 infoPtr->monthdayState[m] & mask);
648 }
649
650 mask<<=1;
651 day++;
652 i++;
653 if(i==7) { /* past saturday, go to next week's sunday */
654 i = 0;
655 j++;
656 }
657 }
658 SetTextColor(hdc, infoPtr->txt);
659
660
661 /* draw `today' date if style allows it, and draw a circle before today's
662 * date if necessary */
663
664 if(!(dwStyle & MCS_NOTODAY)) {
665 int offset = 0;
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);
671 offset+=textWidth;
672 }
673 if (!LoadStringW(COMCTL32_hModule,IDM_TODAY,buf1,countof(buf1)))
674 {
675 WARN("Can't load resource\n");
676 strcpyW(buf1, todayW);
677 }
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);
683
684 DrawTextW(hdc, buf, -1, &rtoday, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE);
685 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rtoday))
686 {
687 DrawTextW(hdc, buf, -1, &rtoday, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
688 }
689 SelectObject(hdc, infoPtr->hFont);
690 }
691
692 /*eventually draw week numbers*/
693 if(dwStyle & MCS_WEEKNUMBERS) {
694 /* display weeknumbers*/
695 int mindays;
696
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
704 */
705 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, countof(buf));
706 weeknum = atoiW(buf);
707 switch (weeknum)
708 {
709 case 1: mindays = 6;
710 break;
711 case 2: mindays = 3;
712 break;
713 case 0:
714 default:
715 mindays = 0;
716 }
717 if (infoPtr->currentMonth < 2)
718 {
719 /* calculate all those exceptions for january */
720 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
721 if ((infoPtr->firstDay +7 - weeknum1)%7 > mindays)
722 weeknum =1;
723 else
724 {
725 weeknum = 0;
726 for(i=0; i<11; i++)
727 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear-1);
728 weeknum +=startofprescal+ 7;
729 weeknum /=7;
730 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear-1);
731 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
732 weeknum++;
733 }
734 }
735 else
736 {
737 weeknum = 0;
738 for(i=0; i<prevMonth-1; i++)
739 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear);
740 weeknum +=startofprescal+ 7;
741 weeknum /=7;
742 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
743 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
744 weeknum++;
745 }
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;
750 for(i=0; i<6; i++) {
751 if((i==0)&&(weeknum>50))
752 {
753 wsprintfW(buf, fmt3W, weeknum);
754 weeknum=0;
755 }
756 else if((i==5)&&(weeknum>47))
757 {
758 wsprintfW(buf, fmt3W, 1);
759 }
760 else
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;
765 }
766
767 MoveToEx(hdc, infoPtr->weeknums.right, infoPtr->weeknums.top + 3 , NULL);
768 LineTo(hdc, infoPtr->weeknums.right, infoPtr->weeknums.bottom );
769
770 }
771 /* currentFont was font at entering Refresh */
772
773 SetBkColor(hdc, oldBkColor);
774 SelectObject(hdc, currentFont);
775 SetTextColor(hdc, oldTextColor);
776 }
777
778
779 static LRESULT
780 MONTHCAL_GetMinReqRect(MONTHCAL_INFO *infoPtr, LPARAM lParam)
781 {
782 LPRECT lpRect = (LPRECT) lParam;
783
784 TRACE("rect %p\n", lpRect);
785
786 /* validate parameters */
787
788 if((infoPtr==NULL) ||(lpRect == NULL) ) return FALSE;
789
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);
795
796 TRACE("%s\n", wine_dbgstr_rect(lpRect));
797
798 return TRUE;
799 }
800
801
802 static LRESULT
803 MONTHCAL_GetColor(MONTHCAL_INFO *infoPtr, WPARAM wParam)
804 {
805 TRACE("\n");
806
807 switch((int)wParam) {
808 case MCSC_BACKGROUND:
809 return infoPtr->bk;
810 case MCSC_TEXT:
811 return infoPtr->txt;
812 case MCSC_TITLEBK:
813 return infoPtr->titlebk;
814 case MCSC_TITLETEXT:
815 return infoPtr->titletxt;
816 case MCSC_MONTHBK:
817 return infoPtr->monthbk;
818 case MCSC_TRAILINGTEXT:
819 return infoPtr->trailingtxt;
820 }
821
822 return -1;
823 }
824
825
826 static LRESULT
827 MONTHCAL_SetColor(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
828 {
829 int prev = -1;
830
831 TRACE("%d: color %08lx\n", wParam, lParam);
832
833 switch((int)wParam) {
834 case MCSC_BACKGROUND:
835 prev = infoPtr->bk;
836 infoPtr->bk = (COLORREF)lParam;
837 break;
838 case MCSC_TEXT:
839 prev = infoPtr->txt;
840 infoPtr->txt = (COLORREF)lParam;
841 break;
842 case MCSC_TITLEBK:
843 prev = infoPtr->titlebk;
844 infoPtr->titlebk = (COLORREF)lParam;
845 break;
846 case MCSC_TITLETEXT:
847 prev=infoPtr->titletxt;
848 infoPtr->titletxt = (COLORREF)lParam;
849 break;
850 case MCSC_MONTHBK:
851 prev = infoPtr->monthbk;
852 infoPtr->monthbk = (COLORREF)lParam;
853 break;
854 case MCSC_TRAILINGTEXT:
855 prev = infoPtr->trailingtxt;
856 infoPtr->trailingtxt = (COLORREF)lParam;
857 break;
858 }
859
860 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
861 return prev;
862 }
863
864
865 static LRESULT
866 MONTHCAL_GetMonthDelta(MONTHCAL_INFO *infoPtr)
867 {
868 TRACE("\n");
869
870 if(infoPtr->delta)
871 return infoPtr->delta;
872 else
873 return infoPtr->visible;
874 }
875
876
877 static LRESULT
878 MONTHCAL_SetMonthDelta(MONTHCAL_INFO *infoPtr, WPARAM wParam)
879 {
880 int prev = infoPtr->delta;
881
882 TRACE("delta %d\n", wParam);
883
884 infoPtr->delta = (int)wParam;
885 return prev;
886 }
887
888
889 static LRESULT
890 MONTHCAL_GetFirstDayOfWeek(MONTHCAL_INFO *infoPtr)
891 {
892 return infoPtr->firstDay;
893 }
894
895
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 */
900 static LRESULT
901 MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO *infoPtr, LPARAM lParam)
902 {
903 int prev = infoPtr->firstDay;
904 WCHAR buf[40];
905
906 TRACE("day %ld\n", lParam);
907
908 if((lParam >= 0) && (lParam < 7)) {
909 infoPtr->firstDay = (int)lParam;
910 }
911 else
912 {
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;
916 }
917 return prev;
918 }
919
920
921 static LRESULT
922 MONTHCAL_GetMonthRange(MONTHCAL_INFO *infoPtr)
923 {
924 TRACE("\n");
925
926 return infoPtr->monthRange;
927 }
928
929
930 static LRESULT
931 MONTHCAL_GetMaxTodayWidth(MONTHCAL_INFO *infoPtr)
932 {
933 return(infoPtr->todayrect.right - infoPtr->todayrect.left);
934 }
935
936
937 /* FIXME: are validated times taken from current date/time or simply
938 * copied?
939 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
940 * adjusting range with MCM_SETRANGE
941 */
942
943 static LRESULT
944 MONTHCAL_SetRange(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
945 {
946 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *)lParam;
947 int prev;
948
949 TRACE("%x %lx\n", wParam, lParam);
950
951 if(wParam & GDTR_MAX) {
952 if(MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
953 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxDate);
954 infoPtr->rangeValid|=GDTR_MAX;
955 } else {
956 GetSystemTime(&infoPtr->todaysDate);
957 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
958 }
959 }
960 if(wParam & GDTR_MIN) {
961 if(MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
962 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minDate);
963 infoPtr->rangeValid|=GDTR_MIN;
964 } else {
965 GetSystemTime(&infoPtr->todaysDate);
966 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
967 }
968 }
969
970 prev = infoPtr->monthRange;
971 infoPtr->monthRange = infoPtr->maxDate.wMonth - infoPtr->minDate.wMonth;
972
973 if(infoPtr->monthRange!=prev) {
974 infoPtr->monthdayState = ReAlloc(infoPtr->monthdayState,
975 infoPtr->monthRange * sizeof(MONTHDAYSTATE));
976 }
977
978 return 1;
979 }
980
981
982 static LRESULT
983 MONTHCAL_GetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
984 {
985 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
986 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *)lParam;
987
988 /* validate parameters */
989
990 if((infoPtr==NULL) || (lprgSysTimeArray==NULL)) return FALSE;
991
992 MONTHCAL_CopyTime(&infoPtr->maxDate, &lprgSysTimeArray[1]);
993 MONTHCAL_CopyTime(&infoPtr->minDate, &lprgSysTimeArray[0]);
994
995 return infoPtr->rangeValid;
996 }
997
998
999 static LRESULT
1000 MONTHCAL_SetDayState(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
1001
1002 {
1003 int i, iMonths = (int)wParam;
1004 MONTHDAYSTATE *dayStates = (LPMONTHDAYSTATE)lParam;
1005
1006 TRACE("%x %lx\n", wParam, lParam);
1007 if(iMonths!=infoPtr->monthRange) return 0;
1008
1009 for(i=0; i<iMonths; i++)
1010 infoPtr->monthdayState[i] = dayStates[i];
1011 return 1;
1012 }
1013
1014 static LRESULT
1015 MONTHCAL_GetCurSel(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1016 {
1017 SYSTEMTIME *lpSel = (SYSTEMTIME *) lParam;
1018
1019 TRACE("%lx\n", lParam);
1020 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1021 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1022
1023 MONTHCAL_CopyTime(&infoPtr->minSel, lpSel);
1024 TRACE("%d/%d/%d\n", lpSel->wYear, lpSel->wMonth, lpSel->wDay);
1025 return TRUE;
1026 }
1027
1028 /* FIXME: if the specified date is not visible, make it visible */
1029 /* FIXME: redraw? */
1030 static LRESULT
1031 MONTHCAL_SetCurSel(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1032 {
1033 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
1034
1035 TRACE("%lx\n", lParam);
1036 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1037 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1038
1039 infoPtr->currentMonth=lpSel->wMonth;
1040 infoPtr->currentYear=lpSel->wYear;
1041
1042 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
1043 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
1044
1045 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1046
1047 return TRUE;
1048 }
1049
1050
1051 static LRESULT
1052 MONTHCAL_GetMaxSelCount(MONTHCAL_INFO *infoPtr)
1053 {
1054 return infoPtr->maxSelCount;
1055 }
1056
1057
1058 static LRESULT
1059 MONTHCAL_SetMaxSelCount(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1060 {
1061 TRACE("%x\n", wParam);
1062
1063 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT) {
1064 infoPtr->maxSelCount = wParam;
1065 }
1066
1067 return TRUE;
1068 }
1069
1070
1071 static LRESULT
1072 MONTHCAL_GetSelRange(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1073 {
1074 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1075
1076 TRACE("%lx\n", lParam);
1077
1078 /* validate parameters */
1079
1080 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1081
1082 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT)
1083 {
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);
1087 return TRUE;
1088 }
1089
1090 return FALSE;
1091 }
1092
1093
1094 static LRESULT
1095 MONTHCAL_SetSelRange(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1096 {
1097 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1098
1099 TRACE("%lx\n", lParam);
1100
1101 /* validate parameters */
1102
1103 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1104
1105 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT)
1106 {
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);
1110 return TRUE;
1111 }
1112
1113 return FALSE;
1114 }
1115
1116
1117 static LRESULT
1118 MONTHCAL_GetToday(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1119 {
1120 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1121
1122 TRACE("%lx\n", lParam);
1123
1124 /* validate parameters */
1125
1126 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
1127 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
1128 return TRUE;
1129 }
1130
1131
1132 static LRESULT
1133 MONTHCAL_SetToday(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1134 {
1135 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1136
1137 TRACE("%lx\n", lParam);
1138
1139 /* validate parameters */
1140
1141 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
1142 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
1143 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1144 return TRUE;
1145 }
1146
1147
1148 static LRESULT
1149 MONTHCAL_HitTest(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1150 {
1151 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
1152 UINT x,y;
1153 DWORD retval;
1154 int day,wday,wnum;
1155
1156
1157 x = lpht->pt.x;
1158 y = lpht->pt.y;
1159 retval = MCHT_NOWHERE;
1160
1161 ZeroMemory(&lpht->st, sizeof(lpht->st));
1162
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);
1173 */
1174
1175 /* are we in the header? */
1176
1177 if(PtInRect(&infoPtr->title, lpht->pt)) {
1178 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1179 retval = MCHT_TITLEBTNPREV;
1180 goto done;
1181 }
1182 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1183 retval = MCHT_TITLEBTNNEXT;
1184 goto done;
1185 }
1186 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1187 retval = MCHT_TITLEMONTH;
1188 goto done;
1189 }
1190 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1191 retval = MCHT_TITLEYEAR;
1192 goto done;
1193 }
1194
1195 retval = MCHT_TITLE;
1196 goto done;
1197 }
1198
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;
1206 goto done;
1207 }
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;
1218 goto done;
1219 }
1220 if(PtInRect(&infoPtr->days, lpht->pt))
1221 {
1222 lpht->st.wYear = infoPtr->currentYear;
1223 if ( day < 1)
1224 {
1225 retval = MCHT_CALENDARDATEPREV;
1226 lpht->st.wMonth = infoPtr->currentMonth - 1;
1227 if (lpht->st.wMonth <1)
1228 {
1229 lpht->st.wMonth = 12;
1230 lpht->st.wYear--;
1231 }
1232 lpht->st.wDay = MONTHCAL_MonthLength(lpht->st.wMonth,lpht->st.wYear) -day;
1233 }
1234 else if (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear))
1235 {
1236 retval = MCHT_CALENDARDATENEXT;
1237 lpht->st.wMonth = infoPtr->currentMonth + 1;
1238 if (lpht->st.wMonth <12)
1239 {
1240 lpht->st.wMonth = 1;
1241 lpht->st.wYear++;
1242 }
1243 lpht->st.wDay = day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) ;
1244 }
1245 else {
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);
1250 }
1251 goto done;
1252 }
1253 if(PtInRect(&infoPtr->todayrect, lpht->pt)) {
1254 retval = MCHT_TODAYLINK;
1255 goto done;
1256 }
1257
1258
1259 /* Hit nothing special? What's left must be background :-) */
1260
1261 retval = MCHT_CALENDARBK;
1262 done:
1263 lpht->uHit = retval;
1264 return retval;
1265 }
1266
1267
1268 static void MONTHCAL_GoToNextMonth(MONTHCAL_INFO *infoPtr)
1269 {
1270 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
1271
1272 TRACE("MONTHCAL_GoToNextMonth\n");
1273
1274 infoPtr->currentMonth++;
1275 if(infoPtr->currentMonth > 12) {
1276 infoPtr->currentYear++;
1277 infoPtr->currentMonth = 1;
1278 }
1279
1280 if(dwStyle & MCS_DAYSTATE) {
1281 NMDAYSTATE nmds;
1282 int i;
1283
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));
1289
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];
1294 }
1295 }
1296
1297
1298 static void MONTHCAL_GoToPrevMonth(MONTHCAL_INFO *infoPtr)
1299 {
1300 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
1301
1302 TRACE("\n");
1303
1304 infoPtr->currentMonth--;
1305 if(infoPtr->currentMonth < 1) {
1306 infoPtr->currentYear--;
1307 infoPtr->currentMonth = 12;
1308 }
1309
1310 if(dwStyle & MCS_DAYSTATE) {
1311 NMDAYSTATE nmds;
1312 int i;
1313
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));
1320
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];
1325 }
1326 }
1327
1328 static LRESULT
1329 MONTHCAL_RButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1330 {
1331 static const WCHAR todayW[] = { 'G','o',' ','t','o',' ','T','o','d','a','y',':',0 };
1332 HMENU hMenu;
1333 POINT menupoint;
1334 WCHAR buf[32];
1335
1336 hMenu = CreatePopupMenu();
1337 if (!LoadStringW(COMCTL32_hModule,IDM_GOTODAY,buf,countof(buf)))
1338 {
1339 WARN("Can't load resource\n");
1340 strcpyW(buf, todayW);
1341 }
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))
1348 {
1349 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1350 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1351 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1352 }
1353 return 0;
1354 }
1355
1356 static LRESULT
1357 MONTHCAL_LButtonDown(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1358 {
1359 static const WCHAR EditW[] = { 'E','D','I','T',0 };
1360 MCHITTESTINFO ht;
1361 DWORD hit;
1362 HMENU hMenu;
1363 RECT rcDay; /* used in determining area to invalidate */
1364 WCHAR buf[32];
1365 int i;
1366 POINT menupoint;
1367
1368 TRACE("%lx\n", lParam);
1369
1370 if (infoPtr->hWndYearUpDown)
1371 {
1372 infoPtr->currentYear=SendMessageW( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)0);
1373 if(!DestroyWindow(infoPtr->hWndYearUpDown))
1374 {
1375 FIXME("Can't destroy Updown Control\n");
1376 }
1377 else
1378 infoPtr->hWndYearUpDown=0;
1379 if(!DestroyWindow(infoPtr->hWndYearEdit))
1380 {
1381 FIXME("Can't destroy Updown Control\n");
1382 }
1383 else
1384 infoPtr->hWndYearEdit=0;
1385 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1386 }
1387
1388 ht.pt.x = (INT)LOWORD(lParam);
1389 ht.pt.y = (INT)HIWORD(lParam);
1390 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1391
1392 /* FIXME: these flags should be checked by */
1393 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1394 /* multi-bit */
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);
1400 return TRUE;
1401 }
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);
1407 return TRUE;
1408 }
1409
1410 if(hit == MCHT_TITLEMONTH) {
1411 hMenu = CreatePopupMenu();
1412
1413 for (i=0; i<12;i++)
1414 {
1415 GetLocaleInfoW(LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+i, buf,countof(buf));
1416 AppendMenuW(hMenu, MF_STRING|MF_ENABLED,i+1, buf);
1417 }
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))
1424 {
1425 infoPtr->currentMonth=i;
1426 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1427 }
1428 }
1429 if(hit == MCHT_TITLEYEAR) {
1430 infoPtr->hWndYearEdit=CreateWindowExW(0,
1431 EditW,
1432 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,
1437 infoPtr->hwndSelf,
1438 NULL,
1439 NULL,
1440 NULL);
1441 SendMessageW( infoPtr->hWndYearEdit, WM_SETFONT, (WPARAM) infoPtr->hBoldFont, (LPARAM)TRUE);
1442 infoPtr->hWndYearUpDown=CreateWindowExW(0,
1443 UPDOWN_CLASSW,
1444 0,
1445 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT|UDS_NOTHOUSANDS|UDS_ARROWKEYS,
1446 infoPtr->titleyear.right+7,infoPtr->titlebtnnext.top,
1447 18,
1448 infoPtr->textHeight,
1449 infoPtr->hwndSelf,
1450 NULL,
1451 NULL,
1452 NULL);
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 );
1456 return TRUE;
1457
1458 }
1459 if(hit == MCHT_TODAYLINK) {
1460 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1461 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1462 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1463 return TRUE;
1464 }
1465 if(hit == MCHT_CALENDARDATE) {
1466 SYSTEMTIME selArray[2];
1467 NMSELCHANGE nmsc;
1468
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);
1479
1480 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
1481 (WPARAM)nmsc.nmhdr.idFrom,(LPARAM)&nmsc);
1482
1483
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);
1488
1489 MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->curSelDay, infoPtr->currentMonth, &rcDay);
1490 InvalidateRect(infoPtr->hwndSelf, &rcDay, TRUE);
1491 }
1492
1493 infoPtr->firstSelDay = ht.st.wDay;
1494 infoPtr->curSelDay = ht.st.wDay;
1495 infoPtr->status = MC_SEL_LBUTDOWN;
1496 return TRUE;
1497 }
1498
1499 return 0;
1500 }
1501
1502
1503 static LRESULT
1504 MONTHCAL_LButtonUp(MONTHCAL_INFO *infoPtr, LPARAM lParam)
1505 {
1506 NMSELCHANGE nmsc;
1507 NMHDR nmhdr;
1508 BOOL redraw = FALSE;
1509 MCHITTESTINFO ht;
1510 DWORD hit;
1511
1512 TRACE("\n");
1513
1514 if(infoPtr->status & MC_NEXTPRESSED) {
1515 KillTimer(infoPtr->hwndSelf, MC_NEXTMONTHTIMER);
1516 infoPtr->status &= ~MC_NEXTPRESSED;
1517 redraw = TRUE;
1518 }
1519 if(infoPtr->status & MC_PREVPRESSED) {
1520 KillTimer(infoPtr->hwndSelf, MC_PREVMONTHTIMER);
1521 infoPtr->status &= ~MC_PREVPRESSED;
1522 redraw = TRUE;
1523 }
1524
1525 ht.pt.x = (INT)LOWORD(lParam);
1526 ht.pt.y = (INT)HIWORD(lParam);
1527 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1528
1529 infoPtr->status = MC_SEL_LBUTUP;
1530
1531 if(hit ==MCHT_CALENDARDATENEXT) {
1532 MONTHCAL_GoToNextMonth(infoPtr);
1533 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1534 return TRUE;
1535 }
1536 if(hit == MCHT_CALENDARDATEPREV){
1537 MONTHCAL_GoToPrevMonth(infoPtr);
1538 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1539 return TRUE;
1540 }
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);
1545
1546 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1547 /* redraw if necessary */
1548 if(redraw)
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);
1557
1558 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, (WPARAM)nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1559
1560 }
1561 return 0;
1562 }
1563
1564
1565 static LRESULT
1566 MONTHCAL_Timer(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1567 {
1568 BOOL redraw = FALSE;
1569
1570 TRACE("%d\n", wParam);
1571
1572 switch(wParam) {
1573 case MC_NEXTMONTHTIMER:
1574 redraw = TRUE;
1575 MONTHCAL_GoToNextMonth(infoPtr);
1576 break;
1577 case MC_PREVMONTHTIMER:
1578 redraw = TRUE;
1579 MONTHCAL_GoToPrevMonth(infoPtr);
1580 break;
1581 default:
1582 ERR("got unknown timer\n");
1583 break;
1584 }
1585
1586 /* redraw only if necessary */
1587 if(redraw)
1588 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1589
1590 return 0;
1591 }
1592
1593
1594 static LRESULT
1595 MONTHCAL_MouseMove(MONTHCAL_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
1596 {
1597 MCHITTESTINFO ht;
1598 int oldselday, selday, hit;
1599 RECT r;
1600
1601 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1602
1603 ht.pt.x = LOWORD(lParam);
1604 ht.pt.y = HIWORD(lParam);
1605
1606 hit = MONTHCAL_HitTest(infoPtr, (LPARAM)&ht);
1607
1608 /* not on the calendar date numbers? bail out */
1609 TRACE("hit:%x\n",hit);
1610 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1611
1612 selday = ht.st.wDay;
1613 oldselday = infoPtr->curSelDay;
1614 infoPtr->curSelDay = selday;
1615 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1616
1617 if(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & MCS_MULTISELECT) {
1618 SYSTEMTIME selArray[2];
1619 int i;
1620
1621 MONTHCAL_GetSelRange(infoPtr, (LPARAM)&selArray);
1622 i = 0;
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;
1630 }
1631
1632 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1633 if(selday>infoPtr->firstSelDay)
1634 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1635 else
1636 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1637 }
1638
1639 if(selArray[i].wDay!=selday) {
1640 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1641
1642 selArray[i].wDay = selday;
1643
1644 if(selArray[0].wDay>selArray[1].wDay) {
1645 DWORD tempday;
1646 tempday = selArray[1].wDay;
1647 selArray[1].wDay = selArray[0].wDay;
1648 selArray[0].wDay = tempday;
1649 }
1650
1651 MONTHCAL_SetSelRange(infoPtr, (LPARAM)&selArray);
1652 }
1653 }
1654
1655 done:
1656
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);
1662
1663 return 0;
1664 }
1665
1666
1667 static LRESULT
1668 MONTHCAL_Paint(MONTHCAL_INFO *infoPtr, WPARAM wParam)
1669 {
1670 HDC hdc;
1671 PAINTSTRUCT ps;
1672
1673 if (wParam)
1674 {
1675 GetClientRect(infoPtr->hwndSelf, &ps.rcPaint);
1676 hdc = (HDC)wParam;
1677 }
1678 else
1679 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
1680
1681 MONTHCAL_Refresh(infoPtr, hdc, &ps);
1682 if (!wParam) EndPaint(infoPtr->hwndSelf, &ps);
1683 return 0;
1684 }
1685
1686
1687 static LRESULT
1688 MONTHCAL_KillFocus(MONTHCAL_INFO *infoPtr)
1689 {
1690 TRACE("\n");
1691
1692 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1693
1694 return 0;
1695 }
1696
1697
1698 static LRESULT
1699 MONTHCAL_SetFocus(MONTHCAL_INFO *infoPtr)
1700 {
1701 TRACE("\n");
1702
1703 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1704
1705 return 0;
1706 }
1707
1708 /* sets the size information */
1709 static void MONTHCAL_UpdateSize(MONTHCAL_INFO *infoPtr)
1710 {
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;
1723 SIZE size;
1724 TEXTMETRICW tm;
1725 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE);
1726 HFONT currentFont;
1727 int xdiv, left_offset;
1728 RECT rcClient;
1729
1730 GetClientRect(infoPtr->hwndSelf, &rcClient);
1731
1732 currentFont = SelectObject(hdc, infoPtr->hFont);
1733
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;
1739
1740 /* recalculate the height and width increments and offsets */
1741 GetTextExtentPoint32W(hdc, O0W, 2, &size);
1742
1743 xdiv = (dwStyle & MCS_WEEKNUMBERS) ? 8 : 7;
1744
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);
1748
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;
1754
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);
1763
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;
1769
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;
1775 else
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;
1781
1782 days->top = weeknumrect->top = wdays->bottom ;
1783 days->bottom = weeknumrect->bottom = days->top + 6 * infoPtr->height_increment;
1784
1785 todayrect->left = rcClient.left;
1786 todayrect->right = rcClient.right;
1787 todayrect->top = days->bottom;
1788 todayrect->bottom = days->bottom + infoPtr->height_increment;
1789
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));
1797
1798 /* restore the originally selected font */
1799 SelectObject(hdc, currentFont);
1800
1801 ReleaseDC(infoPtr->hwndSelf, hdc);
1802 }
1803
1804 static LRESULT MONTHCAL_Size(MONTHCAL_INFO *infoPtr, int Width, int Height)
1805 {
1806 TRACE("(width=%d, height=%d)\n", Width, Height);
1807
1808 MONTHCAL_UpdateSize(infoPtr);
1809
1810 /* invalidate client area and erase background */
1811 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
1812
1813 return 0;
1814 }
1815
1816 static LRESULT MONTHCAL_GetFont(MONTHCAL_INFO *infoPtr)
1817 {
1818 return (LRESULT)infoPtr->hFont;
1819 }
1820
1821 static LRESULT MONTHCAL_SetFont(MONTHCAL_INFO *infoPtr, HFONT hFont, BOOL redraw)
1822 {
1823 HFONT hOldFont;
1824 LOGFONTW lf;
1825
1826 if (!hFont) return 0;
1827
1828 hOldFont = infoPtr->hFont;
1829 infoPtr->hFont = hFont;
1830
1831 GetObjectW(infoPtr->hFont, sizeof(lf), &lf);
1832 lf.lfWeight = FW_BOLD;
1833 infoPtr->hBoldFont = CreateFontIndirectW(&lf);
1834
1835 if (redraw)
1836 InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
1837
1838 return (LRESULT)hOldFont;
1839 }
1840
1841 /* update theme after a WM_THEMECHANGED message */
1842 static LRESULT theme_changed (MONTHCAL_INFO* infoPtr)
1843 {
1844 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
1845 CloseThemeData (theme);
1846 theme = OpenThemeData (infoPtr->hwndSelf, themeClass);
1847 return 0;
1848 }
1849
1850 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1851 static LRESULT
1852 MONTHCAL_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
1853 {
1854 MONTHCAL_INFO *infoPtr;
1855
1856 /* allocate memory for info structure */
1857 infoPtr =(MONTHCAL_INFO*)Alloc(sizeof(MONTHCAL_INFO));
1858 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
1859
1860 if(infoPtr == NULL) {
1861 ERR( "could not allocate info memory!\n");
1862 return 0;
1863 }
1864
1865 infoPtr->hwndSelf = hwnd;
1866 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1867
1868 MONTHCAL_SetFont(infoPtr, GetStockObject(DEFAULT_GUI_FONT), FALSE);
1869
1870 /* initialize info structure */
1871 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1872
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);
1891
1892 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1893 /* of the control */
1894 MONTHCAL_UpdateSize(infoPtr);
1895
1896 OpenThemeData (infoPtr->hwndSelf, themeClass);
1897
1898 return 0;
1899 }
1900
1901
1902 static LRESULT
1903 MONTHCAL_Destroy(MONTHCAL_INFO *infoPtr)
1904 {
1905 /* free month calendar info data */
1906 if(infoPtr->monthdayState)
1907 Free(infoPtr->monthdayState);
1908 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
1909
1910 CloseThemeData (GetWindowTheme (infoPtr->hwndSelf));
1911
1912 Free(infoPtr);
1913 return 0;
1914 }
1915
1916
1917 static LRESULT WINAPI
1918 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1919 {
1920 MONTHCAL_INFO *infoPtr;
1921
1922 TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1923
1924 infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1925 if (!infoPtr && (uMsg != WM_CREATE))
1926 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
1927 switch(uMsg)
1928 {
1929 case MCM_GETCURSEL:
1930 return MONTHCAL_GetCurSel(infoPtr, lParam);
1931
1932 case MCM_SETCURSEL:
1933 return MONTHCAL_SetCurSel(infoPtr, lParam);
1934
1935 case MCM_GETMAXSELCOUNT:
1936 return MONTHCAL_GetMaxSelCount(infoPtr);
1937
1938 case MCM_SETMAXSELCOUNT:
1939 return MONTHCAL_SetMaxSelCount(infoPtr, wParam);
1940
1941 case MCM_GETSELRANGE:
1942 return MONTHCAL_GetSelRange(infoPtr, lParam);
1943
1944 case MCM_SETSELRANGE:
1945 return MONTHCAL_SetSelRange(infoPtr, lParam);
1946
1947 case MCM_GETMONTHRANGE:
1948 return MONTHCAL_GetMonthRange(infoPtr);
1949
1950 case MCM_SETDAYSTATE:
1951 return MONTHCAL_SetDayState(infoPtr, wParam, lParam);
1952
1953 case MCM_GETMINREQRECT:
1954 return MONTHCAL_GetMinReqRect(infoPtr, lParam);
1955
1956 case MCM_GETCOLOR:
1957 return MONTHCAL_GetColor(infoPtr, wParam);
1958
1959 case MCM_SETCOLOR:
1960 return MONTHCAL_SetColor(infoPtr, wParam, lParam);
1961
1962 case MCM_GETTODAY:
1963 return MONTHCAL_GetToday(infoPtr, lParam);
1964
1965 case MCM_SETTODAY:
1966 return MONTHCAL_SetToday(infoPtr, lParam);
1967
1968 case MCM_HITTEST:
1969 return MONTHCAL_HitTest(infoPtr, lParam);
1970
1971 case MCM_GETFIRSTDAYOFWEEK:
1972 return MONTHCAL_GetFirstDayOfWeek(infoPtr);
1973
1974 case MCM_SETFIRSTDAYOFWEEK:
1975 return MONTHCAL_SetFirstDayOfWeek(infoPtr, lParam);
1976
1977 case MCM_GETRANGE:
1978 return MONTHCAL_GetRange(hwnd, wParam, lParam);
1979
1980 case MCM_SETRANGE:
1981 return MONTHCAL_SetRange(infoPtr, wParam, lParam);
1982
1983 case MCM_GETMONTHDELTA:
1984 return MONTHCAL_GetMonthDelta(infoPtr);
1985
1986 case MCM_SETMONTHDELTA:
1987 return MONTHCAL_SetMonthDelta(infoPtr, wParam);
1988
1989 case MCM_GETMAXTODAYWIDTH:
1990 return MONTHCAL_GetMaxTodayWidth(infoPtr);
1991
1992 case WM_GETDLGCODE:
1993 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1994
1995 case WM_KILLFOCUS:
1996 return MONTHCAL_KillFocus(infoPtr);
1997
1998 case WM_RBUTTONDOWN:
1999 return MONTHCAL_RButtonDown(infoPtr, lParam);
2000
2001 case WM_LBUTTONDOWN:
2002 return MONTHCAL_LButtonDown(infoPtr, lParam);
2003
2004 case WM_MOUSEMOVE:
2005 return MONTHCAL_MouseMove(infoPtr, wParam, lParam);
2006
2007 case WM_LBUTTONUP:
2008 return MONTHCAL_LButtonUp(infoPtr, lParam);
2009
2010 case WM_PRINTCLIENT:
2011 case WM_PAINT:
2012 return MONTHCAL_Paint(infoPtr, wParam);
2013
2014 case WM_SETFOCUS:
2015 return MONTHCAL_SetFocus(infoPtr);
2016
2017 case WM_SIZE:
2018 return MONTHCAL_Size(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
2019
2020 case WM_CREATE:
2021 return MONTHCAL_Create(hwnd, wParam, lParam);
2022
2023 case WM_SETFONT:
2024 return MONTHCAL_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
2025
2026 case WM_GETFONT:
2027 return MONTHCAL_GetFont(infoPtr);
2028
2029 case WM_TIMER:
2030 return MONTHCAL_Timer(infoPtr, wParam);
2031
2032 case WM_THEMECHANGED:
2033 return theme_changed (infoPtr);
2034
2035 case WM_DESTROY:
2036 return MONTHCAL_Destroy(infoPtr);
2037
2038 default:
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);
2042 }
2043 }
2044
2045
2046 void
2047 MONTHCAL_Register(void)
2048 {
2049 WNDCLASSW wndClass;
2050
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;
2059
2060 RegisterClassW(&wndClass);
2061 }
2062
2063
2064 void
2065 MONTHCAL_Unregister(void)
2066 {
2067 UnregisterClassW(MONTHCAL_CLASSW, NULL);
2068 }