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 .
27 #include <svl/stylepool.hxx>
28 #include <svl/itemiter.hxx>
29 #include <svl/itempool.hxx>
32 using namespace boost
;
35 // A "Node" represents a subset of inserted SfxItemSets
36 // The root node represents the empty set
37 // The other nodes contain a SfxPoolItem and represents an item set which contains their
38 // pool item and the pool items of their parents.
41 std::vector
<Node
*> mChildren
; // child nodes, create by findChildNode(..)
42 // container of shared pointers of inserted item sets; for non-poolable
43 // items more than one item set is needed
44 std::vector
< StylePool::SfxItemSet_Pointer_t
> maItemSet
;
45 const SfxPoolItem
*mpItem
; // my pool item
46 Node
*mpUpper
; // if I'm a child node that's my parent node
48 const bool mbIsItemIgnorable
;
51 Node() // root node Ctor
56 mbIsItemIgnorable( false )
58 Node( const SfxPoolItem
& rItem
, Node
* pParent
, const bool bIgnorable
) // child node Ctor
61 mpItem( rItem
.Clone() ),
63 mbIsItemIgnorable( bIgnorable
)
67 bool hasItemSet( const bool bCheckUsage
) const;
69 const StylePool::SfxItemSet_Pointer_t
getItemSet() const
71 return maItemSet
.back();
73 const StylePool::SfxItemSet_Pointer_t
getUsedOrLastAddedItemSet() const;
74 void setItemSet( const SfxItemSet
& rSet
){ maItemSet
.push_back( StylePool::SfxItemSet_Pointer_t( rSet
.Clone() ) ); }
76 Node
* findChildNode( const SfxPoolItem
& rItem
,
77 const bool bIsItemIgnorable
= false );
78 Node
* nextItemSet( Node
* pLast
,
79 const bool bSkipUnusedItemSet
,
80 const bool bSkipIgnorable
);
81 const SfxPoolItem
& getPoolItem() const { return *mpItem
; }
83 bool hasIgnorableChildren( const bool bCheckUsage
) const;
84 const StylePool::SfxItemSet_Pointer_t
getItemSetOfIgnorableChild(
85 const bool bSkipUnusedItemSets
) const;
89 const StylePool::SfxItemSet_Pointer_t
Node::getUsedOrLastAddedItemSet() const
91 std::vector
< StylePool::SfxItemSet_Pointer_t
>::const_reverse_iterator aIter
;
93 for ( aIter
= maItemSet
.rbegin(); aIter
!= maItemSet
.rend(); ++aIter
)
95 if ( (*aIter
).use_count() > 1 )
101 return maItemSet
.back();
105 bool Node::hasItemSet( const bool bCheckUsage
) const
107 bool bHasItemSet
= false;
109 if ( !maItemSet
.empty())
113 std::vector
< StylePool::SfxItemSet_Pointer_t
>::const_reverse_iterator aIter
;
115 for ( aIter
= maItemSet
.rbegin(); aIter
!= maItemSet
.rend(); ++aIter
)
117 if ( (*aIter
).use_count() > 1 )
133 Node
* Node::findChildNode( const SfxPoolItem
& rItem
,
134 const bool bIsItemIgnorable
)
136 Node
* pNextNode
= this;
137 std::vector
<Node
*>::iterator aIter
= mChildren
.begin();
138 while( aIter
!= mChildren
.end() )
140 if( rItem
.Which() == (*aIter
)->getPoolItem().Which() &&
141 rItem
== (*aIter
)->getPoolItem() )
146 pNextNode
= new Node( rItem
, pNextNode
, bIsItemIgnorable
);
147 mChildren
.push_back( pNextNode
);
151 /* Find the next node which has a SfxItemSet.
152 The input parameter pLast has a sophisticated meaning:
154 pLast == 0 => scan your children and their children
155 but neither your parents neither your siblings
156 downstairs and upstairs:
157 pLast == this => scan your children, their children,
158 the children of your parent behind you, and so on
159 partial downstairs and upstairs
160 pLast != 0 && pLast != this => scan your children behind the given children,
161 the children of your parent behind you and so on.
163 OD 2008-03-11 #i86923#
164 introduce parameters <bSkipUnusedItemSets> and <bSkipIgnorable>
167 Node
* Node::nextItemSet( Node
* pLast
,
168 const bool bSkipUnusedItemSets
,
169 const bool bSkipIgnorable
)
171 // Searching downstairs
172 std::vector
<Node
*>::iterator aIter
= mChildren
.begin();
173 // For pLast == 0 and pLast == this all children are of interest
174 // for another pLast the search starts behind pLast...
175 if( pLast
&& pLast
!= this )
177 aIter
= std::find( mChildren
.begin(), mChildren
.end(), pLast
);
178 if( aIter
!= mChildren
.end() )
182 while( aIter
!= mChildren
.end() )
185 if ( bSkipIgnorable
&& (*aIter
)->mbIsItemIgnorable
)
192 if ( pNext
->hasItemSet( bSkipUnusedItemSets
) )
196 if ( bSkipIgnorable
&&
197 pNext
->hasIgnorableChildren( bSkipUnusedItemSets
) )
201 pNext
= pNext
->nextItemSet( 0, bSkipUnusedItemSets
, bSkipIgnorable
); // 0 => downstairs only
206 // Searching upstairs
207 if( pLast
&& mpUpper
)
210 pNext
= mpUpper
->nextItemSet( this, bSkipUnusedItemSets
, bSkipIgnorable
);
216 bool Node::hasIgnorableChildren( const bool bCheckUsage
) const
218 bool bHasIgnorableChildren( false );
220 std::vector
<Node
*>::const_iterator aIter
= mChildren
.begin();
221 while( aIter
!= mChildren
.end() && !bHasIgnorableChildren
)
223 Node
* pChild
= *aIter
;
224 if ( pChild
->mbIsItemIgnorable
)
226 bHasIgnorableChildren
=
228 ( pChild
->hasItemSet( bCheckUsage
/* == true */ ) ||
229 pChild
->hasIgnorableChildren( bCheckUsage
/* == true */ ) );
234 return bHasIgnorableChildren
;
237 const StylePool::SfxItemSet_Pointer_t
Node::getItemSetOfIgnorableChild(
238 const bool bSkipUnusedItemSets
) const
240 DBG_ASSERT( hasIgnorableChildren( bSkipUnusedItemSets
),
241 "<Node::getItemSetOfIgnorableChild> - node has no ignorable children" );
243 std::vector
<Node
*>::const_iterator aIter
= mChildren
.begin();
244 while( aIter
!= mChildren
.end() )
246 Node
* pChild
= *aIter
;
247 if ( pChild
->mbIsItemIgnorable
)
249 if ( pChild
->hasItemSet( bSkipUnusedItemSets
) )
251 return pChild
->getUsedOrLastAddedItemSet();
255 pChild
= pChild
->nextItemSet( 0, bSkipUnusedItemSets
, false );
258 return pChild
->getUsedOrLastAddedItemSet();
265 StylePool::SfxItemSet_Pointer_t pReturn
;
271 std::vector
<Node
*>::iterator aIter
= mChildren
.begin();
272 while( aIter
!= mChildren
.end() )
280 class Iterator
: public IStylePoolIteratorAccess
282 std::map
< const SfxItemSet
*, Node
>& mrRoot
;
283 std::map
< const SfxItemSet
*, Node
>::iterator mpCurrNode
;
285 const bool mbSkipUnusedItemSets
;
286 const bool mbSkipIgnorable
;
289 Iterator( std::map
< const SfxItemSet
*, Node
>& rR
,
290 const bool bSkipUnusedItemSets
,
291 const bool bSkipIgnorable
)
293 mpCurrNode( rR
.begin() ),
295 mbSkipUnusedItemSets( bSkipUnusedItemSets
),
296 mbSkipIgnorable( bSkipIgnorable
)
298 virtual StylePool::SfxItemSet_Pointer_t
getNext();
299 virtual OUString
getName();
302 StylePool::SfxItemSet_Pointer_t
Iterator::getNext()
304 StylePool::SfxItemSet_Pointer_t pReturn
;
305 while( mpNode
|| mpCurrNode
!= mrRoot
.end() )
309 mpNode
= &mpCurrNode
->second
;
312 if ( mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
315 return mpNode
->getUsedOrLastAddedItemSet();
319 mpNode
= mpNode
->nextItemSet( mpNode
, mbSkipUnusedItemSets
, mbSkipIgnorable
);
320 if ( mpNode
&& mpNode
->hasItemSet( mbSkipUnusedItemSets
) )
323 return mpNode
->getUsedOrLastAddedItemSet();
325 if ( mbSkipIgnorable
&&
326 mpNode
&& mpNode
->hasIgnorableChildren( mbSkipUnusedItemSets
) )
328 return mpNode
->getItemSetOfIgnorableChild( mbSkipUnusedItemSets
);
334 OUString
Iterator::getName()
337 if( mpNode
&& mpNode
->hasItemSet( false ) )
339 aString
= StylePool::nameOf( mpNode
->getUsedOrLastAddedItemSet() );
346 /* This static method creates a unique name from a shared pointer to a SfxItemSet
347 The name is the memory address of the SfxItemSet itself. */
349 OUString
StylePool::nameOf( SfxItemSet_Pointer_t pSet
)
351 return OUString::number( reinterpret_cast<sal_IntPtr
>( pSet
.get() ), 16 );
354 // class StylePoolImpl organized a tree-structure where every node represents a SfxItemSet.
355 // The insertItemSet method adds a SfxItemSet into the tree if necessary and returns a shared_ptr
356 // to a copy of the SfxItemSet.
357 // The aRoot-Node represents an empty SfxItemSet.
362 std::map
< const SfxItemSet
*, Node
> maRoot
;
365 SfxItemSet
* mpIgnorableItems
;
368 explicit StylePoolImpl( SfxItemSet
* pIgnorableItems
= 0 )
371 mpIgnorableItems( pIgnorableItems
!= 0
372 ? pIgnorableItems
->Clone( sal_False
)
375 DBG_ASSERT( !pIgnorableItems
|| !pIgnorableItems
->Count(),
376 "<StylePoolImpl::StylePoolImpl(..)> - misusage: item set for ignorable item should be empty. Please correct usage." );
377 DBG_ASSERT( !mpIgnorableItems
|| !mpIgnorableItems
->Count(),
378 "<StylePoolImpl::StylePoolImpl(..)> - <SfxItemSet::Clone( sal_False )> does not work as excepted - <mpIgnorableItems> is not empty. Please inform OD." );
383 delete mpIgnorableItems
;
386 StylePool::SfxItemSet_Pointer_t
insertItemSet( const SfxItemSet
& rSet
);
389 IStylePoolIteratorAccess
* createIterator( bool bSkipUnusedItemSets
= false,
390 bool bSkipIgnorableItems
= false );
391 sal_Int32
getCount() const { return mnCount
; }
394 StylePool::SfxItemSet_Pointer_t
StylePoolImpl::insertItemSet( const SfxItemSet
& rSet
)
396 bool bNonPoolable
= false;
397 Node
* pCurNode
= &maRoot
[ rSet
.GetParent() ];
398 SfxItemIter
aIter( rSet
);
399 const SfxPoolItem
* pItem
= aIter
.GetCurItem();
400 // Every SfxPoolItem in the SfxItemSet causes a step deeper into the tree,
401 // a complete empty SfxItemSet would stay at the root node.
402 // #i86923# insert ignorable items to the tree leaves.
403 std::auto_ptr
<SfxItemSet
> pFoundIgnorableItems
;
404 if ( mpIgnorableItems
)
406 pFoundIgnorableItems
.reset( new SfxItemSet( *mpIgnorableItems
) );
410 if( !rSet
.GetPool()->IsItemFlag(pItem
->Which(), SFX_ITEM_POOLABLE
) )
412 if ( !pFoundIgnorableItems
.get() ||
413 ( pFoundIgnorableItems
.get() &&
414 pFoundIgnorableItems
->Put( *pItem
) == 0 ) )
416 pCurNode
= pCurNode
->findChildNode( *pItem
);
418 pItem
= aIter
.NextItem();
420 if ( pFoundIgnorableItems
.get() &&
421 pFoundIgnorableItems
->Count() > 0 )
423 SfxItemIter
aIgnorableItemsIter( *pFoundIgnorableItems
);
424 pItem
= aIgnorableItemsIter
.GetCurItem();
427 if( !rSet
.GetPool()->IsItemFlag(pItem
->Which(), SFX_ITEM_POOLABLE
) )
429 pCurNode
= pCurNode
->findChildNode( *pItem
, true );
430 pItem
= aIgnorableItemsIter
.NextItem();
433 // Every leaf node represents an inserted item set, but "non-leaf" nodes represents subsets
434 // of inserted itemsets.
435 // These nodes could have but does not need to have a shared_ptr to a item set.
436 if( !pCurNode
->hasItemSet( false ) )
438 pCurNode
->setItemSet( rSet
);
439 bNonPoolable
= false; // to avoid a double insertion
442 // If rSet contains at least one non poolable item, a new itemset has to be inserted
444 pCurNode
->setItemSet( rSet
);
447 sal_Int32 nCheck
= -1;
448 IStylePoolIteratorAccess
* pIter
= createIterator();
449 StylePool::SfxItemSet_Pointer_t pTemp
;
453 pTemp
= pIter
->getNext();
454 } while( pTemp
.get() );
455 DBG_ASSERT( mnCount
== nCheck
, "Wrong counting");
459 return pCurNode
->getItemSet();
463 IStylePoolIteratorAccess
* StylePoolImpl::createIterator( bool bSkipUnusedItemSets
,
464 bool bSkipIgnorableItems
)
466 return new Iterator( maRoot
, bSkipUnusedItemSets
, bSkipIgnorableItems
);
468 // Ctor, Dtor and redirected methods of class StylePool, nearly inline ;-)
471 StylePool::StylePool( SfxItemSet
* pIgnorableItems
)
472 : pImpl( new StylePoolImpl( pIgnorableItems
) )
475 StylePool::SfxItemSet_Pointer_t
StylePool::insertItemSet( const SfxItemSet
& rSet
)
476 { return pImpl
->insertItemSet( rSet
); }
479 IStylePoolIteratorAccess
* StylePool::createIterator( const bool bSkipUnusedItemSets
,
480 const bool bSkipIgnorableItems
)
482 return pImpl
->createIterator( bSkipUnusedItemSets
, bSkipIgnorableItems
);
485 sal_Int32
StylePool::getCount() const
486 { return pImpl
->getCount(); }
488 StylePool::~StylePool() { delete pImpl
; }
490 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */