summaryrefslogtreecommitdiffstats
path: root/sw/source/core/unocore/unoobj.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/unocore/unoobj.cxx')
-rw-r--r--sw/source/core/unocore/unoobj.cxx3123
1 files changed, 3123 insertions, 0 deletions
diff --git a/sw/source/core/unocore/unoobj.cxx b/sw/source/core/unocore/unoobj.cxx
new file mode 100644
index 0000000000..b2f83fee30
--- /dev/null
+++ b/sw/source/core/unocore/unoobj.cxx
@@ -0,0 +1,3123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/table/TableSortField.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/itemprop.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/endian.h>
+#include <unotools/collatorwrapper.hxx>
+
+#include <autostyle_helper.hxx>
+#include <swtypes.hxx>
+#include <hintids.hxx>
+#include <cmdid.h>
+#include <unomid.h>
+#include <hints.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <istyleaccess.hxx>
+#include <ndtxt.hxx>
+#include <unocrsr.hxx>
+#include <unocrsrhelper.hxx>
+#include <unoport.hxx>
+#include <swundo.hxx>
+#include <rootfrm.hxx>
+#include <paratr.hxx>
+#include <pam.hxx>
+#include <shellio.hxx>
+#include <unotbl.hxx>
+#include <fmtruby.hxx>
+#include <docsh.hxx>
+#include <docstyle.hxx>
+#include <fmtpdsc.hxx>
+#include <pagedesc.hxx>
+#include <edimp.hxx>
+#include <fchrfmt.hxx>
+#include <fmtautofmt.hxx>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <unometa.hxx>
+#include <unocontentcontrol.hxx>
+#include <unotext.hxx>
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <unotools/syslocale.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <sortopt.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <memory>
+#include <unoparaframeenum.hxx>
+#include <unoparagraph.hxx>
+#include <iodetect.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/profilezone.hxx>
+#include <comphelper/flagguard.hxx>
+#include <swmodule.hxx>
+
+using namespace ::com::sun::star;
+
+// Helper classes
+SwUnoInternalPaM::SwUnoInternalPaM(SwDoc& rDoc) :
+ SwPaM(rDoc.GetNodes())
+{
+}
+
+SwUnoInternalPaM::~SwUnoInternalPaM()
+{
+ while( GetNext() != this)
+ {
+ // coverity[deref_arg] - the delete moves a new entry into GetNext()
+ delete GetNext();
+ }
+}
+
+SwUnoInternalPaM& SwUnoInternalPaM::operator=(const SwPaM& rPaM)
+{
+ const SwPaM* pTmp = &rPaM;
+ *GetPoint() = *rPaM.GetPoint();
+ if(rPaM.HasMark())
+ {
+ SetMark();
+ *GetMark() = *rPaM.GetMark();
+ }
+ else
+ DeleteMark();
+ while(&rPaM != (pTmp = pTmp->GetNext()))
+ {
+ if(pTmp->HasMark())
+ new SwPaM(*pTmp->GetMark(), *pTmp->GetPoint(), this);
+ else
+ new SwPaM(*pTmp->GetPoint(), this);
+ }
+ return *this;
+}
+
+void SwUnoCursorHelper::SelectPam(SwPaM & rPam, const bool bExpand)
+{
+ if (bExpand)
+ {
+ if (!rPam.HasMark())
+ {
+ rPam.SetMark();
+ }
+ }
+ else if (rPam.HasMark())
+ {
+ rPam.DeleteMark();
+ }
+}
+
+void SwUnoCursorHelper::GetTextFromPam(SwPaM & rPam, OUString & rBuffer,
+ SwRootFrame const*const pLayout)
+{
+ if (!rPam.HasMark())
+ {
+ return;
+ }
+ SvMemoryStream aStream;
+#ifdef OSL_BIGENDIAN
+ aStream.SetEndian( SvStreamEndian::BIG );
+#else
+ aStream.SetEndian( SvStreamEndian::LITTLE );
+#endif
+ WriterRef xWrt;
+ // TODO/MBA: looks like a BaseURL doesn't make sense here
+ SwReaderWriter::GetWriter( FILTER_TEXT_DLG, OUString(), xWrt );
+ if( !xWrt.is() )
+ return;
+
+ SwWriter aWriter( aStream, rPam );
+ xWrt->m_bASCII_NoLastLineEnd = true;
+ xWrt->m_bExportParagraphNumbering = false;
+ SwAsciiOptions aOpt = xWrt->GetAsciiOptions();
+ aOpt.SetCharSet( RTL_TEXTENCODING_UNICODE );
+ xWrt->SetAsciiOptions( aOpt );
+ xWrt->m_bUCS2_WithStartChar = false;
+ // #i68522#
+ const bool bOldShowProgress = xWrt->m_bShowProgress;
+ xWrt->m_bShowProgress = false;
+ xWrt->m_bHideDeleteRedlines = pLayout && pLayout->IsHideRedlines();
+ // tdf#155951 SwWriter::Write calls EndAllAction, and that
+ // called SelectShell(), triggering selection change event, which
+ // resulted infinite recursion, if selectionChanged() calls
+ // XTextRange::getString() e.g. on the selected range.
+ ::comphelper::FlagRestorationGuard g(g_bNoInterrupt, true);
+
+ if( ! aWriter.Write( xWrt ).IsError() )
+ {
+ const sal_uInt64 lUniLen = aStream.GetSize()/sizeof( sal_Unicode );
+ if (lUniLen < o3tl::make_unsigned(SAL_MAX_INT32-1))
+ {
+ aStream.WriteUInt16( '\0' );
+
+ aStream.Seek( 0 );
+ aStream.ResetError();
+
+ rtl_uString *pStr = rtl_uString_alloc(lUniLen);
+ aStream.ReadBytes(pStr->buffer, lUniLen * sizeof(sal_Unicode));
+ rBuffer = OUString(pStr, SAL_NO_ACQUIRE);
+ }
+ }
+ xWrt->m_bShowProgress = bOldShowProgress;
+
+}
+
+/// @throws lang::IllegalArgumentException
+/// @throws uno::RuntimeException
+static void
+lcl_setCharStyle(SwDoc& rDoc, const uno::Any & rValue, SfxItemSet & rSet)
+{
+ SwDocShell *const pDocSh = rDoc.GetDocShell();
+ if(!pDocSh)
+ return;
+
+ OUString uStyle;
+ if (!(rValue >>= uStyle))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(uStyle, sStyle,
+ SwGetPoolIdFromName::ChrFmt);
+ SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
+ pDocSh->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
+ if (!pStyle)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ const SwFormatCharFormat aFormat(pStyle->GetCharFormat());
+ rSet.Put(aFormat);
+};
+
+/// @throws lang::IllegalArgumentException
+static void
+lcl_setAutoStyle(IStyleAccess & rStyleAccess, const uno::Any & rValue,
+ SfxItemSet & rSet, const bool bPara)
+{
+ OUString uStyle;
+ if (!(rValue >>= uStyle))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ std::shared_ptr<SfxItemSet> pStyle = bPara ?
+ rStyleAccess.getByName(uStyle, IStyleAccess::AUTO_STYLE_PARA ):
+ rStyleAccess.getByName(uStyle, IStyleAccess::AUTO_STYLE_CHAR );
+ if(!pStyle)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwFormatAutoFormat aFormat( bPara
+ ? sal::static_int_cast< sal_uInt16 >(RES_AUTO_STYLE)
+ : sal::static_int_cast< sal_uInt16 >(RES_TXTATR_AUTOFMT) );
+ aFormat.SetStyleHandle( pStyle );
+ rSet.Put(aFormat);
+};
+
+void
+SwUnoCursorHelper::SetTextFormatColl(const uno::Any & rAny, SwPaM & rPaM)
+{
+ SwDoc& rDoc = rPaM.GetDoc();
+ SwDocShell *const pDocSh = rDoc.GetDocShell();
+ if(!pDocSh)
+ return;
+ OUString uStyle;
+ rAny >>= uStyle;
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(uStyle, sStyle,
+ SwGetPoolIdFromName::TxtColl );
+ SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
+ pDocSh->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Para));
+ if (!pStyle)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwTextFormatColl *const pLocal = pStyle->GetCollection();
+ UnoActionContext aAction(&rDoc);
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPaM *pTmpCursor = &rPaM;
+ do {
+ rDoc.SetTextFormatColl(*pTmpCursor, pLocal);
+ pTmpCursor = pTmpCursor->GetNext();
+ } while ( pTmpCursor != &rPaM );
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+}
+
+bool
+SwUnoCursorHelper::SetPageDesc(
+ const uno::Any& rValue, SwDoc & rDoc, SfxItemSet & rSet)
+{
+ OUString uDescName;
+ if (!(rValue >>= uDescName))
+ {
+ return false;
+ }
+ std::unique_ptr<SwFormatPageDesc> pNewDesc;
+ if(const SwFormatPageDesc* pItem = rSet.GetItemIfSet( RES_PAGEDESC ))
+ {
+ pNewDesc.reset(new SwFormatPageDesc(*pItem));
+ }
+ if (!pNewDesc)
+ {
+ pNewDesc.reset(new SwFormatPageDesc());
+ }
+ OUString sDescName;
+ SwStyleNameMapper::FillUIName(uDescName, sDescName,
+ SwGetPoolIdFromName::PageDesc);
+ if (!pNewDesc->GetPageDesc() ||
+ (pNewDesc->GetPageDesc()->GetName() != sDescName))
+ {
+ bool bPut = false;
+ if (!sDescName.isEmpty())
+ {
+ SwPageDesc *const pPageDesc = SwPageDesc::GetByName(rDoc, sDescName);
+ if (!pPageDesc)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ pNewDesc->RegisterToPageDesc(*pPageDesc);
+ bPut = true;
+ }
+ if(!bPut)
+ {
+ rSet.ClearItem(RES_BREAK);
+ rSet.Put(SwFormatPageDesc());
+ }
+ else
+ {
+ rSet.Put(std::move(pNewDesc));
+ }
+ }
+ return true;
+}
+
+static void
+lcl_SetNodeNumStart(SwPaM & rCursor, uno::Any const& rValue)
+{
+ sal_Int16 nTmp = 1;
+ rValue >>= nTmp;
+ sal_uInt16 nStt = (nTmp < 0 ? USHRT_MAX : o3tl::narrowing<sal_uInt16>(nTmp));
+ SwDoc& rDoc = rCursor.GetDoc();
+ UnoActionContext aAction(&rDoc);
+
+ if( rCursor.GetNext() != &rCursor ) // MultiSelection?
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( rCursor );
+ SwPaM aPam( *rCursor.GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ rDoc.SetNumRuleStart(*aRangeArr.SetPam( n, aPam ).GetPoint());
+ rDoc.SetNodeNumStart(*aRangeArr.SetPam( n, aPam ).GetPoint(),
+ nStt );
+ }
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ {
+ rDoc.SetNumRuleStart( *rCursor.GetPoint());
+ rDoc.SetNodeNumStart( *rCursor.GetPoint(), nStt );
+ }
+}
+
+static bool
+lcl_setCharFormatSequence(SwPaM & rPam, uno::Any const& rValue)
+{
+ uno::Sequence<OUString> aCharStyles;
+ if (!(rValue >>= aCharStyles))
+ {
+ return false;
+ }
+
+ for (sal_Int32 nStyle = 0; nStyle < aCharStyles.getLength(); nStyle++)
+ {
+ uno::Any aStyle;
+ rPam.GetDoc().GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
+ aStyle <<= aCharStyles.getConstArray()[nStyle];
+ // create a local set and apply each format directly
+ SfxItemSetFixed<RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT> aSet(rPam.GetDoc().GetAttrPool());
+ lcl_setCharStyle(rPam.GetDoc(), aStyle, aSet);
+ // the first style should replace the current attributes,
+ // all other have to be added
+ SwUnoCursorHelper::SetCursorAttr(rPam, aSet, nStyle
+ ? SetAttrMode::DONTREPLACE
+ : SetAttrMode::DEFAULT);
+ rPam.GetDoc().GetIDocumentUndoRedo().EndUndo(SwUndoId::START, nullptr);
+ }
+ return true;
+}
+
+static void
+lcl_setDropcapCharStyle(SwPaM const & rPam, SfxItemSet & rItemSet,
+ uno::Any const& rValue)
+{
+ OUString uStyle;
+ if (!(rValue >>= uStyle))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(uStyle, sStyle,
+ SwGetPoolIdFromName::ChrFmt);
+ SwDoc& rDoc = rPam.GetDoc();
+ //default character style must not be set as default format
+ SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
+ rDoc.GetDocShell()
+ ->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
+ if (!pStyle || pStyle->GetCharFormat() == rDoc.GetDfltCharFormat())
+ {
+ throw lang::IllegalArgumentException();
+ }
+ std::unique_ptr<SwFormatDrop> pDrop;
+ if (const SwFormatDrop* pItem = rItemSet.GetItemIfSet(RES_PARATR_DROP))
+ {
+ pDrop.reset(new SwFormatDrop(*pItem));
+ }
+ if (!pDrop)
+ {
+ pDrop.reset(new SwFormatDrop);
+ }
+ const rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*pStyle));
+ pDrop->SetCharFormat(xStyle->GetCharFormat());
+ rItemSet.Put(std::move(pDrop));
+}
+
+static void
+lcl_setRubyCharstyle(SfxItemSet & rItemSet, uno::Any const& rValue)
+{
+ OUString sTmp;
+ if (!(rValue >>= sTmp))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ std::unique_ptr<SwFormatRuby> pRuby;
+ if (const SwFormatRuby* pItem = rItemSet.GetItemIfSet(RES_TXTATR_CJK_RUBY))
+ {
+ pRuby.reset(new SwFormatRuby(*pItem));
+ }
+ if (!pRuby)
+ {
+ pRuby.reset(new SwFormatRuby(OUString()));
+ }
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(sTmp, sStyle,
+ SwGetPoolIdFromName::ChrFmt);
+ pRuby->SetCharFormatName(sStyle);
+ pRuby->SetCharFormatId(0);
+ if (!sStyle.isEmpty())
+ {
+ const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(
+ sStyle, SwGetPoolIdFromName::ChrFmt);
+ pRuby->SetCharFormatId(nId);
+ }
+ rItemSet.Put(std::move(pRuby));
+}
+
+bool
+SwUnoCursorHelper::SetCursorPropertyValue(
+ SfxItemPropertyMapEntry const& rEntry, const uno::Any& rValue,
+ SwPaM & rPam, SfxItemSet & rItemSet)
+{
+ if (!(rEntry.nFlags & beans::PropertyAttribute::MAYBEVOID) &&
+ (rValue.getValueType() == cppu::UnoType<void>::get()))
+ {
+ return false;
+ }
+ bool bRet = true;
+ switch (rEntry.nWID)
+ {
+ case RES_TXTATR_CHARFMT:
+ lcl_setCharStyle(rPam.GetDoc(), rValue, rItemSet);
+ break;
+ case RES_TXTATR_AUTOFMT:
+ lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
+ rValue, rItemSet, false);
+ break;
+ case FN_UNO_CHARFMT_SEQUENCE:
+ lcl_setCharFormatSequence(rPam, rValue);
+ break;
+ case FN_UNO_PARA_STYLE :
+ SwUnoCursorHelper::SetTextFormatColl(rValue, rPam);
+ break;
+ case RES_AUTO_STYLE:
+ lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
+ rValue, rItemSet, true);
+ break;
+ case FN_UNO_PAGE_STYLE:
+ //FIXME nothing here?
+ break;
+ case FN_UNO_NUM_START_VALUE:
+ lcl_SetNodeNumStart( rPam, rValue );
+ break;
+ case FN_UNO_NUM_LEVEL:
+ // #i91601#
+ case FN_UNO_LIST_ID:
+ case FN_UNO_IS_NUMBER:
+ case FN_UNO_PARA_NUM_AUTO_FORMAT:
+ {
+ // multi selection is not considered
+ SwTextNode *const pTextNd = rPam.GetPointNode().GetTextNode();
+ if (!pTextNd)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (FN_UNO_NUM_LEVEL == rEntry.nWID)
+ {
+ sal_Int16 nLevel = 0;
+ if (rValue >>= nLevel)
+ {
+ if (nLevel < 0 || MAXLEVEL <= nLevel)
+ {
+ throw lang::IllegalArgumentException(
+ "invalid NumberingLevel", nullptr, 0);
+ }
+ pTextNd->SetAttrListLevel(nLevel);
+ }
+ }
+ // #i91601#
+ else if (FN_UNO_LIST_ID == rEntry.nWID)
+ {
+ OUString sListId;
+ if (rValue >>= sListId)
+ {
+ pTextNd->SetListId( sListId );
+ }
+ }
+ else if (FN_UNO_IS_NUMBER == rEntry.nWID)
+ {
+ bool bIsNumber(false);
+ if ((rValue >>= bIsNumber) && !bIsNumber)
+ {
+ pTextNd->SetCountedInList( false );
+ }
+ }
+ else if (FN_UNO_PARA_NUM_AUTO_FORMAT == rEntry.nWID)
+ {
+ std::shared_ptr<SfxItemSet> pAutoStyle;
+ if (uno::Sequence<beans::NamedValue> props; rValue >>= props)
+ {
+ // 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?
+ SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE));
+ SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
+ SfxItemSetFixed
+ <RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1>
+ items( rPam.GetDoc().GetAttrPool() );
+
+ for (beans::NamedValue const & prop : std::as_const(props))
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ rMap.getByName(prop.Name);
+ if (!pEntry)
+ {
+ if (prop.Name == "CharStyleName")
+ {
+ lcl_setCharStyle(rPam.GetDoc(), prop.Value, items);
+ continue;
+ }
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + prop.Name);
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw beans::PropertyVetoException(
+ "Property is read-only: " + prop.Name);
+ }
+ rPropSet.setPropertyValue(*pEntry, prop.Value, items);
+ }
+
+ IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
+ // Add it to the autostyle pool, needed by the ODT export.
+ pAutoStyle = rStyleAccess.getAutomaticStyle(items, IStyleAccess::AUTO_STYLE_CHAR);
+ }
+ else if (OUString styleName; rValue >>= styleName)
+ {
+ IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
+ pAutoStyle = rStyleAccess.getByName(styleName, IStyleAccess::AUTO_STYLE_CHAR);
+ }
+ if (pAutoStyle)
+ {
+ SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
+ // 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()
+ item.SetStyleHandle(pAutoStyle);
+ pTextNd->SetAttr(item);
+ }
+ }
+ //PROPERTY_MAYBEVOID!
+ }
+ break;
+ case FN_NUMBER_NEWSTART:
+ {
+ bool bVal = false;
+ if (!(rValue >>= bVal))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rPam.GetDoc().SetNumRuleStart(*rPam.GetPoint(), bVal);
+ }
+ break;
+ case FN_UNO_NUM_RULES:
+ SwUnoCursorHelper::setNumberingProperty(rValue, rPam);
+ break;
+ case RES_PARATR_DROP:
+ {
+ if (MID_DROPCAP_CHAR_STYLE_NAME == rEntry.nMemberId)
+ {
+ lcl_setDropcapCharStyle(rPam, rItemSet, rValue);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ break;
+ case RES_TXTATR_CJK_RUBY:
+ {
+ if (MID_RUBY_CHARSTYLE == rEntry.nMemberId)
+ {
+ lcl_setRubyCharstyle(rItemSet, rValue);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ break;
+ case RES_PAGEDESC:
+ {
+ if (MID_PAGEDESC_PAGEDESCNAME == rEntry.nMemberId)
+ {
+ SwUnoCursorHelper::SetPageDesc(
+ rValue, rPam.GetDoc(), rItemSet);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+SwFormatColl *
+SwUnoCursorHelper::GetCurTextFormatColl(SwPaM & rPaM, const bool bConditional)
+{
+ static const sal_uLong nMaxLookup = 1000;
+ SwFormatColl *pFormat = nullptr;
+ bool bError = false;
+ SwPaM *pTmpCursor = &rPaM;
+ do
+ {
+ const SwNodeOffset nSttNd = pTmpCursor->Start()->GetNodeIndex();
+ const SwNodeOffset nEndNd = pTmpCursor->End()->GetNodeIndex();
+
+ if( nEndNd - nSttNd >= SwNodeOffset(nMaxLookup) )
+ {
+ pFormat = nullptr;
+ break;
+ }
+
+ const SwNodes& rNds = rPaM.GetDoc().GetNodes();
+ for( SwNodeOffset n = nSttNd; n <= nEndNd; ++n )
+ {
+ SwTextNode const*const pNd = rNds[ n ]->GetTextNode();
+ if( pNd )
+ {
+ SwFormatColl *const pNdFormat = bConditional
+ ? pNd->GetFormatColl() : &pNd->GetAnyFormatColl();
+ if( !pFormat )
+ {
+ pFormat = pNdFormat;
+ }
+ else if( pFormat != pNdFormat )
+ {
+ bError = true;
+ break;
+ }
+ }
+ }
+
+ pTmpCursor = pTmpCursor->GetNext();
+ } while ( pTmpCursor != &rPaM );
+ return bError ? nullptr : pFormat;
+}
+
+SwUnoCursor& SwXTextCursor::GetCursor()
+ { return *m_pUnoCursor; }
+
+SwPaM const* SwXTextCursor::GetPaM() const
+ { return m_pUnoCursor.get(); }
+
+SwPaM* SwXTextCursor::GetPaM()
+ { return m_pUnoCursor.get(); }
+
+SwDoc const* SwXTextCursor::GetDoc() const
+ { return m_pUnoCursor ? &m_pUnoCursor->GetDoc() : nullptr; }
+
+SwDoc* SwXTextCursor::GetDoc()
+ { return m_pUnoCursor ? &m_pUnoCursor->GetDoc() : nullptr; }
+
+SwXTextCursor::SwXTextCursor(
+ SwDoc & rDoc,
+ uno::Reference< text::XText > xParent,
+ const CursorType eType,
+ const SwPosition& rPos,
+ SwPosition const*const pMark)
+ : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
+ , m_eType(eType)
+ , m_xParentText(std::move(xParent))
+ , m_pUnoCursor(rDoc.CreateUnoCursor(rPos))
+{
+ if (pMark)
+ {
+ m_pUnoCursor->SetMark();
+ *m_pUnoCursor->GetMark() = *pMark;
+ }
+}
+
+SwXTextCursor::SwXTextCursor(uno::Reference< text::XText > xParent,
+ SwPaM const& rSourceCursor, const CursorType eType)
+ : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
+ , m_eType(eType)
+ , m_xParentText(std::move(xParent))
+ , m_pUnoCursor(rSourceCursor.GetDoc().CreateUnoCursor(*rSourceCursor.GetPoint()))
+{
+ if (rSourceCursor.HasMark())
+ {
+ m_pUnoCursor->SetMark();
+ *m_pUnoCursor->GetMark() = *rSourceCursor.GetMark();
+ }
+}
+
+SwXTextCursor::~SwXTextCursor()
+{
+ SolarMutexGuard g; // #i105557#: call dtor with locked solar mutex
+ m_pUnoCursor.reset(nullptr); // need to delete this with SolarMutex held
+}
+
+void SwXTextCursor::DeleteAndInsert(std::u16string_view aText,
+ ::sw::DeleteAndInsertMode const eMode)
+{
+ auto pUnoCursor = static_cast<SwCursor*>(m_pUnoCursor.get());
+ if (!pUnoCursor)
+ return;
+
+ // Start/EndAction
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+ UnoActionContext aAction(&rDoc);
+ const sal_Int32 nTextLen = aText.size();
+ rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+ auto pCurrent = pUnoCursor;
+ do
+ {
+ if (pCurrent->HasMark())
+ {
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(*pCurrent,
+ // is it "delete" or "replace"?
+ (nTextLen != 0 || eMode & ::sw::DeleteAndInsertMode::ForceReplace) ? SwDeleteFlags::ArtificialSelection : SwDeleteFlags::Default);
+ }
+ if(nTextLen)
+ {
+ const bool bSuccess(
+ SwUnoCursorHelper::DocInsertStringSplitCR(
+ rDoc, *pCurrent, aText, bool(eMode & ::sw::DeleteAndInsertMode::ForceExpandHints)));
+ OSL_ENSURE( bSuccess, "Doc->Insert(Str) failed." );
+
+ SwUnoCursorHelper::SelectPam(*pUnoCursor, true);
+ pCurrent->Left(aText.size());
+ }
+ pCurrent = pCurrent->GetNext();
+ } while (pCurrent != pUnoCursor);
+ rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
+}
+
+namespace {
+
+enum ForceIntoMetaMode { META_CHECK_BOTH, META_INIT_START, META_INIT_END };
+
+enum ForceIntoContentControlMode
+{
+ CONTENT_CONTROL_CHECK_BOTH,
+ CONTENT_CONTROL_INIT_START,
+ CONTENT_CONTROL_INIT_END
+};
+}
+
+static bool
+lcl_ForceIntoMeta(SwPaM & rCursor,
+ uno::Reference<text::XText> const & xParentText,
+ const enum ForceIntoMetaMode eMode)
+{
+ bool bRet( true ); // means not forced in META_CHECK_BOTH
+ SwXMeta const * const pXMeta( dynamic_cast<SwXMeta*>(xParentText.get()) );
+ OSL_ENSURE(pXMeta, "no parent?");
+ if (!pXMeta)
+ throw uno::RuntimeException();
+ SwTextNode * pTextNode;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ const bool bSuccess( pXMeta->SetContentRange(pTextNode, nStart, nEnd) );
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (!bSuccess)
+ throw uno::RuntimeException();
+ // force the cursor back into the meta if it has moved outside
+ SwPosition start(*pTextNode, nStart);
+ SwPosition end(*pTextNode, nEnd);
+ switch (eMode)
+ {
+ case META_INIT_START:
+ *rCursor.GetPoint() = start;
+ break;
+ case META_INIT_END:
+ *rCursor.GetPoint() = end;
+ break;
+ case META_CHECK_BOTH:
+ if (*rCursor.Start() < start)
+ {
+ *rCursor.Start() = start;
+ bRet = false;
+ }
+ if (*rCursor.End() > end)
+ {
+ *rCursor.End() = end;
+ bRet = false;
+ }
+ break;
+ }
+ return bRet;
+}
+
+namespace
+{
+bool lcl_ForceIntoContentControl(SwPaM& rCursor, const uno::Reference<text::XText>& xParentText,
+ ForceIntoContentControlMode eMode)
+{
+ bool bRet = true; // means not forced in CONTENT_CONTROL_CHECK_BOTH
+ auto pXContentControl = dynamic_cast<SwXContentControl*>(xParentText.get());
+ if (!pXContentControl)
+ {
+ SAL_WARN("sw.core", "lcl_ForceIntoContentControl: no parent text");
+ throw uno::RuntimeException();
+ }
+
+ SwTextNode* pTextNode;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ bool bSuccess = pXContentControl->SetContentRange(pTextNode, nStart, nEnd);
+ if (!bSuccess)
+ {
+ SAL_WARN("sw.core", "lcl_ForceIntoContentControl: SetContentRange() failed");
+ throw uno::RuntimeException();
+ }
+
+ // Force the cursor back into the content control if it has moved outside.
+ SwPosition aStart(*pTextNode, nStart);
+ SwPosition aEnd(*pTextNode, nEnd);
+ switch (eMode)
+ {
+ case CONTENT_CONTROL_INIT_START:
+ *rCursor.GetPoint() = aStart;
+ break;
+
+ case CONTENT_CONTROL_INIT_END:
+ *rCursor.GetPoint() = aEnd;
+ break;
+
+ case CONTENT_CONTROL_CHECK_BOTH:
+ if (*rCursor.Start() < aStart)
+ {
+ *rCursor.Start() = aStart;
+ bRet = false;
+ }
+
+ if (*rCursor.End() > aEnd)
+ {
+ *rCursor.End() = aEnd;
+ bRet = false;
+ }
+ break;
+ }
+
+ return bRet;
+}
+}
+
+bool SwXTextCursor::IsAtEndOfMeta() const
+{
+ if (CursorType::Meta == m_eType)
+ {
+ auto pCursor( m_pUnoCursor );
+ SwXMeta const*const pXMeta(
+ dynamic_cast<SwXMeta*>(m_xParentText.get()) );
+ OSL_ENSURE(pXMeta, "no meta?");
+ if (pCursor && pXMeta)
+ {
+ SwTextNode * pTextNode;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ const bool bSuccess(
+ pXMeta->SetContentRange(pTextNode, nStart, nEnd) );
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (bSuccess)
+ {
+ const SwPosition end(*pTextNode, nEnd);
+ if ( (*pCursor->GetPoint() == end)
+ || (*pCursor->GetMark() == end))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool SwXTextCursor::IsAtEndOfContentControl() const
+{
+ if (CursorType::ContentControl == m_eType)
+ {
+ auto pCursor( m_pUnoCursor );
+ auto pXContentControl(
+ dynamic_cast<SwXContentControl*>(m_xParentText.get()) );
+ if (!pXContentControl)
+ {
+ SAL_WARN("sw.core", "SwXTextCursor::IsAtEndOfContentControl: no content control");
+ }
+ if (pCursor && pXContentControl)
+ {
+ SwTextNode * pTextNode;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ const bool bSuccess(
+ pXContentControl->SetContentRange(pTextNode, nStart, nEnd) );
+ if (!bSuccess)
+ {
+ SAL_WARN("sw.core", "SwXTextCursor::IsAtEndOfContentControl: no pam");
+ }
+ else
+ {
+ const SwPosition end(*pTextNode, nEnd);
+ if ( (*pCursor->GetPoint() == end)
+ || (*pCursor->GetMark() == end))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+OUString SwXTextCursor::getImplementationName()
+{
+ return "SwXTextCursor";
+}
+
+sal_Bool SAL_CALL SwXTextCursor::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXTextCursor::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextCursor",
+ "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.CharacterPropertiesAsian",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.ParagraphProperties",
+ "com.sun.star.style.ParagraphPropertiesAsian",
+ "com.sun.star.style.ParagraphPropertiesComplex",
+ "com.sun.star.text.TextSortable"
+ };
+}
+
+void SAL_CALL SwXTextCursor::collapseToStart()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (rUnoCursor.HasMark())
+ {
+ if (*rUnoCursor.GetPoint() > *rUnoCursor.GetMark())
+ {
+ rUnoCursor.Exchange();
+ }
+ rUnoCursor.DeleteMark();
+ }
+}
+
+void SAL_CALL SwXTextCursor::collapseToEnd()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (rUnoCursor.HasMark())
+ {
+ if (*rUnoCursor.GetPoint() < *rUnoCursor.GetMark())
+ {
+ rUnoCursor.Exchange();
+ }
+ rUnoCursor.DeleteMark();
+ }
+}
+
+sal_Bool SAL_CALL SwXTextCursor::isCollapsed()
+{
+ SolarMutexGuard aGuard;
+
+ bool bRet = true;
+ auto pUnoCursor(m_pUnoCursor);
+ if(pUnoCursor && pUnoCursor->GetMark())
+ {
+ bRet = (*pUnoCursor->GetPoint() == *pUnoCursor->GetMark());
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::goLeft(sal_Int16 nCount, sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = rUnoCursor.Left( nCount);
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::goRight(sal_Int16 nCount, sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = rUnoCursor.Right(nCount);
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+void SAL_CALL
+SwXTextCursor::gotoStart(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("gotoStart");
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ if (CursorType::Body == m_eType)
+ {
+ rUnoCursor.Move( fnMoveBackward, GoInDoc );
+ //check, that the cursor is not in a table
+ SwTableNode * pTableNode = rUnoCursor.GetPointNode().FindTableNode();
+ while (pTableNode)
+ {
+ rUnoCursor.GetPoint()->Assign( *pTableNode->EndOfSectionNode() );
+ SwContentNode* pCNode = GetDoc()->GetNodes().GoNext(rUnoCursor.GetPoint());
+ pTableNode = pCNode ? pCNode->FindTableNode() : nullptr;
+ }
+ SwStartNode const*const pTmp =
+ rUnoCursor.GetPointNode().StartOfSectionNode();
+ if (pTmp->IsSectionNode())
+ {
+ SwSectionNode const*const pSectionStartNode =
+ static_cast<SwSectionNode const*>(pTmp);
+ if (pSectionStartNode->GetSection().IsHiddenFlag())
+ {
+ GetDoc()->GetNodes().GoNextSection(
+ rUnoCursor.GetPoint(), true, false);
+ }
+ }
+ }
+ else if ( (CursorType::Frame == m_eType)
+ || (CursorType::TableText == m_eType)
+ || (CursorType::Header == m_eType)
+ || (CursorType::Footer == m_eType)
+ || (CursorType::Footnote== m_eType)
+ || (CursorType::Redline == m_eType))
+ {
+ rUnoCursor.MoveSection(GoCurrSection, fnSectionStart);
+ }
+ else if (CursorType::Meta == m_eType)
+ {
+ lcl_ForceIntoMeta(rUnoCursor, m_xParentText, META_INIT_START);
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_INIT_START);
+ }
+}
+
+void SAL_CALL
+SwXTextCursor::gotoEnd(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("gotoEnd");
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ if (CursorType::Body == m_eType)
+ {
+ rUnoCursor.Move( fnMoveForward, GoInDoc );
+ }
+ else if ( (CursorType::Frame == m_eType)
+ || (CursorType::TableText == m_eType)
+ || (CursorType::Header == m_eType)
+ || (CursorType::Footer == m_eType)
+ || (CursorType::Footnote== m_eType)
+ || (CursorType::Redline == m_eType))
+ {
+ rUnoCursor.MoveSection( GoCurrSection, fnSectionEnd);
+ }
+ else if (CursorType::Meta == m_eType)
+ {
+ lcl_ForceIntoMeta(rUnoCursor, m_xParentText, META_INIT_END);
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_INIT_END);
+ }
+}
+
+void SAL_CALL
+SwXTextCursor::gotoRange(
+ const uno::Reference< text::XTextRange > & xRange, sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+
+ if (!xRange.is())
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwUnoCursor & rOwnCursor( GetCursorOrThrow() );
+
+ SwXTextRange* pRange = dynamic_cast<SwXTextRange*>(xRange.get());
+ OTextCursorHelper* pCursor = dynamic_cast<OTextCursorHelper*>(xRange.get());
+
+ if (!pRange && !pCursor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwPaM aPam(GetDoc()->GetNodes());
+ const SwPaM * pPam(nullptr);
+ if (pCursor)
+ {
+ pPam = pCursor->GetPaM();
+ }
+ else if (pRange)
+ {
+ if (pRange->GetPositions(aPam))
+ {
+ pPam = & aPam;
+ }
+ }
+
+ if (!pPam)
+ {
+ throw uno::RuntimeException();
+ }
+
+ {
+ SwStartNodeType eSearchNodeType = SwNormalStartNode;
+ switch (m_eType)
+ {
+ case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break;
+ case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break;
+ case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break;
+ case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break;
+ case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break;
+ //case CURSOR_INVALID:
+ //case CursorType::Body:
+ default:
+ ;
+ }
+
+ const SwStartNode* pOwnStartNode = rOwnCursor.GetPointNode().FindSttNodeByType(eSearchNodeType);
+ while ( pOwnStartNode != nullptr
+ && pOwnStartNode->IsSectionNode())
+ {
+ pOwnStartNode = pOwnStartNode->StartOfSectionNode();
+ }
+
+ const SwStartNode* pTmp =
+ pPam->GetPointNode().FindSttNodeByType(eSearchNodeType);
+ while ( pTmp != nullptr
+ && pTmp->IsSectionNode() )
+ {
+ pTmp = pTmp->StartOfSectionNode();
+ }
+
+ if ( eSearchNodeType == SwTableBoxStartNode )
+ {
+ if (!pOwnStartNode || !pTmp)
+ {
+ throw uno::RuntimeException();
+ }
+
+ if ( pOwnStartNode->FindTableNode() != pTmp->FindTableNode() )
+ {
+ throw uno::RuntimeException();
+ }
+ }
+ else
+ {
+ if ( pOwnStartNode != pTmp )
+ {
+ throw uno::RuntimeException();
+ }
+ }
+ }
+
+ if (CursorType::Meta == m_eType)
+ {
+ SwPaM CopyPam(*pPam->GetMark(), *pPam->GetPoint());
+ const bool bNotForced( lcl_ForceIntoMeta(
+ CopyPam, m_xParentText, META_CHECK_BOTH) );
+ if (!bNotForced)
+ {
+ throw uno::RuntimeException(
+ "gotoRange: parameter range not contained in nesting"
+ " text content for which this cursor was created",
+ static_cast<text::XWordCursor*>(this));
+ }
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ SwPaM aPaM(*pPam->GetMark(), *pPam->GetPoint());
+ if (!lcl_ForceIntoContentControl(aPaM, m_xParentText, CONTENT_CONTROL_CHECK_BOTH))
+ {
+ throw uno::RuntimeException("gotoRange: xRange is out of bounds of the content control",
+ static_cast<text::XWordCursor*>(this));
+ }
+ }
+
+ // selection has to be expanded here
+ if(bExpand)
+ {
+ // cursor should include its previous range plus the given range
+ const SwPosition aOwnLeft(*rOwnCursor.Start());
+ const SwPosition aOwnRight(*rOwnCursor.End());
+ SwPosition const& rParamLeft = *pPam->Start();
+ SwPosition const& rParamRight = *pPam->End();
+
+ // now there are four SwPositions,
+ // two of them are going to be used, but which ones?
+ if (aOwnRight > rParamRight)
+ *rOwnCursor.GetPoint() = aOwnRight;
+ else
+ *rOwnCursor.GetPoint() = rParamRight;
+ rOwnCursor.SetMark();
+ if (aOwnLeft < rParamLeft)
+ *rOwnCursor.GetMark() = aOwnLeft;
+ else
+ *rOwnCursor.GetMark() = rParamLeft;
+ }
+ else
+ {
+ // cursor should be the given range
+ *rOwnCursor.GetPoint() = *pPam->GetPoint();
+ if (pPam->HasMark())
+ {
+ rOwnCursor.SetMark();
+ *rOwnCursor.GetMark() = *pPam->GetMark();
+ }
+ else
+ {
+ rOwnCursor.DeleteMark();
+ }
+ }
+}
+
+sal_Bool SAL_CALL SwXTextCursor::isStartOfWord()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bRet =
+ rUnoCursor.IsStartWordWT( i18n::WordType::DICTIONARY_WORD );
+ return bRet;
+}
+
+sal_Bool SAL_CALL SwXTextCursor::isEndOfWord()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bRet =
+ rUnoCursor.IsEndWordWT( i18n::WordType::DICTIONARY_WORD );
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoNextWord(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // problems arise when a paragraph starts with something other than a word
+ bool bRet = false;
+ // remember old position to check if cursor has moved
+ // since the called functions are sometimes a bit unreliable
+ // in specific cases...
+ SwPosition *const pPoint = rUnoCursor.GetPoint();
+ SwNode *const pOldNode = &pPoint->GetNode();
+ sal_Int32 const nOldIndex = pPoint->GetContentIndex();
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ // end of paragraph
+ if (rUnoCursor.GetPointContentNode() &&
+ (pPoint->GetContentIndex() == rUnoCursor.GetPointContentNode()->Len()))
+ {
+ rUnoCursor.Right(1);
+ }
+ else
+ {
+ const bool bTmp =
+ rUnoCursor.GoNextWordWT( i18n::WordType::DICTIONARY_WORD );
+ // if there is no next word within the current paragraph
+ // try to go to the start of the next paragraph
+ if (!bTmp)
+ {
+ rUnoCursor.MovePara(GoNextPara, fnParaStart);
+ }
+ }
+
+ // return true if cursor has moved
+ bRet = (&pPoint->GetNode() != pOldNode) ||
+ (pPoint->GetContentIndex() != nOldIndex);
+ if (bRet && (CursorType::Meta == m_eType))
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH);
+ }
+ else if (bRet && m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoPreviousWord(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // white spaces create problems on the paragraph start
+ bool bRet = false;
+ SwPosition *const pPoint = rUnoCursor.GetPoint();
+ SwNode *const pOldNode = &pPoint->GetNode();
+ sal_Int32 const nOldIndex = pPoint->GetContentIndex();
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ // start of paragraph?
+ if (pPoint->GetContentIndex() == 0)
+ {
+ rUnoCursor.Left(1);
+ }
+ else
+ {
+ rUnoCursor.GoPrevWordWT( i18n::WordType::DICTIONARY_WORD );
+ if (pPoint->GetContentIndex() == 0)
+ {
+ rUnoCursor.Left(1);
+ }
+ }
+
+ // return true if cursor has moved
+ bRet = (&pPoint->GetNode() != pOldNode) ||
+ (pPoint->GetContentIndex() != nOldIndex);
+ if (bRet && (CursorType::Meta == m_eType))
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH);
+ }
+ else if (bRet && m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoEndOfWord(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ bool bRet = false;
+ SwPosition *const pPoint = rUnoCursor.GetPoint();
+ SwNode & rOldNode = pPoint->GetNode();
+ sal_Int32 const nOldIndex = pPoint->GetContentIndex();
+
+ const sal_Int16 nWordType = i18n::WordType::DICTIONARY_WORD;
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ if (!rUnoCursor.IsEndWordWT( nWordType ))
+ {
+ rUnoCursor.GoEndWordWT( nWordType );
+ }
+
+ // restore old cursor if we are not at the end of a word by now
+ // otherwise use current one
+ bRet = rUnoCursor.IsEndWordWT( nWordType );
+ if (!bRet)
+ {
+ pPoint->Assign(rOldNode, nOldIndex);
+ }
+ else if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH);
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoStartOfWord(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ bool bRet = false;
+ SwPosition *const pPoint = rUnoCursor.GetPoint();
+ SwNode & rOldNode = pPoint->GetNode();
+ sal_Int32 const nOldIndex = pPoint->GetContentIndex();
+
+ const sal_Int16 nWordType = i18n::WordType::DICTIONARY_WORD;
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ if (!rUnoCursor.IsStartWordWT( nWordType ))
+ {
+ rUnoCursor.GoStartWordWT( nWordType );
+ }
+
+ // restore old cursor if we are not at the start of a word by now
+ // otherwise use current one
+ bRet = rUnoCursor.IsStartWordWT( nWordType );
+ if (!bRet)
+ {
+ pPoint->Assign(rOldNode, nOldIndex);
+ }
+ else if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH);
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::isStartOfSentence()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // start of paragraph?
+ bool bRet = rUnoCursor.GetPoint()->GetContentIndex() == 0;
+ // with mark ->no sentence start
+ // (check if cursor is no selection, i.e. it does not have
+ // a mark or else point and mark are identical)
+ if (!bRet && (!rUnoCursor.HasMark() ||
+ *rUnoCursor.GetPoint() == *rUnoCursor.GetMark()))
+ {
+ SwCursor aCursor(*rUnoCursor.GetPoint(),nullptr);
+ SwPosition aOrigPos = *aCursor.GetPoint();
+ aCursor.GoSentence(SwCursor::START_SENT );
+ bRet = aOrigPos == *aCursor.GetPoint();
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::isEndOfSentence()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // end of paragraph?
+ bool bRet = rUnoCursor.GetPointContentNode() &&
+ (rUnoCursor.GetPoint()->GetContentIndex() == rUnoCursor.GetPointContentNode()->Len());
+ // with mark->no sentence end
+ // (check if cursor is no selection, i.e. it does not have
+ // a mark or else point and mark are identical)
+ if (!bRet && (!rUnoCursor.HasMark() ||
+ *rUnoCursor.GetPoint() == *rUnoCursor.GetMark()))
+ {
+ SwCursor aCursor(*rUnoCursor.GetPoint(), nullptr);
+ SwPosition aOrigPos = *aCursor.GetPoint();
+ aCursor.GoSentence(SwCursor::END_SENT);
+ bRet = aOrigPos == *aCursor.GetPoint();
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoNextSentence(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bWasEOS = isEndOfSentence();
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = rUnoCursor.GoSentence(SwCursor::NEXT_SENT);
+ if (!bRet)
+ {
+ bRet = rUnoCursor.MovePara(GoNextPara, fnParaStart);
+ }
+
+ // if at the end of the sentence (i.e. at the space after the '.')
+ // advance to next word in order for GoSentence to work properly
+ // next time and have isStartOfSentence return true after this call
+ if (!rUnoCursor.IsStartWordWT(css::i18n::WordType::ANYWORD_IGNOREWHITESPACES))
+ {
+ const bool bNextWord = rUnoCursor.GoNextWordWT(i18n::WordType::ANYWORD_IGNOREWHITESPACES);
+ if (bWasEOS && !bNextWord)
+ {
+ bRet = false;
+ }
+ }
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoPreviousSentence(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = rUnoCursor.GoSentence(SwCursor::PREV_SENT);
+ if (!bRet)
+ {
+ bRet = rUnoCursor.MovePara(GoPrevPara, fnParaStart);
+ if (bRet)
+ {
+ rUnoCursor.MovePara(GoCurrPara, fnParaEnd);
+ // at the end of a paragraph move to the sentence end again
+ rUnoCursor.GoSentence(SwCursor::PREV_SENT);
+ }
+ }
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoStartOfSentence(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ // if we're at the para start then we won't move
+ // but bRet is also true if GoSentence failed but
+ // the start of the sentence is reached
+ bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor)
+ || rUnoCursor.GoSentence(SwCursor::START_SENT)
+ || SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoEndOfSentence(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ // bRet is true if GoSentence() succeeded or if the
+ // MovePara() succeeded while the end of the para is
+ // not reached already
+ bool bAlreadyParaEnd = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
+ bool bRet = !bAlreadyParaEnd
+ && (rUnoCursor.GoSentence(SwCursor::END_SENT)
+ || rUnoCursor.MovePara(GoCurrPara, fnParaEnd));
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ else if (m_eType == CursorType::ContentControl)
+ {
+ bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::isStartOfParagraph()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::isEndOfParagraph()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoStartOfParagraph(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (CursorType::Meta == m_eType)
+ {
+ return false;
+ }
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
+ if (!bRet)
+ {
+ bRet = rUnoCursor.MovePara(GoCurrPara, fnParaStart);
+ }
+
+ // since MovePara(GoCurrPara, fnParaStart) only returns false
+ // if we were already at the start of the paragraph this function
+ // should always complete successfully.
+ OSL_ENSURE( bRet, "gotoStartOfParagraph failed" );
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoEndOfParagraph(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (CursorType::Meta == m_eType)
+ {
+ return false;
+ }
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
+ if (!bRet)
+ {
+ bRet = rUnoCursor.MovePara(GoCurrPara, fnParaEnd);
+ }
+
+ // since MovePara(GoCurrPara, fnParaEnd) only returns false
+ // if we were already at the end of the paragraph this function
+ // should always complete successfully.
+ OSL_ENSURE( bRet, "gotoEndOfParagraph failed" );
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoNextParagraph(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (CursorType::Meta == m_eType)
+ {
+ return false;
+ }
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ const bool bRet = rUnoCursor.MovePara(GoNextPara, fnParaStart);
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoPreviousParagraph(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (CursorType::Meta == m_eType)
+ {
+ return false;
+ }
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ const bool bRet = rUnoCursor.MovePara(GoPrevPara, fnParaStart);
+ return bRet;
+}
+
+uno::Reference< text::XText > SAL_CALL
+SwXTextCursor::getText()
+{
+ SolarMutexGuard g;
+
+ return m_xParentText;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXTextCursor::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ uno::Reference< text::XTextRange > xRet;
+ SwPaM aPam(*rUnoCursor.Start());
+ const uno::Reference< text::XText > xParent = getText();
+ if (CursorType::Meta == m_eType)
+ {
+ // return cursor to prevent modifying SwXTextRange for META
+ rtl::Reference<SwXTextCursor> pXCursor(
+ new SwXTextCursor(rUnoCursor.GetDoc(), xParent, CursorType::Meta,
+ *rUnoCursor.GetPoint()) );
+ pXCursor->gotoStart(false);
+ xRet = static_cast<text::XWordCursor*>(pXCursor.get());
+ }
+ else
+ {
+ xRet = new SwXTextRange(aPam, xParent);
+ }
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXTextCursor::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ uno::Reference< text::XTextRange > xRet;
+ SwPaM aPam(*rUnoCursor.End());
+ const uno::Reference< text::XText > xParent = getText();
+ if (CursorType::Meta == m_eType)
+ {
+ // return cursor to prevent modifying SwXTextRange for META
+ rtl::Reference<SwXTextCursor> pXCursor(
+ new SwXTextCursor(rUnoCursor.GetDoc(), xParent, CursorType::Meta,
+ *rUnoCursor.GetPoint()) );
+ pXCursor->gotoEnd(false);
+ xRet = static_cast<text::XWordCursor*>(pXCursor.get());
+ }
+ else
+ {
+ xRet = new SwXTextRange(aPam, xParent);
+ }
+ return xRet;
+}
+
+OUString SAL_CALL SwXTextCursor::getString()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ OUString aText;
+ SwUnoCursorHelper::GetTextFromPam(rUnoCursor, aText);
+ return aText;
+}
+
+void SAL_CALL
+SwXTextCursor::setString(const OUString& aString)
+{
+ SolarMutexGuard aGuard;
+
+ GetCursorOrThrow(); // just to check if valid
+
+ const bool bForceExpandHints( (CursorType::Meta == m_eType)
+ && dynamic_cast<SwXMeta&>(*m_xParentText)
+ .CheckForOwnMemberMeta(*GetPaM(), true) );
+ DeleteAndInsert(aString, bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default);
+}
+
+uno::Any SwUnoCursorHelper::GetPropertyValue(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ std::u16string_view rPropertyName)
+{
+ uno::Any aAny;
+ SfxItemPropertyMapEntry const*const pEntry =
+ rPropSet.getPropertyMap().getByName(rPropertyName);
+
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ OUString::Concat("Unknown property: ") + rPropertyName);
+ }
+
+ beans::PropertyState eTemp;
+ const bool bDone = SwUnoCursorHelper::getCursorPropertyValue(
+ *pEntry, rPaM, &aAny, eTemp );
+
+ if (!bDone)
+ {
+ SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>
+ aSet(rPaM.GetDoc().GetAttrPool());
+
+ SwUnoCursorHelper::GetCursorAttr(rPaM, aSet);
+
+ rPropSet.getPropertyValue(*pEntry, aSet, aAny);
+ }
+
+ return aAny;
+}
+
+void SwUnoCursorHelper::SetPropertyValue(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ const OUString& rPropertyName,
+ const uno::Any& rValue,
+ const SetAttrMode nAttrMode)
+{
+ beans::PropertyValue aVal { comphelper::makePropertyValue(rPropertyName, rValue) };
+ SetPropertyValues(rPaM, rPropSet, std::span<beans::PropertyValue>(&aVal, 1), nAttrMode);
+}
+
+// FN_UNO_PARA_STYLE is known to set attributes for nodes, inside
+// SwUnoCursorHelper::SetTextFormatColl, instead of extending item set.
+// We need to get them from nodes in next call to GetCursorAttr.
+// The rest could cause similar problems in theory, so we just list them here.
+static bool propertyCausesSideEffectsInNodes(sal_uInt16 nWID)
+{
+ return nWID == FN_UNO_PARA_STYLE ||
+ nWID == FN_UNO_CHARFMT_SEQUENCE ||
+ nWID == FN_UNO_NUM_START_VALUE ||
+ nWID == FN_UNO_NUM_RULES;
+}
+
+void SwUnoCursorHelper::SetPropertyValues(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ const uno::Sequence< beans::PropertyValue > &rPropertyValues,
+ const SetAttrMode nAttrMode)
+{
+ SetPropertyValues(rPaM, rPropSet,
+ std::span<const beans::PropertyValue>(rPropertyValues.getConstArray(), rPropertyValues.getLength()),
+ nAttrMode);
+}
+
+void SwUnoCursorHelper::SetPropertyValues(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ std::span< const beans::PropertyValue > aPropertyValues,
+ const SetAttrMode nAttrMode)
+{
+ if (aPropertyValues.empty())
+ return;
+
+ SwDoc& rDoc = rPaM.GetDoc();
+ OUString aUnknownExMsg, aPropertyVetoExMsg;
+
+ // Build set of attributes we want to fetch
+ WhichRangesContainer aRanges;
+ std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aSideEffectsEntries;
+ std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aEntries;
+ aEntries.reserve(aPropertyValues.size());
+ for (const auto& rPropVal : aPropertyValues)
+ {
+ const OUString &rPropertyName = rPropVal.Name;
+
+ SfxItemPropertyMapEntry const* pEntry =
+ rPropSet.getPropertyMap().getByName(rPropertyName);
+
+ // Queue up any exceptions until the end ...
+ if (!pEntry)
+ {
+ aUnknownExMsg += "Unknown property: '" + rPropertyName + "' ";
+ continue;
+ }
+ else if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ aPropertyVetoExMsg += "Property is read-only: '" + rPropertyName + "' ";
+ continue;
+ }
+ if (propertyCausesSideEffectsInNodes(pEntry->nWID))
+ {
+ aSideEffectsEntries.emplace_back(pEntry, rPropVal.Value);
+ }
+ else
+ {
+ aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID);
+ aEntries.emplace_back(pEntry, rPropVal.Value);
+ }
+ }
+
+ // Entries with side effects first, using dedicated one-element SfxItemSet for each
+ for (const auto& [pEntry, rValue] : aSideEffectsEntries)
+ {
+ SfxItemSet aItemSet(rDoc.GetAttrPool(), pEntry->nWID, pEntry->nWID);
+ // we need to get up-to-date item set from nodes
+ SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet);
+ // this can set some attributes in nodes' mpAttrSet
+ if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet))
+ rPropSet.setPropertyValue(*pEntry, rValue, aItemSet);
+ SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false /*bTableMode*/);
+ }
+
+ if (!aEntries.empty())
+ {
+ // Fetch, overwrite, and re-set the attributes from the core
+ SfxItemSet aItemSet(rDoc.GetAttrPool(), std::move(aRanges));
+ // we need to get up-to-date item set from nodes
+ SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet);
+
+ for (const auto& [pEntry, rValue] : aEntries)
+ {
+ // this can set some attributes in nodes' mpAttrSet
+ if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet))
+ rPropSet.setPropertyValue(*pEntry, rValue, aItemSet);
+ }
+
+ SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false /*bTableMode*/);
+ }
+
+ if (!aUnknownExMsg.isEmpty())
+ throw beans::UnknownPropertyException(aUnknownExMsg);
+ if (!aPropertyVetoExMsg.isEmpty())
+ throw beans::PropertyVetoException(aPropertyVetoExMsg);
+}
+
+namespace
+{
+ bool NotInRange(sal_uInt16 nWID, sal_uInt16 nStart, sal_uInt16 nEnd)
+ {
+ return nWID < nStart || nWID > nEnd;
+ }
+}
+
+uno::Sequence< beans::PropertyState >
+SwUnoCursorHelper::GetPropertyStates(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ const uno::Sequence< OUString >& rPropertyNames,
+ const SwGetPropertyStatesCaller eCaller)
+{
+ const OUString* pNames = rPropertyNames.getConstArray();
+ uno::Sequence< beans::PropertyState > aRet(rPropertyNames.getLength());
+ beans::PropertyState* pStates = aRet.getArray();
+ const SfxItemPropertyMap &rMap = rPropSet.getPropertyMap();
+ std::optional<SfxItemSet> oSet;
+ std::optional<SfxItemSet> oSetParent;
+
+ for (sal_Int32 i = 0, nEnd = rPropertyNames.getLength(); i < nEnd; i++)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ rMap.getByName( pNames[i] );
+ if(!pEntry)
+ {
+ if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
+ pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
+ pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ continue;
+ }
+ else if (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT ==
+ eCaller)
+ {
+ //this values marks the element as unknown property
+ pStates[i] = beans::PropertyState::PropertyState_MAKE_FIXED_SIZE;
+ continue;
+ }
+ else
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pNames[i]);
+ }
+ }
+ if (((SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION == eCaller) ||
+ (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT == eCaller)) &&
+ NotInRange(pEntry->nWID, FN_UNO_RANGE_BEGIN, FN_UNO_RANGE_END) &&
+ NotInRange(pEntry->nWID, RES_CHRATR_BEGIN, RES_TXTATR_END) )
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ if ( pEntry->nWID >= FN_UNO_RANGE_BEGIN &&
+ pEntry->nWID <= FN_UNO_RANGE_END )
+ {
+ (void)SwUnoCursorHelper::getCursorPropertyValue(
+ *pEntry, rPaM, nullptr, pStates[i] );
+ }
+ else
+ {
+ if (!oSet)
+ {
+ switch ( eCaller )
+ {
+ case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT:
+ case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION:
+ oSet.emplace( rPaM.GetDoc().GetAttrPool(),
+ svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END> );
+ break;
+ case SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY:
+ oSet.emplace( rPaM.GetDoc().GetAttrPool(),
+ pEntry->nWID, pEntry->nWID );
+ break;
+ default:
+ oSet.emplace(
+ rPaM.GetDoc().GetAttrPool(),
+ svl::Items<
+ RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_CONTAINER,
+ RES_UNKNOWNATR_CONTAINER>);
+ }
+ // #i63870#
+ SwUnoCursorHelper::GetCursorAttr( rPaM, *oSet );
+ }
+
+ pStates[i] = ( oSet->Count() )
+ ? rPropSet.getPropertyState( *pEntry, *oSet )
+ : beans::PropertyState_DEFAULT_VALUE;
+
+ //try again to find out if a value has been inherited
+ if( beans::PropertyState_DIRECT_VALUE == pStates[i] )
+ {
+ if (!oSetParent)
+ {
+ oSetParent.emplace(oSet->CloneAsValue( false ));
+ // #i63870#
+ SwUnoCursorHelper::GetCursorAttr(
+ rPaM, *oSetParent, true, false );
+ }
+
+ pStates[i] = ( oSetParent->Count() )
+ ? rPropSet.getPropertyState( *pEntry, *oSetParent )
+ : beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ }
+ }
+ return aRet;
+}
+
+beans::PropertyState SwUnoCursorHelper::GetPropertyState(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ const OUString& rPropertyName)
+{
+ uno::Sequence< OUString > aStrings { rPropertyName };
+ uno::Sequence< beans::PropertyState > aSeq =
+ GetPropertyStates(rPaM, rPropSet, aStrings,
+ SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY );
+ return aSeq[0];
+}
+
+static void
+lcl_SelectParaAndReset( SwPaM &rPaM, SwDoc & rDoc,
+ o3tl::sorted_vector<sal_uInt16> const &rWhichIds )
+{
+ // if we are resetting paragraph attributes, we need to select the full paragraph first
+ SwPosition aStart = *rPaM.Start();
+ SwPosition aEnd = *rPaM.End();
+ auto pTemp ( rDoc.CreateUnoCursor(aStart) );
+ if(!SwUnoCursorHelper::IsStartOfPara(*pTemp))
+ {
+ pTemp->MovePara(GoCurrPara, fnParaStart);
+ }
+ pTemp->SetMark();
+ *pTemp->GetPoint() = aEnd;
+ SwUnoCursorHelper::SelectPam(*pTemp, true);
+ if(!SwUnoCursorHelper::IsEndOfPara(*pTemp))
+ {
+ pTemp->MovePara(GoCurrPara, fnParaEnd);
+ }
+ rDoc.ResetAttrs(*pTemp, true, rWhichIds);
+}
+
+void SwUnoCursorHelper::SetPropertyToDefault(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ std::u16string_view rPropertyName)
+{
+ SwDoc& rDoc = rPaM.GetDoc();
+ SfxItemPropertyMapEntry const*const pEntry =
+ rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ OUString::Concat("Unknown property: ") + rPropertyName);
+ }
+
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw uno::RuntimeException(
+ OUString::Concat("setPropertyToDefault: property is read-only: ")
+ + rPropertyName, nullptr);
+ }
+
+ if (pEntry->nWID < RES_FRMATR_END)
+ {
+ const o3tl::sorted_vector<sal_uInt16> aWhichIds{ pEntry->nWID };
+ if (pEntry->nWID < RES_PARATR_BEGIN)
+ {
+ rDoc.ResetAttrs(rPaM, true, aWhichIds);
+ }
+ else
+ {
+ lcl_SelectParaAndReset ( rPaM, rDoc, aWhichIds );
+ }
+ }
+ else
+ {
+ SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, rPaM);
+ }
+}
+
+uno::Any SwUnoCursorHelper::GetPropertyDefault(
+ SwPaM const & rPaM, const SfxItemPropertySet& rPropSet,
+ std::u16string_view rPropertyName)
+{
+ SfxItemPropertyMapEntry const*const pEntry =
+ rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ OUString::Concat("Unknown property: ") + rPropertyName);
+ }
+
+ uno::Any aRet;
+ if (pEntry->nWID < RES_FRMATR_END)
+ {
+ SwDoc& rDoc = rPaM.GetDoc();
+ const SfxPoolItem& rDefItem =
+ rDoc.GetAttrPool().GetDefaultItem(pEntry->nWID);
+ rDefItem.QueryValue(aRet, pEntry->nMemberId);
+ }
+ return aRet;
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXTextCursor::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference< beans::XPropertySetInfo > xRef = [&]()
+ {
+ static SfxItemPropertyMapEntry const aCursorExtMap_Impl[] =
+ {
+ { UNO_NAME_IS_SKIP_HIDDEN_TEXT, FN_SKIP_HIDDEN_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { UNO_NAME_IS_SKIP_PROTECTED_TEXT, FN_SKIP_PROTECTED_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { UNO_NAME_NO_FORMAT_ATTR, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ };
+ const uno::Reference< beans::XPropertySetInfo > xInfo =
+ m_rPropSet.getPropertySetInfo();
+ // extend PropertySetInfo!
+ const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties();
+ return rtl::Reference<SfxExtItemPropertySetInfo>(new SfxExtItemPropertySetInfo(
+ aCursorExtMap_Impl,
+ aPropSeq ));
+ }();
+ return xRef;
+}
+
+void SAL_CALL
+SwXTextCursor::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (rPropertyName == UNO_NAME_IS_SKIP_HIDDEN_TEXT)
+ {
+ bool bSet(false);
+ if (!(rValue >>= bSet))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rUnoCursor.SetSkipOverHiddenSections(bSet);
+ }
+ else if (rPropertyName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
+ {
+ bool bSet(false);
+ if (!(rValue >>= bSet))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rUnoCursor.SetSkipOverProtectSections(bSet);
+ }
+ else if (rPropertyName == UNO_NAME_NO_FORMAT_ATTR)
+ {
+ bool bSet(false);
+ if (!(rValue >>= bSet))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (bSet)
+ {
+ m_nAttrMode = SetAttrMode::NOFORMATATTR;
+ }
+ else
+ {
+ m_nAttrMode = SetAttrMode::DEFAULT;
+ }
+ }
+ else if (rPropertyName == "ParaAutoStyleDef")
+ {
+ // Create an autostyle from passed definition (sequence of PropertyValue, same
+ // as in XAutoStyleFamily::insertStyle), using the currently applied properties
+ // from the paragraph to not lose their values when creating complex properties
+ // like SvxULSpaceItem, when only part of the properties stored there is passed;
+ // and apply it to the paragraph.
+ uno::Sequence<beans::PropertyValue> def;
+ if (!(rValue >>= def))
+ throw lang::IllegalArgumentException();
+
+ // See SwUnoCursorHelper::SetPropertyValues
+
+ auto pPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARA_AUTO_STYLE);
+
+ // Build set of attributes we want to fetch
+ WhichRangesContainer aRanges;
+ for (auto& rPropVal : def)
+ {
+ SfxItemPropertyMapEntry const* pEntry =
+ pPropSet->getPropertyMap().getByName(rPropVal.Name);
+ if (!pEntry)
+ continue; // PropValuesToAutoStyleItemSet ignores invalid names
+
+ aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID);
+ }
+
+ if (!aRanges.empty())
+ {
+ SwAttrSet aAutoStyleItemSet(rUnoCursor.GetDoc().GetAttrPool(), std::move(aRanges));
+ // we need to get up-to-date item set: this makes sure that the complex properties,
+ // that are only partially defined by passed definition, do not lose the rest of
+ // their already present data (which will become part of the autostyle, too).
+ SwUnoCursorHelper::GetCursorAttr(rUnoCursor, aAutoStyleItemSet);
+ // Set normal set ranges before putting into autostyle, to the same ranges
+ // that are used for paragraph autostyle in SwXAutoStyleFamily::insertStyle
+ aAutoStyleItemSet.SetRanges(aTextNodeSetRange);
+
+ // Fill the prepared item set, containing current paragraph property values,
+ // with the passed definition, and create the autostyle.
+ auto pStyle = PropValuesToAutoStyleItemSet(
+ rUnoCursor.GetDoc(), IStyleAccess::AUTO_STYLE_PARA, def, aAutoStyleItemSet);
+
+ SwFormatAutoFormat aFormat(RES_AUTO_STYLE);
+ aFormat.SetStyleHandle(pStyle);
+ SfxItemSet rSet(rUnoCursor.GetDoc().GetAttrPool(), RES_AUTO_STYLE, RES_AUTO_STYLE);
+ rSet.Put(aFormat);
+ SwUnoCursorHelper::SetCursorAttr(rUnoCursor, rSet, m_nAttrMode);
+ }
+ }
+ else
+ {
+ SwUnoCursorHelper::SetPropertyValue(rUnoCursor,
+ m_rPropSet, rPropertyName, rValue, m_nAttrMode);
+ }
+}
+
+uno::Any SAL_CALL
+SwXTextCursor::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ uno::Any aAny;
+ if (rPropertyName == UNO_NAME_IS_SKIP_HIDDEN_TEXT)
+ {
+ const bool bSet = rUnoCursor.IsSkipOverHiddenSections();
+ aAny <<= bSet;
+ }
+ else if (rPropertyName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
+ {
+ const bool bSet = rUnoCursor.IsSkipOverProtectSections();
+ aAny <<= bSet;
+ }
+ else
+ {
+ aAny = SwUnoCursorHelper::GetPropertyValue(rUnoCursor,
+ m_rPropSet, rPropertyName);
+ }
+ return aAny;
+}
+
+void SAL_CALL
+SwXTextCursor::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextCursor::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextCursor::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextCursor::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextCursor::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextCursor::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextCursor::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextCursor::removeVetoableChangeListener(): not implemented");
+}
+
+beans::PropertyState SAL_CALL
+SwXTextCursor::getPropertyState(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const beans::PropertyState eRet = SwUnoCursorHelper::GetPropertyState(
+ rUnoCursor, m_rPropSet, rPropertyName);
+ return eRet;
+}
+
+uno::Sequence< beans::PropertyState > SAL_CALL
+SwXTextCursor::getPropertyStates(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ return SwUnoCursorHelper::GetPropertyStates(
+ rUnoCursor, m_rPropSet, rPropertyNames);
+}
+
+void SAL_CALL
+SwXTextCursor::setPropertyToDefault(const OUString& rPropertyName)
+{
+ // forward: need no solar mutex here
+ uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
+ setPropertiesToDefault ( aSequence );
+}
+
+uno::Any SAL_CALL
+SwXTextCursor::getPropertyDefault(const OUString& rPropertyName)
+{
+ // forward: need no solar mutex here
+ const uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
+ return getPropertyDefaults ( aSequence ).getConstArray()[0];
+}
+
+void SAL_CALL SwXTextCursor::setPropertyValues(
+ const uno::Sequence< OUString >& aPropertyNames,
+ const uno::Sequence< uno::Any >& aValues )
+{
+ if( aValues.getLength() != aPropertyNames.getLength() )
+ {
+ OSL_FAIL( "mis-matched property value sequences" );
+ throw lang::IllegalArgumentException();
+ }
+
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // a little lame to have to copy into this.
+ uno::Sequence< beans::PropertyValue > aPropertyValues( aValues.getLength() );
+ auto aPropertyValuesRange = asNonConstRange(aPropertyValues);
+ for ( sal_Int32 i = 0; i < aPropertyNames.getLength(); i++ )
+ {
+ if ( aPropertyNames[ i ] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
+ aPropertyNames[ i ] == UNO_NAME_IS_SKIP_PROTECTED_TEXT )
+ {
+ // the behaviour of these is hard to model in a group
+ OSL_FAIL("invalid property name for batch setting");
+ throw lang::IllegalArgumentException();
+ }
+ aPropertyValuesRange[ i ].Name = aPropertyNames[ i ];
+ aPropertyValuesRange[ i ].Value = aValues[ i ];
+ }
+ try
+ {
+ SwUnoCursorHelper::SetPropertyValues( rUnoCursor, m_rPropSet, aPropertyValues );
+ }
+ catch (const css::beans::UnknownPropertyException& e)
+ {
+ uno::Any a(cppu::getCaughtException());
+ throw lang::WrappedTargetException(
+ "wrapped Exception " + e.Message,
+ uno::Reference<uno::XInterface>(), a);
+ }
+}
+
+uno::Sequence< uno::Any > SAL_CALL
+SwXTextCursor::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames )
+{
+ // a banal implementation for now
+ uno::Sequence< uno::Any > aValues( aPropertyNames.getLength() );
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aValues.getArray(),
+ [this](const OUString& rName) -> uno::Any { return getPropertyValue( rName ); });
+ return aValues;
+}
+
+void SAL_CALL SwXTextCursor::addPropertiesChangeListener(
+ const uno::Sequence< OUString >& /* aPropertyNames */,
+ const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("SwXTextCursor::addPropertiesChangeListener(): not implemented");
+}
+void SAL_CALL SwXTextCursor::removePropertiesChangeListener(
+ const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("SwXTextCursor::removePropertiesChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXTextCursor::firePropertiesChangeEvent(
+ const uno::Sequence< OUString >& /* aPropertyNames */,
+ const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("SwXTextCursor::firePropertiesChangeEvent(): not implemented");
+}
+
+// para specific attribute ranges
+static sal_uInt16 g_ParaResetableSetRange[] = {
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_PARATR_BEGIN, RES_PARATR_END-1,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
+ 0
+};
+
+// selection specific attribute ranges
+static sal_uInt16 g_ResetableSetRange[] = {
+ RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_TXTATR_INETFMT, RES_TXTATR_INETFMT,
+ RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT,
+ RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
+ RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER,
+ 0
+};
+
+static void
+lcl_EnumerateIds(sal_uInt16 const* pIdRange, o3tl::sorted_vector<sal_uInt16> &rWhichIds)
+{
+ while (*pIdRange)
+ {
+ const sal_uInt16 nStart = *pIdRange++;
+ const sal_uInt16 nEnd = *pIdRange++;
+ for (sal_uInt16 nId = nStart + 1; nId <= nEnd; ++nId)
+ {
+ rWhichIds.insert( nId );
+ }
+ }
+}
+
+void SAL_CALL
+SwXTextCursor::setAllPropertiesToDefault()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ o3tl::sorted_vector<sal_uInt16> aParaWhichIds;
+ o3tl::sorted_vector<sal_uInt16> aWhichIds;
+ lcl_EnumerateIds(g_ParaResetableSetRange, aParaWhichIds);
+ lcl_EnumerateIds(g_ResetableSetRange, aWhichIds);
+ if (!aParaWhichIds.empty())
+ {
+ lcl_SelectParaAndReset(rUnoCursor, rUnoCursor.GetDoc(),
+ aParaWhichIds);
+ }
+ if (!aWhichIds.empty())
+ {
+ rUnoCursor.GetDoc().ResetAttrs(rUnoCursor, true, aWhichIds);
+ }
+}
+
+void SAL_CALL
+SwXTextCursor::setPropertiesToDefault(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if ( !rPropertyNames.hasElements() )
+ return;
+
+ SwDoc& rDoc = rUnoCursor.GetDoc();
+ o3tl::sorted_vector<sal_uInt16> aWhichIds;
+ o3tl::sorted_vector<sal_uInt16> aParaWhichIds;
+ for (const OUString& rName : rPropertyNames)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_rPropSet.getPropertyMap().getByName( rName );
+ if (!pEntry)
+ {
+ if (rName == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
+ rName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
+ {
+ continue;
+ }
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rName,
+ getXWeak());
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw uno::RuntimeException(
+ "setPropertiesToDefault: property is read-only: " + rName,
+ getXWeak());
+ }
+
+ if (pEntry->nWID < RES_FRMATR_END)
+ {
+ if (pEntry->nWID < RES_PARATR_BEGIN)
+ {
+ aWhichIds.insert( pEntry->nWID );
+ }
+ else
+ {
+ aParaWhichIds.insert( pEntry->nWID );
+ }
+ }
+ else if (pEntry->nWID == FN_UNO_NUM_START_VALUE)
+ {
+ SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, rUnoCursor);
+ }
+ }
+
+ if (!aParaWhichIds.empty())
+ {
+ lcl_SelectParaAndReset(rUnoCursor, rDoc, aParaWhichIds);
+ }
+ if (!aWhichIds.empty())
+ {
+ rDoc.ResetAttrs(rUnoCursor, true, aWhichIds);
+ }
+}
+
+uno::Sequence< uno::Any > SAL_CALL
+SwXTextCursor::getPropertyDefaults(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const sal_Int32 nCount = rPropertyNames.getLength();
+ uno::Sequence< uno::Any > aRet(nCount);
+ if ( nCount )
+ {
+ SwDoc& rDoc = rUnoCursor.GetDoc();
+ const OUString *pNames = rPropertyNames.getConstArray();
+ uno::Any *pAny = aRet.getArray();
+ for (sal_Int32 i = 0; i < nCount; i++)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_rPropSet.getPropertyMap().getByName( pNames[i] );
+ if (!pEntry)
+ {
+ if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
+ pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
+ pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
+ {
+ continue;
+ }
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pNames[i]);
+ }
+ if (pEntry->nWID < RES_FRMATR_END)
+ {
+ const SfxPoolItem& rDefItem =
+ rDoc.GetAttrPool().GetDefaultItem(pEntry->nWID);
+ rDefItem.QueryValue(pAny[i], pEntry->nMemberId);
+ }
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL SwXTextCursor::invalidateMarkings(::sal_Int32 nType)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwNode& node = rUnoCursor.GetPointNode();
+
+ SwTextNode* txtNode = node.GetTextNode();
+
+ if (txtNode == nullptr) return;
+
+ if ( text::TextMarkupType::SPELLCHECK == nType )
+ {
+ txtNode->SetWrongDirty(sw::WrongState::TODO);
+ txtNode->ClearWrong();
+ }
+ else if( text::TextMarkupType::PROOFREADING == nType )
+ {
+ txtNode->SetGrammarCheckDirty(true);
+ txtNode->ClearGrammarCheck();
+ }
+ else if ( text::TextMarkupType::SMARTTAG == nType )
+ {
+ txtNode->SetSmartTagDirty(true);
+ txtNode->ClearSmartTags();
+ }
+ else return;
+
+ SwFormatColl* fmtColl=txtNode->GetFormatColl();
+
+ if (fmtColl == nullptr) return;
+
+ SwFormatChg aNew( fmtColl );
+ txtNode->CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew));
+}
+
+void SAL_CALL
+SwXTextCursor::makeRedline(
+ const OUString& rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::makeRedline(rUnoCursor, rRedlineType, rRedlineProperties);
+}
+
+void SAL_CALL SwXTextCursor::insertDocumentFromURL(const OUString& rURL,
+ const uno::Sequence< beans::PropertyValue >& rOptions)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::InsertFile(&rUnoCursor, rURL, rOptions);
+}
+
+uno::Sequence< beans::PropertyValue >
+SwUnoCursorHelper::CreateSortDescriptor(const bool bFromTable)
+{
+ uno::Sequence< beans::PropertyValue > aRet(5);
+ beans::PropertyValue* pArray = aRet.getArray();
+
+ uno::Any aVal;
+ aVal <<= bFromTable;
+ pArray[0] = beans::PropertyValue("IsSortInTable", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= u' ';
+ pArray[1] = beans::PropertyValue("Delimiter", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= false;
+ pArray[2] = beans::PropertyValue("IsSortColumns", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= sal_Int32(3);
+ pArray[3] = beans::PropertyValue("MaxSortFieldsCount", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ lang::Locale aLang( SvtSysLocale().GetLanguageTag().getLocale());
+ // get collator algorithm to be used for the locale
+ uno::Sequence< OUString > aSeq(
+ GetAppCollator().listCollatorAlgorithms( aLang ) );
+ const bool bHasElements = aSeq.hasElements();
+ OSL_ENSURE( bHasElements, "list of collator algorithms is empty!");
+ OUString aCollAlg;
+ if (bHasElements)
+ {
+ aCollAlg = aSeq.getConstArray()[0];
+ }
+
+ uno::Sequence< table::TableSortField > aFields
+ {
+ // Field, IsAscending, IsCaseSensitive, FieldType, CollatorLocale, CollatorAlgorithm
+ { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg },
+ { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg },
+ { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg }
+ };
+
+ aVal <<= aFields;
+ pArray[4] = beans::PropertyValue("SortFields", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ return aRet;
+}
+
+uno::Sequence< beans::PropertyValue > SAL_CALL
+SwXTextCursor::createSortDescriptor()
+{
+ SolarMutexGuard aGuard;
+
+ return SwUnoCursorHelper::CreateSortDescriptor(false);
+}
+
+bool SwUnoCursorHelper::ConvertSortProperties(
+ const uno::Sequence< beans::PropertyValue >& rDescriptor,
+ SwSortOptions& rSortOpt)
+{
+ bool bRet = true;
+
+ rSortOpt.bTable = false;
+ rSortOpt.cDeli = ' ';
+ rSortOpt.eDirection = SwSortDirection::Columns; //!! UI text may be contrary though !!
+
+ SwSortKey aKey1;
+ aKey1.nColumnId = USHRT_MAX;
+ aKey1.bIsNumeric = true;
+ aKey1.eSortOrder = SwSortOrder::Ascending;
+
+ SwSortKey aKey2;
+ aKey2.nColumnId = USHRT_MAX;
+ aKey2.bIsNumeric = true;
+ aKey2.eSortOrder = SwSortOrder::Ascending;
+
+ SwSortKey aKey3;
+ aKey3.nColumnId = USHRT_MAX;
+ aKey3.bIsNumeric = true;
+ aKey3.eSortOrder = SwSortOrder::Ascending;
+ SwSortKey* aKeys[3] = {&aKey1, &aKey2, &aKey3};
+
+ bool bOldSortdescriptor(false);
+ bool bNewSortdescriptor(false);
+
+ for (const beans::PropertyValue& rProperty : rDescriptor)
+ {
+ uno::Any aValue( rProperty.Value );
+ const OUString& rPropName = rProperty.Name;
+
+ // old and new sortdescriptor
+ if ( rPropName == "IsSortInTable" )
+ {
+ if (auto b = o3tl::tryAccess<bool>(aValue))
+ {
+ rSortOpt.bTable = *b;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if ( rPropName == "Delimiter" )
+ {
+ sal_Unicode uChar;
+ sal_uInt16 nChar;
+ if (aValue >>= uChar)
+ {
+ rSortOpt.cDeli = uChar;
+ }
+ else if (aValue >>= nChar)
+ {
+ // For compatibility with BASIC, also accept an ANY containing
+ // an UNSIGNED SHORT:
+ rSortOpt.cDeli = nChar;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ // old sortdescriptor
+ else if ( rPropName == "SortColumns" )
+ {
+ bOldSortdescriptor = true;
+ bool bTemp(false);
+ if (aValue >>= bTemp)
+ {
+ rSortOpt.eDirection = bTemp ? SwSortDirection::Columns : SwSortDirection::Rows;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if ( rPropName == "IsCaseSensitive" )
+ {
+ bOldSortdescriptor = true;
+ bool bTemp(false);
+ if (aValue >>= bTemp)
+ {
+ rSortOpt.bIgnoreCase = !bTemp;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if ( rPropName == "CollatorLocale" )
+ {
+ bOldSortdescriptor = true;
+ lang::Locale aLocale;
+ if (aValue >>= aLocale)
+ {
+ rSortOpt.nLanguage = LanguageTag::convertToLanguageType( aLocale);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if (rPropName.startsWith("CollatorAlgorithm") &&
+ rPropName.getLength() == 18 &&
+ (rPropName[17] >= '0' && rPropName[17] <= '9'))
+ {
+ bOldSortdescriptor = true;
+ sal_uInt16 nIndex = rPropName[17];
+ nIndex -= '0';
+ OUString aText;
+ if ((aValue >>= aText) && nIndex < 3)
+ {
+ aKeys[nIndex]->sSortType = aText;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if (rPropName.startsWith("SortRowOrColumnNo") &&
+ rPropName.getLength() == 18 &&
+ (rPropName[17] >= '0' && rPropName[17] <= '9'))
+ {
+ bOldSortdescriptor = true;
+ sal_uInt16 nIndex = rPropName[17];
+ nIndex -= '0';
+ sal_Int16 nCol = -1;
+ if (aValue.getValueType() == ::cppu::UnoType<sal_Int16>::get()
+ && nIndex < 3)
+ {
+ aValue >>= nCol;
+ }
+ if (nCol >= 0)
+ {
+ aKeys[nIndex]->nColumnId = nCol;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if (rPropName.startsWith("IsSortNumeric") &&
+ rPropName.getLength() == 14 &&
+ (rPropName[13] >= '0' && rPropName[13] <= '9'))
+ {
+ bOldSortdescriptor = true;
+ sal_uInt16 nIndex = rPropName[13];
+ nIndex = nIndex - '0';
+ std::optional<const bool> bTemp = o3tl::tryAccess<bool>(aValue);
+ if (bTemp.has_value() && nIndex < 3)
+ {
+ aKeys[nIndex]->bIsNumeric = *bTemp;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if (rPropName.startsWith("IsSortAscending") &&
+ rPropName.getLength() == 16 &&
+ (rPropName[15] >= '0' && rPropName[15] <= '9'))
+ {
+ bOldSortdescriptor = true;
+ sal_uInt16 nIndex = rPropName[15];
+ nIndex -= '0';
+ std::optional<const bool> bTemp = o3tl::tryAccess<bool>(aValue);
+ if (bTemp.has_value() && nIndex < 3)
+ {
+ aKeys[nIndex]->eSortOrder = (*bTemp)
+ ? SwSortOrder::Ascending : SwSortOrder::Descending;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ // new sortdescriptor
+ else if ( rPropName == "IsSortColumns" )
+ {
+ bNewSortdescriptor = true;
+ if (auto bTemp = o3tl::tryAccess<bool>(aValue))
+ {
+ rSortOpt.eDirection = *bTemp ? SwSortDirection::Columns : SwSortDirection::Rows;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if ( rPropName == "SortFields" )
+ {
+ bNewSortdescriptor = true;
+ uno::Sequence < table::TableSortField > aFields;
+ if (aValue >>= aFields)
+ {
+ sal_Int32 nCount(aFields.getLength());
+ if (nCount <= 3)
+ {
+ table::TableSortField* pFields = aFields.getArray();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ rSortOpt.bIgnoreCase = !pFields[i].IsCaseSensitive;
+ rSortOpt.nLanguage =
+ LanguageTag::convertToLanguageType( pFields[i].CollatorLocale );
+ aKeys[i]->sSortType = pFields[i].CollatorAlgorithm;
+ aKeys[i]->nColumnId =
+ o3tl::narrowing<sal_uInt16>(pFields[i].Field);
+ aKeys[i]->bIsNumeric = (pFields[i].FieldType ==
+ table::TableSortFieldType_NUMERIC);
+ aKeys[i]->eSortOrder = (pFields[i].IsAscending)
+ ? SwSortOrder::Ascending : SwSortOrder::Descending;
+ }
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ }
+
+ if (bNewSortdescriptor && bOldSortdescriptor)
+ {
+ OSL_FAIL("someone tried to set the old deprecated and "
+ "the new sortdescriptor");
+ bRet = false;
+ }
+
+ if (aKey1.nColumnId != USHRT_MAX)
+ {
+ rSortOpt.aKeys.push_back(aKey1);
+ }
+ if (aKey2.nColumnId != USHRT_MAX)
+ {
+ rSortOpt.aKeys.push_back(aKey2);
+ }
+ if (aKey3.nColumnId != USHRT_MAX)
+ {
+ rSortOpt.aKeys.push_back(aKey3);
+ }
+
+ return bRet && !rSortOpt.aKeys.empty();
+}
+
+void SAL_CALL
+SwXTextCursor::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (!rUnoCursor.HasMark())
+ return;
+
+ SwSortOptions aSortOpt;
+ if (!SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt))
+ {
+ throw uno::RuntimeException("Bad sort properties");
+ }
+ UnoActionContext aContext( &rUnoCursor.GetDoc() );
+
+ SwPosition & rStart = *rUnoCursor.Start();
+ SwPosition & rEnd = *rUnoCursor.End();
+
+ SwNodeIndex aPrevIdx( rStart.GetNode(), -1 );
+ const SwNodeOffset nOffset = rEnd.GetNodeIndex() - rStart.GetNodeIndex();
+ const sal_Int32 nCntStt = rStart.GetContentIndex();
+
+ rUnoCursor.GetDoc().SortText(rUnoCursor, aSortOpt);
+
+ // update selection
+ rUnoCursor.DeleteMark();
+ rUnoCursor.GetPoint()->Assign( aPrevIdx.GetNode(), SwNodeOffset(1) );
+ SwContentNode *const pCNd = rUnoCursor.GetPointContentNode();
+ sal_Int32 nLen = pCNd->Len();
+ if (nLen > nCntStt)
+ {
+ nLen = nCntStt;
+ }
+ rUnoCursor.GetPoint()->SetContent( nLen );
+ rUnoCursor.SetMark();
+
+ rUnoCursor.GetPoint()->Adjust(nOffset);
+ SwContentNode *const pCNd2 = rUnoCursor.GetPointContentNode();
+ rUnoCursor.GetPoint()->SetContent( pCNd2->Len() );
+
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXTextCursor::createContentEnumeration(const OUString& rServiceName)
+{
+ SolarMutexGuard g;
+ if (rServiceName != "com.sun.star.text.TextContent")
+ throw uno::RuntimeException();
+ SwUnoCursor& rUnoCursor( GetCursorOrThrow() );
+ return SwXParaFrameEnumeration::Create(rUnoCursor, PARAFRAME_PORTION_TEXTRANGE);
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXTextCursor::createEnumeration()
+{
+ SolarMutexGuard g;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwXText* pParentText = dynamic_cast<SwXText*>(m_xParentText.get());
+ OSL_ENSURE(pParentText, "parent is not a SwXText");
+ if (!pParentText)
+ {
+ throw uno::RuntimeException();
+ }
+
+ auto pNewCursor(rUnoCursor.GetDoc().CreateUnoCursor(*rUnoCursor.GetPoint()) );
+ if (rUnoCursor.HasMark())
+ {
+ pNewCursor->SetMark();
+ *pNewCursor->GetMark() = *rUnoCursor.GetMark();
+ }
+ const CursorType eSetType = (CursorType::TableText == m_eType)
+ ? CursorType::SelectionInTable : CursorType::Selection;
+ return SwXParagraphEnumeration::Create(pParentText, pNewCursor, eSetType);
+}
+
+uno::Type SAL_CALL
+SwXTextCursor::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SwXTextCursor::hasElements()
+{
+ return true;
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXTextCursor::getAvailableServiceNames()
+{
+ uno::Sequence<OUString> aRet { "com.sun.star.text.TextContent" };
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */