]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/listctrl.cpp
Override email address for Jaakko Salli.
[wxWidgets.git] / src / msw / listctrl.cpp
index ff4b32b8024ae4e0e322884554a60d8834afb9e8..615d64f8b5a27a533be2197773ba38a403ccad68 100644 (file)
@@ -39,6 +39,7 @@
 #endif
 
 #include "wx/imaglist.h"
+#include "wx/vector.h"
 
 #include "wx/msw/private.h"
 
@@ -82,6 +83,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
 // ----------------------------------------------------------------------------
@@ -159,7 +187,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 +195,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,42 +207,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.
+// a lParam -- and this is what wxMSWListItemData does.
 //
-// class wxListItemInternalData
-// {
-// public:
-//   wxListItemAttr *attr;
-//   long lParam; // user data
-// };
-//
-// 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);
+static wxMSWListItemData *wxGetInternalData(HWND hwnd, long itemId);
+static wxMSWListItemData *wxGetInternalData(const wxListCtrl *ctl, long itemId);
 
 
 #if wxUSE_EXTENDED_RTTI
@@ -304,15 +316,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;
 }
 
@@ -374,15 +388,15 @@ WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
 
     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)
@@ -475,17 +489,11 @@ void wxListCtrl::UpdateStyle()
 
 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();
 }
 
 void wxListCtrl::DeleteEditControl()
@@ -696,33 +704,32 @@ bool wxListCtrl::SetColumnWidth(int col, int width)
 // columns order
 // ----------------------------------------------------------------------------
 
-int wxListCtrl::GetColumnOrder(int col) const
+int wxListCtrl::GetColumnIndexFromOrder(int order) const
 {
     const int numCols = GetColumnCount();
-    wxCHECK_MSG( col >= 0 && col < numCols, -1, _T("Col index out of bounds") );
+    wxCHECK_MSG( order >= 0 && order < numCols, -1,
+                _T("Column position out of bounds") );
 
     wxArrayInt indexArray(numCols);
-
     if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) )
         return -1;
 
-    return indexArray[col];
+    return indexArray[order];
 }
 
-int wxListCtrl::GetColumnIndexFromOrder(int order) const
+int wxListCtrl::GetColumnOrder(int col) const
 {
     const int numCols = GetColumnCount();
-    wxASSERT_MSG( order >= 0 && order < numCols, _T("Col order out of bounds") );
+    wxASSERT_MSG( col >= 0 && col < numCols, _T("Column index out of bounds") );
 
     wxArrayInt indexArray(numCols);
-
     if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) )
         return -1;
 
-    for ( int col = 0; col < numCols; col++ )
+    for ( int pos = 0; pos < numCols; pos++ )
     {
-        if ( indexArray[col] == order )
-            return col;
+        if ( indexArray[pos] == col )
+            return pos;
     }
 
     wxFAIL_MSG( _T("no column with with given order?") );
@@ -777,7 +784,7 @@ wxTextCtrl* wxListCtrl::GetEditControl() const
         HWND hwndEdit = ListView_GetEditControl(GetHwnd());
         if ( hwndEdit )
         {
-            wxListCtrl * const self = wx_const_cast(wxListCtrl *, this);
+            wxListCtrl * const self = const_cast<wxListCtrl *>(this);
 
             if ( !m_textCtrl )
                 self->m_textCtrl = new wxTextCtrl;
@@ -849,28 +856,27 @@ bool wxListCtrl::SetItem(wxListItem& info)
     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
@@ -883,15 +889,14 @@ 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"));
@@ -1055,6 +1060,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
 {
@@ -1126,19 +1144,6 @@ 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
 {
     // ListView_GetSubItemRect() doesn't do subItem error checking and returns
@@ -1148,6 +1153,10 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code)
                     (subItem >= 0 && subItem < GetColumnCount()),
                  false, _T("invalid sub item index") );
 
+    // use wxCHECK_MSG against "item" too, for coherency with the generic implementation:
+    wxCHECK_MSG( item >= 0 && item < GetItemCount(), false,
+                 _T("invalid item in GetSubItemRect") );
+
     int codeWin;
     if ( code == wxLIST_RECT_BOUNDS )
         codeWin = LVIR_BOUNDS;
@@ -1162,13 +1171,13 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code)
     }
 
     RECT rectWin;
-    if ( !ListView_GetSubItemRect
+    if ( !wxGetListCtrlSubItemRect
           (
             GetHwnd(),
             item,
             subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM ? 0 : subItem,
             codeWin,
-            &rectWin
+            rectWin
           ) )
     {
         return false;
@@ -1176,9 +1185,11 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code)
 
     wxCopyRECTToRect(rectWin, rect);
 
-    // for the first sub item, i.e. the main item itself, the returned rect is
-    // the whole line one, we need to truncate it at first column ourselves
-    rect.width = GetColumnWidth(0);
+    // 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 )
+        rect.width = GetColumnWidth(0);
 
     return true;
 }
@@ -1236,7 +1247,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();
 
@@ -1254,7 +1265,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();
 
@@ -1272,7 +1283,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();
 
@@ -1588,27 +1599,46 @@ long wxListCtrl::FindItem(long start, const wxString& str, bool partial)
     // 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
@@ -1631,7 +1661,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,
@@ -1703,18 +1733,18 @@ long wxListCtrl::InsertItem(const wxListItem& info)
     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
@@ -1726,7 +1756,7 @@ long wxListCtrl::InsertItem(const wxListItem& info)
             // and remember that we have some now...
             m_hasAnyAttr = true;
         }
-    };
+    }
 
     long rv = ListView_InsertItem(GetHwnd(), & item);
 
@@ -1846,15 +1876,15 @@ 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);
@@ -1863,9 +1893,9 @@ int CALLBACK wxInternalDataCompareFunc(LPARAM lParam1, LPARAM lParam2,  LPARAM l
 
 }
 
-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;
 
@@ -2074,23 +2104,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;
@@ -2187,8 +2206,24 @@ 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);
+
+                // delete the associated internal data
+                if ( wxMSWListItemData *data = MSWGetItemData(iItem) )
+                {
+                    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);
+                            delete data;
+                            data = NULL;
+                            break;
+                        }
+                    }
+
+                    wxASSERT_MSG( !data, "invalid internal data pointer?" );
+                }
                 break;
 
             case LVN_INSERTITEM:
@@ -2332,13 +2367,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 )
+                {
+                    ::GetCursorPos(&(lvhti.pt));
+                }
+
+                ::ScreenToClient(GetHwnd(), &lvhti.pt);
+                if ( ListView_HitTest(GetHwnd(), &lvhti) != -1 )
                 {
                     if ( lvhti.flags & LVHT_ONITEM )
                     {
@@ -2461,7 +2501,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()
@@ -2502,6 +2542,17 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
     event.SetEventType(eventType);
 
+    // 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)
+    if ( event.m_itemIndex != -1 && !event.m_item.GetMask() )
+    {
+        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
@@ -2551,10 +2602,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
@@ -2577,22 +2628,10 @@ bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount)
     SelectInHDC selFont(hdc, hfont);
 
     // get the rectangle to paint
+    int subitem = colCount ? col + 1 : col;
     RECT rc;
-    ListView_GetSubItemRect(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
-        RECT rc2;
-        ListView_GetSubItemRect(hwndList, item, 1, LVIR_BOUNDS, &rc2);
-
-        rc.right = rc2.left;
-        rc.left += 4;
-    }
-    else // not first subitem
-    {
-        rc.left += 6;
-    }
+    wxGetListCtrlSubItemRect(hwndList, item, subitem, LVIR_BOUNDS, rc);
+    rc.left += 6;
 
     // get the image and text to draw
     wxChar text[512];
@@ -2705,8 +2744,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, -1, LVNI_FOCUSED) == item )
+            ListView_GetNextItem(
+                hwndList, static_cast<WPARAM>(-1), LVNI_FOCUSED) == item )
     {
         nmcd.uItemState |= CDIS_FOCUS;
     }
@@ -2813,14 +2856,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;
@@ -2831,10 +2882,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;
@@ -2854,12 +2906,10 @@ void wxListCtrl::OnPaint(wxPaintEvent& event)
     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))
             {
@@ -2873,17 +2923,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
@@ -2896,11 +2947,14 @@ void wxListCtrl::OnPaint(wxPaintEvent& event)
             dc.SetPen(pen);
             dc.SetBrush(*wxTRANSPARENT_BRUSH);
 
-            int numCols = GetColumnCount();
-            int* indexArray = new int[numCols];
-            if ( !ListView_GetColumnOrderArray( GetHwnd(), numCols, indexArray) )
+            const int numCols = GetColumnCount();
+            wxVector<int> indexArray(numCols);
+            if ( !ListView_GetColumnOrderArray(GetHwnd(),
+                                               numCols,
+                                               &indexArray[0]) )
             {
                 wxFAIL_MSG( _T("invalid column index array in OnPaint()") );
+                return;
             }
 
             int x = itemRect.GetX();
@@ -2911,8 +2965,6 @@ void wxListCtrl::OnPaint(wxPaintEvent& event)
                 dc.DrawLine(x-1, firstItemRect.GetY() - gap,
                             x-1, itemRect.GetBottom());
             }
-
-            delete indexArray;
         }
     }
 }
@@ -2981,10 +3033,13 @@ wxListItemAttr *wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) cons
     return NULL;
 }
 
-wxListItemAttr *wxListCtrl::DoGetItemAttr(long item) const
+wxListItemAttr *wxListCtrl::DoGetItemColumnAttr(long item, long column) const
 {
-    return IsVirtual() ? OnGetItemAttr(item)
-                       : wxGetInternalDataAttr(this, item);
+    if ( IsVirtual() )
+        return OnGetItemColumnAttr(item, column);
+
+    wxMSWListItemData * const data = MSWGetItemData(item);
+    return data ? data->attr : NULL;
 }
 
 void wxListCtrl::SetItemCount(long count)
@@ -3011,51 +3066,6 @@ void wxListCtrl::RefreshItems(long itemFrom, long itemTo)
     ListView_RedrawItems(GetHwnd(), itemFrom, itemTo);
 }
 
-// ----------------------------------------------------------------------------
-// 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;
-    }
-}
-
 // ----------------------------------------------------------------------------
 // wxWin <-> MSW items conversions
 // ----------------------------------------------------------------------------
@@ -3064,8 +3074,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;
@@ -3180,6 +3190,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;