windatepicker.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:16k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // windatepicker.cpp
  2. //
  3. // Copyright (C) 2005, Chris Laurel <claurel@shatters.net>
  4. //
  5. // Windows front end for Celestia.
  6. //
  7. // This program is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU General Public License
  9. // as published by the Free Software Foundation; either version 2
  10. // of the License, or (at your option) any later version.
  11. #include <windows.h>
  12. #include <commctrl.h>
  13. #include "celutil/basictypes.h"
  14. #include "celengine/astro.h"
  15. #include "celutil/util.h"
  16. #include "celutil/winutil.h"
  17. // DatePicker is a Win32 control for setting the date. It replaces the
  18. // date picker from commctl, adding a number of features appropriate
  19. // for astronomical applications:
  20. //
  21. // - The standard Windows date picker does not permit setting years
  22. //   prior to 1752, the point that the US and UK switched to the 
  23. //   Gregorian calendar. Celestia's date picker allows setting any
  24. //   year from -9999 to 9999.
  25. //
  26. // - Astronomical year conventions are used for dates before the
  27. //   year 1. This means that the year 0 is not omitted, and the year
  28. //   2 BCE is entered as -1.
  29. //
  30. // - The first adoption of the Gregorian calendar was in 1582, when
  31. //   days 5-14 were skipped in the month of October. All dates are
  32. //   based on the initial 1582 reform, even though most countries
  33. //   didn't adopt the Gregorian calendar until many years later.
  34. //
  35. // - No invalid date is permitted, including the skipped days in
  36. //   October 1582.
  37. static char* Months[12] = 
  38. {
  39.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  40.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  41. };
  42. enum DatePickerField
  43. {
  44.     InvalidField = -1,
  45.     DayField     =  0,
  46.     MonthField   =  1,
  47.     YearField    =  2,
  48.     NumFields    =  3,
  49. };
  50. class DatePicker
  51. {
  52. public:
  53.     DatePicker(HWND _hwnd, CREATESTRUCT& cs);
  54.     ~DatePicker();
  55.     
  56.     LRESULT paint(HDC hdc);
  57.     void redraw(HDC hdc);
  58.     LRESULT keyDown(DWORD vkcode, LPARAM lParam);
  59.     LRESULT killFocus(HWND lostFocus);
  60.     LRESULT setFocus(HWND lostFocus);
  61.     LRESULT enable(bool);
  62.     LRESULT leftButtonDown(WORD key, int x, int y);
  63.     LRESULT notify(int id, const NMHDR& nmhdr);
  64.     LRESULT command(WPARAM wParam, LPARAM lParam);
  65.     LRESULT resize(WORD flags, int width, int height);
  66.     bool sendNotify(UINT code);
  67.     bool notifyDateChanged();
  68.     LRESULT setSystemTime(DWORD flag, SYSTEMTIME* sysTime);
  69.     LRESULT getSystemTime(SYSTEMTIME* sysTime);
  70.     LRESULT destroy();
  71. private:
  72.     int getFieldWidth(DatePickerField field, HDC hdc);
  73.     void incrementField();
  74.     void decrementField();
  75. private:
  76.     HWND hwnd;
  77.     HWND parent;
  78.     astro::Date date;
  79.     DatePickerField selectedField;
  80.     char textBuffer[64];
  81.     HFONT hFont;
  82.     DWORD style;
  83.     
  84.     bool haveFocus;
  85.     bool firstDigit;
  86.     RECT fieldRects[NumFields];
  87.     RECT clientRect;
  88. };
  89. DatePicker::DatePicker(HWND _hwnd, CREATESTRUCT& cs) :
  90.     hwnd(_hwnd),
  91.     parent(cs.hwndParent),
  92.     date(1970, 10, 25),
  93.     selectedField(YearField),
  94.     haveFocus(false),
  95.     firstDigit(true)
  96. {
  97.     textBuffer[0] = '';
  98.     hFont = reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
  99.     clientRect.left = 0;
  100.     clientRect.right = 0;
  101.     clientRect.top = 0;
  102.     clientRect.bottom = 0;
  103. }
  104. DatePicker::~DatePicker()
  105. {
  106. }
  107. LRESULT
  108. DatePicker::paint(HDC hdc)
  109. {
  110.     PAINTSTRUCT ps;
  111.     if (!hdc)
  112.     {
  113.         hdc = BeginPaint(hwnd, &ps);
  114.         redraw(hdc);
  115.         EndPaint(hwnd, &ps);
  116.     }
  117.     else
  118.     {
  119.         redraw(hdc);
  120.     }
  121.     return 0;
  122. }
  123. void
  124. DatePicker::redraw(HDC hdc)
  125. {
  126.     RECT rect;
  127.     GetClientRect(hwnd, &rect);
  128.     SelectObject(hdc, hFont);
  129.     SetTextColor(hdc, RGB(0, 0, 0));
  130.     SetBkMode(hdc, TRANSPARENT);
  131.     char dayBuf[32];
  132.     char monthBuf[32];
  133.     char yearBuf[32];
  134.     bind_textdomain_codeset("celestia", CurrentCP());
  135.     sprintf(dayBuf, "%02d", date.day);
  136.     sprintf(monthBuf, "%s", _(Months[date.month - 1]));
  137.     sprintf(yearBuf, "%5d", date.year);
  138.     bind_textdomain_codeset("celestia", "UTF8");
  139.     char* fieldText[NumFields];
  140.     fieldText[DayField] = dayBuf;
  141.     fieldText[MonthField] = monthBuf;
  142.     fieldText[YearField] = yearBuf;
  143.     int right = 2;
  144.     for (unsigned int i = 0; i < NumFields; i++)
  145.     {
  146.         SIZE size;
  147.         GetTextExtentPoint(hdc, fieldText[i], strlen(fieldText[i]), &size);
  148.         int fieldWidth = getFieldWidth(DatePickerField(i), hdc);
  149.         fieldRects[i].left = right;
  150.         fieldRects[i].right = right + fieldWidth;
  151.         fieldRects[i].top = rect.top;
  152.         fieldRects[i].bottom = rect.bottom;
  153.         right = fieldRects[i].right;
  154.         if (i == selectedField && haveFocus)
  155.         {
  156.             RECT r = fieldRects[i];
  157.             r.top = (clientRect.bottom - size.cy) / 2;
  158.             r.bottom = r.top + size.cy + 1;
  159.             HBRUSH hbrush = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
  160.             FillRect(hdc, &r, hbrush);
  161.             DeleteObject(hbrush);
  162. SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
  163.         }
  164. else
  165. {
  166. SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
  167. }
  168.         DrawText(hdc, fieldText[i], strlen(fieldText[i]), &fieldRects[i], DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
  169.     }
  170. }
  171. static bool isLeapYear(unsigned int year)
  172. {
  173.     if (year > 1582)
  174.     {
  175.         return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
  176.     }
  177.     else
  178.     {
  179.         return year % 4 == 0;
  180.     }
  181. }
  182. static unsigned int daysInMonth(unsigned int month, unsigned int year)
  183. {
  184.     static unsigned int daysPerMonth[12] =
  185.         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  186.     if (month == 2)
  187.         return isLeapYear(year) ? 29 : 28;
  188.     else
  189.         return daysPerMonth[month - 1];
  190. }
  191. static void clampToValidDate(astro::Date& date)
  192. {
  193.     int days = (int) daysInMonth(date.month, date.year);
  194.     if (date.day > days)
  195.         date.day = days;
  196.     // 10 days skipped in Gregorian calendar reform
  197.     if (date.year == 1582 && date.month == 10 && date.day > 4 && date.day < 15)
  198.     {
  199.         if (date.day < 10)
  200.             date.day = 4;
  201.         else
  202.             date.day = 15;
  203.     }
  204. }
  205. LRESULT
  206. DatePicker::keyDown(DWORD vkcode, LPARAM flags)
  207. {
  208.     if (!haveFocus)
  209.         return 0;
  210.     if (vkcode >= '0' && vkcode <= '9')
  211.     {
  212.         unsigned int digit = vkcode - '0';
  213.         if (firstDigit)
  214.         {
  215.             switch (selectedField)
  216.             {
  217.             case DayField:
  218.                 if (digit != 0)
  219.                     date.day = digit;
  220.                 break;
  221.             case MonthField:
  222.                 if (digit != 0)
  223.                     date.month = digit;
  224.                 break;
  225.             case YearField:
  226.                 if (digit != 0)
  227.                     date.year = digit;
  228.                 break;
  229.             }
  230.             firstDigit = false;
  231.         }
  232.         else
  233.         {
  234.             switch (selectedField)
  235.             {
  236.             case DayField:
  237.                 {
  238.                     unsigned int day = date.day * 10 + digit;
  239.                     if (day >= 10)
  240.                         firstDigit = true;
  241.                     if (day > daysInMonth(date.month, date.year))
  242.                         day = 1;
  243.                     date.day = day;
  244.                 }
  245.                 break;
  246.             
  247.             case MonthField:
  248.                 {
  249.                     unsigned int month = date.month * 10 + digit;
  250.                     if (month > 1)
  251.                         firstDigit = true;
  252.                     if (month > 12)
  253.                         month = 1;
  254.                     date.month = month;
  255.                 }
  256.                 break;
  257.                 
  258.             case YearField:
  259.                 {
  260.                     unsigned int year = date.year * 10 + digit;
  261.                     if (year >= 1000)
  262.                         firstDigit = true;
  263.                     if (year <= 9999)
  264.                         date.year = year;
  265.                 }
  266.                 break;
  267.             }
  268.         }
  269.         clampToValidDate(date);
  270.         notifyDateChanged();
  271.     }
  272.     else if (vkcode == VK_SUBTRACT || vkcode == VK_OEM_MINUS)
  273.     {
  274.         if (selectedField == YearField)
  275.         {
  276.             date.year = -date.year;
  277.             clampToValidDate(date);
  278.             notifyDateChanged();
  279.         }
  280.     }
  281.     else
  282.     {
  283.         firstDigit = true;
  284.         switch (vkcode)
  285.         {
  286.         case VK_LEFT:
  287.             if ((int) selectedField == 0)
  288.                 selectedField = DatePickerField((int) NumFields - 1);
  289.             else 
  290.                 selectedField = DatePickerField((int) selectedField - 1);
  291.             break;
  292.         case VK_RIGHT:
  293.             if ((int) selectedField == (int) NumFields - 1)
  294.                 selectedField = DatePickerField(0);
  295.             else
  296.                 selectedField = DatePickerField((int) selectedField + 1);
  297.             break;
  298.             
  299.         case VK_UP:
  300.             incrementField();
  301.             notifyDateChanged();
  302.             break;
  303.         case VK_DOWN:
  304.             decrementField();
  305.             notifyDateChanged();
  306.             break;
  307.             
  308.         default:
  309.             break;
  310.         }
  311.     }
  312.     InvalidateRect(hwnd, NULL, TRUE);
  313.     return 0;
  314. }
  315. LRESULT
  316. DatePicker::leftButtonDown(WORD key, int x, int y)
  317. {
  318.     POINT pt;
  319.     pt.x = x;
  320.     pt.y = y;
  321.     if (PtInRect(&fieldRects[DayField], pt))
  322.         selectedField = DayField;
  323.     else if (PtInRect(&fieldRects[MonthField], pt))
  324.         selectedField = MonthField;
  325.     else if (PtInRect(&fieldRects[YearField], pt))
  326.         selectedField = YearField;
  327.     InvalidateRect(hwnd, NULL, TRUE);
  328. ::SetFocus(hwnd); // note that this is the Win32 API function, not the class method
  329.     return 0;
  330. }
  331. LRESULT
  332. DatePicker::setFocus(HWND lostFocus)
  333. {
  334.     if (!haveFocus)
  335.     {
  336.         sendNotify(NM_SETFOCUS);
  337.         haveFocus = true;
  338.     }
  339.     firstDigit = true;
  340.     InvalidateRect(hwnd, NULL, TRUE);
  341.     
  342.     return 0;
  343. }
  344. LRESULT
  345. DatePicker::killFocus(HWND lostFocus)
  346. {
  347.     if (haveFocus)
  348.     {
  349.         sendNotify(NM_KILLFOCUS);
  350.         haveFocus = false;
  351.     }
  352.     InvalidateRect(hwnd, NULL, TRUE);
  353.     return 0;
  354. }
  355. LRESULT
  356. DatePicker::enable(bool b)
  357. {
  358.     if (!b)
  359.         style &= ~WS_DISABLED;
  360.     else
  361.         style |= WS_DISABLED;
  362.     return 0;
  363. }
  364. LRESULT
  365. DatePicker::notify(int id, const NMHDR& nmhdr)
  366. {
  367.     return 0;
  368. }
  369. LRESULT
  370. DatePicker::command(WPARAM, LPARAM)
  371. {
  372.     return 0;
  373. }
  374. bool
  375. DatePicker::sendNotify(UINT code)
  376. {
  377.     NMHDR nmhdr;
  378.     ZeroMemory(&nmhdr, sizeof(nmhdr));
  379.     nmhdr.hwndFrom = hwnd;
  380.     nmhdr.idFrom   = GetWindowLongPtr(hwnd, GWLP_ID);
  381.     nmhdr.code     = code;
  382.     return SendMessage(parent, WM_NOTIFY,
  383.                        nmhdr.idFrom,
  384.                        reinterpret_cast<LPARAM>(&nmhdr)) ? true : false;
  385. }
  386. bool
  387. DatePicker::notifyDateChanged()
  388. {
  389.     NMDATETIMECHANGE change;
  390.     ZeroMemory(&change, sizeof(change));
  391.     change.nmhdr.hwndFrom = hwnd;
  392.     change.nmhdr.idFrom   = GetWindowLongPtr(hwnd, GWLP_ID);
  393.     change.nmhdr.code     = DTN_DATETIMECHANGE;
  394.     change.st.wYear       = date.year;
  395.     change.st.wMonth      = date.month;
  396.     change.st.wDay        = date.day;
  397.     return SendMessage(parent, WM_NOTIFY,
  398.                        change.nmhdr.idFrom,
  399.                        reinterpret_cast<LPARAM>(&change)) ? true : false;
  400. }
  401. int
  402. DatePicker::getFieldWidth(DatePickerField field, HDC hdc)
  403. {
  404.     char* maxWidthText = "";
  405.     switch (field)
  406.     {
  407.     case YearField:
  408.         maxWidthText = "-2222 ";
  409.         break;
  410.     case MonthField:
  411.         maxWidthText = " Oct ";
  412.         break;
  413.     case DayField:
  414.         maxWidthText = "22 ";
  415.         break;
  416.     }
  417.     SIZE size;
  418.     GetTextExtentPoint32(hdc, maxWidthText, strlen(maxWidthText), &size);
  419.     return size.cx;
  420. }
  421. void
  422. DatePicker::incrementField()
  423. {
  424.     switch (selectedField)
  425.     {
  426.     case YearField:
  427.         date.year++;
  428.         clampToValidDate(date);
  429.         break;
  430.     case MonthField:
  431.         date.month++;
  432.         if (date.month > 12)
  433.             date.month = 1;
  434.         clampToValidDate(date);
  435.         break;
  436.     case DayField:
  437.         date.day++;
  438.         if (date.day > (int) daysInMonth(date.month, date.year))
  439.             date.day = 1;
  440.         // Skip 10 days deleted in Gregorian calendar reform
  441.         if (date.year == 1582 && date.month == 10 && date.day == 5)
  442.             date.day = 15;
  443.         break;
  444.     }
  445. }
  446. void
  447. DatePicker::decrementField()
  448. {
  449.     switch (selectedField)
  450.     {
  451.     case YearField:
  452.         date.year--;
  453.         clampToValidDate(date);
  454.         break;
  455.     case MonthField:
  456.         date.month--;
  457.         if (date.month < 1)
  458.             date.month = 12;
  459.         clampToValidDate(date);
  460.         break;
  461.     case DayField:
  462.         date.day--;
  463.         if (date.day < 1)
  464.             date.day = daysInMonth(date.month, date.year);
  465.         // Skip 10 days deleted in Gregorian calendar reform
  466.         if (date.year == 1582 && date.month == 10 && date.day == 14)
  467.             date.day = 4;
  468.         break;
  469.     }
  470. }
  471. LRESULT
  472. DatePicker::destroy()
  473. {
  474.     return 0;
  475. }
  476. LRESULT
  477. DatePicker::resize(WORD flags, int width, int height)
  478. {
  479.     clientRect.bottom = height;
  480.     clientRect.right = width;
  481.     InvalidateRect(hwnd, NULL, FALSE);
  482.     return 0;
  483. }
  484. LRESULT
  485. DatePicker::setSystemTime(DWORD flag, SYSTEMTIME* sysTime)
  486. {
  487.     date.year = (int16) sysTime->wYear;
  488.     date.month = sysTime->wMonth;
  489.     date.day = sysTime->wDay;
  490.     InvalidateRect(hwnd, NULL, TRUE);
  491.     return 0;
  492. }
  493. LRESULT
  494. DatePicker::getSystemTime(SYSTEMTIME* sysTime)
  495. {
  496.     if (sysTime != NULL)
  497.     {
  498.         sysTime->wYear = date.year;
  499.         sysTime->wMonth = date.month;
  500.         sysTime->wDay = date.day;
  501.     }
  502.     
  503.     return GDT_VALID;
  504. }
  505. static LRESULT
  506. DatePickerNCCreate(HWND hwnd, CREATESTRUCT& cs)
  507. {
  508.     DWORD exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
  509.     
  510.     exStyle |= WS_EX_CLIENTEDGE;
  511.     SetWindowLong(hwnd, GWL_EXSTYLE, exStyle);
  512.     return DefWindowProc(hwnd, WM_NCCREATE, 0, reinterpret_cast<LPARAM>(&cs));
  513. }
  514. static LRESULT
  515. DatePickerCreate(HWND hwnd, CREATESTRUCT& cs)
  516. {
  517.     DatePicker* dp = new DatePicker(hwnd, cs);
  518.     if (dp == NULL)
  519.         return -1;
  520.     SetWindowLongPtr(hwnd, 0, reinterpret_cast<DWORD_PTR>(dp));
  521.     return 0;
  522. }
  523. static LRESULT WINAPI
  524. DatePickerProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  525. {
  526.     DatePicker* dp = reinterpret_cast<DatePicker*>(GetWindowLongPtr(hwnd, 0));
  527.     if (!dp && (uMsg != WM_CREATE) && (uMsg != WM_NCCREATE))
  528.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  529.     switch (uMsg)
  530.     {
  531.     case DTM_SETSYSTEMTIME:
  532.         return dp->setSystemTime(wParam, reinterpret_cast<SYSTEMTIME*>(lParam));
  533.         break;
  534.         
  535.     case DTM_GETSYSTEMTIME:
  536.         return dp->getSystemTime(reinterpret_cast<SYSTEMTIME*>(lParam));
  537.         break;
  538.     case WM_NOTIFY:
  539.         return dp->notify((int) wParam, *reinterpret_cast<NMHDR*>(lParam));
  540.         break;
  541.     case WM_ENABLE:
  542.         return dp->enable(wParam != 0 ? true : false);
  543.         break;
  544.         //case WM_ERASEBKGND:
  545.         //break;
  546.     case WM_PAINT:
  547.         return dp->paint(reinterpret_cast<HDC>(wParam));
  548.         break;
  549.     case WM_GETDLGCODE:
  550.         return DLGC_WANTARROWS | DLGC_WANTCHARS;
  551.     case WM_KEYDOWN:
  552.         return dp->keyDown(wParam, lParam);
  553.         break;
  554.     case WM_KILLFOCUS:
  555.         return dp->killFocus(reinterpret_cast<HWND>(wParam));
  556.         break;
  557.     case WM_SETFOCUS:
  558.         return dp->setFocus(reinterpret_cast<HWND>(wParam));
  559.         break;
  560.     case WM_NCCREATE:
  561.         return DatePickerNCCreate(hwnd, *reinterpret_cast<CREATESTRUCT*>(lParam));
  562.         break;
  563.     case WM_SIZE:
  564.         return dp->resize(wParam, LOWORD(lParam), HIWORD(lParam));
  565.         break;
  566.     case WM_LBUTTONDOWN:
  567.         return dp->leftButtonDown((WORD) wParam, LOWORD(lParam), HIWORD(lParam));
  568.         break;
  569.     case WM_LBUTTONUP:
  570.         return 0;
  571.         break;
  572.     case WM_CREATE:
  573.         return DatePickerCreate(hwnd, *reinterpret_cast<CREATESTRUCT*>(lParam));
  574.         break;
  575.     case WM_DESTROY:
  576.         return dp->destroy();
  577.         break;
  578.     case WM_COMMAND:
  579.         return dp->command(wParam, lParam);
  580.         break;
  581.     default:
  582.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  583.     }
  584. }
  585. void
  586. RegisterDatePicker()
  587. {
  588.     WNDCLASS wc;
  589.     ZeroMemory(&wc, sizeof(wc));
  590.     wc.style           = CS_GLOBALCLASS;
  591.     wc.lpfnWndProc     = DatePickerProc;
  592.     wc.cbClsExtra      = 0;
  593.     wc.cbWndExtra      = sizeof(DatePicker*);
  594.     wc.hCursor         = LoadCursor(0, IDC_ARROW);
  595.     wc.hbrBackground   = (HBRUSH) (COLOR_WINDOW + 1);
  596.     wc.lpszClassName   = "CelestiaDatePicker";
  597.     RegisterClass(&wc);
  598. }