lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / svl / source / items / stylepool.cxx
blob46a4a726b8507a01010b3d02c8357a21e5c5e520
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 .
20 #include <svl/stylepool.hxx>
21 #include <svl/itemiter.hxx>
22 #include <svl/itempool.hxx>
23 #include <o3tl/make_unique.hxx>
24 #include <tools/debug.hxx>
25 #include <algorithm>
26 #include <map>
27 #include <memory>
28 #include <vector>
30 namespace {
31 /** A "Node" represents a subset of inserted SfxItemSets
32 * The root node represents the empty set
33 * The other nodes contain a SfxPoolItem and represents an item set which contains their
34 * pool item and the pool items of their parents.
36 class Node
38 std::vector<std::unique_ptr<Node>> mChildren; // child nodes, create by findChildNode(..)
39 // container of shared pointers of inserted item sets; for non-poolable
40 // items more than one item set is needed
41 std::vector< std::shared_ptr<SfxItemSet> > maItemSet;
42 std::unique_ptr<const SfxPoolItem> mpItem; // my pool item
43 Node *mpUpper; // if I'm a child node that's my parent node
44 // #i86923#
45 const bool mbIsItemIgnorable;
46 public:
47 // #i86923#
48 Node() // root node Ctor
49 : mChildren(),
50 maItemSet(),
51 mpUpper( nullptr ),
52 mbIsItemIgnorable( false )
54 Node( const SfxPoolItem& rItem, Node* pParent, const bool bIgnorable ) // child node Ctor
55 : mChildren(),
56 maItemSet(),
57 mpItem( rItem.Clone() ),
58 mpUpper( pParent ),
59 mbIsItemIgnorable( bIgnorable )
61 // #i86923#
62 bool hasItemSet( const bool bCheckUsage ) const;
63 // #i87808#
64 std::shared_ptr<SfxItemSet> const & getItemSet() const
66 return maItemSet.back();
68 std::shared_ptr<SfxItemSet> const & getUsedOrLastAddedItemSet() const;
69 void setItemSet( const SfxItemSet& rSet ){ maItemSet.push_back( std::shared_ptr<SfxItemSet>( rSet.Clone() ) ); }
70 // #i86923#
71 Node* findChildNode( const SfxPoolItem& rItem,
72 const bool bIsItemIgnorable );
73 Node* nextItemSet( Node const * pLast,
74 const bool bSkipUnusedItemSet,
75 const bool bSkipIgnorable );
76 // #i86923#
77 bool hasIgnorableChildren( const bool bCheckUsage ) const;
78 const std::shared_ptr<SfxItemSet> getItemSetOfIgnorableChild(
79 const bool bSkipUnusedItemSets ) const;
82 // #i87808#
83 std::shared_ptr<SfxItemSet> const & Node::getUsedOrLastAddedItemSet() const
85 std::vector< std::shared_ptr<SfxItemSet> >::const_reverse_iterator aIter;
87 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
89 if ( (*aIter).use_count() > 1 )
91 return *aIter;
95 return maItemSet.back();
98 // #i86923#
99 bool Node::hasItemSet( const bool bCheckUsage ) const
101 bool bHasItemSet = false;
103 if ( !maItemSet.empty())
105 if ( bCheckUsage )
107 std::vector< std::shared_ptr<SfxItemSet> >::const_reverse_iterator aIter;
109 for ( aIter = maItemSet.rbegin(); aIter != maItemSet.rend(); ++aIter )
111 if ( (*aIter).use_count() > 1 )
113 bHasItemSet = true;
114 break;
118 else
120 bHasItemSet = true;
123 return bHasItemSet;
126 // #i86923#
127 Node* Node::findChildNode( const SfxPoolItem& rItem,
128 const bool bIsItemIgnorable )
130 for( auto const & rChild : mChildren )
132 if( rItem.Which() == rChild->mpItem->Which() &&
133 rItem == *rChild->mpItem )
134 return rChild.get();
136 // #i86923#
137 auto pNextNode = new Node( rItem, this, bIsItemIgnorable );
138 mChildren.emplace_back( pNextNode );
139 return pNextNode;
143 * Find the next node which has a SfxItemSet.
144 * The input parameter pLast has a sophisticated meaning:
145 * downstairs only:
146 * pLast == 0 => scan your children and their children
147 * but neither your parents neither your siblings
148 * downstairs and upstairs:
149 * pLast == this => scan your children, their children,
150 * the children of your parent behind you, and so on
151 * partial downstairs and upstairs
152 * pLast != 0 && pLast != this => scan your children behind the given children,
153 * the children of your parent behind you and so on.
155 * OD 2008-03-11 #i86923#
156 * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
157 * and its handling.
159 Node* Node::nextItemSet( Node const * pLast,
160 const bool bSkipUnusedItemSets,
161 const bool bSkipIgnorable )
163 // Searching downstairs
164 auto aIter = mChildren.begin();
165 // For pLast == 0 and pLast == this all children are of interest
166 // for another pLast the search starts behind pLast...
167 if( pLast && pLast != this )
169 aIter = std::find_if( mChildren.begin(), mChildren.end(),
170 [&] (std::unique_ptr<Node> const &p) { return p.get() == pLast; });
171 if( aIter != mChildren.end() )
172 ++aIter;
174 Node *pNext = nullptr;
175 while( aIter != mChildren.end() )
177 // #i86923#
178 if ( bSkipIgnorable && (*aIter)->mbIsItemIgnorable )
180 ++aIter;
181 continue;
183 pNext = aIter->get();
184 // #i86923#
185 if ( pNext->hasItemSet( bSkipUnusedItemSets ) )
187 return pNext;
189 if ( bSkipIgnorable &&
190 pNext->hasIgnorableChildren( bSkipUnusedItemSets ) )
192 return pNext;
194 pNext = pNext->nextItemSet( nullptr, bSkipUnusedItemSets, bSkipIgnorable ); // 0 => downstairs only
195 if( pNext )
196 return pNext;
197 ++aIter;
199 // Searching upstairs
200 if( pLast && mpUpper )
202 // #i86923#
203 pNext = mpUpper->nextItemSet( this, bSkipUnusedItemSets, bSkipIgnorable );
205 return pNext;
208 // #i86923#
209 bool Node::hasIgnorableChildren( const bool bCheckUsage ) const
211 bool bHasIgnorableChildren( false );
213 auto aIter = mChildren.begin();
214 while( aIter != mChildren.end() && !bHasIgnorableChildren )
216 Node* pChild = aIter->get();
217 if ( pChild->mbIsItemIgnorable )
219 bHasIgnorableChildren =
220 !bCheckUsage ||
221 ( pChild->hasItemSet( bCheckUsage /* == true */ ) ||
222 pChild->hasIgnorableChildren( bCheckUsage /* == true */ ) );
224 ++aIter;
227 return bHasIgnorableChildren;
230 const std::shared_ptr<SfxItemSet> Node::getItemSetOfIgnorableChild(
231 const bool bSkipUnusedItemSets ) const
233 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets ),
234 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
236 auto aIter = mChildren.begin();
237 while( aIter != mChildren.end() )
239 Node* pChild = aIter->get();
240 if ( pChild->mbIsItemIgnorable )
242 if ( pChild->hasItemSet( bSkipUnusedItemSets ) )
244 return pChild->getUsedOrLastAddedItemSet();
246 else
248 pChild = pChild->nextItemSet( nullptr, bSkipUnusedItemSets, false );
249 if ( pChild )
251 return pChild->getUsedOrLastAddedItemSet();
255 ++aIter;
258 std::shared_ptr<SfxItemSet> pReturn;
259 return pReturn;
262 class Iterator : public IStylePoolIteratorAccess
264 std::map< const SfxItemSet*, Node >& mrRoot;
265 std::map< const SfxItemSet*, Node >::iterator mpCurrNode;
266 Node* mpNode;
267 const bool mbSkipUnusedItemSets;
268 const bool mbSkipIgnorable;
269 public:
270 // #i86923#
271 Iterator( std::map< const SfxItemSet*, Node >& rR,
272 const bool bSkipUnusedItemSets,
273 const bool bSkipIgnorable )
274 : mrRoot( rR ),
275 mpCurrNode( rR.begin() ),
276 mpNode(nullptr),
277 mbSkipUnusedItemSets( bSkipUnusedItemSets ),
278 mbSkipIgnorable( bSkipIgnorable )
280 virtual std::shared_ptr<SfxItemSet> getNext() override;
283 std::shared_ptr<SfxItemSet> Iterator::getNext()
285 std::shared_ptr<SfxItemSet> pReturn;
286 while( mpNode || mpCurrNode != mrRoot.end() )
288 if( !mpNode )
290 mpNode = &mpCurrNode->second;
291 ++mpCurrNode;
292 // #i86923#
293 if ( mpNode->hasItemSet( mbSkipUnusedItemSets ) )
295 // #i87808#
296 return mpNode->getUsedOrLastAddedItemSet();
299 // #i86923#
300 mpNode = mpNode->nextItemSet( mpNode, mbSkipUnusedItemSets, mbSkipIgnorable );
301 if ( mpNode && mpNode->hasItemSet( mbSkipUnusedItemSets ) )
303 // #i87808#
304 return mpNode->getUsedOrLastAddedItemSet();
306 if ( mbSkipIgnorable &&
307 mpNode && mpNode->hasIgnorableChildren( mbSkipUnusedItemSets ) )
309 return mpNode->getItemSetOfIgnorableChild( mbSkipUnusedItemSets );
312 return pReturn;
318 * This static method creates a unique name from a shared pointer to a SfxItemSet
319 * The name is the memory address of the SfxItemSet itself.
321 OUString StylePool::nameOf( const std::shared_ptr<SfxItemSet>& pSet )
323 return OUString::number( reinterpret_cast<sal_IntPtr>( pSet.get() ), 16 );
327 * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
328 * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
329 * to a copy of the SfxItemSet.
330 * The aRoot-Node represents an empty SfxItemSet.
332 class StylePoolImpl
334 private:
335 std::map< const SfxItemSet*, Node > maRoot;
336 // #i86923#
337 std::unique_ptr<SfxItemSet> mpIgnorableItems;
338 #ifdef DEBUG
339 sal_Int32 mnCount;
340 #endif
341 public:
342 // #i86923#
343 explicit StylePoolImpl( SfxItemSet const * pIgnorableItems )
344 : maRoot(),
345 #ifdef DEBUG
346 mnCount(0),
347 #endif
348 mpIgnorableItems( pIgnorableItems != nullptr
349 ? pIgnorableItems->Clone( false )
350 : nullptr )
352 DBG_ASSERT( !pIgnorableItems || !pIgnorableItems->Count(),
353 "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
354 DBG_ASSERT( !mpIgnorableItems || !mpIgnorableItems->Count(),
355 "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty." );
358 std::shared_ptr<SfxItemSet> insertItemSet( const SfxItemSet& rSet );
360 // #i86923#
361 std::unique_ptr<IStylePoolIteratorAccess> createIterator( bool bSkipUnusedItemSets,
362 bool bSkipIgnorableItems );
366 std::shared_ptr<SfxItemSet> StylePoolImpl::insertItemSet( const SfxItemSet& rSet )
368 bool bNonPoolable = false;
369 Node* pCurNode = &maRoot[ rSet.GetParent() ];
370 SfxItemIter aIter( rSet );
371 const SfxPoolItem* pItem = aIter.GetCurItem();
372 // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
373 // a complete empty SfxItemSet would stay at the root node.
374 // #i86923# insert ignorable items to the tree leaves.
375 std::unique_ptr<SfxItemSet> xFoundIgnorableItems;
376 if ( mpIgnorableItems )
378 xFoundIgnorableItems.reset( new SfxItemSet( *mpIgnorableItems ) );
380 while( pItem )
382 if( !rSet.GetPool()->IsItemPoolable(pItem->Which() ) )
383 bNonPoolable = true;
384 if (!xFoundIgnorableItems || (xFoundIgnorableItems->Put(*pItem) == nullptr))
386 pCurNode = pCurNode->findChildNode( *pItem, false );
388 pItem = aIter.NextItem();
390 if ( xFoundIgnorableItems.get() &&
391 xFoundIgnorableItems->Count() > 0 )
393 SfxItemIter aIgnorableItemsIter( *xFoundIgnorableItems );
394 pItem = aIgnorableItemsIter.GetCurItem();
395 while( pItem )
397 if( !rSet.GetPool()->IsItemPoolable(pItem->Which() ) )
398 bNonPoolable = true;
399 pCurNode = pCurNode->findChildNode( *pItem, true );
400 pItem = aIgnorableItemsIter.NextItem();
403 // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
404 // of inserted itemsets.
405 // These nodes could have but does not need to have a shared_ptr to a item set.
406 if( !pCurNode->hasItemSet( false ) )
408 pCurNode->setItemSet( rSet );
409 bNonPoolable = false; // to avoid a double insertion
410 #ifdef DEBUG
411 ++mnCount;
412 #endif
414 // If rSet contains at least one non poolable item, a new itemset has to be inserted
415 if( bNonPoolable )
416 pCurNode->setItemSet( rSet );
417 #ifdef DEBUG
419 sal_Int32 nCheck = -1;
420 std::unique_ptr<IStylePoolIteratorAccess> pIter = createIterator(false,false);
421 std::shared_ptr<SfxItemSet> pTemp;
424 ++nCheck;
425 pTemp = pIter->getNext();
426 } while( pTemp.get() );
427 DBG_ASSERT( mnCount == nCheck, "Wrong counting");
429 #endif
430 return pCurNode->getItemSet();
433 // #i86923#
434 std::unique_ptr<IStylePoolIteratorAccess> StylePoolImpl::createIterator( bool bSkipUnusedItemSets,
435 bool bSkipIgnorableItems )
437 return o3tl::make_unique<Iterator>( maRoot, bSkipUnusedItemSets, bSkipIgnorableItems );
439 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
441 // #i86923#
442 StylePool::StylePool( SfxItemSet const * pIgnorableItems )
443 : pImpl( new StylePoolImpl( pIgnorableItems ) )
446 std::shared_ptr<SfxItemSet> StylePool::insertItemSet( const SfxItemSet& rSet )
447 { return pImpl->insertItemSet( rSet ); }
449 // #i86923#
450 std::unique_ptr<IStylePoolIteratorAccess> StylePool::createIterator( const bool bSkipUnusedItemSets,
451 const bool bSkipIgnorableItems )
453 return pImpl->createIterator( bSkipUnusedItemSets, bSkipIgnorableItems );
456 StylePool::~StylePool()
459 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */