summaryrefslogtreecommitdiffstats
path: root/svl/source/items/itemset.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'svl/source/items/itemset.cxx')
-rw-r--r--svl/source/items/itemset.cxx2068
1 files changed, 2068 insertions, 0 deletions
diff --git a/svl/source/items/itemset.cxx b/svl/source/items/itemset.cxx
new file mode 100644
index 0000000000..66780cb581
--- /dev/null
+++ b/svl/source/items/itemset.cxx
@@ -0,0 +1,2068 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <string.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+
+#include <libxml/xmlwriter.h>
+
+#include <sal/log.hxx>
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/setitem.hxx>
+#include <svl/whiter.hxx>
+#include <svl/voiditem.hxx>
+
+#include <items_helper.hxx>
+
+#ifdef DBG_UTIL
+static size_t nAllocatedSfxItemSetCount(0);
+static size_t nUsedSfxItemSetCount(0);
+static size_t nAllocatedSfxPoolItemHolderCount(0);
+static size_t nUsedSfxPoolItemHolderCount(0);
+size_t getAllocatedSfxItemSetCount() { return nAllocatedSfxItemSetCount; }
+size_t getUsedSfxItemSetCount() { return nUsedSfxItemSetCount; }
+size_t getAllocatedSfxPoolItemHolderCount() { return nAllocatedSfxPoolItemHolderCount; }
+size_t getUsedSfxPoolItemHolderCount() { return nUsedSfxPoolItemHolderCount; }
+#endif
+// NOTE: Only needed for one Item in SC (see notes below for
+// ScPatternAttr/ATTR_PATTERN). Still keep it so that when errors
+// come up to this change be able to quickly check using the
+// fallback flag 'ITEM_CLASSIC_MODE'
+static bool g_bItemClassicMode(getenv("ITEM_CLASSIC_MODE"));
+
+// I thought about this constructor a while, but when there is no
+// Item we need no cleanup at destruction (what we would need the
+// Pool for), so it is OK and makes default construction easier
+// when no Pool is needed. The other constructors guanantee that
+// there *cannot* be a state with Item set and Pool not set. IF
+// you change this class, ALWAYS ensure that this can not happen (!)
+SfxPoolItemHolder::SfxPoolItemHolder()
+: m_pPool(nullptr)
+, m_pItem(nullptr)
+#ifdef DBG_UTIL
+, m_bDeleted(false)
+#endif
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxPoolItemHolderCount++;
+ nUsedSfxPoolItemHolderCount++;
+#endif
+}
+
+SfxPoolItemHolder::SfxPoolItemHolder(SfxItemPool& rPool, const SfxPoolItem* pItem, bool bPassingOwnership)
+: m_pPool(&rPool)
+, m_pItem(pItem)
+#ifndef NDEBUG
+, m_bDeleted(false)
+#endif
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxPoolItemHolderCount++;
+ nUsedSfxPoolItemHolderCount++;
+#endif
+ if (nullptr != m_pItem)
+ m_pItem = implCreateItemEntry(*m_pPool, m_pItem, m_pItem->Which(), bPassingOwnership);
+}
+
+SfxPoolItemHolder::SfxPoolItemHolder(const SfxPoolItemHolder& rHolder)
+: m_pPool(rHolder.m_pPool)
+, m_pItem(rHolder.m_pItem)
+#ifndef NDEBUG
+, m_bDeleted(false)
+#endif
+{
+#ifdef DBG_UTIL
+ assert(!rHolder.isDeleted() && "Destructed instance used (!)");
+ nAllocatedSfxPoolItemHolderCount++;
+ nUsedSfxPoolItemHolderCount++;
+#endif
+ if (nullptr != m_pItem)
+ m_pItem = implCreateItemEntry(*m_pPool, m_pItem, m_pItem->Which(), false);
+}
+
+SfxPoolItemHolder::~SfxPoolItemHolder()
+{
+#ifdef DBG_UTIL
+ assert(!isDeleted() && "Destructed instance used (!)");
+ nAllocatedSfxPoolItemHolderCount--;
+#endif
+ if (nullptr != m_pItem)
+ implCleanupItemEntry(*m_pPool, m_pItem);
+#ifdef DBG_UTIL
+ m_bDeleted = true;
+#endif
+}
+
+const SfxPoolItemHolder& SfxPoolItemHolder::operator=(const SfxPoolItemHolder& rHolder)
+{
+ assert(!isDeleted() && "Destructed instance used (!)");
+ assert(!rHolder.isDeleted() && "Destructed instance used (!)");
+ if (this == &rHolder || *this == rHolder)
+ return *this;
+
+ if (nullptr != m_pItem)
+ implCleanupItemEntry(*m_pPool, m_pItem);
+
+ m_pPool = rHolder.m_pPool;
+ m_pItem = rHolder.m_pItem;
+
+ if (nullptr != m_pItem)
+ m_pItem = implCreateItemEntry(*m_pPool, m_pItem, m_pItem->Which(), false);
+
+ return *this;
+}
+
+bool SfxPoolItemHolder::operator==(const SfxPoolItemHolder &rHolder) const
+{
+ assert(!isDeleted() && "Destructed instance used (!)");
+ assert(!rHolder.isDeleted() && "Destructed instance used (!)");
+ return m_pPool == rHolder.m_pPool && areSfxPoolItemPtrsEqual(m_pItem, rHolder.m_pItem);
+}
+
+/**
+ * Ctor for a SfxItemSet with exactly the Which Ranges, which are known to
+ * the supplied SfxItemPool.
+ *
+ * For Sfx programmers: an SfxItemSet constructed in this way cannot
+ * contain any Items with SlotIds as Which values.
+ *
+ * Don't create ItemSets with full range before FreezeIdRanges()!
+ */
+SfxItemSet::SfxItemSet(SfxItemPool& rPool)
+ : m_pPool(&rPool)
+ , m_pParent(nullptr)
+ , m_nCount(0)
+ , m_nTotalCount(svl::detail::CountRanges(rPool.GetFrozenIdRanges()))
+ , m_bItemsFixed(false)
+ , m_ppItems(new SfxPoolItem const *[m_nTotalCount]{})
+ , m_pWhichRanges(rPool.GetFrozenIdRanges())
+ , m_aCallback()
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxItemSetCount++;
+ nUsedSfxItemSetCount++;
+#endif
+ assert(svl::detail::validRanges2(m_pWhichRanges));
+}
+
+SfxItemSet::SfxItemSet( SfxItemPool& rPool, SfxAllItemSetFlag )
+ : m_pPool(&rPool)
+ , m_pParent(nullptr)
+ , m_nCount(0)
+ , m_nTotalCount(0)
+ , m_bItemsFixed(false)
+ , m_ppItems(nullptr)
+ , m_pWhichRanges()
+ , m_aCallback()
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxItemSetCount++;
+ nUsedSfxItemSetCount++;
+#endif
+}
+
+/** special constructor for SfxItemSetFixed */
+SfxItemSet::SfxItemSet( SfxItemPool& rPool, WhichRangesContainer&& ranges, SfxPoolItem const ** ppItems, sal_uInt16 nTotalCount )
+ : m_pPool(&rPool)
+ , m_pParent(nullptr)
+ , m_nCount(0)
+ , m_nTotalCount(nTotalCount)
+ , m_bItemsFixed(true)
+ , m_ppItems(ppItems)
+ , m_pWhichRanges(std::move(ranges))
+ , m_aCallback()
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxItemSetCount++;
+ nUsedSfxItemSetCount++;
+#endif
+ assert(ppItems);
+ assert(m_pWhichRanges.size() > 0);
+ assert(svl::detail::validRanges2(m_pWhichRanges));
+}
+
+SfxItemSet::SfxItemSet(SfxItemPool& pool, WhichRangesContainer wids)
+ : m_pPool(&pool)
+ , m_pParent(nullptr)
+ , m_nCount(0)
+ , m_nTotalCount(svl::detail::CountRanges(wids))
+ , m_bItemsFixed(false)
+ , m_ppItems(new SfxPoolItem const *[m_nTotalCount]{})
+ , m_pWhichRanges(std::move(wids))
+ , m_aCallback()
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxItemSetCount++;
+ nUsedSfxItemSetCount++;
+#endif
+ assert(svl::detail::CountRanges(m_pWhichRanges) != 0);
+ assert(svl::detail::validRanges2(m_pWhichRanges));
+}
+
+SfxPoolItem const* implCreateItemEntry(SfxItemPool& rPool, SfxPoolItem const* pSource, sal_uInt16 nWhich, bool bPassingOwnership)
+{
+ if (nullptr == pSource)
+ // SfxItemState::UNKNOWN aka current default (nullptr)
+ // just use/return nullptr
+ return nullptr;
+
+ if (IsInvalidItem(pSource))
+ // SfxItemState::DONTCARE aka invalid item
+ // just use pSource which equals INVALID_POOL_ITEM
+ return pSource;
+
+ if (pSource->isExceptionalSCItem() && rPool.GetMasterPool()->newItem_UseDirect(*pSource))
+ // exceptional handling for *some* items, see SC
+ // (do not copy item: use directly, it is a pool default)
+ return pSource;
+
+ // CAUTION: static default items are not *that* static as it seems
+ // (or: should be). If they are freed with the Pool (see
+ // ::ReleaseDefaults) they will be deleted. Same is true for
+ // dynamic defaults. Thus currently no default can be shared
+ // at all since these may be deleted with the pool owning them.
+ // That these are not shared but cloned is ensured by those
+ // having a default RefCount of zero, so these are used merely as
+ // templates.
+ // if (IsStaticDefaultItem(pSource))
+ // return pSource;
+
+ if (0 == pSource->Which())
+ // These *should* be SfxVoidItem(0) the only Items with 0 == WhichID,
+ // these need to be cloned (currently...)
+ return pSource->Clone();
+
+ // get correct target WhichID
+ if (0 == nWhich)
+ nWhich = pSource->Which();
+
+ if (SfxItemPool::IsSlot(nWhich))
+ {
+ // SlotItems were always cloned in original (even when bPassingOwnership),
+ // so do that here, too (but without bPassingOwnership).
+ // They do not need to be registered at pool (actually impossible, pools
+ // do not have entries for SlotItems) so handle here early
+ if (!bPassingOwnership)
+ pSource = pSource->Clone(rPool.GetMasterPool());
+
+ if (pSource->Which() != nWhich)
+ const_cast<SfxPoolItem*>(pSource)->SetWhich(nWhich);
+
+ pSource->AddRef();
+ return pSource;
+ }
+
+ // get the pool with which ItemSets have to work, plus get the
+ // pool at which the WhichID is defined, so calls to it do not
+ // have to do this repeatedly
+ SfxItemPool* pMasterPool(rPool.GetMasterPool());
+ assert(nullptr != pMasterPool);
+ SfxItemPool* pTargetPool(pMasterPool);
+ while (!pTargetPool->IsInRange(nWhich))
+ pTargetPool = pTargetPool->GetSecondaryPool();
+
+ // if this goes wrong, an Item with invalid ID for this pool is
+ // processed. This is not allowed (and should not happen, e.g.
+ // ItemSets already have WhichRanges that are checked against
+ // their Pool)
+ if (nullptr == pTargetPool)
+ {
+ assert(false);
+ return pSource;
+ }
+
+ // CAUTION: Shareable_Impl and NeedsPoolRegistration_Impl
+ // use index, not WhichID (one more reason to change the Pools)
+ const sal_uInt16 nIndex(pTargetPool->GetIndex_Impl(nWhich));
+
+ // the Item itself is shareable when it already is used somewhere
+ // which is equivalent to be referenced already. IsPooledItem also
+ // checked for SFX_ITEMS_MAXREF, that is not needed here. Use a
+ // fake 'while' loop and 'break' to make this better readable
+
+ // only try to share items that are already shared somehow, else
+ // these items are probably not (heap) item-ptr's (but on the
+ // stack or else)
+ while(pSource->GetRefCount() > 0)
+ {
+ if (pSource->Which() != nWhich)
+ // If the target WhichID differs from the WhichID of a shared Item
+ // the item needs to be cloned. This happens, e.g. in
+ // ScPatternAttr::GetFromEditItemSet existing items with WhichIDs
+ // from EditEngine get set at a SetItem's ItemSet with different
+ // target ranges (e.g. look for ATTR_FONT_UNDERLINE)
+ break;
+
+ if (!pTargetPool->Shareable_Impl(nIndex))
+ // not shareable, done
+ break;
+
+ // SfxSetItems cannot be shared if they are in/use another pool
+ if (pSource->isSetItem() && static_cast<const SfxSetItem*>(pSource)->GetItemSet().GetPool() != pMasterPool)
+ break;
+
+ // If the Item is registered it is pool-dependent, so do not share when
+ // it is registered but not at this pool
+ if (pSource->isRegisteredAtPool() && !pTargetPool->isSfxPoolItemRegisteredAtThisPool(*pSource))
+ break;
+
+ // If we get here we can share the Item
+ pSource->AddRef();
+ return pSource;
+ }
+
+ // g_bItemClassicMode: try finding already existing item
+ // NOTE: the UnitTest testIteratorsDefPattern claims that that Item "can be
+ // edited by the user" which explains why it breaks so many rules for Items,
+ // it behaves like an alien. That Item in the SC Pool claims to be a
+ // 'StaticDefault' and gets changed (..?)
+
+ // only do this if classic mode or required (calls from Pool::Direct*)
+ while(g_bItemClassicMode || pSource->isExceptionalSCItem())
+ {
+ if (!pTargetPool->Shareable_Impl(nIndex))
+ // not shareable, so no need to search for identical item
+ break;
+
+ // try to get equal Item. This is the expensive part...
+ const SfxPoolItem* pExisting(pTargetPool->tryToGetEqualItem(*pSource, nWhich));
+
+ if (nullptr == pExisting)
+ // none found, done
+ break;
+
+ if (0 == pExisting->GetRefCount())
+ // do not share not-yet shared Items (should not happen)
+ break;
+
+ if (bPassingOwnership)
+ // need to cleanup if we are offered to own pSource
+ delete pSource;
+
+ // If we get here we can share the found Item
+ pExisting->AddRef();
+ return pExisting;
+ }
+
+ // check if the handed over and to be directly used item is a
+ // SfxSetItem, that would make it pool-dependent. It then must have
+ // the same target-pool, ensure that by the cost of cloning it
+ // (should not happen)
+ if (bPassingOwnership
+ && pSource->isSetItem()
+ && static_cast<const SfxSetItem*>(pSource)->GetItemSet().GetPool() != pMasterPool)
+ {
+ const SfxPoolItem* pOld(pSource);
+ pSource = pSource->Clone(pMasterPool);
+ delete pOld;
+ }
+
+ // when we reach this line we know that we have to add/create a new item. If
+ // bPassingOwnership is given just use the item, else clone it
+ if (!bPassingOwnership)
+ pSource = pSource->Clone(pMasterPool);
+
+ // ensure WhichID @Item
+ if (pSource->Which() != nWhich)
+ const_cast<SfxPoolItem*>(pSource)->SetWhich(nWhich);
+
+ // increase RefCnt 0->1
+ pSource->AddRef();
+
+ // Unfortunately e,g, SC does 'special' things for some new Items,
+ // so we need to give the opportunity for this. To limit this to
+ // the needed cases, use m_bExceptionalSCItem flag at item
+ if (pSource->isExceptionalSCItem())
+ pMasterPool->newItem_Callback(*pSource);
+
+ // try to register @Pool (only needed if not yet registered)
+ if (!pSource->isRegisteredAtPool())
+ {
+ bool bRegisterAtPool(pSource->isExceptionalSCItem());
+
+ if (!bRegisterAtPool)
+ {
+ if (g_bItemClassicMode)
+ {
+ // in classic mode register only/all shareable items
+ bRegisterAtPool = pTargetPool->Shareable_Impl(nIndex);
+ }
+ else
+ {
+ // in new mode register only/all items marked as need to be registered
+ bRegisterAtPool = pTargetPool->NeedsPoolRegistration_Impl(nIndex);
+ }
+ }
+
+ if (bRegisterAtPool)
+ pTargetPool->registerSfxPoolItem(*pSource);
+ }
+
+ return pSource;
+}
+
+void implCleanupItemEntry(SfxItemPool& rPool, SfxPoolItem const* pSource)
+{
+ if (nullptr == pSource)
+ // no entry, done
+ return;
+
+ if (IsInvalidItem(pSource))
+ // nothing to do for invalid item entries
+ return;
+
+ if (pSource->isExceptionalSCItem() && rPool.GetMasterPool()->newItem_UseDirect(*pSource))
+ // exceptional handling for *some* items, see SC
+ // do not delete Item, it is a pool default
+ return;
+
+ if (0 == pSource->Which())
+ {
+ // de-register when registered @pool
+ if (pSource->isRegisteredAtPool())
+ rPool.unregisterSfxPoolItem(*pSource);
+
+ // These *should* be SfxVoidItem(0) the only Items with 0 == WhichID
+ // and need to be deleted
+ delete pSource;
+ return;
+ }
+
+ if (1 < pSource->GetRefCount())
+ {
+ // Still multiple references present, so just alter the RefCount
+ pSource->ReleaseRef();
+ return;
+ }
+
+ if (IsDefaultItem(pSource))
+ // default items (static and dynamic) are owned by the pool, do not delete
+ return;
+
+ // decrease RefCnt before deleting (destructor asserts for it and that's
+ // good to find other errors)
+ pSource->ReleaseRef();
+
+ // de-register before deletion when registered @pool
+ if (pSource->isRegisteredAtPool())
+ rPool.unregisterSfxPoolItem(*pSource);
+
+ // delete Item
+ delete pSource;
+}
+
+SfxItemSet::SfxItemSet( const SfxItemSet& rASet )
+ : m_pPool( rASet.m_pPool )
+ , m_pParent( rASet.m_pParent )
+ , m_nCount( rASet.m_nCount )
+ , m_nTotalCount( rASet.m_nTotalCount )
+ , m_bItemsFixed(false)
+ , m_ppItems(nullptr)
+ , m_pWhichRanges( rASet.m_pWhichRanges )
+ , m_aCallback(rASet.m_aCallback)
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxItemSetCount++;
+ nUsedSfxItemSetCount++;
+#endif
+ if (rASet.GetRanges().empty())
+ {
+ return;
+ }
+
+ if (0 == rASet.Count())
+ {
+ // no Items set in source ItemSet, allocate new array
+ // *plus* init to nullptr
+ m_ppItems = new const SfxPoolItem* [TotalCount()]{};
+ return;
+ }
+
+ // allocate new array (no need to initialize, will be done below)
+ m_ppItems = new const SfxPoolItem* [TotalCount()];
+
+ // Copy attributes
+ SfxPoolItem const** ppDst(m_ppItems);
+
+ for (const auto& rSource : rASet)
+ {
+ *ppDst = implCreateItemEntry(*GetPool(), rSource, 0, false);
+ ppDst++;
+ }
+
+ assert(svl::detail::validRanges2(m_pWhichRanges));
+}
+
+SfxItemSet::SfxItemSet(SfxItemSet&& rASet) noexcept
+ : m_pPool( rASet.m_pPool )
+ , m_pParent( rASet.m_pParent )
+ , m_nCount( rASet.m_nCount )
+ , m_nTotalCount( rASet.m_nTotalCount )
+ , m_bItemsFixed(false)
+ , m_ppItems( rASet.m_ppItems )
+ , m_pWhichRanges( std::move(rASet.m_pWhichRanges) )
+ , m_aCallback(rASet.m_aCallback)
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxItemSetCount++;
+ nUsedSfxItemSetCount++;
+#endif
+ if (rASet.m_bItemsFixed)
+ {
+ // have to make a copy
+ m_ppItems = new const SfxPoolItem* [TotalCount()];
+
+ // can just copy the pointers, the ones in the original m_ppItems
+ // array will no longer be used/referenced (unused mem, but not lost,
+ // it's part of the ItemSet-derived object)
+ std::copy(rASet.m_ppItems, rASet.m_ppItems + TotalCount(), m_ppItems);
+ }
+ else
+ {
+ // taking over ownership
+ rASet.m_nTotalCount = 0;
+ rASet.m_ppItems = nullptr;
+ }
+ rASet.m_pPool = nullptr;
+ rASet.m_pParent = nullptr;
+ rASet.m_nCount = 0;
+
+ assert(svl::detail::validRanges2(m_pWhichRanges));
+}
+
+SfxItemSet::~SfxItemSet()
+{
+#ifdef DBG_UTIL
+ nAllocatedSfxItemSetCount--;
+#endif
+ // cleanup items. No std::fill needed, we are done with this ItemSet.
+ // the callback is not set in destructor, so no worries about that
+ ClearAllItemsImpl();
+
+ if (!m_bItemsFixed)
+ {
+ // free SfxPoolItem array
+ delete[] m_ppItems;
+ }
+
+ // for invariant-testing
+ m_pWhichRanges.reset();
+}
+
+// Delete single Items or all Items (nWhich == 0)
+sal_uInt16 SfxItemSet::ClearItem( sal_uInt16 nWhich )
+{
+ if( !Count() )
+ return 0;
+
+ if( nWhich )
+ return ClearSingleItem_ForWhichID(nWhich);
+
+ // clear all & reset to nullptr
+ const sal_uInt16 nRetval(ClearAllItemsImpl());
+ std::fill(begin(), begin() + TotalCount(), nullptr);
+ return nRetval;
+}
+
+sal_uInt16 SfxItemSet::ClearSingleItem_ForWhichID( sal_uInt16 nWhich )
+{
+ const sal_uInt16 nOffset(GetRanges().getOffsetFromWhich(nWhich));
+
+ if (INVALID_WHICHPAIR_OFFSET != nOffset)
+ {
+ // found, continue with offset
+ return ClearSingleItem_ForOffset(nOffset);
+ }
+
+ // not found, return sal_uInt16 nDel = 0;
+ return 0;
+}
+
+sal_uInt16 SfxItemSet::ClearSingleItem_ForOffset( sal_uInt16 nOffset )
+{
+ assert(nOffset < TotalCount());
+ const_iterator aEntry(begin() + nOffset);
+ assert(nullptr == *aEntry || IsInvalidItem(*aEntry) || (*aEntry)->isVoidItem() || 0 != (*aEntry)->Which());
+
+ if (nullptr == *aEntry)
+ // no entry, done
+ return 0;
+
+ // we reset an entry to nullptr -> decrement count
+ --m_nCount;
+
+ // Notification-Callback
+ if(m_aCallback)
+ {
+ m_aCallback(*aEntry, nullptr);
+ }
+
+ // cleanup item & reset ptr
+ implCleanupItemEntry(*GetPool(), *aEntry);
+ *aEntry = nullptr;
+
+ return 1;
+}
+
+sal_uInt16 SfxItemSet::ClearAllItemsImpl()
+{
+ if (0 == Count())
+ // no items set, done
+ return 0;
+
+ // loop & cleanup items
+ for (auto& rCandidate : *this)
+ {
+ if (nullptr != rCandidate && m_aCallback)
+ {
+ m_aCallback(rCandidate, nullptr);
+ }
+
+ implCleanupItemEntry(*GetPool(), rCandidate);
+ }
+
+ // remember count before resetting it, that is the retval
+ const sal_uInt16 nRetval(Count());
+ m_nCount = 0;
+ return nRetval;
+}
+
+void SfxItemSet::ClearInvalidItems()
+{
+ if (0 == Count())
+ // no items set, done
+ return;
+
+ // loop, here using const_iterator due to need to set ptr in m_ppItems array
+ for (const_iterator candidate(begin()); candidate != end(); candidate++)
+ {
+ if (IsInvalidItem(*candidate))
+ {
+ *candidate = nullptr;
+ --m_nCount;
+ }
+ }
+}
+
+void SfxItemSet::InvalidateAllItems()
+{
+ // instead of just asserting, do the change. Clear all items
+ ClearAllItemsImpl();
+
+ // set all to invalid
+ std::fill(begin(), begin() + TotalCount(), INVALID_POOL_ITEM);
+
+ // ...and correct the count - invalid items get counted
+ m_nCount = m_nTotalCount;
+}
+
+SfxItemState SfxItemSet::GetItemState_ForWhichID( SfxItemState eState, sal_uInt16 nWhich, bool bSrchInParent, const SfxPoolItem **ppItem) const
+{
+ const sal_uInt16 nOffset(GetRanges().getOffsetFromWhich(nWhich));
+
+ if (INVALID_WHICHPAIR_OFFSET != nOffset)
+ {
+ // found, continue with offset
+ eState = GetItemState_ForOffset(nOffset, ppItem);
+ }
+
+ // search in parent?
+ if (bSrchInParent && nullptr != GetParent() && (SfxItemState::UNKNOWN == eState || SfxItemState::DEFAULT == eState))
+ {
+ // nOffset was only valid for *local* SfxItemSet, need to continue with WhichID
+ // Use the *highest* SfxItemState as result
+ return GetParent()->GetItemState_ForWhichID( eState, nWhich, true, ppItem);
+ }
+
+ return eState;
+}
+
+SfxItemState SfxItemSet::GetItemState_ForOffset( sal_uInt16 nOffset, const SfxPoolItem **ppItem) const
+{
+ // check and assert from invalid offset. The caller is responsible for
+ // ensuring a valid offset (see callers, all checked & safe)
+ assert(nOffset < TotalCount());
+ SfxPoolItem const* pCandidate(*(begin() + nOffset));
+
+ if (nullptr == pCandidate)
+ // set to Default
+ return SfxItemState::DEFAULT;
+
+ if (IsInvalidItem(pCandidate))
+ // Different ones are present
+ return SfxItemState::DONTCARE;
+
+ if (pCandidate->isVoidItem())
+ // Item is Disabled
+ return SfxItemState::DISABLED;
+
+ if (nullptr != ppItem)
+ // if we have the Item, add it to output an hand back
+ *ppItem = pCandidate;
+
+ // Item is set
+ return SfxItemState::SET;
+}
+
+bool SfxItemSet::HasItem(sal_uInt16 nWhich, const SfxPoolItem** ppItem) const
+{
+ const bool bRet(SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, true, ppItem));
+
+ // we need to reset ppItem when it was *not* set by GetItemState_ForWhichID
+ // since many usages of that return parameter re-use it, so it might still
+ // be set to 'something'
+ if (!bRet && nullptr != ppItem)
+ {
+ *ppItem = nullptr;
+ }
+
+ return bRet;
+}
+
+const SfxPoolItem* SfxItemSet::PutImpl(const SfxPoolItem& rItem, sal_uInt16 nWhich, bool bPassingOwnership)
+{
+ bool bActionNeeded(0 != nWhich);
+ sal_uInt16 nOffset(INVALID_WHICHPAIR_OFFSET);
+
+#ifdef _WIN32
+ // Win build *insists* for initialization, triggers warning C4701:
+ // "potentially uninitialized local variable 'aEntry' used" for
+ // lines below. This is not the case here, but I know of no way
+ // to silence the warning in another way
+ const_iterator aEntry(begin());
+#else
+ const_iterator aEntry;
+#endif
+
+ if (bActionNeeded)
+ {
+ nOffset = GetRanges().getOffsetFromWhich(nWhich);
+ bActionNeeded = (INVALID_WHICHPAIR_OFFSET != nOffset);
+ }
+
+ if (bActionNeeded)
+ {
+ aEntry = begin() + nOffset;
+
+ if (nullptr == *aEntry)
+ {
+ // increase count if there was no entry before
+ ++m_nCount;
+ }
+ else
+ {
+ // compare items, evtl. containing content compare
+ bActionNeeded = !SfxPoolItem::areSame(**aEntry, rItem);
+ }
+ }
+
+ if (!bActionNeeded)
+ {
+ if (bPassingOwnership)
+ {
+ delete &rItem;
+ }
+
+ return nullptr;
+ }
+
+ // prepare new entry
+ SfxPoolItem const* pNew(implCreateItemEntry(*GetPool(), &rItem, nWhich, bPassingOwnership));
+
+ // Notification-Callback
+ if(m_aCallback)
+ {
+ m_aCallback(*aEntry, pNew);
+ }
+
+ // cleanup old entry & set entry at m_ppItems array
+ implCleanupItemEntry(*GetPool(), *aEntry);
+ *aEntry = pNew;
+
+ return pNew;
+}
+
+bool SfxItemSet::Put(const SfxItemSet& rSource, bool bInvalidAsDefault)
+{
+ if (0 == rSource.Count())
+ // no items in source, done
+ return false;
+
+ const_iterator aSource(rSource.begin());
+ sal_uInt16 nNumberToGo(rSource.Count());
+ bool bRetval(false);
+
+ // iterate based on WhichIDs to have it available for evtl. PutImpl calls
+ for (const WhichPair& rPair : rSource.GetRanges())
+ {
+ for (sal_uInt16 nWhich(rPair.first); nWhich <= rPair.second; nWhich++, aSource++)
+ {
+ if (nullptr != *aSource)
+ {
+ nNumberToGo--;
+
+ if (IsInvalidItem(*aSource))
+ {
+ if (bInvalidAsDefault)
+ {
+ bRetval |= 0 != ClearSingleItem_ForWhichID(nWhich);
+ }
+ else
+ {
+ InvalidateItem_ForWhichID(nWhich);
+ }
+ }
+ else
+ {
+ bRetval |= nullptr != PutImpl(**aSource, nWhich, false);
+ }
+ }
+
+ if (0 == nNumberToGo)
+ {
+ // we can return early if all set Items are handled
+ return bRetval;
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+/**
+ * This method takes the Items from the 'rSet' and adds to '*this'.
+ * Which ranges in '*this' that are non-existent in 'rSet' will not
+ * be altered. The Which range of '*this' is also not changed.
+ *
+ * Items set in 'rSet' are also set in '*this'.
+ * Default (0 pointer) and Invalid (-1 pointer) Items are processed
+ * according to their parameter 'eDontCareAs' and 'eDefaultAs':
+ *
+ * SfxItemState::SET: Hard set to the default of the Pool
+ * SfxItemState::DEFAULT: Deleted (0 pointer)
+ * SfxItemState::DONTCARE: Invalid (-1 pointer)
+ *
+ * NB: All other values for 'eDontCareAs' and 'eDefaultAs' are invalid
+ */
+void SfxItemSet::PutExtended
+(
+ const SfxItemSet& rSource, // Source of the Items to be put
+ SfxItemState eDontCareAs, // What will happen to the DontCare Items
+ SfxItemState eDefaultAs // What will happen to the Default Items
+)
+{
+ // don't "optimize" with "if( rSource.Count()" because of dontcare + defaults
+ const_iterator aSource(rSource.begin());
+
+ for (const WhichPair& rPair : rSource.GetRanges())
+ {
+ for (sal_uInt16 nWhich = rPair.first; nWhich <= rPair.second; nWhich++, aSource++)
+ {
+ if (nullptr != *aSource)
+ {
+ if (IsInvalidItem(*aSource))
+ {
+ // Item is DontCare:
+ switch (eDontCareAs)
+ {
+ case SfxItemState::SET:
+ PutImpl(rSource.GetPool()->GetDefaultItem(nWhich), nWhich, false);
+ break;
+
+ case SfxItemState::DEFAULT:
+ ClearSingleItem_ForWhichID(nWhich);
+ break;
+
+ case SfxItemState::DONTCARE:
+ InvalidateItem_ForWhichID(nWhich);
+ break;
+
+ default:
+ assert(!"invalid Argument for eDontCareAs");
+ }
+ }
+ else
+ {
+ // Item is set:
+ PutImpl(**aSource, nWhich, false);
+ }
+ }
+ else
+ {
+ // Item is default:
+ switch (eDefaultAs)
+ {
+ case SfxItemState::SET:
+ PutImpl(rSource.GetPool()->GetDefaultItem(nWhich), nWhich, false);
+ break;
+
+ case SfxItemState::DEFAULT:
+ ClearSingleItem_ForWhichID(nWhich);
+ break;
+
+ case SfxItemState::DONTCARE:
+ InvalidateItem_ForWhichID(nWhich);
+ break;
+
+ default:
+ assert(!"invalid Argument for eDefaultAs");
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Expands the ranges of settable items by 'nFrom' to 'nTo'. Keeps state of
+ * items which are new ranges too.
+ */
+void SfxItemSet::MergeRange( sal_uInt16 nFrom, sal_uInt16 nTo )
+{
+ // check if all from new range are already included. This will
+ // use the cache in WhichRangesContainer since we check linearly.
+ // Start with assuming all are included, but only if not empty.
+ // If empty all included is wrong (and GetRanges().MergeRange
+ // will do the right thing/shortcut)
+ bool bAllIncluded(!GetRanges().empty());
+
+ for (sal_uInt16 a(nFrom); bAllIncluded && a <= nTo; a++)
+ if (INVALID_WHICHPAIR_OFFSET == GetRanges().getOffsetFromWhich(a))
+ bAllIncluded = false;
+
+ // if yes, we are done
+ if (bAllIncluded)
+ return;
+
+ // need to create new WhichRanges
+ auto pNewRanges = m_pWhichRanges.MergeRange(nFrom, nTo);
+ RecreateRanges_Impl(pNewRanges);
+ m_pWhichRanges = std::move(pNewRanges);
+}
+
+/**
+ * Modifies the ranges of settable items. Keeps state of items which
+ * are new ranges too.
+ */
+void SfxItemSet::SetRanges( const WhichRangesContainer& pNewRanges )
+{
+ // Identical Ranges?
+ if (GetRanges() == pNewRanges)
+ return;
+
+ assert(svl::detail::validRanges2(pNewRanges));
+ RecreateRanges_Impl(pNewRanges);
+ m_pWhichRanges = pNewRanges;
+}
+
+void SfxItemSet::SetRanges( WhichRangesContainer&& pNewRanges )
+{
+ // Identical Ranges?
+ if (GetRanges() == pNewRanges)
+ return;
+
+ assert(svl::detail::validRanges2(pNewRanges));
+ RecreateRanges_Impl(pNewRanges);
+ m_pWhichRanges = std::move(pNewRanges);
+}
+
+void SfxItemSet::RecreateRanges_Impl(const WhichRangesContainer& rNewRanges)
+{
+ // create new item-array (by iterating through all new ranges)
+ const sal_uInt16 nNewTotalCount(svl::detail::CountRanges(rNewRanges));
+ SfxPoolItem const** aNewItemArray(new const SfxPoolItem* [nNewTotalCount]);
+ sal_uInt16 nNewCount(0);
+
+ if (0 == Count())
+ {
+ // no Items set, just reset to nullptr (default)
+ std::fill(aNewItemArray, aNewItemArray + nNewTotalCount, nullptr);
+ }
+ else
+ {
+ // iterate over target - all entries there need to be set/initialized. Use
+ // WhichIDs, we need to access two different WhichRanges
+ const_iterator aNewTarget(aNewItemArray);
+
+ for (auto const & rNewRange : rNewRanges)
+ {
+ for (sal_uInt16 nWhich(rNewRange.first); nWhich <= rNewRange.second; nWhich++, aNewTarget++)
+ {
+ // check if we have that WhichID in source ranges
+ const sal_uInt16 nSourceOffset(GetRanges().getOffsetFromWhich(nWhich));
+
+ if (INVALID_WHICHPAIR_OFFSET == nSourceOffset)
+ {
+ // no entry in source, init to nullptr
+ *aNewTarget = nullptr;
+ }
+ else
+ {
+ // we have entry in source, transfer entry - whatever it may be,
+ // also for nullptr (for initialization)
+ const_iterator aSourceEntry(begin() + nSourceOffset);
+ *aNewTarget = *aSourceEntry;
+
+ // take care of new Count
+ if (nullptr != *aNewTarget)
+ {
+ nNewCount++;
+ }
+
+ // reset entry, since it got transferred it should not be cleaned-up
+ *aSourceEntry = nullptr;
+ }
+ }
+ }
+
+ // free old items: only necessary when not all Items were transferred
+ if (nNewCount != Count())
+ {
+ ClearAllItemsImpl();
+ }
+ }
+
+ // replace old items-array and ranges
+ if (m_bItemsFixed)
+ {
+ m_bItemsFixed = false;
+ }
+ else
+ {
+ delete[] m_ppItems;
+ }
+
+ m_nTotalCount = nNewTotalCount;
+ m_ppItems = aNewItemArray;
+ m_nCount = nNewCount;
+}
+
+/**
+ * The SfxItemSet takes over exactly those SfxPoolItems that are
+ * set in rSet and are in their own Which range. All others are removed.
+ * The SfxItemPool is retained, such that SfxPoolItems that have been
+ * taken over, are moved from the rSet's SfxItemPool to the SfxItemPool
+ * of *this.
+ *
+ * SfxPoolItems in rSet, for which holds 'IsInvalidItem() == true' are
+ * taken over as invalid items.
+ *
+ * @return bool true
+ * SfxPoolItems have been taken over
+ *
+ * false
+ * No SfxPoolItems have been taken over, because
+ * e.g. the Which ranges of SfxItemSets are not intersecting
+ * or the intersection does not contain SfxPoolItems that are
+ * set in rSet
+ */
+bool SfxItemSet::Set
+(
+ const SfxItemSet& rSet, /* The SfxItemSet, whose SfxPoolItems are
+ to been taken over */
+
+ bool bDeep /* true (default)
+
+ The SfxPoolItems from the parents that may
+ be present in rSet, are also taken over into
+ this SfxPoolItemSet
+
+ false
+ The SfxPoolItems from the parents of
+ rSet are not taken into account */
+)
+{
+ bool bRet = false;
+ if (Count())
+ ClearItem();
+ if ( bDeep )
+ {
+ SfxWhichIter aIter1(*this);
+ SfxWhichIter aIter2(rSet);
+ sal_uInt16 nWhich1 = aIter1.FirstWhich();
+ sal_uInt16 nWhich2 = aIter2.FirstWhich();
+ for (;;)
+ {
+ if (!nWhich1 || !nWhich2)
+ break;
+ if (nWhich1 > nWhich2)
+ {
+ nWhich2 = aIter2.NextWhich();
+ continue;
+ }
+ if (nWhich1 < nWhich2)
+ {
+ nWhich1 = aIter1.NextWhich();
+ continue;
+ }
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == aIter2.GetItemState( true, &pItem ) )
+ bRet |= nullptr != Put( *pItem, pItem->Which() );
+ nWhich1 = aIter1.NextWhich();
+ nWhich2 = aIter2.NextWhich();
+ }
+ }
+ else
+ bRet = Put(rSet, false);
+
+ return bRet;
+}
+
+const SfxPoolItem* SfxItemSet::GetItem(sal_uInt16 nId, bool bSearchInParent) const
+{
+ // evtl. Convert from SlotID to WhichId
+ const sal_uInt16 nWhich(GetPool()->GetWhich(nId));
+
+ // Is the Item set or 'bDeep == true' available?
+ const SfxPoolItem *pItem(nullptr);
+ const SfxItemState eState(GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, bSearchInParent, &pItem));
+
+ if (bSearchInParent && SfxItemState::DEFAULT == eState && SfxItemPool::IsWhich(nWhich))
+ {
+ pItem = &GetPool()->GetDefaultItem(nWhich);
+ }
+
+ return pItem;
+}
+
+const SfxPoolItem& SfxItemSet::Get( sal_uInt16 nWhich, bool bSrchInParent) const
+{
+ // Search the Range in which the Which is located in:
+ const sal_uInt16 nOffset(GetRanges().getOffsetFromWhich(nWhich));
+
+ if (INVALID_WHICHPAIR_OFFSET != nOffset)
+ {
+ const_iterator aFoundOne(begin() + nOffset);
+
+ if (nullptr != *aFoundOne)
+ {
+ if (IsInvalidItem(*aFoundOne))
+ {
+ //FIXME: The following code is duplicated further down
+ assert(m_pPool);
+ //!((SfxAllItemSet *)this)->aDefault.SetWhich(nWhich);
+ //!return aDefault;
+ return GetPool()->GetDefaultItem(nWhich);
+ }
+#ifdef DBG_UTIL
+ const SfxPoolItem *pItem = *aFoundOne;
+ if ( pItem->isVoidItem() || !pItem->Which() )
+ SAL_INFO("svl.items", "SFX_WARNING: Getting disabled Item");
+#endif
+ return **aFoundOne;
+ }
+ }
+
+ if (bSrchInParent && nullptr != GetParent())
+ {
+ return GetParent()->Get(nWhich, bSrchInParent);
+ }
+
+ // Get the Default from the Pool and return
+ assert(m_pPool);
+ return GetPool()->GetDefaultItem(nWhich);
+}
+
+/**
+ * Only retain the Items that are also present in rSet
+ * (nevermind their value).
+ */
+void SfxItemSet::Intersect( const SfxItemSet& rSet )
+{
+ // Delete all Items not contained in rSet
+ assert(m_pPool && "Not implemented without Pool");
+
+ if (!Count() || this == &rSet)
+ // none set -> none to delete
+ // same ItemSet? -> no Items not contained
+ return;
+
+ if (!rSet.Count())
+ {
+ // no Items contained in rSet -> Delete everything
+ ClearItem();
+ return;
+ }
+
+ // CAUTION: In the former impl, the
+ // - version for different ranges checked for SfxItemState::UNKNOWN
+ // in rSet -> this means that the WhichID is *not* defined in
+ // the ranges of rSet *at all* > definitely an *error*
+ // - version for same ranges checked for
+ // nullptr != local && nullptr == rSet.
+ // All together I think also using the text
+ // "Delete all Items not contained in rSet" leads to
+ // locally delete all Items that are *not* set in rSet
+ // -> != SfxItemState::SET
+
+ // same ranges?
+ if (GetRanges() == rSet.GetRanges())
+ {
+ for (sal_uInt16 nOffset(0); nOffset < TotalCount(); nOffset++)
+ {
+ if (SfxItemState::SET != rSet.GetItemState_ForOffset(nOffset, nullptr))
+ {
+ // can use same offset
+ ClearSingleItem_ForOffset(nOffset);
+ }
+ }
+ }
+ else
+ {
+ sal_uInt16 nOffset(0);
+
+ for (auto const & rRange : GetRanges())
+ {
+ for (sal_uInt16 nWhich(rRange.first); nWhich <= rRange.second; nWhich++, nOffset++)
+ {
+ if (SfxItemState::SET != rSet.GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, nullptr))
+ {
+ // can use offset
+ ClearSingleItem_ForOffset(nOffset);
+ }
+ }
+ }
+ }
+}
+
+void SfxItemSet::Differentiate(const SfxItemSet& rSet)
+{
+ assert(m_pPool && "Not implemented without Pool");
+
+ // Delete all Items contained in rSet
+ if (!Count() || !rSet.Count())
+ // None set?
+ return;
+
+ if (this == &rSet)
+ {
+ // same ItemSet, all Items are contained -> Delete everything
+ ClearItem();
+ return;
+ }
+
+ // CAUTION: In the former impl, the
+ // - version for different ranges checked for SfxItemState::SET
+ // in rSet
+ // - version for same ranges checked for
+ // nullptr != local && nullptr != rSet.
+ // All together I think also using the text
+ // "Delete all Items contained in rSet" leads to
+ // locally delete all Items that *are *not* set in rSet
+ // -> ==SfxItemState::SET
+
+ // same ranges?
+ if (GetRanges() == rSet.GetRanges())
+ {
+ for (sal_uInt16 nOffset(0); nOffset < TotalCount(); nOffset++)
+ {
+ if (SfxItemState::SET == rSet.GetItemState_ForOffset(nOffset, nullptr))
+ {
+ // can use same offset
+ ClearSingleItem_ForOffset(nOffset);
+ }
+ }
+ }
+ else
+ {
+ sal_uInt16 nOffset(0);
+
+ for (auto const & rRange : GetRanges())
+ {
+ for (sal_uInt16 nWhich(rRange.first); nWhich <= rRange.second; nWhich++, nOffset++)
+ {
+ if (SfxItemState::SET == rSet.GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, nullptr))
+ {
+ // can use offset
+ ClearSingleItem_ForOffset(nOffset);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Decision table for MergeValue(s)
+ *
+ * Principles:
+ * 1. If the Which value in the 1st set is "unknown", there's never any action
+ * 2. If the Which value in the 2nd set is "unknown", it's made the "default"
+ * 3. For comparisons the values of the "default" Items are take into account
+ *
+ * 1st Item 2nd Item Values bIgnoreDefs Remove Assign Add
+ *
+ * set set == sal_False - - -
+ * default set == sal_False - - -
+ * dontcare set == sal_False - - -
+ * unknown set == sal_False - - -
+ * set default == sal_False - - -
+ * default default == sal_False - - -
+ * dontcare default == sal_False - - -
+ * unknown default == sal_False - - -
+ * set dontcare == sal_False 1st Item -1 -
+ * default dontcare == sal_False - -1 -
+ * dontcare dontcare == sal_False - - -
+ * unknown dontcare == sal_False - - -
+ * set unknown == sal_False 1st Item -1 -
+ * default unknown == sal_False - - -
+ * dontcare unknown == sal_False - - -
+ * unknown unknown == sal_False - - -
+ *
+ * set set != sal_False 1st Item -1 -
+ * default set != sal_False - -1 -
+ * dontcare set != sal_False - - -
+ * unknown set != sal_False - - -
+ * set default != sal_False 1st Item -1 -
+ * default default != sal_False - - -
+ * dontcare default != sal_False - - -
+ * unknown default != sal_False - - -
+ * set dontcare != sal_False 1st Item -1 -
+ * default dontcare != sal_False - -1 -
+ * dontcare dontcare != sal_False - - -
+ * unknown dontcare != sal_False - - -
+ * set unknown != sal_False 1st Item -1 -
+ * default unknown != sal_False - - -
+ * dontcare unknown != sal_False - - -
+ * unknown unknown != sal_False - - -
+ *
+ * set set == sal_True - - -
+ * default set == sal_True - 2nd Item 2nd Item
+ * dontcare set == sal_True - - -
+ * unknown set == sal_True - - -
+ * set default == sal_True - - -
+ * default default == sal_True - - -
+ * dontcare default == sal_True - - -
+ * unknown default == sal_True - - -
+ * set dontcare == sal_True - - -
+ * default dontcare == sal_True - -1 -
+ * dontcare dontcare == sal_True - - -
+ * unknown dontcare == sal_True - - -
+ * set unknown == sal_True - - -
+ * default unknown == sal_True - - -
+ * dontcare unknown == sal_True - - -
+ * unknown unknown == sal_True - - -
+ *
+ * set set != sal_True 1st Item -1 -
+ * default set != sal_True - 2nd Item 2nd Item
+ * dontcare set != sal_True - - -
+ * unknown set != sal_True - - -
+ * set default != sal_True - - -
+ * default default != sal_True - - -
+ * dontcare default != sal_True - - -
+ * unknown default != sal_True - - -
+ * set dontcare != sal_True 1st Item -1 -
+ * default dontcare != sal_True - -1 -
+ * dontcare dontcare != sal_True - - -
+ * unknown dontcare != sal_True - - -
+ * set unknown != sal_True - - -
+ * default unknown != sal_True - - -
+ * dontcare unknown != sal_True - - -
+ * unknown unknown != sal_True - - -
+ */
+void SfxItemSet::MergeItem_Impl(const SfxPoolItem **ppFnd1, const SfxPoolItem *pFnd2, bool bIgnoreDefaults)
+{
+ assert(ppFnd1 != nullptr && "Merging to 0-Item");
+
+ // 1st Item is Default?
+ if ( !*ppFnd1 )
+ {
+ if ( IsInvalidItem(pFnd2) )
+ // Decision table: default, dontcare, doesn't matter, doesn't matter
+ *ppFnd1 = INVALID_POOL_ITEM;
+
+ else if ( pFnd2 && !bIgnoreDefaults &&
+ GetPool()->GetDefaultItem(pFnd2->Which()) != *pFnd2 )
+ // Decision table: default, set, !=, sal_False
+ *ppFnd1 = INVALID_POOL_ITEM;
+
+ else if ( pFnd2 && bIgnoreDefaults )
+ // Decision table: default, set, doesn't matter, sal_True
+ *ppFnd1 = implCreateItemEntry(*GetPool(), pFnd2, 0, false);
+ // *ppFnd1 = &GetPool()->Put( *pFnd2 );
+
+ if ( *ppFnd1 )
+ ++m_nCount;
+ }
+
+ // 1st Item set?
+ else if ( !IsInvalidItem(*ppFnd1) )
+ {
+ if ( !pFnd2 )
+ {
+ // 2nd Item is Default
+ if ( !bIgnoreDefaults &&
+ **ppFnd1 != GetPool()->GetDefaultItem((*ppFnd1)->Which()) )
+ {
+ // Decision table: set, default, !=, sal_False
+ implCleanupItemEntry(*GetPool(), *ppFnd1);
+ // GetPool()->Remove( **ppFnd1 );
+ *ppFnd1 = INVALID_POOL_ITEM;
+ }
+ }
+ else if ( IsInvalidItem(pFnd2) )
+ {
+ // 2nd Item is dontcare
+ if ( !bIgnoreDefaults ||
+ **ppFnd1 != GetPool()->GetDefaultItem( (*ppFnd1)->Which()) )
+ {
+ // Decision table: set, dontcare, doesn't matter, sal_False
+ // or: set, dontcare, !=, sal_True
+ implCleanupItemEntry(*GetPool(), *ppFnd1);
+ // GetPool()->Remove( **ppFnd1 );
+ *ppFnd1 = INVALID_POOL_ITEM;
+ }
+ }
+ else
+ {
+ // 2nd Item is set
+ if ( **ppFnd1 != *pFnd2 )
+ {
+ // Decision table: set, set, !=, doesn't matter
+ implCleanupItemEntry(*GetPool(), *ppFnd1);
+ // GetPool()->Remove( **ppFnd1 );
+ *ppFnd1 = INVALID_POOL_ITEM;
+ }
+ }
+ }
+}
+
+void SfxItemSet::MergeValues( const SfxItemSet& rSet )
+{
+ // WARNING! When making changes/fixing bugs, always update the table above!!
+ assert( GetPool() == rSet.GetPool() && "MergeValues with different Pools" );
+
+ // CAUTION: Old version did *different* things when the WhichRanges
+ // were the same (true) or different (false) (which is an error/
+ // false optimization):
+ // true: MergeItem_Impl was directly fed with SfxItem*'s
+ // for entry @this & @rSet
+ // false: Looped over rSet WhichID's, fetched defaults from pool,
+ // fed all that to SfxItemSet::MergeValue which then
+ // evtl. could not find that WhichID in local WhichRanges
+ // Better to loop over local WhichRanges (these get changed) and look
+ // for Item with same WhichID in rSet, this is done now.
+ if (GetRanges() == rSet.GetRanges())
+ {
+
+ // loop over both & merge, WhichIDs are identical
+ for (const_iterator dst(begin()), src(rSet.begin()); dst != end(); dst++, src++)
+ {
+ MergeItem_Impl(dst, *src, false/*bIgnoreDefaults*/);
+ }
+ }
+ else
+ {
+ // loop over local which-ranges - the local Items need to be changed
+ const_iterator dst(begin());
+
+ for (auto const & rRange : GetRanges())
+ {
+ for (sal_uInt16 nWhich(rRange.first); nWhich <= rRange.second; nWhich++, dst++)
+ {
+ // try to get offset in rSet for same WhichID
+ const sal_uInt16 nOffset(rSet.GetRanges().getOffsetFromWhich(nWhich));
+
+ if (INVALID_WHICHPAIR_OFFSET != nOffset)
+ {
+ // if entry with same WhichID exists in rSet, merge with local entry
+ MergeItem_Impl(dst, *(rSet.begin() + nOffset), false/*bIgnoreDefaults*/);
+ }
+ }
+ }
+ }
+}
+
+void SfxItemSet::MergeValue(const SfxPoolItem& rAttr, bool bIgnoreDefaults)
+{
+ if (0 == rAttr.Which())
+ // seems to be SfxVoidItem(0), nothing to do
+ return;
+
+ const sal_uInt16 nOffset(GetRanges().getOffsetFromWhich(rAttr.Which()));
+
+ if (INVALID_WHICHPAIR_OFFSET != nOffset)
+ {
+ MergeItem_Impl(begin() + nOffset, &rAttr, bIgnoreDefaults);
+ }
+}
+
+void SfxItemSet::InvalidateItem_ForWhichID(sal_uInt16 nWhich)
+{
+ const sal_uInt16 nOffset(GetRanges().getOffsetFromWhich(nWhich));
+
+ if (INVALID_WHICHPAIR_OFFSET != nOffset)
+ {
+ InvalidateItem_ForOffset(nOffset);
+ }
+}
+
+void SfxItemSet::InvalidateItem_ForOffset(sal_uInt16 nOffset)
+{
+ // check and assert from invalid offset. The caller is responsible for
+ // ensuring a valid offset (see callers, all checked & safe)
+ assert(nOffset < TotalCount());
+ const_iterator aFoundOne(begin() + nOffset);
+
+ if (nullptr == *aFoundOne)
+ {
+ // entry goes from nullptr -> invalid
+ ++m_nCount;
+ }
+ else
+ {
+ // entry is set
+ if (IsInvalidItem(*aFoundOne))
+ // already invalid item, done
+ return;
+
+ // cleanup entry
+ implCleanupItemEntry(*GetPool(), *aFoundOne);
+ }
+
+ // set new entry
+ *aFoundOne = INVALID_POOL_ITEM;
+}
+
+sal_uInt16 SfxItemSet::GetWhichByOffset( sal_uInt16 nOffset ) const
+{
+ assert(nOffset < TotalCount());
+
+ // 1st try to get a set SfxPoolItem and fetch the WhichID from there.
+ const SfxPoolItem* pItem(nullptr);
+ (void)GetItemState_ForOffset(nOffset, &pItem);
+
+ if (nullptr != pItem && 0 != pItem->Which())
+ return pItem->Which();
+
+ // 2nd have to get from WhichRangesContainer. That might use
+ // the buffering, too. We might assert a return value of zero
+ // (which means invalid WhichID), but we already assert for
+ // a valid offset at the start of this method
+ return GetRanges().getWhichFromOffset(nOffset);
+}
+
+bool SfxItemSet::operator==(const SfxItemSet &rCmp) const
+{
+ return Equals( rCmp, true);
+}
+
+bool SfxItemSet::Equals(const SfxItemSet &rCmp, bool bComparePool) const
+{
+ // check if same incarnation
+ if (this == &rCmp)
+ return true;
+
+ // check parents (if requested, also bComparePool)
+ if (bComparePool && GetParent() != rCmp.GetParent())
+ return false;
+
+ // check pools (if requested)
+ if (bComparePool && GetPool() != rCmp.GetPool())
+ return false;
+
+ // check count of set items
+ if (Count() != rCmp.Count())
+ return false;
+
+ // both have no items, done
+ if (0 == Count())
+ return true;
+
+ // check if ranges are equal
+ if (GetRanges() == rCmp.GetRanges())
+ {
+ // if yes, we can simplify: are all pointers the same?
+ if (0 == memcmp( m_ppItems, rCmp.m_ppItems, TotalCount() * sizeof(m_ppItems[0]) ))
+ return true;
+
+ // compare each one separately
+ const SfxPoolItem **ppItem1(m_ppItems);
+ const SfxPoolItem **ppItem2(rCmp.m_ppItems);
+
+ for (sal_uInt16 nPos(0); nPos < TotalCount(); nPos++)
+ {
+ // do full SfxPoolItem compare
+ if (!SfxPoolItem::areSame(*ppItem1, *ppItem2))
+ return false;
+ ++ppItem1;
+ ++ppItem2;
+ }
+
+ return true;
+ }
+
+ // Not same ranges, need to compare content. Only need to check if
+ // the content of one is inside the other due to already having
+ // compared Count() above.
+ // Iterate over local SfxItemSet by using locval ranges and offset,
+ // so we can access needed data at least for one SfxItemSet more
+ // direct. For the 2nd one we need the WhichID which we have by
+ // iterating over the ranges.
+ sal_uInt16 nOffset(0);
+ sal_uInt16 nNumberToGo(Count());
+
+ for (auto const & rRange : GetRanges())
+ {
+ for (sal_uInt16 nWhich(rRange.first); nWhich <= rRange.second; nWhich++, nOffset++)
+ {
+ const SfxPoolItem *pItem1(nullptr);
+ const SfxPoolItem *pItem2(nullptr);
+ const SfxItemState aStateA(GetItemState_ForOffset(nOffset, &pItem1));
+ const SfxItemState aStateB(rCmp.GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem2));
+
+ if (aStateA != aStateB)
+ return false;
+
+ // only compare items if SfxItemState::SET, else the item ptrs are not set
+ if (SfxItemState::SET == aStateA && !SfxPoolItem::areSame(pItem1, pItem2))
+ return false;
+
+ if (SfxItemState::DEFAULT != aStateA)
+ // if local item is not-nullptr we have compared one more, reduce NumberToGo
+ // NOTE: we could also use 'nullptr != *(begin() + nOffset)' here, but the
+ // entry was already checked by GetItemState_ForOffset above
+ nNumberToGo--;
+
+ if (0 == nNumberToGo)
+ // if we have compared Count() number of items and are still here
+ // (all were equal), we can exit early
+ return true;
+ }
+ }
+
+ return true;
+}
+
+std::unique_ptr<SfxItemSet> SfxItemSet::Clone(bool bItems, SfxItemPool *pToPool ) const
+{
+ if (pToPool && pToPool != GetPool())
+ {
+ std::unique_ptr<SfxItemSet> pNewSet(new SfxItemSet(*pToPool, GetRanges()));
+ if ( bItems )
+ {
+ SfxWhichIter aIter(*pNewSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ const SfxPoolItem* pItem;
+ if ( SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem ) )
+ pNewSet->Put( *pItem, pItem->Which() );
+ nWhich = aIter.NextWhich();
+ }
+ }
+ return pNewSet;
+ }
+ else
+ return std::unique_ptr<SfxItemSet>(bItems
+ ? new SfxItemSet(*this)
+ : new SfxItemSet(*GetPool(), GetRanges()));
+}
+
+SfxItemSet SfxItemSet::CloneAsValue(bool bItems, SfxItemPool *pToPool ) const
+{
+ // if you are trying to clone, then the thing you are cloning is polymorphic, which means
+ // it cannot be cloned as a value
+ assert((typeid(*this) == typeid(SfxItemSet)) && "cannot call this on a subclass of SfxItemSet");
+
+ if (pToPool && pToPool != GetPool())
+ {
+ SfxItemSet aNewSet(*pToPool, GetRanges());
+ if ( bItems )
+ {
+ SfxWhichIter aIter(aNewSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ const SfxPoolItem* pItem;
+ if ( SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem ) )
+ aNewSet.Put( *pItem, pItem->Which() );
+ nWhich = aIter.NextWhich();
+ }
+ }
+ return aNewSet;
+ }
+ else
+ return bItems
+ ? *this
+ : SfxItemSet(*GetPool(), GetRanges());
+}
+
+void SfxItemSet::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SfxItemSet"));
+ SfxItemIter aIter(*this);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if (IsInvalidItem(pItem))
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("invalid"));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ else
+ {
+ pItem->dumpAsXml(pWriter);
+ }
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+// ----------------------------------------------- class SfxAllItemSet
+
+SfxAllItemSet::SfxAllItemSet( SfxItemPool &rPool )
+: SfxItemSet(rPool, SfxAllItemSetFlag::Flag)
+{
+}
+
+SfxAllItemSet::SfxAllItemSet(const SfxItemSet &rCopy)
+: SfxItemSet(rCopy)
+{
+}
+
+/**
+ * Explicitly define this ctor to avoid auto-generation by the compiler.
+ * The compiler does not take the ctor with the 'const SfxItemSet&'!
+ */
+SfxAllItemSet::SfxAllItemSet(const SfxAllItemSet &rCopy)
+: SfxItemSet(rCopy)
+{
+}
+
+/**
+ * Putting with automatic extension of the WhichId with the ID of the Item.
+ */
+const SfxPoolItem* SfxAllItemSet::PutImpl( const SfxPoolItem& rItem, sal_uInt16 nWhich, bool bPassingOwnership )
+{
+ MergeRange(nWhich, nWhich);
+ return SfxItemSet::PutImpl(rItem, nWhich, bPassingOwnership);
+}
+
+/**
+ * Disable Item
+ * Using a VoidItem with Which value 0
+ */
+void SfxItemSet::DisableItem(sal_uInt16 nWhich)
+{
+ Put( SfxVoidItem(0), nWhich );
+}
+
+std::unique_ptr<SfxItemSet> SfxAllItemSet::Clone(bool bItems, SfxItemPool *pToPool ) const
+{
+ if (pToPool && pToPool != GetPool())
+ {
+ std::unique_ptr<SfxAllItemSet> pNewSet(new SfxAllItemSet( *pToPool ));
+ if ( bItems )
+ pNewSet->Set( *this );
+ return pNewSet;
+ }
+ else
+ return std::unique_ptr<SfxItemSet>(bItems ? new SfxAllItemSet(*this) : new SfxAllItemSet(*GetPool()));
+}
+
+
+WhichRangesContainer::WhichRangesContainer( const WhichPair* wids, sal_Int32 nSize )
+{
+ auto p = new WhichPair[nSize];
+ for (int i=0; i<nSize; ++i)
+ p[i] = wids[i];
+ m_pairs = p;
+ m_size = nSize;
+ m_bOwnRanges = true;
+ m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
+ m_aLastWhichPairFirst = 0;
+ m_aLastWhichPairSecond = 0;
+}
+
+WhichRangesContainer::WhichRangesContainer(sal_uInt16 nWhichStart, sal_uInt16 nWhichEnd)
+ : m_size(1), m_bOwnRanges(true)
+{
+ auto p = new WhichPair[1];
+ p[0] = { nWhichStart, nWhichEnd };
+ m_pairs = p;
+ m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
+ m_aLastWhichPairFirst = 0;
+ m_aLastWhichPairSecond = 0;
+}
+
+WhichRangesContainer::WhichRangesContainer(WhichRangesContainer && other)
+{
+ std::swap(m_pairs, other.m_pairs);
+ std::swap(m_size, other.m_size);
+ std::swap(m_bOwnRanges, other.m_bOwnRanges);
+ m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
+ m_aLastWhichPairFirst = 0;
+ m_aLastWhichPairSecond = 0;
+}
+
+WhichRangesContainer& WhichRangesContainer::operator=(WhichRangesContainer && other)
+{
+ std::swap(m_pairs, other.m_pairs);
+ std::swap(m_size, other.m_size);
+ std::swap(m_bOwnRanges, other.m_bOwnRanges);
+ m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
+ m_aLastWhichPairFirst = 0;
+ m_aLastWhichPairSecond = 0;
+ return *this;
+}
+
+WhichRangesContainer& WhichRangesContainer::operator=(WhichRangesContainer const & other)
+{
+ reset();
+ m_size = other.m_size;
+ m_bOwnRanges = other.m_bOwnRanges;
+ m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
+ m_aLastWhichPairFirst = 0;
+ m_aLastWhichPairSecond = 0;
+ if (m_bOwnRanges)
+ {
+ auto p = new WhichPair[m_size];
+ for (int i=0; i<m_size; ++i)
+ p[i] = other.m_pairs[i];
+ m_pairs = p;
+ }
+ else
+ m_pairs = other.m_pairs;
+ return *this;
+}
+
+WhichRangesContainer::~WhichRangesContainer()
+{
+ reset();
+}
+
+bool WhichRangesContainer::operator==(WhichRangesContainer const & other) const
+{
+ if (m_size != other.m_size)
+ return false;
+ if (m_pairs == other.m_pairs)
+ return true;
+ return std::equal(m_pairs, m_pairs + m_size, other.m_pairs, other.m_pairs + m_size);
+}
+
+
+void WhichRangesContainer::reset()
+{
+ if (m_bOwnRanges)
+ {
+ delete [] m_pairs;
+ m_bOwnRanges = false;
+ }
+ m_pairs = nullptr;
+ m_size = 0;
+ m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
+ m_aLastWhichPairFirst = 0;
+ m_aLastWhichPairSecond = 0;
+}
+
+#ifdef DBG_UTIL
+static size_t g_nHit(0);
+static size_t g_nMiss(1);
+static bool g_bShowWhichRangesHitRate(getenv("SVL_SHOW_WHICHRANGES_HITRATE"));
+static void isHit() { g_nHit++; }
+static void isMiss()
+{
+ g_nMiss++;
+ const double fHitRate(double(g_nHit) /double(g_nMiss));
+ if (0 == g_nMiss % 1000 && g_bShowWhichRangesHitRate)
+ SAL_WARN("svl", "ITEM: hits: " << g_nHit << " misses: " << g_nMiss << " hits/misses(rate): " << fHitRate);
+}
+#endif
+
+sal_uInt16 WhichRangesContainer::getOffsetFromWhich(sal_uInt16 nWhich) const
+{
+ if (empty())
+ return INVALID_WHICHPAIR_OFFSET;
+
+ // special case for single entry - happens often e.g. UI stuff
+ if (1 == m_size)
+ {
+ if( m_pairs->first <= nWhich && nWhich <= m_pairs->second )
+ return nWhich - m_pairs->first;
+
+ // we have only one WhichPair entry and it's not contained -> failed
+ return INVALID_WHICHPAIR_OFFSET;
+ }
+
+ // check if nWhich is inside last successfully used WhichPair
+ if (INVALID_WHICHPAIR_OFFSET != m_aLastWhichPairOffset
+ && m_aLastWhichPairFirst <= nWhich
+ && nWhich <= m_aLastWhichPairSecond)
+ {
+#ifdef DBG_UTIL
+ isHit();
+#endif
+ // we can re-use the last found WhichPair
+ return m_aLastWhichPairOffset + (nWhich - m_aLastWhichPairFirst);
+ }
+
+#ifdef DBG_UTIL
+ isMiss();
+#endif
+
+ // we have to find the correct WhichPair, iterate linear. This
+ // also directly updates the buffered m_aLastWhichPair* values
+ m_aLastWhichPairOffset = 0;
+
+ for (const WhichPair& rPair : *this)
+ {
+ // Within this range?
+ if( rPair.first <= nWhich && nWhich <= rPair.second )
+ {
+ // found, remember parameters for buffered hits
+ m_aLastWhichPairFirst = rPair.first;
+ m_aLastWhichPairSecond = rPair.second;
+
+ // ...and return
+ return m_aLastWhichPairOffset + (nWhich - m_aLastWhichPairFirst);
+ }
+
+ m_aLastWhichPairOffset += rPair.second - rPair.first + 1;
+ }
+
+ // *need* to reset: if 1st WhichPair only one entry it could be 1
+ // what could wrongly trigger re-use above for next search
+ m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
+
+ return m_aLastWhichPairOffset;
+}
+
+sal_uInt16 WhichRangesContainer::getWhichFromOffset(sal_uInt16 nOffset) const
+{
+ // check for empty, if yes, return null which is an invalid WhichID
+ if (empty())
+ return 0;
+
+ // special case for single entry - happens often e.g. UI stuff
+ if (1 == m_size)
+ {
+ if (nOffset <= m_pairs->second - m_pairs->first)
+ return m_pairs->first + nOffset;
+
+ // we have only one WhichPair entry and it's not contained -> failed
+ return 0;
+ }
+
+ // check if nWhich is inside last successfully used WhichPair
+ if (INVALID_WHICHPAIR_OFFSET != m_aLastWhichPairOffset)
+ {
+ // only try if we are beyond or at m_aLastWhichPairOffset to
+ // not get numerically negative
+ if (nOffset >= m_aLastWhichPairOffset)
+ {
+ const sal_uInt16 nAdaptedOffset(nOffset - m_aLastWhichPairOffset);
+
+ if (nAdaptedOffset <= m_aLastWhichPairSecond - m_aLastWhichPairFirst)
+ {
+#ifdef DBG_UTIL
+ isHit();
+#endif
+ return m_aLastWhichPairFirst + nAdaptedOffset;
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ isMiss();
+#endif
+
+ // Iterate over WhichPairs in WhichRangesContainer
+ // Do not update buffered last hit (m_aLastWhichPair*), these calls
+ // are potentially more rare than getOffsetFromWhich calls. Still,
+ // it could also be done here
+ for( auto const & pPtr : *this )
+ {
+ const sal_uInt16 nWhichPairRange(pPtr.second - pPtr.first);
+ if( nOffset <= nWhichPairRange )
+ return pPtr.first + nOffset;
+ nOffset -= nWhichPairRange + 1;
+ }
+
+ // no WhichID found, return invalid one
+ return 0;
+}
+
+// Adds a range to which ranges, keeping the ranges in valid state (sorted, non-overlapping)
+WhichRangesContainer WhichRangesContainer::MergeRange(sal_uInt16 nFrom,
+ sal_uInt16 nTo) const
+{
+ assert(svl::detail::validRange(nFrom, nTo));
+
+ if (empty())
+ return WhichRangesContainer(nFrom, nTo);
+
+ // reset buffer
+ m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
+
+ // create vector of ranges (sal_uInt16 pairs of lower and upper bound)
+ const size_t nOldCount = size();
+ // Allocate one item more than we already have.
+ // In the worst case scenario we waste a little bit
+ // of memory, but we avoid another allocation, which is more important.
+ std::unique_ptr<WhichPair[]> aRangesTable(new WhichPair[nOldCount+1]);
+ int aRangesTableSize = 0;
+ bool bAdded = false;
+ for (const auto& rPair : *this)
+ {
+ if (!bAdded && rPair.first >= nFrom)
+ { // insert new range, keep ranges sorted
+ aRangesTable[aRangesTableSize++] = { nFrom, nTo };
+ bAdded = true;
+ }
+ // insert current range
+ aRangesTable[aRangesTableSize++] = rPair;
+ }
+ if (!bAdded)
+ aRangesTable[aRangesTableSize++] = { nFrom, nTo };
+
+ // true if ranges overlap or adjoin, false if ranges are separate
+ auto needMerge = [](WhichPair lhs, WhichPair rhs) {
+ return (lhs.first - 1) <= rhs.second && (rhs.first - 1) <= lhs.second;
+ };
+
+ auto it = aRangesTable.get();
+ auto endIt = aRangesTable.get() + aRangesTableSize;
+ // we have at least one range at this point
+ for (;;)
+ {
+ auto itNext = std::next(it);
+ if (itNext == endIt)
+ break;
+ // check if neighbouring ranges overlap or adjoin
+ if (needMerge(*it, *itNext))
+ {
+ // lower bounds are sorted, implies: it->first = min(it[0].first, it[1].first)
+ it->second = std::max(it->second, itNext->second);
+ // remove next element
+ std::move(std::next(itNext), endIt, itNext);
+ --aRangesTableSize;
+ endIt = aRangesTable.get() + aRangesTableSize;
+ }
+ else
+ ++it;
+ }
+
+ return WhichRangesContainer(std::move(aRangesTable), aRangesTableSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */