/////////////////////////////////////////////////////////////////////////////
// Name:        datectrl.cpp
// Purpose:     
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     04/01/98
// RCS-ID:      $Id: datectrl.cpp,v 1.4 2001/04/21 11:48:18 georgetasker Exp $
// Copyright:   (c) PM-Soft
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "selector.cpp"
    #pragma interface "selector.h"
#endif

#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include "wx/wspp/datectrl.h"
#include <math.h>

#define LOG(x) wxLogMessage(x);

/***** Implementation for class SelectorItemInfo *****/

SelectorItemInfo::SelectorItemInfo()

	: mType( -1 ),
	  mpCtrl( 0 )
{}

/***** Implementation for class wxDateCtrl *****/

IMPLEMENT_DYNAMIC_CLASS( wxDateCtrl, wxWindow )

BEGIN_EVENT_TABLE( wxDateCtrl, wxWindow )

	EVT_SIZE( wxDateCtrl::OnSize )
	EVT_SET_FOCUS( wxDateCtrl::OnSetFocus )

END_EVENT_TABLE()

wxDateCtrl::wxDateCtrl()

	: wxWindow()
{
	SetDefaults();
}

wxDateCtrl::wxDateCtrl( wxWindow* parent, wxWindowID id, 
						const wxPoint& pos, const wxSize& size, 
						const wxString& format,
						long style, const wxString& name )

	: wxWindow( parent, id, pos, size, style | wxTAB_TRAVERSAL, name )
{
	SetDefaults();

	mDefaultSizeUsed = size == wxDefaultSize;

	SetFormat( format );
	LayoutControls();
}

wxDateCtrl::~wxDateCtrl()
{
	WX_CLEAR_ARRAY( mItems );

	if ( mpSpy ) mpSpy->SpotSpying();

	// TBD:: delete mpSpy;
}

void wxDateCtrl::SetDefaults()
{
	SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOW ) );

	mpSpy = NULL;
	mItemGap = 0;
	mIsDateEntry = TRUE;
}

void wxDateCtrl::Create( wxWindow* parent, wxWindowID id, 
						 const wxPoint& pos, const wxSize& size, 
						 const wxString& format,
						 long style, const wxString& name )
{
	wxWindow::Create( parent, id, pos, size, style | wxTAB_TRAVERSAL, name );

	SetDefaults();

	mDefaultSizeUsed = size == wxDefaultSize;

	SetFormat( format );
	LayoutControls();
}

void wxDateCtrl::AddNumberRangeItem( int from, int till, 
										 const wxString& defaultValue,
										 char tagChar, bool padZeros )
{
	SelectorItemInfo* pInfo = new SelectorItemInfo();
	pInfo->mType = ST_NUMBER_RANGE;
	pInfo->mFrom = from;
	pInfo->mTill = till;
	pInfo->mPadZeros = padZeros;
	pInfo->mTagChar = tagChar;
	pInfo->mDefault = defaultValue;

	mItems.Add( pInfo );
}

void wxDateCtrl::AddSeparatorLabel( const wxString& label )
{
	SelectorItemInfo* pInfo = new SelectorItemInfo();
	pInfo->mType = ST_LABEL;
	pInfo->mDefault = label;

	mItems.Add( pInfo );
}

void wxDateCtrl::AddStringListItem( wxStringList& values, const wxString& defaultValue, char tagChar )
{
	SelectorItemInfo* pInfo = new SelectorItemInfo();
	int len = values.GetCount();
	pInfo->mType = ST_STRING_LIST;
	pInfo->mStrings = values;
	pInfo->mDefault = defaultValue;
	pInfo->mTagChar = tagChar;

	mItems.Add( pInfo );
}

SelectorItemInfo* wxDateCtrl::FindInfoForWindow( wxWindow* pWnd )
{
	for( size_t i = 0; i != mItems.Count(); ++i )
		
		if ( mItems[i]->mpCtrl == pWnd )
			
			return mItems[i];
		
	return NULL;
}

void wxDateCtrl::Clear()
{
	SelectorItemInfoListT lst = mItems;

	mItems.Clear();

	// destroy windows AFTER we wiped the info-array

	for( size_t i = 0; i != lst.Count(); ++i )
	{
		if ( lst[i]->mpCtrl != NULL )

			lst[i]->mpCtrl->Destroy();

		delete lst[i];
	}
}

SelectorItemInfoListT& wxDateCtrl::GetItems()
{
	return mItems;
}

void wxDateCtrl::SetItemGap( int pixels )
{
	mItemGap = pixels;
}

int wxDateCtrl::GetItemGap()
{
	return mItemGap;
}

// event handlers 

void wxDateCtrl::OnSize( wxSizeEvent& event )
{
	mDefaultSizeUsed = FALSE;

	LayoutControls();

	//event.Skip();
}

void wxDateCtrl::OnSetFocus( wxFocusEvent& event )
{
	if ( mItems.Count() )
	
		mItems[0]->mpCtrl->SetFocus();
}

wxSize wxDateCtrl::DoGetBestSize() const

{
	wxSize sz(0,0);

	for( size_t i = 0; i != mItems.Count(); i++ )
	{
		SelectorItemInfo& item = *mItems[i];

		sz.x += item.mSize.x;
		sz.y = wxMax( item.mSize.y, sz.y );

		if ( i != 0 ) sz.x += mItemGap;
	}

	if ( GetWindowStyle() & wxSUNKEN_BORDER )
	{
		sz.x += GetCharWidth();
		sz.y += 2;
	}
	
	return sz;
}

void wxDateCtrl::SetBgRecursive( wxWindow* pWnd, const wxColour& col )
{
	pWnd->SetBackgroundColour( col );

	wxWindowList& lst = pWnd->GetChildren();

	wxNode* pNode = lst.First();

	while( pNode )
	{
		wxWindow* pChild = (wxWindow*)pNode->GetData();

		SetBgRecursive( pChild, col );
		pNode = pNode->GetNext();
	}
}

bool wxDateCtrl::Enable(bool enable)
{
	bool ok = wxWindow::Enable( enable );

	for( size_t i = 0; i != mItems.Count(); i++ )
	{
		SelectorItemInfo& item = *mItems[i];

		if ( item.mpCtrl )

			item.mpCtrl->Enable( enable );
	}

	wxColour c;

	if ( enable )

		c = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOW   );
	else
		c = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_INACTIVEBORDER );

	this->SetBgRecursive( this, c );
	this->SetBackgroundColour( c );
	this->Refresh();

	return ok;
}

/*** protected methods ***/

static wxString IntToStr( int i )
{
	char buf[50];
	sprintf( buf, "%d", i );
	return wxString(buf);
}

void wxDateCtrl::CreateControls()
{
	if ( !(mItems.Count() && mItems[0]->mpCtrl == NULL) )

		return; // create only once

	for( size_t i = 0; i != mItems.Count(); i++ )
	{
		SelectorItemInfo& item = *mItems[i];

		if ( item.mType == ST_NUMBER_RANGE )
		{
			wxTextCtrl* pCtrl = new wxTextCtrl( this, -1, item.mDefault, wxDefaultPosition, wxDefaultSize,
												wxNO_3D | wxNO_BORDER );

			item.mpCtrl = pCtrl;

			int d = floor( log10( (double)item.mTill ) ) + 1;

			wxString s = "";
			while( d ) 
			{
				s += "9";
				--d;
			}

			GetTextSize( s, item.mSize );

			wxSize sz;
			GetTextSize( "9", sz );
			item.mSize.x += sz.x; // FIXME:: hack, somehow it's not enough

			//LOG( "Item width = " + IntToStr(item.mSize.x) + " height " + IntToStr(item.mSize.y) );
		}
		else
		if ( item.mType == ST_STRING_LIST )
		{
			wxListBox* pCtrl = new wxListBox( this, -1, wxDefaultPosition, wxDefaultSize, 0,
											  NULL, wxNO_3D | wxNO_BORDER  );

			item.mSize = wxSize(0,0);

			item.mpCtrl = pCtrl; int sel = 0;

			// populate the list and find out the widest text-item

			for( size_t i = 0; i != item.mStrings.GetCount(); ++i )
			{
				//pCtrl->Append( item.mStrings[i] );
				wxSize sz; GetTextSize( item.mStrings[i], sz );

				item.mSize.x = wxMax( item.mSize.x, sz.x );
				item.mSize.y = wxMax( item.mSize.y, sz.y );

				if ( item.mStrings[i] == item.mDefault ) sel = i;
			}

			pCtrl->Append( item.mDefault );

			// set default value

			//if ( item.mStrings.GetCount() )

			//	pCtrl->SetSelection( sel );
		}
		else
		if ( item.mType == ST_LABEL )
		{
			wxStaticText* pCtrl = new wxStaticText( this, -1, item.mDefault, wxDefaultPosition );

			item.mpCtrl = pCtrl;

			GetTextSize( item.mDefault, item.mSize );
		}

		if ( !mpSpy ) mpSpy = new wxSelectorKeySpy( this );

		item.mpCtrl->SetNextHandler( mpSpy );
	}
}

void wxDateCtrl::LayoutControls()
{
	CreateControls();

	int w = 0, h = 0; 
	GetClientSize( &w, &h );

	int innerWidth = 0;

	size_t i = 0;
	for( ; i != mItems.Count(); i++ )
	{
		if ( i != 0 ) 
			innerWidth += mItemGap;

		innerWidth += (*mItems[i]).mSize.x;
	}

	int x = ( w - innerWidth ) / 2;
	int y = ( h - GetCharHeight() ) / 2;

	for( i = 0; i != mItems.Count(); ++i )
	{
		SelectorItemInfo& item = *mItems[i];

		item.mpCtrl->SetClientSize( item.mSize.x, item.mSize.y );
		item.mpCtrl->Move( x,y);

		if ( item.mType == ST_STRING_LIST )
		{
			/*
			// OLD STUFF::
			wxWindow* pCtrl = item.mpCtrl;
			HWND hwnd = (HWND)pCtrl->GetHandle();
			DWORD style = ::GetWindowLong( (HWND)pCtrl->GetHandle(), GWL_STYLE );
			bool hasScroll = (style & WS_VSCROLL) != 0;
			style = style & ~WS_VSCROLL;
			style = style & ~WS_HSCROLL;
			style = style & ~WS_EX_RIGHTSCROLLBAR;
			hasScroll = (style & WS_VSCROLL) != 0;
			//::SetWindowLong( (HWND)pCtrl->GetHandle(), GWL_STYLE, style );
			::SetWindowLong( (HWND)pCtrl->GetHandle(), GWL_STYLE, (DWORD)0x50030001 );
			::SetWindowLong( (HWND)pCtrl->GetHandle(), GWL_EXSTYLE, 0x4 );

			style = ::GetWindowLong( (HWND)pCtrl->GetHandle(), GWL_EXSTYLE );
			style = style & ~WS_EX_RIGHTSCROLLBAR;
			::SetWindowLong( (HWND)pCtrl->GetHandle(), GWL_EXSTYLE, style );

			style = ::GetWindowLong( (HWND)pCtrl->GetHandle(), GWL_STYLE );
			hasScroll = (style & WS_VSCROLL) != 0;
			int k; ++k;
			pCtrl->Refresh();
			*/
		}

		x += item.mSize.x + mItemGap;
	}

	this->Refresh();
}

void wxDateCtrl::GetTextSize( const wxString& text, wxSize& sz )
{
	wxClientDC dc( this );
	dc.SetFont( this->GetFont() );
	wxCoord w,h;

	dc.GetTextExtent( text, &w,&h );

	sz.x = w; sz.y = h;
}

bool wxDateCtrl::SetFormat( const wxString& fmt )
{
	this->Clear();

	mFormat = fmt;

	size_t i = 0;

	while( i < fmt.Length() )
	{
		wxString label = "";

		// collect characters of preceeding label (if any) 

		while( i < fmt.Length() && fmt[i] != '%' )
		{
			label += fmt[i];
			++i;
		}

		if ( label.Length() )
			
			AddSeparatorLabel( label );

		++i; // skip '%'
		if ( i >= fmt.Length() ) 
			break;

		char f = fmt[i];

		if ( f == 'a' ) // %a - Abbreviated weekday name
		
			AddStringListItem( GetWeekDeyNamesAbr(), 'a' );
		else
		if ( f == 'A' ) // %A - Full weekday name

			AddStringListItem( GetWeekDeyNames(), 'A' );
		else
		if ( f == 'b' ) // %b - Abbreviated month name

			AddStringListItem( GetMonthNamesAbr(), 'b' );
		else
		if ( f == 'B' ) // %B - Full month name

			AddStringListItem( GetMonthNames(), 'B' );
		else
		if ( f == 'H' ) // %H - Hour in 24-hour format (00  23)

			AddNumberRangeItem( 0, 23, "0", 'H' );
		else
		if ( f == 'I' ) // %I - Hour in 12-hour format (01  12)

			AddNumberRangeItem( 1, 12, "0", 'I' );
		else
		if ( f == 'm' ) // %m - Month as decimal number (01  12)

			AddNumberRangeItem( 1, 12, "1", 'm' );
		else
		if ( f == 'd' ) // %d - Day of month as decimal number (01  31)

			AddNumberRangeItem( 1, 31, "1", 'd' );
		else
		if ( f == 'M' ) // %M - Minute as decimal number (00  59)

			AddNumberRangeItem( 0, 59, "0", 'M' );
		else
		if ( f == 'p' ) // %p - Current locales A.M./P.M. indicator for 12-hour clock

			AddStringListItem( GetAMPMNames(), 'p' );
		else
		if ( f == 'S' ) // %S - Second as decimal number (00  59)

			AddNumberRangeItem( 0, 59, "0", 'S' );
		else
		if ( f == 'y' ) // %y - Year without century, as decimal number (00  99)

			AddNumberRangeItem( 0, 99, "99", 'y' );
		else
		if ( f == 'Y' ) // %Y - Year with century, as decimal number

			AddNumberRangeItem( 0, 3000, "2000", 'Y' );
		else
			; // the rest is ignored (unsupported)

		++i; // skip control-tag  (TBD:: regard for %%)
	}

	CreateControls();

	if ( mDefaultSizeUsed )
	{
		wxSize sz = DoGetBestSize();
		SetSize( sz.x, sz.y );
	}

	return TRUE;
}

static int is_alpha_num( char ch )
{
	return ( ch >= '0' && ch <= '9' ) ||
		   ( ch >= 'A' && ch <= 'Z' ) ||
		   ( ch >= 'a' && ch <= 'z' );
}

static int is_num( char ch )
{
	return ( ch >= '0' && ch <= '9' );
}

bool wxDateCtrl::SetValue( const wxDateTime& dt )
{
	CreateControls();

	wxString s = dt.Format( mFormat );
	size_t n = 0;

	for( size_t i = 0; i != mItems.Count(); ++i )
	{
		if ( n >= s.Length() ) break;

		SelectorItemInfo& item = *mItems[i];

		if ( item.mType == ST_LABEL )
		
			n += item.mDefault.Length(); // skip all characters introduced by label
		else
		if ( item.mType == ST_STRING_LIST )
		{
			wxString val;

			while( n < s.Length() && is_alpha_num( s[n] ) ) 
			{
				val += s[n];
				++n;
			}

			wxListBox* pBox = (wxListBox*)item.mpCtrl;

			pBox->Clear();
			pBox->Append( val );
			pBox->SetSelection( 0, FALSE );
		}
		else
		if ( item.mType == ST_NUMBER_RANGE )
		{
			wxString val;

			while( n < s.Length() && is_num( s[n] ) ) 
			{
				val += s[n];
				++n;
			}

			((wxTextCtrl*)item.mpCtrl)->SetValue( val );
		}
	}

	return TRUE;
}

bool wxDateCtrl::GetValue( wxDateTime& toDt, const wxDateTime& dateDef )
{
	wxString s = "";

	for( size_t i = 0; i != mItems.Count(); ++i )
	{
		SelectorItemInfo& item = *mItems[i];

		if ( item.mType == ST_NUMBER_RANGE )
		
			s += ((wxTextCtrl*)item.mpCtrl)->GetValue();
		else
		if ( item.mType == ST_STRING_LIST )

			s += ((wxListBox*)item.mpCtrl)->GetString(0);
		else
		if ( item.mType == ST_LABEL )

			s += item.mDefault;
		else
		{
			int avoidCompilerWarning = 0;
			wxASSERT(avoidCompilerWarning); // DBG:: bogous item-type
		}
	}

	return toDt.ParseFormat( s, mFormat, dateDef ) != NULL;
}

const wxString& wxDateCtrl::GetFormat()
{
	return mFormat;
}

/*** protected methods ***/

// FIXME:: replace hard-coded names with wxDateTime::GetMonth/etc-Name(...)
//		   to regard current locale

static wxStringList _gWeekDaysEn( 
	"Sunday", "Monday", "Tuesday", "Wernesday", 
	"Thirdsday", "Friday", "Saturday", NULL );

static wxStringList _gWeekDaysAbrEn( 
	"Sun", "Mon", "Tue", "Wen", 
	"Thi", "Fri", "Sat", NULL );

static wxStringList _gMonthEn(
	"January", "February", "March", "April",
	"May", "June", "Juli", "August",
	"Sepetember", "October", "November", "December", NULL );

static wxStringList _gMonthAbrEn(
	"Jan", "Feb", "Mar", "Apr",
	"May", "Jun", "Jul", "Aug",
	"Sep", "Oct", "Nov", "Dec", NULL );

static wxStringList _gAMPMEn( "AM", "PM", NULL );

wxStringList& wxDateCtrl::GetMonthNames()
{
	return _gMonthEn;
}

wxStringList& wxDateCtrl::GetWeekDeyNames()
{
	return _gWeekDaysEn;
}

wxStringList& wxDateCtrl::GetMonthNamesAbr()
{
	return _gMonthAbrEn;
}

wxStringList& wxDateCtrl::GetWeekDeyNamesAbr()
{
	return _gWeekDaysAbrEn;
}

wxStringList& wxDateCtrl::GetAMPMNames()
{
	return _gAMPMEn;
}

SelectorItemInfo* wxDateCtrl::FindItemByTagChar( char ch )
{
	for( size_t i = 0; i != mItems.Count(); ++i )

		if ( mItems[i]->mTagChar == ch )

			return mItems[i];

	return NULL;
}

int wxDateCtrl::GetYear()
{
	SelectorItemInfo* pYearItem = FindItemByTagChar( 'Y' );

	if ( pYearItem == NULL )
		return -1;

	return atoi( ((wxTextCtrl*)pYearItem->mpCtrl)->GetValue() );
}

int wxDateCtrl::GetMonth()
{
	SelectorItemInfo* pYearItem = FindItemByTagChar( 'm' );

	if ( pYearItem == NULL )
		return -1;

	return atoi( ((wxTextCtrl*)pYearItem->mpCtrl)->GetValue() );
}

void wxDateCtrl::FixUpDayFieldsByMonth( int month )
{
	int year = GetYear();

	if ( year == -1 )
		return;

	if ( month == -1 )
	{
		month = GetMonth();
		if ( month == -1 )
			return;
	}

	for( size_t i = 0; i != mItems.Count(); ++i )
	{
		SelectorItemInfo& item = *mItems[i];

		if ( item.mTagChar == 'd' )
		{
			int day = atoi( ((wxTextCtrl*)item.mpCtrl)->GetValue() );

			int maxDays = wxDateTime::GetNumberOfDays( (wxDateTime::Month)(month-1), 
													   (wxDateTime::Calendar)year, 
													   wxDateTime::Gregorian );

			if ( day > maxDays )
			{
				((wxTextCtrl*)item.mpCtrl)->
					SetValue( GetDisplayStringForNumber( maxDays, &item ) );
			}
		}
	}
}

wxString wxDateCtrl::GetDisplayStringForNumber( int n, SelectorItemInfo* pItem )
{
	if ( n >= pItem->mTill ) n = pItem->mTill;
	if ( n <= pItem->mFrom ) n = pItem->mFrom;

	wxString val = IntToStr( n );

	if ( pItem->mPadZeros )
	{
		double a1 = floor( log10( (double)pItem->mTill ) );
		double a2 = floor( log10( (double)n ) );
		if ( n == 0 ) a2 = 0.0;

		int nPad = (int)( a1 - a2 );

		while( nPad-- ) 
		
			val = "0" + val;
	}

	return val;
}

/***** Implementation for class wxSelectorKeySpy *****/

BEGIN_EVENT_TABLE( wxSelectorKeySpy, wxEvtHandler )

	EVT_KEY_DOWN( wxSelectorKeySpy::OnKeyDown )
	EVT_KILL_FOCUS( wxSelectorKeySpy::OnKillFocus )
	EVT_SET_FOCUS( wxSelectorKeySpy::OnSetFocus )

END_EVENT_TABLE()

wxSelectorKeySpy::wxSelectorKeySpy( wxDateCtrl* pSelector )

	: mpSelector( pSelector )
{}

void wxSelectorKeySpy::OnKeyDown( wxKeyEvent& event )
{
	event.Skip();

	if ( !mpSelector ) return;

	SelectorItemInfo* pItem = mpSelector->FindInfoForWindow( (wxWindow*)event.GetEventObject() );

	int code = event.GetKeyCode();

	/*
	// OLD STUFF::
	if ( pItem && code == WXK_TAB )
	{
		SelectorItemInfoListT& items = mpSelector->GetItems();
		size_t n = items.Index( pItem ) + 1;
		size_t cur_n = n - 1;

		event.Skip( FALSE );

		for(; n != cur_n; ++n)
		{
			if ( n > items.Count() ) n = 0;

			pItem = items[n];

			if ( pItem->mType != wxDateCtrl::ST_LABEL )
			{
				pItem->mpCtrl->SetFocus();
				break;
			}
		}
	}
	*/

	if ( pItem && ( code == WXK_UP || code == WXK_DOWN ) 
		 && pItem->mType == wxDateCtrl::ST_NUMBER_RANGE )
	{
		wxTextCtrl* pText = (wxTextCtrl*)pItem->mpCtrl;
		int n = atoi( pText->GetValue() );

		if ( code == WXK_DOWN ) --n;
		if ( code == WXK_UP ) ++n;

		wxString val = mpSelector->GetDisplayStringForNumber( n, pItem );

		pText->SetInsertionPoint( val.Length() );
		pText->SetValue( val );
		pText->SetInsertionPoint( val.Length() );
		pText->SetSelection( 0, 1000 );
		event.Skip( FALSE );
	}
	else
	if ( pItem && ( code == WXK_UP || code == WXK_DOWN || code == WXK_LEFT || code == WXK_RIGHT ) 
		 && pItem->mType == wxDateCtrl::ST_STRING_LIST )
	{
		wxListBox* pBox = (wxListBox*)pItem->mpCtrl;
		wxString sel = pBox->GetStringSelection();

		for( size_t i = 0; i != pItem->mStrings.GetCount(); ++i )
		{
			if ( pItem->mStrings[i] == sel )
			{
				int idx = i;
				if ( code == WXK_DOWN || code == WXK_LEFT ) --idx;
				if ( code == WXK_UP   || code == WXK_RIGHT ) ++idx;

				if ( idx >= (int)pItem->mStrings.GetCount() )

					idx = (int)pItem->mStrings.GetCount()-1;

				if ( idx < 0 ) idx = 0;

				pBox->Clear();
				wxString s = pItem->mStrings[idx];
				pBox->Append( pItem->mStrings[idx] );
				pBox->SetSelection( 0, TRUE );

				// FIXME:: this is excessive, hack to fix up dirt...

				SelectorItemInfoListT& items = mpSelector->GetItems();
				int n = items.Index( pItem );
				for( size_t k = n + 1; k < items.Count(); ++k )

					items[k]->mpCtrl->Refresh();
			}
		}

		event.Skip( FALSE );
	}

	if ( event.GetSkipped() == FALSE )

		mpSelector->FixUpDayFieldsByMonth();
}

void wxSelectorKeySpy::OnKillFocus( wxFocusEvent& event )
{
	event.Skip();

	if ( !mpSelector ) return;

	SelectorItemInfo* pItem = mpSelector->FindInfoForWindow( (wxWindow*)event.GetEventObject() );

	if ( pItem && pItem->mType == wxDateCtrl::ST_STRING_LIST )

		((wxListBox*)pItem->mpCtrl)->SetSelection( 0, FALSE );
	else
	if ( pItem && pItem->mType == wxDateCtrl::ST_NUMBER_RANGE )
	{
		// FIXME:: FOR NOW:: validation and auto-corection are silent

		wxTextCtrl* pText = (wxTextCtrl*)pItem->mpCtrl;

		wxString before = pText->GetValue();

		int n = atoi( pText->GetValue() );

		wxString after = mpSelector->GetDisplayStringForNumber( n, pItem );

		if ( before != after )

			pText->SetValue( after );
	}
}

void wxSelectorKeySpy::OnSetFocus( wxFocusEvent& event )
{
	event.Skip();

	if ( !mpSelector ) return;

	SelectorItemInfo* pItem = mpSelector->FindInfoForWindow( (wxWindow*)event.GetEventObject() );

	if ( pItem )
	{
		if ( pItem->mType == wxDateCtrl::ST_STRING_LIST )
	
			((wxListBox*)pItem->mpCtrl)->SetSelection( 0, TRUE );
		else
		if ( pItem->mType == wxDateCtrl::ST_NUMBER_RANGE )
		
			((wxTextCtrl*)pItem->mpCtrl)->SetSelection( 0, 1000 );
	}
}

void wxSelectorKeySpy::SpotSpying()
{
	mpSelector = NULL;
}
