tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / svl / source / items / itemset.cxx
blobd9da9c093cc95ad4e6d706a15dda8ea8623a8085
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include <string.h>
23 #include <algorithm>
24 #include <cassert>
25 #include <cstddef>
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>
38 #ifdef DBG_UTIL
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}});
58 return;
60 aHit->second.first++;
61 aHit->second.second += nCount;
64 SVL_DLLPUBLIC void listSfxItemSetUsage()
66 struct sorted {
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);
81 sal_uInt32 nUsed(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);
87 SAL_INFO("svl.items",
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);
99 #endif
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()
112 : m_pPool(nullptr)
113 , m_pItem(nullptr)
114 #ifndef NDEBUG
115 , m_bDeleted(false)
116 #endif
118 #ifdef DBG_UTIL
119 nAllocatedSfxPoolItemHolderCount++;
120 nUsedSfxPoolItemHolderCount++;
121 #endif
124 SfxPoolItemHolder::SfxPoolItemHolder(SfxItemPool& rPool, const SfxPoolItem* pItem, bool bPassingOwnership)
125 : m_pPool(&rPool)
126 , m_pItem(pItem)
127 #ifndef NDEBUG
128 , m_bDeleted(false)
129 #endif
131 #ifdef DBG_UTIL
132 nAllocatedSfxPoolItemHolderCount++;
133 nUsedSfxPoolItemHolderCount++;
134 #endif
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)
144 #ifndef NDEBUG
145 , m_bDeleted(false)
146 #endif
148 #ifdef DBG_UTIL
149 assert(!rHolder.isDeleted() && "Destructed instance used (!)");
150 nAllocatedSfxPoolItemHolderCount++;
151 nUsedSfxPoolItemHolderCount++;
152 #endif
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()
161 #ifdef DBG_UTIL
162 assert(!isDeleted() && "Destructed instance used (!)");
163 nAllocatedSfxPoolItemHolderCount--;
164 #endif
165 if (nullptr != m_pItem && getPool().NeedsSurrogateSupport(m_pItem->Which()))
166 getPool().unregisterPoolItemHolder(*this);
167 if (nullptr != m_pItem)
168 implCleanupItemEntry(m_pItem);
169 #ifndef NDEBUG
170 m_bDeleted = true;
171 #endif
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)
179 return *this;
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);
196 return *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)
214 : m_pPool(&rPool)
215 , m_pParent(nullptr)
216 , m_nRegister(0)
217 #ifdef DBG_UTIL
218 , m_nRegisteredSfxItemIter(0)
219 #endif
220 , m_aWhichRanges(rPool.GetMergedIdRanges())
221 , m_aPoolItemMap()
223 #ifdef DBG_UTIL
224 nAllocatedSfxItemSetCount++;
225 nUsedSfxItemSetCount++;
226 #endif
227 assert(m_aWhichRanges.validRanges2());
230 SfxItemSet::SfxItemSet(SfxItemPool& pool, WhichRangesContainer wids)
231 : m_pPool(&pool)
232 , m_pParent(nullptr)
233 , m_nRegister(0)
234 #ifdef DBG_UTIL
235 , m_nRegisteredSfxItemIter(0)
236 #endif
237 , m_aWhichRanges(std::move(wids))
238 , m_aPoolItemMap()
240 #ifdef DBG_UTIL
241 nAllocatedSfxItemSetCount++;
242 nUsedSfxItemSetCount++;
243 #endif
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 )
251 , m_nRegister( 0 )
252 #ifdef DBG_UTIL
253 , m_nRegisteredSfxItemIter(0)
254 #endif
255 , m_aWhichRanges( rASet.m_aWhichRanges )
256 , m_aPoolItemMap()
258 #ifdef DBG_UTIL
259 nAllocatedSfxItemSetCount++;
260 nUsedSfxItemSetCount++;
261 #endif
262 if (rASet.GetRanges().empty())
263 return;
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 )
280 #ifdef DBG_UTIL
281 , m_nRegisteredSfxItemIter(0)
282 #endif
283 , m_aWhichRanges( std::move(rASet.m_aWhichRanges) )
284 , m_aPoolItemMap( std::move(rASet.m_aPoolItemMap) )
286 #ifdef DBG_UTIL
287 nAllocatedSfxItemSetCount++;
288 nUsedSfxItemSetCount++;
289 assert(0 == rASet.m_nRegisteredSfxItemIter && "ITEM: SfxItemSet MOVE constructor with active SfxItemIters (!)");
290 #endif
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()
310 #ifdef DBG_UTIL
311 nAllocatedSfxItemSetCount--;
312 addArrayUsage(Count(), TotalCount());
313 #endif
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
316 ClearAllItemsImpl();
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 )
326 if( !Count() )
327 return 0;
329 if( 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())
341 return 0;
343 #ifdef DBG_UTIL
344 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet ClearItem with active SfxItemIters (!)");
345 #endif
347 ClearSingleItem_PrepareRemove(aHit->second);
348 m_aPoolItemMap.erase(aHit);
350 return 1;
353 void SfxItemSet::ClearSingleItem_PrepareRemove(const SfxPoolItem* pItem)
355 if (nullptr == pItem)
356 return;
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)
371 // no Item, done
372 return;
374 if (IsInvalidItem(pItem) || IsDisabledItem(pItem))
375 // checks IsInvalidItem/IsDisabledItem
376 return;
378 if (SfxItemPool::IsSlot(pItem->Which()))
379 // no slots, these do not support NeedsSurrogateSupport
380 return;
382 if(!GetPool()->NeedsSurrogateSupport(pItem->Which()))
383 // not needed for this item, done
384 return;
386 // there must be a registered one
387 assert(0 != m_nRegister);
389 // decrement counter
390 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)
403 // no Item, done
404 return;
406 if (IsInvalidItem(pItem) || IsDisabledItem(pItem))
407 // checks IsInvalidItem/IsDisabledItem
408 return;
410 if (SfxItemPool::IsSlot(pItem->Which()))
411 // no slots, these do not support NeedsSurrogateSupport
412 return;
414 if(!GetPool()->NeedsSurrogateSupport(pItem->Which()))
415 // not needed for this item, done
416 return;
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);
425 // increment counter
426 m_nRegister++;
429 sal_uInt16 SfxItemSet::ClearAllItemsImpl()
431 if (0 == Count())
432 // no items set, done
433 return 0;
435 #ifdef DBG_UTIL
436 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet ClearAllItems with active SfxItemIters (!)");
437 #endif
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);
450 m_nRegister = 0;
453 return nRetval;
456 void SfxItemSet::ClearInvalidItems()
458 if (0 == Count())
459 // no items set, done
460 return;
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))
467 #ifdef DBG_UTIL
468 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet ClearInvalidItems with active SfxItemIters (!)");
469 #endif
470 aCandidate = m_aPoolItemMap.erase(aCandidate);
472 else
473 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))
488 // Item is Disabled
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;
495 // Item is set
496 return SfxItemState::SET;
499 if (GetRanges().doesContainWhich(nWhich))
501 // set to Default
502 eState = SfxItemState::DEFAULT;
505 // search in parent?
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);
513 return eState;
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)
525 *ppItem = nullptr;
528 return bRet;
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);
545 else
547 if (m_pParent)
548 bHasItem = SfxItemState::SET == m_pParent->GetItemState_ForWhichID( SfxItemState::DEFAULT, nWhich, true, nullptr);
550 if (bHasItem)
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)
574 delete &rItem;
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)
584 delete &rItem;
585 return nullptr;
588 const sal_uInt16 nWhich(rItem.Which());
590 if (!GetRanges().doesContainWhich(nWhich))
592 // no action needed: not in WhichRange
593 if (bPassingOwnership)
594 delete &rItem;
595 return nullptr;
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)
610 delete &rItem;
611 return nullptr;
615 // prepare new entry
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);
629 if (pEntry)
630 aHit->second = pNew;
631 else
633 #ifdef DBG_UTIL
634 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet PutImpl with active SfxItemIters (!)");
635 #endif
636 m_aPoolItemMap[nWhich] = pNew;
639 return pNew;
642 bool SfxItemSet::Put(const SfxItemSet& rSource, bool bInvalidAsDefault)
644 if (0 == rSource.Count())
645 // no items in source, done
646 return false;
648 bool bRetval(false);
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);
658 else
660 DisableOrInvalidateItem_ForWhichID(false, aCandidate->first);
663 else
665 bRetval |= nullptr != PutImpl(*aCandidate->second, false);
669 return bRetval;
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))
705 // Item is DontCare:
706 switch (eDontCareAs)
708 case SfxItemState::SET:
709 PutImpl(rSource.GetPool()->GetUserOrPoolDefaultItem(nWhich), false);
710 break;
712 case SfxItemState::DEFAULT:
713 ClearSingleItem_ForWhichID(nWhich);
714 break;
716 case SfxItemState::INVALID:
717 DisableOrInvalidateItem_ForWhichID(false, nWhich);
718 break;
720 default:
721 assert(!"invalid Argument for eDontCareAs");
724 else
726 // Item is set:
727 PutImpl(*aHit->second, false);
730 else
732 // Item is default:
733 switch (eDefaultAs)
735 case SfxItemState::SET:
736 PutImpl(rSource.GetPool()->GetUserOrPoolDefaultItem(nWhich), false);
737 break;
739 case SfxItemState::DEFAULT:
740 ClearSingleItem_ForWhichID(nWhich);
741 break;
743 case SfxItemState::INVALID:
744 DisableOrInvalidateItem_ForWhichID(false, nWhich);
745 break;
747 default:
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
773 if (bAllIncluded)
774 return;
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 )
788 // Identical Ranges?
789 if (GetRanges() == aNewRanges)
790 return;
792 assert(aNewRanges.validRanges2());
793 RecreateRanges_Impl(aNewRanges);
794 m_aWhichRanges = aNewRanges;
797 void SfxItemSet::SetRanges( WhichRangesContainer&& aNewRanges )
799 // Identical Ranges?
800 if (GetRanges() == aNewRanges)
801 return;
803 assert(aNewRanges.validRanges2());
804 RecreateRanges_Impl(aNewRanges);
805 m_aWhichRanges = std::move(aNewRanges);
808 void SfxItemSet::RecreateRanges_Impl(const WhichRangesContainer& rNewRanges)
810 if (0 == Count())
811 // no existing items, done
812 return;
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))
820 #ifdef DBG_UTIL
821 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet RecreateRanges with active SfxItemIters (!)");
822 #endif
823 ClearSingleItem_PrepareRemove(aCandidate->second);
824 aCandidate = m_aPoolItemMap.erase(aCandidate);
826 else
827 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
836 * of *this.
838 * SfxPoolItems in rSet, for which holds 'IsInvalidItem() == true' are
839 * taken over as invalid items.
841 * @return bool true
842 * SfxPoolItems have been taken over
844 * false
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
848 * set in rSet
850 bool SfxItemSet::Set
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
859 this SfxPoolItemSet
861 false
862 The SfxPoolItems from the parents of
863 rSet are not taken into account */
866 if (Count())
867 ClearItem();
869 if (!bDeep)
870 return Put(rSet, false);
872 bool bRet = false;
873 SfxWhichIter aIter1(*this);
874 SfxWhichIter aIter2(rSet);
875 sal_uInt16 nWhich1 = aIter1.FirstWhich();
876 sal_uInt16 nWhich2 = aIter2.FirstWhich();
877 for (;;)
879 if (!nWhich1 || !nWhich2)
880 break;
881 if (nWhich1 > nWhich2)
883 nWhich2 = aIter2.NextWhich();
884 continue;
886 if (nWhich1 < nWhich2)
888 nWhich1 = aIter1.NextWhich();
889 continue;
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();
898 return bRet;
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);
915 return pItem;
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);
928 #ifdef DBG_UTIL
929 if (IsDisabledItem(aHit->second))
930 SAL_INFO("svl.items", "SFX_WARNING: Getting disabled Item");
931 #endif
932 return *aHit->second;
935 if (bSrchInParent && nullptr != GetParent())
937 return GetParent()->Get(nWhich, bSrchInParent);
940 // Get the Default from the Pool and return
941 assert(m_pPool);
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
964 return;
966 if (!rSet.Count())
968 // no Items contained in rSet -> Delete everything
969 ClearAllItemsImpl();
970 return;
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))
988 #ifdef DBG_UTIL
989 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet Intersect with active SfxItemIters (!)");
990 #endif
991 ClearSingleItem_PrepareRemove(aCandidate->second);
992 aCandidate = m_aPoolItemMap.erase(aCandidate);
994 else
995 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())
1005 // None set?
1006 return;
1008 if (this == &rSet)
1010 // same ItemSet, all Items are contained -> Delete everything
1011 ClearAllItemsImpl();
1012 return;
1015 // CAUTION: In the former impl, the
1016 // - version for different ranges checked for SfxItemState::SET
1017 // in rSet
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))
1029 #ifdef DBG_UTIL
1030 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet Differentiate with active SfxItemIters (!)");
1031 #endif
1032 ClearSingleItem_PrepareRemove(aCandidate->second);
1033 aCandidate = m_aPoolItemMap.erase(aCandidate);
1035 else
1036 aCandidate++;
1041 * Decision table for MergeValue(s)
1043 * Principles:
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);
1142 if (pNew)
1144 #ifdef DBG_UTIL
1145 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet MergeItem with active SfxItemIters (!)");
1146 #endif
1147 m_aPoolItemMap[nWhich] = pNew;
1148 checkAddPoolRegistration(pNew);
1151 return;
1154 const SfxPoolItem* pFnd1(aHit->second);
1156 if (IsInvalidItem(pFnd1))
1158 return;
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
1170 bDoChange = true;
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
1180 bDoChange = true;
1183 else if (*pFnd1 != *pFnd2)
1185 // 2nd Item is set
1186 // Decision table: set, set, !=, doesn't matter
1187 bDoChange = true;
1190 if (bDoChange)
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
1227 return;
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
1243 return;
1245 if (!bDisable && IsInvalidItem(aHit->second))
1246 // already invalid item, done
1247 return;
1249 // cleanup entry
1250 ClearSingleItem_PrepareRemove(aHit->second);
1251 aHit->second = bDisable ? DISABLED_POOL_ITEM : INVALID_POOL_ITEM;
1253 else if (GetRanges().doesContainWhich(nWhich))
1255 #ifdef DBG_UTIL
1256 assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet DisableOrInvalidateItem with active SfxItemIters (!)");
1257 #endif
1258 // new entry
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
1270 if (this == &rCmp)
1271 return true;
1273 // check parents (if requested, also bComparePool)
1274 if (bComparePool && GetParent() != rCmp.GetParent())
1275 return false;
1277 // check pools (if requested)
1278 if (bComparePool && GetPool() != rCmp.GetPool())
1279 return false;
1281 // check count of set items
1282 if (Count() != rCmp.Count())
1283 return false;
1285 // both have no items, done
1286 if (0 == Count())
1287 return true;
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)
1298 return false;
1300 // only compare items if SfxItemState::SET, else the item ptrs are not set
1301 if (SfxItemState::SET == aStateA && !SfxPoolItem::areSame(pItem1, pItem2))
1302 return false;
1305 return true;
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()));
1313 if ( bItems )
1315 SfxWhichIter aIter(*pNewSet);
1316 sal_uInt16 nWhich = aIter.FirstWhich();
1317 while ( nWhich )
1319 const SfxPoolItem* pItem;
1320 if ( SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem ) )
1321 pNewSet->Put( *pItem );
1322 nWhich = aIter.NextWhich();
1325 return pNewSet;
1327 else
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());
1342 if ( bItems )
1344 SfxWhichIter aIter(aNewSet);
1345 sal_uInt16 nWhich = aIter.FirstWhich();
1346 while ( nWhich )
1348 const SfxPoolItem* pItem;
1349 if ( SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem ) )
1350 aNewSet.Put( *pItem );
1351 nWhich = aIter.NextWhich();
1354 return aNewSet;
1356 else
1357 return bItems
1358 ? *this
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);
1373 else
1375 pItem->dumpAsXml(pWriter);
1378 (void)xmlTextWriterEndElement(pWriter);
1382 // ----------------------------------------------- class SfxAllItemSet
1384 SfxAllItemSet::SfxAllItemSet( SfxItemPool &rPool )
1385 : SfxItemSet(rPool)
1389 SfxAllItemSet::SfxAllItemSet(const SfxItemSet &rCopy)
1390 : 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)
1399 : SfxItemSet(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 ));
1417 if ( bItems )
1418 pNewSet->Set( *this );
1419 return pNewSet;
1421 else
1422 return std::unique_ptr<SfxItemSet>(bItems ? new SfxAllItemSet(*this) : new SfxAllItemSet(*GetPool()));
1426 void WhichRangesContainer::CountRanges() const
1428 m_TotalCount = 0;
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 )
1434 : m_pairs(nullptr)
1435 , m_size(nSize)
1436 , m_TotalCount(0)
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)
1444 p[i] = wids[i];
1445 m_pairs = p;
1446 CountRanges();
1449 WhichRangesContainer::WhichRangesContainer(sal_uInt16 nWhichStart, sal_uInt16 nWhichEnd)
1450 : m_pairs(nullptr)
1451 , m_size(1)
1452 , m_TotalCount(0)
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 };
1460 m_pairs = p;
1461 CountRanges();
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);
1484 return *this;
1487 WhichRangesContainer& WhichRangesContainer::operator=(WhichRangesContainer const & other)
1489 reset();
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;
1498 if (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];
1503 m_pairs = p;
1505 else
1506 m_pairs = other.m_pairs;
1508 return *this;
1511 WhichRangesContainer::~WhichRangesContainer()
1513 reset();
1516 bool WhichRangesContainer::operator==(WhichRangesContainer const & other) const
1518 if (m_size != other.m_size)
1519 return false;
1520 if (m_TotalCount != other.m_TotalCount)
1521 return false;
1522 if (m_pairs == other.m_pairs)
1523 return true;
1524 return std::equal(m_pairs, m_pairs + m_size, other.m_pairs, other.m_pairs + m_size);
1528 void WhichRangesContainer::reset()
1530 if (m_bOwnRanges)
1532 delete [] m_pairs;
1533 m_bOwnRanges = false;
1535 m_pairs = nullptr;
1536 m_size = 0;
1537 m_TotalCount = 0;
1538 m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
1539 m_aLastWhichPairFirst = 0;
1540 m_aLastWhichPairSecond = 0;
1543 #ifdef DBG_UTIL
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()
1550 g_nMiss++;
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);
1555 #endif
1557 bool WhichRangesContainer::doesContainWhich(sal_uInt16 nWhich) const
1559 // special case for single entry - happens often e.g. UI stuff
1560 if (m_size == 1)
1562 if( m_pairs->first <= nWhich && nWhich <= m_pairs->second )
1563 return true;
1565 // we have only one WhichPair entry and it's not contained -> failed
1566 return false;
1569 if (m_size == 0)
1570 return false;
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)
1577 #ifdef DBG_UTIL
1578 isHit();
1579 #endif
1580 // we can re-use the last found WhichPair
1581 return true;
1584 #ifdef DBG_UTIL
1585 isMiss();
1586 #endif
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;
1601 // ...and return
1602 return true;
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;
1612 return false;
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));
1621 if (empty())
1622 return WhichRangesContainer(nFrom, nTo);
1624 // reset buffer
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 };
1640 bAdded = true;
1642 // insert current range
1643 aRangesTable[aRangesTableSize++] = rPair;
1645 if (!bAdded)
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
1656 for (;;)
1658 auto itNext = std::next(it);
1659 if (itNext == endIt)
1660 break;
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);
1668 --aRangesTableSize;
1669 endIt = aRangesTable.get() + aRangesTableSize;
1671 else
1672 ++it;
1675 return WhichRangesContainer(std::move(aRangesTable), aRangesTableSize);
1678 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */