X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/601a5d6e72d774b97975e892d733aea8234fef46..b4607e897c31cd59c5ede98c3b5b5d3910902930:/src/msw/listctrl.cpp diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index 63c158daeb..71fb0231df 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -39,8 +39,10 @@ #endif #include "wx/imaglist.h" +#include "wx/vector.h" #include "wx/msw/private.h" +#include "wx/msw/private/keyboard.h" #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) #include @@ -90,7 +92,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) { @@ -139,7 +143,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; } @@ -184,7 +188,7 @@ private: LV_ITEM_NATIVE *m_pItem; LV_ITEM_NATIVE m_item; - DECLARE_NO_COPY_CLASS(wxLV_ITEM) + wxDECLARE_NO_COPY_CLASS(wxLV_ITEM); }; /////////////////////////////////////////////////////// @@ -192,7 +196,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: @@ -204,44 +208,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 ) @@ -329,15 +312,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; } @@ -399,15 +384,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) @@ -415,7 +400,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 @@ -445,7 +430,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; @@ -500,17 +485,11 @@ void wxListCtrl::UpdateStyle() 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(); } void wxListCtrl::DeleteEditControl() @@ -519,8 +498,7 @@ void wxListCtrl::DeleteEditControl() { m_textCtrl->UnsubclassWin(); m_textCtrl->SetHWND(0); - delete m_textCtrl; - m_textCtrl = NULL; + wxDELETE(m_textCtrl); } } @@ -725,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]) ) @@ -737,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]) ) @@ -749,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; } @@ -772,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; } @@ -867,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 @@ -907,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; } @@ -1006,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; } @@ -1055,12 +1031,13 @@ bool wxListCtrl::SetItemColumnImage(long item, long column, int image) } // Gets the item text -wxString wxListCtrl::GetItemText(long item) const +wxString wxListCtrl::GetItemText(long item, int col) const { wxListItem info; info.m_mask = wxLIST_MASK_TEXT; info.m_itemId = item; + info.m_col = col; if (!GetItem(info)) return wxEmptyString; @@ -1079,6 +1056,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 { @@ -1116,7 +1106,7 @@ wxRect wxListCtrl::GetViewRect() const RECT rc; if ( !ListView_GetViewRect(GetHwnd(), &rc) ) { - wxLogDebug(_T("ListView_GetViewRect() failed.")); + wxLogDebug(wxT("ListView_GetViewRect() failed.")); wxZeroMemory(rc); } @@ -1138,7 +1128,7 @@ wxRect wxListCtrl::GetViewRect() const } else { - wxFAIL_MSG( _T("not implemented in this mode") ); + wxFAIL_MSG( wxT("not implemented in this mode") ); } return rect; @@ -1150,19 +1140,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 @@ -1170,7 +1147,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 ) @@ -1181,7 +1162,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; } @@ -1200,9 +1181,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; } @@ -1260,7 +1243,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(); @@ -1278,7 +1261,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(); @@ -1296,7 +1279,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(); @@ -1446,7 +1429,7 @@ bool wxListCtrl::DeleteItem(long item) { if ( !ListView_DeleteItem(GetHwnd(), (int) item) ) { - wxLogLastError(_T("ListView_DeleteItem")); + wxLogLastError(wxT("ListView_DeleteItem")); return false; } @@ -1482,7 +1465,9 @@ bool wxListCtrl::DeleteItem(long item) // Deletes all items bool wxListCtrl::DeleteAllItems() { - return ListView_DeleteAllItems(GetHwnd()) != 0; + // Calling ListView_DeleteAllItems() will always generate an event but we + // shouldn't do it if the control is empty + return !GetItemCount() || ListView_DeleteAllItems(GetHwnd()) != 0; } // Deletes all items @@ -1556,8 +1541,7 @@ wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass) if ( !hWnd ) { // failed to start editing - delete m_textCtrl; - m_textCtrl = NULL; + wxDELETE(m_textCtrl); return NULL; } @@ -1578,14 +1562,21 @@ bool wxListCtrl::EndEditLabel(bool cancel) if ( !hwnd ) return false; - if ( cancel ) - ::SetWindowText(hwnd, wxEmptyString); // dubious but better than nothing - - // we shouldn't destroy the control ourselves according to MSDN, which - // proposes WM_CANCELMODE to do this, but it doesn't seem to work - // - // posting WM_CLOSE to it does seem to work without any side effects - ::PostMessage(hwnd, WM_CLOSE, 0, 0); + // Newer versions of Windows have a special message for cancelling editing, + // use it if available. +#ifdef ListView_CancelEditLabel + if ( cancel && (wxApp::GetComCtl32Version() >= 600) ) + { + ListView_CancelEditLabel(GetHwnd()); + } + else +#endif // ListView_CancelEditLabel + { + // We shouldn't destroy the control ourselves according to MSDN, which + // proposes WM_CANCELMODE to do this, but it doesn't seem to work so + // emulate the corresponding user action instead. + ::SendMessage(hwnd, WM_KEYDOWN, cancel ? VK_ESCAPE : VK_RETURN, 0); + } return true; } @@ -1612,27 +1603,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 @@ -1655,7 +1665,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, @@ -1721,24 +1731,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 @@ -1750,9 +1760,12 @@ long wxListCtrl::InsertItem(const wxListItem& info) // and remember that we have some now... m_hasAnyAttr = true; } - }; + } - long rv = ListView_InsertItem(GetHwnd(), & item); + const long rv = ListView_InsertItem(GetHwnd(), & item); + + // failing to insert the item is really unexpected + wxCHECK_MSG( rv != -1, rv, "failed to insert an item in wxListCtrl" ); m_count++; wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()), @@ -1843,7 +1856,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; } @@ -1870,15 +1883,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); @@ -1887,9 +1900,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; @@ -1898,7 +1911,7 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data) wxInternalDataCompareFunc, (WPARAM) &internalData) ) { - wxLogDebug(_T("ListView_SortItems() failed")); + wxLogDebug(wxT("ListView_SortItems() failed")); return false; } @@ -1964,7 +1977,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 @@ -1973,12 +1986,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); @@ -2025,29 +2038,50 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) event.m_itemIndex = -1; + bool ignore = false; switch ( nmhdr->code ) { // yet another comctl32.dll bug: under NT/W2K it sends Unicode // TRACK messages even to ANSI programs: on my system I get - // HDN_BEGINTRACKW and HDN_ENDTRACKA and no HDN_TRACK at all! + // HDN_BEGINTRACKW and HDN_ENDTRACKA! // // work around is to simply catch both versions and hope that it // works (why should this message exist in ANSI and Unicode is // beyond me as it doesn't deal with strings at all...) // - // note that fr HDN_TRACK another possibility could be to use - // HDN_ITEMCHANGING but it is sent even after HDN_ENDTRACK and when - // something other than the item width changes so we'd have to - // filter out the unwanted events then + // another problem is that HDN_TRACK is not sent at all by header + // with HDS_FULLDRAG style which is used by default by wxListCtrl + // under recent Windows versions (starting from at least XP) so we + // need to use HDN_ITEMCHANGING instead of it case HDN_BEGINTRACKA: case HDN_BEGINTRACKW: eventType = wxEVT_COMMAND_LIST_COL_BEGIN_DRAG; // fall through - case HDN_TRACKA: - case HDN_TRACKW: + case HDN_ITEMCHANGING: if ( eventType == wxEVT_NULL ) + { + if ( !nmHDR->pitem || !(nmHDR->pitem->mask & HDI_WIDTH) ) + { + // something other than the width is being changed, + // ignore it + ignore = true; + break; + } + + // also ignore the events sent when the width didn't really + // change: this is not just an optimization but also gets + // rid of a useless and unexpected DRAGGING event which + // would otherwise be sent after the END_DRAG one as we get + // an HDN_ITEMCHANGING after HDN_ENDTRACK for some reason + if ( nmHDR->pitem->cxy == GetColumnWidth(nmHDR->iItem) ) + { + ignore = true; + break; + } + eventType = wxEVT_COMMAND_LIST_COL_DRAGGING; + } // fall through case HDN_ENDTRACKA: @@ -2085,8 +2119,11 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) return true; default: - return wxControl::MSWOnNotify(idCtrl, lParam, result); + ignore = true; } + + if ( ignore ) + return wxControl::MSWOnNotify(idCtrl, lParam, result); } else #endif // defined(HDN_BEGINTRACKA) @@ -2098,23 +2135,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; @@ -2211,8 +2237,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) eventType = wxEVT_COMMAND_LIST_DELETE_ITEM; event.m_itemIndex = iItem; - // delete the assoicated internal data - wxDeleteInternalData(this, iItem); + break; case LVN_INSERTITEM: @@ -2300,10 +2325,15 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { eventType = wxEVT_COMMAND_LIST_KEY_DOWN; - // wxCharCodeMSWToWX() returns 0 if the key is an ASCII - // value which should be used as is - int code = wxCharCodeMSWToWX(wVKey); - event.m_code = code ? code : wVKey; + event.m_code = wxMSWKeyboard::VKToWX(wVKey); + + if ( event.m_code == WXK_NONE ) + { + // We can't translate this to a standard key code, + // until support for Unicode key codes is added to + // wxListEvent we just ignore them. + return false; + } } event.m_itemIndex = @@ -2356,13 +2386,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 ) { @@ -2432,7 +2467,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 @@ -2440,7 +2475,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // wrap to the beginning if necessary if ( currentPos == maxPos ) { - // somewhat surprizingly, LVFI_WRAP isn't set in + // somewhat surprisingly, LVFI_WRAP isn't set in // flags but we still should wrap currentPos = 0; } @@ -2485,8 +2520,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 - 1); - lvi.pszText[lvi.cchTextMax - 1] = _T('\0'); + wxStrlcpy(lvi.pszText, text.c_str(), lvi.cchTextMax); } // see comment at the end of wxListCtrl::GetColumn() @@ -2527,6 +2561,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 @@ -2548,6 +2593,27 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) m_count = 0; return true; + case LVN_DELETEITEM: + // Delete the associated internal data. Notice that this can be + // done only after the event has been handled as the data could be + // accessed during the handling of the event. + if ( wxMSWListItemData *data = MSWGetItemData(event.m_itemIndex) ) + { + const unsigned count = m_internalData.size(); + for ( unsigned n = 0; n < count; n++ ) + { + if ( m_internalData[n] == data ) + { + m_internalData.erase(m_internalData.begin() + n); + wxDELETE(data); + break; + } + } + + wxASSERT_MSG( !data, "invalid internal data pointer?" ); + } + break; + case LVN_ENDLABELEDITA: case LVN_ENDLABELEDITW: // logic here is inverted compared to all the other messages @@ -2718,8 +2784,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(-1), LVNI_FOCUSED) == item ) { nmcd.uItemState |= CDIS_FOCUS; } @@ -2826,14 +2896,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 +2922,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 +2946,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 +2963,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 @@ -2909,11 +2987,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 indexArray(numCols); + if ( !ListView_GetColumnOrderArray(GetHwnd(), + numCols, + &indexArray[0]) ) { - wxFAIL_MSG( _T("invalid column index array in OnPaint()") ); + wxFAIL_MSG( wxT("invalid column index array in OnPaint()") ); + return; } int x = itemRect.GetX(); @@ -2924,8 +3005,6 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) dc.DrawLine(x-1, firstItemRect.GetY() - gap, x-1, itemRect.GetBottom()); } - - delete indexArray; } } } @@ -2964,7 +3043,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; } @@ -2988,26 +3067,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()), @@ -3024,51 +3106,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 // ---------------------------------------------------------------------------- @@ -3077,8 +3114,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; @@ -3193,6 +3230,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;