Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / unocore / unotext.cxx
blob4fede9da65b42ca00dd8384a4323355cddaee23c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <stdlib.h>
22 #include <memory>
23 #include <set>
25 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
26 #include <com/sun/star/text/ControlCharacter.hpp>
27 #include <com/sun/star/text/TableColumnSeparator.hpp>
28 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
29 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
31 #include <svl/listener.hxx>
32 #include <vcl/svapp.hxx>
33 #include <comphelper/profilezone.hxx>
34 #include <comphelper/sequence.hxx>
35 #include <comphelper/servicehelper.hxx>
36 #include <cppuhelper/exc_hlp.hxx>
37 #include <cppuhelper/supportsservice.hxx>
38 #include <sal/log.hxx>
39 #include <comphelper/diagnose_ex.hxx>
41 #include <cmdid.h>
42 #include <unotextbodyhf.hxx>
43 #include <unotext.hxx>
44 #include <unotextrange.hxx>
45 #include <unotextcursor.hxx>
46 #include <unosection.hxx>
47 #include <unobookmark.hxx>
48 #include <unorefmark.hxx>
49 #include <unoport.hxx>
50 #include <unotbl.hxx>
51 #include <unoidx.hxx>
52 #include <unocoll.hxx>
53 #include <unoframe.hxx>
54 #include <unofield.hxx>
55 #include <unometa.hxx>
56 #include <unomap.hxx>
57 #include <unoprnms.hxx>
58 #include <unoparagraph.hxx>
59 #include <unocrsrhelper.hxx>
60 #include <docary.hxx>
61 #include <doc.hxx>
62 #include <IDocumentRedlineAccess.hxx>
63 #include <IDocumentUndoRedo.hxx>
64 #include <bookmark.hxx>
65 #include <redline.hxx>
66 #include <swundo.hxx>
67 #include <section.hxx>
68 #include <fmtanchr.hxx>
69 #include <fmtcntnt.hxx>
70 #include <ndtxt.hxx>
71 #include <SwRewriter.hxx>
72 #include <strings.hrc>
73 #include <frameformats.hxx>
74 #include <unocontentcontrol.hxx>
76 using namespace ::com::sun::star;
78 constexpr OUStringLiteral cInvalidObject = u"this object is invalid";
80 class SwXText::Impl
83 public:
84 SwXText & m_rThis;
85 SfxItemPropertySet const& m_rPropSet;
86 const CursorType m_eType;
87 SwDoc * m_pDoc;
88 bool m_bIsValid;
90 Impl( SwXText & rThis,
91 SwDoc *const pDoc, const CursorType eType)
92 : m_rThis(rThis)
93 , m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT))
94 , m_eType(eType)
95 , m_pDoc(pDoc)
96 , m_bIsValid(nullptr != pDoc)
100 /// @throws lang::IllegalArgumentException
101 /// @throws uno::RuntimeException
102 uno::Reference< text::XTextRange >
103 finishOrAppendParagraph(
104 const uno::Sequence< beans::PropertyValue >&
105 rCharacterAndParagraphProperties,
106 const uno::Reference< text::XTextRange >& xInsertPosition);
108 /// @throws lang::IllegalArgumentException
109 /// @throws uno::RuntimeException
110 sal_Int16 ComparePositions(
111 const uno::Reference<text::XTextRange>& xPos1,
112 const uno::Reference<text::XTextRange>& xPos2);
114 /// @throws lang::IllegalArgumentException
115 /// @throws uno::RuntimeException
116 bool CheckForOwnMember(const SwPaM & rPaM);
118 void ConvertCell(
119 const uno::Sequence< uno::Reference< text::XTextRange > > & rCell,
120 std::vector<SwNodeRange> & rRowNodes,
121 SwNodeRange *const pLastCell);
125 SwXText::SwXText(SwDoc *const pDoc, const CursorType eType)
126 : m_pImpl( new SwXText::Impl(*this, pDoc, eType) )
130 SwXText::~SwXText()
134 const SwDoc * SwXText::GetDoc() const
136 return m_pImpl->m_pDoc;
139 SwDoc * SwXText::GetDoc()
141 return m_pImpl->m_pDoc;
144 bool SwXText::IsValid() const
146 return m_pImpl->m_bIsValid;
149 void SwXText::Invalidate()
151 m_pImpl->m_bIsValid = false;
154 void SwXText::SetDoc(SwDoc *const pDoc)
156 OSL_ENSURE(!m_pImpl->m_pDoc || !pDoc,
157 "SwXText::SetDoc: already have a doc?");
158 m_pImpl->m_pDoc = pDoc;
159 m_pImpl->m_bIsValid = (nullptr != pDoc);
162 void
163 SwXText::PrepareForAttach(uno::Reference< text::XTextRange > &, const SwPaM &)
167 bool SwXText::CheckForOwnMemberMeta(const SwPaM &, const bool)
169 OSL_ENSURE(CursorType::Meta != m_pImpl->m_eType, "should not be called!");
170 return false;
173 const SwStartNode *SwXText::GetStartNode() const
175 return GetDoc()->GetNodes().GetEndOfContent().StartOfSectionNode();
178 uno::Reference< text::XTextCursor > SAL_CALL SwXText::createTextCursor()
180 SolarMutexGuard aGuard;
181 rtl::Reference<SwXTextCursor> xCursor = createXTextCursor();
182 if (!xCursor.is())
184 uno::RuntimeException aRuntime;
185 aRuntime.Message = cInvalidObject;
186 throw aRuntime;
188 return static_cast<text::XWordCursor*>(xCursor.get());
191 rtl::Reference< SwXTextCursor >
192 SwXText::createXTextCursor()
194 rtl::Reference< SwXTextCursor > xRet;
195 if(IsValid())
197 SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
198 SwPosition aPos(rNode);
199 xRet = new SwXTextCursor(*GetDoc(), this, m_pImpl->m_eType, aPos);
200 xRet->gotoStart(false);
202 return xRet;
205 css::uno::Reference< css::text::XTextCursor > SAL_CALL SwXText::createTextCursorByRange(
206 const ::css::uno::Reference< ::css::text::XTextRange >& aTextPosition )
208 SolarMutexGuard aGuard;
209 return static_cast<text::XWordCursor*>(createXTextCursorByRange(aTextPosition).get());
213 uno::Any SAL_CALL
214 SwXText::queryInterface(const uno::Type& rType)
216 uno::Any aRet;
217 if (rType == cppu::UnoType<text::XText>::get())
219 aRet <<= uno::Reference< text::XText >(this);
221 else if (rType == cppu::UnoType<text::XSimpleText>::get())
223 aRet <<= uno::Reference< text::XSimpleText >(this);
225 else if (rType == cppu::UnoType<text::XTextRange>::get())
227 aRet <<= uno::Reference< text::XTextRange>(this);
229 else if (rType == cppu::UnoType<text::XTextRangeCompare>::get())
231 aRet <<= uno::Reference< text::XTextRangeCompare >(this);
233 else if (rType == cppu::UnoType<lang::XTypeProvider>::get())
235 aRet <<= uno::Reference< lang::XTypeProvider >(this);
237 else if (rType == cppu::UnoType<text::XRelativeTextContentInsert>::get())
239 aRet <<= uno::Reference< text::XRelativeTextContentInsert >(this);
241 else if (rType == cppu::UnoType<text::XRelativeTextContentRemove>::get())
243 aRet <<= uno::Reference< text::XRelativeTextContentRemove >(this);
245 else if (rType == cppu::UnoType<beans::XPropertySet>::get())
247 aRet <<= uno::Reference< beans::XPropertySet >(this);
249 else if (rType == cppu::UnoType<text::XTextAppendAndConvert>::get())
251 aRet <<= uno::Reference< text::XTextAppendAndConvert >(this);
253 else if (rType == cppu::UnoType<text::XTextAppend>::get())
255 aRet <<= uno::Reference< text::XTextAppend >(this);
257 else if (rType == cppu::UnoType<text::XTextPortionAppend>::get())
259 aRet <<= uno::Reference< text::XTextPortionAppend >(this);
261 else if (rType == cppu::UnoType<text::XParagraphAppend>::get())
263 aRet <<= uno::Reference< text::XParagraphAppend >(this);
265 else if (rType == cppu::UnoType<text::XTextConvert>::get() )
267 aRet <<= uno::Reference< text::XTextConvert >(this);
269 else if (rType == cppu::UnoType<text::XTextContentAppend>::get())
271 aRet <<= uno::Reference< text::XTextContentAppend >(this);
273 else if(rType == cppu::UnoType<text::XTextCopy>::get())
275 aRet <<= uno::Reference< text::XTextCopy >( this );
277 return aRet;
280 uno::Sequence< uno::Type > SAL_CALL
281 SwXText::getTypes()
283 static const uno::Sequence< uno::Type > aTypes {
284 cppu::UnoType<text::XText>::get(),
285 cppu::UnoType<text::XTextRangeCompare>::get(),
286 cppu::UnoType<text::XRelativeTextContentInsert>::get(),
287 cppu::UnoType<text::XRelativeTextContentRemove>::get(),
288 cppu::UnoType<lang::XUnoTunnel>::get(),
289 cppu::UnoType<beans::XPropertySet>::get(),
290 cppu::UnoType<text::XTextPortionAppend>::get(),
291 cppu::UnoType<text::XParagraphAppend>::get(),
292 cppu::UnoType<text::XTextContentAppend>::get(),
293 cppu::UnoType<text::XTextConvert>::get(),
294 cppu::UnoType<text::XTextAppend>::get(),
295 cppu::UnoType<text::XTextAppendAndConvert>::get()
297 return aTypes;
300 // belongs the range in the text ? insert it then.
301 void SAL_CALL
302 SwXText::insertString(const uno::Reference< text::XTextRange >& xTextRange,
303 const OUString& rString, sal_Bool bAbsorb)
305 SolarMutexGuard aGuard;
306 comphelper::ProfileZone aZone("SwXText::insertString");
308 if (!xTextRange.is())
310 throw uno::RuntimeException();
312 if (!GetDoc())
314 throw uno::RuntimeException();
316 SwXTextRange *const pRange = dynamic_cast<SwXTextRange*>(xTextRange.get());
317 OTextCursorHelper *const pCursor = dynamic_cast<OTextCursorHelper*>(xTextRange.get());
318 if ((!pRange || &pRange ->GetDoc() != GetDoc()) &&
319 (!pCursor || pCursor->GetDoc() != GetDoc()))
321 throw uno::RuntimeException();
324 const SwStartNode *const pOwnStartNode = GetStartNode();
325 SwPaM aPam(GetDoc()->GetNodes());
326 const SwPaM * pPam(nullptr);
327 if (pCursor)
329 pPam = pCursor->GetPaM();
331 else // pRange
333 if (pRange->GetPositions(aPam))
335 pPam = &aPam;
338 if (!pPam)
340 throw uno::RuntimeException();
343 const SwStartNode* pTmp(pPam->GetPointNode().StartOfSectionNode());
344 while (pTmp && pTmp->IsSectionNode())
346 pTmp = pTmp->StartOfSectionNode();
348 if (!pOwnStartNode || (pOwnStartNode != pTmp))
350 throw uno::RuntimeException();
353 bool bForceExpandHints( false );
354 if (CursorType::Meta == m_pImpl->m_eType)
358 bForceExpandHints = CheckForOwnMemberMeta(*pPam, bAbsorb);
360 catch (const lang::IllegalArgumentException& iae)
362 // stupid method not allowed to throw iae
363 css::uno::Any anyEx = cppu::getCaughtException();
364 throw lang::WrappedTargetRuntimeException( iae.Message,
365 uno::Reference< uno::XInterface >(), anyEx );
368 if (bAbsorb)
370 //!! scan for CR characters and inserting the paragraph breaks
371 //!! has to be done in the called function.
372 //!! Implemented in SwXTextRange::DeleteAndInsert
373 if (pCursor)
375 SwXTextCursor * const pTextCursor(
376 dynamic_cast<SwXTextCursor*>(pCursor) );
377 if (pTextCursor)
379 pTextCursor->DeleteAndInsert(rString, ::sw::DeleteAndInsertMode::ForceReplace
380 | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default));
382 else
384 xTextRange->setString(rString);
387 else
389 pRange->DeleteAndInsert(rString, ::sw::DeleteAndInsertMode::ForceReplace
390 | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default));
393 else
395 // create a PaM positioned before the parameter PaM,
396 // so the text is inserted before
397 UnoActionContext aContext(GetDoc());
398 SwPaM aInsertPam(*pPam->Start());
399 ::sw::GroupUndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo());
400 SwUnoCursorHelper::DocInsertStringSplitCR(
401 *GetDoc(), aInsertPam, rString, bForceExpandHints );
405 void SAL_CALL
406 SwXText::insertControlCharacter(
407 const uno::Reference< text::XTextRange > & xTextRange,
408 sal_Int16 nControlCharacter, sal_Bool bAbsorb)
410 SolarMutexGuard aGuard;
412 if (!xTextRange.is())
414 throw lang::IllegalArgumentException();
416 if (!GetDoc())
418 throw uno::RuntimeException();
421 SwUnoInternalPaM aPam(*GetDoc());
422 if (!::sw::XTextRangeToSwPaM(aPam, xTextRange))
424 throw uno::RuntimeException();
426 const bool bForceExpandHints(CheckForOwnMemberMeta(aPam, bAbsorb));
428 const SwInsertFlags nInsertFlags =
429 bForceExpandHints
430 ? ( SwInsertFlags::FORCEHINTEXPAND | SwInsertFlags::EMPTYEXPAND)
431 : SwInsertFlags::EMPTYEXPAND;
433 if (bAbsorb && aPam.HasMark())
435 m_pImpl->m_pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
436 aPam.DeleteMark();
439 sal_Unicode cIns = 0;
440 switch (nControlCharacter)
442 case text::ControlCharacter::PARAGRAPH_BREAK :
443 // a table cell now becomes an ordinary text cell!
444 m_pImpl->m_pDoc->ClearBoxNumAttrs(aPam.GetPoint()->GetNode());
445 m_pImpl->m_pDoc->getIDocumentContentOperations().SplitNode(*aPam.GetPoint(), false);
446 break;
447 case text::ControlCharacter::APPEND_PARAGRAPH:
449 m_pImpl->m_pDoc->ClearBoxNumAttrs(aPam.GetPoint()->GetNode());
450 m_pImpl->m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPam.GetPoint());
452 SwXTextRange *const pRange =
453 dynamic_cast<SwXTextRange*>(xTextRange.get());
454 OTextCursorHelper *const pCursor =
455 dynamic_cast<OTextCursorHelper*>(xTextRange.get());
456 if (pRange)
458 pRange->SetPositions(aPam);
460 else if (pCursor)
462 SwPaM *const pCursorPam = pCursor->GetPaM();
463 *pCursorPam->GetPoint() = *aPam.GetPoint();
464 pCursorPam->DeleteMark();
467 break;
468 case text::ControlCharacter::LINE_BREAK: cIns = 10; break;
469 case text::ControlCharacter::SOFT_HYPHEN: cIns = CHAR_SOFTHYPHEN; break;
470 case text::ControlCharacter::HARD_HYPHEN: cIns = CHAR_HARDHYPHEN; break;
471 case text::ControlCharacter::HARD_SPACE: cIns = CHAR_HARDBLANK; break;
473 if (cIns)
475 m_pImpl->m_pDoc->getIDocumentContentOperations().InsertString(
476 aPam, OUString(cIns), nInsertFlags);
479 if (!bAbsorb)
480 return;
482 SwXTextRange *const pRange =
483 dynamic_cast<SwXTextRange*>(xTextRange.get());
484 OTextCursorHelper *const pCursor =
485 dynamic_cast<OTextCursorHelper*>(xTextRange.get());
487 SwCursor aCursor(*aPam.GetPoint(), nullptr);
488 SwUnoCursorHelper::SelectPam(aCursor, true);
489 aCursor.Left(1);
490 // here, the PaM needs to be moved:
491 if (pRange)
493 pRange->SetPositions(aCursor);
495 else
497 SwPaM *const pUnoCursor = pCursor->GetPaM();
498 *pUnoCursor->GetPoint() = *aCursor.GetPoint();
499 if (aCursor.HasMark())
501 pUnoCursor->SetMark();
502 *pUnoCursor->GetMark() = *aCursor.GetMark();
504 else
506 pUnoCursor->DeleteMark();
511 void SAL_CALL
512 SwXText::insertTextContent(
513 const uno::Reference< text::XTextRange > & xRange,
514 const uno::Reference< text::XTextContent > & xContent,
515 sal_Bool bAbsorb)
517 SolarMutexGuard aGuard;
518 comphelper::ProfileZone aZone("SwXText::insertTextContent");
520 if (!xRange.is())
522 lang::IllegalArgumentException aIllegal;
523 aIllegal.Message = "first parameter invalid;";
524 throw aIllegal;
526 if (!xContent.is())
528 lang::IllegalArgumentException aIllegal;
529 aIllegal.Message = "second parameter invalid";
530 throw aIllegal;
532 if(!GetDoc())
534 uno::RuntimeException aRuntime;
535 aRuntime.Message = cInvalidObject;
536 throw aRuntime;
539 SwUnoInternalPaM aPam(*GetDoc());
540 if (!::sw::XTextRangeToSwPaM(aPam, xRange))
542 lang::IllegalArgumentException aIllegal;
543 aIllegal.Message = "first parameter invalid";
544 throw aIllegal;
547 // first test if the range is at the right position, then call
548 // xContent->attach
549 const SwStartNode* pOwnStartNode = GetStartNode();
550 SwStartNodeType eSearchNodeType = SwNormalStartNode;
551 switch (m_pImpl->m_eType)
553 case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break;
554 case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break;
555 case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break;
556 case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break;
557 case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break;
558 //case CURSOR_INVALID:
559 //case CursorType::Body:
560 default:
561 break;
564 const SwStartNode* pTmp =
565 aPam.GetPointNode().FindSttNodeByType(eSearchNodeType);
567 // ignore SectionNodes
568 while (pTmp && pTmp->IsSectionNode())
570 pTmp = pTmp->StartOfSectionNode();
572 // if the document starts with a section
573 while (pOwnStartNode && pOwnStartNode->IsSectionNode())
575 pOwnStartNode = pOwnStartNode->StartOfSectionNode();
577 // this checks if (this) and xRange are in the same text::XText interface
578 if (pOwnStartNode != pTmp)
580 uno::RuntimeException aRunException;
581 aRunException.Message = "text interface and cursor not related";
582 throw aRunException;
585 const bool bForceExpandHints(CheckForOwnMemberMeta(aPam, bAbsorb));
587 // special treatment for Contents that do not replace the range, but
588 // instead are "overlaid"
589 SwXDocumentIndexMark *const pDocumentIndexMark =
590 dynamic_cast<SwXDocumentIndexMark*>(xContent.get());
591 SwXTextSection *const pSection =
592 dynamic_cast<SwXTextSection*>(xContent.get());
593 SwXBookmark *const pBookmark =
594 dynamic_cast<SwXBookmark*>(xContent.get());
595 SwXReferenceMark *const pReferenceMark =
596 dynamic_cast<SwXReferenceMark*>(xContent.get());
597 SwXMeta *const pMeta = dynamic_cast<SwXMeta*>(xContent.get());
598 auto* pContentControl = dynamic_cast<SwXContentControl*>(xContent.get());
599 SwXTextField* pTextField = dynamic_cast<SwXTextField*>(xContent.get());
600 if (pTextField && pTextField->GetServiceId() != SwServiceType::FieldTypeAnnotation)
601 pTextField = nullptr;
603 const bool bAttribute = pBookmark || pDocumentIndexMark
604 || pSection || pReferenceMark || pMeta || pContentControl || pTextField;
606 if (bAbsorb && !bAttribute)
608 if (SwXTextRange *const pRange = dynamic_cast<SwXTextRange*>(xRange.get()))
610 pRange->DeleteAndInsert(u"", ::sw::DeleteAndInsertMode::ForceReplace
611 | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default));
613 else if (SwXTextCursor *const pCursor = dynamic_cast<SwXTextCursor*>(dynamic_cast<OTextCursorHelper*>(xRange.get())))
615 pCursor->DeleteAndInsert(u"", ::sw::DeleteAndInsertMode::ForceReplace
616 | (bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default));
618 else
620 xRange->setString(OUString());
623 uno::Reference< text::XTextRange > xTempRange =
624 (bAttribute && bAbsorb) ? xRange : xRange->getStart();
625 if (bForceExpandHints)
627 // if necessary, replace xTempRange with a new SwXTextCursor
628 PrepareForAttach(xTempRange, aPam);
630 xContent->attach(xTempRange);
633 void SAL_CALL
634 SwXText::insertTextContentBefore(
635 const uno::Reference< text::XTextContent>& xNewContent,
636 const uno::Reference< text::XTextContent>& xSuccessor)
638 SolarMutexGuard aGuard;
640 if(!GetDoc())
642 uno::RuntimeException aRuntime;
643 aRuntime.Message = cInvalidObject;
644 throw aRuntime;
647 SwXParagraph *const pPara = dynamic_cast<SwXParagraph*>(xNewContent.get());
648 if (!pPara || !pPara->IsDescriptor() || !xSuccessor.is())
650 throw lang::IllegalArgumentException();
653 bool bRet = false;
654 SwXTextSection *const pXSection = dynamic_cast<SwXTextSection*>(xSuccessor.get());
655 SwXTextTable *const pXTable = dynamic_cast<SwXTextTable*>(xSuccessor.get());
656 SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
657 SwTextNode * pTextNode = nullptr;
658 if(pTableFormat && pTableFormat->GetDoc() == GetDoc())
660 SwTable *const pTable = SwTable::FindTable( pTableFormat );
661 SwTableNode *const pTableNode = pTable->GetTableNode();
663 const SwNodeIndex aTableIdx( *pTableNode, -1 );
664 SwPosition aBefore(aTableIdx);
665 bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
666 pTextNode = aBefore.GetNode().GetTextNode();
668 else if (pXSection && pXSection->GetFormat() &&
669 pXSection->GetFormat()->GetDoc() == GetDoc())
671 SwSectionFormat *const pSectFormat = pXSection->GetFormat();
672 SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
674 const SwNodeIndex aSectIdx( *pSectNode, -1 );
675 SwPosition aBefore(aSectIdx);
676 bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
677 pTextNode = aBefore.GetNode().GetTextNode();
679 if (!bRet || !pTextNode)
681 throw lang::IllegalArgumentException();
683 pPara->attachToText(*this, *pTextNode);
686 void SAL_CALL
687 SwXText::insertTextContentAfter(
688 const uno::Reference< text::XTextContent>& xNewContent,
689 const uno::Reference< text::XTextContent>& xPredecessor)
691 SolarMutexGuard aGuard;
693 if(!GetDoc())
695 throw uno::RuntimeException();
698 SwXParagraph *const pPara = dynamic_cast<SwXParagraph*>(xNewContent.get());
699 if(!pPara || !pPara->IsDescriptor() || !xPredecessor.is())
701 throw lang::IllegalArgumentException();
704 SwXTextSection *const pXSection = dynamic_cast<SwXTextSection*>(xPredecessor.get());
705 SwXTextTable *const pXTable = dynamic_cast<SwXTextTable*>(xPredecessor.get());
706 SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
707 bool bRet = false;
708 SwTextNode * pTextNode = nullptr;
709 if(pTableFormat && pTableFormat->GetDoc() == GetDoc())
711 SwTable *const pTable = SwTable::FindTable( pTableFormat );
712 SwTableNode *const pTableNode = pTable->GetTableNode();
714 SwEndNode *const pTableEnd = pTableNode->EndOfSectionNode();
715 SwPosition aTableEnd(*pTableEnd);
716 bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aTableEnd );
717 pTextNode = aTableEnd.GetNode().GetTextNode();
719 else if (pXSection && pXSection->GetFormat() &&
720 pXSection->GetFormat()->GetDoc() == GetDoc())
722 SwSectionFormat *const pSectFormat = pXSection->GetFormat();
723 SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
724 SwEndNode *const pEnd = pSectNode->EndOfSectionNode();
725 SwPosition aEnd(*pEnd);
726 bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd );
727 pTextNode = aEnd.GetNode().GetTextNode();
729 if (!bRet || !pTextNode)
731 throw lang::IllegalArgumentException();
733 pPara->attachToText(*this, *pTextNode);
736 void SAL_CALL
737 SwXText::removeTextContentBefore(
738 const uno::Reference< text::XTextContent>& xSuccessor)
740 SolarMutexGuard aGuard;
742 if(!GetDoc())
744 uno::RuntimeException aRuntime;
745 aRuntime.Message = cInvalidObject;
746 throw aRuntime;
749 bool bRet = false;
750 SwXTextSection *const pXSection = dynamic_cast<SwXTextSection*>(xSuccessor.get());
751 SwXTextTable *const pXTable = dynamic_cast<SwXTextTable*>(xSuccessor.get());
752 SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
753 if(pTableFormat && pTableFormat->GetDoc() == GetDoc())
755 SwTable *const pTable = SwTable::FindTable( pTableFormat );
756 SwTableNode *const pTableNode = pTable->GetTableNode();
758 const SwNodeIndex aTableIdx( *pTableNode, -1 );
759 if(aTableIdx.GetNode().IsTextNode())
761 SwPaM aBefore(aTableIdx);
762 bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aBefore );
765 else if (pXSection && pXSection->GetFormat() &&
766 pXSection->GetFormat()->GetDoc() == GetDoc())
768 SwSectionFormat *const pSectFormat = pXSection->GetFormat();
769 SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
771 const SwNodeIndex aSectIdx( *pSectNode, -1 );
772 if(aSectIdx.GetNode().IsTextNode())
774 SwPaM aBefore(aSectIdx);
775 bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aBefore );
778 if(!bRet)
780 throw lang::IllegalArgumentException();
784 void SAL_CALL
785 SwXText::removeTextContentAfter(
786 const uno::Reference< text::XTextContent>& xPredecessor)
788 SolarMutexGuard aGuard;
790 if(!GetDoc())
792 uno::RuntimeException aRuntime;
793 aRuntime.Message = cInvalidObject;
794 throw aRuntime;
797 bool bRet = false;
798 SwXTextSection *const pXSection = dynamic_cast<SwXTextSection*>(xPredecessor.get());
799 SwXTextTable *const pXTable = dynamic_cast<SwXTextTable*>(xPredecessor.get());
800 SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
801 if(pTableFormat && pTableFormat->GetDoc() == GetDoc())
803 SwTable *const pTable = SwTable::FindTable( pTableFormat );
804 SwTableNode *const pTableNode = pTable->GetTableNode();
805 SwEndNode *const pTableEnd = pTableNode->EndOfSectionNode();
807 const SwNodeIndex aTableIdx( *pTableEnd, 1 );
808 if(aTableIdx.GetNode().IsTextNode())
810 SwPaM aPaM(aTableIdx);
811 bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aPaM );
814 else if (pXSection && pXSection->GetFormat() &&
815 pXSection->GetFormat()->GetDoc() == GetDoc())
817 SwSectionFormat *const pSectFormat = pXSection->GetFormat();
818 SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
819 SwEndNode *const pEnd = pSectNode->EndOfSectionNode();
820 const SwNodeIndex aSectIdx( *pEnd, 1 );
821 if(aSectIdx.GetNode().IsTextNode())
823 SwPaM aAfter(aSectIdx);
824 bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aAfter );
827 if(!bRet)
829 throw lang::IllegalArgumentException();
833 void SAL_CALL
834 SwXText::removeTextContent(
835 const uno::Reference< text::XTextContent > & xContent)
837 // forward: need no solar mutex here
838 if(!xContent.is())
840 uno::RuntimeException aRuntime;
841 aRuntime.Message = "first parameter invalid";
842 throw aRuntime;
844 xContent->dispose();
847 uno::Reference< text::XText > SAL_CALL
848 SwXText::getText()
850 SolarMutexGuard aGuard;
851 comphelper::ProfileZone aZone("SwXText::getText");
853 const uno::Reference< text::XText > xRet(this);
854 return xRet;
857 uno::Reference< text::XTextRange > SAL_CALL
858 SwXText::getStart()
860 SolarMutexGuard aGuard;
862 const rtl::Reference< SwXTextCursor > xRef = createXTextCursor();
863 if(!xRef.is())
865 uno::RuntimeException aRuntime;
866 aRuntime.Message = cInvalidObject;
867 throw aRuntime;
869 xRef->gotoStart(false);
870 return static_cast<text::XWordCursor*>(xRef.get());
873 uno::Reference< text::XTextRange > SAL_CALL
874 SwXText::getEnd()
876 SolarMutexGuard aGuard;
878 const rtl::Reference< SwXTextCursor > xRef = createXTextCursor();
879 if(!xRef.is())
881 uno::RuntimeException aRuntime;
882 aRuntime.Message = cInvalidObject;
883 throw aRuntime;
885 xRef->gotoEnd(false);
886 return static_cast<text::XWordCursor*>(xRef.get());
889 OUString SAL_CALL SwXText::getString()
891 SolarMutexGuard aGuard;
893 const rtl::Reference< SwXTextCursor > xRet = createXTextCursor();
894 if(!xRet.is())
896 SAL_WARN("sw.uno", "cursor was not created in getString() call. Returning empty string.");
897 return OUString();
899 xRet->gotoEnd(true);
900 return xRet->getString();
903 void SAL_CALL
904 SwXText::setString(const OUString& rString)
906 SolarMutexGuard aGuard;
908 if (!GetDoc())
910 uno::RuntimeException aRuntime;
911 aRuntime.Message = cInvalidObject;
912 throw aRuntime;
915 const SwStartNode* pStartNode = GetStartNode();
916 if (!pStartNode)
918 throw uno::RuntimeException();
921 GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
922 //insert an empty paragraph at the start and at the end to ensure that
923 //all tables and sections can be removed by the selecting text::XTextCursor
924 if (CursorType::Meta != m_pImpl->m_eType)
926 SwPosition aStartPos(*pStartNode);
927 const SwEndNode* pEnd = pStartNode->EndOfSectionNode();
928 SwNodeIndex aEndIdx(*pEnd);
929 --aEndIdx;
930 //the inserting of nodes should only be done if really necessary
931 //to prevent #97924# (removes paragraph attributes when setting the text
932 //e.g. of a table cell
933 bool bInsertNodes = false;
934 SwNodeIndex aStartIdx(*pStartNode);
937 ++aStartIdx;
938 SwNode& rCurrentNode = aStartIdx.GetNode();
939 if(rCurrentNode.GetNodeType() == SwNodeType::Section
940 ||rCurrentNode.GetNodeType() == SwNodeType::Table)
942 bInsertNodes = true;
943 break;
946 while(aStartIdx < aEndIdx);
947 if(bInsertNodes)
949 GetDoc()->getIDocumentContentOperations().AppendTextNode( aStartPos );
950 SwPaM aPam(aEndIdx.GetNode());
951 GetDoc()->getIDocumentContentOperations().AppendTextNode( *aPam.Start() );
955 const rtl::Reference< SwXTextCursor > xRet = createXTextCursor();
956 if(!xRet.is())
958 GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
959 uno::RuntimeException aRuntime;
960 aRuntime.Message = cInvalidObject;
961 throw aRuntime;
963 xRet->gotoEnd(true);
964 xRet->setString(rString);
965 GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
968 //FIXME why is CheckForOwnMember duplicated in some insert methods?
969 // Description: Checks if pRange/pCursor are member of the same text interface.
970 // Only one of the pointers has to be set!
971 bool SwXText::Impl::CheckForOwnMember(
972 const SwPaM & rPaM)
974 const rtl::Reference< SwXTextCursor > xOwnCursor(m_rThis.createXTextCursor());
975 const SwStartNode* pOwnStartNode =
976 xOwnCursor->GetPaM()->GetPointNode().StartOfSectionNode();
977 SwStartNodeType eSearchNodeType = SwNormalStartNode;
978 switch (m_eType)
980 case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break;
981 case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break;
982 case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break;
983 case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break;
984 case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break;
985 //case CURSOR_INVALID:
986 //case CursorType::Body:
987 default:
991 const SwNode& rSrcNode = rPaM.GetPointNode();
992 const SwStartNode* pTmp = rSrcNode.FindSttNodeByType(eSearchNodeType);
994 // skip SectionNodes / TableNodes to be able to compare across table/section boundaries
995 while (pTmp
996 && (pTmp->IsSectionNode() || pTmp->IsTableNode()
997 || (m_eType != CursorType::TableText
998 && pTmp->GetStartNodeType() == SwTableBoxStartNode)))
1000 pTmp = pTmp->StartOfSectionNode();
1003 while (pOwnStartNode->IsSectionNode() || pOwnStartNode->IsTableNode()
1004 || (m_eType != CursorType::TableText
1005 && pOwnStartNode->GetStartNodeType() == SwTableBoxStartNode))
1007 pOwnStartNode = pOwnStartNode->StartOfSectionNode();
1010 //this checks if (this) and xRange are in the same text::XText interface
1011 return (pOwnStartNode == pTmp);
1014 sal_Int16
1015 SwXText::Impl::ComparePositions(
1016 const uno::Reference<text::XTextRange>& xPos1,
1017 const uno::Reference<text::XTextRange>& xPos2)
1019 SwUnoInternalPaM aPam1(*m_pDoc);
1020 SwUnoInternalPaM aPam2(*m_pDoc);
1022 if (!::sw::XTextRangeToSwPaM(aPam1, xPos1) ||
1023 !::sw::XTextRangeToSwPaM(aPam2, xPos2))
1025 throw lang::IllegalArgumentException();
1027 if (!CheckForOwnMember(aPam1) || !CheckForOwnMember(aPam2))
1029 throw lang::IllegalArgumentException();
1032 sal_Int16 nCompare = 0;
1033 SwPosition const*const pStart1 = aPam1.Start();
1034 SwPosition const*const pStart2 = aPam2.Start();
1035 if (*pStart1 < *pStart2)
1037 nCompare = 1;
1039 else if (*pStart1 > *pStart2)
1041 nCompare = -1;
1043 else
1045 OSL_ENSURE(*pStart1 == *pStart2,
1046 "SwPositions should be equal here");
1047 nCompare = 0;
1050 return nCompare;
1053 sal_Int16 SAL_CALL
1054 SwXText::compareRegionStarts(
1055 const uno::Reference<text::XTextRange>& xRange1,
1056 const uno::Reference<text::XTextRange>& xRange2)
1058 SolarMutexGuard aGuard;
1060 if (!xRange1.is() || !xRange2.is())
1062 throw lang::IllegalArgumentException();
1064 const uno::Reference<text::XTextRange> xStart1 = xRange1->getStart();
1065 const uno::Reference<text::XTextRange> xStart2 = xRange2->getStart();
1067 return m_pImpl->ComparePositions(xStart1, xStart2);
1070 sal_Int16 SAL_CALL
1071 SwXText::compareRegionEnds(
1072 const uno::Reference<text::XTextRange>& xRange1,
1073 const uno::Reference<text::XTextRange>& xRange2)
1075 SolarMutexGuard aGuard;
1077 if (!xRange1.is() || !xRange2.is())
1079 throw lang::IllegalArgumentException();
1081 uno::Reference<text::XTextRange> xEnd1 = xRange1->getEnd();
1082 uno::Reference<text::XTextRange> xEnd2 = xRange2->getEnd();
1084 return m_pImpl->ComparePositions(xEnd1, xEnd2);
1087 uno::Reference< beans::XPropertySetInfo > SAL_CALL
1088 SwXText::getPropertySetInfo()
1090 SolarMutexGuard g;
1092 static uno::Reference< beans::XPropertySetInfo > xInfo =
1093 m_pImpl->m_rPropSet.getPropertySetInfo();
1094 return xInfo;
1097 void SAL_CALL
1098 SwXText::setPropertyValue(const OUString& /*aPropertyName*/,
1099 const uno::Any& /*aValue*/)
1101 throw lang::IllegalArgumentException();
1104 uno::Any SAL_CALL
1105 SwXText::getPropertyValue(
1106 const OUString& rPropertyName)
1108 SolarMutexGuard aGuard;
1110 if(!IsValid())
1112 throw uno::RuntimeException();
1115 SfxItemPropertyMapEntry const*const pEntry =
1116 m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
1117 if (!pEntry)
1119 beans::UnknownPropertyException aExcept;
1120 aExcept.Message = "Unknown property: " + rPropertyName;
1121 throw aExcept;
1124 uno::Any aRet;
1125 switch (pEntry->nWID)
1127 // no code necessary - the redline is always located at the end node
1128 // case FN_UNO_REDLINE_NODE_START:
1129 // break;
1130 case FN_UNO_REDLINE_NODE_END:
1132 const SwRedlineTable& rRedTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1133 const size_t nRedTableCount = rRedTable.size();
1134 if (nRedTableCount > 0)
1136 SwStartNode const*const pStartNode = GetStartNode();
1137 const SwNode& rOwnIndex = *pStartNode->EndOfSectionNode();
1138 for (size_t nRed = 0; nRed < nRedTableCount; ++nRed)
1140 SwRangeRedline const*const pRedline = rRedTable[nRed];
1141 SwPosition const*const pRedStart = pRedline->Start();
1142 const SwNode& rRedNode = pRedStart->GetNode();
1143 if (rOwnIndex == rRedNode)
1145 aRet <<= SwXRedlinePortion::CreateRedlineProperties(
1146 *pRedline, true);
1147 break;
1152 break;
1154 return aRet;
1157 void SAL_CALL
1158 SwXText::addPropertyChangeListener(
1159 const OUString& /*rPropertyName*/,
1160 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
1162 OSL_FAIL("SwXText::addPropertyChangeListener(): not implemented");
1165 void SAL_CALL
1166 SwXText::removePropertyChangeListener(
1167 const OUString& /*rPropertyName*/,
1168 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
1170 OSL_FAIL("SwXText::removePropertyChangeListener(): not implemented");
1173 void SAL_CALL
1174 SwXText::addVetoableChangeListener(
1175 const OUString& /*rPropertyName*/,
1176 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
1178 OSL_FAIL("SwXText::addVetoableChangeListener(): not implemented");
1181 void SAL_CALL
1182 SwXText::removeVetoableChangeListener(
1183 const OUString& /*rPropertyName*/,
1184 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
1186 OSL_FAIL("SwXText::removeVetoableChangeListener(): not implemented");
1189 namespace
1193 uno::Reference< text::XTextRange > SAL_CALL
1194 SwXText::finishParagraph(
1195 const uno::Sequence< beans::PropertyValue > & rProperties)
1197 SolarMutexGuard g;
1199 return m_pImpl->finishOrAppendParagraph(rProperties, uno::Reference< text::XTextRange >());
1202 uno::Reference< text::XTextRange > SAL_CALL
1203 SwXText::finishParagraphInsert(
1204 const uno::Sequence< beans::PropertyValue > & rProperties,
1205 const uno::Reference< text::XTextRange >& xInsertPosition)
1207 SolarMutexGuard g;
1209 return m_pImpl->finishOrAppendParagraph(rProperties, xInsertPosition);
1212 uno::Reference< text::XTextRange >
1213 SwXText::Impl::finishOrAppendParagraph(
1214 const uno::Sequence< beans::PropertyValue > & rProperties,
1215 const uno::Reference< text::XTextRange >& xInsertPosition)
1217 if (!m_bIsValid)
1219 throw uno::RuntimeException();
1222 const SwStartNode* pStartNode = m_rThis.GetStartNode();
1223 if(!pStartNode)
1225 throw uno::RuntimeException();
1228 uno::Reference< text::XTextRange > xRet;
1229 bool bIllegalException = false;
1230 bool bRuntimeException = false;
1231 OUString sMessage;
1232 m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::START , nullptr);
1233 // find end node, go backward - don't skip tables because the new
1234 // paragraph has to be the last node
1235 //aPam.Move( fnMoveBackward, GoInNode );
1236 SwPaM aPam(*pStartNode->EndOfSectionNode(), SwNodeOffset(-1));
1237 // If we got a position reference, then the insert point is not the end of
1238 // the document.
1239 if (xInsertPosition.is())
1241 SwUnoInternalPaM aStartPam(*m_rThis.GetDoc());
1242 ::sw::XTextRangeToSwPaM(aStartPam, xInsertPosition);
1243 aPam = aStartPam;
1244 aPam.SetMark();
1246 m_pDoc->getIDocumentContentOperations().AppendTextNode( *aPam.GetPoint() );
1247 // remove attributes from the previous paragraph
1248 m_pDoc->ResetAttrs(aPam);
1249 // in case of finishParagraph the PaM needs to be moved to the
1250 // previous paragraph
1251 aPam.Move( fnMoveBackward, GoInNode );
1255 SfxItemPropertySet const*const pParaPropSet =
1256 aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH);
1258 SwUnoCursorHelper::SetPropertyValues(aPam, *pParaPropSet, rProperties);
1260 // tdf#127616 keep direct character formatting of empty paragraphs,
1261 // if character style of the paragraph sets also the same attributes
1262 if (aPam.Start()->GetNode().GetTextNode()->Len() == 0)
1264 auto itCharStyle = std::find_if(rProperties.begin(), rProperties.end(), [](const beans::PropertyValue& rValue)
1266 return rValue.Name == "CharStyleName";
1268 if ( itCharStyle != rProperties.end() )
1270 for (const auto& rValue : rProperties)
1272 if ( rValue != *itCharStyle && rValue.Name.startsWith("Char") )
1274 SwUnoCursorHelper::SetPropertyValue(aPam, *pParaPropSet, rValue.Name, rValue.Value);
1280 catch (const lang::IllegalArgumentException& rIllegal)
1282 sMessage = rIllegal.Message;
1283 bIllegalException = true;
1285 catch (const uno::RuntimeException& rRuntime)
1287 sMessage = rRuntime.Message;
1288 bRuntimeException = true;
1290 catch (const uno::Exception& rEx)
1292 sMessage = rEx.Message;
1293 bRuntimeException = true;
1296 m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
1297 if (bIllegalException || bRuntimeException)
1299 m_pDoc->GetIDocumentUndoRedo().Undo();
1300 if (bIllegalException)
1302 lang::IllegalArgumentException aEx;
1303 aEx.Message = sMessage;
1304 throw aEx;
1306 else
1308 uno::RuntimeException aEx;
1309 aEx.Message = sMessage;
1310 throw aEx;
1313 SwTextNode *const pTextNode( aPam.Start()->GetNode().GetTextNode() );
1314 OSL_ENSURE(pTextNode, "no SwTextNode?");
1315 if (pTextNode)
1317 xRet = SwXParagraph::CreateXParagraph(*m_pDoc, pTextNode, &m_rThis);
1320 return xRet;
1323 uno::Reference< text::XTextRange > SAL_CALL
1324 SwXText::insertTextPortion(
1325 const OUString& rText,
1326 const uno::Sequence< beans::PropertyValue > &
1327 rCharacterAndParagraphProperties,
1328 const uno::Reference<text::XTextRange>& xInsertPosition)
1330 SolarMutexGuard aGuard;
1332 if(!IsValid())
1334 throw uno::RuntimeException();
1336 uno::Reference< text::XTextRange > xRet;
1337 const rtl::Reference<SwXTextCursor> xTextCursor = createXTextCursorByRange(xInsertPosition);
1339 bool bIllegalException = false;
1340 bool bRuntimeException = false;
1341 OUString sMessage;
1342 m_pImpl->m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
1344 auto& rCursor(xTextCursor->GetCursor());
1345 m_pImpl->m_pDoc->DontExpandFormat( *rCursor.Start() );
1347 if (!rText.isEmpty())
1349 SwNodeIndex const nodeIndex(rCursor.GetPoint()->GetNode(), -1);
1350 const sal_Int32 nContentPos = rCursor.GetPoint()->GetContentIndex();
1351 SwUnoCursorHelper::DocInsertStringSplitCR(
1352 *m_pImpl->m_pDoc, rCursor, rText, false);
1353 SwUnoCursorHelper::SelectPam(rCursor, true);
1354 rCursor.GetPoint()->Assign(nodeIndex.GetNode(), SwNodeOffset(+1), nContentPos);
1359 SfxItemPropertySet const*const pCursorPropSet =
1360 aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR);
1361 SwUnoCursorHelper::SetPropertyValues(rCursor, *pCursorPropSet,
1362 rCharacterAndParagraphProperties,
1363 SetAttrMode::NOFORMATATTR);
1365 catch (const lang::IllegalArgumentException& rIllegal)
1367 sMessage = rIllegal.Message;
1368 bIllegalException = true;
1370 catch (const uno::RuntimeException& rRuntime)
1372 sMessage = rRuntime.Message;
1373 bRuntimeException = true;
1375 m_pImpl->m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
1376 if (bIllegalException || bRuntimeException)
1378 m_pImpl->m_pDoc->GetIDocumentUndoRedo().Undo();
1379 if (bIllegalException)
1381 lang::IllegalArgumentException aEx;
1382 aEx.Message = sMessage;
1383 throw aEx;
1385 else
1387 uno::RuntimeException aEx;
1388 aEx.Message = sMessage;
1389 throw aEx;
1392 xRet = new SwXTextRange(rCursor, this);
1393 return xRet;
1396 // Append text portions at the end of the last paragraph of the text interface.
1397 // Support of import filters.
1398 uno::Reference< text::XTextRange > SAL_CALL
1399 SwXText::appendTextPortion(
1400 const OUString& rText,
1401 const uno::Sequence< beans::PropertyValue > &
1402 rCharacterAndParagraphProperties)
1404 // Right now this doesn't need a guard, as it's just calling the insert
1405 // version, that has it already.
1406 uno::Reference<text::XTextRange> xInsertPosition = getEnd();
1407 return insertTextPortion(rText, rCharacterAndParagraphProperties, xInsertPosition);
1410 // enable inserting/appending text contents like graphic objects, shapes and so on to
1411 // support import filters
1412 uno::Reference< text::XTextRange > SAL_CALL
1413 SwXText::insertTextContentWithProperties(
1414 const uno::Reference< text::XTextContent >& xTextContent,
1415 const uno::Sequence< beans::PropertyValue >&
1416 rCharacterAndParagraphProperties,
1417 const uno::Reference< text::XTextRange >& xInsertPosition)
1419 SolarMutexGuard aGuard;
1421 if (!IsValid())
1423 throw uno::RuntimeException();
1426 SwUnoInternalPaM aPam(*GetDoc());
1427 if (!::sw::XTextRangeToSwPaM(aPam, xInsertPosition))
1429 throw lang::IllegalArgumentException("invalid position", nullptr, 2);
1432 SwRewriter aRewriter;
1433 aRewriter.AddRule(UndoArg1, SwResId(STR_UNDO_INSERT_TEXTBOX));
1435 m_pImpl->m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, &aRewriter);
1437 // Any direct formatting ending at the insert position (xRange) should not
1438 // be expanded to cover the inserted content (xContent)
1439 // (insertTextContent() shouldn't do this, only ...WithProperties()!)
1440 GetDoc()->DontExpandFormat( *aPam.Start() );
1442 // now attach the text content here
1443 insertTextContent( xInsertPosition, xTextContent, false );
1444 // now apply the properties to the anchor
1445 if (rCharacterAndParagraphProperties.hasElements())
1449 const uno::Reference< beans::XPropertySet > xAnchor(
1450 xTextContent->getAnchor(), uno::UNO_QUERY);
1451 if (xAnchor.is())
1453 for (const auto& rProperty : rCharacterAndParagraphProperties)
1455 xAnchor->setPropertyValue(rProperty.Name, rProperty.Value);
1459 catch (const uno::Exception& e)
1461 css::uno::Any anyEx = cppu::getCaughtException();
1462 m_pImpl->m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, &aRewriter);
1463 throw lang::WrappedTargetRuntimeException( e.Message,
1464 uno::Reference< uno::XInterface >(), anyEx );
1467 m_pImpl->m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, &aRewriter);
1468 return xInsertPosition;
1471 uno::Reference< text::XTextRange > SAL_CALL
1472 SwXText::appendTextContent(
1473 const uno::Reference< text::XTextContent >& xTextContent,
1474 const uno::Sequence< beans::PropertyValue >& rCharacterAndParagraphProperties
1477 // Right now this doesn't need a guard, as it's just calling the insert
1478 // version, that has it already.
1479 uno::Reference<text::XTextRange> xInsertPosition = getEnd();
1480 return insertTextContentWithProperties(xTextContent, rCharacterAndParagraphProperties, xInsertPosition);
1483 // determine whether SwFrameFormat is a graphic node
1484 static bool isGraphicNode(const SwFrameFormat* pFrameFormat)
1486 // safety
1487 if( !pFrameFormat->GetContent().GetContentIdx() )
1489 return false;
1491 auto index = *pFrameFormat->GetContent().GetContentIdx();
1492 // consider the next node -> there is the graphic stored
1493 ++index;
1494 return index.GetNode().IsGrfNode();
1497 /// Determines if the at-para rAnchor is anchored at the start or end of rAnchorCheckPam.
1498 static bool IsAtParaMatch(const SwPaM& rAnchorCheckPam, const SwFormatAnchor& rAnchor)
1500 if (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PARA)
1502 return false;
1505 if (rAnchorCheckPam.Start()->GetNode() == *rAnchor.GetAnchorNode())
1507 return true;
1510 if (rAnchorCheckPam.End()->GetNode() == *rAnchor.GetAnchorNode())
1512 SwTextNode* pEndTextNode = rAnchorCheckPam.End()->GetNode().GetTextNode();
1513 if (pEndTextNode && rAnchorCheckPam.End()->GetContentIndex() == pEndTextNode->Len())
1515 // rAnchorCheckPam covers the entire last text node, rAnchor is at-para, consider this
1516 // as "inside pam" rather than "at the end of pam".
1517 return false;
1519 return true;
1522 return false;
1525 // move previously appended paragraphs into a text frames
1526 // to support import filters
1527 uno::Reference< text::XTextContent > SAL_CALL
1528 SwXText::convertToTextFrame(
1529 const uno::Reference< text::XTextRange >& xStart,
1530 const uno::Reference< text::XTextRange >& xEnd,
1531 const uno::Sequence< beans::PropertyValue >& rFrameProperties)
1533 SolarMutexGuard aGuard;
1535 if(!IsValid())
1537 throw uno::RuntimeException();
1539 // tdf#143384 recognize dummy property, that was set to make createTextCursor
1540 // to not ignore tables.
1541 // It is enough to use this hack only for the range start,
1542 // because as far as I know, the range cannot end with table when this property is set.
1543 ::sw::TextRangeMode eMode = ::sw::TextRangeMode::RequireTextNode;
1544 for (const auto& rCellProperty : rFrameProperties)
1546 if (rCellProperty.Name == "CursorNotIgnoreTables")
1548 bool bAllowNonTextNode = false;
1549 rCellProperty.Value >>= bAllowNonTextNode;
1550 if (bAllowNonTextNode)
1551 eMode = ::sw::TextRangeMode::AllowTableNode;
1552 break;
1555 uno::Reference< text::XTextContent > xRet;
1556 std::optional<SwUnoInternalPaM> pTempStartPam(*GetDoc());
1557 std::optional<SwUnoInternalPaM> pEndPam(*GetDoc());
1558 if (!::sw::XTextRangeToSwPaM(*pTempStartPam, xStart, eMode)
1559 || !::sw::XTextRangeToSwPaM(*pEndPam, xEnd))
1561 throw lang::IllegalArgumentException();
1564 auto pStartPam(GetDoc()->CreateUnoCursor(*pTempStartPam->GetPoint()));
1565 if (pTempStartPam->HasMark())
1567 pStartPam->SetMark();
1568 *pStartPam->GetMark() = *pTempStartPam->GetMark();
1570 pTempStartPam.reset();
1572 SwXTextRange *const pStartRange = dynamic_cast<SwXTextRange*>(xStart.get());
1573 SwXTextRange *const pEndRange = dynamic_cast<SwXTextRange*>(xEnd.get());
1574 // bookmarks have to be removed before the referenced text node
1575 // is deleted in DelFullPara
1576 if (pStartRange)
1578 pStartRange->Invalidate();
1580 if (pEndRange)
1582 pEndRange->Invalidate();
1585 m_pImpl->m_pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
1586 bool bIllegalException = false;
1587 bool bRuntimeException = false;
1588 OUString sMessage;
1589 SwStartNode* pStartStartNode = pStartPam->GetPointNode().StartOfSectionNode();
1590 while (pStartStartNode && pStartStartNode->IsSectionNode())
1592 pStartStartNode = pStartStartNode->StartOfSectionNode();
1594 SwStartNode* pEndStartNode = pEndPam->GetPointNode().StartOfSectionNode();
1595 while (pEndStartNode && pEndStartNode->IsSectionNode())
1597 pEndStartNode = pEndStartNode->StartOfSectionNode();
1599 bool bParaAfterInserted = false;
1600 bool bParaBeforeInserted = false;
1601 ::std::optional<SwPaM> oAnchorCheckPam;
1602 oAnchorCheckPam.emplace(*pStartPam->Start(), *pEndPam->End());
1603 if (
1604 pStartStartNode && pEndStartNode &&
1605 (pStartStartNode != pEndStartNode || pStartStartNode != GetStartNode())
1608 // todo: if the start/end is in a table then insert a paragraph
1609 // before/after, move the start/end nodes, then convert and
1610 // remove the additional paragraphs in the end
1611 SwTableNode * pStartTableNode(nullptr);
1612 if (pStartStartNode->GetStartNodeType() == SwTableBoxStartNode)
1614 pStartTableNode = pStartStartNode->FindTableNode();
1615 // Is it the same table start node than the end?
1616 SwTableNode *const pEndStartTableNode(pEndStartNode->FindTableNode());
1617 while (pEndStartTableNode && pStartTableNode &&
1618 pEndStartTableNode->GetIndex() < pStartTableNode->GetIndex())
1620 SwStartNode* pStartStartTableNode = pStartTableNode->StartOfSectionNode();
1621 pStartTableNode = pStartStartTableNode->FindTableNode();
1624 if (pStartTableNode)
1626 const SwNodeIndex aTableIdx( *pStartTableNode, -1 );
1627 SwPosition aBefore(aTableIdx);
1628 bParaBeforeInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
1629 pStartPam->DeleteMark();
1630 *pStartPam->GetPoint() = aBefore;
1631 pStartStartNode = pStartPam->GetPointNode().StartOfSectionNode();
1633 if (pEndStartNode->GetStartNodeType() == SwTableBoxStartNode)
1635 SwTableNode *const pEndTableNode = pEndStartNode->FindTableNode();
1636 SwEndNode *const pTableEnd = pEndTableNode->EndOfSectionNode();
1637 SwPosition aTableEnd(*pTableEnd);
1638 bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aTableEnd );
1639 pEndPam->DeleteMark();
1640 *pEndPam->GetPoint() = aTableEnd;
1641 pEndStartNode = pEndPam->GetPointNode().StartOfSectionNode();
1643 // now we should have the positions in the same hierarchy
1644 if ((pStartStartNode != pEndStartNode) ||
1645 (pStartStartNode != GetStartNode()))
1647 // if not - remove the additional paragraphs and throw
1648 if (bParaBeforeInserted)
1650 SwCursor aDelete(*pStartPam->GetPoint(), nullptr);
1651 *pStartPam->GetPoint() = // park it because node is deleted
1652 SwPosition(GetDoc()->GetNodes().GetEndOfContent());
1653 aDelete.MovePara(GoCurrPara, fnParaStart);
1654 aDelete.SetMark();
1655 aDelete.MovePara(GoCurrPara, fnParaEnd);
1656 GetDoc()->getIDocumentContentOperations().DelFullPara(aDelete);
1658 if (bParaAfterInserted)
1660 SwCursor aDelete(*pEndPam->GetPoint(), nullptr);
1661 *pEndPam->GetPoint() = // park it because node is deleted
1662 SwPosition(GetDoc()->GetNodes().GetEndOfContent());
1663 aDelete.MovePara(GoCurrPara, fnParaStart);
1664 aDelete.SetMark();
1665 aDelete.MovePara(GoCurrPara, fnParaEnd);
1666 GetDoc()->getIDocumentContentOperations().DelFullPara(aDelete);
1668 throw lang::IllegalArgumentException();
1672 // make a selection from pStartPam to pEndPam
1673 // If there is no content in the frame the shape is in
1674 // it gets deleted in the DelFullPara call below,
1675 // In this case insert a tmp text node ( we delete it later )
1676 if (pStartPam->Start()->GetNode() == pEndPam->Start()->GetNode()
1677 && pStartPam->End()->GetNode() == pEndPam->End()->GetNode())
1679 SwPosition aEnd(*pStartPam->End());
1680 bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd );
1681 pEndPam->DeleteMark();
1682 *pEndPam->GetPoint() = aEnd;
1683 *oAnchorCheckPam->End() = aEnd;
1685 pStartPam->SetMark();
1686 *pStartPam->End() = *pEndPam->End();
1687 pEndPam.reset();
1689 // see if there are frames already anchored to this node
1690 // we have to work with the SdrObjects, as unique name is not guaranteed in their frame format
1691 // tdf#115094: do nothing if we have a graphic node
1692 o3tl::sorted_vector<const SdrObject*> aAnchoredObjectsByPtr;
1693 std::set<OUString> aAnchoredObjectsByName;
1694 for (size_t i = 0; i < m_pImpl->m_pDoc->GetSpzFrameFormats()->size(); ++i)
1696 const SwFrameFormat* pFrameFormat = (*m_pImpl->m_pDoc->GetSpzFrameFormats())[i];
1697 const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
1698 // note: Word can do at-char anchors in text frames - sometimes!
1699 // see testFlyInFly for why this checks only the edges of the selection,
1700 // and testFloatingTablesAnchor for why it excludes pre/post table
1701 // added nodes
1702 if (!isGraphicNode(pFrameFormat)
1703 && (IsAtParaMatch(*oAnchorCheckPam, rAnchor)
1704 || (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()
1705 && ( *oAnchorCheckPam->Start() == *rAnchor.GetContentAnchor()
1706 || *oAnchorCheckPam->End() == *rAnchor.GetContentAnchor()))))
1708 if (pFrameFormat->GetName().isEmpty())
1710 aAnchoredObjectsByPtr.insert(pFrameFormat->FindSdrObject());
1712 else
1714 aAnchoredObjectsByName.insert(pFrameFormat->GetName());
1718 oAnchorCheckPam.reset(); // clear SwIndex before deleting nodes
1720 const uno::Reference<text::XTextFrame> xNewFrame(
1721 SwXTextFrame::CreateXTextFrame(*m_pImpl->m_pDoc, nullptr));
1722 SwXTextFrame& rNewFrame = dynamic_cast<SwXTextFrame&>(*xNewFrame);
1725 for (const beans::PropertyValue& rValue : rFrameProperties)
1727 rNewFrame.SwXFrame::setPropertyValue(rValue.Name, rValue.Value);
1730 { // has to be in a block to remove the SwIndexes before
1731 // DelFullPara is called
1732 const uno::Reference< text::XTextRange> xInsertTextRange =
1733 new SwXTextRange(*pStartPam, this);
1734 assert(rNewFrame.IsDescriptor());
1735 rNewFrame.attachToRange(xInsertTextRange, pStartPam.get());
1736 assert(!rNewFrame.getName().isEmpty());
1739 SwTextNode *const pTextNode(pStartPam->GetPointNode().GetTextNode());
1740 assert(pTextNode);
1741 if (!pTextNode || !pTextNode->Len()) // don't remove if it contains text!
1743 { // has to be in a block to remove the SwIndexes before
1744 // DelFullPara is called
1745 SwPaM aMovePam( pStartPam->GetPointNode() );
1746 if (aMovePam.Move( fnMoveForward, GoInContent ))
1748 // move the anchor to the next paragraph
1749 SwFormatAnchor aNewAnchor(rNewFrame.GetFrameFormat()->GetAnchor());
1750 aNewAnchor.SetAnchor( aMovePam.Start() );
1751 m_pImpl->m_pDoc->SetAttr(
1752 aNewAnchor, *rNewFrame.GetFrameFormat() );
1754 // also move frames anchored to us
1755 for (size_t i = 0; i < m_pImpl->m_pDoc->GetSpzFrameFormats()->size(); ++i)
1757 SwFrameFormat* pFrameFormat = (*m_pImpl->m_pDoc->GetSpzFrameFormats())[i];
1758 if ((!pFrameFormat->GetName().isEmpty() && aAnchoredObjectsByName.find(pFrameFormat->GetName()) != aAnchoredObjectsByName.end() ) ||
1759 ( pFrameFormat->GetName().isEmpty() && aAnchoredObjectsByPtr.find(pFrameFormat->FindSdrObject()) != aAnchoredObjectsByPtr.end()) )
1761 // copy the anchor to the next paragraph
1762 SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
1763 aAnchor.SetAnchor(aMovePam.Start());
1764 m_pImpl->m_pDoc->SetAttr(aAnchor, *pFrameFormat);
1766 else
1768 // if this frame is a textbox of a shape anchored to us, move this textbox too.
1769 const auto& pTextBoxes = pFrameFormat->GetOtherTextBoxFormats();
1770 if (pFrameFormat->Which() == RES_FLYFRMFMT && pTextBoxes
1771 && pTextBoxes->GetOwnerShape())
1773 const auto& rShapeAnchor = pTextBoxes->GetOwnerShape()->GetAnchor();
1774 if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
1775 && rShapeAnchor.GetContentAnchor() && pFrameFormat->GetAnchor().GetContentAnchor()
1776 && pStartPam->ContainsPosition(*pFrameFormat->GetAnchor().GetContentAnchor()))
1778 const SwNode& rAnchorNode
1779 = *pFrameFormat->GetAnchor().GetAnchorNode();
1780 if (!(rAnchorNode.FindFooterStartNode() || rAnchorNode.FindHeaderStartNode()))
1782 SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
1783 aAnchor.SetAnchor(aMovePam.Start());
1784 m_pImpl->m_pDoc->SetAttr(aAnchor, *pFrameFormat);
1792 m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pStartPam);
1795 catch (const lang::IllegalArgumentException& rIllegal)
1797 sMessage = rIllegal.Message;
1798 bIllegalException = true;
1800 catch (const uno::RuntimeException& rRuntime)
1802 sMessage = rRuntime.Message;
1803 bRuntimeException = true;
1805 xRet = xNewFrame;
1806 if (bParaBeforeInserted || bParaAfterInserted)
1808 const rtl::Reference<SwXTextCursor> xFrameTextCursor =
1809 rNewFrame.createXTextCursor();
1810 if (bParaBeforeInserted)
1812 // todo: remove paragraph before frame
1813 m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*xFrameTextCursor->GetPaM());
1815 if (bParaAfterInserted)
1817 xFrameTextCursor->gotoEnd(false);
1818 if (!bParaBeforeInserted)
1819 m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*xFrameTextCursor->GetPaM());
1820 else
1822 // In case the frame has a table only, the cursor points to the end of the first cell of the table.
1823 SwPaM aPaM(*xFrameTextCursor->GetPaM()->GetPointNode().FindSttNodeByType(SwFlyStartNode)->EndOfSectionNode());
1824 // Now we have the end of the frame -- the node before that will be the paragraph we want to remove.
1825 aPaM.GetPoint()->Adjust(SwNodeOffset(-1));
1826 m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(aPaM);
1831 m_pImpl->m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
1832 if (bIllegalException || bRuntimeException)
1834 m_pImpl->m_pDoc->GetIDocumentUndoRedo().Undo();
1835 if (bIllegalException)
1837 lang::IllegalArgumentException aEx;
1838 aEx.Message = sMessage;
1839 throw aEx;
1841 else
1843 uno::RuntimeException aEx;
1844 aEx.Message = sMessage;
1845 throw aEx;
1848 return xRet;
1851 namespace {
1853 // Move previously imported paragraphs into a new text table.
1854 struct VerticallyMergedCell
1856 std::vector<uno::Reference< beans::XPropertySet > > aCells;
1857 sal_Int32 nLeftPosition;
1858 bool bOpen;
1860 VerticallyMergedCell(uno::Reference< beans::XPropertySet > const& rxCell,
1861 const sal_Int32 nLeft)
1862 : nLeftPosition( nLeft )
1863 , bOpen( true )
1865 aCells.push_back( rxCell );
1871 #define COL_POS_FUZZY 2
1873 static bool lcl_SimilarPosition( const sal_Int32 nPos1, const sal_Int32 nPos2 )
1875 return abs( nPos1 - nPos2 ) < COL_POS_FUZZY;
1878 void SwXText::Impl::ConvertCell(
1879 const uno::Sequence< uno::Reference< text::XTextRange > > & rCell,
1880 std::vector<SwNodeRange> & rRowNodes,
1881 SwNodeRange *const pLastCell)
1883 if (rCell.getLength() != 2)
1885 throw lang::IllegalArgumentException(
1886 "rCell needs to contain 2 elements",
1887 uno::Reference< text::XTextCopy >( &m_rThis ), sal_Int16( 2 ) );
1889 const uno::Reference<text::XTextRange> xStartRange = rCell[0];
1890 const uno::Reference<text::XTextRange> xEndRange = rCell[1];
1891 SwUnoInternalPaM aStartCellPam(*m_pDoc);
1892 SwUnoInternalPaM aEndCellPam(*m_pDoc);
1894 // !!! TODO - PaMs in tables and sections do not work here -
1895 // the same applies to PaMs in frames !!!
1897 if (!::sw::XTextRangeToSwPaM(aStartCellPam, xStartRange) ||
1898 !::sw::XTextRangeToSwPaM(aEndCellPam, xEndRange))
1900 throw lang::IllegalArgumentException(
1901 "Start or End range cannot be resolved to a SwPaM",
1902 uno::Reference< text::XTextCopy >( &m_rThis ), sal_Int16( 2 ) );
1905 SwNodeRange aTmpRange(aStartCellPam.Start()->GetNode(),
1906 aEndCellPam.End()->GetNode());
1907 std::optional<SwNodeRange> oCorrectedRange;
1908 m_pDoc->GetNodes().ExpandRangeForTableBox(aTmpRange, oCorrectedRange);
1910 if (oCorrectedRange)
1912 SwPaM aNewStartPaM(oCorrectedRange->aStart, 0);
1913 aStartCellPam = aNewStartPaM;
1915 sal_Int32 nEndLen = 0;
1916 SwTextNode * pTextNode = oCorrectedRange->aEnd.GetNode().GetTextNode();
1917 if (pTextNode != nullptr)
1918 nEndLen = pTextNode->Len();
1920 SwPaM aNewEndPaM(oCorrectedRange->aEnd, nEndLen);
1921 aEndCellPam = aNewEndPaM;
1923 oCorrectedRange.reset();
1926 /** check the nodes between start and end
1927 it is allowed to have pairs of StartNode/EndNodes
1929 if (aStartCellPam.Start()->GetNode() < aEndCellPam.End()->GetNode())
1931 // increment on each StartNode and decrement on each EndNode
1932 // we must reach zero at the end and must not go below zero
1933 tools::Long nOpenNodeBlock = 0;
1934 SwNodeIndex aCellIndex(aStartCellPam.Start()->GetNode());
1935 while (aCellIndex < aEndCellPam.End()->GetNodeIndex())
1937 if (aCellIndex.GetNode().IsStartNode())
1939 ++nOpenNodeBlock;
1941 else if (aCellIndex.GetNode().IsEndNode())
1943 --nOpenNodeBlock;
1945 if (nOpenNodeBlock < 0)
1947 throw lang::IllegalArgumentException();
1949 ++aCellIndex;
1951 if (nOpenNodeBlock != 0)
1953 throw lang::IllegalArgumentException();
1957 /** The vector<vector> NodeRanges has to contain consecutive nodes.
1958 In rTableRanges the ranges don't need to be full paragraphs but
1959 they have to follow each other. To process the ranges they
1960 have to be aligned on paragraph borders by inserting paragraph
1961 breaks. Non-consecutive ranges must initiate an exception.
1963 if (!pLastCell) // first cell?
1965 // align the beginning - if necessary
1966 if (aStartCellPam.Start()->GetContentIndex())
1968 m_pDoc->getIDocumentContentOperations().SplitNode(*aStartCellPam.Start(), false);
1971 else
1973 // check the predecessor
1974 const SwNodeOffset nStartCellNodeIndex =
1975 aStartCellPam.Start()->GetNodeIndex();
1976 const SwNodeOffset nLastNodeEndIndex = pLastCell->aEnd.GetIndex();
1977 if (nLastNodeEndIndex == nStartCellNodeIndex)
1979 // same node as predecessor then equal nContent?
1980 if (0 != aStartCellPam.Start()->GetContentIndex())
1982 throw lang::IllegalArgumentException();
1985 m_pDoc->getIDocumentContentOperations().SplitNode(*aStartCellPam.Start(), false);
1986 SwNodeOffset const nNewIndex(aStartCellPam.Start()->GetNodeIndex());
1987 if (nNewIndex != nStartCellNodeIndex)
1989 // aStartCellPam now points to the 2nd node
1990 // the last cell may *also* point to 2nd node now - fix it!
1991 assert(nNewIndex == nStartCellNodeIndex + 1);
1992 if (pLastCell->aEnd.GetIndex() == nNewIndex)
1994 --pLastCell->aEnd;
1995 if (pLastCell->aStart.GetIndex() == nNewIndex)
1997 --pLastCell->aStart;
2002 else if (nStartCellNodeIndex == (nLastNodeEndIndex + 1))
2004 // next paragraph - now the content index of the new should be 0
2005 // and of the old one should be equal to the text length
2006 // but if it isn't we don't care - the cell is being inserted on
2007 // the node border anyway
2009 else
2011 throw lang::IllegalArgumentException();
2014 // now check if there's a need to insert another paragraph break
2015 if (aEndCellPam.End()->GetContentIndex() <
2016 aEndCellPam.End()->GetNode().GetTextNode()->Len())
2018 m_pDoc->getIDocumentContentOperations().SplitNode(*aEndCellPam.End(), false);
2019 // take care that the new start/endcell is moved to the right position
2020 // aStartCellPam has to point to the start of the new (previous) node
2021 // aEndCellPam has to point to the end of the new (previous) node
2022 aStartCellPam.DeleteMark();
2023 aStartCellPam.Move(fnMoveBackward, GoInNode);
2024 aStartCellPam.GetPoint()->SetContent(0);
2025 aEndCellPam.DeleteMark();
2026 aEndCellPam.Move(fnMoveBackward, GoInNode);
2027 aEndCellPam.GetPoint()->SetContent(
2028 aEndCellPam.GetPointNode().GetTextNode()->Len() );
2031 assert(aStartCellPam.Start()->GetContentIndex() == 0);
2032 assert(aEndCellPam.End()->GetContentIndex() == aEndCellPam.End()->GetNode().GetTextNode()->Len());
2033 SwNodeRange aCellRange(aStartCellPam.Start()->GetNode(),
2034 aEndCellPam.End()->GetNode());
2035 rRowNodes.push_back(aCellRange); // note: invalidates pLastCell!
2037 // tdf#149649 delete any fieldmarks overlapping the cell
2038 IDocumentMarkAccess & rIDMA(*m_pDoc->getIDocumentMarkAccess());
2039 while (sw::mark::IFieldmark *const pMark = rIDMA.getInnerFieldmarkFor(*aStartCellPam.Start()))
2041 if (pMark->GetMarkEnd() <= *aEndCellPam.End())
2043 if (pMark->GetMarkStart() < *aStartCellPam.Start())
2045 SAL_INFO("sw.uno", "deleting fieldmark overlapping table cell");
2046 rIDMA.deleteMark(pMark);
2048 else
2050 break;
2053 else
2055 SwPosition const sepPos(::sw::mark::FindFieldSep(*pMark));
2056 if (*aStartCellPam.Start() <= sepPos && sepPos <= *aEndCellPam.End())
2058 SAL_INFO("sw.uno", "deleting fieldmark with separator in table cell");
2059 rIDMA.deleteMark(pMark);
2061 else
2063 break;
2067 while (sw::mark::IFieldmark *const pMark = rIDMA.getInnerFieldmarkFor(*aEndCellPam.End()))
2069 if (*aStartCellPam.Start() <= pMark->GetMarkStart())
2071 if (*aEndCellPam.End() < pMark->GetMarkEnd())
2073 SAL_INFO("sw.uno", "deleting fieldmark overlapping table cell");
2074 rIDMA.deleteMark(pMark);
2076 else
2078 break;
2081 else
2083 SwPosition const sepPos(::sw::mark::FindFieldSep(*pMark));
2084 if (*aStartCellPam.Start() <= sepPos && sepPos <= *aEndCellPam.End())
2086 SAL_INFO("sw.uno", "deleting fieldmark with separator in table cell");
2087 rIDMA.deleteMark(pMark);
2089 else
2091 break;
2097 typedef uno::Sequence< text::TableColumnSeparator > TableColumnSeparators;
2099 static void
2100 lcl_ApplyRowProperties(
2101 uno::Sequence<beans::PropertyValue> const& rRowProperties,
2102 uno::Any const& rRow,
2103 TableColumnSeparators & rRowSeparators)
2105 uno::Reference< beans::XPropertySet > xRow;
2106 rRow >>= xRow;
2107 for (const beans::PropertyValue& rProperty : rRowProperties)
2109 if ( rProperty.Name == "TableColumnSeparators" )
2111 // add the separators to access the cell's positions
2112 // for vertical merging later
2113 TableColumnSeparators aSeparators;
2114 rProperty.Value >>= aSeparators;
2115 rRowSeparators = aSeparators;
2117 xRow->setPropertyValue(rProperty.Name, rProperty.Value);
2121 static sal_Int32 lcl_GetLeftPos(sal_Int32 nCell, TableColumnSeparators const& rRowSeparators)
2123 if(!nCell)
2124 return 0;
2125 if (rRowSeparators.getLength() < nCell)
2126 return -1;
2127 return rRowSeparators[nCell - 1].Position;
2130 static void
2131 lcl_ApplyCellProperties(
2132 const sal_Int32 nLeftPos,
2133 const uno::Sequence< beans::PropertyValue >& rCellProperties,
2134 const uno::Reference< uno::XInterface >& xCell,
2135 std::vector<VerticallyMergedCell> & rMergedCells)
2137 const uno::Reference< beans::XPropertySet > xCellPS(xCell, uno::UNO_QUERY);
2138 for (const auto& rCellProperty : rCellProperties)
2140 const OUString & rName = rCellProperty.Name;
2141 const uno::Any & rValue = rCellProperty.Value;
2142 if ( rName == "VerticalMerge" )
2144 // determine left border position
2145 // add the cell to a queue of merged cells
2146 bool bMerge = false;
2147 rValue >>= bMerge;
2148 if (bMerge)
2150 // 'close' all the cell with the same left position
2151 // if separate vertical merges in the same column exist
2152 for(auto& aMergedCell : rMergedCells)
2154 if(lcl_SimilarPosition(aMergedCell.nLeftPosition, nLeftPos))
2156 aMergedCell.bOpen = false;
2159 // add the new group of merged cells
2160 rMergedCells.emplace_back(xCellPS, nLeftPos);
2162 else
2164 bool bFound = false;
2165 SAL_WARN_IF(rMergedCells.empty(), "sw.uno", "the first merged cell is missing");
2166 for(auto& aMergedCell : rMergedCells)
2168 if (aMergedCell.bOpen && lcl_SimilarPosition(aMergedCell.nLeftPosition, nLeftPos))
2170 aMergedCell.aCells.push_back( xCellPS );
2171 bFound = true;
2174 SAL_WARN_IF(!bFound, "sw.uno", "couldn't find first vertically merged cell" );
2177 else
2181 static const std::initializer_list<std::u16string_view> vDenylist = {
2182 u"LeftMargin",
2184 if (std::find(vDenylist.begin(), vDenylist.end(), rName) == vDenylist.end())
2186 xCellPS->setPropertyValue(rName, rValue);
2189 catch (const uno::Exception&)
2191 TOOLS_WARN_EXCEPTION( "sw.uno", "Exception when setting cell property " << rName );
2197 static void
2198 lcl_MergeCells(std::vector<VerticallyMergedCell> & rMergedCells)
2200 for(auto& aMergedCell : rMergedCells)
2202 // the first of the cells gets the number of cells set as RowSpan
2203 // the others get the inverted number of remaining merged cells
2204 // (3,-2,-1)
2205 sal_Int32 nCellCount = static_cast<sal_Int32>(aMergedCell.aCells.size());
2206 if(nCellCount<2)
2208 SAL_WARN("sw.uno", "incomplete vertical cell merge");
2209 continue;
2211 aMergedCell.aCells.front()->setPropertyValue(UNO_NAME_ROW_SPAN, uno::Any(nCellCount--));
2212 nCellCount*=-1;
2213 for(auto pxPSet = aMergedCell.aCells.begin()+1; nCellCount<0; ++pxPSet, ++nCellCount)
2215 (*pxPSet)->setPropertyValue(UNO_NAME_ROW_SPAN, uno::Any(nCellCount));
2216 (*pxPSet)->setPropertyValue("VerticalMerge", uno::Any(true));
2221 uno::Reference< text::XTextTable > SAL_CALL
2222 SwXText::convertToTable(
2223 const uno::Sequence< uno::Sequence< uno::Sequence<
2224 uno::Reference< text::XTextRange > > > >& rTableRanges,
2225 const uno::Sequence< uno::Sequence< uno::Sequence<
2226 beans::PropertyValue > > >& rCellProperties,
2227 const uno::Sequence< uno::Sequence< beans::PropertyValue > >&
2228 rRowProperties,
2229 const uno::Sequence< beans::PropertyValue >& rTableProperties)
2231 SolarMutexGuard aGuard;
2233 if(!IsValid())
2235 throw uno::RuntimeException();
2238 IDocumentRedlineAccess & rIDRA(m_pImpl->m_pDoc->getIDocumentRedlineAccess());
2239 if (!IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()))
2241 throw uno::RuntimeException(
2242 "cannot convertToTable if tracked changes are hidden!");
2245 //at first collect the text ranges as SwPaMs
2246 const uno::Sequence< uno::Sequence< uno::Reference< text::XTextRange > > >*
2247 pTableRanges = rTableRanges.getConstArray();
2248 std::vector< std::vector<SwNodeRange> > aTableNodes;
2249 for (sal_Int32 nRow = 0; nRow < rTableRanges.getLength(); ++nRow)
2251 std::vector<SwNodeRange> aRowNodes;
2252 const uno::Sequence< uno::Reference< text::XTextRange > >* pRow =
2253 pTableRanges[nRow].getConstArray();
2254 const sal_Int32 nCells(pTableRanges[nRow].getLength());
2256 if (0 == nCells) // this would lead to no pLastCell below
2257 { // and make it impossible to detect node gaps
2258 throw lang::IllegalArgumentException();
2261 for (sal_Int32 nCell = 0; nCell < nCells; ++nCell)
2263 SwNodeRange *const pLastCell(
2264 (nCell == 0)
2265 ? ((nRow == 0)
2266 ? nullptr
2267 : &*aTableNodes.rbegin()->rbegin())
2268 : &*aRowNodes.rbegin());
2269 m_pImpl->ConvertCell(pRow[nCell], aRowNodes, pLastCell);
2271 assert(!aRowNodes.empty());
2272 aTableNodes.push_back(aRowNodes);
2275 std::vector< TableColumnSeparators >
2276 aRowSeparators(rRowProperties.getLength());
2277 std::vector<VerticallyMergedCell> aMergedCells;
2279 SwTable const*const pTable = m_pImpl->m_pDoc->TextToTable( aTableNodes );
2281 if (!pTable)
2282 return uno::Reference< text::XTextTable >();
2284 uno::Reference<text::XTextTable> const xRet =
2285 SwXTextTable::CreateXTextTable(pTable->GetFrameFormat());
2286 uno::Reference<beans::XPropertySet> const xPrSet(xRet, uno::UNO_QUERY);
2287 // set properties to the table
2288 // catch lang::WrappedTargetException and lang::IndexOutOfBoundsException
2291 //apply table properties
2292 for(const auto& rTableProperty : rTableProperties)
2296 static const std::initializer_list<std::u16string_view> vDenylist = {
2297 u"BottomBorder",
2298 u"CharAutoKerning",
2299 u"CharFontName",
2300 u"CharFontNameAsian",
2301 u"CharFontNameComplex",
2302 u"CharHeight",
2303 u"CharHeightAsian",
2304 u"CharHeightComplex",
2305 u"CharInteropGrabBag",
2306 u"CharLocale",
2307 u"CharLocaleAsian",
2308 u"CharLocaleComplex",
2309 u"HorizontalBorder",
2310 u"LeftBorder",
2311 u"ParaAdjust",
2312 u"ParaBottomMargin",
2313 u"ParaIsHyphenation",
2314 u"ParaLineSpacing",
2315 u"ParaOrphans",
2316 u"ParaTopMargin",
2317 u"ParaWidows",
2318 u"RightBorder",
2319 u"TopBorder",
2320 u"VerticalBorder",
2322 if (std::find(vDenylist.begin(), vDenylist.end(), rTableProperty.Name) == vDenylist.end())
2324 xPrSet->setPropertyValue(rTableProperty.Name, rTableProperty.Value);
2327 catch (const uno::Exception&)
2329 TOOLS_WARN_EXCEPTION( "sw.uno", "Exception when setting property: " << rTableProperty.Name );
2333 //apply row properties
2334 const auto xRows = xRet->getRows();
2335 const sal_Int32 nLast = std::min(xRows->getCount(), rRowProperties.getLength());
2336 SAL_WARN_IF(nLast != rRowProperties.getLength(), "sw.uno", "not enough rows for properties");
2337 for(sal_Int32 nCnt = 0; nCnt < nLast; ++nCnt)
2338 lcl_ApplyRowProperties(rRowProperties[nCnt], xRows->getByIndex(nCnt), aRowSeparators[nCnt]);
2340 uno::Reference<table::XCellRange> const xCR(xRet, uno::UNO_QUERY_THROW);
2341 //apply cell properties
2342 sal_Int32 nRow = 0;
2343 for(const auto& rCellPropertiesForRow : rCellProperties)
2345 sal_Int32 nCell = 0;
2346 for(const auto& rCellProps : rCellPropertiesForRow)
2348 lcl_ApplyCellProperties(lcl_GetLeftPos(nCell, aRowSeparators[nRow]),
2349 rCellProps,
2350 xCR->getCellByPosition(nCell, nRow),
2351 aMergedCells);
2352 ++nCell;
2354 ++nRow;
2357 // now that the cell properties are set the vertical merge values
2358 // have to be applied
2359 lcl_MergeCells(aMergedCells);
2361 catch (const lang::WrappedTargetException&)
2364 catch (const lang::IndexOutOfBoundsException&)
2368 assert(SwTable::FindTable(pTable->GetFrameFormat()) == pTable);
2369 assert(pTable->GetFrameFormat() ==
2370 dynamic_cast<SwXTextTable&>(*xRet).GetFrameFormat());
2371 return xRet;
2374 void SAL_CALL
2375 SwXText::copyText(
2376 const uno::Reference< text::XTextCopy >& xSource )
2378 SolarMutexGuard aGuard;
2380 SwXText const* const pSource(dynamic_cast<SwXText*>(xSource.get()));
2382 uno::Reference< text::XText > const xText(xSource, uno::UNO_QUERY_THROW);
2383 uno::Reference< text::XTextCursor > const xCursor =
2384 xText->createTextCursor();
2385 xCursor->gotoEnd( true );
2387 OTextCursorHelper *const pCursor = dynamic_cast<OTextCursorHelper*>(xCursor.get());
2388 if (!pCursor)
2390 throw uno::RuntimeException();
2393 SwNodeIndex rNdIndex( *GetStartNode( ), 1 );
2394 SwPosition rPos( rNdIndex );
2395 // tdf#112202 need SwXText because cursor cannot select table at the start
2396 if (pSource)
2398 SwTextNode * pFirstNode;
2400 SwPaM temp(*pSource->GetStartNode(), *pSource->GetStartNode()->EndOfSectionNode(), SwNodeOffset(+1), SwNodeOffset(-1));
2401 pFirstNode = temp.GetMark()->GetNode().GetTextNode();
2402 if (pFirstNode)
2404 temp.GetMark()->AssignStartIndex(*pFirstNode);
2406 if (SwTextNode *const pNode = temp.GetPoint()->GetNode().GetTextNode())
2408 temp.GetPoint()->AssignEndIndex(*pNode);
2410 // Explicitly request copy text mode, so
2411 // sw::DocumentContentOperationsManager::CopyFlyInFlyImpl() will copy shapes anchored to
2412 // us, even if we have only a single paragraph.
2413 m_pImpl->m_pDoc->getIDocumentContentOperations().CopyRange(temp, rPos, SwCopyFlags::CheckPosInFly);
2415 if (!pFirstNode)
2416 { // the node at rPos was split; get rid of the first empty one so
2417 // that the pasted table is first
2418 auto pDelCursor(m_pImpl->m_pDoc->CreateUnoCursor(SwPosition(*GetStartNode(), SwNodeOffset(1))));
2419 m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pDelCursor);
2422 else
2424 m_pImpl->m_pDoc->getIDocumentContentOperations().CopyRange(*pCursor->GetPaM(), rPos, SwCopyFlags::CheckPosInFly);
2429 SwXBodyText::SwXBodyText(SwDoc *const pDoc)
2430 : SwXText(pDoc, CursorType::Body)
2434 SwXBodyText::~SwXBodyText()
2438 OUString SAL_CALL
2439 SwXBodyText::getImplementationName()
2441 return "SwXBodyText";
2444 sal_Bool SAL_CALL SwXBodyText::supportsService(const OUString& rServiceName)
2446 return cppu::supportsService(this, rServiceName);
2449 uno::Sequence< OUString > SAL_CALL
2450 SwXBodyText::getSupportedServiceNames()
2452 return { "com.sun.star.text.Text" };
2455 uno::Sequence< uno::Type > SAL_CALL
2456 SwXBodyText::getTypes()
2458 const uno::Sequence< uno::Type > aTypes = SwXBodyText_Base::getTypes();
2459 const uno::Sequence< uno::Type > aTextTypes = SwXText::getTypes();
2460 return ::comphelper::concatSequences(aTypes, aTextTypes);
2463 uno::Sequence< sal_Int8 > SAL_CALL
2464 SwXBodyText::getImplementationId()
2466 return css::uno::Sequence<sal_Int8>();
2469 uno::Any SAL_CALL
2470 SwXBodyText::queryInterface(const uno::Type& rType)
2472 const uno::Any ret = SwXText::queryInterface(rType);
2473 return (ret.getValueType() == cppu::UnoType<void>::get())
2474 ? SwXBodyText_Base::queryInterface(rType)
2475 : ret;
2478 rtl::Reference<SwXTextCursor> SwXBodyText::CreateTextCursor(const bool bIgnoreTables)
2480 if(!IsValid())
2482 return nullptr;
2485 // the cursor has to skip tables contained in this text
2486 SwPaM aPam(GetDoc()->GetNodes().GetEndOfContent());
2487 aPam.Move( fnMoveBackward, GoInDoc );
2488 if (!bIgnoreTables)
2490 SwTableNode * pTableNode = aPam.GetPointNode().FindTableNode();
2491 while (pTableNode)
2493 aPam.GetPoint()->Assign( *pTableNode->EndOfSectionNode() );
2494 SwContentNode* pCont = GetDoc()->GetNodes().GoNext(aPam.GetPoint());
2495 pTableNode = pCont->FindTableNode();
2498 return new SwXTextCursor(*GetDoc(), this, CursorType::Body, *aPam.GetPoint());
2501 rtl::Reference< SwXTextCursor >
2502 SwXBodyText::createXTextCursor()
2504 return CreateTextCursor();
2507 rtl::Reference< SwXTextCursor >
2508 SwXBodyText::createXTextCursorByRange(
2509 const uno::Reference< text::XTextRange > & xTextPosition)
2511 if(!IsValid())
2513 uno::RuntimeException aRuntime;
2514 aRuntime.Message = cInvalidObject;
2515 throw aRuntime;
2518 rtl::Reference< SwXTextCursor > aRef;
2519 SwUnoInternalPaM aPam(*GetDoc());
2520 if (::sw::XTextRangeToSwPaM(aPam, xTextPosition))
2522 if ( !aPam.GetPointNode().GetTextNode() )
2523 throw uno::RuntimeException("Invalid text range" );
2525 SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
2527 SwStartNode* p1 = aPam.GetPointNode().StartOfSectionNode();
2528 //document starts with a section?
2529 while(p1->IsSectionNode())
2531 p1 = p1->StartOfSectionNode();
2533 SwStartNode *const p2 = rNode.StartOfSectionNode();
2535 if(p1 == p2)
2537 aRef = new SwXTextCursor(*GetDoc(), this, CursorType::Body,
2538 *aPam.GetPoint(), aPam.GetMark());
2541 if(!aRef.is())
2543 throw uno::RuntimeException( "End of content node doesn't have the proper start node",
2544 uno::Reference< uno::XInterface >( *this ) );
2546 return aRef;
2549 uno::Reference< container::XEnumeration > SAL_CALL
2550 SwXBodyText::createEnumeration()
2552 return createParagraphEnumeration();
2555 rtl::Reference< SwXParagraphEnumeration >
2556 SwXBodyText::createParagraphEnumeration()
2558 SolarMutexGuard aGuard;
2560 if (!IsValid())
2562 uno::RuntimeException aRuntime;
2563 aRuntime.Message = cInvalidObject;
2564 throw aRuntime;
2567 SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
2568 SwPosition aPos(rNode);
2569 auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
2570 pUnoCursor->Move(fnMoveBackward, GoInDoc);
2571 return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Body);
2574 uno::Type SAL_CALL
2575 SwXBodyText::getElementType()
2577 return cppu::UnoType<text::XTextRange>::get();
2580 sal_Bool SAL_CALL
2581 SwXBodyText::hasElements()
2583 SolarMutexGuard aGuard;
2585 if (!IsValid())
2587 uno::RuntimeException aRuntime;
2588 aRuntime.Message = cInvalidObject;
2589 throw aRuntime;
2592 return true;
2595 class SwXHeadFootText::Impl
2596 : public SvtListener
2598 public:
2599 SwFrameFormat* m_pHeadFootFormat;
2600 bool m_bIsHeader;
2602 Impl(SwFrameFormat& rHeadFootFormat, const bool bIsHeader)
2603 : m_pHeadFootFormat(&rHeadFootFormat)
2604 , m_bIsHeader(bIsHeader)
2606 StartListening(m_pHeadFootFormat->GetNotifier());
2609 SwFrameFormat* GetHeadFootFormat() const {
2610 return m_pHeadFootFormat;
2613 SwFrameFormat& GetHeadFootFormatOrThrow() {
2614 if (!m_pHeadFootFormat) {
2615 throw uno::RuntimeException("SwXHeadFootText: disposed or invalid", nullptr);
2617 return *m_pHeadFootFormat;
2619 protected:
2620 virtual void Notify(const SfxHint& rHint) override
2622 if(rHint.GetId() == SfxHintId::Dying)
2623 m_pHeadFootFormat = nullptr;
2627 uno::Reference<text::XText> SwXHeadFootText::CreateXHeadFootText(
2628 SwFrameFormat& rHeadFootFormat,
2629 const bool bIsHeader)
2631 // re-use existing SwXHeadFootText
2632 // #i105557#: do not iterate over the registered clients: race condition
2633 uno::Reference<text::XText> xText(rHeadFootFormat.GetXObject(), uno::UNO_QUERY);
2634 if(!xText.is())
2636 xText = new SwXHeadFootText(rHeadFootFormat, bIsHeader);
2637 rHeadFootFormat.SetXObject(xText);
2639 return xText;
2642 SwXHeadFootText::SwXHeadFootText(SwFrameFormat& rHeadFootFormat, const bool bIsHeader)
2643 : SwXText(
2644 rHeadFootFormat.GetDoc(),
2645 bIsHeader ? CursorType::Header : CursorType::Footer)
2646 , m_pImpl(new SwXHeadFootText::Impl(rHeadFootFormat, bIsHeader))
2650 SwXHeadFootText::~SwXHeadFootText()
2653 OUString SAL_CALL
2654 SwXHeadFootText::getImplementationName()
2656 return {"SwXHeadFootText"};
2659 sal_Bool SAL_CALL SwXHeadFootText::supportsService(const OUString& rServiceName)
2661 return cppu::supportsService(this, rServiceName);
2664 uno::Sequence<OUString> SAL_CALL
2665 SwXHeadFootText::getSupportedServiceNames()
2667 return {"com.sun.star.text.Text"};
2670 const SwStartNode* SwXHeadFootText::GetStartNode() const
2672 const SwStartNode* pSttNd = nullptr;
2673 SwFrameFormat* const pHeadFootFormat = m_pImpl->GetHeadFootFormat();
2674 if(pHeadFootFormat)
2676 const SwFormatContent& rFlyContent = pHeadFootFormat->GetContent();
2677 if(rFlyContent.GetContentIdx())
2679 pSttNd = rFlyContent.GetContentIdx()->GetNode().GetStartNode();
2682 return pSttNd;
2685 uno::Sequence<uno::Type> SAL_CALL SwXHeadFootText::getTypes()
2687 return ::comphelper::concatSequences(
2688 SwXHeadFootText_Base::getTypes(),
2689 SwXText::getTypes());
2692 uno::Sequence<sal_Int8> SAL_CALL SwXHeadFootText::getImplementationId()
2694 return css::uno::Sequence<sal_Int8>();
2697 uno::Any SAL_CALL SwXHeadFootText::queryInterface(const uno::Type& rType)
2699 const uno::Any ret = SwXHeadFootText_Base::queryInterface(rType);
2700 return (ret.getValueType() == cppu::UnoType<void>::get())
2701 ? SwXText::queryInterface(rType)
2702 : ret;
2705 rtl::Reference<SwXTextCursor> SwXHeadFootText::CreateTextCursor(const bool bIgnoreTables)
2707 SwFrameFormat & rHeadFootFormat( m_pImpl->GetHeadFootFormatOrThrow() );
2709 const SwFormatContent& rFlyContent = rHeadFootFormat.GetContent();
2710 const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
2711 SwPosition aPos(rNode);
2712 rtl::Reference<SwXTextCursor> pXCursor = new SwXTextCursor(*GetDoc(), this,
2713 (m_pImpl->m_bIsHeader) ? CursorType::Header : CursorType::Footer, aPos);
2714 auto& rUnoCursor(pXCursor->GetCursor());
2715 rUnoCursor.Move(fnMoveForward, GoInNode);
2717 // save current start node to be able to check if there is content
2718 // after the table - otherwise the cursor would be in the body text!
2719 SwStartNode const*const pOwnStartNode = rNode.FindSttNodeByType(
2720 (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
2722 if (!bIgnoreTables)
2724 // is there a table here?
2725 SwTableNode* pTableNode = rUnoCursor.GetPointNode().FindTableNode();
2726 while (pTableNode)
2728 rUnoCursor.GetPoint()->Assign(*pTableNode->EndOfSectionNode());
2729 SwContentNode* pCont = GetDoc()->GetNodes().GoNext(rUnoCursor.GetPoint());
2730 pTableNode = pCont->FindTableNode();
2733 SwStartNode const*const pNewStartNode = rUnoCursor.GetPointNode().FindSttNodeByType(
2734 (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
2735 if (!pNewStartNode || (pNewStartNode != pOwnStartNode))
2737 uno::RuntimeException aExcept;
2738 aExcept.Message = "no text available";
2739 throw aExcept;
2741 return pXCursor;
2744 rtl::Reference< SwXTextCursor >
2745 SwXHeadFootText::createXTextCursor()
2747 return CreateTextCursor(false);
2750 rtl::Reference<SwXTextCursor> SwXHeadFootText::createXTextCursorByRange(
2751 const uno::Reference<text::XTextRange>& xTextPosition)
2753 SwFrameFormat& rHeadFootFormat( m_pImpl->GetHeadFootFormatOrThrow() );
2755 SwUnoInternalPaM aPam(*GetDoc());
2756 if (!sw::XTextRangeToSwPaM(aPam, xTextPosition))
2758 uno::RuntimeException aRuntime;
2759 aRuntime.Message = cInvalidObject;
2760 throw aRuntime;
2763 SwNode& rNode = rHeadFootFormat.GetContent().GetContentIdx()->GetNode();
2764 SwPosition aPos(rNode);
2765 SwPaM aHFPam(aPos);
2766 aHFPam.Move(fnMoveForward, GoInNode);
2767 SwStartNode* const pOwnStartNode = aHFPam.GetPointNode().FindSttNodeByType(
2768 (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
2769 SwStartNode* const p1 = aPam.GetPointNode().FindSttNodeByType(
2770 (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
2771 if (p1 == pOwnStartNode)
2773 return new SwXTextCursor(
2774 *GetDoc(),
2775 this,
2776 (m_pImpl->m_bIsHeader) ? CursorType::Header : CursorType::Footer,
2777 *aPam.GetPoint(), aPam.GetMark());
2779 return nullptr;
2782 uno::Reference<container::XEnumeration> SAL_CALL SwXHeadFootText::createEnumeration()
2784 SolarMutexGuard aGuard;
2785 SwFrameFormat& rHeadFootFormat(m_pImpl->GetHeadFootFormatOrThrow());
2787 const SwFormatContent& rFlyContent = rHeadFootFormat.GetContent();
2788 const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
2789 SwPosition aPos(rNode);
2790 auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
2791 pUnoCursor->Move(fnMoveForward, GoInNode);
2792 return SwXParagraphEnumeration::Create(
2793 this,
2794 pUnoCursor,
2795 (m_pImpl->m_bIsHeader)
2796 ? CursorType::Header
2797 : CursorType::Footer);
2800 uno::Type SAL_CALL SwXHeadFootText::getElementType()
2801 { return cppu::UnoType<text::XTextRange>::get(); }
2803 sal_Bool SAL_CALL SwXHeadFootText::hasElements()
2804 { return true; }
2806 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */