X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/85ee88cd536cbf8b347e58f84b160b60204ed157..9741fd45f734ea30e094f5b15f092b8394621204:/src/msw/listbox.cpp diff --git a/src/msw/listbox.cpp b/src/msw/listbox.cpp index 29e2e74401..53d60ce89b 100644 --- a/src/msw/listbox.cpp +++ b/src/msw/listbox.cpp @@ -149,11 +149,11 @@ wxOwnerDrawn *wxListBox::CreateLboxItem(size_t WXUNUSED(n)) // creation // ---------------------------------------------------------------------------- -// Listbox item -wxListBox::wxListBox() +void wxListBox::Init() { m_noItems = 0; m_updateHorizontalExtent = false; + m_selectedByKeyboard = false; } bool wxListBox::Create(wxWindow *parent, @@ -165,9 +165,6 @@ bool wxListBox::Create(wxWindow *parent, const wxValidator& validator, const wxString& name) { - m_noItems = 0; - m_updateHorizontalExtent = false; - // initialize base class fields if ( !CreateControl(parent, id, pos, size, style, validator, name) ) return false; @@ -186,7 +183,6 @@ bool wxListBox::Create(wxWindow *parent, } // now we can compute our best size correctly, so do it again - InvalidateBestSize(); SetInitialSize(size); return true; @@ -269,6 +265,22 @@ void wxListBox::OnInternalIdle() } } +void wxListBox::MSWOnItemsChanged() +{ + // we need to do two things when items change: update their max horizontal + // extent so that horizontal scrollbar could be shown or hidden as + // appropriate and also invlaidate the best size + // + // updating the max extent is slow (it's an O(N) operation) and so we defer + // it until the idle time but the best size should be invalidated + // immediately doing it in idle time is too late -- layout using incorrect + // old best size will have been already done by then + + m_updateHorizontalExtent = true; + + InvalidateBestSize(); +} + // ---------------------------------------------------------------------------- // implementation of wxListBoxBase methods // ---------------------------------------------------------------------------- @@ -287,15 +299,17 @@ void wxListBox::DoDeleteOneItem(unsigned int n) wxT("invalid index in wxListBox::Delete") ); #if wxUSE_OWNER_DRAWN - delete m_aItems[n]; - m_aItems.RemoveAt(n); + if ( HasFlag(wxLB_OWNERDRAW) ) + { + delete m_aItems[n]; + m_aItems.RemoveAt(n); + } #endif // wxUSE_OWNER_DRAWN SendMessage(GetHwnd(), LB_DELETESTRING, n, 0); m_noItems--; - // SetHorizontalExtent(wxEmptyString); can be slow - m_updateHorizontalExtent = true; + MSWOnItemsChanged(); UpdateOldSelections(); } @@ -316,7 +330,7 @@ int wxListBox::FindString(const wxString& s, bool bCase) const void wxListBox::DoClear() { #if wxUSE_OWNER_DRAWN - if ( m_windowStyle & wxLB_OWNERDRAW ) + if ( HasFlag(wxLB_OWNERDRAW) ) { WX_CLEAR_ARRAY(m_aItems); } @@ -325,7 +339,7 @@ void wxListBox::DoClear() ListBox_ResetContent(GetHwnd()); m_noItems = 0; - m_updateHorizontalExtent = true; + MSWOnItemsChanged(); UpdateOldSelections(); } @@ -483,7 +497,7 @@ int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items, AssignNewItemClientData(n, clientData, i, type); } - m_updateHorizontalExtent = true; + MSWOnItemsChanged(); UpdateOldSelections(); @@ -534,7 +548,7 @@ void wxListBox::SetString(unsigned int n, const wxString& s) if ( wasSelected ) Select(n); - m_updateHorizontalExtent = true; + MSWOnItemsChanged(); } unsigned int wxListBox::GetCount() const @@ -548,9 +562,6 @@ unsigned int wxListBox::GetCount() const void wxListBox::SetHorizontalExtent(const wxString& s) { - // in any case, our best size could have changed - InvalidateBestSize(); - // the rest is only necessary if we want a horizontal scrollbar if ( !HasFlag(wxHSCROLL) ) return; @@ -633,25 +644,30 @@ wxSize wxListBox::DoGetBestClientSize() const bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) { - if ((param == LBN_SELCHANGE) && HasMultipleSelection()) - { - CalcAndSendEvent(); - return true; - } - wxEventType evtType; - int n; + int n = wxNOT_FOUND; if ( param == LBN_SELCHANGE ) { + if ( HasMultipleSelection() ) + return CalcAndSendEvent(); + evtType = wxEVT_COMMAND_LISTBOX_SELECTED; - n = SendMessage(GetHwnd(), LB_GETCARETINDEX, 0, 0); - // NB: conveniently enough, LB_ERR is the same as wxNOT_FOUND + if ( m_selectedByKeyboard ) + { + // We shouldn't use the mouse position to find the item as mouse + // can be anywhere, ask the listbox itself. Notice that this can't + // be used when the item is selected using the mouse however as + // LB_GETCARETINDEX will always return a valid item, even if the + // mouse is clicked below all the items, which is why we find the + // item ourselves below in this case. + n = SendMessage(GetHwnd(), LB_GETCARETINDEX, 0, 0); + } + //else: n will be determined below from the mouse position } else if ( param == LBN_DBLCLK ) { evtType = wxEVT_COMMAND_LISTBOX_DOUBLECLICKED; - n = HitTest(ScreenToClient(wxGetMousePosition())); } else { @@ -659,26 +675,56 @@ bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) return false; } - // retrieve the affected item + // Find the item position if it was a mouse-generated selection event or a + // double click event (which is always generated using the mouse) if ( n == wxNOT_FOUND ) - return false; + { + const DWORD pos = ::GetMessagePos(); + const wxPoint pt(GET_X_LPARAM(pos), GET_Y_LPARAM(pos)); + n = HitTest(ScreenToClient(wxPoint(pt))); + } - wxCommandEvent event(evtType, m_windowId); - event.SetEventObject(this); + // We get events even when mouse is clicked outside of any valid item from + // Windows, just ignore them. + if ( n == wxNOT_FOUND ) + return false; - if ( HasClientObjectData() ) - event.SetClientObject( GetClientObject(n) ); - else if ( HasClientUntypedData() ) - event.SetClientData( GetClientData(n) ); + // As we don't use m_oldSelections in single selection mode, we store the + // last item that we notified the user about in it in this case because we + // need to remember it to be able to filter out the dummy LBN_SELCHANGE + // messages that we get when the user clicks on an already selected item. + if ( param == LBN_SELCHANGE ) + { + if ( !m_oldSelections.empty() && *m_oldSelections.begin() == n ) + { + // Same item as the last time. + return false; + } - event.SetString(GetString(n)); - event.SetInt(n); + m_oldSelections.clear(); + m_oldSelections.push_back(n); + } - return HandleWindowEvent(event); + // Do generate an event otherwise. + return SendEvent(evtType, n, true /* selection */); +} + +WXLRESULT +wxListBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +{ + // Remember whether there was a keyboard or mouse event before + // LBN_SELCHANGE: this allows us to correctly determine the item affected + // by it in MSWCommand() above in any case. + if ( WM_KEYFIRST <= nMsg && nMsg <= WM_KEYLAST ) + m_selectedByKeyboard = true; + else if ( WM_MOUSEFIRST <= nMsg && nMsg <= WM_MOUSELAST ) + m_selectedByKeyboard = false; + + return wxListBoxBase::MSWWindowProc(nMsg, wParam, lParam); } // ---------------------------------------------------------------------------- -// wxCheckListBox support +// owner-drawn list boxes support // ---------------------------------------------------------------------------- #if wxUSE_OWNER_DRAWN @@ -686,23 +732,14 @@ bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) // misc overloaded methods // ----------------------- -void wxListBox::Delete(unsigned int n) -{ - wxCHECK_RET( IsValid(n), - wxT("invalid index in wxListBox::Delete") ); - - wxListBoxBase::Delete(n); - - // free memory - delete m_aItems[n]; - m_aItems.RemoveAt(n); -} - bool wxListBox::SetFont(const wxFont &font) { - unsigned int i; - for ( i = 0; i < m_aItems.GetCount(); i++ ) - m_aItems[i]->SetFont(font); + if ( HasFlag(wxLB_OWNERDRAW) ) + { + const unsigned count = m_aItems.GetCount(); + for ( unsigned i = 0; i < count; i++ ) + m_aItems[i]->SetFont(font); + } wxListBoxBase::SetFont(font); @@ -759,7 +796,7 @@ namespace bool wxListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item) { // only owner-drawn control should receive this message - wxCHECK( ((m_windowStyle & wxLB_OWNERDRAW) == wxLB_OWNERDRAW), false ); + wxCHECK( HasFlag(wxLB_OWNERDRAW), false ); MEASUREITEMSTRUCT *pStruct = (MEASUREITEMSTRUCT *)item; @@ -790,7 +827,7 @@ bool wxListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item) bool wxListBox::MSWOnDraw(WXDRAWITEMSTRUCT *item) { // only owner-drawn control should receive this message - wxCHECK( ((m_windowStyle & wxLB_OWNERDRAW) == wxLB_OWNERDRAW), false ); + wxCHECK( HasFlag(wxLB_OWNERDRAW), false ); DRAWITEMSTRUCT *pStruct = (DRAWITEMSTRUCT *)item;