X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9e300531e5ef0457c4232bfa23e0a13dba0f9230..b600ed13625645eb985acf49eecd06fd111eb061:/src/msw/listctrl.cpp diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index 943a87ae8d..b009824380 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -5,8 +5,8 @@ // Modified by: // Created: 04/01/98 // RCS-ID: $Id$ -// Copyright: (c) Julian Smart and Markus Holzem -// Licence: wxWindows license +// Copyright: (c) Julian Smart +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // ============================================================================ @@ -45,7 +45,7 @@ #include "wx/msw/private.h" -#if ((defined(__GNUWIN32_OLD__) || defined(__TWIN32__)) && !defined(__CYGWIN10__)) +#if defined(__GNUWIN32_OLD__) && !defined(__CYGWIN10__) #include "wx/msw/gnuwin32/extra.h" #else #include @@ -244,6 +244,8 @@ void wxListCtrl::Init() m_ownsImageListNormal = m_ownsImageListSmall = m_ownsImageListState = FALSE; m_baseStyle = 0; m_colCount = 0; + m_count = 0; + m_ignoreChangeMessages = FALSE; m_textCtrl = NULL; m_AnyInternalData = FALSE; m_hasAnyAttr = FALSE; @@ -286,11 +288,6 @@ bool wxListCtrl::Create(wxWindow *parent, DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS; - if ( m_windowStyle & wxCLIP_SIBLINGS ) - wstyle |= WS_CLIPSIBLINGS; - - if ( wxStyleHasBorder(m_windowStyle) ) - wstyle |= WS_BORDER; m_baseStyle = wstyle; if ( !DoCreateControl(x, y, width, height) ) @@ -306,16 +303,12 @@ bool wxListCtrl::DoCreateControl(int x, int y, int w, int h) { DWORD wstyle = m_baseStyle; - bool want3D; - WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D); - - // Even with extended styles, need to combine with WS_BORDER - // for them to look right. - if ( want3D ) - wstyle |= WS_BORDER; + WXDWORD exStyle = 0; + WXDWORD standardStyle = MSWGetStyle(GetWindowStyle(), & exStyle) ; long oldStyle = 0; // Dummy wstyle |= ConvertToMSWStyle(oldStyle, m_windowStyle); + wstyle |= standardStyle; // Create the ListView control. m_hWnd = (WXHWND)CreateWindowEx(exStyle, @@ -378,8 +371,10 @@ void wxListCtrl::FreeAllInternalData() int n = GetItemCount(); int i = 0; + m_ignoreChangeMessages = TRUE; for (i = 0; i < n; i++) wxDeleteInternalData(this, i); + m_ignoreChangeMessages = FALSE; m_AnyInternalData = FALSE; } @@ -391,8 +386,8 @@ wxListCtrl::~wxListCtrl() if ( m_textCtrl ) { - m_textCtrl->SetHWND(0); m_textCtrl->UnsubclassWin(); + m_textCtrl->SetHWND(0); delete m_textCtrl; m_textCtrl = NULL; } @@ -1014,7 +1009,7 @@ bool wxListCtrl::SetItemPosition(long item, const wxPoint& pos) // Gets the number of items in the list control int wxListCtrl::GetItemCount() const { - return ListView_GetItemCount(GetHwnd()); + return m_count; } // Retrieves the spacing between icons in pixels. @@ -1204,6 +1199,10 @@ bool wxListCtrl::DeleteItem(long item) return FALSE; } + m_count -= 1; + wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()), + wxT("m_count should match ListView_GetItemCount")); + // the virtual list control doesn't refresh itself correctly, help it if ( IsVirtual() ) { @@ -1232,6 +1231,7 @@ bool wxListCtrl::DeleteItem(long item) // Deletes all items bool wxListCtrl::DeleteAllItems() { + FreeAllInternalData(); return ListView_DeleteAllItems(GetHwnd()) != 0; } @@ -1290,8 +1290,8 @@ wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass) // [re]create the text control wrapping the HWND we got if ( m_textCtrl ) { - m_textCtrl->SetHWND(0); m_textCtrl->UnsubclassWin(); + m_textCtrl->SetHWND(0); delete m_textCtrl; } @@ -1448,8 +1448,12 @@ long wxListCtrl::InsertItem(wxListItem& info) } }; + long rv = ListView_InsertItem(GetHwnd(), & item); + m_count += 1; + wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()), + wxT("m_count should match ListView_GetItemCount")); - return (long) ListView_InsertItem(GetHwnd(), & item); + return rv; } long wxListCtrl::InsertItem(long index, const wxString& label) @@ -1641,6 +1645,7 @@ bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id) bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { + // prepare the event // ----------------- @@ -1770,53 +1775,29 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) const int iItem = nmLV->iItem; - // set the data event field for all messages for which the system gives - // us a valid NM_LISTVIEW::lParam - switch ( nmLV->hdr.code ) + + // 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))) { - case LVN_BEGINDRAG: - case LVN_BEGINRDRAG: - case LVN_COLUMNCLICK: - case LVN_ITEMCHANGED: - case LVN_ITEMCHANGING: - if ( iItem != -1 ) - { - if ( iItem >= GetItemCount() ) - { - // there is apparently a bug in comctl32.dll version - // 5.50.4704.1100 (note that the MS DLL database - // doesn't say what this version is, it's not the one - // shipped with W2K although the bug was reported under - // that system) and it sends us LVN_ITEMCHANGING - // notifications with the item out of range -- and as - // we access the items client data, we crash below - // - // see - // - // http://lists.wxwindows.org/cgi-bin/ezmlm-cgi?8:mss:29852:knlihdmadhaljafjajei - // - // and the thread continuation for more details - // (although note that the bug may be present in other - // versions of comctl32.dll as well as it has been - // reported quite a few times) - // - // to fix this, simply ignore these "bad" events (as - // above with HDN_GETDISPINFOW) - return TRUE; - } + return TRUE; + } - wxListItemInternalData *internaldata = - (wxListItemInternalData *) nmLV->lParam; - if ( internaldata ) - event.m_item.m_data = internaldata->lParam; - } + // 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); - default: - // fall through - ; + if ( internaldata ) + event.m_item.m_data = internaldata->lParam; } + switch ( nmhdr->code ) { case LVN_BEGINRDRAG: @@ -1860,7 +1841,19 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxConvertFromMSWListItem(NULL, event.m_item, item); if ( ((LV_ITEM)item).pszText == NULL || ((LV_ITEM)item).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; + } return FALSE; + } event.m_itemIndex = event.m_item.m_itemId; } @@ -1872,7 +1865,19 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxConvertFromMSWListItem(NULL, event.m_item, item); if ( ((LV_ITEM)item).pszText == NULL || ((LV_ITEM)item).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; + } return FALSE; + } event.m_itemIndex = event.m_item.m_itemId; } @@ -1885,11 +1890,17 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; case LVN_DELETEALLITEMS: + m_count = 0; eventType = wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS; event.m_itemIndex = -1; break; case LVN_DELETEITEM: + if (m_count == 0) + // this should be prevented by the post-processing code below, + // but "just in case" + return FALSE; + eventType = wxEVT_COMMAND_LIST_DELETE_ITEM; event.m_itemIndex = iItem; // delete the assoicated internal data @@ -2069,21 +2080,18 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) eventType = wxEVT_COMMAND_LIST_CACHE_HINT; - // we get some really stupid cache hints like ones for items in - // range 0..0 for an empty control or, after deleting an item, - // for items in invalid range - filter this garbage out - if ( cacheHint->iFrom < cacheHint->iTo ) - { - event.m_oldItemIndex = cacheHint->iFrom; - - long iMax = GetItemCount(); - event.m_itemIndex = cacheHint->iTo < iMax ? cacheHint->iTo - : iMax - 1; - } - else - { + // we get some really stupid cache hints like ones for + // items in range 0..0 for an empty control or, after + // deleting an item, for items in invalid range -- filter + // this garbage out + if ( cacheHint->iFrom > cacheHint->iTo ) return FALSE; - } + + event.m_oldItemIndex = cacheHint->iFrom; + + const long iMax = GetItemCount(); + event.m_itemIndex = cacheHint->iTo < iMax ? cacheHint->iTo + : iMax - 1; } break; @@ -2133,12 +2141,10 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) event.SetEventType(eventType); - if ( !GetEventHandler()->ProcessEvent(event) ) - return FALSE; + bool processed = GetEventHandler()->ProcessEvent(event); // post processing // --------------- - switch ( nmhdr->code ) { case LVN_DELETEALLITEMS: @@ -2146,7 +2152,6 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // notifications - this makes deleting all items from a list ctrl // much faster *result = TRUE; - return TRUE; case LVN_ENDLABELEDITA: @@ -2159,8 +2164,8 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { // EDIT control will be deleted by the list control itself so // prevent us from deleting it as well - m_textCtrl->SetHWND(0); m_textCtrl->UnsubclassWin(); + m_textCtrl->SetHWND(0); delete m_textCtrl; m_textCtrl = NULL; } @@ -2168,9 +2173,10 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) return TRUE; } - *result = !event.IsAllowed(); + if ( processed ) + *result = !event.IsAllowed(); - return TRUE; + return processed; } #if defined(_WIN32_IE) && _WIN32_IE >= 0x300 @@ -2340,7 +2346,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("not supposed to be called") ); + wxFAIL_MSG( _T("wxListCtrl::OnGetItemText not supposed to be called") ); return wxEmptyString; } @@ -2348,7 +2354,7 @@ wxString wxListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) cons int wxListCtrl::OnGetItemImage(long WXUNUSED(item)) const { // same as above - wxFAIL_MSG( _T("not supposed to be called") ); + wxFAIL_MSG( _T("wxListCtrl::OnGetItemImage not supposed to be called") ); return -1; } @@ -2366,10 +2372,13 @@ void wxListCtrl::SetItemCount(long count) { wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") ); - if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count, 0) ) + if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count, LVSICF_NOSCROLL) ) { wxLogLastError(_T("ListView_SetItemCount")); } + m_count = count; + wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()), + wxT("m_count should match ListView_GetItemCount")); } void wxListCtrl::RefreshItem(long item) @@ -2432,13 +2441,13 @@ static void wxDeleteInternalData(wxListCtrl* ctl, long itemId) wxListItemInternalData *data = wxGetInternalData(ctl, itemId); if (data) { - delete 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; } }