X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/bca35e84c0a22af6567d260cfddef0a698858e3c..f675b4f521c6696648674d8901e2d6162ab5e2fb:/src/propgrid/propgrid.cpp diff --git a/src/propgrid/propgrid.cpp b/src/propgrid/propgrid.cpp index 6a05ec4948..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) @@ -200,7 +201,10 @@ wxPGGlobalVarsClass::wxPGGlobalVarsClass() m_strMin = wxS("Min"); m_strMax = wxS("Max"); m_strUnits = wxS("Units"); + m_strHint = wxS("Hint"); +#if wxPG_COMPATIBILITY_1_4 m_strInlineHelp = wxS("InlineHelp"); +#endif m_warnings = 0; } @@ -325,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; @@ -337,14 +342,18 @@ 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; m_editorFocused = 0; + // Set up default unspecified value 'colour' + m_unspecifiedAppearance.SetFgCol(*wxLIGHT_GREY); + // Set default keys AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RIGHT ); AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN ); @@ -441,9 +450,9 @@ void wxPropertyGrid::Init2() CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING ); - // Allocate cell datas indirectly by calling setter - m_propertyDefaultCell.SetBgCol(*wxBLACK); - m_categoryDefaultCell.SetBgCol(*wxBLACK); + // Allocate cell datas + m_propertyDefaultCell.SetEmptyData(); + m_categoryDefaultCell.SetEmptyData(); RegainColours(); @@ -1021,7 +1030,7 @@ void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event ) } else { - event.Skip(); + HandleKeyEvent(event, true); } } @@ -1065,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); } @@ -1110,8 +1125,7 @@ void wxPropertyGrid::SetExtraStyle( long exStyle ) else { #if wxPG_DOUBLE_BUFFER - delete m_doubleBuffer; - m_doubleBuffer = NULL; + wxDELETE(m_doubleBuffer); #endif } } @@ -1384,6 +1398,8 @@ void wxPropertyGrid::RegainColours() wxColour bgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ); m_colPropBack = bgCol; m_propertyDefaultCell.GetData()->SetBgCol(bgCol); + if ( !m_unspecifiedAppearance.GetBgCol().IsOk() ) + m_unspecifiedAppearance.SetBgCol(bgCol); } if ( !(m_coloursCustomized & 0x0010) ) @@ -1391,6 +1407,8 @@ void wxPropertyGrid::RegainColours() wxColour fgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ); m_colPropFore = fgCol; m_propertyDefaultCell.GetData()->SetFgCol(fgCol); + if ( !m_unspecifiedAppearance.GetFgCol().IsOk() ) + m_unspecifiedAppearance.SetFgCol(fgCol); } if ( !(m_coloursCustomized & 0x0020) ) @@ -1462,6 +1480,7 @@ void wxPropertyGrid::SetCellBackgroundColour( const wxColour& col ) m_coloursCustomized |= 0x08; m_propertyDefaultCell.GetData()->SetBgCol(col); + m_unspecifiedAppearance.SetBgCol(col); Refresh(); } @@ -1474,6 +1493,7 @@ void wxPropertyGrid::SetCellTextColour( const wxColour& col ) m_coloursCustomized |= 0x10; m_propertyDefaultCell.GetData()->SetFgCol(col); + m_unspecifiedAppearance.SetFgCol(col); Refresh(); } @@ -1552,6 +1572,9 @@ void wxPropertyGrid::PrepareAfterItemsAdded() Sort(wxPG_SORT_TOP_LEVEL_ONLY); RecalculateVirtualSize(); + + // Fix editor position + CorrectEditorWidgetPosY(); } // ----------------------------------------------------------------------- @@ -1613,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 ); } // ----------------------------------------------------------------------- @@ -2111,6 +2134,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, const wxPGProperty* firstSelected = GetSelection(); const wxPropertyGridPageState* state = m_pState; + const wxArrayInt& colWidths = state->m_colWidths; #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT bool wasSelectedPainted = false; @@ -2210,9 +2234,9 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, unsigned int si; int sx = x; - for ( si=0; sim_colWidths.size(); si++ ) + for ( si=0; sim_colWidths[si]; + sx += colWidths[si]; dc.DrawLine( sx, y, sx, y2 ); } @@ -2308,12 +2332,16 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, m_marginWidth, lh ); - if ( p->IsCategory() ) + // Default cell rect fill the entire row + wxRect cellRect(greyDepthX, y, + gridWidth - greyDepth + 2, rowHeight-1 ); + + bool isCategory = p->IsCategory(); + + if ( isCategory ) { - // Captions have their cell areas merged as one dc.SetFont(m_captionFont); fontChanged = true; - wxRect cellRect(greyDepthX, y, gridWidth - greyDepth + 2, rowHeight-1 ); if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol ) { @@ -2325,13 +2353,6 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, { dc.SetTextForeground(rowFgCol); } - - wxPGCellRenderer* renderer = p->GetCellRenderer(0); - renderer->Render( dc, cellRect, this, p, 0, -1, renderFlags ); - - // Tree Item Button - if ( !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() ) - DrawExpanderButton( dc, butRect, p ); } else { @@ -2339,111 +2360,147 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, if ( butRect.x > 0 ) butRect.x += IN_CELL_EXPANDER_BUTTON_X_ADJUST; - if ( p->m_flags & wxPG_PROP_MODIFIED && (windowStyle & wxPG_BOLD_MODIFIED) ) + if ( p->m_flags & wxPG_PROP_MODIFIED && + (windowStyle & wxPG_BOLD_MODIFIED) ) { dc.SetFont(m_captionFont); fontChanged = true; } - unsigned int ci; - int cellX = x + 1; - int nextCellWidth = state->m_colWidths[0] - - (greyDepthX - m_marginWidth); - wxRect cellRect(greyDepthX+1, y, 0, rowHeight-1); - int textXAdd = textMarginHere - greyDepthX; + // Magic fine-tuning for non-category rows + cellRect.x += 1; + } + + int firstCellWidth = colWidths[0] - (greyDepthX - m_marginWidth); + int firstCellX = cellRect.x; + + // Calculate cellRect.x for the last cell + unsigned int ci = 0; + int cellX = x + 1; + for ( ci=0; cim_colWidths.size(); ci++ ) + if ( ci == 0 ) + { + textXAdd = textMarginHere - greyDepthX; + cellRect.width = firstCellWidth; + cellRect.x = firstCellX; + } + else { - cellRect.width = nextCellWidth - 1; + int colWidth = colWidths[ci]; + cellRect.width = colWidth; + cellRect.x -= colWidth; + } - wxWindow* cellEditor = NULL; - int cellRenderFlags = renderFlags; + // Merge with column to the right? + if ( !prevFilled && isCategory ) + { + cellRect.width += colWidths[ci+1]; + } + + if ( !isCategory ) + cellRect.width -= 1; - // Tree Item Button (must be drawn before clipping is set up) - if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() ) - DrawExpanderButton( dc, butRect, p ); + wxWindow* cellEditor = NULL; + int cellRenderFlags = renderFlags; + + // Tree Item Button (must be drawn before clipping is set up) + if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() ) + DrawExpanderButton( dc, butRect, p ); - // Background - if ( isSelected && (ci == 1 || ci == m_selColumn) ) + // Background + if ( isSelected && (ci == 1 || ci == m_selColumn) ) + { + if ( p == firstSelected ) { - if ( p == firstSelected ) - { - if ( ci == 1 && m_wndEditor ) - cellEditor = m_wndEditor; - else if ( ci == m_selColumn && m_labelEditor ) - cellEditor = m_labelEditor; - } + if ( ci == 1 && m_wndEditor ) + cellEditor = m_wndEditor; + else if ( ci == m_selColumn && m_labelEditor ) + cellEditor = m_labelEditor; + } - if ( cellEditor ) - { - wxColour editorBgCol = - cellEditor->GetBackgroundColour(); - dc.SetBrush(editorBgCol); - dc.SetPen(editorBgCol); + if ( cellEditor ) + { + wxColour editorBgCol = + cellEditor->GetBackgroundColour(); + dc.SetBrush(editorBgCol); + dc.SetPen(editorBgCol); + dc.SetTextForeground(m_colPropFore); + dc.DrawRectangle(cellRect); + + if ( m_dragStatus != 0 || + (m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) ) + cellEditor = NULL; + } + else + { + dc.SetBrush(m_colPropBack); + dc.SetPen(m_colPropBack); + if ( p->IsEnabled() ) dc.SetTextForeground(m_colPropFore); - dc.DrawRectangle(cellRect); - - if ( m_dragStatus != 0 || - (m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) ) - cellEditor = NULL; - } else - { - dc.SetBrush(m_colPropBack); - dc.SetPen(m_colPropBack); dc.SetTextForeground(m_colDisPropFore); - if ( p->IsEnabled() ) - dc.SetTextForeground(rowFgCol); - else - dc.SetTextForeground(m_colDisPropFore); - } } - else + } + else + { + if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol ) { - if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol ) - { - dc.SetBrush(rowBgBrush); - dc.SetPen(rowBgCol); - } + dc.SetBrush(rowBgBrush); + dc.SetPen(rowBgCol); + } - if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol ) - { - dc.SetTextForeground(rowFgCol); - } + if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol ) + { + dc.SetTextForeground(rowFgCol); } + } - dc.SetClippingRegion(cellRect); + dc.SetClippingRegion(cellRect); - cellRect.x += textXAdd; - cellRect.width -= textXAdd; + cellRect.x += textXAdd; + cellRect.width -= textXAdd; - // Foreground - if ( !cellEditor ) + // Foreground + if ( !cellEditor ) + { + wxPGCellRenderer* renderer; + int cmnVal = p->GetCommonValue(); + if ( cmnVal == -1 || ci != 1 ) { - wxPGCellRenderer* renderer; - int cmnVal = p->GetCommonValue(); - if ( cmnVal == -1 || ci != 1 ) - { - renderer = p->GetCellRenderer(ci); - renderer->Render( dc, cellRect, this, p, ci, -1, - cellRenderFlags ); - } - else - { - renderer = GetCommonValue(cmnVal)->GetRenderer(); - renderer->Render( dc, cellRect, this, p, ci, -1, - cellRenderFlags ); - } + renderer = p->GetCellRenderer(ci); + prevFilled = renderer->Render(dc, cellRect, this, + p, ci, -1, + cellRenderFlags ); + } + else + { + renderer = GetCommonValue(cmnVal)->GetRenderer(); + prevFilled = renderer->Render(dc, cellRect, this, + p, ci, -1, + cellRenderFlags ); } - - cellX += state->m_colWidths[ci]; - if ( ci < (state->m_colWidths.size()-1) ) - nextCellWidth = state->m_colWidths[ci+1]; - cellRect.x = cellX; - dc.DestroyClippingRegion(); // Is this really necessary? - textXAdd = 0; } + else + { + prevFilled = true; + } + + dc.DestroyClippingRegion(); // Is this really necessary? } + while ( ci > 0 ); if ( fontChanged ) dc.SetFont(normalFont); @@ -2481,6 +2538,8 @@ wxRect wxPropertyGrid::GetPropertyRect( const wxPGProperty* p1, const wxPGProper // // Return rect which encloses the given property range + // (in logical grid coordinates) + // int visTop = p1->GetY(); int visBottom; @@ -2523,6 +2582,13 @@ void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 ) wxRect r = GetPropertyRect(p1, p2); if ( r.width > 0 ) { + // Convert rectangle from logical grid coordinates to physical ones + int vx, vy; + GetViewStart(&vx, &vy); + vx *= wxPG_PIXELS_PER_UNIT; + vy *= wxPG_PIXELS_PER_UNIT; + r.x -= vx; + r.y -= vy; RefreshRect(r); } } @@ -2763,10 +2829,22 @@ void wxPropertyGrid::DoSetSplitterPosition( int newxpos, // ----------------------------------------------------------------------- -void wxPropertyGrid::CenterSplitter( bool enableAutoCentering ) +void wxPropertyGrid::ResetColumnSizes( bool enableAutoResizing ) +{ + wxPropertyGridPageState* state = m_pState; + if ( state ) + state->ResetColumnSizes(0); + + if ( enableAutoResizing && HasFlag(wxPG_SPLITTER_AUTO_CENTER) ) + m_pState->m_dontCenterSplitter = false; +} + +// ----------------------------------------------------------------------- + +void wxPropertyGrid::CenterSplitter( bool enableAutoResizing ) { SetSplitterPosition( m_width/2 ); - if ( enableAutoCentering && HasFlag(wxPG_SPLITTER_AUTO_CENTER) ) + if ( enableAutoResizing && HasFlag(wxPG_SPLITTER_AUTO_CENTER) ) m_pState->m_dontCenterSplitter = false; } @@ -2832,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; @@ -2867,9 +2945,9 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) EditorsValueWasNotModified(); } - bool res = true; + m_inCommitChangesFromEditor = false; - m_inCommitChangesFromEditor = 0; + bool res = true; if ( validationFailure && !forceSuccess ) { @@ -2911,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 @@ -3043,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() ) @@ -3056,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 } // ----------------------------------------------------------------------- @@ -3081,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); @@ -3143,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."); - DoShowPropertyError(property, msg); + #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); + + if ( vfb & wxPG_VFB_SHOW_MESSAGEBOX ) + ::wxMessageBox(msg, _("Property Error")); } return (vfb & wxPG_VFB_STAY_IN_PROPERTY) ? false : true; @@ -3179,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; } // ----------------------------------------------------------------------- @@ -3189,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 ); @@ -3213,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) ) { @@ -3280,8 +3450,6 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL ); - m_inDoPropertyChanged = 0; - return true; } @@ -3335,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; } @@ -3348,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). @@ -3373,21 +3570,30 @@ 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 - if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && - wnd && - wnd->IsKindOf(CLASSINFO(wxTextCtrl)) ) + if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && wnd ) { - wxTextCtrl* tc = (wxTextCtrl*) wnd; + if ( wnd->IsKindOf(CLASSINFO(wxTextCtrl)) ) + { + wxTextCtrl* tc = (wxTextCtrl*) wnd; - wxString newTcValue = tc->GetValue(); - if ( m_prevTcValue == newTcValue ) - return; + wxString newTcValue = tc->GetValue(); + if ( m_prevTcValue == newTcValue ) + return; + m_prevTcValue = newTcValue; + } + else if ( wnd->IsKindOf(CLASSINFO(wxComboCtrl)) ) + { + wxComboCtrl* cc = (wxComboCtrl*) wnd; - m_prevTcValue = newTcValue; + wxString newTcValue = cc->GetTextCtrl()->GetValue(); + if ( m_prevTcValue == newTcValue ) + return; + m_prevTcValue = newTcValue; + } } SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT); @@ -3428,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 { @@ -3510,14 +3723,21 @@ wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p, int column ) const GetViewStart(&vx, &vy); vy *= wxPG_PIXELS_PER_UNIT; - // TODO: If custom image detection changes from current, change this. - if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE ) + if ( column == 1 ) { - //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE; - int iw = p->OnMeasureImage().x; - if ( iw < 1 ) - iw = wxPG_CUSTOM_IMAGE_WIDTH; - imageOffset = p->GetImageOffset(iw); + // TODO: If custom image detection changes from current, change this. + if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE ) + { + //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE; + int iw = p->OnMeasureImage().x; + if ( iw < 1 ) + iw = wxPG_CUSTOM_IMAGE_WIDTH; + imageOffset = p->GetImageOffset(iw); + } + } + else if ( column == 0 ) + { + splitterX += (p->m_depth - 1) * m_subgroup_extramargin; } return wxRect @@ -3620,6 +3840,21 @@ void wxPropertyGrid::CustomSetCursor( int type, bool override ) m_curcursor = type; } +// ----------------------------------------------------------------------- + +wxString +wxPropertyGrid::GetUnspecifiedValueText( int argFlags ) const +{ + const wxPGCell& ua = GetUnspecifiedValueAppearance(); + + if ( ua.HasText() && + !(argFlags & wxPG_FULL_VALUE) && + !(argFlags & wxPG_EDITABLE_VALUE) ) + return ua.GetText(); + + return wxEmptyString; +} + // ----------------------------------------------------------------------- // wxPropertyGrid property selection, editor creation // ----------------------------------------------------------------------- @@ -3646,6 +3881,13 @@ private: m_propGrid->HandleCustomEditorEvent(event); + // + // NB: On wxMSW, a wxTextCtrl with wxTE_PROCESS_ENTER + // may beep annoyingly if that event is skipped + // and passed to parent event handler. + if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER ) + return true; + return wxEvtHandler::ProcessEvent(event); } @@ -3758,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; @@ -3830,7 +4070,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) } } - m_inDoSelectProperty = 0; return true; } @@ -3838,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 ) { @@ -3848,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); @@ -3878,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 ); @@ -3901,6 +4140,9 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) wxRect grect = GetEditorWidgetRect(p, m_selColumn); wxPoint goodPos = grect.GetPosition(); + // Editor appearance can now be considered clear + m_editorAppearance.SetEmptyData(); + const wxPGEditor* editor = p->GetEditorClass(); wxCHECK_MSG(editor, false, wxT("NULL editor class not allowed")); @@ -3970,6 +4212,12 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) p->GetEditorClass()->OnFocus(p, primaryCtrl); } + else + { + if ( p->IsValueUnspecified() ) + SetEditorAppearance(m_unspecifiedAppearance, + true); + } } if ( m_wndEditor2 ) @@ -4047,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; @@ -4082,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 ); @@ -4136,7 +4375,7 @@ void wxPropertyGrid::RefreshEditor() editorClass->UpdateControl(p, wnd); if ( p->IsValueUnspecified() ) - editorClass ->SetValueToUnspecified(p, wnd); + SetEditorAppearance(m_unspecifiedAppearance, true); } // ----------------------------------------------------------------------- @@ -4441,9 +4680,10 @@ bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, evt.SetCanVeto(true); } + wxPropertyGridEvent* prevProcessedEvent = m_processedEvent; m_processedEvent = &evt; m_eventObject->HandleWindowEvent(evt); - m_processedEvent = NULL; + m_processedEvent = prevProcessedEvent; return evt.WasVetoed(); } @@ -4544,7 +4784,7 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even // Double-clicking the splitter causes auto-centering if ( m_pState->GetColumnCount() <= 2 ) { - CenterSplitter( true ); + ResetColumnSizes( true ); SendEvent(wxEVT_PG_COL_DRAGGING, m_propHover, @@ -4719,7 +4959,6 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, { int newSplitterX = x - m_dragOffset; - int splitterX = x - splitterHitOffset; // Splitter redraw required? if ( newSplitterX != splitterX ) @@ -4958,8 +5197,14 @@ bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y), wxPG_SEL_NOVALIDATE, (unsigned int)m_draggedSplitter); - // Disable splitter auto-centering - state->m_dontCenterSplitter = true; + // Disable splitter auto-centering (but only if moved any - + // otherwise we end up disabling auto-center even after a + // recentering double-click). + int posDiff = abs(m_startingSplitterX - + GetSplitterPosition(m_draggedSplitter)); + + if ( posDiff > 1 ) + state->m_dontCenterSplitter = true; // This is necessary to return cursor if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) @@ -5394,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() ) @@ -5449,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; } } @@ -5539,6 +5807,27 @@ void wxPropertyGrid::OnIdle( wxIdleEvent& WXUNUSED(event) ) if ( tlp != m_tlp ) OnTLPChanging(tlp); } + + // + // Resolve pending property removals + if ( m_deletedProperties.size() > 0 ) + { + wxArrayPGProperty& arr = m_deletedProperties; + for ( unsigned int i=0; i 0 ) + { + wxArrayPGProperty& arr = m_removedProperties; + for ( unsigned int i=0; iGetParent(); } + // Notify editor control when it receives a focus + if ( wasEditorFocused && m_curFocused != newFocused ) + { + wxPGProperty* p = GetSelection(); + if ( p ) + { + const wxPGEditor* editor = p->GetEditorClass(); + ResetEditorAppearance(); + editor->OnFocus(p, GetEditorControl()); + } + } + m_curFocused = newFocused; if ( (m_iFlags & wxPG_FL_FOCUSED) !=