560 lines
15 KiB
C++
560 lines
15 KiB
C++
// ==========================================================================
|
|
// Class Implementation : OXCalendar
|
|
// ==========================================================================
|
|
|
|
// Source file :OXCalendar.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 "OXCalendar.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COXCalendar
|
|
|
|
static int nNumDays;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Definition of static members
|
|
|
|
CStringArray COXCalendar::ms_straWeekDays;
|
|
CStringArray COXCalendar::ms_straMonthNames;
|
|
|
|
|
|
// Data members -------------------------------------------------------------
|
|
//protected:
|
|
// COleDateTime m_CurrentDate;
|
|
// --- Current date value
|
|
|
|
// COleDateTime m_StartDate; COleDateTime m_EndDate;
|
|
// --- starting and ending dates of selectable date range
|
|
|
|
// int m_nFirstDayOfWeek;
|
|
//--- number of first day of week
|
|
// this value stored in internal format
|
|
// 0 - Monday 1 - Tuesday, ect., 6 - Sunday
|
|
|
|
// COXCalendarPopup m_wndDatePicker;
|
|
//--- calendar popup window
|
|
|
|
// HCURSOR m_hCursor;
|
|
//--- window cursor
|
|
|
|
// BOOL m_bDrawButton;
|
|
//--- this flag show whether the down arrow will draw
|
|
|
|
// int m_nButtonWidth;
|
|
//--- width in pixels of down arrow
|
|
|
|
//private
|
|
//int ms_nRefCount
|
|
//--- count of present COXCalendar objects (static)
|
|
|
|
// this callback routine is used to fill COXCalendar::ms_straWeekDays
|
|
BOOL CALLBACK COXCalendar::EnumDayOfWeek(LPTSTR lpCalendarInfoString)
|
|
{
|
|
#if defined (_WINDLL)
|
|
#if defined (_AFXDLL)
|
|
AFX_MANAGE_STATE(AfxGetAppModuleState());
|
|
#else
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
#endif
|
|
#endif
|
|
|
|
COXCalendar::ms_straWeekDays.Add(lpCalendarInfoString);
|
|
ASSERT(COXCalendar::ms_straWeekDays.GetSize() <= 7);// we don't need long weeks
|
|
return FALSE;
|
|
}
|
|
|
|
// this callback routine is used to fill COXCalendar::ms_straMonthNames
|
|
BOOL CALLBACK COXCalendar::EnumMonth(LPTSTR lpCalendarInfoString)
|
|
{
|
|
#if defined (_WINDLL)
|
|
#if defined (_AFXDLL)
|
|
AFX_MANAGE_STATE(AfxGetAppModuleState());
|
|
#else
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
#endif
|
|
#endif
|
|
|
|
COXCalendar::ms_straMonthNames.Add(lpCalendarInfoString);
|
|
ASSERT(COXCalendar::ms_straMonthNames.GetSize() <= 12);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void COXCalendar::GetCalendarLocaleData()
|
|
{
|
|
ms_straWeekDays.RemoveAll();
|
|
ms_straMonthNames.RemoveAll();
|
|
|
|
//Retrieving Win32 National Language Support Calendar Information
|
|
// Windows provide weekdays as
|
|
// 0-Monday
|
|
|
|
// for 1st WeekDay, ect.
|
|
EnumCalendarInfo(EnumDayOfWeek,LOCALE_SYSTEM_DEFAULT,1,CAL_SABBREVDAYNAME1);
|
|
EnumCalendarInfo(EnumDayOfWeek,LOCALE_SYSTEM_DEFAULT,1,CAL_SABBREVDAYNAME2);
|
|
EnumCalendarInfo(EnumDayOfWeek,LOCALE_SYSTEM_DEFAULT,1,CAL_SABBREVDAYNAME3);
|
|
EnumCalendarInfo(EnumDayOfWeek,LOCALE_SYSTEM_DEFAULT,1,CAL_SABBREVDAYNAME4);
|
|
EnumCalendarInfo(EnumDayOfWeek,LOCALE_SYSTEM_DEFAULT,1,CAL_SABBREVDAYNAME5);
|
|
EnumCalendarInfo(EnumDayOfWeek,LOCALE_SYSTEM_DEFAULT,1,CAL_SABBREVDAYNAME6);
|
|
EnumCalendarInfo(EnumDayOfWeek,LOCALE_SYSTEM_DEFAULT,1,CAL_SABBREVDAYNAME7);
|
|
|
|
// now retrieving months
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME1);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME2);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME3);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME4);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME5);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME6);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME7);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME8);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME9);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME10);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME11);
|
|
EnumCalendarInfo(EnumMonth,LOCALE_SYSTEM_DEFAULT,1,CAL_SMONTHNAME12);
|
|
}
|
|
|
|
|
|
int COXCalendar::GetLocaleFirstDayOfWeek()
|
|
{
|
|
// get space needed to take locale info
|
|
int nCount =::GetLocaleInfo(LOCALE_SYSTEM_DEFAULT,LOCALE_IFIRSTDAYOFWEEK,NULL,0);
|
|
ASSERT(nCount>0);
|
|
CString sFirstDay;
|
|
// retreiving default number of first day of week
|
|
VERIFY(::GetLocaleInfo(
|
|
LOCALE_SYSTEM_DEFAULT,LOCALE_IFIRSTDAYOFWEEK,
|
|
sFirstDay.GetBufferSetLength(nCount),nCount));
|
|
int nFirstDayOfWeek=_ttoi(sFirstDay.GetBuffer(sFirstDay.GetLength()));
|
|
sFirstDay.ReleaseBuffer();
|
|
|
|
return nFirstDayOfWeek;
|
|
}
|
|
|
|
|
|
COXCalendar::COXCalendar() :
|
|
m_StartDate(100,1,1,0,0,0),
|
|
m_EndDate(9999,12,31,0,0,0),
|
|
m_bDrawButton(FALSE),
|
|
m_sFormat(_T(""))
|
|
{
|
|
m_CurrentDate.SetStatus(COleDateTime::null);
|
|
m_nFirstDayOfWeek=COXCalendar::GetLocaleFirstDayOfWeek();
|
|
|
|
if(ms_straWeekDays.GetSize()==0 || ms_straMonthNames.GetSize()==0)
|
|
{
|
|
COXCalendar::GetCalendarLocaleData();
|
|
}
|
|
}
|
|
|
|
COXCalendar::~COXCalendar()
|
|
{
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(COXCalendar, CWnd)
|
|
//{{AFX_MSG_MAP(COXCalendar)
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_SETFOCUS()
|
|
ON_WM_PAINT()
|
|
ON_WM_SETCURSOR()
|
|
ON_WM_CLOSE()
|
|
//}}AFX_MSG_MAP
|
|
ON_MESSAGE(WM_SETTINGCHANGE,OnSettingChanged)
|
|
ON_EN_SETFOCUS(IDEDIT, OnSetFocusEdit)
|
|
ON_EN_KILLFOCUS(IDEDIT, OnKillFocusEdit)
|
|
ON_EN_CHANGE(IDEDIT,OnEnChange)
|
|
ON_EN_UPDATE(IDEDIT,OnEnUpdate)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COXCalendar message handlers
|
|
|
|
BOOL COXCalendar::Create(DWORD dwStyle, const CRect& r, CWnd* pParentWnd, UINT nID)
|
|
{
|
|
if(!CWnd::Create(NULL,_T(""),dwStyle,r,pParentWnd,nID))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return Init();// initialization
|
|
}
|
|
|
|
void COXCalendar::OnLButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
// TODO: Add your message handler code here and/or call default
|
|
COleDateTime targetDate;
|
|
CRect rWnd;
|
|
GetWindowRect(&rWnd);
|
|
ASSERT(::IsWindow(m_wndDatePicker.m_hWnd));
|
|
GetCurrentDate();//fill m_CurrentDate from text typed in edit control
|
|
|
|
// perform bounds check and corrections
|
|
ShrinkDateInBounds();
|
|
|
|
if(m_wndDatePicker.Pick(m_CurrentDate,m_StartDate,m_EndDate,rWnd))
|
|
{
|
|
//Show selected date in edit window
|
|
CString sText;
|
|
SetOutputText(sText);
|
|
m_wndEdit.SetWindowText(sText);
|
|
}
|
|
SetFocus();
|
|
CWnd::OnLButtonDown(nFlags, point);
|
|
}
|
|
|
|
void COXCalendar::OnSetFocus(CWnd* pOldWnd)
|
|
{
|
|
CWnd::OnSetFocus(pOldWnd);
|
|
|
|
// TODO: Add your message handler code here
|
|
ASSERT(::IsWindow(m_wndEdit.m_hWnd));
|
|
m_wndEdit.SetFocus();
|
|
}
|
|
|
|
void COXCalendar::OnPaint()
|
|
{
|
|
CPaintDC dc(this); // device context for painting
|
|
|
|
// TODO: Add your message handler code here
|
|
if(m_bDrawButton)
|
|
{
|
|
CRect r;
|
|
GetClientRect(&r);
|
|
r.left = r.right - m_nButtonWidth;
|
|
dc.DrawFrameControl(&r,DFC_SCROLL,DFCS_SCROLLDOWN);
|
|
}
|
|
|
|
// Do not call CWnd::OnPaint() for painting messages
|
|
}
|
|
|
|
LRESULT COXCalendar::OnSettingChanged(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
UNREFERENCED_PARAMETER(wParam);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
|
|
COXCalendar::GetCalendarLocaleData();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void COXCalendar::OnKillFocusEdit()
|
|
{
|
|
// now trying to parse edit text and correct mismatched day of week
|
|
GetCurrentDate();//fill m_CurrentDate from text typed in edit control
|
|
// perform bounds check and corrections
|
|
ShrinkDateInBounds();
|
|
if(FALSE || m_CurrentDate.GetStatus() == COleDateTime::valid)
|
|
{
|
|
CString sText;
|
|
SetOutputText(sText);
|
|
m_wndEdit.SetWindowText(sText);
|
|
}
|
|
|
|
int id = GetDlgCtrlID();
|
|
DWORD nCode = EN_KILLFOCUS;
|
|
nCode <<= 16;
|
|
DWORD wParam = nCode | id;
|
|
GetParent()->SendMessage(WM_COMMAND,wParam,(LPARAM)GetSafeHwnd());
|
|
|
|
ReInitLayout(FALSE); // hide dropdown button
|
|
}
|
|
|
|
void COXCalendar::OnSetFocusEdit()
|
|
{
|
|
int id = GetDlgCtrlID();
|
|
DWORD nCode = EN_SETFOCUS;
|
|
nCode <<= 16;
|
|
DWORD wParam = nCode | id;
|
|
GetParent()->SendMessage(WM_COMMAND,wParam,(LPARAM)GetSafeHwnd());
|
|
|
|
ReInitLayout(TRUE); // show dropdown button
|
|
}
|
|
|
|
BOOL COXCalendar::Init()
|
|
// --- In :
|
|
// --- Out :
|
|
// --- Returns : Whether the window initialized succesfully
|
|
// --- Effect : perform initialization, create child windows
|
|
{
|
|
ASSERT(::IsWindow(m_hWnd));
|
|
CRect r;
|
|
GetClientRect(&r);
|
|
// In this test class CEdit styles are fixed
|
|
if(!m_wndEdit.Create(WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL|WS_TABSTOP,
|
|
r,this,IDEDIT))
|
|
{
|
|
TRACE0("COXCalendar:: failed to create child window!");
|
|
return FALSE;
|
|
}
|
|
CWnd * pParent = GetParent();
|
|
if(pParent)
|
|
{
|
|
CFont *pFont = pParent->GetFont();
|
|
if(pFont)
|
|
{
|
|
LOGFONT lf;
|
|
pParent->GetFont()->GetLogFont(&lf);
|
|
m_font.CreateFontIndirect(&lf);
|
|
SetFont(&m_font);
|
|
m_wndEdit.SetFont(&m_font);
|
|
}
|
|
}
|
|
m_nButtonWidth = ::GetSystemMetrics(SM_CXVSCROLL);
|
|
m_hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
|
|
if(!m_wndDatePicker.Create(this))
|
|
{
|
|
TRACE0("COXCalendar:: failed to create child window!");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void COXCalendar::ReInitLayout(BOOL bShowBtn)
|
|
// --- In :Whether the down arrow show
|
|
// --- Out :
|
|
// --- Returns :
|
|
// --- Effect :manage edit box size, redraw window
|
|
{
|
|
CRect r;
|
|
GetClientRect(&r);
|
|
m_bDrawButton = bShowBtn;// OnPaint() check it to draw button
|
|
|
|
if(bShowBtn) // if set input focus, add space to down button
|
|
r.right -= m_nButtonWidth;
|
|
ASSERT(IsWindow(m_wndEdit.m_hWnd));
|
|
m_wndEdit.MoveWindow(&r);
|
|
InvalidateRect(NULL);
|
|
}
|
|
|
|
BOOL COXCalendar::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
|
|
{
|
|
// TODO: Add your message handler code here and/or call default
|
|
if(nHitTest == HTCLIENT && pWnd == this)
|
|
{
|
|
::SetCursor(m_hCursor);
|
|
return TRUE;
|
|
}
|
|
return CWnd::OnSetCursor(pWnd, nHitTest, message);
|
|
}
|
|
|
|
BOOL COXCalendar::SetFirstDayOfWeek(int nDayOfWeek)
|
|
{
|
|
// use the following values: 0 Sunday, 1 Monday, 2 Tuesday,
|
|
//3 Wednesday, 4 Thursday, 5 Friday, 6 Saturday
|
|
if(nDayOfWeek < 0 || nDayOfWeek > 6)
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
// convert it to internal schema:
|
|
// 0 Monday, 1 Tuesday,2 Wednesday,
|
|
// 3 Thursday, 4 Friday, 5 Saturday,6 Sunday
|
|
|
|
m_nFirstDayOfWeek = (nDayOfWeek+6)%7;
|
|
m_wndDatePicker.m_nFirstDayOfWeek = m_nFirstDayOfWeek;
|
|
return TRUE;
|
|
}
|
|
|
|
int COXCalendar::GetFirstDayOfWeek()
|
|
{
|
|
ASSERT(m_nFirstDayOfWeek >= 0 && m_nFirstDayOfWeek < 7);
|
|
// convert from internal schema
|
|
return (m_nFirstDayOfWeek + 1)%7;
|
|
}
|
|
|
|
BOOL COXCalendar::SetDateRange(COleDateTime startDate, COleDateTime endDate)
|
|
{
|
|
if(startDate.GetStatus() == COleDateTime::invalid ||
|
|
endDate.GetStatus() == COleDateTime::invalid)
|
|
{
|
|
// dates must be valid
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
if(startDate.GetStatus() == COleDateTime::valid &&
|
|
endDate.GetStatus() == COleDateTime::valid &&
|
|
startDate > endDate)
|
|
{
|
|
TRACE0("COXCalendar:::SetDateRange: incorrect range specified, function fail!\n");
|
|
return FALSE;
|
|
}
|
|
m_StartDate = startDate;
|
|
m_EndDate = endDate;
|
|
return TRUE;
|
|
}
|
|
|
|
void COXCalendar::GetDateRange(COleDateTime& startDate, COleDateTime& endDate)
|
|
{
|
|
startDate = m_StartDate;
|
|
endDate = m_EndDate;
|
|
}
|
|
|
|
// return current time from edit window
|
|
COleDateTime COXCalendar::GetCurrentDate()
|
|
{
|
|
if(::IsWindow(m_wndEdit.m_hWnd))
|
|
{
|
|
m_CurrentDate.m_dt = 0;
|
|
m_CurrentDate.SetStatus(COleDateTime::null);
|
|
CString sTypedDate;
|
|
m_wndEdit.GetWindowText(sTypedDate);
|
|
//preset contained date to "no selection"
|
|
// COleDateTime don't parse date string if it
|
|
//contained some locale's weekday names
|
|
//We must exclude any leading non-digit characters
|
|
int pos=sTypedDate.FindOneOf(_T("0123456789"));
|
|
if(pos != -1)
|
|
{
|
|
COleDateTime targetDate;
|
|
CString sParseSrc =
|
|
sTypedDate.Right(sTypedDate.GetLength() - pos);
|
|
if(targetDate.ParseDateTime(sParseSrc))
|
|
m_CurrentDate = targetDate;
|
|
}
|
|
}
|
|
else
|
|
TRACE0("COXCalendar::GetCurrentDate() called with no active edit window\n");
|
|
return m_CurrentDate;
|
|
}
|
|
//Get text form edit window and convert it to date value, if possible
|
|
//After this, call CWnd::OnClose() to close window
|
|
void COXCalendar::OnClose()
|
|
{
|
|
GetCurrentDate();
|
|
CWnd::OnClose();
|
|
}
|
|
|
|
BOOL COXCalendar::SetCurrentDate(const COleDateTime &currDate)
|
|
{
|
|
if(currDate.GetStatus() == COleDateTime::invalid
|
|
|| currDate < m_StartDate || currDate > m_EndDate)
|
|
{
|
|
TRACE0("COXCalendar::SetCurrentDate: invalid value specified!");
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
m_CurrentDate = currDate;
|
|
if(::IsWindow(m_wndEdit.m_hWnd))
|
|
{
|
|
CString sText;
|
|
SetOutputText(sText);
|
|
m_wndEdit.SetWindowText(sText);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void COXCalendar::PreSubclassWindow()
|
|
// --- In :
|
|
// --- Out :
|
|
// --- Returns :
|
|
// --- Effect : called by the framework
|
|
// perform initialization
|
|
{
|
|
// TODO: Add your specialized code here and/or call the base class
|
|
_AFX_THREAD_STATE* pThreadState=AfxGetThreadState();
|
|
// hook not already in progress
|
|
if(pThreadState->m_pWndInit==NULL)
|
|
{
|
|
VERIFY(Init());
|
|
}
|
|
|
|
CWnd::PreSubclassWindow();
|
|
}
|
|
|
|
void COXCalendar::ShrinkDateInBounds()
|
|
// --- In :
|
|
// --- Out :
|
|
// --- Returns :
|
|
// --- Effect :check current date for valid range, correct if needed
|
|
{
|
|
if(m_StartDate.GetStatus() == COleDateTime::valid &&
|
|
m_CurrentDate.GetStatus() == COleDateTime::valid &&
|
|
m_CurrentDate < m_StartDate)
|
|
{
|
|
m_CurrentDate = m_StartDate;
|
|
return;
|
|
}
|
|
if(m_EndDate.GetStatus() == COleDateTime::valid &&
|
|
m_CurrentDate.GetStatus() == COleDateTime::valid &&
|
|
m_CurrentDate > m_EndDate)
|
|
{
|
|
m_CurrentDate = m_EndDate;
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
void COXCalendar::SetOutputText(CString& sOutputString)
|
|
{
|
|
if(m_CurrentDate.GetStatus() == COleDateTime::null)
|
|
{
|
|
sOutputString=m_wndDatePicker.m_sNone;
|
|
}
|
|
else
|
|
{
|
|
if(m_sFormat.IsEmpty())
|
|
{
|
|
sOutputString=ms_straWeekDays[(m_CurrentDate.GetDayOfWeek() + 5)%7];
|
|
sOutputString+=_T(' ') + m_CurrentDate.Format();
|
|
}
|
|
else
|
|
{
|
|
sOutputString=m_CurrentDate.Format(m_sFormat);
|
|
}
|
|
}
|
|
}
|
|
|
|
void COXCalendar::OnEnChange()
|
|
{
|
|
// --- In :
|
|
// --- Out :
|
|
// --- Returns :
|
|
// --- Effect notify parent about altered text in edit control
|
|
int id = GetDlgCtrlID();
|
|
DWORD nCode = EN_CHANGE;
|
|
nCode <<= 16;
|
|
DWORD wParam = nCode | id;
|
|
GetParent()->SendMessage(WM_COMMAND,wParam,(LPARAM)GetSafeHwnd());
|
|
}
|
|
|
|
void COXCalendar::OnEnUpdate()
|
|
{
|
|
// --- In :
|
|
// --- Out :
|
|
// --- Returns :
|
|
// --- Effect : notify parent about altered text in edit control
|
|
int id = GetDlgCtrlID();
|
|
DWORD nCode = EN_UPDATE;
|
|
nCode <<= 16;
|
|
DWORD wParam = nCode | id;
|
|
GetParent()->SendMessage(WM_COMMAND,wParam,(LPARAM)GetSafeHwnd());
|
|
}
|
|
|
|
void COXCalendar::PostNcDestroy()
|
|
{
|
|
// TODO: Add your specialized code here and/or call the base class
|
|
if(m_wndDatePicker.GetSafeHwnd())
|
|
m_wndDatePicker.DestroyWindow();
|
|
m_font.DeleteObject();
|
|
CWnd::PostNcDestroy();
|
|
}
|