X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c767a5bbfeb79d3cdd5c2ef222717b656c881e7d..f0ccd2cbfa0b4ac110b81626da5a184b650b1080:/src/msw/listctrl.cpp diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index 1958bc930d..ed4d99e456 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -42,6 +42,7 @@ #include "wx/vector.h" #include "wx/msw/private.h" +#include "wx/msw/private/keyboard.h" #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) #include @@ -142,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; } @@ -224,11 +225,6 @@ public: wxDECLARE_NO_COPY_CLASS(wxMSWListItemData); }; -// Get the internal data structure -static wxMSWListItemData *wxGetInternalData(HWND hwnd, long itemId); -static wxMSWListItemData *wxGetInternalData(const wxListCtrl *ctl, long itemId); - - #if wxUSE_EXTENDED_RTTI WX_DEFINE_FLAGS( wxListCtrlStyle ) @@ -404,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 @@ -434,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; @@ -502,8 +498,7 @@ void wxListCtrl::DeleteEditControl() { m_textCtrl->UnsubclassWin(); m_textCtrl->SetHWND(0); - delete m_textCtrl; - m_textCtrl = NULL; + wxDELETE(m_textCtrl); } } @@ -708,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]) ) @@ -720,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]) ) @@ -732,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; } @@ -755,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; } @@ -850,7 +845,7 @@ 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); @@ -899,7 +894,7 @@ bool wxListCtrl::SetItem(wxListItem& info) { if ( !ListView_SetItem(GetHwnd(), &item) ) { - wxLogDebug(_T("ListView_SetItem() failed")); + wxLogDebug(wxT("ListView_SetItem() failed")); return false; } @@ -987,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; } @@ -1036,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; @@ -1110,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); } @@ -1132,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; @@ -1151,11 +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, - _T("invalid item in GetSubItemRect") ); + wxT("invalid item in GetSubItemRect") ); int codeWin; if ( code == wxLIST_RECT_BOUNDS ) @@ -1166,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; } @@ -1188,7 +1184,7 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code) // 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 ) + if ( subItem == 0 && code == wxLIST_RECT_BOUNDS ) rect.width = GetColumnWidth(0); return true; @@ -1433,7 +1429,7 @@ bool wxListCtrl::DeleteItem(long item) { if ( !ListView_DeleteItem(GetHwnd(), (int) item) ) { - wxLogLastError(_T("ListView_DeleteItem")); + wxLogLastError(wxT("ListView_DeleteItem")); return false; } @@ -1469,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 @@ -1543,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; } @@ -1565,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; } @@ -1727,7 +1731,7 @@ 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); @@ -1758,7 +1762,10 @@ long wxListCtrl::InsertItem(const wxListItem& info) } } - 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()), @@ -1849,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; } @@ -1904,7 +1911,7 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, wxIntPtr data) wxInternalDataCompareFunc, (WPARAM) &internalData) ) { - wxLogDebug(_T("ListView_SortItems() failed")); + wxLogDebug(wxT("ListView_SortItems() failed")); return false; } @@ -1970,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 @@ -1979,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); @@ -2031,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: @@ -2091,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) @@ -2207,23 +2238,6 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) eventType = wxEVT_COMMAND_LIST_DELETE_ITEM; event.m_itemIndex = 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: @@ -2311,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 = @@ -2367,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 ) { @@ -2443,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 @@ -2451,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; } @@ -2537,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 @@ -2558,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 @@ -2937,7 +2993,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; } @@ -2987,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; } @@ -3011,7 +3067,7 @@ 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; @@ -3028,12 +3084,12 @@ wxListItemAttr *wxListCtrl::DoGetItemColumnAttr(long item, long column) const 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()),