UI infrastructure enhancements and bug fixes.

Work around wxWidgets failure to send first key through validator.

Unify treatment of INDETERMINATE values (such as for multiple
selections with mixed values).

(cherry picked from commit 7308729)
This commit is contained in:
Jeff Young 2018-03-07 12:48:08 +00:00
parent f7861c90ec
commit 31aebe6920
9 changed files with 226 additions and 83 deletions

View File

@ -423,7 +423,7 @@ DIALOG_ENV_VAR_SINGLE::DIALOG_ENV_VAR_SINGLE( wxWindow* parent,
{
m_envVarName->SetValue( aEnvVarName );
m_envVarPath->SetValue( aEnvVarPath );
m_envVarName->SetValidator( ENVIRONMENT_VARIABLE_CHAR_VALIDATOR() );
m_envVarName->SetValidator( ENV_VAR_NAME_VALIDATOR() );
}

View File

@ -30,10 +30,39 @@
#include <kicad_string.h>
#include <validators.h>
#include <wx/grid.h>
#include <wx/textctrl.h>
#include <wx/textentry.h>
#include <wx/log.h>
GRID_CELL_TEXT_EDITOR::GRID_CELL_TEXT_EDITOR() : wxGridCellTextEditor()
{
}
void GRID_CELL_TEXT_EDITOR::SetValidator(const wxValidator& validator)
{
// keep our own copy because wxGridCellTextEditor's is annoyingly private
m_validator.reset( static_cast<wxValidator*>( validator.Clone() ) );
wxGridCellTextEditor::SetValidator( validator );
}
void GRID_CELL_TEXT_EDITOR::StartingKey( wxKeyEvent& event )
{
if( m_validator )
{
m_validator.get()->SetWindow( Text() );
m_validator.get()->ProcessEvent( event );
}
if( !event.WasProcessed() )
wxGridCellTextEditor::StartingKey( event );
}
FILE_NAME_CHAR_VALIDATOR::FILE_NAME_CHAR_VALIDATOR( wxString* aValue ) :
wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST, aValue )
{
@ -77,9 +106,74 @@ FILE_NAME_WITH_PATH_CHAR_VALIDATOR::FILE_NAME_WITH_PATH_CHAR_VALIDATOR( wxString
}
ENVIRONMENT_VARIABLE_CHAR_VALIDATOR::ENVIRONMENT_VARIABLE_CHAR_VALIDATOR( wxString* aValue ) :
wxTextValidator( wxFILTER_INCLUDE_CHAR_LIST | wxFILTER_EMPTY, aValue )
ENV_VAR_NAME_VALIDATOR::ENV_VAR_NAME_VALIDATOR( wxString* aValue ) :
wxTextValidator()
{
wxString includeChars( wxT( "abcdefghijklmnopqrstuvwxyABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" ) );
SetCharIncludes( includeChars );
Connect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
}
ENV_VAR_NAME_VALIDATOR::ENV_VAR_NAME_VALIDATOR( const ENV_VAR_NAME_VALIDATOR& val )
: wxTextValidator()
{
wxValidator::Copy( val );
Connect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
}
ENV_VAR_NAME_VALIDATOR::~ENV_VAR_NAME_VALIDATOR()
{
Disconnect( wxEVT_CHAR, wxKeyEventHandler( ENV_VAR_NAME_VALIDATOR::OnChar ) );
}
void ENV_VAR_NAME_VALIDATOR::OnChar( wxKeyEvent& aEvent )
{
if (!m_validatorWindow)
{
aEvent.Skip();
return;
}
int keyCode = aEvent.GetKeyCode();
// we don't filter special keys and delete
if (keyCode < WXK_SPACE || keyCode == WXK_DELETE || keyCode >= WXK_START)
{
aEvent.Skip();
return;
}
wxUniChar c = (wxUChar) keyCode;
if( c == wxT( '_' ) )
{
// OK anywhere
aEvent.Skip();
}
else if( wxIsdigit( c ) )
{
// not as first character
long from, to;
GetTextEntry()->GetSelection( &from, &to );
if( from < 1 )
wxBell();
else
aEvent.Skip();
}
else if( wxIsalpha( c ) )
{
// Capitals only.
// Note: changing the keyCode and/or uniChar in the event and passing it on
// doesn't work. Some platforms look at the original copy as long as the event
// isn't vetoed. Insert the capitalized character by hand.
wxString s( c, 1 );
s = s.Capitalize();
GetTextEntry()->WriteText( s );
}
else
{
wxBell();
}
}

View File

@ -32,11 +32,12 @@
#include "widgets/unit_binder.h"
UNIT_BINDER::UNIT_BINDER( EDA_DRAW_FRAME* aParent,
wxStaticText* aLabel, wxTextEntry* aTextEntry, wxStaticText* aUnitLabel,
wxStaticText* aLabel, wxWindow* aValue, wxStaticText* aUnitLabel,
bool aUseMils, int aMin, int aMax, bool allowEval ) :
m_label( aLabel ),
m_textEntry( aTextEntry ),
m_value( aValue ),
m_unitLabel( aUnitLabel ),
m_showMessage( true ),
m_eval( aParent->GetUserUnits(), aUseMils )
{
// Fix the units (to the current units) for the life of the binder
@ -44,31 +45,30 @@ UNIT_BINDER::UNIT_BINDER( EDA_DRAW_FRAME* aParent,
m_useMils = aUseMils;
m_min = aMin;
m_max = aMax;
m_allowEval = allowEval;
m_allowEval = allowEval && dynamic_cast<wxTextEntry*>( m_value );
auto textEntry = dynamic_cast<wxTextEntry*>( m_value );
if( textEntry )
textEntry->SetValue( wxT( "0" ) );
m_textEntry->SetValue( wxT( "0" ) );
m_unitLabel->SetLabel( GetAbbreviatedUnitsLabel( m_units, aUseMils ) );
wxWindow* textInput = dynamic_cast<wxWindow*>( m_textEntry );
textInput->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( UNIT_BINDER::onSetFocus ), NULL, this );
textInput->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( UNIT_BINDER::onKillFocus ), NULL, this );
textInput->Connect( wxEVT_TEXT_ENTER, wxCommandEventHandler( UNIT_BINDER::onTextEnter ), NULL, this );
}
UNIT_BINDER::~UNIT_BINDER()
{
m_value->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( UNIT_BINDER::onSetFocus ), NULL, this );
m_value->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( UNIT_BINDER::onKillFocus ), NULL, this );
m_value->Connect( wxEVT_TEXT_ENTER, wxCommandEventHandler( UNIT_BINDER::onTextEnter ), NULL, this );
}
void UNIT_BINDER::onSetFocus( wxFocusEvent& aEvent )
{
if( m_allowEval )
auto textEntry = dynamic_cast<wxTextEntry*>( m_value );
if( m_allowEval && textEntry )
{
wxString oldStr = m_eval.OriginalText();
if( oldStr.length() )
m_textEntry->SetValue( oldStr );
textEntry->SetValue( oldStr );
}
aEvent.Skip();
@ -97,17 +97,22 @@ void UNIT_BINDER::onTextEnter( wxCommandEvent& aEvent )
// Send an OK event to the parent dialog
wxCommandEvent event( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK );
wxPostEvent( dynamic_cast<wxWindow*>( m_textEntry )->GetParent(), event );
wxPostEvent( m_value->GetParent(), event );
}
void UNIT_BINDER::evaluate()
{
if( m_textEntry->GetValue().IsEmpty() )
m_textEntry->ChangeValue( "0" );
auto textEntry = dynamic_cast<wxTextEntry*>( m_value );
if( m_eval.Process( m_textEntry->GetValue() ) )
m_textEntry->ChangeValue( m_eval.Result() );
if( !textEntry )
return;
if( textEntry->GetValue().IsEmpty() )
textEntry->ChangeValue( "0" );
if( m_eval.Process( textEntry->GetValue() ) )
textEntry->ChangeValue( m_eval.Result() );
}
@ -122,29 +127,36 @@ wxString valueDescriptionFromLabel( wxStaticText* aLabel )
void UNIT_BINDER::delayedFocusHandler( wxIdleEvent& )
{
wxWindow* textInput = dynamic_cast<wxWindow*>( m_textEntry );
textInput->SetFocus();
m_value->SetFocus();
m_showMessage = true;
textInput->Unbind( wxEVT_IDLE, &UNIT_BINDER::delayedFocusHandler, this );
m_value->Unbind( wxEVT_IDLE, &UNIT_BINDER::delayedFocusHandler, this );
}
bool UNIT_BINDER::Validate( bool setFocusOnError )
{
wxWindow* textInput = dynamic_cast<wxWindow*>( m_textEntry );
auto textEntry = dynamic_cast<wxTextEntry*>( m_value );
if( !textEntry || textEntry->GetValue() == INDETERMINATE )
return true;
if( m_min > INT_MIN && GetValue() < m_min )
{
wxString msg = wxString::Format( _( "%s must be larger than %s." ),
valueDescriptionFromLabel( m_label ),
StringFromValue( m_units, m_min, true ) );
DisplayError( textInput->GetParent(), msg );
if( m_showMessage )
{
wxString msg = wxString::Format( _( "%s must be larger than %s." ),
valueDescriptionFromLabel( m_label ),
StringFromValue( m_units, m_min, true ) );
DisplayError( m_value->GetParent(), msg );
}
if( setFocusOnError )
{
m_textEntry->SelectAll();
textEntry->SelectAll();
// Don't focus directly; we might be inside a KillFocus event handler
textInput->Bind( wxEVT_IDLE, &UNIT_BINDER::delayedFocusHandler, this );
m_value->Bind( wxEVT_IDLE, &UNIT_BINDER::delayedFocusHandler, this );
m_showMessage = false;
}
return false;
@ -152,16 +164,20 @@ bool UNIT_BINDER::Validate( bool setFocusOnError )
if( m_max < INT_MAX && GetValue() > m_max )
{
wxString msg = wxString::Format( _( "%s must be smaller than %s." ),
valueDescriptionFromLabel( m_label ),
StringFromValue( m_units, m_max, true ) );
DisplayError( textInput->GetParent(), msg );
if( m_showMessage )
{
wxString msg = wxString::Format( _( "%s must be smaller than %s." ),
valueDescriptionFromLabel( m_label ),
StringFromValue( m_units, m_max, true ) );
DisplayError( m_value->GetParent(), msg );
}
if( setFocusOnError )
{
m_textEntry->SelectAll();
textEntry->SelectAll();
// Don't focus directly; we might be inside a KillFocus event handler
textInput->Bind( wxEVT_IDLE, &UNIT_BINDER::delayedFocusHandler, this );
m_value->Bind( wxEVT_IDLE, &UNIT_BINDER::delayedFocusHandler, this );
m_showMessage = false;
}
return false;
@ -179,7 +195,10 @@ void UNIT_BINDER::SetValue( int aValue )
void UNIT_BINDER::SetValue( wxString aValue )
{
m_textEntry->SetValue( aValue );
if( dynamic_cast<wxTextEntry*>( m_value ) )
dynamic_cast<wxTextEntry*>( m_value )->SetValue( aValue );
else if( dynamic_cast<wxStaticText*>( m_value ) )
dynamic_cast<wxStaticText*>( m_value )->SetLabel( aValue );
if( m_allowEval )
m_eval.Clear();
@ -190,7 +209,12 @@ void UNIT_BINDER::SetValue( wxString aValue )
int UNIT_BINDER::GetValue() const
{
wxString s = m_textEntry->GetValue();
wxString s;
if( dynamic_cast<wxTextEntry*>( m_value ) )
s = dynamic_cast<wxTextEntry*>( m_value )->GetValue();
else if( dynamic_cast<wxStaticText*>( m_value ) )
s = dynamic_cast<wxStaticText*>( m_value )->GetLabel();
return ValueFromString( m_units, s, m_useMils );
}
@ -198,14 +222,17 @@ int UNIT_BINDER::GetValue() const
bool UNIT_BINDER::IsIndeterminate() const
{
return m_textEntry->GetValue() == INDETERMINATE;
if( dynamic_cast<wxTextEntry*>( m_value ) )
return dynamic_cast<wxTextEntry*>( m_value )->GetValue() == INDETERMINATE;
return false;
}
void UNIT_BINDER::Enable( bool aEnable )
{
m_label->Enable( aEnable );
dynamic_cast<wxWindow*>( m_textEntry )->Enable( aEnable );
m_value->Enable( aEnable );
m_unitLabel->Enable( aEnable );
}

View File

@ -25,16 +25,14 @@
#include <wx/msgdlg.h>
#include <wx/grid.h>
#include <base_units.h>
#include <confirm.h>
#include <bitmaps.h>
#include <grid_tricks.h>
#include <kicad_string.h>
#include <build_version.h>
#include <general.h>
#include <class_library.h>
#include <sch_edit_frame.h>
#include <sch_reference_list.h>
#include <kiface_i.h>
@ -102,10 +100,6 @@ struct DATA_MODEL_ROW
#endif
// Indicator that multiple values exist in child rows
#define ROW_MULT_ITEMS wxString( "< ... >" )
class FIELDS_EDITOR_GRID_DATA_MODEL : public wxGridTableBase
{
protected:
@ -221,7 +215,7 @@ public:
if( &ref == &group.m_Refs.front() )
fieldValue = m_dataStore[ compID ][ m_fieldNames[ aCol ] ];
else if ( fieldValue != m_dataStore[ compID ][ m_fieldNames[ aCol ] ] )
return ROW_MULT_ITEMS;
return INDETERMINATE;
}
}

View File

@ -34,9 +34,6 @@
#include <kiface_i.h>
// Indicator that multiple values exist in child rows
#define ROW_MULT_ITEMS wxString( "< ... >" )
#define PinTableShownColumnsKey wxT( "PinTableShownColumns" )
@ -156,7 +153,7 @@ public:
if( !fieldValue.Length() )
fieldValue = val;
else if( val != fieldValue )
fieldValue = ROW_MULT_ITEMS;
fieldValue = INDETERMINATE;
}
}
@ -165,7 +162,7 @@ public:
void SetValue( int aRow, int aCol, const wxString &aValue ) override
{
if( aValue == ROW_MULT_ITEMS )
if( aValue == INDETERMINATE )
return;
LIB_PINS pins = m_rows[ aRow ];
@ -340,18 +337,18 @@ DIALOG_LIB_EDIT_PIN_TABLE::DIALOG_LIB_EDIT_PIN_TABLE( wxWindow* parent, LIB_PART
g_typeIcons.push_back( GetBitmap( static_cast<ELECTRICAL_PINTYPE>( i ) ) );
for( unsigned i = 0; i < PINTYPE_COUNT; ++i )
g_typeNames.push_back( GetText( static_cast<ELECTRICAL_PINTYPE>( i ) ) );
g_typeNames.push_back( ROW_MULT_ITEMS );
g_typeNames.push_back( INDETERMINATE );
for( unsigned i = 0; i < PINSHAPE_COUNT; ++i )
g_shapeIcons.push_back( GetBitmap( static_cast<GRAPHIC_PINSHAPE>( i ) ) );
for( unsigned i = 0; i < PINSHAPE_COUNT; ++i )
g_shapeNames.push_back( GetText( static_cast<GRAPHIC_PINSHAPE>( i ) ) );
g_shapeNames.push_back( ROW_MULT_ITEMS );
g_shapeNames.push_back( INDETERMINATE );
for( unsigned i = 0; i < LIB_PIN::GetOrientationNames().size(); ++i )
g_orientationIcons.push_back( LIB_PIN::GetOrientationSymbols()[ i ] );
g_orientationNames = LIB_PIN::GetOrientationNames();
g_orientationNames.push_back( ROW_MULT_ITEMS );
g_orientationNames.push_back( INDETERMINATE );
}
m_dataModel = new PIN_TABLE_DATA_MODEL( GetUserUnits() );

View File

@ -25,6 +25,8 @@
#include <kiway_player.h>
#include <dialog_shim.h>
#include <fields_grid_table.h>
#include <sch_field.h>
#include <sch_validators.h>
#include <validators.h>
#include <class_library.h>
#include <template_fieldnames.h>
@ -53,9 +55,9 @@ FIELDS_GRID_TABLE<T>::FIELDS_GRID_TABLE( bool aInLibEdit, EDA_UNITS_T aUserUnits
m_readOnlyAttr->SetReadOnly( true );
m_valueColAttr = new wxGridCellAttr;
m_valueTextEditor = new wxGridCellTextEditor();
m_valueTextEditor->SetValidator( m_valueValidator );
m_valueColAttr->SetEditor( m_valueTextEditor );
GRID_CELL_TEXT_EDITOR* textEditor = new GRID_CELL_TEXT_EDITOR();
textEditor->SetValidator( m_valueValidator );
m_valueColAttr->SetEditor( textEditor );
m_boolColAttr = new wxGridCellAttr;
m_boolColAttr->SetRenderer( new wxGridCellBoolRenderer() );

View File

@ -96,7 +96,6 @@ private:
LIB_PART* m_part;
bool m_inLibEdit;
wxGridCellTextEditor* m_valueTextEditor;
SCH_FIELD_VALIDATOR m_valueValidator;
wxGridCellAttr* m_readOnlyAttr;

View File

@ -31,6 +31,26 @@
#define VALIDATORS_H
#include <wx/valtext.h>
#include <wx/grid.h>
/**
* Class GRID_CELL_TEXT_EDITOR
*
* This class works around a bug in wxGrid where the first keystroke doesn't get sent through
* the validator if the editor wasn't already open.
*/
class GRID_CELL_TEXT_EDITOR : public wxGridCellTextEditor
{
public:
GRID_CELL_TEXT_EDITOR();
virtual void SetValidator(const wxValidator& validator) override;
virtual void StartingKey(wxKeyEvent& event) override;
protected:
wxScopedPtr<wxValidator> m_validator;
};
/**
* Class FILE_NAME_CHAR_VALIDATOR
@ -63,19 +83,31 @@ public:
/**
* Class ENVIRONMENT_VARIABLE_CHAR_VALIDATOR
* Class ENV_VAR_NAME_VALIDATOR
*
* This class provides a custome wxValidator object for limiting the allowable characters
* This class provides a custom wxValidator object for limiting the allowable characters
* when defining an environment varaible name in a text edit control. Only uppercase,
* numbers, and underscore (_) characters are valid and the first character of the name
* cannot start with a number. This is according to IEEE Std 1003.1-2001. Even though
* most systems support other characters, these characters guarantee compatibility for
* all shells.
*/
class ENVIRONMENT_VARIABLE_CHAR_VALIDATOR : public wxTextValidator
class ENV_VAR_NAME_VALIDATOR : public wxTextValidator
{
public:
ENVIRONMENT_VARIABLE_CHAR_VALIDATOR( wxString* aValue = NULL );
ENV_VAR_NAME_VALIDATOR( wxString* aValue = NULL );
ENV_VAR_NAME_VALIDATOR( const ENV_VAR_NAME_VALIDATOR& val );
virtual ~ENV_VAR_NAME_VALIDATOR();
// Make a clone of this validator (or return NULL) - currently necessary
// if you're passing a reference to a validator.
virtual wxObject *Clone() const override
{
return new ENV_VAR_NAME_VALIDATOR( *this );
}
void OnChar(wxKeyEvent& event);
};
#endif // #ifndef VALIDATORS_H

View File

@ -44,8 +44,8 @@ public:
* @param aParent is the parent EDA_DRAW_FRAME.
* @param aLabel is the static text used to label the text input widget (note: the label
* text, trimmed of its colon, will also be used in error messages)
* @param aTextEntry is the text input widget used to edit the given value (wxTextCtrl,
* wxComboBox, ...).
* @param aValue is the control used to edit or display the given value (wxTextCtrl,
* wxComboBox, wxStaticText, etc.).
* @param aUnitLabel is the units label displayed after the text input widget
* @param aUseMils specifies the use of mils for imperial units (instead of inches)
* @param aMin a minimum value (in internal units) for validation
@ -53,13 +53,11 @@ public:
* @param aAllowEval indicates \a aTextInput's content should be eval'ed before storing
*/
UNIT_BINDER( EDA_DRAW_FRAME* aParent,
wxStaticText* aLabel, wxTextEntry* aTextEntry, wxStaticText* aUnitLabel,
wxStaticText* aLabel, wxWindow* aValue, wxStaticText* aUnitLabel,
bool aUseMils = false,
int aMin = INT_MIN, int aMax = INT_MAX,
bool aAllowEval = true );
virtual ~UNIT_BINDER();
/**
* Function SetValue
* Sets new value (in Internal Units) for the text field, taking care of units conversion.
@ -99,7 +97,6 @@ public:
protected:
void onTextChanged( wxEvent& aEvent );
void onSetFocus( wxFocusEvent& aEvent );
void onKillFocus( wxFocusEvent& aEvent );
void onTextEnter( wxCommandEvent& aEvent );
@ -108,21 +105,22 @@ protected:
void evaluate();
///> The bound widgets
wxStaticText* m_label;
wxTextEntry* m_textEntry;
wxStaticText* m_unitLabel;
wxStaticText* m_label;
wxWindow* m_value;
wxStaticText* m_unitLabel;
///> Currently used units.
EDA_UNITS_T m_units;
bool m_useMils;
EDA_UNITS_T m_units;
bool m_useMils;
///> Validation support.
int m_min;
int m_max;
int m_min;
int m_max;
bool m_showMessage;
///> Evaluator
NUMERIC_EVALUATOR m_eval;
bool m_allowEval;
bool m_allowEval;
};
#endif /* __UNIT_BINDER_H_ */