2025-11-27 16:46:48 +09:00

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();
}