]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/listctrl.cpp
Avoid outputting the assert message twice in non-GUI code.
[wxWidgets.git] / src / msw / listctrl.cpp
index 4e2c3de06887bd0b9828507e98c9804d0ab09897..8efb534922cc1a5a4ea417274fe7b97493e0af53 100644 (file)
@@ -91,7 +91,9 @@ namespace
 // 4.3+
 //
 // this function does no error checking on item and subitem parameters, notice
-// that subitem is 0 for whole item or 1-based for the individual columns
+// 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)
 {
@@ -140,7 +142,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;
     }
@@ -185,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);
 };
 
 ///////////////////////////////////////////////////////
@@ -193,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:
@@ -205,44 +207,23 @@ 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);
-
-
 #if wxUSE_EXTENDED_RTTI
 WX_DEFINE_FLAGS( wxListCtrlStyle )
 
@@ -330,15 +311,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;
 }
 
@@ -400,15 +383,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)
@@ -416,7 +399,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
 
@@ -446,7 +429,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;
@@ -501,17 +484,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()
@@ -726,7 +703,7 @@ int wxListCtrl::GetColumnIndexFromOrder(int order) const
 {
     const int numCols = GetColumnCount();
     wxCHECK_MSG( order >= 0 && order < numCols, -1,
-                _T("Column position out of bounds") );
+                wxT("Column position out of bounds") );
 
     wxArrayInt indexArray(numCols);
     if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) )
@@ -738,7 +715,7 @@ int wxListCtrl::GetColumnIndexFromOrder(int order) const
 int wxListCtrl::GetColumnOrder(int col) const
 {
     const int numCols = GetColumnCount();
-    wxASSERT_MSG( col >= 0 && col < numCols, _T("Column index out of bounds") );
+    wxASSERT_MSG( col >= 0 && col < numCols, wxT("Column index out of bounds") );
 
     wxArrayInt indexArray(numCols);
     if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) )
@@ -750,7 +727,7 @@ int wxListCtrl::GetColumnOrder(int col) const
             return pos;
     }
 
-    wxFAIL_MSG( _T("no column with with given order?") );
+    wxFAIL_MSG( wxT("no column with with given order?") );
 
     return -1;
 }
@@ -773,7 +750,7 @@ bool wxListCtrl::SetColumnsOrder(const wxArrayInt& orders)
     const int numCols = GetColumnCount();
 
     wxCHECK_MSG( orders.size() == (size_t)numCols, false,
-                    _T("wrong number of elements in column orders array") );
+                    wxT("wrong number of elements in column orders array") );
 
     return ListView_SetColumnOrderArray(GetHwnd(), numCols, &orders[0]) != 0;
 }
@@ -868,34 +845,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
@@ -908,18 +884,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;
         }
@@ -1007,7 +982,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;
     }
@@ -1080,6 +1055,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
 {
@@ -1117,7 +1105,7 @@ wxRect wxListCtrl::GetViewRect() const
         RECT rc;
         if ( !ListView_GetViewRect(GetHwnd(), &rc) )
         {
-            wxLogDebug(_T("ListView_GetViewRect() failed."));
+            wxLogDebug(wxT("ListView_GetViewRect() failed."));
 
             wxZeroMemory(rc);
         }
@@ -1139,7 +1127,7 @@ wxRect wxListCtrl::GetViewRect() const
     }
     else
     {
-        wxFAIL_MSG( _T("not implemented in this mode") );
+        wxFAIL_MSG( wxT("not implemented in this mode") );
     }
 
     return rect;
@@ -1151,19 +1139,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
@@ -1171,7 +1146,11 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code)
     // completely bogus in this case), so we check item validity ourselves
     wxCHECK_MSG( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM ||
                     (subItem >= 0 && subItem < GetColumnCount()),
-                 false, _T("invalid sub item index") );
+                 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 )
@@ -1182,7 +1161,7 @@ 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;
     }
 
@@ -1201,9 +1180,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 && code == wxLIST_RECT_BOUNDS )
+        rect.width = GetColumnWidth(0);
 
     return true;
 }
@@ -1261,7 +1242,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();
 
@@ -1279,7 +1260,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();
 
@@ -1297,7 +1278,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();
 
@@ -1447,7 +1428,7 @@ bool wxListCtrl::DeleteItem(long item)
 {
     if ( !ListView_DeleteItem(GetHwnd(), (int) item) )
     {
-        wxLogLastError(_T("ListView_DeleteItem"));
+        wxLogLastError(wxT("ListView_DeleteItem"));
         return false;
     }
 
@@ -1613,27 +1594,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
@@ -1656,7 +1656,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,
@@ -1722,24 +1722,24 @@ 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") );
 
     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
@@ -1751,9 +1751,12 @@ long wxListCtrl::InsertItem(const wxListItem& info)
             // and remember that we have some now...
             m_hasAnyAttr = true;
         }
-    };
+    }
+
+    const long rv = ListView_InsertItem(GetHwnd(), & item);
 
-    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()),
@@ -1844,7 +1847,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;
     }
@@ -1871,15 +1874,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);
@@ -1888,9 +1891,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;
 
@@ -1899,7 +1902,7 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data)
                              wxInternalDataCompareFunc,
                              (WPARAM) &internalData) )
     {
-        wxLogDebug(_T("ListView_SortItems() failed"));
+        wxLogDebug(wxT("ListView_SortItems() failed"));
 
         return false;
     }
@@ -1965,7 +1968,7 @@ int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick)
 #endif //__WXWINCE__
     if ( !::GetCursorPos(ptClick) )
     {
-        wxLogLastError(_T("GetCursorPos"));
+        wxLogLastError(wxT("GetCursorPos"));
     }
 
     // we need to use listctrl coordinates for the event point so this is what
@@ -1974,12 +1977,12 @@ int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick)
     POINT ptClickHeader = *ptClick;
     if ( !::ScreenToClient(nmhdr->hwndFrom, &ptClickHeader) )
     {
-        wxLogLastError(_T("ScreenToClient(listctrl header)"));
+        wxLogLastError(wxT("ScreenToClient(listctrl header)"));
     }
 
     if ( !::ScreenToClient(::GetParent(nmhdr->hwndFrom), ptClick) )
     {
-        wxLogLastError(_T("ScreenToClient(listctrl)"));
+        wxLogLastError(wxT("ScreenToClient(listctrl)"));
     }
 
     const int colCount = Header_GetItemCount(nmhdr->hwndFrom);
@@ -2099,23 +2102,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;
@@ -2212,8 +2204,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:
@@ -2357,13 +2365,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 )
                     {
@@ -2433,7 +2446,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     const int startPos = pFindInfo->iStart;
                     const int maxPos = GetItemCount();
                     wxCHECK_MSG( startPos <= maxPos, false,
-                                 _T("bad starting position in LVN_ODFINDITEM") );
+                                 wxT("bad starting position in LVN_ODFINDITEM") );
 
                     int currentPos = startPos;
                     do
@@ -2486,7 +2499,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     if ( lvi.mask & LVIF_TEXT )
                     {
                         wxString text = OnGetItemText(item, lvi.iSubItem);
-                        wxStrlcpy(lvi.pszText, text, lvi.cchTextMax);
+                        wxStrlcpy(lvi.pszText, text.c_str(), lvi.cchTextMax);
                     }
 
                     // see comment at the end of wxListCtrl::GetColumn()
@@ -2527,6 +2540,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
@@ -2718,8 +2742,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;
     }
@@ -2826,14 +2854,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;
@@ -2844,10 +2880,11 @@ WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam)
 // Necessary for drawing hrules and vrules, if specified
 void wxListCtrl::OnPaint(wxPaintEvent& event)
 {
+    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;
@@ -2867,12 +2904,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))
             {
@@ -2886,17 +2921,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
@@ -2915,7 +2951,7 @@ void wxListCtrl::OnPaint(wxPaintEvent& event)
                                                numCols,
                                                &indexArray[0]) )
             {
-                wxFAIL_MSG( _T("invalid column index array in OnPaint()") );
+                wxFAIL_MSG( wxT("invalid column index array in OnPaint()") );
                 return;
             }
 
@@ -2965,7 +3001,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;
 }
@@ -2989,26 +3025,29 @@ int wxListCtrl::OnGetItemColumnImage(long item, long column) const
 wxListItemAttr *wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
 {
     wxASSERT_MSG( item >= 0 && item < GetItemCount(),
-                  _T("invalid item index in OnGetItemAttr()") );
+                  wxT("invalid item index in OnGetItemAttr()") );
 
     // no attributes by default
     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)
 {
-    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()),
@@ -3025,51 +3064,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
 // ----------------------------------------------------------------------------
@@ -3078,8 +3072,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;
@@ -3194,6 +3188,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;