summaryrefslogtreecommitdiffstats
path: root/sw/source/core/ole/ndole.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/ole/ndole.cxx')
-rw-r--r--sw/source/core/ole/ndole.cxx1246
1 files changed, 1246 insertions, 0 deletions
diff --git a/sw/source/core/ole/ndole.cxx b/sw/source/core/ole/ndole.cxx
new file mode 100644
index 000000000..1927872ed
--- /dev/null
+++ b/sw/source/core/ole/ndole.cxx
@@ -0,0 +1,1246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <sot/exchange.hxx>
+#include <tools/globname.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <unotools/configitem.hxx>
+#include <vcl/outdev.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <pam.hxx>
+#include <section.hxx>
+#include <cntfrm.hxx>
+#include <ndole.hxx>
+#include <viewsh.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <comphelper/classids.hxx>
+#include <vcl/graph.hxx>
+#include <sot/formats.hxx>
+#include <vcl/svapp.hxx>
+#include <strings.hrc>
+#include <svx/charthelper.hxx>
+#include <comphelper/threadpool.hxx>
+#include <atomic>
+#include <deque>
+#include <libxml/xmlwriter.h>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star;
+
+namespace {
+
+class SwOLELRUCache
+ : private utl::ConfigItem
+{
+private:
+ typedef std::deque<SwOLEObj *> OleObjects_t;
+ OleObjects_t m_OleObjects;
+ sal_Int32 m_nLRU_InitSize;
+ static uno::Sequence< OUString > GetPropertyNames();
+
+ virtual void ImplCommit() override;
+
+public:
+ SwOLELRUCache();
+
+ virtual void Notify( const uno::Sequence<
+ OUString>& aPropertyNames ) override;
+ void Load();
+
+ void InsertObj( SwOLEObj& rObj );
+ void RemoveObj( SwOLEObj& rObj );
+};
+
+}
+
+static std::shared_ptr<SwOLELRUCache> g_pOLELRU_Cache;
+
+class SwOLEListener_Impl : public ::cppu::WeakImplHelper< embed::XStateChangeListener >
+{
+ SwOLEObj* mpObj;
+public:
+ explicit SwOLEListener_Impl( SwOLEObj* pObj );
+ void dispose();
+ virtual void SAL_CALL changingState( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override;
+};
+
+SwOLEListener_Impl::SwOLEListener_Impl( SwOLEObj* pObj )
+: mpObj( pObj )
+{
+ if ( mpObj->IsOleRef() && mpObj->GetOleRef()->getCurrentState() == embed::EmbedStates::RUNNING )
+ {
+ g_pOLELRU_Cache->InsertObj( *mpObj );
+ }
+}
+
+void SAL_CALL SwOLEListener_Impl::changingState( const lang::EventObject&, ::sal_Int32 , ::sal_Int32 )
+{
+}
+
+void SAL_CALL SwOLEListener_Impl::stateChanged( const lang::EventObject&, ::sal_Int32 nOldState, ::sal_Int32 nNewState )
+{
+ if ( mpObj && nOldState == embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
+ {
+ if (!g_pOLELRU_Cache)
+ g_pOLELRU_Cache = std::make_shared<SwOLELRUCache>();
+ g_pOLELRU_Cache->InsertObj( *mpObj );
+ }
+ else if ( mpObj && nNewState == embed::EmbedStates::LOADED && nOldState == embed::EmbedStates::RUNNING )
+ {
+ if (g_pOLELRU_Cache)
+ g_pOLELRU_Cache->RemoveObj( *mpObj );
+ }
+ else if(mpObj && nNewState == embed::EmbedStates::RUNNING)
+ {
+ mpObj->resetBufferedData();
+ }
+}
+
+void SwOLEListener_Impl::dispose()
+{
+ if (mpObj && g_pOLELRU_Cache)
+ g_pOLELRU_Cache->RemoveObj( *mpObj );
+ mpObj = nullptr;
+}
+
+void SAL_CALL SwOLEListener_Impl::disposing( const lang::EventObject& )
+{
+ if (mpObj && g_pOLELRU_Cache)
+ g_pOLELRU_Cache->RemoveObj( *mpObj );
+}
+
+// TODO/LATER: actually SwEmbedObjectLink should be used here, but because different objects are used to control
+// embedded object different link objects with the same functionality had to be implemented
+
+class SwEmbedObjectLink : public sfx2::SvBaseLink
+{
+ SwOLENode* pOleNode;
+
+public:
+ explicit SwEmbedObjectLink(SwOLENode* pNode);
+
+ virtual void Closed() override;
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+
+ void Connect() { GetRealObject(); }
+};
+
+SwEmbedObjectLink::SwEmbedObjectLink(SwOLENode* pNode):
+ ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB ),
+ pOleNode(pNode)
+{
+ SetSynchron( false );
+}
+
+::sfx2::SvBaseLink::UpdateResult SwEmbedObjectLink::DataChanged(
+ const OUString&, const uno::Any& )
+{
+ if ( !pOleNode->UpdateLinkURL_Impl() )
+ {
+ // the link URL was not changed
+ uno::Reference< embed::XEmbeddedObject > xObject = pOleNode->GetOLEObj().GetOleRef();
+ OSL_ENSURE( xObject.is(), "The object must exist always!" );
+ if ( xObject.is() )
+ {
+ // let the object reload the link
+ // TODO/LATER: reload call could be used for this case
+
+ try
+ {
+ sal_Int32 nState = xObject->getCurrentState();
+ if ( nState != embed::EmbedStates::LOADED )
+ {
+ // in some cases the linked file probably is not locked so it could be changed
+ xObject->changeState( embed::EmbedStates::LOADED );
+ xObject->changeState( nState );
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+
+ pOleNode->GetNewReplacement();
+ pOleNode->SetChanged();
+
+ return SUCCESS;
+}
+
+void SwEmbedObjectLink::Closed()
+{
+ pOleNode->BreakFileLink_Impl();
+ SvBaseLink::Closed();
+}
+
+SwOLENode::SwOLENode( const SwNodeIndex &rWhere,
+ const svt::EmbeddedObjectRef& xObj,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwNoTextNode( rWhere, SwNodeType::Ole, pGrfColl, pAutoAttr ),
+ maOLEObj( xObj ),
+ mbOLESizeInvalid( false ),
+ mpObjectLink( nullptr )
+{
+ maOLEObj.SetNode( this );
+}
+
+SwOLENode::SwOLENode( const SwNodeIndex &rWhere,
+ const OUString &rString,
+ sal_Int64 nAspect,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwNoTextNode( rWhere, SwNodeType::Ole, pGrfColl, pAutoAttr ),
+ maOLEObj( rString, nAspect ),
+ mbOLESizeInvalid( false ),
+ mpObjectLink( nullptr )
+{
+ maOLEObj.SetNode( this );
+}
+
+SwOLENode::~SwOLENode()
+{
+ DisconnectFileLink_Impl();
+}
+
+const Graphic* SwOLENode::GetGraphic()
+{
+ if ( maOLEObj.GetOleRef().is() )
+ return maOLEObj.m_xOLERef.GetGraphic();
+ return nullptr;
+}
+
+/**
+ * Loading an OLE object that has been moved to the Undo Area
+ */
+bool SwOLENode::RestorePersistentData()
+{
+ OSL_ENSURE( maOLEObj.GetOleRef().is(), "No object to restore!" );
+ if ( maOLEObj.m_xOLERef.is() )
+ {
+ // If a SvPersist instance already exists, we use it
+ SfxObjectShell* p = GetDoc()->GetPersist();
+ if( !p )
+ {
+ // TODO/LATER: Isn't an EmbeddedObjectContainer sufficient here?
+ // What happens to this document?
+ OSL_ENSURE( false, "Why are we creating a DocShell here?" );
+ p = new SwDocShell( GetDoc(), SfxObjectCreateMode::INTERNAL );
+ p->DoInitNew();
+ }
+
+ uno::Reference < container::XChild > xChild( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( p->GetModel() );
+
+ OSL_ENSURE( !maOLEObj.m_aName.isEmpty(), "No object name!" );
+ OUString aObjName;
+ if ( !p->GetEmbeddedObjectContainer().InsertEmbeddedObject( maOLEObj.m_xOLERef.GetObject(), aObjName ) )
+ {
+ if ( xChild.is() )
+ xChild->setParent( nullptr );
+ OSL_FAIL( "InsertObject failed" );
+ }
+ else
+ {
+ maOLEObj.m_aName = aObjName;
+ maOLEObj.m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName );
+ CheckFileLink_Impl();
+ }
+ }
+
+ return true;
+}
+
+void SwOLENode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ xmlTextWriterStartElement(pWriter, BAD_CAST("SwOLENode"));
+ xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(GetIndex()).getStr()));
+
+ GetOLEObj().dumpAsXml(pWriter);
+
+ xmlTextWriterEndElement(pWriter);
+}
+
+/**
+ * OLE object is transported into UNDO area
+ */
+bool SwOLENode::SavePersistentData()
+{
+ if( maOLEObj.m_xOLERef.is() )
+ {
+ comphelper::EmbeddedObjectContainer* pCnt = maOLEObj.m_xOLERef.GetContainer();
+
+#if OSL_DEBUG_LEVEL > 0
+ SfxObjectShell* p = GetDoc()->GetPersist();
+ OSL_ENSURE( p, "No document!" );
+ if( p )
+ {
+ comphelper::EmbeddedObjectContainer& rCnt = p->GetEmbeddedObjectContainer();
+ OSL_ENSURE( !pCnt || &rCnt == pCnt, "The helper is assigned to unexpected container!" );
+ }
+#endif
+
+ if ( pCnt && pCnt->HasEmbeddedObject( maOLEObj.m_aName ) )
+ {
+ uno::Reference < container::XChild > xChild( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( nullptr );
+
+ /*
+ #i119941
+ When cut or move the chart, SwUndoFlyBase::DelFly will call SaveSection
+ to store the content to storage. In this step, chart filter functions
+ will be called. And chart filter will call chart core functions to create
+ the chart again. Then chart core function will call the class
+ ExplicitCategoryProvider to create data source. In this step, when SW data
+ source provider create the data source, a UnoActionRemoveContext
+ will mess with the layout and create a new SwFlyFrame.
+ But later in SwUndoFlyBase::DelFly, it will clear anchor related attributes
+ of SwFlyFrame. Then finally null pointer occur.
+ Resolution:
+ In pCnt->RemoveEmbeddedObject in SaveSection process of table chart,
+ only remove the object from the object container, without removing it's
+ storage and graphic stream. The chart already removed from formatter.
+ */
+ bool bKeepObjectToTempStorage = true;
+ uno::Reference < embed::XEmbeddedObject > xIP = GetOLEObj().GetOleRef();
+ if (IsChart() && !msChartTableName.isEmpty()
+ && svt::EmbeddedObjectRef::TryRunningState(xIP))
+ {
+ uno::Reference< chart2::XChartDocument > xChart( xIP->getComponent(), UNO_QUERY );
+ if (xChart.is() && !xChart->hasInternalDataProvider())
+ {
+ bKeepObjectToTempStorage = false;
+ }
+ }
+
+ pCnt->RemoveEmbeddedObject( maOLEObj.m_aName, bKeepObjectToTempStorage );
+
+ // TODO/LATER: aOLEObj.aName has no meaning here, since the undo container contains the object
+ // by different name, in future it might makes sense that the name is transported here.
+ maOLEObj.m_xOLERef.AssignToContainer( nullptr, maOLEObj.m_aName );
+ try
+ {
+ // "unload" object
+ maOLEObj.m_xOLERef->changeState( embed::EmbedStates::LOADED );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+
+ DisconnectFileLink_Impl();
+
+ return true;
+}
+
+SwOLENode * SwNodes::MakeOLENode( const SwNodeIndex & rWhere,
+ const svt::EmbeddedObjectRef& xObj,
+ SwGrfFormatColl* pGrfColl )
+{
+ OSL_ENSURE( pGrfColl,"SwNodes::MakeOLENode: Formatpointer is 0." );
+
+ SwOLENode *pNode =
+ new SwOLENode( rWhere, xObj, pGrfColl, nullptr );
+
+ // set parent if XChild is supported
+ //!! needed to supply Math objects with a valid reference device
+ uno::Reference< container::XChild > xChild( pNode->GetOLEObj().GetObject().GetObject(), UNO_QUERY );
+ if (xChild.is())
+ {
+ SwDocShell *pDocSh = GetDoc()->GetDocShell();
+ if (pDocSh)
+ xChild->setParent( pDocSh->GetModel() );
+ }
+
+ return pNode;
+}
+
+SwOLENode * SwNodes::MakeOLENode( const SwNodeIndex & rWhere,
+ const OUString &rName, sal_Int64 nAspect, SwGrfFormatColl* pGrfColl, SwAttrSet const * pAutoAttr )
+{
+ OSL_ENSURE( pGrfColl,"SwNodes::MakeOLENode: Formatpointer is 0." );
+
+ SwOLENode *pNode =
+ new SwOLENode( rWhere, rName, nAspect, pGrfColl, pAutoAttr );
+
+ // set parent if XChild is supported
+ //!! needed to supply Math objects with a valid reference device
+ uno::Reference< container::XChild > xChild( pNode->GetOLEObj().GetObject().GetObject(), UNO_QUERY );
+ if (xChild.is())
+ {
+ SwDocShell *pDocSh= GetDoc()->GetDocShell();
+ if (pDocSh)
+ xChild->setParent( pDocSh->GetModel() );
+ }
+
+ return pNode;
+}
+
+Size SwOLENode::GetTwipSize() const
+{
+ MapMode aMapMode( MapUnit::MapTwip );
+ return const_cast<SwOLENode*>(this)->maOLEObj.GetObject().GetSize( &aMapMode );
+}
+
+SwContentNode* SwOLENode::MakeCopy( SwDoc* pDoc, const SwNodeIndex& rIdx, bool) const
+{
+ // If there's already a SvPersist instance, we use it
+ SfxObjectShell* pPersistShell = pDoc->GetPersist();
+ if( !pPersistShell )
+ {
+ // TODO/LATER: is EmbeddedObjectContainer not enough?
+ // the created document will be closed by pDoc ( should use SfxObjectShellLock )
+ pPersistShell = new SwDocShell( pDoc, SfxObjectCreateMode::INTERNAL );
+ pDoc->SetTmpDocShell( pPersistShell );
+ pPersistShell->DoInitNew();
+ }
+
+ // We insert it at SvPersist level
+ // TODO/LATER: check if using the same naming scheme for all apps works here
+ OUString aNewName/*( Sw3Io::UniqueName( p->GetStorage(), "Obj" ) )*/;
+ SfxObjectShell* pSrc = GetDoc()->GetPersist();
+
+ pPersistShell->GetEmbeddedObjectContainer().CopyAndGetEmbeddedObject(
+ pSrc->GetEmbeddedObjectContainer(),
+ pSrc->GetEmbeddedObjectContainer().GetEmbeddedObject( maOLEObj.m_aName ),
+ aNewName,
+ pSrc->getDocumentBaseURL(),
+ pPersistShell->getDocumentBaseURL());
+
+ SwOLENode* pOLENd = pDoc->GetNodes().MakeOLENode( rIdx, aNewName, GetAspect(),
+ pDoc->GetDfltGrfFormatColl(),
+ GetpSwAttrSet() );
+
+ pOLENd->SetChartTableName( GetChartTableName() );
+ pOLENd->SetTitle( GetTitle() );
+ pOLENd->SetDescription( GetDescription() );
+ pOLENd->SetContour( HasContour(), HasAutomaticContour() );
+ pOLENd->SetAspect( GetAspect() ); // the replacement image must be already copied
+
+ pOLENd->SetOLESizeInvalid( true );
+ pDoc->SetOLEPrtNotifyPending();
+
+ return pOLENd;
+}
+
+bool SwOLENode::IsInGlobalDocSection() const
+{
+ // Find the "Body Anchor"
+ sal_uLong nEndExtraIdx = GetNodes().GetEndOfExtras().GetIndex();
+ const SwNode* pAnchorNd = this;
+ do {
+ SwFrameFormat* pFlyFormat = pAnchorNd->GetFlyFormat();
+ if( !pFlyFormat )
+ return false;
+
+ const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
+ if( !rAnchor.GetContentAnchor() )
+ return false;
+
+ pAnchorNd = &rAnchor.GetContentAnchor()->nNode.GetNode();
+ } while( pAnchorNd->GetIndex() < nEndExtraIdx );
+
+ const SwSectionNode* pSectNd = pAnchorNd->FindSectionNode();
+ if( !pSectNd )
+ return false;
+
+ while( pSectNd )
+ {
+ pAnchorNd = pSectNd;
+ pSectNd = pAnchorNd->StartOfSectionNode()->FindSectionNode();
+ }
+
+ // pAnchorNd contains the most recently found Section Node, which
+ // now must fulfill the prerequisites for the GlobalDoc
+ pSectNd = static_cast<const SwSectionNode*>(pAnchorNd);
+ return SectionType::FileLink == pSectNd->GetSection().GetType() &&
+ pSectNd->GetIndex() > nEndExtraIdx;
+}
+
+bool SwOLENode::IsOLEObjectDeleted() const
+{
+ if( maOLEObj.m_xOLERef.is() )
+ {
+ SfxObjectShell* p = GetDoc()->GetPersist();
+ if( p ) // Must be there
+ {
+ return !p->GetEmbeddedObjectContainer().HasEmbeddedObject( maOLEObj.m_aName );
+ }
+ }
+ return false;
+}
+
+void SwOLENode::GetNewReplacement()
+{
+ if ( maOLEObj.m_xOLERef.is() )
+ maOLEObj.m_xOLERef.UpdateReplacement();
+}
+
+bool SwOLENode::UpdateLinkURL_Impl()
+{
+ bool bResult = false;
+
+ if ( mpObjectLink )
+ {
+ OUString aNewLinkURL;
+ sfx2::LinkManager::GetDisplayNames( mpObjectLink, nullptr, &aNewLinkURL );
+ if ( !aNewLinkURL.equalsIgnoreAsciiCase( maLinkURL ) )
+ {
+ if ( !maOLEObj.m_xOLERef.is() )
+ maOLEObj.GetOleRef();
+
+ uno::Reference< embed::XEmbeddedObject > xObj = maOLEObj.m_xOLERef.GetObject();
+ uno::Reference< embed::XCommonEmbedPersist > xPersObj( xObj, uno::UNO_QUERY );
+ OSL_ENSURE( xPersObj.is(), "The object must exist!" );
+ if ( xPersObj.is() )
+ {
+ try
+ {
+ sal_Int32 nCurState = xObj->getCurrentState();
+ if ( nCurState != embed::EmbedStates::LOADED )
+ xObj->changeState( embed::EmbedStates::LOADED );
+
+ // TODO/LATER: there should be possible to get current mediadescriptor settings from the object
+ uno::Sequence< beans::PropertyValue > aArgs( 1 );
+ aArgs[0].Name = "URL";
+ aArgs[0].Value <<= aNewLinkURL;
+ xPersObj->reload( aArgs, uno::Sequence< beans::PropertyValue >() );
+
+ maLinkURL = aNewLinkURL;
+ bResult = true;
+
+ if ( nCurState != embed::EmbedStates::LOADED )
+ xObj->changeState( nCurState );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ if ( !bResult )
+ {
+ // TODO/LATER: return the old name to the link manager, is it possible?
+ }
+ }
+ }
+
+ return bResult;
+}
+
+void SwOLENode::BreakFileLink_Impl()
+{
+ SfxObjectShell* pPers = GetDoc()->GetPersist();
+
+ if ( pPers )
+ {
+ uno::Reference< embed::XStorage > xStorage = pPers->GetStorage();
+ if ( xStorage.is() )
+ {
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.GetOleRef(), uno::UNO_QUERY_THROW );
+ xLinkSupport->breakLink( xStorage, maOLEObj.GetCurrentPersistName() );
+ DisconnectFileLink_Impl();
+ maLinkURL.clear();
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ }
+}
+
+void SwOLENode::DisconnectFileLink_Impl()
+{
+ if ( mpObjectLink )
+ {
+ GetDoc()->getIDocumentLinksAdministration().GetLinkManager().Remove( mpObjectLink );
+ mpObjectLink = nullptr;
+ }
+}
+
+void SwOLENode::CheckFileLink_Impl()
+{
+ if ( maOLEObj.m_xOLERef.GetObject().is() && !mpObjectLink )
+ {
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY_THROW );
+ if ( xLinkSupport->isLink() )
+ {
+ const OUString aLinkURL = xLinkSupport->getLinkURL();
+ if ( !aLinkURL.isEmpty() )
+ {
+ // this is a file link so the model link manager should handle it
+ mpObjectLink = new SwEmbedObjectLink( this );
+ maLinkURL = aLinkURL;
+ GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *mpObjectLink, sfx2::SvBaseLinkObjectType::ClientOle, aLinkURL );
+ mpObjectLink->Connect();
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+}
+
+// #i99665#
+bool SwOLENode::IsChart() const
+{
+ bool bIsChart( false );
+
+ const uno::Reference< embed::XEmbeddedObject > xEmbObj =
+ const_cast<SwOLEObj&>(GetOLEObj()).GetOleRef();
+ if ( xEmbObj.is() )
+ {
+ SvGlobalName aClassID( xEmbObj->getClassID() );
+ bIsChart = SotExchange::IsChart( aClassID );
+ }
+
+ return bIsChart;
+}
+
+// react on visual change (invalidate)
+void SwOLENode::SetChanged()
+{
+ SwFrame* pFrame(getLayoutFrame(nullptr));
+
+ if(nullptr == pFrame)
+ {
+ return;
+ }
+
+ const SwRect aFrameArea(pFrame->getFrameArea());
+ SwViewShell* pVSh(GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell());
+
+ if(nullptr == pVSh)
+ {
+ return;
+ }
+
+ for(SwViewShell& rShell : pVSh->GetRingContainer())
+ {
+ SET_CURR_SHELL(&rShell);
+
+ if(rShell.VisArea().IsOver(aFrameArea) && OUTDEV_WINDOW == rShell.GetOut()->GetOutDevType())
+ {
+ // invalidate instead of painting
+ rShell.GetWin()->Invalidate(aFrameArea.SVRect());
+ }
+ }
+}
+
+namespace { class DeflateThread; }
+
+/// Holder for local data for a parallel-executed task to load a chart model
+class DeflateData
+{
+private:
+ friend DeflateThread;
+ friend class SwOLEObj;
+
+ uno::Reference< frame::XModel > maXModel;
+ drawinglayer::primitive2d::Primitive2DContainer maPrimitive2DSequence;
+ basegfx::B2DRange maRange;
+
+ // evtl.set from the SwOLEObj destructor when a WorkerThread is still active
+ // since it is not possible to kill it - let it terminate and delete the
+ // data working on itself
+ std::atomic< bool> mbKilled;
+
+ std::shared_ptr<comphelper::ThreadTaskTag> mpTag;
+
+public:
+ explicit DeflateData(const uno::Reference< frame::XModel >& rXModel)
+ : maXModel(rXModel),
+ maPrimitive2DSequence(),
+ maRange(),
+ mbKilled(false),
+ mpTag( comphelper::ThreadPool::createThreadTaskTag() )
+ {
+ }
+
+ const drawinglayer::primitive2d::Primitive2DContainer& getSequence() const
+ {
+ return maPrimitive2DSequence;
+ }
+
+ const basegfx::B2DRange& getRange() const
+ {
+ return maRange;
+ }
+
+ bool isFinished() const
+ {
+ return comphelper::ThreadPool::isTaskTagDone(mpTag);
+ }
+
+ void waitFinished()
+ {
+ // need to wait until the load in progress is finished.
+ // WorkerThreads need the SolarMutex to be able to continue
+ // and finish the running import.
+ SolarMutexReleaser aReleaser;
+ comphelper::ThreadPool::getSharedOptimalPool().waitUntilDone(mpTag);
+ }
+};
+
+namespace {
+
+/// Task for parallelly-executed task to load a chart model
+class DeflateThread : public comphelper::ThreadTask
+{
+ // the data to work on
+ DeflateData& mrDeflateData;
+
+public:
+ explicit DeflateThread(DeflateData& rDeflateData)
+ : comphelper::ThreadTask(rDeflateData.mpTag), mrDeflateData(rDeflateData)
+ {
+ }
+
+private:
+ virtual void doWork() override
+ {
+ try
+ {
+ // load the chart data and get the primitives
+ mrDeflateData.maPrimitive2DSequence = ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
+ mrDeflateData.maXModel,
+ mrDeflateData.maRange);
+
+ // model no longer needed and done
+ mrDeflateData.maXModel.clear();
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ if(mrDeflateData.mbKilled)
+ {
+ // need to cleanup myself - data will not be used
+ delete &mrDeflateData;
+ }
+ }
+};
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SwOLEObj::SwOLEObj( const svt::EmbeddedObjectRef& xObj ) :
+ m_pOLENode( nullptr ),
+ m_xOLERef( xObj ),
+ m_aPrimitive2DSequence(),
+ m_aRange()
+{
+ m_xOLERef.Lock();
+ if ( xObj.is() )
+ {
+ m_xListener = new SwOLEListener_Impl( this );
+ xObj->addStateChangeListener( m_xListener.get() );
+ }
+}
+
+SwOLEObj::SwOLEObj( const OUString &rString, sal_Int64 nAspect ) :
+ m_pOLENode( nullptr ),
+ m_aName( rString ),
+ m_aPrimitive2DSequence(),
+ m_aRange()
+{
+ m_xOLERef.Lock();
+ m_xOLERef.SetViewAspect( nAspect );
+}
+
+SwOLEObj::~SwOLEObj() COVERITY_NOEXCEPT_FALSE
+{
+ if(m_pDeflateData)
+ {
+ // set flag so that the worker thread will delete m_pDeflateData
+ // when finished and forget about it
+ m_pDeflateData->mbKilled = true;
+ m_pDeflateData = nullptr;
+ }
+
+ if( m_xListener )
+ {
+ if ( m_xOLERef.is() )
+ m_xOLERef->removeStateChangeListener( m_xListener.get() );
+ m_xListener->dispose();
+ m_xListener.clear();
+ }
+
+ if( m_pOLENode && !m_pOLENode->GetDoc()->IsInDtor() )
+ {
+ // if the model is not currently in destruction it means that this object should be removed from the model
+ comphelper::EmbeddedObjectContainer* pCnt = m_xOLERef.GetContainer();
+
+#if OSL_DEBUG_LEVEL > 0
+ SfxObjectShell* p = m_pOLENode->GetDoc()->GetPersist();
+ OSL_ENSURE( p, "No document!" );
+ if( p )
+ {
+ comphelper::EmbeddedObjectContainer& rCnt = p->GetEmbeddedObjectContainer();
+ OSL_ENSURE( !pCnt || &rCnt == pCnt, "The helper is assigned to unexpected container!" );
+ }
+#endif
+
+ if ( pCnt && pCnt->HasEmbeddedObject( m_aName ) )
+ {
+ uno::Reference < container::XChild > xChild( m_xOLERef.GetObject(), uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( nullptr );
+
+ // not already removed by deleting the object
+ m_xOLERef.AssignToContainer( nullptr, m_aName );
+
+ // unlock object so that object can be closed in RemoveEmbeddedObject
+ // successful closing of the object will automatically clear the reference then
+ m_xOLERef.Lock(false);
+
+ // Always remove object from container it is connected to
+ try
+ {
+ // remove object from container but don't close it
+ pCnt->RemoveEmbeddedObject( m_aName );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ }
+
+ if ( m_xOLERef.is() )
+ // in case the object wasn't closed: release it
+ // in case the object was not in the container: it's still locked, try to close
+ m_xOLERef.Clear();
+}
+
+void SwOLEObj::SetNode( SwOLENode* pNode )
+{
+ m_pOLENode = pNode;
+ if ( m_aName.isEmpty() )
+ {
+ SwDoc* pDoc = pNode->GetDoc();
+
+ // If there's already a SvPersist instance, we use it
+ SfxObjectShell* p = pDoc->GetPersist();
+ if( !p )
+ {
+ // TODO/LATER: Isn't an EmbeddedObjectContainer sufficient here?
+ // What happens to the document?
+ OSL_ENSURE( false, "Why are we creating a DocShell here??" );
+ p = new SwDocShell( pDoc, SfxObjectCreateMode::INTERNAL );
+ p->DoInitNew();
+ }
+
+ OUString aObjName;
+ uno::Reference < container::XChild > xChild( m_xOLERef.GetObject(), uno::UNO_QUERY );
+ if ( xChild.is() && xChild->getParent() != p->GetModel() )
+ // it is possible that the parent was set already
+ xChild->setParent( p->GetModel() );
+ if (!p->GetEmbeddedObjectContainer().InsertEmbeddedObject( m_xOLERef.GetObject(), aObjName ) )
+ {
+ OSL_FAIL( "InsertObject failed" );
+ if ( xChild.is() )
+ xChild->setParent( nullptr );
+ }
+ else
+ m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName );
+
+ const_cast<SwOLENode*>(m_pOLENode)->CheckFileLink_Impl(); // for this notification nonconst access is required
+
+ m_aName = aObjName;
+ }
+}
+
+OUString SwOLEObj::GetStyleString()
+{
+ OUString strStyle;
+ if (m_xOLERef.is() && m_xOLERef.IsChart())
+ strStyle = m_xOLERef.GetChartType();
+ return strStyle;
+}
+
+bool SwOLEObj::IsOleRef() const
+{
+ return m_xOLERef.is();
+}
+
+uno::Reference < embed::XEmbeddedObject > const & SwOLEObj::GetOleRef()
+{
+ if( !m_xOLERef.is() )
+ {
+ SfxObjectShell* p = m_pOLENode->GetDoc()->GetPersist();
+ assert(p && "No SvPersist present");
+
+ OUString sDocumentBaseURL = p->getDocumentBaseURL();
+ uno::Reference < embed::XEmbeddedObject > xObj = p->GetEmbeddedObjectContainer().GetEmbeddedObject(m_aName, &sDocumentBaseURL);
+ OSL_ENSURE( !m_xOLERef.is(), "Calling GetOleRef() recursively is not permitted" );
+
+ if ( !xObj.is() )
+ {
+ // We could not load this part (probably broken)
+ tools::Rectangle aArea;
+ SwFrame *pFrame = m_pOLENode->getLayoutFrame(nullptr);
+ if ( pFrame )
+ {
+ Size aSz( pFrame->getFrameArea().SSize() );
+ const MapMode aSrc ( MapUnit::MapTwip );
+ const MapMode aDest( MapUnit::Map100thMM );
+ aSz = OutputDevice::LogicToLogic( aSz, aSrc, aDest );
+ aArea.SetSize( aSz );
+ }
+ else
+ aArea.SetSize( Size( 5000, 5000 ) );
+ // TODO/LATER: set replacement graphic for dead object
+ // It looks as if it should work even without the object, because the replace will be generated automatically
+ OUString aTmpName;
+ xObj = p->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_DUMMY_CLASSID ).GetByteSequence(), aTmpName );
+ }
+ if (xObj.is())
+ {
+ m_xOLERef.Assign( xObj, m_xOLERef.GetViewAspect() );
+ m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), m_aName );
+ m_xListener = new SwOLEListener_Impl( this );
+ xObj->addStateChangeListener( m_xListener.get() );
+ }
+
+ const_cast<SwOLENode*>(m_pOLENode)->CheckFileLink_Impl(); // for this notification nonconst access is required
+ }
+ else if ( m_xOLERef->getCurrentState() == embed::EmbedStates::RUNNING )
+ {
+ // move object to first position in cache
+ if (!g_pOLELRU_Cache)
+ g_pOLELRU_Cache = std::make_shared<SwOLELRUCache>();
+ g_pOLELRU_Cache->InsertObj( *this );
+ }
+
+ return m_xOLERef.GetObject();
+}
+
+svt::EmbeddedObjectRef& SwOLEObj::GetObject()
+{
+ GetOleRef();
+ return m_xOLERef;
+}
+
+bool SwOLEObj::UnloadObject()
+{
+ bool bRet = true;
+ if ( m_pOLENode )
+ {
+ const SwDoc* pDoc = m_pOLENode->GetDoc();
+ bRet = UnloadObject( m_xOLERef.GetObject(), pDoc, m_xOLERef.GetViewAspect() );
+ }
+
+ return bRet;
+}
+
+PurgeGuard::PurgeGuard(const SwDoc& rDoc)
+ : m_rManager(const_cast<SwDoc&>(rDoc).GetDocumentSettingManager())
+ , m_bOrigPurgeOle(m_rManager.get(DocumentSettingId::PURGE_OLE))
+{
+ m_rManager.set(DocumentSettingId::PURGE_OLE, false);
+}
+
+PurgeGuard::~PurgeGuard()
+{
+ m_rManager.set(DocumentSettingId::PURGE_OLE, m_bOrigPurgeOle);
+}
+
+bool SwOLEObj::UnloadObject( uno::Reference< embed::XEmbeddedObject > const & xObj, const SwDoc* pDoc, sal_Int64 nAspect )
+{
+ if ( !pDoc )
+ return false;
+
+ bool bRet = true;
+ sal_Int32 nState = xObj.is() ? xObj->getCurrentState() : embed::EmbedStates::LOADED;
+ bool bIsActive = ( nState != embed::EmbedStates::LOADED && nState != embed::EmbedStates::RUNNING );
+ sal_Int64 nMiscStatus = xObj->getStatus( nAspect );
+
+ if( nState != embed::EmbedStates::LOADED && !pDoc->IsInDtor() && !bIsActive &&
+ embed::EmbedMisc::MS_EMBED_ALWAYSRUN != ( nMiscStatus & embed::EmbedMisc::MS_EMBED_ALWAYSRUN ) &&
+ embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY != ( nMiscStatus & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY ) )
+ {
+ SfxObjectShell* p = pDoc->GetPersist();
+ if( p )
+ {
+ if( pDoc->GetDocumentSettingManager().get(DocumentSettingId::PURGE_OLE) )
+ {
+ try
+ {
+ uno::Reference < util::XModifiable > xMod( xObj->getComponent(), uno::UNO_QUERY );
+ if( xMod.is() && xMod->isModified() )
+ {
+ uno::Reference < embed::XEmbedPersist > xPers( xObj, uno::UNO_QUERY );
+ assert(xPers.is() && "Modified object without persistence in cache!");
+
+ PurgeGuard aGuard(*pDoc);
+ xPers->storeOwn();
+ }
+
+ // setting object to loaded state will remove it from cache
+ xObj->changeState( embed::EmbedStates::LOADED );
+ }
+ catch (const uno::Exception&)
+ {
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ }
+ }
+
+ return bRet;
+}
+
+OUString SwOLEObj::GetDescription()
+{
+ uno::Reference< embed::XEmbeddedObject > xEmbObj = GetOleRef();
+ if ( !xEmbObj.is() )
+ return OUString();
+
+ SvGlobalName aClassID( xEmbObj->getClassID() );
+ if ( SotExchange::IsMath( aClassID ) )
+ return SwResId(STR_MATH_FORMULA);
+
+ if ( SotExchange::IsChart( aClassID ) )
+ return SwResId(STR_CHART);
+
+ return SwResId(STR_OLE);
+}
+
+drawinglayer::primitive2d::Primitive2DContainer const & SwOLEObj::tryToGetChartContentAsPrimitive2DSequence(
+ basegfx::B2DRange& rRange,
+ bool bSynchron)
+{
+ if(m_pDeflateData)
+ {
+ if(bSynchron)
+ {
+ // data in high quality is requested, wait until the data is available
+ // since a WorkerThread was already started to load it
+ m_pDeflateData->waitFinished();
+ }
+
+ if(m_pDeflateData->isFinished())
+ {
+ // copy the result data and cleanup
+ m_aPrimitive2DSequence = m_pDeflateData->getSequence();
+ m_aRange = m_pDeflateData->getRange();
+ m_pDeflateData.reset();
+ }
+ }
+
+ if(m_aPrimitive2DSequence.empty() && m_aRange.isEmpty() && m_xOLERef.is() && m_xOLERef.IsChart())
+ {
+ const uno::Reference< frame::XModel > aXModel(m_xOLERef->getComponent(), uno::UNO_QUERY);
+
+ if(aXModel.is())
+ {
+ // disabled for now, need to check deeper
+ static bool bAsynchronousLoadingAllowed = false; // loplugin:constvars:ignore
+
+ if(bSynchron ||
+ !bAsynchronousLoadingAllowed)
+ {
+ // load chart synchron in this Thread
+ m_aPrimitive2DSequence = ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
+ aXModel,
+ m_aRange);
+ }
+ else
+ {
+ // if not yet setup, initiate and start a WorkerThread to load the chart
+ // and it's primitives asynchron. If it already works, returning nothing
+ // is okay (preview will be reused)
+ if(!m_pDeflateData)
+ {
+ m_pDeflateData.reset( new DeflateData(aXModel) );
+ std::unique_ptr<DeflateThread> pNew( new DeflateThread(*m_pDeflateData) );
+ comphelper::ThreadPool::getSharedOptimalPool().pushTask(std::move(pNew));
+ }
+ }
+ }
+ }
+
+ if(!m_aPrimitive2DSequence.empty() && !m_aRange.isEmpty())
+ {
+ // when we have data, also copy the buffered Range data as output
+ rRange = m_aRange;
+ }
+
+ return m_aPrimitive2DSequence;
+}
+
+void SwOLEObj::resetBufferedData()
+{
+ m_aPrimitive2DSequence = drawinglayer::primitive2d::Primitive2DContainer();
+ m_aRange.reset();
+
+ if(m_pDeflateData)
+ {
+ // load is in progress, wait until finished and cleanup without using it
+ m_pDeflateData->waitFinished();
+ m_pDeflateData.reset();
+ }
+}
+
+void SwOLEObj::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ xmlTextWriterStartElement(pWriter, BAD_CAST("SwOLEObj"));
+ xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ xmlTextWriterStartElement(pWriter, BAD_CAST("m_xOLERef"));
+ xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"),
+ BAD_CAST(typeid(*m_xOLERef.GetObject()).name()));
+ xmlTextWriterEndElement(pWriter);
+
+ xmlTextWriterEndElement(pWriter);
+}
+
+SwOLELRUCache::SwOLELRUCache()
+ : utl::ConfigItem("Office.Common/Cache")
+ , m_nLRU_InitSize( 20 )
+{
+ EnableNotification( GetPropertyNames() );
+ Load();
+}
+
+uno::Sequence< OUString > SwOLELRUCache::GetPropertyNames()
+{
+ Sequence< OUString > aNames { "Writer/OLE_Objects" };
+ return aNames;
+}
+
+void SwOLELRUCache::Notify( const uno::Sequence< OUString>& )
+{
+ Load();
+}
+
+void SwOLELRUCache::ImplCommit()
+{
+}
+
+void SwOLELRUCache::Load()
+{
+ Sequence< OUString > aNames( GetPropertyNames() );
+ Sequence< Any > aValues = GetProperties( aNames );
+ const Any* pValues = aValues.getConstArray();
+ OSL_ENSURE( aValues.getLength() == aNames.getLength(), "GetProperties failed" );
+ if (aValues.getLength() != aNames.getLength() || !pValues->hasValue())
+ return;
+
+ sal_Int32 nVal = 0;
+ *pValues >>= nVal;
+
+ if (nVal < m_nLRU_InitSize)
+ {
+ std::shared_ptr<SwOLELRUCache> xKeepAlive(g_pOLELRU_Cache); // prevent delete this
+ // size of cache has been changed
+ sal_Int32 nCount = m_OleObjects.size();
+ sal_Int32 nPos = nCount;
+
+ // try to remove the last entries until new maximum size is reached
+ while( nCount > nVal )
+ {
+ SwOLEObj *const pObj = m_OleObjects[ --nPos ];
+ if ( pObj->UnloadObject() )
+ nCount--;
+ if ( !nPos )
+ break;
+ }
+ }
+
+ m_nLRU_InitSize = nVal;
+}
+
+void SwOLELRUCache::InsertObj( SwOLEObj& rObj )
+{
+ SwOLEObj* pObj = &rObj;
+ OleObjects_t::iterator it =
+ std::find(m_OleObjects.begin(), m_OleObjects.end(), pObj);
+ if (it != m_OleObjects.end() && it != m_OleObjects.begin())
+ {
+ // object in cache but is currently not the first in cache
+ m_OleObjects.erase(it);
+ it = m_OleObjects.end();
+ }
+ if (it == m_OleObjects.end())
+ {
+ std::shared_ptr<SwOLELRUCache> xKeepAlive(g_pOLELRU_Cache); // prevent delete this
+ // try to remove objects if necessary
+ sal_Int32 nCount = m_OleObjects.size();
+ sal_Int32 nPos = nCount-1;
+ while (nPos >= 0 && nCount >= m_nLRU_InitSize)
+ {
+ pObj = m_OleObjects[ nPos-- ];
+ if ( pObj->UnloadObject() )
+ nCount--;
+ }
+ m_OleObjects.push_front(&rObj);
+ }
+}
+
+void SwOLELRUCache::RemoveObj( SwOLEObj& rObj )
+{
+ OleObjects_t::iterator const it =
+ std::find(m_OleObjects.begin(), m_OleObjects.end(), &rObj);
+ if (it != m_OleObjects.end())
+ {
+ m_OleObjects.erase(it);
+ }
+ if (m_OleObjects.empty())
+ {
+ if (g_pOLELRU_Cache.use_count() == 1) // test that we're not in InsertObj()
+ {
+ g_pOLELRU_Cache.reset();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */