xref: /core/sw/source/core/unocore/unotext.cxx (revision 14e542d58778963df461965f129286fc75459d3b)
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 <stdlib.h>
21 
22 #include <memory>
23 #include <set>
24 
25 #include <frozen/bits/defines.h>
26 #include <frozen/bits/elsa_std.h>
27 #include <frozen/unordered_set.h>
28 
29 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
30 #include <com/sun/star/text/ControlCharacter.hpp>
31 #include <com/sun/star/text/TableColumnSeparator.hpp>
32 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
33 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
34 
35 #include <svl/listener.hxx>
36 #include <vcl/svapp.hxx>
37 #include <comphelper/profilezone.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <comphelper/servicehelper.hxx>
40 #include <cppuhelper/exc_hlp.hxx>
41 #include <cppuhelper/supportsservice.hxx>
42 #include <sal/log.hxx>
43 #include <comphelper/diagnose_ex.hxx>
44 
45 #include <cmdid.h>
46 #include <unotextbodyhf.hxx>
47 #include <unotext.hxx>
48 #include <unotextrange.hxx>
49 #include <unotextcursor.hxx>
50 #include <unosection.hxx>
51 #include <unobookmark.hxx>
52 #include <unorefmark.hxx>
53 #include <unoport.hxx>
54 #include <unotbl.hxx>
55 #include <unoidx.hxx>
56 #include <unocoll.hxx>
57 #include <unoframe.hxx>
58 #include <unofield.hxx>
59 #include <unometa.hxx>
60 #include <unomap.hxx>
61 #include <unoprnms.hxx>
62 #include <unoparagraph.hxx>
63 #include <unocrsrhelper.hxx>
64 #include <docary.hxx>
65 #include <doc.hxx>
66 #include <IDocumentRedlineAccess.hxx>
67 #include <IDocumentUndoRedo.hxx>
68 #include <bookmark.hxx>
69 #include <redline.hxx>
70 #include <swundo.hxx>
71 #include <section.hxx>
72 #include <fmtanchr.hxx>
73 #include <fmtcntnt.hxx>
74 #include <ndtxt.hxx>
75 #include <SwRewriter.hxx>
76 #include <strings.hrc>
77 #include <frameformats.hxx>
78 #include <unocontentcontrol.hxx>
79 
80 using namespace ::com::sun::star;
81 
82 constexpr OUString cInvalidObject = u"this object is invalid"_ustr;
83 
SwXText(SwDoc * const pDoc,const CursorType eType)84 SwXText::SwXText(SwDoc *const pDoc, const CursorType eType)
85     : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT))
86     , m_eType(eType)
87     , m_pDoc(pDoc)
88     , m_bIsValid(nullptr != pDoc)
89 {
90 }
91 
~SwXText()92 SwXText::~SwXText()
93 {
94 }
95 
SetDoc(SwDoc * const pDoc)96 void SwXText::SetDoc(SwDoc *const pDoc)
97 {
98     OSL_ENSURE(!m_pDoc || !pDoc, "SwXText::SetDoc: already have a doc?");
99     m_pDoc = pDoc;
100     m_bIsValid = (nullptr != pDoc);
101 }
102 
103 void
PrepareForAttach(uno::Reference<text::XTextRange> &,const SwPaM &)104 SwXText::PrepareForAttach(uno::Reference< text::XTextRange > &, const SwPaM &)
105 {
106 }
107 
CheckForOwnMemberMeta(const SwPaM &,const bool)108 bool SwXText::CheckForOwnMemberMeta(const SwPaM &, const bool)
109 {
110     OSL_ENSURE(CursorType::Meta != m_eType, "should not be called!");
111     return false;
112 }
113 
GetStartNode() const114 const SwStartNode *SwXText::GetStartNode() const
115 {
116     return GetDoc()->GetNodes().GetEndOfContent().StartOfSectionNode();
117 }
118 
createTextCursor()119 uno::Reference< text::XTextCursor > SAL_CALL SwXText::createTextCursor()
120 {
121     SolarMutexGuard aGuard;
122     rtl::Reference<SwXTextCursor> xCursor = createXTextCursor();
123     if (!xCursor.is())
124         throw uno::RuntimeException(cInvalidObject);
125     return static_cast<text::XWordCursor*>(xCursor.get());
126 }
127 
128 rtl::Reference< SwXTextCursor >
createXTextCursor()129 SwXText::createXTextCursor()
130 {
131     rtl::Reference< SwXTextCursor > xRet;
132     if(IsValid())
133     {
134         SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
135         SwPosition aPos(rNode);
136         xRet = new SwXTextCursor(*GetDoc(), this, m_eType, aPos);
137         xRet->gotoStart(false);
138     }
139     return xRet;
140 }
141 
createTextCursorByRange(const::css::uno::Reference<::css::text::XTextRange> & aTextPosition)142 css::uno::Reference< css::text::XTextCursor > SAL_CALL SwXText::createTextCursorByRange(
143         const ::css::uno::Reference< ::css::text::XTextRange >& aTextPosition )
144 {
145     SolarMutexGuard aGuard;
146     return static_cast<text::XWordCursor*>(createXTextCursorByRange(aTextPosition).get());
147 }
148 
149 
150 uno::Any SAL_CALL
queryInterface(const uno::Type & rType)151 SwXText::queryInterface(const uno::Type& rType)
152 {
153     uno::Any aRet;
154     if (rType == cppu::UnoType<text::XText>::get())
155     {
156         aRet <<= uno::Reference< text::XText >(this);
157     }
158     else if (rType == cppu::UnoType<text::XSimpleText>::get())
159     {
160         aRet <<= uno::Reference< text::XSimpleText >(this);
161     }
162     else if (rType == cppu::UnoType<text::XTextRange>::get())
163     {
164         aRet <<= uno::Reference< text::XTextRange>(this);
165     }
166     else if (rType == cppu::UnoType<text::XTextRangeCompare>::get())
167     {
168         aRet <<= uno::Reference< text::XTextRangeCompare >(this);
169     }
170     else if (rType == cppu::UnoType<lang::XTypeProvider>::get())
171     {
172         aRet <<= uno::Reference< lang::XTypeProvider >(this);
173     }
174     else if (rType == cppu::UnoType<text::XRelativeTextContentInsert>::get())
175     {
176         aRet <<= uno::Reference< text::XRelativeTextContentInsert >(this);
177     }
178     else if (rType == cppu::UnoType<text::XRelativeTextContentRemove>::get())
179     {
180         aRet <<= uno::Reference< text::XRelativeTextContentRemove >(this);
181     }
182     else if (rType == cppu::UnoType<beans::XPropertySet>::get())
183     {
184         aRet <<= uno::Reference< beans::XPropertySet >(this);
185     }
186     else if (rType == cppu::UnoType<text::XTextAppendAndConvert>::get())
187     {
188         aRet <<= uno::Reference< text::XTextAppendAndConvert >(this);
189     }
190     else if (rType == cppu::UnoType<text::XTextAppend>::get())
191     {
192         aRet <<= uno::Reference< text::XTextAppend >(this);
193     }
194     else if (rType == cppu::UnoType<text::XTextPortionAppend>::get())
195     {
196         aRet <<= uno::Reference< text::XTextPortionAppend >(this);
197     }
198     else if (rType == cppu::UnoType<text::XParagraphAppend>::get())
199     {
200         aRet <<= uno::Reference< text::XParagraphAppend >(this);
201     }
202     else if (rType == cppu::UnoType<text::XTextConvert>::get() )
203     {
204         aRet <<= uno::Reference< text::XTextConvert >(this);
205     }
206     else if (rType == cppu::UnoType<text::XTextContentAppend>::get())
207     {
208         aRet <<= uno::Reference< text::XTextContentAppend >(this);
209     }
210     else if(rType == cppu::UnoType<text::XTextCopy>::get())
211     {
212         aRet <<= uno::Reference< text::XTextCopy >( this );
213     }
214     return aRet;
215 }
216 
217 uno::Sequence< uno::Type > SAL_CALL
getTypes()218 SwXText::getTypes()
219 {
220     static const uno::Sequence< uno::Type > aTypes {
221         cppu::UnoType<text::XText>::get(),
222         cppu::UnoType<text::XTextRangeCompare>::get(),
223         cppu::UnoType<text::XRelativeTextContentInsert>::get(),
224         cppu::UnoType<text::XRelativeTextContentRemove>::get(),
225         cppu::UnoType<lang::XUnoTunnel>::get(),
226         cppu::UnoType<beans::XPropertySet>::get(),
227         cppu::UnoType<text::XTextPortionAppend>::get(),
228         cppu::UnoType<text::XParagraphAppend>::get(),
229         cppu::UnoType<text::XTextContentAppend>::get(),
230         cppu::UnoType<text::XTextConvert>::get(),
231         cppu::UnoType<text::XTextAppend>::get(),
232         cppu::UnoType<text::XTextAppendAndConvert>::get()
233     };
234     return aTypes;
235 }
236 
237 // belongs the range in the text ? insert it then.
238 void SAL_CALL
insertString(const uno::Reference<text::XTextRange> & xTextRange,const OUString & rString,sal_Bool bAbsorb)239 SwXText::insertString(const uno::Reference< text::XTextRange >& xTextRange,
240     const OUString& rString, sal_Bool bAbsorb)
241 {
242     SolarMutexGuard aGuard;
243     comphelper::ProfileZone aZone("SwXText::insertString");
244 
245     if (!xTextRange.is())
246     {
247         throw uno::RuntimeException();
248     }
249     if (!GetDoc())
250     {
251         throw uno::RuntimeException();
252     }
253     SwXTextRange *const pRange = dynamic_cast<SwXTextRange*>(xTextRange.get());
254     OTextCursorHelper *const pCursor = dynamic_cast<OTextCursorHelper*>(xTextRange.get());
255     if ((!pRange  || &pRange ->GetDoc() != GetDoc()) &&
256         (!pCursor || pCursor->GetDoc() != GetDoc()))
257     {
258         throw uno::RuntimeException();
259     }
260 
261     const SwStartNode *const pOwnStartNode = GetStartNode();
262     SwPaM aPam(GetDoc()->GetNodes());
263     const SwPaM * pPam(nullptr);
264     if (pCursor)
265     {
266         pPam = pCursor->GetPaM();
267     }
268     else // pRange
269     {
270         if (pRange->GetPositions(aPam))
271         {
272             pPam = &aPam;
273         }
274     }
275     if (!pPam)
276     {
277         throw uno::RuntimeException();
278     }
279 
280     const SwStartNode* pTmp(pPam->GetPointNode().StartOfSectionNode());
281     while (pTmp && pTmp->IsSectionNode())
282     {
283         pTmp = pTmp->StartOfSectionNode();
284     }
285     if (!pOwnStartNode || (pOwnStartNode != pTmp))
286     {
287         throw uno::RuntimeException();
288     }
289 
290     bool bForceExpandHints( false );
291     if (CursorType::Meta == m_eType)
292     {
293         try
294         {
295             bForceExpandHints = CheckForOwnMemberMeta(*pPam, bAbsorb);
296         }
297         catch (const lang::IllegalArgumentException& iae)
298         {
299             // stupid method not allowed to throw iae
300             css::uno::Any anyEx = cppu::getCaughtException();
301             throw lang::WrappedTargetRuntimeException( iae.Message,
302                             uno::Reference< uno::XInterface >(), anyEx );
303         }
304     }
305     if (bAbsorb)
306     {
307         //!! scan for CR characters and inserting the paragraph breaks
308         //!! has to be done in the called function.
309         //!! Implemented in SwXTextRange::DeleteAndInsert
310         if (pCursor)
311         {
312             SwXTextCursor * const pTextCursor(
313                 dynamic_cast<SwXTextCursor*>(pCursor) );
314             if (pTextCursor)
315             {
316                 pTextCursor->DeleteAndInsert(rString, ::sw::DeleteAndInsertMode::ForceReplace
317                     | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default));
318             }
319             else
320             {
321                 xTextRange->setString(rString);
322             }
323         }
324         else
325         {
326             pRange->DeleteAndInsert(rString, ::sw::DeleteAndInsertMode::ForceReplace
327                 | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default));
328         }
329     }
330     else
331     {
332         // create a PaM positioned before the parameter PaM,
333         // so the text is inserted before
334         UnoActionContext aContext(GetDoc());
335         SwPaM aInsertPam(*pPam->Start());
336         ::sw::GroupUndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo());
337         SwUnoCursorHelper::DocInsertStringSplitCR(
338             *GetDoc(), aInsertPam, rString, bForceExpandHints );
339     }
340 }
341 
342 void SAL_CALL
insertControlCharacter(const uno::Reference<text::XTextRange> & xTextRange,sal_Int16 nControlCharacter,sal_Bool bAbsorb)343 SwXText::insertControlCharacter(
344         const uno::Reference< text::XTextRange > & xTextRange,
345         sal_Int16 nControlCharacter, sal_Bool bAbsorb)
346 {
347     SolarMutexGuard aGuard;
348 
349     if (!xTextRange.is())
350     {
351         throw lang::IllegalArgumentException();
352     }
353     if (!GetDoc())
354     {
355         throw uno::RuntimeException();
356     }
357 
358     SwUnoInternalPaM aPam(*GetDoc());
359     if (!::sw::XTextRangeToSwPaM(aPam, xTextRange))
360     {
361         throw uno::RuntimeException();
362     }
363     const bool bForceExpandHints(CheckForOwnMemberMeta(aPam, bAbsorb));
364 
365     const SwInsertFlags nInsertFlags =
366         bForceExpandHints
367         ? ( SwInsertFlags::FORCEHINTEXPAND | SwInsertFlags::EMPTYEXPAND)
368         : SwInsertFlags::EMPTYEXPAND;
369 
370     if (bAbsorb && aPam.HasMark())
371     {
372         m_pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
373         aPam.DeleteMark();
374     }
375 
376     sal_Unicode cIns = 0;
377     switch (nControlCharacter)
378     {
379         case text::ControlCharacter::PARAGRAPH_BREAK :
380             // a table cell now becomes an ordinary text cell!
381             m_pDoc->ClearBoxNumAttrs(aPam.GetPoint()->GetNode());
382             m_pDoc->getIDocumentContentOperations().SplitNode(*aPam.GetPoint(), false);
383             break;
384         case text::ControlCharacter::APPEND_PARAGRAPH:
385         {
386             m_pDoc->ClearBoxNumAttrs(aPam.GetPoint()->GetNode());
387             m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPam.GetPoint());
388 
389             SwXTextRange *const pRange =
390                 dynamic_cast<SwXTextRange*>(xTextRange.get());
391             OTextCursorHelper *const pCursor =
392                 dynamic_cast<OTextCursorHelper*>(xTextRange.get());
393             if (pRange)
394             {
395                 pRange->SetPositions(aPam);
396             }
397             else if (pCursor)
398             {
399                 SwPaM *const pCursorPam = pCursor->GetPaM();
400                 *pCursorPam->GetPoint() = *aPam.GetPoint();
401                 pCursorPam->DeleteMark();
402             }
403         }
404         break;
405         case text::ControlCharacter::LINE_BREAK:  cIns = 10;              break;
406         case text::ControlCharacter::SOFT_HYPHEN: cIns = CHAR_SOFTHYPHEN; break;
407         case text::ControlCharacter::HARD_HYPHEN: cIns = CHAR_HARDHYPHEN; break;
408         case text::ControlCharacter::HARD_SPACE:  cIns = CHAR_HARDBLANK;  break;
409     }
410     if (cIns)
411     {
412         m_pDoc->getIDocumentContentOperations().InsertString(
413                 aPam, OUString(cIns), nInsertFlags);
414     }
415 
416     if (!bAbsorb)
417         return;
418 
419     SwXTextRange *const pRange =
420         dynamic_cast<SwXTextRange*>(xTextRange.get());
421     OTextCursorHelper *const pCursor =
422         dynamic_cast<OTextCursorHelper*>(xTextRange.get());
423 
424     SwCursor aCursor(*aPam.GetPoint(), nullptr);
425     SwUnoCursorHelper::SelectPam(aCursor, true);
426     aCursor.Left(1);
427     // here, the PaM needs to be moved:
428     if (pRange)
429     {
430         pRange->SetPositions(aCursor);
431     }
432     else if (pCursor)
433     {
434         SwPaM *const pUnoCursor = pCursor->GetPaM();
435         *pUnoCursor->GetPoint() = *aCursor.GetPoint();
436         if (aCursor.HasMark())
437         {
438             pUnoCursor->SetMark();
439             *pUnoCursor->GetMark() = *aCursor.GetMark();
440         }
441         else
442         {
443             pUnoCursor->DeleteMark();
444         }
445     }
446 }
447 
448 void SAL_CALL
insertTextContent(const uno::Reference<text::XTextRange> & xRange,const uno::Reference<text::XTextContent> & xContent,sal_Bool bAbsorb)449 SwXText::insertTextContent(
450         const uno::Reference< text::XTextRange > & xRange,
451         const uno::Reference< text::XTextContent > & xContent,
452         sal_Bool bAbsorb)
453 {
454     SolarMutexGuard aGuard;
455     comphelper::ProfileZone aZone("SwXText::insertTextContent");
456 
457     if (!xRange.is())
458         throw lang::IllegalArgumentException(u"first parameter invalid"_ustr, nullptr, 0);
459     if (!xContent.is())
460         throw lang::IllegalArgumentException(u"second parameter invalid"_ustr, nullptr, 1);
461     if (!GetDoc())
462         throw uno::RuntimeException(cInvalidObject);
463 
464     SwUnoInternalPaM aPam(*GetDoc());
465     if (!::sw::XTextRangeToSwPaM(aPam, xRange))
466         throw lang::IllegalArgumentException(u"first parameter invalid"_ustr, nullptr, 0);
467 
468     // first test if the range is at the right position, then call
469     // xContent->attach
470     const SwStartNode* pOwnStartNode = GetStartNode();
471     SwStartNodeType eSearchNodeType = SwNormalStartNode;
472     switch (m_eType)
473     {
474         case CursorType::Frame:      eSearchNodeType = SwFlyStartNode;       break;
475         case CursorType::TableText:    eSearchNodeType = SwTableBoxStartNode;  break;
476         case CursorType::Footnote:   eSearchNodeType = SwFootnoteStartNode;  break;
477         case CursorType::Header:     eSearchNodeType = SwHeaderStartNode;    break;
478         case CursorType::Footer:     eSearchNodeType = SwFooterStartNode;    break;
479         //case CURSOR_INVALID:
480         //case CursorType::Body:
481         default:
482             break;
483     }
484 
485     const SwStartNode* pTmp =
486         aPam.GetPointNode().FindStartNodeByType(eSearchNodeType);
487 
488     // ignore SectionNodes
489     while (pTmp && pTmp->IsSectionNode())
490     {
491         pTmp = pTmp->StartOfSectionNode();
492     }
493     // if the document starts with a section
494     while (pOwnStartNode && pOwnStartNode->IsSectionNode())
495     {
496         pOwnStartNode = pOwnStartNode->StartOfSectionNode();
497     }
498     // this checks if (this) and xRange are in the same text::XText interface
499     if (pOwnStartNode != pTmp)
500         throw uno::RuntimeException(u"text interface and cursor not related"_ustr);
501 
502     const bool bForceExpandHints(CheckForOwnMemberMeta(aPam, bAbsorb));
503 
504     // special treatment for Contents that do not replace the range, but
505     // instead are "overlaid"
506     SwXDocumentIndexMark *const pDocumentIndexMark =
507         dynamic_cast<SwXDocumentIndexMark*>(xContent.get());
508     SwXTextSection *const pSection =
509         dynamic_cast<SwXTextSection*>(xContent.get());
510     SwXBookmark *const pBookmark =
511         dynamic_cast<SwXBookmark*>(xContent.get());
512     SwXReferenceMark *const pReferenceMark =
513         dynamic_cast<SwXReferenceMark*>(xContent.get());
514     SwXMeta *const pMeta = dynamic_cast<SwXMeta*>(xContent.get());
515     auto* pContentControl = dynamic_cast<SwXContentControl*>(xContent.get());
516     SwXTextField* pTextField = dynamic_cast<SwXTextField*>(xContent.get());
517     if (pTextField && pTextField->GetServiceId() != SwServiceType::FieldTypeAnnotation)
518         pTextField = nullptr;
519 
520     const bool bAttribute = pBookmark || pDocumentIndexMark
521         || pSection || pReferenceMark || pMeta || pContentControl || pTextField;
522 
523     if (bAbsorb && !bAttribute)
524     {
525         if (SwXTextRange *const pRange = dynamic_cast<SwXTextRange*>(xRange.get()))
526         {
527             pRange->DeleteAndInsert(u"", ::sw::DeleteAndInsertMode::ForceReplace
528                 | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default));
529         }
530         else if (SwXTextCursor *const pCursor = dynamic_cast<SwXTextCursor*>(dynamic_cast<OTextCursorHelper*>(xRange.get())))
531         {
532             pCursor->DeleteAndInsert(u"", ::sw::DeleteAndInsertMode::ForceReplace
533                 | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default));
534         }
535         else
536         {
537             xRange->setString(OUString());
538         }
539     }
540     uno::Reference< text::XTextRange > xTempRange =
541         (bAttribute && bAbsorb) ? xRange : xRange->getStart();
542     if (bForceExpandHints)
543     {
544         // if necessary, replace xTempRange with a new SwXTextCursor
545         PrepareForAttach(xTempRange, aPam);
546     }
547     xContent->attach(xTempRange);
548 }
549 
550 void SAL_CALL
insertTextContentBefore(const uno::Reference<text::XTextContent> & xNewContent,const uno::Reference<text::XTextContent> & xSuccessor)551 SwXText::insertTextContentBefore(
552     const uno::Reference< text::XTextContent>& xNewContent,
553     const uno::Reference< text::XTextContent>& xSuccessor)
554 {
555     SolarMutexGuard aGuard;
556 
557     if(!GetDoc())
558         throw uno::RuntimeException(cInvalidObject);
559 
560     SwXParagraph *const pPara = dynamic_cast<SwXParagraph*>(xNewContent.get());
561     if (!pPara || !pPara->IsDescriptor() || !xSuccessor.is())
562     {
563         throw lang::IllegalArgumentException();
564     }
565 
566     bool bRet = false;
567     SwXTextSection *const pXSection = dynamic_cast<SwXTextSection*>(xSuccessor.get());
568     SwXTextTable *const pXTable = dynamic_cast<SwXTextTable*>(xSuccessor.get());
569     SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
570     SwTextNode * pTextNode = nullptr;
571     if(pTableFormat && &pTableFormat->GetDoc() == GetDoc())
572     {
573         SwTable *const pTable = SwTable::FindTable( pTableFormat );
574         SwTableNode *const pTableNode = pTable->GetTableNode();
575 
576         const SwNodeIndex aTableIdx( *pTableNode, -1 );
577         SwPosition aBefore(aTableIdx);
578         bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
579         pTextNode = aBefore.GetNode().GetTextNode();
580     }
581     else if (pXSection && pXSection->GetFormat() &&
582             &pXSection->GetFormat()->GetDoc() == GetDoc())
583     {
584         SwSectionFormat *const pSectFormat = pXSection->GetFormat();
585         SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
586 
587         const SwNodeIndex aSectIdx( *pSectNode, -1 );
588         SwPosition aBefore(aSectIdx);
589         bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
590         pTextNode = aBefore.GetNode().GetTextNode();
591     }
592     if (!bRet || !pTextNode)
593     {
594         throw lang::IllegalArgumentException();
595     }
596     pPara->attachToText(*this, *pTextNode);
597 }
598 
599 void SAL_CALL
insertTextContentAfter(const uno::Reference<text::XTextContent> & xNewContent,const uno::Reference<text::XTextContent> & xPredecessor)600 SwXText::insertTextContentAfter(
601     const uno::Reference< text::XTextContent>& xNewContent,
602     const uno::Reference< text::XTextContent>& xPredecessor)
603 {
604     SolarMutexGuard aGuard;
605 
606     if(!GetDoc())
607     {
608         throw uno::RuntimeException();
609     }
610 
611     SwXParagraph *const pPara = dynamic_cast<SwXParagraph*>(xNewContent.get());
612     if(!pPara || !pPara->IsDescriptor() || !xPredecessor.is())
613     {
614         throw lang::IllegalArgumentException();
615     }
616 
617     SwXTextSection *const pXSection = dynamic_cast<SwXTextSection*>(xPredecessor.get());
618     SwXTextTable *const pXTable = dynamic_cast<SwXTextTable*>(xPredecessor.get());
619     SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
620     bool bRet = false;
621     SwTextNode * pTextNode = nullptr;
622     if(pTableFormat && &pTableFormat->GetDoc() == GetDoc())
623     {
624         SwTable *const pTable = SwTable::FindTable( pTableFormat );
625         SwTableNode *const pTableNode = pTable->GetTableNode();
626 
627         SwEndNode *const pTableEnd = pTableNode->EndOfSectionNode();
628         SwPosition aTableEnd(*pTableEnd);
629         bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aTableEnd );
630         pTextNode = aTableEnd.GetNode().GetTextNode();
631     }
632     else if (pXSection && pXSection->GetFormat() &&
633             &pXSection->GetFormat()->GetDoc() == GetDoc())
634     {
635         SwSectionFormat *const pSectFormat = pXSection->GetFormat();
636         SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
637         SwEndNode *const pEnd = pSectNode->EndOfSectionNode();
638         SwPosition aEnd(*pEnd);
639         bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd );
640         pTextNode = aEnd.GetNode().GetTextNode();
641     }
642     if (!bRet || !pTextNode)
643     {
644         throw lang::IllegalArgumentException();
645     }
646     pPara->attachToText(*this, *pTextNode);
647 }
648 
649 void SAL_CALL
removeTextContentBefore(const uno::Reference<text::XTextContent> & xSuccessor)650 SwXText::removeTextContentBefore(
651     const uno::Reference< text::XTextContent>& xSuccessor)
652 {
653     SolarMutexGuard aGuard;
654 
655     if(!GetDoc())
656         throw uno::RuntimeException(cInvalidObject);
657 
658     bool bRet = false;
659     SwXTextSection *const pXSection = dynamic_cast<SwXTextSection*>(xSuccessor.get());
660     SwXTextTable *const pXTable = dynamic_cast<SwXTextTable*>(xSuccessor.get());
661     SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
662     if(pTableFormat && &pTableFormat->GetDoc() == GetDoc())
663     {
664         SwTable *const pTable = SwTable::FindTable( pTableFormat );
665         SwTableNode *const pTableNode = pTable->GetTableNode();
666 
667         const SwNodeIndex aTableIdx( *pTableNode, -1 );
668         if(aTableIdx.GetNode().IsTextNode())
669         {
670             SwPaM aBefore(aTableIdx);
671             bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aBefore );
672         }
673     }
674     else if (pXSection && pXSection->GetFormat() &&
675             &pXSection->GetFormat()->GetDoc() == GetDoc())
676     {
677         SwSectionFormat *const pSectFormat = pXSection->GetFormat();
678         SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
679 
680         const SwNodeIndex aSectIdx(  *pSectNode, -1 );
681         if(aSectIdx.GetNode().IsTextNode())
682         {
683             SwPaM aBefore(aSectIdx);
684             bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aBefore );
685         }
686     }
687     if(!bRet)
688     {
689         throw lang::IllegalArgumentException();
690     }
691 }
692 
693 void SAL_CALL
removeTextContentAfter(const uno::Reference<text::XTextContent> & xPredecessor)694 SwXText::removeTextContentAfter(
695         const uno::Reference< text::XTextContent>& xPredecessor)
696 {
697     SolarMutexGuard aGuard;
698 
699     if(!GetDoc())
700         throw uno::RuntimeException(cInvalidObject);
701 
702     bool bRet = false;
703     SwXTextSection *const pXSection = dynamic_cast<SwXTextSection*>(xPredecessor.get());
704     SwXTextTable *const pXTable = dynamic_cast<SwXTextTable*>(xPredecessor.get());
705     SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
706     if(pTableFormat && &pTableFormat->GetDoc() == GetDoc())
707     {
708         SwTable *const pTable = SwTable::FindTable( pTableFormat );
709         SwTableNode *const pTableNode = pTable->GetTableNode();
710         SwEndNode *const pTableEnd = pTableNode->EndOfSectionNode();
711 
712         const SwNodeIndex aTableIdx( *pTableEnd, 1 );
713         if(aTableIdx.GetNode().IsTextNode())
714         {
715             SwPaM aPaM(aTableIdx);
716             bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aPaM );
717         }
718     }
719     else if (pXSection && pXSection->GetFormat() &&
720             &pXSection->GetFormat()->GetDoc() == GetDoc())
721     {
722         SwSectionFormat *const pSectFormat = pXSection->GetFormat();
723         SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
724         SwEndNode *const pEnd = pSectNode->EndOfSectionNode();
725         const SwNodeIndex aSectIdx(  *pEnd, 1 );
726         if(aSectIdx.GetNode().IsTextNode())
727         {
728             SwPaM aAfter(aSectIdx);
729             bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aAfter );
730         }
731     }
732     if(!bRet)
733     {
734         throw lang::IllegalArgumentException();
735     }
736 }
737 
738 void SAL_CALL
removeTextContent(const uno::Reference<text::XTextContent> & xContent)739 SwXText::removeTextContent(
740         const uno::Reference< text::XTextContent > & xContent)
741 {
742     // forward: need no solar mutex here
743     if(!xContent.is())
744         throw uno::RuntimeException(u"first parameter invalid"_ustr);
745     xContent->dispose();
746 }
747 
748 uno::Reference< text::XText > SAL_CALL
getText()749 SwXText::getText()
750 {
751     SolarMutexGuard aGuard;
752     comphelper::ProfileZone aZone("SwXText::getText");
753 
754     const uno::Reference< text::XText > xRet(this);
755     return xRet;
756 }
757 
758 uno::Reference< text::XTextRange > SAL_CALL
getStart()759 SwXText::getStart()
760 {
761     SolarMutexGuard aGuard;
762 
763     const rtl::Reference< SwXTextCursor > xRef = createXTextCursor();
764     if(!xRef.is())
765         throw uno::RuntimeException(cInvalidObject);
766     xRef->gotoStart(false);
767     return static_cast<text::XWordCursor*>(xRef.get());
768 }
769 
770 uno::Reference< text::XTextRange > SAL_CALL
getEnd()771 SwXText::getEnd()
772 {
773     SolarMutexGuard aGuard;
774     return static_cast<text::XWordCursor*>(getEndImpl(aGuard).get());
775 }
776 
777 rtl::Reference< SwXTextCursor >
getEndImpl(SolarMutexGuard &)778 SwXText::getEndImpl(SolarMutexGuard& /*rGuard*/)
779 {
780     const rtl::Reference< SwXTextCursor > xRef = createXTextCursor();
781     if(!xRef.is())
782         throw uno::RuntimeException(cInvalidObject);
783     xRef->gotoEnd(false);
784     return xRef;
785 }
786 
getString()787 OUString SAL_CALL SwXText::getString()
788 {
789     SolarMutexGuard aGuard;
790 
791     const rtl::Reference< SwXTextCursor > xRet = createXTextCursor();
792     if(!xRet.is())
793     {
794         SAL_WARN("sw.uno", "cursor was not created in getString() call. Returning empty string.");
795         return OUString();
796     }
797     xRet->gotoEnd(true);
798     return xRet->getString();
799 }
800 
801 void SAL_CALL
setString(const OUString & rString)802 SwXText::setString(const OUString& rString)
803 {
804     SolarMutexGuard aGuard;
805 
806     if (!GetDoc())
807         throw uno::RuntimeException(cInvalidObject);
808 
809     const SwStartNode* pStartNode = GetStartNode();
810     if (!pStartNode)
811     {
812         throw uno::RuntimeException();
813     }
814 
815     GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
816     //insert an empty paragraph at the start and at the end to ensure that
817     //all tables and sections can be removed by the selecting text::XTextCursor
818     if (CursorType::Meta != m_eType)
819     {
820         SwPosition aStartPos(*pStartNode);
821         const SwEndNode* pEnd = pStartNode->EndOfSectionNode();
822         SwNodeIndex aEndIdx(*pEnd);
823         --aEndIdx;
824         //the inserting of nodes should only be done if really necessary
825         //to prevent #97924# (removes paragraph attributes when setting the text
826         //e.g. of a table cell
827         bool bInsertNodes = false;
828         SwNodeIndex aStartIdx(*pStartNode);
829         do
830         {
831             ++aStartIdx;
832             SwNode& rCurrentNode = aStartIdx.GetNode();
833             if(rCurrentNode.GetNodeType() == SwNodeType::Section
834                 ||rCurrentNode.GetNodeType() == SwNodeType::Table)
835             {
836                 bInsertNodes = true;
837                 break;
838             }
839         }
840         while(aStartIdx < aEndIdx);
841         if(bInsertNodes)
842         {
843             GetDoc()->getIDocumentContentOperations().AppendTextNode( aStartPos );
844             SwPaM aPam(aEndIdx.GetNode());
845             GetDoc()->getIDocumentContentOperations().AppendTextNode( *aPam.Start() );
846         }
847     }
848 
849     const rtl::Reference< SwXTextCursor > xRet = createXTextCursor();
850     if(!xRet.is())
851     {
852         GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
853         throw uno::RuntimeException(cInvalidObject);
854     }
855     xRet->gotoEnd(true);
856     xRet->setString(rString);
857     GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
858 }
859 
860 //FIXME why is CheckForOwnMember duplicated in some insert methods?
861 //  Description: Checks if pRange/pCursor are member of the same text interface.
862 //              Only one of the pointers has to be set!
CheckForOwnMember(const SwPaM & rPaM)863 bool SwXText::CheckForOwnMember(const SwPaM & rPaM)
864 {
865     const rtl::Reference< SwXTextCursor > xOwnCursor(createXTextCursor());
866     const SwStartNode* pOwnStartNode =
867         xOwnCursor->GetPaM()->GetPointNode().StartOfSectionNode();
868     SwStartNodeType eSearchNodeType = SwNormalStartNode;
869     switch (m_eType)
870     {
871         case CursorType::Frame:      eSearchNodeType = SwFlyStartNode;       break;
872         case CursorType::TableText:    eSearchNodeType = SwTableBoxStartNode;  break;
873         case CursorType::Footnote:   eSearchNodeType = SwFootnoteStartNode;  break;
874         case CursorType::Header:     eSearchNodeType = SwHeaderStartNode;    break;
875         case CursorType::Footer:     eSearchNodeType = SwFooterStartNode;    break;
876         //case CURSOR_INVALID:
877         //case CursorType::Body:
878         default:
879             ;
880     }
881 
882     const SwNode& rSrcNode = rPaM.GetPointNode();
883     const SwStartNode* pTmp = rSrcNode.FindStartNodeByType(eSearchNodeType);
884 
885     // skip SectionNodes / TableNodes to be able to compare across table/section boundaries
886     while (pTmp
887            && (pTmp->IsSectionNode() || pTmp->IsTableNode()
888                || (m_eType != CursorType::TableText
889                    && pTmp->GetStartNodeType() == SwTableBoxStartNode)))
890     {
891         pTmp = pTmp->StartOfSectionNode();
892     }
893 
894     while (pOwnStartNode->IsSectionNode() || pOwnStartNode->IsTableNode()
895            || (m_eType != CursorType::TableText
896                && pOwnStartNode->GetStartNodeType() == SwTableBoxStartNode))
897     {
898         pOwnStartNode = pOwnStartNode->StartOfSectionNode();
899     }
900 
901     //this checks if (this) and xRange are in the same text::XText interface
902     return (pOwnStartNode == pTmp);
903 }
904 
ComparePositions(const uno::Reference<text::XTextRange> & xPos1,const uno::Reference<text::XTextRange> & xPos2)905 sal_Int16 SwXText::ComparePositions(
906     const uno::Reference<text::XTextRange>& xPos1,
907     const uno::Reference<text::XTextRange>& xPos2)
908 {
909     SwUnoInternalPaM aPam1(*m_pDoc);
910     SwUnoInternalPaM aPam2(*m_pDoc);
911 
912     if (!::sw::XTextRangeToSwPaM(aPam1, xPos1) ||
913         !::sw::XTextRangeToSwPaM(aPam2, xPos2))
914     {
915         throw lang::IllegalArgumentException();
916     }
917     // The UNO API documentation for this method says we have to throw
918     // if either aPam1 or aPam2 is not within this text, and some
919     // extensions rely on that behaviour.
920     if (!CheckForOwnMember(aPam1) || !CheckForOwnMember(aPam2))
921     {
922         throw lang::IllegalArgumentException();
923     }
924 
925     sal_Int16 nCompare = 0;
926     SwPosition const*const pStart1 = aPam1.Start();
927     SwPosition const*const pStart2 = aPam2.Start();
928     if (*pStart1 < *pStart2)
929     {
930         nCompare = 1;
931     }
932     else if (*pStart1 > *pStart2)
933     {
934         nCompare = -1;
935     }
936     else
937     {
938         OSL_ENSURE(*pStart1 == *pStart2,
939                 "SwPositions should be equal here");
940         nCompare = 0;
941     }
942 
943     return nCompare;
944 }
945 
946 sal_Int16 SAL_CALL
compareRegionStarts(const uno::Reference<text::XTextRange> & xRange1,const uno::Reference<text::XTextRange> & xRange2)947 SwXText::compareRegionStarts(
948     const uno::Reference<text::XTextRange>& xRange1,
949     const uno::Reference<text::XTextRange>& xRange2)
950 {
951     SolarMutexGuard aGuard;
952 
953     if (!xRange1.is() || !xRange2.is())
954     {
955         throw lang::IllegalArgumentException();
956     }
957     SwXTextRange* pSwXTextRange1 = dynamic_cast<SwXTextRange*>(xRange1.get());
958     SwXTextRange* pSwXTextRange2 = dynamic_cast<SwXTextRange*>(xRange2.get());
959     if (pSwXTextRange1 && pSwXTextRange2)
960         return pSwXTextRange1->compareRegionStarts(*pSwXTextRange2);
961     const uno::Reference<text::XTextRange> xStart1 = xRange1->getStart();
962     const uno::Reference<text::XTextRange> xStart2 = xRange2->getStart();
963 
964     return ComparePositions(xStart1, xStart2);
965 }
966 
967 sal_Int16 SAL_CALL
compareRegionEnds(const uno::Reference<text::XTextRange> & xRange1,const uno::Reference<text::XTextRange> & xRange2)968 SwXText::compareRegionEnds(
969     const uno::Reference<text::XTextRange>& xRange1,
970     const uno::Reference<text::XTextRange>& xRange2)
971 {
972     SolarMutexGuard aGuard;
973 
974     if (!xRange1.is() || !xRange2.is())
975     {
976         throw lang::IllegalArgumentException();
977     }
978     uno::Reference<text::XTextRange> xEnd1 = xRange1->getEnd();
979     uno::Reference<text::XTextRange> xEnd2 = xRange2->getEnd();
980 
981     return ComparePositions(xEnd1, xEnd2);
982 }
983 
984 uno::Reference< beans::XPropertySetInfo > SAL_CALL
getPropertySetInfo()985 SwXText::getPropertySetInfo()
986 {
987     SolarMutexGuard g;
988 
989     static uno::Reference< beans::XPropertySetInfo > xInfo = m_rPropSet.getPropertySetInfo();
990     return xInfo;
991 }
992 
993 void SAL_CALL
setPropertyValue(const OUString &,const uno::Any &)994 SwXText::setPropertyValue(const OUString& /*aPropertyName*/,
995         const uno::Any& /*aValue*/)
996 {
997     throw lang::IllegalArgumentException();
998 }
999 
1000 uno::Any SAL_CALL
getPropertyValue(const OUString & rPropertyName)1001 SwXText::getPropertyValue(
1002     const OUString& rPropertyName)
1003 {
1004     SolarMutexGuard aGuard;
1005 
1006     if(!IsValid())
1007     {
1008         throw  uno::RuntimeException();
1009     }
1010 
1011     SfxItemPropertyMapEntry const*const pEntry =
1012         m_rPropSet.getPropertyMap().getByName(rPropertyName);
1013     if (!pEntry)
1014         throw beans::UnknownPropertyException("Unknown property: " + rPropertyName);
1015 
1016     uno::Any aRet;
1017     switch (pEntry->nWID)
1018     {
1019 //          no code necessary - the redline is always located at the end node
1020 //            case FN_UNO_REDLINE_NODE_START:
1021 //            break;
1022         case FN_UNO_REDLINE_NODE_END:
1023         {
1024             const SwRedlineTable& rRedTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1025             const size_t nRedTableCount = rRedTable.size();
1026             if (nRedTableCount > 0)
1027             {
1028                 SwStartNode const*const pStartNode = GetStartNode();
1029                 const SwNode& rOwnIndex = *pStartNode->EndOfSectionNode();
1030                 for (size_t nRed = 0; nRed < nRedTableCount; ++nRed)
1031                 {
1032                     SwRangeRedline const*const pRedline = rRedTable[nRed];
1033                     SwPosition const*const pRedStart = pRedline->Start();
1034                     const SwNode& rRedNode = pRedStart->GetNode();
1035                     if (rOwnIndex == rRedNode)
1036                     {
1037                         aRet <<= SwXRedlinePortion::CreateRedlineProperties(
1038                                 *pRedline, true);
1039                         break;
1040                     }
1041                 }
1042             }
1043         }
1044         break;
1045         case FN_UNO_IS_CONTENT_EMPTY:
1046         {
1047             const SwStartNode* pStartNode = GetStartNode();
1048             SwNodeOffset nStartIndex = pStartNode->GetIndex();
1049             SwNodeOffset nEndIndex = pStartNode->EndOfSectionIndex();
1050             if (nEndIndex - nStartIndex > SwNodeOffset(2))
1051             {
1052                 // More than 1 node between the start and end one: not empty.
1053                 aRet <<= false;
1054                 return aRet;
1055             }
1056 
1057             if (nEndIndex - nStartIndex == SwNodeOffset(2))
1058             {
1059                 SwPaM aPaM(*pStartNode);
1060                 aPaM.Move(fnMoveForward, GoInNode);
1061                 SwTextNode* pTextNode = aPaM.Start()->GetNode().GetTextNode();
1062                 if (pTextNode && !pTextNode->GetText().isEmpty())
1063                 {
1064                     // 1 node, but that text node has text: not empty.
1065                     aRet <<= false;
1066                     return aRet;
1067                 }
1068             }
1069 
1070             sw::FrameFormats<sw::SpzFrameFormat*>& rFormats = *GetDoc()->GetSpzFrameFormats();
1071             for(sw::SpzFrameFormat* pFormat: rFormats)
1072             {
1073                 const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
1074                 const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
1075                 if (!pAnchorNode)
1076                 {
1077                     continue;
1078                 }
1079 
1080                 SwNodeOffset nAnchorIndex = pAnchorNode->GetIndex();
1081                 if (nAnchorIndex > nStartIndex && nAnchorIndex < nEndIndex)
1082                 {
1083                     // This fly or draw format has an anchor in the node section: not empty.
1084                     aRet <<= false;
1085                     return aRet;
1086                 }
1087             }
1088             // Otherwise empty.
1089             aRet <<= true;
1090         }
1091         break;
1092     }
1093     return aRet;
1094 }
1095 
1096 void SAL_CALL
addPropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)1097 SwXText::addPropertyChangeListener(
1098         const OUString& /*rPropertyName*/,
1099         const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
1100 {
1101     OSL_FAIL("SwXText::addPropertyChangeListener(): not implemented");
1102 }
1103 
1104 void SAL_CALL
removePropertyChangeListener(const OUString &,const uno::Reference<beans::XPropertyChangeListener> &)1105 SwXText::removePropertyChangeListener(
1106         const OUString& /*rPropertyName*/,
1107         const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
1108 {
1109     OSL_FAIL("SwXText::removePropertyChangeListener(): not implemented");
1110 }
1111 
1112 void SAL_CALL
addVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)1113 SwXText::addVetoableChangeListener(
1114         const OUString& /*rPropertyName*/,
1115         const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
1116 {
1117     OSL_FAIL("SwXText::addVetoableChangeListener(): not implemented");
1118 }
1119 
1120 void SAL_CALL
removeVetoableChangeListener(const OUString &,const uno::Reference<beans::XVetoableChangeListener> &)1121 SwXText::removeVetoableChangeListener(
1122         const OUString& /*rPropertyName*/,
1123         const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
1124 {
1125     OSL_FAIL("SwXText::removeVetoableChangeListener(): not implemented");
1126 }
1127 
1128 namespace
1129 {
1130 }
1131 
1132 uno::Reference< text::XTextRange > SAL_CALL
finishParagraph(const uno::Sequence<beans::PropertyValue> & rProperties)1133 SwXText::finishParagraph(
1134         const uno::Sequence< beans::PropertyValue > & rProperties)
1135 {
1136     SolarMutexGuard g;
1137 
1138     return finishOrAppendParagraph(rProperties, uno::Reference< text::XTextRange >());
1139 }
1140 
1141 uno::Reference< text::XTextRange > SAL_CALL
finishParagraphInsert(const uno::Sequence<beans::PropertyValue> & rProperties,const uno::Reference<text::XTextRange> & xInsertPosition)1142 SwXText::finishParagraphInsert(
1143         const uno::Sequence< beans::PropertyValue > & rProperties,
1144         const uno::Reference< text::XTextRange >& xInsertPosition)
1145 {
1146     SolarMutexGuard g;
1147 
1148     return finishOrAppendParagraph(rProperties, xInsertPosition);
1149 }
1150 
1151 rtl::Reference<SwXParagraph>
finishOrAppendParagraph(const uno::Sequence<beans::PropertyValue> & rProperties,const uno::Reference<text::XTextRange> & xInsertPosition)1152 SwXText::finishOrAppendParagraph(
1153         const uno::Sequence< beans::PropertyValue > & rProperties,
1154         const uno::Reference< text::XTextRange >& xInsertPosition)
1155 {
1156     if (!m_bIsValid)
1157     {
1158         throw  uno::RuntimeException();
1159     }
1160 
1161     const SwStartNode* pStartNode = GetStartNode();
1162     if(!pStartNode)
1163     {
1164         throw  uno::RuntimeException();
1165     }
1166 
1167     rtl::Reference<SwXParagraph> xRet;
1168     bool bIllegalException = false;
1169     bool bRuntimeException = false;
1170     OUString sMessage;
1171     m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::START , nullptr);
1172     // find end node, go backward - don't skip tables because the new
1173     // paragraph has to be the last node
1174     //aPam.Move( fnMoveBackward, GoInNode );
1175     SwPaM aPam(*pStartNode->EndOfSectionNode(), SwNodeOffset(-1));
1176     // If we got a position reference, then the insert point is not the end of
1177     // the document.
1178     if (xInsertPosition.is())
1179     {
1180         SwUnoInternalPaM aStartPam(*GetDoc());
1181         ::sw::XTextRangeToSwPaM(aStartPam, xInsertPosition);
1182         aPam = aStartPam;
1183         aPam.SetMark();
1184     }
1185     m_pDoc->getIDocumentContentOperations().AppendTextNode( *aPam.GetPoint() );
1186     // remove attributes from the previous paragraph
1187     m_pDoc->ResetAttrs(aPam);
1188     // in case of finishParagraph the PaM needs to be moved to the
1189     // previous paragraph
1190     aPam.Move( fnMoveBackward, GoInNode );
1191 
1192     try
1193     {
1194         SfxItemPropertySet const*const pParaPropSet =
1195             aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH);
1196 
1197         SwUnoCursorHelper::SetPropertyValues(aPam, *pParaPropSet, rProperties);
1198 
1199         // tdf#127616 keep direct character formatting of empty paragraphs,
1200         // if character style of the paragraph sets also the same attributes
1201         if (aPam.Start()->GetNode().GetTextNode()->Len() == 0)
1202         {
1203             auto itCharStyle = std::find_if(rProperties.begin(), rProperties.end(), [](const beans::PropertyValue& rValue)
1204             {
1205                 return rValue.Name == "CharStyleName";
1206             });
1207             if ( itCharStyle != rProperties.end() )
1208             {
1209                 for (const auto& rValue : rProperties)
1210                 {
1211                     if ( rValue != *itCharStyle && rValue.Name.startsWith("Char") )
1212                     {
1213                         SwUnoCursorHelper::SetPropertyValue(aPam, *pParaPropSet, rValue.Name, rValue.Value);
1214                     }
1215                 }
1216             }
1217         }
1218     }
1219     catch (const lang::IllegalArgumentException& rIllegal)
1220     {
1221         sMessage = rIllegal.Message;
1222         bIllegalException = true;
1223     }
1224     catch (const uno::RuntimeException& rRuntime)
1225     {
1226         sMessage = rRuntime.Message;
1227         bRuntimeException = true;
1228     }
1229     catch (const uno::Exception& rEx)
1230     {
1231         sMessage = rEx.Message;
1232         bRuntimeException = true;
1233     }
1234 
1235     m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
1236     if (bIllegalException || bRuntimeException)
1237     {
1238         m_pDoc->GetIDocumentUndoRedo().Undo();
1239         if (bIllegalException)
1240         {
1241             throw lang::IllegalArgumentException(sMessage, nullptr, 0);
1242         }
1243         else
1244         {
1245             throw uno::RuntimeException(sMessage);
1246         }
1247     }
1248     SwTextNode *const pTextNode( aPam.Start()->GetNode().GetTextNode() );
1249     OSL_ENSURE(pTextNode, "no SwTextNode?");
1250     if (pTextNode)
1251     {
1252         xRet = SwXParagraph::CreateXParagraph(*m_pDoc, pTextNode, this);
1253     }
1254 
1255     return xRet;
1256 }
1257 
1258 uno::Reference< text::XTextRange > SAL_CALL
insertTextPortion(const OUString & rText,const uno::Sequence<beans::PropertyValue> & rCharacterAndParagraphProperties,const uno::Reference<text::XTextRange> & xInsertPosition)1259 SwXText::insertTextPortion(
1260         const OUString& rText,
1261         const uno::Sequence< beans::PropertyValue > &
1262             rCharacterAndParagraphProperties,
1263         const uno::Reference<text::XTextRange>& xInsertPosition)
1264 {
1265     SolarMutexGuard aGuard;
1266 
1267     if(!IsValid())
1268     {
1269         throw  uno::RuntimeException();
1270     }
1271 
1272     const rtl::Reference<SwXTextCursor> xTextCursor = createXTextCursorByRange(xInsertPosition);
1273     return insertTextPortionImpl(aGuard, rText, rCharacterAndParagraphProperties, xTextCursor);
1274 }
1275 
1276 rtl::Reference< SwXTextRange >
insertTextPortionImpl(SolarMutexGuard &,std::u16string_view rText,const uno::Sequence<beans::PropertyValue> & rCharacterAndParagraphProperties,const rtl::Reference<SwXTextCursor> & xTextCursor)1277 SwXText::insertTextPortionImpl(
1278         SolarMutexGuard& /*rGuard*/,
1279         std::u16string_view rText,
1280         const uno::Sequence< beans::PropertyValue > & rCharacterAndParagraphProperties,
1281         const rtl::Reference<SwXTextCursor>& xTextCursor)
1282 {
1283     bool bIllegalException = false;
1284     bool bRuntimeException = false;
1285     OUString sMessage;
1286     m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
1287 
1288     auto& rCursor(xTextCursor->GetCursor());
1289     m_pDoc->DontExpandFormat( *rCursor.Start() );
1290 
1291     if (!rText.empty())
1292     {
1293         SwNodeIndex const nodeIndex(rCursor.GetPoint()->GetNode(), -1);
1294         const sal_Int32 nContentPos = rCursor.GetPoint()->GetContentIndex();
1295         SwUnoCursorHelper::DocInsertStringSplitCR(
1296             *m_pDoc, rCursor, rText, false);
1297         SwUnoCursorHelper::SelectPam(rCursor, true);
1298         rCursor.GetPoint()->Assign(nodeIndex.GetNode(), SwNodeOffset(+1), nContentPos);
1299     }
1300 
1301     try
1302     {
1303       SfxItemPropertySet const*const pCursorPropSet =
1304           aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR);
1305       SwUnoCursorHelper::SetPropertyValues(rCursor, *pCursorPropSet,
1306                                            rCharacterAndParagraphProperties,
1307                                            SetAttrMode::NOFORMATATTR);
1308     }
1309     catch (const lang::IllegalArgumentException& rIllegal)
1310     {
1311         sMessage = rIllegal.Message;
1312         bIllegalException = true;
1313     }
1314     catch (const uno::RuntimeException& rRuntime)
1315     {
1316         sMessage = rRuntime.Message;
1317         bRuntimeException = true;
1318     }
1319     m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
1320     if (bIllegalException || bRuntimeException)
1321     {
1322         m_pDoc->GetIDocumentUndoRedo().Undo();
1323         if (bIllegalException)
1324         {
1325             throw lang::IllegalArgumentException(sMessage, nullptr, 0);
1326         }
1327         else
1328         {
1329             throw uno::RuntimeException(sMessage);
1330         }
1331     }
1332     rtl::Reference< SwXTextRange > xRet = new SwXTextRange(rCursor, this);
1333     return xRet;
1334 }
1335 
1336 // Append text portions at the end of the last paragraph of the text interface.
1337 // Support of import filters.
1338 uno::Reference< text::XTextRange > SAL_CALL
appendTextPortion(const OUString & rText,const uno::Sequence<beans::PropertyValue> & rCharacterAndParagraphProperties)1339 SwXText::appendTextPortion(
1340         const OUString& rText,
1341         const uno::Sequence< beans::PropertyValue > &
1342             rCharacterAndParagraphProperties)
1343 {
1344     SolarMutexGuard aGuard;
1345     rtl::Reference< SwXTextCursor > xInsertPosition = getEndImpl(aGuard);
1346     rtl::Reference< SwXTextRange > xRange = insertTextPortionImpl(aGuard, rText, rCharacterAndParagraphProperties, xInsertPosition);
1347     return xRange;
1348 }
1349 
1350 // enable inserting/appending text contents like graphic objects, shapes and so on to
1351 // support import filters
1352 uno::Reference< text::XTextRange > SAL_CALL
insertTextContentWithProperties(const uno::Reference<text::XTextContent> & xTextContent,const uno::Sequence<beans::PropertyValue> & rCharacterAndParagraphProperties,const uno::Reference<text::XTextRange> & xInsertPosition)1353 SwXText::insertTextContentWithProperties(
1354     const uno::Reference< text::XTextContent >& xTextContent,
1355     const uno::Sequence< beans::PropertyValue >&
1356         rCharacterAndParagraphProperties,
1357     const uno::Reference< text::XTextRange >& xInsertPosition)
1358 {
1359     SolarMutexGuard aGuard;
1360 
1361     if (!IsValid())
1362     {
1363         throw  uno::RuntimeException();
1364     }
1365 
1366     SwUnoInternalPaM aPam(*GetDoc());
1367     if (!::sw::XTextRangeToSwPaM(aPam, xInsertPosition))
1368     {
1369         throw lang::IllegalArgumentException(u"invalid position"_ustr, nullptr, 2);
1370     }
1371 
1372     SwRewriter aRewriter;
1373     aRewriter.AddRule(UndoArg1, SwResId(STR_UNDO_INSERT_TEXTBOX));
1374 
1375     m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, &aRewriter);
1376 
1377     // Any direct formatting ending at the insert position (xRange) should not
1378     // be expanded to cover the inserted content (xContent)
1379     // (insertTextContent() shouldn't do this, only ...WithProperties()!)
1380     GetDoc()->DontExpandFormat( *aPam.Start() );
1381 
1382     // now attach the text content here
1383     insertTextContent( xInsertPosition, xTextContent, false );
1384     // now apply the properties to the anchor
1385     if (rCharacterAndParagraphProperties.hasElements())
1386     {
1387         try
1388         {
1389             const uno::Reference< beans::XPropertySet > xAnchor(
1390                 xTextContent->getAnchor(), uno::UNO_QUERY);
1391             if (xAnchor.is())
1392             {
1393                 for (const auto& rProperty : rCharacterAndParagraphProperties)
1394                 {
1395                     xAnchor->setPropertyValue(rProperty.Name, rProperty.Value);
1396                 }
1397             }
1398         }
1399         catch (const uno::Exception& e)
1400         {
1401             css::uno::Any anyEx = cppu::getCaughtException();
1402             m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, &aRewriter);
1403             throw lang::WrappedTargetRuntimeException( e.Message,
1404                             uno::Reference< uno::XInterface >(), anyEx );
1405         }
1406     }
1407     m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, &aRewriter);
1408     return xInsertPosition;
1409 }
1410 
1411 uno::Reference< text::XTextRange > SAL_CALL
appendTextContent(const uno::Reference<text::XTextContent> & xTextContent,const uno::Sequence<beans::PropertyValue> & rCharacterAndParagraphProperties)1412 SwXText::appendTextContent(
1413     const uno::Reference< text::XTextContent >& xTextContent,
1414     const uno::Sequence< beans::PropertyValue >& rCharacterAndParagraphProperties
1415     )
1416 {
1417     // Right now this doesn't need a guard, as it's just calling the insert
1418     // version, that has it already.
1419     uno::Reference<text::XTextRange> xInsertPosition = getEnd();
1420     return insertTextContentWithProperties(xTextContent, rCharacterAndParagraphProperties, xInsertPosition);
1421 }
1422 
1423 // determine whether SwFrameFormat is a graphic node
isGraphicNode(const SwFrameFormat * pFrameFormat)1424 static bool isGraphicNode(const SwFrameFormat* pFrameFormat)
1425 {
1426     // safety
1427     if( !pFrameFormat->GetContent().GetContentIdx() )
1428     {
1429         return false;
1430     }
1431     auto index = *pFrameFormat->GetContent().GetContentIdx();
1432     // consider the next node -> there is the graphic stored
1433     ++index;
1434     return index.GetNode().IsGrfNode();
1435 }
1436 
1437 /// Determines if the at-para rAnchor is anchored at the start or end of rAnchorCheckPam.
IsAtParaMatch(const SwPaM & rAnchorCheckPam,const SwFormatAnchor & rAnchor)1438 static bool IsAtParaMatch(const SwPaM& rAnchorCheckPam, const SwFormatAnchor& rAnchor)
1439 {
1440     if (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PARA)
1441     {
1442         return false;
1443     }
1444 
1445     if (rAnchorCheckPam.Start()->GetNode() == *rAnchor.GetAnchorNode())
1446     {
1447         return true;
1448     }
1449 
1450     if (rAnchorCheckPam.End()->GetNode() == *rAnchor.GetAnchorNode())
1451     {
1452         SwTextNode* pEndTextNode = rAnchorCheckPam.End()->GetNode().GetTextNode();
1453         if (pEndTextNode && rAnchorCheckPam.End()->GetContentIndex() == pEndTextNode->Len())
1454         {
1455             // rAnchorCheckPam covers the entire last text node, rAnchor is at-para, consider this
1456             // as "inside pam" rather than "at the end of pam".
1457             return false;
1458         }
1459         return true;
1460     }
1461 
1462     return false;
1463 }
1464 
1465 // move previously appended paragraphs into a text frames
1466 // to support import filters
1467 uno::Reference< text::XTextContent > SAL_CALL
convertToTextFrame(const uno::Reference<text::XTextRange> & xStart,const uno::Reference<text::XTextRange> & xEnd,const uno::Sequence<beans::PropertyValue> & rFrameProperties)1468 SwXText::convertToTextFrame(
1469     const uno::Reference< text::XTextRange >& xStart,
1470     const uno::Reference< text::XTextRange >& xEnd,
1471     const uno::Sequence< beans::PropertyValue >& rFrameProperties)
1472 {
1473     SolarMutexGuard aGuard;
1474 
1475     if(!IsValid())
1476     {
1477         throw  uno::RuntimeException();
1478     }
1479     // tdf#143384 recognize dummy property, that was set to make createTextCursor
1480     // to not ignore tables.
1481     // It is enough to use this hack only for the range start,
1482     // because as far as I know, the range cannot end with table when this property is set.
1483     ::sw::TextRangeMode eMode = ::sw::TextRangeMode::RequireTextNode;
1484     for (const auto& rCellProperty : rFrameProperties)
1485     {
1486         if (rCellProperty.Name == "CursorNotIgnoreTables")
1487         {
1488             bool bAllowNonTextNode = false;
1489             rCellProperty.Value >>= bAllowNonTextNode;
1490             if (bAllowNonTextNode)
1491                 eMode = ::sw::TextRangeMode::AllowTableNode;
1492             break;
1493         }
1494     }
1495     uno::Reference< text::XTextContent > xRet;
1496     std::optional<SwUnoInternalPaM> pTempStartPam(*GetDoc());
1497     std::optional<SwUnoInternalPaM> pEndPam(*GetDoc());
1498     if (!::sw::XTextRangeToSwPaM(*pTempStartPam, xStart, eMode)
1499         || !::sw::XTextRangeToSwPaM(*pEndPam, xEnd))
1500     {
1501         throw lang::IllegalArgumentException();
1502     }
1503 
1504     auto pStartPam(GetDoc()->CreateUnoCursor(*pTempStartPam->GetPoint()));
1505     if (pTempStartPam->HasMark())
1506     {
1507         pStartPam->SetMark();
1508         *pStartPam->GetMark() = *pTempStartPam->GetMark();
1509     }
1510     pTempStartPam.reset();
1511 
1512     SwXTextRange *const pStartRange = dynamic_cast<SwXTextRange*>(xStart.get());
1513     SwXTextRange *const pEndRange   = dynamic_cast<SwXTextRange*>(xEnd.get());
1514     // bookmarks have to be removed before the referenced text node
1515     // is deleted in DelFullPara
1516     if (pStartRange)
1517     {
1518         pStartRange->Invalidate();
1519     }
1520     if (pEndRange)
1521     {
1522         pEndRange->Invalidate();
1523     }
1524 
1525     m_pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
1526     bool bIllegalException = false;
1527     bool bRuntimeException = false;
1528     OUString sMessage;
1529     SwStartNode* pStartStartNode = pStartPam->GetPointNode().StartOfSectionNode();
1530     while (pStartStartNode && pStartStartNode->IsSectionNode())
1531     {
1532         pStartStartNode = pStartStartNode->StartOfSectionNode();
1533     }
1534     SwStartNode* pEndStartNode = pEndPam->GetPointNode().StartOfSectionNode();
1535     while (pEndStartNode && pEndStartNode->IsSectionNode())
1536     {
1537         pEndStartNode = pEndStartNode->StartOfSectionNode();
1538     }
1539     bool bParaAfterInserted = false;
1540     bool bParaBeforeInserted = false;
1541     ::std::optional<SwPaM> oAnchorCheckPam;
1542     oAnchorCheckPam.emplace(*pStartPam->Start(), *pEndPam->End());
1543     if (
1544         pStartStartNode && pEndStartNode &&
1545         (pStartStartNode != pEndStartNode || pStartStartNode != GetStartNode())
1546        )
1547     {
1548         // todo: if the start/end is in a table then insert a paragraph
1549         // before/after, move the start/end nodes, then convert and
1550         // remove the additional paragraphs in the end
1551         SwTableNode * pStartTableNode(nullptr);
1552         if (pStartStartNode->GetStartNodeType() == SwTableBoxStartNode)
1553         {
1554             pStartTableNode = pStartStartNode->FindTableNode();
1555             // Is it the same table start node than the end?
1556             SwTableNode *const pEndStartTableNode(pEndStartNode->FindTableNode());
1557             while (pEndStartTableNode && pStartTableNode &&
1558                    pEndStartTableNode->GetIndex() < pStartTableNode->GetIndex())
1559             {
1560                 SwStartNode* pStartStartTableNode = pStartTableNode->StartOfSectionNode();
1561                 pStartTableNode = pStartStartTableNode->FindTableNode();
1562             }
1563         }
1564         if (pStartTableNode)
1565         {
1566             const SwNodeIndex aTableIdx(  *pStartTableNode, -1 );
1567             SwPosition aBefore(aTableIdx);
1568             bParaBeforeInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
1569             pStartPam->DeleteMark();
1570             *pStartPam->GetPoint() = std::move(aBefore);
1571             pStartStartNode = pStartPam->GetPointNode().StartOfSectionNode();
1572         }
1573         if (pEndStartNode->GetStartNodeType() == SwTableBoxStartNode)
1574         {
1575             SwTableNode *const pEndTableNode = pEndStartNode->FindTableNode();
1576             SwEndNode *const pTableEnd = pEndTableNode->EndOfSectionNode();
1577             SwPosition aTableEnd(*pTableEnd);
1578             bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aTableEnd );
1579             pEndPam->DeleteMark();
1580             *pEndPam->GetPoint() = std::move(aTableEnd);
1581             pEndStartNode = pEndPam->GetPointNode().StartOfSectionNode();
1582         }
1583         // now we should have the positions in the same hierarchy
1584         if ((pStartStartNode != pEndStartNode) ||
1585             (pStartStartNode != GetStartNode()))
1586         {
1587             // if not - remove the additional paragraphs and throw
1588             oAnchorCheckPam.reset(); // clear SwContentIndex before deleting nodes
1589             if (bParaBeforeInserted)
1590             {
1591                 SwCursor aDelete(*pStartPam->GetPoint(), nullptr);
1592                 *pStartPam->GetPoint() = // park it because node is deleted
1593                     SwPosition(GetDoc()->GetNodes().GetEndOfContent());
1594                 aDelete.MovePara(GoCurrPara, fnParaStart);
1595                 aDelete.SetMark();
1596                 aDelete.MovePara(GoCurrPara, fnParaEnd);
1597                 GetDoc()->getIDocumentContentOperations().DelFullPara(aDelete);
1598             }
1599             if (bParaAfterInserted)
1600             {
1601                 SwCursor aDelete(*pEndPam->GetPoint(), nullptr);
1602                 *pEndPam->GetPoint() = // park it because node is deleted
1603                     SwPosition(GetDoc()->GetNodes().GetEndOfContent());
1604                 aDelete.MovePara(GoCurrPara, fnParaStart);
1605                 aDelete.SetMark();
1606                 aDelete.MovePara(GoCurrPara, fnParaEnd);
1607                 GetDoc()->getIDocumentContentOperations().DelFullPara(aDelete);
1608             }
1609             throw lang::IllegalArgumentException();
1610         }
1611     }
1612 
1613     // make a selection from pStartPam to pEndPam
1614     // If there is no content in the frame the shape is in
1615     // it gets deleted in the DelFullPara call below,
1616     // In this case insert a tmp text node ( we delete it later )
1617     if (pStartPam->Start()->GetNode() == pEndPam->Start()->GetNode()
1618         && pStartPam->End()->GetNode() == pEndPam->End()->GetNode())
1619     {
1620         SwPosition aEnd(*pStartPam->End());
1621         bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd );
1622         pEndPam->DeleteMark();
1623         *pEndPam->GetPoint() = aEnd;
1624         *oAnchorCheckPam->End() = std::move(aEnd);
1625     }
1626     pStartPam->SetMark();
1627     *pStartPam->End() = *pEndPam->End();
1628     pEndPam.reset();
1629 
1630     // see if there are frames already anchored to this node
1631     // we have to work with the SdrObjects, as unique name is not guaranteed in their frame format
1632     // tdf#115094: do nothing if we have a graphic node
1633     o3tl::sorted_vector<const SdrObject*> aAnchoredObjectsByPtr;
1634     std::set<UIName> aAnchoredObjectsByName;
1635     for (size_t i = 0; i < m_pDoc->GetSpzFrameFormats()->size(); ++i)
1636     {
1637         const SwFrameFormat* pFrameFormat = (*m_pDoc->GetSpzFrameFormats())[i];
1638         const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
1639         // note: Word can do at-char anchors in text frames - sometimes!
1640         // see testFlyInFly for why this checks only the edges of the selection,
1641         // and testFloatingTablesAnchor for why it excludes pre/post table
1642         // added nodes
1643         // TODO: isGraphicNode here looks dubious; see also tdf#47036 fix;
1644         // this needs more investigation when exactly Word considers something
1645         // anchored in text frame vs. anchored in body.
1646         if (!isGraphicNode(pFrameFormat)
1647             && (IsAtParaMatch(*oAnchorCheckPam, rAnchor)
1648                 || (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()
1649                     && (    *oAnchorCheckPam->Start() == *rAnchor.GetContentAnchor()
1650                         ||  *oAnchorCheckPam->End() == *rAnchor.GetContentAnchor()))))
1651         {
1652             if (pFrameFormat->GetName().isEmpty())
1653             {
1654                 aAnchoredObjectsByPtr.insert(pFrameFormat->FindSdrObject());
1655             }
1656             else
1657             {
1658                 aAnchoredObjectsByName.insert(pFrameFormat->GetName());
1659             }
1660         }
1661     }
1662     oAnchorCheckPam.reset(); // clear SwContentIndex before deleting nodes
1663 
1664     const rtl::Reference<SwXTextFrame> xNewFrame =
1665             SwXTextFrame::CreateXTextFrame(*m_pDoc, nullptr);
1666     try
1667     {
1668         for (const beans::PropertyValue& rValue : rFrameProperties)
1669         {
1670             xNewFrame->SwXFrame::setPropertyValue(rValue.Name, rValue.Value);
1671         }
1672 
1673         {   // has to be in a block to remove the SwContentIndexes before
1674             // DelFullPara is called
1675             const uno::Reference< text::XTextRange> xInsertTextRange =
1676                 new SwXTextRange(*pStartPam, this);
1677             assert(xNewFrame->IsDescriptor());
1678             xNewFrame->attachToRange(xInsertTextRange, pStartPam.get());
1679             assert(!xNewFrame->getName().isEmpty());
1680         }
1681 
1682         SwTextNode *const pTextNode(pStartPam->GetPointNode().GetTextNode());
1683         assert(pTextNode);
1684         if (!pTextNode || !pTextNode->Len()) // don't remove if it contains text!
1685         {
1686             bool bDel = false;
1687             {   // has to be in a block to remove the SwContentIndexes before
1688                 // DelFullPara is called
1689                 SwPaM aMovePam( pStartPam->GetPointNode() );
1690                 if (aMovePam.Move( fnMoveForward, GoInContent ))
1691                 {
1692                     // move the anchor to the next paragraph
1693                     SwFormatAnchor aNewAnchor(xNewFrame->GetFrameFormat()->GetAnchor());
1694                     aNewAnchor.SetAnchor( aMovePam.Start() );
1695                     m_pDoc->SetAttr(
1696                         aNewAnchor, *xNewFrame->GetFrameFormat() );
1697 
1698                     // also move frames anchored to us
1699                     for (size_t i = 0; i < m_pDoc->GetSpzFrameFormats()->size(); ++i)
1700                     {
1701                         SwFrameFormat* pFrameFormat = (*m_pDoc->GetSpzFrameFormats())[i];
1702                         if ((!pFrameFormat->GetName().isEmpty() && aAnchoredObjectsByName.find(pFrameFormat->GetName()) != aAnchoredObjectsByName.end() ) ||
1703                             ( pFrameFormat->GetName().isEmpty() && aAnchoredObjectsByPtr.find(pFrameFormat->FindSdrObject()) != aAnchoredObjectsByPtr.end()) )
1704                         {
1705                             // copy the anchor to the next paragraph
1706                             SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
1707                             aAnchor.SetAnchor(aMovePam.Start());
1708                             m_pDoc->SetAttr(aAnchor, *pFrameFormat);
1709                         }
1710                         else
1711                         {
1712                             // if this frame is a textbox of a shape anchored to us, move this textbox too.
1713                             const auto& pTextBoxes = pFrameFormat->GetOtherTextBoxFormats();
1714                             if (pFrameFormat->Which() == RES_FLYFRMFMT && pTextBoxes
1715                                 && pTextBoxes->GetOwnerShape())
1716                             {
1717                                 const auto& rShapeAnchor = pTextBoxes->GetOwnerShape()->GetAnchor();
1718                                 if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
1719                                     && rShapeAnchor.GetContentAnchor() && pFrameFormat->GetAnchor().GetContentAnchor()
1720                                     && pStartPam->ContainsPosition(*pFrameFormat->GetAnchor().GetContentAnchor()))
1721                                 {
1722                                     const SwNode& rAnchorNode
1723                                         = *pFrameFormat->GetAnchor().GetAnchorNode();
1724                                     if (!(rAnchorNode.FindFooterStartNode() || rAnchorNode.FindHeaderStartNode()))
1725                                     {
1726                                         SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
1727                                         aAnchor.SetAnchor(aMovePam.Start());
1728                                         m_pDoc->SetAttr(aAnchor, *pFrameFormat);
1729                                     }
1730                                 }
1731                             }
1732                         }
1733                     }
1734                     bDel = true; // Only delete the ex-anchor, if the frame is moved successfully
1735                 }
1736             }
1737             if (bDel)
1738                 m_pDoc->getIDocumentContentOperations().DelFullPara(*pStartPam);
1739         }
1740     }
1741     catch (const lang::IllegalArgumentException& rIllegal)
1742     {
1743         sMessage = rIllegal.Message;
1744         bIllegalException = true;
1745     }
1746     catch (const uno::RuntimeException& rRuntime)
1747     {
1748         sMessage = rRuntime.Message;
1749         bRuntimeException = true;
1750     }
1751     xRet = static_cast<SwXFrame*>(xNewFrame.get());
1752     if (bParaBeforeInserted || bParaAfterInserted)
1753     {
1754         const rtl::Reference<SwXTextCursor> xFrameTextCursor =
1755             xNewFrame->createXTextCursor();
1756         if (bParaBeforeInserted)
1757         {
1758             // todo: remove paragraph before frame
1759             m_pDoc->getIDocumentContentOperations().DelFullPara(*xFrameTextCursor->GetPaM());
1760         }
1761         if (bParaAfterInserted)
1762         {
1763             xFrameTextCursor->gotoEnd(false);
1764             if (!bParaBeforeInserted)
1765                 m_pDoc->getIDocumentContentOperations().DelFullPara(*xFrameTextCursor->GetPaM());
1766             else
1767             {
1768                 // In case the frame has a table only, the cursor points to the end of the first cell of the table.
1769                 SwPaM aPaM(*xFrameTextCursor->GetPaM()->GetPointNode().FindStartNodeByType(SwFlyStartNode)->EndOfSectionNode());
1770                 // Now we have the end of the frame -- the node before that will be the paragraph we want to remove.
1771                 aPaM.GetPoint()->Adjust(SwNodeOffset(-1));
1772                 m_pDoc->getIDocumentContentOperations().DelFullPara(aPaM);
1773             }
1774         }
1775     }
1776 
1777     m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
1778     if (bIllegalException || bRuntimeException)
1779     {
1780         m_pDoc->GetIDocumentUndoRedo().Undo();
1781         if (bIllegalException)
1782         {
1783             throw lang::IllegalArgumentException(sMessage, nullptr, 0);
1784         }
1785         else
1786         {
1787             throw uno::RuntimeException(sMessage);
1788         }
1789     }
1790     return xRet;
1791 }
1792 
1793 namespace {
1794 
1795 // Move previously imported paragraphs into a new text table.
1796 struct VerticallyMergedCell
1797 {
1798     std::vector<uno::Reference< beans::XPropertySet > > aCells;
1799     sal_Int32                                           nLeftPosition;
1800     bool                                                bOpen;
1801 
VerticallyMergedCell__anon54d157080311::VerticallyMergedCell1802     VerticallyMergedCell(uno::Reference< beans::XPropertySet > const& rxCell,
1803             const sal_Int32 nLeft)
1804         : nLeftPosition( nLeft )
1805         , bOpen( true )
1806     {
1807         aCells.push_back( rxCell );
1808     }
1809 };
1810 
1811 }
1812 
1813 #define COL_POS_FUZZY 2
1814 
lcl_SimilarPosition(const sal_Int32 nPos1,const sal_Int32 nPos2)1815 static bool lcl_SimilarPosition( const sal_Int32 nPos1, const sal_Int32 nPos2 )
1816 {
1817     return abs( nPos1 - nPos2 ) < COL_POS_FUZZY;
1818 }
1819 
ConvertCell(const uno::Sequence<uno::Reference<text::XTextRange>> & rCell,std::vector<SwNodeRange> & rRowNodes,SwNodeRange * const pLastCell)1820 void SwXText::ConvertCell(
1821     const uno::Sequence< uno::Reference< text::XTextRange > > & rCell,
1822     std::vector<SwNodeRange> & rRowNodes,
1823     SwNodeRange *const pLastCell)
1824 {
1825     if (rCell.getLength() != 2)
1826     {
1827         throw lang::IllegalArgumentException(
1828                 u"rCell needs to contain 2 elements"_ustr,
1829                 uno::Reference< text::XTextCopy >( this ), sal_Int16( 2 ) );
1830     }
1831     const uno::Reference<text::XTextRange>& xStartRange = rCell[0];
1832     const uno::Reference<text::XTextRange>& xEndRange = rCell[1];
1833     SwUnoInternalPaM aStartCellPam(*m_pDoc);
1834     SwUnoInternalPaM aEndCellPam(*m_pDoc);
1835 
1836     // !!! TODO - PaMs in tables and sections do not work here -
1837     //     the same applies to PaMs in frames !!!
1838 
1839     if (!::sw::XTextRangeToSwPaM(aStartCellPam, xStartRange) ||
1840         !::sw::XTextRangeToSwPaM(aEndCellPam, xEndRange))
1841     {
1842         throw lang::IllegalArgumentException(
1843                 u"Start or End range cannot be resolved to a SwPaM"_ustr,
1844                 uno::Reference< text::XTextCopy >( this ), sal_Int16( 2 ) );
1845     }
1846 
1847     SwNodeRange aTmpRange(aStartCellPam.Start()->GetNode(),
1848                           aEndCellPam.End()->GetNode());
1849     std::optional<SwNodeRange> oCorrectedRange;
1850     m_pDoc->GetNodes().ExpandRangeForTableBox(aTmpRange, oCorrectedRange);
1851 
1852     if (oCorrectedRange)
1853     {
1854         SwPaM aNewStartPaM(oCorrectedRange->aStart, 0);
1855         aStartCellPam = aNewStartPaM;
1856 
1857         sal_Int32 nEndLen = 0;
1858         SwTextNode * pTextNode = oCorrectedRange->aEnd.GetNode().GetTextNode();
1859         if (pTextNode != nullptr)
1860             nEndLen = pTextNode->Len();
1861 
1862         SwPaM aNewEndPaM(oCorrectedRange->aEnd, nEndLen);
1863         aEndCellPam = aNewEndPaM;
1864 
1865         oCorrectedRange.reset();
1866     }
1867 
1868     /** check the nodes between start and end
1869         it is allowed to have pairs of StartNode/EndNodes
1870      */
1871     if (aStartCellPam.Start()->GetNode() < aEndCellPam.End()->GetNode())
1872     {
1873         // increment on each StartNode and decrement on each EndNode
1874         // we must reach zero at the end and must not go below zero
1875         tools::Long nOpenNodeBlock = 0;
1876         SwNodeIndex aCellIndex(aStartCellPam.Start()->GetNode());
1877         while (aCellIndex < aEndCellPam.End()->GetNodeIndex())
1878         {
1879             if (aCellIndex.GetNode().IsStartNode())
1880             {
1881                 ++nOpenNodeBlock;
1882             }
1883             else if (aCellIndex.GetNode().IsEndNode())
1884             {
1885                 --nOpenNodeBlock;
1886             }
1887             if (nOpenNodeBlock < 0)
1888             {
1889                 throw lang::IllegalArgumentException();
1890             }
1891             ++aCellIndex;
1892         }
1893         if (nOpenNodeBlock != 0)
1894         {
1895             throw lang::IllegalArgumentException();
1896         }
1897     }
1898 
1899     /** The vector<vector> NodeRanges has to contain consecutive nodes.
1900         In rTableRanges the ranges don't need to be full paragraphs but
1901         they have to follow each other. To process the ranges they
1902         have to be aligned on paragraph borders by inserting paragraph
1903         breaks. Non-consecutive ranges must initiate an exception.
1904      */
1905     if (!pLastCell) // first cell?
1906     {
1907         // align the beginning - if necessary
1908         if (aStartCellPam.Start()->GetContentIndex())
1909         {
1910             m_pDoc->getIDocumentContentOperations().SplitNode(*aStartCellPam.Start(), false);
1911         }
1912     }
1913     else
1914     {
1915         // check the predecessor
1916         const SwNodeOffset nStartCellNodeIndex =
1917             aStartCellPam.Start()->GetNodeIndex();
1918         const SwNodeOffset nLastNodeEndIndex = pLastCell->aEnd.GetIndex();
1919         if (nLastNodeEndIndex == nStartCellNodeIndex)
1920         {
1921             // same node as predecessor then equal nContent?
1922             if (0 != aStartCellPam.Start()->GetContentIndex())
1923             {
1924                 throw lang::IllegalArgumentException();
1925             }
1926 
1927             m_pDoc->getIDocumentContentOperations().SplitNode(*aStartCellPam.Start(), false);
1928             SwNodeOffset const nNewIndex(aStartCellPam.Start()->GetNodeIndex());
1929             if (nNewIndex != nStartCellNodeIndex)
1930             {
1931                 // aStartCellPam now points to the 2nd node
1932                 // the last cell may *also* point to 2nd node now - fix it!
1933                 assert(nNewIndex == nStartCellNodeIndex + 1);
1934                 if (pLastCell->aEnd.GetIndex() == nNewIndex)
1935                 {
1936                     --pLastCell->aEnd;
1937                     if (pLastCell->aStart.GetIndex() == nNewIndex)
1938                     {
1939                         --pLastCell->aStart;
1940                     }
1941                 }
1942             }
1943         }
1944         else if (nStartCellNodeIndex == (nLastNodeEndIndex + 1))
1945         {
1946             // next paragraph - now the content index of the new should be 0
1947             // and of the old one should be equal to the text length
1948             // but if it isn't we don't care - the cell is being inserted on
1949             // the node border anyway
1950         }
1951         else
1952         {
1953             throw lang::IllegalArgumentException();
1954         }
1955     }
1956     // now check if there's a need to insert another paragraph break
1957     if (aEndCellPam.End()->GetContentIndex() <
1958             aEndCellPam.End()->GetNode().GetTextNode()->Len())
1959     {
1960         m_pDoc->getIDocumentContentOperations().SplitNode(*aEndCellPam.End(), false);
1961         // take care that the new start/endcell is moved to the right position
1962         // aStartCellPam has to point to the start of the new (previous) node
1963         // aEndCellPam has to point to the end of the new (previous) node
1964         aStartCellPam.DeleteMark();
1965         aStartCellPam.Move(fnMoveBackward, GoInNode);
1966         aStartCellPam.GetPoint()->SetContent(0);
1967         aEndCellPam.DeleteMark();
1968         aEndCellPam.Move(fnMoveBackward, GoInNode);
1969         aEndCellPam.GetPoint()->SetContent(
1970             aEndCellPam.GetPointNode().GetTextNode()->Len() );
1971     }
1972 
1973     assert(aStartCellPam.Start()->GetContentIndex() == 0);
1974     assert(aEndCellPam.End()->GetContentIndex() == aEndCellPam.End()->GetNode().GetTextNode()->Len());
1975     SwNodeRange aCellRange(aStartCellPam.Start()->GetNode(),
1976             aEndCellPam.End()->GetNode());
1977     rRowNodes.push_back(aCellRange); // note: invalidates pLastCell!
1978 
1979     // tdf#149649 delete any fieldmarks overlapping the cell
1980     IDocumentMarkAccess & rIDMA(*m_pDoc->getIDocumentMarkAccess());
1981     while (sw::mark::Fieldmark *const pMark = rIDMA.getInnerFieldmarkFor(*aStartCellPam.Start()))
1982     {
1983         if (pMark->GetMarkEnd() <= *aEndCellPam.End())
1984         {
1985             if (pMark->GetMarkStart() < *aStartCellPam.Start())
1986             {
1987                 SAL_INFO("sw.uno", "deleting fieldmark overlapping table cell");
1988                 rIDMA.deleteMark(pMark);
1989             }
1990             else
1991             {
1992                 break;
1993             }
1994         }
1995         else
1996         {
1997             SwPosition const sepPos(::sw::mark::FindFieldSep(*pMark));
1998             if (*aStartCellPam.Start() <= sepPos && sepPos <= *aEndCellPam.End())
1999             {
2000                 SAL_INFO("sw.uno", "deleting fieldmark with separator in table cell");
2001                 rIDMA.deleteMark(pMark);
2002             }
2003             else
2004             {
2005                 break;
2006             }
2007         }
2008     }
2009     while (sw::mark::Fieldmark *const pMark = rIDMA.getInnerFieldmarkFor(*aEndCellPam.End()))
2010     {
2011         auto [rMarkStart, rMarkEnd] = pMark->GetMarkStartEnd();
2012         if (*aStartCellPam.Start() <= rMarkStart)
2013         {
2014             if (*aEndCellPam.End() < rMarkEnd)
2015             {
2016                 SAL_INFO("sw.uno", "deleting fieldmark overlapping table cell");
2017                 rIDMA.deleteMark(pMark);
2018             }
2019             else
2020             {
2021                 break;
2022             }
2023         }
2024         else
2025         {
2026             SwPosition const sepPos(::sw::mark::FindFieldSep(*pMark));
2027             if (*aStartCellPam.Start() <= sepPos && sepPos <= *aEndCellPam.End())
2028             {
2029                 SAL_INFO("sw.uno", "deleting fieldmark with separator in table cell");
2030                 rIDMA.deleteMark(pMark);
2031             }
2032             else
2033             {
2034                 break;
2035             }
2036        }
2037     }
2038 }
2039 
2040 typedef uno::Sequence< text::TableColumnSeparator > TableColumnSeparators;
2041 
2042 static void
lcl_ApplyRowProperties(uno::Sequence<beans::PropertyValue> const & rRowProperties,uno::Any const & rRow,TableColumnSeparators & rRowSeparators)2043 lcl_ApplyRowProperties(
2044     uno::Sequence<beans::PropertyValue> const& rRowProperties,
2045     uno::Any const& rRow,
2046     TableColumnSeparators & rRowSeparators)
2047 {
2048     uno::Reference< beans::XPropertySet > xRow;
2049     rRow >>= xRow;
2050     for (const beans::PropertyValue& rProperty : rRowProperties)
2051     {
2052         if ( rProperty.Name == "TableColumnSeparators" )
2053         {
2054             // add the separators to access the cell's positions
2055             // for vertical merging later
2056             TableColumnSeparators aSeparators;
2057             rProperty.Value >>= aSeparators;
2058             rRowSeparators = std::move(aSeparators);
2059         }
2060         xRow->setPropertyValue(rProperty.Name, rProperty.Value);
2061     }
2062 }
2063 
lcl_GetLeftPos(sal_Int32 nCell,TableColumnSeparators const & rRowSeparators)2064 static sal_Int32 lcl_GetLeftPos(sal_Int32 nCell, TableColumnSeparators const& rRowSeparators)
2065 {
2066     if(!nCell)
2067         return 0;
2068     if (rRowSeparators.getLength() < nCell)
2069         return -1;
2070     return rRowSeparators[nCell - 1].Position;
2071 }
2072 
2073 static void
lcl_ApplyCellProperties(const sal_Int32 nLeftPos,const uno::Sequence<beans::PropertyValue> & rCellProperties,const uno::Reference<uno::XInterface> & xCell,std::vector<VerticallyMergedCell> & rMergedCells)2074 lcl_ApplyCellProperties(
2075     const sal_Int32 nLeftPos,
2076     const uno::Sequence< beans::PropertyValue >& rCellProperties,
2077     const uno::Reference< uno::XInterface >& xCell,
2078     std::vector<VerticallyMergedCell> & rMergedCells)
2079 {
2080     const uno::Reference< beans::XPropertySet > xCellPS(xCell, uno::UNO_QUERY);
2081     for (const auto& rCellProperty : rCellProperties)
2082     {
2083         const OUString & rName  = rCellProperty.Name;
2084         const uno::Any & rValue = rCellProperty.Value;
2085         if ( rName == "VerticalMerge" )
2086         {
2087             // determine left border position
2088             // add the cell to a queue of merged cells
2089             bool bMerge = false;
2090             rValue >>= bMerge;
2091             if (bMerge)
2092             {
2093                 // 'close' all the cell with the same left position
2094                 // if separate vertical merges in the same column exist
2095                 for(auto& aMergedCell : rMergedCells)
2096                 {
2097                     if(lcl_SimilarPosition(aMergedCell.nLeftPosition, nLeftPos))
2098                     {
2099                         aMergedCell.bOpen = false;
2100                     }
2101                 }
2102                 // add the new group of merged cells
2103                 rMergedCells.emplace_back(xCellPS, nLeftPos);
2104             }
2105             else
2106             {
2107                 bool bFound = false;
2108                 SAL_WARN_IF(rMergedCells.empty(), "sw.uno", "the first merged cell is missing");
2109                 for(auto& aMergedCell : rMergedCells)
2110                 {
2111                     if (aMergedCell.bOpen && lcl_SimilarPosition(aMergedCell.nLeftPosition, nLeftPos))
2112                     {
2113                         aMergedCell.aCells.push_back( xCellPS );
2114                         bFound = true;
2115                     }
2116                 }
2117                 SAL_WARN_IF(!bFound, "sw.uno", "couldn't find first vertically merged cell" );
2118             }
2119         }
2120         else
2121         {
2122             try
2123             {
2124                 static const std::initializer_list<std::u16string_view> vDenylist = {
2125                     u"LeftMargin",
2126                     u"ParaTopBorder",
2127                     u"ParaTopBorderDistance",
2128                     u"ParaTopBorderComplexColor",
2129                     u"ParaLeftBorder",
2130                     u"ParaLeftBorderDistance",
2131                     u"ParaLeftBorderComplexColor",
2132                     u"ParaBottomBorder",
2133                     u"ParaBottomBorderDistance",
2134                     u"ParaBottomBorderComplexColor",
2135                     u"ParaRightBorder",
2136                     u"ParaRightBorderDistance",
2137                     u"ParaRightBorderComplexColor",
2138                 };
2139                 if (std::find(vDenylist.begin(), vDenylist.end(), rName) == vDenylist.end())
2140                 {
2141                     xCellPS->setPropertyValue(rName, rValue);
2142                 }
2143             }
2144             catch (const uno::Exception&)
2145             {
2146                 TOOLS_WARN_EXCEPTION( "sw.uno", "Exception when setting cell property " << rName );
2147             }
2148         }
2149     }
2150 }
2151 
2152 static void
lcl_MergeCells(std::vector<VerticallyMergedCell> & rMergedCells)2153 lcl_MergeCells(std::vector<VerticallyMergedCell> & rMergedCells)
2154 {
2155     for(auto& aMergedCell : rMergedCells)
2156     {
2157         // the first of the cells gets the number of cells set as RowSpan
2158         // the others get the inverted number of remaining merged cells
2159         // (3,-2,-1)
2160         sal_Int32 nCellCount = static_cast<sal_Int32>(aMergedCell.aCells.size());
2161         if(nCellCount<2)
2162         {
2163             SAL_WARN("sw.uno", "incomplete vertical cell merge");
2164             continue;
2165         }
2166         aMergedCell.aCells.front()->setPropertyValue(UNO_NAME_ROW_SPAN, uno::Any(nCellCount--));
2167         nCellCount*=-1;
2168         for(auto pxPSet = aMergedCell.aCells.begin()+1; nCellCount<0; ++pxPSet, ++nCellCount)
2169         {
2170             (*pxPSet)->setPropertyValue(UNO_NAME_ROW_SPAN, uno::Any(nCellCount));
2171             (*pxPSet)->setPropertyValue(u"VerticalMerge"_ustr, uno::Any(true));
2172         }
2173     }
2174 }
2175 
2176 uno::Reference< text::XTextTable > SAL_CALL
convertToTable(const uno::Sequence<uno::Sequence<uno::Sequence<uno::Reference<text::XTextRange>>>> & rTableRanges,const uno::Sequence<uno::Sequence<uno::Sequence<beans::PropertyValue>>> & rCellProperties,const uno::Sequence<uno::Sequence<beans::PropertyValue>> & rRowProperties,const uno::Sequence<beans::PropertyValue> & rTableProperties)2177 SwXText::convertToTable(
2178     const uno::Sequence< uno::Sequence< uno::Sequence<
2179         uno::Reference< text::XTextRange > > > >& rTableRanges,
2180     const uno::Sequence< uno::Sequence< uno::Sequence<
2181         beans::PropertyValue > > >& rCellProperties,
2182     const uno::Sequence< uno::Sequence< beans::PropertyValue > >&
2183         rRowProperties,
2184     const uno::Sequence< beans::PropertyValue >& rTableProperties)
2185 {
2186     return uno::Reference< text::XTextTable >(convertToSwTable(rTableRanges, rCellProperties, rRowProperties, rTableProperties));
2187 }
2188 
2189 rtl::Reference< SwXTextTable >
convertToSwTable(const uno::Sequence<uno::Sequence<uno::Sequence<uno::Reference<text::XTextRange>>>> & rTableRanges,const uno::Sequence<uno::Sequence<uno::Sequence<beans::PropertyValue>>> & rCellProperties,const uno::Sequence<uno::Sequence<beans::PropertyValue>> & rRowProperties,const uno::Sequence<beans::PropertyValue> & rTableProperties)2190 SwXText::convertToSwTable(
2191     const uno::Sequence< uno::Sequence< uno::Sequence<
2192         uno::Reference< text::XTextRange > > > >& rTableRanges,
2193     const uno::Sequence< uno::Sequence< uno::Sequence<
2194         beans::PropertyValue > > >& rCellProperties,
2195     const uno::Sequence< uno::Sequence< beans::PropertyValue > >&
2196         rRowProperties,
2197     const uno::Sequence< beans::PropertyValue >& rTableProperties)
2198 {
2199     SolarMutexGuard aGuard;
2200 
2201     if(!IsValid())
2202     {
2203         throw  uno::RuntimeException();
2204     }
2205 
2206     IDocumentRedlineAccess & rIDRA(m_pDoc->getIDocumentRedlineAccess());
2207     if (!IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()))
2208     {
2209         throw uno::RuntimeException(
2210             u"cannot convertToTable if tracked changes are hidden!"_ustr);
2211     }
2212 
2213     //at first collect the text ranges as SwPaMs
2214     const uno::Sequence< uno::Sequence< uno::Reference< text::XTextRange > > >*
2215         pTableRanges = rTableRanges.getConstArray();
2216     std::vector< std::vector<SwNodeRange> > aTableNodes;
2217     for (sal_Int32 nRow = 0; nRow < rTableRanges.getLength(); ++nRow)
2218     {
2219         std::vector<SwNodeRange> aRowNodes;
2220         const uno::Sequence< uno::Reference< text::XTextRange > >* pRow =
2221             pTableRanges[nRow].getConstArray();
2222         const sal_Int32 nCells(pTableRanges[nRow].getLength());
2223 
2224         if (0 == nCells) // this would lead to no pLastCell below
2225         {                // and make it impossible to detect node gaps
2226             throw lang::IllegalArgumentException();
2227         }
2228 
2229         for (sal_Int32 nCell = 0; nCell < nCells; ++nCell)
2230         {
2231             SwNodeRange *const pLastCell(
2232                 (nCell == 0)
2233                     ? ((nRow == 0)
2234                         ? nullptr
2235                         : &*aTableNodes.rbegin()->rbegin())
2236                     : &*aRowNodes.rbegin());
2237             ConvertCell(pRow[nCell], aRowNodes, pLastCell);
2238         }
2239         assert(!aRowNodes.empty());
2240         aTableNodes.push_back(std::move(aRowNodes));
2241     }
2242 
2243     std::vector< TableColumnSeparators >
2244         aRowSeparators(rRowProperties.getLength());
2245     std::vector<VerticallyMergedCell> aMergedCells;
2246 
2247     SwTable const*const pTable = m_pDoc->TextToTable( aTableNodes );
2248 
2249     if (!pTable)
2250         return {};
2251 
2252     rtl::Reference<SwXTextTable> const xRet =
2253         SwXTextTable::CreateXTextTable(pTable->GetFrameFormat());
2254     // set properties to the table
2255     // catch lang::WrappedTargetException and lang::IndexOutOfBoundsException
2256     try
2257     {
2258         //apply table properties
2259         for(const auto& rTableProperty : rTableProperties)
2260         {
2261             try
2262             {
2263                 static constexpr auto vDenylist = frozen::make_unordered_set<std::u16string_view>({
2264                     u"BottomBorder",
2265                     u"CharAutoKerning",
2266                     u"CharFontName",
2267                     u"CharFontNameAsian",
2268                     u"CharFontNameComplex",
2269                     u"CharHeight",
2270                     u"CharHeightAsian",
2271                     u"CharHeightComplex",
2272                     u"CharInteropGrabBag",
2273                     u"CharLocale",
2274                     u"CharLocaleAsian",
2275                     u"CharLocaleComplex",
2276                     u"HorizontalBorder",
2277                     u"LeftBorder",
2278                     u"ParaAdjust",
2279                     u"ParaBottomMargin",
2280                     u"ParaIsHyphenation",
2281                     u"ParaLineSpacing",
2282                     u"ParaOrphans",
2283                     u"ParaTopMargin",
2284                     u"ParaWidows",
2285                     u"RightBorder",
2286                     u"TopBorder",
2287                     u"VerticalBorder",
2288                     u"ParaTopBorder",
2289                     u"ParaTopBorderDistance",
2290                     u"ParaTopBorderComplexColor",
2291                     u"ParaLeftBorder",
2292                     u"ParaLeftBorderDistance",
2293                     u"ParaLeftBorderComplexColor",
2294                     u"ParaBottomBorder",
2295                     u"ParaBottomBorderDistance",
2296                     u"ParaBottomBorderComplexColor",
2297                     u"ParaRightBorder",
2298                     u"ParaRightBorderDistance",
2299                     u"ParaRightBorderComplexColor",
2300                 });
2301                 if (vDenylist.find(rTableProperty.Name) == vDenylist.end())
2302                 {
2303                     xRet->setPropertyValue(rTableProperty.Name, rTableProperty.Value);
2304                 }
2305             }
2306             catch (const uno::Exception&)
2307             {
2308                 TOOLS_WARN_EXCEPTION( "sw.uno", "Exception when setting property: " << rTableProperty.Name  );
2309             }
2310         }
2311 
2312         //apply row properties
2313         const auto xRows = xRet->getRows();
2314         const sal_Int32 nLast = std::min(xRows->getCount(), rRowProperties.getLength());
2315         SAL_WARN_IF(nLast != rRowProperties.getLength(), "sw.uno", "not enough rows for properties");
2316         for(sal_Int32 nCnt = 0; nCnt < nLast; ++nCnt)
2317             lcl_ApplyRowProperties(rRowProperties[nCnt], xRows->getByIndex(nCnt), aRowSeparators[nCnt]);
2318 
2319         //apply cell properties
2320         sal_Int32 nRow = 0;
2321         for(const auto& rCellPropertiesForRow : rCellProperties)
2322         {
2323             sal_Int32 nCell = 0;
2324             for(const auto& rCellProps : rCellPropertiesForRow)
2325             {
2326                 lcl_ApplyCellProperties(lcl_GetLeftPos(nCell, aRowSeparators[nRow]),
2327                     rCellProps,
2328                     xRet->getCellByPosition(nCell, nRow),
2329                     aMergedCells);
2330                 ++nCell;
2331             }
2332             ++nRow;
2333         }
2334 
2335         // now that the cell properties are set the vertical merge values
2336         // have to be applied
2337         lcl_MergeCells(aMergedCells);
2338     }
2339     catch (const lang::WrappedTargetException&)
2340     {
2341     }
2342     catch (const lang::IndexOutOfBoundsException&)
2343     {
2344     }
2345 
2346     assert(SwTable::FindTable(pTable->GetFrameFormat()) == pTable);
2347     assert(pTable->GetFrameFormat() == xRet->GetFrameFormat());
2348     return xRet;
2349 }
2350 
2351 void SAL_CALL
copyText(const uno::Reference<text::XTextCopy> & xSource)2352 SwXText::copyText(
2353     const uno::Reference< text::XTextCopy >& xSource )
2354 {
2355     SolarMutexGuard aGuard;
2356 
2357     SwXText* const pSource(dynamic_cast<SwXText*>(xSource.get()));
2358     if (!pSource)
2359         throw uno::RuntimeException();
2360 
2361     rtl::Reference< SwXTextCursor > const xCursor = pSource->createXTextCursor();
2362     xCursor->gotoEnd( true );
2363 
2364     SwNodeIndex rNdIndex( *GetStartNode( ), 1 );
2365     SwPosition rPos( rNdIndex );
2366     // tdf#112202 need SwXText because cursor cannot select table at the start
2367     SwTextNode * pFirstNode;
2368     {
2369         SwPaM temp(*pSource->GetStartNode(), *pSource->GetStartNode()->EndOfSectionNode(), SwNodeOffset(+1), SwNodeOffset(-1));
2370         pFirstNode = temp.GetMark()->GetNode().GetTextNode();
2371         if (pFirstNode)
2372         {
2373             temp.GetMark()->AssignStartIndex(*pFirstNode);
2374         }
2375         if (SwTextNode *const pNode = temp.GetPoint()->GetNode().GetTextNode())
2376         {
2377             temp.GetPoint()->AssignEndIndex(*pNode);
2378         }
2379         // Explicitly request copy text mode, so
2380         // sw::DocumentContentOperationsManager::CopyFlyInFlyImpl() will copy shapes anchored to
2381         // us, even if we have only a single paragraph.
2382         m_pDoc->getIDocumentContentOperations().CopyRange(temp, rPos, SwCopyFlags::CheckPosInFly);
2383     }
2384 }
2385 
SwXBodyText(SwDoc * const pDoc)2386 SwXBodyText::SwXBodyText(SwDoc *const pDoc)
2387     : SwXText(pDoc, CursorType::Body)
2388 {
2389 }
2390 
~SwXBodyText()2391 SwXBodyText::~SwXBodyText()
2392 {
2393 }
2394 
2395 OUString SAL_CALL
getImplementationName()2396 SwXBodyText::getImplementationName()
2397 {
2398     return u"SwXBodyText"_ustr;
2399 }
2400 
supportsService(const OUString & rServiceName)2401 sal_Bool SAL_CALL SwXBodyText::supportsService(const OUString& rServiceName)
2402 {
2403     return cppu::supportsService(this, rServiceName);
2404 }
2405 
2406 uno::Sequence< OUString > SAL_CALL
getSupportedServiceNames()2407 SwXBodyText::getSupportedServiceNames()
2408 {
2409     return { u"com.sun.star.text.Text"_ustr };
2410 }
2411 
2412 uno::Sequence< uno::Type > SAL_CALL
getTypes()2413 SwXBodyText::getTypes()
2414 {
2415     const uno::Sequence< uno::Type > aTypes = SwXBodyText_Base::getTypes();
2416     const uno::Sequence< uno::Type > aTextTypes = SwXText::getTypes();
2417     return ::comphelper::concatSequences(aTypes, aTextTypes);
2418 }
2419 
2420 uno::Sequence< sal_Int8 > SAL_CALL
getImplementationId()2421 SwXBodyText::getImplementationId()
2422 {
2423     return css::uno::Sequence<sal_Int8>();
2424 }
2425 
2426 uno::Any SAL_CALL
queryInterface(const uno::Type & rType)2427 SwXBodyText::queryInterface(const uno::Type& rType)
2428 {
2429     const uno::Any ret = SwXText::queryInterface(rType);
2430     return (ret.getValueType() == cppu::UnoType<void>::get())
2431         ?   SwXBodyText_Base::queryInterface(rType)
2432         :   ret;
2433 }
2434 
CreateTextCursor(const bool bIgnoreTables)2435 rtl::Reference<SwXTextCursor> SwXBodyText::CreateTextCursor(const bool bIgnoreTables)
2436 {
2437     if(!IsValid())
2438     {
2439         return nullptr;
2440     }
2441 
2442     // the cursor has to skip tables contained in this text
2443     SwPaM aPam(GetDoc()->GetNodes().GetEndOfContent());
2444     aPam.Move( fnMoveBackward, GoInDoc );
2445     if (!bIgnoreTables)
2446     {
2447         SwTableNode * pTableNode = aPam.GetPointNode().FindTableNode();
2448         while (pTableNode)
2449         {
2450             aPam.GetPoint()->Assign( *pTableNode->EndOfSectionNode() );
2451             SwContentNode* pCont = SwNodes::GoNext(aPam.GetPoint());
2452             pTableNode = pCont->FindTableNode();
2453         }
2454     }
2455     return new SwXTextCursor(*GetDoc(), this, CursorType::Body, *aPam.GetPoint());
2456 }
2457 
2458 rtl::Reference< SwXTextCursor >
createXTextCursor()2459 SwXBodyText::createXTextCursor()
2460 {
2461     return CreateTextCursor();
2462 }
2463 
2464 rtl::Reference< SwXTextCursor >
createXTextCursorByRange(const uno::Reference<text::XTextRange> & xTextPosition)2465 SwXBodyText::createXTextCursorByRange(
2466     const uno::Reference< text::XTextRange > & xTextPosition)
2467 {
2468     SwUnoInternalPaM aPam(*GetDoc());
2469     if (!::sw::XTextRangeToSwPaM(aPam, xTextPosition))
2470         throw uno::RuntimeException();
2471     return createXTextCursorByRangeImpl(aPam);
2472 }
2473 
createXTextCursorByRangeImpl(SwUnoInternalPaM & rPam)2474 rtl::Reference< SwXTextCursor > SwXBodyText::createXTextCursorByRangeImpl(
2475         SwUnoInternalPaM& rPam)
2476 {
2477     if(!IsValid())
2478         throw uno::RuntimeException(cInvalidObject);
2479 
2480     if ( !rPam.GetPointNode().GetTextNode() )
2481         throw uno::RuntimeException(u"Invalid text range"_ustr );
2482 
2483     SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
2484 
2485     SwStartNode* p1 = rPam.GetPointNode().StartOfSectionNode();
2486     //document starts with a section?
2487     while(p1->IsSectionNode())
2488     {
2489         p1 = p1->StartOfSectionNode();
2490     }
2491     SwStartNode *const p2 = rNode.StartOfSectionNode();
2492 
2493     if(p1 != p2)
2494         throw uno::RuntimeException( u"End of content node doesn't have the proper start node"_ustr,
2495                uno::Reference< uno::XInterface >( *this ) );
2496 
2497     rtl::Reference< SwXTextCursor > xRef = new SwXTextCursor(*GetDoc(), this, CursorType::Body,
2498                     *rPam.GetPoint(), rPam.GetMark());
2499     return xRef;
2500 }
2501 
2502 uno::Reference< container::XEnumeration > SAL_CALL
createEnumeration()2503 SwXBodyText::createEnumeration()
2504 {
2505     return createParagraphEnumeration();
2506 }
2507 
2508 rtl::Reference< SwXParagraphEnumeration >
createParagraphEnumeration()2509 SwXBodyText::createParagraphEnumeration()
2510 {
2511     SolarMutexGuard aGuard;
2512 
2513     if (!IsValid())
2514         throw uno::RuntimeException(cInvalidObject);
2515 
2516     SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
2517     SwPosition aPos(rNode);
2518     auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
2519     pUnoCursor->Move(fnMoveBackward, GoInDoc);
2520     return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Body);
2521 }
2522 
2523 uno::Type SAL_CALL
getElementType()2524 SwXBodyText::getElementType()
2525 {
2526     return cppu::UnoType<text::XTextRange>::get();
2527 }
2528 
2529 sal_Bool SAL_CALL
hasElements()2530 SwXBodyText::hasElements()
2531 {
2532     SolarMutexGuard aGuard;
2533 
2534     if (!IsValid())
2535         throw uno::RuntimeException(cInvalidObject);
2536 
2537     return true;
2538 }
2539 
2540 class SwXHeadFootText::Impl
2541     : public SvtListener
2542 {
2543     public:
2544         SwFrameFormat* m_pHeadFootFormat;
2545         bool m_bIsHeader;
2546 
Impl(SwFrameFormat & rHeadFootFormat,const bool bIsHeader)2547         Impl(SwFrameFormat& rHeadFootFormat, const bool bIsHeader)
2548             : m_pHeadFootFormat(&rHeadFootFormat)
2549             , m_bIsHeader(bIsHeader)
2550         {
2551             StartListening(m_pHeadFootFormat->GetNotifier());
2552         }
2553 
GetHeadFootFormat() const2554         SwFrameFormat* GetHeadFootFormat() const {
2555             return m_pHeadFootFormat;
2556         }
2557 
GetHeadFootFormatOrThrow()2558         SwFrameFormat& GetHeadFootFormatOrThrow() {
2559             if (!m_pHeadFootFormat) {
2560                 throw uno::RuntimeException(u"SwXHeadFootText: disposed or invalid"_ustr, nullptr);
2561             }
2562             return *m_pHeadFootFormat;
2563         }
2564     protected:
Notify(const SfxHint & rHint)2565         virtual void Notify(const SfxHint& rHint) override
2566         {
2567             if(rHint.GetId() == SfxHintId::Dying)
2568                 m_pHeadFootFormat = nullptr;
2569         }
2570 };
2571 
CreateXHeadFootText(SwFrameFormat & rHeadFootFormat,const bool bIsHeader)2572 rtl::Reference<SwXHeadFootText> SwXHeadFootText::CreateXHeadFootText(
2573         SwFrameFormat& rHeadFootFormat,
2574         const bool bIsHeader)
2575 {
2576     // re-use existing SwXHeadFootText
2577     // #i105557#: do not iterate over the registered clients: race condition
2578     rtl::Reference<SwXHeadFootText> xText = dynamic_cast<SwXHeadFootText*>(rHeadFootFormat.GetXObject().get().get());
2579     if(!xText.is())
2580     {
2581         xText = new SwXHeadFootText(rHeadFootFormat, bIsHeader);
2582         rHeadFootFormat.SetXObject(static_cast<cppu::OWeakObject*>(xText.get()));
2583     }
2584     return xText;
2585 }
2586 
SwXHeadFootText(SwFrameFormat & rHeadFootFormat,const bool bIsHeader)2587 SwXHeadFootText::SwXHeadFootText(SwFrameFormat& rHeadFootFormat, const bool bIsHeader)
2588     : SwXText(
2589             &rHeadFootFormat.GetDoc(),
2590             bIsHeader ? CursorType::Header : CursorType::Footer)
2591     , m_pImpl(new SwXHeadFootText::Impl(rHeadFootFormat, bIsHeader))
2592 {
2593 }
2594 
~SwXHeadFootText()2595 SwXHeadFootText::~SwXHeadFootText()
2596 { }
2597 
2598 OUString SAL_CALL
getImplementationName()2599 SwXHeadFootText::getImplementationName()
2600 {
2601   return {u"SwXHeadFootText"_ustr};
2602 }
2603 
supportsService(const OUString & rServiceName)2604 sal_Bool SAL_CALL SwXHeadFootText::supportsService(const OUString& rServiceName)
2605 {
2606     return cppu::supportsService(this, rServiceName);
2607 }
2608 
2609 uno::Sequence<OUString> SAL_CALL
getSupportedServiceNames()2610 SwXHeadFootText::getSupportedServiceNames()
2611 {
2612     return {u"com.sun.star.text.Text"_ustr};
2613 }
2614 
GetStartNode() const2615 const SwStartNode* SwXHeadFootText::GetStartNode() const
2616 {
2617     const SwStartNode* pSttNd = nullptr;
2618     SwFrameFormat* const pHeadFootFormat = m_pImpl->GetHeadFootFormat();
2619     if(pHeadFootFormat)
2620     {
2621         const SwFormatContent& rFlyContent = pHeadFootFormat->GetContent();
2622         if(rFlyContent.GetContentIdx())
2623         {
2624             pSttNd = rFlyContent.GetContentIdx()->GetNode().GetStartNode();
2625         }
2626     }
2627     return pSttNd;
2628 }
2629 
getTypes()2630 uno::Sequence<uno::Type> SAL_CALL SwXHeadFootText::getTypes()
2631 {
2632     return ::comphelper::concatSequences(
2633         SwXHeadFootText_Base::getTypes(),
2634         SwXText::getTypes());
2635 }
2636 
getImplementationId()2637 uno::Sequence<sal_Int8> SAL_CALL SwXHeadFootText::getImplementationId()
2638 {
2639     return css::uno::Sequence<sal_Int8>();
2640 }
2641 
queryInterface(const uno::Type & rType)2642 uno::Any SAL_CALL SwXHeadFootText::queryInterface(const uno::Type& rType)
2643 {
2644     const uno::Any ret = SwXHeadFootText_Base::queryInterface(rType);
2645     return (ret.getValueType() == cppu::UnoType<void>::get())
2646         ? SwXText::queryInterface(rType)
2647         : ret;
2648 }
2649 
CreateTextCursor(const bool bIgnoreTables)2650 rtl::Reference<SwXTextCursor> SwXHeadFootText::CreateTextCursor(const bool bIgnoreTables)
2651 {
2652     SwFrameFormat & rHeadFootFormat( m_pImpl->GetHeadFootFormatOrThrow() );
2653 
2654     const SwFormatContent& rFlyContent = rHeadFootFormat.GetContent();
2655     const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
2656     SwPosition aPos(rNode);
2657     rtl::Reference<SwXTextCursor> pXCursor = new SwXTextCursor(*GetDoc(), this,
2658             (m_pImpl->m_bIsHeader) ? CursorType::Header : CursorType::Footer, aPos);
2659     auto& rUnoCursor(pXCursor->GetCursor());
2660     rUnoCursor.Move(fnMoveForward, GoInNode);
2661 
2662     // save current start node to be able to check if there is content
2663     // after the table - otherwise the cursor would be in the body text!
2664     SwStartNode const*const pOwnStartNode = rNode.FindStartNodeByType(
2665             (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
2666 
2667     if (!bIgnoreTables)
2668     {
2669         // is there a table here?
2670         SwTableNode* pTableNode = rUnoCursor.GetPointNode().FindTableNode();
2671         while (pTableNode)
2672         {
2673             rUnoCursor.GetPoint()->Assign(*pTableNode->EndOfSectionNode());
2674             SwContentNode* pCont = SwNodes::GoNext(rUnoCursor.GetPoint());
2675             pTableNode = pCont->FindTableNode();
2676         }
2677     }
2678     SwStartNode const*const pNewStartNode = rUnoCursor.GetPointNode().FindStartNodeByType(
2679             (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
2680     if (!pNewStartNode || (pNewStartNode != pOwnStartNode))
2681     {
2682         throw uno::RuntimeException(u"no text available"_ustr);
2683     }
2684     return pXCursor;
2685 }
2686 
2687 rtl::Reference< SwXTextCursor >
createXTextCursor()2688 SwXHeadFootText::createXTextCursor()
2689 {
2690     return CreateTextCursor(false);
2691 }
2692 
createXTextCursorByRange(const uno::Reference<text::XTextRange> & xTextPosition)2693 rtl::Reference<SwXTextCursor> SwXHeadFootText::createXTextCursorByRange(
2694     const uno::Reference<text::XTextRange>& xTextPosition)
2695 {
2696     SwUnoInternalPaM aPam(*GetDoc());
2697     if (!sw::XTextRangeToSwPaM(aPam, xTextPosition))
2698     {
2699         throw uno::RuntimeException(cInvalidObject);
2700     }
2701     return createXTextCursorByRangeImpl(aPam);
2702 }
2703 
createXTextCursorByRangeImpl(SwUnoInternalPaM & rPam)2704 rtl::Reference< SwXTextCursor > SwXHeadFootText::createXTextCursorByRangeImpl(
2705         SwUnoInternalPaM& rPam)
2706 {
2707     SwFrameFormat& rHeadFootFormat( m_pImpl->GetHeadFootFormatOrThrow() );
2708 
2709     SwNode& rNode = rHeadFootFormat.GetContent().GetContentIdx()->GetNode();
2710     SwPosition aPos(rNode);
2711     SwPaM aHFPam(aPos);
2712     aHFPam.Move(fnMoveForward, GoInNode);
2713     SwStartNode* const pOwnStartNode = aHFPam.GetPointNode().FindStartNodeByType(
2714             (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
2715     SwStartNode* const p1 = rPam.GetPointNode().FindStartNodeByType(
2716             (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
2717     if (p1 == pOwnStartNode)
2718     {
2719         return new SwXTextCursor(
2720                     *GetDoc(),
2721                     this,
2722                     (m_pImpl->m_bIsHeader) ? CursorType::Header : CursorType::Footer,
2723                     *rPam.GetPoint(), rPam.GetMark());
2724     }
2725     return nullptr;
2726 }
2727 
createEnumeration()2728 uno::Reference<container::XEnumeration> SAL_CALL SwXHeadFootText::createEnumeration()
2729 {
2730     SolarMutexGuard aGuard;
2731     SwFrameFormat& rHeadFootFormat(m_pImpl->GetHeadFootFormatOrThrow());
2732 
2733     const SwFormatContent& rFlyContent = rHeadFootFormat.GetContent();
2734     const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
2735     SwPosition aPos(rNode);
2736     auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
2737     pUnoCursor->Move(fnMoveForward, GoInNode);
2738     return SwXParagraphEnumeration::Create(
2739             this,
2740             pUnoCursor,
2741             (m_pImpl->m_bIsHeader)
2742                 ? CursorType::Header
2743                 : CursorType::Footer);
2744 }
2745 
getElementType()2746 uno::Type SAL_CALL SwXHeadFootText::getElementType()
2747     { return cppu::UnoType<text::XTextRange>::get(); }
2748 
hasElements()2749 sal_Bool SAL_CALL SwXHeadFootText::hasElements()
2750     { return true; }
2751 
2752 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2753