]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/listctrl.cpp
Fix drawing of bitmaps with masks in mirrored wxDC.
[wxWidgets.git] / src / msw / listctrl.cpp
index 69b6cfa51f1e30696b26be381714d751f229a30f..941506314e1c6fb762f4ad8e938e1bdcdfd69979 100644 (file)
     #include "wx/intl.h"
     #include "wx/log.h"
     #include "wx/settings.h"
+    #include "wx/stopwatch.h"
     #include "wx/dcclient.h"
     #include "wx/textctrl.h"
 #endif
 
 #include "wx/imaglist.h"
+#include "wx/vector.h"
 
 #include "wx/msw/private.h"
+#include "wx/msw/private/keyboard.h"
 
 #if defined(__WXWINCE__) && !defined(__HANDHELDPC__)
   #include <ole2.h>
@@ -82,6 +85,33 @@ static void wxConvertToMSWListCol(HWND hwndList,
                                   const wxListItem& item,
                                   LV_COLUMN& lvCol);
 
+namespace
+{
+
+// replacement for ListView_GetSubItemRect() which provokes warnings like
+// "the address of 'rc' will always evaluate as 'true'" when used with mingw32
+// 4.3+
+//
+// this function does no error checking on item and subitem parameters, notice
+// that subitem 0 means the whole item so there is no way to retrieve the
+// rectangle of the first subitem using this function, in particular notice
+// that the index is *not* 1-based, in spite of what MSDN says
+inline bool
+wxGetListCtrlSubItemRect(HWND hwnd, int item, int subitem, int flags, RECT& rect)
+{
+    rect.top = subitem;
+    rect.left = flags;
+    return ::SendMessage(hwnd, LVM_GETSUBITEMRECT, item, (LPARAM)&rect) != 0;
+}
+
+inline bool
+wxGetListCtrlItemRect(HWND hwnd, int item, int flags, RECT& rect)
+{
+    return wxGetListCtrlSubItemRect(hwnd, item, 0, flags, rect);
+}
+
+} // anonymous namespace
+
 // ----------------------------------------------------------------------------
 // private helper classes
 // ----------------------------------------------------------------------------
@@ -114,7 +144,7 @@ public:
     // init without conversion
     void Init(LV_ITEM_NATIVE& item)
     {
-        wxASSERT_MSG( !m_pItem, _T("Init() called twice?") );
+        wxASSERT_MSG( !m_pItem, wxT("Init() called twice?") );
 
         m_pItem = &item;
     }
@@ -159,7 +189,7 @@ private:
     LV_ITEM_NATIVE *m_pItem;
     LV_ITEM_NATIVE m_item;
 
-    DECLARE_NO_COPY_CLASS(wxLV_ITEM)
+    wxDECLARE_NO_COPY_CLASS(wxLV_ITEM);
 };
 
 ///////////////////////////////////////////////////////
@@ -167,7 +197,7 @@ private:
 // The MSW version had problems with SetTextColour() et
 // al as the wxListItemAttr's were stored keyed on the
 // item index. If a item was inserted anywhere but the end
-// of the list the the text attributes (colour etc) for
+// of the list the text attributes (colour etc) for
 // the following items were out of sync.
 //
 // Solution:
@@ -179,119 +209,26 @@ private:
 //
 // However what we can do is store a pointer to a
 // structure which contains the attributes we want *and*
-// a lParam for the users data, e.g.
-//
-// class wxListItemInternalData
-// {
-// public:
-//   wxListItemAttr *attr;
-//   long lParam; // user data
-// };
+// a lParam -- and this is what wxMSWListItemData does.
 //
-// To conserve memory, a wxListItemInternalData is
+// To conserve memory, a wxMSWListItemData is
 // only allocated for a LV_ITEM if text attributes or
 // user data(lparam) are being set.
-
-
-// class wxListItemInternalData
-class wxListItemInternalData
+class wxMSWListItemData
 {
 public:
-   wxListItemAttr *attr;
-   LPARAM lParam; // user data
+   wxMSWListItemData() : attr(NULL), lParam(0) {}
+   ~wxMSWListItemData() { delete attr; }
 
-   wxListItemInternalData() : attr(NULL), lParam(0) {}
-   ~wxListItemInternalData()
-   {
-       if (attr)
-           delete attr;
-   };
+    wxListItemAttr *attr;
+    LPARAM lParam; // real user data
 
-    DECLARE_NO_COPY_CLASS(wxListItemInternalData)
+    wxDECLARE_NO_COPY_CLASS(wxMSWListItemData);
 };
 
-// Get the internal data structure
-static wxListItemInternalData *wxGetInternalData(HWND hwnd, long itemId);
-static wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId);
-static wxListItemAttr *wxGetInternalDataAttr(const wxListCtrl *ctl, long itemId);
-static void wxDeleteInternalData(wxListCtrl* ctl, long itemId);
-
-
-#if wxUSE_EXTENDED_RTTI
-WX_DEFINE_FLAGS( wxListCtrlStyle )
-
-wxBEGIN_FLAGS( wxListCtrlStyle )
-    // new style border flags, we put them first to
-    // use them for streaming out
-    wxFLAGS_MEMBER(wxBORDER_SIMPLE)
-    wxFLAGS_MEMBER(wxBORDER_SUNKEN)
-    wxFLAGS_MEMBER(wxBORDER_DOUBLE)
-    wxFLAGS_MEMBER(wxBORDER_RAISED)
-    wxFLAGS_MEMBER(wxBORDER_STATIC)
-    wxFLAGS_MEMBER(wxBORDER_NONE)
-
-    // old style border flags
-    wxFLAGS_MEMBER(wxSIMPLE_BORDER)
-    wxFLAGS_MEMBER(wxSUNKEN_BORDER)
-    wxFLAGS_MEMBER(wxDOUBLE_BORDER)
-    wxFLAGS_MEMBER(wxRAISED_BORDER)
-    wxFLAGS_MEMBER(wxSTATIC_BORDER)
-    wxFLAGS_MEMBER(wxBORDER)
-
-    // standard window styles
-    wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
-    wxFLAGS_MEMBER(wxCLIP_CHILDREN)
-    wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
-    wxFLAGS_MEMBER(wxWANTS_CHARS)
-    wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
-    wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
-    wxFLAGS_MEMBER(wxVSCROLL)
-    wxFLAGS_MEMBER(wxHSCROLL)
-
-    wxFLAGS_MEMBER(wxLC_LIST)
-    wxFLAGS_MEMBER(wxLC_REPORT)
-    wxFLAGS_MEMBER(wxLC_ICON)
-    wxFLAGS_MEMBER(wxLC_SMALL_ICON)
-    wxFLAGS_MEMBER(wxLC_ALIGN_TOP)
-    wxFLAGS_MEMBER(wxLC_ALIGN_LEFT)
-    wxFLAGS_MEMBER(wxLC_AUTOARRANGE)
-    wxFLAGS_MEMBER(wxLC_USER_TEXT)
-    wxFLAGS_MEMBER(wxLC_EDIT_LABELS)
-    wxFLAGS_MEMBER(wxLC_NO_HEADER)
-    wxFLAGS_MEMBER(wxLC_SINGLE_SEL)
-    wxFLAGS_MEMBER(wxLC_SORT_ASCENDING)
-    wxFLAGS_MEMBER(wxLC_SORT_DESCENDING)
-    wxFLAGS_MEMBER(wxLC_VIRTUAL)
-
-wxEND_FLAGS( wxListCtrlStyle )
-
-IMPLEMENT_DYNAMIC_CLASS_XTI(wxListCtrl, wxControl,"wx/listctrl.h")
-
-wxBEGIN_PROPERTIES_TABLE(wxListCtrl)
-    wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent )
-
-    wxPROPERTY_FLAGS( WindowStyle , wxListCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxListCtrl)
-wxEND_HANDLERS_TABLE()
-
-wxCONSTRUCTOR_5( wxListCtrl , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle )
-
-/*
- TODO : Expose more information of a list's layout etc. via appropriate objects (à la NotebookPageInfo)
-*/
-#else
-IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
-#endif
-
-IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
-IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
-
-IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
-
-BEGIN_EVENT_TABLE(wxListCtrl, wxControl)
+BEGIN_EVENT_TABLE(wxListCtrl, wxListCtrlBase)
     EVT_PAINT(wxListCtrl::OnPaint)
+    EVT_CHAR_HOOK(wxListCtrl::OnCharHook)
 END_EVENT_TABLE()
 
 // ============================================================================
@@ -304,15 +241,17 @@ END_EVENT_TABLE()
 
 void wxListCtrl::Init()
 {
-    m_imageListNormal = NULL;
-    m_imageListSmall = NULL;
+    m_imageListNormal =
+    m_imageListSmall =
     m_imageListState = NULL;
-    m_ownsImageListNormal = m_ownsImageListSmall = m_ownsImageListState = false;
+    m_ownsImageListNormal =
+    m_ownsImageListSmall =
+    m_ownsImageListState = false;
+
     m_colCount = 0;
     m_count = 0;
-    m_ignoreChangeMessages = false;
     m_textCtrl = NULL;
-    m_AnyInternalData = false;
+
     m_hasAnyAttr = false;
 }
 
@@ -338,32 +277,51 @@ bool wxListCtrl::Create(wxWindow *parent,
     // GetTextColour will always return black
     SetTextColour(GetDefaultAttributes().colFg);
 
+    if ( InReportView() )
+        MSWSetExListStyles();
+
+    return true;
+}
+
+void wxListCtrl::MSWSetExListStyles()
+{
     // for comctl32.dll v 4.70+ we want to have some non default extended
     // styles because it's prettier (and also because wxGTK does it like this)
-    if ( InReportView() && wxApp::GetComCtl32Version() >= 470 )
-    {
-        ::SendMessage(GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE,
-                      0, LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
+    if ( wxApp::GetComCtl32Version() >= 470 )
+    {
+        ::SendMessage
+        (
+            GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE, 0,
+            // LVS_EX_LABELTIP shouldn't be used under Windows CE where it's
+            // not defined in the SDK headers
+#ifdef LVS_EX_LABELTIP
+            LVS_EX_LABELTIP |
+#endif
+            LVS_EX_FULLROWSELECT |
+            LVS_EX_SUBITEMIMAGES |
+            // normally this should be governed by a style as it's probably not
+            // always appropriate, but we don't have any free styles left and
+            // it seems better to enable it by default than disable
+            LVS_EX_HEADERDRAGDROP
+        );
     }
-
-    return true;
 }
 
 WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
 {
-    WXDWORD wstyle = wxControl::MSWGetStyle(style, exstyle);
+    WXDWORD wstyle = wxListCtrlBase::MSWGetStyle(style, exstyle);
 
     wstyle |= LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS;
 
-#ifdef __WXDEBUG__
+#if wxDEBUG_LEVEL
     size_t nModes = 0;
 
     #define MAP_MODE_STYLE(wx, ms)                                            \
         if ( style & (wx) ) { wstyle |= (ms); nModes++; }
-#else // !__WXDEBUG__
+#else // !wxDEBUG_LEVEL
     #define MAP_MODE_STYLE(wx, ms)                                            \
         if ( style & (wx) ) wstyle |= (ms);
-#endif // __WXDEBUG__
+#endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL
 
     MAP_MODE_STYLE(wxLC_ICON, LVS_ICON)
     MAP_MODE_STYLE(wxLC_SMALL_ICON, LVS_SMALLICON)
@@ -371,7 +329,7 @@ WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
     MAP_MODE_STYLE(wxLC_REPORT, LVS_REPORT)
 
     wxASSERT_MSG( nModes == 1,
-                  _T("wxListCtrl style should have exactly one mode bit set") );
+                  wxT("wxListCtrl style should have exactly one mode bit set") );
 
 #undef MAP_MODE_STYLE
 
@@ -401,7 +359,7 @@ WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
         wstyle |= LVS_SORTASCENDING;
 
         wxASSERT_MSG( !(style & wxLC_SORT_DESCENDING),
-                      _T("can't sort in ascending and descending orders at once") );
+                      wxT("can't sort in ascending and descending orders at once") );
     }
     else if ( style & wxLC_SORT_DESCENDING )
         wstyle |= LVS_SORTDESCENDING;
@@ -445,36 +403,39 @@ void wxListCtrl::UpdateStyle()
         if ( dwStyleOld != dwStyleNew )
         {
             ::SetWindowLong(GetHwnd(), GWL_STYLE, dwStyleNew);
+
+            // if we switched to the report view, set the extended styles for
+            // it too
+            if ( !(dwStyleOld & LVS_REPORT) && (dwStyleNew & LVS_REPORT) )
+                MSWSetExListStyles();
         }
     }
 }
 
 void wxListCtrl::FreeAllInternalData()
 {
-    if (m_AnyInternalData)
-    {
-        int n = GetItemCount();
-
-        m_ignoreChangeMessages = true;
-        for (int i = 0; i < n; i++)
-            wxDeleteInternalData(this, i);
-        m_ignoreChangeMessages = false;
+    const unsigned count = m_internalData.size();
+    for ( unsigned n = 0; n < count; n++ )
+        delete m_internalData[n];
 
-        m_AnyInternalData = false;
-    }
+    m_internalData.clear();
 }
 
-wxListCtrl::~wxListCtrl()
+void wxListCtrl::DeleteEditControl()
 {
-    FreeAllInternalData();
-
     if ( m_textCtrl )
     {
         m_textCtrl->UnsubclassWin();
         m_textCtrl->SetHWND(0);
-        delete m_textCtrl;
-        m_textCtrl = NULL;
+        wxDELETE(m_textCtrl);
     }
+}
+
+wxListCtrl::~wxListCtrl()
+{
+    FreeAllInternalData();
+
+    DeleteEditControl();
 
     if (m_ownsImageListNormal)
         delete m_imageListNormal;
@@ -517,7 +478,7 @@ void wxListCtrl::SetWindowStyleFlag(long flag)
 {
     if ( flag != m_windowStyle )
     {
-        m_windowStyle = flag;
+        wxListCtrlBase::SetWindowStyleFlag(flag);
 
         UpdateStyle();
 
@@ -663,6 +624,67 @@ bool wxListCtrl::SetColumnWidth(int col, int width)
     return ListView_SetColumnWidth(GetHwnd(), col, width) != 0;
 }
 
+// ----------------------------------------------------------------------------
+// columns order
+// ----------------------------------------------------------------------------
+
+int wxListCtrl::GetColumnIndexFromOrder(int order) const
+{
+    const int numCols = GetColumnCount();
+    wxCHECK_MSG( order >= 0 && order < numCols, -1,
+                wxT("Column position out of bounds") );
+
+    wxArrayInt indexArray(numCols);
+    if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) )
+        return -1;
+
+    return indexArray[order];
+}
+
+int wxListCtrl::GetColumnOrder(int col) const
+{
+    const int numCols = GetColumnCount();
+    wxASSERT_MSG( col >= 0 && col < numCols, wxT("Column index out of bounds") );
+
+    wxArrayInt indexArray(numCols);
+    if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) )
+        return -1;
+
+    for ( int pos = 0; pos < numCols; pos++ )
+    {
+        if ( indexArray[pos] == col )
+            return pos;
+    }
+
+    wxFAIL_MSG( wxT("no column with with given order?") );
+
+    return -1;
+}
+
+// Gets the column order for all columns
+wxArrayInt wxListCtrl::GetColumnsOrder() const
+{
+    const int numCols = GetColumnCount();
+
+    wxArrayInt orders(numCols);
+    if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &orders[0]) )
+        orders.clear();
+
+    return orders;
+}
+
+// Sets the column order for all columns
+bool wxListCtrl::SetColumnsOrder(const wxArrayInt& orders)
+{
+    const int numCols = GetColumnCount();
+
+    wxCHECK_MSG( orders.size() == (size_t)numCols, false,
+                    wxT("wrong number of elements in column orders array") );
+
+    return ListView_SetColumnOrderArray(GetHwnd(), numCols, &orders[0]) != 0;
+}
+
+
 // Gets the number of items that can fit vertically in the
 // visible area of the list control (list or report view)
 // or the total number of items in the list control (icon
@@ -675,6 +697,25 @@ int wxListCtrl::GetCountPerPage() const
 // Gets the edit control for editing labels.
 wxTextCtrl* wxListCtrl::GetEditControl() const
 {
+    // first check corresponds to the case when the label editing was started
+    // by user and hence m_textCtrl wasn't created by EditLabel() at all, while
+    // the second case corresponds to us being called from inside EditLabel()
+    // (e.g. from a user wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT handler): in this
+    // case EditLabel() did create the control but it didn't have an HWND to
+    // initialize it with yet
+    if ( !m_textCtrl || !m_textCtrl->GetHWND() )
+    {
+        HWND hwndEdit = ListView_GetEditControl(GetHwnd());
+        if ( hwndEdit )
+        {
+            wxListCtrl * const self = const_cast<wxListCtrl *>(this);
+
+            if ( !m_textCtrl )
+                self->m_textCtrl = new wxTextCtrl;
+            self->InitEditControl((WXHWND)hwndEdit);
+        }
+    }
+
     return m_textCtrl;
 }
 
@@ -733,34 +774,33 @@ bool wxListCtrl::SetItem(wxListItem& info)
 {
     const long id = info.GetId();
     wxCHECK_MSG( id >= 0 && id < GetItemCount(), false,
-                 _T("invalid item index in SetItem") );
+                 wxT("invalid item index in SetItem") );
 
     LV_ITEM item;
     wxConvertToMSWListItem(this, info, item);
 
     // we never update the lParam if it contains our pointer
-    // to the wxListItemInternalData structure
+    // to the wxMSWListItemData structure
     item.mask &= ~LVIF_PARAM;
 
     // check if setting attributes or lParam
-    if (info.HasAttributes() || (info.m_mask  & wxLIST_MASK_DATA))
+    if ( info.HasAttributes() || (info.m_mask & wxLIST_MASK_DATA) )
     {
         // get internal item data
-        // perhaps a cache here ?
-        wxListItemInternalData *data = wxGetInternalData(this, id);
+        wxMSWListItemData *data = MSWGetItemData(id);
 
-        if (! data)
+        if ( !data )
         {
-            // need to set it
-            m_AnyInternalData = true;
-            data = new wxListItemInternalData();
+            // need to allocate the internal data object
+            data = new wxMSWListItemData;
+            m_internalData.push_back(data);
             item.lParam = (LPARAM) data;
             item.mask |= LVIF_PARAM;
-        };
+        }
 
 
         // user data
-        if (info.m_mask  & wxLIST_MASK_DATA)
+        if ( info.m_mask & wxLIST_MASK_DATA )
             data->lParam = info.m_data;
 
         // attributes
@@ -773,18 +813,17 @@ bool wxListCtrl::SetItem(wxListItem& info)
                 data->attr->AssignFrom(attrNew);
             else
                 data->attr = new wxListItemAttr(attrNew);
-        };
-    };
+        }
+    }
 
 
     // we could be changing only the attribute in which case we don't need to
     // call ListView_SetItem() at all
     if ( item.mask )
     {
-        item.cchTextMax = 0;
         if ( !ListView_SetItem(GetHwnd(), &item) )
         {
-            wxLogDebug(_T("ListView_SetItem() failed"));
+            wxLogDebug(wxT("ListView_SetItem() failed"));
 
             return false;
         }
@@ -852,14 +891,15 @@ bool wxListCtrl::SetItemState(long item, long state, long stateMask)
 
     wxConvertToMSWFlags(state, stateMask, lvItem);
 
+    const bool changingFocus = (stateMask & wxLIST_STATE_FOCUSED) &&
+                                    (state & wxLIST_STATE_FOCUSED);
+
     // for the virtual list controls we need to refresh the previously focused
     // item manually when changing focus without changing selection
     // programmatically because otherwise it keeps its focus rectangle until
     // next repaint (yet another comctl32 bug)
     long focusOld;
-    if ( IsVirtual() &&
-         (stateMask & wxLIST_STATE_FOCUSED) &&
-         (state & wxLIST_STATE_FOCUSED) )
+    if ( IsVirtual() && changingFocus )
     {
         focusOld = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
     }
@@ -871,7 +911,7 @@ bool wxListCtrl::SetItemState(long item, long state, long stateMask)
     if ( !::SendMessage(GetHwnd(), LVM_SETITEMSTATE,
                         (WPARAM)item, (LPARAM)&lvItem) )
     {
-        wxLogLastError(_T("ListView_SetItemState"));
+        wxLogLastError(wxT("ListView_SetItemState"));
 
         return false;
     }
@@ -887,6 +927,16 @@ bool wxListCtrl::SetItemState(long item, long state, long stateMask)
         }
     }
 
+    // we expect the selection anchor, i.e. the item from which multiple
+    // selection (such as performed with e.g. Shift-arrows) starts, to be the
+    // same as the currently focused item but the native control doesn't update
+    // it when we change focus and leaves at the last item it set itself focus
+    // to, so do it explicitly
+    if ( changingFocus && !HasFlag(wxLC_SINGLE_SEL) )
+    {
+        ListView_SetSelectionMark(GetHwnd(), item);
+    }
+
     return true;
 }
 
@@ -910,12 +960,13 @@ bool wxListCtrl::SetItemColumnImage(long item, long column, int image)
 }
 
 // Gets the item text
-wxString wxListCtrl::GetItemText(long item) const
+wxString wxListCtrl::GetItemText(long item, int col) const
 {
     wxListItem info;
 
     info.m_mask = wxLIST_MASK_TEXT;
     info.m_itemId = item;
+    info.m_col = col;
 
     if (!GetItem(info))
         return wxEmptyString;
@@ -934,6 +985,19 @@ void wxListCtrl::SetItemText(long item, const wxString& str)
     SetItem(info);
 }
 
+// Gets the internal item data
+wxMSWListItemData *wxListCtrl::MSWGetItemData(long itemId) const
+{
+    LV_ITEM it;
+    it.mask = LVIF_PARAM;
+    it.iItem = itemId;
+
+    if ( !ListView_GetItem(GetHwnd(), &it) )
+        return NULL;
+
+    return (wxMSWListItemData *) it.lParam;
+}
+
 // Gets the item data
 wxUIntPtr wxListCtrl::GetItemData(long item) const
 {
@@ -948,7 +1012,7 @@ wxUIntPtr wxListCtrl::GetItemData(long item) const
 }
 
 // Sets the item data
-bool wxListCtrl::SetItemData(long item, long data)
+bool wxListCtrl::SetItemPtrData(long item, wxUIntPtr data)
 {
     wxListItem info;
 
@@ -961,19 +1025,40 @@ bool wxListCtrl::SetItemData(long item, long data)
 
 wxRect wxListCtrl::GetViewRect() const
 {
-    wxASSERT_MSG( !HasFlag(wxLC_REPORT | wxLC_LIST),
-                    _T("wxListCtrl::GetViewRect() only works in icon mode") );
+    wxRect rect;
 
-    RECT rc;
-    if ( !ListView_GetViewRect(GetHwnd(), &rc) )
+    // ListView_GetViewRect() can only be used in icon and small icon views
+    // (this is documented in MSDN and, indeed, it returns bogus results in
+    // report view, at least with comctl32.dll v6 under Windows 2003)
+    if ( HasFlag(wxLC_ICON | wxLC_SMALL_ICON) )
     {
-        wxLogDebug(_T("ListView_GetViewRect() failed."));
+        RECT rc;
+        if ( !ListView_GetViewRect(GetHwnd(), &rc) )
+        {
+            wxLogDebug(wxT("ListView_GetViewRect() failed."));
+
+            wxZeroMemory(rc);
+        }
 
-        wxZeroMemory(rc);
+        wxCopyRECTToRect(rc, rect);
     }
+    else if ( HasFlag(wxLC_REPORT) )
+    {
+        const long count = GetItemCount();
+        if ( count )
+        {
+            GetItemRect(wxMin(GetTopItem() + GetCountPerPage(), count - 1), rect);
 
-    wxRect rect;
-    wxCopyRECTToRect(rc, rect);
+            // extend the rectangle to start at the top (we include the column
+            // headers, if any, for compatibility with the generic version)
+            rect.height += rect.y;
+            rect.y = 0;
+        }
+    }
+    else
+    {
+        wxFAIL_MSG( wxT("not implemented in this mode") );
+    }
 
     return rect;
 }
@@ -984,22 +1069,18 @@ bool wxListCtrl::GetItemRect(long item, wxRect& rect, int code) const
     return GetSubItemRect( item, wxLIST_GETSUBITEMRECT_WHOLEITEM, rect, code) ;
 }
 
-/*!
- * Retrieve coordinates and size of a specified subitem of a listview control.
- * This function only works if the listview control is in the report mode.
- *
- * @param item : Item number
- * @param subItem : Subitem or column number, use -1 for the whole row including
- *                  all columns or subitems
- * @param rect : A pointer to an allocated wxRect object
- * @param code : Specify the part of the subitem coordinates you need. Choices are
- *               wxLIST_RECT_BOUNDS, wxLIST_RECT_ICON, wxLIST_RECT_LABEL
- *
- * @return bool  : True if successful.
- */
 bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code) const
 {
-    RECT rectWin;
+    // ListView_GetSubItemRect() doesn't do subItem error checking and returns
+    // true even for the out of range values of it (even if the results are
+    // completely bogus in this case), so we check item validity ourselves
+    wxCHECK_MSG( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM ||
+                    (subItem >= 0 && subItem < GetColumnCount()),
+                 false, wxT("invalid sub item index") );
+
+    // use wxCHECK_MSG against "item" too, for coherency with the generic implementation:
+    wxCHECK_MSG( item >= 0 && item < GetItemCount(), false,
+                 wxT("invalid item in GetSubItemRect") );
 
     int codeWin;
     if ( code == wxLIST_RECT_BOUNDS )
@@ -1010,31 +1091,32 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code)
         codeWin = LVIR_LABEL;
     else
     {
-        wxFAIL_MSG( _T("incorrect code in GetItemRect() / GetSubItemRect()") );
+        wxFAIL_MSG( wxT("incorrect code in GetItemRect() / GetSubItemRect()") );
         codeWin = LVIR_BOUNDS;
     }
 
-    bool success;
-    if( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM)
-    {
-      success = ListView_GetItemRect(GetHwnd(), (int) item, &rectWin, codeWin) != 0;
-    }
-    else if( subItem >= 0)
-    {
-      success = ListView_GetSubItemRect( GetHwnd(), (int) item, (int) subItem, codeWin, &rectWin) != 0;
-    }
-    else
+    RECT rectWin;
+    if ( !wxGetListCtrlSubItemRect
+          (
+            GetHwnd(),
+            item,
+            subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM ? 0 : subItem,
+            codeWin,
+            rectWin
+          ) )
     {
-      wxFAIL_MSG( _T("incorrect subItem number in GetSubItemRect()") );
-      return false;
+        return false;
     }
 
-    rect.x = rectWin.left;
-    rect.y = rectWin.top;
-    rect.width = rectWin.right - rectWin.left;
-    rect.height = rectWin.bottom - rectWin.top;
+    wxCopyRECTToRect(rectWin, rect);
 
-    return success;
+    // there is no way to retrieve the first sub item bounding rectangle using
+    // wxGetListCtrlSubItemRect() as 0 means the whole item, so we need to
+    // truncate it at first column ourselves
+    if ( subItem == 0 && code == wxLIST_RECT_BOUNDS )
+        rect.width = GetColumnWidth(0);
+
+    return true;
 }
 
 
@@ -1090,7 +1172,7 @@ void wxListCtrl::SetItemTextColour( long item, const wxColour &col )
 wxColour wxListCtrl::GetItemTextColour( long item ) const
 {
     wxColour col;
-    wxListItemInternalData *data = wxGetInternalData(this, item);
+    wxMSWListItemData *data = MSWGetItemData(item);
     if ( data && data->attr )
         col = data->attr->GetTextColour();
 
@@ -1108,7 +1190,7 @@ void wxListCtrl::SetItemBackgroundColour( long item, const wxColour &col )
 wxColour wxListCtrl::GetItemBackgroundColour( long item ) const
 {
     wxColour col;
-    wxListItemInternalData *data = wxGetInternalData(this, item);
+    wxMSWListItemData *data = MSWGetItemData(item);
     if ( data && data->attr )
         col = data->attr->GetBackgroundColour();
 
@@ -1126,7 +1208,7 @@ void wxListCtrl::SetItemFont( long item, const wxFont &f )
 wxFont wxListCtrl::GetItemFont( long item ) const
 {
     wxFont f;
-    wxListItemInternalData *data = wxGetInternalData(this, item);
+    wxMSWListItemData *data = MSWGetItemData(item);
     if ( data && data->attr )
         f = data->attr->GetFont();
 
@@ -1251,6 +1333,29 @@ void wxListCtrl::AssignImageList(wxImageList *imageList, int which)
         m_ownsImageListState = true;
 }
 
+// ----------------------------------------------------------------------------
+// Geometry
+// ----------------------------------------------------------------------------
+
+wxSize wxListCtrl::MSWGetBestViewRect(int x, int y) const
+{
+    const DWORD rc = ListView_ApproximateViewRect(GetHwnd(), x, y, -1);
+
+    wxSize size(LOWORD(rc), HIWORD(rc));
+
+    // We have to add space for the scrollbars ourselves, they're not taken
+    // into account by ListView_ApproximateViewRect(), at least not with
+    // commctrl32.dll v6.
+    const DWORD mswStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE);
+
+    if ( mswStyle & WS_HSCROLL )
+        size.y += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
+    if ( mswStyle & WS_VSCROLL )
+        size.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
+
+    return size;
+}
+
 // ----------------------------------------------------------------------------
 // Operations
 // ----------------------------------------------------------------------------
@@ -1276,7 +1381,7 @@ bool wxListCtrl::DeleteItem(long item)
 {
     if ( !ListView_DeleteItem(GetHwnd(), (int) item) )
     {
-        wxLogLastError(_T("ListView_DeleteItem"));
+        wxLogLastError(wxT("ListView_DeleteItem"));
         return false;
     }
 
@@ -1312,7 +1417,9 @@ bool wxListCtrl::DeleteItem(long item)
 // Deletes all items
 bool wxListCtrl::DeleteAllItems()
 {
-    return ListView_DeleteAllItems(GetHwnd()) != 0;
+    // Calling ListView_DeleteAllItems() will always generate an event but we
+    // shouldn't do it if the control is empty
+    return !GetItemCount() || ListView_DeleteAllItems(GetHwnd()) != 0;
 }
 
 // Deletes all items
@@ -1353,38 +1460,48 @@ void wxListCtrl::ClearAll()
         DeleteAllColumns();
 }
 
+void wxListCtrl::InitEditControl(WXHWND hWnd)
+{
+    m_textCtrl->SetHWND(hWnd);
+    m_textCtrl->SubclassWin(hWnd);
+    m_textCtrl->SetParent(this);
+
+    // we must disallow TABbing away from the control while the edit control is
+    // shown because this leaves it in some strange state (just try removing
+    // this line and then pressing TAB while editing an item in  listctrl
+    // inside a panel)
+    m_textCtrl->SetWindowStyle(m_textCtrl->GetWindowStyle() | wxTE_PROCESS_TAB);
+}
+
 wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass)
 {
-    wxASSERT( (textControlClass->IsKindOf(CLASSINFO(wxTextCtrl))) );
+    wxCHECK_MSG( textControlClass->IsKindOf(wxCLASSINFO(wxTextCtrl)), NULL,
+                  "control used for label editing must be a wxTextCtrl" );
 
     // ListView_EditLabel requires that the list has focus.
     SetFocus();
 
+    // create m_textCtrl here before calling ListView_EditLabel() because it
+    // generates wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT event from inside it and
+    // the user handler for it can call GetEditControl() resulting in an on
+    // demand creation of a stock wxTextCtrl instead of the control of a
+    // (possibly) custom wxClassInfo
+    DeleteEditControl();
+    m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
+
     WXHWND hWnd = (WXHWND) ListView_EditLabel(GetHwnd(), item);
     if ( !hWnd )
     {
         // failed to start editing
-        return NULL;
-    }
+        wxDELETE(m_textCtrl);
 
-    // [re]create the text control wrapping the HWND we got
-    if ( m_textCtrl )
-    {
-        m_textCtrl->UnsubclassWin();
-        m_textCtrl->SetHWND(0);
-        delete m_textCtrl;
+        return NULL;
     }
 
-    m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
-    m_textCtrl->SetHWND(hWnd);
-    m_textCtrl->SubclassWin(hWnd);
-    m_textCtrl->SetParent(this);
-
-    // we must disallow TABbing away from the control while the edit contol is
-    // shown because this leaves it in some strange state (just try removing
-    // this line and then pressing TAB while editing an item in  listctrl
-    // inside a panel)
-    m_textCtrl->SetWindowStyle(m_textCtrl->GetWindowStyle() | wxTE_PROCESS_TAB);
+    // if GetEditControl() hasn't been called, we need to initialize the edit
+    // control ourselves
+    if ( !m_textCtrl->GetHWND() )
+        InitEditControl(hWnd);
 
     return m_textCtrl;
 }
@@ -1397,14 +1514,15 @@ bool wxListCtrl::EndEditLabel(bool cancel)
     if ( !hwnd )
         return false;
 
-    if ( cancel )
-        ::SetWindowText(hwnd, wxEmptyString); // dubious but better than nothing
+    // Newer versions of Windows have a special ListView_CancelEditLabel()
+    // message for cancelling editing but it, rather counter-intuitively, keeps
+    // the last text entered in the dialog while cancelling as we do it below
+    // restores the original text which is the more expected behaviour.
 
-    // we shouldn't destroy the control ourselves according to MSDN, which
-    // proposes WM_CANCELMODE to do this, but it doesn't seem to work
-    //
-    // posting WM_CLOSE to it does seem to work without any side effects
-    ::PostMessage(hwnd, WM_CLOSE, 0, 0);
+    // We shouldn't destroy the control ourselves according to MSDN, which
+    // proposes WM_CANCELMODE to do this, but it doesn't seem to work so
+    // emulate the corresponding user action instead.
+    ::SendMessage(hwnd, WM_KEYDOWN, cancel ? VK_ESCAPE : VK_RETURN, 0);
 
     return true;
 }
@@ -1424,34 +1542,53 @@ long wxListCtrl::FindItem(long start, const wxString& str, bool partial)
     findInfo.flags = LVFI_STRING;
     if ( partial )
         findInfo.flags |= LVFI_PARTIAL;
-    findInfo.psz = str;
+    findInfo.psz = str.t_str();
 
     // ListView_FindItem() excludes the first item from search and to look
     // through all the items you need to start from -1 which is unnatural and
     // inconsistent with the generic version - so we adjust the index
     if (start != -1)
         start --;
-    return ListView_FindItem(GetHwnd(), (int) start, &findInfo);
+    return ListView_FindItem(GetHwnd(), start, &findInfo);
 }
 
-// Find an item whose data matches this data, starting from the item after 'start'
-// or the beginning if 'start' is -1.
-// NOTE : Lindsay Mathieson - 14-July-2002
-//        No longer use ListView_FindItem as the data attribute is now stored
-//        in a wxListItemInternalData structure refernced by the actual lParam
+// Find an item whose data matches this data, starting from the item after
+// 'start' or the beginning if 'start' is -1.
 long wxListCtrl::FindItem(long start, wxUIntPtr data)
 {
-    long  idx = start + 1;
-    long count = GetItemCount();
-
-    while (idx < count)
+    // we can't use ListView_FindItem() directly as we don't store the data
+    // pointer itself in the control but rather our own internal data, so first
+    // we need to find the right value to search for (and there can be several
+    // of them)
+    int idx = wxNOT_FOUND;
+    const unsigned count = m_internalData.size();
+    for ( unsigned n = 0; n < count; n++ )
     {
-        if (GetItemData(idx) == data)
-            return idx;
-        idx++;
-    };
+        if ( m_internalData[n]->lParam == (LPARAM)data )
+        {
+            LV_FINDINFO findInfo;
+            findInfo.flags = LVFI_PARAM;
+            findInfo.lParam = (LPARAM)wxPtrToUInt(m_internalData[n]);
 
-    return -1;
+            int rc = ListView_FindItem(GetHwnd(), start, &findInfo);
+            if ( rc != -1 )
+            {
+                if ( idx == wxNOT_FOUND || rc < idx )
+                {
+                    idx = rc;
+                    if ( idx == start + 1 )
+                    {
+                        // we can stop here, we don't risk finding a closer
+                        // match
+                        break;
+                    }
+                }
+                //else: this item is after the previously found one
+            }
+        }
+    }
+
+    return idx;
 }
 
 // Find an item nearest this position in the specified direction, starting from
@@ -1474,7 +1611,7 @@ long wxListCtrl::FindItem(long start, const wxPoint& pt, int direction)
     else if ( direction == wxLIST_FIND_RIGHT )
         findInfo.vkDirection = VK_RIGHT;
 
-    return ListView_FindItem(GetHwnd(), (int) start, & findInfo);
+    return ListView_FindItem(GetHwnd(), start, &findInfo);
 }
 
 // Determines which item (if any) is at the specified point,
@@ -1540,24 +1677,30 @@ wxListCtrl::HitTest(const wxPoint& point, int& flags, long *ptrSubItem) const
 // -1 otherwise.
 long wxListCtrl::InsertItem(const wxListItem& info)
 {
-    wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual controls") );
+    wxASSERT_MSG( !IsVirtual(), wxT("can't be used with virtual controls") );
+
+    // In 2.8 it was possible to succeed inserting an item without initializing
+    // its ID as it defaulted to 0. This was however never supported and in 2.9
+    // the ID is -1 by default and inserting it simply fails, but it might be
+    // not obvious why does it happen, so check it proactively.
+    wxASSERT_MSG( info.m_itemId != -1, wxS("Item ID must be set.") );
 
     LV_ITEM item;
     wxConvertToMSWListItem(this, info, item);
     item.mask &= ~LVIF_PARAM;
 
-    // check wether we need to allocate our internal data
-    bool needInternalData = ((info.m_mask & wxLIST_MASK_DATA) || info.HasAttributes());
-    if (needInternalData)
+    // check whether we need to allocate our internal data
+    bool needInternalData = (info.m_mask & wxLIST_MASK_DATA) ||
+                                info.HasAttributes();
+    if ( needInternalData )
     {
-        m_AnyInternalData = true;
         item.mask |= LVIF_PARAM;
 
-        // internal stucture that manages data
-        wxListItemInternalData *data = new wxListItemInternalData();
-        item.lParam = (LPARAM) data;
+        wxMSWListItemData * const data = new wxMSWListItemData;
+        m_internalData.push_back(data);
+        item.lParam = (LPARAM)data;
 
-        if (info.m_mask & wxLIST_MASK_DATA)
+        if ( info.m_mask & wxLIST_MASK_DATA )
             data->lParam = info.m_data;
 
         // check whether it has any custom attributes
@@ -1569,9 +1712,12 @@ long wxListCtrl::InsertItem(const wxListItem& info)
             // and remember that we have some now...
             m_hasAnyAttr = true;
         }
-    };
+    }
 
-    long rv = ListView_InsertItem(GetHwnd(), & item);
+    const long rv = ListView_InsertItem(GetHwnd(), & item);
+
+    // failing to insert the item is really unexpected
+    wxCHECK_MSG( rv != -1, rv, "failed to insert an item in wxListCtrl" );
 
     m_count++;
     wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
@@ -1605,18 +1751,27 @@ long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex)
     wxListItem info;
     info.m_image = imageIndex;
     info.m_text = label;
-    info.m_mask = wxLIST_MASK_IMAGE | wxLIST_MASK_TEXT;
+    info.m_mask = wxLIST_MASK_TEXT;
+    if (imageIndex > -1)
+        info.m_mask |= wxLIST_MASK_IMAGE;
     info.m_itemId = index;
     return InsertItem(info);
 }
 
 // For list view mode (only), inserts a column.
-long wxListCtrl::InsertColumn(long col, const wxListItem& item)
+long wxListCtrl::DoInsertColumn(long col, const wxListItem& item)
 {
     LV_COLUMN lvCol;
     wxConvertToMSWListCol(GetHwnd(), col, item, lvCol);
 
-    if ( !(lvCol.mask & LVCF_WIDTH) )
+    // LVSCW_AUTOSIZE_USEHEADER is not supported when inserting new column,
+    // we'll deal with it below instead. Plain LVSCW_AUTOSIZE is not supported
+    // neither but it doesn't need any special handling as we use fixed value
+    // for it here, both because we can't do anything else (there are no items
+    // with values in this column to compute the size from yet) and for
+    // compatibility as wxLIST_AUTOSIZE == -1 and -1 as InsertColumn() width
+    // parameter used to mean "arbitrary fixed width".
+    if ( !(lvCol.mask & LVCF_WIDTH) || lvCol.cx < 0 )
     {
         // always give some width to the new column: this one is compatible
         // with the generic version
@@ -1625,35 +1780,23 @@ long wxListCtrl::InsertColumn(long col, const wxListItem& item)
     }
 
     long n = ListView_InsertColumn(GetHwnd(), col, &lvCol);
-    if ( n != -1 )
-    {
-        m_colCount++;
-    }
-    else // failed to insert?
+    if ( n == -1 )
     {
         wxLogDebug(wxT("Failed to insert the column '%s' into listview!"),
                    lvCol.pszText);
+        return -1;
     }
 
-    return n;
-}
+    m_colCount++;
 
-long wxListCtrl::InsertColumn(long col,
-                              const wxString& heading,
-                              int format,
-                              int width)
-{
-    wxListItem item;
-    item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
-    item.m_text = heading;
-    if ( width > -1 )
+    // Now adjust the new column size.
+    if ( (item.GetMask() & wxLIST_MASK_WIDTH) &&
+            (item.GetWidth() == wxLIST_AUTOSIZE_USEHEADER) )
     {
-        item.m_mask |= wxLIST_MASK_WIDTH;
-        item.m_width = width;
+        SetColumnWidth(n, wxLIST_AUTOSIZE_USEHEADER);
     }
-    item.m_format = format;
 
-    return InsertColumn(col, item);
+    return n;
 }
 
 // scroll the control by the given number of pixels (exception: in list view,
@@ -1662,7 +1805,7 @@ bool wxListCtrl::ScrollList(int dx, int dy)
 {
     if ( !ListView_Scroll(GetHwnd(), dx, dy) )
     {
-        wxLogDebug(_T("ListView_Scroll(%d, %d) failed"), dx, dy);
+        wxLogDebug(wxT("ListView_Scroll(%d, %d) failed"), dx, dy);
 
         return false;
     }
@@ -1689,26 +1832,26 @@ bool wxListCtrl::ScrollList(int dx, int dy)
 struct wxInternalDataSort
 {
     wxListCtrlCompare user_fn;
-    long data;
+    wxIntPtr data;
 };
 
 int CALLBACK wxInternalDataCompareFunc(LPARAM lParam1, LPARAM lParam2,  LPARAM lParamSort)
 {
-    struct wxInternalDataSort *internalData = (struct wxInternalDataSort *) lParamSort;
+    wxInternalDataSort * const internalData = (wxInternalDataSort *) lParamSort;
 
-    wxListItemInternalData *data1 = (wxListItemInternalData *) lParam1;
-    wxListItemInternalData *data2 = (wxListItemInternalData *) lParam2;
+    wxMSWListItemData *data1 = (wxMSWListItemData *) lParam1;
+    wxMSWListItemData *data2 = (wxMSWListItemData *) lParam2;
 
-    long d1 = (data1 == NULL ? 0 : data1->lParam);
-    long d2 = (data2 == NULL ? 0 : data2->lParam);
+    wxIntPtr d1 = (data1 == NULL ? 0 : data1->lParam);
+    wxIntPtr d2 = (data2 == NULL ? 0 : data2->lParam);
 
     return internalData->user_fn(d1, d2, internalData->data);
 
 }
 
-bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data)
+bool wxListCtrl::SortItems(wxListCtrlCompare fn, wxIntPtr data)
 {
-    struct wxInternalDataSort internalData;
+    wxInternalDataSort internalData;
     internalData.user_fn = fn;
     internalData.data = data;
 
@@ -1717,7 +1860,7 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data)
                              wxInternalDataCompareFunc,
                              (WPARAM) &internalData) )
     {
-        wxLogDebug(_T("ListView_SortItems() failed"));
+        wxLogDebug(wxT("ListView_SortItems() failed"));
 
         return false;
     }
@@ -1735,22 +1878,20 @@ bool wxListCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
 {
     if ( msg->message == WM_KEYDOWN )
     {
-        if ( msg->wParam == VK_RETURN )
+        // Only eat VK_RETURN if not being used by the application in
+        // conjunction with modifiers
+        if ( msg->wParam == VK_RETURN && !wxIsAnyModifierDown() )
         {
-            // We need VK_RETURN to generate wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
-            // but only if none of the modifiers is down.  We'll let normal
-            // accelerators handle those.
-            if ( !wxIsCtrlDown() && !wxIsCtrlDown() &&
-                 !((HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN))
+            // we need VK_RETURN to generate wxEVT_COMMAND_LIST_ITEM_ACTIVATED
             return false;
         }
     }
-
-    return wxControl::MSWShouldPreProcessMessage(msg);
+    return wxListCtrlBase::MSWShouldPreProcessMessage(msg);
 }
 
-bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id)
+bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id_)
 {
+    const int id = (signed short)id_;
     if (cmd == EN_UPDATE)
     {
         wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
@@ -1772,38 +1913,42 @@ bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id)
 // utility used by wxListCtrl::MSWOnNotify and by wxDataViewHeaderWindowMSW::MSWOnNotify
 int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick)
 {
-    wxASSERT(nmhdr && ptClick);
-
-    // find the column clicked: we have to search for it
-    // ourselves as the notification message doesn't provide
-    // this info
+    // find the column clicked: we have to search for it ourselves as the
+    // notification message doesn't provide this info
 
     // where did the click occur?
 #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400
-    if (nmhdr->code == GN_CONTEXTMENU) 
+    if ( nmhdr->code == GN_CONTEXTMENU )
     {
         *ptClick = ((NMRGINFO*)nmhdr)->ptAction;
-    } 
+    }
     else
 #endif //__WXWINCE__
-    if ( !::GetCursorPos(ptClick) )
     {
-        wxLogLastError(_T("GetCursorPos"));
+       wxGetCursorPosMSW(ptClick);
     }
 
-    if ( !::ScreenToClient(nmhdr->hwndFrom, ptClick) )
+    // we need to use listctrl coordinates for the event point so this is what
+    // we return in ptClick, but for comparison with Header_GetItemRect()
+    // result below we need to use header window coordinates
+    POINT ptClickHeader = *ptClick;
+    if ( !::ScreenToClient(nmhdr->hwndFrom, &ptClickHeader) )
     {
-        wxLogLastError(_T("ScreenToClient(header)"));
+        wxLogLastError(wxT("ScreenToClient(listctrl header)"));
     }
 
-    int colCount = Header_GetItemCount(nmhdr->hwndFrom);
+    if ( !::ScreenToClient(::GetParent(nmhdr->hwndFrom), ptClick) )
+    {
+        wxLogLastError(wxT("ScreenToClient(listctrl)"));
+    }
 
-    RECT rect;
+    const int colCount = Header_GetItemCount(nmhdr->hwndFrom);
     for ( int col = 0; col < colCount; col++ )
     {
+        RECT rect;
         if ( Header_GetItemRect(nmhdr->hwndFrom, col, &rect) )
         {
-            if ( ::PtInRect(&rect, *ptClick) )
+            if ( ::PtInRect(&rect, ptClickHeader) )
             {
                 return col;
             }
@@ -1841,29 +1986,50 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
         event.m_itemIndex = -1;
 
+        bool ignore = false;
         switch ( nmhdr->code )
         {
             // yet another comctl32.dll bug: under NT/W2K it sends Unicode
             // TRACK messages even to ANSI programs: on my system I get
-            // HDN_BEGINTRACKW and HDN_ENDTRACKA and no HDN_TRACK at all!
+            // HDN_BEGINTRACKW and HDN_ENDTRACKA!
             //
             // work around is to simply catch both versions and hope that it
             // works (why should this message exist in ANSI and Unicode is
             // beyond me as it doesn't deal with strings at all...)
             //
-            // note that fr HDN_TRACK another possibility could be to use
-            // HDN_ITEMCHANGING but it is sent even after HDN_ENDTRACK and when
-            // something other than the item width changes so we'd have to
-            // filter out the unwanted events then
+            // another problem is that HDN_TRACK is not sent at all by header
+            // with HDS_FULLDRAG style which is used by default by wxListCtrl
+            // under recent Windows versions (starting from at least XP) so we
+            // need to use HDN_ITEMCHANGING instead of it
             case HDN_BEGINTRACKA:
             case HDN_BEGINTRACKW:
                 eventType = wxEVT_COMMAND_LIST_COL_BEGIN_DRAG;
                 // fall through
 
-            case HDN_TRACKA:
-            case HDN_TRACKW:
+            case HDN_ITEMCHANGING:
                 if ( eventType == wxEVT_NULL )
+                {
+                    if ( !nmHDR->pitem || !(nmHDR->pitem->mask & HDI_WIDTH) )
+                    {
+                        // something other than the width is being changed,
+                        // ignore it
+                        ignore = true;
+                        break;
+                    }
+
+                    // also ignore the events sent when the width didn't really
+                    // change: this is not just an optimization but also gets
+                    // rid of a useless and unexpected DRAGGING event which
+                    // would otherwise be sent after the END_DRAG one as we get
+                    // an HDN_ITEMCHANGING after HDN_ENDTRACK for some reason
+                    if ( nmHDR->pitem->cxy == GetColumnWidth(nmHDR->iItem) )
+                    {
+                        ignore = true;
+                        break;
+                    }
+
                     eventType = wxEVT_COMMAND_LIST_COL_DRAGGING;
+                }
                 // fall through
 
             case HDN_ENDTRACKA:
@@ -1901,8 +2067,11 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                 return true;
 
             default:
-                return wxControl::MSWOnNotify(idCtrl, lParam, result);
+                ignore = true;
         }
+
+        if ( ignore )
+            return wxListCtrlBase::MSWOnNotify(idCtrl, lParam, result);
     }
     else
 #endif // defined(HDN_BEGINTRACKA)
@@ -1914,23 +2083,12 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
         const int iItem = nmLV->iItem;
 
 
-        // FreeAllInternalData will cause LVN_ITEMCHANG* messages, which can be
-        // ignored for efficiency.  It is done here because the internal data is in the
-        // process of being deleted so we don't want to try and access it below.
-        if ( m_ignoreChangeMessages &&
-             ( (nmLV->hdr.code == LVN_ITEMCHANGED) ||
-               (nmLV->hdr.code == LVN_ITEMCHANGING)) )
-        {
-            return true;
-        }
-
-
         // If we have a valid item then check if there is a data value
         // associated with it and put it in the event.
         if ( iItem >= 0 && iItem < GetItemCount() )
         {
-            wxListItemInternalData *internaldata =
-                wxGetInternalData(GetHwnd(), iItem);
+            wxMSWListItemData *internaldata =
+                MSWGetItemData(iItem);
 
             if ( internaldata )
                 event.m_item.m_data = internaldata->lParam;
@@ -1993,16 +2151,9 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     const LV_ITEM& lvi = (LV_ITEM)item;
                     if ( !lvi.pszText || lvi.iItem == -1 )
                     {
-                        // don't keep a stale wxTextCtrl around
-                        if ( m_textCtrl )
-                        {
-                            // EDIT control will be deleted by the list control itself so
-                            // prevent us from deleting it as well
-                            m_textCtrl->UnsubclassWin();
-                            m_textCtrl->SetHWND(0);
-                            delete m_textCtrl;
-                            m_textCtrl = NULL;
-                        }
+                        // EDIT control will be deleted by the list control
+                        // itself so prevent us from deleting it as well
+                        DeleteEditControl();
 
                         event.SetEditCanceled(true);
                     }
@@ -2034,8 +2185,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
                 eventType = wxEVT_COMMAND_LIST_DELETE_ITEM;
                 event.m_itemIndex = iItem;
-                // delete the assoicated internal data
-                wxDeleteInternalData(this, iItem);
+
                 break;
 
             case LVN_INSERTITEM:
@@ -2077,7 +2227,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                             // focus event from here and the selection one
                             // below
                             event.SetEventType(eventType);
-                            (void)GetEventHandler()->ProcessEvent(event);
+                            (void)HandleWindowEvent(event);
                         }
                         else // no focus event to send
                         {
@@ -2111,11 +2261,11 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                                              wxLIST_STATE_SELECTED);
 
                     // <Enter> or <Space> activate the selected item if any (but
-                    // not with Shift and/or Ctrl as then they have a predefined
-                    // meaning for the list view)
+                    // not with any modifiers as they have a predefined meaning
+                    // then)
                     if ( lItem != -1 &&
                          (wVKey == VK_RETURN || wVKey == VK_SPACE) &&
-                         !(wxIsShiftDown() || wxIsCtrlDown()) )
+                         !wxIsAnyModifierDown() )
                     {
                         eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
                     }
@@ -2123,10 +2273,15 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     {
                         eventType = wxEVT_COMMAND_LIST_KEY_DOWN;
 
-                        // wxCharCodeMSWToWX() returns 0 if the key is an ASCII
-                        // value which should be used as is
-                        int code = wxCharCodeMSWToWX(wVKey);
-                        event.m_code = code ? code : wVKey;
+                        event.m_code = wxMSWKeyboard::VKToWX(wVKey);
+
+                        if ( event.m_code == WXK_NONE )
+                        {
+                            // We can't translate this to a standard key code,
+                            // until support for Unicode key codes is added to
+                            // wxListEvent we just ignore them.
+                            return false;
+                        }
                     }
 
                     event.m_itemIndex =
@@ -2144,7 +2299,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             case NM_DBLCLK:
                 // if the user processes it in wxEVT_COMMAND_LEFT_CLICK(), don't do
                 // anything else
-                if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
+                if ( wxListCtrlBase::MSWOnNotify(idCtrl, lParam, result) )
                 {
                     return true;
                 }
@@ -2169,7 +2324,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             case NM_RCLICK:
                 // if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(),
                 // don't do anything else
-                if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
+                if ( wxListCtrlBase::MSWOnNotify(idCtrl, lParam, result) )
                 {
                     return true;
                 }
@@ -2179,13 +2334,18 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                 wxZeroMemory(lvhti);
 
 #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400
-              if(nmhdr->code == GN_CONTEXTMENU) {
-                  lvhti.pt = ((NMRGINFO*)nmhdr)->ptAction;
-              } else
+                if ( nmhdr->code == GN_CONTEXTMENU )
+                {
+                    lvhti.pt = ((NMRGINFO*)nmhdr)->ptAction;
+                }
+                else
 #endif //__WXWINCE__
-                ::GetCursorPos(&(lvhti.pt));
-                ::ScreenToClient(GetHwnd(),&(lvhti.pt));
-                if ( ListView_HitTest(GetHwnd(),&lvhti) != -1 )
+                {
+                    wxGetCursorPosMSW(&(lvhti.pt));
+                }
+
+                ::ScreenToClient(GetHwnd(), &lvhti.pt);
+                if ( ListView_HitTest(GetHwnd(), &lvhti) != -1 )
                 {
                     if ( lvhti.flags & LVHT_ONITEM )
                     {
@@ -2227,12 +2387,8 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
 #ifdef HAVE_NMLVFINDITEM
             case LVN_ODFINDITEM:
-                // this message is only used with the virtual list control but
-                // even there we don't want to always use it: in a control with
-                // sufficiently big number of items (defined as > 1000 here),
-                // accidentally pressing a key could result in hanging an
-                // application waiting while it performs linear search
-                if ( IsVirtual() && GetItemCount() <= 1000 )
+                // Find an item in a (necessarily virtual) list control.
+                if ( IsVirtual() )
                 {
                     NMLVFINDITEM* pFindInfo = (NMLVFINDITEM*)lParam;
 
@@ -2252,22 +2408,34 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
                     // this is the first item we should examine, search from it
                     // wrapping if necessary
-                    const int startPos = pFindInfo->iStart;
+                    int startPos = pFindInfo->iStart;
                     const int maxPos = GetItemCount();
-                    wxCHECK_MSG( startPos <= maxPos, false,
-                                 _T("bad starting position in LVN_ODFINDITEM") );
 
-                    int currentPos = startPos;
-                    do
+                    // Check that the index is valid to ensure that our loop
+                    // below always terminates.
+                    if ( startPos < 0 || startPos >= maxPos )
                     {
-                        // wrap to the beginning if necessary
-                        if ( currentPos == maxPos )
+                        // When the last item in the control is selected,
+                        // iStart is really set to (invalid) maxPos index so
+                        // accept this silently.
+                        if ( startPos != maxPos )
                         {
-                            // somewhat surprizingly, LVFI_WRAP isn't set in
-                            // flags but we still should wrap
-                            currentPos = 0;
+                            wxLogDebug(wxT("Ignoring invalid search start ")
+                                       wxT("position %d in list control with ")
+                                       wxT("%d items."), startPos, maxPos);
                         }
 
+                        startPos = 0;
+                    }
+
+                    // Linear search in a control with a lot of items can take
+                    // a long time so we limit the total time of the search to
+                    // ensure that the program doesn't appear to hang.
+#if wxUSE_STOPWATCH
+                    wxStopWatch sw;
+#endif // wxUSE_STOPWATCH
+                    for ( int currentPos = startPos; ; )
+                    {
                         // does this item begin with searchstr?
                         if ( wxStrnicmp(searchstr,
                                             GetItemText(currentPos), len) == 0 )
@@ -2275,13 +2443,46 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                             *result = currentPos;
                             break;
                         }
-                    }
-                    while ( ++currentPos != startPos );
 
-                    if ( *result == -1 )
-                    {
-                        // not found
-                        return false;
+                        // Go to next item with wrapping if necessary.
+                        if ( ++currentPos == maxPos )
+                        {
+                            // Surprisingly, LVFI_WRAP seems to be never set in
+                            // the flags so wrap regardless of it.
+                            currentPos = 0;
+                        }
+
+                        if ( currentPos == startPos )
+                        {
+                            // We examined all items without finding anything.
+                            //
+                            // Notice that we still return true as we did
+                            // perform the search, if we didn't do this the
+                            // message would have been considered unhandled and
+                            // the control seems to always select the first
+                            // item by default in this case.
+                            return true;
+                        }
+
+#if wxUSE_STOPWATCH
+                        // Check the time elapsed only every thousand
+                        // iterations for performance reasons: if we did it
+                        // more often calling wxStopWatch::Time() could take
+                        // noticeable time on its own.
+                        if ( !((currentPos - startPos)%1000) )
+                        {
+                            // We use half a second to limit the search time
+                            // which is about as long as we can take without
+                            // annoying the user.
+                            if ( sw.Time() > 500 )
+                            {
+                                // As above, return true to prevent the control
+                                // from selecting the first item by default.
+                                return true;
+                            }
+                        }
+#endif // wxUSE_STOPWATCH
+
                     }
 
                     SetItemState(*result,
@@ -2308,7 +2509,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     if ( lvi.mask & LVIF_TEXT )
                     {
                         wxString text = OnGetItemText(item, lvi.iSubItem);
-                        wxStrncpy(lvi.pszText, text, lvi.cchTextMax);
+                        wxStrlcpy(lvi.pszText, text.c_str(), lvi.cchTextMax);
                     }
 
                     // see comment at the end of wxListCtrl::GetColumn()
@@ -2336,7 +2537,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
         }
 
         if ( !processed )
-            return wxControl::MSWOnNotify(idCtrl, lParam, result);
+            return wxListCtrlBase::MSWOnNotify(idCtrl, lParam, result);
     }
     else
     {
@@ -2349,7 +2550,22 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
     event.SetEventType(eventType);
 
-    bool processed = GetEventHandler()->ProcessEvent(event);
+    // fill in the item before passing it to the event handler if we do have a
+    // valid item index and haven't filled it yet (e.g. for LVN_ITEMCHANGED)
+    // and we're not using a virtual control as in this case the program
+    // already has the data anyhow and we don't want to call GetItem() for
+    // potentially many items
+    if ( event.m_itemIndex != -1 && !event.m_item.GetMask()
+            && !IsVirtual() )
+    {
+        wxListItem& item = event.m_item;
+
+        item.SetId(event.m_itemIndex);
+        item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA);
+        GetItem(item);
+    }
+
+    bool processed = HandleWindowEvent(event);
 
     // post processing
     // ---------------
@@ -2370,21 +2586,35 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             m_count = 0;
             return true;
 
+        case LVN_DELETEITEM:
+            // Delete the associated internal data. Notice that this can be
+            // done only after the event has been handled as the data could be
+            // accessed during the handling of the event.
+            if ( wxMSWListItemData *data = MSWGetItemData(event.m_itemIndex) )
+            {
+                const unsigned count = m_internalData.size();
+                for ( unsigned n = 0; n < count; n++ )
+                {
+                    if ( m_internalData[n] == data )
+                    {
+                        m_internalData.erase(m_internalData.begin() + n);
+                        wxDELETE(data);
+                        break;
+                    }
+                }
+
+                wxASSERT_MSG( !data, "invalid internal data pointer?" );
+            }
+            break;
+
         case LVN_ENDLABELEDITA:
         case LVN_ENDLABELEDITW:
             // logic here is inverted compared to all the other messages
             *result = event.IsAllowed();
 
-            // don't keep a stale wxTextCtrl around
-            if ( m_textCtrl )
-            {
-                // EDIT control will be deleted by the list control itself so
-                // prevent us from deleting it as well
-                m_textCtrl->UnsubclassWin();
-                m_textCtrl->SetHWND(0);
-                delete m_textCtrl;
-                m_textCtrl = NULL;
-            }
+            // EDIT control will be deleted by the list control itself so
+            // prevent us from deleting it as well
+            DeleteEditControl();
 
             return true;
     }
@@ -2405,10 +2635,10 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 static RECT GetCustomDrawnItemRect(const NMCUSTOMDRAW& nmcd)
 {
     RECT rc;
-    ListView_GetItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, &rc, LVIR_BOUNDS);
+    wxGetListCtrlItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, LVIR_BOUNDS, rc);
 
     RECT rcIcon;
-    ListView_GetItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, &rcIcon, LVIR_ICON);
+    wxGetListCtrlItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, LVIR_ICON, rcIcon);
 
     // exclude the icon part, neither the selection background nor focus rect
     // should cover it
@@ -2417,28 +2647,28 @@ static RECT GetCustomDrawnItemRect(const NMCUSTOMDRAW& nmcd)
     return rc;
 }
 
-static void HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
+static
+bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount)
 {
     NMCUSTOMDRAW& nmcd = pLVCD->nmcd;
 
     HDC hdc = nmcd.hdc;
     HWND hwndList = nmcd.hdr.hwndFrom;
+    const int col = pLVCD->iSubItem;
     const DWORD item = nmcd.dwItemSpec;
 
-
     // the font must be valid, otherwise we wouldn't be painting the item at all
     SelectInHDC selFont(hdc, hfont);
 
     // get the rectangle to paint
     RECT rc;
-    ListView_GetSubItemRect(hwndList, item, pLVCD->iSubItem, LVIR_BOUNDS, &rc);
-    if ( !pLVCD->iSubItem )
+    wxGetListCtrlSubItemRect(hwndList, item, col, LVIR_BOUNDS, rc);
+    if ( !col && colCount > 1 )
     {
-        // broken ListView_GetSubItemRect() returns the entire item rect for
-        // 0th subitem while we really need just the part for this column
+        // ListView_GetSubItemRect() returns the entire item rect for 0th
+        // subitem while we really need just the part for this column
         RECT rc2;
-        ListView_GetSubItemRect(hwndList, item, 1, LVIR_BOUNDS, &rc2);
-
+        wxGetListCtrlSubItemRect(hwndList, item, 1, LVIR_BOUNDS, rc2);
         rc.right = rc2.left;
         rc.left += 4;
     }
@@ -2453,7 +2683,7 @@ static void HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
     wxZeroMemory(it);
     it.mask = LVIF_TEXT | LVIF_IMAGE;
     it.iItem = item;
-    it.iSubItem = pLVCD->iSubItem;
+    it.iSubItem = col;
     it.pszText = text;
     it.cchTextMax = WXSIZEOF(text);
     ListView_GetItem(hwndList, &it);
@@ -2483,12 +2713,38 @@ static void HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
 
     ::SetBkMode(hdc, TRANSPARENT);
 
-    // TODO: support for centred/right aligned columns
-    ::DrawText(hdc, text, -1, &rc,
+    UINT fmt = DT_SINGLELINE |
 #ifndef __WXWINCE__
                DT_WORD_ELLIPSIS |
 #endif // __WXWINCE__
-               DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER);
+               DT_NOPREFIX |
+               DT_VCENTER;
+
+    LV_COLUMN lvCol;
+    wxZeroMemory(lvCol);
+    lvCol.mask = LVCF_FMT;
+    if ( ListView_GetColumn(hwndList, col, &lvCol) )
+    {
+        switch ( lvCol.fmt & LVCFMT_JUSTIFYMASK )
+        {
+            case LVCFMT_LEFT:
+                fmt |= DT_LEFT;
+                break;
+
+            case LVCFMT_CENTER:
+                fmt |= DT_CENTER;
+                break;
+
+            case LVCFMT_RIGHT:
+                fmt |= DT_RIGHT;
+                break;
+        }
+    }
+    //else: failed to get alignment, assume it's DT_LEFT (default)
+
+    DrawText(hdc, text, -1, &rc, fmt);
+
+    return true;
 }
 
 static void HandleItemPostpaint(NMCUSTOMDRAW nmcd)
@@ -2532,8 +2788,12 @@ static void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
     }
 
     // same thing for CDIS_FOCUS (except simpler as there is only one of them)
+    //
+    // NB: cast is needed to work around the bug in mingw32 headers which don't
+    //     have it inside ListView_GetNextItem() itself (unlike SDK ones)
     if ( ::GetFocus() == hwndList &&
-            ListView_GetNextItem(hwndList, (WPARAM)-1, LVNI_FOCUSED) == item )
+            ListView_GetNextItem(
+                hwndList, static_cast<WPARAM>(-1), LVNI_FOCUSED) == item )
     {
         nmcd.uItemState |= CDIS_FOCUS;
     }
@@ -2576,7 +2836,7 @@ static void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
     for ( int col = 0; col < colCount; col++ )
     {
         pLVCD->iSubItem = col;
-        HandleSubItemPrepaint(pLVCD, hfont);
+        HandleSubItemPrepaint(pLVCD, hfont, colCount);
     }
 
     HandleItemPostpaint(nmcd);
@@ -2640,14 +2900,22 @@ WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam)
             break;
 
         case CDDS_ITEMPREPAINT:
+            // get a message for each subitem
+            return CDRF_NOTIFYITEMDRAW;
+
+        case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
             const int item = nmcd.dwItemSpec;
+            const int column = pLVCD->iSubItem;
 
             // we get this message with item == 0 for an empty control, we
             // must ignore it as calling OnGetItemAttr() would be wrong
             if ( item < 0 || item >= GetItemCount() )
                 break;
+            // same for columns
+            if ( column < 0 || column >= GetColumnCount() )
+                break;
 
-            return HandleItemPrepaint(this, pLVCD, DoGetItemAttr(item));
+            return HandleItemPrepaint(this, pLVCD, DoGetItemColumnAttr(item, column));
     }
 
     return CDRF_DODEFAULT;
@@ -2658,10 +2926,11 @@ WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam)
 // Necessary for drawing hrules and vrules, if specified
 void wxListCtrl::OnPaint(wxPaintEvent& event)
 {
-    bool drawHRules = HasFlag(wxLC_HRULES);
-    bool drawVRules = HasFlag(wxLC_VRULES);
+    const int itemCount = GetItemCount();
+    const bool drawHRules = HasFlag(wxLC_HRULES);
+    const bool drawVRules = HasFlag(wxLC_VRULES);
 
-    if (!InReportView() || !drawHRules && !drawVRules)
+    if (!InReportView() || !(drawHRules || drawVRules) || !itemCount)
     {
         event.Skip();
         return;
@@ -2669,24 +2938,22 @@ void wxListCtrl::OnPaint(wxPaintEvent& event)
 
     wxPaintDC dc(this);
 
-    wxControl::OnPaint(event);
+    wxListCtrlBase::OnPaint(event);
 
     // Reset the device origin since it may have been set
     dc.SetDeviceOrigin(0, 0);
 
-    wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT), 1, wxSOLID);
+    wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT));
     dc.SetPen(pen);
     dc.SetBrush(* wxTRANSPARENT_BRUSH);
 
     wxSize clientSize = GetClientSize();
     wxRect itemRect;
 
-    int itemCount = GetItemCount();
-    int i;
     if (drawHRules)
     {
-        long top = GetTopItem();
-        for (i = top; i < top + GetCountPerPage() + 1; i++)
+        const long top = GetTopItem();
+        for ( int i = top; i < top + GetCountPerPage() + 1; i++ )
         {
             if (GetItemRect(i, itemRect))
             {
@@ -2700,17 +2967,18 @@ void wxListCtrl::OnPaint(wxPaintEvent& event)
                 {
                     cy = itemRect.GetBottom();
                     dc.DrawLine(0, cy, clientSize.x, cy);
+                    break;
                 }
             }
         }
     }
-    i = itemCount - 1;
-    if (drawVRules && (i > -1))
+
+    if (drawVRules)
     {
         wxRect firstItemRect;
         GetItemRect(0, firstItemRect);
 
-        if (GetItemRect(i, itemRect))
+        if (GetItemRect(itemCount - 1, itemRect))
         {
             // this is a fix for bug 673394: erase the pixels which we would
             // otherwise leave on the screen
@@ -2722,10 +2990,21 @@ void wxListCtrl::OnPaint(wxPaintEvent& event)
 
             dc.SetPen(pen);
             dc.SetBrush(*wxTRANSPARENT_BRUSH);
+
+            const int numCols = GetColumnCount();
+            wxVector<int> indexArray(numCols);
+            if ( !ListView_GetColumnOrderArray(GetHwnd(),
+                                               numCols,
+                                               &indexArray[0]) )
+            {
+                wxFAIL_MSG( wxT("invalid column index array in OnPaint()") );
+                return;
+            }
+
             int x = itemRect.GetX();
-            for (int col = 0; col < GetColumnCount(); col++)
+            for (int col = 0; col < numCols; col++)
             {
-                int colWidth = GetColumnWidth(col);
+                int colWidth = GetColumnWidth(indexArray[col]);
                 x += colWidth ;
                 dc.DrawLine(x-1, firstItemRect.GetY() - gap,
                             x-1, itemRect.GetBottom());
@@ -2734,6 +3013,27 @@ void wxListCtrl::OnPaint(wxPaintEvent& event)
     }
 }
 
+void wxListCtrl::OnCharHook(wxKeyEvent& event)
+{
+    if ( GetEditControl() )
+    {
+        // We need to ensure that Escape is not stolen from the in-place editor
+        // by the containing dialog.
+        //
+        // Notice that we don't have to care about Enter key here as we return
+        // false from MSWShouldPreProcessMessage() for it.
+        if ( event.GetKeyCode() == WXK_ESCAPE )
+        {
+            EndEditLabel(true /* cancel */);
+
+            // Don't call Skip() below.
+            return;
+        }
+    }
+
+    event.Skip();
+}
+
 WXLRESULT
 wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
 {
@@ -2757,7 +3057,7 @@ wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
             //else: break
     }
 
-    return wxControl::MSWWindowProc(nMsg, wParam, lParam);
+    return wxListCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
 }
 
 // ----------------------------------------------------------------------------
@@ -2768,7 +3068,7 @@ wxString wxListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) cons
 {
     // this is a pure virtual function, in fact - which is not really pure
     // because the controls which are not virtual don't need to implement it
-    wxFAIL_MSG( _T("wxListCtrl::OnGetItemText not supposed to be called") );
+    wxFAIL_MSG( wxT("wxListCtrl::OnGetItemText not supposed to be called") );
 
     return wxEmptyString;
 }
@@ -2789,29 +3089,23 @@ int wxListCtrl::OnGetItemColumnImage(long item, long column) const
     return -1;
 }
 
-wxListItemAttr *wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
+wxListItemAttr *wxListCtrl::DoGetItemColumnAttr(long item, long column) const
 {
-    wxASSERT_MSG( item >= 0 && item < GetItemCount(),
-                  _T("invalid item index in OnGetItemAttr()") );
-
-    // no attributes by default
-    return NULL;
-}
+    if ( IsVirtual() )
+        return OnGetItemColumnAttr(item, column);
 
-wxListItemAttr *wxListCtrl::DoGetItemAttr(long item) const
-{
-    return IsVirtual() ? OnGetItemAttr(item)
-                       : wxGetInternalDataAttr(this, item);
+    wxMSWListItemData * const data = MSWGetItemData(item);
+    return data ? data->attr : NULL;
 }
 
 void wxListCtrl::SetItemCount(long count)
 {
-    wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") );
+    wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") );
 
     if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count,
                         LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL) )
     {
-        wxLogLastError(_T("ListView_SetItemCount"));
+        wxLogLastError(wxT("ListView_SetItemCount"));
     }
     m_count = count;
     wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
@@ -2820,75 +3114,12 @@ void wxListCtrl::SetItemCount(long count)
 
 void wxListCtrl::RefreshItem(long item)
 {
-    // strangely enough, ListView_Update() results in much more flicker here
-    // than a dumb Refresh() -- why?
-#if 0
-    if ( !ListView_Update(GetHwnd(), item) )
-    {
-        wxLogLastError(_T("ListView_Update"));
-    }
-#else // 1
-    wxRect rect;
-    GetItemRect(item, rect);
-    RefreshRect(rect);
-#endif // 0/1
+    RefreshItems(item, item);
 }
 
 void wxListCtrl::RefreshItems(long itemFrom, long itemTo)
 {
-    wxRect rect1, rect2;
-    GetItemRect(itemFrom, rect1);
-    GetItemRect(itemTo, rect2);
-
-    wxRect rect = rect1;
-    rect.height = rect2.GetBottom() - rect1.GetTop();
-
-    RefreshRect(rect);
-}
-
-// ----------------------------------------------------------------------------
-// internal data stuff
-// ----------------------------------------------------------------------------
-
-static wxListItemInternalData *wxGetInternalData(HWND hwnd, long itemId)
-{
-    LV_ITEM it;
-    it.mask = LVIF_PARAM;
-    it.iItem = itemId;
-
-    if ( !ListView_GetItem(hwnd, &it) )
-        return NULL;
-
-    return (wxListItemInternalData *) it.lParam;
-}
-
-static
-wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId)
-{
-    return wxGetInternalData(GetHwndOf(ctl), itemId);
-}
-
-static
-wxListItemAttr *wxGetInternalDataAttr(const wxListCtrl *ctl, long itemId)
-{
-    wxListItemInternalData *data = wxGetInternalData(ctl, itemId);
-
-    return data ? data->attr : NULL;
-}
-
-static void wxDeleteInternalData(wxListCtrl* ctl, long itemId)
-{
-    wxListItemInternalData *data = wxGetInternalData(ctl, itemId);
-    if (data)
-    {
-        LV_ITEM item;
-        memset(&item, 0, sizeof(item));
-        item.iItem = itemId;
-        item.mask = LVIF_PARAM;
-        item.lParam = (LPARAM) 0;
-        ListView_SetItem((HWND)ctl->GetHWND(), &item);
-        delete data;
-    }
+    ListView_RedrawItems(GetHwnd(), itemFrom, itemTo);
 }
 
 // ----------------------------------------------------------------------------
@@ -2899,8 +3130,8 @@ static void wxConvertFromMSWListItem(HWND hwndListCtrl,
                                      wxListItem& info,
                                      LV_ITEM& lvItem)
 {
-    wxListItemInternalData *internaldata =
-        (wxListItemInternalData *) lvItem.lParam;
+    wxMSWListItemData *internaldata =
+        (wxMSWListItemData *) lvItem.lParam;
 
     if (internaldata)
         info.m_data = internaldata->lParam;
@@ -3015,6 +3246,16 @@ static void wxConvertToMSWListItem(const wxListCtrl *ctrl,
                                    const wxListItem& info,
                                    LV_ITEM& lvItem)
 {
+    if ( ctrl->InReportView() )
+    {
+        wxASSERT_MSG( 0 <= info.m_col && info.m_col < ctrl->GetColumnCount(),
+                      "wxListCtrl column index out of bounds" );
+    }
+    else // not in report view
+    {
+        wxASSERT_MSG( info.m_col == 0, "columns only exist in report view" );
+    }
+
     lvItem.iItem = (int) info.m_itemId;
 
     lvItem.iImage = info.m_image;
@@ -3040,7 +3281,7 @@ static void wxConvertToMSWListItem(const wxListCtrl *ctrl,
         else
         {
             // pszText is not const, hence the cast
-            lvItem.pszText = (wxChar *)info.m_text.c_str();
+            lvItem.pszText = wxMSW_CONV_LPTSTR(info.m_text);
             if ( lvItem.pszText )
                 lvItem.cchTextMax = info.m_text.length();
             else
@@ -3061,7 +3302,7 @@ static void wxConvertToMSWListCol(HWND hwndList,
     if ( item.m_mask & wxLIST_MASK_TEXT )
     {
         lvCol.mask |= LVCF_TEXT;
-        lvCol.pszText = (wxChar *)item.m_text.c_str(); // cast is safe
+        lvCol.pszText = wxMSW_CONV_LPTSTR(item.m_text);
     }
 
     if ( item.m_mask & wxLIST_MASK_FORMAT )