Removed all direct calls to MONTHCAL_Refresh() and replaced with
[wine/multimedia.git] / dlls / comctl32 / monthcal.c
blob5fcf2f6852474de6033a806ebd816cbfb90c6582
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>
9 * TODO:
10 * - Notifications.
13 * FIXME: handle resources better (doesn't work now); also take care
14 of internationalization.
15 * FIXME: keyboard handling.
18 #include <math.h>
19 #include <stdio.h>
21 #include "winbase.h"
22 #include "windef.h"
23 #include "wingdi.h"
24 #include "winuser.h"
25 #include "win.h"
26 #include "winnls.h"
27 #include "commctrl.h"
28 #include "comctl32.h"
29 #include "monthcal.h"
30 #include "debugtools.h"
32 DEFAULT_DEBUG_CHANNEL(monthcal);
34 /* take #days/month from ole/parsedt.c;
35 * we want full month-names, and abbreviated weekdays, so these are
36 * defined here */
38 const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
40 const char * const monthtxt[] = {"January", "February", "March", "April", "May",
41 "June", "July", "August", "September", "October",
42 "November", "December"};
43 const char * const daytxt[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
44 static const int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
47 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongA(hwnd, 0))
49 /* helper functions */
51 /* returns the number of days in any given month */
52 /* january is 1, december is 12 */
53 static int MONTHCAL_MonthLength(int month, int year)
55 /* if we have a leap year add 1 day to February */
56 /* a leap year is a year either divisible by 400 */
57 /* or divisible by 4 and not by 100 */
58 if(month == 2) { /* February */
59 return mdays[month - 1] + ((year%400 == 0) ? 1 : ((year%100 != 0) &&
60 (year%4 == 0)) ? 1 : 0);
62 else {
63 return mdays[month - 1];
68 /* make sure that time is valid */
69 static int MONTHCAL_ValidateTime(SYSTEMTIME time)
71 if(time.wMonth > 12) return FALSE;
72 if(time.wDayOfWeek > 6) return FALSE;
73 if(time.wDay > MONTHCAL_MonthLength(time.wMonth, time.wYear))
74 return FALSE;
75 if(time.wHour > 23) return FALSE;
76 if(time.wMinute > 59) return FALSE;
77 if(time.wSecond > 59) return FALSE;
78 if(time.wMilliseconds > 999) return FALSE;
80 return TRUE;
84 void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to)
86 to->wYear = from->wYear;
87 to->wMonth = from->wMonth;
88 to->wDayOfWeek = from->wDayOfWeek;
89 to->wDay = from->wDay;
90 to->wHour = from->wHour;
91 to->wMinute = from->wMinute;
92 to->wSecond = from->wSecond;
93 to->wMilliseconds = from->wMilliseconds;
97 /* Note:Depending on DST, this may be offset by a day.
98 Need to find out if we're on a DST place & adjust the clock accordingly.
99 Above function assumes we have a valid data.
100 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
101 0 = Monday.
104 /* returns the day in the week(0 == sunday, 6 == saturday) */
105 /* day(1 == 1st, 2 == 2nd... etc), year is the year value */
106 int MONTHCAL_CalculateDayOfWeek(DWORD day, DWORD month, DWORD year)
108 year-=(month < 3);
110 return((year + year/4 - year/100 + year/400 +
111 DayOfWeekTable[month-1] + day - 1 ) % 7);
115 static int MONTHCAL_CalcDayFromPos(MONTHCAL_INFO *infoPtr, int x, int y)
117 int daypos, weekpos, retval, firstDay;
119 /* if the point is outside the x bounds of the window put
120 it at the boundry */
121 if(x > (infoPtr->width_increment * 7.0)) {
122 x = infoPtr->rcClient.right - infoPtr->rcClient.left - infoPtr->left_offset;
125 daypos = (x -(infoPtr->prevmonth.left + infoPtr->left_offset)) / infoPtr->width_increment;
126 weekpos = (y - infoPtr->days.bottom - infoPtr->rcClient.top) / infoPtr->height_increment;
128 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
129 retval = daypos + (7 * weekpos) - firstDay;
130 TRACE("%d %d %d\n", daypos, weekpos, retval);
131 return retval;
134 /* day is the day of the month, 1 == 1st day of the month */
135 /* sets x and y to be the position of the day */
136 /* x == day, y == week where(0,0) == sunday, 1st week */
137 static void MONTHCAL_CalcDayXY(MONTHCAL_INFO *infoPtr, int day, int month,
138 int *x, int *y)
140 int firstDay, prevMonth;
142 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
144 if(month==infoPtr->currentMonth) {
145 *x = (day + firstDay) % 7;
146 *y = (day + firstDay - *x) / 7;
147 return;
149 if(month < infoPtr->currentMonth) {
150 prevMonth = month - 1;
151 if(prevMonth==0)
152 prevMonth = 12;
154 *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay) % 7;
155 *y = 0;
156 return;
159 *y = MONTHCAL_MonthLength(month, infoPtr->currentYear - 1) / 7;
160 *x = (day + firstDay + MONTHCAL_MonthLength(month,
161 infoPtr->currentYear)) % 7;
165 /* x: column(day), y: row(week) */
166 static void MONTHCAL_CalcDayRect(MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
168 r->left = infoPtr->prevmonth.left + x * infoPtr->width_increment + infoPtr->left_offset;
169 r->right = r->left + infoPtr->width_increment;
170 r->top = infoPtr->height_increment * y + infoPtr->days.bottom + infoPtr->top_offset;
171 r->bottom = r->top + infoPtr->textHeight;
175 /* sets the RECT struct r to the rectangle around the day and month */
176 /* day is the day value of the month(1 == 1st), month is the month */
177 /* value(january == 1, december == 12) */
178 static inline void MONTHCAL_CalcPosFromDay(MONTHCAL_INFO *infoPtr,
179 int day, int month, RECT *r)
181 int x, y;
183 MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
184 MONTHCAL_CalcDayRect(infoPtr, r, x, y);
188 /* day is the day in the month(1 == 1st of the month) */
189 /* month is the month value(1 == january, 12 == december) */
190 static void MONTHCAL_CircleDay(HDC hdc, MONTHCAL_INFO *infoPtr, int day,
191 int month)
193 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
194 HPEN hOldPen2 = SelectObject(hdc, hRedPen);
195 POINT points[13];
196 int x, y;
197 RECT day_rect;
199 /* use prevmonth to calculate position because it contains the extra width
200 * from MCS_WEEKNUMBERS
203 MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
205 x = day_rect.left;
206 y = day_rect.top;
208 points[0].x = x;
209 points[0].y = y - 1;
210 points[1].x = x + 0.8 * infoPtr->width_increment;
211 points[1].y = y - 1;
212 points[2].x = x + 0.9 * infoPtr->width_increment;
213 points[2].y = y;
214 points[3].x = x + infoPtr->width_increment;
215 points[3].y = y + 0.5 * infoPtr->textHeight;
217 points[4].x = x + infoPtr->width_increment;
218 points[4].y = y + 0.9 * infoPtr->textHeight;
219 points[5].x = x + 0.6 * infoPtr->width_increment;
220 points[5].y = y + 0.9 * infoPtr->textHeight;
221 points[6].x = x + 0.5 * infoPtr->width_increment;
222 points[6].y = y + 0.9 * infoPtr->textHeight; /* bring the bottom up just
223 a hair to fit inside the day rectangle */
225 points[7].x = x + 0.2 * infoPtr->width_increment;
226 points[7].y = y + 0.8 * infoPtr->textHeight;
227 points[8].x = x + 0.1 * infoPtr->width_increment;
228 points[8].y = y + 0.8 * infoPtr->textHeight;
229 points[9].x = x;
230 points[9].y = y + 0.5 * infoPtr->textHeight;
232 points[10].x = x + 0.1 * infoPtr->width_increment;
233 points[10].y = y + 0.2 * infoPtr->textHeight;
234 points[11].x = x + 0.2 * infoPtr->width_increment;
235 points[11].y = y + 0.3 * infoPtr->textHeight;
236 points[12].x = x + 0.5 * infoPtr->width_increment;
237 points[12].y = y + 0.3 * infoPtr->textHeight;
239 PolyBezier(hdc, points, 13);
240 DeleteObject(hRedPen);
241 SelectObject(hdc, hOldPen2);
245 static void MONTHCAL_DrawDay(HDC hdc, MONTHCAL_INFO *infoPtr, int day, int month,
246 int x, int y, int bold)
248 char buf[10];
249 RECT r;
250 static int haveBoldFont, haveSelectedDay = FALSE;
251 HBRUSH hbr;
252 HPEN hNewPen, hOldPen = 0;
253 COLORREF oldCol = 0;
254 COLORREF oldBk = 0;
256 sprintf(buf, "%d", day);
258 /* No need to check styles: when selection is not valid, it is set to zero.
259 * 1<day<31, so evertyhing's OK.
262 MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
264 if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
265 && (month==infoPtr->currentMonth)) {
266 HRGN hrgn;
267 RECT r2;
269 TRACE("%d %d %d\n",day, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
270 TRACE("%d %d %d %d\n", r.left, r.top, r.right, r.bottom);
271 oldCol = SetTextColor(hdc, infoPtr->monthbk);
272 oldBk = SetBkColor(hdc, infoPtr->trailingtxt);
273 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
274 hrgn = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
275 FillRgn(hdc, hrgn, hbr);
277 /* FIXME: this may need to be changed now b/c of the other
278 drawing changes 11/3/99 CMM */
279 r2.left = r.left - 0.25 * infoPtr->textWidth;
280 r2.top = r.top;
281 r2.right = r.left + 0.5 * infoPtr->textWidth;
282 r2.bottom = r.bottom;
283 if(haveSelectedDay) FillRect(hdc, &r2, hbr);
284 haveSelectedDay = TRUE;
285 } else {
286 haveSelectedDay = FALSE;
289 /* need to add some code for multiple selections */
291 if((bold) &&(!haveBoldFont)) {
292 SelectObject(hdc, infoPtr->hBoldFont);
293 haveBoldFont = TRUE;
295 if((!bold) &&(haveBoldFont)) {
296 SelectObject(hdc, infoPtr->hFont);
297 haveBoldFont = FALSE;
300 if(haveSelectedDay) {
301 SetTextColor(hdc, oldCol);
302 SetBkColor(hdc, oldBk);
305 DrawTextA(hdc, buf, lstrlenA(buf), &r,
306 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
308 /* draw a rectangle around the currently selected days text */
309 if((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth)) {
310 hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
311 hbr = GetSysColorBrush(COLOR_WINDOWTEXT);
312 FrameRect(hdc, &r, hbr);
313 SelectObject(hdc, hOldPen);
318 /* CHECKME: For `todays date', do we need to check the locale?*/
319 static void MONTHCAL_Refresh(HWND hwnd, HDC hdc, PAINTSTRUCT* ps)
321 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
322 RECT *rcClient=&infoPtr->rcClient;
323 RECT *rcDraw=&infoPtr->rcDraw;
324 RECT *title=&infoPtr->title;
325 RECT *prev=&infoPtr->titlebtnprev;
326 RECT *next=&infoPtr->titlebtnnext;
327 RECT *titlemonth=&infoPtr->titlemonth;
328 RECT *titleyear=&infoPtr->titleyear;
329 RECT *prevmonth=&infoPtr->prevmonth;
330 RECT *nextmonth=&infoPtr->nextmonth;
331 RECT dayrect;
332 RECT *days=&dayrect;
333 RECT *weeknums=&infoPtr->weeknums;
334 RECT *rtoday=&infoPtr->today;
335 int i, j, m, mask, day, firstDay, weeknum, prevMonth;
336 int textHeight = infoPtr->textHeight, textWidth = infoPtr->textWidth;
337 SIZE size;
338 HBRUSH hbr;
339 HFONT currentFont;
340 /* LOGFONTA logFont; */
341 char buf[20];
342 const char *thisMonthtxt;
343 COLORREF oldTextColor, oldBkColor;
344 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
345 BOOL prssed;
346 RECT rcTemp;
347 RECT rcDay; /* used in MONTHCAL_CalcDayRect() */
349 oldTextColor = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
351 /* draw control edge */
352 if(EqualRect(&(ps->rcPaint), rcClient))
354 hbr = CreateSolidBrush(RGB(255, 255, 255));
355 FillRect(hdc, rcClient, hbr);
356 DrawEdge(hdc, rcClient, EDGE_SUNKEN, BF_RECT);
357 DeleteObject(hbr);
358 prssed = FALSE;
361 /* draw header */
362 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
364 hbr = CreateSolidBrush(infoPtr->titlebk);
365 FillRect(hdc, title, hbr);
368 /* if the previous button is pressed draw it depressed */
369 if(IntersectRect(&rcTemp, &(ps->rcPaint), prev))
371 if((infoPtr->status & MC_PREVPRESSED))
372 DrawFrameControl(hdc, prev, DFC_SCROLL,
373 DFCS_SCROLLLEFT | DFCS_PUSHED |
374 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
375 else /* if the previous button is pressed draw it depressed */
376 DrawFrameControl(hdc, prev, DFC_SCROLL,
377 DFCS_SCROLLLEFT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
380 /* if next button is depressed draw it depressed */
381 if(IntersectRect(&rcTemp, &(ps->rcPaint), next))
383 if((infoPtr->status & MC_NEXTPRESSED))
384 DrawFrameControl(hdc, next, DFC_SCROLL,
385 DFCS_SCROLLRIGHT | DFCS_PUSHED |
386 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
387 else /* if the next button is pressed draw it depressed */
388 DrawFrameControl(hdc, next, DFC_SCROLL,
389 DFCS_SCROLLRIGHT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
392 oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
393 SetTextColor(hdc, infoPtr->titletxt);
394 currentFont = SelectObject(hdc, infoPtr->hBoldFont);
396 /* titlemonth->left and right are set in MONTHCAL_UpdateSize */
397 titlemonth->left = title->left;
398 titlemonth->right = title->right;
400 thisMonthtxt = monthtxt[infoPtr->currentMonth - 1];
401 sprintf(buf, "%s %ld", thisMonthtxt, infoPtr->currentYear);
403 if(IntersectRect(&rcTemp, &(ps->rcPaint), titlemonth))
405 DrawTextA(hdc, buf, strlen(buf), titlemonth,
406 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
409 SelectObject(hdc, infoPtr->hFont);
411 /* titlemonth left/right contained rect for whole titletxt('June 1999')
412 * MCM_HitTestInfo wants month & year rects, so prepare these now.
413 *(no, we can't draw them separately; the whole text is centered)
415 GetTextExtentPoint32A(hdc, buf, lstrlenA(buf), &size);
416 titlemonth->left = title->right / 2 - size.cx / 2;
417 titleyear->right = title->right / 2 + size.cx / 2;
418 GetTextExtentPoint32A(hdc, thisMonthtxt, lstrlenA(thisMonthtxt), &size);
419 titlemonth->right = titlemonth->left + size.cx;
420 titleyear->right = titlemonth->right;
423 /* draw line under day abbreviatons */
425 if(dwStyle & MCS_WEEKNUMBERS)
426 MoveToEx(hdc, rcDraw->left + textWidth + 3, title->bottom + textHeight + 2, NULL);
427 else
428 MoveToEx(hdc, rcDraw->left + 3, title->bottom + textHeight + 2, NULL);
430 LineTo(hdc, rcDraw->right - 3, title->bottom + textHeight + 2);
432 /* draw day abbreviations */
434 SetBkColor(hdc, infoPtr->monthbk);
435 SetTextColor(hdc, infoPtr->trailingtxt);
437 /* copy this rect so we can change the values without changing */
438 /* the original version */
439 days->left = infoPtr->days.left;
440 days->right = infoPtr->days.right;
441 days->top = infoPtr->days.top;
442 days->bottom = infoPtr->days.bottom;
444 i = infoPtr->firstDay;
446 for(j=0; j<7; j++) {
447 DrawTextA(hdc, daytxt[i], strlen(daytxt[i]), days,
448 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
449 i = (i + 1) % 7;
450 days->left+=infoPtr->width_increment;
451 days->right+=infoPtr->width_increment;
454 days->left = rcDraw->left + j;
455 if(dwStyle & MCS_WEEKNUMBERS) days->left+=textWidth;
456 /* FIXME: this may need to be changed now 11/10/99 CMM */
457 days->right = rcDraw->left + (j+1) * textWidth - 2;
459 /* draw day numbers; first, the previous month */
461 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
463 prevMonth = infoPtr->currentMonth - 1;
464 if(prevMonth == 0) /* if currentMonth is january(1) prevMonth is */
465 prevMonth = 12; /* december(12) of the previous year */
467 day = MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay;
468 mask = 1<<(day-1);
470 i = 0;
471 m = 0;
472 while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear)) {
473 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
474 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
476 MONTHCAL_DrawDay(hdc, infoPtr, day, prevMonth, i, 0,
477 infoPtr->monthdayState[m] & mask);
480 mask<<=1;
481 day++;
482 i++;
485 prevmonth->left = 0;
486 if(dwStyle & MCS_WEEKNUMBERS) prevmonth->left = textWidth;
487 prevmonth->right = prevmonth->left + (i * infoPtr->width_increment) +
488 infoPtr->left_offset;
489 prevmonth->top = days->bottom;
490 prevmonth->bottom = prevmonth->top + textHeight;
492 /* draw `current' month */
494 day = 1; /* start at the beginning of the current month */
496 infoPtr->firstDayplace = i;
497 SetTextColor(hdc, infoPtr->txt);
498 m++;
499 mask = 1;
501 /* draw the first week of the current month */
502 while(i<7) {
503 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
504 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
507 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, 0,
508 infoPtr->monthdayState[m] & mask);
510 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
511 (day==infoPtr->todaysDate.wDay) &&
512 (infoPtr->currentYear == infoPtr->todaysDate.wYear)) {
513 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
517 mask<<=1;
518 day++;
519 i++;
522 j = 1; /* move to the 2nd week of the current month */
523 i = 0; /* move back to sunday */
524 while(day <= MONTHCAL_MonthLength(infoPtr->currentMonth, infoPtr->currentYear)) {
525 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
526 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
528 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, j,
529 infoPtr->monthdayState[m] & mask);
531 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
532 (day==infoPtr->todaysDate.wDay) &&
533 (infoPtr->currentYear == infoPtr->todaysDate.wYear))
534 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
536 mask<<=1;
537 day++;
538 i++;
539 if(i>6) { /* past saturday, goto the next weeks sunday */
540 i = 0;
541 j++;
545 /* draw `next' month */
547 /* note: the nextmonth rect only hints for the `half-week' that needs to be
548 * drawn to complete the current week. An eventual next week that needs to
549 * be drawn to complete the month calendar is not taken into account in
550 * this rect -- HitTest knows about this.*/
551 nextmonth->left = rcDraw->left + (i * infoPtr->width_increment) +
552 infoPtr->left_offset;
553 nextmonth->right = rcDraw->right;
554 nextmonth->top = days->bottom + (j+1) * textHeight;
555 nextmonth->bottom = nextmonth->top + textHeight;
557 day = 1; /* start at the first day of the next month */
558 m++;
559 mask = 1;
561 SetTextColor(hdc, infoPtr->trailingtxt);
562 while((i<7) &&(j<6)) {
563 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
564 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
566 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth + 1, i, j,
567 infoPtr->monthdayState[m] & mask);
570 mask<<=1;
571 day++;
572 i++;
573 if(i==7) { /* past saturday, go to next week's sunday */
574 i = 0;
575 j++;
578 SetTextColor(hdc, infoPtr->txt);
581 /* draw `today' date if style allows it, and draw a circle before today's
582 * date if necessary */
584 if(!(dwStyle & MCS_NOTODAY)) {
585 int offset = 0;
586 if(!(dwStyle & MCS_NOTODAYCIRCLE)) {
587 day = MONTHCAL_CalcDayFromPos(infoPtr, 0, nextmonth->bottom + textHeight);
588 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
589 offset+=textWidth;
591 MONTHCAL_CalcDayRect(infoPtr, rtoday, 1, 6);
592 sprintf(buf, "Today: %d/%d/%d", infoPtr->todaysDate.wMonth,
593 infoPtr->todaysDate.wDay, infoPtr->todaysDate.wYear);
594 rtoday->left = rtoday->left + 3; /* move text slightly away from circle */
595 rtoday->right = rcDraw->right;
596 SelectObject(hdc, infoPtr->hBoldFont);
598 if(IntersectRect(&rcTemp, &(ps->rcPaint), rtoday))
600 DrawTextA(hdc, buf, lstrlenA(buf), rtoday,
601 DT_LEFT | DT_VCENTER | DT_SINGLELINE);
603 SelectObject(hdc, infoPtr->hFont);
606 if(dwStyle & MCS_WEEKNUMBERS) {
607 /* display weeknumbers*/
608 weeknums->left = 0;
609 weeknums->right = textWidth;
610 weeknums->top = days->bottom + 2;
611 weeknums->bottom = days->bottom + 2 + textHeight;
613 weeknum = 0;
614 for(i=0; i<infoPtr->currentMonth-1; i++)
615 weeknum+=MONTHCAL_MonthLength(i, infoPtr->currentYear);
617 weeknum/=7;
618 for(i=0; i<6; i++) {
619 sprintf(buf, "%d", weeknum + i);
620 DrawTextA(hdc, buf, lstrlenA(buf), weeknums,
621 DT_CENTER | DT_BOTTOM | DT_SINGLELINE );
622 weeknums->top+=textHeight * 1.25;
623 weeknums->bottom+=textHeight * 1.25;
626 MoveToEx(hdc, weeknums->right, days->bottom + 5 , NULL);
627 LineTo(hdc, weeknums->right, weeknums->bottom - 1.25 * textHeight - 5);
631 /* currentFont was font at entering Refresh */
633 SetBkColor(hdc, oldBkColor);
634 SelectObject(hdc, currentFont);
635 SetTextColor(hdc, oldTextColor);
639 static LRESULT
640 MONTHCAL_GetMinReqRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
642 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
643 LPRECT lpRect = (LPRECT) lParam;
644 TRACE("%x %lx\n", wParam, lParam);
646 /* validate parameters */
648 if((infoPtr==NULL) ||(lpRect == NULL) ) return FALSE;
650 lpRect->left = infoPtr->rcClient.left;
651 lpRect->right = infoPtr->rcClient.right;
652 lpRect->top = infoPtr->rcClient.top;
653 lpRect->bottom = infoPtr->rcClient.bottom;
654 return TRUE;
658 static LRESULT
659 MONTHCAL_GetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
661 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
663 TRACE("%x %lx\n", wParam, lParam);
665 switch((int)wParam) {
666 case MCSC_BACKGROUND:
667 return infoPtr->bk;
668 case MCSC_TEXT:
669 return infoPtr->txt;
670 case MCSC_TITLEBK:
671 return infoPtr->titlebk;
672 case MCSC_TITLETEXT:
673 return infoPtr->titletxt;
674 case MCSC_MONTHBK:
675 return infoPtr->monthbk;
676 case MCSC_TRAILINGTEXT:
677 return infoPtr->trailingtxt;
680 return -1;
684 static LRESULT
685 MONTHCAL_SetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
687 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
688 int prev = -1;
690 TRACE("%x %lx\n", wParam, lParam);
692 switch((int)wParam) {
693 case MCSC_BACKGROUND:
694 prev = infoPtr->bk;
695 infoPtr->bk = (COLORREF)lParam;
696 break;
697 case MCSC_TEXT:
698 prev = infoPtr->txt;
699 infoPtr->txt = (COLORREF)lParam;
700 break;
701 case MCSC_TITLEBK:
702 prev = infoPtr->titlebk;
703 infoPtr->titlebk = (COLORREF)lParam;
704 break;
705 case MCSC_TITLETEXT:
706 prev=infoPtr->titletxt;
707 infoPtr->titletxt = (COLORREF)lParam;
708 break;
709 case MCSC_MONTHBK:
710 prev = infoPtr->monthbk;
711 infoPtr->monthbk = (COLORREF)lParam;
712 break;
713 case MCSC_TRAILINGTEXT:
714 prev = infoPtr->trailingtxt;
715 infoPtr->trailingtxt = (COLORREF)lParam;
716 break;
719 return prev;
723 static LRESULT
724 MONTHCAL_GetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
726 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
728 TRACE("%x %lx\n", wParam, lParam);
730 if(infoPtr->delta)
731 return infoPtr->delta;
732 else
733 return infoPtr->visible;
737 static LRESULT
738 MONTHCAL_SetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
740 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
741 int prev = infoPtr->delta;
743 TRACE("%x %lx\n", wParam, lParam);
745 infoPtr->delta = (int)wParam;
746 return prev;
750 static LRESULT
751 MONTHCAL_GetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
753 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
755 return infoPtr->firstDay;
759 /* sets the first day of the week that will appear in the control */
760 /* 0 == Monday, 6 == Sunday */
761 /* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
762 /* FIXME: we need more error checking here */
763 static LRESULT
764 MONTHCAL_SetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
766 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
767 int prev = infoPtr->firstDay;
768 char buf[40];
769 int day;
771 TRACE("%x %lx\n", wParam, lParam);
773 if((lParam >= 0) && (lParam < 7)) {
774 infoPtr->firstDay = (int)lParam;
775 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK,
776 buf, sizeof(buf));
777 TRACE("%s %d\n", buf, strlen(buf));
778 if((sscanf(buf, "%d", &day) == 1) &&(infoPtr->firstDay != day))
779 infoPtr->firstDay = day;
781 return prev;
785 /* FIXME: fill this in */
786 static LRESULT
787 MONTHCAL_GetMonthRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
789 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
791 TRACE("%x %lx\n", wParam, lParam);
792 FIXME("stub\n");
794 return infoPtr->monthRange;
798 static LRESULT
799 MONTHCAL_GetMaxTodayWidth(HWND hwnd)
801 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
803 return(infoPtr->today.right - infoPtr->today.left);
807 /* FIXME: are validated times taken from current date/time or simply
808 * copied?
809 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
810 * adjusting range with MCM_SETRANGE
813 static LRESULT
814 MONTHCAL_SetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
816 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
817 SYSTEMTIME lprgSysTimeArray[1];
818 int prev;
820 TRACE("%x %lx\n", wParam, lParam);
822 if(wParam & GDTR_MAX) {
823 if(MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
824 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxDate);
825 infoPtr->rangeValid|=GDTR_MAX;
826 } else {
827 GetSystemTime(&infoPtr->todaysDate);
828 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
831 if(wParam & GDTR_MIN) {
832 if(MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
833 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->maxDate);
834 infoPtr->rangeValid|=GDTR_MIN;
835 } else {
836 GetSystemTime(&infoPtr->todaysDate);
837 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
841 prev = infoPtr->monthRange;
842 infoPtr->monthRange = infoPtr->maxDate.wMonth - infoPtr->minDate.wMonth;
844 if(infoPtr->monthRange!=prev) {
845 COMCTL32_ReAlloc(infoPtr->monthdayState,
846 infoPtr->monthRange * sizeof(MONTHDAYSTATE));
849 return 1;
853 /* CHECKME: At the moment, we copy ranges anyway,regardless of
854 * infoPtr->rangeValid; a invalid range is simply filled with zeros in
855 * SetRange. Is this the right behavior?
858 static LRESULT
859 MONTHCAL_GetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
861 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
862 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *)lParam;
864 /* validate parameters */
866 if((infoPtr==NULL) || (lprgSysTimeArray==NULL)) return FALSE;
868 MONTHCAL_CopyTime(&infoPtr->maxDate, &lprgSysTimeArray[1]);
869 MONTHCAL_CopyTime(&infoPtr->minDate, &lprgSysTimeArray[0]);
871 return infoPtr->rangeValid;
875 static LRESULT
876 MONTHCAL_SetDayState(HWND hwnd, WPARAM wParam, LPARAM lParam)
879 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
880 int i, iMonths = (int)wParam;
881 MONTHDAYSTATE *dayStates = (LPMONTHDAYSTATE)lParam;
883 TRACE("%x %lx\n", wParam, lParam);
884 if(iMonths!=infoPtr->monthRange) return 0;
886 for(i=0; i<iMonths; i++)
887 infoPtr->monthdayState[i] = dayStates[i];
888 return 1;
892 static LRESULT
893 MONTHCAL_GetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
895 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
896 SYSTEMTIME *lpSel = (SYSTEMTIME *) lParam;
898 TRACE("%x %lx\n", wParam, lParam);
899 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
900 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
902 MONTHCAL_CopyTime(&infoPtr->minSel, lpSel);
903 return TRUE;
907 /* FIXME: if the specified date is not visible, make it visible */
908 /* FIXME: redraw? */
909 static LRESULT
910 MONTHCAL_SetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
912 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
913 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
915 TRACE("%x %lx\n", wParam, lParam);
916 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
917 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
919 TRACE("%d %d\n", lpSel->wMonth, lpSel->wDay);
921 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
922 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
924 InvalidateRect(hwnd, NULL, FALSE);
926 return TRUE;
930 static LRESULT
931 MONTHCAL_GetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
933 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
935 TRACE("%x %lx\n", wParam, lParam);
936 return infoPtr->maxSelCount;
940 static LRESULT
941 MONTHCAL_SetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
943 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
945 TRACE("%x %lx\n", wParam, lParam);
946 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
947 infoPtr->maxSelCount = wParam;
950 return TRUE;
954 static LRESULT
955 MONTHCAL_GetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
957 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
958 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
960 TRACE("%x %lx\n", wParam, lParam);
962 /* validate parameters */
964 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
966 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT)
968 MONTHCAL_CopyTime(&infoPtr->maxSel, &lprgSysTimeArray[1]);
969 MONTHCAL_CopyTime(&infoPtr->minSel, &lprgSysTimeArray[0]);
970 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
971 return TRUE;
974 return FALSE;
978 static LRESULT
979 MONTHCAL_SetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
981 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
982 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
984 TRACE("%x %lx\n", wParam, lParam);
986 /* validate parameters */
988 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
990 if(GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)
992 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxSel);
993 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minSel);
994 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
995 return TRUE;
998 return FALSE;
1002 static LRESULT
1003 MONTHCAL_GetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
1005 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1006 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1008 TRACE("%x %lx\n", wParam, lParam);
1010 /* validate parameters */
1012 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
1013 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
1014 return TRUE;
1018 static LRESULT
1019 MONTHCAL_SetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
1021 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1022 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1024 TRACE("%x %lx\n", wParam, lParam);
1026 /* validate parameters */
1028 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
1029 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
1030 return TRUE;
1034 static LRESULT
1035 MONTHCAL_HitTest(HWND hwnd, LPARAM lParam)
1037 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1038 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
1039 UINT x,y;
1040 DWORD retval;
1042 x = lpht->pt.x;
1043 y = lpht->pt.y;
1044 retval = MCHT_NOWHERE;
1047 /* are we in the header? */
1049 if(PtInRect(&infoPtr->title, lpht->pt)) {
1050 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1051 retval = MCHT_TITLEBTNPREV;
1052 goto done;
1054 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1055 retval = MCHT_TITLEBTNNEXT;
1056 goto done;
1058 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1059 retval = MCHT_TITLEMONTH;
1060 goto done;
1062 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1063 retval = MCHT_TITLEYEAR;
1064 goto done;
1067 retval = MCHT_TITLE;
1068 goto done;
1071 if(PtInRect(&infoPtr->days, lpht->pt)) {
1072 retval = MCHT_CALENDARDAY; /* FIXME: find out which day we're on */
1073 goto done;
1075 if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
1076 retval = MCHT_CALENDARWEEKNUM; /* FIXME: find out which day we're on */
1077 goto done;
1079 if(PtInRect(&infoPtr->prevmonth, lpht->pt)) {
1080 retval = MCHT_CALENDARDATEPREV;
1081 goto done;
1084 if(PtInRect(&infoPtr->nextmonth, lpht->pt) ||
1085 ((y > infoPtr->nextmonth.bottom) && (y < infoPtr->nextmonth.bottom +
1086 infoPtr->height_increment) && (x < infoPtr->rcClient.right) &&
1087 (x > infoPtr->rcDraw.left))) {
1088 retval = MCHT_CALENDARDATENEXT;
1089 goto done;
1092 if(PtInRect(&infoPtr->today, lpht->pt)) {
1093 retval = MCHT_TODAYLINK;
1094 goto done;
1097 /* MCHT_CALENDARDATE determination: since the next & previous month have
1098 * been handled already(MCHT_CALENDARDATEPREV/NEXT), we only have to check
1099 * whether we're in the calendar area. infoPtr->prevMonth.left handles the
1100 * MCS_WEEKNUMBERS style nicely.
1104 TRACE("%d %d [%d %d %d %d] [%d %d %d %d]\n", x, y,
1105 infoPtr->prevmonth.left, infoPtr->prevmonth.right,
1106 infoPtr->prevmonth.top, infoPtr->prevmonth.bottom,
1107 infoPtr->nextmonth.left, infoPtr->nextmonth.right,
1108 infoPtr->nextmonth.top, infoPtr->nextmonth.bottom);
1109 if((x > infoPtr->rcClient.left) && (x < infoPtr->rcClient.right) &&
1110 (y > infoPtr->rcClient.top) && (y < infoPtr->nextmonth.bottom)) {
1111 lpht->st.wYear = infoPtr->currentYear;
1112 lpht->st.wMonth = infoPtr->currentMonth;
1114 lpht->st.wDay = MONTHCAL_CalcDayFromPos(infoPtr, x, y);
1116 TRACE("day hit: %d\n", lpht->st.wDay);
1117 retval = MCHT_CALENDARDATE;
1118 goto done;
1122 /* Hit nothing special? What's left must be background :-) */
1124 retval = MCHT_CALENDARBK;
1125 done:
1126 lpht->uHit = retval;
1127 return retval;
1131 static void MONTHCAL_GoToNextMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1133 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1135 TRACE("MONTHCAL_GoToNextMonth\n");
1137 infoPtr->currentMonth++;
1138 if(infoPtr->currentMonth > 12) {
1139 infoPtr->currentYear++;
1140 infoPtr->currentMonth = 1;
1143 if(dwStyle & MCS_DAYSTATE) {
1144 NMDAYSTATE nmds;
1145 int i;
1147 nmds.nmhdr.hwndFrom = hwnd;
1148 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1149 nmds.nmhdr.code = MCN_GETDAYSTATE;
1150 nmds.cDayState = infoPtr->monthRange;
1151 nmds.prgDayState = COMCTL32_Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1153 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1154 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1155 for(i=0; i<infoPtr->monthRange; i++)
1156 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1161 static void MONTHCAL_GoToPrevMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1163 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1165 TRACE("MONTHCAL_GoToPrevMonth\n");
1167 infoPtr->currentMonth--;
1168 if(infoPtr->currentMonth < 1) {
1169 infoPtr->currentYear--;
1170 infoPtr->currentMonth = 12;
1173 if(dwStyle & MCS_DAYSTATE) {
1174 NMDAYSTATE nmds;
1175 int i;
1177 nmds.nmhdr.hwndFrom = hwnd;
1178 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1179 nmds.nmhdr.code = MCN_GETDAYSTATE;
1180 nmds.cDayState = infoPtr->monthRange;
1181 nmds.prgDayState = COMCTL32_Alloc
1182 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1184 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1185 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1186 for(i=0; i<infoPtr->monthRange; i++)
1187 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1192 static LRESULT
1193 MONTHCAL_LButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1195 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1196 MCHITTESTINFO ht;
1197 DWORD hit;
1198 HMENU hMenu;
1199 HWND retval;
1200 BOOL redraw = FALSE;
1201 RECT rcDay; /* used in determining area to invalidate */
1203 TRACE("%x %lx\n", wParam, lParam);
1205 ht.pt.x = (INT)LOWORD(lParam);
1206 ht.pt.y = (INT)HIWORD(lParam);
1207 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1209 /* FIXME: these flags should be checked by */
1210 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1211 /* multi-bit */
1212 if(hit & MCHT_NEXT) {
1213 redraw = TRUE;
1214 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1215 infoPtr->status = MC_NEXTPRESSED;
1216 SetTimer(hwnd, MC_NEXTMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1217 InvalidateRect(hwnd, NULL, FALSE);
1219 if(hit & MCHT_PREV) {
1220 redraw = TRUE;
1221 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1222 infoPtr->status = MC_PREVPRESSED;
1223 SetTimer(hwnd, MC_PREVMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1224 InvalidateRect(hwnd, NULL, FALSE);
1227 if(hit == MCHT_TITLEMONTH) {
1229 HRSRC hrsrc = FindResourceA( COMCTL32_hModule, MAKEINTRESOURCEA(IDD_MCMONTHMENU), RT_MENUA );
1230 if(!hrsrc) {
1231 TRACE("returning zero\n");
1232 return 0;
1234 TRACE("resource is:%x\n",hrsrc);
1235 hMenu = LoadMenuIndirectA((LPCVOID)LoadResource( COMCTL32_hModule, hrsrc ));
1237 TRACE("menu is:%x\n",hMenu);
1240 hMenu = CreateMenu();
1241 AppendMenuA(hMenu, MF_STRING,IDM_JAN, "January");
1242 AppendMenuA(hMenu, MF_STRING,IDM_FEB, "February");
1243 AppendMenuA(hMenu, MF_STRING,IDM_MAR, "March");
1245 retval = CreateWindowA(POPUPMENU_CLASS_ATOM, NULL,
1246 WS_CHILD | WS_VISIBLE, 0, 0 ,100 , 220,
1247 hwnd, hMenu, GetWindowLongA(hwnd, GWL_HINSTANCE), NULL);
1248 TRACE("hwnd returned:%x\n", retval);
1251 if(hit == MCHT_TITLEYEAR) {
1252 FIXME("create updown for yearselection\n");
1254 if(hit == MCHT_TODAYLINK) {
1255 FIXME("set currentday\n");
1257 if(hit == MCHT_CALENDARDATE) {
1258 SYSTEMTIME selArray[2];
1259 NMSELCHANGE nmsc;
1261 TRACE("\n");
1262 nmsc.nmhdr.hwndFrom = hwnd;
1263 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1264 nmsc.nmhdr.code = MCN_SELCHANGE;
1265 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1266 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1268 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1269 (WPARAM)nmsc.nmhdr.idFrom,(LPARAM)&nmsc);
1271 MONTHCAL_CopyTime(&ht.st, &selArray[0]);
1272 MONTHCAL_CopyTime(&ht.st, &selArray[1]);
1273 MONTHCAL_SetSelRange(hwnd,0,(LPARAM) &selArray);
1275 /* FIXME: for some reason if RedrawWindow has a NULL instead of zero it gives */
1276 /* a compiler warning */
1277 /* redraw both old and new days if the selected day changed */
1278 if(infoPtr->curSelDay != ht.st.wDay) {
1279 MONTHCAL_CalcPosFromDay(infoPtr, ht.st.wDay, ht.st.wMonth, &rcDay);
1280 RedrawWindow(hwnd, &rcDay, 0, RDW_ERASE|RDW_INVALIDATE);
1282 MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->curSelDay, infoPtr->currentMonth, &rcDay);
1283 RedrawWindow(hwnd, &rcDay, 0, RDW_ERASE|RDW_INVALIDATE);
1286 infoPtr->firstSelDay = ht.st.wDay;
1287 infoPtr->curSelDay = ht.st.wDay;
1288 infoPtr->status = MC_SEL_LBUTDOWN;
1292 return 0;
1296 static LRESULT
1297 MONTHCAL_LButtonUp(HWND hwnd, WPARAM wParam, LPARAM lParam)
1299 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1300 NMSELCHANGE nmsc;
1301 NMHDR nmhdr;
1302 BOOL redraw = FALSE;
1304 TRACE("\n");
1306 if(infoPtr->status & MC_NEXTPRESSED) {
1307 KillTimer(hwnd, MC_NEXTMONTHTIMER);
1308 redraw = TRUE;
1310 if(infoPtr->status & MC_PREVPRESSED) {
1311 KillTimer(hwnd, MC_PREVMONTHTIMER);
1312 redraw = TRUE;
1315 infoPtr->status = MC_SEL_LBUTUP;
1317 nmhdr.hwndFrom = hwnd;
1318 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
1319 nmhdr.code = NM_RELEASEDCAPTURE;
1320 TRACE("Sent notification from %x to %x\n", hwnd, GetParent(hwnd));
1322 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1323 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1325 nmsc.nmhdr.hwndFrom = hwnd;
1326 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1327 nmsc.nmhdr.code = MCN_SELECT;
1328 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1329 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1331 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1332 (WPARAM)nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1334 /* redraw if necessary */
1335 if(redraw)
1336 InvalidateRect(hwnd, NULL, FALSE);
1338 return 0;
1342 static LRESULT
1343 MONTHCAL_Timer(HWND hwnd, WPARAM wParam, LPARAM lParam)
1345 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1346 BOOL redraw = FALSE;
1348 TRACE(" %d\n", wParam);
1349 if(!infoPtr) return 0;
1351 switch(wParam) {
1352 case MC_NEXTMONTHTIMER:
1353 redraw = TRUE;
1354 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1355 break;
1356 case MC_PREVMONTHTIMER:
1357 redraw = TRUE;
1358 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1359 break;
1360 default:
1361 ERR("got unknown timer\n");
1364 /* redraw only if necessary */
1365 if(redraw)
1366 InvalidateRect(hwnd, NULL, FALSE);
1368 return 0;
1372 static LRESULT
1373 MONTHCAL_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1375 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1376 MCHITTESTINFO ht;
1377 int oldselday, selday, hit;
1378 RECT r;
1380 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1382 ht.pt.x = LOWORD(lParam);
1383 ht.pt.y = HIWORD(lParam);
1385 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1387 /* not on the calendar date numbers? bail out */
1388 TRACE("hit:%x\n",hit);
1389 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1391 selday = ht.st.wDay;
1392 oldselday = infoPtr->curSelDay;
1393 infoPtr->curSelDay = selday;
1394 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1396 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1397 SYSTEMTIME selArray[2];
1398 int i;
1400 MONTHCAL_GetSelRange(hwnd, 0, (LPARAM)&selArray);
1401 i = 0;
1402 if(infoPtr->firstSelDay==selArray[0].wDay) i=1;
1403 TRACE("oldRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1404 if(infoPtr->firstSelDay==selArray[1].wDay) {
1405 /* 1st time we get here: selArray[0]=selArray[1]) */
1406 /* if we're still at the first selected date, return */
1407 if(infoPtr->firstSelDay==selday) goto done;
1408 if(selday<infoPtr->firstSelDay) i = 0;
1411 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1412 if(selday>infoPtr->firstSelDay)
1413 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1414 else
1415 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1418 if(selArray[i].wDay!=selday) {
1419 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1421 selArray[i].wDay = selday;
1423 if(selArray[0].wDay>selArray[1].wDay) {
1424 DWORD tempday;
1425 tempday = selArray[1].wDay;
1426 selArray[1].wDay = selArray[0].wDay;
1427 selArray[0].wDay = tempday;
1430 MONTHCAL_SetSelRange(hwnd, 0, (LPARAM)&selArray);
1434 done:
1436 /* only redraw if the currently selected day changed */
1437 /* FIXME: this should specify a rectangle containing only the days that changed */
1438 /* using RedrawWindow */
1439 if(oldselday != infoPtr->curSelDay)
1440 InvalidateRect(hwnd, NULL, FALSE);
1442 return 0;
1446 static LRESULT
1447 MONTHCAL_Paint(HWND hwnd, WPARAM wParam)
1449 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1450 HDC hdc;
1451 PAINTSTRUCT ps;
1453 /* fill ps.rcPaint with a default rect */
1454 memcpy(&(ps.rcPaint), &(infoPtr->rcClient), sizeof(infoPtr->rcClient));
1456 hdc = (wParam==0 ? BeginPaint(hwnd, &ps) : (HDC)wParam);
1457 MONTHCAL_Refresh(hwnd, hdc, &ps);
1458 if(!wParam) EndPaint(hwnd, &ps);
1459 return 0;
1463 static LRESULT
1464 MONTHCAL_KillFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1466 TRACE("\n");
1468 InvalidateRect(hwnd, NULL, TRUE);
1470 return 0;
1474 static LRESULT
1475 MONTHCAL_SetFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1477 TRACE("\n");
1479 InvalidateRect(hwnd, NULL, FALSE);
1481 return 0;
1484 /* sets the size information */
1485 static void MONTHCAL_UpdateSize(HWND hwnd)
1487 HDC hdc = GetDC(hwnd);
1488 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1489 RECT *rcClient=&infoPtr->rcClient;
1490 RECT *rcDraw=&infoPtr->rcDraw;
1491 RECT *title=&infoPtr->title;
1492 RECT *prev=&infoPtr->titlebtnprev;
1493 RECT *next=&infoPtr->titlebtnnext;
1494 RECT *titlemonth=&infoPtr->titlemonth;
1495 RECT *titleyear=&infoPtr->titleyear;
1496 RECT *days=&infoPtr->days;
1497 SIZE size;
1498 TEXTMETRICA tm;
1499 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1500 HFONT currentFont;
1502 currentFont = SelectObject(hdc, infoPtr->hFont);
1504 /* FIXME: need a way to determine current font, without setting it */
1506 if(infoPtr->hFont!=currentFont) {
1507 SelectObject(hdc, currentFont);
1508 infoPtr->hFont=currentFont;
1509 GetObjectA(currentFont, sizeof(LOGFONTA), &logFont);
1510 logFont.lfWeight=FW_BOLD;
1511 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1515 /* get the height and width of each day's text */
1516 GetTextMetricsA(hdc, &tm);
1517 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading;
1518 GetTextExtentPoint32A(hdc, "Sun", 3, &size);
1519 infoPtr->textWidth = size.cx + 2;
1521 /* retrieve the controls client rectangle info infoPtr->rcClient */
1522 GetClientRect(hwnd, rcClient);
1524 if(dwStyle & MCS_WEEKNUMBERS)
1525 infoPtr->rcClient.right+=infoPtr->textWidth;
1527 /* rcDraw is the rectangle the control is drawn in */
1528 rcDraw->left = rcClient->left;
1529 rcDraw->right = rcClient->right;
1530 rcDraw->top = rcClient->top;
1531 rcDraw->bottom = rcClient->bottom;
1533 /* use DrawEdge to adjust the size of rcClient such that we */
1534 /* do not overwrite the border when drawing the control */
1535 DrawEdge((HDC)NULL, rcDraw, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1537 /* this is correct, the control does NOT expand vertically */
1538 /* like it does horizontally */
1539 /* make sure we don't move the controls bottom out of the client */
1540 /* area */
1541 if((rcDraw->top + 8 * infoPtr->textHeight + 5) < rcDraw->bottom) {
1542 rcDraw->bottom = rcDraw->top + 8 * infoPtr->textHeight + 5;
1545 /* calculate title area */
1546 title->top = rcClient->top + 1;
1547 title->bottom = title->top + 2 * infoPtr->textHeight + 4;
1548 title->left = rcClient->left + 1;
1549 title->right = rcClient->right - 1;
1551 /* recalculate the height and width increments and offsets */
1552 infoPtr->width_increment = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) / 7.0;
1553 infoPtr->height_increment = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) / 7.0;
1554 infoPtr->left_offset = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) - (infoPtr->width_increment * 7.0);
1555 infoPtr->top_offset = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) - (infoPtr->height_increment * 7.0);
1557 /* set the dimensions of the next and previous buttons and center */
1558 /* the month text vertically */
1559 prev->top = next->top = title->top + 6;
1560 prev->bottom = next->bottom = title->top + 2 * infoPtr->textHeight - 3;
1561 prev->right = title->left + 28;
1562 prev->left = title->left + 4;
1563 next->left = title->right - 28;
1564 next->right = title->right - 4;
1566 /* titlemonth->left and right change based upon the current month */
1567 /* and are recalculated in refresh as the current month may change */
1568 /* without the control being resized */
1569 titlemonth->bottom = titleyear->bottom = prev->top + 2 * infoPtr->textHeight - 3;
1570 titlemonth->top = titleyear->top = title->top;
1572 /* setup the dimensions of the rectangle we draw the names of the */
1573 /* days of the week in */
1574 days->left = infoPtr->left_offset;
1575 if(dwStyle & MCS_WEEKNUMBERS) days->left+=infoPtr->textWidth;
1576 days->right = days->left + infoPtr->width_increment;
1577 days->top = title->bottom + 2;
1578 days->bottom = title->bottom + infoPtr->textHeight + 2;
1580 /* restore the originally selected font */
1581 SelectObject(hdc, currentFont);
1583 ReleaseDC(hwnd, hdc);
1586 static LRESULT MONTHCAL_Size(HWND hwnd, int Width, int Height)
1588 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
1590 MONTHCAL_UpdateSize(hwnd);
1592 /* invalidate client area and erase background */
1593 InvalidateRect(hwnd, NULL, TRUE);
1595 return 0;
1598 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1599 static LRESULT
1600 MONTHCAL_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
1602 MONTHCAL_INFO *infoPtr;
1603 LOGFONTA logFont;
1605 /* allocate memory for info structure */
1606 infoPtr =(MONTHCAL_INFO*)COMCTL32_Alloc(sizeof(MONTHCAL_INFO));
1607 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1609 if(infoPtr == NULL) {
1610 ERR( "could not allocate info memory!\n");
1611 return 0;
1613 if((MONTHCAL_INFO*)GetWindowLongA(hwnd, 0) != infoPtr) {
1614 ERR( "pointer assignment error!\n");
1615 return 0;
1618 infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1619 GetObjectA(infoPtr->hFont, sizeof(LOGFONTA), &logFont);
1620 logFont.lfWeight = FW_BOLD;
1621 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1623 /* initialize info structure */
1624 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1626 GetSystemTime(&infoPtr->todaysDate);
1627 infoPtr->firstDay = 0;
1628 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1629 infoPtr->currentYear = infoPtr->todaysDate.wYear;
1630 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
1631 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
1632 infoPtr->maxSelCount = 6;
1633 infoPtr->monthRange = 3;
1634 infoPtr->monthdayState = COMCTL32_Alloc
1635 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1636 infoPtr->titlebk = GetSysColor(COLOR_ACTIVECAPTION);
1637 infoPtr->titletxt = GetSysColor(COLOR_WINDOW);
1638 infoPtr->monthbk = GetSysColor(COLOR_WINDOW);
1639 infoPtr->trailingtxt = GetSysColor(COLOR_GRAYTEXT);
1640 infoPtr->bk = GetSysColor(COLOR_WINDOW);
1641 infoPtr->txt = GetSysColor(COLOR_WINDOWTEXT);
1643 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1644 /* of the control */
1645 MONTHCAL_UpdateSize(hwnd);
1647 return 0;
1651 static LRESULT
1652 MONTHCAL_Destroy(HWND hwnd, WPARAM wParam, LPARAM lParam)
1654 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1656 /* free month calendar info data */
1657 COMCTL32_Free(infoPtr);
1658 SetWindowLongA(hwnd, 0, 0);
1659 return 0;
1663 static LRESULT WINAPI
1664 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1666 TRACE("hwnd=%x msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1667 if (!MONTHCAL_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
1668 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
1669 switch(uMsg)
1671 case MCM_GETCURSEL:
1672 return MONTHCAL_GetCurSel(hwnd, wParam, lParam);
1674 case MCM_SETCURSEL:
1675 return MONTHCAL_SetCurSel(hwnd, wParam, lParam);
1677 case MCM_GETMAXSELCOUNT:
1678 return MONTHCAL_GetMaxSelCount(hwnd, wParam, lParam);
1680 case MCM_SETMAXSELCOUNT:
1681 return MONTHCAL_SetMaxSelCount(hwnd, wParam, lParam);
1683 case MCM_GETSELRANGE:
1684 return MONTHCAL_GetSelRange(hwnd, wParam, lParam);
1686 case MCM_SETSELRANGE:
1687 return MONTHCAL_SetSelRange(hwnd, wParam, lParam);
1689 case MCM_GETMONTHRANGE:
1690 return MONTHCAL_GetMonthRange(hwnd, wParam, lParam);
1692 case MCM_SETDAYSTATE:
1693 return MONTHCAL_SetDayState(hwnd, wParam, lParam);
1695 case MCM_GETMINREQRECT:
1696 return MONTHCAL_GetMinReqRect(hwnd, wParam, lParam);
1698 case MCM_GETCOLOR:
1699 return MONTHCAL_GetColor(hwnd, wParam, lParam);
1701 case MCM_SETCOLOR:
1702 return MONTHCAL_SetColor(hwnd, wParam, lParam);
1704 case MCM_GETTODAY:
1705 return MONTHCAL_GetToday(hwnd, wParam, lParam);
1707 case MCM_SETTODAY:
1708 return MONTHCAL_SetToday(hwnd, wParam, lParam);
1710 case MCM_HITTEST:
1711 return MONTHCAL_HitTest(hwnd,lParam);
1713 case MCM_GETFIRSTDAYOFWEEK:
1714 return MONTHCAL_GetFirstDayOfWeek(hwnd, wParam, lParam);
1716 case MCM_SETFIRSTDAYOFWEEK:
1717 return MONTHCAL_SetFirstDayOfWeek(hwnd, wParam, lParam);
1719 case MCM_GETRANGE:
1720 return MONTHCAL_GetRange(hwnd, wParam, lParam);
1722 case MCM_SETRANGE:
1723 return MONTHCAL_SetRange(hwnd, wParam, lParam);
1725 case MCM_GETMONTHDELTA:
1726 return MONTHCAL_GetMonthDelta(hwnd, wParam, lParam);
1728 case MCM_SETMONTHDELTA:
1729 return MONTHCAL_SetMonthDelta(hwnd, wParam, lParam);
1731 case MCM_GETMAXTODAYWIDTH:
1732 return MONTHCAL_GetMaxTodayWidth(hwnd);
1734 case WM_GETDLGCODE:
1735 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1737 case WM_KILLFOCUS:
1738 return MONTHCAL_KillFocus(hwnd, wParam, lParam);
1740 case WM_LBUTTONDOWN:
1741 return MONTHCAL_LButtonDown(hwnd, wParam, lParam);
1743 case WM_MOUSEMOVE:
1744 return MONTHCAL_MouseMove(hwnd, wParam, lParam);
1746 case WM_LBUTTONUP:
1747 return MONTHCAL_LButtonUp(hwnd, wParam, lParam);
1749 case WM_PAINT:
1750 return MONTHCAL_Paint(hwnd, wParam);
1752 case WM_SETFOCUS:
1753 return MONTHCAL_SetFocus(hwnd, wParam, lParam);
1755 case WM_SIZE:
1756 return MONTHCAL_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
1758 case WM_CREATE:
1759 return MONTHCAL_Create(hwnd, wParam, lParam);
1761 case WM_TIMER:
1762 return MONTHCAL_Timer(hwnd, wParam, lParam);
1764 case WM_DESTROY:
1765 return MONTHCAL_Destroy(hwnd, wParam, lParam);
1767 default:
1768 if(uMsg >= WM_USER)
1769 ERR( "unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
1770 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
1772 return 0;
1776 void
1777 MONTHCAL_Register(void)
1779 WNDCLASSA wndClass;
1781 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
1782 wndClass.style = CS_GLOBALCLASS;
1783 wndClass.lpfnWndProc = (WNDPROC)MONTHCAL_WindowProc;
1784 wndClass.cbClsExtra = 0;
1785 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
1786 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
1787 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1788 wndClass.lpszClassName = MONTHCAL_CLASSA;
1790 RegisterClassA(&wndClass);
1794 void
1795 MONTHCAL_Unregister(void)
1797 UnregisterClassA(MONTHCAL_CLASSA, (HINSTANCE)NULL);