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 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 */ 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 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 113 MonthCalWeekInMonth(IN WORD Day, 114 IN WORD DayOfWeek) 115 { 116 return (Day - DayOfWeek + 5) / 7; 117 } 118 119 static WORD 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1114 UnregisterMonthCalControl(IN HINSTANCE hInstance) 1115 { 1116 UnregisterClassW(szMonthCalWndClass, 1117 hInstance); 1118 } 1119