X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/223b7504a2749a972efcf75636f411a9fd91eba9..c9cc9a2f3e41341a4c04c67137b58faaeaa55366:/src/msw/listctrl.cpp?ds=inline diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index f5a5554c9e..615d64f8b5 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -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 @@ -279,7 +291,7 @@ 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) + TODO : Expose more information of a list's layout etc. via appropriate objects (a la NotebookPageInfo) */ #else IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl) @@ -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; } @@ -338,15 +352,34 @@ 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 @@ -355,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) @@ -445,29 +478,26 @@ 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(); + const unsigned count = m_internalData.size(); + for ( unsigned n = 0; n < count; n++ ) + delete m_internalData[n]; - m_ignoreChangeMessages = true; - for (int i = 0; i < n; i++) - wxDeleteInternalData(this, i); - m_ignoreChangeMessages = false; - - m_AnyInternalData = false; - } + m_internalData.clear(); } -wxListCtrl::~wxListCtrl() +void wxListCtrl::DeleteEditControl() { - FreeAllInternalData(); - if ( m_textCtrl ) { m_textCtrl->UnsubclassWin(); @@ -475,6 +505,13 @@ wxListCtrl::~wxListCtrl() delete m_textCtrl; m_textCtrl = NULL; } +} + +wxListCtrl::~wxListCtrl() +{ + FreeAllInternalData(); + + DeleteEditControl(); if (m_ownsImageListNormal) delete m_imageListNormal; @@ -517,7 +554,7 @@ void wxListCtrl::SetWindowStyleFlag(long flag) { if ( flag != m_windowStyle ) { - m_windowStyle = flag; + wxControl::SetWindowStyleFlag(flag); UpdateStyle(); @@ -663,6 +700,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, + _T("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, _T("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( _T("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, + _T("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 +773,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(this); + + if ( !m_textCtrl ) + self->m_textCtrl = new wxTextCtrl; + self->InitEditControl((WXHWND)hwndEdit); + } + } + return m_textCtrl; } @@ -739,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 @@ -773,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")); @@ -852,14 +967,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); } @@ -887,6 +1003,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; } @@ -934,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 { @@ -948,7 +1087,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 +1100,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(_T("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( _T("not implemented in this mode") ); + } return rect; } @@ -984,22 +1144,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, _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 ) @@ -1014,27 +1170,28 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code) 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 ) + rect.width = GetColumnWidth(0); + + return true; } @@ -1090,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(); @@ -1108,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(); @@ -1126,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(); @@ -1353,38 +1510,49 @@ 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 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); +} + wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass) { - wxASSERT( (textControlClass->IsKindOf(CLASSINFO(wxTextCtrl))) ); + wxCHECK_MSG( textControlClass->IsKindOf(CLASSINFO(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; - } - - // [re]create the text control wrapping the HWND we got - if ( m_textCtrl ) - { - m_textCtrl->UnsubclassWin(); - m_textCtrl->SetHWND(0); delete m_textCtrl; - } + m_textCtrl = NULL; - m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject(); - m_textCtrl->SetHWND(hWnd); - m_textCtrl->SubclassWin(hWnd); - m_textCtrl->SetParent(this); + return NULL; + } - // 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; } @@ -1424,34 +1592,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.wx_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 +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, @@ -1546,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 @@ -1569,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); @@ -1689,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); @@ -1706,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; @@ -1735,22 +1922,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); } -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,15 +1957,12 @@ 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; } @@ -1791,19 +1973,27 @@ int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick) wxLogLastError(_T("GetCursorPos")); } - 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(_T("ScreenToClient(listctrl header)")); } - int colCount = Header_GetItemCount(nmhdr->hwndFrom); + if ( !::ScreenToClient(::GetParent(nmhdr->hwndFrom), ptClick) ) + { + wxLogLastError(_T("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; } @@ -1914,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; @@ -1993,16 +2172,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 +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: @@ -2077,7 +2265,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 +2299,11 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxLIST_STATE_SELECTED); // or 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; } @@ -2179,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 ) { @@ -2308,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() @@ -2349,7 +2542,18 @@ 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) + 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 // --------------- @@ -2375,16 +2579,9 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // 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 +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 @@ -2417,7 +2614,8 @@ static RECT GetCustomDrawnItemRect(const NMCUSTOMDRAW& nmcd) return rc; } -static bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont) +static +bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount) { NMCUSTOMDRAW& nmcd = pLVCD->nmcd; @@ -2430,22 +2628,10 @@ static bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont) 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 ) - { - // 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]; @@ -2558,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, (WPARAM)-1, LVNI_FOCUSED) == item ) + ListView_GetNextItem( + hwndList, static_cast(-1), LVNI_FOCUSED) == item ) { nmcd.uItemState |= CDIS_FOCUS; } @@ -2602,7 +2792,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); @@ -2666,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; @@ -2684,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; @@ -2700,19 +2899,17 @@ void wxListCtrl::OnPaint(wxPaintEvent& 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)) { @@ -2726,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 @@ -2748,10 +2946,21 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) dc.SetPen(pen); dc.SetBrush(*wxTRANSPARENT_BRUSH); + + const int numCols = GetColumnCount(); + wxVector indexArray(numCols); + if ( !ListView_GetColumnOrderArray(GetHwnd(), + numCols, + &indexArray[0]) ) + { + wxFAIL_MSG( _T("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()); @@ -2824,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) @@ -2846,75 +3058,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); } // ---------------------------------------------------------------------------- @@ -2925,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; @@ -3041,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;