Replace GetWindowLong by GetWindowLongPtr.
[wine/multimedia.git] / dlls / comctl32 / monthcal.c
blob0a56ac4bc2c1ea2f97bcfdd0963c91f28211b695
1 /* Month calendar control
4 * Copyright 1998, 1999 Eric Kohl (ekohl@abo.rhein-zeitung.de)
5 * Copyright 1999 Alex Priem (alexp@sci.kun.nl)
6 * Copyright 1999 Chris Morgan <cmorgan@wpi.edu> and
7 * James Abbatiello <abbeyj@wpi.edu>
8 * Copyright 2000 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * TODO:
25 * - Notifications.
28 * FIXME: handle resources better (doesn't work now); also take care
29 of internationalization.
30 * FIXME: keyboard handling.
33 #include <math.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43 #include "winnls.h"
44 #include "commctrl.h"
45 #include "comctl32.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(monthcal);
50 #define MC_SEL_LBUTUP 1 /* Left button released */
51 #define MC_SEL_LBUTDOWN 2 /* Left button pressed in calendar */
52 #define MC_PREVPRESSED 4 /* Prev month button pressed */
53 #define MC_NEXTPRESSED 8 /* Next month button pressed */
54 #define MC_NEXTMONTHDELAY 350 /* when continuously pressing `next */
55 /* month', wait 500 ms before going */
56 /* to the next month */
57 #define MC_NEXTMONTHTIMER 1 /* Timer ID's */
58 #define MC_PREVMONTHTIMER 2
60 typedef struct
62 COLORREF bk;
63 COLORREF txt;
64 COLORREF titlebk;
65 COLORREF titletxt;
66 COLORREF monthbk;
67 COLORREF trailingtxt;
68 HFONT hFont;
69 HFONT hBoldFont;
70 int textHeight;
71 int textWidth;
72 int height_increment;
73 int width_increment;
74 int left_offset;
75 int top_offset;
76 int firstDayplace; /* place of the first day of the current month */
77 int delta; /* scroll rate; # of months that the */
78 /* control moves when user clicks a scroll button */
79 int visible; /* # of months visible */
80 int firstDay; /* Start month calendar with firstDay's day */
81 int monthRange;
82 MONTHDAYSTATE *monthdayState;
83 SYSTEMTIME todaysDate;
84 DWORD currentMonth;
85 DWORD currentYear;
86 int status; /* See MC_SEL flags */
87 int curSelDay; /* current selected day */
88 int firstSelDay; /* first selected day */
89 int maxSelCount;
90 SYSTEMTIME minSel;
91 SYSTEMTIME maxSel;
92 DWORD rangeValid;
93 SYSTEMTIME minDate;
94 SYSTEMTIME maxDate;
96 RECT rcClient; /* rect for whole client area */
97 RECT rcDraw; /* rect for drawable portion of client area */
98 RECT title; /* rect for the header above the calendar */
99 RECT titlebtnnext; /* the `next month' button in the header */
100 RECT titlebtnprev; /* the `prev month' button in the header */
101 RECT titlemonth; /* the `month name' txt in the header */
102 RECT titleyear; /* the `year number' txt in the header */
103 RECT wdays; /* week days at top */
104 RECT days; /* calendar area */
105 RECT weeknums; /* week numbers at left side */
106 RECT todayrect; /* `today: xx/xx/xx' text rect */
107 HWND hwndNotify; /* Window to receive the notifications */
108 HWND hWndYearEdit; /* Window Handle of edit box to handle years */
109 HWND hWndYearUpDown;/* Window Handle of updown box to handle years */
110 } MONTHCAL_INFO, *LPMONTHCAL_INFO;
113 /* Offsets of days in the week to the weekday of january 1. */
114 static const int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
117 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongPtrW(hwnd, 0))
119 /* helper functions */
121 /* returns the number of days in any given month, checking for leap days */
122 /* january is 1, december is 12 */
123 int MONTHCAL_MonthLength(int month, int year)
125 const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
126 /*Wrap around, this eases handling*/
127 if(month == 0)
128 month = 12;
129 if(month == 13)
130 month = 1;
132 /* if we have a leap year add 1 day to February */
133 /* a leap year is a year either divisible by 400 */
134 /* or divisible by 4 and not by 100 */
135 if(month == 2) { /* February */
136 return mdays[month - 1] + ((year%400 == 0) ? 1 : ((year%100 != 0) &&
137 (year%4 == 0)) ? 1 : 0);
139 else {
140 return mdays[month - 1];
145 /* make sure that time is valid */
146 static int MONTHCAL_ValidateTime(SYSTEMTIME time)
148 if(time.wMonth > 12) return FALSE;
149 if(time.wDayOfWeek > 6) return FALSE;
150 if(time.wDay > MONTHCAL_MonthLength(time.wMonth, time.wYear))
151 return FALSE;
152 if(time.wHour > 23) return FALSE;
153 if(time.wMinute > 59) return FALSE;
154 if(time.wSecond > 59) return FALSE;
155 if(time.wMilliseconds > 999) return FALSE;
157 return TRUE;
161 void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to)
163 to->wYear = from->wYear;
164 to->wMonth = from->wMonth;
165 to->wDayOfWeek = from->wDayOfWeek;
166 to->wDay = from->wDay;
167 to->wHour = from->wHour;
168 to->wMinute = from->wMinute;
169 to->wSecond = from->wSecond;
170 to->wMilliseconds = from->wMilliseconds;
174 /* Note:Depending on DST, this may be offset by a day.
175 Need to find out if we're on a DST place & adjust the clock accordingly.
176 Above function assumes we have a valid data.
177 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
178 0 = Monday.
181 /* returns the day in the week(0 == monday, 6 == sunday) */
182 /* day(1 == 1st, 2 == 2nd... etc), year is the year value */
183 static int MONTHCAL_CalculateDayOfWeek(DWORD day, DWORD month, DWORD year)
185 year-=(month < 3);
187 return((year + year/4 - year/100 + year/400 +
188 DayOfWeekTable[month-1] + day - 1 ) % 7);
191 /* From a given point, calculate the row (weekpos), column(daypos)
192 and day in the calendar. day== 0 mean the last day of tha last month
194 static int MONTHCAL_CalcDayFromPos(MONTHCAL_INFO *infoPtr, int x, int y,
195 int *daypos,int *weekpos)
197 int retval, firstDay;
199 /* if the point is outside the x bounds of the window put
200 it at the boundry */
201 if(x > infoPtr->rcClient.right) {
202 x = infoPtr->rcClient.right ;
205 *daypos = (x - infoPtr->days.left ) / infoPtr->width_increment;
206 *weekpos = (y - infoPtr->days.top ) / infoPtr->height_increment;
208 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear)+6 - infoPtr->firstDay)%7;
209 retval = *daypos + (7 * *weekpos) - firstDay;
210 return retval;
213 /* day is the day of the month, 1 == 1st day of the month */
214 /* sets x and y to be the position of the day */
215 /* x == day, y == week where(0,0) == firstDay, 1st week */
216 static void MONTHCAL_CalcDayXY(MONTHCAL_INFO *infoPtr, int day, int month,
217 int *x, int *y)
219 int firstDay, prevMonth;
221 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear) +6 - infoPtr->firstDay)%7;
223 if(month==infoPtr->currentMonth) {
224 *x = (day + firstDay) % 7;
225 *y = (day + firstDay - *x) / 7;
226 return;
228 if(month < infoPtr->currentMonth) {
229 prevMonth = month - 1;
230 if(prevMonth==0)
231 prevMonth = 12;
233 *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay) % 7;
234 *y = 0;
235 return;
238 *y = MONTHCAL_MonthLength(month, infoPtr->currentYear - 1) / 7;
239 *x = (day + firstDay + MONTHCAL_MonthLength(month,
240 infoPtr->currentYear)) % 7;
244 /* x: column(day), y: row(week) */
245 static void MONTHCAL_CalcDayRect(MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
247 r->left = infoPtr->days.left + x * infoPtr->width_increment;
248 r->right = r->left + infoPtr->width_increment;
249 r->top = infoPtr->days.top + y * infoPtr->height_increment;
250 r->bottom = r->top + infoPtr->textHeight;
254 /* sets the RECT struct r to the rectangle around the day and month */
255 /* day is the day value of the month(1 == 1st), month is the month */
256 /* value(january == 1, december == 12) */
257 static inline void MONTHCAL_CalcPosFromDay(MONTHCAL_INFO *infoPtr,
258 int day, int month, RECT *r)
260 int x, y;
262 MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
263 MONTHCAL_CalcDayRect(infoPtr, r, x, y);
267 /* day is the day in the month(1 == 1st of the month) */
268 /* month is the month value(1 == january, 12 == december) */
269 static void MONTHCAL_CircleDay(HDC hdc, MONTHCAL_INFO *infoPtr, int day,
270 int month)
272 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
273 HPEN hOldPen2 = SelectObject(hdc, hRedPen);
274 POINT points[13];
275 int x, y;
276 RECT day_rect;
279 MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
281 x = day_rect.left;
282 y = day_rect.top;
284 points[0].x = x;
285 points[0].y = y - 1;
286 points[1].x = x + 0.8 * infoPtr->width_increment;
287 points[1].y = y - 1;
288 points[2].x = x + 0.9 * infoPtr->width_increment;
289 points[2].y = y;
290 points[3].x = x + infoPtr->width_increment;
291 points[3].y = y + 0.5 * infoPtr->height_increment;
293 points[4].x = x + infoPtr->width_increment;
294 points[4].y = y + 0.9 * infoPtr->height_increment;
295 points[5].x = x + 0.6 * infoPtr->width_increment;
296 points[5].y = y + 0.9 * infoPtr->height_increment;
297 points[6].x = x + 0.5 * infoPtr->width_increment;
298 points[6].y = y + 0.9 * infoPtr->height_increment; /* bring the bottom up just
299 a hair to fit inside the day rectangle */
301 points[7].x = x + 0.2 * infoPtr->width_increment;
302 points[7].y = y + 0.8 * infoPtr->height_increment;
303 points[8].x = x + 0.1 * infoPtr->width_increment;
304 points[8].y = y + 0.8 * infoPtr->height_increment;
305 points[9].x = x;
306 points[9].y = y + 0.5 * infoPtr->height_increment;
308 points[10].x = x + 0.1 * infoPtr->width_increment;
309 points[10].y = y + 0.2 * infoPtr->height_increment;
310 points[11].x = x + 0.2 * infoPtr->width_increment;
311 points[11].y = y + 0.3 * infoPtr->height_increment;
312 points[12].x = x + 0.4 * infoPtr->width_increment;
313 points[12].y = y + 0.2 * infoPtr->height_increment;
315 PolyBezier(hdc, points, 13);
316 DeleteObject(hRedPen);
317 SelectObject(hdc, hOldPen2);
321 static void MONTHCAL_DrawDay(HDC hdc, MONTHCAL_INFO *infoPtr, int day, int month,
322 int x, int y, int bold)
324 char buf[10];
325 RECT r;
326 static int haveBoldFont, haveSelectedDay = FALSE;
327 HBRUSH hbr;
328 HPEN hNewPen, hOldPen = 0;
329 COLORREF oldCol = 0;
330 COLORREF oldBk = 0;
332 sprintf(buf, "%d", day);
334 /* No need to check styles: when selection is not valid, it is set to zero.
335 * 1<day<31, so evertyhing's OK.
338 MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
340 if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
341 && (month==infoPtr->currentMonth)) {
342 HRGN hrgn;
343 RECT r2;
345 TRACE("%d %d %d\n",day, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
346 TRACE("%ld %ld %ld %ld\n", r.left, r.top, r.right, r.bottom);
347 oldCol = SetTextColor(hdc, infoPtr->monthbk);
348 oldBk = SetBkColor(hdc, infoPtr->trailingtxt);
349 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
350 hrgn = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
351 FillRgn(hdc, hrgn, hbr);
353 /* FIXME: this may need to be changed now b/c of the other
354 drawing changes 11/3/99 CMM */
355 r2.left = r.left - 0.25 * infoPtr->textWidth;
356 r2.top = r.top;
357 r2.right = r.left + 0.5 * infoPtr->textWidth;
358 r2.bottom = r.bottom;
359 if(haveSelectedDay) FillRect(hdc, &r2, hbr);
360 haveSelectedDay = TRUE;
361 } else {
362 haveSelectedDay = FALSE;
365 /* need to add some code for multiple selections */
367 if((bold) &&(!haveBoldFont)) {
368 SelectObject(hdc, infoPtr->hBoldFont);
369 haveBoldFont = TRUE;
371 if((!bold) &&(haveBoldFont)) {
372 SelectObject(hdc, infoPtr->hFont);
373 haveBoldFont = FALSE;
376 if(haveSelectedDay) {
377 SetTextColor(hdc, oldCol);
378 SetBkColor(hdc, oldBk);
381 SetBkMode(hdc,TRANSPARENT);
382 DrawTextA(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
384 /* draw a rectangle around the currently selected days text */
385 if((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth)) {
386 hNewPen = CreatePen(PS_ALTERNATE, 0, GetSysColor(COLOR_WINDOWTEXT) );
387 hbr = GetSysColorBrush(COLOR_WINDOWTEXT);
388 FrameRect(hdc, &r, hbr);
389 SelectObject(hdc, hOldPen);
394 /* CHECKME: For `todays date', do we need to check the locale?*/
395 static void MONTHCAL_Refresh(HWND hwnd, HDC hdc, PAINTSTRUCT* ps)
397 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
398 RECT *rcClient=&infoPtr->rcClient;
399 RECT *rcDraw=&infoPtr->rcDraw;
400 RECT *title=&infoPtr->title;
401 RECT *prev=&infoPtr->titlebtnprev;
402 RECT *next=&infoPtr->titlebtnnext;
403 RECT *titlemonth=&infoPtr->titlemonth;
404 RECT *titleyear=&infoPtr->titleyear;
405 RECT dayrect;
406 RECT *days=&dayrect;
407 RECT rtoday;
408 int i, j, m, mask, day, firstDay, weeknum, weeknum1,prevMonth;
409 int textHeight = infoPtr->textHeight, textWidth = infoPtr->textWidth;
410 SIZE size;
411 HBRUSH hbr;
412 HFONT currentFont;
413 /* LOGFONTA logFont; */
414 char buf[20];
415 char buf1[20];
416 char buf2[32];
417 COLORREF oldTextColor, oldBkColor;
418 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
419 RECT rcTemp;
420 RECT rcDay; /* used in MONTHCAL_CalcDayRect() */
421 SYSTEMTIME localtime;
422 int startofprescal;
424 oldTextColor = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
427 /* fill background */
428 hbr = CreateSolidBrush (infoPtr->bk);
429 FillRect(hdc, rcClient, hbr);
430 DeleteObject(hbr);
432 /* draw header */
433 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
435 hbr = CreateSolidBrush(infoPtr->titlebk);
436 FillRect(hdc, title, hbr);
437 DeleteObject(hbr);
440 /* if the previous button is pressed draw it depressed */
441 if(IntersectRect(&rcTemp, &(ps->rcPaint), prev))
443 if((infoPtr->status & MC_PREVPRESSED))
444 DrawFrameControl(hdc, prev, DFC_SCROLL,
445 DFCS_SCROLLLEFT | DFCS_PUSHED |
446 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
447 else /* if the previous button is pressed draw it depressed */
448 DrawFrameControl(hdc, prev, DFC_SCROLL,
449 DFCS_SCROLLLEFT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
452 /* if next button is depressed draw it depressed */
453 if(IntersectRect(&rcTemp, &(ps->rcPaint), next))
455 if((infoPtr->status & MC_NEXTPRESSED))
456 DrawFrameControl(hdc, next, DFC_SCROLL,
457 DFCS_SCROLLRIGHT | DFCS_PUSHED |
458 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
459 else /* if the next button is pressed draw it depressed */
460 DrawFrameControl(hdc, next, DFC_SCROLL,
461 DFCS_SCROLLRIGHT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
464 oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
465 SetTextColor(hdc, infoPtr->titletxt);
466 currentFont = SelectObject(hdc, infoPtr->hBoldFont);
468 /* titlemonth->left and right are set in MONTHCAL_UpdateSize */
469 titlemonth->left = title->left;
470 titlemonth->right = title->right;
472 GetLocaleInfoA( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+infoPtr->currentMonth -1,
473 buf1,sizeof(buf1));
474 sprintf(buf, "%s %ld", buf1, infoPtr->currentYear);
476 if(IntersectRect(&rcTemp, &(ps->rcPaint), titlemonth))
478 DrawTextA(hdc, buf, strlen(buf), titlemonth,
479 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
482 SelectObject(hdc, infoPtr->hFont);
484 /* titlemonth left/right contained rect for whole titletxt('June 1999')
485 * MCM_HitTestInfo wants month & year rects, so prepare these now.
486 *(no, we can't draw them separately; the whole text is centered)
488 GetTextExtentPoint32A(hdc, buf, strlen(buf), &size);
489 titlemonth->left = title->right / 2 - size.cx / 2;
490 titleyear->right = title->right / 2 + size.cx / 2;
491 GetTextExtentPoint32A(hdc, buf1, strlen(buf1), &size);
492 titlemonth->right = titlemonth->left + size.cx;
493 titleyear->left = titlemonth->right;
495 /* draw month area */
496 rcTemp.top=infoPtr->wdays.top;
497 rcTemp.left=infoPtr->wdays.left;
498 rcTemp.bottom=infoPtr->todayrect.bottom;
499 rcTemp.right =infoPtr->todayrect.right;
500 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcTemp))
502 hbr = CreateSolidBrush(infoPtr->monthbk);
503 FillRect(hdc, &rcTemp, hbr);
504 DeleteObject(hbr);
507 /* draw line under day abbreviatons */
509 MoveToEx(hdc, infoPtr->days.left + 3, title->bottom + textHeight + 1, NULL);
511 LineTo(hdc, rcDraw->right - 3, title->bottom + textHeight + 1);
513 prevMonth = infoPtr->currentMonth - 1;
514 if(prevMonth == 0) /* if currentMonth is january(1) prevMonth is */
515 prevMonth = 12; /* december(12) of the previous year */
517 infoPtr->wdays.left = infoPtr->days.left = infoPtr->weeknums.right;
518 /* draw day abbreviations */
520 SetBkColor(hdc, infoPtr->monthbk);
521 SetTextColor(hdc, infoPtr->trailingtxt);
523 /* copy this rect so we can change the values without changing */
524 /* the original version */
525 days->left = infoPtr->wdays.left;
526 days->right = days->left + infoPtr->width_increment;
527 days->top = infoPtr->wdays.top;
528 days->bottom = infoPtr->wdays.bottom;
530 i = infoPtr->firstDay;
532 for(j=0; j<7; j++) {
533 GetLocaleInfoA( LOCALE_USER_DEFAULT,LOCALE_SABBREVDAYNAME1 + (i +j)%7,
534 buf,sizeof(buf));
535 DrawTextA(hdc, buf, strlen(buf), days,
536 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
537 days->left+=infoPtr->width_increment;
538 days->right+=infoPtr->width_increment;
541 /* draw day numbers; first, the previous month */
543 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
545 day = MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) +
546 (infoPtr->firstDay + 7 - firstDay)%7 + 1;
547 if (day > MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear))
548 day -=7;
549 startofprescal = day;
550 mask = 1<<(day-1);
552 i = 0;
553 m = 0;
554 while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear)) {
555 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
556 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
558 MONTHCAL_DrawDay(hdc, infoPtr, day, prevMonth, i, 0,
559 infoPtr->monthdayState[m] & mask);
562 mask<<=1;
563 day++;
564 i++;
567 /* draw `current' month */
569 day = 1; /* start at the beginning of the current month */
571 infoPtr->firstDayplace = i;
572 SetTextColor(hdc, infoPtr->txt);
573 m++;
574 mask = 1;
576 /* draw the first week of the current month */
577 while(i<7) {
578 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
579 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
582 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, 0,
583 infoPtr->monthdayState[m] & mask);
585 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
586 (day==infoPtr->todaysDate.wDay) &&
587 (infoPtr->currentYear == infoPtr->todaysDate.wYear)) {
588 if(!(dwStyle & MCS_NOTODAYCIRCLE))
589 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
593 mask<<=1;
594 day++;
595 i++;
598 j = 1; /* move to the 2nd week of the current month */
599 i = 0; /* move back to sunday */
600 while(day <= MONTHCAL_MonthLength(infoPtr->currentMonth, infoPtr->currentYear)) {
601 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
602 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
604 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, j,
605 infoPtr->monthdayState[m] & mask);
607 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
608 (day==infoPtr->todaysDate.wDay) &&
609 (infoPtr->currentYear == infoPtr->todaysDate.wYear))
610 if(!(dwStyle & MCS_NOTODAYCIRCLE))
611 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
613 mask<<=1;
614 day++;
615 i++;
616 if(i>6) { /* past saturday, goto the next weeks sunday */
617 i = 0;
618 j++;
622 /* draw `next' month */
624 day = 1; /* start at the first day of the next month */
625 m++;
626 mask = 1;
628 SetTextColor(hdc, infoPtr->trailingtxt);
629 while((i<7) &&(j<6)) {
630 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
631 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
633 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth + 1, i, j,
634 infoPtr->monthdayState[m] & mask);
637 mask<<=1;
638 day++;
639 i++;
640 if(i==7) { /* past saturday, go to next week's sunday */
641 i = 0;
642 j++;
645 SetTextColor(hdc, infoPtr->txt);
648 /* draw `today' date if style allows it, and draw a circle before today's
649 * date if necessary */
651 if(!(dwStyle & MCS_NOTODAY)) {
652 int offset = 0;
653 if(!(dwStyle & MCS_NOTODAYCIRCLE)) {
654 /*day is the number of days from nextmonth we put on the calendar */
655 MONTHCAL_CircleDay(hdc, infoPtr,
656 day+MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear),
657 infoPtr->currentMonth);
658 offset+=textWidth;
660 if (!LoadStringA(COMCTL32_hModule,IDM_TODAY,buf1,sizeof(buf1)))
662 WARN("Can't load resource\n");
663 strcpy(buf1,"Today:");
665 MONTHCAL_CalcDayRect(infoPtr, &rtoday, 1, 6);
666 MONTHCAL_CopyTime(&infoPtr->todaysDate,&localtime);
667 GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&localtime,NULL,buf2,sizeof(buf2));
668 sprintf(buf, "%s %s", buf1,buf2);
669 SelectObject(hdc, infoPtr->hBoldFont);
671 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rtoday))
673 DrawTextA(hdc, buf, -1, &rtoday, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE);
674 DrawTextA(hdc, buf, -1, &rtoday, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
676 SelectObject(hdc, infoPtr->hFont);
679 /*eventually draw week numbers*/
680 if(dwStyle & MCS_WEEKNUMBERS) {
681 /* display weeknumbers*/
682 int mindays;
684 /* Rules what week to call the first week of a new year:
685 LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?):
686 The week containing Jan 1 is the first week of year
687 LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany):
688 First week of year must contain 4 days of the new year
689 LOCALE_IFIRSTWEEKOFYEAR == 1 (what contries?)
690 The first week of the year must contain only days of the new year
692 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR,
693 buf, sizeof(buf));
694 sscanf(buf, "%d", &weeknum);
695 switch (weeknum)
697 case 1: mindays = 6;
698 break;
699 case 2: mindays = 3;
700 break;
701 case 0:
702 default:
703 mindays = 0;
705 if (infoPtr->currentMonth < 2)
707 /* calculate all those exceptions for january */
708 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
709 if ((infoPtr->firstDay +7 - weeknum1)%7 > mindays)
710 weeknum =1;
711 else
713 weeknum = 0;
714 for(i=0; i<11; i++)
715 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear-1);
716 weeknum +=startofprescal+ 7;
717 weeknum /=7;
718 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear-1);
719 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
720 weeknum++;
723 else
725 weeknum = 0;
726 for(i=0; i<prevMonth-1; i++)
727 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear);
728 weeknum +=startofprescal+ 7;
729 weeknum /=7;
730 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
731 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
732 weeknum++;
734 days->left = infoPtr->weeknums.left;
735 days->right = infoPtr->weeknums.right;
736 days->top = infoPtr->weeknums.top;
737 days->bottom = days->top +infoPtr->height_increment;
738 for(i=0; i<6; i++) {
739 if((i==0)&&(weeknum>50))
741 sprintf(buf, "%d", weeknum);
742 weeknum=0;
744 else if((i==5)&&(weeknum>47))
746 sprintf(buf, "%d", 1);
748 else
749 sprintf(buf, "%d", weeknum + i);
750 DrawTextA(hdc, buf, -1, days, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
751 days->top+=infoPtr->height_increment;
752 days->bottom+=infoPtr->height_increment;
755 MoveToEx(hdc, infoPtr->weeknums.right, infoPtr->weeknums.top + 3 , NULL);
756 LineTo(hdc, infoPtr->weeknums.right, infoPtr->weeknums.bottom );
759 /* currentFont was font at entering Refresh */
761 SetBkColor(hdc, oldBkColor);
762 SelectObject(hdc, currentFont);
763 SetTextColor(hdc, oldTextColor);
767 static LRESULT
768 MONTHCAL_GetMinReqRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
770 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
771 LPRECT lpRect = (LPRECT) lParam;
772 TRACE("%x %lx\n", wParam, lParam);
774 /* validate parameters */
776 if((infoPtr==NULL) ||(lpRect == NULL) ) return FALSE;
778 lpRect->left = infoPtr->rcClient.left;
779 lpRect->right = infoPtr->rcClient.right;
780 lpRect->top = infoPtr->rcClient.top;
781 lpRect->bottom = infoPtr->rcClient.bottom;
782 return TRUE;
786 static LRESULT
787 MONTHCAL_GetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
789 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
791 TRACE("%x %lx\n", wParam, lParam);
793 switch((int)wParam) {
794 case MCSC_BACKGROUND:
795 return infoPtr->bk;
796 case MCSC_TEXT:
797 return infoPtr->txt;
798 case MCSC_TITLEBK:
799 return infoPtr->titlebk;
800 case MCSC_TITLETEXT:
801 return infoPtr->titletxt;
802 case MCSC_MONTHBK:
803 return infoPtr->monthbk;
804 case MCSC_TRAILINGTEXT:
805 return infoPtr->trailingtxt;
808 return -1;
812 static LRESULT
813 MONTHCAL_SetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
815 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
816 int prev = -1;
818 TRACE("%x %lx\n", wParam, lParam);
820 switch((int)wParam) {
821 case MCSC_BACKGROUND:
822 prev = infoPtr->bk;
823 infoPtr->bk = (COLORREF)lParam;
824 break;
825 case MCSC_TEXT:
826 prev = infoPtr->txt;
827 infoPtr->txt = (COLORREF)lParam;
828 break;
829 case MCSC_TITLEBK:
830 prev = infoPtr->titlebk;
831 infoPtr->titlebk = (COLORREF)lParam;
832 break;
833 case MCSC_TITLETEXT:
834 prev=infoPtr->titletxt;
835 infoPtr->titletxt = (COLORREF)lParam;
836 break;
837 case MCSC_MONTHBK:
838 prev = infoPtr->monthbk;
839 infoPtr->monthbk = (COLORREF)lParam;
840 break;
841 case MCSC_TRAILINGTEXT:
842 prev = infoPtr->trailingtxt;
843 infoPtr->trailingtxt = (COLORREF)lParam;
844 break;
847 InvalidateRect(hwnd, NULL, FALSE);
848 return prev;
852 static LRESULT
853 MONTHCAL_GetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
855 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
857 TRACE("%x %lx\n", wParam, lParam);
859 if(infoPtr->delta)
860 return infoPtr->delta;
861 else
862 return infoPtr->visible;
866 static LRESULT
867 MONTHCAL_SetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
869 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
870 int prev = infoPtr->delta;
872 TRACE("%x %lx\n", wParam, lParam);
874 infoPtr->delta = (int)wParam;
875 return prev;
879 static LRESULT
880 MONTHCAL_GetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
882 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
884 return infoPtr->firstDay;
888 /* sets the first day of the week that will appear in the control */
889 /* 0 == Monday, 6 == Sunday */
890 /* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
891 /* FIXME: we need more error checking here */
892 static LRESULT
893 MONTHCAL_SetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
895 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
896 int prev = infoPtr->firstDay;
897 char buf[40];
898 int day;
900 TRACE("%x %lx\n", wParam, lParam);
902 if((lParam >= 0) && (lParam < 7)) {
903 infoPtr->firstDay = (int)lParam;
905 else
907 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK,
908 buf, sizeof(buf));
909 TRACE("%s %d\n", buf, strlen(buf));
910 if(sscanf(buf, "%d", &day) == 1)
911 infoPtr->firstDay = day;
912 else
913 infoPtr->firstDay = 0;
915 return prev;
919 /* FIXME: fill this in */
920 static LRESULT
921 MONTHCAL_GetMonthRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
923 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
925 TRACE("%x %lx\n", wParam, lParam);
926 FIXME("stub\n");
928 return infoPtr->monthRange;
932 static LRESULT
933 MONTHCAL_GetMaxTodayWidth(HWND hwnd)
935 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
937 return(infoPtr->todayrect.right - infoPtr->todayrect.left);
941 /* FIXME: are validated times taken from current date/time or simply
942 * copied?
943 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
944 * adjusting range with MCM_SETRANGE
947 static LRESULT
948 MONTHCAL_SetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
950 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
951 SYSTEMTIME *lprgSysTimeArray=(SYSTEMTIME *)lParam;
952 int prev;
954 TRACE("%x %lx\n", wParam, lParam);
956 if(wParam & GDTR_MAX) {
957 if(MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
958 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxDate);
959 infoPtr->rangeValid|=GDTR_MAX;
960 } else {
961 GetSystemTime(&infoPtr->todaysDate);
962 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
965 if(wParam & GDTR_MIN) {
966 if(MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
967 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->maxDate);
968 infoPtr->rangeValid|=GDTR_MIN;
969 } else {
970 GetSystemTime(&infoPtr->todaysDate);
971 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
975 prev = infoPtr->monthRange;
976 infoPtr->monthRange = infoPtr->maxDate.wMonth - infoPtr->minDate.wMonth;
978 if(infoPtr->monthRange!=prev) {
979 infoPtr->monthdayState = ReAlloc(infoPtr->monthdayState,
980 infoPtr->monthRange * sizeof(MONTHDAYSTATE));
983 return 1;
987 /* CHECKME: At the moment, we copy ranges anyway,regardless of
988 * infoPtr->rangeValid; a invalid range is simply filled with zeros in
989 * SetRange. Is this the right behavior?
992 static LRESULT
993 MONTHCAL_GetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
995 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
996 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *)lParam;
998 /* validate parameters */
1000 if((infoPtr==NULL) || (lprgSysTimeArray==NULL)) return FALSE;
1002 MONTHCAL_CopyTime(&infoPtr->maxDate, &lprgSysTimeArray[1]);
1003 MONTHCAL_CopyTime(&infoPtr->minDate, &lprgSysTimeArray[0]);
1005 return infoPtr->rangeValid;
1009 static LRESULT
1010 MONTHCAL_SetDayState(HWND hwnd, WPARAM wParam, LPARAM lParam)
1013 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1014 int i, iMonths = (int)wParam;
1015 MONTHDAYSTATE *dayStates = (LPMONTHDAYSTATE)lParam;
1017 TRACE("%x %lx\n", wParam, lParam);
1018 if(iMonths!=infoPtr->monthRange) return 0;
1020 for(i=0; i<iMonths; i++)
1021 infoPtr->monthdayState[i] = dayStates[i];
1022 return 1;
1025 static LRESULT
1026 MONTHCAL_GetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
1028 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1029 SYSTEMTIME *lpSel = (SYSTEMTIME *) lParam;
1031 TRACE("%x %lx\n", wParam, lParam);
1032 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1033 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1035 MONTHCAL_CopyTime(&infoPtr->minSel, lpSel);
1036 TRACE("%d/%d/%d\n", lpSel->wYear, lpSel->wMonth, lpSel->wDay);
1037 return TRUE;
1040 /* FIXME: if the specified date is not visible, make it visible */
1041 /* FIXME: redraw? */
1042 static LRESULT
1043 MONTHCAL_SetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
1045 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1046 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
1048 TRACE("%x %lx\n", wParam, lParam);
1049 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1050 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1052 infoPtr->currentMonth=lpSel->wMonth;
1053 infoPtr->currentYear=lpSel->wYear;
1055 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
1056 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
1058 InvalidateRect(hwnd, NULL, FALSE);
1060 return TRUE;
1064 static LRESULT
1065 MONTHCAL_GetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
1067 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1069 TRACE("%x %lx\n", wParam, lParam);
1070 return infoPtr->maxSelCount;
1074 static LRESULT
1075 MONTHCAL_SetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
1077 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1079 TRACE("%x %lx\n", wParam, lParam);
1080 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1081 infoPtr->maxSelCount = wParam;
1084 return TRUE;
1088 static LRESULT
1089 MONTHCAL_GetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
1091 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1092 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1094 TRACE("%x %lx\n", wParam, lParam);
1096 /* validate parameters */
1098 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1100 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT)
1102 MONTHCAL_CopyTime(&infoPtr->maxSel, &lprgSysTimeArray[1]);
1103 MONTHCAL_CopyTime(&infoPtr->minSel, &lprgSysTimeArray[0]);
1104 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1105 return TRUE;
1108 return FALSE;
1112 static LRESULT
1113 MONTHCAL_SetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
1115 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1116 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1118 TRACE("%x %lx\n", wParam, lParam);
1120 /* validate parameters */
1122 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1124 if(GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)
1126 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxSel);
1127 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minSel);
1128 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1129 return TRUE;
1132 return FALSE;
1136 static LRESULT
1137 MONTHCAL_GetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
1139 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1140 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1142 TRACE("%x %lx\n", wParam, lParam);
1144 /* validate parameters */
1146 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
1147 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
1148 return TRUE;
1152 static LRESULT
1153 MONTHCAL_SetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
1155 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1156 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1158 TRACE("%x %lx\n", wParam, lParam);
1160 /* validate parameters */
1162 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
1163 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
1164 InvalidateRect(hwnd, NULL, FALSE);
1165 return TRUE;
1169 static LRESULT
1170 MONTHCAL_HitTest(HWND hwnd, LPARAM lParam)
1172 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1173 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
1174 UINT x,y;
1175 DWORD retval;
1176 int day,wday,wnum;
1179 x = lpht->pt.x;
1180 y = lpht->pt.y;
1181 retval = MCHT_NOWHERE;
1184 /* Comment in for debugging...
1185 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,
1186 infoPtr->wdays.left, infoPtr->wdays.right,
1187 infoPtr->wdays.top, infoPtr->wdays.bottom,
1188 infoPtr->days.left, infoPtr->days.right,
1189 infoPtr->days.top, infoPtr->days.bottom,
1190 infoPtr->todayrect.left, infoPtr->todayrect.right,
1191 infoPtr->todayrect.top, infoPtr->todayrect.bottom,
1192 infoPtr->weeknums.left, infoPtr->weeknums.right,
1193 infoPtr->weeknums.top, infoPtr->weeknums.bottom);
1196 /* are we in the header? */
1198 if(PtInRect(&infoPtr->title, lpht->pt)) {
1199 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1200 retval = MCHT_TITLEBTNPREV;
1201 goto done;
1203 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1204 retval = MCHT_TITLEBTNNEXT;
1205 goto done;
1207 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1208 retval = MCHT_TITLEMONTH;
1209 goto done;
1211 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1212 retval = MCHT_TITLEYEAR;
1213 goto done;
1216 retval = MCHT_TITLE;
1217 goto done;
1220 day = MONTHCAL_CalcDayFromPos(infoPtr,x,y,&wday,&wnum);
1221 if(PtInRect(&infoPtr->wdays, lpht->pt)) {
1222 retval = MCHT_CALENDARDAY;
1223 lpht->st.wYear = infoPtr->currentYear;
1224 lpht->st.wMonth = (day < 1)? infoPtr->currentMonth -1 : infoPtr->currentMonth;
1225 lpht->st.wDay = (day < 1)?
1226 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day : day;
1227 goto done;
1229 if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
1230 retval = MCHT_CALENDARWEEKNUM;
1231 lpht->st.wYear = infoPtr->currentYear;
1232 lpht->st.wMonth = (day < 1) ? infoPtr->currentMonth -1 :
1233 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1234 infoPtr->currentMonth +1 :infoPtr->currentMonth;
1235 lpht->st.wDay = (day < 1 ) ?
1236 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day :
1237 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1238 day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) : day;
1239 goto done;
1241 if(PtInRect(&infoPtr->days, lpht->pt))
1243 lpht->st.wYear = infoPtr->currentYear;
1244 if ( day < 1)
1246 retval = MCHT_CALENDARDATEPREV;
1247 lpht->st.wMonth = infoPtr->currentMonth - 1;
1248 if (lpht->st.wMonth <1)
1250 lpht->st.wMonth = 12;
1251 lpht->st.wYear--;
1253 lpht->st.wDay = MONTHCAL_MonthLength(lpht->st.wMonth,lpht->st.wYear) -day;
1255 else if (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear))
1257 retval = MCHT_CALENDARDATENEXT;
1258 lpht->st.wMonth = infoPtr->currentMonth + 1;
1259 if (lpht->st.wMonth <12)
1261 lpht->st.wMonth = 1;
1262 lpht->st.wYear++;
1264 lpht->st.wDay = day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) ;
1266 else {
1267 retval = MCHT_CALENDARDATE;
1268 lpht->st.wMonth = infoPtr->currentMonth;
1269 lpht->st.wDay = day;
1271 goto done;
1273 if(PtInRect(&infoPtr->todayrect, lpht->pt)) {
1274 retval = MCHT_TODAYLINK;
1275 goto done;
1279 /* Hit nothing special? What's left must be background :-) */
1281 retval = MCHT_CALENDARBK;
1282 done:
1283 lpht->uHit = retval;
1284 return retval;
1288 static void MONTHCAL_GoToNextMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1290 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1292 TRACE("MONTHCAL_GoToNextMonth\n");
1294 infoPtr->currentMonth++;
1295 if(infoPtr->currentMonth > 12) {
1296 infoPtr->currentYear++;
1297 infoPtr->currentMonth = 1;
1300 if(dwStyle & MCS_DAYSTATE) {
1301 NMDAYSTATE nmds;
1302 int i;
1304 nmds.nmhdr.hwndFrom = hwnd;
1305 nmds.nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
1306 nmds.nmhdr.code = MCN_GETDAYSTATE;
1307 nmds.cDayState = infoPtr->monthRange;
1308 nmds.prgDayState = Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1310 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1311 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1312 for(i=0; i<infoPtr->monthRange; i++)
1313 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1318 static void MONTHCAL_GoToPrevMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1320 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1322 TRACE("MONTHCAL_GoToPrevMonth\n");
1324 infoPtr->currentMonth--;
1325 if(infoPtr->currentMonth < 1) {
1326 infoPtr->currentYear--;
1327 infoPtr->currentMonth = 12;
1330 if(dwStyle & MCS_DAYSTATE) {
1331 NMDAYSTATE nmds;
1332 int i;
1334 nmds.nmhdr.hwndFrom = hwnd;
1335 nmds.nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
1336 nmds.nmhdr.code = MCN_GETDAYSTATE;
1337 nmds.cDayState = infoPtr->monthRange;
1338 nmds.prgDayState = Alloc
1339 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1341 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1342 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1343 for(i=0; i<infoPtr->monthRange; i++)
1344 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1348 static LRESULT
1349 MONTHCAL_RButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1351 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1352 HMENU hMenu;
1353 POINT menupoint;
1354 char buf[32];
1356 hMenu = CreatePopupMenu();
1357 if (!LoadStringA(COMCTL32_hModule,IDM_GOTODAY,buf,sizeof(buf)))
1359 WARN("Can't load resource\n");
1360 strcpy(buf,"Go to Today:");
1362 AppendMenuA(hMenu, MF_STRING|MF_ENABLED,1, buf);
1363 menupoint.x=(INT)LOWORD(lParam);
1364 menupoint.y=(INT)HIWORD(lParam);
1365 ClientToScreen(hwnd, &menupoint);
1366 if( TrackPopupMenu(hMenu,TPM_RIGHTBUTTON| TPM_NONOTIFY|TPM_RETURNCMD,
1367 menupoint.x,menupoint.y,0,hwnd,NULL))
1369 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1370 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1371 InvalidateRect(hwnd, NULL, FALSE);
1373 return 0;
1376 static LRESULT
1377 MONTHCAL_LButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1379 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1380 MCHITTESTINFO ht;
1381 DWORD hit;
1382 HMENU hMenu;
1383 RECT rcDay; /* used in determining area to invalidate */
1384 char buf[32];
1385 int i;
1386 POINT menupoint;
1387 TRACE("%x %lx\n", wParam, lParam);
1389 if (infoPtr->hWndYearUpDown)
1391 infoPtr->currentYear=SendMessageA( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)0);
1392 if(!DestroyWindow(infoPtr->hWndYearUpDown))
1394 FIXME("Can't destroy Updown Control\n");
1396 else
1397 infoPtr->hWndYearUpDown=0;
1398 if(!DestroyWindow(infoPtr->hWndYearEdit))
1400 FIXME("Can't destroy Updown Control\n");
1402 else
1403 infoPtr->hWndYearEdit=0;
1404 InvalidateRect(hwnd, NULL, FALSE);
1407 ht.pt.x = (INT)LOWORD(lParam);
1408 ht.pt.y = (INT)HIWORD(lParam);
1409 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1411 /* FIXME: these flags should be checked by */
1412 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1413 /* multi-bit */
1414 if(hit ==MCHT_TITLEBTNNEXT) {
1415 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1416 infoPtr->status = MC_NEXTPRESSED;
1417 SetTimer(hwnd, MC_NEXTMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1418 InvalidateRect(hwnd, NULL, FALSE);
1419 return TRUE;
1421 if(hit == MCHT_TITLEBTNPREV){
1422 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1423 infoPtr->status = MC_PREVPRESSED;
1424 SetTimer(hwnd, MC_PREVMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1425 InvalidateRect(hwnd, NULL, FALSE);
1426 return TRUE;
1429 if(hit == MCHT_TITLEMONTH) {
1430 hMenu = CreatePopupMenu();
1432 for (i=0; i<12;i++)
1434 GetLocaleInfoA( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+i,
1435 buf,sizeof(buf));
1436 AppendMenuA(hMenu, MF_STRING|MF_ENABLED,i+1, buf);
1438 menupoint.x=infoPtr->titlemonth.right;
1439 menupoint.y=infoPtr->titlemonth.bottom;
1440 ClientToScreen(hwnd, &menupoint);
1441 i= TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD,
1442 menupoint.x,menupoint.y,0,hwnd,NULL);
1443 if ((i>0) && (i<13))
1445 infoPtr->currentMonth=i;
1446 InvalidateRect(hwnd, NULL, FALSE);
1449 if(hit == MCHT_TITLEYEAR) {
1450 infoPtr->hWndYearEdit=CreateWindowExA(0,
1451 "EDIT",
1453 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT,
1454 infoPtr->titleyear.left+3,infoPtr->titlebtnnext.top,
1455 infoPtr->titleyear.right-infoPtr->titleyear.left,
1456 infoPtr->textHeight,
1457 hwnd,
1458 NULL,
1459 NULL,
1460 NULL);
1461 infoPtr->hWndYearUpDown=CreateWindowExA(0,
1462 UPDOWN_CLASSA,
1464 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT|UDS_NOTHOUSANDS|UDS_ARROWKEYS,
1465 infoPtr->titleyear.right+6,infoPtr->titlebtnnext.top,
1467 infoPtr->textHeight,
1468 hwnd,
1469 NULL,
1470 NULL,
1471 NULL);
1472 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETRANGE, (WPARAM) 0, MAKELONG (9999, 1753));
1473 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETBUDDY, (WPARAM) infoPtr->hWndYearEdit, (LPARAM)0 );
1474 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)infoPtr->currentYear );
1475 return TRUE;
1478 if(hit == MCHT_TODAYLINK) {
1479 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1480 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1481 InvalidateRect(hwnd, NULL, FALSE);
1482 return TRUE;
1484 if(hit == MCHT_CALENDARDATE) {
1485 SYSTEMTIME selArray[2];
1486 NMSELCHANGE nmsc;
1488 MONTHCAL_CopyTime(&ht.st, &selArray[0]);
1489 MONTHCAL_CopyTime(&ht.st, &selArray[1]);
1490 MONTHCAL_SetSelRange(hwnd,0,(LPARAM) &selArray);
1491 MONTHCAL_SetCurSel(hwnd,0,(LPARAM) &selArray);
1492 TRACE("MCHT_CALENDARDATE\n");
1493 nmsc.nmhdr.hwndFrom = hwnd;
1494 nmsc.nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
1495 nmsc.nmhdr.code = MCN_SELCHANGE;
1496 MONTHCAL_CopyTime(&infoPtr->minSel,&nmsc.stSelStart);
1497 MONTHCAL_CopyTime(&infoPtr->maxSel,&nmsc.stSelEnd);
1499 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1500 (WPARAM)nmsc.nmhdr.idFrom,(LPARAM)&nmsc);
1503 /* redraw both old and new days if the selected day changed */
1504 if(infoPtr->curSelDay != ht.st.wDay) {
1505 MONTHCAL_CalcPosFromDay(infoPtr, ht.st.wDay, ht.st.wMonth, &rcDay);
1506 InvalidateRect(hwnd, &rcDay, TRUE);
1508 MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->curSelDay, infoPtr->currentMonth, &rcDay);
1509 InvalidateRect(hwnd, &rcDay, TRUE);
1512 infoPtr->firstSelDay = ht.st.wDay;
1513 infoPtr->curSelDay = ht.st.wDay;
1514 infoPtr->status = MC_SEL_LBUTDOWN;
1515 return TRUE;
1518 return 0;
1522 static LRESULT
1523 MONTHCAL_LButtonUp(HWND hwnd, WPARAM wParam, LPARAM lParam)
1525 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1526 NMSELCHANGE nmsc;
1527 NMHDR nmhdr;
1528 BOOL redraw = FALSE;
1529 MCHITTESTINFO ht;
1530 DWORD hit;
1532 TRACE("\n");
1534 if(infoPtr->status & MC_NEXTPRESSED) {
1535 KillTimer(hwnd, MC_NEXTMONTHTIMER);
1536 infoPtr->status &= ~MC_NEXTPRESSED;
1537 redraw = TRUE;
1539 if(infoPtr->status & MC_PREVPRESSED) {
1540 KillTimer(hwnd, MC_PREVMONTHTIMER);
1541 infoPtr->status &= ~MC_PREVPRESSED;
1542 redraw = TRUE;
1545 ht.pt.x = (INT)LOWORD(lParam);
1546 ht.pt.y = (INT)HIWORD(lParam);
1547 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1549 infoPtr->status = MC_SEL_LBUTUP;
1551 if(hit ==MCHT_CALENDARDATENEXT) {
1552 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1553 InvalidateRect(hwnd, NULL, FALSE);
1554 return TRUE;
1556 if(hit == MCHT_CALENDARDATEPREV){
1557 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1558 InvalidateRect(hwnd, NULL, FALSE);
1559 return TRUE;
1561 nmhdr.hwndFrom = hwnd;
1562 nmhdr.idFrom = GetWindowLongPtrW( hwnd, GWLP_ID);
1563 nmhdr.code = NM_RELEASEDCAPTURE;
1564 TRACE("Sent notification from %p to %p\n", hwnd, infoPtr->hwndNotify);
1566 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1567 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1568 /* redraw if necessary */
1569 if(redraw)
1570 InvalidateRect(hwnd, NULL, FALSE);
1571 /* only send MCN_SELECT if currently displayed month's day was selected */
1572 if(hit == MCHT_CALENDARDATE) {
1573 nmsc.nmhdr.hwndFrom = hwnd;
1574 nmsc.nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
1575 nmsc.nmhdr.code = MCN_SELECT;
1576 MONTHCAL_CopyTime(&infoPtr->minSel, &nmsc.stSelStart);
1577 MONTHCAL_CopyTime(&infoPtr->maxSel, &nmsc.stSelEnd);
1579 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1580 (WPARAM)nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1583 return 0;
1587 static LRESULT
1588 MONTHCAL_Timer(HWND hwnd, WPARAM wParam, LPARAM lParam)
1590 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1591 BOOL redraw = FALSE;
1593 TRACE(" %d\n", wParam);
1594 if(!infoPtr) return 0;
1596 switch(wParam) {
1597 case MC_NEXTMONTHTIMER:
1598 redraw = TRUE;
1599 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1600 break;
1601 case MC_PREVMONTHTIMER:
1602 redraw = TRUE;
1603 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1604 break;
1605 default:
1606 ERR("got unknown timer\n");
1609 /* redraw only if necessary */
1610 if(redraw)
1611 InvalidateRect(hwnd, NULL, FALSE);
1613 return 0;
1617 static LRESULT
1618 MONTHCAL_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1620 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1621 MCHITTESTINFO ht;
1622 int oldselday, selday, hit;
1623 RECT r;
1625 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1627 ht.pt.x = LOWORD(lParam);
1628 ht.pt.y = HIWORD(lParam);
1630 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1632 /* not on the calendar date numbers? bail out */
1633 TRACE("hit:%x\n",hit);
1634 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1636 selday = ht.st.wDay;
1637 oldselday = infoPtr->curSelDay;
1638 infoPtr->curSelDay = selday;
1639 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1641 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1642 SYSTEMTIME selArray[2];
1643 int i;
1645 MONTHCAL_GetSelRange(hwnd, 0, (LPARAM)&selArray);
1646 i = 0;
1647 if(infoPtr->firstSelDay==selArray[0].wDay) i=1;
1648 TRACE("oldRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1649 if(infoPtr->firstSelDay==selArray[1].wDay) {
1650 /* 1st time we get here: selArray[0]=selArray[1]) */
1651 /* if we're still at the first selected date, return */
1652 if(infoPtr->firstSelDay==selday) goto done;
1653 if(selday<infoPtr->firstSelDay) i = 0;
1656 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1657 if(selday>infoPtr->firstSelDay)
1658 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1659 else
1660 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1663 if(selArray[i].wDay!=selday) {
1664 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1666 selArray[i].wDay = selday;
1668 if(selArray[0].wDay>selArray[1].wDay) {
1669 DWORD tempday;
1670 tempday = selArray[1].wDay;
1671 selArray[1].wDay = selArray[0].wDay;
1672 selArray[0].wDay = tempday;
1675 MONTHCAL_SetSelRange(hwnd, 0, (LPARAM)&selArray);
1679 done:
1681 /* only redraw if the currently selected day changed */
1682 /* FIXME: this should specify a rectangle containing only the days that changed */
1683 /* using InvalidateRect */
1684 if(oldselday != infoPtr->curSelDay)
1685 InvalidateRect(hwnd, NULL, FALSE);
1687 return 0;
1691 static LRESULT
1692 MONTHCAL_Paint(HWND hwnd, WPARAM wParam)
1694 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1695 HDC hdc;
1696 PAINTSTRUCT ps;
1698 /* fill ps.rcPaint with a default rect */
1699 memcpy(&(ps.rcPaint), &(infoPtr->rcClient), sizeof(infoPtr->rcClient));
1701 hdc = (wParam==0 ? BeginPaint(hwnd, &ps) : (HDC)wParam);
1702 MONTHCAL_Refresh(hwnd, hdc, &ps);
1703 if(!wParam) EndPaint(hwnd, &ps);
1704 return 0;
1708 static LRESULT
1709 MONTHCAL_KillFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1711 TRACE("\n");
1713 InvalidateRect(hwnd, NULL, TRUE);
1715 return 0;
1719 static LRESULT
1720 MONTHCAL_SetFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1722 TRACE("\n");
1724 InvalidateRect(hwnd, NULL, FALSE);
1726 return 0;
1729 /* sets the size information */
1730 static void MONTHCAL_UpdateSize(HWND hwnd)
1732 HDC hdc = GetDC(hwnd);
1733 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1734 RECT *rcClient=&infoPtr->rcClient;
1735 RECT *rcDraw=&infoPtr->rcDraw;
1736 RECT *title=&infoPtr->title;
1737 RECT *prev=&infoPtr->titlebtnprev;
1738 RECT *next=&infoPtr->titlebtnnext;
1739 RECT *titlemonth=&infoPtr->titlemonth;
1740 RECT *titleyear=&infoPtr->titleyear;
1741 RECT *wdays=&infoPtr->wdays;
1742 RECT *weeknumrect=&infoPtr->weeknums;
1743 RECT *days=&infoPtr->days;
1744 RECT *todayrect=&infoPtr->todayrect;
1745 SIZE size;
1746 TEXTMETRICA tm;
1747 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1748 HFONT currentFont;
1749 double xdiv;
1751 currentFont = SelectObject(hdc, infoPtr->hFont);
1753 /* FIXME: need a way to determine current font, without setting it */
1755 if(infoPtr->hFont!=currentFont) {
1756 SelectObject(hdc, currentFont);
1757 infoPtr->hFont=currentFont;
1758 GetObjectA(currentFont, sizeof(LOGFONTA), &logFont);
1759 logFont.lfWeight=FW_BOLD;
1760 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1764 /* get the height and width of each day's text */
1765 GetTextMetricsA(hdc, &tm);
1766 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading;
1767 GetTextExtentPoint32A(hdc, "Sun", 3, &size);
1768 infoPtr->textWidth = size.cx + 2;
1770 /* retrieve the controls client rectangle info infoPtr->rcClient */
1771 GetClientRect(hwnd, rcClient);
1773 /* rcDraw is the rectangle the control is drawn in */
1774 rcDraw->left = rcClient->left;
1775 rcDraw->right = rcClient->right;
1776 rcDraw->top = rcClient->top;
1777 rcDraw->bottom = rcClient->bottom;
1779 /* recalculate the height and width increments and offsets */
1780 /* FIXME: We use up all available width. This will inhibit having multiple
1781 calendars in a row, like win doesn
1783 if(dwStyle & MCS_WEEKNUMBERS)
1784 xdiv=8.0;
1785 else
1786 xdiv=7.0;
1787 infoPtr->width_increment = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) / xdiv;
1788 infoPtr->height_increment = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) / 10.0;
1789 infoPtr->left_offset = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) - (infoPtr->width_increment * xdiv);
1790 infoPtr->top_offset = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) - (infoPtr->height_increment * 10.0);
1792 rcDraw->bottom = rcDraw->top + 10 * infoPtr->height_increment;
1793 /* this is correct, the control does NOT expand vertically */
1794 /* like it does horizontally */
1795 /* make sure we don't move the controls bottom out of the client */
1796 /* area */
1797 /* title line has about 3 text heights, abrev days line, 6 weeksline and today circle line*/
1798 /*if((rcDraw->top + 9 * infoPtr->textHeight + 5) < rcDraw->bottom) {
1799 rcDraw->bottom = rcDraw->top + 9 * infoPtr->textHeight + 5;
1802 /* calculate title area */
1803 title->top = rcClient->top;
1804 title->bottom = title->top + 2 * infoPtr->height_increment;
1805 title->left = rcClient->left;
1806 title->right = rcClient->right;
1808 /* set the dimensions of the next and previous buttons and center */
1809 /* the month text vertically */
1810 prev->top = next->top = title->top + 6;
1811 prev->bottom = next->bottom = title->bottom - 6;
1812 prev->left = title->left + 6;
1813 prev->right = prev->left + (title->bottom - title->top) ;
1814 next->right = title->right - 6;
1815 next->left = next->right - (title->bottom - title->top);
1817 /* titlemonth->left and right change based upon the current month */
1818 /* and are recalculated in refresh as the current month may change */
1819 /* without the control being resized */
1820 titlemonth->top = titleyear->top = title->top + (infoPtr->height_increment)/2;
1821 titlemonth->bottom = titleyear->bottom = title->bottom - (infoPtr->height_increment)/2;
1823 /* setup the dimensions of the rectangle we draw the names of the */
1824 /* days of the week in */
1825 weeknumrect->left =infoPtr->left_offset;
1826 if(dwStyle & MCS_WEEKNUMBERS)
1827 weeknumrect->right=prev->right;
1828 else
1829 weeknumrect->right=weeknumrect->left;
1830 wdays->left = days->left = weeknumrect->right;
1831 wdays->right = days->right = wdays->left + 7 * infoPtr->width_increment;
1832 wdays->top = title->bottom ;
1833 wdays->bottom = wdays->top + infoPtr->height_increment;
1835 days->top = weeknumrect->top = wdays->bottom ;
1836 days->bottom = weeknumrect->bottom = days->top + 6 * infoPtr->height_increment;
1838 todayrect->left = rcClient->left;
1839 todayrect->right = rcClient->right;
1840 todayrect->top = days->bottom;
1841 todayrect->bottom = days->bottom + infoPtr->height_increment;
1843 /* uncomment for excessive debugging
1844 TRACE("dx=%d dy=%d rcC[%d %d %d %d] t[%d %d %d %d] wd[%d %d %d %d] w[%d %d %d %d] t[%d %d %d %d]\n",
1845 infoPtr->width_increment,infoPtr->height_increment,
1846 rcClient->left, rcClient->right, rcClient->top, rcClient->bottom,
1847 title->left, title->right, title->top, title->bottom,
1848 wdays->left, wdays->right, wdays->top, wdays->bottom,
1849 days->left, days->right, days->top, days->bottom,
1850 todayrect->left,todayrect->right,todayrect->top,todayrect->bottom);
1853 /* restore the originally selected font */
1854 SelectObject(hdc, currentFont);
1856 ReleaseDC(hwnd, hdc);
1859 static LRESULT MONTHCAL_Size(HWND hwnd, int Width, int Height)
1861 TRACE("(hwnd=%p, width=%d, height=%d)\n", hwnd, Width, Height);
1863 MONTHCAL_UpdateSize(hwnd);
1865 /* invalidate client area and erase background */
1866 InvalidateRect(hwnd, NULL, TRUE);
1868 return 0;
1871 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1872 static LRESULT
1873 MONTHCAL_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
1875 MONTHCAL_INFO *infoPtr;
1876 LOGFONTA logFont;
1878 /* allocate memory for info structure */
1879 infoPtr =(MONTHCAL_INFO*)Alloc(sizeof(MONTHCAL_INFO));
1880 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
1882 if(infoPtr == NULL) {
1883 ERR( "could not allocate info memory!\n");
1884 return 0;
1886 if((MONTHCAL_INFO*)GetWindowLongPtrW(hwnd, 0) != infoPtr) {
1887 ERR( "pointer assignment error!\n");
1888 return 0;
1891 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1893 infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1894 GetObjectA(infoPtr->hFont, sizeof(LOGFONTA), &logFont);
1895 logFont.lfWeight = FW_BOLD;
1896 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1898 /* initialize info structure */
1899 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1901 GetSystemTime(&infoPtr->todaysDate);
1902 MONTHCAL_SetFirstDayOfWeek(hwnd,0,(LPARAM)-1);
1903 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1904 infoPtr->currentYear = infoPtr->todaysDate.wYear;
1905 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
1906 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
1907 infoPtr->maxDate.wYear=2050;
1908 infoPtr->minDate.wYear=1950;
1909 infoPtr->maxSelCount = 7;
1910 infoPtr->monthRange = 3;
1911 infoPtr->monthdayState = Alloc
1912 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1913 infoPtr->titlebk = GetSysColor(COLOR_ACTIVECAPTION);
1914 infoPtr->titletxt = GetSysColor(COLOR_WINDOW);
1915 infoPtr->monthbk = GetSysColor(COLOR_WINDOW);
1916 infoPtr->trailingtxt = GetSysColor(COLOR_GRAYTEXT);
1917 infoPtr->bk = GetSysColor(COLOR_WINDOW);
1918 infoPtr->txt = GetSysColor(COLOR_WINDOWTEXT);
1920 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1921 /* of the control */
1922 MONTHCAL_UpdateSize(hwnd);
1924 return 0;
1928 static LRESULT
1929 MONTHCAL_Destroy(HWND hwnd, WPARAM wParam, LPARAM lParam)
1931 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1933 /* free month calendar info data */
1934 if(infoPtr->monthdayState)
1935 Free(infoPtr->monthdayState);
1936 Free(infoPtr);
1937 SetWindowLongPtrW(hwnd, 0, 0);
1938 return 0;
1942 static LRESULT WINAPI
1943 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1945 TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1946 if (!MONTHCAL_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
1947 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
1948 switch(uMsg)
1950 case MCM_GETCURSEL:
1951 return MONTHCAL_GetCurSel(hwnd, wParam, lParam);
1953 case MCM_SETCURSEL:
1954 return MONTHCAL_SetCurSel(hwnd, wParam, lParam);
1956 case MCM_GETMAXSELCOUNT:
1957 return MONTHCAL_GetMaxSelCount(hwnd, wParam, lParam);
1959 case MCM_SETMAXSELCOUNT:
1960 return MONTHCAL_SetMaxSelCount(hwnd, wParam, lParam);
1962 case MCM_GETSELRANGE:
1963 return MONTHCAL_GetSelRange(hwnd, wParam, lParam);
1965 case MCM_SETSELRANGE:
1966 return MONTHCAL_SetSelRange(hwnd, wParam, lParam);
1968 case MCM_GETMONTHRANGE:
1969 return MONTHCAL_GetMonthRange(hwnd, wParam, lParam);
1971 case MCM_SETDAYSTATE:
1972 return MONTHCAL_SetDayState(hwnd, wParam, lParam);
1974 case MCM_GETMINREQRECT:
1975 return MONTHCAL_GetMinReqRect(hwnd, wParam, lParam);
1977 case MCM_GETCOLOR:
1978 return MONTHCAL_GetColor(hwnd, wParam, lParam);
1980 case MCM_SETCOLOR:
1981 return MONTHCAL_SetColor(hwnd, wParam, lParam);
1983 case MCM_GETTODAY:
1984 return MONTHCAL_GetToday(hwnd, wParam, lParam);
1986 case MCM_SETTODAY:
1987 return MONTHCAL_SetToday(hwnd, wParam, lParam);
1989 case MCM_HITTEST:
1990 return MONTHCAL_HitTest(hwnd,lParam);
1992 case MCM_GETFIRSTDAYOFWEEK:
1993 return MONTHCAL_GetFirstDayOfWeek(hwnd, wParam, lParam);
1995 case MCM_SETFIRSTDAYOFWEEK:
1996 return MONTHCAL_SetFirstDayOfWeek(hwnd, wParam, lParam);
1998 case MCM_GETRANGE:
1999 return MONTHCAL_GetRange(hwnd, wParam, lParam);
2001 case MCM_SETRANGE:
2002 return MONTHCAL_SetRange(hwnd, wParam, lParam);
2004 case MCM_GETMONTHDELTA:
2005 return MONTHCAL_GetMonthDelta(hwnd, wParam, lParam);
2007 case MCM_SETMONTHDELTA:
2008 return MONTHCAL_SetMonthDelta(hwnd, wParam, lParam);
2010 case MCM_GETMAXTODAYWIDTH:
2011 return MONTHCAL_GetMaxTodayWidth(hwnd);
2013 case WM_GETDLGCODE:
2014 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2016 case WM_KILLFOCUS:
2017 return MONTHCAL_KillFocus(hwnd, wParam, lParam);
2019 case WM_RBUTTONDOWN:
2020 return MONTHCAL_RButtonDown(hwnd, wParam, lParam);
2022 case WM_LBUTTONDOWN:
2023 return MONTHCAL_LButtonDown(hwnd, wParam, lParam);
2025 case WM_MOUSEMOVE:
2026 return MONTHCAL_MouseMove(hwnd, wParam, lParam);
2028 case WM_LBUTTONUP:
2029 return MONTHCAL_LButtonUp(hwnd, wParam, lParam);
2031 case WM_PAINT:
2032 return MONTHCAL_Paint(hwnd, wParam);
2034 case WM_SETFOCUS:
2035 return MONTHCAL_SetFocus(hwnd, wParam, lParam);
2037 case WM_SIZE:
2038 return MONTHCAL_Size(hwnd, (short)LOWORD(lParam), (short)HIWORD(lParam));
2040 case WM_CREATE:
2041 return MONTHCAL_Create(hwnd, wParam, lParam);
2043 case WM_TIMER:
2044 return MONTHCAL_Timer(hwnd, wParam, lParam);
2046 case WM_DESTROY:
2047 return MONTHCAL_Destroy(hwnd, wParam, lParam);
2049 default:
2050 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
2051 ERR( "unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
2052 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
2054 return 0;
2058 void
2059 MONTHCAL_Register(void)
2061 WNDCLASSA wndClass;
2063 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
2064 wndClass.style = CS_GLOBALCLASS;
2065 wndClass.lpfnWndProc = (WNDPROC)MONTHCAL_WindowProc;
2066 wndClass.cbClsExtra = 0;
2067 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
2068 wndClass.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2069 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2070 wndClass.lpszClassName = MONTHCAL_CLASSA;
2072 RegisterClassA(&wndClass);
2076 void
2077 MONTHCAL_Unregister(void)
2079 UnregisterClassA(MONTHCAL_CLASSA, NULL);