/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; namespace comphelper { typedef std::unordered_map> EmbeddedObjectContainerNameMap; struct EmbedImpl { // TODO/LATER: remove objects from temp. Container storage when object is disposed EmbeddedObjectContainerNameMap maNameToObjectMap; // to speed up lookup by Reference std::unordered_map, OUString> maObjectToNameMap; uno::Reference < embed::XStorage > mxStorage; EmbeddedObjectContainer* mpTempObjectContainer; uno::Reference < embed::XStorage > mxImageStorage; uno::WeakReference < uno::XInterface > m_xModel; bool mbOwnsStorage : 1; bool mbUserAllowsLinkUpdate : 1; const uno::Reference < embed::XStorage >& GetReplacements(); }; const uno::Reference < embed::XStorage >& EmbedImpl::GetReplacements() { if ( !mxImageStorage.is() ) { try { mxImageStorage = mxStorage->openStorageElement( "ObjectReplacements", embed::ElementModes::READWRITE ); } catch (const uno::Exception&) { mxImageStorage = mxStorage->openStorageElement( "ObjectReplacements", embed::ElementModes::READ ); } } if ( !mxImageStorage.is() ) throw io::IOException("No ObjectReplacements"); return mxImageStorage; } EmbeddedObjectContainer::EmbeddedObjectContainer() : pImpl(new EmbedImpl) { pImpl->mxStorage = ::comphelper::OStorageHelper::GetTemporaryStorage(); pImpl->mbOwnsStorage = true; pImpl->mbUserAllowsLinkUpdate = true; pImpl->mpTempObjectContainer = nullptr; } EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor ) : pImpl(new EmbedImpl) { pImpl->mxStorage = rStor; pImpl->mbOwnsStorage = false; pImpl->mbUserAllowsLinkUpdate = true; pImpl->mpTempObjectContainer = nullptr; } EmbeddedObjectContainer::EmbeddedObjectContainer( const uno::Reference < embed::XStorage >& rStor, const uno::Reference < uno::XInterface >& xModel ) : pImpl(new EmbedImpl) { pImpl->mxStorage = rStor; pImpl->mbOwnsStorage = false; pImpl->mbUserAllowsLinkUpdate = true; pImpl->mpTempObjectContainer = nullptr; pImpl->m_xModel = xModel; } void EmbeddedObjectContainer::SwitchPersistence( const uno::Reference < embed::XStorage >& rStor ) { ReleaseImageSubStorage(); if ( pImpl->mbOwnsStorage ) pImpl->mxStorage->dispose(); pImpl->mxStorage = rStor; pImpl->mbOwnsStorage = false; } bool EmbeddedObjectContainer::CommitImageSubStorage() { if ( !pImpl->mxImageStorage ) return true; try { bool bReadOnlyMode = true; uno::Reference < beans::XPropertySet > xSet(pImpl->mxImageStorage,uno::UNO_QUERY); if ( xSet.is() ) { // get the open mode from the parent storage sal_Int32 nMode = 0; uno::Any aAny = xSet->getPropertyValue("OpenMode"); if ( aAny >>= nMode ) bReadOnlyMode = !(nMode & embed::ElementModes::WRITE ); } // if ( xSet.is() ) if ( !bReadOnlyMode ) { uno::Reference< embed::XTransactedObject > xTransact( pImpl->mxImageStorage, uno::UNO_QUERY_THROW ); xTransact->commit(); } } catch (const uno::Exception&) { return false; } return true; } void EmbeddedObjectContainer::ReleaseImageSubStorage() { CommitImageSubStorage(); if ( pImpl->mxImageStorage.is() ) { try { pImpl->mxImageStorage->dispose(); pImpl->mxImageStorage.clear(); } catch (const uno::Exception&) { SAL_WARN( "comphelper.container", "Problems releasing image substorage!" ); } } } EmbeddedObjectContainer::~EmbeddedObjectContainer() { ReleaseImageSubStorage(); if ( pImpl->mbOwnsStorage ) pImpl->mxStorage->dispose(); delete pImpl->mpTempObjectContainer; } void EmbeddedObjectContainer::CloseEmbeddedObjects() { for( const auto& rObj : pImpl->maNameToObjectMap ) { uno::Reference < util::XCloseable > const & xClose = rObj.second; if( xClose.is() ) { try { xClose->close( true ); } catch (const uno::Exception&) { } } } } OUString EmbeddedObjectContainer::CreateUniqueObjectName() { OUString aStr; sal_Int32 i=1; do { aStr = "Object " + OUString::number( i++ ); } while( HasEmbeddedObject( aStr ) ); // TODO/LATER: should we consider deleted objects? return aStr; } uno::Sequence < OUString > EmbeddedObjectContainer::GetObjectNames() const { return comphelper::mapKeysToSequence(pImpl->maNameToObjectMap); } bool EmbeddedObjectContainer::HasEmbeddedObjects() const { return !pImpl->maNameToObjectMap.empty(); } bool EmbeddedObjectContainer::HasEmbeddedObject( const OUString& rName ) { if (pImpl->maNameToObjectMap.contains(rName)) return true; if (!pImpl->mxStorage.is()) return false; return pImpl->mxStorage->hasByName(rName); } bool EmbeddedObjectContainer::HasEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) const { return pImpl->maObjectToNameMap.contains(xObj); } bool EmbeddedObjectContainer::HasInstantiatedEmbeddedObject( const OUString& rName ) { // allows to detect whether the object was already instantiated // currently the filter instantiate it on loading, so this method allows // to avoid objects pointing to the same persistence return pImpl->maNameToObjectMap.contains(rName); } OUString EmbeddedObjectContainer::GetEmbeddedObjectName( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj ) const { auto it = pImpl->maObjectToNameMap.find(xObj); if (it == pImpl->maObjectToNameMap.end()) { SAL_WARN( "comphelper.container", "Unknown object!" ); return OUString(); } return it->second; } uno::Reference< embed::XEmbeddedObject> EmbeddedObjectContainer::GetEmbeddedObject( const OUString& rName, OUString const*const pBaseURL) { SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Empty object name!"); uno::Reference < embed::XEmbeddedObject > xObj; auto aIt = pImpl->maNameToObjectMap.find( rName ); #if OSL_DEBUG_LEVEL > 1 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY ); uno::Sequence< OUString> aSeq = xAccess->getElementNames(); const OUString* pIter = aSeq.getConstArray(); const OUString* pEnd = pIter + aSeq.getLength(); for(;pIter != pEnd;++pIter) { (void)*pIter; } OSL_ENSURE( aIt != pImpl->maNameToObjectMap.end() || xAccess->hasByName(rName), "Could not return object!" ); #endif // check if object was already created if ( aIt != pImpl->maNameToObjectMap.end() ) xObj = (*aIt).second; else xObj = Get_Impl(rName, uno::Reference(), pBaseURL); return xObj; } uno::Reference EmbeddedObjectContainer::Get_Impl( const OUString& rName, const uno::Reference& xCopy, OUString const*const pBaseURL) { uno::Reference < embed::XEmbeddedObject > xObj; try { // create the object from the storage uno::Reference < beans::XPropertySet > xSet( pImpl->mxStorage, uno::UNO_QUERY ); bool bReadOnlyMode = true; if ( xSet.is() ) { // get the open mode from the parent storage sal_Int32 nMode = 0; uno::Any aAny = xSet->getPropertyValue("OpenMode"); if ( aAny >>= nMode ) bReadOnlyMode = !(nMode & embed::ElementModes::WRITE ); } // object was not added until now - should happen only by calling this method from "inside" //TODO/LATER: it would be good to detect an error when an object should be created already, but isn't (not an "inside" call) uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); uno::Sequence< beans::PropertyValue > aObjDescr(1 + (xCopy.is() ? 1 : 0) + (pBaseURL ? 1 : 0)); auto itObjDescr = aObjDescr.getArray(); itObjDescr->Name = "Parent"; itObjDescr->Value <<= pImpl->m_xModel.get(); if (pBaseURL) { ++itObjDescr; itObjDescr->Name = "DefaultParentBaseURL"; itObjDescr->Value <<= *pBaseURL; } if ( xCopy.is() ) { ++itObjDescr; itObjDescr->Name = "CloneFrom"; itObjDescr->Value <<= xCopy; } uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue( "ReadOnly", bReadOnlyMode) }; xObj.set( xFactory->createInstanceInitFromEntry( pImpl->mxStorage, rName, aMediaDescr, aObjDescr ), uno::UNO_QUERY ); // insert object into my list AddEmbeddedObject( xObj, rName ); } catch (uno::Exception const& e) { SAL_WARN("comphelper.container", "EmbeddedObjectContainer::Get_Impl: exception caught: " << e); } return xObj; } uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, const uno::Sequence < beans::PropertyValue >& rArgs, OUString& rNewName, OUString const* pBaseURL ) { if ( rNewName.isEmpty() ) rNewName = CreateUniqueObjectName(); SAL_WARN_IF( HasEmbeddedObject(rNewName), "comphelper.container", "Object to create already exists!"); // create object from classid by inserting it into storage uno::Reference < embed::XEmbeddedObject > xObj; try { uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); const size_t nExtraArgs = pBaseURL ? 2 : 1; uno::Sequence< beans::PropertyValue > aObjDescr( rArgs.getLength() + nExtraArgs ); auto pObjDescr = aObjDescr.getArray(); pObjDescr[0].Name = "Parent"; pObjDescr[0].Value <<= pImpl->m_xModel.get(); if (pBaseURL) { pObjDescr[1].Name = "DefaultParentBaseURL"; pObjDescr[1].Value <<= *pBaseURL; } std::copy( rArgs.begin(), rArgs.end(), pObjDescr + nExtraArgs ); xObj.set( xFactory->createInstanceInitNew( rClassId, OUString(), pImpl->mxStorage, rNewName, aObjDescr ), uno::UNO_QUERY ); AddEmbeddedObject( xObj, rNewName ); OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED, "A freshly create object should be running always!" ); } catch (uno::Exception const& e) { SAL_WARN("comphelper.container", "EmbeddedObjectContainer::CreateEmbeddedObject: exception caught: " << e); } return xObj; } uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CreateEmbeddedObject( const uno::Sequence < sal_Int8 >& rClassId, OUString& rNewName, OUString const* pBaseURL ) { return CreateEmbeddedObject( rClassId, uno::Sequence < beans::PropertyValue >(), rNewName, pBaseURL ); } void EmbeddedObjectContainer::AddEmbeddedObject( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, const OUString& rName ) { #if OSL_DEBUG_LEVEL > 1 SAL_WARN_IF( rName.isEmpty(), "comphelper.container", "Added object doesn't have a name!"); uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY ); uno::Reference < embed::XEmbedPersist > xEmb( xObj, uno::UNO_QUERY ); uno::Reference < embed::XLinkageSupport > xLink( xEmb, uno::UNO_QUERY ); // if the object has a persistence and the object is not a link than it must have persistence entry in the storage OSL_ENSURE( !( xEmb.is() && ( !xLink.is() || !xLink->isLink() ) ) || xAccess->hasByName(rName), "Added element not in storage!" ); #endif // remember object - it needs to be in storage already auto aIt = pImpl->maNameToObjectMap.find( rName ); OSL_ENSURE( aIt == pImpl->maNameToObjectMap.end(), "Element already inserted!" ); pImpl->maNameToObjectMap[ rName ] = xObj; pImpl->maObjectToNameMap[ xObj ] = rName; uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY ); if ( xChild.is() && xChild->getParent() != pImpl->m_xModel.get() ) xChild->setParent( pImpl->m_xModel.get() ); // look for object in temporary container if ( !pImpl->mpTempObjectContainer ) return; auto& rObjectContainer = pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap; auto aIter = std::find_if(rObjectContainer.begin(), rObjectContainer.end(), [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; }); if (aIter == rObjectContainer.end()) return; // copy replacement image from temporary container (if there is any) OUString aTempName = aIter->first; OUString aMediaType; uno::Reference < io::XInputStream > xStream = pImpl->mpTempObjectContainer->GetGraphicStream( xObj, &aMediaType ); if ( xStream.is() ) { InsertGraphicStream( xStream, rName, aMediaType ); xStream = nullptr; pImpl->mpTempObjectContainer->RemoveGraphicStream( aTempName ); } // remove object from storage of temporary container uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); if ( xPersist.is() ) { try { pImpl->mpTempObjectContainer->pImpl->mxStorage->removeElement( aTempName ); } catch (const uno::Exception&) { } } // temp. container needs to forget the object pImpl->mpTempObjectContainer->pImpl->maObjectToNameMap.erase( aIter->second ); pImpl->mpTempObjectContainer->pImpl->maNameToObjectMap.erase( aIter ); } bool EmbeddedObjectContainer::StoreEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName, bool bCopy, const OUString& rSrcShellID, const OUString& rDestShellID ) { uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); if ( rName.isEmpty() ) rName = CreateUniqueObjectName(); #if OSL_DEBUG_LEVEL > 1 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY ); OSL_ENSURE( !xPersist.is() || !xAccess->hasByName(rName), "Inserting element already present in storage!" ); OSL_ENSURE( xPersist.is() || xObj->getCurrentState() == embed::EmbedStates::RUNNING, "Non persistent object inserted!"); #endif // insert objects' storage into the container storage (if object has one) try { if ( xPersist.is() ) { uno::Sequence < beans::PropertyValue > aSeq; if ( bCopy ) { auto aObjArgs(::comphelper::InitPropertySequence({ { "SourceShellID", uno::Any(rSrcShellID) }, { "DestinationShellID", uno::Any(rDestShellID) } })); xPersist->storeToEntry(pImpl->mxStorage, rName, aSeq, aObjArgs); } else { //TODO/LATER: possible optimization, don't store immediately //xPersist->setPersistentEntry( pImpl->mxStorage, rName, embed::EntryInitModes::ENTRY_NO_INIT, aSeq, aSeq ); xPersist->storeAsEntry( pImpl->mxStorage, rName, aSeq, aSeq ); xPersist->saveCompleted( true ); } } } catch (uno::Exception const& e) { SAL_WARN("comphelper.container", "EmbeddedObjectContainer::StoreEmbeddedObject: exception caught: " << e); // TODO/LATER: better error recovery should keep storage intact return false; } return true; } bool EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, OUString& rName ) { // store it into the container storage if (StoreEmbeddedObject(xObj, rName, false, OUString(), OUString())) { // remember object AddEmbeddedObject( xObj, rName ); return true; } else return false; } uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const uno::Reference < io::XInputStream >& xStm, OUString& rNewName ) { if ( rNewName.isEmpty() ) rNewName = CreateUniqueObjectName(); // store it into the container storage bool bIsStorage = false; try { // first try storage persistence uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm ); // storage was created from stream successfully bIsStorage = true; uno::Reference < embed::XStorage > xNewStore = pImpl->mxStorage->openStorageElement( rNewName, embed::ElementModes::READWRITE ); xStore->copyToStorage( xNewStore ); } catch (const uno::Exception&) { if ( bIsStorage ) // it is storage persistence, but opening of new substorage or copying to it failed return uno::Reference < embed::XEmbeddedObject >(); // stream didn't contain a storage, now try stream persistence try { uno::Reference < io::XStream > xNewStream = pImpl->mxStorage->openStreamElement( rNewName, embed::ElementModes::READWRITE ); ::comphelper::OStorageHelper::CopyInputToOutput( xStm, xNewStream->getOutputStream() ); // No mediatype is provided so the default for OLE objects value is used // it is correct so for now, but what if somebody introduces a new stream based embedded object? // Probably introducing of such an object must be restricted ( a storage must be used! ). uno::Reference< beans::XPropertySet > xProps( xNewStream, uno::UNO_QUERY_THROW ); xProps->setPropertyValue("MediaType", uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) ); } catch (uno::Exception const& e) { // complete disaster! SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedObject: exception caught: " << e); return uno::Reference < embed::XEmbeddedObject >(); } } // stream was copied into the container storage in either way, now try to open something form it uno::Reference < embed::XEmbeddedObject > xRet = GetEmbeddedObject( rNewName ); try { if ( !xRet.is() ) // no object could be created, so withdraw insertion pImpl->mxStorage->removeElement( rNewName ); } catch (const uno::Exception&) { } return xRet; } uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedObject( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName, OUString const* pBaseURL ) { if ( rNewName.isEmpty() ) rNewName = CreateUniqueObjectName(); uno::Reference < embed::XEmbeddedObject > xObj; try { uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); uno::Sequence< beans::PropertyValue > aObjDescr(pBaseURL ? 2 : 1); auto pObjDescr = aObjDescr.getArray(); pObjDescr[0].Name = "Parent"; pObjDescr[0].Value <<= pImpl->m_xModel.get(); if (pBaseURL) { pObjDescr[1].Name = "DefaultParentBaseURL"; pObjDescr[1].Value <<= *pBaseURL; } xObj.set( xFactory->createInstanceInitFromMediaDescriptor( pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY ); uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED, "A freshly create object should be running always!" ); // possible optimization: store later! if ( xPersist.is()) xPersist->storeOwn(); AddEmbeddedObject( xObj, rNewName ); } catch (const uno::Exception&) { } return xObj; } uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::InsertEmbeddedLink( const css::uno::Sequence < css::beans::PropertyValue >& aMedium, OUString& rNewName ) { if ( rNewName.isEmpty() ) rNewName = CreateUniqueObjectName(); uno::Reference < embed::XEmbeddedObject > xObj; try { uno::Reference < embed::XEmbeddedObjectCreator > xFactory = embed::EmbeddedObjectCreator::create(::comphelper::getProcessComponentContext()); uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue( "Parent", pImpl->m_xModel.get()) }; xObj.set( xFactory->createInstanceLink( pImpl->mxStorage, rNewName, aMedium, aObjDescr ), uno::UNO_QUERY ); uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); OSL_ENSURE( !xObj.is() || xObj->getCurrentState() != embed::EmbedStates::LOADED, "A freshly create object should be running always!" ); // possible optimization: store later! if ( xPersist.is()) xPersist->storeOwn(); AddEmbeddedObject( xObj, rNewName ); } catch (uno::Exception const& e) { SAL_WARN("comphelper.container", "EmbeddedObjectContainer::InsertEmbeddedLink: " "exception caught: " << e); } return xObj; } bool EmbeddedObjectContainer::TryToCopyGraphReplacement( EmbeddedObjectContainer& rSrc, const OUString& aOrigName, const OUString& aTargetName ) { bool bResult = false; if ( ( &rSrc != this || aOrigName != aTargetName ) && !aOrigName.isEmpty() && !aTargetName.isEmpty() ) { OUString aMediaType; uno::Reference < io::XInputStream > xGrStream = rSrc.GetGraphicStream( aOrigName, &aMediaType ); if ( xGrStream.is() ) bResult = InsertGraphicStream( xGrStream, aTargetName, aMediaType ); } return bResult; } uno::Reference < embed::XEmbeddedObject > EmbeddedObjectContainer::CopyAndGetEmbeddedObject( EmbeddedObjectContainer& rSrc, const uno::Reference & xObj, OUString& rName, const OUString& rSrcShellID, const OUString& rDestShellID ) { uno::Reference< embed::XEmbeddedObject > xResult; // TODO/LATER: For now only objects that implement XEmbedPersist have a replacement image, it might change in future // do an incompatible change so that object name is provided in all the move and copy methods OUString aOrigName; try { uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY_THROW ); aOrigName = xPersist->getEntryName(); } catch (const uno::Exception&) { } if ( rName.isEmpty() ) rName = CreateUniqueObjectName(); // objects without persistence are not really stored by the method if (xObj.is() && StoreEmbeddedObject(xObj, rName, true, rSrcShellID, rDestShellID)) { SAL_INFO_IF(rDestShellID.isEmpty(), "comphelper.container", "SfxObjectShell with no base URL?"); // every shell has a base URL, except the clipboard SwDocShell xResult = Get_Impl(rName, xObj, &rDestShellID); if ( !xResult.is() ) { // this is a case when object has no real persistence // in such cases a new object should be explicitly created and initialized with the data of the old one try { uno::Reference< embed::XLinkageSupport > xOrigLinkage( xObj, uno::UNO_QUERY ); if ( xOrigLinkage.is() && xOrigLinkage->isLink() ) { // this is an OOo link, it has no persistence OUString aURL = xOrigLinkage->getLinkURL(); if ( aURL.isEmpty() ) throw uno::RuntimeException(); // create new linked object from the URL the link is based on uno::Reference < embed::XEmbeddedObjectCreator > xCreator = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue( "URL", aURL) }; uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue( "Parent", pImpl->m_xModel.get()) }; xResult.set(xCreator->createInstanceLink( pImpl->mxStorage, rName, aMediaDescr, aObjDescr ), uno::UNO_QUERY_THROW ); } else { // the component is required for copying of this object if ( xObj->getCurrentState() == embed::EmbedStates::LOADED ) xObj->changeState( embed::EmbedStates::RUNNING ); // this must be an object based on properties, otherwise we can not copy it currently uno::Reference< beans::XPropertySet > xOrigProps( xObj->getComponent(), uno::UNO_QUERY_THROW ); // use object class ID to create a new one and transfer all the properties uno::Reference < embed::XEmbeddedObjectCreator > xCreator = embed::EmbeddedObjectCreator::create( ::comphelper::getProcessComponentContext() ); uno::Sequence< beans::PropertyValue > aObjDescr{ comphelper::makePropertyValue( "Parent", pImpl->m_xModel.get()) }; xResult.set(xCreator->createInstanceInitNew( xObj->getClassID(), xObj->getClassName(), pImpl->mxStorage, rName, aObjDescr ), uno::UNO_QUERY_THROW ); if ( xResult->getCurrentState() == embed::EmbedStates::LOADED ) xResult->changeState( embed::EmbedStates::RUNNING ); uno::Reference< beans::XPropertySet > xTargetProps( xResult->getComponent(), uno::UNO_QUERY_THROW ); // copy all the properties from xOrigProps to xTargetProps uno::Reference< beans::XPropertySetInfo > xOrigInfo = xOrigProps->getPropertySetInfo(); if ( !xOrigInfo.is() ) throw uno::RuntimeException(); const uno::Sequence< beans::Property > aPropertiesList = xOrigInfo->getProperties(); for ( const auto & p : aPropertiesList ) { try { xTargetProps->setPropertyValue( p.Name, xOrigProps->getPropertyValue( p.Name ) ); } catch (const beans::PropertyVetoException&) { // impossibility to copy readonly property is not treated as an error for now // but the assertion is helpful to detect such scenarios and review them SAL_WARN( "comphelper.container", "Could not copy readonly property!" ); } } } if ( xResult.is() ) AddEmbeddedObject( xResult, rName ); } catch (const uno::Exception&) { if ( xResult.is() ) { try { xResult->close( true ); } catch (const uno::Exception&) { } xResult.clear(); } } } } SAL_WARN_IF( !xResult.is(), "comphelper.container", "Can not copy embedded object that has no persistence!" ); if ( xResult.is() ) { // the object is successfully copied, try to copy graphical replacement if ( !aOrigName.isEmpty() ) TryToCopyGraphReplacement( rSrc, aOrigName, rName ); // the object might need the size to be set try { if ( xResult->getStatus( embed::Aspects::MSOLE_CONTENT ) & embed::EmbedMisc::EMBED_NEEDSSIZEONLOAD ) xResult->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, xObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) ); } catch (const uno::Exception&) { } } return xResult; } // #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+ void EmbeddedObjectContainer::RemoveEmbeddedObject( const OUString& rName, bool bKeepToTempStorage ) { uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( rName ); if ( xObj.is() ) RemoveEmbeddedObject( xObj, bKeepToTempStorage ); } bool EmbeddedObjectContainer::MoveEmbeddedObject( const OUString& rName, EmbeddedObjectContainer& rCnt ) { // find object entry auto aIt2 = rCnt.pImpl->maNameToObjectMap.find( rName ); OSL_ENSURE( aIt2 == rCnt.pImpl->maNameToObjectMap.end(), "Object does already exist in target container!" ); if ( aIt2 != rCnt.pImpl->maNameToObjectMap.end() ) return false; uno::Reference < embed::XEmbeddedObject > xObj; auto aIt = pImpl->maNameToObjectMap.find( rName ); if ( aIt != pImpl->maNameToObjectMap.end() ) { xObj = (*aIt).second; try { if ( xObj.is() ) { // move object OUString aName( rName ); rCnt.InsertEmbeddedObject( xObj, aName ); pImpl->maObjectToNameMap.erase( aIt->second ); pImpl->maNameToObjectMap.erase( aIt ); uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); if ( xPersist.is() ) pImpl->mxStorage->removeElement( rName ); } else { // copy storages; object *must* have persistence! uno::Reference < embed::XStorage > xOld = pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READ ); uno::Reference < embed::XStorage > xNew = rCnt.pImpl->mxStorage->openStorageElement( rName, embed::ElementModes::READWRITE ); xOld->copyToStorage( xNew ); } rCnt.TryToCopyGraphReplacement( *this, rName, rName ); // RemoveGraphicStream( rName ); return true; } catch (const uno::Exception&) { SAL_WARN( "comphelper.container", "Could not move object!"); return false; } } else SAL_WARN( "comphelper.container", "Unknown object!"); return false; } // #i119941, bKeepToTempStorage: use to specify whether store the removed object to temporary storage+ bool EmbeddedObjectContainer::RemoveEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj, bool bKeepToTempStorage ) { uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); OUString aName; if ( xPersist.is() ) aName = xPersist->getEntryName(); #if OSL_DEBUG_LEVEL > 1 uno::Reference < container::XNameAccess > xAccess( pImpl->mxStorage, uno::UNO_QUERY ); uno::Reference < embed::XLinkageSupport > xLink( xPersist, uno::UNO_QUERY ); sal_Bool bIsNotEmbedded = !xPersist.is() || ( xLink.is() && xLink->isLink() ); // if the object has a persistence and the object is not a link than it must have persistence entry in the storage OSL_ENSURE( bIsNotEmbedded || xAccess->hasByName(aName), "Removing element not present in storage!" ); #endif // somebody still needs the object, so we must assign a temporary persistence try { if ( xPersist.is() && bKeepToTempStorage ) // #i119941 { if ( !pImpl->mpTempObjectContainer ) { pImpl->mpTempObjectContainer = new EmbeddedObjectContainer(); try { // TODO/LATER: in future probably the temporary container will have two storages ( of two formats ) // the media type will be provided with object insertion OUString aOrigStorMediaType; uno::Reference< beans::XPropertySet > xStorProps( pImpl->mxStorage, uno::UNO_QUERY_THROW ); static constexpr OUString s_sMediaType(u"MediaType"_ustr); xStorProps->getPropertyValue( s_sMediaType ) >>= aOrigStorMediaType; SAL_WARN_IF( aOrigStorMediaType.isEmpty(), "comphelper.container", "No valuable media type in the storage!" ); uno::Reference< beans::XPropertySet > xTargetStorProps( pImpl->mpTempObjectContainer->pImpl->mxStorage, uno::UNO_QUERY_THROW ); xTargetStorProps->setPropertyValue( s_sMediaType,uno::Any( aOrigStorMediaType ) ); } catch (const uno::Exception&) { SAL_WARN( "comphelper.container", "Can not set the new media type to a storage!" ); } } OUString aTempName, aMediaType; /* Do not create a new name for a removed object, in the pImpl->mpTempObjectContainer, because the original m_aEntryName of xObj will be overwritten by InsertEmbeddedObject(), so uno::Reference < embed::XEmbeddedObject >& xObj will misbehave in EmbeddedObjectContainer::StoreAsChildren and SfxObjectShell::SaveCompletedChildren and will throw an exception because of objects with the same names! */ if( !pImpl->mpTempObjectContainer->HasEmbeddedObject(aName) ) aTempName = aName; pImpl->mpTempObjectContainer->InsertEmbeddedObject( xObj, aTempName ); uno::Reference < io::XInputStream > xStream = GetGraphicStream( xObj, &aMediaType ); if ( xStream.is() ) pImpl->mpTempObjectContainer->InsertGraphicStream( xStream, aTempName, aMediaType ); // object is stored, so at least it can be set to loaded state xObj->changeState( embed::EmbedStates::LOADED ); } else // objects without persistence need to stay in running state if they shall not be closed xObj->changeState( embed::EmbedStates::RUNNING ); } catch (const uno::Exception&) { return false; } auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(), [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; }); if (aIter != pImpl->maNameToObjectMap.end()) { pImpl->maObjectToNameMap.erase( aIter->second ); pImpl->maNameToObjectMap.erase( aIter ); uno::Reference < container::XChild > xChild( xObj, uno::UNO_QUERY ); if ( xChild.is() ) xChild->setParent( uno::Reference < uno::XInterface >() ); } else SAL_WARN( "comphelper.container", "Object not found for removal!" ); if ( !xPersist || !bKeepToTempStorage ) // #i119941# return true; // remove replacement image (if there is one) RemoveGraphicStream( aName ); // now it's time to remove the storage from the container storage try { #if OSL_DEBUG_LEVEL > 1 // if the object has a persistence and the object is not a link than it must have persistence entry in storage OSL_ENSURE( bIsNotEmbedded || pImpl->mxStorage->hasByName( aName ), "The object has no persistence entry in the storage!" ); #endif if ( xPersist.is() && pImpl->mxStorage->hasByName( aName ) ) pImpl->mxStorage->removeElement( aName ); } catch (const uno::Exception&) { SAL_WARN( "comphelper.container", "Failed to remove object from storage!" ); return false; } return true; } void EmbeddedObjectContainer::CloseEmbeddedObject( const uno::Reference < embed::XEmbeddedObject >& xObj ) { // disconnect the object from the container and close it if possible auto aIter = std::find_if(pImpl->maNameToObjectMap.begin(), pImpl->maNameToObjectMap.end(), [&xObj](const EmbeddedObjectContainerNameMap::value_type& rEntry) { return rEntry.second == xObj; }); if (aIter == pImpl->maNameToObjectMap.end()) return; pImpl->maObjectToNameMap.erase( aIter->second ); pImpl->maNameToObjectMap.erase( aIter ); try { xObj->close( true ); } catch (const uno::Exception&) { // it is no problem if the object is already closed // TODO/LATER: what if the object can not be closed? } } uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const OUString& aName, OUString* pMediaType ) { uno::Reference < io::XInputStream > xStream; SAL_WARN_IF( aName.isEmpty(), "comphelper.container", "Retrieving graphic for unknown object!" ); if ( !aName.isEmpty() ) { try { uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements(); uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( aName, embed::ElementModes::READ ); xStream = xGraphicStream->getInputStream(); if ( pMediaType ) { uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY ); if ( xSet.is() ) { uno::Any aAny = xSet->getPropertyValue("MediaType"); aAny >>= *pMediaType; } } } catch (uno::Exception const& e) { SAL_INFO("comphelper.container", "EmbeddedObjectContainer::GetGraphicStream(): " << e); } } return xStream; } uno::Reference < io::XInputStream > EmbeddedObjectContainer::GetGraphicStream( const css::uno::Reference < css::embed::XEmbeddedObject >& xObj, OUString* pMediaType ) { // try to load it from the container storage return GetGraphicStream( GetEmbeddedObjectName( xObj ), pMediaType ); } bool EmbeddedObjectContainer::InsertGraphicStream( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType ) { try { uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements(); // store it into the subfolder uno::Reference < io::XOutputStream > xOutStream; uno::Reference < io::XStream > xGraphicStream = xReplacements->openStreamElement( rObjectName, embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); xOutStream = xGraphicStream->getOutputStream(); ::comphelper::OStorageHelper::CopyInputToOutput( rStream, xOutStream ); xOutStream->flush(); uno::Reference< beans::XPropertySet > xPropSet( xGraphicStream, uno::UNO_QUERY_THROW ); xPropSet->setPropertyValue("UseCommonStoragePasswordEncryption", uno::Any( true ) ); xPropSet->setPropertyValue("MediaType", uno::Any(rMediaType) ); xPropSet->setPropertyValue("Compressed", uno::Any( true ) ); } catch (const uno::Exception&) { return false; } return true; } bool EmbeddedObjectContainer::InsertGraphicStreamDirectly( const css::uno::Reference < css::io::XInputStream >& rStream, const OUString& rObjectName, const OUString& rMediaType ) { try { uno::Reference < embed::XStorage > xReplacement = pImpl->GetReplacements(); uno::Reference < embed::XOptimizedStorage > xOptRepl( xReplacement, uno::UNO_QUERY_THROW ); // store it into the subfolder uno::Sequence< beans::PropertyValue > aProps{ comphelper::makePropertyValue("MediaType", rMediaType), comphelper::makePropertyValue("UseCommonStoragePasswordEncryption", true), comphelper::makePropertyValue("Compressed", true) }; if ( xReplacement->hasByName( rObjectName ) ) xReplacement->removeElement( rObjectName ); xOptRepl->insertStreamElementDirect( rObjectName, rStream, aProps ); } catch (const uno::Exception&) { return false; } return true; } void EmbeddedObjectContainer::RemoveGraphicStream( const OUString& rObjectName ) { try { uno::Reference < embed::XStorage > xReplacements = pImpl->GetReplacements(); xReplacements->removeElement( rObjectName ); } catch (const uno::Exception&) { } } namespace { void InsertStreamIntoPicturesStorage_Impl( const uno::Reference< embed::XStorage >& xDocStor, const uno::Reference< io::XInputStream >& xInStream, const OUString& aStreamName ) { OSL_ENSURE( !aStreamName.isEmpty() && xInStream.is() && xDocStor.is(), "Misuse of the method!" ); try { uno::Reference< embed::XStorage > xPictures = xDocStor->openStorageElement( "Pictures", embed::ElementModes::READWRITE ); uno::Reference< io::XStream > xObjReplStr = xPictures->openStreamElement( aStreamName, embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); uno::Reference< io::XOutputStream > xOutStream( xObjReplStr->getInputStream(), uno::UNO_QUERY_THROW ); ::comphelper::OStorageHelper::CopyInputToOutput( xInStream, xOutStream ); xOutStream->closeOutput(); uno::Reference< embed::XTransactedObject > xTransact( xPictures, uno::UNO_QUERY ); if ( xTransact.is() ) xTransact->commit(); } catch (const uno::Exception&) { SAL_WARN( "comphelper.container", "The images storage is not available!" ); } } } bool EmbeddedObjectContainer::StoreAsChildren(bool _bOasisFormat,bool _bCreateEmbedded, bool _bAutoSaveEvent, const uno::Reference < embed::XStorage >& _xStorage) { bool bResult = false; try { comphelper::EmbeddedObjectContainer aCnt( _xStorage ); const uno::Sequence < OUString > aNames = GetObjectNames(); const OUString* pIter = aNames.getConstArray(); const OUString* pEnd = pIter + aNames.getLength(); for(;pIter != pEnd;++pIter) { uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter ); SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" ); if ( xObj.is() ) { bool bSwitchBackToLoaded = false; uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY ); uno::Reference < io::XInputStream > xStream; OUString aMediaType; sal_Int32 nCurState = xObj->getCurrentState(); if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING ) { // means that the object is not active // copy replacement image from old to new container xStream = GetGraphicStream( xObj, &aMediaType ); } if ( !xStream.is() && getUserAllowsLinkUpdate() ) { // the image must be regenerated // TODO/LATER: another aspect could be used if ( xObj->getCurrentState() == embed::EmbedStates::LOADED ) bSwitchBackToLoaded = true; xStream = GetGraphicReplacementStream( embed::Aspects::MSOLE_CONTENT, xObj, &aMediaType ); } if ( _bOasisFormat || (xLink.is() && xLink->isLink()) ) { if ( xStream.is() ) { if ( _bOasisFormat ) { // if it is an embedded object or the optimized inserting fails the normal inserting should be done if ( _bCreateEmbedded || !aCnt.InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) ) aCnt.InsertGraphicStream( xStream, *pIter, aMediaType ); } else { // it is a linked object exported into SO7 format InsertStreamIntoPicturesStorage_Impl( _xStorage, xStream, *pIter ); } } } uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); if ( xPersist.is() ) { uno::Sequence< beans::PropertyValue > aArgs( _bOasisFormat ? 3 : 4 ); auto pArgs = aArgs.getArray(); pArgs[0].Name = "StoreVisualReplacement"; pArgs[0].Value <<= !_bOasisFormat; // if it is an embedded object or the optimized inserting fails the normal inserting should be done pArgs[1].Name = "CanTryOptimization"; pArgs[1].Value <<= !_bCreateEmbedded; pArgs[2].Name = "AutoSaveEvent"; pArgs[2].Value <<= _bAutoSaveEvent; if ( !_bOasisFormat ) { // if object has no cached replacement it will use this one pArgs[3].Name = "VisualReplacement"; pArgs[3].Value <<= xStream; } try { xPersist->storeAsEntry( _xStorage, xPersist->getEntryName(), uno::Sequence< beans::PropertyValue >(), aArgs ); } catch (const embed::WrongStateException&) { SAL_WARN("comphelper.container", "failed to store '" << *pIter << "'"); } } if ( bSwitchBackToLoaded ) // switch back to loaded state; that way we have a minimum cache confusion xObj->changeState( embed::EmbedStates::LOADED ); } } bResult = aCnt.CommitImageSubStorage(); } catch (const uno::Exception& e) { // TODO/LATER: error handling bResult = false; SAL_WARN("comphelper.container", "failed. Message: " << e); } // the old SO6 format does not store graphical replacements if ( !_bOasisFormat && bResult ) { try { // the substorage still can not be locked by the embedded object container OUString aObjReplElement( "ObjectReplacements" ); if ( _xStorage->hasByName( aObjReplElement ) && _xStorage->isStorageElement( aObjReplElement ) ) _xStorage->removeElement( aObjReplElement ); } catch (const uno::Exception&) { // TODO/LATER: error handling; bResult = false; } } return bResult; } bool EmbeddedObjectContainer::StoreChildren(bool _bOasisFormat,bool _bObjectsOnly) { bool bResult = true; const uno::Sequence < OUString > aNames = GetObjectNames(); const OUString* pIter = aNames.getConstArray(); const OUString* pEnd = pIter + aNames.getLength(); for(;pIter != pEnd;++pIter) { try { uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter ); SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" ); if ( xObj.is() ) { sal_Int32 nCurState = xObj->getCurrentState(); if ( _bOasisFormat && nCurState != embed::EmbedStates::LOADED && nCurState != embed::EmbedStates::RUNNING ) { // means that the object is active // the image must be regenerated OUString aMediaType; // TODO/LATER: another aspect could be used uno::Reference < io::XInputStream > xStream = GetGraphicReplacementStream( embed::Aspects::MSOLE_CONTENT, xObj, &aMediaType ); if ( xStream.is() ) { if ( !InsertGraphicStreamDirectly( xStream, *pIter, aMediaType ) ) InsertGraphicStream( xStream, *pIter, aMediaType ); } } // TODO/LATER: currently the object by default does not cache replacement image // that means that if somebody loads SO7 document and store its objects using // this method the images might be lost. // Currently this method is only used on storing to alien formats, that means // that SO7 documents storing does not use it, and all other filters are // based on OASIS format. But if it changes the method must be fixed. The fix // must be done only on demand since it can affect performance. uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); if ( xPersist.is() ) { try { //TODO/LATER: only storing if changed! //xPersist->storeOwn(); //commented, i120168 // begin:all charts will be persisted as xml format on disk when saving, which is time consuming. // '_bObjectsOnly' mean we are storing to alien formats. // 'isStorageElement' mean current object is NOT a MS OLE format. (may also include in future), i120168 if (_bObjectsOnly && (nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING) && (pImpl->mxStorage->isStorageElement( *pIter ) )) { uno::Reference< util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY ); if ( xModifiable.is() && xModifiable->isModified()) { xPersist->storeOwn(); } else { //do nothing. Embedded model is not modified, no need to persist. } } else //the embedded object is in active status, always store back it. { xPersist->storeOwn(); } //end i120168 } catch (const uno::Exception&) { // TODO/LATER: error handling bResult = false; break; } } if ( !_bOasisFormat && !_bObjectsOnly ) { // copy replacement images for linked objects try { uno::Reference< embed::XLinkageSupport > xLink( xObj, uno::UNO_QUERY ); if ( xLink.is() && xLink->isLink() ) { OUString aMediaType; uno::Reference < io::XInputStream > xInStream = GetGraphicStream( xObj, &aMediaType ); if ( xInStream.is() ) InsertStreamIntoPicturesStorage_Impl( pImpl->mxStorage, xInStream, *pIter ); } } catch (const uno::Exception&) { } } } } catch (const uno::Exception&) { // TODO/LATER: error handling } } if ( bResult && _bOasisFormat ) bResult = CommitImageSubStorage(); if ( bResult && !_bObjectsOnly ) { try { ReleaseImageSubStorage(); OUString aObjReplElement( "ObjectReplacements" ); if ( !_bOasisFormat && pImpl->mxStorage->hasByName( aObjReplElement ) && pImpl->mxStorage->isStorageElement( aObjReplElement ) ) pImpl->mxStorage->removeElement( aObjReplElement ); } catch (const uno::Exception&) { // TODO/LATER: error handling bResult = false; } } return bResult; } uno::Reference< io::XInputStream > EmbeddedObjectContainer::GetGraphicReplacementStream( sal_Int64 nViewAspect, const uno::Reference< embed::XEmbeddedObject >& xObj, OUString* pMediaType ) { uno::Reference< io::XInputStream > xInStream; if ( xObj.is() ) { try { // retrieving of the visual representation can switch object to running state embed::VisualRepresentation aRep = xObj->getPreferredVisualRepresentation( nViewAspect ); if ( pMediaType ) *pMediaType = aRep.Flavor.MimeType; uno::Sequence < sal_Int8 > aSeq; aRep.Data >>= aSeq; xInStream = new ::comphelper::SequenceInputStream( aSeq ); } catch (const uno::Exception&) { } } return xInStream; } bool EmbeddedObjectContainer::SetPersistentEntries(const uno::Reference< embed::XStorage >& _xStorage,bool _bClearModifiedFlag) { bool bError = false; const uno::Sequence < OUString > aNames = GetObjectNames(); const OUString* pIter = aNames.getConstArray(); const OUString* pEnd = pIter + aNames.getLength(); for(;pIter != pEnd;++pIter) { uno::Reference < embed::XEmbeddedObject > xObj = GetEmbeddedObject( *pIter ); SAL_WARN_IF( !xObj.is(), "comphelper.container", "An empty entry in the embedded objects list!" ); if ( xObj.is() ) { uno::Reference< embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); if ( xPersist.is() ) { try { xPersist->setPersistentEntry( _xStorage, *pIter, embed::EntryInitModes::NO_INIT, uno::Sequence< beans::PropertyValue >(), uno::Sequence< beans::PropertyValue >() ); } catch (const uno::Exception&) { // TODO/LATER: error handling bError = true; break; } } if ( _bClearModifiedFlag ) { // if this method is used as part of SaveCompleted the object must stay unmodified after execution try { uno::Reference< util::XModifiable > xModif( xObj->getComponent(), uno::UNO_QUERY_THROW ); if ( xModif->isModified() ) xModif->setModified( false ); } catch (const uno::Exception&) { } } } } return bError; } bool EmbeddedObjectContainer::getUserAllowsLinkUpdate() const { if (officecfg::Office::Common::Security::Scripting::DisableActiveContent::get()) return false; return pImpl->mbUserAllowsLinkUpdate; } void EmbeddedObjectContainer::setUserAllowsLinkUpdate(bool bNew) { if(pImpl->mbUserAllowsLinkUpdate != bNew) { pImpl->mbUserAllowsLinkUpdate = bNew; } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */