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 .
20 #include <svl/stylepool.hxx>
21 #include <svl/itemiter.hxx>
22 #include <svl/itempool.hxx>
29 /** A "Node" represents a subset of inserted SfxItemSets
30 * The root node represents the empty set
31 * The other nodes contain a SfxPoolItem and represents an item set which contains their
32 * pool item and the pool items of their parents.
36 std::vector
<Node
*> mChildren
; // child nodes, create by findChildNode(..)
37 // container of shared pointers of inserted item sets; for non-poolable
38 // items more than one item set is needed
39 std::vector
< StylePool::SfxItemSet_Pointer_t
> maItemSet
;
40 const SfxPoolItem
*mpItem
; // my pool item
41 Node
*mpUpper
; // if I'm a child node that's my parent node
43 const bool mbIsItemIgnorable
;
46 Node() // root node Ctor
51 mbIsItemIgnorable( false )
53 Node( const SfxPoolItem
& rItem
, Node
* pParent
, const bool bIgnorable
) // child node Ctor
56 mpItem( rItem
.Clone() ),
58 mbIsItemIgnorable( bIgnorable
)
62 bool hasItemSet( const bool bCheckUsage
) const;
64 const StylePool::SfxItemSet_Pointer_t
getItemSet() const
66 return maItemSet
.back();
68 const StylePool::SfxItemSet_Pointer_t
getUsedOrLastAddedItemSet() const;
69 void setItemSet( const SfxItemSet
& rSet
){ maItemSet
.push_back( StylePool::SfxItemSet_Pointer_t( rSet
.Clone() ) ); }
71 Node
* findChildNode( const SfxPoolItem
& rItem
,
72 const bool bIsItemIgnorable
= false );
73 Node
* nextItemSet( Node
* pLast
,
74 const bool bSkipUnusedItemSet
,
75 const bool bSkipIgnorable
);
76 const SfxPoolItem
& getPoolItem() const { return *mpItem
; }
78 bool hasIgnorableChildren( const bool bCheckUsage
) const;
79 const StylePool::SfxItemSet_Pointer_t
getItemSetOfIgnorableChild(
80 const bool bSkipUnusedItemSets
) const;
84 const StylePool::SfxItemSet_Pointer_t
Node::getUsedOrLastAddedItemSet() const
86 std::vector
< StylePool::SfxItemSet_Pointer_t
>::const_reverse_iterator aIter
;
88 for ( aIter
= maItemSet
.rbegin(); aIter
!= maItemSet
.rend(); ++aIter
)
90 if ( (*aIter
).use_count() > 1 )
96 return maItemSet
.back();
100 bool Node::hasItemSet( const bool bCheckUsage
) const
102 bool bHasItemSet
= false;
104 if ( !maItemSet
.empty())
108 std::vector
< StylePool::SfxItemSet_Pointer_t
>::const_reverse_iterator aIter
;
110 for ( aIter
= maItemSet
.rbegin(); aIter
!= maItemSet
.rend(); ++aIter
)
112 if ( (*aIter
).use_count() > 1 )
128 Node
* Node::findChildNode( const SfxPoolItem
& rItem
,
129 const bool bIsItemIgnorable
)
131 Node
* pNextNode
= this;
132 std::vector
<Node
*>::iterator aIter
= mChildren
.begin();
133 while( aIter
!= mChildren
.end() )
135 if( rItem
.Which() == (*aIter
)->getPoolItem().Which() &&
136 rItem
== (*aIter
)->getPoolItem() )
141 pNextNode
= new Node( rItem
, pNextNode
, bIsItemIgnorable
);
142 mChildren
.push_back( pNextNode
);
147 * Find the next node which has a SfxItemSet.
148 * The input parameter pLast has a sophisticated meaning:
150 * pLast == 0 => scan your children and their children
151 * but neither your parents neither your siblings
152 * downstairs and upstairs:
153 * pLast == this => scan your children, their children,
154 * the children of your parent behind you, and so on
155 * partial downstairs and upstairs
156 * pLast != 0 && pLast != this => scan your children behind the given children,
157 * the children of your parent behind you and so on.
159 * OD 2008-03-11 #i86923#
160 * introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
163 Node
* Node::nextItemSet( Node
* pLast
,
164 const bool bSkipUnusedItemSets
,
165 const bool bSkipIgnorable
)
167 // Searching downstairs
168 std::vector
<Node
*>::iterator aIter
= mChildren
.begin();
169 // For pLast == 0 and pLast == this all children are of interest
170 // for another pLast the search starts behind pLast...
171 if( pLast
&& pLast
!= this )
173 aIter
= std::find( mChildren
.begin(), mChildren
.end(), pLast
);
174 if( aIter
!= mChildren
.end() )
178 while( aIter
!= mChildren
.end() )
181 if ( bSkipIgnorable
&& (*aIter
)->mbIsItemIgnorable
)
188 if ( pNext
->hasItemSet( bSkipUnusedItemSets
) )
192 if ( bSkipIgnorable
&&
193 pNext
->hasIgnorableChildren( bSkipUnusedItemSets
) )
197 pNext
= pNext
->nextItemSet( 0, bSkipUnusedItemSets
, bSkipIgnorable
); // 0 => downstairs only
202 // Searching upstairs
203 if( pLast
&& mpUpper
)
206 pNext
= mpUpper
->nextItemSet( this, bSkipUnusedItemSets
, bSkipIgnorable
);
212 bool Node::hasIgnorableChildren( const bool bCheckUsage
) const
214 bool bHasIgnorableChildren( false );
216 std::vector
<Node
*>::const_iterator aIter
= mChildren
.begin();
217 while( aIter
!= mChildren
.end() && !bHasIgnorableChildren
)
219 Node
* pChild
= *aIter
;
220 if ( pChild
->mbIsItemIgnorable
)
222 bHasIgnorableChildren
=
224 ( pChild
->hasItemSet( bCheckUsage
/* == true */ ) ||
225 pChild
->hasIgnorableChildren( bCheckUsage
/* == true */ ) );
230 return bHasIgnorableChildren
;
233 const StylePool::SfxItemSet_Pointer_t
Node::getItemSetOfIgnorableChild(
234 const bool bSkipUnusedItemSets
) const
236 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets
),
237 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
239 std::vector
<Node
*>::const_iterator aIter
= mChildren
.begin();
240 while( aIter
!= mChildren
.end() )
242 Node
* pChild
= *aIter
;
243 if ( pChild
->mbIsItemIgnorable
)
245 if ( pChild
->hasItemSet( bSkipUnusedItemSets
) )
247 return pChild
->getUsedOrLastAddedItemSet();
251 pChild
= pChild
->nextItemSet( 0, bSkipUnusedItemSets
, false );
254 return pChild
->getUsedOrLastAddedItemSet();
261 StylePool::SfxItemSet_Pointer_t pReturn
;
267 std::vector
<Node
*>::iterator aIter
= mChildren
.begin();
268 while( aIter
!= mChildren
.end() )
276 class Iterator
: public IStylePoolIteratorAccess
278 std::map
< const SfxItemSet
*, Node
>& mrRoot
;
279 std::map
< const SfxItemSet
*, Node
>::iterator mpCurrNode
;
281 const bool mbSkipUnusedItemSets
;
282 const bool mbSkipIgnorable
;
285 Iterator( std::map
< const SfxItemSet
*, Node
>& rR
,
286 const bool bSkipUnusedItemSets
,
287 const bool bSkipIgnorable
)
289 mpCurrNode( rR
.begin() ),
291 mbSkipUnusedItemSets( bSkipUnusedItemSets
),
292 mbSkipIgnorable( bSkipIgnorable
)
294 virtual StylePool::SfxItemSet_Pointer_t
getNext() SAL_OVERRIDE
;
295 virtual OUString
getName() SAL_OVERRIDE
;
298 StylePool::SfxItemSet_Pointer_t
Iterator::getNext()
300 StylePool::SfxItemSet_Pointer_t pReturn
;
301 while( mpNode
|| mpCurrNode
!= mrRoot
.end() )
305 mpNode
= &mpCurrNode
->second
;
308 if ( mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
311 return mpNode
->getUsedOrLastAddedItemSet();
315 mpNode
= mpNode
->nextItemSet( mpNode
, mbSkipUnusedItemSets
, mbSkipIgnorable
);
316 if ( mpNode
&& mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
319 return mpNode
->getUsedOrLastAddedItemSet();
321 if ( mbSkipIgnorable
&&
322 mpNode
&& mpNode
->hasIgnorableChildren( mbSkipUnusedItemSets
) )
324 return mpNode
->getItemSetOfIgnorableChild( mbSkipUnusedItemSets
);
330 OUString
Iterator::getName()
333 if( mpNode
&& mpNode
->hasItemSet( false ) )
335 aString
= StylePool::nameOf( mpNode
->getUsedOrLastAddedItemSet() );
343 * This static method creates a unique name from a shared pointer to a SfxItemSet
344 * The name is the memory address of the SfxItemSet itself.
346 OUString
StylePool::nameOf( SfxItemSet_Pointer_t pSet
)
348 return OUString::number( reinterpret_cast<sal_IntPtr
>( pSet
.get() ), 16 );
352 * class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
353 * The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
354 * to a copy of the SfxItemSet.
355 * The aRoot-Node represents an empty SfxItemSet.
360 std::map
< const SfxItemSet
*, Node
> maRoot
;
363 SfxItemSet
* mpIgnorableItems
;
366 explicit StylePoolImpl( SfxItemSet
* pIgnorableItems
= 0 )
369 mpIgnorableItems( pIgnorableItems
!= 0
370 ? pIgnorableItems
->Clone( false )
373 DBG_ASSERT( !pIgnorableItems
|| !pIgnorableItems
->Count(),
374 "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
375 DBG_ASSERT( !mpIgnorableItems
|| !mpIgnorableItems
->Count(),
376 "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty. Please inform OD." );
381 delete mpIgnorableItems
;
384 StylePool::SfxItemSet_Pointer_t
insertItemSet( const SfxItemSet
& rSet
);
387 IStylePoolIteratorAccess
* createIterator( bool bSkipUnusedItemSets
= false,
388 bool bSkipIgnorableItems
= false );
389 sal_Int32
getCount() const { return mnCount
; }
392 StylePool::SfxItemSet_Pointer_t
StylePoolImpl::insertItemSet( const SfxItemSet
& rSet
)
394 bool bNonPoolable
= false;
395 Node
* pCurNode
= &maRoot
[ rSet
.GetParent() ];
396 SfxItemIter
aIter( rSet
);
397 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
398 // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
399 // a complete empty SfxItemSet would stay at the root node.
400 // #i86923# insert ignorable items to the tree leaves.
401 std::unique_ptr
<SfxItemSet
> xFoundIgnorableItems
;
402 if ( mpIgnorableItems
)
404 xFoundIgnorableItems
.reset( new SfxItemSet( *mpIgnorableItems
) );
408 if( !rSet
.GetPool()->IsItemFlag(pItem
->Which(), SfxItemPoolFlags::POOLABLE
) )
410 if ( !xFoundIgnorableItems
.get() ||
411 ( xFoundIgnorableItems
.get() &&
412 xFoundIgnorableItems
->Put( *pItem
) == 0 ) )
414 pCurNode
= pCurNode
->findChildNode( *pItem
);
416 pItem
= aIter
.NextItem();
418 if ( xFoundIgnorableItems
.get() &&
419 xFoundIgnorableItems
->Count() > 0 )
421 SfxItemIter
aIgnorableItemsIter( *xFoundIgnorableItems
);
422 pItem
= aIgnorableItemsIter
.GetCurItem();
425 if( !rSet
.GetPool()->IsItemFlag(pItem
->Which(), SfxItemPoolFlags::POOLABLE
) )
427 pCurNode
= pCurNode
->findChildNode( *pItem
, true );
428 pItem
= aIgnorableItemsIter
.NextItem();
431 // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
432 // of inserted itemsets.
433 // These nodes could have but does not need to have a shared_ptr to a item set.
434 if( !pCurNode
->hasItemSet( false ) )
436 pCurNode
->setItemSet( rSet
);
437 bNonPoolable
= false; // to avoid a double insertion
440 // If rSet contains at least one non poolable item, a new itemset has to be inserted
442 pCurNode
->setItemSet( rSet
);
445 sal_Int32 nCheck
= -1;
446 IStylePoolIteratorAccess
* pIter
= createIterator();
447 StylePool::SfxItemSet_Pointer_t pTemp
;
451 pTemp
= pIter
->getNext();
452 } while( pTemp
.get() );
453 DBG_ASSERT( mnCount
== nCheck
, "Wrong counting");
457 return pCurNode
->getItemSet();
461 IStylePoolIteratorAccess
* StylePoolImpl::createIterator( bool bSkipUnusedItemSets
,
462 bool bSkipIgnorableItems
)
464 return new Iterator( maRoot
, bSkipUnusedItemSets
, bSkipIgnorableItems
);
466 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
469 StylePool::StylePool( SfxItemSet
* pIgnorableItems
)
470 : pImpl( new StylePoolImpl( pIgnorableItems
) )
473 StylePool::SfxItemSet_Pointer_t
StylePool::insertItemSet( const SfxItemSet
& rSet
)
474 { return pImpl
->insertItemSet( rSet
); }
477 IStylePoolIteratorAccess
* StylePool::createIterator( const bool bSkipUnusedItemSets
,
478 const bool bSkipIgnorableItems
)
480 return pImpl
->createIterator( bSkipUnusedItemSets
, bSkipIgnorableItems
);
483 sal_Int32
StylePool::getCount() const
484 { return pImpl
->getCount(); }
486 StylePool::~StylePool() { delete pImpl
; }
488 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */