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

Last change on this file since 5280 was 4627, checked in by sandervl, 25 years ago

Resynched with Christoph's changes for unicode handling

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