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