Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / ole / ndole.cxx
blobde94986797f39b64db93f2478f04709033bc5650
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
34 #include <utility>
35 #include <vcl/outdev.hxx>
36 #include <fmtanchr.hxx>
37 #include <frmfmt.hxx>
38 #include <doc.hxx>
39 #include <docsh.hxx>
40 #include <pam.hxx>
41 #include <section.hxx>
42 #include <cntfrm.hxx>
43 #include <ndole.hxx>
44 #include <viewsh.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>
56 #include <atomic>
57 #include <deque>
58 #include <libxml/xmlwriter.h>
59 #include <osl/diagnose.h>
60 #include <flyfrm.hxx>
62 using namespace utl;
63 using namespace com::sun::star::uno;
64 using namespace com::sun::star;
66 namespace {
68 class SwOLELRUCache
69 : private utl::ConfigItem
71 private:
72 std::deque<SwOLEObj *> m_OleObjects;
73 sal_Int32 m_nLRU_InitSize;
74 static uno::Sequence< OUString > GetPropertyNames();
76 virtual void ImplCommit() override;
78 public:
79 SwOLELRUCache();
81 virtual void Notify( const uno::Sequence<
82 OUString>& aPropertyNames ) override;
83 void Load();
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 >
95 SwOLEObj* mpObj;
96 public:
97 explicit SwOLEListener_Impl( SwOLEObj* pObj );
98 void dispose();
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 )
105 : mpObj( 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 )
127 if (g_pOLELRU_Cache)
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 );
140 mpObj = nullptr;
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
152 namespace {
154 class SwEmbedObjectLink : public sfx2::SvBaseLink
156 SwOLENode* m_pOleNode;
158 public:
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)
170 , m_pOleNode(pNode)
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!" );
183 if ( xObject.is() )
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();
207 return SUCCESS;
210 void SwEmbedObjectLink::Closed()
212 m_pOleNode->BreakFileLink_Impl();
213 SvBaseLink::Closed();
216 class SwIFrameLink : public sfx2::SvBaseLink
218 SwOLENode* m_pOleNode;
220 public:
221 explicit SwIFrameLink(SwOLENode* pNode)
222 : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB)
223 , m_pOleNode(pNode)
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);
233 if (xPersObj.is())
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();
247 return SUCCESS;
254 SwOLENode::SwOLENode( SwNode& rWhere,
255 const svt::EmbeddedObjectRef& xObj,
256 SwGrfFormatColl *pGrfColl,
257 SwAttrSet const * pAutoAttr ) :
258 SwNoTextNode( rWhere, SwNodeType::Ole, pGrfColl, pAutoAttr ),
259 maOLEObj( xObj ),
260 mbOLESizeInvalid( false ),
261 mpObjectLink( nullptr )
263 maOLEObj.SetNode( this );
266 SwOLENode::SwOLENode( SwNode& rWhere,
267 const OUString &rString,
268 sal_Int64 nAspect,
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();
289 return nullptr;
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();
302 if( !p )
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 );
308 p->DoInitNew();
311 uno::Reference < container::XChild > xChild( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY );
312 if ( xChild.is() )
313 xChild->setParent( p->GetModel() );
315 OSL_ENSURE( !maOLEObj.m_aName.isEmpty(), "No object name!" );
316 OUString aObjName;
317 if ( !p->GetEmbeddedObjectContainer().InsertEmbeddedObject( maOLEObj.m_xOLERef.GetObject(), aObjName ) )
319 if ( xChild.is() )
320 xChild->setParent( nullptr );
321 OSL_FAIL( "InsertObject failed" );
323 else
325 maOLEObj.m_aName = aObjName;
326 maOLEObj.m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName );
327 CheckFileLink_Impl();
331 return true;
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!" );
358 if( p )
360 comphelper::EmbeddedObjectContainer& rCnt = p->GetEmbeddedObjectContainer();
361 OSL_ENSURE( !pCnt || &rCnt == pCnt, "The helper is assigned to unexpected container!" );
363 #endif
365 if ( pCnt && pCnt->HasEmbeddedObject( maOLEObj.m_aName ) )
367 uno::Reference < container::XChild > xChild( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY );
368 if ( xChild.is() )
369 xChild->setParent( nullptr );
372 #i119941
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.
382 Resolution:
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 );
406 // "unload" object
407 maOLEObj.m_xOLERef->changeState( embed::EmbedStates::LOADED );
409 catch (const uno::Exception&)
415 DisconnectFileLink_Impl();
417 return true;
420 SwOLENode * SwNodes::MakeOLENode( SwNode& rWhere,
421 const svt::EmbeddedObjectRef& xObj,
422 SwGrfFormatColl* pGrfColl )
424 OSL_ENSURE( pGrfColl,"SwNodes::MakeOLENode: Formatpointer is 0." );
426 SwOLENode *pNode =
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 );
432 if (xChild.is())
434 SwDocShell *pDocSh = GetDoc().GetDocShell();
435 if (pDocSh)
436 xChild->setParent( pDocSh->GetModel() );
439 return pNode;
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." );
447 SwOLENode *pNode =
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 );
453 if (xChild.is())
455 SwDocShell *pDocSh = GetDoc().GetDocShell();
456 if (pDocSh)
457 xChild->setParent( pDocSh->GetModel() );
460 return pNode;
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();
473 if( !pPersistShell )
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 ),
490 aNewName,
491 pSrc->getDocumentBaseURL(),
492 pPersistShell->getDocumentBaseURL());
494 SwOLENode* pOLENd = rDoc.GetNodes().MakeOLENode( rIdx, aNewName, GetAspect(),
495 rDoc.GetDfltGrfFormatColl(),
496 GetpSwAttrSet() );
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();
507 return pOLENd;
510 bool SwOLENode::IsInGlobalDocSection() const
512 // Find the "Body Anchor"
513 SwNodeOffset nEndExtraIdx = GetNodes().GetEndOfExtras().GetIndex();
514 const SwNode* pAnchorNd = this;
515 do {
516 SwFrameFormat* pFlyFormat = pAnchorNd->GetFlyFormat();
517 if( !pFlyFormat )
518 return false;
520 const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
521 if( !rAnchor.GetAnchorNode() )
522 return false;
524 pAnchorNd = rAnchor.GetAnchorNode();
525 } while( pAnchorNd->GetIndex() < nEndExtraIdx );
527 const SwSectionNode* pSectNd = pAnchorNd->FindSectionNode();
528 if( !pSectNd )
529 return false;
531 while( pSectNd )
533 pAnchorNd = pSectNd;
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 );
554 return false;
557 void SwOLENode::GetNewReplacement()
559 if ( maOLEObj.m_xOLERef.is() )
560 maOLEObj.m_xOLERef.UpdateReplacement();
563 bool SwOLENode::UpdateLinkURL_Impl()
565 bool bResult = false;
567 if ( mpObjectLink )
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!" );
579 if ( xPersObj.is() )
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;
593 bResult = true;
595 if ( nCurState != embed::EmbedStates::LOADED )
596 xObj->changeState( nCurState );
598 catch (const uno::Exception&)
603 if ( !bResult )
605 // TODO/LATER: return the old name to the link manager, is it possible?
610 return bResult;
613 void SwOLENode::BreakFileLink_Impl()
615 SfxObjectShell* pPers = GetDoc().GetPersist();
617 if ( !pPers )
618 return;
620 uno::Reference< embed::XStorage > xStorage = pPers->GetStorage();
621 if ( !xStorage.is() )
622 return;
626 uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.GetOleRef(), uno::UNO_QUERY_THROW );
627 xLinkSupport->breakLink( xStorage, maOLEObj.GetCurrentPersistName() );
628 DisconnectFileLink_Impl();
629 maLinkURL.clear();
631 catch( uno::Exception& )
636 void SwOLENode::DisconnectFileLink_Impl()
638 if ( mpObjectLink )
640 GetDoc().getIDocumentLinksAdministration().GetLinkManager().Remove( mpObjectLink );
641 mpObjectLink = nullptr;
645 void SwOLENode::CheckFileLink_Impl()
647 if ( !maOLEObj.m_xOLERef.GetObject().is() || mpObjectLink )
648 return;
652 uno::Reference<embed::XEmbeddedObject> xObject = maOLEObj.m_xOLERef.GetObject();
653 if (!xObject)
654 return;
656 bool bIFrame = false;
658 OUString aLinkURL;
659 uno::Reference<embed::XLinkageSupport> xLinkSupport(xObject, uno::UNO_QUERY);
660 if (xLinkSupport)
662 if (xLinkSupport->isLink())
663 aLinkURL = xLinkSupport->getLinkURL();
665 else
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);
673 if (xSet.is())
674 xSet->getPropertyValue("FrameURL") >>= aLinkURL;
675 bIFrame = true;
679 if (!aLinkURL.isEmpty()) // this is a file link so the model link manager should handle it
681 SwEmbedObjectLink* pEmbedObjectLink = nullptr;
682 if (!bIFrame)
684 pEmbedObjectLink = new SwEmbedObjectLink(this);
685 mpObjectLink = pEmbedObjectLink;
687 else
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& )
702 // #i99665#
703 bool SwOLENode::IsChart() const
705 bool bIsChart( false );
707 const uno::Reference< embed::XEmbeddedObject > xEmbObj =
708 const_cast<SwOLEObj&>(GetOLEObj()).GetOleRef();
709 if ( xEmbObj.is() )
711 SvGlobalName aClassID( xEmbObj->getClassID() );
712 bIsChart = SotExchange::IsChart( aClassID );
715 return bIsChart;
718 // react on visual change (invalidate)
719 void SwOLENode::SetChanged()
721 SwFrame* pFrame(getLayoutFrame(nullptr));
723 if(nullptr == pFrame)
725 return;
728 const SwRect aFrameArea(pFrame->getFrameArea());
729 SwViewShell* pVSh(GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell());
731 if(nullptr == pVSh)
733 return;
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
751 class DeflateData
753 private:
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;
768 public:
769 explicit DeflateData(uno::Reference< frame::XModel > xXModel)
770 : maXModel(std::move(xXModel)),
771 mbKilled(false),
772 mpTag( comphelper::ThreadPool::createThreadTaskTag() )
776 const drawinglayer::primitive2d::Primitive2DContainer& getSequence() const
778 return maPrimitive2DSequence;
781 const basegfx::B2DRange& getRange() const
783 return maRange;
786 bool isFinished() const
788 return comphelper::ThreadPool::isTaskTagDone(mpTag);
791 void waitFinished()
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);
801 namespace {
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;
809 public:
810 explicit DeflateThread(DeflateData& rDeflateData)
811 : comphelper::ThreadTask(rDeflateData.mpTag), mrDeflateData(rDeflateData)
815 private:
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 ),
846 m_xOLERef( xObj ),
847 m_nGraphicVersion( 0 )
849 m_xOLERef.Lock();
850 if ( xObj.is() )
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 )
862 m_xOLERef.Lock();
863 m_xOLERef.SetViewAspect( nAspect );
866 SwOLEObj::~SwOLEObj() COVERITY_NOEXCEPT_FALSE
868 if(m_pDeflateData)
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;
876 if( m_xListener )
878 if ( m_xOLERef.is() )
879 m_xOLERef->removeStateChangeListener( m_xListener );
880 m_xListener->dispose();
881 m_xListener.clear();
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!" );
892 if( p )
894 comphelper::EmbeddedObjectContainer& rCnt = p->GetEmbeddedObjectContainer();
895 OSL_ENSURE( !pCnt || &rCnt == pCnt, "The helper is assigned to unexpected container!" );
897 #endif
899 if ( pCnt && pCnt->HasEmbeddedObject( m_aName ) )
901 uno::Reference < container::XChild > xChild( m_xOLERef.GetObject(), uno::UNO_QUERY );
902 if ( xChild.is() )
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
928 m_xOLERef.Clear();
931 void SwOLEObj::SetNode( SwOLENode* pNode )
933 m_pOLENode = pNode;
934 if ( !m_aName.isEmpty() )
935 return;
937 SwDoc& rDoc = pNode->GetDoc();
939 // If there's already a SvPersist instance, we use it
940 SfxObjectShell* p = rDoc.GetPersist();
941 if( !p )
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 );
947 p->DoInitNew();
950 OUString aObjName;
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" );
958 if ( xChild.is() )
959 xChild->setParent( nullptr );
961 else
962 m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName );
964 const_cast<SwOLENode*>(m_pOLENode)->CheckFileLink_Impl(); // for this notification nonconst access is required
966 m_aName = aObjName;
969 OUString SwOLEObj::GetStyleString()
971 OUString strStyle;
972 if (m_xOLERef.is() && m_xOLERef.IsChart())
973 strStyle = m_xOLERef.GetChartType();
974 return strStyle;
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
986 if (!m_pOLENode)
988 return false;
991 SwFrame* pFrame = m_pOLENode->getLayoutFrame(nullptr);
992 if (!pFrame)
994 return false;
996 SwFrame* pUpper = pFrame->GetUpper();
997 if (!pUpper || !pUpper->IsFlyFrame())
999 return false;
1002 auto pFlyFrame = static_cast<SwFlyFrame*>(pUpper);
1003 const SwFrame* pAnchor = pFlyFrame->GetAnchorFrame();
1004 if (!pAnchor)
1006 return false;
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" );
1023 if ( !xObj.is() )
1025 // We could not load this part (probably broken)
1026 tools::Rectangle aArea;
1027 SwFrame *pFrame = m_pOLENode->getLayoutFrame(nullptr);
1028 if ( pFrame )
1030 Size aSz( pFrame->getFrameArea().SSize() );
1031 aSz = o3tl::convert( aSz, o3tl::Length::twip, o3tl::Length::mm100 );
1032 aArea.SetSize( aSz );
1034 else
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
1038 OUString aTmpName;
1039 xObj = p->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_DUMMY_CLASSID ).GetByteSequence(), aTmpName );
1041 if (xObj.is())
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()
1065 GetOleRef();
1066 return m_xOLERef;
1069 bool SwOLEObj::UnloadObject()
1071 bool bRet = true;
1072 if ( m_pOLENode )
1074 const SwDoc& rDoc = m_pOLENode->GetDoc();
1075 bRet = UnloadObject( m_xOLERef.GetObject(), &rDoc, m_xOLERef.GetViewAspect() );
1078 return bRet;
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 )
1095 if ( !pDoc )
1096 return false;
1098 bool bRet = true;
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();
1108 if( p )
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);
1121 xPers->storeOwn();
1124 // setting object to loaded state will remove it from cache
1125 xObj->changeState( embed::EmbedStates::LOADED );
1127 catch (const uno::Exception&)
1129 bRet = false;
1132 else
1133 bRet = false;
1137 return bRet;
1140 OUString SwOLEObj::GetDescription()
1142 uno::Reference< embed::XEmbeddedObject > xEmbObj = GetOleRef();
1143 if ( !xEmbObj.is() )
1144 return OUString();
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,
1158 bool bSynchron)
1160 if(m_pDeflateData)
1162 if(bSynchron)
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);
1198 if(aXModel.is())
1200 // disabled for now, need to check deeper
1201 static bool bAsynchronousLoadingAllowed = false; // loplugin:constvars:ignore
1203 if(bSynchron ||
1204 !bAsynchronousLoadingAllowed)
1206 // load chart synchron in this Thread
1207 m_aPrimitive2DSequence = ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
1208 aXModel,
1209 m_aRange);
1211 else
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)
1216 if(!m_pDeflateData)
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
1229 rRange = m_aRange;
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();
1241 m_aRange.reset();
1243 if(m_pDeflateData)
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() );
1266 Load();
1269 uno::Sequence< OUString > SwOLELRUCache::GetPropertyNames()
1271 Sequence< OUString > aNames { "Writer/OLE_Objects" };
1272 return aNames;
1275 void SwOLELRUCache::Notify( const uno::Sequence< OUString>& )
1277 Load();
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())
1291 return;
1293 sal_Int32 nVal = 0;
1294 *pValues >>= nVal;
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() )
1308 nCount--;
1309 if ( !nPos )
1310 break;
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() )
1337 nCount--;
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: */