1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/container/XChild.hpp>
21 #include <com/sun/star/embed/XEmbeddedObject.hpp>
22 #include <com/sun/star/embed/XEmbedPersist.hpp>
23 #include <com/sun/star/embed/XLinkageSupport.hpp>
24 #include <com/sun/star/embed/EmbedMisc.hpp>
25 #include <com/sun/star/embed/EmbedStates.hpp>
26 #include <com/sun/star/util/XModifiable.hpp>
27 #include <com/sun/star/chart2/XChartDocument.hpp>
28 #include <cppuhelper/implbase.hxx>
30 #include <sot/exchange.hxx>
31 #include <tools/globname.hxx>
32 #include <sfx2/linkmgr.hxx>
33 #include <unotools/configitem.hxx>
35 #include <vcl/outdev.hxx>
36 #include <fmtanchr.hxx>
41 #include <section.hxx>
45 #include <DocumentSettingManager.hxx>
46 #include <IDocumentLinksAdministration.hxx>
47 #include <IDocumentLayoutAccess.hxx>
48 #include <comphelper/classids.hxx>
49 #include <comphelper/propertyvalue.hxx>
50 #include <vcl/graph.hxx>
51 #include <sot/formats.hxx>
52 #include <vcl/svapp.hxx>
53 #include <strings.hrc>
54 #include <svx/charthelper.hxx>
55 #include <comphelper/threadpool.hxx>
58 #include <libxml/xmlwriter.h>
59 #include <osl/diagnose.h>
63 using namespace com::sun::star::uno
;
64 using namespace com::sun::star
;
69 : private utl::ConfigItem
72 std::deque
<SwOLEObj
*> m_OleObjects
;
73 sal_Int32 m_nLRU_InitSize
;
74 static uno::Sequence
< OUString
> GetPropertyNames();
76 virtual void ImplCommit() override
;
81 virtual void Notify( const uno::Sequence
<
82 OUString
>& aPropertyNames
) override
;
85 void InsertObj( SwOLEObj
& rObj
);
86 void RemoveObj( SwOLEObj
& rObj
);
91 static std::shared_ptr
<SwOLELRUCache
> g_pOLELRU_Cache
;
93 class SwOLEListener_Impl
: public ::cppu::WeakImplHelper
< embed::XStateChangeListener
>
97 explicit SwOLEListener_Impl( SwOLEObj
* pObj
);
99 virtual void SAL_CALL
changingState( const lang::EventObject
& aEvent
, ::sal_Int32 nOldState
, ::sal_Int32 nNewState
) override
;
100 virtual void SAL_CALL
stateChanged( const lang::EventObject
& aEvent
, ::sal_Int32 nOldState
, ::sal_Int32 nNewState
) override
;
101 virtual void SAL_CALL
disposing( const lang::EventObject
& aEvent
) override
;
104 SwOLEListener_Impl::SwOLEListener_Impl( SwOLEObj
* pObj
)
107 if ( mpObj
->IsOleRef() && mpObj
->GetOleRef()->getCurrentState() == embed::EmbedStates::RUNNING
)
109 g_pOLELRU_Cache
->InsertObj( *mpObj
);
113 void SAL_CALL
SwOLEListener_Impl::changingState( const lang::EventObject
&, ::sal_Int32
, ::sal_Int32
)
117 void SAL_CALL
SwOLEListener_Impl::stateChanged( const lang::EventObject
&, ::sal_Int32 nOldState
, ::sal_Int32 nNewState
)
119 if ( mpObj
&& nOldState
== embed::EmbedStates::LOADED
&& nNewState
== embed::EmbedStates::RUNNING
)
121 if (!g_pOLELRU_Cache
)
122 g_pOLELRU_Cache
= std::make_shared
<SwOLELRUCache
>();
123 g_pOLELRU_Cache
->InsertObj( *mpObj
);
125 else if ( mpObj
&& nNewState
== embed::EmbedStates::LOADED
&& nOldState
== embed::EmbedStates::RUNNING
)
128 g_pOLELRU_Cache
->RemoveObj( *mpObj
);
130 else if(mpObj
&& nNewState
== embed::EmbedStates::RUNNING
)
132 mpObj
->resetBufferedData();
136 void SwOLEListener_Impl::dispose()
138 if (mpObj
&& g_pOLELRU_Cache
)
139 g_pOLELRU_Cache
->RemoveObj( *mpObj
);
143 void SAL_CALL
SwOLEListener_Impl::disposing( const lang::EventObject
& )
145 if (mpObj
&& g_pOLELRU_Cache
)
146 g_pOLELRU_Cache
->RemoveObj( *mpObj
);
149 // TODO/LATER: actually SwEmbedObjectLink should be used here, but because different objects are used to control
150 // embedded object different link objects with the same functionality had to be implemented
154 class SwEmbedObjectLink
: public sfx2::SvBaseLink
156 SwOLENode
* m_pOleNode
;
159 explicit SwEmbedObjectLink(SwOLENode
* pNode
);
161 virtual void Closed() override
;
162 virtual ::sfx2::SvBaseLink::UpdateResult
DataChanged(
163 const OUString
& rMimeType
, const css::uno::Any
& rValue
) override
;
165 void Connect() { GetRealObject(); }
168 SwEmbedObjectLink::SwEmbedObjectLink(SwOLENode
* pNode
)
169 : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL
, SotClipboardFormatId::SVXB
)
172 SetSynchron( false );
175 ::sfx2::SvBaseLink::UpdateResult
SwEmbedObjectLink::DataChanged(
176 const OUString
&, const uno::Any
& )
178 if (!m_pOleNode
->UpdateLinkURL_Impl())
180 // the link URL was not changed
181 uno::Reference
<embed::XEmbeddedObject
> xObject
= m_pOleNode
->GetOLEObj().GetOleRef();
182 OSL_ENSURE( xObject
.is(), "The object must exist always!" );
185 // let the object reload the link
186 // TODO/LATER: reload call could be used for this case
190 sal_Int32 nState
= xObject
->getCurrentState();
191 if ( nState
!= embed::EmbedStates::LOADED
)
193 // in some cases the linked file probably is not locked so it could be changed
194 xObject
->changeState( embed::EmbedStates::LOADED
);
195 xObject
->changeState( nState
);
198 catch (const uno::Exception
&)
204 m_pOleNode
->GetNewReplacement();
205 m_pOleNode
->SetChanged();
210 void SwEmbedObjectLink::Closed()
212 m_pOleNode
->BreakFileLink_Impl();
213 SvBaseLink::Closed();
216 class SwIFrameLink
: public sfx2::SvBaseLink
218 SwOLENode
* m_pOleNode
;
221 explicit SwIFrameLink(SwOLENode
* pNode
)
222 : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL
, SotClipboardFormatId::SVXB
)
225 SetSynchron( false );
228 ::sfx2::SvBaseLink::UpdateResult
DataChanged(
229 const OUString
&, const uno::Any
& )
231 uno::Reference
<embed::XEmbeddedObject
> xObject
= m_pOleNode
->GetOLEObj().GetOleRef();
232 uno::Reference
<embed::XCommonEmbedPersist
> xPersObj(xObject
, uno::UNO_QUERY
);
235 // let the IFrameObject reload the link
238 xPersObj
->reload(uno::Sequence
<beans::PropertyValue
>(), uno::Sequence
<beans::PropertyValue
>());
240 catch (const uno::Exception
&)
244 m_pOleNode
->SetChanged();
254 SwOLENode::SwOLENode( SwNode
& rWhere
,
255 const svt::EmbeddedObjectRef
& xObj
,
256 SwGrfFormatColl
*pGrfColl
,
257 SwAttrSet
const * pAutoAttr
) :
258 SwNoTextNode( rWhere
, SwNodeType::Ole
, pGrfColl
, pAutoAttr
),
260 mbOLESizeInvalid( false ),
261 mpObjectLink( nullptr )
263 maOLEObj
.SetNode( this );
266 SwOLENode::SwOLENode( SwNode
& rWhere
,
267 const OUString
&rString
,
269 SwGrfFormatColl
*pGrfColl
,
270 SwAttrSet
const * pAutoAttr
) :
271 SwNoTextNode( rWhere
, SwNodeType::Ole
, pGrfColl
, pAutoAttr
),
272 maOLEObj( rString
, nAspect
),
273 mbOLESizeInvalid( false ),
274 mpObjectLink( nullptr )
276 maOLEObj
.SetNode( this );
279 SwOLENode::~SwOLENode()
281 DisconnectFileLink_Impl();
282 ResetAttr(RES_PAGEDESC
);
285 const Graphic
* SwOLENode::GetGraphic()
287 if ( maOLEObj
.GetOleRef().is() )
288 return maOLEObj
.m_xOLERef
.GetGraphic();
293 * Loading an OLE object that has been moved to the Undo Area
295 bool SwOLENode::RestorePersistentData()
297 OSL_ENSURE( maOLEObj
.GetOleRef().is(), "No object to restore!" );
298 if ( maOLEObj
.m_xOLERef
.is() )
300 // If a SvPersist instance already exists, we use it
301 SfxObjectShell
* p
= GetDoc().GetPersist();
304 // TODO/LATER: Isn't an EmbeddedObjectContainer sufficient here?
305 // What happens to this document?
306 OSL_ENSURE( false, "Why are we creating a DocShell here?" );
307 p
= new SwDocShell( GetDoc(), SfxObjectCreateMode::INTERNAL
);
311 uno::Reference
< container::XChild
> xChild( maOLEObj
.m_xOLERef
.GetObject(), uno::UNO_QUERY
);
313 xChild
->setParent( p
->GetModel() );
315 OSL_ENSURE( !maOLEObj
.m_aName
.isEmpty(), "No object name!" );
317 if ( !p
->GetEmbeddedObjectContainer().InsertEmbeddedObject( maOLEObj
.m_xOLERef
.GetObject(), aObjName
) )
320 xChild
->setParent( nullptr );
321 OSL_FAIL( "InsertObject failed" );
325 maOLEObj
.m_aName
= aObjName
;
326 maOLEObj
.m_xOLERef
.AssignToContainer( &p
->GetEmbeddedObjectContainer(), aObjName
);
327 CheckFileLink_Impl();
334 void SwOLENode::dumpAsXml(xmlTextWriterPtr pWriter
) const
336 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwOLENode"));
337 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
338 (void)xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("index"),
339 BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
341 GetOLEObj().dumpAsXml(pWriter
);
343 (void)xmlTextWriterEndElement(pWriter
);
347 * OLE object is transported into UNDO area
349 bool SwOLENode::SavePersistentData()
351 if( maOLEObj
.m_xOLERef
.is() )
353 comphelper::EmbeddedObjectContainer
* pCnt
= maOLEObj
.m_xOLERef
.GetContainer();
355 #if OSL_DEBUG_LEVEL > 0
356 SfxObjectShell
* p
= GetDoc().GetPersist();
357 OSL_ENSURE( p
, "No document!" );
360 comphelper::EmbeddedObjectContainer
& rCnt
= p
->GetEmbeddedObjectContainer();
361 OSL_ENSURE( !pCnt
|| &rCnt
== pCnt
, "The helper is assigned to unexpected container!" );
365 if ( pCnt
&& pCnt
->HasEmbeddedObject( maOLEObj
.m_aName
) )
367 uno::Reference
< container::XChild
> xChild( maOLEObj
.m_xOLERef
.GetObject(), uno::UNO_QUERY
);
369 xChild
->setParent( nullptr );
373 When cut or move the chart, SwUndoFlyBase::DelFly will call SaveSection
374 to store the content to storage. In this step, chart filter functions
375 will be called. And chart filter will call chart core functions to create
376 the chart again. Then chart core function will call the class
377 ExplicitCategoryProvider to create data source. In this step, when SW data
378 source provider create the data source, a UnoActionRemoveContext
379 will mess with the layout and create a new SwFlyFrame.
380 But later in SwUndoFlyBase::DelFly, it will clear anchor related attributes
381 of SwFlyFrame. Then finally null pointer occur.
383 In pCnt->RemoveEmbeddedObject in SaveSection process of table chart,
384 only remove the object from the object container, without removing it's
385 storage and graphic stream. The chart already removed from formatter.
387 bool bKeepObjectToTempStorage
= true;
388 uno::Reference
< embed::XEmbeddedObject
> xIP
= GetOLEObj().GetOleRef();
389 if (IsChart() && !msChartTableName
.isEmpty()
390 && svt::EmbeddedObjectRef::TryRunningState(xIP
))
392 uno::Reference
< chart2::XChartDocument
> xChart( xIP
->getComponent(), UNO_QUERY
);
393 if (xChart
.is() && !xChart
->hasInternalDataProvider())
395 bKeepObjectToTempStorage
= false;
399 pCnt
->RemoveEmbeddedObject( maOLEObj
.m_aName
, bKeepObjectToTempStorage
);
401 // TODO/LATER: aOLEObj.aName has no meaning here, since the undo container contains the object
402 // by different name, in future it might makes sense that the name is transported here.
403 maOLEObj
.m_xOLERef
.AssignToContainer( nullptr, maOLEObj
.m_aName
);
407 maOLEObj
.m_xOLERef
->changeState( embed::EmbedStates::LOADED
);
409 catch (const uno::Exception
&)
415 DisconnectFileLink_Impl();
420 SwOLENode
* SwNodes::MakeOLENode( SwNode
& rWhere
,
421 const svt::EmbeddedObjectRef
& xObj
,
422 SwGrfFormatColl
* pGrfColl
)
424 OSL_ENSURE( pGrfColl
,"SwNodes::MakeOLENode: Formatpointer is 0." );
427 new SwOLENode( rWhere
, xObj
, pGrfColl
, nullptr );
429 // set parent if XChild is supported
430 //!! needed to supply Math objects with a valid reference device
431 uno::Reference
< container::XChild
> xChild( pNode
->GetOLEObj().GetObject().GetObject(), UNO_QUERY
);
434 SwDocShell
*pDocSh
= GetDoc().GetDocShell();
436 xChild
->setParent( pDocSh
->GetModel() );
442 SwOLENode
* SwNodes::MakeOLENode( SwNode
& rWhere
,
443 const OUString
&rName
, sal_Int64 nAspect
, SwGrfFormatColl
* pGrfColl
, SwAttrSet
const * pAutoAttr
)
445 OSL_ENSURE( pGrfColl
,"SwNodes::MakeOLENode: Formatpointer is 0." );
448 new SwOLENode( rWhere
, rName
, nAspect
, pGrfColl
, pAutoAttr
);
450 // set parent if XChild is supported
451 //!! needed to supply Math objects with a valid reference device
452 uno::Reference
< container::XChild
> xChild( pNode
->GetOLEObj().GetObject().GetObject(), UNO_QUERY
);
455 SwDocShell
*pDocSh
= GetDoc().GetDocShell();
457 xChild
->setParent( pDocSh
->GetModel() );
463 Size
SwOLENode::GetTwipSize() const
465 MapMode
aMapMode( MapUnit::MapTwip
);
466 return const_cast<SwOLENode
*>(this)->maOLEObj
.GetObject().GetSize( &aMapMode
);
469 SwContentNode
* SwOLENode::MakeCopy( SwDoc
& rDoc
, SwNode
& rIdx
, bool) const
471 // If there's already a SvPersist instance, we use it
472 SfxObjectShell
* pPersistShell
= rDoc
.GetPersist();
475 // TODO/LATER: is EmbeddedObjectContainer not enough?
476 // the created document will be closed by rDoc ( should use SfxObjectShellLock )
477 pPersistShell
= new SwDocShell( rDoc
, SfxObjectCreateMode::INTERNAL
);
478 rDoc
.SetTmpDocShell( pPersistShell
);
479 pPersistShell
->DoInitNew();
482 // We insert it at SvPersist level
483 // TODO/LATER: check if using the same naming scheme for all apps works here
484 OUString aNewName
/*( Sw3Io::UniqueName( p->GetStorage(), "Obj" ) )*/;
485 SfxObjectShell
* pSrc
= GetDoc().GetPersist();
487 pPersistShell
->GetEmbeddedObjectContainer().CopyAndGetEmbeddedObject(
488 pSrc
->GetEmbeddedObjectContainer(),
489 pSrc
->GetEmbeddedObjectContainer().GetEmbeddedObject( maOLEObj
.m_aName
),
491 pSrc
->getDocumentBaseURL(),
492 pPersistShell
->getDocumentBaseURL());
494 SwOLENode
* pOLENd
= rDoc
.GetNodes().MakeOLENode( rIdx
, aNewName
, GetAspect(),
495 rDoc
.GetDfltGrfFormatColl(),
498 pOLENd
->SetChartTableName( GetChartTableName() );
499 pOLENd
->SetTitle( GetTitle() );
500 pOLENd
->SetDescription( GetDescription() );
501 pOLENd
->SetContour( HasContour(), HasAutomaticContour() );
502 pOLENd
->SetAspect( GetAspect() ); // the replacement image must be already copied
504 pOLENd
->SetOLESizeInvalid( true );
505 rDoc
.SetOLEPrtNotifyPending();
510 bool SwOLENode::IsInGlobalDocSection() const
512 // Find the "Body Anchor"
513 SwNodeOffset nEndExtraIdx
= GetNodes().GetEndOfExtras().GetIndex();
514 const SwNode
* pAnchorNd
= this;
516 SwFrameFormat
* pFlyFormat
= pAnchorNd
->GetFlyFormat();
520 const SwFormatAnchor
& rAnchor
= pFlyFormat
->GetAnchor();
521 if( !rAnchor
.GetAnchorNode() )
524 pAnchorNd
= rAnchor
.GetAnchorNode();
525 } while( pAnchorNd
->GetIndex() < nEndExtraIdx
);
527 const SwSectionNode
* pSectNd
= pAnchorNd
->FindSectionNode();
534 pSectNd
= pAnchorNd
->StartOfSectionNode()->FindSectionNode();
537 // pAnchorNd contains the most recently found Section Node, which
538 // now must fulfill the prerequisites for the GlobalDoc
539 pSectNd
= static_cast<const SwSectionNode
*>(pAnchorNd
);
540 return SectionType::FileLink
== pSectNd
->GetSection().GetType() &&
541 pSectNd
->GetIndex() > nEndExtraIdx
;
544 bool SwOLENode::IsOLEObjectDeleted() const
546 if( maOLEObj
.m_xOLERef
.is() )
548 SfxObjectShell
* p
= GetDoc().GetPersist();
549 if( p
) // Must be there
551 return !p
->GetEmbeddedObjectContainer().HasEmbeddedObject( maOLEObj
.m_aName
);
557 void SwOLENode::GetNewReplacement()
559 if ( maOLEObj
.m_xOLERef
.is() )
560 maOLEObj
.m_xOLERef
.UpdateReplacement();
563 bool SwOLENode::UpdateLinkURL_Impl()
565 bool bResult
= false;
569 OUString aNewLinkURL
;
570 sfx2::LinkManager::GetDisplayNames( mpObjectLink
, nullptr, &aNewLinkURL
);
571 if ( !aNewLinkURL
.equalsIgnoreAsciiCase( maLinkURL
) )
573 if ( !maOLEObj
.m_xOLERef
.is() )
574 maOLEObj
.GetOleRef();
576 uno::Reference
< embed::XEmbeddedObject
> xObj
= maOLEObj
.m_xOLERef
.GetObject();
577 uno::Reference
< embed::XCommonEmbedPersist
> xPersObj( xObj
, uno::UNO_QUERY
);
578 OSL_ENSURE( xPersObj
.is(), "The object must exist!" );
583 sal_Int32 nCurState
= xObj
->getCurrentState();
584 if ( nCurState
!= embed::EmbedStates::LOADED
)
585 xObj
->changeState( embed::EmbedStates::LOADED
);
587 // TODO/LATER: there should be possible to get current mediadescriptor settings from the object
588 uno::Sequence
< beans::PropertyValue
> aArgs
{ comphelper::makePropertyValue(
589 "URL", aNewLinkURL
) };
590 xPersObj
->reload( aArgs
, uno::Sequence
< beans::PropertyValue
>() );
592 maLinkURL
= aNewLinkURL
;
595 if ( nCurState
!= embed::EmbedStates::LOADED
)
596 xObj
->changeState( nCurState
);
598 catch (const uno::Exception
&)
605 // TODO/LATER: return the old name to the link manager, is it possible?
613 void SwOLENode::BreakFileLink_Impl()
615 SfxObjectShell
* pPers
= GetDoc().GetPersist();
620 uno::Reference
< embed::XStorage
> xStorage
= pPers
->GetStorage();
621 if ( !xStorage
.is() )
626 uno::Reference
< embed::XLinkageSupport
> xLinkSupport( maOLEObj
.GetOleRef(), uno::UNO_QUERY_THROW
);
627 xLinkSupport
->breakLink( xStorage
, maOLEObj
.GetCurrentPersistName() );
628 DisconnectFileLink_Impl();
631 catch( uno::Exception
& )
636 void SwOLENode::DisconnectFileLink_Impl()
640 GetDoc().getIDocumentLinksAdministration().GetLinkManager().Remove( mpObjectLink
);
641 mpObjectLink
= nullptr;
645 void SwOLENode::CheckFileLink_Impl()
647 if ( !maOLEObj
.m_xOLERef
.GetObject().is() || mpObjectLink
)
652 uno::Reference
<embed::XEmbeddedObject
> xObject
= maOLEObj
.m_xOLERef
.GetObject();
656 bool bIFrame
= false;
659 uno::Reference
<embed::XLinkageSupport
> xLinkSupport(xObject
, uno::UNO_QUERY
);
662 if (xLinkSupport
->isLink())
663 aLinkURL
= xLinkSupport
->getLinkURL();
667 // get IFrame (Floating Frames) listed and updatable from the
668 // manage links dialog
669 SvGlobalName
aClassId(xObject
->getClassID());
670 if (aClassId
== SvGlobalName(SO3_IFRAME_CLASSID
))
672 uno::Reference
<beans::XPropertySet
> xSet(xObject
->getComponent(), uno::UNO_QUERY
);
674 xSet
->getPropertyValue("FrameURL") >>= aLinkURL
;
679 if (!aLinkURL
.isEmpty()) // this is a file link so the model link manager should handle it
681 SwEmbedObjectLink
* pEmbedObjectLink
= nullptr;
684 pEmbedObjectLink
= new SwEmbedObjectLink(this);
685 mpObjectLink
= pEmbedObjectLink
;
689 mpObjectLink
= new SwIFrameLink(this);
691 maLinkURL
= aLinkURL
;
692 GetDoc().getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *mpObjectLink
, sfx2::SvBaseLinkObjectType::ClientOle
, aLinkURL
);
693 if (pEmbedObjectLink
)
694 pEmbedObjectLink
->Connect();
697 catch( uno::Exception
& )
703 bool SwOLENode::IsChart() const
705 bool bIsChart( false );
707 const uno::Reference
< embed::XEmbeddedObject
> xEmbObj
=
708 const_cast<SwOLEObj
&>(GetOLEObj()).GetOleRef();
711 SvGlobalName
aClassID( xEmbObj
->getClassID() );
712 bIsChart
= SotExchange::IsChart( aClassID
);
718 // react on visual change (invalidate)
719 void SwOLENode::SetChanged()
721 SwFrame
* pFrame(getLayoutFrame(nullptr));
723 if(nullptr == pFrame
)
728 const SwRect
aFrameArea(pFrame
->getFrameArea());
729 SwViewShell
* pVSh(GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell());
736 for(SwViewShell
& rShell
: pVSh
->GetRingContainer())
738 CurrShell
aCurr(&rShell
);
740 if(rShell
.VisArea().Overlaps(aFrameArea
) && OUTDEV_WINDOW
== rShell
.GetOut()->GetOutDevType())
742 // invalidate instead of painting
743 rShell
.GetWin()->Invalidate(aFrameArea
.SVRect());
748 namespace { class DeflateThread
; }
750 /// Holder for local data for a parallel-executed task to load a chart model
754 friend DeflateThread
;
755 friend class SwOLEObj
;
757 uno::Reference
< frame::XModel
> maXModel
;
758 drawinglayer::primitive2d::Primitive2DContainer maPrimitive2DSequence
;
759 basegfx::B2DRange maRange
;
761 // evtl.set from the SwOLEObj destructor when a WorkerThread is still active
762 // since it is not possible to kill it - let it terminate and delete the
763 // data working on itself
764 std::atomic
< bool> mbKilled
;
766 std::shared_ptr
<comphelper::ThreadTaskTag
> mpTag
;
769 explicit DeflateData(uno::Reference
< frame::XModel
> xXModel
)
770 : maXModel(std::move(xXModel
)),
772 mpTag( comphelper::ThreadPool::createThreadTaskTag() )
776 const drawinglayer::primitive2d::Primitive2DContainer
& getSequence() const
778 return maPrimitive2DSequence
;
781 const basegfx::B2DRange
& getRange() const
786 bool isFinished() const
788 return comphelper::ThreadPool::isTaskTagDone(mpTag
);
793 // need to wait until the load in progress is finished.
794 // WorkerThreads need the SolarMutex to be able to continue
795 // and finish the running import.
796 SolarMutexReleaser aReleaser
;
797 comphelper::ThreadPool::getSharedOptimalPool().waitUntilDone(mpTag
);
803 /// Task for parallelly-executed task to load a chart model
804 class DeflateThread
: public comphelper::ThreadTask
806 // the data to work on
807 DeflateData
& mrDeflateData
;
810 explicit DeflateThread(DeflateData
& rDeflateData
)
811 : comphelper::ThreadTask(rDeflateData
.mpTag
), mrDeflateData(rDeflateData
)
816 virtual void doWork() override
820 // load the chart data and get the primitives
821 mrDeflateData
.maPrimitive2DSequence
= ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
822 mrDeflateData
.maXModel
,
823 mrDeflateData
.maRange
);
825 // model no longer needed and done
826 mrDeflateData
.maXModel
.clear();
828 catch (const uno::Exception
&)
832 if(mrDeflateData
.mbKilled
)
834 // need to cleanup myself - data will not be used
835 delete &mrDeflateData
;
842 //////////////////////////////////////////////////////////////////////////////
844 SwOLEObj::SwOLEObj( const svt::EmbeddedObjectRef
& xObj
) :
845 m_pOLENode( nullptr ),
847 m_nGraphicVersion( 0 )
852 m_xListener
= new SwOLEListener_Impl( this );
853 xObj
->addStateChangeListener( m_xListener
);
857 SwOLEObj::SwOLEObj( OUString aString
, sal_Int64 nAspect
) :
858 m_pOLENode( nullptr ),
859 m_aName( std::move(aString
) ),
860 m_nGraphicVersion( 0 )
863 m_xOLERef
.SetViewAspect( nAspect
);
866 SwOLEObj::~SwOLEObj() COVERITY_NOEXCEPT_FALSE
870 // set flag so that the worker thread will delete m_pDeflateData
871 // when finished and forget about it
872 m_pDeflateData
->mbKilled
= true;
873 m_pDeflateData
= nullptr;
878 if ( m_xOLERef
.is() )
879 m_xOLERef
->removeStateChangeListener( m_xListener
);
880 m_xListener
->dispose();
884 if( m_pOLENode
&& !m_pOLENode
->GetDoc().IsInDtor() )
886 // if the model is not currently in destruction it means that this object should be removed from the model
887 comphelper::EmbeddedObjectContainer
* pCnt
= m_xOLERef
.GetContainer();
889 #if OSL_DEBUG_LEVEL > 0
890 SfxObjectShell
* p
= m_pOLENode
->GetDoc().GetPersist();
891 OSL_ENSURE( p
, "No document!" );
894 comphelper::EmbeddedObjectContainer
& rCnt
= p
->GetEmbeddedObjectContainer();
895 OSL_ENSURE( !pCnt
|| &rCnt
== pCnt
, "The helper is assigned to unexpected container!" );
899 if ( pCnt
&& pCnt
->HasEmbeddedObject( m_aName
) )
901 uno::Reference
< container::XChild
> xChild( m_xOLERef
.GetObject(), uno::UNO_QUERY
);
903 xChild
->setParent( nullptr );
905 // not already removed by deleting the object
906 m_xOLERef
.AssignToContainer( nullptr, m_aName
);
908 // unlock object so that object can be closed in RemoveEmbeddedObject
909 // successful closing of the object will automatically clear the reference then
910 m_xOLERef
.Lock(false);
912 // Always remove object from container it is connected to
915 // remove object from container but don't close it
916 pCnt
->RemoveEmbeddedObject( m_aName
);
918 catch ( uno::Exception
& )
925 if ( m_xOLERef
.is() )
926 // in case the object wasn't closed: release it
927 // in case the object was not in the container: it's still locked, try to close
931 void SwOLEObj::SetNode( SwOLENode
* pNode
)
934 if ( !m_aName
.isEmpty() )
937 SwDoc
& rDoc
= pNode
->GetDoc();
939 // If there's already a SvPersist instance, we use it
940 SfxObjectShell
* p
= rDoc
.GetPersist();
943 // TODO/LATER: Isn't an EmbeddedObjectContainer sufficient here?
944 // What happens to the document?
945 OSL_ENSURE( false, "Why are we creating a DocShell here??" );
946 p
= new SwDocShell( rDoc
, SfxObjectCreateMode::INTERNAL
);
951 uno::Reference
< container::XChild
> xChild( m_xOLERef
.GetObject(), uno::UNO_QUERY
);
952 if ( xChild
.is() && xChild
->getParent() != p
->GetModel() )
953 // it is possible that the parent was set already
954 xChild
->setParent( p
->GetModel() );
955 if (!p
->GetEmbeddedObjectContainer().InsertEmbeddedObject( m_xOLERef
.GetObject(), aObjName
) )
957 OSL_FAIL( "InsertObject failed" );
959 xChild
->setParent( nullptr );
962 m_xOLERef
.AssignToContainer( &p
->GetEmbeddedObjectContainer(), aObjName
);
964 const_cast<SwOLENode
*>(m_pOLENode
)->CheckFileLink_Impl(); // for this notification nonconst access is required
969 OUString
SwOLEObj::GetStyleString()
972 if (m_xOLERef
.is() && m_xOLERef
.IsChart())
973 strStyle
= m_xOLERef
.GetChartType();
977 bool SwOLEObj::IsOleRef() const
979 return m_xOLERef
.is();
982 IMPL_LINK_NOARG(SwOLEObj
, IsProtectedHdl
, LinkParamNone
*, bool) { return IsProtected(); }
984 bool SwOLEObj::IsProtected() const
991 SwFrame
* pFrame
= m_pOLENode
->getLayoutFrame(nullptr);
996 SwFrame
* pUpper
= pFrame
->GetUpper();
997 if (!pUpper
|| !pUpper
->IsFlyFrame())
1002 auto pFlyFrame
= static_cast<SwFlyFrame
*>(pUpper
);
1003 const SwFrame
* pAnchor
= pFlyFrame
->GetAnchorFrame();
1009 return pAnchor
->IsProtected();
1012 uno::Reference
< embed::XEmbeddedObject
> const & SwOLEObj::GetOleRef()
1014 if( !m_xOLERef
.is() )
1016 SfxObjectShell
* p
= m_pOLENode
->GetDoc().GetPersist();
1017 assert(p
&& "No SvPersist present");
1019 OUString sDocumentBaseURL
= p
->getDocumentBaseURL();
1020 uno::Reference
< embed::XEmbeddedObject
> xObj
= p
->GetEmbeddedObjectContainer().GetEmbeddedObject(m_aName
, &sDocumentBaseURL
);
1021 OSL_ENSURE( !m_xOLERef
.is(), "Calling GetOleRef() recursively is not permitted" );
1025 // We could not load this part (probably broken)
1026 tools::Rectangle aArea
;
1027 SwFrame
*pFrame
= m_pOLENode
->getLayoutFrame(nullptr);
1030 Size
aSz( pFrame
->getFrameArea().SSize() );
1031 aSz
= o3tl::convert( aSz
, o3tl::Length::twip
, o3tl::Length::mm100
);
1032 aArea
.SetSize( aSz
);
1035 aArea
.SetSize( Size( 5000, 5000 ) );
1036 // TODO/LATER: set replacement graphic for dead object
1037 // It looks as if it should work even without the object, because the replace will be generated automatically
1039 xObj
= p
->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_DUMMY_CLASSID
).GetByteSequence(), aTmpName
);
1043 m_xOLERef
.SetIsProtectedHdl(LINK(this, SwOLEObj
, IsProtectedHdl
));
1044 m_xOLERef
.Assign( xObj
, m_xOLERef
.GetViewAspect() );
1045 m_xOLERef
.AssignToContainer( &p
->GetEmbeddedObjectContainer(), m_aName
);
1046 m_xListener
= new SwOLEListener_Impl( this );
1047 xObj
->addStateChangeListener( m_xListener
);
1050 const_cast<SwOLENode
*>(m_pOLENode
)->CheckFileLink_Impl(); // for this notification nonconst access is required
1052 else if ( m_xOLERef
->getCurrentState() == embed::EmbedStates::RUNNING
)
1054 // move object to first position in cache
1055 if (!g_pOLELRU_Cache
)
1056 g_pOLELRU_Cache
= std::make_shared
<SwOLELRUCache
>();
1057 g_pOLELRU_Cache
->InsertObj( *this );
1060 return m_xOLERef
.GetObject();
1063 svt::EmbeddedObjectRef
& SwOLEObj::GetObject()
1069 bool SwOLEObj::UnloadObject()
1074 const SwDoc
& rDoc
= m_pOLENode
->GetDoc();
1075 bRet
= UnloadObject( m_xOLERef
.GetObject(), &rDoc
, m_xOLERef
.GetViewAspect() );
1081 PurgeGuard::PurgeGuard(const SwDoc
& rDoc
)
1082 : m_rManager(const_cast<SwDoc
&>(rDoc
).GetDocumentSettingManager())
1083 , m_bOrigPurgeOle(m_rManager
.get(DocumentSettingId::PURGE_OLE
))
1085 m_rManager
.set(DocumentSettingId::PURGE_OLE
, false);
1088 PurgeGuard::~PurgeGuard() COVERITY_NOEXCEPT_FALSE
1090 m_rManager
.set(DocumentSettingId::PURGE_OLE
, m_bOrigPurgeOle
);
1093 bool SwOLEObj::UnloadObject( uno::Reference
< embed::XEmbeddedObject
> const & xObj
, const SwDoc
* pDoc
, sal_Int64 nAspect
)
1099 sal_Int32 nState
= xObj
.is() ? xObj
->getCurrentState() : embed::EmbedStates::LOADED
;
1100 bool bIsActive
= ( nState
!= embed::EmbedStates::LOADED
&& nState
!= embed::EmbedStates::RUNNING
);
1101 sal_Int64 nMiscStatus
= xObj
->getStatus( nAspect
);
1103 if( nState
!= embed::EmbedStates::LOADED
&& !pDoc
->IsInDtor() && !bIsActive
&&
1104 embed::EmbedMisc::MS_EMBED_ALWAYSRUN
!= ( nMiscStatus
& embed::EmbedMisc::MS_EMBED_ALWAYSRUN
) &&
1105 embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY
!= ( nMiscStatus
& embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY
) )
1107 SfxObjectShell
* p
= pDoc
->GetPersist();
1110 if( pDoc
->GetDocumentSettingManager().get(DocumentSettingId::PURGE_OLE
) )
1114 uno::Reference
< util::XModifiable
> xMod( xObj
->getComponent(), uno::UNO_QUERY
);
1115 if( xMod
.is() && xMod
->isModified() )
1117 uno::Reference
< embed::XEmbedPersist
> xPers( xObj
, uno::UNO_QUERY
);
1118 assert(xPers
.is() && "Modified object without persistence in cache!");
1120 PurgeGuard
aGuard(*pDoc
);
1124 // setting object to loaded state will remove it from cache
1125 xObj
->changeState( embed::EmbedStates::LOADED
);
1127 catch (const uno::Exception
&)
1140 OUString
SwOLEObj::GetDescription()
1142 uno::Reference
< embed::XEmbeddedObject
> xEmbObj
= GetOleRef();
1143 if ( !xEmbObj
.is() )
1146 SvGlobalName
aClassID( xEmbObj
->getClassID() );
1147 if ( SotExchange::IsMath( aClassID
) )
1148 return SwResId(STR_MATH_FORMULA
);
1150 if ( SotExchange::IsChart( aClassID
) )
1151 return SwResId(STR_CHART
);
1153 return SwResId(STR_OLE
);
1156 drawinglayer::primitive2d::Primitive2DContainer
const & SwOLEObj::tryToGetChartContentAsPrimitive2DSequence(
1157 basegfx::B2DRange
& rRange
,
1164 // data in high quality is requested, wait until the data is available
1165 // since a WorkerThread was already started to load it
1166 m_pDeflateData
->waitFinished();
1169 if(m_pDeflateData
->isFinished())
1171 // copy the result data and cleanup
1172 m_aPrimitive2DSequence
= m_pDeflateData
->getSequence();
1173 m_aRange
= m_pDeflateData
->getRange();
1174 m_nGraphicVersion
= GetObject().getGraphicVersion();
1175 m_pDeflateData
.reset();
1179 if(!m_aPrimitive2DSequence
.empty() && !m_aRange
.isEmpty()
1180 && m_nGraphicVersion
!= GetObject().getGraphicVersion())
1182 // tdf#149189 use getGraphicVersion() from EmbeddedObjectRef
1183 // to decide when to reset buffered data. It gets incremented
1184 // at all occasions where the graphic changes. An alternative
1185 // would be to extend SwOLEListener_Impl with a XModifyListener
1186 // as it is done in EmbedEventListener_Impl, that would
1187 // require all the (add|remove)ModifyListener calls and
1188 // managing these, plus having a 2nd listener to these when
1189 // EmbeddedObjectRef already provides that. Tried that this
1190 // works also if an alternative would be needed.
1191 resetBufferedData();
1194 if(m_aPrimitive2DSequence
.empty() && m_aRange
.isEmpty() && m_xOLERef
.is() && m_xOLERef
.IsChart())
1196 const uno::Reference
< frame::XModel
> aXModel(m_xOLERef
->getComponent(), uno::UNO_QUERY
);
1200 // disabled for now, need to check deeper
1201 static bool bAsynchronousLoadingAllowed
= false; // loplugin:constvars:ignore
1204 !bAsynchronousLoadingAllowed
)
1206 // load chart synchron in this Thread
1207 m_aPrimitive2DSequence
= ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
1213 // if not yet setup, initiate and start a WorkerThread to load the chart
1214 // and it's primitives asynchron. If it already works, returning nothing
1215 // is okay (preview will be reused)
1218 m_pDeflateData
.reset( new DeflateData(aXModel
) );
1219 std::unique_ptr
<DeflateThread
> pNew( new DeflateThread(*m_pDeflateData
) );
1220 comphelper::ThreadPool::getSharedOptimalPool().pushTask(std::move(pNew
));
1226 if(!m_aPrimitive2DSequence
.empty() && !m_aRange
.isEmpty())
1228 // when we have data, also copy the buffered Range data as output
1231 // tdf#149189 ..and the GraphicVersion number to identify changes
1232 m_nGraphicVersion
= GetObject().getGraphicVersion();
1235 return m_aPrimitive2DSequence
;
1238 void SwOLEObj::resetBufferedData()
1240 m_aPrimitive2DSequence
= drawinglayer::primitive2d::Primitive2DContainer();
1245 // load is in progress, wait until finished and cleanup without using it
1246 m_pDeflateData
->waitFinished();
1247 m_pDeflateData
.reset();
1251 void SwOLEObj::dumpAsXml(xmlTextWriterPtr pWriter
) const
1253 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwOLEObj"));
1254 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
1256 m_xOLERef
.dumpAsXml(pWriter
);
1258 (void)xmlTextWriterEndElement(pWriter
);
1261 SwOLELRUCache::SwOLELRUCache()
1262 : utl::ConfigItem("Office.Common/Cache")
1263 , m_nLRU_InitSize( 20 )
1265 EnableNotification( GetPropertyNames() );
1269 uno::Sequence
< OUString
> SwOLELRUCache::GetPropertyNames()
1271 Sequence
< OUString
> aNames
{ "Writer/OLE_Objects" };
1275 void SwOLELRUCache::Notify( const uno::Sequence
< OUString
>& )
1280 void SwOLELRUCache::ImplCommit()
1284 void SwOLELRUCache::Load()
1286 Sequence
< OUString
> aNames( GetPropertyNames() );
1287 Sequence
< Any
> aValues
= GetProperties( aNames
);
1288 const Any
* pValues
= aValues
.getConstArray();
1289 OSL_ENSURE( aValues
.getLength() == aNames
.getLength(), "GetProperties failed" );
1290 if (aValues
.getLength() != aNames
.getLength() || !pValues
->hasValue())
1296 if (nVal
< m_nLRU_InitSize
)
1298 std::shared_ptr
<SwOLELRUCache
> xKeepAlive(g_pOLELRU_Cache
); // prevent delete this
1299 // size of cache has been changed
1300 sal_Int32 nCount
= m_OleObjects
.size();
1301 sal_Int32 nPos
= nCount
;
1303 // try to remove the last entries until new maximum size is reached
1304 while( nCount
> nVal
)
1306 SwOLEObj
*const pObj
= m_OleObjects
[ --nPos
];
1307 if ( pObj
->UnloadObject() )
1314 m_nLRU_InitSize
= nVal
;
1317 void SwOLELRUCache::InsertObj( SwOLEObj
& rObj
)
1319 SwOLEObj
* pObj
= &rObj
;
1320 if (auto const it
= std::find(m_OleObjects
.begin(), m_OleObjects
.end(), pObj
);
1321 it
!= m_OleObjects
.end())
1323 if (it
== m_OleObjects
.begin())
1324 return; // Everything is already in place
1325 // object in cache but is currently not the first in cache
1326 m_OleObjects
.erase(it
);
1329 std::shared_ptr
<SwOLELRUCache
> xKeepAlive(g_pOLELRU_Cache
); // prevent delete this
1330 // try to remove objects if necessary
1331 sal_Int32 nCount
= m_OleObjects
.size();
1332 sal_Int32 nPos
= nCount
-1;
1333 while (nPos
>= 0 && nCount
>= m_nLRU_InitSize
)
1335 pObj
= m_OleObjects
[ nPos
-- ];
1336 if ( pObj
->UnloadObject() )
1339 m_OleObjects
.push_front(&rObj
);
1342 void SwOLELRUCache::RemoveObj( SwOLEObj
& rObj
)
1344 auto const it
= std::find(m_OleObjects
.begin(), m_OleObjects
.end(), &rObj
);
1345 if (it
!= m_OleObjects
.end())
1347 m_OleObjects
.erase(it
);
1349 if (m_OleObjects
.empty())
1351 if (g_pOLELRU_Cache
.use_count() == 1) // test that we're not in InsertObj()
1353 g_pOLELRU_Cache
.reset();
1358 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */