/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; namespace { class SwParaSelection { SwCursor & m_rCursor; public: explicit SwParaSelection(SwCursor & rCursor); ~SwParaSelection(); }; } SwParaSelection::SwParaSelection(SwCursor & rCursor) : m_rCursor(rCursor) { if (m_rCursor.HasMark()) { m_rCursor.DeleteMark(); } // is it at the start? if (m_rCursor.GetPoint()->GetContentIndex() != 0) { m_rCursor.MovePara(GoCurrPara, fnParaStart); } // or at the end already? if (m_rCursor.GetPoint()->GetContentIndex() != m_rCursor.GetPointContentNode()->Len()) { m_rCursor.SetMark(); m_rCursor.MovePara(GoCurrPara, fnParaEnd); } } SwParaSelection::~SwParaSelection() { if (m_rCursor.GetPoint()->GetContentIndex() != 0) { m_rCursor.DeleteMark(); m_rCursor.MovePara(GoCurrPara, fnParaStart); } } /// @throws beans::UnknownPropertyException /// @throws uno::RuntimeException static beans::PropertyState lcl_SwXParagraph_getPropertyState( const SwTextNode& rTextNode, const SwAttrSet** ppSet, const SfxItemPropertyMapEntry& rEntry, bool &rAttrSetFetched ); class SwXParagraph::Impl : public SvtListener { public: SwXParagraph& m_rThis; unotools::WeakReference m_wThis; std::mutex m_Mutex; // just for OInterfaceContainerHelper4 ::comphelper::OInterfaceContainerHelper4 m_EventListeners; SfxItemPropertySet const& m_rPropSet; bool m_bIsDescriptor; sal_Int32 m_nSelectionStartPos; sal_Int32 m_nSelectionEndPos; OUString m_sText; uno::Reference m_xParentText; SwTextNode* m_pTextNode; Impl(SwXParagraph& rThis, SwTextNode* const pTextNode = nullptr, uno::Reference xParent = nullptr, const sal_Int32 nSelStart = -1, const sal_Int32 nSelEnd = -1) : m_rThis(rThis) , m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH)) , m_bIsDescriptor(nullptr == pTextNode) , m_nSelectionStartPos(nSelStart) , m_nSelectionEndPos(nSelEnd) , m_xParentText(std::move(xParent)) , m_pTextNode(pTextNode) { m_pTextNode && StartListening(m_pTextNode->GetNotifier()); } SwTextNode* GetTextNode() { return m_pTextNode; } SwTextNode& GetTextNodeOrThrow() { if (!m_pTextNode) { throw uno::RuntimeException("SwXParagraph: disposed or invalid", nullptr); } return *m_pTextNode; } bool IsDescriptor() const { return m_bIsDescriptor; } /// @throws beans::UnknownPropertyException /// @throws beans::PropertyVetoException /// @throws lang::IllegalArgumentException /// @throws lang::WrappedTargetException /// @throws uno::RuntimeException void SetPropertyValues_Impl( const uno::Sequence< OUString >& rPropertyNames, const uno::Sequence< uno::Any >& rValues); /// @throws beans::UnknownPropertyException /// @throws lang::WrappedTargetException /// @throws uno::RuntimeException uno::Sequence< uno::Any > GetPropertyValues_Impl( const uno::Sequence< OUString >& rPropertyNames); /// @throws uno::RuntimeException void GetSinglePropertyValue_Impl( const SfxItemPropertyMapEntry& rEntry, const SfxItemSet& rSet, uno::Any& rAny ) const; /// @throws uno::RuntimeException uno::Sequence< beans::GetDirectPropertyTolerantResult > GetPropertyValuesTolerant_Impl( const uno::Sequence< OUString >& rPropertyNames, bool bDirectValuesOnly); protected: virtual void Notify(const SfxHint& rHint) override; }; void SwXParagraph::Impl::Notify(const SfxHint& rHint) { if(rHint.GetId() == SfxHintId::Dying) { m_pTextNode = nullptr; std::unique_lock aGuard(m_Mutex); if (m_EventListeners.getLength(aGuard) != 0) { // fdo#72695: if UNO object is already dead, don't revive it with event // The specific pattern we are guarding against is this: // [1] Thread1 takes the SolarMutex // [2] Thread2 decrements the SwXParagraph reference count, and calls the // SwXParagraph destructor, which tries to take the SolarMutex, and blocks // [3] Thread1 destroys a SwTextNode, which calls this Notify event, which results // in a double-free if we construct the xThis object. uno::Reference const xThis(m_wThis); if (!xThis.is()) { // fdo#72695: if UNO object is already dead, don't revive it with event return; } lang::EventObject const ev(xThis); m_EventListeners.disposeAndClear(aGuard, ev); } } } SwXParagraph::SwXParagraph() : m_pImpl( new SwXParagraph::Impl(*this) ) { } SwXParagraph::SwXParagraph( uno::Reference< text::XText > const & xParent, SwTextNode & rTextNode, const sal_Int32 nSelStart, const sal_Int32 nSelEnd) : m_pImpl( new SwXParagraph::Impl(*this, &rTextNode, xParent, nSelStart, nSelEnd)) { } SwXParagraph::~SwXParagraph() { } const SwTextNode * SwXParagraph::GetTextNode() const { return m_pImpl->GetTextNode(); } bool SwXParagraph::IsDescriptor() const { return m_pImpl->IsDescriptor(); } rtl::Reference SwXParagraph::CreateXParagraph(SwDoc & rDoc, SwTextNode *const pTextNode, uno::Reference< text::XText> const& i_xParent, const sal_Int32 nSelStart, const sal_Int32 nSelEnd) { // re-use existing SwXParagraph // #i105557#: do not iterate over the registered clients: race condition rtl::Reference xParagraph; if (pTextNode && (-1 == nSelStart) && (-1 == nSelEnd)) { // only use cache if no selection! xParagraph = pTextNode->GetXParagraph(); } if (xParagraph.is()) { return xParagraph; } // create new SwXParagraph uno::Reference xParentText(i_xParent); if (!xParentText.is() && pTextNode) { SwPosition Pos(*pTextNode); xParentText.set(::sw::CreateParentXText( rDoc, Pos )); } SwXParagraph *const pXPara( pTextNode ? new SwXParagraph(xParentText, *pTextNode, nSelStart, nSelEnd) : new SwXParagraph); // this is why the constructor is private: need to acquire pXPara here xParagraph.set(pXPara); // in order to initialize the weak pointer cache in the core object if (pTextNode && (-1 == nSelStart) && (-1 == nSelEnd)) { pTextNode->SetXParagraph(xParagraph); } // need a permanent Reference to initialize m_wThis pXPara->m_pImpl->m_wThis = xParagraph.get(); return xParagraph; } bool SwXParagraph::SelectPaM(SwPaM & rPaM) { SwTextNode const*const pTextNode( GetTextNode() ); if (!pTextNode) { return false; } rPaM.GetPoint()->Assign( *pTextNode ); // set selection to the whole paragraph rPaM.SetMark(); rPaM.GetMark()->SetContent( pTextNode->GetText().getLength() ); return true; } const uno::Sequence< sal_Int8 > & SwXParagraph::getUnoTunnelId() { static const comphelper::UnoIdInit theSwXParagraphUnoTunnelId; return theSwXParagraphUnoTunnelId.getSeq(); } sal_Int64 SAL_CALL SwXParagraph::getSomething(const uno::Sequence< sal_Int8 >& rId) { return comphelper::getSomethingImpl(rId, this); } OUString SAL_CALL SwXParagraph::getImplementationName() { return "SwXParagraph"; } sal_Bool SAL_CALL SwXParagraph::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } uno::Sequence< OUString > SAL_CALL SwXParagraph::getSupportedServiceNames() { return { "com.sun.star.text.TextContent", "com.sun.star.text.Paragraph", "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" }; } void SwXParagraph::attachToText(SwXText & rParent, SwTextNode & rTextNode) { OSL_ENSURE(m_pImpl->m_bIsDescriptor, "Paragraph is not a descriptor"); if (!m_pImpl->m_bIsDescriptor) return; m_pImpl->m_bIsDescriptor = false; m_pImpl->EndListeningAll(); m_pImpl->StartListening(rTextNode.GetNotifier()); rTextNode.SetXParagraph(this); m_pImpl->m_xParentText = &rParent; if (!m_pImpl->m_sText.isEmpty()) { try { setString(m_pImpl->m_sText); } catch(...){} m_pImpl->m_sText.clear(); } } uno::Reference< beans::XPropertySetInfo > SAL_CALL SwXParagraph::getPropertySetInfo() { SolarMutexGuard g; static uno::Reference< beans::XPropertySetInfo > xRef = m_pImpl->m_rPropSet.getPropertySetInfo(); return xRef; } void SAL_CALL SwXParagraph::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue) { SolarMutexGuard aGuard; m_pImpl->SetPropertyValues_Impl( { rPropertyName }, { rValue } ); } uno::Any SwXParagraph::getPropertyValue(const OUString& rPropertyName) { SolarMutexGuard aGuard; uno::Sequence aPropertyNames { rPropertyName }; const uno::Sequence< uno::Any > aRet = m_pImpl->GetPropertyValues_Impl(aPropertyNames); return aRet.getConstArray()[0]; } void SwXParagraph::Impl::SetPropertyValues_Impl( const uno::Sequence< OUString >& rPropertyNames, const uno::Sequence< uno::Any >& rValues ) { SwTextNode & rTextNode(GetTextNodeOrThrow()); SwPosition aPos( rTextNode ); SwCursor aCursor( aPos, nullptr ); const OUString* pPropertyNames = rPropertyNames.getConstArray(); const uno::Any* pValues = rValues.getConstArray(); const SfxItemPropertyMap &rMap = m_rPropSet.getPropertyMap(); SwParaSelection aParaSel( aCursor ); uno::Sequence< beans::PropertyValue > aValues( rPropertyNames.getLength() ); auto aValuesRange = asNonConstRange(aValues); for (sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); nProp++) { SfxItemPropertyMapEntry const*const pEntry = rMap.getByName( pPropertyNames[nProp] ); if (!pEntry) { throw beans::UnknownPropertyException( "Unknown property: " + pPropertyNames[nProp], static_cast< cppu::OWeakObject * >(&m_rThis)); } if (pEntry->nFlags & beans::PropertyAttribute::READONLY) { throw beans::PropertyVetoException( "Property is read-only: " + pPropertyNames[nProp], static_cast< cppu::OWeakObject * >(&m_rThis)); } aValuesRange[nProp].Name = pPropertyNames[nProp]; aValuesRange[nProp].Value = pValues[nProp]; } SwUnoCursorHelper::SetPropertyValues(aCursor, m_rPropSet, aValues); } void SAL_CALL SwXParagraph::setPropertyValues( const uno::Sequence< OUString >& rPropertyNames, const uno::Sequence< uno::Any >& rValues ) { SolarMutexGuard aGuard; // workaround for bad designed API try { m_pImpl->SetPropertyValues_Impl( rPropertyNames, rValues ); } catch (const beans::UnknownPropertyException &rException) { // wrap the original (here not allowed) exception in // a lang::WrappedTargetException that gets thrown instead. lang::WrappedTargetException aWExc; aWExc.TargetException <<= rException; throw aWExc; } } // Support for DrawingLayer FillStyles for GetPropertyValue() usages void SwXParagraph::Impl::GetSinglePropertyValue_Impl( const SfxItemPropertyMapEntry& rEntry, const SfxItemSet& rSet, uno::Any& rAny ) const { bool bDone(false); switch(rEntry.nWID) { case RES_BACKGROUND: { const std::unique_ptr aOriginalBrushItem(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND)); if(!aOriginalBrushItem->QueryValue(rAny, rEntry.nMemberId)) { OSL_ENSURE(false, "Error getting attribute from RES_BACKGROUND (!)"); } bDone = true; break; } case OWN_ATTR_FILLBMP_MODE: { if (rSet.Get(XATTR_FILLBMP_TILE).GetValue()) { rAny <<= drawing::BitmapMode_REPEAT; } else if (rSet.Get(XATTR_FILLBMP_STRETCH).GetValue()) { rAny <<= drawing::BitmapMode_STRETCH; } else { rAny <<= drawing::BitmapMode_NO_REPEAT; } bDone = true; break; } default: break; } if(bDone) return; // fallback to standard get value implementation used before this helper was created m_rPropSet.getPropertyValue(rEntry, rSet, rAny); if(rEntry.aType == cppu::UnoType::get() && rEntry.aType != rAny.getValueType()) { // since the sfx uInt16 item now exports a sal_Int32, we may have to fix this here sal_Int32 nValue(0); if (rAny >>= nValue) { rAny <<= static_cast(nValue); } } // check for needed metric translation if(!(rEntry.nMoreFlags & PropertyMoreFlags::METRIC_ITEM)) return; bool bDoIt(true); if(XATTR_FILLBMP_SIZEX == rEntry.nWID || XATTR_FILLBMP_SIZEY == rEntry.nWID) { // exception: If these ItemTypes are used, do not convert when these are negative // since this means they are intended as percent values sal_Int32 nValue = 0; if(rAny >>= nValue) { bDoIt = nValue > 0; } } if(bDoIt) { const MapUnit eMapUnit(rSet.GetPool()->GetMetric(rEntry.nWID)); if(eMapUnit != MapUnit::Map100thMM) { SvxUnoConvertToMM(eMapUnit, rAny); } } } uno::Sequence< uno::Any > SwXParagraph::Impl::GetPropertyValues_Impl( const uno::Sequence< OUString > & rPropertyNames ) { SwTextNode & rTextNode(GetTextNodeOrThrow()); uno::Sequence< uno::Any > aValues(rPropertyNames.getLength()); SwPaM aPam( rTextNode ); uno::Any* pValues = aValues.getArray(); const OUString* pPropertyNames = rPropertyNames.getConstArray(); const SfxItemPropertyMap &rMap = m_rPropSet.getPropertyMap(); const SwAttrSet& rAttrSet( rTextNode.GetSwAttrSet() ); for (sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); nProp++) { SfxItemPropertyMapEntry const*const pEntry = rMap.getByName( pPropertyNames[nProp] ); if (!pEntry) { throw beans::UnknownPropertyException( "Unknown property: " + pPropertyNames[nProp], static_cast< cppu::OWeakObject * >(&m_rThis)); } if (! ::sw::GetDefaultTextContentValue( pValues[nProp], pPropertyNames[nProp], pEntry->nWID)) { beans::PropertyState eTemp; const bool bDone = SwUnoCursorHelper::getCursorPropertyValue( *pEntry, aPam, &(pValues[nProp]), eTemp, &rTextNode ); if (!bDone) { GetSinglePropertyValue_Impl(*pEntry, rAttrSet, pValues[nProp]); } } } return aValues; } uno::Sequence< uno::Any > SAL_CALL SwXParagraph::getPropertyValues(const uno::Sequence< OUString >& rPropertyNames) { SolarMutexGuard aGuard; uno::Sequence< uno::Any > aValues; // workaround for bad designed API try { aValues = m_pImpl->GetPropertyValues_Impl( rPropertyNames ); } catch (beans::UnknownPropertyException &) { css::uno::Any anyEx = cppu::getCaughtException(); throw css::lang::WrappedTargetRuntimeException("Unknown property exception caught", static_cast < cppu::OWeakObject * > ( this ), anyEx ); } catch (lang::WrappedTargetException &) { css::uno::Any anyEx = cppu::getCaughtException(); throw css::lang::WrappedTargetRuntimeException("WrappedTargetException caught", static_cast < cppu::OWeakObject * > ( this ), anyEx ); } return aValues; } void SAL_CALL SwXParagraph::addPropertiesChangeListener( const uno::Sequence< OUString >& /*aPropertyNames*/, const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ ) { OSL_FAIL("SwXParagraph::addPropertiesChangeListener(): not implemented"); } void SAL_CALL SwXParagraph::removePropertiesChangeListener( const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ ) { OSL_FAIL("SwXParagraph::removePropertiesChangeListener(): not implemented"); } void SAL_CALL SwXParagraph::firePropertiesChangeEvent( const uno::Sequence< OUString >& /*aPropertyNames*/, const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ ) { OSL_FAIL("SwXParagraph::firePropertiesChangeEvent(): not implemented"); } /* disabled for #i46921# */ uno::Sequence< beans::SetPropertyTolerantFailed > SAL_CALL SwXParagraph::setPropertyValuesTolerant( const uno::Sequence< OUString >& rPropertyNames, const uno::Sequence< uno::Any >& rValues ) { SolarMutexGuard aGuard; if (rPropertyNames.getLength() != rValues.getLength()) { throw lang::IllegalArgumentException(); } SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); //SwNode& rTextNode = pUnoCursor->GetPoint()->GetNode(); //const SwAttrSet& rAttrSet = static_cast(rTextNode).GetSwAttrSet(); //sal_uInt16 nAttrCount = rAttrSet.Count(); const sal_Int32 nProps = rPropertyNames.getLength(); const OUString *pProp = rPropertyNames.getConstArray(); //sal_Int32 nVals = rValues.getLength(); const uno::Any *pValue = rValues.getConstArray(); sal_Int32 nFailed = 0; uno::Sequence< beans::SetPropertyTolerantFailed > aFailed( nProps ); beans::SetPropertyTolerantFailed *pFailed = aFailed.getArray(); // get entry to start with const SfxItemPropertyMap &rPropMap = m_pImpl->m_rPropSet.getPropertyMap(); SwPosition aPos( rTextNode ); SwCursor aCursor( aPos, nullptr ); SwParaSelection aParaSel( aCursor ); for (sal_Int32 i = 0; i < nProps; ++i) { try { pFailed[ nFailed ].Name = pProp[i]; SfxItemPropertyMapEntry const*const pEntry = rPropMap.getByName( pProp[i] ); if (!pEntry) { pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY; } else { // set property value // (compare to SwXParagraph::setPropertyValues) if (pEntry->nFlags & beans::PropertyAttribute::READONLY) { pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::PROPERTY_VETO; } else { SwUnoCursorHelper::SetPropertyValue( aCursor, m_pImpl->m_rPropSet, pProp[i], pValue[i]); } } } catch (beans::UnknownPropertyException &) { // should not occur because property was searched for before TOOLS_WARN_EXCEPTION( "sw", "unexpected exception caught" ); pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY; } catch (lang::IllegalArgumentException &) { pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT; } catch (beans::PropertyVetoException &) { pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::PROPERTY_VETO; } catch (lang::WrappedTargetException &) { pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::WRAPPED_TARGET; } } aFailed.realloc( nFailed ); return aFailed; } uno::Sequence< beans::GetPropertyTolerantResult > SAL_CALL SwXParagraph::getPropertyValuesTolerant( const uno::Sequence< OUString >& rPropertyNames ) { SolarMutexGuard aGuard; const uno::Sequence< beans::GetDirectPropertyTolerantResult > aTmpRes( m_pImpl->GetPropertyValuesTolerant_Impl( rPropertyNames, false ) ); // copy temporary result to final result type const sal_Int32 nLen = aTmpRes.getLength(); uno::Sequence< beans::GetPropertyTolerantResult > aRes( nLen ); std::copy(aTmpRes.begin(), aTmpRes.end(), aRes.getArray()); return aRes; } uno::Sequence< beans::GetDirectPropertyTolerantResult > SAL_CALL SwXParagraph::getDirectPropertyValuesTolerant( const uno::Sequence< OUString >& rPropertyNames ) { SolarMutexGuard aGuard; return m_pImpl->GetPropertyValuesTolerant_Impl( rPropertyNames, true ); } uno::Sequence< beans::GetDirectPropertyTolerantResult > SwXParagraph::Impl::GetPropertyValuesTolerant_Impl( const uno::Sequence< OUString >& rPropertyNames, bool bDirectValuesOnly ) { SolarMutexGuard aGuard; SwTextNode & rTextNode(GetTextNodeOrThrow()); // #i46786# Use SwAttrSet pointer for determining the state. // Use the value SwAttrSet (from the paragraph OR the style) // for determining the actual value(s). const SwAttrSet* pAttrSet = rTextNode.GetpSwAttrSet(); const SwAttrSet& rValueAttrSet = rTextNode.GetSwAttrSet(); sal_Int32 nProps = rPropertyNames.getLength(); uno::Sequence< beans::GetDirectPropertyTolerantResult > aResult( nProps ); beans::GetDirectPropertyTolerantResult *pResult = aResult.getArray(); sal_Int32 nIdx = 0; // get entry to start with const SfxItemPropertyMap &rPropMap = m_rPropSet.getPropertyMap(); for (const OUString& rProp : rPropertyNames) { OSL_ENSURE( nIdx < nProps, "index out of bounds" ); beans::GetDirectPropertyTolerantResult &rResult = pResult[nIdx]; try { rResult.Name = rProp; SfxItemPropertyMapEntry const*const pEntry = rPropMap.getByName( rProp ); if (!pEntry) // property available? { rResult.Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY; } else { // get property state // (compare to SwXParagraph::getPropertyState) bool bAttrSetFetched = true; beans::PropertyState eState = lcl_SwXParagraph_getPropertyState( rTextNode, &pAttrSet, *pEntry, bAttrSetFetched ); rResult.State = eState; rResult.Result = beans::TolerantPropertySetResultType::UNKNOWN_FAILURE; if (!bDirectValuesOnly || (beans::PropertyState_DIRECT_VALUE == eState)) { // get property value // (compare to SwXParagraph::getPropertyValue(s)) uno::Any aValue; if (! ::sw::GetDefaultTextContentValue( aValue, rProp, pEntry->nWID ) ) { SwPaM aPam( rTextNode ); // handle properties that are not part of the attribute // and thus only pretended to be paragraph attributes beans::PropertyState eTemp; const bool bDone = SwUnoCursorHelper::getCursorPropertyValue( *pEntry, aPam, &aValue, eTemp, &rTextNode ); // if not found try the real paragraph attributes... if (!bDone) { GetSinglePropertyValue_Impl(*pEntry, rValueAttrSet, aValue); } } rResult.Value = aValue; rResult.Result = beans::TolerantPropertySetResultType::SUCCESS; nIdx++; } // this assertion should never occur! OSL_ENSURE( nIdx < 1 || pResult[nIdx - 1].Result != beans::TolerantPropertySetResultType::UNKNOWN_FAILURE, "unknown failure while retrieving property" ); } } catch (beans::UnknownPropertyException &) { // should not occur because property was searched for before TOOLS_WARN_EXCEPTION( "sw", "unexpected exception caught" ); rResult.Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY; } catch (lang::IllegalArgumentException &) { rResult.Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT; } catch (beans::PropertyVetoException &) { rResult.Result = beans::TolerantPropertySetResultType::PROPERTY_VETO; } catch (lang::WrappedTargetException &) { rResult.Result = beans::TolerantPropertySetResultType::WRAPPED_TARGET; } } // resize to actually used size aResult.realloc( nIdx ); return aResult; } bool ::sw::GetDefaultTextContentValue( uno::Any& rAny, std::u16string_view rPropertyName, sal_uInt16 nWID) { if(!nWID) { if(rPropertyName == u"" UNO_NAME_ANCHOR_TYPE) nWID = FN_UNO_ANCHOR_TYPE; else if(rPropertyName == u"" UNO_NAME_ANCHOR_TYPES) nWID = FN_UNO_ANCHOR_TYPES; else if(rPropertyName == u"" UNO_NAME_TEXT_WRAP) nWID = FN_UNO_TEXT_WRAP; else return false; } switch(nWID) { case FN_UNO_TEXT_WRAP: rAny <<= text::WrapTextMode_NONE; break; case FN_UNO_ANCHOR_TYPE: rAny <<= text::TextContentAnchorType_AT_PARAGRAPH; break; case FN_UNO_ANCHOR_TYPES: { uno::Sequence aTypes { text::TextContentAnchorType_AT_PARAGRAPH }; rAny <<= aTypes; } break; default: return false; } return true; } void SAL_CALL SwXParagraph::addPropertyChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/) { OSL_FAIL("SwXParagraph::addPropertyChangeListener(): not implemented"); } void SAL_CALL SwXParagraph::removePropertyChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/) { OSL_FAIL("SwXParagraph::removePropertyChangeListener(): not implemented"); } void SAL_CALL SwXParagraph::addVetoableChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/) { OSL_FAIL("SwXParagraph::addVetoableChangeListener(): not implemented"); } void SAL_CALL SwXParagraph::removeVetoableChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/) { OSL_FAIL("SwXParagraph::removeVetoableChangeListener(): not implemented"); } static beans::PropertyState lcl_SwXParagraph_getPropertyState( const SwTextNode& rTextNode, const SwAttrSet** ppSet, const SfxItemPropertyMapEntry& rEntry, bool &rAttrSetFetched) { beans::PropertyState eRet(beans::PropertyState_DEFAULT_VALUE); if(!(*ppSet) && !rAttrSetFetched) { (*ppSet) = rTextNode.GetpSwAttrSet(); rAttrSetFetched = true; } SwPosition aPos(rTextNode); SwPaM aPam(aPos); bool bDone(false); switch(rEntry.nWID) { case FN_UNO_NUM_RULES: { // if numbering is set, return it; else do nothing SwUnoCursorHelper::getNumberingProperty(aPam,eRet,nullptr); bDone = true; break; } case FN_UNO_LIST_ID: { SwNumRule* pNumRule = rTextNode.GetNumRule(); if (pNumRule && pNumRule->HasContinueList()) { eRet = beans::PropertyState_DIRECT_VALUE; } bDone = true; break; } case FN_UNO_ANCHOR_TYPES: { bDone = true; break; } case RES_ANCHOR: { bDone = (MID_SURROUND_SURROUNDTYPE == rEntry.nMemberId); break; } case RES_SURROUND: { bDone = (MID_ANCHOR_ANCHORTYPE == rEntry.nMemberId); break; } case FN_UNO_PARA_STYLE: case FN_UNO_PARA_CONDITIONAL_STYLE_NAME: { SwFormatColl* pFormat = SwUnoCursorHelper::GetCurTextFormatColl(aPam,rEntry.nWID == FN_UNO_PARA_CONDITIONAL_STYLE_NAME); eRet = pFormat ? beans::PropertyState_DIRECT_VALUE : beans::PropertyState_AMBIGUOUS_VALUE; bDone = true; break; } case FN_UNO_PAGE_STYLE: { OUString sVal; SwUnoCursorHelper::GetCurPageStyle( aPam, sVal ); eRet = !sVal.isEmpty() ? beans::PropertyState_DIRECT_VALUE : beans::PropertyState_AMBIGUOUS_VALUE; bDone = true; break; } // DrawingLayer PropertyStyle support case OWN_ATTR_FILLBMP_MODE: { if(*ppSet) { if(SfxItemState::SET == (*ppSet)->GetItemState(XATTR_FILLBMP_STRETCH, false) || SfxItemState::SET == (*ppSet)->GetItemState(XATTR_FILLBMP_TILE, false)) { eRet = beans::PropertyState_DIRECT_VALUE; } else { eRet = beans::PropertyState_AMBIGUOUS_VALUE; } bDone = true; } break; } case RES_BACKGROUND: { if(*ppSet) { if (SWUnoHelper::needToMapFillItemsToSvxBrushItemTypes(**ppSet, rEntry.nMemberId)) { eRet = beans::PropertyState_DIRECT_VALUE; } bDone = true; } break; } } if(!bDone) { if((*ppSet) && SfxItemState::SET == (*ppSet)->GetItemState(rEntry.nWID, false)) { eRet = beans::PropertyState_DIRECT_VALUE; } } return eRet; } beans::PropertyState SAL_CALL SwXParagraph::getPropertyState(const OUString& rPropertyName) { SolarMutexGuard aGuard; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); const SwAttrSet* pSet = nullptr; SfxItemPropertyMapEntry const*const pEntry = m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName); if (!pEntry) { throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast(this)); } bool bDummy = false; const beans::PropertyState eRet = lcl_SwXParagraph_getPropertyState(rTextNode, &pSet, *pEntry, bDummy); return eRet; } uno::Sequence< beans::PropertyState > SAL_CALL SwXParagraph::getPropertyStates( const uno::Sequence< OUString >& PropertyNames) { SolarMutexGuard aGuard; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); const OUString* pNames = PropertyNames.getConstArray(); uno::Sequence< beans::PropertyState > aRet(PropertyNames.getLength()); beans::PropertyState* pStates = aRet.getArray(); const SfxItemPropertyMap &rMap = m_pImpl->m_rPropSet.getPropertyMap(); const SwAttrSet* pSet = nullptr; bool bAttrSetFetched = false; for (sal_Int32 i = 0, nEnd = PropertyNames.getLength(); i < nEnd; ++i, ++pStates, ++pNames) { SfxItemPropertyMapEntry const*const pEntry = rMap.getByName( *pNames ); if (!pEntry) { throw beans::UnknownPropertyException( "Unknown property: " + *pNames, static_cast(this)); } if (bAttrSetFetched && !pSet && isATR(pEntry->nWID)) { *pStates = beans::PropertyState_DEFAULT_VALUE; } else { *pStates = lcl_SwXParagraph_getPropertyState( rTextNode, &pSet, *pEntry, bAttrSetFetched ); } } return aRet; } void SAL_CALL SwXParagraph::setPropertyToDefault(const OUString& rPropertyName) { SolarMutexGuard aGuard; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); SwPosition aPos( rTextNode ); SwCursor aCursor( aPos, nullptr ); if (rPropertyName == UNO_NAME_ANCHOR_TYPE || rPropertyName == UNO_NAME_ANCHOR_TYPES || rPropertyName == UNO_NAME_TEXT_WRAP) { return; } // select paragraph SwParaSelection aParaSel( aCursor ); SfxItemPropertyMapEntry const*const pEntry = m_pImpl->m_rPropSet.getPropertyMap().getByName( rPropertyName ); if (!pEntry) { throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast(this)); } if (pEntry->nFlags & beans::PropertyAttribute::READONLY) { throw uno::RuntimeException( "Property is read-only: " + rPropertyName, static_cast(this)); } const bool bBelowFrameAtrEnd(pEntry->nWID < RES_FRMATR_END); const bool bDrawingLayerRange(XATTR_FILL_FIRST <= pEntry->nWID && XATTR_FILL_LAST >= pEntry->nWID); if(bBelowFrameAtrEnd || bDrawingLayerRange) { o3tl::sorted_vector aWhichIds; // For FillBitmapMode two IDs have to be reset (!) if(OWN_ATTR_FILLBMP_MODE == pEntry->nWID) { aWhichIds.insert(XATTR_FILLBMP_STRETCH); aWhichIds.insert(XATTR_FILLBMP_TILE); } else { aWhichIds.insert(pEntry->nWID); } if (pEntry->nWID < RES_PARATR_BEGIN) { aCursor.GetDoc().ResetAttrs(aCursor, true, aWhichIds); } else { // for paragraph attributes the selection must be extended // to paragraph boundaries SwPosition aStart( *aCursor.Start() ); SwPosition aEnd ( *aCursor.End() ); auto pTemp( aCursor.GetDoc().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); } pTemp->GetDoc().ResetAttrs(*pTemp, true, aWhichIds); } } else { SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, aCursor); } } uno::Any SAL_CALL SwXParagraph::getPropertyDefault(const OUString& rPropertyName) { SolarMutexGuard g; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); uno::Any aRet; if (::sw::GetDefaultTextContentValue(aRet, rPropertyName)) { return aRet; } SfxItemPropertyMapEntry const*const pEntry = m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName); if (!pEntry) { throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast(this)); } const bool bBelowFrameAtrEnd(pEntry->nWID < RES_FRMATR_END); const bool bDrawingLayerRange(XATTR_FILL_FIRST <= pEntry->nWID && XATTR_FILL_LAST >= pEntry->nWID); if(bBelowFrameAtrEnd || bDrawingLayerRange) { const SfxPoolItem& rDefItem = rTextNode.GetDoc().GetAttrPool().GetDefaultItem(pEntry->nWID); rDefItem.QueryValue(aRet, pEntry->nMemberId); } return aRet; } void SAL_CALL SwXParagraph::attach(const uno::Reference< text::XTextRange > & /*xTextRange*/) { // SwXParagraph will only created in order to be inserted by // 'insertTextContentBefore' or 'insertTextContentAfter' therefore // they cannot be attached throw uno::RuntimeException(); } uno::Reference< text::XTextRange > SAL_CALL SwXParagraph::getAnchor() { SolarMutexGuard aGuard; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); SwPosition aPos( rTextNode ); SwCursor aCursor( aPos, nullptr ); // select paragraph SwParaSelection aParaSel( aCursor ); const uno::Reference< text::XTextRange > xRet = new SwXTextRange(aCursor, m_pImpl->m_xParentText); return xRet; } void SAL_CALL SwXParagraph::dispose() { SolarMutexGuard aGuard; SwTextNode *const pTextNode( m_pImpl->GetTextNode() ); if (pTextNode) { SwCursor aCursor( SwPosition( *pTextNode ), nullptr ); pTextNode->GetDoc().getIDocumentContentOperations().DelFullPara(aCursor); lang::EventObject const ev(static_cast< ::cppu::OWeakObject&>(*this)); std::unique_lock aGuard2(m_pImpl->m_Mutex); m_pImpl->m_EventListeners.disposeAndClear(aGuard2, ev); } } void SAL_CALL SwXParagraph::addEventListener( const uno::Reference< lang::XEventListener > & xListener) { // no need to lock here as m_pImpl is const and container threadsafe std::unique_lock aGuard(m_pImpl->m_Mutex); m_pImpl->m_EventListeners.addInterface(aGuard, xListener); } void SAL_CALL SwXParagraph::removeEventListener( const uno::Reference< lang::XEventListener > & xListener) { // no need to lock here as m_pImpl is const and container threadsafe std::unique_lock aGuard(m_pImpl->m_Mutex); m_pImpl->m_EventListeners.removeInterface(aGuard, xListener); } uno::Reference< container::XEnumeration > SAL_CALL SwXParagraph::createEnumeration() { SolarMutexGuard aGuard; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); SwPaM aPam ( rTextNode ); const uno::Reference< container::XEnumeration > xRef = new SwXTextPortionEnumeration(aPam, m_pImpl->m_xParentText, m_pImpl->m_nSelectionStartPos, m_pImpl->m_nSelectionEndPos); return xRef; } /// tries to return less data, but may return more than just text fields rtl::Reference< SwXTextPortionEnumeration > SwXParagraph::createTextFieldsEnumeration() { SolarMutexGuard aGuard; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); SwPaM aPam ( rTextNode ); return new SwXTextPortionEnumeration(aPam, m_pImpl->m_xParentText, m_pImpl->m_nSelectionStartPos, m_pImpl->m_nSelectionEndPos, /*bOnlyTextFields*/true); } uno::Type SAL_CALL SwXParagraph::getElementType() { return cppu::UnoType::get(); } sal_Bool SAL_CALL SwXParagraph::hasElements() { SolarMutexGuard aGuard; return GetTextNode() != nullptr; } uno::Reference< text::XText > SAL_CALL SwXParagraph::getText() { SolarMutexGuard g; return m_pImpl->m_xParentText; } uno::Reference< text::XTextRange > SAL_CALL SwXParagraph::getStart() { SolarMutexGuard aGuard; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); SwPosition aPos( rTextNode ); SwCursor aCursor( aPos, nullptr ); SwParaSelection aParaSel( aCursor ); SwPaM aPam( *aCursor.Start() ); uno::Reference< text::XText > xParent = getText(); const uno::Reference< text::XTextRange > xRet = new SwXTextRange(aPam, xParent); return xRet; } uno::Reference< text::XTextRange > SAL_CALL SwXParagraph::getEnd() { SolarMutexGuard aGuard; SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); SwPosition aPos( rTextNode ); SwCursor aCursor( aPos, nullptr ); SwParaSelection aParaSel( aCursor ); SwPaM aPam( *aCursor.End() ); uno::Reference< text::XText > xParent = getText(); const uno::Reference< text::XTextRange > xRet = new SwXTextRange(aPam, xParent); return xRet; } OUString SAL_CALL SwXParagraph::getString() { SolarMutexGuard aGuard; OUString aRet; SwTextNode const*const pTextNode( GetTextNode() ); if (pTextNode) { SwPosition aPos( *pTextNode ); SwCursor aCursor( aPos, nullptr ); SwParaSelection aParaSel( aCursor ); SwUnoCursorHelper::GetTextFromPam(aCursor, aRet); } else if (m_pImpl->IsDescriptor()) { aRet = m_pImpl->m_sText; } else { // Seems object is being disposed or some other problem occurs. // Anyway from user point of view object still exist, so on that level this is not an error SAL_WARN("sw.uno", "getString() for invalid paragraph called. Returning empty string."); } return aRet; } void SAL_CALL SwXParagraph::setString(const OUString& aString) { SolarMutexGuard aGuard; SwTextNode const*const pTextNode( GetTextNode() ); if (pTextNode) { SwPosition aPos( *pTextNode ); SwCursor aCursor( aPos, nullptr ); if (!SwUnoCursorHelper::IsStartOfPara(aCursor)) { aCursor.MovePara(GoCurrPara, fnParaStart); } SwUnoCursorHelper::SelectPam(aCursor, true); if (pTextNode->GetText().getLength()) { aCursor.MovePara(GoCurrPara, fnParaEnd); } SwUnoCursorHelper::SetString(aCursor, aString); SwUnoCursorHelper::SelectPam(aCursor, false); } else if (m_pImpl->IsDescriptor()) { m_pImpl->m_sText = aString; } else { throw uno::RuntimeException(); } } uno::Reference< container::XEnumeration > SAL_CALL SwXParagraph::createContentEnumeration(const OUString& rServiceName) { SolarMutexGuard g; if ( rServiceName != "com.sun.star.text.TextContent" ) { throw uno::RuntimeException(); } SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow()); SwPaM aPam( rTextNode ); uno::Reference< container::XEnumeration > xRet = SwXParaFrameEnumeration::Create(aPam, PARAFRAME_PORTION_PARAGRAPH); return xRet; } uno::Sequence< OUString > SAL_CALL SwXParagraph::getAvailableServiceNames() { uno::Sequence aRet { "com.sun.star.text.TextContent" }; return aRet; } // MetadatableMixin ::sfx2::Metadatable* SwXParagraph::GetCoreObject() { SwTextNode *const pTextNode( m_pImpl->GetTextNode() ); return pTextNode; } uno::Reference SwXParagraph::GetModel() { SwTextNode *const pTextNode( m_pImpl->GetTextNode() ); if (pTextNode) { SwDocShell const*const pShell( pTextNode->GetDoc().GetDocShell() ); return pShell ? pShell->GetModel() : nullptr; } return nullptr; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */