From adf8f9d0cd2d038f491c42ce51eca73733c29b02 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 27 Sep 2014 20:46:21 +0000 Subject: [PATCH] Handle multiple item insertion and deletion in wxSelectionStore. Rename the existing but not implemented and never used OnItemAdd() method to OnItemsInserted() and add OnItemsDeleted(), which is more efficient than OnItemDelete() when many items are being removed from the control at once. This is not used yet, but will be used in wxDataViewCtrl soon and maybe in the other controls later. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@77904 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/selstore.h | 10 ++++-- src/generic/selstore.cpp | 61 +++++++++++++++++++++++++++++++++++++ tests/misc/selstoretest.cpp | 33 ++++++++++++++++++++ 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/include/wx/selstore.h b/include/wx/selstore.h index fec3c212a4..02545e12b3 100644 --- a/include/wx/selstore.h +++ b/include/wx/selstore.h @@ -47,12 +47,16 @@ public: // special case of SetItemCount(0) void Clear() { m_itemsSel.Clear(); m_count = 0; m_defaultState = false; } - // must be called when a new item is inserted/added - void OnItemAdd(unsigned WXUNUSED(item)) { wxFAIL_MSG( wxT("TODO") ); } + // must be called when new items are inserted/added + void OnItemsInserted(unsigned item, unsigned numItems); - // must be called when an item is deleted + // must be called when an items is deleted void OnItemDelete(unsigned item); + // more efficient version for notifying the selection about deleting + // several items at once, return true if any of them were selected + bool OnItemsDeleted(unsigned item, unsigned numItems); + // select one item, use SelectRange() insted if possible! // // returns true if the items selection really changed diff --git a/src/generic/selstore.cpp b/src/generic/selstore.cpp index 8ea6066c65..5ce9e5cd41 100644 --- a/src/generic/selstore.cpp +++ b/src/generic/selstore.cpp @@ -190,6 +190,31 @@ bool wxSelectionStore::SelectRange(unsigned itemFrom, unsigned itemTo, // callbacks // ---------------------------------------------------------------------------- +void wxSelectionStore::OnItemsInserted(unsigned item, unsigned numItems) +{ + const size_t count = m_itemsSel.GetCount(); + + size_t idx = m_itemsSel.IndexForInsert(item); + + for ( size_t i = idx; i < count; i++ ) + { + m_itemsSel[i] += numItems; + } + + if ( m_defaultState ) + { + // All newly inserted items are not selected, so if the default state + // is to be selected, we need to manually add them to the deselected + // items indices. + for ( unsigned n = item; n < item + numItems; n++ ) + { + m_itemsSel.AddAt(item, idx++); + } + } + + m_count += numItems; +} + void wxSelectionStore::OnItemDelete(unsigned item) { size_t count = m_itemsSel.GetCount(), @@ -211,8 +236,44 @@ void wxSelectionStore::OnItemDelete(unsigned item) m_itemsSel[i++]--; } + + m_count--; } +bool wxSelectionStore::OnItemsDeleted(unsigned item, unsigned numItems) +{ + bool anyDeletedInSelItems = false, + allDeletedInSelItems = true; + + size_t i = m_itemsSel.IndexForInsert(item); + + const unsigned firstAfterDeleted = item + numItems; + while ( i < m_itemsSel.size() ) + { + if ( m_itemsSel[i] < firstAfterDeleted ) + { + // This item is going to be deleted, so remove it from the + // selected indices entirely. Notice that we do not update i here + // as it now refers to the next element. + m_itemsSel.RemoveAt(i); + + anyDeletedInSelItems = true; + } + else + { + // This item remains, just update its index. + m_itemsSel[i++] -= numItems; + + allDeletedInSelItems = false; + } + } + + m_count -= numItems; + + return m_defaultState ? allDeletedInSelItems : anyDeletedInSelItems; +} + + void wxSelectionStore::SetItemCount(unsigned count) { // forget about all items whose indices are now invalid if the size diff --git a/tests/misc/selstoretest.cpp b/tests/misc/selstoretest.cpp index 3184383f84..b1169625b5 100644 --- a/tests/misc/selstoretest.cpp +++ b/tests/misc/selstoretest.cpp @@ -46,6 +46,7 @@ private: CPPUNIT_TEST( SetItemCount ); CPPUNIT_TEST( Clear ); CPPUNIT_TEST( Iterate ); + CPPUNIT_TEST( ItemsAddDelete ); CPPUNIT_TEST_SUITE_END(); void SelectItem(); @@ -53,6 +54,7 @@ private: void SetItemCount(); void Clear(); void Iterate(); + void ItemsAddDelete(); // NB: must be even static const unsigned NUM_ITEMS; @@ -154,3 +156,34 @@ void SelStoreTestCase::Iterate() m_store->SelectItem(0, false); CPPUNIT_ASSERT_EQUAL(1, m_store->GetFirstSelectedItem(cookie)); } + +void SelStoreTestCase::ItemsAddDelete() +{ + m_store->SelectItem(0); + m_store->SelectItem(NUM_ITEMS/2); + m_store->SelectItem(NUM_ITEMS - 1); + + m_store->OnItemsInserted(NUM_ITEMS/2 + 1, 1); + CPPUNIT_ASSERT(m_store->IsSelected(0)); + CPPUNIT_ASSERT(m_store->IsSelected(NUM_ITEMS/2)); + CPPUNIT_ASSERT(m_store->IsSelected(NUM_ITEMS)); + CPPUNIT_ASSERT_EQUAL(3, m_store->GetSelectedCount()); + + CPPUNIT_ASSERT(m_store->OnItemsDeleted(NUM_ITEMS/2 - 1, 2)); + CPPUNIT_ASSERT(m_store->IsSelected(0)); + CPPUNIT_ASSERT(m_store->IsSelected(NUM_ITEMS - 2)); + CPPUNIT_ASSERT_EQUAL(2, m_store->GetSelectedCount()); + + m_store->OnItemsInserted(0, 2); + CPPUNIT_ASSERT(m_store->IsSelected(2)); + CPPUNIT_ASSERT(m_store->IsSelected(NUM_ITEMS)); + CPPUNIT_ASSERT_EQUAL(2, m_store->GetSelectedCount()); + + m_store->OnItemDelete(0); + + m_store->SelectRange(0, NUM_ITEMS - 1); + CPPUNIT_ASSERT(m_store->OnItemsDeleted(0, NUM_ITEMS/2)); + CPPUNIT_ASSERT_EQUAL(NUM_ITEMS/2, m_store->GetSelectedCount()); + CPPUNIT_ASSERT(m_store->IsSelected(0)); + CPPUNIT_ASSERT(m_store->IsSelected(NUM_ITEMS/2)); +}