X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b275d3475206c844580427cdee231304a162a285..76274efd346219516fbc8f04c4546beb1e859f9e:/src/msw/listctrl.cpp diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index 93eebf755c..abfdf8db89 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -34,6 +34,7 @@ #include "wx/intl.h" #include "wx/log.h" #include "wx/settings.h" + #include "wx/stopwatch.h" #include "wx/dcclient.h" #include "wx/textctrl.h" #endif @@ -42,6 +43,7 @@ #include "wx/vector.h" #include "wx/msw/private.h" +#include "wx/msw/private/keyboard.h" #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) #include @@ -224,81 +226,9 @@ public: wxDECLARE_NO_COPY_CLASS(wxMSWListItemData); }; -#if wxUSE_EXTENDED_RTTI -WX_DEFINE_FLAGS( wxListCtrlStyle ) - -wxBEGIN_FLAGS( wxListCtrlStyle ) - // new style border flags, we put them first to - // use them for streaming out - wxFLAGS_MEMBER(wxBORDER_SIMPLE) - wxFLAGS_MEMBER(wxBORDER_SUNKEN) - wxFLAGS_MEMBER(wxBORDER_DOUBLE) - wxFLAGS_MEMBER(wxBORDER_RAISED) - wxFLAGS_MEMBER(wxBORDER_STATIC) - wxFLAGS_MEMBER(wxBORDER_NONE) - - // old style border flags - wxFLAGS_MEMBER(wxSIMPLE_BORDER) - wxFLAGS_MEMBER(wxSUNKEN_BORDER) - wxFLAGS_MEMBER(wxDOUBLE_BORDER) - wxFLAGS_MEMBER(wxRAISED_BORDER) - wxFLAGS_MEMBER(wxSTATIC_BORDER) - wxFLAGS_MEMBER(wxBORDER) - - // standard window styles - wxFLAGS_MEMBER(wxTAB_TRAVERSAL) - wxFLAGS_MEMBER(wxCLIP_CHILDREN) - wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) - wxFLAGS_MEMBER(wxWANTS_CHARS) - wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) - wxFLAGS_MEMBER(wxALWAYS_SHOW_SB ) - wxFLAGS_MEMBER(wxVSCROLL) - wxFLAGS_MEMBER(wxHSCROLL) - - wxFLAGS_MEMBER(wxLC_LIST) - wxFLAGS_MEMBER(wxLC_REPORT) - wxFLAGS_MEMBER(wxLC_ICON) - wxFLAGS_MEMBER(wxLC_SMALL_ICON) - wxFLAGS_MEMBER(wxLC_ALIGN_TOP) - wxFLAGS_MEMBER(wxLC_ALIGN_LEFT) - wxFLAGS_MEMBER(wxLC_AUTOARRANGE) - wxFLAGS_MEMBER(wxLC_USER_TEXT) - wxFLAGS_MEMBER(wxLC_EDIT_LABELS) - wxFLAGS_MEMBER(wxLC_NO_HEADER) - wxFLAGS_MEMBER(wxLC_SINGLE_SEL) - wxFLAGS_MEMBER(wxLC_SORT_ASCENDING) - wxFLAGS_MEMBER(wxLC_SORT_DESCENDING) - wxFLAGS_MEMBER(wxLC_VIRTUAL) - -wxEND_FLAGS( wxListCtrlStyle ) - -IMPLEMENT_DYNAMIC_CLASS_XTI(wxListCtrl, wxControl,"wx/listctrl.h") - -wxBEGIN_PROPERTIES_TABLE(wxListCtrl) - wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent ) - - wxPROPERTY_FLAGS( WindowStyle , wxListCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style -wxEND_PROPERTIES_TABLE() - -wxBEGIN_HANDLERS_TABLE(wxListCtrl) -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 (a la NotebookPageInfo) -*/ -#else -IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl) -#endif - -IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl) -IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject) - -IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent) - -BEGIN_EVENT_TABLE(wxListCtrl, wxControl) +BEGIN_EVENT_TABLE(wxListCtrl, wxListCtrlBase) EVT_PAINT(wxListCtrl::OnPaint) + EVT_CHAR_HOOK(wxListCtrl::OnCharHook) END_EVENT_TABLE() // ============================================================================ @@ -379,7 +309,7 @@ void wxListCtrl::MSWSetExListStyles() WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const { - WXDWORD wstyle = wxControl::MSWGetStyle(style, exstyle); + WXDWORD wstyle = wxListCtrlBase::MSWGetStyle(style, exstyle); wstyle |= LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS; @@ -497,8 +427,7 @@ void wxListCtrl::DeleteEditControl() { m_textCtrl->UnsubclassWin(); m_textCtrl->SetHWND(0); - delete m_textCtrl; - m_textCtrl = NULL; + wxDELETE(m_textCtrl); } } @@ -549,7 +478,7 @@ void wxListCtrl::SetWindowStyleFlag(long flag) { if ( flag != m_windowStyle ) { - wxControl::SetWindowStyleFlag(flag); + wxListCtrlBase::SetWindowStyleFlag(flag); UpdateStyle(); @@ -1031,12 +960,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; @@ -1183,7 +1113,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; @@ -1403,6 +1333,29 @@ void wxListCtrl::AssignImageList(wxImageList *imageList, int which) m_ownsImageListState = true; } +// ---------------------------------------------------------------------------- +// Geometry +// ---------------------------------------------------------------------------- + +wxSize wxListCtrl::MSWGetBestViewRect(int x, int y) const +{ + const DWORD rc = ListView_ApproximateViewRect(GetHwnd(), x, y, -1); + + wxSize size(LOWORD(rc), HIWORD(rc)); + + // We have to add space for the scrollbars ourselves, they're not taken + // into account by ListView_ApproximateViewRect(), at least not with + // commctrl32.dll v6. + const DWORD mswStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE); + + if ( mswStyle & WS_HSCROLL ) + size.y += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y); + if ( mswStyle & WS_VSCROLL ) + size.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + + return size; +} + // ---------------------------------------------------------------------------- // Operations // ---------------------------------------------------------------------------- @@ -1464,7 +1417,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 @@ -1511,7 +1466,7 @@ void wxListCtrl::InitEditControl(WXHWND hWnd) m_textCtrl->SubclassWin(hWnd); m_textCtrl->SetParent(this); - // we must disallow TABbing away from the control while the edit contol is + // we must disallow TABbing away from the control while the edit control 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) @@ -1520,7 +1475,7 @@ void wxListCtrl::InitEditControl(WXHWND hWnd) wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass) { - wxCHECK_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)), NULL, + wxCHECK_MSG( textControlClass->IsKindOf(wxCLASSINFO(wxTextCtrl)), NULL, "control used for label editing must be a wxTextCtrl" ); // ListView_EditLabel requires that the list has focus. @@ -1538,8 +1493,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; } @@ -1560,14 +1514,15 @@ bool wxListCtrl::EndEditLabel(bool cancel) if ( !hwnd ) return false; - if ( cancel ) - ::SetWindowText(hwnd, wxEmptyString); // dubious but better than nothing + // Newer versions of Windows have a special ListView_CancelEditLabel() + // message for cancelling editing but it, rather counter-intuitively, keeps + // the last text entered in the dialog while cancelling as we do it below + // restores the original text which is the more expected behaviour. - // 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); + // 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; } @@ -1587,7 +1542,7 @@ long wxListCtrl::FindItem(long start, const wxString& str, bool partial) findInfo.flags = LVFI_STRING; if ( partial ) findInfo.flags |= LVFI_PARTIAL; - findInfo.psz = str.wx_str(); + findInfo.psz = str.t_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 @@ -1724,6 +1679,12 @@ long wxListCtrl::InsertItem(const wxListItem& info) { wxASSERT_MSG( !IsVirtual(), wxT("can't be used with virtual controls") ); + // In 2.8 it was possible to succeed inserting an item without initializing + // its ID as it defaulted to 0. This was however never supported and in 2.9 + // the ID is -1 by default and inserting it simply fails, but it might be + // not obvious why does it happen, so check it proactively. + wxASSERT_MSG( info.m_itemId != -1, wxS("Item ID must be set.") ); + LV_ITEM item; wxConvertToMSWListItem(this, info, item); item.mask &= ~LVIF_PARAM; @@ -1753,7 +1714,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()), @@ -1787,18 +1751,27 @@ long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex) wxListItem info; info.m_image = imageIndex; info.m_text = label; - info.m_mask = wxLIST_MASK_IMAGE | wxLIST_MASK_TEXT; + info.m_mask = wxLIST_MASK_TEXT; + if (imageIndex > -1) + info.m_mask |= wxLIST_MASK_IMAGE; info.m_itemId = index; return InsertItem(info); } // For list view mode (only), inserts a column. -long wxListCtrl::InsertColumn(long col, const wxListItem& item) +long wxListCtrl::DoInsertColumn(long col, const wxListItem& item) { LV_COLUMN lvCol; wxConvertToMSWListCol(GetHwnd(), col, item, lvCol); - if ( !(lvCol.mask & LVCF_WIDTH) ) + // LVSCW_AUTOSIZE_USEHEADER is not supported when inserting new column, + // we'll deal with it below instead. Plain LVSCW_AUTOSIZE is not supported + // neither but it doesn't need any special handling as we use fixed value + // for it here, both because we can't do anything else (there are no items + // with values in this column to compute the size from yet) and for + // compatibility as wxLIST_AUTOSIZE == -1 and -1 as InsertColumn() width + // parameter used to mean "arbitrary fixed width". + if ( !(lvCol.mask & LVCF_WIDTH) || lvCol.cx < 0 ) { // always give some width to the new column: this one is compatible // with the generic version @@ -1807,35 +1780,23 @@ long wxListCtrl::InsertColumn(long col, const wxListItem& item) } long n = ListView_InsertColumn(GetHwnd(), col, &lvCol); - if ( n != -1 ) - { - m_colCount++; - } - else // failed to insert? + if ( n == -1 ) { wxLogDebug(wxT("Failed to insert the column '%s' into listview!"), lvCol.pszText); + return -1; } - return n; -} + m_colCount++; -long wxListCtrl::InsertColumn(long col, - const wxString& heading, - int format, - int width) -{ - wxListItem item; - item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT; - item.m_text = heading; - if ( width > -1 ) + // Now adjust the new column size. + if ( (item.GetMask() & wxLIST_MASK_WIDTH) && + (item.GetWidth() == wxLIST_AUTOSIZE_USEHEADER) ) { - item.m_mask |= wxLIST_MASK_WIDTH; - item.m_width = width; + SetColumnWidth(n, wxLIST_AUTOSIZE_USEHEADER); } - item.m_format = format; - return InsertColumn(col, item); + return n; } // scroll the control by the given number of pixels (exception: in list view, @@ -1881,8 +1842,8 @@ int CALLBACK wxInternalDataCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM l wxMSWListItemData *data1 = (wxMSWListItemData *) lParam1; wxMSWListItemData *data2 = (wxMSWListItemData *) lParam2; - long d1 = (data1 == NULL ? 0 : data1->lParam); - long d2 = (data2 == NULL ? 0 : data2->lParam); + wxIntPtr d1 = (data1 == NULL ? 0 : data1->lParam); + wxIntPtr d2 = (data2 == NULL ? 0 : data2->lParam); return internalData->user_fn(d1, d2, internalData->data); @@ -1925,7 +1886,7 @@ bool wxListCtrl::MSWShouldPreProcessMessage(WXMSG* msg) return false; } } - return wxControl::MSWShouldPreProcessMessage(msg); + return wxListCtrlBase::MSWShouldPreProcessMessage(msg); } bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id_) @@ -1963,9 +1924,8 @@ int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick) } else #endif //__WXWINCE__ - if ( !::GetCursorPos(ptClick) ) { - wxLogLastError(wxT("GetCursorPos")); + wxGetCursorPosMSW(ptClick); } // we need to use listctrl coordinates for the event point so this is what @@ -2026,29 +1986,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: @@ -2086,8 +2067,11 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) return true; default: - return wxControl::MSWOnNotify(idCtrl, lParam, result); + ignore = true; } + + if ( ignore ) + return wxListCtrlBase::MSWOnNotify(idCtrl, lParam, result); } else #endif // defined(HDN_BEGINTRACKA) @@ -2202,23 +2186,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: @@ -2306,10 +2273,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 = @@ -2327,7 +2299,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case NM_DBLCLK: // if the user processes it in wxEVT_COMMAND_LEFT_CLICK(), don't do // anything else - if ( wxControl::MSWOnNotify(idCtrl, lParam, result) ) + if ( wxListCtrlBase::MSWOnNotify(idCtrl, lParam, result) ) { return true; } @@ -2352,7 +2324,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case NM_RCLICK: // if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(), // don't do anything else - if ( wxControl::MSWOnNotify(idCtrl, lParam, result) ) + if ( wxListCtrlBase::MSWOnNotify(idCtrl, lParam, result) ) { return true; } @@ -2369,7 +2341,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) else #endif //__WXWINCE__ { - ::GetCursorPos(&(lvhti.pt)); + wxGetCursorPosMSW(&(lvhti.pt)); } ::ScreenToClient(GetHwnd(), &lvhti.pt); @@ -2415,12 +2387,8 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) #ifdef HAVE_NMLVFINDITEM case LVN_ODFINDITEM: - // this message is only used with the virtual list control but - // even there we don't want to always use it: in a control with - // sufficiently big number of items (defined as > 1000 here), - // accidentally pressing a key could result in hanging an - // application waiting while it performs linear search - if ( IsVirtual() && GetItemCount() <= 1000 ) + // Find an item in a (necessarily virtual) list control. + if ( IsVirtual() ) { NMLVFINDITEM* pFindInfo = (NMLVFINDITEM*)lParam; @@ -2440,22 +2408,34 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // this is the first item we should examine, search from it // wrapping if necessary - const int startPos = pFindInfo->iStart; + int startPos = pFindInfo->iStart; const int maxPos = GetItemCount(); - wxCHECK_MSG( startPos <= maxPos, false, - wxT("bad starting position in LVN_ODFINDITEM") ); - int currentPos = startPos; - do + // Check that the index is valid to ensure that our loop + // below always terminates. + if ( startPos < 0 || startPos >= maxPos ) { - // wrap to the beginning if necessary - if ( currentPos == maxPos ) + // When the last item in the control is selected, + // iStart is really set to (invalid) maxPos index so + // accept this silently. + if ( startPos != maxPos ) { - // somewhat surprizingly, LVFI_WRAP isn't set in - // flags but we still should wrap - currentPos = 0; + wxLogDebug(wxT("Ignoring invalid search start ") + wxT("position %d in list control with ") + wxT("%d items."), startPos, maxPos); } + startPos = 0; + } + + // Linear search in a control with a lot of items can take + // a long time so we limit the total time of the search to + // ensure that the program doesn't appear to hang. +#if wxUSE_STOPWATCH + wxStopWatch sw; +#endif // wxUSE_STOPWATCH + for ( int currentPos = startPos; ; ) + { // does this item begin with searchstr? if ( wxStrnicmp(searchstr, GetItemText(currentPos), len) == 0 ) @@ -2463,13 +2443,46 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) *result = currentPos; break; } - } - while ( ++currentPos != startPos ); - if ( *result == -1 ) - { - // not found - return false; + // Go to next item with wrapping if necessary. + if ( ++currentPos == maxPos ) + { + // Surprisingly, LVFI_WRAP seems to be never set in + // the flags so wrap regardless of it. + currentPos = 0; + } + + if ( currentPos == startPos ) + { + // We examined all items without finding anything. + // + // Notice that we still return true as we did + // perform the search, if we didn't do this the + // message would have been considered unhandled and + // the control seems to always select the first + // item by default in this case. + return true; + } + +#if wxUSE_STOPWATCH + // Check the time elapsed only every thousand + // iterations for performance reasons: if we did it + // more often calling wxStopWatch::Time() could take + // noticeable time on its own. + if ( !((currentPos - startPos)%1000) ) + { + // We use half a second to limit the search time + // which is about as long as we can take without + // annoying the user. + if ( sw.Time() > 500 ) + { + // As above, return true to prevent the control + // from selecting the first item by default. + return true; + } + } +#endif // wxUSE_STOPWATCH + } SetItemState(*result, @@ -2524,7 +2537,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } if ( !processed ) - return wxControl::MSWOnNotify(idCtrl, lParam, result); + return wxListCtrlBase::MSWOnNotify(idCtrl, lParam, result); } else { @@ -2539,7 +2552,11 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // 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() ) + // and we're not using a virtual control as in this case the program + // already has the data anyhow and we don't want to call GetItem() for + // potentially many items + if ( event.m_itemIndex != -1 && !event.m_item.GetMask() + && !IsVirtual() ) { wxListItem& item = event.m_item; @@ -2569,6 +2586,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 @@ -2623,10 +2661,21 @@ bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount) SelectInHDC selFont(hdc, hfont); // get the rectangle to paint - int subitem = colCount ? col + 1 : col; RECT rc; - wxGetListCtrlSubItemRect(hwndList, item, subitem, LVIR_BOUNDS, rc); - rc.left += 6; + wxGetListCtrlSubItemRect(hwndList, item, col, LVIR_BOUNDS, rc); + if ( !col && colCount > 1 ) + { + // ListView_GetSubItemRect() returns the entire item rect for 0th + // subitem while we really need just the part for this column + RECT rc2; + wxGetListCtrlSubItemRect(hwndList, item, 1, LVIR_BOUNDS, rc2); + rc.right = rc2.left; + rc.left += 4; + } + else // not first subitem + { + rc.left += 6; + } // get the image and text to draw wxChar text[512]; @@ -2889,7 +2938,7 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) wxPaintDC dc(this); - wxControl::OnPaint(event); + wxListCtrlBase::OnPaint(event); // Reset the device origin since it may have been set dc.SetDeviceOrigin(0, 0); @@ -2964,6 +3013,27 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) } } +void wxListCtrl::OnCharHook(wxKeyEvent& event) +{ + if ( GetEditControl() ) + { + // We need to ensure that Escape is not stolen from the in-place editor + // by the containing dialog. + // + // Notice that we don't have to care about Enter key here as we return + // false from MSWShouldPreProcessMessage() for it. + if ( event.GetKeyCode() == WXK_ESCAPE ) + { + EndEditLabel(true /* cancel */); + + // Don't call Skip() below. + return; + } + } + + event.Skip(); +} + WXLRESULT wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { @@ -2987,7 +3057,7 @@ wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) //else: break } - return wxControl::MSWWindowProc(nMsg, wParam, lParam); + return wxListCtrlBase::MSWWindowProc(nMsg, wParam, lParam); } // ---------------------------------------------------------------------------- @@ -3220,7 +3290,7 @@ static void wxConvertToMSWListItem(const wxListCtrl *ctrl, else { // pszText is not const, hence the cast - lvItem.pszText = (wxChar *)info.m_text.wx_str(); + lvItem.pszText = wxMSW_CONV_LPTSTR(info.m_text); if ( lvItem.pszText ) lvItem.cchTextMax = info.m_text.length(); else @@ -3241,7 +3311,7 @@ static void wxConvertToMSWListCol(HWND hwndList, if ( item.m_mask & wxLIST_MASK_TEXT ) { lvCol.mask |= LVCF_TEXT; - lvCol.pszText = (wxChar *)item.m_text.wx_str(); // cast is safe + lvCol.pszText = wxMSW_CONV_LPTSTR(item.m_text); } if ( item.m_mask & wxLIST_MASK_FORMAT )