X11DRV_SetFocus: really don't mess with focus for managed windows.
[wine/multimedia.git] / dlls / comctl32 / monthcal.c
blob1556b33254469b7baa0498fa6305a9d80ece4964
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: refresh should ask for rect of required length. (?)
14 * FIXME: handle resources better (doesn't work now); also take care
15 of internationalization.
16 * FIXME: keyboard handling.
19 #include <math.h>
20 #include <stdio.h>
22 #include "winbase.h"
23 #include "windef.h"
24 #include "wingdi.h"
25 #include "winuser.h"
26 #include "win.h"
27 #include "winnls.h"
28 #include "commctrl.h"
29 #include "comctl32.h"
30 #include "monthcal.h"
31 #include "debugtools.h"
33 DEFAULT_DEBUG_CHANNEL(monthcal);
35 /* take #days/month from ole/parsedt.c;
36 * we want full month-names, and abbreviated weekdays, so these are
37 * defined here */
39 const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
41 const char * const monthtxt[] = {"January", "February", "March", "April", "May",
42 "June", "July", "August", "September", "October",
43 "November", "December"};
44 const char * const daytxt[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
45 static const int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
48 #define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongA(hwnd, 0))
50 /* helper functions */
52 /* returns the number of days in any given month */
53 /* january is 1, december is 12 */
54 static int MONTHCAL_MonthLength(int month, int year)
56 /* if we have a leap year add 1 day to February */
57 /* a leap year is a year either divisible by 400 */
58 /* or divisible by 4 and not by 100 */
59 if(month == 2) { /* February */
60 return mdays[month - 1] + ((year%400 == 0) ? 1 : ((year%100 != 0) &&
61 (year%4 == 0)) ? 1 : 0);
63 else {
64 return mdays[month - 1];
69 /* make sure that time is valid */
70 static int MONTHCAL_ValidateTime(SYSTEMTIME time)
72 if(time.wMonth > 12) return FALSE;
73 if(time.wDayOfWeek > 6) return FALSE;
74 if(time.wDay > MONTHCAL_MonthLength(time.wMonth, time.wYear))
75 return FALSE;
76 if(time.wHour > 23) return FALSE;
77 if(time.wMinute > 59) return FALSE;
78 if(time.wSecond > 59) return FALSE;
79 if(time.wMilliseconds > 999) return FALSE;
81 return TRUE;
85 void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to)
87 to->wYear = from->wYear;
88 to->wMonth = from->wMonth;
89 to->wDayOfWeek = from->wDayOfWeek;
90 to->wDay = from->wDay;
91 to->wHour = from->wHour;
92 to->wMinute = from->wMinute;
93 to->wSecond = from->wSecond;
94 to->wMilliseconds = from->wMilliseconds;
98 /* Note:Depending on DST, this may be offset by a day.
99 Need to find out if we're on a DST place & adjust the clock accordingly.
100 Above function assumes we have a valid data.
101 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
102 0 = Monday.
105 /* returns the day in the week(0 == sunday, 6 == saturday) */
106 /* day(1 == 1st, 2 == 2nd... etc), year is the year value */
107 int MONTHCAL_CalculateDayOfWeek(DWORD day, DWORD month, DWORD year)
109 year-=(month < 3);
111 return((year + year/4 - year/100 + year/400 +
112 DayOfWeekTable[month-1] + day - 1 ) % 7);
116 static int MONTHCAL_CalcDayFromPos(MONTHCAL_INFO *infoPtr, int x, int y)
118 int daypos, weekpos, retval, firstDay;
120 /* if the point is outside the x bounds of the window put
121 it at the boundry */
122 if(x > (infoPtr->width_increment * 7.0)) {
123 x = infoPtr->rcClient.right - infoPtr->rcClient.left - infoPtr->left_offset;
126 daypos = (x -(infoPtr->prevmonth.left + infoPtr->left_offset)) / infoPtr->width_increment;
127 weekpos = (y - infoPtr->days.bottom - infoPtr->rcClient.top) / infoPtr->height_increment;
129 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
130 retval = daypos + (7 * weekpos) - firstDay;
131 TRACE("%d %d %d\n", daypos, weekpos, retval);
132 return retval;
135 /* day is the day of the month, 1 == 1st day of the month */
136 /* sets x and y to be the position of the day */
137 /* x == day, y == week where(0,0) == sunday, 1st week */
138 static void MONTHCAL_CalcDayXY(MONTHCAL_INFO *infoPtr, int day, int month,
139 int *x, int *y)
141 int firstDay, prevMonth;
143 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
145 if(month==infoPtr->currentMonth) {
146 *x = (day + firstDay) % 7;
147 *y = (day + firstDay - *x) / 7;
148 return;
150 if(month < infoPtr->currentMonth) {
151 prevMonth = month - 1;
152 if(prevMonth==0)
153 prevMonth = 12;
155 *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay) % 7;
156 *y = 0;
157 return;
160 *y = MONTHCAL_MonthLength(month, infoPtr->currentYear - 1) / 7;
161 *x = (day + firstDay + MONTHCAL_MonthLength(month,
162 infoPtr->currentYear)) % 7;
166 /* x: column(day), y: row(week) */
167 static void MONTHCAL_CalcDayRect(MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
169 r->left = infoPtr->prevmonth.left + x * infoPtr->width_increment + infoPtr->left_offset;
170 r->right = r->left + infoPtr->width_increment;
171 r->top = infoPtr->height_increment * y + infoPtr->days.bottom + infoPtr->top_offset;
172 r->bottom = r->top + infoPtr->textHeight;
176 /* sets the RECT struct r to the rectangle around the day and month */
177 /* day is the day value of the month(1 == 1st), month is the month */
178 /* value(january == 1, december == 12) */
179 static inline void MONTHCAL_CalcPosFromDay(MONTHCAL_INFO *infoPtr,
180 int day, int month, RECT *r)
182 int x, y;
184 MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
185 MONTHCAL_CalcDayRect(infoPtr, r, x, y);
189 /* day is the day in the month(1 == 1st of the month) */
190 /* month is the month value(1 == january, 12 == december) */
191 static void MONTHCAL_CircleDay(HDC hdc, MONTHCAL_INFO *infoPtr, int day,
192 int month)
194 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
195 HPEN hOldPen2 = SelectObject(hdc, hRedPen);
196 POINT points[13];
197 int x, y;
198 RECT day_rect;
200 /* use prevmonth to calculate position because it contains the extra width
201 * from MCS_WEEKNUMBERS
204 MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
206 x = day_rect.left;
207 y = day_rect.top;
209 points[0].x = x;
210 points[0].y = y - 1;
211 points[1].x = x + 0.8 * infoPtr->width_increment;
212 points[1].y = y - 1;
213 points[2].x = x + 0.9 * infoPtr->width_increment;
214 points[2].y = y;
215 points[3].x = x + infoPtr->width_increment;
216 points[3].y = y + 0.5 * infoPtr->textHeight;
218 points[4].x = x + infoPtr->width_increment;
219 points[4].y = y + 0.9 * infoPtr->textHeight;
220 points[5].x = x + 0.6 * infoPtr->width_increment;
221 points[5].y = y + 0.9 * infoPtr->textHeight;
222 points[6].x = x + 0.5 * infoPtr->width_increment;
223 points[6].y = y + 0.9 * infoPtr->textHeight; /* bring the bottom up just
224 a hair to fit inside the day rectangle */
226 points[7].x = x + 0.2 * infoPtr->width_increment;
227 points[7].y = y + 0.8 * infoPtr->textHeight;
228 points[8].x = x + 0.1 * infoPtr->width_increment;
229 points[8].y = y + 0.8 * infoPtr->textHeight;
230 points[9].x = x;
231 points[9].y = y + 0.5 * infoPtr->textHeight;
233 points[10].x = x + 0.1 * infoPtr->width_increment;
234 points[10].y = y + 0.2 * infoPtr->textHeight;
235 points[11].x = x + 0.2 * infoPtr->width_increment;
236 points[11].y = y + 0.3 * infoPtr->textHeight;
237 points[12].x = x + 0.5 * infoPtr->width_increment;
238 points[12].y = y + 0.3 * infoPtr->textHeight;
240 PolyBezier(hdc, points, 13);
241 DeleteObject(hRedPen);
242 SelectObject(hdc, hOldPen2);
246 static void MONTHCAL_DrawDay(HDC hdc, MONTHCAL_INFO *infoPtr,
247 int day, int month, int x, int y, int bold)
249 char buf[10];
250 RECT r;
251 static int haveBoldFont, haveSelectedDay = FALSE;
252 HBRUSH hbr;
253 HPEN hNewPen, hOldPen = 0;
254 COLORREF oldCol = 0;
255 COLORREF oldBk = 0;
257 sprintf(buf, "%d", day);
259 /* No need to check styles: when selection is not valid, it is set to zero.
260 * 1<day<31, so evertyhing's OK.
263 MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
265 if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
266 && (month==infoPtr->currentMonth)) {
267 HRGN hrgn;
268 RECT r2;
270 TRACE("%d %d %d\n",day, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
271 TRACE("%d %d %d %d\n", r.left, r.top, r.right, r.bottom);
272 oldCol = SetTextColor(hdc, infoPtr->monthbk);
273 oldBk = SetBkColor(hdc, infoPtr->trailingtxt);
274 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
275 hrgn = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
276 FillRgn(hdc, hrgn, hbr);
278 /* FIXME: this may need to be changed now b/c of the other
279 drawing changes 11/3/99 CMM */
280 r2.left = r.left - 0.25 * infoPtr->textWidth;
281 r2.top = r.top;
282 r2.right = r.left + 0.5 * infoPtr->textWidth;
283 r2.bottom = r.bottom;
284 if(haveSelectedDay) FillRect(hdc, &r2, hbr);
285 haveSelectedDay = TRUE;
286 } else {
287 haveSelectedDay = FALSE;
290 /* need to add some code for multiple selections */
292 if((bold) &&(!haveBoldFont)) {
293 SelectObject(hdc, infoPtr->hBoldFont);
294 haveBoldFont = TRUE;
296 if((!bold) &&(haveBoldFont)) {
297 SelectObject(hdc, infoPtr->hFont);
298 haveBoldFont = FALSE;
301 if(haveSelectedDay) {
302 SetTextColor(hdc, oldCol);
303 SetBkColor(hdc, oldBk);
306 DrawTextA(hdc, buf, lstrlenA(buf), &r,
307 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
309 /* draw a rectangle around the currently selected days text */
310 if((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth)) {
311 hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
312 hbr = GetSysColorBrush(COLOR_WINDOWTEXT);
313 FrameRect(hdc, &r, hbr);
314 SelectObject(hdc, hOldPen);
319 /* CHECKME: For `todays date', do we need to check the locale?*/
320 static void MONTHCAL_Refresh(HWND hwnd, HDC hdc)
322 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
323 RECT *rcClient=&infoPtr->rcClient;
324 RECT *rcDraw=&infoPtr->rcDraw;
325 RECT *title=&infoPtr->title;
326 RECT *prev=&infoPtr->titlebtnprev;
327 RECT *next=&infoPtr->titlebtnnext;
328 RECT *titlemonth=&infoPtr->titlemonth;
329 RECT *titleyear=&infoPtr->titleyear;
330 RECT *prevmonth=&infoPtr->prevmonth;
331 RECT *nextmonth=&infoPtr->nextmonth;
332 RECT dayrect;
333 RECT *days=&dayrect;
334 RECT *weeknums=&infoPtr->weeknums;
335 RECT *rtoday=&infoPtr->today;
336 int i, j, m, mask, day, firstDay, weeknum, prevMonth;
337 int textHeight = infoPtr->textHeight, textWidth = infoPtr->textWidth;
338 SIZE size;
339 HBRUSH hbr;
340 HFONT currentFont;
341 /* LOGFONTA logFont; */
342 char buf[20];
343 const char *thisMonthtxt;
344 COLORREF oldTextColor, oldBkColor;
345 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
346 BOOL prssed;
348 oldTextColor = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
350 /* draw control edge */
351 hbr = CreateSolidBrush(RGB(255, 255, 255));
352 FillRect(hdc, rcClient, hbr);
353 DrawEdge(hdc, rcClient, EDGE_SUNKEN, BF_RECT);
354 DeleteObject(hbr);
355 prssed = FALSE;
357 /* draw header */
358 hbr = CreateSolidBrush(infoPtr->titlebk);
359 FillRect(hdc, title, hbr);
361 /* if the previous button is pressed draw it depressed */
362 if((infoPtr->status & MC_PREVPRESSED))
363 DrawFrameControl(hdc, prev, DFC_SCROLL,
364 DFCS_SCROLLLEFT | DFCS_PUSHED |
365 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
366 else /* if the previous button is pressed draw it depressed */
367 DrawFrameControl(hdc, prev, DFC_SCROLL,
368 DFCS_SCROLLLEFT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
370 /* if next button is depressed draw it depressed */
371 if((infoPtr->status & MC_NEXTPRESSED))
372 DrawFrameControl(hdc, next, DFC_SCROLL,
373 DFCS_SCROLLRIGHT | DFCS_PUSHED |
374 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
375 else /* if the next button is pressed draw it depressed */
376 DrawFrameControl(hdc, next, DFC_SCROLL,
377 DFCS_SCROLLRIGHT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
379 oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
380 SetTextColor(hdc, infoPtr->titletxt);
381 currentFont = SelectObject(hdc, infoPtr->hBoldFont);
383 /* titlemonth->left and right are set in MONTHCAL_UpdateSize */
384 titlemonth->left = title->left;
385 titlemonth->right = title->right;
387 thisMonthtxt = monthtxt[infoPtr->currentMonth - 1];
388 sprintf(buf, "%s %ld", thisMonthtxt, infoPtr->currentYear);
389 DrawTextA(hdc, buf, strlen(buf), titlemonth,
390 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
391 SelectObject(hdc, infoPtr->hFont);
393 /* titlemonth left/right contained rect for whole titletxt('June 1999')
394 * MCM_HitTestInfo wants month & year rects, so prepare these now.
395 *(no, we can't draw them separately; the whole text is centered)
397 GetTextExtentPoint32A(hdc, buf, lstrlenA(buf), &size);
398 titlemonth->left = title->right / 2 - size.cx / 2;
399 titleyear->right = title->right / 2 + size.cx / 2;
400 GetTextExtentPoint32A(hdc, thisMonthtxt, lstrlenA(thisMonthtxt), &size);
401 titlemonth->right = titlemonth->left + size.cx;
402 titleyear->right = titlemonth->right;
405 /* draw line under day abbreviatons */
407 if(dwStyle & MCS_WEEKNUMBERS)
408 MoveToEx(hdc, rcDraw->left + textWidth + 3, title->bottom + textHeight + 2, NULL);
409 else
410 MoveToEx(hdc, rcDraw->left + 3, title->bottom + textHeight + 2, NULL);
412 LineTo(hdc, rcDraw->right - 3, title->bottom + textHeight + 2);
414 /* draw day abbreviations */
416 SetBkColor(hdc, infoPtr->monthbk);
417 SetTextColor(hdc, infoPtr->trailingtxt);
419 /* copy this rect so we can change the values without changing */
420 /* the original version */
421 days->left = infoPtr->days.left;
422 days->right = infoPtr->days.right;
423 days->top = infoPtr->days.top;
424 days->bottom = infoPtr->days.bottom;
426 i = infoPtr->firstDay;
428 for(j=0; j<7; j++) {
429 DrawTextA(hdc, daytxt[i], strlen(daytxt[i]), days,
430 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
431 i = (i + 1) % 7;
432 days->left+=infoPtr->width_increment;
433 days->right+=infoPtr->width_increment;
436 days->left = rcDraw->left + j;
437 if(dwStyle & MCS_WEEKNUMBERS) days->left+=textWidth;
438 /* FIXME: this may need to be changed now 11/10/99 CMM */
439 days->right = rcDraw->left + (j+1) * textWidth - 2;
441 /* draw day numbers; first, the previous month */
443 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
445 prevMonth = infoPtr->currentMonth - 1;
446 if(prevMonth == 0) /* if currentMonth is january(1) prevMonth is */
447 prevMonth = 12; /* december(12) of the previous year */
449 day = MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay;
450 mask = 1<<(day-1);
452 i = 0;
453 m = 0;
454 while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear)) {
455 MONTHCAL_DrawDay(hdc, infoPtr, day, prevMonth, i, 0,
456 infoPtr->monthdayState[m] & mask);
457 mask<<=1;
458 day++;
459 i++;
462 prevmonth->left = 0;
463 if(dwStyle & MCS_WEEKNUMBERS) prevmonth->left = textWidth;
464 prevmonth->right = prevmonth->left + i * textWidth;
465 prevmonth->top = days->bottom;
466 prevmonth->bottom = prevmonth->top + textHeight;
468 /* draw `current' month */
470 day = 1; /* start at the beginning of the current month */
472 infoPtr->firstDayplace = i;
473 SetTextColor(hdc, infoPtr->txt);
474 m++;
475 mask = 1;
477 /* draw the first week of the current month */
478 while(i<7) {
479 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, 0,
480 infoPtr->monthdayState[m] & mask);
482 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
483 (day==infoPtr->todaysDate.wDay) &&
484 (infoPtr->currentYear == infoPtr->todaysDate.wYear)) {
485 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
488 mask<<=1;
489 day++;
490 i++;
493 j = 1; /* move to the 2nd week of the current month */
494 i = 0; /* move back to sunday */
495 while(day <= MONTHCAL_MonthLength(infoPtr->currentMonth, infoPtr->currentYear)) {
496 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, j,
497 infoPtr->monthdayState[m] & mask);
499 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
500 (day==infoPtr->todaysDate.wDay) &&
501 (infoPtr->currentYear == infoPtr->todaysDate.wYear))
502 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
504 mask<<=1;
505 day++;
506 i++;
507 if(i>6) { /* past saturday, goto the next weeks sunday */
508 i = 0;
509 j++;
513 /* draw `next' month */
515 /* note: the nextmonth rect only hints for the `half-week' that needs to be
516 * drawn to complete the current week. An eventual next week that needs to
517 * be drawn to complete the month calendar is not taken into account in
518 * this rect -- HitTest knows about this.*/
520 nextmonth->left = prevmonth->left + i * textWidth;
521 nextmonth->right = rcDraw->right;
522 nextmonth->top = days->bottom + (j+1) * textHeight;
523 nextmonth->bottom = nextmonth->top + textHeight;
525 day = 1; /* start at the first day of the next month */
526 m++;
527 mask = 1;
529 SetTextColor(hdc, infoPtr->trailingtxt);
530 while((i<7) &&(j<6)) {
531 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth + 1, i, j,
532 infoPtr->monthdayState[m] & mask);
534 mask<<=1;
535 day++;
536 i++;
537 if(i==7) { /* past saturday, go to next week's sunday */
538 i = 0;
539 j++;
542 SetTextColor(hdc, infoPtr->txt);
545 /* draw `today' date if style allows it, and draw a circle before today's
546 * date if necessary */
548 if(!(dwStyle & MCS_NOTODAY)) {
549 int offset = 0;
550 if(!(dwStyle & MCS_NOTODAYCIRCLE)) {
551 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth + 1);
552 offset+=textWidth;
554 MONTHCAL_CalcDayRect(infoPtr, rtoday, offset==textWidth, 6);
555 sprintf(buf, "Today: %d/%d/%d", infoPtr->todaysDate.wMonth,
556 infoPtr->todaysDate.wDay, infoPtr->todaysDate.wYear % 100);
557 rtoday->right = rcDraw->right;
558 SelectObject(hdc, infoPtr->hBoldFont);
559 DrawTextA(hdc, buf, lstrlenA(buf), rtoday,
560 DT_LEFT | DT_VCENTER | DT_SINGLELINE);
561 SelectObject(hdc, infoPtr->hFont);
564 if(dwStyle & MCS_WEEKNUMBERS) {
565 /* display weeknumbers*/
566 weeknums->left = 0;
567 weeknums->right = textWidth;
568 weeknums->top = days->bottom + 2;
569 weeknums->bottom = days->bottom + 2 + textHeight;
571 weeknum = 0;
572 for(i=0; i<infoPtr->currentMonth-1; i++)
573 weeknum+=MONTHCAL_MonthLength(i, infoPtr->currentYear);
575 weeknum/=7;
576 for(i=0; i<6; i++) {
577 sprintf(buf, "%d", weeknum + i);
578 DrawTextA(hdc, buf, lstrlenA(buf), weeknums,
579 DT_CENTER | DT_BOTTOM | DT_SINGLELINE );
580 weeknums->top+=textHeight * 1.25;
581 weeknums->bottom+=textHeight * 1.25;
584 MoveToEx(hdc, weeknums->right, days->bottom + 5 , NULL);
585 LineTo(hdc, weeknums->right, weeknums->bottom - 1.25 * textHeight - 5);
589 /* currentFont was font at entering Refresh */
591 SetBkColor(hdc, oldBkColor);
592 SelectObject(hdc, currentFont);
593 SetTextColor(hdc, oldTextColor);
597 static LRESULT
598 MONTHCAL_GetMinReqRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
600 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
601 LPRECT lpRect = (LPRECT) lParam;
602 TRACE("%x %lx\n", wParam, lParam);
604 /* validate parameters */
606 if((infoPtr==NULL) ||(lpRect == NULL) ) return FALSE;
608 lpRect->left = infoPtr->rcClient.left;
609 lpRect->right = infoPtr->rcClient.right;
610 lpRect->top = infoPtr->rcClient.top;
611 lpRect->bottom = infoPtr->rcClient.bottom;
612 return TRUE;
615 static LRESULT
616 MONTHCAL_GetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
618 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
620 TRACE("%x %lx\n", wParam, lParam);
622 switch((int)wParam) {
623 case MCSC_BACKGROUND:
624 return infoPtr->bk;
625 case MCSC_TEXT:
626 return infoPtr->txt;
627 case MCSC_TITLEBK:
628 return infoPtr->titlebk;
629 case MCSC_TITLETEXT:
630 return infoPtr->titletxt;
631 case MCSC_MONTHBK:
632 return infoPtr->monthbk;
633 case MCSC_TRAILINGTEXT:
634 return infoPtr->trailingtxt;
637 return -1;
640 static LRESULT
641 MONTHCAL_SetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
643 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
644 int prev = -1;
646 TRACE("%x %lx\n", wParam, lParam);
648 switch((int)wParam) {
649 case MCSC_BACKGROUND:
650 prev = infoPtr->bk;
651 infoPtr->bk = (COLORREF)lParam;
652 break;
653 case MCSC_TEXT:
654 prev = infoPtr->txt;
655 infoPtr->txt = (COLORREF)lParam;
656 break;
657 case MCSC_TITLEBK:
658 prev = infoPtr->titlebk;
659 infoPtr->titlebk = (COLORREF)lParam;
660 break;
661 case MCSC_TITLETEXT:
662 prev=infoPtr->titletxt;
663 infoPtr->titletxt = (COLORREF)lParam;
664 break;
665 case MCSC_MONTHBK:
666 prev = infoPtr->monthbk;
667 infoPtr->monthbk = (COLORREF)lParam;
668 break;
669 case MCSC_TRAILINGTEXT:
670 prev = infoPtr->trailingtxt;
671 infoPtr->trailingtxt = (COLORREF)lParam;
672 break;
675 return prev;
678 static LRESULT
679 MONTHCAL_GetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
681 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
683 TRACE("%x %lx\n", wParam, lParam);
685 if(infoPtr->delta)
686 return infoPtr->delta;
687 else
688 return infoPtr->visible;
691 static LRESULT
692 MONTHCAL_SetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
694 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
695 int prev = infoPtr->delta;
697 TRACE("%x %lx\n", wParam, lParam);
699 infoPtr->delta = (int)wParam;
700 return prev;
704 static LRESULT
705 MONTHCAL_GetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
707 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
709 return infoPtr->firstDay;
713 /* sets the first day of the week that will appear in the control */
714 /* 0 == Monday, 6 == Sunday */
715 /* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
716 /* FIXME: we need more error checking here */
717 static LRESULT
718 MONTHCAL_SetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
720 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
721 int prev = infoPtr->firstDay;
722 char buf[40];
723 int day;
725 TRACE("%x %lx\n", wParam, lParam);
727 if((lParam >= 0) && (lParam < 7)) {
728 infoPtr->firstDay = (int)lParam;
729 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK,
730 buf, sizeof(buf));
731 TRACE("%s %d\n", buf, strlen(buf));
732 if((sscanf(buf, "%d", &day) == 1) &&(infoPtr->firstDay != day))
733 infoPtr->firstDay = day;
735 return prev;
739 /* FIXME: fill this in */
740 static LRESULT
741 MONTHCAL_GetMonthRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
743 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
745 TRACE("%x %lx\n", wParam, lParam);
746 FIXME("stub\n");
748 return infoPtr->monthRange;
752 static LRESULT
753 MONTHCAL_GetMaxTodayWidth(HWND hwnd)
755 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
757 return(infoPtr->today.right - infoPtr->today.left);
761 /* FIXME: are validated times taken from current date/time or simply
762 * copied?
763 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
764 * adjusting range with MCM_SETRANGE
767 static LRESULT
768 MONTHCAL_SetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
770 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
771 SYSTEMTIME lprgSysTimeArray[1];
772 int prev;
774 TRACE("%x %lx\n", wParam, lParam);
776 if(wParam & GDTR_MAX) {
777 if(MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
778 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxDate);
779 infoPtr->rangeValid|=GDTR_MAX;
780 } else {
781 GetSystemTime(&infoPtr->todaysDate);
782 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
785 if(wParam & GDTR_MIN) {
786 if(MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
787 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->maxDate);
788 infoPtr->rangeValid|=GDTR_MIN;
789 } else {
790 GetSystemTime(&infoPtr->todaysDate);
791 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
795 prev = infoPtr->monthRange;
796 infoPtr->monthRange = infoPtr->maxDate.wMonth - infoPtr->minDate.wMonth;
798 if(infoPtr->monthRange!=prev) {
799 COMCTL32_ReAlloc(infoPtr->monthdayState,
800 infoPtr->monthRange * sizeof(MONTHDAYSTATE));
803 return 1;
807 /* CHECKME: At the moment, we copy ranges anyway,regardless of
808 * infoPtr->rangeValid; a invalid range is simply filled with zeros in
809 * SetRange. Is this the right behavior?
812 static LRESULT
813 MONTHCAL_GetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
815 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
816 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *)lParam;
818 /* validate parameters */
820 if((infoPtr==NULL) || (lprgSysTimeArray==NULL)) return FALSE;
822 MONTHCAL_CopyTime(&infoPtr->maxDate, &lprgSysTimeArray[1]);
823 MONTHCAL_CopyTime(&infoPtr->minDate, &lprgSysTimeArray[0]);
825 return infoPtr->rangeValid;
829 static LRESULT
830 MONTHCAL_SetDayState(HWND hwnd, WPARAM wParam, LPARAM lParam)
833 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
834 int i, iMonths = (int)wParam;
835 MONTHDAYSTATE *dayStates = (LPMONTHDAYSTATE)lParam;
837 TRACE("%x %lx\n", wParam, lParam);
838 if(iMonths!=infoPtr->monthRange) return 0;
840 for(i=0; i<iMonths; i++)
841 infoPtr->monthdayState[i] = dayStates[i];
842 return 1;
846 static LRESULT
847 MONTHCAL_GetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
849 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
850 SYSTEMTIME *lpSel = (SYSTEMTIME *) lParam;
852 TRACE("%x %lx\n", wParam, lParam);
853 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
854 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
856 MONTHCAL_CopyTime(&infoPtr->minSel, lpSel);
857 return TRUE;
861 /* FIXME: if the specified date is not visible, make it visible */
862 /* FIXME: redraw? */
863 static LRESULT
864 MONTHCAL_SetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
866 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
867 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
869 TRACE("%x %lx\n", wParam, lParam);
870 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
871 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
873 TRACE("%d %d\n", lpSel->wMonth, lpSel->wDay);
875 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
876 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
878 return TRUE;
882 static LRESULT
883 MONTHCAL_GetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
885 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
887 TRACE("%x %lx\n", wParam, lParam);
888 return infoPtr->maxSelCount;
892 static LRESULT
893 MONTHCAL_SetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
895 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
897 TRACE("%x %lx\n", wParam, lParam);
898 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
899 infoPtr->maxSelCount = wParam;
902 return TRUE;
906 static LRESULT
907 MONTHCAL_GetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
909 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
910 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
912 TRACE("%x %lx\n", wParam, lParam);
914 /* validate parameters */
916 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
918 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT)
920 MONTHCAL_CopyTime(&infoPtr->maxSel, &lprgSysTimeArray[1]);
921 MONTHCAL_CopyTime(&infoPtr->minSel, &lprgSysTimeArray[0]);
922 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
923 return TRUE;
926 return FALSE;
930 static LRESULT
931 MONTHCAL_SetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
933 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
934 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
936 TRACE("%x %lx\n", wParam, lParam);
938 /* validate parameters */
940 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
942 if(GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)
944 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxSel);
945 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minSel);
946 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
947 return TRUE;
950 return FALSE;
954 static LRESULT
955 MONTHCAL_GetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
957 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
958 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
960 TRACE("%x %lx\n", wParam, lParam);
962 /* validate parameters */
964 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
965 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
966 return TRUE;
970 static LRESULT
971 MONTHCAL_SetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
973 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
974 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
976 TRACE("%x %lx\n", wParam, lParam);
978 /* validate parameters */
980 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
981 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
982 return TRUE;
986 static LRESULT
987 MONTHCAL_HitTest(HWND hwnd, LPARAM lParam)
989 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
990 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
991 UINT x,y;
992 DWORD retval;
994 x = lpht->pt.x;
995 y = lpht->pt.y;
996 retval = MCHT_NOWHERE;
999 /* are we in the header? */
1001 if(PtInRect(&infoPtr->title, lpht->pt)) {
1002 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1003 retval = MCHT_TITLEBTNPREV;
1004 goto done;
1006 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1007 retval = MCHT_TITLEBTNNEXT;
1008 goto done;
1010 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1011 retval = MCHT_TITLEMONTH;
1012 goto done;
1014 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1015 retval = MCHT_TITLEYEAR;
1016 goto done;
1019 retval = MCHT_TITLE;
1020 goto done;
1023 if(PtInRect(&infoPtr->days, lpht->pt)) {
1024 retval = MCHT_CALENDARDAY; /* FIXME: find out which day we're on */
1025 goto done;
1027 if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
1028 retval = MCHT_CALENDARWEEKNUM; /* FIXME: find out which day we're on */
1029 goto done;
1031 if(PtInRect(&infoPtr->prevmonth, lpht->pt)) {
1032 retval = MCHT_CALENDARDATEPREV;
1033 goto done;
1036 if(PtInRect(&infoPtr->nextmonth, lpht->pt) ||
1037 ((x>infoPtr->nextmonth.left) &&(x<infoPtr->nextmonth.right) &&
1038 (y>infoPtr->nextmonth.bottom) &&(y<infoPtr->today.top ))) {
1039 retval = MCHT_CALENDARDATENEXT;
1040 goto done;
1043 if(PtInRect(&infoPtr->today, lpht->pt)) {
1044 retval = MCHT_TODAYLINK;
1045 goto done;
1048 /* MCHT_CALENDARDATE determination: since the next & previous month have
1049 * been handled already(MCHT_CALENDARDATEPREV/NEXT), we only have to check
1050 * whether we're in the calendar area. infoPtr->prevMonth.left handles the
1051 * MCS_WEEKNUMBERS style nicely.
1055 TRACE("%d %d [%d %d %d %d] [%d %d %d %d]\n", x, y,
1056 infoPtr->prevmonth.left, infoPtr->prevmonth.right,
1057 infoPtr->prevmonth.top, infoPtr->prevmonth.bottom,
1058 infoPtr->nextmonth.left, infoPtr->nextmonth.right,
1059 infoPtr->nextmonth.top, infoPtr->nextmonth.bottom);
1060 if((x>infoPtr->prevmonth.left) &&(x<infoPtr->nextmonth.right) &&
1061 (y>infoPtr->prevmonth.top) &&(y<infoPtr->nextmonth.bottom)) {
1062 lpht->st.wYear = infoPtr->currentYear;
1063 lpht->st.wMonth = infoPtr->currentMonth;
1065 lpht->st.wDay = MONTHCAL_CalcDayFromPos(infoPtr, x, y);
1067 TRACE("day hit: %d\n", lpht->st.wDay);
1068 retval = MCHT_CALENDARDATE;
1069 goto done;
1073 /* Hit nothing special? What's left must be background :-) */
1075 retval = MCHT_CALENDARBK;
1076 done:
1077 lpht->uHit = retval;
1078 return retval;
1082 static void MONTHCAL_GoToNextMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1084 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1086 TRACE("MONTHCAL_GoToNextMonth\n");
1088 infoPtr->currentMonth++;
1089 if(infoPtr->currentMonth > 12) {
1090 infoPtr->currentYear++;
1091 infoPtr->currentMonth = 1;
1094 if(dwStyle & MCS_DAYSTATE) {
1095 NMDAYSTATE nmds;
1096 int i;
1098 nmds.nmhdr.hwndFrom = hwnd;
1099 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1100 nmds.nmhdr.code = MCN_GETDAYSTATE;
1101 nmds.cDayState = infoPtr->monthRange;
1102 nmds.prgDayState = COMCTL32_Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1104 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1105 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1106 for(i=0; i<infoPtr->monthRange; i++)
1107 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1112 static void MONTHCAL_GoToPrevMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1114 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1116 TRACE("MONTHCAL_GoToPrevMonth\n");
1118 infoPtr->currentMonth--;
1119 if(infoPtr->currentMonth < 1) {
1120 infoPtr->currentYear--;
1121 infoPtr->currentMonth = 12;
1124 if(dwStyle & MCS_DAYSTATE) {
1125 NMDAYSTATE nmds;
1126 int i;
1128 nmds.nmhdr.hwndFrom = hwnd;
1129 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1130 nmds.nmhdr.code = MCN_GETDAYSTATE;
1131 nmds.cDayState = infoPtr->monthRange;
1132 nmds.prgDayState = COMCTL32_Alloc
1133 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1135 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1136 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1137 for(i=0; i<infoPtr->monthRange; i++)
1138 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1143 static LRESULT
1144 MONTHCAL_LButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1146 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1147 MCHITTESTINFO ht;
1148 HDC hdc;
1149 DWORD hit;
1150 HMENU hMenu;
1151 HWND retval;
1152 BOOL redraw = FALSE;
1155 TRACE("%x %lx\n", wParam, lParam);
1157 ht.pt.x = (INT)LOWORD(lParam);
1158 ht.pt.y = (INT)HIWORD(lParam);
1159 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1161 /* FIXME: these flags should be checked by */
1162 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1163 /* multi-bit */
1164 if(hit & MCHT_NEXT) {
1165 redraw = TRUE;
1166 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1167 infoPtr->status = MC_NEXTPRESSED;
1168 SetTimer(hwnd, MC_NEXTMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1170 if(hit & MCHT_PREV) {
1171 redraw = TRUE;
1172 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1173 infoPtr->status = MC_PREVPRESSED;
1174 SetTimer(hwnd, MC_PREVMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1177 if(hit == MCHT_TITLEMONTH) {
1179 HRSRC hrsrc = FindResourceA( COMCTL32_hModule, MAKEINTRESOURCEA(IDD_MCMONTHMENU), RT_MENUA );
1180 if(!hrsrc) {
1181 TRACE("returning zero\n");
1182 return 0;
1184 TRACE("resource is:%x\n",hrsrc);
1185 hMenu = LoadMenuIndirectA((LPCVOID)LoadResource( COMCTL32_hModule, hrsrc ));
1187 TRACE("menu is:%x\n",hMenu);
1190 hMenu = CreateMenu();
1191 AppendMenuA(hMenu, MF_STRING,IDM_JAN, "January");
1192 AppendMenuA(hMenu, MF_STRING,IDM_FEB, "February");
1193 AppendMenuA(hMenu, MF_STRING,IDM_MAR, "March");
1195 retval = CreateWindowA(POPUPMENU_CLASS_ATOM, NULL,
1196 WS_CHILD | WS_VISIBLE, 0, 0 ,100 , 220,
1197 hwnd, hMenu, GetWindowLongA(hwnd, GWL_HINSTANCE), NULL);
1198 TRACE("hwnd returned:%x\n", retval);
1201 if(hit == MCHT_TITLEYEAR) {
1202 FIXME("create updown for yearselection\n");
1204 if(hit == MCHT_TODAYLINK) {
1205 FIXME("set currentday\n");
1207 if(hit == MCHT_CALENDARDATE) {
1208 SYSTEMTIME selArray[2];
1209 NMSELCHANGE nmsc;
1211 TRACE("\n");
1212 nmsc.nmhdr.hwndFrom = hwnd;
1213 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1214 nmsc.nmhdr.code = MCN_SELCHANGE;
1215 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1216 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1218 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1219 (WPARAM)nmsc.nmhdr.idFrom,(LPARAM)&nmsc);
1221 MONTHCAL_CopyTime(&ht.st, &selArray[0]);
1222 MONTHCAL_CopyTime(&ht.st, &selArray[1]);
1223 MONTHCAL_SetSelRange(hwnd,0,(LPARAM) &selArray);
1225 /* redraw if the selected day changed */
1226 if(infoPtr->curSelDay != ht.st.wDay) {
1227 redraw = TRUE;
1230 infoPtr->firstSelDay = ht.st.wDay;
1231 infoPtr->curSelDay = ht.st.wDay;
1232 infoPtr->status = MC_SEL_LBUTDOWN;
1235 /* redraw only if the control changed */
1236 if(redraw) {
1237 hdc = GetDC(hwnd);
1238 MONTHCAL_Refresh(hwnd, hdc);
1239 ReleaseDC(hwnd, hdc);
1242 return 0;
1246 static LRESULT
1247 MONTHCAL_LButtonUp(HWND hwnd, WPARAM wParam, LPARAM lParam)
1249 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1250 NMSELCHANGE nmsc;
1251 NMHDR nmhdr;
1252 HDC hdc;
1253 BOOL redraw = FALSE;
1255 TRACE("\n");
1257 if(infoPtr->status & MC_NEXTPRESSED) {
1258 KillTimer(hwnd, MC_NEXTMONTHTIMER);
1259 redraw = TRUE;
1261 if(infoPtr->status & MC_PREVPRESSED) {
1262 KillTimer(hwnd, MC_PREVMONTHTIMER);
1263 redraw = TRUE;
1266 infoPtr->status = MC_SEL_LBUTUP;
1268 nmhdr.hwndFrom = hwnd;
1269 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
1270 nmhdr.code = NM_RELEASEDCAPTURE;
1271 TRACE("Sent notification from %x to %x\n", hwnd, GetParent(hwnd));
1273 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1274 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1276 nmsc.nmhdr.hwndFrom = hwnd;
1277 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1278 nmsc.nmhdr.code = MCN_SELECT;
1279 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1280 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1282 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1283 (WPARAM)nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1285 /* redraw if necessary */
1286 if(redraw) {
1287 hdc = GetDC(hwnd);
1288 MONTHCAL_Refresh(hwnd, hdc);
1289 ReleaseDC(hwnd, hdc);
1292 return 0;
1296 static LRESULT
1297 MONTHCAL_Timer(HWND hwnd, WPARAM wParam, LPARAM lParam)
1299 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1300 HDC hdc;
1301 BOOL redraw = FALSE;
1303 TRACE(" %d\n", wParam);
1304 if(!infoPtr) return 0;
1306 switch(wParam) {
1307 case MC_NEXTMONTHTIMER:
1308 redraw = TRUE;
1309 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1310 break;
1311 case MC_PREVMONTHTIMER:
1312 redraw = TRUE;
1313 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1314 break;
1315 default:
1316 ERR("got unknown timer\n");
1319 /* redraw only if necessary */
1320 if(redraw) {
1321 hdc = GetDC(hwnd);
1322 MONTHCAL_Refresh(hwnd, hdc);
1323 ReleaseDC(hwnd, hdc);
1326 return 0;
1330 static LRESULT
1331 MONTHCAL_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1333 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1334 MCHITTESTINFO ht;
1335 HDC hdc;
1336 int oldselday, selday, hit;
1337 RECT r;
1339 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1341 ht.pt.x = LOWORD(lParam);
1342 ht.pt.y = HIWORD(lParam);
1344 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1346 /* not on the calendar date numbers? bail out */
1347 TRACE("hit:%x\n",hit);
1348 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1350 selday = ht.st.wDay;
1351 oldselday = infoPtr->curSelDay;
1352 infoPtr->curSelDay = selday;
1353 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1355 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1356 SYSTEMTIME selArray[2];
1357 int i;
1359 MONTHCAL_GetSelRange(hwnd, 0, (LPARAM)&selArray);
1360 i = 0;
1361 if(infoPtr->firstSelDay==selArray[0].wDay) i=1;
1362 TRACE("oldRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1363 if(infoPtr->firstSelDay==selArray[1].wDay) {
1364 /* 1st time we get here: selArray[0]=selArray[1]) */
1365 /* if we're still at the first selected date, return */
1366 if(infoPtr->firstSelDay==selday) goto done;
1367 if(selday<infoPtr->firstSelDay) i = 0;
1370 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1371 if(selday>infoPtr->firstSelDay)
1372 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1373 else
1374 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1377 if(selArray[i].wDay!=selday) {
1378 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1380 selArray[i].wDay = selday;
1382 if(selArray[0].wDay>selArray[1].wDay) {
1383 DWORD tempday;
1384 tempday = selArray[1].wDay;
1385 selArray[1].wDay = selArray[0].wDay;
1386 selArray[0].wDay = tempday;
1389 MONTHCAL_SetSelRange(hwnd, 0, (LPARAM)&selArray);
1393 done:
1395 /* only redraw if the currently selected day changed */
1396 if(oldselday != infoPtr->curSelDay) {
1397 hdc = GetDC(hwnd);
1398 MONTHCAL_Refresh(hwnd, hdc);
1399 ReleaseDC(hwnd, hdc);
1402 return 0;
1406 static LRESULT
1407 MONTHCAL_Paint(HWND hwnd, WPARAM wParam)
1409 HDC hdc;
1410 PAINTSTRUCT ps;
1412 hdc = (wParam==0 ? BeginPaint(hwnd, &ps) : (HDC)wParam);
1413 MONTHCAL_Refresh(hwnd, hdc);
1414 if(!wParam) EndPaint(hwnd, &ps);
1415 return 0;
1419 static LRESULT
1420 MONTHCAL_KillFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1422 HDC hdc;
1424 TRACE("\n");
1426 hdc = GetDC(hwnd);
1427 MONTHCAL_Refresh(hwnd, hdc);
1428 ReleaseDC(hwnd, hdc);
1429 InvalidateRect(hwnd, NULL, TRUE);
1431 return 0;
1435 static LRESULT
1436 MONTHCAL_SetFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1438 HDC hdc;
1440 TRACE("\n");
1442 hdc = GetDC(hwnd);
1443 MONTHCAL_Refresh(hwnd, hdc);
1444 ReleaseDC(hwnd, hdc);
1446 return 0;
1449 /* sets the size information */
1450 static void MONTHCAL_UpdateSize(HWND hwnd)
1452 HDC hdc = GetDC(hwnd);
1453 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1454 RECT *rcClient=&infoPtr->rcClient;
1455 RECT *rcDraw=&infoPtr->rcDraw;
1456 RECT *title=&infoPtr->title;
1457 RECT *prev=&infoPtr->titlebtnprev;
1458 RECT *next=&infoPtr->titlebtnnext;
1459 RECT *titlemonth=&infoPtr->titlemonth;
1460 RECT *titleyear=&infoPtr->titleyear;
1461 RECT *days=&infoPtr->days;
1462 SIZE size;
1463 TEXTMETRICA tm;
1464 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1465 HFONT currentFont;
1467 currentFont = SelectObject(hdc, infoPtr->hFont);
1469 /* FIXME: need a way to determine current font, without setting it */
1471 if(infoPtr->hFont!=currentFont) {
1472 SelectObject(hdc, currentFont);
1473 infoPtr->hFont=currentFont;
1474 GetObjectA(currentFont, sizeof(LOGFONTA), &logFont);
1475 logFont.lfWeight=FW_BOLD;
1476 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1480 /* get the height and width of each day's text */
1481 GetTextMetricsA(hdc, &tm);
1482 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading;
1483 GetTextExtentPoint32A(hdc, "Sun", 3, &size);
1484 infoPtr->textWidth = size.cx + 2;
1486 /* retrieve the controls client rectangle info infoPtr->rcClient */
1487 GetClientRect(hwnd, rcClient);
1489 if(dwStyle & MCS_WEEKNUMBERS)
1490 infoPtr->rcClient.right+=infoPtr->textWidth;
1492 /* rcDraw is the rectangle the control is drawn in */
1493 rcDraw->left = rcClient->left;
1494 rcDraw->right = rcClient->right;
1495 rcDraw->top = rcClient->top;
1496 rcDraw->bottom = rcClient->bottom;
1498 /* use DrawEdge to adjust the size of rcClient such that we */
1499 /* do not overwrite the border when drawing the control */
1500 DrawEdge((HDC)NULL, rcDraw, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
1503 /* this is correct, the control does NOT expand vertically */
1504 /* like it does horizontally */
1505 /* make sure we don't move the controls bottom out of the client */
1506 /* area */
1507 if((rcDraw->top + 8 * infoPtr->textHeight + 5) < rcDraw->bottom) {
1508 rcDraw->bottom = rcDraw->top + 8 * infoPtr->textHeight + 5;
1511 /* calculate title area */
1512 title->top = rcClient->top + 1;
1513 title->bottom = title->top + 2 * infoPtr->textHeight + 4;
1514 title->left = rcClient->left + 1;
1515 title->right = rcClient->right - 1;
1517 /* recalculate the height and width increments and offsets */
1518 infoPtr->width_increment = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) / 7.0;
1519 infoPtr->height_increment = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) / 7.0;
1520 infoPtr->left_offset = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) - (infoPtr->width_increment * 7.0);
1521 infoPtr->top_offset = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) - (infoPtr->height_increment * 7.0);
1523 /* set the dimensions of the next and previous buttons and center */
1524 /* the month text vertically */
1525 prev->top = next->top = title->top + 6;
1526 prev->bottom = next->bottom = title->top + 2 * infoPtr->textHeight - 3;
1527 prev->right = title->left + 28;
1528 prev->left = title->left + 4;
1529 next->left = title->right - 28;
1530 next->right = title->right - 4;
1532 /* titlemonth->left and right change based upon the current month */
1533 /* and are recalculated in refresh as the current month may change */
1534 /* without the control being resized */
1535 titlemonth->bottom = titleyear->bottom = prev->top + 2 * infoPtr->textHeight - 3;
1536 titlemonth->top = titleyear->top = title->top;
1538 /* setup the dimensions of the rectangle we draw the names of the */
1539 /* days of the week in */
1540 days->left = infoPtr->left_offset;
1541 if(dwStyle & MCS_WEEKNUMBERS) days->left+=infoPtr->textWidth;
1542 days->right = days->left + infoPtr->width_increment;
1543 days->top = title->bottom + 2;
1544 days->bottom = title->bottom + infoPtr->textHeight + 2;
1546 /* restore the originally selected font */
1547 SelectObject(hdc, currentFont);
1549 ReleaseDC(hwnd, hdc);
1552 static LRESULT MONTHCAL_Size(HWND hwnd, int Width, int Height)
1554 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
1556 MONTHCAL_UpdateSize(hwnd);
1558 /* invalidate client area and erase background */
1559 InvalidateRect(hwnd, NULL, TRUE);
1561 return 0;
1564 /* FIXME: check whether dateMin/dateMax need to be adjusted. */
1565 static LRESULT
1566 MONTHCAL_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
1568 MONTHCAL_INFO *infoPtr;
1569 LOGFONTA logFont;
1571 /* allocate memory for info structure */
1572 infoPtr =(MONTHCAL_INFO*)COMCTL32_Alloc(sizeof(MONTHCAL_INFO));
1573 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1575 if(infoPtr == NULL) {
1576 ERR( "could not allocate info memory!\n");
1577 return 0;
1579 if((MONTHCAL_INFO*)GetWindowLongA(hwnd, 0) != infoPtr) {
1580 ERR( "pointer assignment error!\n");
1581 return 0;
1584 infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1585 GetObjectA(infoPtr->hFont, sizeof(LOGFONTA), &logFont);
1586 logFont.lfWeight = FW_BOLD;
1587 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1589 /* initialize info structure */
1590 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1592 GetSystemTime(&infoPtr->todaysDate);
1593 infoPtr->firstDay = 0;
1594 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1595 infoPtr->currentYear = infoPtr->todaysDate.wYear;
1596 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
1597 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
1598 infoPtr->maxSelCount = 6;
1599 infoPtr->monthRange = 3;
1600 infoPtr->monthdayState = COMCTL32_Alloc
1601 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1602 infoPtr->titlebk = GetSysColor(COLOR_ACTIVECAPTION);
1603 infoPtr->titletxt = GetSysColor(COLOR_WINDOW);
1604 infoPtr->monthbk = GetSysColor(COLOR_WINDOW);
1605 infoPtr->trailingtxt = GetSysColor(COLOR_GRAYTEXT);
1606 infoPtr->bk = GetSysColor(COLOR_WINDOW);
1607 infoPtr->txt = GetSysColor(COLOR_WINDOWTEXT);
1609 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1610 /* of the control */
1611 MONTHCAL_UpdateSize(hwnd);
1613 return 0;
1617 static LRESULT
1618 MONTHCAL_Destroy(HWND hwnd, WPARAM wParam, LPARAM lParam)
1620 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1622 /* free month calendar info data */
1623 COMCTL32_Free(infoPtr);
1625 return 0;
1629 static LRESULT WINAPI
1630 MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1632 switch(uMsg)
1634 case MCM_GETCURSEL:
1635 return MONTHCAL_GetCurSel(hwnd, wParam, lParam);
1637 case MCM_SETCURSEL:
1638 return MONTHCAL_SetCurSel(hwnd, wParam, lParam);
1640 case MCM_GETMAXSELCOUNT:
1641 return MONTHCAL_GetMaxSelCount(hwnd, wParam, lParam);
1643 case MCM_SETMAXSELCOUNT:
1644 return MONTHCAL_SetMaxSelCount(hwnd, wParam, lParam);
1646 case MCM_GETSELRANGE:
1647 return MONTHCAL_GetSelRange(hwnd, wParam, lParam);
1649 case MCM_SETSELRANGE:
1650 return MONTHCAL_SetSelRange(hwnd, wParam, lParam);
1652 case MCM_GETMONTHRANGE:
1653 return MONTHCAL_GetMonthRange(hwnd, wParam, lParam);
1655 case MCM_SETDAYSTATE:
1656 return MONTHCAL_SetDayState(hwnd, wParam, lParam);
1658 case MCM_GETMINREQRECT:
1659 return MONTHCAL_GetMinReqRect(hwnd, wParam, lParam);
1661 case MCM_GETCOLOR:
1662 return MONTHCAL_GetColor(hwnd, wParam, lParam);
1664 case MCM_SETCOLOR:
1665 return MONTHCAL_SetColor(hwnd, wParam, lParam);
1667 case MCM_GETTODAY:
1668 return MONTHCAL_GetToday(hwnd, wParam, lParam);
1670 case MCM_SETTODAY:
1671 return MONTHCAL_SetToday(hwnd, wParam, lParam);
1673 case MCM_HITTEST:
1674 return MONTHCAL_HitTest(hwnd,lParam);
1676 case MCM_GETFIRSTDAYOFWEEK:
1677 return MONTHCAL_GetFirstDayOfWeek(hwnd, wParam, lParam);
1679 case MCM_SETFIRSTDAYOFWEEK:
1680 return MONTHCAL_SetFirstDayOfWeek(hwnd, wParam, lParam);
1682 case MCM_GETRANGE:
1683 return MONTHCAL_GetRange(hwnd, wParam, lParam);
1685 case MCM_SETRANGE:
1686 return MONTHCAL_SetRange(hwnd, wParam, lParam);
1688 case MCM_GETMONTHDELTA:
1689 return MONTHCAL_GetMonthDelta(hwnd, wParam, lParam);
1691 case MCM_SETMONTHDELTA:
1692 return MONTHCAL_SetMonthDelta(hwnd, wParam, lParam);
1694 case MCM_GETMAXTODAYWIDTH:
1695 return MONTHCAL_GetMaxTodayWidth(hwnd);
1697 case WM_GETDLGCODE:
1698 return DLGC_WANTARROWS | DLGC_WANTCHARS;
1700 case WM_KILLFOCUS:
1701 return MONTHCAL_KillFocus(hwnd, wParam, lParam);
1703 case WM_LBUTTONDOWN:
1704 return MONTHCAL_LButtonDown(hwnd, wParam, lParam);
1706 case WM_MOUSEMOVE:
1707 return MONTHCAL_MouseMove(hwnd, wParam, lParam);
1709 case WM_LBUTTONUP:
1710 return MONTHCAL_LButtonUp(hwnd, wParam, lParam);
1712 case WM_PAINT:
1713 return MONTHCAL_Paint(hwnd, wParam);
1715 case WM_SETFOCUS:
1716 return MONTHCAL_SetFocus(hwnd, wParam, lParam);
1718 case WM_SIZE:
1719 return MONTHCAL_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
1721 case WM_CREATE:
1722 return MONTHCAL_Create(hwnd, wParam, lParam);
1724 case WM_TIMER:
1725 return MONTHCAL_Timer(hwnd, wParam, lParam);
1727 case WM_DESTROY:
1728 return MONTHCAL_Destroy(hwnd, wParam, lParam);
1730 default:
1731 if(uMsg >= WM_USER)
1732 ERR( "unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
1733 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
1735 return 0;
1739 void
1740 MONTHCAL_Register(void)
1742 WNDCLASSA wndClass;
1744 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
1745 wndClass.style = CS_GLOBALCLASS;
1746 wndClass.lpfnWndProc = (WNDPROC)MONTHCAL_WindowProc;
1747 wndClass.cbClsExtra = 0;
1748 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
1749 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
1750 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1751 wndClass.lpszClassName = MONTHCAL_CLASSA;
1753 RegisterClassA(&wndClass);
1757 void
1758 MONTHCAL_Unregister(void)
1760 UnregisterClassA(MONTHCAL_CLASSA, (HINSTANCE)NULL);