1 /*
2 * PROJECT: ReactOS Timedate Control Panel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/cpl/timedate/monthcal.c
5 * PURPOSE: Calander implementation
6 * COPYRIGHT: Copyright 2006 Thomas Weidenmueller <w3seek@reactos.com>
7 *
8 */
9
10 #include "timedate.h"
11
12 #include <windowsx.h>
13
14 static const WCHAR szMonthCalWndClass[] = L"MonthCalWnd";
15
16 #define MONTHCAL_HEADERBG COLOR_INACTIVECAPTION
17 #define MONTHCAL_HEADERFG COLOR_INACTIVECAPTIONTEXT
18 #define MONTHCAL_CTRLBG COLOR_WINDOW
19 #define MONTHCAL_CTRLFG COLOR_WINDOWTEXT
20 #define MONTHCAL_SELBG COLOR_ACTIVECAPTION
21 #define MONTHCAL_SELFG COLOR_CAPTIONTEXT
22 #define MONTHCAL_DISABLED_HEADERBG COLOR_INACTIVECAPTION
23 #define MONTHCAL_DISABLED_HEADERFG COLOR_INACTIVECAPTIONTEXT
24 #define MONTHCAL_DISABLED_CTRLBG COLOR_WINDOW
25 #define MONTHCAL_DISABLED_CTRLFG COLOR_WINDOWTEXT
26 #define MONTHCAL_DISABLED_SELBG COLOR_INACTIVECAPTION
27 #define MONTHCAL_DISABLED_SELFG COLOR_INACTIVECAPTIONTEXT
28
29 #define ID_DAYTIMER 1
30
31 typedef struct _MONTHCALWND
32 {
33 HWND hSelf;
34 HWND hNotify;
35 WORD Day;
36 WORD Month;
37 WORD Year;
38 WORD FirstDayOfWeek;
39 BYTE Days[6][7];
40 WCHAR Week[7];
41 SIZE CellSize;
42 SIZE ClientSize;
43
44 HFONT hFont;
45 HBRUSH hbHeader;
46 HBRUSH hbSelection;
47
48 DWORD UIState;
49 UINT Changed : 1;
50 UINT DayTimerSet : 1;
51 UINT Enabled : 1;
52 UINT HasFocus : 1;
53 } MONTHCALWND, *PMONTHCALWND;
54
55 static LRESULT
MonthCalNotifyControlParent(IN PMONTHCALWND infoPtr,IN UINT code,IN OUT PVOID data)56 MonthCalNotifyControlParent(IN PMONTHCALWND infoPtr,
57 IN UINT code,
58 IN OUT PVOID data)
59 {
60 LRESULT Ret = 0;
61
62 if (infoPtr->hNotify != NULL)
63 {
64 LPNMHDR pnmh = (LPNMHDR)data;
65
66 pnmh->hwndFrom = infoPtr->hSelf;
67 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hSelf,
68 GWLP_ID);
69 pnmh->code = code;
70
71 Ret = SendMessageW(infoPtr->hNotify,
72 WM_NOTIFY,
73 (WPARAM)pnmh->idFrom,
74 (LPARAM)pnmh);
75 }
76
77 return Ret;
78 }
79
80 /*
81 * For the year range 1..9999
82 * return 1 if is leap year otherwise 0
83 */
LeapYear(IN WORD Year)84 static WORD LeapYear(IN WORD Year)
85 {
86 return
87 #ifdef WITH_1752
88 (Year <= 1752) ? !(Year % 4) :
89 #endif
90 !(Year % 4) && ((Year % 100) || !(Year % 400));
91 }
92
93 static WORD
MonthCalMonthLength(IN WORD Month,IN WORD Year)94 MonthCalMonthLength(IN WORD Month,
95 IN WORD Year)
96 {
97 const BYTE MonthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
98
99 if(Month == 2)
100 return MonthDays[Month - 1] + LeapYear(Year);
101 else
102 {
103 #ifdef WITH_1752
104 if ((Year == 1752) && (Month == 9))
105 return 19; // Special case: September 1752 has no 3rd-13th
106 else
107 #endif
108 return MonthDays[Month - 1];
109 }
110 }
111
112 static WORD
MonthCalWeekInMonth(IN WORD Day,IN WORD DayOfWeek)113 MonthCalWeekInMonth(IN WORD Day,
114 IN WORD DayOfWeek)
115 {
116 return (Day - DayOfWeek + 5) / 7;
117 }
118
119 static WORD
MonthCalDayOfWeek(IN PMONTHCALWND infoPtr,IN WORD Day,IN WORD Month,IN WORD Year)120 MonthCalDayOfWeek(IN PMONTHCALWND infoPtr,
121 IN WORD Day,
122 IN WORD Month,
123 IN WORD Year)
124 {
125 const BYTE DayOfWeek[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
126 WORD Ret;
127
128 Year -= (Month < 3);
129 Ret = (Year + (Year / 4) - (Year / 100) + (Year / 400) + DayOfWeek[Month - 1] + Day + 6) % 7;
130
131 return (7 + Ret - infoPtr->FirstDayOfWeek) % 7;
132 }
133
134 static WORD
MonthCalFirstDayOfWeek(VOID)135 MonthCalFirstDayOfWeek(VOID)
136 {
137 WCHAR szBuf[2] = {0};
138 WORD Ret = 0;
139
140 if (GetLocaleInfoW(LOCALE_USER_DEFAULT,
141 LOCALE_IFIRSTDAYOFWEEK,
142 szBuf,
143 sizeof(szBuf) / sizeof(szBuf[0])) != 0)
144 {
145 Ret = (WORD)(szBuf[0] - TEXT('0'));
146 }
147
148 return Ret;
149 }
150
151 static BOOL
MonthCalValidDate(IN WORD Day,IN WORD Month,IN WORD Year)152 MonthCalValidDate(IN WORD Day,
153 IN WORD Month,
154 IN WORD Year)
155 {
156 if (Month < 1 || Month > 12 ||
157 Day == 0 || Day > MonthCalMonthLength(Month,
158 Year) ||
159 Year < 1899 || Year > 9999)
160 {
161 return FALSE;
162 }
163
164 return TRUE;
165 }
166
167 static VOID
MonthCalUpdate(IN PMONTHCALWND infoPtr)168 MonthCalUpdate(IN PMONTHCALWND infoPtr)
169 {
170 PBYTE pDay, pDayEnd;
171 WORD DayOfWeek, MonthLength, d = 0;
172 SIZE NewCellSize;
173 BOOL RepaintHeader = FALSE;
174
175 NewCellSize.cx = infoPtr->ClientSize.cx / 7;
176 NewCellSize.cy = infoPtr->ClientSize.cy / 7;
177
178 if (infoPtr->CellSize.cx != NewCellSize.cx ||
179 infoPtr->CellSize.cy != NewCellSize.cy)
180 {
181 infoPtr->CellSize = NewCellSize;
182 RepaintHeader = TRUE;
183 }
184
185 /* Update the days layout of the current month */
186 ZeroMemory(infoPtr->Days,
187 sizeof(infoPtr->Days));
188
189 DayOfWeek = MonthCalDayOfWeek(infoPtr,
190 1,
191 infoPtr->Month,
192 infoPtr->Year);
193
194 MonthLength = MonthCalMonthLength(infoPtr->Month,
195 infoPtr->Year);
196
197 pDay = &infoPtr->Days[0][DayOfWeek];
198 pDayEnd = pDay + MonthLength;
199 while (pDay != pDayEnd)
200 {
201 *(pDay++) = (BYTE)++d;
202 }
203
204 /* Repaint the control */
205 if (RepaintHeader)
206 {
207 InvalidateRect(infoPtr->hSelf,
208 NULL,
209 TRUE);
210 }
211 else
212 {
213 RECT rcClient;
214
215 rcClient.left = 0;
216 rcClient.top = infoPtr->CellSize.cy;
217 rcClient.right = infoPtr->ClientSize.cx;
218 rcClient.bottom = infoPtr->ClientSize.cy;
219
220 InvalidateRect(infoPtr->hSelf,
221 &rcClient,
222 TRUE);
223 }
224 }
225
226 static VOID
MonthCalSetupDayTimer(IN PMONTHCALWND infoPtr)227 MonthCalSetupDayTimer(IN PMONTHCALWND infoPtr)
228 {
229 SYSTEMTIME LocalTime = {0};
230 UINT uElapse;
231
232 /* Update the current date */
233 GetLocalTime(&LocalTime);
234
235 /* Calculate the number of remaining milliseconds until midnight */
236 uElapse = 1000 - (UINT)LocalTime.wMilliseconds;
237 uElapse += (59 - (UINT)LocalTime.wSecond) * 1000;
238 uElapse += (59 - (UINT)LocalTime.wMinute) * 60 * 1000;
239 uElapse += (23 - (UINT)LocalTime.wHour) * 60 * 60 * 1000;
240
241 /* Setup the new timer */
242 if (SetTimer(infoPtr->hSelf,
243 ID_DAYTIMER,
244 uElapse,
245 NULL) != 0)
246 {
247 infoPtr->DayTimerSet = TRUE;
248 }
249 }
250
251 static VOID
MonthCalReload(IN PMONTHCALWND infoPtr)252 MonthCalReload(IN PMONTHCALWND infoPtr)
253 {
254 WCHAR szBuf[64];
255 UINT i;
256
257 infoPtr->UIState = (DWORD)SendMessageW(GetAncestor(infoPtr->hSelf,
258 GA_PARENT),
259 WM_QUERYUISTATE,
260 0,
261 0);
262
263 /* Cache the configuration */
264 infoPtr->FirstDayOfWeek = MonthCalFirstDayOfWeek();
265
266 infoPtr->hbHeader = GetSysColorBrush(infoPtr->Enabled ? MONTHCAL_HEADERBG : MONTHCAL_DISABLED_HEADERBG);
267 infoPtr->hbSelection = GetSysColorBrush(infoPtr->Enabled ? MONTHCAL_SELBG : MONTHCAL_DISABLED_SELBG);
268
269 for (i = 0; i < 7; i++)
270 {
271 if (GetLocaleInfoW(LOCALE_USER_DEFAULT,
272 LOCALE_SABBREVDAYNAME1 +
273 ((i + infoPtr->FirstDayOfWeek) % 7),
274 szBuf,
275 sizeof(szBuf) / sizeof(szBuf[0])) != 0)
276 {
277 infoPtr->Week[i] = szBuf[0];
278 }
279 }
280
281 /* Update the control */
282 MonthCalUpdate(infoPtr);
283 }
284
285 static BOOL
MonthCalGetDayRect(IN PMONTHCALWND infoPtr,IN WORD Day,OUT RECT * rcCell)286 MonthCalGetDayRect(IN PMONTHCALWND infoPtr,
287 IN WORD Day,
288 OUT RECT *rcCell)
289 {
290 if (Day >= 1 && Day <= MonthCalMonthLength(infoPtr->Month,
291 infoPtr->Year))
292 {
293 WORD DayOfWeek;
294
295 DayOfWeek = MonthCalDayOfWeek(infoPtr,
296 Day,
297 infoPtr->Month,
298 infoPtr->Year);
299
300 rcCell->left = DayOfWeek * infoPtr->CellSize.cx;
301 rcCell->top = (MonthCalWeekInMonth(Day,
302 DayOfWeek) + 1) * infoPtr->CellSize.cy;
303 rcCell->right = rcCell->left + infoPtr->CellSize.cx;
304 rcCell->bottom = rcCell->top + infoPtr->CellSize.cy;
305
306 return TRUE;
307 }
308
309 return FALSE;
310 }
311
312 static VOID
MonthCalChange(IN PMONTHCALWND infoPtr)313 MonthCalChange(IN PMONTHCALWND infoPtr)
314 {
315 infoPtr->Changed = TRUE;
316
317 /* Kill the day timer */
318 if (infoPtr->DayTimerSet)
319 {
320 KillTimer(infoPtr->hSelf,
321 ID_DAYTIMER);
322 infoPtr->DayTimerSet = FALSE;
323 }
324 }
325
326
327 static BOOL
MonthCalSetDate(IN PMONTHCALWND infoPtr,IN WORD Day,IN WORD Month,IN WORD Year)328 MonthCalSetDate(IN PMONTHCALWND infoPtr,
329 IN WORD Day,
330 IN WORD Month,
331 IN WORD Year)
332 {
333 NMMCCSELCHANGE sc;
334 BOOL Ret = FALSE;
335
336 sc.OldDay = infoPtr->Day;
337 sc.OldMonth = infoPtr->Month;
338 sc.OldYear = infoPtr->Year;
339 sc.NewDay = Day;
340 sc.NewMonth = Month;
341 sc.NewYear = Year;
342
343 /* Notify the parent */
344 if (!MonthCalNotifyControlParent(infoPtr,
345 MCCN_SELCHANGE,
346 &sc))
347 {
348 /* Check if we actually need to update */
349 if (infoPtr->Month != sc.NewMonth ||
350 infoPtr->Year != sc.NewYear)
351 {
352 infoPtr->Day = sc.NewDay;
353 infoPtr->Month = sc.NewMonth;
354 infoPtr->Year = sc.NewYear;
355
356 MonthCalChange(infoPtr);
357
358 /* Repaint the entire control */
359 MonthCalUpdate(infoPtr);
360
361 Ret = TRUE;
362 }
363 else if (infoPtr->Day != sc.NewDay)
364 {
365 RECT rcUpdate;
366
367 infoPtr->Day = sc.NewDay;
368
369 MonthCalChange(infoPtr);
370
371 if (MonthCalGetDayRect(infoPtr,
372 sc.OldDay,
373 &rcUpdate))
374 {
375 /* Repaint the day cells that need to be updated */
376 InvalidateRect(infoPtr->hSelf,
377 &rcUpdate,
378 TRUE);
379 if (MonthCalGetDayRect(infoPtr,
380 sc.NewDay,
381 &rcUpdate))
382 {
383 InvalidateRect(infoPtr->hSelf,
384 &rcUpdate,
385 TRUE);
386 }
387 }
388
389 Ret = TRUE;
390 }
391 }
392
393 return Ret;
394 }
395
396 static VOID
MonthCalSetLocalTime(IN PMONTHCALWND infoPtr,OUT SYSTEMTIME * Time)397 MonthCalSetLocalTime(IN PMONTHCALWND infoPtr,
398 OUT SYSTEMTIME *Time)
399 {
400 NMMCCAUTOUPDATE au;
401 SYSTEMTIME LocalTime = {0};
402
403 GetLocalTime(&LocalTime);
404
405 au.SystemTime = LocalTime;
406 if (!MonthCalNotifyControlParent(infoPtr,
407 MCCN_AUTOUPDATE,
408 &au))
409 {
410 if (MonthCalSetDate(infoPtr,
411 LocalTime.wDay,
412 LocalTime.wMonth,
413 LocalTime.wYear))
414 {
415 infoPtr->Changed = FALSE;
416 }
417 }
418
419 /* Kill the day timer */
420 if (infoPtr->DayTimerSet)
421 {
422 KillTimer(infoPtr->hSelf,
423 ID_DAYTIMER);
424 infoPtr->DayTimerSet = FALSE;
425 }
426
427 /* Setup the new day timer */
428 MonthCalSetupDayTimer(infoPtr);
429
430 if (Time != NULL)
431 {
432 *Time = LocalTime;
433 }
434 }
435
436 static VOID
MonthCalRepaintDay(IN PMONTHCALWND infoPtr,IN WORD Day)437 MonthCalRepaintDay(IN PMONTHCALWND infoPtr,
438 IN WORD Day)
439 {
440 RECT rcCell;
441
442 if (MonthCalGetDayRect(infoPtr,
443 Day,
444 &rcCell))
445 {
446 InvalidateRect(infoPtr->hSelf,
447 &rcCell,
448 TRUE);
449 }
450 }
451
452 static VOID
MonthCalPaint(IN PMONTHCALWND infoPtr,IN HDC hDC,IN LPRECT prcUpdate)453 MonthCalPaint(IN PMONTHCALWND infoPtr,
454 IN HDC hDC,
455 IN LPRECT prcUpdate)
456 {
457 LONG x, y;
458 RECT rcCell;
459 COLORREF crOldText, crOldCtrlText = CLR_INVALID;
460 HFONT hOldFont;
461 INT iOldBkMode;
462
463 #if MONTHCAL_CTRLBG != MONTHCAL_DISABLED_CTRLBG
464 if (!infoPtr->Enabled)
465 {
466 FillRect(hDC,
467 prcUpdate,
468 GetSysColorBrush(MONTHCAL_DISABLED_CTRLBG));
469 }
470 #endif
471
472 iOldBkMode = SetBkMode(hDC,
473 TRANSPARENT);
474 hOldFont = (HFONT)SelectObject(hDC,
475 infoPtr->hFont);
476
477 for (y = prcUpdate->top / infoPtr->CellSize.cy;
478 y <= prcUpdate->bottom / infoPtr->CellSize.cy && y < 7;
479 y++)
480 {
481 rcCell.top = y * infoPtr->CellSize.cy;
482 rcCell.bottom = rcCell.top + infoPtr->CellSize.cy;
483
484 if (y == 0)
485 {
486 RECT rcHeader;
487
488 /* Paint the header */
489 rcHeader.left = prcUpdate->left;
490 rcHeader.top = rcCell.top;
491 rcHeader.right = prcUpdate->right;
492 rcHeader.bottom = rcCell.bottom;
493
494 FillRect(hDC,
495 &rcHeader,
496 infoPtr->hbHeader);
497
498 crOldText = SetTextColor(hDC,
499 GetSysColor(infoPtr->Enabled ? MONTHCAL_HEADERFG : MONTHCAL_DISABLED_HEADERFG));
500
501 for (x = prcUpdate->left / infoPtr->CellSize.cx;
502 x <= prcUpdate->right / infoPtr->CellSize.cx && x < 7;
503 x++)
504 {
505 rcCell.left = x * infoPtr->CellSize.cx;
506 rcCell.right = rcCell.left + infoPtr->CellSize.cx;
507
508 /* Write the first letter of each weekday */
509 DrawTextW(hDC,
510 &infoPtr->Week[x],
511 1,
512 &rcCell,
513 DT_SINGLELINE | DT_NOPREFIX | DT_CENTER | DT_VCENTER);
514 }
515
516 SetTextColor(hDC,
517 crOldText);
518 }
519 else
520 {
521 if (crOldCtrlText == CLR_INVALID)
522 {
523 crOldCtrlText = SetTextColor(hDC,
524 GetSysColor(infoPtr->Enabled ? MONTHCAL_CTRLFG : MONTHCAL_DISABLED_CTRLFG));
525 }
526
527 for (x = prcUpdate->left / infoPtr->CellSize.cx;
528 x <= prcUpdate->right / infoPtr->CellSize.cx && x < 7;
529 x++)
530 {
531 UINT Day = infoPtr->Days[y - 1][x];
532
533 rcCell.left = x * infoPtr->CellSize.cx;
534 rcCell.right = rcCell.left + infoPtr->CellSize.cx;
535
536 /* Write the day number */
537 if (Day != 0 && Day < 100)
538 {
539 WCHAR szDay[3];
540 INT szDayLen;
541 RECT rcText;
542 SIZE TextSize;
543
544 szDayLen = swprintf(szDay,
545 L"%lu",
546 Day);
547
548 if (GetTextExtentPoint32W(hDC,
549 szDay,
550 szDayLen,
551 &TextSize))
552 {
553 RECT rcHighlight = { 0, 0, 0, 0 };
554
555 rcText.left = rcCell.left + (infoPtr->CellSize.cx / 2) - (TextSize.cx / 2);
556 rcText.top = rcCell.top + (infoPtr->CellSize.cy / 2) - (TextSize.cy / 2);
557 rcText.right = rcText.left + TextSize.cx;
558 rcText.bottom = rcText.top + TextSize.cy;
559
560 if (Day == infoPtr->Day)
561 {
562 SIZE TextSel;
563
564 TextSel.cx = (infoPtr->CellSize.cx * 2) / 3;
565 TextSel.cy = (infoPtr->CellSize.cy * 3) / 4;
566
567 if (TextSel.cx < rcText.right - rcText.left)
568 TextSel.cx = rcText.right - rcText.left;
569 if (TextSel.cy < rcText.bottom - rcText.top)
570 TextSel.cy = rcText.bottom - rcText.top;
571
572 rcHighlight.left = rcCell.left + (infoPtr->CellSize.cx / 2) - (TextSel.cx / 2);
573 rcHighlight.right = rcHighlight.left + TextSel.cx;
574 rcHighlight.top = rcCell.top + (infoPtr->CellSize.cy / 2) - (TextSel.cy / 2);
575 rcHighlight.bottom = rcHighlight.top + TextSel.cy;
576
577 InflateRect(&rcHighlight,
578 GetSystemMetrics(SM_CXFOCUSBORDER),
579 GetSystemMetrics(SM_CYFOCUSBORDER));
580
581 if (!FillRect(hDC,
582 &rcHighlight,
583 infoPtr->hbSelection))
584 {
585 goto FailNoHighlight;
586 }
587
588 /* Highlight the selected day */
589 crOldText = SetTextColor(hDC,
590 GetSysColor(infoPtr->Enabled ? MONTHCAL_SELFG : MONTHCAL_DISABLED_SELFG));
591 }
592 else
593 {
594 FailNoHighlight:
595 /* Don't change the text color, we're not highlighting it... */
596 crOldText = CLR_INVALID;
597 }
598
599 TextOutW(hDC,
600 rcText.left,
601 rcText.top,
602 szDay,
603 szDayLen);
604
605 if (Day == infoPtr->Day && crOldText != CLR_INVALID)
606 {
607 if (infoPtr->HasFocus && infoPtr->Enabled && !(infoPtr->UIState & UISF_HIDEFOCUS))
608 {
609 COLORREF crOldBk;
610
611 crOldBk = SetBkColor(hDC,
612 GetSysColor(infoPtr->Enabled ? MONTHCAL_SELBG : MONTHCAL_DISABLED_SELBG));
613
614 DrawFocusRect(hDC,
615 &rcHighlight);
616
617 SetBkColor(hDC,
618 crOldBk);
619 }
620
621 SetTextColor(hDC,
622 crOldText);
623 }
624 }
625 }
626 }
627 }
628 }
629
630 if (crOldCtrlText != CLR_INVALID)
631 {
632 SetTextColor(hDC,
633 crOldCtrlText);
634 }
635
636 SetBkMode(hDC,
637 iOldBkMode);
638 SelectObject(hDC,
639 (HGDIOBJ)hOldFont);
640 }
641
642 static HFONT
MonthCalChangeFont(IN PMONTHCALWND infoPtr,IN HFONT hFont,IN BOOL Redraw)643 MonthCalChangeFont(IN PMONTHCALWND infoPtr,
644 IN HFONT hFont,
645 IN BOOL Redraw)
646 {
647 HFONT hOldFont = infoPtr->hFont;
648 infoPtr->hFont = hFont;
649
650 if (Redraw)
651 {
652 InvalidateRect(infoPtr->hSelf,
653 NULL,
654 TRUE);
655 }
656
657 return hOldFont;
658 }
659
660 static WORD
MonthCalPtToDay(IN PMONTHCALWND infoPtr,IN INT x,IN INT y)661 MonthCalPtToDay(IN PMONTHCALWND infoPtr,
662 IN INT x,
663 IN INT y)
664 {
665 WORD Ret = 0;
666
667 if (infoPtr->CellSize.cx != 0 && infoPtr->CellSize.cy != 0 &&
668 x >= 0 && y >= 0)
669 {
670 x /= infoPtr->CellSize.cx;
671 y /= infoPtr->CellSize.cy;
672
673 if (x < 7 && y != 0 && y < 7)
674 {
675 Ret = (WORD)infoPtr->Days[y - 1][x];
676 }
677 }
678
679 return Ret;
680 }
681
682 static LRESULT CALLBACK
MonthCalWndProc(IN HWND hwnd,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam)683 MonthCalWndProc(IN HWND hwnd,
684 IN UINT uMsg,
685 IN WPARAM wParam,
686 IN LPARAM lParam)
687 {
688 PMONTHCALWND infoPtr;
689 LRESULT Ret = 0;
690
691 infoPtr = (PMONTHCALWND)GetWindowLongPtrW(hwnd,
692 0);
693
694 if (infoPtr == NULL && uMsg != WM_CREATE)
695 {
696 goto HandleDefaultMessage;
697 }
698
699 switch (uMsg)
700 {
701 #if MONTHCAL_CTRLBG != MONTHCAL_DISABLED_CTRLBG
702 case WM_ERASEBKGND:
703 Ret = !infoPtr->Enabled;
704 break;
705 #endif
706
707 case WM_PAINT:
708 case WM_PRINTCLIENT:
709 {
710 if (infoPtr->CellSize.cx != 0 && infoPtr->CellSize.cy != 0)
711 {
712 PAINTSTRUCT ps;
713 HDC hDC;
714
715 if (wParam != 0)
716 {
717 if (!GetUpdateRect(hwnd,
718 &ps.rcPaint,
719 TRUE))
720 {
721 break;
722 }
723 hDC = (HDC)wParam;
724 }
725 else
726 {
727 hDC = BeginPaint(hwnd,
728 &ps);
729 if (hDC == NULL)
730 {
731 break;
732 }
733 }
734
735 MonthCalPaint(infoPtr,
736 hDC,
737 &ps.rcPaint);
738
739 if (wParam == 0)
740 {
741 EndPaint(hwnd,
742 &ps);
743 }
744 }
745 break;
746 }
747
748 case WM_LBUTTONDBLCLK:
749 case WM_LBUTTONDOWN:
750 {
751 WORD SelDay;
752
753 SelDay = MonthCalPtToDay(infoPtr,
754 GET_X_LPARAM(lParam),
755 GET_Y_LPARAM(lParam));
756 if (SelDay != 0 && SelDay != infoPtr->Day)
757 {
758 MonthCalSetDate(infoPtr,
759 SelDay,
760 infoPtr->Month,
761 infoPtr->Year);
762 }
763
764 /* Fall through */
765 }
766
767 case WM_MBUTTONDOWN:
768 case WM_RBUTTONDOWN:
769 {
770 if (!infoPtr->HasFocus)
771 {
772 SetFocus(hwnd);
773 }
774 break;
775 }
776
777 case WM_KEYDOWN:
778 {
779 WORD NewDay = 0;
780
781 switch (wParam)
782 {
783 case VK_UP:
784 {
785 if (infoPtr->Day > 7)
786 {
787 NewDay = infoPtr->Day - 7;
788 }
789 break;
790 }
791
792 case VK_DOWN:
793 {
794 if (infoPtr->Day + 7 <= MonthCalMonthLength(infoPtr->Month,
795 infoPtr->Year))
796 {
797 NewDay = infoPtr->Day + 7;
798 }
799 break;
800 }
801
802 case VK_LEFT:
803 {
804 if (infoPtr->Day > 1)
805 {
806 NewDay = infoPtr->Day - 1;
807 }
808 break;
809 }
810
811 case VK_RIGHT:
812 {
813 if (infoPtr->Day < MonthCalMonthLength(infoPtr->Month,
814 infoPtr->Year))
815 {
816 NewDay = infoPtr->Day + 1;
817 }
818 break;
819 }
820 }
821
822 /* Update the selection */
823 if (NewDay != 0)
824 {
825 MonthCalSetDate(infoPtr,
826 NewDay,
827 infoPtr->Month,
828 infoPtr->Year);
829 }
830
831 goto HandleDefaultMessage;
832 }
833
834 case WM_GETDLGCODE:
835 {
836 INT virtKey;
837
838 virtKey = (lParam != 0 ? (INT)((LPMSG)lParam)->wParam : 0);
839 switch (virtKey)
840 {
841 case VK_TAB:
842 {
843 /* Change the UI status */
844 SendMessageW(GetAncestor(hwnd,
845 GA_PARENT),
846 WM_CHANGEUISTATE,
847 MAKEWPARAM(UIS_INITIALIZE,
848 0),
849 0);
850 break;
851 }
852 }
853
854 Ret |= DLGC_WANTARROWS;
855 break;
856 }
857
858 case WM_SETFOCUS:
859 {
860 infoPtr->HasFocus = TRUE;
861 MonthCalRepaintDay(infoPtr,
862 infoPtr->Day);
863 break;
864 }
865
866 case WM_KILLFOCUS:
867 {
868 infoPtr->HasFocus = FALSE;
869 MonthCalRepaintDay(infoPtr,
870 infoPtr->Day);
871 break;
872 }
873
874 case WM_UPDATEUISTATE:
875 {
876 DWORD OldUIState;
877
878 Ret = DefWindowProcW(hwnd,
879 uMsg,
880 wParam,
881 lParam);
882
883 OldUIState = infoPtr->UIState;
884 switch (LOWORD(wParam))
885 {
886 case UIS_SET:
887 infoPtr->UIState |= HIWORD(wParam);
888 break;
889
890 case UIS_CLEAR:
891 infoPtr->UIState &= ~HIWORD(wParam);
892 break;
893 }
894
895 if (infoPtr->UIState != OldUIState)
896 {
897 MonthCalRepaintDay(infoPtr,
898 infoPtr->Day);
899 }
900 break;
901 }
902
903 case MCCM_SETDATE:
904 {
905 WORD Day, Month, Year, DaysCount;
906
907 Day = LOWORD(wParam);
908 Month = HIWORD(wParam);
909 Year = LOWORD(lParam);
910
911 if (Day == (WORD)-1)
912 Day = infoPtr->Day;
913 if (Month == (WORD)-1)
914 Month = infoPtr->Month;
915 if (Year == (WORD)-1)
916 Year = infoPtr->Year;
917
918 DaysCount = MonthCalMonthLength(Month,
919 Year);
920 if (Day > DaysCount)
921 Day = DaysCount;
922
923 if (MonthCalValidDate(Day,
924 Month,
925 Year))
926 {
927 if (Day != infoPtr->Day ||
928 Month != infoPtr->Month ||
929 Year != infoPtr->Year)
930 {
931 Ret = MonthCalSetDate(infoPtr,
932 Day,
933 Month,
934 Year);
935 }
936 }
937 break;
938 }
939
940 case MCCM_GETDATE:
941 {
942 LPSYSTEMTIME lpSystemTime = (LPSYSTEMTIME)wParam;
943
944 lpSystemTime->wYear = infoPtr->Year;
945 lpSystemTime->wMonth = infoPtr->Month;
946 lpSystemTime->wDay = infoPtr->Day;
947
948 Ret = TRUE;
949 break;
950 }
951
952 case MCCM_RESET:
953 {
954 MonthCalSetLocalTime(infoPtr,
955 NULL);
956 Ret = TRUE;
957 break;
958 }
959
960 case MCCM_CHANGED:
961 {
962 Ret = infoPtr->Changed;
963 break;
964 }
965
966 case WM_TIMER:
967 {
968 switch (wParam)
969 {
970 case ID_DAYTIMER:
971 {
972 /* Kill the timer */
973 KillTimer(hwnd,
974 ID_DAYTIMER);
975 infoPtr->DayTimerSet = FALSE;
976
977 if (!infoPtr->Changed)
978 {
979 /* Update the system time and setup the new day timer */
980 MonthCalSetLocalTime(infoPtr,
981 NULL);
982
983 /* Update the control */
984 MonthCalUpdate(infoPtr);
985 }
986 break;
987 }
988 }
989 break;
990 }
991
992 case WM_SETFONT:
993 {
994 Ret = (LRESULT)MonthCalChangeFont(infoPtr,
995 (HFONT)wParam,
996 (BOOL)LOWORD(lParam));
997 break;
998 }
999
1000 case WM_SIZE:
1001 {
1002 infoPtr->ClientSize.cx = LOWORD(lParam);
1003 infoPtr->ClientSize.cy = HIWORD(lParam);
1004 infoPtr->CellSize.cx = infoPtr->ClientSize.cx / 7;
1005 infoPtr->CellSize.cy = infoPtr->ClientSize.cy / 7;
1006
1007 /* Repaint the control */
1008 InvalidateRect(hwnd,
1009 NULL,
1010 TRUE);
1011 break;
1012 }
1013
1014 case WM_GETFONT:
1015 {
1016 Ret = (LRESULT)infoPtr->hFont;
1017 break;
1018 }
1019
1020 case WM_ENABLE:
1021 {
1022 infoPtr->Enabled = ((BOOL)wParam != FALSE);
1023 MonthCalReload(infoPtr);
1024 break;
1025 }
1026
1027 case WM_STYLECHANGED:
1028 {
1029 if (wParam == GWL_STYLE)
1030 {
1031 unsigned int OldEnabled = infoPtr->Enabled;
1032 infoPtr->Enabled = !(((LPSTYLESTRUCT)lParam)->styleNew & WS_DISABLED);
1033
1034 if (OldEnabled != infoPtr->Enabled)
1035 {
1036 MonthCalReload(infoPtr);
1037 }
1038 }
1039 break;
1040 }
1041
1042 case WM_CREATE:
1043 {
1044 infoPtr = (MONTHCALWND*) HeapAlloc(GetProcessHeap(),
1045 0,
1046 sizeof(MONTHCALWND));
1047 if (infoPtr == NULL)
1048 {
1049 Ret = (LRESULT)-1;
1050 break;
1051 }
1052
1053 SetWindowLongPtrW(hwnd,
1054 0,
1055 (LONG_PTR)infoPtr);
1056
1057 ZeroMemory(infoPtr,
1058 sizeof(MONTHCALWND));
1059
1060 infoPtr->hSelf = hwnd;
1061 infoPtr->hNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1062 infoPtr->Enabled = !(((LPCREATESTRUCTW)lParam)->style & WS_DISABLED);
1063
1064 MonthCalSetLocalTime(infoPtr,
1065 NULL);
1066
1067 MonthCalReload(infoPtr);
1068 break;
1069 }
1070
1071 case WM_DESTROY:
1072 {
1073 HeapFree(GetProcessHeap(),
1074 0,
1075 infoPtr);
1076 SetWindowLongPtrW(hwnd,
1077 0,
1078 (DWORD_PTR)NULL);
1079 break;
1080 }
1081
1082 default:
1083 {
1084 HandleDefaultMessage:
1085 Ret = DefWindowProcW(hwnd,
1086 uMsg,
1087 wParam,
1088 lParam);
1089 break;
1090 }
1091 }
1092
1093 return Ret;
1094 }
1095
1096 BOOL
RegisterMonthCalControl(IN HINSTANCE hInstance)1097 RegisterMonthCalControl(IN HINSTANCE hInstance)
1098 {
1099 WNDCLASSW wc = {0};
1100
1101 wc.style = CS_DBLCLKS;
1102 wc.lpfnWndProc = MonthCalWndProc;
1103 wc.cbWndExtra = sizeof(PMONTHCALWND);
1104 wc.hInstance = hInstance;
1105 wc.hCursor = LoadCursorW(NULL,
1106 (LPWSTR)IDC_ARROW);
1107 wc.hbrBackground = (HBRUSH)(MONTHCAL_CTRLBG + 1);
1108 wc.lpszClassName = szMonthCalWndClass;
1109
1110 return RegisterClassW(&wc) != 0;
1111 }
1112
1113 VOID
UnregisterMonthCalControl(IN HINSTANCE hInstance)1114 UnregisterMonthCalControl(IN HINSTANCE hInstance)
1115 {
1116 UnregisterClassW(szMonthCalWndClass,
1117 hInstance);
1118 }
1119