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