X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e948e2140fd78e9196821e3ba2d5fba467db61eb..f675b4f521c6696648674d8901e2d6162ab5e2fb:/src/propgrid/propgrid.cpp diff --git a/src/propgrid/propgrid.cpp b/src/propgrid/propgrid.cpp index 71253a2c5a..5d3fc06925 100644 --- a/src/propgrid/propgrid.cpp +++ b/src/propgrid/propgrid.cpp @@ -6,7 +6,7 @@ // Created: 2004-09-25 // RCS-ID: $Id$ // Copyright: (c) Jaakko Salli -// Licence: wxWindows license +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx/wx.h". @@ -63,6 +63,7 @@ #include "wx/timer.h" #include "wx/dcbuffer.h" +#include "wx/scopeguard.h" // Two pics for the expand / collapse buttons. // Files are not supplied with this project (since it is @@ -147,7 +148,7 @@ class wxPGGlobalVarsClassManager : public wxModule public: wxPGGlobalVarsClassManager() {} virtual bool OnInit() { wxPGGlobalVars = new wxPGGlobalVarsClass(); return true; } - virtual void OnExit() { delete wxPGGlobalVars; wxPGGlobalVars = NULL; } + virtual void OnExit() { wxDELETE(wxPGGlobalVars); } }; IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule) @@ -328,6 +329,7 @@ void wxPropertyGrid::Init1() if ( wxPGGlobalVars->m_mapEditorClasses.empty() ) wxPropertyGrid::RegisterDefaultEditors(); + m_validatingEditor = 0; m_iFlags = 0; m_pState = NULL; m_wndEditor = m_wndEditor2 = NULL; @@ -340,9 +342,10 @@ void wxPropertyGrid::Init1() m_curFocused = NULL; m_processedEvent = NULL; m_sortFunction = NULL; - m_inDoPropertyChanged = 0; - m_inCommitChangesFromEditor = 0; - m_inDoSelectProperty = 0; + m_inDoPropertyChanged = false; + m_inCommitChangesFromEditor = false; + m_inDoSelectProperty = false; + m_inOnValidationFailure = false; m_permanentValidationFailureBehavior = wxPG_VFB_DEFAULT; m_dragStatus = 0; m_mouseSide = 16; @@ -1027,7 +1030,7 @@ void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event ) } else { - event.Skip(); + HandleKeyEvent(event, true); } } @@ -1071,11 +1074,17 @@ void wxPropertyGrid::DoEndLabelEdit( bool commit, int selFlags ) } m_selColumn = 1; + int wasFocused = m_iFlags & wxPG_FL_FOCUSED; DestroyEditorWnd(m_labelEditor); + m_labelEditor = NULL; m_labelEditorProperty = NULL; + // Fix focus (needed at least on wxGTK) + if ( wasFocused ) + SetFocusOnCanvas(); + DrawItem(prop); } @@ -1116,8 +1125,7 @@ void wxPropertyGrid::SetExtraStyle( long exStyle ) else { #if wxPG_DOUBLE_BUFFER - delete m_doubleBuffer; - m_doubleBuffer = NULL; + wxDELETE(m_doubleBuffer); #endif } } @@ -1628,8 +1636,8 @@ bool wxPropertyGrid::EnsureVisible( wxPGPropArg id ) // Control font changer helper. void wxPropertyGrid::SetCurControlBoldFont() { - wxASSERT( m_wndEditor ); - m_wndEditor->SetFont( m_captionFont ); + wxWindow* editor = GetEditorControl(); + editor->SetFont( m_captionFont ); } // ----------------------------------------------------------------------- @@ -2902,7 +2910,7 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) (m_iFlags & wxPG_FL_INITIALIZED) && selected ) { - m_inCommitChangesFromEditor = 1; + m_inCommitChangesFromEditor = true; wxVariant variant(selected->GetValueRef()); bool valueIsPending = false; @@ -2937,9 +2945,9 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) EditorsValueWasNotModified(); } - bool res = true; + m_inCommitChangesFromEditor = false; - m_inCommitChangesFromEditor = 0; + bool res = true; if ( validationFailure && !forceSuccess ) { @@ -2981,6 +2989,7 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue // m_validationInfo.m_failureBehavior = m_permanentValidationFailureBehavior; + m_validationInfo.m_isFailing = true; // // Variant list a special value that cannot be validated @@ -3113,11 +3122,29 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue pendingValue = value; } + m_validationInfo.m_isFailing = false; + return true; } // ----------------------------------------------------------------------- +#if wxUSE_STATUSBAR +wxStatusBar* wxPropertyGrid::GetStatusBar() +{ + wxWindow* topWnd = ::wxGetTopLevelParent(this); + if ( topWnd && topWnd->IsKindOf(CLASSINFO(wxFrame)) ) + { + wxFrame* pFrame = wxStaticCast(topWnd, wxFrame); + if ( pFrame ) + return pFrame->GetStatusBar(); + } + return NULL; +} +#endif + +// ----------------------------------------------------------------------- + void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), const wxString& msg ) { if ( !msg.length() ) @@ -3126,24 +3153,33 @@ void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), cons #if wxUSE_STATUSBAR if ( !wxPGGlobalVars->m_offline ) { - wxWindow* topWnd = ::wxGetTopLevelParent(this); - if ( topWnd ) + wxStatusBar* pStatusBar = GetStatusBar(); + if ( pStatusBar ) { - wxFrame* pFrame = wxDynamicCast(topWnd, wxFrame); - if ( pFrame ) - { - wxStatusBar* pStatusBar = pFrame->GetStatusBar(); - if ( pStatusBar ) - { - pStatusBar->SetStatusText(msg); - return; - } - } + pStatusBar->SetStatusText(msg); + return; } } #endif - ::wxMessageBox(msg, wxT("Property Error")); + ::wxMessageBox(msg, _("Property Error")); +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::DoHidePropertyError( wxPGProperty* WXUNUSED(property) ) +{ +#if wxUSE_STATUSBAR + if ( !wxPGGlobalVars->m_offline ) + { + wxStatusBar* pStatusBar = GetStatusBar(); + if ( pStatusBar ) + { + pStatusBar->SetStatusText(wxEmptyString); + return; + } + } +#endif } // ----------------------------------------------------------------------- @@ -3151,7 +3187,27 @@ void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), cons bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property, wxVariant& invalidValue ) { + if ( m_inOnValidationFailure ) + return true; + + m_inOnValidationFailure = true; + wxON_BLOCK_EXIT_SET(m_inOnValidationFailure, false); + wxWindow* editor = GetEditorControl(); + int vfb = m_validationInfo.m_failureBehavior; + + if ( m_inDoSelectProperty ) + { + // When property selection is being changed, do not display any + // messages, if some were already shown for this property. + if ( property->HasFlag(wxPG_PROP_INVALID_VALUE) ) + { + m_validationInfo.m_failureBehavior = + vfb & ~(wxPG_VFB_SHOW_MESSAGE | + wxPG_VFB_SHOW_MESSAGEBOX | + wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR); + } + } // First call property's handler property->OnValidationFailure(invalidValue); @@ -3213,14 +3269,32 @@ bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& W } } - if ( vfb & wxPG_VFB_SHOW_MESSAGE ) + if ( vfb & (wxPG_VFB_SHOW_MESSAGE | + wxPG_VFB_SHOW_MESSAGEBOX | + wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR) ) { wxString msg = m_validationInfo.m_failureMessage; if ( !msg.length() ) - msg = wxT("You have entered invalid value. Press ESC to cancel editing."); + msg = _("You have entered invalid value. Press ESC to cancel editing."); + + #if wxUSE_STATUSBAR + if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR ) + { + if ( !wxPGGlobalVars->m_offline ) + { + wxStatusBar* pStatusBar = GetStatusBar(); + if ( pStatusBar ) + pStatusBar->SetStatusText(msg); + } + } + #endif + + if ( vfb & wxPG_VFB_SHOW_MESSAGE ) + DoShowPropertyError(property, msg); - DoShowPropertyError(property, msg); + if ( vfb & wxPG_VFB_SHOW_MESSAGEBOX ) + ::wxMessageBox(msg, _("Property Error")); } return (vfb & wxPG_VFB_STAY_IN_PROPERTY) ? false : true; @@ -3249,6 +3323,25 @@ void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty* property ) DrawItemAndChildren(property); } } + +#if wxUSE_STATUSBAR + if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR ) + { + if ( !wxPGGlobalVars->m_offline ) + { + wxStatusBar* pStatusBar = GetStatusBar(); + if ( pStatusBar ) + pStatusBar->SetStatusText(wxEmptyString); + } + } +#endif + + if ( vfb & wxPG_VFB_SHOW_MESSAGE ) + { + DoHidePropertyError(property); + } + + m_validationInfo.m_isFailing = false; } // ----------------------------------------------------------------------- @@ -3259,12 +3352,15 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) if ( m_inDoPropertyChanged ) return true; - wxWindow* editor = GetEditorControl(); + m_inDoPropertyChanged = true; + wxON_BLOCK_EXIT_SET(m_inDoPropertyChanged, false); + wxPGProperty* selected = GetSelection(); m_pState->m_anyModified = 1; - m_inDoPropertyChanged = 1; + // If property's value is being changed, assume it is valid + OnValidationFailureReset(selected); // Maybe need to update control wxASSERT( m_chgInfo_changedProperty != NULL ); @@ -3283,6 +3379,10 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) changedProperty->SetValue(value, &m_chgInfo_valueList, wxPG_SETVAL_BY_USER); + // NB: Call GetEditorControl() as late as possible, because OnSetValue() + // and perhaps other user-defined virtual functions may change it. + wxWindow* editor = GetEditorControl(); + // Set as Modified (not if dragging just began) if ( !(p->m_flags & wxPG_PROP_MODIFIED) ) { @@ -3350,8 +3450,6 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL ); - m_inDoPropertyChanged = 0; - return true; } @@ -3405,6 +3503,25 @@ wxVariant wxPropertyGrid::GetUncommittedPropertyValue() // Runs wxValidator for the selected property bool wxPropertyGrid::DoEditorValidate() { +#if wxUSE_VALIDATORS + wxRecursionGuard guard(m_validatingEditor); + if ( guard.IsInside() ) + return false; + + wxPGProperty* selected = GetSelection(); + if ( selected ) + { + wxWindow* wnd = GetEditorControl(); + + wxValidator* validator = selected->GetValidator(); + if ( validator && wnd ) + { + validator->SetWindow(wnd); + if ( !validator->Validate(this) ) + return false; + } + } +#endif return true; } @@ -3418,12 +3535,22 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) if ( !m_pState ) return; + // Don't care about the event if it originated from the + // 'label editor'. In this function we only care about the + // property value editor. + if ( m_labelEditor && event.GetId() == m_labelEditor->GetId() ) + { + event.Skip(); + return; + } + wxPGProperty* selected = GetSelection(); // Somehow, event is handled after property has been deselected. // Possibly, but very rare. if ( !selected || selected->HasFlag(wxPG_PROP_BEING_DELETED) || + m_inOnValidationFailure || // Also don't handle editor event if wxEVT_PG_CHANGED or // similar is currently doing something (showing a // message box, for instance). @@ -3443,7 +3570,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) m_chgInfo_changedProperty = NULL; - m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED|wxPG_FL_VALUE_CHANGE_IN_EVENT); + m_iFlags &= ~wxPG_FL_VALUE_CHANGE_IN_EVENT; // // Filter out excess wxTextCtrl modified events @@ -3507,6 +3634,13 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) selected, wnd ) ) valueIsPending = true; + + // Mark value always as pending if validation is currently + // failing and value was not unspecified + if ( !valueIsPending && + !pendingValue.IsNull() && + m_validationInfo.m_isFailing ) + valueIsPending = true; } else { @@ -3866,13 +4000,11 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) if ( m_inDoSelectProperty ) return true; - m_inDoSelectProperty = 1; + m_inDoSelectProperty = true; + wxON_BLOCK_EXIT_SET(m_inDoSelectProperty, false); if ( !m_pState ) - { - m_inDoSelectProperty = 0; return false; - } wxArrayPGProperty prevSelection = m_pState->m_selection; wxPGProperty* prevFirstSel; @@ -3938,7 +4070,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) } } - m_inDoSelectProperty = 0; return true; } @@ -3946,8 +4077,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) // First, deactivate previous if ( prevFirstSel ) { - OnValidationFailureReset(prevFirstSel); - // Must double-check if this is an selected in case of forceswitch if ( p != prevFirstSel ) { @@ -3956,11 +4085,15 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) // Validation has failed, so we can't exit the previous editor //::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."), // _("Invalid Value"),wxOK|wxICON_ERROR); - m_inDoSelectProperty = 0; return false; } } + // This should be called after CommitChangesFromEditor(), so that + // OnValidationFailure() still has information on property's + // validation state. + OnValidationFailureReset(prevFirstSel); + FreeEditors(); m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR); @@ -3986,8 +4119,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) int splitterX = GetSplitterPosition(); m_editorFocused = 0; m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE; - if ( p != prevFirstSel ) - m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED); wxASSERT( m_wndEditor == NULL ); @@ -4164,14 +4295,7 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS) ) { - wxStatusBar* statusbar = NULL; - if ( !(m_iFlags & wxPG_FL_NOSTATUSBARHELP) ) - { - wxFrame* frame = wxDynamicCast(::wxGetTopLevelParent(this),wxFrame); - if ( frame ) - statusbar = frame->GetStatusBar(); - } - + wxStatusBar* statusbar = GetStatusBar(); if ( statusbar ) { const wxString* pHelpString = (const wxString*) NULL; @@ -4199,8 +4323,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) } #endif - m_inDoSelectProperty = 0; - // call wx event handler (here so that it also occurs on deselection) if ( !(flags & wxPG_SEL_DONT_SEND_EVENT) ) SendEvent( wxEVT_PG_SELECTED, p, NULL ); @@ -5517,8 +5639,10 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) return; } - // Except for TAB and ESC, handle child control events in child control - if ( fromChild ) + // Except for TAB, ESC, and any keys specifically dedicated to + // wxPropertyGrid itself, handle child control events in child control. + if ( fromChild && + wxPGFindInVector(m_dedicatedKeys, keycode) == wxNOT_FOUND ) { // Only propagate event if it had modifiers if ( !event.HasModifiers() ) @@ -5572,7 +5696,28 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) { p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir ); if ( p ) - DoSelectProperty(p); + { + int selFlags = 0; + int reopenLabelEditorCol = -1; + + if ( editorFocused ) + { + // If editor was focused, then make the next editor + // focused as well + selFlags |= wxPG_SEL_FOCUS; + } + else + { + // Also maintain the same label editor focus state + if ( m_labelEditor ) + reopenLabelEditorCol = m_selColumn; + } + + DoSelectProperty(p, selFlags); + + if ( reopenLabelEditorCol >= 0 ) + DoBeginLabelEdit(reopenLabelEditorCol); + } wasHandled = true; } } @@ -5699,6 +5844,13 @@ bool wxPropertyGrid::IsEditorFocused() const // Called by focus event handlers. newFocused is the window that becomes focused. void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused ) { + // + // Never allow focus to be changed when handling editor event. + // Especially because they may be displaing a dialog which + // could cause all kinds of weird (native) focus changes. + if ( HasInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT) ) + return; + unsigned int oldFlags = m_iFlags; bool wasEditorFocused = false; wxWindow* wndEditor = m_wndEditor;