Files
wxWidgets/src/propgrid/props.cpp
Vadim Zeitlin ce7fe42e84 Provide shorter synonyms for wxEVT_XXX constants.
Use the same short names as are used by the event table macros for the event
type constants themselves. This makes them much more comfortable to use, e.g.
Bind(wxEVT_BUTTON) compared to Bind(wxEVT_COMMAND_BUTTON_CLICKED).

The old long names are still kept for backwards compatibility and shouldn't be
removed as it doesn't really cost anything to continue providing them, but all
new event types should only use the short versions.

Closes #10661.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73850 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-04-25 10:11:03 +00:00

2755 lines
72 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/propgrid/props.cpp
// Purpose: Basic Property Classes
// Author: Jaakko Salli
// Modified by:
// Created: 2005-05-14
// RCS-ID: $Id$
// 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
#if wxUSE_PROPGRID
#ifndef WX_PRECOMP
#include "wx/defs.h"
#include "wx/object.h"
#include "wx/hash.h"
#include "wx/string.h"
#include "wx/log.h"
#include "wx/event.h"
#include "wx/window.h"
#include "wx/panel.h"
#include "wx/dc.h"
#include "wx/dcclient.h"
#include "wx/dcmemory.h"
#include "wx/button.h"
#include "wx/bmpbuttn.h"
#include "wx/pen.h"
#include "wx/brush.h"
#include "wx/cursor.h"
#include "wx/dialog.h"
#include "wx/settings.h"
#include "wx/msgdlg.h"
#include "wx/choice.h"
#include "wx/stattext.h"
#include "wx/scrolwin.h"
#include "wx/dirdlg.h"
#include "wx/combobox.h"
#include "wx/layout.h"
#include "wx/sizer.h"
#include "wx/textdlg.h"
#include "wx/filedlg.h"
#include "wx/intl.h"
#endif
#include "wx/filename.h"
#include "wx/propgrid/propgrid.h"
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
// -----------------------------------------------------------------------
// wxStringProperty
// -----------------------------------------------------------------------
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxStringProperty,wxPGProperty,
wxString,const wxString&,TextCtrl)
wxStringProperty::wxStringProperty( const wxString& label,
const wxString& name,
const wxString& value )
: wxPGProperty(label,name)
{
SetValue(value);
}
void wxStringProperty::OnSetValue()
{
if ( !m_value.IsNull() && m_value.GetString() == wxS("<composed>") )
SetFlag(wxPG_PROP_COMPOSED_VALUE);
if ( HasFlag(wxPG_PROP_COMPOSED_VALUE) )
{
wxString s;
DoGenerateComposedValue(s);
m_value = s;
}
}
wxStringProperty::~wxStringProperty() { }
wxString wxStringProperty::ValueToString( wxVariant& value,
int argFlags ) const
{
wxString s = value.GetString();
if ( GetChildCount() && HasFlag(wxPG_PROP_COMPOSED_VALUE) )
{
// Value stored in m_value is non-editable, non-full value
if ( (argFlags & wxPG_FULL_VALUE) ||
(argFlags & wxPG_EDITABLE_VALUE) ||
!s.length() )
{
// Calling this under incorrect conditions will fail
wxASSERT_MSG( argFlags & wxPG_VALUE_IS_CURRENT,
"Sorry, currently default wxPGProperty::ValueToString() "
"implementation only works if value is m_value." );
DoGenerateComposedValue(s, argFlags);
}
return s;
}
// If string is password and value is for visual purposes,
// then return asterisks instead the actual string.
if ( (m_flags & wxPG_PROP_PASSWORD) && !(argFlags & (wxPG_FULL_VALUE|wxPG_EDITABLE_VALUE)) )
return wxString(wxChar('*'), s.Length());
return s;
}
bool wxStringProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
{
if ( GetChildCount() && HasFlag(wxPG_PROP_COMPOSED_VALUE) )
return wxPGProperty::StringToValue(variant, text, argFlags);
if ( variant != text )
{
variant = text;
return true;
}
return false;
}
bool wxStringProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
if ( name == wxPG_STRING_PASSWORD )
{
m_flags &= ~(wxPG_PROP_PASSWORD);
if ( value.GetLong() ) m_flags |= wxPG_PROP_PASSWORD;
RecreateEditor();
return false;
}
return true;
}
// -----------------------------------------------------------------------
// wxNumericPropertyValidator
// -----------------------------------------------------------------------
#if wxUSE_VALIDATORS
wxNumericPropertyValidator::
wxNumericPropertyValidator( NumericType numericType, int base )
: wxTextValidator(wxFILTER_INCLUDE_CHAR_LIST)
{
wxArrayString arr;
arr.Add(wxS("0"));
arr.Add(wxS("1"));
arr.Add(wxS("2"));
arr.Add(wxS("3"));
arr.Add(wxS("4"));
arr.Add(wxS("5"));
arr.Add(wxS("6"));
arr.Add(wxS("7"));
if ( base >= 10 )
{
arr.Add(wxS("8"));
arr.Add(wxS("9"));
if ( base >= 16 )
{
arr.Add(wxS("a")); arr.Add(wxS("A"));
arr.Add(wxS("b")); arr.Add(wxS("B"));
arr.Add(wxS("c")); arr.Add(wxS("C"));
arr.Add(wxS("d")); arr.Add(wxS("D"));
arr.Add(wxS("e")); arr.Add(wxS("E"));
arr.Add(wxS("f")); arr.Add(wxS("F"));
}
}
if ( numericType == Signed )
{
arr.Add(wxS("+"));
arr.Add(wxS("-"));
}
else if ( numericType == Float )
{
arr.Add(wxS("+"));
arr.Add(wxS("-"));
arr.Add(wxS("e"));
// Use locale-specific decimal point
arr.Add(wxString::Format("%g", 1.1)[1]);
}
SetIncludes(arr);
}
bool wxNumericPropertyValidator::Validate(wxWindow* parent)
{
if ( !wxTextValidator::Validate(parent) )
return false;
wxWindow* wnd = GetWindow();
if ( !wxDynamicCast(wnd, wxTextCtrl) )
return true;
// Do not allow zero-length string
wxTextCtrl* tc = static_cast<wxTextCtrl*>(wnd);
wxString text = tc->GetValue();
if ( text.empty() )
return false;
return true;
}
#endif // wxUSE_VALIDATORS
// -----------------------------------------------------------------------
// wxIntProperty
// -----------------------------------------------------------------------
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxIntProperty,wxPGProperty,
long,long,TextCtrl)
wxIntProperty::wxIntProperty( const wxString& label, const wxString& name,
long value ) : wxPGProperty(label,name)
{
SetValue(value);
}
wxIntProperty::wxIntProperty( const wxString& label, const wxString& name,
const wxLongLong& value ) : wxPGProperty(label,name)
{
SetValue(WXVARIANT(value));
}
wxIntProperty::~wxIntProperty() { }
wxString wxIntProperty::ValueToString( wxVariant& value,
int WXUNUSED(argFlags) ) const
{
if ( value.GetType() == wxPG_VARIANT_TYPE_LONG )
{
return wxString::Format(wxS("%li"),value.GetLong());
}
else if ( value.GetType() == wxPG_VARIANT_TYPE_LONGLONG )
{
wxLongLong ll = value.GetLongLong();
return ll.ToString();
}
return wxEmptyString;
}
bool wxIntProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
{
wxString s;
long value32;
if ( text.empty() )
{
variant.MakeNull();
return true;
}
// We know it is a number, but let's still check
// the return value.
if ( text.IsNumber() )
{
// Remove leading zeroes, so that the number is not interpreted as octal
wxString::const_iterator i = text.begin();
wxString::const_iterator iMax = text.end() - 1; // Let's allow one, last zero though
int firstNonZeroPos = 0;
for ( ; i != iMax; ++i )
{
wxChar c = *i;
if ( c != wxS('0') && c != wxS(' ') )
break;
firstNonZeroPos++;
}
wxString useText = text.substr(firstNonZeroPos, text.length() - firstNonZeroPos);
wxString variantType = variant.GetType();
bool isPrevLong = variantType == wxPG_VARIANT_TYPE_LONG;
wxLongLong_t value64 = 0;
if ( useText.ToLongLong(&value64, 10) &&
( value64 >= INT_MAX || value64 <= INT_MIN )
)
{
bool doChangeValue = isPrevLong;
if ( !isPrevLong && variantType == wxPG_VARIANT_TYPE_LONGLONG )
{
wxLongLong oldValue = variant.GetLongLong();
if ( oldValue.GetValue() != value64 )
doChangeValue = true;
}
if ( doChangeValue )
{
wxLongLong ll(value64);
variant = ll;
return true;
}
}
if ( useText.ToLong( &value32, 0 ) )
{
if ( !isPrevLong || variant != value32 )
{
variant = value32;
return true;
}
}
}
else if ( argFlags & wxPG_REPORT_ERROR )
{
}
return false;
}
bool wxIntProperty::IntToValue( wxVariant& variant, int value, int WXUNUSED(argFlags) ) const
{
if ( variant.GetType() != wxPG_VARIANT_TYPE_LONG || variant != (long)value )
{
variant = (long)value;
return true;
}
return false;
}
//
// Common validation code to be called in ValidateValue()
// implementations.
//
// Note that 'value' is reference on purpose, so we can write
// back to it when mode is wxPG_PROPERTY_VALIDATION_SATURATE.
//
template<typename T>
bool NumericValidation( const wxPGProperty* property,
T& value,
wxPGValidationInfo* pValidationInfo,
int mode,
const wxString& strFmt )
{
T min = (T) wxINT64_MIN;
T max = (T) wxINT64_MAX;
wxVariant variant;
bool minOk = false;
bool maxOk = false;
variant = property->GetAttribute(wxPGGlobalVars->m_strMin);
if ( !variant.IsNull() )
{
variant.Convert(&min);
minOk = true;
}
variant = property->GetAttribute(wxPGGlobalVars->m_strMax);
if ( !variant.IsNull() )
{
variant.Convert(&max);
maxOk = true;
}
if ( minOk )
{
if ( value < min )
{
if ( mode == wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE )
{
wxString msg;
wxString smin = wxString::Format(strFmt, min);
wxString smax = wxString::Format(strFmt, max);
if ( !maxOk )
msg = wxString::Format(
_("Value must be %s or higher."),
smin.c_str());
else
msg = wxString::Format(
_("Value must be between %s and %s."),
smin.c_str(), smax.c_str());
pValidationInfo->SetFailureMessage(msg);
}
else if ( mode == wxPG_PROPERTY_VALIDATION_SATURATE )
value = min;
else
value = max - (min - value);
return false;
}
}
if ( maxOk )
{
if ( value > max )
{
if ( mode == wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE )
{
wxString msg;
wxString smin = wxString::Format(strFmt, min);
wxString smax = wxString::Format(strFmt, max);
if ( !minOk )
msg = wxString::Format(
_("Value must be %s or less."),
smax.c_str());
else
msg = wxString::Format(
_("Value must be between %s and %s."),
smin.c_str(), smax.c_str());
pValidationInfo->SetFailureMessage(msg);
}
else if ( mode == wxPG_PROPERTY_VALIDATION_SATURATE )
value = max;
else
value = min + (value - max);
return false;
}
}
return true;
}
bool wxIntProperty::DoValidation( const wxPGProperty* property,
wxLongLong_t& value,
wxPGValidationInfo* pValidationInfo,
int mode )
{
return NumericValidation<wxLongLong_t>(property,
value,
pValidationInfo,
mode,
wxS("%lld"));
}
bool wxIntProperty::ValidateValue( wxVariant& value,
wxPGValidationInfo& validationInfo ) const
{
wxLongLong_t ll = value.GetLongLong().GetValue();
return DoValidation(this, ll, &validationInfo,
wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE);
}
wxValidator* wxIntProperty::GetClassValidator()
{
#if wxUSE_VALIDATORS
WX_PG_DOGETVALIDATOR_ENTRY()
wxValidator* validator = new wxNumericPropertyValidator(
wxNumericPropertyValidator::Signed);
WX_PG_DOGETVALIDATOR_EXIT(validator)
#else
return NULL;
#endif
}
wxValidator* wxIntProperty::DoGetValidator() const
{
return GetClassValidator();
}
// -----------------------------------------------------------------------
// wxUIntProperty
// -----------------------------------------------------------------------
#define wxPG_UINT_TEMPLATE_MAX 8
static const wxChar* const gs_uintTemplates32[wxPG_UINT_TEMPLATE_MAX] = {
wxT("%lx"),wxT("0x%lx"),wxT("$%lx"),
wxT("%lX"),wxT("0x%lX"),wxT("$%lX"),
wxT("%lu"),wxT("%lo")
};
static const char* const gs_uintTemplates64[wxPG_UINT_TEMPLATE_MAX] = {
"%" wxLongLongFmtSpec "x",
"0x%" wxLongLongFmtSpec "x",
"$%" wxLongLongFmtSpec "x",
"%" wxLongLongFmtSpec "X",
"0x%" wxLongLongFmtSpec "X",
"$%" wxLongLongFmtSpec "X",
"%" wxLongLongFmtSpec "u",
"%" wxLongLongFmtSpec "o"
};
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxUIntProperty,wxPGProperty,
long,unsigned long,TextCtrl)
void wxUIntProperty::Init()
{
m_base = 6; // This is magic number for dec base (must be same as in setattribute)
m_realBase = 10;
m_prefix = wxPG_PREFIX_NONE;
}
wxUIntProperty::wxUIntProperty( const wxString& label, const wxString& name,
unsigned long value ) : wxPGProperty(label,name)
{
Init();
SetValue((long)value);
}
wxUIntProperty::wxUIntProperty( const wxString& label, const wxString& name,
const wxULongLong& value ) : wxPGProperty(label,name)
{
Init();
SetValue(WXVARIANT(value));
}
wxUIntProperty::~wxUIntProperty() { }
wxString wxUIntProperty::ValueToString( wxVariant& value,
int WXUNUSED(argFlags) ) const
{
size_t index = m_base + m_prefix;
if ( index >= wxPG_UINT_TEMPLATE_MAX )
index = wxPG_BASE_DEC;
if ( value.GetType() == wxPG_VARIANT_TYPE_LONG )
{
return wxString::Format(gs_uintTemplates32[index],
(unsigned long)value.GetLong());
}
wxULongLong ull = value.GetULongLong();
return wxString::Format(gs_uintTemplates64[index], ull.GetValue());
}
bool wxUIntProperty::StringToValue( wxVariant& variant, const wxString& text, int WXUNUSED(argFlags) ) const
{
wxString variantType = variant.GetType();
bool isPrevLong = variantType == wxPG_VARIANT_TYPE_LONG;
if ( text.empty() )
{
variant.MakeNull();
return true;
}
size_t start = 0;
if ( text[0] == wxS('$') )
start++;
wxULongLong_t value64 = 0;
wxString s = text.substr(start, text.length() - start);
if ( s.ToULongLong(&value64, (unsigned int)m_realBase) )
{
if ( value64 >= LONG_MAX )
{
bool doChangeValue = isPrevLong;
if ( !isPrevLong && variantType == wxPG_VARIANT_TYPE_ULONGLONG )
{
wxULongLong oldValue = variant.GetULongLong();
if ( oldValue.GetValue() != value64 )
doChangeValue = true;
}
if ( doChangeValue )
{
variant = wxULongLong(value64);
return true;
}
}
else
{
unsigned long value32 = wxLongLong(value64).GetLo();
if ( !isPrevLong || m_value != (long)value32 )
{
variant = (long)value32;
return true;
}
}
}
return false;
}
bool wxUIntProperty::IntToValue( wxVariant& variant, int number, int WXUNUSED(argFlags) ) const
{
if ( variant != (long)number )
{
variant = (long)number;
return true;
}
return false;
}
bool wxUIntProperty::ValidateValue( wxVariant& value, wxPGValidationInfo& validationInfo ) const
{
wxULongLong_t uul = value.GetULongLong().GetValue();
return
NumericValidation<wxULongLong_t>(this,
uul,
&validationInfo,
wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE,
wxS("%llu"));
}
wxValidator* wxUIntProperty::DoGetValidator() const
{
#if wxUSE_VALIDATORS
WX_PG_DOGETVALIDATOR_ENTRY()
wxValidator* validator = new wxNumericPropertyValidator(
wxNumericPropertyValidator::Unsigned,
m_realBase);
WX_PG_DOGETVALIDATOR_EXIT(validator)
#else
return NULL;
#endif
}
bool wxUIntProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
if ( name == wxPG_UINT_BASE )
{
int val = value.GetLong();
m_realBase = (wxByte) val;
if ( m_realBase > 16 )
m_realBase = 16;
//
// Translate logical base to a template array index
m_base = 7; // oct
if ( val == wxPG_BASE_HEX )
m_base = 3;
else if ( val == wxPG_BASE_DEC )
m_base = 6;
else if ( val == wxPG_BASE_HEXL )
m_base = 0;
return true;
}
else if ( name == wxPG_UINT_PREFIX )
{
m_prefix = (wxByte) value.GetLong();
return true;
}
return false;
}
// -----------------------------------------------------------------------
// wxFloatProperty
// -----------------------------------------------------------------------
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFloatProperty,wxPGProperty,
double,double,TextCtrl)
wxFloatProperty::wxFloatProperty( const wxString& label,
const wxString& name,
double value )
: wxPGProperty(label,name)
{
m_precision = -1;
SetValue(value);
}
wxFloatProperty::~wxFloatProperty() { }
// This helper method provides standard way for floating point-using
// properties to convert values to string.
const wxString& wxPropertyGrid::DoubleToString(wxString& target,
double value,
int precision,
bool removeZeroes,
wxString* precTemplate)
{
if ( precision >= 0 )
{
wxString text1;
if (!precTemplate)
precTemplate = &text1;
if ( precTemplate->empty() )
{
*precTemplate = wxS("%.");
*precTemplate << wxString::Format( wxS("%i"), precision );
*precTemplate << wxS('f');
}
target.Printf( precTemplate->c_str(), value );
}
else
{
target.Printf( wxS("%f"), value );
}
if ( removeZeroes && precision != 0 && !target.empty() )
{
// Remove excess zeroes (do not remove this code just yet,
// since sprintf can't do the same consistently across platforms).
wxString::const_iterator i = target.end() - 1;
size_t new_len = target.length() - 1;
for ( ; i != target.begin(); --i )
{
if ( *i != wxS('0') )
break;
new_len--;
}
wxChar cur_char = *i;
if ( cur_char != wxS('.') && cur_char != wxS(',') )
new_len++;
if ( new_len != target.length() )
target.resize(new_len);
}
// Remove sign from zero
if ( target.length() >= 2 && target[0] == wxS('-') )
{
bool isZero = true;
wxString::const_iterator i = target.begin() + 1;
for ( ; i != target.end(); i++ )
{
if ( *i != wxS('0') && *i != wxS('.') && *i != wxS(',') )
{
isZero = false;
break;
}
}
if ( isZero )
target.erase(target.begin());
}
return target;
}
wxString wxFloatProperty::ValueToString( wxVariant& value,
int argFlags ) const
{
wxString text;
if ( !value.IsNull() )
{
wxPropertyGrid::DoubleToString(text,
value,
m_precision,
!(argFlags & wxPG_FULL_VALUE),
NULL);
}
return text;
}
bool wxFloatProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
{
wxString s;
double value;
if ( text.empty() )
{
variant.MakeNull();
return true;
}
bool res = text.ToDouble(&value);
if ( res )
{
if ( variant != value )
{
variant = value;
return true;
}
}
else if ( argFlags & wxPG_REPORT_ERROR )
{
}
return false;
}
bool wxFloatProperty::DoValidation( const wxPGProperty* property,
double& value,
wxPGValidationInfo* pValidationInfo,
int mode )
{
return NumericValidation<double>(property,
value,
pValidationInfo,
mode,
wxS("%g"));
}
bool
wxFloatProperty::ValidateValue( wxVariant& value,
wxPGValidationInfo& validationInfo ) const
{
double fpv = value.GetDouble();
return DoValidation(this, fpv, &validationInfo,
wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE);
}
bool wxFloatProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
if ( name == wxPG_FLOAT_PRECISION )
{
m_precision = value.GetLong();
return true;
}
return false;
}
wxValidator*
wxFloatProperty::GetClassValidator()
{
#if wxUSE_VALIDATORS
WX_PG_DOGETVALIDATOR_ENTRY()
wxValidator* validator = new wxNumericPropertyValidator(
wxNumericPropertyValidator::Float);
WX_PG_DOGETVALIDATOR_EXIT(validator)
#else
return NULL;
#endif
}
wxValidator* wxFloatProperty::DoGetValidator() const
{
return GetClassValidator();
}
// -----------------------------------------------------------------------
// wxBoolProperty
// -----------------------------------------------------------------------
// We cannot use standard WX_PG_IMPLEMENT_PROPERTY_CLASS macro, since
// there is a custom GetEditorClass.
IMPLEMENT_DYNAMIC_CLASS(wxBoolProperty, wxPGProperty)
const wxPGEditor* wxBoolProperty::DoGetEditorClass() const
{
// Select correct editor control.
#if wxPG_INCLUDE_CHECKBOX
if ( !(m_flags & wxPG_PROP_USE_CHECKBOX) )
return wxPGEditor_Choice;
return wxPGEditor_CheckBox;
#else
return wxPGEditor_Choice;
#endif
}
wxBoolProperty::wxBoolProperty( const wxString& label, const wxString& name, bool value ) :
wxPGProperty(label,name)
{
m_choices.Assign(wxPGGlobalVars->m_boolChoices);
SetValue(wxPGVariant_Bool(value));
m_flags |= wxPG_PROP_USE_DCC;
}
wxBoolProperty::~wxBoolProperty() { }
wxString wxBoolProperty::ValueToString( wxVariant& value,
int argFlags ) const
{
bool boolValue = value.GetBool();
// As a fragment of composite string value,
// make it a little more readable.
if ( argFlags & wxPG_COMPOSITE_FRAGMENT )
{
if ( boolValue )
{
return m_label;
}
else
{
if ( argFlags & wxPG_UNEDITABLE_COMPOSITE_FRAGMENT )
return wxEmptyString;
wxString notFmt;
if ( wxPGGlobalVars->m_autoGetTranslation )
notFmt = _("Not %s");
else
notFmt = wxS("Not %s");
return wxString::Format(notFmt.c_str(), m_label.c_str());
}
}
if ( !(argFlags & wxPG_FULL_VALUE) )
{
return wxPGGlobalVars->m_boolChoices[boolValue?1:0].GetText();
}
wxString text;
if ( boolValue ) text = wxS("true");
else text = wxS("false");
return text;
}
bool wxBoolProperty::StringToValue( wxVariant& variant, const wxString& text, int WXUNUSED(argFlags) ) const
{
bool boolValue = false;
if ( text.CmpNoCase(wxPGGlobalVars->m_boolChoices[1].GetText()) == 0 ||
text.CmpNoCase(wxS("true")) == 0 ||
text.CmpNoCase(m_label) == 0 )
boolValue = true;
if ( text.empty() )
{
variant.MakeNull();
return true;
}
if ( variant != boolValue )
{
variant = wxPGVariant_Bool(boolValue);
return true;
}
return false;
}
bool wxBoolProperty::IntToValue( wxVariant& variant, int value, int ) const
{
bool boolValue = value ? true : false;
if ( variant != boolValue )
{
variant = wxPGVariant_Bool(boolValue);
return true;
}
return false;
}
bool wxBoolProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
#if wxPG_INCLUDE_CHECKBOX
if ( name == wxPG_BOOL_USE_CHECKBOX )
{
if ( value.GetLong() )
m_flags |= wxPG_PROP_USE_CHECKBOX;
else
m_flags &= ~(wxPG_PROP_USE_CHECKBOX);
return true;
}
#endif
if ( name == wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING )
{
if ( value.GetLong() )
m_flags |= wxPG_PROP_USE_DCC;
else
m_flags &= ~(wxPG_PROP_USE_DCC);
return true;
}
return false;
}
// -----------------------------------------------------------------------
// wxEnumProperty
// -----------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxEnumProperty, wxPGProperty)
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEnumProperty,long,Choice)
wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
const long* values, int value ) : wxPGProperty(label,name)
{
SetIndex(0);
if ( labels )
{
m_choices.Add(labels,values);
if ( GetItemCount() )
SetValue( (long)value );
}
}
wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
const long* values, wxPGChoices* choicesCache, int value )
: wxPGProperty(label,name)
{
SetIndex(0);
wxASSERT( choicesCache );
if ( choicesCache->IsOk() )
{
m_choices.Assign( *choicesCache );
m_value = wxPGVariant_Zero;
}
else if ( labels )
{
m_choices.Add(labels,values);
if ( GetItemCount() )
SetValue( (long)value );
}
}
wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name,
const wxArrayString& labels, const wxArrayInt& values, int value )
: wxPGProperty(label,name)
{
SetIndex(0);
if ( &labels && labels.size() )
{
m_choices.Set(labels, values);
if ( GetItemCount() )
SetValue( (long)value );
}
}
wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name,
wxPGChoices& choices, int value )
: wxPGProperty(label,name)
{
m_choices.Assign( choices );
if ( GetItemCount() )
SetValue( (long)value );
}
int wxEnumProperty::GetIndexForValue( int value ) const
{
if ( !m_choices.IsOk() )
return -1;
int intVal = m_choices.Index(value);
if ( intVal >= 0 )
return intVal;
return value;
}
wxEnumProperty::~wxEnumProperty ()
{
}
int wxEnumProperty::ms_nextIndex = -2;
void wxEnumProperty::OnSetValue()
{
wxString variantType = m_value.GetType();
if ( variantType == wxPG_VARIANT_TYPE_LONG )
{
ValueFromInt_( m_value, m_value.GetLong(), wxPG_FULL_VALUE );
}
else if ( variantType == wxPG_VARIANT_TYPE_STRING )
{
ValueFromString_( m_value, m_value.GetString(), 0 );
}
else
{
wxFAIL;
}
if ( ms_nextIndex != -2 )
{
m_index = ms_nextIndex;
ms_nextIndex = -2;
}
}
bool wxEnumProperty::ValidateValue( wxVariant& value, wxPGValidationInfo& WXUNUSED(validationInfo) ) const
{
// Make sure string value is in the list,
// unless property has string as preferred value type
// To reduce code size, use conversion here as well
if ( value.GetType() == wxPG_VARIANT_TYPE_STRING &&
!wxDynamicCastThis(wxEditEnumProperty) )
return ValueFromString_( value, value.GetString(), wxPG_PROPERTY_SPECIFIC );
return true;
}
wxString wxEnumProperty::ValueToString( wxVariant& value,
int WXUNUSED(argFlags) ) const
{
if ( value.GetType() == wxPG_VARIANT_TYPE_STRING )
return value.GetString();
int index = m_choices.Index(value.GetLong());
if ( index < 0 )
return wxEmptyString;
return m_choices.GetLabel(index);
}
bool wxEnumProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
{
return ValueFromString_( variant, text, argFlags );
}
bool wxEnumProperty::IntToValue( wxVariant& variant, int intVal, int argFlags ) const
{
return ValueFromInt_( variant, intVal, argFlags );
}
bool wxEnumProperty::ValueFromString_( wxVariant& value, const wxString& text, int argFlags ) const
{
int useIndex = -1;
long useValue = 0;
for ( unsigned int i=0; i<m_choices.GetCount(); i++ )
{
const wxString& entryLabel = m_choices.GetLabel(i);
if ( text.CmpNoCase(entryLabel) == 0 )
{
useIndex = (int)i;
useValue = m_choices.GetValue(i);
break;
}
}
bool asText = false;
bool isEdit = this->IsKindOf(wxCLASSINFO(wxEditEnumProperty));
// If text not any of the choices, store as text instead
// (but only if we are wxEditEnumProperty)
if ( useIndex == -1 && isEdit )
{
asText = true;
}
int setAsNextIndex = -2;
if ( asText )
{
setAsNextIndex = -1;
value = text;
}
else if ( useIndex != GetIndex() )
{
if ( useIndex != -1 )
{
setAsNextIndex = useIndex;
value = (long)useValue;
}
else
{
setAsNextIndex = -1;
value = wxPGVariant_MinusOne;
}
}
if ( setAsNextIndex != -2 )
{
// If wxPG_PROPERTY_SPECIFIC is set, then this is done for
// validation purposes only, and index must not be changed
if ( !(argFlags & wxPG_PROPERTY_SPECIFIC) )
ms_nextIndex = setAsNextIndex;
if ( isEdit || setAsNextIndex != -1 )
return true;
else
return false;
}
return false;
}
bool wxEnumProperty::ValueFromInt_( wxVariant& variant, int intVal, int argFlags ) const
{
// If wxPG_FULL_VALUE is *not* in argFlags, then intVal is index from combo box.
//
ms_nextIndex = -2;
if ( argFlags & wxPG_FULL_VALUE )
{
ms_nextIndex = GetIndexForValue( intVal );
}
else
{
if ( intVal != GetIndex() )
{
ms_nextIndex = intVal;
}
}
if ( ms_nextIndex != -2 )
{
if ( !(argFlags & wxPG_FULL_VALUE) )
intVal = m_choices.GetValue(intVal);
variant = (long)intVal;
return true;
}
return false;
}
void
wxEnumProperty::OnValidationFailure( wxVariant& WXUNUSED(pendingValue) )
{
// Revert index
ResetNextIndex();
}
void wxEnumProperty::SetIndex( int index )
{
ms_nextIndex = -2;
m_index = index;
}
int wxEnumProperty::GetIndex() const
{
if ( m_value.IsNull() )
return -1;
if ( ms_nextIndex != -2 )
return ms_nextIndex;
return m_index;
}
// -----------------------------------------------------------------------
// wxEditEnumProperty
// -----------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxEditEnumProperty, wxPGProperty)
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEditEnumProperty,wxString,ComboBox)
wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
const long* values, const wxString& value )
: wxEnumProperty(label,name,labels,values,0)
{
SetValue( value );
}
wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
const long* values, wxPGChoices* choicesCache, const wxString& value )
: wxEnumProperty(label,name,labels,values,choicesCache,0)
{
SetValue( value );
}
wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name,
const wxArrayString& labels, const wxArrayInt& values, const wxString& value )
: wxEnumProperty(label,name,labels,values,0)
{
SetValue( value );
}
wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name,
wxPGChoices& choices, const wxString& value )
: wxEnumProperty(label,name,choices,0)
{
SetValue( value );
}
wxEditEnumProperty::~wxEditEnumProperty()
{
}
// -----------------------------------------------------------------------
// wxFlagsProperty
// -----------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxFlagsProperty,wxPGProperty)
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxFlagsProperty,long,TextCtrl)
void wxFlagsProperty::Init()
{
long value = m_value;
//
// Generate children
//
unsigned int i;
unsigned int prevChildCount = m_children.size();
int oldSel = -1;
if ( prevChildCount )
{
wxPropertyGridPageState* state = GetParentState();
// State safety check (it may be NULL in immediate parent)
wxASSERT( state );
if ( state )
{
wxPGProperty* selected = state->GetSelection();
if ( selected )
{
if ( selected->GetParent() == this )
oldSel = selected->GetIndexInParent();
else if ( selected == this )
oldSel = -2;
}
}
state->DoClearSelection();
}
// Delete old children
for ( i=0; i<prevChildCount; i++ )
delete m_children[i];
m_children.clear();
// Relay wxPG_BOOL_USE_CHECKBOX and wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING
// to child bool property controls.
long attrUseCheckBox = GetAttributeAsLong(wxPG_BOOL_USE_CHECKBOX, 0);
long attrUseDCC = GetAttributeAsLong(wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING,
0);
if ( m_choices.IsOk() )
{
const wxPGChoices& choices = m_choices;
for ( i=0; i<GetItemCount(); i++ )
{
bool child_val;
child_val = ( value & choices.GetValue(i) )?true:false;
wxPGProperty* boolProp;
wxString label = GetLabel(i);
#if wxUSE_INTL
if ( wxPGGlobalVars->m_autoGetTranslation )
{
boolProp = new wxBoolProperty( ::wxGetTranslation(label), label, child_val );
}
else
#endif
{
boolProp = new wxBoolProperty( label, label, child_val );
}
if ( attrUseCheckBox )
boolProp->SetAttribute(wxPG_BOOL_USE_CHECKBOX,
true);
if ( attrUseDCC )
boolProp->SetAttribute(wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING,
true);
AddPrivateChild(boolProp);
}
m_oldChoicesData = m_choices.GetDataPtr();
}
m_oldValue = m_value;
if ( prevChildCount )
SubPropsChanged(oldSel);
}
wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
const wxChar* const* labels, const long* values, long value ) : wxPGProperty(label,name)
{
m_oldChoicesData = NULL;
if ( labels )
{
m_choices.Set(labels,values);
wxASSERT( GetItemCount() );
SetValue( value );
}
else
{
m_value = wxPGVariant_Zero;
}
}
wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
const wxArrayString& labels, const wxArrayInt& values, int value )
: wxPGProperty(label,name)
{
m_oldChoicesData = NULL;
if ( &labels && labels.size() )
{
m_choices.Set(labels,values);
wxASSERT( GetItemCount() );
SetValue( (long)value );
}
else
{
m_value = wxPGVariant_Zero;
}
}
wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
wxPGChoices& choices, long value )
: wxPGProperty(label,name)
{
m_oldChoicesData = NULL;
if ( choices.IsOk() )
{
m_choices.Assign(choices);
wxASSERT( GetItemCount() );
SetValue( value );
}
else
{
m_value = wxPGVariant_Zero;
}
}
wxFlagsProperty::~wxFlagsProperty()
{
}
void wxFlagsProperty::OnSetValue()
{
if ( !m_choices.IsOk() || !GetItemCount() )
{
m_value = wxPGVariant_Zero;
}
else
{
long val = m_value.GetLong();
long fullFlags = 0;
// normalize the value (i.e. remove extra flags)
unsigned int i;
const wxPGChoices& choices = m_choices;
for ( i = 0; i < GetItemCount(); i++ )
{
fullFlags |= choices.GetValue(i);
}
val &= fullFlags;
m_value = val;
// Need to (re)init now?
if ( GetChildCount() != GetItemCount() ||
m_choices.GetDataPtr() != m_oldChoicesData )
{
Init();
}
}
long newFlags = m_value;
if ( newFlags != m_oldValue )
{
// Set child modified states
unsigned int i;
const wxPGChoices& choices = m_choices;
for ( i = 0; i<GetItemCount(); i++ )
{
int flag;
flag = choices.GetValue(i);
if ( (newFlags & flag) != (m_oldValue & flag) )
Item(i)->ChangeFlag( wxPG_PROP_MODIFIED, true );
}
m_oldValue = newFlags;
}
}
wxString wxFlagsProperty::ValueToString( wxVariant& value,
int WXUNUSED(argFlags) ) const
{
wxString text;
if ( !m_choices.IsOk() )
return text;
long flags = value;
unsigned int i;
const wxPGChoices& choices = m_choices;
for ( i = 0; i < GetItemCount(); i++ )
{
int doAdd;
doAdd = ( flags & choices.GetValue(i) );
if ( doAdd )
{
text += choices.GetLabel(i);
text += wxS(", ");
}
}
// remove last comma
if ( text.Len() > 1 )
text.Truncate ( text.Len() - 2 );
return text;
}
// Translate string into flag tokens
bool wxFlagsProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
{
if ( !m_choices.IsOk() )
return false;
long newFlags = 0;
// semicolons are no longer valid delimeters
WX_PG_TOKENIZER1_BEGIN(text,wxS(','))
if ( !token.empty() )
{
// Determine which one it is
long bit = IdToBit( token );
if ( bit != -1 )
{
// Changed?
newFlags |= bit;
}
else
{
break;
}
}
WX_PG_TOKENIZER1_END()
if ( variant != (long)newFlags )
{
variant = (long)newFlags;
return true;
}
return false;
}
// Converts string id to a relevant bit.
long wxFlagsProperty::IdToBit( const wxString& id ) const
{
unsigned int i;
for ( i = 0; i < GetItemCount(); i++ )
{
if ( id == GetLabel(i) )
{
return m_choices.GetValue(i);
}
}
return -1;
}
void wxFlagsProperty::RefreshChildren()
{
if ( !m_choices.IsOk() || !GetChildCount() ) return;
int flags = m_value.GetLong();
const wxPGChoices& choices = m_choices;
unsigned int i;
for ( i = 0; i < GetItemCount(); i++ )
{
long flag;
flag = choices.GetValue(i);
long subVal = flags & flag;
wxPGProperty* p = Item(i);
if ( subVal != (m_oldValue & flag) )
p->ChangeFlag( wxPG_PROP_MODIFIED, true );
p->SetValue( subVal?true:false );
}
m_oldValue = flags;
}
wxVariant wxFlagsProperty::ChildChanged( wxVariant& thisValue,
int childIndex,
wxVariant& childValue ) const
{
long oldValue = thisValue.GetLong();
long val = childValue.GetLong();
unsigned long vi = m_choices.GetValue(childIndex);
if ( val )
return (long) (oldValue | vi);
return (long) (oldValue & ~(vi));
}
bool wxFlagsProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
if ( name == wxPG_BOOL_USE_CHECKBOX ||
name == wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING )
{
for ( size_t i=0; i<GetChildCount(); i++ )
{
Item(i)->SetAttribute(name, value);
}
// Must return false so that the attribute is stored in
// flag property's actual property storage
return false;
}
return false;
}
// -----------------------------------------------------------------------
// wxDirProperty
// -----------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxDirProperty, wxLongStringProperty)
wxDirProperty::wxDirProperty( const wxString& name, const wxString& label, const wxString& value )
: wxLongStringProperty(name,label,value)
{
m_flags |= wxPG_PROP_NO_ESCAPE;
}
wxDirProperty::~wxDirProperty() { }
wxValidator* wxDirProperty::DoGetValidator() const
{
return wxFileProperty::GetClassValidator();
}
bool wxDirProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
{
// Update property value from editor, if necessary
wxSize dlg_sz(300,400);
wxString dlgMessage(m_dlgMessage);
if ( dlgMessage.empty() )
dlgMessage = _("Choose a directory:");
wxDirDialog dlg( propGrid,
dlgMessage,
value,
0,
#if !wxPG_SMALL_SCREEN
propGrid->GetGoodEditorDialogPosition(this,dlg_sz),
dlg_sz
#else
wxDefaultPosition,
wxDefaultSize
#endif
);
if ( dlg.ShowModal() == wxID_OK )
{
value = dlg.GetPath();
return true;
}
return false;
}
bool wxDirProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
if ( name == wxPG_DIR_DIALOG_MESSAGE )
{
m_dlgMessage = value.GetString();
return true;
}
return false;
}
// -----------------------------------------------------------------------
// wxPGFileDialogAdapter
// -----------------------------------------------------------------------
bool wxPGFileDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
{
wxFileProperty* fileProp = NULL;
wxString path;
int indFilter = -1;
if ( wxDynamicCast(property, wxFileProperty) )
{
fileProp = ((wxFileProperty*)property);
wxFileName filename = fileProp->GetValue().GetString();
path = filename.GetPath();
indFilter = fileProp->m_indFilter;
if ( path.empty() && !fileProp->m_basePath.empty() )
path = fileProp->m_basePath;
}
else
{
wxFileName fn(property->GetValue().GetString());
path = fn.GetPath();
}
wxFileDialog dlg( propGrid->GetPanel(),
property->GetAttribute(wxS("DialogTitle"), _("Choose a file")),
property->GetAttribute(wxS("InitialPath"), path),
wxEmptyString,
property->GetAttribute(wxPG_FILE_WILDCARD, wxALL_FILES),
property->GetAttributeAsLong(wxPG_FILE_DIALOG_STYLE, 0),
wxDefaultPosition );
if ( indFilter >= 0 )
dlg.SetFilterIndex( indFilter );
if ( dlg.ShowModal() == wxID_OK )
{
if ( fileProp )
fileProp->m_indFilter = dlg.GetFilterIndex();
SetValue( dlg.GetPath() );
return true;
}
return false;
}
// -----------------------------------------------------------------------
// wxFileProperty
// -----------------------------------------------------------------------
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFileProperty,wxPGProperty,
wxString,const wxString&,TextCtrlAndButton)
wxFileProperty::wxFileProperty( const wxString& label, const wxString& name,
const wxString& value ) : wxPGProperty(label,name)
{
m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
m_indFilter = -1;
SetAttribute( wxPG_FILE_WILDCARD, wxALL_FILES);
SetValue(value);
}
wxFileProperty::~wxFileProperty() {}
wxValidator* wxFileProperty::GetClassValidator()
{
#if wxUSE_VALIDATORS
WX_PG_DOGETVALIDATOR_ENTRY()
// Atleast wxPython 2.6.2.1 required that the string argument is given
static wxString v;
wxTextValidator* validator = new wxTextValidator(wxFILTER_EXCLUDE_CHAR_LIST,&v);
wxArrayString exChars;
exChars.Add(wxS("?"));
exChars.Add(wxS("*"));
exChars.Add(wxS("|"));
exChars.Add(wxS("<"));
exChars.Add(wxS(">"));
exChars.Add(wxS("\""));
validator->SetExcludes(exChars);
WX_PG_DOGETVALIDATOR_EXIT(validator)
#else
return NULL;
#endif
}
wxValidator* wxFileProperty::DoGetValidator() const
{
return GetClassValidator();
}
void wxFileProperty::OnSetValue()
{
const wxString& fnstr = m_value.GetString();
wxFileName filename = fnstr;
if ( !filename.HasName() )
{
m_value = wxPGVariant_EmptyString;
}
// Find index for extension.
if ( m_indFilter < 0 && !fnstr.empty() )
{
wxString ext = filename.GetExt();
int curind = 0;
size_t pos = 0;
size_t len = m_wildcard.length();
pos = m_wildcard.find(wxS("|"), pos);
while ( pos != wxString::npos && pos < (len-3) )
{
size_t ext_begin = pos + 3;
pos = m_wildcard.find(wxS("|"), ext_begin);
if ( pos == wxString::npos )
pos = len;
wxString found_ext = m_wildcard.substr(ext_begin, pos-ext_begin);
if ( !found_ext.empty() )
{
if ( found_ext[0] == wxS('*') )
{
m_indFilter = curind;
break;
}
if ( ext.CmpNoCase(found_ext) == 0 )
{
m_indFilter = curind;
break;
}
}
if ( pos != len )
pos = m_wildcard.find(wxS("|"), pos+1);
curind++;
}
}
}
wxFileName wxFileProperty::GetFileName() const
{
wxFileName filename;
if ( !m_value.IsNull() )
filename = m_value.GetString();
return filename;
}
wxString wxFileProperty::ValueToString( wxVariant& value,
int argFlags ) const
{
wxFileName filename = value.GetString();
if ( !filename.HasName() )
return wxEmptyString;
wxString fullName = filename.GetFullName();
if ( fullName.empty() )
return wxEmptyString;
if ( argFlags & wxPG_FULL_VALUE )
{
return filename.GetFullPath();
}
else if ( m_flags & wxPG_PROP_SHOW_FULL_FILENAME )
{
if ( !m_basePath.empty() )
{
wxFileName fn2(filename);
fn2.MakeRelativeTo(m_basePath);
return fn2.GetFullPath();
}
return filename.GetFullPath();
}
return filename.GetFullName();
}
wxPGEditorDialogAdapter* wxFileProperty::GetEditorDialog() const
{
return new wxPGFileDialogAdapter();
}
bool wxFileProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
{
wxFileName filename = variant.GetString();
if ( (m_flags & wxPG_PROP_SHOW_FULL_FILENAME) || (argFlags & wxPG_FULL_VALUE) )
{
if ( filename != text )
{
variant = text;
return true;
}
}
else
{
if ( filename.GetFullName() != text )
{
wxFileName fn = filename;
fn.SetFullName(text);
variant = fn.GetFullPath();
return true;
}
}
return false;
}
bool wxFileProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
// Return false on some occasions to make sure those attribs will get
// stored in m_attributes.
if ( name == wxPG_FILE_SHOW_FULL_PATH )
{
if ( value.GetLong() )
m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
else
m_flags &= ~(wxPG_PROP_SHOW_FULL_FILENAME);
return true;
}
else if ( name == wxPG_FILE_WILDCARD )
{
m_wildcard = value.GetString();
}
else if ( name == wxPG_FILE_SHOW_RELATIVE_PATH )
{
m_basePath = value.GetString();
// Make sure wxPG_FILE_SHOW_FULL_PATH is also set
m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
}
else if ( name == wxPG_FILE_INITIAL_PATH )
{
m_initialPath = value.GetString();
return true;
}
else if ( name == wxPG_FILE_DIALOG_TITLE )
{
m_dlgTitle = value.GetString();
return true;
}
return false;
}
// -----------------------------------------------------------------------
// wxPGLongStringDialogAdapter
// -----------------------------------------------------------------------
bool wxPGLongStringDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
{
wxString val1 = property->GetValueAsString(0);
wxString val_orig = val1;
wxString value;
if ( !property->HasFlag(wxPG_PROP_NO_ESCAPE) )
wxPropertyGrid::ExpandEscapeSequences(value, val1);
else
value = wxString(val1);
// Run editor dialog.
if ( wxLongStringProperty::DisplayEditorDialog(property, propGrid, value) )
{
if ( !property->HasFlag(wxPG_PROP_NO_ESCAPE) )
wxPropertyGrid::CreateEscapeSequences(val1,value);
else
val1 = value;
if ( val1 != val_orig )
{
SetValue( val1 );
return true;
}
}
return false;
}
// -----------------------------------------------------------------------
// wxLongStringProperty
// -----------------------------------------------------------------------
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxLongStringProperty,wxPGProperty,
wxString,const wxString&,TextCtrlAndButton)
wxLongStringProperty::wxLongStringProperty( const wxString& label, const wxString& name,
const wxString& value ) : wxPGProperty(label,name)
{
SetValue(value);
}
wxLongStringProperty::~wxLongStringProperty() {}
wxString wxLongStringProperty::ValueToString( wxVariant& value,
int WXUNUSED(argFlags) ) const
{
return value;
}
bool wxLongStringProperty::OnEvent( wxPropertyGrid* propGrid, wxWindow* WXUNUSED(primary),
wxEvent& event )
{
if ( propGrid->IsMainButtonEvent(event) )
{
// Update the value
wxVariant useValue = propGrid->GetUncommittedPropertyValue();
wxString val1 = useValue.GetString();
wxString val_orig = val1;
wxString value;
if ( !(m_flags & wxPG_PROP_NO_ESCAPE) )
wxPropertyGrid::ExpandEscapeSequences(value,val1);
else
value = wxString(val1);
// Run editor dialog.
if ( OnButtonClick(propGrid,value) )
{
if ( !(m_flags & wxPG_PROP_NO_ESCAPE) )
wxPropertyGrid::CreateEscapeSequences(val1,value);
else
val1 = value;
if ( val1 != val_orig )
{
SetValueInEvent( val1 );
return true;
}
}
}
return false;
}
bool wxLongStringProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
{
return DisplayEditorDialog(this, propGrid, value);
}
bool wxLongStringProperty::DisplayEditorDialog( wxPGProperty* prop, wxPropertyGrid* propGrid, wxString& value )
{
// launch editor dialog
wxDialog* dlg = new wxDialog(propGrid,-1,prop->GetLabel(),wxDefaultPosition,wxDefaultSize,
wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxCLIP_CHILDREN);
dlg->SetFont(propGrid->GetFont()); // To allow entering chars of the same set as the propGrid
// Multi-line text editor dialog.
#if !wxPG_SMALL_SCREEN
const int spacing = 8;
#else
const int spacing = 4;
#endif
wxBoxSizer* topsizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* rowsizer = new wxBoxSizer( wxHORIZONTAL );
wxTextCtrl* ed = new wxTextCtrl(dlg,11,value,
wxDefaultPosition,wxDefaultSize,wxTE_MULTILINE);
rowsizer->Add( ed, 1, wxEXPAND|wxALL, spacing );
topsizer->Add( rowsizer, 1, wxEXPAND, 0 );
wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
buttonSizer->AddButton(new wxButton(dlg, wxID_OK));
buttonSizer->AddButton(new wxButton(dlg, wxID_CANCEL));
buttonSizer->Realize();
topsizer->Add( buttonSizer, 0,
wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL|wxBOTTOM|wxRIGHT,
spacing );
dlg->SetSizer( topsizer );
topsizer->SetSizeHints( dlg );
#if !wxPG_SMALL_SCREEN
dlg->SetSize(400,300);
dlg->Move( propGrid->GetGoodEditorDialogPosition(prop,dlg->GetSize()) );
#endif
int res = dlg->ShowModal();
if ( res == wxID_OK )
{
value = ed->GetValue();
dlg->Destroy();
return true;
}
dlg->Destroy();
return false;
}
bool wxLongStringProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
{
if ( variant != text )
{
variant = text;
return true;
}
return false;
}
#if wxUSE_EDITABLELISTBOX
// -----------------------------------------------------------------------
// wxPGArrayEditorDialog
// -----------------------------------------------------------------------
BEGIN_EVENT_TABLE(wxPGArrayEditorDialog, wxDialog)
EVT_IDLE(wxPGArrayEditorDialog::OnIdle)
END_EVENT_TABLE()
IMPLEMENT_ABSTRACT_CLASS(wxPGArrayEditorDialog, wxDialog)
#include "wx/editlbox.h"
#include "wx/listctrl.h"
// -----------------------------------------------------------------------
void wxPGArrayEditorDialog::OnIdle(wxIdleEvent& event)
{
// Repair focus - wxEditableListBox has bitmap buttons, which
// get focus, and lose focus (into the oblivion) when they
// become disabled due to change in control state.
wxWindow* lastFocused = m_lastFocused;
wxWindow* focus = ::wxWindow::FindFocus();
// If last focused control became disabled, set focus back to
// wxEditableListBox
if ( lastFocused && focus != lastFocused &&
lastFocused->GetParent() == m_elbSubPanel &&
!lastFocused->IsEnabled() )
{
m_elb->GetListCtrl()->SetFocus();
}
m_lastFocused = focus;
event.Skip();
}
// -----------------------------------------------------------------------
wxPGArrayEditorDialog::wxPGArrayEditorDialog()
: wxDialog()
{
Init();
}
// -----------------------------------------------------------------------
void wxPGArrayEditorDialog::Init()
{
m_lastFocused = NULL;
m_hasCustomNewAction = false;
m_itemPendingAtIndex = -1;
}
// -----------------------------------------------------------------------
wxPGArrayEditorDialog::wxPGArrayEditorDialog( wxWindow *parent,
const wxString& message,
const wxString& caption,
long style,
const wxPoint& pos,
const wxSize& sz )
: wxDialog()
{
Init();
Create(parent,message,caption,style,pos,sz);
}
// -----------------------------------------------------------------------
bool wxPGArrayEditorDialog::Create( wxWindow *parent,
const wxString& message,
const wxString& caption,
long style,
const wxPoint& pos,
const wxSize& sz )
{
// On wxMAC the dialog shows incorrectly if style is not exactly wxCAPTION
// FIXME: This should be only a temporary fix.
#ifdef __WXMAC__
wxUnusedVar(style);
int useStyle = wxCAPTION;
#else
int useStyle = style;
#endif
bool res = wxDialog::Create(parent, wxID_ANY, caption, pos, sz, useStyle);
SetFont(parent->GetFont()); // To allow entering chars of the same set as the propGrid
#if !wxPG_SMALL_SCREEN
const int spacing = 4;
#else
const int spacing = 3;
#endif
m_modified = false;
wxBoxSizer* topsizer = new wxBoxSizer( wxVERTICAL );
// Message
if ( !message.empty() )
topsizer->Add( new wxStaticText(this,-1,message),
0, wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL|wxALL, spacing );
m_elb = new wxEditableListBox(this, wxID_ANY, message,
wxDefaultPosition,
wxDefaultSize,
wxEL_ALLOW_NEW |
wxEL_ALLOW_EDIT |
wxEL_ALLOW_DELETE);
// Populate the list box
wxArrayString arr;
for ( unsigned int i=0; i<ArrayGetCount(); i++ )
arr.push_back(ArrayGet(i));
m_elb->SetStrings(arr);
// Connect event handlers
wxButton* but;
wxListCtrl* lc = m_elb->GetListCtrl();
but = m_elb->GetNewButton();
m_elbSubPanel = but->GetParent();
but->Connect(but->GetId(), wxEVT_BUTTON,
wxCommandEventHandler(wxPGArrayEditorDialog::OnAddClick),
NULL, this);
but = m_elb->GetDelButton();
but->Connect(but->GetId(), wxEVT_BUTTON,
wxCommandEventHandler(wxPGArrayEditorDialog::OnDeleteClick),
NULL, this);
but = m_elb->GetUpButton();
but->Connect(but->GetId(), wxEVT_BUTTON,
wxCommandEventHandler(wxPGArrayEditorDialog::OnUpClick),
NULL, this);
but = m_elb->GetDownButton();
but->Connect(but->GetId(), wxEVT_BUTTON,
wxCommandEventHandler(wxPGArrayEditorDialog::OnDownClick),
NULL, this);
lc->Connect(lc->GetId(), wxEVT_LIST_END_LABEL_EDIT,
wxListEventHandler(wxPGArrayEditorDialog::OnEndLabelEdit),
NULL, this);
topsizer->Add( m_elb, 1, wxEXPAND, spacing );
// Standard dialog buttons
wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
buttonSizer->AddButton(new wxButton(this, wxID_OK));
buttonSizer->AddButton(new wxButton(this, wxID_CANCEL));
buttonSizer->Realize();
topsizer->Add( buttonSizer, 0,
wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL|wxALL,
spacing );
m_elb->SetFocus();
SetSizer( topsizer );
topsizer->SetSizeHints( this );
#if !wxPG_SMALL_SCREEN
if ( sz.x == wxDefaultSize.x &&
sz.y == wxDefaultSize.y )
SetSize( wxSize(275,360) );
else
SetSize(sz);
#endif
return res;
}
// -----------------------------------------------------------------------
int wxPGArrayEditorDialog::GetSelection() const
{
wxListCtrl* lc = m_elb->GetListCtrl();
int index = lc->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if ( index == -1 )
return wxNOT_FOUND;
return index;
}
// -----------------------------------------------------------------------
void wxPGArrayEditorDialog::OnAddClick(wxCommandEvent& event)
{
wxListCtrl* lc = m_elb->GetListCtrl();
int newItemIndex = lc->GetItemCount() - 1;
if ( m_hasCustomNewAction )
{
wxString str;
if ( OnCustomNewAction(&str) )
{
if ( ArrayInsert(str, newItemIndex) )
{
lc->InsertItem(newItemIndex, str);
m_modified = true;
}
}
// Do *not* skip the event! We do not want the wxEditableListBox
// to do anything.
}
else
{
m_itemPendingAtIndex = newItemIndex;
event.Skip();
}
}
// -----------------------------------------------------------------------
void wxPGArrayEditorDialog::OnDeleteClick(wxCommandEvent& event)
{
int index = GetSelection();
if ( index >= 0 )
{
ArrayRemoveAt( index );
m_modified = true;
}
event.Skip();
}
// -----------------------------------------------------------------------
void wxPGArrayEditorDialog::OnUpClick(wxCommandEvent& event)
{
int index = GetSelection();
if ( index > 0 )
{
ArraySwap(index-1,index);
m_modified = true;
}
event.Skip();
}
// -----------------------------------------------------------------------
void wxPGArrayEditorDialog::OnDownClick(wxCommandEvent& event)
{
wxListCtrl* lc = m_elb->GetListCtrl();
int index = GetSelection();
int lastStringIndex = lc->GetItemCount() - 1;
if ( index >= 0 && index < lastStringIndex )
{
ArraySwap(index, index+1);
m_modified = true;
}
event.Skip();
}
// -----------------------------------------------------------------------
void wxPGArrayEditorDialog::OnEndLabelEdit(wxListEvent& event)
{
wxString str = event.GetLabel();
if ( m_itemPendingAtIndex >= 0 )
{
// Add a new item
if ( ArrayInsert(str, m_itemPendingAtIndex) )
{
m_modified = true;
}
else
{
// Editable list box doesn't really respect Veto(), but
// it recognizes if no text was added, so we simulate
// Veto() using it.
event.m_item.SetText(wxEmptyString);
m_elb->GetListCtrl()->SetItemText(m_itemPendingAtIndex,
wxEmptyString);
event.Veto();
}
}
else
{
// Change an existing item
int index = GetSelection();
wxASSERT( index != wxNOT_FOUND );
if ( ArraySet(index, str) )
m_modified = true;
else
event.Veto();
}
event.Skip();
}
#endif // wxUSE_EDITABLELISTBOX
// -----------------------------------------------------------------------
// wxPGArrayStringEditorDialog
// -----------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
BEGIN_EVENT_TABLE(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
END_EVENT_TABLE()
// -----------------------------------------------------------------------
wxString wxPGArrayStringEditorDialog::ArrayGet( size_t index )
{
return m_array[index];
}
size_t wxPGArrayStringEditorDialog::ArrayGetCount()
{
return m_array.size();
}
bool wxPGArrayStringEditorDialog::ArrayInsert( const wxString& str, int index )
{
if (index<0)
m_array.Add(str);
else
m_array.Insert(str,index);
return true;
}
bool wxPGArrayStringEditorDialog::ArraySet( size_t index, const wxString& str )
{
m_array[index] = str;
return true;
}
void wxPGArrayStringEditorDialog::ArrayRemoveAt( int index )
{
m_array.RemoveAt(index);
}
void wxPGArrayStringEditorDialog::ArraySwap( size_t first, size_t second )
{
wxString old_str = m_array[first];
wxString new_str = m_array[second];
m_array[first] = new_str;
m_array[second] = old_str;
}
wxPGArrayStringEditorDialog::wxPGArrayStringEditorDialog()
: wxPGArrayEditorDialog()
{
Init();
}
void wxPGArrayStringEditorDialog::Init()
{
m_pCallingClass = NULL;
}
bool
wxPGArrayStringEditorDialog::OnCustomNewAction(wxString* resString)
{
return m_pCallingClass->OnCustomStringEdit(m_parent, *resString);
}
// -----------------------------------------------------------------------
// wxArrayStringProperty
// -----------------------------------------------------------------------
WX_PG_IMPLEMENT_PROPERTY_CLASS(wxArrayStringProperty, // Property name
wxPGProperty, // Property we inherit from
wxArrayString, // Value type name
const wxArrayString&, // Value type, as given in constructor
TextCtrlAndButton) // Initial editor
wxArrayStringProperty::wxArrayStringProperty( const wxString& label,
const wxString& name,
const wxArrayString& array )
: wxPGProperty(label,name)
{
m_delimiter = ',';
SetValue( array );
}
wxArrayStringProperty::~wxArrayStringProperty() { }
void wxArrayStringProperty::OnSetValue()
{
GenerateValueAsString();
}
void
wxArrayStringProperty::ConvertArrayToString(const wxArrayString& arr,
wxString* pString,
const wxUniChar& delimiter) const
{
if ( delimiter == '"' || delimiter == '\'' )
{
// Quoted strings
ArrayStringToString(*pString,
arr,
delimiter,
Escape | QuoteStrings);
}
else
{
// Regular delimiter
ArrayStringToString(*pString,
arr,
delimiter,
0);
}
}
wxString wxArrayStringProperty::ValueToString( wxVariant& WXUNUSED(value),
int argFlags ) const
{
//
// If this is called from GetValueAsString(), return cached string
if ( argFlags & wxPG_VALUE_IS_CURRENT )
{
return m_display;
}
wxArrayString arr = m_value.GetArrayString();
wxString s;
ConvertArrayToString(arr, &s, m_delimiter);
return s;
}
// Converts wxArrayString to a string separated by delimeters and spaces.
// preDelim is useful for "str1" "str2" style. Set flags to 1 to do slash
// conversion.
void
wxArrayStringProperty::ArrayStringToString( wxString& dst,
const wxArrayString& src,
wxUniChar delimiter, int flags )
{
wxString pdr;
wxString preas;
unsigned int i;
unsigned int itemCount = src.size();
dst.Empty();
if ( flags & Escape )
{
preas = delimiter;
pdr = wxS("\\");
pdr += delimiter;
}
if ( itemCount )
dst.append( preas );
wxString delimStr(delimiter);
for ( i = 0; i < itemCount; i++ )
{
wxString str( src.Item(i) );
// Do some character conversion.
// Converts \ to \\ and $delimiter to \$delimiter
// Useful when quoting.
if ( flags & Escape )
{
str.Replace( wxS("\\"), wxS("\\\\"), true );
if ( !pdr.empty() )
str.Replace( preas, pdr, true );
}
dst.append( str );
if ( i < (itemCount-1) )
{
dst.append( delimStr );
dst.append( wxS(" ") );
dst.append( preas );
}
else if ( flags & QuoteStrings )
dst.append( delimStr );
}
}
void wxArrayStringProperty::GenerateValueAsString()
{
wxArrayString arr = m_value.GetArrayString();
ConvertArrayToString(arr, &m_display, m_delimiter);
}
// Default implementation doesn't do anything.
bool wxArrayStringProperty::OnCustomStringEdit( wxWindow*, wxString& )
{
return false;
}
wxPGArrayEditorDialog* wxArrayStringProperty::CreateEditorDialog()
{
return new wxPGArrayStringEditorDialog();
}
bool wxArrayStringProperty::OnButtonClick( wxPropertyGrid* propGrid,
wxWindow* WXUNUSED(primaryCtrl),
const wxChar* cbt )
{
// Update the value
wxVariant useValue = propGrid->GetUncommittedPropertyValue();
if ( !propGrid->EditorValidate() )
return false;
// Create editor dialog.
wxPGArrayEditorDialog* dlg = CreateEditorDialog();
#if wxUSE_VALIDATORS
wxValidator* validator = GetValidator();
wxPGInDialogValidator dialogValidator;
#endif
wxPGArrayStringEditorDialog* strEdDlg = wxDynamicCast(dlg, wxPGArrayStringEditorDialog);
if ( strEdDlg )
strEdDlg->SetCustomButton(cbt, this);
dlg->SetDialogValue( useValue );
dlg->Create(propGrid, wxEmptyString, m_label);
#if !wxPG_SMALL_SCREEN
dlg->Move( propGrid->GetGoodEditorDialogPosition(this,dlg->GetSize()) );
#endif
bool retVal;
for (;;)
{
retVal = false;
int res = dlg->ShowModal();
if ( res == wxID_OK && dlg->IsModified() )
{
wxVariant value = dlg->GetDialogValue();
if ( !value.IsNull() )
{
wxArrayString actualValue = value.GetArrayString();
wxString tempStr;
ConvertArrayToString(actualValue, &tempStr, m_delimiter);
#if wxUSE_VALIDATORS
if ( dialogValidator.DoValidate(propGrid, validator,
tempStr) )
#endif
{
SetValueInEvent( actualValue );
retVal = true;
break;
}
}
else
break;
}
else
break;
}
delete dlg;
return retVal;
}
bool wxArrayStringProperty::OnEvent( wxPropertyGrid* propGrid,
wxWindow* primary,
wxEvent& event )
{
if ( propGrid->IsMainButtonEvent(event) )
return OnButtonClick(propGrid,primary,(const wxChar*) NULL);
return false;
}
bool wxArrayStringProperty::StringToValue( wxVariant& variant,
const wxString& text, int ) const
{
wxArrayString arr;
if ( m_delimiter == '"' || m_delimiter == '\'' )
{
// Quoted strings
WX_PG_TOKENIZER2_BEGIN(text, m_delimiter)
// Need to replace backslashes with empty characters
// (opposite what is done in ConvertArrayToString()).
token.Replace ( wxS("\\\\"), wxS("\\"), true );
arr.Add( token );
WX_PG_TOKENIZER2_END()
}
else
{
// Regular delimiter
WX_PG_TOKENIZER1_BEGIN(text, m_delimiter)
arr.Add( token );
WX_PG_TOKENIZER1_END()
}
variant = arr;
return true;
}
bool wxArrayStringProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
if ( name == wxPG_ARRAY_DELIMITER )
{
m_delimiter = value.GetChar();
GenerateValueAsString();
return false;
}
return true;
}
// -----------------------------------------------------------------------
// wxPGInDialogValidator
// -----------------------------------------------------------------------
#if wxUSE_VALIDATORS
bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* propGrid,
wxValidator* validator,
const wxString& value )
{
if ( !validator )
return true;
wxTextCtrl* tc = m_textCtrl;
if ( !tc )
{
{
tc = new wxTextCtrl( propGrid, wxPG_SUBID_TEMP1, wxEmptyString,
wxPoint(30000,30000));
tc->Hide();
}
m_textCtrl = tc;
}
tc->SetValue(value);
validator->SetWindow(tc);
bool res = validator->Validate(propGrid);
return res;
}
#else
bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* WXUNUSED(propGrid),
wxValidator* WXUNUSED(validator),
const wxString& WXUNUSED(value) )
{
return true;
}
#endif
// -----------------------------------------------------------------------
#endif // wxUSE_PROPGRID