source: trunk/src/comctl32/monthcal.c@ 6502

Last change on this file since 6502 was 5416, checked in by sandervl, 24 years ago

Resync with Wine + previous merge fixes

File size: 58.7 KB
Line 
1/* Month calendar control
2
3 *
4 * Copyright 1998, 1999 Eric Kohl (ekohl@abo.rhein-zeitung.de)
5 * Copyright 1999 Alex Priem (alexp@sci.kun.nl)
6 * Copyright 1999 Chris Morgan <cmorgan@wpi.edu> and
7 * James Abbatiello <abbeyj@wpi.edu>
8 * Copyright 2000 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
9 *
10 * TODO:
11 * - Notifications.
12 *
13 *
14 * FIXME: handle resources better (doesn't work now); also take care
15 of internationalization.
16 * FIXME: keyboard handling.
17 */
18
19#ifdef __WIN32OS2__
20#include <odin.h>
21#include <string.h>
22#define inline
23#endif
24
25#include <math.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include "winbase.h"
31#include "windef.h"
32#include "wingdi.h"
33#include "winuser.h"
34#include "winnls.h"
35#include "commctrl.h"
36#include "comctl32.h"
37#include "debugtools.h"
38
39#ifdef __WIN32OS2__
40#include "ccbase.h"
41#endif
42
43DEFAULT_DEBUG_CHANNEL(monthcal);
44
45#define MC_SEL_LBUTUP 1 /* Left button released */
46#define MC_SEL_LBUTDOWN 2 /* Left button pressed in calendar */
47#define MC_PREVPRESSED 4 /* Prev month button pressed */
48#define MC_NEXTPRESSED 8 /* Next month button pressed */
49#define MC_NEXTMONTHDELAY 350 /* when continuously pressing `next */
50 /* month', wait 500 ms before going */
51 /* to the next month */
52#define MC_NEXTMONTHTIMER 1 /* Timer ID's */
53#define MC_PREVMONTHTIMER 2
54
55typedef struct
56{
57#ifdef __WIN32OS2__
58 COMCTL32_HEADER header;
59#endif
60 COLORREF bk;
61 COLORREF txt;
62 COLORREF titlebk;
63 COLORREF titletxt;
64 COLORREF monthbk;
65 COLORREF trailingtxt;
66 HFONT hFont;
67 HFONT hBoldFont;
68 int textHeight;
69 int textWidth;
70 int height_increment;
71 int width_increment;
72 int left_offset;
73 int top_offset;
74 int firstDayplace; /* place of the first day of the current month */
75 int delta; /* scroll rate; # of months that the */
76 /* control moves when user clicks a scroll button */
77 int visible; /* # of months visible */
78 int firstDay; /* Start month calendar with firstDay's day */
79 int monthRange;
80 MONTHDAYSTATE *monthdayState;
81 SYSTEMTIME todaysDate;
82 DWORD currentMonth;
83 DWORD currentYear;
84 int status; /* See MC_SEL flags */
85 int curSelDay; /* current selected day */
86 int firstSelDay; /* first selected day */
87 int maxSelCount;
88 SYSTEMTIME minSel;
89 SYSTEMTIME maxSel;
90 DWORD rangeValid;
91 SYSTEMTIME minDate;
92 SYSTEMTIME maxDate;
93
94 RECT rcClient; /* rect for whole client area */
95 RECT rcDraw; /* rect for drawable portion of client area */
96 RECT title; /* rect for the header above the calendar */
97 RECT titlebtnnext; /* the `next month' button in the header */
98 RECT titlebtnprev; /* the `prev month' button in the header */
99 RECT titlemonth; /* the `month name' txt in the header */
100 RECT titleyear; /* the `year number' txt in the header */
101 RECT wdays; /* week days at top */
102 RECT days; /* calendar area */
103 RECT weeknums; /* week numbers at left side */
104 RECT todayrect; /* `today: xx/xx/xx' text rect */
105 HWND hWndYearEdit; /* Window Handle of edit box to handle years */
106 HWND hWndYearUpDown;/* Window Handle of updown box to handle years */
107} MONTHCAL_INFO, *LPMONTHCAL_INFO;
108
109
110/* Offsets of days in the week to the weekday of january 1. */
111static const int DayOfWeekTable[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
112
113
114#define MONTHCAL_GetInfoPtr(hwnd) ((MONTHCAL_INFO *)GetWindowLongA(hwnd, 0))
115
116/* helper functions */
117
118/* returns the number of days in any given month, checking for leap days */
119/* january is 1, december is 12 */
120int MONTHCAL_MonthLength(int month, int year)
121{
122const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
123 /*Wrap around, this eases handleing*/
124 if(month == 0)
125 month = 12;
126 if(month == 13)
127 month = 1;
128
129 /* if we have a leap year add 1 day to February */
130 /* a leap year is a year either divisible by 400 */
131 /* or divisible by 4 and not by 100 */
132 if(month == 2) { /* February */
133 return mdays[month - 1] + ((year%400 == 0) ? 1 : ((year%100 != 0) &&
134 (year%4 == 0)) ? 1 : 0);
135 }
136 else {
137 return mdays[month - 1];
138 }
139}
140
141
142/* make sure that time is valid */
143static int MONTHCAL_ValidateTime(SYSTEMTIME time)
144{
145 if(time.wMonth > 12) return FALSE;
146 if(time.wDayOfWeek > 6) return FALSE;
147 if(time.wDay > MONTHCAL_MonthLength(time.wMonth, time.wYear))
148 return FALSE;
149 if(time.wHour > 23) return FALSE;
150 if(time.wMinute > 59) return FALSE;
151 if(time.wSecond > 59) return FALSE;
152 if(time.wMilliseconds > 999) return FALSE;
153
154 return TRUE;
155}
156
157
158void MONTHCAL_CopyTime(const SYSTEMTIME *from, SYSTEMTIME *to)
159{
160 to->wYear = from->wYear;
161 to->wMonth = from->wMonth;
162 to->wDayOfWeek = from->wDayOfWeek;
163 to->wDay = from->wDay;
164 to->wHour = from->wHour;
165 to->wMinute = from->wMinute;
166 to->wSecond = from->wSecond;
167 to->wMilliseconds = from->wMilliseconds;
168}
169
170
171/* Note:Depending on DST, this may be offset by a day.
172 Need to find out if we're on a DST place & adjust the clock accordingly.
173 Above function assumes we have a valid data.
174 Valid for year>1752; 1 <= d <= 31, 1 <= m <= 12.
175 0 = Monday.
176*/
177
178/* returns the day in the week(0 == monday, 6 == sunday) */
179/* day(1 == 1st, 2 == 2nd... etc), year is the year value */
180static int MONTHCAL_CalculateDayOfWeek(DWORD day, DWORD month, DWORD year)
181{
182 year-=(month < 3);
183
184 return((year + year/4 - year/100 + year/400 +
185 DayOfWeekTable[month-1] + day - 1 ) % 7);
186}
187
188/* From a given point, calculate the row (weekpos), column(daypos)
189 and day in the calendar. day== 0 mean the last day of tha last month
190*/
191static int MONTHCAL_CalcDayFromPos(MONTHCAL_INFO *infoPtr, int x, int y,
192 int *daypos,int *weekpos)
193{
194 int retval, firstDay;
195
196 /* if the point is outside the x bounds of the window put
197 it at the boundry */
198 if(x > infoPtr->rcClient.right) {
199 x = infoPtr->rcClient.right ;
200 }
201
202 *daypos = (x - infoPtr->days.left ) / infoPtr->width_increment;
203 *weekpos = (y - infoPtr->days.top ) / infoPtr->height_increment;
204
205 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear)+6 - infoPtr->firstDay)%7;
206 retval = *daypos + (7 * *weekpos) - firstDay;
207 return retval;
208}
209
210/* day is the day of the month, 1 == 1st day of the month */
211/* sets x and y to be the position of the day */
212/* x == day, y == week where(0,0) == firstDay, 1st week */
213static void MONTHCAL_CalcDayXY(MONTHCAL_INFO *infoPtr, int day, int month,
214 int *x, int *y)
215{
216 int firstDay, prevMonth;
217
218 firstDay = (MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear) +6 - infoPtr->firstDay)%7;
219
220 if(month==infoPtr->currentMonth) {
221 *x = (day + firstDay) % 7;
222 *y = (day + firstDay - *x) / 7;
223 return;
224 }
225 if(month < infoPtr->currentMonth) {
226 prevMonth = month - 1;
227 if(prevMonth==0)
228 prevMonth = 12;
229
230 *x = (MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) - firstDay) % 7;
231 *y = 0;
232 return;
233 }
234
235 *y = MONTHCAL_MonthLength(month, infoPtr->currentYear - 1) / 7;
236 *x = (day + firstDay + MONTHCAL_MonthLength(month,
237 infoPtr->currentYear)) % 7;
238}
239
240
241/* x: column(day), y: row(week) */
242static void MONTHCAL_CalcDayRect(MONTHCAL_INFO *infoPtr, RECT *r, int x, int y)
243{
244 r->left = infoPtr->days.left + x * infoPtr->width_increment;
245 r->right = r->left + infoPtr->width_increment;
246 r->top = infoPtr->days.top + y * infoPtr->height_increment;
247 r->bottom = r->top + infoPtr->textHeight;
248}
249
250
251/* sets the RECT struct r to the rectangle around the day and month */
252/* day is the day value of the month(1 == 1st), month is the month */
253/* value(january == 1, december == 12) */
254static inline void MONTHCAL_CalcPosFromDay(MONTHCAL_INFO *infoPtr,
255 int day, int month, RECT *r)
256{
257 int x, y;
258
259 MONTHCAL_CalcDayXY(infoPtr, day, month, &x, &y);
260 MONTHCAL_CalcDayRect(infoPtr, r, x, y);
261}
262
263
264/* day is the day in the month(1 == 1st of the month) */
265/* month is the month value(1 == january, 12 == december) */
266static void MONTHCAL_CircleDay(HDC hdc, MONTHCAL_INFO *infoPtr, int day,
267int month)
268{
269 HPEN hRedPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
270 HPEN hOldPen2 = SelectObject(hdc, hRedPen);
271 POINT points[13];
272 int x, y;
273 RECT day_rect;
274
275
276 MONTHCAL_CalcPosFromDay(infoPtr, day, month, &day_rect);
277
278 x = day_rect.left;
279 y = day_rect.top;
280
281 points[0].x = x;
282 points[0].y = y - 1;
283 points[1].x = x + 0.8 * infoPtr->width_increment;
284 points[1].y = y - 1;
285 points[2].x = x + 0.9 * infoPtr->width_increment;
286 points[2].y = y;
287 points[3].x = x + infoPtr->width_increment;
288 points[3].y = y + 0.5 * infoPtr->height_increment;
289
290 points[4].x = x + infoPtr->width_increment;
291 points[4].y = y + 0.9 * infoPtr->height_increment;
292 points[5].x = x + 0.6 * infoPtr->width_increment;
293 points[5].y = y + 0.9 * infoPtr->height_increment;
294 points[6].x = x + 0.5 * infoPtr->width_increment;
295 points[6].y = y + 0.9 * infoPtr->height_increment; /* bring the bottom up just
296 a hair to fit inside the day rectangle */
297
298 points[7].x = x + 0.2 * infoPtr->width_increment;
299 points[7].y = y + 0.8 * infoPtr->height_increment;
300 points[8].x = x + 0.1 * infoPtr->width_increment;
301 points[8].y = y + 0.8 * infoPtr->height_increment;
302 points[9].x = x;
303 points[9].y = y + 0.5 * infoPtr->height_increment;
304
305 points[10].x = x + 0.1 * infoPtr->width_increment;
306 points[10].y = y + 0.2 * infoPtr->height_increment;
307 points[11].x = x + 0.2 * infoPtr->width_increment;
308 points[11].y = y + 0.3 * infoPtr->height_increment;
309 points[12].x = x + 0.4 * infoPtr->width_increment;
310 points[12].y = y + 0.2 * infoPtr->height_increment;
311
312 PolyBezier(hdc, points, 13);
313 DeleteObject(hRedPen);
314 SelectObject(hdc, hOldPen2);
315}
316
317
318static void MONTHCAL_DrawDay(HDC hdc, MONTHCAL_INFO *infoPtr, int day, int month,
319 int x, int y, int bold)
320{
321 char buf[10];
322 RECT r;
323 static int haveBoldFont, haveSelectedDay = FALSE;
324 HBRUSH hbr;
325 HPEN hNewPen, hOldPen = 0;
326 COLORREF oldCol = 0;
327 COLORREF oldBk = 0;
328
329 sprintf(buf, "%d", day);
330
331/* No need to check styles: when selection is not valid, it is set to zero.
332 * 1<day<31, so evertyhing's OK.
333 */
334
335 MONTHCAL_CalcDayRect(infoPtr, &r, x, y);
336
337 if((day>=infoPtr->minSel.wDay) && (day<=infoPtr->maxSel.wDay)
338 && (month==infoPtr->currentMonth)) {
339 HRGN hrgn;
340 RECT r2;
341
342 TRACE("%d %d %d\n",day, infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
343 TRACE("%d %d %d %d\n", r.left, r.top, r.right, r.bottom);
344 oldCol = SetTextColor(hdc, infoPtr->monthbk);
345 oldBk = SetBkColor(hdc, infoPtr->trailingtxt);
346 hbr = GetSysColorBrush(COLOR_GRAYTEXT);
347 hrgn = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
348 FillRgn(hdc, hrgn, hbr);
349
350 /* FIXME: this may need to be changed now b/c of the other
351 drawing changes 11/3/99 CMM */
352 r2.left = r.left - 0.25 * infoPtr->textWidth;
353 r2.top = r.top;
354 r2.right = r.left + 0.5 * infoPtr->textWidth;
355 r2.bottom = r.bottom;
356 if(haveSelectedDay) FillRect(hdc, &r2, hbr);
357 haveSelectedDay = TRUE;
358 } else {
359 haveSelectedDay = FALSE;
360 }
361
362 /* need to add some code for multiple selections */
363
364 if((bold) &&(!haveBoldFont)) {
365 SelectObject(hdc, infoPtr->hBoldFont);
366 haveBoldFont = TRUE;
367 }
368 if((!bold) &&(haveBoldFont)) {
369 SelectObject(hdc, infoPtr->hFont);
370 haveBoldFont = FALSE;
371 }
372
373 if(haveSelectedDay) {
374 SetTextColor(hdc, oldCol);
375 SetBkColor(hdc, oldBk);
376 }
377
378 SetBkMode(hdc,TRANSPARENT);
379 DrawTextA(hdc, buf, -1, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
380
381 /* draw a rectangle around the currently selected days text */
382 if((day==infoPtr->curSelDay) && (month==infoPtr->currentMonth)) {
383 hNewPen = CreatePen(PS_ALTERNATE, 0, GetSysColor(COLOR_WINDOWTEXT) );
384 hbr = GetSysColorBrush(COLOR_WINDOWTEXT);
385 FrameRect(hdc, &r, hbr);
386 SelectObject(hdc, hOldPen);
387 }
388}
389
390
391/* CHECKME: For `todays date', do we need to check the locale?*/
392static void MONTHCAL_Refresh(HWND hwnd, HDC hdc, PAINTSTRUCT* ps)
393{
394 MONTHCAL_INFO *infoPtr=MONTHCAL_GetInfoPtr(hwnd);
395 RECT *rcClient=&infoPtr->rcClient;
396 RECT *rcDraw=&infoPtr->rcDraw;
397 RECT *title=&infoPtr->title;
398 RECT *prev=&infoPtr->titlebtnprev;
399 RECT *next=&infoPtr->titlebtnnext;
400 RECT *titlemonth=&infoPtr->titlemonth;
401 RECT *titleyear=&infoPtr->titleyear;
402 RECT dayrect;
403 RECT *days=&dayrect;
404 RECT rtoday;
405 int i, j, m, mask, day, firstDay, weeknum, weeknum1,prevMonth;
406 int textHeight = infoPtr->textHeight, textWidth = infoPtr->textWidth;
407 SIZE size;
408 HBRUSH hbr;
409 HFONT currentFont;
410 /* LOGFONTA logFont; */
411 char buf[20];
412 char buf1[20];
413 char buf2[32];
414 COLORREF oldTextColor, oldBkColor;
415 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
416 RECT rcTemp;
417 RECT rcDay; /* used in MONTHCAL_CalcDayRect() */
418 SYSTEMTIME localtime;
419 int startofprescal;
420
421 oldTextColor = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
422
423
424 /* fill background */
425 hbr = CreateSolidBrush (infoPtr->bk);
426 FillRect(hdc, rcClient, hbr);
427 DeleteObject(hbr);
428
429 /* draw header */
430 if(IntersectRect(&rcTemp, &(ps->rcPaint), title))
431 {
432 hbr = CreateSolidBrush(infoPtr->titlebk);
433 FillRect(hdc, title, hbr);
434 DeleteObject(hbr);
435 }
436
437 /* if the previous button is pressed draw it depressed */
438 if(IntersectRect(&rcTemp, &(ps->rcPaint), prev))
439 {
440 if((infoPtr->status & MC_PREVPRESSED))
441 DrawFrameControl(hdc, prev, DFC_SCROLL,
442 DFCS_SCROLLLEFT | DFCS_PUSHED |
443 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
444 else /* if the previous button is pressed draw it depressed */
445 DrawFrameControl(hdc, prev, DFC_SCROLL,
446 DFCS_SCROLLLEFT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
447 }
448
449 /* if next button is depressed draw it depressed */
450 if(IntersectRect(&rcTemp, &(ps->rcPaint), next))
451 {
452 if((infoPtr->status & MC_NEXTPRESSED))
453 DrawFrameControl(hdc, next, DFC_SCROLL,
454 DFCS_SCROLLRIGHT | DFCS_PUSHED |
455 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
456 else /* if the next button is pressed draw it depressed */
457 DrawFrameControl(hdc, next, DFC_SCROLL,
458 DFCS_SCROLLRIGHT |(dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0));
459 }
460
461 oldBkColor = SetBkColor(hdc, infoPtr->titlebk);
462 SetTextColor(hdc, infoPtr->titletxt);
463 currentFont = SelectObject(hdc, infoPtr->hBoldFont);
464
465 /* titlemonth->left and right are set in MONTHCAL_UpdateSize */
466 titlemonth->left = title->left;
467 titlemonth->right = title->right;
468
469 GetLocaleInfoA( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+infoPtr->currentMonth -1,
470 buf1,sizeof(buf1));
471 sprintf(buf, "%s %ld", buf1, infoPtr->currentYear);
472
473 if(IntersectRect(&rcTemp, &(ps->rcPaint), titlemonth))
474 {
475 DrawTextA(hdc, buf, strlen(buf), titlemonth,
476 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
477 }
478
479 SelectObject(hdc, infoPtr->hFont);
480
481/* titlemonth left/right contained rect for whole titletxt('June 1999')
482 * MCM_HitTestInfo wants month & year rects, so prepare these now.
483 *(no, we can't draw them separately; the whole text is centered)
484 */
485 GetTextExtentPoint32A(hdc, buf, strlen(buf), &size);
486 titlemonth->left = title->right / 2 - size.cx / 2;
487 titleyear->right = title->right / 2 + size.cx / 2;
488 GetTextExtentPoint32A(hdc, buf1, strlen(buf1), &size);
489 titlemonth->right = titlemonth->left + size.cx;
490 titleyear->left = titlemonth->right;
491
492 /* draw month area */
493 rcTemp.top=infoPtr->wdays.top;
494 rcTemp.left=infoPtr->wdays.left;
495 rcTemp.bottom=infoPtr->todayrect.bottom;
496 rcTemp.right =infoPtr->todayrect.right;
497 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcTemp))
498 {
499 hbr = CreateSolidBrush(infoPtr->monthbk);
500 FillRect(hdc, &rcTemp, hbr);
501 DeleteObject(hbr);
502 }
503
504/* draw line under day abbreviatons */
505
506 MoveToEx(hdc, infoPtr->days.left + 3, title->bottom + textHeight + 1, NULL);
507
508 LineTo(hdc, rcDraw->right - 3, title->bottom + textHeight + 1);
509
510 prevMonth = infoPtr->currentMonth - 1;
511 if(prevMonth == 0) /* if currentMonth is january(1) prevMonth is */
512 prevMonth = 12; /* december(12) of the previous year */
513
514 infoPtr->wdays.left = infoPtr->days.left = infoPtr->weeknums.right;
515/* draw day abbreviations */
516
517 SetBkColor(hdc, infoPtr->monthbk);
518 SetTextColor(hdc, infoPtr->trailingtxt);
519
520 /* copy this rect so we can change the values without changing */
521 /* the original version */
522 days->left = infoPtr->wdays.left;
523 days->right = days->left + infoPtr->width_increment;
524 days->top = infoPtr->wdays.top;
525 days->bottom = infoPtr->wdays.bottom;
526
527 i = infoPtr->firstDay;
528
529 for(j=0; j<7; j++) {
530 GetLocaleInfoA( LOCALE_USER_DEFAULT,LOCALE_SABBREVDAYNAME1 + (i +j)%7,
531 buf,sizeof(buf));
532 DrawTextA(hdc, buf, strlen(buf), days,
533 DT_CENTER | DT_VCENTER | DT_SINGLELINE );
534 days->left+=infoPtr->width_increment;
535 days->right+=infoPtr->width_increment;
536 }
537
538/* draw day numbers; first, the previous month */
539
540 firstDay = MONTHCAL_CalculateDayOfWeek(1, infoPtr->currentMonth, infoPtr->currentYear);
541
542 day = MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear) +
543 (infoPtr->firstDay + 7 - firstDay)%7 + 1;
544 if (day > MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear))
545 day -=7;
546 startofprescal = day;
547 mask = 1<<(day-1);
548
549 i = 0;
550 m = 0;
551 while(day <= MONTHCAL_MonthLength(prevMonth, infoPtr->currentYear)) {
552 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
553 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
554 {
555 MONTHCAL_DrawDay(hdc, infoPtr, day, prevMonth, i, 0,
556 infoPtr->monthdayState[m] & mask);
557 }
558
559 mask<<=1;
560 day++;
561 i++;
562 }
563
564/* draw `current' month */
565
566 day = 1; /* start at the beginning of the current month */
567
568 infoPtr->firstDayplace = i;
569 SetTextColor(hdc, infoPtr->txt);
570 m++;
571 mask = 1;
572
573 /* draw the first week of the current month */
574 while(i<7) {
575 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, 0);
576 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
577 {
578
579 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, 0,
580 infoPtr->monthdayState[m] & mask);
581
582 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
583 (day==infoPtr->todaysDate.wDay) &&
584 (infoPtr->currentYear == infoPtr->todaysDate.wYear)) {
585 if(!(dwStyle & MCS_NOTODAYCIRCLE))
586 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
587 }
588 }
589
590 mask<<=1;
591 day++;
592 i++;
593 }
594
595 j = 1; /* move to the 2nd week of the current month */
596 i = 0; /* move back to sunday */
597 while(day <= MONTHCAL_MonthLength(infoPtr->currentMonth, infoPtr->currentYear)) {
598 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
599 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
600 {
601 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth, i, j,
602 infoPtr->monthdayState[m] & mask);
603
604 if((infoPtr->currentMonth==infoPtr->todaysDate.wMonth) &&
605 (day==infoPtr->todaysDate.wDay) &&
606 (infoPtr->currentYear == infoPtr->todaysDate.wYear))
607 if(!(dwStyle & MCS_NOTODAYCIRCLE))
608 MONTHCAL_CircleDay(hdc, infoPtr, day, infoPtr->currentMonth);
609 }
610 mask<<=1;
611 day++;
612 i++;
613 if(i>6) { /* past saturday, goto the next weeks sunday */
614 i = 0;
615 j++;
616 }
617 }
618
619/* draw `next' month */
620
621 day = 1; /* start at the first day of the next month */
622 m++;
623 mask = 1;
624
625 SetTextColor(hdc, infoPtr->trailingtxt);
626 while((i<7) &&(j<6)) {
627 MONTHCAL_CalcDayRect(infoPtr, &rcDay, i, j);
628 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rcDay))
629 {
630 MONTHCAL_DrawDay(hdc, infoPtr, day, infoPtr->currentMonth + 1, i, j,
631 infoPtr->monthdayState[m] & mask);
632 }
633
634 mask<<=1;
635 day++;
636 i++;
637 if(i==7) { /* past saturday, go to next week's sunday */
638 i = 0;
639 j++;
640 }
641 }
642 SetTextColor(hdc, infoPtr->txt);
643
644
645/* draw `today' date if style allows it, and draw a circle before today's
646 * date if necessary */
647
648 if(!(dwStyle & MCS_NOTODAY)) {
649 int offset = 0;
650 if(!(dwStyle & MCS_NOTODAYCIRCLE)) {
651 /*day is the number of days from nextmonth we put on the calendar */
652 MONTHCAL_CircleDay(hdc, infoPtr,
653 day+MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear),
654 infoPtr->currentMonth);
655 offset+=textWidth;
656 }
657 if (!LoadStringA(COMCTL32_hModule,IDM_TODAY,buf1,sizeof(buf1)))
658 {
659 WARN("Can't load resource\n");
660 strcpy(buf1,"Today:");
661 }
662 MONTHCAL_CalcDayRect(infoPtr, &rtoday, 1, 6);
663 MONTHCAL_CopyTime(&infoPtr->todaysDate,&localtime);
664 GetDateFormatA(LOCALE_USER_DEFAULT,DATE_SHORTDATE,&localtime,NULL,buf2,sizeof(buf2));
665 sprintf(buf, "%s %s", buf1,buf2);
666 SelectObject(hdc, infoPtr->hBoldFont);
667
668 if(IntersectRect(&rcTemp, &(ps->rcPaint), &rtoday))
669 {
670 DrawTextA(hdc, buf, -1, &rtoday, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE);
671 DrawTextA(hdc, buf, -1, &rtoday, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
672 }
673 SelectObject(hdc, infoPtr->hFont);
674 }
675
676/*eventually draw week numbers*/
677 if(dwStyle & MCS_WEEKNUMBERS) {
678 /* display weeknumbers*/
679 int mindays;
680
681 /* Rules what week to call the first week of a new year:
682 LOCALE_IFIRSTWEEKOFYEAR == 0 (e.g US?):
683 The week containing Jan 1 is the first week of year
684 LOCALE_IFIRSTWEEKOFYEAR == 2 (e.g. Germany):
685 First week of year must contain 4 days of the new year
686 LOCALE_IFIRSTWEEKOFYEAR == 1 (what contries?)
687 The first week of the year must contain only days of the new year
688 */
689 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR,
690 buf, sizeof(buf));
691 sscanf(buf, "%d", &weeknum);
692 switch (weeknum)
693 {
694 case 1: mindays = 6;
695 break;
696 case 2: mindays = 3;
697 break;
698 case 0:
699 default:
700 mindays = 0;
701 }
702 if (infoPtr->currentMonth < 2)
703 {
704 /* calculate all those exceptions for january */
705 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
706 if ((infoPtr->firstDay +7 - weeknum1)%7 > mindays)
707 weeknum =1;
708 else
709 {
710 weeknum = 0;
711 for(i=0; i<11; i++)
712 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear-1);
713 weeknum +=startofprescal+ 7;
714 weeknum /=7;
715 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear-1);
716 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
717 weeknum++;
718 }
719 }
720 else
721 {
722 weeknum = 0;
723 for(i=0; i<prevMonth-1; i++)
724 weeknum+=MONTHCAL_MonthLength(i+1, infoPtr->currentYear);
725 weeknum +=startofprescal+ 7;
726 weeknum /=7;
727 weeknum1=MONTHCAL_CalculateDayOfWeek(1,1,infoPtr->currentYear);
728 if ((infoPtr->firstDay + 7 - weeknum1)%7 > mindays)
729 weeknum++;
730 }
731 days->left = infoPtr->weeknums.left;
732 days->right = infoPtr->weeknums.right;
733 days->top = infoPtr->weeknums.top;
734 days->bottom = days->top +infoPtr->height_increment;
735 for(i=0; i<6; i++) {
736 if((i==0)&&(weeknum>50))
737 {
738 sprintf(buf, "%d", weeknum);
739 weeknum=0;
740 }
741 else if((i==5)&&(weeknum>47))
742 {
743 sprintf(buf, "%d", 1);
744 }
745 else
746 sprintf(buf, "%d", weeknum + i);
747 DrawTextA(hdc, buf, -1, days, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
748 days->top+=infoPtr->height_increment;
749 days->bottom+=infoPtr->height_increment;
750 }
751
752 MoveToEx(hdc, infoPtr->weeknums.right, infoPtr->weeknums.top + 3 , NULL);
753 LineTo(hdc, infoPtr->weeknums.right, infoPtr->weeknums.bottom );
754
755 }
756 /* currentFont was font at entering Refresh */
757
758 SetBkColor(hdc, oldBkColor);
759 SelectObject(hdc, currentFont);
760 SetTextColor(hdc, oldTextColor);
761}
762
763
764static LRESULT
765MONTHCAL_GetMinReqRect(HWND hwnd, WPARAM wParam, LPARAM lParam)
766{
767 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
768 LPRECT lpRect = (LPRECT) lParam;
769 TRACE("%x %lx\n", wParam, lParam);
770
771 /* validate parameters */
772
773 if((infoPtr==NULL) ||(lpRect == NULL) ) return FALSE;
774
775 lpRect->left = infoPtr->rcClient.left;
776 lpRect->right = infoPtr->rcClient.right;
777 lpRect->top = infoPtr->rcClient.top;
778 lpRect->bottom = infoPtr->rcClient.bottom;
779 return TRUE;
780}
781
782
783static LRESULT
784MONTHCAL_GetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
785{
786 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
787
788 TRACE("%x %lx\n", wParam, lParam);
789
790 switch((int)wParam) {
791 case MCSC_BACKGROUND:
792 return infoPtr->bk;
793 case MCSC_TEXT:
794 return infoPtr->txt;
795 case MCSC_TITLEBK:
796 return infoPtr->titlebk;
797 case MCSC_TITLETEXT:
798 return infoPtr->titletxt;
799 case MCSC_MONTHBK:
800 return infoPtr->monthbk;
801 case MCSC_TRAILINGTEXT:
802 return infoPtr->trailingtxt;
803 }
804
805 return -1;
806}
807
808
809static LRESULT
810MONTHCAL_SetColor(HWND hwnd, WPARAM wParam, LPARAM lParam)
811{
812 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
813 int prev = -1;
814
815 TRACE("%x %lx\n", wParam, lParam);
816
817 switch((int)wParam) {
818 case MCSC_BACKGROUND:
819 prev = infoPtr->bk;
820 infoPtr->bk = (COLORREF)lParam;
821 break;
822 case MCSC_TEXT:
823 prev = infoPtr->txt;
824 infoPtr->txt = (COLORREF)lParam;
825 break;
826 case MCSC_TITLEBK:
827 prev = infoPtr->titlebk;
828 infoPtr->titlebk = (COLORREF)lParam;
829 break;
830 case MCSC_TITLETEXT:
831 prev=infoPtr->titletxt;
832 infoPtr->titletxt = (COLORREF)lParam;
833 break;
834 case MCSC_MONTHBK:
835 prev = infoPtr->monthbk;
836 infoPtr->monthbk = (COLORREF)lParam;
837 break;
838 case MCSC_TRAILINGTEXT:
839 prev = infoPtr->trailingtxt;
840 infoPtr->trailingtxt = (COLORREF)lParam;
841 break;
842 }
843
844 InvalidateRect(hwnd, NULL, FALSE);
845 return prev;
846}
847
848
849static LRESULT
850MONTHCAL_GetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
851{
852 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
853
854 TRACE("%x %lx\n", wParam, lParam);
855
856 if(infoPtr->delta)
857 return infoPtr->delta;
858 else
859 return infoPtr->visible;
860}
861
862
863static LRESULT
864MONTHCAL_SetMonthDelta(HWND hwnd, WPARAM wParam, LPARAM lParam)
865{
866 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
867 int prev = infoPtr->delta;
868
869 TRACE("%x %lx\n", wParam, lParam);
870
871 infoPtr->delta = (int)wParam;
872 return prev;
873}
874
875
876static LRESULT
877MONTHCAL_GetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
878{
879 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
880
881 return infoPtr->firstDay;
882}
883
884
885/* sets the first day of the week that will appear in the control */
886/* 0 == Monday, 6 == Sunday */
887/* FIXME: this needs to be implemented properly in MONTHCAL_Refresh() */
888/* FIXME: we need more error checking here */
889static LRESULT
890MONTHCAL_SetFirstDayOfWeek(HWND hwnd, WPARAM wParam, LPARAM lParam)
891{
892 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
893 int prev = infoPtr->firstDay;
894 char buf[40];
895 int day;
896
897 TRACE("%x %lx\n", wParam, lParam);
898
899 if((lParam >= 0) && (lParam < 7)) {
900 infoPtr->firstDay = (int)lParam;
901 }
902 else
903 {
904 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK,
905 buf, sizeof(buf));
906 TRACE("%s %d\n", buf, strlen(buf));
907 if(sscanf(buf, "%d", &day) == 1)
908 infoPtr->firstDay = day;
909 else
910 infoPtr->firstDay = 0;
911 }
912 return prev;
913}
914
915
916/* FIXME: fill this in */
917static LRESULT
918MONTHCAL_GetMonthRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
919{
920 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
921
922 TRACE("%x %lx\n", wParam, lParam);
923 FIXME("stub\n");
924
925 return infoPtr->monthRange;
926}
927
928
929static LRESULT
930MONTHCAL_GetMaxTodayWidth(HWND hwnd)
931{
932 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
933
934 return(infoPtr->todayrect.right - infoPtr->todayrect.left);
935}
936
937
938/* FIXME: are validated times taken from current date/time or simply
939 * copied?
940 * FIXME: check whether MCM_GETMONTHRANGE shows correct result after
941 * adjusting range with MCM_SETRANGE
942 */
943
944static LRESULT
945MONTHCAL_SetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
946{
947 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
948 SYSTEMTIME lprgSysTimeArray[1];
949 int prev;
950
951 TRACE("%x %lx\n", wParam, lParam);
952
953 if(wParam & GDTR_MAX) {
954 if(MONTHCAL_ValidateTime(lprgSysTimeArray[1])){
955 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxDate);
956 infoPtr->rangeValid|=GDTR_MAX;
957 } else {
958 GetSystemTime(&infoPtr->todaysDate);
959 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
960 }
961 }
962 if(wParam & GDTR_MIN) {
963 if(MONTHCAL_ValidateTime(lprgSysTimeArray[0])) {
964 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->maxDate);
965 infoPtr->rangeValid|=GDTR_MIN;
966 } else {
967 GetSystemTime(&infoPtr->todaysDate);
968 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
969 }
970 }
971
972 prev = infoPtr->monthRange;
973 infoPtr->monthRange = infoPtr->maxDate.wMonth - infoPtr->minDate.wMonth;
974
975 if(infoPtr->monthRange!=prev) {
976 COMCTL32_ReAlloc(infoPtr->monthdayState,
977 infoPtr->monthRange * sizeof(MONTHDAYSTATE));
978 }
979
980 return 1;
981}
982
983
984/* CHECKME: At the moment, we copy ranges anyway,regardless of
985 * infoPtr->rangeValid; a invalid range is simply filled with zeros in
986 * SetRange. Is this the right behavior?
987*/
988
989static LRESULT
990MONTHCAL_GetRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
991{
992 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
993 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *)lParam;
994
995 /* validate parameters */
996
997 if((infoPtr==NULL) || (lprgSysTimeArray==NULL)) return FALSE;
998
999 MONTHCAL_CopyTime(&infoPtr->maxDate, &lprgSysTimeArray[1]);
1000 MONTHCAL_CopyTime(&infoPtr->minDate, &lprgSysTimeArray[0]);
1001
1002 return infoPtr->rangeValid;
1003}
1004
1005
1006static LRESULT
1007MONTHCAL_SetDayState(HWND hwnd, WPARAM wParam, LPARAM lParam)
1008
1009{
1010 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1011 int i, iMonths = (int)wParam;
1012 MONTHDAYSTATE *dayStates = (LPMONTHDAYSTATE)lParam;
1013
1014 TRACE("%x %lx\n", wParam, lParam);
1015 if(iMonths!=infoPtr->monthRange) return 0;
1016
1017 for(i=0; i<iMonths; i++)
1018 infoPtr->monthdayState[i] = dayStates[i];
1019 return 1;
1020}
1021
1022static LRESULT
1023MONTHCAL_GetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
1024{
1025 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1026 SYSTEMTIME *lpSel = (SYSTEMTIME *) lParam;
1027
1028 TRACE("%x %lx\n", wParam, lParam);
1029 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1030 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1031
1032 MONTHCAL_CopyTime(&infoPtr->minSel, lpSel);
1033 return TRUE;
1034}
1035
1036/* FIXME: if the specified date is not visible, make it visible */
1037/* FIXME: redraw? */
1038static LRESULT
1039MONTHCAL_SetCurSel(HWND hwnd, WPARAM wParam, LPARAM lParam)
1040{
1041 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1042 SYSTEMTIME *lpSel = (SYSTEMTIME *)lParam;
1043
1044 TRACE("%x %lx\n", wParam, lParam);
1045 if((infoPtr==NULL) ||(lpSel==NULL)) return FALSE;
1046 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) return FALSE;
1047
1048 TRACE("%d %d\n", lpSel->wMonth, lpSel->wDay);
1049
1050 MONTHCAL_CopyTime(lpSel, &infoPtr->minSel);
1051 MONTHCAL_CopyTime(lpSel, &infoPtr->maxSel);
1052
1053 InvalidateRect(hwnd, NULL, FALSE);
1054
1055 return TRUE;
1056}
1057
1058
1059static LRESULT
1060MONTHCAL_GetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
1061{
1062 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1063
1064 TRACE("%x %lx\n", wParam, lParam);
1065 return infoPtr->maxSelCount;
1066}
1067
1068
1069static LRESULT
1070MONTHCAL_SetMaxSelCount(HWND hwnd, WPARAM wParam, LPARAM lParam)
1071{
1072 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1073
1074 TRACE("%x %lx\n", wParam, lParam);
1075 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1076 infoPtr->maxSelCount = wParam;
1077 }
1078
1079 return TRUE;
1080}
1081
1082
1083static LRESULT
1084MONTHCAL_GetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
1085{
1086 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1087 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1088
1089 TRACE("%x %lx\n", wParam, lParam);
1090
1091 /* validate parameters */
1092
1093 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1094
1095 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT)
1096 {
1097 MONTHCAL_CopyTime(&infoPtr->maxSel, &lprgSysTimeArray[1]);
1098 MONTHCAL_CopyTime(&infoPtr->minSel, &lprgSysTimeArray[0]);
1099 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1100 return TRUE;
1101 }
1102
1103 return FALSE;
1104}
1105
1106
1107static LRESULT
1108MONTHCAL_SetSelRange(HWND hwnd, WPARAM wParam, LPARAM lParam)
1109{
1110 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1111 SYSTEMTIME *lprgSysTimeArray = (SYSTEMTIME *) lParam;
1112
1113 TRACE("%x %lx\n", wParam, lParam);
1114
1115 /* validate parameters */
1116
1117 if((infoPtr==NULL) ||(lprgSysTimeArray==NULL)) return FALSE;
1118
1119 if(GetWindowLongA( hwnd, GWL_STYLE) & MCS_MULTISELECT)
1120 {
1121 MONTHCAL_CopyTime(&lprgSysTimeArray[1], &infoPtr->maxSel);
1122 MONTHCAL_CopyTime(&lprgSysTimeArray[0], &infoPtr->minSel);
1123 TRACE("[min,max]=[%d %d]\n", infoPtr->minSel.wDay, infoPtr->maxSel.wDay);
1124 return TRUE;
1125 }
1126
1127 return FALSE;
1128}
1129
1130
1131static LRESULT
1132MONTHCAL_GetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
1133{
1134 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1135 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1136
1137 TRACE("%x %lx\n", wParam, lParam);
1138
1139 /* validate parameters */
1140
1141 if((infoPtr==NULL) || (lpToday==NULL)) return FALSE;
1142 MONTHCAL_CopyTime(&infoPtr->todaysDate, lpToday);
1143 return TRUE;
1144}
1145
1146
1147static LRESULT
1148MONTHCAL_SetToday(HWND hwnd, WPARAM wParam, LPARAM lParam)
1149{
1150 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1151 SYSTEMTIME *lpToday = (SYSTEMTIME *) lParam;
1152
1153 TRACE("%x %lx\n", wParam, lParam);
1154
1155 /* validate parameters */
1156
1157 if((infoPtr==NULL) ||(lpToday==NULL)) return FALSE;
1158 MONTHCAL_CopyTime(lpToday, &infoPtr->todaysDate);
1159 InvalidateRect(hwnd, NULL, FALSE);
1160 return TRUE;
1161}
1162
1163
1164static LRESULT
1165MONTHCAL_HitTest(HWND hwnd, LPARAM lParam)
1166{
1167 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1168 PMCHITTESTINFO lpht = (PMCHITTESTINFO)lParam;
1169 UINT x,y;
1170 DWORD retval;
1171 int day,wday,wnum;
1172
1173
1174 x = lpht->pt.x;
1175 y = lpht->pt.y;
1176 retval = MCHT_NOWHERE;
1177
1178
1179 /* Comment in for debugging...
1180 TRACE("%d %d wd[%d %d %d %d] d[%d %d %d %d] t[%d %d %d %d] wn[%d %d %d %d]\n", x, y,
1181 infoPtr->wdays.left, infoPtr->wdays.right,
1182 infoPtr->wdays.top, infoPtr->wdays.bottom,
1183 infoPtr->days.left, infoPtr->days.right,
1184 infoPtr->days.top, infoPtr->days.bottom,
1185 infoPtr->todayrect.left, infoPtr->todayrect.right,
1186 infoPtr->todayrect.top, infoPtr->todayrect.bottom,
1187 infoPtr->weeknums.left, infoPtr->weeknums.right,
1188 infoPtr->weeknums.top, infoPtr->weeknums.bottom);
1189 */
1190
1191 /* are we in the header? */
1192
1193 if(PtInRect(&infoPtr->title, lpht->pt)) {
1194 if(PtInRect(&infoPtr->titlebtnprev, lpht->pt)) {
1195 retval = MCHT_TITLEBTNPREV;
1196 goto done;
1197 }
1198 if(PtInRect(&infoPtr->titlebtnnext, lpht->pt)) {
1199 retval = MCHT_TITLEBTNNEXT;
1200 goto done;
1201 }
1202 if(PtInRect(&infoPtr->titlemonth, lpht->pt)) {
1203 retval = MCHT_TITLEMONTH;
1204 goto done;
1205 }
1206 if(PtInRect(&infoPtr->titleyear, lpht->pt)) {
1207 retval = MCHT_TITLEYEAR;
1208 goto done;
1209 }
1210
1211 retval = MCHT_TITLE;
1212 goto done;
1213 }
1214
1215 day = MONTHCAL_CalcDayFromPos(infoPtr,x,y,&wday,&wnum);
1216 if(PtInRect(&infoPtr->wdays, lpht->pt)) {
1217 retval = MCHT_CALENDARDAY;
1218 lpht->st.wYear = infoPtr->currentYear;
1219 lpht->st.wMonth = (day < 1)? infoPtr->currentMonth -1 : infoPtr->currentMonth;
1220 lpht->st.wDay = (day < 1)?
1221 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day : day;
1222 goto done;
1223 }
1224 if(PtInRect(&infoPtr->weeknums, lpht->pt)) {
1225 retval = MCHT_CALENDARWEEKNUM;
1226 lpht->st.wYear = infoPtr->currentYear;
1227 lpht->st.wMonth = (day < 1) ? infoPtr->currentMonth -1 :
1228 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1229 infoPtr->currentMonth +1 :infoPtr->currentMonth;
1230 lpht->st.wDay = (day < 1 ) ?
1231 MONTHCAL_MonthLength(infoPtr->currentMonth-1,infoPtr->currentYear) -day :
1232 (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear)) ?
1233 day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) : day;
1234 goto done;
1235 }
1236 if(PtInRect(&infoPtr->days, lpht->pt))
1237 {
1238 lpht->st.wYear = infoPtr->currentYear;
1239 if ( day < 1)
1240 {
1241 retval = MCHT_CALENDARDATEPREV;
1242 lpht->st.wMonth = infoPtr->currentMonth - 1;
1243 if (lpht->st.wMonth <1)
1244 {
1245 lpht->st.wMonth = 12;
1246 lpht->st.wYear--;
1247 }
1248 lpht->st.wDay = MONTHCAL_MonthLength(lpht->st.wMonth,lpht->st.wYear) -day;
1249 }
1250 else if (day > MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear))
1251 {
1252 retval = MCHT_CALENDARDATENEXT;
1253 lpht->st.wMonth = infoPtr->currentMonth + 1;
1254 if (lpht->st.wMonth <12)
1255 {
1256 lpht->st.wMonth = 1;
1257 lpht->st.wYear++;
1258 }
1259 lpht->st.wDay = day - MONTHCAL_MonthLength(infoPtr->currentMonth,infoPtr->currentYear) ;
1260 }
1261 else {
1262 retval = MCHT_CALENDARDATE;
1263 lpht->st.wMonth = infoPtr->currentMonth;
1264 lpht->st.wDay = day;
1265 }
1266 goto done;
1267 }
1268 if(PtInRect(&infoPtr->todayrect, lpht->pt)) {
1269 retval = MCHT_TODAYLINK;
1270 goto done;
1271 }
1272
1273
1274 /* Hit nothing special? What's left must be background :-) */
1275
1276 retval = MCHT_CALENDARBK;
1277 done:
1278 lpht->uHit = retval;
1279 return retval;
1280}
1281
1282
1283static void MONTHCAL_GoToNextMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1284{
1285 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1286
1287 TRACE("MONTHCAL_GoToNextMonth\n");
1288
1289 infoPtr->currentMonth++;
1290 if(infoPtr->currentMonth > 12) {
1291 infoPtr->currentYear++;
1292 infoPtr->currentMonth = 1;
1293 }
1294
1295 if(dwStyle & MCS_DAYSTATE) {
1296 NMDAYSTATE nmds;
1297 int i;
1298
1299 nmds.nmhdr.hwndFrom = hwnd;
1300 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1301 nmds.nmhdr.code = MCN_GETDAYSTATE;
1302 nmds.cDayState = infoPtr->monthRange;
1303 nmds.prgDayState = COMCTL32_Alloc(infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1304
1305 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1306 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1307 for(i=0; i<infoPtr->monthRange; i++)
1308 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1309 }
1310}
1311
1312
1313static void MONTHCAL_GoToPrevMonth(HWND hwnd, MONTHCAL_INFO *infoPtr)
1314{
1315 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1316
1317 TRACE("MONTHCAL_GoToPrevMonth\n");
1318
1319 infoPtr->currentMonth--;
1320 if(infoPtr->currentMonth < 1) {
1321 infoPtr->currentYear--;
1322 infoPtr->currentMonth = 12;
1323 }
1324
1325 if(dwStyle & MCS_DAYSTATE) {
1326 NMDAYSTATE nmds;
1327 int i;
1328
1329 nmds.nmhdr.hwndFrom = hwnd;
1330 nmds.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1331 nmds.nmhdr.code = MCN_GETDAYSTATE;
1332 nmds.cDayState = infoPtr->monthRange;
1333 nmds.prgDayState = COMCTL32_Alloc
1334 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1335
1336 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1337 (WPARAM)nmds.nmhdr.idFrom, (LPARAM)&nmds);
1338 for(i=0; i<infoPtr->monthRange; i++)
1339 infoPtr->monthdayState[i] = nmds.prgDayState[i];
1340 }
1341}
1342
1343static LRESULT
1344MONTHCAL_RButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1345{
1346 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1347 HMENU hMenu;
1348 POINT menupoint;
1349 char buf[32];
1350
1351 hMenu = CreatePopupMenu();
1352 if (!LoadStringA(COMCTL32_hModule,IDM_GOTODAY,buf,sizeof(buf)))
1353 {
1354 WARN("Can't load resource\n");
1355 strcpy(buf,"Go to Today:");
1356 }
1357 AppendMenuA(hMenu, MF_STRING|MF_ENABLED,1, buf);
1358 menupoint.x=(INT)LOWORD(lParam);
1359 menupoint.y=(INT)HIWORD(lParam);
1360 ClientToScreen(hwnd, &menupoint);
1361 if( TrackPopupMenu(hMenu,TPM_RIGHTBUTTON| TPM_NONOTIFY|TPM_RETURNCMD,
1362 menupoint.x,menupoint.y,0,hwnd,NULL))
1363 {
1364 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1365 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1366 InvalidateRect(hwnd, NULL, FALSE);
1367 }
1368 return 0;
1369}
1370
1371static LRESULT
1372MONTHCAL_LButtonDown(HWND hwnd, WPARAM wParam, LPARAM lParam)
1373{
1374 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1375 MCHITTESTINFO ht;
1376 DWORD hit;
1377 HMENU hMenu;
1378 RECT rcDay; /* used in determining area to invalidate */
1379 char buf[32];
1380 int i;
1381 POINT menupoint;
1382 TRACE("%x %lx\n", wParam, lParam);
1383
1384 if (infoPtr->hWndYearUpDown)
1385 {
1386 infoPtr->currentYear=SendMessageA( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)0);
1387 if(!DestroyWindow(infoPtr->hWndYearUpDown))
1388 {
1389 FIXME("Can't destroy Updown Control\n");
1390 }
1391 else
1392 infoPtr->hWndYearUpDown=0;
1393 if(!DestroyWindow(infoPtr->hWndYearEdit))
1394 {
1395 FIXME("Can't destroy Updown Control\n");
1396 }
1397 else
1398 infoPtr->hWndYearEdit=0;
1399 InvalidateRect(hwnd, NULL, FALSE);
1400 }
1401
1402 ht.pt.x = (INT)LOWORD(lParam);
1403 ht.pt.y = (INT)HIWORD(lParam);
1404 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1405
1406 /* FIXME: these flags should be checked by */
1407 /*((hit & MCHT_XXX) == MCHT_XXX) b/c some of the flags are */
1408 /* multi-bit */
1409 if(hit ==MCHT_TITLEBTNNEXT) {
1410 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1411 infoPtr->status = MC_NEXTPRESSED;
1412 SetTimer(hwnd, MC_NEXTMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1413 InvalidateRect(hwnd, NULL, FALSE);
1414 return TRUE;
1415 }
1416 if(hit == MCHT_TITLEBTNPREV){
1417 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1418 infoPtr->status = MC_PREVPRESSED;
1419 SetTimer(hwnd, MC_PREVMONTHTIMER, MC_NEXTMONTHDELAY, 0);
1420 InvalidateRect(hwnd, NULL, FALSE);
1421 return TRUE;
1422 }
1423
1424 if(hit == MCHT_TITLEMONTH) {
1425 hMenu = CreatePopupMenu();
1426
1427 for (i=0; i<12;i++)
1428 {
1429 GetLocaleInfoA( LOCALE_USER_DEFAULT,LOCALE_SMONTHNAME1+i,
1430 buf,sizeof(buf));
1431 AppendMenuA(hMenu, MF_STRING|MF_ENABLED,i+1, buf);
1432 }
1433 menupoint.x=infoPtr->titlemonth.right;
1434 menupoint.y=infoPtr->titlemonth.bottom;
1435 ClientToScreen(hwnd, &menupoint);
1436 i= TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD,
1437 menupoint.x,menupoint.y,0,hwnd,NULL);
1438 if ((i>0) && (i<13))
1439 {
1440 infoPtr->currentMonth=i;
1441 InvalidateRect(hwnd, NULL, FALSE);
1442 }
1443 }
1444 if(hit == MCHT_TITLEYEAR) {
1445 infoPtr->hWndYearEdit=CreateWindowExA(0,
1446 "EDIT",
1447 0,
1448 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT,
1449 infoPtr->titleyear.left+3,infoPtr->titlebtnnext.top,
1450 infoPtr->titleyear.right-infoPtr->titleyear.left,
1451 infoPtr->textHeight,
1452 hwnd,
1453 (HMENU)NULL,
1454 (HINSTANCE)NULL,
1455 NULL);
1456 infoPtr->hWndYearUpDown=CreateWindowExA(0,
1457 UPDOWN_CLASSA,
1458 0,
1459 WS_VISIBLE | WS_CHILD |UDS_SETBUDDYINT|UDS_NOTHOUSANDS|UDS_ARROWKEYS,
1460 infoPtr->titleyear.right+6,infoPtr->titlebtnnext.top,
1461 20,
1462 infoPtr->textHeight,
1463 hwnd,
1464 (HMENU)NULL,
1465 (HINSTANCE)NULL,
1466 NULL);
1467 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETRANGE, (WPARAM) 0, MAKELONG (9999, 1753));
1468 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETBUDDY, (WPARAM) infoPtr->hWndYearEdit, (LPARAM)0 );
1469 SendMessageA( infoPtr->hWndYearUpDown, UDM_SETPOS, (WPARAM) 0,(LPARAM)infoPtr->currentYear );
1470 return TRUE;
1471
1472 }
1473 if(hit == MCHT_TODAYLINK) {
1474 infoPtr->currentMonth=infoPtr->todaysDate.wMonth;
1475 infoPtr->currentYear=infoPtr->todaysDate.wYear;
1476 InvalidateRect(hwnd, NULL, FALSE);
1477 return TRUE;
1478 }
1479 if(hit && MCHT_CALENDARDATE) {
1480 SYSTEMTIME selArray[2];
1481 NMSELCHANGE nmsc;
1482
1483 TRACE("MCHT_CALENDARDATE\n");
1484 nmsc.nmhdr.hwndFrom = hwnd;
1485 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1486 nmsc.nmhdr.code = MCN_SELCHANGE;
1487 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1488 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1489
1490 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1491 (WPARAM)nmsc.nmhdr.idFrom,(LPARAM)&nmsc);
1492
1493 MONTHCAL_CopyTime(&ht.st, &selArray[0]);
1494 MONTHCAL_CopyTime(&ht.st, &selArray[1]);
1495 MONTHCAL_SetSelRange(hwnd,0,(LPARAM) &selArray);
1496
1497 /* redraw both old and new days if the selected day changed */
1498 if(infoPtr->curSelDay != ht.st.wDay) {
1499 MONTHCAL_CalcPosFromDay(infoPtr, ht.st.wDay, ht.st.wMonth, &rcDay);
1500 InvalidateRect(hwnd, &rcDay, TRUE);
1501
1502 MONTHCAL_CalcPosFromDay(infoPtr, infoPtr->curSelDay, infoPtr->currentMonth, &rcDay);
1503 InvalidateRect(hwnd, &rcDay, TRUE);
1504 }
1505
1506 infoPtr->firstSelDay = ht.st.wDay;
1507 infoPtr->curSelDay = ht.st.wDay;
1508 infoPtr->status = MC_SEL_LBUTDOWN;
1509 return TRUE;
1510 }
1511
1512 return 0;
1513}
1514
1515
1516static LRESULT
1517MONTHCAL_LButtonUp(HWND hwnd, WPARAM wParam, LPARAM lParam)
1518{
1519 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1520 NMSELCHANGE nmsc;
1521 NMHDR nmhdr;
1522 BOOL redraw = FALSE;
1523 MCHITTESTINFO ht;
1524 DWORD hit;
1525
1526 TRACE("\n");
1527
1528 if(infoPtr->status & MC_NEXTPRESSED) {
1529 KillTimer(hwnd, MC_NEXTMONTHTIMER);
1530 redraw = TRUE;
1531 }
1532 if(infoPtr->status & MC_PREVPRESSED) {
1533 KillTimer(hwnd, MC_PREVMONTHTIMER);
1534 redraw = TRUE;
1535 }
1536
1537 ht.pt.x = (INT)LOWORD(lParam);
1538 ht.pt.y = (INT)HIWORD(lParam);
1539 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1540
1541 infoPtr->status = MC_SEL_LBUTUP;
1542
1543 if(hit ==MCHT_CALENDARDATENEXT) {
1544 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1545 InvalidateRect(hwnd, NULL, FALSE);
1546 return TRUE;
1547 }
1548 if(hit == MCHT_CALENDARDATEPREV){
1549 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1550 InvalidateRect(hwnd, NULL, FALSE);
1551 return TRUE;
1552 }
1553 nmhdr.hwndFrom = hwnd;
1554 nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
1555 nmhdr.code = NM_RELEASEDCAPTURE;
1556 TRACE("Sent notification from %x to %x\n", hwnd, GetParent(hwnd));
1557
1558 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1559 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1560
1561 nmsc.nmhdr.hwndFrom = hwnd;
1562 nmsc.nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
1563 nmsc.nmhdr.code = MCN_SELECT;
1564 MONTHCAL_CopyTime(&nmsc.stSelStart, &infoPtr->minSel);
1565 MONTHCAL_CopyTime(&nmsc.stSelEnd, &infoPtr->maxSel);
1566
1567 SendMessageA(GetParent(hwnd), WM_NOTIFY,
1568 (WPARAM)nmsc.nmhdr.idFrom, (LPARAM)&nmsc);
1569
1570 /* redraw if necessary */
1571 if(redraw)
1572 InvalidateRect(hwnd, NULL, FALSE);
1573
1574 return 0;
1575}
1576
1577
1578static LRESULT
1579MONTHCAL_Timer(HWND hwnd, WPARAM wParam, LPARAM lParam)
1580{
1581 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1582 BOOL redraw = FALSE;
1583
1584 TRACE(" %d\n", wParam);
1585 if(!infoPtr) return 0;
1586
1587 switch(wParam) {
1588 case MC_NEXTMONTHTIMER:
1589 redraw = TRUE;
1590 MONTHCAL_GoToNextMonth(hwnd, infoPtr);
1591 break;
1592 case MC_PREVMONTHTIMER:
1593 redraw = TRUE;
1594 MONTHCAL_GoToPrevMonth(hwnd, infoPtr);
1595 break;
1596 default:
1597 ERR("got unknown timer\n");
1598 }
1599
1600 /* redraw only if necessary */
1601 if(redraw)
1602 InvalidateRect(hwnd, NULL, FALSE);
1603
1604 return 0;
1605}
1606
1607
1608static LRESULT
1609MONTHCAL_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1610{
1611 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1612 MCHITTESTINFO ht;
1613 int oldselday, selday, hit;
1614 RECT r;
1615
1616 if(!(infoPtr->status & MC_SEL_LBUTDOWN)) return 0;
1617
1618 ht.pt.x = LOWORD(lParam);
1619 ht.pt.y = HIWORD(lParam);
1620
1621 hit = MONTHCAL_HitTest(hwnd, (LPARAM)&ht);
1622
1623 /* not on the calendar date numbers? bail out */
1624 TRACE("hit:%x\n",hit);
1625 if((hit & MCHT_CALENDARDATE) != MCHT_CALENDARDATE) return 0;
1626
1627 selday = ht.st.wDay;
1628 oldselday = infoPtr->curSelDay;
1629 infoPtr->curSelDay = selday;
1630 MONTHCAL_CalcPosFromDay(infoPtr, selday, ht.st. wMonth, &r);
1631
1632 if(GetWindowLongA(hwnd, GWL_STYLE) & MCS_MULTISELECT) {
1633 SYSTEMTIME selArray[2];
1634 int i;
1635
1636 MONTHCAL_GetSelRange(hwnd, 0, (LPARAM)&selArray);
1637 i = 0;
1638 if(infoPtr->firstSelDay==selArray[0].wDay) i=1;
1639 TRACE("oldRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1640 if(infoPtr->firstSelDay==selArray[1].wDay) {
1641 /* 1st time we get here: selArray[0]=selArray[1]) */
1642 /* if we're still at the first selected date, return */
1643 if(infoPtr->firstSelDay==selday) goto done;
1644 if(selday<infoPtr->firstSelDay) i = 0;
1645 }
1646
1647 if(abs(infoPtr->firstSelDay - selday) >= infoPtr->maxSelCount) {
1648 if(selday>infoPtr->firstSelDay)
1649 selday = infoPtr->firstSelDay + infoPtr->maxSelCount;
1650 else
1651 selday = infoPtr->firstSelDay - infoPtr->maxSelCount;
1652 }
1653
1654 if(selArray[i].wDay!=selday) {
1655 TRACE("newRange:%d %d %d %d\n", infoPtr->firstSelDay, selArray[0].wDay, selArray[1].wDay, i);
1656
1657 selArray[i].wDay = selday;
1658
1659 if(selArray[0].wDay>selArray[1].wDay) {
1660 DWORD tempday;
1661 tempday = selArray[1].wDay;
1662 selArray[1].wDay = selArray[0].wDay;
1663 selArray[0].wDay = tempday;
1664 }
1665
1666 MONTHCAL_SetSelRange(hwnd, 0, (LPARAM)&selArray);
1667 }
1668 }
1669
1670done:
1671
1672 /* only redraw if the currently selected day changed */
1673 /* FIXME: this should specify a rectangle containing only the days that changed */
1674 /* using InvalidateRect */
1675 if(oldselday != infoPtr->curSelDay)
1676 InvalidateRect(hwnd, NULL, FALSE);
1677
1678 return 0;
1679}
1680
1681
1682static LRESULT
1683MONTHCAL_Paint(HWND hwnd, WPARAM wParam)
1684{
1685 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1686 HDC hdc;
1687 PAINTSTRUCT ps;
1688
1689 /* fill ps.rcPaint with a default rect */
1690 memcpy(&(ps.rcPaint), &(infoPtr->rcClient), sizeof(infoPtr->rcClient));
1691
1692 hdc = (wParam==0 ? BeginPaint(hwnd, &ps) : (HDC)wParam);
1693 MONTHCAL_Refresh(hwnd, hdc, &ps);
1694 if(!wParam) EndPaint(hwnd, &ps);
1695 return 0;
1696}
1697
1698
1699static LRESULT
1700MONTHCAL_KillFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1701{
1702 TRACE("\n");
1703
1704 InvalidateRect(hwnd, NULL, TRUE);
1705
1706 return 0;
1707}
1708
1709
1710static LRESULT
1711MONTHCAL_SetFocus(HWND hwnd, WPARAM wParam, LPARAM lParam)
1712{
1713 TRACE("\n");
1714
1715 InvalidateRect(hwnd, NULL, FALSE);
1716
1717 return 0;
1718}
1719
1720/* sets the size information */
1721static void MONTHCAL_UpdateSize(HWND hwnd)
1722{
1723 HDC hdc = GetDC(hwnd);
1724 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1725 RECT *rcClient=&infoPtr->rcClient;
1726 RECT *rcDraw=&infoPtr->rcDraw;
1727 RECT *title=&infoPtr->title;
1728 RECT *prev=&infoPtr->titlebtnprev;
1729 RECT *next=&infoPtr->titlebtnnext;
1730 RECT *titlemonth=&infoPtr->titlemonth;
1731 RECT *titleyear=&infoPtr->titleyear;
1732 RECT *wdays=&infoPtr->wdays;
1733 RECT *weeknumrect=&infoPtr->weeknums;
1734 RECT *days=&infoPtr->days;
1735 RECT *todayrect=&infoPtr->todayrect;
1736 SIZE size;
1737 TEXTMETRICA tm;
1738 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
1739 HFONT currentFont;
1740 double xdiv;
1741
1742 currentFont = SelectObject(hdc, infoPtr->hFont);
1743
1744 /* FIXME: need a way to determine current font, without setting it */
1745 /*
1746 if(infoPtr->hFont!=currentFont) {
1747 SelectObject(hdc, currentFont);
1748 infoPtr->hFont=currentFont;
1749 GetObjectA(currentFont, sizeof(LOGFONTA), &logFont);
1750 logFont.lfWeight=FW_BOLD;
1751 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1752 }
1753 */
1754
1755 /* get the height and width of each day's text */
1756 GetTextMetricsA(hdc, &tm);
1757 infoPtr->textHeight = tm.tmHeight + tm.tmExternalLeading;
1758 GetTextExtentPoint32A(hdc, "Sun", 3, &size);
1759 infoPtr->textWidth = size.cx + 2;
1760
1761 /* retrieve the controls client rectangle info infoPtr->rcClient */
1762 GetClientRect(hwnd, rcClient);
1763
1764 /* rcDraw is the rectangle the control is drawn in */
1765 rcDraw->left = rcClient->left;
1766 rcDraw->right = rcClient->right;
1767 rcDraw->top = rcClient->top;
1768 rcDraw->bottom = rcClient->bottom;
1769
1770 /* recalculate the height and width increments and offsets */
1771 /* FIXME: We use up all available width. This will inhibit having multiple
1772 calendars in a row, like win doesn
1773 */
1774 if(dwStyle & MCS_WEEKNUMBERS)
1775 xdiv=8.0;
1776 else
1777 xdiv=7.0;
1778 infoPtr->width_increment = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) / xdiv;
1779 infoPtr->height_increment = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) / 10.0;
1780 infoPtr->left_offset = (infoPtr->rcDraw.right - infoPtr->rcDraw.left) - (infoPtr->width_increment * xdiv);
1781 infoPtr->top_offset = (infoPtr->rcDraw.bottom - infoPtr->rcDraw.top) - (infoPtr->height_increment * 10.0);
1782
1783 rcDraw->bottom = rcDraw->top + 10 * infoPtr->height_increment;
1784 /* this is correct, the control does NOT expand vertically */
1785 /* like it does horizontally */
1786 /* make sure we don't move the controls bottom out of the client */
1787 /* area */
1788 /* title line has about 3 text heights, abrev days line, 6 weeksline and today circle line*/
1789 /*if((rcDraw->top + 9 * infoPtr->textHeight + 5) < rcDraw->bottom) {
1790 rcDraw->bottom = rcDraw->top + 9 * infoPtr->textHeight + 5;
1791 }*/
1792
1793 /* calculate title area */
1794 title->top = rcClient->top;
1795 title->bottom = title->top + 2 * infoPtr->height_increment;
1796 title->left = rcClient->left;
1797 title->right = rcClient->right;
1798
1799 /* set the dimensions of the next and previous buttons and center */
1800 /* the month text vertically */
1801 prev->top = next->top = title->top + 6;
1802 prev->bottom = next->bottom = title->bottom - 6;
1803 prev->left = title->left + 6;
1804 prev->right = prev->left + (title->bottom - title->top) ;
1805 next->right = title->right - 6;
1806 next->left = next->right - (title->bottom - title->top);
1807
1808 /* titlemonth->left and right change based upon the current month */
1809 /* and are recalculated in refresh as the current month may change */
1810 /* without the control being resized */
1811 titlemonth->top = titleyear->top = title->top + (infoPtr->height_increment)/2;
1812 titlemonth->bottom = titleyear->bottom = title->bottom - (infoPtr->height_increment)/2;
1813
1814 /* setup the dimensions of the rectangle we draw the names of the */
1815 /* days of the week in */
1816 weeknumrect->left =infoPtr->left_offset;
1817 if(dwStyle & MCS_WEEKNUMBERS)
1818 weeknumrect->right=prev->right;
1819 else
1820 weeknumrect->right=weeknumrect->left;
1821 wdays->left = days->left = weeknumrect->right;
1822 wdays->right = days->right = wdays->left + 7 * infoPtr->width_increment;
1823 wdays->top = title->bottom ;
1824 wdays->bottom = wdays->top + infoPtr->height_increment;
1825
1826 days->top = weeknumrect->top = wdays->bottom ;
1827 days->bottom = weeknumrect->bottom = days->top + 6 * infoPtr->height_increment;
1828
1829 todayrect->left = rcClient->left;
1830 todayrect->right = rcClient->right;
1831 todayrect->top = days->bottom;
1832 todayrect->bottom = days->bottom + infoPtr->height_increment;
1833
1834 /* uncomment for excessive debugging
1835 TRACE("dx=%d dy=%d rcC[%d %d %d %d] t[%d %d %d %d] wd[%d %d %d %d] w[%d %d %d %d] t[%d %d %d %d]\n",
1836 infoPtr->width_increment,infoPtr->height_increment,
1837 rcClient->left, rcClient->right, rcClient->top, rcClient->bottom,
1838 title->left, title->right, title->top, title->bottom,
1839 wdays->left, wdays->right, wdays->top, wdays->bottom,
1840 days->left, days->right, days->top, days->bottom,
1841 todayrect->left,todayrect->right,todayrect->top,todayrect->bottom);
1842 */
1843
1844 /* restore the originally selected font */
1845 SelectObject(hdc, currentFont);
1846
1847 ReleaseDC(hwnd, hdc);
1848}
1849
1850static LRESULT MONTHCAL_Size(HWND hwnd, int Width, int Height)
1851{
1852 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
1853
1854 MONTHCAL_UpdateSize(hwnd);
1855
1856 /* invalidate client area and erase background */
1857 InvalidateRect(hwnd, NULL, TRUE);
1858
1859 return 0;
1860}
1861
1862/* FIXME: check whether dateMin/dateMax need to be adjusted. */
1863static LRESULT
1864MONTHCAL_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
1865{
1866 MONTHCAL_INFO *infoPtr;
1867 LOGFONTA logFont;
1868
1869 /* allocate memory for info structure */
1870#ifdef __WIN32OS2__
1871 infoPtr =(MONTHCAL_INFO*)initControl(hwnd,sizeof(MONTHCAL_INFO));
1872#else
1873 infoPtr =(MONTHCAL_INFO*)COMCTL32_Alloc(sizeof(MONTHCAL_INFO));
1874#endif
1875 SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
1876
1877 if(infoPtr == NULL) {
1878 ERR( "could not allocate info memory!\n");
1879 return 0;
1880 }
1881 if((MONTHCAL_INFO*)GetWindowLongA(hwnd, 0) != infoPtr) {
1882 ERR( "pointer assignment error!\n");
1883 return 0;
1884 }
1885
1886 infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
1887 GetObjectA(infoPtr->hFont, sizeof(LOGFONTA), &logFont);
1888 logFont.lfWeight = FW_BOLD;
1889 infoPtr->hBoldFont = CreateFontIndirectA(&logFont);
1890
1891 /* initialize info structure */
1892 /* FIXME: calculate systemtime ->> localtime(substract timezoneinfo) */
1893
1894 GetSystemTime(&infoPtr->todaysDate);
1895 MONTHCAL_SetFirstDayOfWeek(hwnd,0,(LPARAM)-1);
1896 infoPtr->currentMonth = infoPtr->todaysDate.wMonth;
1897 infoPtr->currentYear = infoPtr->todaysDate.wYear;
1898 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->minDate);
1899 MONTHCAL_CopyTime(&infoPtr->todaysDate, &infoPtr->maxDate);
1900 infoPtr->maxSelCount = 7;
1901 infoPtr->monthRange = 3;
1902 infoPtr->monthdayState = COMCTL32_Alloc
1903 (infoPtr->monthRange * sizeof(MONTHDAYSTATE));
1904 infoPtr->titlebk = GetSysColor(COLOR_ACTIVECAPTION);
1905 infoPtr->titletxt = GetSysColor(COLOR_WINDOW);
1906 infoPtr->monthbk = GetSysColor(COLOR_WINDOW);
1907 infoPtr->trailingtxt = GetSysColor(COLOR_GRAYTEXT);
1908 infoPtr->bk = GetSysColor(COLOR_WINDOW);
1909 infoPtr->txt = GetSysColor(COLOR_WINDOWTEXT);
1910
1911 /* call MONTHCAL_UpdateSize to set all of the dimensions */
1912 /* of the control */
1913 MONTHCAL_UpdateSize(hwnd);
1914
1915 return 0;
1916}
1917
1918
1919static LRESULT
1920MONTHCAL_Destroy(HWND hwnd, WPARAM wParam, LPARAM lParam)
1921{
1922 MONTHCAL_INFO *infoPtr = MONTHCAL_GetInfoPtr(hwnd);
1923
1924 /* free month calendar info data */
1925 COMCTL32_Free(infoPtr);
1926 SetWindowLongA(hwnd, 0, 0);
1927 return 0;
1928}
1929
1930
1931static LRESULT WINAPI
1932MONTHCAL_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1933{
1934 TRACE("hwnd=%x msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam);
1935 if (!MONTHCAL_GetInfoPtr(hwnd) && (uMsg != WM_CREATE))
1936 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
1937 switch(uMsg)
1938 {
1939 case MCM_GETCURSEL:
1940 return MONTHCAL_GetCurSel(hwnd, wParam, lParam);
1941
1942 case MCM_SETCURSEL:
1943 return MONTHCAL_SetCurSel(hwnd, wParam, lParam);
1944
1945 case MCM_GETMAXSELCOUNT:
1946 return MONTHCAL_GetMaxSelCount(hwnd, wParam, lParam);
1947
1948 case MCM_SETMAXSELCOUNT:
1949 return MONTHCAL_SetMaxSelCount(hwnd, wParam, lParam);
1950
1951 case MCM_GETSELRANGE:
1952 return MONTHCAL_GetSelRange(hwnd, wParam, lParam);
1953
1954 case MCM_SETSELRANGE:
1955 return MONTHCAL_SetSelRange(hwnd, wParam, lParam);
1956
1957 case MCM_GETMONTHRANGE:
1958 return MONTHCAL_GetMonthRange(hwnd, wParam, lParam);
1959
1960 case MCM_SETDAYSTATE:
1961 return MONTHCAL_SetDayState(hwnd, wParam, lParam);
1962
1963 case MCM_GETMINREQRECT:
1964 return MONTHCAL_GetMinReqRect(hwnd, wParam, lParam);
1965
1966 case MCM_GETCOLOR:
1967 return MONTHCAL_GetColor(hwnd, wParam, lParam);
1968
1969 case MCM_SETCOLOR:
1970 return MONTHCAL_SetColor(hwnd, wParam, lParam);
1971
1972 case MCM_GETTODAY:
1973 return MONTHCAL_GetToday(hwnd, wParam, lParam);
1974
1975 case MCM_SETTODAY:
1976 return MONTHCAL_SetToday(hwnd, wParam, lParam);
1977
1978 case MCM_HITTEST:
1979 return MONTHCAL_HitTest(hwnd,lParam);
1980
1981 case MCM_GETFIRSTDAYOFWEEK:
1982 return MONTHCAL_GetFirstDayOfWeek(hwnd, wParam, lParam);
1983
1984 case MCM_SETFIRSTDAYOFWEEK:
1985 return MONTHCAL_SetFirstDayOfWeek(hwnd, wParam, lParam);
1986
1987 case MCM_GETRANGE:
1988 return MONTHCAL_GetRange(hwnd, wParam, lParam);
1989
1990 case MCM_SETRANGE:
1991 return MONTHCAL_SetRange(hwnd, wParam, lParam);
1992
1993 case MCM_GETMONTHDELTA:
1994 return MONTHCAL_GetMonthDelta(hwnd, wParam, lParam);
1995
1996 case MCM_SETMONTHDELTA:
1997 return MONTHCAL_SetMonthDelta(hwnd, wParam, lParam);
1998
1999 case MCM_GETMAXTODAYWIDTH:
2000 return MONTHCAL_GetMaxTodayWidth(hwnd);
2001
2002 case WM_GETDLGCODE:
2003 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2004
2005 case WM_KILLFOCUS:
2006 return MONTHCAL_KillFocus(hwnd, wParam, lParam);
2007
2008 case WM_RBUTTONDOWN:
2009 return MONTHCAL_RButtonDown(hwnd, wParam, lParam);
2010
2011 case WM_LBUTTONDOWN:
2012 return MONTHCAL_LButtonDown(hwnd, wParam, lParam);
2013
2014 case WM_MOUSEMOVE:
2015 return MONTHCAL_MouseMove(hwnd, wParam, lParam);
2016
2017 case WM_LBUTTONUP:
2018 return MONTHCAL_LButtonUp(hwnd, wParam, lParam);
2019
2020 case WM_PAINT:
2021 return MONTHCAL_Paint(hwnd, wParam);
2022
2023 case WM_SETFOCUS:
2024 return MONTHCAL_SetFocus(hwnd, wParam, lParam);
2025
2026 case WM_SIZE:
2027 return MONTHCAL_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
2028
2029 case WM_CREATE:
2030 return MONTHCAL_Create(hwnd, wParam, lParam);
2031
2032 case WM_TIMER:
2033 return MONTHCAL_Timer(hwnd, wParam, lParam);
2034
2035 case WM_DESTROY:
2036 return MONTHCAL_Destroy(hwnd, wParam, lParam);
2037
2038 default:
2039 if(uMsg >= WM_USER)
2040 ERR( "unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
2041#ifdef __WIN32OS2__
2042 return defComCtl32ProcA(hwnd, uMsg, wParam, lParam);
2043#else
2044 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
2045#endif
2046 }
2047 return 0;
2048}
2049
2050
2051void
2052MONTHCAL_Register(void)
2053{
2054 WNDCLASSA wndClass;
2055
2056 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
2057 wndClass.style = CS_GLOBALCLASS;
2058 wndClass.lpfnWndProc = (WNDPROC)MONTHCAL_WindowProc;
2059 wndClass.cbClsExtra = 0;
2060 wndClass.cbWndExtra = sizeof(MONTHCAL_INFO *);
2061 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
2062 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2063 wndClass.lpszClassName = MONTHCAL_CLASSA;
2064
2065 RegisterClassA(&wndClass);
2066}
2067
2068
2069void
2070MONTHCAL_Unregister(void)
2071{
2072 UnregisterClassA(MONTHCAL_CLASSA, (HINSTANCE)NULL);
2073}
Note: See TracBrowser for help on using the repository browser.