]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/datavgen.cpp
compilation fix for case that wxHAS_RAW_BITMAP is NOT defined
[wxWidgets.git] / src / generic / datavgen.cpp
index d962bfd8f8ee04235c24c38fa9b6d16f272826ba..7fef06b83bfdfaaa1c5eb3d1a1d4d5c8105f7d1d 100644 (file)
@@ -38,7 +38,6 @@
 #endif
 
 #include "wx/stockitem.h"
-#include "wx/calctrl.h"
 #include "wx/popupwin.h"
 #include "wx/renderer.h"
 #include "wx/dcbuffer.h"
@@ -49,6 +48,7 @@
 #include "wx/headerctrl.h"
 #include "wx/dnd.h"
 #include "wx/stopwatch.h"
+#include "wx/weakref.h"
 
 //-----------------------------------------------------------------------------
 // classes
@@ -378,6 +378,22 @@ public:
         m_branchData->children.Remove(node);
     }
 
+    // returns position of child node for given item in children list or wxNOT_FOUND
+    int FindChildByItem(const wxDataViewItem& item) const
+    {
+        if ( !m_branchData )
+            return wxNOT_FOUND;
+
+        const wxDataViewTreeNodes& nodes = m_branchData->children;
+        const int len = nodes.size();
+        for ( int i = 0; i < len; i++ )
+        {
+            if ( nodes[i]->m_item == item )
+                return i;
+        }
+        return wxNOT_FOUND;
+    }
+
     const wxDataViewItem & GetItem() const { return m_item; }
     void SetItem( const wxDataViewItem & item ) { m_item = item; }
 
@@ -584,6 +600,7 @@ public:
     wxBitmap CreateItemBitmap( unsigned int row, int &indent );
 #endif // wxUSE_DRAG_AND_DROP
     void OnPaint( wxPaintEvent &event );
+    void OnCharHook( wxKeyEvent &event );
     void OnChar( wxKeyEvent &event );
     void OnVerticalNavigation(unsigned int newCurrent, const wxKeyEvent& event);
     void OnLeftKey();
@@ -606,6 +623,9 @@ public:
     void ChangeCurrentRow( unsigned int row );
     bool TryAdvanceCurrentColumn(wxDataViewTreeNode *node, bool forward);
 
+    wxDataViewColumn *GetCurrentColumn() const { return m_currentCol; }
+    void ClearCurrentColumn() { m_currentCol = NULL; }
+
     bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); }
     bool IsEmpty() { return GetRowCount() == 0; }
 
@@ -617,7 +637,7 @@ public:
     // the displaying number of the tree are changing along with the
     // expanding/collapsing of the tree nodes
     unsigned int GetLastVisibleRow();
-    unsigned int GetRowCount();
+    unsigned int GetRowCount() const;
 
     const wxDataViewSelection& GetSelections() const { return m_selection; }
     void SetSelections( const wxDataViewSelection & sel )
@@ -647,11 +667,16 @@ public:
     int GetLineAt( unsigned int y ) const;       // y / m_lineHeight in fixed mode
 
     void SetRowHeight( int lineHeight ) { m_lineHeight = lineHeight; }
+    int GetRowHeight() const { return m_lineHeight; }
 
     // Some useful functions for row and item mapping
     wxDataViewItem GetItemByRow( unsigned int row ) const;
     int GetRowByItem( const wxDataViewItem & item ) const;
 
+    wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row ) const;
+    // We did not need this temporarily
+    // wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
+
     // Methods for building the mapping tree
     void BuildTree( wxDataViewModel  * model );
     void DestroyTree();
@@ -676,12 +701,12 @@ public:
 
     void OnColumnsCountChanged();
 
-private:
-    wxDataViewTreeNode * GetTreeNodeByRow( unsigned int row ) const;
-    // We did not need this temporarily
-    // wxDataViewTreeNode * GetTreeNodeByItem( const wxDataViewItem & item );
+    // Called by wxDataViewCtrl and our own OnRenameTimer() to start edit the
+    // specified item in the given column.
+    void StartEditing(const wxDataViewItem& item, const wxDataViewColumn* col);
 
-    int RecalculateCount();
+private:
+    int RecalculateCount() const;
 
     // Return false only if the event was vetoed by its handler.
     bool SendExpanderEvent(wxEventType type, const wxDataViewItem& item);
@@ -690,6 +715,10 @@ private:
 
     wxDataViewColumn *FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode);
 
+    bool IsCellEditableInMode(const wxDataViewItem& item, const wxDataViewColumn *col, wxDataViewCellMode mode) const;
+
+    void DrawCellBackground( wxDataViewRenderer* cell, wxDC& dc, const wxRect& rect );
+
 private:
     wxDataViewCtrl             *m_owner;
     int                         m_lineHeight;
@@ -737,6 +766,12 @@ private:
     // This is the tree node under the cursor
     wxDataViewTreeNode * m_underMouse;
 
+    // The control used for editing or NULL.
+    wxWeakRef<wxWindow> m_editorCtrl;
+
+    // Id m_editorCtrl is non-NULL, pointer to the associated renderer.
+    wxDataViewRenderer* m_editorRenderer;
+
 private:
     DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
     DECLARE_EVENT_TABLE()
@@ -972,11 +1007,13 @@ bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state
         GetEnabled() == false)
         flags |= wxCONTROL_DISABLED;
 
-    // check boxes we draw must always have the same, standard size (if it's
-    // bigger than the cell size the checkbox will be truncated because the
-    // caller had set the clipping rectangle to prevent us from drawing outside
-    // the cell)
-    cell.SetSize(GetSize());
+    // Ensure that the check boxes always have at least the minimal required
+    // size, otherwise DrawCheckBox() doesn't really work well. If this size is
+    // greater than the cell size, the checkbox will be truncated but this is a
+    // lesser evil.
+    wxSize size = cell.GetSize();
+    size.IncTo(GetSize());
+    cell.SetSize(size);
 
     wxRendererNative::Get().DrawCheckBox(
             GetOwner()->GetOwner(),
@@ -987,31 +1024,21 @@ bool wxDataViewToggleRenderer::Render( wxRect cell, wxDC *dc, int WXUNUSED(state
     return true;
 }
 
-bool wxDataViewToggleRenderer::WXOnLeftClick(const wxPoint& cursor,
-                                             const wxRect& cell,
-                                             wxDataViewModel *model,
-                                             const wxDataViewItem& item,
-                                             unsigned int col)
-{
-    // only react to clicks directly on the checkbox, not elsewhere in the same cell:
-    if (!wxRect(GetSize()).Contains(cursor))
-        return false;
-
-    return WXOnActivate(cell, model, item, col);
-}
-
-bool wxDataViewToggleRenderer::WXOnActivate(const wxRect& WXUNUSED(cell),
-                                            wxDataViewModel *model,
-                                            const wxDataViewItem& item,
-                                            unsigned int col)
+bool wxDataViewToggleRenderer::WXActivateCell(const wxRect& WXUNUSED(cell),
+                                              wxDataViewModel *model,
+                                              const wxDataViewItem& item,
+                                              unsigned int col,
+                                              const wxMouseEvent *mouseEvent)
 {
-    if (model->IsEnabled(item, col))
+    if ( mouseEvent )
     {
-        model->ChangeValue(!m_toggle, item, col);
-        return true;
+        // only react to clicks directly on the checkbox, not elsewhere in the same cell:
+        if ( !wxRect(GetSize()).Contains(mouseEvent->GetPosition()) )
+            return false;
     }
 
-    return false;
+    model->ChangeValue(!m_toggle, item, col);
+    return true;
 }
 
 wxSize wxDataViewToggleRenderer::GetSize() const
@@ -1077,113 +1104,6 @@ wxSize wxDataViewProgressRenderer::GetSize() const
     return wxSize(40,12);
 }
 
-// ---------------------------------------------------------
-// wxDataViewDateRenderer
-// ---------------------------------------------------------
-
-#define wxUSE_DATE_RENDERER_POPUP (wxUSE_CALENDARCTRL && wxUSE_POPUPWIN)
-
-#if wxUSE_DATE_RENDERER_POPUP
-
-class wxDataViewDateRendererPopupTransient: public wxPopupTransientWindow
-{
-public:
-    wxDataViewDateRendererPopupTransient( wxWindow* parent, wxDateTime *value,
-        wxDataViewModel *model, const wxDataViewItem & item, unsigned int col) :
-        wxPopupTransientWindow( parent, wxBORDER_SIMPLE ),
-        m_item( item )
-    {
-        m_model = model;
-        m_col = col;
-        m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
-        wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
-        sizer->Add( m_cal, 1, wxGROW );
-        SetSizer( sizer );
-        sizer->Fit( this );
-    }
-
-    void OnCalendar( wxCalendarEvent &event );
-
-    wxCalendarCtrl      *m_cal;
-    wxDataViewModel *m_model;
-    unsigned int               m_col;
-    const wxDataViewItem &   m_item;
-
-protected:
-    virtual void OnDismiss()
-    {
-    }
-
-private:
-    DECLARE_EVENT_TABLE()
-};
-
-BEGIN_EVENT_TABLE(wxDataViewDateRendererPopupTransient,wxPopupTransientWindow)
-    EVT_CALENDAR( wxID_ANY, wxDataViewDateRendererPopupTransient::OnCalendar )
-END_EVENT_TABLE()
-
-void wxDataViewDateRendererPopupTransient::OnCalendar( wxCalendarEvent &event )
-{
-    m_model->ChangeValue( event.GetDate(), m_item, m_col );
-    DismissAndNotify();
-}
-
-#endif // wxUSE_DATE_RENDERER_POPUP
-
-IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateRenderer, wxDataViewRenderer)
-
-wxDataViewDateRenderer::wxDataViewDateRenderer( const wxString &varianttype,
-                        wxDataViewCellMode mode, int align ) :
-    wxDataViewRenderer( varianttype, mode, align )
-{
-}
-
-bool wxDataViewDateRenderer::SetValue( const wxVariant &value )
-{
-    m_date = value.GetDateTime();
-
-    return true;
-}
-
-bool wxDataViewDateRenderer::GetValue( wxVariant &value ) const
-{
-    value = m_date;
-    return true;
-}
-
-bool wxDataViewDateRenderer::Render( wxRect cell, wxDC *dc, int state )
-{
-    wxString tmp = m_date.FormatDate();
-    RenderText( tmp, 0, cell, dc, state );
-    return true;
-}
-
-wxSize wxDataViewDateRenderer::GetSize() const
-{
-    return GetTextExtent(m_date.FormatDate());
-}
-
-bool wxDataViewDateRenderer::WXOnActivate(const wxRect& WXUNUSED(cell),
-                                          wxDataViewModel *model,
-                                          const wxDataViewItem& item,
-                                          unsigned int col)
-{
-    wxDateTime dtOld = m_date;
-
-#if wxUSE_DATE_RENDERER_POPUP
-    wxDataViewDateRendererPopupTransient *popup = new wxDataViewDateRendererPopupTransient(
-        GetOwner()->GetOwner()->GetParent(), &dtOld, model, item, col);
-    wxPoint pos = wxGetMousePosition();
-    popup->Move( pos );
-    popup->Layout();
-    popup->Popup( popup->m_cal );
-#else // !wxUSE_DATE_RENDERER_POPUP
-    wxMessageBox(dtOld.Format());
-#endif // wxUSE_DATE_RENDERER_POPUP/!wxUSE_DATE_RENDERER_POPUP
-
-    return true;
-}
-
 // ---------------------------------------------------------
 // wxDataViewIconTextRenderer
 // ---------------------------------------------------------
@@ -1269,7 +1189,17 @@ bool wxDataViewIconTextRenderer::GetValueFromEditorCtrl( wxWindow *editor, wxVar
 {
     wxTextCtrl *text = (wxTextCtrl*) editor;
 
-    wxDataViewIconText iconText(text->GetValue(), m_value.GetIcon());
+    // The icon can't be edited so get its old value and reuse it.
+    wxVariant valueOld;
+    wxDataViewColumn* const col = GetOwner();
+    GetView()->GetModel()->GetValue(valueOld, m_item, col->GetModelColumn());
+
+    wxDataViewIconText iconText;
+    iconText << valueOld;
+
+    // But replace the text with the value entered by user.
+    iconText.SetText(text->GetValue());
+
     value << iconText;
     return true;
 }
@@ -1436,6 +1366,7 @@ BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
     EVT_MOUSE_EVENTS  (wxDataViewMainWindow::OnMouse)
     EVT_SET_FOCUS     (wxDataViewMainWindow::OnSetFocus)
     EVT_KILL_FOCUS    (wxDataViewMainWindow::OnKillFocus)
+    EVT_CHAR_HOOK     (wxDataViewMainWindow::OnCharHook)
     EVT_CHAR          (wxDataViewMainWindow::OnChar)
 END_EVENT_TABLE()
 
@@ -1447,6 +1378,8 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i
 {
     SetOwner( parent );
 
+    m_editorRenderer = NULL;
+
     m_lastOnSame = false;
     m_renameTimer = new wxDataViewRenameTimer( this );
 
@@ -1456,7 +1389,14 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i
     m_useCellFocus = false;
     m_currentRow = 0;
 
-    m_lineHeight = wxMax( 17, GetCharHeight() + 2 ); // 17 = mini icon height + 1
+#ifdef __WXMSW__
+    // We would like to use the same line height that Explorer uses. This is
+    // different from standard ListView control since Vista.
+    if ( wxGetWinVersion() >= wxWinVersion_Vista )
+        m_lineHeight = wxMax(16, GetCharHeight()) + 6; // 16 = mini icon height
+    else
+#endif // __WXMSW__
+        m_lineHeight = wxMax(16, GetCharHeight()) + 1; // 16 = mini icon height
 
 #if wxUSE_DRAG_AND_DROP
     m_dragCount = 0;
@@ -1552,6 +1492,7 @@ wxDragResult wxDataViewMainWindow::OnDragOver( wxDataFormat format, wxCoord x,
     event.SetItem( item );
     event.SetModel( model );
     event.SetDataFormat( format );
+    event.SetDropEffect( def );
     if (!m_owner->HandleWindowEvent( event ))
     {
         RemoveDropHint();
@@ -1628,6 +1569,7 @@ wxDragResult wxDataViewMainWindow::OnData( wxDataFormat format, wxCoord x, wxCoo
     event.SetDataFormat( format );
     event.SetDataSize( obj->GetSize() );
     event.SetDataBuffer( obj->GetData() );
+    event.SetDropEffect( def );
     if (!m_owner->HandleWindowEvent( event ))
         return wxDragNone;
 
@@ -1824,6 +1766,37 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
         x_last += col->GetWidth();
     }
 
+    // Draw background of alternate rows specially if required
+    if ( m_owner->HasFlag(wxDV_ROW_LINES) )
+    {
+        wxColour altRowColour = m_owner->m_alternateRowColour;
+        if ( !altRowColour.IsOk() )
+        {
+            // Determine the alternate rows colour automatically from the
+            // background colour.
+            const wxColour bgColour = m_owner->GetBackgroundColour();
+
+            // Depending on the background, alternate row color
+            // will be 3% more dark or 50% brighter.
+            int alpha = bgColour.GetRGB() > 0x808080 ? 97 : 150;
+            altRowColour = bgColour.ChangeLightness(alpha);
+        }
+
+        dc.SetPen(*wxTRANSPARENT_PEN);
+        dc.SetBrush(wxBrush(altRowColour));
+
+        for (unsigned int item = item_start; item < item_last; item++)
+        {
+            if ( item % 2 )
+            {
+                dc.DrawRectangle(x_start,
+                                 GetLineStart(item),
+                                 GetClientSize().GetWidth(),
+                                 GetLineHeight(item));
+            }
+        }
+    }
+
     // Draw horizontal rules if required
     if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
     {
@@ -2018,6 +1991,11 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
             cell_rect.y = GetLineStart( item );
             cell_rect.height = GetLineHeight( item );
 
+            // draw the background
+            bool selected = m_selection.Index( item ) != wxNOT_FOUND;
+            if ( !selected )
+                DrawCellBackground( cell, dc, cell_rect );
+
             // deal with the expander
             int indent = 0;
             if ((!IsList()) && (col == expander))
@@ -2071,7 +2049,7 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
                 continue;
 
             int state = 0;
-            if (m_hasFocus && (m_selection.Index(item) != wxNOT_FOUND))
+            if (m_hasFocus && selected)
                 state |= wxDATAVIEW_CELL_SELECTED;
 
             // TODO: it would be much more efficient to create a clipping
@@ -2090,6 +2068,28 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
     }
 }
 
+
+void wxDataViewMainWindow::DrawCellBackground( wxDataViewRenderer* cell, wxDC& dc, const wxRect& rect )
+{
+    wxRect rectBg( rect );
+
+    // don't overlap the horizontal rules
+    if ( m_owner->HasFlag(wxDV_HORIZ_RULES) )
+    {
+        rectBg.x++;
+        rectBg.width--;
+    }
+
+    // don't overlap the vertical rules
+    if ( m_owner->HasFlag(wxDV_VERT_RULES) )
+    {
+        rectBg.y++;
+        rectBg.height--;
+    }
+
+    cell->RenderBackground(&dc, rectBg);
+}
+
 void wxDataViewMainWindow::OnRenameTimer()
 {
     // We have to call this here because changes may just have
@@ -2103,9 +2103,25 @@ void wxDataViewMainWindow::OnRenameTimer()
 
     wxDataViewItem item = GetItemByRow( m_currentRow );
 
-    wxRect labelRect = GetItemRect(item, m_currentCol);
+    StartEditing( item, m_currentCol );
+}
+
+void
+wxDataViewMainWindow::StartEditing(const wxDataViewItem& item,
+                                   const wxDataViewColumn* col)
+{
+    wxDataViewRenderer* renderer = col->GetRenderer();
+    if ( !IsCellEditableInMode(item, col, wxDATAVIEW_CELL_EDITABLE) )
+        return;
 
-    m_currentCol->GetRenderer()->StartEditing( item, labelRect );
+    const wxRect itemRect = GetItemRect(item, col);
+    if ( renderer->StartEditing(item, itemRect) )
+    {
+        // Save the renderer to be able to finish/cancel editing it later and
+        // save the control to be able to detect if we're still editing it.
+        m_editorRenderer = renderer;
+        m_editorCtrl = renderer->GetEditorCtrl();
+    }
 }
 
 //-----------------------------------------------------------------------------
@@ -2175,17 +2191,59 @@ bool wxDataViewMainWindow::ItemAdded(const wxDataViewItem & parent, const wxData
         if ( !parentNode )
             return false;
 
-        wxDataViewItemArray siblings;
-        GetModel()->GetChildren(parent, siblings);
-        int itemPos = siblings.Index(item, /*fromEnd=*/true);
-        wxCHECK_MSG( itemPos != wxNOT_FOUND, false, "adding non-existent item?" );
+        wxDataViewItemArray modelSiblings;
+        GetModel()->GetChildren(parent, modelSiblings);
+        const int modelSiblingsSize = modelSiblings.size();
+
+        int posInModel = modelSiblings.Index(item, /*fromEnd=*/true);
+        wxCHECK_MSG( posInModel != wxNOT_FOUND, false, "adding non-existent item?" );
 
         wxDataViewTreeNode *itemNode = new wxDataViewTreeNode(parentNode, item);
         itemNode->SetHasChildren(GetModel()->IsContainer(item));
 
         parentNode->SetHasChildren(true);
-        parentNode->InsertChild(itemNode, itemPos);
+
+        const wxDataViewTreeNodes& nodeSiblings = parentNode->GetChildNodes();
+        const int nodeSiblingsSize = nodeSiblings.size();
+
+        int nodePos = 0;
+
+        if ( posInModel == modelSiblingsSize - 1 )
+        {
+            nodePos = nodeSiblingsSize;
+        }
+        else if ( modelSiblingsSize == nodeSiblingsSize + 1 )
+        {
+            // This is the simple case when our node tree already matches the
+            // model and only this one item is missing.
+            nodePos = posInModel;
+        }
+        else
+        {
+            // It's possible that a larger discrepancy between the model and
+            // our realization exists. This can happen e.g. when adding a bunch
+            // of items to the model and then calling ItemsAdded() just once
+            // afterwards. In this case, we must find the right position by
+            // looking at sibling items.
+
+            // append to the end if we won't find a better position:
+            nodePos = nodeSiblingsSize;
+
+            for ( int nextItemPos = posInModel + 1;
+                  nextItemPos < modelSiblingsSize;
+                  nextItemPos++ )
+            {
+                int nextNodePos = parentNode->FindChildByItem(modelSiblings[nextItemPos]);
+                if ( nextNodePos != wxNOT_FOUND )
+                {
+                    nodePos = nextNodePos;
+                    break;
+                }
+            }
+        }
+
         parentNode->ChangeSubTreeCount(+1);
+        parentNode->InsertChild(itemNode, nodePos);
 
         m_count = -1;
     }
@@ -2234,7 +2292,7 @@ bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
         // collapsed) item in a tree model. So it's not an error if we don't know
         // about this item, just return without doing anything then.
         if ( !parentNode )
-            return false;
+            return true;
 
         wxCHECK_MSG( parentNode->HasChildren(), false, "parent node doesn't have children?" );
         const wxDataViewTreeNodes& parentsChildren = parentNode->GetChildNodes();
@@ -2265,7 +2323,7 @@ bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
             if ( parentNode->GetChildNodes().empty() )
                 parentNode->SetHasChildren(GetModel()->IsContainer(parent));
 
-            return false;
+            return true;
         }
 
         // Delete the item from wxDataViewTreeNode representation:
@@ -2281,7 +2339,17 @@ bool wxDataViewMainWindow::ItemDeleted(const wxDataViewItem& parent,
         // If this was the last child to be removed, it's possible the parent
         // node became a leaf. Let's ask the model about it.
         if ( parentNode->GetChildNodes().empty() )
-            parentNode->SetHasChildren(GetModel()->IsContainer(parent));
+        {
+            bool isContainer = GetModel()->IsContainer(parent);
+            parentNode->SetHasChildren(isContainer);
+            if ( isContainer )
+            {
+                // If it's still a container, make sure we show "+" icon for it
+                // and not "-" one as there is nothing to collapse any more.
+                if ( parentNode->IsOpen() )
+                    parentNode->ToggleOpen();
+            }
+        }
 
         // Update selection by removing 'item' and its entire children tree from the selection.
         if ( !m_selection.empty() )
@@ -2458,7 +2526,7 @@ void wxDataViewMainWindow::ScrollTo( int rows, int column )
     int x, y;
     m_owner->GetScrollPixelsPerUnit( &x, &y );
     int sy = GetLineStart( rows )/y;
-    int sx = 0;
+    int sx = -1;
     if( column != -1 )
     {
         wxRect rect = GetClientRect();
@@ -2532,12 +2600,14 @@ unsigned int wxDataViewMainWindow::GetLastVisibleRow()
     return wxMin( GetRowCount()-1, row );
 }
 
-unsigned int wxDataViewMainWindow::GetRowCount()
+unsigned int wxDataViewMainWindow::GetRowCount() const
 {
     if ( m_count == -1 )
     {
-        m_count = RecalculateCount();
-        UpdateDisplay();
+        wxDataViewMainWindow* const
+            self = const_cast<wxDataViewMainWindow*>(this);
+        self->m_count = RecalculateCount();
+        self->UpdateDisplay();
     }
     return m_count;
 }
@@ -2924,15 +2994,20 @@ wxDataViewTreeNode * wxDataViewMainWindow::GetTreeNodeByRow(unsigned int row) co
 
 wxDataViewItem wxDataViewMainWindow::GetItemByRow(unsigned int row) const
 {
+    wxDataViewItem item;
     if (IsVirtualList())
     {
-        return wxDataViewItem( wxUIntToPtr(row+1) );
+        if ( row < GetRowCount() )
+            item = wxDataViewItem(wxUIntToPtr(row+1));
     }
     else
     {
         wxDataViewTreeNode *node = GetTreeNodeByRow(row);
-        return node ? node->GetItem() : wxDataViewItem();
+        if ( node )
+            item = node->GetItem();
     }
+
+    return item;
 }
 
 bool
@@ -3253,7 +3328,7 @@ wxRect wxDataViewMainWindow::GetItemRect( const wxDataViewItem & item,
     return itemRect;
 }
 
-int wxDataViewMainWindow::RecalculateCount()
+int wxDataViewMainWindow::RecalculateCount() const
 {
     if (IsVirtualList())
     {
@@ -3409,7 +3484,7 @@ wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataVie
     wxDataViewColumn *candidate = m_currentCol;
 
     if ( candidate &&
-         candidate->GetRenderer()->GetMode() != mode &&
+         !IsCellEditableInMode(item, candidate, mode) &&
          !m_currentColSetByKeyboard )
     {
         // If current column was set by mouse to something not editable (in
@@ -3433,7 +3508,7 @@ wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataVie
             if ( c->IsHidden() )
                 continue;
 
-            if ( c->GetRenderer()->GetMode() == mode )
+            if ( IsCellEditableInMode(item, c, mode) )
             {
                 candidate = c;
                 break;
@@ -3454,12 +3529,46 @@ wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataVie
     if ( !candidate )
        return NULL;
 
-   if ( candidate->GetRenderer()->GetMode() != mode )
+   if ( !IsCellEditableInMode(item, candidate, mode) )
        return NULL;
 
    return candidate;
 }
 
+bool wxDataViewMainWindow::IsCellEditableInMode(const wxDataViewItem& item,
+                                                const wxDataViewColumn *col,
+                                                wxDataViewCellMode mode) const
+{
+    if ( col->GetRenderer()->GetMode() != mode )
+        return false;
+
+    if ( !GetModel()->IsEnabled(item, col->GetModelColumn()) )
+        return false;
+
+    return true;
+}
+
+void wxDataViewMainWindow::OnCharHook(wxKeyEvent& event)
+{
+    if ( m_editorCtrl )
+    {
+        // Handle any keys special for the in-place editor and return without
+        // calling Skip() below.
+        switch ( event.GetKeyCode() )
+        {
+            case WXK_ESCAPE:
+                m_editorRenderer->CancelEditing();
+                return;
+
+            case WXK_RETURN:
+                m_editorRenderer->FinishEditing();
+                return;
+        }
+    }
+
+    event.Skip();
+}
+
 void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
 {
     wxWindow * const parent = GetParent();
@@ -3487,10 +3596,16 @@ void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
     switch ( event.GetKeyCode() )
     {
         case WXK_RETURN:
+            if ( event.HasModifiers() )
+            {
+                event.Skip();
+                break;
+            }
+            else
             {
                 // Enter activates the item, i.e. sends wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED to
                 // it. Only if that event is not handled do we activate column renderer (which
-                // is normally done by Space).
+                // is normally done by Space) or even inline editing.
 
                 const wxDataViewItem item = GetItemByRow(m_currentRow);
 
@@ -3506,7 +3621,18 @@ void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
             }
 
         case WXK_SPACE:
+            if ( event.HasModifiers() )
+            {
+                event.Skip();
+                break;
+            }
+            else
             {
+                // Space toggles activatable items or -- if not activatable --
+                // starts inline editing (this is normally done using F2 on
+                // Windows, but Space is common everywhere else, so use it too
+                // for greater cross-platform compatibility).
+
                 const wxDataViewItem item = GetItemByRow(m_currentRow);
 
                 // Activate the current activatable column. If not column is focused (typically
@@ -3522,7 +3648,42 @@ void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
 
                     wxDataViewRenderer *cell = activatableCol->GetRenderer();
                     cell->PrepareForItem(GetModel(), item, colIdx);
-                    cell->WXOnActivate(cell_rect, GetModel(), item, colIdx);
+                    cell->WXActivateCell(cell_rect, GetModel(), item, colIdx, NULL);
+
+                    break;
+                }
+                // else: fall through to WXK_F2 handling
+            }
+
+        case WXK_F2:
+            if ( event.HasModifiers() )
+            {
+                event.Skip();
+                break;
+            }
+            else
+            {
+                if( !m_selection.empty() )
+                {
+                    // Mimic Windows 7 behavior: edit the item that has focus
+                    // if it is selected and the first selected item if focus
+                    // is out of selection.
+                    int sel;
+                    if ( m_selection.Index(m_currentRow) != wxNOT_FOUND )
+                        sel = m_currentRow;
+                    else
+                        sel = m_selection[0];
+
+
+                    const wxDataViewItem item = GetItemByRow(sel);
+
+                    // Edit the current column. If no column is focused
+                    // (typically because the user has full row selected), try
+                    // to find the first editable column.
+                    wxDataViewColumn *editableCol = FindColumnForEditing(item, wxDATAVIEW_CELL_EDITABLE);
+
+                    if ( editableCol )
+                        GetOwner()->EditItem(item, editableCol);
                 }
             }
             break;
@@ -3579,33 +3740,6 @@ void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
             }
             break;
 
-        case WXK_F2:
-            {
-                if( !m_selection.empty() )
-                {
-                    // Mimic Windows 7 behavior: edit the item that has focus
-                    // if it is selected and the first selected item if focus
-                    // is out of selection.
-                    int sel;
-                    if ( m_selection.Index(m_currentRow) != wxNOT_FOUND )
-                        sel = m_currentRow;
-                    else
-                        sel = m_selection[0];
-
-
-                    const wxDataViewItem item = GetItemByRow(sel);
-
-                    // Edit the current column. If not column is focused
-                    // (typically because the user has full row selected), try
-                    // to find the first editable column.
-                    wxDataViewColumn *editableCol = FindColumnForEditing(item, wxDATAVIEW_CELL_EDITABLE);
-
-                    if ( editableCol )
-                        GetOwner()->StartEditor(item, GetOwner()->GetColumnIndex(editableCol));
-                }
-            }
-            break;
-
         default:
             event.Skip();
     }
@@ -3772,6 +3906,8 @@ bool wxDataViewMainWindow::TryAdvanceCurrentColumn(wxDataViewTreeNode *node, boo
     if ( idx >= (int)GetOwner()->GetColumnCount() )
         return false;
 
+    GetOwner()->EnsureVisible(m_currentRow, idx);
+
     if ( idx < 1 )
     {
         // We are going to the left of the second column. Reset to whole-row
@@ -3961,7 +4097,7 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
 
             wxDataViewDropSource drag( this, drag_item_row );
             drag.SetData( *obj );
-            /* wxDragResult res = */ drag.DoDragDrop();
+            /* wxDragResult res = */ drag.DoDragDrop(event.GetDragFlags());
             delete obj;
         }
         return;
@@ -3994,31 +4130,15 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
         }
         else if ( current == m_lineLastClicked )
         {
-            bool activated = false;
-
-            if ((!ignore_other_columns) && (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE))
-            {
-                const unsigned colIdx = col->GetModelColumn();
-
-                cell->PrepareForItem(model, item, colIdx);
-
-                wxRect cell_rect( xpos, GetLineStart( current ),
-                                col->GetWidth(), GetLineHeight( current ) );
-                activated = cell->WXOnActivate( cell_rect, model, item, colIdx );
-            }
-
-            if ( !activated )
-            {
-                wxWindow *parent = GetParent();
-                wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
-                le.SetItem( item );
-                le.SetColumn( col->GetModelColumn() );
-                le.SetDataViewColumn( col );
-                le.SetEventObject(parent);
-                le.SetModel(GetModel());
+            wxWindow *parent = GetParent();
+            wxDataViewEvent le(wxEVT_COMMAND_DATAVIEW_ITEM_ACTIVATED, parent->GetId());
+            le.SetItem( item );
+            le.SetColumn( col->GetModelColumn() );
+            le.SetDataViewColumn( col );
+            le.SetEventObject(parent);
+            le.SetModel(GetModel());
 
-                parent->ProcessWindowEvent(le);
-            }
+            parent->ProcessWindowEvent(le);
             return;
         }
         else
@@ -4044,7 +4164,7 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
         if (m_lastOnSame && !ignore_other_columns)
         {
             if ((col == m_currentCol) && (current == m_currentRow) &&
-                (cell->GetMode() & wxDATAVIEW_CELL_EDITABLE) )
+                IsCellEditableInMode(item, col, wxDATAVIEW_CELL_EDITABLE) )
             {
                 m_renameTimer->Start( 100, true );
             }
@@ -4162,8 +4282,8 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
         m_lastOnSame = !simulateClick && ((col == oldCurrentCol) &&
                         (current == oldCurrentRow)) && oldWasSelected;
 
-        // Call LeftClick after everything else as under GTK+
-        if (cell->GetMode() & wxDATAVIEW_CELL_ACTIVATABLE)
+        // Call ActivateCell() after everything else as under GTK+
+        if ( IsCellEditableInMode(item, col, wxDATAVIEW_CELL_ACTIVATABLE) )
         {
             // notify cell about click
             cell->PrepareForItem(model, item, col->GetModelColumn());
@@ -4203,14 +4323,19 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
                 }
             }
 
-            wxPoint pos( event.GetPosition() );
-            pos.x -= rectItem.x;
-            pos.y -= rectItem.y;
-
-            m_owner->CalcUnscrolledPosition( pos.x, pos.y, &pos.x, &pos.y );
+            wxMouseEvent event2(event);
+            event2.m_x -= rectItem.x;
+            event2.m_y -= rectItem.y;
+            m_owner->CalcUnscrolledPosition(event2.m_x, event2.m_y, &event2.m_x, &event2.m_y);
 
-             /* ignore ret */ cell->WXOnLeftClick( pos, cell_rect,
-                              model, item, col->GetModelColumn());
+             /* ignore ret */ cell->WXActivateCell
+                                    (
+                                        cell_rect,
+                                        model,
+                                        item,
+                                        col->GetModelColumn(),
+                                        &event2
+                                    );
         }
     }
 }
@@ -4434,7 +4559,7 @@ bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
         return false;
 
     m_cols.Append( col );
-    m_colsBestWidths.push_back(0);
+    m_colsBestWidths.push_back(CachedColWidthInfo());
     OnColumnsCountChanged();
     return true;
 }
@@ -4445,7 +4570,7 @@ bool wxDataViewCtrl::PrependColumn( wxDataViewColumn *col )
         return false;
 
     m_cols.Insert( col );
-    m_colsBestWidths.insert(m_colsBestWidths.begin(), 0);
+    m_colsBestWidths.insert(m_colsBestWidths.begin(), CachedColWidthInfo());
     OnColumnsCountChanged();
     return true;
 }
@@ -4456,7 +4581,7 @@ bool wxDataViewCtrl::InsertColumn( unsigned int pos, wxDataViewColumn *col )
         return false;
 
     m_cols.Insert( pos, col );
-    m_colsBestWidths.insert(m_colsBestWidths.begin() + pos, 0);
+    m_colsBestWidths.insert(m_colsBestWidths.begin() + pos, CachedColWidthInfo());
     OnColumnsCountChanged();
     return true;
 }
@@ -4531,8 +4656,8 @@ int wxDataViewCtrl::GetColumnIndex(const wxDataViewColumn *column) const
 
 unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
 {
-    if ( m_colsBestWidths[idx] != 0 )
-        return m_colsBestWidths[idx];
+    if ( m_colsBestWidths[idx].width != 0 )
+        return m_colsBestWidths[idx].width;
 
     const int count = m_clientArea->GetRowCount();
     wxDataViewColumn *column = GetColumn(idx);
@@ -4542,16 +4667,25 @@ unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
     class MaxWidthCalculator
     {
     public:
-        MaxWidthCalculator(wxDataViewMainWindow *clientArea,
+        MaxWidthCalculator(const wxDataViewCtrl *dvc,
+                           wxDataViewMainWindow *clientArea,
                            wxDataViewRenderer *renderer,
                            const wxDataViewModel *model,
-                           unsigned column)
+                           unsigned column,
+                           int expanderSize)
             : m_width(0),
+              m_dvc(dvc),
               m_clientArea(clientArea),
               m_renderer(renderer),
               m_model(model),
-              m_column(column)
+              m_column(column),
+              m_expanderSize(expanderSize)
+
         {
+            m_isExpanderCol =
+                !clientArea->IsList() &&
+                (column == 0 ||
+                 GetExpanderColumnOrFirstOne(const_cast<wxDataViewCtrl*>(dvc)) == dvc->GetColumnAt(column));
         }
 
         void UpdateWithWidth(int width)
@@ -4561,32 +4695,45 @@ unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
 
         void UpdateWithRow(int row)
         {
-            wxDataViewItem item = m_clientArea->GetItemByRow(row);
+            int indent = 0;
+            wxDataViewItem item;
+
+            if ( m_isExpanderCol )
+            {
+                wxDataViewTreeNode *node = m_clientArea->GetTreeNodeByRow(row);
+                item = node->GetItem();
+                indent = m_dvc->GetIndent() * node->GetIndentLevel() + m_expanderSize;
+            }
+            else
+            {
+                item = m_clientArea->GetItemByRow(row);
+            }
+
             m_renderer->PrepareForItem(m_model, item, m_column);
-            m_width = wxMax(m_width, m_renderer->GetSize().x);
+            m_width = wxMax(m_width, m_renderer->GetSize().x + indent);
         }
 
         int GetMaxWidth() const { return m_width; }
 
     private:
         int m_width;
+        const wxDataViewCtrl *m_dvc;
         wxDataViewMainWindow *m_clientArea;
         wxDataViewRenderer *m_renderer;
         const wxDataViewModel *m_model;
         unsigned m_column;
+        bool m_isExpanderCol;
+        int m_expanderSize;
     };
 
-    MaxWidthCalculator calculator(m_clientArea, renderer,
-                                  GetModel(), column->GetModelColumn());
+    MaxWidthCalculator calculator(this, m_clientArea, renderer,
+                                  GetModel(), column->GetModelColumn(),
+                                  m_clientArea->GetRowHeight());
+
+    calculator.UpdateWithWidth(column->GetMinWidth());
 
     if ( m_headerArea )
-    {
-        int header_width = m_headerArea->GetTextExtent(column->GetTitle()).x;
-        // Labels on native MSW header are indented on both sides
-        header_width +=
-            wxRendererNative::Get().GetHeaderButtonMargin(m_headerArea);
-        calculator.UpdateWithWidth(header_width);
-    }
+        calculator.UpdateWithWidth(m_headerArea->GetColumnTitleWidth(*column));
 
     // The code below deserves some explanation. For very large controls, we
     // simply can't afford to calculate sizes for all items, it takes too
@@ -4660,7 +4807,7 @@ unsigned int wxDataViewCtrl::GetBestColumnWidth(int idx) const
     if ( max_width > 0 )
         max_width += 2 * PADDING_RIGHTLEFT;
 
-    const_cast<wxDataViewCtrl*>(this)->m_colsBestWidths[idx] = max_width;
+    const_cast<wxDataViewCtrl*>(this)->m_colsBestWidths[idx].width = max_width;
     return max_width;
 }
 
@@ -4681,6 +4828,10 @@ bool wxDataViewCtrl::DeleteColumn( wxDataViewColumn *column )
 
     m_colsBestWidths.erase(m_colsBestWidths.begin() + GetColumnIndex(column));
     m_cols.Erase(ret);
+
+    if ( m_clientArea->GetCurrentColumn() == column )
+        m_clientArea->ClearCurrentColumn();
+
     OnColumnsCountChanged();
 
     return true;
@@ -4691,18 +4842,24 @@ bool wxDataViewCtrl::ClearColumns()
     SetExpanderColumn(NULL);
     m_cols.Clear();
     m_colsBestWidths.clear();
+
+    m_clientArea->ClearCurrentColumn();
+
     OnColumnsCountChanged();
+
     return true;
 }
 
 void wxDataViewCtrl::InvalidateColBestWidth(int idx)
 {
-    m_colsBestWidths[idx] = 0;
+    m_colsBestWidths[idx].width = 0;
+    m_colsBestWidths[idx].dirty = true;
     m_colsDirty = true;
 }
 
 void wxDataViewCtrl::InvalidateColBestWidths()
 {
+    // mark all columns as dirty:
     m_colsBestWidths.clear();
     m_colsBestWidths.resize(m_cols.size());
     m_colsDirty = true;
@@ -4710,14 +4867,27 @@ void wxDataViewCtrl::InvalidateColBestWidths()
 
 void wxDataViewCtrl::UpdateColWidths()
 {
+    m_colsDirty = false;
+
     if ( !m_headerArea )
         return;
 
     const unsigned len = m_colsBestWidths.size();
     for ( unsigned i = 0; i < len; i++ )
     {
-        if ( m_colsBestWidths[i] == 0 )
+        // Note that we have to have an explicit 'dirty' flag here instead of
+        // checking if the width==0, as is done in GetBestColumnWidth().
+        //
+        // Testing width==0 wouldn't work correctly if some code called
+        // GetWidth() after col. width invalidation but before
+        // wxDataViewCtrl::UpdateColWidths() was called at idle time. This
+        // would result in the header's column width getting out of sync with
+        // the control itself.
+        if ( m_colsBestWidths[i].dirty )
+        {
             m_headerArea->UpdateColumn(i);
+            m_colsBestWidths[i].dirty = false;
+        }
     }
 }
 
@@ -4726,15 +4896,11 @@ void wxDataViewCtrl::OnInternalIdle()
     wxDataViewCtrlBase::OnInternalIdle();
 
     if ( m_colsDirty )
-    {
-        m_colsDirty = false;
         UpdateColWidths();
-    }
 }
 
 int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
 {
-#if 1
     unsigned int len = GetColumnCount();
     for ( unsigned int i = 0; i < len; i++ )
     {
@@ -4744,25 +4910,6 @@ int wxDataViewCtrl::GetColumnPosition( const wxDataViewColumn *column ) const
     }
 
     return wxNOT_FOUND;
-#else
-    // This returns the position in pixels which is not what we want.
-    int ret = 0,
-        dummy = 0;
-    unsigned int len = GetColumnCount();
-    for ( unsigned int i = 0; i < len; i++ )
-    {
-        wxDataViewColumn * col = GetColumnAt(i);
-        if (col->IsHidden())
-            continue;
-        ret += col->GetWidth();
-        if (column==col)
-        {
-            CalcScrolledPosition( ret, dummy, &ret, &dummy );
-            break;
-        }
-    }
-    return ret;
-#endif
 }
 
 wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const
@@ -4789,6 +4936,11 @@ void wxDataViewCtrl::DoSetCurrentItem(const wxDataViewItem& item)
     }
 }
 
+wxDataViewColumn *wxDataViewCtrl::GetCurrentColumn() const
+{
+    return m_clientArea->GetCurrentColumn();
+}
+
 int wxDataViewCtrl::GetSelectedItemsCount() const
 {
     return m_clientArea->GetSelections().size();
@@ -4877,6 +5029,11 @@ bool wxDataViewCtrl::IsSelected( const wxDataViewItem & item ) const
     return false;
 }
 
+void wxDataViewCtrl::SetAlternateRowColour(const wxColour& colour)
+{
+    m_alternateRowColour = colour;
+}
+
 void wxDataViewCtrl::SelectAll()
 {
     m_clientArea->SelectAllRows(true);
@@ -4949,14 +5106,20 @@ void wxDataViewCtrl::Expand( const wxDataViewItem & item )
 
     int row = m_clientArea->GetRowByItem( item );
     if (row != -1)
+    {
         m_clientArea->Expand(row);
+        InvalidateColBestWidths();
+    }
 }
 
 void wxDataViewCtrl::Collapse( const wxDataViewItem & item )
 {
     int row = m_clientArea->GetRowByItem( item );
     if (row != -1)
+    {
         m_clientArea->Collapse(row);
+        InvalidateColBestWidths();
+    }
 }
 
 bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
@@ -4967,18 +5130,12 @@ bool wxDataViewCtrl::IsExpanded( const wxDataViewItem & item ) const
     return false;
 }
 
-void wxDataViewCtrl::StartEditor( const wxDataViewItem & item, unsigned int column )
+void wxDataViewCtrl::EditItem(const wxDataViewItem& item, const wxDataViewColumn *column)
 {
-    wxDataViewColumn* col = GetColumn( column );
-    if (!col)
-        return;
+    wxCHECK_RET( item.IsOk(), "invalid item" );
+    wxCHECK_RET( column, "no column provided" );
 
-    wxDataViewRenderer* renderer = col->GetRenderer();
-    if (renderer->GetMode() != wxDATAVIEW_CELL_EDITABLE)
-        return;
-
-    const wxRect itemRect = GetItemRect(item, col);
-    renderer->StartEditing(item, itemRect);
+    m_clientArea->StartEditing(item, column);
 }
 
 #endif // !wxUSE_GENERICDATAVIEWCTRL