Rewrote the collapsing of . and .. in RtlGetFullPathName_U for better
[wine.git] / dlls / comctl32 / monthcal.c
blob5514da253789698b14c52650a3f860671774a995
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 *)GetWindowLongA(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[1];
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 return TRUE;
1039 /* FIXME: if the specified date is not visible, make it visible */
1040 /* FIXME: redraw? */
1041 static LRESULT
1042 MONTHCAL_SetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
1044 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1045 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
1047 TRACE("%x %lx\n", wParam, lParam);
1048 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1049 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1051 TRACE("%d %d\n", lpSel->wMonth, lpSel->wDay);
1053 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
1054 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
1056 InvalidateRect(hwnd, NULL, FALSE);
1058 return TRUE;
1062 static LRESULT
1063 MONTHCAL_GetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
1065 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1067 TRACE("%x %lx\n", wParam, lParam);
1068 return infoPtr->maxSelCount;
1072 static LRESULT
1073 MONTHCAL_SetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
1075 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1077 TRACE("%x %lx\n", wParam, lParam);
1078 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1079 infoPtr->maxSelCount = wParam;
1082 return TRUE;
1086 static LRESULT
1087 MONTHCAL_GetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
1089 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1090 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1092 TRACE("%x %lx\n", wParam, lParam);
1094 /* validate parameters */
1096 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1098 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT)
1100 MONTHCAL_CopyTime(&infoPtr->maxSel, &lprgSysTimeArray[1]);
1101 MONTHCAL_CopyTime(&infoPtr->minSel, &lprgSysTimeArray[0]);
1102 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1103 return TRUE;
1106 return FALSE;
1110 static LRESULT
1111 MONTHCAL_SetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
1113 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1114 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1116 TRACE("%x %lx\n", wParam, lParam);
1118 /* validate parameters */
1120 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1122 if(GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)
1124 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxSel);
1125 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minSel);
1126 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1127 return TRUE;
1130 return FALSE;
1134 static LRESULT
1135 MONTHCAL_GetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
1137 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1138 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1140 TRACE("%x %lx\n", wParam, lParam);
1142 /* validate parameters */
1144 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
1145 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
1146 return TRUE;
1150 static LRESULT
1151 MONTHCAL_SetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
1153 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1154 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1156 TRACE("%x %lx\n", wParam, lParam);
1158 /* validate parameters */
1160 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
1161 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
1162 InvalidateRect(hwnd, NULL, FALSE);
1163 return TRUE;
1167 static LRESULT
1168 MONTHCAL_HitTest(HWND hwnd, LPARAM lParam)
1170 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1171 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
1172 UINT x,y;
1173 DWORD retval;
1174 int day,wday,wnum;
1177 x = lpht->pt.x;
1178 y = lpht->pt.y;
1179 retval = MCHT_NOWHERE;
1182 /* Comment in for debugging...
1183 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,
1184 infoPtr->wdays.left, infoPtr->wdays.right,
1185 infoPtr->wdays.top, infoPtr->wdays.bottom,
1186 infoPtr->days.left, infoPtr->days.right,
1187 infoPtr->days.top, infoPtr->days.bottom,
1188 infoPtr->todayrect.left, infoPtr->todayrect.right,
1189 infoPtr->todayrect.top, infoPtr->todayrect.bottom,
1190 infoPtr->weeknums.left, infoPtr->weeknums.right,
1191 infoPtr->weeknums.top, infoPtr->weeknums.bottom);
1194 /* are we in the header? */
1196 if(PtInRect(&infoPtr->title, lpht->pt)) {
1197 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1198 retval = MCHT_TITLEBTNPREV;
1199 goto done;
1201 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1202 retval = MCHT_TITLEBTNNEXT;
1203 goto done;
1205 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1206 retval = MCHT_TITLEMONTH;
1207 goto done;
1209 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1210 retval = MCHT_TITLEYEAR;
1211 goto done;
1214 retval = MCHT_TITLE;
1215 goto done;
1218 day = MONTHCAL_CalcDayFromPos(infoPtr,x,y,&wday,&wnum);
1219 if(PtInRect(&infoPtr->wdays, lpht->pt)) {
1220 retval = MCHT_CALENDARDAY;
1221 lpht->st.wYear = infoPtr->currentYear;
1222 lpht->st.wMonth = (day < 1)? infoPtr->currentMonth -1 : infoPtr->currentMonth;
1223 lpht->st.wDay = (day < 1)?
1224 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day : day;
1225 goto done;
1227 if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
1228 retval = MCHT_CALENDARWEEKNUM;
1229 lpht->st.wYear = infoPtr->currentYear;
1230 lpht->st.wMonth = (day < 1) ? infoPtr->currentMonth -1 :
1231 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1232 infoPtr->currentMonth +1 :infoPtr->currentMonth;
1233 lpht->st.wDay = (day < 1 ) ?
1234 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day :
1235 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1236 day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) : day;
1237 goto done;
1239 if(PtInRect(&infoPtr->days, lpht->pt))
1241 lpht->st.wYear = infoPtr->currentYear;
1242 if ( day < 1)
1244 retval = MCHT_CALENDARDATEPREV;
1245 lpht->st.wMonth = infoPtr->currentMonth - 1;
1246 if (lpht->st.wMonth <1)
1248 lpht->st.wMonth = 12;
1249 lpht->st.wYear--;
1251 lpht->st.wDay = MONTHCAL_MonthLength(lpht->st.wMonth,lpht->st.wYear) -day;
1253 else if (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear))
1255 retval = MCHT_CALENDARDATENEXT;
1256 lpht->st.wMonth = infoPtr->currentMonth + 1;
1257 if (lpht->st.wMonth <12)
1259 lpht->st.wMonth = 1;
1260 lpht->st.wYear++;
1262 lpht->st.wDay = day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) ;
1264 else {
1265 retval = MCHT_CALENDARDATE;
1266 lpht->st.wMonth = infoPtr->currentMonth;
1267 lpht->st.wDay = day;
1269 goto done;
1271 if(PtInRect(&infoPtr->todayrect, lpht->pt)) {
1272 retval = MCHT_TODAYLINK;
1273 goto done;
1277 /* Hit nothing special? What's left must be background :-) */
1279 retval = MCHT_CALENDARBK;
1280 done:
1281 lpht->uHit = retval;
1282 return retval;
1286 static void MONTHCAL_GoToNextMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1288 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1290 TRACE("MONTHCAL_GoToNextMonth\n");
1292 infoPtr->currentMonth++;
1293 if(infoPtr->currentMonth > 12) {
1294 infoPtr->currentYear++;
1295 infoPtr->currentMonth = 1;
1298 if(dwStyle & MCS_DAYSTATE) {
1299 NMDAYSTATE nmds;
1300 int i;
1302 nmds.nmhdr.hwndFrom = hwnd;
1303 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1304 nmds.nmhdr.code = MCN_GETDAYSTATE;
1305 nmds.cDayState = infoPtr->monthRange;
1306 nmds.prgDayState = Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1308 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1309 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1310 for(i=0; i<infoPtr->monthRange; i++)
1311 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1316 static void MONTHCAL_GoToPrevMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1318 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1320 TRACE("MONTHCAL_GoToPrevMonth\n");
1322 infoPtr->currentMonth--;
1323 if(infoPtr->currentMonth < 1) {
1324 infoPtr->currentYear--;
1325 infoPtr->currentMonth = 12;
1328 if(dwStyle & MCS_DAYSTATE) {
1329 NMDAYSTATE nmds;
1330 int i;
1332 nmds.nmhdr.hwndFrom = hwnd;
1333 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1334 nmds.nmhdr.code = MCN_GETDAYSTATE;
1335 nmds.cDayState = infoPtr->monthRange;
1336 nmds.prgDayState = Alloc
1337 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1339 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1340 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1341 for(i=0; i<infoPtr->monthRange; i++)
1342 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1346 static LRESULT
1347 MONTHCAL_RButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1349 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1350 HMENU hMenu;
1351 POINT menupoint;
1352 char buf[32];
1354 hMenu = CreatePopupMenu();
1355 if (!LoadStringA(COMCTL32_hModule,IDM_GOTODAY,buf,sizeof(buf)))
1357 WARN("Can't load resource\n");
1358 strcpy(buf,"Go to Today:");
1360 AppendMenuA(hMenu, MF_STRING|MF_ENABLED,1, buf);
1361 menupoint.x=(INT)LOWORD(lParam);
1362 menupoint.y=(INT)HIWORD(lParam);
1363 ClientToScreen(hwnd, &menupoint);
1364 if( TrackPopupMenu(hMenu,TPM_RIGHTBUTTON| TPM_NONOTIFY|TPM_RETURNCMD,
1365 menupoint.x,menupoint.y,0,hwnd,NULL))
1367 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1368 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1369 InvalidateRect(hwnd, NULL, FALSE);
1371 return 0;
1374 static LRESULT
1375 MONTHCAL_LButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1377 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1378 MCHITTESTINFO ht;
1379 DWORD hit;
1380 HMENU hMenu;
1381 RECT rcDay; /* used in determining area to invalidate */
1382 char buf[32];
1383 int i;
1384 POINT menupoint;
1385 TRACE("%x %lx\n", wParam, lParam);
1387 if (infoPtr->hWndYearUpDown)
1389 infoPtr->currentYear=SendMessageA( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)0);
1390 if(!DestroyWindow(infoPtr->hWndYearUpDown))
1392 FIXME("Can't destroy Updown Control\n");
1394 else
1395 infoPtr->hWndYearUpDown=0;
1396 if(!DestroyWindow(infoPtr->hWndYearEdit))
1398 FIXME("Can't destroy Updown Control\n");
1400 else
1401 infoPtr->hWndYearEdit=0;
1402 InvalidateRect(hwnd, NULL, FALSE);
1405 ht.pt.x = (INT)LOWORD(lParam);
1406 ht.pt.y = (INT)HIWORD(lParam);
1407 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1409 /* FIXME: these flags should be checked by */
1410 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1411 /* multi-bit */
1412 if(hit ==MCHT_TITLEBTNNEXT) {
1413 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1414 infoPtr->status = MC_NEXTPRESSED;
1415 SetTimer(hwnd, MC_NEXTMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1416 InvalidateRect(hwnd, NULL, FALSE);
1417 return TRUE;
1419 if(hit == MCHT_TITLEBTNPREV){
1420 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1421 infoPtr->status = MC_PREVPRESSED;
1422 SetTimer(hwnd, MC_PREVMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1423 InvalidateRect(hwnd, NULL, FALSE);
1424 return TRUE;
1427 if(hit == MCHT_TITLEMONTH) {
1428 hMenu = CreatePopupMenu();
1430 for (i=0; i<12;i++)
1432 GetLocaleInfoA( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+i,
1433 buf,sizeof(buf));
1434 AppendMenuA(hMenu, MF_STRING|MF_ENABLED,i+1, buf);
1436 menupoint.x=infoPtr->titlemonth.right;
1437 menupoint.y=infoPtr->titlemonth.bottom;
1438 ClientToScreen(hwnd, &menupoint);
1439 i= TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD,
1440 menupoint.x,menupoint.y,0,hwnd,NULL);
1441 if ((i>0) && (i<13))
1443 infoPtr->currentMonth=i;
1444 InvalidateRect(hwnd, NULL, FALSE);
1447 if(hit == MCHT_TITLEYEAR) {
1448 infoPtr->hWndYearEdit=CreateWindowExA(0,
1449 "EDIT",
1451 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT,
1452 infoPtr->titleyear.left+3,infoPtr->titlebtnnext.top,
1453 infoPtr->titleyear.right-infoPtr->titleyear.left,
1454 infoPtr->textHeight,
1455 hwnd,
1456 NULL,
1457 NULL,
1458 NULL);
1459 infoPtr->hWndYearUpDown=CreateWindowExA(0,
1460 UPDOWN_CLASSA,
1462 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT|UDS_NOTHOUSANDS|UDS_ARROWKEYS,
1463 infoPtr->titleyear.right+6,infoPtr->titlebtnnext.top,
1465 infoPtr->textHeight,
1466 hwnd,
1467 NULL,
1468 NULL,
1469 NULL);
1470 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETRANGE, (WPARAM) 0, MAKELONG (9999, 1753));
1471 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETBUDDY, (WPARAM) infoPtr->hWndYearEdit, (LPARAM)0 );
1472 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)infoPtr->currentYear );
1473 return TRUE;
1476 if(hit == MCHT_TODAYLINK) {
1477 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1478 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1479 InvalidateRect(hwnd, NULL, FALSE);
1480 return TRUE;
1482 if(hit && MCHT_CALENDARDATE) {
1483 SYSTEMTIME selArray[2];
1484 NMSELCHANGE nmsc;
1486 TRACE("MCHT_CALENDARDATE\n");
1487 nmsc.nmhdr.hwndFrom = hwnd;
1488 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1489 nmsc.nmhdr.code = MCN_SELCHANGE;
1490 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1491 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1493 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1494 (WPARAM)nmsc.nmhdr.idFrom,(LPARAM)&nmsc);
1496 MONTHCAL_CopyTime(&ht.st, &selArray[0]);
1497 MONTHCAL_CopyTime(&ht.st, &selArray[1]);
1498 MONTHCAL_SetSelRange(hwnd,0,(LPARAM) &selArray);
1500 /* redraw both old and new days if the selected day changed */
1501 if(infoPtr->curSelDay != ht.st.wDay) {
1502 MONTHCAL_CalcPosFromDay(infoPtr, ht.st.wDay, ht.st.wMonth, &rcDay);
1503 InvalidateRect(hwnd, &rcDay, TRUE);
1505 MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->curSelDay, infoPtr->currentMonth, &rcDay);
1506 InvalidateRect(hwnd, &rcDay, TRUE);
1509 infoPtr->firstSelDay = ht.st.wDay;
1510 infoPtr->curSelDay = ht.st.wDay;
1511 infoPtr->status = MC_SEL_LBUTDOWN;
1512 return TRUE;
1515 return 0;
1519 static LRESULT
1520 MONTHCAL_LButtonUp(HWND hwnd, WPARAM wParam, LPARAM lParam)
1522 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1523 NMSELCHANGE nmsc;
1524 NMHDR nmhdr;
1525 BOOL redraw = FALSE;
1526 MCHITTESTINFO ht;
1527 DWORD hit;
1529 TRACE("\n");
1531 if(infoPtr->status & MC_NEXTPRESSED) {
1532 KillTimer(hwnd, MC_NEXTMONTHTIMER);
1533 redraw = TRUE;
1535 if(infoPtr->status & MC_PREVPRESSED) {
1536 KillTimer(hwnd, MC_PREVMONTHTIMER);
1537 redraw = TRUE;
1540 ht.pt.x = (INT)LOWORD(lParam);
1541 ht.pt.y = (INT)HIWORD(lParam);
1542 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1544 infoPtr->status = MC_SEL_LBUTUP;
1546 if(hit ==MCHT_CALENDARDATENEXT) {
1547 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1548 InvalidateRect(hwnd, NULL, FALSE);
1549 return TRUE;
1551 if(hit == MCHT_CALENDARDATEPREV){
1552 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1553 InvalidateRect(hwnd, NULL, FALSE);
1554 return TRUE;
1556 nmhdr.hwndFrom = hwnd;
1557 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
1558 nmhdr.code = NM_RELEASEDCAPTURE;
1559 TRACE("Sent notification from %p to %p\n", hwnd, infoPtr->hwndNotify);
1561 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1562 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1564 nmsc.nmhdr.hwndFrom = hwnd;
1565 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1566 nmsc.nmhdr.code = MCN_SELECT;
1567 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1568 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1570 SendMessageA(infoPtr->hwndNotify, WM_NOTIFY,
1571 (WPARAM)nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1573 /* redraw if necessary */
1574 if(redraw)
1575 InvalidateRect(hwnd, NULL, FALSE);
1577 return 0;
1581 static LRESULT
1582 MONTHCAL_Timer(HWND hwnd, WPARAM wParam, LPARAM lParam)
1584 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1585 BOOL redraw = FALSE;
1587 TRACE(" %d\n", wParam);
1588 if(!infoPtr) return 0;
1590 switch(wParam) {
1591 case MC_NEXTMONTHTIMER:
1592 redraw = TRUE;
1593 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1594 break;
1595 case MC_PREVMONTHTIMER:
1596 redraw = TRUE;
1597 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1598 break;
1599 default:
1600 ERR("got unknown timer\n");
1603 /* redraw only if necessary */
1604 if(redraw)
1605 InvalidateRect(hwnd, NULL, FALSE);
1607 return 0;
1611 static LRESULT
1612 MONTHCAL_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1614 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1615 MCHITTESTINFO ht;
1616 int oldselday, selday, hit;
1617 RECT r;
1619 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1621 ht.pt.x = LOWORD(lParam);
1622 ht.pt.y = HIWORD(lParam);
1624 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1626 /* not on the calendar date numbers? bail out */
1627 TRACE("hit:%x\n",hit);
1628 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1630 selday = ht.st.wDay;
1631 oldselday = infoPtr->curSelDay;
1632 infoPtr->curSelDay = selday;
1633 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1635 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1636 SYSTEMTIME selArray[2];
1637 int i;
1639 MONTHCAL_GetSelRange(hwnd, 0, (LPARAM)&selArray);
1640 i = 0;
1641 if(infoPtr->firstSelDay==selArray[0].wDay) i=1;
1642 TRACE("oldRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1643 if(infoPtr->firstSelDay==selArray[1].wDay) {
1644 /* 1st time we get here: selArray[0]=selArray[1]) */
1645 /* if we're still at the first selected date, return */
1646 if(infoPtr->firstSelDay==selday) goto done;
1647 if(selday<infoPtr->firstSelDay) i = 0;
1650 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1651 if(selday>infoPtr->firstSelDay)
1652 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1653 else
1654 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1657 if(selArray[i].wDay!=selday) {
1658 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1660 selArray[i].wDay = selday;
1662 if(selArray[0].wDay>selArray[1].wDay) {
1663 DWORD tempday;
1664 tempday = selArray[1].wDay;
1665 selArray[1].wDay = selArray[0].wDay;
1666 selArray[0].wDay = tempday;
1669 MONTHCAL_SetSelRange(hwnd, 0, (LPARAM)&selArray);
1673 done:
1675 /* only redraw if the currently selected day changed */
1676 /* FIXME: this should specify a rectangle containing only the days that changed */
1677 /* using InvalidateRect */
1678 if(oldselday != infoPtr->curSelDay)
1679 InvalidateRect(hwnd, NULL, FALSE);
1681 return 0;
1685 static LRESULT
1686 MONTHCAL_Paint(HWND hwnd, WPARAM wParam)
1688 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1689 HDC hdc;
1690 PAINTSTRUCT ps;
1692 /* fill ps.rcPaint with a default rect */
1693 memcpy(&(ps.rcPaint), &(infoPtr->rcClient), sizeof(infoPtr->rcClient));
1695 hdc = (wParam==0 ? BeginPaint(hwnd, &ps) : (HDC)wParam);
1696 MONTHCAL_Refresh(hwnd, hdc, &ps);
1697 if(!wParam) EndPaint(hwnd, &ps);
1698 return 0;
1702 static LRESULT
1703 MONTHCAL_KillFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1705 TRACE("\n");
1707 InvalidateRect(hwnd, NULL, TRUE);
1709 return 0;
1713 static LRESULT
1714 MONTHCAL_SetFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1716 TRACE("\n");
1718 InvalidateRect(hwnd, NULL, FALSE);
1720 return 0;
1723 /* sets the size information */
1724 static void MONTHCAL_UpdateSize(HWND hwnd)
1726 HDC hdc = GetDC(hwnd);
1727 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1728 RECT *rcClient=&infoPtr->rcClient;
1729 RECT *rcDraw=&infoPtr->rcDraw;
1730 RECT *title=&infoPtr->title;
1731 RECT *prev=&infoPtr->titlebtnprev;
1732 RECT *next=&infoPtr->titlebtnnext;
1733 RECT *titlemonth=&infoPtr->titlemonth;
1734 RECT *titleyear=&infoPtr->titleyear;
1735 RECT *wdays=&infoPtr->wdays;
1736 RECT *weeknumrect=&infoPtr->weeknums;
1737 RECT *days=&infoPtr->days;
1738 RECT *todayrect=&infoPtr->todayrect;
1739 SIZE size;
1740 TEXTMETRICA tm;
1741 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1742 HFONT currentFont;
1743 double xdiv;
1745 currentFont = SelectObject(hdc, infoPtr->hFont);
1747 /* FIXME: need a way to determine current font, without setting it */
1749 if(infoPtr->hFont!=currentFont) {
1750 SelectObject(hdc, currentFont);
1751 infoPtr->hFont=currentFont;
1752 GetObjectA(currentFont, sizeof(LOGFONTA), &logFont);
1753 logFont.lfWeight=FW_BOLD;
1754 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1758 /* get the height and width of each day's text */
1759 GetTextMetricsA(hdc, &tm);
1760 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading;
1761 GetTextExtentPoint32A(hdc, "Sun", 3, &size);
1762 infoPtr->textWidth = size.cx + 2;
1764 /* retrieve the controls client rectangle info infoPtr->rcClient */
1765 GetClientRect(hwnd, rcClient);
1767 /* rcDraw is the rectangle the control is drawn in */
1768 rcDraw->left = rcClient->left;
1769 rcDraw->right = rcClient->right;
1770 rcDraw->top = rcClient->top;
1771 rcDraw->bottom = rcClient->bottom;
1773 /* recalculate the height and width increments and offsets */
1774 /* FIXME: We use up all available width. This will inhibit having multiple
1775 calendars in a row, like win doesn
1777 if(dwStyle & MCS_WEEKNUMBERS)
1778 xdiv=8.0;
1779 else
1780 xdiv=7.0;
1781 infoPtr->width_increment = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) / xdiv;
1782 infoPtr->height_increment = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) / 10.0;
1783 infoPtr->left_offset = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) - (infoPtr->width_increment * xdiv);
1784 infoPtr->top_offset = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) - (infoPtr->height_increment * 10.0);
1786 rcDraw->bottom = rcDraw->top + 10 * infoPtr->height_increment;
1787 /* this is correct, the control does NOT expand vertically */
1788 /* like it does horizontally */
1789 /* make sure we don't move the controls bottom out of the client */
1790 /* area */
1791 /* title line has about 3 text heights, abrev days line, 6 weeksline and today circle line*/
1792 /*if((rcDraw->top + 9 * infoPtr->textHeight + 5) < rcDraw->bottom) {
1793 rcDraw->bottom = rcDraw->top + 9 * infoPtr->textHeight + 5;
1796 /* calculate title area */
1797 title->top = rcClient->top;
1798 title->bottom = title->top + 2 * infoPtr->height_increment;
1799 title->left = rcClient->left;
1800 title->right = rcClient->right;
1802 /* set the dimensions of the next and previous buttons and center */
1803 /* the month text vertically */
1804 prev->top = next->top = title->top + 6;
1805 prev->bottom = next->bottom = title->bottom - 6;
1806 prev->left = title->left + 6;
1807 prev->right = prev->left + (title->bottom - title->top) ;
1808 next->right = title->right - 6;
1809 next->left = next->right - (title->bottom - title->top);
1811 /* titlemonth->left and right change based upon the current month */
1812 /* and are recalculated in refresh as the current month may change */
1813 /* without the control being resized */
1814 titlemonth->top = titleyear->top = title->top + (infoPtr->height_increment)/2;
1815 titlemonth->bottom = titleyear->bottom = title->bottom - (infoPtr->height_increment)/2;
1817 /* setup the dimensions of the rectangle we draw the names of the */
1818 /* days of the week in */
1819 weeknumrect->left =infoPtr->left_offset;
1820 if(dwStyle & MCS_WEEKNUMBERS)
1821 weeknumrect->right=prev->right;
1822 else
1823 weeknumrect->right=weeknumrect->left;
1824 wdays->left = days->left = weeknumrect->right;
1825 wdays->right = days->right = wdays->left + 7 * infoPtr->width_increment;
1826 wdays->top = title->bottom ;
1827 wdays->bottom = wdays->top + infoPtr->height_increment;
1829 days->top = weeknumrect->top = wdays->bottom ;
1830 days->bottom = weeknumrect->bottom = days->top + 6 * infoPtr->height_increment;
1832 todayrect->left = rcClient->left;
1833 todayrect->right = rcClient->right;
1834 todayrect->top = days->bottom;
1835 todayrect->bottom = days->bottom + infoPtr->height_increment;
1837 /* uncomment for excessive debugging
1838 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",
1839 infoPtr->width_increment,infoPtr->height_increment,
1840 rcClient->left, rcClient->right, rcClient->top, rcClient->bottom,
1841 title->left, title->right, title->top, title->bottom,
1842 wdays->left, wdays->right, wdays->top, wdays->bottom,
1843 days->left, days->right, days->top, days->bottom,
1844 todayrect->left,todayrect->right,todayrect->top,todayrect->bottom);
1847 /* restore the originally selected font */
1848 SelectObject(hdc, currentFont);
1850 ReleaseDC(hwnd, hdc);
1853 static LRESULT MONTHCAL_Size(HWND hwnd, int Width, int Height)
1855 TRACE("(hwnd=%p, width=%d, height=%d)\n", hwnd, Width, Height);
1857 MONTHCAL_UpdateSize(hwnd);
1859 /* invalidate client area and erase background */
1860 InvalidateRect(hwnd, NULL, TRUE);
1862 return 0;
1865 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1866 static LRESULT
1867 MONTHCAL_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
1869 MONTHCAL_INFO *infoPtr;
1870 LOGFONTA logFont;
1872 /* allocate memory for info structure */
1873 infoPtr =(MONTHCAL_INFO*)Alloc(sizeof(MONTHCAL_INFO));
1874 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1876 if(infoPtr == NULL) {
1877 ERR( "could not allocate info memory!\n");
1878 return 0;
1880 if((MONTHCAL_INFO*)GetWindowLongA(hwnd, 0) != infoPtr) {
1881 ERR( "pointer assignment error!\n");
1882 return 0;
1885 infoPtr->hwndNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1887 infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1888 GetObjectA(infoPtr->hFont, sizeof(LOGFONTA), &logFont);
1889 logFont.lfWeight = FW_BOLD;
1890 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1892 /* initialize info structure */
1893 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1895 GetSystemTime(&infoPtr->todaysDate);
1896 MONTHCAL_SetFirstDayOfWeek(hwnd,0,(LPARAM)-1);
1897 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1898 infoPtr->currentYear = infoPtr->todaysDate.wYear;
1899 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
1900 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
1901 infoPtr->maxSelCount = 7;
1902 infoPtr->monthRange = 3;
1903 infoPtr->monthdayState = Alloc
1904 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1905 infoPtr->titlebk = GetSysColor(COLOR_ACTIVECAPTION);
1906 infoPtr->titletxt = GetSysColor(COLOR_WINDOW);
1907 infoPtr->monthbk = GetSysColor(COLOR_WINDOW);
1908 infoPtr->trailingtxt = GetSysColor(COLOR_GRAYTEXT);
1909 infoPtr->bk = GetSysColor(COLOR_WINDOW);
1910 infoPtr->txt = GetSysColor(COLOR_WINDOWTEXT);
1912 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1913 /* of the control */
1914 MONTHCAL_UpdateSize(hwnd);
1916 return 0;
1920 static LRESULT
1921 MONTHCAL_Destroy(HWND hwnd, WPARAM wParam, LPARAM lParam)
1923 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1925 /* free month calendar info data */
1926 if(infoPtr->monthdayState)
1927 Free(infoPtr->monthdayState);
1928 Free(infoPtr);
1929 SetWindowLongA(hwnd, 0, 0);
1930 return 0;
1934 static LRESULT WINAPI
1935 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1937 TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1938 if (!MONTHCAL_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
1939 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
1940 switch(uMsg)
1942 case MCM_GETCURSEL:
1943 return MONTHCAL_GetCurSel(hwnd, wParam, lParam);
1945 case MCM_SETCURSEL:
1946 return MONTHCAL_SetCurSel(hwnd, wParam, lParam);
1948 case MCM_GETMAXSELCOUNT:
1949 return MONTHCAL_GetMaxSelCount(hwnd, wParam, lParam);
1951 case MCM_SETMAXSELCOUNT:
1952 return MONTHCAL_SetMaxSelCount(hwnd, wParam, lParam);
1954 case MCM_GETSELRANGE:
1955 return MONTHCAL_GetSelRange(hwnd, wParam, lParam);
1957 case MCM_SETSELRANGE:
1958 return MONTHCAL_SetSelRange(hwnd, wParam, lParam);
1960 case MCM_GETMONTHRANGE:
1961 return MONTHCAL_GetMonthRange(hwnd, wParam, lParam);
1963 case MCM_SETDAYSTATE:
1964 return MONTHCAL_SetDayState(hwnd, wParam, lParam);
1966 case MCM_GETMINREQRECT:
1967 return MONTHCAL_GetMinReqRect(hwnd, wParam, lParam);
1969 case MCM_GETCOLOR:
1970 return MONTHCAL_GetColor(hwnd, wParam, lParam);
1972 case MCM_SETCOLOR:
1973 return MONTHCAL_SetColor(hwnd, wParam, lParam);
1975 case MCM_GETTODAY:
1976 return MONTHCAL_GetToday(hwnd, wParam, lParam);
1978 case MCM_SETTODAY:
1979 return MONTHCAL_SetToday(hwnd, wParam, lParam);
1981 case MCM_HITTEST:
1982 return MONTHCAL_HitTest(hwnd,lParam);
1984 case MCM_GETFIRSTDAYOFWEEK:
1985 return MONTHCAL_GetFirstDayOfWeek(hwnd, wParam, lParam);
1987 case MCM_SETFIRSTDAYOFWEEK:
1988 return MONTHCAL_SetFirstDayOfWeek(hwnd, wParam, lParam);
1990 case MCM_GETRANGE:
1991 return MONTHCAL_GetRange(hwnd, wParam, lParam);
1993 case MCM_SETRANGE:
1994 return MONTHCAL_SetRange(hwnd, wParam, lParam);
1996 case MCM_GETMONTHDELTA:
1997 return MONTHCAL_GetMonthDelta(hwnd, wParam, lParam);
1999 case MCM_SETMONTHDELTA:
2000 return MONTHCAL_SetMonthDelta(hwnd, wParam, lParam);
2002 case MCM_GETMAXTODAYWIDTH:
2003 return MONTHCAL_GetMaxTodayWidth(hwnd);
2005 case WM_GETDLGCODE:
2006 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2008 case WM_KILLFOCUS:
2009 return MONTHCAL_KillFocus(hwnd, wParam, lParam);
2011 case WM_RBUTTONDOWN:
2012 return MONTHCAL_RButtonDown(hwnd, wParam, lParam);
2014 case WM_LBUTTONDOWN:
2015 return MONTHCAL_LButtonDown(hwnd, wParam, lParam);
2017 case WM_MOUSEMOVE:
2018 return MONTHCAL_MouseMove(hwnd, wParam, lParam);
2020 case WM_LBUTTONUP:
2021 return MONTHCAL_LButtonUp(hwnd, wParam, lParam);
2023 case WM_PAINT:
2024 return MONTHCAL_Paint(hwnd, wParam);
2026 case WM_SETFOCUS:
2027 return MONTHCAL_SetFocus(hwnd, wParam, lParam);
2029 case WM_SIZE:
2030 return MONTHCAL_Size(hwnd, (short)LOWORD(lParam), (short)HIWORD(lParam));
2032 case WM_CREATE:
2033 return MONTHCAL_Create(hwnd, wParam, lParam);
2035 case WM_TIMER:
2036 return MONTHCAL_Timer(hwnd, wParam, lParam);
2038 case WM_DESTROY:
2039 return MONTHCAL_Destroy(hwnd, wParam, lParam);
2041 default:
2042 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
2043 ERR( "unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
2044 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
2046 return 0;
2050 void
2051 MONTHCAL_Register(void)
2053 WNDCLASSA wndClass;
2055 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
2056 wndClass.style = CS_GLOBALCLASS;
2057 wndClass.lpfnWndProc = (WNDPROC)MONTHCAL_WindowProc;
2058 wndClass.cbClsExtra = 0;
2059 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
2060 wndClass.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
2061 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2062 wndClass.lpszClassName = MONTHCAL_CLASSA;
2064 RegisterClassA(&wndClass);
2068 void
2069 MONTHCAL_Unregister(void)
2071 UnregisterClassA(MONTHCAL_CLASSA, NULL);