diff options
Diffstat (limited to 'sfx2/source/appl/linkmgr2.cxx')
-rw-r--r-- | sfx2/source/appl/linkmgr2.cxx | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/sfx2/source/appl/linkmgr2.cxx b/sfx2/source/appl/linkmgr2.cxx new file mode 100644 index 000000000..8245f6da6 --- /dev/null +++ b/sfx2/source/appl/linkmgr2.cxx @@ -0,0 +1,710 @@ +/* -*- 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 <comphelper/string.hxx> +#include <sfx2/linkmgr.hxx> +#include <sfx2/sfxsids.hrc> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <osl/file.hxx> +#include <sfx2/objsh.hxx> +#include <svl/urihelper.hxx> +#include <sot/formats.hxx> +#include <tools/urlobj.hxx> +#include <sot/exchange.hxx> +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/gdimtf.hxx> +#include <sfx2/lnkbase.hxx> +#include <sfx2/app.hxx> +#include <vcl/graph.hxx> +#include <svl/stritem.hxx> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <vcl/dibtools.hxx> +#include <unotools/charclass.hxx> +#include <unotools/securityoptions.hxx> +#include <vcl/GraphicLoader.hxx> + +#include "fileobj.hxx" +#include "impldde.hxx" +#include <sfx2/strings.hrc> +#include <sfx2/sfxresid.hxx> + +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::lang::XComponent; +using ::com::sun::star::util::XCloseable; + +namespace sfx2 +{ + +namespace { + +class SvxInternalLink : public sfx2::SvLinkSource +{ +public: + SvxInternalLink() {} + + virtual bool Connect( sfx2::SvBaseLink* ) override; +}; + +} + +LinkManager::LinkManager(SfxObjectShell* p) + : pPersist( p ) +{ +} + +LinkManager::~LinkManager() +{ + for(tools::SvRef<SvBaseLink> & rTmp : aLinkTbl) + { + if( rTmp.is() ) + { + rTmp->Disconnect(); + rTmp->SetLinkManager( nullptr ); + } + } +} + +void LinkManager::InsertCachedComp(const Reference<XComponent>& xComp) +{ + maCachedComps.push_back(xComp); +} + +void LinkManager::CloseCachedComps() +{ + for (const auto& rxCachedComp : maCachedComps) + { + Reference<XCloseable> xCloseable(rxCachedComp, UNO_QUERY); + if (!xCloseable.is()) + continue; + + xCloseable->close(true); + } + maCachedComps.clear(); +} + +void LinkManager::Remove( SvBaseLink const *pLink ) +{ + // No duplicate links inserted + bool bFound = false; + for( size_t n = 0; n < aLinkTbl.size(); ) + { + tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ]; + if( pLink == rTmp.get() ) + { + rTmp->Disconnect(); + rTmp->SetLinkManager( nullptr ); + rTmp.clear(); + bFound = true; + } + + // Remove empty ones if they exist + if( !rTmp.is() ) + { + aLinkTbl.erase( aLinkTbl.begin() + n ); + if( bFound ) + return ; + } + else + ++n; + } +} + +void LinkManager::Remove( size_t nPos, size_t nCnt ) +{ + if( !nCnt || nPos >= aLinkTbl.size() ) + return; + + if (sal::static_int_cast<size_t>(nPos + nCnt) > aLinkTbl.size()) + nCnt = aLinkTbl.size() - nPos; + + for( size_t n = nPos; n < nPos + nCnt; ++n) + { + tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ]; + if( rTmp.is() ) + { + rTmp->Disconnect(); + rTmp->SetLinkManager( nullptr ); + } + } + aLinkTbl.erase( aLinkTbl.begin() + nPos, aLinkTbl.begin() + nPos + nCnt ); +} + +bool LinkManager::Insert( SvBaseLink* pLink ) +{ + for( size_t n = 0; n < aLinkTbl.size(); ++n ) + { + tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ]; + if( !rTmp.is() ) + { + aLinkTbl.erase( aLinkTbl.begin() + n-- ); + } + else if( pLink == rTmp.get() ) + return false; // No duplicate links inserted + } + + pLink->SetLinkManager( this ); + aLinkTbl.emplace_back(pLink ); + return true; +} + +bool LinkManager::InsertLink( SvBaseLink * pLink, + SvBaseLinkObjectType nObjType, + SfxLinkUpdateMode nUpdateMode, + const OUString* pName ) +{ + // This First + pLink->SetObjType( nObjType ); + if( pName ) + pLink->SetName( *pName ); + pLink->SetUpdateMode( nUpdateMode ); + return Insert( pLink ); +} + +void LinkManager::InsertDDELink( SvBaseLink * pLink, + const OUString& rServer, + const OUString& rTopic, + const OUString& rItem ) +{ + if( !isClientType( pLink->GetObjType() ) ) + return; + + OUString sCmd; + ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem ); + + pLink->SetObjType( SvBaseLinkObjectType::ClientDde ); + pLink->SetName( sCmd ); + Insert( pLink ); +} + +void LinkManager::InsertDDELink( SvBaseLink * pLink ) +{ + DBG_ASSERT( isClientType(pLink->GetObjType()), "no OBJECT_CLIENT_SO" ); + if( !isClientType( pLink->GetObjType() ) ) + return; + + if( pLink->GetObjType() == SvBaseLinkObjectType::ClientSo ) + pLink->SetObjType( SvBaseLinkObjectType::ClientDde ); + + Insert( pLink ); +} + +// Obtain the string for the dialog +bool LinkManager::GetDisplayNames( const SvBaseLink * pLink, + OUString* pType, + OUString* pFile, + OUString* pLinkStr, + OUString* pFilter ) +{ + bool bRet = false; + const OUString& sLNm( pLink->GetLinkSourceName() ); + if( !sLNm.isEmpty() ) + { + switch( pLink->GetObjType() ) + { + case SvBaseLinkObjectType::ClientFile: + case SvBaseLinkObjectType::ClientGraphic: + case SvBaseLinkObjectType::ClientOle: + { + sal_Int32 nPos = 0; + OUString sFile( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) ); + OUString sRange( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) ); + + if( pFile ) + *pFile = sFile; + if( pLinkStr ) + *pLinkStr = sRange; + if( pFilter ) + *pFilter = nPos == -1 ? OUString() : sLNm.copy(nPos); + + if( pType ) + { + SvBaseLinkObjectType nObjType = pLink->GetObjType(); + *pType = SfxResId( + ( SvBaseLinkObjectType::ClientFile == nObjType || SvBaseLinkObjectType::ClientOle == nObjType ) + ? RID_SVXSTR_FILELINK + : RID_SVXSTR_GRAFIKLINK); + } + bRet = true; + } + break; + case SvBaseLinkObjectType::ClientDde: + { + sal_Int32 nTmp = 0; + OUString sServer( sLNm.getToken( 0, cTokenSeparator, nTmp ) ); + OUString sTopic( sLNm.getToken( 0, cTokenSeparator, nTmp ) ); + + if( pType ) + *pType = sServer; + if( pFile ) + *pFile = sTopic; + if( pLinkStr ) + *pLinkStr = nTmp != -1 ? sLNm.copy(nTmp) : OUString(); + bRet = true; + } + break; + default: + break; + } + } + + return bRet; +} + +void LinkManager::UpdateAllLinks( + bool bAskUpdate, + bool bUpdateGrfLinks, + weld::Window* pParentWin ) +{ + // First make a copy of the array in order to update links + // links in ... no contact between them! + std::vector<SvBaseLink*> aTmpArr; + for( size_t n = 0; n < aLinkTbl.size(); ++n ) + { + tools::SvRef<SvBaseLink>& rLink = aLinkTbl[ n ]; + if( !rLink.is() ) + { + Remove( n-- ); + continue; + } + aTmpArr.push_back( rLink.get() ); + } + + for(SvBaseLink* pLink : aTmpArr) + { + // search first in the array after the entry + bool bFound = false; + for(const tools::SvRef<SvBaseLink> & i : aLinkTbl) + if( pLink == i.get() ) + { + bFound = true; + break; + } + + if( !bFound ) + continue; // was not available! + + // Graphic-Links not to update yet + if( !pLink->IsVisible() || + ( !bUpdateGrfLinks && SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() )) + continue; + + if( bAskUpdate ) + { + OUString aMsg = SfxResId(STR_QUERY_UPDATE_LINKS); + INetURLObject aURL(pPersist->getDocumentBaseURL()); + aMsg = aMsg.replaceFirst("%{filename}", aURL.GetLastName()); + + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParentWin, + VclMessageType::Question, VclButtonsType::YesNo, aMsg)); + xQueryBox->set_default_response(RET_YES); + + int nRet = xQueryBox->run(); + if( RET_YES != nRet ) + { + SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist(); + + if(pShell) + { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pShell->getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false); + } + + return ; // nothing should be updated + } + bAskUpdate = false; // once is enough + } + + pLink->Update(); + } + CloseCachedComps(); +} + +SvLinkSourceRef LinkManager::CreateObj( SvBaseLink const * pLink ) +{ + switch( pLink->GetObjType() ) + { + case SvBaseLinkObjectType::ClientFile: + case SvBaseLinkObjectType::ClientGraphic: + case SvBaseLinkObjectType::ClientOle: + return new SvFileObject; + case SvBaseLinkObjectType::Internal: + return new SvxInternalLink; + case SvBaseLinkObjectType::ClientDde: + return new SvDDEObject; + default: + return SvLinkSourceRef(); + } +} + +bool LinkManager::InsertServer( SvLinkSource* pObj ) +{ + // no duplicate inserts + if( !pObj ) + return false; + + return aServerTbl.insert( pObj ).second; +} + +void LinkManager::RemoveServer( SvLinkSource* pObj ) +{ + aServerTbl.erase( pObj ); +} + +void MakeLnkName( OUString& rName, const OUString* pType, const OUString& rFile, + const OUString& rLink, const OUString* pFilter ) +{ + if( pType ) + { + rName = comphelper::string::strip(*pType, ' ') + + OUStringChar(cTokenSeparator); + } + else + rName.clear(); + + rName += rFile; + + rName = comphelper::string::strip(rName, ' ') + + OUStringChar(cTokenSeparator); + rName = comphelper::string::strip(rName, ' ') + rLink; + if( pFilter ) + { + rName += OUStringChar(cTokenSeparator) + *pFilter; + rName = comphelper::string::strip(rName, ' '); + } +} + +void LinkManager::ReconnectDdeLink(SfxObjectShell& rServer) +{ + SfxMedium* pMed = rServer.GetMedium(); + if (!pMed) + return; + + const ::sfx2::SvBaseLinks& rLinks = GetLinks(); + size_t n = rLinks.size(); + + for (size_t i = 0; i < n; ++i) + { + ::sfx2::SvBaseLink* p = rLinks[i].get(); + OUString aType, aFile, aLink, aFilter; + if (!GetDisplayNames(p, &aType, &aFile, &aLink, &aFilter)) + continue; + + if (aType != "soffice") + // DDE connections between OOo apps are always named 'soffice'. + continue; + + OUString aTmp; + OUString aURL = aFile; + if (osl::FileBase::getFileURLFromSystemPath(aFile, aTmp) + == osl::FileBase::E_None) + aURL = aTmp; + + if (!aURL.equalsIgnoreAsciiCase(pMed->GetName())) + // This DDE link is not associated with this server shell... Skip it. + continue; + + if (aLink.isEmpty()) + continue; + + LinkServerShell(aLink, rServer, *p); + } +} + +void LinkManager::LinkServerShell(const OUString& rPath, SfxObjectShell& rServer, ::sfx2::SvBaseLink& rLink) +{ + ::sfx2::SvLinkSource* pSrvSrc = rServer.DdeCreateLinkSource(rPath); + if (pSrvSrc) + { + css::datatransfer::DataFlavor aFl; + SotExchange::GetFormatDataFlavor(rLink.GetContentType(), aFl); + rLink.SetObj(pSrvSrc); + pSrvSrc->AddDataAdvise( + &rLink, aFl.MimeType, + SfxLinkUpdateMode::ONCALL == rLink.GetUpdateMode() ? ADVISEMODE_ONLYONCE : 0); + } +} + +void LinkManager::InsertFileLink( + sfx2::SvBaseLink& rLink, SvBaseLinkObjectType nFileType, const OUString& rFileNm, + const OUString* pFilterNm, const OUString* pRange) +{ + if (!isClientType(rLink.GetObjType())) + return; + + OUStringBuffer aBuf(64); + aBuf.append(rFileNm); + aBuf.append(sfx2::cTokenSeparator); + + if (pRange) + aBuf.append(*pRange); + + if (pFilterNm) + { + aBuf.append(sfx2::cTokenSeparator); + aBuf.append(*pFilterNm); + } + + OUString aCmd = aBuf.makeStringAndClear(); + InsertLink(&rLink, nFileType, SfxLinkUpdateMode::ONCALL, &aCmd); +} + +// A transfer is aborted, so cancel all download media +// (for now this is only of interest for the file links!) +void LinkManager::CancelTransfers() +{ + + const sfx2::SvBaseLinks& rLnks = GetLinks(); + for( size_t n = rLnks.size(); n; ) + { + const sfx2::SvBaseLink& rLnk = *rLnks[--n]; + if (isClientFileType(rLnk.GetObjType())) + { + if (SvFileObject* pFileObj = static_cast<SvFileObject*>(rLnk.GetObj())) + pFileObj->CancelTransfers(); + } + } +} + +// For the purpose of sending Status information from the file object to +// the base link, there exist a dedicated ClipBoardId. The SvData-object +// gets the appropriate information as a string +// For now this is required for file object in conjunction with JavaScript +// - needs information about Load/Abort/Error +SotClipboardFormatId LinkManager::RegisterStatusInfoId() +{ + static SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; + + if( nFormat == SotClipboardFormatId::NONE ) + { + nFormat = SotExchange::RegisterFormatName( + "StatusInfo from SvxInternalLink"); + } + return nFormat; +} + +bool LinkManager::GetGraphicFromAny(const OUString& rMimeType, + const css::uno::Any & rValue, + Graphic& rGraphic, + weld::Window* pParentWin) +{ + bool bRet = false; + + if (!rValue.hasValue()) + return bRet; + + if (rValue.has<OUString>()) + { + OUString sReferer; + SfxObjectShell* sh = GetPersist(); + if (sh && sh->HasName()) + sReferer = sh->GetMedium()->GetName(); + + OUString sURL = rValue.get<OUString>(); + if (!SvtSecurityOptions().isUntrustedReferer(sReferer)) + rGraphic = vcl::graphic::loadFromURL(sURL, pParentWin); + if (rGraphic.IsNone()) + rGraphic.SetDefaultType(); + rGraphic.setOriginURL(sURL); + return true; + } + else if (rValue.has<css::uno::Sequence<sal_Int8>>()) + { + auto aSeq = rValue.get<css::uno::Sequence<sal_Int8>>(); + + SvMemoryStream aMemStm( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(), + StreamMode::READ ); + aMemStm.Seek( 0 ); + + switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) ) + { + case SotClipboardFormatId::SVXB: + { + ReadGraphic( aMemStm, rGraphic ); + bRet = true; + } + break; + case SotClipboardFormatId::GDIMETAFILE: + { + GDIMetaFile aMtf; + aMtf.Read( aMemStm ); + rGraphic = aMtf; + bRet = true; + } + break; + case SotClipboardFormatId::BITMAP: + { + Bitmap aBmp; + ReadDIB(aBmp, aMemStm, true); + rGraphic = aBmp; + bRet = true; + } + break; + default: break; + } + } + return bRet; +} + +static OUString lcl_DDE_RelToAbs( const OUString& rTopic, const OUString& rBaseURL ) +{ + OUString sRet; + INetURLObject aURL( rTopic ); + if( INetProtocol::NotValid == aURL.GetProtocol() ) + osl::FileBase::getFileURLFromSystemPath(rTopic, sRet); + if( sRet.isEmpty() ) + sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl() ); + return sRet; +} + +bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink ) +{ + SfxObjectShell* pFndShell = nullptr; + sal_uInt16 nUpdateMode = css::document::UpdateDocMode::NO_UPDATE; + OUString sTopic, sItem, sReferer; + LinkManager* pLinkMgr = pLink->GetLinkManager(); + if (pLinkMgr && sfx2::LinkManager::GetDisplayNames(pLink, nullptr, &sTopic, &sItem) && !sTopic.isEmpty()) + { + // first only loop over the DocumentShells the shells and find those + // with the name: + CharClass aCC( LanguageTag( LANGUAGE_SYSTEM) ); + + bool bFirst = true; + SfxObjectShell* pShell = pLinkMgr->GetPersist(); + if( pShell && pShell->GetMedium() ) + { + sReferer = pShell->GetMedium()->GetBaseURL(); + const SfxUInt16Item* pItem = SfxItemSet::GetItem<SfxUInt16Item>(pShell->GetMedium()->GetItemSet(), SID_UPDATEDOCMODE, false); + if ( pItem ) + nUpdateMode = pItem->GetValue(); + } + + OUString sNmURL(aCC.lowercase(lcl_DDE_RelToAbs(sTopic, sReferer))); + + if ( !pShell ) + { + bFirst = false; + pShell = SfxObjectShell::GetFirst( nullptr, false ); + } + + OUString sTmp; + while( pShell ) + { + if( sTmp.isEmpty() ) + { + sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME ); + sTmp = lcl_DDE_RelToAbs(sTmp, sReferer ); + } + + + sTmp = aCC.lowercase( sTmp ); + if( sTmp == sNmURL ) // we want these + { + pFndShell = pShell; + break; + } + + if( bFirst ) + { + bFirst = false; + pShell = SfxObjectShell::GetFirst( nullptr, false ); + } + else + pShell = SfxObjectShell::GetNext( *pShell, nullptr, false ); + + sTmp.clear(); + } + } + + // empty topics are not allowed - which document is it + if( sTopic.isEmpty() ) + return false; + + if (pFndShell) + { + sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem ); + if( pNewSrc ) + { + css::datatransfer::DataFlavor aFl; + SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl ); + + pLink->SetObj( pNewSrc ); + pNewSrc->AddDataAdvise( pLink, aFl.MimeType, + SfxLinkUpdateMode::ONCALL == pLink->GetUpdateMode() + ? ADVISEMODE_ONLYONCE + : 0 ); + return true; + } + } + else + { + // then try to download the file: + INetURLObject aURL( sTopic ); + INetProtocol eOld = aURL.GetProtocol(); + sTopic = lcl_DDE_RelToAbs( sTopic, sReferer ); + aURL.SetURL( sTopic ); + if( INetProtocol::NotValid != eOld || + INetProtocol::Http != aURL.GetProtocol() ) + { + SfxStringItem aName( SID_FILE_NAME, sTopic ); + SfxBoolItem aMinimized(SID_MINIMIZED, true); + SfxBoolItem aHidden(SID_HIDDEN, true); + SfxStringItem aTarget( SID_TARGETNAME, "_blank" ); + SfxStringItem aReferer( SID_REFERER, sReferer ); + SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode ); + SfxBoolItem aReadOnly(SID_DOC_READONLY, false); + + // Disable automatic re-connection to avoid this link instance + // being destroyed at re-connection. + SfxBoolItem aDdeConnect(SID_DDE_RECONNECT_ONLOAD, false); + + // #i14200# (DDE-link crashes wordprocessor) + SfxAllItemSet aArgs( SfxGetpApp()->GetPool() ); + aArgs.Put(aReferer); + aArgs.Put(aTarget); + aArgs.Put(aHidden); + aArgs.Put(aMinimized); + aArgs.Put(aName); + aArgs.Put(aUpdate); + aArgs.Put(aReadOnly); + aArgs.Put(aDdeConnect); + Reference<XComponent> xComp = SfxObjectShell::CreateAndLoadComponent(aArgs); + pFndShell = SfxObjectShell::GetShellFromComponent(xComp); + if (xComp.is() && pFndShell) + { + pLinkMgr->InsertCachedComp(xComp); + sfx2::LinkManager::LinkServerShell(sItem, *pFndShell, *pLink); + return true; + } + } + } + + return false; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |