/* -*- 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 using namespace ::com::sun::star; namespace { uno::Sequence< OUString > GetSupportedServiceNamesImpl( size_t const nServices, char const*const pServices[]) { uno::Sequence< OUString > ret(static_cast(nServices)); std::transform(pServices, pServices + nServices, ret.begin(), [](const char* pService) -> OUString { return OUString::createFromAscii(pService); }); return ret; } } class SwXFootnote::Impl : public SvtListener { private: ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2 public: SwXFootnote& m_rThis; uno::WeakReference m_wThis; const bool m_bIsEndnote; ::comphelper::OInterfaceContainerHelper2 m_EventListeners; bool m_bIsDescriptor; SwFormatFootnote* m_pFormatFootnote; OUString m_sLabel; Impl(SwXFootnote& rThis, SwFormatFootnote* const pFootnote, const bool bIsEndnote) : m_rThis(rThis) , m_bIsEndnote(bIsEndnote) , m_EventListeners(m_Mutex) , m_bIsDescriptor(nullptr == pFootnote) , m_pFormatFootnote(pFootnote) { m_pFormatFootnote && StartListening(m_pFormatFootnote->GetNotifier()); } const SwFormatFootnote* GetFootnoteFormat() const { return m_rThis.GetDoc() ? m_pFormatFootnote : nullptr; } SwFormatFootnote const& GetFootnoteFormatOrThrow() const { SwFormatFootnote const*const pFootnote( GetFootnoteFormat() ); if (!pFootnote) { throw uno::RuntimeException("SwXFootnote: disposed or invalid", nullptr); } return *pFootnote; } void Invalidate(); protected: void Notify(const SfxHint& rHint) override; }; void SwXFootnote::Impl::Invalidate() { EndListeningAll(); m_pFormatFootnote = nullptr; m_rThis.SetDoc(nullptr); 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(ev); } void SwXFootnote::Impl::Notify(const SfxHint& rHint) { if(rHint.GetId() == SfxHintId::Dying) Invalidate(); } SwXFootnote::SwXFootnote(const bool bEndnote) : SwXText(nullptr, CursorType::Footnote) , m_pImpl( new SwXFootnote::Impl(*this, nullptr, bEndnote) ) { } SwXFootnote::SwXFootnote(SwDoc & rDoc, SwFormatFootnote & rFormat) : SwXText(& rDoc, CursorType::Footnote) , m_pImpl( new SwXFootnote::Impl(*this, &rFormat, rFormat.IsEndNote()) ) { } SwXFootnote::~SwXFootnote() { } uno::Reference SwXFootnote::CreateXFootnote(SwDoc & rDoc, SwFormatFootnote *const pFootnoteFormat, bool const isEndnote) { // i#105557: do not iterate over the registered clients: race condition uno::Reference xNote; if (pFootnoteFormat) { xNote = pFootnoteFormat->GetXFootnote(); } if (!xNote.is()) { SwXFootnote *const pNote(pFootnoteFormat ? new SwXFootnote(rDoc, *pFootnoteFormat) : new SwXFootnote(isEndnote)); xNote.set(pNote); if (pFootnoteFormat) { pFootnoteFormat->SetXFootnote(xNote); } // need a permanent Reference to initialize m_wThis pNote->m_pImpl->m_wThis = xNote; } return xNote; } namespace { class theSwXFootnoteUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXFootnoteUnoTunnelId > {}; } const uno::Sequence< sal_Int8 > & SwXFootnote::getUnoTunnelId() { return theSwXFootnoteUnoTunnelId::get().getSeq(); } sal_Int64 SAL_CALL SwXFootnote::getSomething(const uno::Sequence< sal_Int8 >& rId) { const sal_Int64 nRet( ::sw::UnoTunnelImpl(rId, this) ); return nRet ? nRet : SwXText::getSomething(rId); } OUString SAL_CALL SwXFootnote::getImplementationName() { return "SwXFootnote"; } char const*const g_ServicesFootnote[] = { "com.sun.star.text.TextContent", "com.sun.star.text.Footnote", "com.sun.star.text.Text", "com.sun.star.text.Endnote", // NB: only supported for endnotes! }; const size_t g_nServicesEndnote( SAL_N_ELEMENTS(g_ServicesFootnote) ); const size_t g_nServicesFootnote( g_nServicesEndnote - 1 ); // NB: omit! sal_Bool SAL_CALL SwXFootnote::supportsService(const OUString& rServiceName) { return cppu::supportsService(this, rServiceName); } uno::Sequence< OUString > SAL_CALL SwXFootnote::getSupportedServiceNames() { SolarMutexGuard g; return GetSupportedServiceNamesImpl( (m_pImpl->m_bIsEndnote) ? g_nServicesEndnote : g_nServicesFootnote, g_ServicesFootnote); } uno::Sequence< uno::Type > SAL_CALL SwXFootnote::getTypes() { const uno::Sequence< uno::Type > aTypes = SwXFootnote_Base::getTypes(); const uno::Sequence< uno::Type > aTextTypes = SwXText::getTypes(); return ::comphelper::concatSequences(aTypes, aTextTypes); } uno::Sequence< sal_Int8 > SAL_CALL SwXFootnote::getImplementationId() { return css::uno::Sequence(); } uno::Any SAL_CALL SwXFootnote::queryInterface(const uno::Type& rType) { const uno::Any ret = SwXFootnote_Base::queryInterface(rType); return (ret.getValueType() == cppu::UnoType::get()) ? SwXText::queryInterface(rType) : ret; } OUString SAL_CALL SwXFootnote::getLabel() { SolarMutexGuard aGuard; OUString sRet; SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat(); if(pFormat) { sRet = pFormat->GetNumStr(); } else if (m_pImpl->m_bIsDescriptor) { sRet = m_pImpl->m_sLabel; } else { throw uno::RuntimeException(); } return sRet; } void SAL_CALL SwXFootnote::setLabel(const OUString& aLabel) { SolarMutexGuard aGuard; OUString newLabel(aLabel); //new line must not occur as footnote label if(newLabel.indexOf('\n') >=0 ) { newLabel = newLabel.replace('\n', ' '); } SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat(); if(pFormat) { const SwTextFootnote* pTextFootnote = pFormat->GetTextFootnote(); OSL_ENSURE(pTextFootnote, "No TextNode?"); SwTextNode& rTextNode = const_cast(pTextFootnote->GetTextNode()); SwPaM aPam(rTextNode, pTextFootnote->GetStart()); GetDoc()->SetCurFootnote(aPam, newLabel, pFormat->IsEndNote()); } else if (m_pImpl->m_bIsDescriptor) { m_pImpl->m_sLabel = newLabel; } else { throw uno::RuntimeException(); } } void SAL_CALL SwXFootnote::attach(const uno::Reference< text::XTextRange > & xTextRange) { SolarMutexGuard aGuard; if (!m_pImpl->m_bIsDescriptor) { throw uno::RuntimeException(); } const uno::Reference xRangeTunnel( xTextRange, uno::UNO_QUERY); SwXTextRange *const pRange = ::sw::UnoTunnelGetImplementation(xRangeTunnel); OTextCursorHelper *const pCursor = ::sw::UnoTunnelGetImplementation(xRangeTunnel); SwDoc *const pNewDoc = pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr); if (!pNewDoc) { throw lang::IllegalArgumentException(); } SwUnoInternalPaM aPam(*pNewDoc); // this now needs to return TRUE ::sw::XTextRangeToSwPaM(aPam, xTextRange); UnoActionContext aCont(pNewDoc); pNewDoc->getIDocumentContentOperations().DeleteAndJoin(aPam); aPam.DeleteMark(); SwFormatFootnote aFootNote(m_pImpl->m_bIsEndnote); if (!m_pImpl->m_sLabel.isEmpty()) { aFootNote.SetNumStr(m_pImpl->m_sLabel); } SwXTextCursor const*const pTextCursor( dynamic_cast(pCursor)); const bool bForceExpandHints( pTextCursor && pTextCursor->IsAtEndOfMeta() ); const SetAttrMode nInsertFlags = bForceExpandHints ? SetAttrMode::FORCEHINTEXPAND : SetAttrMode::DEFAULT; pNewDoc->getIDocumentContentOperations().InsertPoolItem(aPam, aFootNote, nInsertFlags); SwTextFootnote *const pTextAttr = static_cast( aPam.GetNode().GetTextNode()->GetTextAttrForCharAt( aPam.GetPoint()->nContent.GetIndex()-1, RES_TXTATR_FTN )); if (pTextAttr) { m_pImpl->EndListeningAll(); SwFormatFootnote* pFootnote = const_cast(&pTextAttr->GetFootnote()); m_pImpl->m_pFormatFootnote = pFootnote; m_pImpl->StartListening(pFootnote->GetNotifier()); // force creation of sequence id - is used for references if (pNewDoc->IsInReading()) { pTextAttr->SetSeqNo(pNewDoc->GetFootnoteIdxs().size()); } else { pTextAttr->SetSeqRefNo(); } } m_pImpl->m_bIsDescriptor = false; SetDoc(pNewDoc); } uno::Reference< text::XTextRange > SAL_CALL SwXFootnote::getAnchor() { SolarMutexGuard aGuard; return m_pImpl->GetFootnoteFormatOrThrow().getAnchor(*GetDoc()); } void SAL_CALL SwXFootnote::dispose() { SolarMutexGuard aGuard; SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() ); SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote(); OSL_ENSURE(pTextFootnote, "no TextNode?"); SwTextNode& rTextNode = const_cast(pTextFootnote->GetTextNode()); const sal_Int32 nPos = pTextFootnote->GetStart(); SwPaM aPam(rTextNode, nPos, rTextNode, nPos+1); GetDoc()->getIDocumentContentOperations().DeleteAndJoin( aPam ); } void SAL_CALL SwXFootnote::addEventListener( const uno::Reference< lang::XEventListener > & xListener) { // no need to lock here as m_pImpl is const and container threadsafe m_pImpl->m_EventListeners.addInterface(xListener); } void SAL_CALL SwXFootnote::removeEventListener( const uno::Reference< lang::XEventListener > & xListener) { // no need to lock here as m_pImpl is const and container threadsafe m_pImpl->m_EventListeners.removeInterface(xListener); } const SwStartNode *SwXFootnote::GetStartNode() const { SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat(); if(pFormat) { const SwTextFootnote* pTextFootnote = pFormat->GetTextFootnote(); if( pTextFootnote ) { return pTextFootnote->GetStartNode()->GetNode().GetStartNode(); } } return nullptr; } uno::Reference< text::XTextCursor > SwXFootnote::CreateCursor() { return createTextCursor(); } uno::Reference< text::XTextCursor > SAL_CALL SwXFootnote::createTextCursor() { SolarMutexGuard aGuard; SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() ); SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote(); SwPosition aPos( *pTextFootnote->GetStartNode() ); SwXTextCursor *const pXCursor = new SwXTextCursor(*GetDoc(), this, CursorType::Footnote, aPos); auto& rUnoCursor(pXCursor->GetCursor()); rUnoCursor.Move(fnMoveForward, GoInNode); const uno::Reference< text::XTextCursor > xRet = static_cast(pXCursor); return xRet; } uno::Reference< text::XTextCursor > SAL_CALL SwXFootnote::createTextCursorByRange( const uno::Reference< text::XTextRange > & xTextPosition) { SolarMutexGuard aGuard; SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() ); SwUnoInternalPaM aPam(*GetDoc()); if (!::sw::XTextRangeToSwPaM(aPam, xTextPosition)) { throw uno::RuntimeException(); } SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote(); SwNode const*const pFootnoteStartNode = &pTextFootnote->GetStartNode()->GetNode(); const SwNode* pStart = aPam.GetNode().FindFootnoteStartNode(); if (pStart != pFootnoteStartNode) { throw uno::RuntimeException(); } const uno::Reference< text::XTextCursor > xRet = static_cast( new SwXTextCursor(*GetDoc(), this, CursorType::Footnote, *aPam.GetPoint(), aPam.GetMark())); return xRet; } uno::Reference< container::XEnumeration > SAL_CALL SwXFootnote::createEnumeration() { SolarMutexGuard aGuard; SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() ); SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote(); SwPosition aPos( *pTextFootnote->GetStartNode() ); auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos)); pUnoCursor->Move(fnMoveForward, GoInNode); return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Footnote); } uno::Type SAL_CALL SwXFootnote::getElementType() { return cppu::UnoType::get(); } sal_Bool SAL_CALL SwXFootnote::hasElements() { return true; } uno::Reference< beans::XPropertySetInfo > SAL_CALL SwXFootnote::getPropertySetInfo() { SolarMutexGuard g; static uno::Reference< beans::XPropertySetInfo > xRet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_FOOTNOTE) ->getPropertySetInfo(); return xRet; } void SAL_CALL SwXFootnote::setPropertyValue(const OUString&, const uno::Any&) { //no values to be set throw lang::IllegalArgumentException(); } uno::Any SAL_CALL SwXFootnote::getPropertyValue(const OUString& rPropertyName) { SolarMutexGuard aGuard; uno::Any aRet; if (! ::sw::GetDefaultTextContentValue(aRet, rPropertyName)) { if (rPropertyName == UNO_NAME_START_REDLINE || rPropertyName == UNO_NAME_END_REDLINE) { //redline can only be returned if it's a living object if (!m_pImpl->m_bIsDescriptor) { aRet = SwXText::getPropertyValue(rPropertyName); } } else if (rPropertyName == UNO_NAME_REFERENCE_ID) { SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat(); if (pFormat) { SwTextFootnote const*const pTextFootnote = pFormat->GetTextFootnote(); OSL_ENSURE(pTextFootnote, "no TextNode?"); aRet <<= static_cast(pTextFootnote->GetSeqRefNo()); } } else { beans::UnknownPropertyException aExcept; aExcept.Message = rPropertyName; throw aExcept; } } return aRet; } void SAL_CALL SwXFootnote::addPropertyChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/) { OSL_FAIL("SwXFootnote::addPropertyChangeListener(): not implemented"); } void SAL_CALL SwXFootnote::removePropertyChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/) { OSL_FAIL("SwXFootnote::removePropertyChangeListener(): not implemented"); } void SAL_CALL SwXFootnote::addVetoableChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/) { OSL_FAIL("SwXFootnote::addVetoableChangeListener(): not implemented"); } void SAL_CALL SwXFootnote::removeVetoableChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/) { OSL_FAIL("SwXFootnote::removeVetoableChangeListener(): not implemented"); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */