1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
26 #include <unordered_map>
28 #include <libxml/xmlwriter.h>
30 #include <sal/log.hxx>
31 #include <svl/itemset.hxx>
32 #include <svl/itempool.hxx>
33 #include <svl/itemiter.hxx>
34 #include <svl/setitem.hxx>
35 #include <svl/whiter.hxx>
36 #include <svl/voiditem.hxx>
39 static size_t nAllocatedSfxItemSetCount(0);
40 static size_t nUsedSfxItemSetCount(0);
41 static size_t nAllocatedSfxPoolItemHolderCount(0);
42 static size_t nUsedSfxPoolItemHolderCount(0);
43 size_t getAllocatedSfxItemSetCount() { return nAllocatedSfxItemSetCount
; }
44 size_t getUsedSfxItemSetCount() { return nUsedSfxItemSetCount
; }
45 size_t getAllocatedSfxPoolItemHolderCount() { return nAllocatedSfxPoolItemHolderCount
; }
46 size_t getUsedSfxPoolItemHolderCount() { return nUsedSfxPoolItemHolderCount
; }
48 // <TotalCount, <number of entries, sum of used count>>
49 typedef std::unordered_map
<sal_uInt16
, std::pair
<sal_uInt32
, sal_uInt32
>> ItemArrayUsage
;
50 static ItemArrayUsage aItemArrayUsage
;
52 static void addArrayUsage(sal_uInt16 nCount
, sal_uInt16 nTotalCount
)
54 ItemArrayUsage::iterator
aHit(aItemArrayUsage
.find(nTotalCount
));
55 if (aHit
== aItemArrayUsage
.end())
57 aItemArrayUsage
.insert({nTotalCount
, {1, nCount
}});
61 aHit
->second
.second
+= nCount
;
64 SVL_DLLPUBLIC
void listSfxItemSetUsage()
67 sal_uInt16 nTotalCount
;
68 sal_uInt32 nAppearances
;
69 sal_uInt32 nAllUsedCount
;
70 sorted(sal_uInt16 _nTotalCount
, sal_uInt32 _nAppearances
, sal_uInt32 _nAllUsedCount
)
71 : nTotalCount(_nTotalCount
), nAppearances(_nAppearances
), nAllUsedCount(_nAllUsedCount
) {}
72 bool operator<(const sorted
& rDesc
) const { return nTotalCount
> rDesc
.nTotalCount
; }
74 std::vector
<sorted
> aSorted
;
75 aSorted
.reserve(aItemArrayUsage
.size());
76 for (const auto& rEntry
: aItemArrayUsage
)
77 aSorted
.emplace_back(rEntry
.first
, rEntry
.second
.first
, rEntry
.second
.second
);
78 std::sort(aSorted
.begin(), aSorted
.end());
79 SAL_INFO("svl.items", "ITEM: List of " << aItemArrayUsage
.size() << " SfxItemPool TotalCounts with usages:");
80 double fAllFillRatePercent(0.0);
82 sal_uInt32
nAllocated(0);
83 for (const auto& rEntry
: aSorted
)
85 const sal_uInt32
nAllCount(rEntry
.nAppearances
* rEntry
.nTotalCount
);
86 const double fFillRatePercent(0 == nAllCount
? 0.0 : (static_cast<double>(rEntry
.nAllUsedCount
) / static_cast<double>(nAllCount
)) * 100.0);
88 " TotalCount: " << rEntry
.nTotalCount
89 << " Appearances: " << rEntry
.nAppearances
90 << " FillRate(%): " << fFillRatePercent
);
91 fAllFillRatePercent
+= fFillRatePercent
;
92 nUsed
+= rEntry
.nAllUsedCount
;
93 nAllocated
+= rEntry
.nTotalCount
* rEntry
.nAppearances
;
95 SAL_INFO("svl.items", " Average FillRate(%): " << fAllFillRatePercent
/ aItemArrayUsage
.size());
96 SAL_INFO("svl.items", " Used: " << nUsed
<< " Allocated: " << nAllocated
);
97 SAL_INFO("svl.items", " Average Used/Allocated(%): " << (static_cast<double>(nUsed
) / static_cast<double>(nAllocated
)) * 100.0);
100 // NOTE: Only needed for one Item in SC (see notes below for
101 // ScPatternAttr). Still keep it so that when errors
102 // come up to this change be able to quickly check using the
103 // fallback flag 'ITEM_CLASSIC_MODE'
105 // I thought about this constructor a while, but when there is no
106 // Item we need no cleanup at destruction (what we would need the
107 // Pool for), so it is OK and makes default construction easier
108 // when no Pool is needed. The other constructors guarantee that
109 // there *cannot* be a state with Item set and Pool not set. IF
110 // you change this class, ALWAYS ensure that this can not happen (!)
111 SfxPoolItemHolder::SfxPoolItemHolder()
119 nAllocatedSfxPoolItemHolderCount
++;
120 nUsedSfxPoolItemHolderCount
++;
124 SfxPoolItemHolder::SfxPoolItemHolder(SfxItemPool
& rPool
, const SfxPoolItem
* pItem
, bool bPassingOwnership
)
132 nAllocatedSfxPoolItemHolderCount
++;
133 nUsedSfxPoolItemHolderCount
++;
135 if (nullptr != m_pItem
)
136 m_pItem
= implCreateItemEntry(getPool(), m_pItem
, bPassingOwnership
);
137 if (nullptr != m_pItem
&& getPool().NeedsSurrogateSupport(m_pItem
->Which()))
138 getPool().registerPoolItemHolder(*this);
141 SfxPoolItemHolder::SfxPoolItemHolder(const SfxPoolItemHolder
& rHolder
)
142 : m_pPool(rHolder
.m_pPool
)
143 , m_pItem(rHolder
.m_pItem
)
149 assert(!rHolder
.isDeleted() && "Destructed instance used (!)");
150 nAllocatedSfxPoolItemHolderCount
++;
151 nUsedSfxPoolItemHolderCount
++;
153 if (nullptr != m_pItem
)
154 m_pItem
= implCreateItemEntry(getPool(), m_pItem
, false);
155 if (nullptr != m_pItem
&& getPool().NeedsSurrogateSupport(m_pItem
->Which()))
156 getPool().registerPoolItemHolder(*this);
159 SfxPoolItemHolder::~SfxPoolItemHolder()
162 assert(!isDeleted() && "Destructed instance used (!)");
163 nAllocatedSfxPoolItemHolderCount
--;
165 if (nullptr != m_pItem
&& getPool().NeedsSurrogateSupport(m_pItem
->Which()))
166 getPool().unregisterPoolItemHolder(*this);
167 if (nullptr != m_pItem
)
168 implCleanupItemEntry(m_pItem
);
174 const SfxPoolItemHolder
& SfxPoolItemHolder::operator=(const SfxPoolItemHolder
& rHolder
)
176 assert(!isDeleted() && "Destructed instance used (!)");
177 assert(!rHolder
.isDeleted() && "Destructed instance used (!)");
178 if (this == &rHolder
|| *this == rHolder
)
181 if (nullptr != m_pItem
&& getPool().NeedsSurrogateSupport(m_pItem
->Which()))
182 getPool().unregisterPoolItemHolder(*this);
184 if (nullptr != m_pItem
)
185 implCleanupItemEntry(m_pItem
);
187 m_pPool
= rHolder
.m_pPool
;
188 m_pItem
= rHolder
.m_pItem
;
190 if (nullptr != m_pItem
)
191 m_pItem
= implCreateItemEntry(getPool(), m_pItem
, false);
193 if (nullptr != m_pItem
&& getPool().NeedsSurrogateSupport(m_pItem
->Which()))
194 getPool().registerPoolItemHolder(*this);
199 bool SfxPoolItemHolder::operator==(const SfxPoolItemHolder
&rHolder
) const
201 assert(!isDeleted() && "Destructed instance used (!)");
202 assert(!rHolder
.isDeleted() && "Destructed instance used (!)");
203 return m_pPool
== rHolder
.m_pPool
&& areSfxPoolItemPtrsEqual(m_pItem
, rHolder
.m_pItem
);
207 * Ctor for a SfxItemSet with exactly the Which Ranges, which are known to
208 * the supplied SfxItemPool.
210 * For Sfx programmers: an SfxItemSet constructed in this way cannot
211 * contain any Items with SlotIds as Which values.
213 SfxItemSet::SfxItemSet(SfxItemPool
& rPool
)
218 , m_nRegisteredSfxItemIter(0)
220 , m_aWhichRanges(rPool
.GetMergedIdRanges())
224 nAllocatedSfxItemSetCount
++;
225 nUsedSfxItemSetCount
++;
227 assert(m_aWhichRanges
.validRanges2());
230 SfxItemSet::SfxItemSet(SfxItemPool
& pool
, WhichRangesContainer wids
)
235 , m_nRegisteredSfxItemIter(0)
237 , m_aWhichRanges(std::move(wids
))
241 nAllocatedSfxItemSetCount
++;
242 nUsedSfxItemSetCount
++;
244 assert(m_aWhichRanges
.TotalCount() != 0);
245 assert(m_aWhichRanges
.validRanges2());
248 SfxItemSet::SfxItemSet( const SfxItemSet
& rASet
)
249 : m_pPool( rASet
.m_pPool
)
250 , m_pParent( rASet
.m_pParent
)
253 , m_nRegisteredSfxItemIter(0)
255 , m_aWhichRanges( rASet
.m_aWhichRanges
)
259 nAllocatedSfxItemSetCount
++;
260 nUsedSfxItemSetCount
++;
262 if (rASet
.GetRanges().empty())
265 for (const auto& rSource
: rASet
.m_aPoolItemMap
)
267 const SfxPoolItem
* pNew(implCreateItemEntry(*GetPool(), rSource
.second
, false));
268 m_aPoolItemMap
[rSource
.first
] = pNew
;
269 if (m_nRegister
!= rASet
.m_nRegister
)
270 checkAddPoolRegistration(pNew
);
273 assert(m_aWhichRanges
.validRanges2());
276 SfxItemSet::SfxItemSet(SfxItemSet
&& rASet
) noexcept
277 : m_pPool( rASet
.m_pPool
)
278 , m_pParent( rASet
.m_pParent
)
279 , m_nRegister( rASet
.m_nRegister
)
281 , m_nRegisteredSfxItemIter(0)
283 , m_aWhichRanges( std::move(rASet
.m_aWhichRanges
) )
284 , m_aPoolItemMap( std::move(rASet
.m_aPoolItemMap
) )
287 nAllocatedSfxItemSetCount
++;
288 nUsedSfxItemSetCount
++;
289 assert(0 == rASet
.m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet MOVE constructor with active SfxItemIters (!)");
291 // deregister if rASet is registered before ptrs vanish
292 if (0 != rASet
.m_nRegister
)
293 rASet
.GetPool()->unregisterItemSet(rASet
);
295 // register if new set needs that
296 if (0 != m_nRegister
)
297 GetPool()->registerItemSet(*this);
299 // taking over ownership
300 rASet
.m_pPool
= nullptr;
301 rASet
.m_pParent
= nullptr;
302 rASet
.m_nRegister
= 0;
303 rASet
.m_aWhichRanges
.reset();
305 assert(m_aWhichRanges
.validRanges2());
308 SfxItemSet::~SfxItemSet()
311 nAllocatedSfxItemSetCount
--;
312 addArrayUsage(Count(), TotalCount());
314 // cleanup items. No std::fill needed, we are done with this ItemSet.
315 // the callback is not set in destructor, so no worries about that
318 // for invariant-testing
319 m_aWhichRanges
.reset();
323 // Delete single Items or all Items (nWhich == 0)
324 sal_uInt16
SfxItemSet::ClearItem( sal_uInt16 nWhich
)
330 return ClearSingleItem_ForWhichID(nWhich
);
332 // clear all & reset to nullptr
333 return ClearAllItemsImpl();
336 sal_uInt16
SfxItemSet::ClearSingleItem_ForWhichID( sal_uInt16 nWhich
)
338 PoolItemMap::iterator
aHit(m_aPoolItemMap
.find(nWhich
));
340 if (aHit
== m_aPoolItemMap
.end())
344 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet ClearItem with active SfxItemIters (!)");
347 ClearSingleItem_PrepareRemove(aHit
->second
);
348 m_aPoolItemMap
.erase(aHit
);
353 void SfxItemSet::ClearSingleItem_PrepareRemove(const SfxPoolItem
* pItem
)
355 if (nullptr == pItem
)
358 // Notification-Callback
359 Changed(pItem
, nullptr);
361 // check register for remove
362 checkRemovePoolRegistration(pItem
);
364 // cleanup item & reset ptr
365 implCleanupItemEntry(pItem
);
368 void SfxItemSet::checkRemovePoolRegistration(const SfxPoolItem
* pItem
)
370 if (nullptr == pItem
)
374 if (IsInvalidItem(pItem
) || IsDisabledItem(pItem
))
375 // checks IsInvalidItem/IsDisabledItem
378 if (SfxItemPool::IsSlot(pItem
->Which()))
379 // no slots, these do not support NeedsSurrogateSupport
382 if(!GetPool()->NeedsSurrogateSupport(pItem
->Which()))
383 // not needed for this item, done
386 // there must be a registered one
387 assert(0 != m_nRegister
);
392 // deregister when no more Items that NeedsSurrogateSupport exist
393 if (0 == m_nRegister
)
394 GetPool()->unregisterItemSet(*this);
396 if (pItem
->isNameOrIndex())
397 GetPool()->unregisterNameOrIndex(*pItem
);
400 void SfxItemSet::checkAddPoolRegistration(const SfxPoolItem
* pItem
)
402 if (nullptr == pItem
)
406 if (IsInvalidItem(pItem
) || IsDisabledItem(pItem
))
407 // checks IsInvalidItem/IsDisabledItem
410 if (SfxItemPool::IsSlot(pItem
->Which()))
411 // no slots, these do not support NeedsSurrogateSupport
414 if(!GetPool()->NeedsSurrogateSupport(pItem
->Which()))
415 // not needed for this item, done
418 // register when first Item that NeedsSurrogateSupport exist
419 if (0 == m_nRegister
)
420 GetPool()->registerItemSet(*this);
422 if (pItem
->isNameOrIndex())
423 GetPool()->registerNameOrIndex(*pItem
);
429 sal_uInt16
SfxItemSet::ClearAllItemsImpl()
432 // no items set, done
436 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet ClearAllItems with active SfxItemIters (!)");
439 // loop & cleanup items
440 for (const auto& rCandidate
: m_aPoolItemMap
)
441 ClearSingleItem_PrepareRemove(rCandidate
.second
);
443 // remember count before resetting it, that is the retval
444 const sal_uInt16
nRetval(Count());
445 m_aPoolItemMap
.clear();
447 if (0 != m_nRegister
)
449 GetPool()->unregisterItemSet(*this);
456 void SfxItemSet::ClearInvalidItems()
459 // no items set, done
462 // loop, here using const_iterator due to need to set ptr in m_ppItems array
463 for (PoolItemMap::iterator
aCandidate(m_aPoolItemMap
.begin()); aCandidate
!= m_aPoolItemMap
.end();)
465 if (IsInvalidItem(aCandidate
->second
))
468 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet ClearInvalidItems with active SfxItemIters (!)");
470 aCandidate
= m_aPoolItemMap
.erase(aCandidate
);
477 SfxItemState
SfxItemSet::GetItemState_ForWhichID( SfxItemState eState
, sal_uInt16 nWhich
, bool bSrchInParent
, const SfxPoolItem
**ppItem
) const
479 PoolItemMap::const_iterator
aHit(m_aPoolItemMap
.find(nWhich
));
481 if (aHit
!= m_aPoolItemMap
.end())
483 if (IsInvalidItem(aHit
->second
))
484 // Different ones are present
485 return SfxItemState::INVALID
;
487 if (IsDisabledItem(aHit
->second
))
489 return SfxItemState::DISABLED
;
491 // if we have the Item, add it to output an hand back
492 if (nullptr != ppItem
)
493 *ppItem
= aHit
->second
;
496 return SfxItemState::SET
;
499 if (GetRanges().doesContainWhich(nWhich
))
502 eState
= SfxItemState::DEFAULT
;
506 if (bSrchInParent
&& nullptr != GetParent() && (SfxItemState::UNKNOWN
== eState
|| SfxItemState::DEFAULT
== eState
))
508 // nOffset was only valid for *local* SfxItemSet, need to continue with WhichID
509 // Use the *highest* SfxItemState as result
510 return GetParent()->GetItemState_ForWhichID( eState
, nWhich
, true, ppItem
);
516 bool SfxItemSet::HasItem(sal_uInt16 nWhich
, const SfxPoolItem
** ppItem
) const
518 const bool bRet(SfxItemState::SET
== GetItemState_ForWhichID(SfxItemState::UNKNOWN
, nWhich
, true, ppItem
));
520 // we need to reset ppItem when it was *not* set by GetItemState_ForWhichID
521 // since many usages of that return parameter re-use it, so it might still
522 // be set to 'something'
523 if (!bRet
&& nullptr != ppItem
)
531 void SfxItemSet::CollectHasItems(std::vector
<sal_uInt16
>& rItemWhichs
) const
533 for(auto const & rPair
: m_aWhichRanges
)
535 const sal_uInt16 nBeg
= rPair
.first
;
536 const sal_uInt16 nEnd
= rPair
.second
;
537 for( sal_uInt16 nWhich
= nBeg
; nWhich
<= nEnd
; ++nWhich
)
539 bool bHasItem
= false;
540 auto aHit(m_aPoolItemMap
.find(nWhich
));
541 if (aHit
!= m_aPoolItemMap
.end())
543 bHasItem
= !IsInvalidItem(aHit
->second
) && !IsDisabledItem(aHit
->second
);
548 bHasItem
= SfxItemState::SET
== m_pParent
->GetItemState_ForWhichID( SfxItemState::DEFAULT
, nWhich
, true, nullptr);
551 rItemWhichs
.push_back( nWhich
);
556 const SfxPoolItem
* SfxItemSet::PutImplAsTargetWhich(const SfxPoolItem
& rItem
, sal_uInt16 nTargetWhich
, bool bPassingOwnership
)
558 if (0 == nTargetWhich
|| nTargetWhich
== rItem
.Which())
559 // nTargetWhich not different or not given, use default
560 return PutImpl(rItem
, bPassingOwnership
);
562 if (bPassingOwnership
&& 0 == rItem
.GetRefCount())
564 // we *can* use rItem when it's not pooled AKA has no RefCount
565 const_cast<SfxPoolItem
&>(rItem
).SetWhich(nTargetWhich
);
566 return PutImpl(rItem
, true);
569 // else we have to create a clone, set WhichID at it and
570 // delete rItem when bPassingOwnership was intended
571 SfxPoolItem
* pClone(rItem
.Clone(GetPool()));
572 pClone
->SetWhich(nTargetWhich
);
573 if (bPassingOwnership
)
575 return PutImpl(*pClone
, true);
578 const SfxPoolItem
* SfxItemSet::PutImpl(const SfxPoolItem
& rItem
, bool bPassingOwnership
)
580 if (IsDisabledItem(&rItem
))
582 // no action needed: IsDisabledItem
583 if (bPassingOwnership
)
588 const sal_uInt16
nWhich(rItem
.Which());
590 if (!GetRanges().doesContainWhich(nWhich
))
592 // no action needed: not in WhichRange
593 if (bPassingOwnership
)
598 const SfxPoolItem
* pEntry(nullptr);
599 PoolItemMap::iterator
aHit(m_aPoolItemMap
.find(nWhich
));
601 if (aHit
!= m_aPoolItemMap
.end())
603 // compare items, evtl. containing content compare
604 pEntry
= aHit
->second
;
606 if (SfxPoolItem::areSame(*pEntry
, rItem
))
608 // no action needed: identical item already in place
609 if (bPassingOwnership
)
616 const SfxPoolItem
* pNew(implCreateItemEntry(*GetPool(), &rItem
, bPassingOwnership
));
618 // Notification-Callback
619 Changed(pEntry
, pNew
);
621 // check register for add/remove. add first so that unregister/register
622 // is avoided when an Item is replaced (increase, decrease, do not reach 0)
623 checkAddPoolRegistration(pNew
);
624 checkRemovePoolRegistration(pEntry
);
626 // cleanup old entry & set entry at m_ppItems array
627 implCleanupItemEntry(pEntry
);
634 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet PutImpl with active SfxItemIters (!)");
636 m_aPoolItemMap
[nWhich
] = pNew
;
642 bool SfxItemSet::Put(const SfxItemSet
& rSource
, bool bInvalidAsDefault
)
644 if (0 == rSource
.Count())
645 // no items in source, done
650 for (PoolItemMap::const_iterator
aCandidate(rSource
.m_aPoolItemMap
.begin()); aCandidate
!= rSource
.m_aPoolItemMap
.end(); aCandidate
++)
652 if (IsInvalidItem(aCandidate
->second
))
654 if (bInvalidAsDefault
)
656 bRetval
|= 0 != ClearSingleItem_ForWhichID(aCandidate
->first
);
660 DisableOrInvalidateItem_ForWhichID(false, aCandidate
->first
);
665 bRetval
|= nullptr != PutImpl(*aCandidate
->second
, false);
673 * This method takes the Items from the 'rSet' and adds to '*this'.
674 * Which ranges in '*this' that are non-existent in 'rSet' will not
675 * be altered. The Which range of '*this' is also not changed.
677 * Items set in 'rSet' are also set in '*this'.
678 * Default (0 pointer) and Invalid (-1 pointer) Items are processed
679 * according to their parameter 'eDontCareAs' and 'eDefaultAs':
681 * SfxItemState::SET: Hard set to the default of the Pool
682 * SfxItemState::DEFAULT: Deleted (0 pointer)
683 * SfxItemState::INVALID: Invalid (-1 pointer)
685 * NB: All other values for 'eDontCareAs' and 'eDefaultAs' are invalid
687 void SfxItemSet::PutExtended
689 const SfxItemSet
& rSource
, // Source of the Items to be put
690 SfxItemState eDontCareAs
, // What will happen to the DontCare Items
691 SfxItemState eDefaultAs
// What will happen to the Default Items
694 // don't "optimize" with "if( rSource.Count()" because of dontcare + defaults
695 for (const WhichPair
& rPair
: rSource
.GetRanges())
697 for (sal_uInt16 nWhich
= rPair
.first
; nWhich
<= rPair
.second
; nWhich
++)
699 PoolItemMap::const_iterator
aHit(rSource
.m_aPoolItemMap
.find(nWhich
));
701 if (aHit
!= rSource
.m_aPoolItemMap
.end())
703 if (IsInvalidItem(aHit
->second
))
708 case SfxItemState::SET
:
709 PutImpl(rSource
.GetPool()->GetUserOrPoolDefaultItem(nWhich
), false);
712 case SfxItemState::DEFAULT
:
713 ClearSingleItem_ForWhichID(nWhich
);
716 case SfxItemState::INVALID
:
717 DisableOrInvalidateItem_ForWhichID(false, nWhich
);
721 assert(!"invalid Argument for eDontCareAs");
727 PutImpl(*aHit
->second
, false);
735 case SfxItemState::SET
:
736 PutImpl(rSource
.GetPool()->GetUserOrPoolDefaultItem(nWhich
), false);
739 case SfxItemState::DEFAULT
:
740 ClearSingleItem_ForWhichID(nWhich
);
743 case SfxItemState::INVALID
:
744 DisableOrInvalidateItem_ForWhichID(false, nWhich
);
748 assert(!"invalid Argument for eDefaultAs");
756 * Expands the ranges of settable items by 'nFrom' to 'nTo'. Keeps state of
757 * items which are new ranges too.
759 void SfxItemSet::MergeRange( sal_uInt16 nFrom
, sal_uInt16 nTo
)
761 // check if all from new range are already included. This will
762 // use the cache in WhichRangesContainer since we check linearly.
763 // Start with assuming all are included, but only if not empty.
764 // If empty all included is wrong (and GetRanges().MergeRange
765 // will do the right thing/shortcut)
766 bool bAllIncluded(!GetRanges().empty());
768 for (sal_uInt16
a(nFrom
); bAllIncluded
&& a
<= nTo
; a
++)
769 if (!GetRanges().doesContainWhich(a
))
770 bAllIncluded
= false;
772 // if yes, we are done
776 // need to create new WhichRanges
777 auto aNewRanges
= m_aWhichRanges
.MergeRange(nFrom
, nTo
);
778 RecreateRanges_Impl(aNewRanges
);
779 m_aWhichRanges
= std::move(aNewRanges
);
783 * Modifies the ranges of settable items. Keeps state of items which
784 * are new ranges too.
786 void SfxItemSet::SetRanges( const WhichRangesContainer
& aNewRanges
)
789 if (GetRanges() == aNewRanges
)
792 assert(aNewRanges
.validRanges2());
793 RecreateRanges_Impl(aNewRanges
);
794 m_aWhichRanges
= aNewRanges
;
797 void SfxItemSet::SetRanges( WhichRangesContainer
&& aNewRanges
)
800 if (GetRanges() == aNewRanges
)
803 assert(aNewRanges
.validRanges2());
804 RecreateRanges_Impl(aNewRanges
);
805 m_aWhichRanges
= std::move(aNewRanges
);
808 void SfxItemSet::RecreateRanges_Impl(const WhichRangesContainer
& rNewRanges
)
811 // no existing items, done
814 // check if existing items are in the new ItemRanges.
815 // if they are not, remove the item
816 for (PoolItemMap::iterator
aCandidate(m_aPoolItemMap
.begin()); aCandidate
!= m_aPoolItemMap
.end();)
818 if (!rNewRanges
.doesContainWhich(aCandidate
->first
))
821 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet RecreateRanges with active SfxItemIters (!)");
823 ClearSingleItem_PrepareRemove(aCandidate
->second
);
824 aCandidate
= m_aPoolItemMap
.erase(aCandidate
);
832 * The SfxItemSet takes over exactly those SfxPoolItems that are
833 * set in rSet and are in their own Which range. All others are removed.
834 * The SfxItemPool is retained, such that SfxPoolItems that have been
835 * taken over, are moved from the rSet's SfxItemPool to the SfxItemPool
838 * SfxPoolItems in rSet, for which holds 'IsInvalidItem() == true' are
839 * taken over as invalid items.
842 * SfxPoolItems have been taken over
845 * No SfxPoolItems have been taken over, because
846 * e.g. the Which ranges of SfxItemSets are not intersecting
847 * or the intersection does not contain SfxPoolItems that are
852 const SfxItemSet
& rSet
, /* The SfxItemSet, whose SfxPoolItems are
853 to been taken over */
855 bool bDeep
/* true (default)
857 The SfxPoolItems from the parents that may
858 be present in rSet, are also taken over into
862 The SfxPoolItems from the parents of
863 rSet are not taken into account */
870 return Put(rSet
, false);
873 SfxWhichIter
aIter1(*this);
874 SfxWhichIter
aIter2(rSet
);
875 sal_uInt16 nWhich1
= aIter1
.FirstWhich();
876 sal_uInt16 nWhich2
= aIter2
.FirstWhich();
879 if (!nWhich1
|| !nWhich2
)
881 if (nWhich1
> nWhich2
)
883 nWhich2
= aIter2
.NextWhich();
886 if (nWhich1
< nWhich2
)
888 nWhich1
= aIter1
.NextWhich();
891 const SfxPoolItem
* pItem
;
892 if( SfxItemState::SET
== aIter2
.GetItemState( true, &pItem
) )
893 bRet
|= nullptr != Put( *pItem
);
894 nWhich1
= aIter1
.NextWhich();
895 nWhich2
= aIter2
.NextWhich();
901 const SfxPoolItem
* SfxItemSet::GetItem(sal_uInt16 nId
, bool bSearchInParent
) const
903 // evtl. Convert from SlotID to WhichId
904 const sal_uInt16
nWhich(GetPool()->GetWhichIDFromSlotID(nId
));
906 // Is the Item set or 'bDeep == true' available?
907 const SfxPoolItem
*pItem(nullptr);
908 const SfxItemState
eState(GetItemState_ForWhichID(SfxItemState::UNKNOWN
, nWhich
, bSearchInParent
, &pItem
));
910 if (bSearchInParent
&& SfxItemState::DEFAULT
== eState
&& SfxItemPool::IsWhich(nWhich
))
912 pItem
= &GetPool()->GetUserOrPoolDefaultItem(nWhich
);
918 const SfxPoolItem
& SfxItemSet::Get( sal_uInt16 nWhich
, bool bSrchInParent
) const
920 PoolItemMap::const_iterator
aHit(m_aPoolItemMap
.find(nWhich
));
922 if (aHit
!= m_aPoolItemMap
.end())
924 if (IsInvalidItem(aHit
->second
))
926 return GetPool()->GetUserOrPoolDefaultItem(nWhich
);
929 if (IsDisabledItem(aHit
->second
))
930 SAL_INFO("svl.items", "SFX_WARNING: Getting disabled Item");
932 return *aHit
->second
;
935 if (bSrchInParent
&& nullptr != GetParent())
937 return GetParent()->Get(nWhich
, bSrchInParent
);
940 // Get the Default from the Pool and return
942 return GetPool()->GetUserOrPoolDefaultItem(nWhich
);
946 * Notification callback
948 void SfxItemSet::Changed(const SfxPoolItem
*, const SfxPoolItem
*) const
953 * Only retain the Items that are also present in rSet
954 * (nevermind their value).
956 void SfxItemSet::Intersect( const SfxItemSet
& rSet
)
958 // Delete all Items not contained in rSet
959 assert(m_pPool
&& "Not implemented without Pool");
961 if (!Count() || this == &rSet
)
962 // none set -> none to delete
963 // same ItemSet? -> no Items not contained
968 // no Items contained in rSet -> Delete everything
973 // CAUTION: In the former impl, the
974 // - version for different ranges checked for SfxItemState::UNKNOWN
975 // in rSet -> this means that the WhichID is *not* defined in
976 // the ranges of rSet *at all* > definitely an *error*
977 // - version for same ranges checked for
978 // nullptr != local && nullptr == rSet.
979 // All together I think also using the text
980 // "Delete all Items not contained in rSet" leads to
981 // locally delete all Items that are *not* set in rSet
982 // -> != SfxItemState::SET
984 for (PoolItemMap::iterator
aCandidate(m_aPoolItemMap
.begin()); aCandidate
!= m_aPoolItemMap
.end();)
986 if (SfxItemState::SET
!= rSet
.GetItemState_ForWhichID(SfxItemState::UNKNOWN
, aCandidate
->first
, false, nullptr))
989 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet Intersect with active SfxItemIters (!)");
991 ClearSingleItem_PrepareRemove(aCandidate
->second
);
992 aCandidate
= m_aPoolItemMap
.erase(aCandidate
);
999 void SfxItemSet::Differentiate(const SfxItemSet
& rSet
)
1001 assert(m_pPool
&& "Not implemented without Pool");
1003 // Delete all Items contained in rSet
1004 if (!Count() || !rSet
.Count())
1010 // same ItemSet, all Items are contained -> Delete everything
1011 ClearAllItemsImpl();
1015 // CAUTION: In the former impl, the
1016 // - version for different ranges checked for SfxItemState::SET
1018 // - version for same ranges checked for
1019 // nullptr != local && nullptr != rSet.
1020 // All together I think also using the text
1021 // "Delete all Items contained in rSet" leads to
1022 // locally delete all Items that *are *not* set in rSet
1023 // -> ==SfxItemState::SET
1025 for (PoolItemMap::iterator
aCandidate(m_aPoolItemMap
.begin()); aCandidate
!= m_aPoolItemMap
.end();)
1027 if (SfxItemState::SET
== rSet
.GetItemState_ForWhichID(SfxItemState::UNKNOWN
, aCandidate
->first
, false, nullptr))
1030 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet Differentiate with active SfxItemIters (!)");
1032 ClearSingleItem_PrepareRemove(aCandidate
->second
);
1033 aCandidate
= m_aPoolItemMap
.erase(aCandidate
);
1041 * Decision table for MergeValue(s)
1044 * 1. If the Which value in the 1st set is "unknown", there's never any action
1045 * 2. If the Which value in the 2nd set is "unknown", it's made the "default"
1046 * 3. For comparisons the values of the "default" Items are take into account
1048 * 1st Item 2nd Item Values bIgnoreDefs Remove Assign Add
1050 * set set == sal_False - - -
1051 * default set == sal_False - - -
1052 * dontcare set == sal_False - - -
1053 * unknown set == sal_False - - -
1054 * set default == sal_False - - -
1055 * default default == sal_False - - -
1056 * dontcare default == sal_False - - -
1057 * unknown default == sal_False - - -
1058 * set dontcare == sal_False 1st Item -1 -
1059 * default dontcare == sal_False - -1 -
1060 * dontcare dontcare == sal_False - - -
1061 * unknown dontcare == sal_False - - -
1062 * set unknown == sal_False 1st Item -1 -
1063 * default unknown == sal_False - - -
1064 * dontcare unknown == sal_False - - -
1065 * unknown unknown == sal_False - - -
1067 * set set != sal_False 1st Item -1 -
1068 * default set != sal_False - -1 -
1069 * dontcare set != sal_False - - -
1070 * unknown set != sal_False - - -
1071 * set default != sal_False 1st Item -1 -
1072 * default default != sal_False - - -
1073 * dontcare default != sal_False - - -
1074 * unknown default != sal_False - - -
1075 * set dontcare != sal_False 1st Item -1 -
1076 * default dontcare != sal_False - -1 -
1077 * dontcare dontcare != sal_False - - -
1078 * unknown dontcare != sal_False - - -
1079 * set unknown != sal_False 1st Item -1 -
1080 * default unknown != sal_False - - -
1081 * dontcare unknown != sal_False - - -
1082 * unknown unknown != sal_False - - -
1084 * set set == sal_True - - -
1085 * default set == sal_True - 2nd Item 2nd Item
1086 * dontcare set == sal_True - - -
1087 * unknown set == sal_True - - -
1088 * set default == sal_True - - -
1089 * default default == sal_True - - -
1090 * dontcare default == sal_True - - -
1091 * unknown default == sal_True - - -
1092 * set dontcare == sal_True - - -
1093 * default dontcare == sal_True - -1 -
1094 * dontcare dontcare == sal_True - - -
1095 * unknown dontcare == sal_True - - -
1096 * set unknown == sal_True - - -
1097 * default unknown == sal_True - - -
1098 * dontcare unknown == sal_True - - -
1099 * unknown unknown == sal_True - - -
1101 * set set != sal_True 1st Item -1 -
1102 * default set != sal_True - 2nd Item 2nd Item
1103 * dontcare set != sal_True - - -
1104 * unknown set != sal_True - - -
1105 * set default != sal_True - - -
1106 * default default != sal_True - - -
1107 * dontcare default != sal_True - - -
1108 * unknown default != sal_True - - -
1109 * set dontcare != sal_True 1st Item -1 -
1110 * default dontcare != sal_True - -1 -
1111 * dontcare dontcare != sal_True - - -
1112 * unknown dontcare != sal_True - - -
1113 * set unknown != sal_True - - -
1114 * default unknown != sal_True - - -
1115 * dontcare unknown != sal_True - - -
1116 * unknown unknown != sal_True - - -
1119 void SfxItemSet::MergeItem_Impl(sal_uInt16 nWhich
, const SfxPoolItem
*pFnd2
, bool bIgnoreDefaults
)
1121 // callers need to ensure that nWhich is in local range
1122 assert(GetRanges().doesContainWhich(nWhich
) && "ITEM: call to MergeItem_Impl with WhichID outside local range (!)");
1123 const PoolItemMap::iterator
aHit(m_aPoolItemMap
.find(nWhich
));
1125 if (aHit
== m_aPoolItemMap
.end())
1127 // 1st Item nWhich is not set (Default)
1128 const SfxPoolItem
* pNew(nullptr);
1130 if (IsInvalidItem(pFnd2
))
1131 // Decision table: default, dontcare, doesn't matter, doesn't matter
1132 pNew
= INVALID_POOL_ITEM
;
1134 else if (pFnd2
&& !bIgnoreDefaults
&& GetPool()->GetUserOrPoolDefaultItem(nWhich
) != *pFnd2
)
1135 // Decision table: default, set, !=, sal_False
1136 pNew
= INVALID_POOL_ITEM
;
1138 else if (pFnd2
&& bIgnoreDefaults
)
1139 // Decision table: default, set, doesn't matter, sal_True
1140 pNew
= implCreateItemEntry(*GetPool(), pFnd2
, false);
1145 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet MergeItem with active SfxItemIters (!)");
1147 m_aPoolItemMap
[nWhich
] = pNew
;
1148 checkAddPoolRegistration(pNew
);
1154 const SfxPoolItem
* pFnd1(aHit
->second
);
1156 if (IsInvalidItem(pFnd1
))
1161 // 1st Item is set, check for change
1162 bool bDoChange(false);
1164 if (nullptr == pFnd2
)
1166 // 2nd Item is not set (Default)
1167 if (!bIgnoreDefaults
&& *pFnd1
!= GetPool()->GetUserOrPoolDefaultItem(nWhich
))
1169 // Decision table: set, default, !=, sal_False
1173 else if (IsInvalidItem(pFnd2
))
1175 // 2nd Item is invalid (dontcare)
1176 if (!bIgnoreDefaults
|| *pFnd1
!= GetPool()->GetUserOrPoolDefaultItem(nWhich
))
1178 // Decision table: set, dontcare, doesn't matter, sal_False
1179 // or: set, dontcare, !=, sal_True
1183 else if (*pFnd1
!= *pFnd2
)
1186 // Decision table: set, set, !=, doesn't matter
1192 ClearSingleItem_PrepareRemove(pFnd1
);
1193 aHit
->second
= INVALID_POOL_ITEM
;
1197 void SfxItemSet::MergeValues( const SfxItemSet
& rSet
)
1199 // WARNING! When making changes/fixing bugs, always update the table above!!
1200 assert( GetPool() == rSet
.GetPool() && "MergeValues with different Pools" );
1202 // CAUTION: Old version did *different* things when the WhichRanges
1203 // were the same (true) or different (false) (which is an error/
1204 // false optimization):
1205 // true: MergeItem_Impl was directly fed with SfxItem*'s
1206 // for entry @this & @rSet
1207 // false: Looped over rSet WhichID's, fetched defaults from pool,
1208 // fed all that to SfxItemSet::MergeValue which then
1209 // evtl. could not find that WhichID in local WhichRanges
1210 // Better to loop over local WhichRanges (these get changed) and look
1211 // for Item with same WhichID in rSet, this is done now.
1212 for (auto const & rRange
: GetRanges())
1214 for (sal_uInt16
nWhich(rRange
.first
); nWhich
<= rRange
.second
; nWhich
++)
1216 PoolItemMap::const_iterator
aHit(rSet
.m_aPoolItemMap
.find(nWhich
));
1217 const SfxPoolItem
* src(aHit
== rSet
.m_aPoolItemMap
.end() ? nullptr : aHit
->second
);
1218 MergeItem_Impl(nWhich
, src
, false/*bIgnoreDefaults*/);
1223 void SfxItemSet::MergeValue(const SfxPoolItem
& rAttr
)
1225 if (IsDisabledItem(&rAttr
))
1226 // DisabledItem, nothing to do
1229 if (GetRanges().doesContainWhich(rAttr
.Which()))
1231 MergeItem_Impl(rAttr
.Which(), &rAttr
, /*bIgnoreDefaults*/true);
1235 void SfxItemSet::DisableOrInvalidateItem_ForWhichID(bool bDisable
, sal_uInt16 nWhich
)
1237 PoolItemMap::iterator
aHit(m_aPoolItemMap
.find(nWhich
));
1239 if (aHit
!= m_aPoolItemMap
.end())
1241 if (bDisable
&& IsDisabledItem(aHit
->second
))
1242 // already disabled item, done
1245 if (!bDisable
&& IsInvalidItem(aHit
->second
))
1246 // already invalid item, done
1250 ClearSingleItem_PrepareRemove(aHit
->second
);
1251 aHit
->second
= bDisable
? DISABLED_POOL_ITEM
: INVALID_POOL_ITEM
;
1253 else if (GetRanges().doesContainWhich(nWhich
))
1256 assert(0 == m_nRegisteredSfxItemIter
&& "ITEM: SfxItemSet DisableOrInvalidateItem with active SfxItemIters (!)");
1259 m_aPoolItemMap
[nWhich
] = bDisable
? DISABLED_POOL_ITEM
: INVALID_POOL_ITEM
;
1262 bool SfxItemSet::operator==(const SfxItemSet
&rCmp
) const
1264 return Equals( rCmp
, true);
1267 bool SfxItemSet::Equals(const SfxItemSet
&rCmp
, bool bComparePool
) const
1269 // check if same incarnation
1273 // check parents (if requested, also bComparePool)
1274 if (bComparePool
&& GetParent() != rCmp
.GetParent())
1277 // check pools (if requested)
1278 if (bComparePool
&& GetPool() != rCmp
.GetPool())
1281 // check count of set items
1282 if (Count() != rCmp
.Count())
1285 // both have no items, done
1289 for (PoolItemMap::const_iterator
aCandidate(m_aPoolItemMap
.begin()); aCandidate
!= m_aPoolItemMap
.end(); aCandidate
++)
1291 const SfxPoolItem
*pItem1(nullptr);
1292 const SfxPoolItem
*pItem2(nullptr);
1293 const sal_uInt16
nWhich(aCandidate
->first
);
1294 const SfxItemState
aStateA(GetItemState_ForWhichID(SfxItemState::UNKNOWN
, nWhich
, false, &pItem1
));
1295 const SfxItemState
aStateB(rCmp
.GetItemState_ForWhichID(SfxItemState::UNKNOWN
, nWhich
, false, &pItem2
));
1297 if (aStateA
!= aStateB
)
1300 // only compare items if SfxItemState::SET, else the item ptrs are not set
1301 if (SfxItemState::SET
== aStateA
&& !SfxPoolItem::areSame(pItem1
, pItem2
))
1308 std::unique_ptr
<SfxItemSet
> SfxItemSet::Clone(bool bItems
, SfxItemPool
*pToPool
) const
1310 if (pToPool
&& pToPool
!= GetPool())
1312 std::unique_ptr
<SfxItemSet
> pNewSet(new SfxItemSet(*pToPool
, GetRanges()));
1315 SfxWhichIter
aIter(*pNewSet
);
1316 sal_uInt16 nWhich
= aIter
.FirstWhich();
1319 const SfxPoolItem
* pItem
;
1320 if ( SfxItemState::SET
== GetItemState_ForWhichID(SfxItemState::UNKNOWN
, nWhich
, false, &pItem
) )
1321 pNewSet
->Put( *pItem
);
1322 nWhich
= aIter
.NextWhich();
1328 return std::unique_ptr
<SfxItemSet
>(bItems
1329 ? new SfxItemSet(*this)
1330 : new SfxItemSet(*GetPool(), GetRanges()));
1333 SfxItemSet
SfxItemSet::CloneAsValue(bool bItems
, SfxItemPool
*pToPool
) const
1335 // if you are trying to clone, then the thing you are cloning is polymorphic, which means
1336 // it cannot be cloned as a value
1337 assert((typeid(*this) == typeid(SfxItemSet
)) && "cannot call this on a subclass of SfxItemSet");
1339 if (pToPool
&& pToPool
!= GetPool())
1341 SfxItemSet
aNewSet(*pToPool
, GetRanges());
1344 SfxWhichIter
aIter(aNewSet
);
1345 sal_uInt16 nWhich
= aIter
.FirstWhich();
1348 const SfxPoolItem
* pItem
;
1349 if ( SfxItemState::SET
== GetItemState_ForWhichID(SfxItemState::UNKNOWN
, nWhich
, false, &pItem
) )
1350 aNewSet
.Put( *pItem
);
1351 nWhich
= aIter
.NextWhich();
1359 : SfxItemSet(*GetPool(), GetRanges());
1362 void SfxItemSet::dumpAsXml(xmlTextWriterPtr pWriter
) const
1364 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SfxItemSet"));
1365 SfxItemIter
aIter(*this);
1366 for (const SfxPoolItem
* pItem
= aIter
.GetCurItem(); pItem
; pItem
= aIter
.NextItem())
1368 if (IsInvalidItem(pItem
))
1370 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("invalid"));
1371 (void)xmlTextWriterEndElement(pWriter
);
1375 pItem
->dumpAsXml(pWriter
);
1378 (void)xmlTextWriterEndElement(pWriter
);
1382 // ----------------------------------------------- class SfxAllItemSet
1384 SfxAllItemSet::SfxAllItemSet( SfxItemPool
&rPool
)
1389 SfxAllItemSet::SfxAllItemSet(const SfxItemSet
&rCopy
)
1395 * Explicitly define this ctor to avoid auto-generation by the compiler.
1396 * The compiler does not take the ctor with the 'const SfxItemSet&'!
1398 SfxAllItemSet::SfxAllItemSet(const SfxAllItemSet
&rCopy
)
1404 * Putting with automatic extension of the WhichId with the ID of the Item.
1406 const SfxPoolItem
* SfxAllItemSet::PutImpl( const SfxPoolItem
& rItem
, bool bPassingOwnership
)
1408 MergeRange(rItem
.Which(), rItem
.Which());
1409 return SfxItemSet::PutImpl(rItem
, bPassingOwnership
);
1412 std::unique_ptr
<SfxItemSet
> SfxAllItemSet::Clone(bool bItems
, SfxItemPool
*pToPool
) const
1414 if (pToPool
&& pToPool
!= GetPool())
1416 std::unique_ptr
<SfxAllItemSet
> pNewSet(new SfxAllItemSet( *pToPool
));
1418 pNewSet
->Set( *this );
1422 return std::unique_ptr
<SfxItemSet
>(bItems
? new SfxAllItemSet(*this) : new SfxAllItemSet(*GetPool()));
1426 void WhichRangesContainer::CountRanges() const
1429 for (const auto& rPair
: *this)
1430 m_TotalCount
+= svl::detail::rangeSize(rPair
.first
, rPair
.second
);
1433 WhichRangesContainer::WhichRangesContainer( const WhichPair
* wids
, sal_Int32 nSize
)
1437 , m_aLastWhichPairOffset(INVALID_WHICHPAIR_OFFSET
)
1438 , m_aLastWhichPairFirst(0)
1439 , m_aLastWhichPairSecond(0)
1440 , m_bOwnRanges(true)
1442 auto p
= new WhichPair
[nSize
];
1443 for (int i
=0; i
<nSize
; ++i
)
1449 WhichRangesContainer::WhichRangesContainer(sal_uInt16 nWhichStart
, sal_uInt16 nWhichEnd
)
1453 , m_aLastWhichPairOffset(INVALID_WHICHPAIR_OFFSET
)
1454 , m_aLastWhichPairFirst(0)
1455 , m_aLastWhichPairSecond(0)
1456 , m_bOwnRanges(true)
1458 auto p
= new WhichPair
[1];
1459 p
[0] = { nWhichStart
, nWhichEnd
};
1464 WhichRangesContainer::WhichRangesContainer(WhichRangesContainer
&& other
)
1466 std::swap(m_pairs
, other
.m_pairs
);
1467 std::swap(m_size
, other
.m_size
);
1468 std::swap(m_TotalCount
, other
.m_TotalCount
);
1469 std::swap(m_aLastWhichPairOffset
, other
.m_aLastWhichPairOffset
);
1470 std::swap(m_aLastWhichPairFirst
, other
.m_aLastWhichPairFirst
);
1471 std::swap(m_aLastWhichPairSecond
, other
.m_aLastWhichPairSecond
);
1472 std::swap(m_bOwnRanges
, other
.m_bOwnRanges
);
1475 WhichRangesContainer
& WhichRangesContainer::operator=(WhichRangesContainer
&& other
)
1477 std::swap(m_pairs
, other
.m_pairs
);
1478 std::swap(m_size
, other
.m_size
);
1479 std::swap(m_TotalCount
, other
.m_TotalCount
);
1480 std::swap(m_aLastWhichPairOffset
, other
.m_aLastWhichPairOffset
);
1481 std::swap(m_aLastWhichPairFirst
, other
.m_aLastWhichPairFirst
);
1482 std::swap(m_aLastWhichPairSecond
, other
.m_aLastWhichPairSecond
);
1483 std::swap(m_bOwnRanges
, other
.m_bOwnRanges
);
1487 WhichRangesContainer
& WhichRangesContainer::operator=(WhichRangesContainer
const & other
)
1491 m_size
= other
.m_size
;
1492 m_TotalCount
= other
.m_TotalCount
;
1493 m_aLastWhichPairOffset
= other
.m_aLastWhichPairOffset
;
1494 m_aLastWhichPairFirst
= other
.m_aLastWhichPairFirst
;
1495 m_aLastWhichPairSecond
= other
.m_aLastWhichPairSecond
;
1496 m_bOwnRanges
= other
.m_bOwnRanges
;
1500 auto p
= new WhichPair
[m_size
];
1501 for (int i
=0; i
<m_size
; ++i
)
1502 p
[i
] = other
.m_pairs
[i
];
1506 m_pairs
= other
.m_pairs
;
1511 WhichRangesContainer::~WhichRangesContainer()
1516 bool WhichRangesContainer::operator==(WhichRangesContainer
const & other
) const
1518 if (m_size
!= other
.m_size
)
1520 if (m_TotalCount
!= other
.m_TotalCount
)
1522 if (m_pairs
== other
.m_pairs
)
1524 return std::equal(m_pairs
, m_pairs
+ m_size
, other
.m_pairs
, other
.m_pairs
+ m_size
);
1528 void WhichRangesContainer::reset()
1533 m_bOwnRanges
= false;
1538 m_aLastWhichPairOffset
= INVALID_WHICHPAIR_OFFSET
;
1539 m_aLastWhichPairFirst
= 0;
1540 m_aLastWhichPairSecond
= 0;
1544 static size_t g_nHit(0);
1545 static size_t g_nMiss(1);
1546 static bool g_bShowWhichRangesHitRate(getenv("SVL_SHOW_WHICHRANGES_HITRATE"));
1547 static void isHit() { g_nHit
++; }
1548 static void isMiss()
1551 const double fHitRate(double(g_nHit
) /double(g_nMiss
));
1552 if (0 == g_nMiss
% 1000 && g_bShowWhichRangesHitRate
)
1553 SAL_WARN("svl", "ITEM: hits: " << g_nHit
<< " misses: " << g_nMiss
<< " hits/misses(rate): " << fHitRate
);
1557 bool WhichRangesContainer::doesContainWhich(sal_uInt16 nWhich
) const
1559 // special case for single entry - happens often e.g. UI stuff
1562 if( m_pairs
->first
<= nWhich
&& nWhich
<= m_pairs
->second
)
1565 // we have only one WhichPair entry and it's not contained -> failed
1572 // check if nWhich is inside last successfully used WhichPair
1573 if (INVALID_WHICHPAIR_OFFSET
!= m_aLastWhichPairOffset
1574 && m_aLastWhichPairFirst
<= nWhich
1575 && nWhich
<= m_aLastWhichPairSecond
)
1580 // we can re-use the last found WhichPair
1588 // we have to find the correct WhichPair, iterate linear. This
1589 // also directly updates the buffered m_aLastWhichPair* values
1590 m_aLastWhichPairOffset
= 0;
1592 for (const WhichPair
& rPair
: *this)
1594 // Within this range?
1595 if( rPair
.first
<= nWhich
&& nWhich
<= rPair
.second
)
1597 // found, remember parameters for buffered hits
1598 m_aLastWhichPairFirst
= rPair
.first
;
1599 m_aLastWhichPairSecond
= rPair
.second
;
1605 m_aLastWhichPairOffset
+= rPair
.second
- rPair
.first
+ 1;
1608 // *need* to reset: if 1st WhichPair only one entry it could be 1
1609 // what could wrongly trigger re-use above for next search
1610 m_aLastWhichPairOffset
= INVALID_WHICHPAIR_OFFSET
;
1615 // Adds a range to which ranges, keeping the ranges in valid state (sorted, non-overlapping)
1616 WhichRangesContainer
WhichRangesContainer::MergeRange(sal_uInt16 nFrom
,
1617 sal_uInt16 nTo
) const
1619 assert(svl::detail::validRange(nFrom
, nTo
));
1622 return WhichRangesContainer(nFrom
, nTo
);
1625 m_aLastWhichPairOffset
= INVALID_WHICHPAIR_OFFSET
;
1627 // create vector of ranges (sal_uInt16 pairs of lower and upper bound)
1628 const size_t nOldCount
= size();
1629 // Allocate one item more than we already have.
1630 // In the worst case scenario we waste a little bit
1631 // of memory, but we avoid another allocation, which is more important.
1632 std::unique_ptr
<WhichPair
[]> aRangesTable(new WhichPair
[nOldCount
+1]);
1633 int aRangesTableSize
= 0;
1634 bool bAdded
= false;
1635 for (const auto& rPair
: *this)
1637 if (!bAdded
&& rPair
.first
>= nFrom
)
1638 { // insert new range, keep ranges sorted
1639 aRangesTable
[aRangesTableSize
++] = { nFrom
, nTo
};
1642 // insert current range
1643 aRangesTable
[aRangesTableSize
++] = rPair
;
1646 aRangesTable
[aRangesTableSize
++] = { nFrom
, nTo
};
1648 // true if ranges overlap or adjoin, false if ranges are separate
1649 auto needMerge
= [](WhichPair lhs
, WhichPair rhs
) {
1650 return (lhs
.first
- 1) <= rhs
.second
&& (rhs
.first
- 1) <= lhs
.second
;
1653 auto it
= aRangesTable
.get();
1654 auto endIt
= aRangesTable
.get() + aRangesTableSize
;
1655 // we have at least one range at this point
1658 auto itNext
= std::next(it
);
1659 if (itNext
== endIt
)
1661 // check if neighbouring ranges overlap or adjoin
1662 if (needMerge(*it
, *itNext
))
1664 // lower bounds are sorted, implies: it->first = min(it[0].first, it[1].first)
1665 it
->second
= std::max(it
->second
, itNext
->second
);
1666 // remove next element
1667 std::move(std::next(itNext
), endIt
, itNext
);
1669 endIt
= aRangesTable
.get() + aRangesTableSize
;
1675 return WhichRangesContainer(std::move(aRangesTable
), aRangesTableSize
);
1678 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */