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