]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/datavgen.cpp
basic blitting implementation
[wxWidgets.git] / src / generic / datavgen.cpp
index 4e8b0fbb8d11c25ee2e57841094316f111638f9b..7d404f3e72f5d1e2795624ef217ad112cc09f4a4 100644 (file)
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
 #if wxUSE_DATAVIEWCTRL
 
 #include "wx/dataview.h"
 #ifdef wxUSE_GENERICDATAVIEWCTRL
 
 #ifndef WX_PRECOMP
+    #ifdef __WXMSW__
+        #include "wx/msw/wrapwin.h"
+    #endif
     #include "wx/sizer.h"
     #include "wx/log.h"
+    #include "wx/dcclient.h"
+    #include "wx/timer.h"
+    #include "wx/settings.h"
 #endif
 
 #include "wx/stockitem.h"
-#include "wx/dcclient.h"
 #include "wx/calctrl.h"
 #include "wx/popupwin.h"
 #include "wx/renderer.h"
-#include "wx/timer.h"
-
-#ifdef __WXMSW__
-    #include "wx/msw/wrapwin.h"
-#endif
 
 //-----------------------------------------------------------------------------
 // classes
@@ -50,7 +54,7 @@ public:
                             const wxPoint &pos = wxDefaultPosition,
                             const wxSize &size = wxDefaultSize,
                             const wxString &name = wxT("wxdataviewctrlheaderwindow") );
-    ~wxDataViewHeaderWindow();
+    virtual ~wxDataViewHeaderWindow();
 
     void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
     wxDataViewCtrl *GetOwner() { return m_owner; }
@@ -113,7 +117,7 @@ private:
     wxTextCtrl             *m_text;
     wxString                m_startValue;
     wxDataViewListModel    *m_model;
-    size_t                  m_col; 
+    size_t                  m_col;
     size_t                  m_row;
     bool                    m_finished;
     bool                    m_aboutToFinish;
@@ -125,6 +129,8 @@ private:
 // wxDataViewMainWindow
 //-----------------------------------------------------------------------------
 
+WX_DEFINE_SORTED_ARRAY_SIZE_T( size_t, wxDataViewSelection );
+
 class wxDataViewMainWindow: public wxWindow
 {
 public:
@@ -133,7 +139,7 @@ public:
                             const wxPoint &pos = wxDefaultPosition,
                             const wxSize &size = wxDefaultSize,
                             const wxString &name = wxT("wxdataviewctrlmainwindow") );
-    ~wxDataViewMainWindow();
+    virtual ~wxDataViewMainWindow();
 
     // notifications from wxDataViewListModel
     bool RowAppended();
@@ -149,8 +155,11 @@ public:
     wxDataViewCtrl *GetOwner() { return m_owner; }
 
     void OnPaint( wxPaintEvent &event );
+    void OnArrowChar(size_t newCurrent, const wxKeyEvent& event);
+    void OnChar( wxKeyEvent &event );
     void OnMouse( wxMouseEvent &event );
     void OnSetFocus( wxFocusEvent &event );
+    void OnKillFocus( wxFocusEvent &event );
 
     void UpdateDisplay();
     void RecalculateDisplay();
@@ -160,18 +169,52 @@ public:
     void FinishEditing( wxTextCtrl *text );
 
     void ScrollWindow( int dx, int dy, const wxRect *rect );
+
+    bool HasCurrentRow() { return m_currentRow != (size_t)-1; }
+    void ChangeCurrentRow( size_t row );
+
+    bool IsSingleSel() const { return !GetParent()->HasFlag(wxDV_MULTIPLE); };
+    bool IsEmpty() { return GetRowCount() == 0; }
+
+    int GetCountPerPage();
+    int GetEndOfLastCol();
+    size_t GetFirstVisibleRow();
+    size_t GetLastVisibleRow();
+    size_t GetRowCount();
+
+    void SelectAllRows( bool on );
+    void SelectRow( size_t row, bool on );
+    void SelectRows( size_t from, size_t to, bool on );
+    void ReverseRowSelection( size_t row );
+    bool IsRowSelected( size_t row );
+
+    void RefreshRow( size_t row );
+    void RefreshRows( size_t from, size_t to );
+    void RefreshRowsAfter( size_t firstRow );
+
 private:
     wxDataViewCtrl             *m_owner;
     int                         m_lineHeight;
     bool                        m_dirty;
-    
+
     wxDataViewColumn           *m_currentCol;
     size_t                      m_currentRow;
-    
+    wxDataViewSelection         m_selection;
+
     wxDataViewRenameTimer      *m_renameTimer;
     wxDataViewTextCtrlWrapper  *m_textctrlWrapper;
     bool                        m_lastOnSame;
 
+    bool                        m_hasFocus;
+
+    int                         m_dragCount;
+    wxPoint                     m_dragStart;
+
+    // for double click logic
+    size_t m_lineLastClicked,
+           m_lineBeforeLastClicked,
+           m_lineSelectSingleOnUp;
+
 private:
     DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
     DECLARE_EVENT_TABLE()
@@ -303,7 +346,7 @@ bool wxDataViewToggleCell::SetValue( const wxVariant &value )
 {
     m_toggle = value.GetBool();
 
-    return true;;
+    return true;
 }
 
 bool wxDataViewToggleCell::GetValue( wxVariant &WXUNUSED(value) )
@@ -320,14 +363,14 @@ bool wxDataViewToggleCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
     rect.width = 20;
     rect.y = cell.y + cell.height/2 - 10;
     rect.height = 20;
-    
+
     int flags = 0;
     if (m_toggle)
         flags |= wxCONTROL_CHECKED;
     if (GetMode() != wxDATAVIEW_CELL_ACTIVATABLE)
         flags |= wxCONTROL_DISABLED;
 
-    wxRendererNative::Get().DrawCheckButton(
+    wxRendererNative::Get().DrawCheckBox(
             GetOwner()->GetOwner(),
             *dc,
             rect,
@@ -420,10 +463,6 @@ public:
         sizer->Fit( this );
     }
 
-    virtual void OnDismiss()
-    {
-    }
-
     void OnCalendar( wxCalendarEvent &event );
 
     wxCalendarCtrl      *m_cal;
@@ -431,6 +470,11 @@ public:
     size_t               m_col;
     size_t               m_row;
 
+protected:
+    virtual void OnDismiss()
+    {
+    }
+
 private:
     DECLARE_EVENT_TABLE()
 };
@@ -503,11 +547,14 @@ bool wxDataViewDateCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *m
 
 IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
 
-wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewCell *cell,
-    size_t model_column, int flags ) :
+wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewCell *cell, size_t model_column,
+        int fixed_width, wxDataViewColumnSizing sizing, int flags ) :
     wxDataViewColumnBase( title, cell, model_column, flags )
 {
-    m_width = 80;
+    m_sizing = sizing;
+
+    m_width = fixed_width;
+    m_fixedWidth = fixed_width;
 }
 
 wxDataViewColumn::~wxDataViewColumn()
@@ -520,6 +567,27 @@ void wxDataViewColumn::SetTitle( const wxString &title )
 
 }
 
+int wxDataViewColumn::GetWidth()
+{
+    return m_width;
+}
+
+void wxDataViewColumn::SetFixedWidth( int width )
+{
+    m_fixedWidth = width;
+
+    if (m_sizing == wxDATAVIEW_COL_WIDTH_FIXED)
+    {
+        m_width = width;
+        // Set dirty
+    }
+}
+
+int wxDataViewColumn::GetFixedWidth()
+{
+    return m_fixedWidth;
+}
+
 //-----------------------------------------------------------------------------
 // wxDataViewHeaderWindow
 //-----------------------------------------------------------------------------
@@ -578,21 +646,14 @@ void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
         wxDataViewColumn *col = GetOwner()->GetColumn( i );
         int width = col->GetWidth();
 
-        // the width of the rect to draw: make it smaller to fit entirely
-        // inside the column rect
-#ifdef __WXMAC__
         int cw = width;
         int ch = h;
-#else
-        int cw = width - 2;
-        int ch = h - 2;
-#endif
 
         wxRendererNative::Get().DrawHeaderButton
                                 (
                                     this,
                                     dc,
-                                    wxRect(xpos, 0, cw, ch),
+                                    wxRect(xpos, 0, cw, ch-1),
                                     m_parent->IsEnabled() ? 0
                                                           : (int)wxCONTROL_DISABLED
                                 );
@@ -609,6 +670,7 @@ void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &WXUNUSED(event) )
 
 void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
 {
+    GetParent()->SetFocus();
     event.Skip();
 }
 
@@ -647,15 +709,15 @@ wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
     m_model = model;
     m_row = row;
     m_col = col;
-    m_text = text; 
-    
+    m_text = text;
+
     m_finished = false;
     m_aboutToFinish = false;
-    
+
     wxVariant value;
     model->GetValue( value, col, row );
     m_startValue = value.GetString();
-    
+
     m_owner->GetOwner()->CalcScrolledPosition(
         rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y );
 
@@ -663,7 +725,7 @@ wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
                     wxPoint(rectLabel.x-2,rectLabel.y-2),
                     wxSize(rectLabel.width+7,rectLabel.height+4) );
     m_text->SetFocus();
-                       
+
     m_text->PushEventHandler(this);
 }
 
@@ -726,7 +788,7 @@ void wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
         AcceptChanges();
         //if ( !AcceptChanges() )
         //    m_owner->OnRenameCancelled( m_itemEdited );
-        
+
         Finish();
     }
 
@@ -773,24 +835,36 @@ void wxDataViewTextCtrlWrapper::Finish()
 // wxDataViewMainWindow
 //-----------------------------------------------------------------------------
 
+int LINKAGEMODE wxDataViewSelectionCmp( size_t row1, size_t row2 )
+{
+    if (row1 > row2) return 1;
+    if (row1 == row2) return 0;
+    return -1;
+}
+
+
 IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
 
 BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
     EVT_PAINT         (wxDataViewMainWindow::OnPaint)
     EVT_MOUSE_EVENTS  (wxDataViewMainWindow::OnMouse)
     EVT_SET_FOCUS     (wxDataViewMainWindow::OnSetFocus)
+    EVT_KILL_FOCUS    (wxDataViewMainWindow::OnKillFocus)
+    EVT_CHAR          (wxDataViewMainWindow::OnChar)
 END_EVENT_TABLE()
 
 wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
     const wxPoint &pos, const wxSize &size, const wxString &name ) :
-    wxWindow( parent, id, pos, size, 0, name )
+    wxWindow( parent, id, pos, size, wxWANTS_CHARS, name ),
+    m_selection( wxDataViewSelectionCmp )
+
 {
     SetOwner( parent );
 
     m_lastOnSame = false;
     m_renameTimer = new wxDataViewRenameTimer( this );
     m_textctrlWrapper = NULL;
-    
+
     // TODO: user better initial values/nothing selected
     m_currentCol = NULL;
     m_currentRow = 0;
@@ -798,6 +872,16 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i
     // TODO: we need to calculate this smartly
     m_lineHeight = 20;
 
+    m_dragCount = 0;
+    m_dragStart = wxPoint(0,0);
+    m_lineLastClicked = (size_t) -1;
+    m_lineBeforeLastClicked = (size_t) -1;
+    m_lineSelectSingleOnUp = (size_t) -1;
+
+    m_hasFocus = false;
+
+    SetBackgroundColour( *wxWHITE );
+
     UpdateDisplay();
 }
 
@@ -829,7 +913,7 @@ void wxDataViewMainWindow::OnRenameTimer()
     wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl);
 
     wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
-    m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(), 
+    m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(),
         m_currentCol->GetModelColumn(), m_currentRow, labelRect );
 }
 
@@ -878,7 +962,7 @@ bool wxDataViewMainWindow::ValueChanged( size_t WXUNUSED(col), size_t row )
 bool wxDataViewMainWindow::RowsReordered( size_t *WXUNUSED(new_order) )
 {
     Refresh();
-    
+
     return true;
 }
 
@@ -948,9 +1032,50 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
 
     wxDataViewListModel *model = GetOwner()->GetModel();
 
-    size_t item_start = wxMax( 0, update.y / m_lineHeight );
-    size_t item_count = wxMin( (update.height / m_lineHeight) + 1, 
-                                (int)(model->GetNumberOfRows()-item_start) );
+    size_t item_start = wxMax( 0, (update.y / m_lineHeight) );
+    size_t item_count = wxMin( (int)(((update.y + update.height) / m_lineHeight) - item_start + 1),
+                               (int)(model->GetNumberOfRows()-item_start) );
+
+
+
+    size_t item;
+    for (item = item_start; item < item_start+item_count; item++)
+    {
+        if (m_selection.Index( item ) != wxNOT_FOUND)
+        {
+            int flags = wxCONTROL_SELECTED;
+            if (item == m_currentRow)
+                flags |= wxCONTROL_CURRENT;
+            if (m_hasFocus)
+                flags |= wxCONTROL_FOCUSED;
+            wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
+            wxRendererNative::Get().DrawItemSelectionRect
+                                (
+                                    this,
+                                    dc,
+                                    rect,
+                                    flags
+                                );
+        }
+        else
+        {
+            if (item == m_currentRow)
+            {
+                int flags = wxCONTROL_CURRENT;
+                if (m_hasFocus)
+                    flags |= wxCONTROL_FOCUSED;  // should have no effect
+                wxRect rect( 0, item*m_lineHeight+1, GetEndOfLastCol(), m_lineHeight-2 );
+                wxRendererNative::Get().DrawItemSelectionRect
+                                (
+                                    this,
+                                    dc,
+                                    rect,
+                                    flags
+                                );
+
+            }
+        }
+    }
 
     wxRect cell_rect;
     cell_rect.x = 0;
@@ -963,7 +1088,6 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
         wxDataViewCell *cell = col->GetCell();
         cell_rect.width = col->GetWidth();
 
-        size_t item;
         for (item = item_start; item < item_start+item_count; item++)
         {
             cell_rect.y = item*m_lineHeight;
@@ -989,8 +1113,317 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
     }
 }
 
+int wxDataViewMainWindow::GetCountPerPage()
+{
+    wxSize size = GetClientSize();
+    return size.y / m_lineHeight;
+}
+
+int wxDataViewMainWindow::GetEndOfLastCol()
+{
+    int width = 0;
+    size_t i;
+    for (i = 0; i < GetOwner()->GetNumberOfColumns(); i++)
+    {
+        wxDataViewColumn *c = GetOwner()->GetColumn( i );
+        width += c->GetWidth();
+    }
+    return width;
+}
+
+size_t wxDataViewMainWindow::GetFirstVisibleRow()
+{
+    int x = 0;
+    int y = 0;
+    m_owner->CalcUnscrolledPosition( x, y, &x, &y );
+
+    return y / m_lineHeight;
+}
+
+size_t wxDataViewMainWindow::GetLastVisibleRow()
+{
+    wxSize client_size = GetClientSize();
+    m_owner->CalcUnscrolledPosition( client_size.x, client_size.y, &client_size.x, &client_size.y );
+
+    return wxMin( GetRowCount()-1, ((unsigned)client_size.y/m_lineHeight)+1 );
+}
+
+size_t wxDataViewMainWindow::GetRowCount()
+{
+    return GetOwner()->GetModel()->GetNumberOfRows();
+}
+
+void wxDataViewMainWindow::ChangeCurrentRow( size_t row )
+{
+    m_currentRow = row;
+
+    // send event
+}
+
+void wxDataViewMainWindow::SelectAllRows( bool on )
+{
+    if (IsEmpty())
+        return;
+
+    if (on)
+    {
+        m_selection.Clear();
+        for (size_t i = 0; i < GetRowCount(); i++)
+            m_selection.Add( i );
+        Refresh();
+    }
+    else
+    {
+        size_t first_visible = GetFirstVisibleRow();
+        size_t last_visible = GetLastVisibleRow();
+        size_t i;
+        for (i = 0; i < m_selection.GetCount(); i++)
+        {
+            size_t row = m_selection[i];
+            if ((row >= first_visible) && (row <= last_visible))
+                RefreshRow( row );
+        }
+        m_selection.Clear();
+    }
+}
+
+void wxDataViewMainWindow::SelectRow( size_t row, bool on )
+{
+    if (m_selection.Index( row ) == wxNOT_FOUND)
+    {
+        if (on)
+        {
+            m_selection.Add( row );
+            RefreshRow( row );
+        }
+    }
+    else
+    {
+        if (!on)
+        {
+            m_selection.Remove( row );
+            RefreshRow( row );
+        }
+    }
+}
+
+void wxDataViewMainWindow::SelectRows( size_t from, size_t to, bool on )
+{
+    if (from > to)
+    {
+        size_t tmp = from;
+        from = to;
+        to = tmp;
+    }
+
+    size_t i;
+    for (i = from; i <= to; i++)
+    {
+        if (m_selection.Index( i ) == wxNOT_FOUND)
+        {
+            if (on)
+                m_selection.Add( i );
+        }
+        else
+        {
+            if (!on)
+                m_selection.Remove( i );
+        }
+    }
+    RefreshRows( from, to );
+}
+
+void wxDataViewMainWindow::ReverseRowSelection( size_t row )
+{
+    if (m_selection.Index( row ) == wxNOT_FOUND)
+        m_selection.Add( row );
+    else
+        m_selection.Remove( row );
+    RefreshRow( row );
+}
+
+bool wxDataViewMainWindow::IsRowSelected( size_t row )
+{
+    return (m_selection.Index( row ) != wxNOT_FOUND);
+}
+
+void wxDataViewMainWindow::RefreshRow( size_t row )
+{
+    wxRect rect( 0, row*m_lineHeight, GetEndOfLastCol(), m_lineHeight );
+    m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+
+    wxSize client_size = GetClientSize();
+    wxRect client_rect( 0, 0, client_size.x, client_size.y );
+    wxRect intersect_rect = client_rect.Intersect( rect );
+    if (intersect_rect.width > 0)
+        Refresh( true, &intersect_rect );
+}
+
+void wxDataViewMainWindow::RefreshRows( size_t from, size_t to )
+{
+    if (from > to)
+    {
+        size_t tmp = to;
+        to = from;
+        from = tmp;
+    }
+
+    wxRect rect( 0, from*m_lineHeight, GetEndOfLastCol(), (to-from+1) * m_lineHeight );
+    m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+
+    wxSize client_size = GetClientSize();
+    wxRect client_rect( 0, 0, client_size.x, client_size.y );
+    wxRect intersect_rect = client_rect.Intersect( rect );
+    if (intersect_rect.width > 0)
+        Refresh( true, &intersect_rect );
+}
+
+void wxDataViewMainWindow::RefreshRowsAfter( size_t firstRow )
+{
+    size_t count = GetRowCount();
+    if (firstRow > count)
+        return;
+
+    wxRect rect( 0, firstRow*m_lineHeight, GetEndOfLastCol(), count * m_lineHeight );
+    m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
+
+    wxSize client_size = GetClientSize();
+    wxRect client_rect( 0, 0, client_size.x, client_size.y );
+    wxRect intersect_rect = client_rect.Intersect( rect );
+    if (intersect_rect.width > 0)
+        Refresh( true, &intersect_rect );
+}
+
+void wxDataViewMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event)
+{
+    wxCHECK_RET( newCurrent < GetRowCount(),
+                 _T("invalid item index in OnArrowChar()") );
+
+    // if there is no selection, we cannot move it anywhere
+    if (!HasCurrentRow())
+        return;
+
+    size_t oldCurrent = m_currentRow;
+
+    // in single selection we just ignore Shift as we can't select several
+    // items anyhow
+    if ( event.ShiftDown() && !IsSingleSel() )
+    {
+        RefreshRow( oldCurrent );
+
+        ChangeCurrentRow( newCurrent );
+
+        // select all the items between the old and the new one
+        if ( oldCurrent > newCurrent )
+        {
+            newCurrent = oldCurrent;
+            oldCurrent = m_currentRow;
+        }
+
+        SelectRows( oldCurrent, newCurrent, true );
+    }
+    else // !shift
+    {
+        RefreshRow( oldCurrent );
+
+        // all previously selected items are unselected unless ctrl is held
+        if ( !event.ControlDown() )
+            SelectAllRows(false);
+
+        ChangeCurrentRow( newCurrent );
+
+        if ( !event.ControlDown() )
+            SelectRow( m_currentRow, true );
+        else
+            RefreshRow( m_currentRow );
+    }
+
+    // MoveToFocus();
+}
+
+void wxDataViewMainWindow::OnChar( wxKeyEvent &event )
+{
+    if (event.GetKeyCode() == WXK_TAB)
+    {
+        wxNavigationKeyEvent nevent;
+        nevent.SetWindowChange( event.ControlDown() );
+        nevent.SetDirection( !event.ShiftDown() );
+        nevent.SetEventObject( GetParent()->GetParent() );
+        nevent.SetCurrentFocus( m_parent );
+        if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent ))
+            return;
+    }
+
+    // no item -> nothing to do
+    if (!HasCurrentRow())
+    {
+        event.Skip();
+        return;
+    }
+
+    // don't use m_linesPerPage directly as it might not be computed yet
+    const int pageSize = GetCountPerPage();
+    wxCHECK_RET( pageSize, _T("should have non zero page size") );
+
+    switch ( event.GetKeyCode() )
+    {
+        case WXK_UP:
+            if ( m_currentRow > 0 )
+                OnArrowChar( m_currentRow - 1, event );
+            break;
+
+        case WXK_DOWN:
+            if ( m_currentRow < GetRowCount() - 1 )
+                OnArrowChar( m_currentRow + 1, event );
+            break;
+
+        case WXK_END:
+            if (!IsEmpty())
+                OnArrowChar( GetRowCount() - 1, event );
+            break;
+
+        case WXK_HOME:
+            if (!IsEmpty())
+                OnArrowChar( 0, event );
+            break;
+
+        case WXK_PAGEUP:
+            {
+                int steps = pageSize - 1;
+                int index = m_currentRow - steps;
+                if (index < 0)
+                    index = 0;
+
+                OnArrowChar( index, event );
+            }
+            break;
+
+        case WXK_PAGEDOWN:
+            {
+                int steps = pageSize - 1;
+                size_t index = m_currentRow + steps;
+                size_t count = GetRowCount();
+                if ( index >= count )
+                    index = count - 1;
+
+                OnArrowChar( index, event );
+            }
+            break;
+
+        default:
+            event.Skip();
+    }
+}
+
 void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
 {
+    if (event.GetEventType() == wxEVT_MOUSEWHEEL)
+    {
+        // let the base handle mouse wheel events.
+        event.Skip();
+        return;
+    }
+
     int x = event.GetX();
     int y = event.GetY();
     m_owner->CalcUnscrolledPosition( x, y, &x, &y );
@@ -1014,10 +1447,44 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
         return;
     wxDataViewCell *cell = col->GetCell();
 
-    size_t row = y / m_lineHeight;
+    size_t current = y / m_lineHeight;
+
+    if ((current > GetRowCount()) || (x > GetEndOfLastCol()))
+    {
+        // Unselect all if below the last row ?
+        return;
+    }
 
     wxDataViewListModel *model = GetOwner()->GetModel();
 
+    if (event.Dragging())
+    {
+        if (m_dragCount == 0)
+        {
+            // we have to report the raw, physical coords as we want to be
+            // able to call HitTest(event.m_pointDrag) from the user code to
+            // get the item being dragged
+            m_dragStart = event.GetPosition();
+        }
+
+        m_dragCount++;
+
+        if (m_dragCount != 3)
+            return;
+
+        if (event.LeftIsDown())
+        {
+            // Notify cell about drag
+        }
+        return;
+    }
+    else
+    {
+        m_dragCount = 0;
+    }
+
+    bool forceClick = false;
+
     if (event.ButtonDClick())
     {
         m_renameTimer->Stop();
@@ -1026,22 +1493,38 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
 
     if (event.LeftDClick())
     {
-        if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
+        if ( current == m_lineLastClicked )
         {
-            wxVariant value;
-            model->GetValue( value, col->GetModelColumn(), row );
-            cell->SetValue( value );
-            wxRect cell_rect( xpos, row * m_lineHeight, col->GetWidth(), m_lineHeight );
-            cell->Activate( cell_rect, model, col->GetModelColumn(), row );
+            if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
+            {
+                wxVariant value;
+                model->GetValue( value, col->GetModelColumn(), current );
+                cell->SetValue( value );
+                wxRect cell_rect( xpos, current * m_lineHeight, col->GetWidth(), m_lineHeight );
+                cell->Activate( cell_rect, model, col->GetModelColumn(), current );
+            }
+            return;
         }
+        else
+        {
+            // The first click was on another item, so don't interpret this as
+            // a double click, but as a simple click instead
+            forceClick = true;
+        }
+    }
 
-        return;
-    } else
     if (event.LeftUp())
     {
+        if (m_lineSelectSingleOnUp != (size_t)-1)
+        {
+            // select single line
+            SelectAllRows( false );
+            SelectRow( m_lineSelectSingleOnUp, true );
+        }
+
         if (m_lastOnSame)
         {
-            if ((col == m_currentCol) & (row == m_currentRow) &&
+            if ((col == m_currentCol) && (current == m_currentRow) &&
                 (cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
             {
                 m_renameTimer->Start( 100, true );
@@ -1049,26 +1532,130 @@ void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
         }
 
         m_lastOnSame = false;
-    } else
-    if (event.LeftDown())
+        m_lineSelectSingleOnUp = (size_t)-1;
+    }
+    else
     {
-        wxDataViewColumn *oldCurrentCol = m_currentCol;
+        // This is necessary, because after a DnD operation in
+        // from and to ourself, the up event is swallowed by the
+        // DnD code. So on next non-up event (which means here and
+        // now) m_lineSelectSingleOnUp should be reset.
+        m_lineSelectSingleOnUp = (size_t)-1;
+    }
+
+    if (event.RightDown())
+    {
+        m_lineBeforeLastClicked = m_lineLastClicked;
+        m_lineLastClicked = current;
+
+        // If the item is already selected, do not update the selection.
+        // Multi-selections should not be cleared if a selected item is clicked.
+        if (!IsRowSelected(current))
+        {
+            SelectAllRows(false);
+            ChangeCurrentRow(current);
+            SelectRow(m_currentRow,true);
+        }
+
+        // notify cell about right click
+        // cell->...
+
+        // Allow generation of context menu event
+        event.Skip();
+    }
+    else if (event.MiddleDown())
+    {
+        // notify cell about middle click
+        // cell->...
+    }
+    if (event.LeftDown() || forceClick)
+    {
+#ifdef __WXMSW__
+        SetFocus();
+#endif
+
+        m_lineBeforeLastClicked = m_lineLastClicked;
+        m_lineLastClicked = current;
+
         size_t oldCurrentRow = m_currentRow;
-        
+        bool oldWasSelected = IsRowSelected(m_currentRow);
+
+        bool cmdModifierDown = event.CmdDown();
+        if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
+        {
+            if ( IsSingleSel() || !IsRowSelected(current) )
+            {
+                SelectAllRows( false );
+
+                ChangeCurrentRow(current);
+
+                SelectRow(m_currentRow,true);
+            }
+            else // multi sel & current is highlighted & no mod keys
+            {
+                m_lineSelectSingleOnUp = current;
+                ChangeCurrentRow(current); // change focus
+            }
+        }
+        else // multi sel & either ctrl or shift is down
+        {
+            if (cmdModifierDown)
+            {
+                ChangeCurrentRow(current);
+
+                ReverseRowSelection(m_currentRow);
+            }
+            else if (event.ShiftDown())
+            {
+                ChangeCurrentRow(current);
+
+                size_t lineFrom = oldCurrentRow,
+                       lineTo = current;
+
+                if ( lineTo < lineFrom )
+                {
+                    lineTo = lineFrom;
+                    lineFrom = m_currentRow;
+                }
+
+                SelectRows(lineFrom, lineTo, true);
+            }
+            else // !ctrl, !shift
+            {
+                // test in the enclosing if should make it impossible
+                wxFAIL_MSG( _T("how did we get here?") );
+            }
+        }
+
+        if (m_currentRow != oldCurrentRow)
+            RefreshRow( oldCurrentRow );
+
+        wxDataViewColumn *oldCurrentCol = m_currentCol;
+
         // Update selection here...
         m_currentCol = col;
-        m_currentRow = row;
-    
-        m_lastOnSame = (col == oldCurrentCol) && (row == oldCurrentRow);
-        
-        return;
+
+        m_lastOnSame = !forceClick && ((col == oldCurrentCol) && (current == oldCurrentRow)) && oldWasSelected;
     }
+}
+
+void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
+{
+    m_hasFocus = true;
+
+    if (HasCurrentRow())
+        Refresh();
 
     event.Skip();
 }
 
-void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
+void wxDataViewMainWindow::OnKillFocus( wxFocusEvent &event )
 {
+    m_hasFocus = false;
+
+    if (HasCurrentRow())
+        Refresh();
+
     event.Skip();
 }
 
@@ -1107,7 +1694,11 @@ bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
 #endif
 
     m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
+#ifdef __WXMSW__
+    m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,22) );
+#else
     m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,25) );
+#endif
 
     SetTargetWindow( m_clientArea );
 
@@ -1181,4 +1772,3 @@ bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
 
 #endif
     // wxUSE_DATAVIEWCTRL
-