X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/87815d3d0a59968543980ec415889333c08f6519..07edda049fbe3b6ae08daff5ce864543f155bab0:/src/univ/listbox.cpp diff --git a/src/univ/listbox.cpp b/src/univ/listbox.cpp index 45e5f77e87..147ebfb148 100644 --- a/src/univ/listbox.cpp +++ b/src/univ/listbox.cpp @@ -37,12 +37,66 @@ #include "wx/univ/inphand.h" #include "wx/univ/theme.h" +// ---------------------------------------------------------------------------- +// wxStdListboxInputHandler: handles mouse and kbd in a single or multi +// selection listbox +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxStdListboxInputHandler : public wxStdInputHandler +{ +public: + // if pressing the mouse button in a multiselection listbox should toggle + // the item under mouse immediately, then specify true as the second + // parameter (this is the standard behaviour, under GTK the item is toggled + // only when the mouse is released in the multi selection listbox) + wxStdListboxInputHandler(wxInputHandler *inphand, + bool toggleOnPressAlways = true); + + // base class methods + virtual bool HandleKey(wxInputConsumer *consumer, + const wxKeyEvent& event, + bool pressed); + virtual bool HandleMouse(wxInputConsumer *consumer, + const wxMouseEvent& event); + virtual bool HandleMouseMove(wxInputConsumer *consumer, + const wxMouseEvent& event); + +protected: + // return the item under mouse, 0 if the mouse is above the listbox or + // GetCount() if it is below it + int HitTest(const wxListBox *listbox, const wxMouseEvent& event); + + // parts of HitTest(): first finds the pseudo (because not in range) index + // of the item and the second one adjusts it if necessary - that is if the + // third one returns false + int HitTestUnsafe(const wxListBox *listbox, const wxMouseEvent& event); + int FixItemIndex(const wxListBox *listbox, int item); + bool IsValidIndex(const wxListBox *listbox, int item); + + // init m_btnCapture and m_actionMouse + wxControlAction SetupCapture(wxListBox *lbox, + const wxMouseEvent& event, + int item); + + wxRenderer *m_renderer; + + // the button which initiated the mouse capture (currently 0 or 1) + int m_btnCapture; + + // the action to perform when the mouse moves while we capture it + wxControlAction m_actionMouse; + + // the ctor parameter toggleOnPressAlways (see comments near it) + bool m_toggleOnPressAlways; + + // do we track the mouse outside the window when it is captured? + bool m_trackMouseOutside; +}; + // ============================================================================ // implementation of wxListBox // ============================================================================ -IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl) - BEGIN_EVENT_TABLE(wxListBox, wxListBoxBase) EVT_SIZE(wxListBox::OnSize) END_EVENT_TABLE() @@ -59,7 +113,7 @@ void wxListBox::Init() m_maxWidth = 0; m_scrollRangeY = 0; m_maxWidthItem = -1; - m_strings = NULL; + m_strings.unsorted = NULL; // no items hence no current item m_current = -1; @@ -136,11 +190,14 @@ bool wxListBox::Create(wxWindow *parent, validator, name) ) return false; - m_strings = new wxArrayString; + if ( IsSorted() ) + m_strings.sorted = new wxSortedArrayString; + else + m_strings.unsorted = new wxArrayString; Set(n, choices); - SetBestSize(size); + SetInitialSize(size); CreateInputHandler(wxINP_HANDLER_LISTBOX); @@ -152,76 +209,59 @@ wxListBox::~wxListBox() // call this just to free the client data -- and avoid leaking memory DoClear(); - delete m_strings; + if ( IsSorted() ) + delete m_strings.sorted; + else + delete m_strings.unsorted; - m_strings = NULL; + m_strings.sorted = NULL; } // ---------------------------------------------------------------------------- -// adding/inserting strings +// accessing strings // ---------------------------------------------------------------------------- -int wxCMPFUNC_CONV wxListBoxSortNoCase(wxString* s1, wxString* s2) +unsigned int wxListBox::GetCount() const { - return s1->CmpNoCase(*s2); + return IsSorted() ? m_strings.sorted->size() + : m_strings.unsorted->size(); } -int wxListBox::DoAppendOnly(const wxString& item) +wxString wxListBox::GetString(unsigned int n) const { - unsigned int index; - - if ( IsSorted() ) - { - m_strings->Add(item); - m_strings->Sort(wxListBoxSortNoCase); - index = m_strings->Index(item); - } - else - { - index = m_strings->GetCount(); - m_strings->Add(item); - } - - return index; + return IsSorted() ? m_strings.sorted->Item(n) + : m_strings.unsorted->Item(n); } -int wxListBox::DoAppend(const wxString& item) +int wxListBox::FindString(const wxString& s, bool bCase) const { - size_t index = DoAppendOnly( item ); + return IsSorted() ? m_strings.sorted->Index(s, bCase) + : m_strings.unsorted->Index(s, bCase); +} - m_itemsClientData.Insert(NULL, index); +// ---------------------------------------------------------------------------- +// adding/inserting strings +// ---------------------------------------------------------------------------- - m_updateScrollbarY = true; +int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items, + unsigned int pos, + void **clientData, + wxClientDataType type) +{ + int idx = wxNOT_FOUND; - if ( HasHorzScrollbar() ) + const unsigned int numItems = items.GetCount(); + for ( unsigned int i = 0; i < numItems; ++i ) { - // has the max width increased? - wxCoord width; - GetTextExtent(item, &width, NULL); - if ( width > m_maxWidth ) - { - m_maxWidth = width; - m_maxWidthItem = index; - m_updateScrollbarX = true; - } - } - - RefreshFromItemToEnd(index); + const wxString& item = items[i]; + idx = IsSorted() ? m_strings.sorted->Add(item) + : (m_strings.unsorted->Insert(item, pos), pos++); - return index; -} + m_itemsClientData.Insert(NULL, idx); + AssignNewItemClientData(idx, clientData, i, type); -void wxListBox::DoInsertItems(const wxArrayString& items, unsigned int pos) -{ - // the position of the item being added to a sorted listbox can't be - // specified - wxCHECK_RET( !IsSorted(), _T("can't insert items into sorted listbox") ); - - unsigned int count = items.GetCount(); - for ( unsigned int n = 0; n < count; n++ ) - { - m_strings->Insert(items[n], pos + n); - m_itemsClientData.Insert(NULL, pos + n); + // call the wxCheckListBox hook + OnItemInserted(idx); } // the number of items has changed so we might have to show the scrollbar @@ -235,36 +275,18 @@ void wxListBox::DoInsertItems(const wxArrayString& items, unsigned int pos) // note that we have to refresh all the items after the ones we inserted, // not just these items RefreshFromItemToEnd(pos); -} - -void wxListBox::DoSetItems(const wxArrayString& items, void **clientData) -{ - DoClear(); - - unsigned int count = items.GetCount(); - if ( !count ) - return; - - m_strings->Alloc(count); - m_itemsClientData.Alloc(count); - for ( unsigned int n = 0; n < count; n++ ) - { - unsigned int index = DoAppendOnly(items[n]); - - m_itemsClientData.Insert(clientData ? clientData[n] : NULL, index); - } - - m_updateScrollbarY = true; - - RefreshAll(); + return idx; } void wxListBox::SetString(unsigned int n, const wxString& s) { - wxCHECK_RET( !IsSorted(), _T("can't set string in sorted listbox") ); + wxCHECK_RET( !IsSorted(), wxT("can't set string in sorted listbox") ); - (*m_strings)[n] = s; + if ( IsSorted() ) + (*m_strings.sorted)[n] = s; + else + (*m_strings.unsorted)[n] = s; if ( HasHorzScrollbar() ) { @@ -297,26 +319,15 @@ void wxListBox::SetString(unsigned int n, const wxString& s) void wxListBox::DoClear() { - m_strings->Clear(); - - if ( HasClientObjectData() ) - { - unsigned int count = m_itemsClientData.GetCount(); - for ( unsigned int n = 0; n < count; n++ ) - { - delete (wxClientData *) m_itemsClientData[n]; - } - } + if ( IsSorted() ) + m_strings.sorted->Clear(); + else + m_strings.unsorted->Clear(); m_itemsClientData.Clear(); m_selections.Clear(); m_current = -1; -} - -void wxListBox::Clear() -{ - DoClear(); m_updateScrollbarY = true; @@ -325,21 +336,19 @@ void wxListBox::Clear() RefreshAll(); } -void wxListBox::Delete(unsigned int n) +void wxListBox::DoDeleteOneItem(unsigned int n) { wxCHECK_RET( IsValid(n), - _T("invalid index in wxListBox::Delete") ); + wxT("invalid index in wxListBox::Delete") ); // do it before removing the index as otherwise the last item will not be // refreshed (as GetCount() will be decremented) RefreshFromItemToEnd(n); - m_strings->RemoveAt(n); - - if ( HasClientObjectData() ) - { - delete (wxClientData *)m_itemsClientData[n]; - } + if ( IsSorted() ) + m_strings.sorted->RemoveAt(n); + else + m_strings.unsorted->RemoveAt(n); m_itemsClientData.RemoveAt(n); @@ -402,16 +411,6 @@ void *wxListBox::DoGetItemClientData(unsigned int n) const return m_itemsClientData[n]; } -void wxListBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData) -{ - m_itemsClientData[n] = clientData; -} - -wxClientData* wxListBox::DoGetItemClientObject(unsigned int n) const -{ - return (wxClientData *)m_itemsClientData[n]; -} - // ---------------------------------------------------------------------------- // selection // ---------------------------------------------------------------------------- @@ -420,7 +419,16 @@ void wxListBox::DoSetSelection(int n, bool select) { if ( select ) { - if ( m_selections.Index(n) == wxNOT_FOUND ) + if ( n == wxNOT_FOUND ) + { + if ( !HasMultipleSelection() ) + { + // selecting wxNOT_FOUND is documented to deselect all items + DeselectAll(); + return; + } + } + else if ( m_selections.Index(n) == wxNOT_FOUND ) { if ( !HasMultipleSelection() ) { @@ -450,7 +458,7 @@ void wxListBox::DoSetSelection(int n, bool select) // sanity check: a single selection listbox can't have more than one item // selected wxASSERT_MSG( HasMultipleSelection() || (m_selections.GetCount() < 2), - _T("multiple selected items in single selection lbox?") ); + wxT("multiple selected items in single selection lbox?") ); if ( select ) { @@ -462,12 +470,12 @@ void wxListBox::DoSetSelection(int n, bool select) int wxListBox::GetSelection() const { wxCHECK_MSG( !HasMultipleSelection(), wxNOT_FOUND, - _T("use wxListBox::GetSelections for ths listbox") ); + wxT("use wxListBox::GetSelections for ths listbox") ); return m_selections.IsEmpty() ? wxNOT_FOUND : m_selections[0]; } -int wxCMPFUNC_CONV wxCompareInts(int *n, int *m) +static int wxCMPFUNC_CONV wxCompareInts(int *n, int *m) { return *n - *m; } @@ -629,7 +637,7 @@ void wxListBox::UpdateItems() if ( m_updateCount == -1 ) { // refresh all - wxLogTrace(_T("listbox"), _T("Refreshing all")); + wxLogTrace(wxT("listbox"), wxT("Refreshing all")); Refresh(); } @@ -646,7 +654,7 @@ void wxListBox::UpdateItems() // entire line(s) CalcScrolledPosition(0, rect.y, NULL, &rect.y); - wxLogTrace(_T("listbox"), _T("Refreshing items %d..%d (%d-%d)"), + wxLogTrace(wxT("listbox"), wxT("Refreshing items %d..%d (%d-%d)"), m_updateFrom, m_updateFrom + m_updateCount - 1, rect.GetTop(), rect.GetBottom()); @@ -707,7 +715,7 @@ void wxListBox::DoDraw(wxControlRenderer *renderer) wxCoord lineHeight = GetLineHeight(); unsigned int itemFirst = yTop / lineHeight, itemLast = (yBottom + lineHeight - 1) / lineHeight, - itemMax = m_strings->GetCount(); + itemMax = GetCount(); if ( itemFirst >= itemMax ) return; @@ -716,7 +724,7 @@ void wxListBox::DoDraw(wxControlRenderer *renderer) itemLast = itemMax; // do draw them - wxLogTrace(_T("listbox"), _T("Repainting items %d..%d"), + wxLogTrace(wxT("listbox"), wxT("Repainting items %d..%d"), itemFirst, itemLast); DoDrawRange(renderer, itemFirst, itemLast); @@ -776,7 +784,7 @@ wxCoord wxListBox::GetMaxWidth() const { wxListBox *self = wxConstCast(this, wxListBox); wxCoord width; - unsigned int count = m_strings->GetCount(); + unsigned int count = GetCount(); for ( unsigned int n = 0; n < count; n++ ) { GetTextExtent(this->GetString(n), &width, NULL); @@ -833,7 +841,7 @@ wxSize wxListBox::DoGetBestClientSize() const wxCoord width = 0, height = 0; - unsigned int count = m_strings->GetCount(); + unsigned int count = GetCount(); for ( unsigned int n = 0; n < count; n++ ) { wxCoord w,h; @@ -937,7 +945,7 @@ bool wxListBox::FindItem(const wxString& prefix, bool strictlyAfter) int last = first == 0 ? count - 1 : first - 1; // if this is not true we'd never exit from the loop below! - wxASSERT_MSG( first < (int)count && last < (int)count, _T("logic error") ); + wxASSERT_MSG( first < (int)count && last < (int)count, wxT("logic error") ); // precompute it outside the loop size_t len = prefix.length(); @@ -1183,13 +1191,21 @@ bool wxListBox::PerformAction(const wxControlAction& action, AnchorSelection(item == -1 ? m_current : item); else if ( action == wxACTION_LISTBOX_SELECTALL || action == wxACTION_LISTBOX_SELTOGGLE ) - wxFAIL_MSG(_T("unimplemented yet")); + wxFAIL_MSG(wxT("unimplemented yet")); else return wxControl::PerformAction(action, numArg, strArg); return true; } +/* static */ +wxInputHandler *wxListBox::GetStdInputHandler(wxInputHandler *handlerDef) +{ + static wxStdListboxInputHandler s_handler(handlerDef); + + return &s_handler; +} + // ============================================================================ // implementation of wxStdListboxInputHandler // ============================================================================ @@ -1486,7 +1502,7 @@ bool wxStdListboxInputHandler::HandleMouseMove(wxInputConsumer *consumer, { // pass something into strArg to tell the listbox that it shouldn't // send the notification message: see PerformAction() above - lbox->PerformAction(m_actionMouse, item, _T("no")); + lbox->PerformAction(m_actionMouse, item, wxT("no")); } // else: don't pass invalid index to the listbox }