// ========================================================================== // Class Implementation : COXCalendarEdit, COXMonthCalPopup // ========================================================================== // Source file : OXCalendarEdit.cpp // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. #include "stdafx.h" #include "OXCalendarEdit.h" #pragma warning(disable : 4706) #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // COXMonthCalPopup COXMonthCalPopup::COXMonthCalPopup() { } COXMonthCalPopup::~COXMonthCalPopup() { } BEGIN_MESSAGE_MAP(COXMonthCalPopup, CMonthCalCtrl) //{{AFX_MSG_MAP(COXMonthCalPopup) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP() BOOL COXMonthCalPopup::Pick(CRect rect, CRect rectParent, COleDateTime dtInitialDate, COleDateTime& dtPickedDate) { AdjustDisplayRectangle(rect, rectParent); SetCurSel(dtInitialDate); MoveWindow(rect); ShowWindow(SW_SHOW); SetCapture(); // init message loop bool bBreak = false; BOOL bValidDatePicked = FALSE; while (!bBreak) { MSG msg; VERIFY(::GetMessage(&msg, NULL, 0, 0)); if (msg.message == WM_LBUTTONDOWN) { DispatchMessage(&msg); SetCapture(); } else if (msg.message == WM_LBUTTONUP) { DispatchMessage(&msg); MCHITTESTINFO hti; ::memset(&hti, 0, sizeof(hti)); hti.pt.x = GET_X_LPARAM(msg.lParam); hti.pt.y = GET_Y_LPARAM(msg.lParam); hti.cbSize = sizeof(hti); HitTest(&hti); DWORD dwResult = HitTest(&hti); if (dwResult == MCHT_CALENDARDATE) { // The user clicked on a date, so save and close SYSTEMTIME stOut; GetCurSel(&stOut); stOut.wHour = 0; stOut.wMinute = 0; stOut.wSecond = 0; stOut.wMilliseconds = 0; dtPickedDate = COleDateTime(stOut); bValidDatePicked = TRUE; bBreak = true; } else if (dwResult == MCHT_NOWHERE) { // The user clicked outside bBreak = true; } SetCapture(); } else if (msg.message == WM_KEYDOWN) { // Handle ESCAPE and ENTER if (msg.wParam == VK_ESCAPE) bBreak = true; else if (msg.wParam == VK_RETURN) { DispatchMessage(&msg); SYSTEMTIME stOut; GetCurSel(&stOut); stOut.wHour = 0; stOut.wMinute = 0; stOut.wSecond = 0; stOut.wMilliseconds = 0; dtPickedDate = COleDateTime(stOut); bValidDatePicked = TRUE; bBreak = true; } else DispatchMessage(&msg); } else { DispatchMessage(&msg); } } ReleaseCapture(); ShowWindow(SW_HIDE); return bValidDatePicked; } ///////////////////////////////////////////////////////////////////////////// // COXCalendarEdit IMPLEMENT_DYNCREATE(COXCalendarEdit, COXMaskedEdit) COXCalendarEdit::COXCalendarEdit() { m_strDateFormat = _T(""); } COXCalendarEdit::COXCalendarEdit(LPCTSTR lpszDateFormat) { SetDateFormat(lpszDateFormat); } COXCalendarEdit::~COXCalendarEdit() { } BEGIN_MESSAGE_MAP(COXCalendarEdit, COXMaskedEdit) //{{AFX_MSG_MAP(COXCalendarEdit) ON_WM_KILLFOCUS() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXCalendarEdit message handlers BOOL COXCalendarEdit::InitializeDropEdit() { if(!m_Calendar.CreateEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, _T("SysMonthCal32"),_T(""), WS_POPUP | WS_BORDER, CRect(0, 0, 0, 0), this, 0, NULL)) { TRACE(_T("Unable to create drop list.\n")); AfxThrowResourceException(); } m_Calendar.SetOwner(this); return TRUE; } void COXCalendarEdit::OnDropButton() { CRect rect; GetClientRect(rect); ClientToScreen(rect); rect.right += GetButtonWidth(); CRect rectParent = rect; rect.top = rect.bottom; rect.left = rect.right; // Get the size of the calendar control RECT rectSize; m_Calendar.GetMinReqRect(&rectSize); rect.left -= rectSize.right; rect.bottom += rectSize.bottom; // Read the initial date CString strText; GetWindowText(strText); COleDateTime dtInitialDate = GetDateFromString(strText); if (dtInitialDate.GetStatus() != COleDateTime::valid) dtInitialDate = COleDateTime::GetCurrentTime(); // Display the calendar popup window COleDateTime dtPickedDate; BOOL bDatePicked = m_Calendar.Pick(rect, rectParent, dtInitialDate, dtPickedDate); if (bDatePicked) SetWindowText(GetStringFromDate(dtPickedDate)); } CString COXCalendarEdit::GetErrorString() { return _T(""); } void COXCalendarEdit::SetDateFormat(LPCTSTR lpszDateFormat) { m_strDateFormat = lpszDateFormat; m_strDateFormat.MakeUpper(); // Set the masked edit format CString strMaskedEditFormat(m_strDateFormat); strMaskedEditFormat.Replace('D', '#'); strMaskedEditFormat.Replace('M', '#'); strMaskedEditFormat.Replace('Y', '#'); SetMask(strMaskedEditFormat); } COleDateTime COXCalendarEdit::GetDateFromString(LPCTSTR lpszDateString) { CString strDate = lpszDateString; // First get the year // 1. Find where the year starts and where it ends int iYearStartIdx = m_strDateFormat.Find('Y'); int iYearEndIdx = m_strDateFormat.ReverseFind('Y'); CString strYearOnly = strDate.Mid(iYearStartIdx, iYearEndIdx - iYearStartIdx + 1); int iYear = ::_ttoi(strYearOnly); // 2. Find where the month starts and where it ends int iMonthStartIdx = m_strDateFormat.Find('M'); int iMonthEndIdx = m_strDateFormat.ReverseFind('M'); CString strMonthOnly = strDate.Mid(iMonthStartIdx, iMonthEndIdx - iMonthStartIdx + 1); int iMonth = ::_ttoi(strMonthOnly); // 3. Find where the day starts and where it ends int iDayStartIdx = m_strDateFormat.Find('D'); int iDayEndIdx = m_strDateFormat.ReverseFind('D'); CString strDayOnly = strDate.Mid(iDayStartIdx, iDayEndIdx - iDayStartIdx + 1); int iDay = ::_ttoi(strDayOnly); return COleDateTime(iYear, iMonth, iDay, 0, 0, 0); } void COXCalendarEdit::OnKillFocus(CWnd* pNewWnd) { COXMaskedEdit::OnKillFocus(pNewWnd); } CString COXCalendarEdit::GetStringFromDate(COleDateTime dtDate) { // Format the picked date and put it into the edit box // We need to convert the COXCalendarEdit date format string into // and strftime() style format string. CString strNewFormat(m_strDateFormat); strNewFormat.Replace(_T("YYYY"), _T("%Y")); strNewFormat.Replace(_T("YY"), _T("%y")); strNewFormat.Replace(_T("MM"), _T("%m")); strNewFormat.Replace(_T("DD"), _T("%d")); return dtDate.Format(strNewFormat); } CString COXCalendarEdit::GetText() { CString strText = COXMaskedEdit::GetText(); if (IsEmptyDate(strText)) return _T(""); else if (IsValidDate(strText)) return strText; else return GetErrorString(); } BOOL COXCalendarEdit::IsValidDate(LPCTSTR lpszTestDate) { ASSERT(lpszTestDate != NULL); if (IsEmptyDate(lpszTestDate)) return TRUE; COleDateTime dtDate = GetDateFromString(lpszTestDate); if (dtDate.GetStatus() == COleDateTime::valid) return TRUE; else return FALSE; } void COXMonthCalPopup::AdjustDisplayRectangle(CRect &rect, CRect rectParent) { // Get the rectangle of the monitor closest to the menu rectangle HMONITOR hMonitor = ::MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST); MONITORINFO mi; mi.cbSize = sizeof(MONITORINFO); ::GetMonitorInfo(hMonitor, &mi); const int iMixScreenX = mi.rcMonitor.left; const int iMaxScreenX = mi.rcMonitor.right; const int iMaxScreenY = mi.rcMonitor.bottom; CSize sizeParent(rectParent.Width(), rectParent.Height()); CPoint ptTopLeft(rect.left, rect.top); if (ptTopLeft.x < iMixScreenX) ptTopLeft.x = iMixScreenX; if (ptTopLeft.x + rect.Width() > iMaxScreenX) ptTopLeft.x = iMaxScreenX - rect.Width(); // Make sure the popup is not clipped at the bottom if (rect.bottom > iMaxScreenY) { // The popup should be above the item ptTopLeft.y = rectParent.top - rect.Height(); } CSize sizeTemp = rect.Size(); rect.SetRect(ptTopLeft.x, ptTopLeft.y, ptTopLeft.x + sizeTemp.cx, ptTopLeft.y + sizeTemp.cy); } BOOL COXCalendarEdit::IsEmptyDate(LPCTSTR lpszTestDate) { CString strTestDate = lpszTestDate; if (strTestDate.IsEmpty()) return TRUE; // Produce the empty date string CString strEmptyDate = m_strDateFormat; strEmptyDate.Replace('Y', ' '); strEmptyDate.Replace('M', ' '); strEmptyDate.Replace('D', ' '); if (strTestDate == strEmptyDate) return TRUE; else return FALSE; } void COXCalendarEdit::SetText(LPCTSTR lpszDate) { if (IsValidDate(lpszDate)) COXMaskedEdit::SetText(lpszDate); else COXMaskedEdit::SetText(_T("")); }