Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / unocore / unoobj.cxx
blob14cceaee3f38cfd6f43a2c37e115e376116d583c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/table/TableSortField.hpp>
21 #include <cppuhelper/exc_hlp.hxx>
22 #include <cppuhelper/supportsservice.hxx>
23 #include <svl/itemprop.hxx>
24 #include <o3tl/any.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <osl/endian.h>
27 #include <unotools/collatorwrapper.hxx>
29 #include <autostyle_helper.hxx>
30 #include <swtypes.hxx>
31 #include <hintids.hxx>
32 #include <cmdid.h>
33 #include <unomid.h>
34 #include <hints.hxx>
35 #include <doc.hxx>
36 #include <IDocumentUndoRedo.hxx>
37 #include <istyleaccess.hxx>
38 #include <ndtxt.hxx>
39 #include <unocrsr.hxx>
40 #include <unocrsrhelper.hxx>
41 #include <swundo.hxx>
42 #include <rootfrm.hxx>
43 #include <paratr.hxx>
44 #include <pam.hxx>
45 #include <shellio.hxx>
46 #include <unotbl.hxx>
47 #include <fmtruby.hxx>
48 #include <docsh.hxx>
49 #include <docstyle.hxx>
50 #include <fmtpdsc.hxx>
51 #include <pagedesc.hxx>
52 #include <edimp.hxx>
53 #include <fchrfmt.hxx>
54 #include <fmtautofmt.hxx>
55 #include <unotextrange.hxx>
56 #include <unotextcursor.hxx>
57 #include <unomap.hxx>
58 #include <unoprnms.hxx>
59 #include <unometa.hxx>
60 #include <unocontentcontrol.hxx>
61 #include <unotext.hxx>
62 #include <com/sun/star/text/TextMarkupType.hpp>
63 #include <utility>
64 #include <vcl/svapp.hxx>
65 #include <unotools/syslocale.hxx>
66 #include <i18nlangtag/languagetag.hxx>
67 #include <SwStyleNameMapper.hxx>
68 #include <sortopt.hxx>
69 #include <com/sun/star/beans/PropertyAttribute.hpp>
70 #include <com/sun/star/beans/NamedValue.hpp>
71 #include <com/sun/star/i18n/WordType.hpp>
72 #include <memory>
73 #include <unoparaframeenum.hxx>
74 #include <unoparagraph.hxx>
75 #include <iodetect.hxx>
76 #include <comphelper/propertyvalue.hxx>
77 #include <comphelper/servicehelper.hxx>
78 #include <comphelper/profilezone.hxx>
79 #include <comphelper/flagguard.hxx>
80 #include <swmodule.hxx>
82 using namespace ::com::sun::star;
84 // Helper classes
85 SwUnoInternalPaM::SwUnoInternalPaM(SwDoc& rDoc) :
86 SwPaM(rDoc.GetNodes())
90 SwUnoInternalPaM::~SwUnoInternalPaM()
92 while( GetNext() != this)
94 // coverity[deref_arg] - the delete moves a new entry into GetNext()
95 delete GetNext();
99 SwUnoInternalPaM& SwUnoInternalPaM::operator=(const SwPaM& rPaM)
101 const SwPaM* pTmp = &rPaM;
102 *GetPoint() = *rPaM.GetPoint();
103 if(rPaM.HasMark())
105 SetMark();
106 *GetMark() = *rPaM.GetMark();
108 else
109 DeleteMark();
110 while(&rPaM != (pTmp = pTmp->GetNext()))
112 if(pTmp->HasMark())
113 new SwPaM(*pTmp->GetMark(), *pTmp->GetPoint(), this);
114 else
115 new SwPaM(*pTmp->GetPoint(), this);
117 return *this;
120 void SwUnoCursorHelper::SelectPam(SwPaM & rPam, const bool bExpand)
122 if (bExpand)
124 if (!rPam.HasMark())
126 rPam.SetMark();
129 else if (rPam.HasMark())
131 rPam.DeleteMark();
135 void SwUnoCursorHelper::GetTextFromPam(SwPaM & rPam, OUString & rBuffer,
136 SwRootFrame const*const pLayout)
138 if (!rPam.HasMark())
140 return;
142 SvMemoryStream aStream;
143 #ifdef OSL_BIGENDIAN
144 aStream.SetEndian( SvStreamEndian::BIG );
145 #else
146 aStream.SetEndian( SvStreamEndian::LITTLE );
147 #endif
148 WriterRef xWrt;
149 // TODO/MBA: looks like a BaseURL doesn't make sense here
150 SwReaderWriter::GetWriter( FILTER_TEXT_DLG, OUString(), xWrt );
151 if( !xWrt.is() )
152 return;
154 SwWriter aWriter( aStream, rPam );
155 xWrt->m_bASCII_NoLastLineEnd = true;
156 xWrt->m_bExportParagraphNumbering = false;
157 SwAsciiOptions aOpt = xWrt->GetAsciiOptions();
158 aOpt.SetCharSet( RTL_TEXTENCODING_UNICODE );
159 xWrt->SetAsciiOptions( aOpt );
160 xWrt->m_bUCS2_WithStartChar = false;
161 // #i68522#
162 const bool bOldShowProgress = xWrt->m_bShowProgress;
163 xWrt->m_bShowProgress = false;
164 xWrt->m_bHideDeleteRedlines = pLayout && pLayout->IsHideRedlines();
165 // tdf#155951 SwWriter::Write calls EndAllAction, and that
166 // called SelectShell(), triggering selection change event, which
167 // resulted infinite recursion, if selectionChanged() calls
168 // XTextRange::getString() e.g. on the selected range.
169 ::comphelper::FlagRestorationGuard g(g_bNoInterrupt, true);
171 if( ! aWriter.Write( xWrt ).IsError() )
173 const sal_uInt64 lUniLen = aStream.GetSize()/sizeof( sal_Unicode );
174 if (lUniLen < o3tl::make_unsigned(SAL_MAX_INT32-1))
176 aStream.WriteUInt16( '\0' );
178 aStream.Seek( 0 );
179 aStream.ResetError();
181 rtl_uString *pStr = rtl_uString_alloc(lUniLen);
182 aStream.ReadBytes(pStr->buffer, lUniLen * sizeof(sal_Unicode));
183 rBuffer = OUString(pStr, SAL_NO_ACQUIRE);
186 xWrt->m_bShowProgress = bOldShowProgress;
190 /// @throws lang::IllegalArgumentException
191 /// @throws uno::RuntimeException
192 static void
193 lcl_setCharStyle(SwDoc& rDoc, const uno::Any & rValue, SfxItemSet & rSet)
195 SwDocShell *const pDocSh = rDoc.GetDocShell();
196 if(!pDocSh)
197 return;
199 OUString uStyle;
200 if (!(rValue >>= uStyle))
202 throw lang::IllegalArgumentException();
204 OUString sStyle;
205 SwStyleNameMapper::FillUIName(uStyle, sStyle,
206 SwGetPoolIdFromName::ChrFmt);
207 SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
208 pDocSh->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
209 if (!pStyle)
211 throw lang::IllegalArgumentException();
213 const SwFormatCharFormat aFormat(pStyle->GetCharFormat());
214 rSet.Put(aFormat);
217 /// @throws lang::IllegalArgumentException
218 static void
219 lcl_setAutoStyle(IStyleAccess & rStyleAccess, const uno::Any & rValue,
220 SfxItemSet & rSet, const bool bPara)
222 OUString uStyle;
223 if (!(rValue >>= uStyle))
225 throw lang::IllegalArgumentException();
227 std::shared_ptr<SfxItemSet> pStyle = bPara ?
228 rStyleAccess.getByName(uStyle, IStyleAccess::AUTO_STYLE_PARA ):
229 rStyleAccess.getByName(uStyle, IStyleAccess::AUTO_STYLE_CHAR );
230 if(!pStyle)
232 throw lang::IllegalArgumentException();
235 SwFormatAutoFormat aFormat( bPara
236 ? sal::static_int_cast< sal_uInt16 >(RES_AUTO_STYLE)
237 : sal::static_int_cast< sal_uInt16 >(RES_TXTATR_AUTOFMT) );
238 aFormat.SetStyleHandle( pStyle );
239 rSet.Put(aFormat);
242 void
243 SwUnoCursorHelper::SetTextFormatColl(const uno::Any & rAny, SwPaM & rPaM)
245 SwDoc& rDoc = rPaM.GetDoc();
246 SwDocShell *const pDocSh = rDoc.GetDocShell();
247 if(!pDocSh)
248 return;
249 OUString uStyle;
250 rAny >>= uStyle;
251 OUString sStyle;
252 SwStyleNameMapper::FillUIName(uStyle, sStyle,
253 SwGetPoolIdFromName::TxtColl );
254 SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
255 pDocSh->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Para));
256 if (!pStyle)
258 throw lang::IllegalArgumentException();
261 SwTextFormatColl *const pLocal = pStyle->GetCollection();
262 UnoActionContext aAction(&rDoc);
263 rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
264 SwPaM *pTmpCursor = &rPaM;
265 do {
266 rDoc.SetTextFormatColl(*pTmpCursor, pLocal);
267 pTmpCursor = pTmpCursor->GetNext();
268 } while ( pTmpCursor != &rPaM );
269 rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
272 bool
273 SwUnoCursorHelper::SetPageDesc(
274 const uno::Any& rValue, SwDoc & rDoc, SfxItemSet & rSet)
276 OUString uDescName;
277 if (!(rValue >>= uDescName))
279 return false;
281 std::unique_ptr<SwFormatPageDesc> pNewDesc;
282 if(const SwFormatPageDesc* pItem = rSet.GetItemIfSet( RES_PAGEDESC ))
284 pNewDesc.reset(new SwFormatPageDesc(*pItem));
286 if (!pNewDesc)
288 pNewDesc.reset(new SwFormatPageDesc());
290 OUString sDescName;
291 SwStyleNameMapper::FillUIName(uDescName, sDescName,
292 SwGetPoolIdFromName::PageDesc);
293 if (!pNewDesc->GetPageDesc() ||
294 (pNewDesc->GetPageDesc()->GetName() != sDescName))
296 bool bPut = false;
297 if (!sDescName.isEmpty())
299 SwPageDesc *const pPageDesc = SwPageDesc::GetByName(rDoc, sDescName);
300 if (!pPageDesc)
302 throw lang::IllegalArgumentException();
304 pNewDesc->RegisterToPageDesc(*pPageDesc);
305 bPut = true;
307 if(!bPut)
309 rSet.ClearItem(RES_BREAK);
310 rSet.Put(SwFormatPageDesc());
312 else
314 rSet.Put(std::move(pNewDesc));
317 return true;
320 static void
321 lcl_SetNodeNumStart(SwPaM & rCursor, uno::Any const& rValue)
323 sal_Int16 nTmp = 1;
324 rValue >>= nTmp;
325 sal_uInt16 nStt = (nTmp < 0 ? USHRT_MAX : o3tl::narrowing<sal_uInt16>(nTmp));
326 SwDoc& rDoc = rCursor.GetDoc();
327 UnoActionContext aAction(&rDoc);
329 if( rCursor.GetNext() != &rCursor ) // MultiSelection?
331 rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
332 SwPamRanges aRangeArr( rCursor );
333 SwPaM aPam( *rCursor.GetPoint() );
334 for( size_t n = 0; n < aRangeArr.Count(); ++n )
336 rDoc.SetNumRuleStart(*aRangeArr.SetPam( n, aPam ).GetPoint());
337 rDoc.SetNodeNumStart(*aRangeArr.SetPam( n, aPam ).GetPoint(),
338 nStt );
340 rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
342 else
344 rDoc.SetNumRuleStart( *rCursor.GetPoint());
345 rDoc.SetNodeNumStart( *rCursor.GetPoint(), nStt );
349 static bool
350 lcl_setCharFormatSequence(SwPaM & rPam, uno::Any const& rValue)
352 uno::Sequence<OUString> aCharStyles;
353 if (!(rValue >>= aCharStyles))
355 return false;
358 for (sal_Int32 nStyle = 0; nStyle < aCharStyles.getLength(); nStyle++)
360 uno::Any aStyle;
361 rPam.GetDoc().GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
362 aStyle <<= aCharStyles.getConstArray()[nStyle];
363 // create a local set and apply each format directly
364 SfxItemSetFixed<RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT> aSet(rPam.GetDoc().GetAttrPool());
365 lcl_setCharStyle(rPam.GetDoc(), aStyle, aSet);
366 // the first style should replace the current attributes,
367 // all other have to be added
368 SwUnoCursorHelper::SetCursorAttr(rPam, aSet, nStyle
369 ? SetAttrMode::DONTREPLACE
370 : SetAttrMode::DEFAULT);
371 rPam.GetDoc().GetIDocumentUndoRedo().EndUndo(SwUndoId::START, nullptr);
373 return true;
376 static void
377 lcl_setDropcapCharStyle(SwPaM const & rPam, SfxItemSet & rItemSet,
378 uno::Any const& rValue)
380 OUString uStyle;
381 if (!(rValue >>= uStyle))
383 throw lang::IllegalArgumentException();
385 OUString sStyle;
386 SwStyleNameMapper::FillUIName(uStyle, sStyle,
387 SwGetPoolIdFromName::ChrFmt);
388 SwDoc& rDoc = rPam.GetDoc();
389 //default character style must not be set as default format
390 SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
391 rDoc.GetDocShell()
392 ->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
393 if (!pStyle || pStyle->GetCharFormat() == rDoc.GetDfltCharFormat())
395 throw lang::IllegalArgumentException();
397 std::unique_ptr<SwFormatDrop> pDrop;
398 if (const SwFormatDrop* pItem = rItemSet.GetItemIfSet(RES_PARATR_DROP))
400 pDrop.reset(new SwFormatDrop(*pItem));
402 if (!pDrop)
404 pDrop.reset(new SwFormatDrop);
406 const rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*pStyle));
407 pDrop->SetCharFormat(xStyle->GetCharFormat());
408 rItemSet.Put(std::move(pDrop));
411 static void
412 lcl_setRubyCharstyle(SfxItemSet & rItemSet, uno::Any const& rValue)
414 OUString sTmp;
415 if (!(rValue >>= sTmp))
417 throw lang::IllegalArgumentException();
420 std::unique_ptr<SwFormatRuby> pRuby;
421 if (const SwFormatRuby* pItem = rItemSet.GetItemIfSet(RES_TXTATR_CJK_RUBY))
423 pRuby.reset(new SwFormatRuby(*pItem));
425 if (!pRuby)
427 pRuby.reset(new SwFormatRuby(OUString()));
429 OUString sStyle;
430 SwStyleNameMapper::FillUIName(sTmp, sStyle,
431 SwGetPoolIdFromName::ChrFmt);
432 pRuby->SetCharFormatName(sStyle);
433 pRuby->SetCharFormatId(0);
434 if (!sStyle.isEmpty())
436 const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(
437 sStyle, SwGetPoolIdFromName::ChrFmt);
438 pRuby->SetCharFormatId(nId);
440 rItemSet.Put(std::move(pRuby));
443 bool
444 SwUnoCursorHelper::SetCursorPropertyValue(
445 SfxItemPropertyMapEntry const& rEntry, const uno::Any& rValue,
446 SwPaM & rPam, SfxItemSet & rItemSet)
448 if (!(rEntry.nFlags & beans::PropertyAttribute::MAYBEVOID) &&
449 (rValue.getValueType() == cppu::UnoType<void>::get()))
451 return false;
453 bool bRet = true;
454 switch (rEntry.nWID)
456 case RES_TXTATR_CHARFMT:
457 lcl_setCharStyle(rPam.GetDoc(), rValue, rItemSet);
458 break;
459 case RES_TXTATR_AUTOFMT:
460 lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
461 rValue, rItemSet, false);
462 break;
463 case FN_UNO_CHARFMT_SEQUENCE:
464 lcl_setCharFormatSequence(rPam, rValue);
465 break;
466 case FN_UNO_PARA_STYLE :
467 SwUnoCursorHelper::SetTextFormatColl(rValue, rPam);
468 break;
469 case RES_AUTO_STYLE:
470 lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
471 rValue, rItemSet, true);
472 break;
473 case FN_UNO_PAGE_STYLE:
474 //FIXME nothing here?
475 break;
476 case FN_UNO_NUM_START_VALUE:
477 lcl_SetNodeNumStart( rPam, rValue );
478 break;
479 case FN_UNO_NUM_LEVEL:
480 // #i91601#
481 case FN_UNO_LIST_ID:
482 case FN_UNO_IS_NUMBER:
483 case FN_UNO_PARA_NUM_AUTO_FORMAT:
485 // multi selection is not considered
486 SwTextNode *const pTextNd = rPam.GetPointNode().GetTextNode();
487 if (!pTextNd)
489 throw lang::IllegalArgumentException();
491 if (FN_UNO_NUM_LEVEL == rEntry.nWID)
493 sal_Int16 nLevel = 0;
494 if (rValue >>= nLevel)
496 if (nLevel < 0 || MAXLEVEL <= nLevel)
498 throw lang::IllegalArgumentException(
499 "invalid NumberingLevel", nullptr, 0);
501 pTextNd->SetAttrListLevel(nLevel);
504 // #i91601#
505 else if (FN_UNO_LIST_ID == rEntry.nWID)
507 OUString sListId;
508 if (rValue >>= sListId)
510 pTextNd->SetListId( sListId );
513 else if (FN_UNO_IS_NUMBER == rEntry.nWID)
515 bool bIsNumber(false);
516 if ((rValue >>= bIsNumber) && !bIsNumber)
518 pTextNd->SetCountedInList( false );
521 else if (FN_UNO_PARA_NUM_AUTO_FORMAT == rEntry.nWID)
523 std::shared_ptr<SfxItemSet> pAutoStyle;
524 if (uno::Sequence<beans::NamedValue> props; rValue >>= props)
526 // TODO create own map for this, it contains UNO_NAME_DISPLAY_NAME? or make property readable so ODF export can map it to a automatic style?
527 SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE));
528 SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
529 SfxItemSetFixed
530 <RES_CHRATR_BEGIN, RES_CHRATR_END-1,
531 RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT,
532 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1>
533 items( rPam.GetDoc().GetAttrPool() );
535 for (beans::NamedValue const & prop : std::as_const(props))
537 SfxItemPropertyMapEntry const*const pEntry =
538 rMap.getByName(prop.Name);
539 if (!pEntry)
541 if (prop.Name == "CharStyleName")
543 lcl_setCharStyle(rPam.GetDoc(), prop.Value, items);
544 continue;
546 throw beans::UnknownPropertyException(
547 "Unknown property: " + prop.Name);
549 if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
551 throw beans::PropertyVetoException(
552 "Property is read-only: " + prop.Name);
554 rPropSet.setPropertyValue(*pEntry, prop.Value, items);
557 IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
558 // Add it to the autostyle pool, needed by the ODT export.
559 pAutoStyle = rStyleAccess.getAutomaticStyle(items, IStyleAccess::AUTO_STYLE_CHAR);
561 else if (OUString styleName; rValue >>= styleName)
563 IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
564 pAutoStyle = rStyleAccess.getByName(styleName, IStyleAccess::AUTO_STYLE_CHAR);
566 if (pAutoStyle)
568 SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
569 // note: paragraph auto styles have ParaStyleName property for the parent style; character auto styles currently do not because there's a separate hint, but for this it would be a good way to add it in order to export it as style:parent-style-name, see XMLTextParagraphExport::Add()
570 item.SetStyleHandle(pAutoStyle);
571 pTextNd->SetAttr(item);
574 //PROPERTY_MAYBEVOID!
576 break;
577 case FN_NUMBER_NEWSTART:
579 bool bVal = false;
580 if (!(rValue >>= bVal))
582 throw lang::IllegalArgumentException();
584 rPam.GetDoc().SetNumRuleStart(*rPam.GetPoint(), bVal);
586 break;
587 case FN_UNO_NUM_RULES:
588 SwUnoCursorHelper::setNumberingProperty(rValue, rPam);
589 break;
590 case RES_PARATR_DROP:
592 if (MID_DROPCAP_CHAR_STYLE_NAME == rEntry.nMemberId)
594 lcl_setDropcapCharStyle(rPam, rItemSet, rValue);
596 else
598 bRet = false;
601 break;
602 case RES_TXTATR_CJK_RUBY:
604 if (MID_RUBY_CHARSTYLE == rEntry.nMemberId)
606 lcl_setRubyCharstyle(rItemSet, rValue);
608 else
610 bRet = false;
613 break;
614 case RES_PAGEDESC:
616 if (MID_PAGEDESC_PAGEDESCNAME == rEntry.nMemberId)
618 SwUnoCursorHelper::SetPageDesc(
619 rValue, rPam.GetDoc(), rItemSet);
621 else
623 bRet = false;
626 break;
627 default:
628 bRet = false;
630 return bRet;
633 SwFormatColl *
634 SwUnoCursorHelper::GetCurTextFormatColl(SwPaM & rPaM, const bool bConditional)
636 static const sal_uLong nMaxLookup = 1000;
637 SwFormatColl *pFormat = nullptr;
638 bool bError = false;
639 SwPaM *pTmpCursor = &rPaM;
642 const SwNodeOffset nSttNd = pTmpCursor->Start()->GetNodeIndex();
643 const SwNodeOffset nEndNd = pTmpCursor->End()->GetNodeIndex();
645 if( nEndNd - nSttNd >= SwNodeOffset(nMaxLookup) )
647 pFormat = nullptr;
648 break;
651 const SwNodes& rNds = rPaM.GetDoc().GetNodes();
652 for( SwNodeOffset n = nSttNd; n <= nEndNd; ++n )
654 SwTextNode const*const pNd = rNds[ n ]->GetTextNode();
655 if( pNd )
657 SwFormatColl *const pNdFormat = bConditional
658 ? pNd->GetFormatColl() : &pNd->GetAnyFormatColl();
659 if( !pFormat )
661 pFormat = pNdFormat;
663 else if( pFormat != pNdFormat )
665 bError = true;
666 break;
671 pTmpCursor = pTmpCursor->GetNext();
672 } while ( pTmpCursor != &rPaM );
673 return bError ? nullptr : pFormat;
676 SwUnoCursor& SwXTextCursor::GetCursor()
677 { return *m_pUnoCursor; }
679 SwPaM const* SwXTextCursor::GetPaM() const
680 { return m_pUnoCursor.get(); }
682 SwPaM* SwXTextCursor::GetPaM()
683 { return m_pUnoCursor.get(); }
685 SwDoc const* SwXTextCursor::GetDoc() const
686 { return m_pUnoCursor ? &m_pUnoCursor->GetDoc() : nullptr; }
688 SwDoc* SwXTextCursor::GetDoc()
689 { return m_pUnoCursor ? &m_pUnoCursor->GetDoc() : nullptr; }
691 SwXTextCursor::SwXTextCursor(
692 SwDoc & rDoc,
693 uno::Reference< text::XText > xParent,
694 const CursorType eType,
695 const SwPosition& rPos,
696 SwPosition const*const pMark)
697 : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
698 , m_eType(eType)
699 , m_xParentText(std::move(xParent))
700 , m_pUnoCursor(rDoc.CreateUnoCursor(rPos))
702 if (pMark)
704 m_pUnoCursor->SetMark();
705 *m_pUnoCursor->GetMark() = *pMark;
709 SwXTextCursor::SwXTextCursor(uno::Reference< text::XText > xParent,
710 SwPaM const& rSourceCursor, const CursorType eType)
711 : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
712 , m_eType(eType)
713 , m_xParentText(std::move(xParent))
714 , m_pUnoCursor(rSourceCursor.GetDoc().CreateUnoCursor(*rSourceCursor.GetPoint()))
716 if (rSourceCursor.HasMark())
718 m_pUnoCursor->SetMark();
719 *m_pUnoCursor->GetMark() = *rSourceCursor.GetMark();
723 SwXTextCursor::~SwXTextCursor()
725 SolarMutexGuard g; // #i105557#: call dtor with locked solar mutex
726 m_pUnoCursor.reset(nullptr); // need to delete this with SolarMutex held
729 void SwXTextCursor::DeleteAndInsert(std::u16string_view aText,
730 ::sw::DeleteAndInsertMode const eMode)
732 auto pUnoCursor = static_cast<SwCursor*>(m_pUnoCursor.get());
733 if (!pUnoCursor)
734 return;
736 // Start/EndAction
737 SwDoc& rDoc = pUnoCursor->GetDoc();
738 UnoActionContext aAction(&rDoc);
739 const sal_Int32 nTextLen = aText.size();
740 rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
741 auto pCurrent = pUnoCursor;
744 if (pCurrent->HasMark())
746 rDoc.getIDocumentContentOperations().DeleteAndJoin(*pCurrent,
747 // is it "delete" or "replace"?
748 (nTextLen != 0 || eMode & ::sw::DeleteAndInsertMode::ForceReplace) ? SwDeleteFlags::ArtificialSelection : SwDeleteFlags::Default);
750 if(nTextLen)
752 const bool bSuccess(
753 SwUnoCursorHelper::DocInsertStringSplitCR(
754 rDoc, *pCurrent, aText, bool(eMode & ::sw::DeleteAndInsertMode::ForceExpandHints)));
755 OSL_ENSURE( bSuccess, "Doc->Insert(Str) failed." );
757 SwUnoCursorHelper::SelectPam(*pUnoCursor, true);
758 pCurrent->Left(aText.size());
760 pCurrent = pCurrent->GetNext();
761 } while (pCurrent != pUnoCursor);
762 rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
765 namespace {
767 enum ForceIntoMetaMode { META_CHECK_BOTH, META_INIT_START, META_INIT_END };
769 enum ForceIntoContentControlMode
771 CONTENT_CONTROL_CHECK_BOTH,
772 CONTENT_CONTROL_INIT_START,
773 CONTENT_CONTROL_INIT_END
777 static bool
778 lcl_ForceIntoMeta(SwPaM & rCursor,
779 uno::Reference<text::XText> const & xParentText,
780 const enum ForceIntoMetaMode eMode)
782 bool bRet( true ); // means not forced in META_CHECK_BOTH
783 SwXMeta const * const pXMeta( dynamic_cast<SwXMeta*>(xParentText.get()) );
784 OSL_ENSURE(pXMeta, "no parent?");
785 if (!pXMeta)
786 throw uno::RuntimeException();
787 SwTextNode * pTextNode;
788 sal_Int32 nStart;
789 sal_Int32 nEnd;
790 const bool bSuccess( pXMeta->SetContentRange(pTextNode, nStart, nEnd) );
791 OSL_ENSURE(bSuccess, "no pam?");
792 if (!bSuccess)
793 throw uno::RuntimeException();
794 // force the cursor back into the meta if it has moved outside
795 SwPosition start(*pTextNode, nStart);
796 SwPosition end(*pTextNode, nEnd);
797 switch (eMode)
799 case META_INIT_START:
800 *rCursor.GetPoint() = start;
801 break;
802 case META_INIT_END:
803 *rCursor.GetPoint() = end;
804 break;
805 case META_CHECK_BOTH:
806 if (*rCursor.Start() < start)
808 *rCursor.Start() = start;
809 bRet = false;
811 if (*rCursor.End() > end)
813 *rCursor.End() = end;
814 bRet = false;
816 break;
818 return bRet;
821 namespace
823 bool lcl_ForceIntoContentControl(SwPaM& rCursor, const uno::Reference<text::XText>& xParentText,
824 ForceIntoContentControlMode eMode)
826 bool bRet = true; // means not forced in CONTENT_CONTROL_CHECK_BOTH
827 auto pXContentControl = dynamic_cast<SwXContentControl*>(xParentText.get());
828 if (!pXContentControl)
830 SAL_WARN("sw.core", "lcl_ForceIntoContentControl: no parent text");
831 throw uno::RuntimeException();
834 SwTextNode* pTextNode;
835 sal_Int32 nStart;
836 sal_Int32 nEnd;
837 bool bSuccess = pXContentControl->SetContentRange(pTextNode, nStart, nEnd);
838 if (!bSuccess)
840 SAL_WARN("sw.core", "lcl_ForceIntoContentControl: SetContentRange() failed");
841 throw uno::RuntimeException();
844 // Force the cursor back into the content control if it has moved outside.
845 SwPosition aStart(*pTextNode, nStart);
846 SwPosition aEnd(*pTextNode, nEnd);
847 switch (eMode)
849 case CONTENT_CONTROL_INIT_START:
850 *rCursor.GetPoint() = aStart;
851 break;
853 case CONTENT_CONTROL_INIT_END:
854 *rCursor.GetPoint() = aEnd;
855 break;
857 case CONTENT_CONTROL_CHECK_BOTH:
858 if (*rCursor.Start() < aStart)
860 *rCursor.Start() = aStart;
861 bRet = false;
864 if (*rCursor.End() > aEnd)
866 *rCursor.End() = aEnd;
867 bRet = false;
869 break;
872 return bRet;
876 bool SwXTextCursor::IsAtEndOfMeta() const
878 if (CursorType::Meta == m_eType)
880 auto pCursor( m_pUnoCursor );
881 SwXMeta const*const pXMeta(
882 dynamic_cast<SwXMeta*>(m_xParentText.get()) );
883 OSL_ENSURE(pXMeta, "no meta?");
884 if (pCursor && pXMeta)
886 SwTextNode * pTextNode;
887 sal_Int32 nStart;
888 sal_Int32 nEnd;
889 const bool bSuccess(
890 pXMeta->SetContentRange(pTextNode, nStart, nEnd) );
891 OSL_ENSURE(bSuccess, "no pam?");
892 if (bSuccess)
894 const SwPosition end(*pTextNode, nEnd);
895 if ( (*pCursor->GetPoint() == end)
896 || (*pCursor->GetMark() == end))
898 return true;
903 return false;
906 bool SwXTextCursor::IsAtEndOfContentControl() const
908 if (CursorType::ContentControl == m_eType)
910 auto pCursor( m_pUnoCursor );
911 auto pXContentControl(
912 dynamic_cast<SwXContentControl*>(m_xParentText.get()) );
913 if (!pXContentControl)
915 SAL_WARN("sw.core", "SwXTextCursor::IsAtEndOfContentControl: no content control");
917 if (pCursor && pXContentControl)
919 SwTextNode * pTextNode;
920 sal_Int32 nStart;
921 sal_Int32 nEnd;
922 const bool bSuccess(
923 pXContentControl->SetContentRange(pTextNode, nStart, nEnd) );
924 if (!bSuccess)
926 SAL_WARN("sw.core", "SwXTextCursor::IsAtEndOfContentControl: no pam");
928 else
930 const SwPosition end(*pTextNode, nEnd);
931 if ( (*pCursor->GetPoint() == end)
932 || (*pCursor->GetMark() == end))
934 return true;
939 return false;
942 OUString SwXTextCursor::getImplementationName()
944 return "SwXTextCursor";
947 sal_Bool SAL_CALL SwXTextCursor::supportsService(const OUString& rServiceName)
949 return cppu::supportsService(this, rServiceName);
952 uno::Sequence< OUString > SAL_CALL
953 SwXTextCursor::getSupportedServiceNames()
955 return {
956 "com.sun.star.text.TextCursor",
957 "com.sun.star.style.CharacterProperties",
958 "com.sun.star.style.CharacterPropertiesAsian",
959 "com.sun.star.style.CharacterPropertiesComplex",
960 "com.sun.star.style.ParagraphProperties",
961 "com.sun.star.style.ParagraphPropertiesAsian",
962 "com.sun.star.style.ParagraphPropertiesComplex",
963 "com.sun.star.text.TextSortable"
967 void SAL_CALL SwXTextCursor::collapseToStart()
969 SolarMutexGuard aGuard;
971 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
973 if (rUnoCursor.HasMark())
975 if (*rUnoCursor.GetPoint() > *rUnoCursor.GetMark())
977 rUnoCursor.Exchange();
979 rUnoCursor.DeleteMark();
983 void SAL_CALL SwXTextCursor::collapseToEnd()
985 SolarMutexGuard aGuard;
987 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
989 if (rUnoCursor.HasMark())
991 if (*rUnoCursor.GetPoint() < *rUnoCursor.GetMark())
993 rUnoCursor.Exchange();
995 rUnoCursor.DeleteMark();
999 sal_Bool SAL_CALL SwXTextCursor::isCollapsed()
1001 SolarMutexGuard aGuard;
1003 bool bRet = true;
1004 auto pUnoCursor(m_pUnoCursor);
1005 if(pUnoCursor && pUnoCursor->GetMark())
1007 bRet = (*pUnoCursor->GetPoint() == *pUnoCursor->GetMark());
1009 return bRet;
1012 sal_Bool SAL_CALL
1013 SwXTextCursor::goLeft(sal_Int16 nCount, sal_Bool Expand)
1015 SolarMutexGuard aGuard;
1017 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1019 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1020 bool bRet = rUnoCursor.Left( nCount);
1021 if (CursorType::Meta == m_eType)
1023 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1024 META_CHECK_BOTH)
1025 && bRet;
1027 else if (m_eType == CursorType::ContentControl)
1029 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1030 && bRet;
1032 return bRet;
1035 sal_Bool SAL_CALL
1036 SwXTextCursor::goRight(sal_Int16 nCount, sal_Bool Expand)
1038 SolarMutexGuard aGuard;
1040 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1042 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1043 bool bRet = rUnoCursor.Right(nCount);
1044 if (CursorType::Meta == m_eType)
1046 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1047 META_CHECK_BOTH)
1048 && bRet;
1050 else if (m_eType == CursorType::ContentControl)
1052 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1053 && bRet;
1055 return bRet;
1058 void SAL_CALL
1059 SwXTextCursor::gotoStart(sal_Bool Expand)
1061 SolarMutexGuard aGuard;
1062 comphelper::ProfileZone aZone("gotoStart");
1064 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1066 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1067 if (CursorType::Body == m_eType)
1069 rUnoCursor.Move( fnMoveBackward, GoInDoc );
1070 //check, that the cursor is not in a table
1071 SwTableNode * pTableNode = rUnoCursor.GetPointNode().FindTableNode();
1072 while (pTableNode)
1074 rUnoCursor.GetPoint()->Assign( *pTableNode->EndOfSectionNode() );
1075 SwContentNode* pCNode = GetDoc()->GetNodes().GoNext(rUnoCursor.GetPoint());
1076 pTableNode = pCNode ? pCNode->FindTableNode() : nullptr;
1078 SwStartNode const*const pTmp =
1079 rUnoCursor.GetPointNode().StartOfSectionNode();
1080 if (pTmp->IsSectionNode())
1082 SwSectionNode const*const pSectionStartNode =
1083 static_cast<SwSectionNode const*>(pTmp);
1084 if (pSectionStartNode->GetSection().IsHiddenFlag())
1086 GetDoc()->GetNodes().GoNextSection(
1087 rUnoCursor.GetPoint(), true, false);
1091 else if ( (CursorType::Frame == m_eType)
1092 || (CursorType::TableText == m_eType)
1093 || (CursorType::Header == m_eType)
1094 || (CursorType::Footer == m_eType)
1095 || (CursorType::Footnote== m_eType)
1096 || (CursorType::Redline == m_eType))
1098 rUnoCursor.MoveSection(GoCurrSection, fnSectionStart);
1100 else if (CursorType::Meta == m_eType)
1102 lcl_ForceIntoMeta(rUnoCursor, m_xParentText, META_INIT_START);
1104 else if (m_eType == CursorType::ContentControl)
1106 lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_INIT_START);
1110 void SAL_CALL
1111 SwXTextCursor::gotoEnd(sal_Bool Expand)
1113 SolarMutexGuard aGuard;
1114 comphelper::ProfileZone aZone("gotoEnd");
1116 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1118 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1119 if (CursorType::Body == m_eType)
1121 rUnoCursor.Move( fnMoveForward, GoInDoc );
1123 else if ( (CursorType::Frame == m_eType)
1124 || (CursorType::TableText == m_eType)
1125 || (CursorType::Header == m_eType)
1126 || (CursorType::Footer == m_eType)
1127 || (CursorType::Footnote== m_eType)
1128 || (CursorType::Redline == m_eType))
1130 rUnoCursor.MoveSection( GoCurrSection, fnSectionEnd);
1132 else if (CursorType::Meta == m_eType)
1134 lcl_ForceIntoMeta(rUnoCursor, m_xParentText, META_INIT_END);
1136 else if (m_eType == CursorType::ContentControl)
1138 lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_INIT_END);
1142 void SAL_CALL
1143 SwXTextCursor::gotoRange(
1144 const uno::Reference< text::XTextRange > & xRange, sal_Bool bExpand)
1146 SolarMutexGuard aGuard;
1148 if (!xRange.is())
1150 throw uno::RuntimeException();
1153 SwUnoCursor & rOwnCursor( GetCursorOrThrow() );
1155 SwXTextRange* pRange = dynamic_cast<SwXTextRange*>(xRange.get());
1156 OTextCursorHelper* pCursor = dynamic_cast<OTextCursorHelper*>(xRange.get());
1158 if (!pRange && !pCursor)
1160 throw uno::RuntimeException();
1163 SwPaM aPam(GetDoc()->GetNodes());
1164 const SwPaM * pPam(nullptr);
1165 if (pCursor)
1167 pPam = pCursor->GetPaM();
1169 else if (pRange)
1171 if (pRange->GetPositions(aPam))
1173 pPam = & aPam;
1177 if (!pPam)
1179 throw uno::RuntimeException();
1183 SwStartNodeType eSearchNodeType = SwNormalStartNode;
1184 switch (m_eType)
1186 case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break;
1187 case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break;
1188 case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break;
1189 case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break;
1190 case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break;
1191 //case CURSOR_INVALID:
1192 //case CursorType::Body:
1193 default:
1197 const SwStartNode* pOwnStartNode = rOwnCursor.GetPointNode().FindSttNodeByType(eSearchNodeType);
1198 while ( pOwnStartNode != nullptr
1199 && pOwnStartNode->IsSectionNode())
1201 pOwnStartNode = pOwnStartNode->StartOfSectionNode();
1204 const SwStartNode* pTmp =
1205 pPam->GetPointNode().FindSttNodeByType(eSearchNodeType);
1206 while ( pTmp != nullptr
1207 && pTmp->IsSectionNode() )
1209 pTmp = pTmp->StartOfSectionNode();
1212 if ( eSearchNodeType == SwTableBoxStartNode )
1214 if (!pOwnStartNode || !pTmp)
1216 throw uno::RuntimeException();
1219 if ( pOwnStartNode->FindTableNode() != pTmp->FindTableNode() )
1221 throw uno::RuntimeException();
1224 else
1226 if ( pOwnStartNode != pTmp )
1228 throw uno::RuntimeException();
1233 if (CursorType::Meta == m_eType)
1235 SwPaM CopyPam(*pPam->GetMark(), *pPam->GetPoint());
1236 const bool bNotForced( lcl_ForceIntoMeta(
1237 CopyPam, m_xParentText, META_CHECK_BOTH) );
1238 if (!bNotForced)
1240 throw uno::RuntimeException(
1241 "gotoRange: parameter range not contained in nesting"
1242 " text content for which this cursor was created",
1243 static_cast<text::XWordCursor*>(this));
1246 else if (m_eType == CursorType::ContentControl)
1248 SwPaM aPaM(*pPam->GetMark(), *pPam->GetPoint());
1249 if (!lcl_ForceIntoContentControl(aPaM, m_xParentText, CONTENT_CONTROL_CHECK_BOTH))
1251 throw uno::RuntimeException("gotoRange: xRange is out of bounds of the content control",
1252 static_cast<text::XWordCursor*>(this));
1256 // selection has to be expanded here
1257 if(bExpand)
1259 // cursor should include its previous range plus the given range
1260 const SwPosition aOwnLeft(*rOwnCursor.Start());
1261 const SwPosition aOwnRight(*rOwnCursor.End());
1262 SwPosition const& rParamLeft = *pPam->Start();
1263 SwPosition const& rParamRight = *pPam->End();
1265 // now there are four SwPositions,
1266 // two of them are going to be used, but which ones?
1267 if (aOwnRight > rParamRight)
1268 *rOwnCursor.GetPoint() = aOwnRight;
1269 else
1270 *rOwnCursor.GetPoint() = rParamRight;
1271 rOwnCursor.SetMark();
1272 if (aOwnLeft < rParamLeft)
1273 *rOwnCursor.GetMark() = aOwnLeft;
1274 else
1275 *rOwnCursor.GetMark() = rParamLeft;
1277 else
1279 // cursor should be the given range
1280 *rOwnCursor.GetPoint() = *pPam->GetPoint();
1281 if (pPam->HasMark())
1283 rOwnCursor.SetMark();
1284 *rOwnCursor.GetMark() = *pPam->GetMark();
1286 else
1288 rOwnCursor.DeleteMark();
1293 sal_Bool SAL_CALL SwXTextCursor::isStartOfWord()
1295 SolarMutexGuard aGuard;
1297 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1299 const bool bRet =
1300 rUnoCursor.IsStartWordWT( i18n::WordType::DICTIONARY_WORD );
1301 return bRet;
1304 sal_Bool SAL_CALL SwXTextCursor::isEndOfWord()
1306 SolarMutexGuard aGuard;
1308 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1310 const bool bRet =
1311 rUnoCursor.IsEndWordWT( i18n::WordType::DICTIONARY_WORD );
1312 return bRet;
1315 sal_Bool SAL_CALL
1316 SwXTextCursor::gotoNextWord(sal_Bool Expand)
1318 SolarMutexGuard aGuard;
1320 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1322 // problems arise when a paragraph starts with something other than a word
1323 bool bRet = false;
1324 // remember old position to check if cursor has moved
1325 // since the called functions are sometimes a bit unreliable
1326 // in specific cases...
1327 SwPosition *const pPoint = rUnoCursor.GetPoint();
1328 SwNode *const pOldNode = &pPoint->GetNode();
1329 sal_Int32 const nOldIndex = pPoint->GetContentIndex();
1331 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1332 // end of paragraph
1333 if (rUnoCursor.GetPointContentNode() &&
1334 (pPoint->GetContentIndex() == rUnoCursor.GetPointContentNode()->Len()))
1336 rUnoCursor.Right(1);
1338 else
1340 const bool bTmp =
1341 rUnoCursor.GoNextWordWT( i18n::WordType::DICTIONARY_WORD );
1342 // if there is no next word within the current paragraph
1343 // try to go to the start of the next paragraph
1344 if (!bTmp)
1346 rUnoCursor.MovePara(GoNextPara, fnParaStart);
1350 // return true if cursor has moved
1351 bRet = (&pPoint->GetNode() != pOldNode) ||
1352 (pPoint->GetContentIndex() != nOldIndex);
1353 if (bRet && (CursorType::Meta == m_eType))
1355 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1356 META_CHECK_BOTH);
1358 else if (bRet && m_eType == CursorType::ContentControl)
1360 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
1363 return bRet;
1366 sal_Bool SAL_CALL
1367 SwXTextCursor::gotoPreviousWord(sal_Bool Expand)
1369 SolarMutexGuard aGuard;
1371 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1373 // white spaces create problems on the paragraph start
1374 bool bRet = false;
1375 SwPosition *const pPoint = rUnoCursor.GetPoint();
1376 SwNode *const pOldNode = &pPoint->GetNode();
1377 sal_Int32 const nOldIndex = pPoint->GetContentIndex();
1379 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1380 // start of paragraph?
1381 if (pPoint->GetContentIndex() == 0)
1383 rUnoCursor.Left(1);
1385 else
1387 rUnoCursor.GoPrevWordWT( i18n::WordType::DICTIONARY_WORD );
1388 if (pPoint->GetContentIndex() == 0)
1390 rUnoCursor.Left(1);
1394 // return true if cursor has moved
1395 bRet = (&pPoint->GetNode() != pOldNode) ||
1396 (pPoint->GetContentIndex() != nOldIndex);
1397 if (bRet && (CursorType::Meta == m_eType))
1399 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1400 META_CHECK_BOTH);
1402 else if (bRet && m_eType == CursorType::ContentControl)
1404 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
1407 return bRet;
1410 sal_Bool SAL_CALL
1411 SwXTextCursor::gotoEndOfWord(sal_Bool Expand)
1413 SolarMutexGuard aGuard;
1415 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1417 bool bRet = false;
1418 SwPosition *const pPoint = rUnoCursor.GetPoint();
1419 SwNode & rOldNode = pPoint->GetNode();
1420 sal_Int32 const nOldIndex = pPoint->GetContentIndex();
1422 const sal_Int16 nWordType = i18n::WordType::DICTIONARY_WORD;
1423 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1424 if (!rUnoCursor.IsEndWordWT( nWordType ))
1426 rUnoCursor.GoEndWordWT( nWordType );
1429 // restore old cursor if we are not at the end of a word by now
1430 // otherwise use current one
1431 bRet = rUnoCursor.IsEndWordWT( nWordType );
1432 if (!bRet)
1434 pPoint->Assign(rOldNode, nOldIndex);
1436 else if (CursorType::Meta == m_eType)
1438 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1439 META_CHECK_BOTH);
1441 else if (m_eType == CursorType::ContentControl)
1443 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
1446 return bRet;
1449 sal_Bool SAL_CALL
1450 SwXTextCursor::gotoStartOfWord(sal_Bool Expand)
1452 SolarMutexGuard aGuard;
1454 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1456 bool bRet = false;
1457 SwPosition *const pPoint = rUnoCursor.GetPoint();
1458 SwNode & rOldNode = pPoint->GetNode();
1459 sal_Int32 const nOldIndex = pPoint->GetContentIndex();
1461 const sal_Int16 nWordType = i18n::WordType::DICTIONARY_WORD;
1462 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1463 if (!rUnoCursor.IsStartWordWT( nWordType ))
1465 rUnoCursor.GoStartWordWT( nWordType );
1468 // restore old cursor if we are not at the start of a word by now
1469 // otherwise use current one
1470 bRet = rUnoCursor.IsStartWordWT( nWordType );
1471 if (!bRet)
1473 pPoint->Assign(rOldNode, nOldIndex);
1475 else if (CursorType::Meta == m_eType)
1477 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1478 META_CHECK_BOTH);
1480 else if (m_eType == CursorType::ContentControl)
1482 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
1485 return bRet;
1488 sal_Bool SAL_CALL
1489 SwXTextCursor::isStartOfSentence()
1491 SolarMutexGuard aGuard;
1493 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1495 // start of paragraph?
1496 bool bRet = rUnoCursor.GetPoint()->GetContentIndex() == 0;
1497 // with mark ->no sentence start
1498 // (check if cursor is no selection, i.e. it does not have
1499 // a mark or else point and mark are identical)
1500 if (!bRet && (!rUnoCursor.HasMark() ||
1501 *rUnoCursor.GetPoint() == *rUnoCursor.GetMark()))
1503 SwCursor aCursor(*rUnoCursor.GetPoint(),nullptr);
1504 SwPosition aOrigPos = *aCursor.GetPoint();
1505 aCursor.GoSentence(SwCursor::START_SENT );
1506 bRet = aOrigPos == *aCursor.GetPoint();
1508 return bRet;
1511 sal_Bool SAL_CALL
1512 SwXTextCursor::isEndOfSentence()
1514 SolarMutexGuard aGuard;
1516 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1518 // end of paragraph?
1519 bool bRet = rUnoCursor.GetPointContentNode() &&
1520 (rUnoCursor.GetPoint()->GetContentIndex() == rUnoCursor.GetPointContentNode()->Len());
1521 // with mark->no sentence end
1522 // (check if cursor is no selection, i.e. it does not have
1523 // a mark or else point and mark are identical)
1524 if (!bRet && (!rUnoCursor.HasMark() ||
1525 *rUnoCursor.GetPoint() == *rUnoCursor.GetMark()))
1527 SwCursor aCursor(*rUnoCursor.GetPoint(), nullptr);
1528 SwPosition aOrigPos = *aCursor.GetPoint();
1529 aCursor.GoSentence(SwCursor::END_SENT);
1530 bRet = aOrigPos == *aCursor.GetPoint();
1532 return bRet;
1535 sal_Bool SAL_CALL
1536 SwXTextCursor::gotoNextSentence(sal_Bool Expand)
1538 SolarMutexGuard aGuard;
1540 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1542 const bool bWasEOS = isEndOfSentence();
1543 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1544 bool bRet = rUnoCursor.GoSentence(SwCursor::NEXT_SENT);
1545 if (!bRet)
1547 bRet = rUnoCursor.MovePara(GoNextPara, fnParaStart);
1550 // if at the end of the sentence (i.e. at the space after the '.')
1551 // advance to next word in order for GoSentence to work properly
1552 // next time and have isStartOfSentence return true after this call
1553 if (!rUnoCursor.IsStartWordWT(css::i18n::WordType::ANYWORD_IGNOREWHITESPACES))
1555 const bool bNextWord = rUnoCursor.GoNextWordWT(i18n::WordType::ANYWORD_IGNOREWHITESPACES);
1556 if (bWasEOS && !bNextWord)
1558 bRet = false;
1561 if (CursorType::Meta == m_eType)
1563 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1564 META_CHECK_BOTH)
1565 && bRet;
1567 else if (m_eType == CursorType::ContentControl)
1569 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1570 && bRet;
1572 return bRet;
1575 sal_Bool SAL_CALL
1576 SwXTextCursor::gotoPreviousSentence(sal_Bool Expand)
1578 SolarMutexGuard aGuard;
1580 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1582 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1583 bool bRet = rUnoCursor.GoSentence(SwCursor::PREV_SENT);
1584 if (!bRet)
1586 bRet = rUnoCursor.MovePara(GoPrevPara, fnParaStart);
1587 if (bRet)
1589 rUnoCursor.MovePara(GoCurrPara, fnParaEnd);
1590 // at the end of a paragraph move to the sentence end again
1591 rUnoCursor.GoSentence(SwCursor::PREV_SENT);
1594 if (CursorType::Meta == m_eType)
1596 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1597 META_CHECK_BOTH)
1598 && bRet;
1600 else if (m_eType == CursorType::ContentControl)
1602 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1603 && bRet;
1605 return bRet;
1608 sal_Bool SAL_CALL
1609 SwXTextCursor::gotoStartOfSentence(sal_Bool Expand)
1611 SolarMutexGuard aGuard;
1613 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1615 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1616 // if we're at the para start then we won't move
1617 // but bRet is also true if GoSentence failed but
1618 // the start of the sentence is reached
1619 bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor)
1620 || rUnoCursor.GoSentence(SwCursor::START_SENT)
1621 || SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
1622 if (CursorType::Meta == m_eType)
1624 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1625 META_CHECK_BOTH)
1626 && bRet;
1628 else if (m_eType == CursorType::ContentControl)
1630 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1631 && bRet;
1633 return bRet;
1636 sal_Bool SAL_CALL
1637 SwXTextCursor::gotoEndOfSentence(sal_Bool Expand)
1639 SolarMutexGuard aGuard;
1641 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1643 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1644 // bRet is true if GoSentence() succeeded or if the
1645 // MovePara() succeeded while the end of the para is
1646 // not reached already
1647 bool bAlreadyParaEnd = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
1648 bool bRet = !bAlreadyParaEnd
1649 && (rUnoCursor.GoSentence(SwCursor::END_SENT)
1650 || rUnoCursor.MovePara(GoCurrPara, fnParaEnd));
1651 if (CursorType::Meta == m_eType)
1653 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1654 META_CHECK_BOTH)
1655 && bRet;
1657 else if (m_eType == CursorType::ContentControl)
1659 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1660 && bRet;
1662 return bRet;
1665 sal_Bool SAL_CALL
1666 SwXTextCursor::isStartOfParagraph()
1668 SolarMutexGuard aGuard;
1670 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1672 const bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
1673 return bRet;
1676 sal_Bool SAL_CALL
1677 SwXTextCursor::isEndOfParagraph()
1679 SolarMutexGuard aGuard;
1681 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1683 const bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
1684 return bRet;
1687 sal_Bool SAL_CALL
1688 SwXTextCursor::gotoStartOfParagraph(sal_Bool Expand)
1690 SolarMutexGuard aGuard;
1692 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1694 if (CursorType::Meta == m_eType)
1696 return false;
1698 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1699 bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
1700 if (!bRet)
1702 bRet = rUnoCursor.MovePara(GoCurrPara, fnParaStart);
1705 // since MovePara(GoCurrPara, fnParaStart) only returns false
1706 // if we were already at the start of the paragraph this function
1707 // should always complete successfully.
1708 OSL_ENSURE( bRet, "gotoStartOfParagraph failed" );
1709 return bRet;
1712 sal_Bool SAL_CALL
1713 SwXTextCursor::gotoEndOfParagraph(sal_Bool Expand)
1715 SolarMutexGuard aGuard;
1717 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1719 if (CursorType::Meta == m_eType)
1721 return false;
1723 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1724 bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
1725 if (!bRet)
1727 bRet = rUnoCursor.MovePara(GoCurrPara, fnParaEnd);
1730 // since MovePara(GoCurrPara, fnParaEnd) only returns false
1731 // if we were already at the end of the paragraph this function
1732 // should always complete successfully.
1733 OSL_ENSURE( bRet, "gotoEndOfParagraph failed" );
1734 return bRet;
1737 sal_Bool SAL_CALL
1738 SwXTextCursor::gotoNextParagraph(sal_Bool Expand)
1740 SolarMutexGuard aGuard;
1742 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1744 if (CursorType::Meta == m_eType)
1746 return false;
1748 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1749 const bool bRet = rUnoCursor.MovePara(GoNextPara, fnParaStart);
1750 return bRet;
1753 sal_Bool SAL_CALL
1754 SwXTextCursor::gotoPreviousParagraph(sal_Bool Expand)
1756 SolarMutexGuard aGuard;
1758 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1760 if (CursorType::Meta == m_eType)
1762 return false;
1764 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1765 const bool bRet = rUnoCursor.MovePara(GoPrevPara, fnParaStart);
1766 return bRet;
1769 uno::Reference< text::XText > SAL_CALL
1770 SwXTextCursor::getText()
1772 SolarMutexGuard g;
1774 return m_xParentText;
1777 uno::Reference< text::XTextRange > SAL_CALL
1778 SwXTextCursor::getStart()
1780 SolarMutexGuard aGuard;
1782 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1784 uno::Reference< text::XTextRange > xRet;
1785 SwPaM aPam(*rUnoCursor.Start());
1786 const uno::Reference< text::XText > xParent = getText();
1787 if (CursorType::Meta == m_eType)
1789 // return cursor to prevent modifying SwXTextRange for META
1790 rtl::Reference<SwXTextCursor> pXCursor(
1791 new SwXTextCursor(rUnoCursor.GetDoc(), xParent, CursorType::Meta,
1792 *rUnoCursor.GetPoint()) );
1793 pXCursor->gotoStart(false);
1794 xRet = static_cast<text::XWordCursor*>(pXCursor.get());
1796 else
1798 xRet = new SwXTextRange(aPam, xParent);
1800 return xRet;
1803 uno::Reference< text::XTextRange > SAL_CALL
1804 SwXTextCursor::getEnd()
1806 SolarMutexGuard aGuard;
1808 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1810 uno::Reference< text::XTextRange > xRet;
1811 SwPaM aPam(*rUnoCursor.End());
1812 const uno::Reference< text::XText > xParent = getText();
1813 if (CursorType::Meta == m_eType)
1815 // return cursor to prevent modifying SwXTextRange for META
1816 rtl::Reference<SwXTextCursor> pXCursor(
1817 new SwXTextCursor(rUnoCursor.GetDoc(), xParent, CursorType::Meta,
1818 *rUnoCursor.GetPoint()) );
1819 pXCursor->gotoEnd(false);
1820 xRet = static_cast<text::XWordCursor*>(pXCursor.get());
1822 else
1824 xRet = new SwXTextRange(aPam, xParent);
1826 return xRet;
1829 OUString SAL_CALL SwXTextCursor::getString()
1831 SolarMutexGuard aGuard;
1833 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1835 OUString aText;
1836 SwUnoCursorHelper::GetTextFromPam(rUnoCursor, aText);
1837 return aText;
1840 void SAL_CALL
1841 SwXTextCursor::setString(const OUString& aString)
1843 SolarMutexGuard aGuard;
1845 GetCursorOrThrow(); // just to check if valid
1847 const bool bForceExpandHints( (CursorType::Meta == m_eType)
1848 && dynamic_cast<SwXMeta&>(*m_xParentText)
1849 .CheckForOwnMemberMeta(*GetPaM(), true) );
1850 DeleteAndInsert(aString, bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default);
1853 uno::Any SwUnoCursorHelper::GetPropertyValue(
1854 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
1855 std::u16string_view rPropertyName)
1857 uno::Any aAny;
1858 SfxItemPropertyMapEntry const*const pEntry =
1859 rPropSet.getPropertyMap().getByName(rPropertyName);
1861 if (!pEntry)
1863 throw beans::UnknownPropertyException(
1864 OUString::Concat("Unknown property: ") + rPropertyName,
1865 static_cast<cppu::OWeakObject *>(nullptr));
1868 beans::PropertyState eTemp;
1869 const bool bDone = SwUnoCursorHelper::getCursorPropertyValue(
1870 *pEntry, rPaM, &aAny, eTemp );
1872 if (!bDone)
1874 SfxItemSetFixed<
1875 RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
1876 RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>
1877 aSet(rPaM.GetDoc().GetAttrPool());
1879 SwUnoCursorHelper::GetCursorAttr(rPaM, aSet);
1881 rPropSet.getPropertyValue(*pEntry, aSet, aAny);
1884 return aAny;
1887 void SwUnoCursorHelper::SetPropertyValue(
1888 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
1889 const OUString& rPropertyName,
1890 const uno::Any& rValue,
1891 const SetAttrMode nAttrMode)
1893 beans::PropertyValue aVal { comphelper::makePropertyValue(rPropertyName, rValue) };
1894 SetPropertyValues(rPaM, rPropSet, o3tl::span<beans::PropertyValue>(&aVal, 1), nAttrMode);
1897 // FN_UNO_PARA_STYLE is known to set attributes for nodes, inside
1898 // SwUnoCursorHelper::SetTextFormatColl, instead of extending item set.
1899 // We need to get them from nodes in next call to GetCursorAttr.
1900 // The rest could cause similar problems in theory, so we just list them here.
1901 static bool propertyCausesSideEffectsInNodes(sal_uInt16 nWID)
1903 return nWID == FN_UNO_PARA_STYLE ||
1904 nWID == FN_UNO_CHARFMT_SEQUENCE ||
1905 nWID == FN_UNO_NUM_START_VALUE ||
1906 nWID == FN_UNO_NUM_RULES;
1909 void SwUnoCursorHelper::SetPropertyValues(
1910 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
1911 const uno::Sequence< beans::PropertyValue > &rPropertyValues,
1912 const SetAttrMode nAttrMode)
1914 SetPropertyValues(rPaM, rPropSet,
1915 o3tl::span<const beans::PropertyValue>(rPropertyValues.getConstArray(), rPropertyValues.getLength()),
1916 nAttrMode);
1919 void SwUnoCursorHelper::SetPropertyValues(
1920 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
1921 o3tl::span< const beans::PropertyValue > aPropertyValues,
1922 const SetAttrMode nAttrMode)
1924 if (aPropertyValues.empty())
1925 return;
1927 SwDoc& rDoc = rPaM.GetDoc();
1928 OUString aUnknownExMsg, aPropertyVetoExMsg;
1930 // Build set of attributes we want to fetch
1931 WhichRangesContainer aRanges;
1932 std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aSideEffectsEntries;
1933 std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aEntries;
1934 aEntries.reserve(aPropertyValues.size());
1935 for (const auto& rPropVal : aPropertyValues)
1937 const OUString &rPropertyName = rPropVal.Name;
1939 SfxItemPropertyMapEntry const* pEntry =
1940 rPropSet.getPropertyMap().getByName(rPropertyName);
1942 // Queue up any exceptions until the end ...
1943 if (!pEntry)
1945 aUnknownExMsg += "Unknown property: '" + rPropertyName + "' ";
1946 continue;
1948 else if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
1950 aPropertyVetoExMsg += "Property is read-only: '" + rPropertyName + "' ";
1951 continue;
1953 if (propertyCausesSideEffectsInNodes(pEntry->nWID))
1955 aSideEffectsEntries.emplace_back(pEntry, rPropVal.Value);
1957 else
1959 aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID);
1960 aEntries.emplace_back(pEntry, rPropVal.Value);
1964 // Entries with side effects first, using dedicated one-element SfxItemSet for each
1965 for (const auto& [pEntry, rValue] : aSideEffectsEntries)
1967 SfxItemSet aItemSet(rDoc.GetAttrPool(), pEntry->nWID, pEntry->nWID);
1968 // we need to get up-to-date item set from nodes
1969 SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet);
1970 // this can set some attributes in nodes' mpAttrSet
1971 if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet))
1972 rPropSet.setPropertyValue(*pEntry, rValue, aItemSet);
1973 SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false /*bTableMode*/);
1976 if (!aEntries.empty())
1978 // Fetch, overwrite, and re-set the attributes from the core
1979 SfxItemSet aItemSet(rDoc.GetAttrPool(), std::move(aRanges));
1980 // we need to get up-to-date item set from nodes
1981 SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet);
1983 for (const auto& [pEntry, rValue] : aEntries)
1985 // this can set some attributes in nodes' mpAttrSet
1986 if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet))
1987 rPropSet.setPropertyValue(*pEntry, rValue, aItemSet);
1990 SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false /*bTableMode*/);
1993 if (!aUnknownExMsg.isEmpty())
1994 throw beans::UnknownPropertyException(aUnknownExMsg, static_cast<cppu::OWeakObject *>(nullptr));
1995 if (!aPropertyVetoExMsg.isEmpty())
1996 throw beans::PropertyVetoException(aPropertyVetoExMsg, static_cast<cppu::OWeakObject *>(nullptr));
1999 namespace
2001 bool NotInRange(sal_uInt16 nWID, sal_uInt16 nStart, sal_uInt16 nEnd)
2003 return nWID < nStart || nWID > nEnd;
2007 uno::Sequence< beans::PropertyState >
2008 SwUnoCursorHelper::GetPropertyStates(
2009 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
2010 const uno::Sequence< OUString >& rPropertyNames,
2011 const SwGetPropertyStatesCaller eCaller)
2013 const OUString* pNames = rPropertyNames.getConstArray();
2014 uno::Sequence< beans::PropertyState > aRet(rPropertyNames.getLength());
2015 beans::PropertyState* pStates = aRet.getArray();
2016 const SfxItemPropertyMap &rMap = rPropSet.getPropertyMap();
2017 std::optional<SfxItemSet> oSet;
2018 std::optional<SfxItemSet> oSetParent;
2020 for (sal_Int32 i = 0, nEnd = rPropertyNames.getLength(); i < nEnd; i++)
2022 SfxItemPropertyMapEntry const*const pEntry =
2023 rMap.getByName( pNames[i] );
2024 if(!pEntry)
2026 if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
2027 pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
2028 pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
2030 pStates[i] = beans::PropertyState_DEFAULT_VALUE;
2031 continue;
2033 else if (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT ==
2034 eCaller)
2036 //this values marks the element as unknown property
2037 pStates[i] = beans::PropertyState::PropertyState_MAKE_FIXED_SIZE;
2038 continue;
2040 else
2042 throw beans::UnknownPropertyException(
2043 "Unknown property: " + pNames[i],
2044 static_cast<cppu::OWeakObject *>(nullptr));
2047 if (((SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION == eCaller) ||
2048 (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT == eCaller)) &&
2049 NotInRange(pEntry->nWID, FN_UNO_RANGE_BEGIN, FN_UNO_RANGE_END) &&
2050 NotInRange(pEntry->nWID, RES_CHRATR_BEGIN, RES_TXTATR_END) )
2052 pStates[i] = beans::PropertyState_DEFAULT_VALUE;
2054 else
2056 if ( pEntry->nWID >= FN_UNO_RANGE_BEGIN &&
2057 pEntry->nWID <= FN_UNO_RANGE_END )
2059 (void)SwUnoCursorHelper::getCursorPropertyValue(
2060 *pEntry, rPaM, nullptr, pStates[i] );
2062 else
2064 if (!oSet)
2066 switch ( eCaller )
2068 case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT:
2069 case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION:
2070 oSet.emplace( rPaM.GetDoc().GetAttrPool(),
2071 svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END> );
2072 break;
2073 case SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY:
2074 oSet.emplace( rPaM.GetDoc().GetAttrPool(),
2075 pEntry->nWID, pEntry->nWID );
2076 break;
2077 default:
2078 oSet.emplace(
2079 rPaM.GetDoc().GetAttrPool(),
2080 svl::Items<
2081 RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
2082 RES_UNKNOWNATR_CONTAINER,
2083 RES_UNKNOWNATR_CONTAINER>);
2085 // #i63870#
2086 SwUnoCursorHelper::GetCursorAttr( rPaM, *oSet );
2089 pStates[i] = ( oSet->Count() )
2090 ? rPropSet.getPropertyState( *pEntry, *oSet )
2091 : beans::PropertyState_DEFAULT_VALUE;
2093 //try again to find out if a value has been inherited
2094 if( beans::PropertyState_DIRECT_VALUE == pStates[i] )
2096 if (!oSetParent)
2098 oSetParent.emplace(oSet->CloneAsValue( false ));
2099 // #i63870#
2100 SwUnoCursorHelper::GetCursorAttr(
2101 rPaM, *oSetParent, true, false );
2104 pStates[i] = ( oSetParent->Count() )
2105 ? rPropSet.getPropertyState( *pEntry, *oSetParent )
2106 : beans::PropertyState_DEFAULT_VALUE;
2111 return aRet;
2114 beans::PropertyState SwUnoCursorHelper::GetPropertyState(
2115 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
2116 const OUString& rPropertyName)
2118 uno::Sequence< OUString > aStrings { rPropertyName };
2119 uno::Sequence< beans::PropertyState > aSeq =
2120 GetPropertyStates(rPaM, rPropSet, aStrings,
2121 SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY );
2122 return aSeq[0];
2125 static void
2126 lcl_SelectParaAndReset( SwPaM &rPaM, SwDoc & rDoc,
2127 o3tl::sorted_vector<sal_uInt16> const &rWhichIds )
2129 // if we are resetting paragraph attributes, we need to select the full paragraph first
2130 SwPosition aStart = *rPaM.Start();
2131 SwPosition aEnd = *rPaM.End();
2132 auto pTemp ( rDoc.CreateUnoCursor(aStart) );
2133 if(!SwUnoCursorHelper::IsStartOfPara(*pTemp))
2135 pTemp->MovePara(GoCurrPara, fnParaStart);
2137 pTemp->SetMark();
2138 *pTemp->GetPoint() = aEnd;
2139 SwUnoCursorHelper::SelectPam(*pTemp, true);
2140 if(!SwUnoCursorHelper::IsEndOfPara(*pTemp))
2142 pTemp->MovePara(GoCurrPara, fnParaEnd);
2144 rDoc.ResetAttrs(*pTemp, true, rWhichIds);
2147 void SwUnoCursorHelper::SetPropertyToDefault(
2148 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
2149 std::u16string_view rPropertyName)
2151 SwDoc& rDoc = rPaM.GetDoc();
2152 SfxItemPropertyMapEntry const*const pEntry =
2153 rPropSet.getPropertyMap().getByName(rPropertyName);
2154 if (!pEntry)
2156 throw beans::UnknownPropertyException(
2157 OUString::Concat("Unknown property: ") + rPropertyName,
2158 static_cast<cppu::OWeakObject *>(nullptr));
2161 if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
2163 throw uno::RuntimeException(
2164 OUString::Concat("setPropertyToDefault: property is read-only: ")
2165 + rPropertyName, nullptr);
2168 if (pEntry->nWID < RES_FRMATR_END)
2170 const o3tl::sorted_vector<sal_uInt16> aWhichIds{ pEntry->nWID };
2171 if (pEntry->nWID < RES_PARATR_BEGIN)
2173 rDoc.ResetAttrs(rPaM, true, aWhichIds);
2175 else
2177 lcl_SelectParaAndReset ( rPaM, rDoc, aWhichIds );
2180 else
2182 SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, rPaM);
2186 uno::Any SwUnoCursorHelper::GetPropertyDefault(
2187 SwPaM const & rPaM, const SfxItemPropertySet& rPropSet,
2188 std::u16string_view rPropertyName)
2190 SfxItemPropertyMapEntry const*const pEntry =
2191 rPropSet.getPropertyMap().getByName(rPropertyName);
2192 if (!pEntry)
2194 throw beans::UnknownPropertyException(
2195 OUString::Concat("Unknown property: ") + rPropertyName,
2196 static_cast<cppu::OWeakObject *>(nullptr));
2199 uno::Any aRet;
2200 if (pEntry->nWID < RES_FRMATR_END)
2202 SwDoc& rDoc = rPaM.GetDoc();
2203 const SfxPoolItem& rDefItem =
2204 rDoc.GetAttrPool().GetDefaultItem(pEntry->nWID);
2205 rDefItem.QueryValue(aRet, pEntry->nMemberId);
2207 return aRet;
2210 uno::Reference< beans::XPropertySetInfo > SAL_CALL
2211 SwXTextCursor::getPropertySetInfo()
2213 SolarMutexGuard g;
2215 static uno::Reference< beans::XPropertySetInfo > xRef = [&]()
2217 static SfxItemPropertyMapEntry const aCursorExtMap_Impl[] =
2219 { UNO_NAME_IS_SKIP_HIDDEN_TEXT, FN_SKIP_HIDDEN_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
2220 { UNO_NAME_IS_SKIP_PROTECTED_TEXT, FN_SKIP_PROTECTED_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
2221 { UNO_NAME_NO_FORMAT_ATTR, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
2223 const uno::Reference< beans::XPropertySetInfo > xInfo =
2224 m_rPropSet.getPropertySetInfo();
2225 // extend PropertySetInfo!
2226 const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties();
2227 return rtl::Reference<SfxExtItemPropertySetInfo>(new SfxExtItemPropertySetInfo(
2228 aCursorExtMap_Impl,
2229 aPropSeq ));
2230 }();
2231 return xRef;
2234 void SAL_CALL
2235 SwXTextCursor::setPropertyValue(
2236 const OUString& rPropertyName, const uno::Any& rValue)
2238 SolarMutexGuard aGuard;
2240 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2242 if (rPropertyName == UNO_NAME_IS_SKIP_HIDDEN_TEXT)
2244 bool bSet(false);
2245 if (!(rValue >>= bSet))
2247 throw lang::IllegalArgumentException();
2249 rUnoCursor.SetSkipOverHiddenSections(bSet);
2251 else if (rPropertyName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
2253 bool bSet(false);
2254 if (!(rValue >>= bSet))
2256 throw lang::IllegalArgumentException();
2258 rUnoCursor.SetSkipOverProtectSections(bSet);
2260 else if (rPropertyName == UNO_NAME_NO_FORMAT_ATTR)
2262 bool bSet(false);
2263 if (!(rValue >>= bSet))
2265 throw lang::IllegalArgumentException();
2267 if (bSet)
2269 m_nAttrMode = SetAttrMode::NOFORMATATTR;
2271 else
2273 m_nAttrMode = SetAttrMode::DEFAULT;
2276 else if (rPropertyName == "ParaAutoStyleDef")
2278 // Create an autostyle from passed definition (sequence of PropertyValue, same
2279 // as in XAutoStyleFamily::insertStyle), using the currently applied properties
2280 // from the paragraph to not lose their values when creating complex properties
2281 // like SvxULSpaceItem, when only part of the properties stored there is passed;
2282 // and apply it to the paragraph.
2283 uno::Sequence<beans::PropertyValue> def;
2284 if (!(rValue >>= def))
2285 throw lang::IllegalArgumentException();
2287 // See SwUnoCursorHelper::SetPropertyValues
2289 auto pPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARA_AUTO_STYLE);
2291 // Build set of attributes we want to fetch
2292 WhichRangesContainer aRanges;
2293 for (auto& rPropVal : def)
2295 SfxItemPropertyMapEntry const* pEntry =
2296 pPropSet->getPropertyMap().getByName(rPropVal.Name);
2297 if (!pEntry)
2298 continue; // PropValuesToAutoStyleItemSet ignores invalid names
2300 aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID);
2303 if (!aRanges.empty())
2305 SwAttrSet aAutoStyleItemSet(rUnoCursor.GetDoc().GetAttrPool(), std::move(aRanges));
2306 // we need to get up-to-date item set: this makes sure that the complex properties,
2307 // that are only partially defined by passed definition, do not lose the rest of
2308 // their already present data (which will become part of the autostyle, too).
2309 SwUnoCursorHelper::GetCursorAttr(rUnoCursor, aAutoStyleItemSet);
2310 // Set normal set ranges before putting into autostyle, to the same ranges
2311 // that are used for paragraph autostyle in SwXAutoStyleFamily::insertStyle
2312 aAutoStyleItemSet.SetRanges(aTextNodeSetRange);
2314 // Fill the prepared item set, containing current paragraph property values,
2315 // with the passed definition, and create the autostyle.
2316 auto pStyle = PropValuesToAutoStyleItemSet(
2317 rUnoCursor.GetDoc(), IStyleAccess::AUTO_STYLE_PARA, def, aAutoStyleItemSet);
2319 SwFormatAutoFormat aFormat(RES_AUTO_STYLE);
2320 aFormat.SetStyleHandle(pStyle);
2321 SfxItemSet rSet(rUnoCursor.GetDoc().GetAttrPool(), RES_AUTO_STYLE, RES_AUTO_STYLE);
2322 rSet.Put(aFormat);
2323 SwUnoCursorHelper::SetCursorAttr(rUnoCursor, rSet, m_nAttrMode);
2326 else
2328 SwUnoCursorHelper::SetPropertyValue(rUnoCursor,
2329 m_rPropSet, rPropertyName, rValue, m_nAttrMode);
2333 uno::Any SAL_CALL
2334 SwXTextCursor::getPropertyValue(const OUString& rPropertyName)
2336 SolarMutexGuard aGuard;
2338 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2340 uno::Any aAny;
2341 if (rPropertyName == UNO_NAME_IS_SKIP_HIDDEN_TEXT)
2343 const bool bSet = rUnoCursor.IsSkipOverHiddenSections();
2344 aAny <<= bSet;
2346 else if (rPropertyName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
2348 const bool bSet = rUnoCursor.IsSkipOverProtectSections();
2349 aAny <<= bSet;
2351 else
2353 aAny = SwUnoCursorHelper::GetPropertyValue(rUnoCursor,
2354 m_rPropSet, rPropertyName);
2356 return aAny;
2359 void SAL_CALL
2360 SwXTextCursor::addPropertyChangeListener(
2361 const OUString& /*rPropertyName*/,
2362 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
2364 OSL_FAIL("SwXTextCursor::addPropertyChangeListener(): not implemented");
2367 void SAL_CALL
2368 SwXTextCursor::removePropertyChangeListener(
2369 const OUString& /*rPropertyName*/,
2370 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
2372 OSL_FAIL("SwXTextCursor::removePropertyChangeListener(): not implemented");
2375 void SAL_CALL
2376 SwXTextCursor::addVetoableChangeListener(
2377 const OUString& /*rPropertyName*/,
2378 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
2380 OSL_FAIL("SwXTextCursor::addVetoableChangeListener(): not implemented");
2383 void SAL_CALL
2384 SwXTextCursor::removeVetoableChangeListener(
2385 const OUString& /*rPropertyName*/,
2386 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
2388 OSL_FAIL("SwXTextCursor::removeVetoableChangeListener(): not implemented");
2391 beans::PropertyState SAL_CALL
2392 SwXTextCursor::getPropertyState(const OUString& rPropertyName)
2394 SolarMutexGuard aGuard;
2396 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2398 const beans::PropertyState eRet = SwUnoCursorHelper::GetPropertyState(
2399 rUnoCursor, m_rPropSet, rPropertyName);
2400 return eRet;
2403 uno::Sequence< beans::PropertyState > SAL_CALL
2404 SwXTextCursor::getPropertyStates(
2405 const uno::Sequence< OUString >& rPropertyNames)
2407 SolarMutexGuard aGuard;
2409 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2411 return SwUnoCursorHelper::GetPropertyStates(
2412 rUnoCursor, m_rPropSet, rPropertyNames);
2415 void SAL_CALL
2416 SwXTextCursor::setPropertyToDefault(const OUString& rPropertyName)
2418 // forward: need no solar mutex here
2419 uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
2420 setPropertiesToDefault ( aSequence );
2423 uno::Any SAL_CALL
2424 SwXTextCursor::getPropertyDefault(const OUString& rPropertyName)
2426 // forward: need no solar mutex here
2427 const uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
2428 return getPropertyDefaults ( aSequence ).getConstArray()[0];
2431 void SAL_CALL SwXTextCursor::setPropertyValues(
2432 const uno::Sequence< OUString >& aPropertyNames,
2433 const uno::Sequence< uno::Any >& aValues )
2435 if( aValues.getLength() != aPropertyNames.getLength() )
2437 OSL_FAIL( "mis-matched property value sequences" );
2438 throw lang::IllegalArgumentException();
2441 SolarMutexGuard aGuard;
2443 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2445 // a little lame to have to copy into this.
2446 uno::Sequence< beans::PropertyValue > aPropertyValues( aValues.getLength() );
2447 auto aPropertyValuesRange = asNonConstRange(aPropertyValues);
2448 for ( sal_Int32 i = 0; i < aPropertyNames.getLength(); i++ )
2450 if ( aPropertyNames[ i ] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
2451 aPropertyNames[ i ] == UNO_NAME_IS_SKIP_PROTECTED_TEXT )
2453 // the behaviour of these is hard to model in a group
2454 OSL_FAIL("invalid property name for batch setting");
2455 throw lang::IllegalArgumentException();
2457 aPropertyValuesRange[ i ].Name = aPropertyNames[ i ];
2458 aPropertyValuesRange[ i ].Value = aValues[ i ];
2462 SwUnoCursorHelper::SetPropertyValues( rUnoCursor, m_rPropSet, aPropertyValues );
2464 catch (const css::beans::UnknownPropertyException& e)
2466 uno::Any a(cppu::getCaughtException());
2467 throw lang::WrappedTargetException(
2468 "wrapped Exception " + e.Message,
2469 uno::Reference<uno::XInterface>(), a);
2473 uno::Sequence< uno::Any > SAL_CALL
2474 SwXTextCursor::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames )
2476 // a banal implementation for now
2477 uno::Sequence< uno::Any > aValues( aPropertyNames.getLength() );
2478 std::transform(aPropertyNames.begin(), aPropertyNames.end(), aValues.getArray(),
2479 [this](const OUString& rName) -> uno::Any { return getPropertyValue( rName ); });
2480 return aValues;
2483 void SAL_CALL SwXTextCursor::addPropertiesChangeListener(
2484 const uno::Sequence< OUString >& /* aPropertyNames */,
2485 const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
2487 OSL_FAIL("SwXTextCursor::addPropertiesChangeListener(): not implemented");
2489 void SAL_CALL SwXTextCursor::removePropertiesChangeListener(
2490 const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
2492 OSL_FAIL("SwXTextCursor::removePropertiesChangeListener(): not implemented");
2495 void SAL_CALL SwXTextCursor::firePropertiesChangeEvent(
2496 const uno::Sequence< OUString >& /* aPropertyNames */,
2497 const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
2499 OSL_FAIL("SwXTextCursor::firePropertiesChangeEvent(): not implemented");
2502 // para specific attribute ranges
2503 static sal_uInt16 g_ParaResetableSetRange[] = {
2504 RES_FRMATR_BEGIN, RES_FRMATR_END-1,
2505 RES_PARATR_BEGIN, RES_PARATR_END-1,
2506 RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1,
2507 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
2511 // selection specific attribute ranges
2512 static sal_uInt16 g_ResetableSetRange[] = {
2513 RES_CHRATR_BEGIN, RES_CHRATR_END-1,
2514 RES_TXTATR_INETFMT, RES_TXTATR_INETFMT,
2515 RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT,
2516 RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
2517 RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER,
2521 static void
2522 lcl_EnumerateIds(sal_uInt16 const* pIdRange, o3tl::sorted_vector<sal_uInt16> &rWhichIds)
2524 while (*pIdRange)
2526 const sal_uInt16 nStart = *pIdRange++;
2527 const sal_uInt16 nEnd = *pIdRange++;
2528 for (sal_uInt16 nId = nStart + 1; nId <= nEnd; ++nId)
2530 rWhichIds.insert( nId );
2535 void SAL_CALL
2536 SwXTextCursor::setAllPropertiesToDefault()
2538 SolarMutexGuard aGuard;
2540 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2542 o3tl::sorted_vector<sal_uInt16> aParaWhichIds;
2543 o3tl::sorted_vector<sal_uInt16> aWhichIds;
2544 lcl_EnumerateIds(g_ParaResetableSetRange, aParaWhichIds);
2545 lcl_EnumerateIds(g_ResetableSetRange, aWhichIds);
2546 if (!aParaWhichIds.empty())
2548 lcl_SelectParaAndReset(rUnoCursor, rUnoCursor.GetDoc(),
2549 aParaWhichIds);
2551 if (!aWhichIds.empty())
2553 rUnoCursor.GetDoc().ResetAttrs(rUnoCursor, true, aWhichIds);
2557 void SAL_CALL
2558 SwXTextCursor::setPropertiesToDefault(
2559 const uno::Sequence< OUString >& rPropertyNames)
2561 SolarMutexGuard aGuard;
2563 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2565 if ( !rPropertyNames.hasElements() )
2566 return;
2568 SwDoc& rDoc = rUnoCursor.GetDoc();
2569 o3tl::sorted_vector<sal_uInt16> aWhichIds;
2570 o3tl::sorted_vector<sal_uInt16> aParaWhichIds;
2571 for (const OUString& rName : rPropertyNames)
2573 SfxItemPropertyMapEntry const*const pEntry =
2574 m_rPropSet.getPropertyMap().getByName( rName );
2575 if (!pEntry)
2577 if (rName == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
2578 rName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
2580 continue;
2582 throw beans::UnknownPropertyException(
2583 "Unknown property: " + rName,
2584 static_cast<cppu::OWeakObject *>(this));
2586 if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
2588 throw uno::RuntimeException(
2589 "setPropertiesToDefault: property is read-only: " + rName,
2590 static_cast<cppu::OWeakObject *>(this));
2593 if (pEntry->nWID < RES_FRMATR_END)
2595 if (pEntry->nWID < RES_PARATR_BEGIN)
2597 aWhichIds.insert( pEntry->nWID );
2599 else
2601 aParaWhichIds.insert( pEntry->nWID );
2604 else if (pEntry->nWID == FN_UNO_NUM_START_VALUE)
2606 SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, rUnoCursor);
2610 if (!aParaWhichIds.empty())
2612 lcl_SelectParaAndReset(rUnoCursor, rDoc, aParaWhichIds);
2614 if (!aWhichIds.empty())
2616 rDoc.ResetAttrs(rUnoCursor, true, aWhichIds);
2620 uno::Sequence< uno::Any > SAL_CALL
2621 SwXTextCursor::getPropertyDefaults(
2622 const uno::Sequence< OUString >& rPropertyNames)
2624 SolarMutexGuard aGuard;
2626 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2628 const sal_Int32 nCount = rPropertyNames.getLength();
2629 uno::Sequence< uno::Any > aRet(nCount);
2630 if ( nCount )
2632 SwDoc& rDoc = rUnoCursor.GetDoc();
2633 const OUString *pNames = rPropertyNames.getConstArray();
2634 uno::Any *pAny = aRet.getArray();
2635 for (sal_Int32 i = 0; i < nCount; i++)
2637 SfxItemPropertyMapEntry const*const pEntry =
2638 m_rPropSet.getPropertyMap().getByName( pNames[i] );
2639 if (!pEntry)
2641 if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
2642 pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
2643 pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
2645 continue;
2647 throw beans::UnknownPropertyException(
2648 "Unknown property: " + pNames[i],
2649 static_cast<cppu::OWeakObject *>(nullptr));
2651 if (pEntry->nWID < RES_FRMATR_END)
2653 const SfxPoolItem& rDefItem =
2654 rDoc.GetAttrPool().GetDefaultItem(pEntry->nWID);
2655 rDefItem.QueryValue(pAny[i], pEntry->nMemberId);
2659 return aRet;
2662 void SAL_CALL SwXTextCursor::invalidateMarkings(::sal_Int32 nType)
2664 SolarMutexGuard aGuard;
2666 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2668 SwNode& node = rUnoCursor.GetPointNode();
2670 SwTextNode* txtNode = node.GetTextNode();
2672 if (txtNode == nullptr) return;
2674 if ( text::TextMarkupType::SPELLCHECK == nType )
2676 txtNode->SetWrongDirty(sw::WrongState::TODO);
2677 txtNode->ClearWrong();
2679 else if( text::TextMarkupType::PROOFREADING == nType )
2681 txtNode->SetGrammarCheckDirty(true);
2682 txtNode->ClearGrammarCheck();
2684 else if ( text::TextMarkupType::SMARTTAG == nType )
2686 txtNode->SetSmartTagDirty(true);
2687 txtNode->ClearSmartTags();
2689 else return;
2691 SwFormatColl* fmtColl=txtNode->GetFormatColl();
2693 if (fmtColl == nullptr) return;
2695 SwFormatChg aNew( fmtColl );
2696 txtNode->CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew));
2699 void SAL_CALL
2700 SwXTextCursor::makeRedline(
2701 const OUString& rRedlineType,
2702 const uno::Sequence< beans::PropertyValue >& rRedlineProperties)
2704 SolarMutexGuard aGuard;
2706 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2708 SwUnoCursorHelper::makeRedline(rUnoCursor, rRedlineType, rRedlineProperties);
2711 void SAL_CALL SwXTextCursor::insertDocumentFromURL(const OUString& rURL,
2712 const uno::Sequence< beans::PropertyValue >& rOptions)
2714 SolarMutexGuard aGuard;
2716 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2718 SwUnoCursorHelper::InsertFile(&rUnoCursor, rURL, rOptions);
2721 uno::Sequence< beans::PropertyValue >
2722 SwUnoCursorHelper::CreateSortDescriptor(const bool bFromTable)
2724 uno::Sequence< beans::PropertyValue > aRet(5);
2725 beans::PropertyValue* pArray = aRet.getArray();
2727 uno::Any aVal;
2728 aVal <<= bFromTable;
2729 pArray[0] = beans::PropertyValue("IsSortInTable", -1, aVal,
2730 beans::PropertyState_DIRECT_VALUE);
2732 aVal <<= u' ';
2733 pArray[1] = beans::PropertyValue("Delimiter", -1, aVal,
2734 beans::PropertyState_DIRECT_VALUE);
2736 aVal <<= false;
2737 pArray[2] = beans::PropertyValue("IsSortColumns", -1, aVal,
2738 beans::PropertyState_DIRECT_VALUE);
2740 aVal <<= sal_Int32(3);
2741 pArray[3] = beans::PropertyValue("MaxSortFieldsCount", -1, aVal,
2742 beans::PropertyState_DIRECT_VALUE);
2744 lang::Locale aLang( SvtSysLocale().GetLanguageTag().getLocale());
2745 // get collator algorithm to be used for the locale
2746 uno::Sequence< OUString > aSeq(
2747 GetAppCollator().listCollatorAlgorithms( aLang ) );
2748 const bool bHasElements = aSeq.hasElements();
2749 OSL_ENSURE( bHasElements, "list of collator algorithms is empty!");
2750 OUString aCollAlg;
2751 if (bHasElements)
2753 aCollAlg = aSeq.getConstArray()[0];
2756 uno::Sequence< table::TableSortField > aFields
2758 // Field, IsAscending, IsCaseSensitive, FieldType, CollatorLocale, CollatorAlgorithm
2759 { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg },
2760 { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg },
2761 { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg }
2764 aVal <<= aFields;
2765 pArray[4] = beans::PropertyValue("SortFields", -1, aVal,
2766 beans::PropertyState_DIRECT_VALUE);
2768 return aRet;
2771 uno::Sequence< beans::PropertyValue > SAL_CALL
2772 SwXTextCursor::createSortDescriptor()
2774 SolarMutexGuard aGuard;
2776 return SwUnoCursorHelper::CreateSortDescriptor(false);
2779 bool SwUnoCursorHelper::ConvertSortProperties(
2780 const uno::Sequence< beans::PropertyValue >& rDescriptor,
2781 SwSortOptions& rSortOpt)
2783 bool bRet = true;
2785 rSortOpt.bTable = false;
2786 rSortOpt.cDeli = ' ';
2787 rSortOpt.eDirection = SwSortDirection::Columns; //!! UI text may be contrary though !!
2789 SwSortKey aKey1;
2790 aKey1.nColumnId = USHRT_MAX;
2791 aKey1.bIsNumeric = true;
2792 aKey1.eSortOrder = SwSortOrder::Ascending;
2794 SwSortKey aKey2;
2795 aKey2.nColumnId = USHRT_MAX;
2796 aKey2.bIsNumeric = true;
2797 aKey2.eSortOrder = SwSortOrder::Ascending;
2799 SwSortKey aKey3;
2800 aKey3.nColumnId = USHRT_MAX;
2801 aKey3.bIsNumeric = true;
2802 aKey3.eSortOrder = SwSortOrder::Ascending;
2803 SwSortKey* aKeys[3] = {&aKey1, &aKey2, &aKey3};
2805 bool bOldSortdescriptor(false);
2806 bool bNewSortdescriptor(false);
2808 for (const beans::PropertyValue& rProperty : rDescriptor)
2810 uno::Any aValue( rProperty.Value );
2811 const OUString& rPropName = rProperty.Name;
2813 // old and new sortdescriptor
2814 if ( rPropName == "IsSortInTable" )
2816 if (auto b = o3tl::tryAccess<bool>(aValue))
2818 rSortOpt.bTable = *b;
2820 else
2822 bRet = false;
2825 else if ( rPropName == "Delimiter" )
2827 sal_Unicode uChar;
2828 sal_uInt16 nChar;
2829 if (aValue >>= uChar)
2831 rSortOpt.cDeli = uChar;
2833 else if (aValue >>= nChar)
2835 // For compatibility with BASIC, also accept an ANY containing
2836 // an UNSIGNED SHORT:
2837 rSortOpt.cDeli = nChar;
2839 else
2841 bRet = false;
2844 // old sortdescriptor
2845 else if ( rPropName == "SortColumns" )
2847 bOldSortdescriptor = true;
2848 bool bTemp(false);
2849 if (aValue >>= bTemp)
2851 rSortOpt.eDirection = bTemp ? SwSortDirection::Columns : SwSortDirection::Rows;
2853 else
2855 bRet = false;
2858 else if ( rPropName == "IsCaseSensitive" )
2860 bOldSortdescriptor = true;
2861 bool bTemp(false);
2862 if (aValue >>= bTemp)
2864 rSortOpt.bIgnoreCase = !bTemp;
2866 else
2868 bRet = false;
2871 else if ( rPropName == "CollatorLocale" )
2873 bOldSortdescriptor = true;
2874 lang::Locale aLocale;
2875 if (aValue >>= aLocale)
2877 rSortOpt.nLanguage = LanguageTag::convertToLanguageType( aLocale);
2879 else
2881 bRet = false;
2884 else if (rPropName.startsWith("CollatorAlgorithm") &&
2885 rPropName.getLength() == 18 &&
2886 (rPropName[17] >= '0' && rPropName[17] <= '9'))
2888 bOldSortdescriptor = true;
2889 sal_uInt16 nIndex = rPropName[17];
2890 nIndex -= '0';
2891 OUString aText;
2892 if ((aValue >>= aText) && nIndex < 3)
2894 aKeys[nIndex]->sSortType = aText;
2896 else
2898 bRet = false;
2901 else if (rPropName.startsWith("SortRowOrColumnNo") &&
2902 rPropName.getLength() == 18 &&
2903 (rPropName[17] >= '0' && rPropName[17] <= '9'))
2905 bOldSortdescriptor = true;
2906 sal_uInt16 nIndex = rPropName[17];
2907 nIndex -= '0';
2908 sal_Int16 nCol = -1;
2909 if (aValue.getValueType() == ::cppu::UnoType<sal_Int16>::get()
2910 && nIndex < 3)
2912 aValue >>= nCol;
2914 if (nCol >= 0)
2916 aKeys[nIndex]->nColumnId = nCol;
2918 else
2920 bRet = false;
2923 else if (rPropName.startsWith("IsSortNumeric") &&
2924 rPropName.getLength() == 14 &&
2925 (rPropName[13] >= '0' && rPropName[13] <= '9'))
2927 bOldSortdescriptor = true;
2928 sal_uInt16 nIndex = rPropName[13];
2929 nIndex = nIndex - '0';
2930 auto bTemp = o3tl::tryAccess<bool>(aValue);
2931 if (bTemp && nIndex < 3)
2933 aKeys[nIndex]->bIsNumeric = *bTemp;
2935 else
2937 bRet = false;
2940 else if (rPropName.startsWith("IsSortAscending") &&
2941 rPropName.getLength() == 16 &&
2942 (rPropName[15] >= '0' && rPropName[15] <= '9'))
2944 bOldSortdescriptor = true;
2945 sal_uInt16 nIndex = rPropName[15];
2946 nIndex -= '0';
2947 auto bTemp = o3tl::tryAccess<bool>(aValue);
2948 if (bTemp && nIndex < 3)
2950 aKeys[nIndex]->eSortOrder = (*bTemp)
2951 ? SwSortOrder::Ascending : SwSortOrder::Descending;
2953 else
2955 bRet = false;
2958 // new sortdescriptor
2959 else if ( rPropName == "IsSortColumns" )
2961 bNewSortdescriptor = true;
2962 if (auto bTemp = o3tl::tryAccess<bool>(aValue))
2964 rSortOpt.eDirection = *bTemp ? SwSortDirection::Columns : SwSortDirection::Rows;
2966 else
2968 bRet = false;
2971 else if ( rPropName == "SortFields" )
2973 bNewSortdescriptor = true;
2974 uno::Sequence < table::TableSortField > aFields;
2975 if (aValue >>= aFields)
2977 sal_Int32 nCount(aFields.getLength());
2978 if (nCount <= 3)
2980 table::TableSortField* pFields = aFields.getArray();
2981 for (sal_Int32 i = 0; i < nCount; ++i)
2983 rSortOpt.bIgnoreCase = !pFields[i].IsCaseSensitive;
2984 rSortOpt.nLanguage =
2985 LanguageTag::convertToLanguageType( pFields[i].CollatorLocale );
2986 aKeys[i]->sSortType = pFields[i].CollatorAlgorithm;
2987 aKeys[i]->nColumnId =
2988 o3tl::narrowing<sal_uInt16>(pFields[i].Field);
2989 aKeys[i]->bIsNumeric = (pFields[i].FieldType ==
2990 table::TableSortFieldType_NUMERIC);
2991 aKeys[i]->eSortOrder = (pFields[i].IsAscending)
2992 ? SwSortOrder::Ascending : SwSortOrder::Descending;
2995 else
2997 bRet = false;
3000 else
3002 bRet = false;
3007 if (bNewSortdescriptor && bOldSortdescriptor)
3009 OSL_FAIL("someone tried to set the old deprecated and "
3010 "the new sortdescriptor");
3011 bRet = false;
3014 if (aKey1.nColumnId != USHRT_MAX)
3016 rSortOpt.aKeys.push_back(aKey1);
3018 if (aKey2.nColumnId != USHRT_MAX)
3020 rSortOpt.aKeys.push_back(aKey2);
3022 if (aKey3.nColumnId != USHRT_MAX)
3024 rSortOpt.aKeys.push_back(aKey3);
3027 return bRet && !rSortOpt.aKeys.empty();
3030 void SAL_CALL
3031 SwXTextCursor::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor)
3033 SolarMutexGuard aGuard;
3035 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
3037 if (!rUnoCursor.HasMark())
3038 return;
3040 SwSortOptions aSortOpt;
3041 if (!SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt))
3043 throw uno::RuntimeException("Bad sort properties");
3045 UnoActionContext aContext( &rUnoCursor.GetDoc() );
3047 SwPosition & rStart = *rUnoCursor.Start();
3048 SwPosition & rEnd = *rUnoCursor.End();
3050 SwNodeIndex aPrevIdx( rStart.GetNode(), -1 );
3051 const SwNodeOffset nOffset = rEnd.GetNodeIndex() - rStart.GetNodeIndex();
3052 const sal_Int32 nCntStt = rStart.GetContentIndex();
3054 rUnoCursor.GetDoc().SortText(rUnoCursor, aSortOpt);
3056 // update selection
3057 rUnoCursor.DeleteMark();
3058 rUnoCursor.GetPoint()->Assign( aPrevIdx.GetNode(), SwNodeOffset(1) );
3059 SwContentNode *const pCNd = rUnoCursor.GetPointContentNode();
3060 sal_Int32 nLen = pCNd->Len();
3061 if (nLen > nCntStt)
3063 nLen = nCntStt;
3065 rUnoCursor.GetPoint()->SetContent( nLen );
3066 rUnoCursor.SetMark();
3068 rUnoCursor.GetPoint()->Adjust(nOffset);
3069 SwContentNode *const pCNd2 = rUnoCursor.GetPointContentNode();
3070 rUnoCursor.GetPoint()->SetContent( pCNd2->Len() );
3074 uno::Reference< container::XEnumeration > SAL_CALL
3075 SwXTextCursor::createContentEnumeration(const OUString& rServiceName)
3077 SolarMutexGuard g;
3078 if (rServiceName != "com.sun.star.text.TextContent")
3079 throw uno::RuntimeException();
3080 SwUnoCursor& rUnoCursor( GetCursorOrThrow() );
3081 return SwXParaFrameEnumeration::Create(rUnoCursor, PARAFRAME_PORTION_TEXTRANGE);
3084 uno::Reference< container::XEnumeration > SAL_CALL
3085 SwXTextCursor::createEnumeration()
3087 SolarMutexGuard g;
3089 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
3091 SwXText* pParentText = dynamic_cast<SwXText*>(m_xParentText.get());
3092 OSL_ENSURE(pParentText, "parent is not a SwXText");
3093 if (!pParentText)
3095 throw uno::RuntimeException();
3098 auto pNewCursor(rUnoCursor.GetDoc().CreateUnoCursor(*rUnoCursor.GetPoint()) );
3099 if (rUnoCursor.HasMark())
3101 pNewCursor->SetMark();
3102 *pNewCursor->GetMark() = *rUnoCursor.GetMark();
3104 const CursorType eSetType = (CursorType::TableText == m_eType)
3105 ? CursorType::SelectionInTable : CursorType::Selection;
3106 return SwXParagraphEnumeration::Create(pParentText, pNewCursor, eSetType);
3109 uno::Type SAL_CALL
3110 SwXTextCursor::getElementType()
3112 return cppu::UnoType<text::XTextRange>::get();
3115 sal_Bool SAL_CALL SwXTextCursor::hasElements()
3117 return true;
3120 uno::Sequence< OUString > SAL_CALL
3121 SwXTextCursor::getAvailableServiceNames()
3123 uno::Sequence<OUString> aRet { "com.sun.star.text.TextContent" };
3124 return aRet;
3127 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */