]> git.saurik.com Git - wxWidgets.git/blobdiff - src/propgrid/propgrid.cpp
Somehow, setting a tint color makes gauge work :/.
[wxWidgets.git] / src / propgrid / propgrid.cpp
index 7ae8c3013e9e9c6f25652ac219e8e6e90609d6fb..63b5099321f10ed3c11ebb34beebd5bcf0323ad6 100644 (file)
@@ -4,9 +4,8 @@
 // Author:      Jaakko Salli
 // Modified by:
 // 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 +62,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
 
 //#define wxPG_TOOLTIP_DELAY              1000
 
+// This is the number of pixels the expander button inside
+// property cells (i.e. not in the grey margin area are
+// adjusted.
+#define IN_CELL_EXPANDER_BUTTON_X_ADJUST    2
+
 // -----------------------------------------------------------------------
 
 #if wxUSE_INTL
@@ -142,7 +147,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)
@@ -153,8 +158,8 @@ IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule)
 void wxPGInitResourceModule()
 {
     wxModule* module = new wxPGGlobalVarsClassManager;
-    module->Init();
     wxModule::RegisterModule(module);
+    wxModule::InitializeModules();
 }
 
 wxPGGlobalVarsClass* wxPGGlobalVars = NULL;
@@ -195,7 +200,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;
 }
@@ -226,6 +234,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;
 }
 
@@ -237,9 +249,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)
@@ -262,7 +274,7 @@ END_EVENT_TABLE()
 // -----------------------------------------------------------------------
 
 wxPropertyGrid::wxPropertyGrid()
-    : wxScrolledWindow()
+    : wxControl(), wxScrollHelper(this)
 {
     Init1();
 }
@@ -275,7 +287,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);
@@ -302,7 +314,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();
 
@@ -320,6 +335,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;
@@ -331,15 +347,20 @@ void wxPropertyGrid::Init1()
     m_eventObject = this;
     m_curFocused = NULL;
     m_processedEvent = NULL;
+    m_tlp = 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 );
@@ -354,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;
@@ -436,9 +455,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();
 
@@ -446,7 +465,6 @@ void wxPropertyGrid::Init2()
     SetBackgroundStyle( wxBG_STYLE_CUSTOM );
 
     // Hook the top-level parent
-    m_tlp = NULL;
     m_tlpClosed = NULL;
     m_tlpClosedTime = 0;
 
@@ -456,10 +474,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();
@@ -527,10 +541,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;
@@ -558,7 +570,7 @@ bool wxPropertyGrid::Destroy()
     if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
         ReleaseMouse();
 
-    return wxScrolledWindow::Destroy();
+    return wxControl::Destroy();
 }
 
 // -----------------------------------------------------------------------
@@ -617,12 +629,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 )
     {
@@ -640,7 +652,7 @@ void wxPropertyGrid::Freeze()
 {
     if ( !m_frozen )
     {
-        wxScrolledWindow::Freeze();
+        wxControl::Freeze();
     }
     m_frozen++;
 }
@@ -653,11 +665,9 @@ void wxPropertyGrid::Thaw()
 
     if ( !m_frozen )
     {
-        wxScrolledWindow::Thaw();
+        wxControl::Thaw();
         RecalculateVirtualSize();
-    #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
         Refresh();
-    #endif
 
         // Force property re-selection
         // NB: We must copy the selection.
@@ -774,9 +784,12 @@ bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop,
                                                    wxMouseEvent* mouseEvent,
                                                    int selFlags )
 {
+    const wxArrayPGProperty& selection = GetSelectedProperties();
     bool alreadySelected = m_pState->DoIsPropertySelected(prop);
     bool res = true;
-    bool addToExistingSelection;
+
+    // Set to 2 if also add all items in between
+    int addToExistingSelection = 0;
 
     if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION )
     {
@@ -794,21 +807,24 @@ bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop,
             }
             else
             {
-                addToExistingSelection = mouseEvent->ShiftDown();
+                if ( mouseEvent->ControlDown() )
+                {
+                    addToExistingSelection = 1;
+                }
+                else if ( mouseEvent->ShiftDown() )
+                {
+                    if ( selection.size() > 0 && !prop->IsCategory() )
+                        addToExistingSelection = 2;
+                    else
+                        addToExistingSelection = 1;
+                }
             }
         }
-        else
-        {
-            addToExistingSelection = false;
-        }
-    }
-    else
-    {
-        addToExistingSelection = false;
     }
 
-    if ( addToExistingSelection )
+    if ( addToExistingSelection == 1 )
     {
+        // Add/remove one
         if ( !alreadySelected )
         {
             res = DoAddToSelection(prop, selFlags);
@@ -818,6 +834,59 @@ bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop,
             res = DoRemoveFromSelection(prop, selFlags);
         }
     }
+    else if ( addToExistingSelection == 2 )
+    {
+        // Add this, and all in between
+
+        // Find top selected property
+        wxPGProperty* topSelProp = selection[0];
+        int topSelPropY = topSelProp->GetY();
+        for ( unsigned int i=1; i<selection.size(); i++ )
+        {
+            wxPGProperty* p = selection[i];
+            int y = p->GetY();
+            if ( y < topSelPropY )
+            {
+                topSelProp = p;
+                topSelPropY = y;
+            }
+        }
+
+        wxPGProperty* startFrom;
+        wxPGProperty* stopAt;
+
+        if ( prop->GetY() <= topSelPropY )
+        {
+            // Property is above selection (or same)
+            startFrom = prop;
+            stopAt = topSelProp;
+        }
+        else
+        {
+            // Property is below selection
+            startFrom = topSelProp;
+            stopAt = prop;
+        }
+
+        // Iterate through properties in-between, and select them
+        wxPropertyGridIterator it;
+
+        for ( it = GetIterator(wxPG_ITERATE_VISIBLE, startFrom);
+              !it.AtEnd();
+              it++ )
+        {
+            wxPGProperty* p = *it;
+
+            if ( !p->IsCategory() &&
+                 !m_pState->DoIsPropertySelected(p) )
+            {
+                DoAddToSelection(p, selFlags);
+            }
+
+            if ( p == stopAt )
+                break;
+        }
+    }
     else
     {
         res = DoSelectAndEdit(prop, colIndex, selFlags);
@@ -924,7 +993,7 @@ void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex,
                                           colIndex);
 
     wxWindowID id = tc->GetId();
-    tc->Connect(id, wxEVT_COMMAND_TEXT_ENTER,
+    tc->Connect(id, wxEVT_TEXT_ENTER,
         wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress),
         NULL, this);
     tc->Connect(id, wxEVT_KEY_DOWN,
@@ -957,7 +1026,7 @@ void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event )
     }
     else
     {
-        event.Skip();
+        HandleKeyEvent(event, true);
     }
 }
 
@@ -1001,11 +1070,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);
 }
 
@@ -1045,14 +1120,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();
@@ -1158,7 +1230,7 @@ bool wxPropertyGrid::Reparent( wxWindowBase *newParent )
 {
     OnTLPChanging((wxWindow*)newParent);
 
-    bool res = wxScrolledWindow::Reparent(newParent);
+    bool res = wxControl::Reparent(newParent);
 
     return res;
 }
@@ -1171,7 +1243,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);
@@ -1320,6 +1392,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) )
@@ -1327,6 +1401,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) )
@@ -1362,7 +1438,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 );
@@ -1398,6 +1474,7 @@ void wxPropertyGrid::SetCellBackgroundColour( const wxColour& col )
     m_coloursCustomized |= 0x08;
 
     m_propertyDefaultCell.GetData()->SetBgCol(col);
+    m_unspecifiedAppearance.SetBgCol(col);
 
     Refresh();
 }
@@ -1410,6 +1487,7 @@ void wxPropertyGrid::SetCellTextColour( const wxColour& col )
     m_coloursCustomized |= 0x10;
 
     m_propertyDefaultCell.GetData()->SetFgCol(col);
+    m_unspecifiedAppearance.SetFgCol(col);
 
     Refresh();
 }
@@ -1488,6 +1566,9 @@ void wxPropertyGrid::PrepareAfterItemsAdded()
         Sort(wxPG_SORT_TOP_LEVEL_ONLY);
 
     RecalculateVirtualSize();
+
+    // Fix editor position
+    CorrectEditorWidgetPosY();
 }
 
 // -----------------------------------------------------------------------
@@ -1549,8 +1630,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 );
 }
 
 // -----------------------------------------------------------------------
@@ -1598,7 +1679,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;
@@ -1657,7 +1738,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;
@@ -1740,9 +1821,11 @@ void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
 
     // FIXME: This is just a workaround for a bug that causes splitters not
     //        to paint when other windows are being dragged over the grid.
-    wxRect fullRect = GetRect();
-    r.x = fullRect.x;
-    r.width = fullRect.width;
+    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 );
@@ -1833,7 +1916,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 ||
@@ -1843,15 +1926,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();
 
@@ -1862,14 +1956,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
@@ -1883,13 +1976,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 )
@@ -1900,48 +1992,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() );
 
@@ -1950,8 +2038,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;
@@ -1961,20 +2049,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;
@@ -1984,31 +2072,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;
 
@@ -2048,10 +2134,7 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc,
 
     const wxPGProperty* firstSelected = GetSelection();
     const wxPropertyGridPageState* state = m_pState;
-
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
-    bool wasSelectedPainted = false;
-#endif
+    const wxArrayInt& colWidths = state->m_colWidths;
 
     // TODO: Only render columns that are within clipping region.
 
@@ -2147,9 +2230,9 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc,
         unsigned int si;
         int sx = x;
 
-        for ( si=0; si<state->m_colWidths.size(); si++ )
+        for ( si=0; si<colWidths.size(); si++ )
         {
-            sx += state->m_colWidths[si];
+            sx += colWidths[si];
             dc.DrawLine( sx, y, sx, y2 );
         }
 
@@ -2180,11 +2263,6 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc,
         }
         else
         {
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
-            if ( p == firstSelected )
-                wasSelectedPainted = true;
-#endif
-
             renderFlags |= wxPGCellRenderer::Selected;
 
             if ( !p->IsCategory() )
@@ -2245,12 +2323,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 )
             {
@@ -2262,121 +2344,154 @@ 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
         {
-            if ( p->m_flags & wxPG_PROP_MODIFIED && (windowStyle & wxPG_BOLD_MODIFIED) )
+            // Fine tune button rectangle to actually fit the cell
+            if ( butRect.x > 0 )
+                butRect.x += IN_CELL_EXPANDER_BUTTON_X_ADJUST;
+
+            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; ci<colWidths.size(); ci++ )
+            cellX += colWidths[ci];
+        cellRect.x = cellX;
+
+        // Draw cells from back to front so that we can easily tell if the
+        // cell on the right was empty from text
+        bool prevFilled = true;
+        ci = colWidths.size();
+        do
+        {
+            ci--;
+
+            int textXAdd = 0;
+
+            if ( ci == 0 )
+            {
+                textXAdd = textMarginHere - greyDepthX;
+                cellRect.width = firstCellWidth;
+                cellRect.x = firstCellX;
+            }
+            else
+            {
+                int colWidth = colWidths[ci];
+                cellRect.width = colWidth;
+                cellRect.x -= colWidth;
+            }
 
-            for ( ci=0; ci<state->m_colWidths.size(); ci++ )
+            // Merge with column to the right?
+            if ( !prevFilled && isCategory )
             {
-                cellRect.width = nextCellWidth - 1;
+                cellRect.width += colWidths[ci+1];
+            }
+
+            if ( !isCategory )
+                cellRect.width -= 1;
 
-                wxWindow* cellEditor = NULL;
-                int cellRenderFlags = renderFlags;
+            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 );
+            // 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);
@@ -2384,18 +2499,6 @@ int wxPropertyGrid::DoDrawItems( wxDC& dc,
         y += rowHeight;
     }
 
-    // Refresh editor controls (seems not needed on msw)
-    // NOTE: This code is mandatory for GTK!
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
-    if ( wasSelectedPainted )
-    {
-        if ( m_wndEditor )
-            m_wndEditor->Refresh();
-        if ( m_wndEditor2 )
-            m_wndEditor2->Refresh();
-    }
-#endif
-
     return y;
 }
 
@@ -2414,6 +2517,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;
@@ -2456,6 +2561,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);
     }
 }
@@ -2464,7 +2576,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;
@@ -2526,7 +2638,7 @@ void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground),
 
     wxWindow::Refresh(false, rect);
 
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+#if wxPG_REFRESH_CONTROLS
     // I think this really helps only GTK+1.2
     if ( m_wndEditor ) m_wndEditor->Refresh();
     if ( m_wndEditor2 ) m_wndEditor2->Refresh();
@@ -2669,32 +2781,49 @@ void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState )
 
 // Call to SetSplitterPosition will always disable splitter auto-centering
 // if parent window is shown.
-void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh,
-                                             int splitterIndex,
-                                             bool allPages )
+void wxPropertyGrid::DoSetSplitterPosition( int newxpos,
+                                            int splitterIndex,
+                                            int flags )
 {
     if ( ( newxpos < wxPG_DRAG_MARGIN ) )
         return;
 
     wxPropertyGridPageState* state = m_pState;
 
-    state->DoSetSplitterPosition( newxpos, splitterIndex, allPages );
+    if ( flags & wxPG_SPLITTER_FROM_EVENT )
+        state->m_dontCenterSplitter = true;
+
+    state->DoSetSplitterPosition(newxpos, splitterIndex, flags);
 
-    if ( refresh )
+    if ( flags & wxPG_SPLITTER_REFRESH )
     {
         if ( GetSelection() )
             CorrectEditorWidgetSizeX();
 
         Refresh();
     }
+
+    return;
+}
+
+// -----------------------------------------------------------------------
+
+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 enableAutoCentering )
+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;
 }
 
@@ -2760,7 +2889,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;
@@ -2795,9 +2924,9 @@ bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags )
             EditorsValueWasNotModified();
         }
 
-        bool res = true;
+        m_inCommitChangesFromEditor = false;
 
-        m_inCommitChangesFromEditor = 0;
+        bool res = true;
 
         if ( validationFailure && !forceSuccess )
         {
@@ -2839,6 +2968,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
@@ -2927,7 +3057,7 @@ bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue
             if ( changedProperty == GetSelection() )
             {
                 wxWindow* editor = GetEditorControl();
-                wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) );
+                wxASSERT( wxDynamicCast(editor, wxTextCtrl) );
                 evtChangingValue = wxStaticCast(editor, wxTextCtrl)->GetValue();
             }
             else
@@ -2971,37 +3101,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 ( wxDynamicCast(topWnd, 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
 }
 
 // -----------------------------------------------------------------------
@@ -3009,7 +3166,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);
@@ -3018,7 +3195,7 @@ bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property,
 
     //
     // For non-wxTextCtrl editors, we do need to revert the value
-    if ( !editor->IsKindOf(CLASSINFO(wxTextCtrl)) &&
+    if ( !wxDynamicCast(editor, wxTextCtrl) &&
          property == GetSelection() )
     {
         property->GetEditorClass()->UpdateControl(property, editor);
@@ -3071,14 +3248,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.");
 
-        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;
@@ -3107,6 +3302,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;
 }
 
 // -----------------------------------------------------------------------
@@ -3117,12 +3331,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 );
@@ -3141,6 +3358,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) )
     {
@@ -3184,7 +3405,7 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags )
     }
     else
     {
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+#if wxPG_REFRESH_CONTROLS
         if ( m_wndEditor ) m_wndEditor->Refresh();
         if ( m_wndEditor2 ) m_wndEditor2->Refresh();
 #endif
@@ -3208,8 +3429,6 @@ bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags )
 
     SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL );
 
-    m_inDoPropertyChanged = 0;
-
     return true;
 }
 
@@ -3263,27 +3482,68 @@ 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 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();
 
     // 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).
           m_processedEvent )
-        return;
+        return true;
 
     if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT )
-        return;
+        return true;
 
     wxVariant pendingValue(selected->GetValueRef());
     wxWindow* wnd = GetEditorControl();
@@ -3295,31 +3555,47 @@ 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_TEXT && wnd )
     {
-        wxTextCtrl* tc = (wxTextCtrl*) wnd;
+        if ( wxDynamicCast(wnd, wxTextCtrl) )
+        {
+            wxTextCtrl* tc = (wxTextCtrl*) wnd;
 
-        wxString newTcValue = tc->GetValue();
-        if ( m_prevTcValue == newTcValue )
-            return;
+            wxString newTcValue = tc->GetValue();
+            if ( m_prevTcValue == newTcValue )
+                return true;
+            m_prevTcValue = newTcValue;
+        }
+        else if ( wxDynamicCast(wnd, wxComboCtrl) )
+        {
+            // In some cases we might stumble unintentionally on
+            // wxComboCtrl's embedded wxTextCtrl's events. Let's
+            // avoid them.
+            if ( wxDynamicCast(editorWnd, wxTextCtrl) )
+                return false;
 
-        m_prevTcValue = newTcValue;
+            wxComboCtrl* cc = (wxComboCtrl*) wnd;
+
+            wxString newTcValue = cc->GetTextCtrl()->GetValue();
+            if ( m_prevTcValue == newTcValue )
+                return true;
+            m_prevTcValue = newTcValue;
+        }
     }
 
     SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
 
     bool validationFailure = false;
     bool buttonWasHandled = false;
+    bool result = false;
 
     //
     // Try common button handling
-    if ( m_wndEditor2 && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
+    if ( m_wndEditor2 && event.GetEventType() == wxEVT_BUTTON )
     {
         wxPGEditorDialogAdapter* adapter = selected->GetEditorDialog();
 
@@ -3327,7 +3603,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
         {
             buttonWasHandled = true;
             // Store as res2, as previously (and still currently alternatively)
-            // dialogs can be shown by handling wxEVT_COMMAND_BUTTON_CLICKED
+            // dialogs can be shown by handling wxEVT_BUTTON
             // in wxPGProperty::OnEvent().
             adapter->ShowDialog( this, selected );
             delete adapter;
@@ -3343,6 +3619,8 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
 
             if ( editor->OnEvent( this, selected, editorWnd, event ) )
             {
+                result = true;
+
                 // If changes, validate them
                 if ( DoEditorValidate() )
                 {
@@ -3350,6 +3628,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
                 {
@@ -3390,7 +3675,7 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
 
         // Regardless of editor type, unfocus editor on
         // text-editing related enter press.
-        if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
+        if ( event.GetEventType() == wxEVT_TEXT_ENTER )
         {
             SetFocusOnCanvas();
         }
@@ -3401,20 +3686,23 @@ void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
 
         // Regardless of editor type, unfocus editor on
         // text-editing related enter press.
-        if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
+        if ( event.GetEventType() == wxEVT_TEXT_ENTER )
         {
             SetFocusOnCanvas();
         }
 
         // Let unhandled button click events go to the parent
-        if ( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
+        if ( !buttonWasHandled && event.GetEventType() == wxEVT_BUTTON )
         {
-            wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId());
+            result = true;
+            wxCommandEvent evt(wxEVT_BUTTON,GetId());
             GetEventHandler()->AddPendingEvent(evt);
         }
     }
 
     ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
+
+    return result;
 }
 
 // -----------------------------------------------------------------------
@@ -3432,14 +3720,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 )
+    {
+        // 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 )
     {
-        //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);
+        splitterX += (p->m_depth - 1) * m_subgroup_extramargin;
     }
 
     return wxRect
@@ -3542,6 +3837,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
 // -----------------------------------------------------------------------
@@ -3568,6 +3878,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_TEXT_ENTER )
+            return true;
+
         return wxEvtHandler::ProcessEvent(event);
     }
 
@@ -3627,20 +3952,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 )
@@ -3680,13 +3992,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;
@@ -3714,6 +4024,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 )
     {
@@ -3752,7 +4064,6 @@ bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags )
                 }
             }
 
-            m_inDoSelectProperty = 0;
             return true;
         }
 
@@ -3760,8 +4071,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 )
             {
@@ -3770,11 +4079,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);
@@ -3800,8 +4113,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 );
 
@@ -3823,6 +4134,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"));
@@ -3837,7 +4151,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
@@ -3892,6 +4206,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 )
@@ -3959,41 +4279,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.
@@ -4001,10 +4310,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) )
@@ -4058,7 +4378,7 @@ void wxPropertyGrid::RefreshEditor()
     editorClass->UpdateControl(p, wnd);
 
     if ( p->IsValueUnspecified() )
-        editorClass ->SetValueToUnspecified(p, wnd);
+        SetEditorAppearance(m_unspecifiedAppearance, true);
 }
 
 // -----------------------------------------------------------------------
@@ -4172,7 +4492,11 @@ bool wxPropertyGrid::DoHideProperty( wxPGProperty* p, bool hide, int flags )
 
 void wxPropertyGrid::RecalculateVirtualSize( int forceXPos )
 {
-    if ( (m_iFlags & wxPG_FL_RECALCULATING_VIRTUAL_SIZE) || m_frozen )
+    // Don't check for !HasInternalFlag(wxPG_FL_INITIALIZED) here. Otherwise
+    // virtual size calculation may go wrong.
+    if ( HasInternalFlag(wxPG_FL_RECALCULATING_VIRTUAL_SIZE) ||
+         m_frozen ||
+         !m_pState )
         return;
 
     //
@@ -4221,13 +4545,17 @@ 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<T>.
+    AdjustScrollbars();
+
     // Must re-get size now
     GetClientSize(&width,&height);
 
     if ( !HasVirtualWidth() )
     {
         m_pState->SetVirtualWidth(width);
-        x = width;
     }
 
     m_width = width;
@@ -4254,7 +4582,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);
@@ -4282,8 +4609,6 @@ void wxPropertyGrid::OnResize( wxSizeEvent& event )
         }
     }
 
-#endif
-
     m_pState->OnClientWidthChange( width, event.GetSize().x - m_ncWidth, true );
     m_ncWidth = event.GetSize().x;
 
@@ -4319,7 +4644,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;
 }
 
@@ -4334,6 +4676,9 @@ bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p,
                                 unsigned int selFlags,
                                 unsigned int column )
 {
+    // selFlags should have wxPG_SEL_NOVALIDATE if event is not
+    // vetoable.
+
     // Send property grid event of specific type and with specific property
     wxPropertyGridEvent evt( eventType, m_eventObject->GetId() );
     evt.SetPropertyGrid(this);
@@ -4356,9 +4701,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();
 }
@@ -4447,7 +4793,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
             {
@@ -4457,7 +4806,16 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even
                     if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
                     {
                         // Double-clicking the splitter causes auto-centering
-                        CenterSplitter( true );
+                        if ( m_pState->GetColumnCount() <= 2 )
+                        {
+                            ResetColumnSizes( true );
+
+                            SendEvent(wxEVT_PG_COL_DRAGGING,
+                                      m_propHover,
+                                      NULL,
+                                      wxPG_SEL_NOVALIDATE,
+                                      (unsigned int)m_draggedSplitter);
+                        }
                     }
                     else if ( m_dragStatus == 0 )
                     {
@@ -4468,33 +4826,39 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even
                         // send event
                         DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE);
 
-                        if ( m_wndEditor )
+                        // Allow application to veto dragging
+                        if ( !SendEvent(wxEVT_PG_COL_BEGIN_DRAG,
+                                        p, NULL, 0,
+                                        (unsigned int)splitterHit) )
                         {
-                            // Changes must be committed here or the
-                            // value won't be drawn correctly
-                            if ( !CommitChangesFromEditor() )
-                                return res;
+                            if ( m_wndEditor )
+                            {
+                                // Changes must be committed here or the
+                                // value won't be drawn correctly
+                                if ( !CommitChangesFromEditor() )
+                                    return res;
 
-                            m_wndEditor->Show ( false );
-                        }
+                                m_wndEditor->Show ( false );
+                            }
 
-                        if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
-                        {
-                            CaptureMouse();
-                            m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
-                        }
+                            if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
+                            {
+                                CaptureMouse();
+                                m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
+                            }
 
-                        m_dragStatus = 1;
-                        m_draggedSplitter = splitterHit;
-                        m_dragOffset = splitterHitOffset;
+                            m_dragStatus = 1;
+                            m_draggedSplitter = splitterHit;
+                            m_dragOffset = splitterHitOffset;
 
-                    #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
-                        // Fixes button disappearance bug
-                        if ( m_wndEditor2 )
-                            m_wndEditor2->Show ( false );
-                    #endif
+                        #if wxPG_REFRESH_CONTROLS
+                            // Fixes button disappearance bug
+                            if ( m_wndEditor2 )
+                                m_wndEditor2->Show ( false );
+                        #endif
 
-                        m_startingSplitterX = x - splitterHitOffset;
+                            m_startingSplitterX = x - splitterHitOffset;
+                        }
                     }
                 }
             }
@@ -4506,6 +4870,10 @@ bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &even
             {
                 int nx = x + m_marginWidth - marginEnds; // Normalize x.
 
+                // Fine tune cell button x
+                if ( !p->IsCategory() )
+                    nx -= IN_CELL_EXPANDER_BUTTON_X_ADJUST;
+
                 if ( (nx >= m_gutterWidth && nx < (m_gutterWidth+m_iconWidth)) )
                 {
                     int y2 = y % m_lineHeight;
@@ -4567,28 +4935,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 )
@@ -4615,23 +4961,21 @@ bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y,
         {
 
             int newSplitterX = x - m_dragOffset;
-            int splitterX = x - splitterHitOffset;
 
             // Splitter redraw required?
             if ( newSplitterX != splitterX )
             {
                 // Move everything
-                state->m_dontCenterSplitter = true;
-                state->DoSetSplitterPosition(newSplitterX,
-                                             m_draggedSplitter,
-                                             false);
-                state->m_fSplitterX = (float) newSplitterX;
-
-                if ( GetSelection() )
-                    CorrectEditorWidgetSizeX();
-
-                Update();
-                Refresh();
+                DoSetSplitterPosition(newSplitterX,
+                                      m_draggedSplitter,
+                                      wxPG_SPLITTER_REFRESH |
+                                      wxPG_SPLITTER_FROM_EVENT);
+
+                SendEvent(wxEVT_PG_COL_DRAGGING,
+                          m_propHover,
+                          NULL,
+                          wxPG_SEL_NOVALIDATE,
+                          (unsigned int)m_draggedSplitter);
             }
 
             m_dragStatus = 2;
@@ -4680,8 +5024,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() )
@@ -4721,34 +5063,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 );
                 }
             }
         }
@@ -4837,7 +5163,7 @@ bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y),
     int splitterHitOffset;
     state->HitTestH( x, &splitterHit, &splitterHitOffset );
 
-    // No event type check - basicly calling this method should
+    // No event type check - basically calling this method should
     // just stop dragging.
     // Left up after dragged?
     if ( m_dragStatus >= 1 )
@@ -4849,8 +5175,20 @@ bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y),
         // (it is only here as a reminder to not to do it)
         //splitterX = x;
 
-        // Disable splitter auto-centering
-        state->m_dontCenterSplitter = true;
+        SendEvent(wxEVT_PG_COL_END_DRAG,
+                  m_propHover,
+                  NULL,
+                  wxPG_SEL_NOVALIDATE,
+                  (unsigned int)m_draggedSplitter);
+
+        // 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 )
@@ -4878,7 +5216,7 @@ bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y),
             m_wndEditor->Show ( true );
         }
 
-    #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+    #if wxPG_REFRESH_CONTROLS
         // Fixes button disappearance bug
         if ( m_wndEditor2 )
             m_wndEditor2->Show ( true );
@@ -4904,7 +5242,7 @@ bool wxPropertyGrid::OnMouseCommon( wxMouseEvent& event, int* px, int* py )
 
     // Hide popup on clicks
     if ( event.GetEventType() != wxEVT_MOTION )
-        if ( wnd && wnd->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
+        if ( wxDynamicCast(wnd, wxOwnerDrawnComboBox) )
         {
             ((wxOwnerDrawnComboBox*)wnd)->HidePopup();
         }
@@ -4939,9 +5277,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();
 }
 
 // -----------------------------------------------------------------------
@@ -4964,7 +5306,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.
 }
 
 // -----------------------------------------------------------------------
@@ -4986,9 +5330,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();
 }
 
 // -----------------------------------------------------------------------
@@ -5203,12 +5551,14 @@ void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild )
 
     if ( keycode == WXK_TAB )
     {
+#if defined(__WXGTK__)
         wxWindow* mainControl;
 
         if ( HasInternalFlag(wxPG_FL_IN_MANAGER) )
             mainControl = GetParent();
         else
             mainControl = this;
+#endif
 
         if ( !event.ShiftDown() )
         {
@@ -5285,8 +5635,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() )
@@ -5307,6 +5659,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;
 
@@ -5340,7 +5698,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;
         }
     }
@@ -5394,7 +5773,7 @@ bool wxPropertyGrid::ButtonTriggerKeyTest( int action, wxKeyEvent& event )
     if ( action == wxPG_ACTION_PRESS_BUTTON &&
          m_wndEditor2 )
     {
-        wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, m_wndEditor2->GetId());
+        wxCommandEvent evt(wxEVT_BUTTON, m_wndEditor2->GetId());
         GetEventHandler()->AddPendingEvent(evt);
         return true;
     }
@@ -5430,6 +5809,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<arr.size(); i++ )
+        {
+            DeleteProperty(arr[i]);
+        }
+        arr.clear();
+    }
+    if ( m_removedProperties.size() > 0 )
+    {
+        wxArrayPGProperty& arr = m_removedProperties;
+        for ( unsigned int i=0; i<arr.size(); i++ )
+        {
+            RemoveProperty(arr[i]);
+        }
+        arr.clear();
+    }
 }
 
 bool wxPropertyGrid::IsEditorFocused() const
@@ -5446,7 +5846,16 @@ 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;
 
     m_iFlags &= ~(wxPG_FL_FOCUSED);
 
@@ -5455,9 +5864,13 @@ void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused )
     // This must be one of nextFocus' parents.
     while ( parent )
     {
+        if ( parent == wndEditor )
+        {
+            wasEditorFocused = true;
+        }
         // Use m_eventObject, which is either wxPropertyGrid or
         // wxPropertyGridManager, as appropriate.
-        if ( parent == m_eventObject )
+        else if ( parent == m_eventObject )
         {
             m_iFlags |= wxPG_FL_FOCUSED;
             break;
@@ -5465,6 +5878,18 @@ void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused )
         parent = parent->GetParent();
     }
 
+    // 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) !=
@@ -5567,7 +5992,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?
@@ -5767,6 +6192,9 @@ wxDEFINE_EVENT( wxEVT_PG_ITEM_COLLAPSED, wxPropertyGridEvent );
 wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK, wxPropertyGridEvent );
 wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_BEGIN, wxPropertyGridEvent );
 wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_ENDING, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_BEGIN_DRAG, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_DRAGGING, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_END_DRAG, wxPropertyGridEvent );
 
 // -----------------------------------------------------------------------
 
@@ -5776,6 +6204,7 @@ void wxPropertyGridEvent::Init()
     m_column = 1;
     m_canVeto = false;
     m_wasVetoed = false;
+    m_pg = NULL;
 }
 
 // -----------------------------------------------------------------------
@@ -5913,7 +6342,7 @@ wxPGProperty* wxPropertyGridPopulator::Add( const wxString& propClass,
         return NULL;
     }
 
-    if ( !classInfo || !classInfo->IsKindOf(CLASSINFO(wxPGProperty)) )
+    if ( !classInfo || !classInfo->IsKindOf(wxCLASSINFO(wxPGProperty)) )
     {
         ProcessError(wxString::Format(wxT("'%s' is not valid property class"),propClass.c_str()));
         return NULL;
@@ -5965,7 +6394,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() )
@@ -6041,7 +6470,7 @@ wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString
             }
 
             // Assign to id
-            if ( idString.length() )
+            if ( !idString.empty() )
                 m_dictIdChoices[idString] = choices.GetData();
         }
     }
@@ -6082,7 +6511,7 @@ bool wxPropertyGridPopulator::AddAttribute( const wxString& name,
     wxString valuel = value.Lower();
     wxVariant variant;
 
-    if ( type.length() == 0 )
+    if ( type.empty() )
     {
         long v;