sw/CppunitTest_sw_apitests.mk | 1 sw/inc/fmtfld.hxx | 2 sw/qa/api/SwXTextField.cxx | 89 ++++++++++++++++++ sw/qa/python/xtext.py | 8 + sw/source/core/txtnode/atrfld.cxx | 4 sw/source/core/unocore/unofield.cxx | 174 ++++++++++++++++-------------------- 6 files changed, 177 insertions(+), 101 deletions(-)
New commits: commit e18359445fabad9ba1a704600e9ee327112cc6ae Author: Bjoern Michaelsen <bjoern.michael...@libreoffice.org> AuthorDate: Sun Apr 14 13:33:35 2019 +0200 Commit: Björn Michaelsen <bjoern.michael...@libreoffice.org> CommitDate: Sun May 5 22:27:54 2019 +0200 [API CHANGE] SwXTextField: no more SwModify/SwClient - fix unittest assuming the double-insertion of annotation throwing an IllegalArgumentException was intentional - remove SwModify/SwClient in SwXTextField - also: add basic C++ api test Change-Id: Ia4657dc65dfadc3e975bdf409bd5e43413ea1f5a Reviewed-on: https://gerrit.libreoffice.org/71452 Tested-by: Jenkins Reviewed-by: Björn Michaelsen <bjoern.michael...@libreoffice.org> diff --git a/sw/CppunitTest_sw_apitests.mk b/sw/CppunitTest_sw_apitests.mk index ca2c919e0aa6..370ed779d8b9 100644 --- a/sw/CppunitTest_sw_apitests.mk +++ b/sw/CppunitTest_sw_apitests.mk @@ -16,6 +16,7 @@ $(eval $(call gb_CppunitTest_use_external,sw_apitests,boost_headers)) $(eval $(call gb_CppunitTest_add_exception_objects,sw_apitests, \ sw/qa/api/SwXDocumentIndex \ sw/qa/api/SwXDocumentSettings \ + sw/qa/api/SwXTextField \ sw/qa/api/SwXTextTable \ )) diff --git a/sw/inc/fmtfld.hxx b/sw/inc/fmtfld.hxx index b144243ec4c9..d9eafeea82a3 100644 --- a/sw/inc/fmtfld.hxx +++ b/sw/inc/fmtfld.hxx @@ -36,7 +36,7 @@ namespace com { namespace sun { namespace star { namespace text { class XTextFie // ATT_FLD class SW_DLLPUBLIC SwFormatField : public SfxPoolItem - , public SwModify + , public sw::BroadcastingModify , public SfxBroadcaster { friend void InitCore(); diff --git a/sw/qa/api/SwXTextField.cxx b/sw/qa/api/SwXTextField.cxx new file mode 100644 index 000000000000..1de775d1e3d7 --- /dev/null +++ b/sw/qa/api/SwXTextField.cxx @@ -0,0 +1,89 @@ +/* -*- 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/. + */ + +#include <test/bootstrapfixture.hxx> +#include <test/lang/xcomponent.hxx> +#include <unotest/macros_test.hxx> + +#include <com/sun/star/frame/Desktop.hpp> + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextContent.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/XTextField.hpp> +#include <com/sun/star/text/XDependentTextField.hpp> +#include <com/sun/star/text/XTextCursor.hpp> + +#include <comphelper/processfactory.hxx> + +using namespace css; +using namespace css::uno; +using namespace css::beans; + +namespace +{ +/** + * Initial tests for SwXTextField. + */ +struct SwXTextField final : public test::BootstrapFixture, + public unotest::MacrosTest, + public apitest::XComponent +{ + virtual void setUp() override; + + Reference<XInterface> init() override; + void triggerDesktopTerminate() override; + + CPPUNIT_TEST_SUITE(SwXTextField); + CPPUNIT_TEST(testAddEventListener); + CPPUNIT_TEST(testRemoveEventListener); + //CPPUNIT_TEST(testDisposedByDesktopTerminate); + CPPUNIT_TEST_SUITE_END(); +}; + +void SwXTextField::setUp() +{ + test::BootstrapFixture::setUp(); + mxDesktop.set( + frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory()))); +} + +void SwXTextField::triggerDesktopTerminate() { mxDesktop->terminate(); } + +Reference<XInterface> SwXTextField::init() +{ + auto xComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"); + CPPUNIT_ASSERT(xComponent.is()); + Reference<text::XTextDocument> xTextDocument(xComponent, UNO_QUERY_THROW); + Reference<lang::XMultiServiceFactory> xMSF(xComponent, UNO_QUERY_THROW); + + Reference<XPropertySet> xFieldMaster( + xMSF->createInstance("com.sun.star.text.FieldMaster.Database"), UNO_QUERY_THROW); + xFieldMaster->setPropertyValue("DataBaseName", makeAny(OUString("Address Book File"))); + xFieldMaster->setPropertyValue("DataTableName", makeAny(OUString("address"))); + xFieldMaster->setPropertyValue("DataColumnName", makeAny(OUString("FIRSTNAME"))); + + Reference<text::XDependentTextField> xField( + xMSF->createInstance("com.sun.star.text.TextField.Database"), UNO_QUERY_THROW); + xField->attachTextFieldMaster(xFieldMaster); + Reference<text::XText> xText = xTextDocument->getText(); + Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + Reference<text::XTextContent> xFieldAsContent(xField, UNO_QUERY_THROW); + xText->insertTextContent(xCursor, xFieldAsContent, false); + return Reference<XInterface>(xField, UNO_QUERY_THROW); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(SwXTextField); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/python/xtext.py b/sw/qa/python/xtext.py index d137cb1f69ce..b8bc25d90e92 100644 --- a/sw/qa/python/xtext.py +++ b/sw/qa/python/xtext.py @@ -37,9 +37,11 @@ class TestXText(unittest.TestCase): # And the same once again, actually not inserted x_text.insertTextContent(x_cursor, x_annotation, False) - # Exception if we try to replace object by itself - with self.assertRaises(IllegalArgumentException): - x_text.insertTextContent(x_cursor, x_annotation, True) + # no exception if we try to replace object by itself: + # this did throw in the past, but only because the inserted + # UNO annotation had a core object assigned, but no document + # which insertTextContent then didnt like on another call. + x_text.insertTextContent(x_cursor, x_annotation, True) # We expect just one annotation actually self.check_annotations(["John Doe"]) diff --git a/sw/source/core/txtnode/atrfld.cxx b/sw/source/core/txtnode/atrfld.cxx index 3e7ffb0604fa..6f433b2056f5 100644 --- a/sw/source/core/txtnode/atrfld.cxx +++ b/sw/source/core/txtnode/atrfld.cxx @@ -47,7 +47,6 @@ // constructor for default item in attribute-pool SwFormatField::SwFormatField( sal_uInt16 nWhich ) : SfxPoolItem( nWhich ) - , SwModify(nullptr) , SfxBroadcaster() , mpTextField( nullptr ) { @@ -55,11 +54,11 @@ SwFormatField::SwFormatField( sal_uInt16 nWhich ) SwFormatField::SwFormatField( const SwField &rField ) : SfxPoolItem( RES_TXTATR_FIELD ) - , SwModify( rField.GetTyp() ) , SfxBroadcaster() , mpField( rField.CopyField() ) , mpTextField( nullptr ) { + rField.GetTyp()->Add(this); if ( mpField->GetTyp()->Which() == SwFieldIds::Input ) { // input field in-place editing @@ -93,7 +92,6 @@ SwFormatField::SwFormatField( const SwField &rField ) // corrected SwFormatField::SwFormatField( const SwFormatField& rAttr ) : SfxPoolItem( rAttr ) - , SwModify(nullptr) , SfxBroadcaster() , mpTextField( nullptr ) { diff --git a/sw/source/core/unocore/unofield.cxx b/sw/source/core/unocore/unofield.cxx index 14f8f3ca0b98..63614495ab9c 100644 --- a/sw/source/core/unocore/unofield.cxx +++ b/sw/source/core/unocore/unofield.cxx @@ -1117,39 +1117,37 @@ struct SwFieldProperties_Impl }; class SwXTextField::Impl - : public SwClient, public SvtListener + : public SvtListener { private: ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 SwFieldType* m_pFieldType; + SwFormatField* m_pFormatField; public: uno::WeakReference<uno::XInterface> m_wThis; ::comphelper::OInterfaceContainerHelper2 m_EventListeners; - SwFormatField const* m_pFormatField; - SwDoc * m_pDoc; + SwDoc* m_pDoc; rtl::Reference<SwTextAPIObject> m_xTextObject; - - bool m_bIsDescriptor; - bool m_bCallUpdate; - SwServiceType const m_nServiceId; - OUString m_sTypeName; + bool m_bCallUpdate; + SwServiceType const m_nServiceId; + OUString m_sTypeName; std::unique_ptr<SwFieldProperties_Impl> m_pProps; - Impl(SwDoc *const pDoc, SwFormatField *const pFormat, - SwServiceType nServiceId) - : SwClient(pFormat) + Impl(SwDoc *const pDoc, SwFormatField *const pFormat, SwServiceType nServiceId) + : m_pFormatField(pFormat) , m_EventListeners(m_Mutex) - , m_pFormatField(pFormat) , m_pDoc(pDoc) - , m_bIsDescriptor(pFormat == nullptr) , m_bCallUpdate(false) , m_nServiceId(pFormat ? lcl_GetServiceForField(*pFormat->GetField()) : nServiceId) , m_pProps(pFormat ? nullptr : new SwFieldProperties_Impl) - { } + { + if(m_pFormatField) + StartListening(m_pFormatField->GetNotifier()); + } virtual ~Impl() override { @@ -1159,21 +1157,37 @@ public: } } + void SetFormatField(SwFormatField* pFormatField, SwDoc* pDoc) + { + m_pFormatField = pFormatField; + m_pDoc = pDoc; + if(m_pFormatField) + { + EndListeningAll(); + StartListening(m_pFormatField->GetNotifier()); + } + } + SwFormatField* GetFormatField() + { + return m_pFormatField; + } + bool IsDescriptor() const + { + return !m_pFormatField; + } void Invalidate(); - const SwField* GetField() const; + const SwField* GetField() const; - SwFieldType* GetFieldType() + SwFieldType* GetFieldType() const { - if (m_bIsDescriptor) + if (IsDescriptor()) return m_pFieldType; - if (!GetRegisteredIn()) - throw uno::RuntimeException(); return m_pFormatField->GetField()->GetTyp(); } void SetFieldType(SwFieldType& rType) { - SvtListener::EndListeningAll(); + EndListeningAll(); m_pFieldType = &rType; StartListening(m_pFieldType->GetNotifier()); } @@ -1182,9 +1196,6 @@ public: SvtListener::EndListeningAll(); m_pFieldType = nullptr; } -protected: - // SwClient - virtual void Modify(SfxPoolItem const* pOld, SfxPoolItem const* pNew) override; virtual void Notify(const SfxHint&) override; }; @@ -1292,12 +1303,8 @@ void SwXTextField::TransmuteLeadToInputField(SwSetExpField & rField) uno::Reference<lang::XUnoTunnel>(xField, uno::UNO_QUERY_THROW) ->getSomething(getUnoTunnelId()))) : nullptr; - if (xField.is()) - { - assert(pXField->m_pImpl->m_pFormatField == rField.GetFormatField()); - rField.GetFormatField()->Remove(pXField->m_pImpl.get()); - pXField->m_pImpl->m_pFormatField = nullptr; - } + if (pXField) + pXField->m_pImpl->SetFormatField(nullptr, nullptr); SwTextField *const pOldAttr(rField.GetFormatField()->GetTextField()); SwSetExpField tempField(rField); tempField.SetInputFlag(!rField.GetInputFlag()); @@ -1323,8 +1330,7 @@ void SwXTextField::TransmuteLeadToInputField(SwSetExpField & rField) assert(static_cast<SwSetExpField const*>(rNewFormat.GetField())->GetInputFlag() == (dynamic_cast<SwTextInputField const*>(pNewAttr) != nullptr)); if (xField.is()) { - pXField->m_pImpl->m_pFormatField = &rNewFormat; - const_cast<SwFormatField&>(rNewFormat).Add(pXField->m_pImpl.get()); + pXField->m_pImpl->SetFormatField(const_cast<SwFormatField*>(&rNewFormat), rNode.GetDoc()); const_cast<SwFormatField&>(rNewFormat).SetXTextField(xField); } } @@ -1334,7 +1340,7 @@ void SAL_CALL SwXTextField::attachTextFieldMaster( { SolarMutexGuard aGuard; - if (!m_pImpl->m_bIsDescriptor) + if (!m_pImpl->IsDescriptor()) throw uno::RuntimeException(); uno::Reference< lang::XUnoTunnel > xMasterTunnel(xFieldMaster, uno::UNO_QUERY); if (!xMasterTunnel.is()) @@ -1378,7 +1384,7 @@ void SAL_CALL SwXTextField::attach( const uno::Reference< text::XTextRange > & xTextRange) { SolarMutexGuard aGuard; - if (m_pImpl->m_bIsDescriptor) + if (m_pImpl->IsDescriptor()) { uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY); SwXTextRange* pRange = nullptr; @@ -2005,7 +2011,7 @@ void SAL_CALL SwXTextField::attach( throw uno::RuntimeException("no SwTextAttr inserted?"); // could theoretically happen, if paragraph is full const SwFormatField& rField = pTextAttr->GetFormatField(); - m_pImpl->m_pFormatField = &rField; + m_pImpl->SetFormatField(const_cast<SwFormatField*>(&rField), pDoc); if ( pTextAttr->Which() == RES_TXTATR_ANNOTATION && *aPam.GetPoint() != *aPam.GetMark() ) @@ -2022,18 +2028,16 @@ void SAL_CALL SwXTextField::attach( xField.reset(); - assert(m_pImpl->m_pFormatField); + assert(m_pImpl->GetFormatField()); m_pImpl->m_pDoc = pDoc; - const_cast<SwFormatField *>(m_pImpl->m_pFormatField)->Add(m_pImpl.get()); - const_cast<SwFormatField *>(m_pImpl->m_pFormatField)->SetXTextField(this); + m_pImpl->GetFormatField()->SetXTextField(this); m_pImpl->m_wThis = *this; - m_pImpl->m_bIsDescriptor = false; m_pImpl->ClearFieldType(); m_pImpl->m_pProps.reset(); if (m_pImpl->m_bCallUpdate) update(); } - else if ( m_pImpl->m_pFormatField != nullptr + else if ( !m_pImpl->IsDescriptor() && m_pImpl->m_pDoc != nullptr && m_pImpl->m_nServiceId == SwServiceType::FieldTypeAnnotation ) { @@ -2047,14 +2051,14 @@ void SAL_CALL SwXTextField::attach( { UnoActionContext aCont( m_pImpl->m_pDoc ); // insert copy of annotation at new text range - std::unique_ptr<SwPostItField> pPostItField(static_cast< SwPostItField* >(m_pImpl->m_pFormatField->GetField()->CopyField().release())); + std::unique_ptr<SwPostItField> pPostItField(static_cast< SwPostItField* >(m_pImpl->GetFormatField()->GetField()->CopyField().release())); SwFormatField aFormatField( *pPostItField ); pPostItField.reset(); SwPaM aEnd( *aIntPam.End(), *aIntPam.End() ); m_pImpl->m_pDoc->getIDocumentContentOperations().InsertPoolItem( aEnd, aFormatField ); // delete former annotation { - const SwTextField* pTextField = m_pImpl->m_pFormatField->GetTextField(); + const SwTextField* pTextField = m_pImpl->GetFormatField()->GetTextField(); SwTextNode& rTextNode = *pTextField->GetpTextNode(); SwPaM aPam( rTextNode, pTextField->GetStart() ); aPam.SetMark(); @@ -2066,7 +2070,7 @@ void SAL_CALL SwXTextField::attach( SwTextField* pTextAttr = aEnd.GetNode().GetTextNode()->GetFieldTextAttrAt( aEnd.End()->nContent.GetIndex()-1, true ); if ( pTextAttr != nullptr ) { - m_pImpl->m_pFormatField = &pTextAttr->GetFormatField(); + m_pImpl->SetFormatField(const_cast<SwFormatField*>(&pTextAttr->GetFormatField()), m_pImpl->m_pDoc); if ( *aIntPam.GetPoint() != *aIntPam.GetMark() ) { @@ -2097,7 +2101,7 @@ SwXTextField::getAnchor() if (!pField) return nullptr; - const SwTextField* pTextField = m_pImpl->m_pFormatField->GetTextField(); + const SwTextField* pTextField = m_pImpl->GetFormatField()->GetTextField(); if (!pTextField) throw uno::RuntimeException(); @@ -2130,12 +2134,11 @@ void SAL_CALL SwXTextField::dispose() { SolarMutexGuard aGuard; SwField const*const pField = m_pImpl->GetField(); - if(pField) + if(pField && m_pImpl->m_pDoc) { UnoActionContext aContext(m_pImpl->m_pDoc); - - assert(m_pImpl->m_pFormatField->GetTextField() && "<SwXTextField::dispose()> - missing <SwTextField> --> crash"); - SwTextField::DeleteTextField(*(m_pImpl->m_pFormatField->GetTextField())); + assert(m_pImpl->GetFormatField()->GetTextField() && "<SwXTextField::dispose()> - missing <SwTextField> --> crash"); + SwTextField::DeleteTextField(*(m_pImpl->GetFormatField()->GetTextField())); } if (m_pImpl->m_xTextObject.is()) @@ -2143,6 +2146,7 @@ void SAL_CALL SwXTextField::dispose() m_pImpl->m_xTextObject->DisposeEditSource(); m_pImpl->m_xTextObject.clear(); } + m_pImpl->Invalidate(); } void SAL_CALL SwXTextField::addEventListener( @@ -2213,7 +2217,7 @@ SwXTextField::setPropertyValue( { SwDoc * pDoc = m_pImpl->m_pDoc; assert(pDoc); - const SwTextField* pTextField = m_pImpl->m_pFormatField->GetTextField(); + const SwTextField* pTextField = m_pImpl->GetFormatField()->GetTextField(); if(!pTextField) throw uno::RuntimeException(); SwPosition aPosition( pTextField->GetTextNode() ); @@ -2222,16 +2226,16 @@ SwXTextField::setPropertyValue( } //#i100374# notify SwPostIt about new field content - if (SwFieldIds::Postit == nWhich && m_pImpl->m_pFormatField) + if (SwFieldIds::Postit == nWhich && !m_pImpl->IsDescriptor()) { - const_cast<SwFormatField*>(m_pImpl->m_pFormatField)->Broadcast( + m_pImpl->GetFormatField()->Broadcast( SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::CHANGED )); } // fdo#42073 notify SwTextField about changes of the expanded string - if (m_pImpl->m_pFormatField->GetTextField()) + if (m_pImpl->GetFormatField()->GetTextField()) { - m_pImpl->m_pFormatField->GetTextField()->ExpandTextField(); + m_pImpl->GetFormatField()->GetTextField()->ExpandTextField(); } //#i100374# changing a document field should set the modify flag @@ -2396,9 +2400,9 @@ uno::Any SAL_CALL SwXTextField::getPropertyValue(const OUString& rPropertyName) // get text node for the text field const SwFormatField *pFieldFormat = - (m_pImpl->GetField()) ? m_pImpl->m_pFormatField : nullptr; + (m_pImpl->GetField()) ? m_pImpl->GetFormatField() : nullptr; const SwTextField* pTextField = pFieldFormat - ? m_pImpl->m_pFormatField->GetTextField() : nullptr; + ? m_pImpl->GetFormatField()->GetTextField() : nullptr; if(!pTextField) throw uno::RuntimeException(); const SwTextNode& rTextNode = pTextField->GetTextNode(); @@ -2586,7 +2590,7 @@ void SAL_CALL SwXTextField::update() default: break; } // Text formatting has to be triggered. - const_cast<SwFormatField*>(m_pImpl->m_pFormatField)->ModifyNotification(nullptr, nullptr); + m_pImpl->GetFormatField()->ModifyNotification(nullptr, nullptr); } else m_pImpl->m_bCallUpdate = true; @@ -2637,56 +2641,38 @@ uno::Sequence< OUString > SAL_CALL SwXTextField::getSupportedServiceNames() void SwXTextField::Impl::Invalidate() { - if (GetRegisteredIn()) - { - SwClient::EndListeningAll(); - m_pFormatField = nullptr; - m_pDoc = nullptr; - uno::Reference<uno::XInterface> 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(ev); - } -} - -void SwXTextField::Impl::Modify( - SfxPoolItem const*const pOld, SfxPoolItem const*const pNew) -{ - switch( pOld ? pOld->Which() : 0 ) - { - case RES_REMOVE_UNO_OBJECT: - case RES_OBJECTDYING: - if( static_cast<void*>(GetRegisteredIn()) == static_cast<const SwPtrMsgPoolItem *>(pOld)->pObject ) - Invalidate(); - break; - - case RES_FMT_CHG: - // Am I re-attached to a new one and will the old one be deleted? - if( static_cast<const SwFormatChg*>(pNew)->pChangedFormat == GetRegisteredIn() && - static_cast<const SwFormatChg*>(pOld)->pChangedFormat->IsFormatInDTOR() ) - Invalidate(); - break; + EndListeningAll(); + m_pFormatField = nullptr; + m_pDoc = nullptr; + uno::Reference<uno::XInterface> 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(ev); } void SwXTextField::Impl::Notify(const SfxHint& rHint) { + if(rHint.GetId() == SfxHintId::Dying) + Invalidate(); + else if (auto pLegacyHint = dynamic_cast<const sw::LegacyModifyHint*>(&rHint)) { - m_pFieldType = nullptr; + switch(pLegacyHint->m_pOld ? pLegacyHint->m_pOld->Which() : 0) + { + case RES_REMOVE_UNO_OBJECT: + case RES_OBJECTDYING: + Invalidate(); + break; + } } } -const SwField* SwXTextField::Impl::GetField() const +const SwField* SwXTextField::Impl::GetField() const { - if (GetRegisteredIn() && m_pFormatField) - { - return m_pFormatField->GetField(); - } - return nullptr; + return IsDescriptor() ? nullptr : m_pFormatField->GetField(); } OUString SwXTextFieldMasters::getImplementationName() _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits