///////////////////////////////////////////////////////////////////////////// // Name: samples/propgrid/sampleprops.cpp // Purpose: wxPropertyGrid Sample Properties // Author: Jaakko Salli // Modified by: // Created: 2006-03-05 // Copyright: (c) Jaakko Salli // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif // for all others, include the necessary headers (this file is usually all you // need because it includes almost all "standard" wxWidgets headers) #ifndef WX_PRECOMP #include "wx/wx.h" #endif #include "wx/fontdlg.h" #include "wx/numformatter.h" // ----------------------------------------------------------------------- #include "wx/propgrid/propgrid.h" #include "wx/propgrid/advprops.h" #ifndef WX_PROPGRID_SAMPLEPROPS_H #include "sampleprops.h" #endif // ----------------------------------------------------------------------- // wxFontDataProperty // ----------------------------------------------------------------------- // Dummy comparison required by value type implementation. bool operator == (const wxFontData&, const wxFontData&) { return false; } // Custom version of wxFontProperty that also holds colour in the value. // Original version by Vladimir Vainer. IMPLEMENT_VARIANT_OBJECT_SHALLOWCMP(wxFontData) wxPG_IMPLEMENT_PROPERTY_CLASS(wxFontDataProperty,wxFontProperty,TextCtrlAndButton) wxFontDataProperty::wxFontDataProperty( const wxString& label, const wxString& name, const wxFontData& value ) : wxFontProperty(label,name,value.GetInitialFont()) { wxFontData fontData(value); // Fix value. wxFont font; font << m_value; // Get font data from base object. fontData.SetChosenFont(font); if ( !fontData.GetColour().IsOk() ) fontData.SetColour(*wxBLACK); // Set initial value - should be done in a simpler way like this // (instead of calling SetValue) in derived (wxObject) properties. m_value_wxFontData << fontData; // Add extra children. AddPrivateChild( new wxColourProperty(wxT("Colour"), wxPG_LABEL, fontData.GetColour() ) ); } wxFontDataProperty::~wxFontDataProperty () { } void wxFontDataProperty::OnSetValue() { if ( !m_value.IsType(wxT("wxFontData")) ) { if ( m_value.IsType(wxT("wxFont")) ) { wxFont font; font << m_value; wxFontData fontData; fontData.SetChosenFont(font); if ( !m_value_wxFontData.IsNull() ) { wxFontData oldFontData; oldFontData << m_value_wxFontData; fontData.SetColour(oldFontData.GetColour()); } else { fontData.SetColour(*wxBLACK); } wxVariant variant; variant << fontData; m_value_wxFontData = variant; } else { wxFAIL_MSG(wxT("Value to wxFontDataProperty must be either wxFontData or wxFont")); } } else { // Set m_value to wxFont so that wxFontProperty methods will work // correctly. m_value_wxFontData = m_value; wxFontData fontData; fontData << m_value_wxFontData; wxFont font = fontData.GetChosenFont(); if ( !font.IsOk() ) font = wxFontInfo(10).Family(wxFONTFAMILY_SWISS); m_value = WXVARIANT(font); } } wxVariant wxFontDataProperty::DoGetValue() const { return m_value_wxFontData; } // Must re-create font dialog displayer. bool wxFontDataProperty::OnEvent( wxPropertyGrid* propgrid, wxWindow* WXUNUSED(primary), wxEvent& event ) { if ( propgrid->IsMainButtonEvent(event) ) { wxVariant useValue = propgrid->GetUncommittedPropertyValue(); wxFontData fontData; fontData << useValue; fontData.SetInitialFont(fontData.GetChosenFont()); wxFontDialog dlg(propgrid, fontData); if ( dlg.ShowModal() == wxID_OK ) { wxVariant variant; variant << dlg.GetFontData(); SetValueInEvent( variant ); return true; } } return false; } void wxFontDataProperty::RefreshChildren() { wxFontProperty::RefreshChildren(); if ( GetChildCount() < 6 ) // Number is count of wxFontProperty's children + 1. return; wxFontData fontData; fontData << m_value_wxFontData; wxVariant variant; variant << fontData.GetColour(); Item(6)->SetValue( variant ); } wxVariant wxFontDataProperty::ChildChanged( wxVariant& thisValue, int childIndex, wxVariant& childValue ) const { wxFontData fontData; fontData << thisValue; wxColour col; wxVariant variant; switch ( childIndex ) { case 6: col << childValue; fontData.SetColour( col ); break; default: // Transfer from subset to superset. wxFont font = fontData.GetChosenFont(); variant = WXVARIANT(font); variant = wxFontProperty::ChildChanged( variant, childIndex, childValue ); font << variant; fontData.SetChosenFont(font); } wxVariant newVariant; newVariant << fontData; return newVariant; } // ----------------------------------------------------------------------- // wxSizeProperty // ----------------------------------------------------------------------- wxPG_IMPLEMENT_PROPERTY_CLASS(wxSizeProperty,wxPGProperty,TextCtrl) wxSizeProperty::wxSizeProperty( const wxString& label, const wxString& name, const wxSize& value) : wxPGProperty(label,name) { SetValueI(value); AddPrivateChild( new wxIntProperty(wxT("Width"),wxPG_LABEL,value.x) ); AddPrivateChild( new wxIntProperty(wxT("Height"),wxPG_LABEL,value.y) ); } wxSizeProperty::~wxSizeProperty() { } void wxSizeProperty::RefreshChildren() { if ( !GetChildCount() ) return; const wxSize& size = wxSizeRefFromVariant(m_value); Item(0)->SetValue( (long)size.x ); Item(1)->SetValue( (long)size.y ); } wxVariant wxSizeProperty::ChildChanged( wxVariant& thisValue, int childIndex, wxVariant& childValue ) const { wxSize& size = wxSizeRefFromVariant(thisValue); int val = childValue.GetLong(); switch ( childIndex ) { case 0: size.x = val; break; case 1: size.y = val; break; } wxVariant newVariant; newVariant << size; return newVariant; } // ----------------------------------------------------------------------- // wxPointProperty // ----------------------------------------------------------------------- wxPG_IMPLEMENT_PROPERTY_CLASS(wxPointProperty,wxPGProperty,TextCtrl) wxPointProperty::wxPointProperty( const wxString& label, const wxString& name, const wxPoint& value) : wxPGProperty(label,name) { SetValueI(value); AddPrivateChild( new wxIntProperty(wxT("X"),wxPG_LABEL,value.x) ); AddPrivateChild( new wxIntProperty(wxT("Y"),wxPG_LABEL,value.y) ); } wxPointProperty::~wxPointProperty() { } void wxPointProperty::RefreshChildren() { if ( !GetChildCount() ) return; const wxPoint& point = wxPointRefFromVariant(m_value); Item(0)->SetValue( (long)point.x ); Item(1)->SetValue( (long)point.y ); } wxVariant wxPointProperty::ChildChanged( wxVariant& thisValue, int childIndex, wxVariant& childValue ) const { wxPoint& point = wxPointRefFromVariant(thisValue); int val = childValue.GetLong(); switch ( childIndex ) { case 0: point.x = val; break; case 1: point.y = val; break; } wxVariant newVariant; newVariant << point; return newVariant; } // ----------------------------------------------------------------------- // Dirs Property // ----------------------------------------------------------------------- WX_PG_IMPLEMENT_ARRAYSTRING_PROPERTY_WITH_VALIDATOR(wxDirsProperty, ',', wxT("Browse")) #if wxUSE_VALIDATORS wxValidator* wxDirsProperty::DoGetValidator() const { return wxFileProperty::GetClassValidator(); } #endif bool wxDirsProperty::OnCustomStringEdit( wxWindow* parent, wxString& value ) { wxDirDialog dlg(parent, wxT("Select a directory to be added to the list:"), value, 0); if ( dlg.ShowModal() == wxID_OK ) { value = dlg.GetPath(); return true; } return false; } // ----------------------------------------------------------------------- // wxArrayDoubleEditorDialog // ----------------------------------------------------------------------- // // You can *almost* convert wxArrayDoubleEditorDialog to wxArrayXXXEditorDialog // by replacing each ArrayDouble with ArrayXXX. // class wxArrayDoubleEditorDialog : public wxPGArrayEditorDialog { public: wxArrayDoubleEditorDialog(); void Init(); wxArrayDoubleEditorDialog(wxWindow *parent, const wxString& message, const wxString& caption, wxArrayDouble& array, long style = wxAEDIALOG_STYLE, const wxPoint& pos = wxDefaultPosition, const wxSize& sz = wxDefaultSize ); bool Create(wxWindow *parent, const wxString& message, const wxString& caption, wxArrayDouble& array, long style = wxAEDIALOG_STYLE, const wxPoint& pos = wxDefaultPosition, const wxSize& sz = wxDefaultSize ); const wxArrayDouble& GetArray() const { return m_array; } // Extra method for this type of array void SetPrecision ( int precision ) { m_precision = precision; } protected: // Mandatory array of type wxArrayDouble m_array; int m_precision; // Mandatory overridden methods virtual wxString ArrayGet( size_t index ) wxOVERRIDE; virtual size_t ArrayGetCount() wxOVERRIDE; virtual bool ArrayInsert( const wxString& str, int index ) wxOVERRIDE; virtual bool ArraySet( size_t index, const wxString& str ) wxOVERRIDE; virtual void ArrayRemoveAt( int index ) wxOVERRIDE; virtual void ArraySwap( size_t first, size_t second ) wxOVERRIDE; private: wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxArrayDoubleEditorDialog); }; wxIMPLEMENT_DYNAMIC_CLASS(wxArrayDoubleEditorDialog, wxPGArrayEditorDialog); // // Array dialog array access and manipulation // wxString wxArrayDoubleEditorDialog::ArrayGet( size_t index ) { return wxNumberFormatter::ToString( m_array[index], m_precision, wxNumberFormatter::Style_NoTrailingZeroes); } size_t wxArrayDoubleEditorDialog::ArrayGetCount() { return m_array.GetCount(); } bool wxArrayDoubleEditorDialog::ArrayInsert( const wxString& str, int index ) { double d; if ( !str.ToDouble(&d) ) return false; if (index<0) m_array.Add(d); else m_array.Insert(d,index); return true; } bool wxArrayDoubleEditorDialog::ArraySet( size_t index, const wxString& str ) { double d; if ( !str.ToDouble(&d) ) return false; m_array[index] = d; return true; } void wxArrayDoubleEditorDialog::ArrayRemoveAt( int index ) { m_array.RemoveAt(index); } void wxArrayDoubleEditorDialog::ArraySwap( size_t first, size_t second ) { wxSwap(m_array[first], m_array[second]); } // // Array dialog construction etc. // wxArrayDoubleEditorDialog::wxArrayDoubleEditorDialog() : wxPGArrayEditorDialog() { Init(); } void wxArrayDoubleEditorDialog::Init() { wxPGArrayEditorDialog::Init(); SetPrecision(-1); } wxArrayDoubleEditorDialog::wxArrayDoubleEditorDialog(wxWindow *parent, const wxString& message, const wxString& caption, wxArrayDouble& array, long style, const wxPoint& pos, const wxSize& sz ) : wxPGArrayEditorDialog() { Init(); Create(parent,message,caption,array,style,pos,sz); } bool wxArrayDoubleEditorDialog::Create(wxWindow *parent, const wxString& message, const wxString& caption, wxArrayDouble& array, long style, const wxPoint& pos, const wxSize& sz ) { m_array = array; return wxPGArrayEditorDialog::Create (parent,message,caption,style,pos,sz); } // ----------------------------------------------------------------------- // wxArrayDoubleProperty // ----------------------------------------------------------------------- #include // for fabs // Comparison required by value type implementation. bool operator == (const wxArrayDouble& a, const wxArrayDouble& b) { if ( a.GetCount() != b.GetCount() ) return false; size_t i; for ( i=0; i 0.0000000001 ) { //wxLogDebug(wxT("%f != %f"),a[i],b[i]); return false; } } return true; } WX_PG_IMPLEMENT_VARIANT_DATA_DUMMY_EQ(wxArrayDouble) wxPG_IMPLEMENT_PROPERTY_CLASS(wxArrayDoubleProperty, wxPGProperty, TextCtrlAndButton) wxArrayDoubleProperty::wxArrayDoubleProperty (const wxString& label, const wxString& name, const wxArrayDouble& array ) : wxPGProperty(label,name) { m_precision = -1; // // Need to figure out delimiter needed for this locale // (i.e. can't use comma when comma acts as decimal point in float). wxChar use_delimiter = wxT(','); if (wxString::Format(wxT("%.2f"),12.34).Find(use_delimiter) >= 0) use_delimiter = wxT(';'); m_delimiter = use_delimiter; SetValue( WXVARIANT(array) ); } wxArrayDoubleProperty::~wxArrayDoubleProperty () { } void wxArrayDoubleProperty::OnSetValue() { // Generate cached display string, to optimize grid drawing GenerateValueAsString( m_display, m_precision, true ); } wxString wxArrayDoubleProperty::ValueToString( wxVariant& value, int argFlags ) const { wxString s; if ( argFlags & wxPG_FULL_VALUE ) { GenerateValueAsString(s,-1,false); } else { // // Display cached string only if value truly matches m_value if ( value.GetData() == m_value.GetData() ) return m_display; else GenerateValueAsString( s, m_precision, true ); } return s; } void wxArrayDoubleProperty::GenerateValueAsString( wxString& target, int prec, bool removeZeroes ) const { wxChar between[3] = wxT(", "); size_t i; between[0] = m_delimiter; target.Empty(); const wxArrayDouble& value = wxArrayDoubleRefFromVariant(m_value); wxNumberFormatter::Style style = wxNumberFormatter::Style_None; if (removeZeroes) style = wxNumberFormatter::Style_NoTrailingZeroes; for ( i=0; iIsMainButtonEvent(event) ) { // Update the value in case of last minute changes wxVariant useValue = propgrid->GetUncommittedPropertyValue(); wxArrayDouble& value = wxArrayDoubleRefFromVariant(useValue); // Create editor dialog. wxArrayDoubleEditorDialog dlg; dlg.SetPrecision(m_precision); dlg.Create( propgrid, wxEmptyString, m_label, value ); dlg.Move( propgrid->GetGoodEditorDialogPosition(this,dlg.GetSize()) ); // Execute editor dialog int res = dlg.ShowModal(); if ( res == wxID_OK && dlg.IsModified() ) { SetValueInEvent( WXVARIANT(dlg.GetArray()) ); return true; } return false; } return false; } bool wxArrayDoubleProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const { // Add values to a temporary array so that in case // of error we can opt not to use them. wxArrayDouble new_array; bool ok = true; wxChar delimiter = m_delimiter; WX_PG_TOKENIZER1_BEGIN(text,delimiter) if ( !token.empty() ) { double tval; // If token was invalid, exit the loop now if ( !token.ToDouble(&tval) ) { ok = false; break; } new_array.Add(tval); } WX_PG_TOKENIZER1_END() // When invalid token found signal the error // by returning pending value of non-wxArrayDouble type. if ( !ok ) { variant = (long)0; return true; } if ( !(wxArrayDoubleRefFromVariant(m_value) == new_array) ) { variant = WXVARIANT(new_array); return true; } return false; } bool wxArrayDoubleProperty::DoSetAttribute( const wxString& name, wxVariant& value ) { if ( name == wxPG_FLOAT_PRECISION ) { m_precision = value.GetLong(); GenerateValueAsString( m_display, m_precision, true ); return true; } return false; } wxValidator* wxArrayDoubleProperty::DoGetValidator() const { #if wxUSE_VALIDATORS WX_PG_DOGETVALIDATOR_ENTRY() wxTextValidator* validator = new wxTextValidator(wxFILTER_INCLUDE_CHAR_LIST); // Accept characters for numeric elements wxNumericPropertyValidator numValidator(wxNumericPropertyValidator::Float); wxArrayString incChars(numValidator.GetIncludes()); // Accept also a delimiter and space character incChars.Add(m_delimiter); incChars.Add(wxT(" ")); validator->SetIncludes(incChars); WX_PG_DOGETVALIDATOR_EXIT(validator) #else return NULL; #endif } bool wxArrayDoubleProperty::ValidateValue(wxVariant& value, wxPGValidationInfo& validationInfo) const { if (!value.IsType(wxT("wxArrayDouble"))) { validationInfo.SetFailureMessage(wxT("At least one element is not a valid floating-point number.")); return false; } return true; }