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