DateTimeEditCtrl.cpp
上传用户:yd19691116
上传日期:2013-02-12
资源大小:43k
文件大小:38k
源码类别:

Windows编程

开发平台:

Visual C++

  1. ////////////////////////////////////////////////////////////////////////////
  2. // File: DateTimeEditCtrl.cpp
  3. // Version: 3
  4. // Created: 17-Jan-2003
  5. //
  6. // Author: Paul S. Vickery
  7. // E-mail: paul@vickeryhome.freeserve.co.uk
  8. //
  9. // Class to provide an editable Date picker control. Based on CDateTimeCtrl.
  10. //
  11. // You are free to use or modify this code, with no restrictions, other than
  12. // you continue to acknowledge me as the original author in this source code,
  13. // or any code derived from it.
  14. //
  15. // If you use this code, or use it as a base for your own code, it would be 
  16. // nice to hear from you simply so I know it's not been a waste of time!
  17. //
  18. // Copyright (c) 2003 Paul S. Vickery
  19. //
  20. ////////////////////////////////////////////////////////////////////////////
  21. // Version History:
  22. //
  23. // Version 3 - 17-Jan-2003
  24. // =======================
  25. // - Fixed bug where a call to Get/SetWindowText, SetFont, or any function 
  26. //   which resulted in a sending of a DTM_XXX message caused an assertion the 
  27. //   first time round, if called from the OnInitDialog of a parent dialog, or 
  28. //   equivalent.
  29. // 
  30. // Version 2 - 07-Jan-2003
  31. // =======================
  32. // - Edit control notifications (eg EN_CHANGE) now get sent to date control's 
  33. //   parent window
  34. // - Added features as suggested by Marc Clifton on CodeProject:
  35. //   - Added function to validate a date string (IsValidDate)
  36. //   - Added function to only allow valid chars to be typed into the edit control.
  37. //     Also a function to set the valid characters. The default valid characters 
  38. //     are 0-9 and the current user's locale's date separator. If the user changes 
  39. //     the locale settings then the control will automatically pick this up and 
  40. //     use the new separator.
  41. //   - Added ability to use up/down keys to edit portions of the date text
  42. // 
  43. // Version 1 - 03-Jan-2003
  44. // =======================
  45. // Initial version
  46. // 
  47. ////////////////////////////////////////////////////////////////////////////
  48. // PLEASE LEAVE THIS HEADER INTACT
  49. ////////////////////////////////////////////////////////////////////////////
  50. #include "stdafx.h"
  51. #include "DateTimeEditCtrl.h"
  52. #include <string>
  53. using namespace std;
  54. #ifdef _DEBUG
  55. #define new DEBUG_NEW
  56. #undef THIS_FILE
  57. static char THIS_FILE[] = __FILE__;
  58. #endif
  59. #define ID_EDIT 1
  60. #define ID_BUTTON 2
  61. #define ID_CALENDAR 3
  62. #define ID_DROPWND 4
  63. #define CALENDAR_AS_POPUP 1
  64. /////////////////////////////////////////////////////////////////////////////
  65. // CDateTimeEditCtrlButton
  66. BEGIN_MESSAGE_MAP(CDateTimeEditCtrlButton, CButton)
  67. //{{AFX_MSG_MAP(CDateTimeEditCtrlButton)
  68. ON_WM_LBUTTONDOWN()
  69. ON_WM_LBUTTONUP()
  70. ON_WM_KEYDOWN()
  71. //}}AFX_MSG_MAP
  72. END_MESSAGE_MAP()
  73. /////////////////////////////////////////////////////////////////////////////
  74. // CDateTimeEditCtrlButton message handlers
  75. void CDateTimeEditCtrlButton::OnLButtonUp(UINT nFlags, CPoint point) 
  76. {
  77.   CButton::OnLButtonUp(nFlags, point);
  78.   // set the focus back to the parent, and stop it looking like the default button
  79.   ModifyStyle(BS_DEFPUSHBUTTON, 0);
  80.   if (! m_bNonEditable && m_bRestoreFocus && m_pWndLastFocus != NULL && ::IsWindow(m_pWndLastFocus->m_hWnd))
  81.     m_pWndLastFocus->SetFocus();
  82.   else
  83.     GetParent()->SetFocus();
  84. }
  85. void CDateTimeEditCtrlButton::OnLButtonDown(UINT nFlags, CPoint point) 
  86. {
  87.   if (! m_bNonEditable && m_bRestoreFocus)
  88.     m_pWndLastFocus = CWnd::GetFocus();
  89.   else
  90.     m_pWndLastFocus = NULL;
  91.   CButton::OnLButtonDown(nFlags, point);
  92.   // stop it looking like the default button
  93.   ModifyStyle(BS_DEFPUSHBUTTON, 0);
  94. }
  95. void CDateTimeEditCtrlButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
  96. {
  97.   CButton::OnKeyDown(nChar, nRepCnt, nFlags);
  98.   if (nChar == VK_F4)
  99.     // hide or show cal ctrl, by simulating a button click
  100.     GetParent()->PostMessage(WM_COMMAND, MAKEWPARAM(ID_BUTTON, BN_CLICKED), (LPARAM)GetNextWindow()->GetSafeHwnd());
  101. }
  102. BOOL CDateTimeEditCtrlButton::PreTranslateMessage(MSG* pMsg) 
  103. {
  104.   if (pMsg->message == WM_KEYDOWN && 
  105. (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN))
  106.   {
  107.     // destroy calendar
  108.     if (GetParent()->SendMessage(DTCEM_DESTROY_CALENDAR, (pMsg->wParam == VK_ESCAPE)))
  109.       return TRUE;
  110.   }
  111.   return CButton::PreTranslateMessage(pMsg);
  112. }
  113. /////////////////////////////////////////////////////////////////////////////
  114. // CDateTimeEditCtrlEditCtrl
  115. BEGIN_MESSAGE_MAP(CDateTimeEditCtrlEditCtrl, CEdit)
  116. //{{AFX_MSG_MAP(CDateTimeEditCtrlEditCtrl)
  117. ON_WM_GETDLGCODE()
  118. ON_WM_SETCURSOR()
  119. ON_WM_CTLCOLOR_REFLECT()
  120. ON_WM_KEYDOWN()
  121. ON_WM_CHAR()
  122. ON_WM_SETTINGCHANGE()
  123. //}}AFX_MSG_MAP
  124. END_MESSAGE_MAP()
  125. /////////////////////////////////////////////////////////////////////////////
  126. // CDateTimeEditCtrlEditCtrl message handlers
  127. static TCHAR s_chDateSep = '/';
  128. static TCHAR s_szDateFormat[81] = { '' };
  129. CDateTimeEditCtrlEditCtrl::CDateTimeEditCtrlEditCtrl()
  130. {
  131.   m_bNonEditable = FALSE;
  132.   m_bValidCharsOnly = FALSE;
  133.   m_bAllowUpDownKeys = TRUE;
  134.   int nLen = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, &s_chDateSep, 0);
  135.   if (nLen != 0)
  136.     ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, &s_chDateSep, nLen);
  137.   nLen = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, s_szDateFormat, 0);
  138.   if (nLen != 0)
  139.     ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, s_szDateFormat, nLen);
  140. }
  141. UINT CDateTimeEditCtrlEditCtrl::OnGetDlgCode() 
  142. {
  143.   if (m_bNonEditable)
  144.     return DLGC_STATIC;
  145.   return CEdit::OnGetDlgCode();
  146. }
  147. BOOL CDateTimeEditCtrlEditCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
  148. {
  149.   // if we are non-editable, we want the cursor to look like an arrow
  150.   // not like an I-bar
  151.   if (m_bNonEditable && nHitTest == HTCLIENT)
  152.   {
  153.     AfxGetApp()->LoadStandardCursor(IDC_ARROW);
  154.     return TRUE;
  155.   }
  156.   return CEdit::OnSetCursor(pWnd, nHitTest, message);
  157. }
  158. HBRUSH CDateTimeEditCtrlEditCtrl::CtlColor(CDC* pDC, UINT nCtlColor) 
  159. {
  160.   // if we are non-editable, we want the edit to be readonly, but look
  161.   // like editable (ie with white (standard 'window') background)
  162.   if (m_bNonEditable)
  163.   {
  164.     pDC->SetBkColor(GetSysColor(COLOR_WINDOW));
  165.     return GetSysColorBrush(COLOR_WINDOW);
  166.   }
  167.   return NULL;
  168. }
  169. BOOL CDateTimeEditCtrlEditCtrl::PreTranslateMessage(MSG* pMsg) 
  170. {
  171.   if (pMsg->message == WM_KEYDOWN && 
  172. (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN))
  173.   {
  174.     // destroy calendar
  175.     if (GetParent()->SendMessage(DTCEM_DESTROY_CALENDAR, (pMsg->wParam == VK_ESCAPE)))
  176.       return TRUE;
  177.   }
  178.   return CEdit::PreTranslateMessage(pMsg);
  179. }
  180. int ReverseFindOneOf(const CString& sString, LPCTSTR lpszCharSet)
  181. {
  182.   string s = sString;
  183.   return s.find_last_of(lpszCharSet);
  184. }
  185. int FindOneOf(const CString& sString, LPCTSTR lpszCharSet, int nStart)
  186. {
  187.   string s = sString;
  188.   return s.find_first_of(lpszCharSet, nStart);
  189. }
  190. void CDateTimeEditCtrlEditCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
  191. {
  192.   if (m_bAllowUpDownKeys && nChar == VK_UP || nChar == VK_DOWN)
  193.   {
  194.     BOOL bInc = (nChar == VK_UP);
  195.     // increment part of date in which cursor is positioned
  196.     int nStart, nEnd;
  197.     GetSel(nStart, nEnd);
  198.     CString sText;
  199.     GetWindowText(sText);
  200.     CString sSeps = s_chDateSep;
  201.     // find portion of text we're in, and select
  202.     // between two separators
  203.     if (! m_sValidChars.IsEmpty())
  204.     {
  205.       sSeps = m_sValidChars;
  206.       for (char chRemove = '0'; chRemove <= '9'; chRemove++)
  207. sSeps.Remove(chRemove);
  208.       // sSeps just contains the separators now
  209.     }
  210.     int nSep2 = FindOneOf(sText, sSeps, nEnd);
  211.     CString sStart = sText;
  212.     if (nSep2 >= 0)
  213.       sStart = sText.Left(nSep2);
  214.     int nPos = ReverseFindOneOf(sStart, sSeps);
  215.     BOOL bLeftPos = TRUE;
  216.     BOOL bMidPos = FALSE;
  217.     int nSep1 = 0;
  218.     if (nPos >= 0)
  219.     {
  220.       // mid position
  221.       if (nSep2 >= 0)
  222.       {
  223. bLeftPos = FALSE;
  224. bMidPos = TRUE;
  225.       }
  226.       nSep1 = nPos + 1;
  227.     }
  228.     if (nSep2 < 0)
  229.     {
  230.       // right-most portion
  231.       nSep2 = sText.GetLength();
  232.       bLeftPos = FALSE;
  233.     }
  234.     CString sDateFormat = s_szDateFormat;
  235.     BOOL bDay = FALSE;
  236.     BOOL bMonth = FALSE;
  237.     BOOL bYear = FALSE;
  238.     CString sPortion;
  239.     int nFirstSep = sDateFormat.FindOneOf(sSeps);
  240.     // find out which part of the format we are in
  241.     if (bLeftPos)
  242.     {
  243.       // get bit before first sep
  244.       if (nFirstSep > 0)
  245. sPortion = sDateFormat.Left(nFirstSep);
  246.     }
  247.     else if (bMidPos)
  248.     {
  249.       // get bit after first sep, and before last
  250.       if (nFirstSep > 0)
  251.       {
  252. nFirstSep++;
  253. int nPos = FindOneOf(sDateFormat, sSeps, nFirstSep);
  254. if (nPos >= nFirstSep)
  255.   sPortion = sDateFormat.Mid(nFirstSep, nPos - nFirstSep);
  256.       }
  257.     }
  258.     else
  259.     {
  260.       // get bit after last sep
  261.       int nPos = ReverseFindOneOf(sDateFormat, sSeps);
  262.       if (nPos >= 0)
  263. sPortion = sDateFormat.Right(sDateFormat.GetLength() - (nPos+1));
  264.     }
  265.     // select new portion
  266.     SetSel(nSep1, nSep2);
  267.     CString sVal = sStart.GetLength() > nSep1 ? sStart.Mid(nSep1) : sStart;
  268.     LPSTR lpszFormat = "%02d";
  269.     int nVal = atoi(sVal);
  270.     int nMin = 1;
  271.     int nMax = 12;
  272.     // make sure the number is within bounds depending on portion
  273.     if (! sPortion.IsEmpty())
  274.     {
  275.       int nLen = sPortion.GetLength();
  276.       if (nLen != 2)
  277. lpszFormat = "%0d";
  278.       switch (sPortion.GetAt(0))
  279.       {
  280.       case 'd': // day
  281.       case 'D': // day
  282. nMax = 31;
  283. break;
  284.       case 'M': // month
  285.       case 'm': // month
  286. nMax = 12;
  287. break;
  288.       case 'y': // year
  289.       case 'Y': // year
  290. {
  291.   nMin = 0;
  292.   if (nLen == 1)
  293.     nMax = 9;
  294.   else if (nLen == 2)
  295.     nMax = 99;
  296.   else if (nLen == 4)
  297.   {
  298.     lpszFormat = "%04d";
  299.     nMax = 9999;
  300.   }
  301. }
  302. break;
  303.       };
  304.     }
  305.     if (bInc)
  306.       nVal++;
  307.     else
  308.       nVal--;
  309.     // wrap values
  310.     if (nVal < nMin)
  311.       nVal = nMax;
  312.     else if (nVal > nMax)
  313.       nVal = nMin;
  314.     sVal.Format(lpszFormat, nVal);
  315.     // replace and re-select new portion
  316.     ReplaceSel(sVal);
  317.     nSep2 = nSep1 + sVal.GetLength();
  318.     SetSel(nSep1, nSep2);
  319.     SendDateTimeChange();
  320.     return;
  321.   }
  322.   CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
  323.   if (nChar == VK_F4)
  324.     // hide or show cal ctrl, by simulating a button click
  325.     GetParent()->PostMessage(WM_COMMAND, MAKEWPARAM(ID_BUTTON, BN_CLICKED), (LPARAM)GetNextWindow()->GetSafeHwnd());
  326. }
  327. void CDateTimeEditCtrlEditCtrl::SendDateTimeChange()
  328. {
  329.   CDateTimeEditCtrl* pCtrl = DYNAMIC_DOWNCAST(CDateTimeEditCtrl, GetParent());
  330.   if (pCtrl != NULL)
  331.   {
  332.     CWnd* pParent = pCtrl->GetParent();
  333.     if (pParent != NULL)
  334.     {
  335.       COleDateTime date;
  336.       CString sText;
  337.       GetWindowText(sText);
  338.       date.ParseDateTime(sText);
  339.       SYSTEMTIME st = { '' };
  340.       date.GetAsSystemTime(st);
  341.       // tell parent about it (DTN_DATETIMECHANGE)
  342.       NMDATETIMECHANGE nmdtc;
  343.       nmdtc.nmhdr.code = DTN_DATETIMECHANGE;
  344.       nmdtc.nmhdr.hwndFrom = pCtrl->GetSafeHwnd();
  345.       nmdtc.nmhdr.idFrom = pCtrl->GetDlgCtrlID();
  346.       nmdtc.dwFlags = GDT_VALID;
  347.       nmdtc.st = st;
  348.       pParent->SendMessage(WM_NOTIFY, (WPARAM)nmdtc.nmhdr.idFrom, (LPARAM)&nmdtc);
  349.     }
  350.   }
  351. }
  352. // set whether the user can only enter chars that are valid
  353. void CDateTimeEditCtrlEditCtrl::SetValidCharsOnly(BOOL bValidCharsOnly/*=TRUE*/)
  354. {
  355.   m_bValidCharsOnly = bValidCharsOnly;
  356. }
  357. // returns whether the user can only enter valid chars into the edit control
  358. BOOL CDateTimeEditCtrlEditCtrl::GetValidCharsOnly()
  359. {
  360.   return m_bValidCharsOnly;
  361. }
  362. // sets the chars that are valid for the user to type into the edit control
  363. // if NULL is specified, then the default chars are used (0-9 and current 
  364. // user's locale's date separator). If the user changes the locale settings 
  365. // then the control will automatically pick this up and use the new separator.
  366. void CDateTimeEditCtrlEditCtrl::SetValidChars(LPCTSTR lpszValidChars/*=NULL*/)
  367. {
  368.   m_sValidChars = lpszValidChars;
  369. }
  370. // returns the chars that are valid for the user to type into the edit control
  371. CString CDateTimeEditCtrlEditCtrl::GetValidChars()
  372. {
  373.   return m_sValidChars;
  374. }
  375. void CDateTimeEditCtrlEditCtrl::SetAllowUpDownKeys(BOOL bAllow/*=TRUE*/)
  376. {
  377.   m_bAllowUpDownKeys = bAllow;
  378. }
  379. BOOL CDateTimeEditCtrlEditCtrl::GetAllowUpDownKeys()
  380. {
  381.   return m_bAllowUpDownKeys;
  382. }
  383. void CDateTimeEditCtrlEditCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
  384. {
  385.   if (m_bValidCharsOnly && nChar >= 0x20)
  386.   {
  387.     BOOL bValid = TRUE;
  388.     if (m_sValidChars.IsEmpty())
  389.     {
  390.       // only allow numeric or date separator
  391.       if (nChar >= 0x20 && (nChar < '0' || nChar > '9') && nChar != (UINT)s_chDateSep)
  392. bValid = FALSE;
  393.     }
  394.     else
  395.     {
  396.       // see if the char is in the valid chars list
  397.       if (m_sValidChars.Find((TCHAR)nChar) == -1)
  398. bValid = FALSE;
  399.     }
  400.     if (! bValid)
  401.     {
  402.       // don't allow it
  403.       MessageBeep(MB_ICONEXCLAMATION);
  404.       return;
  405.     }
  406.   }
  407.   CEdit::OnChar(nChar, nRepCnt, nFlags);
  408.   // tell the parent dialog of the change
  409.   SendDateTimeChange();
  410. }
  411. void CDateTimeEditCtrlEditCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection) 
  412. {
  413.   if (lpszSection != NULL && stricmp(lpszSection, "intl") == 0)
  414.   {
  415.     int nLen = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, &s_chDateSep, 0);
  416.     if (nLen != 0)
  417.       ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, &s_chDateSep, nLen);
  418.   }
  419.   CEdit::OnSettingChange(uFlags, lpszSection);
  420. }
  421. /////////////////////////////////////////////////////////////////////////////
  422. // CDateTimeEditCtrlMonthCalCtrl
  423. CDateTimeEditCtrlMonthCalCtrl::CDateTimeEditCtrlMonthCalCtrl()
  424. {
  425. }
  426. CDateTimeEditCtrlMonthCalCtrl::~CDateTimeEditCtrlMonthCalCtrl()
  427. {
  428. }
  429. BEGIN_MESSAGE_MAP(CDateTimeEditCtrlMonthCalCtrl, CMonthCalCtrl)
  430. //{{AFX_MSG_MAP(CDateTimeEditCtrlMonthCalCtrl)
  431. ON_WM_CREATE()
  432. ON_WM_KEYDOWN()
  433. //}}AFX_MSG_MAP
  434. END_MESSAGE_MAP()
  435. /////////////////////////////////////////////////////////////////////////////
  436. // CDateTimeEditCtrlMonthCalCtrl message handlers
  437. LRESULT CDateTimeEditCtrlMonthCalCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  438. {
  439.   if (message == WM_LBUTTONDOWN || 
  440.       message == WM_MBUTTONDOWN || 
  441.       message == WM_RBUTTONDOWN)
  442.   {
  443.     // Is mouse within control
  444.     CPoint point(lParam);
  445.     CRect rcClient;
  446.     GetClientRect(rcClient);
  447.     if (! rcClient.PtInRect(point))
  448.     {
  449.       ReleaseCapture();
  450.       GetOwner()->PostMessage(DTCEM_DESTROY_CALENDAR);
  451.     }
  452.     else
  453.       SetCapture();
  454.   }
  455.   else if (message == WM_LBUTTONUP || 
  456.    message == WM_MBUTTONUP || 
  457.    message == WM_RBUTTONUP)
  458.   {
  459.     CMonthCalCtrl::WindowProc(message, wParam, lParam);
  460.     // we seem to lose capture on Xbuttonup, which stops us catching
  461.     // out-of-rect messages after changing, for instance, the month
  462.     // so we need to re-capture messages. However, if the Xbuttondown
  463.     // was out-of-rect, then we won't exist by this point, so test validity
  464.     if (::IsWindow(m_hWnd))
  465.       SetCapture();
  466.     return 0;
  467.   }
  468.   else if (message == WM_PARENTNOTIFY)
  469.   {
  470.     if (LOWORD(wParam) == WM_DESTROY)
  471.       // just destroyed the 'year' edit/updown, but this makes us lose capture
  472.       SetCapture();
  473.   }
  474.   return CMonthCalCtrl::WindowProc(message, wParam, lParam);
  475. }
  476. /////////////////////////////////////////////////////////////////////////////
  477. // CDateTimeEditCtrlCalendarWnd
  478. CDateTimeEditCtrlCalendarWnd::CDateTimeEditCtrlCalendarWnd(CWnd* pComboParent, DWORD dwMCStyle/*=0*/)
  479. {
  480.   m_pComboParent = pComboParent;
  481.   m_dwMCStyle = dwMCStyle;
  482.   m_pCalendar = NULL;
  483. }
  484. CDateTimeEditCtrlCalendarWnd::~CDateTimeEditCtrlCalendarWnd()
  485. {
  486.   delete m_pCalendar;
  487. }
  488. BEGIN_MESSAGE_MAP(CDateTimeEditCtrlCalendarWnd, CWnd)
  489. //{{AFX_MSG_MAP(CDateTimeEditCtrlCalendarWnd)
  490. ON_WM_CREATE()
  491. ON_WM_SIZE()
  492. ON_WM_ACTIVATEAPP()
  493. //}}AFX_MSG_MAP
  494. END_MESSAGE_MAP()
  495. /////////////////////////////////////////////////////////////////////////////
  496. // CDateTimeEditCtrlCalendarWnd message handlers
  497. BOOL CDateTimeEditCtrlCalendarWnd::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
  498. {
  499.   return CWnd::Create(0, 0, dwStyle, rect, pParentWnd, nID, pContext);
  500. }
  501. int CDateTimeEditCtrlCalendarWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  502. {
  503.   if (CWnd::OnCreate(lpCreateStruct) == -1)
  504.     return -1;
  505.   // hide the taskbar button
  506.   if (!(lpCreateStruct->style & WS_POPUP))
  507.     ModifyStyleEx(0, WS_EX_TOOLWINDOW);
  508.   // Create calendar control
  509.   m_pCalendar = new CDateTimeEditCtrlMonthCalCtrl;
  510.   DWORD dwStyle = m_dwMCStyle & ~(WS_VISIBLE);
  511.   VERIFY(m_pCalendar->Create(dwStyle | WS_CHILD, CPoint(0, 0), this, ID_CALENDAR));
  512.   m_pCalendar->SizeMinReq();
  513.   m_pCalendar->SetOwner(m_pComboParent);
  514.   // size self to fit calendar
  515.   // and make us top-most, so we're seen
  516.   CRect rcCal;
  517.   m_pCalendar->GetWindowRect(&rcCal);
  518.   CalcWindowRect(&rcCal);
  519.   SetWindowPos(&wndTopMost, 0, 0, rcCal.Width(), rcCal.Height(), SWP_NOMOVE | SWP_NOACTIVATE);
  520.   m_pCalendar->ShowWindow(SW_SHOW);
  521.   // the calendar needs to catch all mouse messages, so it can respond to
  522.   // changes in the visible month etc
  523.   m_pCalendar->SetCapture();
  524.   return 0;
  525. }
  526. BOOL CDateTimeEditCtrlCalendarWnd::DestroyWindow() 
  527. {
  528.   ReleaseCapture();
  529.   m_pCalendar->DestroyWindow();
  530.   return CWnd::DestroyWindow();
  531. }
  532. void CDateTimeEditCtrlCalendarWnd::OnActivateApp(BOOL bActive, HTASK hTask) 
  533. {
  534.   CWnd::OnActivateApp(bActive, hTask);
  535.   if (! bActive && m_pComboParent != NULL)
  536.     m_pComboParent->PostMessage(DTCEM_DESTROY_CALENDAR, TRUE);
  537. }
  538. BOOL CDateTimeEditCtrlCalendarWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
  539. {
  540.   LPNMHDR lpnmhdr = (LPNMHDR)lParam;
  541.   if (lpnmhdr != NULL && m_pComboParent != NULL && 
  542.     (lpnmhdr->code == MCN_SELECT || lpnmhdr->code == MCN_SELCHANGE))
  543.   {
  544.     *pResult = m_pComboParent->SendMessage(WM_NOTIFY, wParam, lParam);
  545.     return TRUE;
  546.   }
  547.   return CWnd::OnNotify(wParam, lParam, pResult);
  548. }
  549. /////////////////////////////////////////////////////////////////////////////
  550. // CDateTimeEditCtrl
  551. IMPLEMENT_DYNAMIC(CDateTimeEditCtrl, CDateTimeCtrl)
  552. CDateTimeEditCtrl::CDateTimeEditCtrl()
  553. {
  554.   m_pEdit = NULL;
  555.   m_pBtn = NULL;
  556.   m_bNonEditable = FALSE;
  557.   m_pCalWnd = NULL;
  558.   m_bInCreate = FALSE;
  559.   m_hMCFont = NULL;
  560.   // initial values of cal ctrl colours
  561.   m_acrMonthCal[0] = (COLORREF)-1; // the background color (between months)
  562.   m_acrMonthCal[1] = GetSysColor(COLOR_BTNTEXT);// the dates
  563.   m_acrMonthCal[2] = GetSysColor(COLOR_ACTIVECAPTION); // background of the title
  564.   m_acrMonthCal[3] = (COLORREF)-1; // title text
  565.   m_acrMonthCal[4] = (COLORREF)-1; // background within the month cal
  566.   m_acrMonthCal[5] = GetSysColor(COLOR_3DSHADOW);// the text color of header & trailing days
  567. }
  568. CDateTimeEditCtrl::~CDateTimeEditCtrl()
  569. {
  570.   delete m_pEdit;
  571.   delete m_pBtn;
  572.   delete m_pCalWnd;
  573.   ReleaseCapture();
  574. }
  575. BEGIN_MESSAGE_MAP(CDateTimeEditCtrl, CDateTimeCtrl)
  576. //{{AFX_MSG_MAP(CDateTimeEditCtrl)
  577. ON_WM_CREATE()
  578. ON_WM_ENABLE()
  579. ON_WM_SETFOCUS()
  580. ON_WM_SIZE()
  581. ON_WM_CANCELMODE()
  582. ON_WM_KEYDOWN()
  583. //}}AFX_MSG_MAP
  584. ON_MESSAGE(DTCEM_DESTROY_CALENDAR, OnDestroyCalendar)
  585. ON_WM_STYLECHANGING()
  586. ON_MESSAGE(DTCEM_RECREATE, OnRecreate)
  587. END_MESSAGE_MAP()
  588. /////////////////////////////////////////////////////////////////////////////
  589. // CDateTimeEditCtrl message handlers
  590. LONG CDateTimeEditCtrl::OnDestroyCalendar(WPARAM wParam, LPARAM lParam)
  591. {
  592.   // wParam is the key this is in response to if applicable
  593.   return DestroyCalendar(wParam);
  594. }
  595. BOOL CDateTimeEditCtrl::Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT nID)
  596. {
  597.   m_bInCreate = TRUE;
  598.   if (! CWnd::Create(NULL, NULL, dwStyle, rect, pParentWnd, nID))
  599.     return FALSE;
  600.   return TRUE;
  601. }
  602. int CDateTimeEditCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  603. {
  604.   m_bInCreate = TRUE; // we're being created explicitly
  605.   DWORD dwStyleDTS = (lpCreateStruct->style & 0xFF);
  606.   lpCreateStruct->style &= ~0xFF;
  607.   if (CDateTimeCtrl::OnCreate(lpCreateStruct) == -1)
  608.     return -1;
  609.   ASSERT(!(dwStyleDTS & DTS_UPDOWN));
  610.   ASSERT(!(dwStyleDTS & DTS_SHOWNONE));
  611.   ASSERT(!(dwStyleDTS & DTS_APPCANPARSE));
  612.   ASSERT(!(dwStyleDTS & DTS_LONGDATEFORMAT));
  613.   ASSERT(!(dwStyleDTS & DTS_TIMEFORMAT));
  614.   CRect rc(0, 0, 0, 0);
  615.   m_pEdit = new CDateTimeEditCtrlEditCtrl;
  616.   m_pBtn = new CDateTimeEditCtrlButton;
  617.   // Get the edit control styles and create the edit control
  618.   // we want to isolate the edit styles from the style, and
  619.   // visible and disabled if specified
  620.   // then add in WS_CHILD
  621.   DWORD dwStyleEdit = lpCreateStruct->style & (WS_VISIBLE | WS_DISABLED | 0x3DFFL);
  622.   if (dwStyleDTS & DTS_RIGHTALIGN)
  623.     dwStyleEdit |= ES_RIGHT;
  624.   dwStyleEdit |= WS_CHILD | WS_CLIPSIBLINGS;
  625.   if (!m_pEdit->Create(dwStyleEdit, rc, this, ID_EDIT))
  626.     return -1;
  627.   // Get the button styles and create the button
  628.   // just allow visible and disabled from the specified style
  629.   // and add WS_CHILD and BS_PUSHBUTTON
  630.   DWORD dwStyleBtn = lpCreateStruct->style & (WS_VISIBLE | WS_DISABLED);
  631.   dwStyleBtn |= WS_CHILD | WS_CLIPSIBLINGS | BS_PUSHBUTTON;
  632.   if (!m_pBtn->Create(_T("6"), dwStyleBtn, rc, this, ID_BUTTON))
  633.     return -1;
  634.   // if the edit control is readonly, disable the button
  635.   if (dwStyleEdit & ES_READONLY)
  636.     m_pBtn->EnableWindow(FALSE);
  637.   // get parent's font, and apply to this control
  638.   CWnd* pParent = GetParent();
  639.   if (pParent != NULL)
  640.   {
  641.     CFont* pFont = pParent->GetFont();
  642.     SetFont(pFont);
  643.   }
  644.   CFont font;
  645.   font.CreatePointFont(100, _T("Marlett"));
  646.   HFONT hFont = (HFONT)font.Detach();
  647.   m_pBtn->SendMessage(WM_SETFONT, (WPARAM)hFont, (LPARAM)TRUE);
  648.   return 0;
  649. }
  650. void CDateTimeEditCtrl::OnStyleChanging(int nStyleType, LPSTYLESTRUCT lpStyleStruct)
  651. {
  652.   ASSERT(lpStyleStruct != NULL);
  653.   if (nStyleType & GWL_STYLE && !(nStyleType & GWL_EXSTYLE))
  654.   {
  655.     DWORD dwStyleDTSOld = (lpStyleStruct->styleOld & 0xFF);
  656.     DWORD dwStyleDTSNew = (lpStyleStruct->styleNew & 0xFF);
  657.     // isolate bits which have changed (xor old and new)
  658.     DWORD dwStyleDTS = dwStyleDTSOld ^ dwStyleDTSNew;
  659.     ASSERT(!(dwStyleDTS & DTS_UPDOWN));
  660.     ASSERT(!(dwStyleDTS & DTS_SHOWNONE));
  661.     ASSERT(!(dwStyleDTS & DTS_APPCANPARSE));
  662.     ASSERT(!(dwStyleDTS & DTS_LONGDATEFORMAT));
  663.     ASSERT(!(dwStyleDTS & DTS_TIMEFORMAT));
  664.     if (dwStyleDTS & DTS_RIGHTALIGN)
  665.       m_pEdit->ModifyStyle(dwStyleDTSOld & ES_RIGHT, dwStyleDTSNew & ES_RIGHT);
  666.   }
  667. }
  668. void CDateTimeEditCtrl::OnEnable(BOOL bEnable) 
  669. {
  670.   CDateTimeCtrl::OnEnable(bEnable);
  671.   if (m_pEdit != NULL)
  672.     m_pEdit->EnableWindow(bEnable);
  673.   if (m_pBtn != NULL)
  674.     m_pBtn->EnableWindow(bEnable);
  675. }
  676. void CDateTimeEditCtrl::EnableButton(BOOL bEnable/*=TRUE*/)
  677. {
  678.   if (m_pBtn != NULL)
  679.     m_pBtn->EnableWindow(bEnable);
  680. }
  681. void CDateTimeEditCtrl::OnSetFocus(CWnd* pOldWnd) 
  682. {
  683.   CDateTimeCtrl::OnSetFocus(pOldWnd);
  684.   if (m_bNonEditable && m_pBtn != NULL && ::IsWindow(m_pBtn->m_hWnd))
  685.   {
  686.     // set the focus to the button control
  687.     m_pBtn->SetFocus();
  688.   }
  689.   else if (m_pEdit != NULL && ::IsWindow(m_pEdit->m_hWnd))
  690.   {
  691.     // set the focus to the edit control
  692.     m_pEdit->SetFocus();
  693.     m_pEdit->SetSel(0, -1);
  694.   }
  695. }
  696. void CDateTimeEditCtrl::OnSize(UINT nType, int cx, int cy) 
  697. {
  698.   CDateTimeCtrl::OnSize(nType, cx, cy);
  699.   // size the child controls
  700.   m_pEdit->MoveWindow(0, 0, cx - cy, cy);
  701.   m_pBtn->MoveWindow(cx - cy, 0, cy, cy);
  702. }
  703. BOOL CDateTimeEditCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
  704. {
  705.   NMHDR* pnmhdr = (NMHDR*)lParam;
  706.   if (pnmhdr != NULL)
  707.   {
  708.     // if notification from cal ctrl then act on them, and destroy cal ctrl
  709.     if (m_pCalWnd != NULL && pnmhdr->idFrom == ID_CALENDAR)
  710.     {
  711.       if (pnmhdr->code == MCN_SELECT || 
  712.   pnmhdr->code == MCN_SELCHANGE || 
  713.   pnmhdr->code == MCN_GETDAYSTATE)
  714.       {
  715. // get date, and put in edit ctrl
  716. LPNMSELCHANGE lpnmsc = (LPNMSELCHANGE)pnmhdr;
  717. COleDateTime date(lpnmsc->stSelStart);
  718. ASSERT(date.GetStatus() == COleDateTime::valid);
  719. CString sDate = date.Format(VAR_DATEVALUEONLY);
  720. m_pEdit->SetWindowText(sDate);
  721. if (pnmhdr->code == MCN_SELECT)
  722. {
  723.   // we want to close the calendar when the user selects a date
  724.   DestroyCalendar();
  725. }
  726. else if (pnmhdr->code == MCN_SELCHANGE)
  727. {
  728.   // tell parent about it (DTN_DATETIMECHANGE)
  729.   CWnd* pParent = GetParent();
  730.   if (pParent != NULL)
  731.   {
  732.     NMDATETIMECHANGE nmdtc;
  733.     nmdtc.nmhdr.code = DTN_DATETIMECHANGE;
  734.     nmdtc.nmhdr.hwndFrom = GetSafeHwnd();
  735.     nmdtc.nmhdr.idFrom = GetDlgCtrlID();
  736.     nmdtc.dwFlags = GDT_VALID;
  737.     nmdtc.st = lpnmsc->stSelStart;
  738.     pParent->SendMessage(WM_NOTIFY, (WPARAM)nmdtc.nmhdr.idFrom, (LPARAM)&nmdtc);
  739.   }
  740. }
  741. return TRUE;
  742.       }
  743.     }
  744.   }
  745.   return CDateTimeCtrl::OnNotify(wParam, lParam, pResult);
  746. }
  747. BOOL CDateTimeEditCtrl::OnCommand(WPARAM wParam, LPARAM lParam) 
  748. {
  749.   // if button clicked, show calendar control
  750.   if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_BUTTON)
  751.   {
  752.     if (m_pCalWnd != NULL)
  753.       DestroyCalendar();
  754.     else
  755.       CreateCalendar();
  756.     return TRUE;
  757.   }
  758.   return CDateTimeCtrl::OnCommand(wParam, lParam);
  759. }
  760. void CDateTimeEditCtrl::SetNonEditable(BOOL bNonEditable/*=TRUE*/)
  761. {
  762.   if (m_pEdit == NULL)
  763.     return;
  764.   m_bNonEditable = bNonEditable;
  765.   DWORD dwStyleEdit = m_pEdit->GetStyle();
  766.   CFont* pFont = m_pEdit->GetFont();
  767.   CString sText;
  768.   m_pEdit->GetWindowText(sText);
  769.   m_pEdit->DestroyWindow();
  770.   if (m_bNonEditable)
  771.     dwStyleEdit |= ES_READONLY;
  772.   else
  773.     dwStyleEdit &= ~ES_READONLY;
  774.   if (! m_pEdit->Create(dwStyleEdit, CRect(0, 0, 0, 0), this, ID_EDIT))
  775.     return;
  776.   m_pEdit->SetNonEditable(m_bNonEditable);
  777.   m_pEdit->SetFont(pFont);
  778.   m_pEdit->SetWindowText(sText);
  779.   CRect rc;
  780.   GetClientRect(&rc);
  781.   m_pEdit->MoveWindow(0, 0, rc.Width() - rc.Height(), rc.Height());
  782.   // tell the button whether we are non-editable, so it can decide
  783.   // whether to try and restore the focus
  784.   m_pBtn->SetNonEditable(m_bNonEditable);
  785. }
  786. BOOL CDateTimeEditCtrl::CreateCalendar()
  787. {
  788.   CRect rc;
  789.   ASSERT(m_pCalWnd == NULL);
  790.   m_pCalWnd = new CDateTimeEditCtrlCalendarWnd(this);
  791.   GetWindowRect(&rc);
  792.   rc.top = rc.bottom;
  793.   // Get screen size
  794.   CRect rcWorkArea;
  795.   SystemParametersInfo(SPI_GETWORKAREA, 0, (LPRECT)rcWorkArea, 0);
  796.   if (rc.bottom >= rcWorkArea.bottom)
  797.     rc.bottom = rcWorkArea.bottom;
  798. #if CALENDAR_AS_POPUP
  799.   m_pCalWnd->CreateEx(0, NULL, NULL, WS_CHILD | WS_POPUP | WS_BORDER | WS_CLIPSIBLINGS, rc, this, 0);
  800. #else
  801.   m_pCalWnd->Create(WS_CHILD | WS_BORDER, rc, GetDesktopWindow(), ID_DROPWND);
  802. #endif // CALENDAR_AS_POPUP
  803.   // line calendar window up with right-hand edge of control
  804.   CRect rcCal, rcClient;
  805.   m_pCalWnd->GetWindowRect(&rcCal);
  806.   GetClientRect(&rcClient);
  807.   ClientToScreen(&rcClient);
  808.   rc.right = rcClient.right;
  809.   rc.left = rc.right - rcCal.Width();
  810.   rc.bottom = rc.top + rcCal.Height();
  811.   // if it goes off the bottom of the screen, then put it above this control
  812.   if (rc.bottom > rcWorkArea.bottom)
  813.   {
  814.     CRect rcWnd;
  815.     GetWindowRect(&rcWnd);
  816.     rc.OffsetRect(0, -(rcCal.Height() + rcWnd.Height()));
  817.   }
  818.   // if it's off the left, then nudge it over
  819.   if (rc.left < rcWorkArea.left)
  820.   {
  821.     rc.OffsetRect(rcWorkArea.left - rc.left, 0);
  822.   }
  823.   m_pCalWnd->SetWindowPos(NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  824.   m_pCalWnd->ShowWindow(SW_SHOWNA);
  825.   // get edit's current date/time, and set ctrl accordingly
  826.   m_pEdit->GetWindowText(m_sOrigDate);
  827.   CDateTimeEditCtrlMonthCalCtrl* pCal = m_pCalWnd->GetMonthCalCtrl();
  828.   if (! m_sOrigDate.IsEmpty() && pCal != NULL)
  829.   {
  830.     COleDateTime date;
  831.     if (date.ParseDateTime(m_sOrigDate))
  832.       pCal->SetCurSel(date);
  833.   }
  834.   // set font of cal ctrl if font is non-NULL
  835.   // set colours of cal ctrl if colours are not -1
  836.   if (pCal != NULL)
  837.   {
  838.     if (m_hMCFont != NULL)
  839.     {
  840.       CFont* pFont = CFont::FromHandle(m_hMCFont);
  841.       if (pFont != NULL)
  842. pCal->SetFont(pFont);
  843.     }
  844.     for (int n = 0; n < 6; n++)
  845.     {
  846.       if (m_acrMonthCal[n] != -1)
  847. pCal->SetColor(n, m_acrMonthCal[n]);
  848.     }
  849.   }
  850.   // tell parent about it (DTN_DROPDOWN)
  851.   CWnd* pParent = GetParent();
  852.   if (pParent != NULL)
  853.   {
  854.     NMHDR nmhdr;
  855.     nmhdr.code = DTN_DROPDOWN;
  856.     nmhdr.hwndFrom = GetSafeHwnd();
  857.     nmhdr.idFrom = GetDlgCtrlID();
  858.     pParent->SendMessage(WM_NOTIFY, (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
  859.   }
  860.   return TRUE;
  861. }
  862. // destroy the cal ctrl if shown
  863. // returns TRUE if destroyed, else FALSE if not shown
  864. BOOL CDateTimeEditCtrl::DestroyCalendar(BOOL bDiscard/*=FALSE*/)
  865. {
  866.   if (m_pCalWnd == NULL)
  867.     return FALSE;
  868.   if (::IsWindow(m_pCalWnd->m_hWnd))
  869.     m_pCalWnd->DestroyWindow();
  870.   delete m_pCalWnd;
  871.   m_pCalWnd = NULL;
  872.   m_pEdit->SetFocus();
  873.   CWnd* pParent = GetParent();
  874.   // if we canceled, set the original time string, and send change
  875.   if (bDiscard)
  876.   {
  877.     m_pEdit->SetWindowText(m_sOrigDate);
  878.     // tell parent about it (DTN_DATETIMECHANGE)
  879.     if (pParent != NULL)
  880.     {
  881.       NMDATETIMECHANGE nmdtc;
  882.       nmdtc.nmhdr.code = DTN_DATETIMECHANGE;
  883.       nmdtc.nmhdr.hwndFrom = GetSafeHwnd();
  884.       nmdtc.nmhdr.idFrom = GetDlgCtrlID();
  885.       COleDateTime date;
  886.       date.ParseDateTime(m_sOrigDate);
  887.       if (date.GetStatus() == COleDateTime::valid)
  888. nmdtc.dwFlags = GDT_VALID;
  889.       else
  890. nmdtc.dwFlags = GDT_NONE;
  891.       date.GetAsSystemTime(nmdtc.st);
  892.       pParent->SendMessage(WM_NOTIFY, (WPARAM)nmdtc.nmhdr.idFrom, (LPARAM)&nmdtc);
  893.     }
  894.   }
  895.   // tell parent about it (DTN_CLOSEUP)
  896.   if (pParent != NULL)
  897.   {
  898.     NMHDR nmhdr;
  899.     nmhdr.code = DTN_CLOSEUP;
  900.     nmhdr.hwndFrom = GetSafeHwnd();
  901.     nmhdr.idFrom = GetDlgCtrlID();
  902.     pParent->SendMessage(WM_NOTIFY, (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
  903.   }
  904.   return TRUE;
  905. }
  906. void CDateTimeEditCtrl::OnCancelMode() 
  907. {
  908.   CDateTimeCtrl::OnCancelMode();
  909.   DestroyCalendar();
  910. }
  911. #define MAP_DTM(dtm) case dtm: s = #dtm;
  912. CString FormatDTM(UINT message)
  913. {
  914.   CString s("<unknown>");
  915.   switch (message)
  916.   {
  917.   MAP_DTM(DTM_GETSYSTEMTIME)
  918.   MAP_DTM(DTM_SETSYSTEMTIME)
  919.   MAP_DTM(DTM_GETRANGE)
  920.   MAP_DTM(DTM_SETRANGE)
  921.   MAP_DTM(DTM_SETFORMAT)
  922.   MAP_DTM(DTM_SETMCCOLOR)
  923.   MAP_DTM(DTM_GETMCCOLOR)
  924.   MAP_DTM(DTM_GETMONTHCAL)
  925.   MAP_DTM(DTM_SETMCFONT)
  926.   MAP_DTM(DTM_GETMCFONT)
  927.   };
  928.   return s;
  929. }
  930. LRESULT CDateTimeEditCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
  931. {
  932.   // if a recreate message is pending, then we won't have created 
  933.   // the edit control etc yet, so deal with it now
  934.   if (m_pEdit == NULL)
  935.   {
  936.     MSG msg;
  937.     if (PeekMessage(&msg, NULL, DTCEM_RECREATE, DTCEM_RECREATE, PM_REMOVE))
  938.     {
  939.       DispatchMessage(&msg);
  940.       ASSERT(m_pEdit != NULL);
  941.       ASSERT(m_pBtn != NULL);
  942.     }
  943.   }
  944.   switch (message)
  945.   {
  946.   case WM_SETTEXT:
  947.   case WM_GETTEXT:
  948.   case WM_GETTEXTLENGTH:
  949.     {
  950.       ASSERT(m_pEdit != NULL);
  951.       return m_pEdit->SendMessage(message, wParam, lParam);
  952.     }
  953.     break;
  954.   case WM_SETFONT:
  955.     {
  956.       ASSERT(m_pEdit != NULL);
  957.       ASSERT(m_pBtn != NULL);
  958.       m_pEdit->SendMessage(WM_SETFONT, wParam, lParam);
  959.       CFont font;
  960.       font.CreatePointFont(100, _T("Marlett"));
  961.       HFONT hFont = (HFONT)font.Detach();
  962.       m_pBtn->SendMessage(WM_SETFONT, (WPARAM)hFont, lParam);
  963.     }
  964.     break;
  965.   case WM_COMMAND:
  966.     {
  967.       // send notifications from the edit control to the parent
  968.       if (LOWORD(wParam) == ID_EDIT)
  969.       {
  970. CWnd* pParent = GetParent();
  971. if (pParent != NULL)
  972.   return pParent->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), HIWORD(wParam)), (LPARAM)GetSafeHwnd());
  973.       }
  974.     }
  975.     break;
  976.   case DTM_GETSYSTEMTIME:
  977.     {
  978.       // get the time from the current edit text
  979.       // and return whether it's valid
  980.       CString sDate;
  981.       ASSERT(m_pEdit != NULL);
  982.       m_pEdit->GetWindowText(sDate);
  983.       if (sDate.IsEmpty())
  984. return GDT_NONE;
  985.       else
  986.       {
  987. LPSYSTEMTIME lpst = (LPSYSTEMTIME)lParam;
  988. ASSERT(lpst != NULL);
  989. COleDateTime date;
  990. date.ParseDateTime(sDate);
  991. ASSERT(date.GetStatus() == COleDateTime::valid);
  992. date.GetAsSystemTime(*lpst);
  993. return GDT_VALID;
  994.       }
  995.     }
  996.     break;
  997.   case DTM_SETSYSTEMTIME:
  998.     {
  999.       // set the control's time to the time specified
  1000.       ASSERT(m_pEdit != NULL);
  1001.       if (wParam == GDT_NONE)
  1002. m_pEdit->SetWindowText("");
  1003.       else if (wParam == GDT_VALID)
  1004.       {
  1005. if (lParam == 0)
  1006.   return FALSE;
  1007. LPSYSTEMTIME lpst = (LPSYSTEMTIME)lParam;
  1008. COleDateTime date(*lpst);
  1009. if (date.GetStatus() != COleDateTime::valid)
  1010.   return FALSE;
  1011. CString sDate = date.Format(VAR_DATEVALUEONLY);
  1012. m_pEdit->SetWindowText(sDate);
  1013.       }
  1014.       else
  1015. return FALSE;
  1016.       return TRUE;
  1017.     }
  1018.     break;
  1019.   case DTM_GETMONTHCAL:
  1020.     {
  1021.       if (m_pCalWnd != NULL)
  1022.       {
  1023. CMonthCalCtrl* pCal = m_pCalWnd->GetMonthCalCtrl();
  1024. if (pCal != NULL)
  1025.   return (LRESULT)pCal->m_hWnd;
  1026.       }
  1027.       return (LRESULT)NULL;
  1028.     }
  1029.     break;
  1030.   case DTM_SETMCFONT:
  1031.     {
  1032.       m_hMCFont = (HFONT)wParam;
  1033.       BOOL bRedraw = (BOOL)lParam;
  1034.       if (m_pCalWnd == NULL)
  1035. return 0;
  1036.       CMonthCalCtrl* pCal = m_pCalWnd->GetMonthCalCtrl();
  1037.       if (pCal == NULL)
  1038. return 0;
  1039.       CFont* pFont = CFont::FromHandle(m_hMCFont);
  1040.       pCal->SetFont(pFont, bRedraw);
  1041.     }
  1042.     break;
  1043.   case DTM_GETMCFONT:
  1044.     {
  1045.       if (m_pCalWnd == NULL)
  1046. return (LRESULT)m_hMCFont;
  1047.       CMonthCalCtrl* pCal = m_pCalWnd->GetMonthCalCtrl();
  1048.       if (pCal == NULL)
  1049. return (LRESULT)m_hMCFont;
  1050.       CFont* pFont = pCal->GetFont();
  1051.       return (LRESULT)pFont->m_hObject;
  1052.     }
  1053.     break;
  1054.   case DTM_SETMCCOLOR:
  1055.     {
  1056.       // set the colour accordingly
  1057.       int nIndex = (int)wParam;
  1058.       ASSERT(nIndex >= 0 && nIndex < 6);
  1059.       COLORREF crOld = m_acrMonthCal[nIndex];
  1060.       m_acrMonthCal[nIndex] = (COLORREF)lParam;
  1061.       // if the cal ctrl is present, set its colours
  1062.       if (m_pCalWnd != NULL)
  1063.       {
  1064. CMonthCalCtrl* pCal = m_pCalWnd->GetMonthCalCtrl();
  1065. if (pCal != NULL)
  1066.   return (LRESULT)pCal->SetColor(nIndex, m_acrMonthCal[nIndex]);
  1067.       }
  1068.       return (LRESULT)crOld;
  1069.     }
  1070.     break;
  1071.   case DTM_GETMCCOLOR:
  1072.     {
  1073.       // get the colour accordingly
  1074.       int nIndex = (int)wParam;
  1075.       ASSERT(nIndex >= 0 && nIndex < 6);
  1076.       // if the cal ctrl is present, get its colours
  1077.       if (m_pCalWnd != NULL)
  1078.       {
  1079. CMonthCalCtrl* pCal = m_pCalWnd->GetMonthCalCtrl();
  1080. if (pCal != NULL)
  1081.   return (LRESULT)pCal->GetColor(nIndex);
  1082.       }
  1083.       return (LRESULT)m_acrMonthCal[nIndex];
  1084.     }
  1085.     break;
  1086.   case DTM_GETRANGE:
  1087.   case DTM_SETRANGE:
  1088.   case DTM_SETFORMAT:
  1089.     TRACE("Got DTM: %sn", FormatDTM(message));
  1090.     ASSERT(FALSE);  // not supported
  1091.     AfxThrowNotSupportedException();
  1092.     break;
  1093.   }
  1094.   return CDateTimeCtrl::WindowProc(message, wParam, lParam);
  1095. }
  1096. void CDateTimeEditCtrl::PreSubclassWindow() 
  1097. {
  1098.   CDateTimeCtrl::PreSubclassWindow();
  1099.   // if we were created explicitly then everything is hunky-dory
  1100.   // else we need to destroy the datetimectrl and create our own stuff
  1101.   if (m_bInCreate)
  1102.     return;
  1103.   PostMessage(DTCEM_RECREATE);
  1104. }
  1105. LONG CDateTimeEditCtrl::OnRecreate(WPARAM wParam, LPARAM lParam)
  1106. {
  1107.   // we come in here if we've been subclassed, so we can destroy
  1108.   // the existing control, and create our own version
  1109.   CWnd* pParentWnd = GetParent();
  1110.   if (pParentWnd == NULL)
  1111.     pParentWnd = GetDesktopWindow();
  1112.   CWnd* pWndFocus = CWnd::GetFocus();
  1113.   BOOL bFocus = (pWndFocus == this);
  1114.   // get current attributes
  1115.   DWORD dwStyle = GetStyle();
  1116.   DWORD dwStyleEx = GetExStyle();
  1117.   CRect rc;
  1118.   GetWindowRect(&rc);
  1119.   pParentWnd->ScreenToClient(&rc); // map to client co-ords
  1120.   UINT nID = GetDlgCtrlID();
  1121.   CFont* pFont = GetFont();
  1122.   CWnd* pWndAfter = GetNextWindow(GW_HWNDPREV);
  1123.   DestroyWindow();
  1124.   m_hWnd = NULL;
  1125.   m_bInCreate = TRUE;
  1126.   if (! CWnd::CreateEx(dwStyleEx, NULL, NULL, dwStyle, rc, pParentWnd, nID))
  1127.     return -1;
  1128.   // re-apply attributes
  1129.   if (pFont == NULL)
  1130.     pFont = pParentWnd->GetFont();
  1131.   SetFont(pFont);
  1132.   // re-set focus
  1133.   if (bFocus)
  1134.     SetFocus();
  1135.   // position correctly in z-order
  1136.   SetWindowPos(pWndAfter == NULL ? &CWnd::wndBottom : pWndAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
  1137.   return 0;
  1138. }
  1139. // returns whether the date passed is valid
  1140. // if NULL is passed, then the date in the edit control is used
  1141. BOOL CDateTimeEditCtrl::IsValidDate(LPCTSTR lpszDate/*=NULL*/)
  1142. {
  1143.   CString sDate = lpszDate;
  1144.   if (sDate.IsEmpty())
  1145.     GetWindowText(sDate);
  1146.   COleDateTime date;
  1147.   return date.ParseDateTime(sDate);
  1148. }
  1149. // set whether the user can only enter chars that are valid
  1150. void CDateTimeEditCtrl::SetValidCharsOnly(BOOL bValidCharsOnly/*=TRUE*/)
  1151. {
  1152.   if (m_pEdit != NULL)
  1153.     m_pEdit->SetValidCharsOnly(bValidCharsOnly);
  1154. }
  1155. // returns whether the user can only enter valid chars into the edit control
  1156. BOOL CDateTimeEditCtrl::GetValidCharsOnly()
  1157. {
  1158.   if (m_pEdit != NULL)
  1159.     return m_pEdit->GetValidCharsOnly();
  1160.   return FALSE;
  1161. }
  1162. // sets the chars that are valid for the user to type into the edit control
  1163. // if NULL is specified, then the default chars are used (0-9 and current 
  1164. // user's locale's date separator). If the user changes the locale settings 
  1165. // then the control will automatically pick this up and use the new separator.
  1166. void CDateTimeEditCtrl::SetValidChars(LPCTSTR lpszValidChars/*=NULL*/)
  1167. {
  1168.   if (m_pEdit != NULL)
  1169.     m_pEdit->SetValidChars(lpszValidChars);
  1170. }
  1171. // returns the chars that are valid for the user to type into the edit control
  1172. CString CDateTimeEditCtrl::GetValidChars()
  1173. {
  1174.   if (m_pEdit != NULL)
  1175.     return m_pEdit->GetValidChars();
  1176.   return "";
  1177. }
  1178. void CDateTimeEditCtrl::SetAllowUpDownKeys(BOOL bAllow/*=TRUE*/)
  1179. {
  1180.   if (m_pEdit != NULL)
  1181.     m_pEdit->SetAllowUpDownKeys(bAllow);
  1182. }
  1183. BOOL CDateTimeEditCtrl::GetAllowUpDownKeys()
  1184. {
  1185.   if (m_pEdit != NULL)
  1186.     return m_pEdit->GetAllowUpDownKeys();
  1187.   return FALSE;
  1188. }