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 #include <sal/config.h>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 
25 #include <cstddef>
26 
27 #include <hintids.hxx>
28 #include <hints.hxx>
29 
30 #include <svl/cintitem.hxx>
31 #include <svl/stritem.hxx>
32 #include <fmtanchr.hxx>
33 #include <fmtfld.hxx>
34 #include <redline.hxx>
35 #include <pam.hxx>
36 #include <doc.hxx>
37 #include <IDocumentFieldsAccess.hxx>
38 #include <IDocumentRedlineAccess.hxx>
39 #include <IDocumentLayoutAccess.hxx>
40 #include <IDocumentMarkAccess.hxx>
41 #include <ndtxt.hxx>
42 #include <fldbas.hxx>
43 #include <docufld.hxx>
44 #include <txtfld.hxx>
45 #include <tox.hxx>
46 #include <expfld.hxx>
47 #include <bookmrk.hxx>
48 #include <fltshell.hxx>
49 #include <rdfhelper.hxx>
50 
51 using namespace com::sun::star;
52 
GetContentNode(SwDoc & rDoc,SwNodeIndex & rIdx,bool bNext)53 static SwContentNode* GetContentNode(SwDoc& rDoc, SwNodeIndex& rIdx, bool bNext)
54 {
55     SwContentNode * pCNd = rIdx.GetNode().GetContentNode();
56     if(!pCNd && nullptr == (pCNd = bNext ? rDoc.GetNodes().GoNext(&rIdx)
57                                      : SwNodes::GoPrevious(&rIdx)))
58     {
59         pCNd = bNext ? SwNodes::GoPrevious(&rIdx)
60                      : rDoc.GetNodes().GoNext(&rIdx);
61         OSL_ENSURE(pCNd, "no ContentNode found");
62     }
63     return pCNd;
64 }
65 
lcl_getTypePath(OUString & rType)66 static OUString lcl_getTypePath(OUString& rType)
67 {
68     OUString aRet;
69     if (rType.startsWith("urn:bails"))
70     {
71         rType = "urn:bails";
72         aRet = "tscp/bails.rdf";
73     }
74     return aRet;
75 }
76 
77 // Stack entry for all text attributes
SwFltStackEntry(const SwPosition & rStartPos,std::unique_ptr<SfxPoolItem> pHt)78 SwFltStackEntry::SwFltStackEntry(const SwPosition& rStartPos, std::unique_ptr<SfxPoolItem> pHt)
79     : m_aMkPos(rStartPos)
80     , m_aPtPos(rStartPos)
81     , m_pAttr( std::move(pHt) )
82     , m_isAnnotationOnEnd(false)
83     , mnStartCP(-1)
84     , mnEndCP(-1)
85     , m_bIsParaEnd(false)
86 {
87     m_bOld    = false;    // used for marking Attributes *before* skipping field results
88     m_bOpen = true;       // lock the attribute --> may first
89     m_bConsumedByField = false;
90 }
91 
~SwFltStackEntry()92 SwFltStackEntry::~SwFltStackEntry()
93 {
94     // Although attribute got passed as pointer, it gets deleted here
95 }
96 
SetEndPos(const SwPosition & rEndPos)97 void SwFltStackEntry::SetEndPos(const SwPosition& rEndPos)
98 {
99     // Release attribute and keep track of end
100     // Everything with sal_uInt16s, lest the inserting of new text at
101     // the cursor position moves the attribute's range
102     // That's not the desired behavior!
103     m_bOpen = false;                  // release and remember END
104     m_aPtPos.FromSwPosition(rEndPos);
105 }
106 
MakeRegion(SwDoc & rDoc,SwPaM & rRegion,RegionMode const eCheck,const SwFltPosition & rMkPos,const SwFltPosition & rPtPos,bool bIsParaEnd,sal_uInt16 nWhich)107 bool SwFltStackEntry::MakeRegion(SwDoc& rDoc, SwPaM& rRegion, RegionMode const eCheck,
108     const SwFltPosition &rMkPos, const SwFltPosition &rPtPos, bool bIsParaEnd,
109     sal_uInt16 nWhich)
110 {
111     // does this range actually contain something?
112     // empty range is allowed if at start of empty paragraph
113     // fields are special: never have range, so leave them
114 
115     // The only position of 0x0D will not be able to make region in the old logic
116     // because it is beyond the length of para...need special consideration here.
117     sal_uLong nMk = rMkPos.m_nNode.GetIndex() + 1;
118     const SwNodes& rMkNodes = rMkPos.m_nNode.GetNodes();
119     if (nMk >= rMkNodes.Count())
120         return false;
121     SwContentNode *const pContentNode(rMkNodes[nMk]->GetContentNode());
122     if (rMkPos == rPtPos &&
123         ((0 != rPtPos.m_nContent) || (pContentNode && (0 != pContentNode->Len())))
124         && ( RES_TXTATR_FIELD != nWhich
125              && RES_TXTATR_ANNOTATION != nWhich
126              && RES_TXTATR_INPUTFIELD != nWhich )
127         && !(bIsParaEnd && pContentNode && pContentNode->IsTextNode() && 0 != pContentNode->Len() ))
128     {
129         return false;
130     }
131     // The content indices always apply to the node!
132     rRegion.GetPoint()->nNode = rMkPos.m_nNode.GetIndex() + 1;
133     SwContentNode* pCNd = GetContentNode(rDoc, rRegion.GetPoint()->nNode, true);
134 
135     SAL_WARN_IF(pCNd->Len() < rMkPos.m_nContent, "sw.ww8",
136         "invalid content index " << rMkPos.m_nContent << " but text node has only " << pCNd->Len());
137     rRegion.GetPoint()->nContent.Assign(pCNd,
138             std::min<sal_Int32>(rMkPos.m_nContent, pCNd->Len()));
139     rRegion.SetMark();
140     if (rMkPos.m_nNode != rPtPos.m_nNode)
141     {
142         sal_uLong n = rPtPos.m_nNode.GetIndex() + 1;
143         SwNodes& rNodes = rRegion.GetPoint()->nNode.GetNodes();
144         if (n >= rNodes.Count())
145             return false;
146         rRegion.GetPoint()->nNode = n;
147         pCNd = GetContentNode(rDoc, rRegion.GetPoint()->nNode, false);
148     }
149     SAL_WARN_IF(pCNd->Len() < rPtPos.m_nContent, "sw.ww8",
150         "invalid content index " << rPtPos.m_nContent << " but text node has only " << pCNd->Len());
151     rRegion.GetPoint()->nContent.Assign(pCNd,
152             std::min<sal_Int32>(rPtPos.m_nContent, pCNd->Len()));
153     OSL_ENSURE( CheckNodesRange( rRegion.Start()->nNode,
154                              rRegion.End()->nNode, true ),
155              "attribute or similar crosses section-boundaries" );
156     bool bRet = true;
157     if (eCheck & RegionMode::CheckNodes)
158     {
159         bRet &= CheckNodesRange(rRegion.Start()->nNode,
160                                 rRegion.End()->nNode, true);
161     }
162     if (eCheck & RegionMode::CheckFieldmark)
163     {
164         bRet &= !sw::mark::IsFieldmarkOverlap(rRegion);
165     }
166     return bRet;
167 }
168 
MakeRegion(SwDoc & rDoc,SwPaM & rRegion,RegionMode eCheck) const169 bool SwFltStackEntry::MakeRegion(SwDoc& rDoc, SwPaM& rRegion, RegionMode eCheck) const
170 {
171     return MakeRegion(rDoc, rRegion, eCheck, m_aMkPos, m_aPtPos, m_bIsParaEnd,
172         m_pAttr->Which());
173 }
174 
SwFltControlStack(SwDoc & rDo,sal_uLong nFieldFl)175 SwFltControlStack::SwFltControlStack(SwDoc& rDo, sal_uLong nFieldFl)
176     : m_nFieldFlags(nFieldFl),m_bHasSdOD(true), m_bSdODChecked(false), m_rDoc(rDo), m_bIsEndStack(false)
177 {
178 }
179 
~SwFltControlStack()180 SwFltControlStack::~SwFltControlStack()
181 {
182     OSL_ENSURE(m_Entries.empty(), "There are still Attributes on the stack");
183 }
184 
185 // MoveAttrs() is meant to address the following problem:
186 // When a field like "set variable" is set through the stack, the text
187 // is shifted by one \xff character, which makes all subsequent
188 // attribute positions invalid.
189 // After setting the attribute in the doc, MoveAttrs() needs to be
190 // called in order to push all attribute positions to the right in the
191 // same paragraph further out by one character.
MoveAttrs(const SwPosition & rPos,MoveAttrsMode eMode)192 void SwFltControlStack::MoveAttrs(const SwPosition& rPos, MoveAttrsMode eMode)
193 {
194     sal_uLong nPosNd = rPos.nNode.GetIndex();
195     sal_uInt16 nPosCt = rPos.nContent.GetIndex() - 1;
196 
197     for (size_t i = 0, nCnt = m_Entries.size(); i < nCnt; ++i)
198     {
199         SwFltStackEntry& rEntry = *m_Entries[i];
200         if (
201             (rEntry.m_aMkPos.m_nNode.GetIndex()+1 == nPosNd) &&
202             (rEntry.m_aMkPos.m_nContent >= nPosCt)
203            )
204         {
205             rEntry.m_aMkPos.m_nContent++;
206             OSL_ENSURE( rEntry.m_aMkPos.m_nContent
207                 <= m_rDoc.GetNodes()[nPosNd]->GetContentNode()->Len(),
208                     "Attribute ends after end of line" );
209         }
210         if (
211             (rEntry.m_aPtPos.m_nNode.GetIndex()+1 == nPosNd) &&
212             (rEntry.m_aPtPos.m_nContent >= nPosCt)
213            )
214         {
215             if (    !rEntry.m_isAnnotationOnEnd
216                 ||  rEntry.m_aPtPos.m_nContent > nPosCt)
217             {
218                 assert(!(rEntry.m_isAnnotationOnEnd && rEntry.m_aPtPos.m_nContent > nPosCt));
219                 if (    eMode == MoveAttrsMode::POSTIT_INSERTED
220                     &&  rEntry.m_aPtPos.m_nContent == nPosCt
221                     &&  rEntry.m_pAttr->Which() == RES_FLTR_ANNOTATIONMARK)
222                 {
223                     rEntry.m_isAnnotationOnEnd = true;
224                     eMode = MoveAttrsMode::DEFAULT; // only set 1 flag
225                 }
226                 rEntry.m_aPtPos.m_nContent++;
227                 OSL_ENSURE( rEntry.m_aPtPos.m_nContent
228                     <= m_rDoc.GetNodes()[nPosNd]->GetContentNode()->Len(),
229                         "Attribute ends after end of line" );
230             }
231         }
232     }
233 }
234 
MarkAllAttrsOld()235 void SwFltControlStack::MarkAllAttrsOld()
236 {
237     size_t nCnt = m_Entries.size();
238     for (size_t i=0; i < nCnt; ++i)
239         m_Entries[i]->m_bOld = true;
240 }
241 
242 namespace
243 {
couldExtendEntry(const SwFltStackEntry * pExtendCandidate,const SfxPoolItem & rAttr)244     bool couldExtendEntry(const SwFltStackEntry *pExtendCandidate,
245         const SfxPoolItem& rAttr)
246     {
247         return (pExtendCandidate &&
248                 !pExtendCandidate->m_bConsumedByField &&
249                 //if we bring character attributes into the fold we need to both
250                 //a) consider RES_CHRATR_FONTSIZE and RES_CHRATR_FONT wrt Word's CJK/CTL variants
251                 //b) consider crossing table cell boundaries (tdf#102334)
252                 isPARATR_LIST(rAttr.Which()) &&
253                 *(pExtendCandidate->m_pAttr) == rAttr);
254     }
255 }
256 
NewAttr(const SwPosition & rPos,const SfxPoolItem & rAttr)257 void SwFltControlStack::NewAttr(const SwPosition& rPos, const SfxPoolItem& rAttr)
258 {
259     sal_uInt16 nWhich = rAttr.Which();
260     // Set end position of potentially equal attributes on stack, so
261     // as to avoid having them accumulate
262     SwFltStackEntry *pExtendCandidate = SetAttr(rPos, nWhich);
263     if (couldExtendEntry(pExtendCandidate, rAttr))
264     {
265         //Here we optimize by seeing if there is an attribute uncommitted
266         //to the document which
267 
268         //a) has the same value as this attribute
269         //b) is already open, or ends at the same place as where we're starting
270         //from. If so we merge it with this one and elide adding another
271         //to the stack
272         pExtendCandidate->SetEndPos(rPos);
273         pExtendCandidate->m_bOpen=true;
274     }
275     else
276     {
277         SwFltStackEntry *pTmp = new SwFltStackEntry(rPos, std::unique_ptr<SfxPoolItem>(rAttr.Clone()) );
278         pTmp->SetStartCP(GetCurrAttrCP());
279         m_Entries.push_back(std::unique_ptr<SwFltStackEntry>(pTmp));
280     }
281 }
282 
DeleteAndDestroy(Entries::size_type nCnt)283 void SwFltControlStack::DeleteAndDestroy(Entries::size_type nCnt)
284 {
285     OSL_ENSURE(nCnt < m_Entries.size(), "Out of range!");
286     if (nCnt < m_Entries.size())
287     {
288         auto aElement = m_Entries.begin() + nCnt;
289         m_Entries.erase(aElement);
290     }
291     //Clear the para end position recorded in reader intermittently for the least impact on loading performance
292     //Because the attributes handled based on the unit of para
293     if ( empty() )
294     {
295         ClearParaEndPosition();
296         m_bHasSdOD = true;
297         m_bSdODChecked = false;
298     }
299 }
300 
301 // SwFltControlStack::StealAttr() removes attributes of the given type
302 // from the stack. Allowed as nAttrId: 0 meaning any, or a specific
303 // type.  This makes them disappear from the doc structure. Only
304 // attributes from the same paragraph as rPos are removed. Used for
305 // graphic apos -> images.
StealAttr(const SwNodeIndex & rNode)306 void SwFltControlStack::StealAttr(const SwNodeIndex& rNode)
307 {
308     size_t nCnt = m_Entries.size();
309 
310     while (nCnt)
311     {
312         nCnt --;
313         SwFltStackEntry& rEntry = *m_Entries[nCnt];
314         if (rEntry.m_aPtPos.m_nNode.GetIndex()+1 == rNode.GetIndex())
315         {
316             DeleteAndDestroy(nCnt);     // delete from the stack
317         }
318     }
319 }
320 
321 // SwFltControlStack::KillUnlockedAttr() removes all attributes from
322 // the stack, which are assigned to an rPos. This makes them disappear
323 // from the doc structure. Used in WW import for ignoring attributes
324 // assigned to the 0x0c section break symbol.
KillUnlockedAttrs(const SwPosition & rPos)325 void SwFltControlStack::KillUnlockedAttrs(const SwPosition& rPos)
326 {
327     SwFltPosition aFltPos(rPos);
328 
329     size_t nCnt = m_Entries.size();
330     while( nCnt )
331     {
332         nCnt --;
333         SwFltStackEntry& rEntry = *m_Entries[nCnt];
334         if(    !rEntry.m_bOld
335             && !rEntry.m_bOpen
336             && (rEntry.m_aMkPos == aFltPos)
337             && (rEntry.m_aPtPos == aFltPos))
338         {
339             DeleteAndDestroy( nCnt ); // remove from stack
340         }
341     }
342 }
343 
344 // Unlock all locked attributes and move to the end, all others will
345 // be applied to the document and removed from the stack.
346 // Returns if there were any selected attributes on the stack
SetAttr(const SwPosition & rPos,sal_uInt16 nAttrId,bool bTstEnd,tools::Long nHand,bool consumedByField)347 SwFltStackEntry* SwFltControlStack::SetAttr(const SwPosition& rPos,
348     sal_uInt16 nAttrId, bool bTstEnd, tools::Long nHand,
349     bool consumedByField)
350 {
351     SwFltStackEntry *pRet = nullptr;
352 
353     SwFltPosition aFltPos(rPos);
354 
355     OSL_ENSURE(!nAttrId ||
356         (POOLATTR_BEGIN <= nAttrId && POOLATTR_END > nAttrId) ||
357         (RES_FLTRATTR_BEGIN <= nAttrId && RES_FLTRATTR_END > nAttrId),
358         "Wrong id for attribute");
359 
360     auto aI = m_Entries.begin();
361     while (aI != m_Entries.end())
362     {
363         bool bLastEntry = aI == m_Entries.end() - 1;
364 
365         SwFltStackEntry& rEntry = **aI;
366         if (rEntry.m_bOpen)
367         {
368             // set end of attribute
369             bool bF = false;
370             if (!nAttrId )
371             {
372                 bF = true;
373             }
374             else if (nAttrId == rEntry.m_pAttr->Which())
375             {
376                 if( nAttrId != RES_FLTR_BOOKMARK && nAttrId != RES_FLTR_ANNOTATIONMARK && nAttrId != RES_FLTR_RDFMARK )
377                 {
378                     // query handle
379                     bF = true;
380                 }
381                 else if (nAttrId == RES_FLTR_BOOKMARK && nHand == static_cast<SwFltBookmark*>(rEntry.m_pAttr.get())->GetHandle())
382                 {
383                     bF = true;
384                 }
385                 else if (nAttrId == RES_FLTR_ANNOTATIONMARK && nHand == static_cast<CntUInt16Item*>(rEntry.m_pAttr.get())->GetValue())
386                 {
387                     bF = true;
388                 }
389                 else if (nAttrId == RES_FLTR_RDFMARK && nHand == static_cast<SwFltRDFMark*>(rEntry.m_pAttr.get())->GetHandle())
390                 {
391                     bF = true;
392                 }
393             }
394             if (bF)
395             {
396                 rEntry.m_bConsumedByField = consumedByField;
397                 rEntry.SetEndPos(rPos);
398                 rEntry.SetEndCP(GetCurrAttrCP());
399                 if (bLastEntry && nAttrId == rEntry.m_pAttr->Which())
400                 {
401                     //potential candidate for merging with an identical
402                     //property beginning at rPos
403                     pRet = &rEntry;
404                 }
405             }
406             ++aI;
407             continue;
408         }
409 
410         // if the end position is equal to the cursor position, then
411         // refrain from applying it; there needs to be following text,
412         // except at the very end. (attribute expansion !!)
413         // Never apply end stack except at document ending
414         if (bTstEnd)
415         {
416             if (m_bIsEndStack)
417             {
418                 ++aI;
419                 continue;
420             }
421 
422             //defer inserting this attribute into the document until
423             //we advance to the next node, or finish processing the document
424             if (rEntry.m_aPtPos.m_nNode.GetIndex() == aFltPos.m_nNode.GetIndex())
425             {
426                 if (bLastEntry && nAttrId == rEntry.m_pAttr->Which() &&
427                     rEntry.m_aPtPos.m_nContent == aFltPos.m_nContent)
428                 {
429                     //potential candidate for merging with an identical
430                     //property beginning at rPos
431                     pRet = &rEntry;
432                 }
433 
434                 ++aI;
435                 continue;
436             }
437         }
438         SetAttrInDoc(rPos, rEntry);
439         aI = m_Entries.erase(aI);
440     }
441 
442     return pRet;
443 }
444 
MakePoint(const SwFltStackEntry & rEntry,SwDoc & rDoc,SwPaM & rRegion)445 static bool MakePoint(const SwFltStackEntry& rEntry, SwDoc& rDoc,
446     SwPaM& rRegion)
447 {
448     // the anchor is the Pam's Point. It's modified when inserting
449     // text, etc.; therefore it is kept on the stack. Only the
450     // attribute's format needs to be set.
451     rRegion.DeleteMark();
452 
453     sal_uLong nMk = rEntry.m_aMkPos.m_nNode.GetIndex() + 1;
454     const SwNodes& rMkNodes = rEntry.m_aMkPos.m_nNode.GetNodes();
455     if (nMk >= rMkNodes.Count())
456         return false;
457 
458     rRegion.GetPoint()->nNode = nMk;
459     SwContentNode* pCNd = GetContentNode(rDoc, rRegion.GetPoint()->nNode, true);
460     rRegion.GetPoint()->nContent.Assign(pCNd, rEntry.m_aMkPos.m_nContent);
461     return true;
462 }
463 
464 // MakeBookRegionOrPoint() behaves like MakeRegionOrPoint, except that
465 // it adheres to certain restrictions on bookmarks in tables (cannot
466 // span more than one cell)
MakeBookRegionOrPoint(const SwFltStackEntry & rEntry,SwDoc & rDoc,SwPaM & rRegion)467 static bool MakeBookRegionOrPoint(const SwFltStackEntry& rEntry, SwDoc& rDoc,
468                     SwPaM& rRegion )
469 {
470     if (rEntry.MakeRegion(rDoc, rRegion, SwFltStackEntry::RegionMode::CheckNodes))
471     {
472         if (rRegion.GetPoint()->nNode.GetNode().FindTableBoxStartNode()
473               != rRegion.GetMark()->nNode.GetNode().FindTableBoxStartNode())
474         {
475             rRegion.Exchange();         // invalid range
476             rRegion.DeleteMark();       // -> both to mark
477         }
478         return true;
479     }
480     return MakePoint(rEntry, rDoc, rRegion);
481 }
482 
483 // IterateNumrulePiece() looks for the first range valid for Numrules
484 // between rTmpStart and rEnd.
485 
486 // rNds denotes the doc nodes
487 // rEnd denotes the range end,
488 // rTmpStart is an in/out parameter: in: start of range to be searched,
489 //                                   out: start of valid range
490 // rTmpEnd is an out parameter
491 // Returns true for valid range
IterateNumrulePiece(const SwNodeIndex & rEnd,SwNodeIndex & rTmpStart,SwNodeIndex & rTmpEnd)492 static bool IterateNumrulePiece( const SwNodeIndex& rEnd,
493                                 SwNodeIndex& rTmpStart, SwNodeIndex& rTmpEnd )
494 {
495     while( ( rTmpStart <= rEnd )
496            && !( rTmpStart.GetNode().IsTextNode() ) )    // look for valid start
497         ++rTmpStart;
498 
499     rTmpEnd = rTmpStart;
500     while( ( rTmpEnd <= rEnd )
501            && ( rTmpEnd.GetNode().IsTextNode() ) )       // look for valid end + 1
502         ++rTmpEnd;
503 
504     --rTmpEnd;                                      // valid end
505 
506     return rTmpStart <= rTmpEnd;                    // valid ?
507 }
508 
509 //***This function will check whether there is existing individual attribute position for 0x0D***/
510 //The check will happen only once for a paragraph during loading
HasSdOD()511 bool SwFltControlStack::HasSdOD()
512 {
513     bool bRet = false;
514 
515     for (auto const& it : m_Entries)
516     {
517         SwFltStackEntry& rEntry = *it;
518         if ( rEntry.mnStartCP == rEntry.mnEndCP )
519         {
520             if ( CheckSdOD(rEntry.mnStartCP,rEntry.mnEndCP) )
521             {
522                 bRet = true;
523                 break;
524             }
525         }
526     }
527 
528     return bRet;
529 }
530 
SetAttrInDoc(const SwPosition & rTmpPos,SwFltStackEntry & rEntry)531 void SwFltControlStack::SetAttrInDoc(const SwPosition& rTmpPos,
532     SwFltStackEntry& rEntry)
533 {
534     SwPaM aRegion( rTmpPos );
535 
536     switch(rEntry.m_pAttr->Which())
537     {
538     case RES_FLTR_ANCHOR:
539         {
540             SwFrameFormat* pFormat = static_cast<SwFltAnchor*>(rEntry.m_pAttr.get())->GetFrameFormat();
541             if (pFormat != nullptr)
542             {
543                 MakePoint(rEntry, m_rDoc, aRegion);
544                 SwFormatAnchor aAnchor(pFormat->GetAnchor());
545                 aAnchor.SetAnchor(aRegion.GetPoint());
546                 pFormat->SetFormatAttr(aAnchor);
547                 // So the frames will be created when inserting into
548                 // existing doc (after setting the anchor!):
549                 if (m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()
550                    && (RndStdIds::FLY_AT_PARA == pFormat->GetAnchor().GetAnchorId()))
551                 {
552                     pFormat->MakeFrames();
553                 }
554             }
555         }
556         break;
557 
558     case RES_TXTATR_FIELD:
559     case RES_TXTATR_ANNOTATION:
560     case RES_TXTATR_INPUTFIELD:
561         break;
562 
563     case RES_TXTATR_TOXMARK:
564         break;
565 
566     case RES_FLTR_NUMRULE:          // insert Numrule
567         {
568             const OUString& rNumNm = static_cast<SfxStringItem*>(rEntry.m_pAttr.get())->GetValue();
569             SwNumRule* pNumRule = m_rDoc.FindNumRulePtr( rNumNm );
570             if( pNumRule )
571             {
572                 if (rEntry.MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::CheckNodes))
573                 {
574                     SwNodeIndex aTmpStart( aRegion.Start()->nNode );
575                     SwNodeIndex aTmpEnd( aTmpStart );
576                     SwNodeIndex& rRegEndNd = aRegion.End()->nNode;
577                     while( IterateNumrulePiece( rRegEndNd,
578                                                 aTmpStart, aTmpEnd ) )
579                     {
580                         SwPaM aTmpPam( aTmpStart, aTmpEnd );
581                         // no start of a new list
582                         m_rDoc.SetNumRule( aTmpPam, *pNumRule, false );
583 
584                         aTmpStart = aTmpEnd;    // here starts the next range
585                         ++aTmpStart;
586                     }
587                 }
588                 else
589                     m_rDoc.DelNumRule( rNumNm );
590             }
591         }
592         break;
593 
594     case RES_FLTR_BOOKMARK:
595         {
596             SwFltBookmark* pB = static_cast<SwFltBookmark*>(rEntry.m_pAttr.get());
597             const OUString& rName = static_cast<SwFltBookmark*>(rEntry.m_pAttr.get())->GetName();
598 
599             if (IsFlagSet(BOOK_TO_VAR_REF))
600             {
601                 SwFieldType* pFT = m_rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rName, false);
602                 if (!pFT)
603                 {
604                     SwSetExpFieldType aS(&m_rDoc, rName, nsSwGetSetExpType::GSE_STRING);
605                     pFT = m_rDoc.getIDocumentFieldsAccess().InsertFieldType(aS);
606                 }
607                 SwSetExpField aField(static_cast<SwSetExpFieldType*>(pFT), pB->GetValSys());
608                 aField.SetSubType( nsSwExtendedSubType::SUB_INVISIBLE );
609                 MakePoint(rEntry, m_rDoc, aRegion);
610                 m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, SwFormatField(aField));
611                 MoveAttrs( *(aRegion.GetPoint()) );
612             }
613             if ( ( !IsFlagSet(HYPO) || IsFlagSet(BOOK_AND_REF) ) &&
614                  !rEntry.m_bConsumedByField )
615             {
616                 MakeBookRegionOrPoint(rEntry, m_rDoc, aRegion);
617                 // #i120879# - create a cross reference heading bookmark if appropriate.
618                 const IDocumentMarkAccess::MarkType eBookmarkType =
619                     ( pB->IsTOCBookmark() &&
620                       IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( aRegion ) )
621                     ? IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
622                     : IDocumentMarkAccess::MarkType::BOOKMARK;
623                 m_rDoc.getIDocumentMarkAccess()->makeMark(aRegion, rName, eBookmarkType, sw::mark::InsertMode::New);
624             }
625         }
626         break;
627     case RES_FLTR_ANNOTATIONMARK:
628         {
629             if (MakeBookRegionOrPoint(rEntry, m_rDoc, aRegion))
630             {
631                 SwTextNode const*const pTextNode(
632                         aRegion.End()->nNode.GetNode().GetTextNode());
633                 SwTextField const*const pField = pTextNode ? pTextNode->GetFieldTextAttrAt(
634                         aRegion.End()->nContent.GetIndex() - 1, true) : nullptr;
635                 if (pField)
636                 {
637                     SwPostItField const*const pPostIt(
638                         dynamic_cast<SwPostItField const*>(pField->GetFormatField().GetField()));
639                     if (pPostIt)
640                     {
641                         assert(pPostIt->GetName().isEmpty());
642 
643                         if (!aRegion.HasMark())
644                         {
645                             // Annotation range was found in the file, but start/end is the same,
646                             // pointing after the postit placeholder (see assert above).
647                             // Adjust the start of the range to actually cover the comment, similar
648                             // to what the UI and the UNO API does.
649                             aRegion.SetMark();
650                             --aRegion.Start()->nContent;
651                         }
652 
653                         m_rDoc.getIDocumentMarkAccess()->makeAnnotationMark(aRegion, OUString());
654                     }
655                     else
656                     {
657                         SAL_WARN("sw", "RES_FLTR_ANNOTATIONMARK: unexpected field");
658                     }
659                 }
660                 else
661                 {
662                     SAL_WARN("sw", "RES_FLTR_ANNOTATIONMARK: missing field");
663                 }
664             }
665             else
666                 SAL_WARN("sw", "failed to make book region or point");
667         }
668         break;
669     case RES_FLTR_RDFMARK:
670         {
671             if (MakeBookRegionOrPoint(rEntry, m_rDoc, aRegion))
672             {
673                 SwFltRDFMark* pMark = static_cast<SwFltRDFMark*>(rEntry.m_pAttr.get());
674                 if (aRegion.GetNode().IsTextNode())
675                 {
676                     SwTextNode& rTextNode = *aRegion.GetNode().GetTextNode();
677 
678                     for (const std::pair<OUString, OUString>& rAttribute : pMark->GetAttributes())
679                     {
680                         OUString aTypeNS = rAttribute.first;
681                         OUString aMetadataFilePath = lcl_getTypePath(aTypeNS);
682                         if (aMetadataFilePath.isEmpty())
683                             continue;
684 
685                         SwRDFHelper::addTextNodeStatement(aTypeNS, aMetadataFilePath, rTextNode, rAttribute.first, rAttribute.second);
686                     }
687                 }
688             }
689             else
690                 SAL_WARN("sw", "failed to make book region or point");
691         }
692         break;
693     case RES_FLTR_TOX:
694         {
695             MakePoint(rEntry, m_rDoc, aRegion);
696 
697             SwPosition* pPoint = aRegion.GetPoint();
698 
699             SwFltTOX* pTOXAttr = static_cast<SwFltTOX*>(rEntry.m_pAttr.get());
700 
701             // test if on this node there had been a pagebreak BEFORE the
702             //     tox attribute was put on the stack
703             SfxItemSet aBkSet( m_rDoc.GetAttrPool(), svl::Items<RES_PAGEDESC, RES_BREAK>{} );
704             SwContentNode* pNd = nullptr;
705             if( !pTOXAttr->HadBreakItem() || !pTOXAttr->HadPageDescItem() )
706             {
707                 pNd = pPoint->nNode.GetNode().GetContentNode();
708                 if( pNd )
709                 {
710                     const SfxItemSet* pSet = pNd->GetpSwAttrSet();
711                     const SfxPoolItem* pItem;
712                     if( pSet )
713                     {
714                         if(    !pTOXAttr->HadBreakItem()
715                             && SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem ) )
716                         {
717                             aBkSet.Put( *pItem );
718                             pNd->ResetAttr( RES_BREAK );
719                         }
720                         if(    !pTOXAttr->HadPageDescItem()
721                             && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem ) )
722                         {
723                             aBkSet.Put( *pItem );
724                             pNd->ResetAttr( RES_PAGEDESC );
725                         }
726                     }
727                 }
728             }
729 
730             // set (above saved and removed) the break item at the node following the TOX
731             if (pNd && aBkSet.Count())
732                 pNd->SetAttr(aBkSet);
733         }
734         break;
735     case RES_FLTR_REDLINE:
736         {
737             if (rEntry.MakeRegion(m_rDoc, aRegion,
738                     SwFltStackEntry::RegionMode::CheckNodes|SwFltStackEntry::RegionMode::CheckFieldmark))
739             {
740                 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On
741                                               | RedlineFlags::ShowInsert
742                                               | RedlineFlags::ShowDelete );
743                 SwFltRedline& rFltRedline = *static_cast<SwFltRedline*>(rEntry.m_pAttr.get());
744 
745                 SwRedlineData aData(rFltRedline.m_eType,
746                                     rFltRedline.m_nAutorNo,
747                                     rFltRedline.m_aStamp,
748                                     OUString(),
749                                     nullptr
750                                     );
751                 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(aData, aRegion), true );
752                 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::NONE
753                                                 | RedlineFlags::ShowInsert
754                                                 | RedlineFlags::ShowDelete );
755             }
756         }
757         break;
758     default:
759         {
760             // Revised for more complex situations should be considered
761             if ( !m_bSdODChecked )
762             {
763                 m_bHasSdOD = HasSdOD();
764                 m_bSdODChecked = true;
765             }
766             sal_Int32 nStart = rEntry.GetStartCP();
767             sal_Int32 nEnd = rEntry.GetEndCP();
768             if (nStart != -1 && nEnd != -1 && nEnd >= nStart )
769             {
770                 rEntry.SetIsParaEnd( IsParaEndInCPs(nStart,nEnd,m_bHasSdOD) );
771             }
772             if (rEntry.MakeRegion(m_rDoc, aRegion, SwFltStackEntry::RegionMode::NoCheck))
773             {
774                 if (rEntry.IsParaEnd())
775                 {
776                     m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.m_pAttr, SetAttrMode::DEFAULT, nullptr, true);
777                 }
778                 else
779                 {
780                     m_rDoc.getIDocumentContentOperations().InsertPoolItem(aRegion, *rEntry.m_pAttr);
781                 }
782             }
783         }
784         break;
785     }
786 }
787 
IsParaEndInCPs(sal_Int32,sal_Int32,bool) const788 bool SwFltControlStack::IsParaEndInCPs(sal_Int32 /*nStart*/, sal_Int32 /*nEnd*/,bool /*bSdOD*/) const
789 {
790     return false;
791 }
792 
CheckSdOD(sal_Int32,sal_Int32)793 bool SwFltControlStack::CheckSdOD(sal_Int32 /*nStart*/, sal_Int32 /*nEnd*/)
794 {
795     return false;
796 }
797 
GetFormatStackAttr(sal_uInt16 nWhich,sal_uInt16 * pPos)798 SfxPoolItem* SwFltControlStack::GetFormatStackAttr(sal_uInt16 nWhich, sal_uInt16 * pPos)
799 {
800     size_t nSize = m_Entries.size();
801 
802     while (nSize)
803     {
804         // is it the looked-for attribute ? (only applies to locked, meaning
805         // currently set attributes!!)
806         SwFltStackEntry &rEntry = *m_Entries[--nSize];
807         if (rEntry.m_bOpen && rEntry.m_pAttr->Which() == nWhich)
808         {
809             if (pPos)
810                 *pPos = nSize;
811             return rEntry.m_pAttr.get();      // Ok, so stop
812         }
813     }
814     return nullptr;
815 }
816 
GetOpenStackAttr(const SwPosition & rPos,sal_uInt16 nWhich)817 const SfxPoolItem* SwFltControlStack::GetOpenStackAttr(const SwPosition& rPos, sal_uInt16 nWhich)
818 {
819     SwFltPosition aFltPos(rPos);
820 
821     size_t nSize = m_Entries.size();
822 
823     while (nSize)
824     {
825         SwFltStackEntry &rEntry = *m_Entries[--nSize];
826         if (rEntry.m_bOpen && rEntry.m_pAttr->Which() == nWhich && rEntry.m_aMkPos == aFltPos)
827         {
828             return rEntry.m_pAttr.get();
829         }
830     }
831     return nullptr;
832 }
833 
Delete(const SwPaM & rPam)834 void SwFltControlStack::Delete(const SwPaM &rPam)
835 {
836     const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
837 
838     if( !rPam.HasMark() || *pStt >= *pEnd )
839         return;
840 
841     SwNodeIndex aStartNode(pStt->nNode, -1);
842     const sal_Int32 nStartIdx = pStt->nContent.GetIndex();
843     SwNodeIndex aEndNode(pEnd->nNode, -1);
844     const sal_Int32 nEndIdx = pEnd->nContent.GetIndex();
845 
846     // We don't support deleting content that is over one node, or removing a node.
847     OSL_ENSURE(aEndNode == aStartNode, "nodes must be the same, or this method extended");
848     if (aEndNode != aStartNode)
849         return;
850 
851     for (size_t nSize = m_Entries.size(); nSize > 0;)
852     {
853         SwFltStackEntry& rEntry = *m_Entries[--nSize];
854 
855         bool bEntryStartAfterSelStart =
856             (rEntry.m_aMkPos.m_nNode == aStartNode &&
857              rEntry.m_aMkPos.m_nContent >= nStartIdx);
858 
859         bool bEntryStartBeforeSelEnd =
860             (rEntry.m_aMkPos.m_nNode == aEndNode &&
861              rEntry.m_aMkPos.m_nContent <= nEndIdx);
862 
863         bool bEntryEndAfterSelStart = false;
864         bool bEntryEndBeforeSelEnd = false;
865         if (!rEntry.m_bOpen)
866         {
867             bEntryEndAfterSelStart =
868                 (rEntry.m_aPtPos.m_nNode == aStartNode &&
869                  rEntry.m_aPtPos.m_nContent >= nStartIdx);
870 
871             bEntryEndBeforeSelEnd =
872                 (rEntry.m_aPtPos.m_nNode == aEndNode &&
873                  rEntry.m_aPtPos.m_nContent <= nEndIdx);
874         }
875 
876         bool bTotallyContained = false;
877         if (
878              bEntryStartAfterSelStart && bEntryStartBeforeSelEnd &&
879              bEntryEndAfterSelStart && bEntryEndBeforeSelEnd
880            )
881         {
882            bTotallyContained = true;
883         }
884 
885         if (bTotallyContained)
886         {
887             // after start, before end, delete
888             DeleteAndDestroy(nSize);
889             continue;
890         }
891 
892         const sal_Int32 nContentDiff = nEndIdx - nStartIdx;
893 
894         // to be adjusted
895         if (bEntryStartAfterSelStart)
896         {
897             if (bEntryStartBeforeSelEnd)
898             {
899                 // move start to new start
900                 rEntry.m_aMkPos.SetPos(aStartNode, nStartIdx);
901             }
902             else
903                 rEntry.m_aMkPos.m_nContent -= nContentDiff;
904         }
905 
906         if (bEntryEndAfterSelStart)
907         {
908             if (bEntryEndBeforeSelEnd)
909                 rEntry.m_aPtPos.SetPos(aStartNode, nStartIdx);
910             else
911                 rEntry.m_aPtPos.m_nContent -= nContentDiff;
912         }
913 
914         //That's what Open is, end equal to start, and nPtContent is invalid
915         if (rEntry.m_bOpen)
916             rEntry.m_aPtPos = rEntry.m_aMkPos;
917     }
918 }
919 
920 // methods of SwFltAnchor follow
SwFltAnchor(SwFrameFormat * pFormat)921 SwFltAnchor::SwFltAnchor(SwFrameFormat* pFormat) :
922     SfxPoolItem(RES_FLTR_ANCHOR), m_pFrameFormat(pFormat)
923 {
924     m_pListener.reset(new SwFltAnchorListener(this));
925     m_pListener->StartListening(m_pFrameFormat->GetNotifier());
926 }
927 
SwFltAnchor(const SwFltAnchor & rCpy)928 SwFltAnchor::SwFltAnchor(const SwFltAnchor& rCpy) :
929     SfxPoolItem(RES_FLTR_ANCHOR), m_pFrameFormat(rCpy.m_pFrameFormat)
930 {
931     m_pListener.reset(new SwFltAnchorListener(this));
932     m_pListener->StartListening(m_pFrameFormat->GetNotifier());
933 }
934 
~SwFltAnchor()935 SwFltAnchor::~SwFltAnchor()
936 {
937 }
938 
SetFrameFormat(SwFrameFormat * _pFrameFormat)939 void SwFltAnchor::SetFrameFormat(SwFrameFormat * _pFrameFormat)
940 {
941     m_pFrameFormat = _pFrameFormat;
942 }
943 
944 
operator ==(const SfxPoolItem & rItem) const945 bool SwFltAnchor::operator==(const SfxPoolItem& rItem) const
946 {
947     return SfxPoolItem::operator==(rItem) &&
948         m_pFrameFormat == static_cast<const SwFltAnchor&>(rItem).m_pFrameFormat;
949 }
950 
Clone(SfxItemPool *) const951 SwFltAnchor* SwFltAnchor::Clone(SfxItemPool*) const
952 {
953     return new SwFltAnchor(*this);
954 }
955 
SwFltAnchorListener(SwFltAnchor * pFltAnchor)956 SwFltAnchorListener::SwFltAnchorListener(SwFltAnchor* pFltAnchor)
957     : m_pFltAnchor(pFltAnchor)
958 { }
959 
Notify(const SfxHint & rHint)960 void SwFltAnchorListener::Notify(const SfxHint& rHint)
961 {
962     if (rHint.GetId() == SfxHintId::Dying)
963         m_pFltAnchor->SetFrameFormat(nullptr);
964     else if (auto pDrawFrameFormatHint = dynamic_cast<const sw::DrawFrameFormatHint*>(&rHint))
965     {
966         if (pDrawFrameFormatHint->m_eId != sw::DrawFrameFormatHintId::DYING)
967             return;
968         m_pFltAnchor->SetFrameFormat(nullptr);
969     }
970     else if (rHint.GetId() == SfxHintId::SwLegacyModify)
971     {
972         auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
973         if(pLegacyHint->m_pNew->Which() != RES_FMT_CHG)
974             return;
975         auto pFormatChg = dynamic_cast<const SwFormatChg*>(pLegacyHint->m_pNew);
976         auto pFrameFormat = pFormatChg ? dynamic_cast<SwFrameFormat*>(pFormatChg->pChangedFormat) : nullptr;
977         if(pFrameFormat)
978             m_pFltAnchor->SetFrameFormat(pFrameFormat);
979     }
980 }
981 
982 // methods of SwFltRedline follow
operator ==(const SfxPoolItem & rItem) const983 bool SwFltRedline::operator==(const SfxPoolItem& rItem) const
984 {
985     return SfxPoolItem::operator==(rItem) &&
986         this == &rItem;
987 }
988 
Clone(SfxItemPool *) const989 SwFltRedline* SwFltRedline::Clone( SfxItemPool* ) const
990 {
991     return new SwFltRedline(*this);
992 }
993 
994 // methods of SwFltBookmark follow
SwFltBookmark(const OUString & rNa,const OUString & rVa,tools::Long nHand,const bool bIsTOCBookmark)995 SwFltBookmark::SwFltBookmark( const OUString& rNa, const OUString& rVa,
996                               tools::Long nHand, const bool bIsTOCBookmark )
997     : SfxPoolItem( RES_FLTR_BOOKMARK )
998     , mnHandle( nHand )
999     , maName( rNa )
1000     , maVal( rVa )
1001     , mbIsTOCBookmark( bIsTOCBookmark )
1002 {
1003     // eSrc: CHARSET_DONTKNOW for no transform at operator <<
1004     // Upcase is always done.
1005     // Transform is never done at XXXStack.NewAttr(...).
1006     // otherwise: Src Charset from argument for aName
1007     // Src Charset from filter for aVal ( Text )
1008 
1009     if ( IsTOCBookmark() && ! rNa.startsWith(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()) )
1010     {
1011         maName = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix();
1012         maName += rNa;
1013     }
1014 }
1015 
operator ==(const SfxPoolItem & rItem) const1016 bool SwFltBookmark::operator==(const SfxPoolItem& rItem) const
1017 {
1018     return SfxPoolItem::operator==(rItem)
1019         && maName == static_cast<const SwFltBookmark&>(rItem).maName
1020         && mnHandle == static_cast<const SwFltBookmark&>(rItem).mnHandle;
1021 }
1022 
Clone(SfxItemPool *) const1023 SwFltBookmark* SwFltBookmark::Clone(SfxItemPool*) const
1024 {
1025     return new SwFltBookmark(*this);
1026 }
1027 
SwFltRDFMark()1028 SwFltRDFMark::SwFltRDFMark()
1029     : SfxPoolItem(RES_FLTR_RDFMARK),
1030       m_nHandle(0)
1031 {
1032 }
1033 
operator ==(const SfxPoolItem & rItem) const1034 bool SwFltRDFMark::operator==(const SfxPoolItem& rItem) const
1035 {
1036     if (!SfxPoolItem::operator==(rItem))
1037         return false;
1038 
1039     const SwFltRDFMark& rMark = static_cast<const SwFltRDFMark&>(rItem);
1040 
1041     return m_nHandle == rMark.m_nHandle && m_aAttributes == rMark.m_aAttributes;
1042 }
1043 
Clone(SfxItemPool *) const1044 SwFltRDFMark* SwFltRDFMark::Clone(SfxItemPool*) const
1045 {
1046     return new SwFltRDFMark(*this);
1047 }
1048 
SetHandle(tools::Long nHandle)1049 void SwFltRDFMark::SetHandle(tools::Long nHandle)
1050 {
1051     m_nHandle = nHandle;
1052 }
1053 
GetHandle() const1054 tools::Long SwFltRDFMark::GetHandle() const
1055 {
1056     return m_nHandle;
1057 }
1058 
SetAttributes(const std::vector<std::pair<OUString,OUString>> & rAttributes)1059 void SwFltRDFMark::SetAttributes(const std::vector< std::pair<OUString, OUString> >& rAttributes)
1060 {
1061     m_aAttributes = rAttributes;
1062 }
1063 
GetAttributes() const1064 const std::vector< std::pair<OUString, OUString> >& SwFltRDFMark::GetAttributes() const
1065 {
1066     return m_aAttributes;
1067 }
1068 
1069 // methods of SwFltTOX follow
SwFltTOX(std::shared_ptr<SwTOXBase> xBase)1070 SwFltTOX::SwFltTOX(std::shared_ptr<SwTOXBase> xBase)
1071     : SfxPoolItem(RES_FLTR_TOX), m_xTOXBase(std::move(xBase)),
1072       m_bHadBreakItem( false ), m_bHadPageDescItem( false )
1073 {
1074 }
1075 
operator ==(const SfxPoolItem & rItem) const1076 bool SwFltTOX::operator==(const SfxPoolItem& rItem) const
1077 {
1078     return SfxPoolItem::operator==(rItem) &&
1079         m_xTOXBase.get() == static_cast<const SwFltTOX&>(rItem).m_xTOXBase.get();
1080 }
1081 
Clone(SfxItemPool *) const1082 SwFltTOX* SwFltTOX::Clone(SfxItemPool*) const
1083 {
1084     return new SwFltTOX(*this);
1085 }
1086 
1087 // UpdatePageDescs needs to be called at end of parsing to make Writer actually
1088 // accept Pagedescs contents
UpdatePageDescs(SwDoc & rDoc,size_t nInPageDescOffset)1089 void UpdatePageDescs(SwDoc &rDoc, size_t nInPageDescOffset)
1090 {
1091     // Update document page descriptors (only this way also left pages
1092     // get adjusted)
1093 
1094     // PageDesc "Standard"
1095     rDoc.ChgPageDesc(0, rDoc.GetPageDesc(0));
1096 
1097     // PageDescs "Convert..."
1098     for (size_t i = nInPageDescOffset; i < rDoc.GetPageDescCnt(); ++i)
1099         rDoc.ChgPageDesc(i, rDoc.GetPageDesc(i));
1100 }
1101 
FrameDeleteWatch(SwFrameFormat * pFormat)1102 FrameDeleteWatch::FrameDeleteWatch(SwFrameFormat* pFormat)
1103     : m_pFormat(pFormat)
1104 {
1105     if(m_pFormat)
1106         StartListening(pFormat->GetNotifier());
1107 }
1108 
Notify(const SfxHint & rHint)1109 void FrameDeleteWatch::Notify(const SfxHint& rHint)
1110 {
1111     bool bDying = false;
1112     if (rHint.GetId() == SfxHintId::Dying)
1113         bDying = true;
1114     else if (auto pDrawFrameFormatHint = dynamic_cast<const sw::DrawFrameFormatHint*>(&rHint))
1115         bDying = pDrawFrameFormatHint->m_eId == sw::DrawFrameFormatHintId::DYING;
1116     if (bDying)
1117     {
1118         m_pFormat = nullptr;
1119         EndListeningAll();
1120     }
1121 }
1122 
~FrameDeleteWatch()1123 FrameDeleteWatch::~FrameDeleteWatch()
1124 {
1125     m_pFormat = nullptr;
1126     EndListeningAll();
1127 }
1128 
1129 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1130