359 lines
18 KiB
C++
359 lines
18 KiB
C++
// ==========================================================================
|
||
// 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 <multimon.h>
|
||
|
||
#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("<bad date>");
|
||
}
|
||
|
||
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(""));
|
||
}
|