1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <memory>
21
22 #include <cellatr.hxx>
23 #include <charfmt.hxx>
24 #include <fchrfmt.hxx>
25 #include <doc.hxx>
26 #include <IDocumentListsAccess.hxx>
27 #include <editeng/editeng.hxx>
28 #include <fmtanchr.hxx>
29 #include <fmtpdsc.hxx>
30 #include <fmtautofmt.hxx>
31 #include <hintids.hxx>
32 #include <list.hxx>
33 #include <node.hxx>
34 #include <numrule.hxx>
35 #include <pagedesc.hxx>
36 #include <paratr.hxx>
37 #include <osl/diagnose.h>
38 #include <svl/whiter.hxx>
39
40 #include <svx/svdpool.hxx>
41 #include <svx/sxenditm.hxx>
42 #include <svx/sdsxyitm.hxx>
43
SwAttrPool(SwDoc * pD)44 SwAttrPool::SwAttrPool( SwDoc* pD )
45 : SfxItemPool( "SWG",
46 POOLATTR_BEGIN, POOLATTR_END-1,
47 aSlotTab, &aAttrTab ),
48 m_pDoc( pD )
49 {
50 // create secondary pools immediately
51 createAndAddSecondaryPools();
52 }
53
~SwAttrPool()54 SwAttrPool::~SwAttrPool()
55 {
56 // cleanup secondary pools first
57 removeAndDeleteSecondaryPools();
58 }
59
createAndAddSecondaryPools()60 void SwAttrPool::createAndAddSecondaryPools()
61 {
62 const SfxItemPool* pCheckAlreadySet = GetSecondaryPool();
63
64 if(pCheckAlreadySet)
65 {
66 OSL_ENSURE(false, "SwAttrPool already has a secondary pool (!)");
67 return;
68 }
69
70 // create SfxItemPool and EditEngine pool and add these in a chain. These
71 // belong us and will be removed/destroyed in removeAndDeleteSecondaryPools() used from
72 // the destructor
73 SfxItemPool *pSdrPool = new SdrItemPool(this);
74
75 // #75371# change DefaultItems for the SdrEdgeObj distance items
76 // to TWIPS.
77 // 1/100th mm in twips
78 const long nDefEdgeDist = (500 * 72) / 127;
79
80 pSdrPool->SetPoolDefaultItem(SdrEdgeNode1HorzDistItem(nDefEdgeDist));
81 pSdrPool->SetPoolDefaultItem(SdrEdgeNode1VertDistItem(nDefEdgeDist));
82 pSdrPool->SetPoolDefaultItem(SdrEdgeNode2HorzDistItem(nDefEdgeDist));
83 pSdrPool->SetPoolDefaultItem(SdrEdgeNode2VertDistItem(nDefEdgeDist));
84
85 // #i33700# // Set shadow distance defaults as PoolDefaultItems
86 pSdrPool->SetPoolDefaultItem(makeSdrShadowXDistItem((300 * 72) / 127));
87 pSdrPool->SetPoolDefaultItem(makeSdrShadowYDistItem((300 * 72) / 127));
88
89 SfxItemPool *pEEgPool = EditEngine::CreatePool();
90
91 pSdrPool->SetSecondaryPool(pEEgPool);
92
93 if(!GetFrozenIdRanges())
94 {
95 FreezeIdRanges();
96 }
97 else
98 {
99 pSdrPool->FreezeIdRanges();
100 }
101 }
102
removeAndDeleteSecondaryPools()103 void SwAttrPool::removeAndDeleteSecondaryPools()
104 {
105 SfxItemPool *pSdrPool = GetSecondaryPool();
106
107 if(!pSdrPool)
108 {
109 OSL_ENSURE(false, "SwAttrPool has no secondary pool, it's missing (!)");
110 return;
111 }
112
113 SfxItemPool *pEEgPool = pSdrPool->GetSecondaryPool();
114
115 if(!pEEgPool)
116 {
117 OSL_ENSURE(false, "i don't accept additional pools");
118 return;
119 }
120
121 // first delete the items, then break the linking
122 pSdrPool->Delete();
123
124 SetSecondaryPool(nullptr);
125 pSdrPool->SetSecondaryPool(nullptr);
126
127 // final cleanup of secondary pool(s)
128 SfxItemPool::Free(pSdrPool);
129 SfxItemPool::Free(pEEgPool);
130 }
131
SwAttrSet(SwAttrPool & rPool,sal_uInt16 nWh1,sal_uInt16 nWh2)132 SwAttrSet::SwAttrSet( SwAttrPool& rPool, sal_uInt16 nWh1, sal_uInt16 nWh2 )
133 : SfxItemSet( rPool, {{nWh1, nWh2}} ), m_pOldSet( nullptr ), m_pNewSet( nullptr )
134 {
135 }
136
SwAttrSet(SwAttrPool & rPool,const sal_uInt16 * nWhichPairTable)137 SwAttrSet::SwAttrSet( SwAttrPool& rPool, const sal_uInt16* nWhichPairTable )
138 : SfxItemSet( rPool, nWhichPairTable ), m_pOldSet( nullptr ), m_pNewSet( nullptr )
139 {
140 }
141
SwAttrSet(const SwAttrSet & rSet)142 SwAttrSet::SwAttrSet( const SwAttrSet& rSet )
143 : SfxItemSet( rSet ), m_pOldSet( nullptr ), m_pNewSet( nullptr )
144 {
145 }
146
Clone(bool bItems,SfxItemPool * pToPool) const147 std::unique_ptr<SfxItemSet> SwAttrSet::Clone( bool bItems, SfxItemPool *pToPool ) const
148 {
149 if ( pToPool && pToPool != GetPool() )
150 {
151 SwAttrPool* pAttrPool = dynamic_cast< SwAttrPool* >(pToPool);
152 std::unique_ptr<SfxItemSet> pTmpSet;
153 if ( !pAttrPool )
154 pTmpSet = SfxItemSet::Clone( bItems, pToPool );
155 else
156 {
157 pTmpSet.reset(new SwAttrSet( *pAttrPool, GetRanges() ));
158 if ( bItems )
159 {
160 SfxWhichIter aIter(*pTmpSet);
161 sal_uInt16 nWhich = aIter.FirstWhich();
162 while ( nWhich )
163 {
164 const SfxPoolItem* pItem;
165 if ( SfxItemState::SET == GetItemState( nWhich, false, &pItem ) )
166 pTmpSet->Put( *pItem );
167 nWhich = aIter.NextWhich();
168 }
169 }
170 }
171 return pTmpSet;
172 }
173 else
174 return std::unique_ptr<SfxItemSet>(
175 bItems
176 ? new SwAttrSet( *this )
177 : new SwAttrSet( *GetPool(), GetRanges() ));
178 }
179
Put_BC(const SfxPoolItem & rAttr,SwAttrSet * pOld,SwAttrSet * pNew)180 bool SwAttrSet::Put_BC( const SfxPoolItem& rAttr,
181 SwAttrSet* pOld, SwAttrSet* pNew )
182 {
183 m_pNewSet = pNew;
184 m_pOldSet = pOld;
185 bool bRet = nullptr != SfxItemSet::Put( rAttr );
186 m_pOldSet = m_pNewSet = nullptr;
187 return bRet;
188 }
189
Put_BC(const SfxItemSet & rSet,SwAttrSet * pOld,SwAttrSet * pNew)190 bool SwAttrSet::Put_BC( const SfxItemSet& rSet,
191 SwAttrSet* pOld, SwAttrSet* pNew )
192 {
193 m_pNewSet = pNew;
194 m_pOldSet = pOld;
195 bool bRet = SfxItemSet::Put( rSet );
196 m_pOldSet = m_pNewSet = nullptr;
197 return bRet;
198 }
199
ClearItem_BC(sal_uInt16 nWhich,SwAttrSet * pOld,SwAttrSet * pNew)200 sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich,
201 SwAttrSet* pOld, SwAttrSet* pNew )
202 {
203 m_pNewSet = pNew;
204 m_pOldSet = pOld;
205 sal_uInt16 nRet = SfxItemSet::ClearItem( nWhich );
206 m_pOldSet = m_pNewSet = nullptr;
207 return nRet;
208 }
209
ClearItem_BC(sal_uInt16 nWhich1,sal_uInt16 nWhich2,SwAttrSet * pOld,SwAttrSet * pNew)210 sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich1, sal_uInt16 nWhich2,
211 SwAttrSet* pOld, SwAttrSet* pNew )
212 {
213 OSL_ENSURE( nWhich1 <= nWhich2, "no valid range" );
214 m_pNewSet = pNew;
215 m_pOldSet = pOld;
216 sal_uInt16 nRet = 0;
217 for( ; nWhich1 <= nWhich2; ++nWhich1 )
218 nRet = nRet + SfxItemSet::ClearItem( nWhich1 );
219 m_pOldSet = m_pNewSet = nullptr;
220 return nRet;
221 }
222
Intersect_BC(const SfxItemSet & rSet,SwAttrSet * pOld,SwAttrSet * pNew)223 int SwAttrSet::Intersect_BC( const SfxItemSet& rSet,
224 SwAttrSet* pOld, SwAttrSet* pNew )
225 {
226 m_pNewSet = pNew;
227 m_pOldSet = pOld;
228 SfxItemSet::Intersect( rSet );
229 m_pOldSet = m_pNewSet = nullptr;
230 return pNew ? pNew->Count() : ( pOld ? pOld->Count() : 0 );
231 }
232
233 /// Notification callback
Changed(const SfxPoolItem & rOld,const SfxPoolItem & rNew)234 void SwAttrSet::Changed( const SfxPoolItem& rOld, const SfxPoolItem& rNew )
235 {
236 if( m_pOldSet )
237 m_pOldSet->PutChgd( rOld );
238 if( m_pNewSet )
239 m_pNewSet->PutChgd( rNew );
240 }
241
242 /** special treatment for some attributes
243
244 Set the Modify pointer (old pDefinedIn) for the following attributes:
245 - SwFormatDropCaps
246 - SwFormatPageDesc
247
248 (Is called at inserts into formats/nodes)
249 */
SetModifyAtAttr(const SwModify * pModify)250 bool SwAttrSet::SetModifyAtAttr( const SwModify* pModify )
251 {
252 bool bSet = false;
253
254 const SfxPoolItem* pItem;
255 if( SfxItemState::SET == GetItemState( RES_PAGEDESC, false, &pItem ) &&
256 static_cast<const SwFormatPageDesc*>(pItem)->GetDefinedIn() != pModify )
257 {
258 const_cast<SwFormatPageDesc*>(static_cast<const SwFormatPageDesc*>(pItem))->ChgDefinedIn( pModify );
259 bSet = true;
260 }
261
262 if( SfxItemState::SET == GetItemState( RES_PARATR_DROP, false, &pItem ) &&
263 static_cast<const SwFormatDrop*>(pItem)->GetDefinedIn() != pModify )
264 {
265 // If CharFormat is set and it is set in different attribute pools then
266 // the CharFormat has to be copied.
267 SwCharFormat* pCharFormat;
268 if( nullptr != ( pCharFormat = const_cast<SwFormatDrop*>(static_cast<const SwFormatDrop*>(pItem))->GetCharFormat() )
269 && GetPool() != pCharFormat->GetAttrSet().GetPool() )
270 {
271 pCharFormat = GetDoc()->CopyCharFormat( *pCharFormat );
272 const_cast<SwFormatDrop*>(static_cast<const SwFormatDrop*>(pItem))->SetCharFormat( pCharFormat );
273 }
274 const_cast<SwFormatDrop*>(static_cast<const SwFormatDrop*>(pItem))->ChgDefinedIn( pModify );
275 bSet = true;
276 }
277
278 if( SfxItemState::SET == GetItemState( RES_BOXATR_FORMULA, false, &pItem ) &&
279 static_cast<const SwTableBoxFormula*>(pItem)->GetDefinedIn() != pModify )
280 {
281 const_cast<SwTableBoxFormula*>(static_cast<const SwTableBoxFormula*>(pItem))->ChgDefinedIn( pModify );
282 bSet = true;
283 }
284
285 return bSet;
286 }
287
CopyToModify(SwModify & rMod) const288 void SwAttrSet::CopyToModify( SwModify& rMod ) const
289 {
290 // copy attributes across multiple documents if needed
291 SwContentNode* pCNd = dynamic_cast<SwContentNode*>( &rMod );
292 SwFormat* pFormat = dynamic_cast<SwFormat*>( &rMod );
293
294 if( pCNd || pFormat )
295 {
296 if( Count() )
297 {
298 // #i92811#
299 std::unique_ptr<SfxStringItem> pNewListIdItem;
300
301 const SfxPoolItem* pItem;
302 const SwDoc *pSrcDoc = GetDoc();
303 SwDoc *pDstDoc = pCNd ? pCNd->GetDoc() : pFormat->GetDoc();
304
305 // Does the NumRule has to be copied?
306 if( pSrcDoc != pDstDoc &&
307 SfxItemState::SET == GetItemState( RES_PARATR_NUMRULE, false, &pItem ) )
308 {
309 const OUString& rNm = static_cast<const SwNumRuleItem*>(pItem)->GetValue();
310 if( !rNm.isEmpty() )
311 {
312 SwNumRule* pDestRule = pDstDoc->FindNumRulePtr( rNm );
313 if( pDestRule )
314 pDestRule->SetInvalidRule( true );
315 else
316 pDstDoc->MakeNumRule( rNm, pSrcDoc->FindNumRulePtr( rNm ) );
317 }
318 }
319
320 // copy list and if needed also the corresponding list style
321 // for text nodes
322 if ( pSrcDoc != pDstDoc &&
323 pCNd && pCNd->IsTextNode() &&
324 GetItemState( RES_PARATR_LIST_ID, false, &pItem ) == SfxItemState::SET )
325 {
326 auto pStrItem = dynamic_cast<const SfxStringItem*>(pItem);
327 assert(pStrItem);
328 const OUString& sListId = pStrItem->GetValue();
329 if ( !sListId.isEmpty() &&
330 !pDstDoc->getIDocumentListsAccess().getListByName( sListId ) )
331 {
332 const SwList* pList = pSrcDoc->getIDocumentListsAccess().getListByName( sListId );
333 // copy list style, if needed
334 const OUString& sDefaultListStyleName =
335 pList->GetDefaultListStyleName();
336 // #i92811#
337 const SwNumRule* pDstDocNumRule =
338 pDstDoc->FindNumRulePtr( sDefaultListStyleName );
339 if ( !pDstDocNumRule )
340 {
341 pDstDoc->MakeNumRule( sDefaultListStyleName,
342 pSrcDoc->FindNumRulePtr( sDefaultListStyleName ) );
343 }
344 else
345 {
346 const SwNumRule* pSrcDocNumRule =
347 pSrcDoc->FindNumRulePtr( sDefaultListStyleName );
348 // If list id of text node equals the list style's
349 // default list id in the source document, the same
350 // should be hold in the destination document.
351 // Thus, create new list id item.
352 if (pSrcDocNumRule && sListId == pSrcDocNumRule->GetDefaultListId())
353 {
354 pNewListIdItem.reset(new SfxStringItem (
355 RES_PARATR_LIST_ID,
356 pDstDocNumRule->GetDefaultListId() ));
357 }
358 }
359 // check again, if list exist, because <SwDoc::MakeNumRule(..)>
360 // could have also created it.
361 if ( pNewListIdItem == nullptr &&
362 !pDstDoc->getIDocumentListsAccess().getListByName( sListId ) )
363 {
364 // copy list
365 pDstDoc->getIDocumentListsAccess().createList( sListId, sDefaultListStyleName );
366 }
367 }
368 }
369
370 std::unique_ptr< SfxItemSet > tmpSet;
371
372 const SwPageDesc* pPgDesc;
373 if( pSrcDoc != pDstDoc && SfxItemState::SET == GetItemState(
374 RES_PAGEDESC, false, &pItem ) &&
375 nullptr != ( pPgDesc = static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc()) )
376 {
377 tmpSet.reset(new SfxItemSet(*this));
378
379 SwPageDesc* pDstPgDesc = pDstDoc->FindPageDesc(pPgDesc->GetName());
380 if( !pDstPgDesc )
381 {
382 pDstPgDesc = pDstDoc->MakePageDesc(pPgDesc->GetName());
383 pDstDoc->CopyPageDesc( *pPgDesc, *pDstPgDesc );
384 }
385 SwFormatPageDesc aDesc( pDstPgDesc );
386 aDesc.SetNumOffset( static_cast<const SwFormatPageDesc*>(pItem)->GetNumOffset() );
387 tmpSet->Put( aDesc );
388 }
389
390 if( pSrcDoc != pDstDoc && SfxItemState::SET == GetItemState( RES_ANCHOR, false, &pItem )
391 && static_cast< const SwFormatAnchor* >( pItem )->GetContentAnchor() != nullptr )
392 {
393 if( !tmpSet )
394 tmpSet.reset( new SfxItemSet( *this ));
395 // Anchors at any node position cannot be copied to another document, because the SwPosition
396 // would still point to the old document. It needs to be fixed up explicitly.
397 tmpSet->ClearItem( RES_ANCHOR );
398 }
399
400 if (pSrcDoc != pDstDoc &&
401 SfxItemState::SET == GetItemState(RES_PARATR_LIST_AUTOFMT, false, &pItem))
402 {
403 SfxItemSet const& rAutoStyle(*static_cast<SwFormatAutoFormat const&>(*pItem).GetStyleHandle());
404 std::shared_ptr<SfxItemSet> const pNewSet(
405 rAutoStyle.SfxItemSet::Clone(true, &pDstDoc->GetAttrPool()));
406
407 // fix up character style, it contains pointers to pSrcDoc
408 if (SfxItemState::SET == pNewSet->GetItemState(RES_TXTATR_CHARFMT, false, &pItem))
409 {
410 auto const* pChar(static_cast<SwFormatCharFormat const*>(pItem));
411 SwCharFormat *const pCopy(pDstDoc->CopyCharFormat(*pChar->GetCharFormat()));
412 const_cast<SwFormatCharFormat*>(pChar)->SetCharFormat(pCopy);
413 }
414
415 SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
416 // TODO: for ODF export we'd need to add it to the autostyle pool
417 item.SetStyleHandle(pNewSet);
418 if (!tmpSet)
419 {
420 tmpSet.reset(new SfxItemSet(*this));
421 }
422 tmpSet->Put(item);
423 }
424
425 if( tmpSet )
426 {
427 if( pCNd )
428 {
429 // #i92811#
430 if ( pNewListIdItem != nullptr )
431 {
432 tmpSet->Put( *pNewListIdItem );
433 }
434 pCNd->SetAttr( *tmpSet );
435 }
436 else
437 {
438 pFormat->SetFormatAttr( *tmpSet );
439 }
440 }
441 else if( pCNd )
442 {
443 // #i92811#
444 if ( pNewListIdItem != nullptr )
445 {
446 SfxItemSet aTmpSet( *this );
447 aTmpSet.Put( *pNewListIdItem );
448 pCNd->SetAttr( aTmpSet );
449 }
450 else
451 {
452 pCNd->SetAttr( *this );
453 }
454 }
455 else
456 {
457 pFormat->SetFormatAttr( *this );
458 }
459 }
460 }
461 #if OSL_DEBUG_LEVEL > 0
462 else
463 OSL_FAIL("neither Format nor ContentNode - no Attributes copied");
464 #endif
465 }
466
467 /// check if ID is in range of attribute set IDs
IsInRange(const sal_uInt16 * pRange,const sal_uInt16 nId)468 bool IsInRange( const sal_uInt16* pRange, const sal_uInt16 nId )
469 {
470 while( *pRange )
471 {
472 if( *pRange <= nId && nId <= *(pRange+1) )
473 return true;
474 pRange += 2;
475 }
476 return false;
477 }
478
479 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
480