summaryrefslogtreecommitdiffstats
path: root/sfx2/source/appl/lnkbase2.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/appl/lnkbase2.cxx')
-rw-r--r--sfx2/source/appl/lnkbase2.cxx612
1 files changed, 612 insertions, 0 deletions
diff --git a/sfx2/source/appl/lnkbase2.cxx b/sfx2/source/appl/lnkbase2.cxx
new file mode 100644
index 000000000..8df00b9bd
--- /dev/null
+++ b/sfx2/source/appl/lnkbase2.cxx
@@ -0,0 +1,612 @@
+/* -*- 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 <memory>
+#include <sfx2/lnkbase.hxx>
+#include <sot/exchange.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <sfx2/linkmgr.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <tools/debug.hxx>
+#include <svl/svdde.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sfx2
+{
+
+namespace {
+
+class ImplDdeItem;
+
+}
+
+struct BaseLink_Impl
+{
+ Link<SvBaseLink&,void> m_aEndEditLink;
+ LinkManager* m_pLinkMgr;
+ weld::Window* m_pParentWin;
+ std::unique_ptr<FileDialogHelper>
+ m_pFileDlg;
+ bool m_bIsConnect;
+
+ BaseLink_Impl() :
+ m_pLinkMgr( nullptr )
+ , m_pParentWin( nullptr )
+ , m_bIsConnect( false )
+ {}
+};
+
+// only for internal management
+struct ImplBaseLinkData
+{
+ struct tClientType
+ {
+ // applies for all links
+ SotClipboardFormatId nCntntType; // Update Format
+ // Not Ole-Links
+ bool bIntrnlLnk; // It is an internal link
+ SfxLinkUpdateMode nUpdateMode; // UpdateMode
+ };
+
+ struct tDDEType
+ {
+ ImplDdeItem* pItem;
+ };
+
+ union {
+ tClientType ClientType;
+ tDDEType DDEType;
+ };
+ ImplBaseLinkData()
+ {
+ ClientType.nCntntType = SotClipboardFormatId::NONE;
+ ClientType.bIntrnlLnk = false;
+ ClientType.nUpdateMode = SfxLinkUpdateMode::NONE;
+ DDEType.pItem = nullptr;
+ }
+};
+
+namespace {
+
+class ImplDdeItem : public DdeGetPutItem
+{
+ SvBaseLink* pLink;
+ DdeData aData;
+ Sequence< sal_Int8 > aSeq; // Datacontainer for DdeData !!!
+ bool bIsValidData : 1;
+ bool bIsInDTOR : 1;
+public:
+#if defined(_WIN32)
+ ImplDdeItem( SvBaseLink& rLink, const OUString& rStr )
+ : DdeGetPutItem( rStr ), pLink( &rLink ), bIsValidData( false ),
+ bIsInDTOR( false )
+ {}
+#endif
+ virtual ~ImplDdeItem() override;
+
+ virtual DdeData* Get( SotClipboardFormatId ) override;
+ virtual bool Put( const DdeData* ) override;
+ virtual void AdviseLoop( bool ) override;
+
+ void Notify()
+ {
+ bIsValidData = false;
+ DdeGetPutItem::NotifyClient();
+ }
+
+ bool IsInDTOR() const { return bIsInDTOR; }
+};
+
+}
+
+SvBaseLink::SvBaseLink()
+ : pImpl ( new BaseLink_Impl ),
+ m_bIsReadOnly(false)
+{
+ mnObjType = SvBaseLinkObjectType::ClientSo;
+ pImplData.reset( new ImplBaseLinkData );
+ bVisible = bSynchron = true;
+ bWasLastEditOK = false;
+}
+
+
+SvBaseLink::SvBaseLink( SfxLinkUpdateMode nUpdateMode, SotClipboardFormatId nContentType )
+ : pImpl( new BaseLink_Impl ),
+ m_bIsReadOnly(false)
+{
+ mnObjType = SvBaseLinkObjectType::ClientSo;
+ pImplData.reset( new ImplBaseLinkData );
+ bVisible = bSynchron = true;
+ bWasLastEditOK = false;
+
+ // It is going to be an OLE-Link,
+ pImplData->ClientType.nUpdateMode = nUpdateMode;
+ pImplData->ClientType.nCntntType = nContentType;
+ pImplData->ClientType.bIntrnlLnk = false;
+}
+
+#if defined(_WIN32)
+
+static DdeTopic* FindTopic( const OUString & rLinkName, sal_uInt16* pItemStt )
+{
+ if( rLinkName.isEmpty() )
+ return nullptr;
+
+ OUString sNm( rLinkName );
+ sal_Int32 nTokenPos = 0;
+ OUString sService( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
+
+ DdeServices& rSvc = DdeService::GetServices();
+ for (auto const& elem : rSvc)
+ {
+ if(elem->GetName() == sService)
+ {
+ // then we search for the Topic
+ OUString sTopic( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
+ if( pItemStt )
+ *pItemStt = nTokenPos;
+
+ std::vector<DdeTopic*>& rTopics = elem->GetTopics();
+
+ for (auto const& topic : rTopics)
+ if( topic->GetName() == sTopic )
+ return topic;
+ break;
+ }
+ }
+ return nullptr;
+}
+
+SvBaseLink::SvBaseLink( const OUString& rLinkName, SvBaseLinkObjectType nObjectType, SvLinkSource* pObj )
+ : pImpl()
+ , m_bIsReadOnly(false)
+{
+ bVisible = bSynchron = true;
+ bWasLastEditOK = false;
+ aLinkName = rLinkName;
+ pImplData.reset( new ImplBaseLinkData );
+ mnObjType = nObjectType;
+
+ if( !pObj )
+ {
+ DBG_ASSERT( pObj, "Where is my left-most object" );
+ return;
+ }
+
+ if( SvBaseLinkObjectType::DdeExternal == mnObjType )
+ {
+ sal_uInt16 nItemStt = 0;
+ DdeTopic* pTopic = FindTopic( aLinkName, &nItemStt );
+ if( pTopic )
+ {
+ // then we have it all together
+ // MM_TODO how do I get the name
+ OUString aStr = aLinkName; // xLinkName->GetDisplayName();
+ aStr = aStr.copy( nItemStt );
+ pImplData->DDEType.pItem = new ImplDdeItem( *this, aStr );
+ pTopic->InsertItem( pImplData->DDEType.pItem );
+
+ // store the Advice
+ xObj = pObj;
+ }
+ }
+ else if( pObj->Connect( this ) )
+ xObj = pObj;
+}
+
+#endif
+
+SvBaseLink::~SvBaseLink()
+{
+ Disconnect();
+
+ if( mnObjType == SvBaseLinkObjectType::DdeExternal )
+ {
+ if( !pImplData->DDEType.pItem->IsInDTOR() )
+ delete pImplData->DDEType.pItem;
+ }
+
+ pImplData.reset();
+}
+
+IMPL_LINK( SvBaseLink, EndEditHdl, const OUString&, _rNewName, void )
+{
+ OUString sNewName = _rNewName;
+ if ( !ExecuteEdit( sNewName ) )
+ sNewName.clear();
+ bWasLastEditOK = !sNewName.isEmpty();
+ pImpl->m_aEndEditLink.Call( *this );
+}
+
+
+void SvBaseLink::SetObjType( SvBaseLinkObjectType mnObjTypeP )
+{
+ DBG_ASSERT( mnObjType != SvBaseLinkObjectType::ClientDde, "type already set" );
+ DBG_ASSERT( !xObj.is(), "object exist" );
+
+ mnObjType = mnObjTypeP;
+}
+
+
+void SvBaseLink::SetName( const OUString & rNm )
+{
+ aLinkName = rNm;
+}
+
+
+void SvBaseLink::SetObj( SvLinkSource * pObj )
+{
+ DBG_ASSERT( (isClientType(mnObjType) &&
+ pImplData->ClientType.bIntrnlLnk) ||
+ mnObjType == SvBaseLinkObjectType::ClientGraphic,
+ "no intern link" );
+ xObj = pObj;
+}
+
+
+void SvBaseLink::SetLinkSourceName( const OUString & rLnkNm )
+{
+ if( aLinkName == rLnkNm )
+ return;
+
+ AddNextRef(); // should be superfluous
+ // remove old connection
+ Disconnect();
+
+ aLinkName = rLnkNm;
+
+ // New Connection
+ GetRealObject_();
+ ReleaseRef(); // should be superfluous
+}
+
+
+void SvBaseLink::SetUpdateMode( SfxLinkUpdateMode nMode )
+{
+ if( isClientType(mnObjType) &&
+ pImplData->ClientType.nUpdateMode != nMode )
+ {
+ AddNextRef();
+ Disconnect();
+
+ pImplData->ClientType.nUpdateMode = nMode;
+ GetRealObject_();
+ ReleaseRef();
+ }
+}
+
+// #i88291#
+void SvBaseLink::clearStreamToLoadFrom()
+{
+ m_xInputStreamToLoadFrom.clear();
+ if( xObj.is() )
+ {
+ xObj->clearStreamToLoadFrom();
+ }
+}
+
+bool SvBaseLink::Update()
+{
+ if( isClientType(mnObjType) )
+ {
+ AddNextRef();
+ Disconnect();
+
+ GetRealObject_();
+ ReleaseRef();
+ if( xObj.is() )
+ {
+ xObj->setStreamToLoadFrom(m_xInputStreamToLoadFrom,m_bIsReadOnly);
+ OUString sMimeType( SotExchange::GetFormatMimeType(
+ pImplData->ClientType.nCntntType ));
+ Any aData;
+
+ if( xObj->GetData( aData, sMimeType ) )
+ {
+ UpdateResult eRes = DataChanged(sMimeType, aData);
+ bool bSuccess = eRes == SUCCESS;
+ //for manual Updates there is no need to hold the ServerObject
+ if( SvBaseLinkObjectType::ClientDde == mnObjType &&
+ SfxLinkUpdateMode::ONCALL == GetUpdateMode() && xObj.is() )
+ xObj->RemoveAllDataAdvise( this );
+ return bSuccess;
+ }
+ if( xObj.is() )
+ {
+ // should be asynchronous?
+ if( xObj->IsPending() )
+ return true;
+
+ // we do not need the object anymore
+ AddNextRef();
+ Disconnect();
+ ReleaseRef();
+ }
+ }
+ }
+ return false;
+}
+
+
+SfxLinkUpdateMode SvBaseLink::GetUpdateMode() const
+{
+ return isClientType(mnObjType)
+ ? pImplData->ClientType.nUpdateMode
+ : SfxLinkUpdateMode::ONCALL;
+}
+
+
+void SvBaseLink::GetRealObject_( bool bConnect)
+{
+ if( !pImpl->m_pLinkMgr )
+ return;
+
+ DBG_ASSERT( !xObj.is(), "object already exist" );
+
+ if( SvBaseLinkObjectType::ClientDde == mnObjType )
+ {
+ OUString sServer;
+ if( sfx2::LinkManager::GetDisplayNames( this, &sServer ) &&
+ sServer == Application::GetAppName() ) // internal Link !!!
+ {
+ // so that the Internal link can be created!
+ mnObjType = SvBaseLinkObjectType::Internal;
+ xObj = sfx2::LinkManager::CreateObj( this );
+
+ pImplData->ClientType.bIntrnlLnk = true;
+ mnObjType = SvBaseLinkObjectType::ClientDde; // so we know what it once was!
+ }
+ else
+ {
+ pImplData->ClientType.bIntrnlLnk = false;
+ xObj = sfx2::LinkManager::CreateObj( this );
+ }
+ }
+ else if( isClientType(mnObjType) )
+ xObj = sfx2::LinkManager::CreateObj( this );
+
+ if( bConnect && ( !xObj.is() || !xObj->Connect( this ) ) )
+ Disconnect();
+}
+
+SotClipboardFormatId SvBaseLink::GetContentType() const
+{
+ if( isClientType(mnObjType) )
+ return pImplData->ClientType.nCntntType;
+
+ return SotClipboardFormatId::NONE; // all Formats ?
+}
+
+
+void SvBaseLink::SetContentType( SotClipboardFormatId nType )
+{
+ if( isClientType(mnObjType) )
+ {
+ pImplData->ClientType.nCntntType = nType;
+ }
+}
+
+LinkManager* SvBaseLink::GetLinkManager()
+{
+ return pImpl->m_pLinkMgr;
+}
+
+const LinkManager* SvBaseLink::GetLinkManager() const
+{
+ return pImpl->m_pLinkMgr;
+}
+
+void SvBaseLink::SetLinkManager( LinkManager* _pMgr )
+{
+ pImpl->m_pLinkMgr = _pMgr;
+}
+
+void SvBaseLink::Disconnect()
+{
+ if( xObj.is() )
+ {
+ xObj->RemoveAllDataAdvise( this );
+ xObj->RemoveConnectAdvise( this );
+ xObj.clear();
+ }
+}
+
+SvBaseLink::UpdateResult SvBaseLink::DataChanged( const OUString &, const css::uno::Any & )
+{
+ if ( mnObjType == SvBaseLinkObjectType::DdeExternal )
+ {
+ if( pImplData->DDEType.pItem )
+ pImplData->DDEType.pItem->Notify();
+ }
+ return SUCCESS;
+}
+
+void SvBaseLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& rEndEditHdl )
+{
+ pImpl->m_pParentWin = pParent;
+ pImpl->m_aEndEditLink = rEndEditHdl;
+ pImpl->m_bIsConnect = xObj.is();
+ if( !pImpl->m_bIsConnect )
+ GetRealObject_( xObj.is() );
+
+ bool bAsync = false;
+ Link<const OUString&, void> aLink = LINK( this, SvBaseLink, EndEditHdl );
+
+ if( isClientType(mnObjType) && pImplData->ClientType.bIntrnlLnk )
+ {
+ if( pImpl->m_pLinkMgr )
+ {
+ SvLinkSourceRef ref = sfx2::LinkManager::CreateObj( this );
+ if( ref.is() )
+ {
+ ref->Edit( pParent, this, aLink );
+ bAsync = true;
+ }
+ }
+ }
+ else
+ {
+ xObj->Edit( pParent, this, aLink );
+ bAsync = true;
+ }
+
+ if ( !bAsync )
+ {
+ ExecuteEdit( OUString() );
+ bWasLastEditOK = false;
+ pImpl->m_aEndEditLink.Call( *this );
+ }
+}
+
+bool SvBaseLink::ExecuteEdit( const OUString& _rNewName )
+{
+ if( !_rNewName.isEmpty() )
+ {
+ SetLinkSourceName( _rNewName );
+ if( !Update() )
+ {
+ OUString sApp, sTopic, sItem, sError;
+ sfx2::LinkManager::GetDisplayNames( this, &sApp, &sTopic, &sItem );
+ if( mnObjType == SvBaseLinkObjectType::ClientDde )
+ {
+ sError = SfxResId(STR_DDE_ERROR);
+
+ sal_Int32 nFndPos = sError.indexOf( "%1" );
+ if( -1 != nFndPos )
+ {
+ sError = sError.replaceAt( nFndPos, 2, sApp );
+ nFndPos = nFndPos + sApp.getLength();
+
+ if( -1 != ( nFndPos = sError.indexOf( "%2", nFndPos )))
+ {
+ sError = sError.replaceAt( nFndPos, 2, sTopic );
+ nFndPos = nFndPos + sTopic.getLength();
+
+ if( -1 != ( nFndPos = sError.indexOf( "%3", nFndPos )))
+ sError = sError.replaceAt( nFndPos, 2, sItem );
+ }
+ }
+ }
+ else
+ return false;
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pImpl->m_pParentWin,
+ VclMessageType::Warning, VclButtonsType::Ok, sError));
+ xBox->run();
+ }
+ }
+ else if( !pImpl->m_bIsConnect )
+ Disconnect();
+ pImpl->m_bIsConnect = false;
+ return true;
+}
+
+void SvBaseLink::Closed()
+{
+ if( xObj.is() )
+ xObj->RemoveAllDataAdvise( this );
+}
+
+FileDialogHelper & SvBaseLink::GetInsertFileDialog(const OUString& rFactory) const
+{
+ pImpl->m_pFileDlg.reset( new FileDialogHelper(
+ ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::Insert, rFactory, SfxFilterFlags::NONE, SfxFilterFlags::NONE, pImpl->m_pParentWin) );
+ return *pImpl->m_pFileDlg;
+}
+
+ImplDdeItem::~ImplDdeItem()
+{
+ bIsInDTOR = true;
+ // So that no-one gets the idea to delete the pointer when Disconnecting!
+ tools::SvRef<SvBaseLink> aRef( pLink );
+ aRef->Disconnect();
+}
+
+DdeData* ImplDdeItem::Get( SotClipboardFormatId nFormat )
+{
+ if( pLink->GetObj() )
+ {
+ // is it still valid?
+ if( bIsValidData && nFormat == aData.GetFormat() )
+ return &aData;
+
+ Any aValue;
+ OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
+ if( pLink->GetObj()->GetData( aValue, sMimeType ) )
+ {
+ if( aValue >>= aSeq )
+ {
+ aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
+
+ bIsValidData = true;
+ return &aData;
+ }
+ }
+ }
+ aSeq.realloc( 0 );
+ bIsValidData = false;
+ return nullptr;
+}
+
+
+bool ImplDdeItem::Put( const DdeData* )
+{
+ OSL_FAIL( "ImplDdeItem::Put not implemented" );
+ return false;
+}
+
+
+void ImplDdeItem::AdviseLoop( bool bOpen )
+{
+ // Connection is closed, so also unsubscribe link
+ if( !pLink->GetObj() )
+ return;
+
+ if( bOpen )
+ {
+ // A connection is re-established
+ if( SvBaseLinkObjectType::DdeExternal == pLink->GetObjType() )
+ {
+ pLink->GetObj()->AddDataAdvise( pLink, "text/plain;charset=utf-16", ADVISEMODE_NODATA );
+ pLink->GetObj()->AddConnectAdvise( pLink );
+ }
+ }
+ else
+ {
+ // So that no-one gets the idea to delete the pointer
+ // when Disconnecting!
+ tools::SvRef<SvBaseLink> aRef( pLink );
+ aRef->Disconnect();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */