X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/48567d2790eecb851fdd1d4c7d208b65e161d031..7198c3368055d88249a338eb33b21f051f674806:/src/propgrid/propgrid.cpp diff --git a/src/propgrid/propgrid.cpp b/src/propgrid/propgrid.cpp index 0ba675584e..5af8805a19 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) @@ -234,6 +235,10 @@ wxPGGlobalVarsClass::~wxPGGlobalVarsClass() delete ((wxPGEditor*)vt_it->second); } + // Make sure the global pointers have been reset + wxASSERT(wxPG_EDITOR(TextCtrl) == NULL); + wxASSERT(wxPG_EDITOR(ChoiceAndButton) == NULL); + delete wxPGProperty::sm_wxPG_LABEL; } @@ -245,9 +250,9 @@ void wxPropertyGridInitGlobalsIfNeeded() // wxPropertyGrid // ----------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxScrolledWindow) +IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxControl) -BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow) +BEGIN_EVENT_TABLE(wxPropertyGrid, wxControl) EVT_IDLE(wxPropertyGrid::OnIdle) EVT_PAINT(wxPropertyGrid::OnPaint) EVT_SIZE(wxPropertyGrid::OnResize) @@ -270,7 +275,7 @@ END_EVENT_TABLE() // ----------------------------------------------------------------------- wxPropertyGrid::wxPropertyGrid() - : wxScrolledWindow() + : wxControl(), wxScrollHelper(this) { Init1(); } @@ -283,7 +288,7 @@ wxPropertyGrid::wxPropertyGrid( wxWindow *parent, const wxSize& size, long style, const wxString& name ) - : wxScrolledWindow() + : wxControl(), wxScrollHelper(this) { Init1(); Create(parent,id,pos,size,style,name); @@ -310,7 +315,10 @@ bool wxPropertyGrid::Create( wxWindow *parent, style &= ~(wxTAB_TRAVERSAL); style |= wxWANTS_CHARS; - wxScrolledWindow::Create(parent,id,pos,size,style,name); + wxControl::Create(parent, id, pos, size, + style | wxScrolledWindowStyle, + wxDefaultValidator, + name); Init2(); @@ -328,6 +336,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 +349,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; @@ -365,9 +375,7 @@ void wxPropertyGrid::Init1() m_coloursCustomized = 0; m_frozen = 0; -#if wxPG_DOUBLE_BUFFER m_doubleBuffer = NULL; -#endif #ifndef wxPG_ICON_WIDTH m_expandbmp = NULL; @@ -467,10 +475,6 @@ void wxPropertyGrid::Init2() m_timeCreated = ::wxGetLocalTimeMillis(); - //m_canvas->Create(this, wxID_ANY, wxPoint(0, 0), GetClientSize(), - // wxWANTS_CHARS | wxCLIP_CHILDREN); - SetBackgroundStyle( wxBG_STYLE_CUSTOM ); - m_iFlags |= wxPG_FL_INITIALIZED; m_ncWidth = wndsize.GetWidth(); @@ -538,10 +542,8 @@ wxPropertyGrid::~wxPropertyGrid() wxS("Close(false).)") ); } -#if wxPG_DOUBLE_BUFFER if ( m_doubleBuffer ) delete m_doubleBuffer; -#endif if ( m_iFlags & wxPG_FL_CREATEDSTATE ) delete m_pState; @@ -569,7 +571,7 @@ bool wxPropertyGrid::Destroy() if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) ReleaseMouse(); - return wxScrolledWindow::Destroy(); + return wxControl::Destroy(); } // ----------------------------------------------------------------------- @@ -628,12 +630,12 @@ void wxPropertyGrid::SetWindowStyleFlag( long style ) // // Tooltips disabled // - wxScrolledWindow::SetToolTip( NULL ); + SetToolTip( NULL ); } #endif } - wxScrolledWindow::SetWindowStyleFlag ( style ); + wxControl::SetWindowStyleFlag ( style ); if ( m_iFlags & wxPG_FL_INITIALIZED ) { @@ -651,7 +653,7 @@ void wxPropertyGrid::Freeze() { if ( !m_frozen ) { - wxScrolledWindow::Freeze(); + wxControl::Freeze(); } m_frozen++; } @@ -664,7 +666,7 @@ void wxPropertyGrid::Thaw() if ( !m_frozen ) { - wxScrolledWindow::Thaw(); + wxControl::Thaw(); RecalculateVirtualSize(); #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT Refresh(); @@ -1027,7 +1029,7 @@ void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event ) } else { - event.Skip(); + HandleKeyEvent(event, true); } } @@ -1071,11 +1073,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); } @@ -1115,14 +1123,11 @@ void wxPropertyGrid::SetExtraStyle( long exStyle ) } else { - #if wxPG_DOUBLE_BUFFER - delete m_doubleBuffer; - m_doubleBuffer = NULL; - #endif + wxDELETE(m_doubleBuffer); } } - wxScrolledWindow::SetExtraStyle( exStyle ); + wxControl::SetExtraStyle( exStyle ); if ( exStyle & wxPG_EX_INIT_NOCAT ) m_pState->InitNonCatMode(); @@ -1228,7 +1233,7 @@ bool wxPropertyGrid::Reparent( wxWindowBase *newParent ) { OnTLPChanging((wxWindow*)newParent); - bool res = wxScrolledWindow::Reparent(newParent); + bool res = wxControl::Reparent(newParent); return res; } @@ -1241,7 +1246,7 @@ void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing ) { int x = 0, y = 0; - m_captionFont = wxScrolledWindow::GetFont(); + m_captionFont = wxControl::GetFont(); GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont); m_subgroup_extramargin = x + (x/2); @@ -1436,7 +1441,7 @@ bool wxPropertyGrid::SetFont( const wxFont& font ) // Must disable active editor. DoClearSelection(); - bool res = wxScrolledWindow::SetFont( font ); + bool res = wxControl::SetFont( font ); if ( res && GetParent()) // may not have been Create()ed yet if SetFont called from SetWindowVariant { CalculateFontAndBitmapStuff( m_vspacing ); @@ -1628,8 +1633,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 ); } // ----------------------------------------------------------------------- @@ -1677,7 +1682,7 @@ wxPoint wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty* p, wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str ) { - if ( src_str.length() == 0 ) + if ( src_str.empty() ) { dst_str = src_str; return src_str; @@ -1736,7 +1741,7 @@ wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& sr wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str ) { - if ( src_str.length() == 0 ) + if ( src_str.empty() ) { dst_str = src_str; return src_str; @@ -1822,6 +1827,9 @@ void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) ) r.x = 0; r.width = GetClientSize().x; + r.y = vy; + r.height = GetClientSize().y; + // Repaint this rectangle DrawItems( dc, r.y, r.y + r.height, &r ); @@ -1911,7 +1919,7 @@ void wxPropertyGrid::DrawExpanderButton( wxDC& dc, const wxRect& rect, void wxPropertyGrid::DrawItems( wxDC& dc, unsigned int topItemY, unsigned int bottomItemY, - const wxRect* drawRect ) + const wxRect* itemsRect ) { if ( m_frozen || m_height < 1 || @@ -1921,15 +1929,26 @@ void wxPropertyGrid::DrawItems( wxDC& dc, m_pState->EnsureVirtualHeight(); - wxRect tempDrawRect; - if ( !drawRect ) + wxRect tempItemsRect; + if ( !itemsRect ) { - tempDrawRect = wxRect(0, topItemY, - m_pState->m_width, - bottomItemY); - drawRect = &tempDrawRect; + tempItemsRect = wxRect(0, topItemY, + m_pState->m_width, + bottomItemY); + itemsRect = &tempItemsRect; } + int vx, vy; + GetViewStart(&vx, &vy); + vx *= wxPG_PIXELS_PER_UNIT; + vy *= wxPG_PIXELS_PER_UNIT; + + // itemRect is in virtual grid space + wxRect drawRect(itemsRect->x - vx, + itemsRect->y - vy, + itemsRect->width, + itemsRect->height); + // items added check if ( m_pState->m_itemsAdded ) PrepareAfterItemsAdded(); @@ -1940,14 +1959,13 @@ void wxPropertyGrid::DrawItems( wxDC& dc, wxDC* dcPtr = &dc; bool isBuffered = false; - #if wxPG_DOUBLE_BUFFER wxMemoryDC* bufferDC = NULL; if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) ) { if ( !m_doubleBuffer ) { - paintFinishY = drawRect->y; + paintFinishY = itemsRect->y; dcPtr = NULL; } else @@ -1961,13 +1979,12 @@ void wxPropertyGrid::DrawItems( wxDC& dc, isBuffered = true; } } - #endif if ( dcPtr ) { - dc.SetClippingRegion( *drawRect ); - paintFinishY = DoDrawItems( *dcPtr, drawRect, isBuffered ); - int drawBottomY = drawRect->y + drawRect->height; + // paintFinishY and drawBottomY are in buffer/physical space + paintFinishY = DoDrawItems( *dcPtr, itemsRect, isBuffered ); + int drawBottomY = itemsRect->y + itemsRect->height - vy; // Clear area beyond last painted property if ( paintFinishY < drawBottomY ) @@ -1978,48 +1995,44 @@ void wxPropertyGrid::DrawItems( wxDC& dc, m_width, drawBottomY ); } - - dc.DestroyClippingRegion(); } - #if wxPG_DOUBLE_BUFFER if ( bufferDC ) { - dc.Blit( drawRect->x, drawRect->y, drawRect->width, - drawRect->height, + dc.Blit( drawRect.x, drawRect.y, drawRect.width, + drawRect.height, bufferDC, 0, 0, wxCOPY ); delete bufferDC; } - #endif } else { // Just clear the area dc.SetPen(m_colEmptySpace); dc.SetBrush(m_colEmptySpace); - dc.DrawRectangle(*drawRect); + dc.DrawRectangle(drawRect); } } // ----------------------------------------------------------------------- int wxPropertyGrid::DoDrawItems( wxDC& dc, - const wxRect* drawRect, + const wxRect* itemsRect, bool isBuffered ) const { const wxPGProperty* firstItem; const wxPGProperty* lastItem; - firstItem = DoGetItemAtY(drawRect->y); - lastItem = DoGetItemAtY(drawRect->y+drawRect->height-1); + firstItem = DoGetItemAtY(itemsRect->y); + lastItem = DoGetItemAtY(itemsRect->y+itemsRect->height-1); if ( !lastItem ) lastItem = GetLastItem( wxPG_ITERATE_VISIBLE ); if ( m_frozen || m_height < 1 || firstItem == NULL ) - return drawRect->y; + return itemsRect->y; - wxCHECK_MSG( !m_pState->m_itemsAdded, drawRect->y, + wxCHECK_MSG( !m_pState->m_itemsAdded, itemsRect->y, "no items added" ); wxASSERT( m_pState->m_properties->GetChildCount() ); @@ -2028,8 +2041,8 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, int firstItemTopY; int lastItemBottomY; - firstItemTopY = drawRect->y; - lastItemBottomY = drawRect->y + drawRect->height; + firstItemTopY = itemsRect->y; + lastItemBottomY = itemsRect->y + itemsRect->height; // Align y coordinates to item boundaries firstItemTopY -= firstItemTopY % lh; @@ -2039,20 +2052,20 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, // Entire range outside scrolled, visible area? if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() || lastItemBottomY <= 0 ) - return drawRect->y; + return itemsRect->y; wxCHECK_MSG( firstItemTopY < lastItemBottomY, - drawRect->y, + itemsRect->y, "invalid y values" ); /* wxLogDebug(" -> DoDrawItems ( \"%s\" -> \"%s\" - "height=%i (ch=%i), drawRect = 0x%lX )", + "height=%i (ch=%i), itemsRect = 0x%lX )", firstItem->GetLabel().c_str(), lastItem->GetLabel().c_str(), (int)(lastItemBottomY - firstItemTopY), (int)m_height, - (unsigned long)drawRect ); + (unsigned long)&itemsRect ); */ wxRect r; @@ -2062,31 +2075,29 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc, int xRelMod = 0; // - // With wxPG_DOUBLE_BUFFER, do double buffering - // - buffer's y = 0, so align drawRect and coordinates to that + // For now, do some manual calculation for double buffering + // - buffer's y = 0, so align itemsRect and coordinates to that + // + // TODO: In future use wxAutoBufferedPaintDC (for example) // -#if wxPG_DOUBLE_BUFFER int yRelMod = 0; wxRect cr2; if ( isBuffered ) { - xRelMod = drawRect->x; - yRelMod = drawRect->y; + xRelMod = itemsRect->x; + yRelMod = itemsRect->y; // - // drawRect conversion - cr2 = *drawRect; + // itemsRect conversion + cr2 = *itemsRect; cr2.x -= xRelMod; cr2.y -= yRelMod; - drawRect = &cr2; + itemsRect = &cr2; firstItemTopY -= yRelMod; lastItemBottomY -= yRelMod; } -#else - wxUnusedVar(isBuffered); -#endif int x = m_marginWidth - xRelMod; @@ -2530,6 +2541,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; @@ -2572,6 +2585,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); } } @@ -2580,7 +2600,7 @@ void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 ) void wxPropertyGrid::RefreshProperty( wxPGProperty* p ) { - if ( m_pState->DoIsPropertySelected(p) ) + if ( m_pState->DoIsPropertySelected(p) || p->IsChildSelected(true) ) { // NB: We must copy the selection. wxArrayPGProperty selection = m_pState->m_selection; @@ -2893,7 +2913,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; @@ -2928,9 +2948,9 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags ) EditorsValueWasNotModified(); } - bool res = true; + m_inCommitChangesFromEditor = false; - m_inCommitChangesFromEditor = 0; + bool res = true; if ( validationFailure && !forceSuccess ) { @@ -2972,6 +2992,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 @@ -3104,37 +3125,64 @@ 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() ) + if ( msg.empty() ) return; #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 } // ----------------------------------------------------------------------- @@ -3142,7 +3190,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); @@ -3204,14 +3272,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."); + if ( msg.empty() ) + 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; @@ -3240,6 +3326,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; } // ----------------------------------------------------------------------- @@ -3250,12 +3355,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 ); @@ -3274,6 +3382,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) ) { @@ -3341,8 +3453,6 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags ) SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL ); - m_inDoPropertyChanged = 0; - return true; } @@ -3396,18 +3506,52 @@ 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; } // ----------------------------------------------------------------------- -void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) +bool wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) { + // + // NB: We should return true if the event was recognized as + // a dedicated wxPropertyGrid event, and as such was + // either properly handled or ignored. + // + // It is possible that this handler receives event even before // the control has been properly initialized. Let's skip the // event handling in that case. if ( !m_pState ) - return; + return false; + + // 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 true; + } wxPGProperty* selected = GetSelection(); @@ -3415,14 +3559,15 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) // 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). m_processedEvent ) - return; + return true; if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT ) - return; + return true; wxVariant pendingValue(selected->GetValueRef()); wxWindow* wnd = GetEditorControl(); @@ -3434,7 +3579,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 @@ -3446,16 +3591,22 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) wxString newTcValue = tc->GetValue(); if ( m_prevTcValue == newTcValue ) - return; + return true; m_prevTcValue = newTcValue; } else if ( wnd->IsKindOf(CLASSINFO(wxComboCtrl)) ) { + // In some cases we might stumble unintentionally on + // wxComboCtrl's embedded wxTextCtrl's events. Let's + // avoid them. + if ( editorWnd->IsKindOf(CLASSINFO(wxTextCtrl)) ) + return false; + wxComboCtrl* cc = (wxComboCtrl*) wnd; wxString newTcValue = cc->GetTextCtrl()->GetValue(); if ( m_prevTcValue == newTcValue ) - return; + return true; m_prevTcValue = newTcValue; } } @@ -3464,6 +3615,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) bool validationFailure = false; bool buttonWasHandled = false; + bool result = false; // // Try common button handling @@ -3491,6 +3643,8 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) if ( editor->OnEvent( this, selected, editorWnd, event ) ) { + result = true; + // If changes, validate them if ( DoEditorValidate() ) { @@ -3498,6 +3652,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 { @@ -3557,12 +3718,15 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event ) // Let unhandled button click events go to the parent if ( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED ) { + result = true; wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId()); GetEventHandler()->AddPendingEvent(evt); } } ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT); + + return result; } // ----------------------------------------------------------------------- @@ -3580,14 +3744,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 @@ -3731,6 +3902,21 @@ private: m_propGrid->HandleCustomEditorEvent(event); + // + // NB: We should return true if the event was recognized as + // a dedicated wxPropertyGrid event, and as such was + // either properly handled or ignored. + // + if ( m_propGrid->IsMainButtonEvent(event) ) + return true; + + // + // 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); } @@ -3790,20 +3976,7 @@ void wxPropertyGrid::FreeEditors() // Return focus back to canvas from children (this is required at least for // GTK+, which, unlike Windows, clears focus when control is destroyed // instead of moving it to closest parent). - wxWindow* focus = wxWindow::FindFocus(); - if ( focus ) - { - wxWindow* parent = focus->GetParent(); - while ( parent ) - { - if ( parent == this ) - { - SetFocusOnCanvas(); - break; - } - parent = parent->GetParent(); - } - } + SetFocusOnCanvas(); // Do not free editors immediately if processing events if ( m_wndEditor2 ) @@ -3843,13 +4016,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; @@ -3877,6 +4048,8 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) wxPrintf( "P = NULL\n" ); */ + wxWindow* primaryCtrl = NULL; + // If we are frozen, then just set the values. if ( m_frozen ) { @@ -3915,7 +4088,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) } } - m_inDoSelectProperty = 0; return true; } @@ -3923,8 +4095,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 ) { @@ -3933,11 +4103,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); @@ -3963,8 +4137,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 ); @@ -4003,7 +4175,7 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_wndEditor = wndList.m_primary; m_wndEditor2 = wndList.m_secondary; - wxWindow* primaryCtrl = GetEditorControl(); + primaryCtrl = GetEditorControl(); // // Essentially, primaryCtrl == m_wndEditor @@ -4131,41 +4303,30 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY); } -#if wxUSE_STATUSBAR + const wxString* pHelpString = NULL; - // - // Show help text in status bar. - // (if found and grid not embedded in manager with help box and - // style wxPG_EX_HELP_AS_TOOLTIPS is not used). - // + if ( p ) + pHelpString = &p->GetHelpString(); 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(); - } +#if wxUSE_STATUSBAR + // + // Show help text in status bar. + // (if found and grid not embedded in manager with help box and + // style wxPG_EX_HELP_AS_TOOLTIPS is not used). + // + wxStatusBar* statusbar = GetStatusBar(); if ( statusbar ) { - const wxString* pHelpString = (const wxString*) NULL; - - if ( p ) + if ( pHelpString && !pHelpString->empty() ) { - pHelpString = &p->GetHelpString(); - if ( pHelpString->length() ) - { - // Set help box text. - statusbar->SetStatusText( *pHelpString ); - m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR; - } + // Set help box text. + statusbar->SetStatusText( *pHelpString ); + m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR; } - - if ( (!pHelpString || !pHelpString->length()) && - (m_iFlags & wxPG_FL_STRING_IN_STATUSBAR) ) + else if ( m_iFlags & wxPG_FL_STRING_IN_STATUSBAR ) { // Clear help box - but only if it was written // by us at previous time. @@ -4173,10 +4334,21 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags ) m_iFlags &= ~(wxPG_FL_STRING_IN_STATUSBAR); } } +#endif } + else + { +#if wxPG_SUPPORT_TOOLTIPS + // + // Show help as a tool tip on the editor control. + // + if ( pHelpString && !pHelpString->empty() && + primaryCtrl ) + { + primaryCtrl->SetToolTip(*pHelpString); + } #endif - - m_inDoSelectProperty = 0; + } // call wx event handler (here so that it also occurs on deselection) if ( !(flags & wxPG_SEL_DONT_SEND_EVENT) ) @@ -4397,6 +4569,11 @@ void wxPropertyGrid::RecalculateVirtualSize( int forceXPos ) SetScrollbars( wxPG_PIXELS_PER_UNIT, wxPG_PIXELS_PER_UNIT, xAmount, yAmount, xPos, yPos, true ); + // This may be needed in addition to calling SetScrollbars() + // when class inherits from wxScrollHelper instead of + // actual wxScrolled. + AdjustScrollbars(); + // Must re-get size now GetClientSize(&width,&height); @@ -4430,7 +4607,6 @@ void wxPropertyGrid::OnResize( wxSizeEvent& event ) m_width = width; m_height = height; -#if wxPG_DOUBLE_BUFFER if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) ) { int dblh = (m_lineHeight*2); @@ -4458,8 +4634,6 @@ void wxPropertyGrid::OnResize( wxSizeEvent& event ) } } -#endif - m_pState->OnClientWidthChange( width, event.GetSize().x - m_ncWidth, true ); m_ncWidth = event.GetSize().x; @@ -4495,7 +4669,24 @@ void wxPropertyGrid::SetVirtualWidth( int width ) void wxPropertyGrid::SetFocusOnCanvas() { - SetFocusIgnoringChildren(); + // To prevent wxPropertyGrid from stealing focus from other controls, + // only move focus to the grid if it was already in one if its child + // controls. + wxWindow* focus = wxWindow::FindFocus(); + if ( focus ) + { + wxWindow* parent = focus->GetParent(); + while ( parent ) + { + if ( parent == this ) + { + SetFocus(); + break; + } + parent = parent->GetParent(); + } + } + m_editorFocused = 0; } @@ -4627,7 +4818,10 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even else DoExpand( p, true ); } - res = false; + // Do not Skip() the event after selection has been made. + // Otherwise default event handling behaviour kicks in + // and may revert focus back to the main canvas. + res = true; } else { @@ -4766,28 +4960,6 @@ bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), // ----------------------------------------------------------------------- -#if wxPG_SUPPORT_TOOLTIPS - -void wxPropertyGrid::SetToolTip( const wxString& tipString ) -{ - if ( tipString.length() ) - { - wxScrolledWindow::SetToolTip(tipString); - } - else - { - #if wxPG_ALLOW_EMPTY_TOOLTIPS - wxScrolledWindow::SetToolTip( m_emptyString ); - #else - wxScrolledWindow::SetToolTip( NULL ); - #endif - } -} - -#endif // #if wxPG_SUPPORT_TOOLTIPS - -// ----------------------------------------------------------------------- - // Return false if should be skipped bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event ) @@ -4814,7 +4986,6 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, { int newSplitterX = x - m_dragOffset; - int splitterX = x - splitterHitOffset; // Splitter redraw required? if ( newSplitterX != splitterX ) @@ -4878,8 +5049,6 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, // if ( m_windowStyle & wxPG_TOOLTIPS ) { - wxToolTip* tooltip = GetToolTip(); - if ( m_propHover != prevHover || prevSide != m_mouseSide ) { if ( m_propHover && !m_propHover->IsCategory() ) @@ -4919,34 +5088,18 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, int tw, th; GetTextExtent( tipString, &tw, &th, 0, 0 ); if ( tw > space ) - { SetToolTip( tipString ); - } } else { - if ( tooltip ) - { - #if wxPG_ALLOW_EMPTY_TOOLTIPS - SetToolTip( m_emptyString ); - #else - wxScrolledWindow::SetToolTip( NULL ); - #endif - } + SetToolTip( m_emptyString ); } } } else { - if ( tooltip ) - { - #if wxPG_ALLOW_EMPTY_TOOLTIPS - SetToolTip( m_emptyString ); - #else - wxScrolledWindow::SetToolTip( NULL ); - #endif - } + SetToolTip( m_emptyString ); } } } @@ -5056,7 +5209,7 @@ bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y), // 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 - + int posDiff = abs(m_startingSplitterX - GetSplitterPosition(m_draggedSplitter)); if ( posDiff > 1 ) @@ -5149,9 +5302,13 @@ void wxPropertyGrid::OnMouseClick( wxMouseEvent &event ) int x, y; if ( OnMouseCommon( event, &x, &y ) ) { - HandleMouseClick(x,y,event); + if ( !HandleMouseClick(x, y, event) ) + event.Skip(); + } + else + { + event.Skip(); } - event.Skip(); } // ----------------------------------------------------------------------- @@ -5174,7 +5331,9 @@ void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent &event ) int x, y; CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y ); HandleMouseDoubleClick(x,y,event); - event.Skip(); + + // Do not Skip() event here - OnMouseClick() call above + // should have already taken care of it. } // ----------------------------------------------------------------------- @@ -5196,9 +5355,13 @@ void wxPropertyGrid::OnMouseUp( wxMouseEvent &event ) int x, y; if ( OnMouseCommon( event, &x, &y ) ) { - HandleMouseUp(x,y,event); + if ( !HandleMouseUp(x, y, event) ) + event.Skip(); + } + else + { + event.Skip(); } - event.Skip(); } // ----------------------------------------------------------------------- @@ -5495,8 +5658,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() ) @@ -5517,6 +5682,12 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild ) wxPGProperty* p = selected; + if ( action == wxPG_ACTION_EDIT && !editorFocused ) + { + DoSelectProperty( p, wxPG_SEL_FOCUS ); + wasHandled = true; + } + // Travel and expand/collapse int selectDir = -2; @@ -5550,7 +5721,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; } } @@ -5677,6 +5869,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; @@ -5816,7 +6015,7 @@ wxPGEditor* wxPropertyGrid::DoRegisterEditorClass( wxPGEditor* editorClass, RegisterDefaultEditors(); wxString name = editorName; - if ( name.length() == 0 ) + if ( name.empty() ) name = editorClass->GetName(); // Existing editor under this name? @@ -6217,7 +6416,7 @@ wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString else { bool found = false; - if ( idString.length() ) + if ( !idString.empty() ) { wxPGHashMapS2P::iterator it = m_dictIdChoices.find(idString); if ( it != m_dictIdChoices.end() ) @@ -6293,7 +6492,7 @@ wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString } // Assign to id - if ( idString.length() ) + if ( !idString.empty() ) m_dictIdChoices[idString] = choices.GetData(); } } @@ -6334,7 +6533,7 @@ bool wxPropertyGridPopulator::AddAttribute( const wxString& name, wxString valuel = value.Lower(); wxVariant variant; - if ( type.length() == 0 ) + if ( type.empty() ) { long v;