mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-26 02:02:33 +00:00
583 lines
14 KiB
C++
583 lines
14 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "DateEdit.h"
|
|
|
|
|
|
#ifdef DEBUG_mitch
|
|
#define DEBUGONLY( stmt ) stmt;
|
|
#define DEBUGOUT(s) OutputDebugString(s)
|
|
#else
|
|
#define DEBUGONLY( stmt ) ;
|
|
#define DEBUGOUT(s)
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define ID_DAY 101
|
|
#define ID_MONTH 102
|
|
#define ID_YEAR 103
|
|
|
|
static int _GetMonthDays(int nYear, int nMonth)
|
|
{
|
|
static int cDaysInMonth[] = {
|
|
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
};
|
|
|
|
if( nYear % 100 != 0 && nYear % 4 == 0 && nMonth == 2)
|
|
return 29;
|
|
|
|
return cDaysInMonth[nMonth - 1];
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNSDateEdit public API
|
|
|
|
|
|
//=============================================================== CNSDateEdit
|
|
CNSDateEdit::CNSDateEdit()
|
|
{
|
|
m_bNeedControls = TRUE;
|
|
m_nDay = m_nMonth = m_nYear = 0;
|
|
m_iCurrentField = 0;
|
|
m_pFields[0] = m_pFields[1] = m_pFields[2] = NULL;
|
|
}
|
|
|
|
|
|
//============================================================== ~CNSDateEdit
|
|
CNSDateEdit::~CNSDateEdit()
|
|
{
|
|
}
|
|
|
|
|
|
//=================================================================== SetDate
|
|
BOOL CNSDateEdit::SetDate( CTime &d )
|
|
{
|
|
return SetDate( d.GetYear(), d.GetMonth(), d.GetDay() );
|
|
}
|
|
|
|
|
|
//=================================================================== SetDate
|
|
BOOL CNSDateEdit::SetDate( int nYear, int nMonth, int nDay )
|
|
{
|
|
m_nDay = nDay;
|
|
m_nMonth = nMonth;
|
|
m_nYear = nYear;
|
|
if ( !m_bNeedControls )
|
|
{
|
|
m_DayField.SetValue( m_nDay );
|
|
m_MonthField.SetValue( m_nMonth );
|
|
m_YearField.SetValue( m_nYear );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//=================================================================== GetDate
|
|
BOOL CNSDateEdit::GetDate( CTime &d )
|
|
{
|
|
int nYear, nMonth, nDay;
|
|
if ( !GetDate( nYear, nMonth, nDay ) )
|
|
return FALSE;
|
|
d = CTime( nYear, nMonth, nDay, 0, 0, 0 );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//=================================================================== GetDate
|
|
BOOL CNSDateEdit::GetDate( int &nYear, int &nMonth, int &nDay )
|
|
{
|
|
if (Validate()) {
|
|
nYear = m_nYear;
|
|
nMonth = m_nMonth;
|
|
nDay = m_nDay;
|
|
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNSDateEdit message handlers
|
|
|
|
BEGIN_MESSAGE_MAP(CNSDateEdit, CStatic)
|
|
ON_WM_PAINT()
|
|
ON_WM_SETFOCUS()
|
|
ON_WM_SIZE()
|
|
ON_WM_ENABLE()
|
|
ON_WM_GETDLGCODE()
|
|
ON_EN_MAXTEXT(ID_DAY, OnMaxText)
|
|
ON_EN_MAXTEXT(ID_MONTH, OnMaxText)
|
|
ON_EN_MAXTEXT(ID_YEAR, OnMaxText)
|
|
ON_EN_SETFOCUS(ID_DAY, OnFocusDay)
|
|
ON_EN_SETFOCUS(ID_MONTH, OnFocusMonth)
|
|
ON_EN_SETFOCUS(ID_YEAR, OnFocusYear)
|
|
ON_EN_KILLFOCUS(ID_DAY, OnKillFocusDay)
|
|
ON_EN_KILLFOCUS(ID_MONTH, OnKillFocusMonth)
|
|
ON_EN_KILLFOCUS(ID_YEAR, OnKillFocusYear)
|
|
ON_MESSAGE(NSDE_RELAYEVENT, OnRelayEvent)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
//=================================================================== OnPaint
|
|
void CNSDateEdit::OnPaint()
|
|
{
|
|
ASSERT( IsWindow( m_hWnd ) );
|
|
|
|
char szSeparator[2];
|
|
#ifdef WIN32
|
|
VERIFY( GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SDATE, szSeparator, 2 ) == 2 );
|
|
#else
|
|
static char cName [] = "intl" ;
|
|
GetProfileString (cName, "sDate", "/", szSeparator, 2) ;
|
|
#endif
|
|
|
|
BOOL bEnabled = IsWindowEnabled();
|
|
CPaintDC dc(this);
|
|
CBrush winBrush( GetSysColor( bEnabled ? COLOR_WINDOW : COLOR_BTNFACE ) );
|
|
dc.FillRect( &dc.m_ps.rcPaint, &winBrush );
|
|
|
|
if ( m_bNeedControls )
|
|
CreateSubWindows( );
|
|
|
|
CFont *pOldFont = dc.SelectObject( CFont::FromHandle( (HFONT)::GetStockObject( ANSI_VAR_FONT ) ) );
|
|
int oldMode = dc.SetBkMode( TRANSPARENT );
|
|
COLORREF oldTextColor = dc.SetTextColor( GetSysColor( bEnabled ? COLOR_BTNTEXT : COLOR_GRAYTEXT ) );
|
|
|
|
dc.DrawText( szSeparator, -1, m_Sep1, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
|
|
dc.DrawText( szSeparator, -1, m_Sep2, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
|
|
|
|
dc.SelectObject( pOldFont );
|
|
dc.SetBkMode( oldMode );
|
|
dc.SetTextColor( oldTextColor );
|
|
}
|
|
|
|
|
|
void CNSDateEdit::OnSetFocus(CWnd* pOldWnd)
|
|
{
|
|
if ( m_bNeedControls )
|
|
CreateSubWindows( );
|
|
|
|
m_pFields[0]->SetFocus();
|
|
m_iCurrentField = 0;
|
|
}
|
|
|
|
void CNSDateEdit::OnSize( UINT nType, int cx, int cy )
|
|
{
|
|
if ( m_bNeedControls )
|
|
CreateSubWindows( );
|
|
|
|
if ( nType != SIZE_MINIMIZED ) {
|
|
RECT r;
|
|
::SetRect( &r, 0, 0, cx, cy );
|
|
LayoutSubWindows( &r );
|
|
}
|
|
}
|
|
|
|
void CNSDateEdit::OnEnable( BOOL bEnable )
|
|
{
|
|
if ( m_bNeedControls )
|
|
CreateSubWindows( );
|
|
|
|
m_YearField.EnableWindow( bEnable );
|
|
m_MonthField.EnableWindow( bEnable );
|
|
m_DayField.EnableWindow( bEnable );
|
|
|
|
CStatic::OnEnable( bEnable );
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNSDateEdit protected & private methods
|
|
|
|
|
|
//========================================================== CreateSubWindows
|
|
void CNSDateEdit::CreateSubWindows( )
|
|
{
|
|
m_bNeedControls = FALSE;
|
|
|
|
RECT r, rcClient;
|
|
|
|
::SetRectEmpty(&r);
|
|
GetClientRect( &rcClient );
|
|
|
|
VERIFY( m_YearField.Create( WS_VISIBLE, r, this, (UINT)ID_YEAR ) );
|
|
m_YearField.LimitText( 4 );
|
|
|
|
if ( m_nYear != 0 )
|
|
m_YearField.SetValue( m_nYear );
|
|
|
|
VERIFY( m_MonthField.Create( WS_VISIBLE, r, this, (UINT)ID_MONTH ) );
|
|
m_MonthField.LimitText( 2 );
|
|
if ( m_nMonth != 0 )
|
|
m_MonthField.SetValue( m_nMonth );
|
|
|
|
VERIFY( m_DayField.Create( WS_VISIBLE, r, this, (UINT)ID_DAY ) );
|
|
m_DayField.LimitText( 2 );
|
|
if ( m_nDay != 0 )
|
|
m_DayField.SetValue( m_nDay );
|
|
|
|
m_wndSpin.Create(UDS_WRAP|UDS_ARROWKEYS|UDS_SETBUDDYINT|UDS_NOTHOUSANDS|WS_CHILD|WS_VISIBLE,
|
|
CRect(0,0,0,0), this, 104 );
|
|
|
|
LayoutSubWindows( &rcClient );
|
|
|
|
m_wndSpin.SetBuddy(m_pFields[0]);
|
|
if (m_pFields[0] == &m_DayField) {
|
|
int nDays = _GetMonthDays(m_nYear, m_nMonth);
|
|
m_wndSpin.SetRange(1, nDays);
|
|
m_wndSpin.SetPos(m_nDay);
|
|
} else if (m_pFields[0] == &m_MonthField) {
|
|
m_wndSpin.SetRange(1, 12);
|
|
m_wndSpin.SetPos(m_nMonth);
|
|
} else {
|
|
m_wndSpin.SetRange(1970, 2037);
|
|
m_wndSpin.SetPos(m_nMonth);
|
|
}
|
|
}
|
|
|
|
void CNSDateEdit::LayoutSubWindows( RECT *pRect )
|
|
{
|
|
HDC hDC = ::GetDC( m_hWnd );
|
|
ASSERT( hDC );
|
|
|
|
int w = pRect->right - pRect->left;
|
|
RECT r, rcSep, rcNum;
|
|
TCHAR szSeparator[2], szFormat[30], *s;
|
|
HFONT hFont = (HFONT)::GetStockObject( ANSI_VAR_FONT );
|
|
CFont *fontCur = CFont::FromHandle( hFont );
|
|
|
|
LOGFONT lf;
|
|
GetObject( hFont, sizeof(lf), &lf);
|
|
|
|
// Get locale info
|
|
#ifdef WIN32
|
|
VERIFY( GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, szFormat, 30 ) > 0 );
|
|
VERIFY( GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SDATE, szSeparator, 2 ) > 0 );
|
|
#else
|
|
static char cName [] = "intl" ;
|
|
GetProfileString (cName, "sDate", "/", szSeparator, 2) ;
|
|
|
|
int iDate = GetProfileInt (cName, "iDate", 0) ;
|
|
sprintf( szFormat, "%c%s%c%s%c",
|
|
iDate == 1 ? 'd' : iDate == 2 ? 'y' : 'M', szSeparator,
|
|
iDate == 1 ? 'M' : iDate == 2 ? 'M' : 'd', szSeparator,
|
|
iDate == 1 ? 'y' : iDate == 2 ? 'd' : 'y' );
|
|
#endif
|
|
|
|
// Get font info
|
|
::SetRectEmpty(&rcSep);
|
|
::SetRectEmpty(&rcNum);
|
|
|
|
DrawText( hDC, szSeparator, -1, &rcSep,
|
|
DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT );
|
|
DrawText( hDC, _T("0"), -1, &rcNum,
|
|
DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT );
|
|
|
|
ASSERT( w >= ( rcSep.right * 2 + rcNum.right * 8 ) );
|
|
|
|
// Start on the left
|
|
|
|
int iHeight = lf.lfHeight;
|
|
int iWidth = 0;
|
|
|
|
r.top = pRect->top - ((pRect->top-pRect->bottom)+iHeight)/2 - 1;
|
|
r.bottom = r.top + iHeight + 2;
|
|
|
|
r.right = pRect->left + 1;
|
|
|
|
// this loop parses the short date format, creating the fields as it goes
|
|
s = strtok( szFormat, szSeparator );
|
|
for( int i = 0; (i < 3) && (s != NULL); i++ )
|
|
{
|
|
switch ( s[0] )
|
|
{
|
|
case 'M':
|
|
case 'm':
|
|
r.left = r.right + 1;
|
|
r.right = r.left + rcNum.right * 2 + iWidth; // room for two characters
|
|
m_MonthField.MoveWindow( &r );
|
|
m_MonthField.SetFont( fontCur );
|
|
m_pFields[i] = &m_MonthField;
|
|
break;
|
|
case 'Y':
|
|
case 'y':
|
|
r.left = r.right + 1;
|
|
r.right = r.left + rcNum.right * 4 + iWidth; // room for four characters
|
|
m_YearField.MoveWindow( &r );
|
|
m_YearField.SetFont( fontCur );
|
|
m_pFields[i] = &m_YearField;
|
|
break;
|
|
case 'D':
|
|
case 'd':
|
|
r.left = r.right + 1;
|
|
r.right = r.left + rcNum.right * 2 + iWidth; // room for two characters
|
|
m_DayField.MoveWindow( &r );
|
|
m_DayField.SetFont( fontCur );
|
|
m_pFields[i] = &m_DayField;
|
|
break;
|
|
default:
|
|
DebugBreak();
|
|
}
|
|
if ( i == 0 )
|
|
{
|
|
r.left = r.right + 1;
|
|
r.right = r.left + rcSep.right;
|
|
m_Sep1 = r;
|
|
}
|
|
else if ( i == 1 )
|
|
{
|
|
r.left = r.right + 1;
|
|
r.right = r.left + rcSep.right;
|
|
m_Sep2 = r;
|
|
}
|
|
|
|
s = strtok( NULL, szSeparator );
|
|
}
|
|
|
|
r = *pRect;
|
|
r.left = r.right - GetSystemMetrics(SM_CXVSCROLL);
|
|
|
|
m_wndSpin.MoveWindow(&r);
|
|
|
|
::ReleaseDC( m_hWnd, hDC );
|
|
}
|
|
|
|
//================================================================ Validate
|
|
BOOL CNSDateEdit::Validate()
|
|
{
|
|
int nYear = m_YearField.GetValue();
|
|
int nMonth = m_MonthField.GetValue();
|
|
int nDay = m_DayField.GetValue();
|
|
|
|
BOOL res = TRUE;
|
|
|
|
if (nYear >= 1970 && nYear < 2038 && nMonth >= 1 && nMonth <= 12 && nDay >= 1 && nDay <= 31) {
|
|
CTime ctime(nYear, nMonth, nDay, 0, 0, 0);
|
|
|
|
res = (ctime.GetYear() == nYear) &&
|
|
(ctime.GetMonth() == nMonth) &&
|
|
(ctime.GetDay() == nDay);
|
|
} else {
|
|
res = FALSE;
|
|
}
|
|
|
|
if (res) {
|
|
m_nDay = nDay;
|
|
m_nMonth = nMonth;
|
|
m_nYear = nYear;
|
|
} else {
|
|
m_DayField.SetValue(m_nDay);
|
|
m_MonthField.SetValue(m_nMonth);
|
|
m_YearField.SetValue(m_nYear);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//================================================================ OnKeyPress
|
|
BOOL CNSDateEdit::OnKeyPress( UINT nKey, UINT nRepCnt, UINT nFlags )
|
|
{
|
|
if (nKey == VK_TAB || nKey == VK_SPACE)
|
|
{
|
|
// Don't move on to the next field/window if the current
|
|
// value is invalid.
|
|
if ( !Validate() )
|
|
{
|
|
MessageBeep( MB_OK );
|
|
m_pFields[m_iCurrentField]->SetSel( 0, -1 );
|
|
return TRUE;
|
|
}
|
|
// Move the focus to the next field or control
|
|
BOOL bShift = GetKeyState( VK_SHIFT ) & 0x8000;
|
|
|
|
if (bShift) {
|
|
if (m_iCurrentField > 0 || nKey == VK_SPACE)
|
|
m_iCurrentField += 2;
|
|
else
|
|
return FALSE;
|
|
} else {
|
|
if (m_iCurrentField < 2 || nKey == VK_SPACE)
|
|
m_iCurrentField += 1;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
m_iCurrentField %= 3;
|
|
|
|
m_pFields[m_iCurrentField]->SetFocus();
|
|
|
|
return TRUE; // handled
|
|
}
|
|
|
|
return FALSE; // not handled - let the child continue processing this key
|
|
}
|
|
|
|
//============================================================= OnMaxText
|
|
void CNSDateEdit::OnMaxText()
|
|
{
|
|
}
|
|
|
|
//============================================================= OnFocusDay
|
|
void CNSDateEdit::OnFocusDay()
|
|
{
|
|
int nDays = _GetMonthDays(m_nYear, m_nMonth);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
if (m_pFields[i] == &m_DayField)
|
|
m_iCurrentField = i;
|
|
}
|
|
m_wndSpin.SetBuddy(&m_DayField);
|
|
m_wndSpin.SetRange(1, nDays);
|
|
m_wndSpin.SetPos(m_nDay);
|
|
}
|
|
|
|
//============================================================= OnFocusMonth
|
|
void CNSDateEdit::OnFocusMonth()
|
|
{
|
|
for (int i = 0; i < 3; i++) {
|
|
if (m_pFields[i] == &m_MonthField)
|
|
m_iCurrentField = i;
|
|
}
|
|
m_wndSpin.SetBuddy(&m_MonthField);
|
|
m_wndSpin.SetRange(1, 12);
|
|
m_wndSpin.SetPos(m_nMonth);
|
|
}
|
|
|
|
//============================================================= OnFocusYear
|
|
void CNSDateEdit::OnFocusYear()
|
|
{
|
|
for (int i = 0; i < 3; i++) {
|
|
if (m_pFields[i] == &m_YearField)
|
|
m_iCurrentField = i;
|
|
}
|
|
m_wndSpin.SetBuddy(&m_YearField);
|
|
m_wndSpin.SetRange(1970, 2037);
|
|
m_wndSpin.SetPos(m_nYear);
|
|
}
|
|
|
|
//============================================================= OnKillFocusDay
|
|
void CNSDateEdit::OnKillFocusDay()
|
|
{
|
|
if (!Validate()) {
|
|
MessageBeep( MB_OK );
|
|
}
|
|
}
|
|
|
|
//============================================================= OnKillFocusMonth
|
|
void CNSDateEdit::OnKillFocusMonth()
|
|
{
|
|
if (!Validate()) {
|
|
MessageBeep( MB_OK );
|
|
}
|
|
}
|
|
|
|
//============================================================= OnKillFocusYear
|
|
void CNSDateEdit::OnKillFocusYear()
|
|
{
|
|
if (!Validate()) {
|
|
MessageBeep( MB_OK );
|
|
}
|
|
}
|
|
|
|
LRESULT CNSDateEdit::OnRelayEvent(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPMSG pMsg = (LPMSG) lParam;
|
|
if (pMsg->message == WM_KEYDOWN) {
|
|
return (LRESULT) OnKeyPress( (UINT) pMsg->wParam, (UINT) LOWORD(pMsg->lParam), (UINT) HIWORD(pMsg->lParam));
|
|
}
|
|
return (LRESULT) FALSE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNSDateSubedit
|
|
|
|
//============================================================ CNSDateSubedit
|
|
CNSDateSubedit::CNSDateSubedit()
|
|
{
|
|
}
|
|
|
|
|
|
//=========================================================== ~CNSDateSubedit
|
|
CNSDateSubedit::~CNSDateSubedit()
|
|
{
|
|
}
|
|
|
|
BOOL CNSDateSubedit::PreTranslateMessage( MSG* pMsg )
|
|
{
|
|
if (GetParent()->SendMessage(NSDE_RELAYEVENT, (WPARAM) 0, (LPARAM) pMsg))
|
|
return TRUE;
|
|
|
|
return CEdit::PreTranslateMessage(pMsg);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNSDateSubedit message handlers
|
|
|
|
BEGIN_MESSAGE_MAP(CNSDateSubedit, CEdit)
|
|
ON_WM_CHAR()
|
|
ON_WM_SETFOCUS()
|
|
END_MESSAGE_MAP()
|
|
|
|
//==================================================================== OnChar
|
|
void CNSDateSubedit::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags )
|
|
{
|
|
if ( ((nChar >= '0') && (nChar <= '9')) || (nChar == 0x08) ) {
|
|
CEdit::OnChar(nChar, nRepCnt, nFlags);
|
|
} else if (nChar == ' ') {
|
|
MSG msg;
|
|
msg.message = WM_KEYDOWN;
|
|
msg.wParam = (WPARAM) VK_SPACE;
|
|
GetParent()->SendMessage(NSDE_RELAYEVENT, (WPARAM) 0, (LPARAM) &msg);
|
|
} else {
|
|
MessageBeep( MB_OK );
|
|
}
|
|
}
|
|
|
|
//=============================================================== OnSetFocus
|
|
void CNSDateSubedit::OnSetFocus( CWnd* pOldWnd )
|
|
{
|
|
CEdit::OnSetFocus( pOldWnd );
|
|
SetSel(0,-1);
|
|
}
|
|
|
|
//================================================================== SetValue
|
|
int CNSDateSubedit::SetValue( int nNewValue )
|
|
{
|
|
char buff[10];
|
|
SetWindowText( itoa( nNewValue, buff, 10 ) );
|
|
return nNewValue;
|
|
}
|
|
|
|
|
|
//================================================================== GetValue
|
|
int CNSDateSubedit::GetValue( void )
|
|
{
|
|
char buff[10];
|
|
GetWindowText( buff, 10 );
|
|
if ( strlen( buff ) > 0 )
|
|
return atoi( buff );
|
|
return 0;
|
|
}
|