X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c8074be06b2ee7cd5f6d7a90e6b4bf392415fc51..079f4130b861d094c79e0952966caad8b85dab67:/src/propgrid/advprops.cpp diff --git a/src/propgrid/advprops.cpp b/src/propgrid/advprops.cpp index 71a57d4cb1..83d392b310 100644 --- a/src/propgrid/advprops.cpp +++ b/src/propgrid/advprops.cpp @@ -4,7 +4,7 @@ // Author: Jaakko Salli // Modified by: // Created: 2004-09-25 -// RCS-ID: $Id: +// RCS-ID: $Id$ // Copyright: (c) Jaakko Salli // Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// @@ -51,15 +51,15 @@ #define __wxPG_SOURCE_FILE__ -#include +#include "wx/propgrid/propgrid.h" #if wxPG_INCLUDE_ADVPROPS -#include +#include "wx/propgrid/advprops.h" #ifdef __WXMSW__ - #include - #include + #include "wx/msw/private.h" + #include "wx/msw/dc.h" #endif // ----------------------------------------------------------------------- @@ -80,11 +80,6 @@ // ----------------------------------------------------------------------- -bool operator == (const wxFont&, const wxFont&) -{ - return false; -} - // Implement dynamic class for type value. IMPLEMENT_DYNAMIC_CLASS(wxColourPropertyValue, wxObject) @@ -113,9 +108,134 @@ bool operator == (const wxArrayInt& array1, const wxArrayInt& array2) #if wxUSE_SPINBTN -// This macro also defines global wxPGEditor_SpinCtrl for storing -// the singleton class instance. -WX_PG_IMPLEMENT_EDITOR_CLASS(SpinCtrl,wxPGSpinCtrlEditor,wxPGEditor) +#ifdef __WXMSW__ + #define IS_MOTION_SPIN_SUPPORTED 1 +#else + #define IS_MOTION_SPIN_SUPPORTED 0 +#endif + +#if IS_MOTION_SPIN_SUPPORTED + +// +// This class implements ability to rapidly change "spin" value +// by moving mouse when one of the spin buttons is depressed. +class wxPGSpinButton : public wxSpinButton +{ +public: + wxPGSpinButton() : wxSpinButton() + { + m_bLeftDown = false; + m_hasCapture = false; + m_spins = 1; + + Connect( wxEVT_LEFT_DOWN, + wxMouseEventHandler(wxPGSpinButton::OnMouseEvent) ); + Connect( wxEVT_LEFT_UP, + wxMouseEventHandler(wxPGSpinButton::OnMouseEvent) ); + Connect( wxEVT_MOTION, + wxMouseEventHandler(wxPGSpinButton::OnMouseEvent) ); + Connect( wxEVT_MOUSE_CAPTURE_LOST, + wxMouseCaptureLostEventHandler(wxPGSpinButton::OnMouseCaptureLost) ); + } + + int GetSpins() const + { + return m_spins; + } + +private: + wxPoint m_ptPosition; + + // Having a separate spins variable allows us to handle validation etc. for + // multiple spin events at once (with quick mouse movements there could be + // hundreds of 'spins' being done at once). Technically things like this + // should be stored in event (wxSpinEvent in this case), but there probably + // isn't anything there that can be reliably reused. + int m_spins; + + bool m_bLeftDown; + + // SpinButton seems to be a special for mouse capture, so we may need track + // privately whether mouse is actually captured. + bool m_hasCapture; + + void Capture() + { + if ( !m_hasCapture ) + { + CaptureMouse(); + m_hasCapture = true; + } + + SetCursor(wxCURSOR_SIZENS); + } + void Release() + { + m_bLeftDown = false; + + if ( m_hasCapture ) + { + ReleaseMouse(); + m_hasCapture = false; + } + + wxWindow *parent = GetParent(); + if ( parent ) + SetCursor(parent->GetCursor()); + else + SetCursor(wxNullCursor); + } + + void OnMouseEvent(wxMouseEvent& event) + { + if ( event.GetEventType() == wxEVT_LEFT_DOWN ) + { + m_bLeftDown = true; + m_ptPosition = event.GetPosition(); + } + else if ( event.GetEventType() == wxEVT_LEFT_UP ) + { + Release(); + m_bLeftDown = false; + } + else if ( event.GetEventType() == wxEVT_MOTION ) + { + if ( m_bLeftDown ) + { + int dy = m_ptPosition.y - event.GetPosition().y; + if ( dy ) + { + Capture(); + m_ptPosition = event.GetPosition(); + + wxSpinEvent evtscroll( (dy >= 0) ? wxEVT_SCROLL_LINEUP : + wxEVT_SCROLL_LINEDOWN, + GetId() ); + evtscroll.SetEventObject(this); + + wxASSERT( m_spins == 1 ); + + m_spins = abs(dy); + GetEventHandler()->ProcessEvent(evtscroll); + m_spins = 1; + } + } + } + + event.Skip(); + } + void OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event)) + { + Release(); + } +}; + +#endif // IS_MOTION_SPIN_SUPPORTED + + +WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(SpinCtrl, + wxPGSpinCtrlEditor, + wxPGEditor) // Trivial destructor. @@ -133,7 +253,19 @@ wxPGWindowList wxPGSpinCtrlEditor::CreateControls( wxPropertyGrid* propgrid, wxP wxSize tcSz(sz.x - butSz.x - margin, sz.y); wxPoint butPos(pos.x + tcSz.x + margin, pos.y); - wxSpinButton* wnd2 = new wxSpinButton(); + wxSpinButton* wnd2; + +#if IS_MOTION_SPIN_SUPPORTED + if ( property->GetAttributeAsLong(wxT("MotionSpin"), 0) ) + { + wnd2 = new wxPGSpinButton(); + } + else +#endif + { + wnd2 = new wxSpinButton(); + } + #ifdef __WXMSW__ wnd2->Hide(); #endif @@ -142,16 +274,6 @@ wxPGWindowList wxPGSpinCtrlEditor::CreateControls( wxPropertyGrid* propgrid, wxP wnd2->SetRange( INT_MIN, INT_MAX ); wnd2->SetValue( 0 ); - propgrid->Connect( wxPG_SUBID2, wxEVT_SCROLL_LINEUP, - (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) - &wxPropertyGrid::OnCustomEditorEvent, NULL, propgrid ); - propgrid->Connect( wxPG_SUBID2, wxEVT_SCROLL_LINEDOWN, - (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) - &wxPropertyGrid::OnCustomEditorEvent, NULL, propgrid ); - propgrid->Connect( wxPG_SUBID1, wxEVT_KEY_DOWN, - (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) - &wxPropertyGrid::OnCustomEditorEvent, NULL, propgrid ); - // Let's add validator to make sure only numbers can be entered wxTextValidator validator(wxFILTER_NUMERIC, &m_tempString); @@ -167,6 +289,7 @@ bool wxPGSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* proper { int evtType = event.GetEventType(); int keycode = -1; + int spins = 1; bool bigStep = false; if ( evtType == wxEVT_KEY_DOWN ) @@ -192,6 +315,17 @@ bool wxPGSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* proper if ( evtType == wxEVT_SCROLL_LINEUP || evtType == wxEVT_SCROLL_LINEDOWN ) { + #if IS_MOTION_SPIN_SUPPORTED + if ( property->GetAttributeAsLong(wxT("MotionSpin"), 0) ) + { + wxPGSpinButton* spinButton = + (wxPGSpinButton*) propgrid->GetEditorControlSecondary(); + + if ( spinButton ) + spins = spinButton->GetSpins(); + } + #endif + wxString s; // Can't use wnd since it might be clipper window wxTextCtrl* tc = wxDynamicCast(propgrid->GetEditorControl(), wxTextCtrl); @@ -217,6 +351,8 @@ bool wxPGSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* proper if ( bigStep ) step *= 10.0; + step *= (double) spins; + if ( evtType == wxEVT_SCROLL_LINEUP ) v_d += step; else v_d -= step; @@ -241,6 +377,8 @@ bool wxPGSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* proper if ( bigStep ) step *= 10; + step *= spins; + if ( evtType == wxEVT_SCROLL_LINEUP ) v_ll += step; else v_ll -= step; @@ -279,17 +417,20 @@ bool wxPGSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* proper #if wxUSE_DATEPICKCTRL -#include -#include +#include "wx/datectrl.h" +#include "wx/dateevt.h" class wxPGDatePickerCtrlEditor : public wxPGEditor { - WX_PG_DECLARE_EDITOR_CLASS(wxPGDatePickerCtrlEditor) + DECLARE_DYNAMIC_CLASS(wxPGDatePickerCtrlEditor) public: virtual ~wxPGDatePickerCtrlEditor(); - wxPG_DECLARE_CREATECONTROLS - + wxString GetName() const; + virtual wxPGWindowList CreateControls(wxPropertyGrid* propgrid, + wxPGProperty* property, + const wxPoint& pos, + const wxSize& size) const; virtual void UpdateControl( wxPGProperty* property, wxWindow* wnd ) const; virtual bool OnEvent( wxPropertyGrid* propgrid, wxPGProperty* property, wxWindow* wnd, wxEvent& event ) const; @@ -298,7 +439,9 @@ public: }; -WX_PG_IMPLEMENT_EDITOR_CLASS(DatePickerCtrl,wxPGDatePickerCtrlEditor,wxPGEditor) +WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(DatePickerCtrl, + wxPGDatePickerCtrlEditor, + wxPGEditor) wxPGDatePickerCtrlEditor::~wxPGDatePickerCtrlEditor() @@ -325,20 +468,20 @@ wxPGWindowList wxPGDatePickerCtrlEditor::CreateControls( wxPropertyGrid* propgri #else wxSize useSz = sz; #endif + + wxDateTime dateValue(wxInvalidDateTime); + + wxVariant value = prop->GetValue(); + if ( value.GetType() == wxT("datetime") ) + dateValue = value.GetDateTime(); + ctrl->Create(propgrid->GetPanel(), wxPG_SUBID1, - prop->GetDateValue(), + dateValue, pos, useSz, prop->GetDatePickerStyle() | wxNO_BORDER); - // Connect all required events to grid's OnCustomEditorEvent - // (all relevenat wxTextCtrl, wxComboBox and wxButton events are - // already connected) - propgrid->Connect( wxPG_SUBID1, wxEVT_DATE_CHANGED, - (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) - &wxPropertyGrid::OnCustomEditorEvent ); - #ifdef __WXMSW__ ctrl->Show(); #endif @@ -393,8 +536,8 @@ void wxPGDatePickerCtrlEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(pro // wxFontProperty // ----------------------------------------------------------------------- -#include -#include +#include "wx/fontdlg.h" +#include "wx/fontenum.h" static const wxChar* gs_fp_es_family_labels[] = { wxT("Default"), wxT("Decorative"), @@ -451,14 +594,7 @@ wxFontProperty::wxFontProperty( const wxString& label, const wxString& name, // Initialize font family choices list if ( !wxPGGlobalVars->m_fontFamilyChoices ) { - wxFontEnumerator enumerator; - enumerator.EnumerateFacenames(); - -#if wxMINOR_VERSION > 6 - wxArrayString faceNames = enumerator.GetFacenames(); -#else - wxArrayString& faceNames = *enumerator.GetFacenames(); -#endif + wxArrayString faceNames = wxFontEnumerator::GetFacenames(); faceNames.Sort(); @@ -470,6 +606,8 @@ wxFontProperty::wxFontProperty( const wxString& label, const wxString& name, wxFont font; font << m_value; + SetParentalType(wxPG_PROP_AGGREGATE); + AddChild( new wxIntProperty( _("Point Size"), wxS("Point Size"),(long)font.GetPointSize() ) ); AddChild( new wxEnumProperty(_("Family"), wxS("PointSize"), @@ -513,9 +651,10 @@ void wxFontProperty::OnSetValue() } } -wxString wxFontProperty::GetValueAsString( int argFlags ) const +wxString wxFontProperty::ValueToString( wxVariant& value, + int argFlags ) const { - return wxPGProperty::GetValueAsString(argFlags); + return wxPGProperty::ValueToString(value, argFlags); } bool wxFontProperty::OnEvent( wxPropertyGrid* propgrid, wxWindow* WXUNUSED(primary), @@ -524,11 +663,11 @@ bool wxFontProperty::OnEvent( wxPropertyGrid* propgrid, wxWindow* WXUNUSED(prima if ( propgrid->IsMainButtonEvent(event) ) { // Update value from last minute changes - PrepareValueForDialogEditing(propgrid); + wxVariant useValue = propgrid->GetUncommittedPropertyValue(); wxFontData data; wxFont font; - font << m_value; + font << useValue; data.SetInitialFont( font ); data.SetColour(*wxBLACK); @@ -663,7 +802,7 @@ void wxFontProperty::OnCustomPaint(wxDC& dc, // wxEnumProperty based classes cannot use wxPG_PROP_CLASS_SPECIFIC_1 #define wxPG_PROP_HIDE_CUSTOM_COLOUR wxPG_PROP_CLASS_SPECIFIC_2 -#include +#include "wx/colordlg.h" //#define wx_cp_es_syscolours_len 25 static const wxChar* gs_cp_es_syscolour_labels[] = { @@ -892,13 +1031,6 @@ int wxSystemColourProperty::ColToInd( const wxColour& colour ) const return wxNOT_FOUND; } - -static inline wxColour wxColourFromPGLong( long col ) -{ - return wxColour((col&0xFF),((col>>8)&0xFF),((col>>16)&0xFF)); -} - - void wxSystemColourProperty::OnSetValue() { // Convert from generic wxobject ptr to wxPGVariantDataColour @@ -924,7 +1056,7 @@ void wxSystemColourProperty::OnSetValue() m_value = TranslateVal(val); } - int ind; + int ind = wxNOT_FOUND; if ( m_value.GetType() == wxS("wxColourPropertyValue") ) { @@ -941,10 +1073,7 @@ void wxSystemColourProperty::OnSetValue() if ( cpv.m_type < wxPG_COLOUR_WEB_BASE ) { - if ( m_choices.HasValues() ) - ind = GetIndexForValue(cpv.m_type); - else - ind = ColToInd(col); + ind = GetIndexForValue(cpv.m_type); } else { @@ -990,24 +1119,30 @@ wxString wxSystemColourProperty::ColourToString( const wxColour& col, int index return m_choices.GetLabel(index); } -wxString wxSystemColourProperty::GetValueAsString( int argFlags ) const +wxString wxSystemColourProperty::ValueToString( wxVariant& value, + int argFlags ) const { - wxColourPropertyValue val = GetVal(); + wxColourPropertyValue val = GetVal(&value); - int ind = GetIndex(); + int index; - // Always show custom colour for textctrl-editor - if ( val.m_type == wxPG_COLOUR_CUSTOM || - ind == GetCustomColourIndex() || - (argFlags & wxPG_PROPERTY_SPECIFIC) ) + if ( argFlags & wxPG_VALUE_IS_CURRENT ) { - return ColourToString(val.m_colour, wxNOT_FOUND); + // GetIndex() only works reliably if wxPG_VALUE_IS_CURRENT flag is set, + // but we should use it whenever possible. + index = GetIndex(); + + // If custom colour was selected, use invalid index, so that + // ColourToString() will return properly formatted colour text. + if ( index == GetCustomColourIndex() ) + index = wxNOT_FOUND; + } + else + { + index = m_choices.Index(val.m_type); } - if ( ind == -1 ) - return wxEmptyString; - - return ColourToString(val.m_colour, ind); + return ColourToString(val.m_colour, index); } @@ -1032,7 +1167,7 @@ bool wxSystemColourProperty::QueryColourFromUser( wxVariant& variant ) const wxASSERT( propgrid ); // Must only occur when user triggers event - if ( !(propgrid->GetInternalFlags() & wxPG_FL_IN_ONCUSTOMEDITOREVENT) ) + if ( !(propgrid->GetInternalFlags() & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT) ) return res; wxColourPropertyValue val = GetVal(); @@ -1069,14 +1204,9 @@ bool wxSystemColourProperty::QueryColourFromUser( wxVariant& variant ) const bool wxSystemColourProperty::IntToValue( wxVariant& variant, int number, int WXUNUSED(argFlags) ) const { int index = number; - int type = GetValueForIndex(index); - bool hasValue = m_choices[index].HasValue(); + int type = m_choices.GetValue(index); - if ( ( hasValue && type == wxPG_COLOUR_CUSTOM ) || - ( !hasValue && (index == (int)GetCustomColourIndex() && - !(m_flags & wxPG_PROP_HIDE_CUSTOM_COLOUR)) - ) - ) + if ( type == wxPG_COLOUR_CUSTOM ) { QueryColourFromUser(variant); } @@ -1234,7 +1364,7 @@ bool wxSystemColourProperty::StringToValue( wxVariant& value, const wxString& te if ( res && GetIndex() >= 0 ) { val.m_type = GetIndex(); - if ( val.m_type >= 0 && val.m_type < m_choices.GetCount() && m_choices[val.m_type].HasValue() ) + if ( val.m_type < m_choices.GetCount() ) val.m_type = m_choices[val.m_type].GetValue(); // Get proper colour for type. @@ -1349,11 +1479,63 @@ static unsigned long gs_cp_es_normcolour_colours[] = { wxPG_COLOUR(0,0,0) }; -WX_PG_IMPLEMENT_CUSTOM_COLOUR_PROPERTY_USES_WXCOLOUR2(wxColourProperty, - gs_cp_es_normcolour_labels, - (const long*)NULL, - gs_cp_es_normcolour_colours, - TextCtrlAndButton) +WX_PG_IMPLEMENT_PROPERTY_CLASS(wxColourProperty, wxSystemColourProperty, + wxColour, const wxColour&, TextCtrlAndButton) + +static wxPGChoices gs_wxColourProperty_choicesCache; + +wxColourProperty::wxColourProperty( const wxString& label, + const wxString& name, + const wxColour& value ) + : wxSystemColourProperty(label, name, gs_cp_es_normcolour_labels, + NULL, + &gs_wxColourProperty_choicesCache, value ) +{ + Init( value ); + + m_flags |= wxPG_PROP_TRANSLATE_CUSTOM; +} + +wxColourProperty::~wxColourProperty() +{ +} + +void wxColourProperty::Init( wxColour colour ) +{ + if ( !colour.Ok() ) + colour = *wxWHITE; + wxVariant variant; + variant << colour; + m_value = variant; + int ind = ColToInd(colour); + if ( ind < 0 ) + ind = m_choices.GetCount() - 1; + SetIndex( ind ); +} + +wxString wxColourProperty::ValueToString( wxVariant& value, + int argFlags ) const +{ + const wxPGEditor* editor = GetEditorClass(); + if ( editor != wxPGEditor_Choice && + editor != wxPGEditor_ChoiceAndButton && + editor != wxPGEditor_ComboBox ) + argFlags |= wxPG_PROPERTY_SPECIFIC; + + return wxSystemColourProperty::ValueToString(value, argFlags); +} + +wxColour wxColourProperty::GetColour( int index ) const +{ + return gs_cp_es_normcolour_colours[m_choices.GetValue(index)]; +} + +wxVariant wxColourProperty::DoTranslateVal( wxColourPropertyValue& v ) const +{ + wxVariant variant; + variant << v.m_colour; + return variant; +} // ----------------------------------------------------------------------- // wxCursorProperty @@ -1489,7 +1671,10 @@ void wxCursorProperty::OnCustomPaint( wxDC& dc, 0, 0, NULL, - DI_COMPAT | DI_DEFAULTSIZE | DI_NORMAL + #if !defined(__WXWINCE__) + DI_COMPAT | DI_DEFAULTSIZE | + #endif + DI_NORMAL ); #endif } @@ -1527,7 +1712,7 @@ const wxString& wxPGGetDefaultImageWildcard() // Let's iterate over the image handler list. //for ( wxList::Node *node = handlers.GetFirst(); node; node = node->GetNext() ) - for ( node = handlers.begin(); node != handlers.end(); node++ ) + for ( node = handlers.begin(); node != handlers.end(); ++node ) { wxImageHandler *handler = (wxImageHandler*)*node; @@ -1586,10 +1771,12 @@ void wxImageFileProperty::OnSetValue() m_pBitmap = NULL; } + wxFileName filename = GetFileName(); + // Create the image thumbnail - if ( m_filename.FileExists() ) + if ( filename.FileExists() ) { - m_pImage = new wxImage( m_filename.GetFullPath() ); + m_pImage = new wxImage( filename.GetFullPath() ); } } @@ -1633,7 +1820,7 @@ void wxImageFileProperty::OnCustomPaint( wxDC& dc, #if wxUSE_CHOICEDLG -#include +#include "wx/choicdlg.h" WX_PG_IMPLEMENT_PROPERTY_CLASS(wxMultiChoiceProperty,wxPGProperty, wxArrayInt,const wxArrayInt&,TextCtrlAndButton) @@ -1674,22 +1861,30 @@ wxMultiChoiceProperty::~wxMultiChoiceProperty() void wxMultiChoiceProperty::OnSetValue() { - GenerateValueAsString(); + GenerateValueAsString(m_value, &m_display); } -wxString wxMultiChoiceProperty::GetValueAsString( int ) const +wxString wxMultiChoiceProperty::ValueToString( wxVariant& value, + int argFlags ) const { - return m_display; + // If possible, use cached string + if ( argFlags & wxPG_VALUE_IS_CURRENT ) + return m_display; + + wxString s; + GenerateValueAsString(value, &s); + return s; } -void wxMultiChoiceProperty::GenerateValueAsString() +void wxMultiChoiceProperty::GenerateValueAsString( wxVariant& value, + wxString* target ) const { wxArrayString strings; - if ( m_value.GetType() == wxPG_VARIANT_TYPE_ARRSTRING ) - strings = m_value.GetArrayString(); + if ( value.GetType() == wxPG_VARIANT_TYPE_ARRSTRING ) + strings = value.GetArrayString(); - wxString& tempStr = m_display; + wxString& tempStr = *target; unsigned int i; unsigned int itemCount = strings.size(); @@ -1709,7 +1904,8 @@ void wxMultiChoiceProperty::GenerateValueAsString() wxArrayInt wxMultiChoiceProperty::GetValueAsIndices() const { - const wxArrayInt& valueArr = wxArrayIntRefFromVariant(GetValue()); + wxVariant variant = GetValue(); + const wxArrayInt& valueArr = wxArrayIntRefFromVariant(variant); unsigned int i; // Translate values to string indices. @@ -1740,7 +1936,7 @@ bool wxMultiChoiceProperty::OnEvent( wxPropertyGrid* propgrid, if ( propgrid->IsMainButtonEvent(event) ) { // Update the value - PrepareValueForDialogEditing(propgrid); + wxVariant useValue = propgrid->GetUncommittedPropertyValue(); wxArrayString labels = m_choices.GetLabels(); unsigned int choiceCount; @@ -1760,7 +1956,7 @@ bool wxMultiChoiceProperty::OnEvent( wxPropertyGrid* propgrid, dlg.Move( propgrid->GetGoodEditorDialogPosition(this,dlg.GetSize()) ); - wxArrayString strings = m_value.GetArrayString(); + wxArrayString strings = useValue.GetArrayString(); wxArrayString extraStrings; dlg.SetSelections(m_choices.GetIndicesForStrings(strings, &extraStrings)); @@ -1886,11 +2082,12 @@ bool wxDateProperty::StringToValue( wxVariant& variant, const wxString& text, return false; } -wxString wxDateProperty::GetValueAsString( int argFlags ) const +wxString wxDateProperty::ValueToString( wxVariant& value, + int argFlags ) const { const wxChar* format = (const wxChar*) NULL; - wxDateTime dateTime = m_value.GetDateTime(); + wxDateTime dateTime = value.GetDateTime(); if ( !dateTime.IsValid() ) return wxT("Invalid");