Filter out more unwanted command URIs
[LibreOffice.git] / sw / source / core / unocore / unoobj.cxx
blob5047a2ddab86139f05e3ffed636328f1f2041696
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 <unoport.hxx>
42 #include <swundo.hxx>
43 #include <rootfrm.hxx>
44 #include <paratr.hxx>
45 #include <pam.hxx>
46 #include <shellio.hxx>
47 #include <unotbl.hxx>
48 #include <fmtruby.hxx>
49 #include <docsh.hxx>
50 #include <docstyle.hxx>
51 #include <fmtpdsc.hxx>
52 #include <pagedesc.hxx>
53 #include <edimp.hxx>
54 #include <fchrfmt.hxx>
55 #include <fmtautofmt.hxx>
56 #include <unotextrange.hxx>
57 #include <unotextcursor.hxx>
58 #include <unomap.hxx>
59 #include <unoprnms.hxx>
60 #include <unometa.hxx>
61 #include <unocontentcontrol.hxx>
62 #include <unotext.hxx>
63 #include <com/sun/star/text/TextMarkupType.hpp>
64 #include <utility>
65 #include <vcl/svapp.hxx>
66 #include <unotools/syslocale.hxx>
67 #include <i18nlangtag/languagetag.hxx>
68 #include <SwStyleNameMapper.hxx>
69 #include <sortopt.hxx>
70 #include <com/sun/star/beans/PropertyAttribute.hpp>
71 #include <com/sun/star/beans/NamedValue.hpp>
72 #include <com/sun/star/i18n/WordType.hpp>
73 #include <memory>
74 #include <unoparaframeenum.hxx>
75 #include <unoparagraph.hxx>
76 #include <iodetect.hxx>
77 #include <comphelper/propertyvalue.hxx>
78 #include <comphelper/servicehelper.hxx>
79 #include <comphelper/profilezone.hxx>
80 #include <comphelper/flagguard.hxx>
81 #include <swmodule.hxx>
82 #include <names.hxx>
84 using namespace ::com::sun::star;
86 // Helper classes
87 SwUnoInternalPaM::SwUnoInternalPaM(SwDoc& rDoc) :
88 SwPaM(rDoc.GetNodes())
92 SwUnoInternalPaM::~SwUnoInternalPaM()
94 while( GetNext() != this)
96 // coverity[deref_arg] - the delete moves a new entry into GetNext()
97 delete GetNext();
101 SwUnoInternalPaM& SwUnoInternalPaM::operator=(const SwPaM& rPaM)
103 const SwPaM* pTmp = &rPaM;
104 *GetPoint() = *rPaM.GetPoint();
105 if(rPaM.HasMark())
107 SetMark();
108 *GetMark() = *rPaM.GetMark();
110 else
111 DeleteMark();
112 while(&rPaM != (pTmp = pTmp->GetNext()))
114 if(pTmp->HasMark())
115 new SwPaM(*pTmp->GetMark(), *pTmp->GetPoint(), this);
116 else
117 new SwPaM(*pTmp->GetPoint(), this);
119 return *this;
122 void SwUnoCursorHelper::SelectPam(SwPaM & rPam, const bool bExpand)
124 if (bExpand)
126 if (!rPam.HasMark())
128 rPam.SetMark();
131 else if (rPam.HasMark())
133 rPam.DeleteMark();
137 void SwUnoCursorHelper::GetTextFromPam(SwPaM & rPam, OUString & rBuffer,
138 SwRootFrame const*const pLayout)
140 if (!rPam.HasMark())
142 return;
144 SvMemoryStream aStream;
145 #ifdef OSL_BIGENDIAN
146 aStream.SetEndian( SvStreamEndian::BIG );
147 #else
148 aStream.SetEndian( SvStreamEndian::LITTLE );
149 #endif
150 WriterRef xWrt;
151 // TODO/MBA: looks like a BaseURL doesn't make sense here
152 SwReaderWriter::GetWriter( FILTER_TEXT_DLG, OUString(), xWrt );
153 if( !xWrt.is() )
154 return;
156 SwWriter aWriter( aStream, rPam );
157 xWrt->m_bASCII_NoLastLineEnd = true;
158 xWrt->m_bExportParagraphNumbering = false;
159 SwAsciiOptions aOpt = xWrt->GetAsciiOptions();
160 aOpt.SetCharSet( RTL_TEXTENCODING_UNICODE );
161 xWrt->SetAsciiOptions( aOpt );
162 xWrt->m_bUCS2_WithStartChar = false;
163 // #i68522#
164 const bool bOldShowProgress = xWrt->m_bShowProgress;
165 xWrt->m_bShowProgress = false;
166 xWrt->m_bHideDeleteRedlines = pLayout && pLayout->IsHideRedlines();
167 // tdf#155951 SwWriter::Write calls EndAllAction, and that
168 // called SelectShell(), triggering selection change event, which
169 // resulted infinite recursion, if selectionChanged() calls
170 // XTextRange::getString() e.g. on the selected range.
171 ::comphelper::FlagRestorationGuard g(g_bNoInterrupt, true);
173 if( ! aWriter.Write( xWrt ).IsError() )
175 const sal_uInt64 lUniLen = aStream.GetSize()/sizeof( sal_Unicode );
176 if (lUniLen < o3tl::make_unsigned(SAL_MAX_INT32-1))
178 aStream.WriteUInt16( '\0' );
180 aStream.Seek( 0 );
181 aStream.ResetError();
183 rtl_uString *pStr = rtl_uString_alloc(lUniLen);
184 aStream.ReadBytes(pStr->buffer, lUniLen * sizeof(sal_Unicode));
185 rBuffer = OUString(pStr, SAL_NO_ACQUIRE);
188 xWrt->m_bShowProgress = bOldShowProgress;
192 /// @throws lang::IllegalArgumentException
193 /// @throws uno::RuntimeException
194 static void
195 lcl_setCharStyle(SwDoc& rDoc, const uno::Any & rValue, SfxItemSet & rSet)
197 SwDocShell *const pDocSh = rDoc.GetDocShell();
198 if(!pDocSh)
199 return;
201 OUString uStyle;
202 if (!(rValue >>= uStyle))
204 throw lang::IllegalArgumentException();
206 OUString sStyle;
207 SwStyleNameMapper::FillUIName(ProgName(uStyle), sStyle,
208 SwGetPoolIdFromName::ChrFmt);
209 SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
210 pDocSh->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
211 if (!pStyle)
213 throw lang::IllegalArgumentException();
215 const SwFormatCharFormat aFormat(pStyle->GetCharFormat());
216 rSet.Put(aFormat);
219 /// @throws lang::IllegalArgumentException
220 static void
221 lcl_setAutoStyle(IStyleAccess & rStyleAccess, const uno::Any & rValue,
222 SfxItemSet & rSet, const bool bPara)
224 OUString uStyle;
225 if (!(rValue >>= uStyle))
227 throw lang::IllegalArgumentException();
229 std::shared_ptr<SfxItemSet> pStyle = bPara ?
230 rStyleAccess.getByName(uStyle, IStyleAccess::AUTO_STYLE_PARA ):
231 rStyleAccess.getByName(uStyle, IStyleAccess::AUTO_STYLE_CHAR );
232 if(!pStyle)
234 throw lang::IllegalArgumentException();
237 SwFormatAutoFormat aFormat( bPara
238 ? sal::static_int_cast< sal_uInt16 >(RES_AUTO_STYLE)
239 : sal::static_int_cast< sal_uInt16 >(RES_TXTATR_AUTOFMT) );
240 aFormat.SetStyleHandle( pStyle );
241 rSet.Put(aFormat);
244 void
245 SwUnoCursorHelper::SetTextFormatColl(const uno::Any & rAny, SwPaM & rPaM)
247 SwDoc& rDoc = rPaM.GetDoc();
248 SwDocShell *const pDocSh = rDoc.GetDocShell();
249 if(!pDocSh)
250 return;
251 OUString uStyle;
252 rAny >>= uStyle;
253 OUString sStyle;
254 SwStyleNameMapper::FillUIName(ProgName(uStyle), sStyle,
255 SwGetPoolIdFromName::TxtColl );
256 SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
257 pDocSh->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Para));
258 if (!pStyle)
260 throw lang::IllegalArgumentException();
263 SwTextFormatColl *const pLocal = pStyle->GetCollection();
264 UnoActionContext aAction(&rDoc);
265 rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
266 SwPaM *pTmpCursor = &rPaM;
267 do {
268 rDoc.SetTextFormatColl(*pTmpCursor, pLocal);
269 pTmpCursor = pTmpCursor->GetNext();
270 } while ( pTmpCursor != &rPaM );
271 rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
274 bool
275 SwUnoCursorHelper::SetPageDesc(
276 const uno::Any& rValue, SwDoc & rDoc, SfxItemSet & rSet)
278 OUString uDescName;
279 if (!(rValue >>= uDescName))
281 return false;
283 std::unique_ptr<SwFormatPageDesc> pNewDesc;
284 if(const SwFormatPageDesc* pItem = rSet.GetItemIfSet( RES_PAGEDESC ))
286 pNewDesc.reset(new SwFormatPageDesc(*pItem));
288 if (!pNewDesc)
290 pNewDesc.reset(new SwFormatPageDesc());
292 OUString sDescName;
293 SwStyleNameMapper::FillUIName(ProgName(uDescName), sDescName,
294 SwGetPoolIdFromName::PageDesc);
295 if (!pNewDesc->GetPageDesc() ||
296 (pNewDesc->GetPageDesc()->GetName() != sDescName))
298 bool bPut = false;
299 if (!sDescName.isEmpty())
301 SwPageDesc *const pPageDesc = SwPageDesc::GetByName(rDoc, sDescName);
302 if (!pPageDesc)
304 throw lang::IllegalArgumentException();
306 pNewDesc->RegisterToPageDesc(*pPageDesc);
307 bPut = true;
309 if(!bPut)
311 rSet.ClearItem(RES_BREAK);
312 rSet.Put(SwFormatPageDesc());
314 else
316 rSet.Put(std::move(pNewDesc));
319 return true;
322 static void
323 lcl_SetNodeNumStart(SwPaM & rCursor, uno::Any const& rValue)
325 sal_Int16 nTmp = 1;
326 rValue >>= nTmp;
327 sal_uInt16 nStt = (nTmp < 0 ? USHRT_MAX : o3tl::narrowing<sal_uInt16>(nTmp));
328 SwDoc& rDoc = rCursor.GetDoc();
329 UnoActionContext aAction(&rDoc);
331 if( rCursor.GetNext() != &rCursor ) // MultiSelection?
333 rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
334 SwPamRanges aRangeArr( rCursor );
335 SwPaM aPam( *rCursor.GetPoint() );
336 for( size_t n = 0; n < aRangeArr.Count(); ++n )
338 rDoc.SetNumRuleStart(*aRangeArr.SetPam( n, aPam ).GetPoint());
339 rDoc.SetNodeNumStart(*aRangeArr.SetPam( n, aPam ).GetPoint(),
340 nStt );
342 rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
344 else
346 rDoc.SetNumRuleStart( *rCursor.GetPoint());
347 rDoc.SetNodeNumStart( *rCursor.GetPoint(), nStt );
351 static bool
352 lcl_setCharFormatSequence(SwPaM & rPam, uno::Any const& rValue)
354 uno::Sequence<OUString> aCharStyles;
355 if (!(rValue >>= aCharStyles))
357 return false;
360 for (sal_Int32 nStyle = 0; nStyle < aCharStyles.getLength(); nStyle++)
362 uno::Any aStyle;
363 rPam.GetDoc().GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
364 aStyle <<= aCharStyles.getConstArray()[nStyle];
365 // create a local set and apply each format directly
366 SfxItemSetFixed<RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT> aSet(rPam.GetDoc().GetAttrPool());
367 lcl_setCharStyle(rPam.GetDoc(), aStyle, aSet);
368 // the first style should replace the current attributes,
369 // all other have to be added
370 SwUnoCursorHelper::SetCursorAttr(rPam, aSet, nStyle
371 ? SetAttrMode::DONTREPLACE
372 : SetAttrMode::DEFAULT);
373 rPam.GetDoc().GetIDocumentUndoRedo().EndUndo(SwUndoId::START, nullptr);
375 return true;
378 static void
379 lcl_setDropcapCharStyle(SwPaM const & rPam, SfxItemSet & rItemSet,
380 uno::Any const& rValue)
382 OUString uStyle;
383 if (!(rValue >>= uStyle))
385 throw lang::IllegalArgumentException();
387 OUString sStyle;
388 SwStyleNameMapper::FillUIName(ProgName(uStyle), sStyle,
389 SwGetPoolIdFromName::ChrFmt);
390 SwDoc& rDoc = rPam.GetDoc();
391 //default character style must not be set as default format
392 SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
393 rDoc.GetDocShell()
394 ->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
395 if (!pStyle || pStyle->GetCharFormat() == rDoc.GetDfltCharFormat())
397 throw lang::IllegalArgumentException();
399 std::unique_ptr<SwFormatDrop> pDrop;
400 if (const SwFormatDrop* pItem = rItemSet.GetItemIfSet(RES_PARATR_DROP))
402 pDrop.reset(new SwFormatDrop(*pItem));
404 if (!pDrop)
406 pDrop.reset(new SwFormatDrop);
408 const rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*pStyle));
409 pDrop->SetCharFormat(xStyle->GetCharFormat());
410 rItemSet.Put(std::move(pDrop));
413 static void
414 lcl_setRubyCharstyle(SfxItemSet & rItemSet, uno::Any const& rValue)
416 OUString sTmp;
417 if (!(rValue >>= sTmp))
419 throw lang::IllegalArgumentException();
422 std::unique_ptr<SwFormatRuby> pRuby;
423 if (const SwFormatRuby* pItem = rItemSet.GetItemIfSet(RES_TXTATR_CJK_RUBY))
425 pRuby.reset(new SwFormatRuby(*pItem));
427 if (!pRuby)
429 pRuby.reset(new SwFormatRuby(OUString()));
431 OUString sStyle;
432 SwStyleNameMapper::FillUIName(ProgName(sTmp), sStyle,
433 SwGetPoolIdFromName::ChrFmt);
434 pRuby->SetCharFormatName(sStyle);
435 pRuby->SetCharFormatId(0);
436 if (!sStyle.isEmpty())
438 const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(
439 sStyle, SwGetPoolIdFromName::ChrFmt);
440 pRuby->SetCharFormatId(nId);
442 rItemSet.Put(std::move(pRuby));
445 bool
446 SwUnoCursorHelper::SetCursorPropertyValue(
447 SfxItemPropertyMapEntry const& rEntry, const uno::Any& rValue,
448 SwPaM & rPam, SfxItemSet & rItemSet)
450 if (!(rEntry.nFlags & beans::PropertyAttribute::MAYBEVOID) &&
451 (rValue.getValueType() == cppu::UnoType<void>::get()))
453 return false;
455 bool bRet = true;
456 switch (rEntry.nWID)
458 case RES_TXTATR_CHARFMT:
459 lcl_setCharStyle(rPam.GetDoc(), rValue, rItemSet);
460 break;
461 case RES_TXTATR_AUTOFMT:
462 lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
463 rValue, rItemSet, false);
464 break;
465 case FN_UNO_CHARFMT_SEQUENCE:
466 lcl_setCharFormatSequence(rPam, rValue);
467 break;
468 case FN_UNO_PARA_STYLE :
469 SwUnoCursorHelper::SetTextFormatColl(rValue, rPam);
470 break;
471 case RES_AUTO_STYLE:
472 lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
473 rValue, rItemSet, true);
474 break;
475 case FN_UNO_PAGE_STYLE:
476 //FIXME nothing here?
477 break;
478 case FN_UNO_NUM_START_VALUE:
479 lcl_SetNodeNumStart( rPam, rValue );
480 break;
481 case FN_UNO_NUM_LEVEL:
482 // #i91601#
483 case FN_UNO_LIST_ID:
484 case FN_UNO_IS_NUMBER:
485 case FN_UNO_PARA_NUM_AUTO_FORMAT:
487 // multi selection is not considered
488 SwTextNode *const pTextNd = rPam.GetPointNode().GetTextNode();
489 if (!pTextNd)
491 throw lang::IllegalArgumentException();
493 if (FN_UNO_NUM_LEVEL == rEntry.nWID)
495 sal_Int16 nLevel = 0;
496 if (rValue >>= nLevel)
498 if (nLevel < 0 || MAXLEVEL <= nLevel)
500 throw lang::IllegalArgumentException(
501 u"invalid NumberingLevel"_ustr, nullptr, 0);
503 pTextNd->SetAttrListLevel(nLevel);
506 // #i91601#
507 else if (FN_UNO_LIST_ID == rEntry.nWID)
509 OUString sListId;
510 if (rValue >>= sListId)
512 pTextNd->SetListId( sListId );
515 else if (FN_UNO_IS_NUMBER == rEntry.nWID)
517 bool bIsNumber(false);
518 if ((rValue >>= bIsNumber) && !bIsNumber)
520 pTextNd->SetCountedInList( false );
523 else if (FN_UNO_PARA_NUM_AUTO_FORMAT == rEntry.nWID)
525 std::shared_ptr<SfxItemSet> pAutoStyle;
526 if (uno::Sequence<beans::NamedValue> props; rValue >>= props)
528 // 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?
529 SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE));
530 SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
531 SfxItemSetFixed
532 <RES_CHRATR_BEGIN, RES_CHRATR_END-1,
533 RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT,
534 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1>
535 items( rPam.GetDoc().GetAttrPool() );
537 for (beans::NamedValue const& prop : props)
539 SfxItemPropertyMapEntry const*const pEntry =
540 rMap.getByName(prop.Name);
541 if (!pEntry)
543 throw beans::UnknownPropertyException(
544 "Unknown property: " + prop.Name);
546 if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
548 throw beans::PropertyVetoException(
549 "Property is read-only: " + prop.Name);
551 if (prop.Name == "CharStyleName")
553 lcl_setCharStyle(rPam.GetDoc(), prop.Value, items);
555 else
557 SfxItemPropertySet::setPropertyValue(*pEntry, prop.Value, items);
561 IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
562 // Add it to the autostyle pool, needed by the ODT export.
563 pAutoStyle = rStyleAccess.getAutomaticStyle(items, IStyleAccess::AUTO_STYLE_CHAR);
565 else if (OUString styleName; rValue >>= styleName)
567 IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
568 pAutoStyle = rStyleAccess.getByName(styleName, IStyleAccess::AUTO_STYLE_CHAR);
570 if (pAutoStyle)
572 SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
573 // 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()
574 item.SetStyleHandle(pAutoStyle);
575 pTextNd->SetAttr(item);
578 //PROPERTY_MAYBEVOID!
580 break;
581 case FN_NUMBER_NEWSTART:
583 bool bVal = false;
584 if (!(rValue >>= bVal))
586 throw lang::IllegalArgumentException();
588 rPam.GetDoc().SetNumRuleStart(*rPam.GetPoint(), bVal);
590 break;
591 case FN_UNO_NUM_RULES:
592 SwUnoCursorHelper::setNumberingProperty(rValue, rPam);
593 break;
594 case RES_PARATR_DROP:
596 if (MID_DROPCAP_CHAR_STYLE_NAME == rEntry.nMemberId)
598 lcl_setDropcapCharStyle(rPam, rItemSet, rValue);
600 else
602 bRet = false;
605 break;
606 case RES_TXTATR_CJK_RUBY:
608 if (MID_RUBY_CHARSTYLE == rEntry.nMemberId)
610 lcl_setRubyCharstyle(rItemSet, rValue);
612 else
614 bRet = false;
617 break;
618 case RES_PAGEDESC:
620 if (MID_PAGEDESC_PAGEDESCNAME == rEntry.nMemberId)
622 SwUnoCursorHelper::SetPageDesc(
623 rValue, rPam.GetDoc(), rItemSet);
625 else
627 bRet = false;
630 break;
631 default:
632 bRet = false;
634 return bRet;
637 SwFormatColl *
638 SwUnoCursorHelper::GetCurTextFormatColl(SwPaM & rPaM, const bool bConditional)
640 static constexpr sal_Int32 nMaxLookup = 1000;
641 SwFormatColl *pFormat = nullptr;
642 bool bError = false;
643 SwPaM *pTmpCursor = &rPaM;
646 const SwNodeOffset nSttNd = pTmpCursor->Start()->GetNodeIndex();
647 const SwNodeOffset nEndNd = pTmpCursor->End()->GetNodeIndex();
649 if( nEndNd - nSttNd >= SwNodeOffset(nMaxLookup) )
651 pFormat = nullptr;
652 break;
655 const SwNodes& rNds = rPaM.GetDoc().GetNodes();
656 for( SwNodeOffset n = nSttNd; n <= nEndNd; ++n )
658 SwTextNode const*const pNd = rNds[ n ]->GetTextNode();
659 if( pNd )
661 SwFormatColl *const pNdFormat = bConditional
662 ? pNd->GetFormatColl() : &pNd->GetAnyFormatColl();
663 if( !pFormat )
665 pFormat = pNdFormat;
667 else if( pFormat != pNdFormat )
669 bError = true;
670 break;
675 pTmpCursor = pTmpCursor->GetNext();
676 } while ( pTmpCursor != &rPaM );
677 return bError ? nullptr : pFormat;
680 SwUnoCursor& SwXTextCursor::GetCursor()
681 { return *m_pUnoCursor; }
683 SwPaM const* SwXTextCursor::GetPaM() const
684 { return m_pUnoCursor.get(); }
686 SwPaM* SwXTextCursor::GetPaM()
687 { return m_pUnoCursor.get(); }
689 SwDoc const* SwXTextCursor::GetDoc() const
690 { return m_pUnoCursor ? &m_pUnoCursor->GetDoc() : nullptr; }
692 SwDoc* SwXTextCursor::GetDoc()
693 { return m_pUnoCursor ? &m_pUnoCursor->GetDoc() : nullptr; }
695 SwXTextCursor::SwXTextCursor(
696 SwDoc & rDoc,
697 uno::Reference< text::XText > xParent,
698 const CursorType eType,
699 const SwPosition& rPos,
700 SwPosition const*const pMark)
701 : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
702 , m_eType(eType)
703 , m_xParentText(std::move(xParent))
704 , m_pUnoCursor(rDoc.CreateUnoCursor(rPos))
706 if (pMark)
708 m_pUnoCursor->SetMark();
709 *m_pUnoCursor->GetMark() = *pMark;
713 SwXTextCursor::SwXTextCursor(uno::Reference< text::XText > xParent,
714 SwPaM const& rSourceCursor, const CursorType eType)
715 : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
716 , m_eType(eType)
717 , m_xParentText(std::move(xParent))
718 , m_pUnoCursor(rSourceCursor.GetDoc().CreateUnoCursor(*rSourceCursor.GetPoint()))
720 if (rSourceCursor.HasMark())
722 m_pUnoCursor->SetMark();
723 *m_pUnoCursor->GetMark() = *rSourceCursor.GetMark();
727 SwXTextCursor::~SwXTextCursor()
729 SolarMutexGuard g; // #i105557#: call dtor with locked solar mutex
730 m_pUnoCursor.reset(nullptr); // need to delete this with SolarMutex held
733 void SwXTextCursor::DeleteAndInsert(std::u16string_view aText,
734 ::sw::DeleteAndInsertMode const eMode)
736 auto pUnoCursor = static_cast<SwCursor*>(m_pUnoCursor.get());
737 if (!pUnoCursor)
738 return;
740 // Start/EndAction
741 SwDoc& rDoc = pUnoCursor->GetDoc();
742 UnoActionContext aAction(&rDoc);
743 const sal_Int32 nTextLen = aText.size();
744 rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
745 auto pCurrent = pUnoCursor;
748 if (pCurrent->HasMark())
750 rDoc.getIDocumentContentOperations().DeleteAndJoin(*pCurrent,
751 // is it "delete" or "replace"?
752 (nTextLen != 0 || eMode & ::sw::DeleteAndInsertMode::ForceReplace) ? SwDeleteFlags::ArtificialSelection : SwDeleteFlags::Default);
754 if(nTextLen)
756 // Store node and content indexes prior to insertion: to select the inserted text,
757 // we need to account for possible surrogate pairs, combining characters, etc.; it
758 // is easier to just restore the correct position from the indexes.
759 const auto start = pCurrent->Start();
760 const auto nodeIndex = start->GetNodeIndex();
761 const auto contentIndex = start->GetContentIndex();
762 const bool bSuccess(
763 SwUnoCursorHelper::DocInsertStringSplitCR(
764 rDoc, SwPaM(*start, pCurrent), aText, bool(eMode & ::sw::DeleteAndInsertMode::ForceExpandHints)));
765 OSL_ENSURE( bSuccess, "Doc->Insert(Str) failed." );
767 pCurrent->SetMark();
768 pCurrent->GetPoint()->Assign(nodeIndex, contentIndex);
770 pCurrent = pCurrent->GetNext();
771 } while (pCurrent != pUnoCursor);
772 rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
775 namespace {
777 enum ForceIntoMetaMode { META_CHECK_BOTH, META_INIT_START, META_INIT_END };
779 enum ForceIntoContentControlMode
781 CONTENT_CONTROL_CHECK_BOTH,
782 CONTENT_CONTROL_INIT_START,
783 CONTENT_CONTROL_INIT_END
787 static bool
788 lcl_ForceIntoMeta(SwPaM & rCursor,
789 uno::Reference<text::XText> const & xParentText,
790 const enum ForceIntoMetaMode eMode)
792 bool bRet( true ); // means not forced in META_CHECK_BOTH
793 SwXMeta const * const pXMeta( dynamic_cast<SwXMeta*>(xParentText.get()) );
794 OSL_ENSURE(pXMeta, "no parent?");
795 if (!pXMeta)
796 throw uno::RuntimeException();
797 SwTextNode * pTextNode;
798 sal_Int32 nStart;
799 sal_Int32 nEnd;
800 const bool bSuccess( pXMeta->SetContentRange(pTextNode, nStart, nEnd) );
801 OSL_ENSURE(bSuccess, "no pam?");
802 if (!bSuccess)
803 throw uno::RuntimeException();
804 // force the cursor back into the meta if it has moved outside
805 SwPosition start(*pTextNode, nStart);
806 SwPosition end(*pTextNode, nEnd);
807 switch (eMode)
809 case META_INIT_START:
810 *rCursor.GetPoint() = start;
811 break;
812 case META_INIT_END:
813 *rCursor.GetPoint() = end;
814 break;
815 case META_CHECK_BOTH:
816 if (*rCursor.Start() < start)
818 *rCursor.Start() = std::move(start);
819 bRet = false;
821 if (*rCursor.End() > end)
823 *rCursor.End() = std::move(end);
824 bRet = false;
826 break;
828 return bRet;
831 namespace
833 bool lcl_ForceIntoContentControl(SwPaM& rCursor, const uno::Reference<text::XText>& xParentText,
834 ForceIntoContentControlMode eMode)
836 bool bRet = true; // means not forced in CONTENT_CONTROL_CHECK_BOTH
837 auto pXContentControl = dynamic_cast<SwXContentControl*>(xParentText.get());
838 if (!pXContentControl)
840 SAL_WARN("sw.core", "lcl_ForceIntoContentControl: no parent text");
841 throw uno::RuntimeException();
844 SwTextNode* pTextNode;
845 sal_Int32 nStart;
846 sal_Int32 nEnd;
847 bool bSuccess = pXContentControl->SetContentRange(pTextNode, nStart, nEnd);
848 if (!bSuccess)
850 SAL_WARN("sw.core", "lcl_ForceIntoContentControl: SetContentRange() failed");
851 throw uno::RuntimeException();
854 // Force the cursor back into the content control if it has moved outside.
855 SwPosition aStart(*pTextNode, nStart);
856 SwPosition aEnd(*pTextNode, nEnd);
857 switch (eMode)
859 case CONTENT_CONTROL_INIT_START:
860 *rCursor.GetPoint() = aStart;
861 break;
863 case CONTENT_CONTROL_INIT_END:
864 *rCursor.GetPoint() = aEnd;
865 break;
867 case CONTENT_CONTROL_CHECK_BOTH:
868 if (*rCursor.Start() < aStart)
870 *rCursor.Start() = std::move(aStart);
871 bRet = false;
874 if (*rCursor.End() > aEnd)
876 *rCursor.End() = std::move(aEnd);
877 bRet = false;
879 break;
882 return bRet;
886 bool SwXTextCursor::IsAtEndOfMeta() const
888 if (CursorType::Meta == m_eType)
890 sw::UnoCursorPointer pCursor( m_pUnoCursor );
891 SwXMeta const*const pXMeta(
892 dynamic_cast<SwXMeta*>(m_xParentText.get()) );
893 OSL_ENSURE(pXMeta, "no meta?");
894 if (pCursor && pXMeta)
896 SwTextNode * pTextNode;
897 sal_Int32 nStart;
898 sal_Int32 nEnd;
899 const bool bSuccess(
900 pXMeta->SetContentRange(pTextNode, nStart, nEnd) );
901 OSL_ENSURE(bSuccess, "no pam?");
902 if (bSuccess)
904 const SwPosition end(*pTextNode, nEnd);
905 if ( (*pCursor->GetPoint() == end)
906 || (*pCursor->GetMark() == end))
908 return true;
913 return false;
916 bool SwXTextCursor::IsAtEndOfContentControl() const
918 if (CursorType::ContentControl == m_eType)
920 sw::UnoCursorPointer pCursor( m_pUnoCursor );
921 auto pXContentControl(
922 dynamic_cast<SwXContentControl*>(m_xParentText.get()) );
923 if (!pXContentControl)
925 SAL_WARN("sw.core", "SwXTextCursor::IsAtEndOfContentControl: no content control");
927 if (pCursor && pXContentControl)
929 SwTextNode * pTextNode;
930 sal_Int32 nStart;
931 sal_Int32 nEnd;
932 const bool bSuccess(
933 pXContentControl->SetContentRange(pTextNode, nStart, nEnd) );
934 if (!bSuccess)
936 SAL_WARN("sw.core", "SwXTextCursor::IsAtEndOfContentControl: no pam");
938 else
940 const SwPosition end(*pTextNode, nEnd);
941 if ( (*pCursor->GetPoint() == end)
942 || (*pCursor->GetMark() == end))
944 return true;
949 return false;
952 OUString SwXTextCursor::getImplementationName()
954 return u"SwXTextCursor"_ustr;
957 sal_Bool SAL_CALL SwXTextCursor::supportsService(const OUString& rServiceName)
959 return cppu::supportsService(this, rServiceName);
962 uno::Sequence< OUString > SAL_CALL
963 SwXTextCursor::getSupportedServiceNames()
965 return {
966 u"com.sun.star.text.TextCursor"_ustr,
967 u"com.sun.star.style.CharacterProperties"_ustr,
968 u"com.sun.star.style.CharacterPropertiesAsian"_ustr,
969 u"com.sun.star.style.CharacterPropertiesComplex"_ustr,
970 u"com.sun.star.style.ParagraphProperties"_ustr,
971 u"com.sun.star.style.ParagraphPropertiesAsian"_ustr,
972 u"com.sun.star.style.ParagraphPropertiesComplex"_ustr,
973 u"com.sun.star.text.TextSortable"_ustr
977 void SAL_CALL SwXTextCursor::collapseToStart()
979 SolarMutexGuard aGuard;
981 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
983 if (rUnoCursor.HasMark())
985 if (*rUnoCursor.GetPoint() > *rUnoCursor.GetMark())
987 rUnoCursor.Exchange();
989 rUnoCursor.DeleteMark();
993 void SAL_CALL SwXTextCursor::collapseToEnd()
995 SolarMutexGuard aGuard;
997 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
999 if (rUnoCursor.HasMark())
1001 if (*rUnoCursor.GetPoint() < *rUnoCursor.GetMark())
1003 rUnoCursor.Exchange();
1005 rUnoCursor.DeleteMark();
1009 sal_Bool SAL_CALL SwXTextCursor::isCollapsed()
1011 SolarMutexGuard aGuard;
1013 bool bRet = true;
1014 sw::UnoCursorPointer pUnoCursor(m_pUnoCursor);
1015 if(pUnoCursor && pUnoCursor->GetMark())
1017 bRet = (*pUnoCursor->GetPoint() == *pUnoCursor->GetMark());
1019 return bRet;
1022 sal_Bool SAL_CALL
1023 SwXTextCursor::goLeft(sal_Int16 nCount, sal_Bool Expand)
1025 SolarMutexGuard aGuard;
1027 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1029 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1030 bool bRet = rUnoCursor.Left( nCount);
1031 if (CursorType::Meta == m_eType)
1033 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1034 META_CHECK_BOTH)
1035 && bRet;
1037 else if (m_eType == CursorType::ContentControl)
1039 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1040 && bRet;
1042 return bRet;
1045 sal_Bool SAL_CALL
1046 SwXTextCursor::goRight(sal_Int16 nCount, sal_Bool Expand)
1048 SolarMutexGuard aGuard;
1050 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1052 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1053 bool bRet = rUnoCursor.Right(nCount);
1054 if (CursorType::Meta == m_eType)
1056 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1057 META_CHECK_BOTH)
1058 && bRet;
1060 else if (m_eType == CursorType::ContentControl)
1062 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1063 && bRet;
1065 return bRet;
1068 void SAL_CALL
1069 SwXTextCursor::gotoStart(sal_Bool Expand)
1071 SolarMutexGuard aGuard;
1072 comphelper::ProfileZone aZone("gotoStart");
1074 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1076 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1077 if (CursorType::Body == m_eType)
1079 rUnoCursor.Move( fnMoveBackward, GoInDoc );
1080 //check, that the cursor is not in a table
1081 SwTableNode * pTableNode = rUnoCursor.GetPointNode().FindTableNode();
1082 while (pTableNode)
1084 rUnoCursor.GetPoint()->Assign( *pTableNode->EndOfSectionNode() );
1085 SwContentNode* pCNode = SwNodes::GoNext(rUnoCursor.GetPoint());
1086 pTableNode = pCNode ? pCNode->FindTableNode() : nullptr;
1088 SwStartNode const*const pTmp =
1089 rUnoCursor.GetPointNode().StartOfSectionNode();
1090 if (pTmp->IsSectionNode())
1092 SwSectionNode const*const pSectionStartNode =
1093 static_cast<SwSectionNode const*>(pTmp);
1094 if (pSectionStartNode->GetSection().IsHiddenFlag())
1096 SwNodes::GoNextSection(
1097 rUnoCursor.GetPoint(), true, false);
1101 else if ( (CursorType::Frame == m_eType)
1102 || (CursorType::TableText == m_eType)
1103 || (CursorType::Header == m_eType)
1104 || (CursorType::Footer == m_eType)
1105 || (CursorType::Footnote== m_eType)
1106 || (CursorType::Redline == m_eType))
1108 rUnoCursor.MoveSection(GoCurrSection, fnSectionStart);
1110 else if (CursorType::Meta == m_eType)
1112 lcl_ForceIntoMeta(rUnoCursor, m_xParentText, META_INIT_START);
1114 else if (m_eType == CursorType::ContentControl)
1116 lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_INIT_START);
1120 void SAL_CALL
1121 SwXTextCursor::gotoEnd(sal_Bool Expand)
1123 SolarMutexGuard aGuard;
1124 comphelper::ProfileZone aZone("gotoEnd");
1126 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1128 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1129 if (CursorType::Body == m_eType)
1131 rUnoCursor.Move( fnMoveForward, GoInDoc );
1133 else if ( (CursorType::Frame == m_eType)
1134 || (CursorType::TableText == m_eType)
1135 || (CursorType::Header == m_eType)
1136 || (CursorType::Footer == m_eType)
1137 || (CursorType::Footnote== m_eType)
1138 || (CursorType::Redline == m_eType))
1140 rUnoCursor.MoveSection( GoCurrSection, fnSectionEnd);
1142 else if (CursorType::Meta == m_eType)
1144 lcl_ForceIntoMeta(rUnoCursor, m_xParentText, META_INIT_END);
1146 else if (m_eType == CursorType::ContentControl)
1148 lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_INIT_END);
1152 void SAL_CALL
1153 SwXTextCursor::gotoRange(
1154 const uno::Reference< text::XTextRange > & xRange, sal_Bool bExpand)
1156 SolarMutexGuard aGuard;
1157 if (!xRange.is())
1159 throw uno::RuntimeException();
1161 SwXTextRange* pRange = dynamic_cast<SwXTextRange*>(xRange.get());
1162 OTextCursorHelper* pCursor = dynamic_cast<OTextCursorHelper*>(xRange.get());
1163 if (!pRange && !pCursor)
1165 throw uno::RuntimeException();
1168 gotoRangeImpl(pRange, pCursor, bExpand);
1171 void
1172 SwXTextCursor::gotoRangeImpl(
1173 SwXTextRange* pRange,
1174 OTextCursorHelper* pCursor,
1175 bool bExpand)
1177 DBG_TESTSOLARMUTEX();
1178 assert((pRange || pCursor) && "one of these parameters must be non-null");
1180 SwUnoCursor & rOwnCursor( GetCursorOrThrow() );
1182 SwPaM aPam(GetDoc()->GetNodes());
1183 const SwPaM * pPam(nullptr);
1184 if (pCursor)
1186 pPam = pCursor->GetPaM();
1188 else
1190 if (pRange->GetPositions(aPam))
1192 pPam = & aPam;
1196 if (!pPam)
1198 throw uno::RuntimeException();
1202 SwStartNodeType eSearchNodeType = SwNormalStartNode;
1203 switch (m_eType)
1205 case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break;
1206 case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break;
1207 case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break;
1208 case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break;
1209 case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break;
1210 //case CURSOR_INVALID:
1211 //case CursorType::Body:
1212 default:
1216 const SwStartNode* pOwnStartNode = rOwnCursor.GetPointNode().FindStartNodeByType(eSearchNodeType);
1217 while ( pOwnStartNode != nullptr
1218 && pOwnStartNode->IsSectionNode())
1220 pOwnStartNode = pOwnStartNode->StartOfSectionNode();
1223 const SwStartNode* pTmp =
1224 pPam->GetPointNode().FindStartNodeByType(eSearchNodeType);
1225 while ( pTmp != nullptr
1226 && pTmp->IsSectionNode() )
1228 pTmp = pTmp->StartOfSectionNode();
1231 if ( eSearchNodeType == SwTableBoxStartNode )
1233 if (!pOwnStartNode || !pTmp)
1235 throw uno::RuntimeException();
1238 if ( pOwnStartNode->FindTableNode() != pTmp->FindTableNode() )
1240 throw uno::RuntimeException();
1243 else
1245 if ( pOwnStartNode != pTmp )
1247 throw uno::RuntimeException();
1252 if (CursorType::Meta == m_eType)
1254 SwPaM CopyPam(*pPam->GetMark(), *pPam->GetPoint());
1255 const bool bNotForced( lcl_ForceIntoMeta(
1256 CopyPam, m_xParentText, META_CHECK_BOTH) );
1257 if (!bNotForced)
1259 throw uno::RuntimeException(
1260 u"gotoRange: parameter range not contained in nesting"
1261 " text content for which this cursor was created"_ustr,
1262 static_cast<text::XWordCursor*>(this));
1265 else if (m_eType == CursorType::ContentControl)
1267 SwPaM aPaM(*pPam->GetMark(), *pPam->GetPoint());
1268 if (!lcl_ForceIntoContentControl(aPaM, m_xParentText, CONTENT_CONTROL_CHECK_BOTH))
1270 throw uno::RuntimeException(u"gotoRange: xRange is out of bounds of the content control"_ustr,
1271 static_cast<text::XWordCursor*>(this));
1275 // selection has to be expanded here
1276 if(bExpand)
1278 // cursor should include its previous range plus the given range
1279 const SwPosition aOwnLeft(*rOwnCursor.Start());
1280 const SwPosition aOwnRight(*rOwnCursor.End());
1281 SwPosition const& rParamLeft = *pPam->Start();
1282 SwPosition const& rParamRight = *pPam->End();
1284 // now there are four SwPositions,
1285 // two of them are going to be used, but which ones?
1286 if (aOwnRight > rParamRight)
1287 *rOwnCursor.GetPoint() = aOwnRight;
1288 else
1289 *rOwnCursor.GetPoint() = rParamRight;
1290 rOwnCursor.SetMark();
1291 if (aOwnLeft < rParamLeft)
1292 *rOwnCursor.GetMark() = aOwnLeft;
1293 else
1294 *rOwnCursor.GetMark() = rParamLeft;
1296 else
1298 // cursor should be the given range
1299 *rOwnCursor.GetPoint() = *pPam->GetPoint();
1300 if (pPam->HasMark())
1302 rOwnCursor.SetMark();
1303 *rOwnCursor.GetMark() = *pPam->GetMark();
1305 else
1307 rOwnCursor.DeleteMark();
1312 sal_Bool SAL_CALL SwXTextCursor::isStartOfWord()
1314 SolarMutexGuard aGuard;
1316 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1318 const bool bRet =
1319 rUnoCursor.IsStartWordWT( i18n::WordType::DICTIONARY_WORD );
1320 return bRet;
1323 sal_Bool SAL_CALL SwXTextCursor::isEndOfWord()
1325 SolarMutexGuard aGuard;
1327 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1329 const bool bRet =
1330 rUnoCursor.IsEndWordWT( i18n::WordType::DICTIONARY_WORD );
1331 return bRet;
1334 sal_Bool SAL_CALL
1335 SwXTextCursor::gotoNextWord(sal_Bool Expand)
1337 SolarMutexGuard aGuard;
1339 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1341 // problems arise when a paragraph starts with something other than a word
1342 bool bRet = false;
1343 // remember old position to check if cursor has moved
1344 // since the called functions are sometimes a bit unreliable
1345 // in specific cases...
1346 SwPosition *const pPoint = rUnoCursor.GetPoint();
1347 SwNode *const pOldNode = &pPoint->GetNode();
1348 sal_Int32 const nOldIndex = pPoint->GetContentIndex();
1350 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1351 // end of paragraph
1352 if (rUnoCursor.GetPointContentNode() &&
1353 (pPoint->GetContentIndex() == rUnoCursor.GetPointContentNode()->Len()))
1355 rUnoCursor.Right(1);
1357 else
1359 const bool bTmp =
1360 rUnoCursor.GoNextWordWT( i18n::WordType::DICTIONARY_WORD );
1361 // if there is no next word within the current paragraph
1362 // try to go to the start of the next paragraph
1363 if (!bTmp)
1365 rUnoCursor.MovePara(GoNextPara, fnParaStart);
1369 // return true if cursor has moved
1370 bRet = (&pPoint->GetNode() != pOldNode) ||
1371 (pPoint->GetContentIndex() != nOldIndex);
1372 if (bRet && (CursorType::Meta == m_eType))
1374 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1375 META_CHECK_BOTH);
1377 else if (bRet && m_eType == CursorType::ContentControl)
1379 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
1382 return bRet;
1385 sal_Bool SAL_CALL
1386 SwXTextCursor::gotoPreviousWord(sal_Bool Expand)
1388 SolarMutexGuard aGuard;
1390 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1392 // white spaces create problems on the paragraph start
1393 bool bRet = false;
1394 SwPosition *const pPoint = rUnoCursor.GetPoint();
1395 SwNode *const pOldNode = &pPoint->GetNode();
1396 sal_Int32 const nOldIndex = pPoint->GetContentIndex();
1398 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1399 // start of paragraph?
1400 if (pPoint->GetContentIndex() == 0)
1402 rUnoCursor.Left(1);
1404 else
1406 rUnoCursor.GoPrevWordWT( i18n::WordType::DICTIONARY_WORD );
1407 if (pPoint->GetContentIndex() == 0)
1409 rUnoCursor.Left(1);
1413 // return true if cursor has moved
1414 bRet = (&pPoint->GetNode() != pOldNode) ||
1415 (pPoint->GetContentIndex() != nOldIndex);
1416 if (bRet && (CursorType::Meta == m_eType))
1418 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1419 META_CHECK_BOTH);
1421 else if (bRet && m_eType == CursorType::ContentControl)
1423 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
1426 return bRet;
1429 sal_Bool SAL_CALL
1430 SwXTextCursor::gotoEndOfWord(sal_Bool Expand)
1432 SolarMutexGuard aGuard;
1434 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1436 bool bRet = false;
1437 SwPosition *const pPoint = rUnoCursor.GetPoint();
1438 SwNode & rOldNode = pPoint->GetNode();
1439 sal_Int32 const nOldIndex = pPoint->GetContentIndex();
1441 const sal_Int16 nWordType = i18n::WordType::DICTIONARY_WORD;
1442 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1443 if (!rUnoCursor.IsEndWordWT( nWordType ))
1445 rUnoCursor.GoEndWordWT( nWordType );
1448 // restore old cursor if we are not at the end of a word by now
1449 // otherwise use current one
1450 bRet = rUnoCursor.IsEndWordWT( nWordType );
1451 if (!bRet)
1453 pPoint->Assign(rOldNode, nOldIndex);
1455 else if (CursorType::Meta == m_eType)
1457 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1458 META_CHECK_BOTH);
1460 else if (m_eType == CursorType::ContentControl)
1462 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
1465 return bRet;
1468 sal_Bool SAL_CALL
1469 SwXTextCursor::gotoStartOfWord(sal_Bool Expand)
1471 SolarMutexGuard aGuard;
1473 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1475 bool bRet = false;
1476 SwPosition *const pPoint = rUnoCursor.GetPoint();
1477 SwNode & rOldNode = pPoint->GetNode();
1478 sal_Int32 const nOldIndex = pPoint->GetContentIndex();
1480 const sal_Int16 nWordType = i18n::WordType::DICTIONARY_WORD;
1481 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1482 if (!rUnoCursor.IsStartWordWT( nWordType ))
1484 rUnoCursor.GoStartWordWT( nWordType );
1487 // restore old cursor if we are not at the start of a word by now
1488 // otherwise use current one
1489 bRet = rUnoCursor.IsStartWordWT( nWordType );
1490 if (!bRet)
1492 pPoint->Assign(rOldNode, nOldIndex);
1494 else if (CursorType::Meta == m_eType)
1496 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1497 META_CHECK_BOTH);
1499 else if (m_eType == CursorType::ContentControl)
1501 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH);
1504 return bRet;
1507 sal_Bool SAL_CALL
1508 SwXTextCursor::isStartOfSentence()
1510 SolarMutexGuard aGuard;
1512 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1514 // start of paragraph?
1515 bool bRet = rUnoCursor.GetPoint()->GetContentIndex() == 0;
1516 // with mark ->no sentence start
1517 // (check if cursor is no selection, i.e. it does not have
1518 // a mark or else point and mark are identical)
1519 if (!bRet && (!rUnoCursor.HasMark() ||
1520 *rUnoCursor.GetPoint() == *rUnoCursor.GetMark()))
1522 SwCursor aCursor(*rUnoCursor.GetPoint(),nullptr);
1523 SwPosition aOrigPos = *aCursor.GetPoint();
1524 aCursor.GoSentence(SwCursor::START_SENT );
1525 bRet = aOrigPos == *aCursor.GetPoint();
1527 return bRet;
1530 sal_Bool SAL_CALL
1531 SwXTextCursor::isEndOfSentence()
1533 SolarMutexGuard aGuard;
1535 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1537 // end of paragraph?
1538 bool bRet = rUnoCursor.GetPointContentNode() &&
1539 (rUnoCursor.GetPoint()->GetContentIndex() == rUnoCursor.GetPointContentNode()->Len());
1540 // with mark->no sentence end
1541 // (check if cursor is no selection, i.e. it does not have
1542 // a mark or else point and mark are identical)
1543 if (!bRet && (!rUnoCursor.HasMark() ||
1544 *rUnoCursor.GetPoint() == *rUnoCursor.GetMark()))
1546 SwCursor aCursor(*rUnoCursor.GetPoint(), nullptr);
1547 SwPosition aOrigPos = *aCursor.GetPoint();
1548 aCursor.GoSentence(SwCursor::END_SENT);
1549 bRet = aOrigPos == *aCursor.GetPoint();
1551 return bRet;
1554 sal_Bool SAL_CALL
1555 SwXTextCursor::gotoNextSentence(sal_Bool Expand)
1557 SolarMutexGuard aGuard;
1559 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1561 const bool bWasEOS = isEndOfSentence();
1562 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1563 bool bRet = rUnoCursor.GoSentence(SwCursor::NEXT_SENT);
1564 if (!bRet)
1566 bRet = rUnoCursor.MovePara(GoNextPara, fnParaStart);
1569 // if at the end of the sentence (i.e. at the space after the '.')
1570 // advance to next word in order for GoSentence to work properly
1571 // next time and have isStartOfSentence return true after this call
1572 if (!rUnoCursor.IsStartWordWT(css::i18n::WordType::ANYWORD_IGNOREWHITESPACES))
1574 const bool bNextWord = rUnoCursor.GoNextWordWT(i18n::WordType::ANYWORD_IGNOREWHITESPACES);
1575 if (bWasEOS && !bNextWord)
1577 bRet = false;
1580 if (CursorType::Meta == m_eType)
1582 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1583 META_CHECK_BOTH)
1584 && bRet;
1586 else if (m_eType == CursorType::ContentControl)
1588 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1589 && bRet;
1591 return bRet;
1594 sal_Bool SAL_CALL
1595 SwXTextCursor::gotoPreviousSentence(sal_Bool Expand)
1597 SolarMutexGuard aGuard;
1599 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1601 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1602 bool bRet = rUnoCursor.GoSentence(SwCursor::PREV_SENT);
1603 if (!bRet)
1605 bRet = rUnoCursor.MovePara(GoPrevPara, fnParaStart);
1606 if (bRet)
1608 rUnoCursor.MovePara(GoCurrPara, fnParaEnd);
1609 // at the end of a paragraph move to the sentence end again
1610 rUnoCursor.GoSentence(SwCursor::PREV_SENT);
1613 if (CursorType::Meta == m_eType)
1615 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1616 META_CHECK_BOTH)
1617 && bRet;
1619 else if (m_eType == CursorType::ContentControl)
1621 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1622 && bRet;
1624 return bRet;
1627 sal_Bool SAL_CALL
1628 SwXTextCursor::gotoStartOfSentence(sal_Bool Expand)
1630 SolarMutexGuard aGuard;
1632 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1634 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1635 // if we're at the para start then we won't move
1636 // but bRet is also true if GoSentence failed but
1637 // the start of the sentence is reached
1638 bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor)
1639 || rUnoCursor.GoSentence(SwCursor::START_SENT)
1640 || SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
1641 if (CursorType::Meta == m_eType)
1643 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1644 META_CHECK_BOTH)
1645 && bRet;
1647 else if (m_eType == CursorType::ContentControl)
1649 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1650 && bRet;
1652 return bRet;
1655 sal_Bool SAL_CALL
1656 SwXTextCursor::gotoEndOfSentence(sal_Bool Expand)
1658 SolarMutexGuard aGuard;
1660 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1662 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1663 // bRet is true if GoSentence() succeeded or if the
1664 // MovePara() succeeded while the end of the para is
1665 // not reached already
1666 bool bAlreadyParaEnd = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
1667 bool bRet = !bAlreadyParaEnd
1668 && (rUnoCursor.GoSentence(SwCursor::END_SENT)
1669 || rUnoCursor.MovePara(GoCurrPara, fnParaEnd));
1670 if (CursorType::Meta == m_eType)
1672 bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
1673 META_CHECK_BOTH)
1674 && bRet;
1676 else if (m_eType == CursorType::ContentControl)
1678 bRet = lcl_ForceIntoContentControl(rUnoCursor, m_xParentText, CONTENT_CONTROL_CHECK_BOTH)
1679 && bRet;
1681 return bRet;
1684 sal_Bool SAL_CALL
1685 SwXTextCursor::isStartOfParagraph()
1687 SolarMutexGuard aGuard;
1689 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1691 const bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
1692 return bRet;
1695 sal_Bool SAL_CALL
1696 SwXTextCursor::isEndOfParagraph()
1698 SolarMutexGuard aGuard;
1700 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1702 const bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
1703 return bRet;
1706 sal_Bool SAL_CALL
1707 SwXTextCursor::gotoStartOfParagraph(sal_Bool Expand)
1709 SolarMutexGuard aGuard;
1711 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1713 if (CursorType::Meta == m_eType)
1715 return false;
1717 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1718 bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
1719 if (!bRet)
1721 bRet = rUnoCursor.MovePara(GoCurrPara, fnParaStart);
1724 // since MovePara(GoCurrPara, fnParaStart) only returns false
1725 // if we were already at the start of the paragraph this function
1726 // should always complete successfully.
1727 OSL_ENSURE( bRet, "gotoStartOfParagraph failed" );
1728 return bRet;
1731 sal_Bool SAL_CALL
1732 SwXTextCursor::gotoEndOfParagraph(sal_Bool Expand)
1734 SolarMutexGuard aGuard;
1736 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1738 if (CursorType::Meta == m_eType)
1740 return false;
1742 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1743 bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
1744 if (!bRet)
1746 bRet = rUnoCursor.MovePara(GoCurrPara, fnParaEnd);
1749 // since MovePara(GoCurrPara, fnParaEnd) only returns false
1750 // if we were already at the end of the paragraph this function
1751 // should always complete successfully.
1752 OSL_ENSURE( bRet, "gotoEndOfParagraph failed" );
1753 return bRet;
1756 sal_Bool SAL_CALL
1757 SwXTextCursor::gotoNextParagraph(sal_Bool Expand)
1759 SolarMutexGuard aGuard;
1761 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1763 if (CursorType::Meta == m_eType)
1765 return false;
1767 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1768 const bool bRet = rUnoCursor.MovePara(GoNextPara, fnParaStart);
1769 return bRet;
1772 sal_Bool SAL_CALL
1773 SwXTextCursor::gotoPreviousParagraph(sal_Bool Expand)
1775 SolarMutexGuard aGuard;
1777 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1779 if (CursorType::Meta == m_eType)
1781 return false;
1783 SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
1784 const bool bRet = rUnoCursor.MovePara(GoPrevPara, fnParaStart);
1785 return bRet;
1788 uno::Reference< text::XText > SAL_CALL
1789 SwXTextCursor::getText()
1791 SolarMutexGuard g;
1793 return m_xParentText;
1796 uno::Reference< text::XTextRange > SAL_CALL
1797 SwXTextCursor::getStart()
1799 SolarMutexGuard aGuard;
1801 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1803 uno::Reference< text::XTextRange > xRet;
1804 SwPaM aPam(*rUnoCursor.Start());
1805 const uno::Reference< text::XText > xParent = getText();
1806 if (CursorType::Meta == m_eType)
1808 // return cursor to prevent modifying SwXTextRange for META
1809 rtl::Reference<SwXTextCursor> pXCursor(
1810 new SwXTextCursor(rUnoCursor.GetDoc(), xParent, CursorType::Meta,
1811 *rUnoCursor.GetPoint()) );
1812 pXCursor->gotoStart(false);
1813 xRet = static_cast<text::XWordCursor*>(pXCursor.get());
1815 else
1817 xRet = new SwXTextRange(aPam, xParent);
1819 return xRet;
1822 uno::Reference< text::XTextRange > SAL_CALL
1823 SwXTextCursor::getEnd()
1825 SolarMutexGuard aGuard;
1827 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1829 uno::Reference< text::XTextRange > xRet;
1830 SwPaM aPam(*rUnoCursor.End());
1831 const uno::Reference< text::XText > xParent = getText();
1832 if (CursorType::Meta == m_eType)
1834 // return cursor to prevent modifying SwXTextRange for META
1835 rtl::Reference<SwXTextCursor> pXCursor(
1836 new SwXTextCursor(rUnoCursor.GetDoc(), xParent, CursorType::Meta,
1837 *rUnoCursor.GetPoint()) );
1838 pXCursor->gotoEnd(false);
1839 xRet = static_cast<text::XWordCursor*>(pXCursor.get());
1841 else
1843 xRet = new SwXTextRange(aPam, xParent);
1845 return xRet;
1848 OUString SAL_CALL SwXTextCursor::getString()
1850 SolarMutexGuard aGuard;
1852 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
1854 OUString aText;
1855 SwUnoCursorHelper::GetTextFromPam(rUnoCursor, aText);
1856 return aText;
1859 void SAL_CALL
1860 SwXTextCursor::setString(const OUString& aString)
1862 SolarMutexGuard aGuard;
1864 GetCursorOrThrow(); // just to check if valid
1866 const bool bForceExpandHints( (CursorType::Meta == m_eType)
1867 && dynamic_cast<SwXMeta&>(*m_xParentText)
1868 .CheckForOwnMemberMeta(*GetPaM(), true) );
1869 DeleteAndInsert(aString, bForceExpandHints ? ::sw::DeleteAndInsertMode::ForceExpandHints : ::sw::DeleteAndInsertMode::Default);
1872 uno::Any SwUnoCursorHelper::GetPropertyValue(
1873 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
1874 std::u16string_view rPropertyName)
1876 uno::Any aAny;
1877 SfxItemPropertyMapEntry const*const pEntry =
1878 rPropSet.getPropertyMap().getByName(rPropertyName);
1880 if (!pEntry)
1882 throw beans::UnknownPropertyException(
1883 OUString::Concat("Unknown property: ") + rPropertyName);
1886 beans::PropertyState eTemp;
1887 const bool bDone = SwUnoCursorHelper::getCursorPropertyValue(
1888 *pEntry, rPaM, &aAny, eTemp );
1890 if (!bDone)
1892 SfxItemSetFixed<
1893 RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
1894 RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>
1895 aSet(rPaM.GetDoc().GetAttrPool());
1897 SwUnoCursorHelper::GetCursorAttr(rPaM, aSet);
1899 SfxItemPropertySet::getPropertyValue(*pEntry, aSet, aAny);
1902 return aAny;
1905 void SwUnoCursorHelper::SetPropertyValue(
1906 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
1907 const OUString& rPropertyName,
1908 const uno::Any& rValue,
1909 const SetAttrMode nAttrMode)
1911 beans::PropertyValue aVal { comphelper::makePropertyValue(rPropertyName, rValue) };
1912 SetPropertyValues(rPaM, rPropSet, std::span<beans::PropertyValue>(&aVal, 1), nAttrMode);
1915 // FN_UNO_PARA_STYLE is known to set attributes for nodes, inside
1916 // SwUnoCursorHelper::SetTextFormatColl, instead of extending item set.
1917 // We need to get them from nodes in next call to GetCursorAttr.
1918 // The rest could cause similar problems in theory, so we just list them here.
1919 static bool propertyCausesSideEffectsInNodes(sal_uInt16 nWID)
1921 return nWID == FN_UNO_PARA_STYLE ||
1922 nWID == FN_UNO_CHARFMT_SEQUENCE ||
1923 nWID == FN_UNO_NUM_START_VALUE ||
1924 nWID == FN_UNO_NUM_RULES;
1927 void SwUnoCursorHelper::SetPropertyValues(
1928 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
1929 const uno::Sequence< beans::PropertyValue > &rPropertyValues,
1930 const SetAttrMode nAttrMode)
1932 SetPropertyValues(rPaM, rPropSet,
1933 std::span<const beans::PropertyValue>(rPropertyValues.getConstArray(), rPropertyValues.getLength()),
1934 nAttrMode);
1937 void SwUnoCursorHelper::SetPropertyValues(
1938 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
1939 std::span< const beans::PropertyValue > aPropertyValues,
1940 const SetAttrMode nAttrMode)
1942 if (aPropertyValues.empty())
1943 return;
1945 SwDoc& rDoc = rPaM.GetDoc();
1946 OUString aUnknownExMsg, aPropertyVetoExMsg;
1948 // Build set of attributes we want to fetch
1949 WhichRangesContainer aRanges;
1950 std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aSideEffectsEntries;
1951 std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aEntries;
1952 aEntries.reserve(aPropertyValues.size());
1953 for (const auto& rPropVal : aPropertyValues)
1955 const OUString &rPropertyName = rPropVal.Name;
1957 SfxItemPropertyMapEntry const* pEntry =
1958 rPropSet.getPropertyMap().getByName(rPropertyName);
1960 // Queue up any exceptions until the end ...
1961 if (!pEntry)
1963 aUnknownExMsg += "Unknown property: '" + rPropertyName + "' ";
1964 continue;
1966 else if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
1968 aPropertyVetoExMsg += "Property is read-only: '" + rPropertyName + "' ";
1969 continue;
1971 if (propertyCausesSideEffectsInNodes(pEntry->nWID))
1973 aSideEffectsEntries.emplace_back(pEntry, rPropVal.Value);
1975 else
1977 aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID);
1978 aEntries.emplace_back(pEntry, rPropVal.Value);
1982 // Entries with side effects first, using dedicated one-element SfxItemSet for each
1983 for (const auto& [pEntry, rValue] : aSideEffectsEntries)
1985 SfxItemSet aItemSet(rDoc.GetAttrPool(), pEntry->nWID, pEntry->nWID);
1986 // we need to get up-to-date item set from nodes
1987 SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet);
1988 // this can set some attributes in nodes' mpAttrSet
1989 if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet))
1990 SfxItemPropertySet::setPropertyValue(*pEntry, rValue, aItemSet);
1991 SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false /*bTableMode*/);
1994 if (!aEntries.empty())
1996 // Fetch, overwrite, and re-set the attributes from the core
1997 SfxItemSet aItemSet(rDoc.GetAttrPool(), std::move(aRanges));
1998 // we need to get up-to-date item set from nodes
1999 SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet);
2001 for (const auto& [pEntry, rValue] : aEntries)
2003 // this can set some attributes in nodes' mpAttrSet
2004 if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet))
2005 SfxItemPropertySet::setPropertyValue(*pEntry, rValue, aItemSet);
2008 SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false /*bTableMode*/);
2011 if (!aUnknownExMsg.isEmpty())
2012 throw beans::UnknownPropertyException(aUnknownExMsg);
2013 if (!aPropertyVetoExMsg.isEmpty())
2014 throw beans::PropertyVetoException(aPropertyVetoExMsg);
2017 namespace
2019 bool NotInRange(sal_uInt16 nWID, sal_uInt16 nStart, sal_uInt16 nEnd)
2021 return nWID < nStart || nWID > nEnd;
2025 uno::Sequence< beans::PropertyState >
2026 SwUnoCursorHelper::GetPropertyStates(
2027 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
2028 const uno::Sequence< OUString >& rPropertyNames,
2029 const SwGetPropertyStatesCaller eCaller)
2031 const OUString* pNames = rPropertyNames.getConstArray();
2032 uno::Sequence< beans::PropertyState > aRet(rPropertyNames.getLength());
2033 beans::PropertyState* pStates = aRet.getArray();
2034 const SfxItemPropertyMap &rMap = rPropSet.getPropertyMap();
2035 std::optional<SfxItemSet> oSet;
2036 std::optional<SfxItemSet> oSetParent;
2038 for (sal_Int32 i = 0, nEnd = rPropertyNames.getLength(); i < nEnd; i++)
2040 SfxItemPropertyMapEntry const*const pEntry =
2041 rMap.getByName( pNames[i] );
2042 if(!pEntry)
2044 if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
2045 pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
2046 pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
2048 pStates[i] = beans::PropertyState_DEFAULT_VALUE;
2049 continue;
2051 else if (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT ==
2052 eCaller)
2054 //this values marks the element as unknown property
2055 pStates[i] = beans::PropertyState::PropertyState_MAKE_FIXED_SIZE;
2056 continue;
2058 else
2060 throw beans::UnknownPropertyException(
2061 "Unknown property: " + pNames[i]);
2064 if (((SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION == eCaller) ||
2065 (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT == eCaller)) &&
2066 NotInRange(pEntry->nWID, FN_UNO_RANGE_BEGIN, FN_UNO_RANGE_END) &&
2067 NotInRange(pEntry->nWID, RES_CHRATR_BEGIN, RES_TXTATR_END) )
2069 pStates[i] = beans::PropertyState_DEFAULT_VALUE;
2071 else
2073 if ( pEntry->nWID >= FN_UNO_RANGE_BEGIN &&
2074 pEntry->nWID <= FN_UNO_RANGE_END )
2076 (void)SwUnoCursorHelper::getCursorPropertyValue(
2077 *pEntry, rPaM, nullptr, pStates[i] );
2079 else
2081 if (!oSet)
2083 switch ( eCaller )
2085 case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT:
2086 case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION:
2087 oSet.emplace( rPaM.GetDoc().GetAttrPool(),
2088 svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END> );
2089 break;
2090 case SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY:
2091 oSet.emplace( rPaM.GetDoc().GetAttrPool(),
2092 pEntry->nWID, pEntry->nWID );
2093 break;
2094 default:
2095 oSet.emplace(
2096 rPaM.GetDoc().GetAttrPool(),
2097 svl::Items<
2098 RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
2099 RES_UNKNOWNATR_CONTAINER,
2100 RES_UNKNOWNATR_CONTAINER>);
2102 // #i63870#
2103 SwUnoCursorHelper::GetCursorAttr( rPaM, *oSet );
2106 pStates[i] = ( oSet->Count() )
2107 ? SfxItemPropertySet::getPropertyState( *pEntry, *oSet )
2108 : beans::PropertyState_DEFAULT_VALUE;
2110 //try again to find out if a value has been inherited
2111 if( beans::PropertyState_DIRECT_VALUE == pStates[i] )
2113 if (!oSetParent)
2115 oSetParent.emplace(oSet->CloneAsValue( false ));
2116 // #i63870#
2117 SwUnoCursorHelper::GetCursorAttr(
2118 rPaM, *oSetParent, true, false );
2121 pStates[i] = ( oSetParent->Count() )
2122 ? SfxItemPropertySet::getPropertyState( *pEntry, *oSetParent )
2123 : beans::PropertyState_DEFAULT_VALUE;
2128 return aRet;
2131 beans::PropertyState SwUnoCursorHelper::GetPropertyState(
2132 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
2133 const OUString& rPropertyName)
2135 uno::Sequence< OUString > aStrings { rPropertyName };
2136 uno::Sequence< beans::PropertyState > aSeq =
2137 GetPropertyStates(rPaM, rPropSet, aStrings,
2138 SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY );
2139 return aSeq[0];
2142 static void
2143 lcl_SelectParaAndReset( SwPaM &rPaM, SwDoc & rDoc,
2144 o3tl::sorted_vector<sal_uInt16> const &rWhichIds )
2146 // if we are resetting paragraph attributes, we need to select the full paragraph first
2147 SwPosition aStart = *rPaM.Start();
2148 SwPosition aEnd = *rPaM.End();
2149 auto pTemp ( rDoc.CreateUnoCursor(aStart) );
2150 if(!SwUnoCursorHelper::IsStartOfPara(*pTemp))
2152 pTemp->MovePara(GoCurrPara, fnParaStart);
2154 pTemp->SetMark();
2155 *pTemp->GetPoint() = std::move(aEnd);
2156 SwUnoCursorHelper::SelectPam(*pTemp, true);
2157 if(!SwUnoCursorHelper::IsEndOfPara(*pTemp))
2159 pTemp->MovePara(GoCurrPara, fnParaEnd);
2161 rDoc.ResetAttrs(*pTemp, true, rWhichIds);
2164 void SwUnoCursorHelper::SetPropertyToDefault(
2165 SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
2166 std::u16string_view rPropertyName)
2168 SwDoc& rDoc = rPaM.GetDoc();
2169 SfxItemPropertyMapEntry const*const pEntry =
2170 rPropSet.getPropertyMap().getByName(rPropertyName);
2171 if (!pEntry)
2173 throw beans::UnknownPropertyException(
2174 OUString::Concat("Unknown property: ") + rPropertyName);
2177 if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
2179 throw uno::RuntimeException(
2180 OUString::Concat("setPropertyToDefault: property is read-only: ")
2181 + rPropertyName, nullptr);
2184 if (pEntry->nWID < RES_FRMATR_END)
2186 const o3tl::sorted_vector<sal_uInt16> aWhichIds{ pEntry->nWID };
2187 if (pEntry->nWID < RES_PARATR_BEGIN)
2189 rDoc.ResetAttrs(rPaM, true, aWhichIds);
2191 else
2193 lcl_SelectParaAndReset ( rPaM, rDoc, aWhichIds );
2196 else
2198 SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, rPaM);
2202 uno::Any SwUnoCursorHelper::GetPropertyDefault(
2203 SwPaM const & rPaM, const SfxItemPropertySet& rPropSet,
2204 std::u16string_view rPropertyName)
2206 SfxItemPropertyMapEntry const*const pEntry =
2207 rPropSet.getPropertyMap().getByName(rPropertyName);
2208 if (!pEntry)
2210 throw beans::UnknownPropertyException(
2211 OUString::Concat("Unknown property: ") + rPropertyName);
2214 uno::Any aRet;
2215 if (pEntry->nWID < RES_FRMATR_END)
2217 SwDoc& rDoc = rPaM.GetDoc();
2218 const SfxPoolItem& rDefItem =
2219 rDoc.GetAttrPool().GetUserOrPoolDefaultItem(pEntry->nWID);
2220 rDefItem.QueryValue(aRet, pEntry->nMemberId);
2222 return aRet;
2225 uno::Reference< beans::XPropertySetInfo > SAL_CALL
2226 SwXTextCursor::getPropertySetInfo()
2228 SolarMutexGuard g;
2230 static uno::Reference< beans::XPropertySetInfo > xRef = [&]()
2232 static SfxItemPropertyMapEntry const aCursorExtMap_Impl[] =
2234 { UNO_NAME_IS_SKIP_HIDDEN_TEXT, FN_SKIP_HIDDEN_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
2235 { UNO_NAME_IS_SKIP_PROTECTED_TEXT, FN_SKIP_PROTECTED_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
2236 { UNO_NAME_NO_FORMAT_ATTR, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
2238 const uno::Reference< beans::XPropertySetInfo > xInfo =
2239 m_rPropSet.getPropertySetInfo();
2240 // extend PropertySetInfo!
2241 const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties();
2242 return rtl::Reference<SfxExtItemPropertySetInfo>(new SfxExtItemPropertySetInfo(
2243 aCursorExtMap_Impl,
2244 aPropSeq ));
2245 }();
2246 return xRef;
2249 void SAL_CALL
2250 SwXTextCursor::setPropertyValue(
2251 const OUString& rPropertyName, const uno::Any& rValue)
2253 SolarMutexGuard aGuard;
2255 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2257 if (rPropertyName == UNO_NAME_IS_SKIP_HIDDEN_TEXT)
2259 bool bSet(false);
2260 if (!(rValue >>= bSet))
2262 throw lang::IllegalArgumentException();
2264 rUnoCursor.SetSkipOverHiddenSections(bSet);
2266 else if (rPropertyName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
2268 bool bSet(false);
2269 if (!(rValue >>= bSet))
2271 throw lang::IllegalArgumentException();
2273 rUnoCursor.SetSkipOverProtectSections(bSet);
2275 else if (rPropertyName == UNO_NAME_NO_FORMAT_ATTR)
2277 bool bSet(false);
2278 if (!(rValue >>= bSet))
2280 throw lang::IllegalArgumentException();
2282 if (bSet)
2284 m_nAttrMode = SetAttrMode::NOFORMATATTR;
2286 else
2288 m_nAttrMode = SetAttrMode::DEFAULT;
2291 else if (rPropertyName == "ParaAutoStyleDef")
2293 // Create an autostyle from passed definition (sequence of PropertyValue, same
2294 // as in XAutoStyleFamily::insertStyle), using the currently applied properties
2295 // from the paragraph to not lose their values when creating complex properties
2296 // like SvxULSpaceItem, when only part of the properties stored there is passed;
2297 // and apply it to the paragraph.
2298 uno::Sequence<beans::PropertyValue> def;
2299 if (!(rValue >>= def))
2300 throw lang::IllegalArgumentException();
2302 // See SwUnoCursorHelper::SetPropertyValues
2304 auto pPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARA_AUTO_STYLE);
2306 // Build set of attributes we want to fetch
2307 WhichRangesContainer aRanges;
2308 for (auto& rPropVal : def)
2310 SfxItemPropertyMapEntry const* pEntry =
2311 pPropSet->getPropertyMap().getByName(rPropVal.Name);
2312 if (!pEntry)
2313 continue; // PropValuesToAutoStyleItemSet ignores invalid names
2315 aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID);
2318 if (!aRanges.empty())
2320 SwAttrSet aAutoStyleItemSet(rUnoCursor.GetDoc().GetAttrPool(), std::move(aRanges));
2321 // we need to get up-to-date item set: this makes sure that the complex properties,
2322 // that are only partially defined by passed definition, do not lose the rest of
2323 // their already present data (which will become part of the autostyle, too).
2324 SwUnoCursorHelper::GetCursorAttr(rUnoCursor, aAutoStyleItemSet);
2325 // Set normal set ranges before putting into autostyle, to the same ranges
2326 // that are used for paragraph autostyle in SwXAutoStyleFamily::insertStyle
2327 aAutoStyleItemSet.SetRanges(aTextNodeSetRange);
2329 // Fill the prepared item set, containing current paragraph property values,
2330 // with the passed definition, and create the autostyle.
2331 auto pStyle = PropValuesToAutoStyleItemSet(
2332 rUnoCursor.GetDoc(), IStyleAccess::AUTO_STYLE_PARA, def, aAutoStyleItemSet);
2334 SwFormatAutoFormat aFormat(RES_AUTO_STYLE);
2335 aFormat.SetStyleHandle(pStyle);
2336 SfxItemSet rSet(rUnoCursor.GetDoc().GetAttrPool(), RES_AUTO_STYLE, RES_AUTO_STYLE);
2337 rSet.Put(aFormat);
2338 SwUnoCursorHelper::SetCursorAttr(rUnoCursor, rSet, m_nAttrMode);
2341 else
2343 SwUnoCursorHelper::SetPropertyValue(rUnoCursor,
2344 m_rPropSet, rPropertyName, rValue, m_nAttrMode);
2348 uno::Any SAL_CALL
2349 SwXTextCursor::getPropertyValue(const OUString& rPropertyName)
2351 SolarMutexGuard aGuard;
2353 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2355 uno::Any aAny;
2356 if (rPropertyName == UNO_NAME_IS_SKIP_HIDDEN_TEXT)
2358 const bool bSet = rUnoCursor.IsSkipOverHiddenSections();
2359 aAny <<= bSet;
2361 else if (rPropertyName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
2363 const bool bSet = rUnoCursor.IsSkipOverProtectSections();
2364 aAny <<= bSet;
2366 else
2368 aAny = SwUnoCursorHelper::GetPropertyValue(rUnoCursor,
2369 m_rPropSet, rPropertyName);
2371 return aAny;
2374 void SAL_CALL
2375 SwXTextCursor::addPropertyChangeListener(
2376 const OUString& /*rPropertyName*/,
2377 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
2379 OSL_FAIL("SwXTextCursor::addPropertyChangeListener(): not implemented");
2382 void SAL_CALL
2383 SwXTextCursor::removePropertyChangeListener(
2384 const OUString& /*rPropertyName*/,
2385 const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
2387 OSL_FAIL("SwXTextCursor::removePropertyChangeListener(): not implemented");
2390 void SAL_CALL
2391 SwXTextCursor::addVetoableChangeListener(
2392 const OUString& /*rPropertyName*/,
2393 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
2395 OSL_FAIL("SwXTextCursor::addVetoableChangeListener(): not implemented");
2398 void SAL_CALL
2399 SwXTextCursor::removeVetoableChangeListener(
2400 const OUString& /*rPropertyName*/,
2401 const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
2403 OSL_FAIL("SwXTextCursor::removeVetoableChangeListener(): not implemented");
2406 beans::PropertyState SAL_CALL
2407 SwXTextCursor::getPropertyState(const OUString& rPropertyName)
2409 SolarMutexGuard aGuard;
2411 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2413 const beans::PropertyState eRet = SwUnoCursorHelper::GetPropertyState(
2414 rUnoCursor, m_rPropSet, rPropertyName);
2415 return eRet;
2418 uno::Sequence< beans::PropertyState > SAL_CALL
2419 SwXTextCursor::getPropertyStates(
2420 const uno::Sequence< OUString >& rPropertyNames)
2422 SolarMutexGuard aGuard;
2424 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2426 return SwUnoCursorHelper::GetPropertyStates(
2427 rUnoCursor, m_rPropSet, rPropertyNames);
2430 void SAL_CALL
2431 SwXTextCursor::setPropertyToDefault(const OUString& rPropertyName)
2433 // forward: need no solar mutex here
2434 uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
2435 setPropertiesToDefault ( aSequence );
2438 uno::Any SAL_CALL
2439 SwXTextCursor::getPropertyDefault(const OUString& rPropertyName)
2441 // forward: need no solar mutex here
2442 const uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
2443 return getPropertyDefaults ( aSequence ).getConstArray()[0];
2446 void SAL_CALL SwXTextCursor::setPropertyValues(
2447 const uno::Sequence< OUString >& aPropertyNames,
2448 const uno::Sequence< uno::Any >& aValues )
2450 if( aValues.getLength() != aPropertyNames.getLength() )
2452 OSL_FAIL( "mis-matched property value sequences" );
2453 throw lang::IllegalArgumentException();
2456 SolarMutexGuard aGuard;
2458 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2460 // a little lame to have to copy into this.
2461 uno::Sequence< beans::PropertyValue > aPropertyValues( aValues.getLength() );
2462 auto aPropertyValuesRange = asNonConstRange(aPropertyValues);
2463 for ( sal_Int32 i = 0; i < aPropertyNames.getLength(); i++ )
2465 if ( aPropertyNames[ i ] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
2466 aPropertyNames[ i ] == UNO_NAME_IS_SKIP_PROTECTED_TEXT )
2468 // the behaviour of these is hard to model in a group
2469 OSL_FAIL("invalid property name for batch setting");
2470 throw lang::IllegalArgumentException();
2472 aPropertyValuesRange[ i ].Name = aPropertyNames[ i ];
2473 aPropertyValuesRange[ i ].Value = aValues[ i ];
2477 SwUnoCursorHelper::SetPropertyValues( rUnoCursor, m_rPropSet, aPropertyValues );
2479 catch (const css::beans::UnknownPropertyException& e)
2481 uno::Any a(cppu::getCaughtException());
2482 throw lang::WrappedTargetException(
2483 "wrapped Exception " + e.Message,
2484 uno::Reference<uno::XInterface>(), a);
2488 uno::Sequence< uno::Any > SAL_CALL
2489 SwXTextCursor::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames )
2491 // a banal implementation for now
2492 uno::Sequence< uno::Any > aValues( aPropertyNames.getLength() );
2493 std::transform(aPropertyNames.begin(), aPropertyNames.end(), aValues.getArray(),
2494 [this](const OUString& rName) -> uno::Any { return getPropertyValue( rName ); });
2495 return aValues;
2498 void SAL_CALL SwXTextCursor::addPropertiesChangeListener(
2499 const uno::Sequence< OUString >& /* aPropertyNames */,
2500 const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
2502 OSL_FAIL("SwXTextCursor::addPropertiesChangeListener(): not implemented");
2504 void SAL_CALL SwXTextCursor::removePropertiesChangeListener(
2505 const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
2507 OSL_FAIL("SwXTextCursor::removePropertiesChangeListener(): not implemented");
2510 void SAL_CALL SwXTextCursor::firePropertiesChangeEvent(
2511 const uno::Sequence< OUString >& /* aPropertyNames */,
2512 const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
2514 OSL_FAIL("SwXTextCursor::firePropertiesChangeEvent(): not implemented");
2517 // para specific attribute ranges
2518 static sal_uInt16 g_ParaResetableSetRange[] = {
2519 RES_FRMATR_BEGIN, RES_FRMATR_END-1,
2520 RES_PARATR_BEGIN, RES_PARATR_END-1,
2521 RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1,
2522 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
2526 // selection specific attribute ranges
2527 static sal_uInt16 g_ResetableSetRange[] = {
2528 RES_CHRATR_BEGIN, RES_CHRATR_END-1,
2529 RES_TXTATR_INETFMT, RES_TXTATR_INETFMT,
2530 RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT,
2531 RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
2532 RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER,
2536 static void
2537 lcl_EnumerateIds(sal_uInt16 const* pIdRange, o3tl::sorted_vector<sal_uInt16> &rWhichIds)
2539 while (*pIdRange)
2541 const sal_uInt16 nStart = *pIdRange++;
2542 const sal_uInt16 nEnd = *pIdRange++;
2543 for (sal_uInt16 nId = nStart + 1; nId <= nEnd; ++nId)
2545 rWhichIds.insert( nId );
2550 void SAL_CALL
2551 SwXTextCursor::setAllPropertiesToDefault()
2553 SolarMutexGuard aGuard;
2555 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2557 o3tl::sorted_vector<sal_uInt16> aParaWhichIds;
2558 o3tl::sorted_vector<sal_uInt16> aWhichIds;
2559 lcl_EnumerateIds(g_ParaResetableSetRange, aParaWhichIds);
2560 lcl_EnumerateIds(g_ResetableSetRange, aWhichIds);
2561 if (!aParaWhichIds.empty())
2563 lcl_SelectParaAndReset(rUnoCursor, rUnoCursor.GetDoc(),
2564 aParaWhichIds);
2566 if (!aWhichIds.empty())
2568 rUnoCursor.GetDoc().ResetAttrs(rUnoCursor, true, aWhichIds);
2572 void SAL_CALL
2573 SwXTextCursor::setPropertiesToDefault(
2574 const uno::Sequence< OUString >& rPropertyNames)
2576 SolarMutexGuard aGuard;
2578 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2580 if ( !rPropertyNames.hasElements() )
2581 return;
2583 SwDoc& rDoc = rUnoCursor.GetDoc();
2584 o3tl::sorted_vector<sal_uInt16> aWhichIds;
2585 o3tl::sorted_vector<sal_uInt16> aParaWhichIds;
2586 for (const OUString& rName : rPropertyNames)
2588 SfxItemPropertyMapEntry const*const pEntry =
2589 m_rPropSet.getPropertyMap().getByName( rName );
2590 if (!pEntry)
2592 if (rName == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
2593 rName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
2595 continue;
2597 throw beans::UnknownPropertyException(
2598 "Unknown property: " + rName,
2599 getXWeak());
2601 if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
2603 throw uno::RuntimeException(
2604 "setPropertiesToDefault: property is read-only: " + rName,
2605 getXWeak());
2608 if (pEntry->nWID < RES_FRMATR_END)
2610 if (pEntry->nWID < RES_PARATR_BEGIN)
2612 aWhichIds.insert( pEntry->nWID );
2614 else
2616 aParaWhichIds.insert( pEntry->nWID );
2619 else if (pEntry->nWID == FN_UNO_NUM_START_VALUE)
2621 SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, rUnoCursor);
2625 if (!aParaWhichIds.empty())
2627 lcl_SelectParaAndReset(rUnoCursor, rDoc, aParaWhichIds);
2629 if (!aWhichIds.empty())
2631 rDoc.ResetAttrs(rUnoCursor, true, aWhichIds);
2635 uno::Sequence< uno::Any > SAL_CALL
2636 SwXTextCursor::getPropertyDefaults(
2637 const uno::Sequence< OUString >& rPropertyNames)
2639 SolarMutexGuard aGuard;
2641 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2643 const sal_Int32 nCount = rPropertyNames.getLength();
2644 uno::Sequence< uno::Any > aRet(nCount);
2645 if ( nCount )
2647 SwDoc& rDoc = rUnoCursor.GetDoc();
2648 const OUString *pNames = rPropertyNames.getConstArray();
2649 uno::Any *pAny = aRet.getArray();
2650 for (sal_Int32 i = 0; i < nCount; i++)
2652 SfxItemPropertyMapEntry const*const pEntry =
2653 m_rPropSet.getPropertyMap().getByName( pNames[i] );
2654 if (!pEntry)
2656 if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
2657 pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
2658 pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
2660 continue;
2662 throw beans::UnknownPropertyException(
2663 "Unknown property: " + pNames[i]);
2665 if (pEntry->nWID < RES_FRMATR_END)
2667 const SfxPoolItem& rDefItem =
2668 rDoc.GetAttrPool().GetUserOrPoolDefaultItem(pEntry->nWID);
2669 rDefItem.QueryValue(pAny[i], pEntry->nMemberId);
2673 return aRet;
2676 void SAL_CALL SwXTextCursor::invalidateMarkings(::sal_Int32 nType)
2678 SolarMutexGuard aGuard;
2680 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2682 SwNode& node = rUnoCursor.GetPointNode();
2684 SwTextNode* txtNode = node.GetTextNode();
2686 if (txtNode == nullptr) return;
2688 if ( text::TextMarkupType::SPELLCHECK == nType )
2690 txtNode->SetWrongDirty(sw::WrongState::TODO);
2691 txtNode->ClearWrong();
2693 else if( text::TextMarkupType::PROOFREADING == nType )
2695 txtNode->SetGrammarCheckDirty(true);
2696 txtNode->ClearGrammarCheck();
2698 else if ( text::TextMarkupType::SMARTTAG == nType )
2700 txtNode->SetSmartTagDirty(true);
2701 txtNode->ClearSmartTags();
2703 else return;
2705 SwFormatColl* fmtColl=txtNode->GetFormatColl();
2707 if (fmtColl == nullptr) return;
2709 txtNode->CallSwClientNotify(SwFormatChangeHint(nullptr, fmtColl));
2712 void SAL_CALL
2713 SwXTextCursor::makeRedline(
2714 const OUString& rRedlineType,
2715 const uno::Sequence< beans::PropertyValue >& rRedlineProperties)
2717 SolarMutexGuard aGuard;
2719 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2721 SwUnoCursorHelper::makeRedline(rUnoCursor, rRedlineType, rRedlineProperties);
2724 void SAL_CALL SwXTextCursor::insertDocumentFromURL(const OUString& rURL,
2725 const uno::Sequence< beans::PropertyValue >& rOptions)
2727 SolarMutexGuard aGuard;
2729 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
2731 SwUnoCursorHelper::InsertFile(&rUnoCursor, rURL, rOptions);
2734 uno::Sequence< beans::PropertyValue >
2735 SwUnoCursorHelper::CreateSortDescriptor(const bool bFromTable)
2737 uno::Sequence< beans::PropertyValue > aRet(5);
2738 beans::PropertyValue* pArray = aRet.getArray();
2740 uno::Any aVal;
2741 aVal <<= bFromTable;
2742 pArray[0] = beans::PropertyValue(u"IsSortInTable"_ustr, -1, aVal,
2743 beans::PropertyState_DIRECT_VALUE);
2745 aVal <<= u' ';
2746 pArray[1] = beans::PropertyValue(u"Delimiter"_ustr, -1, aVal,
2747 beans::PropertyState_DIRECT_VALUE);
2749 aVal <<= false;
2750 pArray[2] = beans::PropertyValue(u"IsSortColumns"_ustr, -1, aVal,
2751 beans::PropertyState_DIRECT_VALUE);
2753 aVal <<= sal_Int32(3);
2754 pArray[3] = beans::PropertyValue(u"MaxSortFieldsCount"_ustr, -1, aVal,
2755 beans::PropertyState_DIRECT_VALUE);
2757 lang::Locale aLang( SvtSysLocale().GetLanguageTag().getLocale());
2758 // get collator algorithm to be used for the locale
2759 uno::Sequence< OUString > aSeq(
2760 GetAppCollator().listCollatorAlgorithms( aLang ) );
2761 const bool bHasElements = aSeq.hasElements();
2762 OSL_ENSURE( bHasElements, "list of collator algorithms is empty!");
2763 OUString aCollAlg;
2764 if (bHasElements)
2766 aCollAlg = aSeq.getConstArray()[0];
2769 uno::Sequence< table::TableSortField > aFields
2771 // Field, IsAscending, IsCaseSensitive, FieldType, CollatorLocale, CollatorAlgorithm
2772 { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg },
2773 { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg },
2774 { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg }
2777 aVal <<= aFields;
2778 pArray[4] = beans::PropertyValue(u"SortFields"_ustr, -1, aVal,
2779 beans::PropertyState_DIRECT_VALUE);
2781 return aRet;
2784 uno::Sequence< beans::PropertyValue > SAL_CALL
2785 SwXTextCursor::createSortDescriptor()
2787 SolarMutexGuard aGuard;
2789 return SwUnoCursorHelper::CreateSortDescriptor(false);
2792 bool SwUnoCursorHelper::ConvertSortProperties(
2793 const uno::Sequence< beans::PropertyValue >& rDescriptor,
2794 SwSortOptions& rSortOpt)
2796 bool bRet = true;
2798 rSortOpt.bTable = false;
2799 rSortOpt.cDeli = ' ';
2800 rSortOpt.eDirection = SwSortDirection::Columns; //!! UI text may be contrary though !!
2802 SwSortKey aKey1;
2803 aKey1.nColumnId = USHRT_MAX;
2804 aKey1.bIsNumeric = true;
2805 aKey1.eSortOrder = SwSortOrder::Ascending;
2807 SwSortKey aKey2;
2808 aKey2.nColumnId = USHRT_MAX;
2809 aKey2.bIsNumeric = true;
2810 aKey2.eSortOrder = SwSortOrder::Ascending;
2812 SwSortKey aKey3;
2813 aKey3.nColumnId = USHRT_MAX;
2814 aKey3.bIsNumeric = true;
2815 aKey3.eSortOrder = SwSortOrder::Ascending;
2816 SwSortKey* aKeys[3] = {&aKey1, &aKey2, &aKey3};
2818 bool bOldSortdescriptor(false);
2819 bool bNewSortdescriptor(false);
2821 for (const beans::PropertyValue& rProperty : rDescriptor)
2823 uno::Any aValue( rProperty.Value );
2824 const OUString& rPropName = rProperty.Name;
2826 // old and new sortdescriptor
2827 if ( rPropName == "IsSortInTable" )
2829 if (auto b = o3tl::tryAccess<bool>(aValue))
2831 rSortOpt.bTable = *b;
2833 else
2835 bRet = false;
2838 else if ( rPropName == "Delimiter" )
2840 sal_Unicode uChar;
2841 sal_uInt16 nChar;
2842 if (aValue >>= uChar)
2844 rSortOpt.cDeli = uChar;
2846 else if (aValue >>= nChar)
2848 // For compatibility with BASIC, also accept an ANY containing
2849 // an UNSIGNED SHORT:
2850 rSortOpt.cDeli = nChar;
2852 else
2854 bRet = false;
2857 // old sortdescriptor
2858 else if ( rPropName == "SortColumns" )
2860 bOldSortdescriptor = true;
2861 bool bTemp(false);
2862 if (aValue >>= bTemp)
2864 rSortOpt.eDirection = bTemp ? SwSortDirection::Columns : SwSortDirection::Rows;
2866 else
2868 bRet = false;
2871 else if ( rPropName == "IsCaseSensitive" )
2873 bOldSortdescriptor = true;
2874 bool bTemp(false);
2875 if (aValue >>= bTemp)
2877 rSortOpt.bIgnoreCase = !bTemp;
2879 else
2881 bRet = false;
2884 else if ( rPropName == "CollatorLocale" )
2886 bOldSortdescriptor = true;
2887 lang::Locale aLocale;
2888 if (aValue >>= aLocale)
2890 rSortOpt.nLanguage = LanguageTag::convertToLanguageType( aLocale);
2892 else
2894 bRet = false;
2897 else if (rPropName.startsWith("CollatorAlgorithm") &&
2898 rPropName.getLength() == 18 &&
2899 (rPropName[17] >= '0' && rPropName[17] <= '9'))
2901 bOldSortdescriptor = true;
2902 sal_uInt16 nIndex = rPropName[17];
2903 nIndex -= '0';
2904 OUString aText;
2905 if ((aValue >>= aText) && nIndex < 3)
2907 aKeys[nIndex]->sSortType = aText;
2909 else
2911 bRet = false;
2914 else if (rPropName.startsWith("SortRowOrColumnNo") &&
2915 rPropName.getLength() == 18 &&
2916 (rPropName[17] >= '0' && rPropName[17] <= '9'))
2918 bOldSortdescriptor = true;
2919 sal_uInt16 nIndex = rPropName[17];
2920 nIndex -= '0';
2921 sal_Int16 nCol = -1;
2922 if (aValue.getValueType() == ::cppu::UnoType<sal_Int16>::get()
2923 && nIndex < 3)
2925 aValue >>= nCol;
2927 if (nCol >= 0)
2929 aKeys[nIndex]->nColumnId = nCol;
2931 else
2933 bRet = false;
2936 else if (rPropName.startsWith("IsSortNumeric") &&
2937 rPropName.getLength() == 14 &&
2938 (rPropName[13] >= '0' && rPropName[13] <= '9'))
2940 bOldSortdescriptor = true;
2941 sal_uInt16 nIndex = rPropName[13];
2942 nIndex = nIndex - '0';
2943 std::optional<const bool> bTemp = o3tl::tryAccess<bool>(aValue);
2944 if (bTemp.has_value() && nIndex < 3)
2946 aKeys[nIndex]->bIsNumeric = *bTemp;
2948 else
2950 bRet = false;
2953 else if (rPropName.startsWith("IsSortAscending") &&
2954 rPropName.getLength() == 16 &&
2955 (rPropName[15] >= '0' && rPropName[15] <= '9'))
2957 bOldSortdescriptor = true;
2958 sal_uInt16 nIndex = rPropName[15];
2959 nIndex -= '0';
2960 std::optional<const bool> bTemp = o3tl::tryAccess<bool>(aValue);
2961 if (bTemp.has_value() && nIndex < 3)
2963 aKeys[nIndex]->eSortOrder = (*bTemp)
2964 ? SwSortOrder::Ascending : SwSortOrder::Descending;
2966 else
2968 bRet = false;
2971 // new sortdescriptor
2972 else if ( rPropName == "IsSortColumns" )
2974 bNewSortdescriptor = true;
2975 if (auto bTemp = o3tl::tryAccess<bool>(aValue))
2977 rSortOpt.eDirection = *bTemp ? SwSortDirection::Columns : SwSortDirection::Rows;
2979 else
2981 bRet = false;
2984 else if ( rPropName == "SortFields" )
2986 bNewSortdescriptor = true;
2987 uno::Sequence < table::TableSortField > aFields;
2988 if (aValue >>= aFields)
2990 sal_Int32 nCount(aFields.getLength());
2991 if (nCount <= 3)
2993 table::TableSortField* pFields = aFields.getArray();
2994 for (sal_Int32 i = 0; i < nCount; ++i)
2996 rSortOpt.bIgnoreCase = !pFields[i].IsCaseSensitive;
2997 rSortOpt.nLanguage =
2998 LanguageTag::convertToLanguageType( pFields[i].CollatorLocale );
2999 aKeys[i]->sSortType = pFields[i].CollatorAlgorithm;
3000 aKeys[i]->nColumnId =
3001 o3tl::narrowing<sal_uInt16>(pFields[i].Field);
3002 aKeys[i]->bIsNumeric = (pFields[i].FieldType ==
3003 table::TableSortFieldType_NUMERIC);
3004 aKeys[i]->eSortOrder = (pFields[i].IsAscending)
3005 ? SwSortOrder::Ascending : SwSortOrder::Descending;
3008 else
3010 bRet = false;
3013 else
3015 bRet = false;
3020 if (bNewSortdescriptor && bOldSortdescriptor)
3022 OSL_FAIL("someone tried to set the old deprecated and "
3023 "the new sortdescriptor");
3024 bRet = false;
3027 if (aKey1.nColumnId != USHRT_MAX)
3029 rSortOpt.aKeys.push_back(aKey1);
3031 if (aKey2.nColumnId != USHRT_MAX)
3033 rSortOpt.aKeys.push_back(aKey2);
3035 if (aKey3.nColumnId != USHRT_MAX)
3037 rSortOpt.aKeys.push_back(aKey3);
3040 return bRet && !rSortOpt.aKeys.empty();
3043 void SAL_CALL
3044 SwXTextCursor::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor)
3046 SolarMutexGuard aGuard;
3048 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
3050 if (!rUnoCursor.HasMark())
3051 return;
3053 SwSortOptions aSortOpt;
3054 if (!SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt))
3056 throw uno::RuntimeException(u"Bad sort properties"_ustr);
3058 UnoActionContext aContext( &rUnoCursor.GetDoc() );
3060 SwPosition & rStart = *rUnoCursor.Start();
3061 SwPosition & rEnd = *rUnoCursor.End();
3063 SwNodeIndex aPrevIdx( rStart.GetNode(), -1 );
3064 const SwNodeOffset nOffset = rEnd.GetNodeIndex() - rStart.GetNodeIndex();
3065 const sal_Int32 nCntStt = rStart.GetContentIndex();
3067 rUnoCursor.GetDoc().SortText(rUnoCursor, aSortOpt);
3069 // update selection
3070 rUnoCursor.DeleteMark();
3071 rUnoCursor.GetPoint()->Assign( aPrevIdx.GetNode(), SwNodeOffset(1) );
3072 SwContentNode *const pCNd = rUnoCursor.GetPointContentNode();
3073 sal_Int32 nLen = pCNd->Len();
3074 if (nLen > nCntStt)
3076 nLen = nCntStt;
3078 rUnoCursor.GetPoint()->SetContent( nLen );
3079 rUnoCursor.SetMark();
3081 rUnoCursor.GetPoint()->Adjust(nOffset);
3082 SwContentNode *const pCNd2 = rUnoCursor.GetPointContentNode();
3083 rUnoCursor.GetPoint()->SetContent( pCNd2->Len() );
3087 uno::Reference< container::XEnumeration > SAL_CALL
3088 SwXTextCursor::createContentEnumeration(const OUString& rServiceName)
3090 SolarMutexGuard g;
3091 if (rServiceName != "com.sun.star.text.TextContent")
3092 throw uno::RuntimeException();
3093 SwUnoCursor& rUnoCursor( GetCursorOrThrow() );
3094 return SwXParaFrameEnumeration::Create(rUnoCursor, PARAFRAME_PORTION_TEXTRANGE);
3097 uno::Reference< container::XEnumeration > SAL_CALL
3098 SwXTextCursor::createEnumeration()
3100 SolarMutexGuard g;
3102 SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
3104 SwXText* pParentText = dynamic_cast<SwXText*>(m_xParentText.get());
3105 OSL_ENSURE(pParentText, "parent is not a SwXText");
3106 if (!pParentText)
3108 throw uno::RuntimeException();
3111 auto pNewCursor(rUnoCursor.GetDoc().CreateUnoCursor(*rUnoCursor.GetPoint()) );
3112 if (rUnoCursor.HasMark())
3114 pNewCursor->SetMark();
3115 *pNewCursor->GetMark() = *rUnoCursor.GetMark();
3117 const CursorType eSetType = (CursorType::TableText == m_eType)
3118 ? CursorType::SelectionInTable : CursorType::Selection;
3119 return SwXParagraphEnumeration::Create(pParentText, pNewCursor, eSetType);
3122 uno::Type SAL_CALL
3123 SwXTextCursor::getElementType()
3125 return cppu::UnoType<text::XTextRange>::get();
3128 sal_Bool SAL_CALL SwXTextCursor::hasElements()
3130 return true;
3133 uno::Sequence< OUString > SAL_CALL
3134 SwXTextCursor::getAvailableServiceNames()
3136 uno::Sequence<OUString> aRet { u"com.sun.star.text.TextContent"_ustr };
3137 return aRet;
3140 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */